From 74b4ba13b642fc0afd1c6cc7c67c9ae1f2297244 Mon Sep 17 00:00:00 2001 From: Mikhail Kashkarov Date: Mon, 15 Apr 2019 14:15:49 +0300 Subject: [PATCH] Add .note.gnu.property runtime verification and merge support - 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 | 7 ++ bfd/configure | 33 +++++++- bfd/configure.ac | 21 +++++ bfd/elf-bfd.h | 1 + bfd/elf-properties.c | 166 ++++++++++++++++++++++++++++++++++++++++ binutils/readelf.c | 26 +++++++ include/elf/common.h | 11 +++ packaging/binutils-aarch64.spec | 2 + packaging/binutils-armv7hl.spec | 2 + packaging/binutils-armv7l.spec | 2 + packaging/binutils.spec | 2 + 11 files changed, 271 insertions(+), 2 deletions(-) diff --git a/bfd/config.in b/bfd/config.in index 341afae..3c654d2 100644 --- a/bfd/config.in +++ b/bfd/config.in @@ -10,6 +10,13 @@ /* Name of host specific core header file to include in elf.c. */ #undef CORE_HEADER +/* 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. */ #undef ENABLE_NLS diff --git a/bfd/configure b/bfd/configure index 130678f..3162aff 100755 --- a/bfd/configure +++ b/bfd/configure @@ -793,6 +793,7 @@ enable_64_bit_archive with_mmap enable_secureplt enable_leading_mingw64_underscores +enable_annobin_verification with_separate_debug_dir with_pkgversion with_bugurl @@ -1447,6 +1448,9 @@ Optional Features: --enable-secureplt Default to creating read-only plt entries --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 enable make rules and dependencies not useful @@ -11427,7 +11431,7 @@ else lt_dlunknown=0; lt_dlno_uscore=1; lt_dlneed_uscore=2 lt_status=$lt_dlunknown cat > conftest.$ac_ext <<_LT_EOF -#line 11430 "configure" +#line 11434 "configure" #include "confdefs.h" #if HAVE_DLFCN_H @@ -11533,7 +11537,7 @@ else lt_dlunknown=0; lt_dlno_uscore=1; lt_dlneed_uscore=2 lt_status=$lt_dlunknown cat > conftest.$ac_ext <<_LT_EOF -#line 11536 "configure" +#line 11540 "configure" #include "confdefs.h" #if HAVE_DLFCN_H @@ -12181,6 +12185,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. diff --git a/bfd/configure.ac b/bfd/configure.ac index 0ecbf08..0bd5008 100644 --- a/bfd/configure.ac +++ b/bfd/configure.ac @@ -105,6 +105,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], diff --git a/bfd/elf-bfd.h b/bfd/elf-bfd.h index b2f7bf4..b8b0c42 100644 --- a/bfd/elf-bfd.h +++ b/bfd/elf-bfd.h @@ -779,6 +779,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 diff --git a/bfd/elf-properties.c b/bfd/elf-properties.c index a0456f8..95e66ff 100644 --- a/bfd/elf-properties.c +++ b/bfd/elf-properties.c @@ -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; @@ -163,6 +164,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; } @@ -184,6 +205,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. */ @@ -218,6 +343,44 @@ elf_merge_gnu_properties (bfd *abfd, elf_property *aprop, /* 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. */ @@ -380,6 +543,9 @@ _bfd_elf_link_setup_gnu_properties (struct bfd_link_info *info) sec = bfd_get_section_by_name (first_pbfd, NOTE_GNU_PROPERTY_SECTION_NAME); + if (!sec) + return; + /* Update stack size in .note.gnu.property with -z stack-size=N if N > 0. */ if (info->stacksize > 0) diff --git a/binutils/readelf.c b/binutils/readelf.c index 5acc770..9212a8f 100644 --- a/binutils/readelf.c +++ b/binutils/readelf.c @@ -15441,6 +15441,20 @@ decode_x86_isa (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 (Elf_Internal_Note * pnote) { unsigned char * ptr = (unsigned char *) pnote->descdata; @@ -15526,6 +15540,18 @@ print_gnu_property_note (Elf_Internal_Note * pnote) printf (_(" "), datasz); goto next; + case GNU_PROPERTY_COMPILER_FLAGS: + printf ("compilations flags: "); + if ((datasz != 4) && (datasz != 8)) + printf (_(" "), datasz); + else + { + unsigned long bitmask_flags; + bitmask_flags = (unsigned long) byte_get (ptr, size); + decode_compiler_flags_notes (bitmask_flags); + } + goto next; + default: break; } diff --git a/include/elf/common.h b/include/elf/common.h index 4d8e42e..ff3a08d 100644 --- a/include/elf/common.h +++ b/include/elf/common.h @@ -669,6 +669,17 @@ /* 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 diff --git a/packaging/binutils-aarch64.spec b/packaging/binutils-aarch64.spec index 4c3e55f..3460085 100644 --- a/packaging/binutils-aarch64.spec +++ b/packaging/binutils-aarch64.spec @@ -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 \ diff --git a/packaging/binutils-armv7hl.spec b/packaging/binutils-armv7hl.spec index c1b09e3..ecdde91 100644 --- a/packaging/binutils-armv7hl.spec +++ b/packaging/binutils-armv7hl.spec @@ -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 \ diff --git a/packaging/binutils-armv7l.spec b/packaging/binutils-armv7l.spec index 422c453..bedf7a1 100644 --- a/packaging/binutils-armv7l.spec +++ b/packaging/binutils-armv7l.spec @@ -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 \ diff --git a/packaging/binutils.spec b/packaging/binutils.spec index 7f2479d..f98fd94 100644 --- a/packaging/binutils.spec +++ b/packaging/binutils.spec @@ -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 \ -- 2.7.4