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