50f89d
commit d524fa6c35e675eedbd8fe6cdf4db0b49c658026
50f89d
Author: H.J. Lu <hjl.tools@gmail.com>
50f89d
Date:   Thu Nov 8 10:06:58 2018 -0800
50f89d
50f89d
    Check multiple NT_GNU_PROPERTY_TYPE_0 notes [BZ #23509]
50f89d
    
50f89d
    Linkers group input note sections with the same name into one output
50f89d
    note section with the same name.  One output note section is placed in
50f89d
    one PT_NOTE segment.  Since new linkers merge input .note.gnu.property
50f89d
    sections into one output .note.gnu.property section, there is only
50f89d
    one NT_GNU_PROPERTY_TYPE_0 note in one PT_NOTE segment with new linkers.
50f89d
    Since older linkers treat input .note.gnu.property section as a generic
50f89d
    note section and just concatenate all input .note.gnu.property sections
50f89d
    into one output .note.gnu.property section without merging them, we may
50f89d
    see multiple NT_GNU_PROPERTY_TYPE_0 notes in one PT_NOTE segment with
50f89d
    older linkers.
50f89d
    
50f89d
    When an older linker is used to created the program on CET-enabled OS,
50f89d
    the linker output has a single .note.gnu.property section with multiple
50f89d
    NT_GNU_PROPERTY_TYPE_0 notes, some of which have IBT and SHSTK enable
50f89d
    bits set even if the program isn't CET enabled.  Such programs will
50f89d
    crash on CET-enabled machines.  This patch updates the note parser:
50f89d
    
50f89d
    1. Skip note parsing if a NT_GNU_PROPERTY_TYPE_0 note has been processed.
50f89d
    2. Check multiple NT_GNU_PROPERTY_TYPE_0 notes.
50f89d
    
50f89d
            [BZ #23509]
50f89d
            * sysdeps/x86/dl-prop.h (_dl_process_cet_property_note): Skip
50f89d
            note parsing if a NT_GNU_PROPERTY_TYPE_0 note has been processed.
50f89d
            Update the l_cet field when processing NT_GNU_PROPERTY_TYPE_0 note.
50f89d
            Check multiple NT_GNU_PROPERTY_TYPE_0 notes.
50f89d
            * sysdeps/x86/link_map.h (l_cet): Expand to 3 bits,  Add
50f89d
            lc_unknown.
50f89d
50f89d
diff --git a/sysdeps/x86/dl-prop.h b/sysdeps/x86/dl-prop.h
50f89d
index 26c3131ac5e2d080..9ab890d12bb99133 100644
50f89d
--- a/sysdeps/x86/dl-prop.h
50f89d
+++ b/sysdeps/x86/dl-prop.h
50f89d
@@ -49,6 +49,10 @@ _dl_process_cet_property_note (struct link_map *l,
50f89d
 			      const ElfW(Addr) align)
50f89d
 {
50f89d
 #if CET_ENABLED
50f89d
+  /* Skip if we have seen a NT_GNU_PROPERTY_TYPE_0 note before.  */
50f89d
+  if (l->l_cet != lc_unknown)
50f89d
+    return;
50f89d
+
50f89d
   /* The NT_GNU_PROPERTY_TYPE_0 note must be aliged to 4 bytes in
50f89d
      32-bit objects and to 8 bytes in 64-bit objects.  Skip notes
50f89d
      with incorrect alignment.  */
50f89d
@@ -57,6 +61,9 @@ _dl_process_cet_property_note (struct link_map *l,
50f89d
 
50f89d
   const ElfW(Addr) start = (ElfW(Addr)) note;
50f89d
 
50f89d
+  unsigned int feature_1 = 0;
50f89d
+  unsigned int last_type = 0;
50f89d
+
50f89d
   while ((ElfW(Addr)) (note + 1) - start < size)
50f89d
     {
50f89d
       /* Find the NT_GNU_PROPERTY_TYPE_0 note.  */
50f89d
@@ -64,10 +71,18 @@ _dl_process_cet_property_note (struct link_map *l,
50f89d
 	  && note->n_type == NT_GNU_PROPERTY_TYPE_0
50f89d
 	  && memcmp (note + 1, "GNU", 4) == 0)
50f89d
 	{
50f89d
+	  /* Stop if we see more than one GNU property note which may
50f89d
+	     be generated by the older linker.  */
50f89d
+	  if (l->l_cet != lc_unknown)
50f89d
+	    return;
50f89d
+
50f89d
+	  /* Check CET status now.  */
50f89d
+	  l->l_cet = lc_none;
50f89d
+
50f89d
 	  /* Check for invalid property.  */
50f89d
 	  if (note->n_descsz < 8
50f89d
 	      || (note->n_descsz % sizeof (ElfW(Addr))) != 0)
50f89d
-	    break;
50f89d
+	    return;
50f89d
 
50f89d
 	  /* Start and end of property array.  */
50f89d
 	  unsigned char *ptr = (unsigned char *) (note + 1) + 4;
50f89d
@@ -78,9 +93,15 @@ _dl_process_cet_property_note (struct link_map *l,
50f89d
 	      unsigned int type = *(unsigned int *) ptr;
50f89d
 	      unsigned int datasz = *(unsigned int *) (ptr + 4);
50f89d
 
50f89d
+	      /* Property type must be in ascending order.  */
50f89d
+	      if (type < last_type)
50f89d
+		return;
50f89d
+
50f89d
 	      ptr += 8;
50f89d
 	      if ((ptr + datasz) > ptr_end)
50f89d
-		break;
50f89d
+		return;
50f89d
+
50f89d
+	      last_type = type;
50f89d
 
50f89d
 	      if (type == GNU_PROPERTY_X86_FEATURE_1_AND)
50f89d
 		{
50f89d
@@ -89,14 +110,18 @@ _dl_process_cet_property_note (struct link_map *l,
50f89d
 		     we stop the search regardless if its size is correct
50f89d
 		     or not.  There is no point to continue if this note
50f89d
 		     is ill-formed.  */
50f89d
-		  if (datasz == 4)
50f89d
-		    {
50f89d
-		      unsigned int feature_1 = *(unsigned int *) ptr;
50f89d
-		      if ((feature_1 & GNU_PROPERTY_X86_FEATURE_1_IBT))
50f89d
-			l->l_cet |= lc_ibt;
50f89d
-		      if ((feature_1 & GNU_PROPERTY_X86_FEATURE_1_SHSTK))
50f89d
-			l->l_cet |= lc_shstk;
50f89d
-		    }
50f89d
+		  if (datasz != 4)
50f89d
+		    return;
50f89d
+
50f89d
+		  feature_1 = *(unsigned int *) ptr;
50f89d
+
50f89d
+		  /* Keep searching for the next GNU property note
50f89d
+		     generated by the older linker.  */
50f89d
+		  break;
50f89d
+		}
50f89d
+	      else if (type > GNU_PROPERTY_X86_FEATURE_1_AND)
50f89d
+		{
50f89d
+		  /* Stop since property type is in ascending order.  */
50f89d
 		  return;
50f89d
 		}
50f89d
 
50f89d
@@ -112,6 +137,12 @@ _dl_process_cet_property_note (struct link_map *l,
50f89d
 	      + ELF_NOTE_NEXT_OFFSET (note->n_namesz, note->n_descsz,
50f89d
 				      align));
50f89d
     }
50f89d
+
50f89d
+  /* We get here only if there is one or no GNU property note.  */
50f89d
+  if ((feature_1 & GNU_PROPERTY_X86_FEATURE_1_IBT))
50f89d
+    l->l_cet |= lc_ibt;
50f89d
+  if ((feature_1 & GNU_PROPERTY_X86_FEATURE_1_SHSTK))
50f89d
+    l->l_cet |= lc_shstk;
50f89d
 #endif
50f89d
 }
50f89d
 
50f89d
diff --git a/sysdeps/x86/link_map.h b/sysdeps/x86/link_map.h
50f89d
index ef1206a9d2396a6f..9367ed08896794a4 100644
50f89d
--- a/sysdeps/x86/link_map.h
50f89d
+++ b/sysdeps/x86/link_map.h
50f89d
@@ -19,8 +19,9 @@
50f89d
 /* If this object is enabled with CET.  */
50f89d
 enum
50f89d
   {
50f89d
-    lc_none = 0,			 /* Not enabled with CET.  */
50f89d
-    lc_ibt = 1 << 0,			 /* Enabled with IBT.  */
50f89d
-    lc_shstk = 1 << 1,			 /* Enabled with STSHK.  */
50f89d
+    lc_unknown = 0,			 /* Unknown CET status.  */
50f89d
+    lc_none = 1 << 0,			 /* Not enabled with CET.  */
50f89d
+    lc_ibt = 1 << 1,			 /* Enabled with IBT.  */
50f89d
+    lc_shstk = 1 << 2,			 /* Enabled with STSHK.  */
50f89d
     lc_ibt_and_shstk = lc_ibt | lc_shstk /* Enabled with both.  */
50f89d
-  } l_cet:2;
50f89d
+  } l_cet:3;