diff options
Diffstat (limited to 'lib/asan/asan_rtl.cc')
-rw-r--r-- | lib/asan/asan_rtl.cc | 425 |
1 files changed, 127 insertions, 298 deletions
diff --git a/lib/asan/asan_rtl.cc b/lib/asan/asan_rtl.cc index 34324fa16d08..11adbee5bdea 100644 --- a/lib/asan/asan_rtl.cc +++ b/lib/asan/asan_rtl.cc @@ -13,29 +13,29 @@ //===----------------------------------------------------------------------===// #include "asan_allocator.h" #include "asan_interceptors.h" -#include "asan_interface.h" #include "asan_internal.h" -#include "asan_lock.h" #include "asan_mapping.h" +#include "asan_report.h" #include "asan_stack.h" #include "asan_stats.h" #include "asan_thread.h" #include "asan_thread_registry.h" +#include "sanitizer/asan_interface.h" #include "sanitizer_common/sanitizer_atomic.h" #include "sanitizer_common/sanitizer_flags.h" #include "sanitizer_common/sanitizer_libc.h" +#include "sanitizer_common/sanitizer_symbolizer.h" -namespace __sanitizer { -using namespace __asan; +namespace __asan { -void Die() { +static void AsanDie() { static atomic_uint32_t num_calls; if (atomic_fetch_add(&num_calls, 1, memory_order_relaxed) != 0) { // Don't die twice - run a busy loop. while (1) { } } if (flags()->sleep_before_dying) { - Report("Sleeping for %zd second(s)\n", flags()->sleep_before_dying); + Report("Sleeping for %d second(s)\n", flags()->sleep_before_dying); SleepForSeconds(flags()->sleep_before_dying); } if (flags()->unmap_shadow_on_exit) @@ -47,19 +47,17 @@ void Die() { Exit(flags()->exitcode); } -void CheckFailed(const char *file, int line, const char *cond, u64 v1, u64 v2) { - AsanReport("AddressSanitizer CHECK failed: %s:%d \"%s\" (0x%zx, 0x%zx)\n", +static void AsanCheckFailed(const char *file, int line, const char *cond, + u64 v1, u64 v2) { + Report("AddressSanitizer CHECK failed: %s:%d \"%s\" (0x%zx, 0x%zx)\n", file, line, cond, (uptr)v1, (uptr)v2); + // FIXME: check for infinite recursion without a thread-local counter here. PRINT_CURRENT_STACK(); - ShowStatsAndAbort(); + Die(); } -} // namespace __sanitizer - -namespace __asan { - // -------------------------- Flags ------------------------- {{{1 -static const int kMallocContextSize = 30; +static const int kDeafultMallocContextSize = 30; static Flags asan_flags; @@ -67,6 +65,10 @@ Flags *flags() { return &asan_flags; } +static const char *MaybeCallAsanDefaultOptions() { + return (&__asan_default_options) ? __asan_default_options() : ""; +} + static void ParseFlagsFromString(Flags *f, const char *str) { ParseFlag(str, &f->quarantine_size, "quarantine_size"); ParseFlag(str, &f->symbolize, "symbolize"); @@ -77,8 +79,9 @@ static void ParseFlagsFromString(Flags *f, const char *str) { ParseFlag(str, &f->debug, "debug"); ParseFlag(str, &f->report_globals, "report_globals"); + ParseFlag(str, &f->check_initialization_order, "initialization_order"); ParseFlag(str, &f->malloc_context_size, "malloc_context_size"); - CHECK(f->malloc_context_size <= kMallocContextSize); + CHECK((uptr)f->malloc_context_size <= kStackTraceMax); ParseFlag(str, &f->replace_str, "replace_str"); ParseFlag(str, &f->replace_intrin, "replace_intrin"); @@ -96,22 +99,28 @@ static void ParseFlagsFromString(Flags *f, const char *str) { ParseFlag(str, &f->abort_on_error, "abort_on_error"); ParseFlag(str, &f->atexit, "atexit"); ParseFlag(str, &f->disable_core, "disable_core"); + ParseFlag(str, &f->strip_path_prefix, "strip_path_prefix"); + ParseFlag(str, &f->allow_reexec, "allow_reexec"); + ParseFlag(str, &f->print_full_thread_history, "print_full_thread_history"); + ParseFlag(str, &f->log_path, "log_path"); + ParseFlag(str, &f->fast_unwind_on_fatal, "fast_unwind_on_fatal"); + ParseFlag(str, &f->fast_unwind_on_malloc, "fast_unwind_on_malloc"); + ParseFlag(str, &f->poison_heap, "poison_heap"); + ParseFlag(str, &f->alloc_dealloc_mismatch, "alloc_dealloc_mismatch"); + ParseFlag(str, &f->use_stack_depot, "use_stack_depot"); } -extern "C" { -const char* WEAK __asan_default_options() { return ""; } -} // extern "C" - void InitializeFlags(Flags *f, const char *env) { internal_memset(f, 0, sizeof(*f)); - f->quarantine_size = (ASAN_LOW_MEMORY) ? 1UL << 24 : 1UL << 28; + f->quarantine_size = (ASAN_LOW_MEMORY) ? 1UL << 26 : 1UL << 28; f->symbolize = false; f->verbosity = 0; - f->redzone = (ASAN_LOW_MEMORY) ? 64 : 128; + f->redzone = ASAN_ALLOCATOR_VERSION == 2 ? 16 : (ASAN_LOW_MEMORY) ? 64 : 128; f->debug = false; f->report_globals = 1; - f->malloc_context_size = kMallocContextSize; + f->check_initialization_order = true; + f->malloc_context_size = kDeafultMallocContextSize; f->replace_str = true; f->replace_intrin = true; f->replace_cfallocator = true; @@ -127,13 +136,24 @@ void InitializeFlags(Flags *f, const char *env) { f->unmap_shadow_on_exit = false; f->abort_on_error = false; f->atexit = false; - f->disable_core = (__WORDSIZE == 64); + f->disable_core = (SANITIZER_WORDSIZE == 64); + f->strip_path_prefix = ""; + f->allow_reexec = true; + f->print_full_thread_history = true; + f->log_path = 0; + f->fast_unwind_on_fatal = false; + f->fast_unwind_on_malloc = true; + f->poison_heap = true; + // Turn off alloc/dealloc mismatch checker on Mac for now. + // TODO(glider): Fix known issues and enable this back. + f->alloc_dealloc_mismatch = (ASAN_MAC == 0); + f->use_stack_depot = true; // Only affects allocator2. // Override from user-specified string. - ParseFlagsFromString(f, __asan_default_options()); + ParseFlagsFromString(f, MaybeCallAsanDefaultOptions()); if (flags()->verbosity) { Report("Using the defaults from __asan_default_options: %s\n", - __asan_default_options()); + MaybeCallAsanDefaultOptions()); } // Override from command line. @@ -144,10 +164,6 @@ void InitializeFlags(Flags *f, const char *env) { int asan_inited; bool asan_init_is_running; void (*death_callback)(void); -static void (*error_report_callback)(const char*); -char *error_message_buffer = 0; -uptr error_message_buffer_pos = 0; -uptr error_message_buffer_size = 0; // -------------------------- Misc ---------------- {{{1 void ShowStatsAndAbort() { @@ -155,146 +171,23 @@ void ShowStatsAndAbort() { Die(); } -static void PrintBytes(const char *before, uptr *a) { - u8 *bytes = (u8*)a; - uptr byte_num = (__WORDSIZE) / 8; - AsanPrintf("%s%p:", before, (void*)a); - for (uptr i = 0; i < byte_num; i++) { - AsanPrintf(" %x%x", bytes[i] >> 4, bytes[i] & 15); - } - AsanPrintf("\n"); -} - -void AppendToErrorMessageBuffer(const char *buffer) { - if (error_message_buffer) { - uptr length = internal_strlen(buffer); - CHECK_GE(error_message_buffer_size, error_message_buffer_pos); - uptr remaining = error_message_buffer_size - error_message_buffer_pos; - internal_strncpy(error_message_buffer + error_message_buffer_pos, - buffer, remaining); - error_message_buffer[error_message_buffer_size - 1] = '\0'; - // FIXME: reallocate the buffer instead of truncating the message. - error_message_buffer_pos += remaining > length ? length : remaining; - } -} - // ---------------------- mmap -------------------- {{{1 // Reserve memory range [beg, end]. static void ReserveShadowMemoryRange(uptr beg, uptr end) { - CHECK((beg % kPageSize) == 0); - CHECK(((end + 1) % kPageSize) == 0); + CHECK((beg % GetPageSizeCached()) == 0); + CHECK(((end + 1) % GetPageSizeCached()) == 0); uptr size = end - beg + 1; void *res = MmapFixedNoReserve(beg, size); - CHECK(res == (void*)beg && "ReserveShadowMemoryRange failed"); -} - -// ---------------------- LowLevelAllocator ------------- {{{1 -void *LowLevelAllocator::Allocate(uptr size) { - CHECK((size & (size - 1)) == 0 && "size must be a power of two"); - if (allocated_end_ - allocated_current_ < (sptr)size) { - uptr size_to_allocate = Max(size, kPageSize); - allocated_current_ = - (char*)MmapOrDie(size_to_allocate, __FUNCTION__); - allocated_end_ = allocated_current_ + size_to_allocate; - PoisonShadow((uptr)allocated_current_, size_to_allocate, - kAsanInternalHeapMagic); - } - CHECK(allocated_end_ - allocated_current_ >= (sptr)size); - void *res = allocated_current_; - allocated_current_ += size; - return res; -} - -// ---------------------- DescribeAddress -------------------- {{{1 -static bool DescribeStackAddress(uptr addr, uptr access_size) { - AsanThread *t = asanThreadRegistry().FindThreadByStackAddress(addr); - if (!t) return false; - const sptr kBufSize = 4095; - char buf[kBufSize]; - uptr offset = 0; - const char *frame_descr = t->GetFrameNameByAddr(addr, &offset); - // This string is created by the compiler and has the following form: - // "FunctioName n alloc_1 alloc_2 ... alloc_n" - // where alloc_i looks like "offset size len ObjectName ". - CHECK(frame_descr); - // Report the function name and the offset. - const char *name_end = internal_strchr(frame_descr, ' '); - CHECK(name_end); - buf[0] = 0; - internal_strncat(buf, frame_descr, - Min(kBufSize, - static_cast<sptr>(name_end - frame_descr))); - AsanPrintf("Address %p is located at offset %zu " - "in frame <%s> of T%d's stack:\n", - (void*)addr, offset, buf, t->tid()); - // Report the number of stack objects. - char *p; - uptr n_objects = internal_simple_strtoll(name_end, &p, 10); - CHECK(n_objects > 0); - AsanPrintf(" This frame has %zu object(s):\n", n_objects); - // Report all objects in this frame. - for (uptr i = 0; i < n_objects; i++) { - uptr beg, size; - sptr len; - beg = internal_simple_strtoll(p, &p, 10); - size = internal_simple_strtoll(p, &p, 10); - len = internal_simple_strtoll(p, &p, 10); - if (beg <= 0 || size <= 0 || len < 0 || *p != ' ') { - AsanPrintf("AddressSanitizer can't parse the stack frame " - "descriptor: |%s|\n", frame_descr); - break; - } - p++; - buf[0] = 0; - internal_strncat(buf, p, Min(kBufSize, len)); - p += len; - AsanPrintf(" [%zu, %zu) '%s'\n", beg, beg + size, buf); - } - AsanPrintf("HINT: this may be a false positive if your program uses " - "some custom stack unwind mechanism\n" - " (longjmp and C++ exceptions *are* supported)\n"); - t->summary()->Announce(); - return true; -} - -static bool DescribeAddrIfShadow(uptr addr) { - if (AddrIsInMem(addr)) - return false; - static const char kAddrInShadowReport[] = - "Address %p is located in the %s.\n"; - if (AddrIsInShadowGap(addr)) { - AsanPrintf(kAddrInShadowReport, addr, "shadow gap area"); - return true; - } - if (AddrIsInHighShadow(addr)) { - AsanPrintf(kAddrInShadowReport, addr, "high shadow area"); - return true; - } - if (AddrIsInLowShadow(addr)) { - AsanPrintf(kAddrInShadowReport, addr, "low shadow area"); - return true; + if (res != (void*)beg) { + Report("ReserveShadowMemoryRange failed while trying to map 0x%zx bytes. " + "Perhaps you're using ulimit -v\n", size); + Abort(); } - - CHECK(0); // Unreachable. - return false; } -static NOINLINE void DescribeAddress(uptr addr, uptr access_size) { - // Check if this is shadow or shadow gap. - if (DescribeAddrIfShadow(addr)) - return; - - CHECK(AddrIsInMem(addr)); - - // Check if this is a global. - if (DescribeAddrIfGlobal(addr)) - return; - - if (DescribeStackAddress(addr, access_size)) - return; - - // finally, check if this is a heap. - DescribeHeapAddress(addr, access_size); +// --------------- LowLevelAllocateCallbac ---------- {{{1 +static void OnLowLevelAllocate(uptr ptr, uptr size) { + PoisonShadow(ptr, size, kAsanInternalHeapMagic); } // -------------------------- Run-time entry ------------------- {{{1 @@ -325,29 +218,48 @@ ASAN_REPORT_ERROR(store, true, 16) // time. static NOINLINE void force_interface_symbols() { volatile int fake_condition = 0; // prevent dead condition elimination. - if (fake_condition) { - __asan_report_load1(0); - __asan_report_load2(0); - __asan_report_load4(0); - __asan_report_load8(0); - __asan_report_load16(0); - __asan_report_store1(0); - __asan_report_store2(0); - __asan_report_store4(0); - __asan_report_store8(0); - __asan_report_store16(0); - __asan_register_global(0, 0, 0); - __asan_register_globals(0, 0); - __asan_unregister_globals(0, 0); - __asan_set_death_callback(0); - __asan_set_error_report_callback(0); - __asan_handle_no_return(); + // __asan_report_* functions are noreturn, so we need a switch to prevent + // the compiler from removing any of them. + switch (fake_condition) { + case 1: __asan_report_load1(0); break; + case 2: __asan_report_load2(0); break; + case 3: __asan_report_load4(0); break; + case 4: __asan_report_load8(0); break; + case 5: __asan_report_load16(0); break; + case 6: __asan_report_store1(0); break; + case 7: __asan_report_store2(0); break; + case 8: __asan_report_store4(0); break; + case 9: __asan_report_store8(0); break; + case 10: __asan_report_store16(0); break; + case 12: __asan_register_globals(0, 0); break; + case 13: __asan_unregister_globals(0, 0); break; + case 14: __asan_set_death_callback(0); break; + case 15: __asan_set_error_report_callback(0); break; + case 16: __asan_handle_no_return(); break; + case 17: __asan_address_is_poisoned(0); break; + case 18: __asan_get_allocated_size(0); break; + case 19: __asan_get_current_allocated_bytes(); break; + case 20: __asan_get_estimated_allocated_size(0); break; + case 21: __asan_get_free_bytes(); break; + case 22: __asan_get_heap_size(); break; + case 23: __asan_get_ownership(0); break; + case 24: __asan_get_unmapped_bytes(); break; + case 25: __asan_poison_memory_region(0, 0); break; + case 26: __asan_unpoison_memory_region(0, 0); break; + case 27: __asan_set_error_exit_code(0); break; + case 28: __asan_stack_free(0, 0, 0); break; + case 29: __asan_stack_malloc(0, 0); break; + case 30: __asan_before_dynamic_init(0, 0); break; + case 31: __asan_after_dynamic_init(); break; + case 32: __asan_poison_stack_memory(0, 0); break; + case 33: __asan_unpoison_stack_memory(0, 0); break; + case 34: __asan_region_is_poisoned(0, 0); break; + case 35: __asan_describe_address(0); break; } } -// -------------------------- Init ------------------- {{{1 static void asan_atexit() { - AsanPrintf("AddressSanitizer exit stats:\n"); + Printf("AddressSanitizer exit stats:\n"); __asan_print_accumulated_stats(); } @@ -356,7 +268,14 @@ static void asan_atexit() { // ---------------------- Interface ---------------- {{{1 using namespace __asan; // NOLINT -int __asan_set_error_exit_code(int exit_code) { +#if !SANITIZER_SUPPORTS_WEAK_HOOKS +extern "C" { +SANITIZER_WEAK_ATTRIBUTE SANITIZER_INTERFACE_ATTRIBUTE +const char* __asan_default_options() { return ""; } +} // extern "C" +#endif + +int NOINLINE __asan_set_error_exit_code(int exit_code) { int old = flags()->exitcode; flags()->exitcode = exit_code; return old; @@ -366,8 +285,9 @@ void NOINLINE __asan_handle_no_return() { int local_stack; AsanThread *curr_thread = asanThreadRegistry().GetCurrent(); CHECK(curr_thread); + uptr PageSize = GetPageSizeCached(); uptr top = curr_thread->stack_top(); - uptr bottom = ((uptr)&local_stack - kPageSize) & ~(kPageSize-1); + uptr bottom = ((uptr)&local_stack - PageSize) & ~(PageSize-1); PoisonShadow(bottom, top - bottom, 0); } @@ -375,134 +295,35 @@ void NOINLINE __asan_set_death_callback(void (*callback)(void)) { death_callback = callback; } -void NOINLINE __asan_set_error_report_callback(void (*callback)(const char*)) { - error_report_callback = callback; - if (callback) { - error_message_buffer_size = 1 << 16; - error_message_buffer = - (char*)MmapOrDie(error_message_buffer_size, __FUNCTION__); - error_message_buffer_pos = 0; - } -} - -void __asan_report_error(uptr pc, uptr bp, uptr sp, - uptr addr, bool is_write, uptr access_size) { - static atomic_uint32_t num_calls; - if (atomic_fetch_add(&num_calls, 1, memory_order_relaxed) != 0) { - // Do not print more than one report, otherwise they will mix up. - // We can not return here because the function is marked as never-return. - AsanPrintf("AddressSanitizer: while reporting a bug found another one." - "Ignoring.\n"); - SleepForSeconds(5); - Die(); - } - - AsanPrintf("====================================================" - "=============\n"); - const char *bug_descr = "unknown-crash"; - if (AddrIsInMem(addr)) { - u8 *shadow_addr = (u8*)MemToShadow(addr); - // If we are accessing 16 bytes, look at the second shadow byte. - if (*shadow_addr == 0 && access_size > SHADOW_GRANULARITY) - shadow_addr++; - // If we are in the partial right redzone, look at the next shadow byte. - if (*shadow_addr > 0 && *shadow_addr < 128) - shadow_addr++; - switch (*shadow_addr) { - case kAsanHeapLeftRedzoneMagic: - case kAsanHeapRightRedzoneMagic: - bug_descr = "heap-buffer-overflow"; - break; - case kAsanHeapFreeMagic: - bug_descr = "heap-use-after-free"; - break; - case kAsanStackLeftRedzoneMagic: - bug_descr = "stack-buffer-underflow"; - break; - case kAsanStackMidRedzoneMagic: - case kAsanStackRightRedzoneMagic: - case kAsanStackPartialRedzoneMagic: - bug_descr = "stack-buffer-overflow"; - break; - case kAsanStackAfterReturnMagic: - bug_descr = "stack-use-after-return"; - break; - case kAsanUserPoisonedMemoryMagic: - bug_descr = "use-after-poison"; - break; - case kAsanGlobalRedzoneMagic: - bug_descr = "global-buffer-overflow"; - break; - } - } - - AsanThread *curr_thread = asanThreadRegistry().GetCurrent(); - u32 curr_tid = asanThreadRegistry().GetCurrentTidOrInvalid(); - - if (curr_thread) { - // We started reporting an error message. Stop using the fake stack - // in case we will call an instrumented function from a symbolizer. - curr_thread->fake_stack().StopUsingFakeStack(); - } - - AsanReport("ERROR: AddressSanitizer %s on address " - "%p at pc 0x%zx bp 0x%zx sp 0x%zx\n", - bug_descr, (void*)addr, pc, bp, sp); - - AsanPrintf("%s of size %zu at %p thread T%d\n", - access_size ? (is_write ? "WRITE" : "READ") : "ACCESS", - access_size, (void*)addr, curr_tid); - - if (flags()->debug) { - PrintBytes("PC: ", (uptr*)pc); - } - - GET_STACK_TRACE_WITH_PC_AND_BP(kStackTraceMax, pc, bp); - stack.PrintStack(); - - DescribeAddress(addr, access_size); - - if (AddrIsInMem(addr)) { - uptr shadow_addr = MemToShadow(addr); - AsanReport("ABORTING\n"); - __asan_print_accumulated_stats(); - AsanPrintf("Shadow byte and word:\n"); - AsanPrintf(" %p: %x\n", (void*)shadow_addr, *(unsigned char*)shadow_addr); - uptr aligned_shadow = shadow_addr & ~(kWordSize - 1); - PrintBytes(" ", (uptr*)(aligned_shadow)); - AsanPrintf("More shadow bytes:\n"); - PrintBytes(" ", (uptr*)(aligned_shadow-4*kWordSize)); - PrintBytes(" ", (uptr*)(aligned_shadow-3*kWordSize)); - PrintBytes(" ", (uptr*)(aligned_shadow-2*kWordSize)); - PrintBytes(" ", (uptr*)(aligned_shadow-1*kWordSize)); - PrintBytes("=>", (uptr*)(aligned_shadow+0*kWordSize)); - PrintBytes(" ", (uptr*)(aligned_shadow+1*kWordSize)); - PrintBytes(" ", (uptr*)(aligned_shadow+2*kWordSize)); - PrintBytes(" ", (uptr*)(aligned_shadow+3*kWordSize)); - PrintBytes(" ", (uptr*)(aligned_shadow+4*kWordSize)); - } - if (error_report_callback) { - error_report_callback(error_message_buffer); - } - Die(); -} - - void __asan_init() { if (asan_inited) return; + CHECK(!asan_init_is_running && "ASan init calls itself!"); asan_init_is_running = true; // Make sure we are not statically linked. AsanDoesNotSupportStaticLinkage(); - // Initialize flags. + // Install tool-specific callbacks in sanitizer_common. + SetDieCallback(AsanDie); + SetCheckFailedCallback(AsanCheckFailed); + SetPrintfAndReportCallback(AppendToErrorMessageBuffer); + + // Initialize flags. This must be done early, because most of the + // initialization steps look at flags(). const char *options = GetEnv("ASAN_OPTIONS"); InitializeFlags(flags(), options); + __sanitizer_set_report_path(flags()->log_path); if (flags()->verbosity && options) { Report("Parsed ASAN_OPTIONS: %s\n", options); } + // Re-exec ourselves if we need to set additional env or command line args. + MaybeReexec(); + + // Setup internal allocator callback. + SetLowLevelAllocateCallback(OnLowLevelAllocate); + if (flags()->atexit) { Atexit(asan_atexit); } @@ -543,12 +364,13 @@ void __asan_init() { } uptr shadow_start = kLowShadowBeg; - if (kLowShadowBeg > 0) shadow_start -= kMmapGranularity; + if (kLowShadowBeg > 0) shadow_start -= GetMmapGranularity(); uptr shadow_end = kHighShadowEnd; if (MemoryRangeIsAvailable(shadow_start, shadow_end)) { if (kLowShadowBeg != kLowShadowEnd) { // mmap the low shadow plus at least one page. - ReserveShadowMemoryRange(kLowShadowBeg - kMmapGranularity, kLowShadowEnd); + ReserveShadowMemoryRange(kLowShadowBeg - GetMmapGranularity(), + kLowShadowEnd); } // mmap the high shadow. ReserveShadowMemoryRange(kHighShadowBeg, kHighShadowEnd); @@ -563,6 +385,13 @@ void __asan_init() { } InstallSignalHandlers(); + // Start symbolizer process if necessary. + if (flags()->symbolize) { + const char *external_symbolizer = GetEnv("ASAN_SYMBOLIZER_PATH"); + if (external_symbolizer) { + InitializeExternalSymbolizer(external_symbolizer); + } + } // On Linux AsanThread::ThreadStart() calls malloc() that's why asan_inited // should be set to 1 prior to initializing the threads. |