From 059ce098ce3e18d59259644aa49c0ccb68d44b3c Mon Sep 17 00:00:00 2001 From: Maxim Ostapenko Date: Mon, 8 Feb 2016 08:39:59 +0000 Subject: [asan] Introduce new approach for ODR violation detection based on odr indicator symbols. This is a compiler-rt part of this http://reviews.llvm.org/D15642 patch. Here, we add a new approach for ODR violation detection. Instead of using __asan_region_is_poisoned(g->beg, g->size_with_redzone) on global address (that would return false now due to using private alias), we can use new globally visible indicator symbol to perform the check. Differential Revision: http://reviews.llvm.org/D15644 git-svn-id: https://llvm.org/svn/llvm-project/compiler-rt/trunk@260076 91177308-0d34-0410-b5e6-96231b3b80d8 --- lib/asan/asan_flags.inc | 2 + lib/asan/asan_globals.cc | 85 +++++++++++++++++++++++++++++++++----- lib/asan/asan_init_version.h | 3 +- lib/asan/asan_interface_internal.h | 1 + 4 files changed, 79 insertions(+), 12 deletions(-) (limited to 'lib/asan') diff --git a/lib/asan/asan_flags.inc b/lib/asan/asan_flags.inc index 5e69242fb..830dbc4ab 100644 --- a/lib/asan/asan_flags.inc +++ b/lib/asan/asan_flags.inc @@ -135,3 +135,5 @@ ASAN_FLAG(const char *, suppressions, "", "Suppressions file name.") ASAN_FLAG(bool, halt_on_error, true, "Crash the program after printing the first error report " "(WARNING: USE AT YOUR OWN RISK!)") +ASAN_FLAG(bool, use_odr_indicator, false, + "Use special ODR indicator symbol for ODR violation detection") diff --git a/lib/asan/asan_globals.cc b/lib/asan/asan_globals.cc index ebe0d103a..f2cc449b7 100644 --- a/lib/asan/asan_globals.cc +++ b/lib/asan/asan_globals.cc @@ -135,6 +135,70 @@ bool GetInfoForAddressIfGlobal(uptr addr, AddressDescription *descr) { return false; } +enum GlobalSymbolState { + UNREGISTERED = 0, + REGISTERED = 1 +}; + +// Check ODR violation for given global G via special ODR indicator. We use +// this method in case compiler instruments global variables through their +// local aliases. +static void CheckODRViolationViaIndicator(const Global *g) { + u8 *odr_indicator = reinterpret_cast(g->odr_indicator); + if (*odr_indicator == UNREGISTERED) { + *odr_indicator = REGISTERED; + return; + } + // If *odr_indicator is DEFINED, some module have already registered + // externally visible symbol with the same name. This is an ODR violation. + for (ListOfGlobals *l = list_of_all_globals; l; l = l->next) { + if (g->odr_indicator == l->g->odr_indicator && + (flags()->detect_odr_violation >= 2 || g->size != l->g->size) && + !IsODRViolationSuppressed(g->name)) + ReportODRViolation(g, FindRegistrationSite(g), + l->g, FindRegistrationSite(l->g)); + } +} + +// Check ODR violation for given global G by checking if it's already poisoned. +// We use this method in case compiler doesn't use private aliases for global +// variables. +static void CheckODRViolationViaPoisoning(const Global *g) { + if (__asan_region_is_poisoned(g->beg, g->size_with_redzone)) { + // This check may not be enough: if the first global is much larger + // the entire redzone of the second global may be within the first global. + for (ListOfGlobals *l = list_of_all_globals; l; l = l->next) { + if (g->beg == l->g->beg && + (flags()->detect_odr_violation >= 2 || g->size != l->g->size) && + !IsODRViolationSuppressed(g->name)) + ReportODRViolation(g, FindRegistrationSite(g), + l->g, FindRegistrationSite(l->g)); + } + } +} + +// Clang provides two different ways for global variables protection: +// it can poison the global itself or it's private alias. In former +// case we may poison same symbol multiple times, that can help us to +// cheaply detect ODR violation: if we try to poison an already poisoned +// global, we have ODR violation error. +// In latter case, we poison each symbol exactly once, so we use special +// indicator symbol to perform similar check. +// In either case, compiler provides a special odr_indicator field to Global +// structure, that can contain two kinds of values: +// 1) Non-zero value. In this case, odr_indicator is an address of +// corresponding indicator variable for given global. +// 2) Zero. This means that we don't use private aliases for global variables +// and can freely check ODR violation with the first method. +// +// This routine chooses between two different methods of ODR violation +// detection. +static inline bool UseODRIndicator(const Global *g) { + // Use ODR indicator method iff use_odr_indicator flag is set and + // indicator symbol address is not 0. + return flags()->use_odr_indicator && g->odr_indicator > 0; +} + // Register a global variable. // This function may be called more than once for every global // so we store the globals in a map. @@ -158,17 +222,10 @@ static void RegisterGlobal(const Global *g) { if (flags()->detect_odr_violation) { // Try detecting ODR (One Definition Rule) violation, i.e. the situation // where two globals with the same name are defined in different modules. - if (__asan_region_is_poisoned(g->beg, g->size_with_redzone)) { - // This check may not be enough: if the first global is much larger - // the entire redzone of the second global may be within the first global. - for (ListOfGlobals *l = list_of_all_globals; l; l = l->next) { - if (g->beg == l->g->beg && - (flags()->detect_odr_violation >= 2 || g->size != l->g->size) && - !IsODRViolationSuppressed(g->name)) - ReportODRViolation(g, FindRegistrationSite(g), - l->g, FindRegistrationSite(l->g)); - } - } + if (UseODRIndicator(g)) + CheckODRViolationViaIndicator(g); + else + CheckODRViolationViaPoisoning(g); } if (CanPoisonMemory()) PoisonRedZones(*g); @@ -199,6 +256,12 @@ static void UnregisterGlobal(const Global *g) { // We unpoison the shadow memory for the global but we do not remove it from // the list because that would require O(n^2) time with the current list // implementation. It might not be worth doing anyway. + + // Release ODR indicator. + if (UseODRIndicator(g)) { + u8 *odr_indicator = reinterpret_cast(g->odr_indicator); + *odr_indicator = UNREGISTERED; + } } void StopInitOrderChecking() { diff --git a/lib/asan/asan_init_version.h b/lib/asan/asan_init_version.h index bc8a622f5..8c6f3d671 100644 --- a/lib/asan/asan_init_version.h +++ b/lib/asan/asan_init_version.h @@ -28,7 +28,8 @@ extern "C" { // v4=>v5: changed the semantics and format of __asan_stack_malloc_ and // __asan_stack_free_ functions. // v5=>v6: changed the name of the version check symbol - #define __asan_version_mismatch_check __asan_version_mismatch_check_v6 + // v6=>v7: added 'odr_indicator' to __asan_global. + #define __asan_version_mismatch_check __asan_version_mismatch_check_v7 } #endif // ASAN_INIT_VERSION_H diff --git a/lib/asan/asan_interface_internal.h b/lib/asan/asan_interface_internal.h index 9efddcbd4..7bd241936 100644 --- a/lib/asan/asan_interface_internal.h +++ b/lib/asan/asan_interface_internal.h @@ -54,6 +54,7 @@ extern "C" { uptr has_dynamic_init; // Non-zero if the global has dynamic initializer. __asan_global_source_location *location; // Source location of a global, // or NULL if it is unknown. + uptr odr_indicator; // The address of the ODR indicator symbol. }; // These two functions should be called by the instrumented code. -- cgit v1.2.3