From a69d4cc1b9d46e75a24ab008ef78b0d5073e3994 Mon Sep 17 00:00:00 2001 From: Alexey Samsonov Date: Wed, 2 Jul 2014 16:54:41 +0000 Subject: [PATCH] [ASan] Print exact source location of global variables in error reports. See https://code.google.com/p/address-sanitizer/issues/detail?id=299 for the original feature request. Introduce llvm.asan.globals metadata, which Clang (or any other frontend) may use to report extra information about global variables to ASan instrumentation pass in the backend. This metadata replaces llvm.asan.dynamically_initialized_globals that was used to detect init-order bugs. llvm.asan.globals contains the following data for each global: 1) source location (file/line/column info); 2) whether it is dynamically initialized; 3) whether it is blacklisted (shouldn't be instrumented). Source location data is then emitted in the binary and can be picked up by ASan runtime in case it needs to print error report involving some global. For example: 0x... is located 4 bytes to the right of global variable 'C::array' defined in '/path/to/file:17:8' (0x...) of size 40 These source locations are printed even if the binary doesn't have any debug info. This is an ABI-breaking change. ASan initialization is renamed to __asan_init_v4(). Pre-built libraries compiled with older Clang will not work with the fresh runtime. git-svn-id: https://llvm.org/svn/llvm-project/compiler-rt/trunk@212188 91177308-0d34-0410-b5e6-96231b3b80d8 --- lib/asan/asan_dll_thunk.cc | 8 +++---- lib/asan/asan_interface_internal.h | 15 ++++++++++++-- lib/asan/asan_report.cc | 35 ++++++++++++++++++++++++++----- test/asan/TestCases/global-location.cc | 38 ++++++++++++++++++++++++++++++++++ 4 files changed, 85 insertions(+), 11 deletions(-) create mode 100644 test/asan/TestCases/global-location.cc diff --git a/lib/asan/asan_dll_thunk.cc b/lib/asan/asan_dll_thunk.cc index aa27c404f..7b6ccc6a6 100644 --- a/lib/asan/asan_dll_thunk.cc +++ b/lib/asan/asan_dll_thunk.cc @@ -203,13 +203,13 @@ extern "C" { // Manually wrap __asan_init as we need to initialize // __asan_option_detect_stack_use_after_return afterwards. - void __asan_init_v3() { + void __asan_init_v4() { typedef void (*fntype)(); static fntype fn = 0; - // __asan_init_v3 is expected to be called by only one thread. + // __asan_init_v4 is expected to be called by only one thread. if (fn) return; - fn = (fntype)getRealProcAddressOrDie("__asan_init_v3"); + fn = (fntype)getRealProcAddressOrDie("__asan_init_v4"); fn(); __asan_option_detect_stack_use_after_return = (__asan_should_detect_stack_use_after_return() != 0); @@ -339,7 +339,7 @@ void InterceptHooks() { // In DLLs, the callbacks are expected to return 0, // otherwise CRT initialization fails. static int call_asan_init() { - __asan_init_v3(); + __asan_init_v4(); return 0; } #pragma section(".CRT$XIB", long, read) // NOLINT diff --git a/lib/asan/asan_interface_internal.h b/lib/asan/asan_interface_internal.h index 84525d090..6d95ad819 100644 --- a/lib/asan/asan_interface_internal.h +++ b/lib/asan/asan_interface_internal.h @@ -30,8 +30,17 @@ extern "C" { // v2=>v3: stack frame description (created by the compiler) // contains the function PC as the 3-rd field (see // DescribeAddressIfStack). - SANITIZER_INTERFACE_ATTRIBUTE void __asan_init_v3(); - #define __asan_init __asan_init_v3 + // v3=>v4: added '__asan_global_source_location' to __asan_global. + SANITIZER_INTERFACE_ATTRIBUTE void __asan_init_v4(); + #define __asan_init __asan_init_v4 + + // This structure is used to describe the source location of a place where + // global was defined. + struct __asan_global_source_location { + const char *filename; + int line_no; + int column_no; + }; // This structure describes an instrumented global variable. struct __asan_global { @@ -42,6 +51,8 @@ extern "C" { const char *module_name; // Module name as a C string. This pointer is a // unique identifier of a module. 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. }; // These two functions should be called by the instrumented code. diff --git a/lib/asan/asan_report.cc b/lib/asan/asan_report.cc index 3b8fa3e80..66329c078 100644 --- a/lib/asan/asan_report.cc +++ b/lib/asan/asan_report.cc @@ -212,6 +212,26 @@ static void PrintGlobalNameIfASCII(InternalScopedString *str, (char *)g.beg); } +static const char *GlobalFilename(const __asan_global &g) { + const char *res = g.module_name; + // Prefer the filename from source location, if is available. + if (g.location) + res = g.location->filename; + CHECK(res); + return res; +} + +static void PrintGlobalLocation(InternalScopedString *str, + const __asan_global &g) { + str->append("%s", GlobalFilename(g)); + if (!g.location) + return; + if (g.location->line_no) + str->append(":%d", g.location->line_no); + if (g.location->column_no) + str->append(":%d", g.location->column_no); +} + bool DescribeAddressRelativeToGlobal(uptr addr, uptr size, const __asan_global &g) { static const uptr kMinimalDistanceFromAnotherGlobal = 64; @@ -232,8 +252,10 @@ bool DescribeAddressRelativeToGlobal(uptr addr, uptr size, // Can it happen? str.append("%p is located %zd bytes inside", (void *)addr, addr - g.beg); } - str.append(" of global variable '%s' from '%s' (0x%zx) of size %zu\n", - MaybeDemangleGlobalName(g.name), g.module_name, g.beg, g.size); + str.append(" of global variable '%s' defined in '", + MaybeDemangleGlobalName(g.name)); + PrintGlobalLocation(&str, g); + str.append("' (0x%zx) of size %zu\n", g.beg, g.size); str.append("%s", d.EndLocation()); PrintGlobalNameIfASCII(&str, g); Printf("%s", str.data()); @@ -742,8 +764,11 @@ void ReportODRViolation(const __asan_global *g1, u32 stack_id1, Printf("%s", d.Warning()); Report("ERROR: AddressSanitizer: odr-violation (%p):\n", g1->beg); Printf("%s", d.EndWarning()); - Printf(" [1] size=%zd %s %s\n", g1->size, g1->name, g1->module_name); - Printf(" [2] size=%zd %s %s\n", g2->size, g2->name, g2->module_name); + InternalScopedString g1_loc(256), g2_loc(256); + PrintGlobalLocation(&g1_loc, *g1); + PrintGlobalLocation(&g2_loc, *g2); + Printf(" [1] size=%zd %s %s\n", g1->size, g1->name, g1_loc.data()); + Printf(" [2] size=%zd %s %s\n", g2->size, g2->name, g2_loc.data()); if (stack_id1 && stack_id2) { Printf("These globals were registered at these points:\n"); Printf(" [1]:\n"); @@ -756,7 +781,7 @@ void ReportODRViolation(const __asan_global *g1, u32 stack_id1, } Report("HINT: if you don't care about these warnings you may set " "ASAN_OPTIONS=detect_odr_violation=0\n"); - ReportErrorSummary("odr-violation", g1->module_name, 0, g1->name); + ReportErrorSummary("odr-violation", g1_loc.data(), 0, g1->name); } // ----------------------- CheckForInvalidPointerPair ----------- {{{1 diff --git a/test/asan/TestCases/global-location.cc b/test/asan/TestCases/global-location.cc new file mode 100644 index 000000000..4e386c988 --- /dev/null +++ b/test/asan/TestCases/global-location.cc @@ -0,0 +1,38 @@ +// RUN: %clangxx_asan -O2 %s -o %t +// RUN: not %run %t g 2>&1 | FileCheck %s --check-prefix=CHECK --check-prefix=GLOB +// RUN: not %run %t c 2>&1 | FileCheck %s --check-prefix=CHECK --check-prefix=CLASS_STATIC +// RUN: not %run %t f 2>&1 | FileCheck %s --check-prefix=CHECK --check-prefix=FUNC_STATIC +// RUN: not %run %t l 2>&1 | FileCheck %s --check-prefix=CHECK --check-prefix=LITERAL + +// CHECK: AddressSanitizer: global-buffer-overflow + +#include + +struct C { + static int array[10]; +}; + +int global[10]; +// GLOB: 0x{{.*}} is located 4 bytes to the right of global variable 'global' defined in '{{.*}}global-location.cc:[[@LINE-1]]:5' {{.*}} of size 40 +int C::array[10]; +// CLASS_STATIC: 0x{{.*}} is located 4 bytes to the right of global variable 'C::array' defined in '{{.*}}global-location.cc:[[@LINE-1]]:8' {{.*}} of size 40 + +int main(int argc, char **argv) { + int one = argc - 1; + switch (argv[1][0]) { + case 'g': return global[one * 11]; + case 'c': return C::array[one * 11]; + case 'f': + static int array[10]; + // FUNC_STATIC: 0x{{.*}} is located 4 bytes to the right of global variable 'main::array' defined in '{{.*}}global-location.cc:[[@LINE-1]]:16' {{.*}} of size 40 + memset(array, 0, 10); + return array[one * 11]; + case 'l': + const char *str = "0123456789"; + // LITERAL: 0x{{.*}} is located 0 bytes to the right of global variable {{.*}} defined in '{{.*}}global-location.cc:[[@LINE-1]]:23' {{.*}} of size 11 + return str[one * 11]; + } + return 0; +} + +// CHECK: SUMMARY: AddressSanitizer: global-buffer-overflow -- 2.11.4.GIT