Add .note.gnu.property runtime verification and merge support
authorMikhail Kashkarov <m.kashkarov@partner.samsung.com>
Mon, 15 Apr 2019 11:15:49 +0000 (14:15 +0300)
committerSlava Barinov <v.barinov@samsung.com>
Mon, 16 Dec 2019 12:28:16 +0000 (15:28 +0300)
- Define annobin .note.gnu.property bits for sanitization/CxxABI checks
- New configure option --enable-annobin-verification=warn|strict to enable
  runtime verification support for gnu property notes.
- Add merging support for compilation flags annobin gnu notes.
- Add readelf section text for compiler properties.

Change-Id: I452029baca753f6a97ef4b7297ef9a8905a7b79c

bfd/config.in
bfd/configure
bfd/configure.ac
bfd/elf-bfd.h
bfd/elf-properties.c
binutils/readelf.c
include/elf/common.h
packaging/binutils-aarch64.spec
packaging/binutils-armv7hl.spec
packaging/binutils-armv7l.spec
packaging/binutils.spec

index be57296..01c38b7 100644 (file)
 /* Define to 1 if you want to enable -z separate-code in ELF linker by
    default. */
 #undef DEFAULT_LD_Z_SEPARATE_CODE
+/* Define to enable annobin runtime checks for static linker (warnings only)
+   */
+#undef ENABLE_ANNOBIN_VERIFICATION
+
+/* Define to enable annobin runtime strict checks (error mode) */
+#undef ENABLE_ANNOBIN_VERIFICATION_STRICT
 
 /* Define to 1 if translation of program messages to the user's native
    language is requested. */
index a292a2e..50e4bd3 100755 (executable)
@@ -832,6 +832,7 @@ with_mmap
 enable_secureplt
 enable_separate_code
 enable_leading_mingw64_underscores
+enable_annobin_verification
 with_separate_debug_dir
 with_pkgversion
 with_bugurl
@@ -1490,6 +1491,9 @@ Optional Features:
   --enable-separate-code  enable -z separate-code in ELF linker by default
   --enable-leading-mingw64-underscores
                           Enable leading underscores on 64 bit mingw targets
+  --enable-annobin-verification[=warn|strict]
+                          enable runtime checks with dynamic notes generated
+                          by annobin plugin
   --enable-werror         treat compile warnings as errors
   --enable-build-warnings enable build-time compiler warnings
   --enable-maintainer-mode
@@ -11728,7 +11732,7 @@ else
   lt_dlunknown=0; lt_dlno_uscore=1; lt_dlneed_uscore=2
   lt_status=$lt_dlunknown
   cat > conftest.$ac_ext <<_LT_EOF
-#line 11731 "configure"
+#line 11735 "configure"
 #include "confdefs.h"
 
 #if HAVE_DLFCN_H
@@ -11834,7 +11838,7 @@ else
   lt_dlunknown=0; lt_dlno_uscore=1; lt_dlneed_uscore=2
   lt_status=$lt_dlunknown
   cat > conftest.$ac_ext <<_LT_EOF
-#line 11837 "configure"
+#line 11841 "configure"
 #include "confdefs.h"
 
 #if HAVE_DLFCN_H
@@ -12507,6 +12511,31 @@ $as_echo "#define USE_MINGW64_LEADING_UNDERSCORES 1" >>confdefs.h
 
 fi
 
+# Check whether --enable-annobin_verification was given.
+if test "${enable_annobin_verification+set}" = set; then :
+  enableval=$enable_annobin_verification; case "${enableval}" in
+  yes | warn | "" ) annobin_verification=warn ;;
+  strict ) annobin_verification=strict ;;
+  no) annobin_verification=no ;;
+  *) annobin_verification=no ;;
+ esac
+else
+  annobin_verification=no
+fi
+
+if test "$annobin_verification" = "yes" ||
+   test "$annobin_verification" = "warn" ||
+   test "$annobin_verification" = "strict"; then
+
+$as_echo "#define ENABLE_ANNOBIN_VERIFICATION 1" >>confdefs.h
+
+   if test "$annobin_verification" = "strict"; then
+
+$as_echo "#define ENABLE_ANNOBIN_VERIFICATION_STRICT 1" >>confdefs.h
+
+   fi
+fi
+
 DEBUGDIR=${libdir}/debug
 
 # Check whether --with-separate-debug-dir was given.
index 39702ce..a492b26 100644 (file)
@@ -126,6 +126,27 @@ AS_IF([ test x"$enable_leading_mingw64_underscores" = xyes ],
   [AC_DEFINE(USE_MINGW64_LEADING_UNDERSCORES, 1,
     [Define if we should use leading underscore on 64 bit mingw targets])])
 
+AC_ARG_ENABLE(annobin_verification,
+[AS_HELP_STRING([[--enable-annobin-verification[=warn|strict]]],
+[enable runtime checks with dynamic notes generated by annobin plugin])],
+[case "${enableval}" in
+  yes | warn | "" ) annobin_verification=warn ;;
+  strict ) annobin_verification=strict ;;
+  no) annobin_verification=no ;;
+  *) annobin_verification=no ;;
+ esac],
+[annobin_verification=no])
+if test "$annobin_verification" = "yes" ||
+   test "$annobin_verification" = "warn" ||
+   test "$annobin_verification" = "strict"; then
+   AC_DEFINE(ENABLE_ANNOBIN_VERIFICATION, 1,
+           [Define to enable annobin runtime checks for static linker (warnings only)])
+   if test "$annobin_verification" = "strict"; then
+     AC_DEFINE(ENABLE_ANNOBIN_VERIFICATION_STRICT, 1,
+             [Define to enable annobin runtime strict checks (error mode)])
+   fi
+fi
+
 DEBUGDIR=${libdir}/debug
 AC_ARG_WITH(separate-debug-dir,
   AS_HELP_STRING([--with-separate-debug-dir=DIR],
index 521d35d..7905b37 100644 (file)
@@ -831,6 +831,7 @@ typedef struct elf_property
       /* Add a new one if elf_property_kind is updated.  */
     } u;
   enum elf_property_kind pr_kind;
+  const char *filename;
 } elf_property;
 
 typedef struct elf_property_list
index 198eece..b510b21 100644 (file)
@@ -69,6 +69,7 @@ _bfd_elf_get_property (bfd *abfd, unsigned int type, unsigned int datasz)
   memset (p, 0, sizeof (*p));
   p->property.pr_type = type;
   p->property.pr_datasz = datasz;
+  p->property.filename = abfd->filename;
   p->next = *lastp;
   *lastp = p;
   return &p->property;
@@ -177,6 +178,26 @@ bad_size:
              prop->pr_kind = property_number;
              goto next;
 
+           case GNU_PROPERTY_COMPILER_FLAGS:
+             /* Note that annobin uses 4-byte alignment even on 64-bit targets. */
+             if (!((datasz == 4) || (datasz == 8)))
+               {
+                 _bfd_error_handler
+                   (_("warning: %B: corrupt compiler flags: 0x%x\n"),
+                    abfd, datasz);
+                 /* Clear all properties.  */
+                 elf_properties (abfd) = NULL;
+                 return FALSE;
+               }
+             prop = _bfd_elf_get_property (abfd, type, datasz);
+             if (datasz == 8)
+               prop->u.number = bfd_h_get_64 (abfd, ptr);
+             else
+               prop->u.number = bfd_h_get_32 (abfd, ptr);
+
+             prop->pr_kind = property_number;
+             goto next;
+
            default:
              break;
            }
@@ -193,6 +214,110 @@ next:
   return TRUE;
 }
 
+/*  If validation bits are set - compare bit flags for equality.  */
+inline static bfd_boolean
+elf_gnu_property_validate_flag (bfd_vma anum,
+                               bfd_vma bnum,
+                               bfd_vma validation_bit_flag,
+                               bfd_vma validation_bit)
+{
+  if ((anum & validation_bit_flag) && (bnum & validation_bit_flag)
+      && ((anum & validation_bit) != (bnum & validation_bit)))
+    {
+      return FALSE;
+    }
+  return TRUE;
+}
+
+static bfd_boolean
+elf_validate_compiler_flags_properties (elf_property *aprop,
+                                       elf_property *bprop)
+{
+  bfd_boolean is_flag_set;
+
+  /* Sanity check to verify correct usage.  */
+  BFD_ASSERT(aprop);
+  BFD_ASSERT(bprop);
+
+  if (aprop->pr_type != bprop->pr_type)
+    return TRUE;
+
+  BFD_ASSERT(aprop->pr_type == GNU_PROPERTY_COMPILER_FLAGS);
+
+  /* Return TRUE if compiler flags are identical (likely?).  */
+  if (aprop->u.number == bprop->u.number)
+    return TRUE;
+
+  if (!elf_gnu_property_validate_flag(aprop->u.number,
+                                     bprop->u.number,
+                                     GNU_PROPERTY_SANITIZE_VALIDATION,
+                                     GNU_PROPERTY_SANITIZE_ADDRESS))
+    {
+      is_flag_set = bprop->u.number & GNU_PROPERTY_SANITIZE_ADDRESS;
+      _bfd_error_handler
+       (_("%s: ERROR: Validation failed, linking %s object with previous %s"),
+        bprop->filename, is_flag_set ? "sanitized" : "unsanitized",
+        !is_flag_set ? "sanitized" : "unsanitized");
+      return FALSE;
+    }
+
+  if (!elf_gnu_property_validate_flag(aprop->u.number,
+                                     bprop->u.number,
+                                     GNU_PROPERTY_USECXX_VALIDATION,
+                                     GNU_PROPERTY_USECXX11_ABI))
+    {
+      is_flag_set = bprop->u.number & GNU_PROPERTY_USECXX11_ABI;
+      _bfd_error_handler
+       (_("ERROR: Validation failed, linking %s ABI object with %s ABI"),
+        bprop->filename, is_flag_set ? "pre-cxx11" : "cxx11",
+        !is_flag_set ? "pre-cxx11" : "cxx11");
+      return FALSE;
+    }
+
+  return TRUE;
+}
+
+
+/* Merge bprop into aprop according compiler-flags properties, return TRUE if */
+/* aprop is updated. */
+static bfd_boolean
+elf_merge_gnu_properties_compiler_flags (elf_property *aprop,
+                                        elf_property *bprop)
+{
+  bfd_boolean is_updated = FALSE;
+  /* Likely that objects have the same properties.  */
+  if (aprop->u.number == bprop->u.number) {
+    return FALSE;
+  }
+
+  /* Validation bit + no-validation bit = validation bit.  */
+  if ((aprop->u.number ^ bprop->u.number) & GNU_PROPERTY_SANITIZE_VALIDATION)
+    {
+      aprop->u.number |= GNU_PROPERTY_SANITIZE_VALIDATION;
+      is_updated = TRUE;
+    }
+  /* Sanitized object + unsanitized results = sanitized final object.  */
+  if ((aprop->u.number ^ bprop->u.number) & GNU_PROPERTY_SANITIZE_ADDRESS)
+    {
+      aprop->u.number |= GNU_PROPERTY_SANITIZE_ADDRESS;
+      is_updated = TRUE;
+    }
+
+  if ((aprop->u.number ^ bprop->u.number) & GNU_PROPERTY_USECXX_VALIDATION)
+    {
+      aprop->u.number |= GNU_PROPERTY_USECXX_VALIDATION;
+      is_updated = TRUE;
+    }
+
+  if ((aprop->u.number ^ bprop->u.number) & GNU_PROPERTY_USECXX11_ABI)
+    {
+      aprop->u.number |= GNU_PROPERTY_USECXX11_ABI;
+      is_updated = TRUE;
+    }
+
+  return is_updated;
+}
+
 /* Merge GNU property BPROP with APROP.  If APROP isn't NULL, return TRUE
    if APROP is updated.  Otherwise, return TRUE if BPROP should be merged
    with ABFD.  */
@@ -227,6 +352,44 @@ elf_merge_gnu_properties (struct bfd_link_info *info, bfd *abfd, bfd *bbfd,
       /* Return TRUE if APROP is NULL to indicate that BPROP should
         be added to ABFD.  */
       return aprop == NULL;
+      /* FALLTHROUGH */
+
+    case GNU_PROPERTY_COMPILER_FLAGS:
+      {
+       bfd_boolean is_updated;
+       if (aprop != NULL && bprop != NULL)
+         {
+#ifdef ENABLE_ANNOBIN_VERIFICATION
+           if (!elf_validate_compiler_flags_properties (aprop, bprop))
+             {
+               _bfd_error_handler(_("ERROR: Linking failed due to incompatible "
+                                    "compilation flags used to generate objects\n"));
+               /* Strict mode, abort.  */
+#ifdef ENABLE_ANNOBIN_VERIFICATION_STRICT
+               _exit (EXIT_FAILURE);
+#endif
+             }
+#endif
+           is_updated = elf_merge_gnu_properties_compiler_flags (aprop, bprop);
+         }
+       else
+         {
+           is_updated = FALSE;
+           /* aprop or bprop is NULL, warn for missing GNU_PROPERTY_COMPILER_FLAGS.
+
+              This is too noisy since glibc's crt{i,n}.o are compiled with
+              predefined flags (without annobin support), so we'll have at
+              least 2x of the following warning for every linking. May be
+              enabled after we verified crt* compilation with plugins.  */
+         /*  if (bprop == NULL)
+             _bfd_error_handler
+               (_("WARNING: not found (generated by annobin gcc plugin), "
+                  "runtime verification may be incomplete"),
+                NOTE_GNU_PROPERTY_SECTION_NAME);  */
+         }
+       return is_updated;
+      }
+      /* FALLTHROUGH */
 
     default:
       /* Never should happen.  */
@@ -601,6 +764,9 @@ _bfd_elf_link_setup_gnu_properties (struct bfd_link_info *info)
                                     NOTE_GNU_PROPERTY_SECTION_NAME);
       BFD_ASSERT (sec != NULL);
 
+      if (!sec)
+       return;
+
       /* Update stack size in .note.gnu.property with -z stack-size=N
         if N > 0.  */
       if (info->stacksize > 0)
index 0962877..7f22c58 100644 (file)
@@ -17750,6 +17750,20 @@ decode_aarch64_feature_1_and (unsigned int bitmask)
 }
 
 static void
+decode_compiler_flags_notes (unsigned long bitmask)
+{
+  printf("%#lx [", bitmask);
+
+  printf("%c%ccxx11, ", bitmask & GNU_PROPERTY_USECXX_VALIDATION ? '=' : '~',
+        bitmask & GNU_PROPERTY_USECXX11_ABI ? '+' : '!');
+
+  printf("%c%casan", bitmask & GNU_PROPERTY_SANITIZE_VALIDATION ? '=' : '~',
+        bitmask & GNU_PROPERTY_SANITIZE_ADDRESS ? '+' : '!');
+
+  printf("]");
+}
+
+static void
 print_gnu_property_note (Filedata * filedata, Elf_Internal_Note * pnote)
 {
   unsigned char * ptr = (unsigned char *) pnote->descdata;
@@ -17914,6 +17928,18 @@ print_gnu_property_note (Filedata * filedata, Elf_Internal_Note * pnote)
                printf (_("<corrupt length: %#x> "), datasz);
              goto next;
 
+           case GNU_PROPERTY_COMPILER_FLAGS:
+             printf ("compilations flags: ");
+             if ((datasz != 4) && (datasz != 8))
+               printf (_("<corrupt length: %#x> "), datasz);
+             else
+               {
+                 unsigned long bitmask_flags;
+                 bitmask_flags = (unsigned long) byte_get (ptr, size);
+                 decode_compiler_flags_notes (bitmask_flags);
+               }
+             goto next;
+
            default:
              break;
            }
index 75c4fb7..182818e 100644 (file)
 /* Values used in GNU .note.gnu.property notes (NT_GNU_PROPERTY_TYPE_0).  */
 #define GNU_PROPERTY_STACK_SIZE                        1
 #define GNU_PROPERTY_NO_COPY_ON_PROTECTED      2
+#define GNU_PROPERTY_COMPILER_FLAGS            32
+
+/* Bit masks for compiler flags:  */
+/* Pre/post cxx11 ABI.  */
+#define GNU_PROPERTY_USECXX_VALIDATION         (1U << 0)
+#define GNU_PROPERTY_USECXX11_ABI              (1U << 1)
+/* Sanitizer flags.  */
+#define GNU_PROPERTY_SANITIZE_VALIDATION       (1U << 2)
+#define GNU_PROPERTY_SANITIZE_ADDRESS          (1U << 3)
+#define GNU_PROPERTY_SANITIZE_UNDEFINED                (1U << 4)
+#define GNU_PROPERTY_SANITIZE_THREAD           (1U << 5)
 
 /* Processor-specific semantics, lo */
 #define GNU_PROPERTY_LOPROC  0xc0000000
index 4c3e55f..3460085 100644 (file)
@@ -104,6 +104,8 @@ cd build-dir
        --with-pic \
        --build=%{host_arch} --target=%{target_arch} \
        --host=%{host_arch} \
+       %{?annobin_verification: --enable-annobin-verification=warn } \
+       %{?annobin_verification_strict: --enable-annobin-verification=strict } \
 %{?cross: \
        --enable-targets=%{target_arch} \
        --enable-64-bit-bfd \
index c1b09e3..ecdde91 100644 (file)
@@ -104,6 +104,8 @@ cd build-dir
        --with-pic \
        --build=%{host_arch} --target=%{target_arch} \
        --host=%{host_arch} \
+       %{?annobin_verification: --enable-annobin-verification=warn } \
+       %{?annobin_verification_strict: --enable-annobin-verification=strict } \
 %{?cross: \
        --enable-targets=%{target_arch} \
        --enable-64-bit-bfd \
index 422c453..bedf7a1 100644 (file)
@@ -104,6 +104,8 @@ cd build-dir
        --with-pic \
        --build=%{host_arch} --target=%{target_arch} \
        --host=%{host_arch} \
+       %{?annobin_verification: --enable-annobin-verification=warn } \
+       %{?annobin_verification_strict: --enable-annobin-verification=strict } \
 %{?cross: \
        --enable-targets=%{target_arch} \
        --enable-64-bit-bfd \
index 7f2479d..f98fd94 100644 (file)
@@ -101,6 +101,8 @@ cd build-dir
        --with-pic \
        --build=%{host_arch} --target=%{target_arch} \
        --host=%{host_arch} \
+       %{?annobin_verification: --enable-annobin-verification=warn } \
+       %{?annobin_verification_strict: --enable-annobin-verification=strict } \
 %{?cross: \
        --enable-targets=%{target_arch} \
        --enable-64-bit-bfd \