aboutsummaryrefslogtreecommitdiff
path: root/lib/asan
diff options
context:
space:
mode:
authorAndrew Turner <andrew@FreeBSD.org>2013-01-18 20:06:45 +0000
committerAndrew Turner <andrew@FreeBSD.org>2013-01-18 20:06:45 +0000
commit58aabf08b77d221489f10e274812ec60917c21a8 (patch)
treeb946f82269be87d83f086167c762c362e734c5bb /lib/asan
parent37dfff057418e02f8e5322da12684dd927e3d881 (diff)
downloadsrc-vendor/compiler-rt/compiler-rt-r172839.tar.gz
src-vendor/compiler-rt/compiler-rt-r172839.zip
Import compiler-rt r172839.vendor/compiler-rt/compiler-rt-r172839
Diffstat (limited to 'lib/asan')
-rw-r--r--lib/asan/CMakeLists.txt121
-rw-r--r--lib/asan/Makefile.mk7
-rw-r--r--lib/asan/Makefile.old349
-rw-r--r--lib/asan/README.txt26
-rw-r--r--lib/asan/asan_allocator.cc536
-rw-r--r--lib/asan/asan_allocator.h147
-rw-r--r--lib/asan/asan_allocator2.cc710
-rw-r--r--lib/asan/asan_fake_stack.cc182
-rw-r--r--lib/asan/asan_flags.h33
-rw-r--r--lib/asan/asan_globals.cc151
-rw-r--r--lib/asan/asan_intercepted_functions.h260
-rw-r--r--lib/asan/asan_interceptors.cc393
-rw-r--r--lib/asan/asan_interceptors.h1
-rw-r--r--lib/asan/asan_interface.h159
-rw-r--r--lib/asan/asan_internal.h81
-rw-r--r--lib/asan/asan_linux.cc111
-rw-r--r--lib/asan/asan_lock.h42
-rw-r--r--lib/asan/asan_mac.cc287
-rw-r--r--lib/asan/asan_mac.h8
-rw-r--r--lib/asan/asan_malloc_linux.cc42
-rw-r--r--lib/asan/asan_malloc_mac.cc195
-rw-r--r--lib/asan/asan_malloc_win.cc15
-rw-r--r--lib/asan/asan_mapping.h31
-rw-r--r--lib/asan/asan_new_delete.cc62
-rw-r--r--lib/asan/asan_poisoning.cc71
-rw-r--r--lib/asan/asan_posix.cc10
-rw-r--r--lib/asan/asan_printf.cc59
-rw-r--r--lib/asan/asan_report.cc681
-rw-r--r--lib/asan/asan_report.h57
-rw-r--r--lib/asan/asan_rtl.cc425
-rw-r--r--lib/asan/asan_stack.cc225
-rw-r--r--lib/asan/asan_stack.h90
-rw-r--r--lib/asan/asan_stats.cc36
-rw-r--r--lib/asan/asan_stats.h10
-rw-r--r--lib/asan/asan_thread.cc35
-rw-r--r--lib/asan/asan_thread.h28
-rw-r--r--lib/asan/asan_thread_registry.cc62
-rw-r--r--lib/asan/asan_thread_registry.h12
-rw-r--r--lib/asan/asan_win.cc154
-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.txt32
-rw-r--r--lib/asan/lit_tests/Helpers/blacklist-extra.cc5
-rw-r--r--lib/asan/lit_tests/Helpers/initialization-blacklist-extra.cc15
-rw-r--r--lib/asan/lit_tests/Helpers/initialization-blacklist.txt2
-rw-r--r--lib/asan/lit_tests/Helpers/initialization-bug-extra.cc5
-rw-r--r--lib/asan/lit_tests/Helpers/initialization-bug-extra2.cc6
-rw-r--r--lib/asan/lit_tests/Helpers/initialization-nobug-extra.cc9
-rw-r--r--lib/asan/lit_tests/Helpers/lit.local.cfg3
-rw-r--r--lib/asan/lit_tests/Linux/clone_test.cc48
-rw-r--r--lib/asan/lit_tests/Linux/initialization-bug-any-order.cc37
-rw-r--r--lib/asan/lit_tests/Linux/interception_failure_test.cc26
-rw-r--r--lib/asan/lit_tests/Linux/interception_malloc_test.cc27
-rw-r--r--lib/asan/lit_tests/Linux/interception_test.cc26
-rw-r--r--lib/asan/lit_tests/Linux/lit.local.cfg9
-rw-r--r--lib/asan/lit_tests/Linux/malloc-in-qsort.cc50
-rw-r--r--lib/asan/lit_tests/Linux/overflow-in-qsort.cc47
-rw-r--r--lib/asan/lit_tests/Linux/rlimit_mmap_test.cc16
-rw-r--r--lib/asan/lit_tests/Linux/swapcontext_test.cc66
-rw-r--r--lib/asan/lit_tests/SharedLibs/dlclose-test-so.cc (renamed from lib/asan/output_tests/dlclose-test-so.cc)0
-rw-r--r--lib/asan/lit_tests/SharedLibs/lit.local.cfg4
-rw-r--r--lib/asan/lit_tests/SharedLibs/shared-lib-test-so.cc (renamed from lib/asan/output_tests/shared-lib-test-so.cc)0
-rw-r--r--lib/asan/lit_tests/Unit/lit.cfg27
-rw-r--r--lib/asan/lit_tests/Unit/lit.site.cfg.in10
-rw-r--r--lib/asan/lit_tests/blacklist.cc44
-rw-r--r--lib/asan/lit_tests/deep_stack_uaf.cc36
-rw-r--r--lib/asan/lit_tests/deep_tail_call.cc24
-rw-r--r--lib/asan/lit_tests/deep_thread_stack.cc61
-rw-r--r--lib/asan/lit_tests/default_options.cc (renamed from lib/asan/output_tests/default_options.cc)5
-rw-r--r--lib/asan/lit_tests/dlclose-test.cc (renamed from lib/asan/output_tests/dlclose-test.cc)40
-rw-r--r--lib/asan/lit_tests/force_inline_opt0.cc14
-rw-r--r--lib/asan/lit_tests/global-overflow.cc25
-rw-r--r--lib/asan/lit_tests/heap-overflow.cc38
-rw-r--r--lib/asan/lit_tests/initialization-blacklist.cc32
-rw-r--r--lib/asan/lit_tests/initialization-bug.cc46
-rw-r--r--lib/asan/lit_tests/initialization-nobug.cc67
-rw-r--r--lib/asan/lit_tests/interface_symbols.c28
-rw-r--r--lib/asan/lit_tests/large_func_test.cc62
-rw-r--r--lib/asan/lit_tests/lit.cfg97
-rw-r--r--lib/asan/lit_tests/lit.site.cfg.in20
-rw-r--r--lib/asan/lit_tests/log-path_test.cc39
-rw-r--r--lib/asan/lit_tests/log_path_fork_test.cc22
-rw-r--r--lib/asan/lit_tests/malloc_delete_mismatch.cc26
-rw-r--r--lib/asan/lit_tests/malloc_hook.cc24
-rw-r--r--lib/asan/lit_tests/memcmp_test.cc19
-rw-r--r--lib/asan/lit_tests/null_deref.cc31
-rw-r--r--lib/asan/lit_tests/on_error_callback.cc16
-rw-r--r--lib/asan/lit_tests/sanity_check_pure_c.c19
-rw-r--r--lib/asan/lit_tests/shared-lib-test.cc54
-rw-r--r--lib/asan/lit_tests/sleep_before_dying.c10
-rw-r--r--lib/asan/lit_tests/stack-frame-demangle.cc24
-rw-r--r--lib/asan/lit_tests/stack-overflow.cc19
-rw-r--r--lib/asan/lit_tests/stack-use-after-return.cc45
-rw-r--r--lib/asan/lit_tests/strip_path_prefix.c12
-rw-r--r--lib/asan/lit_tests/strncpy-overflow.cc40
-rw-r--r--lib/asan/lit_tests/symbolize_callback.cc17
-rw-r--r--lib/asan/lit_tests/use-after-free.cc48
-rw-r--r--lib/asan/lit_tests/use-after-scope-inlined.cc29
-rw-r--r--lib/asan/output_tests/clone_test.cc34
-rw-r--r--lib/asan/output_tests/deep_tail_call.cc15
-rw-r--r--lib/asan/output_tests/global-overflow.cc16
-rw-r--r--lib/asan/output_tests/heap-overflow.cc22
-rw-r--r--lib/asan/output_tests/interception_failure_test-linux.cc17
-rw-r--r--lib/asan/output_tests/interception_malloc_test-linux.cc19
-rw-r--r--lib/asan/output_tests/interception_test-linux.cc18
-rw-r--r--lib/asan/output_tests/large_func_test.cc48
-rw-r--r--lib/asan/output_tests/memcmp_test.cc10
-rw-r--r--lib/asan/output_tests/null_deref.cc17
-rw-r--r--lib/asan/output_tests/shared-lib-test.cc42
-rw-r--r--lib/asan/output_tests/stack-overflow.cc11
-rw-r--r--lib/asan/output_tests/stack-use-after-return.cc.disabled27
-rw-r--r--lib/asan/output_tests/strncpy-overflow.cc24
-rwxr-xr-xlib/asan/output_tests/test_output.sh79
-rw-r--r--lib/asan/output_tests/use-after-free.c9
-rw-r--r--lib/asan/output_tests/use-after-free.cc31
-rwxr-xr-xlib/asan/scripts/asan_symbolize.py419
-rw-r--r--lib/asan/tests/CMakeLists.txt237
-rw-r--r--lib/asan/tests/asan_benchmarks_test.cc1
-rw-r--r--lib/asan/tests/asan_globals_test.cc2
-rw-r--r--lib/asan/tests/asan_mac_test.mm12
-rw-r--r--lib/asan/tests/asan_noinst_test.cc233
-rw-r--r--lib/asan/tests/asan_test.cc749
-rw-r--r--lib/asan/tests/asan_test.ignore1
-rw-r--r--lib/asan/tests/asan_test_config.h18
-rw-r--r--lib/asan/tests/asan_test_main.cc (renamed from lib/asan/tests/asan_break_optimization.cc)12
-rw-r--r--lib/asan/tests/asan_test_utils.h45
126 files changed, 6338 insertions, 3473 deletions
diff --git a/lib/asan/CMakeLists.txt b/lib/asan/CMakeLists.txt
index ce985f528172..92cba6dee622 100644
--- a/lib/asan/CMakeLists.txt
+++ b/lib/asan/CMakeLists.txt
@@ -2,6 +2,8 @@
set(ASAN_SOURCES
asan_allocator.cc
+ asan_allocator2.cc
+ asan_fake_stack.cc
asan_globals.cc
asan_interceptors.cc
asan_linux.cc
@@ -12,7 +14,7 @@ set(ASAN_SOURCES
asan_new_delete.cc
asan_poisoning.cc
asan_posix.cc
- asan_printf.cc
+ asan_report.cc
asan_rtl.cc
asan_stack.cc
asan_stats.cc
@@ -21,62 +23,99 @@ set(ASAN_SOURCES
asan_win.cc
)
+set(ASAN_DYLIB_SOURCES
+ ${ASAN_SOURCES}
+ dynamic/asan_interceptors_dynamic.cc
+ )
+
include_directories(..)
-set(ASAN_CFLAGS
- -fPIC
- -fno-exceptions
- -funwind-tables
- -fvisibility=hidden
- -fno-builtin
- -fomit-frame-pointer
- -O3
- )
-if (SUPPORTS_NO_VARIADIC_MACROS_FLAG)
- list(APPEND ASAN_CFLAGS -Wno-variadic-macros)
-endif ()
+set(ASAN_CFLAGS ${SANITIZER_COMMON_CFLAGS})
-if (APPLE)
- list(APPEND ASAN_CFLAGS -mmacosx-version-min=10.5)
+if(ANDROID)
+ set(ASAN_COMMON_DEFINITIONS
+ ASAN_HAS_EXCEPTIONS=1
+ ASAN_FLEXIBLE_MAPPING_AND_OFFSET=0
+ ASAN_NEEDS_SEGV=0
+ ASAN_LOW_MEMORY=1
+ )
+else()
+ set(ASAN_COMMON_DEFINITIONS
+ ASAN_HAS_EXCEPTIONS=1
+ ASAN_FLEXIBLE_MAPPING_AND_OFFSET=0
+ ASAN_NEEDS_SEGV=1
+ )
endif()
-set(ASAN_COMMON_DEFINITIONS
- ASAN_HAS_EXCEPTIONS=1
- ASAN_NEEDS_SEGV=1
+set(ASAN_DYLIB_DEFINITIONS
+ ${ASAN_COMMON_DEFINITIONS}
+ MAC_INTERPOSE_FUNCTIONS=1
)
-# FIXME: We need to build universal binaries on OS X instead of
-# two arch-specific binaries.
+# Architectures supported by ASan.
+filter_available_targets(ASAN_SUPPORTED_ARCH
+ x86_64 i386)
-if(CAN_TARGET_X86_64)
- add_library(clang_rt.asan-x86_64 STATIC
+set(ASAN_RUNTIME_LIBRARIES)
+if(APPLE)
+ # Build universal binary on APPLE.
+ add_library(clang_rt.asan_osx STATIC
${ASAN_SOURCES}
- $<TARGET_OBJECTS:RTInterception.x86_64>
- $<TARGET_OBJECTS:RTSanitizerCommon.x86_64>
- )
- set_target_compile_flags(clang_rt.asan-x86_64
- ${ASAN_CFLAGS}
- ${TARGET_X86_64_CFLAGS}
+ $<TARGET_OBJECTS:RTInterception.osx>
+ $<TARGET_OBJECTS:RTSanitizerCommon.osx>
)
- set_property(TARGET clang_rt.asan-x86_64 APPEND PROPERTY COMPILE_DEFINITIONS
- ${ASAN_COMMON_DEFINITIONS})
- add_clang_runtime_static_library(clang_rt.asan-x86_64)
-endif()
-if(CAN_TARGET_I386)
- add_library(clang_rt.asan-i386 STATIC
+ 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)
+elseif(ANDROID)
+ add_library(clang_rt.asan-arm-android SHARED
${ASAN_SOURCES}
- $<TARGET_OBJECTS:RTInterception.i386>
- $<TARGET_OBJECTS:RTSanitizerCommon.i386>
+ $<TARGET_OBJECTS:RTInterception.arm.android>
+ $<TARGET_OBJECTS:RTSanitizerCommon.arm.android>
)
- set_target_compile_flags(clang_rt.asan-i386
+ set_target_compile_flags(clang_rt.asan-arm-android
${ASAN_CFLAGS}
- ${TARGET_I386_CFLAGS}
)
- set_property(TARGET clang_rt.asan-i386 APPEND PROPERTY COMPILE_DEFINITIONS
- ${ASAN_COMMON_DEFINITIONS})
- add_clang_runtime_static_library(clang_rt.asan-i386)
+ 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})
+ 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})
+
+
if(LLVM_INCLUDE_TESTS)
add_subdirectory(tests)
endif()
+
+add_subdirectory(lit_tests)
diff --git a/lib/asan/Makefile.mk b/lib/asan/Makefile.mk
index 9d1a2e8a9a28..af9602e8b242 100644
--- a/lib/asan/Makefile.mk
+++ b/lib/asan/Makefile.mk
@@ -8,7 +8,7 @@
#===------------------------------------------------------------------------===#
ModuleName := asan
-SubDirs :=
+SubDirs := dynamic
Sources := $(foreach file,$(wildcard $(Dir)/*.cc),$(notdir $(file)))
ObjNames := $(Sources:%.cc=%.o)
@@ -17,8 +17,9 @@ 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)/../interception/*.h)
+Dependencies += $(wildcard $(Dir)/../interception/mach_override/*.h)
+Dependencies += $(wildcard $(Dir)/../sanitizer_common/*.h)
# Define a convenience variable for all the asan functions.
AsanFunctions := $(Sources:%.cc=%)
diff --git a/lib/asan/Makefile.old b/lib/asan/Makefile.old
deleted file mode 100644
index 4ab80e20bcb1..000000000000
--- a/lib/asan/Makefile.old
+++ /dev/null
@@ -1,349 +0,0 @@
-#===- lib/asan/Makefile.old --------------------------------*- Makefile -*--===#
-#
-# The LLVM Compiler Infrastructure
-#
-# This file is distributed under the University of Illinois Open Source
-# License. See LICENSE.TXT for details.
-#
-#===------------------------------------------------------------------------===#
-
-OS=$(shell uname | tr '[A-Z]' '[a-z]')
-ROOT=$(shell pwd)
-MAKEFILE=Makefile.old # this file.
-
-ifeq ($(ARCH), android)
- ANDROID_CFLAGS= \
- -DANDROID \
- -D__WORDSIZE=32 \
- -I$(ANDROID_BUILD_TOP)/external/stlport/stlport \
- -I$(ANDROID_BUILD_TOP)/bionic \
- -I$(ANDROID_BUILD_TOP)/bionic/libstdc++/include \
- -I$(ANDROID_BUILD_TOP)/bionic/libc/arch-arm/include \
- -I$(ANDROID_BUILD_TOP)/bionic/libc/include \
- -I$(ANDROID_BUILD_TOP)/bionic/libc/kernel/common \
- -I$(ANDROID_BUILD_TOP)/bionic/libc/kernel/arch-arm \
- -I$(ANDROID_BUILD_TOP)/bionic/libm/include \
- -I$(ANDROID_BUILD_TOP)/bionic/libm/include/arm \
- -I$(ANDROID_BUILD_TOP)/bionic/libthread_db/include \
- -L$(ANDROID_PRODUCT_OUT)/obj/lib
- CLANG_FLAGS= \
- -ccc-host-triple arm-linux-androideabi \
- -D__compiler_offsetof=__builtin_offsetof \
- -D__ELF__=1 \
- -ccc-gcc-name arm-linux-androideabi-g++ \
- $(ANDROID_CFLAGS)
- CC=$(ANDROID_EABI_TOOLCHAIN)/arm-linux-androideabi-gcc $(ANDROID_CFLAGS)
- CXX=$(ANDROID_EABI_TOOLCHAIN)/arm-linux-androideabi-g++ $(ANDROID_CFLAGS)
-endif
-
-ifeq ($(ARCH), arm)
- # Example make command line:
- # CROSSTOOL=$HOME/x-tools/arm-unknown-linux-gnueabi/ PATH=$CROSSTOOL/bin:$PATH make ARCH=arm asan_test
- CLANG_FLAGS= \
- -ccc-host-triple arm-unknown-linux-gnueabi \
- -march=armv7-a -mfloat-abi=softfp -mfp=neon \
- -ccc-gcc-name arm-unknown-linux-gnueabi-g++ \
- -B$(CROSSTOOL)/lib/gcc/arm-unknown-linux-gnueabi/4.4.4 \
- -B$(CROSSTOOL)/arm-unknown-linux-gnueabi/sys-root/usr/lib \
- -I$(CROSSTOOL)/lib/gcc/arm-unknown-linux-gnueabi/4.4.4/include \
- -I$(CROSSTOOL)/arm-unknown-linux-gnueabi/include/c++/4.4.4 \
- -I$(CROSSTOOL)/arm-unknown-linux-gnueabi/include/c++/4.4.4/arm-unknown-linux-gnueabi \
- -I$(CROSSTOOL)/arm-unknown-linux-gnueabi/sys-root/include \
- -I$(CROSSTOOL)/arm-unknown-linux-gnueabi/sys-root/usr/include \
- -L$(CROSSTOOL)/lib/gcc/arm-unknown-linux-gnueabi/4.4.4 \
- -L$(CROSSTOOL)/arm-unknown-linux-gnueabi/sys-root/lib \
- -L$(CROSSTOOL)/arm-unknown-linux-gnueabi/sys-root/usr/lib
- CC=$(CROSSTOOL)/bin/arm-unknown-linux-gnueabi-gcc
- CXX=$(CROSSTOOL)/bin/arm-unknown-linux-gnueabi-g++
-endif
-
-CLANG_FLAGS=
-CLANG_VERSION=3.2
-CLANG_BUILD=$(ROOT)/../../../../build/Release+Asserts
-CLANG_CC=$(CLANG_BUILD)/bin/clang $(CLANG_FLAGS)
-CLANG_CXX=$(CLANG_BUILD)/bin/clang++ $(CLANG_FLAGS)
-FILE_CHECK=$(CLANG_BUILD)/bin/FileCheck
-
-CC=$(CLANG_CC)
-CXX=$(CLANG_CXX)
-
-CFLAGS:=-Wall -fvisibility=hidden
-
-CLEANROOM_CXX=$(CXX) -Wall
-
-INSTALL_DIR=../asan_clang_$(OS)
-BIN=bin_$(OS)
-
-LIBS=#-lpthread -ldl
-ARCH=x86_64
-
-ASAN_STACK=1
-ASAN_GLOBALS=1
-ASAN_SCALE=0 # default will be used
-ASAN_OFFSET=-1 #default will be used
-ASAN_UAR=0
-ASAN_HAS_EXCEPTIONS=1
-ASAN_FLEXIBLE_MAPPING_AND_OFFSET=0
-ASAN_HAS_BLACKLIST=1
-ASAN_NEEDS_SEGV=1
-ASAN_PIE=0
-
-ifeq ($(ARCH), i386)
-BITS=32
-SUFF=$(BITS)
-CFLAGS:=$(CFLAGS) -m$(BITS)
-endif
-
-ifeq ($(ARCH), x86_64)
-BITS=64
-SUFF=$(BITS)
-CFLAGS:=$(CFLAGS) -m$(BITS)
-endif
-
-ifeq ($(ARCH), arm)
-BITS=32
-SUFF=_arm
-CFLAGS:=$(CFLAGS) -march=armv7-a
-ASAN_HAS_EXCEPTIONS=0
-endif
-
-ifeq ($(ARCH), android)
-BITS=32
-SUFF=_android
-CFLAGS:=$(CFLAGS)
-ASAN_HAS_EXCEPTIONS=0
-endif
-
-PIE=
-ifeq ($(ASAN_PIE), 1)
- PIE=-fPIE -pie
-endif
-
-# This will build libasan on linux for both x86_64 and i386 in the
-# desired location. The Mac library is already build by the clang's make.
-# $(CLANG_BUILD)/lib/clang/$(CLANG_VERSION)/lib/$(OS)/libclang_rt.asan-$(ARCH).a
-LIBASAN_INST_DIR=$(CLANG_BUILD)/lib/clang/$(CLANG_VERSION)/lib/$(OS)
-LIBASAN_A=$(LIBASAN_INST_DIR)/libclang_rt.asan-$(ARCH).a
-
-BLACKLIST=
-ifeq ($(ASAN_HAS_BLACKLIST), 1)
- BLACKLIST=-mllvm -asan-blacklist=$(ROOT)/tests/asan_test.ignore
-endif
-
-COMMON_ASAN_DEFINES=\
- -DASAN_UAR=$(ASAN_UAR) \
- -DASAN_HAS_EXCEPTIONS=$(ASAN_HAS_EXCEPTIONS) \
- -DASAN_NEEDS_SEGV=$(ASAN_NEEDS_SEGV) \
- -DASAN_HAS_BLACKLIST=$(ASAN_HAS_BLACKLIST)
-
-CLANG_ASAN_CXX=$(CLANG_CXX) \
- -faddress-sanitizer \
- $(BLACKLIST) \
- -mllvm -asan-stack=$(ASAN_STACK) \
- -mllvm -asan-globals=$(ASAN_GLOBALS) \
- -mllvm -asan-mapping-scale=$(ASAN_SCALE) \
- -mllvm -asan-mapping-offset-log=$(ASAN_OFFSET) \
- -mllvm -asan-use-after-return=$(ASAN_UAR) \
- $(COMMON_ASAN_DEFINES)
-
-CLANG_ASAN_LD=$(CLANG_CXX) -faddress-sanitizer
-
-GCC_ASAN_PATH=SET_FROM_COMMAND_LINE
-GCC_ASAN_CXX=$(GCC_ASAN_PATH)/g++ \
- -faddress-sanitizer \
- $(COMMON_ASAN_DEFINES)
-
-GCC_ASAN_LD=$(GCC_ASAN_PATH)/g++ -ldl -lpthread
-
-ASAN_COMPILER=clang
-
-ifeq ($(ASAN_COMPILER), clang)
- ASAN_CXX=$(CLANG_ASAN_CXX)
- ASAN_LD=$(CLANG_ASAN_LD)
- ASAN_LD_TAIL=
-endif
-
-ifeq ($(ASAN_COMPILER), gcc)
- ASAN_CXX=$(GCC_ASAN_CXX)
- ASAN_LD=$(GCC_ASAN_LD)
- ASAN_LD_TAIL=$(LIBASAN_A)
-endif
-
-INTERCEPTION=../interception
-MACH_OVERRIDE=$(INTERCEPTION)/mach_override
-COMMON=../sanitizer_common
-
-RTL_HDR=$(wildcard *.h) \
- $(wildcard $(INTERCEPTION)/*.h) \
- $(wildcard $(MACH_OVERRIDE)/*.h) \
- $(wildcard $(COMMON)/*.h)
-
-LIBTSAN_SRC=$(wildcard *.cc)
-INTERCEPTION_SRC=$(wildcard $(INTERCEPTION)/*.cc)
-MACH_OVERRIDE_SRC=$(wildcard $(MACH_OVERRIDE)/*.c)
-COMMON_SRC=$(wildcard $(COMMON)/*.cc)
-
-
-LIBASAN_OBJ=$(patsubst %.cc,$(BIN)/%$(SUFF).o,$(LIBTSAN_SRC)) \
- $(patsubst $(INTERCEPTION)/%.cc,$(BIN)/%$(SUFF).o,$(INTERCEPTION_SRC)) \
- $(patsubst $(COMMON)/%.cc,$(BIN)/%$(SUFF).o,$(COMMON_SRC)) \
- $(patsubst $(MACH_OVERRIDE)/%.c,$(BIN)/%$(SUFF).o,$(MACH_OVERRIDE_SRC))
-
-GTEST_ROOT=third_party/googletest
-GTEST_INCLUDE=-I$(GTEST_ROOT)/include
-GTEST_MAKE_DIR=$(GTEST_ROOT)/make-$(OS)$(SUFF)
-GTEST_LIB=$(GTEST_MAKE_DIR)/gtest-all.o
-
-all: b64 b32
-
-test: t64 t32 output_tests lint
- @echo "ALL TESTS PASSED"
-
-output_tests: b32 b64
- cd output_tests && ./test_output.sh $(CLANG_CXX) $(CLANG_CC) $(FILE_CHECK)
-
-t64: b64
- $(BIN)/asan_test64
-t32: b32
- $(BIN)/asan_test32
-
-b64: | mk_bin_dir
- $(MAKE) -f $(MAKEFILE) ARCH=x86_64 asan_test asan_benchmarks
-b32: | mk_bin_dir
- $(MAKE) -f $(MAKEFILE) ARCH=i386 asan_test asan_benchmarks
-
-lib64:
- $(MAKE) -f $(MAKEFILE) ARCH=x86_64 lib
-lib32:
- $(MAKE) -f $(MAKEFILE) ARCH=i386 lib
-
-mk_bin_dir:
- mkdir -p $(BIN)
-
-clang:
- cd ../ && llvm/rebuild_clang_and_asan.sh > /dev/null
-
-install: install_clang
-
-$(INSTALL_DIR):
- mkdir -p $(INSTALL_DIR) $(INSTALL_DIR)/bin $(INSTALL_DIR)/lib
-
-install_clang: | $(INSTALL_DIR)
- cp -v $(CLANG_BUILD)/bin/clang $(INSTALL_DIR)/bin
- cp -rv $(CLANG_BUILD)/lib/clang $(INSTALL_DIR)/lib
- (cd $(INSTALL_DIR)/bin; ln -sf clang clang++)
-
-#install_lib: | $(INSTALL_DIR)
-# cp -v $(CLANG_BUILD)/lib/libasan*.a $(INSTALL_DIR)/lib
-
-$(BIN)/asan_noinst_test$(SUFF).o: tests/asan_noinst_test.cc $(RTL_HDR) $(MAKEFILE)
- $(CLEANROOM_CXX) $(PIE) $(CFLAGS) $(GTEST_INCLUDE) -I. -I.. -g -c $< -O2 -o $@
-
-$(BIN)/asan_break_optimization$(SUFF).o: tests/asan_break_optimization.cc $(MAKEFILE)
- $(CLEANROOM_CXX) $(PIE) $(CFLAGS) -c $< -O0 -o $@
-
-$(BIN)/%_test$(SUFF).o: tests/%_test.cc $(RTL_HDR) $(MAKEFILE)
- $(ASAN_CXX) $(GTEST_INCLUDE) -I. -I.. -g -c $< -O2 -o $@ $(PIE) $(CFLAGS)
-
-$(BIN)/%_test$(SUFF).o: tests/%_test.mm $(RTL_HDR) $(MAKEFILE)
- $(ASAN_CXX) $(GTEST_INCLUDE) -I. -I.. -g -c $< -O2 -o $@ -ObjC $(PIE) $(CFLAGS)
-
-RTL_COMMON_FLAGS=$(PIE) $(CFLAGS) -fPIC -c -O2 -fno-exceptions -funwind-tables \
- -Ithird_party -I.. $(ASAN_FLAGS)
-
-$(BIN)/%$(SUFF).o: $(INTERCEPTION)/%.cc $(RTL_HDR) $(MAKEFILE)
- $(CXX) $(RTL_COMMON_FLAGS) -o $@ -g $<
-
-$(BIN)/%$(SUFF).o: $(COMMON)/%.cc $(RTL_HDR) $(MAKEFILE)
- $(CXX) $(RTL_COMMON_FLAGS) -o $@ -g $<
-
-$(BIN)/%$(SUFF).o: $(MACH_OVERRIDE)/%.c $(RTL_HDR) $(MAKEFILE)
- $(CC) $(RTL_COMMON_FLAGS) -o $@ -g $<
-
-$(BIN)/%$(SUFF).o: %.cc $(RTL_HDR) $(MAKEFILE)
- $(CXX) $(RTL_COMMON_FLAGS) -o $@ -g $< \
- -DASAN_NEEDS_SEGV=$(ASAN_NEEDS_SEGV) \
- -DASAN_HAS_EXCEPTIONS=$(ASAN_HAS_EXCEPTIONS) \
- -DASAN_FLEXIBLE_MAPPING_AND_OFFSET=$(ASAN_FLEXIBLE_MAPPING_AND_OFFSET)
-
-$(BIN)/%$(SUFF).o: %.c $(RTL_HDR) $(MAKEFILE)
- $(CC) $(PIE) $(CFLAGS) -fPIC -c -O2 -o $@ -g $< -Ithird_party \
- $(ASAN_FLAGS)
-
-ifeq ($(OS),darwin)
-LD_FLAGS=-framework Foundation
-else
-LD_FLAGS=
-endif
-
-lib: $(LIBASAN_A)
-
-$(LIBASAN_A): mk_bin_dir $(LIBASAN_OBJ) $(MAKEFILE)
- mkdir -p $(LIBASAN_INST_DIR)
- ar ru $@ $(LIBASAN_OBJ)
- $(CXX) -shared $(CFLAGS) $(LIBASAN_OBJ) $(LD_FLAGS) -o $(BIN)/libasan$(SUFF).so
-
-TEST_OBJECTS_COMMON=\
- $(BIN)/asan_globals_test$(SUFF).o \
- $(BIN)/asan_break_optimization$(SUFF).o \
- $(BIN)/asan_noinst_test$(SUFF).o \
- $(BIN)/asan_test$(SUFF).o
-
-BENCHMARK_OBJECTS=\
- $(BIN)/asan_benchmarks_test$(SUFF).o \
- $(BIN)/asan_break_optimization$(SUFF).o
-
-ifeq ($(OS),darwin)
-TEST_OBJECTS=$(TEST_OBJECTS_COMMON) \
- $(BIN)/asan_mac_test$(SUFF).o
-else
-TEST_OBJECTS=$(TEST_OBJECTS_COMMON)
-endif
-
-$(BIN)/asan_test$(SUFF): $(TEST_OBJECTS) $(LIBASAN_A) $(MAKEFILE) tests/asan_test.ignore $(GTEST_LIB)
- $(ASAN_LD) $(PIE) $(CFLAGS) -g -O3 $(TEST_OBJECTS) \
- $(LD_FLAGS) -o $@ $(LIBS) $(GTEST_LIB) $(ASAN_LD_TAIL)
-
-$(BIN)/asan_benchmarks$(SUFF): $(BENCHMARK_OBJECTS) $(LIBASAN_A) $(MAKEFILE) $(GTEST_LIB)
- $(ASAN_LD) $(PIE) $(CFLAGS) -g -O3 $(BENCHMARK_OBJECTS) \
- $(LD_FLAGS) -o $@ $(LIBS) $(GTEST_LIB) $(ASAN_LD_TAIL)
-
-asan_test: $(BIN)/asan_test$(SUFF)
-
-asan_benchmarks: $(BIN)/asan_benchmarks$(SUFF)
-
-# for now, build gtest with clang/asan even if we use a different compiler.
-$(GTEST_LIB):
- mkdir -p $(GTEST_MAKE_DIR) && \
- cd $(GTEST_MAKE_DIR) && \
- $(MAKE) -f ../make/Makefile CXXFLAGS="$(PIE) $(CFLAGS) -g -w" \
- CXX="$(CLANG_CXX)"
-
-RTL_LINT_FILTER=-readability/casting,-readability/check,-build/include,-build/header_guard,-build/class,-legal/copyright,-build/namespaces
-# TODO(kcc): remove these filters one by one
-TEST_LINT_FILTER=-readability/casting,-build/include,-legal/copyright,-whitespace/newline,-runtime/sizeof,-runtime/int,-runtime/printf
-
-LLVM_LINT_FILTER=-,+whitespace
-
-ADDRESS_SANITIZER_CPP=../../../../lib/Transforms/Instrumentation/AddressSanitizer.cpp
-
-lint:
- third_party/cpplint/cpplint.py --filter=$(LLVM_LINT_FILTER) $(ADDRESS_SANITIZER_CPP)
- third_party/cpplint/cpplint.py --filter=$(RTL_LINT_FILTER) asan_*.cc asan_*.h
- third_party/cpplint/cpplint.py --filter=$(RTL_LINT_FILTER) $(INTERCEPTION)/interception*.h $(INTERCEPTION)/interception*.cc
- third_party/cpplint/cpplint.py --filter=$(RTL_LINT_FILTER) $(COMMON)/sanitizer_*.h $(COMMON)/sanitizer_*.cc
- third_party/cpplint/cpplint.py --filter=$(TEST_LINT_FILTER) tests/*.cc output_tests/*.cc
-
-get_third_party:
- rm -rf third_party
- mkdir third_party
- (cd third_party && \
- svn co -r375 http://googletest.googlecode.com/svn/trunk googletest && \
- svn co -r69 http://google-styleguide.googlecode.com/svn/trunk/cpplint cpplint \
- )
-
-clean:
- rm -f *.o *.ll *.S *.a *.log asan_test64* asan_test32* a.out perf.data log
- rm -f $(LIBASAN_INST_DIR)/libclang_rt.asan-*.a
- rm -rf $(BIN)
- rm -rf $(GTEST_ROOT)/make-*
diff --git a/lib/asan/README.txt b/lib/asan/README.txt
index 5e6600489a69..e4f4961c5d4f 100644
--- a/lib/asan/README.txt
+++ b/lib/asan/README.txt
@@ -4,22 +4,26 @@ This directory contains sources of the AddressSanitizer (asan) run-time library.
We are in the process of integrating AddressSanitizer with LLVM, stay tuned.
Directory structre:
-
README.txt : This file.
-Makefile.mk : Currently a stub for a proper makefile. not usable.
-Makefile.old : Old out-of-tree makefile, the only usable one so far.
+Makefile.mk : File for make-based build.
+CMakeLists.txt : File for cmake-based build.
asan_*.{cc,h} : Sources of the asan run-time lirbary.
-mach_override/* : Utility to override functions on Darwin (MIT License).
scripts/* : Helper scripts.
+tests/* : ASan unit tests.
+lit_tests/* : ASan output tests.
-Temporary build instructions (verified on linux):
+Also ASan runtime needs the following libraries:
+lib/interception/ : Machinery used to intercept function calls.
+lib/sanitizer_common/ : Code shared between ASan and TSan.
-cd lib/asan
-make -f Makefile.old get_third_party # gets googletest and cpplint
-make -f Makefile.old test -j 8 CLANG_BUILD=/path/to/Release+Asserts
-# Optional:
-# make -f Makefile.old install # installs clang and rt to lib/asan_clang_linux
+Currently ASan runtime can be built by both make and cmake build systems.
+(see compiler-rt/make and files Makefile.mk for make-based build and
+files CMakeLists.txt for cmake-based build).
-For more info see http://code.google.com/p/address-sanitizer/
+ASan unit and output tests work only with cmake. You may run this
+command from the root of your cmake build tree:
+make check-asan
+For more instructions see:
+http://code.google.com/p/address-sanitizer/wiki/HowToBuild
diff --git a/lib/asan/asan_allocator.cc b/lib/asan/asan_allocator.cc
index 352cce00fbee..30dd4ceddd88 100644
--- a/lib/asan/asan_allocator.cc
+++ b/lib/asan/asan_allocator.cc
@@ -24,21 +24,19 @@
// 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_interface.h"
#include "asan_internal.h"
-#include "asan_lock.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"
-
-#if defined(_WIN32) && !defined(__clang__)
-#include <intrin.h>
-#endif
+#include "sanitizer_common/sanitizer_mutex.h"
namespace __asan {
@@ -57,43 +55,7 @@ static const uptr kMallocSizeClassStepLog = 26;
static const uptr kMallocSizeClassStep = 1UL << kMallocSizeClassStepLog;
static const uptr kMaxAllowedMallocSize =
- (__WORDSIZE == 32) ? 3UL << 30 : 8UL << 30;
-
-static inline bool IsAligned(uptr a, uptr alignment) {
- return (a & (alignment - 1)) == 0;
-}
-
-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 = __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);
-}
+ (SANITIZER_WORDSIZE == 32) ? 3UL << 30 : 8UL << 30;
static inline uptr SizeClassToSize(u8 size_class) {
CHECK(size_class < kNumberOfSizeClasses);
@@ -131,7 +93,7 @@ static void PoisonHeapPartialRightRedzone(uptr mem, uptr size) {
}
static u8 *MmapNewPagesAndPoisonShadow(uptr size) {
- CHECK(IsAligned(size, kPageSize));
+ CHECK(IsAligned(size, GetPageSizeCached()));
u8 *res = (u8*)MmapOrDie(size, __FUNCTION__);
PoisonShadow((uptr)res, size, kAsanHeapLeftRedzoneMagic);
if (flags()->debug) {
@@ -166,7 +128,8 @@ struct ChunkBase {
// Second 8 bytes.
uptr alignment_log : 8;
- uptr used_size : FIRST_32_SECOND_64(32, 56); // Size requested by the user.
+ 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.
@@ -198,50 +161,23 @@ struct AsanChunk: public ChunkBase {
if (REDZONE < sizeof(ChunkBase)) return 0;
return (REDZONE) / sizeof(u32);
}
+};
- bool AddrIsInside(uptr addr, uptr access_size, uptr *offset) {
- if (addr >= Beg() && (addr + access_size) <= (Beg() + used_size)) {
- *offset = addr - Beg();
- return true;
- }
- return false;
- }
-
- bool AddrIsAtLeft(uptr addr, uptr access_size, uptr *offset) {
- if (addr < Beg()) {
- *offset = Beg() - addr;
- return true;
- }
- return false;
- }
+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; }
- bool AddrIsAtRight(uptr addr, uptr access_size, uptr *offset) {
- if (addr + access_size >= Beg() + used_size) {
- if (addr <= Beg() + used_size)
- *offset = 0;
- else
- *offset = addr - (Beg() + used_size);
- return true;
- }
- return false;
- }
+void AsanChunkView::GetAllocStack(StackTrace *stack) {
+ StackTrace::UncompressStack(stack, chunk_->compressed_alloc_stack(),
+ chunk_->compressed_alloc_stack_size());
+}
- void DescribeAddress(uptr addr, uptr access_size) {
- uptr offset;
- AsanPrintf("%p is located ", (void*)addr);
- if (AddrIsInside(addr, access_size, &offset)) {
- AsanPrintf("%zu bytes inside of", offset);
- } else if (AddrIsAtLeft(addr, access_size, &offset)) {
- AsanPrintf("%zu bytes to the left of", offset);
- } else if (AddrIsAtRight(addr, access_size, &offset)) {
- AsanPrintf("%zu bytes to the right of", offset);
- } else {
- AsanPrintf(" somewhere around (this is AddressSanitizer bug!)");
- }
- AsanPrintf(" %zu-byte region [%p,%p)\n",
- used_size, (void*)Beg(), (void*)(Beg() + used_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);
@@ -251,37 +187,15 @@ static AsanChunk *PtrToChunk(uptr ptr) {
return m;
}
-
void AsanChunkFifoList::PushList(AsanChunkFifoList *q) {
CHECK(q->size() > 0);
- if (last_) {
- CHECK(first_);
- CHECK(!last_->next);
- last_->next = q->first_;
- last_ = q->last_;
- } else {
- CHECK(!first_);
- last_ = q->last_;
- first_ = q->first_;
- CHECK(first_);
- }
- CHECK(last_);
- CHECK(!last_->next);
size_ += q->size();
+ append_back(q);
q->clear();
}
void AsanChunkFifoList::Push(AsanChunk *n) {
- CHECK(n->next == 0);
- if (last_) {
- CHECK(first_);
- CHECK(!last_->next);
- last_->next = n;
- last_ = n;
- } else {
- CHECK(!first_);
- last_ = first_ = n;
- }
+ push_back(n);
size_ += n->Size();
}
@@ -290,15 +204,9 @@ void AsanChunkFifoList::Push(AsanChunk *n) {
// ago. Not sure if we can or want to do anything with this.
AsanChunk *AsanChunkFifoList::Pop() {
CHECK(first_);
- AsanChunk *res = first_;
- first_ = first_->next;
- if (first_ == 0)
- last_ = 0;
- CHECK(size_ >= res->Size());
+ AsanChunk *res = front();
size_ -= res->Size();
- if (last_) {
- CHECK(!last_->next);
- }
+ pop_front();
return res;
}
@@ -315,14 +223,13 @@ struct PageGroup {
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];
{
- ScopedLock lock(&mu_);
+ BlockingMutexLock lock(&mu_);
for (uptr i = 0; i < n_chunks; i++) {
if (!(*fl)) {
*fl = GetNewChunks(size_class);
@@ -340,7 +247,7 @@ class MallocInfo {
void SwallowThreadLocalMallocStorage(AsanThreadLocalMallocStorage *x,
bool eat_free_lists) {
CHECK(flags()->quarantine_size > 0);
- ScopedLock lock(&mu_);
+ BlockingMutexLock lock(&mu_);
AsanChunkFifoList *q = &x->quarantine_;
if (q->size() > 0) {
quarantine_.PushList(q);
@@ -364,23 +271,24 @@ class MallocInfo {
}
void BypassThreadLocalQuarantine(AsanChunk *chunk) {
- ScopedLock lock(&mu_);
+ BlockingMutexLock lock(&mu_);
quarantine_.Push(chunk);
}
- AsanChunk *FindMallocedOrFreed(uptr addr, uptr access_size) {
- ScopedLock lock(&mu_);
- return FindChunkByAddr(addr);
+ AsanChunk *FindChunkByAddr(uptr addr) {
+ BlockingMutexLock lock(&mu_);
+ return FindChunkByAddrUnlocked(addr);
}
uptr AllocationSize(uptr ptr) {
if (!ptr) return 0;
- ScopedLock lock(&mu_);
+ 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;
- // first, check if this is our memory
- PageGroup *g = FindPageGroupUnlocked(ptr);
- if (!g) return 0;
- AsanChunk *m = PtrToChunk(ptr);
if (m->chunk_state == CHUNK_ALLOCATED) {
return m->used_size;
} else {
@@ -397,7 +305,7 @@ class MallocInfo {
}
void PrintStatus() {
- ScopedLock lock(&mu_);
+ BlockingMutexLock lock(&mu_);
uptr malloced = 0;
Printf(" MallocInfo: in quarantine: %zu malloced: %zu; ",
@@ -415,7 +323,7 @@ class MallocInfo {
}
PageGroup *FindPageGroup(uptr addr) {
- ScopedLock lock(&mu_);
+ BlockingMutexLock lock(&mu_);
return FindPageGroupUnlocked(addr);
}
@@ -462,14 +370,14 @@ class MallocInfo {
return left_chunk;
// Choose based on offset.
uptr l_offset = 0, r_offset = 0;
- CHECK(left_chunk->AddrIsAtRight(addr, 1, &l_offset));
- CHECK(right_chunk->AddrIsAtLeft(addr, 1, &r_offset));
+ 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 *FindChunkByAddr(uptr addr) {
+ AsanChunk *FindChunkByAddrUnlocked(uptr addr) {
PageGroup *g = FindPageGroupUnlocked(addr);
if (!g) return 0;
CHECK(g->size_of_chunk);
@@ -482,17 +390,18 @@ class MallocInfo {
m->chunk_state == CHUNK_AVAILABLE ||
m->chunk_state == CHUNK_QUARANTINE);
uptr offset = 0;
- if (m->AddrIsInside(addr, 1, &offset))
+ AsanChunkView m_view(m);
+ if (m_view.AddrIsInside(addr, 1, &offset))
return m;
- if (m->AddrIsAtRight(addr, 1, &offset)) {
+ 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->AddrIsAtLeft(addr, 1, &offset));
+ 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;
@@ -533,12 +442,13 @@ class MallocInfo {
uptr mmap_size = Max(size, kMinMmapSize);
uptr n_chunks = mmap_size / size;
CHECK(n_chunks * size == mmap_size);
- if (size < kPageSize) {
+ 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 += kPageSize;
+ mmap_size += PageSize;
}
CHECK(n_chunks > 0);
u8 *mem = MmapNewPagesAndPoisonShadow(mmap_size);
@@ -564,14 +474,14 @@ class MallocInfo {
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)ASAN_ARRAY_SIZE(page_groups_));
+ CHECK(idx < (int)ARRAY_SIZE(page_groups_));
page_groups_[idx] = pg;
return res;
}
AsanChunk *free_lists_[kNumberOfSizeClasses];
AsanChunkFifoList quarantine_;
- AsanLock mu_;
+ BlockingMutex mu_;
PageGroup *page_groups_[kMaxAvailableRam / kMinMmapSize];
atomic_uint32_t n_page_groups_;
@@ -584,42 +494,12 @@ void AsanThreadLocalMallocStorage::CommitBack() {
malloc_info.SwallowThreadLocalMallocStorage(this, true);
}
-static void Describe(uptr addr, uptr access_size) {
- AsanChunk *m = malloc_info.FindMallocedOrFreed(addr, access_size);
- if (!m) return;
- m->DescribeAddress(addr, access_size);
- CHECK(m->alloc_tid >= 0);
- AsanThreadSummary *alloc_thread =
- asanThreadRegistry().FindByTid(m->alloc_tid);
- AsanStackTrace alloc_stack;
- AsanStackTrace::UncompressStack(&alloc_stack, m->compressed_alloc_stack(),
- m->compressed_alloc_stack_size());
- AsanThread *t = asanThreadRegistry().GetCurrent();
- CHECK(t);
- if (m->free_tid != kInvalidTid) {
- AsanThreadSummary *free_thread =
- asanThreadRegistry().FindByTid(m->free_tid);
- AsanPrintf("freed by thread T%d here:\n", free_thread->tid());
- AsanStackTrace free_stack;
- AsanStackTrace::UncompressStack(&free_stack, m->compressed_free_stack(),
- m->compressed_free_stack_size());
- free_stack.PrintStack();
- AsanPrintf("previously allocated by thread T%d here:\n",
- alloc_thread->tid());
-
- alloc_stack.PrintStack();
- t->summary()->Announce();
- free_thread->Announce();
- alloc_thread->Announce();
- } else {
- AsanPrintf("allocated by thread T%d here:\n", alloc_thread->tid());
- alloc_stack.PrintStack();
- t->summary()->Announce();
- alloc_thread->Announce();
- }
+AsanChunkView FindHeapChunkByAddress(uptr address) {
+ return AsanChunkView(malloc_info.FindChunkByAddr(address));
}
-static u8 *Allocate(uptr alignment, uptr size, AsanStackTrace *stack) {
+static u8 *Allocate(uptr alignment, uptr size, StackTrace *stack,
+ AllocType alloc_type) {
__asan_init();
CHECK(stack);
if (size == 0) {
@@ -676,6 +556,7 @@ static u8 *Allocate(uptr alignment, uptr size, AsanStackTrace *stack) {
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;
@@ -697,7 +578,7 @@ static u8 *Allocate(uptr alignment, uptr size, AsanStackTrace *stack) {
CHECK(m->Beg() == addr);
m->alloc_tid = t ? t->tid() : 0;
m->free_tid = kInvalidTid;
- AsanStackTrace::CompressStack(stack, m->compressed_alloc_stack(),
+ StackTrace::CompressStack(stack, m->compressed_alloc_stack(),
m->compressed_alloc_stack_size());
PoisonShadow(addr, rounded_size, 0);
if (size < rounded_size) {
@@ -710,7 +591,7 @@ static u8 *Allocate(uptr alignment, uptr size, AsanStackTrace *stack) {
return (u8*)addr;
}
-static void Deallocate(u8 *ptr, AsanStackTrace *stack) {
+static void Deallocate(u8 *ptr, StackTrace *stack, AllocType alloc_type) {
if (!ptr) return;
CHECK(stack);
@@ -726,24 +607,21 @@ static void Deallocate(u8 *ptr, AsanStackTrace *stack) {
memory_order_acq_rel);
if (old_chunk_state == CHUNK_QUARANTINE) {
- AsanReport("ERROR: AddressSanitizer attempting double-free on %p:\n", ptr);
- stack->PrintStack();
- Describe((uptr)ptr, 1);
- ShowStatsAndAbort();
+ ReportDoubleFree((uptr)ptr, stack);
} else if (old_chunk_state != CHUNK_ALLOCATED) {
- AsanReport("ERROR: AddressSanitizer attempting free on address "
- "which was not malloc()-ed: %p\n", ptr);
- stack->PrintStack();
- ShowStatsAndAbort();
+ 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;
- AsanStackTrace::CompressStack(stack, m->compressed_free_stack(),
+ 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);
@@ -769,7 +647,7 @@ static void Deallocate(u8 *ptr, AsanStackTrace *stack) {
}
static u8 *Reallocate(u8 *old_ptr, uptr new_size,
- AsanStackTrace *stack) {
+ StackTrace *stack) {
CHECK(old_ptr && new_size);
// Statistics.
@@ -781,114 +659,112 @@ static u8 *Reallocate(u8 *old_ptr, uptr new_size,
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);
+ 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);
+ Deallocate(old_ptr, stack, FROM_MALLOC);
}
return new_ptr;
}
} // namespace __asan
-// Malloc hooks declaration.
-// ASAN_NEW_HOOK(ptr, size) is called immediately after
-// allocation of "size" bytes, which returned "ptr".
-// ASAN_DELETE_HOOK(ptr) is called immediately before
-// deallocation of "ptr".
-// If ASAN_NEW_HOOK or ASAN_DELETE_HOOK is defined, user
-// program must provide implementation of this hook.
-// If macro is undefined, the hook is no-op.
-#ifdef ASAN_NEW_HOOK
-extern "C" void ASAN_NEW_HOOK(void *ptr, uptr size);
-#else
-static inline void ASAN_NEW_HOOK(void *ptr, uptr size) { }
-#endif
-
-#ifdef ASAN_DELETE_HOOK
-extern "C" void ASAN_DELETE_HOOK(void *ptr);
-#else
-static inline void ASAN_DELETE_HOOK(void *ptr) { }
+#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 *asan_memalign(uptr alignment, uptr size, AsanStackTrace *stack) {
- void *ptr = (void*)Allocate(alignment, size, stack);
- ASAN_NEW_HOOK(ptr, size);
+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;
}
-void asan_free(void *ptr, AsanStackTrace *stack) {
- ASAN_DELETE_HOOK(ptr);
- Deallocate((u8*)ptr, stack);
+SANITIZER_INTERFACE_ATTRIBUTE
+void asan_free(void *ptr, StackTrace *stack, AllocType alloc_type) {
+ ASAN_FREE_HOOK(ptr);
+ Deallocate((u8*)ptr, stack, alloc_type);
}
-void *asan_malloc(uptr size, AsanStackTrace *stack) {
- void *ptr = (void*)Allocate(0, size, stack);
- ASAN_NEW_HOOK(ptr, size);
+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, AsanStackTrace *stack) {
- void *ptr = (void*)Allocate(0, nmemb * size, stack);
+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_NEW_HOOK(ptr, nmemb * size);
+ ASAN_MALLOC_HOOK(ptr, size);
return ptr;
}
-void *asan_realloc(void *p, uptr size, AsanStackTrace *stack) {
+void *asan_realloc(void *p, uptr size, StackTrace *stack) {
if (p == 0) {
- void *ptr = (void*)Allocate(0, size, stack);
- ASAN_NEW_HOOK(ptr, size);
+ void *ptr = (void*)Allocate(0, size, stack, FROM_MALLOC);
+ ASAN_MALLOC_HOOK(ptr, size);
return ptr;
} else if (size == 0) {
- ASAN_DELETE_HOOK(p);
- Deallocate((u8*)p, stack);
+ ASAN_FREE_HOOK(p);
+ Deallocate((u8*)p, stack, FROM_MALLOC);
return 0;
}
return Reallocate((u8*)p, size, stack);
}
-void *asan_valloc(uptr size, AsanStackTrace *stack) {
- void *ptr = (void*)Allocate(kPageSize, size, stack);
- ASAN_NEW_HOOK(ptr, size);
+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, AsanStackTrace *stack) {
- size = RoundUpTo(size, kPageSize);
+void *asan_pvalloc(uptr size, StackTrace *stack) {
+ uptr PageSize = GetPageSizeCached();
+ size = RoundUpTo(size, PageSize);
if (size == 0) {
// pvalloc(0) should allocate one page.
- size = kPageSize;
+ size = PageSize;
}
- void *ptr = (void*)Allocate(kPageSize, size, stack);
- ASAN_NEW_HOOK(ptr, size);
+ 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,
- AsanStackTrace *stack) {
- void *ptr = Allocate(alignment, size, stack);
+ StackTrace *stack) {
+ void *ptr = Allocate(alignment, size, stack, FROM_MALLOC);
CHECK(IsAligned((uptr)ptr, alignment));
- ASAN_NEW_HOOK(ptr, size);
+ ASAN_MALLOC_HOOK(ptr, size);
*memptr = ptr;
return 0;
}
-uptr asan_malloc_usable_size(void *ptr, AsanStackTrace *stack) {
+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)) {
- AsanReport("ERROR: AddressSanitizer attempting to call "
- "malloc_usable_size() for pointer which is "
- "not owned: %p\n", ptr);
- stack->PrintStack();
- Describe((uptr)ptr, 1);
- ShowStatsAndAbort();
+ ReportMallocUsableSizeNotOwned((uptr)ptr, stack);
}
return usable_size;
}
@@ -897,10 +773,6 @@ uptr asan_mz_size(const void *ptr) {
return malloc_info.AllocationSize((uptr)ptr);
}
-void DescribeHeapAddress(uptr addr, uptr access_size) {
- Describe(addr, access_size);
-}
-
void asan_mz_force_lock() {
malloc_info.ForceLock();
}
@@ -909,170 +781,11 @@ void asan_mz_force_unlock() {
malloc_info.ForceUnlock();
}
-// ---------------------- Fake stack-------------------- {{{1
-FakeStack::FakeStack() {
- CHECK(REAL(memset) != 0);
- REAL(memset)(this, 0, sizeof(*this));
-}
-
-bool FakeStack::AddrIsInSizeClass(uptr addr, uptr size_class) {
- uptr mem = allocated_size_classes_[size_class];
- uptr size = ClassMmapSize(size_class);
- bool res = mem && addr >= mem && addr < mem + size;
- return res;
-}
-
-uptr FakeStack::AddrIsInFakeStack(uptr addr) {
- for (uptr i = 0; i < kNumberOfSizeClasses; i++) {
- if (AddrIsInSizeClass(addr, i)) return allocated_size_classes_[i];
- }
- return 0;
-}
-
-// We may want to compute this during compilation.
-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)));
- uptr res = log < kMinStackFrameSizeLog ? 0 : log - kMinStackFrameSizeLog;
- CHECK(res < kNumberOfSizeClasses);
- CHECK(ClassSize(res) >= rounded_size);
- return res;
-}
-
-void FakeFrameFifo::FifoPush(FakeFrame *node) {
- CHECK(node);
- node->next = 0;
- if (first_ == 0 && last_ == 0) {
- first_ = last_ = node;
- } else {
- CHECK(first_);
- CHECK(last_);
- last_->next = node;
- last_ = node;
- }
-}
-
-FakeFrame *FakeFrameFifo::FifoPop() {
- CHECK(first_ && last_ && "Exhausted fake stack");
- FakeFrame *res = 0;
- if (first_ == last_) {
- res = first_;
- first_ = last_ = 0;
- } else {
- res = first_;
- first_ = first_->next;
- }
- return res;
-}
-
-void FakeStack::Init(uptr stack_size) {
- stack_size_ = stack_size;
- alive_ = true;
-}
-
-void FakeStack::Cleanup() {
- alive_ = false;
- for (uptr i = 0; i < kNumberOfSizeClasses; i++) {
- uptr mem = allocated_size_classes_[i];
- if (mem) {
- PoisonShadow(mem, ClassMmapSize(i), 0);
- allocated_size_classes_[i] = 0;
- UnmapOrDie((void*)mem, ClassMmapSize(i));
- }
- }
-}
-
-uptr FakeStack::ClassMmapSize(uptr size_class) {
- return RoundUpToPowerOfTwo(stack_size_);
-}
-
-void FakeStack::AllocateOneSizeClass(uptr size_class) {
- CHECK(ClassMmapSize(size_class) >= kPageSize);
- uptr new_mem = (uptr)MmapOrDie(
- ClassMmapSize(size_class), __FUNCTION__);
- // Printf("T%d new_mem[%zu]: %p-%p mmap %zu\n",
- // asanThreadRegistry().GetCurrent()->tid(),
- // size_class, new_mem, new_mem + ClassMmapSize(size_class),
- // ClassMmapSize(size_class));
- uptr i;
- for (i = 0; i < ClassMmapSize(size_class);
- i += ClassSize(size_class)) {
- size_classes_[size_class].FifoPush((FakeFrame*)(new_mem + i));
- }
- CHECK(i == ClassMmapSize(size_class));
- allocated_size_classes_[size_class] = new_mem;
-}
-
-uptr FakeStack::AllocateStack(uptr size, uptr real_stack) {
- if (!alive_) return real_stack;
- CHECK(size <= kMaxStackMallocSize && size > 1);
- uptr size_class = ComputeSizeClass(size);
- if (!allocated_size_classes_[size_class]) {
- AllocateOneSizeClass(size_class);
- }
- FakeFrame *fake_frame = size_classes_[size_class].FifoPop();
- CHECK(fake_frame);
- fake_frame->size_minus_one = size - 1;
- fake_frame->real_stack = real_stack;
- while (FakeFrame *top = call_stack_.top()) {
- if (top->real_stack > real_stack) break;
- call_stack_.LifoPop();
- DeallocateFrame(top);
- }
- call_stack_.LifoPush(fake_frame);
- uptr ptr = (uptr)fake_frame;
- PoisonShadow(ptr, size, 0);
- return ptr;
-}
-
-void FakeStack::DeallocateFrame(FakeFrame *fake_frame) {
- CHECK(alive_);
- uptr size = fake_frame->size_minus_one + 1;
- uptr size_class = ComputeSizeClass(size);
- CHECK(allocated_size_classes_[size_class]);
- uptr ptr = (uptr)fake_frame;
- CHECK(AddrIsInSizeClass(ptr, size_class));
- CHECK(AddrIsInSizeClass(ptr + size - 1, size_class));
- size_classes_[size_class].FifoPush(fake_frame);
-}
-
-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);
- PoisonShadow(ptr, size, kAsanStackAfterReturnMagic);
-}
-
} // namespace __asan
// ---------------------- Interface ---------------- {{{1
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();
- if (!t) {
- // TSD is gone, use the real stack.
- return real_stack;
- }
- uptr ptr = t->fake_stack().AllocateStack(size, real_stack);
- // Printf("__asan_stack_malloc %p %zu %p\n", ptr, size, real_stack);
- return ptr;
-}
-
-void __asan_stack_free(uptr ptr, uptr size, uptr real_stack) {
- if (!flags()->use_fake_stack) return;
- if (ptr != real_stack) {
- FakeStack::OnFree(ptr, size, real_stack);
- }
-}
-
// ASan allocator doesn't reserve extra bytes, so normally we would
// just return "size".
uptr __asan_get_estimated_allocated_size(uptr size) {
@@ -1089,12 +802,9 @@ uptr __asan_get_allocated_size(const void *p) {
uptr allocated_size = malloc_info.AllocationSize((uptr)p);
// Die if p is not malloced or if it is already freed.
if (allocated_size == 0) {
- AsanReport("ERROR: AddressSanitizer attempting to call "
- "__asan_get_allocated_size() for pointer which is "
- "not owned: %p\n", p);
- PRINT_CURRENT_STACK();
- Describe((uptr)p, 1);
- ShowStatsAndAbort();
+ 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 2aed59853868..cca24edad81f 100644
--- a/lib/asan/asan_allocator.h
+++ b/lib/asan/asan_allocator.h
@@ -17,13 +17,76 @@
#include "asan_internal.h"
#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 {
+ FROM_MALLOC = 1, // Memory block came from malloc, calloc, realloc, etc.
+ FROM_NEW = 2, // Memory block came from operator new.
+ FROM_NEW_BR = 3 // Memory block came from operator new [ ]
+};
+
static const uptr kNumberOfSizeClasses = 255;
struct AsanChunk;
-class AsanChunkFifoList {
+class AsanChunkView {
+ public:
+ explicit AsanChunkView(AsanChunk *chunk) : chunk_(chunk) {}
+ bool IsValid() { return chunk_ != 0; }
+ uptr Beg(); // first byte of user memory.
+ uptr End(); // last byte of user memory.
+ uptr UsedSize(); // size requested by the user.
+ uptr AllocTid();
+ uptr FreeTid();
+ void GetAllocStack(StackTrace *stack);
+ void GetFreeStack(StackTrace *stack);
+ bool AddrIsInside(uptr addr, uptr access_size, uptr *offset) {
+ if (addr >= Beg() && (addr + access_size) <= End()) {
+ *offset = addr - Beg();
+ return true;
+ }
+ return false;
+ }
+ bool AddrIsAtLeft(uptr addr, uptr access_size, uptr *offset) {
+ (void)access_size;
+ if (addr < Beg()) {
+ *offset = Beg() - addr;
+ return true;
+ }
+ 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();
+ return true;
+ }
+ return false;
+ }
+
+ private:
+ AsanChunk *const chunk_;
+};
+
+AsanChunkView FindHeapChunkByAddress(uptr address);
+
+// List of AsanChunks with total size.
+class AsanChunkFifoList: public IntrusiveList<AsanChunk> {
public:
explicit AsanChunkFifoList(LinkerInitialized) { }
AsanChunkFifoList() { clear(); }
@@ -32,25 +95,31 @@ class AsanChunkFifoList {
AsanChunk *Pop();
uptr size() { return size_; }
void clear() {
- first_ = last_ = 0;
+ IntrusiveList<AsanChunk>::clear();
size_ = 0;
}
private:
- AsanChunk *first_;
- AsanChunk *last_;
uptr size_;
};
struct AsanThreadLocalMallocStorage {
explicit AsanThreadLocalMallocStorage(LinkerInitialized x)
- : quarantine_(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();
};
@@ -108,6 +177,7 @@ class FakeStack {
// 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.
@@ -137,23 +207,70 @@ class FakeStack {
FakeFrameLifo call_stack_;
};
-void *asan_memalign(uptr alignment, uptr size, AsanStackTrace *stack);
-void asan_free(void *ptr, AsanStackTrace *stack);
+void *asan_memalign(uptr alignment, uptr size, StackTrace *stack,
+ AllocType alloc_type);
+void asan_free(void *ptr, StackTrace *stack, AllocType alloc_type);
-void *asan_malloc(uptr size, AsanStackTrace *stack);
-void *asan_calloc(uptr nmemb, uptr size, AsanStackTrace *stack);
-void *asan_realloc(void *p, uptr size, AsanStackTrace *stack);
-void *asan_valloc(uptr size, AsanStackTrace *stack);
-void *asan_pvalloc(uptr size, AsanStackTrace *stack);
+void *asan_malloc(uptr size, StackTrace *stack);
+void *asan_calloc(uptr nmemb, uptr size, StackTrace *stack);
+void *asan_realloc(void *p, uptr size, StackTrace *stack);
+void *asan_valloc(uptr size, StackTrace *stack);
+void *asan_pvalloc(uptr size, StackTrace *stack);
int asan_posix_memalign(void **memptr, uptr alignment, uptr size,
- AsanStackTrace *stack);
-uptr asan_malloc_usable_size(void *ptr, AsanStackTrace *stack);
+ StackTrace *stack);
+uptr asan_malloc_usable_size(void *ptr, StackTrace *stack);
uptr asan_mz_size(const void *ptr);
void asan_mz_force_lock();
void asan_mz_force_unlock();
-void DescribeHeapAddress(uptr addr, uptr access_size);
+
+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
new file mode 100644
index 000000000000..42d8b29afd6b
--- /dev/null
+++ b/lib/asan/asan_allocator2.cc
@@ -0,0 +1,710 @@
+//===-- asan_allocator2.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, 2-nd version.
+// 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_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_internal_defs.h"
+#include "sanitizer_common/sanitizer_list.h"
+#include "sanitizer_common/sanitizer_stackdepot.h"
+#include "sanitizer_common/sanitizer_quarantine.h"
+
+namespace __asan {
+
+struct AsanMapUnmapCallback {
+ void OnMap(uptr p, uptr size) const {
+ PoisonShadow(p, size, kAsanHeapLeftRedzoneMagic);
+ // Statistics.
+ AsanStats &thread_stats = asanThreadRegistry().GetCurrentThreadStats();
+ thread_stats.mmaps++;
+ thread_stats.mmaped += size;
+ }
+ void OnUnmap(uptr p, uptr size) const {
+ PoisonShadow(p, size, 0);
+ // We are about to unmap a chunk of user memory.
+ // Mark the corresponding shadow memory as not needed.
+ // Since asan's mapping is compacting, the shadow chunk may be
+ // not page-aligned, so we only flush the page-aligned portion.
+ uptr page_size = GetPageSizeCached();
+ uptr shadow_beg = RoundUpTo(MemToShadow(p), page_size);
+ uptr shadow_end = RoundDownTo(MemToShadow(p + size), page_size);
+ FlushUnneededShadowMemory(shadow_beg, shadow_end - shadow_beg);
+ // Statistics.
+ AsanStats &thread_stats = asanThreadRegistry().GetCurrentThreadStats();
+ thread_stats.munmaps++;
+ thread_stats.munmaped += size;
+ }
+};
+
+#if SANITIZER_WORDSIZE == 64
+const uptr kAllocatorSpace = 0x600000000000ULL;
+const uptr kAllocatorSize = 0x10000000000ULL; // 1T.
+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;
+typedef SizeClassAllocator32<0, kAddressSpaceSize, 16,
+ SizeClassMap, AsanMapUnmapCallback> PrimaryAllocator;
+#endif
+
+typedef SizeClassAllocatorLocalCache<PrimaryAllocator> AllocatorCache;
+typedef LargeMmapAllocator<AsanMapUnmapCallback> SecondaryAllocator;
+typedef CombinedAllocator<PrimaryAllocator, AllocatorCache,
+ SecondaryAllocator> Allocator;
+
+// We can not use THREADLOCAL because it is not supported on some of the
+// platforms we care about (OSX 10.6, Android).
+// static THREADLOCAL AllocatorCache cache;
+AllocatorCache *GetAllocatorCache(AsanThreadLocalMallocStorage *ms) {
+ CHECK(ms);
+ CHECK_LE(sizeof(AllocatorCache), sizeof(ms->allocator2_cache));
+ return reinterpret_cast<AllocatorCache *>(ms->allocator2_cache);
+}
+
+static Allocator allocator;
+
+static const uptr kMaxAllowedMallocSize =
+ FIRST_32_SECOND_64(3UL << 30, 8UL << 30);
+
+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.
+// CHUNK_QUARANTINE: the chunk was freed and put into quarantine zone.
+enum {
+ CHUNK_AVAILABLE = 0, // 0 is the default value even if we didn't set it.
+ CHUNK_ALLOCATED = 2,
+ CHUNK_QUARANTINE = 3
+};
+
+// Valid redzone sizes are 16, 32, 64, ... 2048, so we encode them in 3 bits.
+// We use adaptive redzones: for larger allocation larger redzones are used.
+static u32 RZLog2Size(u32 rz_log) {
+ CHECK_LT(rz_log, 8);
+ return 16 << rz_log;
+}
+
+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;
+ CHECK_EQ(rz_size, RZLog2Size(res));
+ return res;
+}
+
+static uptr ComputeRZLog(uptr user_requested_size) {
+ u32 rz_log =
+ user_requested_size <= 64 - 16 ? 0 :
+ user_requested_size <= 128 - 32 ? 1 :
+ user_requested_size <= 512 - 64 ? 2 :
+ user_requested_size <= 4096 - 128 ? 3 :
+ user_requested_size <= (1 << 14) - 256 ? 4 :
+ user_requested_size <= (1 << 15) - 512 ? 5 :
+ user_requested_size <= (1 << 16) - 1024 ? 6 : 7;
+ return Max(rz_log, RZSize2Log(flags()->redzone));
+}
+
+// The memory chunk allocated from the underlying allocator looks like this:
+// L L L L L L H H U U U U U U R R
+// L -- left redzone words (0 or more bytes)
+// H -- ChunkHeader (16 bytes), which is also a part of the left redzone.
+// U -- user memory.
+// R -- right redzone (0 or more bytes)
+// ChunkBase consists of ChunkHeader and other bytes that overlap with user
+// memory.
+
+// If a memory chunk is allocated by memalign and we had to increase the
+// allocation size to achieve the proper alignment, then we store this magic
+// value in the first uptr word of the memory block and store the address of
+// ChunkBase in the next uptr.
+// M B ? ? ? L L L L L L H H U U U U U U
+// M -- magic value kMemalignMagic
+// B -- address of ChunkHeader pointing to the first 'H'
+static const uptr kMemalignMagic = 0xCC6E96B9;
+
+struct ChunkHeader {
+ // 1-st 8 bytes.
+ u32 chunk_state : 8; // Must be first.
+ u32 alloc_tid : 24;
+
+ u32 free_tid : 24;
+ u32 from_memalign : 1;
+ u32 alloc_type : 2;
+ u32 rz_log : 3;
+ // 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
+ // SecondaryAllocator's metadata.
+ u32 user_requested_size;
+ u32 alloc_context_id;
+};
+
+struct ChunkBase : ChunkHeader {
+ // Header2, intersects with user memory.
+ AsanChunk *next;
+ u32 free_context_id;
+};
+
+static const uptr kChunkHeaderSize = sizeof(ChunkHeader);
+static const uptr kChunkHeader2Size = sizeof(ChunkBase) - kChunkHeaderSize;
+COMPILER_CHECK(kChunkHeaderSize == 16);
+COMPILER_CHECK(kChunkHeader2Size <= 16);
+
+struct AsanChunk: ChunkBase {
+ uptr Beg() { return reinterpret_cast<uptr>(this) + kChunkHeaderSize; }
+ uptr UsedSize() {
+ if (user_requested_size != SizeClassMap::kMaxSize)
+ return user_requested_size;
+ return *reinterpret_cast<uptr *>(allocator.GetMetaData(AllocBeg()));
+ }
+ void *AllocBeg() {
+ if (from_memalign)
+ 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.
+ u32 *AllocStackBeg() {
+ return (u32*)(Beg() - RZLog2Size(rz_log));
+ }
+ uptr AllocStackSize() {
+ CHECK_LE(RZLog2Size(rz_log), kChunkHeaderSize);
+ return (RZLog2Size(rz_log) - kChunkHeaderSize) / sizeof(u32);
+ }
+ u32 *FreeStackBeg() {
+ return (u32*)(Beg() + kChunkHeader2Size);
+ }
+ uptr FreeStackSize() {
+ if (user_requested_size < kChunkHeader2Size) return 0;
+ uptr available = RoundUpTo(user_requested_size, SHADOW_GRANULARITY);
+ return (available - kChunkHeader2Size) / sizeof(u32);
+ }
+};
+
+uptr AsanChunkView::Beg() { return chunk_->Beg(); }
+uptr AsanChunkView::End() { return Beg() + UsedSize(); }
+uptr AsanChunkView::UsedSize() { return chunk_->UsedSize(); }
+uptr AsanChunkView::AllocTid() { return chunk_->alloc_tid; }
+uptr AsanChunkView::FreeTid() { return chunk_->free_tid; }
+
+static void GetStackTraceFromId(u32 id, StackTrace *stack) {
+ CHECK(id);
+ uptr size = 0;
+ const uptr *trace = StackDepotGet(id, &size);
+ CHECK_LT(size, kStackTraceMax);
+ internal_memcpy(stack->trace, trace, sizeof(uptr) * size);
+ stack->size = size;
+}
+
+void AsanChunkView::GetAllocStack(StackTrace *stack) {
+ if (flags()->use_stack_depot)
+ GetStackTraceFromId(chunk_->alloc_context_id, stack);
+ else
+ StackTrace::UncompressStack(stack, chunk_->AllocStackBeg(),
+ chunk_->AllocStackSize());
+}
+
+void AsanChunkView::GetFreeStack(StackTrace *stack) {
+ if (flags()->use_stack_depot)
+ GetStackTraceFromId(chunk_->free_context_id, stack);
+ else
+ StackTrace::UncompressStack(stack, chunk_->FreeStackBeg(),
+ chunk_->FreeStackSize());
+}
+
+struct QuarantineCallback;
+typedef Quarantine<QuarantineCallback, AsanChunk> AsanQuarantine;
+typedef AsanQuarantine::Cache QuarantineCache;
+static AsanQuarantine quarantine(LINKER_INITIALIZED);
+static QuarantineCache fallback_quarantine_cache(LINKER_INITIALIZED);
+static AllocatorCache fallback_allocator_cache;
+static SpinMutex fallback_mutex;
+
+QuarantineCache *GetQuarantineCache(AsanThreadLocalMallocStorage *ms) {
+ CHECK(ms);
+ CHECK_LE(sizeof(QuarantineCache), sizeof(ms->quarantine_cache));
+ return reinterpret_cast<QuarantineCache *>(ms->quarantine_cache);
+}
+
+struct QuarantineCallback {
+ explicit QuarantineCallback(AllocatorCache *cache)
+ : cache_(cache) {
+ }
+
+ void Recycle(AsanChunk *m) {
+ CHECK(m->chunk_state == CHUNK_QUARANTINE);
+ m->chunk_state = CHUNK_AVAILABLE;
+ CHECK_NE(m->alloc_tid, kInvalidTid);
+ CHECK_NE(m->free_tid, kInvalidTid);
+ PoisonShadow(m->Beg(),
+ RoundUpTo(m->UsedSize(), SHADOW_GRANULARITY),
+ kAsanHeapLeftRedzoneMagic);
+ void *p = reinterpret_cast<void *>(m->AllocBeg());
+ if (m->from_memalign) {
+ uptr *memalign_magic = reinterpret_cast<uptr *>(p);
+ CHECK_EQ(memalign_magic[0], kMemalignMagic);
+ CHECK_EQ(memalign_magic[1], reinterpret_cast<uptr>(m));
+ }
+
+ // Statistics.
+ AsanStats &thread_stats = asanThreadRegistry().GetCurrentThreadStats();
+ thread_stats.real_frees++;
+ thread_stats.really_freed += m->UsedSize();
+
+ allocator.Deallocate(cache_, p);
+ }
+
+ void *Allocate(uptr size) {
+ return allocator.Allocate(cache_, size, 1, false);
+ }
+
+ void Deallocate(void *p) {
+ allocator.Deallocate(cache_, p);
+ }
+
+ AllocatorCache *cache_;
+};
+
+static void Init() {
+ static int inited = 0;
+ if (inited) return;
+ __asan_init();
+ inited = true; // this must happen before any threads are created.
+ allocator.Init();
+ quarantine.Init((uptr)flags()->quarantine_size, kMaxThreadLocalQuarantine);
+}
+
+static void *Allocate(uptr size, uptr alignment, StackTrace *stack,
+ AllocType alloc_type) {
+ Init();
+ 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.
+ }
+ 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 needed_size = rounded_size + rz_size;
+ if (alignment > min_alignment)
+ needed_size += alignment;
+ bool using_primary_allocator = true;
+ // If we are allocating from the secondary allocator, there will be no
+ // automatic right redzone, so add the right redzone manually.
+ if (!PrimaryAllocator::CanAllocate(needed_size, alignment)) {
+ needed_size += rz_size;
+ using_primary_allocator = false;
+ }
+ CHECK(IsAligned(needed_size, min_alignment));
+ if (size > kMaxAllowedMallocSize || needed_size > kMaxAllowedMallocSize) {
+ Report("WARNING: AddressSanitizer failed to allocate %p bytes\n",
+ (void*)size);
+ return 0;
+ }
+
+ AsanThread *t = asanThreadRegistry().GetCurrent();
+ void *allocated;
+ if (t) {
+ AllocatorCache *cache = GetAllocatorCache(&t->malloc_storage());
+ allocated = allocator.Allocate(cache, needed_size, 8, false);
+ } else {
+ SpinMutexLock l(&fallback_mutex);
+ AllocatorCache *cache = &fallback_allocator_cache;
+ allocated = allocator.Allocate(cache, needed_size, 8, false);
+ }
+ uptr alloc_beg = reinterpret_cast<uptr>(allocated);
+ // Clear the first allocated word (an old kMemalignMagic may still be there).
+ reinterpret_cast<uptr *>(alloc_beg)[0] = 0;
+ uptr alloc_end = alloc_beg + needed_size;
+ uptr beg_plus_redzone = alloc_beg + rz_size;
+ uptr user_beg = beg_plus_redzone;
+ if (!IsAligned(user_beg, alignment))
+ user_beg = RoundUpTo(user_beg, alignment);
+ uptr user_end = user_beg + size;
+ 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;
+ m->alloc_tid = alloc_tid;
+ CHECK_EQ(alloc_tid, m->alloc_tid); // Does alloc_tid fit into the bitfield?
+ m->free_tid = kInvalidTid;
+ m->from_memalign = user_beg != beg_plus_redzone;
+ if (m->from_memalign) {
+ CHECK_LE(beg_plus_redzone + 2 * sizeof(uptr), user_beg);
+ uptr *memalign_magic = reinterpret_cast<uptr *>(alloc_beg);
+ memalign_magic[0] = kMemalignMagic;
+ memalign_magic[1] = chunk_beg;
+ }
+ if (using_primary_allocator) {
+ CHECK(size);
+ m->user_requested_size = size;
+ CHECK(allocator.FromPrimary(allocated));
+ } else {
+ CHECK(!allocator.FromPrimary(allocated));
+ m->user_requested_size = SizeClassMap::kMaxSize;
+ uptr *meta = reinterpret_cast<uptr *>(allocator.GetMetaData(allocated));
+ meta[0] = size;
+ meta[1] = chunk_beg;
+ }
+
+ if (flags()->use_stack_depot) {
+ m->alloc_context_id = StackDepotPut(stack->trace, stack->size);
+ } else {
+ m->alloc_context_id = 0;
+ StackTrace::CompressStack(stack, m->AllocStackBeg(), m->AllocStackSize());
+ }
+
+ uptr size_rounded_down_to_granularity = RoundDownTo(size, SHADOW_GRANULARITY);
+ // Unpoison the bulk of the memory region.
+ 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) {
+ u8 *shadow = (u8*)MemToShadow(user_beg + size_rounded_down_to_granularity);
+ *shadow = size & (SHADOW_GRANULARITY - 1);
+ }
+
+ AsanStats &thread_stats = asanThreadRegistry().GetCurrentThreadStats();
+ thread_stats.mallocs++;
+ thread_stats.malloced += size;
+ thread_stats.malloced_redzones += needed_size - size;
+ uptr class_id = Min(kNumberOfSizeClasses, SizeClassMap::ClassID(needed_size));
+ thread_stats.malloced_by_size[class_id]++;
+ if (needed_size > SizeClassMap::kMaxSize)
+ thread_stats.malloc_large++;
+
+ void *res = reinterpret_cast<void *>(user_beg);
+ 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);
+
+ // 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 (m->alloc_type != alloc_type && flags()->alloc_dealloc_mismatch)
+ ReportAllocTypeMismatch((uptr)ptr, stack,
+ (AllocType)m->alloc_type, (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();
+ m->free_tid = t ? t->tid() : 0;
+ if (flags()->use_stack_depot) {
+ m->free_context_id = StackDepotPut(stack->trace, stack->size);
+ } else {
+ 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();
+ thread_stats.frees++;
+ thread_stats.freed += m->UsedSize();
+
+ // Push into quarantine.
+ if (t) {
+ AsanThreadLocalMallocStorage *ms = &t->malloc_storage();
+ AllocatorCache *ac = GetAllocatorCache(ms);
+ quarantine.Put(GetQuarantineCache(ms), QuarantineCallback(ac),
+ m, m->UsedSize());
+ } else {
+ SpinMutexLock l(&fallback_mutex);
+ AllocatorCache *ac = &fallback_allocator_cache;
+ quarantine.Put(&fallback_quarantine_cache, QuarantineCallback(ac),
+ m, m->UsedSize());
+ }
+
+ ASAN_FREE_HOOK(ptr);
+}
+
+static void *Reallocate(void *old_ptr, uptr new_size, StackTrace *stack) {
+ CHECK(old_ptr && new_size);
+ uptr p = reinterpret_cast<uptr>(old_ptr);
+ uptr chunk_beg = p - kChunkHeaderSize;
+ AsanChunk *m = reinterpret_cast<AsanChunk *>(chunk_beg);
+
+ AsanStats &thread_stats = asanThreadRegistry().GetCurrentThreadStats();
+ thread_stats.reallocs++;
+ thread_stats.realloced += new_size;
+
+ CHECK(m->chunk_state == CHUNK_ALLOCATED);
+ uptr old_size = m->UsedSize();
+ uptr memcpy_size = Min(new_size, old_size);
+ void *new_ptr = Allocate(new_size, 8, 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;
+}
+
+static AsanChunk *GetAsanChunkByAddr(uptr p) {
+ void *ptr = reinterpret_cast<void *>(p);
+ uptr alloc_beg = reinterpret_cast<uptr>(allocator.GetBlockBegin(ptr));
+ if (!alloc_beg) return 0;
+ uptr *memalign_magic = reinterpret_cast<uptr *>(alloc_beg);
+ if (memalign_magic[0] == kMemalignMagic) {
+ AsanChunk *m = reinterpret_cast<AsanChunk *>(memalign_magic[1]);
+ CHECK(m->from_memalign);
+ return m;
+ }
+ if (!allocator.FromPrimary(ptr)) {
+ uptr *meta = reinterpret_cast<uptr *>(
+ allocator.GetMetaData(reinterpret_cast<void *>(alloc_beg)));
+ AsanChunk *m = reinterpret_cast<AsanChunk *>(meta[1]);
+ return m;
+ }
+ uptr actual_size = allocator.GetActuallyAllocatedSize(ptr);
+ CHECK_LE(actual_size, SizeClassMap::kMaxSize);
+ // We know the actually allocted size, but we don't know the redzone size.
+ // Just try all possible redzone sizes.
+ for (u32 rz_log = 0; rz_log < 8; rz_log++) {
+ u32 rz_size = RZLog2Size(rz_log);
+ uptr max_possible_size = actual_size - rz_size;
+ if (ComputeRZLog(max_possible_size) != rz_log)
+ continue;
+ return reinterpret_cast<AsanChunk *>(
+ alloc_beg + rz_size - kChunkHeaderSize);
+ }
+ return 0;
+}
+
+static uptr AllocationSize(uptr p) {
+ AsanChunk *m = GetAsanChunkByAddr(p);
+ if (!m) return 0;
+ if (m->chunk_state != CHUNK_ALLOCATED) return 0;
+ if (m->Beg() != p) return 0;
+ return m->UsedSize();
+}
+
+// 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 over freed chunk and freed chunk
+ // over available chunk.
+ if (left_chunk->chunk_state != right_chunk->chunk_state) {
+ if (left_chunk->chunk_state == CHUNK_ALLOCATED)
+ return left_chunk;
+ if (right_chunk->chunk_state == CHUNK_ALLOCATED)
+ return right_chunk;
+ if (left_chunk->chunk_state == CHUNK_QUARANTINE)
+ return left_chunk;
+ if (right_chunk->chunk_state == CHUNK_QUARANTINE)
+ return right_chunk;
+ }
+ // Same chunk_state: 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;
+}
+
+AsanChunkView FindHeapChunkByAddress(uptr addr) {
+ AsanChunk *m1 = GetAsanChunkByAddr(addr);
+ if (!m1) return AsanChunkView(m1);
+ uptr 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.
+ // Search a bit to the left to see if there is another chunk.
+ AsanChunk *m2 = 0;
+ for (uptr l = 1; l < GetPageSizeCached(); l++) {
+ m2 = GetAsanChunkByAddr(addr - l);
+ if (m2 == m1) continue; // Still the same chunk.
+ break;
+ }
+ if (m2 && AsanChunkView(m2).AddrIsAtRight(addr, 1, &offset))
+ m1 = ChooseChunk(addr, m2, m1);
+ }
+ return AsanChunkView(m1);
+}
+
+void AsanThreadLocalMallocStorage::CommitBack() {
+ AllocatorCache *ac = GetAllocatorCache(this);
+ quarantine.Drain(GetQuarantineCache(this), QuarantineCallback(ac));
+ allocator.SwallowCache(GetAllocatorCache(this));
+}
+
+void PrintInternalAllocatorStats() {
+ allocator.PrintStats();
+}
+
+SANITIZER_INTERFACE_ATTRIBUTE
+void *asan_memalign(uptr alignment, uptr size, StackTrace *stack,
+ AllocType alloc_type) {
+ return Allocate(size, alignment, stack, alloc_type);
+}
+
+SANITIZER_INTERFACE_ATTRIBUTE
+void asan_free(void *ptr, StackTrace *stack, AllocType alloc_type) {
+ Deallocate(ptr, stack, alloc_type);
+}
+
+SANITIZER_INTERFACE_ATTRIBUTE
+void *asan_malloc(uptr size, StackTrace *stack) {
+ return Allocate(size, 8, stack, FROM_MALLOC);
+}
+
+void *asan_calloc(uptr nmemb, uptr size, StackTrace *stack) {
+ void *ptr = Allocate(nmemb * size, 8, stack, FROM_MALLOC);
+ if (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);
+ if (size == 0) {
+ Deallocate(p, stack, FROM_MALLOC);
+ return 0;
+ }
+ return Reallocate(p, size, stack);
+}
+
+void *asan_valloc(uptr size, StackTrace *stack) {
+ return Allocate(size, GetPageSizeCached(), stack, FROM_MALLOC);
+}
+
+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;
+ }
+ return Allocate(size, PageSize, stack, FROM_MALLOC);
+}
+
+int asan_posix_memalign(void **memptr, uptr alignment, uptr size,
+ StackTrace *stack) {
+ void *ptr = Allocate(size, alignment, stack, FROM_MALLOC);
+ CHECK(IsAligned((uptr)ptr, alignment));
+ *memptr = ptr;
+ return 0;
+}
+
+uptr asan_malloc_usable_size(void *ptr, StackTrace *stack) {
+ CHECK(stack);
+ if (ptr == 0) return 0;
+ uptr usable_size = AllocationSize(reinterpret_cast<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) {
+ UNIMPLEMENTED();
+ return 0;
+}
+
+void asan_mz_force_lock() {
+ UNIMPLEMENTED();
+}
+
+void asan_mz_force_unlock() {
+ UNIMPLEMENTED();
+}
+
+} // namespace __asan
+
+// ---------------------- Interface ---------------- {{{1
+using namespace __asan; // NOLINT
+
+// ASan allocator doesn't reserve extra bytes, so normally we would
+// just return "size". We don't want to expose our redzone sizes, etc here.
+uptr __asan_get_estimated_allocated_size(uptr size) {
+ return size;
+}
+
+bool __asan_get_ownership(const void *p) {
+ uptr ptr = reinterpret_cast<uptr>(p);
+ return (ptr == kReturnOnZeroMalloc) || (AllocationSize(ptr) > 0);
+}
+
+uptr __asan_get_allocated_size(const void *p) {
+ if (p == 0) return 0;
+ 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) {
+ GET_STACK_TRACE_FATAL_HERE;
+ ReportAsanGetAllocatedSizeNotOwned(ptr, &stack);
+ }
+ return allocated_size;
+}
+
+#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
+
+
+#endif // ASAN_ALLOCATOR_VERSION
diff --git a/lib/asan/asan_fake_stack.cc b/lib/asan/asan_fake_stack.cc
new file mode 100644
index 000000000000..7c5a16312d46
--- /dev/null
+++ b/lib/asan/asan_fake_stack.cc
@@ -0,0 +1,182 @@
+//===-- asan_fake_stack.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.
+//
+// FakeStack is used to detect use-after-return bugs.
+//===----------------------------------------------------------------------===//
+#include "asan_allocator.h"
+#include "asan_thread.h"
+#include "asan_thread_registry.h"
+#include "sanitizer/asan_interface.h"
+
+namespace __asan {
+
+FakeStack::FakeStack() {
+ CHECK(REAL(memset) != 0);
+ REAL(memset)(this, 0, sizeof(*this));
+}
+
+bool FakeStack::AddrIsInSizeClass(uptr addr, uptr size_class) {
+ uptr mem = allocated_size_classes_[size_class];
+ uptr size = ClassMmapSize(size_class);
+ bool res = mem && addr >= mem && addr < mem + size;
+ return res;
+}
+
+uptr FakeStack::AddrIsInFakeStack(uptr addr) {
+ for (uptr i = 0; i < kNumberOfSizeClasses; i++) {
+ if (AddrIsInSizeClass(addr, i)) return allocated_size_classes_[i];
+ }
+ return 0;
+}
+
+// We may want to compute this during compilation.
+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)));
+ uptr res = log < kMinStackFrameSizeLog ? 0 : log - kMinStackFrameSizeLog;
+ CHECK(res < kNumberOfSizeClasses);
+ CHECK(ClassSize(res) >= rounded_size);
+ return res;
+}
+
+void FakeFrameFifo::FifoPush(FakeFrame *node) {
+ CHECK(node);
+ node->next = 0;
+ if (first_ == 0 && last_ == 0) {
+ first_ = last_ = node;
+ } else {
+ CHECK(first_);
+ CHECK(last_);
+ last_->next = node;
+ last_ = node;
+ }
+}
+
+FakeFrame *FakeFrameFifo::FifoPop() {
+ CHECK(first_ && last_ && "Exhausted fake stack");
+ FakeFrame *res = 0;
+ if (first_ == last_) {
+ res = first_;
+ first_ = last_ = 0;
+ } else {
+ res = first_;
+ first_ = first_->next;
+ }
+ return res;
+}
+
+void FakeStack::Init(uptr stack_size) {
+ stack_size_ = stack_size;
+ alive_ = true;
+}
+
+void FakeStack::Cleanup() {
+ alive_ = false;
+ for (uptr i = 0; i < kNumberOfSizeClasses; i++) {
+ uptr mem = allocated_size_classes_[i];
+ if (mem) {
+ PoisonShadow(mem, ClassMmapSize(i), 0);
+ allocated_size_classes_[i] = 0;
+ UnmapOrDie((void*)mem, ClassMmapSize(i));
+ }
+ }
+}
+
+uptr FakeStack::ClassMmapSize(uptr size_class) {
+ return RoundUpToPowerOfTwo(stack_size_);
+}
+
+void FakeStack::AllocateOneSizeClass(uptr size_class) {
+ CHECK(ClassMmapSize(size_class) >= GetPageSizeCached());
+ uptr new_mem = (uptr)MmapOrDie(
+ ClassMmapSize(size_class), __FUNCTION__);
+ // Printf("T%d new_mem[%zu]: %p-%p mmap %zu\n",
+ // asanThreadRegistry().GetCurrent()->tid(),
+ // size_class, new_mem, new_mem + ClassMmapSize(size_class),
+ // ClassMmapSize(size_class));
+ uptr i;
+ for (i = 0; i < ClassMmapSize(size_class);
+ i += ClassSize(size_class)) {
+ size_classes_[size_class].FifoPush((FakeFrame*)(new_mem + i));
+ }
+ CHECK(i == ClassMmapSize(size_class));
+ allocated_size_classes_[size_class] = new_mem;
+}
+
+uptr FakeStack::AllocateStack(uptr size, uptr real_stack) {
+ if (!alive_) return real_stack;
+ CHECK(size <= kMaxStackMallocSize && size > 1);
+ uptr size_class = ComputeSizeClass(size);
+ if (!allocated_size_classes_[size_class]) {
+ AllocateOneSizeClass(size_class);
+ }
+ FakeFrame *fake_frame = size_classes_[size_class].FifoPop();
+ CHECK(fake_frame);
+ fake_frame->size_minus_one = size - 1;
+ fake_frame->real_stack = real_stack;
+ while (FakeFrame *top = call_stack_.top()) {
+ if (top->real_stack > real_stack) break;
+ call_stack_.LifoPop();
+ DeallocateFrame(top);
+ }
+ call_stack_.LifoPush(fake_frame);
+ uptr ptr = (uptr)fake_frame;
+ PoisonShadow(ptr, size, 0);
+ return ptr;
+}
+
+void FakeStack::DeallocateFrame(FakeFrame *fake_frame) {
+ CHECK(alive_);
+ uptr size = fake_frame->size_minus_one + 1;
+ uptr size_class = ComputeSizeClass(size);
+ CHECK(allocated_size_classes_[size_class]);
+ uptr ptr = (uptr)fake_frame;
+ CHECK(AddrIsInSizeClass(ptr, size_class));
+ CHECK(AddrIsInSizeClass(ptr + size - 1, size_class));
+ size_classes_[size_class].FifoPush(fake_frame);
+}
+
+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);
+ PoisonShadow(ptr, size, kAsanStackAfterReturnMagic);
+}
+
+} // namespace __asan
+
+// ---------------------- Interface ---------------- {{{1
+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();
+ if (!t) {
+ // TSD is gone, use the real stack.
+ return real_stack;
+ }
+ uptr ptr = t->fake_stack().AllocateStack(size, real_stack);
+ // Printf("__asan_stack_malloc %p %zu %p\n", ptr, size, real_stack);
+ return ptr;
+}
+
+void __asan_stack_free(uptr ptr, uptr size, uptr real_stack) {
+ if (!flags()->use_fake_stack) return;
+ if (ptr != real_stack) {
+ FakeStack::OnFree(ptr, size, real_stack);
+ }
+}
diff --git a/lib/asan/asan_flags.h b/lib/asan/asan_flags.h
index ca9cf84ba6c5..d7b21ea4a45f 100644
--- a/lib/asan/asan_flags.h
+++ b/lib/asan/asan_flags.h
@@ -15,7 +15,7 @@
#ifndef ASAN_FLAGS_H
#define ASAN_FLAGS_H
-#include "sanitizer_common/sanitizer_interface_defs.h"
+#include "sanitizer/common_interface_defs.h"
// ASan flag values can be defined in three ways:
// 1) initialized with default values at startup.
@@ -23,11 +23,6 @@
// __asan_default_options().
// 3) overriden from env variable ASAN_OPTIONS.
-extern "C" {
-// Can be overriden by user.
-const char *__asan_default_options() SANITIZER_WEAK_ATTRIBUTE;
-} // extern "C"
-
namespace __asan {
struct Flags {
@@ -48,7 +43,9 @@ struct Flags {
// on globals, 1 - detect buffer overflow, 2 - print data about registered
// globals).
int report_globals;
- // Max number of stack frames kept for each allocation.
+ // 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.
@@ -87,6 +84,28 @@ struct Flags {
// By default, disable core dumper on 64-bit - it makes little sense
// to dump 16T+ core.
bool disable_core;
+ // 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;
+ // Report errors on malloc/delete, new/free, new/delete[], etc.
+ bool alloc_dealloc_mismatch;
+ // Use stack depot instead of storing stacks in the redzones.
+ bool use_stack_depot;
};
Flags *flags();
diff --git a/lib/asan/asan_globals.cc b/lib/asan/asan_globals.cc
index f8c4040b8e86..4e18bb8e2355 100644
--- a/lib/asan/asan_globals.cc
+++ b/lib/asan/asan_globals.cc
@@ -12,15 +12,14 @@
// Handle globals.
//===----------------------------------------------------------------------===//
#include "asan_interceptors.h"
-#include "asan_interface.h"
#include "asan_internal.h"
-#include "asan_lock.h"
#include "asan_mapping.h"
+#include "asan_report.h"
#include "asan_stack.h"
#include "asan_stats.h"
#include "asan_thread.h"
-
-#include <ctype.h>
+#include "sanitizer/asan_interface.h"
+#include "sanitizer_common/sanitizer_mutex.h"
namespace __asan {
@@ -31,9 +30,10 @@ struct ListOfGlobals {
ListOfGlobals *next;
};
-static AsanLock mu_for_globals(LINKER_INITIALIZED);
-static ListOfGlobals *list_of_globals;
-static LowLevelAllocator allocator_for_globals(LINKER_INITIALIZED);
+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;
@@ -55,48 +55,16 @@ void PoisonRedZones(const Global &g) {
}
}
-static uptr GetAlignedSize(uptr size) {
- return ((size + kGlobalAndStackRedzone - 1) / kGlobalAndStackRedzone)
- * kGlobalAndStackRedzone;
-}
-
- // Check if the global is a zero-terminated ASCII string. If so, print it.
-void PrintIfASCII(const Global &g) {
- for (uptr p = g.beg; p < g.beg + g.size - 1; p++) {
- if (!isascii(*(char*)p)) return;
- }
- if (*(char*)(g.beg + g.size - 1) != 0) return;
- AsanPrintf(" '%s' is ascii string '%s'\n", g.name, (char*)g.beg);
-}
-
-bool DescribeAddrIfMyRedZone(const Global &g, uptr addr) {
- if (addr < g.beg - kGlobalAndStackRedzone) return false;
- if (addr >= g.beg + g.size_with_redzone) return false;
- AsanPrintf("%p is located ", (void*)addr);
- if (addr < g.beg) {
- AsanPrintf("%zd bytes to the left", g.beg - addr);
- } else if (addr >= g.beg + g.size) {
- AsanPrintf("%zd bytes to the right", addr - (g.beg + g.size));
- } else {
- AsanPrintf("%zd bytes inside", addr - g.beg); // Can it happen?
- }
- AsanPrintf(" of global variable '%s' (0x%zx) of size %zu\n",
- g.name, g.beg, g.size);
- PrintIfASCII(g);
- return true;
-}
-
-
-bool DescribeAddrIfGlobal(uptr addr) {
+bool DescribeAddressIfGlobal(uptr addr) {
if (!flags()->report_globals) return false;
- ScopedLock lock(&mu_for_globals);
+ BlockingMutexLock lock(&mu_for_globals);
bool res = false;
- for (ListOfGlobals *l = list_of_globals; l; l = l->next) {
+ for (ListOfGlobals *l = list_of_all_globals; l; l = l->next) {
const Global &g = *l->g;
if (flags()->report_globals >= 2)
- AsanPrintf("Search Global: beg=%p size=%zu name=%s\n",
- (void*)g.beg, g.size, (char*)g.name);
- res |= DescribeAddrIfMyRedZone(g, addr);
+ Report("Search Global: beg=%p size=%zu name=%s\n",
+ (void*)g.beg, g.size, (char*)g.name);
+ res |= DescribeAddressRelativeToGlobal(addr, g);
}
return res;
}
@@ -106,6 +74,10 @@ bool DescribeAddrIfGlobal(uptr addr) {
// so we store the globals in a map.
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);
CHECK(flags()->report_globals);
CHECK(AddrIsInMem(g->beg));
CHECK(AddrIsAlignedByGranularity(g->beg));
@@ -114,11 +86,14 @@ static void RegisterGlobal(const Global *g) {
ListOfGlobals *l =
(ListOfGlobals*)allocator_for_globals.Allocate(sizeof(ListOfGlobals));
l->g = g;
- l->next = list_of_globals;
- list_of_globals = l;
- if (flags()->report_globals >= 2)
- Report("Added Global: beg=%p size=%zu name=%s\n",
- (void*)g->beg, g->size, g->name);
+ 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;
+ }
}
static void UnregisterGlobal(const Global *g) {
@@ -133,39 +108,83 @@ static void UnregisterGlobal(const Global *g) {
// 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);
+}
+
} // namespace __asan
// ---------------------- Interface ---------------- {{{1
using namespace __asan; // NOLINT
-// Register one global with a default redzone.
-void __asan_register_global(uptr addr, uptr size,
- const char *name) {
- if (!flags()->report_globals) return;
- ScopedLock lock(&mu_for_globals);
- Global *g = (Global *)allocator_for_globals.Allocate(sizeof(Global));
- g->beg = addr;
- g->size = size;
- g->size_with_redzone = GetAlignedSize(size) + kGlobalAndStackRedzone;
- g->name = name;
- RegisterGlobal(g);
-}
-
// Register an array of globals.
void __asan_register_globals(__asan_global *globals, uptr n) {
if (!flags()->report_globals) return;
- ScopedLock lock(&mu_for_globals);
+ BlockingMutexLock lock(&mu_for_globals);
for (uptr i = 0; i < n; i++) {
RegisterGlobal(&globals[i]);
}
}
// Unregister an array of globals.
-// We must do it when a shared objects gets dlclosed.
+// We must do this when a shared objects gets dlclosed.
void __asan_unregister_globals(__asan_global *globals, uptr n) {
if (!flags()->report_globals) return;
- ScopedLock lock(&mu_for_globals);
+ BlockingMutexLock lock(&mu_for_globals);
for (uptr i = 0; i < n; i++) {
UnregisterGlobal(&globals[i]);
}
}
+
+// This method runs immediately prior to dynamic initialization in each TU,
+// 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);
+ 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;
+ }
+ 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;
+ BlockingMutexLock lock(&mu_for_globals);
+ for (ListOfGlobals *l = list_of_dynamic_init_globals; l; l = l->next)
+ UnpoisonGlobal(l->g);
+}
diff --git a/lib/asan/asan_intercepted_functions.h b/lib/asan/asan_intercepted_functions.h
new file mode 100644
index 000000000000..a1faf713c130
--- /dev/null
+++ b/lib/asan/asan_intercepted_functions.h
@@ -0,0 +1,260 @@
+//===-- asan_intercepted_functions.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 containing prototypes for wrapper functions and wrappers
+//===----------------------------------------------------------------------===//
+#ifndef ASAN_INTERCEPTED_FUNCTIONS_H
+#define ASAN_INTERCEPTED_FUNCTIONS_H
+
+#include "asan_internal.h"
+#include "interception/interception.h"
+#include "sanitizer_common/sanitizer_platform_interceptors.h"
+
+#include <stdarg.h>
+
+using __sanitizer::uptr;
+
+// Use macro to describe if specific function should be
+// intercepted on a given platform.
+#if !defined(_WIN32)
+# 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
+#else
+# 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__)
+# define ASAN_USE_ALIAS_ATTRIBUTE_FOR_INDEX 1
+#else
+# define ASAN_USE_ALIAS_ATTRIBUTE_FOR_INDEX 0
+#endif
+
+#if !defined(__APPLE__)
+# define ASAN_INTERCEPT_STRNLEN 1
+#else
+# define ASAN_INTERCEPT_STRNLEN 0
+#endif
+
+#if defined(__linux__) && !defined(ANDROID)
+# define ASAN_INTERCEPT_SWAPCONTEXT 1
+#else
+# define ASAN_INTERCEPT_SWAPCONTEXT 0
+#endif
+
+#if !defined(ANDROID) && !defined(_WIN32)
+# 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)
+# define ASAN_INTERCEPT_SIGLONGJMP 1
+#else
+# define ASAN_INTERCEPT_SIGLONGJMP 0
+#endif
+
+#if ASAN_HAS_EXCEPTIONS && !defined(_WIN32)
+# 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);
+#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
+
+// 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
+
+#endif // ASAN_INTERCEPTED_FUNCTIONS_H
diff --git a/lib/asan/asan_interceptors.cc b/lib/asan/asan_interceptors.cc
index 2ce5826e1495..6170974d6f5e 100644
--- a/lib/asan/asan_interceptors.cc
+++ b/lib/asan/asan_interceptors.cc
@@ -14,136 +14,33 @@
#include "asan_interceptors.h"
#include "asan_allocator.h"
-#include "asan_interface.h"
+#include "asan_intercepted_functions.h"
#include "asan_internal.h"
#include "asan_mapping.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"
-// Use macro to describe if specific function should be
-// intercepted on a given platform.
-#if !defined(_WIN32)
-# define ASAN_INTERCEPT_ATOLL_AND_STRTOLL 1
-#else
-# define ASAN_INTERCEPT_ATOLL_AND_STRTOLL 0
-#endif
-
-#if !defined(__APPLE__)
-# define ASAN_INTERCEPT_STRNLEN 1
-#else
-# define ASAN_INTERCEPT_STRNLEN 0
-#endif
-
-#if defined(ANDROID) || defined(_WIN32)
-# define ASAN_INTERCEPT_SIGNAL_AND_SIGACTION 0
-#else
-# define ASAN_INTERCEPT_SIGNAL_AND_SIGACTION 1
-#endif
-
-// 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;
-int sigaction(int sig, const struct sigaction *act,
- struct sigaction *oldact);
-void *signal(int signum, void *handler);
-# endif
-
-// setjmp.h
-void longjmp(void* env, int value);
-# if !defined(_WIN32)
-void _longjmp(void *env, int value);
-# endif
-
-// string.h / strings.h
-int memcmp(const void *a1, const void *a2, uptr size);
-void* memmove(void *to, const void *from, uptr size);
-void* memcpy(void *to, const void *from, uptr size);
-void* memset(void *block, int c, uptr size);
-char* strchr(const char *str, int c);
-# if defined(__APPLE__)
-char* index(const char *string, int c);
-# endif
-char* strcat(char *to, const char* from); // NOLINT
-char *strncat(char *to, const char* from, uptr size);
-char* strcpy(char *to, const char* from); // NOLINT
-char* strncpy(char *to, const char* from, uptr size);
-int strcmp(const char *s1, const char* s2);
-int strncmp(const char *s1, const char* s2, uptr size);
-# if !defined(_WIN32)
-int strcasecmp(const char *s1, const char *s2);
-int strncasecmp(const char *s1, const char *s2, uptr n);
-char* strdup(const char *s);
-# endif
-uptr strlen(const char *s);
-# if ASAN_INTERCEPT_STRNLEN
-uptr strnlen(const char *s, uptr maxlen);
-# endif
-
-// stdlib.h
-int atoi(const char *nptr);
-long atol(const char *nptr); // NOLINT
-long strtol(const char *nptr, char **endptr, int base); // NOLINT
-# if ASAN_INTERCEPT_ATOLL_AND_STRTOLL
-long long atoll(const char *nptr); // NOLINT
-long long strtoll(const char *nptr, char **endptr, int base); // NOLINT
-# endif
-
-// 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 !defined(_WIN32)
-int pthread_create(void *thread, void *attr, void *(*start_routine)(void*),
- void *arg);
-# endif
-} // extern "C"
-#endif
-
namespace __asan {
-// Instruments read/write access to a single byte in memory.
-// On error calls __asan_report_error, which aborts the program.
-#define ACCESS_ADDRESS(address, isWrite) do { \
- if (!AddrIsInMem(address) || AddressIsPoisoned(address)) { \
- GET_CURRENT_PC_BP_SP; \
- __asan_report_error(pc, bp, sp, address, isWrite, /* access_size */ 1); \
- } \
-} while (0)
-
// 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.
-
-// Instruments read/write access to a memory range.
-// More complex implementation is possible, for now just
-// checking the first and the last byte of a range.
-#define ACCESS_MEMORY_RANGE(offset, size, isWrite) do { \
- if (size > 0) { \
- uptr ptr = (uptr)(offset); \
- ACCESS_ADDRESS(ptr, isWrite); \
- ACCESS_ADDRESS(ptr + (size) - 1, isWrite); \
- } \
+// 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 ASAN_READ_RANGE(offset, size) do { \
- ACCESS_MEMORY_RANGE(offset, size, false); \
-} while (0)
-
-#define ASAN_WRITE_RANGE(offset, size) do { \
- ACCESS_MEMORY_RANGE(offset, size, true); \
-} 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);
// Behavior of functions like "memcpy" or "strcpy" is undefined
// if memory intervals overlap. We report error in this case.
@@ -156,11 +53,9 @@ static inline bool RangesOverlap(const char *offset1, uptr length1,
const char *offset1 = (const char*)_offset1; \
const char *offset2 = (const char*)_offset2; \
if (RangesOverlap(offset1, length1, offset2, length2)) { \
- AsanReport("ERROR: AddressSanitizer %s-param-overlap: " \
- "memory ranges [%p,%p) and [%p, %p) overlap\n", \
- name, offset1, offset1 + length1, offset2, offset2 + length2); \
- PRINT_CURRENT_STACK(); \
- ShowStatsAndAbort(); \
+ GET_STACK_TRACE_FATAL_HERE; \
+ ReportStringFunctionMemoryRangesOverlap(name, offset1, length1, \
+ offset2, length2, &stack); \
} \
} while (0)
@@ -180,27 +75,47 @@ static inline uptr MaybeRealStrnlen(const char *s, uptr maxlen) {
return internal_strnlen(s, maxlen);
}
+void SetThreadName(const char *name) {
+ AsanThread *t = asanThreadRegistry().GetCurrent();
+ if (t)
+ t->summary()->set_name(name);
+}
+
} // namespace __asan
// ---------------------- Wrappers ---------------- {{{1
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(); \
+ } 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"
+
static thread_return_t THREAD_CALLING_CONV asan_thread_start(void *arg) {
AsanThread *t = (AsanThread*)arg;
asanThreadRegistry().SetCurrent(t);
return t->ThreadStart();
}
-#ifndef _WIN32
+#if ASAN_INTERCEPT_PTHREAD_CREATE
INTERCEPTOR(int, pthread_create, void *thread,
void *attr, void *(*start_routine)(void*), void *arg) {
- GET_STACK_TRACE_HERE(kStackTraceMax);
+ GET_STACK_TRACE_THREAD;
u32 current_tid = asanThreadRegistry().GetCurrentTidOrInvalid();
AsanThread *t = AsanThread::Create(current_tid, start_routine, arg, &stack);
asanThreadRegistry().RegisterThread(t);
return REAL(pthread_create)(thread, attr, asan_thread_start, t);
}
-#endif // !_WIN32
+#endif // ASAN_INTERCEPT_PTHREAD_CREATE
#if ASAN_INTERCEPT_SIGNAL_AND_SIGACTION
INTERCEPTOR(void*, signal, int signum, void *handler) {
@@ -223,28 +138,62 @@ DEFINE_REAL(int, sigaction, int signum, const struct sigaction *act,
struct sigaction *oldact);
#endif // ASAN_INTERCEPT_SIGNAL_AND_SIGACTION
+#if ASAN_INTERCEPT_SWAPCONTEXT
+static void ClearShadowMemoryForContextStack(uptr stack, uptr ssize) {
+ // Align to page size.
+ uptr PageSize = GetPageSizeCached();
+ uptr bottom = stack & ~(PageSize - 1);
+ ssize += stack - bottom;
+ ssize = RoundUpTo(ssize, PageSize);
+ static const uptr kMaxSaneContextStackSize = 1 << 22; // 4 Mb
+ if (ssize && ssize <= kMaxSaneContextStackSize) {
+ PoisonShadow(bottom, ssize, 0);
+ }
+}
+
+INTERCEPTOR(int, swapcontext, struct ucontext_t *oucp,
+ struct ucontext_t *ucp) {
+ static bool reported_warning = false;
+ if (!reported_warning) {
+ Report("WARNING: ASan doesn't fully support makecontext/swapcontext "
+ "functions and may produce false positives in some cases!\n");
+ reported_warning = true;
+ }
+ // Clear shadow memory for new context (it may share stack
+ // with current context).
+ uptr stack, ssize;
+ ReadContextStack(ucp, &stack, &ssize);
+ ClearShadowMemoryForContextStack(stack, ssize);
+ int res = REAL(swapcontext)(oucp, ucp);
+ // swapcontext technically does not return, but program may swap context to
+ // "oucp" later, that would look as if swapcontext() returned 0.
+ // We need to clear shadow for ucp once again, as it may be in arbitrary
+ // state.
+ ClearShadowMemoryForContextStack(stack, ssize);
+ return res;
+}
+#endif // ASAN_INTERCEPT_SWAPCONTEXT
+
INTERCEPTOR(void, longjmp, void *env, int val) {
__asan_handle_no_return();
REAL(longjmp)(env, val);
}
-#if !defined(_WIN32)
+#if ASAN_INTERCEPT__LONGJMP
INTERCEPTOR(void, _longjmp, void *env, int val) {
__asan_handle_no_return();
REAL(_longjmp)(env, val);
}
+#endif
+#if ASAN_INTERCEPT_SIGLONGJMP
INTERCEPTOR(void, siglongjmp, void *env, int val) {
__asan_handle_no_return();
REAL(siglongjmp)(env, val);
}
#endif
-#if ASAN_HAS_EXCEPTIONS == 1
-#ifdef __APPLE__
-extern "C" void __cxa_throw(void *a, void *b, void *c);
-#endif // __APPLE__
-
+#if ASAN_INTERCEPT___CXA_THROW
INTERCEPTOR(void, __cxa_throw, void *a, void *b, void *c) {
CHECK(REAL(__cxa_throw));
__asan_handle_no_return();
@@ -263,26 +212,22 @@ static void MlockIsUnsupported() {
}
extern "C" {
-INTERCEPTOR_ATTRIBUTE
-int mlock(const void *addr, uptr len) {
+INTERCEPTOR(int, mlock, const void *addr, uptr len) {
MlockIsUnsupported();
return 0;
}
-INTERCEPTOR_ATTRIBUTE
-int munlock(const void *addr, uptr len) {
+INTERCEPTOR(int, munlock, const void *addr, uptr len) {
MlockIsUnsupported();
return 0;
}
-INTERCEPTOR_ATTRIBUTE
-int mlockall(int flags) {
+INTERCEPTOR(int, mlockall, int flags) {
MlockIsUnsupported();
return 0;
}
-INTERCEPTOR_ATTRIBUTE
-int munlockall(void) {
+INTERCEPTOR(int, munlockall, void) {
MlockIsUnsupported();
return 0;
}
@@ -299,6 +244,7 @@ static inline int CharCaseCmp(unsigned char c1, unsigned char c2) {
}
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;
@@ -315,6 +261,7 @@ INTERCEPTOR(int, memcmp, const void *a1, const void *a2, uptr size) {
}
INTERCEPTOR(void*, memcpy, void *to, const void *from, uptr size) {
+ if (!asan_inited) return internal_memcpy(to, from, size);
// memcpy is called during __asan_init() from the internals
// of printf(...).
if (asan_init_is_running) {
@@ -327,25 +274,39 @@ INTERCEPTOR(void*, memcpy, void *to, const void *from, uptr size) {
// See http://llvm.org/bugs/show_bug.cgi?id=11763.
CHECK_RANGES_OVERLAP("memcpy", to, size, from, size);
}
- ASAN_WRITE_RANGE(from, size);
- ASAN_READ_RANGE(to, 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) {
+ if (!asan_inited) return internal_memmove(to, from, size);
if (asan_init_is_running) {
return REAL(memmove)(to, from, size);
}
ENSURE_ASAN_INITED();
if (flags()->replace_intrin) {
- ASAN_WRITE_RANGE(from, size);
- ASAN_READ_RANGE(to, 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) {
+ if (!asan_inited) return internal_memset(block, c, size);
// memset is called inside Printf.
if (asan_init_is_running) {
return REAL(memset)(block, c, size);
@@ -358,6 +319,12 @@ INTERCEPTOR(void*, memset, void *block, int c, uptr size) {
}
INTERCEPTOR(char*, strchr, const char *str, int c) {
+ if (!asan_inited) return internal_strchr(str, c);
+ // strchr is called inside create_purgeable_zone() when MallocGuardEdges=1 is
+ // used.
+ if (asan_init_is_running) {
+ return REAL(strchr)(str, c);
+ }
ENSURE_ASAN_INITED();
char *result = REAL(strchr)(str, c);
if (flags()->replace_str) {
@@ -367,37 +334,31 @@ INTERCEPTOR(char*, strchr, const char *str, int c) {
return result;
}
-#ifdef __linux__
+#if ASAN_INTERCEPT_INDEX
+# if ASAN_USE_ALIAS_ATTRIBUTE_FOR_INDEX
INTERCEPTOR(char*, index, const char *string, int c)
ALIAS(WRAPPER_NAME(strchr));
-#else
+# else
DEFINE_REAL(char*, index, const char *string, int c)
-#endif
-
-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);
-}
+# endif
+#endif // ASAN_INTERCEPT_INDEX
+// For both strcat() and strncat() we need to check the validity of |to|
+// argument irrespective of the |from| length.
INTERCEPTOR(char*, strcat, char *to, const char *from) { // NOLINT
ENSURE_ASAN_INITED();
if (flags()->replace_str) {
uptr from_length = REAL(strlen)(from);
ASAN_READ_RANGE(from, from_length + 1);
+ uptr to_length = REAL(strlen)(to);
+ ASAN_READ_RANGE(to, to_length);
+ ASAN_WRITE_RANGE(to + to_length, from_length + 1);
+ // If the copying actually happens, the |from| string should not overlap
+ // with the resulting string starting at |to|, which has a length of
+ // to_length + from_length + 1.
if (from_length > 0) {
- uptr to_length = REAL(strlen)(to);
- ASAN_READ_RANGE(to, to_length);
- ASAN_WRITE_RANGE(to + to_length, from_length + 1);
- CHECK_RANGES_OVERLAP("strcat", to, to_length + 1, from, from_length + 1);
+ CHECK_RANGES_OVERLAP("strcat", to, from_length + to_length + 1,
+ from, from_length + 1);
}
}
return REAL(strcat)(to, from); // NOLINT
@@ -405,23 +366,25 @@ INTERCEPTOR(char*, strcat, char *to, const char *from) { // NOLINT
INTERCEPTOR(char*, strncat, char *to, const char *from, uptr size) {
ENSURE_ASAN_INITED();
- if (flags()->replace_str && size > 0) {
+ if (flags()->replace_str) {
uptr from_length = MaybeRealStrnlen(from, size);
- ASAN_READ_RANGE(from, Min(size, from_length + 1));
+ uptr copy_length = Min(size, from_length + 1);
+ ASAN_READ_RANGE(from, copy_length);
uptr to_length = REAL(strlen)(to);
ASAN_READ_RANGE(to, to_length);
ASAN_WRITE_RANGE(to + to_length, from_length + 1);
if (from_length > 0) {
- CHECK_RANGES_OVERLAP("strncat", to, to_length + 1,
- from, Min(size, from_length + 1));
+ CHECK_RANGES_OVERLAP("strncat", to, to_length + copy_length + 1,
+ from, copy_length);
}
}
return REAL(strncat)(to, from, size);
}
INTERCEPTOR(int, strcmp, const char *s1, const char *s2) {
- if (!asan_inited) {
- return internal_strcmp(s1, s2);
+ if (!asan_inited) return internal_strcmp(s1, s2);
+ if (asan_init_is_running) {
+ return REAL(strcmp)(s1, s2);
}
ENSURE_ASAN_INITED();
unsigned char c1, c2;
@@ -437,6 +400,9 @@ INTERCEPTOR(int, strcmp, const char *s1, const char *s2) {
}
INTERCEPTOR(char*, strcpy, char *to, const char *from) { // NOLINT
+#if MAC_INTERPOSE_FUNCTIONS
+ if (!asan_inited) return REAL(strcpy)(to, from); // NOLINT
+#endif
// strcpy is called from malloc_default_purgeable_zone()
// in __asan::ReplaceSystemAlloc() on Mac.
if (asan_init_is_running) {
@@ -452,7 +418,17 @@ INTERCEPTOR(char*, strcpy, char *to, const char *from) { // NOLINT
return REAL(strcpy)(to, from); // NOLINT
}
+#if ASAN_INTERCEPT_STRDUP
INTERCEPTOR(char*, strdup, const char *s) {
+#if MAC_INTERPOSE_FUNCTIONS
+ // 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
+ // starts using mmap() instead.
+ // See also http://code.google.com/p/address-sanitizer/issues/detail?id=123.
+ if (!asan_inited) return REAL(strdup)(s);
+#endif
+ if (!asan_inited) return internal_strdup(s);
ENSURE_ASAN_INITED();
if (flags()->replace_str) {
uptr length = REAL(strlen)(s);
@@ -460,8 +436,10 @@ INTERCEPTOR(char*, strdup, const char *s) {
}
return REAL(strdup)(s);
}
+#endif
INTERCEPTOR(uptr, strlen, const char *s) {
+ if (!asan_inited) return internal_strlen(s);
// strlen is called from malloc_default_purgeable_zone()
// in __asan::ReplaceSystemAlloc() on Mac.
if (asan_init_is_running) {
@@ -475,6 +453,21 @@ 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;
@@ -488,8 +481,10 @@ INTERCEPTOR(int, strncasecmp, const char *s1, const char *s2, uptr 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()
// in __asan::ReplaceSystemAlloc() on Mac.
if (asan_init_is_running) {
@@ -566,6 +561,9 @@ INTERCEPTOR(long, strtol, const char *nptr, // NOLINT
}
INTERCEPTOR(int, atoi, const char *nptr) {
+#if MAC_INTERPOSE_FUNCTIONS
+ if (!asan_inited) return REAL(atoi)(nptr);
+#endif
ENSURE_ASAN_INITED();
if (!flags()->replace_str) {
return REAL(atoi)(nptr);
@@ -582,6 +580,9 @@ INTERCEPTOR(int, atoi, const char *nptr) {
}
INTERCEPTOR(long, atol, const char *nptr) { // NOLINT
+#if MAC_INTERPOSE_FUNCTIONS
+ if (!asan_inited) return REAL(atol)(nptr);
+#endif
ENSURE_ASAN_INITED();
if (!flags()->replace_str) {
return REAL(atol)(nptr);
@@ -638,7 +639,7 @@ INTERCEPTOR_WINAPI(DWORD, CreateThread,
void* security, uptr stack_size,
DWORD (__stdcall *start_routine)(void*), void* arg,
DWORD flags, void* tid) {
- GET_STACK_TRACE_HERE(kStackTraceMax);
+ GET_STACK_TRACE_THREAD;
u32 current_tid = asanThreadRegistry().GetCurrentTidOrInvalid();
AsanThread *t = AsanThread::Create(current_tid, start_routine, arg, &stack);
asanThreadRegistry().RegisterThread(t);
@@ -660,6 +661,12 @@ void InitializeAsanInterceptors() {
static bool was_called_once;
CHECK(was_called_once == false);
was_called_once = true;
+#if MAC_INTERPOSE_FUNCTIONS
+ return;
+#endif
+
+ SANITIZER_COMMON_INTERCEPTORS_INIT;
+
// Intercept mem* functions.
ASAN_INTERCEPT_FUNC(memcmp);
ASAN_INTERCEPT_FUNC(memmove);
@@ -667,7 +674,11 @@ void InitializeAsanInterceptors() {
if (PLATFORM_HAS_DIFFERENT_MEMCPY_AND_MEMMOVE) {
ASAN_INTERCEPT_FUNC(memcpy);
} else {
- REAL(memcpy) = REAL(memmove);
+#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.
@@ -679,19 +690,23 @@ void InitializeAsanInterceptors() {
ASAN_INTERCEPT_FUNC(strncat);
ASAN_INTERCEPT_FUNC(strncmp);
ASAN_INTERCEPT_FUNC(strncpy);
-#if !defined(_WIN32)
+#if ASAN_INTERCEPT_STRCASECMP_AND_STRNCASECMP
ASAN_INTERCEPT_FUNC(strcasecmp);
- ASAN_INTERCEPT_FUNC(strdup);
ASAN_INTERCEPT_FUNC(strncasecmp);
-# ifndef __APPLE__
+#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
ASAN_INTERCEPT_FUNC(index);
# else
CHECK(OVERRIDE_FUNCTION(index, WRAP(strchr)));
# endif
#endif
-#if ASAN_INTERCEPT_STRNLEN
- ASAN_INTERCEPT_FUNC(strnlen);
-#endif
ASAN_INTERCEPT_FUNC(atoi);
ASAN_INTERCEPT_FUNC(atol);
@@ -701,25 +716,37 @@ void InitializeAsanInterceptors() {
ASAN_INTERCEPT_FUNC(strtoll);
#endif
+#if ASAN_INTERCEPT_MLOCKX
+ // Intercept mlock/munlock.
+ ASAN_INTERCEPT_FUNC(mlock);
+ ASAN_INTERCEPT_FUNC(munlock);
+ ASAN_INTERCEPT_FUNC(mlockall);
+ ASAN_INTERCEPT_FUNC(munlockall);
+#endif
+
// Intecept signal- and jump-related functions.
ASAN_INTERCEPT_FUNC(longjmp);
#if ASAN_INTERCEPT_SIGNAL_AND_SIGACTION
ASAN_INTERCEPT_FUNC(sigaction);
ASAN_INTERCEPT_FUNC(signal);
#endif
-
-#if !defined(_WIN32)
+#if ASAN_INTERCEPT_SWAPCONTEXT
+ ASAN_INTERCEPT_FUNC(swapcontext);
+#endif
+#if ASAN_INTERCEPT__LONGJMP
ASAN_INTERCEPT_FUNC(_longjmp);
- INTERCEPT_FUNCTION(__cxa_throw);
-# if !defined(__APPLE__)
- // On Darwin siglongjmp tailcalls longjmp, so we don't want to intercept it
- // there.
+#endif
+#if ASAN_INTERCEPT_SIGLONGJMP
ASAN_INTERCEPT_FUNC(siglongjmp);
-# endif
+#endif
+
+ // Intercept exception handling functions.
+#if ASAN_INTERCEPT___CXA_THROW
+ INTERCEPT_FUNCTION(__cxa_throw);
#endif
// Intercept threading-related functions
-#if !defined(_WIN32)
+#if ASAN_INTERCEPT_PTHREAD_CREATE
ASAN_INTERCEPT_FUNC(pthread_create);
#endif
diff --git a/lib/asan/asan_interceptors.h b/lib/asan/asan_interceptors.h
index 32816920f7a3..3b3e90ef93ff 100644
--- a/lib/asan/asan_interceptors.h
+++ b/lib/asan/asan_interceptors.h
@@ -24,6 +24,7 @@ DECLARE_REAL(char*, strchr, const char *str, int c)
DECLARE_REAL(uptr, strlen, const char *s)
DECLARE_REAL(char*, strncpy, char *to, const char *from, uptr size)
DECLARE_REAL(uptr, strnlen, const char *s, uptr maxlen)
+DECLARE_REAL(char*, strstr, const char *s1, const char *s2)
struct sigaction;
DECLARE_REAL(int, sigaction, int signum, const struct sigaction *act,
struct sigaction *oldact)
diff --git a/lib/asan/asan_interface.h b/lib/asan/asan_interface.h
deleted file mode 100644
index c625a6217c0c..000000000000
--- a/lib/asan/asan_interface.h
+++ /dev/null
@@ -1,159 +0,0 @@
-//===-- asan_interface.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_H
-#define ASAN_INTERFACE_H
-
-#include "sanitizer_common/sanitizer_interface_defs.h"
-// ----------- ATTENTION -------------
-// This header should NOT include any other headers from ASan runtime.
-// All functions in this header are extern "C" and start with __asan_.
-
-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.
- void __asan_init() SANITIZER_INTERFACE_ATTRIBUTE;
-
- // This function should be called by the instrumented code.
- // 'addr' is the address of a global variable called 'name' of 'size' bytes.
- void __asan_register_global(uptr addr, uptr size, const char *name)
- SANITIZER_INTERFACE_ATTRIBUTE;
-
- // 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.
- };
-
- // 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 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;
-
- // Marks memory region [addr, addr+size) as unaddressable.
- // This memory must be previously allocated by the user program. Accessing
- // addresses in this region from instrumented code is forbidden until
- // this region is unpoisoned. This function is not guaranteed to poison
- // the whole region - it may poison only subregion of [addr, addr+size) due
- // to ASan alignment restrictions.
- // Method is NOT thread-safe in the sense that no two threads can
- // (un)poison memory in the same memory region simultaneously.
- void __asan_poison_memory_region(void const volatile *addr, uptr size)
- SANITIZER_INTERFACE_ATTRIBUTE;
- // Marks memory region [addr, addr+size) as addressable.
- // This memory must be previously allocated by the user program. Accessing
- // addresses in this region is allowed until this region is poisoned again.
- // This function may unpoison a superregion of [addr, addr+size) due to
- // ASan alignment restrictions.
- // Method is NOT thread-safe in the sense that no two threads can
- // (un)poison memory in the same memory region simultaneously.
- void __asan_unpoison_memory_region(void const volatile *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;
-
-// User code should use macro instead of functions.
-#if __has_feature(address_sanitizer)
-#define ASAN_POISON_MEMORY_REGION(addr, size) \
- __asan_poison_memory_region((addr), (size))
-#define ASAN_UNPOISON_MEMORY_REGION(addr, size) \
- __asan_unpoison_memory_region((addr), (size))
-#else
-#define ASAN_POISON_MEMORY_REGION(addr, size) \
- ((void)(addr), (void)(size))
-#define ASAN_UNPOISON_MEMORY_REGION(addr, size) \
- ((void)(addr), (void)(size))
-#endif
-
- // Returns true iff addr is poisoned (i.e. 1-byte read/write access to this
- // address will result in error report from AddressSanitizer).
- bool __asan_address_is_poisoned(void const volatile *addr)
- SANITIZER_INTERFACE_ATTRIBUTE;
-
- // This is an internal function that is called to report an error.
- // However it is still a part of the interface because users may want to
- // set a breakpoint on this function in a debugger.
- void __asan_report_error(uptr pc, uptr bp, uptr sp,
- uptr addr, bool is_write, uptr access_size)
- SANITIZER_INTERFACE_ATTRIBUTE;
-
- // Sets the exit code to use when reporting an error.
- // Returns the old value.
- int __asan_set_error_exit_code(int exit_code)
- SANITIZER_INTERFACE_ATTRIBUTE;
-
- // Sets the callback to be called right before death on error.
- // Passing 0 will unset the callback.
- void __asan_set_death_callback(void (*callback)(void))
- SANITIZER_INTERFACE_ATTRIBUTE;
-
- void __asan_set_error_report_callback(void (*callback)(const char*))
- SANITIZER_INTERFACE_ATTRIBUTE;
-
- // Returns the estimated number of bytes that will be reserved by allocator
- // for request of "size" bytes. If ASan allocator can't allocate that much
- // memory, returns the maximal possible allocation size, otherwise returns
- // "size".
- uptr __asan_get_estimated_allocated_size(uptr size)
- SANITIZER_INTERFACE_ATTRIBUTE;
- // Returns true if p was returned by the ASan allocator and
- // is not yet freed.
- bool __asan_get_ownership(const void *p)
- SANITIZER_INTERFACE_ATTRIBUTE;
- // Returns the number of bytes reserved for the pointer p.
- // Requires (get_ownership(p) == true) or (p == 0).
- uptr __asan_get_allocated_size(const void *p)
- SANITIZER_INTERFACE_ATTRIBUTE;
- // Number of bytes, allocated and not yet freed by the application.
- uptr __asan_get_current_allocated_bytes()
- SANITIZER_INTERFACE_ATTRIBUTE;
- // Number of bytes, mmaped by asan allocator to fulfill allocation requests.
- // Generally, for request of X bytes, allocator can reserve and add to free
- // lists a large number of chunks of size X to use them for future requests.
- // All these chunks count toward the heap size. Currently, allocator never
- // releases memory to OS (instead, it just puts freed chunks to free lists).
- uptr __asan_get_heap_size()
- SANITIZER_INTERFACE_ATTRIBUTE;
- // Number of bytes, mmaped by asan allocator, which can be used to fulfill
- // allocation requests. When a user program frees memory chunk, it can first
- // fall into quarantine and will count toward __asan_get_free_bytes() later.
- uptr __asan_get_free_bytes()
- SANITIZER_INTERFACE_ATTRIBUTE;
- // Number of bytes in unmapped pages, that are released to OS. Currently,
- // always returns 0.
- uptr __asan_get_unmapped_bytes()
- SANITIZER_INTERFACE_ATTRIBUTE;
- // Prints accumulated stats to stderr. Used for debugging.
- void __asan_print_accumulated_stats()
- SANITIZER_INTERFACE_ATTRIBUTE;
-} // namespace
-
-#endif // ASAN_INTERFACE_H
diff --git a/lib/asan/asan_internal.h b/lib/asan/asan_internal.h
index 8c1f32028f2c..5d3bffa814da 100644
--- a/lib/asan/asan_internal.h
+++ b/lib/asan/asan_internal.h
@@ -17,17 +17,13 @@
#include "asan_flags.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
-#if defined(_WIN32)
-extern "C" void* _ReturnAddress(void);
-# pragma intrinsic(_ReturnAddress)
-#endif // defined(_WIN32)
-
#define ASAN_DEFAULT_FAILURE_EXITCODE 1
#if defined(__linux__)
@@ -48,6 +44,13 @@ extern "C" void* _ReturnAddress(void);
# 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)
@@ -59,7 +62,11 @@ extern "C" void* _ReturnAddress(void);
// If set, asan will install its own SEGV signal handler.
#ifndef ASAN_NEEDS_SEGV
-# define ASAN_NEEDS_SEGV 1
+# if ASAN_ANDROID == 1
+# define ASAN_NEEDS_SEGV 0
+# else
+# define ASAN_NEEDS_SEGV 1
+# endif
#endif
// If set, asan will intercept C++ exception api call(s).
@@ -76,7 +83,11 @@ extern "C" void* _ReturnAddress(void);
// If set, values like allocator chunk size, as well as defaults for some flags
// will be changed towards less memory overhead.
#ifndef ASAN_LOW_MEMORY
-# define ASAN_LOW_MEMORY 0
+#if SANITIZER_WORDSIZE == 32
+# define ASAN_LOW_MEMORY 1
+#else
+# define ASAN_LOW_MEMORY 0
+# endif
#endif
// All internal functions in asan reside inside the __asan namespace
@@ -86,14 +97,11 @@ extern "C" void* _ReturnAddress(void);
namespace __asan {
class AsanThread;
-struct AsanStackTrace;
+using __sanitizer::StackTrace;
// asan_rtl.cc
void NORETURN ShowStatsAndAbort();
-// asan_globals.cc
-bool DescribeAddrIfGlobal(uptr addr);
-
void ReplaceOperatorsNewAndDelete();
// asan_malloc_linux.cc / asan_malloc_mac.cc
void ReplaceSystemMalloc();
@@ -103,10 +111,12 @@ void *AsanDoesNotSupportStaticLinkage();
void GetPcSpBp(void *context, uptr *pc, uptr *sp, uptr *bp);
+void MaybeReexec();
bool AsanInterceptsSignal(int signum);
void SetAlternateSignalStack();
void UnsetAlternateSignalStack();
void InstallSignalHandlers();
+void ReadContextStack(void *context, uptr *stack, uptr *ssize);
void AsanPlatformThreadInit();
// Wrapper for TLS/TSD.
@@ -115,9 +125,6 @@ void *AsanTSDGet();
void AsanTSDSet(void *tsd);
void AppendToErrorMessageBuffer(const char *buffer);
-// asan_printf.cc
-void AsanPrintf(const char *format, ...);
-void AsanReport(const char *format, ...);
// asan_poisoning.cc
// Poisons the shadow memory for "size" bytes starting from "addr".
@@ -138,33 +145,20 @@ bool PlatformHasDifferentMemcpyAndMemmove();
# define PLATFORM_HAS_DIFFERENT_MEMCPY_AND_MEMMOVE true
#endif // __APPLE__
+// Add convenient macro for interface functions that may be represented as
+// weak hooks.
+#define ASAN_MALLOC_HOOK(ptr, size) \
+ if (&__asan_malloc_hook) __asan_malloc_hook(ptr, size)
+#define ASAN_FREE_HOOK(ptr) \
+ if (&__asan_free_hook) __asan_free_hook(ptr)
+#define ASAN_ON_ERROR() \
+ if (&__asan_on_error) __asan_on_error()
+
extern int asan_inited;
// Used to avoid infinite recursion in __asan_init().
extern bool asan_init_is_running;
extern void (*death_callback)(void);
-enum LinkerInitialized { LINKER_INITIALIZED = 0 };
-
-#define ASAN_ARRAY_SIZE(a) (sizeof(a)/sizeof((a)[0]))
-
-#if !defined(_WIN32) || defined(__clang__)
-# define GET_CALLER_PC() (uptr)__builtin_return_address(0)
-# define GET_CURRENT_FRAME() (uptr)__builtin_frame_address(0)
-#else
-# define GET_CALLER_PC() (uptr)_ReturnAddress()
-// CaptureStackBackTrace doesn't need to know BP on Windows.
-// FIXME: This macro is still used when printing error reports though it's not
-// clear if the BP value is needed in the ASan reports on Windows.
-# define GET_CURRENT_FRAME() (uptr)0xDEADBEEF
-#endif
-
-#ifdef _WIN32
-# ifndef ASAN_USE_EXTERNAL_SYMBOLIZER
-# define ASAN_USE_EXTERNAL_SYMBOLIZER __asan_WinSymbolize
-bool __asan_WinSymbolize(const void *addr, char *out_buffer, int buffer_size);
-# endif
-#endif // _WIN32
-
// These magic values are written to shadow for better error reporting.
const int kAsanHeapLeftRedzoneMagic = 0xfa;
const int kAsanHeapRightRedzoneMagic = 0xfb;
@@ -174,26 +168,15 @@ const int kAsanStackMidRedzoneMagic = 0xf2;
const int kAsanStackRightRedzoneMagic = 0xf3;
const int kAsanStackPartialRedzoneMagic = 0xf4;
const int kAsanStackAfterReturnMagic = 0xf5;
+const int kAsanInitializationOrderMagic = 0xf6;
const int kAsanUserPoisonedMemoryMagic = 0xf7;
+const int kAsanStackUseAfterScopeMagic = 0xf8;
const int kAsanGlobalRedzoneMagic = 0xf9;
const int kAsanInternalHeapMagic = 0xfe;
static const uptr kCurrentStackFrameMagic = 0x41B58AB3;
static const uptr kRetiredStackFrameMagic = 0x45E0360E;
-// -------------------------- LowLevelAllocator ----- {{{1
-// A simple low-level memory allocator for internal use.
-class LowLevelAllocator {
- public:
- explicit LowLevelAllocator(LinkerInitialized) {}
- // 'size' must be a power of two.
- // Requires an external lock.
- void *Allocate(uptr size);
- private:
- char *allocated_end_;
- char *allocated_current_;
-};
-
} // namespace __asan
#endif // ASAN_INTERNAL_H
diff --git a/lib/asan/asan_linux.cc b/lib/asan/asan_linux.cc
index 9a3d6bdb15a6..845493de0956 100644
--- a/lib/asan/asan_linux.cc
+++ b/lib/asan/asan_linux.cc
@@ -15,8 +15,8 @@
#include "asan_interceptors.h"
#include "asan_internal.h"
-#include "asan_lock.h"
#include "asan_thread.h"
+#include "asan_thread_registry.h"
#include "sanitizer_common/sanitizer_libc.h"
#include "sanitizer_common/sanitizer_procmaps.h"
@@ -31,7 +31,7 @@
#include <unistd.h>
#include <unwind.h>
-#ifndef ANDROID
+#if !ASAN_ANDROID
// FIXME: where to get ucontext on Android?
#include <sys/ucontext.h>
#endif
@@ -40,13 +40,17 @@ extern "C" void* _DYNAMIC;
namespace __asan {
+void MaybeReexec() {
+ // No need to re-exec on Linux.
+}
+
void *AsanDoesNotSupportStaticLinkage() {
// This will fail to link with -static.
return &_DYNAMIC; // defined in link.h
}
void GetPcSpBp(void *context, uptr *pc, uptr *sp, uptr *bp) {
-#ifdef ANDROID
+#if ASAN_ANDROID
*pc = *sp = *bp = 0;
#elif defined(__arm__)
ucontext_t *ucontext = (ucontext_t*)context;
@@ -63,6 +67,27 @@ void GetPcSpBp(void *context, uptr *pc, uptr *sp, uptr *bp) {
*pc = ucontext->uc_mcontext.gregs[REG_EIP];
*bp = ucontext->uc_mcontext.gregs[REG_EBP];
*sp = ucontext->uc_mcontext.gregs[REG_ESP];
+# elif defined(__powerpc__) || defined(__powerpc64__)
+ ucontext_t *ucontext = (ucontext_t*)context;
+ *pc = ucontext->uc_mcontext.regs->nip;
+ *sp = ucontext->uc_mcontext.regs->gpr[PT_R1];
+ // The powerpc{,64}-linux ABIs do not specify r31 as the frame
+ // pointer, but GCC always uses r31 when we need a frame pointer.
+ *bp = ucontext->uc_mcontext.regs->gpr[PT_R31];
+# elif defined(__sparc__)
+ ucontext_t *ucontext = (ucontext_t*)context;
+ uptr *stk_ptr;
+# if defined (__arch64__)
+ *pc = ucontext->uc_mcontext.mc_gregs[MC_PC];
+ *sp = ucontext->uc_mcontext.mc_gregs[MC_O6];
+ stk_ptr = (uptr *) (*sp + 2047);
+ *bp = stk_ptr[15];
+# else
+ *pc = ucontext->uc_mcontext.gregs[REG_PC];
+ *sp = ucontext->uc_mcontext.gregs[REG_O6];
+ stk_ptr = (uptr *) *sp;
+ *bp = stk_ptr[15];
+# endif
#else
# error "Unsupported arch"
#endif
@@ -76,69 +101,35 @@ void AsanPlatformThreadInit() {
// Nothing here for now.
}
-AsanLock::AsanLock(LinkerInitialized) {
- // We assume that pthread_mutex_t initialized to all zeroes is a valid
- // unlocked mutex. We can not use PTHREAD_MUTEX_INITIALIZER as it triggers
- // a gcc warning:
- // extended initializer lists only available with -std=c++0x or -std=gnu++0x
-}
-
-void AsanLock::Lock() {
- CHECK(sizeof(pthread_mutex_t) <= sizeof(opaque_storage_));
- pthread_mutex_lock((pthread_mutex_t*)&opaque_storage_);
- CHECK(!owner_);
- owner_ = (uptr)pthread_self();
-}
-
-void AsanLock::Unlock() {
- CHECK(owner_ == (uptr)pthread_self());
- owner_ = 0;
- pthread_mutex_unlock((pthread_mutex_t*)&opaque_storage_);
-}
-
-#ifdef __arm__
-#define UNWIND_STOP _URC_END_OF_STACK
-#define UNWIND_CONTINUE _URC_NO_REASON
-#else
-#define UNWIND_STOP _URC_NORMAL_STOP
-#define UNWIND_CONTINUE _URC_NO_REASON
-#endif
-
-uptr Unwind_GetIP(struct _Unwind_Context *ctx) {
-#ifdef __arm__
- uptr val;
- _Unwind_VRS_Result res = _Unwind_VRS_Get(ctx, _UVRSC_CORE,
- 15 /* r15 = PC */, _UVRSD_UINT32, &val);
- CHECK(res == _UVRSR_OK && "_Unwind_VRS_Get failed");
- // Clear the Thumb bit.
- return val & ~(uptr)1;
-#else
- return _Unwind_GetIP(ctx);
+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());
+ }
}
-_Unwind_Reason_Code Unwind_Trace(struct _Unwind_Context *ctx,
- void *param) {
- AsanStackTrace *b = (AsanStackTrace*)param;
- CHECK(b->size < b->max_size);
- uptr pc = Unwind_GetIP(ctx);
- b->trace[b->size++] = pc;
- if (b->size == b->max_size) return UNWIND_STOP;
- return UNWIND_CONTINUE;
+#if !ASAN_ANDROID
+void ReadContextStack(void *context, uptr *stack, uptr *ssize) {
+ ucontext_t *ucp = (ucontext_t*)context;
+ *stack = (uptr)ucp->uc_stack.ss_sp;
+ *ssize = ucp->uc_stack.ss_size;
}
-
-void AsanStackTrace::GetStackTrace(uptr max_s, uptr pc, uptr bp) {
- size = 0;
- trace[0] = pc;
- if ((max_s) > 1) {
- max_size = max_s;
-#ifdef __arm__
- _Unwind_Backtrace(Unwind_Trace, this);
#else
- FastUnwindStack(pc, bp);
-#endif
- }
+void ReadContextStack(void *context, uptr *stack, uptr *ssize) {
+ UNIMPLEMENTED();
}
+#endif
} // namespace __asan
diff --git a/lib/asan/asan_lock.h b/lib/asan/asan_lock.h
index edee49adf6a7..e69de29bb2d1 100644
--- a/lib/asan/asan_lock.h
+++ b/lib/asan/asan_lock.h
@@ -1,42 +0,0 @@
-//===-- asan_lock.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.
-//
-// A wrapper for a simple lock.
-//===----------------------------------------------------------------------===//
-#ifndef ASAN_LOCK_H
-#define ASAN_LOCK_H
-
-#include "sanitizer_common/sanitizer_mutex.h"
-#include "asan_internal.h"
-
-// The locks in ASan are global objects and they are never destroyed to avoid
-// at-exit races (that is, a lock is being used by other threads while the main
-// thread is doing atexit destructors).
-// We define the class using opaque storage to avoid including system headers.
-
-namespace __asan {
-
-class AsanLock {
- public:
- explicit AsanLock(LinkerInitialized);
- void Lock();
- void Unlock();
- bool IsLocked() { return owner_ != 0; }
- private:
- uptr opaque_storage_[10];
- uptr owner_; // for debugging and for malloc_introspection_t interface
-};
-
-typedef GenericScopedLock<AsanLock> ScopedLock;
-
-} // namespace __asan
-
-#endif // ASAN_LOCK_H
diff --git a/lib/asan/asan_mac.cc b/lib/asan/asan_mac.cc
index a3d39e7ae05c..3ed9e06eeb6f 100644
--- a/lib/asan/asan_mac.cc
+++ b/lib/asan/asan_mac.cc
@@ -23,7 +23,8 @@
#include "asan_thread_registry.h"
#include "sanitizer_common/sanitizer_libc.h"
-#include <crt_externs.h> // for _NSGetEnviron
+#include <crt_externs.h> // for _NSGetArgv
+#include <dlfcn.h> // for dladdr()
#include <mach-o/dyld.h>
#include <mach-o/loader.h>
#include <sys/mman.h>
@@ -41,7 +42,7 @@ namespace __asan {
void GetPcSpBp(void *context, uptr *pc, uptr *sp, uptr *bp) {
ucontext_t *ucontext = (ucontext_t*)context;
-# if __WORDSIZE == 64
+# if SANITIZER_WORDSIZE == 64
*pc = ucontext->uc_mcontext->__ss.__rip;
*bp = ucontext->uc_mcontext->__ss.__rbp;
*sp = ucontext->uc_mcontext->__ss.__rsp;
@@ -49,7 +50,7 @@ void GetPcSpBp(void *context, uptr *pc, uptr *sp, uptr *bp) {
*pc = ucontext->uc_mcontext->__ss.__eip;
*bp = ucontext->uc_mcontext->__ss.__ebp;
*sp = ucontext->uc_mcontext->__ss.__esp;
-# endif // __WORDSIZE
+# endif // SANITIZER_WORDSIZE
}
int GetMacosVersion() {
@@ -67,6 +68,7 @@ int GetMacosVersion() {
switch (version[1]) {
case '0': return MACOS_VERSION_SNOW_LEOPARD;
case '1': return MACOS_VERSION_LION;
+ case '2': return MACOS_VERSION_MOUNTAIN_LION;
default: return MACOS_VERSION_UNKNOWN;
}
}
@@ -83,6 +85,42 @@ bool PlatformHasDifferentMemcpyAndMemmove() {
return GetMacosVersion() == MACOS_VERSION_SNOW_LEOPARD;
}
+extern "C"
+void __asan_init();
+
+static const char kDyldInsertLibraries[] = "DYLD_INSERT_LIBRARIES";
+
+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.
+ Dl_info info;
+ CHECK(dladdr((void*)((uptr)__asan_init), &info));
+ const char *dyld_insert_libraries = GetEnv(kDyldInsertLibraries);
+ if (!dyld_insert_libraries ||
+ !REAL(strstr)(dyld_insert_libraries, info.dli_fname)) {
+ // DYLD_INSERT_LIBRARIES is not set or does not contain the runtime
+ // library.
+ 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);
+ if (flags()->verbosity >= 1) {
+ Report("exec()-ing the program with\n");
+ Report("%s=%s\n", kDyldInsertLibraries, info.dli_fname);
+ Report("to enable ASan wrappers.\n");
+ Report("Set ASAN_OPTIONS=allow_reexec=0 to disable this.\n");
+ }
+ execv(program_name, *_NSGetArgv());
+ }
+#endif // MAC_INTERPOSE_FUNCTIONS
+ // If we're not using the dynamic runtime, do nothing.
+}
+
// No-op. Mac does not support static linkage anyway.
void *AsanDoesNotSupportStaticLinkage() {
return 0;
@@ -93,37 +131,32 @@ bool AsanInterceptsSignal(int signum) {
}
void AsanPlatformThreadInit() {
- ReplaceCFAllocator();
-}
-
-AsanLock::AsanLock(LinkerInitialized) {
- // We assume that OS_SPINLOCK_INIT is zero
-}
-
-void AsanLock::Lock() {
- CHECK(sizeof(OSSpinLock) <= sizeof(opaque_storage_));
- CHECK(OS_SPINLOCK_INIT == 0);
- CHECK(owner_ != (uptr)pthread_self());
- OSSpinLockLock((OSSpinLock*)&opaque_storage_);
- CHECK(!owner_);
- owner_ = (uptr)pthread_self();
-}
-
-void AsanLock::Unlock() {
- CHECK(owner_ == (uptr)pthread_self());
- owner_ = 0;
- OSSpinLockUnlock((OSSpinLock*)&opaque_storage_);
+ // 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 AsanStackTrace::GetStackTrace(uptr max_s, uptr pc, uptr bp) {
- size = 0;
- trace[0] = pc;
+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) {
- max_size = max_s;
- FastUnwindStack(pc, bp);
+ 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.
@@ -132,12 +165,12 @@ void AsanStackTrace::GetStackTrace(uptr max_s, uptr pc, uptr bp) {
// kHighMemBeg or kHighMemEnd.
static void *island_allocator_pos = 0;
-#if __WORDSIZE == 32
-# define kIslandEnd (0xffdf0000 - kPageSize)
-# define kIslandBeg (kIslandEnd - 256 * kPageSize)
+#if SANITIZER_WORDSIZE == 32
+# define kIslandEnd (0xffdf0000 - GetPageSizeCached())
+# define kIslandBeg (kIslandEnd - 256 * GetPageSizeCached())
#else
-# define kIslandEnd (0x7fffffdf0000 - kPageSize)
-# define kIslandBeg (kIslandEnd - 256 * kPageSize)
+# define kIslandEnd (0x7fffffdf0000 - GetPageSizeCached())
+# define kIslandBeg (kIslandEnd - 256 * GetPageSizeCached())
#endif
extern "C"
@@ -161,7 +194,7 @@ mach_error_t __interception_allocate_island(void **ptr,
internal_memset(island_allocator_pos, 0xCC, kIslandEnd - kIslandBeg);
};
*ptr = island_allocator_pos;
- island_allocator_pos = (char*)island_allocator_pos + kPageSize;
+ island_allocator_pos = (char*)island_allocator_pos + GetPageSizeCached();
if (flags()->verbosity) {
Report("Branch island allocated at %p\n", *ptr);
}
@@ -209,6 +242,7 @@ 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);
@@ -236,30 +270,34 @@ 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);
-int pthread_workqueue_additem_np(pthread_workqueue_t workq,
- void *(*workitem_func)(void *), void * workitem_arg,
- pthread_workitem_handle_t * itemhandlep, unsigned int *gencountp);
} // extern "C"
+static ALWAYS_INLINE
+void asan_register_worker_thread(int parent_tid, StackTrace *stack) {
+ AsanThread *t = asanThreadRegistry().GetCurrent();
+ if (!t) {
+ t = AsanThread::Create(parent_tid, 0, 0, stack);
+ asanThreadRegistry().RegisterThread(t);
+ t->Init();
+ asanThreadRegistry().SetCurrent(t);
+ }
+}
+
+// For use by only those functions that allocated the context via
+// alloc_asan_context().
extern "C"
void asan_dispatch_call_block_and_release(void *block) {
- GET_STACK_TRACE_HERE(kStackTraceMax);
+ GET_STACK_TRACE_THREAD;
asan_block_context_t *context = (asan_block_context_t*)block;
if (flags()->verbosity >= 2) {
Report("asan_dispatch_call_block_and_release(): "
"context: %p, pthread_self: %p\n",
block, pthread_self());
}
- AsanThread *t = asanThreadRegistry().GetCurrent();
- if (!t) {
- t = AsanThread::Create(context->parent_tid, 0, 0, &stack);
- asanThreadRegistry().RegisterThread(t);
- t->Init();
- asanThreadRegistry().SetCurrent(t);
- }
+ asan_register_worker_thread(context->parent_tid, &stack);
// Call the original dispatcher for the block.
context->func(context->block);
- asan_free(context, &stack);
+ asan_free(context, &stack, FROM_MALLOC);
}
} // namespace __asan
@@ -270,7 +308,7 @@ using namespace __asan; // NOLINT
// The caller retains control of the allocated context.
extern "C"
asan_block_context_t *alloc_asan_context(void *ctxt, dispatch_function_t func,
- AsanStackTrace *stack) {
+ StackTrace *stack) {
asan_block_context_t *asan_ctxt =
(asan_block_context_t*) asan_malloc(sizeof(asan_block_context_t), stack);
asan_ctxt->block = ctxt;
@@ -279,37 +317,30 @@ asan_block_context_t *alloc_asan_context(void *ctxt, dispatch_function_t func,
return asan_ctxt;
}
-// TODO(glider): can we reduce code duplication by introducing a macro?
-INTERCEPTOR(void, dispatch_async_f, dispatch_queue_t dq, void *ctxt,
- dispatch_function_t func) {
- GET_STACK_TRACE_HERE(kStackTraceMax);
- asan_block_context_t *asan_ctxt = alloc_asan_context(ctxt, func, &stack);
- if (flags()->verbosity >= 2) {
- Report("dispatch_async_f(): context: %p, pthread_self: %p\n",
- asan_ctxt, pthread_self());
- PRINT_CURRENT_STACK();
+// Define interceptor for dispatch_*_f function with the three most common
+// parameters: dispatch_queue_t, context, dispatch_function_t.
+#define INTERCEPT_DISPATCH_X_F_3(dispatch_x_f) \
+ INTERCEPTOR(void, dispatch_x_f, dispatch_queue_t dq, void *ctxt, \
+ dispatch_function_t func) { \
+ GET_STACK_TRACE_THREAD; \
+ asan_block_context_t *asan_ctxt = alloc_asan_context(ctxt, func, &stack); \
+ if (flags()->verbosity >= 2) { \
+ Report(#dispatch_x_f "(): context: %p, pthread_self: %p\n", \
+ asan_ctxt, pthread_self()); \
+ PRINT_CURRENT_STACK(); \
+ } \
+ return REAL(dispatch_x_f)(dq, (void*)asan_ctxt, \
+ asan_dispatch_call_block_and_release); \
}
- return REAL(dispatch_async_f)(dq, (void*)asan_ctxt,
- asan_dispatch_call_block_and_release);
-}
-INTERCEPTOR(void, dispatch_sync_f, dispatch_queue_t dq, void *ctxt,
- dispatch_function_t func) {
- GET_STACK_TRACE_HERE(kStackTraceMax);
- asan_block_context_t *asan_ctxt = alloc_asan_context(ctxt, func, &stack);
- if (flags()->verbosity >= 2) {
- Report("dispatch_sync_f(): context: %p, pthread_self: %p\n",
- asan_ctxt, pthread_self());
- PRINT_CURRENT_STACK();
- }
- return REAL(dispatch_sync_f)(dq, (void*)asan_ctxt,
- asan_dispatch_call_block_and_release);
-}
+INTERCEPT_DISPATCH_X_F_3(dispatch_async_f)
+INTERCEPT_DISPATCH_X_F_3(dispatch_sync_f)
+INTERCEPT_DISPATCH_X_F_3(dispatch_barrier_async_f)
INTERCEPTOR(void, dispatch_after_f, dispatch_time_t when,
dispatch_queue_t dq, void *ctxt,
dispatch_function_t func) {
- GET_STACK_TRACE_HERE(kStackTraceMax);
+ GET_STACK_TRACE_THREAD;
asan_block_context_t *asan_ctxt = alloc_asan_context(ctxt, func, &stack);
if (flags()->verbosity >= 2) {
Report("dispatch_after_f: %p\n", asan_ctxt);
@@ -319,23 +350,10 @@ INTERCEPTOR(void, dispatch_after_f, dispatch_time_t when,
asan_dispatch_call_block_and_release);
}
-INTERCEPTOR(void, dispatch_barrier_async_f, dispatch_queue_t dq, void *ctxt,
- dispatch_function_t func) {
- GET_STACK_TRACE_HERE(kStackTraceMax);
- asan_block_context_t *asan_ctxt = alloc_asan_context(ctxt, func, &stack);
- if (flags()->verbosity >= 2) {
- Report("dispatch_barrier_async_f(): context: %p, pthread_self: %p\n",
- asan_ctxt, pthread_self());
- PRINT_CURRENT_STACK();
- }
- REAL(dispatch_barrier_async_f)(dq, (void*)asan_ctxt,
- asan_dispatch_call_block_and_release);
-}
-
INTERCEPTOR(void, dispatch_group_async_f, dispatch_group_t group,
dispatch_queue_t dq, void *ctxt,
dispatch_function_t func) {
- GET_STACK_TRACE_HERE(kStackTraceMax);
+ GET_STACK_TRACE_THREAD;
asan_block_context_t *asan_ctxt = alloc_asan_context(ctxt, func, &stack);
if (flags()->verbosity >= 2) {
Report("dispatch_group_async_f(): context: %p, pthread_self: %p\n",
@@ -346,43 +364,66 @@ INTERCEPTOR(void, dispatch_group_async_f, dispatch_group_t group,
asan_dispatch_call_block_and_release);
}
-// The following stuff has been extremely helpful while looking for the
-// unhandled functions that spawned jobs on Chromium shutdown. If the verbosity
-// level is 2 or greater, we wrap pthread_workqueue_additem_np() in order to
-// find the points of worker thread creation (each of such threads may be used
-// to run several tasks, that's why this is not enough to support the whole
-// libdispatch API.
-extern "C"
-void *wrap_workitem_func(void *arg) {
- if (flags()->verbosity >= 2) {
- Report("wrap_workitem_func: %p, pthread_self: %p\n", arg, pthread_self());
- }
- asan_block_context_t *ctxt = (asan_block_context_t*)arg;
- worker_t fn = (worker_t)(ctxt->func);
- void *result = fn(ctxt->block);
- GET_STACK_TRACE_HERE(kStackTraceMax);
- asan_free(arg, &stack);
- return result;
+#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.
+
+extern "C" {
+// FIXME: consolidate these declarations with asan_intercepted_functions.h.
+void dispatch_async(dispatch_queue_t dq, void(^work)(void));
+void dispatch_group_async(dispatch_group_t dg, dispatch_queue_t dq,
+ void(^work)(void));
+void dispatch_after(dispatch_time_t when, dispatch_queue_t queue,
+ void(^work)(void));
+void dispatch_source_set_cancel_handler(dispatch_source_t ds,
+ void(^work)(void));
+void dispatch_source_set_event_handler(dispatch_source_t ds, void(^work)(void));
}
-INTERCEPTOR(int, pthread_workqueue_additem_np, pthread_workqueue_t workq,
- void *(*workitem_func)(void *), void * workitem_arg,
- pthread_workitem_handle_t * itemhandlep, unsigned int *gencountp) {
- GET_STACK_TRACE_HERE(kStackTraceMax);
- asan_block_context_t *asan_ctxt =
- (asan_block_context_t*) asan_malloc(sizeof(asan_block_context_t), &stack);
- asan_ctxt->block = workitem_arg;
- asan_ctxt->func = (dispatch_function_t)workitem_func;
- asan_ctxt->parent_tid = asanThreadRegistry().GetCurrentTidOrInvalid();
- if (flags()->verbosity >= 2) {
- Report("pthread_workqueue_additem_np: %p\n", asan_ctxt);
- PRINT_CURRENT_STACK();
+#define GET_ASAN_BLOCK(work) \
+ void (^asan_block)(void); \
+ int parent_tid = asanThreadRegistry().GetCurrentTidOrInvalid(); \
+ asan_block = ^(void) { \
+ GET_STACK_TRACE_THREAD; \
+ asan_register_worker_thread(parent_tid, &stack); \
+ work(); \
}
- return REAL(pthread_workqueue_additem_np)(workq, wrap_workitem_func,
- asan_ctxt, itemhandlep,
- gencountp);
+
+INTERCEPTOR(void, dispatch_async,
+ dispatch_queue_t dq, void(^work)(void)) {
+ GET_ASAN_BLOCK(work);
+ REAL(dispatch_async)(dq, asan_block);
+}
+
+INTERCEPTOR(void, dispatch_group_async,
+ dispatch_group_t dg, dispatch_queue_t dq, void(^work)(void)) {
+ GET_ASAN_BLOCK(work);
+ REAL(dispatch_group_async)(dg, dq, asan_block);
+}
+
+INTERCEPTOR(void, dispatch_after,
+ dispatch_time_t when, dispatch_queue_t queue, void(^work)(void)) {
+ GET_ASAN_BLOCK(work);
+ REAL(dispatch_after)(when, queue, asan_block);
}
+INTERCEPTOR(void, dispatch_source_set_cancel_handler,
+ dispatch_source_t ds, void(^work)(void)) {
+ GET_ASAN_BLOCK(work);
+ REAL(dispatch_source_set_cancel_handler)(ds, asan_block);
+}
+
+INTERCEPTOR(void, dispatch_source_set_event_handler,
+ dispatch_source_t ds, void(^work)(void)) {
+ GET_ASAN_BLOCK(work);
+ REAL(dispatch_source_set_event_handler)(ds, asan_block);
+}
+#endif
+
// See http://opensource.apple.com/source/CF/CF-635.15/CFString.c
int __CFStrIsConstant(CFStringRef str) {
CFRuntimeBase *base = (CFRuntimeBase*)str;
@@ -404,9 +445,7 @@ INTERCEPTOR(CFStringRef, CFStringCreateCopy, CFAllocatorRef alloc,
DECLARE_REAL_AND_INTERCEPTOR(void, free, void *ptr)
-extern "C"
-void __CFInitialize();
-DECLARE_REAL_AND_INTERCEPTOR(void, __CFInitialize)
+DECLARE_REAL_AND_INTERCEPTOR(void, __CFInitialize, void)
namespace __asan {
@@ -416,12 +455,6 @@ void InitializeMacInterceptors() {
CHECK(INTERCEPT_FUNCTION(dispatch_after_f));
CHECK(INTERCEPT_FUNCTION(dispatch_barrier_async_f));
CHECK(INTERCEPT_FUNCTION(dispatch_group_async_f));
- // We don't need to intercept pthread_workqueue_additem_np() to support the
- // libdispatch API, but it helps us to debug the unsupported functions. Let's
- // intercept it only during verbose runs.
- if (flags()->verbosity >= 2) {
- CHECK(INTERCEPT_FUNCTION(pthread_workqueue_additem_np));
- }
// 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
diff --git a/lib/asan/asan_mac.h b/lib/asan/asan_mac.h
index 6c65765a804c..be913865c440 100644
--- a/lib/asan/asan_mac.h
+++ b/lib/asan/asan_mac.h
@@ -40,13 +40,17 @@ enum {
MACOS_VERSION_UNKNOWN = 0,
MACOS_VERSION_LEOPARD,
MACOS_VERSION_SNOW_LEOPARD,
- MACOS_VERSION_LION
+ MACOS_VERSION_LION,
+ MACOS_VERSION_MOUNTAIN_LION
};
+// Used by asan_malloc_mac.cc and asan_mac.cc
+extern "C" void __CFInitialize();
+
namespace __asan {
int GetMacosVersion();
-void ReplaceCFAllocator();
+void MaybeReplaceCFAllocator();
} // namespace __asan
diff --git a/lib/asan/asan_malloc_linux.cc b/lib/asan/asan_malloc_linux.cc
index 1046f4c843a8..b95cfe3149b7 100644
--- a/lib/asan/asan_malloc_linux.cc
+++ b/lib/asan/asan_malloc_linux.cc
@@ -19,8 +19,16 @@
#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
+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)
+DECLARE_REAL_AND_INTERCEPTOR(void*, realloc, void *ptr, uptr size)
+DECLARE_REAL_AND_INTERCEPTOR(void*, memalign, uptr boundary, uptr size)
-#ifdef ANDROID
struct MallocDebug {
void* (*malloc)(uptr bytes);
void (*free)(void* mem);
@@ -30,7 +38,7 @@ struct MallocDebug {
};
const MallocDebug asan_malloc_dispatch ALIGNED(32) = {
- malloc, free, calloc, realloc, memalign
+ WRAP(malloc), WRAP(free), WRAP(calloc), WRAP(realloc), WRAP(memalign)
};
extern "C" const MallocDebug* __libc_malloc_dispatch;
@@ -53,17 +61,17 @@ void ReplaceSystemMalloc() {
using namespace __asan; // NOLINT
INTERCEPTOR(void, free, void *ptr) {
- GET_STACK_TRACE_HERE_FOR_FREE(ptr);
- asan_free(ptr, &stack);
+ GET_STACK_TRACE_FREE;
+ asan_free(ptr, &stack, FROM_MALLOC);
}
INTERCEPTOR(void, cfree, void *ptr) {
- GET_STACK_TRACE_HERE_FOR_FREE(ptr);
- asan_free(ptr, &stack);
+ GET_STACK_TRACE_FREE;
+ asan_free(ptr, &stack, FROM_MALLOC);
}
INTERCEPTOR(void*, malloc, uptr size) {
- GET_STACK_TRACE_HERE_FOR_MALLOC;
+ GET_STACK_TRACE_MALLOC;
return asan_malloc(size, &stack);
}
@@ -79,25 +87,25 @@ INTERCEPTOR(void*, calloc, uptr nmemb, uptr size) {
CHECK(allocated < kCallocPoolSize);
return mem;
}
- GET_STACK_TRACE_HERE_FOR_MALLOC;
+ GET_STACK_TRACE_MALLOC;
return asan_calloc(nmemb, size, &stack);
}
INTERCEPTOR(void*, realloc, void *ptr, uptr size) {
- GET_STACK_TRACE_HERE_FOR_MALLOC;
+ GET_STACK_TRACE_MALLOC;
return asan_realloc(ptr, size, &stack);
}
INTERCEPTOR(void*, memalign, uptr boundary, uptr size) {
- GET_STACK_TRACE_HERE_FOR_MALLOC;
- return asan_memalign(boundary, size, &stack);
+ GET_STACK_TRACE_MALLOC;
+ return asan_memalign(boundary, size, &stack, FROM_MALLOC);
}
INTERCEPTOR(void*, __libc_memalign, uptr align, uptr s)
ALIAS("memalign");
INTERCEPTOR(uptr, malloc_usable_size, void *ptr) {
- GET_STACK_TRACE_HERE_FOR_MALLOC;
+ GET_STACK_TRACE_MALLOC;
return asan_malloc_usable_size(ptr, &stack);
}
@@ -120,19 +128,23 @@ INTERCEPTOR(int, mallopt, int cmd, int value) {
}
INTERCEPTOR(int, posix_memalign, void **memptr, uptr alignment, uptr size) {
- GET_STACK_TRACE_HERE_FOR_MALLOC;
+ GET_STACK_TRACE_MALLOC;
// Printf("posix_memalign: %zx %zu\n", alignment, size);
return asan_posix_memalign(memptr, alignment, size, &stack);
}
INTERCEPTOR(void*, valloc, uptr size) {
- GET_STACK_TRACE_HERE_FOR_MALLOC;
+ GET_STACK_TRACE_MALLOC;
return asan_valloc(size, &stack);
}
INTERCEPTOR(void*, pvalloc, uptr size) {
- GET_STACK_TRACE_HERE_FOR_MALLOC;
+ GET_STACK_TRACE_MALLOC;
return asan_pvalloc(size, &stack);
}
+INTERCEPTOR(void, malloc_stats, void) {
+ __asan_print_accumulated_stats();
+}
+
#endif // __linux__
diff --git a/lib/asan/asan_malloc_mac.cc b/lib/asan/asan_malloc_mac.cc
index 1a6c84052a0a..545ede2debe7 100644
--- a/lib/asan/asan_malloc_mac.cc
+++ b/lib/asan/asan_malloc_mac.cc
@@ -1,4 +1,4 @@
-//===-- asan_rtl.cc -------------------------------------------------------===//
+//===-- asan_malloc_mac.cc ------------------------------------------------===//
//
// The LLVM Compiler Infrastructure
//
@@ -18,13 +18,15 @@
#include <CoreFoundation/CFBase.h>
#include <dlfcn.h>
#include <malloc/malloc.h>
-#include <setjmp.h>
#include "asan_allocator.h"
#include "asan_interceptors.h"
#include "asan_internal.h"
#include "asan_mac.h"
+#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.
@@ -38,6 +40,30 @@ 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;
+ }
+ }
+ return ptr;
+}
+
// 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
@@ -65,26 +91,12 @@ INTERCEPTOR(void, free, void *ptr) {
malloc_zone_free(zone, ptr);
#endif
} else {
- 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;
- }
- }
- GET_STACK_TRACE_HERE_FOR_FREE(ptr);
- asan_free(ptr, &stack);
+ if (!asan_mz_size(ptr)) ptr = get_saved_cfallocator_ref(ptr);
+ GET_STACK_TRACE_FREE;
+ asan_free(ptr, &stack, FROM_MALLOC);
}
}
-namespace __asan {
- void ReplaceCFAllocator();
-}
-
// 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
@@ -94,11 +106,15 @@ namespace __asan {
//
// 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) {
+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) ReplaceCFAllocator();
+ if (!cf_asan && asan_inited) MaybeReplaceCFAllocator();
}
namespace {
@@ -114,7 +130,7 @@ void *mz_malloc(malloc_zone_t *zone, size_t size) {
CHECK(system_malloc_zone);
return malloc_zone_malloc(system_malloc_zone, size);
}
- GET_STACK_TRACE_HERE_FOR_MALLOC;
+ GET_STACK_TRACE_MALLOC;
return asan_malloc(size, &stack);
}
@@ -123,7 +139,7 @@ void *cf_malloc(CFIndex size, CFOptionFlags hint, void *info) {
CHECK(system_malloc_zone);
return malloc_zone_malloc(system_malloc_zone, size);
}
- GET_STACK_TRACE_HERE_FOR_MALLOC;
+ GET_STACK_TRACE_MALLOC;
return asan_malloc(size, &stack);
}
@@ -139,7 +155,7 @@ void *mz_calloc(malloc_zone_t *zone, size_t nmemb, size_t size) {
CHECK(allocated < kCallocPoolSize);
return mem;
}
- GET_STACK_TRACE_HERE_FOR_MALLOC;
+ GET_STACK_TRACE_MALLOC;
return asan_calloc(nmemb, size, &stack);
}
@@ -148,39 +164,41 @@ void *mz_valloc(malloc_zone_t *zone, size_t size) {
CHECK(system_malloc_zone);
return malloc_zone_valloc(system_malloc_zone, size);
}
- GET_STACK_TRACE_HERE_FOR_MALLOC;
- return asan_memalign(kPageSize, size, &stack);
+ GET_STACK_TRACE_MALLOC;
+ return asan_memalign(GetPageSizeCached(), size, &stack, FROM_MALLOC);
}
-void print_zone_for_ptr(void *ptr) {
- malloc_zone_t *orig_zone = malloc_zone_from_ptr(ptr);
- if (orig_zone) {
- if (orig_zone->zone_name) {
- AsanPrintf("malloc_zone_from_ptr(%p) = %p, which is %s\n",
- ptr, orig_zone, orig_zone->zone_name);
- } else {
- AsanPrintf("malloc_zone_from_ptr(%p) = %p, which doesn't have a name\n",
- ptr, orig_zone);
- }
- } else {
- AsanPrintf("malloc_zone_from_ptr(%p) = 0\n", ptr);
- }
-}
+#define GET_ZONE_FOR_PTR(ptr) \
+ malloc_zone_t *zone_ptr = malloc_zone_from_ptr(ptr); \
+ const char *zone_name = (zone_ptr == 0) ? 0 : zone_ptr->zone_name
void ALWAYS_INLINE free_common(void *context, void *ptr) {
if (!ptr) return;
- if (!flags()->mac_ignore_invalid_free || asan_mz_size(ptr)) {
- GET_STACK_TRACE_HERE_FOR_FREE(ptr);
- asan_free(ptr, &stack);
+ if (asan_mz_size(ptr)) {
+ GET_STACK_TRACE_FREE;
+ asan_free(ptr, &stack, FROM_MALLOC);
} else {
- // Let us just leak this memory for now.
- AsanPrintf("free_common(%p) -- attempting to free unallocated memory.\n"
- "AddressSanitizer is ignoring this error on Mac OS now.\n",
- ptr);
- print_zone_for_ptr(ptr);
- GET_STACK_TRACE_HERE_FOR_FREE(ptr);
- stack.PrintStack();
- return;
+ // 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;
+ }
+ }
}
}
@@ -195,55 +213,45 @@ void cf_free(void *ptr, void *info) {
void *mz_realloc(malloc_zone_t *zone, void *ptr, size_t size) {
if (!ptr) {
- GET_STACK_TRACE_HERE_FOR_MALLOC;
+ GET_STACK_TRACE_MALLOC;
return asan_malloc(size, &stack);
} else {
if (asan_mz_size(ptr)) {
- GET_STACK_TRACE_HERE_FOR_MALLOC;
+ 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.
- AsanPrintf("mz_realloc(%p) -- attempting to realloc unallocated memory.\n"
- "This is an unrecoverable problem, exiting now.\n",
- ptr);
- print_zone_for_ptr(ptr);
- GET_STACK_TRACE_HERE_FOR_FREE(ptr);
- stack.PrintStack();
- ShowStatsAndAbort();
- return 0; // unreachable
+ GET_STACK_TRACE_FREE;
+ GET_ZONE_FOR_PTR(ptr);
+ ReportMacMzReallocUnknown((uptr)ptr, (uptr)zone_ptr, zone_name, &stack);
}
}
}
void *cf_realloc(void *ptr, CFIndex size, CFOptionFlags hint, void *info) {
if (!ptr) {
- GET_STACK_TRACE_HERE_FOR_MALLOC;
+ GET_STACK_TRACE_MALLOC;
return asan_malloc(size, &stack);
} else {
if (asan_mz_size(ptr)) {
- GET_STACK_TRACE_HERE_FOR_MALLOC;
+ 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.
- AsanPrintf("cf_realloc(%p) -- attempting to realloc unallocated memory.\n"
- "This is an unrecoverable problem, exiting now.\n",
- ptr);
- print_zone_for_ptr(ptr);
- GET_STACK_TRACE_HERE_FOR_FREE(ptr);
- stack.PrintStack();
- ShowStatsAndAbort();
- return 0; // unreachable
+ 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!
- AsanPrintf("mz_destroy() called -- ignoring\n");
+ Printf("mz_destroy() called -- ignoring\n");
}
// from AvailabilityMacros.h
#if defined(MAC_OS_X_VERSION_10_6) && \
@@ -253,8 +261,8 @@ void *mz_memalign(malloc_zone_t *zone, size_t align, size_t size) {
CHECK(system_malloc_zone);
return malloc_zone_memalign(system_malloc_zone, align, size);
}
- GET_STACK_TRACE_HERE_FOR_MALLOC;
- return asan_memalign(align, size, &stack);
+ GET_STACK_TRACE_MALLOC;
+ return asan_memalign(align, size, &stack, FROM_MALLOC);
}
// This function is currently unused, and we build with -Werror.
@@ -266,7 +274,6 @@ void mz_free_definite_size(malloc_zone_t* zone, void *ptr, size_t size) {
#endif
#endif
-// malloc_introspection callbacks. I'm not clear on what all of these do.
kern_return_t mi_enumerator(task_t task, void *,
unsigned type_mask, vm_address_t zone_address,
memory_reader_t reader,
@@ -282,12 +289,10 @@ size_t mi_good_size(malloc_zone_t *zone, size_t size) {
boolean_t mi_check(malloc_zone_t *zone) {
UNIMPLEMENTED();
- return true;
}
void mi_print(malloc_zone_t *zone, boolean_t verbose) {
UNIMPLEMENTED();
- return;
}
void mi_log(malloc_zone_t *zone, void *address) {
@@ -302,17 +307,12 @@ void mi_force_unlock(malloc_zone_t *zone) {
asan_mz_force_unlock();
}
-// This function is currently unused, and we build with -Werror.
-#if 0
void mi_statistics(malloc_zone_t *zone, malloc_statistics_t *stats) {
- // TODO(csilvers): figure out how to fill these out
- // TODO(glider): port this from tcmalloc when ready.
- stats->blocks_in_use = 0;
- stats->size_in_use = 0;
- stats->max_size_in_use = 0;
- stats->size_allocated = 0;
+ AsanMallocStats malloc_stats;
+ asanThreadRegistry().FillMallocStatistics(&malloc_stats);
+ CHECK(sizeof(malloc_statistics_t) == sizeof(AsanMallocStats));
+ internal_memcpy(stats, &malloc_stats, sizeof(malloc_statistics_t));
}
-#endif
#if defined(MAC_OS_X_VERSION_10_6) && \
MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_6
@@ -327,7 +327,7 @@ boolean_t mi_zone_locked(malloc_zone_t *zone) {
extern int __CFRuntimeClassTableSize;
namespace __asan {
-void ReplaceCFAllocator() {
+void MaybeReplaceCFAllocator() {
static CFAllocatorContext asan_context = {
/*version*/ 0, /*info*/ &asan_zone,
/*retain*/ 0, /*release*/ 0,
@@ -338,7 +338,7 @@ void ReplaceCFAllocator() {
/*preferredSize*/ 0 };
if (!cf_asan)
cf_asan = CFAllocatorCreate(kCFAllocatorUseContext, &asan_context);
- if (CFAllocatorGetDefault() != cf_asan)
+ if (flags()->replace_cfallocator && CFAllocatorGetDefault() != cf_asan)
CFAllocatorSetDefault(cf_asan);
}
@@ -354,6 +354,7 @@ void ReplaceSystemMalloc() {
asan_introspection.log = &mi_log;
asan_introspection.force_lock = &mi_force_lock;
asan_introspection.force_unlock = &mi_force_unlock;
+ asan_introspection.statistics = &mi_statistics;
internal_memset(&asan_zone, 0, sizeof(malloc_zone_t));
@@ -405,16 +406,14 @@ void ReplaceSystemMalloc() {
// Make sure the default allocator was replaced.
CHECK(malloc_default_zone() == &asan_zone);
- if (flags()->replace_cfallocator) {
- // 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) {
- ReplaceCFAllocator();
- }
+ // 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
diff --git a/lib/asan/asan_malloc_win.cc b/lib/asan/asan_malloc_win.cc
index 6c00e77caae8..9fcfea56384f 100644
--- a/lib/asan/asan_malloc_win.cc
+++ b/lib/asan/asan_malloc_win.cc
@@ -17,9 +17,10 @@
#include "asan_interceptors.h"
#include "asan_internal.h"
#include "asan_stack.h"
-
#include "interception/interception.h"
+#include <stddef.h>
+
// ---------------------- Replacement functions ---------------- {{{1
using namespace __asan; // NOLINT
@@ -30,8 +31,8 @@ using namespace __asan; // NOLINT
extern "C" {
void free(void *ptr) {
- GET_STACK_TRACE_HERE_FOR_FREE(ptr);
- return asan_free(ptr, &stack);
+ GET_STACK_TRACE_FREE;
+ return asan_free(ptr, &stack, FROM_MALLOC);
}
void _free_dbg(void* ptr, int) {
@@ -43,7 +44,7 @@ void cfree(void *ptr) {
}
void *malloc(size_t size) {
- GET_STACK_TRACE_HERE_FOR_MALLOC;
+ GET_STACK_TRACE_MALLOC;
return asan_malloc(size, &stack);
}
@@ -52,7 +53,7 @@ void* _malloc_dbg(size_t size, int , const char*, int) {
}
void *calloc(size_t nmemb, size_t size) {
- GET_STACK_TRACE_HERE_FOR_MALLOC;
+ GET_STACK_TRACE_MALLOC;
return asan_calloc(nmemb, size, &stack);
}
@@ -65,7 +66,7 @@ void *_calloc_impl(size_t nmemb, size_t size, int *errno_tmp) {
}
void *realloc(void *ptr, size_t size) {
- GET_STACK_TRACE_HERE_FOR_MALLOC;
+ GET_STACK_TRACE_MALLOC;
return asan_realloc(ptr, size, &stack);
}
@@ -84,7 +85,7 @@ void* _recalloc(void* p, size_t n, size_t elem_size) {
}
size_t _msize(void *ptr) {
- GET_STACK_TRACE_HERE_FOR_MALLOC;
+ GET_STACK_TRACE_MALLOC;
return asan_malloc_usable_size(ptr, &stack);
}
diff --git a/lib/asan/asan_mapping.h b/lib/asan/asan_mapping.h
index 8e0c6ec5db20..5e3067031f4a 100644
--- a/lib/asan/asan_mapping.h
+++ b/lib/asan/asan_mapping.h
@@ -20,20 +20,24 @@
// http://code.google.com/p/address-sanitizer/wiki/AddressSanitizerAlgorithm
#if ASAN_FLEXIBLE_MAPPING_AND_OFFSET == 1
-extern __attribute__((visibility("default"))) uptr __asan_mapping_scale;
-extern __attribute__((visibility("default"))) uptr __asan_mapping_offset;
+extern SANITIZER_INTERFACE_ATTRIBUTE uptr __asan_mapping_scale;
+extern SANITIZER_INTERFACE_ATTRIBUTE uptr __asan_mapping_offset;
# define SHADOW_SCALE (__asan_mapping_scale)
# define SHADOW_OFFSET (__asan_mapping_offset)
#else
-# ifdef ANDROID
+# if ASAN_ANDROID
# define SHADOW_SCALE (3)
# define SHADOW_OFFSET (0)
# else
# define SHADOW_SCALE (3)
-# if __WORDSIZE == 32
+# if SANITIZER_WORDSIZE == 32
# define SHADOW_OFFSET (1 << 29)
# else
-# define SHADOW_OFFSET (1ULL << 44)
+# if defined(__powerpc64__)
+# define SHADOW_OFFSET (1ULL << 41)
+# else
+# define SHADOW_OFFSET (1ULL << 44)
+# endif
# endif
# endif
#endif // ASAN_FLEXIBLE_MAPPING_AND_OFFSET
@@ -42,11 +46,15 @@ extern __attribute__((visibility("default"))) uptr __asan_mapping_offset;
#define MEM_TO_SHADOW(mem) (((mem) >> SHADOW_SCALE) | (SHADOW_OFFSET))
#define SHADOW_TO_MEM(shadow) (((shadow) - SHADOW_OFFSET) << SHADOW_SCALE)
-#if __WORDSIZE == 64
+#if SANITIZER_WORDSIZE == 64
+# if defined(__powerpc64__)
+ static const uptr kHighMemEnd = 0x00000fffffffffffUL;
+# else
static const uptr kHighMemEnd = 0x00007fffffffffffUL;
-#else // __WORDSIZE == 32
+# endif
+#else // SANITIZER_WORDSIZE == 32
static const uptr kHighMemEnd = 0xffffffff;
-#endif // __WORDSIZE
+#endif // SANITIZER_WORDSIZE
#define kLowMemBeg 0
@@ -60,7 +68,12 @@ extern __attribute__((visibility("default"))) uptr __asan_mapping_offset;
#define kHighShadowBeg MEM_TO_SHADOW(kHighMemBeg)
#define kHighShadowEnd MEM_TO_SHADOW(kHighMemEnd)
-#define kShadowGapBeg (kLowShadowEnd ? kLowShadowEnd + 1 : 16 * kPageSize)
+// 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 kGlobalAndStackRedzone \
diff --git a/lib/asan/asan_new_delete.cc b/lib/asan/asan_new_delete.cc
index 4a7275842f08..5d1f23c542e6 100644
--- a/lib/asan/asan_new_delete.cc
+++ b/lib/asan/asan_new_delete.cc
@@ -17,7 +17,6 @@
#include "asan_stack.h"
#include <stddef.h>
-#include <new>
namespace __asan {
// This function is a no-op. We need it to make sure that object file
@@ -28,29 +27,42 @@ void ReplaceOperatorsNewAndDelete() { }
using namespace __asan; // NOLINT
-#define OPERATOR_NEW_BODY \
- GET_STACK_TRACE_HERE_FOR_MALLOC;\
- return asan_memalign(0, size, &stack);
-
-#ifdef ANDROID
-void *operator new(size_t size) { OPERATOR_NEW_BODY; }
-void *operator new[](size_t size) { OPERATOR_NEW_BODY; }
-#else
-void *operator new(size_t size) throw(std::bad_alloc) { OPERATOR_NEW_BODY; }
-void *operator new[](size_t size) throw(std::bad_alloc) { OPERATOR_NEW_BODY; }
-void *operator new(size_t size, std::nothrow_t const&) throw()
-{ OPERATOR_NEW_BODY; }
-void *operator new[](size_t size, std::nothrow_t const&) throw()
-{ OPERATOR_NEW_BODY; }
-#endif
+// On Android new() goes through malloc interceptors.
+#if !ASAN_ANDROID
+
+// Fake std::nothrow_t to avoid including <new>.
+namespace std {
+struct nothrow_t {};
+} // namespace std
+
+#define OPERATOR_NEW_BODY(type) \
+ GET_STACK_TRACE_MALLOC;\
+ return asan_memalign(0, size, &stack, type);
-#define OPERATOR_DELETE_BODY \
- GET_STACK_TRACE_HERE_FOR_FREE(ptr);\
- asan_free(ptr, &stack);
+INTERCEPTOR_ATTRIBUTE
+void *operator new(size_t size) { OPERATOR_NEW_BODY(FROM_NEW); }
+INTERCEPTOR_ATTRIBUTE
+void *operator new[](size_t size) { OPERATOR_NEW_BODY(FROM_NEW_BR); }
+INTERCEPTOR_ATTRIBUTE
+void *operator new(size_t size, std::nothrow_t const&)
+{ OPERATOR_NEW_BODY(FROM_NEW); }
+INTERCEPTOR_ATTRIBUTE
+void *operator new[](size_t size, std::nothrow_t const&)
+{ OPERATOR_NEW_BODY(FROM_NEW_BR); }
-void operator delete(void *ptr) throw() { OPERATOR_DELETE_BODY; }
-void operator delete[](void *ptr) throw() { OPERATOR_DELETE_BODY; }
-void operator delete(void *ptr, std::nothrow_t const&) throw()
-{ OPERATOR_DELETE_BODY; }
-void operator delete[](void *ptr, std::nothrow_t const&) throw()
-{ OPERATOR_DELETE_BODY; }
+#define OPERATOR_DELETE_BODY(type) \
+ GET_STACK_TRACE_FREE;\
+ asan_free(ptr, &stack, type);
+
+INTERCEPTOR_ATTRIBUTE
+void operator delete(void *ptr) { OPERATOR_DELETE_BODY(FROM_NEW); }
+INTERCEPTOR_ATTRIBUTE
+void operator delete[](void *ptr) { OPERATOR_DELETE_BODY(FROM_NEW_BR); }
+INTERCEPTOR_ATTRIBUTE
+void operator delete(void *ptr, std::nothrow_t const&)
+{ OPERATOR_DELETE_BODY(FROM_NEW); }
+INTERCEPTOR_ATTRIBUTE
+void operator delete[](void *ptr, std::nothrow_t const&)
+{ OPERATOR_DELETE_BODY(FROM_NEW_BR); }
+
+#endif
diff --git a/lib/asan/asan_poisoning.cc b/lib/asan/asan_poisoning.cc
index 3b9d9f6795ca..dc5749243569 100644
--- a/lib/asan/asan_poisoning.cc
+++ b/lib/asan/asan_poisoning.cc
@@ -13,17 +13,19 @@
//===----------------------------------------------------------------------===//
#include "asan_interceptors.h"
-#include "asan_interface.h"
#include "asan_internal.h"
#include "asan_mapping.h"
+#include "sanitizer/asan_interface.h"
+#include "sanitizer_common/sanitizer_libc.h"
namespace __asan {
void PoisonShadow(uptr addr, uptr size, u8 value) {
+ if (!flags()->poison_heap) return;
CHECK(AddrIsAlignedByGranularity(addr));
CHECK(AddrIsAlignedByGranularity(addr + size));
uptr shadow_beg = MemToShadow(addr);
- uptr shadow_end = MemToShadow(addr + size);
+ uptr shadow_end = MemToShadow(addr + size - SHADOW_GRANULARITY) + 1;
CHECK(REAL(memset) != 0);
REAL(memset)((void*)shadow_beg, value, shadow_end - shadow_beg);
}
@@ -32,6 +34,7 @@ void PoisonShadowPartialRightRedzone(uptr addr,
uptr size,
uptr redzone_size,
u8 value) {
+ if (!flags()->poison_heap) return;
CHECK(AddrIsAlignedByGranularity(addr));
u8 *shadow = (u8*)MemToShadow(addr);
for (uptr i = 0; i < redzone_size;
@@ -151,3 +154,67 @@ void __asan_unpoison_memory_region(void const volatile *addr, uptr size) {
bool __asan_address_is_poisoned(void const volatile *addr) {
return __asan::AddressIsPoisoned((uptr)addr);
}
+
+uptr __asan_region_is_poisoned(uptr beg, uptr size) {
+ if (!size) return 0;
+ uptr end = beg + size;
+ if (!AddrIsInMem(beg)) return beg;
+ if (!AddrIsInMem(end)) return end;
+ uptr aligned_b = RoundUpTo(beg, SHADOW_GRANULARITY);
+ uptr aligned_e = RoundDownTo(end, SHADOW_GRANULARITY);
+ uptr shadow_beg = MemToShadow(aligned_b);
+ uptr shadow_end = MemToShadow(aligned_e);
+ // First check the first and the last application bytes,
+ // then check the SHADOW_GRANULARITY-aligned region by calling
+ // mem_is_zero on the corresponding shadow.
+ if (!__asan::AddressIsPoisoned(beg) &&
+ !__asan::AddressIsPoisoned(end - 1) &&
+ (shadow_end <= shadow_beg ||
+ __sanitizer::mem_is_zero((const char *)shadow_beg,
+ shadow_end - shadow_beg)))
+ return 0;
+ // The fast check failed, so we have a poisoned byte somewhere.
+ // Find it slowly.
+ for (; beg < end; beg++)
+ if (__asan::AddressIsPoisoned(beg))
+ return beg;
+ UNREACHABLE("mem_is_zero returned false, but poisoned byte was not found");
+ return 0;
+}
+
+// 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) {
+ if (size == 0) return;
+ uptr aligned_size = size & ~(SHADOW_GRANULARITY - 1);
+ PoisonShadow(addr, aligned_size,
+ do_poison ? kAsanStackUseAfterScopeMagic : 0);
+ if (size == aligned_size)
+ return;
+ s8 end_offset = (s8)(size - aligned_size);
+ s8* shadow_end = (s8*)MemToShadow(addr + aligned_size);
+ s8 end_value = *shadow_end;
+ if (do_poison) {
+ // If possible, mark all the bytes mapping to last shadow byte as
+ // unaddressable.
+ if (end_value > 0 && end_value <= end_offset)
+ *shadow_end = (s8)kAsanStackUseAfterScopeMagic;
+ } else {
+ // If necessary, mark few first bytes mapping to last shadow byte
+ // as addressable
+ if (end_value != 0)
+ *shadow_end = Max(end_value, end_offset);
+ }
+}
+
+void __asan_poison_stack_memory(uptr addr, uptr size) {
+ if (flags()->verbosity > 0)
+ Report("poisoning: %p %zx\n", (void*)addr, size);
+ PoisonAlignedStackMemory(addr, size, true);
+}
+
+void __asan_unpoison_stack_memory(uptr addr, uptr size) {
+ if (flags()->verbosity > 0)
+ Report("unpoisoning: %p %zx\n", (void*)addr, size);
+ PoisonAlignedStackMemory(addr, size, false);
+}
diff --git a/lib/asan/asan_posix.cc b/lib/asan/asan_posix.cc
index 061bb193034d..ceaf120fc803 100644
--- a/lib/asan/asan_posix.cc
+++ b/lib/asan/asan_posix.cc
@@ -16,6 +16,7 @@
#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"
@@ -53,14 +54,7 @@ static void ASAN_OnSIGSEGV(int, siginfo_t *siginfo, void *context) {
if (13 != internal_write(2, "ASAN:SIGSEGV\n", 13)) Die();
uptr pc, sp, bp;
GetPcSpBp(context, &pc, &sp, &bp);
- AsanReport("ERROR: AddressSanitizer crashed on unknown address %p"
- " (pc %p sp %p bp %p T%d)\n",
- (void*)addr, (void*)pc, (void*)sp, (void*)bp,
- asanThreadRegistry().GetCurrentTidOrInvalid());
- AsanPrintf("AddressSanitizer can not provide additional info. ABORTING\n");
- GET_STACK_TRACE_WITH_PC_AND_BP(kStackTraceMax, pc, bp);
- stack.PrintStack();
- ShowStatsAndAbort();
+ ReportSIGSEGV(pc, sp, bp, addr);
}
void SetAlternateSignalStack() {
diff --git a/lib/asan/asan_printf.cc b/lib/asan/asan_printf.cc
deleted file mode 100644
index e1304f0fbe3f..000000000000
--- a/lib/asan/asan_printf.cc
+++ /dev/null
@@ -1,59 +0,0 @@
-//===-- asan_printf.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.
-//
-// Internal printf function, used inside ASan run-time library.
-// We can't use libc printf because we intercept some of the functions used
-// inside it.
-//===----------------------------------------------------------------------===//
-
-#include "asan_internal.h"
-#include "asan_interceptors.h"
-#include "sanitizer_common/sanitizer_libc.h"
-#include "sanitizer_common/sanitizer_common.h"
-
-#include <stdarg.h>
-#include <stdio.h>
-
-namespace __sanitizer {
-int VSNPrintf(char *buff, int buff_length, const char *format, va_list args);
-} // namespace __sanitizer
-
-namespace __asan {
-
-void AsanPrintf(const char *format, ...) {
- const int kLen = 1024 * 4;
- char buffer[kLen];
- va_list args;
- va_start(args, format);
- int needed_length = VSNPrintf(buffer, kLen, format, args);
- va_end(args);
- RAW_CHECK_MSG(needed_length < kLen, "Buffer in Printf is too short!\n");
- RawWrite(buffer);
- AppendToErrorMessageBuffer(buffer);
-}
-
-// Like AsanPrintf, but prints the current PID before the output string.
-void AsanReport(const char *format, ...) {
- const int kLen = 1024 * 4;
- char buffer[kLen];
- int needed_length = internal_snprintf(buffer, kLen, "==%d== ", GetPid());
- RAW_CHECK_MSG(needed_length < kLen, "Buffer in Report is too short!\n");
- va_list args;
- va_start(args, format);
- needed_length += VSNPrintf(buffer + needed_length, kLen - needed_length,
- format, args);
- va_end(args);
- RAW_CHECK_MSG(needed_length < kLen, "Buffer in Report is too short!\n");
- RawWrite(buffer);
- AppendToErrorMessageBuffer(buffer);
-}
-
-} // namespace __asan
diff --git a/lib/asan/asan_report.cc b/lib/asan/asan_report.cc
new file mode 100644
index 000000000000..35ab9cabde67
--- /dev/null
+++ b/lib/asan/asan_report.cc
@@ -0,0 +1,681 @@
+//===-- asan_report.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.
+//
+// This file contains error reporting code.
+//===----------------------------------------------------------------------===//
+#include "asan_flags.h"
+#include "asan_internal.h"
+#include "asan_mapping.h"
+#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_report_decorator.h"
+#include "sanitizer_common/sanitizer_symbolizer.h"
+
+namespace __asan {
+
+// -------------------- User-specified callbacks ----------------- {{{1
+static void (*error_report_callback)(const char*);
+static char *error_message_buffer = 0;
+static uptr error_message_buffer_pos = 0;
+static uptr error_message_buffer_size = 0;
+
+void AppendToErrorMessageBuffer(const char *buffer) {
+ if (error_message_buffer) {
+ uptr length = internal_strlen(buffer);
+ CHECK_GE(error_message_buffer_size, error_message_buffer_pos);
+ uptr remaining = error_message_buffer_size - error_message_buffer_pos;
+ internal_strncpy(error_message_buffer + error_message_buffer_pos,
+ buffer, remaining);
+ error_message_buffer[error_message_buffer_size - 1] = '\0';
+ // FIXME: reallocate the buffer instead of truncating the message.
+ error_message_buffer_pos += remaining > length ? length : remaining;
+ }
+}
+
+// ---------------------- Decorator ------------------------------ {{{1
+bool PrintsToTtyCached() {
+ static int cached = 0;
+ static bool prints_to_tty;
+ if (!cached) { // Ok wrt threads since we are printing only from one thread.
+ prints_to_tty = PrintsToTty();
+ cached = 1;
+ }
+ return prints_to_tty;
+}
+class Decorator: private __sanitizer::AnsiColorDecorator {
+ public:
+ Decorator() : __sanitizer::AnsiColorDecorator(PrintsToTtyCached()) { }
+ const char *Warning() { return Red(); }
+ const char *EndWarning() { return Default(); }
+ const char *Access() { return Blue(); }
+ const char *EndAccess() { return Default(); }
+ const char *Location() { return Green(); }
+ const char *EndLocation() { return Default(); }
+ const char *Allocation() { return Magenta(); }
+ const char *EndAllocation() { return Default(); }
+
+ const char *ShadowByte(u8 byte) {
+ switch (byte) {
+ case kAsanHeapLeftRedzoneMagic:
+ case kAsanHeapRightRedzoneMagic:
+ return Red();
+ case kAsanHeapFreeMagic:
+ return Magenta();
+ case kAsanStackLeftRedzoneMagic:
+ case kAsanStackMidRedzoneMagic:
+ case kAsanStackRightRedzoneMagic:
+ case kAsanStackPartialRedzoneMagic:
+ return Red();
+ case kAsanStackAfterReturnMagic:
+ return Magenta();
+ case kAsanInitializationOrderMagic:
+ return Cyan();
+ case kAsanUserPoisonedMemoryMagic:
+ return Blue();
+ case kAsanStackUseAfterScopeMagic:
+ return Magenta();
+ case kAsanGlobalRedzoneMagic:
+ return Red();
+ case kAsanInternalHeapMagic:
+ return Yellow();
+ default:
+ return Default();
+ }
+ }
+ const char *EndShadowByte() { return Default(); }
+};
+
+// ---------------------- Helper functions ----------------------- {{{1
+
+static void PrintShadowByte(const char *before, u8 byte,
+ const char *after = "\n") {
+ Decorator d;
+ Printf("%s%s%x%x%s%s", before,
+ d.ShadowByte(byte), byte >> 4, byte & 15, d.EndShadowByte(), after);
+}
+
+static void PrintShadowBytes(const char *before, u8 *bytes,
+ u8 *guilty, uptr n) {
+ Decorator d;
+ if (before)
+ Printf("%s%p:", before, bytes);
+ for (uptr i = 0; i < n; i++) {
+ u8 *p = bytes + i;
+ const char *before = p == guilty ? "[" :
+ p - 1 == guilty ? "" : " ";
+ const char *after = p == guilty ? "]" : "";
+ PrintShadowByte(before, *p, after);
+ }
+ 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);
+ }
+ Printf("Shadow byte legend (one shadow byte represents %d "
+ "application bytes):\n", (int)SHADOW_GRANULARITY);
+ PrintShadowByte(" Addressable: ", 0);
+ Printf(" Partially addressable: ");
+ for (uptr i = 1; i < SHADOW_GRANULARITY; i++)
+ PrintShadowByte("", i, " ");
+ Printf("\n");
+ PrintShadowByte(" Heap left redzone: ", kAsanHeapLeftRedzoneMagic);
+ PrintShadowByte(" Heap righ redzone: ", kAsanHeapRightRedzoneMagic);
+ PrintShadowByte(" Freed Heap region: ", kAsanHeapFreeMagic);
+ PrintShadowByte(" Stack left redzone: ", kAsanStackLeftRedzoneMagic);
+ PrintShadowByte(" Stack mid redzone: ", kAsanStackMidRedzoneMagic);
+ PrintShadowByte(" Stack right redzone: ", kAsanStackRightRedzoneMagic);
+ PrintShadowByte(" Stack partial redzone: ", kAsanStackPartialRedzoneMagic);
+ PrintShadowByte(" Stack after return: ", kAsanStackAfterReturnMagic);
+ PrintShadowByte(" Stack use after scope: ", kAsanStackUseAfterScopeMagic);
+ PrintShadowByte(" Global redzone: ", kAsanGlobalRedzoneMagic);
+ PrintShadowByte(" Global init order: ", kAsanInitializationOrderMagic);
+ PrintShadowByte(" Poisoned by user: ", kAsanUserPoisonedMemoryMagic);
+ PrintShadowByte(" ASan internal: ", kAsanInternalHeapMagic);
+}
+
+static void PrintZoneForPointer(uptr ptr, uptr zone_ptr,
+ const char *zone_name) {
+ if (zone_ptr) {
+ if (zone_name) {
+ Printf("malloc_zone_from_ptr(%p) = %p, which is %s\n",
+ ptr, zone_ptr, zone_name);
+ } else {
+ Printf("malloc_zone_from_ptr(%p) = %p, which doesn't have a name\n",
+ ptr, zone_ptr);
+ }
+ } else {
+ Printf("malloc_zone_from_ptr(%p) = 0\n", ptr);
+ }
+}
+
+// ---------------------- Address Descriptions ------------------- {{{1
+
+static bool IsASCII(unsigned char c) {
+ return /*0x00 <= c &&*/ c <= 0x7F;
+}
+
+// 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;
+ }
+ if (*(char*)(g.beg + g.size - 1) != 0) return;
+ Printf(" '%s' is ascii string '%s'\n", g.name, (char*)g.beg);
+}
+
+bool DescribeAddressRelativeToGlobal(uptr addr, const __asan_global &g) {
+ if (addr < g.beg - kGlobalAndStackRedzone) 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));
+ } else {
+ Printf("%zd bytes inside", addr - g.beg); // Can it happen?
+ }
+ Printf(" of global variable '%s' (0x%zx) of size %zu\n",
+ g.name, g.beg, g.size);
+ Printf("%s", d.EndLocation());
+ PrintGlobalNameIfASCII(g);
+ return true;
+}
+
+bool DescribeAddressIfShadow(uptr addr) {
+ if (AddrIsInMem(addr))
+ return false;
+ static const char kAddrInShadowReport[] =
+ "Address %p is located in the %s.\n";
+ if (AddrIsInShadowGap(addr)) {
+ Printf(kAddrInShadowReport, addr, "shadow gap area");
+ return true;
+ }
+ if (AddrIsInHighShadow(addr)) {
+ Printf(kAddrInShadowReport, addr, "high shadow area");
+ return true;
+ }
+ if (AddrIsInLowShadow(addr)) {
+ Printf(kAddrInShadowReport, addr, "low shadow area");
+ return true;
+ }
+ CHECK(0 && "Address is not in memory and not in shadow?");
+ return false;
+}
+
+bool DescribeAddressIfStack(uptr addr, uptr access_size) {
+ AsanThread *t = asanThreadRegistry().FindThreadByStackAddress(addr);
+ if (!t) return false;
+ const sptr kBufSize = 4095;
+ char buf[kBufSize];
+ uptr offset = 0;
+ const char *frame_descr = t->GetFrameNameByAddr(addr, &offset);
+ // This string is created by the compiler and has the following form:
+ // "FunctioName n alloc_1 alloc_2 ... alloc_n"
+ // where alloc_i looks like "offset size len ObjectName ".
+ CHECK(frame_descr);
+ // Report the function name and the offset.
+ const char *name_end = internal_strchr(frame_descr, ' ');
+ CHECK(name_end);
+ buf[0] = 0;
+ internal_strncat(buf, frame_descr,
+ Min(kBufSize,
+ static_cast<sptr>(name_end - frame_descr)));
+ 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("%s", d.EndLocation());
+ // Report the number of stack objects.
+ char *p;
+ uptr n_objects = internal_simple_strtoll(name_end, &p, 10);
+ CHECK(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++) {
+ uptr beg, size;
+ sptr len;
+ beg = internal_simple_strtoll(p, &p, 10);
+ size = internal_simple_strtoll(p, &p, 10);
+ len = internal_simple_strtoll(p, &p, 10);
+ if (beg <= 0 || size <= 0 || len < 0 || *p != ' ') {
+ Printf("AddressSanitizer can't parse the stack frame "
+ "descriptor: |%s|\n", frame_descr);
+ break;
+ }
+ p++;
+ buf[0] = 0;
+ internal_strncat(buf, p, Min(kBufSize, len));
+ p += len;
+ Printf(" [%zu, %zu) '%s'\n", beg, beg + size, buf);
+ }
+ 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());
+ return true;
+}
+
+static void DescribeAccessToHeapChunk(AsanChunkView chunk, uptr addr,
+ uptr access_size) {
+ uptr 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);
+ } else if (chunk.AddrIsAtRight(addr, access_size, &offset)) {
+ Printf("%zu bytes to the right of", offset);
+ } else {
+ Printf(" somewhere around (this is AddressSanitizer bug!)");
+ }
+ 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());
+ StackTrace alloc_stack;
+ chunk.GetAllocStack(&alloc_stack);
+ AsanThread *t = asanThreadRegistry().GetCurrent();
+ CHECK(t);
+ char tname[128];
+ Decorator d;
+ if (chunk.FreeTid() != kInvalidTid) {
+ AsanThreadSummary *free_thread =
+ asanThreadRegistry().FindByTid(chunk.FreeTid());
+ Printf("%sfreed by thread T%d%s here:%s\n", d.Allocation(),
+ 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(),
+ ThreadNameWithParenthesis(alloc_thread, tname, sizeof(tname)),
+ d.EndAllocation());
+ PrintStack(&alloc_stack);
+ DescribeThread(t->summary());
+ DescribeThread(free_thread);
+ DescribeThread(alloc_thread);
+ } else {
+ Printf("%sallocated by thread T%d%s here:%s\n", d.Allocation(),
+ alloc_thread->tid(),
+ ThreadNameWithParenthesis(alloc_thread, tname, sizeof(tname)),
+ d.EndAllocation());
+ PrintStack(&alloc_stack);
+ DescribeThread(t->summary());
+ DescribeThread(alloc_thread);
+ }
+}
+
+void DescribeAddress(uptr addr, uptr access_size) {
+ // Check if this is shadow or shadow gap.
+ if (DescribeAddressIfShadow(addr))
+ return;
+ CHECK(AddrIsInMem(addr));
+ if (DescribeAddressIfGlobal(addr))
+ return;
+ if (DescribeAddressIfStack(addr, access_size))
+ return;
+ // Assume it is a heap address.
+ DescribeHeapAddress(addr, access_size);
+}
+
+// ------------------- Thread description -------------------- {{{1
+
+void DescribeThread(AsanThreadSummary *summary) {
+ CHECK(summary);
+ // No need to announce the main thread.
+ if (summary->tid() == 0 || summary->announced()) {
+ return;
+ }
+ summary->set_announced(true);
+ char tname[128];
+ Printf("Thread T%d%s", summary->tid(),
+ ThreadNameWithParenthesis(summary->tid(), tname, sizeof(tname)));
+ Printf(" created by T%d%s here:\n",
+ summary->parent_tid(),
+ ThreadNameWithParenthesis(summary->parent_tid(),
+ tname, sizeof(tname)));
+ PrintStack(summary->stack());
+ // Recursively described parent thread if needed.
+ if (flags()->print_full_thread_history) {
+ AsanThreadSummary *parent_summary =
+ asanThreadRegistry().FindByTid(summary->parent_tid());
+ DescribeThread(parent_summary);
+ }
+}
+
+// -------------------- Different kinds of reports ----------------- {{{1
+
+// Use ScopedInErrorReport to run common actions just before and
+// immediately after printing error report.
+class ScopedInErrorReport {
+ public:
+ ScopedInErrorReport() {
+ static atomic_uint32_t num_calls;
+ static u32 reporting_thread_tid;
+ if (atomic_fetch_add(&num_calls, 1, memory_order_relaxed) != 0) {
+ // Do not print more than one report, otherwise they will mix up.
+ // Error reporting functions shouldn't return at this situation, as
+ // they are defined as no-return.
+ Report("AddressSanitizer: while reporting a bug found another one."
+ "Ignoring.\n");
+ u32 current_tid = asanThreadRegistry().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
+ // Die() to bypass any additional checks.
+ Exit(flags()->exitcode);
+ }
+ ASAN_ON_ERROR();
+ reporting_thread_tid = asanThreadRegistry().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();
+ CHECK(curr_thread);
+ curr_thread->fake_stack().StopUsingFakeStack();
+ }
+ }
+ // Destructor is NORETURN, as functions that report errors are.
+ NORETURN ~ScopedInErrorReport() {
+ // Make sure the current thread is announced.
+ AsanThread *curr_thread = asanThreadRegistry().GetCurrent();
+ if (curr_thread) {
+ DescribeThread(curr_thread->summary());
+ }
+ // Print memory stats.
+ __asan_print_accumulated_stats();
+ if (error_report_callback) {
+ error_report_callback(error_message_buffer);
+ }
+ Report("ABORTING\n");
+ Die();
+ }
+};
+
+void ReportSIGSEGV(uptr pc, uptr sp, uptr bp, uptr addr) {
+ ScopedInErrorReport in_report;
+ Decorator d;
+ Printf("%s", d.Warning());
+ 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());
+ Printf("%s", d.EndWarning());
+ Printf("AddressSanitizer can not provide additional info.\n");
+ GET_STACK_TRACE_FATAL(pc, bp);
+ PrintStack(&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);
+ Printf("%s", d.EndWarning());
+ PrintStack(stack);
+ DescribeHeapAddress(addr, 1);
+}
+
+void ReportFreeNotMalloced(uptr addr, StackTrace *stack) {
+ ScopedInErrorReport in_report;
+ Decorator d;
+ Printf("%s", d.Warning());
+ Report("ERROR: AddressSanitizer: attempting free on address "
+ "which was not malloc()-ed: %p\n", addr);
+ Printf("%s", d.EndWarning());
+ PrintStack(stack);
+ DescribeHeapAddress(addr, 1);
+}
+
+void ReportAllocTypeMismatch(uptr addr, StackTrace *stack,
+ AllocType alloc_type,
+ AllocType dealloc_type) {
+ static const char *alloc_names[] =
+ {"INVALID", "malloc", "operator new", "operator new []"};
+ static const char *dealloc_names[] =
+ {"INVALID", "free", "operator delete", "operator delete []"};
+ CHECK_NE(alloc_type, dealloc_type);
+ ScopedInErrorReport in_report;
+ Decorator d;
+ Printf("%s", d.Warning());
+ Report("ERROR: AddressSanitizer: alloc-dealloc-mismatch (%s vs %s) on %p\n",
+ alloc_names[alloc_type], dealloc_names[dealloc_type], addr);
+ Printf("%s", d.EndWarning());
+ PrintStack(stack);
+ DescribeHeapAddress(addr, 1);
+ Report("HINT: if you don't care about these warnings you may set "
+ "ASAN_OPTIONS=alloc_dealloc_mismatch=0\n");
+}
+
+void ReportMallocUsableSizeNotOwned(uptr addr, StackTrace *stack) {
+ ScopedInErrorReport in_report;
+ Decorator d;
+ Printf("%s", d.Warning());
+ Report("ERROR: AddressSanitizer: attempting to call "
+ "malloc_usable_size() for pointer which is "
+ "not owned: %p\n", addr);
+ Printf("%s", d.EndWarning());
+ PrintStack(stack);
+ DescribeHeapAddress(addr, 1);
+}
+
+void ReportAsanGetAllocatedSizeNotOwned(uptr addr, StackTrace *stack) {
+ ScopedInErrorReport in_report;
+ Decorator d;
+ Printf("%s", d.Warning());
+ Report("ERROR: AddressSanitizer: attempting to call "
+ "__asan_get_allocated_size() for pointer which is "
+ "not owned: %p\n", addr);
+ Printf("%s", d.EndWarning());
+ PrintStack(stack);
+ DescribeHeapAddress(addr, 1);
+}
+
+void ReportStringFunctionMemoryRangesOverlap(
+ const char *function, const char *offset1, uptr length1,
+ const char *offset2, uptr length2, StackTrace *stack) {
+ ScopedInErrorReport in_report;
+ Decorator d;
+ Printf("%s", d.Warning());
+ Report("ERROR: AddressSanitizer: %s-param-overlap: "
+ "memory ranges [%p,%p) and [%p, %p) overlap\n", \
+ function, offset1, offset1 + length1, offset2, offset2 + length2);
+ Printf("%s", d.EndWarning());
+ PrintStack(stack);
+ DescribeAddress((uptr)offset1, length1);
+ DescribeAddress((uptr)offset2, length2);
+}
+
+// ----------------------- Mac-specific reports ----------------- {{{1
+
+void WarnMacFreeUnallocated(
+ uptr addr, uptr zone_ptr, const char *zone_name, StackTrace *stack) {
+ // Just print a warning here.
+ Printf("free_common(%p) -- attempting to free unallocated memory.\n"
+ "AddressSanitizer is ignoring this error on Mac OS now.\n",
+ addr);
+ PrintZoneForPointer(addr, zone_ptr, zone_name);
+ PrintStack(stack);
+ DescribeHeapAddress(addr, 1);
+}
+
+void ReportMacMzReallocUnknown(
+ uptr addr, uptr zone_ptr, const char *zone_name, StackTrace *stack) {
+ ScopedInErrorReport in_report;
+ Printf("mz_realloc(%p) -- attempting to realloc unallocated memory.\n"
+ "This is an unrecoverable problem, exiting now.\n",
+ addr);
+ PrintZoneForPointer(addr, zone_ptr, zone_name);
+ PrintStack(stack);
+ DescribeHeapAddress(addr, 1);
+}
+
+void ReportMacCfReallocUnknown(
+ uptr addr, uptr zone_ptr, const char *zone_name, StackTrace *stack) {
+ ScopedInErrorReport in_report;
+ Printf("cf_realloc(%p) -- attempting to realloc unallocated memory.\n"
+ "This is an unrecoverable problem, exiting now.\n",
+ addr);
+ PrintZoneForPointer(addr, zone_ptr, zone_name);
+ PrintStack(stack);
+ DescribeHeapAddress(addr, 1);
+}
+
+} // namespace __asan
+
+// --------------------------- Interface --------------------- {{{1
+using namespace __asan; // NOLINT
+
+void __asan_report_error(uptr pc, uptr bp, uptr sp,
+ uptr addr, bool is_write, uptr access_size) {
+ ScopedInErrorReport in_report;
+
+ // Determine the error type.
+ const char *bug_descr = "unknown-crash";
+ if (AddrIsInMem(addr)) {
+ u8 *shadow_addr = (u8*)MemToShadow(addr);
+ // If we are accessing 16 bytes, look at the second shadow byte.
+ if (*shadow_addr == 0 && access_size > SHADOW_GRANULARITY)
+ shadow_addr++;
+ // If we are in the partial right redzone, look at the next shadow byte.
+ if (*shadow_addr > 0 && *shadow_addr < 128)
+ shadow_addr++;
+ switch (*shadow_addr) {
+ case kAsanHeapLeftRedzoneMagic:
+ case kAsanHeapRightRedzoneMagic:
+ bug_descr = "heap-buffer-overflow";
+ break;
+ case kAsanHeapFreeMagic:
+ bug_descr = "heap-use-after-free";
+ break;
+ case kAsanStackLeftRedzoneMagic:
+ bug_descr = "stack-buffer-underflow";
+ break;
+ case kAsanInitializationOrderMagic:
+ bug_descr = "initialization-order-fiasco";
+ break;
+ case kAsanStackMidRedzoneMagic:
+ case kAsanStackRightRedzoneMagic:
+ case kAsanStackPartialRedzoneMagic:
+ bug_descr = "stack-buffer-overflow";
+ break;
+ case kAsanStackAfterReturnMagic:
+ bug_descr = "stack-use-after-return";
+ break;
+ case kAsanUserPoisonedMemoryMagic:
+ bug_descr = "use-after-poison";
+ break;
+ case kAsanStackUseAfterScopeMagic:
+ bug_descr = "stack-use-after-scope";
+ break;
+ case kAsanGlobalRedzoneMagic:
+ bug_descr = "global-buffer-overflow";
+ break;
+ }
+ }
+ Decorator d;
+ Printf("%s", d.Warning());
+ Report("ERROR: AddressSanitizer: %s on address "
+ "%p at pc 0x%zx bp 0x%zx sp 0x%zx\n",
+ bug_descr, (void*)addr, pc, bp, sp);
+ Printf("%s", d.EndWarning());
+
+ u32 curr_tid = asanThreadRegistry().GetCurrentTidOrInvalid();
+ char tname[128];
+ Printf("%s%s of size %zu at %p thread T%d%s%s\n",
+ d.Access(),
+ access_size ? (is_write ? "WRITE" : "READ") : "ACCESS",
+ access_size, (void*)addr, curr_tid,
+ ThreadNameWithParenthesis(curr_tid, tname, sizeof(tname)),
+ d.EndAccess());
+
+ GET_STACK_TRACE_FATAL(pc, bp);
+ PrintStack(&stack);
+
+ DescribeAddress(addr, access_size);
+
+ PrintShadowMemoryForAddress(addr);
+}
+
+void NOINLINE __asan_set_error_report_callback(void (*callback)(const char*)) {
+ error_report_callback = callback;
+ if (callback) {
+ error_message_buffer_size = 1 << 16;
+ error_message_buffer =
+ (char*)MmapOrDie(error_message_buffer_size, __FUNCTION__);
+ error_message_buffer_pos = 0;
+ }
+}
+
+void __asan_describe_address(uptr addr) {
+ DescribeAddress(addr, 1);
+}
+
+#if !SANITIZER_SUPPORTS_WEAK_HOOKS
+// Provide default implementation of __asan_on_error that does nothing
+// and may be overriden by user.
+SANITIZER_WEAK_ATTRIBUTE SANITIZER_INTERFACE_ATTRIBUTE NOINLINE
+void __asan_on_error() {}
+#endif
diff --git a/lib/asan/asan_report.h b/lib/asan/asan_report.h
new file mode 100644
index 000000000000..f0617f91970e
--- /dev/null
+++ b/lib/asan/asan_report.h
@@ -0,0 +1,57 @@
+//===-- asan_report.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 error reporting functions.
+//===----------------------------------------------------------------------===//
+
+#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 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);
+
+// Different kinds of error reports.
+void NORETURN ReportSIGSEGV(uptr pc, uptr sp, uptr bp, uptr addr);
+void NORETURN ReportDoubleFree(uptr addr, StackTrace *stack);
+void NORETURN ReportFreeNotMalloced(uptr addr, StackTrace *stack);
+void NORETURN ReportAllocTypeMismatch(uptr addr, StackTrace *stack,
+ AllocType alloc_type,
+ AllocType dealloc_type);
+void NORETURN ReportMallocUsableSizeNotOwned(uptr addr,
+ StackTrace *stack);
+void NORETURN ReportAsanGetAllocatedSizeNotOwned(uptr addr,
+ StackTrace *stack);
+void NORETURN ReportStringFunctionMemoryRangesOverlap(
+ const char *function, const char *offset1, uptr length1,
+ const char *offset2, uptr length2, StackTrace *stack);
+
+// Mac-specific errors and warnings.
+void WarnMacFreeUnallocated(
+ uptr addr, uptr zone_ptr, const char *zone_name, StackTrace *stack);
+void NORETURN ReportMacMzReallocUnknown(
+ uptr addr, uptr zone_ptr, const char *zone_name, StackTrace *stack);
+void NORETURN ReportMacCfReallocUnknown(
+ uptr addr, uptr zone_ptr, const char *zone_name, StackTrace *stack);
+
+} // namespace __asan
diff --git a/lib/asan/asan_rtl.cc b/lib/asan/asan_rtl.cc
index 34324fa16d08..11adbee5bdea 100644
--- a/lib/asan/asan_rtl.cc
+++ b/lib/asan/asan_rtl.cc
@@ -13,29 +13,29 @@
//===----------------------------------------------------------------------===//
#include "asan_allocator.h"
#include "asan_interceptors.h"
-#include "asan_interface.h"
#include "asan_internal.h"
-#include "asan_lock.h"
#include "asan_mapping.h"
+#include "asan_report.h"
#include "asan_stack.h"
#include "asan_stats.h"
#include "asan_thread.h"
#include "asan_thread_registry.h"
+#include "sanitizer/asan_interface.h"
#include "sanitizer_common/sanitizer_atomic.h"
#include "sanitizer_common/sanitizer_flags.h"
#include "sanitizer_common/sanitizer_libc.h"
+#include "sanitizer_common/sanitizer_symbolizer.h"
-namespace __sanitizer {
-using namespace __asan;
+namespace __asan {
-void Die() {
+static void AsanDie() {
static atomic_uint32_t num_calls;
if (atomic_fetch_add(&num_calls, 1, memory_order_relaxed) != 0) {
// Don't die twice - run a busy loop.
while (1) { }
}
if (flags()->sleep_before_dying) {
- Report("Sleeping for %zd second(s)\n", flags()->sleep_before_dying);
+ Report("Sleeping for %d second(s)\n", flags()->sleep_before_dying);
SleepForSeconds(flags()->sleep_before_dying);
}
if (flags()->unmap_shadow_on_exit)
@@ -47,19 +47,17 @@ void Die() {
Exit(flags()->exitcode);
}
-void CheckFailed(const char *file, int line, const char *cond, u64 v1, u64 v2) {
- AsanReport("AddressSanitizer CHECK failed: %s:%d \"%s\" (0x%zx, 0x%zx)\n",
+static void AsanCheckFailed(const char *file, int line, const char *cond,
+ u64 v1, u64 v2) {
+ Report("AddressSanitizer CHECK failed: %s:%d \"%s\" (0x%zx, 0x%zx)\n",
file, line, cond, (uptr)v1, (uptr)v2);
+ // FIXME: check for infinite recursion without a thread-local counter here.
PRINT_CURRENT_STACK();
- ShowStatsAndAbort();
+ Die();
}
-} // namespace __sanitizer
-
-namespace __asan {
-
// -------------------------- Flags ------------------------- {{{1
-static const int kMallocContextSize = 30;
+static const int kDeafultMallocContextSize = 30;
static Flags asan_flags;
@@ -67,6 +65,10 @@ Flags *flags() {
return &asan_flags;
}
+static const char *MaybeCallAsanDefaultOptions() {
+ return (&__asan_default_options) ? __asan_default_options() : "";
+}
+
static void ParseFlagsFromString(Flags *f, const char *str) {
ParseFlag(str, &f->quarantine_size, "quarantine_size");
ParseFlag(str, &f->symbolize, "symbolize");
@@ -77,8 +79,9 @@ static void ParseFlagsFromString(Flags *f, const char *str) {
ParseFlag(str, &f->debug, "debug");
ParseFlag(str, &f->report_globals, "report_globals");
+ ParseFlag(str, &f->check_initialization_order, "initialization_order");
ParseFlag(str, &f->malloc_context_size, "malloc_context_size");
- CHECK(f->malloc_context_size <= kMallocContextSize);
+ CHECK((uptr)f->malloc_context_size <= kStackTraceMax);
ParseFlag(str, &f->replace_str, "replace_str");
ParseFlag(str, &f->replace_intrin, "replace_intrin");
@@ -96,22 +99,28 @@ static void ParseFlagsFromString(Flags *f, const char *str) {
ParseFlag(str, &f->abort_on_error, "abort_on_error");
ParseFlag(str, &f->atexit, "atexit");
ParseFlag(str, &f->disable_core, "disable_core");
+ ParseFlag(str, &f->strip_path_prefix, "strip_path_prefix");
+ ParseFlag(str, &f->allow_reexec, "allow_reexec");
+ ParseFlag(str, &f->print_full_thread_history, "print_full_thread_history");
+ ParseFlag(str, &f->log_path, "log_path");
+ ParseFlag(str, &f->fast_unwind_on_fatal, "fast_unwind_on_fatal");
+ ParseFlag(str, &f->fast_unwind_on_malloc, "fast_unwind_on_malloc");
+ ParseFlag(str, &f->poison_heap, "poison_heap");
+ ParseFlag(str, &f->alloc_dealloc_mismatch, "alloc_dealloc_mismatch");
+ ParseFlag(str, &f->use_stack_depot, "use_stack_depot");
}
-extern "C" {
-const char* WEAK __asan_default_options() { return ""; }
-} // extern "C"
-
void InitializeFlags(Flags *f, const char *env) {
internal_memset(f, 0, sizeof(*f));
- f->quarantine_size = (ASAN_LOW_MEMORY) ? 1UL << 24 : 1UL << 28;
+ f->quarantine_size = (ASAN_LOW_MEMORY) ? 1UL << 26 : 1UL << 28;
f->symbolize = false;
f->verbosity = 0;
- f->redzone = (ASAN_LOW_MEMORY) ? 64 : 128;
+ f->redzone = ASAN_ALLOCATOR_VERSION == 2 ? 16 : (ASAN_LOW_MEMORY) ? 64 : 128;
f->debug = false;
f->report_globals = 1;
- f->malloc_context_size = kMallocContextSize;
+ f->check_initialization_order = true;
+ f->malloc_context_size = kDeafultMallocContextSize;
f->replace_str = true;
f->replace_intrin = true;
f->replace_cfallocator = true;
@@ -127,13 +136,24 @@ void InitializeFlags(Flags *f, const char *env) {
f->unmap_shadow_on_exit = false;
f->abort_on_error = false;
f->atexit = false;
- f->disable_core = (__WORDSIZE == 64);
+ f->disable_core = (SANITIZER_WORDSIZE == 64);
+ f->strip_path_prefix = "";
+ f->allow_reexec = true;
+ f->print_full_thread_history = true;
+ f->log_path = 0;
+ f->fast_unwind_on_fatal = false;
+ f->fast_unwind_on_malloc = true;
+ f->poison_heap = true;
+ // Turn off alloc/dealloc mismatch checker on Mac for now.
+ // TODO(glider): Fix known issues and enable this back.
+ f->alloc_dealloc_mismatch = (ASAN_MAC == 0);
+ f->use_stack_depot = true; // Only affects allocator2.
// Override from user-specified string.
- ParseFlagsFromString(f, __asan_default_options());
+ ParseFlagsFromString(f, MaybeCallAsanDefaultOptions());
if (flags()->verbosity) {
Report("Using the defaults from __asan_default_options: %s\n",
- __asan_default_options());
+ MaybeCallAsanDefaultOptions());
}
// Override from command line.
@@ -144,10 +164,6 @@ void InitializeFlags(Flags *f, const char *env) {
int asan_inited;
bool asan_init_is_running;
void (*death_callback)(void);
-static void (*error_report_callback)(const char*);
-char *error_message_buffer = 0;
-uptr error_message_buffer_pos = 0;
-uptr error_message_buffer_size = 0;
// -------------------------- Misc ---------------- {{{1
void ShowStatsAndAbort() {
@@ -155,146 +171,23 @@ void ShowStatsAndAbort() {
Die();
}
-static void PrintBytes(const char *before, uptr *a) {
- u8 *bytes = (u8*)a;
- uptr byte_num = (__WORDSIZE) / 8;
- AsanPrintf("%s%p:", before, (void*)a);
- for (uptr i = 0; i < byte_num; i++) {
- AsanPrintf(" %x%x", bytes[i] >> 4, bytes[i] & 15);
- }
- AsanPrintf("\n");
-}
-
-void AppendToErrorMessageBuffer(const char *buffer) {
- if (error_message_buffer) {
- uptr length = internal_strlen(buffer);
- CHECK_GE(error_message_buffer_size, error_message_buffer_pos);
- uptr remaining = error_message_buffer_size - error_message_buffer_pos;
- internal_strncpy(error_message_buffer + error_message_buffer_pos,
- buffer, remaining);
- error_message_buffer[error_message_buffer_size - 1] = '\0';
- // FIXME: reallocate the buffer instead of truncating the message.
- error_message_buffer_pos += remaining > length ? length : remaining;
- }
-}
-
// ---------------------- mmap -------------------- {{{1
// Reserve memory range [beg, end].
static void ReserveShadowMemoryRange(uptr beg, uptr end) {
- CHECK((beg % kPageSize) == 0);
- CHECK(((end + 1) % kPageSize) == 0);
+ CHECK((beg % GetPageSizeCached()) == 0);
+ CHECK(((end + 1) % GetPageSizeCached()) == 0);
uptr size = end - beg + 1;
void *res = MmapFixedNoReserve(beg, size);
- CHECK(res == (void*)beg && "ReserveShadowMemoryRange failed");
-}
-
-// ---------------------- LowLevelAllocator ------------- {{{1
-void *LowLevelAllocator::Allocate(uptr size) {
- CHECK((size & (size - 1)) == 0 && "size must be a power of two");
- if (allocated_end_ - allocated_current_ < (sptr)size) {
- uptr size_to_allocate = Max(size, kPageSize);
- allocated_current_ =
- (char*)MmapOrDie(size_to_allocate, __FUNCTION__);
- allocated_end_ = allocated_current_ + size_to_allocate;
- PoisonShadow((uptr)allocated_current_, size_to_allocate,
- kAsanInternalHeapMagic);
- }
- CHECK(allocated_end_ - allocated_current_ >= (sptr)size);
- void *res = allocated_current_;
- allocated_current_ += size;
- return res;
-}
-
-// ---------------------- DescribeAddress -------------------- {{{1
-static bool DescribeStackAddress(uptr addr, uptr access_size) {
- AsanThread *t = asanThreadRegistry().FindThreadByStackAddress(addr);
- if (!t) return false;
- const sptr kBufSize = 4095;
- char buf[kBufSize];
- uptr offset = 0;
- const char *frame_descr = t->GetFrameNameByAddr(addr, &offset);
- // This string is created by the compiler and has the following form:
- // "FunctioName n alloc_1 alloc_2 ... alloc_n"
- // where alloc_i looks like "offset size len ObjectName ".
- CHECK(frame_descr);
- // Report the function name and the offset.
- const char *name_end = internal_strchr(frame_descr, ' ');
- CHECK(name_end);
- buf[0] = 0;
- internal_strncat(buf, frame_descr,
- Min(kBufSize,
- static_cast<sptr>(name_end - frame_descr)));
- AsanPrintf("Address %p is located at offset %zu "
- "in frame <%s> of T%d's stack:\n",
- (void*)addr, offset, buf, t->tid());
- // Report the number of stack objects.
- char *p;
- uptr n_objects = internal_simple_strtoll(name_end, &p, 10);
- CHECK(n_objects > 0);
- AsanPrintf(" This frame has %zu object(s):\n", n_objects);
- // Report all objects in this frame.
- for (uptr i = 0; i < n_objects; i++) {
- uptr beg, size;
- sptr len;
- beg = internal_simple_strtoll(p, &p, 10);
- size = internal_simple_strtoll(p, &p, 10);
- len = internal_simple_strtoll(p, &p, 10);
- if (beg <= 0 || size <= 0 || len < 0 || *p != ' ') {
- AsanPrintf("AddressSanitizer can't parse the stack frame "
- "descriptor: |%s|\n", frame_descr);
- break;
- }
- p++;
- buf[0] = 0;
- internal_strncat(buf, p, Min(kBufSize, len));
- p += len;
- AsanPrintf(" [%zu, %zu) '%s'\n", beg, beg + size, buf);
- }
- AsanPrintf("HINT: this may be a false positive if your program uses "
- "some custom stack unwind mechanism\n"
- " (longjmp and C++ exceptions *are* supported)\n");
- t->summary()->Announce();
- return true;
-}
-
-static bool DescribeAddrIfShadow(uptr addr) {
- if (AddrIsInMem(addr))
- return false;
- static const char kAddrInShadowReport[] =
- "Address %p is located in the %s.\n";
- if (AddrIsInShadowGap(addr)) {
- AsanPrintf(kAddrInShadowReport, addr, "shadow gap area");
- return true;
- }
- if (AddrIsInHighShadow(addr)) {
- AsanPrintf(kAddrInShadowReport, addr, "high shadow area");
- return true;
- }
- if (AddrIsInLowShadow(addr)) {
- AsanPrintf(kAddrInShadowReport, addr, "low shadow area");
- return true;
+ if (res != (void*)beg) {
+ Report("ReserveShadowMemoryRange failed while trying to map 0x%zx bytes. "
+ "Perhaps you're using ulimit -v\n", size);
+ Abort();
}
-
- CHECK(0); // Unreachable.
- return false;
}
-static NOINLINE void DescribeAddress(uptr addr, uptr access_size) {
- // Check if this is shadow or shadow gap.
- if (DescribeAddrIfShadow(addr))
- return;
-
- CHECK(AddrIsInMem(addr));
-
- // Check if this is a global.
- if (DescribeAddrIfGlobal(addr))
- return;
-
- if (DescribeStackAddress(addr, access_size))
- return;
-
- // finally, check if this is a heap.
- DescribeHeapAddress(addr, access_size);
+// --------------- LowLevelAllocateCallbac ---------- {{{1
+static void OnLowLevelAllocate(uptr ptr, uptr size) {
+ PoisonShadow(ptr, size, kAsanInternalHeapMagic);
}
// -------------------------- Run-time entry ------------------- {{{1
@@ -325,29 +218,48 @@ ASAN_REPORT_ERROR(store, true, 16)
// time.
static NOINLINE void force_interface_symbols() {
volatile int fake_condition = 0; // prevent dead condition elimination.
- if (fake_condition) {
- __asan_report_load1(0);
- __asan_report_load2(0);
- __asan_report_load4(0);
- __asan_report_load8(0);
- __asan_report_load16(0);
- __asan_report_store1(0);
- __asan_report_store2(0);
- __asan_report_store4(0);
- __asan_report_store8(0);
- __asan_report_store16(0);
- __asan_register_global(0, 0, 0);
- __asan_register_globals(0, 0);
- __asan_unregister_globals(0, 0);
- __asan_set_death_callback(0);
- __asan_set_error_report_callback(0);
- __asan_handle_no_return();
+ // __asan_report_* functions are noreturn, so we need a switch to prevent
+ // the compiler from removing any of them.
+ switch (fake_condition) {
+ case 1: __asan_report_load1(0); break;
+ case 2: __asan_report_load2(0); break;
+ case 3: __asan_report_load4(0); break;
+ case 4: __asan_report_load8(0); break;
+ case 5: __asan_report_load16(0); break;
+ case 6: __asan_report_store1(0); break;
+ case 7: __asan_report_store2(0); break;
+ case 8: __asan_report_store4(0); break;
+ case 9: __asan_report_store8(0); break;
+ case 10: __asan_report_store16(0); break;
+ case 12: __asan_register_globals(0, 0); break;
+ case 13: __asan_unregister_globals(0, 0); break;
+ case 14: __asan_set_death_callback(0); break;
+ case 15: __asan_set_error_report_callback(0); break;
+ case 16: __asan_handle_no_return(); break;
+ case 17: __asan_address_is_poisoned(0); break;
+ case 18: __asan_get_allocated_size(0); break;
+ case 19: __asan_get_current_allocated_bytes(); break;
+ case 20: __asan_get_estimated_allocated_size(0); break;
+ case 21: __asan_get_free_bytes(); break;
+ case 22: __asan_get_heap_size(); break;
+ case 23: __asan_get_ownership(0); break;
+ case 24: __asan_get_unmapped_bytes(); break;
+ case 25: __asan_poison_memory_region(0, 0); break;
+ case 26: __asan_unpoison_memory_region(0, 0); break;
+ case 27: __asan_set_error_exit_code(0); break;
+ case 28: __asan_stack_free(0, 0, 0); break;
+ case 29: __asan_stack_malloc(0, 0); break;
+ case 30: __asan_before_dynamic_init(0, 0); break;
+ case 31: __asan_after_dynamic_init(); break;
+ case 32: __asan_poison_stack_memory(0, 0); break;
+ case 33: __asan_unpoison_stack_memory(0, 0); break;
+ case 34: __asan_region_is_poisoned(0, 0); break;
+ case 35: __asan_describe_address(0); break;
}
}
-// -------------------------- Init ------------------- {{{1
static void asan_atexit() {
- AsanPrintf("AddressSanitizer exit stats:\n");
+ Printf("AddressSanitizer exit stats:\n");
__asan_print_accumulated_stats();
}
@@ -356,7 +268,14 @@ static void asan_atexit() {
// ---------------------- Interface ---------------- {{{1
using namespace __asan; // NOLINT
-int __asan_set_error_exit_code(int exit_code) {
+#if !SANITIZER_SUPPORTS_WEAK_HOOKS
+extern "C" {
+SANITIZER_WEAK_ATTRIBUTE SANITIZER_INTERFACE_ATTRIBUTE
+const char* __asan_default_options() { return ""; }
+} // extern "C"
+#endif
+
+int NOINLINE __asan_set_error_exit_code(int exit_code) {
int old = flags()->exitcode;
flags()->exitcode = exit_code;
return old;
@@ -366,8 +285,9 @@ void NOINLINE __asan_handle_no_return() {
int local_stack;
AsanThread *curr_thread = asanThreadRegistry().GetCurrent();
CHECK(curr_thread);
+ uptr PageSize = GetPageSizeCached();
uptr top = curr_thread->stack_top();
- uptr bottom = ((uptr)&local_stack - kPageSize) & ~(kPageSize-1);
+ uptr bottom = ((uptr)&local_stack - PageSize) & ~(PageSize-1);
PoisonShadow(bottom, top - bottom, 0);
}
@@ -375,134 +295,35 @@ void NOINLINE __asan_set_death_callback(void (*callback)(void)) {
death_callback = callback;
}
-void NOINLINE __asan_set_error_report_callback(void (*callback)(const char*)) {
- error_report_callback = callback;
- if (callback) {
- error_message_buffer_size = 1 << 16;
- error_message_buffer =
- (char*)MmapOrDie(error_message_buffer_size, __FUNCTION__);
- error_message_buffer_pos = 0;
- }
-}
-
-void __asan_report_error(uptr pc, uptr bp, uptr sp,
- uptr addr, bool is_write, uptr access_size) {
- static atomic_uint32_t num_calls;
- if (atomic_fetch_add(&num_calls, 1, memory_order_relaxed) != 0) {
- // Do not print more than one report, otherwise they will mix up.
- // We can not return here because the function is marked as never-return.
- AsanPrintf("AddressSanitizer: while reporting a bug found another one."
- "Ignoring.\n");
- SleepForSeconds(5);
- Die();
- }
-
- AsanPrintf("===================================================="
- "=============\n");
- const char *bug_descr = "unknown-crash";
- if (AddrIsInMem(addr)) {
- u8 *shadow_addr = (u8*)MemToShadow(addr);
- // If we are accessing 16 bytes, look at the second shadow byte.
- if (*shadow_addr == 0 && access_size > SHADOW_GRANULARITY)
- shadow_addr++;
- // If we are in the partial right redzone, look at the next shadow byte.
- if (*shadow_addr > 0 && *shadow_addr < 128)
- shadow_addr++;
- switch (*shadow_addr) {
- case kAsanHeapLeftRedzoneMagic:
- case kAsanHeapRightRedzoneMagic:
- bug_descr = "heap-buffer-overflow";
- break;
- case kAsanHeapFreeMagic:
- bug_descr = "heap-use-after-free";
- break;
- case kAsanStackLeftRedzoneMagic:
- bug_descr = "stack-buffer-underflow";
- break;
- case kAsanStackMidRedzoneMagic:
- case kAsanStackRightRedzoneMagic:
- case kAsanStackPartialRedzoneMagic:
- bug_descr = "stack-buffer-overflow";
- break;
- case kAsanStackAfterReturnMagic:
- bug_descr = "stack-use-after-return";
- break;
- case kAsanUserPoisonedMemoryMagic:
- bug_descr = "use-after-poison";
- break;
- case kAsanGlobalRedzoneMagic:
- bug_descr = "global-buffer-overflow";
- break;
- }
- }
-
- AsanThread *curr_thread = asanThreadRegistry().GetCurrent();
- u32 curr_tid = asanThreadRegistry().GetCurrentTidOrInvalid();
-
- if (curr_thread) {
- // We started reporting an error message. Stop using the fake stack
- // in case we will call an instrumented function from a symbolizer.
- curr_thread->fake_stack().StopUsingFakeStack();
- }
-
- AsanReport("ERROR: AddressSanitizer %s on address "
- "%p at pc 0x%zx bp 0x%zx sp 0x%zx\n",
- bug_descr, (void*)addr, pc, bp, sp);
-
- AsanPrintf("%s of size %zu at %p thread T%d\n",
- access_size ? (is_write ? "WRITE" : "READ") : "ACCESS",
- access_size, (void*)addr, curr_tid);
-
- if (flags()->debug) {
- PrintBytes("PC: ", (uptr*)pc);
- }
-
- GET_STACK_TRACE_WITH_PC_AND_BP(kStackTraceMax, pc, bp);
- stack.PrintStack();
-
- DescribeAddress(addr, access_size);
-
- if (AddrIsInMem(addr)) {
- uptr shadow_addr = MemToShadow(addr);
- AsanReport("ABORTING\n");
- __asan_print_accumulated_stats();
- AsanPrintf("Shadow byte and word:\n");
- AsanPrintf(" %p: %x\n", (void*)shadow_addr, *(unsigned char*)shadow_addr);
- uptr aligned_shadow = shadow_addr & ~(kWordSize - 1);
- PrintBytes(" ", (uptr*)(aligned_shadow));
- AsanPrintf("More shadow bytes:\n");
- PrintBytes(" ", (uptr*)(aligned_shadow-4*kWordSize));
- PrintBytes(" ", (uptr*)(aligned_shadow-3*kWordSize));
- PrintBytes(" ", (uptr*)(aligned_shadow-2*kWordSize));
- PrintBytes(" ", (uptr*)(aligned_shadow-1*kWordSize));
- PrintBytes("=>", (uptr*)(aligned_shadow+0*kWordSize));
- PrintBytes(" ", (uptr*)(aligned_shadow+1*kWordSize));
- PrintBytes(" ", (uptr*)(aligned_shadow+2*kWordSize));
- PrintBytes(" ", (uptr*)(aligned_shadow+3*kWordSize));
- PrintBytes(" ", (uptr*)(aligned_shadow+4*kWordSize));
- }
- if (error_report_callback) {
- error_report_callback(error_message_buffer);
- }
- Die();
-}
-
-
void __asan_init() {
if (asan_inited) return;
+ CHECK(!asan_init_is_running && "ASan init calls itself!");
asan_init_is_running = true;
// Make sure we are not statically linked.
AsanDoesNotSupportStaticLinkage();
- // Initialize flags.
+ // Install tool-specific callbacks in sanitizer_common.
+ SetDieCallback(AsanDie);
+ SetCheckFailedCallback(AsanCheckFailed);
+ SetPrintfAndReportCallback(AppendToErrorMessageBuffer);
+
+ // Initialize flags. This must be done early, because most of the
+ // initialization steps look at flags().
const char *options = GetEnv("ASAN_OPTIONS");
InitializeFlags(flags(), options);
+ __sanitizer_set_report_path(flags()->log_path);
if (flags()->verbosity && options) {
Report("Parsed ASAN_OPTIONS: %s\n", options);
}
+ // Re-exec ourselves if we need to set additional env or command line args.
+ MaybeReexec();
+
+ // Setup internal allocator callback.
+ SetLowLevelAllocateCallback(OnLowLevelAllocate);
+
if (flags()->atexit) {
Atexit(asan_atexit);
}
@@ -543,12 +364,13 @@ void __asan_init() {
}
uptr shadow_start = kLowShadowBeg;
- if (kLowShadowBeg > 0) shadow_start -= kMmapGranularity;
+ if (kLowShadowBeg > 0) shadow_start -= GetMmapGranularity();
uptr shadow_end = kHighShadowEnd;
if (MemoryRangeIsAvailable(shadow_start, shadow_end)) {
if (kLowShadowBeg != kLowShadowEnd) {
// mmap the low shadow plus at least one page.
- ReserveShadowMemoryRange(kLowShadowBeg - kMmapGranularity, kLowShadowEnd);
+ ReserveShadowMemoryRange(kLowShadowBeg - GetMmapGranularity(),
+ kLowShadowEnd);
}
// mmap the high shadow.
ReserveShadowMemoryRange(kHighShadowBeg, kHighShadowEnd);
@@ -563,6 +385,13 @@ void __asan_init() {
}
InstallSignalHandlers();
+ // Start symbolizer process if necessary.
+ if (flags()->symbolize) {
+ const char *external_symbolizer = GetEnv("ASAN_SYMBOLIZER_PATH");
+ if (external_symbolizer) {
+ InitializeExternalSymbolizer(external_symbolizer);
+ }
+ }
// On Linux AsanThread::ThreadStart() calls malloc() that's why asan_inited
// should be set to 1 prior to initializing the threads.
diff --git a/lib/asan/asan_stack.cc b/lib/asan/asan_stack.cc
index d6103c2c98fa..ebf22fd34ca1 100644
--- a/lib/asan/asan_stack.cc
+++ b/lib/asan/asan_stack.cc
@@ -11,222 +11,33 @@
//
// Code for ASan stack trace.
//===----------------------------------------------------------------------===//
-#include "asan_interceptors.h"
-#include "asan_lock.h"
+#include "asan_flags.h"
#include "asan_stack.h"
-#include "asan_thread.h"
-#include "asan_thread_registry.h"
-#include "sanitizer_common/sanitizer_procmaps.h"
-#include "sanitizer_common/sanitizer_symbolizer.h"
-
-#ifdef ASAN_USE_EXTERNAL_SYMBOLIZER
-extern bool
-ASAN_USE_EXTERNAL_SYMBOLIZER(const void *pc, char *out, int out_size);
-#endif
+#include "sanitizer/asan_interface.h"
namespace __asan {
-// ----------------------- AsanStackTrace ----------------------------- {{{1
-// PCs in stack traces are actually the return addresses, that is,
-// addresses of the next instructions after the call. That's why we
-// decrement them.
-static uptr patch_pc(uptr pc) {
-#ifdef __arm__
- // Cancel Thumb bit.
- pc = pc & (~1);
-#endif
- return pc - 1;
+static bool MaybeCallAsanSymbolize(const void *pc, char *out_buffer,
+ int out_size) {
+ return (&__asan_symbolize) ? __asan_symbolize(pc, out_buffer, out_size)
+ : false;
}
-#if defined(ASAN_USE_EXTERNAL_SYMBOLIZER)
-void AsanStackTrace::PrintStack(uptr *addr, uptr size) {
- for (uptr i = 0; i < size && addr[i]; i++) {
- uptr pc = addr[i];
- if (i < size - 1 && addr[i + 1])
- pc = patch_pc(pc);
- char buff[4096];
- ASAN_USE_EXTERNAL_SYMBOLIZER((void*)pc, buff, sizeof(buff));
- AsanPrintf(" #%zu 0x%zx %s\n", i, pc, buff);
- }
+void PrintStack(StackTrace *stack) {
+ stack->PrintStack(stack->trace, stack->size, flags()->symbolize,
+ flags()->strip_path_prefix, MaybeCallAsanSymbolize);
}
-#else // ASAN_USE_EXTERNAL_SYMBOLIZER
-void AsanStackTrace::PrintStack(uptr *addr, uptr size) {
- ProcessMaps proc_maps;
- uptr frame_num = 0;
- for (uptr i = 0; i < size && addr[i]; i++) {
- uptr pc = addr[i];
- if (i < size - 1 && addr[i + 1])
- pc = patch_pc(pc);
- AddressInfo addr_frames[64];
- uptr addr_frames_num = 0;
- if (flags()->symbolize) {
- addr_frames_num = SymbolizeCode(pc, addr_frames,
- ASAN_ARRAY_SIZE(addr_frames));
- }
- if (addr_frames_num > 0) {
- for (uptr j = 0; j < addr_frames_num; j++) {
- AddressInfo &info = addr_frames[j];
- AsanPrintf(" #%zu 0x%zx", frame_num, pc);
- if (info.function) {
- AsanPrintf(" in %s", info.function);
- }
- if (info.file) {
- AsanPrintf(" %s:%d:%d", info.file, info.line, info.column);
- } else if (info.module) {
- AsanPrintf(" (%s+0x%zx)", info.module, info.module_offset);
- }
- AsanPrintf("\n");
- info.Clear();
- frame_num++;
- }
- } else {
- uptr offset;
- char filename[4096];
- if (proc_maps.GetObjectNameAndOffset(pc, &offset,
- filename, sizeof(filename))) {
- AsanPrintf(" #%zu 0x%zx (%s+0x%zx)\n", frame_num, pc, filename,
- offset);
- } else {
- AsanPrintf(" #%zu 0x%zx\n", frame_num, pc);
- }
- frame_num++;
- }
- }
-}
-#endif // ASAN_USE_EXTERNAL_SYMBOLIZER
+} // namespace __asan
-uptr AsanStackTrace::GetCurrentPc() {
- return GET_CALLER_PC();
-}
+// ------------------ Interface -------------- {{{1
-void AsanStackTrace::FastUnwindStack(uptr pc, uptr bp) {
- CHECK(size == 0 && trace[0] == pc);
- size = 1;
- if (!asan_inited) return;
- AsanThread *t = asanThreadRegistry().GetCurrent();
- if (!t) return;
- uptr *frame = (uptr*)bp;
- uptr *prev_frame = frame;
- uptr *top = (uptr*)t->stack_top();
- uptr *bottom = (uptr*)t->stack_bottom();
- while (frame >= prev_frame &&
- frame < top - 2 &&
- frame > bottom &&
- size < max_size) {
- uptr pc1 = frame[1];
- if (pc1 != pc) {
- trace[size++] = pc1;
- }
- prev_frame = frame;
- frame = (uptr*)frame[0];
- }
+// 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
+SANITIZER_WEAK_ATTRIBUTE SANITIZER_INTERFACE_ATTRIBUTE NOINLINE
+bool __asan_symbolize(const void *pc, char *out_buffer, int out_size) {
+ return false;
}
-
-// On 32-bits we don't compress stack traces.
-// On 64-bits we compress stack traces: if a given pc differes slightly from
-// the previous one, we record a 31-bit offset instead of the full pc.
-uptr AsanStackTrace::CompressStack(AsanStackTrace *stack,
- u32 *compressed, uptr size) {
-#if __WORDSIZE == 32
- // Don't compress, just copy.
- uptr res = 0;
- for (uptr i = 0; i < stack->size && i < size; i++) {
- compressed[i] = stack->trace[i];
- res++;
- }
- if (stack->size < size)
- compressed[stack->size] = 0;
-#else // 64 bits, compress.
- uptr prev_pc = 0;
- const uptr kMaxOffset = (1ULL << 30) - 1;
- uptr c_index = 0;
- uptr res = 0;
- for (uptr i = 0, n = stack->size; i < n; i++) {
- uptr pc = stack->trace[i];
- if (!pc) break;
- if ((s64)pc < 0) break;
- // Printf("C pc[%zu] %zx\n", i, pc);
- if (prev_pc - pc < kMaxOffset || pc - prev_pc < kMaxOffset) {
- uptr offset = (s64)(pc - prev_pc);
- offset |= (1U << 31);
- if (c_index >= size) break;
- // Printf("C co[%zu] offset %zx\n", i, offset);
- compressed[c_index++] = offset;
- } else {
- uptr hi = pc >> 32;
- uptr lo = (pc << 32) >> 32;
- CHECK((hi & (1 << 31)) == 0);
- if (c_index + 1 >= size) break;
- // Printf("C co[%zu] hi/lo: %zx %zx\n", c_index, hi, lo);
- compressed[c_index++] = hi;
- compressed[c_index++] = lo;
- }
- res++;
- prev_pc = pc;
- }
- if (c_index < size)
- compressed[c_index] = 0;
- if (c_index + 1 < size)
- compressed[c_index + 1] = 0;
-#endif // __WORDSIZE
-
- // debug-only code
-#if 0
- AsanStackTrace check_stack;
- UncompressStack(&check_stack, compressed, size);
- if (res < check_stack.size) {
- Printf("res %zu check_stack.size %zu; c_size %zu\n", res,
- check_stack.size, size);
- }
- // |res| may be greater than check_stack.size, because
- // UncompressStack(CompressStack(stack)) eliminates the 0x0 frames.
- CHECK(res >= check_stack.size);
- CHECK(0 == REAL(memcmp)(check_stack.trace, stack->trace,
- check_stack.size * sizeof(uptr)));
#endif
-
- return res;
-}
-
-void AsanStackTrace::UncompressStack(AsanStackTrace *stack,
- u32 *compressed, uptr size) {
-#if __WORDSIZE == 32
- // Don't uncompress, just copy.
- stack->size = 0;
- for (uptr i = 0; i < size && i < kStackTraceMax; i++) {
- if (!compressed[i]) break;
- stack->size++;
- stack->trace[i] = compressed[i];
- }
-#else // 64 bits, uncompress
- uptr prev_pc = 0;
- stack->size = 0;
- for (uptr i = 0; i < size && stack->size < kStackTraceMax; i++) {
- u32 x = compressed[i];
- uptr pc = 0;
- if (x & (1U << 31)) {
- // Printf("U co[%zu] offset: %x\n", i, x);
- // this is an offset
- s32 offset = x;
- offset = (offset << 1) >> 1; // remove the 31-byte and sign-extend.
- pc = prev_pc + offset;
- CHECK(pc);
- } else {
- // CHECK(i + 1 < size);
- if (i + 1 >= size) break;
- uptr hi = x;
- uptr lo = compressed[i+1];
- // Printf("U co[%zu] hi/lo: %zx %zx\n", i, hi, lo);
- i++;
- pc = (hi << 32) | lo;
- if (!pc) break;
- }
- // Printf("U pc[%zu] %zx\n", stack->size, pc);
- stack->trace[stack->size++] = pc;
- prev_pc = pc;
- }
-#endif // __WORDSIZE
-}
-
-} // namespace __asan
diff --git a/lib/asan/asan_stack.h b/lib/asan/asan_stack.h
index 6ca9a0b28bfc..46c9f3408725 100644
--- a/lib/asan/asan_stack.h
+++ b/lib/asan/asan_stack.h
@@ -14,91 +14,53 @@
#ifndef ASAN_STACK_H
#define ASAN_STACK_H
-#include "asan_internal.h"
+#include "sanitizer_common/sanitizer_stacktrace.h"
+#include "asan_flags.h"
namespace __asan {
-static const uptr kStackTraceMax = 64;
-
-struct AsanStackTrace {
- uptr size;
- uptr max_size;
- uptr trace[kStackTraceMax];
- static void PrintStack(uptr *addr, uptr size);
- void PrintStack() {
- PrintStack(this->trace, this->size);
- }
- void CopyTo(uptr *dst, uptr dst_size) {
- for (uptr i = 0; i < size && i < dst_size; i++)
- dst[i] = trace[i];
- for (uptr i = size; i < dst_size; i++)
- dst[i] = 0;
- }
-
- void CopyFrom(uptr *src, uptr src_size) {
- size = src_size;
- if (size > kStackTraceMax) size = kStackTraceMax;
- for (uptr i = 0; i < size; i++) {
- trace[i] = src[i];
- }
- }
-
- void GetStackTrace(uptr max_s, uptr pc, uptr bp);
-
- void FastUnwindStack(uptr pc, uptr bp);
-
- static uptr GetCurrentPc();
-
- static uptr CompressStack(AsanStackTrace *stack,
- u32 *compressed, uptr size);
- static void UncompressStack(AsanStackTrace *stack,
- u32 *compressed, uptr size);
-};
+void GetStackTrace(StackTrace *stack, uptr max_s, uptr pc, uptr bp, bool fast);
+void PrintStack(StackTrace *stack);
} // namespace __asan
-// Use this macro if you want to print stack trace with the caller
-// of the current function in the top frame.
-#define GET_CALLER_PC_BP_SP \
- uptr bp = GET_CURRENT_FRAME(); \
- uptr pc = GET_CALLER_PC(); \
- uptr local_stack; \
- uptr sp = (uptr)&local_stack
-
-// Use this macro if you want to print stack trace with the current
-// function in the top frame.
-#define GET_CURRENT_PC_BP_SP \
- uptr bp = GET_CURRENT_FRAME(); \
- uptr pc = AsanStackTrace::GetCurrentPc(); \
- uptr local_stack; \
- uptr sp = (uptr)&local_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.
-#define GET_STACK_TRACE_WITH_PC_AND_BP(max_s, pc, bp) \
- AsanStackTrace stack; \
- stack.GetStackTrace(max_s, pc, bp)
+#define GET_STACK_TRACE_WITH_PC_AND_BP(max_s, pc, bp, fast) \
+ StackTrace stack; \
+ GetStackTrace(&stack, max_s, pc, bp, fast)
// 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
// don't want stack trace to contain functions from ASan internals.
-#define GET_STACK_TRACE_HERE(max_size) \
+#define GET_STACK_TRACE(max_size, fast) \
GET_STACK_TRACE_WITH_PC_AND_BP(max_size, \
- AsanStackTrace::GetCurrentPc(), GET_CURRENT_FRAME())
+ StackTrace::GetCurrentPc(), GET_CURRENT_FRAME(), fast)
+
+#define GET_STACK_TRACE_FATAL(pc, bp) \
+ GET_STACK_TRACE_WITH_PC_AND_BP(kStackTraceMax, pc, bp, \
+ flags()->fast_unwind_on_fatal)
+
+#define GET_STACK_TRACE_FATAL_HERE \
+ GET_STACK_TRACE(kStackTraceMax, flags()->fast_unwind_on_fatal)
+
+#define GET_STACK_TRACE_THREAD \
+ GET_STACK_TRACE(kStackTraceMax, true)
-#define GET_STACK_TRACE_HERE_FOR_MALLOC \
- GET_STACK_TRACE_HERE(flags()->malloc_context_size)
+#define GET_STACK_TRACE_MALLOC \
+ GET_STACK_TRACE(flags()->malloc_context_size, \
+ flags()->fast_unwind_on_malloc)
-#define GET_STACK_TRACE_HERE_FOR_FREE(ptr) \
- GET_STACK_TRACE_HERE(flags()->malloc_context_size)
+#define GET_STACK_TRACE_FREE GET_STACK_TRACE_MALLOC
#define PRINT_CURRENT_STACK() \
{ \
- GET_STACK_TRACE_HERE(kStackTraceMax); \
- stack.PrintStack(); \
+ GET_STACK_TRACE(kStackTraceMax, \
+ flags()->fast_unwind_on_fatal); \
+ PrintStack(&stack); \
}
#endif // ASAN_STACK_H
diff --git a/lib/asan/asan_stats.cc b/lib/asan/asan_stats.cc
index ef5e53a8bf33..c57c8cc61aed 100644
--- a/lib/asan/asan_stats.cc
+++ b/lib/asan/asan_stats.cc
@@ -12,11 +12,11 @@
// Code related to statistics collected by AddressSanitizer.
//===----------------------------------------------------------------------===//
#include "asan_interceptors.h"
-#include "asan_interface.h"
#include "asan_internal.h"
-#include "asan_lock.h"
#include "asan_stats.h"
#include "asan_thread_registry.h"
+#include "sanitizer/asan_interface.h"
+#include "sanitizer_common/sanitizer_stackdepot.h"
namespace __asan {
@@ -27,39 +27,45 @@ AsanStats::AsanStats() {
static void PrintMallocStatsArray(const char *prefix,
uptr (&array)[kNumberOfSizeClasses]) {
- AsanPrintf("%s", prefix);
+ Printf("%s", prefix);
for (uptr i = 0; i < kNumberOfSizeClasses; i++) {
if (!array[i]) continue;
- AsanPrintf("%zu:%zu; ", i, array[i]);
+ Printf("%zu:%zu; ", i, array[i]);
}
- AsanPrintf("\n");
+ Printf("\n");
}
void AsanStats::Print() {
- AsanPrintf("Stats: %zuM malloced (%zuM for red zones) by %zu calls\n",
+ Printf("Stats: %zuM malloced (%zuM for red zones) by %zu calls\n",
malloced>>20, malloced_redzones>>20, mallocs);
- AsanPrintf("Stats: %zuM realloced by %zu calls\n", realloced>>20, reallocs);
- AsanPrintf("Stats: %zuM freed by %zu calls\n", freed>>20, frees);
- AsanPrintf("Stats: %zuM really freed by %zu calls\n",
+ Printf("Stats: %zuM realloced by %zu calls\n", realloced>>20, reallocs);
+ Printf("Stats: %zuM freed by %zu calls\n", freed>>20, frees);
+ Printf("Stats: %zuM really freed by %zu calls\n",
really_freed>>20, real_frees);
- AsanPrintf("Stats: %zuM (%zu full pages) mmaped in %zu calls\n",
- mmaped>>20, mmaped / kPageSize, mmaps);
+ Printf("Stats: %zuM (%zuM-%zuM) mmaped; %zu maps, %zu unmaps\n",
+ (mmaped-munmaped)>>20, mmaped>>20, munmaped>>20,
+ mmaps, munmaps);
PrintMallocStatsArray(" mmaps by size class: ", mmaped_by_size);
PrintMallocStatsArray(" mallocs by size class: ", malloced_by_size);
PrintMallocStatsArray(" frees by size class: ", freed_by_size);
PrintMallocStatsArray(" rfrees by size class: ", really_freed_by_size);
- AsanPrintf("Stats: malloc large: %zu small slow: %zu\n",
+ Printf("Stats: malloc large: %zu small slow: %zu\n",
malloc_large, malloc_small_slow);
}
-static AsanLock print_lock(LINKER_INITIALIZED);
+static BlockingMutex print_lock(LINKER_INITIALIZED);
static void PrintAccumulatedStats() {
- AsanStats stats = asanThreadRegistry().GetAccumulatedStats();
+ AsanStats stats;
+ asanThreadRegistry().GetAccumulatedStats(&stats);
// Use lock to keep reports from mixing up.
- ScopedLock lock(&print_lock);
+ BlockingMutexLock lock(&print_lock);
stats.Print();
+ StackDepotStats *stack_depot_stats = StackDepotGetStats();
+ Printf("Stats: StackDepot: %zd ids; %zdM mapped\n",
+ stack_depot_stats->n_uniq_ids, stack_depot_stats->mapped >> 20);
+ PrintInternalAllocatorStats();
}
} // namespace __asan
diff --git a/lib/asan/asan_stats.h b/lib/asan/asan_stats.h
index b4c63f44fc68..37846bc92ad2 100644
--- a/lib/asan/asan_stats.h
+++ b/lib/asan/asan_stats.h
@@ -37,6 +37,8 @@ struct AsanStats {
uptr realloced;
uptr mmaps;
uptr mmaped;
+ uptr munmaps;
+ uptr munmaped;
uptr mmaped_by_size[kNumberOfSizeClasses];
uptr malloced_by_size[kNumberOfSizeClasses];
uptr freed_by_size[kNumberOfSizeClasses];
@@ -54,6 +56,14 @@ struct AsanStats {
void Print();
};
+// A cross-platform equivalent of malloc_statistics_t on Mac OS.
+struct AsanMallocStats {
+ uptr blocks_in_use;
+ uptr size_in_use;
+ uptr max_size_in_use;
+ uptr size_allocated;
+};
+
} // namespace __asan
#endif // ASAN_STATS_H
diff --git a/lib/asan/asan_thread.cc b/lib/asan/asan_thread.cc
index 05a41ea9685a..778e91932ed5 100644
--- a/lib/asan/asan_thread.cc
+++ b/lib/asan/asan_thread.cc
@@ -26,24 +26,18 @@ AsanThread::AsanThread(LinkerInitialized x)
malloc_storage_(x),
stats_(x) { }
-static AsanLock mu_for_thread_summary(LINKER_INITIALIZED);
-static LowLevelAllocator allocator_for_thread_summary(LINKER_INITIALIZED);
-
AsanThread *AsanThread::Create(u32 parent_tid, thread_callback_t start_routine,
- void *arg, AsanStackTrace *stack) {
- uptr size = RoundUpTo(sizeof(AsanThread), kPageSize);
+ void *arg, StackTrace *stack) {
+ 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 = 1024;
+ const uptr kSummaryAllocSize = PageSize;
CHECK_LE(sizeof(AsanThreadSummary), kSummaryAllocSize);
- AsanThreadSummary *summary;
- {
- ScopedLock lock(&mu_for_thread_summary);
- summary = (AsanThreadSummary*)
- allocator_for_thread_summary.Allocate(kSummaryAllocSize);
- }
+ AsanThreadSummary *summary =
+ (AsanThreadSummary*)MmapOrDie(PageSize, "AsanThreadSummary");
summary->Init(parent_tid, stack);
summary->set_thread(thread);
thread->set_summary(summary);
@@ -73,14 +67,14 @@ void AsanThread::Destroy() {
// and we don't want it to have any poisoned stack.
ClearShadowForThreadStack();
fake_stack().Cleanup();
- uptr size = RoundUpTo(sizeof(AsanThread), kPageSize);
+ uptr size = RoundUpTo(sizeof(AsanThread), GetPageSizeCached());
UnmapOrDie(this, size);
}
void AsanThread::Init() {
SetThreadStackTopAndBottom();
CHECK(AddrIsInMem(stack_bottom_));
- CHECK(AddrIsInMem(stack_top_));
+ CHECK(AddrIsInMem(stack_top_ - 1));
ClearShadowForThreadStack();
if (flags()->verbosity >= 1) {
int local = 0;
@@ -125,25 +119,25 @@ void AsanThread::ClearShadowForThreadStack() {
const char *AsanThread::GetFrameNameByAddr(uptr addr, uptr *offset) {
uptr bottom = 0;
- bool is_fake_stack = false;
if (AddrIsInStack(addr)) {
bottom = stack_bottom();
} else {
bottom = fake_stack().AddrIsInFakeStack(addr);
CHECK(bottom);
- is_fake_stack = true;
+ *offset = addr - bottom;
+ return (const char *)((uptr*)bottom)[1];
}
- uptr aligned_addr = addr & ~(__WORDSIZE/8 - 1); // align addr.
+ uptr aligned_addr = addr & ~(SANITIZER_WORDSIZE/8 - 1); // align addr.
u8 *shadow_ptr = (u8*)MemToShadow(aligned_addr);
u8 *shadow_bottom = (u8*)MemToShadow(bottom);
while (shadow_ptr >= shadow_bottom &&
- *shadow_ptr != kAsanStackLeftRedzoneMagic) {
+ *shadow_ptr != kAsanStackLeftRedzoneMagic) {
shadow_ptr--;
}
while (shadow_ptr >= shadow_bottom &&
- *shadow_ptr == kAsanStackLeftRedzoneMagic) {
+ *shadow_ptr == kAsanStackLeftRedzoneMagic) {
shadow_ptr--;
}
@@ -153,8 +147,7 @@ const char *AsanThread::GetFrameNameByAddr(uptr addr, uptr *offset) {
}
uptr* ptr = (uptr*)SHADOW_TO_MEM((uptr)(shadow_ptr + 1));
- CHECK((ptr[0] == kCurrentStackFrameMagic) ||
- (is_fake_stack && ptr[0] == kRetiredStackFrameMagic));
+ CHECK(ptr[0] == kCurrentStackFrameMagic);
*offset = addr - (uptr)ptr;
return (const char*)ptr[1];
}
diff --git a/lib/asan/asan_thread.h b/lib/asan/asan_thread.h
index 9a032fe3e66c..acc27e52e224 100644
--- a/lib/asan/asan_thread.h
+++ b/lib/asan/asan_thread.h
@@ -31,7 +31,7 @@ class AsanThread;
class AsanThreadSummary {
public:
explicit AsanThreadSummary(LinkerInitialized) { } // for T0.
- void Init(u32 parent_tid, AsanStackTrace *stack) {
+ void Init(u32 parent_tid, StackTrace *stack) {
parent_tid_ = parent_tid;
announced_ = false;
tid_ = kInvalidTid;
@@ -39,35 +39,40 @@ class AsanThreadSummary {
internal_memcpy(&stack_, stack, sizeof(*stack));
}
thread_ = 0;
- }
- void Announce() {
- if (tid_ == 0) return; // no need to announce the main thread.
- if (!announced_) {
- announced_ = true;
- AsanPrintf("Thread T%d created by T%d here:\n", tid_, parent_tid_);
- stack_.PrintStack();
- }
+ name_[0] = 0;
}
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_; }
private:
u32 tid_;
u32 parent_tid_;
bool announced_;
- AsanStackTrace stack_;
+ StackTrace stack_;
AsanThread *thread_;
+ char name_[128];
};
+// AsanThreadSummary objects are never freed, so we need many of them.
+COMPILER_CHECK(sizeof(AsanThreadSummary) <= 4094);
+
// 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, AsanStackTrace *stack);
+ void *arg, StackTrace *stack);
void Destroy();
void Init(); // Should be called from the thread itself.
@@ -91,7 +96,6 @@ class AsanThread {
AsanStats &stats() { return stats_; }
private:
-
void SetThreadStackTopAndBottom();
void ClearShadowForThreadStack();
AsanThreadSummary *summary_;
diff --git a/lib/asan/asan_thread_registry.cc b/lib/asan/asan_thread_registry.cc
index 4540d589c552..80675405fbd5 100644
--- a/lib/asan/asan_thread_registry.cc
+++ b/lib/asan/asan_thread_registry.cc
@@ -20,7 +20,7 @@
namespace __asan {
-static AsanThreadRegistry asan_thread_registry(__asan::LINKER_INITIALIZED);
+static AsanThreadRegistry asan_thread_registry(LINKER_INITIALIZED);
AsanThreadRegistry &asanThreadRegistry() {
return asan_thread_registry;
@@ -30,6 +30,7 @@ AsanThreadRegistry::AsanThreadRegistry(LinkerInitialized x)
: main_thread_(x),
main_thread_summary_(x),
accumulated_stats_(x),
+ max_malloced_memory_(x),
mu_(x) { }
void AsanThreadRegistry::Init() {
@@ -43,7 +44,7 @@ void AsanThreadRegistry::Init() {
}
void AsanThreadRegistry::RegisterThread(AsanThread *thread) {
- ScopedLock lock(&mu_);
+ BlockingMutexLock lock(&mu_);
u32 tid = n_threads_;
n_threads_++;
CHECK(n_threads_ < kMaxNumberOfThreads);
@@ -55,7 +56,7 @@ void AsanThreadRegistry::RegisterThread(AsanThread *thread) {
}
void AsanThreadRegistry::UnregisterThread(AsanThread *thread) {
- ScopedLock lock(&mu_);
+ BlockingMutexLock lock(&mu_);
FlushToAccumulatedStatsUnlocked(&thread->stats());
AsanThreadSummary *summary = thread->summary();
CHECK(summary);
@@ -69,7 +70,7 @@ AsanThread *AsanThreadRegistry::GetMain() {
AsanThread *AsanThreadRegistry::GetCurrent() {
AsanThreadSummary *summary = (AsanThreadSummary *)AsanTSDGet();
if (!summary) {
-#ifdef ANDROID
+#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
@@ -103,32 +104,51 @@ AsanStats &AsanThreadRegistry::GetCurrentThreadStats() {
return (t) ? t->stats() : main_thread_.stats();
}
-AsanStats AsanThreadRegistry::GetAccumulatedStats() {
- ScopedLock lock(&mu_);
+void AsanThreadRegistry::GetAccumulatedStats(AsanStats *stats) {
+ BlockingMutexLock lock(&mu_);
UpdateAccumulatedStatsUnlocked();
- return accumulated_stats_;
+ internal_memcpy(stats, &accumulated_stats_, sizeof(accumulated_stats_));
}
uptr AsanThreadRegistry::GetCurrentAllocatedBytes() {
- ScopedLock lock(&mu_);
+ BlockingMutexLock lock(&mu_);
UpdateAccumulatedStatsUnlocked();
- return accumulated_stats_.malloced - accumulated_stats_.freed;
+ 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() {
- ScopedLock lock(&mu_);
+ BlockingMutexLock lock(&mu_);
UpdateAccumulatedStatsUnlocked();
- return accumulated_stats_.mmaped;
+ return accumulated_stats_.mmaped - accumulated_stats_.munmaped;
}
uptr AsanThreadRegistry::GetFreeBytes() {
- ScopedLock lock(&mu_);
+ BlockingMutexLock lock(&mu_);
UpdateAccumulatedStatsUnlocked();
- return accumulated_stats_.mmaped
- - accumulated_stats_.malloced
- - accumulated_stats_.malloced_redzones
- + accumulated_stats_.really_freed
- + accumulated_stats_.really_freed_redzones;
+ 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) {
@@ -138,7 +158,7 @@ AsanThreadSummary *AsanThreadRegistry::FindByTid(u32 tid) {
}
AsanThread *AsanThreadRegistry::FindThreadByStackAddress(uptr addr) {
- ScopedLock lock(&mu_);
+ BlockingMutexLock lock(&mu_);
for (u32 tid = 0; tid < n_threads_; tid++) {
AsanThread *t = thread_summaries_[tid]->thread();
if (!t || !(t->fake_stack().StackSize())) continue;
@@ -156,6 +176,12 @@ void AsanThreadRegistry::UpdateAccumulatedStatsUnlocked() {
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) {
diff --git a/lib/asan/asan_thread_registry.h b/lib/asan/asan_thread_registry.h
index 7037b9edc161..adb1a6d4f32d 100644
--- a/lib/asan/asan_thread_registry.h
+++ b/lib/asan/asan_thread_registry.h
@@ -15,10 +15,10 @@
#ifndef ASAN_THREAD_REGISTRY_H
#define ASAN_THREAD_REGISTRY_H
-#include "asan_lock.h"
#include "asan_stack.h"
#include "asan_stats.h"
#include "asan_thread.h"
+#include "sanitizer_common/sanitizer_mutex.h"
namespace __asan {
@@ -47,12 +47,13 @@ class AsanThreadRegistry {
// Returns stats for GetCurrent(), or stats for
// T0 if GetCurrent() returns 0.
AsanStats &GetCurrentThreadStats();
- // Flushes all thread-local stats to accumulated stats, and returns
+ // Flushes all thread-local stats to accumulated stats, and makes
// a copy of accumulated stats.
- AsanStats GetAccumulatedStats();
+ void GetAccumulatedStats(AsanStats *stats);
uptr GetCurrentAllocatedBytes();
uptr GetHeapSize();
uptr GetFreeBytes();
+ void FillMallocStatistics(AsanMallocStats *malloc_stats);
AsanThreadSummary *FindByTid(u32 tid);
AsanThread *FindThreadByStackAddress(uptr addr);
@@ -68,8 +69,11 @@ class AsanThreadRegistry {
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_;
- AsanLock mu_;
+ BlockingMutex mu_;
bool inited_;
};
diff --git a/lib/asan/asan_win.cc b/lib/asan/asan_win.cc
index 9e899d5865fa..d8ce050641bc 100644
--- a/lib/asan/asan_win.cc
+++ b/lib/asan/asan_win.cc
@@ -17,30 +17,29 @@
#include <dbghelp.h>
#include <stdlib.h>
-#include <new> // FIXME: temporarily needed for placement new in AsanLock.
-
#include "asan_interceptors.h"
#include "asan_internal.h"
-#include "asan_lock.h"
#include "asan_thread.h"
#include "sanitizer_common/sanitizer_libc.h"
+#include "sanitizer_common/sanitizer_mutex.h"
namespace __asan {
// ---------------------- Stacktraces, symbols, etc. ---------------- {{{1
-static AsanLock dbghelp_lock(LINKER_INITIALIZED);
+static BlockingMutex dbghelp_lock(LINKER_INITIALIZED);
static bool dbghelp_initialized = false;
#pragma comment(lib, "dbghelp.lib")
-void AsanStackTrace::GetStackTrace(uptr max_s, uptr pc, uptr bp) {
- max_size = max_s;
+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, max_size, tmp, 0),
- offset = 0;
+ 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++) {
@@ -50,86 +49,9 @@ void AsanStackTrace::GetStackTrace(uptr max_s, uptr pc, uptr bp) {
break;
}
- size = cs_ret - offset;
- for (uptr i = 0; i < size; i++)
- trace[i] = (uptr)tmp[i + offset];
-}
-
-bool __asan_WinSymbolize(const void *addr, char *out_buffer, int buffer_size) {
- ScopedLock lock(&dbghelp_lock);
- if (!dbghelp_initialized) {
- SymSetOptions(SYMOPT_DEFERRED_LOADS |
- SYMOPT_UNDNAME |
- SYMOPT_LOAD_LINES);
- CHECK(SymInitialize(GetCurrentProcess(), 0, TRUE));
- // FIXME: We don't call SymCleanup() on exit yet - should we?
- dbghelp_initialized = true;
- }
-
- // See http://msdn.microsoft.com/en-us/library/ms680578(VS.85).aspx
- char buffer[sizeof(SYMBOL_INFO) + MAX_SYM_NAME * sizeof(CHAR)];
- PSYMBOL_INFO symbol = (PSYMBOL_INFO)buffer;
- symbol->SizeOfStruct = sizeof(SYMBOL_INFO);
- symbol->MaxNameLen = MAX_SYM_NAME;
- DWORD64 offset = 0;
- BOOL got_objname = SymFromAddr(GetCurrentProcess(),
- (DWORD64)addr, &offset, symbol);
- if (!got_objname)
- return false;
-
- DWORD unused;
- IMAGEHLP_LINE64 info;
- info.SizeOfStruct = sizeof(IMAGEHLP_LINE64);
- BOOL got_fileline = SymGetLineFromAddr64(GetCurrentProcess(),
- (DWORD64)addr, &unused, &info);
- int written = 0;
- out_buffer[0] = '\0';
- // FIXME: it might be useful to print out 'obj' or 'obj+offset' info too.
- if (got_fileline) {
- written += internal_snprintf(out_buffer + written, buffer_size - written,
- " %s %s:%d", symbol->Name,
- info.FileName, info.LineNumber);
- } else {
- written += internal_snprintf(out_buffer + written, buffer_size - written,
- " %s+0x%p", symbol->Name, offset);
- }
- return true;
-}
-
-// ---------------------- AsanLock ---------------- {{{1
-enum LockState {
- LOCK_UNINITIALIZED = 0,
- LOCK_READY = -1,
-};
-
-AsanLock::AsanLock(LinkerInitialized li) {
- // FIXME: see comments in AsanLock::Lock() for the details.
- CHECK(li == LINKER_INITIALIZED || owner_ == LOCK_UNINITIALIZED);
-
- CHECK(sizeof(CRITICAL_SECTION) <= sizeof(opaque_storage_));
- InitializeCriticalSection((LPCRITICAL_SECTION)opaque_storage_);
- owner_ = LOCK_READY;
-}
-
-void AsanLock::Lock() {
- if (owner_ == LOCK_UNINITIALIZED) {
- // FIXME: hm, global AsanLock objects are not initialized?!?
- // This might be a side effect of the clang+cl+link Frankenbuild...
- new(this) AsanLock((LinkerInitialized)(LINKER_INITIALIZED + 1));
-
- // FIXME: If it turns out the linker doesn't invoke our
- // constructors, we should probably manually Lock/Unlock all the global
- // locks while we're starting in one thread to avoid double-init races.
- }
- EnterCriticalSection((LPCRITICAL_SECTION)opaque_storage_);
- CHECK(owner_ == LOCK_READY);
- owner_ = GetThreadSelf();
-}
-
-void AsanLock::Unlock() {
- CHECK(owner_ == GetThreadSelf());
- owner_ = LOCK_READY;
- LeaveCriticalSection((LPCRITICAL_SECTION)opaque_storage_);
+ stack->size = cs_ret - offset;
+ for (uptr i = 0; i < stack->size; i++)
+ stack->trace[i] = (uptr)tmp[i + offset];
}
// ---------------------- TSD ---------------- {{{1
@@ -153,6 +75,10 @@ void AsanTSDSet(void *tsd) {
}
// ---------------------- Various stuff ---------------- {{{1
+void MaybeReexec() {
+ // No need to re-exec on Windows.
+}
+
void *AsanDoesNotSupportStaticLinkage() {
#if defined(_DEBUG)
#error Please build the runtime with a non-debug CRT: /MD or /MT
@@ -176,6 +102,58 @@ void AsanPlatformThreadInit() {
// Nothing here for now.
}
+void ReadContextStack(void *context, uptr *stack, uptr *ssize) {
+ UNIMPLEMENTED();
+}
+
} // namespace __asan
+// ---------------------- Interface ---------------- {{{1
+using namespace __asan; // NOLINT
+
+extern "C" {
+SANITIZER_INTERFACE_ATTRIBUTE NOINLINE
+bool __asan_symbolize(const void *addr, char *out_buffer, int buffer_size) {
+ BlockingMutexLock lock(&dbghelp_lock);
+ if (!dbghelp_initialized) {
+ SymSetOptions(SYMOPT_DEFERRED_LOADS |
+ SYMOPT_UNDNAME |
+ SYMOPT_LOAD_LINES);
+ CHECK(SymInitialize(GetCurrentProcess(), 0, TRUE));
+ // FIXME: We don't call SymCleanup() on exit yet - should we?
+ dbghelp_initialized = true;
+ }
+
+ // See http://msdn.microsoft.com/en-us/library/ms680578(VS.85).aspx
+ char buffer[sizeof(SYMBOL_INFO) + MAX_SYM_NAME * sizeof(CHAR)];
+ PSYMBOL_INFO symbol = (PSYMBOL_INFO)buffer;
+ symbol->SizeOfStruct = sizeof(SYMBOL_INFO);
+ symbol->MaxNameLen = MAX_SYM_NAME;
+ DWORD64 offset = 0;
+ BOOL got_objname = SymFromAddr(GetCurrentProcess(),
+ (DWORD64)addr, &offset, symbol);
+ if (!got_objname)
+ return false;
+
+ DWORD unused;
+ IMAGEHLP_LINE64 info;
+ info.SizeOfStruct = sizeof(IMAGEHLP_LINE64);
+ BOOL got_fileline = SymGetLineFromAddr64(GetCurrentProcess(),
+ (DWORD64)addr, &unused, &info);
+ int written = 0;
+ out_buffer[0] = '\0';
+ // FIXME: it might be useful to print out 'obj' or 'obj+offset' info too.
+ if (got_fileline) {
+ written += internal_snprintf(out_buffer + written, buffer_size - written,
+ " %s %s:%d", symbol->Name,
+ info.FileName, info.LineNumber);
+ } else {
+ written += internal_snprintf(out_buffer + written, buffer_size - written,
+ " %s+0x%p", symbol->Name, offset);
+ }
+ return true;
+}
+} // extern "C"
+
+
#endif // _WIN32
diff --git a/lib/asan/dynamic/Makefile.mk b/lib/asan/dynamic/Makefile.mk
new file mode 100644
index 000000000000..897844e7eed4
--- /dev/null
+++ b/lib/asan/dynamic/Makefile.mk
@@ -0,0 +1,25 @@
+#===- 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
new file mode 100644
index 000000000000..4f0f7bd2d5f8
--- /dev/null
+++ b/lib/asan/dynamic/asan_interceptors_dynamic.cc
@@ -0,0 +1,111 @@
+//===-- 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
new file mode 100644
index 000000000000..1609032d4670
--- /dev/null
+++ b/lib/asan/lit_tests/CMakeLists.txt
@@ -0,0 +1,32 @@
+set(ASAN_SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/..)
+set(ASAN_BINARY_DIR ${CMAKE_CURRENT_BINARY_DIR}/..)
+
+configure_lit_site_cfg(
+ ${CMAKE_CURRENT_SOURCE_DIR}/lit.site.cfg.in
+ ${CMAKE_CURRENT_BINARY_DIR}/lit.site.cfg
+ )
+
+configure_lit_site_cfg(
+ ${CMAKE_CURRENT_SOURCE_DIR}/Unit/lit.site.cfg.in
+ ${CMAKE_CURRENT_BINARY_DIR}/Unit/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
+ ${ASAN_RUNTIME_LIBRARIES}
+ )
+ set(ASAN_TEST_PARAMS
+ asan_site_config=${CMAKE_CURRENT_BINARY_DIR}/lit.site.cfg
+ )
+ if(LLVM_INCLUDE_TESTS)
+ list(APPEND ASAN_TEST_DEPS AsanUnitTests)
+ endif()
+ add_lit_testsuite(check-asan "Running the AddressSanitizer tests"
+ ${CMAKE_CURRENT_BINARY_DIR}
+ PARAMS ${ASAN_TEST_PARAMS}
+ DEPENDS ${ASAN_TEST_DEPS}
+ )
+ set_target_properties(check-asan PROPERTIES FOLDER "ASan tests")
+endif()
diff --git a/lib/asan/lit_tests/Helpers/blacklist-extra.cc b/lib/asan/lit_tests/Helpers/blacklist-extra.cc
new file mode 100644
index 000000000000..627115cdda2b
--- /dev/null
+++ b/lib/asan/lit_tests/Helpers/blacklist-extra.cc
@@ -0,0 +1,5 @@
+// This function is broken, but this file is blacklisted
+int externalBrokenFunction(int argc) {
+ char x[10] = {0};
+ return x[argc * 10]; // BOOM
+}
diff --git a/lib/asan/lit_tests/Helpers/initialization-blacklist-extra.cc b/lib/asan/lit_tests/Helpers/initialization-blacklist-extra.cc
new file mode 100644
index 000000000000..09aed2112d5e
--- /dev/null
+++ b/lib/asan/lit_tests/Helpers/initialization-blacklist-extra.cc
@@ -0,0 +1,15 @@
+int zero_init() { return 0; }
+int badGlobal = zero_init();
+int readBadGlobal() { return badGlobal; }
+
+namespace badNamespace {
+class BadClass {
+ public:
+ BadClass() { value = 0; }
+ int value;
+};
+// Global object with non-trivial constructor.
+BadClass bad_object;
+} // namespace badNamespace
+
+int accessBadObject() { return badNamespace::bad_object.value; }
diff --git a/lib/asan/lit_tests/Helpers/initialization-blacklist.txt b/lib/asan/lit_tests/Helpers/initialization-blacklist.txt
new file mode 100644
index 000000000000..c5f6610937f0
--- /dev/null
+++ b/lib/asan/lit_tests/Helpers/initialization-blacklist.txt
@@ -0,0 +1,2 @@
+global-init:*badGlobal*
+global-init-type:*badNamespace::BadClass*
diff --git a/lib/asan/lit_tests/Helpers/initialization-bug-extra.cc b/lib/asan/lit_tests/Helpers/initialization-bug-extra.cc
new file mode 100644
index 000000000000..3c4cb411defa
--- /dev/null
+++ b/lib/asan/lit_tests/Helpers/initialization-bug-extra.cc
@@ -0,0 +1,5 @@
+// This file simply declares a dynamically initialized var by the name of 'y'.
+int initY() {
+ return 5;
+}
+int y = initY();
diff --git a/lib/asan/lit_tests/Helpers/initialization-bug-extra2.cc b/lib/asan/lit_tests/Helpers/initialization-bug-extra2.cc
new file mode 100644
index 000000000000..a3d8f190e58b
--- /dev/null
+++ b/lib/asan/lit_tests/Helpers/initialization-bug-extra2.cc
@@ -0,0 +1,6 @@
+// 'z' is dynamically initialized global from different TU.
+extern int z;
+int __attribute__((noinline)) initY() {
+ return z + 1;
+}
+int y = initY();
diff --git a/lib/asan/lit_tests/Helpers/initialization-nobug-extra.cc b/lib/asan/lit_tests/Helpers/initialization-nobug-extra.cc
new file mode 100644
index 000000000000..490b3339054a
--- /dev/null
+++ b/lib/asan/lit_tests/Helpers/initialization-nobug-extra.cc
@@ -0,0 +1,9 @@
+// Linker initialized:
+int getAB();
+static int ab = getAB();
+// Function local statics:
+int countCalls();
+static int one = countCalls();
+// Constexpr:
+int getCoolestInteger();
+static int coolest_integer = getCoolestInteger();
diff --git a/lib/asan/lit_tests/Helpers/lit.local.cfg b/lib/asan/lit_tests/Helpers/lit.local.cfg
new file mode 100644
index 000000000000..2fc4d99456b0
--- /dev/null
+++ b/lib/asan/lit_tests/Helpers/lit.local.cfg
@@ -0,0 +1,3 @@
+# Sources in this directory are helper files for tests which test functionality
+# involving multiple translation units.
+config.suffixes = []
diff --git a/lib/asan/lit_tests/Linux/clone_test.cc b/lib/asan/lit_tests/Linux/clone_test.cc
new file mode 100644
index 000000000000..ca13b22425f2
--- /dev/null
+++ b/lib/asan/lit_tests/Linux/clone_test.cc
@@ -0,0 +1,48 @@
+// Regression test for:
+// http://code.google.com/p/address-sanitizer/issues/detail?id=37
+
+// RUN: %clangxx_asan -m64 -O0 %s -o %t && %t | FileCheck %s
+// RUN: %clangxx_asan -m64 -O1 %s -o %t && %t | FileCheck %s
+// RUN: %clangxx_asan -m64 -O2 %s -o %t && %t | FileCheck %s
+// RUN: %clangxx_asan -m64 -O3 %s -o %t && %t | FileCheck %s
+// RUN: %clangxx_asan -m32 -O0 %s -o %t && %t | FileCheck %s
+// RUN: %clangxx_asan -m32 -O1 %s -o %t && %t | FileCheck %s
+// RUN: %clangxx_asan -m32 -O2 %s -o %t && %t | FileCheck %s
+// RUN: %clangxx_asan -m32 -O3 %s -o %t && %t | FileCheck %s
+
+#include <stdio.h>
+#include <sched.h>
+#include <sys/syscall.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <unistd.h>
+
+int Child(void *arg) {
+ char x[32] = {0}; // Stack gets poisoned.
+ printf("Child: %p\n", x);
+ _exit(1); // NoReturn, stack will remain unpoisoned unless we do something.
+}
+
+int main(int argc, char **argv) {
+ const int kStackSize = 1 << 20;
+ char child_stack[kStackSize + 1];
+ char *sp = child_stack + kStackSize; // Stack grows down.
+ printf("Parent: %p\n", sp);
+ pid_t clone_pid = clone(Child, sp, CLONE_FILES | CLONE_VM, NULL, 0, 0, 0);
+ int status;
+ pid_t wait_result = waitpid(clone_pid, &status, __WCLONE);
+ if (wait_result < 0) {
+ perror("waitpid");
+ return 0;
+ }
+ if (wait_result == clone_pid && WIFEXITED(status)) {
+ // Make sure the child stack was indeed unpoisoned.
+ for (int i = 0; i < kStackSize; i++)
+ child_stack[i] = i;
+ int ret = child_stack[argc - 1];
+ printf("PASSED\n");
+ // CHECK: PASSED
+ return ret;
+ }
+ 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
new file mode 100644
index 000000000000..645fe1c85ed4
--- /dev/null
+++ b/lib/asan/lit_tests/Linux/initialization-bug-any-order.cc
@@ -0,0 +1,37 @@
+// 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.
+
+// RUN: %clangxx_asan -m64 -O0 %s %p/../Helpers/initialization-bug-extra.cc\
+// RUN: -fsanitize=init-order -o %t && %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: | %symbolize | FileCheck %s
+
+// Do not test with optimization -- the error may be optimized away.
+
+#include <cstdio>
+
+// 'y' is a dynamically initialized global residing in a different TU. This
+// dynamic initializer will read the value of 'y' before main starts. The
+// result is undefined behavior, which should be caught by initialization order
+// checking.
+extern int y;
+int __attribute__((noinline)) initX() {
+ return y + 1;
+ // CHECK: {{AddressSanitizer: initialization-order-fiasco}}
+ // CHECK: {{READ of size .* at 0x.* thread T0}}
+ // CHECK: {{#0 0x.* in .*initX.* .*initialization-bug-any-order.cc:}}[[@LINE-3]]
+ // CHECK: {{0x.* is located 0 bytes inside of global variable .*y.*}}
+}
+
+// This initializer begins our initialization order problems.
+static int x = initX();
+
+int main() {
+ // ASan should have caused an exit before main runs.
+ printf("PASS\n");
+ // CHECK-NOT: PASS
+ return 0;
+}
diff --git a/lib/asan/lit_tests/Linux/interception_failure_test.cc b/lib/asan/lit_tests/Linux/interception_failure_test.cc
new file mode 100644
index 000000000000..dfad909f528c
--- /dev/null
+++ b/lib/asan/lit_tests/Linux/interception_failure_test.cc
@@ -0,0 +1,26 @@
+// If user provides his own libc functions, ASan doesn't
+// intercept these functions.
+
+// RUN: %clangxx_asan -m64 -O0 %s -o %t && %t 2>&1 | FileCheck %s
+// RUN: %clangxx_asan -m64 -O1 %s -o %t && %t 2>&1 | FileCheck %s
+// RUN: %clangxx_asan -m64 -O2 %s -o %t && %t 2>&1 | FileCheck %s
+// RUN: %clangxx_asan -m64 -O3 %s -o %t && %t 2>&1 | FileCheck %s
+// RUN: %clangxx_asan -m32 -O0 %s -o %t && %t 2>&1 | FileCheck %s
+// 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
+#include <stdlib.h>
+#include <stdio.h>
+
+extern "C" long strtol(const char *nptr, char **endptr, int base) {
+ fprintf(stderr, "my_strtol_interceptor\n");
+ return 0;
+}
+
+int main() {
+ char *x = (char*)malloc(10 * sizeof(char));
+ free(x);
+ return (int)strtol(x, 0, 10);
+ // CHECK: my_strtol_interceptor
+ // CHECK-NOT: heap-use-after-free
+}
diff --git a/lib/asan/lit_tests/Linux/interception_malloc_test.cc b/lib/asan/lit_tests/Linux/interception_malloc_test.cc
new file mode 100644
index 000000000000..8f66788e9a82
--- /dev/null
+++ b/lib/asan/lit_tests/Linux/interception_malloc_test.cc
@@ -0,0 +1,27 @@
+// ASan interceptor can be accessed with __interceptor_ prefix.
+
+// RUN: %clangxx_asan -m64 -O0 %s -o %t && %t 2>&1 | FileCheck %s
+// RUN: %clangxx_asan -m64 -O1 %s -o %t && %t 2>&1 | FileCheck %s
+// RUN: %clangxx_asan -m64 -O2 %s -o %t && %t 2>&1 | FileCheck %s
+// RUN: %clangxx_asan -m64 -O3 %s -o %t && %t 2>&1 | FileCheck %s
+// RUN: %clangxx_asan -m32 -O0 %s -o %t && %t 2>&1 | FileCheck %s
+// 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
+#include <stdlib.h>
+#include <stdio.h>
+#include <unistd.h>
+
+extern "C" void *__interceptor_malloc(size_t size);
+extern "C" void *malloc(size_t size) {
+ write(2, "malloc call\n", sizeof("malloc call\n") - 1);
+ return __interceptor_malloc(size);
+}
+
+int main() {
+ char *x = (char*)malloc(10 * sizeof(char));
+ free(x);
+ return (int)strtol(x, 0, 10);
+ // CHECK: malloc call
+ // CHECK: heap-use-after-free
+}
diff --git a/lib/asan/lit_tests/Linux/interception_test.cc b/lib/asan/lit_tests/Linux/interception_test.cc
new file mode 100644
index 000000000000..94fb499f2f87
--- /dev/null
+++ b/lib/asan/lit_tests/Linux/interception_test.cc
@@ -0,0 +1,26 @@
+// ASan interceptor can be accessed with __interceptor_ prefix.
+
+// RUN: %clangxx_asan -m64 -O0 %s -o %t && %t 2>&1 | FileCheck %s
+// RUN: %clangxx_asan -m64 -O1 %s -o %t && %t 2>&1 | FileCheck %s
+// RUN: %clangxx_asan -m64 -O2 %s -o %t && %t 2>&1 | FileCheck %s
+// RUN: %clangxx_asan -m64 -O3 %s -o %t && %t 2>&1 | FileCheck %s
+// RUN: %clangxx_asan -m32 -O0 %s -o %t && %t 2>&1 | FileCheck %s
+// 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
+#include <stdlib.h>
+#include <stdio.h>
+
+extern "C" long __interceptor_strtol(const char *nptr, char **endptr, int base);
+extern "C" long strtol(const char *nptr, char **endptr, int base) {
+ fprintf(stderr, "my_strtol_interceptor\n");
+ return __interceptor_strtol(nptr, endptr, base);
+}
+
+int main() {
+ char *x = (char*)malloc(10 * sizeof(char));
+ free(x);
+ return (int)strtol(x, 0, 10);
+ // CHECK: my_strtol_interceptor
+ // CHECK: heap-use-after-free
+}
diff --git a/lib/asan/lit_tests/Linux/lit.local.cfg b/lib/asan/lit_tests/Linux/lit.local.cfg
new file mode 100644
index 000000000000..57271b8078a4
--- /dev/null
+++ b/lib/asan/lit_tests/Linux/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 ['Linux']:
+ config.unsupported = True
diff --git a/lib/asan/lit_tests/Linux/malloc-in-qsort.cc b/lib/asan/lit_tests/Linux/malloc-in-qsort.cc
new file mode 100644
index 000000000000..a3fa255b186d
--- /dev/null
+++ b/lib/asan/lit_tests/Linux/malloc-in-qsort.cc
@@ -0,0 +1,50 @@
+// RUN: %clangxx_asan -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
+#include <stdlib.h>
+#include <stdio.h>
+
+int *GlobalPtr;
+
+extern "C" {
+int QsortCallback(const void *a, const void *b) {
+ char *x = (char*)a;
+ char *y = (char*)b;
+ printf("Calling QsortCallback\n");
+ GlobalPtr = new int[10];
+ return (int)*x - (int)*y;
+}
+
+__attribute__((noinline))
+void MyQsort(char *a, size_t size) {
+ printf("Calling qsort\n");
+ qsort(a, size, sizeof(char), QsortCallback);
+ printf("Done\n"); // Avoid tail call.
+}
+} // extern "C"
+
+int main() {
+ char a[2] = {1, 2};
+ MyQsort(a, 2);
+ return GlobalPtr[10];
+}
+
+// Fast unwind: can not unwind through qsort.
+// FIXME: this test does not properly work with slow unwind yet.
+
+// CHECK-FAST: ERROR: AddressSanitizer: heap-buffer-overflow
+// CHECK-FAST: is located 0 bytes to the right
+// CHECK-FAST: #0{{.*}}operator new
+// CHECK-FAST-NEXT: #1{{.*}}QsortCallback
+// CHECK-FAST-NOT: MyQsort
+//
+// CHECK-SLOW: ERROR: AddressSanitizer: heap-buffer-overflow
+// CHECK-SLOW: is located 0 bytes to the right
+// CHECK-SLOW: #0{{.*}}operator new
+// CHECK-SLOW-NEXT: #1{{.*}}QsortCallback
+// CHECK-SLOW: #{{.*}}MyQsort
+// CHECK-SLOW-NEXT: #{{.*}}main
diff --git a/lib/asan/lit_tests/Linux/overflow-in-qsort.cc b/lib/asan/lit_tests/Linux/overflow-in-qsort.cc
new file mode 100644
index 000000000000..c298991a8348
--- /dev/null
+++ b/lib/asan/lit_tests/Linux/overflow-in-qsort.cc
@@ -0,0 +1,47 @@
+// RUN: %clangxx_asan -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
+#include <stdlib.h>
+#include <stdio.h>
+
+int global_array[10];
+volatile int one = 1;
+
+extern "C" {
+int QsortCallback(const void *a, const void *b) {
+ char *x = (char*)a;
+ char *y = (char*)b;
+ printf("Calling QsortCallback\n");
+ global_array[one * 10] = 0; // BOOM
+ return (int)*x - (int)*y;
+}
+
+__attribute__((noinline))
+void MyQsort(char *a, size_t size) {
+ printf("Calling qsort\n");
+ qsort(a, size, sizeof(char), QsortCallback);
+ printf("Done\n"); // Avoid tail call.
+}
+} // extern "C"
+
+int main() {
+ char a[2] = {1, 2};
+ MyQsort(a, 2);
+}
+
+// Fast unwind: can not unwind through qsort.
+
+// CHECK-FAST: ERROR: AddressSanitizer: global-buffer-overflow
+// CHECK-FAST: #0{{.*}} in QsortCallback
+// CHECK-FAST-NOT: MyQsort
+// CHECK-FAST: is located 0 bytes to the right of global variable 'global_array
+
+// CHECK-SLOW: ERROR: AddressSanitizer: global-buffer-overflow
+// CHECK-SLOW: #0{{.*}} in QsortCallback
+// CHECK-SLOW: #{{.*}} in MyQsort
+// CHECK-SLOW: #{{.*}} in main
+// CHECK-SLOW: is located 0 bytes to the right of global variable 'global_array
diff --git a/lib/asan/lit_tests/Linux/rlimit_mmap_test.cc b/lib/asan/lit_tests/Linux/rlimit_mmap_test.cc
new file mode 100644
index 000000000000..5026e24e424d
--- /dev/null
+++ b/lib/asan/lit_tests/Linux/rlimit_mmap_test.cc
@@ -0,0 +1,16 @@
+// Check that we properly report mmap failure.
+// RUN: %clangxx_asan %s -o %t && %t 2>&1 | FileCheck %s
+#include <stdlib.h>
+#include <assert.h>
+#include <sys/time.h>
+#include <sys/resource.h>
+
+static volatile void *x;
+
+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
+ return 0;
+}
diff --git a/lib/asan/lit_tests/Linux/swapcontext_test.cc b/lib/asan/lit_tests/Linux/swapcontext_test.cc
new file mode 100644
index 000000000000..0404b4f602bd
--- /dev/null
+++ b/lib/asan/lit_tests/Linux/swapcontext_test.cc
@@ -0,0 +1,66 @@
+// Check that ASan plays well with easy cases of makecontext/swapcontext.
+
+// RUN: %clangxx_asan -m64 -O0 %s -o %t && %t 2>&1 | FileCheck %s
+// RUN: %clangxx_asan -m64 -O1 %s -o %t && %t 2>&1 | FileCheck %s
+// RUN: %clangxx_asan -m64 -O2 %s -o %t && %t 2>&1 | FileCheck %s
+// RUN: %clangxx_asan -m64 -O3 %s -o %t && %t 2>&1 | FileCheck %s
+// RUN: %clangxx_asan -m32 -O0 %s -o %t && %t 2>&1 | FileCheck %s
+// 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
+
+#include <stdio.h>
+#include <ucontext.h>
+#include <unistd.h>
+
+ucontext_t orig_context;
+ucontext_t child_context;
+
+void Child(int mode) {
+ char x[32] = {0}; // Stack gets poisoned.
+ printf("Child: %p\n", x);
+ // (a) Do nothing, just return to parent function.
+ // (b) Jump into the original function. Stack remains poisoned unless we do
+ // something.
+ if (mode == 1) {
+ if (swapcontext(&child_context, &orig_context) < 0) {
+ perror("swapcontext");
+ _exit(0);
+ }
+ }
+}
+
+int Run(int arg, int mode) {
+ const int kStackSize = 1 << 20;
+ char child_stack[kStackSize + 1];
+ printf("Child stack: %p\n", child_stack);
+ // Setup child context.
+ getcontext(&child_context);
+ child_context.uc_stack.ss_sp = child_stack;
+ child_context.uc_stack.ss_size = kStackSize / 2;
+ if (mode == 0) {
+ child_context.uc_link = &orig_context;
+ }
+ makecontext(&child_context, (void (*)())Child, 1, mode);
+ if (swapcontext(&orig_context, &child_context) < 0) {
+ perror("swapcontext");
+ return 0;
+ }
+ // Touch childs's stack to make sure it's unpoisoned.
+ for (int i = 0; i < kStackSize; i++) {
+ child_stack[i] = i;
+ }
+ return child_stack[arg];
+}
+
+int main(int argc, char **argv) {
+ // CHECK: WARNING: ASan doesn't fully support makecontext/swapcontext
+ int ret = 0;
+ ret += Run(argc - 1, 0);
+ printf("Test1 passed\n");
+ // CHECK: Test1 passed
+ ret += Run(argc - 1, 1);
+ printf("Test2 passed\n");
+ // CHECK: Test2 passed
+ return ret;
+}
diff --git a/lib/asan/output_tests/dlclose-test-so.cc b/lib/asan/lit_tests/SharedLibs/dlclose-test-so.cc
index 73e00507358a..73e00507358a 100644
--- a/lib/asan/output_tests/dlclose-test-so.cc
+++ b/lib/asan/lit_tests/SharedLibs/dlclose-test-so.cc
diff --git a/lib/asan/lit_tests/SharedLibs/lit.local.cfg b/lib/asan/lit_tests/SharedLibs/lit.local.cfg
new file mode 100644
index 000000000000..b3677c17a0f2
--- /dev/null
+++ b/lib/asan/lit_tests/SharedLibs/lit.local.cfg
@@ -0,0 +1,4 @@
+# Sources in this directory are compiled as shared libraries and used by
+# tests in parent directory.
+
+config.suffixes = []
diff --git a/lib/asan/output_tests/shared-lib-test-so.cc b/lib/asan/lit_tests/SharedLibs/shared-lib-test-so.cc
index 686a24578082..686a24578082 100644
--- a/lib/asan/output_tests/shared-lib-test-so.cc
+++ b/lib/asan/lit_tests/SharedLibs/shared-lib-test-so.cc
diff --git a/lib/asan/lit_tests/Unit/lit.cfg b/lib/asan/lit_tests/Unit/lit.cfg
new file mode 100644
index 000000000000..243eb7fbeec0
--- /dev/null
+++ b/lib/asan/lit_tests/Unit/lit.cfg
@@ -0,0 +1,27 @@
+# -*- Python -*-
+
+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 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",
+ "lit.common.unit.cfg")
+lit.load_config(config, compiler_rt_lit_unit_cfg)
+
+# Setup config name.
+config.name = 'AddressSanitizer-Unit'
+
+# Setup test source and exec root. For unit tests, we define
+# it as build directory with ASan unit tests.
+asan_binary_dir = get_required_attr(config, "asan_binary_dir")
+config.test_exec_root = os.path.join(asan_binary_dir, "tests")
+config.test_source_root = config.test_exec_root
diff --git a/lib/asan/lit_tests/Unit/lit.site.cfg.in b/lib/asan/lit_tests/Unit/lit.site.cfg.in
new file mode 100644
index 000000000000..401c3a8cc2eb
--- /dev/null
+++ b/lib/asan/lit_tests/Unit/lit.site.cfg.in
@@ -0,0 +1,10 @@
+## Autogenerated by LLVM/Clang configuration.
+# Do not edit!
+
+config.target_triple = "@TARGET_TRIPLE@"
+config.llvm_src_root = "@LLVM_SOURCE_DIR@"
+config.build_type = "@CMAKE_BUILD_TYPE@"
+config.asan_binary_dir = "@ASAN_BINARY_DIR@"
+
+# 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/blacklist.cc b/lib/asan/lit_tests/blacklist.cc
new file mode 100644
index 000000000000..6cfc1500c503
--- /dev/null
+++ b/lib/asan/lit_tests/blacklist.cc
@@ -0,0 +1,44 @@
+// Test the blacklist functionality of ASan
+
+// RUN: echo "fun:*brokenFunction*" > %tmp
+// RUN: echo "global:*badGlobal*" >> %tmp
+// RUN: echo "src:*blacklist-extra.cc" >> %tmp
+// RUN: %clangxx_asan -fsanitize-blacklist=%tmp -m64 -O0 %s -o %t \
+// RUN: %p/Helpers/blacklist-extra.cc && %t 2>&1
+// RUN: %clangxx_asan -fsanitize-blacklist=%tmp -m64 -O1 %s -o %t \
+// RUN: %p/Helpers/blacklist-extra.cc && %t 2>&1
+// RUN: %clangxx_asan -fsanitize-blacklist=%tmp -m64 -O2 %s -o %t \
+// RUN: %p/Helpers/blacklist-extra.cc && %t 2>&1
+// RUN: %clangxx_asan -fsanitize-blacklist=%tmp -m64 -O3 %s -o %t \
+// RUN: %p/Helpers/blacklist-extra.cc && %t 2>&1
+// RUN: %clangxx_asan -fsanitize-blacklist=%tmp -m32 -O0 %s -o %t \
+// RUN: %p/Helpers/blacklist-extra.cc && %t 2>&1
+// RUN: %clangxx_asan -fsanitize-blacklist=%tmp -m32 -O1 %s -o %t \
+// RUN: %p/Helpers/blacklist-extra.cc && %t 2>&1
+// RUN: %clangxx_asan -fsanitize-blacklist=%tmp -m32 -O2 %s -o %t \
+// RUN: %p/Helpers/blacklist-extra.cc && %t 2>&1
+// RUN: %clangxx_asan -fsanitize-blacklist=%tmp -m32 -O3 %s -o %t \
+// RUN: %p/Helpers/blacklist-extra.cc && %t 2>&1
+
+// badGlobal is accessed improperly, but we blacklisted it.
+int badGlobal;
+int readBadGlobal() {
+ return (&badGlobal)[1];
+}
+
+// A function which is broken, but excluded in the blacklist.
+int brokenFunction(int argc) {
+ char x[10] = {0};
+ return x[argc * 10]; // BOOM
+}
+
+// This function is defined in Helpers/blacklist-extra.cc, a source file which
+// is blacklisted by name
+int externalBrokenFunction(int x);
+
+int main(int argc, char **argv) {
+ brokenFunction(argc);
+ int x = readBadGlobal();
+ externalBrokenFunction(argc);
+ return 0;
+}
diff --git a/lib/asan/lit_tests/deep_stack_uaf.cc b/lib/asan/lit_tests/deep_stack_uaf.cc
new file mode 100644
index 000000000000..7b32798fefcc
--- /dev/null
+++ b/lib/asan/lit_tests/deep_stack_uaf.cc
@@ -0,0 +1,36 @@
+// Check that we can store lots of stack frames if asked to.
+
+// RUN: %clangxx_asan -m64 -O0 %s -o %t 2>&1
+// RUN: ASAN_OPTIONS=malloc_context_size=120:redzone=512 %t 2>&1 | \
+// RUN: %symbolize | FileCheck %s
+
+// RUN: %clangxx_asan -m32 -O0 %s -o %t 2>&1
+// RUN: ASAN_OPTIONS=malloc_context_size=120:redzone=512 %t 2>&1 | \
+// RUN: %symbolize | FileCheck %s
+#include <stdlib.h>
+#include <stdio.h>
+
+template <int depth>
+struct DeepFree {
+ static void free(char *x) {
+ DeepFree<depth - 1>::free(x);
+ }
+};
+
+template<>
+struct DeepFree<0> {
+ static void free(char *x) {
+ ::free(x);
+ }
+};
+
+int main() {
+ char *x = (char*)malloc(10);
+ // deep_free(x);
+ DeepFree<200>::free(x);
+ return x[5];
+ // CHECK: {{.*ERROR: AddressSanitizer: heap-use-after-free on address}}
+ // CHECK: DeepFree<36>
+ // CHECK: DeepFree<98>
+ // CHECK: DeepFree<115>
+}
diff --git a/lib/asan/lit_tests/deep_tail_call.cc b/lib/asan/lit_tests/deep_tail_call.cc
new file mode 100644
index 000000000000..6aa15e81f6ec
--- /dev/null
+++ b/lib/asan/lit_tests/deep_tail_call.cc
@@ -0,0 +1,24 @@
+// 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
+
+// CHECK: AddressSanitizer: global-buffer-overflow
+int global[10];
+// CHECK: {{#0.*call4}}
+void __attribute__((noinline)) call4(int i) { global[i+10]++; }
+// CHECK: {{#1.*call3}}
+void __attribute__((noinline)) call3(int i) { call4(i); }
+// CHECK: {{#2.*call2}}
+void __attribute__((noinline)) call2(int i) { call3(i); }
+// CHECK: {{#3.*call1}}
+void __attribute__((noinline)) call1(int i) { call2(i); }
+// CHECK: {{#4.*main}}
+int main(int argc, char **argv) {
+ call1(argc);
+ return global[0];
+}
diff --git a/lib/asan/lit_tests/deep_thread_stack.cc b/lib/asan/lit_tests/deep_thread_stack.cc
new file mode 100644
index 000000000000..781508d61616
--- /dev/null
+++ b/lib/asan/lit_tests/deep_thread_stack.cc
@@ -0,0 +1,61 @@
+// 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 <pthread.h>
+
+int *x;
+
+void *AllocThread(void *arg) {
+ x = new int;
+ *x = 42;
+ return NULL;
+}
+
+void *FreeThread(void *arg) {
+ delete x;
+ return NULL;
+}
+
+void *AccessThread(void *arg) {
+ *x = 43; // BOOM
+ return NULL;
+}
+
+typedef void* (*callback_type)(void* arg);
+
+void *RunnerThread(void *function) {
+ pthread_t thread;
+ pthread_create(&thread, NULL, (callback_type)function, NULL);
+ pthread_join(thread, NULL);
+ return NULL;
+}
+
+void RunThread(callback_type function) {
+ pthread_t runner;
+ pthread_create(&runner, NULL, RunnerThread, (void*)function);
+ pthread_join(runner, NULL);
+}
+
+int main(int argc, char *argv[]) {
+ RunThread(AllocThread);
+ RunThread(FreeThread);
+ RunThread(AccessThread);
+ return (x != 0);
+}
+
+// CHECK: AddressSanitizer: heap-use-after-free
+// CHECK: WRITE of size 4 at 0x{{.*}} thread T[[ACCESS_THREAD:[0-9]+]]
+// CHECK: freed by thread T[[FREE_THREAD:[0-9]+]] here:
+// CHECK: previously allocated by thread T[[ALLOC_THREAD:[0-9]+]] here:
+// CHECK: Thread T[[ACCESS_THREAD]] created by T[[ACCESS_RUNNER:[0-9]+]] here:
+// CHECK: Thread T[[ACCESS_RUNNER]] created by T0 here:
+// CHECK: Thread T[[FREE_THREAD]] created by T[[FREE_RUNNER:[0-9]+]] here:
+// CHECK: Thread T[[FREE_RUNNER]] created by T0 here:
+// CHECK: Thread T[[ALLOC_THREAD]] created by T[[ALLOC_RUNNER:[0-9]+]] here:
+// CHECK: Thread T[[ALLOC_RUNNER]] created by T0 here:
diff --git a/lib/asan/output_tests/default_options.cc b/lib/asan/lit_tests/default_options.cc
index d6c70291e6ff..950a7d879194 100644
--- a/lib/asan/output_tests/default_options.cc
+++ b/lib/asan/lit_tests/default_options.cc
@@ -1,12 +1,15 @@
+// RUN: %clangxx_asan -O2 %s -o %t
+// RUN: %t 2>&1 | FileCheck %s
+
const char *kAsanDefaultOptions="verbosity=1 foo=bar";
extern "C"
__attribute__((no_address_safety_analysis))
const char *__asan_default_options() {
+ // CHECK: Using the defaults from __asan_default_options: {{.*}} foo=bar
return kAsanDefaultOptions;
}
int main() {
- // Check-Common: foo=bar
return 0;
}
diff --git a/lib/asan/output_tests/dlclose-test.cc b/lib/asan/lit_tests/dlclose-test.cc
index 16126eb9042f..229f508294bf 100644
--- a/lib/asan/output_tests/dlclose-test.cc
+++ b/lib/asan/lit_tests/dlclose-test.cc
@@ -1,14 +1,3 @@
-//===----------- dlclose-test.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.
-//
// Regression test for
// http://code.google.com/p/address-sanitizer/issues/detail?id=19
// Bug description:
@@ -19,7 +8,32 @@
// 5. application starts using this mmaped memory, but asan still thinks there
// are globals.
// 6. BOOM
-//===----------------------------------------------------------------------===//
+
+// 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
+// RUN: %clangxx_asan -m64 -O1 %p/SharedLibs/dlclose-test-so.cc \
+// RUN: -fPIC -shared -o %t-so.so
+// RUN: %clangxx_asan -m64 -O1 %s -o %t && %t 2>&1 | FileCheck %s
+// RUN: %clangxx_asan -m64 -O2 %p/SharedLibs/dlclose-test-so.cc \
+// RUN: -fPIC -shared -o %t-so.so
+// RUN: %clangxx_asan -m64 -O2 %s -o %t && %t 2>&1 | FileCheck %s
+// RUN: %clangxx_asan -m64 -O3 %p/SharedLibs/dlclose-test-so.cc \
+// RUN: -fPIC -shared -o %t-so.so
+// RUN: %clangxx_asan -m64 -O3 %s -o %t && %t 2>&1 | FileCheck %s
+// RUN: %clangxx_asan -m32 -O0 %p/SharedLibs/dlclose-test-so.cc \
+// RUN: -fPIC -shared -o %t-so.so
+// RUN: %clangxx_asan -m32 -O0 %s -o %t && %t 2>&1 | FileCheck %s
+// RUN: %clangxx_asan -m32 -O1 %p/SharedLibs/dlclose-test-so.cc \
+// RUN: -fPIC -shared -o %t-so.so
+// RUN: %clangxx_asan -m32 -O1 %s -o %t && %t 2>&1 | FileCheck %s
+// RUN: %clangxx_asan -m32 -O2 %p/SharedLibs/dlclose-test-so.cc \
+// RUN: -fPIC -shared -o %t-so.so
+// RUN: %clangxx_asan -m32 -O2 %s -o %t && %t 2>&1 | FileCheck %s
+// RUN: %clangxx_asan -m32 -O3 %p/SharedLibs/dlclose-test-so.cc \
+// RUN: -fPIC -shared -o %t-so.so
+// RUN: %clangxx_asan -m32 -O3 %s -o %t && %t 2>&1 | FileCheck %s
+
#include <assert.h>
#include <dlfcn.h>
#include <stdio.h>
@@ -69,6 +83,6 @@ int main(int argc, char *argv[]) {
}
addr[1] = 2; // BOOM (if the bug is not fixed).
printf("PASS\n");
- // Check-Common: PASS
+ // CHECK: PASS
return 0;
}
diff --git a/lib/asan/lit_tests/force_inline_opt0.cc b/lib/asan/lit_tests/force_inline_opt0.cc
new file mode 100644
index 000000000000..955ce38156fb
--- /dev/null
+++ b/lib/asan/lit_tests/force_inline_opt0.cc
@@ -0,0 +1,14 @@
+// This test checks that we are no instrumenting a memory access twice
+// (before and after inlining)
+// RUN: %clangxx_asan -m64 -O1 %s -o %t && %t
+// RUN: %clangxx_asan -m64 -O0 %s -o %t && %t
+__attribute__((always_inline))
+void foo(int *x) {
+ *x = 0;
+}
+
+int main() {
+ int x;
+ foo(&x);
+ return x;
+}
diff --git a/lib/asan/lit_tests/global-overflow.cc b/lib/asan/lit_tests/global-overflow.cc
new file mode 100644
index 000000000000..6a2f12e106fe
--- /dev/null
+++ b/lib/asan/lit_tests/global-overflow.cc
@@ -0,0 +1,25 @@
+// 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 <string.h>
+int main(int argc, char **argv) {
+ static char XXX[10];
+ static char YYY[10];
+ static char ZZZ[10];
+ memset(XXX, 0, 10);
+ memset(YYY, 0, 10);
+ memset(ZZZ, 0, 10);
+ int res = YYY[argc * 10]; // BOOOM
+ // CHECK: {{READ of size 1 at 0x.* thread T0}}
+ // CHECK: {{ #0 0x.* in _?main .*global-overflow.cc:}}[[@LINE-2]]
+ // CHECK: {{0x.* is located 0 bytes to the right of global variable}}
+ // CHECK: {{.*YYY.* of size 10}}
+ res += XXX[argc] + ZZZ[argc];
+ return res;
+}
diff --git a/lib/asan/lit_tests/heap-overflow.cc b/lib/asan/lit_tests/heap-overflow.cc
new file mode 100644
index 000000000000..2648ec7e5f1f
--- /dev/null
+++ b/lib/asan/lit_tests/heap-overflow.cc
@@ -0,0 +1,38 @@
+// 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
+
+#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 * 10]; // BOOOM
+ // CHECK: {{READ of size 1 at 0x.* thread T0}}
+ // CHECK: {{ #0 0x.* in _?main .*heap-overflow.cc:}}[[@LINE-2]]
+ // CHECK: {{0x.* is located 0 bytes to the right of 10-byte region}}
+ // CHECK: {{allocated by thread T0 here:}}
+
+ // 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}}
+ free(x);
+ return res;
+}
diff --git a/lib/asan/lit_tests/initialization-blacklist.cc b/lib/asan/lit_tests/initialization-blacklist.cc
new file mode 100644
index 000000000000..f8df24c68ea6
--- /dev/null
+++ b/lib/asan/lit_tests/initialization-blacklist.cc
@@ -0,0 +1,32 @@
+// Test for blacklist functionality of initialization-order checker.
+
+// RUN: %clangxx_asan -m64 -O0 %s %p/Helpers/initialization-blacklist-extra.cc\
+// RUN: -fsanitize-blacklist=%p/Helpers/initialization-blacklist.txt \
+// RUN: -fsanitize=init-order -o %t && %t 2>&1
+// RUN: %clangxx_asan -m64 -O1 %s %p/Helpers/initialization-blacklist-extra.cc\
+// RUN: -fsanitize-blacklist=%p/Helpers/initialization-blacklist.txt \
+// RUN: -fsanitize=init-order -o %t && %t 2>&1
+// RUN: %clangxx_asan -m64 -O2 %s %p/Helpers/initialization-blacklist-extra.cc\
+// RUN: -fsanitize-blacklist=%p/Helpers/initialization-blacklist.txt \
+// RUN: -fsanitize=init-order -o %t && %t 2>&1
+// RUN: %clangxx_asan -m32 -O0 %s %p/Helpers/initialization-blacklist-extra.cc\
+// RUN: -fsanitize-blacklist=%p/Helpers/initialization-blacklist.txt \
+// RUN: -fsanitize=init-order -o %t && %t 2>&1
+// RUN: %clangxx_asan -m32 -O1 %s %p/Helpers/initialization-blacklist-extra.cc\
+// RUN: -fsanitize-blacklist=%p/Helpers/initialization-blacklist.txt \
+// RUN: -fsanitize=init-order -o %t && %t 2>&1
+// RUN: %clangxx_asan -m32 -O2 %s %p/Helpers/initialization-blacklist-extra.cc\
+// RUN: -fsanitize-blacklist=%p/Helpers/initialization-blacklist.txt \
+// RUN: -fsanitize=init-order -o %t && %t 2>&1
+
+// Function is defined in another TU.
+int readBadGlobal();
+int x = readBadGlobal(); // init-order bug.
+
+// Function is defined in another TU.
+int accessBadObject();
+int y = accessBadObject(); // init-order bug.
+
+int main(int argc, char **argv) {
+ return argc + x + y - 1;
+}
diff --git a/lib/asan/lit_tests/initialization-bug.cc b/lib/asan/lit_tests/initialization-bug.cc
new file mode 100644
index 000000000000..8f4e33ef5a35
--- /dev/null
+++ b/lib/asan/lit_tests/initialization-bug.cc
@@ -0,0 +1,46 @@
+// 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: | %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: | %symbolize | FileCheck %s
+
+// Do not test with optimization -- the error may be optimized away.
+
+#include <cstdio>
+
+// The structure of the test is:
+// "x", "y", "z" are dynamically initialized globals.
+// Value of "x" depends on "y", value of "y" depends on "z".
+// "x" and "z" are defined in this TU, "y" is defined in another one.
+// Thus we shoud stably report initialization order fiasco independently of
+// the translation unit order.
+
+int initZ() {
+ return 5;
+}
+int z = initZ();
+
+// 'y' is a dynamically initialized global residing in a different TU. This
+// dynamic initializer will read the value of 'y' before main starts. The
+// result is undefined behavior, which should be caught by initialization order
+// checking.
+extern int y;
+int __attribute__((noinline)) initX() {
+ return y + 1;
+ // CHECK: {{AddressSanitizer: initialization-order-fiasco}}
+ // CHECK: {{READ of size .* at 0x.* thread T0}}
+ // CHECK: {{0x.* is located 0 bytes inside of global variable .*(y|z).*}}
+}
+
+// This initializer begins our initialization order problems.
+static int x = initX();
+
+int main() {
+ // ASan should have caused an exit before main runs.
+ printf("PASS\n");
+ // CHECK-NOT: PASS
+ return 0;
+}
diff --git a/lib/asan/lit_tests/initialization-nobug.cc b/lib/asan/lit_tests/initialization-nobug.cc
new file mode 100644
index 000000000000..1b8961606811
--- /dev/null
+++ b/lib/asan/lit_tests/initialization-nobug.cc
@@ -0,0 +1,67 @@
+// 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
+
+// Simple access:
+// Make sure that accessing a global in the same TU is safe
+
+bool condition = true;
+int initializeSameTU() {
+ return condition ? 0x2a : 052;
+}
+int sameTU = initializeSameTU();
+
+// Linker initialized:
+// Check that access to linker initialized globals originating from a different
+// TU's initializer is safe.
+
+int A = (1 << 1) + (1 << 3) + (1 << 5), B;
+int getAB() {
+ return A * B;
+}
+
+// Function local statics:
+// Check that access to function local statics originating from a different
+// TU's initializer is safe.
+
+int countCalls() {
+ static int calls;
+ 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:
+ 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/interface_symbols.c b/lib/asan/lit_tests/interface_symbols.c
new file mode 100644
index 000000000000..f3167f562922
--- /dev/null
+++ b/lib/asan/lit_tests/interface_symbols.c
@@ -0,0 +1,28 @@
+// 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: | 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: | 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: cat %t.interface | sort -u | diff %t.symbols -
+
+int main() { return 0; }
diff --git a/lib/asan/lit_tests/large_func_test.cc b/lib/asan/lit_tests/large_func_test.cc
new file mode 100644
index 000000000000..a74828811f74
--- /dev/null
+++ b/lib/asan/lit_tests/large_func_test.cc
@@ -0,0 +1,62 @@
+// 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
+
+#include <stdlib.h>
+__attribute__((noinline))
+static void LargeFunction(int *x, int zero) {
+ x[0]++;
+ x[1]++;
+ x[2]++;
+ x[3]++;
+ x[4]++;
+ x[5]++;
+ x[6]++;
+ x[7]++;
+ x[8]++;
+ x[9]++;
+
+ // 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
+ // atos incorrectly extracts the symbol name for the static functions on
+ // Darwin.
+ // CHECK-Linux: {{#0 0x.* in LargeFunction.*large_func_test.cc:}}[[@LINE-3]]
+ // CHECK-Darwin: {{#0 0x.* in .*LargeFunction.*large_func_test.cc}}:[[@LINE-4]]
+
+ x[10]++;
+ x[11]++;
+ x[12]++;
+ x[13]++;
+ x[14]++;
+ x[15]++;
+ x[16]++;
+ x[17]++;
+ x[18]++;
+ x[19]++;
+}
+
+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: {{allocated by thread T0 here:}}
+ // CHECK: {{ #0 0x.* in operator new.*}}
+ // CHECK: {{ #1 0x.* in _?main .*large_func_test.cc:}}[[@LINE-6]]
+ delete x;
+}
diff --git a/lib/asan/lit_tests/lit.cfg b/lib/asan/lit_tests/lit.cfg
new file mode 100644
index 000000000000..7875281b1f2f
--- /dev/null
+++ b/lib/asan/lit_tests/lit.cfg
@@ -0,0 +1,97 @@
+# -*- Python -*-
+
+import os
+
+# Setup config name.
+config.name = 'AddressSanitizer'
+
+# Setup source root.
+config.test_source_root = os.path.dirname(__file__)
+
+def DisplayNoConfigMessage():
+ lit.fatal("No site specific configuration available! " +
+ "Try running your test from the build tree or running " +
+ "make check-asan")
+
+# Figure out LLVM source root.
+llvm_src_root = getattr(config, 'llvm_src_root', None)
+if llvm_src_root is None:
+ # We probably haven't loaded the site-specific configuration: the user
+ # is likely trying to run a test file directly, and the site configuration
+ # wasn't created by the build system.
+ asan_site_cfg = lit.params.get('asan_site_config', None)
+ if (asan_site_cfg) and (os.path.exists(asan_site_cfg)):
+ lit.load_config(config, asan_site_cfg)
+ raise SystemExit
+
+ # Try to guess the location of site-specific configuration using llvm-config
+ # util that can point where the build tree is.
+ llvm_config = lit.util.which("llvm-config", config.environment["PATH"])
+ 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",
+ "lib", "asan", "lit_tests", "lit.site.cfg")
+ if (not asan_site_cfg) or (not os.path.exists(asan_site_cfg)):
+ DisplayNoConfigMessage()
+
+ lit.load_config(config, asan_site_cfg)
+ 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")
+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)
+lit.load_config(config, compiler_rt_lit_cfg)
+
+# Setup default compiler flags used with -fsanitize=address option.
+# FIXME: Review the set of required flags and check if it can be reduced.
+clang_asan_cxxflags = ("-ccc-cxx "
+ + "-fsanitize=address "
+ + "-mno-omit-leaf-frame-pointer "
+ + "-fno-omit-frame-pointer "
+ + "-fno-optimize-sibling-calls "
+ + "-g")
+config.substitutions.append( ("%clangxx_asan ", (" " + config.clang + " " +
+ clang_asan_cxxflags + " ")) )
+
+# Setup path to external LLVM symbolizer to run AddressSanitizer output tests.
+llvm_tools_dir = getattr(config, 'llvm_tools_dir', None)
+if llvm_tools_dir:
+ config.environment['LLVM_SYMBOLIZER_PATH'] = os.path.join(
+ llvm_tools_dir, "llvm-symbolizer")
+
+# Setup path to symbolizer script.
+# FIXME: Instead we should copy this script to the build tree and point
+# at it there.
+asan_source_dir = os.path.join(config.test_source_root, "..")
+symbolizer = os.path.join(asan_source_dir,
+ 'scripts', 'asan_symbolize.py')
+if not os.path.exists(symbolizer):
+ lit.fatal("Can't find symbolizer script on path %r" % symbolizer)
+# Define %symbolize substitution that filters output through
+# symbolizer and c++filt (for demangling).
+config.substitutions.append( ("%symbolize ", (" " + symbolizer +
+ " | c++filt " )))
+
+# Define CHECK-%os to check for OS-dependent output.
+config.substitutions.append( ('CHECK-%os', ("CHECK-" + config.host_os)))
+
+# Default test suffixes.
+config.suffixes = ['.c', '.cc', '.cpp']
+
+# AddressSanitizer tests are currently supported on Linux and Darwin only.
+if config.host_os not in ['Linux', 'Darwin']:
+ config.unsupported = True
diff --git a/lib/asan/lit_tests/lit.site.cfg.in b/lib/asan/lit_tests/lit.site.cfg.in
new file mode 100644
index 000000000000..cf439309c6ad
--- /dev/null
+++ b/lib/asan/lit_tests/lit.site.cfg.in
@@ -0,0 +1,20 @@
+## Autogenerated by LLVM/Clang configuration.
+# Do not edit!
+
+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.llvm_tools_dir = "@LLVM_TOOLS_DIR@"
+config.clang = "@LLVM_BINARY_DIR@/bin/clang"
+
+# LLVM tools dir can be passed in lit parameters, so try to
+# apply substitution.
+try:
+ config.llvm_tools_dir = config.llvm_tools_dir % 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/lit.cfg")
diff --git a/lib/asan/lit_tests/log-path_test.cc b/lib/asan/lit_tests/log-path_test.cc
new file mode 100644
index 000000000000..1072670fbff4
--- /dev/null
+++ b/lib/asan/lit_tests/log-path_test.cc
@@ -0,0 +1,39 @@
+// RUN: %clangxx_asan %s -o %t
+
+// Regular run.
+// RUN: not %t 2> %t.out
+// RUN: FileCheck %s --check-prefix=CHECK-ERROR < %t.out
+
+// Good log_path.
+// RUN: rm -f %t.log.*
+// RUN: ASAN_OPTIONS=log_path=%t.log not %t 2> %t.out
+// RUN: FileCheck %s --check-prefix=CHECK-ERROR < %t.log.*
+
+// Invalid log_path.
+// RUN: ASAN_OPTIONS=log_path=/INVALID not %t 2> %t.out
+// RUN: FileCheck %s --check-prefix=CHECK-INVALID < %t.out
+
+// Too long log_path.
+// RUN: ASAN_OPTIONS=log_path=`for((i=0;i<10000;i++)); do echo -n $i; done` \
+// RUN: not %t 2> %t.out
+// RUN: FileCheck %s --check-prefix=CHECK-LONG < %t.out
+
+// Run w/o errors should not produce any log.
+// RUN: rm -f %t.log.*
+// RUN: ASAN_OPTIONS=log_path=%t.log %t ARG ARG ARG
+// RUN: not cat %t.log.*
+
+
+#include <stdlib.h>
+#include <string.h>
+int main(int argc, char **argv) {
+ if (argc > 2) return 0;
+ char *x = (char*)malloc(10);
+ memset(x, 0, 10);
+ int res = x[argc * 10]; // BOOOM
+ free(x);
+ return res;
+}
+// CHECK-ERROR: ERROR: AddressSanitizer
+// CHECK-INVALID: ERROR: Can't open file: /INVALID
+// CHECK-LONG: ERROR: Path is too long: 01234
diff --git a/lib/asan/lit_tests/log_path_fork_test.cc b/lib/asan/lit_tests/log_path_fork_test.cc
new file mode 100644
index 000000000000..c6c1b49e994d
--- /dev/null
+++ b/lib/asan/lit_tests/log_path_fork_test.cc
@@ -0,0 +1,22 @@
+// RUN: %clangxx_asan %s -o %t
+// RUN: rm -f %t.log.*
+// Set verbosity to 1 so that the log files are opened prior to fork().
+// RUN: ASAN_OPTIONS="log_path=%t.log verbosity=1" not %t 2> %t.out
+// RUN: for f in %t.log.* ; do FileCheck %s < $f; done
+// RUN: [ `ls %t.log.* | wc -l` == 2 ]
+
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+int main(int argc, char **argv) {
+ void *x = malloc(10);
+ free(x);
+ if (fork() == -1) return 1;
+ // There are two processes at this point, thus there should be two distinct
+ // error logs.
+ free(x);
+ return 0;
+}
+
+// CHECK: ERROR: AddressSanitizer
diff --git a/lib/asan/lit_tests/malloc_delete_mismatch.cc b/lib/asan/lit_tests/malloc_delete_mismatch.cc
new file mode 100644
index 000000000000..f34b33a38fb3
--- /dev/null
+++ b/lib/asan/lit_tests/malloc_delete_mismatch.cc
@@ -0,0 +1,26 @@
+// Check that we detect malloc/delete mismatch only if the approptiate flag
+// is set.
+
+// RUN: %clangxx_asan -g %s -o %t 2>&1
+// RUN: ASAN_OPTIONS=alloc_dealloc_mismatch=1 %t 2>&1 | \
+// RUN: %symbolize | FileCheck %s
+
+// No error here.
+// RUN: ASAN_OPTIONS=alloc_dealloc_mismatch=0 %t
+#include <stdlib.h>
+
+static volatile char *x;
+
+int main() {
+ x = (char*)malloc(10);
+ x[0] = 0;
+ delete x;
+}
+// CHECK: ERROR: AddressSanitizer: alloc-dealloc-mismatch (malloc vs operator delete) on 0x
+// CHECK-NEXT: #0{{.*}}operator delete
+// CHECK: #{{.*}}main
+// CHECK: is located 0 bytes inside of 10-byte region
+// CHECK-NEXT: allocated by thread T0 here:
+// CHECK-NEXT: #0{{.*}}malloc
+// CHECK: #{{.*}}main
+// CHECK: HINT: {{.*}} you may set ASAN_OPTIONS=alloc_dealloc_mismatch=0
diff --git a/lib/asan/lit_tests/malloc_hook.cc b/lib/asan/lit_tests/malloc_hook.cc
new file mode 100644
index 000000000000..6435d105ee26
--- /dev/null
+++ b/lib/asan/lit_tests/malloc_hook.cc
@@ -0,0 +1,24 @@
+// RUN: %clangxx_asan -O2 %s -o %t
+// RUN: %t 2>&1 | FileCheck %s
+#include <stdlib.h>
+#include <unistd.h>
+
+extern "C" {
+// Note: avoid calling functions that allocate memory in malloc/free
+// to avoid infinite recursion.
+void __asan_malloc_hook(void *ptr, size_t sz) {
+ write(1, "MallocHook\n", sizeof("MallocHook\n"));
+}
+void __asan_free_hook(void *ptr) {
+ write(1, "FreeHook\n", sizeof("FreeHook\n"));
+}
+} // extern "C"
+
+int main() {
+ volatile int *x = new int;
+ // CHECK: MallocHook
+ *x = 0;
+ delete x;
+ // CHECK: FreeHook
+ return 0;
+}
diff --git a/lib/asan/lit_tests/memcmp_test.cc b/lib/asan/lit_tests/memcmp_test.cc
new file mode 100644
index 000000000000..ac3f7f32ea75
--- /dev/null
+++ b/lib/asan/lit_tests/memcmp_test.cc
@@ -0,0 +1,19 @@
+// 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 <string.h>
+int main(int argc, char **argv) {
+ char a1[] = {argc, 2, 3, 4};
+ char a2[] = {1, 2*argc, 3, 4};
+ int res = memcmp(a1, a2, 4 + argc); // BOOM
+ // CHECK: AddressSanitizer: stack-buffer-overflow
+ // CHECK: {{#0.*memcmp}}
+ // CHECK: {{#1.*main}}
+ return res;
+}
diff --git a/lib/asan/lit_tests/null_deref.cc b/lib/asan/lit_tests/null_deref.cc
new file mode 100644
index 000000000000..60a521d1c210
--- /dev/null
+++ b/lib/asan/lit_tests/null_deref.cc
@@ -0,0 +1,31 @@
+// 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
+
+__attribute__((noinline))
+static void NullDeref(int *ptr) {
+ // CHECK: ERROR: AddressSanitizer: SEGV on unknown address
+ // CHECK: {{0x0*00028 .*pc 0x.*}}
+ // CHECK: {{AddressSanitizer can not provide additional info.}}
+ ptr[10]++; // BOOM
+ // atos on Mac cannot extract the symbol name correctly.
+ // CHECK-Linux: {{ #0 0x.* in NullDeref.*null_deref.cc:}}[[@LINE-2]]
+ // CHECK-Darwin: {{ #0 0x.* in .*NullDeref.*null_deref.cc:}}[[@LINE-3]]
+}
+int main() {
+ NullDeref((int*)0);
+ // CHECK: {{ #1 0x.* in _?main.*null_deref.cc:}}[[@LINE-1]]
+}
diff --git a/lib/asan/lit_tests/on_error_callback.cc b/lib/asan/lit_tests/on_error_callback.cc
new file mode 100644
index 000000000000..bb94d9fb579b
--- /dev/null
+++ b/lib/asan/lit_tests/on_error_callback.cc
@@ -0,0 +1,16 @@
+// RUN: %clangxx_asan -O2 %s -o %t && %t 2>&1 | FileCheck %s
+
+#include <stdio.h>
+#include <stdlib.h>
+
+extern "C"
+void __asan_on_error() {
+ fprintf(stderr, "__asan_on_error called");
+}
+
+int main() {
+ char *x = (char*)malloc(10 * sizeof(char));
+ free(x);
+ return x[5];
+ // CHECK: __asan_on_error called
+}
diff --git a/lib/asan/lit_tests/sanity_check_pure_c.c b/lib/asan/lit_tests/sanity_check_pure_c.c
new file mode 100644
index 000000000000..3d830653e33e
--- /dev/null
+++ b/lib/asan/lit_tests/sanity_check_pure_c.c
@@ -0,0 +1,19 @@
+// Sanity checking a test in pure C.
+// RUN: %clang -g -fsanitize=address -O2 %s -o %t
+// RUN: %t 2>&1 | %symbolize | FileCheck %s
+
+// Sanity checking a test in pure C with -pie.
+// RUN: %clang -g -fsanitize=address -O2 %s -pie -o %t
+// RUN: %t 2>&1 | %symbolize | FileCheck %s
+
+#include <stdlib.h>
+int main() {
+ char *x = (char*)malloc(10 * sizeof(char));
+ free(x);
+ return x[5];
+ // CHECK: heap-use-after-free
+ // CHECK: free
+ // CHECK: main{{.*}}sanity_check_pure_c.c:[[@LINE-4]]
+ // CHECK: malloc
+ // CHECK: main{{.*}}sanity_check_pure_c.c:[[@LINE-7]]
+}
diff --git a/lib/asan/lit_tests/shared-lib-test.cc b/lib/asan/lit_tests/shared-lib-test.cc
new file mode 100644
index 000000000000..05bf3ecdf4f9
--- /dev/null
+++ b/lib/asan/lit_tests/shared-lib-test.cc
@@ -0,0 +1,54 @@
+// RUN: %clangxx_asan -m64 -O0 %p/SharedLibs/shared-lib-test-so.cc \
+// RUN: -fPIC -shared -o %t-so.so
+// RUN: %clangxx_asan -m64 -O0 %s -o %t && %t 2>&1 | %symbolize | FileCheck %s
+// RUN: %clangxx_asan -m64 -O1 %p/SharedLibs/shared-lib-test-so.cc \
+// RUN: -fPIC -shared -o %t-so.so
+// RUN: %clangxx_asan -m64 -O1 %s -o %t && %t 2>&1 | %symbolize | FileCheck %s
+// RUN: %clangxx_asan -m64 -O2 %p/SharedLibs/shared-lib-test-so.cc \
+// RUN: -fPIC -shared -o %t-so.so
+// RUN: %clangxx_asan -m64 -O2 %s -o %t && %t 2>&1 | %symbolize | FileCheck %s
+// RUN: %clangxx_asan -m64 -O3 %p/SharedLibs/shared-lib-test-so.cc \
+// RUN: -fPIC -shared -o %t-so.so
+// RUN: %clangxx_asan -m64 -O3 %s -o %t && %t 2>&1 | %symbolize | FileCheck %s
+// RUN: %clangxx_asan -m32 -O0 %p/SharedLibs/shared-lib-test-so.cc \
+// RUN: -fPIC -shared -o %t-so.so
+// RUN: %clangxx_asan -m32 -O0 %s -o %t && %t 2>&1 | %symbolize | FileCheck %s
+// RUN: %clangxx_asan -m32 -O1 %p/SharedLibs/shared-lib-test-so.cc \
+// RUN: -fPIC -shared -o %t-so.so
+// RUN: %clangxx_asan -m32 -O1 %s -o %t && %t 2>&1 | %symbolize | FileCheck %s
+// RUN: %clangxx_asan -m32 -O2 %p/SharedLibs/shared-lib-test-so.cc \
+// RUN: -fPIC -shared -o %t-so.so
+// RUN: %clangxx_asan -m32 -O2 %s -o %t && %t 2>&1 | %symbolize | FileCheck %s
+// RUN: %clangxx_asan -m32 -O3 %p/SharedLibs/shared-lib-test-so.cc \
+// RUN: -fPIC -shared -o %t-so.so
+// RUN: %clangxx_asan -m32 -O3 %s -o %t && %t 2>&1 | %symbolize | FileCheck %s
+
+#include <dlfcn.h>
+#include <stdio.h>
+#include <string.h>
+
+#include <string>
+
+using std::string;
+
+typedef void (fun_t)(int x);
+
+int main(int argc, char *argv[]) {
+ string path = string(argv[0]) + "-so.so";
+ printf("opening %s ... \n", path.c_str());
+ void *lib = dlopen(path.c_str(), RTLD_NOW);
+ if (!lib) {
+ printf("error in dlopen(): %s\n", dlerror());
+ return 1;
+ }
+ fun_t *inc = (fun_t*)dlsym(lib, "inc");
+ if (!inc) return 1;
+ printf("ok\n");
+ inc(1);
+ inc(-1); // BOOM
+ // CHECK: {{.*ERROR: AddressSanitizer: global-buffer-overflow}}
+ // CHECK: {{READ of size 4 at 0x.* thread T0}}
+ // CHECK: {{ #0 0x.*}}
+ // CHECK: {{ #1 0x.* in _?main .*shared-lib-test.cc:}}[[@LINE-4]]
+ return 0;
+}
diff --git a/lib/asan/lit_tests/sleep_before_dying.c b/lib/asan/lit_tests/sleep_before_dying.c
new file mode 100644
index 000000000000..df9eba276039
--- /dev/null
+++ b/lib/asan/lit_tests/sleep_before_dying.c
@@ -0,0 +1,10 @@
+// RUN: %clang -g -fsanitize=address -O2 %s -o %t
+// RUN: ASAN_OPTIONS="sleep_before_dying=1" %t 2>&1 | FileCheck %s
+
+#include <stdlib.h>
+int main() {
+ char *x = (char*)malloc(10 * sizeof(char));
+ free(x);
+ return x[5];
+ // CHECK: Sleeping for 1 second
+}
diff --git a/lib/asan/lit_tests/stack-frame-demangle.cc b/lib/asan/lit_tests/stack-frame-demangle.cc
new file mode 100644
index 000000000000..7f4d59fc5838
--- /dev/null
+++ b/lib/asan/lit_tests/stack-frame-demangle.cc
@@ -0,0 +1,24 @@
+// 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
+
+#include <string.h>
+
+namespace XXX {
+struct YYY {
+ static int ZZZ(int x) {
+ 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(.*)>}}
+ }
+};
+};
+
+int main(int argc, char **argv) {
+ int res = XXX::YYY::ZZZ(argc + 10);
+ return res;
+}
diff --git a/lib/asan/lit_tests/stack-overflow.cc b/lib/asan/lit_tests/stack-overflow.cc
new file mode 100644
index 000000000000..3deb1e91de6c
--- /dev/null
+++ b/lib/asan/lit_tests/stack-overflow.cc
@@ -0,0 +1,19 @@
+// 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 <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 .*stack-overflow.cc:}}[[@LINE-2]]
+ // CHECK: {{Address 0x.* is .* frame <main>}}
+ return res;
+}
diff --git a/lib/asan/lit_tests/stack-use-after-return.cc b/lib/asan/lit_tests/stack-use-after-return.cc
new file mode 100644
index 000000000000..f8d8a1a2ae39
--- /dev/null
+++ b/lib/asan/lit_tests/stack-use-after-return.cc
@@ -0,0 +1,45 @@
+// XFAIL: *
+// 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 -O1 %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 -m64 -O3 %s -o %t && \
+// RUN: %t 2>&1 | %symbolize | FileCheck %s
+// RUN: %clangxx_asan -fsanitize=use-after-return -m32 -O0 %s -o %t && \
+// RUN: %t 2>&1 | %symbolize | FileCheck %s
+// RUN: %clangxx_asan -fsanitize=use-after-return -m32 -O1 %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
+// RUN: %clangxx_asan -fsanitize=use-after-return -m32 -O3 %s -o %t && \
+// RUN: %t 2>&1 | %symbolize | FileCheck %s
+
+#include <stdio.h>
+
+__attribute__((noinline))
+char *Ident(char *x) {
+ fprintf(stderr, "1: %p\n", x);
+ return x;
+}
+
+__attribute__((noinline))
+char *Func1() {
+ char local;
+ return Ident(&local);
+}
+
+__attribute__((noinline))
+void Func2(char *x) {
+ fprintf(stderr, "2: %p\n", x);
+ *x = 1;
+ // CHECK: WRITE of size 1 {{.*}} thread T0
+ // CHECK: #0{{.*}}Func2{{.*}}stack-use-after-return.cc:[[@LINE-2]]
+ // CHECK: is located {{.*}} in frame <{{.*}}Func1{{.*}}> of T0's stack
+}
+
+int main(int argc, char **argv) {
+ Func2(Func1());
+ return 0;
+}
diff --git a/lib/asan/lit_tests/strip_path_prefix.c b/lib/asan/lit_tests/strip_path_prefix.c
new file mode 100644
index 000000000000..ef7bf98ab3c2
--- /dev/null
+++ b/lib/asan/lit_tests/strip_path_prefix.c
@@ -0,0 +1,12 @@
+// RUN: %clang -g -fsanitize=address -O2 %s -o %t
+// RUN: ASAN_OPTIONS="strip_path_prefix='/'" %t 2>&1 | FileCheck %s
+
+#include <stdlib.h>
+int main() {
+ char *x = (char*)malloc(10 * sizeof(char));
+ free(x);
+ return x[5];
+ // Check that paths in error report don't start with slash.
+ // CHECK: heap-use-after-free
+ // CHECK-NOT: #0 0x{{.*}} ({{[/].*}})
+}
diff --git a/lib/asan/lit_tests/strncpy-overflow.cc b/lib/asan/lit_tests/strncpy-overflow.cc
new file mode 100644
index 000000000000..18711843c4c8
--- /dev/null
+++ b/lib/asan/lit_tests/strncpy-overflow.cc
@@ -0,0 +1,40 @@
+// 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
+
+#include <string.h>
+#include <stdlib.h>
+int main(int argc, char **argv) {
+ char *hello = (char*)malloc(6);
+ 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-Linux: {{ #0 0x.* in .*strncpy}}
+ // CHECK-Darwin: {{ #0 0x.* in _?wrap_strncpy}}
+ // CHECK: {{ #1 0x.* in _?main .*strncpy-overflow.cc:}}[[@LINE-4]]
+ // CHECK: {{0x.* is located 0 bytes to the right of 9-byte region}}
+ // CHECK: {{allocated by thread T0 here:}}
+
+ // 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]]
+ return short_buffer[8];
+}
diff --git a/lib/asan/lit_tests/symbolize_callback.cc b/lib/asan/lit_tests/symbolize_callback.cc
new file mode 100644
index 000000000000..0691d501e24d
--- /dev/null
+++ b/lib/asan/lit_tests/symbolize_callback.cc
@@ -0,0 +1,17 @@
+// RUN: %clangxx_asan -O2 %s -o %t && %t 2>&1 | FileCheck %s
+
+#include <stdio.h>
+#include <stdlib.h>
+
+extern "C"
+bool __asan_symbolize(const void *pc, char *out_buffer, int out_size) {
+ snprintf(out_buffer, out_size, "MySymbolizer");
+ return true;
+}
+
+int main() {
+ char *x = (char*)malloc(10 * sizeof(char));
+ free(x);
+ return x[5];
+ // CHECK: MySymbolizer
+}
diff --git a/lib/asan/lit_tests/use-after-free.cc b/lib/asan/lit_tests/use-after-free.cc
new file mode 100644
index 000000000000..24d5a2a54807
--- /dev/null
+++ b/lib/asan/lit_tests/use-after-free.cc
@@ -0,0 +1,48 @@
+// 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
+
+#include <stdlib.h>
+int main() {
+ char *x = (char*)malloc(10 * sizeof(char));
+ free(x);
+ return x[5];
+ // CHECK: {{.*ERROR: AddressSanitizer: heap-use-after-free on address}}
+ // CHECK: {{0x.* at pc 0x.* bp 0x.* sp 0x.*}}
+ // CHECK: {{READ of size 1 at 0x.* thread T0}}
+ // CHECK: {{ #0 0x.* in _?main .*use-after-free.cc:22}}
+ // CHECK: {{0x.* is located 5 bytes inside of 10-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.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: {{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}}
+}
diff --git a/lib/asan/lit_tests/use-after-scope-inlined.cc b/lib/asan/lit_tests/use-after-scope-inlined.cc
new file mode 100644
index 000000000000..3d730de6ab35
--- /dev/null
+++ b/lib/asan/lit_tests/use-after-scope-inlined.cc
@@ -0,0 +1,29 @@
+// Test with "-O2" only to make sure inlining (leading to use-after-scope)
+// happens. "always_inline" is not enough, as Clang doesn't emit
+// llvm.lifetime intrinsics at -O0.
+//
+// RUN: %clangxx_asan -m64 -O2 -fsanitize=use-after-scope %s -o %t && \
+// RUN: %t 2>&1 | %symbolize | FileCheck %s
+// RUN: %clangxx_asan -m32 -O2 -fsanitize=use-after-scope %s -o %t && \
+// RUN: %t 2>&1 | %symbolize | FileCheck %s
+
+int *arr;
+
+__attribute__((always_inline))
+void inlined(int arg) {
+ int x[5];
+ for (int i = 0; i < arg; i++) x[i] = i;
+ arr = x;
+}
+
+int main(int argc, char *argv[]) {
+ inlined(argc);
+ return arr[argc - 1]; // BOOM
+ // CHECK: ERROR: AddressSanitizer: stack-use-after-scope
+ // 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: {{\[}}[[OFFSET]], {{.*}}) 'x.i'
+}
diff --git a/lib/asan/output_tests/clone_test.cc b/lib/asan/output_tests/clone_test.cc
deleted file mode 100644
index b18d2550bc7c..000000000000
--- a/lib/asan/output_tests/clone_test.cc
+++ /dev/null
@@ -1,34 +0,0 @@
-#ifdef __linux__
-#include <stdio.h>
-#include <sched.h>
-#include <sys/syscall.h>
-#include <sys/types.h>
-#include <sys/wait.h>
-#include <unistd.h>
-
-int Child(void *arg) {
- char x[32] = {0}; // Stack gets poisoned.
- printf("Child: %p\n", x);
- _exit(1); // NoReturn, stack will remain unpoisoned unless we do something.
-}
-
-int main(int argc, char **argv) {
- const int kStackSize = 1 << 20;
- char child_stack[kStackSize + 1];
- char *sp = child_stack + kStackSize; // Stack grows down.
- printf("Parent: %p\n", sp);
- pid_t clone_pid = clone(Child, sp, CLONE_FILES | CLONE_VM, NULL, 0, 0, 0);
- waitpid(clone_pid, NULL, 0);
- for (int i = 0; i < kStackSize; i++)
- child_stack[i] = i;
- int ret = child_stack[argc - 1];
- printf("PASSED\n");
- return ret;
-}
-#else // not __linux__
-#include <stdio.h>
-int main() {
- printf("PASSED\n");
- // Check-Common: PASSED
-}
-#endif
diff --git a/lib/asan/output_tests/deep_tail_call.cc b/lib/asan/output_tests/deep_tail_call.cc
deleted file mode 100644
index cb69e8925197..000000000000
--- a/lib/asan/output_tests/deep_tail_call.cc
+++ /dev/null
@@ -1,15 +0,0 @@
-// Check-Common: AddressSanitizer global-buffer-overflow
-int global[10];
-// Check-Common: {{#0.*call4}}
-void __attribute__((noinline)) call4(int i) { global[i+10]++; }
-// Check-Common: {{#1.*call3}}
-void __attribute__((noinline)) call3(int i) { call4(i); }
-// Check-Common: {{#2.*call2}}
-void __attribute__((noinline)) call2(int i) { call3(i); }
-// Check-Common: {{#3.*call1}}
-void __attribute__((noinline)) call1(int i) { call2(i); }
-// Check-Common: {{#4.*main}}
-int main(int argc, char **argv) {
- call1(argc);
- return global[0];
-}
diff --git a/lib/asan/output_tests/global-overflow.cc b/lib/asan/output_tests/global-overflow.cc
deleted file mode 100644
index a63eb733365f..000000000000
--- a/lib/asan/output_tests/global-overflow.cc
+++ /dev/null
@@ -1,16 +0,0 @@
-#include <string.h>
-int main(int argc, char **argv) {
- static char XXX[10];
- static char YYY[10];
- static char ZZZ[10];
- memset(XXX, 0, 10);
- memset(YYY, 0, 10);
- memset(ZZZ, 0, 10);
- int res = YYY[argc * 10]; // BOOOM
- // Check-Common: {{READ of size 1 at 0x.* thread T0}}
- // Check-Common: {{ #0 0x.* in main .*global-overflow.cc:9}}
- // Check-Common: {{0x.* is located 0 bytes to the right of global variable}}
- // Check-Common: {{.*YYY.* of size 10}}
- res += XXX[argc] + ZZZ[argc];
- return res;
-}
diff --git a/lib/asan/output_tests/heap-overflow.cc b/lib/asan/output_tests/heap-overflow.cc
deleted file mode 100644
index 534fbe00b355..000000000000
--- a/lib/asan/output_tests/heap-overflow.cc
+++ /dev/null
@@ -1,22 +0,0 @@
-#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 * 10]; // BOOOM
- free(x);
- return res;
-}
-
-// Check-Common: {{READ of size 1 at 0x.* thread T0}}
-// Check-Common: {{ #0 0x.* in main .*heap-overflow.cc:6}}
-// Check-Common: {{0x.* is located 0 bytes to the right of 10-byte region}}
-// Check-Common: {{allocated by thread T0 here:}}
-
-// Check-Linux: {{ #0 0x.* in .*malloc}}
-// Check-Linux: {{ #1 0x.* in main .*heap-overflow.cc:4}}
-
-// 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:4}}
diff --git a/lib/asan/output_tests/interception_failure_test-linux.cc b/lib/asan/output_tests/interception_failure_test-linux.cc
deleted file mode 100644
index 9e8b7536906b..000000000000
--- a/lib/asan/output_tests/interception_failure_test-linux.cc
+++ /dev/null
@@ -1,17 +0,0 @@
-#include <stdlib.h>
-#include <stdio.h>
-
-extern "C" long strtol(const char *nptr, char **endptr, int base) {
- fprintf(stderr, "my_strtol_interceptor\n");
- return 0;
-}
-
-int main() {
- char *x = (char*)malloc(10 * sizeof(char));
- free(x);
- return (int)strtol(x, 0, 10);
-}
-
-// Check-Common: my_strtol_interceptor
-// CHECK-NOT: heap-use-after-free
-
diff --git a/lib/asan/output_tests/interception_malloc_test-linux.cc b/lib/asan/output_tests/interception_malloc_test-linux.cc
deleted file mode 100644
index 4bb3bd66de33..000000000000
--- a/lib/asan/output_tests/interception_malloc_test-linux.cc
+++ /dev/null
@@ -1,19 +0,0 @@
-#include <stdlib.h>
-#include <stdio.h>
-#include <unistd.h>
-
-extern "C" void *__interceptor_malloc(size_t size);
-extern "C" void *malloc(size_t size) {
- write(2, "malloc call\n", sizeof("malloc call\n") - 1);
- return __interceptor_malloc(size);
-}
-
-int main() {
- char *x = (char*)malloc(10 * sizeof(char));
- free(x);
- return (int)strtol(x, 0, 10);
-}
-
-// Check-Common: malloc call
-// Check-Common: heap-use-after-free
-
diff --git a/lib/asan/output_tests/interception_test-linux.cc b/lib/asan/output_tests/interception_test-linux.cc
deleted file mode 100644
index 0523510465a1..000000000000
--- a/lib/asan/output_tests/interception_test-linux.cc
+++ /dev/null
@@ -1,18 +0,0 @@
-#include <stdlib.h>
-#include <stdio.h>
-
-extern "C" long __interceptor_strtol(const char *nptr, char **endptr, int base);
-extern "C" long strtol(const char *nptr, char **endptr, int base) {
- fprintf(stderr, "my_strtol_interceptor\n");
- return __interceptor_strtol(nptr, endptr, base);
-}
-
-int main() {
- char *x = (char*)malloc(10 * sizeof(char));
- free(x);
- return (int)strtol(x, 0, 10);
-}
-
-// Check-Common: my_strtol_interceptor
-// Check-Common: heap-use-after-free
-
diff --git a/lib/asan/output_tests/large_func_test.cc b/lib/asan/output_tests/large_func_test.cc
deleted file mode 100644
index 49751b39277a..000000000000
--- a/lib/asan/output_tests/large_func_test.cc
+++ /dev/null
@@ -1,48 +0,0 @@
-#include <stdlib.h>
-__attribute__((noinline))
-static void LargeFunction(int *x, int zero) {
- x[0]++;
- x[1]++;
- x[2]++;
- x[3]++;
- x[4]++;
- x[5]++;
- x[6]++;
- x[7]++;
- x[8]++;
- x[9]++;
-
- x[zero + 111]++; // we should report this exact line
-
- x[10]++;
- x[11]++;
- x[12]++;
- x[13]++;
- x[14]++;
- x[15]++;
- x[16]++;
- x[17]++;
- x[18]++;
- x[19]++;
-}
-
-int main(int argc, char **argv) {
- int *x = new int[100];
- LargeFunction(x, argc - 1);
- delete x;
-}
-
-// Check-Common: {{.*ERROR: AddressSanitizer heap-buffer-overflow on address}}
-// Check-Common: {{0x.* at pc 0x.* bp 0x.* sp 0x.*}}
-// Check-Common: {{READ of size 4 at 0x.* thread T0}}
-
-// atos incorrectly extracts the symbol name for the static functions on
-// Darwin.
-// Check-Linux: {{ #0 0x.* in LargeFunction.*large_func_test.cc:15}}
-// Check-Darwin: {{ #0 0x.* in .*LargeFunction.*large_func_test.cc:15}}
-
-// Check-Common: {{ #1 0x.* in main .*large_func_test.cc:31}}
-// Check-Common: {{0x.* is located 44 bytes to the right of 400-byte region}}
-// Check-Common: {{allocated by thread T0 here:}}
-// Check-Common: {{ #0 0x.* in operator new.*}}
-// Check-Common: {{ #1 0x.* in main .*large_func_test.cc:30}}
diff --git a/lib/asan/output_tests/memcmp_test.cc b/lib/asan/output_tests/memcmp_test.cc
deleted file mode 100644
index d0e5a43b4355..000000000000
--- a/lib/asan/output_tests/memcmp_test.cc
+++ /dev/null
@@ -1,10 +0,0 @@
-#include <string.h>
-int main(int argc, char **argv) {
- char a1[] = {argc, 2, 3, 4};
- char a2[] = {1, 2*argc, 3, 4};
-// Check-Common: AddressSanitizer stack-buffer-overflow
-// Check-Common: {{#0.*memcmp}}
-// Check-Common: {{#1.*main}}
- int res = memcmp(a1, a2, 4 + argc); // BOOM
- return res;
-}
diff --git a/lib/asan/output_tests/null_deref.cc b/lib/asan/output_tests/null_deref.cc
deleted file mode 100644
index c152a4202e33..000000000000
--- a/lib/asan/output_tests/null_deref.cc
+++ /dev/null
@@ -1,17 +0,0 @@
-__attribute__((noinline))
-static void NullDeref(int *ptr) {
- ptr[10]++;
-}
-int main() {
- NullDeref((int*)0);
-}
-
-// Check-Common: {{.*ERROR: AddressSanitizer crashed on unknown address}}
-// Check-Common: {{0x0*00028 .*pc 0x.*}}
-// Check-Common: {{AddressSanitizer can not provide additional info. ABORTING}}
-
-// atos on Mac cannot extract the symbol name correctly.
-// Check-Linux: {{ #0 0x.* in NullDeref.*null_deref.cc:3}}
-// Check-Darwin: {{ #0 0x.* in .*NullDeref.*null_deref.cc:3}}
-
-// Check-Common: {{ #1 0x.* in main.*null_deref.cc:6}}
diff --git a/lib/asan/output_tests/shared-lib-test.cc b/lib/asan/output_tests/shared-lib-test.cc
deleted file mode 100644
index 060fcde35f0d..000000000000
--- a/lib/asan/output_tests/shared-lib-test.cc
+++ /dev/null
@@ -1,42 +0,0 @@
-//===----------- shared-lib-test.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.
-//
-//===----------------------------------------------------------------------===//
-#include <dlfcn.h>
-#include <stdio.h>
-#include <string.h>
-
-#include <string>
-
-using std::string;
-
-typedef void (fun_t)(int x);
-
-int main(int argc, char *argv[]) {
- string path = string(argv[0]) + "-so.so";
- printf("opening %s ... \n", path.c_str());
- void *lib = dlopen(path.c_str(), RTLD_NOW);
- if (!lib) {
- printf("error in dlopen(): %s\n", dlerror());
- return 1;
- }
- fun_t *inc = (fun_t*)dlsym(lib, "inc");
- if (!inc) return 1;
- printf("ok\n");
- inc(1);
- inc(-1); // BOOM
- return 0;
-}
-
-// Check-Common: {{.*ERROR: AddressSanitizer global-buffer-overflow}}
-// Check-Common: {{READ of size 4 at 0x.* thread T0}}
-// Check-Common: {{ #0 0x.*}}
-// Check-Common: {{ #1 0x.* in main .*shared-lib-test.cc:35}}
diff --git a/lib/asan/output_tests/stack-overflow.cc b/lib/asan/output_tests/stack-overflow.cc
deleted file mode 100644
index 35fa8a66c34c..000000000000
--- a/lib/asan/output_tests/stack-overflow.cc
+++ /dev/null
@@ -1,11 +0,0 @@
-#include <string.h>
-int main(int argc, char **argv) {
- char x[10];
- memset(x, 0, 10);
- int res = x[argc * 10]; // BOOOM
- return res;
-}
-
-// Check-Common: {{READ of size 1 at 0x.* thread T0}}
-// Check-Common: {{ #0 0x.* in main .*stack-overflow.cc:5}}
-// Check-Common: {{Address 0x.* is .* frame <main>}}
diff --git a/lib/asan/output_tests/stack-use-after-return.cc.disabled b/lib/asan/output_tests/stack-use-after-return.cc.disabled
deleted file mode 100644
index f49715737430..000000000000
--- a/lib/asan/output_tests/stack-use-after-return.cc.disabled
+++ /dev/null
@@ -1,27 +0,0 @@
-#include <stdio.h>
-
-__attribute__((noinline))
-char *Ident(char *x) {
- fprintf(stderr, "1: %p\n", x);
- return x;
-}
-
-__attribute__((noinline))
-char *Func1() {
- char local;
- return Ident(&local);
-}
-
-__attribute__((noinline))
-void Func2(char *x) {
- fprintf(stderr, "2: %p\n", x);
- *x = 1;
- // Check-Common: {{WRITE of size 1 .* thread T0}}
- // Check-Common: {{ #0.*Func2.*stack-use-after-return.cc:18}}
- // Check-Common: {{is located in frame <.*Func1.*> of T0's stack}}
-}
-
-int main(int argc, char **argv) {
- Func2(Func1());
- return 0;
-}
diff --git a/lib/asan/output_tests/strncpy-overflow.cc b/lib/asan/output_tests/strncpy-overflow.cc
deleted file mode 100644
index 66d5810b7040..000000000000
--- a/lib/asan/output_tests/strncpy-overflow.cc
+++ /dev/null
@@ -1,24 +0,0 @@
-#include <string.h>
-#include <stdlib.h>
-int main(int argc, char **argv) {
- char *hello = (char*)malloc(6);
- strcpy(hello, "hello");
- char *short_buffer = (char*)malloc(9);
- strncpy(short_buffer, hello, 10); // BOOM
- return short_buffer[8];
-}
-
-// Check-Common: {{WRITE of size 1 at 0x.* thread T0}}
-// Check-Linux: {{ #0 0x.* in .*strncpy}}
-// Check-Darwin: {{ #0 0x.* in wrap_strncpy}}
-// Check-Common: {{ #1 0x.* in main .*strncpy-overflow.cc:7}}
-// Check-Common: {{0x.* is located 0 bytes to the right of 9-byte region}}
-// Check-Common: {{allocated by thread T0 here:}}
-
-// Check-Linux: {{ #0 0x.* in .*malloc}}
-// Check-Linux: {{ #1 0x.* in main .*strncpy-overflow.cc:6}}
-
-// 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:6}}
diff --git a/lib/asan/output_tests/test_output.sh b/lib/asan/output_tests/test_output.sh
deleted file mode 100755
index 6510043396e4..000000000000
--- a/lib/asan/output_tests/test_output.sh
+++ /dev/null
@@ -1,79 +0,0 @@
-#!/bin/bash
-
-set -e # fail on any error
-
-OS=`uname`
-CXX=$1
-CC=$2
-FILE_CHECK=$3
-CXXFLAGS="-mno-omit-leaf-frame-pointer -fno-omit-frame-pointer -fno-optimize-sibling-calls -g"
-SYMBOLIZER=../scripts/asan_symbolize.py
-TMP_ASAN_REPORT=asan_report.tmp
-
-run_program() {
- ./$1 2>&1 | $SYMBOLIZER 2> /dev/null | c++filt > $TMP_ASAN_REPORT
-}
-
-# check_program exe_file source_file check_prefixf
-check_program() {
- run_program $1
- $FILE_CHECK $2 --check-prefix=$3 < $TMP_ASAN_REPORT
- rm -f $TMP_ASAN_REPORT
-}
-
-C_TEST=use-after-free
-echo "Sanity checking a test in pure C"
-$CC -g -faddress-sanitizer -O2 $C_TEST.c
-check_program a.out $C_TEST.c CHECK
-rm ./a.out
-
-echo "Sanity checking a test in pure C with -pie"
-$CC -g -faddress-sanitizer -O2 $C_TEST.c -pie
-check_program a.out $C_TEST.c CHECK
-rm ./a.out
-
-echo "Testing sleep_before_dying"
-$CC -g -faddress-sanitizer -O2 $C_TEST.c
-export ASAN_OPTIONS="sleep_before_dying=1"
-check_program a.out $C_TEST.c CHECKSLEEP
-export ASAN_OPTIONS=""
-rm ./a.out
-
-# FIXME: some tests do not need to be ran for all the combinations of arch
-# and optimization mode.
-for t in *.cc; do
- for b in 32 64; do
- for O in 0 1 2 3; do
- c=`basename $t .cc`
- if [[ "$c" == *"-so" ]]; then
- continue
- fi
- if [[ "$c" == *"-linux" ]]; then
- if [[ "$OS" != "Linux" ]]; then
- continue
- fi
- fi
- c_so=$c-so
- exe=$c.$b.O$O
- so=$c.$b.O$O-so.so
- echo testing $exe
- build_command="$CXX $CXXFLAGS -m$b -faddress-sanitizer -O$O $c.cc -o $exe"
- [ "$DEBUG" == "1" ] && echo $build_command
- $build_command
- [ -e "$c_so.cc" ] && $CXX $CXXFLAGS -m$b -faddress-sanitizer -O$O $c_so.cc -fPIC -shared -o $so
- run_program $exe
- # Check common expected lines for OS.
- $FILE_CHECK $c.cc --check-prefix="Check-Common" < $TMP_ASAN_REPORT
- # Check OS-specific lines.
- if [ `grep -c "Check-$OS" $c.cc` -gt 0 ]
- then
- $FILE_CHECK $c.cc --check-prefix="Check-$OS" < $TMP_ASAN_REPORT
- fi
- rm ./$exe
- rm ./$TMP_ASAN_REPORT
- [ -e "$so" ] && rm ./$so
- done
- done
-done
-
-exit 0
diff --git a/lib/asan/output_tests/use-after-free.c b/lib/asan/output_tests/use-after-free.c
deleted file mode 100644
index 801d3f68a466..000000000000
--- a/lib/asan/output_tests/use-after-free.c
+++ /dev/null
@@ -1,9 +0,0 @@
-#include <stdlib.h>
-int main() {
- char *x = (char*)malloc(10 * sizeof(char));
- free(x);
- return x[5];
-}
-
-// CHECK: heap-use-after-free
-// CHECKSLEEP: Sleeping for 1 second
diff --git a/lib/asan/output_tests/use-after-free.cc b/lib/asan/output_tests/use-after-free.cc
deleted file mode 100644
index c3e9dbe6db75..000000000000
--- a/lib/asan/output_tests/use-after-free.cc
+++ /dev/null
@@ -1,31 +0,0 @@
-#include <stdlib.h>
-int main() {
- char *x = (char*)malloc(10 * sizeof(char));
- free(x);
- return x[5];
-}
-
-// Check-Common: {{.*ERROR: AddressSanitizer heap-use-after-free on address}}
-// Check-Common: {{0x.* at pc 0x.* bp 0x.* sp 0x.*}}
-// Check-Common: {{READ of size 1 at 0x.* thread T0}}
-// Check-Common: {{ #0 0x.* in main .*use-after-free.cc:5}}
-// Check-Common: {{0x.* is located 5 bytes inside of 10-byte region .0x.*,0x.*}}
-// Check-Common: {{freed by thread T0 here:}}
-
-// Check-Linux: {{ #0 0x.* in .*free}}
-// Check-Linux: {{ #1 0x.* in main .*use-after-free.cc:4}}
-
-// Check-Darwin: {{ #0 0x.* in .*mz_free.*}}
-// We override free() on Darwin, thus no malloc_zone_free
-// Check-Darwin: {{ #1 0x.* in wrap_free}}
-// Check-Darwin: {{ #2 0x.* in main .*use-after-free.cc:4}}
-
-// Check-Common: {{previously allocated by thread T0 here:}}
-
-// Check-Linux: {{ #0 0x.* in .*malloc}}
-// Check-Linux: {{ #1 0x.* in main .*use-after-free.cc:3}}
-
-// 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:3}}
diff --git a/lib/asan/scripts/asan_symbolize.py b/lib/asan/scripts/asan_symbolize.py
index e4897d0c7649..7b30bb55914e 100755
--- a/lib/asan/scripts/asan_symbolize.py
+++ b/lib/asan/scripts/asan_symbolize.py
@@ -7,121 +7,350 @@
# License. See LICENSE.TXT for details.
#
#===------------------------------------------------------------------------===#
+import bisect
import os
import re
-import sys
-import string
import subprocess
+import sys
-pipes = {}
+llvm_symbolizer = None
+symbolizers = {}
filetypes = {}
-DEBUG=False
+vmaddrs = {}
+DEBUG = False
+
+# FIXME: merge the code that calls fix_filename().
def fix_filename(file_name):
for path_to_cut in sys.argv[1:]:
- file_name = re.sub(".*" + path_to_cut, "", file_name)
- file_name = re.sub(".*asan_[a-z_]*.cc:[0-9]*", "_asan_rtl_", file_name)
- file_name = re.sub(".*crtstuff.c:0", "???:0", file_name)
+ file_name = re.sub('.*' + path_to_cut, '', file_name)
+ file_name = re.sub('.*asan_[a-z_]*.cc:[0-9]*', '_asan_rtl_', file_name)
+ file_name = re.sub('.*crtstuff.c:0', '???:0', file_name)
return file_name
-# TODO(glider): need some refactoring here
-def symbolize_addr2line(line):
- #0 0x7f6e35cf2e45 (/blah/foo.so+0x11fe45)
- match = re.match('^( *#([0-9]+) *0x[0-9a-f]+) *\((.*)\+(0x[0-9a-f]+)\)', line)
- if match:
- frameno = match.group(2)
- binary = match.group(3)
- addr = match.group(4)
- if not pipes.has_key(binary):
- pipes[binary] = subprocess.Popen(["addr2line", "-f", "-e", binary],
- stdin=subprocess.PIPE, stdout=subprocess.PIPE)
- p = pipes[binary]
+class Symbolizer(object):
+ def __init__(self):
+ pass
+
+ def symbolize(self, addr, binary, offset):
+ """Symbolize the given address (pair of binary and offset).
+
+ Overriden in subclasses.
+ Args:
+ addr: virtual address of an instruction.
+ binary: path to executable/shared object containing this instruction.
+ offset: instruction offset in the @binary.
+ Returns:
+ list of strings (one string for each inlined frame) describing
+ the code locations for this instruction (that is, function name, file
+ name, line and column numbers).
+ """
+ return None
+
+
+class LLVMSymbolizer(Symbolizer):
+ def __init__(self, symbolizer_path):
+ super(LLVMSymbolizer, self).__init__()
+ self.symbolizer_path = symbolizer_path
+ self.pipe = self.open_llvm_symbolizer()
+
+ def open_llvm_symbolizer(self):
+ if not os.path.exists(self.symbolizer_path):
+ return None
+ cmd = [self.symbolizer_path,
+ '--use-symbol-table=true',
+ '--demangle=false',
+ '--functions=true',
+ '--inlining=true']
+ if DEBUG:
+ print ' '.join(cmd)
+ return subprocess.Popen(cmd, stdin=subprocess.PIPE,
+ stdout=subprocess.PIPE)
+
+ def symbolize(self, addr, binary, offset):
+ """Overrides Symbolizer.symbolize."""
+ if not self.pipe:
+ return None
+ result = []
try:
- print >>p.stdin, addr
- function_name = p.stdout.readline().rstrip()
- file_name = p.stdout.readline().rstrip()
- except:
- function_name = ""
- file_name = ""
- file_name = fix_filename(file_name)
+ symbolizer_input = '%s %s' % (binary, offset)
+ if DEBUG:
+ print symbolizer_input
+ print >> self.pipe.stdin, symbolizer_input
+ while True:
+ function_name = self.pipe.stdout.readline().rstrip()
+ if not function_name:
+ break
+ file_name = self.pipe.stdout.readline().rstrip()
+ file_name = fix_filename(file_name)
+ if (not function_name.startswith('??') and
+ not file_name.startswith('??')):
+ # Append only valid frames.
+ result.append('%s in %s %s' % (addr, function_name,
+ file_name))
+ except Exception:
+ result = []
+ if not result:
+ result = None
+ return result
- print match.group(1), "in", function_name, file_name
- else:
- print line.rstrip()
-
-
-def get_macho_filetype(binary):
- if not filetypes.has_key(binary):
- otool_pipe = subprocess.Popen(["otool", "-Vh", binary],
- stdin=subprocess.PIPE, stdout=subprocess.PIPE)
- otool_line = "".join(otool_pipe.stdout.readlines())
- for t in ["DYLIB", "EXECUTE"]:
- if t in otool_line:
- filetypes[binary] = t
- otool_pipe.stdin.close()
- return filetypes[binary]
-
-
-def symbolize_atos(line):
- #0 0x7f6e35cf2e45 (/blah/foo.so+0x11fe45)
- match = re.match('^( *#([0-9]+) *)(0x[0-9a-f]+) *\((.*)\+(0x[0-9a-f]+)\)', line)
- if match:
- #print line
- prefix = match.group(1)
- frameno = match.group(2)
- orig_addr = match.group(3)
- binary = match.group(4)
- offset = match.group(5)
- addr = orig_addr
- load_addr = hex(int(orig_addr, 16) - int(offset, 16))
- filetype = get_macho_filetype(binary)
-
- if not pipes.has_key(binary):
- # Guess which arch we're running. 10 = len("0x") + 8 hex digits.
- if len(addr) > 10:
- arch = "x86_64"
- else:
- arch = "i386"
- if filetype == "DYLIB":
- load_addr = "0x0"
+def LLVMSymbolizerFactory(system):
+ symbolizer_path = os.getenv('LLVM_SYMBOLIZER_PATH')
+ if not symbolizer_path:
+ # Assume llvm-symbolizer is in PATH.
+ symbolizer_path = 'llvm-symbolizer'
+ return LLVMSymbolizer(symbolizer_path)
+
+
+class Addr2LineSymbolizer(Symbolizer):
+ def __init__(self, binary):
+ super(Addr2LineSymbolizer, self).__init__()
+ self.binary = binary
+ self.pipe = self.open_addr2line()
+
+ def open_addr2line(self):
+ cmd = ['addr2line', '-f', '-e', self.binary]
if DEBUG:
- print "atos -o %s -arch %s -l %s" % (binary, arch, load_addr)
- cmd = ["atos", "-o", binary, "-arch", arch, "-l", load_addr]
- pipes[binary] = subprocess.Popen(cmd,
- stdin=subprocess.PIPE,
- stdout=subprocess.PIPE,
- stderr=subprocess.PIPE)
- p = pipes[binary]
- if filetype == "DYLIB":
- print >>p.stdin, "%s" % offset
+ print ' '.join(cmd)
+ return subprocess.Popen(cmd,
+ stdin=subprocess.PIPE, stdout=subprocess.PIPE)
+
+ def symbolize(self, addr, binary, offset):
+ """Overrides Symbolizer.symbolize."""
+ if self.binary != binary:
+ return None
+ try:
+ print >> self.pipe.stdin, offset
+ function_name = self.pipe.stdout.readline().rstrip()
+ file_name = self.pipe.stdout.readline().rstrip()
+ except Exception:
+ function_name = ''
+ file_name = ''
+ file_name = fix_filename(file_name)
+ return ['%s in %s %s' % (addr, function_name, file_name)]
+
+
+class DarwinSymbolizer(Symbolizer):
+ def __init__(self, addr, binary):
+ super(DarwinSymbolizer, self).__init__()
+ self.binary = binary
+ # Guess which arch we're running. 10 = len('0x') + 8 hex digits.
+ if len(addr) > 10:
+ self.arch = 'x86_64'
else:
- print >>p.stdin, "%s" % addr
- # TODO(glider): it's more efficient to make a batch atos run for each binary.
- p.stdin.close()
- atos_line = p.stdout.readline().rstrip()
+ self.arch = 'i386'
+ self.vmaddr = None
+ self.pipe = None
+
+ def write_addr_to_pipe(self, offset):
+ print >> self.pipe.stdin, '0x%x' % int(offset, 16)
+
+ def open_atos(self):
+ if DEBUG:
+ print 'atos -o %s -arch %s' % (self.binary, self.arch)
+ cmdline = ['atos', '-o', self.binary, '-arch', self.arch]
+ self.pipe = subprocess.Popen(cmdline,
+ stdin=subprocess.PIPE,
+ stdout=subprocess.PIPE,
+ stderr=subprocess.PIPE)
+
+ def symbolize(self, addr, binary, offset):
+ """Overrides Symbolizer.symbolize."""
+ if self.binary != binary:
+ return None
+ self.open_atos()
+ self.write_addr_to_pipe(offset)
+ self.pipe.stdin.close()
+ atos_line = self.pipe.stdout.readline().rstrip()
# A well-formed atos response looks like this:
# foo(type1, type2) (in object.name) (filename.cc:80)
match = re.match('^(.*) \(in (.*)\) \((.*:\d*)\)$', atos_line)
- #print "atos_line: ", atos_line
+ if DEBUG:
+ print 'atos_line: ', atos_line
if match:
function_name = match.group(1)
- function_name = re.sub("\(.*?\)", "", function_name)
+ function_name = re.sub('\(.*?\)', '', function_name)
file_name = fix_filename(match.group(3))
- print "%s%s in %s %s" % (prefix, addr, function_name, file_name)
+ return ['%s in %s %s' % (addr, function_name, file_name)]
+ else:
+ return ['%s in %s' % (addr, atos_line)]
+
+
+# Chain several symbolizers so that if one symbolizer fails, we fall back
+# to the next symbolizer in chain.
+class ChainSymbolizer(Symbolizer):
+ def __init__(self, symbolizer_list):
+ super(ChainSymbolizer, self).__init__()
+ self.symbolizer_list = symbolizer_list
+
+ def symbolize(self, addr, binary, offset):
+ """Overrides Symbolizer.symbolize."""
+ for symbolizer in self.symbolizer_list:
+ if symbolizer:
+ result = symbolizer.symbolize(addr, binary, offset)
+ if result:
+ return result
+ return None
+
+ def append_symbolizer(self, symbolizer):
+ self.symbolizer_list.append(symbolizer)
+
+
+def BreakpadSymbolizerFactory(binary):
+ suffix = os.getenv('BREAKPAD_SUFFIX')
+ if suffix:
+ filename = binary + suffix
+ if os.access(filename, os.F_OK):
+ return BreakpadSymbolizer(filename)
+ return None
+
+
+def SystemSymbolizerFactory(system, addr, binary):
+ if system == 'Darwin':
+ return DarwinSymbolizer(addr, binary)
+ elif system == 'Linux':
+ return Addr2LineSymbolizer(binary)
+
+
+class BreakpadSymbolizer(Symbolizer):
+ def __init__(self, filename):
+ super(BreakpadSymbolizer, self).__init__()
+ self.filename = filename
+ lines = file(filename).readlines()
+ self.files = []
+ self.symbols = {}
+ self.address_list = []
+ self.addresses = {}
+ # MODULE mac x86_64 A7001116478B33F18FF9BEDE9F615F190 t
+ fragments = lines[0].rstrip().split()
+ self.arch = fragments[2]
+ self.debug_id = fragments[3]
+ self.binary = ' '.join(fragments[4:])
+ self.parse_lines(lines[1:])
+
+ def parse_lines(self, lines):
+ cur_function_addr = ''
+ for line in lines:
+ fragments = line.split()
+ if fragments[0] == 'FILE':
+ assert int(fragments[1]) == len(self.files)
+ self.files.append(' '.join(fragments[2:]))
+ elif fragments[0] == 'PUBLIC':
+ self.symbols[int(fragments[1], 16)] = ' '.join(fragments[3:])
+ elif fragments[0] in ['CFI', 'STACK']:
+ pass
+ elif fragments[0] == 'FUNC':
+ cur_function_addr = int(fragments[1], 16)
+ if not cur_function_addr in self.symbols.keys():
+ self.symbols[cur_function_addr] = ' '.join(fragments[4:])
+ else:
+ # Line starting with an address.
+ addr = int(fragments[0], 16)
+ self.address_list.append(addr)
+ # Tuple of symbol address, size, line, file number.
+ self.addresses[addr] = (cur_function_addr,
+ int(fragments[1], 16),
+ int(fragments[2]),
+ int(fragments[3]))
+ self.address_list.sort()
+
+ def get_sym_file_line(self, addr):
+ key = None
+ if addr in self.addresses.keys():
+ key = addr
else:
- print "%s%s in %s" % (prefix, addr, atos_line)
- del pipes[binary]
- else:
- print line.rstrip()
-
-system = os.uname()[0]
-if system in ['Linux', 'Darwin']:
- for line in sys.stdin:
- if system == 'Linux':
- symbolize_addr2line(line)
- elif system == 'Darwin':
- symbolize_atos(line)
-else:
- print 'Unknown system: ', system
+ index = bisect.bisect_left(self.address_list, addr)
+ if index == 0:
+ return None
+ else:
+ key = self.address_list[index - 1]
+ sym_id, size, line_no, file_no = self.addresses[key]
+ symbol = self.symbols[sym_id]
+ filename = self.files[file_no]
+ if addr < key + size:
+ return symbol, filename, line_no
+ else:
+ return None
+
+ def symbolize(self, addr, binary, offset):
+ if self.binary != binary:
+ return None
+ res = self.get_sym_file_line(int(offset, 16))
+ if res:
+ function_name, file_name, line_no = res
+ result = ['%s in %s %s:%d' % (
+ addr, function_name, file_name, line_no)]
+ print result
+ return result
+ else:
+ return None
+
+
+class SymbolizationLoop(object):
+ def __init__(self, binary_name_filter=None):
+ # Used by clients who may want to supply a different binary name.
+ # E.g. in Chrome several binaries may share a single .dSYM.
+ self.binary_name_filter = binary_name_filter
+ self.system = os.uname()[0]
+ if self.system in ['Linux', 'Darwin']:
+ self.llvm_symbolizer = LLVMSymbolizerFactory(self.system)
+ else:
+ raise Exception('Unknown system')
+
+ def symbolize_address(self, addr, binary, offset):
+ # Use the chain of symbolizers:
+ # Breakpad symbolizer -> LLVM symbolizer -> addr2line/atos
+ # (fall back to next symbolizer if the previous one fails).
+ if not binary in symbolizers:
+ symbolizers[binary] = ChainSymbolizer(
+ [BreakpadSymbolizerFactory(binary), self.llvm_symbolizer])
+ result = symbolizers[binary].symbolize(addr, binary, offset)
+ if result is None:
+ # Initialize system symbolizer only if other symbolizers failed.
+ symbolizers[binary].append_symbolizer(
+ SystemSymbolizerFactory(self.system, addr, binary))
+ result = symbolizers[binary].symbolize(addr, binary, offset)
+ # The system symbolizer must produce some result.
+ assert result
+ return result
+
+ def print_symbolized_lines(self, symbolized_lines):
+ if not symbolized_lines:
+ print self.current_line
+ else:
+ for symbolized_frame in symbolized_lines:
+ print ' #' + str(self.frame_no) + ' ' + symbolized_frame.rstrip()
+ self.frame_no += 1
+
+ def process_stdin(self):
+ self.frame_no = 0
+ for line in sys.stdin:
+ self.current_line = line.rstrip()
+ #0 0x7f6e35cf2e45 (/blah/foo.so+0x11fe45)
+ stack_trace_line_format = (
+ '^( *#([0-9]+) *)(0x[0-9a-f]+) *\((.*)\+(0x[0-9a-f]+)\)')
+ match = re.match(stack_trace_line_format, line)
+ if not match:
+ print self.current_line
+ continue
+ if DEBUG:
+ print line
+ _, frameno_str, addr, binary, offset = match.groups()
+ if frameno_str == '0':
+ # Assume that frame #0 is the first frame of new stack trace.
+ self.frame_no = 0
+ original_binary = binary
+ if self.binary_name_filter:
+ binary = self.binary_name_filter(binary)
+ symbolized_line = self.symbolize_address(addr, binary, offset)
+ if not symbolized_line:
+ if original_binary != binary:
+ symbolized_line = self.symbolize_address(addr, binary, offset)
+ self.print_symbolized_lines(symbolized_line)
+
+
+if __name__ == '__main__':
+ loop = SymbolizationLoop()
+ loop.process_stdin()
diff --git a/lib/asan/tests/CMakeLists.txt b/lib/asan/tests/CMakeLists.txt
index d409d50b995e..272950bc5450 100644
--- a/lib/asan/tests/CMakeLists.txt
+++ b/lib/asan/tests/CMakeLists.txt
@@ -10,109 +10,178 @@
# instrumentation against the just-built runtime library.
include(CheckCXXCompilerFlag)
+include(CompilerRTCompile)
include_directories(..)
include_directories(../..)
+set(ASAN_UNITTEST_HEADERS
+ asan_mac_test.h
+ asan_test_config.h
+ asan_test_utils.h)
+
set(ASAN_UNITTEST_COMMON_CFLAGS
+ ${COMPILER_RT_GTEST_INCLUDE_CFLAGS}
+ -I${COMPILER_RT_SOURCE_DIR}/include
+ -I${COMPILER_RT_SOURCE_DIR}/lib
+ -I${COMPILER_RT_SOURCE_DIR}/lib/asan
-Wall
-Wno-format
- -fvisibility=hidden
+ -Werror
+ -g
+ -O2
)
-# Support 64-bit and 32-bit builds.
-if(LLVM_BUILD_32_BITS)
- list(APPEND ASAN_UNITTEST_COMMON_CFLAGS -m32)
+
+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.
+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
+ )
else()
- list(APPEND ASAN_UNITTEST_COMMON_CFLAGS -m64)
+ list(APPEND ASAN_UNITTEST_COMMON_CFLAGS
+ -DASAN_HAS_BLACKLIST=1
+ -DASAN_HAS_EXCEPTIONS=1
+ -DASAN_NEEDS_SEGV=1
+ -DASAN_UAR=0
+ )
endif()
-set(ASAN_GTEST_INCLUDE_CFLAGS
- -I${LLVM_MAIN_SRC_DIR}/utils/unittest/googletest/include
- -I${LLVM_MAIN_SRC_DIR}/include
- -I${LLVM_BINARY_DIR}/include
- -D__STDC_CONSTANT_MACROS
- -D__STDC_LIMIT_MACROS
-)
+set(ASAN_LINK_FLAGS)
+if(ANDROID)
+ # On Android, we link with ASan runtime manually
+ list(APPEND ASAN_LINK_FLAGS -pie)
+else()
+ # On other platforms, we depend on Clang driver behavior,
+ # passing -fsanitize=address flag.
+ list(APPEND ASAN_LINK_FLAGS -fsanitize=address)
+endif()
+
+# Unit tests on Mac depend on Foundation.
+if(APPLE)
+ list(APPEND ASAN_LINK_FLAGS -framework Foundation)
+endif()
+# Unit tests require libstdc++.
+list(APPEND ASAN_LINK_FLAGS -lstdc++)
+
+set(ASAN_BLACKLIST_FILE "${CMAKE_CURRENT_SOURCE_DIR}/asan_test.ignore")
set(ASAN_UNITTEST_INSTRUMENTED_CFLAGS
${ASAN_UNITTEST_COMMON_CFLAGS}
- ${ASAN_GTEST_INCLUDE_CFLAGS}
- -faddress-sanitizer
- -O2
- -g
- -mllvm "-asan-blacklist=${CMAKE_CURRENT_SOURCE_DIR}/asan_test.ignore"
- -DASAN_HAS_BLACKLIST=1
- -DASAN_HAS_EXCEPTIONS=1
- -DASAN_NEEDS_SEGV=1
- -DASAN_UAR=0
+ -fsanitize=address
+ -mllvm "-asan-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
)
-add_custom_target(AsanTests)
-set_target_properties(AsanTests PROPERTIES FOLDER "ASan tests")
-function(add_asan_test testname)
- add_unittest(AsanTests ${testname} ${ARGN})
- if(LLVM_BUILD_32_BITS)
- target_link_libraries(${testname} clang_rt.asan-i386)
- else()
- target_link_libraries(${testname} clang_rt.asan-x86_64)
- endif()
- if (APPLE)
- # Darwin-specific linker flags.
- set_property(TARGET ${testname} APPEND PROPERTY
- LINK_FLAGS "-framework Foundation")
- elseif (UNIX)
- # Linux-specific linker flags.
- set_property(TARGET ${testname} APPEND PROPERTY
- LINK_FLAGS "-lpthread -ldl -export-dynamic")
- endif()
- set(add_compile_flags "")
- get_property(compile_flags TARGET ${testname} PROPERTY COMPILE_FLAGS)
- foreach(arg ${ASAN_UNITTEST_COMMON_CFLAGS})
- set(add_compile_flags "${add_compile_flags} ${arg}")
- endforeach(arg ${ASAN_UNITTEST_COMMON_CFLAGS})
- set_property(TARGET ${testname} PROPERTY COMPILE_FLAGS
- "${compile_flags} ${add_compile_flags}")
-endfunction()
-
-set(ASAN_NOINST_TEST_SOURCES
- asan_noinst_test.cc
- asan_break_optimization.cc
-)
+# Compile source for the given architecture, using compiler
+# options in ${ARGN}, and add it to the object list.
+macro(asan_compile obj_list source arch)
+ get_filename_component(basename ${source} NAME)
+ set(output_obj "${basename}.${arch}.o")
+ get_target_flags_for_arch(${arch} TARGET_CFLAGS)
+ clang_compile(${output_obj} ${source}
+ CFLAGS ${ARGN} ${TARGET_CFLAGS}
+ DEPS gtest ${ASAN_RUNTIME_LIBRARIES}
+ ${ASAN_UNITTEST_HEADERS}
+ ${ASAN_BLACKLIST_FILE})
+ list(APPEND ${obj_list} ${output_obj})
+endmacro()
-set(ASAN_INST_TEST_OBJECTS)
-
-# We only support building instrumented tests when we're not cross compiling
-# and targeting a unix-like system where we can predict viable compilation and
-# linking strategies.
-if("${CMAKE_HOST_SYSTEM}" STREQUAL "${CMAKE_SYSTEM}" AND UNIX)
-
- # This function is a custom routine to manage manually compiling source files
- # for unit tests with the just-built Clang binary, using the ASan
- # instrumentation, and linking them into a test executable.
- function(add_asan_compile_command source extra_cflags)
- set(output_obj "${source}.asan.o")
- add_custom_command(
- OUTPUT ${output_obj}
- COMMAND clang
- ${ASAN_UNITTEST_INSTRUMENTED_CFLAGS}
- ${extra_cflags}
- -c -o "${output_obj}"
- ${CMAKE_CURRENT_SOURCE_DIR}/${source}
- MAIN_DEPENDENCY ${source}
- DEPENDS clang clang_rt.asan-i386 clang_rt.asan-x86_64 ${ARGN}
- )
- endfunction()
-
- add_asan_compile_command(asan_globals_test.cc "")
- add_asan_compile_command(asan_test.cc "")
- list(APPEND ASAN_INST_TEST_OBJECTS asan_globals_test.cc.asan.o
- asan_test.cc.asan.o)
+# Link ASan unit test for a given architecture from a set
+# of objects in ${ARGN}.
+macro(add_asan_test test_suite test_name arch)
+ get_target_flags_for_arch(${arch} TARGET_LINK_FLAGS)
+ add_compiler_rt_test(${test_suite} ${test_name}
+ OBJECTS ${ARGN}
+ DEPS ${ASAN_RUNTIME_LIBRARIES} ${ARGN}
+ LINK_FLAGS ${ASAN_LINK_FLAGS}
+ ${TARGET_LINK_FLAGS})
+endmacro()
+
+# Main AddressSanitizer unit tests.
+add_custom_target(AsanUnitTests)
+set_target_properties(AsanUnitTests PROPERTIES FOLDER "ASan unit tests")
+# ASan benchmarks (not actively used now).
+add_custom_target(AsanBenchmarks)
+set_target_properties(AsanBenchmarks PROPERTIES FOLDER "Asan benchmarks")
+
+# Adds ASan unit tests and benchmarks for architecture.
+macro(add_asan_tests_for_arch arch)
+ # Build gtest instrumented with ASan.
+ set(ASAN_INST_GTEST)
+ asan_compile(ASAN_INST_GTEST ${COMPILER_RT_GTEST_SOURCE} ${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})
if (APPLE)
- add_asan_compile_command(asan_mac_test.mm "-ObjC")
- list(APPEND ASAN_INST_TEST_OBJECTS asan_mac_test.mm.asan.o)
+ asan_compile(ASAN_INST_TEST_OBJECTS asan_mac_test.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})
+ # Link everything together.
+ add_asan_test(AsanUnitTests "Asan-${arch}-Test" ${arch}
+ ${ASAN_NOINST_TEST_OBJECTS}
+ ${ASAN_INST_TEST_OBJECTS} ${ASAN_INST_GTEST})
+
+ # Instrumented benchmarks.
+ set(ASAN_BENCHMARKS_OBJECTS)
+ asan_compile(ASAN_BENCHMARKS_OBJECTS asan_benchmarks_test.cc ${arch}
+ ${ASAN_UNITTEST_INSTRUMENTED_CFLAGS})
+ # Link benchmarks.
+ add_asan_test(AsanBenchmarks "Asan-${arch}-Benchmark" ${arch}
+ ${ASAN_BENCHMARKS_OBJECTS} ${ASAN_INST_GTEST})
+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()
endif()
-add_asan_test(AsanTest ${ASAN_NOINST_TEST_SOURCES}
- ${ASAN_INST_TEST_OBJECTS})
+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
+ ${ASAN_INST_TEST_SOURCES} ${COMPILER_RT_GTEST_SOURCE})
+ set_target_compile_flags(asan_inst_test ${ASAN_UNITTEST_INSTRUMENTED_CFLAGS})
+ add_executable(AsanTest
+ $<TARGET_OBJECTS:asan_noinst_test>
+ $<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})
+ set_target_link_flags(AsanTest ${ASAN_LINK_FLAGS})
+ target_link_libraries(AsanTest clang_rt.asan-arm-android)
+ # Add unit test to test suite.
+ add_dependencies(AsanUnitTests AsanTest)
+endif()
diff --git a/lib/asan/tests/asan_benchmarks_test.cc b/lib/asan/tests/asan_benchmarks_test.cc
index a142fd23e1b8..fc522de475fa 100644
--- a/lib/asan/tests/asan_benchmarks_test.cc
+++ b/lib/asan/tests/asan_benchmarks_test.cc
@@ -12,7 +12,6 @@
// Some benchmarks for the instrumented code.
//===----------------------------------------------------------------------===//
-#include "asan_test_config.h"
#include "asan_test_utils.h"
template<class T>
diff --git a/lib/asan/tests/asan_globals_test.cc b/lib/asan/tests/asan_globals_test.cc
index 6467524ca32c..dc2e9bbb0530 100644
--- a/lib/asan/tests/asan_globals_test.cc
+++ b/lib/asan/tests/asan_globals_test.cc
@@ -1,4 +1,4 @@
-//===-- asan_globals_test.cc ----------------------===//
+//===-- asan_globals_test.cc ----------------------------------------------===//
//
// The LLVM Compiler Infrastructure
//
diff --git a/lib/asan/tests/asan_mac_test.mm b/lib/asan/tests/asan_mac_test.mm
index 4e5873b74485..4cbd2bb247fd 100644
--- a/lib/asan/tests/asan_mac_test.mm
+++ b/lib/asan/tests/asan_mac_test.mm
@@ -57,7 +57,7 @@ char kStartupStr[] =
@implementation LoadSomething
+(void) load {
- for (int i = 0; i < strlen(kStartupStr); i++) {
+ for (size_t i = 0; i < strlen(kStartupStr); i++) {
access_memory(&kStartupStr[i]); // make sure no optimizations occur.
}
// Don't print anything here not to interfere with the death tests.
@@ -66,13 +66,13 @@ char kStartupStr[] =
@end
void worker_do_alloc(int size) {
- char * volatile mem = malloc(size);
+ char * volatile mem = (char * volatile)malloc(size);
mem[0] = 0; // Ok
free(mem);
}
void worker_do_crash(int size) {
- char * volatile mem = malloc(size);
+ char * volatile mem = (char * volatile)malloc(size);
access_memory(&mem[size]); // BOOM
free(mem);
}
@@ -167,7 +167,7 @@ void TestGCDSourceEvent() {
dispatch_time(DISPATCH_TIME_NOW, 1LL * NSEC_PER_SEC);
dispatch_source_set_timer(timer, milestone, DISPATCH_TIME_FOREVER, 0);
- char * volatile mem = malloc(10);
+ char * volatile mem = (char * volatile)malloc(10);
dispatch_source_set_event_handler(timer, ^{
access_memory(&mem[10]);
});
@@ -184,7 +184,7 @@ void TestGCDSourceCancel() {
dispatch_time(DISPATCH_TIME_NOW, 1LL * NSEC_PER_SEC);
dispatch_source_set_timer(timer, milestone, DISPATCH_TIME_FOREVER, 0);
- char * volatile mem = malloc(10);
+ char * volatile mem = (char * volatile)malloc(10);
// Both dispatch_source_set_cancel_handler() and
// dispatch_source_set_event_handler() use dispatch_barrier_async_f().
// It's tricky to test dispatch_source_set_cancel_handler() separately,
@@ -202,7 +202,7 @@ void TestGCDSourceCancel() {
void TestGCDGroupAsync() {
dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
dispatch_group_t group = dispatch_group_create();
- char * volatile mem = malloc(10);
+ char * volatile mem = (char * volatile)malloc(10);
dispatch_group_async(group, queue, ^{
access_memory(&mem[10]);
});
diff --git a/lib/asan/tests/asan_noinst_test.cc b/lib/asan/tests/asan_noinst_test.cc
index 44d4c3c845b2..576312bf319f 100644
--- a/lib/asan/tests/asan_noinst_test.cc
+++ b/lib/asan/tests/asan_noinst_test.cc
@@ -1,4 +1,4 @@
-//===-- asan_noinst_test.cc ----------------------===//
+//===-- asan_noinst_test.cc -----------------------------------------------===//
//
// The LLVM Compiler Infrastructure
//
@@ -11,12 +11,13 @@
//
// This test file should be compiled w/o asan instrumentation.
//===----------------------------------------------------------------------===//
+
#include "asan_allocator.h"
-#include "asan_interface.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>
@@ -24,15 +25,6 @@
#include <string.h> // for memset()
#include <algorithm>
#include <vector>
-#include "gtest/gtest.h"
-
-// Simple stand-alone pseudorandom number generator.
-// Current algorithm is ANSI C linear congruential PRNG.
-static inline u32 my_rand(u32* state) {
- return (*state = *state * 1103515245 + 12345) >> 16;
-}
-
-static u32 global_seed = 0;
TEST(AddressSanitizer, InternalSimpleDeathTest) {
@@ -40,18 +32,18 @@ TEST(AddressSanitizer, InternalSimpleDeathTest) {
}
static void MallocStress(size_t n) {
- u32 seed = my_rand(&global_seed);
- __asan::AsanStackTrace stack1;
+ u32 seed = my_rand();
+ __asan::StackTrace stack1;
stack1.trace[0] = 0xa123;
stack1.trace[1] = 0xa456;
stack1.size = 2;
- __asan::AsanStackTrace stack2;
+ __asan::StackTrace stack2;
stack2.trace[0] = 0xb123;
stack2.trace[1] = 0xb456;
stack2.size = 2;
- __asan::AsanStackTrace stack3;
+ __asan::StackTrace stack3;
stack3.trace[0] = 0xc123;
stack3.trace[1] = 0xc456;
stack3.size = 2;
@@ -60,20 +52,21 @@ static void MallocStress(size_t n) {
for (size_t i = 0; i < n; i++) {
if ((i % 3) == 0) {
if (vec.empty()) continue;
- size_t idx = my_rand(&seed) % vec.size();
+ size_t idx = my_rand_r(&seed) % vec.size();
void *ptr = vec[idx];
vec[idx] = vec.back();
vec.pop_back();
- __asan::asan_free(ptr, &stack1);
+ __asan::asan_free(ptr, &stack1, __asan::FROM_MALLOC);
} else {
- size_t size = my_rand(&seed) % 1000 + 1;
- switch ((my_rand(&seed) % 128)) {
+ size_t size = my_rand_r(&seed) % 1000 + 1;
+ switch ((my_rand_r(&seed) % 128)) {
case 0: size += 1024; break;
case 1: size += 2048; break;
case 2: size += 4096; break;
}
- size_t alignment = 1 << (my_rand(&seed) % 10 + 1);
- char *ptr = (char*)__asan::asan_memalign(alignment, size, &stack2);
+ size_t alignment = 1 << (my_rand_r(&seed) % 10 + 1);
+ char *ptr = (char*)__asan::asan_memalign(alignment, size,
+ &stack2, __asan::FROM_MALLOC);
vec.push_back(ptr);
ptr[0] = 0;
ptr[size-1] = 0;
@@ -81,7 +74,7 @@ static void MallocStress(size_t n) {
}
}
for (size_t i = 0; i < vec.size(); i++)
- __asan::asan_free(vec[i], &stack3);
+ __asan::asan_free(vec[i], &stack3, __asan::FROM_MALLOC);
}
@@ -118,7 +111,7 @@ TEST(AddressSanitizer, DISABLED_InternalPrintShadow) {
}
static uptr pc_array[] = {
-#if __WORDSIZE == 64
+#if SANITIZER_WORDSIZE == 64
0x7effbf756068ULL,
0x7effbf75e5abULL,
0x7effc0625b7cULL,
@@ -164,7 +157,7 @@ static uptr pc_array[] = {
0x7effbcc3e726ULL,
0x7effbcc40852ULL,
0x7effb681ec4dULL,
-#endif // __WORDSIZE
+#endif // SANITIZER_WORDSIZE
0xB0B5E768,
0x7B682EC1,
0x367F9918,
@@ -208,22 +201,22 @@ static uptr pc_array[] = {
};
void CompressStackTraceTest(size_t n_iter) {
- u32 seed = my_rand(&global_seed);
- const size_t kNumPcs = ASAN_ARRAY_SIZE(pc_array);
+ u32 seed = my_rand();
+ const size_t kNumPcs = ARRAY_SIZE(pc_array);
u32 compressed[2 * kNumPcs];
for (size_t iter = 0; iter < n_iter; iter++) {
std::random_shuffle(pc_array, pc_array + kNumPcs);
- __asan::AsanStackTrace stack0, stack1;
+ __asan::StackTrace stack0, stack1;
stack0.CopyFrom(pc_array, kNumPcs);
- stack0.size = std::max((size_t)1, (size_t)(my_rand(&seed) % stack0.size));
+ 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(&seed) % (2 * kNumPcs));
+ std::max((size_t)2, (size_t)my_rand_r(&seed) % (2 * kNumPcs));
size_t n_frames =
- __asan::AsanStackTrace::CompressStack(&stack0, compressed, compress_size);
+ __asan::StackTrace::CompressStack(&stack0, compressed, compress_size);
Ident(n_frames);
assert(n_frames <= stack0.size);
- __asan::AsanStackTrace::UncompressStack(&stack1, compressed, compress_size);
+ __asan::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]);
@@ -236,17 +229,17 @@ TEST(AddressSanitizer, CompressStackTraceTest) {
}
void CompressStackTraceBenchmark(size_t n_iter) {
- const size_t kNumPcs = ASAN_ARRAY_SIZE(pc_array);
+ const size_t kNumPcs = ARRAY_SIZE(pc_array);
u32 compressed[2 * kNumPcs];
std::random_shuffle(pc_array, pc_array + kNumPcs);
- __asan::AsanStackTrace stack0;
+ __asan::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::AsanStackTrace::CompressStack(&stack0, compressed, compress_size);
+ __asan::StackTrace::CompressStack(&stack0, compressed, compress_size);
Ident(n_frames);
}
}
@@ -256,18 +249,18 @@ TEST(AddressSanitizer, CompressStackTraceBenchmark) {
}
TEST(AddressSanitizer, QuarantineTest) {
- __asan::AsanStackTrace stack;
+ __asan::StackTrace stack;
stack.trace[0] = 0x890;
stack.size = 1;
const int size = 32;
void *p = __asan::asan_malloc(size, &stack);
- __asan::asan_free(p, &stack);
+ __asan::asan_free(p, &stack, __asan::FROM_MALLOC);
size_t i;
size_t max_i = 1 << 30;
for (i = 0; i < max_i; i++) {
void *p1 = __asan::asan_malloc(size, &stack);
- __asan::asan_free(p1, &stack);
+ __asan::asan_free(p1, &stack, __asan::FROM_MALLOC);
if (p1 == p) break;
}
// fprintf(stderr, "i=%ld\n", i);
@@ -277,14 +270,14 @@ TEST(AddressSanitizer, QuarantineTest) {
void *ThreadedQuarantineTestWorker(void *unused) {
(void)unused;
- u32 seed = my_rand(&global_seed);
- __asan::AsanStackTrace stack;
+ u32 seed = my_rand();
+ __asan::StackTrace stack;
stack.trace[0] = 0x890;
stack.size = 1;
for (size_t i = 0; i < 1000; i++) {
- void *p = __asan::asan_malloc(1 + (my_rand(&seed) % 4000), &stack);
- __asan::asan_free(p, &stack);
+ void *p = __asan::asan_malloc(1 + (my_rand_r(&seed) % 4000), &stack);
+ __asan::asan_free(p, &stack, __asan::FROM_MALLOC);
}
return NULL;
}
@@ -296,8 +289,8 @@ TEST(AddressSanitizer, ThreadedQuarantineTest) {
size_t mmaped1 = __asan_get_heap_size();
for (int i = 0; i < n_threads; i++) {
pthread_t t;
- pthread_create(&t, NULL, ThreadedQuarantineTestWorker, 0);
- pthread_join(t, 0);
+ PTHREAD_CREATE(&t, NULL, ThreadedQuarantineTestWorker, 0);
+ PTHREAD_JOIN(t, 0);
size_t mmaped2 = __asan_get_heap_size();
EXPECT_LT(mmaped2 - mmaped1, 320U * (1 << 20));
}
@@ -305,7 +298,7 @@ TEST(AddressSanitizer, ThreadedQuarantineTest) {
void *ThreadedOneSizeMallocStress(void *unused) {
(void)unused;
- __asan::AsanStackTrace stack;
+ __asan::StackTrace stack;
stack.trace[0] = 0x890;
stack.size = 1;
const size_t kNumMallocs = 1000;
@@ -315,7 +308,7 @@ void *ThreadedOneSizeMallocStress(void *unused) {
p[i] = __asan::asan_malloc(32, &stack);
}
for (size_t i = 0; i < kNumMallocs; i++) {
- __asan::asan_free(p[i], &stack);
+ __asan::asan_free(p[i], &stack, __asan::FROM_MALLOC);
}
}
return NULL;
@@ -325,10 +318,10 @@ TEST(AddressSanitizer, ThreadedOneSizeMallocStressTest) {
const int kNumThreads = 4;
pthread_t t[kNumThreads];
for (int i = 0; i < kNumThreads; i++) {
- pthread_create(&t[i], 0, ThreadedOneSizeMallocStress, 0);
+ PTHREAD_CREATE(&t[i], 0, ThreadedOneSizeMallocStress, 0);
}
for (int i = 0; i < kNumThreads; i++) {
- pthread_join(t[i], 0);
+ PTHREAD_JOIN(t[i], 0);
}
}
@@ -336,16 +329,20 @@ TEST(AddressSanitizer, MemsetWildAddressTest) {
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 + kPageSize), 0, 100),
+ EXPECT_DEATH(libc_memset((void*)(kLowShadowBeg + 200), 0, 100),
"unknown-crash.*low shadow");
- EXPECT_DEATH(libc_memset((void*)(kShadowGapBeg + kPageSize), 0, 100),
+ EXPECT_DEATH(libc_memset((void*)(kShadowGapBeg + 200), 0, 100),
"unknown-crash.*shadow gap");
- EXPECT_DEATH(libc_memset((void*)(kHighShadowBeg + kPageSize), 0, 100),
+ EXPECT_DEATH(libc_memset((void*)(kHighShadowBeg + 200), 0, 100),
"unknown-crash.*high shadow");
}
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]));
@@ -370,23 +367,32 @@ TEST(AddressSanitizerInterface, GetAllocatedSizeAndOwnershipTest) {
// We cannot call GetAllocatedSize from the memory we didn't map,
// and from the interior pointers (not returned by previous malloc).
void *wild_addr = (void*)0x1;
- EXPECT_EQ(false, __asan_get_ownership(wild_addr));
+ EXPECT_FALSE(__asan_get_ownership(wild_addr));
EXPECT_DEATH(__asan_get_allocated_size(wild_addr), kGetAllocatedSizeErrorMsg);
- EXPECT_EQ(false, __asan_get_ownership(array + kArraySize / 2));
+ EXPECT_FALSE(__asan_get_ownership(array + kArraySize / 2));
EXPECT_DEATH(__asan_get_allocated_size(array + kArraySize / 2),
kGetAllocatedSizeErrorMsg);
// NULL is not owned, but is a valid argument for __asan_get_allocated_size().
- EXPECT_EQ(false, __asan_get_ownership(NULL));
+ EXPECT_FALSE(__asan_get_ownership(NULL));
EXPECT_EQ(0U, __asan_get_allocated_size(NULL));
// When memory is freed, it's not owned, and call to GetAllocatedSize
// is forbidden.
free(array);
- EXPECT_EQ(false, __asan_get_ownership(array));
+ EXPECT_FALSE(__asan_get_ownership(array));
EXPECT_DEATH(__asan_get_allocated_size(array), kGetAllocatedSizeErrorMsg);
-
delete int_ptr;
+
+ void *zero_alloc = Ident(malloc(0));
+ if (zero_alloc != 0) {
+ // If malloc(0) is not null, this pointer is owned and should have valid
+ // allocated size.
+ EXPECT_TRUE(__asan_get_ownership(zero_alloc));
+ // Allocated size is 0 or 1 depending on the allocator used.
+ EXPECT_LT(__asan_get_allocated_size(zero_alloc), 2U);
+ }
+ free(zero_alloc);
}
TEST(AddressSanitizerInterface, GetCurrentAllocatedBytesTest) {
@@ -410,6 +416,7 @@ 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.
@@ -441,9 +448,26 @@ static void RunGetHeapSizeTestAndDie() {
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
+ uptr old_heap_size = __asan_get_heap_size();
+ for (int i = 0; i < 3; i++) {
+ // fprintf(stderr, "allocating %zu bytes:\n", kLargeMallocSize);
+ free(Ident(malloc(kLargeMallocSize)));
+ 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
@@ -455,33 +479,42 @@ static void DoLargeMallocForGetFreeBytesTestAndDie() {
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) {
- 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;
+#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.
- 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;
+ // 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<<20, 357};
+static const size_t kManyThreadsMallocSizes[] = {5, 1UL<<10, 1UL<<14, 357};
static const size_t kManyThreadsIterations = 250;
-static const size_t kManyThreadsNumThreads = (__WORDSIZE == 32) ? 40 : 200;
+static const size_t kManyThreadsNumThreads =
+ (SANITIZER_WORDSIZE == 32) ? 40 : 200;
void *ManyThreadsWithStatsWorker(void *arg) {
(void)arg;
@@ -490,6 +523,8 @@ void *ManyThreadsWithStatsWorker(void *arg) {
free(Ident(malloc(kManyThreadsMallocSizes[size_index])));
}
}
+ // Just one large allocation.
+ free(Ident(malloc(1 << 20)));
return 0;
}
@@ -498,11 +533,11 @@ TEST(AddressSanitizerInterface, ManyThreadsWithStatsStressTest) {
pthread_t threads[kManyThreadsNumThreads];
before_test = __asan_get_current_allocated_bytes();
for (i = 0; i < kManyThreadsNumThreads; i++) {
- pthread_create(&threads[i], 0,
+ PTHREAD_CREATE(&threads[i], 0,
(void* (*)(void *x))ManyThreadsWithStatsWorker, (void*)i);
}
for (i = 0; i < kManyThreadsNumThreads; i++) {
- pthread_join(threads[i], 0);
+ PTHREAD_JOIN(threads[i], 0);
}
after_test = __asan_get_current_allocated_bytes();
// ASan stats also reflect memory usage of internal ASan RTL structs,
@@ -649,6 +684,45 @@ TEST(AddressSanitizerInterface, PoisoningStressTest) {
}
}
+TEST(AddressSanitizerInterface, PoisonedRegion) {
+ size_t rz = 16;
+ for (size_t size = 1; size <= 64; size++) {
+ char *p = new char[size];
+ uptr x = reinterpret_cast<uptr>(p);
+ for (size_t beg = 0; beg < size + rz; beg++) {
+ for (size_t end = beg; end < size + rz; end++) {
+ uptr first_poisoned = __asan_region_is_poisoned(x + beg, end - beg);
+ if (beg == end) {
+ EXPECT_FALSE(first_poisoned);
+ } else if (beg < size && end <= size) {
+ EXPECT_FALSE(first_poisoned);
+ } else if (beg >= size) {
+ EXPECT_EQ(x + beg, first_poisoned);
+ } else {
+ EXPECT_GT(end, size);
+ EXPECT_EQ(x + size, first_poisoned);
+ }
+ }
+ }
+ delete [] p;
+ }
+}
+
+// This is a performance benchmark for manual runs.
+// asan's memset interceptor calls mem_is_zero for the entire shadow region.
+// the profile should look like this:
+// 89.10% [.] __memset_sse2
+// 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) {
+ size_t size = 1 << 20;
+ char *x = new char[size];
+ for (int i = 0; i < 100000; i++)
+ Ident(memset)(x, 0, size);
+ delete [] x;
+}
+
static const char *kInvalidPoisonMessage = "invalid-poison-memory-range";
static const char *kInvalidUnpoisonMessage = "invalid-unpoison-memory-range";
@@ -670,20 +744,29 @@ TEST(AddressSanitizerInterface, DISABLED_InvalidPoisonAndUnpoisonCallsTest) {
}
static void ErrorReportCallbackOneToZ(const char *report) {
- write(2, "ABCDEF", 6);
+ int report_len = strlen(report);
+ ASSERT_EQ(6, write(2, "ABCDEF", 6));
+ ASSERT_EQ(report_len, write(2, report, report_len));
+ ASSERT_EQ(6, write(2, "ABCDEF", 6));
+ _exit(1);
}
TEST(AddressSanitizerInterface, SetErrorReportCallbackTest) {
__asan_set_error_report_callback(ErrorReportCallbackOneToZ);
- EXPECT_DEATH(__asan_report_error(0, 0, 0, 0, true, 1), "ABCDEF");
+ EXPECT_DEATH(__asan_report_error(0, 0, 0, 0, true, 1),
+ ASAN_PCRE_DOTALL "ABCDEF.*AddressSanitizer.*WRITE.*ABCDEF");
__asan_set_error_report_callback(NULL);
}
TEST(AddressSanitizerInterface, GetOwnershipStressTest) {
std::vector<char *> pointers;
std::vector<size_t> sizes;
+#if ASAN_ALLOCATOR_VERSION == 1
const size_t kNumMallocs =
- (__WORDSIZE <= 32 || ASAN_LOW_MEMORY) ? 1 << 10 : 1 << 14;
+ (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));
diff --git a/lib/asan/tests/asan_test.cc b/lib/asan/tests/asan_test.cc
index 8e967e929899..5fa65b2af5dc 100644
--- a/lib/asan/tests/asan_test.cc
+++ b/lib/asan/tests/asan_test.cc
@@ -1,4 +1,4 @@
-//===-- asan_test.cc ----------------------===//
+//===-- asan_test.cc ------------------------------------------------------===//
//
// The LLVM Compiler Infrastructure
//
@@ -19,17 +19,26 @@
#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_config.h"
#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__
@@ -47,17 +56,8 @@ typedef uint16_t U2;
typedef uint32_t U4;
typedef uint64_t U8;
-static const char *progname;
static const int kPageSize = 4096;
-// Simple stand-alone pseudorandom number generator.
-// Current algorithm is ANSI C linear congruential PRNG.
-static inline uint32_t my_rand(uint32_t* state) {
- return (*state = *state * 1103515245 + 12345) >> 16;
-}
-
-static uint32_t global_seed = 0;
-
const size_t kLargeMalloc = 1 << 24;
template<typename T>
@@ -66,7 +66,7 @@ NOINLINE void asan_write(T *a) {
}
NOINLINE void asan_write_sized_aligned(uint8_t *p, size_t size) {
- EXPECT_EQ(0, ((uintptr_t)p % 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);
@@ -130,6 +130,8 @@ NOINLINE void uaf_test(int size, int off) {
TEST(AddressSanitizer, HasFeatureAddressSanitizerTest) {
#if defined(__has_feature) && __has_feature(address_sanitizer)
bool asan = 1;
+#elif defined(__SANITIZE_ADDRESS__)
+ bool asan = 1;
#else
bool asan = 0;
#endif
@@ -141,29 +143,24 @@ TEST(AddressSanitizer, SimpleDeathTest) {
}
TEST(AddressSanitizer, VariousMallocsTest) {
- // fprintf(stderr, "malloc:\n");
int *a = (int*)malloc(100 * sizeof(int));
a[50] = 0;
free(a);
- // fprintf(stderr, "realloc:\n");
int *r = (int*)malloc(10);
r = (int*)realloc(r, 2000 * sizeof(int));
r[1000] = 0;
free(r);
- // fprintf(stderr, "operator new []\n");
int *b = new int[100];
b[50] = 0;
delete [] b;
- // fprintf(stderr, "operator new\n");
int *c = new int;
*c = 0;
delete c;
-#if !defined(__APPLE__) && !defined(ANDROID)
- // fprintf(stderr, "posix_memalign\n");
+#if !defined(__APPLE__) && !defined(ANDROID) && !defined(__ANDROID__)
int *pm;
int pm_res = posix_memalign((void**)&pm, kPageSize, kPageSize);
EXPECT_EQ(0, pm_res);
@@ -172,7 +169,7 @@ TEST(AddressSanitizer, VariousMallocsTest) {
#if !defined(__APPLE__)
int *ma = (int*)memalign(kPageSize, kPageSize);
- EXPECT_EQ(0, (uintptr_t)ma % kPageSize);
+ EXPECT_EQ(0U, (uintptr_t)ma % kPageSize);
ma[123] = 0;
free(ma);
#endif // __APPLE__
@@ -186,19 +183,19 @@ TEST(AddressSanitizer, CallocTest) {
TEST(AddressSanitizer, VallocTest) {
void *a = valloc(100);
- EXPECT_EQ(0, (uintptr_t)a % kPageSize);
+ EXPECT_EQ(0U, (uintptr_t)a % kPageSize);
free(a);
}
#ifndef __APPLE__
TEST(AddressSanitizer, PvallocTest) {
char *a = (char*)pvalloc(kPageSize + 100);
- EXPECT_EQ(0, (uintptr_t)a % kPageSize);
+ EXPECT_EQ(0U, (uintptr_t)a % kPageSize);
a[kPageSize + 101] = 1; // we should not report an error here.
free(a);
a = (char*)pvalloc(0); // pvalloc(0) should allocate at least one page.
- EXPECT_EQ(0, (uintptr_t)a % kPageSize);
+ EXPECT_EQ(0U, (uintptr_t)a % kPageSize);
a[101] = 1; // we should not report an error here.
free(a);
}
@@ -214,8 +211,8 @@ void *TSDWorker(void *test_key) {
void TSDDestructor(void *tsd) {
// Spawning a thread will check that the current thread id is not -1.
pthread_t th;
- pthread_create(&th, NULL, TSDWorker, NULL);
- pthread_join(th, NULL);
+ PTHREAD_CREATE(&th, NULL, TSDWorker, NULL);
+ PTHREAD_JOIN(th, NULL);
}
// This tests triggers the thread-specific data destruction fiasco which occurs
@@ -229,8 +226,8 @@ TEST(AddressSanitizer, DISABLED_TSDTest) {
pthread_t th;
pthread_key_t test_key;
pthread_key_create(&test_key, TSDDestructor);
- pthread_create(&th, NULL, TSDWorker, &test_key);
- pthread_join(th, NULL);
+ PTHREAD_CREATE(&th, NULL, TSDWorker, &test_key);
+ PTHREAD_JOIN(th, NULL);
pthread_key_delete(test_key);
}
@@ -245,10 +242,10 @@ void OOBTest() {
EXPECT_DEATH(oob_test<T>(size, i), expected_str);
}
- for (int i = 0; i < size - sizeof(T) + 1; i++)
+ for (int i = 0; i < (int)(size - sizeof(T) + 1); i++)
oob_test<T>(size, i);
- for (int i = size - sizeof(T) + 1; i <= size + 3 * sizeof(T); 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;
@@ -304,8 +301,20 @@ TEST(AddressSanitizer, OOBRightTest) {
}
}
+#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";
+ const char *uaf_string = "AddressSanitizer:.*heap-use-after-free";
EXPECT_DEATH(uaf_test<U1>(1, 0), uaf_string);
EXPECT_DEATH(uaf_test<U1>(10, 0), uaf_string);
EXPECT_DEATH(uaf_test<U1>(10, 10), uaf_string);
@@ -335,7 +344,7 @@ TEST(AddressSanitizer, BitFieldPositiveTest) {
EXPECT_DEATH(x->bf2 = 0, "use-after-free");
EXPECT_DEATH(x->bf3 = 0, "use-after-free");
EXPECT_DEATH(x->bf4 = 0, "use-after-free");
-};
+}
struct StructWithBitFields_8_24 {
int a:8;
@@ -350,7 +359,7 @@ TEST(AddressSanitizer, BitFieldNegativeTest) {
}
TEST(AddressSanitizer, OutOfMemoryTest) {
- size_t size = __WORDSIZE == 64 ? (size_t)(1ULL << 48) : (0xf0000000);
+ size_t size = SANITIZER_WORDSIZE == 64 ? (size_t)(1ULL << 48) : (0xf0000000);
EXPECT_EQ(0, realloc(0, size));
EXPECT_EQ(0, realloc(0, ~Ident(0)));
EXPECT_EQ(0, malloc(size));
@@ -360,28 +369,61 @@ TEST(AddressSanitizer, OutOfMemoryTest) {
}
#if ASAN_NEEDS_SEGV
+namespace {
+
+const char kUnknownCrash[] = "AddressSanitizer: SEGV on unknown address";
+const char kOverriddenHandler[] = "ASan signal handler has been overridden\n";
+
TEST(AddressSanitizer, WildAddressTest) {
char *c = (char*)0x123;
- EXPECT_DEATH(*c = 0, "AddressSanitizer crashed on unknown address");
+ EXPECT_DEATH(*c = 0, kUnknownCrash);
+}
+
+void my_sigaction_sighandler(int, siginfo_t*, void*) {
+ fprintf(stderr, kOverriddenHandler);
+ exit(1);
}
+
+void my_signal_sighandler(int signum) {
+ fprintf(stderr, kOverriddenHandler);
+ exit(1);
+}
+
+TEST(AddressSanitizer, SignalTest) {
+ struct sigaction sigact;
+ memset(&sigact, 0, sizeof(sigact));
+ sigact.sa_sigaction = my_sigaction_sighandler;
+ sigact.sa_flags = SA_SIGINFO;
+ // ASan should silently ignore sigaction()...
+ EXPECT_EQ(0, sigaction(SIGSEGV, &sigact, 0));
+#ifdef __APPLE__
+ EXPECT_EQ(0, sigaction(SIGBUS, &sigact, 0));
+#endif
+ char *c = (char*)0x123;
+ EXPECT_DEATH(*c = 0, kUnknownCrash);
+ // ... and signal().
+ EXPECT_EQ(0, signal(SIGSEGV, my_signal_sighandler));
+ EXPECT_DEATH(*c = 0, kUnknownCrash);
+}
+} // namespace
#endif
static void MallocStress(size_t n) {
- uint32_t seed = my_rand(&global_seed);
+ 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(&seed) % vec.size();
+ 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(&seed) % 1000 + 1;
+ size_t size = my_rand_r(&seed) % 1000 + 1;
#ifndef __APPLE__
- size_t alignment = 1 << (my_rand(&seed) % 7 + 3);
+ size_t alignment = 1 << (my_rand_r(&seed) % 7 + 3);
char *ptr = (char*)memalign_aaa(alignment, size);
#else
char *ptr = (char*) malloc_aaa(size);
@@ -421,24 +463,42 @@ TEST(AddressSanitizer, HugeMallocTest) {
// 32-bit Mac 10.7 gives even less (< 1G).
// (the libSystem malloc() allows allocating up to 2300 megabytes without
// ASan).
- size_t n_megs = __WORDSIZE == 32 ? 500 : 4100;
+ size_t n_megs = SANITIZER_WORDSIZE == 32 ? 500 : 4100;
#else
- size_t n_megs = __WORDSIZE == 32 ? 2600 : 4100;
+ size_t n_megs = SANITIZER_WORDSIZE == 32 ? 2600 : 4100;
#endif
TestLargeMalloc(n_megs << 20);
}
#endif
+#ifndef __APPLE__
+void MemalignRun(size_t align, size_t size, int idx) {
+ char *p = (char *)memalign(align, size);
+ Ident(p)[idx] = 0;
+ free(p);
+}
+
+TEST(AddressSanitizer, memalign) {
+ for (int align = 16; align <= (1 << 23); align *= 2) {
+ size_t size = align * 5;
+ EXPECT_DEATH(MemalignRun(align, size, -1),
+ "is located 1 bytes to the left");
+ EXPECT_DEATH(MemalignRun(align, size, size + 1),
+ "is located 1 bytes to the right");
+ }
+}
+#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,
+ PTHREAD_CREATE(&t[i], 0, (void* (*)(void *x))MallocStress,
(void*)kNumIterations);
}
for (int i = 0; i < kNumThreads; i++) {
- pthread_join(t[i], 0);
+ PTHREAD_JOIN(t[i], 0);
}
}
@@ -452,13 +512,14 @@ void *ManyThreadsWorker(void *a) {
}
TEST(AddressSanitizer, ManyThreadsTest) {
- const size_t kNumThreads = __WORDSIZE == 32 ? 30 : 1000;
+ const size_t kNumThreads =
+ (SANITIZER_WORDSIZE == 32 || ASAN_AVOID_EXPENSIVE_TESTS) ? 30 : 1000;
pthread_t t[kNumThreads];
for (size_t i = 0; i < kNumThreads; i++) {
- pthread_create(&t[i], 0, (void* (*)(void *x))ManyThreadsWorker, (void*)i);
+ PTHREAD_CREATE(&t[i], 0, ManyThreadsWorker, (void*)i);
}
for (size_t i = 0; i < kNumThreads; i++) {
- pthread_join(t[i], 0);
+ PTHREAD_JOIN(t[i], 0);
}
}
@@ -468,20 +529,20 @@ TEST(AddressSanitizer, ReallocTest) {
ptr[3] = 3;
for (int i = 0; i < 10000; i++) {
ptr = (int*)realloc(ptr,
- (my_rand(&global_seed) % 1000 + kMinElem) * sizeof(int));
+ (my_rand() % 1000 + kMinElem) * sizeof(int));
EXPECT_EQ(3, ptr[3]);
}
}
#ifndef __APPLE__
static const char *kMallocUsableSizeErrorMsg =
- "AddressSanitizer attempting to call malloc_usable_size()";
+ "AddressSanitizer: attempting to call malloc_usable_size()";
TEST(AddressSanitizer, MallocUsableSizeTest) {
const size_t kArraySize = 100;
char *array = Ident((char*)malloc(kArraySize));
int *int_ptr = Ident(new int);
- EXPECT_EQ(0, malloc_usable_size(NULL));
+ EXPECT_EQ(0U, malloc_usable_size(NULL));
EXPECT_EQ(kArraySize, malloc_usable_size(array));
EXPECT_EQ(sizeof(int), malloc_usable_size(int_ptr));
EXPECT_DEATH(malloc_usable_size((void*)0x123), kMallocUsableSizeErrorMsg);
@@ -501,7 +562,7 @@ void WrongFree() {
TEST(AddressSanitizer, WrongFreeTest) {
EXPECT_DEATH(WrongFree(),
- "ERROR: AddressSanitizer attempting free.*not malloc");
+ "ERROR: AddressSanitizer: attempting free.*not malloc");
}
void DoubleFree() {
@@ -515,7 +576,7 @@ void DoubleFree() {
TEST(AddressSanitizer, DoubleFreeTest) {
EXPECT_DEATH(DoubleFree(), ASAN_PCRE_DOTALL
- "ERROR: AddressSanitizer attempting double-free"
+ "ERROR: AddressSanitizer: attempting double-free"
".*is located 0 bytes inside of 400-byte region"
".*freed by thread T0 here"
".*previously allocated by thread T0 here");
@@ -610,6 +671,17 @@ NOINLINE void LongJmpFunc1(jmp_buf buf) {
longjmp(buf, 1);
}
+NOINLINE void BuiltinLongJmpFunc1(jmp_buf buf) {
+ // create three red zones for these two stack objects.
+ int a;
+ int b;
+
+ int *A = Ident(&a);
+ int *B = Ident(&b);
+ *A = *B;
+ __builtin_longjmp((void**)buf, 1);
+}
+
NOINLINE void UnderscopeLongJmpFunc1(jmp_buf buf) {
// create three red zones for these two stack objects.
int a;
@@ -650,6 +722,17 @@ TEST(AddressSanitizer, LongJmpTest) {
}
}
+#if not defined(__ANDROID__)
+TEST(AddressSanitizer, BuiltinLongJmpTest) {
+ static jmp_buf buf;
+ if (!__builtin_setjmp((void**)buf)) {
+ BuiltinLongJmpFunc1(buf);
+ } else {
+ TouchStackFunc();
+ }
+}
+#endif // not defined(__ANDROID__)
+
TEST(AddressSanitizer, UnderscopeLongJmpTest) {
static jmp_buf buf;
if (!_setjmp(buf)) {
@@ -683,7 +766,7 @@ NOINLINE void ThrowFunc() {
TEST(AddressSanitizer, CxxExceptionTest) {
if (ASAN_UAR) return;
// TODO(kcc): this test crashes on 32-bit for some reason...
- if (__WORDSIZE == 32) return;
+ if (SANITIZER_WORDSIZE == 32) return;
try {
ThrowFunc();
} catch(...) {}
@@ -710,10 +793,10 @@ void *ThreadStackReuseFunc2(void *unused) {
TEST(AddressSanitizer, ThreadStackReuseTest) {
pthread_t t;
- pthread_create(&t, 0, ThreadStackReuseFunc1, 0);
- pthread_join(t, 0);
- pthread_create(&t, 0, ThreadStackReuseFunc2, 0);
- pthread_join(t, 0);
+ PTHREAD_CREATE(&t, 0, ThreadStackReuseFunc1, 0);
+ PTHREAD_JOIN(t, 0);
+ PTHREAD_CREATE(&t, 0, ThreadStackReuseFunc2, 0);
+ PTHREAD_JOIN(t, 0);
}
#if defined(__i386__) || defined(__x86_64__)
@@ -725,7 +808,7 @@ TEST(AddressSanitizer, Store128Test) {
assert(((uintptr_t)p % 16) == 0);
__m128i value_wide = _mm_set1_epi16(0x1234);
EXPECT_DEATH(_mm_store_si128((__m128i*)p, value_wide),
- "AddressSanitizer heap-buffer-overflow");
+ "AddressSanitizer: heap-buffer-overflow");
EXPECT_DEATH(_mm_store_si128((__m128i*)p, value_wide),
"WRITE of size 16");
EXPECT_DEATH(_mm_store_si128((__m128i*)p, value_wide),
@@ -734,14 +817,39 @@ TEST(AddressSanitizer, Store128Test) {
}
#endif
-static string RightOOBErrorMessage(int oob_distance) {
+static string RightOOBErrorMessage(int oob_distance, bool is_write) {
assert(oob_distance >= 0);
char expected_str[100];
- sprintf(expected_str, "located %d bytes to the right", oob_distance);
+ sprintf(expected_str, ASAN_PCRE_DOTALL "%s.*located %d bytes to the right",
+ is_write ? "WRITE" : "READ", oob_distance);
return string(expected_str);
}
-static string LeftOOBErrorMessage(int oob_distance) {
+static string RightOOBWriteMessage(int oob_distance) {
+ return RightOOBErrorMessage(oob_distance, /*is_write*/true);
+}
+
+static string RightOOBReadMessage(int oob_distance) {
+ return RightOOBErrorMessage(oob_distance, /*is_write*/false);
+}
+
+static 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",
+ is_write ? "WRITE" : "READ", oob_distance);
+ return string(expected_str);
+}
+
+static string LeftOOBWriteMessage(int oob_distance) {
+ return LeftOOBErrorMessage(oob_distance, /*is_write*/true);
+}
+
+static string LeftOOBReadMessage(int oob_distance) {
+ return LeftOOBErrorMessage(oob_distance, /*is_write*/false);
+}
+
+static string LeftOOBAccessMessage(int oob_distance) {
assert(oob_distance > 0);
char expected_str[100];
sprintf(expected_str, "located %d bytes to the left", oob_distance);
@@ -755,44 +863,48 @@ void MemSetOOBTestTemplate(size_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(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);
+ 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),
- RightOOBErrorMessage(0));
- EXPECT_DEATH(memset((char*)(array + length) - 1, element, 6),
- RightOOBErrorMessage(4));
- EXPECT_DEATH(memset(array + 1, element, size + sizeof(T)),
- RightOOBErrorMessage(2 * sizeof(T) - 1));
+ 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),
- RightOOBErrorMessage(sizeof(T)));
+ 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),
- LeftOOBErrorMessage(1));
- EXPECT_DEATH(memset((char*)array - 5, 0, 6),
- LeftOOBErrorMessage(5));
- EXPECT_DEATH(memset(array - 5, element, size + 5 * sizeof(T)),
- LeftOOBErrorMessage(5 * sizeof(T)));
+ 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)),
- LeftOOBErrorMessage(2 * sizeof(T)));
+ 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),
- LeftOOBErrorMessage(2));
+ EXPECT_DEATH(MEMSET((char*)array - 2, element, size + 4),
+ LeftOOBWriteMessage(2));
free(array);
}
@@ -804,6 +916,51 @@ TEST(AddressSanitizer, MemSetOOBTest) {
// 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) {
@@ -827,27 +984,27 @@ void MemTransferOOBTestTemplate(size_t length) {
// try to change mem to the right of dest
EXPECT_DEATH(M::transfer(dest + 1, src, size),
- RightOOBErrorMessage(sizeof(T) - 1));
+ RightOOBWriteMessage(0));
EXPECT_DEATH(M::transfer((char*)(dest + length) - 1, src, 5),
- RightOOBErrorMessage(3));
+ RightOOBWriteMessage(0));
// try to change mem to the left of dest
EXPECT_DEATH(M::transfer(dest - 2, src, size),
- LeftOOBErrorMessage(2 * sizeof(T)));
+ LeftOOBWriteMessage(2 * sizeof(T)));
EXPECT_DEATH(M::transfer((char*)dest - 3, src, 4),
- LeftOOBErrorMessage(3));
+ LeftOOBWriteMessage(3));
// try to access mem to the right of src
EXPECT_DEATH(M::transfer(dest, src + 2, size),
- RightOOBErrorMessage(2 * sizeof(T) - 1));
+ RightOOBReadMessage(0));
EXPECT_DEATH(M::transfer(dest, (char*)(src + length) - 3, 6),
- RightOOBErrorMessage(2));
+ RightOOBReadMessage(0));
// try to access mem to the left of src
EXPECT_DEATH(M::transfer(dest, src - 1, size),
- LeftOOBErrorMessage(sizeof(T)));
+ LeftOOBReadMessage(sizeof(T)));
EXPECT_DEATH(M::transfer(dest, (char*)src - 6, 7),
- LeftOOBErrorMessage(6));
+ LeftOOBReadMessage(6));
// Generally we don't need to test cases where both accessing src and writing
// to dest address to poisoned memory.
@@ -856,10 +1013,10 @@ void MemTransferOOBTestTemplate(size_t length) {
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),
- LeftOOBErrorMessage(sizeof(T)));
+ LeftOOBWriteMessage(sizeof(T)));
// try to access mem to both sides of src
EXPECT_DEATH(M::transfer(big_dest, src - 2, size * 2),
- LeftOOBErrorMessage(2 * sizeof(T)));
+ LeftOOBReadMessage(2 * sizeof(T)));
free(src);
free(dest);
@@ -870,7 +1027,7 @@ void MemTransferOOBTestTemplate(size_t length) {
class MemCpyWrapper {
public:
static void* transfer(void *to, const void *from, size_t size) {
- return memcpy(to, from, size);
+ return Ident(memcpy)(to, from, size);
}
};
TEST(AddressSanitizer, MemCpyOOBTest) {
@@ -881,7 +1038,7 @@ TEST(AddressSanitizer, MemCpyOOBTest) {
class MemMoveWrapper {
public:
static void* transfer(void *to, const void *from, size_t size) {
- return memmove(to, from, size);
+ return Ident(memmove)(to, from, size);
}
};
TEST(AddressSanitizer, MemMoveOOBTest) {
@@ -902,21 +1059,21 @@ void StrLenOOBTestTemplate(char *str, size_t length, bool is_global) {
// Normal strlen calls
EXPECT_EQ(strlen(str), length);
if (length > 0) {
- EXPECT_EQ(strlen(str + 1), length - 1);
- EXPECT_EQ(strlen(str + 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)), LeftOOBErrorMessage(1));
- EXPECT_DEATH(Ident(strlen(str - 5)), LeftOOBErrorMessage(5));
+ EXPECT_DEATH(Ident(strlen(str - 1)), LeftOOBReadMessage(1));
+ EXPECT_DEATH(Ident(strlen(str - 5)), LeftOOBReadMessage(5));
}
- EXPECT_DEATH(Ident(strlen(str + length + 1)), RightOOBErrorMessage(0));
+ 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)), RightOOBErrorMessage(0));
- EXPECT_DEATH(Ident(strlen(str + length)), RightOOBErrorMessage(0));
+ EXPECT_DEATH(Ident(strlen(str)), RightOOBReadMessage(0));
+ EXPECT_DEATH(Ident(strlen(str + length)), RightOOBReadMessage(0));
// Restore terminator
str[length] = 0;
}
@@ -925,7 +1082,8 @@ TEST(AddressSanitizer, StrLenOOBTest) {
size_t length = Ident(10);
char *heap_string = Ident((char*)malloc(length + 1));
char stack_string[10 + 1];
- for (int i = 0; i < length; i++) {
+ break_optimization(&stack_string);
+ for (size_t i = 0; i < length; i++) {
heap_string[i] = 'a';
stack_string[i] = 'b';
}
@@ -959,11 +1117,11 @@ TEST(AddressSanitizer, StrNLenOOBTest) {
str[size - 1] = '\0';
Ident(strnlen(str, 2 * size));
// Argument points to not allocated memory.
- EXPECT_DEATH(Ident(strnlen(str - 1, 1)), LeftOOBErrorMessage(1));
- EXPECT_DEATH(Ident(strnlen(str + size, 1)), RightOOBErrorMessage(0));
+ 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)), RightOOBErrorMessage(0));
+ EXPECT_DEATH(Ident(strnlen(str, size + 1)), RightOOBReadMessage(0));
free(str);
}
#endif
@@ -979,11 +1137,11 @@ TEST(AddressSanitizer, StrDupOOBTest) {
new_str = strdup(str + size - 1);
free(new_str);
// Argument points to not allocated memory.
- EXPECT_DEATH(Ident(strdup(str - 1)), LeftOOBErrorMessage(1));
- EXPECT_DEATH(Ident(strdup(str + size)), RightOOBErrorMessage(0));
+ 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)), RightOOBErrorMessage(0));
+ EXPECT_DEATH(Ident(strdup(str)), RightOOBReadMessage(0));
free(str);
}
@@ -997,15 +1155,15 @@ TEST(AddressSanitizer, StrCpyOOBTest) {
strcpy(to, from);
strcpy(to + to_size - from_size, from);
// Length of "from" is too small.
- EXPECT_DEATH(Ident(strcpy(from, "hello2")), RightOOBErrorMessage(0));
+ EXPECT_DEATH(Ident(strcpy(from, "hello2")), RightOOBWriteMessage(0));
// "to" or "from" points to not allocated memory.
- EXPECT_DEATH(Ident(strcpy(to - 1, from)), LeftOOBErrorMessage(1));
- EXPECT_DEATH(Ident(strcpy(to, from - 1)), LeftOOBErrorMessage(1));
- EXPECT_DEATH(Ident(strcpy(to, from + from_size)), RightOOBErrorMessage(0));
- EXPECT_DEATH(Ident(strcpy(to + to_size, from)), RightOOBErrorMessage(0));
+ 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)), RightOOBErrorMessage(0));
+ EXPECT_DEATH(Ident(strcpy(to, from)), RightOOBReadMessage(0));
free(to);
free(from);
}
@@ -1027,31 +1185,37 @@ TEST(AddressSanitizer, StrNCpyOOBTest) {
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)),
- LeftOOBErrorMessage(1));
+ LeftOOBReadMessage(1));
EXPECT_DEATH(Ident(strncpy(to - 1, from, from_size)),
- LeftOOBErrorMessage(1));
+ LeftOOBWriteMessage(1));
EXPECT_DEATH(Ident(strncpy(to, from + from_size, 1)),
- RightOOBErrorMessage(0));
+ RightOOBReadMessage(0));
EXPECT_DEATH(Ident(strncpy(to + to_size, from, 1)),
- RightOOBErrorMessage(0));
+ RightOOBWriteMessage(0));
// Length of "to" is too small
EXPECT_DEATH(Ident(strncpy(to + to_size - from_size + 1, from, from_size)),
- RightOOBErrorMessage(0));
+ RightOOBWriteMessage(0));
EXPECT_DEATH(Ident(strncpy(to + 1, from, to_size)),
- RightOOBErrorMessage(0));
+ 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)),
- RightOOBErrorMessage(0));
+ RightOOBReadMessage(0));
free(to);
free(from);
}
-typedef char*(*PointerToStrChr)(const char*, int);
-void RunStrChrTest(PointerToStrChr StrChr) {
+// 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';
@@ -1060,13 +1224,30 @@ void RunStrChrTest(PointerToStrChr StrChr) {
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')), LeftOOBErrorMessage(1));
- EXPECT_DEATH(Ident(StrChr(str + size, 'z')), RightOOBErrorMessage(0));
+ 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')), RightOOBErrorMessage(0));
+ 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);
@@ -1124,8 +1305,9 @@ TEST(AddressSanitizer, StrCmpAndFriendsLogicTest) {
typedef int(*PointerToStrCmp)(const char*, const char*);
void RunStrCmpTest(PointerToStrCmp StrCmp) {
size_t size = Ident(100);
- char *s1 = MallocAndMemsetString(size);
- char *s2 = MallocAndMemsetString(size);
+ int fill = 'o';
+ char *s1 = MallocAndMemsetString(size, fill);
+ char *s2 = MallocAndMemsetString(size, fill);
s1[size - 1] = '\0';
s2[size - 1] = '\0';
// Normal StrCmp calls
@@ -1136,14 +1318,14 @@ void RunStrCmpTest(PointerToStrCmp StrCmp) {
s2[size - 1] = 'x';
Ident(StrCmp(s1, s2));
// One of arguments points to not allocated memory.
- EXPECT_DEATH(Ident(StrCmp)(s1 - 1, s2), LeftOOBErrorMessage(1));
- EXPECT_DEATH(Ident(StrCmp)(s1, s2 - 1), LeftOOBErrorMessage(1));
- EXPECT_DEATH(Ident(StrCmp)(s1 + size, s2), RightOOBErrorMessage(0));
- EXPECT_DEATH(Ident(StrCmp)(s1, s2 + size), RightOOBErrorMessage(0));
+ 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.
- s2[size - 1] = 'z';
- EXPECT_DEATH(Ident(StrCmp)(s1, s1), RightOOBErrorMessage(0));
- EXPECT_DEATH(Ident(StrCmp)(s1 + size - 1, s2), RightOOBErrorMessage(0));
+ 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);
}
@@ -1172,13 +1354,13 @@ void RunStrNCmpTest(PointerToStrNCmp StrNCmp) {
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), LeftOOBErrorMessage(1));
- EXPECT_DEATH(Ident(StrNCmp)(s1, s2 - 1, 1), LeftOOBErrorMessage(1));
- EXPECT_DEATH(Ident(StrNCmp)(s1 + size, s2, 1), RightOOBErrorMessage(0));
- EXPECT_DEATH(Ident(StrNCmp)(s1, s2 + size, 1), RightOOBErrorMessage(0));
+ 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), RightOOBErrorMessage(0));
- EXPECT_DEATH(Ident(StrNCmp)(s1 + size - 1, s2, 2), RightOOBErrorMessage(0));
+ 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);
}
@@ -1200,22 +1382,23 @@ TEST(AddressSanitizer, MemCmpOOBTest) {
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), LeftOOBErrorMessage(1));
- EXPECT_DEATH(Ident(memcmp)(s1, s2 - 1, 1), LeftOOBErrorMessage(1));
- EXPECT_DEATH(Ident(memcmp)(s1 + size, s2, 1), RightOOBErrorMessage(0));
- EXPECT_DEATH(Ident(memcmp)(s1, s2 + size, 1), RightOOBErrorMessage(0));
+ 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), RightOOBErrorMessage(0));
- EXPECT_DEATH(Ident(memcmp)(s1 + size - 1, s2, 2), RightOOBErrorMessage(0));
+ 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), RightOOBErrorMessage(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';
@@ -1226,24 +1409,25 @@ TEST(AddressSanitizer, StrCatOOBTest) {
strcat(to, from);
strcat(to, from);
strcat(to + from_size, from + from_size - 2);
- // Catenate empty string is not always an error.
- strcat(to - 1, from + from_size - 1);
+ // 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), LeftOOBErrorMessage(1));
- EXPECT_DEATH(strcat(to, from - 1), LeftOOBErrorMessage(1));
- EXPECT_DEATH(strcat(to + to_size, from), RightOOBErrorMessage(0));
- EXPECT_DEATH(strcat(to, from + from_size), RightOOBErrorMessage(0));
+ 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), RightOOBErrorMessage(0));
+ 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), RightOOBErrorMessage(0));
+ 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), RightOOBErrorMessage(0));
+ EXPECT_DEATH(strcat(to, from), RightOOBWriteMessage(0));
// length of "to" is just enough.
strcat(to, from + 1);
@@ -1252,6 +1436,7 @@ TEST(AddressSanitizer, StrCatOOBTest) {
}
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';
@@ -1262,26 +1447,26 @@ TEST(AddressSanitizer, StrNCatOOBTest) {
strncat(to, from, from_size);
from[from_size - 1] = '\0';
strncat(to, from, 2 * from_size);
- // Catenating empty string is not an error.
- strncat(to - 1, from, 0);
+ // 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), LeftOOBErrorMessage(1));
- EXPECT_DEATH(strncat(to, from - 1, 2), LeftOOBErrorMessage(1));
- EXPECT_DEATH(strncat(to + to_size, from, 2), RightOOBErrorMessage(0));
- EXPECT_DEATH(strncat(to, from + from_size, 2), RightOOBErrorMessage(0));
+ 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), RightOOBErrorMessage(0));
+ EXPECT_DEATH(strncat(to, from, from_size + 1), RightOOBReadMessage(0));
// "to" is not zero-terminated.
- EXPECT_DEATH(strncat(to + 1, from, 1), RightOOBErrorMessage(0));
+ 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), RightOOBErrorMessage(0));
+ EXPECT_DEATH(strncat(to, from, from_size - 1), RightOOBWriteMessage(0));
// "to" is just enough.
strncat(to, from, from_size - 2);
@@ -1336,7 +1521,7 @@ TEST(AddressSanitizer, StrArgsOverlapTest) {
str[10] = '\0';
str[20] = '\0';
strcat(str, str + 10);
- strcat(str, str + 11);
+ EXPECT_DEATH(strcat(str, str + 11), OverlapErrorMessage("strcat"));
str[10] = '\0';
strcat(str + 11, str);
EXPECT_DEATH(strcat(str, str + 9), OverlapErrorMessage("strcat"));
@@ -1347,7 +1532,7 @@ TEST(AddressSanitizer, StrArgsOverlapTest) {
memset(str, 'z', size);
str[10] = '\0';
strncat(str, str + 10, 10); // from is empty
- strncat(str, str + 11, 10);
+ EXPECT_DEATH(strncat(str, str + 11, 10), OverlapErrorMessage("strncat"));
str[10] = '\0';
str[20] = '\0';
strncat(str + 5, str, 5);
@@ -1372,10 +1557,10 @@ typedef void(*PointerToCallAtoi)(const char*);
void RunAtoiOOBTest(PointerToCallAtoi Atoi) {
char *array = MallocAndMemsetString(10, '1');
// Invalid pointer to the string.
- EXPECT_DEATH(Atoi(array + 11), RightOOBErrorMessage(1));
- EXPECT_DEATH(Atoi(array - 1), LeftOOBErrorMessage(1));
+ 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), RightOOBErrorMessage(0));
+ EXPECT_DEATH(Atoi(array), RightOOBReadMessage(0));
// Make last symbol a terminating NULL or other non-digit.
array[9] = '\0';
Atoi(array);
@@ -1384,13 +1569,13 @@ void RunAtoiOOBTest(PointerToCallAtoi Atoi) {
Atoi(array + 9);
// Sometimes we need to detect overflow if no digits are found.
memset(array, ' ', 10);
- EXPECT_DEATH(Atoi(array), RightOOBErrorMessage(0));
+ EXPECT_DEATH(Atoi(array), RightOOBReadMessage(0));
array[9] = '-';
- EXPECT_DEATH(Atoi(array), RightOOBErrorMessage(0));
- EXPECT_DEATH(Atoi(array + 9), RightOOBErrorMessage(0));
+ EXPECT_DEATH(Atoi(array), RightOOBReadMessage(0));
+ EXPECT_DEATH(Atoi(array + 9), RightOOBReadMessage(0));
array[8] = '-';
Atoi(array);
- delete array;
+ free(array);
}
TEST(AddressSanitizer, AtoiAndFriendsOOBTest) {
@@ -1414,16 +1599,16 @@ void RunStrtolOOBTest(PointerToCallStrtol Strtol) {
array[1] = '2';
array[2] = '3';
// Invalid pointer to the string.
- EXPECT_DEATH(Strtol(array + 3, NULL, 0), RightOOBErrorMessage(0));
- EXPECT_DEATH(Strtol(array - 1, NULL, 0), LeftOOBErrorMessage(1));
+ 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), RightOOBErrorMessage(0));
+ 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), RightOOBErrorMessage(0));
+ EXPECT_DEATH(Strtol(array, NULL, 36), RightOOBReadMessage(0));
// Add terminating zero to get rid of overflow.
array[2] = '\0';
Strtol(array, NULL, 36);
@@ -1432,11 +1617,11 @@ void RunStrtolOOBTest(PointerToCallStrtol Strtol) {
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), RightOOBErrorMessage(0));
+ EXPECT_DEATH(Strtol(array, NULL, 0), RightOOBReadMessage(0));
array[2] = '+';
- EXPECT_DEATH(Strtol(array, NULL, 0), RightOOBErrorMessage(0));
+ EXPECT_DEATH(Strtol(array, NULL, 0), RightOOBReadMessage(0));
array[2] = '-';
- EXPECT_DEATH(Strtol(array, NULL, 0), RightOOBErrorMessage(0));
+ EXPECT_DEATH(Strtol(array, NULL, 0), RightOOBReadMessage(0));
array[1] = '+';
Strtol(array, NULL, 0);
array[1] = array[2] = 'z';
@@ -1444,7 +1629,7 @@ void RunStrtolOOBTest(PointerToCallStrtol Strtol) {
EXPECT_EQ(array, endptr);
Strtol(array + 2, NULL, 0);
EXPECT_EQ(array, endptr);
- delete array;
+ free(array);
}
TEST(AddressSanitizer, StrtollOOBTest) {
@@ -1463,7 +1648,7 @@ 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), RightOOBErrorMessage(0));
+ EXPECT_DEATH(MemSet(array, 0, 101), RightOOBWriteMessage(0));
free(array);
}
@@ -1471,7 +1656,7 @@ 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), RightOOBErrorMessage(0));
+ EXPECT_DEATH(MemTransfer(dst, src, 101), RightOOBWriteMessage(0));
free(src);
free(dst);
}
@@ -1482,12 +1667,37 @@ TEST(AddressSanitizer, DISABLED_MemIntrinsicCallByPointerTest) {
CallMemTransferByPointer(&memmove);
}
+#if defined(__linux__) && !defined(ANDROID) && !defined(__ANDROID__)
+#define READ_TEST(READ_N_BYTES) \
+ char *x = new char[10]; \
+ int fd = open("/proc/self/stat", O_RDONLY); \
+ ASSERT_GT(fd, 0); \
+ EXPECT_DEATH(READ_N_BYTES, \
+ ASAN_PCRE_DOTALL \
+ "AddressSanitizer: heap-buffer-overflow" \
+ ".* is located 0 bytes to the right of 10-byte region"); \
+ close(fd); \
+ delete [] x; \
+
+TEST(AddressSanitizer, pread) {
+ READ_TEST(pread(fd, x, 15, 0));
+}
+
+TEST(AddressSanitizer, pread64) {
+ READ_TEST(pread64(fd, x, 15, 0));
+}
+
+TEST(AddressSanitizer, read) {
+ READ_TEST(read(fd, x, 15));
+}
+#endif // defined(__linux__) && !defined(ANDROID) && !defined(__ANDROID__)
+
// This test case fails
// Clang optimizes memcpy/memset calls which lead to unaligned access
TEST(AddressSanitizer, DISABLED_MemIntrinsicUnalignedAccessTest) {
int size = Ident(4096);
char *s = Ident((char*)malloc(size));
- EXPECT_DEATH(memset(s + size - 1, 0, 2), RightOOBErrorMessage(0));
+ EXPECT_DEATH(memset(s + size - 1, 0, 2), RightOOBWriteMessage(0));
free(s);
}
@@ -1529,7 +1739,7 @@ NOINLINE static int LargeFunction(bool do_bad_access) {
TEST(AddressSanitizer, DISABLED_LargeFunctionSymbolizeTest) {
int failing_line = LargeFunction(false);
char expected_warning[128];
- sprintf(expected_warning, "LargeFunction.*asan_test.cc:%d", failing_line);
+ sprintf(expected_warning, "LargeFunction.*asan_test.*:%d", failing_line);
EXPECT_DEATH(LargeFunction(true), expected_warning);
}
@@ -1542,19 +1752,30 @@ TEST(AddressSanitizer, DISABLED_MallocFreeUnwindAndSymbolizeTest) {
"malloc_fff.*malloc_eee.*malloc_ddd");
}
+static bool TryToSetThreadName(const char *name) {
+#if defined(__linux__) && defined(PR_SET_NAME)
+ return 0 == prctl(PR_SET_NAME, (unsigned long)name, 0, 0, 0);
+#else
+ return false;
+#endif
+}
+
void *ThreadedTestAlloc(void *a) {
+ EXPECT_EQ(true, TryToSetThreadName("AllocThr"));
int **p = (int**)a;
*p = new int;
return 0;
}
void *ThreadedTestFree(void *a) {
+ EXPECT_EQ(true, TryToSetThreadName("FreeThr"));
int **p = (int**)a;
delete *p;
return 0;
}
void *ThreadedTestUse(void *a) {
+ EXPECT_EQ(true, TryToSetThreadName("UseThr"));
int **p = (int**)a;
**p = 1;
return 0;
@@ -1563,12 +1784,12 @@ void *ThreadedTestUse(void *a) {
void ThreadedTestSpawn() {
pthread_t t;
int *x;
- pthread_create(&t, 0, ThreadedTestAlloc, &x);
- pthread_join(t, 0);
- pthread_create(&t, 0, ThreadedTestFree, &x);
- pthread_join(t, 0);
- pthread_create(&t, 0, ThreadedTestUse, &x);
- pthread_join(t, 0);
+ PTHREAD_CREATE(&t, 0, ThreadedTestAlloc, &x);
+ PTHREAD_JOIN(t, 0);
+ PTHREAD_CREATE(&t, 0, ThreadedTestFree, &x);
+ PTHREAD_JOIN(t, 0);
+ PTHREAD_CREATE(&t, 0, ThreadedTestUse, &x);
+ PTHREAD_JOIN(t, 0);
}
TEST(AddressSanitizer, ThreadedTest) {
@@ -1579,14 +1800,38 @@ TEST(AddressSanitizer, ThreadedTest) {
".*Thread T.*created");
}
+void *ThreadedTestFunc(void *unused) {
+ // Check if prctl(PR_SET_NAME) is supported. Return if not.
+ if (!TryToSetThreadName("TestFunc"))
+ return 0;
+ EXPECT_DEATH(ThreadedTestSpawn(),
+ ASAN_PCRE_DOTALL
+ "WRITE .*thread T. .UseThr."
+ ".*freed by thread T. .FreeThr. here:"
+ ".*previously allocated by thread T. .AllocThr. here:"
+ ".*Thread T. .UseThr. created by T.*TestFunc"
+ ".*Thread T. .FreeThr. created by T"
+ ".*Thread T. .AllocThr. created by T"
+ "");
+ return 0;
+}
+
+TEST(AddressSanitizer, ThreadNamesTest) {
+ // Run ThreadedTestFunc in a separate thread because it tries to set a
+ // thread name and we don't want to change the main thread's name.
+ pthread_t t;
+ PTHREAD_CREATE(&t, 0, ThreadedTestFunc, 0);
+ PTHREAD_JOIN(t, 0);
+}
+
#if ASAN_NEEDS_SEGV
TEST(AddressSanitizer, ShadowGapTest) {
-#if __WORDSIZE == 32
+#if SANITIZER_WORDSIZE == 32
char *addr = (char*)0x22000000;
#else
char *addr = (char*)0x0000100000080000;
#endif
- EXPECT_DEATH(*addr = 1, "AddressSanitizer crashed on unknown");
+ EXPECT_DEATH(*addr = 1, "AddressSanitizer: SEGV on unknown");
}
#endif // ASAN_NEEDS_SEGV
@@ -1673,7 +1918,7 @@ TEST(AddressSanitizer, FileNameInGlobalReportTest) {
static char zoo[10];
const char *p = Ident(zoo);
// The file name should be present in the report.
- EXPECT_DEATH(Ident(p[15]), "zoo.*asan_test.cc");
+ EXPECT_DEATH(Ident(p[15]), "zoo.*asan_test.");
}
int *ReturnsPointerToALocalObject() {
@@ -1688,7 +1933,7 @@ TEST(AddressSanitizer, LocalReferenceReturnTest) {
// Call 'f' a few more times, 'p' should still be poisoned.
for (int i = 0; i < 32; i++)
f();
- EXPECT_DEATH(*p = 1, "AddressSanitizer stack-use-after-return");
+ EXPECT_DEATH(*p = 1, "AddressSanitizer: stack-use-after-return");
EXPECT_DEATH(*p = 1, "is located.*in frame .*ReturnsPointerToALocal");
}
#endif
@@ -1726,10 +1971,10 @@ TEST(AddressSanitizer, ThreadedStressStackReuseTest) {
const int kNumThreads = 20;
pthread_t t[kNumThreads];
for (int i = 0; i < kNumThreads; i++) {
- pthread_create(&t[i], 0, (void* (*)(void *x))LotsOfStackReuse, 0);
+ PTHREAD_CREATE(&t[i], 0, (void* (*)(void *x))LotsOfStackReuse, 0);
}
for (int i = 0; i < kNumThreads; i++) {
- pthread_join(t[i], 0);
+ PTHREAD_JOIN(t[i], 0);
}
}
@@ -1741,8 +1986,8 @@ static void *PthreadExit(void *a) {
TEST(AddressSanitizer, PthreadExitTest) {
pthread_t t;
for (int i = 0; i < 1000; i++) {
- pthread_create(&t, 0, PthreadExit, 0);
- pthread_join(t, 0);
+ PTHREAD_CREATE(&t, 0, PthreadExit, 0);
+ PTHREAD_JOIN(t, 0);
}
}
@@ -1782,7 +2027,7 @@ TEST(AddressSanitizer, LargeStructCopyTest) {
*Ident(&a) = *Ident(&a);
}
-__attribute__((no_address_safety_analysis))
+ATTRIBUTE_NO_ADDRESS_SAFETY_ANALYSIS
static void NoAddressSafety() {
char *foo = new char[10];
Ident(foo)[10] = 0;
@@ -1793,6 +2038,29 @@ 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__)
+static string MismatchStr(const string &str) {
+ return string("AddressSanitizer: alloc-dealloc-mismatch \\(") + str;
+}
+
+TEST(AddressSanitizer, AllocDeallocMismatch) {
+ EXPECT_DEATH(free(Ident(new int)),
+ MismatchStr("operator new vs free"));
+ EXPECT_DEATH(free(Ident(new int[2])),
+ MismatchStr("operator new \\[\\] vs free"));
+ EXPECT_DEATH(delete (Ident(new int[2])),
+ MismatchStr("operator new \\[\\] vs operator delete"));
+ EXPECT_DEATH(delete (Ident((int*)malloc(2 * sizeof(int)))),
+ MismatchStr("malloc vs operator delete"));
+ EXPECT_DEATH(delete [] (Ident(new int)),
+ MismatchStr("operator new vs operator delete \\[\\]"));
+ EXPECT_DEATH(delete [] (Ident((int*)malloc(2 * sizeof(int)))),
+ MismatchStr("malloc vs operator delete \\[\\]"));
+}
+#endif
+
// ------------------ demo tests; run each one-by-one -------------
// e.g. --gtest_filter=*DemoOOBLeftHigh --gtest_also_run_disabled_tests
TEST(AddressSanitizer, DISABLED_DemoThreadedTest) {
@@ -1811,8 +2079,8 @@ TEST(AddressSanitizer, DISABLED_DemoStackTest) {
TEST(AddressSanitizer, DISABLED_DemoThreadStackTest) {
pthread_t t;
- pthread_create(&t, 0, SimpleBugOnSTack, 0);
- pthread_join(t, 0);
+ PTHREAD_CREATE(&t, 0, SimpleBugOnSTack, 0);
+ PTHREAD_JOIN(t, 0);
}
TEST(AddressSanitizer, DISABLED_DemoUAFLowIn) {
@@ -1846,7 +2114,7 @@ TEST(AddressSanitizer, DISABLED_DemoOOBRightHigh) {
}
TEST(AddressSanitizer, DISABLED_DemoOOM) {
- size_t size = __WORDSIZE == 64 ? (size_t)(1ULL << 40) : (0xf0000000);
+ size_t size = SANITIZER_WORDSIZE == 64 ? (size_t)(1ULL << 40) : (0xf0000000);
printf("%p\n", malloc(size));
}
@@ -1878,7 +2146,7 @@ TEST(AddressSanitizer, DISABLED_DemoTooMuchMemoryTest) {
char *x = (char*)malloc(kAllocSize);
memset(x, 0, kAllocSize);
total_size += kAllocSize;
- fprintf(stderr, "total: %ldM\n", (long)total_size >> 20);
+ fprintf(stderr, "total: %ldM %p\n", (long)total_size >> 20, x);
}
}
@@ -1888,7 +2156,7 @@ TEST(AddressSanitizer, BufferOverflowAfterManyFrees) {
delete [] (Ident(new char [8644]));
}
char *x = new char[8192];
- EXPECT_DEATH(x[Ident(8192)] = 0, "AddressSanitizer heap-buffer-overflow");
+ EXPECT_DEATH(x[Ident(8192)] = 0, "AddressSanitizer: heap-buffer-overflow");
delete [] Ident(x);
}
@@ -1902,8 +2170,8 @@ TEST(AddressSanitizerMac, CFAllocatorDefaultDoubleFree) {
void CFAllocator_DoubleFreeOnPthread() {
pthread_t child;
- pthread_create(&child, NULL, CFAllocatorDefaultDoubleFree, NULL);
- pthread_join(child, NULL); // Shouldn't be reached.
+ PTHREAD_CREATE(&child, NULL, CFAllocatorDefaultDoubleFree, NULL);
+ PTHREAD_JOIN(child, NULL); // Shouldn't be reached.
}
TEST(AddressSanitizerMac, CFAllocatorDefaultDoubleFree_ChildPhread) {
@@ -1928,10 +2196,10 @@ void *CFAllocatorDeallocateFromGlob(void *unused) {
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);
+ PTHREAD_CREATE(&th1, NULL, CFAllocatorAllocateToGlob, NULL);
+ PTHREAD_JOIN(th1, NULL);
+ PTHREAD_CREATE(&th2, NULL, CFAllocatorDeallocateFromGlob, NULL);
+ PTHREAD_JOIN(th2, NULL);
}
TEST(AddressSanitizerMac, CFAllocator_PassMemoryToAnotherThread) {
@@ -1958,53 +2226,56 @@ 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 and word");
+ 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 and word");
+ 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 and word");
+ 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 and word");
+ 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 and word");
+ 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 and word");
+ 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 and word");
+ 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 and word");
+ EXPECT_DEATH(TestGCDGroupAsync(), "Shadow byte legend");
}
void *MallocIntrospectionLockWorker(void *_) {
@@ -2047,13 +2318,13 @@ TEST(AddressSanitizerMac, MallocIntrospectionLock) {
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(&workers[i], 0, MallocIntrospectionLockWorker, 0);
}
- pthread_create(&forker, 0, MallocIntrospectionLockForker, 0);
+ PTHREAD_CREATE(&forker, 0, MallocIntrospectionLockForker, 0);
for (i = 0; i < kNumWorkers; i++) {
- pthread_join(workers[i], 0);
+ PTHREAD_JOIN(workers[i], 0);
}
- pthread_join(forker, 0);
+ PTHREAD_JOIN(forker, 0);
}
}
@@ -2069,8 +2340,8 @@ 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_CREATE(&th, NULL, TSDAllocWorker, &test_key);
+ PTHREAD_JOIN(th, NULL);
pthread_key_delete(test_key);
}
@@ -2092,6 +2363,19 @@ TEST(AddressSanitizerMac, NSObjectOOB) {
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
@@ -2102,11 +2386,4 @@ TEST(AddressSanitizer, LongDoubleNegativeTest) {
static long double c;
memcpy(Ident(&a), Ident(&b), sizeof(long double));
memcpy(Ident(&c), Ident(&b), sizeof(long double));
-};
-
-int main(int argc, char **argv) {
- progname = argv[0];
- testing::GTEST_FLAG(death_test_style) = "threadsafe";
- testing::InitGoogleTest(&argc, argv);
- return RUN_ALL_TESTS();
}
diff --git a/lib/asan/tests/asan_test.ignore b/lib/asan/tests/asan_test.ignore
index 7bafa83bf62e..ea5c26099e75 100644
--- a/lib/asan/tests/asan_test.ignore
+++ b/lib/asan/tests/asan_test.ignore
@@ -1,2 +1,3 @@
+# blacklisted functions for instrumented ASan unit test
fun:*IgnoreTest*
fun:*SomeOtherFunc*
diff --git a/lib/asan/tests/asan_test_config.h b/lib/asan/tests/asan_test_config.h
index 6cf0e6958484..1d28e99a4b10 100644
--- a/lib/asan/tests/asan_test_config.h
+++ b/lib/asan/tests/asan_test_config.h
@@ -1,4 +1,4 @@
-//===-- asan_test_config.h ------------*- C++ -*-===//
+//===-- asan_test_config.h --------------------------------------*- C++ -*-===//
//
// The LLVM Compiler Infrastructure
//
@@ -10,6 +10,10 @@
// This file is a part of AddressSanitizer, an address sanity checker.
//
//===----------------------------------------------------------------------===//
+#if !defined(INCLUDED_FROM_ASAN_TEST_UTILS_H)
+# error "This file should be included into asan_test_utils.h only"
+#endif
+
#ifndef ASAN_TEST_CONFIG_H
#define ASAN_TEST_CONFIG_H
@@ -17,7 +21,11 @@
#include <string>
#include <map>
-#include "gtest/gtest.h"
+#if ASAN_USE_DEJAGNU_GTEST
+# include "dejagnu-gtest.h"
+#else
+# include "gtest/gtest.h"
+#endif
using std::string;
using std::vector;
@@ -40,7 +48,11 @@ using std::map;
#endif
#ifndef ASAN_LOW_MEMORY
-#define ASAN_LOW_MEMORY 0
+# define ASAN_LOW_MEMORY 0
+#endif
+
+#ifndef ASAN_AVOID_EXPENSIVE_TESTS
+# define ASAN_AVOID_EXPENSIVE_TESTS 0
#endif
#define ASAN_PCRE_DOTALL ""
diff --git a/lib/asan/tests/asan_break_optimization.cc b/lib/asan/tests/asan_test_main.cc
index 022a9f8b8505..1746c5f4837b 100644
--- a/lib/asan/tests/asan_break_optimization.cc
+++ b/lib/asan/tests/asan_test_main.cc
@@ -1,4 +1,4 @@
-//===-- asan_break_optimization.cc ----------------------===//
+//===-- asan_test_main.cc -------------------------------------------------===//
//
// The LLVM Compiler Infrastructure
//
@@ -10,10 +10,10 @@
// This file is a part of AddressSanitizer, an address sanity checker.
//
//===----------------------------------------------------------------------===//
-
#include "asan_test_utils.h"
-// Have this function in a separate file to avoid inlining.
-// (Yes, we know about cross-file inlining, but let's assume we don't use it).
-extern "C" void break_optimization(void *x) {
- (void)x;
+
+int main(int argc, char **argv) {
+ testing::GTEST_FLAG(death_test_style) = "threadsafe";
+ testing::InitGoogleTest(&argc, argv);
+ return RUN_ALL_TESTS();
}
diff --git a/lib/asan/tests/asan_test_utils.h b/lib/asan/tests/asan_test_utils.h
index fb509cc43e30..6ed9f90df906 100644
--- a/lib/asan/tests/asan_test_utils.h
+++ b/lib/asan/tests/asan_test_utils.h
@@ -1,4 +1,4 @@
-//===-- asan_test_utils.h ------------*- C++ -*-===//
+//===-- asan_test_utils.h ---------------------------------------*- C++ -*-===//
//
// The LLVM Compiler Infrastructure
//
@@ -14,43 +14,16 @@
#ifndef ASAN_TEST_UTILS_H
#define ASAN_TEST_UTILS_H
-#if defined(_WIN32)
-typedef unsigned __int8 uint8_t;
-typedef unsigned __int16 uint16_t;
-typedef unsigned __int32 uint32_t;
-typedef unsigned __int64 uint64_t;
-typedef __int8 int8_t;
-typedef __int16 int16_t;
-typedef __int32 int32_t;
-typedef __int64 int64_t;
-# define NOINLINE __declspec(noinline)
-#else // defined(_WIN32)
-# define NOINLINE __attribute__((noinline))
-#endif // defined(_WIN32)
-
-#if !defined(__has_feature)
-#define __has_feature(x) 0
-#endif
-
-#ifndef __WORDSIZE
-#if __LP64__ || defined(_WIN64)
-#define __WORDSIZE 64
-#else
-#define __WORDSIZE 32
-#endif
+#if !defined(ASAN_EXTERNAL_TEST_CONFIG)
+# define INCLUDED_FROM_ASAN_TEST_UTILS_H
+# include "asan_test_config.h"
+# undef INCLUDED_FROM_ASAN_TEST_UTILS_H
#endif
-// Make the compiler think that something is going on there.
-extern "C" void break_optimization(void *);
+#include "sanitizer_common/tests/sanitizer_test_utils.h"
-// This function returns its parameter but in such a way that compiler
-// can not prove it.
-template<class T>
-NOINLINE
-static T Ident(T t) {
- T ret = t;
- break_optimization(&ret);
- return ret;
-}
+// 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))
#endif // ASAN_TEST_UTILS_H