forked from OSchip/llvm-project
[asan] Implement delayed activation of AddressSanitizer
This change adds ASAN_OPTIONS=start_deactivated=1 flag. When present, ASan will start in "deactivated" mode, with no heap poisoning, no quarantine, no stack trace gathering, and minimal redzones. All this features come back when __asan_init is called for the constructor of an instrumented library. The primary use case for this feature is Android. Code itself is not Android-specific, and this patch includes a Linux test for it. llvm-svn: 199377
This commit is contained in:
parent
3657cb0350
commit
c61623b170
|
@ -2,6 +2,7 @@
|
||||||
|
|
||||||
set(ASAN_SOURCES
|
set(ASAN_SOURCES
|
||||||
asan_allocator2.cc
|
asan_allocator2.cc
|
||||||
|
asan_activation.cc
|
||||||
asan_fake_stack.cc
|
asan_fake_stack.cc
|
||||||
asan_globals.cc
|
asan_globals.cc
|
||||||
asan_interceptors.cc
|
asan_interceptors.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
|
|
@ -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
|
|
@ -312,7 +312,7 @@ void InitializeAllocator() {
|
||||||
static void *Allocate(uptr size, uptr alignment, StackTrace *stack,
|
static void *Allocate(uptr size, uptr alignment, StackTrace *stack,
|
||||||
AllocType alloc_type, bool can_fill) {
|
AllocType alloc_type, bool can_fill) {
|
||||||
if (!asan_inited)
|
if (!asan_inited)
|
||||||
__asan_init();
|
AsanInitFromRtl();
|
||||||
Flags &fl = *flags();
|
Flags &fl = *flags();
|
||||||
CHECK(stack);
|
CHECK(stack);
|
||||||
const uptr min_alignment = SHADOW_GRANULARITY;
|
const uptr min_alignment = SHADOW_GRANULARITY;
|
||||||
|
@ -357,6 +357,16 @@ static void *Allocate(uptr size, uptr alignment, StackTrace *stack,
|
||||||
AllocatorCache *cache = &fallback_allocator_cache;
|
AllocatorCache *cache = &fallback_allocator_cache;
|
||||||
allocated = allocator.Allocate(cache, needed_size, 8, false);
|
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<uptr>(allocated);
|
uptr alloc_beg = reinterpret_cast<uptr>(allocated);
|
||||||
uptr alloc_end = alloc_beg + needed_size;
|
uptr alloc_end = alloc_beg + needed_size;
|
||||||
uptr beg_plus_redzone = alloc_beg + rz_size;
|
uptr beg_plus_redzone = alloc_beg + rz_size;
|
||||||
|
|
|
@ -115,6 +115,11 @@ struct Flags {
|
||||||
// If true, assume that dynamic initializers can never access globals from
|
// If true, assume that dynamic initializers can never access globals from
|
||||||
// other modules, even if the latter are already initialized.
|
// other modules, even if the latter are already initialized.
|
||||||
bool strict_init_order;
|
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;
|
extern Flags asan_flags_dont_use_directly;
|
||||||
|
|
|
@ -73,7 +73,7 @@ static inline bool RangesOverlap(const char *offset1, uptr length1,
|
||||||
#define ENSURE_ASAN_INITED() do { \
|
#define ENSURE_ASAN_INITED() do { \
|
||||||
CHECK(!asan_init_is_running); \
|
CHECK(!asan_init_is_running); \
|
||||||
if (!asan_inited) { \
|
if (!asan_inited) { \
|
||||||
__asan_init(); \
|
AsanInitFromRtl(); \
|
||||||
} \
|
} \
|
||||||
} while (0)
|
} while (0)
|
||||||
|
|
||||||
|
|
|
@ -67,6 +67,8 @@ namespace __asan {
|
||||||
class AsanThread;
|
class AsanThread;
|
||||||
using __sanitizer::StackTrace;
|
using __sanitizer::StackTrace;
|
||||||
|
|
||||||
|
void AsanInitFromRtl();
|
||||||
|
|
||||||
// asan_rtl.cc
|
// asan_rtl.cc
|
||||||
void NORETURN ShowStatsAndAbort();
|
void NORETURN ShowStatsAndAbort();
|
||||||
|
|
||||||
|
|
|
@ -41,7 +41,7 @@ static malloc_zone_t asan_zone;
|
||||||
|
|
||||||
INTERCEPTOR(malloc_zone_t *, malloc_create_zone,
|
INTERCEPTOR(malloc_zone_t *, malloc_create_zone,
|
||||||
vm_size_t start_size, unsigned zone_flags) {
|
vm_size_t start_size, unsigned zone_flags) {
|
||||||
if (!asan_inited) __asan_init();
|
if (!asan_inited) AsanInitFromRtl();
|
||||||
GET_STACK_TRACE_MALLOC;
|
GET_STACK_TRACE_MALLOC;
|
||||||
uptr page_size = GetPageSizeCached();
|
uptr page_size = GetPageSizeCached();
|
||||||
uptr allocated_size = RoundUpTo(sizeof(asan_zone), page_size);
|
uptr allocated_size = RoundUpTo(sizeof(asan_zone), page_size);
|
||||||
|
@ -60,34 +60,34 @@ INTERCEPTOR(malloc_zone_t *, malloc_create_zone,
|
||||||
}
|
}
|
||||||
|
|
||||||
INTERCEPTOR(malloc_zone_t *, malloc_default_zone, void) {
|
INTERCEPTOR(malloc_zone_t *, malloc_default_zone, void) {
|
||||||
if (!asan_inited) __asan_init();
|
if (!asan_inited) AsanInitFromRtl();
|
||||||
return &asan_zone;
|
return &asan_zone;
|
||||||
}
|
}
|
||||||
|
|
||||||
INTERCEPTOR(malloc_zone_t *, malloc_default_purgeable_zone, void) {
|
INTERCEPTOR(malloc_zone_t *, malloc_default_purgeable_zone, void) {
|
||||||
// FIXME: ASan should support purgeable allocations.
|
// FIXME: ASan should support purgeable allocations.
|
||||||
// https://code.google.com/p/address-sanitizer/issues/detail?id=139
|
// https://code.google.com/p/address-sanitizer/issues/detail?id=139
|
||||||
if (!asan_inited) __asan_init();
|
if (!asan_inited) AsanInitFromRtl();
|
||||||
return &asan_zone;
|
return &asan_zone;
|
||||||
}
|
}
|
||||||
|
|
||||||
INTERCEPTOR(void, malloc_make_purgeable, void *ptr) {
|
INTERCEPTOR(void, malloc_make_purgeable, void *ptr) {
|
||||||
// FIXME: ASan should support purgeable allocations. Ignoring them is fine
|
// FIXME: ASan should support purgeable allocations. Ignoring them is fine
|
||||||
// for now.
|
// for now.
|
||||||
if (!asan_inited) __asan_init();
|
if (!asan_inited) AsanInitFromRtl();
|
||||||
}
|
}
|
||||||
|
|
||||||
INTERCEPTOR(int, malloc_make_nonpurgeable, void *ptr) {
|
INTERCEPTOR(int, malloc_make_nonpurgeable, void *ptr) {
|
||||||
// FIXME: ASan should support purgeable allocations. Ignoring them is fine
|
// FIXME: ASan should support purgeable allocations. Ignoring them is fine
|
||||||
// for now.
|
// 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
|
// Must return 0 if the contents were not purged since the last call to
|
||||||
// malloc_make_purgeable().
|
// malloc_make_purgeable().
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
INTERCEPTOR(void, malloc_set_zone_name, malloc_zone_t *zone, const char *name) {
|
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.
|
// Allocate |strlen("asan-") + 1 + internal_strlen(name)| bytes.
|
||||||
size_t buflen = 6 + (name ? internal_strlen(name) : 0);
|
size_t buflen = 6 + (name ? internal_strlen(name) : 0);
|
||||||
InternalScopedBuffer<char> new_name(buflen);
|
InternalScopedBuffer<char> new_name(buflen);
|
||||||
|
@ -102,44 +102,44 @@ INTERCEPTOR(void, malloc_set_zone_name, malloc_zone_t *zone, const char *name) {
|
||||||
}
|
}
|
||||||
|
|
||||||
INTERCEPTOR(void *, malloc, size_t size) {
|
INTERCEPTOR(void *, malloc, size_t size) {
|
||||||
if (!asan_inited) __asan_init();
|
if (!asan_inited) AsanInitFromRtl();
|
||||||
GET_STACK_TRACE_MALLOC;
|
GET_STACK_TRACE_MALLOC;
|
||||||
void *res = asan_malloc(size, &stack);
|
void *res = asan_malloc(size, &stack);
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
INTERCEPTOR(void, free, void *ptr) {
|
INTERCEPTOR(void, free, void *ptr) {
|
||||||
if (!asan_inited) __asan_init();
|
if (!asan_inited) AsanInitFromRtl();
|
||||||
if (!ptr) return;
|
if (!ptr) return;
|
||||||
GET_STACK_TRACE_FREE;
|
GET_STACK_TRACE_FREE;
|
||||||
asan_free(ptr, &stack, FROM_MALLOC);
|
asan_free(ptr, &stack, FROM_MALLOC);
|
||||||
}
|
}
|
||||||
|
|
||||||
INTERCEPTOR(void *, realloc, void *ptr, size_t size) {
|
INTERCEPTOR(void *, realloc, void *ptr, size_t size) {
|
||||||
if (!asan_inited) __asan_init();
|
if (!asan_inited) AsanInitFromRtl();
|
||||||
GET_STACK_TRACE_MALLOC;
|
GET_STACK_TRACE_MALLOC;
|
||||||
return asan_realloc(ptr, size, &stack);
|
return asan_realloc(ptr, size, &stack);
|
||||||
}
|
}
|
||||||
|
|
||||||
INTERCEPTOR(void *, calloc, size_t nmemb, size_t size) {
|
INTERCEPTOR(void *, calloc, size_t nmemb, size_t size) {
|
||||||
if (!asan_inited) __asan_init();
|
if (!asan_inited) AsanInitFromRtl();
|
||||||
GET_STACK_TRACE_MALLOC;
|
GET_STACK_TRACE_MALLOC;
|
||||||
return asan_calloc(nmemb, size, &stack);
|
return asan_calloc(nmemb, size, &stack);
|
||||||
}
|
}
|
||||||
|
|
||||||
INTERCEPTOR(void *, valloc, size_t size) {
|
INTERCEPTOR(void *, valloc, size_t size) {
|
||||||
if (!asan_inited) __asan_init();
|
if (!asan_inited) AsanInitFromRtl();
|
||||||
GET_STACK_TRACE_MALLOC;
|
GET_STACK_TRACE_MALLOC;
|
||||||
return asan_memalign(GetPageSizeCached(), size, &stack, FROM_MALLOC);
|
return asan_memalign(GetPageSizeCached(), size, &stack, FROM_MALLOC);
|
||||||
}
|
}
|
||||||
|
|
||||||
INTERCEPTOR(size_t, malloc_good_size, size_t size) {
|
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);
|
return asan_zone.introspect->good_size(&asan_zone, size);
|
||||||
}
|
}
|
||||||
|
|
||||||
INTERCEPTOR(int, posix_memalign, void **memptr, size_t alignment, size_t size) {
|
INTERCEPTOR(int, posix_memalign, void **memptr, size_t alignment, size_t size) {
|
||||||
if (!asan_inited) __asan_init();
|
if (!asan_inited) AsanInitFromRtl();
|
||||||
CHECK(memptr);
|
CHECK(memptr);
|
||||||
GET_STACK_TRACE_MALLOC;
|
GET_STACK_TRACE_MALLOC;
|
||||||
void *result = asan_memalign(alignment, size, &stack, FROM_MALLOC);
|
void *result = asan_memalign(alignment, size, &stack, FROM_MALLOC);
|
||||||
|
|
|
@ -11,6 +11,7 @@
|
||||||
//
|
//
|
||||||
// Main file of the ASan run-time library.
|
// Main file of the ASan run-time library.
|
||||||
//===----------------------------------------------------------------------===//
|
//===----------------------------------------------------------------------===//
|
||||||
|
#include "asan_activation.h"
|
||||||
#include "asan_allocator.h"
|
#include "asan_allocator.h"
|
||||||
#include "asan_interceptors.h"
|
#include "asan_interceptors.h"
|
||||||
#include "asan_interface_internal.h"
|
#include "asan_interface_internal.h"
|
||||||
|
@ -138,6 +139,7 @@ static void ParseFlagsFromString(Flags *f, const char *str) {
|
||||||
ParseFlag(str, &f->alloc_dealloc_mismatch, "alloc_dealloc_mismatch");
|
ParseFlag(str, &f->alloc_dealloc_mismatch, "alloc_dealloc_mismatch");
|
||||||
ParseFlag(str, &f->strict_memcmp, "strict_memcmp");
|
ParseFlag(str, &f->strict_memcmp, "strict_memcmp");
|
||||||
ParseFlag(str, &f->strict_init_order, "strict_init_order");
|
ParseFlag(str, &f->strict_init_order, "strict_init_order");
|
||||||
|
ParseFlag(str, &f->start_deactivated, "start_deactivated");
|
||||||
}
|
}
|
||||||
|
|
||||||
void InitializeFlags(Flags *f, const char *env) {
|
void InitializeFlags(Flags *f, const char *env) {
|
||||||
|
@ -185,6 +187,7 @@ void InitializeFlags(Flags *f, const char *env) {
|
||||||
f->alloc_dealloc_mismatch = (SANITIZER_MAC == 0) && (SANITIZER_WINDOWS == 0);
|
f->alloc_dealloc_mismatch = (SANITIZER_MAC == 0) && (SANITIZER_WINDOWS == 0);
|
||||||
f->strict_memcmp = true;
|
f->strict_memcmp = true;
|
||||||
f->strict_init_order = false;
|
f->strict_init_order = false;
|
||||||
|
f->start_deactivated = false;
|
||||||
|
|
||||||
// Override from compile definition.
|
// Override from compile definition.
|
||||||
ParseFlagsFromString(f, MaybeUseAsanDefaultOptionsCompileDefiniton());
|
ParseFlagsFromString(f, MaybeUseAsanDefaultOptionsCompileDefiniton());
|
||||||
|
@ -397,55 +400,7 @@ static void PrintAddressSpaceLayout() {
|
||||||
kHighShadowBeg > kMidMemEnd);
|
kHighShadowBeg > kMidMemEnd);
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace __asan
|
static void AsanInitInternal() {
|
||||||
|
|
||||||
// ---------------------- 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() {
|
|
||||||
if (asan_inited) return;
|
if (asan_inited) return;
|
||||||
SanitizerToolName = "AddressSanitizer";
|
SanitizerToolName = "AddressSanitizer";
|
||||||
CHECK(!asan_init_is_running && "ASan init calls itself!");
|
CHECK(!asan_init_is_running && "ASan init calls itself!");
|
||||||
|
@ -473,6 +428,9 @@ void __asan_init() {
|
||||||
VReport(1, "Parsed ASAN_OPTIONS: %s\n", options);
|
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.
|
// Re-exec ourselves if we need to set additional env or command line args.
|
||||||
MaybeReexec();
|
MaybeReexec();
|
||||||
|
|
||||||
|
@ -575,3 +533,64 @@ void __asan_init() {
|
||||||
|
|
||||||
VReport(1, "AddressSanitizer Init done\n");
|
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();
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,7 @@
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
|
||||||
|
extern "C" void do_another_bad_thing() {
|
||||||
|
char *volatile p = (char *)malloc(100);
|
||||||
|
printf("%hhx\n", p[105]);
|
||||||
|
}
|
|
@ -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 <dlfcn.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
#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;
|
||||||
|
}
|
Loading…
Reference in New Issue