Index: lib/asan/CMakeLists.txt =================================================================== --- lib/asan/CMakeLists.txt +++ lib/asan/CMakeLists.txt @@ -2,6 +2,7 @@ set(ASAN_SOURCES asan_allocator2.cc + asan_activation.cc asan_fake_stack.cc asan_globals.cc asan_interceptors.cc Index: lib/asan/asan_activation.h =================================================================== --- lib/asan/asan_activation.h +++ lib/asan/asan_activation.h @@ -0,0 +1,23 @@ +//===-- asan_activation.h ---------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file is a part of AddressSanitizer, an address sanity checker. +// +// ASan activation/deactivation logic. +//===----------------------------------------------------------------------===// + +#ifndef ASAN_ACTIVATION_H +#define ASAN_ACTIVATION_H + +namespace __asan { +void AsanStartDeactivated(); +void AsanActivate(); +} // namespace __asan + +#endif // ASAN_ACTIVATION_H Index: lib/asan/asan_activation.cc =================================================================== --- lib/asan/asan_activation.cc +++ lib/asan/asan_activation.cc @@ -0,0 +1,69 @@ +//===-- asan_activation.cc --------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file is a part of AddressSanitizer, an address sanity checker. +// +// ASan activation/deactivation logic. +//===----------------------------------------------------------------------===// + +#include "asan_activation.h" +#include "asan_flags.h" +#include "asan_internal.h" +#include "sanitizer_common/sanitizer_flags.h" + +namespace __asan { + +static struct AsanDeactivatedFlags { + int quarantine_size; + int max_redzone; + int poison_heap; + int malloc_context_size; +} asan_deactivated_flags; + +static bool asan_is_deactivated; + +void AsanStartDeactivated() { + VReport(1, "Deactivating asan\n"); + // Save flag values. + asan_deactivated_flags.quarantine_size = flags()->quarantine_size; + asan_deactivated_flags.max_redzone = flags()->max_redzone; + asan_deactivated_flags.poison_heap = flags()->poison_heap; + asan_deactivated_flags.malloc_context_size = + common_flags()->malloc_context_size; + + flags()->quarantine_size = 0; + flags()->max_redzone = 16; + flags()->poison_heap = false; + common_flags()->malloc_context_size = 0; + + asan_is_deactivated = true; +} + +void AsanActivate() { + if (!asan_is_deactivated) return; + VReport(1, "Activating asan\n"); + + // Restore flag values. + // FIXME: this is not atomic, and there may be other threads alive. + flags()->quarantine_size = asan_deactivated_flags.quarantine_size; + flags()->max_redzone = asan_deactivated_flags.max_redzone; + flags()->poison_heap = asan_deactivated_flags.poison_heap; + common_flags()->malloc_context_size = + asan_deactivated_flags.malloc_context_size; + + asan_is_deactivated = false; + VReport( + 1, + "quarantine_size %d, max_redzone %d, poison_heap %d, malloc_context_size " + "%d\n", + flags()->quarantine_size, flags()->max_redzone, flags()->poison_heap, + common_flags()->malloc_context_size); +} + +} // namespace __asan Index: lib/asan/asan_allocator2.cc =================================================================== --- lib/asan/asan_allocator2.cc +++ lib/asan/asan_allocator2.cc @@ -312,7 +312,7 @@ static void *Allocate(uptr size, uptr alignment, StackTrace *stack, AllocType alloc_type, bool can_fill) { if (!asan_inited) - __asan_init(); + AsanInitFromRtl(); Flags &fl = *flags(); CHECK(stack); const uptr min_alignment = SHADOW_GRANULARITY; @@ -357,6 +357,16 @@ AllocatorCache *cache = &fallback_allocator_cache; allocated = allocator.Allocate(cache, needed_size, 8, false); } + + if (*(u8 *)MEM_TO_SHADOW((u64)allocated) == 0 && flags()->poison_heap) { + // Heap poisoning is enabled, but the allocator provides an unpoisoned + // chunk. This is possible if flags()->poison_heap was disabled for some + // time, for example, due to flags()->start_disabled. + // Anyway, poison the block before using it for anything else. + uptr allocated_size = allocator.GetActuallyAllocatedSize(allocated); + PoisonShadow((uptr)allocated, allocated_size, kAsanHeapLeftRedzoneMagic); + } + uptr alloc_beg = reinterpret_cast(allocated); uptr alloc_end = alloc_beg + needed_size; uptr beg_plus_redzone = alloc_beg + rz_size; Index: lib/asan/asan_flags.h =================================================================== --- lib/asan/asan_flags.h +++ lib/asan/asan_flags.h @@ -115,6 +115,11 @@ // If true, assume that dynamic initializers can never access globals from // other modules, even if the latter are already initialized. bool strict_init_order; + // If true, ASan tweaks a bunch of other flags (quarantine, redzone, heap + // poisoning) to reduce memory consumption as much as possible, and restores + // them to original values when the first instrumented module is loaded into + // the process. This is mainly intended to be used on Android. + bool start_deactivated; }; extern Flags asan_flags_dont_use_directly; Index: lib/asan/asan_interceptors.cc =================================================================== --- lib/asan/asan_interceptors.cc +++ lib/asan/asan_interceptors.cc @@ -73,7 +73,7 @@ #define ENSURE_ASAN_INITED() do { \ CHECK(!asan_init_is_running); \ if (!asan_inited) { \ - __asan_init(); \ + AsanInitFromRtl(); \ } \ } while (0) Index: lib/asan/asan_internal.h =================================================================== --- lib/asan/asan_internal.h +++ lib/asan/asan_internal.h @@ -67,6 +67,8 @@ class AsanThread; using __sanitizer::StackTrace; +void AsanInitFromRtl(); + // asan_rtl.cc void NORETURN ShowStatsAndAbort(); Index: lib/asan/asan_malloc_mac.cc =================================================================== --- lib/asan/asan_malloc_mac.cc +++ lib/asan/asan_malloc_mac.cc @@ -41,7 +41,7 @@ INTERCEPTOR(malloc_zone_t *, malloc_create_zone, vm_size_t start_size, unsigned zone_flags) { - if (!asan_inited) __asan_init(); + if (!asan_inited) AsanInitFromRtl(); GET_STACK_TRACE_MALLOC; uptr page_size = GetPageSizeCached(); uptr allocated_size = RoundUpTo(sizeof(asan_zone), page_size); @@ -60,34 +60,34 @@ } INTERCEPTOR(malloc_zone_t *, malloc_default_zone, void) { - if (!asan_inited) __asan_init(); + if (!asan_inited) AsanInitFromRtl(); return &asan_zone; } INTERCEPTOR(malloc_zone_t *, malloc_default_purgeable_zone, void) { // FIXME: ASan should support purgeable allocations. // https://code.google.com/p/address-sanitizer/issues/detail?id=139 - if (!asan_inited) __asan_init(); + if (!asan_inited) AsanInitFromRtl(); return &asan_zone; } INTERCEPTOR(void, malloc_make_purgeable, void *ptr) { // FIXME: ASan should support purgeable allocations. Ignoring them is fine // for now. - if (!asan_inited) __asan_init(); + if (!asan_inited) AsanInitFromRtl(); } INTERCEPTOR(int, malloc_make_nonpurgeable, void *ptr) { // FIXME: ASan should support purgeable allocations. Ignoring them is fine // for now. - if (!asan_inited) __asan_init(); + if (!asan_inited) AsanInitFromRtl(); // Must return 0 if the contents were not purged since the last call to // malloc_make_purgeable(). return 0; } INTERCEPTOR(void, malloc_set_zone_name, malloc_zone_t *zone, const char *name) { - if (!asan_inited) __asan_init(); + if (!asan_inited) AsanInitFromRtl(); // Allocate |strlen("asan-") + 1 + internal_strlen(name)| bytes. size_t buflen = 6 + (name ? internal_strlen(name) : 0); InternalScopedBuffer new_name(buflen); @@ -102,44 +102,44 @@ } INTERCEPTOR(void *, malloc, size_t size) { - if (!asan_inited) __asan_init(); + if (!asan_inited) AsanInitFromRtl(); GET_STACK_TRACE_MALLOC; void *res = asan_malloc(size, &stack); return res; } INTERCEPTOR(void, free, void *ptr) { - if (!asan_inited) __asan_init(); + if (!asan_inited) AsanInitFromRtl(); if (!ptr) return; GET_STACK_TRACE_FREE; asan_free(ptr, &stack, FROM_MALLOC); } INTERCEPTOR(void *, realloc, void *ptr, size_t size) { - if (!asan_inited) __asan_init(); + if (!asan_inited) AsanInitFromRtl(); GET_STACK_TRACE_MALLOC; return asan_realloc(ptr, size, &stack); } INTERCEPTOR(void *, calloc, size_t nmemb, size_t size) { - if (!asan_inited) __asan_init(); + if (!asan_inited) AsanInitFromRtl(); GET_STACK_TRACE_MALLOC; return asan_calloc(nmemb, size, &stack); } INTERCEPTOR(void *, valloc, size_t size) { - if (!asan_inited) __asan_init(); + if (!asan_inited) AsanInitFromRtl(); GET_STACK_TRACE_MALLOC; return asan_memalign(GetPageSizeCached(), size, &stack, FROM_MALLOC); } INTERCEPTOR(size_t, malloc_good_size, size_t size) { - if (!asan_inited) __asan_init(); + if (!asan_inited) AsanInitFromRtl(); return asan_zone.introspect->good_size(&asan_zone, size); } INTERCEPTOR(int, posix_memalign, void **memptr, size_t alignment, size_t size) { - if (!asan_inited) __asan_init(); + if (!asan_inited) AsanInitFromRtl(); CHECK(memptr); GET_STACK_TRACE_MALLOC; void *result = asan_memalign(alignment, size, &stack, FROM_MALLOC); Index: lib/asan/asan_rtl.cc =================================================================== --- lib/asan/asan_rtl.cc +++ lib/asan/asan_rtl.cc @@ -11,6 +11,7 @@ // // Main file of the ASan run-time library. //===----------------------------------------------------------------------===// +#include "asan_activation.h" #include "asan_allocator.h" #include "asan_interceptors.h" #include "asan_interface_internal.h" @@ -138,6 +139,7 @@ ParseFlag(str, &f->alloc_dealloc_mismatch, "alloc_dealloc_mismatch"); ParseFlag(str, &f->strict_memcmp, "strict_memcmp"); ParseFlag(str, &f->strict_init_order, "strict_init_order"); + ParseFlag(str, &f->start_deactivated, "start_deactivated"); } void InitializeFlags(Flags *f, const char *env) { @@ -185,6 +187,7 @@ f->alloc_dealloc_mismatch = (SANITIZER_MAC == 0) && (SANITIZER_WINDOWS == 0); f->strict_memcmp = true; f->strict_init_order = false; + f->start_deactivated = false; // Override from compile definition. ParseFlagsFromString(f, MaybeUseAsanDefaultOptionsCompileDefiniton()); @@ -397,55 +400,7 @@ kHighShadowBeg > kMidMemEnd); } -} // namespace __asan - -// ---------------------- Interface ---------------- {{{1 -using namespace __asan; // NOLINT - -#if !SANITIZER_SUPPORTS_WEAK_HOOKS -extern "C" { -SANITIZER_INTERFACE_ATTRIBUTE SANITIZER_WEAK_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; -} - -void NOINLINE __asan_handle_no_return() { - int local_stack; - AsanThread *curr_thread = GetCurrentThread(); - CHECK(curr_thread); - uptr PageSize = GetPageSizeCached(); - uptr top = curr_thread->stack_top(); - uptr bottom = ((uptr)&local_stack - PageSize) & ~(PageSize-1); - static const uptr kMaxExpectedCleanupSize = 64 << 20; // 64M - if (top - bottom > kMaxExpectedCleanupSize) { - static bool reported_warning = false; - if (reported_warning) - return; - reported_warning = true; - Report("WARNING: ASan is ignoring requested __asan_handle_no_return: " - "stack top: %p; bottom %p; size: %p (%zd)\n" - "False positive error reports may follow\n" - "For details see " - "http://code.google.com/p/address-sanitizer/issues/detail?id=189\n", - top, bottom, top - bottom, top - bottom); - return; - } - PoisonShadow(bottom, top - bottom, 0); - if (curr_thread->has_fake_stack()) - curr_thread->fake_stack()->HandleNoReturn(); -} - -void NOINLINE __asan_set_death_callback(void (*callback)(void)) { - death_callback = callback; -} - -void __asan_init() { +static void AsanInitInternal() { if (asan_inited) return; SanitizerToolName = "AddressSanitizer"; CHECK(!asan_init_is_running && "ASan init calls itself!"); @@ -473,6 +428,9 @@ VReport(1, "Parsed ASAN_OPTIONS: %s\n", options); } + if (flags()->start_deactivated) + AsanStartDeactivated(); + // Re-exec ourselves if we need to set additional env or command line args. MaybeReexec(); @@ -575,3 +533,64 @@ VReport(1, "AddressSanitizer Init done\n"); } + +// Initialize as requested from some part of ASan runtime library (interceptors, +// allocator, etc). +void AsanInitFromRtl() { + AsanInitInternal(); +} + +} // namespace __asan + +// ---------------------- Interface ---------------- {{{1 +using namespace __asan; // NOLINT + +#if !SANITIZER_SUPPORTS_WEAK_HOOKS +extern "C" { +SANITIZER_INTERFACE_ATTRIBUTE SANITIZER_WEAK_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; +} + +void NOINLINE __asan_handle_no_return() { + int local_stack; + AsanThread *curr_thread = GetCurrentThread(); + CHECK(curr_thread); + uptr PageSize = GetPageSizeCached(); + uptr top = curr_thread->stack_top(); + uptr bottom = ((uptr)&local_stack - PageSize) & ~(PageSize-1); + static const uptr kMaxExpectedCleanupSize = 64 << 20; // 64M + if (top - bottom > kMaxExpectedCleanupSize) { + static bool reported_warning = false; + if (reported_warning) + return; + reported_warning = true; + Report("WARNING: ASan is ignoring requested __asan_handle_no_return: " + "stack top: %p; bottom %p; size: %p (%zd)\n" + "False positive error reports may follow\n" + "For details see " + "http://code.google.com/p/address-sanitizer/issues/detail?id=189\n", + top, bottom, top - bottom, top - bottom); + return; + } + PoisonShadow(bottom, top - bottom, 0); + if (curr_thread->has_fake_stack()) + curr_thread->fake_stack()->HandleNoReturn(); +} + +void NOINLINE __asan_set_death_callback(void (*callback)(void)) { + death_callback = callback; +} + +// Initialize as requested from instrumented application code. +// We use this call as a trigger to wake up ASan from deactivated state. +void __asan_init() { + AsanActivate(); + AsanInitInternal(); +} Index: lib/asan/lit_tests/TestCases/SharedLibs/start-deactivated-so.cc =================================================================== --- lib/asan/lit_tests/TestCases/SharedLibs/start-deactivated-so.cc +++ lib/asan/lit_tests/TestCases/SharedLibs/start-deactivated-so.cc @@ -0,0 +1,7 @@ +#include +#include + +extern "C" void do_another_bad_thing() { + char *volatile p = (char *)malloc(100); + printf("%hhx\n", p[105]); +} Index: lib/asan/lit_tests/TestCases/start-deactivated.cc =================================================================== --- lib/asan/lit_tests/TestCases/start-deactivated.cc +++ lib/asan/lit_tests/TestCases/start-deactivated.cc @@ -0,0 +1,58 @@ +// Test for ASAN_OPTIONS=start_deactivated=1 mode. +// Main executable is uninstrumented, but linked to ASan runtime. The shared +// library is instrumented. Memory errors before dlopen are not detected. + +// RUN: %clangxx_asan -O0 %p/SharedLibs/start-deactivated-so.cc \ +// RUN: -fPIC -shared -o %t-so.so +// RUN: %clangxx -O0 %s -c -o %t.o +// RUN: %clangxx_asan -O0 %t.o -o %t +// RUN: ASAN_OPTIONS=start_deactivated=1 not %t 2>&1 | FileCheck %s + +#include +#include +#include +#include +#include + +#include + +#include "sanitizer/asan_interface.h" + +void test_malloc_shadow() { + char *p = (char *)malloc(100); + char *q = (char *)__asan_region_is_poisoned(p + 95, 8); + fprintf(stderr, "=%zd=\n", q ? q - (p + 95) : -1); + free(p); +} + +typedef void (*Fn)(); + +int main(int argc, char *argv[]) { + test_malloc_shadow(); + // CHECK: =-1= + + std::string path = std::string(argv[0]) + "-so.so"; + void *dso = dlopen(path.c_str(), RTLD_NOW); + if (!dso) { + fprintf(stderr, "dlopen failed: %s\n", dlerror()); + return 1; + } + + test_malloc_shadow(); + // CHECK: =5= + + void *fn = dlsym(dso, "do_another_bad_thing"); + if (!fn) { + fprintf(stderr, "dlsym failed: %s\n", dlerror()); + return 1; + } + + ((Fn)fn)(); + // CHECK: AddressSanitizer: heap-buffer-overflow + // CHECK: READ of size 1 + // CHECK: {{#0 .* in do_another_bad_thing}} + // CHECK: is located 5 bytes to the right of 100-byte region + // CHECK: in do_another_bad_thing + + return 0; +}