aboutsummaryrefslogtreecommitdiff
path: root/lib/asan
diff options
context:
space:
mode:
Diffstat (limited to 'lib/asan')
-rw-r--r--lib/asan/CMakeLists.txt95
-rw-r--r--lib/asan/Makefile.mk3
-rw-r--r--lib/asan/asan.syms5
-rw-r--r--lib/asan/asan_allocator.cc810
-rw-r--r--lib/asan/asan_allocator.h166
-rw-r--r--lib/asan/asan_allocator2.cc265
-rw-r--r--lib/asan/asan_blacklist.txt10
-rw-r--r--lib/asan/asan_fake_stack.cc43
-rw-r--r--lib/asan/asan_fake_stack.h117
-rw-r--r--lib/asan/asan_flags.h49
-rw-r--r--lib/asan/asan_globals.cc168
-rw-r--r--lib/asan/asan_intercepted_functions.h214
-rw-r--r--lib/asan/asan_interceptors.cc236
-rw-r--r--lib/asan/asan_interceptors.h3
-rw-r--r--lib/asan/asan_interface_internal.h141
-rw-r--r--lib/asan/asan_internal.h55
-rw-r--r--lib/asan/asan_linux.cc31
-rw-r--r--lib/asan/asan_mac.cc298
-rw-r--r--lib/asan/asan_mac.h2
-rw-r--r--lib/asan/asan_malloc_linux.cc10
-rw-r--r--lib/asan/asan_malloc_mac.cc288
-rw-r--r--lib/asan/asan_malloc_win.cc4
-rw-r--r--lib/asan/asan_mapping.h131
-rw-r--r--lib/asan/asan_new_delete.cc42
-rw-r--r--lib/asan/asan_poisoning.cc76
-rw-r--r--lib/asan/asan_poisoning.h58
-rw-r--r--lib/asan/asan_posix.cc19
-rw-r--r--lib/asan/asan_preinit.cc31
-rw-r--r--lib/asan/asan_report.cc300
-rw-r--r--lib/asan/asan_report.h8
-rw-r--r--lib/asan/asan_rtl.cc335
-rw-r--r--lib/asan/asan_stack.cc9
-rw-r--r--lib/asan/asan_stack.h39
-rw-r--r--lib/asan/asan_stats.cc96
-rw-r--r--lib/asan/asan_stats.h11
-rw-r--r--lib/asan/asan_thread.cc166
-rw-r--r--lib/asan/asan_thread.h84
-rw-r--r--lib/asan/asan_thread_registry.cc198
-rw-r--r--lib/asan/asan_thread_registry.h85
-rw-r--r--lib/asan/asan_win.cc28
-rw-r--r--lib/asan/dynamic/Makefile.mk25
-rw-r--r--lib/asan/dynamic/asan_interceptors_dynamic.cc111
-rw-r--r--lib/asan/lit_tests/CMakeLists.txt4
-rw-r--r--lib/asan/lit_tests/Darwin/interface_symbols_darwin.c39
-rw-r--r--lib/asan/lit_tests/Darwin/lit.local.cfg9
-rw-r--r--lib/asan/lit_tests/Darwin/reexec-insert-libraries-env.cc20
-rw-r--r--lib/asan/lit_tests/Darwin/unset-insert-libraries-on-exec.cc20
-rw-r--r--lib/asan/lit_tests/Helpers/init-order-atexit-extra.cc16
-rw-r--r--lib/asan/lit_tests/Helpers/initialization-blacklist-extra2.cc4
-rw-r--r--lib/asan/lit_tests/Helpers/initialization-blacklist.txt1
-rw-r--r--lib/asan/lit_tests/Helpers/initialization-constexpr-extra.cc3
-rw-r--r--lib/asan/lit_tests/Helpers/initialization-nobug-extra.cc6
-rw-r--r--lib/asan/lit_tests/Linux/asan_prelink_test.cc28
-rw-r--r--lib/asan/lit_tests/Linux/glob.cc30
-rw-r--r--lib/asan/lit_tests/Linux/glob_test_root/aa0
-rw-r--r--lib/asan/lit_tests/Linux/glob_test_root/ab0
-rw-r--r--lib/asan/lit_tests/Linux/glob_test_root/ba0
-rw-r--r--lib/asan/lit_tests/Linux/heavy_uar_test.cc55
-rw-r--r--lib/asan/lit_tests/Linux/initialization-bug-any-order.cc11
-rw-r--r--lib/asan/lit_tests/Linux/interface_symbols_linux.c (renamed from lib/asan/lit_tests/interface_symbols.c)12
-rw-r--r--lib/asan/lit_tests/Linux/malloc-in-qsort.cc6
-rw-r--r--lib/asan/lit_tests/Linux/malloc_delete_mismatch.cc (renamed from lib/asan/lit_tests/malloc_delete_mismatch.cc)0
-rw-r--r--lib/asan/lit_tests/Linux/overflow-in-qsort.cc6
-rw-r--r--lib/asan/lit_tests/Linux/preinit_test.cc27
-rw-r--r--lib/asan/lit_tests/Linux/rlimit_mmap_test.cc2
-rw-r--r--lib/asan/lit_tests/Linux/swapcontext_test.cc38
-rw-r--r--lib/asan/lit_tests/Linux/syscalls.cc22
-rw-r--r--lib/asan/lit_tests/Linux/time_null_regtest.cc20
-rw-r--r--lib/asan/lit_tests/Linux/zero-base-shadow.cc31
-rw-r--r--lib/asan/lit_tests/SharedLibs/darwin-dummy-shared-lib-so.cc13
-rw-r--r--lib/asan/lit_tests/SharedLibs/init-order-dlopen-so.cc12
-rw-r--r--lib/asan/lit_tests/Unit/lit.cfg5
-rw-r--r--lib/asan/lit_tests/Unit/lit.site.cfg.in9
-rw-r--r--lib/asan/lit_tests/allow_user_segv.cc50
-rw-r--r--lib/asan/lit_tests/default_blacklist.cc3
-rw-r--r--lib/asan/lit_tests/default_options.cc2
-rw-r--r--lib/asan/lit_tests/dlclose-test.cc13
-rw-r--r--lib/asan/lit_tests/double-free.cc18
-rw-r--r--lib/asan/lit_tests/global-demangle.cc18
-rw-r--r--lib/asan/lit_tests/heap-overflow.cc6
-rw-r--r--lib/asan/lit_tests/huge_negative_hea_oob.cc13
-rw-r--r--lib/asan/lit_tests/init-order-atexit.cc31
-rw-r--r--lib/asan/lit_tests/init-order-dlopen.cc52
-rw-r--r--lib/asan/lit_tests/initialization-blacklist.cc29
-rw-r--r--lib/asan/lit_tests/initialization-bug.cc11
-rw-r--r--lib/asan/lit_tests/initialization-constexpr.cc43
-rw-r--r--lib/asan/lit_tests/initialization-nobug.cc53
-rw-r--r--lib/asan/lit_tests/interface_test.cc8
-rw-r--r--lib/asan/lit_tests/invalid-free.cc16
-rw-r--r--lib/asan/lit_tests/large_func_test.cc9
-rw-r--r--lib/asan/lit_tests/lit.cfg21
-rw-r--r--lib/asan/lit_tests/lit.site.cfg.in2
-rw-r--r--lib/asan/lit_tests/log_path_fork_test.cc.disabled (renamed from lib/asan/lit_tests/log_path_fork_test.cc)0
-rw-r--r--lib/asan/lit_tests/malloc_fill.cc22
-rw-r--r--lib/asan/lit_tests/memcmp_strict_test.cc16
-rw-r--r--lib/asan/lit_tests/partial_right.cc17
-rw-r--r--lib/asan/lit_tests/stack-frame-demangle.cc14
-rw-r--r--lib/asan/lit_tests/stack-oob-frames.cc59
-rw-r--r--lib/asan/lit_tests/stack-overflow.cc3
-rw-r--r--lib/asan/lit_tests/strncpy-overflow.cc8
-rw-r--r--lib/asan/lit_tests/throw_call_test.cc45
-rw-r--r--lib/asan/lit_tests/throw_invoke_test.cc50
-rw-r--r--lib/asan/lit_tests/time_interceptor.cc16
-rw-r--r--lib/asan/lit_tests/unaligned_loads_and_stores.cc52
-rw-r--r--lib/asan/lit_tests/use-after-free-right.cc46
-rw-r--r--lib/asan/lit_tests/use-after-free.cc13
-rw-r--r--lib/asan/lit_tests/use-after-poison.cc20
-rw-r--r--lib/asan/lit_tests/use-after-scope-inlined.cc5
-rw-r--r--lib/asan/lit_tests/wait.cc77
-rwxr-xr-xlib/asan/scripts/asan_symbolize.py13
-rw-r--r--lib/asan/tests/CMakeLists.txt93
-rw-r--r--lib/asan/tests/asan_globals_test.cc23
-rw-r--r--lib/asan/tests/asan_mac_test.cc236
-rw-r--r--lib/asan/tests/asan_mac_test_helpers.mm (renamed from lib/asan/tests/asan_mac_test.mm)0
-rw-r--r--lib/asan/tests/asan_mem_test.cc240
-rw-r--r--lib/asan/tests/asan_noinst_test.cc240
-rw-r--r--lib/asan/tests/asan_oob_test.cc126
-rw-r--r--lib/asan/tests/asan_str_test.cc572
-rw-r--r--lib/asan/tests/asan_test.cc1373
-rw-r--r--lib/asan/tests/asan_test_config.h4
-rw-r--r--lib/asan/tests/asan_test_utils.h84
121 files changed, 4900 insertions, 4279 deletions
diff --git a/lib/asan/CMakeLists.txt b/lib/asan/CMakeLists.txt
index 92cba6dee622..a567a4d3e970 100644
--- a/lib/asan/CMakeLists.txt
+++ b/lib/asan/CMakeLists.txt
@@ -1,7 +1,6 @@
# Build for the AddressSanitizer runtime support library.
set(ASAN_SOURCES
- asan_allocator.cc
asan_allocator2.cc
asan_fake_stack.cc
asan_globals.cc
@@ -14,60 +13,58 @@ set(ASAN_SOURCES
asan_new_delete.cc
asan_poisoning.cc
asan_posix.cc
+ asan_preinit.cc
asan_report.cc
asan_rtl.cc
asan_stack.cc
asan_stats.cc
asan_thread.cc
- asan_thread_registry.cc
asan_win.cc
)
set(ASAN_DYLIB_SOURCES
${ASAN_SOURCES}
- dynamic/asan_interceptors_dynamic.cc
)
include_directories(..)
-set(ASAN_CFLAGS ${SANITIZER_COMMON_CFLAGS})
+set(ASAN_CFLAGS
+ ${SANITIZER_COMMON_CFLAGS}
+ -fno-rtti)
+
+set(ASAN_COMMON_DEFINITIONS
+ ASAN_HAS_EXCEPTIONS=1)
if(ANDROID)
- set(ASAN_COMMON_DEFINITIONS
- ASAN_HAS_EXCEPTIONS=1
+ list(APPEND ASAN_COMMON_DEFINITIONS
ASAN_FLEXIBLE_MAPPING_AND_OFFSET=0
ASAN_NEEDS_SEGV=0
- ASAN_LOW_MEMORY=1
- )
+ ASAN_LOW_MEMORY=1)
else()
- set(ASAN_COMMON_DEFINITIONS
- ASAN_HAS_EXCEPTIONS=1
- ASAN_FLEXIBLE_MAPPING_AND_OFFSET=0
- ASAN_NEEDS_SEGV=1
- )
+ list(APPEND ASAN_COMMON_DEFINITIONS
+ ASAN_FLEXIBLE_MAPPING_AND_OFFSET=1
+ ASAN_NEEDS_SEGV=1)
endif()
-set(ASAN_DYLIB_DEFINITIONS
- ${ASAN_COMMON_DEFINITIONS}
- MAC_INTERPOSE_FUNCTIONS=1
- )
-
# Architectures supported by ASan.
filter_available_targets(ASAN_SUPPORTED_ARCH
- x86_64 i386)
+ x86_64 i386 powerpc64 powerpc)
set(ASAN_RUNTIME_LIBRARIES)
if(APPLE)
# Build universal binary on APPLE.
- add_library(clang_rt.asan_osx STATIC
- ${ASAN_SOURCES}
- $<TARGET_OBJECTS:RTInterception.osx>
- $<TARGET_OBJECTS:RTSanitizerCommon.osx>
- )
- set_target_compile_flags(clang_rt.asan_osx ${ASAN_CFLAGS})
- set_target_properties(clang_rt.asan_osx PROPERTIES
- OSX_ARCHITECTURES "${ASAN_SUPPORTED_ARCH}")
- list(APPEND ASAN_RUNTIME_LIBRARIES clang_rt.asan_osx)
+ add_compiler_rt_osx_dynamic_runtime(clang_rt.asan_osx_dynamic
+ ARCH ${ASAN_SUPPORTED_ARCH}
+ SOURCES ${ASAN_DYLIB_SOURCES}
+ $<TARGET_OBJECTS:RTInterception.osx>
+ $<TARGET_OBJECTS:RTSanitizerCommon.osx>
+ CFLAGS ${ASAN_CFLAGS}
+ DEFS ${ASAN_COMMON_DEFINITIONS}
+ # Dynamic lookup is needed because shadow scale and offset are
+ # provided by the instrumented modules.
+ LINKFLAGS "-framework Foundation"
+ "-undefined dynamic_lookup")
+ list(APPEND ASAN_RUNTIME_LIBRARIES clang_rt.asan_osx_dynamic)
elseif(ANDROID)
add_library(clang_rt.asan-arm-android SHARED
${ASAN_SOURCES}
@@ -75,44 +72,28 @@ elseif(ANDROID)
$<TARGET_OBJECTS:RTSanitizerCommon.arm.android>
)
set_target_compile_flags(clang_rt.asan-arm-android
- ${ASAN_CFLAGS}
- )
+ ${ASAN_CFLAGS})
+ set_property(TARGET clang_rt.asan-arm-android APPEND PROPERTY
+ COMPILE_DEFINITIONS ${ASAN_COMMON_DEFINITIONS})
target_link_libraries(clang_rt.asan-arm-android dl)
list(APPEND ASAN_RUNTIME_LIBRARIES clang_rt.asan-arm-android)
else()
# Otherwise, build separate libraries for each target.
foreach(arch ${ASAN_SUPPORTED_ARCH})
- add_library(clang_rt.asan-${arch} STATIC
- ${ASAN_SOURCES}
- $<TARGET_OBJECTS:RTInterception.${arch}>
- $<TARGET_OBJECTS:RTSanitizerCommon.${arch}>)
- set_target_compile_flags(clang_rt.asan-${arch}
- ${ASAN_CFLAGS} ${TARGET_${arch}_CFLAGS})
+ add_compiler_rt_static_runtime(clang_rt.asan-${arch} ${arch}
+ SOURCES ${ASAN_SOURCES}
+ $<TARGET_OBJECTS:RTInterception.${arch}>
+ $<TARGET_OBJECTS:RTSanitizerCommon.${arch}>
+ $<TARGET_OBJECTS:RTSanitizerCommonLibc.${arch}>
+ $<TARGET_OBJECTS:RTLSanCommon.${arch}>
+ CFLAGS ${ASAN_CFLAGS}
+ DEFS ${ASAN_COMMON_DEFINITIONS}
+ SYMS asan.syms)
list(APPEND ASAN_RUNTIME_LIBRARIES clang_rt.asan-${arch})
endforeach()
endif()
-set_property(TARGET ${ASAN_RUNTIME_LIBRARIES} APPEND PROPERTY
- COMPILE_DEFINITIONS ${ASAN_COMMON_DEFINITIONS})
-add_clang_compiler_rt_libraries(${ASAN_RUNTIME_LIBRARIES})
-
-set(ASAN_DYNAMIC_RUNTIME_LIBRARIES)
-if(APPLE)
- # Build universal binary on APPLE.
- add_library(clang_rt.asan_osx_dynamic SHARED
- ${ASAN_DYLIB_SOURCES}
- $<TARGET_OBJECTS:RTInterception.osx>
- $<TARGET_OBJECTS:RTSanitizerCommon.osx>
- )
- set_target_compile_flags(clang_rt.asan_osx_dynamic ${ASAN_CFLAGS})
- set_target_properties(clang_rt.asan_osx_dynamic PROPERTIES
- COMPILE_DEFINITIONS "${ASAN_DYLIB_DEFINITIONS}"
- OSX_ARCHITECTURES "${ASAN_SUPPORTED_ARCH}"
- LINK_FLAGS "-framework Foundation")
- list(APPEND ASAN_DYNAMIC_RUNTIME_LIBRARIES clang_rt.asan_osx_dynamic)
-endif()
-add_clang_compiler_rt_libraries(${ASAN_DYNAMIC_RUNTIME_LIBRARIES})
-
+add_compiler_rt_resource_file(asan_blacklist asan_blacklist.txt)
if(LLVM_INCLUDE_TESTS)
add_subdirectory(tests)
diff --git a/lib/asan/Makefile.mk b/lib/asan/Makefile.mk
index af9602e8b242..97da64bec573 100644
--- a/lib/asan/Makefile.mk
+++ b/lib/asan/Makefile.mk
@@ -8,7 +8,7 @@
#===------------------------------------------------------------------------===#
ModuleName := asan
-SubDirs := dynamic
+SubDirs :=
Sources := $(foreach file,$(wildcard $(Dir)/*.cc),$(notdir $(file)))
ObjNames := $(Sources:%.cc=%.o)
@@ -18,7 +18,6 @@ Implementation := Generic
# FIXME: use automatic dependencies?
Dependencies := $(wildcard $(Dir)/*.h)
Dependencies += $(wildcard $(Dir)/../interception/*.h)
-Dependencies += $(wildcard $(Dir)/../interception/mach_override/*.h)
Dependencies += $(wildcard $(Dir)/../sanitizer_common/*.h)
# Define a convenience variable for all the asan functions.
diff --git a/lib/asan/asan.syms b/lib/asan/asan.syms
new file mode 100644
index 000000000000..fce367314093
--- /dev/null
+++ b/lib/asan/asan.syms
@@ -0,0 +1,5 @@
+{
+ __asan_*;
+ __sanitizer_syscall_pre_*;
+ __sanitizer_syscall_post_*;
+};
diff --git a/lib/asan/asan_allocator.cc b/lib/asan/asan_allocator.cc
deleted file mode 100644
index 30dd4ceddd88..000000000000
--- a/lib/asan/asan_allocator.cc
+++ /dev/null
@@ -1,810 +0,0 @@
-//===-- asan_allocator.cc -------------------------------------------------===//
-//
-// 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.
-//
-// Implementation of ASan's memory allocator.
-// Evey piece of memory (AsanChunk) allocated by the allocator
-// has a left redzone of REDZONE bytes and
-// a right redzone such that the end of the chunk is aligned by REDZONE
-// (i.e. the right redzone is between 0 and REDZONE-1).
-// The left redzone is always poisoned.
-// The right redzone is poisoned on malloc, the body is poisoned on free.
-// Once freed, a chunk is moved to a quarantine (fifo list).
-// After quarantine, a chunk is returned to freelists.
-//
-// The left redzone contains ASan's internal data and the stack trace of
-// the malloc call.
-// Once freed, the body of the chunk contains the stack trace of the free call.
-//
-//===----------------------------------------------------------------------===//
-#include "asan_allocator.h"
-
-#if ASAN_ALLOCATOR_VERSION == 1
-#include "asan_interceptors.h"
-#include "asan_internal.h"
-#include "asan_mapping.h"
-#include "asan_stats.h"
-#include "asan_report.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_mutex.h"
-
-namespace __asan {
-
-#define REDZONE ((uptr)(flags()->redzone))
-static const uptr kMinAllocSize = REDZONE * 2;
-static const u64 kMaxAvailableRam = 128ULL << 30; // 128G
-static const uptr kMaxThreadLocalQuarantine = 1 << 20; // 1M
-
-static const uptr kMinMmapSize = (ASAN_LOW_MEMORY) ? 4UL << 17 : 4UL << 20;
-static const uptr kMaxSizeForThreadLocalFreeList =
- (ASAN_LOW_MEMORY) ? 1 << 15 : 1 << 17;
-
-// Size classes less than kMallocSizeClassStep are powers of two.
-// All other size classes are multiples of kMallocSizeClassStep.
-static const uptr kMallocSizeClassStepLog = 26;
-static const uptr kMallocSizeClassStep = 1UL << kMallocSizeClassStepLog;
-
-static const uptr kMaxAllowedMallocSize =
- (SANITIZER_WORDSIZE == 32) ? 3UL << 30 : 8UL << 30;
-
-static inline uptr SizeClassToSize(u8 size_class) {
- CHECK(size_class < kNumberOfSizeClasses);
- if (size_class <= kMallocSizeClassStepLog) {
- return 1UL << size_class;
- } else {
- return (size_class - kMallocSizeClassStepLog) * kMallocSizeClassStep;
- }
-}
-
-static inline u8 SizeToSizeClass(uptr size) {
- u8 res = 0;
- if (size <= kMallocSizeClassStep) {
- uptr rounded = RoundUpToPowerOfTwo(size);
- res = Log2(rounded);
- } else {
- res = ((size + kMallocSizeClassStep - 1) / kMallocSizeClassStep)
- + kMallocSizeClassStepLog;
- }
- CHECK(res < kNumberOfSizeClasses);
- CHECK(size <= SizeClassToSize(res));
- return res;
-}
-
-// Given REDZONE bytes, we need to mark first size bytes
-// as addressable and the rest REDZONE-size bytes as unaddressable.
-static void PoisonHeapPartialRightRedzone(uptr mem, uptr size) {
- CHECK(size <= REDZONE);
- CHECK(IsAligned(mem, REDZONE));
- CHECK(IsPowerOfTwo(SHADOW_GRANULARITY));
- CHECK(IsPowerOfTwo(REDZONE));
- CHECK(REDZONE >= SHADOW_GRANULARITY);
- PoisonShadowPartialRightRedzone(mem, size, REDZONE,
- kAsanHeapRightRedzoneMagic);
-}
-
-static u8 *MmapNewPagesAndPoisonShadow(uptr size) {
- CHECK(IsAligned(size, GetPageSizeCached()));
- u8 *res = (u8*)MmapOrDie(size, __FUNCTION__);
- PoisonShadow((uptr)res, size, kAsanHeapLeftRedzoneMagic);
- if (flags()->debug) {
- Printf("ASAN_MMAP: [%p, %p)\n", res, res + size);
- }
- return res;
-}
-
-// Every chunk of memory allocated by this allocator can be in one of 3 states:
-// CHUNK_AVAILABLE: the chunk is in the free list and ready to be allocated.
-// CHUNK_ALLOCATED: the chunk is allocated and not yet freed.
-// CHUNK_QUARANTINE: the chunk was freed and put into quarantine zone.
-//
-// The pseudo state CHUNK_MEMALIGN is used to mark that the address is not
-// the beginning of a AsanChunk (in which the actual chunk resides at
-// this - this->used_size).
-//
-// The magic numbers for the enum values are taken randomly.
-enum {
- CHUNK_AVAILABLE = 0x57,
- CHUNK_ALLOCATED = 0x32,
- CHUNK_QUARANTINE = 0x19,
- CHUNK_MEMALIGN = 0xDC
-};
-
-struct ChunkBase {
- // First 8 bytes.
- uptr chunk_state : 8;
- uptr alloc_tid : 24;
- uptr size_class : 8;
- uptr free_tid : 24;
-
- // Second 8 bytes.
- uptr alignment_log : 8;
- uptr alloc_type : 2;
- uptr used_size : FIRST_32_SECOND_64(32, 54); // Size requested by the user.
-
- // This field may overlap with the user area and thus should not
- // be used while the chunk is in CHUNK_ALLOCATED state.
- AsanChunk *next;
-
- // Typically the beginning of the user-accessible memory is 'this'+REDZONE
- // and is also aligned by REDZONE. However, if the memory is allocated
- // by memalign, the alignment might be higher and the user-accessible memory
- // starts at the first properly aligned address after 'this'.
- uptr Beg() { return RoundUpTo((uptr)this + 1, 1 << alignment_log); }
- uptr Size() { return SizeClassToSize(size_class); }
- u8 SizeClass() { return size_class; }
-};
-
-struct AsanChunk: public ChunkBase {
- u32 *compressed_alloc_stack() {
- return (u32*)((uptr)this + sizeof(ChunkBase));
- }
- u32 *compressed_free_stack() {
- return (u32*)((uptr)this + Max((uptr)REDZONE, (uptr)sizeof(ChunkBase)));
- }
-
- // The left redzone after the ChunkBase is given to the alloc stack trace.
- uptr compressed_alloc_stack_size() {
- if (REDZONE < sizeof(ChunkBase)) return 0;
- return (REDZONE - sizeof(ChunkBase)) / sizeof(u32);
- }
- uptr compressed_free_stack_size() {
- if (REDZONE < sizeof(ChunkBase)) return 0;
- return (REDZONE) / sizeof(u32);
- }
-};
-
-uptr AsanChunkView::Beg() { return chunk_->Beg(); }
-uptr AsanChunkView::End() { return Beg() + UsedSize(); }
-uptr AsanChunkView::UsedSize() { return chunk_->used_size; }
-uptr AsanChunkView::AllocTid() { return chunk_->alloc_tid; }
-uptr AsanChunkView::FreeTid() { return chunk_->free_tid; }
-
-void AsanChunkView::GetAllocStack(StackTrace *stack) {
- StackTrace::UncompressStack(stack, chunk_->compressed_alloc_stack(),
- chunk_->compressed_alloc_stack_size());
-}
-
-void AsanChunkView::GetFreeStack(StackTrace *stack) {
- StackTrace::UncompressStack(stack, chunk_->compressed_free_stack(),
- chunk_->compressed_free_stack_size());
-}
-
-static AsanChunk *PtrToChunk(uptr ptr) {
- AsanChunk *m = (AsanChunk*)(ptr - REDZONE);
- if (m->chunk_state == CHUNK_MEMALIGN) {
- m = (AsanChunk*)((uptr)m - m->used_size);
- }
- return m;
-}
-
-void AsanChunkFifoList::PushList(AsanChunkFifoList *q) {
- CHECK(q->size() > 0);
- size_ += q->size();
- append_back(q);
- q->clear();
-}
-
-void AsanChunkFifoList::Push(AsanChunk *n) {
- push_back(n);
- size_ += n->Size();
-}
-
-// Interesting performance observation: this function takes up to 15% of overal
-// allocator time. That's because *first_ has been evicted from cache long time
-// ago. Not sure if we can or want to do anything with this.
-AsanChunk *AsanChunkFifoList::Pop() {
- CHECK(first_);
- AsanChunk *res = front();
- size_ -= res->Size();
- pop_front();
- return res;
-}
-
-// All pages we ever allocated.
-struct PageGroup {
- uptr beg;
- uptr end;
- uptr size_of_chunk;
- uptr last_chunk;
- bool InRange(uptr addr) {
- return addr >= beg && addr < end;
- }
-};
-
-class MallocInfo {
- public:
- explicit MallocInfo(LinkerInitialized x) : mu_(x) { }
-
- AsanChunk *AllocateChunks(u8 size_class, uptr n_chunks) {
- AsanChunk *m = 0;
- AsanChunk **fl = &free_lists_[size_class];
- {
- BlockingMutexLock lock(&mu_);
- for (uptr i = 0; i < n_chunks; i++) {
- if (!(*fl)) {
- *fl = GetNewChunks(size_class);
- }
- AsanChunk *t = *fl;
- *fl = t->next;
- t->next = m;
- CHECK(t->chunk_state == CHUNK_AVAILABLE);
- m = t;
- }
- }
- return m;
- }
-
- void SwallowThreadLocalMallocStorage(AsanThreadLocalMallocStorage *x,
- bool eat_free_lists) {
- CHECK(flags()->quarantine_size > 0);
- BlockingMutexLock lock(&mu_);
- AsanChunkFifoList *q = &x->quarantine_;
- if (q->size() > 0) {
- quarantine_.PushList(q);
- while (quarantine_.size() > (uptr)flags()->quarantine_size) {
- QuarantinePop();
- }
- }
- if (eat_free_lists) {
- for (uptr size_class = 0; size_class < kNumberOfSizeClasses;
- size_class++) {
- AsanChunk *m = x->free_lists_[size_class];
- while (m) {
- AsanChunk *t = m->next;
- m->next = free_lists_[size_class];
- free_lists_[size_class] = m;
- m = t;
- }
- x->free_lists_[size_class] = 0;
- }
- }
- }
-
- void BypassThreadLocalQuarantine(AsanChunk *chunk) {
- BlockingMutexLock lock(&mu_);
- quarantine_.Push(chunk);
- }
-
- AsanChunk *FindChunkByAddr(uptr addr) {
- BlockingMutexLock lock(&mu_);
- return FindChunkByAddrUnlocked(addr);
- }
-
- uptr AllocationSize(uptr ptr) {
- if (!ptr) return 0;
- BlockingMutexLock lock(&mu_);
-
- // Make sure this is our chunk and |ptr| actually points to the beginning
- // of the allocated memory.
- AsanChunk *m = FindChunkByAddrUnlocked(ptr);
- if (!m || m->Beg() != ptr) return 0;
-
- if (m->chunk_state == CHUNK_ALLOCATED) {
- return m->used_size;
- } else {
- return 0;
- }
- }
-
- void ForceLock() {
- mu_.Lock();
- }
-
- void ForceUnlock() {
- mu_.Unlock();
- }
-
- void PrintStatus() {
- BlockingMutexLock lock(&mu_);
- uptr malloced = 0;
-
- Printf(" MallocInfo: in quarantine: %zu malloced: %zu; ",
- quarantine_.size() >> 20, malloced >> 20);
- for (uptr j = 1; j < kNumberOfSizeClasses; j++) {
- AsanChunk *i = free_lists_[j];
- if (!i) continue;
- uptr t = 0;
- for (; i; i = i->next) {
- t += i->Size();
- }
- Printf("%zu:%zu ", j, t >> 20);
- }
- Printf("\n");
- }
-
- PageGroup *FindPageGroup(uptr addr) {
- BlockingMutexLock lock(&mu_);
- return FindPageGroupUnlocked(addr);
- }
-
- private:
- PageGroup *FindPageGroupUnlocked(uptr addr) {
- int n = atomic_load(&n_page_groups_, memory_order_relaxed);
- // If the page groups are not sorted yet, sort them.
- if (n_sorted_page_groups_ < n) {
- SortArray((uptr*)page_groups_, n);
- n_sorted_page_groups_ = n;
- }
- // Binary search over the page groups.
- int beg = 0, end = n;
- while (beg < end) {
- int med = (beg + end) / 2;
- uptr g = (uptr)page_groups_[med];
- if (addr > g) {
- // 'g' points to the end of the group, so 'addr'
- // may not belong to page_groups_[med] or any previous group.
- beg = med + 1;
- } else {
- // 'addr' may belong to page_groups_[med] or a previous group.
- end = med;
- }
- }
- if (beg >= n)
- return 0;
- PageGroup *g = page_groups_[beg];
- CHECK(g);
- if (g->InRange(addr))
- return g;
- return 0;
- }
-
- // We have an address between two chunks, and we want to report just one.
- AsanChunk *ChooseChunk(uptr addr,
- AsanChunk *left_chunk, AsanChunk *right_chunk) {
- // Prefer an allocated chunk or a chunk from quarantine.
- if (left_chunk->chunk_state == CHUNK_AVAILABLE &&
- right_chunk->chunk_state != CHUNK_AVAILABLE)
- return right_chunk;
- if (right_chunk->chunk_state == CHUNK_AVAILABLE &&
- left_chunk->chunk_state != CHUNK_AVAILABLE)
- return left_chunk;
- // Choose based on offset.
- uptr l_offset = 0, r_offset = 0;
- CHECK(AsanChunkView(left_chunk).AddrIsAtRight(addr, 1, &l_offset));
- CHECK(AsanChunkView(right_chunk).AddrIsAtLeft(addr, 1, &r_offset));
- if (l_offset < r_offset)
- return left_chunk;
- return right_chunk;
- }
-
- AsanChunk *FindChunkByAddrUnlocked(uptr addr) {
- PageGroup *g = FindPageGroupUnlocked(addr);
- if (!g) return 0;
- CHECK(g->size_of_chunk);
- uptr offset_from_beg = addr - g->beg;
- uptr this_chunk_addr = g->beg +
- (offset_from_beg / g->size_of_chunk) * g->size_of_chunk;
- CHECK(g->InRange(this_chunk_addr));
- AsanChunk *m = (AsanChunk*)this_chunk_addr;
- CHECK(m->chunk_state == CHUNK_ALLOCATED ||
- m->chunk_state == CHUNK_AVAILABLE ||
- m->chunk_state == CHUNK_QUARANTINE);
- uptr offset = 0;
- AsanChunkView m_view(m);
- if (m_view.AddrIsInside(addr, 1, &offset))
- return m;
-
- if (m_view.AddrIsAtRight(addr, 1, &offset)) {
- if (this_chunk_addr == g->last_chunk) // rightmost chunk
- return m;
- uptr right_chunk_addr = this_chunk_addr + g->size_of_chunk;
- CHECK(g->InRange(right_chunk_addr));
- return ChooseChunk(addr, m, (AsanChunk*)right_chunk_addr);
- } else {
- CHECK(m_view.AddrIsAtLeft(addr, 1, &offset));
- if (this_chunk_addr == g->beg) // leftmost chunk
- return m;
- uptr left_chunk_addr = this_chunk_addr - g->size_of_chunk;
- CHECK(g->InRange(left_chunk_addr));
- return ChooseChunk(addr, (AsanChunk*)left_chunk_addr, m);
- }
- }
-
- void QuarantinePop() {
- CHECK(quarantine_.size() > 0);
- AsanChunk *m = quarantine_.Pop();
- CHECK(m);
- // if (F_v >= 2) Printf("MallocInfo::pop %p\n", m);
-
- CHECK(m->chunk_state == CHUNK_QUARANTINE);
- m->chunk_state = CHUNK_AVAILABLE;
- PoisonShadow((uptr)m, m->Size(), kAsanHeapLeftRedzoneMagic);
- CHECK(m->alloc_tid >= 0);
- CHECK(m->free_tid >= 0);
-
- uptr size_class = m->SizeClass();
- m->next = free_lists_[size_class];
- free_lists_[size_class] = m;
-
- // Statistics.
- AsanStats &thread_stats = asanThreadRegistry().GetCurrentThreadStats();
- thread_stats.real_frees++;
- thread_stats.really_freed += m->used_size;
- thread_stats.really_freed_redzones += m->Size() - m->used_size;
- thread_stats.really_freed_by_size[m->SizeClass()]++;
- }
-
- // Get a list of newly allocated chunks.
- AsanChunk *GetNewChunks(u8 size_class) {
- uptr size = SizeClassToSize(size_class);
- CHECK(IsPowerOfTwo(kMinMmapSize));
- CHECK(size < kMinMmapSize || (size % kMinMmapSize) == 0);
- uptr mmap_size = Max(size, kMinMmapSize);
- uptr n_chunks = mmap_size / size;
- CHECK(n_chunks * size == mmap_size);
- uptr PageSize = GetPageSizeCached();
- if (size < PageSize) {
- // Size is small, just poison the last chunk.
- n_chunks--;
- } else {
- // Size is large, allocate an extra page at right and poison it.
- mmap_size += PageSize;
- }
- CHECK(n_chunks > 0);
- u8 *mem = MmapNewPagesAndPoisonShadow(mmap_size);
-
- // Statistics.
- AsanStats &thread_stats = asanThreadRegistry().GetCurrentThreadStats();
- thread_stats.mmaps++;
- thread_stats.mmaped += mmap_size;
- thread_stats.mmaped_by_size[size_class] += n_chunks;
-
- AsanChunk *res = 0;
- for (uptr i = 0; i < n_chunks; i++) {
- AsanChunk *m = (AsanChunk*)(mem + i * size);
- m->chunk_state = CHUNK_AVAILABLE;
- m->size_class = size_class;
- m->next = res;
- res = m;
- }
- PageGroup *pg = (PageGroup*)(mem + n_chunks * size);
- // This memory is already poisoned, no need to poison it again.
- pg->beg = (uptr)mem;
- pg->end = pg->beg + mmap_size;
- pg->size_of_chunk = size;
- pg->last_chunk = (uptr)(mem + size * (n_chunks - 1));
- int idx = atomic_fetch_add(&n_page_groups_, 1, memory_order_relaxed);
- CHECK(idx < (int)ARRAY_SIZE(page_groups_));
- page_groups_[idx] = pg;
- return res;
- }
-
- AsanChunk *free_lists_[kNumberOfSizeClasses];
- AsanChunkFifoList quarantine_;
- BlockingMutex mu_;
-
- PageGroup *page_groups_[kMaxAvailableRam / kMinMmapSize];
- atomic_uint32_t n_page_groups_;
- int n_sorted_page_groups_;
-};
-
-static MallocInfo malloc_info(LINKER_INITIALIZED);
-
-void AsanThreadLocalMallocStorage::CommitBack() {
- malloc_info.SwallowThreadLocalMallocStorage(this, true);
-}
-
-AsanChunkView FindHeapChunkByAddress(uptr address) {
- return AsanChunkView(malloc_info.FindChunkByAddr(address));
-}
-
-static u8 *Allocate(uptr alignment, uptr size, StackTrace *stack,
- AllocType alloc_type) {
- __asan_init();
- CHECK(stack);
- if (size == 0) {
- size = 1; // TODO(kcc): do something smarter
- }
- CHECK(IsPowerOfTwo(alignment));
- uptr rounded_size = RoundUpTo(size, REDZONE);
- uptr needed_size = rounded_size + REDZONE;
- if (alignment > REDZONE) {
- needed_size += alignment;
- }
- CHECK(IsAligned(needed_size, REDZONE));
- if (size > kMaxAllowedMallocSize || needed_size > kMaxAllowedMallocSize) {
- Report("WARNING: AddressSanitizer failed to allocate %p bytes\n",
- (void*)size);
- return 0;
- }
-
- u8 size_class = SizeToSizeClass(needed_size);
- uptr size_to_allocate = SizeClassToSize(size_class);
- CHECK(size_to_allocate >= kMinAllocSize);
- CHECK(size_to_allocate >= needed_size);
- CHECK(IsAligned(size_to_allocate, REDZONE));
-
- if (flags()->verbosity >= 3) {
- Printf("Allocate align: %zu size: %zu class: %u real: %zu\n",
- alignment, size, size_class, size_to_allocate);
- }
-
- AsanThread *t = asanThreadRegistry().GetCurrent();
- AsanStats &thread_stats = asanThreadRegistry().GetCurrentThreadStats();
- // Statistics
- thread_stats.mallocs++;
- thread_stats.malloced += size;
- thread_stats.malloced_redzones += size_to_allocate - size;
- thread_stats.malloced_by_size[size_class]++;
-
- AsanChunk *m = 0;
- if (!t || size_to_allocate >= kMaxSizeForThreadLocalFreeList) {
- // get directly from global storage.
- m = malloc_info.AllocateChunks(size_class, 1);
- thread_stats.malloc_large++;
- } else {
- // get from the thread-local storage.
- AsanChunk **fl = &t->malloc_storage().free_lists_[size_class];
- if (!*fl) {
- uptr n_new_chunks = kMaxSizeForThreadLocalFreeList / size_to_allocate;
- *fl = malloc_info.AllocateChunks(size_class, n_new_chunks);
- thread_stats.malloc_small_slow++;
- }
- m = *fl;
- *fl = (*fl)->next;
- }
- CHECK(m);
- CHECK(m->chunk_state == CHUNK_AVAILABLE);
- m->chunk_state = CHUNK_ALLOCATED;
- m->alloc_type = alloc_type;
- m->next = 0;
- CHECK(m->Size() == size_to_allocate);
- uptr addr = (uptr)m + REDZONE;
- CHECK(addr <= (uptr)m->compressed_free_stack());
-
- if (alignment > REDZONE && (addr & (alignment - 1))) {
- addr = RoundUpTo(addr, alignment);
- CHECK((addr & (alignment - 1)) == 0);
- AsanChunk *p = (AsanChunk*)(addr - REDZONE);
- p->chunk_state = CHUNK_MEMALIGN;
- p->used_size = (uptr)p - (uptr)m;
- m->alignment_log = Log2(alignment);
- CHECK(m->Beg() == addr);
- } else {
- m->alignment_log = Log2(REDZONE);
- }
- CHECK(m == PtrToChunk(addr));
- m->used_size = size;
- CHECK(m->Beg() == addr);
- m->alloc_tid = t ? t->tid() : 0;
- m->free_tid = kInvalidTid;
- StackTrace::CompressStack(stack, m->compressed_alloc_stack(),
- m->compressed_alloc_stack_size());
- PoisonShadow(addr, rounded_size, 0);
- if (size < rounded_size) {
- PoisonHeapPartialRightRedzone(addr + rounded_size - REDZONE,
- size & (REDZONE - 1));
- }
- if (size <= (uptr)(flags()->max_malloc_fill_size)) {
- REAL(memset)((void*)addr, 0, rounded_size);
- }
- return (u8*)addr;
-}
-
-static void Deallocate(u8 *ptr, StackTrace *stack, AllocType alloc_type) {
- if (!ptr) return;
- CHECK(stack);
-
- if (flags()->debug) {
- CHECK(malloc_info.FindPageGroup((uptr)ptr));
- }
-
- // Printf("Deallocate %p\n", ptr);
- AsanChunk *m = PtrToChunk((uptr)ptr);
-
- // Flip the chunk_state atomically to avoid race on double-free.
- u8 old_chunk_state = atomic_exchange((atomic_uint8_t*)m, CHUNK_QUARANTINE,
- memory_order_acq_rel);
-
- if (old_chunk_state == CHUNK_QUARANTINE) {
- ReportDoubleFree((uptr)ptr, stack);
- } else if (old_chunk_state != CHUNK_ALLOCATED) {
- ReportFreeNotMalloced((uptr)ptr, stack);
- }
- CHECK(old_chunk_state == CHUNK_ALLOCATED);
- if (m->alloc_type != alloc_type && flags()->alloc_dealloc_mismatch)
- ReportAllocTypeMismatch((uptr)ptr, stack,
- (AllocType)m->alloc_type, (AllocType)alloc_type);
- // With REDZONE==16 m->next is in the user area, otherwise it should be 0.
- CHECK(REDZONE <= 16 || !m->next);
- CHECK(m->free_tid == kInvalidTid);
- CHECK(m->alloc_tid >= 0);
- AsanThread *t = asanThreadRegistry().GetCurrent();
- m->free_tid = t ? t->tid() : 0;
- StackTrace::CompressStack(stack, m->compressed_free_stack(),
- m->compressed_free_stack_size());
- uptr rounded_size = RoundUpTo(m->used_size, REDZONE);
- PoisonShadow((uptr)ptr, rounded_size, kAsanHeapFreeMagic);
-
- // Statistics.
- AsanStats &thread_stats = asanThreadRegistry().GetCurrentThreadStats();
- thread_stats.frees++;
- thread_stats.freed += m->used_size;
- thread_stats.freed_by_size[m->SizeClass()]++;
-
- CHECK(m->chunk_state == CHUNK_QUARANTINE);
-
- if (t) {
- AsanThreadLocalMallocStorage *ms = &t->malloc_storage();
- ms->quarantine_.Push(m);
-
- if (ms->quarantine_.size() > kMaxThreadLocalQuarantine) {
- malloc_info.SwallowThreadLocalMallocStorage(ms, false);
- }
- } else {
- malloc_info.BypassThreadLocalQuarantine(m);
- }
-}
-
-static u8 *Reallocate(u8 *old_ptr, uptr new_size,
- StackTrace *stack) {
- CHECK(old_ptr && new_size);
-
- // Statistics.
- AsanStats &thread_stats = asanThreadRegistry().GetCurrentThreadStats();
- thread_stats.reallocs++;
- thread_stats.realloced += new_size;
-
- AsanChunk *m = PtrToChunk((uptr)old_ptr);
- CHECK(m->chunk_state == CHUNK_ALLOCATED);
- uptr old_size = m->used_size;
- uptr memcpy_size = Min(new_size, old_size);
- u8 *new_ptr = Allocate(0, new_size, stack, FROM_MALLOC);
- if (new_ptr) {
- CHECK(REAL(memcpy) != 0);
- REAL(memcpy)(new_ptr, old_ptr, memcpy_size);
- Deallocate(old_ptr, stack, FROM_MALLOC);
- }
- return new_ptr;
-}
-
-} // namespace __asan
-
-#if !SANITIZER_SUPPORTS_WEAK_HOOKS
-// Provide default (no-op) implementation of malloc hooks.
-extern "C" {
-SANITIZER_WEAK_ATTRIBUTE SANITIZER_INTERFACE_ATTRIBUTE
-void __asan_malloc_hook(void *ptr, uptr size) {
- (void)ptr;
- (void)size;
-}
-SANITIZER_WEAK_ATTRIBUTE SANITIZER_INTERFACE_ATTRIBUTE
-void __asan_free_hook(void *ptr) {
- (void)ptr;
-}
-} // extern "C"
-#endif
-
-namespace __asan {
-
-void PrintInternalAllocatorStats() {
-}
-
-SANITIZER_INTERFACE_ATTRIBUTE
-void *asan_memalign(uptr alignment, uptr size, StackTrace *stack,
- AllocType alloc_type) {
- void *ptr = (void*)Allocate(alignment, size, stack, alloc_type);
- ASAN_MALLOC_HOOK(ptr, size);
- return ptr;
-}
-
-SANITIZER_INTERFACE_ATTRIBUTE
-void asan_free(void *ptr, StackTrace *stack, AllocType alloc_type) {
- ASAN_FREE_HOOK(ptr);
- Deallocate((u8*)ptr, stack, alloc_type);
-}
-
-SANITIZER_INTERFACE_ATTRIBUTE
-void *asan_malloc(uptr size, StackTrace *stack) {
- void *ptr = (void*)Allocate(0, size, stack, FROM_MALLOC);
- ASAN_MALLOC_HOOK(ptr, size);
- return ptr;
-}
-
-void *asan_calloc(uptr nmemb, uptr size, StackTrace *stack) {
- void *ptr = (void*)Allocate(0, nmemb * size, stack, FROM_MALLOC);
- if (ptr)
- REAL(memset)(ptr, 0, nmemb * size);
- ASAN_MALLOC_HOOK(ptr, size);
- return ptr;
-}
-
-void *asan_realloc(void *p, uptr size, StackTrace *stack) {
- if (p == 0) {
- void *ptr = (void*)Allocate(0, size, stack, FROM_MALLOC);
- ASAN_MALLOC_HOOK(ptr, size);
- return ptr;
- } else if (size == 0) {
- ASAN_FREE_HOOK(p);
- Deallocate((u8*)p, stack, FROM_MALLOC);
- return 0;
- }
- return Reallocate((u8*)p, size, stack);
-}
-
-void *asan_valloc(uptr size, StackTrace *stack) {
- void *ptr = (void*)Allocate(GetPageSizeCached(), size, stack, FROM_MALLOC);
- ASAN_MALLOC_HOOK(ptr, size);
- return ptr;
-}
-
-void *asan_pvalloc(uptr size, StackTrace *stack) {
- uptr PageSize = GetPageSizeCached();
- size = RoundUpTo(size, PageSize);
- if (size == 0) {
- // pvalloc(0) should allocate one page.
- size = PageSize;
- }
- void *ptr = (void*)Allocate(PageSize, size, stack, FROM_MALLOC);
- ASAN_MALLOC_HOOK(ptr, size);
- return ptr;
-}
-
-int asan_posix_memalign(void **memptr, uptr alignment, uptr size,
- StackTrace *stack) {
- void *ptr = Allocate(alignment, size, stack, FROM_MALLOC);
- CHECK(IsAligned((uptr)ptr, alignment));
- ASAN_MALLOC_HOOK(ptr, size);
- *memptr = ptr;
- return 0;
-}
-
-uptr asan_malloc_usable_size(void *ptr, StackTrace *stack) {
- CHECK(stack);
- if (ptr == 0) return 0;
- uptr usable_size = malloc_info.AllocationSize((uptr)ptr);
- if (flags()->check_malloc_usable_size && (usable_size == 0)) {
- ReportMallocUsableSizeNotOwned((uptr)ptr, stack);
- }
- return usable_size;
-}
-
-uptr asan_mz_size(const void *ptr) {
- return malloc_info.AllocationSize((uptr)ptr);
-}
-
-void asan_mz_force_lock() {
- malloc_info.ForceLock();
-}
-
-void asan_mz_force_unlock() {
- malloc_info.ForceUnlock();
-}
-
-} // namespace __asan
-
-// ---------------------- Interface ---------------- {{{1
-using namespace __asan; // NOLINT
-
-// ASan allocator doesn't reserve extra bytes, so normally we would
-// just return "size".
-uptr __asan_get_estimated_allocated_size(uptr size) {
- if (size == 0) return 1;
- return Min(size, kMaxAllowedMallocSize);
-}
-
-bool __asan_get_ownership(const void *p) {
- return malloc_info.AllocationSize((uptr)p) > 0;
-}
-
-uptr __asan_get_allocated_size(const void *p) {
- if (p == 0) return 0;
- uptr allocated_size = malloc_info.AllocationSize((uptr)p);
- // Die if p is not malloced or if it is already freed.
- if (allocated_size == 0) {
- GET_STACK_TRACE_FATAL_HERE;
- ReportAsanGetAllocatedSizeNotOwned((uptr)p, &stack);
- }
- return allocated_size;
-}
-#endif // ASAN_ALLOCATOR_VERSION
diff --git a/lib/asan/asan_allocator.h b/lib/asan/asan_allocator.h
index cca24edad81f..f817ce352ee2 100644
--- a/lib/asan/asan_allocator.h
+++ b/lib/asan/asan_allocator.h
@@ -9,7 +9,7 @@
//
// This file is a part of AddressSanitizer, an address sanity checker.
//
-// ASan-private header for asan_allocator.cc.
+// ASan-private header for asan_allocator2.cc.
//===----------------------------------------------------------------------===//
#ifndef ASAN_ALLOCATOR_H
@@ -19,18 +19,6 @@
#include "asan_interceptors.h"
#include "sanitizer_common/sanitizer_list.h"
-// We are in the process of transitioning from the old allocator (version 1)
-// to a new one (version 2). The change is quite intrusive so both allocators
-// will co-exist in the source base for a while. The actual allocator is chosen
-// at build time by redefining this macro.
-#ifndef ASAN_ALLOCATOR_VERSION
-# if ASAN_LINUX && !ASAN_ANDROID
-# define ASAN_ALLOCATOR_VERSION 2
-# else
-# define ASAN_ALLOCATOR_VERSION 1
-# endif
-#endif // ASAN_ALLOCATOR_VERSION
-
namespace __asan {
enum AllocType {
@@ -42,6 +30,8 @@ enum AllocType {
static const uptr kNumberOfSizeClasses = 255;
struct AsanChunk;
+void InitializeAllocator();
+
class AsanChunkView {
public:
explicit AsanChunkView(AsanChunk *chunk) : chunk_(chunk) {}
@@ -53,14 +43,14 @@ class AsanChunkView {
uptr FreeTid();
void GetAllocStack(StackTrace *stack);
void GetFreeStack(StackTrace *stack);
- bool AddrIsInside(uptr addr, uptr access_size, uptr *offset) {
+ bool AddrIsInside(uptr addr, uptr access_size, sptr *offset) {
if (addr >= Beg() && (addr + access_size) <= End()) {
*offset = addr - Beg();
return true;
}
return false;
}
- bool AddrIsAtLeft(uptr addr, uptr access_size, uptr *offset) {
+ bool AddrIsAtLeft(uptr addr, uptr access_size, sptr *offset) {
(void)access_size;
if (addr < Beg()) {
*offset = Beg() - addr;
@@ -68,12 +58,9 @@ class AsanChunkView {
}
return false;
}
- bool AddrIsAtRight(uptr addr, uptr access_size, uptr *offset) {
- if (addr + access_size >= End()) {
- if (addr <= End())
- *offset = 0;
- else
- *offset = addr - End();
+ bool AddrIsAtRight(uptr addr, uptr access_size, sptr *offset) {
+ if (addr + access_size > End()) {
+ *offset = addr - End();
return true;
}
return false;
@@ -104,109 +91,17 @@ class AsanChunkFifoList: public IntrusiveList<AsanChunk> {
struct AsanThreadLocalMallocStorage {
explicit AsanThreadLocalMallocStorage(LinkerInitialized x)
-#if ASAN_ALLOCATOR_VERSION == 1
- : quarantine_(x)
-#endif
{ }
AsanThreadLocalMallocStorage() {
CHECK(REAL(memset));
REAL(memset)(this, 0, sizeof(AsanThreadLocalMallocStorage));
}
-#if ASAN_ALLOCATOR_VERSION == 1
- AsanChunkFifoList quarantine_;
- AsanChunk *free_lists_[kNumberOfSizeClasses];
-#else
uptr quarantine_cache[16];
uptr allocator2_cache[96 * (512 * 8 + 16)]; // Opaque.
-#endif
void CommitBack();
};
-// Fake stack frame contains local variables of one function.
-// This struct should fit into a stack redzone (32 bytes).
-struct FakeFrame {
- uptr magic; // Modified by the instrumented code.
- uptr descr; // Modified by the instrumented code.
- FakeFrame *next;
- u64 real_stack : 48;
- u64 size_minus_one : 16;
-};
-
-struct FakeFrameFifo {
- public:
- void FifoPush(FakeFrame *node);
- FakeFrame *FifoPop();
- private:
- FakeFrame *first_, *last_;
-};
-
-class FakeFrameLifo {
- public:
- void LifoPush(FakeFrame *node) {
- node->next = top_;
- top_ = node;
- }
- void LifoPop() {
- CHECK(top_);
- top_ = top_->next;
- }
- FakeFrame *top() { return top_; }
- private:
- FakeFrame *top_;
-};
-
-// For each thread we create a fake stack and place stack objects on this fake
-// stack instead of the real stack. The fake stack is not really a stack but
-// a fast malloc-like allocator so that when a function exits the fake stack
-// is not poped but remains there for quite some time until gets used again.
-// So, we poison the objects on the fake stack when function returns.
-// It helps us find use-after-return bugs.
-// We can not rely on __asan_stack_free being called on every function exit,
-// so we maintain a lifo list of all current fake frames and update it on every
-// call to __asan_stack_malloc.
-class FakeStack {
- public:
- FakeStack();
- explicit FakeStack(LinkerInitialized) {}
- void Init(uptr stack_size);
- void StopUsingFakeStack() { alive_ = false; }
- void Cleanup();
- uptr AllocateStack(uptr size, uptr real_stack);
- static void OnFree(uptr ptr, uptr size, uptr real_stack);
- // Return the bottom of the maped region.
- uptr AddrIsInFakeStack(uptr addr);
- bool StackSize() { return stack_size_; }
-
- private:
- static const uptr kMinStackFrameSizeLog = 9; // Min frame is 512B.
- static const uptr kMaxStackFrameSizeLog = 16; // Max stack frame is 64K.
- static const uptr kMaxStackMallocSize = 1 << kMaxStackFrameSizeLog;
- static const uptr kNumberOfSizeClasses =
- kMaxStackFrameSizeLog - kMinStackFrameSizeLog + 1;
-
- bool AddrIsInSizeClass(uptr addr, uptr size_class);
-
- // Each size class should be large enough to hold all frames.
- uptr ClassMmapSize(uptr size_class);
-
- uptr ClassSize(uptr size_class) {
- return 1UL << (size_class + kMinStackFrameSizeLog);
- }
-
- void DeallocateFrame(FakeFrame *fake_frame);
-
- uptr ComputeSizeClass(uptr alloc_size);
- void AllocateOneSizeClass(uptr size_class);
-
- uptr stack_size_;
- bool alive_;
-
- uptr allocated_size_classes_[kNumberOfSizeClasses];
- FakeFrameFifo size_classes_[kNumberOfSizeClasses];
- FakeFrameLifo call_stack_;
-};
-
void *asan_memalign(uptr alignment, uptr size, StackTrace *stack,
AllocType alloc_type);
void asan_free(void *ptr, StackTrace *stack, AllocType alloc_type);
@@ -227,50 +122,5 @@ void asan_mz_force_unlock();
void PrintInternalAllocatorStats();
-// Log2 and RoundUpToPowerOfTwo should be inlined for performance.
-#if defined(_WIN32) && !defined(__clang__)
-extern "C" {
-unsigned char _BitScanForward(unsigned long *index, unsigned long mask); // NOLINT
-unsigned char _BitScanReverse(unsigned long *index, unsigned long mask); // NOLINT
-#if defined(_WIN64)
-unsigned char _BitScanForward64(unsigned long *index, unsigned __int64 mask); // NOLINT
-unsigned char _BitScanReverse64(unsigned long *index, unsigned __int64 mask); // NOLINT
-#endif
-}
-#endif
-
-static inline uptr Log2(uptr x) {
- CHECK(IsPowerOfTwo(x));
-#if !defined(_WIN32) || defined(__clang__)
- return __builtin_ctzl(x);
-#elif defined(_WIN64)
- unsigned long ret; // NOLINT
- _BitScanForward64(&ret, x);
- return ret;
-#else
- unsigned long ret; // NOLINT
- _BitScanForward(&ret, x);
- return ret;
-#endif
-}
-
-static inline uptr RoundUpToPowerOfTwo(uptr size) {
- CHECK(size);
- if (IsPowerOfTwo(size)) return size;
-
- unsigned long up; // NOLINT
-#if !defined(_WIN32) || defined(__clang__)
- up = SANITIZER_WORDSIZE - 1 - __builtin_clzl(size);
-#elif defined(_WIN64)
- _BitScanReverse64(&up, size);
-#else
- _BitScanReverse(&up, size);
-#endif
- CHECK(size < (1ULL << (up + 1)));
- CHECK(size > (1ULL << up));
- return 1UL << (up + 1);
-}
-
-
} // namespace __asan
#endif // ASAN_ALLOCATOR_H
diff --git a/lib/asan/asan_allocator2.cc b/lib/asan/asan_allocator2.cc
index 42d8b29afd6b..d74aa553a288 100644
--- a/lib/asan/asan_allocator2.cc
+++ b/lib/asan/asan_allocator2.cc
@@ -13,21 +13,20 @@
// This variant uses the allocator from sanitizer_common, i.e. the one shared
// with ThreadSanitizer and MemorySanitizer.
//
-// Status: under development, not enabled by default yet.
//===----------------------------------------------------------------------===//
#include "asan_allocator.h"
-#if ASAN_ALLOCATOR_VERSION == 2
#include "asan_mapping.h"
+#include "asan_poisoning.h"
#include "asan_report.h"
#include "asan_thread.h"
-#include "asan_thread_registry.h"
-#include "sanitizer/asan_interface.h"
#include "sanitizer_common/sanitizer_allocator.h"
+#include "sanitizer_common/sanitizer_flags.h"
#include "sanitizer_common/sanitizer_internal_defs.h"
#include "sanitizer_common/sanitizer_list.h"
#include "sanitizer_common/sanitizer_stackdepot.h"
#include "sanitizer_common/sanitizer_quarantine.h"
+#include "lsan/lsan_common.h"
namespace __asan {
@@ -35,7 +34,7 @@ struct AsanMapUnmapCallback {
void OnMap(uptr p, uptr size) const {
PoisonShadow(p, size, kAsanHeapLeftRedzoneMagic);
// Statistics.
- AsanStats &thread_stats = asanThreadRegistry().GetCurrentThreadStats();
+ AsanStats &thread_stats = GetCurrentThreadStats();
thread_stats.mmaps++;
thread_stats.mmaped += size;
}
@@ -50,23 +49,32 @@ struct AsanMapUnmapCallback {
uptr shadow_end = RoundDownTo(MemToShadow(p + size), page_size);
FlushUnneededShadowMemory(shadow_beg, shadow_end - shadow_beg);
// Statistics.
- AsanStats &thread_stats = asanThreadRegistry().GetCurrentThreadStats();
+ AsanStats &thread_stats = GetCurrentThreadStats();
thread_stats.munmaps++;
thread_stats.munmaped += size;
}
};
#if SANITIZER_WORDSIZE == 64
+#if defined(__powerpc64__)
+const uptr kAllocatorSpace = 0xa0000000000ULL;
+const uptr kAllocatorSize = 0x20000000000ULL; // 2T.
+#else
const uptr kAllocatorSpace = 0x600000000000ULL;
-const uptr kAllocatorSize = 0x10000000000ULL; // 1T.
+const uptr kAllocatorSize = 0x40000000000ULL; // 4T.
+#endif
typedef DefaultSizeClassMap SizeClassMap;
typedef SizeClassAllocator64<kAllocatorSpace, kAllocatorSize, 0 /*metadata*/,
SizeClassMap, AsanMapUnmapCallback> PrimaryAllocator;
#elif SANITIZER_WORDSIZE == 32
static const u64 kAddressSpaceSize = 1ULL << 32;
typedef CompactSizeClassMap SizeClassMap;
+static const uptr kRegionSizeLog = 20;
+static const uptr kFlatByteMapSize = kAddressSpaceSize >> kRegionSizeLog;
typedef SizeClassAllocator32<0, kAddressSpaceSize, 16,
- SizeClassMap, AsanMapUnmapCallback> PrimaryAllocator;
+ SizeClassMap, kRegionSizeLog,
+ FlatByteMap<kFlatByteMapSize>,
+ AsanMapUnmapCallback> PrimaryAllocator;
#endif
typedef SizeClassAllocatorLocalCache<PrimaryAllocator> AllocatorCache;
@@ -91,8 +99,6 @@ static const uptr kMaxAllowedMallocSize =
static const uptr kMaxThreadLocalQuarantine =
FIRST_32_SECOND_64(1 << 18, 1 << 20);
-static const uptr kReturnOnZeroMalloc = 2048; // Zero page is protected.
-
// Every chunk of memory allocated by this allocator can be in one of 3 states:
// CHUNK_AVAILABLE: the chunk is in the free list and ready to be allocated.
// CHUNK_ALLOCATED: the chunk is allocated and not yet freed.
@@ -114,7 +120,7 @@ static u32 RZSize2Log(u32 rz_size) {
CHECK_GE(rz_size, 16);
CHECK_LE(rz_size, 2048);
CHECK(IsPowerOfTwo(rz_size));
- u32 res = __builtin_ctz(rz_size) - 4;
+ u32 res = Log2(rz_size) - 4;
CHECK_EQ(rz_size, RZLog2Size(res));
return res;
}
@@ -158,6 +164,7 @@ struct ChunkHeader {
u32 from_memalign : 1;
u32 alloc_type : 2;
u32 rz_log : 3;
+ u32 lsan_tag : 2;
// 2-nd 8 bytes
// This field is used for small sizes. For large sizes it is equal to
// SizeClassMap::kMaxSize and the actual size is stored in the
@@ -168,7 +175,6 @@ struct ChunkHeader {
struct ChunkBase : ChunkHeader {
// Header2, intersects with user memory.
- AsanChunk *next;
u32 free_context_id;
};
@@ -189,7 +195,8 @@ struct AsanChunk: ChunkBase {
return allocator.GetBlockBegin(reinterpret_cast<void *>(this));
return reinterpret_cast<void*>(Beg() - RZLog2Size(rz_log));
}
- // We store the alloc/free stack traces in the chunk itself.
+ // If we don't use stack depot, we store the alloc/free stack traces
+ // in the chunk itself.
u32 *AllocStackBeg() {
return (u32*)(Beg() - RZLog2Size(rz_log));
}
@@ -205,6 +212,9 @@ struct AsanChunk: ChunkBase {
uptr available = RoundUpTo(user_requested_size, SHADOW_GRANULARITY);
return (available - kChunkHeader2Size) / sizeof(u32);
}
+ bool AddrIsInside(uptr addr) {
+ return (addr >= Beg()) && (addr < Beg() + UsedSize());
+ }
};
uptr AsanChunkView::Beg() { return chunk_->Beg(); }
@@ -258,8 +268,8 @@ struct QuarantineCallback {
}
void Recycle(AsanChunk *m) {
- CHECK(m->chunk_state == CHUNK_QUARANTINE);
- m->chunk_state = CHUNK_AVAILABLE;
+ CHECK_EQ(m->chunk_state, CHUNK_QUARANTINE);
+ atomic_store((atomic_uint8_t*)m, CHUNK_AVAILABLE, memory_order_relaxed);
CHECK_NE(m->alloc_tid, kInvalidTid);
CHECK_NE(m->free_tid, kInvalidTid);
PoisonShadow(m->Beg(),
@@ -273,7 +283,7 @@ struct QuarantineCallback {
}
// Statistics.
- AsanStats &thread_stats = asanThreadRegistry().GetCurrentThreadStats();
+ AsanStats &thread_stats = GetCurrentThreadStats();
thread_stats.real_frees++;
thread_stats.really_freed += m->UsedSize();
@@ -291,34 +301,32 @@ struct QuarantineCallback {
AllocatorCache *cache_;
};
-static void Init() {
- static int inited = 0;
- if (inited) return;
- __asan_init();
- inited = true; // this must happen before any threads are created.
+void InitializeAllocator() {
allocator.Init();
quarantine.Init((uptr)flags()->quarantine_size, kMaxThreadLocalQuarantine);
}
static void *Allocate(uptr size, uptr alignment, StackTrace *stack,
- AllocType alloc_type) {
- Init();
+ AllocType alloc_type, bool can_fill) {
+ if (!asan_inited)
+ __asan_init();
+ Flags &fl = *flags();
CHECK(stack);
const uptr min_alignment = SHADOW_GRANULARITY;
if (alignment < min_alignment)
alignment = min_alignment;
if (size == 0) {
- if (alignment <= kReturnOnZeroMalloc)
- return reinterpret_cast<void *>(kReturnOnZeroMalloc);
- else
- return 0; // 0 bytes with large alignment requested. Just return 0.
+ // We'd be happy to avoid allocating memory for zero-size requests, but
+ // some programs/tests depend on this behavior and assume that malloc would
+ // not return NULL even for zero-size allocations. Moreover, it looks like
+ // operator new should never return NULL, and results of consecutive "new"
+ // calls must be different even if the allocated size is zero.
+ size = 1;
}
CHECK(IsPowerOfTwo(alignment));
uptr rz_log = ComputeRZLog(size);
uptr rz_size = RZLog2Size(rz_log);
- uptr rounded_size = RoundUpTo(size, alignment);
- if (rounded_size < kChunkHeader2Size)
- rounded_size = kChunkHeader2Size;
+ uptr rounded_size = RoundUpTo(Max(size, kChunkHeader2Size), alignment);
uptr needed_size = rounded_size + rz_size;
if (alignment > min_alignment)
needed_size += alignment;
@@ -336,7 +344,7 @@ static void *Allocate(uptr size, uptr alignment, StackTrace *stack,
return 0;
}
- AsanThread *t = asanThreadRegistry().GetCurrent();
+ AsanThread *t = GetCurrentThread();
void *allocated;
if (t) {
AllocatorCache *cache = GetAllocatorCache(&t->malloc_storage());
@@ -358,7 +366,6 @@ static void *Allocate(uptr size, uptr alignment, StackTrace *stack,
CHECK_LE(user_end, alloc_end);
uptr chunk_beg = user_beg - kChunkHeaderSize;
AsanChunk *m = reinterpret_cast<AsanChunk *>(chunk_beg);
- m->chunk_state = CHUNK_ALLOCATED;
m->alloc_type = alloc_type;
m->rz_log = rz_log;
u32 alloc_tid = t ? t->tid() : 0;
@@ -384,7 +391,7 @@ static void *Allocate(uptr size, uptr alignment, StackTrace *stack,
meta[1] = chunk_beg;
}
- if (flags()->use_stack_depot) {
+ if (fl.use_stack_depot) {
m->alloc_context_id = StackDepotPut(stack->trace, stack->size);
} else {
m->alloc_context_id = 0;
@@ -396,12 +403,12 @@ static void *Allocate(uptr size, uptr alignment, StackTrace *stack,
if (size_rounded_down_to_granularity)
PoisonShadow(user_beg, size_rounded_down_to_granularity, 0);
// Deal with the end of the region if size is not aligned to granularity.
- if (size != size_rounded_down_to_granularity && flags()->poison_heap) {
+ if (size != size_rounded_down_to_granularity && fl.poison_heap) {
u8 *shadow = (u8*)MemToShadow(user_beg + size_rounded_down_to_granularity);
*shadow = size & (SHADOW_GRANULARITY - 1);
}
- AsanStats &thread_stats = asanThreadRegistry().GetCurrentThreadStats();
+ AsanStats &thread_stats = GetCurrentThreadStats();
thread_stats.mallocs++;
thread_stats.malloced += size;
thread_stats.malloced_redzones += needed_size - size;
@@ -411,25 +418,42 @@ static void *Allocate(uptr size, uptr alignment, StackTrace *stack,
thread_stats.malloc_large++;
void *res = reinterpret_cast<void *>(user_beg);
+ if (can_fill && fl.max_malloc_fill_size) {
+ uptr fill_size = Min(size, (uptr)fl.max_malloc_fill_size);
+ REAL(memset)(res, fl.malloc_fill_byte, fill_size);
+ }
+ // Must be the last mutation of metadata in this function.
+ atomic_store((atomic_uint8_t *)m, CHUNK_ALLOCATED, memory_order_release);
ASAN_MALLOC_HOOK(res, size);
return res;
}
-static void Deallocate(void *ptr, StackTrace *stack, AllocType alloc_type) {
- uptr p = reinterpret_cast<uptr>(ptr);
- if (p == 0 || p == kReturnOnZeroMalloc) return;
- uptr chunk_beg = p - kChunkHeaderSize;
- AsanChunk *m = reinterpret_cast<AsanChunk *>(chunk_beg);
-
+static void AtomicallySetQuarantineFlag(AsanChunk *m,
+ void *ptr, StackTrace *stack) {
+ u8 old_chunk_state = CHUNK_ALLOCATED;
// Flip the chunk_state atomically to avoid race on double-free.
- u8 old_chunk_state = atomic_exchange((atomic_uint8_t*)m, CHUNK_QUARANTINE,
- memory_order_relaxed);
-
- if (old_chunk_state == CHUNK_QUARANTINE)
- ReportDoubleFree((uptr)ptr, stack);
- else if (old_chunk_state != CHUNK_ALLOCATED)
- ReportFreeNotMalloced((uptr)ptr, stack);
- CHECK(old_chunk_state == CHUNK_ALLOCATED);
+ if (!atomic_compare_exchange_strong((atomic_uint8_t*)m, &old_chunk_state,
+ CHUNK_QUARANTINE, memory_order_acquire)) {
+ if (old_chunk_state == CHUNK_QUARANTINE)
+ ReportDoubleFree((uptr)ptr, stack);
+ else
+ ReportFreeNotMalloced((uptr)ptr, stack);
+ }
+ CHECK_EQ(CHUNK_ALLOCATED, old_chunk_state);
+}
+
+// Expects the chunk to already be marked as quarantined by using
+// AtomicallySetQuarantineFlag.
+static void QuarantineChunk(AsanChunk *m, void *ptr,
+ StackTrace *stack, AllocType alloc_type) {
+ CHECK_EQ(m->chunk_state, CHUNK_QUARANTINE);
+
+ // FIXME: if the free hook produces an ASan report (e.g. due to a bug),
+ // printing the report may crash as the AsanChunk free-related fields have not
+ // been updated yet. We might need to introduce yet another chunk state to
+ // handle this correctly, but don't want to yet.
+ ASAN_FREE_HOOK(ptr);
+
if (m->alloc_type != alloc_type && flags()->alloc_dealloc_mismatch)
ReportAllocTypeMismatch((uptr)ptr, stack,
(AllocType)m->alloc_type, (AllocType)alloc_type);
@@ -437,7 +461,7 @@ static void Deallocate(void *ptr, StackTrace *stack, AllocType alloc_type) {
CHECK_GE(m->alloc_tid, 0);
if (SANITIZER_WORDSIZE == 64) // On 32-bits this resides in user area.
CHECK_EQ(m->free_tid, kInvalidTid);
- AsanThread *t = asanThreadRegistry().GetCurrent();
+ AsanThread *t = GetCurrentThread();
m->free_tid = t ? t->tid() : 0;
if (flags()->use_stack_depot) {
m->free_context_id = StackDepotPut(stack->trace, stack->size);
@@ -445,13 +469,12 @@ static void Deallocate(void *ptr, StackTrace *stack, AllocType alloc_type) {
m->free_context_id = 0;
StackTrace::CompressStack(stack, m->FreeStackBeg(), m->FreeStackSize());
}
- CHECK(m->chunk_state == CHUNK_QUARANTINE);
// Poison the region.
PoisonShadow(m->Beg(),
RoundUpTo(m->UsedSize(), SHADOW_GRANULARITY),
kAsanHeapFreeMagic);
- AsanStats &thread_stats = asanThreadRegistry().GetCurrentThreadStats();
+ AsanStats &thread_stats = GetCurrentThreadStats();
thread_stats.frees++;
thread_stats.freed += m->UsedSize();
@@ -467,8 +490,17 @@ static void Deallocate(void *ptr, StackTrace *stack, AllocType alloc_type) {
quarantine.Put(&fallback_quarantine_cache, QuarantineCallback(ac),
m, m->UsedSize());
}
+}
- ASAN_FREE_HOOK(ptr);
+static void Deallocate(void *ptr, StackTrace *stack, AllocType alloc_type) {
+ uptr p = reinterpret_cast<uptr>(ptr);
+ if (p == 0) return;
+
+ uptr chunk_beg = p - kChunkHeaderSize;
+ AsanChunk *m = reinterpret_cast<AsanChunk *>(chunk_beg);
+ // Must mark the chunk as quarantined before any changes to its metadata.
+ AtomicallySetQuarantineFlag(m, ptr, stack);
+ QuarantineChunk(m, ptr, stack, alloc_type);
}
static void *Reallocate(void *old_ptr, uptr new_size, StackTrace *stack) {
@@ -477,18 +509,21 @@ static void *Reallocate(void *old_ptr, uptr new_size, StackTrace *stack) {
uptr chunk_beg = p - kChunkHeaderSize;
AsanChunk *m = reinterpret_cast<AsanChunk *>(chunk_beg);
- AsanStats &thread_stats = asanThreadRegistry().GetCurrentThreadStats();
+ AsanStats &thread_stats = GetCurrentThreadStats();
thread_stats.reallocs++;
thread_stats.realloced += new_size;
- CHECK(m->chunk_state == CHUNK_ALLOCATED);
+ // Must mark the chunk as quarantined before any changes to its metadata.
+ // This also ensures that other threads can't deallocate it in the meantime.
+ AtomicallySetQuarantineFlag(m, old_ptr, stack);
+
uptr old_size = m->UsedSize();
uptr memcpy_size = Min(new_size, old_size);
- void *new_ptr = Allocate(new_size, 8, stack, FROM_MALLOC);
+ void *new_ptr = Allocate(new_size, 8, stack, FROM_MALLOC, true);
if (new_ptr) {
- CHECK(REAL(memcpy) != 0);
+ CHECK_NE(REAL(memcpy), (void*)0);
REAL(memcpy)(new_ptr, old_ptr, memcpy_size);
- Deallocate(old_ptr, stack, FROM_MALLOC);
+ QuarantineChunk(m, old_ptr, stack, FROM_MALLOC);
}
return new_ptr;
}
@@ -548,7 +583,7 @@ AsanChunk *ChooseChunk(uptr addr,
return right_chunk;
}
// Same chunk_state: choose based on offset.
- uptr l_offset = 0, r_offset = 0;
+ sptr l_offset = 0, r_offset = 0;
CHECK(AsanChunkView(left_chunk).AddrIsAtRight(addr, 1, &l_offset));
CHECK(AsanChunkView(right_chunk).AddrIsAtLeft(addr, 1, &r_offset));
if (l_offset < r_offset)
@@ -559,7 +594,7 @@ AsanChunk *ChooseChunk(uptr addr,
AsanChunkView FindHeapChunkByAddress(uptr addr) {
AsanChunk *m1 = GetAsanChunkByAddr(addr);
if (!m1) return AsanChunkView(m1);
- uptr offset = 0;
+ sptr offset = 0;
if (AsanChunkView(m1).AddrIsAtLeft(addr, 1, &offset)) {
// The address is in the chunk's left redzone, so maybe it is actually
// a right buffer overflow from the other chunk to the left.
@@ -589,7 +624,7 @@ void PrintInternalAllocatorStats() {
SANITIZER_INTERFACE_ATTRIBUTE
void *asan_memalign(uptr alignment, uptr size, StackTrace *stack,
AllocType alloc_type) {
- return Allocate(size, alignment, stack, alloc_type);
+ return Allocate(size, alignment, stack, alloc_type, true);
}
SANITIZER_INTERFACE_ATTRIBUTE
@@ -599,19 +634,22 @@ void asan_free(void *ptr, StackTrace *stack, AllocType alloc_type) {
SANITIZER_INTERFACE_ATTRIBUTE
void *asan_malloc(uptr size, StackTrace *stack) {
- return Allocate(size, 8, stack, FROM_MALLOC);
+ return Allocate(size, 8, stack, FROM_MALLOC, true);
}
void *asan_calloc(uptr nmemb, uptr size, StackTrace *stack) {
- void *ptr = Allocate(nmemb * size, 8, stack, FROM_MALLOC);
- if (ptr)
+ if (CallocShouldReturnNullDueToOverflow(size, nmemb)) return 0;
+ void *ptr = Allocate(nmemb * size, 8, stack, FROM_MALLOC, false);
+ // If the memory comes from the secondary allocator no need to clear it
+ // as it comes directly from mmap.
+ if (ptr && allocator.FromPrimary(ptr))
REAL(memset)(ptr, 0, nmemb * size);
return ptr;
}
void *asan_realloc(void *p, uptr size, StackTrace *stack) {
if (p == 0)
- return Allocate(size, 8, stack, FROM_MALLOC);
+ return Allocate(size, 8, stack, FROM_MALLOC, true);
if (size == 0) {
Deallocate(p, stack, FROM_MALLOC);
return 0;
@@ -620,7 +658,7 @@ void *asan_realloc(void *p, uptr size, StackTrace *stack) {
}
void *asan_valloc(uptr size, StackTrace *stack) {
- return Allocate(size, GetPageSizeCached(), stack, FROM_MALLOC);
+ return Allocate(size, GetPageSizeCached(), stack, FROM_MALLOC, true);
}
void *asan_pvalloc(uptr size, StackTrace *stack) {
@@ -630,12 +668,12 @@ void *asan_pvalloc(uptr size, StackTrace *stack) {
// pvalloc(0) should allocate one page.
size = PageSize;
}
- return Allocate(size, PageSize, stack, FROM_MALLOC);
+ return Allocate(size, PageSize, stack, FROM_MALLOC, true);
}
int asan_posix_memalign(void **memptr, uptr alignment, uptr size,
StackTrace *stack) {
- void *ptr = Allocate(size, alignment, stack, FROM_MALLOC);
+ void *ptr = Allocate(size, alignment, stack, FROM_MALLOC, true);
CHECK(IsAligned((uptr)ptr, alignment));
*memptr = ptr;
return 0;
@@ -651,20 +689,96 @@ uptr asan_malloc_usable_size(void *ptr, StackTrace *stack) {
}
uptr asan_mz_size(const void *ptr) {
- UNIMPLEMENTED();
- return 0;
+ return AllocationSize(reinterpret_cast<uptr>(ptr));
}
void asan_mz_force_lock() {
- UNIMPLEMENTED();
+ allocator.ForceLock();
+ fallback_mutex.Lock();
}
void asan_mz_force_unlock() {
- UNIMPLEMENTED();
+ fallback_mutex.Unlock();
+ allocator.ForceUnlock();
}
} // namespace __asan
+// --- Implementation of LSan-specific functions --- {{{1
+namespace __lsan {
+void LockAllocator() {
+ __asan::allocator.ForceLock();
+}
+
+void UnlockAllocator() {
+ __asan::allocator.ForceUnlock();
+}
+
+void GetAllocatorGlobalRange(uptr *begin, uptr *end) {
+ *begin = (uptr)&__asan::allocator;
+ *end = *begin + sizeof(__asan::allocator);
+}
+
+void *PointsIntoChunk(void* p) {
+ uptr addr = reinterpret_cast<uptr>(p);
+ __asan::AsanChunk *m = __asan::GetAsanChunkByAddr(addr);
+ if (!m) return 0;
+ uptr chunk = m->Beg();
+ if ((m->chunk_state == __asan::CHUNK_ALLOCATED) && m->AddrIsInside(addr))
+ return reinterpret_cast<void *>(chunk);
+ return 0;
+}
+
+void *GetUserBegin(void *p) {
+ __asan::AsanChunk *m = __asan::GetAsanChunkByAddr(reinterpret_cast<uptr>(p));
+ CHECK(m);
+ return reinterpret_cast<void *>(m->Beg());
+}
+
+LsanMetadata::LsanMetadata(void *chunk) {
+ uptr addr = reinterpret_cast<uptr>(chunk);
+ metadata_ = reinterpret_cast<void *>(addr - __asan::kChunkHeaderSize);
+}
+
+bool LsanMetadata::allocated() const {
+ __asan::AsanChunk *m = reinterpret_cast<__asan::AsanChunk *>(metadata_);
+ return m->chunk_state == __asan::CHUNK_ALLOCATED;
+}
+
+ChunkTag LsanMetadata::tag() const {
+ __asan::AsanChunk *m = reinterpret_cast<__asan::AsanChunk *>(metadata_);
+ return static_cast<ChunkTag>(m->lsan_tag);
+}
+
+void LsanMetadata::set_tag(ChunkTag value) {
+ __asan::AsanChunk *m = reinterpret_cast<__asan::AsanChunk *>(metadata_);
+ m->lsan_tag = value;
+}
+
+uptr LsanMetadata::requested_size() const {
+ __asan::AsanChunk *m = reinterpret_cast<__asan::AsanChunk *>(metadata_);
+ return m->UsedSize();
+}
+
+u32 LsanMetadata::stack_trace_id() const {
+ __asan::AsanChunk *m = reinterpret_cast<__asan::AsanChunk *>(metadata_);
+ return m->alloc_context_id;
+}
+
+template <typename Callable> void ForEachChunk(Callable const &callback) {
+ __asan::allocator.ForEachChunk(callback);
+}
+#if CAN_SANITIZE_LEAKS
+template void ForEachChunk<ProcessPlatformSpecificAllocationsCb>(
+ ProcessPlatformSpecificAllocationsCb const &callback);
+template void ForEachChunk<PrintLeakedCb>(PrintLeakedCb const &callback);
+template void ForEachChunk<CollectLeaksCb>(CollectLeaksCb const &callback);
+template void ForEachChunk<MarkIndirectlyLeakedCb>(
+ MarkIndirectlyLeakedCb const &callback);
+template void ForEachChunk<ClearTagCb>(ClearTagCb const &callback);
+#endif // CAN_SANITIZE_LEAKS
+} // namespace __lsan
+
// ---------------------- Interface ---------------- {{{1
using namespace __asan; // NOLINT
@@ -676,7 +790,7 @@ uptr __asan_get_estimated_allocated_size(uptr size) {
bool __asan_get_ownership(const void *p) {
uptr ptr = reinterpret_cast<uptr>(p);
- return (ptr == kReturnOnZeroMalloc) || (AllocationSize(ptr) > 0);
+ return (AllocationSize(ptr) > 0);
}
uptr __asan_get_allocated_size(const void *p) {
@@ -684,7 +798,7 @@ uptr __asan_get_allocated_size(const void *p) {
uptr ptr = reinterpret_cast<uptr>(p);
uptr allocated_size = AllocationSize(ptr);
// Die if p is not malloced or if it is already freed.
- if (allocated_size == 0 && ptr != kReturnOnZeroMalloc) {
+ if (allocated_size == 0) {
GET_STACK_TRACE_FATAL_HERE;
ReportAsanGetAllocatedSizeNotOwned(ptr, &stack);
}
@@ -705,6 +819,3 @@ void __asan_free_hook(void *ptr) {
}
} // extern "C"
#endif
-
-
-#endif // ASAN_ALLOCATOR_VERSION
diff --git a/lib/asan/asan_blacklist.txt b/lib/asan/asan_blacklist.txt
new file mode 100644
index 000000000000..03da08598d23
--- /dev/null
+++ b/lib/asan/asan_blacklist.txt
@@ -0,0 +1,10 @@
+# Blacklist for AddressSanitizer. Turns off instrumentation of particular
+# functions or sources. Use with care. You may set location of blacklist
+# at compile-time using -fsanitize-blacklist=<path> flag.
+
+# Example usage:
+# fun:*bad_function_name*
+# src:file_with_tricky_code.cc
+# global:*global_with_bad_access_or_initialization*
+# global-init:*global_with_initialization_issues*
+# global-init-type:*Namespace::ClassName*
diff --git a/lib/asan/asan_fake_stack.cc b/lib/asan/asan_fake_stack.cc
index 7c5a16312d46..23eebe64e612 100644
--- a/lib/asan/asan_fake_stack.cc
+++ b/lib/asan/asan_fake_stack.cc
@@ -12,14 +12,13 @@
// FakeStack is used to detect use-after-return bugs.
//===----------------------------------------------------------------------===//
#include "asan_allocator.h"
+#include "asan_poisoning.h"
#include "asan_thread.h"
-#include "asan_thread_registry.h"
-#include "sanitizer/asan_interface.h"
namespace __asan {
FakeStack::FakeStack() {
- CHECK(REAL(memset) != 0);
+ CHECK(REAL(memset));
REAL(memset)(this, 0, sizeof(*this));
}
@@ -31,24 +30,26 @@ bool FakeStack::AddrIsInSizeClass(uptr addr, uptr size_class) {
}
uptr FakeStack::AddrIsInFakeStack(uptr addr) {
- for (uptr i = 0; i < kNumberOfSizeClasses; i++) {
- if (AddrIsInSizeClass(addr, i)) return allocated_size_classes_[i];
+ for (uptr size_class = 0; size_class < kNumberOfSizeClasses; size_class++) {
+ if (!AddrIsInSizeClass(addr, size_class)) continue;
+ uptr size_class_first_ptr = allocated_size_classes_[size_class];
+ uptr size = ClassSize(size_class);
+ CHECK_LE(size_class_first_ptr, addr);
+ CHECK_GT(size_class_first_ptr + ClassMmapSize(size_class), addr);
+ return size_class_first_ptr + ((addr - size_class_first_ptr) / size) * size;
}
return 0;
}
// We may want to compute this during compilation.
-inline uptr FakeStack::ComputeSizeClass(uptr alloc_size) {
+ALWAYS_INLINE uptr FakeStack::ComputeSizeClass(uptr alloc_size) {
uptr rounded_size = RoundUpToPowerOfTwo(alloc_size);
uptr log = Log2(rounded_size);
- CHECK(alloc_size <= (1UL << log));
- if (!(alloc_size > (1UL << (log-1)))) {
- Printf("alloc_size %zu log %zu\n", alloc_size, log);
- }
- CHECK(alloc_size > (1UL << (log-1)));
+ CHECK_LE(alloc_size, (1UL << log));
+ CHECK_GT(alloc_size, (1UL << (log-1)));
uptr res = log < kMinStackFrameSizeLog ? 0 : log - kMinStackFrameSizeLog;
- CHECK(res < kNumberOfSizeClasses);
- CHECK(ClassSize(res) >= rounded_size);
+ CHECK_LT(res, kNumberOfSizeClasses);
+ CHECK_GE(ClassSize(res), rounded_size);
return res;
}
@@ -104,7 +105,7 @@ void FakeStack::AllocateOneSizeClass(uptr size_class) {
uptr new_mem = (uptr)MmapOrDie(
ClassMmapSize(size_class), __FUNCTION__);
// Printf("T%d new_mem[%zu]: %p-%p mmap %zu\n",
- // asanThreadRegistry().GetCurrent()->tid(),
+ // GetCurrentThread()->tid(),
// size_class, new_mem, new_mem + ClassMmapSize(size_class),
// ClassMmapSize(size_class));
uptr i;
@@ -116,7 +117,7 @@ void FakeStack::AllocateOneSizeClass(uptr size_class) {
allocated_size_classes_[size_class] = new_mem;
}
-uptr FakeStack::AllocateStack(uptr size, uptr real_stack) {
+ALWAYS_INLINE uptr FakeStack::AllocateStack(uptr size, uptr real_stack) {
if (!alive_) return real_stack;
CHECK(size <= kMaxStackMallocSize && size > 1);
uptr size_class = ComputeSizeClass(size);
@@ -138,7 +139,7 @@ uptr FakeStack::AllocateStack(uptr size, uptr real_stack) {
return ptr;
}
-void FakeStack::DeallocateFrame(FakeFrame *fake_frame) {
+ALWAYS_INLINE void FakeStack::DeallocateFrame(FakeFrame *fake_frame) {
CHECK(alive_);
uptr size = fake_frame->size_minus_one + 1;
uptr size_class = ComputeSizeClass(size);
@@ -149,11 +150,11 @@ void FakeStack::DeallocateFrame(FakeFrame *fake_frame) {
size_classes_[size_class].FifoPush(fake_frame);
}
-void FakeStack::OnFree(uptr ptr, uptr size, uptr real_stack) {
+ALWAYS_INLINE void FakeStack::OnFree(uptr ptr, uptr size, uptr real_stack) {
FakeFrame *fake_frame = (FakeFrame*)ptr;
- CHECK(fake_frame->magic = kRetiredStackFrameMagic);
- CHECK(fake_frame->descr != 0);
- CHECK(fake_frame->size_minus_one == size - 1);
+ CHECK_EQ(fake_frame->magic, kRetiredStackFrameMagic);
+ CHECK_NE(fake_frame->descr, 0);
+ CHECK_EQ(fake_frame->size_minus_one, size - 1);
PoisonShadow(ptr, size, kAsanStackAfterReturnMagic);
}
@@ -164,7 +165,7 @@ using namespace __asan; // NOLINT
uptr __asan_stack_malloc(uptr size, uptr real_stack) {
if (!flags()->use_fake_stack) return real_stack;
- AsanThread *t = asanThreadRegistry().GetCurrent();
+ AsanThread *t = GetCurrentThread();
if (!t) {
// TSD is gone, use the real stack.
return real_stack;
diff --git a/lib/asan/asan_fake_stack.h b/lib/asan/asan_fake_stack.h
new file mode 100644
index 000000000000..308b4c571832
--- /dev/null
+++ b/lib/asan/asan_fake_stack.h
@@ -0,0 +1,117 @@
+//===-- asan_fake_stack.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-private header for asan_fake_stack.cc
+//===----------------------------------------------------------------------===//
+
+#ifndef ASAN_FAKE_STACK_H
+#define ASAN_FAKE_STACK_H
+
+namespace __asan {
+
+// Fake stack frame contains local variables of one function.
+struct FakeFrame {
+ uptr magic; // Modified by the instrumented code.
+ uptr descr; // Modified by the instrumented code.
+ uptr pc; // Modified by the instrumented code.
+ u64 real_stack : 48;
+ u64 size_minus_one : 16;
+ // End of the first 32 bytes.
+ // The rest should not be used when the frame is active.
+ FakeFrame *next;
+};
+
+struct FakeFrameFifo {
+ public:
+ void FifoPush(FakeFrame *node);
+ FakeFrame *FifoPop();
+ private:
+ FakeFrame *first_, *last_;
+};
+
+template<uptr kMaxNumberOfFrames>
+class FakeFrameLifo {
+ public:
+ explicit FakeFrameLifo(LinkerInitialized) {}
+ FakeFrameLifo() : n_frames_(0) {}
+ void LifoPush(FakeFrame *node) {
+ CHECK_LT(n_frames_, kMaxNumberOfFrames);
+ frames_[n_frames_++] = node;
+ }
+ void LifoPop() {
+ CHECK(n_frames_);
+ n_frames_--;
+ }
+ FakeFrame *top() {
+ if (n_frames_ == 0)
+ return 0;
+ return frames_[n_frames_ - 1];
+ }
+ private:
+ uptr n_frames_;
+ FakeFrame *frames_[kMaxNumberOfFrames];
+};
+
+// For each thread we create a fake stack and place stack objects on this fake
+// stack instead of the real stack. The fake stack is not really a stack but
+// a fast malloc-like allocator so that when a function exits the fake stack
+// is not poped but remains there for quite some time until gets used again.
+// So, we poison the objects on the fake stack when function returns.
+// It helps us find use-after-return bugs.
+// We can not rely on __asan_stack_free being called on every function exit,
+// so we maintain a lifo list of all current fake frames and update it on every
+// call to __asan_stack_malloc.
+class FakeStack {
+ public:
+ FakeStack();
+ explicit FakeStack(LinkerInitialized x) : call_stack_(x) {}
+ void Init(uptr stack_size);
+ void StopUsingFakeStack() { alive_ = false; }
+ void Cleanup();
+ uptr AllocateStack(uptr size, uptr real_stack);
+ static void OnFree(uptr ptr, uptr size, uptr real_stack);
+ // Return the bottom of the maped region.
+ uptr AddrIsInFakeStack(uptr addr);
+ bool StackSize() { return stack_size_; }
+
+ private:
+ static const uptr kMinStackFrameSizeLog = 9; // Min frame is 512B.
+ static const uptr kMaxStackFrameSizeLog = 16; // Max stack frame is 64K.
+ static const uptr kMaxStackMallocSize = 1 << kMaxStackFrameSizeLog;
+ static const uptr kNumberOfSizeClasses =
+ kMaxStackFrameSizeLog - kMinStackFrameSizeLog + 1;
+ static const uptr kMaxRecursionDepth = 1023;
+
+ bool AddrIsInSizeClass(uptr addr, uptr size_class);
+
+ // Each size class should be large enough to hold all frames.
+ uptr ClassMmapSize(uptr size_class);
+
+ uptr ClassSize(uptr size_class) {
+ return 1UL << (size_class + kMinStackFrameSizeLog);
+ }
+
+ void DeallocateFrame(FakeFrame *fake_frame);
+
+ uptr ComputeSizeClass(uptr alloc_size);
+ void AllocateOneSizeClass(uptr size_class);
+
+ uptr stack_size_;
+ bool alive_;
+
+ uptr allocated_size_classes_[kNumberOfSizeClasses];
+ FakeFrameFifo size_classes_[kNumberOfSizeClasses];
+ FakeFrameLifo<kMaxRecursionDepth> call_stack_;
+};
+
+} // namespace __asan
+
+#endif // ASAN_FAKE_STACK_H
diff --git a/lib/asan/asan_flags.h b/lib/asan/asan_flags.h
index d7b21ea4a45f..2f3bc9051ae1 100644
--- a/lib/asan/asan_flags.h
+++ b/lib/asan/asan_flags.h
@@ -15,13 +15,15 @@
#ifndef ASAN_FLAGS_H
#define ASAN_FLAGS_H
-#include "sanitizer/common_interface_defs.h"
+#include "sanitizer_common/sanitizer_internal_defs.h"
-// ASan flag values can be defined in three ways:
+// ASan flag values can be defined in four ways:
// 1) initialized with default values at startup.
-// 2) overriden from string returned by user-specified function
+// 2) overriden during compilation of ASan runtime by providing
+// compile definition ASAN_DEFAULT_OPTIONS.
+// 3) overriden from string returned by user-specified function
// __asan_default_options().
-// 3) overriden from env variable ASAN_OPTIONS.
+// 4) overriden from env variable ASAN_OPTIONS.
namespace __asan {
@@ -30,8 +32,6 @@ struct Flags {
// Lower value may reduce memory usage but increase the chance of
// false negatives.
int quarantine_size;
- // If set, uses in-process symbolizer from common sanitizer runtime.
- bool symbolize;
// Verbosity level (0 - silent, 1 - a bit of output, 2+ - more output).
int verbosity;
// Size (in bytes) of redzones around heap objects.
@@ -45,22 +45,18 @@ struct Flags {
int report_globals;
// If set, attempts to catch initialization order issues.
bool check_initialization_order;
- // Max number of stack frames kept for each allocation/deallocation.
- int malloc_context_size;
// If set, uses custom wrappers and replacements for libc string functions
// to find more errors.
bool replace_str;
// If set, uses custom wrappers for memset/memcpy/memmove intinsics.
bool replace_intrin;
- // Used on Mac only. See comments in asan_mac.cc and asan_malloc_mac.cc.
- bool replace_cfallocator;
// Used on Mac only.
bool mac_ignore_invalid_free;
- // ASan allocator flag. See asan_allocator.cc.
+ // ASan allocator flag.
bool use_fake_stack;
- // ASan allocator flag. Sets the maximal size of allocation request
- // that would return memory filled with zero bytes.
- int max_malloc_fill_size;
+ // ASan allocator flag. max_malloc_fill_size is the maximal amount of bytes
+ // that will be filled with malloc_fill_byte on malloc.
+ int max_malloc_fill_size, malloc_fill_byte;
// Override exit status if something was reported.
int exitcode;
// If set, user may manually mark memory regions as poisoned or unpoisoned.
@@ -71,6 +67,8 @@ struct Flags {
int sleep_before_dying;
// If set, registers ASan custom segv handler.
bool handle_segv;
+ // If set, allows user register segv handler even if ASan registers one.
+ bool allow_user_segv_handler;
// If set, uses alternate stack for signal handling.
bool use_sigaltstack;
// Allow the users to work around the bug in Nvidia drivers prior to 295.*.
@@ -79,6 +77,10 @@ struct Flags {
bool unmap_shadow_on_exit;
// If set, calls abort() instead of _exit() after printing an error report.
bool abort_on_error;
+ // Print various statistics after printing an error message or if atexit=1.
+ bool print_stats;
+ // Print the legend for the shadow bytes.
+ bool print_legend;
// If set, prints ASan exit stats even after program terminates successfully.
bool atexit;
// By default, disable core dumper on 64-bit - it makes little sense
@@ -87,18 +89,12 @@ struct Flags {
// Allow the tool to re-exec the program. This may interfere badly with the
// debugger.
bool allow_reexec;
- // Strips this prefix from file paths in error reports.
- const char *strip_path_prefix;
// If set, prints not only thread creation stacks for threads in error report,
// but also thread creation stacks for threads that created those threads,
// etc. up to main thread.
bool print_full_thread_history;
// ASan will write logs to "log_path.pid" instead of stderr.
const char *log_path;
- // Use fast (frame-pointer-based) unwinder on fatal errors (if available).
- bool fast_unwind_on_fatal;
- // Use fast (frame-pointer-based) unwinder on malloc/free (if available).
- bool fast_unwind_on_malloc;
// Poison (or not) the heap memory on [de]allocation. Zero value is useful
// for benchmarking the allocator or instrumentator.
bool poison_heap;
@@ -106,9 +102,20 @@ struct Flags {
bool alloc_dealloc_mismatch;
// Use stack depot instead of storing stacks in the redzones.
bool use_stack_depot;
+ // If true, assume that memcmp(p1, p2, n) always reads n bytes before
+ // comparing p1 and p2.
+ bool strict_memcmp;
+ // If true, assume that dynamic initializers can never access globals from
+ // other modules, even if the latter are already initialized.
+ bool strict_init_order;
+ // Invoke LeakSanitizer at process exit.
+ bool detect_leaks;
};
-Flags *flags();
+extern Flags asan_flags_dont_use_directly;
+inline Flags *flags() {
+ return &asan_flags_dont_use_directly;
+}
void InitializeFlags(Flags *f, const char *env);
} // namespace __asan
diff --git a/lib/asan/asan_globals.cc b/lib/asan/asan_globals.cc
index 4e18bb8e2355..301ea44f2ca5 100644
--- a/lib/asan/asan_globals.cc
+++ b/lib/asan/asan_globals.cc
@@ -14,12 +14,14 @@
#include "asan_interceptors.h"
#include "asan_internal.h"
#include "asan_mapping.h"
+#include "asan_poisoning.h"
#include "asan_report.h"
#include "asan_stack.h"
#include "asan_stats.h"
#include "asan_thread.h"
-#include "sanitizer/asan_interface.h"
+#include "sanitizer_common/sanitizer_common.h"
#include "sanitizer_common/sanitizer_mutex.h"
+#include "sanitizer_common/sanitizer_placement_new.h"
namespace __asan {
@@ -33,38 +35,48 @@ struct ListOfGlobals {
static BlockingMutex mu_for_globals(LINKER_INITIALIZED);
static LowLevelAllocator allocator_for_globals;
static ListOfGlobals *list_of_all_globals;
-static ListOfGlobals *list_of_dynamic_init_globals;
-
-void PoisonRedZones(const Global &g) {
- uptr shadow_rz_size = kGlobalAndStackRedzone >> SHADOW_SCALE;
- CHECK(shadow_rz_size == 1 || shadow_rz_size == 2 || shadow_rz_size == 4);
- // full right redzone
- uptr g_aligned_size = kGlobalAndStackRedzone *
- ((g.size + kGlobalAndStackRedzone - 1) / kGlobalAndStackRedzone);
- PoisonShadow(g.beg + g_aligned_size,
- kGlobalAndStackRedzone, kAsanGlobalRedzoneMagic);
- if ((g.size % kGlobalAndStackRedzone) != 0) {
- // partial right redzone
- u64 g_aligned_down_size = kGlobalAndStackRedzone *
- (g.size / kGlobalAndStackRedzone);
- CHECK(g_aligned_down_size == g_aligned_size - kGlobalAndStackRedzone);
- PoisonShadowPartialRightRedzone(g.beg + g_aligned_down_size,
- g.size % kGlobalAndStackRedzone,
- kGlobalAndStackRedzone,
- kAsanGlobalRedzoneMagic);
+
+static const int kDynamicInitGlobalsInitialCapacity = 512;
+struct DynInitGlobal {
+ Global g;
+ bool initialized;
+};
+typedef InternalVector<DynInitGlobal> VectorOfGlobals;
+// Lazy-initialized and never deleted.
+static VectorOfGlobals *dynamic_init_globals;
+
+ALWAYS_INLINE void PoisonShadowForGlobal(const Global *g, u8 value) {
+ FastPoisonShadow(g->beg, g->size_with_redzone, value);
+}
+
+ALWAYS_INLINE void PoisonRedZones(const Global &g) {
+ uptr aligned_size = RoundUpTo(g.size, SHADOW_GRANULARITY);
+ FastPoisonShadow(g.beg + aligned_size, g.size_with_redzone - aligned_size,
+ kAsanGlobalRedzoneMagic);
+ if (g.size != aligned_size) {
+ FastPoisonShadowPartialRightRedzone(
+ g.beg + RoundDownTo(g.size, SHADOW_GRANULARITY),
+ g.size % SHADOW_GRANULARITY,
+ SHADOW_GRANULARITY,
+ kAsanGlobalRedzoneMagic);
}
}
-bool DescribeAddressIfGlobal(uptr addr) {
+static void ReportGlobal(const Global &g, const char *prefix) {
+ Report("%s Global: beg=%p size=%zu/%zu name=%s module=%s dyn_init=%zu\n",
+ prefix, (void*)g.beg, g.size, g.size_with_redzone, g.name,
+ g.module_name, g.has_dynamic_init);
+}
+
+bool DescribeAddressIfGlobal(uptr addr, uptr size) {
if (!flags()->report_globals) return false;
BlockingMutexLock lock(&mu_for_globals);
bool res = false;
for (ListOfGlobals *l = list_of_all_globals; l; l = l->next) {
const Global &g = *l->g;
if (flags()->report_globals >= 2)
- Report("Search Global: beg=%p size=%zu name=%s\n",
- (void*)g.beg, g.size, (char*)g.name);
- res |= DescribeAddressRelativeToGlobal(addr, g);
+ ReportGlobal(g, "Search");
+ res |= DescribeAddressRelativeToGlobal(addr, size, g);
}
return res;
}
@@ -75,24 +87,26 @@ bool DescribeAddressIfGlobal(uptr addr) {
static void RegisterGlobal(const Global *g) {
CHECK(asan_inited);
if (flags()->report_globals >= 2)
- Report("Added Global: beg=%p size=%zu/%zu name=%s dyn.init=%zu\n",
- (void*)g->beg, g->size, g->size_with_redzone, g->name,
- g->has_dynamic_init);
+ ReportGlobal(*g, "Added");
CHECK(flags()->report_globals);
CHECK(AddrIsInMem(g->beg));
CHECK(AddrIsAlignedByGranularity(g->beg));
CHECK(AddrIsAlignedByGranularity(g->size_with_redzone));
- PoisonRedZones(*g);
+ if (flags()->poison_heap)
+ PoisonRedZones(*g);
ListOfGlobals *l =
(ListOfGlobals*)allocator_for_globals.Allocate(sizeof(ListOfGlobals));
l->g = g;
l->next = list_of_all_globals;
list_of_all_globals = l;
if (g->has_dynamic_init) {
- l = (ListOfGlobals*)allocator_for_globals.Allocate(sizeof(ListOfGlobals));
- l->g = g;
- l->next = list_of_dynamic_init_globals;
- list_of_dynamic_init_globals = l;
+ if (dynamic_init_globals == 0) {
+ void *mem = allocator_for_globals.Allocate(sizeof(VectorOfGlobals));
+ dynamic_init_globals = new(mem)
+ VectorOfGlobals(kDynamicInitGlobalsInitialCapacity);
+ }
+ DynInitGlobal dyn_global = { *g, false };
+ dynamic_init_globals->push_back(dyn_global);
}
}
@@ -102,34 +116,26 @@ static void UnregisterGlobal(const Global *g) {
CHECK(AddrIsInMem(g->beg));
CHECK(AddrIsAlignedByGranularity(g->beg));
CHECK(AddrIsAlignedByGranularity(g->size_with_redzone));
- PoisonShadow(g->beg, g->size_with_redzone, 0);
+ if (flags()->poison_heap)
+ PoisonShadowForGlobal(g, 0);
// 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.
}
-// Poison all shadow memory for a single global.
-static void PoisonGlobalAndRedzones(const Global *g) {
- CHECK(asan_inited);
- CHECK(flags()->check_initialization_order);
- CHECK(AddrIsInMem(g->beg));
- CHECK(AddrIsAlignedByGranularity(g->beg));
- CHECK(AddrIsAlignedByGranularity(g->size_with_redzone));
- if (flags()->report_globals >= 3)
- Printf("DynInitPoison : %s\n", g->name);
- PoisonShadow(g->beg, g->size_with_redzone, kAsanInitializationOrderMagic);
-}
-
-static void UnpoisonGlobal(const Global *g) {
- CHECK(asan_inited);
- CHECK(flags()->check_initialization_order);
- CHECK(AddrIsInMem(g->beg));
- CHECK(AddrIsAlignedByGranularity(g->beg));
- CHECK(AddrIsAlignedByGranularity(g->size_with_redzone));
- if (flags()->report_globals >= 3)
- Printf("DynInitUnpoison: %s\n", g->name);
- PoisonShadow(g->beg, g->size_with_redzone, 0);
- PoisonRedZones(*g);
+void StopInitOrderChecking() {
+ BlockingMutexLock lock(&mu_for_globals);
+ if (!flags()->check_initialization_order || !dynamic_init_globals)
+ return;
+ flags()->check_initialization_order = false;
+ for (uptr i = 0, n = dynamic_init_globals->size(); i < n; ++i) {
+ DynInitGlobal &dyn_g = (*dynamic_init_globals)[i];
+ const Global *g = &dyn_g.g;
+ // Unpoison the whole global.
+ PoisonShadowForGlobal(g, 0);
+ // Poison redzones back.
+ PoisonRedZones(*g);
+ }
}
} // namespace __asan
@@ -160,31 +166,47 @@ void __asan_unregister_globals(__asan_global *globals, uptr n) {
// when all dynamically initialized globals are unpoisoned. This method
// poisons all global variables not defined in this TU, so that a dynamic
// initializer can only touch global variables in the same TU.
-void __asan_before_dynamic_init(uptr first_addr, uptr last_addr) {
- if (!flags()->check_initialization_order) return;
- CHECK(list_of_dynamic_init_globals);
+void __asan_before_dynamic_init(const char *module_name) {
+ if (!flags()->check_initialization_order ||
+ !flags()->poison_heap)
+ return;
+ bool strict_init_order = flags()->strict_init_order;
+ CHECK(dynamic_init_globals);
+ CHECK(module_name);
+ CHECK(asan_inited);
BlockingMutexLock lock(&mu_for_globals);
- bool from_current_tu = false;
- // The list looks like:
- // a => ... => b => last_addr => ... => first_addr => c => ...
- // The globals of the current TU reside between last_addr and first_addr.
- for (ListOfGlobals *l = list_of_dynamic_init_globals; l; l = l->next) {
- if (l->g->beg == last_addr)
- from_current_tu = true;
- if (!from_current_tu)
- PoisonGlobalAndRedzones(l->g);
- if (l->g->beg == first_addr)
- from_current_tu = false;
+ if (flags()->report_globals >= 3)
+ Printf("DynInitPoison module: %s\n", module_name);
+ for (uptr i = 0, n = dynamic_init_globals->size(); i < n; ++i) {
+ DynInitGlobal &dyn_g = (*dynamic_init_globals)[i];
+ const Global *g = &dyn_g.g;
+ if (dyn_g.initialized)
+ continue;
+ if (g->module_name != module_name)
+ PoisonShadowForGlobal(g, kAsanInitializationOrderMagic);
+ else if (!strict_init_order)
+ dyn_g.initialized = true;
}
- CHECK(!from_current_tu);
}
// This method runs immediately after dynamic initialization in each TU, when
// all dynamically initialized globals except for those defined in the current
// TU are poisoned. It simply unpoisons all dynamically initialized globals.
void __asan_after_dynamic_init() {
- if (!flags()->check_initialization_order) return;
+ if (!flags()->check_initialization_order ||
+ !flags()->poison_heap)
+ return;
+ CHECK(asan_inited);
BlockingMutexLock lock(&mu_for_globals);
- for (ListOfGlobals *l = list_of_dynamic_init_globals; l; l = l->next)
- UnpoisonGlobal(l->g);
+ // FIXME: Optionally report that we're unpoisoning globals from a module.
+ for (uptr i = 0, n = dynamic_init_globals->size(); i < n; ++i) {
+ DynInitGlobal &dyn_g = (*dynamic_init_globals)[i];
+ const Global *g = &dyn_g.g;
+ if (!dyn_g.initialized) {
+ // Unpoison the whole global.
+ PoisonShadowForGlobal(g, 0);
+ // Poison redzones back.
+ PoisonRedZones(*g);
+ }
+ }
}
diff --git a/lib/asan/asan_intercepted_functions.h b/lib/asan/asan_intercepted_functions.h
index a1faf713c130..842781cdb17f 100644
--- a/lib/asan/asan_intercepted_functions.h
+++ b/lib/asan/asan_intercepted_functions.h
@@ -19,16 +19,16 @@
#include "sanitizer_common/sanitizer_platform_interceptors.h"
#include <stdarg.h>
+#include <stddef.h>
using __sanitizer::uptr;
// Use macro to describe if specific function should be
// intercepted on a given platform.
-#if !defined(_WIN32)
+#if !SANITIZER_WINDOWS
# define ASAN_INTERCEPT_ATOLL_AND_STRTOLL 1
# define ASAN_INTERCEPT__LONGJMP 1
# define ASAN_INTERCEPT_STRDUP 1
-# define ASAN_INTERCEPT_STRCASECMP_AND_STRNCASECMP 1
# define ASAN_INTERCEPT_INDEX 1
# define ASAN_INTERCEPT_PTHREAD_CREATE 1
# define ASAN_INTERCEPT_MLOCKX 1
@@ -36,225 +36,79 @@ using __sanitizer::uptr;
# define ASAN_INTERCEPT_ATOLL_AND_STRTOLL 0
# define ASAN_INTERCEPT__LONGJMP 0
# define ASAN_INTERCEPT_STRDUP 0
-# define ASAN_INTERCEPT_STRCASECMP_AND_STRNCASECMP 0
# define ASAN_INTERCEPT_INDEX 0
# define ASAN_INTERCEPT_PTHREAD_CREATE 0
# define ASAN_INTERCEPT_MLOCKX 0
#endif
-#if defined(__linux__)
+#if SANITIZER_LINUX
# define ASAN_USE_ALIAS_ATTRIBUTE_FOR_INDEX 1
#else
# define ASAN_USE_ALIAS_ATTRIBUTE_FOR_INDEX 0
#endif
-#if !defined(__APPLE__)
+#if !SANITIZER_MAC
# define ASAN_INTERCEPT_STRNLEN 1
#else
# define ASAN_INTERCEPT_STRNLEN 0
#endif
-#if defined(__linux__) && !defined(ANDROID)
+#if SANITIZER_LINUX && !SANITIZER_ANDROID
# define ASAN_INTERCEPT_SWAPCONTEXT 1
#else
# define ASAN_INTERCEPT_SWAPCONTEXT 0
#endif
-#if !defined(ANDROID) && !defined(_WIN32)
+#if !SANITIZER_ANDROID && !SANITIZER_WINDOWS
# define ASAN_INTERCEPT_SIGNAL_AND_SIGACTION 1
#else
# define ASAN_INTERCEPT_SIGNAL_AND_SIGACTION 0
#endif
-// On Darwin siglongjmp tailcalls longjmp, so we don't want to intercept it
-// there.
-#if !defined(_WIN32) && (!defined(__APPLE__) || MAC_INTERPOSE_FUNCTIONS)
+#if !SANITIZER_WINDOWS
# define ASAN_INTERCEPT_SIGLONGJMP 1
#else
# define ASAN_INTERCEPT_SIGLONGJMP 0
#endif
-#if ASAN_HAS_EXCEPTIONS && !defined(_WIN32)
+#if ASAN_HAS_EXCEPTIONS && !SANITIZER_WINDOWS
# define ASAN_INTERCEPT___CXA_THROW 1
#else
# define ASAN_INTERCEPT___CXA_THROW 0
#endif
-#define DECLARE_FUNCTION_AND_WRAPPER(ret_type, func, ...) \
- ret_type func(__VA_ARGS__); \
- ret_type WRAP(func)(__VA_ARGS__)
-
-// Use extern declarations of intercepted functions on Mac and Windows
-// to avoid including system headers.
-#if defined(__APPLE__) || (defined(_WIN32) && !defined(_DLL))
-extern "C" {
-// signal.h
-# if ASAN_INTERCEPT_SIGNAL_AND_SIGACTION
-struct sigaction;
-DECLARE_FUNCTION_AND_WRAPPER(int, sigaction, int sig,
- const struct sigaction *act,
- struct sigaction *oldact);
-DECLARE_FUNCTION_AND_WRAPPER(void*, signal, int signum, void *handler);
-# endif
-
-// setjmp.h
-DECLARE_FUNCTION_AND_WRAPPER(void, longjmp, void *env, int value);
-# if ASAN_INTERCEPT__LONGJMP
-DECLARE_FUNCTION_AND_WRAPPER(void, _longjmp, void *env, int value);
-# endif
-# if ASAN_INTERCEPT_SIGLONGJMP
-DECLARE_FUNCTION_AND_WRAPPER(void, siglongjmp, void *env, int value);
-# endif
-# if ASAN_INTERCEPT___CXA_THROW
-DECLARE_FUNCTION_AND_WRAPPER(void, __cxa_throw, void *a, void *b, void *c);
-#endif
-
-// string.h / strings.h
-DECLARE_FUNCTION_AND_WRAPPER(int, memcmp,
- const void *a1, const void *a2, uptr size);
-DECLARE_FUNCTION_AND_WRAPPER(void*, memmove,
- void *to, const void *from, uptr size);
-DECLARE_FUNCTION_AND_WRAPPER(void*, memcpy,
- void *to, const void *from, uptr size);
-DECLARE_FUNCTION_AND_WRAPPER(void*, memset, void *block, int c, uptr size);
-DECLARE_FUNCTION_AND_WRAPPER(char*, strchr, const char *str, int c);
-DECLARE_FUNCTION_AND_WRAPPER(char*, strcat, /* NOLINT */
- char *to, const char* from);
-DECLARE_FUNCTION_AND_WRAPPER(char*, strncat,
- char *to, const char* from, uptr size);
-DECLARE_FUNCTION_AND_WRAPPER(char*, strcpy, /* NOLINT */
- char *to, const char* from);
-DECLARE_FUNCTION_AND_WRAPPER(char*, strncpy,
- char *to, const char* from, uptr size);
-DECLARE_FUNCTION_AND_WRAPPER(int, strcmp, const char *s1, const char* s2);
-DECLARE_FUNCTION_AND_WRAPPER(int, strncmp,
- const char *s1, const char* s2, uptr size);
-DECLARE_FUNCTION_AND_WRAPPER(uptr, strlen, const char *s);
-# if ASAN_INTERCEPT_STRCASECMP_AND_STRNCASECMP
-DECLARE_FUNCTION_AND_WRAPPER(int, strcasecmp, const char *s1, const char *s2);
-DECLARE_FUNCTION_AND_WRAPPER(int, strncasecmp,
- const char *s1, const char *s2, uptr n);
-# endif
-# if ASAN_INTERCEPT_STRDUP
-DECLARE_FUNCTION_AND_WRAPPER(char*, strdup, const char *s);
-# endif
-# if ASAN_INTERCEPT_STRNLEN
-DECLARE_FUNCTION_AND_WRAPPER(uptr, strnlen, const char *s, uptr maxlen);
-# endif
-#if ASAN_INTERCEPT_INDEX
-DECLARE_FUNCTION_AND_WRAPPER(char*, index, const char *string, int c);
-#endif
-
-// stdlib.h
-DECLARE_FUNCTION_AND_WRAPPER(int, atoi, const char *nptr);
-DECLARE_FUNCTION_AND_WRAPPER(long, atol, const char *nptr); // NOLINT
-DECLARE_FUNCTION_AND_WRAPPER(long, strtol, const char *nptr, char **endptr, int base); // NOLINT
-# if ASAN_INTERCEPT_ATOLL_AND_STRTOLL
-DECLARE_FUNCTION_AND_WRAPPER(long long, atoll, const char *nptr); // NOLINT
-DECLARE_FUNCTION_AND_WRAPPER(long long, strtoll, const char *nptr, char **endptr, int base); // NOLINT
-# endif
-
-// unistd.h
-# if SANITIZER_INTERCEPT_READ
-DECLARE_FUNCTION_AND_WRAPPER(SSIZE_T, read, int fd, void *buf, SIZE_T count);
-# endif
-# if SANITIZER_INTERCEPT_PREAD
-DECLARE_FUNCTION_AND_WRAPPER(SSIZE_T, pread, int fd, void *buf,
- SIZE_T count, OFF_T offset);
-# endif
-# if SANITIZER_INTERCEPT_PREAD64
-DECLARE_FUNCTION_AND_WRAPPER(SSIZE_T, pread64, int fd, void *buf,
- SIZE_T count, OFF64_T offset);
-# endif
-
-#if SANITIZER_INTERCEPT_WRITE
-DECLARE_FUNCTION_AND_WRAPPER(SSIZE_T, write, int fd, void *ptr, SIZE_T count);
-#endif
-#if SANITIZER_INTERCEPT_PWRITE
-DECLARE_FUNCTION_AND_WRAPPER(SSIZE_T, pwrite, int fd, void *ptr, SIZE_T count);
+#if !SANITIZER_WINDOWS
+# define ASAN_INTERCEPT___CXA_ATEXIT 1
+#else
+# define ASAN_INTERCEPT___CXA_ATEXIT 0
#endif
-# if ASAN_INTERCEPT_MLOCKX
-// mlock/munlock
-DECLARE_FUNCTION_AND_WRAPPER(int, mlock, const void *addr, SIZE_T len);
-DECLARE_FUNCTION_AND_WRAPPER(int, munlock, const void *addr, SIZE_T len);
-DECLARE_FUNCTION_AND_WRAPPER(int, mlockall, int flags);
-DECLARE_FUNCTION_AND_WRAPPER(int, munlockall, void);
-# endif
-
+# if SANITIZER_WINDOWS
+extern "C" {
// Windows threads.
-# if defined(_WIN32)
__declspec(dllimport)
void* __stdcall CreateThread(void *sec, uptr st, void* start,
void *arg, DWORD fl, DWORD *id);
-# endif
-// Posix threads.
-# if ASAN_INTERCEPT_PTHREAD_CREATE
-DECLARE_FUNCTION_AND_WRAPPER(int, pthread_create,
- void *thread, void *attr,
- void *(*start_routine)(void*), void *arg);
-# endif
-
-#if defined(__APPLE__)
-typedef void* pthread_workqueue_t;
-typedef void* pthread_workitem_handle_t;
-
-typedef void* dispatch_group_t;
-typedef void* dispatch_queue_t;
-typedef void* dispatch_source_t;
-typedef u64 dispatch_time_t;
-typedef void (*dispatch_function_t)(void *block);
-typedef void* (*worker_t)(void *block);
-typedef void* CFStringRef;
-typedef void* CFAllocatorRef;
-
-DECLARE_FUNCTION_AND_WRAPPER(void, dispatch_async_f,
- dispatch_queue_t dq,
- void *ctxt, dispatch_function_t func);
-DECLARE_FUNCTION_AND_WRAPPER(void, dispatch_sync_f,
- dispatch_queue_t dq,
- void *ctxt, dispatch_function_t func);
-DECLARE_FUNCTION_AND_WRAPPER(void, dispatch_after_f,
- dispatch_time_t when, dispatch_queue_t dq,
- void *ctxt, dispatch_function_t func);
-DECLARE_FUNCTION_AND_WRAPPER(void, dispatch_barrier_async_f,
- dispatch_queue_t dq,
- void *ctxt, dispatch_function_t func);
-DECLARE_FUNCTION_AND_WRAPPER(void, dispatch_group_async_f,
- dispatch_group_t group, dispatch_queue_t dq,
- void *ctxt, dispatch_function_t func);
-DECLARE_FUNCTION_AND_WRAPPER(void, __CFInitialize, void);
-DECLARE_FUNCTION_AND_WRAPPER(CFStringRef, CFStringCreateCopy,
- CFAllocatorRef alloc, CFStringRef str);
-DECLARE_FUNCTION_AND_WRAPPER(void, free, void* ptr);
-
-DECLARE_FUNCTION_AND_WRAPPER(int, vscanf, const char *format, va_list ap);
-DECLARE_FUNCTION_AND_WRAPPER(int, vsscanf, const char *str, const char *format,
- va_list ap);
-DECLARE_FUNCTION_AND_WRAPPER(int, vfscanf, void *stream, const char *format,
- va_list ap);
-DECLARE_FUNCTION_AND_WRAPPER(int, scanf, const char *format, ...);
-DECLARE_FUNCTION_AND_WRAPPER(int, fscanf,
- void* stream, const char *format, ...);
-DECLARE_FUNCTION_AND_WRAPPER(int, sscanf, // NOLINT
- const char *str, const char *format, ...);
-
-#if MAC_INTERPOSE_FUNCTIONS && !defined(MISSING_BLOCKS_SUPPORT)
-DECLARE_FUNCTION_AND_WRAPPER(void, dispatch_group_async,
- dispatch_group_t dg,
- dispatch_queue_t dq, void (^work)(void));
-DECLARE_FUNCTION_AND_WRAPPER(void, dispatch_async,
- dispatch_queue_t dq, void (^work)(void));
-DECLARE_FUNCTION_AND_WRAPPER(void, dispatch_after,
- dispatch_queue_t dq, void (^work)(void));
-DECLARE_FUNCTION_AND_WRAPPER(void, dispatch_source_set_event_handler,
- dispatch_source_t ds, void (^work)(void));
-DECLARE_FUNCTION_AND_WRAPPER(void, dispatch_source_set_cancel_handler,
- dispatch_source_t ds, void (^work)(void));
-#endif // MAC_INTERPOSE_FUNCTIONS
-#endif // __APPLE__
-} // extern "C"
-#endif
+int memcmp(const void *a1, const void *a2, uptr size);
+void memmove(void *to, const void *from, uptr size);
+void* memset(void *block, int c, uptr size);
+void* memcpy(void *to, const void *from, uptr size);
+char* strcat(char *to, const char* from); // NOLINT
+char* strchr(const char *str, int c);
+int strcmp(const char *s1, const char* s2);
+char* strcpy(char *to, const char* from); // NOLINT
+uptr strlen(const char *s);
+char* strncat(char *to, const char* from, uptr size);
+int strncmp(const char *s1, const char* s2, uptr size);
+char* strncpy(char *to, const char* from, uptr size);
+uptr strnlen(const char *s, uptr maxlen);
+int atoi(const char *nptr);
+long atol(const char *nptr); // NOLINT
+long strtol(const char *nptr, char **endptr, int base); // NOLINT
+void longjmp(void *env, int value);
+double frexp(double x, int *expptr);
+}
+# endif
#endif // ASAN_INTERCEPTED_FUNCTIONS_H
diff --git a/lib/asan/asan_interceptors.cc b/lib/asan/asan_interceptors.cc
index 6170974d6f5e..7e7deea29634 100644
--- a/lib/asan/asan_interceptors.cc
+++ b/lib/asan/asan_interceptors.cc
@@ -17,27 +17,40 @@
#include "asan_intercepted_functions.h"
#include "asan_internal.h"
#include "asan_mapping.h"
+#include "asan_poisoning.h"
#include "asan_report.h"
#include "asan_stack.h"
#include "asan_stats.h"
-#include "asan_thread_registry.h"
#include "interception/interception.h"
-#include "sanitizer/asan_interface.h"
#include "sanitizer_common/sanitizer_libc.h"
namespace __asan {
+// Return true if we can quickly decide that the region is unpoisoned.
+static inline bool QuickCheckForUnpoisonedRegion(uptr beg, uptr size) {
+ if (size == 0) return true;
+ if (size <= 32)
+ return !AddressIsPoisoned(beg) &&
+ !AddressIsPoisoned(beg + size - 1) &&
+ !AddressIsPoisoned(beg + size / 2);
+ return false;
+}
+
// We implement ACCESS_MEMORY_RANGE, ASAN_READ_RANGE,
// and ASAN_WRITE_RANGE as macro instead of function so
// that no extra frames are created, and stack trace contains
// relevant information only.
// We check all shadow bytes.
-#define ACCESS_MEMORY_RANGE(offset, size, isWrite) do { \
- if (uptr __ptr = __asan_region_is_poisoned((uptr)(offset), size)) { \
- GET_CURRENT_PC_BP_SP; \
- __asan_report_error(pc, bp, sp, __ptr, isWrite, /* access_size */1); \
- } \
-} while (0)
+#define ACCESS_MEMORY_RANGE(offset, size, isWrite) do { \
+ uptr __offset = (uptr)(offset); \
+ uptr __size = (uptr)(size); \
+ uptr __bad = 0; \
+ if (!QuickCheckForUnpoisonedRegion(__offset, __size) && \
+ (__bad = __asan_region_is_poisoned(__offset, __size))) { \
+ GET_CURRENT_PC_BP_SP; \
+ __asan_report_error(pc, bp, sp, __bad, isWrite, __size); \
+ } \
+ } while (0)
#define ASAN_READ_RANGE(offset, size) ACCESS_MEMORY_RANGE(offset, size, false)
#define ASAN_WRITE_RANGE(offset, size) ACCESS_MEMORY_RANGE(offset, size, true);
@@ -76,9 +89,14 @@ static inline uptr MaybeRealStrnlen(const char *s, uptr maxlen) {
}
void SetThreadName(const char *name) {
- AsanThread *t = asanThreadRegistry().GetCurrent();
+ AsanThread *t = GetCurrentThread();
if (t)
- t->summary()->set_name(name);
+ asanThreadRegistry().SetThreadName(t->tid(), name);
+}
+
+static void DisableStrictInitOrderChecker() {
+ if (flags()->strict_init_order)
+ flags()->check_initialization_order = false;
}
} // namespace __asan
@@ -89,37 +107,54 @@ using namespace __asan; // NOLINT
#define COMMON_INTERCEPTOR_WRITE_RANGE(ctx, ptr, size) \
ASAN_WRITE_RANGE(ptr, size)
#define COMMON_INTERCEPTOR_READ_RANGE(ctx, ptr, size) ASAN_READ_RANGE(ptr, size)
-#define COMMON_INTERCEPTOR_ENTER(ctx, func, ...) \
- do { \
- ctx = 0; \
- (void)ctx; \
- ENSURE_ASAN_INITED(); \
+#define COMMON_INTERCEPTOR_ENTER(ctx, func, ...) \
+ do { \
+ if (asan_init_is_running) \
+ return REAL(func)(__VA_ARGS__); \
+ ctx = 0; \
+ (void)ctx; \
+ ENSURE_ASAN_INITED(); \
} while (false)
#define COMMON_INTERCEPTOR_FD_ACQUIRE(ctx, fd) do { } while (false)
#define COMMON_INTERCEPTOR_FD_RELEASE(ctx, fd) do { } while (false)
#define COMMON_INTERCEPTOR_SET_THREAD_NAME(ctx, name) SetThreadName(name)
#include "sanitizer_common/sanitizer_common_interceptors.inc"
+#define COMMON_SYSCALL_PRE_READ_RANGE(p, s) ASAN_READ_RANGE(p, s)
+#define COMMON_SYSCALL_PRE_WRITE_RANGE(p, s) ASAN_WRITE_RANGE(p, s)
+#define COMMON_SYSCALL_POST_READ_RANGE(p, s)
+#define COMMON_SYSCALL_POST_WRITE_RANGE(p, s)
+#include "sanitizer_common/sanitizer_common_syscalls.inc"
+
static thread_return_t THREAD_CALLING_CONV asan_thread_start(void *arg) {
AsanThread *t = (AsanThread*)arg;
- asanThreadRegistry().SetCurrent(t);
- return t->ThreadStart();
+ SetCurrentThread(t);
+ return t->ThreadStart(GetTid());
}
#if ASAN_INTERCEPT_PTHREAD_CREATE
+extern "C" int pthread_attr_getdetachstate(void *attr, int *v);
+
INTERCEPTOR(int, pthread_create, void *thread,
void *attr, void *(*start_routine)(void*), void *arg) {
+ // Strict init-order checking in thread-hostile.
+ DisableStrictInitOrderChecker();
GET_STACK_TRACE_THREAD;
- u32 current_tid = asanThreadRegistry().GetCurrentTidOrInvalid();
- AsanThread *t = AsanThread::Create(current_tid, start_routine, arg, &stack);
- asanThreadRegistry().RegisterThread(t);
+ int detached = 0;
+ if (attr != 0)
+ pthread_attr_getdetachstate(attr, &detached);
+
+ u32 current_tid = GetCurrentTidOrInvalid();
+ AsanThread *t = AsanThread::Create(start_routine, arg);
+ CreateThreadContextArgs args = { t, &stack };
+ asanThreadRegistry().CreateThread(*(uptr*)t, detached, current_tid, &args);
return REAL(pthread_create)(thread, attr, asan_thread_start, t);
}
#endif // ASAN_INTERCEPT_PTHREAD_CREATE
#if ASAN_INTERCEPT_SIGNAL_AND_SIGACTION
INTERCEPTOR(void*, signal, int signum, void *handler) {
- if (!AsanInterceptsSignal(signum)) {
+ if (!AsanInterceptsSignal(signum) || flags()->allow_user_segv_handler) {
return REAL(signal)(signum, handler);
}
return 0;
@@ -127,12 +162,12 @@ INTERCEPTOR(void*, signal, int signum, void *handler) {
INTERCEPTOR(int, sigaction, int signum, const struct sigaction *act,
struct sigaction *oldact) {
- if (!AsanInterceptsSignal(signum)) {
+ if (!AsanInterceptsSignal(signum) || flags()->allow_user_segv_handler) {
return REAL(sigaction)(signum, act, oldact);
}
return 0;
}
-#elif ASAN_POSIX
+#elif SANITIZER_POSIX
// We need to have defined REAL(sigaction) on posix systems.
DEFINE_REAL(int, sigaction, int signum, const struct sigaction *act,
struct sigaction *oldact);
@@ -237,27 +272,32 @@ static inline int CharCmp(unsigned char c1, unsigned char c2) {
return (c1 == c2) ? 0 : (c1 < c2) ? -1 : 1;
}
-static inline int CharCaseCmp(unsigned char c1, unsigned char c2) {
- int c1_low = ToLower(c1);
- int c2_low = ToLower(c2);
- return c1_low - c2_low;
-}
-
INTERCEPTOR(int, memcmp, const void *a1, const void *a2, uptr size) {
if (!asan_inited) return internal_memcmp(a1, a2, size);
ENSURE_ASAN_INITED();
- unsigned char c1 = 0, c2 = 0;
- const unsigned char *s1 = (const unsigned char*)a1;
- const unsigned char *s2 = (const unsigned char*)a2;
- uptr i;
- for (i = 0; i < size; i++) {
- c1 = s1[i];
- c2 = s2[i];
- if (c1 != c2) break;
+ if (flags()->replace_intrin) {
+ if (flags()->strict_memcmp) {
+ // Check the entire regions even if the first bytes of the buffers are
+ // different.
+ ASAN_READ_RANGE(a1, size);
+ ASAN_READ_RANGE(a2, size);
+ // Fallthrough to REAL(memcmp) below.
+ } else {
+ unsigned char c1 = 0, c2 = 0;
+ const unsigned char *s1 = (const unsigned char*)a1;
+ const unsigned char *s2 = (const unsigned char*)a2;
+ uptr i;
+ for (i = 0; i < size; i++) {
+ c1 = s1[i];
+ c2 = s2[i];
+ if (c1 != c2) break;
+ }
+ ASAN_READ_RANGE(s1, Min(i + 1, size));
+ ASAN_READ_RANGE(s2, Min(i + 1, size));
+ return CharCmp(c1, c2);
+ }
}
- ASAN_READ_RANGE(s1, Min(i + 1, size));
- ASAN_READ_RANGE(s2, Min(i + 1, size));
- return CharCmp(c1, c2);
+ return REAL(memcmp(a1, a2, size));
}
INTERCEPTOR(void*, memcpy, void *to, const void *from, uptr size) {
@@ -277,13 +317,9 @@ INTERCEPTOR(void*, memcpy, void *to, const void *from, uptr size) {
ASAN_READ_RANGE(from, size);
ASAN_WRITE_RANGE(to, size);
}
-#if MAC_INTERPOSE_FUNCTIONS
// Interposing of resolver functions is broken on Mac OS 10.7 and 10.8.
// See also http://code.google.com/p/address-sanitizer/issues/detail?id=116.
return internal_memcpy(to, from, size);
-#else
- return REAL(memcpy)(to, from, size);
-#endif
}
INTERCEPTOR(void*, memmove, void *to, const void *from, uptr size) {
@@ -296,13 +332,9 @@ INTERCEPTOR(void*, memmove, void *to, const void *from, uptr size) {
ASAN_READ_RANGE(from, size);
ASAN_WRITE_RANGE(to, size);
}
-#if MAC_INTERPOSE_FUNCTIONS
// Interposing of resolver functions is broken on Mac OS 10.7 and 10.8.
// See also http://code.google.com/p/address-sanitizer/issues/detail?id=116.
return internal_memmove(to, from, size);
-#else
- return REAL(memmove)(to, from, size);
-#endif
}
INTERCEPTOR(void*, memset, void *block, int c, uptr size) {
@@ -339,7 +371,12 @@ INTERCEPTOR(char*, strchr, const char *str, int c) {
INTERCEPTOR(char*, index, const char *string, int c)
ALIAS(WRAPPER_NAME(strchr));
# else
-DEFINE_REAL(char*, index, const char *string, int c)
+# if SANITIZER_MAC
+DECLARE_REAL(char*, index, const char *string, int c)
+OVERRIDE_FUNCTION(index, strchr);
+# else
+DEFINE_REAL(char*, index, const char *string, int c);
+# endif
# endif
#endif // ASAN_INTERCEPT_INDEX
@@ -400,7 +437,7 @@ INTERCEPTOR(int, strcmp, const char *s1, const char *s2) {
}
INTERCEPTOR(char*, strcpy, char *to, const char *from) { // NOLINT
-#if MAC_INTERPOSE_FUNCTIONS
+#if SANITIZER_MAC
if (!asan_inited) return REAL(strcpy)(to, from); // NOLINT
#endif
// strcpy is called from malloc_default_purgeable_zone()
@@ -420,7 +457,7 @@ INTERCEPTOR(char*, strcpy, char *to, const char *from) { // NOLINT
#if ASAN_INTERCEPT_STRDUP
INTERCEPTOR(char*, strdup, const char *s) {
-#if MAC_INTERPOSE_FUNCTIONS
+#if SANITIZER_MAC
// FIXME: because internal_strdup() uses InternalAlloc(), which currently
// just calls malloc() on Mac, we can't use internal_strdup() with the
// dynamic runtime. We can remove the call to REAL(strdup) once InternalAlloc
@@ -453,36 +490,6 @@ INTERCEPTOR(uptr, strlen, const char *s) {
return length;
}
-#if ASAN_INTERCEPT_STRCASECMP_AND_STRNCASECMP
-INTERCEPTOR(int, strcasecmp, const char *s1, const char *s2) {
- ENSURE_ASAN_INITED();
- unsigned char c1, c2;
- uptr i;
- for (i = 0; ; i++) {
- c1 = (unsigned char)s1[i];
- c2 = (unsigned char)s2[i];
- if (CharCaseCmp(c1, c2) != 0 || c1 == '\0') break;
- }
- ASAN_READ_RANGE(s1, i + 1);
- ASAN_READ_RANGE(s2, i + 1);
- return CharCaseCmp(c1, c2);
-}
-
-INTERCEPTOR(int, strncasecmp, const char *s1, const char *s2, uptr n) {
- ENSURE_ASAN_INITED();
- unsigned char c1 = 0, c2 = 0;
- uptr i;
- for (i = 0; i < n; i++) {
- c1 = (unsigned char)s1[i];
- c2 = (unsigned char)s2[i];
- if (CharCaseCmp(c1, c2) != 0 || c1 == '\0') break;
- }
- ASAN_READ_RANGE(s1, Min(i + 1, n));
- ASAN_READ_RANGE(s2, Min(i + 1, n));
- return CharCaseCmp(c1, c2);
-}
-#endif // ASAN_INTERCEPT_STRCASECMP_AND_STRNCASECMP
-
INTERCEPTOR(int, strncmp, const char *s1, const char *s2, uptr size) {
if (!asan_inited) return internal_strncmp(s1, s2, size);
// strncmp is called from malloc_default_purgeable_zone()
@@ -530,7 +537,7 @@ static inline bool IsValidStrtolBase(int base) {
}
static inline void FixRealStrtolEndptr(const char *nptr, char **endptr) {
- CHECK(endptr != 0);
+ CHECK(endptr);
if (nptr == *endptr) {
// No digits were found at strtol call, we need to find out the last
// symbol accessed by strtoll on our own.
@@ -561,7 +568,7 @@ INTERCEPTOR(long, strtol, const char *nptr, // NOLINT
}
INTERCEPTOR(int, atoi, const char *nptr) {
-#if MAC_INTERPOSE_FUNCTIONS
+#if SANITIZER_MAC
if (!asan_inited) return REAL(atoi)(nptr);
#endif
ENSURE_ASAN_INITED();
@@ -580,7 +587,7 @@ INTERCEPTOR(int, atoi, const char *nptr) {
}
INTERCEPTOR(long, atol, const char *nptr) { // NOLINT
-#if MAC_INTERPOSE_FUNCTIONS
+#if SANITIZER_MAC
if (!asan_inited) return REAL(atol)(nptr);
#endif
ENSURE_ASAN_INITED();
@@ -629,20 +636,39 @@ INTERCEPTOR(long long, atoll, const char *nptr) { // NOLINT
}
#endif // ASAN_INTERCEPT_ATOLL_AND_STRTOLL
+static void AtCxaAtexit(void *unused) {
+ (void)unused;
+ StopInitOrderChecking();
+}
+
+#if ASAN_INTERCEPT___CXA_ATEXIT
+INTERCEPTOR(int, __cxa_atexit, void (*func)(void *), void *arg,
+ void *dso_handle) {
+ ENSURE_ASAN_INITED();
+ int res = REAL(__cxa_atexit)(func, arg, dso_handle);
+ REAL(__cxa_atexit)(AtCxaAtexit, 0, 0);
+ return res;
+}
+#endif // ASAN_INTERCEPT___CXA_ATEXIT
+
#define ASAN_INTERCEPT_FUNC(name) do { \
if (!INTERCEPT_FUNCTION(name) && flags()->verbosity > 0) \
Report("AddressSanitizer: failed to intercept '" #name "'\n"); \
} while (0)
-#if defined(_WIN32)
+#if SANITIZER_WINDOWS
INTERCEPTOR_WINAPI(DWORD, CreateThread,
void* security, uptr stack_size,
DWORD (__stdcall *start_routine)(void*), void* arg,
DWORD flags, void* tid) {
+ // Strict init-order checking in thread-hostile.
+ DisableStrictInitOrderChecker();
GET_STACK_TRACE_THREAD;
- u32 current_tid = asanThreadRegistry().GetCurrentTidOrInvalid();
- AsanThread *t = AsanThread::Create(current_tid, start_routine, arg, &stack);
- asanThreadRegistry().RegisterThread(t);
+ u32 current_tid = GetCurrentTidOrInvalid();
+ AsanThread *t = AsanThread::Create(start_routine, arg);
+ CreateThreadContextArgs args = { t, &stack };
+ int detached = 0; // FIXME: how can we determine it on Windows?
+ asanThreadRegistry().CreateThread(*(uptr*)t, detached, current_tid, &args);
return REAL(CreateThread)(security, stack_size,
asan_thread_start, t, flags, tid);
}
@@ -661,10 +687,9 @@ void InitializeAsanInterceptors() {
static bool was_called_once;
CHECK(was_called_once == false);
was_called_once = true;
-#if MAC_INTERPOSE_FUNCTIONS
+#if SANITIZER_MAC
return;
-#endif
-
+#else
SANITIZER_COMMON_INTERCEPTORS_INIT;
// Intercept mem* functions.
@@ -673,12 +698,6 @@ void InitializeAsanInterceptors() {
ASAN_INTERCEPT_FUNC(memset);
if (PLATFORM_HAS_DIFFERENT_MEMCPY_AND_MEMMOVE) {
ASAN_INTERCEPT_FUNC(memcpy);
- } else {
-#if !MAC_INTERPOSE_FUNCTIONS
- // If we're using dynamic interceptors on Mac, these two are just plain
- // functions.
- internal_memcpy(&REAL(memcpy), &REAL(memmove), sizeof(REAL(memmove)));
-#endif
}
// Intercept str* functions.
@@ -690,22 +709,14 @@ void InitializeAsanInterceptors() {
ASAN_INTERCEPT_FUNC(strncat);
ASAN_INTERCEPT_FUNC(strncmp);
ASAN_INTERCEPT_FUNC(strncpy);
-#if ASAN_INTERCEPT_STRCASECMP_AND_STRNCASECMP
- ASAN_INTERCEPT_FUNC(strcasecmp);
- ASAN_INTERCEPT_FUNC(strncasecmp);
-#endif
#if ASAN_INTERCEPT_STRDUP
ASAN_INTERCEPT_FUNC(strdup);
#endif
#if ASAN_INTERCEPT_STRNLEN
ASAN_INTERCEPT_FUNC(strnlen);
#endif
-#if ASAN_INTERCEPT_INDEX
-# if ASAN_USE_ALIAS_ATTRIBUTE_FOR_INDEX
+#if ASAN_INTERCEPT_INDEX && ASAN_USE_ALIAS_ATTRIBUTE_FOR_INDEX
ASAN_INTERCEPT_FUNC(index);
-# else
- CHECK(OVERRIDE_FUNCTION(index, WRAP(strchr)));
-# endif
#endif
ASAN_INTERCEPT_FUNC(atoi);
@@ -750,19 +761,20 @@ void InitializeAsanInterceptors() {
ASAN_INTERCEPT_FUNC(pthread_create);
#endif
- // Some Windows-specific interceptors.
-#if defined(_WIN32)
- InitializeWindowsInterceptors();
+ // Intercept atexit function.
+#if ASAN_INTERCEPT___CXA_ATEXIT
+ ASAN_INTERCEPT_FUNC(__cxa_atexit);
#endif
- // Some Mac-specific interceptors.
-#if defined(__APPLE__)
- InitializeMacInterceptors();
+ // Some Windows-specific interceptors.
+#if SANITIZER_WINDOWS
+ InitializeWindowsInterceptors();
#endif
if (flags()->verbosity > 0) {
Report("AddressSanitizer: libc interceptors initialized\n");
}
+#endif // SANITIZER_MAC
}
} // namespace __asan
diff --git a/lib/asan/asan_interceptors.h b/lib/asan/asan_interceptors.h
index 3b3e90ef93ff..91830aa145a9 100644
--- a/lib/asan/asan_interceptors.h
+++ b/lib/asan/asan_interceptors.h
@@ -32,9 +32,6 @@ DECLARE_REAL(int, sigaction, int signum, const struct sigaction *act,
namespace __asan {
void InitializeAsanInterceptors();
-#if defined(__APPLE__)
-void InitializeMacInterceptors();
-#endif // __APPLE__
} // namespace __asan
diff --git a/lib/asan/asan_interface_internal.h b/lib/asan/asan_interface_internal.h
new file mode 100644
index 000000000000..24f76253bccd
--- /dev/null
+++ b/lib/asan/asan_interface_internal.h
@@ -0,0 +1,141 @@
+//===-- asan_interface_internal.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.
+//
+// This header can be included by the instrumented program to fetch
+// data (mostly allocator statistics) from ASan runtime library.
+//===----------------------------------------------------------------------===//
+#ifndef ASAN_INTERFACE_INTERNAL_H
+#define ASAN_INTERFACE_INTERNAL_H
+
+#include "sanitizer_common/sanitizer_internal_defs.h"
+
+using __sanitizer::uptr;
+
+extern "C" {
+ // This function should be called at the very beginning of the process,
+ // before any instrumented code is executed and before any call to malloc.
+ // Everytime the asan ABI changes we also change the version number in this
+ // name. Objects build with incompatible asan ABI version
+ // will not link with run-time.
+ // Changes between ABI versions:
+ // v1=>v2: added 'module_name' to __asan_global
+ // v2=>v3: stack frame description (created by the compiler)
+ // contains the function PC as the 3-rd field (see
+ // DescribeAddressIfStack).
+ void __asan_init_v3() SANITIZER_INTERFACE_ATTRIBUTE;
+ #define __asan_init __asan_init_v3
+
+ // This structure describes an instrumented global variable.
+ struct __asan_global {
+ uptr beg; // The address of the global.
+ uptr size; // The original size of the global.
+ uptr size_with_redzone; // The size with the redzone.
+ const char *name; // Name as a C string.
+ 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.
+ };
+
+ // These two functions should be called by the instrumented code.
+ // 'globals' is an array of structures describing 'n' globals.
+ void __asan_register_globals(__asan_global *globals, uptr n)
+ SANITIZER_INTERFACE_ATTRIBUTE;
+ void __asan_unregister_globals(__asan_global *globals, uptr n)
+ SANITIZER_INTERFACE_ATTRIBUTE;
+
+ // These two functions should be called before and after dynamic initializers
+ // of a single module run, respectively.
+ void __asan_before_dynamic_init(const char *module_name)
+ SANITIZER_INTERFACE_ATTRIBUTE;
+ void __asan_after_dynamic_init()
+ SANITIZER_INTERFACE_ATTRIBUTE;
+
+ // These two functions are used by the instrumented code in the
+ // use-after-return mode. __asan_stack_malloc allocates size bytes of
+ // fake stack and __asan_stack_free poisons it. real_stack is a pointer to
+ // the real stack region.
+ uptr __asan_stack_malloc(uptr size, uptr real_stack)
+ SANITIZER_INTERFACE_ATTRIBUTE;
+ void __asan_stack_free(uptr ptr, uptr size, uptr real_stack)
+ SANITIZER_INTERFACE_ATTRIBUTE;
+
+ // These two functions are used by instrumented code in the
+ // use-after-scope mode. They mark memory for local variables as
+ // unaddressable when they leave scope and addressable before the
+ // function exits.
+ void __asan_poison_stack_memory(uptr addr, uptr size)
+ SANITIZER_INTERFACE_ATTRIBUTE;
+ void __asan_unpoison_stack_memory(uptr addr, uptr size)
+ SANITIZER_INTERFACE_ATTRIBUTE;
+
+ // Performs cleanup before a NoReturn function. Must be called before things
+ // like _exit and execl to avoid false positives on stack.
+ void __asan_handle_no_return() SANITIZER_INTERFACE_ATTRIBUTE;
+
+ void __asan_poison_memory_region(void const volatile *addr, uptr size)
+ SANITIZER_INTERFACE_ATTRIBUTE;
+ void __asan_unpoison_memory_region(void const volatile *addr, uptr size)
+ SANITIZER_INTERFACE_ATTRIBUTE;
+
+ bool __asan_address_is_poisoned(void const volatile *addr)
+ SANITIZER_INTERFACE_ATTRIBUTE;
+
+ uptr __asan_region_is_poisoned(uptr beg, uptr size)
+ SANITIZER_INTERFACE_ATTRIBUTE;
+
+ void __asan_describe_address(uptr addr)
+ SANITIZER_INTERFACE_ATTRIBUTE;
+
+ void __asan_report_error(uptr pc, uptr bp, uptr sp,
+ uptr addr, bool is_write, uptr access_size)
+ SANITIZER_INTERFACE_ATTRIBUTE;
+
+ int __asan_set_error_exit_code(int exit_code)
+ SANITIZER_INTERFACE_ATTRIBUTE;
+ void __asan_set_death_callback(void (*callback)(void))
+ SANITIZER_INTERFACE_ATTRIBUTE;
+ void __asan_set_error_report_callback(void (*callback)(const char*))
+ SANITIZER_INTERFACE_ATTRIBUTE;
+
+ /* OPTIONAL */ void __asan_on_error()
+ SANITIZER_WEAK_ATTRIBUTE SANITIZER_INTERFACE_ATTRIBUTE;
+
+ /* OPTIONAL */ bool __asan_symbolize(const void *pc, char *out_buffer,
+ int out_size)
+ SANITIZER_WEAK_ATTRIBUTE SANITIZER_INTERFACE_ATTRIBUTE;
+
+ uptr __asan_get_estimated_allocated_size(uptr size)
+ SANITIZER_INTERFACE_ATTRIBUTE;
+ bool __asan_get_ownership(const void *p)
+ SANITIZER_INTERFACE_ATTRIBUTE;
+ uptr __asan_get_allocated_size(const void *p)
+ SANITIZER_INTERFACE_ATTRIBUTE;
+ uptr __asan_get_current_allocated_bytes()
+ SANITIZER_INTERFACE_ATTRIBUTE;
+ uptr __asan_get_heap_size()
+ SANITIZER_INTERFACE_ATTRIBUTE;
+ uptr __asan_get_free_bytes()
+ SANITIZER_INTERFACE_ATTRIBUTE;
+ uptr __asan_get_unmapped_bytes()
+ SANITIZER_INTERFACE_ATTRIBUTE;
+ void __asan_print_accumulated_stats()
+ SANITIZER_INTERFACE_ATTRIBUTE;
+
+ /* OPTIONAL */ const char* __asan_default_options()
+ SANITIZER_WEAK_ATTRIBUTE SANITIZER_INTERFACE_ATTRIBUTE;
+
+ /* OPTIONAL */ void __asan_malloc_hook(void *ptr, uptr size)
+ SANITIZER_WEAK_ATTRIBUTE SANITIZER_INTERFACE_ATTRIBUTE;
+ /* OPTIONAL */ void __asan_free_hook(void *ptr)
+ SANITIZER_WEAK_ATTRIBUTE SANITIZER_INTERFACE_ATTRIBUTE;
+} // extern "C"
+
+#endif // ASAN_INTERFACE_INTERNAL_H
diff --git a/lib/asan/asan_internal.h b/lib/asan/asan_internal.h
index 5d3bffa814da..7a4d74472bcd 100644
--- a/lib/asan/asan_internal.h
+++ b/lib/asan/asan_internal.h
@@ -15,45 +15,15 @@
#define ASAN_INTERNAL_H
#include "asan_flags.h"
+#include "asan_interface_internal.h"
#include "sanitizer_common/sanitizer_common.h"
#include "sanitizer_common/sanitizer_internal_defs.h"
#include "sanitizer_common/sanitizer_stacktrace.h"
#include "sanitizer_common/sanitizer_libc.h"
-#if !defined(__linux__) && !defined(__APPLE__) && !defined(_WIN32)
-# error "This operating system is not supported by AddressSanitizer"
-#endif
-
#define ASAN_DEFAULT_FAILURE_EXITCODE 1
-#if defined(__linux__)
-# define ASAN_LINUX 1
-#else
-# define ASAN_LINUX 0
-#endif
-
-#if defined(__APPLE__)
-# define ASAN_MAC 1
-#else
-# define ASAN_MAC 0
-#endif
-
-#if defined(_WIN32)
-# define ASAN_WINDOWS 1
-#else
-# define ASAN_WINDOWS 0
-#endif
-
-#if defined(__ANDROID__) || defined(ANDROID)
-# define ASAN_ANDROID 1
-#else
-# define ASAN_ANDROID 0
-#endif
-
-
-#define ASAN_POSIX (ASAN_LINUX || ASAN_MAC)
-
-#if __has_feature(address_sanitizer)
+#if __has_feature(address_sanitizer) || defined(__SANITIZE_ADDRESS__)
# error "The AddressSanitizer run-time should not be"
" instrumented by AddressSanitizer"
#endif
@@ -62,7 +32,7 @@
// If set, asan will install its own SEGV signal handler.
#ifndef ASAN_NEEDS_SEGV
-# if ASAN_ANDROID == 1
+# if SANITIZER_ANDROID == 1
# define ASAN_NEEDS_SEGV 0
# else
# define ASAN_NEEDS_SEGV 1
@@ -90,6 +60,10 @@
# endif
#endif
+#ifndef ASAN_USE_PREINIT_ARRAY
+# define ASAN_USE_PREINIT_ARRAY (SANITIZER_LINUX && !SANITIZER_ANDROID)
+#endif
+
// All internal functions in asan reside inside the __asan namespace
// to avoid namespace collisions with the user programs.
// Seperate namespace also makes it simpler to distinguish the asan run-time
@@ -118,6 +92,7 @@ void UnsetAlternateSignalStack();
void InstallSignalHandlers();
void ReadContextStack(void *context, uptr *stack, uptr *ssize);
void AsanPlatformThreadInit();
+void StopInitOrderChecking();
// Wrapper for TLS/TSD.
void AsanTSDInit(void (*destructor)(void *tsd));
@@ -126,24 +101,14 @@ void AsanTSDSet(void *tsd);
void AppendToErrorMessageBuffer(const char *buffer);
-// asan_poisoning.cc
-// Poisons the shadow memory for "size" bytes starting from "addr".
-void PoisonShadow(uptr addr, uptr size, u8 value);
-// Poisons the shadow memory for "redzone_size" bytes starting from
-// "addr + size".
-void PoisonShadowPartialRightRedzone(uptr addr,
- uptr size,
- uptr redzone_size,
- u8 value);
-
// Platfrom-specific options.
-#ifdef __APPLE__
+#if SANITIZER_MAC
bool PlatformHasDifferentMemcpyAndMemmove();
# define PLATFORM_HAS_DIFFERENT_MEMCPY_AND_MEMMOVE \
(PlatformHasDifferentMemcpyAndMemmove())
#else
# define PLATFORM_HAS_DIFFERENT_MEMCPY_AND_MEMMOVE true
-#endif // __APPLE__
+#endif // SANITIZER_MAC
// Add convenient macro for interface functions that may be represented as
// weak hooks.
diff --git a/lib/asan/asan_linux.cc b/lib/asan/asan_linux.cc
index 845493de0956..17bb4ca5f01c 100644
--- a/lib/asan/asan_linux.cc
+++ b/lib/asan/asan_linux.cc
@@ -11,12 +11,13 @@
//
// Linux-specific details.
//===----------------------------------------------------------------------===//
-#ifdef __linux__
+
+#include "sanitizer_common/sanitizer_platform.h"
+#if SANITIZER_LINUX
#include "asan_interceptors.h"
#include "asan_internal.h"
#include "asan_thread.h"
-#include "asan_thread_registry.h"
#include "sanitizer_common/sanitizer_libc.h"
#include "sanitizer_common/sanitizer_procmaps.h"
@@ -31,7 +32,7 @@
#include <unistd.h>
#include <unwind.h>
-#if !ASAN_ANDROID
+#if !SANITIZER_ANDROID
// FIXME: where to get ucontext on Android?
#include <sys/ucontext.h>
#endif
@@ -50,7 +51,7 @@ void *AsanDoesNotSupportStaticLinkage() {
}
void GetPcSpBp(void *context, uptr *pc, uptr *sp, uptr *bp) {
-#if ASAN_ANDROID
+#if SANITIZER_ANDROID
*pc = *sp = *bp = 0;
#elif defined(__arm__)
ucontext_t *ucontext = (ucontext_t*)context;
@@ -101,25 +102,7 @@ void AsanPlatformThreadInit() {
// Nothing here for now.
}
-void GetStackTrace(StackTrace *stack, uptr max_s, uptr pc, uptr bp, bool fast) {
-#if defined(__arm__) || \
- defined(__powerpc__) || defined(__powerpc64__) || \
- defined(__sparc__)
- fast = false;
-#endif
- if (!fast)
- return stack->SlowUnwindStack(pc, max_s);
- stack->size = 0;
- stack->trace[0] = pc;
- if (max_s > 1) {
- stack->max_size = max_s;
- if (!asan_inited) return;
- if (AsanThread *t = asanThreadRegistry().GetCurrent())
- stack->FastUnwindStack(pc, bp, t->stack_top(), t->stack_bottom());
- }
-}
-
-#if !ASAN_ANDROID
+#if !SANITIZER_ANDROID
void ReadContextStack(void *context, uptr *stack, uptr *ssize) {
ucontext_t *ucp = (ucontext_t*)context;
*stack = (uptr)ucp->uc_stack.ss_sp;
@@ -133,4 +116,4 @@ void ReadContextStack(void *context, uptr *stack, uptr *ssize) {
} // namespace __asan
-#endif // __linux__
+#endif // SANITIZER_LINUX
diff --git a/lib/asan/asan_mac.cc b/lib/asan/asan_mac.cc
index 3ed9e06eeb6f..4313534008e7 100644
--- a/lib/asan/asan_mac.cc
+++ b/lib/asan/asan_mac.cc
@@ -12,7 +12,8 @@
// Mac-specific details.
//===----------------------------------------------------------------------===//
-#ifdef __APPLE__
+#include "sanitizer_common/sanitizer_platform.h"
+#if SANITIZER_MAC
#include "asan_interceptors.h"
#include "asan_internal.h"
@@ -20,7 +21,6 @@
#include "asan_mapping.h"
#include "asan_stack.h"
#include "asan_thread.h"
-#include "asan_thread_registry.h"
#include "sanitizer_common/sanitizer_libc.h"
#include <crt_externs.h> // for _NSGetArgv
@@ -36,7 +36,6 @@
#include <stdlib.h> // for free()
#include <unistd.h>
#include <libkern/OSAtomic.h>
-#include <CoreFoundation/CFString.h>
namespace __asan {
@@ -59,9 +58,9 @@ int GetMacosVersion() {
uptr len = 0, maxlen = sizeof(version) / sizeof(version[0]);
for (uptr i = 0; i < maxlen; i++) version[i] = '\0';
// Get the version length.
- CHECK(sysctl(mib, 2, 0, &len, 0, 0) != -1);
- CHECK(len < maxlen);
- CHECK(sysctl(mib, 2, version, &len, 0, 0) != -1);
+ CHECK_NE(sysctl(mib, 2, 0, &len, 0, 0), -1);
+ CHECK_LT(len, maxlen);
+ CHECK_NE(sysctl(mib, 2, version, &len, 0, 0), -1);
switch (version[0]) {
case '9': return MACOS_VERSION_LEOPARD;
case '1': {
@@ -89,16 +88,52 @@ extern "C"
void __asan_init();
static const char kDyldInsertLibraries[] = "DYLD_INSERT_LIBRARIES";
+LowLevelAllocator allocator_for_env;
+
+// Change the value of the env var |name|, leaking the original value.
+// If |name_value| is NULL, the variable is deleted from the environment,
+// otherwise the corresponding "NAME=value" string is replaced with
+// |name_value|.
+void LeakyResetEnv(const char *name, const char *name_value) {
+ char ***env_ptr = _NSGetEnviron();
+ CHECK(env_ptr);
+ char **environ = *env_ptr;
+ CHECK(environ);
+ uptr name_len = internal_strlen(name);
+ while (*environ != 0) {
+ uptr len = internal_strlen(*environ);
+ if (len > name_len) {
+ const char *p = *environ;
+ if (!internal_memcmp(p, name, name_len) && p[name_len] == '=') {
+ // Match.
+ if (name_value) {
+ // Replace the old value with the new one.
+ *environ = const_cast<char*>(name_value);
+ } else {
+ // Shift the subsequent pointers back.
+ char **del = environ;
+ do {
+ del[0] = del[1];
+ } while (*del++);
+ }
+ }
+ }
+ environ++;
+ }
+}
void MaybeReexec() {
if (!flags()->allow_reexec) return;
-#if MAC_INTERPOSE_FUNCTIONS
- // If the program is linked with the dynamic ASan runtime library, make sure
- // the library is preloaded so that the wrappers work. If it is not, set
- // DYLD_INSERT_LIBRARIES and re-exec ourselves.
+ // Make sure the dynamic ASan runtime library is preloaded so that the
+ // wrappers work. If it is not, set DYLD_INSERT_LIBRARIES and re-exec
+ // ourselves.
Dl_info info;
CHECK(dladdr((void*)((uptr)__asan_init), &info));
- const char *dyld_insert_libraries = GetEnv(kDyldInsertLibraries);
+ char *dyld_insert_libraries =
+ const_cast<char*>(GetEnv(kDyldInsertLibraries));
+ uptr old_env_len = dyld_insert_libraries ?
+ internal_strlen(dyld_insert_libraries) : 0;
+ uptr fname_len = internal_strlen(info.dli_fname);
if (!dyld_insert_libraries ||
!REAL(strstr)(dyld_insert_libraries, info.dli_fname)) {
// DYLD_INSERT_LIBRARIES is not set or does not contain the runtime
@@ -106,19 +141,80 @@ void MaybeReexec() {
char program_name[1024];
uint32_t buf_size = sizeof(program_name);
_NSGetExecutablePath(program_name, &buf_size);
- // Ok to use setenv() since the wrappers don't depend on the value of
- // asan_inited.
- setenv(kDyldInsertLibraries, info.dli_fname, /*overwrite*/0);
+ char *new_env = const_cast<char*>(info.dli_fname);
+ if (dyld_insert_libraries) {
+ // Append the runtime dylib name to the existing value of
+ // DYLD_INSERT_LIBRARIES.
+ new_env = (char*)allocator_for_env.Allocate(old_env_len + fname_len + 2);
+ internal_strncpy(new_env, dyld_insert_libraries, old_env_len);
+ new_env[old_env_len] = ':';
+ // Copy fname_len and add a trailing zero.
+ internal_strncpy(new_env + old_env_len + 1, info.dli_fname,
+ fname_len + 1);
+ // Ok to use setenv() since the wrappers don't depend on the value of
+ // asan_inited.
+ setenv(kDyldInsertLibraries, new_env, /*overwrite*/1);
+ } else {
+ // Set DYLD_INSERT_LIBRARIES equal to the runtime dylib name.
+ setenv(kDyldInsertLibraries, info.dli_fname, /*overwrite*/0);
+ }
if (flags()->verbosity >= 1) {
Report("exec()-ing the program with\n");
- Report("%s=%s\n", kDyldInsertLibraries, info.dli_fname);
+ Report("%s=%s\n", kDyldInsertLibraries, new_env);
Report("to enable ASan wrappers.\n");
Report("Set ASAN_OPTIONS=allow_reexec=0 to disable this.\n");
}
execv(program_name, *_NSGetArgv());
+ } else {
+ // DYLD_INSERT_LIBRARIES is set and contains the runtime library.
+ if (old_env_len == fname_len) {
+ // It's just the runtime library name - fine to unset the variable.
+ LeakyResetEnv(kDyldInsertLibraries, NULL);
+ } else {
+ uptr env_name_len = internal_strlen(kDyldInsertLibraries);
+ // Allocate memory to hold the previous env var name, its value, the '='
+ // sign and the '\0' char.
+ char *new_env = (char*)allocator_for_env.Allocate(
+ old_env_len + 2 + env_name_len);
+ CHECK(new_env);
+ internal_memset(new_env, '\0', old_env_len + 2 + env_name_len);
+ internal_strncpy(new_env, kDyldInsertLibraries, env_name_len);
+ new_env[env_name_len] = '=';
+ char *new_env_pos = new_env + env_name_len + 1;
+
+ // Iterate over colon-separated pieces of |dyld_insert_libraries|.
+ char *piece_start = dyld_insert_libraries;
+ char *piece_end = NULL;
+ char *old_env_end = dyld_insert_libraries + old_env_len;
+ do {
+ if (piece_start[0] == ':') piece_start++;
+ piece_end = REAL(strchr)(piece_start, ':');
+ if (!piece_end) piece_end = dyld_insert_libraries + old_env_len;
+ if ((uptr)(piece_start - dyld_insert_libraries) > old_env_len) break;
+ uptr piece_len = piece_end - piece_start;
+
+ // If the current piece isn't the runtime library name,
+ // append it to new_env.
+ if ((piece_len != fname_len) ||
+ (internal_strncmp(piece_start, info.dli_fname, fname_len) != 0)) {
+ if (new_env_pos != new_env + env_name_len + 1) {
+ new_env_pos[0] = ':';
+ new_env_pos++;
+ }
+ internal_strncpy(new_env_pos, piece_start, piece_len);
+ }
+ // Move on to the next piece.
+ new_env_pos += piece_len;
+ piece_start = piece_end;
+ } while (piece_start < old_env_end);
+
+ // Can't use setenv() here, because it requires the allocator to be
+ // initialized.
+ // FIXME: instead of filtering DYLD_INSERT_LIBRARIES here, do it in
+ // a separate function called after InitializeAllocator().
+ LeakyResetEnv(kDyldInsertLibraries, new_env);
+ }
}
-#endif // MAC_INTERPOSE_FUNCTIONS
- // If we're not using the dynamic runtime, do nothing.
}
// No-op. Mac does not support static linkage anyway.
@@ -131,83 +227,12 @@ bool AsanInterceptsSignal(int signum) {
}
void AsanPlatformThreadInit() {
- // For the first program thread, we can't replace the allocator before
- // __CFInitialize() has been called. If it hasn't, we'll call
- // MaybeReplaceCFAllocator() later on this thread.
- // For other threads __CFInitialize() has been called before their creation.
- // See also asan_malloc_mac.cc.
- if (((CFRuntimeBase*)kCFAllocatorSystemDefault)->_cfisa) {
- MaybeReplaceCFAllocator();
- }
-}
-
-void GetStackTrace(StackTrace *stack, uptr max_s, uptr pc, uptr bp, bool fast) {
- (void)fast;
- stack->size = 0;
- stack->trace[0] = pc;
- if ((max_s) > 1) {
- stack->max_size = max_s;
- if (!asan_inited) return;
- if (AsanThread *t = asanThreadRegistry().GetCurrent())
- stack->FastUnwindStack(pc, bp, t->stack_top(), t->stack_bottom());
- }
}
void ReadContextStack(void *context, uptr *stack, uptr *ssize) {
UNIMPLEMENTED();
}
-// The range of pages to be used for escape islands.
-// TODO(glider): instead of mapping a fixed range we must find a range of
-// unmapped pages in vmmap and take them.
-// These constants were chosen empirically and may not work if the shadow
-// memory layout changes. Unfortunately they do necessarily depend on
-// kHighMemBeg or kHighMemEnd.
-static void *island_allocator_pos = 0;
-
-#if SANITIZER_WORDSIZE == 32
-# define kIslandEnd (0xffdf0000 - GetPageSizeCached())
-# define kIslandBeg (kIslandEnd - 256 * GetPageSizeCached())
-#else
-# define kIslandEnd (0x7fffffdf0000 - GetPageSizeCached())
-# define kIslandBeg (kIslandEnd - 256 * GetPageSizeCached())
-#endif
-
-extern "C"
-mach_error_t __interception_allocate_island(void **ptr,
- uptr unused_size,
- void *unused_hint) {
- if (!island_allocator_pos) {
- island_allocator_pos =
- internal_mmap((void*)kIslandBeg, kIslandEnd - kIslandBeg,
- PROT_READ | PROT_WRITE | PROT_EXEC,
- MAP_PRIVATE | MAP_ANON | MAP_FIXED,
- -1, 0);
- if (island_allocator_pos != (void*)kIslandBeg) {
- return KERN_NO_SPACE;
- }
- if (flags()->verbosity) {
- Report("Mapped pages %p--%p for branch islands.\n",
- (void*)kIslandBeg, (void*)kIslandEnd);
- }
- // Should not be very performance-critical.
- internal_memset(island_allocator_pos, 0xCC, kIslandEnd - kIslandBeg);
- };
- *ptr = island_allocator_pos;
- island_allocator_pos = (char*)island_allocator_pos + GetPageSizeCached();
- if (flags()->verbosity) {
- Report("Branch island allocated at %p\n", *ptr);
- }
- return err_none;
-}
-
-extern "C"
-mach_error_t __interception_deallocate_island(void *ptr) {
- // Do nothing.
- // TODO(glider): allow to free and reuse the island memory.
- return err_none;
-}
-
// Support for the following functions from libdispatch on Mac OS:
// dispatch_async_f()
// dispatch_async()
@@ -237,9 +262,6 @@ mach_error_t __interception_deallocate_island(void *ptr) {
// The implementation details are at
// http://libdispatch.macosforge.org/trac/browser/trunk/src/queue.c
-typedef void* pthread_workqueue_t;
-typedef void* pthread_workitem_handle_t;
-
typedef void* dispatch_group_t;
typedef void* dispatch_queue_t;
typedef void* dispatch_source_t;
@@ -254,32 +276,16 @@ typedef struct {
u32 parent_tid;
} asan_block_context_t;
-// We use extern declarations of libdispatch functions here instead
-// of including <dispatch/dispatch.h>. This header is not present on
-// Mac OS X Leopard and eariler, and although we don't expect ASan to
-// work on legacy systems, it's bad to break the build of
-// LLVM compiler-rt there.
-extern "C" {
-void dispatch_async_f(dispatch_queue_t dq, void *ctxt,
- dispatch_function_t func);
-void dispatch_sync_f(dispatch_queue_t dq, void *ctxt,
- dispatch_function_t func);
-void dispatch_after_f(dispatch_time_t when, dispatch_queue_t dq, void *ctxt,
- dispatch_function_t func);
-void dispatch_barrier_async_f(dispatch_queue_t dq, void *ctxt,
- dispatch_function_t func);
-void dispatch_group_async_f(dispatch_group_t group, dispatch_queue_t dq,
- void *ctxt, dispatch_function_t func);
-} // extern "C"
-
-static ALWAYS_INLINE
+ALWAYS_INLINE
void asan_register_worker_thread(int parent_tid, StackTrace *stack) {
- AsanThread *t = asanThreadRegistry().GetCurrent();
+ AsanThread *t = GetCurrentThread();
if (!t) {
- t = AsanThread::Create(parent_tid, 0, 0, stack);
- asanThreadRegistry().RegisterThread(t);
+ t = AsanThread::Create(0, 0);
+ CreateThreadContextArgs args = { t, stack };
+ asanThreadRegistry().CreateThread(*(uptr*)t, true, parent_tid, &args);
t->Init();
- asanThreadRegistry().SetCurrent(t);
+ asanThreadRegistry().StartThread(t->tid(), 0, 0);
+ SetCurrentThread(t);
}
}
@@ -313,7 +319,7 @@ asan_block_context_t *alloc_asan_context(void *ctxt, dispatch_function_t func,
(asan_block_context_t*) asan_malloc(sizeof(asan_block_context_t), stack);
asan_ctxt->block = ctxt;
asan_ctxt->func = func;
- asan_ctxt->parent_tid = asanThreadRegistry().GetCurrentTidOrInvalid();
+ asan_ctxt->parent_tid = GetCurrentTidOrInvalid();
return asan_ctxt;
}
@@ -364,14 +370,7 @@ INTERCEPTOR(void, dispatch_group_async_f, dispatch_group_t group,
asan_dispatch_call_block_and_release);
}
-#if MAC_INTERPOSE_FUNCTIONS && !defined(MISSING_BLOCKS_SUPPORT)
-// dispatch_async, dispatch_group_async and others tailcall the corresponding
-// dispatch_*_f functions. When wrapping functions with mach_override, those
-// dispatch_*_f are intercepted automatically. But with dylib interposition
-// this does not work, because the calls within the same library are not
-// interposed.
-// Therefore we need to re-implement dispatch_async and friends.
-
+#if !defined(MISSING_BLOCKS_SUPPORT)
extern "C" {
// FIXME: consolidate these declarations with asan_intercepted_functions.h.
void dispatch_async(dispatch_queue_t dq, void(^work)(void));
@@ -386,7 +385,7 @@ void dispatch_source_set_event_handler(dispatch_source_t ds, void(^work)(void));
#define GET_ASAN_BLOCK(work) \
void (^asan_block)(void); \
- int parent_tid = asanThreadRegistry().GetCurrentTidOrInvalid(); \
+ int parent_tid = GetCurrentTidOrInvalid(); \
asan_block = ^(void) { \
GET_STACK_TRACE_THREAD; \
asan_register_worker_thread(parent_tid, &stack); \
@@ -424,53 +423,4 @@ INTERCEPTOR(void, dispatch_source_set_event_handler,
}
#endif
-// See http://opensource.apple.com/source/CF/CF-635.15/CFString.c
-int __CFStrIsConstant(CFStringRef str) {
- CFRuntimeBase *base = (CFRuntimeBase*)str;
-#if __LP64__
- return base->_rc == 0;
-#else
- return (base->_cfinfo[CF_RC_BITS]) == 0;
-#endif
-}
-
-INTERCEPTOR(CFStringRef, CFStringCreateCopy, CFAllocatorRef alloc,
- CFStringRef str) {
- if (__CFStrIsConstant(str)) {
- return str;
- } else {
- return REAL(CFStringCreateCopy)(alloc, str);
- }
-}
-
-DECLARE_REAL_AND_INTERCEPTOR(void, free, void *ptr)
-
-DECLARE_REAL_AND_INTERCEPTOR(void, __CFInitialize, void)
-
-namespace __asan {
-
-void InitializeMacInterceptors() {
- CHECK(INTERCEPT_FUNCTION(dispatch_async_f));
- CHECK(INTERCEPT_FUNCTION(dispatch_sync_f));
- CHECK(INTERCEPT_FUNCTION(dispatch_after_f));
- CHECK(INTERCEPT_FUNCTION(dispatch_barrier_async_f));
- CHECK(INTERCEPT_FUNCTION(dispatch_group_async_f));
- // Normally CFStringCreateCopy should not copy constant CF strings.
- // Replacing the default CFAllocator causes constant strings to be copied
- // rather than just returned, which leads to bugs in big applications like
- // Chromium and WebKit, see
- // http://code.google.com/p/address-sanitizer/issues/detail?id=10
- // Until this problem is fixed we need to check that the string is
- // non-constant before calling CFStringCreateCopy.
- CHECK(INTERCEPT_FUNCTION(CFStringCreateCopy));
- // Some of the library functions call free() directly, so we have to
- // intercept it.
- CHECK(INTERCEPT_FUNCTION(free));
- if (flags()->replace_cfallocator) {
- CHECK(INTERCEPT_FUNCTION(__CFInitialize));
- }
-}
-
-} // namespace __asan
-
-#endif // __APPLE__
+#endif // SANITIZER_MAC
diff --git a/lib/asan/asan_mac.h b/lib/asan/asan_mac.h
index be913865c440..b1a1966dbc6e 100644
--- a/lib/asan/asan_mac.h
+++ b/lib/asan/asan_mac.h
@@ -12,7 +12,7 @@
// Mac-specific ASan definitions.
//===----------------------------------------------------------------------===//
#ifndef ASAN_MAC_H
-#define ASAN_MAC_H
+#define ASAN__MAC_H
// CF_RC_BITS, the layout of CFRuntimeBase and __CFStrIsConstant are internal
// and subject to change in further CoreFoundation versions. Apple does not
diff --git a/lib/asan/asan_malloc_linux.cc b/lib/asan/asan_malloc_linux.cc
index b95cfe3149b7..20e636b9b3c0 100644
--- a/lib/asan/asan_malloc_linux.cc
+++ b/lib/asan/asan_malloc_linux.cc
@@ -13,16 +13,16 @@
// We simply define functions like malloc, free, realloc, etc.
// They will replace the corresponding libc functions automagically.
//===----------------------------------------------------------------------===//
-#ifdef __linux__
+
+#include "sanitizer_common/sanitizer_platform.h"
+#if SANITIZER_LINUX
#include "asan_allocator.h"
#include "asan_interceptors.h"
#include "asan_internal.h"
#include "asan_stack.h"
-#include "asan_thread_registry.h"
-#include "sanitizer/asan_interface.h"
-#if ASAN_ANDROID
+#if SANITIZER_ANDROID
DECLARE_REAL_AND_INTERCEPTOR(void*, malloc, uptr size)
DECLARE_REAL_AND_INTERCEPTOR(void, free, void *ptr)
DECLARE_REAL_AND_INTERCEPTOR(void*, calloc, uptr nmemb, uptr size)
@@ -147,4 +147,4 @@ INTERCEPTOR(void, malloc_stats, void) {
__asan_print_accumulated_stats();
}
-#endif // __linux__
+#endif // SANITIZER_LINUX
diff --git a/lib/asan/asan_malloc_mac.cc b/lib/asan/asan_malloc_mac.cc
index 545ede2debe7..4f353cb99ca7 100644
--- a/lib/asan/asan_malloc_mac.cc
+++ b/lib/asan/asan_malloc_mac.cc
@@ -12,7 +12,8 @@
// Mac-specific malloc interception.
//===----------------------------------------------------------------------===//
-#ifdef __APPLE__
+#include "sanitizer_common/sanitizer_platform.h"
+#if SANITIZER_MAC
#include <AvailabilityMacros.h>
#include <CoreFoundation/CFBase.h>
@@ -26,7 +27,6 @@
#include "asan_report.h"
#include "asan_stack.h"
#include "asan_stats.h"
-#include "asan_thread_registry.h"
// Similar code is used in Google Perftools,
// http://code.google.com/p/google-perftools.
@@ -36,85 +36,108 @@ using namespace __asan; // NOLINT
// TODO(glider): do we need both zones?
static malloc_zone_t *system_malloc_zone = 0;
-static malloc_zone_t *system_purgeable_zone = 0;
static malloc_zone_t asan_zone;
-CFAllocatorRef cf_asan = 0;
-
-// _CFRuntimeCreateInstance() checks whether the supplied allocator is
-// kCFAllocatorSystemDefault and, if it is not, stores the allocator reference
-// at the beginning of the allocated memory and returns the pointer to the
-// allocated memory plus sizeof(CFAllocatorRef). See
-// http://www.opensource.apple.com/source/CF/CF-635.21/CFRuntime.c
-// Pointers returned by _CFRuntimeCreateInstance() can then be passed directly
-// to free() or CFAllocatorDeallocate(), which leads to false invalid free
-// reports.
-// The corresponding rdar bug is http://openradar.appspot.com/radar?id=1796404.
-void* ALWAYS_INLINE get_saved_cfallocator_ref(void *ptr) {
- if (flags()->replace_cfallocator) {
- // Make sure we're not hitting the previous page. This may be incorrect
- // if ASan's malloc returns an address ending with 0xFF8, which will be
- // then padded to a page boundary with a CFAllocatorRef.
- uptr arith_ptr = (uptr)ptr;
- if ((arith_ptr & 0xFFF) > sizeof(CFAllocatorRef)) {
- CFAllocatorRef *saved =
- (CFAllocatorRef*)(arith_ptr - sizeof(CFAllocatorRef));
- if ((*saved == cf_asan) && asan_mz_size(saved)) ptr = (void*)saved;
- }
+
+INTERCEPTOR(malloc_zone_t *, malloc_create_zone,
+ vm_size_t start_size, unsigned zone_flags) {
+ if (!asan_inited) __asan_init();
+ GET_STACK_TRACE_MALLOC;
+ malloc_zone_t *new_zone =
+ (malloc_zone_t*)asan_malloc(sizeof(asan_zone), &stack);
+ internal_memcpy(new_zone, &asan_zone, sizeof(asan_zone));
+ new_zone->zone_name = NULL; // The name will be changed anyway.
+ return new_zone;
+}
+
+INTERCEPTOR(malloc_zone_t *, malloc_default_zone, void) {
+ if (!asan_inited) __asan_init();
+ 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();
+ 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();
+}
+
+INTERCEPTOR(int, malloc_make_nonpurgeable, void *ptr) {
+ // FIXME: ASan should support purgeable allocations. Ignoring them is fine
+ // for now.
+ if (!asan_inited) __asan_init();
+ // 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();
+ // Allocate |strlen("asan-") + 1 + internal_strlen(name)| bytes.
+ size_t buflen = 6 + (name ? internal_strlen(name) : 0);
+ InternalScopedBuffer<char> new_name(buflen);
+ if (name && zone->introspect == asan_zone.introspect) {
+ internal_snprintf(new_name.data(), buflen, "asan-%s", name);
+ name = new_name.data();
}
- return ptr;
+
+ // Call the system malloc's implementation for both external and our zones,
+ // since that appropriately changes VM region protections on the zone.
+ REAL(malloc_set_zone_name)(zone, name);
+}
+
+INTERCEPTOR(void *, malloc, size_t size) {
+ if (!asan_inited) __asan_init();
+ GET_STACK_TRACE_MALLOC;
+ void *res = asan_malloc(size, &stack);
+ return res;
}
-// The free() implementation provided by OS X calls malloc_zone_from_ptr()
-// to find the owner of |ptr|. If the result is 0, an invalid free() is
-// reported. Our implementation falls back to asan_free() in this case
-// in order to print an ASan-style report.
-//
-// For the objects created by _CFRuntimeCreateInstance a CFAllocatorRef is
-// placed at the beginning of the allocated chunk and the pointer returned by
-// our allocator is off by sizeof(CFAllocatorRef). This pointer can be then
-// passed directly to free(), which will lead to errors.
-// To overcome this we're checking whether |ptr-sizeof(CFAllocatorRef)|
-// contains a pointer to our CFAllocator (assuming no other allocator is used).
-// See http://code.google.com/p/address-sanitizer/issues/detail?id=70 for more
-// info.
INTERCEPTOR(void, free, void *ptr) {
- malloc_zone_t *zone = malloc_zone_from_ptr(ptr);
- if (zone) {
-#if defined(MAC_OS_X_VERSION_10_6) && \
- MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_6
- if ((zone->version >= 6) && (zone->free_definite_size)) {
- zone->free_definite_size(zone, ptr, malloc_size(ptr));
- } else {
- malloc_zone_free(zone, ptr);
- }
-#else
- malloc_zone_free(zone, ptr);
-#endif
- } else {
- if (!asan_mz_size(ptr)) ptr = get_saved_cfallocator_ref(ptr);
- GET_STACK_TRACE_FREE;
- asan_free(ptr, &stack, FROM_MALLOC);
- }
+ if (!asan_inited) __asan_init();
+ if (!ptr) return;
+ GET_STACK_TRACE_FREE;
+ asan_free(ptr, &stack, FROM_MALLOC);
}
-// We can't always replace the default CFAllocator with cf_asan right in
-// ReplaceSystemMalloc(), because it is sometimes called before
-// __CFInitialize(), when the default allocator is invalid and replacing it may
-// crash the program. Instead we wait for the allocator to initialize and jump
-// in just after __CFInitialize(). Nobody is going to allocate memory using
-// CFAllocators before that, so we won't miss anything.
-//
-// See http://code.google.com/p/address-sanitizer/issues/detail?id=87
-// and http://opensource.apple.com/source/CF/CF-550.43/CFRuntime.c
-INTERCEPTOR(void, __CFInitialize, void) {
- // If the runtime is built as dynamic library, __CFInitialize wrapper may be
- // called before __asan_init.
-#if !MAC_INTERPOSE_FUNCTIONS
- CHECK(flags()->replace_cfallocator);
- CHECK(asan_inited);
-#endif
- REAL(__CFInitialize)();
- if (!cf_asan && asan_inited) MaybeReplaceCFAllocator();
+INTERCEPTOR(void *, realloc, void *ptr, size_t size) {
+ if (!asan_inited) __asan_init();
+ GET_STACK_TRACE_MALLOC;
+ return asan_realloc(ptr, size, &stack);
+}
+
+INTERCEPTOR(void *, calloc, size_t nmemb, size_t size) {
+ if (!asan_inited) __asan_init();
+ GET_STACK_TRACE_MALLOC;
+ return asan_calloc(nmemb, size, &stack);
+}
+
+INTERCEPTOR(void *, valloc, size_t size) {
+ if (!asan_inited) __asan_init();
+ 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();
+ 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();
+ CHECK(memptr);
+ GET_STACK_TRACE_MALLOC;
+ void *result = asan_memalign(alignment, size, &stack, FROM_MALLOC);
+ if (result) {
+ *memptr = result;
+ return 0;
+ }
+ return -1;
}
namespace {
@@ -134,15 +157,6 @@ void *mz_malloc(malloc_zone_t *zone, size_t size) {
return asan_malloc(size, &stack);
}
-void *cf_malloc(CFIndex size, CFOptionFlags hint, void *info) {
- if (!asan_inited) {
- CHECK(system_malloc_zone);
- return malloc_zone_malloc(system_malloc_zone, size);
- }
- GET_STACK_TRACE_MALLOC;
- return asan_malloc(size, &stack);
-}
-
void *mz_calloc(malloc_zone_t *zone, size_t nmemb, size_t size) {
if (!asan_inited) {
// Hack: dlsym calls calloc before REAL(calloc) is retrieved from dlsym.
@@ -174,31 +188,14 @@ void *mz_valloc(malloc_zone_t *zone, size_t size) {
void ALWAYS_INLINE free_common(void *context, void *ptr) {
if (!ptr) return;
- if (asan_mz_size(ptr)) {
- GET_STACK_TRACE_FREE;
+ GET_STACK_TRACE_FREE;
+ // FIXME: need to retire this flag.
+ if (!flags()->mac_ignore_invalid_free) {
asan_free(ptr, &stack, FROM_MALLOC);
} else {
- // If the pointer does not belong to any of the zones, use one of the
- // fallback methods to free memory.
- malloc_zone_t *zone_ptr = malloc_zone_from_ptr(ptr);
- if (zone_ptr == system_purgeable_zone) {
- // allocations from malloc_default_purgeable_zone() done before
- // __asan_init() may be occasionally freed via free_common().
- // see http://code.google.com/p/address-sanitizer/issues/detail?id=99.
- malloc_zone_free(zone_ptr, ptr);
- } else {
- // If the memory chunk pointer was moved to store additional
- // CFAllocatorRef, fix it back.
- ptr = get_saved_cfallocator_ref(ptr);
- GET_STACK_TRACE_FREE;
- if (!flags()->mac_ignore_invalid_free) {
- asan_free(ptr, &stack, FROM_MALLOC);
- } else {
- GET_ZONE_FOR_PTR(ptr);
- WarnMacFreeUnallocated((uptr)ptr, (uptr)zone_ptr, zone_name, &stack);
- return;
- }
- }
+ GET_ZONE_FOR_PTR(ptr);
+ WarnMacFreeUnallocated((uptr)ptr, (uptr)zone_ptr, zone_name, &stack);
+ return;
}
}
@@ -207,10 +204,6 @@ void mz_free(malloc_zone_t *zone, void *ptr) {
free_common(zone, ptr);
}
-void cf_free(void *ptr, void *info) {
- free_common(info, ptr);
-}
-
void *mz_realloc(malloc_zone_t *zone, void *ptr, size_t size) {
if (!ptr) {
GET_STACK_TRACE_MALLOC;
@@ -230,29 +223,11 @@ void *mz_realloc(malloc_zone_t *zone, void *ptr, size_t size) {
}
}
-void *cf_realloc(void *ptr, CFIndex size, CFOptionFlags hint, void *info) {
- if (!ptr) {
- GET_STACK_TRACE_MALLOC;
- return asan_malloc(size, &stack);
- } else {
- if (asan_mz_size(ptr)) {
- GET_STACK_TRACE_MALLOC;
- return asan_realloc(ptr, size, &stack);
- } else {
- // We can't recover from reallocating an unknown address, because
- // this would require reading at most |size| bytes from
- // potentially unaccessible memory.
- GET_STACK_TRACE_FREE;
- GET_ZONE_FOR_PTR(ptr);
- ReportMacCfReallocUnknown((uptr)ptr, (uptr)zone_ptr, zone_name, &stack);
- }
- }
-}
-
void mz_destroy(malloc_zone_t* zone) {
// A no-op -- we will not be destroyed!
- Printf("mz_destroy() called -- ignoring\n");
+ Report("mz_destroy() called -- ignoring\n");
}
+
// from AvailabilityMacros.h
#if defined(MAC_OS_X_VERSION_10_6) && \
MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_6
@@ -309,7 +284,7 @@ void mi_force_unlock(malloc_zone_t *zone) {
void mi_statistics(malloc_zone_t *zone, malloc_statistics_t *stats) {
AsanMallocStats malloc_stats;
- asanThreadRegistry().FillMallocStatistics(&malloc_stats);
+ FillMallocStatistics(&malloc_stats);
CHECK(sizeof(malloc_statistics_t) == sizeof(AsanMallocStats));
internal_memcpy(stats, &malloc_stats, sizeof(malloc_statistics_t));
}
@@ -324,23 +299,7 @@ boolean_t mi_zone_locked(malloc_zone_t *zone) {
} // unnamed namespace
-extern int __CFRuntimeClassTableSize;
-
namespace __asan {
-void MaybeReplaceCFAllocator() {
- static CFAllocatorContext asan_context = {
- /*version*/ 0, /*info*/ &asan_zone,
- /*retain*/ 0, /*release*/ 0,
- /*copyDescription*/0,
- /*allocate*/ &cf_malloc,
- /*reallocate*/ &cf_realloc,
- /*deallocate*/ &cf_free,
- /*preferredSize*/ 0 };
- if (!cf_asan)
- cf_asan = CFAllocatorCreate(kCFAllocatorUseContext, &asan_context);
- if (flags()->replace_cfallocator && CFAllocatorGetDefault() != cf_asan)
- CFAllocatorSetDefault(cf_asan);
-}
void ReplaceSystemMalloc() {
static malloc_introspection_t asan_introspection;
@@ -380,42 +339,11 @@ void ReplaceSystemMalloc() {
asan_zone.free_definite_size = 0;
asan_zone.memalign = &mz_memalign;
asan_introspection.zone_locked = &mi_zone_locked;
-
- // Request the default purgable zone to force its creation. The
- // current default zone is registered with the purgable zone for
- // doing tiny and small allocs. Sadly, it assumes that the default
- // zone is the szone implementation from OS X and will crash if it
- // isn't. By creating the zone now, this will be true and changing
- // the default zone won't cause a problem. (OS X 10.6 and higher.)
- system_purgeable_zone = malloc_default_purgeable_zone();
#endif
- // Register the ASan zone. At this point, it will not be the
- // default zone.
+ // Register the ASan zone.
malloc_zone_register(&asan_zone);
-
- // Unregister and reregister the default zone. Unregistering swaps
- // the specified zone with the last one registered which for the
- // default zone makes the more recently registered zone the default
- // zone. The default zone is then re-registered to ensure that
- // allocations made from it earlier will be handled correctly.
- // Things are not guaranteed to work that way, but it's how they work now.
- system_malloc_zone = malloc_default_zone();
- malloc_zone_unregister(system_malloc_zone);
- malloc_zone_register(system_malloc_zone);
- // Make sure the default allocator was replaced.
- CHECK(malloc_default_zone() == &asan_zone);
-
- // If __CFInitialize() hasn't been called yet, cf_asan will be created and
- // installed as the default allocator after __CFInitialize() finishes (see
- // the interceptor for __CFInitialize() above). Otherwise install cf_asan
- // right now. On both Snow Leopard and Lion __CFInitialize() calls
- // __CFAllocatorInitialize(), which initializes the _base._cfisa field of
- // the default allocators we check here.
- if (((CFRuntimeBase*)kCFAllocatorSystemDefault)->_cfisa) {
- MaybeReplaceCFAllocator();
- }
}
} // namespace __asan
-#endif // __APPLE__
+#endif // SANITIZER_MAC
diff --git a/lib/asan/asan_malloc_win.cc b/lib/asan/asan_malloc_win.cc
index 9fcfea56384f..31fb777c7045 100644
--- a/lib/asan/asan_malloc_win.cc
+++ b/lib/asan/asan_malloc_win.cc
@@ -11,7 +11,9 @@
//
// Windows-specific malloc interception.
//===----------------------------------------------------------------------===//
-#ifdef _WIN32
+
+#include "sanitizer_common/sanitizer_platform.h"
+#if SANITIZER_WINDOWS
#include "asan_allocator.h"
#include "asan_interceptors.h"
diff --git a/lib/asan/asan_mapping.h b/lib/asan/asan_mapping.h
index 5e3067031f4a..f04629222419 100644
--- a/lib/asan/asan_mapping.h
+++ b/lib/asan/asan_mapping.h
@@ -18,6 +18,37 @@
// The full explanation of the memory mapping could be found here:
// http://code.google.com/p/address-sanitizer/wiki/AddressSanitizerAlgorithm
+//
+// Typical shadow mapping on Linux/x86_64 with SHADOW_OFFSET == 0x00007fff8000:
+// || `[0x10007fff8000, 0x7fffffffffff]` || HighMem ||
+// || `[0x02008fff7000, 0x10007fff7fff]` || HighShadow ||
+// || `[0x00008fff7000, 0x02008fff6fff]` || ShadowGap ||
+// || `[0x00007fff8000, 0x00008fff6fff]` || LowShadow ||
+// || `[0x000000000000, 0x00007fff7fff]` || LowMem ||
+//
+// When SHADOW_OFFSET is zero (-pie):
+// || `[0x100000000000, 0x7fffffffffff]` || HighMem ||
+// || `[0x020000000000, 0x0fffffffffff]` || HighShadow ||
+// || `[0x000000040000, 0x01ffffffffff]` || ShadowGap ||
+//
+// Special case when something is already mapped between
+// 0x003000000000 and 0x005000000000 (e.g. when prelink is installed):
+// || `[0x10007fff8000, 0x7fffffffffff]` || HighMem ||
+// || `[0x02008fff7000, 0x10007fff7fff]` || HighShadow ||
+// || `[0x005000000000, 0x02008fff6fff]` || ShadowGap3 ||
+// || `[0x003000000000, 0x004fffffffff]` || MidMem ||
+// || `[0x000a7fff8000, 0x002fffffffff]` || ShadowGap2 ||
+// || `[0x00067fff8000, 0x000a7fff7fff]` || MidShadow ||
+// || `[0x00008fff7000, 0x00067fff7fff]` || ShadowGap ||
+// || `[0x00007fff8000, 0x00008fff6fff]` || LowShadow ||
+// || `[0x000000000000, 0x00007fff7fff]` || LowMem ||
+//
+// Default Linux/i386 mapping:
+// || `[0x40000000, 0xffffffff]` || HighMem ||
+// || `[0x28000000, 0x3fffffff]` || HighShadow ||
+// || `[0x24000000, 0x27ffffff]` || ShadowGap ||
+// || `[0x20000000, 0x23ffffff]` || LowShadow ||
+// || `[0x00000000, 0x1fffffff]` || LowMem ||
#if ASAN_FLEXIBLE_MAPPING_AND_OFFSET == 1
extern SANITIZER_INTERFACE_ATTRIBUTE uptr __asan_mapping_scale;
@@ -25,7 +56,7 @@ extern SANITIZER_INTERFACE_ATTRIBUTE uptr __asan_mapping_offset;
# define SHADOW_SCALE (__asan_mapping_scale)
# define SHADOW_OFFSET (__asan_mapping_offset)
#else
-# if ASAN_ANDROID
+# if SANITIZER_ANDROID
# define SHADOW_SCALE (3)
# define SHADOW_OFFSET (0)
# else
@@ -36,27 +67,20 @@ extern SANITIZER_INTERFACE_ATTRIBUTE uptr __asan_mapping_offset;
# if defined(__powerpc64__)
# define SHADOW_OFFSET (1ULL << 41)
# else
-# define SHADOW_OFFSET (1ULL << 44)
+# if SANITIZER_MAC
+# define SHADOW_OFFSET (1ULL << 44)
+# else
+# define SHADOW_OFFSET 0x7fff8000ULL
+# endif
# endif
# endif
# endif
#endif // ASAN_FLEXIBLE_MAPPING_AND_OFFSET
#define SHADOW_GRANULARITY (1ULL << SHADOW_SCALE)
-#define MEM_TO_SHADOW(mem) (((mem) >> SHADOW_SCALE) | (SHADOW_OFFSET))
+#define MEM_TO_SHADOW(mem) (((mem) >> SHADOW_SCALE) + (SHADOW_OFFSET))
#define SHADOW_TO_MEM(shadow) (((shadow) - SHADOW_OFFSET) << SHADOW_SCALE)
-#if SANITIZER_WORDSIZE == 64
-# if defined(__powerpc64__)
- static const uptr kHighMemEnd = 0x00000fffffffffffUL;
-# else
- static const uptr kHighMemEnd = 0x00007fffffffffffUL;
-# endif
-#else // SANITIZER_WORDSIZE == 32
- static const uptr kHighMemEnd = 0xffffffff;
-#endif // SANITIZER_WORDSIZE
-
-
#define kLowMemBeg 0
#define kLowMemEnd (SHADOW_OFFSET ? SHADOW_OFFSET - 1 : 0)
@@ -68,59 +92,121 @@ extern SANITIZER_INTERFACE_ATTRIBUTE uptr __asan_mapping_offset;
#define kHighShadowBeg MEM_TO_SHADOW(kHighMemBeg)
#define kHighShadowEnd MEM_TO_SHADOW(kHighMemEnd)
+# define kMidShadowBeg MEM_TO_SHADOW(kMidMemBeg)
+# define kMidShadowEnd MEM_TO_SHADOW(kMidMemEnd)
+
// With the zero shadow base we can not actually map pages starting from 0.
// This constant is somewhat arbitrary.
#define kZeroBaseShadowStart (1 << 18)
#define kShadowGapBeg (kLowShadowEnd ? kLowShadowEnd + 1 \
: kZeroBaseShadowStart)
-#define kShadowGapEnd (kHighShadowBeg - 1)
+#define kShadowGapEnd ((kMidMemBeg ? kMidShadowBeg : kHighShadowBeg) - 1)
-#define kGlobalAndStackRedzone \
- (SHADOW_GRANULARITY < 32 ? 32 : SHADOW_GRANULARITY)
+#define kShadowGap2Beg (kMidMemBeg ? kMidShadowEnd + 1 : 0)
+#define kShadowGap2End (kMidMemBeg ? kMidMemBeg - 1 : 0)
+
+#define kShadowGap3Beg (kMidMemBeg ? kMidMemEnd + 1 : 0)
+#define kShadowGap3End (kMidMemBeg ? kHighShadowBeg - 1 : 0)
+
+#define DO_ASAN_MAPPING_PROFILE 0 // Set to 1 to profile the functions below.
+
+#if DO_ASAN_MAPPING_PROFILE
+# define PROFILE_ASAN_MAPPING() AsanMappingProfile[__LINE__]++;
+#else
+# define PROFILE_ASAN_MAPPING()
+#endif
+
+// If 1, all shadow boundaries are constants.
+// Don't set to 1 other than for testing.
+#define ASAN_FIXED_MAPPING 0
namespace __asan {
+extern uptr AsanMappingProfile[];
+
+#if ASAN_FIXED_MAPPING
+// Fixed mapping for 64-bit Linux. Mostly used for performance comparison
+// with non-fixed mapping. As of r175253 (Feb 2013) the performance
+// difference between fixed and non-fixed mapping is below the noise level.
+static uptr kHighMemEnd = 0x7fffffffffffULL;
+static uptr kMidMemBeg = 0x3000000000ULL;
+static uptr kMidMemEnd = 0x4fffffffffULL;
+#else
+SANITIZER_INTERFACE_ATTRIBUTE
+extern uptr kHighMemEnd, kMidMemBeg, kMidMemEnd; // Initialized in __asan_init.
+#endif
+
static inline bool AddrIsInLowMem(uptr a) {
+ PROFILE_ASAN_MAPPING();
return a < kLowMemEnd;
}
static inline bool AddrIsInLowShadow(uptr a) {
+ PROFILE_ASAN_MAPPING();
return a >= kLowShadowBeg && a <= kLowShadowEnd;
}
static inline bool AddrIsInHighMem(uptr a) {
+ PROFILE_ASAN_MAPPING();
return a >= kHighMemBeg && a <= kHighMemEnd;
}
+static inline bool AddrIsInMidMem(uptr a) {
+ PROFILE_ASAN_MAPPING();
+ return kMidMemBeg && a >= kMidMemBeg && a <= kMidMemEnd;
+}
+
static inline bool AddrIsInMem(uptr a) {
- return AddrIsInLowMem(a) || AddrIsInHighMem(a);
+ PROFILE_ASAN_MAPPING();
+ return AddrIsInLowMem(a) || AddrIsInMidMem(a) || AddrIsInHighMem(a);
}
static inline uptr MemToShadow(uptr p) {
+ PROFILE_ASAN_MAPPING();
CHECK(AddrIsInMem(p));
return MEM_TO_SHADOW(p);
}
static inline bool AddrIsInHighShadow(uptr a) {
- return a >= kHighShadowBeg && a <= kHighMemEnd;
+ PROFILE_ASAN_MAPPING();
+ return a >= kHighShadowBeg && a <= kHighMemEnd;
+}
+
+static inline bool AddrIsInMidShadow(uptr a) {
+ PROFILE_ASAN_MAPPING();
+ return kMidMemBeg && a >= kMidShadowBeg && a <= kMidMemEnd;
}
static inline bool AddrIsInShadow(uptr a) {
- return AddrIsInLowShadow(a) || AddrIsInHighShadow(a);
+ PROFILE_ASAN_MAPPING();
+ return AddrIsInLowShadow(a) || AddrIsInMidShadow(a) || AddrIsInHighShadow(a);
}
static inline bool AddrIsInShadowGap(uptr a) {
+ PROFILE_ASAN_MAPPING();
+ if (kMidMemBeg) {
+ if (a <= kShadowGapEnd)
+ return SHADOW_OFFSET == 0 || a >= kShadowGapBeg;
+ return (a >= kShadowGap2Beg && a <= kShadowGap2End) ||
+ (a >= kShadowGap3Beg && a <= kShadowGap3End);
+ }
+ // In zero-based shadow mode we treat addresses near zero as addresses
+ // in shadow gap as well.
+ if (SHADOW_OFFSET == 0)
+ return a <= kShadowGapEnd;
return a >= kShadowGapBeg && a <= kShadowGapEnd;
}
static inline bool AddrIsAlignedByGranularity(uptr a) {
+ PROFILE_ASAN_MAPPING();
return (a & (SHADOW_GRANULARITY - 1)) == 0;
}
static inline bool AddressIsPoisoned(uptr a) {
+ PROFILE_ASAN_MAPPING();
const uptr kAccessSize = 1;
- u8 *shadow_address = (u8*)MemToShadow(a);
+ u8 *shadow_address = (u8*)MEM_TO_SHADOW(a);
s8 shadow_value = *shadow_address;
if (shadow_value) {
u8 last_accessed_byte = (a & (SHADOW_GRANULARITY - 1))
@@ -130,6 +216,9 @@ static inline bool AddressIsPoisoned(uptr a) {
return false;
}
+// Must be after all calls to PROFILE_ASAN_MAPPING().
+static const uptr kAsanMappingProfileSize = __LINE__;
+
} // namespace __asan
#endif // ASAN_MAPPING_H
diff --git a/lib/asan/asan_new_delete.cc b/lib/asan/asan_new_delete.cc
index 5d1f23c542e6..d5eb6eca9321 100644
--- a/lib/asan/asan_new_delete.cc
+++ b/lib/asan/asan_new_delete.cc
@@ -28,7 +28,8 @@ void ReplaceOperatorsNewAndDelete() { }
using namespace __asan; // NOLINT
// On Android new() goes through malloc interceptors.
-#if !ASAN_ANDROID
+// See also https://code.google.com/p/address-sanitizer/issues/detail?id=131.
+#if !SANITIZER_ANDROID
// Fake std::nothrow_t to avoid including <new>.
namespace std {
@@ -39,6 +40,14 @@ struct nothrow_t {};
GET_STACK_TRACE_MALLOC;\
return asan_memalign(0, size, &stack, type);
+// On OS X it's not enough to just provide our own 'operator new' and
+// 'operator delete' implementations, because they're going to be in the
+// runtime dylib, and the main executable will depend on both the runtime
+// dylib and libstdc++, each of those'll have its implementation of new and
+// delete.
+// To make sure that C++ allocation/deallocation operators are overridden on
+// OS X we need to intercept them using their mangled names.
+#if !SANITIZER_MAC
INTERCEPTOR_ATTRIBUTE
void *operator new(size_t size) { OPERATOR_NEW_BODY(FROM_NEW); }
INTERCEPTOR_ATTRIBUTE
@@ -50,10 +59,26 @@ INTERCEPTOR_ATTRIBUTE
void *operator new[](size_t size, std::nothrow_t const&)
{ OPERATOR_NEW_BODY(FROM_NEW_BR); }
+#else // SANITIZER_MAC
+INTERCEPTOR(void *, _Znwm, size_t size) {
+ OPERATOR_NEW_BODY(FROM_NEW);
+}
+INTERCEPTOR(void *, _Znam, size_t size) {
+ OPERATOR_NEW_BODY(FROM_NEW_BR);
+}
+INTERCEPTOR(void *, _ZnwmRKSt9nothrow_t, size_t size, std::nothrow_t const&) {
+ OPERATOR_NEW_BODY(FROM_NEW);
+}
+INTERCEPTOR(void *, _ZnamRKSt9nothrow_t, size_t size, std::nothrow_t const&) {
+ OPERATOR_NEW_BODY(FROM_NEW_BR);
+}
+#endif
+
#define OPERATOR_DELETE_BODY(type) \
GET_STACK_TRACE_FREE;\
asan_free(ptr, &stack, type);
+#if !SANITIZER_MAC
INTERCEPTOR_ATTRIBUTE
void operator delete(void *ptr) { OPERATOR_DELETE_BODY(FROM_NEW); }
INTERCEPTOR_ATTRIBUTE
@@ -65,4 +90,19 @@ INTERCEPTOR_ATTRIBUTE
void operator delete[](void *ptr, std::nothrow_t const&)
{ OPERATOR_DELETE_BODY(FROM_NEW_BR); }
+#else // SANITIZER_MAC
+INTERCEPTOR(void, _ZdlPv, void *ptr) {
+ OPERATOR_DELETE_BODY(FROM_NEW);
+}
+INTERCEPTOR(void, _ZdaPv, void *ptr) {
+ OPERATOR_DELETE_BODY(FROM_NEW_BR);
+}
+INTERCEPTOR(void, _ZdlPvRKSt9nothrow_t, void *ptr, std::nothrow_t const&) {
+ OPERATOR_DELETE_BODY(FROM_NEW);
+}
+INTERCEPTOR(void, _ZdaPvRKSt9nothrow_t, void *ptr, std::nothrow_t const&) {
+ OPERATOR_DELETE_BODY(FROM_NEW_BR);
+}
+#endif
+
#endif
diff --git a/lib/asan/asan_poisoning.cc b/lib/asan/asan_poisoning.cc
index dc5749243569..772b5e64b027 100644
--- a/lib/asan/asan_poisoning.cc
+++ b/lib/asan/asan_poisoning.cc
@@ -12,10 +12,7 @@
// Shadow memory poisoning by ASan RTL and by user application.
//===----------------------------------------------------------------------===//
-#include "asan_interceptors.h"
-#include "asan_internal.h"
-#include "asan_mapping.h"
-#include "sanitizer/asan_interface.h"
+#include "asan_poisoning.h"
#include "sanitizer_common/sanitizer_libc.h"
namespace __asan {
@@ -23,11 +20,11 @@ namespace __asan {
void PoisonShadow(uptr addr, uptr size, u8 value) {
if (!flags()->poison_heap) return;
CHECK(AddrIsAlignedByGranularity(addr));
+ CHECK(AddrIsInMem(addr));
CHECK(AddrIsAlignedByGranularity(addr + size));
- uptr shadow_beg = MemToShadow(addr);
- uptr shadow_end = MemToShadow(addr + size - SHADOW_GRANULARITY) + 1;
- CHECK(REAL(memset) != 0);
- REAL(memset)((void*)shadow_beg, value, shadow_end - shadow_beg);
+ CHECK(AddrIsInMem(addr + size - SHADOW_GRANULARITY));
+ CHECK(REAL(memset));
+ FastPoisonShadow(addr, size, value);
}
void PoisonShadowPartialRightRedzone(uptr addr,
@@ -36,20 +33,10 @@ void PoisonShadowPartialRightRedzone(uptr addr,
u8 value) {
if (!flags()->poison_heap) return;
CHECK(AddrIsAlignedByGranularity(addr));
- u8 *shadow = (u8*)MemToShadow(addr);
- for (uptr i = 0; i < redzone_size;
- i += SHADOW_GRANULARITY, shadow++) {
- if (i + SHADOW_GRANULARITY <= size) {
- *shadow = 0; // fully addressable
- } else if (i >= size) {
- *shadow = (SHADOW_GRANULARITY == 128) ? 0xff : value; // unaddressable
- } else {
- *shadow = size - i; // first size-i bytes are addressable
- }
- }
+ CHECK(AddrIsInMem(addr));
+ FastPoisonShadowPartialRightRedzone(addr, size, redzone_size, value);
}
-
struct ShadowSegmentEndpoint {
u8 *chunk;
s8 offset; // in [0, SHADOW_GRANULARITY)
@@ -182,6 +169,55 @@ uptr __asan_region_is_poisoned(uptr beg, uptr size) {
return 0;
}
+#define CHECK_SMALL_REGION(p, size, isWrite) \
+ do { \
+ uptr __p = reinterpret_cast<uptr>(p); \
+ uptr __size = size; \
+ if (UNLIKELY(__asan::AddressIsPoisoned(__p) || \
+ __asan::AddressIsPoisoned(__p + __size - 1))) { \
+ GET_CURRENT_PC_BP_SP; \
+ uptr __bad = __asan_region_is_poisoned(__p, __size); \
+ __asan_report_error(pc, bp, sp, __bad, isWrite, __size);\
+ } \
+ } while (false); \
+
+
+extern "C" SANITIZER_INTERFACE_ATTRIBUTE
+u16 __sanitizer_unaligned_load16(const u16 *p) {
+ CHECK_SMALL_REGION(p, sizeof(*p), false);
+ return *p;
+}
+
+extern "C" SANITIZER_INTERFACE_ATTRIBUTE
+u32 __sanitizer_unaligned_load32(const u32 *p) {
+ CHECK_SMALL_REGION(p, sizeof(*p), false);
+ return *p;
+}
+
+extern "C" SANITIZER_INTERFACE_ATTRIBUTE
+u64 __sanitizer_unaligned_load64(const u64 *p) {
+ CHECK_SMALL_REGION(p, sizeof(*p), false);
+ return *p;
+}
+
+extern "C" SANITIZER_INTERFACE_ATTRIBUTE
+void __sanitizer_unaligned_store16(u16 *p, u16 x) {
+ CHECK_SMALL_REGION(p, sizeof(*p), true);
+ *p = x;
+}
+
+extern "C" SANITIZER_INTERFACE_ATTRIBUTE
+void __sanitizer_unaligned_store32(u32 *p, u32 x) {
+ CHECK_SMALL_REGION(p, sizeof(*p), true);
+ *p = x;
+}
+
+extern "C" SANITIZER_INTERFACE_ATTRIBUTE
+void __sanitizer_unaligned_store64(u64 *p, u64 x) {
+ CHECK_SMALL_REGION(p, sizeof(*p), true);
+ *p = x;
+}
+
// This is a simplified version of __asan_(un)poison_memory_region, which
// assumes that left border of region to be poisoned is properly aligned.
static void PoisonAlignedStackMemory(uptr addr, uptr size, bool do_poison) {
diff --git a/lib/asan/asan_poisoning.h b/lib/asan/asan_poisoning.h
new file mode 100644
index 000000000000..86f81e5d0ae5
--- /dev/null
+++ b/lib/asan/asan_poisoning.h
@@ -0,0 +1,58 @@
+//===-- asan_poisoning.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.
+//
+// Shadow memory poisoning by ASan RTL and by user application.
+//===----------------------------------------------------------------------===//
+
+#include "asan_interceptors.h"
+#include "asan_internal.h"
+#include "asan_mapping.h"
+
+namespace __asan {
+
+// Poisons the shadow memory for "size" bytes starting from "addr".
+void PoisonShadow(uptr addr, uptr size, u8 value);
+
+// Poisons the shadow memory for "redzone_size" bytes starting from
+// "addr + size".
+void PoisonShadowPartialRightRedzone(uptr addr,
+ uptr size,
+ uptr redzone_size,
+ u8 value);
+
+// Fast versions of PoisonShadow and PoisonShadowPartialRightRedzone that
+// assume that memory addresses are properly aligned. Use in
+// performance-critical code with care.
+ALWAYS_INLINE void FastPoisonShadow(uptr aligned_beg, uptr aligned_size,
+ u8 value) {
+ DCHECK(flags()->poison_heap);
+ uptr shadow_beg = MEM_TO_SHADOW(aligned_beg);
+ uptr shadow_end = MEM_TO_SHADOW(
+ aligned_beg + aligned_size - SHADOW_GRANULARITY) + 1;
+ REAL(memset)((void*)shadow_beg, value, shadow_end - shadow_beg);
+}
+
+ALWAYS_INLINE void FastPoisonShadowPartialRightRedzone(
+ uptr aligned_addr, uptr size, uptr redzone_size, u8 value) {
+ DCHECK(flags()->poison_heap);
+ u8 *shadow = (u8*)MEM_TO_SHADOW(aligned_addr);
+ for (uptr i = 0; i < redzone_size; i += SHADOW_GRANULARITY, shadow++) {
+ if (i + SHADOW_GRANULARITY <= size) {
+ *shadow = 0; // fully addressable
+ } else if (i >= size) {
+ *shadow = (SHADOW_GRANULARITY == 128) ? 0xff : value; // unaddressable
+ } else {
+ *shadow = size - i; // first size-i bytes are addressable
+ }
+ }
+}
+
+} // namespace __asan
diff --git a/lib/asan/asan_posix.cc b/lib/asan/asan_posix.cc
index ceaf120fc803..5126a756d1c8 100644
--- a/lib/asan/asan_posix.cc
+++ b/lib/asan/asan_posix.cc
@@ -11,14 +11,15 @@
//
// Posix-specific details.
//===----------------------------------------------------------------------===//
-#if defined(__linux__) || defined(__APPLE__)
+
+#include "sanitizer_common/sanitizer_platform.h"
+#if SANITIZER_LINUX || SANITIZER_MAC
#include "asan_internal.h"
#include "asan_interceptors.h"
#include "asan_mapping.h"
#include "asan_report.h"
#include "asan_stack.h"
-#include "asan_thread_registry.h"
#include "sanitizer_common/sanitizer_libc.h"
#include "sanitizer_common/sanitizer_procmaps.h"
@@ -42,7 +43,7 @@ static void MaybeInstallSigaction(int signum,
sigact.sa_sigaction = handler;
sigact.sa_flags = SA_SIGINFO;
if (flags()->use_sigaltstack) sigact.sa_flags |= SA_ONSTACK;
- CHECK(0 == REAL(sigaction)(signum, &sigact, 0));
+ CHECK_EQ(0, REAL(sigaction)(signum, &sigact, 0));
if (flags()->verbosity >= 1) {
Report("Installed the sigaction for signal %d\n", signum);
}
@@ -59,7 +60,7 @@ static void ASAN_OnSIGSEGV(int, siginfo_t *siginfo, void *context) {
void SetAlternateSignalStack() {
stack_t altstack, oldstack;
- CHECK(0 == sigaltstack(0, &oldstack));
+ CHECK_EQ(0, sigaltstack(0, &oldstack));
// If the alternate stack is already in place, do nothing.
if ((oldstack.ss_flags & SS_DISABLE) == 0) return;
// TODO(glider): the mapped stack should have the MAP_STACK flag in the
@@ -69,10 +70,10 @@ void SetAlternateSignalStack() {
altstack.ss_sp = base;
altstack.ss_flags = 0;
altstack.ss_size = kAltStackSize;
- CHECK(0 == sigaltstack(&altstack, 0));
+ CHECK_EQ(0, sigaltstack(&altstack, 0));
if (flags()->verbosity > 0) {
Report("Alternative stack for T%d set: [%p,%p)\n",
- asanThreadRegistry().GetCurrentTidOrInvalid(),
+ GetCurrentTidOrInvalid(),
altstack.ss_sp, (char*)altstack.ss_sp + altstack.ss_size);
}
}
@@ -82,7 +83,7 @@ void UnsetAlternateSignalStack() {
altstack.ss_sp = 0;
altstack.ss_flags = SS_DISABLE;
altstack.ss_size = 0;
- CHECK(0 == sigaltstack(&altstack, &oldstack));
+ CHECK_EQ(0, sigaltstack(&altstack, &oldstack));
UnmapOrDie(oldstack.ss_sp, oldstack.ss_size);
}
@@ -102,7 +103,7 @@ static bool tsd_key_inited = false;
void AsanTSDInit(void (*destructor)(void *tsd)) {
CHECK(!tsd_key_inited);
tsd_key_inited = true;
- CHECK(0 == pthread_key_create(&tsd_key, destructor));
+ CHECK_EQ(0, pthread_key_create(&tsd_key, destructor));
}
void *AsanTSDGet() {
@@ -117,4 +118,4 @@ void AsanTSDSet(void *tsd) {
} // namespace __asan
-#endif // __linux__ || __APPLE_
+#endif // SANITIZER_LINUX || SANITIZER_MAC
diff --git a/lib/asan/asan_preinit.cc b/lib/asan/asan_preinit.cc
new file mode 100644
index 000000000000..586f551c23c3
--- /dev/null
+++ b/lib/asan/asan_preinit.cc
@@ -0,0 +1,31 @@
+//===-- asan_preinit.cc ---------------------------------------------------===//
+//
+// 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.
+//
+// Call __asan_init at the very early stage of process startup.
+// On Linux we use .preinit_array section (unless PIC macro is defined).
+//===----------------------------------------------------------------------===//
+#include "asan_internal.h"
+
+#if ASAN_USE_PREINIT_ARRAY && !defined(PIC)
+ // On Linux, we force __asan_init to be called before anyone else
+ // by placing it into .preinit_array section.
+ // FIXME: do we have anything like this on Mac?
+ // The symbol is called __local_asan_preinit, because it's not intended to be
+ // exported.
+ __attribute__((section(".preinit_array"), used))
+ void (*__local_asan_preinit)(void) = __asan_init;
+#elif SANITIZER_WINDOWS && defined(_DLL)
+ // On Windows, when using dynamic CRT (/MD), we can put a pointer
+ // to __asan_init into the global list of C initializers.
+ // See crt0dat.c in the CRT sources for the details.
+ #pragma section(".CRT$XIB", long, read) // NOLINT
+ __declspec(allocate(".CRT$XIB")) void (*__asan_preinit)() = __asan_init;
+#endif
diff --git a/lib/asan/asan_report.cc b/lib/asan/asan_report.cc
index 35ab9cabde67..aeeebf452ca8 100644
--- a/lib/asan/asan_report.cc
+++ b/lib/asan/asan_report.cc
@@ -17,8 +17,8 @@
#include "asan_report.h"
#include "asan_stack.h"
#include "asan_thread.h"
-#include "asan_thread_registry.h"
#include "sanitizer_common/sanitizer_common.h"
+#include "sanitizer_common/sanitizer_flags.h"
#include "sanitizer_common/sanitizer_report_decorator.h"
#include "sanitizer_common/sanitizer_symbolizer.h"
@@ -120,19 +120,7 @@ static void PrintShadowBytes(const char *before, u8 *bytes,
Printf("\n");
}
-static void PrintShadowMemoryForAddress(uptr addr) {
- if (!AddrIsInMem(addr))
- return;
- uptr shadow_addr = MemToShadow(addr);
- const uptr n_bytes_per_row = 16;
- uptr aligned_shadow = shadow_addr & ~(n_bytes_per_row - 1);
- Printf("Shadow bytes around the buggy address:\n");
- for (int i = -5; i <= 5; i++) {
- const char *prefix = (i == 0) ? "=>" : " ";
- PrintShadowBytes(prefix,
- (u8*)(aligned_shadow + i * n_bytes_per_row),
- (u8*)shadow_addr, n_bytes_per_row);
- }
+static void PrintLegend() {
Printf("Shadow byte legend (one shadow byte represents %d "
"application bytes):\n", (int)SHADOW_GRANULARITY);
PrintShadowByte(" Addressable: ", 0);
@@ -141,8 +129,8 @@ static void PrintShadowMemoryForAddress(uptr addr) {
PrintShadowByte("", i, " ");
Printf("\n");
PrintShadowByte(" Heap left redzone: ", kAsanHeapLeftRedzoneMagic);
- PrintShadowByte(" Heap righ redzone: ", kAsanHeapRightRedzoneMagic);
- PrintShadowByte(" Freed Heap region: ", kAsanHeapFreeMagic);
+ PrintShadowByte(" Heap right redzone: ", kAsanHeapRightRedzoneMagic);
+ PrintShadowByte(" Freed heap region: ", kAsanHeapFreeMagic);
PrintShadowByte(" Stack left redzone: ", kAsanStackLeftRedzoneMagic);
PrintShadowByte(" Stack mid redzone: ", kAsanStackMidRedzoneMagic);
PrintShadowByte(" Stack right redzone: ", kAsanStackRightRedzoneMagic);
@@ -155,6 +143,23 @@ static void PrintShadowMemoryForAddress(uptr addr) {
PrintShadowByte(" ASan internal: ", kAsanInternalHeapMagic);
}
+static void PrintShadowMemoryForAddress(uptr addr) {
+ if (!AddrIsInMem(addr))
+ return;
+ uptr shadow_addr = MemToShadow(addr);
+ const uptr n_bytes_per_row = 16;
+ uptr aligned_shadow = shadow_addr & ~(n_bytes_per_row - 1);
+ Printf("Shadow bytes around the buggy address:\n");
+ for (int i = -5; i <= 5; i++) {
+ const char *prefix = (i == 0) ? "=>" : " ";
+ PrintShadowBytes(prefix,
+ (u8*)(aligned_shadow + i * n_bytes_per_row),
+ (u8*)shadow_addr, n_bytes_per_row);
+ }
+ if (flags()->print_legend)
+ PrintLegend();
+}
+
static void PrintZoneForPointer(uptr ptr, uptr zone_ptr,
const char *zone_name) {
if (zone_ptr) {
@@ -176,30 +181,43 @@ static bool IsASCII(unsigned char c) {
return /*0x00 <= c &&*/ c <= 0x7F;
}
+static const char *MaybeDemangleGlobalName(const char *name) {
+ // We can spoil names of globals with C linkage, so use an heuristic
+ // approach to check if the name should be demangled.
+ return (name[0] == '_' && name[1] == 'Z') ? Demangle(name) : name;
+}
+
// Check if the global is a zero-terminated ASCII string. If so, print it.
static void PrintGlobalNameIfASCII(const __asan_global &g) {
for (uptr p = g.beg; p < g.beg + g.size - 1; p++) {
- if (!IsASCII(*(unsigned char*)p)) return;
+ unsigned char c = *(unsigned char*)p;
+ if (c == '\0' || !IsASCII(c)) return;
}
- if (*(char*)(g.beg + g.size - 1) != 0) return;
- Printf(" '%s' is ascii string '%s'\n", g.name, (char*)g.beg);
+ if (*(char*)(g.beg + g.size - 1) != '\0') return;
+ Printf(" '%s' is ascii string '%s'\n",
+ MaybeDemangleGlobalName(g.name), (char*)g.beg);
}
-bool DescribeAddressRelativeToGlobal(uptr addr, const __asan_global &g) {
- if (addr < g.beg - kGlobalAndStackRedzone) return false;
+bool DescribeAddressRelativeToGlobal(uptr addr, uptr size,
+ const __asan_global &g) {
+ static const uptr kMinimalDistanceFromAnotherGlobal = 64;
+ if (addr <= g.beg - kMinimalDistanceFromAnotherGlobal) return false;
if (addr >= g.beg + g.size_with_redzone) return false;
Decorator d;
Printf("%s", d.Location());
- Printf("%p is located ", (void*)addr);
if (addr < g.beg) {
- Printf("%zd bytes to the left", g.beg - addr);
- } else if (addr >= g.beg + g.size) {
- Printf("%zd bytes to the right", addr - (g.beg + g.size));
+ Printf("%p is located %zd bytes to the left", (void*)addr, g.beg - addr);
+ } else if (addr + size > g.beg + g.size) {
+ if (addr < g.beg + g.size)
+ addr = g.beg + g.size;
+ Printf("%p is located %zd bytes to the right", (void*)addr,
+ addr - (g.beg + g.size));
} else {
- Printf("%zd bytes inside", addr - g.beg); // Can it happen?
+ // Can it happen?
+ Printf("%p is located %zd bytes inside", (void*)addr, addr - g.beg);
}
- Printf(" of global variable '%s' (0x%zx) of size %zu\n",
- g.name, g.beg, g.size);
+ Printf(" of global variable '%s' from '%s' (0x%zx) of size %zu\n",
+ MaybeDemangleGlobalName(g.name), g.module_name, g.beg, g.size);
Printf("%s", d.EndLocation());
PrintGlobalNameIfASCII(g);
return true;
@@ -226,34 +244,70 @@ bool DescribeAddressIfShadow(uptr addr) {
return false;
}
+// Return " (thread_name) " or an empty string if the name is empty.
+const char *ThreadNameWithParenthesis(AsanThreadContext *t, char buff[],
+ uptr buff_len) {
+ const char *name = t->name;
+ if (name[0] == '\0') return "";
+ buff[0] = 0;
+ internal_strncat(buff, " (", 3);
+ internal_strncat(buff, name, buff_len - 4);
+ internal_strncat(buff, ")", 2);
+ return buff;
+}
+
+const char *ThreadNameWithParenthesis(u32 tid, char buff[],
+ uptr buff_len) {
+ if (tid == kInvalidTid) return "";
+ asanThreadRegistry().CheckLocked();
+ AsanThreadContext *t = GetThreadContextByTidLocked(tid);
+ return ThreadNameWithParenthesis(t, buff, buff_len);
+}
+
bool DescribeAddressIfStack(uptr addr, uptr access_size) {
- AsanThread *t = asanThreadRegistry().FindThreadByStackAddress(addr);
+ AsanThread *t = FindThreadByStackAddress(addr);
if (!t) return false;
const sptr kBufSize = 4095;
char buf[kBufSize];
uptr offset = 0;
- const char *frame_descr = t->GetFrameNameByAddr(addr, &offset);
+ uptr frame_pc = 0;
+ char tname[128];
+ const char *frame_descr = t->GetFrameNameByAddr(addr, &offset, &frame_pc);
+
+#ifdef __powerpc64__
+ // On PowerPC64, the address of a function actually points to a
+ // three-doubleword data structure with the first field containing
+ // the address of the function's code.
+ frame_pc = *reinterpret_cast<uptr *>(frame_pc);
+#endif
+
// This string is created by the compiler and has the following form:
- // "FunctioName n alloc_1 alloc_2 ... alloc_n"
+ // "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)));
Decorator d;
Printf("%s", d.Location());
- Printf("Address %p is located at offset %zu "
- "in frame <%s> of T%d's stack:\n",
- (void*)addr, offset, Demangle(buf), t->tid());
+ Printf("Address %p is located in stack of thread T%d%s "
+ "at offset %zu in frame\n",
+ addr, t->tid(),
+ ThreadNameWithParenthesis(t->tid(), tname, sizeof(tname)),
+ offset);
+ // Now we print the frame where the alloca has happened.
+ // We print this frame as a stack trace with one element.
+ // The symbolizer may print more than one frame if inlining was involved.
+ // The frame numbers may be different than those in the stack trace printed
+ // previously. That's unfortunate, but I have no better solution,
+ // especially given that the alloca may be from entirely different place
+ // (e.g. use-after-scope, or different thread's stack).
+ StackTrace alloca_stack;
+ alloca_stack.trace[0] = frame_pc + 16;
+ alloca_stack.size = 1;
Printf("%s", d.EndLocation());
+ PrintStack(&alloca_stack);
// Report the number of stack objects.
char *p;
- uptr n_objects = internal_simple_strtoll(name_end, &p, 10);
- CHECK(n_objects > 0);
+ uptr n_objects = internal_simple_strtoll(frame_descr, &p, 10);
+ CHECK_GT(n_objects, 0);
Printf(" This frame has %zu object(s):\n", n_objects);
// Report all objects in this frame.
for (uptr i = 0; i < n_objects; i++) {
@@ -276,87 +330,73 @@ bool DescribeAddressIfStack(uptr addr, uptr access_size) {
Printf("HINT: this may be a false positive if your program uses "
"some custom stack unwind mechanism or swapcontext\n"
" (longjmp and C++ exceptions *are* supported)\n");
- DescribeThread(t->summary());
+ DescribeThread(t->context());
return true;
}
static void DescribeAccessToHeapChunk(AsanChunkView chunk, uptr addr,
uptr access_size) {
- uptr offset;
+ sptr offset;
Decorator d;
Printf("%s", d.Location());
- Printf("%p is located ", (void*)addr);
- if (chunk.AddrIsInside(addr, access_size, &offset)) {
- Printf("%zu bytes inside of", offset);
- } else if (chunk.AddrIsAtLeft(addr, access_size, &offset)) {
- Printf("%zu bytes to the left of", offset);
+ if (chunk.AddrIsAtLeft(addr, access_size, &offset)) {
+ Printf("%p is located %zd bytes to the left of", (void*)addr, offset);
} else if (chunk.AddrIsAtRight(addr, access_size, &offset)) {
- Printf("%zu bytes to the right of", offset);
+ if (offset < 0) {
+ addr -= offset;
+ offset = 0;
+ }
+ Printf("%p is located %zd bytes to the right of", (void*)addr, offset);
+ } else if (chunk.AddrIsInside(addr, access_size, &offset)) {
+ Printf("%p is located %zd bytes inside of", (void*)addr, offset);
} else {
- Printf(" somewhere around (this is AddressSanitizer bug!)");
+ Printf("%p is located somewhere around (this is AddressSanitizer bug!)",
+ (void*)addr);
}
Printf(" %zu-byte region [%p,%p)\n", chunk.UsedSize(),
(void*)(chunk.Beg()), (void*)(chunk.End()));
Printf("%s", d.EndLocation());
}
-// Return " (thread_name) " or an empty string if the name is empty.
-const char *ThreadNameWithParenthesis(AsanThreadSummary *t, char buff[],
- uptr buff_len) {
- const char *name = t->name();
- if (*name == 0) return "";
- buff[0] = 0;
- internal_strncat(buff, " (", 3);
- internal_strncat(buff, name, buff_len - 4);
- internal_strncat(buff, ")", 2);
- return buff;
-}
-
-const char *ThreadNameWithParenthesis(u32 tid, char buff[],
- uptr buff_len) {
- if (tid == kInvalidTid) return "";
- AsanThreadSummary *t = asanThreadRegistry().FindByTid(tid);
- return ThreadNameWithParenthesis(t, buff, buff_len);
-}
-
void DescribeHeapAddress(uptr addr, uptr access_size) {
AsanChunkView chunk = FindHeapChunkByAddress(addr);
if (!chunk.IsValid()) return;
DescribeAccessToHeapChunk(chunk, addr, access_size);
CHECK(chunk.AllocTid() != kInvalidTid);
- AsanThreadSummary *alloc_thread =
- asanThreadRegistry().FindByTid(chunk.AllocTid());
+ asanThreadRegistry().CheckLocked();
+ AsanThreadContext *alloc_thread =
+ GetThreadContextByTidLocked(chunk.AllocTid());
StackTrace alloc_stack;
chunk.GetAllocStack(&alloc_stack);
- AsanThread *t = asanThreadRegistry().GetCurrent();
+ AsanThread *t = GetCurrentThread();
CHECK(t);
char tname[128];
Decorator d;
if (chunk.FreeTid() != kInvalidTid) {
- AsanThreadSummary *free_thread =
- asanThreadRegistry().FindByTid(chunk.FreeTid());
+ AsanThreadContext *free_thread =
+ GetThreadContextByTidLocked(chunk.FreeTid());
Printf("%sfreed by thread T%d%s here:%s\n", d.Allocation(),
- free_thread->tid(),
+ free_thread->tid,
ThreadNameWithParenthesis(free_thread, tname, sizeof(tname)),
d.EndAllocation());
StackTrace free_stack;
chunk.GetFreeStack(&free_stack);
PrintStack(&free_stack);
Printf("%spreviously allocated by thread T%d%s here:%s\n",
- d.Allocation(), alloc_thread->tid(),
+ d.Allocation(), alloc_thread->tid,
ThreadNameWithParenthesis(alloc_thread, tname, sizeof(tname)),
d.EndAllocation());
PrintStack(&alloc_stack);
- DescribeThread(t->summary());
+ DescribeThread(t->context());
DescribeThread(free_thread);
DescribeThread(alloc_thread);
} else {
Printf("%sallocated by thread T%d%s here:%s\n", d.Allocation(),
- alloc_thread->tid(),
+ alloc_thread->tid,
ThreadNameWithParenthesis(alloc_thread, tname, sizeof(tname)),
d.EndAllocation());
PrintStack(&alloc_stack);
- DescribeThread(t->summary());
+ DescribeThread(t->context());
DescribeThread(alloc_thread);
}
}
@@ -366,7 +406,7 @@ void DescribeAddress(uptr addr, uptr access_size) {
if (DescribeAddressIfShadow(addr))
return;
CHECK(AddrIsInMem(addr));
- if (DescribeAddressIfGlobal(addr))
+ if (DescribeAddressIfGlobal(addr, access_size))
return;
if (DescribeAddressIfStack(addr, access_size))
return;
@@ -376,26 +416,27 @@ void DescribeAddress(uptr addr, uptr access_size) {
// ------------------- Thread description -------------------- {{{1
-void DescribeThread(AsanThreadSummary *summary) {
- CHECK(summary);
+void DescribeThread(AsanThreadContext *context) {
+ CHECK(context);
+ asanThreadRegistry().CheckLocked();
// No need to announce the main thread.
- if (summary->tid() == 0 || summary->announced()) {
+ if (context->tid == 0 || context->announced) {
return;
}
- summary->set_announced(true);
+ context->announced = true;
char tname[128];
- Printf("Thread T%d%s", summary->tid(),
- ThreadNameWithParenthesis(summary->tid(), tname, sizeof(tname)));
+ Printf("Thread T%d%s", context->tid,
+ ThreadNameWithParenthesis(context->tid, tname, sizeof(tname)));
Printf(" created by T%d%s here:\n",
- summary->parent_tid(),
- ThreadNameWithParenthesis(summary->parent_tid(),
+ context->parent_tid,
+ ThreadNameWithParenthesis(context->parent_tid,
tname, sizeof(tname)));
- PrintStack(summary->stack());
+ PrintStack(&context->stack);
// Recursively described parent thread if needed.
if (flags()->print_full_thread_history) {
- AsanThreadSummary *parent_summary =
- asanThreadRegistry().FindByTid(summary->parent_tid());
- DescribeThread(parent_summary);
+ AsanThreadContext *parent_context =
+ GetThreadContextByTidLocked(context->parent_tid);
+ DescribeThread(parent_context);
}
}
@@ -414,25 +455,31 @@ class ScopedInErrorReport {
// they are defined as no-return.
Report("AddressSanitizer: while reporting a bug found another one."
"Ignoring.\n");
- u32 current_tid = asanThreadRegistry().GetCurrentTidOrInvalid();
+ u32 current_tid = GetCurrentTidOrInvalid();
if (current_tid != reporting_thread_tid) {
// ASan found two bugs in different threads simultaneously. Sleep
// long enough to make sure that the thread which started to print
// an error report will finish doing it.
SleepForSeconds(Max(100, flags()->sleep_before_dying + 1));
}
- // If we're still not dead for some reason, use raw Exit() instead of
+ // If we're still not dead for some reason, use raw _exit() instead of
// Die() to bypass any additional checks.
- Exit(flags()->exitcode);
+ internal__exit(flags()->exitcode);
}
ASAN_ON_ERROR();
- reporting_thread_tid = asanThreadRegistry().GetCurrentTidOrInvalid();
+ // Make sure the registry and sanitizer report mutexes are locked while
+ // we're printing an error report.
+ // We can lock them only here to avoid self-deadlock in case of
+ // recursive reports.
+ asanThreadRegistry().Lock();
+ CommonSanitizerReportMutex.Lock();
+ reporting_thread_tid = GetCurrentTidOrInvalid();
Printf("===================================================="
"=============\n");
if (reporting_thread_tid != kInvalidTid) {
// We started reporting an error message. Stop using the fake stack
// in case we call an instrumented function from a symbolizer.
- AsanThread *curr_thread = asanThreadRegistry().GetCurrent();
+ AsanThread *curr_thread = GetCurrentThread();
CHECK(curr_thread);
curr_thread->fake_stack().StopUsingFakeStack();
}
@@ -440,12 +487,13 @@ class ScopedInErrorReport {
// Destructor is NORETURN, as functions that report errors are.
NORETURN ~ScopedInErrorReport() {
// Make sure the current thread is announced.
- AsanThread *curr_thread = asanThreadRegistry().GetCurrent();
+ AsanThread *curr_thread = GetCurrentThread();
if (curr_thread) {
- DescribeThread(curr_thread->summary());
+ DescribeThread(curr_thread->context());
}
// Print memory stats.
- __asan_print_accumulated_stats();
+ if (flags()->print_stats)
+ __asan_print_accumulated_stats();
if (error_report_callback) {
error_report_callback(error_message_buffer);
}
@@ -454,6 +502,22 @@ class ScopedInErrorReport {
}
};
+static void ReportSummary(const char *error_type, StackTrace *stack) {
+ if (!stack->size) return;
+ if (IsSymbolizerAvailable()) {
+ AddressInfo ai;
+ // Currently, we include the first stack frame into the report summary.
+ // Maybe sometimes we need to choose another frame (e.g. skip memcpy/etc).
+ uptr pc = StackTrace::GetPreviousInstructionPc(stack->trace[0]);
+ SymbolizeCode(pc, &ai, 1);
+ ReportErrorSummary(error_type,
+ StripPathPrefix(ai.file,
+ common_flags()->strip_path_prefix),
+ ai.line, ai.function);
+ }
+ // FIXME: do we need to print anything at all if there is no symbolizer?
+}
+
void ReportSIGSEGV(uptr pc, uptr sp, uptr bp, uptr addr) {
ScopedInErrorReport in_report;
Decorator d;
@@ -461,32 +525,44 @@ void ReportSIGSEGV(uptr pc, uptr sp, uptr bp, uptr addr) {
Report("ERROR: AddressSanitizer: SEGV on unknown address %p"
" (pc %p sp %p bp %p T%d)\n",
(void*)addr, (void*)pc, (void*)sp, (void*)bp,
- asanThreadRegistry().GetCurrentTidOrInvalid());
+ GetCurrentTidOrInvalid());
Printf("%s", d.EndWarning());
Printf("AddressSanitizer can not provide additional info.\n");
GET_STACK_TRACE_FATAL(pc, bp);
PrintStack(&stack);
+ ReportSummary("SEGV", &stack);
}
void ReportDoubleFree(uptr addr, StackTrace *stack) {
ScopedInErrorReport in_report;
Decorator d;
Printf("%s", d.Warning());
- Report("ERROR: AddressSanitizer: attempting double-free on %p:\n", addr);
+ char tname[128];
+ u32 curr_tid = GetCurrentTidOrInvalid();
+ Report("ERROR: AddressSanitizer: attempting double-free on %p in "
+ "thread T%d%s:\n",
+ addr, curr_tid,
+ ThreadNameWithParenthesis(curr_tid, tname, sizeof(tname)));
+
Printf("%s", d.EndWarning());
PrintStack(stack);
DescribeHeapAddress(addr, 1);
+ ReportSummary("double-free", stack);
}
void ReportFreeNotMalloced(uptr addr, StackTrace *stack) {
ScopedInErrorReport in_report;
Decorator d;
Printf("%s", d.Warning());
+ char tname[128];
+ u32 curr_tid = GetCurrentTidOrInvalid();
Report("ERROR: AddressSanitizer: attempting free on address "
- "which was not malloc()-ed: %p\n", addr);
+ "which was not malloc()-ed: %p in thread T%d%s\n", addr,
+ curr_tid, ThreadNameWithParenthesis(curr_tid, tname, sizeof(tname)));
Printf("%s", d.EndWarning());
PrintStack(stack);
DescribeHeapAddress(addr, 1);
+ ReportSummary("bad-free", stack);
}
void ReportAllocTypeMismatch(uptr addr, StackTrace *stack,
@@ -505,6 +581,7 @@ void ReportAllocTypeMismatch(uptr addr, StackTrace *stack,
Printf("%s", d.EndWarning());
PrintStack(stack);
DescribeHeapAddress(addr, 1);
+ ReportSummary("alloc-dealloc-mismatch", stack);
Report("HINT: if you don't care about these warnings you may set "
"ASAN_OPTIONS=alloc_dealloc_mismatch=0\n");
}
@@ -519,6 +596,7 @@ void ReportMallocUsableSizeNotOwned(uptr addr, StackTrace *stack) {
Printf("%s", d.EndWarning());
PrintStack(stack);
DescribeHeapAddress(addr, 1);
+ ReportSummary("bad-malloc_usable_size", stack);
}
void ReportAsanGetAllocatedSizeNotOwned(uptr addr, StackTrace *stack) {
@@ -531,6 +609,7 @@ void ReportAsanGetAllocatedSizeNotOwned(uptr addr, StackTrace *stack) {
Printf("%s", d.EndWarning());
PrintStack(stack);
DescribeHeapAddress(addr, 1);
+ ReportSummary("bad-__asan_get_allocated_size", stack);
}
void ReportStringFunctionMemoryRangesOverlap(
@@ -538,14 +617,17 @@ void ReportStringFunctionMemoryRangesOverlap(
const char *offset2, uptr length2, StackTrace *stack) {
ScopedInErrorReport in_report;
Decorator d;
+ char bug_type[100];
+ internal_snprintf(bug_type, sizeof(bug_type), "%s-param-overlap", function);
Printf("%s", d.Warning());
- Report("ERROR: AddressSanitizer: %s-param-overlap: "
+ Report("ERROR: AddressSanitizer: %s: "
"memory ranges [%p,%p) and [%p, %p) overlap\n", \
- function, offset1, offset1 + length1, offset2, offset2 + length2);
+ bug_type, offset1, offset1 + length1, offset2, offset2 + length2);
Printf("%s", d.EndWarning());
PrintStack(stack);
DescribeAddress((uptr)offset1, length1);
DescribeAddress((uptr)offset2, length2);
+ ReportSummary(bug_type, stack);
}
// ----------------------- Mac-specific reports ----------------- {{{1
@@ -642,7 +724,7 @@ void __asan_report_error(uptr pc, uptr bp, uptr sp,
bug_descr, (void*)addr, pc, bp, sp);
Printf("%s", d.EndWarning());
- u32 curr_tid = asanThreadRegistry().GetCurrentTidOrInvalid();
+ u32 curr_tid = GetCurrentTidOrInvalid();
char tname[128];
Printf("%s%s of size %zu at %p thread T%d%s%s\n",
d.Access(),
@@ -655,7 +737,7 @@ void __asan_report_error(uptr pc, uptr bp, uptr sp,
PrintStack(&stack);
DescribeAddress(addr, access_size);
-
+ ReportSummary(bug_descr, &stack);
PrintShadowMemoryForAddress(addr);
}
diff --git a/lib/asan/asan_report.h b/lib/asan/asan_report.h
index f0617f91970e..db271fc10e97 100644
--- a/lib/asan/asan_report.h
+++ b/lib/asan/asan_report.h
@@ -15,21 +15,21 @@
#include "asan_allocator.h"
#include "asan_internal.h"
#include "asan_thread.h"
-#include "sanitizer/asan_interface.h"
namespace __asan {
// The following functions prints address description depending
// on the memory type (shadow/heap/stack/global).
void DescribeHeapAddress(uptr addr, uptr access_size);
-bool DescribeAddressIfGlobal(uptr addr);
-bool DescribeAddressRelativeToGlobal(uptr addr, const __asan_global &g);
+bool DescribeAddressIfGlobal(uptr addr, uptr access_size);
+bool DescribeAddressRelativeToGlobal(uptr addr, uptr access_size,
+ const __asan_global &g);
bool DescribeAddressIfShadow(uptr addr);
bool DescribeAddressIfStack(uptr addr, uptr access_size);
// Determines memory type on its own.
void DescribeAddress(uptr addr, uptr access_size);
-void DescribeThread(AsanThreadSummary *summary);
+void DescribeThread(AsanThreadContext *context);
// Different kinds of error reports.
void NORETURN ReportSIGSEGV(uptr pc, uptr sp, uptr bp, uptr addr);
diff --git a/lib/asan/asan_rtl.cc b/lib/asan/asan_rtl.cc
index 11adbee5bdea..f989c5c0d2a5 100644
--- a/lib/asan/asan_rtl.cc
+++ b/lib/asan/asan_rtl.cc
@@ -15,19 +15,21 @@
#include "asan_interceptors.h"
#include "asan_internal.h"
#include "asan_mapping.h"
+#include "asan_poisoning.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"
+#include "lsan/lsan_common.h"
namespace __asan {
+uptr AsanMappingProfile[kAsanMappingProfileSize];
+
static void AsanDie() {
static atomic_uint32_t num_calls;
if (atomic_fetch_add(&num_calls, 1, memory_order_relaxed) != 0) {
@@ -38,13 +40,19 @@ static void AsanDie() {
Report("Sleeping for %d second(s)\n", flags()->sleep_before_dying);
SleepForSeconds(flags()->sleep_before_dying);
}
- if (flags()->unmap_shadow_on_exit)
- UnmapOrDie((void*)kLowShadowBeg, kHighShadowEnd - kLowShadowBeg);
+ if (flags()->unmap_shadow_on_exit) {
+ if (kMidMemBeg) {
+ UnmapOrDie((void*)kLowShadowBeg, kMidMemBeg - kLowShadowBeg);
+ UnmapOrDie((void*)kMidMemEnd, kHighShadowEnd - kMidMemEnd);
+ } else {
+ UnmapOrDie((void*)kLowShadowBeg, kHighShadowEnd - kLowShadowBeg);
+ }
+ }
if (death_callback)
death_callback();
if (flags()->abort_on_error)
Abort();
- Exit(flags()->exitcode);
+ internal__exit(flags()->exitcode);
}
static void AsanCheckFailed(const char *file, int line, const char *cond,
@@ -57,97 +65,118 @@ static void AsanCheckFailed(const char *file, int line, const char *cond,
}
// -------------------------- Flags ------------------------- {{{1
-static const int kDeafultMallocContextSize = 30;
-
-static Flags asan_flags;
+static const int kDefaultMallocContextSize = 30;
-Flags *flags() {
- return &asan_flags;
-}
+Flags asan_flags_dont_use_directly; // use via flags().
static const char *MaybeCallAsanDefaultOptions() {
return (&__asan_default_options) ? __asan_default_options() : "";
}
+static const char *MaybeUseAsanDefaultOptionsCompileDefiniton() {
+#ifdef ASAN_DEFAULT_OPTIONS
+// Stringize the macro value.
+# define ASAN_STRINGIZE(x) #x
+# define ASAN_STRINGIZE_OPTIONS(options) ASAN_STRINGIZE(options)
+ return ASAN_STRINGIZE_OPTIONS(ASAN_DEFAULT_OPTIONS);
+#else
+ return "";
+#endif
+}
+
static void ParseFlagsFromString(Flags *f, const char *str) {
+ ParseCommonFlagsFromString(str);
+ CHECK((uptr)common_flags()->malloc_context_size <= kStackTraceMax);
+
ParseFlag(str, &f->quarantine_size, "quarantine_size");
- ParseFlag(str, &f->symbolize, "symbolize");
ParseFlag(str, &f->verbosity, "verbosity");
ParseFlag(str, &f->redzone, "redzone");
- CHECK(f->redzone >= 16);
+ CHECK_GE(f->redzone, 16);
CHECK(IsPowerOfTwo(f->redzone));
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((uptr)f->malloc_context_size <= kStackTraceMax);
+ ParseFlag(str, &f->check_initialization_order, "check_initialization_order");
ParseFlag(str, &f->replace_str, "replace_str");
ParseFlag(str, &f->replace_intrin, "replace_intrin");
- ParseFlag(str, &f->replace_cfallocator, "replace_cfallocator");
ParseFlag(str, &f->mac_ignore_invalid_free, "mac_ignore_invalid_free");
ParseFlag(str, &f->use_fake_stack, "use_fake_stack");
ParseFlag(str, &f->max_malloc_fill_size, "max_malloc_fill_size");
+ ParseFlag(str, &f->malloc_fill_byte, "malloc_fill_byte");
ParseFlag(str, &f->exitcode, "exitcode");
ParseFlag(str, &f->allow_user_poisoning, "allow_user_poisoning");
ParseFlag(str, &f->sleep_before_dying, "sleep_before_dying");
ParseFlag(str, &f->handle_segv, "handle_segv");
+ ParseFlag(str, &f->allow_user_segv_handler, "allow_user_segv_handler");
ParseFlag(str, &f->use_sigaltstack, "use_sigaltstack");
ParseFlag(str, &f->check_malloc_usable_size, "check_malloc_usable_size");
ParseFlag(str, &f->unmap_shadow_on_exit, "unmap_shadow_on_exit");
ParseFlag(str, &f->abort_on_error, "abort_on_error");
+ ParseFlag(str, &f->print_stats, "print_stats");
+ ParseFlag(str, &f->print_legend, "print_legend");
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");
+ ParseFlag(str, &f->strict_memcmp, "strict_memcmp");
+ ParseFlag(str, &f->strict_init_order, "strict_init_order");
+ ParseFlag(str, &f->detect_leaks, "detect_leaks");
}
void InitializeFlags(Flags *f, const char *env) {
- internal_memset(f, 0, sizeof(*f));
+ CommonFlags *cf = common_flags();
+ cf->external_symbolizer_path = GetEnv("ASAN_SYMBOLIZER_PATH");
+ cf->symbolize = true;
+ cf->malloc_context_size = kDefaultMallocContextSize;
+ cf->fast_unwind_on_fatal = false;
+ cf->fast_unwind_on_malloc = true;
+ cf->strip_path_prefix = "";
+ internal_memset(f, 0, sizeof(*f));
f->quarantine_size = (ASAN_LOW_MEMORY) ? 1UL << 26 : 1UL << 28;
- f->symbolize = false;
f->verbosity = 0;
- f->redzone = ASAN_ALLOCATOR_VERSION == 2 ? 16 : (ASAN_LOW_MEMORY) ? 64 : 128;
+ f->redzone = 16;
f->debug = false;
f->report_globals = 1;
- f->check_initialization_order = true;
- f->malloc_context_size = kDeafultMallocContextSize;
+ f->check_initialization_order = false;
f->replace_str = true;
f->replace_intrin = true;
- f->replace_cfallocator = true;
f->mac_ignore_invalid_free = false;
f->use_fake_stack = true;
- f->max_malloc_fill_size = 0;
+ f->max_malloc_fill_size = 0x1000; // By default, fill only the first 4K.
+ f->malloc_fill_byte = 0xbe;
f->exitcode = ASAN_DEFAULT_FAILURE_EXITCODE;
f->allow_user_poisoning = true;
f->sleep_before_dying = 0;
f->handle_segv = ASAN_NEEDS_SEGV;
+ f->allow_user_segv_handler = false;
f->use_sigaltstack = false;
f->check_malloc_usable_size = true;
f->unmap_shadow_on_exit = false;
f->abort_on_error = false;
+ f->print_stats = false;
+ f->print_legend = true;
f->atexit = false;
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.
+ f->alloc_dealloc_mismatch = (SANITIZER_MAC == 0);;
+ f->use_stack_depot = true;
+ f->strict_memcmp = true;
+ f->strict_init_order = false;
+ f->detect_leaks = false;
+
+ // Override from compile definition.
+ ParseFlagsFromString(f, MaybeUseAsanDefaultOptionsCompileDefiniton());
// Override from user-specified string.
ParseFlagsFromString(f, MaybeCallAsanDefaultOptions());
@@ -158,6 +187,20 @@ void InitializeFlags(Flags *f, const char *env) {
// Override from command line.
ParseFlagsFromString(f, env);
+
+#if !CAN_SANITIZE_LEAKS
+ if (f->detect_leaks) {
+ Report("%s: detect_leaks is not supported on this platform.\n",
+ SanitizerToolName);
+ f->detect_leaks = false;
+ }
+#endif
+
+ if (f->detect_leaks && !f->use_stack_depot) {
+ Report("%s: detect_leaks is ignored (requires use_stack_depot).\n",
+ SanitizerToolName);
+ f->detect_leaks = false;
+ }
}
// -------------------------- Globals --------------------- {{{1
@@ -165,6 +208,10 @@ int asan_inited;
bool asan_init_is_running;
void (*death_callback)(void);
+#if !ASAN_FIXED_MAPPING
+uptr kHighMemEnd, kMidMemBeg, kMidMemEnd;
+#endif
+
// -------------------------- Misc ---------------- {{{1
void ShowStatsAndAbort() {
__asan_print_accumulated_stats();
@@ -174,8 +221,8 @@ void ShowStatsAndAbort() {
// ---------------------- mmap -------------------- {{{1
// Reserve memory range [beg, end].
static void ReserveShadowMemoryRange(uptr beg, uptr end) {
- CHECK((beg % GetPageSizeCached()) == 0);
- CHECK(((end + 1) % GetPageSizeCached()) == 0);
+ CHECK_EQ((beg % GetPageSizeCached()), 0);
+ CHECK_EQ(((end + 1) % GetPageSizeCached()), 0);
uptr size = end - beg + 1;
void *res = MmapFixedNoReserve(beg, size);
if (res != (void*)beg) {
@@ -211,6 +258,17 @@ ASAN_REPORT_ERROR(store, true, 4)
ASAN_REPORT_ERROR(store, true, 8)
ASAN_REPORT_ERROR(store, true, 16)
+#define ASAN_REPORT_ERROR_N(type, is_write) \
+extern "C" NOINLINE INTERFACE_ATTRIBUTE \
+void __asan_report_ ## type ## _n(uptr addr, uptr size); \
+void __asan_report_ ## type ## _n(uptr addr, uptr size) { \
+ GET_CALLER_PC_BP_SP; \
+ __asan_report_error(pc, bp, sp, addr, is_write, size); \
+}
+
+ASAN_REPORT_ERROR_N(load, false)
+ASAN_REPORT_ERROR_N(store, true)
+
// Force the linker to keep the symbols for various ASan interface functions.
// We want to keep those in the executable in order to let the instrumented
// dynamic libraries access the symbol even if it is not used by the executable
@@ -249,7 +307,7 @@ static NOINLINE void force_interface_symbols() {
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 30: __asan_before_dynamic_init(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;
@@ -261,6 +319,83 @@ static NOINLINE void force_interface_symbols() {
static void asan_atexit() {
Printf("AddressSanitizer exit stats:\n");
__asan_print_accumulated_stats();
+ // Print AsanMappingProfile.
+ for (uptr i = 0; i < kAsanMappingProfileSize; i++) {
+ if (AsanMappingProfile[i] == 0) continue;
+ Printf("asan_mapping.h:%zd -- %zd\n", i, AsanMappingProfile[i]);
+ }
+}
+
+static void InitializeHighMemEnd() {
+#if !ASAN_FIXED_MAPPING
+#if SANITIZER_WORDSIZE == 64
+# if defined(__powerpc64__)
+ // FIXME:
+ // On PowerPC64 we have two different address space layouts: 44- and 46-bit.
+ // We somehow need to figure our which one we are using now and choose
+ // one of 0x00000fffffffffffUL and 0x00003fffffffffffUL.
+ // Note that with 'ulimit -s unlimited' the stack is moved away from the top
+ // of the address space, so simply checking the stack address is not enough.
+ kHighMemEnd = (1ULL << 44) - 1; // 0x00000fffffffffffUL
+# else
+ kHighMemEnd = (1ULL << 47) - 1; // 0x00007fffffffffffUL;
+# endif
+#else // SANITIZER_WORDSIZE == 32
+ kHighMemEnd = (1ULL << 32) - 1; // 0xffffffff;
+#endif // SANITIZER_WORDSIZE
+#endif // !ASAN_FIXED_MAPPING
+}
+
+static void ProtectGap(uptr a, uptr size) {
+ CHECK_EQ(a, (uptr)Mprotect(a, size));
+}
+
+static void PrintAddressSpaceLayout() {
+ Printf("|| `[%p, %p]` || HighMem ||\n",
+ (void*)kHighMemBeg, (void*)kHighMemEnd);
+ Printf("|| `[%p, %p]` || HighShadow ||\n",
+ (void*)kHighShadowBeg, (void*)kHighShadowEnd);
+ if (kMidMemBeg) {
+ Printf("|| `[%p, %p]` || ShadowGap3 ||\n",
+ (void*)kShadowGap3Beg, (void*)kShadowGap3End);
+ Printf("|| `[%p, %p]` || MidMem ||\n",
+ (void*)kMidMemBeg, (void*)kMidMemEnd);
+ Printf("|| `[%p, %p]` || ShadowGap2 ||\n",
+ (void*)kShadowGap2Beg, (void*)kShadowGap2End);
+ Printf("|| `[%p, %p]` || MidShadow ||\n",
+ (void*)kMidShadowBeg, (void*)kMidShadowEnd);
+ }
+ Printf("|| `[%p, %p]` || ShadowGap ||\n",
+ (void*)kShadowGapBeg, (void*)kShadowGapEnd);
+ if (kLowShadowBeg) {
+ Printf("|| `[%p, %p]` || LowShadow ||\n",
+ (void*)kLowShadowBeg, (void*)kLowShadowEnd);
+ Printf("|| `[%p, %p]` || LowMem ||\n",
+ (void*)kLowMemBeg, (void*)kLowMemEnd);
+ }
+ Printf("MemToShadow(shadow): %p %p %p %p",
+ (void*)MEM_TO_SHADOW(kLowShadowBeg),
+ (void*)MEM_TO_SHADOW(kLowShadowEnd),
+ (void*)MEM_TO_SHADOW(kHighShadowBeg),
+ (void*)MEM_TO_SHADOW(kHighShadowEnd));
+ if (kMidMemBeg) {
+ Printf(" %p %p",
+ (void*)MEM_TO_SHADOW(kMidShadowBeg),
+ (void*)MEM_TO_SHADOW(kMidShadowEnd));
+ }
+ Printf("\n");
+ Printf("red_zone=%zu\n", (uptr)flags()->redzone);
+ Printf("malloc_context_size=%zu\n",
+ (uptr)common_flags()->malloc_context_size);
+
+ Printf("SHADOW_SCALE: %zx\n", (uptr)SHADOW_SCALE);
+ Printf("SHADOW_GRANULARITY: %zx\n", (uptr)SHADOW_GRANULARITY);
+ Printf("SHADOW_OFFSET: %zx\n", (uptr)SHADOW_OFFSET);
+ CHECK(SHADOW_SCALE >= 3 && SHADOW_SCALE <= 7);
+ if (kMidMemBeg)
+ CHECK(kMidShadowBeg > kLowShadowEnd &&
+ kMidMemBeg > kMidShadowEnd &&
+ kHighShadowBeg > kMidMemEnd);
}
} // namespace __asan
@@ -283,11 +418,25 @@ int NOINLINE __asan_set_error_exit_code(int exit_code) {
void NOINLINE __asan_handle_no_return() {
int local_stack;
- AsanThread *curr_thread = asanThreadRegistry().GetCurrent();
+ 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);
}
@@ -297,8 +446,10 @@ void NOINLINE __asan_set_death_callback(void (*callback)(void)) {
void __asan_init() {
if (asan_inited) return;
+ SanitizerToolName = "AddressSanitizer";
CHECK(!asan_init_is_running && "ASan init calls itself!");
asan_init_is_running = true;
+ InitializeHighMemEnd();
// Make sure we are not statically linked.
AsanDoesNotSupportStaticLinkage();
@@ -334,49 +485,48 @@ void __asan_init() {
ReplaceSystemMalloc();
ReplaceOperatorsNewAndDelete();
- if (flags()->verbosity) {
- Printf("|| `[%p, %p]` || HighMem ||\n",
- (void*)kHighMemBeg, (void*)kHighMemEnd);
- Printf("|| `[%p, %p]` || HighShadow ||\n",
- (void*)kHighShadowBeg, (void*)kHighShadowEnd);
- Printf("|| `[%p, %p]` || ShadowGap ||\n",
- (void*)kShadowGapBeg, (void*)kShadowGapEnd);
- Printf("|| `[%p, %p]` || LowShadow ||\n",
- (void*)kLowShadowBeg, (void*)kLowShadowEnd);
- Printf("|| `[%p, %p]` || LowMem ||\n",
- (void*)kLowMemBeg, (void*)kLowMemEnd);
- Printf("MemToShadow(shadow): %p %p %p %p\n",
- (void*)MEM_TO_SHADOW(kLowShadowBeg),
- (void*)MEM_TO_SHADOW(kLowShadowEnd),
- (void*)MEM_TO_SHADOW(kHighShadowBeg),
- (void*)MEM_TO_SHADOW(kHighShadowEnd));
- Printf("red_zone=%zu\n", (uptr)flags()->redzone);
- Printf("malloc_context_size=%zu\n", (uptr)flags()->malloc_context_size);
-
- Printf("SHADOW_SCALE: %zx\n", (uptr)SHADOW_SCALE);
- Printf("SHADOW_GRANULARITY: %zx\n", (uptr)SHADOW_GRANULARITY);
- Printf("SHADOW_OFFSET: %zx\n", (uptr)SHADOW_OFFSET);
- CHECK(SHADOW_SCALE >= 3 && SHADOW_SCALE <= 7);
+ uptr shadow_start = kLowShadowBeg;
+ if (kLowShadowBeg) shadow_start -= GetMmapGranularity();
+ uptr shadow_end = kHighShadowEnd;
+ bool full_shadow_is_available =
+ MemoryRangeIsAvailable(shadow_start, shadow_end);
+
+#if SANITIZER_LINUX && defined(__x86_64__) && !ASAN_FIXED_MAPPING
+ if (!full_shadow_is_available) {
+ kMidMemBeg = kLowMemEnd < 0x3000000000ULL ? 0x3000000000ULL : 0;
+ kMidMemEnd = kLowMemEnd < 0x3000000000ULL ? 0x4fffffffffULL : 0;
}
+#endif
+
+ if (flags()->verbosity)
+ PrintAddressSpaceLayout();
if (flags()->disable_core) {
DisableCoreDumper();
}
- uptr shadow_start = kLowShadowBeg;
- 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 - GetMmapGranularity(),
- kLowShadowEnd);
- }
+ if (full_shadow_is_available) {
+ // mmap the low shadow plus at least one page at the left.
+ if (kLowShadowBeg)
+ ReserveShadowMemoryRange(shadow_start, kLowShadowEnd);
+ // mmap the high shadow.
+ ReserveShadowMemoryRange(kHighShadowBeg, kHighShadowEnd);
+ // protect the gap.
+ ProtectGap(kShadowGapBeg, kShadowGapEnd - kShadowGapBeg + 1);
+ } else if (kMidMemBeg &&
+ MemoryRangeIsAvailable(shadow_start, kMidMemBeg - 1) &&
+ MemoryRangeIsAvailable(kMidMemEnd + 1, shadow_end)) {
+ CHECK(kLowShadowBeg != kLowShadowEnd);
+ // mmap the low shadow plus at least one page at the left.
+ ReserveShadowMemoryRange(shadow_start, kLowShadowEnd);
+ // mmap the mid shadow.
+ ReserveShadowMemoryRange(kMidShadowBeg, kMidShadowEnd);
// mmap the high shadow.
ReserveShadowMemoryRange(kHighShadowBeg, kHighShadowEnd);
- // protect the gap
- void *prot = Mprotect(kShadowGapBeg, kShadowGapEnd - kShadowGapBeg + 1);
- CHECK(prot == (void*)kShadowGapBeg);
+ // protect the gaps.
+ ProtectGap(kShadowGapBeg, kShadowGapEnd - kShadowGapBeg + 1);
+ ProtectGap(kShadowGap2Beg, kShadowGap2End - kShadowGap2Beg + 1);
+ ProtectGap(kShadowGap3Beg, kShadowGap3End - kShadowGap3Beg + 1);
} else {
Report("Shadow memory range interleaves with an existing memory mapping. "
"ASan cannot proceed correctly. ABORTING.\n");
@@ -386,11 +536,10 @@ 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);
- }
+ const char* external_symbolizer = common_flags()->external_symbolizer_path;
+ if (common_flags()->symbolize && external_symbolizer &&
+ external_symbolizer[0]) {
+ InitializeExternalSymbolizer(external_symbolizer);
}
// On Linux AsanThread::ThreadStart() calls malloc() that's why asan_inited
@@ -398,25 +547,27 @@ void __asan_init() {
asan_inited = 1;
asan_init_is_running = false;
- asanThreadRegistry().Init();
- asanThreadRegistry().GetMain()->ThreadStart();
+ // Create main thread.
+ AsanTSDInit(AsanThread::TSDDtor);
+ AsanThread *main_thread = AsanThread::Create(0, 0);
+ CreateThreadContextArgs create_main_args = { main_thread, 0 };
+ u32 main_tid = asanThreadRegistry().CreateThread(
+ 0, true, 0, &create_main_args);
+ CHECK_EQ(0, main_tid);
+ SetCurrentThread(main_thread);
+ main_thread->ThreadStart(internal_getpid());
force_interface_symbols(); // no-op.
+ InitializeAllocator();
+
+#if CAN_SANITIZE_LEAKS
+ __lsan::InitCommonLsan();
+ if (flags()->detect_leaks) {
+ Atexit(__lsan::DoLeakCheck);
+ }
+#endif // CAN_SANITIZE_LEAKS
+
if (flags()->verbosity) {
Report("AddressSanitizer Init done\n");
}
}
-
-#if defined(ASAN_USE_PREINIT_ARRAY)
- // On Linux, we force __asan_init to be called before anyone else
- // by placing it into .preinit_array section.
- // FIXME: do we have anything like this on Mac?
- __attribute__((section(".preinit_array")))
- typeof(__asan_init) *__asan_preinit =__asan_init;
-#elif defined(_WIN32) && defined(_DLL)
- // On Windows, when using dynamic CRT (/MD), we can put a pointer
- // to __asan_init into the global list of C initializers.
- // See crt0dat.c in the CRT sources for the details.
- #pragma section(".CRT$XIB", long, read) // NOLINT
- __declspec(allocate(".CRT$XIB")) void (*__asan_preinit)() = __asan_init;
-#endif
diff --git a/lib/asan/asan_stack.cc b/lib/asan/asan_stack.cc
index ebf22fd34ca1..21dae7df096a 100644
--- a/lib/asan/asan_stack.cc
+++ b/lib/asan/asan_stack.cc
@@ -11,9 +11,10 @@
//
// Code for ASan stack trace.
//===----------------------------------------------------------------------===//
+#include "asan_internal.h"
#include "asan_flags.h"
#include "asan_stack.h"
-#include "sanitizer/asan_interface.h"
+#include "sanitizer_common/sanitizer_flags.h"
namespace __asan {
@@ -24,8 +25,8 @@ static bool MaybeCallAsanSymbolize(const void *pc, char *out_buffer,
}
void PrintStack(StackTrace *stack) {
- stack->PrintStack(stack->trace, stack->size, flags()->symbolize,
- flags()->strip_path_prefix, MaybeCallAsanSymbolize);
+ stack->PrintStack(stack->trace, stack->size, common_flags()->symbolize,
+ common_flags()->strip_path_prefix, MaybeCallAsanSymbolize);
}
} // namespace __asan
@@ -35,7 +36,7 @@ void PrintStack(StackTrace *stack) {
// Provide default implementation of __asan_symbolize that does nothing
// and may be overriden by user if he wants to use his own symbolization.
// ASan on Windows has its own implementation of this.
-#if !defined(_WIN32) && !SANITIZER_SUPPORTS_WEAK_HOOKS
+#if !SANITIZER_WINDOWS && !SANITIZER_SUPPORTS_WEAK_HOOKS
SANITIZER_WEAK_ATTRIBUTE SANITIZER_INTERFACE_ATTRIBUTE NOINLINE
bool __asan_symbolize(const void *pc, char *out_buffer, int out_size) {
return false;
diff --git a/lib/asan/asan_stack.h b/lib/asan/asan_stack.h
index 46c9f3408725..176aa183c93b 100644
--- a/lib/asan/asan_stack.h
+++ b/lib/asan/asan_stack.h
@@ -14,12 +14,13 @@
#ifndef ASAN_STACK_H
#define ASAN_STACK_H
-#include "sanitizer_common/sanitizer_stacktrace.h"
#include "asan_flags.h"
+#include "asan_thread.h"
+#include "sanitizer_common/sanitizer_flags.h"
+#include "sanitizer_common/sanitizer_stacktrace.h"
namespace __asan {
-void GetStackTrace(StackTrace *stack, uptr max_s, uptr pc, uptr bp, bool fast);
void PrintStack(StackTrace *stack);
} // namespace __asan
@@ -27,10 +28,24 @@ void PrintStack(StackTrace *stack);
// Get the stack trace with the given pc and bp.
// The pc will be in the position 0 of the resulting stack trace.
// The bp may refer to the current frame or to the caller's frame.
-// fast_unwind is currently unused.
+#if SANITIZER_WINDOWS
#define GET_STACK_TRACE_WITH_PC_AND_BP(max_s, pc, bp, fast) \
StackTrace stack; \
- GetStackTrace(&stack, max_s, pc, bp, fast)
+ GetStackTrace(&stack, max_s, pc, bp, 0, 0, fast)
+#else
+#define GET_STACK_TRACE_WITH_PC_AND_BP(max_s, pc, bp, fast) \
+ StackTrace stack; \
+ { \
+ uptr stack_top = 0, stack_bottom = 0; \
+ AsanThread *t; \
+ if (asan_inited && (t = GetCurrentThread())) { \
+ stack_top = t->stack_top(); \
+ stack_bottom = t->stack_bottom(); \
+ } \
+ GetStackTrace(&stack, max_s, pc, bp, \
+ stack_top, stack_bottom, fast); \
+ }
+#endif // SANITIZER_WINDOWS
// NOTE: A Rule of thumb is to retrieve stack trace in the interceptors
// as early as possible (in functions exposed to the user), as we generally
@@ -42,24 +57,24 @@ void PrintStack(StackTrace *stack);
#define GET_STACK_TRACE_FATAL(pc, bp) \
GET_STACK_TRACE_WITH_PC_AND_BP(kStackTraceMax, pc, bp, \
- flags()->fast_unwind_on_fatal)
+ common_flags()->fast_unwind_on_fatal)
-#define GET_STACK_TRACE_FATAL_HERE \
- GET_STACK_TRACE(kStackTraceMax, flags()->fast_unwind_on_fatal)
+#define GET_STACK_TRACE_FATAL_HERE \
+ GET_STACK_TRACE(kStackTraceMax, common_flags()->fast_unwind_on_fatal)
-#define GET_STACK_TRACE_THREAD \
+#define GET_STACK_TRACE_THREAD \
GET_STACK_TRACE(kStackTraceMax, true)
-#define GET_STACK_TRACE_MALLOC \
- GET_STACK_TRACE(flags()->malloc_context_size, \
- flags()->fast_unwind_on_malloc)
+#define GET_STACK_TRACE_MALLOC \
+ GET_STACK_TRACE(common_flags()->malloc_context_size, \
+ common_flags()->fast_unwind_on_malloc)
#define GET_STACK_TRACE_FREE GET_STACK_TRACE_MALLOC
#define PRINT_CURRENT_STACK() \
{ \
GET_STACK_TRACE(kStackTraceMax, \
- flags()->fast_unwind_on_fatal); \
+ common_flags()->fast_unwind_on_fatal); \
PrintStack(&stack); \
}
diff --git a/lib/asan/asan_stats.cc b/lib/asan/asan_stats.cc
index c57c8cc61aed..ba7c1ab6e91a 100644
--- a/lib/asan/asan_stats.cc
+++ b/lib/asan/asan_stats.cc
@@ -14,14 +14,14 @@
#include "asan_interceptors.h"
#include "asan_internal.h"
#include "asan_stats.h"
-#include "asan_thread_registry.h"
-#include "sanitizer/asan_interface.h"
+#include "asan_thread.h"
+#include "sanitizer_common/sanitizer_mutex.h"
#include "sanitizer_common/sanitizer_stackdepot.h"
namespace __asan {
AsanStats::AsanStats() {
- CHECK(REAL(memset) != 0);
+ CHECK(REAL(memset));
REAL(memset)(this, 0, sizeof(AsanStats));
}
@@ -58,7 +58,7 @@ static BlockingMutex print_lock(LINKER_INITIALIZED);
static void PrintAccumulatedStats() {
AsanStats stats;
- asanThreadRegistry().GetAccumulatedStats(&stats);
+ GetAccumulatedStats(&stats);
// Use lock to keep reports from mixing up.
BlockingMutexLock lock(&print_lock);
stats.Print();
@@ -68,21 +68,103 @@ static void PrintAccumulatedStats() {
PrintInternalAllocatorStats();
}
+static AsanStats unknown_thread_stats(LINKER_INITIALIZED);
+static AsanStats accumulated_stats(LINKER_INITIALIZED);
+// Required for malloc_zone_statistics() on OS X. This can't be stored in
+// per-thread AsanStats.
+static uptr max_malloced_memory;
+static BlockingMutex acc_stats_lock(LINKER_INITIALIZED);
+
+static void FlushToAccumulatedStatsUnlocked(AsanStats *stats) {
+ acc_stats_lock.CheckLocked();
+ uptr *dst = (uptr*)&accumulated_stats;
+ uptr *src = (uptr*)stats;
+ uptr num_fields = sizeof(*stats) / sizeof(uptr);
+ for (uptr i = 0; i < num_fields; i++) {
+ dst[i] += src[i];
+ src[i] = 0;
+ }
+}
+
+static void FlushThreadStats(ThreadContextBase *tctx_base, void *arg) {
+ AsanThreadContext *tctx = static_cast<AsanThreadContext*>(tctx_base);
+ if (AsanThread *t = tctx->thread)
+ FlushToAccumulatedStatsUnlocked(&t->stats());
+}
+
+static void UpdateAccumulatedStatsUnlocked() {
+ acc_stats_lock.CheckLocked();
+ {
+ ThreadRegistryLock l(&asanThreadRegistry());
+ asanThreadRegistry().RunCallbackForEachThreadLocked(FlushThreadStats, 0);
+ }
+ FlushToAccumulatedStatsUnlocked(&unknown_thread_stats);
+ // This is not very accurate: we may miss allocation peaks that happen
+ // between two updates of accumulated_stats_. For more accurate bookkeeping
+ // the maximum should be updated on every malloc(), which is unacceptable.
+ if (max_malloced_memory < accumulated_stats.malloced) {
+ max_malloced_memory = accumulated_stats.malloced;
+ }
+}
+
+void FlushToAccumulatedStats(AsanStats *stats) {
+ BlockingMutexLock lock(&acc_stats_lock);
+ FlushToAccumulatedStatsUnlocked(stats);
+}
+
+void GetAccumulatedStats(AsanStats *stats) {
+ BlockingMutexLock lock(&acc_stats_lock);
+ UpdateAccumulatedStatsUnlocked();
+ internal_memcpy(stats, &accumulated_stats, sizeof(accumulated_stats));
+}
+
+void FillMallocStatistics(AsanMallocStats *malloc_stats) {
+ BlockingMutexLock lock(&acc_stats_lock);
+ UpdateAccumulatedStatsUnlocked();
+ malloc_stats->blocks_in_use = accumulated_stats.mallocs;
+ malloc_stats->size_in_use = accumulated_stats.malloced;
+ malloc_stats->max_size_in_use = max_malloced_memory;
+ malloc_stats->size_allocated = accumulated_stats.mmaped;
+}
+
+AsanStats &GetCurrentThreadStats() {
+ AsanThread *t = GetCurrentThread();
+ return (t) ? t->stats() : unknown_thread_stats;
+}
+
} // namespace __asan
// ---------------------- Interface ---------------- {{{1
using namespace __asan; // NOLINT
uptr __asan_get_current_allocated_bytes() {
- return asanThreadRegistry().GetCurrentAllocatedBytes();
+ BlockingMutexLock lock(&acc_stats_lock);
+ UpdateAccumulatedStatsUnlocked();
+ uptr malloced = accumulated_stats.malloced;
+ uptr freed = accumulated_stats.freed;
+ // Return sane value if malloced < freed due to racy
+ // way we update accumulated stats.
+ return (malloced > freed) ? malloced - freed : 1;
}
uptr __asan_get_heap_size() {
- return asanThreadRegistry().GetHeapSize();
+ BlockingMutexLock lock(&acc_stats_lock);
+ UpdateAccumulatedStatsUnlocked();
+ return accumulated_stats.mmaped - accumulated_stats.munmaped;
}
uptr __asan_get_free_bytes() {
- return asanThreadRegistry().GetFreeBytes();
+ BlockingMutexLock lock(&acc_stats_lock);
+ UpdateAccumulatedStatsUnlocked();
+ uptr total_free = accumulated_stats.mmaped
+ - accumulated_stats.munmaped
+ + accumulated_stats.really_freed
+ + accumulated_stats.really_freed_redzones;
+ uptr total_used = accumulated_stats.malloced
+ + accumulated_stats.malloced_redzones;
+ // Return sane value if total_free < total_used due to racy
+ // way we update accumulated stats.
+ return (total_free > total_used) ? total_free - total_used : 1;
}
uptr __asan_get_unmapped_bytes() {
diff --git a/lib/asan/asan_stats.h b/lib/asan/asan_stats.h
index 37846bc92ad2..68495fb33f95 100644
--- a/lib/asan/asan_stats.h
+++ b/lib/asan/asan_stats.h
@@ -56,6 +56,15 @@ struct AsanStats {
void Print();
};
+// Returns stats for GetCurrentThread(), or stats for fake "unknown thread"
+// if GetCurrentThread() returns 0.
+AsanStats &GetCurrentThreadStats();
+// Flushes all thread-local stats to accumulated stats, and makes
+// a copy of accumulated stats.
+void GetAccumulatedStats(AsanStats *stats);
+// Flushes a given stats into accumulated stats.
+void FlushToAccumulatedStats(AsanStats *stats);
+
// A cross-platform equivalent of malloc_statistics_t on Mac OS.
struct AsanMallocStats {
uptr blocks_in_use;
@@ -64,6 +73,8 @@ struct AsanMallocStats {
uptr size_allocated;
};
+void FillMallocStatistics(AsanMallocStats *malloc_stats);
+
} // namespace __asan
#endif // ASAN_STATS_H
diff --git a/lib/asan/asan_thread.cc b/lib/asan/asan_thread.cc
index 778e91932ed5..da28381031a5 100644
--- a/lib/asan/asan_thread.cc
+++ b/lib/asan/asan_thread.cc
@@ -13,46 +13,81 @@
//===----------------------------------------------------------------------===//
#include "asan_allocator.h"
#include "asan_interceptors.h"
+#include "asan_poisoning.h"
#include "asan_stack.h"
#include "asan_thread.h"
-#include "asan_thread_registry.h"
#include "asan_mapping.h"
#include "sanitizer_common/sanitizer_common.h"
+#include "sanitizer_common/sanitizer_placement_new.h"
+#include "lsan/lsan_common.h"
namespace __asan {
-AsanThread::AsanThread(LinkerInitialized x)
- : fake_stack_(x),
- malloc_storage_(x),
- stats_(x) { }
+// AsanThreadContext implementation.
-AsanThread *AsanThread::Create(u32 parent_tid, thread_callback_t start_routine,
- void *arg, StackTrace *stack) {
+void AsanThreadContext::OnCreated(void *arg) {
+ CreateThreadContextArgs *args = static_cast<CreateThreadContextArgs*>(arg);
+ if (args->stack) {
+ internal_memcpy(&stack, args->stack, sizeof(stack));
+ }
+ thread = args->thread;
+ thread->set_context(this);
+}
+
+void AsanThreadContext::OnFinished() {
+ // Drop the link to the AsanThread object.
+ thread = 0;
+}
+
+static char thread_registry_placeholder[sizeof(ThreadRegistry)];
+static ThreadRegistry *asan_thread_registry;
+
+static ThreadContextBase *GetAsanThreadContext(u32 tid) {
+ void *mem = MmapOrDie(sizeof(AsanThreadContext), "AsanThreadContext");
+ return new(mem) AsanThreadContext(tid);
+}
+
+ThreadRegistry &asanThreadRegistry() {
+ static bool initialized;
+ // Don't worry about thread_safety - this should be called when there is
+ // a single thread.
+ if (!initialized) {
+ // Never reuse ASan threads: we store pointer to AsanThreadContext
+ // in TSD and can't reliably tell when no more TSD destructors will
+ // be called. It would be wrong to reuse AsanThreadContext for another
+ // thread before all TSD destructors will be called for it.
+ asan_thread_registry = new(thread_registry_placeholder) ThreadRegistry(
+ GetAsanThreadContext, kMaxNumberOfThreads, kMaxNumberOfThreads);
+ initialized = true;
+ }
+ return *asan_thread_registry;
+}
+
+AsanThreadContext *GetThreadContextByTidLocked(u32 tid) {
+ return static_cast<AsanThreadContext *>(
+ asanThreadRegistry().GetThreadLocked(tid));
+}
+
+// AsanThread implementation.
+
+AsanThread *AsanThread::Create(thread_callback_t start_routine,
+ void *arg) {
uptr PageSize = GetPageSizeCached();
uptr size = RoundUpTo(sizeof(AsanThread), PageSize);
AsanThread *thread = (AsanThread*)MmapOrDie(size, __FUNCTION__);
thread->start_routine_ = start_routine;
thread->arg_ = arg;
-
- const uptr kSummaryAllocSize = PageSize;
- CHECK_LE(sizeof(AsanThreadSummary), kSummaryAllocSize);
- AsanThreadSummary *summary =
- (AsanThreadSummary*)MmapOrDie(PageSize, "AsanThreadSummary");
- summary->Init(parent_tid, stack);
- summary->set_thread(thread);
- thread->set_summary(summary);
+ thread->context_ = 0;
return thread;
}
-void AsanThreadSummary::TSDDtor(void *tsd) {
- AsanThreadSummary *summary = (AsanThreadSummary*)tsd;
- if (flags()->verbosity >= 1) {
- Report("T%d TSDDtor\n", summary->tid());
- }
- if (summary->thread()) {
- summary->thread()->Destroy();
- }
+void AsanThread::TSDDtor(void *tsd) {
+ AsanThreadContext *context = (AsanThreadContext*)tsd;
+ if (flags()->verbosity >= 1)
+ Report("T%d TSDDtor\n", context->tid);
+ if (context->thread)
+ context->thread->Destroy();
}
void AsanThread::Destroy() {
@@ -60,8 +95,8 @@ void AsanThread::Destroy() {
Report("T%d exited\n", tid());
}
- asanThreadRegistry().UnregisterThread(this);
- CHECK(summary()->thread() == 0);
+ asanThreadRegistry().FinishThread(tid());
+ FlushToAccumulatedStats(&stats_);
// We also clear the shadow on thread destruction because
// some code may still be executing in later TSD destructors
// and we don't want it to have any poisoned stack.
@@ -86,15 +121,16 @@ void AsanThread::Init() {
AsanPlatformThreadInit();
}
-thread_return_t AsanThread::ThreadStart() {
+thread_return_t AsanThread::ThreadStart(uptr os_id) {
Init();
+ asanThreadRegistry().StartThread(tid(), os_id, 0);
if (flags()->use_sigaltstack) SetAlternateSignalStack();
if (!start_routine_) {
// start_routine_ == 0 if we're on the main thread or on one of the
// OS X libdispatch worker threads. But nobody is supposed to call
// ThreadStart() for the worker threads.
- CHECK(tid() == 0);
+ CHECK_EQ(tid(), 0);
return 0;
}
@@ -117,7 +153,8 @@ void AsanThread::ClearShadowForThreadStack() {
PoisonShadow(stack_bottom_, stack_top_ - stack_bottom_, 0);
}
-const char *AsanThread::GetFrameNameByAddr(uptr addr, uptr *offset) {
+const char *AsanThread::GetFrameNameByAddr(uptr addr, uptr *offset,
+ uptr *frame_pc) {
uptr bottom = 0;
if (AddrIsInStack(addr)) {
bottom = stack_bottom();
@@ -125,6 +162,7 @@ const char *AsanThread::GetFrameNameByAddr(uptr addr, uptr *offset) {
bottom = fake_stack().AddrIsInFakeStack(addr);
CHECK(bottom);
*offset = addr - bottom;
+ *frame_pc = ((uptr*)bottom)[2];
return (const char *)((uptr*)bottom)[1];
}
uptr aligned_addr = addr & ~(SANITIZER_WORDSIZE/8 - 1); // align addr.
@@ -149,7 +187,79 @@ const char *AsanThread::GetFrameNameByAddr(uptr addr, uptr *offset) {
uptr* ptr = (uptr*)SHADOW_TO_MEM((uptr)(shadow_ptr + 1));
CHECK(ptr[0] == kCurrentStackFrameMagic);
*offset = addr - (uptr)ptr;
+ *frame_pc = ptr[2];
return (const char*)ptr[1];
}
+static bool ThreadStackContainsAddress(ThreadContextBase *tctx_base,
+ void *addr) {
+ AsanThreadContext *tctx = static_cast<AsanThreadContext*>(tctx_base);
+ AsanThread *t = tctx->thread;
+ return (t && t->fake_stack().StackSize() &&
+ (t->fake_stack().AddrIsInFakeStack((uptr)addr) ||
+ t->AddrIsInStack((uptr)addr)));
+}
+
+AsanThread *GetCurrentThread() {
+ AsanThreadContext *context = (AsanThreadContext*)AsanTSDGet();
+ if (!context) {
+ if (SANITIZER_ANDROID) {
+ // On Android, libc constructor is called _after_ asan_init, and cleans up
+ // TSD. Try to figure out if this is still the main thread by the stack
+ // address. We are not entirely sure that we have correct main thread
+ // limits, so only do this magic on Android, and only if the found thread
+ // is the main thread.
+ AsanThreadContext *tctx = GetThreadContextByTidLocked(0);
+ if (ThreadStackContainsAddress(tctx, &context)) {
+ SetCurrentThread(tctx->thread);
+ return tctx->thread;
+ }
+ }
+ return 0;
+ }
+ return context->thread;
+}
+
+void SetCurrentThread(AsanThread *t) {
+ CHECK(t->context());
+ if (flags()->verbosity >= 2) {
+ Report("SetCurrentThread: %p for thread %p\n",
+ t->context(), (void*)GetThreadSelf());
+ }
+ // Make sure we do not reset the current AsanThread.
+ CHECK_EQ(0, AsanTSDGet());
+ AsanTSDSet(t->context());
+ CHECK_EQ(t->context(), AsanTSDGet());
+}
+
+u32 GetCurrentTidOrInvalid() {
+ AsanThread *t = GetCurrentThread();
+ return t ? t->tid() : kInvalidTid;
+}
+
+AsanThread *FindThreadByStackAddress(uptr addr) {
+ asanThreadRegistry().CheckLocked();
+ AsanThreadContext *tctx = static_cast<AsanThreadContext *>(
+ asanThreadRegistry().FindThreadContextLocked(ThreadStackContainsAddress,
+ (void *)addr));
+ return tctx ? tctx->thread : 0;
+}
} // namespace __asan
+
+// --- Implementation of LSan-specific functions --- {{{1
+namespace __lsan {
+bool GetThreadRangesLocked(uptr os_id, uptr *stack_begin, uptr *stack_end,
+ uptr *tls_begin, uptr *tls_end,
+ uptr *cache_begin, uptr *cache_end) {
+ // FIXME: Stub.
+ return false;
+}
+
+void LockThreadRegistry() {
+ __asan::asanThreadRegistry().Lock();
+}
+
+void UnlockThreadRegistry() {
+ __asan::asanThreadRegistry().Unlock();
+}
+} // namespace __lsan
diff --git a/lib/asan/asan_thread.h b/lib/asan/asan_thread.h
index acc27e52e224..14062b62f751 100644
--- a/lib/asan/asan_thread.h
+++ b/lib/asan/asan_thread.h
@@ -16,76 +16,58 @@
#include "asan_allocator.h"
#include "asan_internal.h"
+#include "asan_fake_stack.h"
#include "asan_stack.h"
#include "asan_stats.h"
#include "sanitizer_common/sanitizer_libc.h"
+#include "sanitizer_common/sanitizer_thread_registry.h"
namespace __asan {
const u32 kInvalidTid = 0xffffff; // Must fit into 24 bits.
+const u32 kMaxNumberOfThreads = (1 << 22); // 4M
class AsanThread;
// These objects are created for every thread and are never deleted,
// so we can find them by tid even if the thread is long dead.
-class AsanThreadSummary {
+class AsanThreadContext : public ThreadContextBase {
public:
- explicit AsanThreadSummary(LinkerInitialized) { } // for T0.
- void Init(u32 parent_tid, StackTrace *stack) {
- parent_tid_ = parent_tid;
- announced_ = false;
- tid_ = kInvalidTid;
- if (stack) {
- internal_memcpy(&stack_, stack, sizeof(*stack));
- }
- thread_ = 0;
- name_[0] = 0;
+ explicit AsanThreadContext(int tid)
+ : ThreadContextBase(tid),
+ announced(false),
+ thread(0) {
+ internal_memset(&stack, 0, sizeof(stack));
}
- u32 tid() { return tid_; }
- void set_tid(u32 tid) { tid_ = tid; }
- u32 parent_tid() { return parent_tid_; }
- bool announced() { return announced_; }
- void set_announced(bool announced) { announced_ = announced; }
- StackTrace *stack() { return &stack_; }
- AsanThread *thread() { return thread_; }
- void set_thread(AsanThread *thread) { thread_ = thread; }
- static void TSDDtor(void *tsd);
- void set_name(const char *name) {
- internal_strncpy(name_, name, sizeof(name_) - 1);
- }
- const char *name() { return name_; }
+ bool announced;
+ StackTrace stack;
+ AsanThread *thread;
- private:
- u32 tid_;
- u32 parent_tid_;
- bool announced_;
- StackTrace stack_;
- AsanThread *thread_;
- char name_[128];
+ void OnCreated(void *arg);
+ void OnFinished();
};
-// AsanThreadSummary objects are never freed, so we need many of them.
-COMPILER_CHECK(sizeof(AsanThreadSummary) <= 4094);
+// AsanThreadContext objects are never freed, so we need many of them.
+COMPILER_CHECK(sizeof(AsanThreadContext) <= 4096);
// AsanThread are stored in TSD and destroyed when the thread dies.
class AsanThread {
public:
- explicit AsanThread(LinkerInitialized); // for T0.
- static AsanThread *Create(u32 parent_tid, thread_callback_t start_routine,
- void *arg, StackTrace *stack);
+ static AsanThread *Create(thread_callback_t start_routine, void *arg);
+ static void TSDDtor(void *tsd);
void Destroy();
void Init(); // Should be called from the thread itself.
- thread_return_t ThreadStart();
+ thread_return_t ThreadStart(uptr os_id);
uptr stack_top() { return stack_top_; }
uptr stack_bottom() { return stack_bottom_; }
uptr stack_size() { return stack_top_ - stack_bottom_; }
- u32 tid() { return summary_->tid(); }
- AsanThreadSummary *summary() { return summary_; }
- void set_summary(AsanThreadSummary *summary) { summary_ = summary; }
+ u32 tid() { return context_->tid; }
+ AsanThreadContext *context() { return context_; }
+ void set_context(AsanThreadContext *context) { context_ = context; }
- const char *GetFrameNameByAddr(uptr addr, uptr *offset);
+ const char *GetFrameNameByAddr(uptr addr, uptr *offset, uptr *frame_pc);
bool AddrIsInStack(uptr addr) {
return addr >= stack_bottom_ && addr < stack_top_;
@@ -96,9 +78,10 @@ class AsanThread {
AsanStats &stats() { return stats_; }
private:
+ AsanThread() {}
void SetThreadStackTopAndBottom();
void ClearShadowForThreadStack();
- AsanThreadSummary *summary_;
+ AsanThreadContext *context_;
thread_callback_t start_routine_;
void *arg_;
uptr stack_top_;
@@ -109,6 +92,23 @@ class AsanThread {
AsanStats stats_;
};
+struct CreateThreadContextArgs {
+ AsanThread *thread;
+ StackTrace *stack;
+};
+
+// Returns a single instance of registry.
+ThreadRegistry &asanThreadRegistry();
+
+// Must be called under ThreadRegistryLock.
+AsanThreadContext *GetThreadContextByTidLocked(u32 tid);
+
+// Get the current thread. May return 0.
+AsanThread *GetCurrentThread();
+void SetCurrentThread(AsanThread *t);
+u32 GetCurrentTidOrInvalid();
+AsanThread *FindThreadByStackAddress(uptr addr);
+
} // namespace __asan
#endif // ASAN_THREAD_H
diff --git a/lib/asan/asan_thread_registry.cc b/lib/asan/asan_thread_registry.cc
deleted file mode 100644
index 80675405fbd5..000000000000
--- a/lib/asan/asan_thread_registry.cc
+++ /dev/null
@@ -1,198 +0,0 @@
-//===-- asan_thread_registry.cc -------------------------------------------===//
-//
-// 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.
-//
-// AsanThreadRegistry-related code. AsanThreadRegistry is a container
-// for summaries of all created threads.
-//===----------------------------------------------------------------------===//
-
-#include "asan_stack.h"
-#include "asan_thread.h"
-#include "asan_thread_registry.h"
-#include "sanitizer_common/sanitizer_common.h"
-
-namespace __asan {
-
-static AsanThreadRegistry asan_thread_registry(LINKER_INITIALIZED);
-
-AsanThreadRegistry &asanThreadRegistry() {
- return asan_thread_registry;
-}
-
-AsanThreadRegistry::AsanThreadRegistry(LinkerInitialized x)
- : main_thread_(x),
- main_thread_summary_(x),
- accumulated_stats_(x),
- max_malloced_memory_(x),
- mu_(x) { }
-
-void AsanThreadRegistry::Init() {
- AsanTSDInit(AsanThreadSummary::TSDDtor);
- main_thread_.set_summary(&main_thread_summary_);
- main_thread_summary_.set_thread(&main_thread_);
- RegisterThread(&main_thread_);
- SetCurrent(&main_thread_);
- // At this point only one thread exists.
- inited_ = true;
-}
-
-void AsanThreadRegistry::RegisterThread(AsanThread *thread) {
- BlockingMutexLock lock(&mu_);
- u32 tid = n_threads_;
- n_threads_++;
- CHECK(n_threads_ < kMaxNumberOfThreads);
-
- AsanThreadSummary *summary = thread->summary();
- CHECK(summary != 0);
- summary->set_tid(tid);
- thread_summaries_[tid] = summary;
-}
-
-void AsanThreadRegistry::UnregisterThread(AsanThread *thread) {
- BlockingMutexLock lock(&mu_);
- FlushToAccumulatedStatsUnlocked(&thread->stats());
- AsanThreadSummary *summary = thread->summary();
- CHECK(summary);
- summary->set_thread(0);
-}
-
-AsanThread *AsanThreadRegistry::GetMain() {
- return &main_thread_;
-}
-
-AsanThread *AsanThreadRegistry::GetCurrent() {
- AsanThreadSummary *summary = (AsanThreadSummary *)AsanTSDGet();
- if (!summary) {
-#if ASAN_ANDROID
- // On Android, libc constructor is called _after_ asan_init, and cleans up
- // TSD. Try to figure out if this is still the main thread by the stack
- // address. We are not entirely sure that we have correct main thread
- // limits, so only do this magic on Android, and only if the found thread is
- // the main thread.
- AsanThread* thread = FindThreadByStackAddress((uptr)&summary);
- if (thread && thread->tid() == 0) {
- SetCurrent(thread);
- return thread;
- }
-#endif
- return 0;
- }
- return summary->thread();
-}
-
-void AsanThreadRegistry::SetCurrent(AsanThread *t) {
- CHECK(t->summary());
- if (flags()->verbosity >= 2) {
- Report("SetCurrent: %p for thread %p\n",
- t->summary(), (void*)GetThreadSelf());
- }
- // Make sure we do not reset the current AsanThread.
- CHECK(AsanTSDGet() == 0);
- AsanTSDSet(t->summary());
- CHECK(AsanTSDGet() == t->summary());
-}
-
-AsanStats &AsanThreadRegistry::GetCurrentThreadStats() {
- AsanThread *t = GetCurrent();
- return (t) ? t->stats() : main_thread_.stats();
-}
-
-void AsanThreadRegistry::GetAccumulatedStats(AsanStats *stats) {
- BlockingMutexLock lock(&mu_);
- UpdateAccumulatedStatsUnlocked();
- internal_memcpy(stats, &accumulated_stats_, sizeof(accumulated_stats_));
-}
-
-uptr AsanThreadRegistry::GetCurrentAllocatedBytes() {
- BlockingMutexLock lock(&mu_);
- UpdateAccumulatedStatsUnlocked();
- uptr malloced = accumulated_stats_.malloced;
- uptr freed = accumulated_stats_.freed;
- // Return sane value if malloced < freed due to racy
- // way we update accumulated stats.
- return (malloced > freed) ? malloced - freed : 1;
-}
-
-uptr AsanThreadRegistry::GetHeapSize() {
- BlockingMutexLock lock(&mu_);
- UpdateAccumulatedStatsUnlocked();
- return accumulated_stats_.mmaped - accumulated_stats_.munmaped;
-}
-
-uptr AsanThreadRegistry::GetFreeBytes() {
- BlockingMutexLock lock(&mu_);
- UpdateAccumulatedStatsUnlocked();
- uptr total_free = accumulated_stats_.mmaped
- - accumulated_stats_.munmaped
- + accumulated_stats_.really_freed
- + accumulated_stats_.really_freed_redzones;
- uptr total_used = accumulated_stats_.malloced
- + accumulated_stats_.malloced_redzones;
- // Return sane value if total_free < total_used due to racy
- // way we update accumulated stats.
- return (total_free > total_used) ? total_free - total_used : 1;
-}
-
-// Return several stats counters with a single call to
-// UpdateAccumulatedStatsUnlocked().
-void AsanThreadRegistry::FillMallocStatistics(AsanMallocStats *malloc_stats) {
- BlockingMutexLock lock(&mu_);
- UpdateAccumulatedStatsUnlocked();
- malloc_stats->blocks_in_use = accumulated_stats_.mallocs;
- malloc_stats->size_in_use = accumulated_stats_.malloced;
- malloc_stats->max_size_in_use = max_malloced_memory_;
- malloc_stats->size_allocated = accumulated_stats_.mmaped;
-}
-
-AsanThreadSummary *AsanThreadRegistry::FindByTid(u32 tid) {
- CHECK(tid < n_threads_);
- CHECK(thread_summaries_[tid]);
- return thread_summaries_[tid];
-}
-
-AsanThread *AsanThreadRegistry::FindThreadByStackAddress(uptr addr) {
- BlockingMutexLock lock(&mu_);
- for (u32 tid = 0; tid < n_threads_; tid++) {
- AsanThread *t = thread_summaries_[tid]->thread();
- if (!t || !(t->fake_stack().StackSize())) continue;
- if (t->fake_stack().AddrIsInFakeStack(addr) || t->AddrIsInStack(addr)) {
- return t;
- }
- }
- return 0;
-}
-
-void AsanThreadRegistry::UpdateAccumulatedStatsUnlocked() {
- for (u32 tid = 0; tid < n_threads_; tid++) {
- AsanThread *t = thread_summaries_[tid]->thread();
- if (t != 0) {
- FlushToAccumulatedStatsUnlocked(&t->stats());
- }
- }
- // This is not very accurate: we may miss allocation peaks that happen
- // between two updates of accumulated_stats_. For more accurate bookkeeping
- // the maximum should be updated on every malloc(), which is unacceptable.
- if (max_malloced_memory_ < accumulated_stats_.malloced) {
- max_malloced_memory_ = accumulated_stats_.malloced;
- }
-}
-
-void AsanThreadRegistry::FlushToAccumulatedStatsUnlocked(AsanStats *stats) {
- // AsanStats consists of variables of type uptr only.
- uptr *dst = (uptr*)&accumulated_stats_;
- uptr *src = (uptr*)stats;
- uptr num_fields = sizeof(AsanStats) / sizeof(uptr);
- for (uptr i = 0; i < num_fields; i++) {
- dst[i] += src[i];
- src[i] = 0;
- }
-}
-
-} // namespace __asan
diff --git a/lib/asan/asan_thread_registry.h b/lib/asan/asan_thread_registry.h
deleted file mode 100644
index adb1a6d4f32d..000000000000
--- a/lib/asan/asan_thread_registry.h
+++ /dev/null
@@ -1,85 +0,0 @@
-//===-- asan_thread_registry.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-private header for asan_thread_registry.cc
-//===----------------------------------------------------------------------===//
-
-#ifndef ASAN_THREAD_REGISTRY_H
-#define ASAN_THREAD_REGISTRY_H
-
-#include "asan_stack.h"
-#include "asan_stats.h"
-#include "asan_thread.h"
-#include "sanitizer_common/sanitizer_mutex.h"
-
-namespace __asan {
-
-// Stores summaries of all created threads, returns current thread,
-// thread by tid, thread by stack address. There is a single instance
-// of AsanThreadRegistry for the whole program.
-// AsanThreadRegistry is thread-safe.
-class AsanThreadRegistry {
- public:
- explicit AsanThreadRegistry(LinkerInitialized);
- void Init();
- void RegisterThread(AsanThread *thread);
- void UnregisterThread(AsanThread *thread);
-
- AsanThread *GetMain();
- // Get the current thread. May return 0.
- AsanThread *GetCurrent();
- void SetCurrent(AsanThread *t);
-
- u32 GetCurrentTidOrInvalid() {
- if (!inited_) return 0;
- AsanThread *t = GetCurrent();
- return t ? t->tid() : kInvalidTid;
- }
-
- // Returns stats for GetCurrent(), or stats for
- // T0 if GetCurrent() returns 0.
- AsanStats &GetCurrentThreadStats();
- // Flushes all thread-local stats to accumulated stats, and makes
- // a copy of accumulated stats.
- void GetAccumulatedStats(AsanStats *stats);
- uptr GetCurrentAllocatedBytes();
- uptr GetHeapSize();
- uptr GetFreeBytes();
- void FillMallocStatistics(AsanMallocStats *malloc_stats);
-
- AsanThreadSummary *FindByTid(u32 tid);
- AsanThread *FindThreadByStackAddress(uptr addr);
-
- private:
- void UpdateAccumulatedStatsUnlocked();
- // Adds values of all counters in "stats" to accumulated stats,
- // and fills "stats" with zeroes.
- void FlushToAccumulatedStatsUnlocked(AsanStats *stats);
-
- static const u32 kMaxNumberOfThreads = (1 << 22); // 4M
- AsanThreadSummary *thread_summaries_[kMaxNumberOfThreads];
- AsanThread main_thread_;
- AsanThreadSummary main_thread_summary_;
- AsanStats accumulated_stats_;
- // Required for malloc_zone_statistics() on OS X. This can't be stored in
- // per-thread AsanStats.
- uptr max_malloced_memory_;
- u32 n_threads_;
- BlockingMutex mu_;
- bool inited_;
-};
-
-// Returns a single instance of registry.
-AsanThreadRegistry &asanThreadRegistry();
-
-} // namespace __asan
-
-#endif // ASAN_THREAD_REGISTRY_H
diff --git a/lib/asan/asan_win.cc b/lib/asan/asan_win.cc
index d8ce050641bc..f74de7227ed2 100644
--- a/lib/asan/asan_win.cc
+++ b/lib/asan/asan_win.cc
@@ -11,7 +11,9 @@
//
// Windows-specific details.
//===----------------------------------------------------------------------===//
-#ifdef _WIN32
+
+#include "sanitizer_common/sanitizer_platform.h"
+#if SANITIZER_WINDOWS
#include <windows.h>
#include <dbghelp.h>
@@ -30,30 +32,6 @@ static BlockingMutex dbghelp_lock(LINKER_INITIALIZED);
static bool dbghelp_initialized = false;
#pragma comment(lib, "dbghelp.lib")
-void GetStackTrace(StackTrace *stack, uptr max_s, uptr pc, uptr bp, bool fast) {
- (void)fast;
- stack->max_size = max_s;
- void *tmp[kStackTraceMax];
-
- // FIXME: CaptureStackBackTrace might be too slow for us.
- // FIXME: Compare with StackWalk64.
- // FIXME: Look at LLVMUnhandledExceptionFilter in Signals.inc
- uptr cs_ret = CaptureStackBackTrace(1, stack->max_size, tmp, 0);
- uptr offset = 0;
- // Skip the RTL frames by searching for the PC in the stacktrace.
- // FIXME: this doesn't work well for the malloc/free stacks yet.
- for (uptr i = 0; i < cs_ret; i++) {
- if (pc != (uptr)tmp[i])
- continue;
- offset = i;
- break;
- }
-
- stack->size = cs_ret - offset;
- for (uptr i = 0; i < stack->size; i++)
- stack->trace[i] = (uptr)tmp[i + offset];
-}
-
// ---------------------- TSD ---------------- {{{1
static bool tsd_key_inited = false;
diff --git a/lib/asan/dynamic/Makefile.mk b/lib/asan/dynamic/Makefile.mk
deleted file mode 100644
index 897844e7eed4..000000000000
--- a/lib/asan/dynamic/Makefile.mk
+++ /dev/null
@@ -1,25 +0,0 @@
-#===- lib/asan/dynamic/Makefile.mk -------------------------*- Makefile -*--===#
-#
-# The LLVM Compiler Infrastructure
-#
-# This file is distributed under the University of Illinois Open Source
-# License. See LICENSE.TXT for details.
-#
-#===------------------------------------------------------------------------===#
-
-ModuleName := asan_dynamic
-SubDirs :=
-
-Sources := $(foreach file,$(wildcard $(Dir)/*.cc),$(notdir $(file)))
-ObjNames := $(Sources:%.cc=%.o)
-
-Implementation := Generic
-
-# FIXME: use automatic dependencies?
-Dependencies := $(wildcard $(Dir)/*.h)
-Dependencies += $(wildcard $(Dir)/../../interception/*.h)
-Dependencies += $(wildcard $(Dir)/../../interception/mach_override/*.h)
-Dependencies += $(wildcard $(Dir)/../../sanitizer_common/*.h)
-
-# Define a convenience variable for the asan dynamic functions.
-AsanDynamicFunctions := $(Sources:%.cc=%)
diff --git a/lib/asan/dynamic/asan_interceptors_dynamic.cc b/lib/asan/dynamic/asan_interceptors_dynamic.cc
deleted file mode 100644
index 4f0f7bd2d5f8..000000000000
--- a/lib/asan/dynamic/asan_interceptors_dynamic.cc
+++ /dev/null
@@ -1,111 +0,0 @@
-//===-- asan_interceptors_dynamic.cc --------------------------------------===//
-//
-// 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.
-//
-// __DATA,__interpose section of the dynamic runtime library for Mac OS.
-//===----------------------------------------------------------------------===//
-
-#if defined(__APPLE__)
-
-#include "../asan_interceptors.h"
-#include "../asan_intercepted_functions.h"
-
-namespace __asan {
-
-#if !MAC_INTERPOSE_FUNCTIONS
-# error \
- Dynamic interposing library should be built with -DMAC_INTERPOSE_FUNCTIONS
-#endif
-
-#define INTERPOSE_FUNCTION(function) \
- { reinterpret_cast<const uptr>(WRAP(function)), \
- reinterpret_cast<const uptr>(function) }
-
-#define INTERPOSE_FUNCTION_2(function, wrapper) \
- { reinterpret_cast<const uptr>(wrapper), \
- reinterpret_cast<const uptr>(function) }
-
-struct interpose_substitution {
- const uptr replacement;
- const uptr original;
-};
-
-__attribute__((used))
-const interpose_substitution substitutions[]
- __attribute__((section("__DATA, __interpose"))) = {
- INTERPOSE_FUNCTION(strlen),
- INTERPOSE_FUNCTION(memcmp),
- INTERPOSE_FUNCTION(memcpy),
- INTERPOSE_FUNCTION(memmove),
- INTERPOSE_FUNCTION(memset),
- INTERPOSE_FUNCTION(strchr),
- INTERPOSE_FUNCTION(strcat),
- INTERPOSE_FUNCTION(strncat),
- INTERPOSE_FUNCTION(strcpy),
- INTERPOSE_FUNCTION(strncpy),
- INTERPOSE_FUNCTION(pthread_create),
- INTERPOSE_FUNCTION(longjmp),
-#if ASAN_INTERCEPT__LONGJMP
- INTERPOSE_FUNCTION(_longjmp),
-#endif
-#if ASAN_INTERCEPT_SIGLONGJMP
- INTERPOSE_FUNCTION(siglongjmp),
-#endif
-#if ASAN_INTERCEPT_STRDUP
- INTERPOSE_FUNCTION(strdup),
-#endif
-#if ASAN_INTERCEPT_STRNLEN
- INTERPOSE_FUNCTION(strnlen),
-#endif
-#if ASAN_INTERCEPT_INDEX
- INTERPOSE_FUNCTION_2(index, WRAP(strchr)),
-#endif
- INTERPOSE_FUNCTION(strcmp),
- INTERPOSE_FUNCTION(strncmp),
-#if ASAN_INTERCEPT_STRCASECMP_AND_STRNCASECMP
- INTERPOSE_FUNCTION(strcasecmp),
- INTERPOSE_FUNCTION(strncasecmp),
-#endif
- INTERPOSE_FUNCTION(atoi),
- INTERPOSE_FUNCTION(atol),
- INTERPOSE_FUNCTION(strtol),
-#if ASAN_INTERCEPT_ATOLL_AND_STRTOLL
- INTERPOSE_FUNCTION(atoll),
- INTERPOSE_FUNCTION(strtoll),
-#endif
-#if ASAN_INTERCEPT_MLOCKX
- INTERPOSE_FUNCTION(mlock),
- INTERPOSE_FUNCTION(munlock),
- INTERPOSE_FUNCTION(mlockall),
- INTERPOSE_FUNCTION(munlockall),
-#endif
- INTERPOSE_FUNCTION(dispatch_async_f),
- INTERPOSE_FUNCTION(dispatch_sync_f),
- INTERPOSE_FUNCTION(dispatch_after_f),
- INTERPOSE_FUNCTION(dispatch_barrier_async_f),
- INTERPOSE_FUNCTION(dispatch_group_async_f),
-#ifndef MISSING_BLOCKS_SUPPORT
- INTERPOSE_FUNCTION(dispatch_group_async),
- INTERPOSE_FUNCTION(dispatch_async),
- INTERPOSE_FUNCTION(dispatch_after),
- INTERPOSE_FUNCTION(dispatch_source_set_event_handler),
- INTERPOSE_FUNCTION(dispatch_source_set_cancel_handler),
-#endif
- INTERPOSE_FUNCTION(signal),
- INTERPOSE_FUNCTION(sigaction),
-
- INTERPOSE_FUNCTION(__CFInitialize),
- INTERPOSE_FUNCTION(CFStringCreateCopy),
- INTERPOSE_FUNCTION(free),
-};
-
-} // namespace __asan
-
-#endif // __APPLE__
diff --git a/lib/asan/lit_tests/CMakeLists.txt b/lib/asan/lit_tests/CMakeLists.txt
index 1609032d4670..d2420b50da83 100644
--- a/lib/asan/lit_tests/CMakeLists.txt
+++ b/lib/asan/lit_tests/CMakeLists.txt
@@ -14,9 +14,9 @@ configure_lit_site_cfg(
if(COMPILER_RT_CAN_EXECUTE_TESTS)
# Run ASan tests only if we're sure we may produce working binaries.
set(ASAN_TEST_DEPS
- clang clang-headers FileCheck count not llvm-nm llvm-symbolizer
+ ${SANITIZER_COMMON_LIT_TEST_DEPS}
${ASAN_RUNTIME_LIBRARIES}
- )
+ asan_blacklist)
set(ASAN_TEST_PARAMS
asan_site_config=${CMAKE_CURRENT_BINARY_DIR}/lit.site.cfg
)
diff --git a/lib/asan/lit_tests/Darwin/interface_symbols_darwin.c b/lib/asan/lit_tests/Darwin/interface_symbols_darwin.c
new file mode 100644
index 000000000000..3fca6e915324
--- /dev/null
+++ b/lib/asan/lit_tests/Darwin/interface_symbols_darwin.c
@@ -0,0 +1,39 @@
+// Check the presense of interface symbols in the ASan runtime dylib.
+// If you're changing this file, please also change
+// ../Linux/interface_symbols.c
+
+// RUN: %clang -fsanitize=address -dead_strip -O2 %s -o %t.exe
+// RUN: rm -f %t.symbols %t.interface
+
+// RUN: nm -g `otool -L %t.exe | grep "asan_osx_dynamic.dylib" | \
+// RUN: tr -d '\011' | \
+// RUN: sed "s/.dylib.*/.dylib/"` \
+// RUN: | grep " T " | sed "s/.* T //" \
+// RUN: | grep "__asan_" | sed "s/___asan_/__asan_/" \
+// RUN: | grep -v "__asan_malloc_hook" \
+// RUN: | grep -v "__asan_free_hook" \
+// RUN: | grep -v "__asan_symbolize" \
+// RUN: | grep -v "__asan_default_options" \
+// RUN: | grep -v "__asan_on_error" > %t.symbols
+
+// RUN: cat %p/../../asan_interface_internal.h \
+// RUN: | sed "s/\/\/.*//" | sed "s/typedef.*//" \
+// RUN: | grep -v "OPTIONAL" \
+// RUN: | grep "__asan_.*(" | sed "s/.* __asan_/__asan_/;s/(.*//" \
+// RUN: > %t.interface
+// RUN: echo __asan_report_load1 >> %t.interface
+// RUN: echo __asan_report_load2 >> %t.interface
+// RUN: echo __asan_report_load4 >> %t.interface
+// RUN: echo __asan_report_load8 >> %t.interface
+// RUN: echo __asan_report_load16 >> %t.interface
+// RUN: echo __asan_report_store1 >> %t.interface
+// RUN: echo __asan_report_store2 >> %t.interface
+// RUN: echo __asan_report_store4 >> %t.interface
+// RUN: echo __asan_report_store8 >> %t.interface
+// RUN: echo __asan_report_store16 >> %t.interface
+// RUN: echo __asan_report_load_n >> %t.interface
+// RUN: echo __asan_report_store_n >> %t.interface
+
+// RUN: cat %t.interface | sort -u | diff %t.symbols -
+
+int main() { return 0; }
diff --git a/lib/asan/lit_tests/Darwin/lit.local.cfg b/lib/asan/lit_tests/Darwin/lit.local.cfg
new file mode 100644
index 000000000000..a85dfcd24c08
--- /dev/null
+++ b/lib/asan/lit_tests/Darwin/lit.local.cfg
@@ -0,0 +1,9 @@
+def getRoot(config):
+ if not config.parent:
+ return config
+ return getRoot(config.parent)
+
+root = getRoot(config)
+
+if root.host_os not in ['Darwin']:
+ config.unsupported = True
diff --git a/lib/asan/lit_tests/Darwin/reexec-insert-libraries-env.cc b/lib/asan/lit_tests/Darwin/reexec-insert-libraries-env.cc
new file mode 100644
index 000000000000..40a459fd84db
--- /dev/null
+++ b/lib/asan/lit_tests/Darwin/reexec-insert-libraries-env.cc
@@ -0,0 +1,20 @@
+// Make sure ASan doesn't hang in an exec loop if DYLD_INSERT_LIBRARIES is set.
+// This is a regression test for
+// https://code.google.com/p/address-sanitizer/issues/detail?id=159
+
+// RUN: %clangxx_asan -m64 %s -o %t
+// RUN: %clangxx -m64 %p/../SharedLibs/darwin-dummy-shared-lib-so.cc \
+// RUN: -dynamiclib -o darwin-dummy-shared-lib-so.dylib
+
+// FIXME: the following command line may hang in the case of a regression.
+// RUN: DYLD_INSERT_LIBRARIES=darwin-dummy-shared-lib-so.dylib \
+// RUN: %t 2>&1 | FileCheck %s || exit 1
+#include <stdio.h>
+#include <stdlib.h>
+
+int main() {
+ const char kEnvName[] = "DYLD_INSERT_LIBRARIES";
+ printf("%s=%s\n", kEnvName, getenv(kEnvName));
+ // CHECK: {{DYLD_INSERT_LIBRARIES=.*darwin-dummy-shared-lib-so.dylib.*}}
+ return 0;
+}
diff --git a/lib/asan/lit_tests/Darwin/unset-insert-libraries-on-exec.cc b/lib/asan/lit_tests/Darwin/unset-insert-libraries-on-exec.cc
new file mode 100644
index 000000000000..cf89949cf942
--- /dev/null
+++ b/lib/asan/lit_tests/Darwin/unset-insert-libraries-on-exec.cc
@@ -0,0 +1,20 @@
+// Make sure ASan removes the runtime library from DYLD_INSERT_LIBRARIES before
+// executing other programs.
+
+// RUN: %clangxx_asan -m64 %s -o %t
+// RUN: %clangxx -m64 %p/../SharedLibs/darwin-dummy-shared-lib-so.cc \
+// RUN: -dynamiclib -o darwin-dummy-shared-lib-so.dylib
+
+// Make sure DYLD_INSERT_LIBRARIES doesn't contain the runtime library before
+// execl().
+
+// RUN: %t >/dev/null 2>&1
+// RUN: DYLD_INSERT_LIBRARIES=darwin-dummy-shared-lib-so.dylib \
+// RUN: %t 2>&1 | FileCheck %s || exit 1
+#include <unistd.h>
+int main() {
+ execl("/bin/bash", "/bin/bash", "-c",
+ "echo DYLD_INSERT_LIBRARIES=$DYLD_INSERT_LIBRARIES", NULL);
+ // CHECK: {{DYLD_INSERT_LIBRARIES=.*darwin-dummy-shared-lib-so.dylib.*}}
+ return 0;
+}
diff --git a/lib/asan/lit_tests/Helpers/init-order-atexit-extra.cc b/lib/asan/lit_tests/Helpers/init-order-atexit-extra.cc
new file mode 100644
index 000000000000..e4189d19d099
--- /dev/null
+++ b/lib/asan/lit_tests/Helpers/init-order-atexit-extra.cc
@@ -0,0 +1,16 @@
+#include <stdio.h>
+
+class C {
+ public:
+ C() { value = 42; }
+ ~C() { }
+ int value;
+};
+
+C c;
+
+void AccessC() {
+ printf("C value: %d\n", c.value);
+}
+
+int main() { return 0; }
diff --git a/lib/asan/lit_tests/Helpers/initialization-blacklist-extra2.cc b/lib/asan/lit_tests/Helpers/initialization-blacklist-extra2.cc
new file mode 100644
index 000000000000..69455a0a6fc9
--- /dev/null
+++ b/lib/asan/lit_tests/Helpers/initialization-blacklist-extra2.cc
@@ -0,0 +1,4 @@
+int zero_init();
+int badSrcGlobal = zero_init();
+int readBadSrcGlobal() { return badSrcGlobal; }
+
diff --git a/lib/asan/lit_tests/Helpers/initialization-blacklist.txt b/lib/asan/lit_tests/Helpers/initialization-blacklist.txt
index c5f6610937f0..fa4a83667f4b 100644
--- a/lib/asan/lit_tests/Helpers/initialization-blacklist.txt
+++ b/lib/asan/lit_tests/Helpers/initialization-blacklist.txt
@@ -1,2 +1,3 @@
global-init:*badGlobal*
global-init-type:*badNamespace::BadClass*
+global-init-src:*initialization-blacklist-extra2.cc
diff --git a/lib/asan/lit_tests/Helpers/initialization-constexpr-extra.cc b/lib/asan/lit_tests/Helpers/initialization-constexpr-extra.cc
new file mode 100644
index 000000000000..b32466a981b3
--- /dev/null
+++ b/lib/asan/lit_tests/Helpers/initialization-constexpr-extra.cc
@@ -0,0 +1,3 @@
+// Constexpr:
+int getCoolestInteger();
+static int coolest_integer = getCoolestInteger();
diff --git a/lib/asan/lit_tests/Helpers/initialization-nobug-extra.cc b/lib/asan/lit_tests/Helpers/initialization-nobug-extra.cc
index 490b3339054a..886165affd76 100644
--- a/lib/asan/lit_tests/Helpers/initialization-nobug-extra.cc
+++ b/lib/asan/lit_tests/Helpers/initialization-nobug-extra.cc
@@ -4,6 +4,6 @@ static int ab = getAB();
// Function local statics:
int countCalls();
static int one = countCalls();
-// Constexpr:
-int getCoolestInteger();
-static int coolest_integer = getCoolestInteger();
+// Trivial constructor, non-trivial destructor:
+int getStructWithDtorValue();
+static int val = getStructWithDtorValue();
diff --git a/lib/asan/lit_tests/Linux/asan_prelink_test.cc b/lib/asan/lit_tests/Linux/asan_prelink_test.cc
new file mode 100644
index 000000000000..c209c39c8c42
--- /dev/null
+++ b/lib/asan/lit_tests/Linux/asan_prelink_test.cc
@@ -0,0 +1,28 @@
+// Test if asan works with prelink.
+// It does not actually use prelink, but relies on ld's flag -Ttext-segment
+// or gold's flag -Ttext (we try the first flag first, if that fails we
+// try the second flag).
+//
+// RUN: %clangxx_asan -m64 -c %s -o %t.o
+// RUN: %clangxx_asan -m64 -DBUILD_SO=1 -fPIC -shared %s -o %t.so -Wl,-Ttext-segment=0x3600000000 ||\
+// RUN: %clangxx_asan -m64 -DBUILD_SO=1 -fPIC -shared %s -o %t.so -Wl,-Ttext=0x3600000000
+// RUN: %clangxx_asan -m64 %t.o %t.so -Wl,-R. -o %t
+// RUN: ASAN_OPTIONS=verbosity=1 %t 2>&1 | FileCheck %s
+
+// REQUIRES: x86_64-supported-target
+#if BUILD_SO
+int G;
+int *getG() {
+ return &G;
+}
+#else
+#include <stdio.h>
+extern int *getG();
+int main(int argc, char **argv) {
+ long p = (long)getG();
+ printf("SO mapped at %lx\n", p & ~0xffffffffUL);
+ *getG() = 0;
+}
+#endif
+// CHECK: 0x003000000000, 0x004fffffffff{{.*}} MidMem
+// CHECK: SO mapped at 3600000000
diff --git a/lib/asan/lit_tests/Linux/glob.cc b/lib/asan/lit_tests/Linux/glob.cc
new file mode 100644
index 000000000000..e05228ff39e3
--- /dev/null
+++ b/lib/asan/lit_tests/Linux/glob.cc
@@ -0,0 +1,30 @@
+// RUN: %clangxx_asan -m64 -O0 %s -o %t && %t %p 2>&1 | FileCheck %s
+// RUN: %clangxx_asan -m64 -O3 %s -o %t && %t %p 2>&1 | FileCheck %s
+// RUN: %clangxx_asan -m32 -O0 %s -o %t && %t %p 2>&1 | FileCheck %s
+// RUN: %clangxx_asan -m32 -O3 %s -o %t && %t %p 2>&1 | FileCheck %s
+
+#include <assert.h>
+#include <glob.h>
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+#include <string>
+
+
+int main(int argc, char *argv[]) {
+ std::string path = argv[1];
+ std::string pattern = path + "/glob_test_root/*a";
+ printf("pattern: %s\n", pattern.c_str());
+
+ glob_t globbuf;
+ int res = glob(pattern.c_str(), 0, 0, &globbuf);
+
+ printf("%d %s\n", errno, strerror(errno));
+ assert(res == 0);
+ assert(globbuf.gl_pathc == 2);
+ printf("%zu\n", strlen(globbuf.gl_pathv[0]));
+ printf("%zu\n", strlen(globbuf.gl_pathv[1]));
+ printf("PASS\n");
+ // CHECK: PASS
+ return 0;
+}
diff --git a/lib/asan/lit_tests/Linux/glob_test_root/aa b/lib/asan/lit_tests/Linux/glob_test_root/aa
new file mode 100644
index 000000000000..e69de29bb2d1
--- /dev/null
+++ b/lib/asan/lit_tests/Linux/glob_test_root/aa
diff --git a/lib/asan/lit_tests/Linux/glob_test_root/ab b/lib/asan/lit_tests/Linux/glob_test_root/ab
new file mode 100644
index 000000000000..e69de29bb2d1
--- /dev/null
+++ b/lib/asan/lit_tests/Linux/glob_test_root/ab
diff --git a/lib/asan/lit_tests/Linux/glob_test_root/ba b/lib/asan/lit_tests/Linux/glob_test_root/ba
new file mode 100644
index 000000000000..e69de29bb2d1
--- /dev/null
+++ b/lib/asan/lit_tests/Linux/glob_test_root/ba
diff --git a/lib/asan/lit_tests/Linux/heavy_uar_test.cc b/lib/asan/lit_tests/Linux/heavy_uar_test.cc
new file mode 100644
index 000000000000..c0f4560fb4e7
--- /dev/null
+++ b/lib/asan/lit_tests/Linux/heavy_uar_test.cc
@@ -0,0 +1,55 @@
+// RUN: %clangxx_asan -fsanitize=use-after-return -m64 -O0 %s -o %t && \
+// RUN: %t 2>&1 | %symbolize | FileCheck %s
+// RUN: %clangxx_asan -fsanitize=use-after-return -m64 -O2 %s -o %t && \
+// RUN: %t 2>&1 | %symbolize | FileCheck %s
+// RUN: %clangxx_asan -fsanitize=use-after-return -m32 -O2 %s -o %t && \
+// RUN: %t 2>&1 | %symbolize | FileCheck %s
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+
+__attribute__((noinline))
+char *pretend_to_do_something(char *x) {
+ __asm__ __volatile__("" : : "r" (x) : "memory");
+ return x;
+}
+
+__attribute__((noinline))
+char *LeakStack() {
+ char x[1024];
+ memset(x, 0, sizeof(x));
+ return pretend_to_do_something(x);
+}
+
+template<size_t kFrameSize>
+__attribute__((noinline))
+void RecuriveFunctionWithStackFrame(int depth) {
+ if (depth <= 0) return;
+ char x[kFrameSize];
+ x[0] = depth;
+ pretend_to_do_something(x);
+ RecuriveFunctionWithStackFrame<kFrameSize>(depth - 1);
+}
+
+int main(int argc, char **argv) {
+ int n_iter = argc >= 2 ? atoi(argv[1]) : 1000;
+ int depth = argc >= 3 ? atoi(argv[2]) : 500;
+ for (int i = 0; i < n_iter; i++) {
+ RecuriveFunctionWithStackFrame<10>(depth);
+ RecuriveFunctionWithStackFrame<100>(depth);
+ RecuriveFunctionWithStackFrame<500>(depth);
+ RecuriveFunctionWithStackFrame<1024>(depth);
+ RecuriveFunctionWithStackFrame<2000>(depth);
+ RecuriveFunctionWithStackFrame<5000>(depth);
+ RecuriveFunctionWithStackFrame<10000>(depth);
+ }
+ char *stale_stack = LeakStack();
+ RecuriveFunctionWithStackFrame<1024>(10);
+ stale_stack[100]++;
+ // CHECK: ERROR: AddressSanitizer: stack-use-after-return on address
+ // CHECK: is located in stack of thread T0 at offset 132 in frame
+ // CHECK: in LeakStack(){{.*}}heavy_uar_test.cc:
+ // CHECK: [32, 1056) 'x'
+ return 0;
+}
diff --git a/lib/asan/lit_tests/Linux/initialization-bug-any-order.cc b/lib/asan/lit_tests/Linux/initialization-bug-any-order.cc
index 645fe1c85ed4..4f41dda18128 100644
--- a/lib/asan/lit_tests/Linux/initialization-bug-any-order.cc
+++ b/lib/asan/lit_tests/Linux/initialization-bug-any-order.cc
@@ -1,12 +1,13 @@
// Test to make sure basic initialization order errors are caught.
// Check that on Linux initialization order bugs are caught
-// independently on order in which we list source files.
+// independently on order in which we list source files (if we specify
+// strict init-order checking).
-// RUN: %clangxx_asan -m64 -O0 %s %p/../Helpers/initialization-bug-extra.cc\
-// RUN: -fsanitize=init-order -o %t && %t 2>&1 \
+// RUN: %clangxx_asan -m64 -O0 %s %p/../Helpers/initialization-bug-extra.cc -o %t
+// RUN: ASAN_OPTIONS=check_initialization_order=true:strict_init_order=true %t 2>&1 \
// RUN: | %symbolize | FileCheck %s
-// RUN: %clangxx_asan -m64 -O0 %p/../Helpers/initialization-bug-extra.cc %s\
-// RUN: -fsanitize=init-order -o %t && %t 2>&1 \
+// RUN: %clangxx_asan -m64 -O0 %p/../Helpers/initialization-bug-extra.cc %s -o %t
+// RUN: ASAN_OPTIONS=check_initialization_order=true:strict_init_order=true %t 2>&1 \
// RUN: | %symbolize | FileCheck %s
// Do not test with optimization -- the error may be optimized away.
diff --git a/lib/asan/lit_tests/interface_symbols.c b/lib/asan/lit_tests/Linux/interface_symbols_linux.c
index f3167f562922..4134c8744043 100644
--- a/lib/asan/lit_tests/interface_symbols.c
+++ b/lib/asan/lit_tests/Linux/interface_symbols_linux.c
@@ -1,14 +1,14 @@
// Check the presense of interface symbols in compiled file.
-// RUN: %clang -fsanitize=address -dead_strip -O2 %s -o %t.exe
-// RUN: nm %t.exe | grep " T " | sed "s/.* T //" \
+// RUN: %clang -fsanitize=address -O2 %s -o %t.exe
+// RUN: nm -D %t.exe | grep " T " | sed "s/.* T //" \
// RUN: | grep "__asan_" | sed "s/___asan_/__asan_/" \
// RUN: | grep -v "__asan_malloc_hook" \
// RUN: | grep -v "__asan_free_hook" \
// RUN: | grep -v "__asan_symbolize" \
// RUN: | grep -v "__asan_default_options" \
// RUN: | grep -v "__asan_on_error" > %t.symbols
-// RUN: cat %p/../../../include/sanitizer/asan_interface.h \
+// RUN: cat %p/../../asan_interface_internal.h \
// RUN: | sed "s/\/\/.*//" | sed "s/typedef.*//" \
// RUN: | grep -v "OPTIONAL" \
// RUN: | grep "__asan_.*(" | sed "s/.* __asan_/__asan_/;s/(.*//" \
@@ -23,6 +23,12 @@
// RUN: echo __asan_report_store4 >> %t.interface
// RUN: echo __asan_report_store8 >> %t.interface
// RUN: echo __asan_report_store16 >> %t.interface
+// RUN: echo __asan_report_load_n >> %t.interface
+// RUN: echo __asan_report_store_n >> %t.interface
// RUN: cat %t.interface | sort -u | diff %t.symbols -
+// FIXME: nm -D on powerpc somewhy shows ASan interface symbols residing
+// in "initialized data section".
+// REQUIRES: x86_64-supported-target,i386-supported-target
+
int main() { return 0; }
diff --git a/lib/asan/lit_tests/Linux/malloc-in-qsort.cc b/lib/asan/lit_tests/Linux/malloc-in-qsort.cc
index a3fa255b186d..ee2e81f0d2ab 100644
--- a/lib/asan/lit_tests/Linux/malloc-in-qsort.cc
+++ b/lib/asan/lit_tests/Linux/malloc-in-qsort.cc
@@ -1,10 +1,14 @@
-// RUN: %clangxx_asan -O2 %s -o %t
+// RUN: %clangxx_asan -m64 -O2 %s -o %t
// RUN: ASAN_OPTIONS=fast_unwind_on_malloc=1 %t 2>&1 | %symbolize | FileCheck %s --check-prefix=CHECK-FAST
// RUN: ASAN_OPTIONS=fast_unwind_on_malloc=0 %t 2>&1 | %symbolize | FileCheck %s --check-prefix=CHECK-SLOW
// Test how well we unwind in presence of qsort in the stack
// (i.e. if we can unwind through a function compiled w/o frame pointers).
// https://code.google.com/p/address-sanitizer/issues/detail?id=137
+
+// Fast unwinder is only avaliable on x86_64 and i386.
+// REQUIRES: x86_64-supported-target
+
#include <stdlib.h>
#include <stdio.h>
diff --git a/lib/asan/lit_tests/malloc_delete_mismatch.cc b/lib/asan/lit_tests/Linux/malloc_delete_mismatch.cc
index f34b33a38fb3..f34b33a38fb3 100644
--- a/lib/asan/lit_tests/malloc_delete_mismatch.cc
+++ b/lib/asan/lit_tests/Linux/malloc_delete_mismatch.cc
diff --git a/lib/asan/lit_tests/Linux/overflow-in-qsort.cc b/lib/asan/lit_tests/Linux/overflow-in-qsort.cc
index c298991a8348..8bc43ca0a5c3 100644
--- a/lib/asan/lit_tests/Linux/overflow-in-qsort.cc
+++ b/lib/asan/lit_tests/Linux/overflow-in-qsort.cc
@@ -1,10 +1,14 @@
-// RUN: %clangxx_asan -O2 %s -o %t
+// RUN: %clangxx_asan -m64 -O2 %s -o %t
// RUN: ASAN_OPTIONS=fast_unwind_on_fatal=1 %t 2>&1 | %symbolize | FileCheck %s --check-prefix=CHECK-FAST
// RUN: ASAN_OPTIONS=fast_unwind_on_fatal=0 %t 2>&1 | %symbolize | FileCheck %s --check-prefix=CHECK-SLOW
// Test how well we unwind in presence of qsort in the stack
// (i.e. if we can unwind through a function compiled w/o frame pointers).
// https://code.google.com/p/address-sanitizer/issues/detail?id=137
+
+// Fast unwinder is only avaliable on x86_64 and i386.
+// REQUIRES: x86_64-supported-target
+
#include <stdlib.h>
#include <stdio.h>
diff --git a/lib/asan/lit_tests/Linux/preinit_test.cc b/lib/asan/lit_tests/Linux/preinit_test.cc
new file mode 100644
index 000000000000..28e509472c0c
--- /dev/null
+++ b/lib/asan/lit_tests/Linux/preinit_test.cc
@@ -0,0 +1,27 @@
+// RUN: %clangxx -DFUNC=zzzz %s -shared -o %t.so -fPIC
+// RUN: %clangxx_asan -DFUNC=main %s -o %t -Wl,-R. %t.so
+// RUN: %t
+
+// This test ensures that we call __asan_init early enough.
+// We build a shared library w/o asan instrumentation
+// and the binary with asan instrumentation.
+// Both files include the same header (emulated by -DFUNC here)
+// with C++ template magic which runs global initializer at library load time.
+// The function get() is instrumented with asan, but called
+// before the usual constructors are run.
+// So, we must make sure that __asan_init is executed even earlier.
+//
+// See http://gcc.gnu.org/bugzilla/show_bug.cgi?id=56393
+
+struct A {
+ int foo() const { return 0; }
+};
+A get () { return A(); }
+template <class> struct O {
+ static A const e;
+};
+template <class T> A const O <T>::e = get();
+int FUNC() {
+ return O<int>::e.foo();
+}
+
diff --git a/lib/asan/lit_tests/Linux/rlimit_mmap_test.cc b/lib/asan/lit_tests/Linux/rlimit_mmap_test.cc
index 5026e24e424d..86794756c76f 100644
--- a/lib/asan/lit_tests/Linux/rlimit_mmap_test.cc
+++ b/lib/asan/lit_tests/Linux/rlimit_mmap_test.cc
@@ -11,6 +11,6 @@ int main(int argc, char **argv) {
struct rlimit mmap_resource_limit = { 0, 0 };
assert(0 == setrlimit(RLIMIT_AS, &mmap_resource_limit));
x = malloc(10000000);
-// CHECK: AddressSanitizer is unable to mmap
+// CHECK: ERROR: Failed to mmap
return 0;
}
diff --git a/lib/asan/lit_tests/Linux/swapcontext_test.cc b/lib/asan/lit_tests/Linux/swapcontext_test.cc
index 0404b4f602bd..47a8d9891f51 100644
--- a/lib/asan/lit_tests/Linux/swapcontext_test.cc
+++ b/lib/asan/lit_tests/Linux/swapcontext_test.cc
@@ -8,6 +8,9 @@
// RUN: %clangxx_asan -m32 -O1 %s -o %t && %t 2>&1 | FileCheck %s
// RUN: %clangxx_asan -m32 -O2 %s -o %t && %t 2>&1 | FileCheck %s
// RUN: %clangxx_asan -m32 -O3 %s -o %t && %t 2>&1 | FileCheck %s
+//
+// This test is too sublte to try on non-x86 arch for now.
+// REQUIRES: x86_64-supported-target,i386-supported-target
#include <stdio.h>
#include <ucontext.h>
@@ -16,9 +19,26 @@
ucontext_t orig_context;
ucontext_t child_context;
+const int kStackSize = 1 << 20;
+
+__attribute__((noinline))
+void Throw() {
+ throw 1;
+}
+
+__attribute__((noinline))
+void ThrowAndCatch() {
+ try {
+ Throw();
+ } catch(int a) {
+ printf("ThrowAndCatch: %d\n", a);
+ }
+}
+
void Child(int mode) {
char x[32] = {0}; // Stack gets poisoned.
printf("Child: %p\n", x);
+ ThrowAndCatch(); // Simulate __asan_handle_no_return().
// (a) Do nothing, just return to parent function.
// (b) Jump into the original function. Stack remains poisoned unless we do
// something.
@@ -30,9 +50,7 @@ void Child(int mode) {
}
}
-int Run(int arg, int mode) {
- const int kStackSize = 1 << 20;
- char child_stack[kStackSize + 1];
+int Run(int arg, int mode, char *child_stack) {
printf("Child stack: %p\n", child_stack);
// Setup child context.
getcontext(&child_context);
@@ -54,13 +72,23 @@ int Run(int arg, int mode) {
}
int main(int argc, char **argv) {
+ char stack[kStackSize + 1];
// CHECK: WARNING: ASan doesn't fully support makecontext/swapcontext
int ret = 0;
- ret += Run(argc - 1, 0);
+ ret += Run(argc - 1, 0, stack);
printf("Test1 passed\n");
// CHECK: Test1 passed
- ret += Run(argc - 1, 1);
+ ret += Run(argc - 1, 1, stack);
printf("Test2 passed\n");
// CHECK: Test2 passed
+ char *heap = new char[kStackSize + 1];
+ ret += Run(argc - 1, 0, heap);
+ printf("Test3 passed\n");
+ // CHECK: Test3 passed
+ ret += Run(argc - 1, 1, heap);
+ printf("Test4 passed\n");
+ // CHECK: Test4 passed
+
+ delete [] heap;
return ret;
}
diff --git a/lib/asan/lit_tests/Linux/syscalls.cc b/lib/asan/lit_tests/Linux/syscalls.cc
new file mode 100644
index 000000000000..b2edcfb92375
--- /dev/null
+++ b/lib/asan/lit_tests/Linux/syscalls.cc
@@ -0,0 +1,22 @@
+// RUN: %clangxx_asan -m64 -O0 %s -o %t && %t 2>&1 | %symbolize | FileCheck %s
+// RUN: %clangxx_asan -m64 -O3 %s -o %t && %t 2>&1 | %symbolize | FileCheck %s
+
+#include <assert.h>
+#include <errno.h>
+#include <glob.h>
+#include <stdio.h>
+#include <string.h>
+
+#include <sanitizer/linux_syscall_hooks.h>
+
+/* Test the presence of __sanitizer_syscall_ in the tool runtime, and general
+ sanity of their behaviour. */
+
+int main(int argc, char *argv[]) {
+ char buf[1000];
+ __sanitizer_syscall_pre_recvmsg(0, buf - 1, 0);
+ // CHECK: AddressSanitizer: stack-buffer-{{.*}}erflow
+ // CHECK: READ of size {{.*}} at {{.*}} thread T0
+ // CHECK: #0 {{.*}} in __sanitizer_syscall_pre_recvmsg
+ return 0;
+}
diff --git a/lib/asan/lit_tests/Linux/time_null_regtest.cc b/lib/asan/lit_tests/Linux/time_null_regtest.cc
new file mode 100644
index 000000000000..975bca3d105a
--- /dev/null
+++ b/lib/asan/lit_tests/Linux/time_null_regtest.cc
@@ -0,0 +1,20 @@
+// RUN: %clangxx_asan -m64 -O0 %s -fsanitize-address-zero-base-shadow -pie -o %t && %t 2>&1 | %symbolize | FileCheck %s
+
+// Zero-base shadow only works on x86_64 and i386.
+// REQUIRES: x86_64-supported-target
+
+// A regression test for time(NULL), which caused ASan to crash in the
+// zero-based shadow mode on Linux.
+// FIXME: this test does not work on Darwin, because the code pages of the
+// executable interleave with the zero-based shadow.
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <time.h>
+
+int main() {
+ time_t t = time(NULL);
+ fprintf(stderr, "Time: %s\n", ctime(&t)); // NOLINT
+ // CHECK: {{Time: .* .* .*}}
+ return 0;
+}
diff --git a/lib/asan/lit_tests/Linux/zero-base-shadow.cc b/lib/asan/lit_tests/Linux/zero-base-shadow.cc
new file mode 100644
index 000000000000..682e7e8d7e59
--- /dev/null
+++ b/lib/asan/lit_tests/Linux/zero-base-shadow.cc
@@ -0,0 +1,31 @@
+// RUN: %clangxx_asan -m64 -O0 -fsanitize-address-zero-base-shadow -fPIE -pie %s -o %t && %t 2>&1 | %symbolize > %t.out
+// RUN: FileCheck %s < %t.out && FileCheck %s --check-prefix=CHECK-64 < %t.out
+// RUN: %clangxx_asan -m64 -O1 -fsanitize-address-zero-base-shadow -fPIE -pie %s -o %t && %t 2>&1 | %symbolize > %t.out
+// RUN: FileCheck %s < %t.out && FileCheck %s --check-prefix=CHECK-64 < %t.out
+// RUN: %clangxx_asan -m64 -O2 -fsanitize-address-zero-base-shadow -fPIE -pie %s -o %t && %t 2>&1 | %symbolize > %t.out
+// RUN: FileCheck %s < %t.out && FileCheck %s --check-prefix=CHECK-64 < %t.out
+// RUN: %clangxx_asan -m32 -O0 -fsanitize-address-zero-base-shadow -fPIE -pie %s -o %t && %t 2>&1 | %symbolize > %t.out
+// RUN: FileCheck %s < %t.out && FileCheck %s --check-prefix=CHECK-32 < %t.out
+// RUN: %clangxx_asan -m32 -O1 -fsanitize-address-zero-base-shadow -fPIE -pie %s -o %t && %t 2>&1 | %symbolize > %t.out
+// RUN: FileCheck %s < %t.out && FileCheck %s --check-prefix=CHECK-32 < %t.out
+// RUN: %clangxx_asan -m32 -O2 -fsanitize-address-zero-base-shadow -fPIE -pie %s -o %t && %t 2>&1 | %symbolize > %t.out
+// RUN: FileCheck %s < %t.out && FileCheck %s --check-prefix=CHECK-32 < %t.out
+
+// Zero-base shadow only works on x86_64 and i386.
+// REQUIRES: x86_64-supported-target,i386-supported-target
+
+#include <string.h>
+int main(int argc, char **argv) {
+ char x[10];
+ memset(x, 0, 10);
+ int res = x[argc * 10]; // BOOOM
+ // CHECK: {{READ of size 1 at 0x.* thread T0}}
+ // CHECK: {{ #0 0x.* in _?main .*zero-base-shadow.cc:}}[[@LINE-2]]
+ // CHECK: {{Address 0x.* is .* frame}}
+ // CHECK: main
+
+ // Check that shadow for stack memory occupies lower part of address space.
+ // CHECK-64: =>0x0f
+ // CHECK-32: =>0x1
+ return res;
+}
diff --git a/lib/asan/lit_tests/SharedLibs/darwin-dummy-shared-lib-so.cc b/lib/asan/lit_tests/SharedLibs/darwin-dummy-shared-lib-so.cc
new file mode 100644
index 000000000000..5d939991476e
--- /dev/null
+++ b/lib/asan/lit_tests/SharedLibs/darwin-dummy-shared-lib-so.cc
@@ -0,0 +1,13 @@
+//===----------- darwin-dummy-shared-lib-so.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.
+//
+//===----------------------------------------------------------------------===//
+void foo() {}
diff --git a/lib/asan/lit_tests/SharedLibs/init-order-dlopen-so.cc b/lib/asan/lit_tests/SharedLibs/init-order-dlopen-so.cc
new file mode 100644
index 000000000000..20ef2d8a00bb
--- /dev/null
+++ b/lib/asan/lit_tests/SharedLibs/init-order-dlopen-so.cc
@@ -0,0 +1,12 @@
+#include <stdio.h>
+#include <unistd.h>
+
+void inc_global();
+
+int slow_init() {
+ sleep(1);
+ inc_global();
+ return 42;
+}
+
+int slowly_init_glob = slow_init();
diff --git a/lib/asan/lit_tests/Unit/lit.cfg b/lib/asan/lit_tests/Unit/lit.cfg
index 243eb7fbeec0..e24361b014e9 100644
--- a/lib/asan/lit_tests/Unit/lit.cfg
+++ b/lib/asan/lit_tests/Unit/lit.cfg
@@ -11,9 +11,8 @@ def get_required_attr(config, attr_name):
return attr_value
# Setup attributes common for all compiler-rt projects.
-llvm_src_root = get_required_attr(config, 'llvm_src_root')
-compiler_rt_lit_unit_cfg = os.path.join(llvm_src_root, "projects",
- "compiler-rt", "lib",
+compiler_rt_src_root = get_required_attr(config, 'compiler_rt_src_root')
+compiler_rt_lit_unit_cfg = os.path.join(compiler_rt_src_root, "lib",
"lit.common.unit.cfg")
lit.load_config(config, compiler_rt_lit_unit_cfg)
diff --git a/lib/asan/lit_tests/Unit/lit.site.cfg.in b/lib/asan/lit_tests/Unit/lit.site.cfg.in
index 401c3a8cc2eb..315d24d1ed09 100644
--- a/lib/asan/lit_tests/Unit/lit.site.cfg.in
+++ b/lib/asan/lit_tests/Unit/lit.site.cfg.in
@@ -3,8 +3,15 @@
config.target_triple = "@TARGET_TRIPLE@"
config.llvm_src_root = "@LLVM_SOURCE_DIR@"
-config.build_type = "@CMAKE_BUILD_TYPE@"
+config.compiler_rt_src_root = "@COMPILER_RT_SOURCE_DIR@"
+config.llvm_build_mode = "@LLVM_BUILD_MODE@"
config.asan_binary_dir = "@ASAN_BINARY_DIR@"
+try:
+ config.llvm_build_mode = config.llvm_build_mode % lit.params
+except KeyError,e:
+ key, = e.args
+ lit.fatal("unable to find %r parameter, use '--param=%s=VALUE'" % (key, key))
+
# Let the main config do the real work.
lit.load_config(config, "@ASAN_SOURCE_DIR@/lit_tests/Unit/lit.cfg")
diff --git a/lib/asan/lit_tests/allow_user_segv.cc b/lib/asan/lit_tests/allow_user_segv.cc
new file mode 100644
index 000000000000..f8aed0d4ca80
--- /dev/null
+++ b/lib/asan/lit_tests/allow_user_segv.cc
@@ -0,0 +1,50 @@
+// Regression test for
+// https://code.google.com/p/address-sanitizer/issues/detail?id=180
+
+// RUN: %clangxx_asan -m64 -O0 %s -o %t && ASAN_OPTIONS=allow_user_segv_handler=true %t 2>&1 | FileCheck %s
+// RUN: %clangxx_asan -m64 -O2 %s -o %t && ASAN_OPTIONS=allow_user_segv_handler=true %t 2>&1 | FileCheck %s
+// RUN: %clangxx_asan -m32 -O0 %s -o %t && ASAN_OPTIONS=allow_user_segv_handler=true %t 2>&1 | FileCheck %s
+// RUN: %clangxx_asan -m32 -O2 %s -o %t && ASAN_OPTIONS=allow_user_segv_handler=true %t 2>&1 | FileCheck %s
+
+#include <signal.h>
+#include <stdio.h>
+
+struct sigaction user_sigaction;
+struct sigaction original_sigaction;
+
+void User_OnSIGSEGV(int signum, siginfo_t *siginfo, void *context) {
+ fprintf(stderr, "User sigaction called\n");
+ if (original_sigaction.sa_flags | SA_SIGINFO)
+ original_sigaction.sa_sigaction(signum, siginfo, context);
+ else
+ original_sigaction.sa_handler(signum);
+}
+
+int DoSEGV() {
+ volatile int *x = 0;
+ return *x;
+}
+
+int main() {
+ user_sigaction.sa_sigaction = User_OnSIGSEGV;
+ user_sigaction.sa_flags = SA_SIGINFO;
+#if defined(__APPLE__) && !defined(__LP64__)
+ // On 32-bit Darwin KERN_PROTECTION_FAILURE (SIGBUS) is delivered.
+ int signum = SIGBUS;
+#else
+ // On 64-bit Darwin KERN_INVALID_ADDRESS (SIGSEGV) is delivered.
+ // On Linux SIGSEGV is delivered as well.
+ int signum = SIGSEGV;
+#endif
+ if (sigaction(signum, &user_sigaction, &original_sigaction)) {
+ perror("sigaction");
+ return 1;
+ }
+ fprintf(stderr, "User sigaction installed\n");
+ return DoSEGV();
+}
+
+// CHECK: User sigaction installed
+// CHECK-NEXT: User sigaction called
+// CHECK-NEXT: ASAN:SIGSEGV
+// CHECK: AddressSanitizer: SEGV on unknown address
diff --git a/lib/asan/lit_tests/default_blacklist.cc b/lib/asan/lit_tests/default_blacklist.cc
new file mode 100644
index 000000000000..25a1ae1752b0
--- /dev/null
+++ b/lib/asan/lit_tests/default_blacklist.cc
@@ -0,0 +1,3 @@
+// Test that ASan uses the default blacklist from resource directory.
+// RUN: %clangxx_asan -### %s 2>&1 | FileCheck %s
+// CHECK: fsanitize-blacklist={{.*}}asan_blacklist.txt
diff --git a/lib/asan/lit_tests/default_options.cc b/lib/asan/lit_tests/default_options.cc
index 950a7d879194..84b80557b852 100644
--- a/lib/asan/lit_tests/default_options.cc
+++ b/lib/asan/lit_tests/default_options.cc
@@ -4,7 +4,7 @@
const char *kAsanDefaultOptions="verbosity=1 foo=bar";
extern "C"
-__attribute__((no_address_safety_analysis))
+__attribute__((no_sanitize_address))
const char *__asan_default_options() {
// CHECK: Using the defaults from __asan_default_options: {{.*}} foo=bar
return kAsanDefaultOptions;
diff --git a/lib/asan/lit_tests/dlclose-test.cc b/lib/asan/lit_tests/dlclose-test.cc
index 229f508294bf..b15895bf3579 100644
--- a/lib/asan/lit_tests/dlclose-test.cc
+++ b/lib/asan/lit_tests/dlclose-test.cc
@@ -9,6 +9,11 @@
// are globals.
// 6. BOOM
+// This sublte test assumes that after a foo.so is dlclose-d
+// we can mmap the region of memory that has been occupied by the library.
+// It works on i368/x86_64 Linux, but not necessary anywhere else.
+// REQUIRES: x86_64-supported-target,i386-supported-target
+
// RUN: %clangxx_asan -m64 -O0 %p/SharedLibs/dlclose-test-so.cc \
// RUN: -fPIC -shared -o %t-so.so
// RUN: %clangxx_asan -m64 -O0 %s -o %t && %t 2>&1 | FileCheck %s
@@ -39,17 +44,17 @@
#include <stdio.h>
#include <string.h>
#include <sys/mman.h>
+#include <unistd.h>
#include <string>
using std::string;
-static const int kPageSize = 4096;
-
typedef int *(fun_t)();
int main(int argc, char *argv[]) {
string path = string(argv[0]) + "-so.so";
+ size_t PageSize = sysconf(_SC_PAGESIZE);
printf("opening %s ... \n", path.c_str());
void *lib = dlopen(path.c_str(), RTLD_NOW);
if (!lib) {
@@ -73,8 +78,8 @@ int main(int argc, char *argv[]) {
return 1;
}
// Now, the page where 'addr' is unmapped. Map it.
- size_t page_beg = ((size_t)addr) & ~(kPageSize - 1);
- void *res = mmap((void*)(page_beg), kPageSize,
+ size_t page_beg = ((size_t)addr) & ~(PageSize - 1);
+ void *res = mmap((void*)(page_beg), PageSize,
PROT_READ | PROT_WRITE,
MAP_PRIVATE | MAP_ANON | MAP_FIXED | MAP_NORESERVE, 0, 0);
if (res == (char*)-1L) {
diff --git a/lib/asan/lit_tests/double-free.cc b/lib/asan/lit_tests/double-free.cc
new file mode 100644
index 000000000000..9e201117c563
--- /dev/null
+++ b/lib/asan/lit_tests/double-free.cc
@@ -0,0 +1,18 @@
+// RUN: %clangxx_asan -m64 -O0 %s -o %t && %t 2>&1 | %symbolize | FileCheck %s
+
+#include <stdlib.h>
+#include <string.h>
+int main(int argc, char **argv) {
+ char *x = (char*)malloc(10 * sizeof(char));
+ memset(x, 0, 10);
+ int res = x[argc];
+ free(x);
+ free(x + argc - 1); // BOOM
+ // CHECK: AddressSanitizer: attempting double-free{{.*}}in thread T0
+ // CHECK: double-free.cc:[[@LINE-2]]
+ // CHECK: freed by thread T0 here:
+ // CHECK: double-free.cc:[[@LINE-5]]
+ // CHECK: allocated by thread T0 here:
+ // CHECK: double-free.cc:[[@LINE-10]]
+ return res;
+}
diff --git a/lib/asan/lit_tests/global-demangle.cc b/lib/asan/lit_tests/global-demangle.cc
new file mode 100644
index 000000000000..5696a38a7705
--- /dev/null
+++ b/lib/asan/lit_tests/global-demangle.cc
@@ -0,0 +1,18 @@
+// Don't run through %symbolize to avoid c++filt demangling.
+// RUN: %clangxx_asan -m64 -O0 %s -o %t && %t 2>&1 | FileCheck %s
+
+namespace XXX {
+class YYY {
+ public:
+ static char ZZZ[];
+};
+char YYY::ZZZ[] = "abc";
+}
+
+int main(int argc, char **argv) {
+ return (int)XXX::YYY::ZZZ[argc + 5]; // BOOM
+ // CHECK: {{READ of size 1 at 0x.*}}
+ // CHECK: {{0x.* is located 2 bytes to the right of global variable}}
+ // CHECK: 'XXX::YYY::ZZZ' {{.*}} of size 4
+ // CHECK: 'XXX::YYY::ZZZ' is ascii string 'abc'
+}
diff --git a/lib/asan/lit_tests/heap-overflow.cc b/lib/asan/lit_tests/heap-overflow.cc
index 2648ec7e5f1f..f1d719cd0b20 100644
--- a/lib/asan/lit_tests/heap-overflow.cc
+++ b/lib/asan/lit_tests/heap-overflow.cc
@@ -29,10 +29,8 @@ int main(int argc, char **argv) {
// CHECK-Linux: {{ #0 0x.* in .*malloc}}
// CHECK-Linux: {{ #1 0x.* in main .*heap-overflow.cc:21}}
- // CHECK-Darwin: {{ #0 0x.* in .*mz_malloc.*}}
- // CHECK-Darwin: {{ #1 0x.* in malloc_zone_malloc.*}}
- // CHECK-Darwin: {{ #2 0x.* in malloc.*}}
- // CHECK-Darwin: {{ #3 0x.* in _?main .*heap-overflow.cc:21}}
+ // CHECK-Darwin: {{ #0 0x.* in _?wrap_malloc.*}}
+ // CHECK-Darwin: {{ #1 0x.* in _?main .*heap-overflow.cc:21}}
free(x);
return res;
}
diff --git a/lib/asan/lit_tests/huge_negative_hea_oob.cc b/lib/asan/lit_tests/huge_negative_hea_oob.cc
new file mode 100644
index 000000000000..a09e3bf87d60
--- /dev/null
+++ b/lib/asan/lit_tests/huge_negative_hea_oob.cc
@@ -0,0 +1,13 @@
+// RUN: %clangxx_asan -m64 %s -o %t && %t 2>&1 | FileCheck %s
+// RUN: %clangxx_asan -m64 -O %s -o %t && %t 2>&1 | FileCheck %s
+// Check that we can find huge buffer overflows to the left.
+#include <stdlib.h>
+#include <string.h>
+int main(int argc, char **argv) {
+ char *x = (char*)malloc(1 << 20);
+ memset(x, 0, 10);
+ int res = x[-argc * 4000]; // BOOOM
+ // CHECK: is located 4000 bytes to the left of
+ free(x);
+ return res;
+}
diff --git a/lib/asan/lit_tests/init-order-atexit.cc b/lib/asan/lit_tests/init-order-atexit.cc
new file mode 100644
index 000000000000..45f4f17c0cb0
--- /dev/null
+++ b/lib/asan/lit_tests/init-order-atexit.cc
@@ -0,0 +1,31 @@
+// Test for the following situation:
+// (1) global A is constructed.
+// (2) exit() is called during construction of global B.
+// (3) destructor of A reads uninitialized global C from another module.
+// We do *not* want to report init-order bug in this case.
+
+// RUN: %clangxx_asan -m64 -O0 %s %p/Helpers/init-order-atexit-extra.cc -o %t
+// RUN: ASAN_OPTIONS=check_initialization_order=true:strict_init_order=true %t 2>&1 | FileCheck %s
+
+#include <stdio.h>
+#include <stdlib.h>
+
+void AccessC();
+
+class A {
+ public:
+ A() { }
+ ~A() { AccessC(); printf("PASSED\n"); }
+ // CHECK-NOT: AddressSanitizer
+ // CHECK: PASSED
+};
+
+A a;
+
+class B {
+ public:
+ B() { exit(1); }
+ ~B() { }
+};
+
+B b;
diff --git a/lib/asan/lit_tests/init-order-dlopen.cc b/lib/asan/lit_tests/init-order-dlopen.cc
new file mode 100644
index 000000000000..228f44204c99
--- /dev/null
+++ b/lib/asan/lit_tests/init-order-dlopen.cc
@@ -0,0 +1,52 @@
+// Regression test for
+// https://code.google.com/p/address-sanitizer/issues/detail?id=178
+
+// RUN: %clangxx_asan -m64 -O0 %p/SharedLibs/init-order-dlopen-so.cc \
+// RUN: -fPIC -shared -o %t-so.so
+// If the linker doesn't support --export-dynamic (which is ELF-specific),
+// try to link without that option.
+// FIXME: find a better solution.
+// RUN: %clangxx_asan -m64 -O0 %s -o %t -Wl,--export-dynamic || \
+// RUN: %clangxx_asan -m64 -O0 %s -o %t
+// RUN: ASAN_OPTIONS=check_initialization_order=true:strict_init_order=true %t 2>&1 | FileCheck %s
+#include <dlfcn.h>
+#include <pthread.h>
+#include <stdio.h>
+#include <unistd.h>
+
+#include <string>
+
+using std::string;
+
+int foo() {
+ return 42;
+}
+int global = foo();
+
+__attribute__((visibility("default")))
+void inc_global() {
+ global++;
+}
+
+void *global_poller(void *arg) {
+ while (true) {
+ if (global != 42)
+ break;
+ usleep(100);
+ }
+ return 0;
+}
+
+int main(int argc, char *argv[]) {
+ pthread_t p;
+ pthread_create(&p, 0, global_poller, 0);
+ string path = string(argv[0]) + "-so.so";
+ if (0 == dlopen(path.c_str(), RTLD_NOW)) {
+ fprintf(stderr, "dlerror: %s\n", dlerror());
+ return 1;
+ }
+ pthread_join(p, 0);
+ printf("PASSED\n");
+ // CHECK: PASSED
+ return 0;
+}
diff --git a/lib/asan/lit_tests/initialization-blacklist.cc b/lib/asan/lit_tests/initialization-blacklist.cc
index f8df24c68ea6..12fbc49ed91b 100644
--- a/lib/asan/lit_tests/initialization-blacklist.cc
+++ b/lib/asan/lit_tests/initialization-blacklist.cc
@@ -1,23 +1,35 @@
// Test for blacklist functionality of initialization-order checker.
// RUN: %clangxx_asan -m64 -O0 %s %p/Helpers/initialization-blacklist-extra.cc\
+// RUN: %p/Helpers/initialization-blacklist-extra2.cc \
// RUN: -fsanitize-blacklist=%p/Helpers/initialization-blacklist.txt \
-// RUN: -fsanitize=init-order -o %t && %t 2>&1
+// RUN: -fsanitize=init-order -o %t
+// RUN: ASAN_OPTIONS=check_initialization_order=true %t 2>&1
// RUN: %clangxx_asan -m64 -O1 %s %p/Helpers/initialization-blacklist-extra.cc\
+// RUN: %p/Helpers/initialization-blacklist-extra2.cc \
// RUN: -fsanitize-blacklist=%p/Helpers/initialization-blacklist.txt \
-// RUN: -fsanitize=init-order -o %t && %t 2>&1
+// RUN: -fsanitize=init-order -o %t
+// RUN: ASAN_OPTIONS=check_initialization_order=true %t 2>&1
// RUN: %clangxx_asan -m64 -O2 %s %p/Helpers/initialization-blacklist-extra.cc\
+// RUN: %p/Helpers/initialization-blacklist-extra2.cc \
// RUN: -fsanitize-blacklist=%p/Helpers/initialization-blacklist.txt \
-// RUN: -fsanitize=init-order -o %t && %t 2>&1
+// RUN: -fsanitize=init-order -o %t
+// RUN: ASAN_OPTIONS=check_initialization_order=true %t 2>&1
// RUN: %clangxx_asan -m32 -O0 %s %p/Helpers/initialization-blacklist-extra.cc\
+// RUN: %p/Helpers/initialization-blacklist-extra2.cc \
// RUN: -fsanitize-blacklist=%p/Helpers/initialization-blacklist.txt \
-// RUN: -fsanitize=init-order -o %t && %t 2>&1
+// RUN: -fsanitize=init-order -o %t
+// RUN: ASAN_OPTIONS=check_initialization_order=true %t 2>&1
// RUN: %clangxx_asan -m32 -O1 %s %p/Helpers/initialization-blacklist-extra.cc\
+// RUN: %p/Helpers/initialization-blacklist-extra2.cc \
// RUN: -fsanitize-blacklist=%p/Helpers/initialization-blacklist.txt \
-// RUN: -fsanitize=init-order -o %t && %t 2>&1
+// RUN: -fsanitize=init-order -o %t
+// RUN: ASAN_OPTIONS=check_initialization_order=true %t 2>&1
// RUN: %clangxx_asan -m32 -O2 %s %p/Helpers/initialization-blacklist-extra.cc\
+// RUN: %p/Helpers/initialization-blacklist-extra2.cc \
// RUN: -fsanitize-blacklist=%p/Helpers/initialization-blacklist.txt \
-// RUN: -fsanitize=init-order -o %t && %t 2>&1
+// RUN: -fsanitize=init-order -o %t
+// RUN: ASAN_OPTIONS=check_initialization_order=true %t 2>&1
// Function is defined in another TU.
int readBadGlobal();
@@ -27,6 +39,9 @@ int x = readBadGlobal(); // init-order bug.
int accessBadObject();
int y = accessBadObject(); // init-order bug.
+int readBadSrcGlobal();
+int z = readBadSrcGlobal(); // init-order bug.
+
int main(int argc, char **argv) {
- return argc + x + y - 1;
+ return argc + x + y + z - 1;
}
diff --git a/lib/asan/lit_tests/initialization-bug.cc b/lib/asan/lit_tests/initialization-bug.cc
index 8f4e33ef5a35..ee2c725f0b13 100644
--- a/lib/asan/lit_tests/initialization-bug.cc
+++ b/lib/asan/lit_tests/initialization-bug.cc
@@ -1,14 +1,17 @@
// Test to make sure basic initialization order errors are caught.
-// RUN: %clangxx_asan -m64 -O0 %s %p/Helpers/initialization-bug-extra2.cc\
-// RUN: -fsanitize=init-order -o %t && %t 2>&1 \
+// RUN: %clangxx_asan -m64 -O0 %s %p/Helpers/initialization-bug-extra2.cc -o %t
+// RUN: ASAN_OPTIONS=check_initialization_order=true %t 2>&1 \
// RUN: | %symbolize | FileCheck %s
-// RUN: %clangxx_asan -m32 -O0 %s %p/Helpers/initialization-bug-extra2.cc\
-// RUN: -fsanitize=init-order -o %t && %t 2>&1 \
+// RUN: %clangxx_asan -m32 -O0 %s %p/Helpers/initialization-bug-extra2.cc -o %t
+// RUN: ASAN_OPTIONS=check_initialization_order=true %t 2>&1 \
// RUN: | %symbolize | FileCheck %s
// Do not test with optimization -- the error may be optimized away.
+// FIXME: https://code.google.com/p/address-sanitizer/issues/detail?id=186
+// XFAIL: darwin
+
#include <cstdio>
// The structure of the test is:
diff --git a/lib/asan/lit_tests/initialization-constexpr.cc b/lib/asan/lit_tests/initialization-constexpr.cc
new file mode 100644
index 000000000000..ba5410674f76
--- /dev/null
+++ b/lib/asan/lit_tests/initialization-constexpr.cc
@@ -0,0 +1,43 @@
+// Constexpr:
+// We need to check that a global variable initialized with a constexpr
+// constructor can be accessed during dynamic initialization (as a constexpr
+// constructor implies that it was initialized during constant initialization,
+// not dynamic initialization).
+
+// RUN: %clangxx_asan -m64 -O0 %s %p/Helpers/initialization-constexpr-extra.cc\
+// RUN: --std=c++11 -fsanitize=init-order -o %t
+// RUN: ASAN_OPTIONS=check_initialization_order=true %t 2>&1
+// RUN: %clangxx_asan -m64 -O1 %s %p/Helpers/initialization-constexpr-extra.cc\
+// RUN: --std=c++11 -fsanitize=init-order -o %t
+// RUN: ASAN_OPTIONS=check_initialization_order=true %t 2>&1
+// RUN: %clangxx_asan -m64 -O2 %s %p/Helpers/initialization-constexpr-extra.cc\
+// RUN: --std=c++11 -fsanitize=init-order -o %t
+// RUN: ASAN_OPTIONS=check_initialization_order=true %t 2>&1
+// RUN: %clangxx_asan -m64 -O3 %s %p/Helpers/initialization-constexpr-extra.cc\
+// RUN: --std=c++11 -fsanitize=init-order -o %t
+// RUN: ASAN_OPTIONS=check_initialization_order=true %t 2>&1
+// RUN: %clangxx_asan -m32 -O0 %s %p/Helpers/initialization-constexpr-extra.cc\
+// RUN: --std=c++11 -fsanitize=init-order -o %t
+// RUN: ASAN_OPTIONS=check_initialization_order=true %t 2>&1
+// RUN: %clangxx_asan -m32 -O1 %s %p/Helpers/initialization-constexpr-extra.cc\
+// RUN: --std=c++11 -fsanitize=init-order -o %t
+// RUN: ASAN_OPTIONS=check_initialization_order=true %t 2>&1
+// RUN: %clangxx_asan -m32 -O2 %s %p/Helpers/initialization-constexpr-extra.cc\
+// RUN: --std=c++11 -fsanitize=init-order -o %t
+// RUN: ASAN_OPTIONS=check_initialization_order=true %t 2>&1
+// RUN: %clangxx_asan -m32 -O3 %s %p/Helpers/initialization-constexpr-extra.cc\
+// RUN: --std=c++11 -fsanitize=init-order -o %t
+// RUN: ASAN_OPTIONS=check_initialization_order=true %t 2>&1
+
+class Integer {
+ private:
+ int value;
+
+ public:
+ constexpr Integer(int x = 0) : value(x) {}
+ int getValue() {return value;}
+};
+Integer coolestInteger(42);
+int getCoolestInteger() { return coolestInteger.getValue(); }
+
+int main() { return 0; }
diff --git a/lib/asan/lit_tests/initialization-nobug.cc b/lib/asan/lit_tests/initialization-nobug.cc
index 1b8961606811..407226e29a1b 100644
--- a/lib/asan/lit_tests/initialization-nobug.cc
+++ b/lib/asan/lit_tests/initialization-nobug.cc
@@ -1,24 +1,22 @@
// A collection of various initializers which shouldn't trip up initialization
// order checking. If successful, this will just return 0.
-// RUN: %clangxx_asan -m64 -O0 %s %p/Helpers/initialization-nobug-extra.cc\
-// RUN: --std=c++11 -fsanitize=init-order -o %t && %t 2>&1
-// RUN: %clangxx_asan -m64 -O1 %s %p/Helpers/initialization-nobug-extra.cc\
-// RUN: --std=c++11 -fsanitize=init-order -o %t && %t 2>&1
-// RUN: %clangxx_asan -m64 -O2 %s %p/Helpers/initialization-nobug-extra.cc\
-// RUN: --std=c++11 -fsanitize=init-order -o %t && %t 2>&1
-// RUN: %clangxx_asan -m64 -O3 %s %p/Helpers/initialization-nobug-extra.cc\
-// RUN: --std=c++11 -fsanitize=init-order -o %t && %t 2>&1
-// RUN: %clangxx_asan -m32 -O0 %s %p/Helpers/initialization-nobug-extra.cc\
-// RUN: --std=c++11 -fsanitize=init-order -o %t && %t 2>&1
-// RUN: %clangxx_asan -m32 -O0 %s %p/Helpers/initialization-nobug-extra.cc\
-// RUN: --std=c++11 -fsanitize=init-order -o %t && %t 2>&1
-// RUN: %clangxx_asan -m32 -O1 %s %p/Helpers/initialization-nobug-extra.cc\
-// RUN: --std=c++11 -fsanitize=init-order -o %t && %t 2>&1
-// RUN: %clangxx_asan -m32 -O2 %s %p/Helpers/initialization-nobug-extra.cc\
-// RUN: --std=c++11 -fsanitize=init-order -o %t && %t 2>&1
-// RUN: %clangxx_asan -m32 -O3 %s %p/Helpers/initialization-nobug-extra.cc\
-// RUN: --std=c++11 -fsanitize=init-order -o %t && %t 2>&1
+// RUN: %clangxx_asan -m64 -O0 %s %p/Helpers/initialization-nobug-extra.cc -fsanitize=init-order -o %t
+// RUN: ASAN_OPTIONS=check_initialization_order=true %t 2>&1
+// RUN: %clangxx_asan -m64 -O1 %s %p/Helpers/initialization-nobug-extra.cc -fsanitize=init-order -o %t
+// RUN: ASAN_OPTIONS=check_initialization_order=true %t 2>&1
+// RUN: %clangxx_asan -m64 -O2 %s %p/Helpers/initialization-nobug-extra.cc -fsanitize=init-order -o %t
+// RUN: ASAN_OPTIONS=check_initialization_order=true %t 2>&1
+// RUN: %clangxx_asan -m64 -O3 %s %p/Helpers/initialization-nobug-extra.cc -fsanitize=init-order -o %t
+// RUN: ASAN_OPTIONS=check_initialization_order=true %t 2>&1
+// RUN: %clangxx_asan -m32 -O0 %s %p/Helpers/initialization-nobug-extra.cc -fsanitize=init-order -o %t
+// RUN: ASAN_OPTIONS=check_initialization_order=true %t 2>&1
+// RUN: %clangxx_asan -m32 -O1 %s %p/Helpers/initialization-nobug-extra.cc -fsanitize=init-order -o %t
+// RUN: ASAN_OPTIONS=check_initialization_order=true %t 2>&1
+// RUN: %clangxx_asan -m32 -O2 %s %p/Helpers/initialization-nobug-extra.cc -fsanitize=init-order -o %t
+// RUN: ASAN_OPTIONS=check_initialization_order=true %t 2>&1
+// RUN: %clangxx_asan -m32 -O3 %s %p/Helpers/initialization-nobug-extra.cc -fsanitize=init-order -o %t
+// RUN: ASAN_OPTIONS=check_initialization_order=true %t 2>&1
// Simple access:
// Make sure that accessing a global in the same TU is safe
@@ -47,21 +45,12 @@ int countCalls() {
return ++calls;
}
-// Constexpr:
-// We need to check that a global variable initialized with a constexpr
-// constructor can be accessed during dynamic initialization (as a constexpr
-// constructor implies that it was initialized during constant initialization,
-// not dynamic initialization).
-
-class Integer {
- private:
+// Trivial constructor, non-trivial destructor.
+struct StructWithDtor {
+ ~StructWithDtor() { }
int value;
-
- public:
- constexpr Integer(int x = 0) : value(x) {}
- int getValue() {return value;}
};
-Integer coolestInteger(42);
-int getCoolestInteger() { return coolestInteger.getValue(); }
+StructWithDtor struct_with_dtor;
+int getStructWithDtorValue() { return struct_with_dtor.value; }
int main() { return 0; }
diff --git a/lib/asan/lit_tests/interface_test.cc b/lib/asan/lit_tests/interface_test.cc
new file mode 100644
index 000000000000..428a109fe70d
--- /dev/null
+++ b/lib/asan/lit_tests/interface_test.cc
@@ -0,0 +1,8 @@
+// Check that user may include ASan interface header.
+// RUN: %clang -fsanitize=address -I %p/../../../include %s -o %t && %t
+// RUN: %clang -I %p/../../../include %s -o %t && %t
+#include <sanitizer/asan_interface.h>
+
+int main() {
+ return 0;
+}
diff --git a/lib/asan/lit_tests/invalid-free.cc b/lib/asan/lit_tests/invalid-free.cc
new file mode 100644
index 000000000000..0ef064056b63
--- /dev/null
+++ b/lib/asan/lit_tests/invalid-free.cc
@@ -0,0 +1,16 @@
+// RUN: %clangxx_asan -m64 -O0 %s -o %t && %t 2>&1 | %symbolize | FileCheck %s
+
+#include <stdlib.h>
+#include <string.h>
+int main(int argc, char **argv) {
+ char *x = (char*)malloc(10 * sizeof(char));
+ memset(x, 0, 10);
+ int res = x[argc];
+ free(x + 5); // BOOM
+ // CHECK: AddressSanitizer: attempting free on address{{.*}}in thread T0
+ // CHECK: invalid-free.cc:[[@LINE-2]]
+ // CHECK: is located 5 bytes inside of 10-byte region
+ // CHECK: allocated by thread T0 here:
+ // CHECK: invalid-free.cc:[[@LINE-8]]
+ return res;
+}
diff --git a/lib/asan/lit_tests/large_func_test.cc b/lib/asan/lit_tests/large_func_test.cc
index a74828811f74..ceecc29b7b0a 100644
--- a/lib/asan/lit_tests/large_func_test.cc
+++ b/lib/asan/lit_tests/large_func_test.cc
@@ -32,7 +32,7 @@ static void LargeFunction(int *x, int zero) {
// CHECK: {{.*ERROR: AddressSanitizer: heap-buffer-overflow on address}}
// CHECK: {{0x.* at pc 0x.* bp 0x.* sp 0x.*}}
// CHECK: {{READ of size 4 at 0x.* thread T0}}
- x[zero + 111]++; // we should report this exact line
+ x[zero + 103]++; // we should report this exact line
// atos incorrectly extracts the symbol name for the static functions on
// Darwin.
// CHECK-Linux: {{#0 0x.* in LargeFunction.*large_func_test.cc:}}[[@LINE-3]]
@@ -54,9 +54,10 @@ int main(int argc, char **argv) {
int *x = new int[100];
LargeFunction(x, argc - 1);
// CHECK: {{ #1 0x.* in _?main .*large_func_test.cc:}}[[@LINE-1]]
- // CHECK: {{0x.* is located 44 bytes to the right of 400-byte region}}
+ // CHECK: {{0x.* is located 12 bytes to the right of 400-byte region}}
// CHECK: {{allocated by thread T0 here:}}
- // CHECK: {{ #0 0x.* in operator new.*}}
- // CHECK: {{ #1 0x.* in _?main .*large_func_test.cc:}}[[@LINE-6]]
+ // CHECK-Linux: {{ #0 0x.* in operator new.*}}
+ // CHECK-Darwin: {{ #0 0x.* in .*_Zna.*}}
+ // CHECK: {{ #1 0x.* in _?main .*large_func_test.cc:}}[[@LINE-7]]
delete x;
}
diff --git a/lib/asan/lit_tests/lit.cfg b/lib/asan/lit_tests/lit.cfg
index 7875281b1f2f..5daecd9e557d 100644
--- a/lib/asan/lit_tests/lit.cfg
+++ b/lib/asan/lit_tests/lit.cfg
@@ -2,6 +2,14 @@
import os
+def get_required_attr(config, attr_name):
+ attr_value = getattr(config, attr_name, None)
+ if not attr_value:
+ lit.fatal("No attribute %r in test configuration! You may need to run "
+ "tests from your build directory or add this attribute "
+ "to lit.site.cfg " % attr_name)
+ return attr_value
+
# Setup config name.
config.name = 'AddressSanitizer'
@@ -30,14 +38,6 @@ if llvm_src_root is None:
if not llvm_config:
DisplayNoConfigMessage()
- # Validate that llvm-config points to the same source tree.
- llvm_src_root = lit.util.capture(["llvm-config", "--src-root"]).strip()
- asan_test_src_root = os.path.join(llvm_src_root, "projects", "compiler-rt",
- "lib", "asan", "lit_tests")
- if (os.path.realpath(asan_test_src_root) !=
- os.path.realpath(config.test_source_root)):
- DisplayNoConfigMessage()
-
# Find out the presumed location of generated site config.
llvm_obj_root = lit.util.capture(["llvm-config", "--obj-root"]).strip()
asan_site_cfg = os.path.join(llvm_obj_root, "projects", "compiler-rt",
@@ -49,8 +49,9 @@ if llvm_src_root is None:
raise SystemExit
# Setup attributes common for all compiler-rt projects.
-compiler_rt_lit_cfg = os.path.join(llvm_src_root, "projects", "compiler-rt",
- "lib", "lit.common.cfg")
+compiler_rt_src_root = get_required_attr(config, "compiler_rt_src_root")
+compiler_rt_lit_cfg = os.path.join(compiler_rt_src_root, "lib",
+ "lit.common.cfg")
if (not compiler_rt_lit_cfg) or (not os.path.exists(compiler_rt_lit_cfg)):
lit.fatal("Can't find common compiler-rt lit config at: %r"
% compiler_rt_lit_cfg)
diff --git a/lib/asan/lit_tests/lit.site.cfg.in b/lib/asan/lit_tests/lit.site.cfg.in
index cf439309c6ad..08546cdabe02 100644
--- a/lib/asan/lit_tests/lit.site.cfg.in
+++ b/lib/asan/lit_tests/lit.site.cfg.in
@@ -5,8 +5,10 @@ config.target_triple = "@TARGET_TRIPLE@"
config.host_os = "@HOST_OS@"
config.llvm_src_root = "@LLVM_SOURCE_DIR@"
config.llvm_obj_root = "@LLVM_BINARY_DIR@"
+config.compiler_rt_src_root = "@COMPILER_RT_SOURCE_DIR@"
config.llvm_tools_dir = "@LLVM_TOOLS_DIR@"
config.clang = "@LLVM_BINARY_DIR@/bin/clang"
+config.compiler_rt_arch = "@COMPILER_RT_SUPPORTED_ARCH@"
# LLVM tools dir can be passed in lit parameters, so try to
# apply substitution.
diff --git a/lib/asan/lit_tests/log_path_fork_test.cc b/lib/asan/lit_tests/log_path_fork_test.cc.disabled
index c6c1b49e994d..c6c1b49e994d 100644
--- a/lib/asan/lit_tests/log_path_fork_test.cc
+++ b/lib/asan/lit_tests/log_path_fork_test.cc.disabled
diff --git a/lib/asan/lit_tests/malloc_fill.cc b/lib/asan/lit_tests/malloc_fill.cc
new file mode 100644
index 000000000000..c23516b33299
--- /dev/null
+++ b/lib/asan/lit_tests/malloc_fill.cc
@@ -0,0 +1,22 @@
+// Check that we fill malloc-ed memory correctly.
+// RUN: %clangxx_asan -m64 %s -o %t
+// RUN: %t | FileCheck %s
+// RUN: ASAN_OPTIONS=max_malloc_fill_size=10:malloc_fill_byte=8 %t | FileCheck %s --check-prefix=CHECK-10-8
+// RUN: ASAN_OPTIONS=max_malloc_fill_size=20:malloc_fill_byte=171 %t | FileCheck %s --check-prefix=CHECK-20-ab
+
+#include <stdio.h>
+int main(int argc, char **argv) {
+ // With asan allocator this makes sure we get memory from mmap.
+ static const int kSize = 1 << 25;
+ unsigned char *x = new unsigned char[kSize];
+ printf("-");
+ for (int i = 0; i <= 32; i++) {
+ printf("%02x", x[i]);
+ }
+ printf("-\n");
+ delete [] x;
+}
+
+// CHECK: -bebebebebebebebebebebebebebebebebebebebebebebebebebebebebebebebebe-
+// CHECK-10-8: -080808080808080808080000000000000000000000000000000000000000000000-
+// CHECK-20-ab: -abababababababababababababababababababab00000000000000000000000000-
diff --git a/lib/asan/lit_tests/memcmp_strict_test.cc b/lib/asan/lit_tests/memcmp_strict_test.cc
new file mode 100644
index 000000000000..00bf921c744a
--- /dev/null
+++ b/lib/asan/lit_tests/memcmp_strict_test.cc
@@ -0,0 +1,16 @@
+// RUN: %clangxx_asan -m64 -O0 %s -o %t && ASAN_OPTIONS=strict_memcmp=0 %t 2>&1 | %symbolize | FileCheck %s --check-prefix=CHECK-nonstrict
+// RUN: %clangxx_asan -m64 -O0 %s -o %t && ASAN_OPTIONS=strict_memcmp=1 %t 2>&1 | %symbolize | FileCheck %s --check-prefix=CHECK-strict
+// Default to strict_memcmp=1.
+// RUN: %clangxx_asan -m64 -O0 %s -o %t && %t 2>&1 | %symbolize | FileCheck %s --check-prefix=CHECK-strict
+
+#include <stdio.h>
+#include <string.h>
+int main() {
+ char kFoo[] = "foo";
+ char kFubar[] = "fubar";
+ int res = memcmp(kFoo, kFubar, strlen(kFubar));
+ printf("res: %d\n", res);
+ // CHECK-nonstrict: {{res: -1}}
+ // CHECK-strict: AddressSanitizer: stack-buffer-overflow
+ return 0;
+}
diff --git a/lib/asan/lit_tests/partial_right.cc b/lib/asan/lit_tests/partial_right.cc
new file mode 100644
index 000000000000..c579262726f9
--- /dev/null
+++ b/lib/asan/lit_tests/partial_right.cc
@@ -0,0 +1,17 @@
+// RUN: %clangxx_asan -m64 -O0 %s -o %t && %t 2>&1 | %symbolize | FileCheck %s
+// RUN: %clangxx_asan -m64 -O1 %s -o %t && %t 2>&1 | %symbolize | FileCheck %s
+// RUN: %clangxx_asan -m64 -O2 %s -o %t && %t 2>&1 | %symbolize | FileCheck %s
+// RUN: %clangxx_asan -m64 -O3 %s -o %t && %t 2>&1 | %symbolize | FileCheck %s
+// RUN: %clangxx_asan -m32 -O0 %s -o %t && %t 2>&1 | %symbolize | FileCheck %s
+// RUN: %clangxx_asan -m32 -O1 %s -o %t && %t 2>&1 | %symbolize | FileCheck %s
+// RUN: %clangxx_asan -m32 -O2 %s -o %t && %t 2>&1 | %symbolize | FileCheck %s
+// RUN: %clangxx_asan -m32 -O3 %s -o %t && %t 2>&1 | %symbolize | FileCheck %s
+
+#include <stdlib.h>
+int main(int argc, char **argv) {
+ volatile int *x = (int*)malloc(2*sizeof(int) + 2);
+ int res = x[2]; // BOOOM
+ // CHECK: {{READ of size 4 at 0x.* thread T0}}
+ // CHECK: [[ADDR:0x[01-9a-fa-f]+]] is located 0 bytes to the right of {{.*}}-byte region [{{.*}},{{.*}}[[ADDR]])
+ return res;
+}
diff --git a/lib/asan/lit_tests/stack-frame-demangle.cc b/lib/asan/lit_tests/stack-frame-demangle.cc
index 7f4d59fc5838..bb8de16b2b8a 100644
--- a/lib/asan/lit_tests/stack-frame-demangle.cc
+++ b/lib/asan/lit_tests/stack-frame-demangle.cc
@@ -1,7 +1,4 @@
-// Check that ASan is able to print demangled frame name even w/o
-// symbolization.
-
-// RUN: %clangxx_asan -m64 -O0 %s -o %t && %t 2>&1 | FileCheck %s
+// RUN: %clangxx_asan -m64 -O0 %s -o %t && %t 2>&1 | %symbolize | FileCheck %s
#include <string.h>
@@ -11,12 +8,13 @@ struct YYY {
char array[10];
memset(array, 0, 10);
return array[x]; // BOOOM
- // CHECK: {{ERROR: AddressSanitizer: stack-buffer-overflow}}
- // CHECK: {{READ of size 1 at 0x.* thread T0}}
- // CHECK: {{Address 0x.* is .* frame <XXX::YYY::ZZZ(.*)>}}
+ // CHECK: ERROR: AddressSanitizer: stack-buffer-overflow
+ // CHECK: READ of size 1 at
+ // CHECK: is located in stack of thread T0 at offset
+ // CHECK: XXX::YYY::ZZZ
}
};
-};
+} // namespace XXX
int main(int argc, char **argv) {
int res = XXX::YYY::ZZZ(argc + 10);
diff --git a/lib/asan/lit_tests/stack-oob-frames.cc b/lib/asan/lit_tests/stack-oob-frames.cc
new file mode 100644
index 000000000000..0395522252e8
--- /dev/null
+++ b/lib/asan/lit_tests/stack-oob-frames.cc
@@ -0,0 +1,59 @@
+// RUN: %clangxx_asan -m64 -O1 %s -o %t
+// RUN: %t 0 2>&1 | %symbolize | FileCheck %s --check-prefix=CHECK0
+// RUN: %t 1 2>&1 | %symbolize | FileCheck %s --check-prefix=CHECK1
+// RUN: %t 2 2>&1 | %symbolize | FileCheck %s --check-prefix=CHECK2
+// RUN: %t 3 2>&1 | %symbolize | FileCheck %s --check-prefix=CHECK3
+
+#define NOINLINE __attribute__((noinline))
+inline void break_optimization(void *arg) {
+ __asm__ __volatile__("" : : "r" (arg) : "memory");
+}
+
+NOINLINE static void Frame0(int frame, char *a, char *b, char *c) {
+ char s[4] = {0};
+ char *d = s;
+ break_optimization(&d);
+ switch (frame) {
+ case 3: a[5]++; break;
+ case 2: b[5]++; break;
+ case 1: c[5]++; break;
+ case 0: d[5]++; break;
+ }
+}
+NOINLINE static void Frame1(int frame, char *a, char *b) {
+ char c[4] = {0}; Frame0(frame, a, b, c);
+ break_optimization(0);
+}
+NOINLINE static void Frame2(int frame, char *a) {
+ char b[4] = {0}; Frame1(frame, a, b);
+ break_optimization(0);
+}
+NOINLINE static void Frame3(int frame) {
+ char a[4] = {0}; Frame2(frame, a);
+ break_optimization(0);
+}
+
+int main(int argc, char **argv) {
+ if (argc != 2) return 1;
+ Frame3(argv[1][0] - '0');
+}
+
+// CHECK0: AddressSanitizer: stack-buffer-overflow
+// CHECK0: #0{{.*}}Frame0
+// CHECK0: #1{{.*}}Frame1
+// CHECK0: #2{{.*}}Frame2
+// CHECK0: #3{{.*}}Frame3
+// CHECK0: is located in stack of thread T0 at offset
+// CHECK0-NEXT: #0{{.*}}Frame0
+//
+// CHECK1: AddressSanitizer: stack-buffer-overflow
+// CHECK1: is located in stack of thread T0 at offset
+// CHECK1-NEXT: #0{{.*}}Frame1
+//
+// CHECK2: AddressSanitizer: stack-buffer-overflow
+// CHECK2: is located in stack of thread T0 at offset
+// CHECK2-NEXT: #0{{.*}}Frame2
+//
+// CHECK3: AddressSanitizer: stack-buffer-overflow
+// CHECK3: is located in stack of thread T0 at offset
+// CHECK3-NEXT: #0{{.*}}Frame3
diff --git a/lib/asan/lit_tests/stack-overflow.cc b/lib/asan/lit_tests/stack-overflow.cc
index 3deb1e91de6c..25ea43af48a4 100644
--- a/lib/asan/lit_tests/stack-overflow.cc
+++ b/lib/asan/lit_tests/stack-overflow.cc
@@ -14,6 +14,7 @@ int main(int argc, char **argv) {
int res = x[argc * 10]; // BOOOM
// CHECK: {{READ of size 1 at 0x.* thread T0}}
// CHECK: {{ #0 0x.* in _?main .*stack-overflow.cc:}}[[@LINE-2]]
- // CHECK: {{Address 0x.* is .* frame <main>}}
+ // CHECK: {{Address 0x.* is located in stack of thread T0 at offset}}
+ // CHECK-NEXT: in{{.*}}main{{.*}}stack-overflow.cc
return res;
}
diff --git a/lib/asan/lit_tests/strncpy-overflow.cc b/lib/asan/lit_tests/strncpy-overflow.cc
index 18711843c4c8..5133b5c1653e 100644
--- a/lib/asan/lit_tests/strncpy-overflow.cc
+++ b/lib/asan/lit_tests/strncpy-overflow.cc
@@ -22,7 +22,7 @@ int main(int argc, char **argv) {
strcpy(hello, "hello");
char *short_buffer = (char*)malloc(9);
strncpy(short_buffer, hello, 10); // BOOM
- // CHECK: {{WRITE of size 1 at 0x.* thread T0}}
+ // CHECK: {{WRITE of size 10 at 0x.* thread T0}}
// CHECK-Linux: {{ #0 0x.* in .*strncpy}}
// CHECK-Darwin: {{ #0 0x.* in _?wrap_strncpy}}
// CHECK: {{ #1 0x.* in _?main .*strncpy-overflow.cc:}}[[@LINE-4]]
@@ -32,9 +32,7 @@ int main(int argc, char **argv) {
// CHECK-Linux: {{ #0 0x.* in .*malloc}}
// CHECK-Linux: {{ #1 0x.* in main .*strncpy-overflow.cc:}}[[@LINE-10]]
- // CHECK-Darwin: {{ #0 0x.* in .*mz_malloc.*}}
- // CHECK-Darwin: {{ #1 0x.* in malloc_zone_malloc.*}}
- // CHECK-Darwin: {{ #2 0x.* in malloc.*}}
- // CHECK-Darwin: {{ #3 0x.* in _?main .*strncpy-overflow.cc:}}[[@LINE-15]]
+ // CHECK-Darwin: {{ #0 0x.* in _?wrap_malloc.*}}
+ // CHECK-Darwin: {{ #1 0x.* in _?main .*strncpy-overflow.cc:}}[[@LINE-13]]
return short_buffer[8];
}
diff --git a/lib/asan/lit_tests/throw_call_test.cc b/lib/asan/lit_tests/throw_call_test.cc
new file mode 100644
index 000000000000..974bc51d97c5
--- /dev/null
+++ b/lib/asan/lit_tests/throw_call_test.cc
@@ -0,0 +1,45 @@
+// RUN: %clangxx_asan %s -o %t && %t
+// http://code.google.com/p/address-sanitizer/issues/detail?id=147 (not fixed).
+// BROKEN: %clangxx_asan %s -o %t -static-libstdc++ && %t
+#include <stdio.h>
+static volatile int zero = 0;
+inline void pretend_to_do_something(void *x) {
+ __asm__ __volatile__("" : : "r" (x) : "memory");
+}
+
+__attribute__((noinline, no_sanitize_address))
+void ReallyThrow() {
+ fprintf(stderr, "ReallyThrow\n");
+ if (zero == 0)
+ throw 42;
+}
+
+__attribute__((noinline))
+void Throw() {
+ int a, b, c, d, e;
+ pretend_to_do_something(&a);
+ pretend_to_do_something(&b);
+ pretend_to_do_something(&c);
+ pretend_to_do_something(&d);
+ pretend_to_do_something(&e);
+ fprintf(stderr, "Throw stack = %p\n", &a);
+ ReallyThrow();
+}
+
+__attribute__((noinline))
+void CheckStack() {
+ int ar[100];
+ pretend_to_do_something(ar);
+ for (int i = 0; i < 100; i++)
+ ar[i] = i;
+ fprintf(stderr, "CheckStack stack = %p, %p\n", ar, ar + 100);
+}
+
+int main(int argc, char** argv) {
+ try {
+ Throw();
+ } catch(int a) {
+ fprintf(stderr, "a = %d\n", a);
+ }
+ CheckStack();
+}
diff --git a/lib/asan/lit_tests/throw_invoke_test.cc b/lib/asan/lit_tests/throw_invoke_test.cc
new file mode 100644
index 000000000000..077a940e8d19
--- /dev/null
+++ b/lib/asan/lit_tests/throw_invoke_test.cc
@@ -0,0 +1,50 @@
+// RUN: %clangxx_asan %s -o %t && %t
+// RUN: %clangxx_asan %s -o %t -static-libstdc++ && %t
+#include <stdio.h>
+static volatile int zero = 0;
+inline void pretend_to_do_something(void *x) {
+ __asm__ __volatile__("" : : "r" (x) : "memory");
+}
+
+__attribute__((noinline))
+void ReallyThrow() {
+ fprintf(stderr, "ReallyThrow\n");
+ try {
+ if (zero == 0)
+ throw 42;
+ else if (zero == 1)
+ throw 1.;
+ } catch(double x) {
+ }
+}
+
+__attribute__((noinline))
+void Throw() {
+ int a, b, c, d, e;
+ pretend_to_do_something(&a);
+ pretend_to_do_something(&b);
+ pretend_to_do_something(&c);
+ pretend_to_do_something(&d);
+ pretend_to_do_something(&e);
+ fprintf(stderr, "Throw stack = %p\n", &a);
+ ReallyThrow();
+}
+
+__attribute__((noinline))
+void CheckStack() {
+ int ar[100];
+ pretend_to_do_something(ar);
+ for (int i = 0; i < 100; i++)
+ ar[i] = i;
+ fprintf(stderr, "CheckStack stack = %p, %p\n", ar, ar + 100);
+}
+
+int main(int argc, char** argv) {
+ try {
+ Throw();
+ } catch(int a) {
+ fprintf(stderr, "a = %d\n", a);
+ }
+ CheckStack();
+}
+
diff --git a/lib/asan/lit_tests/time_interceptor.cc b/lib/asan/lit_tests/time_interceptor.cc
new file mode 100644
index 000000000000..f5f2ad62b815
--- /dev/null
+++ b/lib/asan/lit_tests/time_interceptor.cc
@@ -0,0 +1,16 @@
+// RUN: %clangxx_asan -m64 -O0 %s -o %t && %t 2>&1 | %symbolize | FileCheck %s
+
+// Test the time() interceptor.
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <time.h>
+
+int main() {
+ time_t *tm = (time_t*)malloc(sizeof(time_t));
+ free(tm);
+ time_t t = time(tm);
+ printf("Time: %s\n", ctime(&t)); // NOLINT
+ // CHECK: use-after-free
+ return 0;
+}
diff --git a/lib/asan/lit_tests/unaligned_loads_and_stores.cc b/lib/asan/lit_tests/unaligned_loads_and_stores.cc
new file mode 100644
index 000000000000..bcae089b427b
--- /dev/null
+++ b/lib/asan/lit_tests/unaligned_loads_and_stores.cc
@@ -0,0 +1,52 @@
+// RUN: %clangxx_asan -O0 -I %p/../../../include %s -o %t
+// RUN: %t A 2>&1 | %symbolize | FileCheck --check-prefix=CHECK-A %s
+// RUN: %t B 2>&1 | %symbolize | FileCheck --check-prefix=CHECK-B %s
+// RUN: %t C 2>&1 | %symbolize | FileCheck --check-prefix=CHECK-C %s
+// RUN: %t D 2>&1 | %symbolize | FileCheck --check-prefix=CHECK-D %s
+// RUN: %t E 2>&1 | %symbolize | FileCheck --check-prefix=CHECK-E %s
+
+// RUN: %t K 2>&1 | %symbolize | FileCheck --check-prefix=CHECK-K %s
+// RUN: %t L 2>&1 | %symbolize | FileCheck --check-prefix=CHECK-L %s
+// RUN: %t M 2>&1 | %symbolize | FileCheck --check-prefix=CHECK-M %s
+// RUN: %t N 2>&1 | %symbolize | FileCheck --check-prefix=CHECK-N %s
+// RUN: %t O 2>&1 | %symbolize | FileCheck --check-prefix=CHECK-O %s
+
+#include <sanitizer/asan_interface.h>
+
+#include <stdlib.h>
+#include <string.h>
+int main(int argc, char **argv) {
+ if (argc != 2) return 1;
+ char *x = new char[16];
+ memset(x, 0xab, 16);
+ int res = 1;
+ switch (argv[1][0]) {
+ case 'A': res = __sanitizer_unaligned_load16(x + 15); break;
+// CHECK-A ERROR: AddressSanitizer: heap-buffer-overflow on address
+// CHECK-A: main{{.*}}unaligned_loads_and_stores.cc:[[@LINE-2]]
+// CHECK-A: is located 0 bytes to the right of 16-byte region
+ case 'B': res = __sanitizer_unaligned_load32(x + 14); break;
+// CHECK-B: main{{.*}}unaligned_loads_and_stores.cc:[[@LINE-1]]
+ case 'C': res = __sanitizer_unaligned_load32(x + 13); break;
+// CHECK-C: main{{.*}}unaligned_loads_and_stores.cc:[[@LINE-1]]
+ case 'D': res = __sanitizer_unaligned_load64(x + 15); break;
+// CHECK-D: main{{.*}}unaligned_loads_and_stores.cc:[[@LINE-1]]
+ case 'E': res = __sanitizer_unaligned_load64(x + 9); break;
+// CHECK-E: main{{.*}}unaligned_loads_and_stores.cc:[[@LINE-1]]
+
+ case 'K': __sanitizer_unaligned_store16(x + 15, 0); break;
+// CHECK-K ERROR: AddressSanitizer: heap-buffer-overflow on address
+// CHECK-K: main{{.*}}unaligned_loads_and_stores.cc:[[@LINE-2]]
+// CHECK-K: is located 0 bytes to the right of 16-byte region
+ case 'L': __sanitizer_unaligned_store32(x + 15, 0); break;
+// CHECK-L: main{{.*}}unaligned_loads_and_stores.cc:[[@LINE-1]]
+ case 'M': __sanitizer_unaligned_store32(x + 13, 0); break;
+// CHECK-M: main{{.*}}unaligned_loads_and_stores.cc:[[@LINE-1]]
+ case 'N': __sanitizer_unaligned_store64(x + 10, 0); break;
+// CHECK-N: main{{.*}}unaligned_loads_and_stores.cc:[[@LINE-1]]
+ case 'O': __sanitizer_unaligned_store64(x + 14, 0); break;
+// CHECK-O: main{{.*}}unaligned_loads_and_stores.cc:[[@LINE-1]]
+ }
+ delete x;
+ return res;
+}
diff --git a/lib/asan/lit_tests/use-after-free-right.cc b/lib/asan/lit_tests/use-after-free-right.cc
new file mode 100644
index 000000000000..b0de07b04a08
--- /dev/null
+++ b/lib/asan/lit_tests/use-after-free-right.cc
@@ -0,0 +1,46 @@
+// RUN: %clangxx_asan -m64 -O0 %s -o %t && %t 2>&1 | %symbolize > %t.out
+// RUN: FileCheck %s < %t.out && FileCheck %s --check-prefix=CHECK-%os < %t.out
+// RUN: %clangxx_asan -m64 -O1 %s -o %t && %t 2>&1 | %symbolize > %t.out
+// RUN: FileCheck %s < %t.out && FileCheck %s --check-prefix=CHECK-%os < %t.out
+// RUN: %clangxx_asan -m64 -O2 %s -o %t && %t 2>&1 | %symbolize > %t.out
+// RUN: FileCheck %s < %t.out && FileCheck %s --check-prefix=CHECK-%os < %t.out
+// RUN: %clangxx_asan -m64 -O3 %s -o %t && %t 2>&1 | %symbolize > %t.out
+// RUN: FileCheck %s < %t.out && FileCheck %s --check-prefix=CHECK-%os < %t.out
+// RUN: %clangxx_asan -m32 -O0 %s -o %t && %t 2>&1 | %symbolize > %t.out
+// RUN: FileCheck %s < %t.out && FileCheck %s --check-prefix=CHECK-%os < %t.out
+// RUN: %clangxx_asan -m32 -O1 %s -o %t && %t 2>&1 | %symbolize > %t.out
+// RUN: FileCheck %s < %t.out && FileCheck %s --check-prefix=CHECK-%os < %t.out
+// RUN: %clangxx_asan -m32 -O2 %s -o %t && %t 2>&1 | %symbolize > %t.out
+// RUN: FileCheck %s < %t.out && FileCheck %s --check-prefix=CHECK-%os < %t.out
+// RUN: %clangxx_asan -m32 -O3 %s -o %t && %t 2>&1 | %symbolize > %t.out
+// RUN: FileCheck %s < %t.out && FileCheck %s --check-prefix=CHECK-%os < %t.out
+
+// Test use-after-free report in the case when access is at the right border of
+// the allocation.
+
+#include <stdlib.h>
+int main() {
+ volatile char *x = (char*)malloc(sizeof(char));
+ free((void*)x);
+ *x = 42;
+ // CHECK: {{.*ERROR: AddressSanitizer: heap-use-after-free on address}}
+ // CHECK: {{0x.* at pc 0x.* bp 0x.* sp 0x.*}}
+ // CHECK: {{WRITE of size 1 at 0x.* thread T0}}
+ // CHECK: {{ #0 0x.* in _?main .*use-after-free-right.cc:25}}
+ // CHECK: {{0x.* is located 0 bytes inside of 1-byte region .0x.*,0x.*}}
+ // CHECK: {{freed by thread T0 here:}}
+
+ // CHECK-Linux: {{ #0 0x.* in .*free}}
+ // CHECK-Linux: {{ #1 0x.* in main .*use-after-free-right.cc:24}}
+
+ // CHECK-Darwin: {{ #0 0x.* in _?wrap_free}}
+ // CHECK-Darwin: {{ #1 0x.* in _?main .*use-after-free-right.cc:24}}
+
+ // CHECK: {{previously allocated by thread T0 here:}}
+
+ // CHECK-Linux: {{ #0 0x.* in .*malloc}}
+ // CHECK-Linux: {{ #1 0x.* in main .*use-after-free-right.cc:23}}
+
+ // CHECK-Darwin: {{ #0 0x.* in _?wrap_malloc.*}}
+ // CHECK-Darwin: {{ #1 0x.* in _?main .*use-after-free-right.cc:23}}
+}
diff --git a/lib/asan/lit_tests/use-after-free.cc b/lib/asan/lit_tests/use-after-free.cc
index 24d5a2a54807..aee185dc4518 100644
--- a/lib/asan/lit_tests/use-after-free.cc
+++ b/lib/asan/lit_tests/use-after-free.cc
@@ -30,19 +30,14 @@ int main() {
// CHECK-Linux: {{ #0 0x.* in .*free}}
// CHECK-Linux: {{ #1 0x.* in main .*use-after-free.cc:21}}
- // CHECK-Darwin: {{ #0 0x.* in .*free_common.*}}
- // CHECK-Darwin: {{ #1 0x.* in .*mz_free.*}}
- // We override free() on Darwin, thus no malloc_zone_free
- // CHECK-Darwin: {{ #2 0x.* in _?wrap_free}}
- // CHECK-Darwin: {{ #3 0x.* in _?main .*use-after-free.cc:21}}
+ // CHECK-Darwin: {{ #0 0x.* in _?wrap_free}}
+ // CHECK-Darwin: {{ #1 0x.* in _?main .*use-after-free.cc:21}}
// CHECK: {{previously allocated by thread T0 here:}}
// CHECK-Linux: {{ #0 0x.* in .*malloc}}
// CHECK-Linux: {{ #1 0x.* in main .*use-after-free.cc:20}}
- // CHECK-Darwin: {{ #0 0x.* in .*mz_malloc.*}}
- // CHECK-Darwin: {{ #1 0x.* in malloc_zone_malloc.*}}
- // CHECK-Darwin: {{ #2 0x.* in malloc.*}}
- // CHECK-Darwin: {{ #3 0x.* in _?main .*use-after-free.cc:20}}
+ // CHECK-Darwin: {{ #0 0x.* in _?wrap_malloc.*}}
+ // CHECK-Darwin: {{ #1 0x.* in _?main .*use-after-free.cc:20}}
}
diff --git a/lib/asan/lit_tests/use-after-poison.cc b/lib/asan/lit_tests/use-after-poison.cc
new file mode 100644
index 000000000000..d87342900245
--- /dev/null
+++ b/lib/asan/lit_tests/use-after-poison.cc
@@ -0,0 +1,20 @@
+// Check that __asan_poison_memory_region works.
+// RUN: %clangxx_asan -m64 -O0 %s -o %t && %t 2>&1 | %symbolize | FileCheck %s
+//
+// Check that we can disable it
+// RUN: ASAN_OPTIONS=allow_user_poisoning=0 %t
+
+#include <stdlib.h>
+
+extern "C" void __asan_poison_memory_region(void *, size_t);
+
+int main(int argc, char **argv) {
+ char *x = new char[16];
+ x[10] = 0;
+ __asan_poison_memory_region(x, 16);
+ int res = x[argc * 10]; // BOOOM
+ // CHECK: ERROR: AddressSanitizer: use-after-poison
+ // CHECK: main{{.*}}use-after-poison.cc:[[@LINE-2]]
+ delete [] x;
+ return res;
+}
diff --git a/lib/asan/lit_tests/use-after-scope-inlined.cc b/lib/asan/lit_tests/use-after-scope-inlined.cc
index 3d730de6ab35..5c121ea187eb 100644
--- a/lib/asan/lit_tests/use-after-scope-inlined.cc
+++ b/lib/asan/lit_tests/use-after-scope-inlined.cc
@@ -23,7 +23,8 @@ int main(int argc, char *argv[]) {
// CHECK: READ of size 4 at 0x{{.*}} thread T0
// CHECK: #0 0x{{.*}} in {{_?}}main
// CHECK: {{.*}}use-after-scope-inlined.cc:[[@LINE-4]]
- // CHECK: Address 0x{{.*}} is located at offset
- // CHECK: [[OFFSET:[^ ]*]] in frame <main> of T0{{.*}}:
+ // CHECK: Address 0x{{.*}} is located in stack of thread T0 at offset
+ // CHECK: [[OFFSET:[^ ]*]] in frame
+ // CHECK: main
// CHECK: {{\[}}[[OFFSET]], {{.*}}) 'x.i'
}
diff --git a/lib/asan/lit_tests/wait.cc b/lib/asan/lit_tests/wait.cc
new file mode 100644
index 000000000000..88fbb17176fa
--- /dev/null
+++ b/lib/asan/lit_tests/wait.cc
@@ -0,0 +1,77 @@
+// RUN: %clangxx_asan -DWAIT -m64 -O0 %s -o %t && %t 2>&1 | %symbolize | FileCheck %s
+// RUN: %clangxx_asan -DWAIT -m64 -O3 %s -o %t && %t 2>&1 | %symbolize | FileCheck %s
+// RUN: %clangxx_asan -DWAIT -m32 -O0 %s -o %t && %t 2>&1 | %symbolize | FileCheck %s
+// RUN: %clangxx_asan -DWAIT -m32 -O3 %s -o %t && %t 2>&1 | %symbolize | FileCheck %s
+
+// RUN: %clangxx_asan -DWAITPID -m64 -O0 %s -o %t && %t 2>&1 | %symbolize | FileCheck %s
+// RUN: %clangxx_asan -DWAITPID -m64 -O3 %s -o %t && %t 2>&1 | %symbolize | FileCheck %s
+// RUN: %clangxx_asan -DWAITPID -m32 -O0 %s -o %t && %t 2>&1 | %symbolize | FileCheck %s
+// RUN: %clangxx_asan -DWAITPID -m32 -O3 %s -o %t && %t 2>&1 | %symbolize | FileCheck %s
+
+// RUN: %clangxx_asan -DWAITID -m64 -O0 %s -o %t && %t 2>&1 | %symbolize | FileCheck %s
+// RUN: %clangxx_asan -DWAITID -m64 -O3 %s -o %t && %t 2>&1 | %symbolize | FileCheck %s
+// RUN: %clangxx_asan -DWAITID -m32 -O0 %s -o %t && %t 2>&1 | %symbolize | FileCheck %s
+// RUN: %clangxx_asan -DWAITID -m32 -O3 %s -o %t && %t 2>&1 | %symbolize | FileCheck %s
+
+// RUN: %clangxx_asan -DWAIT3 -m64 -O0 %s -o %t && %t 2>&1 | %symbolize | FileCheck %s
+// RUN: %clangxx_asan -DWAIT3 -m64 -O3 %s -o %t && %t 2>&1 | %symbolize | FileCheck %s
+// RUN: %clangxx_asan -DWAIT3 -m32 -O0 %s -o %t && %t 2>&1 | %symbolize | FileCheck %s
+// RUN: %clangxx_asan -DWAIT3 -m32 -O3 %s -o %t && %t 2>&1 | %symbolize | FileCheck %s
+
+// RUN: %clangxx_asan -DWAIT4 -m64 -O0 %s -o %t && %t 2>&1 | %symbolize | FileCheck %s
+// RUN: %clangxx_asan -DWAIT4 -m64 -O3 %s -o %t && %t 2>&1 | %symbolize | FileCheck %s
+// RUN: %clangxx_asan -DWAIT4 -m32 -O0 %s -o %t && %t 2>&1 | %symbolize | FileCheck %s
+// RUN: %clangxx_asan -DWAIT4 -m32 -O3 %s -o %t && %t 2>&1 | %symbolize | FileCheck %s
+
+// RUN: %clangxx_asan -DWAIT3_RUSAGE -m64 -O0 %s -o %t && %t 2>&1 | %symbolize | FileCheck %s
+// RUN: %clangxx_asan -DWAIT3_RUSAGE -m64 -O3 %s -o %t && %t 2>&1 | %symbolize | FileCheck %s
+// RUN: %clangxx_asan -DWAIT3_RUSAGE -m32 -O0 %s -o %t && %t 2>&1 | %symbolize | FileCheck %s
+// RUN: %clangxx_asan -DWAIT3_RUSAGE -m32 -O3 %s -o %t && %t 2>&1 | %symbolize | FileCheck %s
+
+// RUN: %clangxx_asan -DWAIT4_RUSAGE -m64 -O0 %s -o %t && %t 2>&1 | %symbolize | FileCheck %s
+// RUN: %clangxx_asan -DWAIT4_RUSAGE -m64 -O3 %s -o %t && %t 2>&1 | %symbolize | FileCheck %s
+// RUN: %clangxx_asan -DWAIT4_RUSAGE -m32 -O0 %s -o %t && %t 2>&1 | %symbolize | FileCheck %s
+// RUN: %clangxx_asan -DWAIT4_RUSAGE -m32 -O3 %s -o %t && %t 2>&1 | %symbolize | FileCheck %s
+
+
+#include <assert.h>
+#include <sys/wait.h>
+#include <unistd.h>
+
+int main(int argc, char **argv) {
+ pid_t pid = fork();
+ if (pid) { // parent
+ int x[3];
+ int *status = x + argc * 3;
+ int res;
+#if defined(WAIT)
+ res = wait(status);
+#elif defined(WAITPID)
+ res = waitpid(pid, status, WNOHANG);
+#elif defined(WAITID)
+ siginfo_t *si = (siginfo_t*)(x + argc * 3);
+ res = waitid(P_ALL, 0, si, WEXITED | WNOHANG);
+#elif defined(WAIT3)
+ res = wait3(status, WNOHANG, NULL);
+#elif defined(WAIT4)
+ res = wait4(pid, status, WNOHANG, NULL);
+#elif defined(WAIT3_RUSAGE) || defined(WAIT4_RUSAGE)
+ struct rusage *ru = (struct rusage*)(x + argc * 3);
+ int good_status;
+# if defined(WAIT3_RUSAGE)
+ res = wait3(&good_status, WNOHANG, ru);
+# elif defined(WAIT4_RUSAGE)
+ res = wait4(pid, &good_status, WNOHANG, ru);
+# endif
+#endif
+ // CHECK: stack-buffer-overflow
+ // CHECK: {{WRITE of size .* at 0x.* thread T0}}
+ // CHECK: {{in .*wait}}
+ // CHECK: {{in _?main .*wait.cc:}}
+ // CHECK: is located in stack of thread T0 at offset
+ // CHECK: {{in _?main}}
+ return res != -1;
+ }
+ // child
+ return 0;
+}
diff --git a/lib/asan/scripts/asan_symbolize.py b/lib/asan/scripts/asan_symbolize.py
index 7b30bb55914e..bd3bf1e9b53e 100755
--- a/lib/asan/scripts/asan_symbolize.py
+++ b/lib/asan/scripts/asan_symbolize.py
@@ -8,6 +8,7 @@
#
#===------------------------------------------------------------------------===#
import bisect
+import getopt
import os
import re
import subprocess
@@ -18,6 +19,7 @@ symbolizers = {}
filetypes = {}
vmaddrs = {}
DEBUG = False
+demangle = False;
# FIXME: merge the code that calls fix_filename().
@@ -60,7 +62,7 @@ class LLVMSymbolizer(Symbolizer):
return None
cmd = [self.symbolizer_path,
'--use-symbol-table=true',
- '--demangle=false',
+ '--demangle=%s' % demangle,
'--functions=true',
'--inlining=true']
if DEBUG:
@@ -111,7 +113,10 @@ class Addr2LineSymbolizer(Symbolizer):
self.pipe = self.open_addr2line()
def open_addr2line(self):
- cmd = ['addr2line', '-f', '-e', self.binary]
+ cmd = ['addr2line', '-f']
+ if demangle:
+ cmd += ['--demangle']
+ cmd += ['-e', self.binary]
if DEBUG:
print ' '.join(cmd)
return subprocess.Popen(cmd,
@@ -352,5 +357,9 @@ class SymbolizationLoop(object):
if __name__ == '__main__':
+ opts, args = getopt.getopt(sys.argv[1:], "d", ["demangle"])
+ for o, a in opts:
+ if o in ("-d", "--demangle"):
+ demangle = True;
loop = SymbolizationLoop()
loop.process_stdin()
diff --git a/lib/asan/tests/CMakeLists.txt b/lib/asan/tests/CMakeLists.txt
index 272950bc5450..80d6f5d67aad 100644
--- a/lib/asan/tests/CMakeLists.txt
+++ b/lib/asan/tests/CMakeLists.txt
@@ -15,6 +15,13 @@ include(CompilerRTCompile)
include_directories(..)
include_directories(../..)
+# Use zero-based shadow on Android.
+if(ANDROID)
+ set(ASAN_TESTS_USE_ZERO_BASE_SHADOW TRUE)
+else()
+ set(ASAN_TESTS_USE_ZERO_BASE_SHADOW FALSE)
+endif()
+
set(ASAN_UNITTEST_HEADERS
asan_mac_test.h
asan_test_config.h
@@ -25,6 +32,7 @@ set(ASAN_UNITTEST_COMMON_CFLAGS
-I${COMPILER_RT_SOURCE_DIR}/include
-I${COMPILER_RT_SOURCE_DIR}/lib
-I${COMPILER_RT_SOURCE_DIR}/lib/asan
+ -I${COMPILER_RT_SOURCE_DIR}/lib/sanitizer_common/tests
-Wall
-Wno-format
-Werror
@@ -32,39 +40,37 @@ set(ASAN_UNITTEST_COMMON_CFLAGS
-O2
)
+if(ASAN_TESTS_USE_ZERO_BASE_SHADOW)
+ list(APPEND ASAN_UNITTEST_COMMON_CFLAGS -fPIE)
+endif()
if(SUPPORTS_NO_VARIADIC_MACROS_FLAG)
list(APPEND ASAN_UNITTEST_COMMON_CFLAGS -Wno-variadic-macros)
endif()
# Use -D instead of definitions to please custom compile command.
+list(APPEND ASAN_UNITTEST_COMMON_CFLAGS
+ -DASAN_HAS_BLACKLIST=1
+ -DASAN_HAS_EXCEPTIONS=1
+ -DASAN_UAR=0)
if(ANDROID)
list(APPEND ASAN_UNITTEST_COMMON_CFLAGS
- -DASAN_LOW_MEMORY=1
- -DASAN_HAS_BLACKLIST=1
- -DASAN_HAS_EXCEPTIONS=1
- -DASAN_NEEDS_SEGV=0
- -DASAN_UAR=0
- -fPIE
- )
+ -DASAN_FLEXIBLE_MAPPING_AND_OFFSET=0
+ -DASAN_NEEDS_SEGV=0)
else()
list(APPEND ASAN_UNITTEST_COMMON_CFLAGS
- -DASAN_HAS_BLACKLIST=1
- -DASAN_HAS_EXCEPTIONS=1
- -DASAN_NEEDS_SEGV=1
- -DASAN_UAR=0
- )
+ -DASAN_FLEXIBLE_MAPPING_AND_OFFSET=1
+ -DASAN_NEEDS_SEGV=1)
endif()
set(ASAN_LINK_FLAGS)
-if(ANDROID)
- # On Android, we link with ASan runtime manually
+if(ASAN_TESTS_USE_ZERO_BASE_SHADOW)
list(APPEND ASAN_LINK_FLAGS -pie)
-else()
- # On other platforms, we depend on Clang driver behavior,
- # passing -fsanitize=address flag.
+endif()
+# On Android, we link with ASan runtime manually. On other platforms we depend
+# on Clang driver behavior, passing -fsanitize=address flag.
+if(NOT ANDROID)
list(APPEND ASAN_LINK_FLAGS -fsanitize=address)
endif()
-
# Unit tests on Mac depend on Foundation.
if(APPLE)
list(APPEND ASAN_LINK_FLAGS -framework Foundation)
@@ -77,13 +83,17 @@ set(ASAN_BLACKLIST_FILE "${CMAKE_CURRENT_SOURCE_DIR}/asan_test.ignore")
set(ASAN_UNITTEST_INSTRUMENTED_CFLAGS
${ASAN_UNITTEST_COMMON_CFLAGS}
-fsanitize=address
- -mllvm "-asan-blacklist=${ASAN_BLACKLIST_FILE}"
+ "-fsanitize-blacklist=${ASAN_BLACKLIST_FILE}"
-mllvm -asan-stack=1
-mllvm -asan-globals=1
-mllvm -asan-mapping-scale=0 # default will be used
-mllvm -asan-mapping-offset-log=-1 # default will be used
-mllvm -asan-use-after-return=0
)
+if(ASAN_TESTS_USE_ZERO_BASE_SHADOW)
+ list(APPEND ASAN_UNITTEST_INSTRUMENTED_CFLAGS
+ -fsanitize-address-zero-base-shadow)
+endif()
# Compile source for the given architecture, using compiler
# options in ${ARGN}, and add it to the object list.
@@ -117,6 +127,16 @@ set_target_properties(AsanUnitTests PROPERTIES FOLDER "ASan unit tests")
add_custom_target(AsanBenchmarks)
set_target_properties(AsanBenchmarks PROPERTIES FOLDER "Asan benchmarks")
+set(ASAN_NOINST_TEST_SOURCES
+ asan_noinst_test.cc
+ asan_test_main.cc)
+set(ASAN_INST_TEST_SOURCES
+ asan_globals_test.cc
+ asan_test.cc
+ asan_oob_test.cc
+ asan_mem_test.cc
+ asan_str_test.cc)
+
# Adds ASan unit tests and benchmarks for architecture.
macro(add_asan_tests_for_arch arch)
# Build gtest instrumented with ASan.
@@ -125,20 +145,23 @@ macro(add_asan_tests_for_arch arch)
${ASAN_UNITTEST_INSTRUMENTED_CFLAGS})
# Instrumented tests.
set(ASAN_INST_TEST_OBJECTS)
- asan_compile(ASAN_INST_TEST_OBJECTS asan_globals_test.cc ${arch}
- ${ASAN_UNITTEST_INSTRUMENTED_CFLAGS})
- asan_compile(ASAN_INST_TEST_OBJECTS asan_test.cc ${arch}
- ${ASAN_UNITTEST_INSTRUMENTED_CFLAGS})
+ foreach(src ${ASAN_INST_TEST_SOURCES})
+ asan_compile(ASAN_INST_TEST_OBJECTS ${src} ${arch}
+ ${ASAN_UNITTEST_INSTRUMENTED_CFLAGS})
+ endforeach()
+ # Add Mac-specific tests.
if (APPLE)
- asan_compile(ASAN_INST_TEST_OBJECTS asan_mac_test.mm ${arch}
+ asan_compile(ASAN_INST_TEST_OBJECTS asan_mac_test.cc ${arch}
+ ${ASAN_UNITTEST_INSTRUMENTED_CFLAGS})
+ asan_compile(ASAN_INST_TEST_OBJECTS asan_mac_test_helpers.mm ${arch}
${ASAN_UNITTEST_INSTRUMENTED_CFLAGS} -ObjC)
endif()
# Uninstrumented tests.
set(ASAN_NOINST_TEST_OBJECTS)
- asan_compile(ASAN_NOINST_TEST_OBJECTS asan_noinst_test.cc ${arch}
- ${ASAN_UNITTEST_COMMON_CFLAGS})
- asan_compile(ASAN_NOINST_TEST_OBJECTS asan_test_main.cc ${arch}
- ${ASAN_UNITTEST_COMMON_CFLAGS})
+ foreach(src ${ASAN_NOINST_TEST_SOURCES})
+ asan_compile(ASAN_NOINST_TEST_OBJECTS ${src} ${arch}
+ ${ASAN_UNITTEST_COMMON_CFLAGS})
+ endforeach()
# Link everything together.
add_asan_test(AsanUnitTests "Asan-${arch}-Test" ${arch}
${ASAN_NOINST_TEST_OBJECTS}
@@ -154,19 +177,14 @@ macro(add_asan_tests_for_arch arch)
endmacro()
if(COMPILER_RT_CAN_EXECUTE_TESTS)
- if(CAN_TARGET_x86_64)
- add_asan_tests_for_arch(x86_64)
- endif()
- if(CAN_TARGET_i386)
- add_asan_tests_for_arch(i386)
- endif()
+ foreach(arch ${ASAN_SUPPORTED_ARCH})
+ add_asan_tests_for_arch(${arch})
+ endforeach()
endif()
if(ANDROID)
# We assume that unit tests on Android are built in a build
# tree with fresh Clang as a host compiler.
- set(ASAN_NOINST_TEST_SOURCES asan_noinst_test.cc asan_test_main.cc)
- set(ASAN_INST_TEST_SOURCES asan_globals_test.cc asan_test.cc)
add_library(asan_noinst_test OBJECT ${ASAN_NOINST_TEST_SOURCES})
set_target_compile_flags(asan_noinst_test ${ASAN_UNITTEST_COMMON_CFLAGS})
add_library(asan_inst_test OBJECT
@@ -177,9 +195,8 @@ if(ANDROID)
$<TARGET_OBJECTS:asan_inst_test>
)
# Setup correct output directory and link flags.
- get_unittest_directory(OUTPUT_DIR)
set_target_properties(AsanTest PROPERTIES
- RUNTIME_OUTPUT_DIRECTORY ${OUTPUT_DIR})
+ RUNTIME_OUTPUT_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR})
set_target_link_flags(AsanTest ${ASAN_LINK_FLAGS})
target_link_libraries(AsanTest clang_rt.asan-arm-android)
# Add unit test to test suite.
diff --git a/lib/asan/tests/asan_globals_test.cc b/lib/asan/tests/asan_globals_test.cc
index dc2e9bbb0530..5042ef07378d 100644
--- a/lib/asan/tests/asan_globals_test.cc
+++ b/lib/asan/tests/asan_globals_test.cc
@@ -11,8 +11,29 @@
//
// Some globals in a separate file.
//===----------------------------------------------------------------------===//
+#include "asan_test_utils.h"
+
+char glob1[1];
+char glob2[2];
+char glob3[3];
+char glob4[4];
+char glob5[5];
+char glob6[6];
+char glob7[7];
+char glob8[8];
+char glob9[9];
+char glob10[10];
+char glob11[11];
+char glob12[12];
+char glob13[13];
+char glob14[14];
+char glob15[15];
+char glob16[16];
+char glob17[17];
+char glob1000[1000];
+char glob10000[10000];
+char glob100000[100000];
-extern char glob5[5];
static char static10[10];
int GlobalsTest(int zero) {
diff --git a/lib/asan/tests/asan_mac_test.cc b/lib/asan/tests/asan_mac_test.cc
new file mode 100644
index 000000000000..cabdfd711ea2
--- /dev/null
+++ b/lib/asan/tests/asan_mac_test.cc
@@ -0,0 +1,236 @@
+//===-- asan_test_mac.cc --------------------------------------------------===//
+//
+// 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.
+//
+//===----------------------------------------------------------------------===//
+
+#include "asan_test_utils.h"
+
+#include "asan_mac_test.h"
+
+#include <malloc/malloc.h>
+#include <AvailabilityMacros.h> // For MAC_OS_X_VERSION_*
+#include <CoreFoundation/CFString.h>
+
+TEST(AddressSanitizerMac, CFAllocatorDefaultDoubleFree) {
+ EXPECT_DEATH(
+ CFAllocatorDefaultDoubleFree(NULL),
+ "attempting double-free");
+}
+
+void CFAllocator_DoubleFreeOnPthread() {
+ pthread_t child;
+ PTHREAD_CREATE(&child, NULL, CFAllocatorDefaultDoubleFree, NULL);
+ PTHREAD_JOIN(child, NULL); // Shouldn't be reached.
+}
+
+TEST(AddressSanitizerMac, CFAllocatorDefaultDoubleFree_ChildPhread) {
+ EXPECT_DEATH(CFAllocator_DoubleFreeOnPthread(), "attempting double-free");
+}
+
+namespace {
+
+void *GLOB;
+
+void *CFAllocatorAllocateToGlob(void *unused) {
+ GLOB = CFAllocatorAllocate(NULL, 100, /*hint*/0);
+ return NULL;
+}
+
+void *CFAllocatorDeallocateFromGlob(void *unused) {
+ char *p = (char*)GLOB;
+ p[100] = 'A'; // ASan should report an error here.
+ CFAllocatorDeallocate(NULL, GLOB);
+ return NULL;
+}
+
+void CFAllocator_PassMemoryToAnotherThread() {
+ pthread_t th1, th2;
+ PTHREAD_CREATE(&th1, NULL, CFAllocatorAllocateToGlob, NULL);
+ PTHREAD_JOIN(th1, NULL);
+ PTHREAD_CREATE(&th2, NULL, CFAllocatorDeallocateFromGlob, NULL);
+ PTHREAD_JOIN(th2, NULL);
+}
+
+TEST(AddressSanitizerMac, CFAllocator_PassMemoryToAnotherThread) {
+ EXPECT_DEATH(CFAllocator_PassMemoryToAnotherThread(),
+ "heap-buffer-overflow");
+}
+
+} // namespace
+
+// TODO(glider): figure out whether we still need these tests. Is it correct
+// to intercept the non-default CFAllocators?
+TEST(AddressSanitizerMac, DISABLED_CFAllocatorSystemDefaultDoubleFree) {
+ EXPECT_DEATH(
+ CFAllocatorSystemDefaultDoubleFree(),
+ "attempting double-free");
+}
+
+// We're intercepting malloc, so kCFAllocatorMalloc is routed to ASan.
+TEST(AddressSanitizerMac, CFAllocatorMallocDoubleFree) {
+ EXPECT_DEATH(CFAllocatorMallocDoubleFree(), "attempting double-free");
+}
+
+TEST(AddressSanitizerMac, DISABLED_CFAllocatorMallocZoneDoubleFree) {
+ EXPECT_DEATH(CFAllocatorMallocZoneDoubleFree(), "attempting double-free");
+}
+
+// For libdispatch tests below we check that ASan got to the shadow byte
+// legend, i.e. managed to print the thread stacks (this almost certainly
+// means that the libdispatch task creation has been intercepted correctly).
+TEST(AddressSanitizerMac, GCDDispatchAsync) {
+ // Make sure the whole ASan report is printed, i.e. that we don't die
+ // on a CHECK.
+ EXPECT_DEATH(TestGCDDispatchAsync(), "Shadow byte legend");
+}
+
+TEST(AddressSanitizerMac, GCDDispatchSync) {
+ // Make sure the whole ASan report is printed, i.e. that we don't die
+ // on a CHECK.
+ EXPECT_DEATH(TestGCDDispatchSync(), "Shadow byte legend");
+}
+
+
+TEST(AddressSanitizerMac, GCDReuseWqthreadsAsync) {
+ // Make sure the whole ASan report is printed, i.e. that we don't die
+ // on a CHECK.
+ EXPECT_DEATH(TestGCDReuseWqthreadsAsync(), "Shadow byte legend");
+}
+
+TEST(AddressSanitizerMac, GCDReuseWqthreadsSync) {
+ // Make sure the whole ASan report is printed, i.e. that we don't die
+ // on a CHECK.
+ EXPECT_DEATH(TestGCDReuseWqthreadsSync(), "Shadow byte legend");
+}
+
+TEST(AddressSanitizerMac, GCDDispatchAfter) {
+ // Make sure the whole ASan report is printed, i.e. that we don't die
+ // on a CHECK.
+ EXPECT_DEATH(TestGCDDispatchAfter(), "Shadow byte legend");
+}
+
+TEST(AddressSanitizerMac, GCDSourceEvent) {
+ // Make sure the whole ASan report is printed, i.e. that we don't die
+ // on a CHECK.
+ EXPECT_DEATH(TestGCDSourceEvent(), "Shadow byte legend");
+}
+
+TEST(AddressSanitizerMac, GCDSourceCancel) {
+ // Make sure the whole ASan report is printed, i.e. that we don't die
+ // on a CHECK.
+ EXPECT_DEATH(TestGCDSourceCancel(), "Shadow byte legend");
+}
+
+TEST(AddressSanitizerMac, GCDGroupAsync) {
+ // Make sure the whole ASan report is printed, i.e. that we don't die
+ // on a CHECK.
+ EXPECT_DEATH(TestGCDGroupAsync(), "Shadow byte legend");
+}
+
+void *MallocIntrospectionLockWorker(void *_) {
+ const int kNumPointers = 100;
+ int i;
+ void *pointers[kNumPointers];
+ for (i = 0; i < kNumPointers; i++) {
+ pointers[i] = malloc(i + 1);
+ }
+ for (i = 0; i < kNumPointers; i++) {
+ free(pointers[i]);
+ }
+
+ return NULL;
+}
+
+void *MallocIntrospectionLockForker(void *_) {
+ pid_t result = fork();
+ if (result == -1) {
+ perror("fork");
+ }
+ assert(result != -1);
+ if (result == 0) {
+ // Call malloc in the child process to make sure we won't deadlock.
+ void *ptr = malloc(42);
+ free(ptr);
+ exit(0);
+ } else {
+ // Return in the parent process.
+ return NULL;
+ }
+}
+
+TEST(AddressSanitizerMac, MallocIntrospectionLock) {
+ // Incorrect implementation of force_lock and force_unlock in our malloc zone
+ // will cause forked processes to deadlock.
+ // TODO(glider): need to detect that none of the child processes deadlocked.
+ const int kNumWorkers = 5, kNumIterations = 100;
+ int i, iter;
+ for (iter = 0; iter < kNumIterations; iter++) {
+ pthread_t workers[kNumWorkers], forker;
+ for (i = 0; i < kNumWorkers; i++) {
+ PTHREAD_CREATE(&workers[i], 0, MallocIntrospectionLockWorker, 0);
+ }
+ PTHREAD_CREATE(&forker, 0, MallocIntrospectionLockForker, 0);
+ for (i = 0; i < kNumWorkers; i++) {
+ PTHREAD_JOIN(workers[i], 0);
+ }
+ PTHREAD_JOIN(forker, 0);
+ }
+}
+
+void *TSDAllocWorker(void *test_key) {
+ if (test_key) {
+ void *mem = malloc(10);
+ pthread_setspecific(*(pthread_key_t*)test_key, mem);
+ }
+ return NULL;
+}
+
+TEST(AddressSanitizerMac, DISABLED_TSDWorkqueueTest) {
+ pthread_t th;
+ pthread_key_t test_key;
+ pthread_key_create(&test_key, CallFreeOnWorkqueue);
+ PTHREAD_CREATE(&th, NULL, TSDAllocWorker, &test_key);
+ PTHREAD_JOIN(th, NULL);
+ pthread_key_delete(test_key);
+}
+
+// Test that CFStringCreateCopy does not copy constant strings.
+TEST(AddressSanitizerMac, CFStringCreateCopy) {
+ CFStringRef str = CFSTR("Hello world!\n");
+ CFStringRef str2 = CFStringCreateCopy(0, str);
+ EXPECT_EQ(str, str2);
+}
+
+TEST(AddressSanitizerMac, NSObjectOOB) {
+ // Make sure that our allocators are used for NSObjects.
+ EXPECT_DEATH(TestOOBNSObjects(), "heap-buffer-overflow");
+}
+
+// Make sure that correct pointer is passed to free() when deallocating a
+// NSURL object.
+// See http://code.google.com/p/address-sanitizer/issues/detail?id=70.
+TEST(AddressSanitizerMac, NSURLDeallocation) {
+ TestNSURLDeallocation();
+}
+
+// See http://code.google.com/p/address-sanitizer/issues/detail?id=109.
+TEST(AddressSanitizerMac, Mstats) {
+ malloc_statistics_t stats1, stats2;
+ malloc_zone_statistics(/*all zones*/NULL, &stats1);
+ const size_t kMallocSize = 100000;
+ void *alloc = Ident(malloc(kMallocSize));
+ malloc_zone_statistics(/*all zones*/NULL, &stats2);
+ EXPECT_GT(stats2.blocks_in_use, stats1.blocks_in_use);
+ EXPECT_GE(stats2.size_in_use - stats1.size_in_use, kMallocSize);
+ free(alloc);
+ // Even the default OSX allocator may not change the stats after free().
+}
+
diff --git a/lib/asan/tests/asan_mac_test.mm b/lib/asan/tests/asan_mac_test_helpers.mm
index 4cbd2bb247fd..4cbd2bb247fd 100644
--- a/lib/asan/tests/asan_mac_test.mm
+++ b/lib/asan/tests/asan_mac_test_helpers.mm
diff --git a/lib/asan/tests/asan_mem_test.cc b/lib/asan/tests/asan_mem_test.cc
new file mode 100644
index 000000000000..60f5cd4cf760
--- /dev/null
+++ b/lib/asan/tests/asan_mem_test.cc
@@ -0,0 +1,240 @@
+//===-- asan_mem_test.cc --------------------------------------------------===//
+//
+// 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.
+//
+//===----------------------------------------------------------------------===//
+#include "asan_test_utils.h"
+
+template<typename T>
+void MemSetOOBTestTemplate(size_t length) {
+ if (length == 0) return;
+ size_t size = Ident(sizeof(T) * length);
+ T *array = Ident((T*)malloc(size));
+ int element = Ident(42);
+ int zero = Ident(0);
+ void *(*MEMSET)(void *s, int c, size_t n) = Ident(memset);
+ // memset interval inside array
+ MEMSET(array, element, size);
+ MEMSET(array, element, size - 1);
+ MEMSET(array + length - 1, element, sizeof(T));
+ MEMSET(array, element, 1);
+
+ // memset 0 bytes
+ MEMSET(array - 10, element, zero);
+ MEMSET(array - 1, element, zero);
+ MEMSET(array, element, zero);
+ MEMSET(array + length, 0, zero);
+ MEMSET(array + length + 1, 0, zero);
+
+ // try to memset bytes to the right of array
+ EXPECT_DEATH(MEMSET(array, 0, size + 1),
+ RightOOBWriteMessage(0));
+ EXPECT_DEATH(MEMSET((char*)(array + length) - 1, element, 6),
+ RightOOBWriteMessage(0));
+ EXPECT_DEATH(MEMSET(array + 1, element, size + sizeof(T)),
+ RightOOBWriteMessage(0));
+ // whole interval is to the right
+ EXPECT_DEATH(MEMSET(array + length + 1, 0, 10),
+ RightOOBWriteMessage(sizeof(T)));
+
+ // try to memset bytes to the left of array
+ EXPECT_DEATH(MEMSET((char*)array - 1, element, size),
+ LeftOOBWriteMessage(1));
+ EXPECT_DEATH(MEMSET((char*)array - 5, 0, 6),
+ LeftOOBWriteMessage(5));
+ if (length >= 100) {
+ // Large OOB, we find it only if the redzone is large enough.
+ EXPECT_DEATH(memset(array - 5, element, size + 5 * sizeof(T)),
+ LeftOOBWriteMessage(5 * sizeof(T)));
+ }
+ // whole interval is to the left
+ EXPECT_DEATH(MEMSET(array - 2, 0, sizeof(T)),
+ LeftOOBWriteMessage(2 * sizeof(T)));
+
+ // try to memset bytes both to the left & to the right
+ EXPECT_DEATH(MEMSET((char*)array - 2, element, size + 4),
+ LeftOOBWriteMessage(2));
+
+ free(array);
+}
+
+TEST(AddressSanitizer, MemSetOOBTest) {
+ MemSetOOBTestTemplate<char>(100);
+ MemSetOOBTestTemplate<int>(5);
+ MemSetOOBTestTemplate<double>(256);
+ // We can test arrays of structres/classes here, but what for?
+}
+
+// Try to allocate two arrays of 'size' bytes that are near each other.
+// Strictly speaking we are not guaranteed to find such two pointers,
+// but given the structure of asan's allocator we will.
+static bool AllocateTwoAdjacentArrays(char **x1, char **x2, size_t size) {
+ vector<char *> v;
+ bool res = false;
+ for (size_t i = 0; i < 1000U && !res; i++) {
+ v.push_back(new char[size]);
+ if (i == 0) continue;
+ sort(v.begin(), v.end());
+ for (size_t j = 1; j < v.size(); j++) {
+ assert(v[j] > v[j-1]);
+ if ((size_t)(v[j] - v[j-1]) < size * 2) {
+ *x2 = v[j];
+ *x1 = v[j-1];
+ res = true;
+ break;
+ }
+ }
+ }
+
+ for (size_t i = 0; i < v.size(); i++) {
+ if (res && v[i] == *x1) continue;
+ if (res && v[i] == *x2) continue;
+ delete [] v[i];
+ }
+ return res;
+}
+
+TEST(AddressSanitizer, LargeOOBInMemset) {
+ for (size_t size = 200; size < 100000; size += size / 2) {
+ char *x1, *x2;
+ if (!Ident(AllocateTwoAdjacentArrays)(&x1, &x2, size))
+ continue;
+ // fprintf(stderr, " large oob memset: %p %p %zd\n", x1, x2, size);
+ // Do a memset on x1 with huge out-of-bound access that will end up in x2.
+ EXPECT_DEATH(Ident(memset)(x1, 0, size * 2),
+ "is located 0 bytes to the right");
+ delete [] x1;
+ delete [] x2;
+ return;
+ }
+ assert(0 && "Did not find two adjacent malloc-ed pointers");
+}
+
+// Same test for memcpy and memmove functions
+template <typename T, class M>
+void MemTransferOOBTestTemplate(size_t length) {
+ if (length == 0) return;
+ size_t size = Ident(sizeof(T) * length);
+ T *src = Ident((T*)malloc(size));
+ T *dest = Ident((T*)malloc(size));
+ int zero = Ident(0);
+
+ // valid transfer of bytes between arrays
+ M::transfer(dest, src, size);
+ M::transfer(dest + 1, src, size - sizeof(T));
+ M::transfer(dest, src + length - 1, sizeof(T));
+ M::transfer(dest, src, 1);
+
+ // transfer zero bytes
+ M::transfer(dest - 1, src, 0);
+ M::transfer(dest + length, src, zero);
+ M::transfer(dest, src - 1, zero);
+ M::transfer(dest, src, zero);
+
+ // try to change mem to the right of dest
+ EXPECT_DEATH(M::transfer(dest + 1, src, size),
+ RightOOBWriteMessage(0));
+ EXPECT_DEATH(M::transfer((char*)(dest + length) - 1, src, 5),
+ RightOOBWriteMessage(0));
+
+ // try to change mem to the left of dest
+ EXPECT_DEATH(M::transfer(dest - 2, src, size),
+ LeftOOBWriteMessage(2 * sizeof(T)));
+ EXPECT_DEATH(M::transfer((char*)dest - 3, src, 4),
+ LeftOOBWriteMessage(3));
+
+ // try to access mem to the right of src
+ EXPECT_DEATH(M::transfer(dest, src + 2, size),
+ RightOOBReadMessage(0));
+ EXPECT_DEATH(M::transfer(dest, (char*)(src + length) - 3, 6),
+ RightOOBReadMessage(0));
+
+ // try to access mem to the left of src
+ EXPECT_DEATH(M::transfer(dest, src - 1, size),
+ LeftOOBReadMessage(sizeof(T)));
+ EXPECT_DEATH(M::transfer(dest, (char*)src - 6, 7),
+ LeftOOBReadMessage(6));
+
+ // Generally we don't need to test cases where both accessing src and writing
+ // to dest address to poisoned memory.
+
+ T *big_src = Ident((T*)malloc(size * 2));
+ T *big_dest = Ident((T*)malloc(size * 2));
+ // try to change mem to both sides of dest
+ EXPECT_DEATH(M::transfer(dest - 1, big_src, size * 2),
+ LeftOOBWriteMessage(sizeof(T)));
+ // try to access mem to both sides of src
+ EXPECT_DEATH(M::transfer(big_dest, src - 2, size * 2),
+ LeftOOBReadMessage(2 * sizeof(T)));
+
+ free(src);
+ free(dest);
+ free(big_src);
+ free(big_dest);
+}
+
+class MemCpyWrapper {
+ public:
+ static void* transfer(void *to, const void *from, size_t size) {
+ return Ident(memcpy)(to, from, size);
+ }
+};
+
+TEST(AddressSanitizer, MemCpyOOBTest) {
+ MemTransferOOBTestTemplate<char, MemCpyWrapper>(100);
+ MemTransferOOBTestTemplate<int, MemCpyWrapper>(1024);
+}
+
+class MemMoveWrapper {
+ public:
+ static void* transfer(void *to, const void *from, size_t size) {
+ return Ident(memmove)(to, from, size);
+ }
+};
+
+TEST(AddressSanitizer, MemMoveOOBTest) {
+ MemTransferOOBTestTemplate<char, MemMoveWrapper>(100);
+ MemTransferOOBTestTemplate<int, MemMoveWrapper>(1024);
+}
+
+
+TEST(AddressSanitizer, MemCmpOOBTest) {
+ size_t size = Ident(100);
+ char *s1 = MallocAndMemsetString(size);
+ char *s2 = MallocAndMemsetString(size);
+ // Normal memcmp calls.
+ Ident(memcmp(s1, s2, size));
+ Ident(memcmp(s1 + size - 1, s2 + size - 1, 1));
+ Ident(memcmp(s1 - 1, s2 - 1, 0));
+ // One of arguments points to not allocated memory.
+ EXPECT_DEATH(Ident(memcmp)(s1 - 1, s2, 1), LeftOOBReadMessage(1));
+ EXPECT_DEATH(Ident(memcmp)(s1, s2 - 1, 1), LeftOOBReadMessage(1));
+ EXPECT_DEATH(Ident(memcmp)(s1 + size, s2, 1), RightOOBReadMessage(0));
+ EXPECT_DEATH(Ident(memcmp)(s1, s2 + size, 1), RightOOBReadMessage(0));
+ // Hit unallocated memory and die.
+ EXPECT_DEATH(Ident(memcmp)(s1 + 1, s2 + 1, size), RightOOBReadMessage(0));
+ EXPECT_DEATH(Ident(memcmp)(s1 + size - 1, s2, 2), RightOOBReadMessage(0));
+ // Zero bytes are not terminators and don't prevent from OOB.
+ s1[size - 1] = '\0';
+ s2[size - 1] = '\0';
+ EXPECT_DEATH(Ident(memcmp)(s1, s2, size + 1), RightOOBReadMessage(0));
+
+ // Even if the buffers differ in the first byte, we still assume that
+ // memcmp may access the whole buffer and thus reporting the overflow here:
+ s1[0] = 1;
+ s2[0] = 123;
+ EXPECT_DEATH(Ident(memcmp)(s1, s2, size + 1), RightOOBReadMessage(0));
+
+ free(s1);
+ free(s2);
+}
+
+
+
diff --git a/lib/asan/tests/asan_noinst_test.cc b/lib/asan/tests/asan_noinst_test.cc
index 576312bf319f..54fdd1979b8e 100644
--- a/lib/asan/tests/asan_noinst_test.cc
+++ b/lib/asan/tests/asan_noinst_test.cc
@@ -15,9 +15,7 @@
#include "asan_allocator.h"
#include "asan_internal.h"
#include "asan_mapping.h"
-#include "asan_stack.h"
#include "asan_test_utils.h"
-#include "sanitizer/asan_interface.h"
#include <assert.h>
#include <stdio.h>
@@ -25,6 +23,7 @@
#include <string.h> // for memset()
#include <algorithm>
#include <vector>
+#include <limits>
TEST(AddressSanitizer, InternalSimpleDeathTest) {
@@ -33,17 +32,17 @@ TEST(AddressSanitizer, InternalSimpleDeathTest) {
static void MallocStress(size_t n) {
u32 seed = my_rand();
- __asan::StackTrace stack1;
+ StackTrace stack1;
stack1.trace[0] = 0xa123;
stack1.trace[1] = 0xa456;
stack1.size = 2;
- __asan::StackTrace stack2;
+ StackTrace stack2;
stack2.trace[0] = 0xb123;
stack2.trace[1] = 0xb456;
stack2.size = 2;
- __asan::StackTrace stack3;
+ StackTrace stack3;
stack3.trace[0] = 0xc123;
stack3.trace[1] = 0xc456;
stack3.size = 2;
@@ -79,11 +78,20 @@ static void MallocStress(size_t n) {
TEST(AddressSanitizer, NoInstMallocTest) {
-#ifdef __arm__
- MallocStress(300000);
-#else
- MallocStress(1000000);
-#endif
+ MallocStress(ASAN_LOW_MEMORY ? 300000 : 1000000);
+}
+
+TEST(AddressSanitizer, ThreadedMallocStressTest) {
+ const int kNumThreads = 4;
+ const int kNumIterations = (ASAN_LOW_MEMORY) ? 10000 : 100000;
+ pthread_t t[kNumThreads];
+ for (int i = 0; i < kNumThreads; i++) {
+ PTHREAD_CREATE(&t[i], 0, (void* (*)(void *x))MallocStress,
+ (void*)kNumIterations);
+ }
+ for (int i = 0; i < kNumThreads; i++) {
+ PTHREAD_JOIN(t[i], 0);
+ }
}
static void PrintShadow(const char *tag, uptr ptr, size_t size) {
@@ -207,16 +215,16 @@ void CompressStackTraceTest(size_t n_iter) {
for (size_t iter = 0; iter < n_iter; iter++) {
std::random_shuffle(pc_array, pc_array + kNumPcs);
- __asan::StackTrace stack0, stack1;
+ StackTrace stack0, stack1;
stack0.CopyFrom(pc_array, kNumPcs);
stack0.size = std::max((size_t)1, (size_t)(my_rand_r(&seed) % stack0.size));
size_t compress_size =
std::max((size_t)2, (size_t)my_rand_r(&seed) % (2 * kNumPcs));
size_t n_frames =
- __asan::StackTrace::CompressStack(&stack0, compressed, compress_size);
+ StackTrace::CompressStack(&stack0, compressed, compress_size);
Ident(n_frames);
assert(n_frames <= stack0.size);
- __asan::StackTrace::UncompressStack(&stack1, compressed, compress_size);
+ StackTrace::UncompressStack(&stack1, compressed, compress_size);
assert(stack1.size == n_frames);
for (size_t i = 0; i < stack1.size; i++) {
assert(stack0.trace[i] == stack1.trace[i]);
@@ -233,13 +241,13 @@ void CompressStackTraceBenchmark(size_t n_iter) {
u32 compressed[2 * kNumPcs];
std::random_shuffle(pc_array, pc_array + kNumPcs);
- __asan::StackTrace stack0;
+ StackTrace stack0;
stack0.CopyFrom(pc_array, kNumPcs);
stack0.size = kNumPcs;
for (size_t iter = 0; iter < n_iter; iter++) {
size_t compress_size = kNumPcs;
size_t n_frames =
- __asan::StackTrace::CompressStack(&stack0, compressed, compress_size);
+ StackTrace::CompressStack(&stack0, compressed, compress_size);
Ident(n_frames);
}
}
@@ -249,11 +257,11 @@ TEST(AddressSanitizer, CompressStackTraceBenchmark) {
}
TEST(AddressSanitizer, QuarantineTest) {
- __asan::StackTrace stack;
+ StackTrace stack;
stack.trace[0] = 0x890;
stack.size = 1;
- const int size = 32;
+ const int size = 1024;
void *p = __asan::asan_malloc(size, &stack);
__asan::asan_free(p, &stack, __asan::FROM_MALLOC);
size_t i;
@@ -263,15 +271,14 @@ TEST(AddressSanitizer, QuarantineTest) {
__asan::asan_free(p1, &stack, __asan::FROM_MALLOC);
if (p1 == p) break;
}
- // fprintf(stderr, "i=%ld\n", i);
- EXPECT_GE(i, 100000U);
+ EXPECT_GE(i, 10000U);
EXPECT_LT(i, max_i);
}
void *ThreadedQuarantineTestWorker(void *unused) {
(void)unused;
u32 seed = my_rand();
- __asan::StackTrace stack;
+ StackTrace stack;
stack.trace[0] = 0x890;
stack.size = 1;
@@ -298,7 +305,7 @@ TEST(AddressSanitizer, ThreadedQuarantineTest) {
void *ThreadedOneSizeMallocStress(void *unused) {
(void)unused;
- __asan::StackTrace stack;
+ StackTrace stack;
stack.trace[0] = 0x890;
stack.size = 1;
const size_t kNumMallocs = 1000;
@@ -326,11 +333,13 @@ TEST(AddressSanitizer, ThreadedOneSizeMallocStressTest) {
}
TEST(AddressSanitizer, MemsetWildAddressTest) {
+ using __asan::kHighMemEnd;
typedef void*(*memset_p)(void*, int, size_t);
// Prevent inlining of memset().
volatile memset_p libc_memset = (memset_p)memset;
EXPECT_DEATH(libc_memset((void*)(kLowShadowBeg + 200), 0, 100),
- "unknown-crash.*low shadow");
+ (kLowShadowEnd == 0) ? "unknown-crash.*shadow gap"
+ : "unknown-crash.*low shadow");
EXPECT_DEATH(libc_memset((void*)(kShadowGapBeg + 200), 0, 100),
"unknown-crash.*shadow gap");
EXPECT_DEATH(libc_memset((void*)(kHighShadowBeg + 200), 0, 100),
@@ -338,11 +347,7 @@ TEST(AddressSanitizer, MemsetWildAddressTest) {
}
TEST(AddressSanitizerInterface, GetEstimatedAllocatedSize) {
-#if ASAN_ALLOCATOR_VERSION == 1
- EXPECT_EQ(1U, __asan_get_estimated_allocated_size(0));
-#elif ASAN_ALLOCATOR_VERSION == 2
EXPECT_EQ(0U, __asan_get_estimated_allocated_size(0));
-#endif
const size_t sizes[] = { 1, 30, 1<<30 };
for (size_t i = 0; i < 3; i++) {
EXPECT_EQ(sizes[i], __asan_get_estimated_allocated_size(sizes[i]));
@@ -416,44 +421,12 @@ static void DoDoubleFree() {
delete Ident(x);
}
-#if ASAN_ALLOCATOR_VERSION == 1
-// This test is run in a separate process, so that large malloced
-// chunk won't remain in the free lists after the test.
-// Note: use ASSERT_* instead of EXPECT_* here.
-static void RunGetHeapSizeTestAndDie() {
- size_t old_heap_size, new_heap_size, heap_growth;
- // We unlikely have have chunk of this size in free list.
- static const size_t kLargeMallocSize = 1 << 29; // 512M
- old_heap_size = __asan_get_heap_size();
- fprintf(stderr, "allocating %zu bytes:\n", kLargeMallocSize);
- free(Ident(malloc(kLargeMallocSize)));
- new_heap_size = __asan_get_heap_size();
- heap_growth = new_heap_size - old_heap_size;
- fprintf(stderr, "heap growth after first malloc: %zu\n", heap_growth);
- ASSERT_GE(heap_growth, kLargeMallocSize);
- ASSERT_LE(heap_growth, 2 * kLargeMallocSize);
-
- // Now large chunk should fall into free list, and can be
- // allocated without increasing heap size.
- old_heap_size = new_heap_size;
- free(Ident(malloc(kLargeMallocSize)));
- heap_growth = __asan_get_heap_size() - old_heap_size;
- fprintf(stderr, "heap growth after second malloc: %zu\n", heap_growth);
- ASSERT_LT(heap_growth, kLargeMallocSize);
-
- // Test passed. Now die with expected double-free.
- DoDoubleFree();
-}
-
-TEST(AddressSanitizerInterface, GetHeapSizeTest) {
- EXPECT_DEATH(RunGetHeapSizeTestAndDie(), "double-free");
-}
-#elif ASAN_ALLOCATOR_VERSION == 2
TEST(AddressSanitizerInterface, GetHeapSizeTest) {
// asan_allocator2 does not keep huge chunks in free list, but unmaps them.
// The chunk should be greater than the quarantine size,
// otherwise it will be stuck in quarantine instead of being unmaped.
- static const size_t kLargeMallocSize = 1 << 29; // 512M
+ static const size_t kLargeMallocSize = (1 << 28) + 1; // 256M
+ free(Ident(malloc(kLargeMallocSize))); // Drain quarantine.
uptr old_heap_size = __asan_get_heap_size();
for (int i = 0; i < 3; i++) {
// fprintf(stderr, "allocating %zu bytes:\n", kLargeMallocSize);
@@ -461,55 +434,6 @@ TEST(AddressSanitizerInterface, GetHeapSizeTest) {
EXPECT_EQ(old_heap_size, __asan_get_heap_size());
}
}
-#endif
-
-// Note: use ASSERT_* instead of EXPECT_* here.
-static void DoLargeMallocForGetFreeBytesTestAndDie() {
-#if ASAN_ALLOCATOR_VERSION == 1
- // asan_allocator2 does not keep large chunks in free_lists, so this test
- // will not work.
- size_t old_free_bytes, new_free_bytes;
- static const size_t kLargeMallocSize = 1 << 29; // 512M
- // If we malloc and free a large memory chunk, it will not fall
- // into quarantine and will be available for future requests.
- old_free_bytes = __asan_get_free_bytes();
- fprintf(stderr, "allocating %zu bytes:\n", kLargeMallocSize);
- fprintf(stderr, "free bytes before malloc: %zu\n", old_free_bytes);
- free(Ident(malloc(kLargeMallocSize)));
- new_free_bytes = __asan_get_free_bytes();
- fprintf(stderr, "free bytes after malloc and free: %zu\n", new_free_bytes);
- ASSERT_GE(new_free_bytes, old_free_bytes + kLargeMallocSize);
-#endif // ASAN_ALLOCATOR_VERSION
- // Test passed.
- DoDoubleFree();
-}
-
-TEST(AddressSanitizerInterface, GetFreeBytesTest) {
-#if ASAN_ALLOCATOR_VERSION == 1
- // Allocate a small chunk. Now allocator probably has a lot of these
- // chunks to fulfill future requests. So, future requests will decrease
- // the number of free bytes. Do this only on systems where there
- // is enough memory for such assumptions.
- if (SANITIZER_WORDSIZE == 64 && !ASAN_LOW_MEMORY) {
- static const size_t kNumOfChunks = 100;
- static const size_t kChunkSize = 100;
- char *chunks[kNumOfChunks];
- size_t i;
- size_t old_free_bytes, new_free_bytes;
- chunks[0] = Ident((char*)malloc(kChunkSize));
- old_free_bytes = __asan_get_free_bytes();
- for (i = 1; i < kNumOfChunks; i++) {
- chunks[i] = Ident((char*)malloc(kChunkSize));
- new_free_bytes = __asan_get_free_bytes();
- EXPECT_LT(new_free_bytes, old_free_bytes);
- old_free_bytes = new_free_bytes;
- }
- for (i = 0; i < kNumOfChunks; i++)
- free(chunks[i]);
- }
-#endif
- EXPECT_DEATH(DoLargeMallocForGetFreeBytesTestAndDie(), "double-free");
-}
static const size_t kManyThreadsMallocSizes[] = {5, 1UL<<10, 1UL<<14, 357};
static const size_t kManyThreadsIterations = 250;
@@ -631,6 +555,53 @@ TEST(AddressSanitizerInterface, PushAndPopWithPoisoningTest) {
free(vec);
}
+TEST(AddressSanitizerInterface, GlobalRedzones) {
+ GOOD_ACCESS(glob1, 1 - 1);
+ GOOD_ACCESS(glob2, 2 - 1);
+ GOOD_ACCESS(glob3, 3 - 1);
+ GOOD_ACCESS(glob4, 4 - 1);
+ GOOD_ACCESS(glob5, 5 - 1);
+ GOOD_ACCESS(glob6, 6 - 1);
+ GOOD_ACCESS(glob7, 7 - 1);
+ GOOD_ACCESS(glob8, 8 - 1);
+ GOOD_ACCESS(glob9, 9 - 1);
+ GOOD_ACCESS(glob10, 10 - 1);
+ GOOD_ACCESS(glob11, 11 - 1);
+ GOOD_ACCESS(glob12, 12 - 1);
+ GOOD_ACCESS(glob13, 13 - 1);
+ GOOD_ACCESS(glob14, 14 - 1);
+ GOOD_ACCESS(glob15, 15 - 1);
+ GOOD_ACCESS(glob16, 16 - 1);
+ GOOD_ACCESS(glob17, 17 - 1);
+ GOOD_ACCESS(glob1000, 1000 - 1);
+ GOOD_ACCESS(glob10000, 10000 - 1);
+ GOOD_ACCESS(glob100000, 100000 - 1);
+
+ BAD_ACCESS(glob1, 1);
+ BAD_ACCESS(glob2, 2);
+ BAD_ACCESS(glob3, 3);
+ BAD_ACCESS(glob4, 4);
+ BAD_ACCESS(glob5, 5);
+ BAD_ACCESS(glob6, 6);
+ BAD_ACCESS(glob7, 7);
+ BAD_ACCESS(glob8, 8);
+ BAD_ACCESS(glob9, 9);
+ BAD_ACCESS(glob10, 10);
+ BAD_ACCESS(glob11, 11);
+ BAD_ACCESS(glob12, 12);
+ BAD_ACCESS(glob13, 13);
+ BAD_ACCESS(glob14, 14);
+ BAD_ACCESS(glob15, 15);
+ BAD_ACCESS(glob16, 16);
+ BAD_ACCESS(glob17, 17);
+ BAD_ACCESS(glob1000, 1000);
+ BAD_ACCESS(glob1000, 1100); // Redzone is at least 101 bytes.
+ BAD_ACCESS(glob10000, 10000);
+ BAD_ACCESS(glob10000, 11000); // Redzone is at least 1001 bytes.
+ BAD_ACCESS(glob100000, 100000);
+ BAD_ACCESS(glob100000, 110000); // Redzone is at least 10001 bytes.
+}
+
// Make sure that each aligned block of size "2^granularity" doesn't have
// "true" value before "false" value.
static void MakeShadowValid(bool *shadow, int length, int granularity) {
@@ -715,7 +686,7 @@ TEST(AddressSanitizerInterface, PoisonedRegion) {
// 10.50% [.] __sanitizer::mem_is_zero
// I.e. mem_is_zero should consume ~ SHADOW_GRANULARITY less CPU cycles
// than memset itself.
-TEST(AddressSanitizerInterface, DISABLED_Stress_memset) {
+TEST(AddressSanitizerInterface, DISABLED_StressLargeMemset) {
size_t size = 1 << 20;
char *x = new char[size];
for (int i = 0; i < 100000; i++)
@@ -723,6 +694,15 @@ TEST(AddressSanitizerInterface, DISABLED_Stress_memset) {
delete [] x;
}
+// Same here, but we run memset with small sizes.
+TEST(AddressSanitizerInterface, DISABLED_StressSmallMemset) {
+ size_t size = 32;
+ char *x = new char[size];
+ for (int i = 0; i < 100000000; i++)
+ Ident(memset)(x, 0, size);
+ delete [] x;
+}
+
static const char *kInvalidPoisonMessage = "invalid-poison-memory-range";
static const char *kInvalidUnpoisonMessage = "invalid-unpoison-memory-range";
@@ -761,12 +741,7 @@ TEST(AddressSanitizerInterface, SetErrorReportCallbackTest) {
TEST(AddressSanitizerInterface, GetOwnershipStressTest) {
std::vector<char *> pointers;
std::vector<size_t> sizes;
-#if ASAN_ALLOCATOR_VERSION == 1
- const size_t kNumMallocs =
- (SANITIZER_WORDSIZE <= 32 || ASAN_LOW_MEMORY) ? 1 << 10 : 1 << 14;
-#elif ASAN_ALLOCATOR_VERSION == 2 // too slow with asan_allocator2. :(
const size_t kNumMallocs = 1 << 9;
-#endif
for (size_t i = 0; i < kNumMallocs; i++) {
size_t size = i * 100 + 1;
pointers.push_back((char*)malloc(size));
@@ -782,3 +757,38 @@ TEST(AddressSanitizerInterface, GetOwnershipStressTest) {
for (size_t i = 0, n = pointers.size(); i < n; i++)
free(pointers[i]);
}
+
+TEST(AddressSanitizerInterface, CallocOverflow) {
+ size_t kArraySize = 4096;
+ volatile size_t kMaxSizeT = std::numeric_limits<size_t>::max();
+ volatile size_t kArraySize2 = kMaxSizeT / kArraySize + 10;
+ void *p = calloc(kArraySize, kArraySize2); // Should return 0.
+ EXPECT_EQ(0L, Ident(p));
+}
+
+TEST(AddressSanitizerInterface, CallocOverflow2) {
+#if SANITIZER_WORDSIZE == 32
+ size_t kArraySize = 112;
+ volatile size_t kArraySize2 = 43878406;
+ void *p = calloc(kArraySize, kArraySize2); // Should return 0.
+ EXPECT_EQ(0L, Ident(p));
+#endif
+}
+
+TEST(AddressSanitizerInterface, CallocReturnsZeroMem) {
+ size_t sizes[] = {16, 1000, 10000, 100000, 2100000};
+ for (size_t s = 0; s < ARRAY_SIZE(sizes); s++) {
+ size_t size = sizes[s];
+ for (size_t iter = 0; iter < 5; iter++) {
+ char *x = Ident((char*)calloc(1, size));
+ EXPECT_EQ(x[0], 0);
+ EXPECT_EQ(x[size - 1], 0);
+ EXPECT_EQ(x[size / 2], 0);
+ EXPECT_EQ(x[size / 3], 0);
+ EXPECT_EQ(x[size / 4], 0);
+ memset(x, 0x42, size);
+ free(Ident(x));
+ free(Ident(malloc(Ident(1 << 27)))); // Try to drain the quarantine.
+ }
+ }
+}
diff --git a/lib/asan/tests/asan_oob_test.cc b/lib/asan/tests/asan_oob_test.cc
new file mode 100644
index 000000000000..f8343f19cfcb
--- /dev/null
+++ b/lib/asan/tests/asan_oob_test.cc
@@ -0,0 +1,126 @@
+//===-- asan_oob_test.cc --------------------------------------------------===//
+//
+// 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.
+//
+//===----------------------------------------------------------------------===//
+#include "asan_test_utils.h"
+
+NOINLINE void asan_write_sized_aligned(uint8_t *p, size_t size) {
+ EXPECT_EQ(0U, ((uintptr_t)p % size));
+ if (size == 1) asan_write((uint8_t*)p);
+ else if (size == 2) asan_write((uint16_t*)p);
+ else if (size == 4) asan_write((uint32_t*)p);
+ else if (size == 8) asan_write((uint64_t*)p);
+}
+
+template<typename T>
+NOINLINE void oob_test(int size, int off) {
+ char *p = (char*)malloc_aaa(size);
+ // fprintf(stderr, "writing %d byte(s) into [%p,%p) with offset %d\n",
+ // sizeof(T), p, p + size, off);
+ asan_write((T*)(p + off));
+ free_aaa(p);
+}
+
+template<typename T>
+void OOBTest() {
+ char expected_str[100];
+ for (int size = sizeof(T); size < 20; size += 5) {
+ for (int i = -5; i < 0; i++) {
+ const char *str =
+ "is located.*%d byte.*to the left";
+ sprintf(expected_str, str, abs(i));
+ EXPECT_DEATH(oob_test<T>(size, i), expected_str);
+ }
+
+ for (int i = 0; i < (int)(size - sizeof(T) + 1); i++)
+ oob_test<T>(size, i);
+
+ for (int i = size - sizeof(T) + 1; i <= (int)(size + 2 * sizeof(T)); i++) {
+ const char *str =
+ "is located.*%d byte.*to the right";
+ int off = i >= size ? (i - size) : 0;
+ // we don't catch unaligned partially OOB accesses.
+ if (i % sizeof(T)) continue;
+ sprintf(expected_str, str, off);
+ EXPECT_DEATH(oob_test<T>(size, i), expected_str);
+ }
+ }
+
+ EXPECT_DEATH(oob_test<T>(kLargeMalloc, -1),
+ "is located.*1 byte.*to the left");
+ EXPECT_DEATH(oob_test<T>(kLargeMalloc, kLargeMalloc),
+ "is located.*0 byte.*to the right");
+}
+
+// TODO(glider): the following tests are EXTREMELY slow on Darwin:
+// AddressSanitizer.OOB_char (125503 ms)
+// AddressSanitizer.OOB_int (126890 ms)
+// AddressSanitizer.OOBRightTest (315605 ms)
+// AddressSanitizer.SimpleStackTest (366559 ms)
+
+TEST(AddressSanitizer, OOB_char) {
+ OOBTest<U1>();
+}
+
+TEST(AddressSanitizer, OOB_int) {
+ OOBTest<U4>();
+}
+
+TEST(AddressSanitizer, OOBRightTest) {
+ for (size_t access_size = 1; access_size <= 8; access_size *= 2) {
+ for (size_t alloc_size = 1; alloc_size <= 8; alloc_size++) {
+ for (size_t offset = 0; offset <= 8; offset += access_size) {
+ void *p = malloc(alloc_size);
+ // allocated: [p, p + alloc_size)
+ // accessed: [p + offset, p + offset + access_size)
+ uint8_t *addr = (uint8_t*)p + offset;
+ if (offset + access_size <= alloc_size) {
+ asan_write_sized_aligned(addr, access_size);
+ } else {
+ int outside_bytes = offset > alloc_size ? (offset - alloc_size) : 0;
+ const char *str =
+ "is located.%d *byte.*to the right";
+ char expected_str[100];
+ sprintf(expected_str, str, outside_bytes);
+ EXPECT_DEATH(asan_write_sized_aligned(addr, access_size),
+ expected_str);
+ }
+ free(p);
+ }
+ }
+ }
+}
+
+TEST(AddressSanitizer, LargeOOBRightTest) {
+ size_t large_power_of_two = 1 << 19;
+ for (size_t i = 16; i <= 256; i *= 2) {
+ size_t size = large_power_of_two - i;
+ char *p = Ident(new char[size]);
+ EXPECT_DEATH(p[size] = 0, "is located 0 bytes to the right");
+ delete [] p;
+ }
+}
+
+TEST(AddressSanitizer, DISABLED_DemoOOBLeftLow) {
+ oob_test<U1>(10, -1);
+}
+
+TEST(AddressSanitizer, DISABLED_DemoOOBLeftHigh) {
+ oob_test<U1>(kLargeMalloc, -1);
+}
+
+TEST(AddressSanitizer, DISABLED_DemoOOBRightLow) {
+ oob_test<U1>(10, 10);
+}
+
+TEST(AddressSanitizer, DISABLED_DemoOOBRightHigh) {
+ oob_test<U1>(kLargeMalloc, kLargeMalloc);
+}
diff --git a/lib/asan/tests/asan_str_test.cc b/lib/asan/tests/asan_str_test.cc
new file mode 100644
index 000000000000..128fb61c25a9
--- /dev/null
+++ b/lib/asan/tests/asan_str_test.cc
@@ -0,0 +1,572 @@
+//=-- asan_str_test.cc ----------------------------------------------------===//
+//
+// 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.
+//
+//===----------------------------------------------------------------------===//
+#include "asan_test_utils.h"
+
+// Used for string functions tests
+static char global_string[] = "global";
+static size_t global_string_length = 6;
+
+// Input to a test is a zero-terminated string str with given length
+// Accesses to the bytes to the left and to the right of str
+// are presumed to produce OOB errors
+void StrLenOOBTestTemplate(char *str, size_t length, bool is_global) {
+ // Normal strlen calls
+ EXPECT_EQ(strlen(str), length);
+ if (length > 0) {
+ EXPECT_EQ(length - 1, strlen(str + 1));
+ EXPECT_EQ(0U, strlen(str + length));
+ }
+ // Arg of strlen is not malloced, OOB access
+ if (!is_global) {
+ // We don't insert RedZones to the left of global variables
+ EXPECT_DEATH(Ident(strlen(str - 1)), LeftOOBReadMessage(1));
+ EXPECT_DEATH(Ident(strlen(str - 5)), LeftOOBReadMessage(5));
+ }
+ EXPECT_DEATH(Ident(strlen(str + length + 1)), RightOOBReadMessage(0));
+ // Overwrite terminator
+ str[length] = 'a';
+ // String is not zero-terminated, strlen will lead to OOB access
+ EXPECT_DEATH(Ident(strlen(str)), RightOOBReadMessage(0));
+ EXPECT_DEATH(Ident(strlen(str + length)), RightOOBReadMessage(0));
+ // Restore terminator
+ str[length] = 0;
+}
+TEST(AddressSanitizer, StrLenOOBTest) {
+ // Check heap-allocated string
+ size_t length = Ident(10);
+ char *heap_string = Ident((char*)malloc(length + 1));
+ char stack_string[10 + 1];
+ break_optimization(&stack_string);
+ for (size_t i = 0; i < length; i++) {
+ heap_string[i] = 'a';
+ stack_string[i] = 'b';
+ }
+ heap_string[length] = 0;
+ stack_string[length] = 0;
+ StrLenOOBTestTemplate(heap_string, length, false);
+ // TODO(samsonov): Fix expected messages in StrLenOOBTestTemplate to
+ // make test for stack_string work. Or move it to output tests.
+ // StrLenOOBTestTemplate(stack_string, length, false);
+ StrLenOOBTestTemplate(global_string, global_string_length, true);
+ free(heap_string);
+}
+
+#ifndef __APPLE__
+TEST(AddressSanitizer, StrNLenOOBTest) {
+ size_t size = Ident(123);
+ char *str = MallocAndMemsetString(size);
+ // Normal strnlen calls.
+ Ident(strnlen(str - 1, 0));
+ Ident(strnlen(str, size));
+ Ident(strnlen(str + size - 1, 1));
+ str[size - 1] = '\0';
+ Ident(strnlen(str, 2 * size));
+ // Argument points to not allocated memory.
+ EXPECT_DEATH(Ident(strnlen(str - 1, 1)), LeftOOBReadMessage(1));
+ EXPECT_DEATH(Ident(strnlen(str + size, 1)), RightOOBReadMessage(0));
+ // Overwrite the terminating '\0' and hit unallocated memory.
+ str[size - 1] = 'z';
+ EXPECT_DEATH(Ident(strnlen(str, size + 1)), RightOOBReadMessage(0));
+ free(str);
+}
+#endif
+
+TEST(AddressSanitizer, StrDupOOBTest) {
+ size_t size = Ident(42);
+ char *str = MallocAndMemsetString(size);
+ char *new_str;
+ // Normal strdup calls.
+ str[size - 1] = '\0';
+ new_str = strdup(str);
+ free(new_str);
+ new_str = strdup(str + size - 1);
+ free(new_str);
+ // Argument points to not allocated memory.
+ EXPECT_DEATH(Ident(strdup(str - 1)), LeftOOBReadMessage(1));
+ EXPECT_DEATH(Ident(strdup(str + size)), RightOOBReadMessage(0));
+ // Overwrite the terminating '\0' and hit unallocated memory.
+ str[size - 1] = 'z';
+ EXPECT_DEATH(Ident(strdup(str)), RightOOBReadMessage(0));
+ free(str);
+}
+
+TEST(AddressSanitizer, StrCpyOOBTest) {
+ size_t to_size = Ident(30);
+ size_t from_size = Ident(6); // less than to_size
+ char *to = Ident((char*)malloc(to_size));
+ char *from = Ident((char*)malloc(from_size));
+ // Normal strcpy calls.
+ strcpy(from, "hello");
+ strcpy(to, from);
+ strcpy(to + to_size - from_size, from);
+ // Length of "from" is too small.
+ EXPECT_DEATH(Ident(strcpy(from, "hello2")), RightOOBWriteMessage(0));
+ // "to" or "from" points to not allocated memory.
+ EXPECT_DEATH(Ident(strcpy(to - 1, from)), LeftOOBWriteMessage(1));
+ EXPECT_DEATH(Ident(strcpy(to, from - 1)), LeftOOBReadMessage(1));
+ EXPECT_DEATH(Ident(strcpy(to, from + from_size)), RightOOBReadMessage(0));
+ EXPECT_DEATH(Ident(strcpy(to + to_size, from)), RightOOBWriteMessage(0));
+ // Overwrite the terminating '\0' character and hit unallocated memory.
+ from[from_size - 1] = '!';
+ EXPECT_DEATH(Ident(strcpy(to, from)), RightOOBReadMessage(0));
+ free(to);
+ free(from);
+}
+
+TEST(AddressSanitizer, StrNCpyOOBTest) {
+ size_t to_size = Ident(20);
+ size_t from_size = Ident(6); // less than to_size
+ char *to = Ident((char*)malloc(to_size));
+ // From is a zero-terminated string "hello\0" of length 6
+ char *from = Ident((char*)malloc(from_size));
+ strcpy(from, "hello");
+ // copy 0 bytes
+ strncpy(to, from, 0);
+ strncpy(to - 1, from - 1, 0);
+ // normal strncpy calls
+ strncpy(to, from, from_size);
+ strncpy(to, from, to_size);
+ strncpy(to, from + from_size - 1, to_size);
+ strncpy(to + to_size - 1, from, 1);
+ // One of {to, from} points to not allocated memory
+ EXPECT_DEATH(Ident(strncpy(to, from - 1, from_size)),
+ LeftOOBReadMessage(1));
+ EXPECT_DEATH(Ident(strncpy(to - 1, from, from_size)),
+ LeftOOBWriteMessage(1));
+ EXPECT_DEATH(Ident(strncpy(to, from + from_size, 1)),
+ RightOOBReadMessage(0));
+ EXPECT_DEATH(Ident(strncpy(to + to_size, from, 1)),
+ RightOOBWriteMessage(0));
+ // Length of "to" is too small
+ EXPECT_DEATH(Ident(strncpy(to + to_size - from_size + 1, from, from_size)),
+ RightOOBWriteMessage(0));
+ EXPECT_DEATH(Ident(strncpy(to + 1, from, to_size)),
+ RightOOBWriteMessage(0));
+ // Overwrite terminator in from
+ from[from_size - 1] = '!';
+ // normal strncpy call
+ strncpy(to, from, from_size);
+ // Length of "from" is too small
+ EXPECT_DEATH(Ident(strncpy(to, from, to_size)),
+ RightOOBReadMessage(0));
+ free(to);
+ free(from);
+}
+
+// Users may have different definitions of "strchr" and "index", so provide
+// function pointer typedefs and overload RunStrChrTest implementation.
+// We can't use macro for RunStrChrTest body here, as this macro would
+// confuse EXPECT_DEATH gtest macro.
+typedef char*(*PointerToStrChr1)(const char*, int);
+typedef char*(*PointerToStrChr2)(char*, int);
+
+USED static void RunStrChrTest(PointerToStrChr1 StrChr) {
+ size_t size = Ident(100);
+ char *str = MallocAndMemsetString(size);
+ str[10] = 'q';
+ str[11] = '\0';
+ EXPECT_EQ(str, StrChr(str, 'z'));
+ EXPECT_EQ(str + 10, StrChr(str, 'q'));
+ EXPECT_EQ(NULL, StrChr(str, 'a'));
+ // StrChr argument points to not allocated memory.
+ EXPECT_DEATH(Ident(StrChr(str - 1, 'z')), LeftOOBReadMessage(1));
+ EXPECT_DEATH(Ident(StrChr(str + size, 'z')), RightOOBReadMessage(0));
+ // Overwrite the terminator and hit not allocated memory.
+ str[11] = 'z';
+ EXPECT_DEATH(Ident(StrChr(str, 'a')), RightOOBReadMessage(0));
+ free(str);
+}
+USED static void RunStrChrTest(PointerToStrChr2 StrChr) {
+ size_t size = Ident(100);
+ char *str = MallocAndMemsetString(size);
+ str[10] = 'q';
+ str[11] = '\0';
+ EXPECT_EQ(str, StrChr(str, 'z'));
+ EXPECT_EQ(str + 10, StrChr(str, 'q'));
+ EXPECT_EQ(NULL, StrChr(str, 'a'));
+ // StrChr argument points to not allocated memory.
+ EXPECT_DEATH(Ident(StrChr(str - 1, 'z')), LeftOOBReadMessage(1));
+ EXPECT_DEATH(Ident(StrChr(str + size, 'z')), RightOOBReadMessage(0));
+ // Overwrite the terminator and hit not allocated memory.
+ str[11] = 'z';
+ EXPECT_DEATH(Ident(StrChr(str, 'a')), RightOOBReadMessage(0));
+ free(str);
+}
+
+TEST(AddressSanitizer, StrChrAndIndexOOBTest) {
+ RunStrChrTest(&strchr);
+ RunStrChrTest(&index);
+}
+
+TEST(AddressSanitizer, StrCmpAndFriendsLogicTest) {
+ // strcmp
+ EXPECT_EQ(0, strcmp("", ""));
+ EXPECT_EQ(0, strcmp("abcd", "abcd"));
+ EXPECT_GT(0, strcmp("ab", "ac"));
+ EXPECT_GT(0, strcmp("abc", "abcd"));
+ EXPECT_LT(0, strcmp("acc", "abc"));
+ EXPECT_LT(0, strcmp("abcd", "abc"));
+
+ // strncmp
+ EXPECT_EQ(0, strncmp("a", "b", 0));
+ EXPECT_EQ(0, strncmp("abcd", "abcd", 10));
+ EXPECT_EQ(0, strncmp("abcd", "abcef", 3));
+ EXPECT_GT(0, strncmp("abcde", "abcfa", 4));
+ EXPECT_GT(0, strncmp("a", "b", 5));
+ EXPECT_GT(0, strncmp("bc", "bcde", 4));
+ EXPECT_LT(0, strncmp("xyz", "xyy", 10));
+ EXPECT_LT(0, strncmp("baa", "aaa", 1));
+ EXPECT_LT(0, strncmp("zyx", "", 2));
+
+ // strcasecmp
+ EXPECT_EQ(0, strcasecmp("", ""));
+ EXPECT_EQ(0, strcasecmp("zzz", "zzz"));
+ EXPECT_EQ(0, strcasecmp("abCD", "ABcd"));
+ EXPECT_GT(0, strcasecmp("aB", "Ac"));
+ EXPECT_GT(0, strcasecmp("ABC", "ABCd"));
+ EXPECT_LT(0, strcasecmp("acc", "abc"));
+ EXPECT_LT(0, strcasecmp("ABCd", "abc"));
+
+ // strncasecmp
+ EXPECT_EQ(0, strncasecmp("a", "b", 0));
+ EXPECT_EQ(0, strncasecmp("abCD", "ABcd", 10));
+ EXPECT_EQ(0, strncasecmp("abCd", "ABcef", 3));
+ EXPECT_GT(0, strncasecmp("abcde", "ABCfa", 4));
+ EXPECT_GT(0, strncasecmp("a", "B", 5));
+ EXPECT_GT(0, strncasecmp("bc", "BCde", 4));
+ EXPECT_LT(0, strncasecmp("xyz", "xyy", 10));
+ EXPECT_LT(0, strncasecmp("Baa", "aaa", 1));
+ EXPECT_LT(0, strncasecmp("zyx", "", 2));
+
+ // memcmp
+ EXPECT_EQ(0, memcmp("a", "b", 0));
+ EXPECT_EQ(0, memcmp("ab\0c", "ab\0c", 4));
+ EXPECT_GT(0, memcmp("\0ab", "\0ac", 3));
+ EXPECT_GT(0, memcmp("abb\0", "abba", 4));
+ EXPECT_LT(0, memcmp("ab\0cd", "ab\0c\0", 5));
+ EXPECT_LT(0, memcmp("zza", "zyx", 3));
+}
+
+typedef int(*PointerToStrCmp)(const char*, const char*);
+void RunStrCmpTest(PointerToStrCmp StrCmp) {
+ size_t size = Ident(100);
+ int fill = 'o';
+ char *s1 = MallocAndMemsetString(size, fill);
+ char *s2 = MallocAndMemsetString(size, fill);
+ s1[size - 1] = '\0';
+ s2[size - 1] = '\0';
+ // Normal StrCmp calls
+ Ident(StrCmp(s1, s2));
+ Ident(StrCmp(s1, s2 + size - 1));
+ Ident(StrCmp(s1 + size - 1, s2 + size - 1));
+ s1[size - 1] = 'z';
+ s2[size - 1] = 'x';
+ Ident(StrCmp(s1, s2));
+ // One of arguments points to not allocated memory.
+ EXPECT_DEATH(Ident(StrCmp)(s1 - 1, s2), LeftOOBReadMessage(1));
+ EXPECT_DEATH(Ident(StrCmp)(s1, s2 - 1), LeftOOBReadMessage(1));
+ EXPECT_DEATH(Ident(StrCmp)(s1 + size, s2), RightOOBReadMessage(0));
+ EXPECT_DEATH(Ident(StrCmp)(s1, s2 + size), RightOOBReadMessage(0));
+ // Hit unallocated memory and die.
+ s1[size - 1] = fill;
+ EXPECT_DEATH(Ident(StrCmp)(s1, s1), RightOOBReadMessage(0));
+ EXPECT_DEATH(Ident(StrCmp)(s1 + size - 1, s2), RightOOBReadMessage(0));
+ free(s1);
+ free(s2);
+}
+
+TEST(AddressSanitizer, StrCmpOOBTest) {
+ RunStrCmpTest(&strcmp);
+}
+
+TEST(AddressSanitizer, StrCaseCmpOOBTest) {
+ RunStrCmpTest(&strcasecmp);
+}
+
+typedef int(*PointerToStrNCmp)(const char*, const char*, size_t);
+void RunStrNCmpTest(PointerToStrNCmp StrNCmp) {
+ size_t size = Ident(100);
+ char *s1 = MallocAndMemsetString(size);
+ char *s2 = MallocAndMemsetString(size);
+ s1[size - 1] = '\0';
+ s2[size - 1] = '\0';
+ // Normal StrNCmp calls
+ Ident(StrNCmp(s1, s2, size + 2));
+ s1[size - 1] = 'z';
+ s2[size - 1] = 'x';
+ Ident(StrNCmp(s1 + size - 2, s2 + size - 2, size));
+ s2[size - 1] = 'z';
+ Ident(StrNCmp(s1 - 1, s2 - 1, 0));
+ Ident(StrNCmp(s1 + size - 1, s2 + size - 1, 1));
+ // One of arguments points to not allocated memory.
+ EXPECT_DEATH(Ident(StrNCmp)(s1 - 1, s2, 1), LeftOOBReadMessage(1));
+ EXPECT_DEATH(Ident(StrNCmp)(s1, s2 - 1, 1), LeftOOBReadMessage(1));
+ EXPECT_DEATH(Ident(StrNCmp)(s1 + size, s2, 1), RightOOBReadMessage(0));
+ EXPECT_DEATH(Ident(StrNCmp)(s1, s2 + size, 1), RightOOBReadMessage(0));
+ // Hit unallocated memory and die.
+ EXPECT_DEATH(Ident(StrNCmp)(s1 + 1, s2 + 1, size), RightOOBReadMessage(0));
+ EXPECT_DEATH(Ident(StrNCmp)(s1 + size - 1, s2, 2), RightOOBReadMessage(0));
+ free(s1);
+ free(s2);
+}
+
+TEST(AddressSanitizer, StrNCmpOOBTest) {
+ RunStrNCmpTest(&strncmp);
+}
+
+TEST(AddressSanitizer, StrNCaseCmpOOBTest) {
+ RunStrNCmpTest(&strncasecmp);
+}
+TEST(AddressSanitizer, StrCatOOBTest) {
+ // strcat() reads strlen(to) bytes from |to| before concatenating.
+ size_t to_size = Ident(100);
+ char *to = MallocAndMemsetString(to_size);
+ to[0] = '\0';
+ size_t from_size = Ident(20);
+ char *from = MallocAndMemsetString(from_size);
+ from[from_size - 1] = '\0';
+ // Normal strcat calls.
+ strcat(to, from);
+ strcat(to, from);
+ strcat(to + from_size, from + from_size - 2);
+ // Passing an invalid pointer is an error even when concatenating an empty
+ // string.
+ EXPECT_DEATH(strcat(to - 1, from + from_size - 1), LeftOOBAccessMessage(1));
+ // One of arguments points to not allocated memory.
+ EXPECT_DEATH(strcat(to - 1, from), LeftOOBAccessMessage(1));
+ EXPECT_DEATH(strcat(to, from - 1), LeftOOBReadMessage(1));
+ EXPECT_DEATH(strcat(to + to_size, from), RightOOBWriteMessage(0));
+ EXPECT_DEATH(strcat(to, from + from_size), RightOOBReadMessage(0));
+
+ // "from" is not zero-terminated.
+ from[from_size - 1] = 'z';
+ EXPECT_DEATH(strcat(to, from), RightOOBReadMessage(0));
+ from[from_size - 1] = '\0';
+ // "to" is not zero-terminated.
+ memset(to, 'z', to_size);
+ EXPECT_DEATH(strcat(to, from), RightOOBWriteMessage(0));
+ // "to" is too short to fit "from".
+ to[to_size - from_size + 1] = '\0';
+ EXPECT_DEATH(strcat(to, from), RightOOBWriteMessage(0));
+ // length of "to" is just enough.
+ strcat(to, from + 1);
+
+ free(to);
+ free(from);
+}
+
+TEST(AddressSanitizer, StrNCatOOBTest) {
+ // strncat() reads strlen(to) bytes from |to| before concatenating.
+ size_t to_size = Ident(100);
+ char *to = MallocAndMemsetString(to_size);
+ to[0] = '\0';
+ size_t from_size = Ident(20);
+ char *from = MallocAndMemsetString(from_size);
+ // Normal strncat calls.
+ strncat(to, from, 0);
+ strncat(to, from, from_size);
+ from[from_size - 1] = '\0';
+ strncat(to, from, 2 * from_size);
+ // Catenating empty string with an invalid string is still an error.
+ EXPECT_DEATH(strncat(to - 1, from, 0), LeftOOBAccessMessage(1));
+ strncat(to, from + from_size - 1, 10);
+ // One of arguments points to not allocated memory.
+ EXPECT_DEATH(strncat(to - 1, from, 2), LeftOOBAccessMessage(1));
+ EXPECT_DEATH(strncat(to, from - 1, 2), LeftOOBReadMessage(1));
+ EXPECT_DEATH(strncat(to + to_size, from, 2), RightOOBWriteMessage(0));
+ EXPECT_DEATH(strncat(to, from + from_size, 2), RightOOBReadMessage(0));
+
+ memset(from, 'z', from_size);
+ memset(to, 'z', to_size);
+ to[0] = '\0';
+ // "from" is too short.
+ EXPECT_DEATH(strncat(to, from, from_size + 1), RightOOBReadMessage(0));
+ // "to" is not zero-terminated.
+ EXPECT_DEATH(strncat(to + 1, from, 1), RightOOBWriteMessage(0));
+ // "to" is too short to fit "from".
+ to[0] = 'z';
+ to[to_size - from_size + 1] = '\0';
+ EXPECT_DEATH(strncat(to, from, from_size - 1), RightOOBWriteMessage(0));
+ // "to" is just enough.
+ strncat(to, from, from_size - 2);
+
+ free(to);
+ free(from);
+}
+
+static string OverlapErrorMessage(const string &func) {
+ return func + "-param-overlap";
+}
+
+TEST(AddressSanitizer, StrArgsOverlapTest) {
+ size_t size = Ident(100);
+ char *str = Ident((char*)malloc(size));
+
+// Do not check memcpy() on OS X 10.7 and later, where it actually aliases
+// memmove().
+#if !defined(__APPLE__) || !defined(MAC_OS_X_VERSION_10_7) || \
+ (MAC_OS_X_VERSION_MAX_ALLOWED < MAC_OS_X_VERSION_10_7)
+ // Check "memcpy". Use Ident() to avoid inlining.
+ memset(str, 'z', size);
+ Ident(memcpy)(str + 1, str + 11, 10);
+ Ident(memcpy)(str, str, 0);
+ EXPECT_DEATH(Ident(memcpy)(str, str + 14, 15), OverlapErrorMessage("memcpy"));
+ EXPECT_DEATH(Ident(memcpy)(str + 14, str, 15), OverlapErrorMessage("memcpy"));
+#endif
+
+ // We do not treat memcpy with to==from as a bug.
+ // See http://llvm.org/bugs/show_bug.cgi?id=11763.
+ // EXPECT_DEATH(Ident(memcpy)(str + 20, str + 20, 1),
+ // OverlapErrorMessage("memcpy"));
+
+ // Check "strcpy".
+ memset(str, 'z', size);
+ str[9] = '\0';
+ strcpy(str + 10, str);
+ EXPECT_DEATH(strcpy(str + 9, str), OverlapErrorMessage("strcpy"));
+ EXPECT_DEATH(strcpy(str, str + 4), OverlapErrorMessage("strcpy"));
+ strcpy(str, str + 5);
+
+ // Check "strncpy".
+ memset(str, 'z', size);
+ strncpy(str, str + 10, 10);
+ EXPECT_DEATH(strncpy(str, str + 9, 10), OverlapErrorMessage("strncpy"));
+ EXPECT_DEATH(strncpy(str + 9, str, 10), OverlapErrorMessage("strncpy"));
+ str[10] = '\0';
+ strncpy(str + 11, str, 20);
+ EXPECT_DEATH(strncpy(str + 10, str, 20), OverlapErrorMessage("strncpy"));
+
+ // Check "strcat".
+ memset(str, 'z', size);
+ str[10] = '\0';
+ str[20] = '\0';
+ strcat(str, str + 10);
+ EXPECT_DEATH(strcat(str, str + 11), OverlapErrorMessage("strcat"));
+ str[10] = '\0';
+ strcat(str + 11, str);
+ EXPECT_DEATH(strcat(str, str + 9), OverlapErrorMessage("strcat"));
+ EXPECT_DEATH(strcat(str + 9, str), OverlapErrorMessage("strcat"));
+ EXPECT_DEATH(strcat(str + 10, str), OverlapErrorMessage("strcat"));
+
+ // Check "strncat".
+ memset(str, 'z', size);
+ str[10] = '\0';
+ strncat(str, str + 10, 10); // from is empty
+ EXPECT_DEATH(strncat(str, str + 11, 10), OverlapErrorMessage("strncat"));
+ str[10] = '\0';
+ str[20] = '\0';
+ strncat(str + 5, str, 5);
+ str[10] = '\0';
+ EXPECT_DEATH(strncat(str + 5, str, 6), OverlapErrorMessage("strncat"));
+ EXPECT_DEATH(strncat(str, str + 9, 10), OverlapErrorMessage("strncat"));
+
+ free(str);
+}
+
+void CallAtoi(const char *nptr) {
+ Ident(atoi(nptr));
+}
+void CallAtol(const char *nptr) {
+ Ident(atol(nptr));
+}
+void CallAtoll(const char *nptr) {
+ Ident(atoll(nptr));
+}
+typedef void(*PointerToCallAtoi)(const char*);
+
+void RunAtoiOOBTest(PointerToCallAtoi Atoi) {
+ char *array = MallocAndMemsetString(10, '1');
+ // Invalid pointer to the string.
+ EXPECT_DEATH(Atoi(array + 11), RightOOBReadMessage(1));
+ EXPECT_DEATH(Atoi(array - 1), LeftOOBReadMessage(1));
+ // Die if a buffer doesn't have terminating NULL.
+ EXPECT_DEATH(Atoi(array), RightOOBReadMessage(0));
+ // Make last symbol a terminating NULL or other non-digit.
+ array[9] = '\0';
+ Atoi(array);
+ array[9] = 'a';
+ Atoi(array);
+ Atoi(array + 9);
+ // Sometimes we need to detect overflow if no digits are found.
+ memset(array, ' ', 10);
+ EXPECT_DEATH(Atoi(array), RightOOBReadMessage(0));
+ array[9] = '-';
+ EXPECT_DEATH(Atoi(array), RightOOBReadMessage(0));
+ EXPECT_DEATH(Atoi(array + 9), RightOOBReadMessage(0));
+ array[8] = '-';
+ Atoi(array);
+ free(array);
+}
+
+TEST(AddressSanitizer, AtoiAndFriendsOOBTest) {
+ RunAtoiOOBTest(&CallAtoi);
+ RunAtoiOOBTest(&CallAtol);
+ RunAtoiOOBTest(&CallAtoll);
+}
+
+void CallStrtol(const char *nptr, char **endptr, int base) {
+ Ident(strtol(nptr, endptr, base));
+}
+void CallStrtoll(const char *nptr, char **endptr, int base) {
+ Ident(strtoll(nptr, endptr, base));
+}
+typedef void(*PointerToCallStrtol)(const char*, char**, int);
+
+void RunStrtolOOBTest(PointerToCallStrtol Strtol) {
+ char *array = MallocAndMemsetString(3);
+ char *endptr = NULL;
+ array[0] = '1';
+ array[1] = '2';
+ array[2] = '3';
+ // Invalid pointer to the string.
+ EXPECT_DEATH(Strtol(array + 3, NULL, 0), RightOOBReadMessage(0));
+ EXPECT_DEATH(Strtol(array - 1, NULL, 0), LeftOOBReadMessage(1));
+ // Buffer overflow if there is no terminating null (depends on base).
+ Strtol(array, &endptr, 3);
+ EXPECT_EQ(array + 2, endptr);
+ EXPECT_DEATH(Strtol(array, NULL, 0), RightOOBReadMessage(0));
+ array[2] = 'z';
+ Strtol(array, &endptr, 35);
+ EXPECT_EQ(array + 2, endptr);
+ EXPECT_DEATH(Strtol(array, NULL, 36), RightOOBReadMessage(0));
+ // Add terminating zero to get rid of overflow.
+ array[2] = '\0';
+ Strtol(array, NULL, 36);
+ // Don't check for overflow if base is invalid.
+ Strtol(array - 1, NULL, -1);
+ Strtol(array + 3, NULL, 1);
+ // Sometimes we need to detect overflow if no digits are found.
+ array[0] = array[1] = array[2] = ' ';
+ EXPECT_DEATH(Strtol(array, NULL, 0), RightOOBReadMessage(0));
+ array[2] = '+';
+ EXPECT_DEATH(Strtol(array, NULL, 0), RightOOBReadMessage(0));
+ array[2] = '-';
+ EXPECT_DEATH(Strtol(array, NULL, 0), RightOOBReadMessage(0));
+ array[1] = '+';
+ Strtol(array, NULL, 0);
+ array[1] = array[2] = 'z';
+ Strtol(array, &endptr, 0);
+ EXPECT_EQ(array, endptr);
+ Strtol(array + 2, NULL, 0);
+ EXPECT_EQ(array, endptr);
+ free(array);
+}
+
+TEST(AddressSanitizer, StrtollOOBTest) {
+ RunStrtolOOBTest(&CallStrtoll);
+}
+TEST(AddressSanitizer, StrtolOOBTest) {
+ RunStrtolOOBTest(&CallStrtol);
+}
+
+
diff --git a/lib/asan/tests/asan_test.cc b/lib/asan/tests/asan_test.cc
index 5fa65b2af5dc..5ae525de9876 100644
--- a/lib/asan/tests/asan_test.cc
+++ b/lib/asan/tests/asan_test.cc
@@ -10,69 +10,8 @@
// This file is a part of AddressSanitizer, an address sanity checker.
//
//===----------------------------------------------------------------------===//
-#include <stdio.h>
-#include <signal.h>
-#include <stdlib.h>
-#include <string.h>
-#include <strings.h>
-#include <pthread.h>
-#include <stdint.h>
-#include <setjmp.h>
-#include <assert.h>
-#include <algorithm>
-
-#ifdef __linux__
-# include <sys/prctl.h>
-# include <sys/types.h>
-# include <sys/stat.h>
-# include <fcntl.h>
-#include <unistd.h>
-#endif
-
-#if defined(__i386__) || defined(__x86_64__)
-#include <emmintrin.h>
-#endif
-
#include "asan_test_utils.h"
-#ifndef __APPLE__
-#include <malloc.h>
-#else
-#include <malloc/malloc.h>
-#include <AvailabilityMacros.h> // For MAC_OS_X_VERSION_*
-#include <CoreFoundation/CFString.h>
-#endif // __APPLE__
-
-#if ASAN_HAS_EXCEPTIONS
-# define ASAN_THROW(x) throw (x)
-#else
-# define ASAN_THROW(x)
-#endif
-
-#include <sys/mman.h>
-
-typedef uint8_t U1;
-typedef uint16_t U2;
-typedef uint32_t U4;
-typedef uint64_t U8;
-
-static const int kPageSize = 4096;
-
-const size_t kLargeMalloc = 1 << 24;
-
-template<typename T>
-NOINLINE void asan_write(T *a) {
- *a = 0;
-}
-
-NOINLINE void asan_write_sized_aligned(uint8_t *p, size_t size) {
- EXPECT_EQ(0U, ((uintptr_t)p % size));
- if (size == 1) asan_write((uint8_t*)p);
- else if (size == 2) asan_write((uint16_t*)p);
- else if (size == 4) asan_write((uint32_t*)p);
- else if (size == 8) asan_write((uint64_t*)p);
-}
-
NOINLINE void *malloc_fff(size_t size) {
void *res = malloc/**/(size); break_optimization(0); return res;}
NOINLINE void *malloc_eee(size_t size) {
@@ -106,15 +45,6 @@ NOINLINE void free_ccc(void *p) { free(p); break_optimization(0);}
NOINLINE void free_bbb(void *p) { free_ccc(p); break_optimization(0);}
NOINLINE void free_aaa(void *p) { free_bbb(p); break_optimization(0);}
-template<typename T>
-NOINLINE void oob_test(int size, int off) {
- char *p = (char*)malloc_aaa(size);
- // fprintf(stderr, "writing %d byte(s) into [%p,%p) with offset %d\n",
- // sizeof(T), p, p + size, off);
- asan_write((T*)(p + off));
- free_aaa(p);
-}
-
template<typename T>
NOINLINE void uaf_test(int size, int off) {
@@ -231,88 +161,6 @@ TEST(AddressSanitizer, DISABLED_TSDTest) {
pthread_key_delete(test_key);
}
-template<typename T>
-void OOBTest() {
- char expected_str[100];
- for (int size = sizeof(T); size < 20; size += 5) {
- for (int i = -5; i < 0; i++) {
- const char *str =
- "is located.*%d byte.*to the left";
- sprintf(expected_str, str, abs(i));
- EXPECT_DEATH(oob_test<T>(size, i), expected_str);
- }
-
- for (int i = 0; i < (int)(size - sizeof(T) + 1); i++)
- oob_test<T>(size, i);
-
- for (int i = size - sizeof(T) + 1; i <= (int)(size + 2 * sizeof(T)); i++) {
- const char *str =
- "is located.*%d byte.*to the right";
- int off = i >= size ? (i - size) : 0;
- // we don't catch unaligned partially OOB accesses.
- if (i % sizeof(T)) continue;
- sprintf(expected_str, str, off);
- EXPECT_DEATH(oob_test<T>(size, i), expected_str);
- }
- }
-
- EXPECT_DEATH(oob_test<T>(kLargeMalloc, -1),
- "is located.*1 byte.*to the left");
- EXPECT_DEATH(oob_test<T>(kLargeMalloc, kLargeMalloc),
- "is located.*0 byte.*to the right");
-}
-
-// TODO(glider): the following tests are EXTREMELY slow on Darwin:
-// AddressSanitizer.OOB_char (125503 ms)
-// AddressSanitizer.OOB_int (126890 ms)
-// AddressSanitizer.OOBRightTest (315605 ms)
-// AddressSanitizer.SimpleStackTest (366559 ms)
-
-TEST(AddressSanitizer, OOB_char) {
- OOBTest<U1>();
-}
-
-TEST(AddressSanitizer, OOB_int) {
- OOBTest<U4>();
-}
-
-TEST(AddressSanitizer, OOBRightTest) {
- for (size_t access_size = 1; access_size <= 8; access_size *= 2) {
- for (size_t alloc_size = 1; alloc_size <= 8; alloc_size++) {
- for (size_t offset = 0; offset <= 8; offset += access_size) {
- void *p = malloc(alloc_size);
- // allocated: [p, p + alloc_size)
- // accessed: [p + offset, p + offset + access_size)
- uint8_t *addr = (uint8_t*)p + offset;
- if (offset + access_size <= alloc_size) {
- asan_write_sized_aligned(addr, access_size);
- } else {
- int outside_bytes = offset > alloc_size ? (offset - alloc_size) : 0;
- const char *str =
- "is located.%d *byte.*to the right";
- char expected_str[100];
- sprintf(expected_str, str, outside_bytes);
- EXPECT_DEATH(asan_write_sized_aligned(addr, access_size),
- expected_str);
- }
- free(p);
- }
- }
- }
-}
-
-#if ASAN_ALLOCATOR_VERSION == 2 // Broken with the asan_allocator1
-TEST(AddressSanitizer, LargeOOBRightTest) {
- size_t large_power_of_two = 1 << 19;
- for (size_t i = 16; i <= 256; i *= 2) {
- size_t size = large_power_of_two - i;
- char *p = Ident(new char[size]);
- EXPECT_DEATH(p[size] = 0, "is located 0 bytes to the right");
- delete [] p;
- }
-}
-#endif // ASAN_ALLOCATOR_VERSION == 2
-
TEST(AddressSanitizer, UAF_char) {
const char *uaf_string = "AddressSanitizer:.*heap-use-after-free";
EXPECT_DEATH(uaf_test<U1>(1, 0), uaf_string);
@@ -322,6 +170,27 @@ TEST(AddressSanitizer, UAF_char) {
EXPECT_DEATH(uaf_test<U1>(kLargeMalloc, kLargeMalloc / 2), uaf_string);
}
+TEST(AddressSanitizer, UAF_long_double) {
+ if (sizeof(long double) == sizeof(double)) return;
+ long double *p = Ident(new long double[10]);
+ EXPECT_DEATH(Ident(p)[12] = 0, "WRITE of size 1[06]");
+ EXPECT_DEATH(Ident(p)[0] = Ident(p)[12], "READ of size 1[06]");
+ delete [] Ident(p);
+}
+
+struct Packed5 {
+ int x;
+ char c;
+} __attribute__((packed));
+
+
+TEST(AddressSanitizer, UAF_Packed5) {
+ Packed5 *p = Ident(new Packed5[2]);
+ EXPECT_DEATH(p[0] = p[3], "READ of size 5");
+ EXPECT_DEATH(p[3] = p[0], "WRITE of size 5");
+ delete [] Ident(p);
+}
+
#if ASAN_HAS_BLACKLIST
TEST(AddressSanitizer, IgnoreTest) {
int *x = Ident(new int);
@@ -408,41 +277,6 @@ TEST(AddressSanitizer, SignalTest) {
} // namespace
#endif
-static void MallocStress(size_t n) {
- uint32_t seed = my_rand();
- for (size_t iter = 0; iter < 10; iter++) {
- vector<void *> vec;
- for (size_t i = 0; i < n; i++) {
- if ((i % 3) == 0) {
- if (vec.empty()) continue;
- size_t idx = my_rand_r(&seed) % vec.size();
- void *ptr = vec[idx];
- vec[idx] = vec.back();
- vec.pop_back();
- free_aaa(ptr);
- } else {
- size_t size = my_rand_r(&seed) % 1000 + 1;
-#ifndef __APPLE__
- size_t alignment = 1 << (my_rand_r(&seed) % 7 + 3);
- char *ptr = (char*)memalign_aaa(alignment, size);
-#else
- char *ptr = (char*) malloc_aaa(size);
-#endif
- vec.push_back(ptr);
- ptr[0] = 0;
- ptr[size-1] = 0;
- ptr[size/2] = 0;
- }
- }
- for (size_t i = 0; i < vec.size(); i++)
- free_aaa(vec[i]);
- }
-}
-
-TEST(AddressSanitizer, MallocStressTest) {
- MallocStress((ASAN_LOW_MEMORY) ? 20000 : 200000);
-}
-
static void TestLargeMalloc(size_t size) {
char buff[1024];
sprintf(buff, "is located 1 bytes to the left of %lu-byte", (long)size);
@@ -450,26 +284,17 @@ static void TestLargeMalloc(size_t size) {
}
TEST(AddressSanitizer, LargeMallocTest) {
- for (int i = 113; i < (1 << 28); i = i * 2 + 13) {
+ const int max_size = (SANITIZER_WORDSIZE == 32) ? 1 << 26 : 1 << 28;
+ for (int i = 113; i < max_size; i = i * 2 + 13) {
TestLargeMalloc(i);
}
}
-#if ASAN_LOW_MEMORY != 1
TEST(AddressSanitizer, HugeMallocTest) {
-#ifdef __APPLE__
- // It was empirically found out that 1215 megabytes is the maximum amount of
- // memory available to the process under AddressSanitizer on 32-bit Mac 10.6.
- // 32-bit Mac 10.7 gives even less (< 1G).
- // (the libSystem malloc() allows allocating up to 2300 megabytes without
- // ASan).
- size_t n_megs = SANITIZER_WORDSIZE == 32 ? 500 : 4100;
-#else
- size_t n_megs = SANITIZER_WORDSIZE == 32 ? 2600 : 4100;
-#endif
+ if (SANITIZER_WORDSIZE != 64) return;
+ size_t n_megs = 4100;
TestLargeMalloc(n_megs << 20);
}
-#endif
#ifndef __APPLE__
void MemalignRun(size_t align, size_t size, int idx) {
@@ -489,19 +314,6 @@ TEST(AddressSanitizer, memalign) {
}
#endif
-TEST(AddressSanitizer, ThreadedMallocStressTest) {
- const int kNumThreads = 4;
- const int kNumIterations = (ASAN_LOW_MEMORY) ? 10000 : 100000;
- pthread_t t[kNumThreads];
- for (int i = 0; i < kNumThreads; i++) {
- PTHREAD_CREATE(&t[i], 0, (void* (*)(void *x))MallocStress,
- (void*)kNumIterations);
- }
- for (int i = 0; i < kNumThreads; i++) {
- PTHREAD_JOIN(t[i], 0);
- }
-}
-
void *ManyThreadsWorker(void *a) {
for (int iter = 0; iter < 100; iter++) {
for (size_t size = 100; size < 2000; size *= 2) {
@@ -532,6 +344,45 @@ TEST(AddressSanitizer, ReallocTest) {
(my_rand() % 1000 + kMinElem) * sizeof(int));
EXPECT_EQ(3, ptr[3]);
}
+ free(ptr);
+ // Realloc pointer returned by malloc(0).
+ int *ptr2 = Ident((int*)malloc(0));
+ ptr2 = Ident((int*)realloc(ptr2, sizeof(*ptr2)));
+ *ptr2 = 42;
+ EXPECT_EQ(42, *ptr2);
+ free(ptr2);
+}
+
+TEST(AddressSanitizer, ReallocFreedPointerTest) {
+ void *ptr = Ident(malloc(42));
+ ASSERT_TRUE(NULL != ptr);
+ free(ptr);
+ EXPECT_DEATH(ptr = realloc(ptr, 77), "attempting double-free");
+}
+
+TEST(AddressSanitizer, ReallocInvalidPointerTest) {
+ void *ptr = Ident(malloc(42));
+ EXPECT_DEATH(ptr = realloc((int*)ptr + 1, 77), "attempting free.*not malloc");
+}
+
+TEST(AddressSanitizer, ZeroSizeMallocTest) {
+ // Test that malloc(0) and similar functions don't return NULL.
+ void *ptr = Ident(malloc(0));
+ EXPECT_TRUE(NULL != ptr);
+ free(ptr);
+#if !defined(__APPLE__) && !defined(ANDROID) && !defined(__ANDROID__)
+ int pm_res = posix_memalign(&ptr, 1<<20, 0);
+ EXPECT_EQ(0, pm_res);
+ EXPECT_TRUE(NULL != ptr);
+ free(ptr);
+#endif
+ int *int_ptr = new int[0];
+ int *int_ptr2 = new int[0];
+ EXPECT_TRUE(NULL != int_ptr);
+ EXPECT_TRUE(NULL != int_ptr2);
+ EXPECT_NE(int_ptr, int_ptr2);
+ delete[] int_ptr;
+ delete[] int_ptr2;
}
#ifndef __APPLE__
@@ -561,8 +412,10 @@ void WrongFree() {
}
TEST(AddressSanitizer, WrongFreeTest) {
- EXPECT_DEATH(WrongFree(),
- "ERROR: AddressSanitizer: attempting free.*not malloc");
+ EXPECT_DEATH(WrongFree(), ASAN_PCRE_DOTALL
+ "ERROR: AddressSanitizer: attempting free.*not malloc"
+ ".*is located 4 bytes inside of 400-byte region"
+ ".*allocated by thread");
}
void DoubleFree() {
@@ -624,6 +477,9 @@ TEST(AddressSanitizer, ManyStackObjectsTest) {
EXPECT_DEATH(Ident(ZZZ)[-1] = 0, ASAN_PCRE_DOTALL "XXX.*YYY.*ZZZ");
}
+#if 0 // This test requires online symbolizer.
+// Moved to lit_tests/stack-oob-frames.cc.
+// Reenable here once we have online symbolizer by default.
NOINLINE static void Frame0(int frame, char *a, char *b, char *c) {
char d[4] = {0};
char *D = Ident(d);
@@ -659,6 +515,7 @@ TEST(AddressSanitizer, GuiltyStackFrame2Test) {
TEST(AddressSanitizer, GuiltyStackFrame3Test) {
EXPECT_DEATH(Frame3(3), "located .*in frame <.*Frame3");
}
+#endif
NOINLINE void LongJmpFunc1(jmp_buf buf) {
// create three red zones for these two stack objects.
@@ -722,7 +579,10 @@ TEST(AddressSanitizer, LongJmpTest) {
}
}
-#if not defined(__ANDROID__)
+#if !defined(__ANDROID__) && \
+ !defined(__powerpc64__) && !defined(__powerpc__)
+// Does not work on Power:
+// https://code.google.com/p/address-sanitizer/issues/detail?id=185
TEST(AddressSanitizer, BuiltinLongJmpTest) {
static jmp_buf buf;
if (!__builtin_setjmp((void**)buf)) {
@@ -817,23 +677,24 @@ TEST(AddressSanitizer, Store128Test) {
}
#endif
-static string RightOOBErrorMessage(int oob_distance, bool is_write) {
+string RightOOBErrorMessage(int oob_distance, bool is_write) {
assert(oob_distance >= 0);
char expected_str[100];
- sprintf(expected_str, ASAN_PCRE_DOTALL "%s.*located %d bytes to the right",
+ sprintf(expected_str, ASAN_PCRE_DOTALL
+ "buffer-overflow.*%s.*located %d bytes to the right",
is_write ? "WRITE" : "READ", oob_distance);
return string(expected_str);
}
-static string RightOOBWriteMessage(int oob_distance) {
+string RightOOBWriteMessage(int oob_distance) {
return RightOOBErrorMessage(oob_distance, /*is_write*/true);
}
-static string RightOOBReadMessage(int oob_distance) {
+string RightOOBReadMessage(int oob_distance) {
return RightOOBErrorMessage(oob_distance, /*is_write*/false);
}
-static string LeftOOBErrorMessage(int oob_distance, bool is_write) {
+string LeftOOBErrorMessage(int oob_distance, bool is_write) {
assert(oob_distance > 0);
char expected_str[100];
sprintf(expected_str, ASAN_PCRE_DOTALL "%s.*located %d bytes to the left",
@@ -841,830 +702,29 @@ static string LeftOOBErrorMessage(int oob_distance, bool is_write) {
return string(expected_str);
}
-static string LeftOOBWriteMessage(int oob_distance) {
+string LeftOOBWriteMessage(int oob_distance) {
return LeftOOBErrorMessage(oob_distance, /*is_write*/true);
}
-static string LeftOOBReadMessage(int oob_distance) {
+string LeftOOBReadMessage(int oob_distance) {
return LeftOOBErrorMessage(oob_distance, /*is_write*/false);
}
-static string LeftOOBAccessMessage(int oob_distance) {
+string LeftOOBAccessMessage(int oob_distance) {
assert(oob_distance > 0);
char expected_str[100];
sprintf(expected_str, "located %d bytes to the left", oob_distance);
return string(expected_str);
}
-template<typename T>
-void MemSetOOBTestTemplate(size_t length) {
- if (length == 0) return;
- size_t size = Ident(sizeof(T) * length);
- T *array = Ident((T*)malloc(size));
- int element = Ident(42);
- int zero = Ident(0);
- void *(*MEMSET)(void *s, int c, size_t n) = Ident(memset);
- // memset interval inside array
- MEMSET(array, element, size);
- MEMSET(array, element, size - 1);
- MEMSET(array + length - 1, element, sizeof(T));
- MEMSET(array, element, 1);
-
- // memset 0 bytes
- MEMSET(array - 10, element, zero);
- MEMSET(array - 1, element, zero);
- MEMSET(array, element, zero);
- MEMSET(array + length, 0, zero);
- MEMSET(array + length + 1, 0, zero);
-
- // try to memset bytes to the right of array
- EXPECT_DEATH(MEMSET(array, 0, size + 1),
- RightOOBWriteMessage(0));
- EXPECT_DEATH(MEMSET((char*)(array + length) - 1, element, 6),
- RightOOBWriteMessage(0));
- EXPECT_DEATH(MEMSET(array + 1, element, size + sizeof(T)),
- RightOOBWriteMessage(0));
- // whole interval is to the right
- EXPECT_DEATH(MEMSET(array + length + 1, 0, 10),
- RightOOBWriteMessage(sizeof(T)));
-
- // try to memset bytes to the left of array
- EXPECT_DEATH(MEMSET((char*)array - 1, element, size),
- LeftOOBWriteMessage(1));
- EXPECT_DEATH(MEMSET((char*)array - 5, 0, 6),
- LeftOOBWriteMessage(5));
- if (length >= 100) {
- // Large OOB, we find it only if the redzone is large enough.
- EXPECT_DEATH(memset(array - 5, element, size + 5 * sizeof(T)),
- LeftOOBWriteMessage(5 * sizeof(T)));
- }
- // whole interval is to the left
- EXPECT_DEATH(MEMSET(array - 2, 0, sizeof(T)),
- LeftOOBWriteMessage(2 * sizeof(T)));
-
- // try to memset bytes both to the left & to the right
- EXPECT_DEATH(MEMSET((char*)array - 2, element, size + 4),
- LeftOOBWriteMessage(2));
-
- free(array);
-}
-
-TEST(AddressSanitizer, MemSetOOBTest) {
- MemSetOOBTestTemplate<char>(100);
- MemSetOOBTestTemplate<int>(5);
- MemSetOOBTestTemplate<double>(256);
- // We can test arrays of structres/classes here, but what for?
-}
-
-// Try to allocate two arrays of 'size' bytes that are near each other.
-// Strictly speaking we are not guaranteed to find such two pointers,
-// but given the structure of asan's allocator we will.
-static bool AllocateTwoAdjacentArrays(char **x1, char **x2, size_t size) {
- vector<char *> v;
- bool res = false;
- for (size_t i = 0; i < 1000U && !res; i++) {
- v.push_back(new char[size]);
- if (i == 0) continue;
- sort(v.begin(), v.end());
- for (size_t j = 1; j < v.size(); j++) {
- assert(v[j] > v[j-1]);
- if ((size_t)(v[j] - v[j-1]) < size * 2) {
- *x2 = v[j];
- *x1 = v[j-1];
- res = true;
- break;
- }
- }
- }
-
- for (size_t i = 0; i < v.size(); i++) {
- if (res && v[i] == *x1) continue;
- if (res && v[i] == *x2) continue;
- delete [] v[i];
- }
- return res;
-}
-
-TEST(AddressSanitizer, LargeOOBInMemset) {
- for (size_t size = 200; size < 100000; size += size / 2) {
- char *x1, *x2;
- if (!Ident(AllocateTwoAdjacentArrays)(&x1, &x2, size))
- continue;
- // fprintf(stderr, " large oob memset: %p %p %zd\n", x1, x2, size);
- // Do a memset on x1 with huge out-of-bound access that will end up in x2.
- EXPECT_DEATH(Ident(memset)(x1, 0, size * 2),
- "is located 0 bytes to the right");
- delete [] x1;
- delete [] x2;
- return;
- }
- assert(0 && "Did not find two adjacent malloc-ed pointers");
-}
-
-// Same test for memcpy and memmove functions
-template <typename T, class M>
-void MemTransferOOBTestTemplate(size_t length) {
- if (length == 0) return;
- size_t size = Ident(sizeof(T) * length);
- T *src = Ident((T*)malloc(size));
- T *dest = Ident((T*)malloc(size));
- int zero = Ident(0);
-
- // valid transfer of bytes between arrays
- M::transfer(dest, src, size);
- M::transfer(dest + 1, src, size - sizeof(T));
- M::transfer(dest, src + length - 1, sizeof(T));
- M::transfer(dest, src, 1);
-
- // transfer zero bytes
- M::transfer(dest - 1, src, 0);
- M::transfer(dest + length, src, zero);
- M::transfer(dest, src - 1, zero);
- M::transfer(dest, src, zero);
-
- // try to change mem to the right of dest
- EXPECT_DEATH(M::transfer(dest + 1, src, size),
- RightOOBWriteMessage(0));
- EXPECT_DEATH(M::transfer((char*)(dest + length) - 1, src, 5),
- RightOOBWriteMessage(0));
-
- // try to change mem to the left of dest
- EXPECT_DEATH(M::transfer(dest - 2, src, size),
- LeftOOBWriteMessage(2 * sizeof(T)));
- EXPECT_DEATH(M::transfer((char*)dest - 3, src, 4),
- LeftOOBWriteMessage(3));
-
- // try to access mem to the right of src
- EXPECT_DEATH(M::transfer(dest, src + 2, size),
- RightOOBReadMessage(0));
- EXPECT_DEATH(M::transfer(dest, (char*)(src + length) - 3, 6),
- RightOOBReadMessage(0));
-
- // try to access mem to the left of src
- EXPECT_DEATH(M::transfer(dest, src - 1, size),
- LeftOOBReadMessage(sizeof(T)));
- EXPECT_DEATH(M::transfer(dest, (char*)src - 6, 7),
- LeftOOBReadMessage(6));
-
- // Generally we don't need to test cases where both accessing src and writing
- // to dest address to poisoned memory.
-
- T *big_src = Ident((T*)malloc(size * 2));
- T *big_dest = Ident((T*)malloc(size * 2));
- // try to change mem to both sides of dest
- EXPECT_DEATH(M::transfer(dest - 1, big_src, size * 2),
- LeftOOBWriteMessage(sizeof(T)));
- // try to access mem to both sides of src
- EXPECT_DEATH(M::transfer(big_dest, src - 2, size * 2),
- LeftOOBReadMessage(2 * sizeof(T)));
-
- free(src);
- free(dest);
- free(big_src);
- free(big_dest);
-}
-
-class MemCpyWrapper {
- public:
- static void* transfer(void *to, const void *from, size_t size) {
- return Ident(memcpy)(to, from, size);
- }
-};
-TEST(AddressSanitizer, MemCpyOOBTest) {
- MemTransferOOBTestTemplate<char, MemCpyWrapper>(100);
- MemTransferOOBTestTemplate<int, MemCpyWrapper>(1024);
-}
-
-class MemMoveWrapper {
- public:
- static void* transfer(void *to, const void *from, size_t size) {
- return Ident(memmove)(to, from, size);
- }
-};
-TEST(AddressSanitizer, MemMoveOOBTest) {
- MemTransferOOBTestTemplate<char, MemMoveWrapper>(100);
- MemTransferOOBTestTemplate<int, MemMoveWrapper>(1024);
-}
-
-// Tests for string functions
-
-// Used for string functions tests
-static char global_string[] = "global";
-static size_t global_string_length = 6;
-
-// Input to a test is a zero-terminated string str with given length
-// Accesses to the bytes to the left and to the right of str
-// are presumed to produce OOB errors
-void StrLenOOBTestTemplate(char *str, size_t length, bool is_global) {
- // Normal strlen calls
- EXPECT_EQ(strlen(str), length);
- if (length > 0) {
- EXPECT_EQ(length - 1, strlen(str + 1));
- EXPECT_EQ(0U, strlen(str + length));
- }
- // Arg of strlen is not malloced, OOB access
- if (!is_global) {
- // We don't insert RedZones to the left of global variables
- EXPECT_DEATH(Ident(strlen(str - 1)), LeftOOBReadMessage(1));
- EXPECT_DEATH(Ident(strlen(str - 5)), LeftOOBReadMessage(5));
- }
- EXPECT_DEATH(Ident(strlen(str + length + 1)), RightOOBReadMessage(0));
- // Overwrite terminator
- str[length] = 'a';
- // String is not zero-terminated, strlen will lead to OOB access
- EXPECT_DEATH(Ident(strlen(str)), RightOOBReadMessage(0));
- EXPECT_DEATH(Ident(strlen(str + length)), RightOOBReadMessage(0));
- // Restore terminator
- str[length] = 0;
-}
-TEST(AddressSanitizer, StrLenOOBTest) {
- // Check heap-allocated string
- size_t length = Ident(10);
- char *heap_string = Ident((char*)malloc(length + 1));
- char stack_string[10 + 1];
- break_optimization(&stack_string);
- for (size_t i = 0; i < length; i++) {
- heap_string[i] = 'a';
- stack_string[i] = 'b';
- }
- heap_string[length] = 0;
- stack_string[length] = 0;
- StrLenOOBTestTemplate(heap_string, length, false);
- // TODO(samsonov): Fix expected messages in StrLenOOBTestTemplate to
- // make test for stack_string work. Or move it to output tests.
- // StrLenOOBTestTemplate(stack_string, length, false);
- StrLenOOBTestTemplate(global_string, global_string_length, true);
- free(heap_string);
-}
-
-static inline char* MallocAndMemsetString(size_t size, char ch) {
+char* MallocAndMemsetString(size_t size, char ch) {
char *s = Ident((char*)malloc(size));
memset(s, ch, size);
return s;
}
-static inline char* MallocAndMemsetString(size_t size) {
- return MallocAndMemsetString(size, 'z');
-}
-
-#ifndef __APPLE__
-TEST(AddressSanitizer, StrNLenOOBTest) {
- size_t size = Ident(123);
- char *str = MallocAndMemsetString(size);
- // Normal strnlen calls.
- Ident(strnlen(str - 1, 0));
- Ident(strnlen(str, size));
- Ident(strnlen(str + size - 1, 1));
- str[size - 1] = '\0';
- Ident(strnlen(str, 2 * size));
- // Argument points to not allocated memory.
- EXPECT_DEATH(Ident(strnlen(str - 1, 1)), LeftOOBReadMessage(1));
- EXPECT_DEATH(Ident(strnlen(str + size, 1)), RightOOBReadMessage(0));
- // Overwrite the terminating '\0' and hit unallocated memory.
- str[size - 1] = 'z';
- EXPECT_DEATH(Ident(strnlen(str, size + 1)), RightOOBReadMessage(0));
- free(str);
-}
-#endif
-
-TEST(AddressSanitizer, StrDupOOBTest) {
- size_t size = Ident(42);
- char *str = MallocAndMemsetString(size);
- char *new_str;
- // Normal strdup calls.
- str[size - 1] = '\0';
- new_str = strdup(str);
- free(new_str);
- new_str = strdup(str + size - 1);
- free(new_str);
- // Argument points to not allocated memory.
- EXPECT_DEATH(Ident(strdup(str - 1)), LeftOOBReadMessage(1));
- EXPECT_DEATH(Ident(strdup(str + size)), RightOOBReadMessage(0));
- // Overwrite the terminating '\0' and hit unallocated memory.
- str[size - 1] = 'z';
- EXPECT_DEATH(Ident(strdup(str)), RightOOBReadMessage(0));
- free(str);
-}
-
-TEST(AddressSanitizer, StrCpyOOBTest) {
- size_t to_size = Ident(30);
- size_t from_size = Ident(6); // less than to_size
- char *to = Ident((char*)malloc(to_size));
- char *from = Ident((char*)malloc(from_size));
- // Normal strcpy calls.
- strcpy(from, "hello");
- strcpy(to, from);
- strcpy(to + to_size - from_size, from);
- // Length of "from" is too small.
- EXPECT_DEATH(Ident(strcpy(from, "hello2")), RightOOBWriteMessage(0));
- // "to" or "from" points to not allocated memory.
- EXPECT_DEATH(Ident(strcpy(to - 1, from)), LeftOOBWriteMessage(1));
- EXPECT_DEATH(Ident(strcpy(to, from - 1)), LeftOOBReadMessage(1));
- EXPECT_DEATH(Ident(strcpy(to, from + from_size)), RightOOBReadMessage(0));
- EXPECT_DEATH(Ident(strcpy(to + to_size, from)), RightOOBWriteMessage(0));
- // Overwrite the terminating '\0' character and hit unallocated memory.
- from[from_size - 1] = '!';
- EXPECT_DEATH(Ident(strcpy(to, from)), RightOOBReadMessage(0));
- free(to);
- free(from);
-}
-
-TEST(AddressSanitizer, StrNCpyOOBTest) {
- size_t to_size = Ident(20);
- size_t from_size = Ident(6); // less than to_size
- char *to = Ident((char*)malloc(to_size));
- // From is a zero-terminated string "hello\0" of length 6
- char *from = Ident((char*)malloc(from_size));
- strcpy(from, "hello");
- // copy 0 bytes
- strncpy(to, from, 0);
- strncpy(to - 1, from - 1, 0);
- // normal strncpy calls
- strncpy(to, from, from_size);
- strncpy(to, from, to_size);
- strncpy(to, from + from_size - 1, to_size);
- strncpy(to + to_size - 1, from, 1);
- // One of {to, from} points to not allocated memory
- EXPECT_DEATH(Ident(strncpy(to, from - 1, from_size)),
- LeftOOBReadMessage(1));
- EXPECT_DEATH(Ident(strncpy(to - 1, from, from_size)),
- LeftOOBWriteMessage(1));
- EXPECT_DEATH(Ident(strncpy(to, from + from_size, 1)),
- RightOOBReadMessage(0));
- EXPECT_DEATH(Ident(strncpy(to + to_size, from, 1)),
- RightOOBWriteMessage(0));
- // Length of "to" is too small
- EXPECT_DEATH(Ident(strncpy(to + to_size - from_size + 1, from, from_size)),
- RightOOBWriteMessage(0));
- EXPECT_DEATH(Ident(strncpy(to + 1, from, to_size)),
- RightOOBWriteMessage(0));
- // Overwrite terminator in from
- from[from_size - 1] = '!';
- // normal strncpy call
- strncpy(to, from, from_size);
- // Length of "from" is too small
- EXPECT_DEATH(Ident(strncpy(to, from, to_size)),
- RightOOBReadMessage(0));
- free(to);
- free(from);
-}
-
-// Users may have different definitions of "strchr" and "index", so provide
-// function pointer typedefs and overload RunStrChrTest implementation.
-// We can't use macro for RunStrChrTest body here, as this macro would
-// confuse EXPECT_DEATH gtest macro.
-typedef char*(*PointerToStrChr1)(const char*, int);
-typedef char*(*PointerToStrChr2)(char*, int);
-
-USED static void RunStrChrTest(PointerToStrChr1 StrChr) {
- size_t size = Ident(100);
- char *str = MallocAndMemsetString(size);
- str[10] = 'q';
- str[11] = '\0';
- EXPECT_EQ(str, StrChr(str, 'z'));
- EXPECT_EQ(str + 10, StrChr(str, 'q'));
- EXPECT_EQ(NULL, StrChr(str, 'a'));
- // StrChr argument points to not allocated memory.
- EXPECT_DEATH(Ident(StrChr(str - 1, 'z')), LeftOOBReadMessage(1));
- EXPECT_DEATH(Ident(StrChr(str + size, 'z')), RightOOBReadMessage(0));
- // Overwrite the terminator and hit not allocated memory.
- str[11] = 'z';
- EXPECT_DEATH(Ident(StrChr(str, 'a')), RightOOBReadMessage(0));
- free(str);
-}
-USED static void RunStrChrTest(PointerToStrChr2 StrChr) {
- size_t size = Ident(100);
- char *str = MallocAndMemsetString(size);
- str[10] = 'q';
- str[11] = '\0';
- EXPECT_EQ(str, StrChr(str, 'z'));
- EXPECT_EQ(str + 10, StrChr(str, 'q'));
- EXPECT_EQ(NULL, StrChr(str, 'a'));
- // StrChr argument points to not allocated memory.
- EXPECT_DEATH(Ident(StrChr(str - 1, 'z')), LeftOOBReadMessage(1));
- EXPECT_DEATH(Ident(StrChr(str + size, 'z')), RightOOBReadMessage(0));
- // Overwrite the terminator and hit not allocated memory.
- str[11] = 'z';
- EXPECT_DEATH(Ident(StrChr(str, 'a')), RightOOBReadMessage(0));
- free(str);
-}
-
-TEST(AddressSanitizer, StrChrAndIndexOOBTest) {
- RunStrChrTest(&strchr);
- RunStrChrTest(&index);
-}
-
-TEST(AddressSanitizer, StrCmpAndFriendsLogicTest) {
- // strcmp
- EXPECT_EQ(0, strcmp("", ""));
- EXPECT_EQ(0, strcmp("abcd", "abcd"));
- EXPECT_GT(0, strcmp("ab", "ac"));
- EXPECT_GT(0, strcmp("abc", "abcd"));
- EXPECT_LT(0, strcmp("acc", "abc"));
- EXPECT_LT(0, strcmp("abcd", "abc"));
-
- // strncmp
- EXPECT_EQ(0, strncmp("a", "b", 0));
- EXPECT_EQ(0, strncmp("abcd", "abcd", 10));
- EXPECT_EQ(0, strncmp("abcd", "abcef", 3));
- EXPECT_GT(0, strncmp("abcde", "abcfa", 4));
- EXPECT_GT(0, strncmp("a", "b", 5));
- EXPECT_GT(0, strncmp("bc", "bcde", 4));
- EXPECT_LT(0, strncmp("xyz", "xyy", 10));
- EXPECT_LT(0, strncmp("baa", "aaa", 1));
- EXPECT_LT(0, strncmp("zyx", "", 2));
-
- // strcasecmp
- EXPECT_EQ(0, strcasecmp("", ""));
- EXPECT_EQ(0, strcasecmp("zzz", "zzz"));
- EXPECT_EQ(0, strcasecmp("abCD", "ABcd"));
- EXPECT_GT(0, strcasecmp("aB", "Ac"));
- EXPECT_GT(0, strcasecmp("ABC", "ABCd"));
- EXPECT_LT(0, strcasecmp("acc", "abc"));
- EXPECT_LT(0, strcasecmp("ABCd", "abc"));
-
- // strncasecmp
- EXPECT_EQ(0, strncasecmp("a", "b", 0));
- EXPECT_EQ(0, strncasecmp("abCD", "ABcd", 10));
- EXPECT_EQ(0, strncasecmp("abCd", "ABcef", 3));
- EXPECT_GT(0, strncasecmp("abcde", "ABCfa", 4));
- EXPECT_GT(0, strncasecmp("a", "B", 5));
- EXPECT_GT(0, strncasecmp("bc", "BCde", 4));
- EXPECT_LT(0, strncasecmp("xyz", "xyy", 10));
- EXPECT_LT(0, strncasecmp("Baa", "aaa", 1));
- EXPECT_LT(0, strncasecmp("zyx", "", 2));
-
- // memcmp
- EXPECT_EQ(0, memcmp("a", "b", 0));
- EXPECT_EQ(0, memcmp("ab\0c", "ab\0c", 4));
- EXPECT_GT(0, memcmp("\0ab", "\0ac", 3));
- EXPECT_GT(0, memcmp("abb\0", "abba", 4));
- EXPECT_LT(0, memcmp("ab\0cd", "ab\0c\0", 5));
- EXPECT_LT(0, memcmp("zza", "zyx", 3));
-}
-
-typedef int(*PointerToStrCmp)(const char*, const char*);
-void RunStrCmpTest(PointerToStrCmp StrCmp) {
- size_t size = Ident(100);
- int fill = 'o';
- char *s1 = MallocAndMemsetString(size, fill);
- char *s2 = MallocAndMemsetString(size, fill);
- s1[size - 1] = '\0';
- s2[size - 1] = '\0';
- // Normal StrCmp calls
- Ident(StrCmp(s1, s2));
- Ident(StrCmp(s1, s2 + size - 1));
- Ident(StrCmp(s1 + size - 1, s2 + size - 1));
- s1[size - 1] = 'z';
- s2[size - 1] = 'x';
- Ident(StrCmp(s1, s2));
- // One of arguments points to not allocated memory.
- EXPECT_DEATH(Ident(StrCmp)(s1 - 1, s2), LeftOOBReadMessage(1));
- EXPECT_DEATH(Ident(StrCmp)(s1, s2 - 1), LeftOOBReadMessage(1));
- EXPECT_DEATH(Ident(StrCmp)(s1 + size, s2), RightOOBReadMessage(0));
- EXPECT_DEATH(Ident(StrCmp)(s1, s2 + size), RightOOBReadMessage(0));
- // Hit unallocated memory and die.
- s1[size - 1] = fill;
- EXPECT_DEATH(Ident(StrCmp)(s1, s1), RightOOBReadMessage(0));
- EXPECT_DEATH(Ident(StrCmp)(s1 + size - 1, s2), RightOOBReadMessage(0));
- free(s1);
- free(s2);
-}
-
-TEST(AddressSanitizer, StrCmpOOBTest) {
- RunStrCmpTest(&strcmp);
-}
-
-TEST(AddressSanitizer, StrCaseCmpOOBTest) {
- RunStrCmpTest(&strcasecmp);
-}
-
-typedef int(*PointerToStrNCmp)(const char*, const char*, size_t);
-void RunStrNCmpTest(PointerToStrNCmp StrNCmp) {
- size_t size = Ident(100);
- char *s1 = MallocAndMemsetString(size);
- char *s2 = MallocAndMemsetString(size);
- s1[size - 1] = '\0';
- s2[size - 1] = '\0';
- // Normal StrNCmp calls
- Ident(StrNCmp(s1, s2, size + 2));
- s1[size - 1] = 'z';
- s2[size - 1] = 'x';
- Ident(StrNCmp(s1 + size - 2, s2 + size - 2, size));
- s2[size - 1] = 'z';
- Ident(StrNCmp(s1 - 1, s2 - 1, 0));
- Ident(StrNCmp(s1 + size - 1, s2 + size - 1, 1));
- // One of arguments points to not allocated memory.
- EXPECT_DEATH(Ident(StrNCmp)(s1 - 1, s2, 1), LeftOOBReadMessage(1));
- EXPECT_DEATH(Ident(StrNCmp)(s1, s2 - 1, 1), LeftOOBReadMessage(1));
- EXPECT_DEATH(Ident(StrNCmp)(s1 + size, s2, 1), RightOOBReadMessage(0));
- EXPECT_DEATH(Ident(StrNCmp)(s1, s2 + size, 1), RightOOBReadMessage(0));
- // Hit unallocated memory and die.
- EXPECT_DEATH(Ident(StrNCmp)(s1 + 1, s2 + 1, size), RightOOBReadMessage(0));
- EXPECT_DEATH(Ident(StrNCmp)(s1 + size - 1, s2, 2), RightOOBReadMessage(0));
- free(s1);
- free(s2);
-}
-
-TEST(AddressSanitizer, StrNCmpOOBTest) {
- RunStrNCmpTest(&strncmp);
-}
-
-TEST(AddressSanitizer, StrNCaseCmpOOBTest) {
- RunStrNCmpTest(&strncasecmp);
-}
-
-TEST(AddressSanitizer, MemCmpOOBTest) {
- size_t size = Ident(100);
- char *s1 = MallocAndMemsetString(size);
- char *s2 = MallocAndMemsetString(size);
- // Normal memcmp calls.
- Ident(memcmp(s1, s2, size));
- Ident(memcmp(s1 + size - 1, s2 + size - 1, 1));
- Ident(memcmp(s1 - 1, s2 - 1, 0));
- // One of arguments points to not allocated memory.
- EXPECT_DEATH(Ident(memcmp)(s1 - 1, s2, 1), LeftOOBReadMessage(1));
- EXPECT_DEATH(Ident(memcmp)(s1, s2 - 1, 1), LeftOOBReadMessage(1));
- EXPECT_DEATH(Ident(memcmp)(s1 + size, s2, 1), RightOOBReadMessage(0));
- EXPECT_DEATH(Ident(memcmp)(s1, s2 + size, 1), RightOOBReadMessage(0));
- // Hit unallocated memory and die.
- EXPECT_DEATH(Ident(memcmp)(s1 + 1, s2 + 1, size), RightOOBReadMessage(0));
- EXPECT_DEATH(Ident(memcmp)(s1 + size - 1, s2, 2), RightOOBReadMessage(0));
- // Zero bytes are not terminators and don't prevent from OOB.
- s1[size - 1] = '\0';
- s2[size - 1] = '\0';
- EXPECT_DEATH(Ident(memcmp)(s1, s2, size + 1), RightOOBReadMessage(0));
- free(s1);
- free(s2);
-}
-
-TEST(AddressSanitizer, StrCatOOBTest) {
- // strcat() reads strlen(to) bytes from |to| before concatenating.
- size_t to_size = Ident(100);
- char *to = MallocAndMemsetString(to_size);
- to[0] = '\0';
- size_t from_size = Ident(20);
- char *from = MallocAndMemsetString(from_size);
- from[from_size - 1] = '\0';
- // Normal strcat calls.
- strcat(to, from);
- strcat(to, from);
- strcat(to + from_size, from + from_size - 2);
- // Passing an invalid pointer is an error even when concatenating an empty
- // string.
- EXPECT_DEATH(strcat(to - 1, from + from_size - 1), LeftOOBAccessMessage(1));
- // One of arguments points to not allocated memory.
- EXPECT_DEATH(strcat(to - 1, from), LeftOOBAccessMessage(1));
- EXPECT_DEATH(strcat(to, from - 1), LeftOOBReadMessage(1));
- EXPECT_DEATH(strcat(to + to_size, from), RightOOBWriteMessage(0));
- EXPECT_DEATH(strcat(to, from + from_size), RightOOBReadMessage(0));
-
- // "from" is not zero-terminated.
- from[from_size - 1] = 'z';
- EXPECT_DEATH(strcat(to, from), RightOOBReadMessage(0));
- from[from_size - 1] = '\0';
- // "to" is not zero-terminated.
- memset(to, 'z', to_size);
- EXPECT_DEATH(strcat(to, from), RightOOBWriteMessage(0));
- // "to" is too short to fit "from".
- to[to_size - from_size + 1] = '\0';
- EXPECT_DEATH(strcat(to, from), RightOOBWriteMessage(0));
- // length of "to" is just enough.
- strcat(to, from + 1);
-
- free(to);
- free(from);
-}
-
-TEST(AddressSanitizer, StrNCatOOBTest) {
- // strncat() reads strlen(to) bytes from |to| before concatenating.
- size_t to_size = Ident(100);
- char *to = MallocAndMemsetString(to_size);
- to[0] = '\0';
- size_t from_size = Ident(20);
- char *from = MallocAndMemsetString(from_size);
- // Normal strncat calls.
- strncat(to, from, 0);
- strncat(to, from, from_size);
- from[from_size - 1] = '\0';
- strncat(to, from, 2 * from_size);
- // Catenating empty string with an invalid string is still an error.
- EXPECT_DEATH(strncat(to - 1, from, 0), LeftOOBAccessMessage(1));
- strncat(to, from + from_size - 1, 10);
- // One of arguments points to not allocated memory.
- EXPECT_DEATH(strncat(to - 1, from, 2), LeftOOBAccessMessage(1));
- EXPECT_DEATH(strncat(to, from - 1, 2), LeftOOBReadMessage(1));
- EXPECT_DEATH(strncat(to + to_size, from, 2), RightOOBWriteMessage(0));
- EXPECT_DEATH(strncat(to, from + from_size, 2), RightOOBReadMessage(0));
-
- memset(from, 'z', from_size);
- memset(to, 'z', to_size);
- to[0] = '\0';
- // "from" is too short.
- EXPECT_DEATH(strncat(to, from, from_size + 1), RightOOBReadMessage(0));
- // "to" is not zero-terminated.
- EXPECT_DEATH(strncat(to + 1, from, 1), RightOOBWriteMessage(0));
- // "to" is too short to fit "from".
- to[0] = 'z';
- to[to_size - from_size + 1] = '\0';
- EXPECT_DEATH(strncat(to, from, from_size - 1), RightOOBWriteMessage(0));
- // "to" is just enough.
- strncat(to, from, from_size - 2);
-
- free(to);
- free(from);
-}
-
-static string OverlapErrorMessage(const string &func) {
- return func + "-param-overlap";
-}
-
-TEST(AddressSanitizer, StrArgsOverlapTest) {
- size_t size = Ident(100);
- char *str = Ident((char*)malloc(size));
-
-// Do not check memcpy() on OS X 10.7 and later, where it actually aliases
-// memmove().
-#if !defined(__APPLE__) || !defined(MAC_OS_X_VERSION_10_7) || \
- (MAC_OS_X_VERSION_MAX_ALLOWED < MAC_OS_X_VERSION_10_7)
- // Check "memcpy". Use Ident() to avoid inlining.
- memset(str, 'z', size);
- Ident(memcpy)(str + 1, str + 11, 10);
- Ident(memcpy)(str, str, 0);
- EXPECT_DEATH(Ident(memcpy)(str, str + 14, 15), OverlapErrorMessage("memcpy"));
- EXPECT_DEATH(Ident(memcpy)(str + 14, str, 15), OverlapErrorMessage("memcpy"));
-#endif
-
- // We do not treat memcpy with to==from as a bug.
- // See http://llvm.org/bugs/show_bug.cgi?id=11763.
- // EXPECT_DEATH(Ident(memcpy)(str + 20, str + 20, 1),
- // OverlapErrorMessage("memcpy"));
-
- // Check "strcpy".
- memset(str, 'z', size);
- str[9] = '\0';
- strcpy(str + 10, str);
- EXPECT_DEATH(strcpy(str + 9, str), OverlapErrorMessage("strcpy"));
- EXPECT_DEATH(strcpy(str, str + 4), OverlapErrorMessage("strcpy"));
- strcpy(str, str + 5);
-
- // Check "strncpy".
- memset(str, 'z', size);
- strncpy(str, str + 10, 10);
- EXPECT_DEATH(strncpy(str, str + 9, 10), OverlapErrorMessage("strncpy"));
- EXPECT_DEATH(strncpy(str + 9, str, 10), OverlapErrorMessage("strncpy"));
- str[10] = '\0';
- strncpy(str + 11, str, 20);
- EXPECT_DEATH(strncpy(str + 10, str, 20), OverlapErrorMessage("strncpy"));
-
- // Check "strcat".
- memset(str, 'z', size);
- str[10] = '\0';
- str[20] = '\0';
- strcat(str, str + 10);
- EXPECT_DEATH(strcat(str, str + 11), OverlapErrorMessage("strcat"));
- str[10] = '\0';
- strcat(str + 11, str);
- EXPECT_DEATH(strcat(str, str + 9), OverlapErrorMessage("strcat"));
- EXPECT_DEATH(strcat(str + 9, str), OverlapErrorMessage("strcat"));
- EXPECT_DEATH(strcat(str + 10, str), OverlapErrorMessage("strcat"));
-
- // Check "strncat".
- memset(str, 'z', size);
- str[10] = '\0';
- strncat(str, str + 10, 10); // from is empty
- EXPECT_DEATH(strncat(str, str + 11, 10), OverlapErrorMessage("strncat"));
- str[10] = '\0';
- str[20] = '\0';
- strncat(str + 5, str, 5);
- str[10] = '\0';
- EXPECT_DEATH(strncat(str + 5, str, 6), OverlapErrorMessage("strncat"));
- EXPECT_DEATH(strncat(str, str + 9, 10), OverlapErrorMessage("strncat"));
-
- free(str);
-}
-
-void CallAtoi(const char *nptr) {
- Ident(atoi(nptr));
-}
-void CallAtol(const char *nptr) {
- Ident(atol(nptr));
-}
-void CallAtoll(const char *nptr) {
- Ident(atoll(nptr));
-}
-typedef void(*PointerToCallAtoi)(const char*);
-
-void RunAtoiOOBTest(PointerToCallAtoi Atoi) {
- char *array = MallocAndMemsetString(10, '1');
- // Invalid pointer to the string.
- EXPECT_DEATH(Atoi(array + 11), RightOOBReadMessage(1));
- EXPECT_DEATH(Atoi(array - 1), LeftOOBReadMessage(1));
- // Die if a buffer doesn't have terminating NULL.
- EXPECT_DEATH(Atoi(array), RightOOBReadMessage(0));
- // Make last symbol a terminating NULL or other non-digit.
- array[9] = '\0';
- Atoi(array);
- array[9] = 'a';
- Atoi(array);
- Atoi(array + 9);
- // Sometimes we need to detect overflow if no digits are found.
- memset(array, ' ', 10);
- EXPECT_DEATH(Atoi(array), RightOOBReadMessage(0));
- array[9] = '-';
- EXPECT_DEATH(Atoi(array), RightOOBReadMessage(0));
- EXPECT_DEATH(Atoi(array + 9), RightOOBReadMessage(0));
- array[8] = '-';
- Atoi(array);
- free(array);
-}
-
-TEST(AddressSanitizer, AtoiAndFriendsOOBTest) {
- RunAtoiOOBTest(&CallAtoi);
- RunAtoiOOBTest(&CallAtol);
- RunAtoiOOBTest(&CallAtoll);
-}
-
-void CallStrtol(const char *nptr, char **endptr, int base) {
- Ident(strtol(nptr, endptr, base));
-}
-void CallStrtoll(const char *nptr, char **endptr, int base) {
- Ident(strtoll(nptr, endptr, base));
-}
-typedef void(*PointerToCallStrtol)(const char*, char**, int);
-
-void RunStrtolOOBTest(PointerToCallStrtol Strtol) {
- char *array = MallocAndMemsetString(3);
- char *endptr = NULL;
- array[0] = '1';
- array[1] = '2';
- array[2] = '3';
- // Invalid pointer to the string.
- EXPECT_DEATH(Strtol(array + 3, NULL, 0), RightOOBReadMessage(0));
- EXPECT_DEATH(Strtol(array - 1, NULL, 0), LeftOOBReadMessage(1));
- // Buffer overflow if there is no terminating null (depends on base).
- Strtol(array, &endptr, 3);
- EXPECT_EQ(array + 2, endptr);
- EXPECT_DEATH(Strtol(array, NULL, 0), RightOOBReadMessage(0));
- array[2] = 'z';
- Strtol(array, &endptr, 35);
- EXPECT_EQ(array + 2, endptr);
- EXPECT_DEATH(Strtol(array, NULL, 36), RightOOBReadMessage(0));
- // Add terminating zero to get rid of overflow.
- array[2] = '\0';
- Strtol(array, NULL, 36);
- // Don't check for overflow if base is invalid.
- Strtol(array - 1, NULL, -1);
- Strtol(array + 3, NULL, 1);
- // Sometimes we need to detect overflow if no digits are found.
- array[0] = array[1] = array[2] = ' ';
- EXPECT_DEATH(Strtol(array, NULL, 0), RightOOBReadMessage(0));
- array[2] = '+';
- EXPECT_DEATH(Strtol(array, NULL, 0), RightOOBReadMessage(0));
- array[2] = '-';
- EXPECT_DEATH(Strtol(array, NULL, 0), RightOOBReadMessage(0));
- array[1] = '+';
- Strtol(array, NULL, 0);
- array[1] = array[2] = 'z';
- Strtol(array, &endptr, 0);
- EXPECT_EQ(array, endptr);
- Strtol(array + 2, NULL, 0);
- EXPECT_EQ(array, endptr);
- free(array);
-}
-
-TEST(AddressSanitizer, StrtollOOBTest) {
- RunStrtolOOBTest(&CallStrtoll);
-}
-TEST(AddressSanitizer, StrtolOOBTest) {
- RunStrtolOOBTest(&CallStrtol);
-}
-
-// At the moment we instrument memcpy/memove/memset calls at compile time so we
-// can't handle OOB error if these functions are called by pointer, see disabled
-// MemIntrinsicCallByPointerTest below
-typedef void*(*PointerToMemTransfer)(void*, const void*, size_t);
-typedef void*(*PointerToMemSet)(void*, int, size_t);
-
-void CallMemSetByPointer(PointerToMemSet MemSet) {
- size_t size = Ident(100);
- char *array = Ident((char*)malloc(size));
- EXPECT_DEATH(MemSet(array, 0, 101), RightOOBWriteMessage(0));
- free(array);
-}
-
-void CallMemTransferByPointer(PointerToMemTransfer MemTransfer) {
- size_t size = Ident(100);
- char *src = Ident((char*)malloc(size));
- char *dst = Ident((char*)malloc(size));
- EXPECT_DEATH(MemTransfer(dst, src, 101), RightOOBWriteMessage(0));
- free(src);
- free(dst);
-}
-TEST(AddressSanitizer, DISABLED_MemIntrinsicCallByPointerTest) {
- CallMemSetByPointer(&memset);
- CallMemTransferByPointer(&memcpy);
- CallMemTransferByPointer(&memmove);
+char* MallocAndMemsetString(size_t size) {
+ return MallocAndMemsetString(size, 'z');
}
#if defined(__linux__) && !defined(ANDROID) && !defined(__ANDROID__)
@@ -1829,7 +889,11 @@ TEST(AddressSanitizer, ShadowGapTest) {
#if SANITIZER_WORDSIZE == 32
char *addr = (char*)0x22000000;
#else
+# if defined(__powerpc64__)
+ char *addr = (char*)0x024000800000;
+# else
char *addr = (char*)0x0000100000080000;
+# endif
#endif
EXPECT_DEATH(*addr = 1, "AddressSanitizer: SEGV on unknown");
}
@@ -1853,11 +917,9 @@ TEST(AddressSanitizer, StrDupTest) {
}
// Currently we create and poison redzone at right of global variables.
-char glob5[5];
static char static110[110];
const char ConstGlob[7] = {1, 2, 3, 4, 5, 6, 7};
static const char StaticConstGlob[3] = {9, 8, 7};
-extern int GlobalsTest(int x);
TEST(AddressSanitizer, GlobalTest) {
static char func_static15[15];
@@ -2038,9 +1100,10 @@ TEST(AddressSanitizer, AttributeNoAddressSafetyTest) {
Ident(NoAddressSafety)();
}
-// TODO(glider): Enable this test on Mac.
// It doesn't work on Android, as calls to new/delete go through malloc/free.
-#if !defined(__APPLE__) && !defined(ANDROID) && !defined(__ANDROID__)
+// Neither it does on OS X, see
+// https://code.google.com/p/address-sanitizer/issues/detail?id=131.
+#if !defined(ANDROID) && !defined(__ANDROID__) && !defined(__APPLE__)
static string MismatchStr(const string &str) {
return string("AddressSanitizer: alloc-dealloc-mismatch \\(") + str;
}
@@ -2097,22 +1160,6 @@ TEST(AddressSanitizer, DISABLED_DemoUAFHigh) {
uaf_test<U1>(kLargeMalloc, 0);
}
-TEST(AddressSanitizer, DISABLED_DemoOOBLeftLow) {
- oob_test<U1>(10, -1);
-}
-
-TEST(AddressSanitizer, DISABLED_DemoOOBLeftHigh) {
- oob_test<U1>(kLargeMalloc, -1);
-}
-
-TEST(AddressSanitizer, DISABLED_DemoOOBRightLow) {
- oob_test<U1>(10, 10);
-}
-
-TEST(AddressSanitizer, DISABLED_DemoOOBRightHigh) {
- oob_test<U1>(kLargeMalloc, kLargeMalloc);
-}
-
TEST(AddressSanitizer, DISABLED_DemoOOM) {
size_t size = SANITIZER_WORDSIZE == 64 ? (size_t)(1ULL << 40) : (0xf0000000);
printf("%p\n", malloc(size));
@@ -2160,223 +1207,6 @@ TEST(AddressSanitizer, BufferOverflowAfterManyFrees) {
delete [] Ident(x);
}
-#ifdef __APPLE__
-#include "asan_mac_test.h"
-TEST(AddressSanitizerMac, CFAllocatorDefaultDoubleFree) {
- EXPECT_DEATH(
- CFAllocatorDefaultDoubleFree(NULL),
- "attempting double-free");
-}
-
-void CFAllocator_DoubleFreeOnPthread() {
- pthread_t child;
- PTHREAD_CREATE(&child, NULL, CFAllocatorDefaultDoubleFree, NULL);
- PTHREAD_JOIN(child, NULL); // Shouldn't be reached.
-}
-
-TEST(AddressSanitizerMac, CFAllocatorDefaultDoubleFree_ChildPhread) {
- EXPECT_DEATH(CFAllocator_DoubleFreeOnPthread(), "attempting double-free");
-}
-
-namespace {
-
-void *GLOB;
-
-void *CFAllocatorAllocateToGlob(void *unused) {
- GLOB = CFAllocatorAllocate(NULL, 100, /*hint*/0);
- return NULL;
-}
-
-void *CFAllocatorDeallocateFromGlob(void *unused) {
- char *p = (char*)GLOB;
- p[100] = 'A'; // ASan should report an error here.
- CFAllocatorDeallocate(NULL, GLOB);
- return NULL;
-}
-
-void CFAllocator_PassMemoryToAnotherThread() {
- pthread_t th1, th2;
- PTHREAD_CREATE(&th1, NULL, CFAllocatorAllocateToGlob, NULL);
- PTHREAD_JOIN(th1, NULL);
- PTHREAD_CREATE(&th2, NULL, CFAllocatorDeallocateFromGlob, NULL);
- PTHREAD_JOIN(th2, NULL);
-}
-
-TEST(AddressSanitizerMac, CFAllocator_PassMemoryToAnotherThread) {
- EXPECT_DEATH(CFAllocator_PassMemoryToAnotherThread(),
- "heap-buffer-overflow");
-}
-
-} // namespace
-
-// TODO(glider): figure out whether we still need these tests. Is it correct
-// to intercept the non-default CFAllocators?
-TEST(AddressSanitizerMac, DISABLED_CFAllocatorSystemDefaultDoubleFree) {
- EXPECT_DEATH(
- CFAllocatorSystemDefaultDoubleFree(),
- "attempting double-free");
-}
-
-// We're intercepting malloc, so kCFAllocatorMalloc is routed to ASan.
-TEST(AddressSanitizerMac, CFAllocatorMallocDoubleFree) {
- EXPECT_DEATH(CFAllocatorMallocDoubleFree(), "attempting double-free");
-}
-
-TEST(AddressSanitizerMac, DISABLED_CFAllocatorMallocZoneDoubleFree) {
- EXPECT_DEATH(CFAllocatorMallocZoneDoubleFree(), "attempting double-free");
-}
-
-// For libdispatch tests below we check that ASan got to the shadow byte
-// legend, i.e. managed to print the thread stacks (this almost certainly
-// means that the libdispatch task creation has been intercepted correctly).
-TEST(AddressSanitizerMac, GCDDispatchAsync) {
- // Make sure the whole ASan report is printed, i.e. that we don't die
- // on a CHECK.
- EXPECT_DEATH(TestGCDDispatchAsync(), "Shadow byte legend");
-}
-
-TEST(AddressSanitizerMac, GCDDispatchSync) {
- // Make sure the whole ASan report is printed, i.e. that we don't die
- // on a CHECK.
- EXPECT_DEATH(TestGCDDispatchSync(), "Shadow byte legend");
-}
-
-
-TEST(AddressSanitizerMac, GCDReuseWqthreadsAsync) {
- // Make sure the whole ASan report is printed, i.e. that we don't die
- // on a CHECK.
- EXPECT_DEATH(TestGCDReuseWqthreadsAsync(), "Shadow byte legend");
-}
-
-TEST(AddressSanitizerMac, GCDReuseWqthreadsSync) {
- // Make sure the whole ASan report is printed, i.e. that we don't die
- // on a CHECK.
- EXPECT_DEATH(TestGCDReuseWqthreadsSync(), "Shadow byte legend");
-}
-
-TEST(AddressSanitizerMac, GCDDispatchAfter) {
- // Make sure the whole ASan report is printed, i.e. that we don't die
- // on a CHECK.
- EXPECT_DEATH(TestGCDDispatchAfter(), "Shadow byte legend");
-}
-
-TEST(AddressSanitizerMac, GCDSourceEvent) {
- // Make sure the whole ASan report is printed, i.e. that we don't die
- // on a CHECK.
- EXPECT_DEATH(TestGCDSourceEvent(), "Shadow byte legend");
-}
-
-TEST(AddressSanitizerMac, GCDSourceCancel) {
- // Make sure the whole ASan report is printed, i.e. that we don't die
- // on a CHECK.
- EXPECT_DEATH(TestGCDSourceCancel(), "Shadow byte legend");
-}
-
-TEST(AddressSanitizerMac, GCDGroupAsync) {
- // Make sure the whole ASan report is printed, i.e. that we don't die
- // on a CHECK.
- EXPECT_DEATH(TestGCDGroupAsync(), "Shadow byte legend");
-}
-
-void *MallocIntrospectionLockWorker(void *_) {
- const int kNumPointers = 100;
- int i;
- void *pointers[kNumPointers];
- for (i = 0; i < kNumPointers; i++) {
- pointers[i] = malloc(i + 1);
- }
- for (i = 0; i < kNumPointers; i++) {
- free(pointers[i]);
- }
-
- return NULL;
-}
-
-void *MallocIntrospectionLockForker(void *_) {
- pid_t result = fork();
- if (result == -1) {
- perror("fork");
- }
- assert(result != -1);
- if (result == 0) {
- // Call malloc in the child process to make sure we won't deadlock.
- void *ptr = malloc(42);
- free(ptr);
- exit(0);
- } else {
- // Return in the parent process.
- return NULL;
- }
-}
-
-TEST(AddressSanitizerMac, MallocIntrospectionLock) {
- // Incorrect implementation of force_lock and force_unlock in our malloc zone
- // will cause forked processes to deadlock.
- // TODO(glider): need to detect that none of the child processes deadlocked.
- const int kNumWorkers = 5, kNumIterations = 100;
- int i, iter;
- for (iter = 0; iter < kNumIterations; iter++) {
- pthread_t workers[kNumWorkers], forker;
- for (i = 0; i < kNumWorkers; i++) {
- PTHREAD_CREATE(&workers[i], 0, MallocIntrospectionLockWorker, 0);
- }
- PTHREAD_CREATE(&forker, 0, MallocIntrospectionLockForker, 0);
- for (i = 0; i < kNumWorkers; i++) {
- PTHREAD_JOIN(workers[i], 0);
- }
- PTHREAD_JOIN(forker, 0);
- }
-}
-
-void *TSDAllocWorker(void *test_key) {
- if (test_key) {
- void *mem = malloc(10);
- pthread_setspecific(*(pthread_key_t*)test_key, mem);
- }
- return NULL;
-}
-
-TEST(AddressSanitizerMac, DISABLED_TSDWorkqueueTest) {
- pthread_t th;
- pthread_key_t test_key;
- pthread_key_create(&test_key, CallFreeOnWorkqueue);
- PTHREAD_CREATE(&th, NULL, TSDAllocWorker, &test_key);
- PTHREAD_JOIN(th, NULL);
- pthread_key_delete(test_key);
-}
-
-// Test that CFStringCreateCopy does not copy constant strings.
-TEST(AddressSanitizerMac, CFStringCreateCopy) {
- CFStringRef str = CFSTR("Hello world!\n");
- CFStringRef str2 = CFStringCreateCopy(0, str);
- EXPECT_EQ(str, str2);
-}
-
-TEST(AddressSanitizerMac, NSObjectOOB) {
- // Make sure that our allocators are used for NSObjects.
- EXPECT_DEATH(TestOOBNSObjects(), "heap-buffer-overflow");
-}
-
-// Make sure that correct pointer is passed to free() when deallocating a
-// NSURL object.
-// See http://code.google.com/p/address-sanitizer/issues/detail?id=70.
-TEST(AddressSanitizerMac, NSURLDeallocation) {
- TestNSURLDeallocation();
-}
-
-// See http://code.google.com/p/address-sanitizer/issues/detail?id=109.
-TEST(AddressSanitizerMac, Mstats) {
- malloc_statistics_t stats1, stats2;
- malloc_zone_statistics(/*all zones*/NULL, &stats1);
- const size_t kMallocSize = 100000;
- void *alloc = Ident(malloc(kMallocSize));
- malloc_zone_statistics(/*all zones*/NULL, &stats2);
- EXPECT_GT(stats2.blocks_in_use, stats1.blocks_in_use);
- EXPECT_GE(stats2.size_in_use - stats1.size_in_use, kMallocSize);
- free(alloc);
- // Even the default OSX allocator may not change the stats after free().
-}
-#endif // __APPLE__
// Test that instrumentation of stack allocations takes into account
// AllocSize of a type, and not its StoreSize (16 vs 10 bytes for long double).
@@ -2387,3 +1217,16 @@ TEST(AddressSanitizer, LongDoubleNegativeTest) {
memcpy(Ident(&a), Ident(&b), sizeof(long double));
memcpy(Ident(&c), Ident(&b), sizeof(long double));
}
+
+TEST(AddressSanitizer, pthread_getschedparam) {
+ int policy;
+ struct sched_param param;
+ EXPECT_DEATH(
+ pthread_getschedparam(pthread_self(), &policy, Ident(&param) + 2),
+ "AddressSanitizer: stack-buffer-overflow");
+ EXPECT_DEATH(
+ pthread_getschedparam(pthread_self(), Ident(&policy) - 1, &param),
+ "AddressSanitizer: stack-buffer-overflow");
+ int res = pthread_getschedparam(pthread_self(), &policy, &param);
+ ASSERT_EQ(0, res);
+}
diff --git a/lib/asan/tests/asan_test_config.h b/lib/asan/tests/asan_test_config.h
index 1d28e99a4b10..6eb33ce4431b 100644
--- a/lib/asan/tests/asan_test_config.h
+++ b/lib/asan/tests/asan_test_config.h
@@ -47,10 +47,6 @@ using std::map;
# error "please define ASAN_NEEDS_SEGV"
#endif
-#ifndef ASAN_LOW_MEMORY
-# define ASAN_LOW_MEMORY 0
-#endif
-
#ifndef ASAN_AVOID_EXPENSIVE_TESTS
# define ASAN_AVOID_EXPENSIVE_TESTS 0
#endif
diff --git a/lib/asan/tests/asan_test_utils.h b/lib/asan/tests/asan_test_utils.h
index 6ed9f90df906..403773180c2f 100644
--- a/lib/asan/tests/asan_test_utils.h
+++ b/lib/asan/tests/asan_test_utils.h
@@ -20,10 +20,92 @@
# undef INCLUDED_FROM_ASAN_TEST_UTILS_H
#endif
-#include "sanitizer_common/tests/sanitizer_test_utils.h"
+#include "sanitizer_test_utils.h"
+#include <stdio.h>
+#include <signal.h>
+#include <stdlib.h>
+#include <string.h>
+#include <strings.h>
+#include <pthread.h>
+#include <stdint.h>
+#include <setjmp.h>
+#include <assert.h>
+#include <algorithm>
+#include <sys/mman.h>
+
+#ifdef __linux__
+# include <sys/prctl.h>
+# include <sys/types.h>
+# include <sys/stat.h>
+# include <fcntl.h>
+#include <unistd.h>
+#endif
+
+#if defined(__i386__) || defined(__x86_64__)
+#include <emmintrin.h>
+#endif
+
+#ifndef __APPLE__
+#include <malloc.h>
+#endif
// Check that pthread_create/pthread_join return success.
#define PTHREAD_CREATE(a, b, c, d) ASSERT_EQ(0, pthread_create(a, b, c, d))
#define PTHREAD_JOIN(a, b) ASSERT_EQ(0, pthread_join(a, b))
+#if ASAN_HAS_EXCEPTIONS
+# define ASAN_THROW(x) throw (x)
+#else
+# define ASAN_THROW(x)
+#endif
+
+typedef uint8_t U1;
+typedef uint16_t U2;
+typedef uint32_t U4;
+typedef uint64_t U8;
+
+static const int kPageSize = 4096;
+
+const size_t kLargeMalloc = 1 << 24;
+
+extern void free_aaa(void *p);
+extern void *malloc_aaa(size_t size);
+
+template<typename T>
+NOINLINE void asan_write(T *a) {
+ *a = 0;
+}
+
+string RightOOBErrorMessage(int oob_distance, bool is_write);
+string RightOOBWriteMessage(int oob_distance);
+string RightOOBReadMessage(int oob_distance);
+string LeftOOBErrorMessage(int oob_distance, bool is_write);
+string LeftOOBWriteMessage(int oob_distance);
+string LeftOOBReadMessage(int oob_distance);
+string LeftOOBAccessMessage(int oob_distance);
+char* MallocAndMemsetString(size_t size, char ch);
+char* MallocAndMemsetString(size_t size);
+
+extern char glob1[1];
+extern char glob2[2];
+extern char glob3[3];
+extern char glob4[4];
+extern char glob5[5];
+extern char glob6[6];
+extern char glob7[7];
+extern char glob8[8];
+extern char glob9[9];
+extern char glob10[10];
+extern char glob11[11];
+extern char glob12[12];
+extern char glob13[13];
+extern char glob14[14];
+extern char glob15[15];
+extern char glob16[16];
+extern char glob17[17];
+extern char glob1000[1000];
+extern char glob10000[10000];
+extern char glob100000[100000];
+extern int GlobalsTest(int x);
+
#endif // ASAN_TEST_UTILS_H