diff options
author | Alexey Samsonov <samsonov@google.com> | 2013-09-16 15:50:53 +0000 |
---|---|---|
committer | Alexey Samsonov <samsonov@google.com> | 2013-09-16 15:50:53 +0000 |
commit | 05fa3808f6ac96023cdf583a1a1b7220e5b451b8 (patch) | |
tree | 61a33375c8c0db94fbf2fcf72a641ab1a48250cf | |
parent | 22e21b044c9337a2fa921f268b7d221c693ad78b (diff) | |
download | compiler-rt-05fa3808f6ac96023cdf583a1a1b7220e5b451b8.tar.gz compiler-rt-05fa3808f6ac96023cdf583a1a1b7220e5b451b8.tar.bz2 compiler-rt-05fa3808f6ac96023cdf583a1a1b7220e5b451b8.tar.xz |
[ASan] Split ASan unit tests into two different binaries:
(1) instrumented, i.e. compiled and linked with -fsanitize=address
(2) not instrumented, compiled w/o -fsanitize=address and linked with ASan runtime statically.
git-svn-id: https://llvm.org/svn/llvm-project/compiler-rt/trunk@190788 91177308-0d34-0410-b5e6-96231b3b80d8
-rw-r--r-- | cmake/Modules/AddCompilerRT.cmake | 12 | ||||
-rw-r--r-- | lib/asan/CMakeLists.txt | 42 | ||||
-rw-r--r-- | lib/asan/tests/CMakeLists.txt | 166 | ||||
-rw-r--r-- | lib/asan/tests/asan_interface_test.cc | 426 | ||||
-rw-r--r-- | lib/asan/tests/asan_noinst_test.cc | 502 | ||||
-rw-r--r-- | lib/asan/tests/asan_test.cc | 29 |
6 files changed, 643 insertions, 534 deletions
diff --git a/cmake/Modules/AddCompilerRT.cmake b/cmake/Modules/AddCompilerRT.cmake index 1ece3eb9..021920c9 100644 --- a/cmake/Modules/AddCompilerRT.cmake +++ b/cmake/Modules/AddCompilerRT.cmake @@ -6,13 +6,16 @@ include(CompilerRTUtils) # with name "<name>.<arch>" if architecture can be targeted. # add_compiler_rt_object_library(<name> <arch> # SOURCES <source files> -# CFLAGS <compile flags>) +# CFLAGS <compile flags> +# DEFS <compile definitions>) macro(add_compiler_rt_object_library name arch) if(CAN_TARGET_${arch}) - parse_arguments(LIB "SOURCES;CFLAGS" "" ${ARGN}) + parse_arguments(LIB "SOURCES;CFLAGS;DEFS" "" ${ARGN}) add_library(${name}.${arch} OBJECT ${LIB_SOURCES}) set_target_compile_flags(${name}.${arch} ${TARGET_${arch}_CFLAGS} ${LIB_CFLAGS}) + set_property(TARGET ${name}.${arch} APPEND PROPERTY + COMPILE_DEFINITIONS ${LIB_DEFS}) else() message(FATAL_ERROR "Archtecture ${arch} can't be targeted") endif() @@ -23,12 +26,15 @@ endmacro() # add_compiler_rt_osx_object_library(<name> ARCH <architectures> # SOURCES <source files> # CFLAGS <compile flags>) +# DEFS <compile definitions>) macro(add_compiler_rt_osx_object_library name) - parse_arguments(LIB "ARCH;SOURCES;CFLAGS" "" ${ARGN}) + parse_arguments(LIB "ARCH;SOURCES;CFLAGS;DEFS" "" ${ARGN}) set(libname "${name}.osx") add_library(${libname} OBJECT ${LIB_SOURCES}) set_target_compile_flags(${libname} ${LIB_CFLAGS}) set_target_properties(${libname} PROPERTIES OSX_ARCHITECTURES "${LIB_ARCH}") + set_property(TARGET ${libname} APPEND PROPERTY + COMPILE_DEFINITIONS ${LIB_DEFS}) endmacro() # Adds static runtime for a given architecture and puts it in the proper diff --git a/lib/asan/CMakeLists.txt b/lib/asan/CMakeLists.txt index b590ffc3..17552b3a 100644 --- a/lib/asan/CMakeLists.txt +++ b/lib/asan/CMakeLists.txt @@ -21,9 +21,6 @@ set(ASAN_SOURCES asan_thread.cc asan_win.cc) -set(ASAN_DYLIB_SOURCES - ${ASAN_SOURCES}) - include_directories(..) if (NOT MSVC) @@ -58,12 +55,32 @@ endif() filter_available_targets(ASAN_SUPPORTED_ARCH x86_64 i386 powerpc64) +# Compile ASan sources into an object library. +if(APPLE) + add_compiler_rt_osx_object_library(RTAsan + ARCH ${ASAN_SUPPORTED_ARCH} + SOURCES ${ASAN_SOURCES} + CFLAGS ${ASAN_CFLAGS} + DEFS ${ASAN_COMMON_DEFINITIONS}) +elseif(ANDROID) + add_library(RTAsan.arm.android OBJECT ${ASAN_SOURCES}) + set_target_compile_flags(RTAsan.arm.android ${ASAN_CFLAGS}) + set_property(TARGET RTAsan.arm.android APPEND PROPERTY + COMPILE_DEFINITIONS ${ASAN_COMMON_DEFINITIONS}) +else() + foreach(arch ${ASAN_SUPPORTED_ARCH}) + add_compiler_rt_object_library(RTAsan ${arch} + SOURCES ${ASAN_SOURCES} CFLAGS ${ASAN_CFLAGS} + DEFS ${ASAN_COMMON_DEFINITIONS}) + endforeach() +endif() + +# Build ASan runtimes shipped with Clang. set(ASAN_RUNTIME_LIBRARIES) if(APPLE) - # Build universal binary on APPLE. add_compiler_rt_osx_dynamic_runtime(clang_rt.asan_osx_dynamic ARCH ${ASAN_SUPPORTED_ARCH} - SOURCES ${ASAN_DYLIB_SOURCES} + SOURCES $<TARGET_OBJECTS:RTAsan.osx> $<TARGET_OBJECTS:RTInterception.osx> $<TARGET_OBJECTS:RTSanitizerCommon.osx> $<TARGET_OBJECTS:RTLSanCommon.osx> @@ -76,10 +93,9 @@ if(APPLE) list(APPEND ASAN_RUNTIME_LIBRARIES clang_rt.asan_osx_dynamic) elseif(ANDROID) add_library(clang_rt.asan-arm-android SHARED - ${ASAN_SOURCES} + $<TARGET_OBJECTS:RTAsan.arm.android> $<TARGET_OBJECTS:RTInterception.arm.android> - $<TARGET_OBJECTS:RTSanitizerCommon.arm.android> - ) + $<TARGET_OBJECTS:RTSanitizerCommon.arm.android>) set_target_compile_flags(clang_rt.asan-arm-android ${ASAN_CFLAGS}) set_property(TARGET clang_rt.asan-arm-android APPEND PROPERTY @@ -87,20 +103,20 @@ elseif(ANDROID) 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. - + # Build separate libraries for each target. foreach(arch ${ASAN_SUPPORTED_ARCH}) - set(ASAN_SOURCE_LIBS + set(ASAN_RUNTIME_OBJECTS + $<TARGET_OBJECTS:RTAsan.${arch}> $<TARGET_OBJECTS:RTInterception.${arch}> $<TARGET_OBJECTS:RTSanitizerCommon.${arch}> $<TARGET_OBJECTS:RTSanitizerCommonLibc.${arch}>) if (NOT MSVC) # We can't build Leak Sanitizer on Windows yet. - list(APPEND ASAN_SOURCE_LIBS $<TARGET_OBJECTS:RTLSanCommon.${arch}>) + list(APPEND ASAN_RUNTIME_OBJECTS $<TARGET_OBJECTS:RTLSanCommon.${arch}>) endif() add_compiler_rt_static_runtime(clang_rt.asan-${arch} ${arch} - SOURCES ${ASAN_SOURCES} ${ASAN_SOURCE_LIBS} + SOURCES ${ASAN_RUNTIME_OBJECTS} CFLAGS ${ASAN_CFLAGS} DEFS ${ASAN_COMMON_DEFINITIONS}) list(APPEND ASAN_RUNTIME_LIBRARIES clang_rt.asan-${arch}) diff --git a/lib/asan/tests/CMakeLists.txt b/lib/asan/tests/CMakeLists.txt index ffe16147..8600fc0b 100644 --- a/lib/asan/tests/CMakeLists.txt +++ b/lib/asan/tests/CMakeLists.txt @@ -39,10 +39,6 @@ set(ASAN_UNITTEST_COMMON_CFLAGS -Werror=sign-compare -g -O2) - -if(ASAN_TESTS_USE_ZERO_BASE_SHADOW) - list(APPEND ASAN_UNITTEST_COMMON_CFLAGS -fPIE) -endif() if(SUPPORTS_NO_VARIADIC_MACROS_FLAG) list(APPEND ASAN_UNITTEST_COMMON_CFLAGS -Wno-variadic-macros) endif() @@ -62,24 +58,7 @@ else() -DASAN_NEEDS_SEGV=1) endif() -set(ASAN_LINK_FLAGS) -if(ASAN_TESTS_USE_ZERO_BASE_SHADOW) - list(APPEND ASAN_LINK_FLAGS -pie) -endif() -# On Android, we link with ASan runtime manually. On other platforms we depend -# on Clang driver behavior, passing -fsanitize=address flag. -if(NOT ANDROID) - list(APPEND ASAN_LINK_FLAGS -fsanitize=address) -endif() -# Unit tests on Mac depend on Foundation. -if(APPLE) - list(APPEND ASAN_LINK_FLAGS -framework Foundation) -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} -fsanitize=address @@ -95,11 +74,30 @@ if(ASAN_TESTS_USE_ZERO_BASE_SHADOW) -fsanitize-address-zero-base-shadow) endif() +# Unit tests require libstdc++. +set(ASAN_UNITTEST_COMMON_LINKFLAGS -lstdc++) +# Unit tests on Mac depend on Foundation. +if(APPLE) + list(APPEND ASAN_UNITTEST_COMMON_LINKFLAGS -framework Foundation) +endif() + +set(ASAN_UNITTEST_INSTRUMENTED_LINKFLAGS + ${ASAN_UNITTEST_COMMON_LINKFLAGS}) +# On Android, we link with ASan runtime manually. On other platforms we depend +# on Clang driver behavior, passing -fsanitize=address flag. +if(NOT ANDROID) + list(APPEND ASAN_UNITTEST_INSTRUMENTED_LINKFLAGS -fsanitize=address) +endif() + +set(ASAN_UNITTEST_NOINST_LINKFLAGS + ${ASAN_UNITTEST_COMMON_LINKFLAGS} + -lpthread -ldl) + # 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") + set(output_obj "${obj_list}.${basename}.${arch}.o") get_target_flags_for_arch(${arch} TARGET_CFLAGS) clang_compile(${output_obj} ${source} CFLAGS ${ARGN} ${TARGET_CFLAGS} @@ -110,13 +108,19 @@ macro(asan_compile obj_list source arch) endmacro() # Link ASan unit test for a given architecture from a set -# of objects in ${ARGN}. +# of objects in with given linker flags. macro(add_asan_test test_suite test_name arch) + parse_arguments(TEST "OBJECTS;LINKFLAGS" "WITH_TEST_RUNTIME" ${ARGN}) get_target_flags_for_arch(${arch} TARGET_LINK_FLAGS) + set(TEST_DEPS ${ASAN_RUNTIME_LIBRARIES} ${TEST_OBJECTS}) + if(TEST_WITH_TEST_RUNTIME) + list(APPEND TEST_DEPS ${ASAN_TEST_RUNTIME}) + list(APPEND TEST_OBJECTS lib${ASAN_TEST_RUNTIME}.a) + endif() add_compiler_rt_test(${test_suite} ${test_name} - OBJECTS ${ARGN} - DEPS ${ASAN_RUNTIME_LIBRARIES} ${ARGN} - LINK_FLAGS ${ASAN_LINK_FLAGS} + OBJECTS ${TEST_OBJECTS} + DEPS ${TEST_DEPS} + LINK_FLAGS ${TEST_LINKFLAGS} ${TARGET_LINK_FLAGS}) endmacro() @@ -127,64 +131,90 @@ set_target_properties(AsanUnitTests PROPERTIES FOLDER "ASan unit tests") add_custom_target(AsanBenchmarks) set_target_properties(AsanBenchmarks PROPERTIES FOLDER "Asan benchmarks") -if (NOT APPLE AND NOT ANDROID) -# Do not build asan_fake_stack_test.cc on OS X and Android, since it requires -# exposing additional functions from the runtime. -# See also https://code.google.com/p/address-sanitizer/issues/detail?id=222 -set(ASAN_NOINST_TEST_SOURCES - asan_fake_stack_test.cc - asan_noinst_test.cc - asan_test_main.cc) -else() set(ASAN_NOINST_TEST_SOURCES + ${COMPILER_RT_GTEST_SOURCE} asan_noinst_test.cc asan_test_main.cc) +if(NOT APPLE AND NOT ANDROID) + # Do not build asan_fake_stack_test.cc on OS X and Android, since it requires + # exposing additional functions from the runtime. + # See also https://code.google.com/p/address-sanitizer/issues/detail?id=222 + list(APPEND ASAN_NOINST_TEST_SOURCES asan_fake_stack_test.cc) endif() set(ASAN_INST_TEST_SOURCES + ${COMPILER_RT_GTEST_SOURCE} asan_globals_test.cc + asan_interface_test.cc asan_test.cc asan_oob_test.cc asan_mem_test.cc - asan_str_test.cc) + asan_str_test.cc + asan_test_main.cc) +if(APPLE) + list(APPEND ASAN_INST_TEST_SOURCES asan_mac_test.cc) +endif() + +set(ASAN_BENCHMARKS_SOURCES + ${COMPILER_RT_GTEST_SOURCE} + asan_benchmarks_test.cc) # 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) foreach(src ${ASAN_INST_TEST_SOURCES}) asan_compile(ASAN_INST_TEST_OBJECTS ${src} ${arch} ${ASAN_UNITTEST_INSTRUMENTED_CFLAGS}) endforeach() - # Add Mac-specific tests. if (APPLE) - asan_compile(ASAN_INST_TEST_OBJECTS asan_mac_test.cc ${arch} - ${ASAN_UNITTEST_INSTRUMENTED_CFLAGS}) + # Add Mac-specific helper. asan_compile(ASAN_INST_TEST_OBJECTS asan_mac_test_helpers.mm ${arch} ${ASAN_UNITTEST_INSTRUMENTED_CFLAGS} -ObjC) endif() + add_asan_test(AsanUnitTests "Asan-${arch}-Test" ${arch} + OBJECTS ${ASAN_INST_TEST_OBJECTS} + LINKFLAGS ${ASAN_UNITTEST_INSTRUMENTED_LINKFLAGS}) + + # Add static ASan runtime that will be linked with uninstrumented tests. + set(ASAN_TEST_RUNTIME RTAsanTest.${arch}) + if(APPLE) + set(ASAN_TEST_RUNTIME_OBJECTS + $<TARGET_OBJECTS:RTAsan.osx> + $<TARGET_OBJECTS:RTInterception.osx> + $<TARGET_OBJECTS:RTSanitizerCommon.osx> + $<TARGET_OBJECTS:RTLSanCommon.osx>) + else() + set(ASAN_TEST_RUNTIME_OBJECTS + $<TARGET_OBJECTS:RTAsan.${arch}> + $<TARGET_OBJECTS:RTInterception.${arch}> + $<TARGET_OBJECTS:RTLSanCommon.${arch}> + $<TARGET_OBJECTS:RTSanitizerCommon.${arch}> + $<TARGET_OBJECTS:RTSanitizerCommonLibc.${arch}>) + endif() + add_library(${ASAN_TEST_RUNTIME} STATIC ${ASAN_TEST_RUNTIME_OBJECTS}) + set_target_properties(${ASAN_TEST_RUNTIME} PROPERTIES + ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}) # Uninstrumented tests. set(ASAN_NOINST_TEST_OBJECTS) foreach(src ${ASAN_NOINST_TEST_SOURCES}) asan_compile(ASAN_NOINST_TEST_OBJECTS ${src} ${arch} ${ASAN_UNITTEST_COMMON_CFLAGS}) endforeach() - # Link everything together. - add_asan_test(AsanUnitTests "Asan-${arch}-Test" ${arch} - ${ASAN_NOINST_TEST_OBJECTS} - ${ASAN_INST_TEST_OBJECTS} ${ASAN_INST_GTEST}) + add_asan_test(AsanUnitTests "Asan-${arch}-Noinst-Test" ${arch} + OBJECTS ${ASAN_NOINST_TEST_OBJECTS} + LINKFLAGS ${ASAN_UNITTEST_NOINST_LINKFLAGS} + WITH_TEST_RUNTIME) - # Instrumented benchmarks. + # Benchmarks. set(ASAN_BENCHMARKS_OBJECTS) - asan_compile(ASAN_BENCHMARKS_OBJECTS asan_benchmarks_test.cc ${arch} - ${ASAN_UNITTEST_INSTRUMENTED_CFLAGS}) - # Link benchmarks. + foreach(src ${ASAN_BENCHMARKS_SOURCES}) + asan_compile(ASAN_BENCHMARKS_OBJECTS ${src} ${arch} + ${ASAN_UNITTEST_INSTRUMENTED_CFLAGS}) + endforeach() add_asan_test(AsanBenchmarks "Asan-${arch}-Benchmark" ${arch} - ${ASAN_BENCHMARKS_OBJECTS} ${ASAN_INST_GTEST}) + OBJECTS ${ASAN_BENCHMARKS_OBJECTS} + LINKFLAGS ${ASAN_UNITTEST_INSTRUMENTED_LINKFLAGS}) endmacro() if(COMPILER_RT_CAN_EXECUTE_TESTS) @@ -196,20 +226,28 @@ endif() if(ANDROID) # We assume that unit tests on Android are built in a build # tree with fresh Clang as a host compiler. - 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}) + + # Test w/o ASan instrumentation. Link it with ASan statically. + add_executable(AsanNoinstTest + $<TARGET_OBJECTS:RTAsan.arm.android> + $<TARGET_OBJECTS:RTInterception.arm.android> + $<TARGET_OBJECTS:RTSanitizerCommon.arm.android> + ${COMPILER_RT_GTEST_SOURCE} + ${ASAN_NOINST_TEST_SOURCES}) + set_target_compile_flags(AsanNoinstTest ${ASAN_UNITTEST_COMMON_CFLAGS}) + set_target_link_flags(AsanNoinstTest ${ASAN_UNITTEST_NOINST_LINKFLAGS}) + + # Test with ASan instrumentation. Link with ASan dynamic runtime. add_executable(AsanTest - $<TARGET_OBJECTS:asan_noinst_test> - $<TARGET_OBJECTS:asan_inst_test> - ) + ${COMPILER_RT_GTEST_SOURCE} + ${ASAN_INST_TEST_SOURCES}) + set_target_compile_flags(AsanTest ${ASAN_UNITTEST_INSTRUMENTED_CFLAGS}) + set_target_link_flags(AsanTest ${ASAN_UNITTEST_INSTRUMENTED_LINKFLAGS}) + target_link_libraries(AsanTest clang_rt.asan-arm-android) + # Setup correct output directory and link flags. - set_target_properties(AsanTest PROPERTIES + set_target_properties(AsanNoinstTest AsanTest PROPERTIES RUNTIME_OUTPUT_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}) - set_target_link_flags(AsanTest ${ASAN_LINK_FLAGS}) - target_link_libraries(AsanTest clang_rt.asan-arm-android) # Add unit test to test suite. - add_dependencies(AsanUnitTests AsanTest) + add_dependencies(AsanUnitTests AsanNoinstTest AsanTest) endif() diff --git a/lib/asan/tests/asan_interface_test.cc b/lib/asan/tests/asan_interface_test.cc new file mode 100644 index 00000000..f3667901 --- /dev/null +++ b/lib/asan/tests/asan_interface_test.cc @@ -0,0 +1,426 @@ +//===-- asan_interface_test.cc --------------------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file is a part of AddressSanitizer, an address sanity checker. +// +//===----------------------------------------------------------------------===// +#include "asan_test_utils.h" +#include "sanitizer/asan_interface.h" + +TEST(AddressSanitizerInterface, GetEstimatedAllocatedSize) { + EXPECT_EQ(0U, __asan_get_estimated_allocated_size(0)); + 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])); + } +} + +static const char* kGetAllocatedSizeErrorMsg = + "attempting to call __asan_get_allocated_size()"; + +TEST(AddressSanitizerInterface, GetAllocatedSizeAndOwnershipTest) { + const size_t kArraySize = 100; + char *array = Ident((char*)malloc(kArraySize)); + int *int_ptr = Ident(new int); + + // Allocated memory is owned by allocator. Allocated size should be + // equal to requested size. + EXPECT_EQ(true, __asan_get_ownership(array)); + EXPECT_EQ(kArraySize, __asan_get_allocated_size(array)); + EXPECT_EQ(true, __asan_get_ownership(int_ptr)); + EXPECT_EQ(sizeof(int), __asan_get_allocated_size(int_ptr)); + + // 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_FALSE(__asan_get_ownership(wild_addr)); + EXPECT_DEATH(__asan_get_allocated_size(wild_addr), kGetAllocatedSizeErrorMsg); + 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_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_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) { + size_t before_malloc, after_malloc, after_free; + char *array; + const size_t kMallocSize = 100; + before_malloc = __asan_get_current_allocated_bytes(); + + array = Ident((char*)malloc(kMallocSize)); + after_malloc = __asan_get_current_allocated_bytes(); + EXPECT_EQ(before_malloc + kMallocSize, after_malloc); + + free(array); + after_free = __asan_get_current_allocated_bytes(); + EXPECT_EQ(before_malloc, after_free); +} + +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 << 28) + 1; // 256M + free(Ident(malloc(kLargeMallocSize))); // Drain quarantine. + size_t 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()); + } +} + +static const size_t kManyThreadsMallocSizes[] = {5, 1UL<<10, 1UL<<14, 357}; +static const size_t kManyThreadsIterations = 250; +static const size_t kManyThreadsNumThreads = + (SANITIZER_WORDSIZE == 32) ? 40 : 200; + +static void *ManyThreadsWithStatsWorker(void *arg) { + (void)arg; + for (size_t iter = 0; iter < kManyThreadsIterations; iter++) { + for (size_t size_index = 0; size_index < 4; size_index++) { + free(Ident(malloc(kManyThreadsMallocSizes[size_index]))); + } + } + // Just one large allocation. + free(Ident(malloc(1 << 20))); + return 0; +} + +TEST(AddressSanitizerInterface, ManyThreadsWithStatsStressTest) { + size_t before_test, after_test, i; + pthread_t threads[kManyThreadsNumThreads]; + before_test = __asan_get_current_allocated_bytes(); + for (i = 0; i < kManyThreadsNumThreads; i++) { + PTHREAD_CREATE(&threads[i], 0, + (void* (*)(void *x))ManyThreadsWithStatsWorker, (void*)i); + } + for (i = 0; i < kManyThreadsNumThreads; i++) { + PTHREAD_JOIN(threads[i], 0); + } + after_test = __asan_get_current_allocated_bytes(); + // ASan stats also reflect memory usage of internal ASan RTL structs, + // so we can't check for equality here. + EXPECT_LT(after_test, before_test + (1UL<<20)); +} + +static void DoDoubleFree() { + int *x = Ident(new int); + delete Ident(x); + delete Ident(x); +} + +TEST(AddressSanitizerInterface, ExitCode) { + int original_exit_code = __asan_set_error_exit_code(7); + EXPECT_EXIT(DoDoubleFree(), ::testing::ExitedWithCode(7), ""); + EXPECT_EQ(7, __asan_set_error_exit_code(8)); + EXPECT_EXIT(DoDoubleFree(), ::testing::ExitedWithCode(8), ""); + EXPECT_EQ(8, __asan_set_error_exit_code(original_exit_code)); + EXPECT_EXIT(DoDoubleFree(), + ::testing::ExitedWithCode(original_exit_code), ""); +} + +static void MyDeathCallback() { + fprintf(stderr, "MyDeathCallback\n"); +} + +TEST(AddressSanitizerInterface, DeathCallbackTest) { + __asan_set_death_callback(MyDeathCallback); + EXPECT_DEATH(DoDoubleFree(), "MyDeathCallback"); + __asan_set_death_callback(NULL); +} + +static const char* kUseAfterPoisonErrorMessage = "use-after-poison"; + +#define GOOD_ACCESS(ptr, offset) \ + EXPECT_FALSE(__asan_address_is_poisoned(ptr + offset)) + +#define BAD_ACCESS(ptr, offset) \ + EXPECT_TRUE(__asan_address_is_poisoned(ptr + offset)) + +TEST(AddressSanitizerInterface, SimplePoisonMemoryRegionTest) { + char *array = Ident((char*)malloc(120)); + // poison array[40..80) + __asan_poison_memory_region(array + 40, 40); + GOOD_ACCESS(array, 39); + GOOD_ACCESS(array, 80); + BAD_ACCESS(array, 40); + BAD_ACCESS(array, 60); + BAD_ACCESS(array, 79); + char value; + EXPECT_DEATH(value = Ident(array[40]), kUseAfterPoisonErrorMessage); + __asan_unpoison_memory_region(array + 40, 40); + // access previously poisoned memory. + GOOD_ACCESS(array, 40); + GOOD_ACCESS(array, 79); + free(array); +} + +TEST(AddressSanitizerInterface, OverlappingPoisonMemoryRegionTest) { + char *array = Ident((char*)malloc(120)); + // Poison [0..40) and [80..120) + __asan_poison_memory_region(array, 40); + __asan_poison_memory_region(array + 80, 40); + BAD_ACCESS(array, 20); + GOOD_ACCESS(array, 60); + BAD_ACCESS(array, 100); + // Poison whole array - [0..120) + __asan_poison_memory_region(array, 120); + BAD_ACCESS(array, 60); + // Unpoison [24..96) + __asan_unpoison_memory_region(array + 24, 72); + BAD_ACCESS(array, 23); + GOOD_ACCESS(array, 24); + GOOD_ACCESS(array, 60); + GOOD_ACCESS(array, 95); + BAD_ACCESS(array, 96); + free(array); +} + +TEST(AddressSanitizerInterface, PushAndPopWithPoisoningTest) { + // Vector of capacity 20 + char *vec = Ident((char*)malloc(20)); + __asan_poison_memory_region(vec, 20); + for (size_t i = 0; i < 7; i++) { + // Simulate push_back. + __asan_unpoison_memory_region(vec + i, 1); + GOOD_ACCESS(vec, i); + BAD_ACCESS(vec, i + 1); + } + for (size_t i = 7; i > 0; i--) { + // Simulate pop_back. + __asan_poison_memory_region(vec + i - 1, 1); + BAD_ACCESS(vec, i - 1); + if (i > 1) GOOD_ACCESS(vec, i - 2); + } + free(vec); +} + +// Make sure that each aligned block of size "2^granularity" doesn't have +// "true" value before "false" value. +static void MakeShadowValid(bool *shadow, int length, int granularity) { + bool can_be_poisoned = true; + for (int i = length - 1; i >= 0; i--) { + if (!shadow[i]) + can_be_poisoned = false; + if (!can_be_poisoned) + shadow[i] = false; + if (i % (1 << granularity) == 0) { + can_be_poisoned = true; + } + } +} + +TEST(AddressSanitizerInterface, PoisoningStressTest) { + const size_t kSize = 24; + bool expected[kSize]; + char *arr = Ident((char*)malloc(kSize)); + for (size_t l1 = 0; l1 < kSize; l1++) { + for (size_t s1 = 1; l1 + s1 <= kSize; s1++) { + for (size_t l2 = 0; l2 < kSize; l2++) { + for (size_t s2 = 1; l2 + s2 <= kSize; s2++) { + // Poison [l1, l1+s1), [l2, l2+s2) and check result. + __asan_unpoison_memory_region(arr, kSize); + __asan_poison_memory_region(arr + l1, s1); + __asan_poison_memory_region(arr + l2, s2); + memset(expected, false, kSize); + memset(expected + l1, true, s1); + MakeShadowValid(expected, kSize, /*granularity*/ 3); + memset(expected + l2, true, s2); + MakeShadowValid(expected, kSize, /*granularity*/ 3); + for (size_t i = 0; i < kSize; i++) { + ASSERT_EQ(expected[i], __asan_address_is_poisoned(arr + i)); + } + // Unpoison [l1, l1+s1) and [l2, l2+s2) and check result. + __asan_poison_memory_region(arr, kSize); + __asan_unpoison_memory_region(arr + l1, s1); + __asan_unpoison_memory_region(arr + l2, s2); + memset(expected, true, kSize); + memset(expected + l1, false, s1); + MakeShadowValid(expected, kSize, /*granularity*/ 3); + memset(expected + l2, false, s2); + MakeShadowValid(expected, kSize, /*granularity*/ 3); + for (size_t i = 0; i < kSize; i++) { + ASSERT_EQ(expected[i], __asan_address_is_poisoned(arr + i)); + } + } + } + } + } + free(arr); +} + +TEST(AddressSanitizerInterface, GlobalRedzones) { + GOOD_ACCESS(glob1, 1 - 1); + GOOD_ACCESS(glob2, 2 - 1); + GOOD_ACCESS(glob3, 3 - 1); + GOOD_ACCESS(glob4, 4 - 1); + GOOD_ACCESS(glob5, 5 - 1); + GOOD_ACCESS(glob6, 6 - 1); + GOOD_ACCESS(glob7, 7 - 1); + GOOD_ACCESS(glob8, 8 - 1); + GOOD_ACCESS(glob9, 9 - 1); + GOOD_ACCESS(glob10, 10 - 1); + GOOD_ACCESS(glob11, 11 - 1); + GOOD_ACCESS(glob12, 12 - 1); + GOOD_ACCESS(glob13, 13 - 1); + GOOD_ACCESS(glob14, 14 - 1); + GOOD_ACCESS(glob15, 15 - 1); + GOOD_ACCESS(glob16, 16 - 1); + GOOD_ACCESS(glob17, 17 - 1); + GOOD_ACCESS(glob1000, 1000 - 1); + GOOD_ACCESS(glob10000, 10000 - 1); + GOOD_ACCESS(glob100000, 100000 - 1); + + BAD_ACCESS(glob1, 1); + BAD_ACCESS(glob2, 2); + BAD_ACCESS(glob3, 3); + BAD_ACCESS(glob4, 4); + BAD_ACCESS(glob5, 5); + BAD_ACCESS(glob6, 6); + BAD_ACCESS(glob7, 7); + BAD_ACCESS(glob8, 8); + BAD_ACCESS(glob9, 9); + BAD_ACCESS(glob10, 10); + BAD_ACCESS(glob11, 11); + BAD_ACCESS(glob12, 12); + BAD_ACCESS(glob13, 13); + BAD_ACCESS(glob14, 14); + BAD_ACCESS(glob15, 15); + BAD_ACCESS(glob16, 16); + BAD_ACCESS(glob17, 17); + BAD_ACCESS(glob1000, 1000); + BAD_ACCESS(glob1000, 1100); // Redzone is at least 101 bytes. + BAD_ACCESS(glob10000, 10000); + BAD_ACCESS(glob10000, 11000); // Redzone is at least 1001 bytes. + BAD_ACCESS(glob100000, 100000); + BAD_ACCESS(glob100000, 110000); // Redzone is at least 10001 bytes. +} + +TEST(AddressSanitizerInterface, PoisonedRegion) { + size_t rz = 16; + for (size_t size = 1; size <= 64; size++) { + char *p = new char[size]; + for (size_t beg = 0; beg < size + rz; beg++) { + for (size_t end = beg; end < size + rz; end++) { + void *first_poisoned = __asan_region_is_poisoned(p + 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(p + beg, first_poisoned); + } else { + EXPECT_GT(end, size); + EXPECT_EQ(p + 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_StressLargeMemset) { + size_t size = 1 << 20; + char *x = new char[size]; + for (int i = 0; i < 100000; i++) + Ident(memset)(x, 0, size); + delete [] x; +} + +// Same here, but we run memset with small sizes. +TEST(AddressSanitizerInterface, DISABLED_StressSmallMemset) { + size_t size = 32; + char *x = new char[size]; + for (int i = 0; i < 100000000; i++) + Ident(memset)(x, 0, size); + delete [] x; +} +static const char *kInvalidPoisonMessage = "invalid-poison-memory-range"; +static const char *kInvalidUnpoisonMessage = "invalid-unpoison-memory-range"; + +TEST(AddressSanitizerInterface, DISABLED_InvalidPoisonAndUnpoisonCallsTest) { + char *array = Ident((char*)malloc(120)); + __asan_unpoison_memory_region(array, 120); + // Try to unpoison not owned memory + EXPECT_DEATH(__asan_unpoison_memory_region(array, 121), + kInvalidUnpoisonMessage); + EXPECT_DEATH(__asan_unpoison_memory_region(array - 1, 120), + kInvalidUnpoisonMessage); + + __asan_poison_memory_region(array, 120); + // Try to poison not owned memory. + EXPECT_DEATH(__asan_poison_memory_region(array, 121), kInvalidPoisonMessage); + EXPECT_DEATH(__asan_poison_memory_region(array - 1, 120), + kInvalidPoisonMessage); + free(array); +} + +static void ErrorReportCallbackOneToZ(const char *report) { + 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), + 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; + const size_t kNumMallocs = 1 << 9; + for (size_t i = 0; i < kNumMallocs; i++) { + size_t size = i * 100 + 1; + pointers.push_back((char*)malloc(size)); + sizes.push_back(size); + } + for (size_t i = 0; i < 4000000; i++) { + EXPECT_FALSE(__asan_get_ownership(&pointers)); + EXPECT_FALSE(__asan_get_ownership((void*)0x1234)); + size_t idx = i % kNumMallocs; + EXPECT_TRUE(__asan_get_ownership(pointers[idx])); + EXPECT_EQ(sizes[idx], __asan_get_allocated_size(pointers[idx])); + } + for (size_t i = 0, n = pointers.size(); i < n; i++) + free(pointers[i]); +} + diff --git a/lib/asan/tests/asan_noinst_test.cc b/lib/asan/tests/asan_noinst_test.cc index 8168b3ec..b170314a 100644 --- a/lib/asan/tests/asan_noinst_test.cc +++ b/lib/asan/tests/asan_noinst_test.cc @@ -25,6 +25,46 @@ #include <vector> #include <limits> +#if ASAN_FLEXIBLE_MAPPING_AND_OFFSET == 1 +// Manually set correct ASan mapping scale and offset, as they won't be +// exported from instrumented sources (there are none). +# define FLEXIBLE_SHADOW_SCALE kDefaultShadowScale +# if SANITIZER_ANDROID +# define FLEXIBLE_SHADOW_OFFSET (0) +# else +# if SANITIZER_WORDSIZE == 32 +# if defined(__mips__) +# define FLEXIBLE_SHADOW_OFFSET kMIPS32_ShadowOffset32 +# else +# define FLEXIBLE_SHADOW_OFFSET kDefaultShadowOffset32 +# endif +# else +# if defined(__powerpc64__) +# define FLEXIBLE_SHADOW_OFFSET kPPC64_ShadowOffset64 +# elif SANITIZER_MAC +# define FLEXIBLE_SHADOW_OFFSET kDefaultShadowOffset64 +# else +# define FLEXIBLE_SHADOW_OFFSET kDefaultShort64bitShadowOffset +# endif +# endif +# endif +SANITIZER_INTERFACE_ATTRIBUTE uptr __asan_mapping_scale = FLEXIBLE_SHADOW_SCALE; +SANITIZER_INTERFACE_ATTRIBUTE uptr __asan_mapping_offset = + FLEXIBLE_SHADOW_OFFSET; +#endif // ASAN_FLEXIBLE_MAPPING_AND_OFFSET + +extern "C" { +// Set specific ASan options for uninstrumented unittest. +const char* __asan_default_options() { + return "allow_reexec=0"; +} +} // extern "C" + +// Make sure __asan_init is called before any test case is run. +struct AsanInitCaller { + AsanInitCaller() { __asan_init(); } +}; +static AsanInitCaller asan_init_caller; TEST(AddressSanitizer, InternalSimpleDeathTest) { EXPECT_DEATH(exit(1), ""); @@ -333,459 +373,13 @@ TEST(AddressSanitizer, ThreadedOneSizeMallocStressTest) { } } -TEST(AddressSanitizer, MemsetWildAddressTest) { +TEST(AddressSanitizer, ShadowRegionIsPoisonedTest) { using __asan::kHighMemEnd; - typedef void*(*memset_p)(void*, int, size_t); - // Prevent inlining of memset(). - volatile memset_p libc_memset = (memset_p)memset; - EXPECT_DEATH(libc_memset((void*)(kLowShadowBeg + 200), 0, 100), - (kLowShadowEnd == 0) ? "unknown-crash.*shadow gap" - : "unknown-crash.*low shadow"); - EXPECT_DEATH(libc_memset((void*)(kShadowGapBeg + 200), 0, 100), - "unknown-crash.*shadow gap"); - EXPECT_DEATH(libc_memset((void*)(kHighShadowBeg + 200), 0, 100), - "unknown-crash.*high shadow"); -} - -TEST(AddressSanitizerInterface, GetEstimatedAllocatedSize) { - EXPECT_EQ(0U, __asan_get_estimated_allocated_size(0)); - 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])); - } -} - -static const char* kGetAllocatedSizeErrorMsg = - "attempting to call __asan_get_allocated_size()"; - -TEST(AddressSanitizerInterface, GetAllocatedSizeAndOwnershipTest) { - const size_t kArraySize = 100; - char *array = Ident((char*)malloc(kArraySize)); - int *int_ptr = Ident(new int); - - // Allocated memory is owned by allocator. Allocated size should be - // equal to requested size. - EXPECT_EQ(true, __asan_get_ownership(array)); - EXPECT_EQ(kArraySize, __asan_get_allocated_size(array)); - EXPECT_EQ(true, __asan_get_ownership(int_ptr)); - EXPECT_EQ(sizeof(int), __asan_get_allocated_size(int_ptr)); - - // 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_FALSE(__asan_get_ownership(wild_addr)); - EXPECT_DEATH(__asan_get_allocated_size(wild_addr), kGetAllocatedSizeErrorMsg); - 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_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_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) { - size_t before_malloc, after_malloc, after_free; - char *array; - const size_t kMallocSize = 100; - before_malloc = __asan_get_current_allocated_bytes(); - - array = Ident((char*)malloc(kMallocSize)); - after_malloc = __asan_get_current_allocated_bytes(); - EXPECT_EQ(before_malloc + kMallocSize, after_malloc); - - free(array); - after_free = __asan_get_current_allocated_bytes(); - EXPECT_EQ(before_malloc, after_free); -} - -static void DoDoubleFree() { - int *x = Ident(new int); - delete Ident(x); - delete Ident(x); -} - -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 << 28) + 1; // 256M - free(Ident(malloc(kLargeMallocSize))); // Drain quarantine. - uptr old_heap_size = __asan_get_heap_size(); - for (int i = 0; i < 3; i++) { - // fprintf(stderr, "allocating %zu bytes:\n", kLargeMallocSize); - free(Ident(malloc(kLargeMallocSize))); - EXPECT_EQ(old_heap_size, __asan_get_heap_size()); - } -} - -static const size_t kManyThreadsMallocSizes[] = {5, 1UL<<10, 1UL<<14, 357}; -static const size_t kManyThreadsIterations = 250; -static const size_t kManyThreadsNumThreads = - (SANITIZER_WORDSIZE == 32) ? 40 : 200; - -void *ManyThreadsWithStatsWorker(void *arg) { - (void)arg; - for (size_t iter = 0; iter < kManyThreadsIterations; iter++) { - for (size_t size_index = 0; size_index < 4; size_index++) { - free(Ident(malloc(kManyThreadsMallocSizes[size_index]))); - } - } - // Just one large allocation. - free(Ident(malloc(1 << 20))); - return 0; -} - -TEST(AddressSanitizerInterface, ManyThreadsWithStatsStressTest) { - size_t before_test, after_test, i; - pthread_t threads[kManyThreadsNumThreads]; - before_test = __asan_get_current_allocated_bytes(); - for (i = 0; i < kManyThreadsNumThreads; i++) { - PTHREAD_CREATE(&threads[i], 0, - (void* (*)(void *x))ManyThreadsWithStatsWorker, (void*)i); - } - for (i = 0; i < kManyThreadsNumThreads; i++) { - PTHREAD_JOIN(threads[i], 0); - } - after_test = __asan_get_current_allocated_bytes(); - // ASan stats also reflect memory usage of internal ASan RTL structs, - // so we can't check for equality here. - EXPECT_LT(after_test, before_test + (1UL<<20)); -} - -TEST(AddressSanitizerInterface, ExitCode) { - int original_exit_code = __asan_set_error_exit_code(7); - EXPECT_EXIT(DoDoubleFree(), ::testing::ExitedWithCode(7), ""); - EXPECT_EQ(7, __asan_set_error_exit_code(8)); - EXPECT_EXIT(DoDoubleFree(), ::testing::ExitedWithCode(8), ""); - EXPECT_EQ(8, __asan_set_error_exit_code(original_exit_code)); - EXPECT_EXIT(DoDoubleFree(), - ::testing::ExitedWithCode(original_exit_code), ""); -} - -static void MyDeathCallback() { - fprintf(stderr, "MyDeathCallback\n"); -} - -TEST(AddressSanitizerInterface, DeathCallbackTest) { - __asan_set_death_callback(MyDeathCallback); - EXPECT_DEATH(DoDoubleFree(), "MyDeathCallback"); - __asan_set_death_callback(NULL); -} - -static const char* kUseAfterPoisonErrorMessage = "use-after-poison"; - -#define GOOD_ACCESS(ptr, offset) \ - EXPECT_FALSE(__asan::AddressIsPoisoned((uptr)(ptr + offset))) - -#define BAD_ACCESS(ptr, offset) \ - EXPECT_TRUE(__asan::AddressIsPoisoned((uptr)(ptr + offset))) - -TEST(AddressSanitizerInterface, SimplePoisonMemoryRegionTest) { - char *array = Ident((char*)malloc(120)); - // poison array[40..80) - __asan_poison_memory_region(array + 40, 40); - GOOD_ACCESS(array, 39); - GOOD_ACCESS(array, 80); - BAD_ACCESS(array, 40); - BAD_ACCESS(array, 60); - BAD_ACCESS(array, 79); - EXPECT_DEATH(__asan_report_error(0, 0, 0, (uptr)(array + 40), true, 1), - kUseAfterPoisonErrorMessage); - __asan_unpoison_memory_region(array + 40, 40); - // access previously poisoned memory. - GOOD_ACCESS(array, 40); - GOOD_ACCESS(array, 79); - free(array); -} - -TEST(AddressSanitizerInterface, OverlappingPoisonMemoryRegionTest) { - char *array = Ident((char*)malloc(120)); - // Poison [0..40) and [80..120) - __asan_poison_memory_region(array, 40); - __asan_poison_memory_region(array + 80, 40); - BAD_ACCESS(array, 20); - GOOD_ACCESS(array, 60); - BAD_ACCESS(array, 100); - // Poison whole array - [0..120) - __asan_poison_memory_region(array, 120); - BAD_ACCESS(array, 60); - // Unpoison [24..96) - __asan_unpoison_memory_region(array + 24, 72); - BAD_ACCESS(array, 23); - GOOD_ACCESS(array, 24); - GOOD_ACCESS(array, 60); - GOOD_ACCESS(array, 95); - BAD_ACCESS(array, 96); - free(array); -} - -TEST(AddressSanitizerInterface, PushAndPopWithPoisoningTest) { - // Vector of capacity 20 - char *vec = Ident((char*)malloc(20)); - __asan_poison_memory_region(vec, 20); - for (size_t i = 0; i < 7; i++) { - // Simulate push_back. - __asan_unpoison_memory_region(vec + i, 1); - GOOD_ACCESS(vec, i); - BAD_ACCESS(vec, i + 1); - } - for (size_t i = 7; i > 0; i--) { - // Simulate pop_back. - __asan_poison_memory_region(vec + i - 1, 1); - BAD_ACCESS(vec, i - 1); - if (i > 1) GOOD_ACCESS(vec, i - 2); - } - free(vec); -} - -TEST(AddressSanitizerInterface, GlobalRedzones) { - GOOD_ACCESS(glob1, 1 - 1); - GOOD_ACCESS(glob2, 2 - 1); - GOOD_ACCESS(glob3, 3 - 1); - GOOD_ACCESS(glob4, 4 - 1); - GOOD_ACCESS(glob5, 5 - 1); - GOOD_ACCESS(glob6, 6 - 1); - GOOD_ACCESS(glob7, 7 - 1); - GOOD_ACCESS(glob8, 8 - 1); - GOOD_ACCESS(glob9, 9 - 1); - GOOD_ACCESS(glob10, 10 - 1); - GOOD_ACCESS(glob11, 11 - 1); - GOOD_ACCESS(glob12, 12 - 1); - GOOD_ACCESS(glob13, 13 - 1); - GOOD_ACCESS(glob14, 14 - 1); - GOOD_ACCESS(glob15, 15 - 1); - GOOD_ACCESS(glob16, 16 - 1); - GOOD_ACCESS(glob17, 17 - 1); - GOOD_ACCESS(glob1000, 1000 - 1); - GOOD_ACCESS(glob10000, 10000 - 1); - GOOD_ACCESS(glob100000, 100000 - 1); - - BAD_ACCESS(glob1, 1); - BAD_ACCESS(glob2, 2); - BAD_ACCESS(glob3, 3); - BAD_ACCESS(glob4, 4); - BAD_ACCESS(glob5, 5); - BAD_ACCESS(glob6, 6); - BAD_ACCESS(glob7, 7); - BAD_ACCESS(glob8, 8); - BAD_ACCESS(glob9, 9); - BAD_ACCESS(glob10, 10); - BAD_ACCESS(glob11, 11); - BAD_ACCESS(glob12, 12); - BAD_ACCESS(glob13, 13); - BAD_ACCESS(glob14, 14); - BAD_ACCESS(glob15, 15); - BAD_ACCESS(glob16, 16); - BAD_ACCESS(glob17, 17); - BAD_ACCESS(glob1000, 1000); - BAD_ACCESS(glob1000, 1100); // Redzone is at least 101 bytes. - BAD_ACCESS(glob10000, 10000); - BAD_ACCESS(glob10000, 11000); // Redzone is at least 1001 bytes. - BAD_ACCESS(glob100000, 100000); - BAD_ACCESS(glob100000, 110000); // Redzone is at least 10001 bytes. -} - -// Make sure that each aligned block of size "2^granularity" doesn't have -// "true" value before "false" value. -static void MakeShadowValid(bool *shadow, int length, int granularity) { - bool can_be_poisoned = true; - for (int i = length - 1; i >= 0; i--) { - if (!shadow[i]) - can_be_poisoned = false; - if (!can_be_poisoned) - shadow[i] = false; - if (i % (1 << granularity) == 0) { - can_be_poisoned = true; - } - } -} - -TEST(AddressSanitizerInterface, PoisoningStressTest) { - const size_t kSize = 24; - bool expected[kSize]; - char *arr = Ident((char*)malloc(kSize)); - for (size_t l1 = 0; l1 < kSize; l1++) { - for (size_t s1 = 1; l1 + s1 <= kSize; s1++) { - for (size_t l2 = 0; l2 < kSize; l2++) { - for (size_t s2 = 1; l2 + s2 <= kSize; s2++) { - // Poison [l1, l1+s1), [l2, l2+s2) and check result. - __asan_unpoison_memory_region(arr, kSize); - __asan_poison_memory_region(arr + l1, s1); - __asan_poison_memory_region(arr + l2, s2); - memset(expected, false, kSize); - memset(expected + l1, true, s1); - MakeShadowValid(expected, kSize, /*granularity*/ 3); - memset(expected + l2, true, s2); - MakeShadowValid(expected, kSize, /*granularity*/ 3); - for (size_t i = 0; i < kSize; i++) { - ASSERT_EQ(expected[i], __asan_address_is_poisoned(arr + i)); - } - // Unpoison [l1, l1+s1) and [l2, l2+s2) and check result. - __asan_poison_memory_region(arr, kSize); - __asan_unpoison_memory_region(arr + l1, s1); - __asan_unpoison_memory_region(arr + l2, s2); - memset(expected, true, kSize); - memset(expected + l1, false, s1); - MakeShadowValid(expected, kSize, /*granularity*/ 3); - memset(expected + l2, false, s2); - MakeShadowValid(expected, kSize, /*granularity*/ 3); - for (size_t i = 0; i < kSize; i++) { - ASSERT_EQ(expected[i], __asan_address_is_poisoned(arr + i)); - } - } - } - } - } - free(arr); -} - -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_StressLargeMemset) { - size_t size = 1 << 20; - char *x = new char[size]; - for (int i = 0; i < 100000; i++) - Ident(memset)(x, 0, size); - delete [] x; -} - -// Same here, but we run memset with small sizes. -TEST(AddressSanitizerInterface, DISABLED_StressSmallMemset) { - size_t size = 32; - char *x = new char[size]; - for (int i = 0; i < 100000000; i++) - Ident(memset)(x, 0, size); - delete [] x; -} - -static const char *kInvalidPoisonMessage = "invalid-poison-memory-range"; -static const char *kInvalidUnpoisonMessage = "invalid-unpoison-memory-range"; - -TEST(AddressSanitizerInterface, DISABLED_InvalidPoisonAndUnpoisonCallsTest) { - char *array = Ident((char*)malloc(120)); - __asan_unpoison_memory_region(array, 120); - // Try to unpoison not owned memory - EXPECT_DEATH(__asan_unpoison_memory_region(array, 121), - kInvalidUnpoisonMessage); - EXPECT_DEATH(__asan_unpoison_memory_region(array - 1, 120), - kInvalidUnpoisonMessage); - - __asan_poison_memory_region(array, 120); - // Try to poison not owned memory. - EXPECT_DEATH(__asan_poison_memory_region(array, 121), kInvalidPoisonMessage); - EXPECT_DEATH(__asan_poison_memory_region(array - 1, 120), - kInvalidPoisonMessage); - free(array); -} - -static void ErrorReportCallbackOneToZ(const char *report) { - 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), - 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; - const size_t kNumMallocs = 1 << 9; - for (size_t i = 0; i < kNumMallocs; i++) { - size_t size = i * 100 + 1; - pointers.push_back((char*)malloc(size)); - sizes.push_back(size); - } - for (size_t i = 0; i < 4000000; i++) { - EXPECT_FALSE(__asan_get_ownership(&pointers)); - EXPECT_FALSE(__asan_get_ownership((void*)0x1234)); - size_t idx = i % kNumMallocs; - EXPECT_TRUE(__asan_get_ownership(pointers[idx])); - EXPECT_EQ(sizes[idx], __asan_get_allocated_size(pointers[idx])); - } - for (size_t i = 0, n = pointers.size(); i < n; i++) - free(pointers[i]); -} - - -TEST(AddressSanitizerInterface, CallocOverflow32) { -#if SANITIZER_WORDSIZE == 32 - size_t kArraySize = 112; - volatile size_t kArraySize2 = 43878406; - void *p = 0; - EXPECT_DEATH(p = calloc(kArraySize, kArraySize2), - "allocator is terminating the process instead of returning 0"); - assert(!p); -#endif -} - -TEST(AddressSanitizerInterface, CallocReturnsZeroMem) { - size_t sizes[] = {16, 1000, 10000, 100000, 2100000}; - for (size_t s = 0; s < ARRAY_SIZE(sizes); s++) { - size_t size = sizes[s]; - for (size_t iter = 0; iter < 5; iter++) { - char *x = Ident((char*)calloc(1, size)); - EXPECT_EQ(x[0], 0); - EXPECT_EQ(x[size - 1], 0); - EXPECT_EQ(x[size / 2], 0); - EXPECT_EQ(x[size / 3], 0); - EXPECT_EQ(x[size / 4], 0); - memset(x, 0x42, size); - free(Ident(x)); - free(Ident(malloc(Ident(1 << 27)))); // Try to drain the quarantine. - } - } + // Check that __asan_region_is_poisoned works for shadow regions. + uptr ptr = kLowShadowBeg + 200; + EXPECT_EQ(ptr, __asan_region_is_poisoned(ptr, 100)); + ptr = kShadowGapBeg + 200; + EXPECT_EQ(ptr, __asan_region_is_poisoned(ptr, 100)); + ptr = kHighShadowBeg + 200; + EXPECT_EQ(ptr, __asan_region_is_poisoned(ptr, 100)); } diff --git a/lib/asan/tests/asan_test.cc b/lib/asan/tests/asan_test.cc index 292cfdb8..ba7bd838 100644 --- a/lib/asan/tests/asan_test.cc +++ b/lib/asan/tests/asan_test.cc @@ -111,6 +111,35 @@ TEST(AddressSanitizer, CallocTest) { free(a); } +TEST(AddressSanitizer, CallocOverflow32) { +#if SANITIZER_WORDSIZE == 32 + size_t kArraySize = 112; + volatile size_t kArraySize2 = 43878406; + void *p = 0; + EXPECT_DEATH(p = calloc(kArraySize, kArraySize2), + "allocator is terminating the process instead of returning 0"); + assert(!p); +#endif +} + +TEST(AddressSanitizer, CallocReturnsZeroMem) { + size_t sizes[] = {16, 1000, 10000, 100000, 2100000}; + for (size_t s = 0; s < sizeof(sizes)/sizeof(sizes[0]); s++) { + size_t size = sizes[s]; + for (size_t iter = 0; iter < 5; iter++) { + char *x = Ident((char*)calloc(1, size)); + EXPECT_EQ(x[0], 0); + EXPECT_EQ(x[size - 1], 0); + EXPECT_EQ(x[size / 2], 0); + EXPECT_EQ(x[size / 3], 0); + EXPECT_EQ(x[size / 4], 0); + memset(x, 0x42, size); + free(Ident(x)); + free(Ident(malloc(Ident(1 << 27)))); // Try to drain the quarantine. + } + } +} + TEST(AddressSanitizer, VallocTest) { void *a = valloc(100); EXPECT_EQ(0U, (uintptr_t)a % kPageSize); |