diff options
Diffstat (limited to 'lib/lsan')
52 files changed, 441 insertions, 1400 deletions
diff --git a/lib/lsan/CMakeLists.txt b/lib/lsan/CMakeLists.txt index 3018a06622ee..2ea765de1bc7 100644 --- a/lib/lsan/CMakeLists.txt +++ b/lib/lsan/CMakeLists.txt @@ -1,8 +1,7 @@ include_directories(..) -set(LSAN_CFLAGS - ${SANITIZER_COMMON_CFLAGS} - -fno-rtti) +set(LSAN_CFLAGS ${SANITIZER_COMMON_CFLAGS}) +append_no_rtti_flag(LSAN_CFLAGS) set(LSAN_COMMON_SOURCES lsan_common.cc @@ -17,16 +16,7 @@ set(LSAN_SOURCES set(LSAN_SRC_DIR ${CMAKE_CURRENT_SOURCE_DIR}) -# The common files need to build on every arch supported by ASan. -# (Even if they build into dummy object files.) -filter_available_targets(LSAN_COMMON_SUPPORTED_ARCH - x86_64 i386 powerpc64) - -# Architectures supported by the standalone LSan. -filter_available_targets(LSAN_SUPPORTED_ARCH - x86_64) - -set(LSAN_RUNTIME_LIBRARIES) +add_custom_target(lsan) if(APPLE) foreach(os ${SANITIZER_COMMON_SUPPORTED_DARWIN_OS}) @@ -35,7 +25,7 @@ if(APPLE) SOURCES ${LSAN_COMMON_SOURCES} CFLAGS ${LSAN_CFLAGS}) endforeach() -elseif(NOT ANDROID) +else() foreach(arch ${LSAN_COMMON_SUPPORTED_ARCH}) add_compiler_rt_object_library(RTLSanCommon ${arch} SOURCES ${LSAN_COMMON_SOURCES} @@ -43,18 +33,15 @@ elseif(NOT ANDROID) endforeach() foreach(arch ${LSAN_SUPPORTED_ARCH}) - add_compiler_rt_static_runtime(clang_rt.lsan-${arch} ${arch} + add_compiler_rt_runtime(clang_rt.lsan-${arch} ${arch} STATIC SOURCES ${LSAN_SOURCES} $<TARGET_OBJECTS:RTInterception.${arch}> $<TARGET_OBJECTS:RTSanitizerCommon.${arch}> $<TARGET_OBJECTS:RTSanitizerCommonLibc.${arch}> $<TARGET_OBJECTS:RTLSanCommon.${arch}> CFLAGS ${LSAN_CFLAGS}) - list(APPEND LSAN_RUNTIME_LIBRARIES clang_rt.lsan-${arch}) + add_dependencies(lsan clang_rt.lsan-${arch}) endforeach() endif() -if (LLVM_INCLUDE_TESTS) - add_subdirectory(tests) -endif() -add_subdirectory(lit_tests) +add_dependencies(compiler-rt lsan) diff --git a/lib/lsan/lit_tests/AsanConfig/lit.cfg b/lib/lsan/lit_tests/AsanConfig/lit.cfg deleted file mode 100644 index ae9198173ffc..000000000000 --- a/lib/lsan/lit_tests/AsanConfig/lit.cfg +++ /dev/null @@ -1,32 +0,0 @@ -# -*- Python -*- - -import os - -def get_required_attr(config, attr_name): - attr_value = getattr(config, attr_name, None) - if not attr_value: - lit_config.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 - -lsan_lit_src_root = get_required_attr(config, "lsan_lit_src_root") -lsan_lit_cfg = os.path.join(lsan_lit_src_root, "lit.common.cfg") -if not os.path.exists(lsan_lit_cfg): - lit_config.fatal("Can't find common LSan lit config at: %r" % lsan_lit_cfg) -lit_config.load_config(config, lsan_lit_cfg) - -config.name = 'LeakSanitizer-AddressSanitizer' - -clang_lsan_cxxflags = config.clang_cxxflags + " -fsanitize=address " - -config.substitutions.append( ("%clangxx_lsan ", (" " + config.clang + " " + - clang_lsan_cxxflags + " ")) ) - -clang_lsan_cflags = config.clang_cflags + " -fsanitize=address " - -config.substitutions.append( ("%clang_lsan ", (" " + config.clang + " " + - clang_lsan_cflags + " ")) ) - -config.environment['ASAN_OPTIONS'] = 'detect_leaks=1' diff --git a/lib/lsan/lit_tests/AsanConfig/lit.site.cfg.in b/lib/lsan/lit_tests/AsanConfig/lit.site.cfg.in deleted file mode 100644 index 9cf6572c54b7..000000000000 --- a/lib/lsan/lit_tests/AsanConfig/lit.site.cfg.in +++ /dev/null @@ -1,8 +0,0 @@ -# Load common config for all compiler-rt lit tests. -lit_config.load_config(config, "@COMPILER_RT_BINARY_DIR@/lib/lit.common.configured") - -# Tool-specific config options. -config.lsan_lit_src_root = "@LSAN_LIT_SOURCE_DIR@" - -# Load tool-specific config that would do the real work. -lit_config.load_config(config, "@LSAN_LIT_SOURCE_DIR@/AsanConfig/lit.cfg") diff --git a/lib/lsan/lit_tests/CMakeLists.txt b/lib/lsan/lit_tests/CMakeLists.txt deleted file mode 100644 index 526d71bf3418..000000000000 --- a/lib/lsan/lit_tests/CMakeLists.txt +++ /dev/null @@ -1,37 +0,0 @@ -set(LSAN_SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/..) -set(LSAN_BINARY_DIR ${CMAKE_CURRENT_BINARY_DIR}/..) - -set(LSAN_LIT_SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR}) - -configure_lit_site_cfg( - ${CMAKE_CURRENT_SOURCE_DIR}/LsanConfig/lit.site.cfg.in - ${CMAKE_CURRENT_BINARY_DIR}/LsanConfig/lit.site.cfg - ) - -configure_lit_site_cfg( - ${CMAKE_CURRENT_SOURCE_DIR}/AsanConfig/lit.site.cfg.in - ${CMAKE_CURRENT_BINARY_DIR}/AsanConfig/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 AND NOT APPLE AND NOT ANDROID) - set(LSAN_TEST_DEPS - ${SANITIZER_COMMON_LIT_TEST_DEPS} - ${LSAN_RUNTIME_LIBRARIES}) - foreach(arch ${LSAN_SUPPORTED_ARCH}) - list(APPEND LSAN_TEST_DEPS clang_rt.asan-${arch}) - endforeach() - if(LLVM_INCLUDE_TESTS) - list(APPEND LSAN_TEST_DEPS LsanUnitTests) - endif() - add_lit_testsuite(check-lsan "Running the LeakSanitizer tests" - ${CMAKE_CURRENT_BINARY_DIR}/LsanConfig - ${CMAKE_CURRENT_BINARY_DIR}/AsanConfig - ${CMAKE_CURRENT_BINARY_DIR}/Unit - DEPENDS ${LSAN_TEST_DEPS}) - set_target_properties(check-lsan PROPERTIES FOLDER "LSan tests") -endif() diff --git a/lib/lsan/lit_tests/LsanConfig/lit.cfg b/lib/lsan/lit_tests/LsanConfig/lit.cfg deleted file mode 100644 index 84faf9167a78..000000000000 --- a/lib/lsan/lit_tests/LsanConfig/lit.cfg +++ /dev/null @@ -1,30 +0,0 @@ -# -*- Python -*- - -import os - -def get_required_attr(config, attr_name): - attr_value = getattr(config, attr_name, None) - if not attr_value: - lit_config.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 - -lsan_lit_src_root = get_required_attr(config, "lsan_lit_src_root") -lsan_lit_cfg = os.path.join(lsan_lit_src_root, "lit.common.cfg") -if not os.path.exists(lsan_lit_cfg): - lit_config.fatal("Can't find common LSan lit config at: %r" % lsan_lit_cfg) -lit_config.load_config(config, lsan_lit_cfg) - -config.name = 'LeakSanitizer-Standalone' - -clang_lsan_cxxflags = config.clang_cxxflags + " -fsanitize=leak " - -config.substitutions.append( ("%clangxx_lsan ", (" " + config.clang + " " + - clang_lsan_cxxflags + " ")) ) - -clang_lsan_cflags = config.clang_cflags + " -fsanitize=leak " - -config.substitutions.append( ("%clang_lsan ", (" " + config.clang + " " + - clang_lsan_cflags + " ")) ) diff --git a/lib/lsan/lit_tests/LsanConfig/lit.site.cfg.in b/lib/lsan/lit_tests/LsanConfig/lit.site.cfg.in deleted file mode 100644 index 2a6d724c0436..000000000000 --- a/lib/lsan/lit_tests/LsanConfig/lit.site.cfg.in +++ /dev/null @@ -1,8 +0,0 @@ -# Load common config for all compiler-rt lit tests. -lit_config.load_config(config, "@COMPILER_RT_BINARY_DIR@/lib/lit.common.configured") - -# Tool-specific config options. -config.lsan_lit_src_root = "@LSAN_LIT_SOURCE_DIR@" - -# Load tool-specific config that would do the real work. -lit_config.load_config(config, "@LSAN_LIT_SOURCE_DIR@/LsanConfig/lit.cfg") diff --git a/lib/lsan/lit_tests/TestCases/SharedLibs/huge_tls_lib_so.cc b/lib/lsan/lit_tests/TestCases/SharedLibs/huge_tls_lib_so.cc deleted file mode 100644 index 475a66ec12a4..000000000000 --- a/lib/lsan/lit_tests/TestCases/SharedLibs/huge_tls_lib_so.cc +++ /dev/null @@ -1,12 +0,0 @@ -// A loadable module with a large thread local section, which would require -// allocation of a new TLS storage chunk when loaded with dlopen(). We use it -// to test the reachability of such chunks in LSan tests. - -// This must be large enough that it doesn't fit into preallocated static TLS -// space (see STATIC_TLS_SURPLUS in glibc). -__thread void *huge_thread_local_array[(1 << 20) / sizeof(void *)]; // NOLINT - -extern "C" void **StoreToTLS(void *p) { - huge_thread_local_array[0] = p; - return &huge_thread_local_array[0]; -} diff --git a/lib/lsan/lit_tests/TestCases/SharedLibs/lit.local.cfg b/lib/lsan/lit_tests/TestCases/SharedLibs/lit.local.cfg deleted file mode 100644 index b3677c17a0f2..000000000000 --- a/lib/lsan/lit_tests/TestCases/SharedLibs/lit.local.cfg +++ /dev/null @@ -1,4 +0,0 @@ -# Sources in this directory are compiled as shared libraries and used by -# tests in parent directory. - -config.suffixes = [] diff --git a/lib/lsan/lit_tests/TestCases/cleanup_in_tsd_destructor.cc b/lib/lsan/lit_tests/TestCases/cleanup_in_tsd_destructor.cc deleted file mode 100644 index ab368245317c..000000000000 --- a/lib/lsan/lit_tests/TestCases/cleanup_in_tsd_destructor.cc +++ /dev/null @@ -1,45 +0,0 @@ -// Regression test for thread lifetime tracking. Thread data should be -// considered live during the thread's termination, at least until the -// user-installed TSD destructors have finished running (since they may contain -// additional cleanup tasks). LSan doesn't actually meet that goal 100%, but it -// makes its best effort. -// RUN: LSAN_BASE="report_objects=1:use_registers=0:use_stacks=0:use_globals=0" -// RUN: %clangxx_lsan %s -o %t -// RUN: LSAN_OPTIONS=$LSAN_BASE:use_tls=1 %t -// RUN: LSAN_OPTIONS=$LSAN_BASE:use_tls=0 not %t 2>&1 | FileCheck %s - -#include <assert.h> -#include <pthread.h> -#include <stdio.h> -#include <stdlib.h> - -#include "sanitizer/lsan_interface.h" - -pthread_key_t key; -__thread void *p; - -void key_destructor(void *arg) { - // Generally this may happen on a different thread. - __lsan_do_leak_check(); -} - -void *thread_func(void *arg) { - p = malloc(1337); - fprintf(stderr, "Test alloc: %p.\n", p); - int res = pthread_setspecific(key, (void*)1); - assert(res == 0); - return 0; -} - -int main() { - int res = pthread_key_create(&key, &key_destructor); - assert(res == 0); - pthread_t thread_id; - res = pthread_create(&thread_id, 0, thread_func, 0); - assert(res == 0); - res = pthread_join(thread_id, 0); - assert(res == 0); - return 0; -} -// CHECK: Test alloc: [[ADDR:.*]]. -// CHECK: leaked 1337 byte object at [[ADDR]] diff --git a/lib/lsan/lit_tests/TestCases/disabler.cc b/lib/lsan/lit_tests/TestCases/disabler.cc deleted file mode 100644 index db0cd8fabe4d..000000000000 --- a/lib/lsan/lit_tests/TestCases/disabler.cc +++ /dev/null @@ -1,23 +0,0 @@ -// Test for ScopedDisabler. -// RUN: LSAN_BASE="report_objects=1:use_registers=0:use_stacks=0:use_globals=0:use_tls=0" -// RUN: %clangxx_lsan %s -o %t -// RUN: LSAN_OPTIONS=$LSAN_BASE not %t 2>&1 | FileCheck %s - -#include <stdio.h> -#include <stdlib.h> - -#include "sanitizer/lsan_interface.h" - -int main() { - void **p; - { - __lsan::ScopedDisabler d; - p = new void *; - } - *reinterpret_cast<void **>(p) = malloc(666); - void *q = malloc(1337); - // Break optimization. - fprintf(stderr, "Test alloc: %p.\n", q); - return 0; -} -// CHECK: SUMMARY: {{(Leak|Address)}}Sanitizer: 1337 byte(s) leaked in 1 allocation(s) diff --git a/lib/lsan/lit_tests/TestCases/disabler_in_tsd_destructor.cc b/lib/lsan/lit_tests/TestCases/disabler_in_tsd_destructor.cc deleted file mode 100644 index 94e4fc390b3b..000000000000 --- a/lib/lsan/lit_tests/TestCases/disabler_in_tsd_destructor.cc +++ /dev/null @@ -1,38 +0,0 @@ -// Regression test. Disabler should not depend on TSD validity. -// RUN: LSAN_BASE="report_objects=1:use_registers=0:use_stacks=0:use_globals=0:use_tls=1" -// RUN: %clangxx_lsan %s -o %t -// RUN: LSAN_OPTIONS=$LSAN_BASE %t - -#include <assert.h> -#include <pthread.h> -#include <stdio.h> -#include <stdlib.h> - -#include "sanitizer/lsan_interface.h" - -pthread_key_t key; - -void key_destructor(void *arg) { - __lsan::ScopedDisabler d; - void *p = malloc(1337); - // Break optimization. - fprintf(stderr, "Test alloc: %p.\n", p); - pthread_setspecific(key, 0); -} - -void *thread_func(void *arg) { - int res = pthread_setspecific(key, (void*)1); - assert(res == 0); - return 0; -} - -int main() { - int res = pthread_key_create(&key, &key_destructor); - assert(res == 0); - pthread_t thread_id; - res = pthread_create(&thread_id, 0, thread_func, 0); - assert(res == 0); - res = pthread_join(thread_id, 0); - assert(res == 0); - return 0; -} diff --git a/lib/lsan/lit_tests/TestCases/do_leak_check_override.cc b/lib/lsan/lit_tests/TestCases/do_leak_check_override.cc deleted file mode 100644 index be0ed0a6d48f..000000000000 --- a/lib/lsan/lit_tests/TestCases/do_leak_check_override.cc +++ /dev/null @@ -1,36 +0,0 @@ -// Test for __lsan_do_leak_check(). We test it by making the leak check run -// before global destructors, which also tests compatibility with HeapChecker's -// "normal" mode (LSan runs in "strict" mode by default). -// RUN: LSAN_BASE="use_stacks=0:use_registers=0" -// RUN: %clangxx_lsan %s -o %t -// RUN: LSAN_OPTIONS=$LSAN_BASE not %t 2>&1 | FileCheck --check-prefix=CHECK-strict %s -// RUN: LSAN_OPTIONS=$LSAN_BASE not %t foo 2>&1 | FileCheck --check-prefix=CHECK-normal %s - -#include <stdio.h> -#include <stdlib.h> -#include <sanitizer/lsan_interface.h> - -struct LeakyGlobal { - LeakyGlobal() { - p = malloc(1337); - } - ~LeakyGlobal() { - p = 0; - } - void *p; -}; - -LeakyGlobal leaky_global; - -int main(int argc, char *argv[]) { - // Register leak check to run before global destructors. - if (argc > 1) - atexit(&__lsan_do_leak_check); - void *p = malloc(666); - printf("Test alloc: %p\n", p); - printf("Test alloc in leaky global: %p\n", leaky_global.p); - return 0; -} - -// CHECK-strict: SUMMARY: {{(Leak|Address)}}Sanitizer: 2003 byte(s) leaked in 2 allocation(s) -// CHECK-normal: SUMMARY: {{(Leak|Address)}}Sanitizer: 666 byte(s) leaked in 1 allocation(s) diff --git a/lib/lsan/lit_tests/TestCases/fork.cc b/lib/lsan/lit_tests/TestCases/fork.cc deleted file mode 100644 index 69258d9a0c72..000000000000 --- a/lib/lsan/lit_tests/TestCases/fork.cc +++ /dev/null @@ -1,24 +0,0 @@ -// Test that thread local data is handled correctly after forking without exec(). -// RUN: %clangxx_lsan %s -o %t -// RUN: %t 2>&1 - -#include <assert.h> -#include <stdio.h> -#include <stdlib.h> -#include <sys/wait.h> -#include <unistd.h> - -__thread void *thread_local_var; - -int main() { - int status = 0; - thread_local_var = malloc(1337); - pid_t pid = fork(); - assert(pid >= 0); - if (pid > 0) { - waitpid(pid, &status, 0); - assert(WIFEXITED(status)); - return WEXITSTATUS(status); - } - return 0; -} diff --git a/lib/lsan/lit_tests/TestCases/fork_threaded.cc b/lib/lsan/lit_tests/TestCases/fork_threaded.cc deleted file mode 100644 index 24a586109e28..000000000000 --- a/lib/lsan/lit_tests/TestCases/fork_threaded.cc +++ /dev/null @@ -1,43 +0,0 @@ -// Test that thread local data is handled correctly after forking without -// exec(). In this test leak checking is initiated from a non-main thread. -// RUN: %clangxx_lsan %s -o %t -// RUN: %t 2>&1 - -#include <assert.h> -#include <pthread.h> -#include <stdio.h> -#include <stdlib.h> -#include <sys/wait.h> -#include <unistd.h> - -__thread void *thread_local_var; - -void *exit_thread_func(void *arg) { - exit(0); -} - -void ExitFromThread() { - pthread_t tid; - int res; - res = pthread_create(&tid, 0, exit_thread_func, 0); - assert(res == 0); - pthread_join(tid, 0); -} - -int main() { - int status = 0; - thread_local_var = malloc(1337); - pid_t pid = fork(); - assert(pid >= 0); - if (pid > 0) { - waitpid(pid, &status, 0); - assert(WIFEXITED(status)); - return WEXITSTATUS(status); - } else { - // Spawn a thread and call exit() from there, to check that we track main - // thread's pid correctly even if leak checking is initiated from another - // thread. - ExitFromThread(); - } - return 0; -} diff --git a/lib/lsan/lit_tests/TestCases/high_allocator_contention.cc b/lib/lsan/lit_tests/TestCases/high_allocator_contention.cc deleted file mode 100644 index 1cecb2a550a1..000000000000 --- a/lib/lsan/lit_tests/TestCases/high_allocator_contention.cc +++ /dev/null @@ -1,48 +0,0 @@ -// A benchmark that executes malloc/free pairs in parallel. -// Usage: ./a.out number_of_threads total_number_of_allocations -// RUN: %clangxx_lsan %s -o %t -// RUN: %t 5 1000000 2>&1 -#include <assert.h> -#include <pthread.h> -#include <stdlib.h> -#include <stdio.h> - -int num_threads; -int total_num_alloc; -const int kMaxNumThreads = 5000; -pthread_t tid[kMaxNumThreads]; - -pthread_cond_t cond = PTHREAD_COND_INITIALIZER; -pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER; -bool go = false; - -void *thread_fun(void *arg) { - pthread_mutex_lock(&mutex); - while (!go) pthread_cond_wait(&cond, &mutex); - pthread_mutex_unlock(&mutex); - for (int i = 0; i < total_num_alloc / num_threads; i++) { - void *p = malloc(10); - __asm__ __volatile__("" : : "r"(p) : "memory"); - free((void *)p); - } - return 0; -} - -int main(int argc, char** argv) { - assert(argc == 3); - num_threads = atoi(argv[1]); - assert(num_threads > 0); - assert(num_threads <= kMaxNumThreads); - total_num_alloc = atoi(argv[2]); - assert(total_num_alloc > 0); - printf("%d threads, %d allocations in each\n", num_threads, - total_num_alloc / num_threads); - for (int i = 0; i < num_threads; i++) - pthread_create(&tid[i], 0, thread_fun, 0); - pthread_mutex_lock(&mutex); - go = true; - pthread_cond_broadcast(&cond); - pthread_mutex_unlock(&mutex); - for (int i = 0; i < num_threads; i++) pthread_join(tid[i], 0); - return 0; -} diff --git a/lib/lsan/lit_tests/TestCases/ignore_object.cc b/lib/lsan/lit_tests/TestCases/ignore_object.cc deleted file mode 100644 index cbc743b75497..000000000000 --- a/lib/lsan/lit_tests/TestCases/ignore_object.cc +++ /dev/null @@ -1,30 +0,0 @@ -// Test for __lsan_ignore_object(). -// RUN: LSAN_BASE="report_objects=1:use_registers=0:use_stacks=0:use_globals=0:use_tls=0:verbosity=3" -// RUN: %clangxx_lsan %s -o %t -// RUN: LSAN_OPTIONS=$LSAN_BASE not %t 2>&1 | FileCheck %s - -#include <stdio.h> -#include <stdlib.h> - -#include "sanitizer/lsan_interface.h" - -int main() { - { - // The first malloc call can cause an allocation in libdl. Ignore it here so - // it doesn't show up in our output. - __lsan::ScopedDisabler d; - malloc(1); - } - // Explicitly ignored object. - void **p = new void *; - // Transitively ignored object. - *p = malloc(666); - // Non-ignored object. - volatile void *q = malloc(1337); - fprintf(stderr, "Test alloc: %p.\n", p); - __lsan_ignore_object(p); - return 0; -} -// CHECK: Test alloc: [[ADDR:.*]]. -// CHECK: ignoring heap object at [[ADDR]] -// CHECK: SUMMARY: {{(Leak|Address)}}Sanitizer: 1337 byte(s) leaked in 1 allocation(s) diff --git a/lib/lsan/lit_tests/TestCases/ignore_object_errors.cc b/lib/lsan/lit_tests/TestCases/ignore_object_errors.cc deleted file mode 100644 index 2a6c72551772..000000000000 --- a/lib/lsan/lit_tests/TestCases/ignore_object_errors.cc +++ /dev/null @@ -1,22 +0,0 @@ -// Test for incorrect use of __lsan_ignore_object(). -// RUN: LSAN_BASE="verbosity=2" -// RUN: %clangxx_lsan %s -o %t -// RUN: LSAN_OPTIONS=$LSAN_BASE %t 2>&1 | FileCheck %s - -#include <stdio.h> -#include <stdlib.h> - -#include "sanitizer/lsan_interface.h" - -int main() { - void *p = malloc(1337); - fprintf(stderr, "Test alloc: %p.\n", p); - __lsan_ignore_object(p); - __lsan_ignore_object(p); - free(p); - __lsan_ignore_object(p); - return 0; -} -// CHECK: Test alloc: [[ADDR:.*]]. -// CHECK: heap object at [[ADDR]] is already being ignored -// CHECK: no heap object found at [[ADDR]] diff --git a/lib/lsan/lit_tests/TestCases/large_allocation_leak.cc b/lib/lsan/lit_tests/TestCases/large_allocation_leak.cc deleted file mode 100644 index 57d056597ee2..000000000000 --- a/lib/lsan/lit_tests/TestCases/large_allocation_leak.cc +++ /dev/null @@ -1,18 +0,0 @@ -// Test that LargeMmapAllocator's chunks aren't reachable via some internal data structure. -// RUN: LSAN_BASE="report_objects=1:use_stacks=0:use_registers=0" -// RUN: %clangxx_lsan %s -o %t -// RUN: LSAN_OPTIONS=$LSAN_BASE not %t 2>&1 | FileCheck %s - -#include <stdio.h> -#include <stdlib.h> - -int main() { - // maxsize in primary allocator is always less than this (1 << 25). - void *large_alloc = malloc(33554432); - fprintf(stderr, "Test alloc: %p.\n", large_alloc); - return 0; -} -// CHECK: Test alloc: [[ADDR:.*]]. -// CHECK: Directly leaked 33554432 byte object at [[ADDR]] -// CHECK: LeakSanitizer: detected memory leaks -// CHECK: SUMMARY: {{(Leak|Address)}}Sanitizer: diff --git a/lib/lsan/lit_tests/TestCases/leak_check_at_exit.cc b/lib/lsan/lit_tests/TestCases/leak_check_at_exit.cc deleted file mode 100644 index 38c1063b6ebb..000000000000 --- a/lib/lsan/lit_tests/TestCases/leak_check_at_exit.cc +++ /dev/null @@ -1,19 +0,0 @@ -// Test for the leak_check_at_exit flag. -// RUN: %clangxx_lsan %s -o %t -// RUN: LSAN_OPTIONS="verbosity=1" %t foo 2>&1 | FileCheck %s --check-prefix=CHECK-do -// RUN: LSAN_OPTIONS="verbosity=1" %t 2>&1 | FileCheck %s --check-prefix=CHECK-do -// RUN: LSAN_OPTIONS="verbosity=1:leak_check_at_exit=0" ASAN_OPTIONS="$ASAN_OPTIONS:leak_check_at_exit=0" %t foo 2>&1 | FileCheck %s --check-prefix=CHECK-do -// RUN: LSAN_OPTIONS="verbosity=1:leak_check_at_exit=0" ASAN_OPTIONS="$ASAN_OPTIONS:leak_check_at_exit=0" %t 2>&1 | FileCheck %s --check-prefix=CHECK-dont - -#include <stdio.h> -#include <sanitizer/lsan_interface.h> - -int main(int argc, char *argv[]) { - printf("printf to break optimization\n"); - if (argc > 1) - __lsan_do_leak_check(); - return 0; -} - -// CHECK-do: SUMMARY: {{(Leak|Address)}}Sanitizer: -// CHECK-dont-NOT: SUMMARY: {{(Leak|Address)}}Sanitizer: diff --git a/lib/lsan/lit_tests/TestCases/link_turned_off.cc b/lib/lsan/lit_tests/TestCases/link_turned_off.cc deleted file mode 100644 index 93628a1d15ee..000000000000 --- a/lib/lsan/lit_tests/TestCases/link_turned_off.cc +++ /dev/null @@ -1,24 +0,0 @@ -// Test for disabling LSan at link-time. -// RUN: LSAN_BASE="use_stacks=0:use_registers=0" -// RUN: %clangxx_lsan %s -o %t -// RUN: LSAN_OPTIONS=$LSAN_BASE %t -// RUN: LSAN_OPTIONS=$LSAN_BASE not %t foo 2>&1 | FileCheck %s - -#include <sanitizer/lsan_interface.h> - -int argc_copy; - -extern "C" { -int __lsan_is_turned_off() { - return (argc_copy == 1); -} -} - -int main(int argc, char *argv[]) { - volatile int *x = new int; - *x = 42; - argc_copy = argc; - return 0; -} - -// CHECK: SUMMARY: {{(Leak|Address)}}Sanitizer: 4 byte(s) leaked in 1 allocation(s) diff --git a/lib/lsan/lit_tests/TestCases/pointer_to_self.cc b/lib/lsan/lit_tests/TestCases/pointer_to_self.cc deleted file mode 100644 index 0d2818d2fa1d..000000000000 --- a/lib/lsan/lit_tests/TestCases/pointer_to_self.cc +++ /dev/null @@ -1,18 +0,0 @@ -// Regression test: pointers to self should not confuse LSan into thinking the -// object is indirectly leaked. Only external pointers count. -// RUN: LSAN_BASE="report_objects=1:use_registers=0" -// RUN: %clangxx_lsan %s -o %t -// RUN: LSAN_OPTIONS=$LSAN_BASE:"use_stacks=0" not %t 2>&1 | FileCheck %s - -#include <stdio.h> -#include <stdlib.h> - -int main() { - void *p = malloc(1337); - *reinterpret_cast<void **>(p) = p; - fprintf(stderr, "Test alloc: %p.\n", p); -} -// CHECK: Test alloc: [[ADDR:.*]]. -// CHECK: Directly leaked 1337 byte object at [[ADDR]] -// CHECK: LeakSanitizer: detected memory leaks -// CHECK: SUMMARY: {{(Leak|Address)}}Sanitizer: diff --git a/lib/lsan/lit_tests/TestCases/sanity_check_pure_c.c b/lib/lsan/lit_tests/TestCases/sanity_check_pure_c.c deleted file mode 100644 index 085412b47d55..000000000000 --- a/lib/lsan/lit_tests/TestCases/sanity_check_pure_c.c +++ /dev/null @@ -1,10 +0,0 @@ -// Check that we can build C code. -// RUN: %clang_lsan %s -o %t -#ifdef __cplusplus -#error "This test must be built in C mode" -#endif - -int main() { - // FIXME: ideally this should somehow check that we don't have libstdc++ - return 0; -} diff --git a/lib/lsan/lit_tests/TestCases/stale_stack_leak.cc b/lib/lsan/lit_tests/TestCases/stale_stack_leak.cc deleted file mode 100644 index fabfb4ff21a9..000000000000 --- a/lib/lsan/lit_tests/TestCases/stale_stack_leak.cc +++ /dev/null @@ -1,42 +0,0 @@ -// Test that out-of-scope local variables are ignored by LSan. -// RUN: LSAN_BASE="report_objects=1:use_registers=0:use_stacks=1" -// RUN: %clangxx_lsan %s -o %t -// RUN: LSAN_OPTIONS=$LSAN_BASE not %t 2>&1 | FileCheck %s -// RUN: LSAN_OPTIONS=$LSAN_BASE":exitcode=0" %t 2>&1 | FileCheck --check-prefix=CHECK-sanity %s - -#include <stdio.h> -#include <stdlib.h> - -void **pp; - -// Put pointer far enough on the stack that LSan has space to run in without -// overwriting it. -// Hopefully the argument p will be passed on a register, saving us from false -// negatives. -__attribute__((noinline)) -void *PutPointerOnStaleStack(void *p) { - void *locals[2048]; - locals[0] = p; - pp = &locals[0]; - fprintf(stderr, "Test alloc: %p.\n", locals[0]); - return 0; -} - -int main() { - PutPointerOnStaleStack(malloc(1337)); - return 0; -} - -// This must run after LSan, to ensure LSan didn't overwrite the pointer before -// it had a chance to see it. If LSan is invoked with atexit(), this works. -// Otherwise, we need a different method. -__attribute__((destructor)) -void ConfirmPointerHasSurvived() { - fprintf(stderr, "Value after LSan: %p.\n", *pp); -} -// CHECK: Test alloc: [[ADDR:.*]]. -// CHECK-sanity: Test alloc: [[ADDR:.*]]. -// CHECK: Directly leaked 1337 byte object at [[ADDR]] -// CHECK: LeakSanitizer: detected memory leaks -// CHECK: SUMMARY: {{(Leak|Address)}}Sanitizer: -// CHECK-sanity: Value after LSan: [[ADDR]]. diff --git a/lib/lsan/lit_tests/TestCases/suppressions_default.cc b/lib/lsan/lit_tests/TestCases/suppressions_default.cc deleted file mode 100644 index 9a165f8770f9..000000000000 --- a/lib/lsan/lit_tests/TestCases/suppressions_default.cc +++ /dev/null @@ -1,29 +0,0 @@ -// Test for ScopedDisabler. -// RUN: LSAN_BASE="use_registers=0:use_stacks=0" -// RUN: %clangxx_lsan %s -o %t -// RUN: LSAN_OPTIONS=$LSAN_BASE not %t 2>&1 | FileCheck %s - -#include <stdio.h> -#include <stdlib.h> - -#include "sanitizer/lsan_interface.h" - -extern "C" -const char *__lsan_default_suppressions() { - return "leak:*LSanTestLeakingFunc*"; -} - -void LSanTestLeakingFunc() { - void *p = malloc(666); - fprintf(stderr, "Test alloc: %p.\n", p); -} - -int main() { - LSanTestLeakingFunc(); - void *q = malloc(1337); - fprintf(stderr, "Test alloc: %p.\n", q); - return 0; -} -// CHECK: Suppressions used: -// CHECK: 1 666 *LSanTestLeakingFunc* -// CHECK: SUMMARY: {{(Leak|Address)}}Sanitizer: 1337 byte(s) leaked in 1 allocation(s) diff --git a/lib/lsan/lit_tests/TestCases/suppressions_file.cc b/lib/lsan/lit_tests/TestCases/suppressions_file.cc deleted file mode 100644 index 9a165f8770f9..000000000000 --- a/lib/lsan/lit_tests/TestCases/suppressions_file.cc +++ /dev/null @@ -1,29 +0,0 @@ -// Test for ScopedDisabler. -// RUN: LSAN_BASE="use_registers=0:use_stacks=0" -// RUN: %clangxx_lsan %s -o %t -// RUN: LSAN_OPTIONS=$LSAN_BASE not %t 2>&1 | FileCheck %s - -#include <stdio.h> -#include <stdlib.h> - -#include "sanitizer/lsan_interface.h" - -extern "C" -const char *__lsan_default_suppressions() { - return "leak:*LSanTestLeakingFunc*"; -} - -void LSanTestLeakingFunc() { - void *p = malloc(666); - fprintf(stderr, "Test alloc: %p.\n", p); -} - -int main() { - LSanTestLeakingFunc(); - void *q = malloc(1337); - fprintf(stderr, "Test alloc: %p.\n", q); - return 0; -} -// CHECK: Suppressions used: -// CHECK: 1 666 *LSanTestLeakingFunc* -// CHECK: SUMMARY: {{(Leak|Address)}}Sanitizer: 1337 byte(s) leaked in 1 allocation(s) diff --git a/lib/lsan/lit_tests/TestCases/suppressions_file.cc.supp b/lib/lsan/lit_tests/TestCases/suppressions_file.cc.supp deleted file mode 100644 index 8d8e560cba4c..000000000000 --- a/lib/lsan/lit_tests/TestCases/suppressions_file.cc.supp +++ /dev/null @@ -1 +0,0 @@ -leak:*LSanTestLeakingFunc* diff --git a/lib/lsan/lit_tests/TestCases/swapcontext.cc b/lib/lsan/lit_tests/TestCases/swapcontext.cc deleted file mode 100644 index a06685ca2f03..000000000000 --- a/lib/lsan/lit_tests/TestCases/swapcontext.cc +++ /dev/null @@ -1,42 +0,0 @@ -// We can't unwind stack if we're running coroutines on heap-allocated -// memory. Make sure we don't report these leaks. - -// RUN: %clangxx_lsan %s -o %t -// RUN: %t 2>&1 -// RUN: not %t foo 2>&1 | FileCheck %s - -#include <stdio.h> -#include <ucontext.h> -#include <unistd.h> - -const int kStackSize = 1 << 20; - -void Child() { - int child_stack; - printf("Child: %p\n", &child_stack); - int *leaked = new int[666]; -} - -int main(int argc, char *argv[]) { - char stack_memory[kStackSize + 1]; - char *heap_memory = new char[kStackSize + 1]; - char *child_stack = (argc > 1) ? stack_memory : heap_memory; - - printf("Child stack: %p\n", child_stack); - ucontext_t orig_context; - ucontext_t child_context; - getcontext(&child_context); - child_context.uc_stack.ss_sp = child_stack; - child_context.uc_stack.ss_size = kStackSize / 2; - child_context.uc_link = &orig_context; - makecontext(&child_context, Child, 0); - if (swapcontext(&orig_context, &child_context) < 0) { - perror("swapcontext"); - return 1; - } - - delete[] heap_memory; - return 0; -} - -// CHECK: SUMMARY: {{(Leak|Address)}}Sanitizer: 2664 byte(s) leaked in 1 allocation(s) diff --git a/lib/lsan/lit_tests/TestCases/use_after_return.cc b/lib/lsan/lit_tests/TestCases/use_after_return.cc deleted file mode 100644 index 93b0ea6068ef..000000000000 --- a/lib/lsan/lit_tests/TestCases/use_after_return.cc +++ /dev/null @@ -1,23 +0,0 @@ -// Test that fake stack (introduced by ASan's use-after-return mode) is included -// in the root set. -// RUN: LSAN_BASE="report_objects=1:use_registers=0" -// RUN: %clangxx_lsan %s -O2 -o %t -// RUN: ASAN_OPTIONS=$ASAN_OPTIONS:detect_stack_use_after_return=1 LSAN_OPTIONS=$LSAN_BASE:"use_stacks=0" not %t 2>&1 | FileCheck %s -// RUN: ASAN_OPTIONS=$ASAN_OPTIONS:detect_stack_use_after_return=1 LSAN_OPTIONS=$LSAN_BASE:"use_stacks=1" %t 2>&1 -// RUN: ASAN_OPTIONS=$ASAN_OPTIONS:detect_stack_use_after_return=1 LSAN_OPTIONS="" %t 2>&1 - -#include <stdio.h> -#include <stdlib.h> - -int main() { - void *stack_var = malloc(1337); - fprintf(stderr, "Test alloc: %p.\n", stack_var); - // Take pointer to variable, to ensure it's not optimized into a register. - fprintf(stderr, "Stack var at: %p.\n", &stack_var); - // Do not return from main to prevent the pointer from going out of scope. - exit(0); -} -// CHECK: Test alloc: [[ADDR:.*]]. -// CHECK: Directly leaked 1337 byte object at [[ADDR]] -// CHECK: LeakSanitizer: detected memory leaks -// CHECK: SUMMARY: {{(Leak|Address)}}Sanitizer: diff --git a/lib/lsan/lit_tests/TestCases/use_globals_initialized.cc b/lib/lsan/lit_tests/TestCases/use_globals_initialized.cc deleted file mode 100644 index 5a7c48bdf49a..000000000000 --- a/lib/lsan/lit_tests/TestCases/use_globals_initialized.cc +++ /dev/null @@ -1,21 +0,0 @@ -// Test that initialized globals are included in the root set. -// RUN: LSAN_BASE="report_objects=1:use_stacks=0:use_registers=0" -// RUN: %clangxx_lsan %s -o %t -// RUN: LSAN_OPTIONS=$LSAN_BASE:"use_globals=0" not %t 2>&1 | FileCheck %s -// RUN: LSAN_OPTIONS=$LSAN_BASE:"use_globals=1" %t 2>&1 -// RUN: LSAN_OPTIONS="" %t 2>&1 - -#include <stdio.h> -#include <stdlib.h> - -void *data_var = (void *)1; - -int main() { - data_var = malloc(1337); - fprintf(stderr, "Test alloc: %p.\n", data_var); - return 0; -} -// CHECK: Test alloc: [[ADDR:.*]]. -// CHECK: Directly leaked 1337 byte object at [[ADDR]] -// CHECK: LeakSanitizer: detected memory leaks -// CHECK: SUMMARY: {{(Leak|Address)}}Sanitizer: diff --git a/lib/lsan/lit_tests/TestCases/use_globals_uninitialized.cc b/lib/lsan/lit_tests/TestCases/use_globals_uninitialized.cc deleted file mode 100644 index e1d045e3f79f..000000000000 --- a/lib/lsan/lit_tests/TestCases/use_globals_uninitialized.cc +++ /dev/null @@ -1,21 +0,0 @@ -// Test that uninitialized globals are included in the root set. -// RUN: LSAN_BASE="report_objects=1:use_stacks=0:use_registers=0" -// RUN: %clangxx_lsan %s -o %t -// RUN: LSAN_OPTIONS=$LSAN_BASE:"use_globals=0" not %t 2>&1 | FileCheck %s -// RUN: LSAN_OPTIONS=$LSAN_BASE:"use_globals=1" %t 2>&1 -// RUN: LSAN_OPTIONS="" %t 2>&1 - -#include <stdio.h> -#include <stdlib.h> - -void *bss_var; - -int main() { - bss_var = malloc(1337); - fprintf(stderr, "Test alloc: %p.\n", bss_var); - return 0; -} -// CHECK: Test alloc: [[ADDR:.*]]. -// CHECK: Directly leaked 1337 byte object at [[ADDR]] -// CHECK: LeakSanitizer: detected memory leaks -// CHECK: SUMMARY: {{(Leak|Address)}}Sanitizer: diff --git a/lib/lsan/lit_tests/TestCases/use_registers.cc b/lib/lsan/lit_tests/TestCases/use_registers.cc deleted file mode 100644 index a7d8a69d7173..000000000000 --- a/lib/lsan/lit_tests/TestCases/use_registers.cc +++ /dev/null @@ -1,51 +0,0 @@ -// Test that registers of running threads are included in the root set. -// RUN: LSAN_BASE="report_objects=1:use_stacks=0" -// RUN: %clangxx_lsan -pthread %s -o %t -// RUN: LSAN_OPTIONS=$LSAN_BASE:"use_registers=0" not %t 2>&1 | FileCheck %s -// RUN: LSAN_OPTIONS=$LSAN_BASE:"use_registers=1" %t 2>&1 -// RUN: LSAN_OPTIONS="" %t 2>&1 - -#include <assert.h> -#include <pthread.h> -#include <stdio.h> -#include <stdlib.h> - -extern "C" -void *registers_thread_func(void *arg) { - int *sync = reinterpret_cast<int *>(arg); - void *p = malloc(1337); - // To store the pointer, choose a register which is unlikely to be reused by - // a function call. -#if defined(__i386__) - asm ( "mov %0, %%esi" - : - : "r" (p) - ); -#elif defined(__x86_64__) - asm ( "mov %0, %%r15" - : - : "r" (p) - ); -#else -#error "Test is not supported on this architecture." -#endif - fprintf(stderr, "Test alloc: %p.\n", p); - fflush(stderr); - __sync_fetch_and_xor(sync, 1); - while (true) - pthread_yield(); -} - -int main() { - int sync = 0; - pthread_t thread_id; - int res = pthread_create(&thread_id, 0, registers_thread_func, &sync); - assert(res == 0); - while (!__sync_fetch_and_xor(&sync, 0)) - pthread_yield(); - return 0; -} -// CHECK: Test alloc: [[ADDR:.*]]. -// CHECK: Directly leaked 1337 byte object at [[ADDR]] -// CHECK: LeakSanitizer: detected memory leaks -// CHECK: SUMMARY: {{(Leak|Address)}}Sanitizer: diff --git a/lib/lsan/lit_tests/TestCases/use_stacks.cc b/lib/lsan/lit_tests/TestCases/use_stacks.cc deleted file mode 100644 index 4287a96b2285..000000000000 --- a/lib/lsan/lit_tests/TestCases/use_stacks.cc +++ /dev/null @@ -1,20 +0,0 @@ -// Test that stack of main thread is included in the root set. -// RUN: LSAN_BASE="report_objects=1:use_registers=0" -// RUN: %clangxx_lsan %s -o %t -// RUN: LSAN_OPTIONS=$LSAN_BASE:"use_stacks=0" not %t 2>&1 | FileCheck %s -// RUN: LSAN_OPTIONS=$LSAN_BASE:"use_stacks=1" %t 2>&1 -// RUN: LSAN_OPTIONS="" %t 2>&1 - -#include <stdio.h> -#include <stdlib.h> - -int main() { - void *stack_var = malloc(1337); - fprintf(stderr, "Test alloc: %p.\n", stack_var); - // Do not return from main to prevent the pointer from going out of scope. - exit(0); -} -// CHECK: Test alloc: [[ADDR:.*]]. -// CHECK: Directly leaked 1337 byte object at [[ADDR]] -// CHECK: LeakSanitizer: detected memory leaks -// CHECK: SUMMARY: {{(Leak|Address)}}Sanitizer: diff --git a/lib/lsan/lit_tests/TestCases/use_stacks_threaded.cc b/lib/lsan/lit_tests/TestCases/use_stacks_threaded.cc deleted file mode 100644 index c7dfaf8abad6..000000000000 --- a/lib/lsan/lit_tests/TestCases/use_stacks_threaded.cc +++ /dev/null @@ -1,36 +0,0 @@ -// Test that stacks of non-main threads are included in the root set. -// RUN: LSAN_BASE="report_objects=1:use_registers=0" -// RUN: %clangxx_lsan -pthread %s -o %t -// RUN: LSAN_OPTIONS=$LSAN_BASE:"use_stacks=0" not %t 2>&1 | FileCheck %s -// RUN: LSAN_OPTIONS=$LSAN_BASE:"use_stacks=1" %t 2>&1 -// RUN: LSAN_OPTIONS="" %t 2>&1 - -#include <assert.h> -#include <pthread.h> -#include <stdio.h> -#include <stdlib.h> - -extern "C" -void *stacks_thread_func(void *arg) { - int *sync = reinterpret_cast<int *>(arg); - void *p = malloc(1337); - fprintf(stderr, "Test alloc: %p.\n", p); - fflush(stderr); - __sync_fetch_and_xor(sync, 1); - while (true) - pthread_yield(); -} - -int main() { - int sync = 0; - pthread_t thread_id; - int res = pthread_create(&thread_id, 0, stacks_thread_func, &sync); - assert(res == 0); - while (!__sync_fetch_and_xor(&sync, 0)) - pthread_yield(); - return 0; -} -// CHECK: Test alloc: [[ADDR:.*]]. -// CHECK: Directly leaked 1337 byte object at [[ADDR]] -// CHECK: LeakSanitizer: detected memory leaks -// CHECK: SUMMARY: {{(Leak|Address)}}Sanitizer: diff --git a/lib/lsan/lit_tests/TestCases/use_tls_dynamic.cc b/lib/lsan/lit_tests/TestCases/use_tls_dynamic.cc deleted file mode 100644 index 2570b63f0c5e..000000000000 --- a/lib/lsan/lit_tests/TestCases/use_tls_dynamic.cc +++ /dev/null @@ -1,33 +0,0 @@ -// Test that dynamically allocated TLS space is included in the root set. -// RUN: LSAN_BASE="report_objects=1:use_stacks=0:use_registers=0" -// RUN: %clangxx %p/SharedLibs/huge_tls_lib_so.cc -fPIC -shared -o %t-so.so -// RUN: %clangxx_lsan %s -o %t -// RUN: LSAN_OPTIONS=$LSAN_BASE:"use_tls=0" not %t 2>&1 | FileCheck %s -// RUN: LSAN_OPTIONS=$LSAN_BASE:"use_tls=1" %t 2>&1 -// RUN: LSAN_OPTIONS="" %t 2>&1 - -#include <assert.h> -#include <dlfcn.h> -#include <stdio.h> -#include <stdlib.h> -#include <string> - -int main(int argc, char *argv[]) { - std::string path = std::string(argv[0]) + "-so.so"; - - void *handle = dlopen(path.c_str(), RTLD_LAZY); - assert(handle != 0); - typedef void **(* store_t)(void *p); - store_t StoreToTLS = (store_t)dlsym(handle, "StoreToTLS"); - assert(dlerror() == 0); - - void *p = malloc(1337); - void **p_in_tls = StoreToTLS(p); - assert(*p_in_tls == p); - fprintf(stderr, "Test alloc: %p.\n", p); - return 0; -} -// CHECK: Test alloc: [[ADDR:.*]]. -// CHECK: leaked 1337 byte object at [[ADDR]] -// CHECK: LeakSanitizer: detected memory leaks -// CHECK: SUMMARY: {{(Leak|Address)}}Sanitizer: diff --git a/lib/lsan/lit_tests/TestCases/use_tls_pthread_specific_dynamic.cc b/lib/lsan/lit_tests/TestCases/use_tls_pthread_specific_dynamic.cc deleted file mode 100644 index 3dea41edddd4..000000000000 --- a/lib/lsan/lit_tests/TestCases/use_tls_pthread_specific_dynamic.cc +++ /dev/null @@ -1,37 +0,0 @@ -// Test that dynamically allocated thread-specific storage is included in the root set. -// RUN: LSAN_BASE="report_objects=1:use_stacks=0:use_registers=0" -// RUN: %clangxx_lsan %s -o %t -// RUN: LSAN_OPTIONS=$LSAN_BASE:"use_tls=0" not %t 2>&1 | FileCheck %s -// RUN: LSAN_OPTIONS=$LSAN_BASE:"use_tls=1" %t 2>&1 -// RUN: LSAN_OPTIONS="" %t 2>&1 - -#include <assert.h> -#include <pthread.h> -#include <stdio.h> -#include <stdlib.h> - -// From glibc: this many keys are stored in the thread descriptor directly. -const unsigned PTHREAD_KEY_2NDLEVEL_SIZE = 32; - -int main() { - static const unsigned kDummyKeysCount = PTHREAD_KEY_2NDLEVEL_SIZE; - int res; - pthread_key_t dummy_keys[kDummyKeysCount]; - for (unsigned i = 0; i < kDummyKeysCount; i++) { - res = pthread_key_create(&dummy_keys[i], NULL); - assert(res == 0); - } - pthread_key_t key; - res = pthread_key_create(&key, NULL); - assert(key >= PTHREAD_KEY_2NDLEVEL_SIZE); - assert(res == 0); - void *p = malloc(1337); - res = pthread_setspecific(key, p); - assert(res == 0); - fprintf(stderr, "Test alloc: %p.\n", p); - return 0; -} -// CHECK: Test alloc: [[ADDR:.*]]. -// CHECK: leaked 1337 byte object at [[ADDR]] -// CHECK: LeakSanitizer: detected memory leaks -// CHECK: SUMMARY: {{(Leak|Address)}}Sanitizer: diff --git a/lib/lsan/lit_tests/TestCases/use_tls_pthread_specific_static.cc b/lib/lsan/lit_tests/TestCases/use_tls_pthread_specific_static.cc deleted file mode 100644 index b75f15153863..000000000000 --- a/lib/lsan/lit_tests/TestCases/use_tls_pthread_specific_static.cc +++ /dev/null @@ -1,31 +0,0 @@ -// Test that statically allocated thread-specific storage is included in the root set. -// RUN: LSAN_BASE="report_objects=1:use_stacks=0:use_registers=0" -// RUN: %clangxx_lsan %s -o %t -// RUN: LSAN_OPTIONS=$LSAN_BASE:"use_tls=0" not %t 2>&1 | FileCheck %s -// RUN: LSAN_OPTIONS=$LSAN_BASE:"use_tls=1" %t 2>&1 -// RUN: LSAN_OPTIONS="" %t 2>&1 - -#include <assert.h> -#include <pthread.h> -#include <stdio.h> -#include <stdlib.h> - -// From glibc: this many keys are stored in the thread descriptor directly. -const unsigned PTHREAD_KEY_2NDLEVEL_SIZE = 32; - -int main() { - pthread_key_t key; - int res; - res = pthread_key_create(&key, NULL); - assert(res == 0); - assert(key < PTHREAD_KEY_2NDLEVEL_SIZE); - void *p = malloc(1337); - res = pthread_setspecific(key, p); - assert(res == 0); - fprintf(stderr, "Test alloc: %p.\n", p); - return 0; -} -// CHECK: Test alloc: [[ADDR:.*]]. -// CHECK: Directly leaked 1337 byte object at [[ADDR]] -// CHECK: LeakSanitizer: detected memory leaks -// CHECK: SUMMARY: {{(Leak|Address)}}Sanitizer: diff --git a/lib/lsan/lit_tests/TestCases/use_tls_static.cc b/lib/lsan/lit_tests/TestCases/use_tls_static.cc deleted file mode 100644 index 9ccb2b2b7fb1..000000000000 --- a/lib/lsan/lit_tests/TestCases/use_tls_static.cc +++ /dev/null @@ -1,21 +0,0 @@ -// Test that statically allocated TLS space is included in the root set. -// RUN: LSAN_BASE="report_objects=1:use_stacks=0:use_registers=0" -// RUN: %clangxx_lsan %s -o %t -// RUN: LSAN_OPTIONS=$LSAN_BASE:"use_tls=0" not %t 2>&1 | FileCheck %s -// RUN: LSAN_OPTIONS=$LSAN_BASE:"use_tls=1" %t 2>&1 -// RUN: LSAN_OPTIONS="" %t 2>&1 - -#include <stdio.h> -#include <stdlib.h> - -__thread void *tls_var; - -int main() { - tls_var = malloc(1337); - fprintf(stderr, "Test alloc: %p.\n", tls_var); - return 0; -} -// CHECK: Test alloc: [[ADDR:.*]]. -// CHECK: Directly leaked 1337 byte object at [[ADDR]] -// CHECK: LeakSanitizer: detected memory leaks -// CHECK: SUMMARY: {{(Leak|Address)}}Sanitizer: diff --git a/lib/lsan/lit_tests/TestCases/use_unaligned.cc b/lib/lsan/lit_tests/TestCases/use_unaligned.cc deleted file mode 100644 index bc75f11b99f0..000000000000 --- a/lib/lsan/lit_tests/TestCases/use_unaligned.cc +++ /dev/null @@ -1,23 +0,0 @@ -// Test that unaligned pointers are detected correctly. -// RUN: LSAN_BASE="report_objects=1:use_stacks=0:use_registers=0" -// RUN: %clangxx_lsan %s -o %t -// RUN: LSAN_OPTIONS=$LSAN_BASE:"use_unaligned=0" not %t 2>&1 | FileCheck %s -// RUN: LSAN_OPTIONS=$LSAN_BASE:"use_unaligned=1" %t 2>&1 - -#include <stdio.h> -#include <stdlib.h> -#include <string.h> - -void *arr[2]; - -int main() { - void *p = malloc(1337); - fprintf(stderr, "Test alloc: %p.\n", p); - char *char_arr = (char *)arr; - memcpy(char_arr + 1, &p, sizeof(p)); - return 0; -} -// CHECK: Test alloc: [[ADDR:.*]]. -// CHECK: Directly leaked 1337 byte object at [[ADDR]] -// CHECK: LeakSanitizer: detected memory leaks -// CHECK: SUMMARY: {{(Leak|Address)}}Sanitizer: diff --git a/lib/lsan/lit_tests/Unit/lit.site.cfg.in b/lib/lsan/lit_tests/Unit/lit.site.cfg.in deleted file mode 100644 index a3a4e9ad0b13..000000000000 --- a/lib/lsan/lit_tests/Unit/lit.site.cfg.in +++ /dev/null @@ -1,12 +0,0 @@ -## Autogenerated by LLVM/Clang configuration. -# Do not edit! - -# Load common config for all compiler-rt unit tests. -lit_config.load_config(config, "@COMPILER_RT_BINARY_DIR@/lib/lit.common.unit.configured") - -# Setup config name. -config.name = 'LeakSanitizer-Unit' -# Setup test source and exec root. For unit tests, we define -# it as build directory with LSan unit tests. -config.test_exec_root = "@LSAN_BINARY_DIR@/tests" -config.test_source_root = config.test_exec_root diff --git a/lib/lsan/lit_tests/lit.common.cfg b/lib/lsan/lit_tests/lit.common.cfg deleted file mode 100644 index 96dc1b1f55fc..000000000000 --- a/lib/lsan/lit_tests/lit.common.cfg +++ /dev/null @@ -1,43 +0,0 @@ -# -*- Python -*- - -# Common configuration for running leak detection tests under LSan/ASan. - -import os - -def get_required_attr(config, attr_name): - attr_value = getattr(config, attr_name, None) - if not attr_value: - lit_config.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 source root. -lsan_lit_src_root = get_required_attr(config, 'lsan_lit_src_root') -config.test_source_root = os.path.join(lsan_lit_src_root, 'TestCases') - -clang_cxxflags = ("--driver-mode=g++ " - + "-g " - + "-O0 " - + "-m64 ") - -clang_cflags = ("-g " - + "-O0 " - + "-m64 ") - -config.clang_cxxflags = clang_cxxflags - -config.substitutions.append( ("%clangxx ", (" " + config.clang + " " + - clang_cxxflags + " ")) ) - -config.clang_cflags = clang_cflags - -config.substitutions.append( ("%clang ", (" " + config.clang + " " + - clang_cflags + " ")) ) - -# LeakSanitizer tests are currently supported on x86-64 Linux only. -if config.host_os not in ['Linux'] or config.host_arch not in ['x86_64']: - config.unsupported = True - -config.suffixes = ['.c', '.cc', '.cpp'] diff --git a/lib/lsan/lsan.cc b/lib/lsan/lsan.cc index 058bbdba3907..1598fcac4a76 100644 --- a/lib/lsan/lsan.cc +++ b/lib/lsan/lsan.cc @@ -25,14 +25,9 @@ bool lsan_init_is_running; namespace __lsan { -static void InitializeCommonFlags() { - CommonFlags *cf = common_flags(); - SetCommonFlagDefaults(); - cf->external_symbolizer_path = GetEnv("LSAN_SYMBOLIZER_PATH"); - cf->malloc_context_size = 30; - cf->detect_leaks = true; - - ParseCommonFlagsFromString(GetEnv("LSAN_OPTIONS")); +///// Interface to the common LSan module. ///// +bool WordIsPoisoned(uptr addr) { + return false; } } // namespace __lsan @@ -45,7 +40,7 @@ extern "C" void __lsan_init() { return; lsan_init_is_running = true; SanitizerToolName = "LeakSanitizer"; - InitializeCommonFlags(); + InitCommonLsan(true); InitializeAllocator(); InitTlsSize(); InitializeInterceptors(); @@ -55,17 +50,14 @@ extern "C" void __lsan_init() { ThreadStart(tid, GetTid()); SetCurrentThread(tid); - // Start symbolizer process if necessary. - if (common_flags()->symbolize) { - Symbolizer::Init(common_flags()->external_symbolizer_path); - } else { - Symbolizer::Disable(); - } - - InitCommonLsan(); if (common_flags()->detect_leaks && common_flags()->leak_check_at_exit) Atexit(DoLeakCheck); lsan_inited = true; lsan_init_is_running = false; } +extern "C" SANITIZER_INTERFACE_ATTRIBUTE +void __sanitizer_print_stack_trace() { + GET_STACK_TRACE_FATAL; + stack.Print(); +} diff --git a/lib/lsan/lsan.h b/lib/lsan/lsan.h index 3e7f76b08193..53783cdc9c7d 100644 --- a/lib/lsan/lsan.h +++ b/lib/lsan/lsan.h @@ -15,6 +15,26 @@ #include "sanitizer_common/sanitizer_flags.h" #include "sanitizer_common/sanitizer_stacktrace.h" +#define GET_STACK_TRACE(max_size, fast) \ + BufferedStackTrace stack; \ + { \ + uptr stack_top = 0, stack_bottom = 0; \ + ThreadContext *t; \ + if (fast && (t = CurrentThreadContext())) { \ + stack_top = t->stack_end(); \ + stack_bottom = t->stack_begin(); \ + } \ + stack.Unwind(max_size, StackTrace::GetCurrentPc(), GET_CURRENT_FRAME(), \ + /* context */ 0, stack_top, stack_bottom, fast); \ + } + +#define GET_STACK_TRACE_FATAL \ + GET_STACK_TRACE(kStackTraceMax, common_flags()->fast_unwind_on_fatal) + +#define GET_STACK_TRACE_MALLOC \ + GET_STACK_TRACE(__sanitizer::common_flags()->malloc_context_size, \ + common_flags()->fast_unwind_on_malloc) + namespace __lsan { void InitializeInterceptors(); diff --git a/lib/lsan/lsan_allocator.cc b/lib/lsan/lsan_allocator.cc index f7eee1314bf0..8be2a2ad9224 100644 --- a/lib/lsan/lsan_allocator.cc +++ b/lib/lsan/lsan_allocator.cc @@ -15,6 +15,7 @@ #include "lsan_allocator.h" #include "sanitizer_common/sanitizer_allocator.h" +#include "sanitizer_common/sanitizer_allocator_interface.h" #include "sanitizer_common/sanitizer_internal_defs.h" #include "sanitizer_common/sanitizer_stackdepot.h" #include "sanitizer_common/sanitizer_stacktrace.h" @@ -53,7 +54,7 @@ void AllocatorThreadFinish() { allocator.SwallowCache(&cache); } -static ChunkMetadata *Metadata(void *p) { +static ChunkMetadata *Metadata(const void *p) { return reinterpret_cast<ChunkMetadata *>(allocator.GetMetaData(p)); } @@ -62,7 +63,7 @@ static void RegisterAllocation(const StackTrace &stack, void *p, uptr size) { ChunkMetadata *m = Metadata(p); CHECK(m); m->tag = DisabledInThisThread() ? kIgnored : kDirectlyLeaked; - m->stack_trace_id = StackDepotPut(stack.trace, stack.size); + m->stack_trace_id = StackDepotPut(stack); m->requested_size = size; atomic_store(reinterpret_cast<atomic_uint8_t *>(m), 1, memory_order_relaxed); } @@ -87,10 +88,12 @@ void *Allocate(const StackTrace &stack, uptr size, uptr alignment, if (cleared && allocator.FromPrimary(p)) memset(p, 0, size); RegisterAllocation(stack, p, size); + if (&__sanitizer_malloc_hook) __sanitizer_malloc_hook(p, size); return p; } void Deallocate(void *p) { + if (&__sanitizer_free_hook) __sanitizer_free_hook(p); RegisterDeallocation(p); allocator.Deallocate(&cache, p); } @@ -113,7 +116,7 @@ void GetAllocatorCacheRange(uptr *begin, uptr *end) { *end = *begin + sizeof(cache); } -uptr GetMallocUsableSize(void *p) { +uptr GetMallocUsableSize(const void *p) { ChunkMetadata *m = Metadata(p); if (!m) return 0; return m->requested_size; @@ -143,7 +146,11 @@ uptr PointsIntoChunk(void* p) { if (addr < chunk) return 0; ChunkMetadata *m = Metadata(reinterpret_cast<void *>(chunk)); CHECK(m); - if (m->allocated && addr < chunk + m->requested_size) + if (!m->allocated) + return 0; + if (addr < chunk + m->requested_size) + return chunk; + if (IsSpecialCaseOfOperatorNew0(chunk, m->requested_size, addr)) return chunk; return 0; } @@ -196,3 +203,38 @@ IgnoreObjectResult IgnoreObjectLocked(const void *p) { } } } // namespace __lsan + +using namespace __lsan; + +extern "C" { +SANITIZER_INTERFACE_ATTRIBUTE +uptr __sanitizer_get_current_allocated_bytes() { + uptr stats[AllocatorStatCount]; + allocator.GetStats(stats); + return stats[AllocatorStatAllocated]; +} + +SANITIZER_INTERFACE_ATTRIBUTE +uptr __sanitizer_get_heap_size() { + uptr stats[AllocatorStatCount]; + allocator.GetStats(stats); + return stats[AllocatorStatMapped]; +} + +SANITIZER_INTERFACE_ATTRIBUTE +uptr __sanitizer_get_free_bytes() { return 0; } + +SANITIZER_INTERFACE_ATTRIBUTE +uptr __sanitizer_get_unmapped_bytes() { return 0; } + +SANITIZER_INTERFACE_ATTRIBUTE +uptr __sanitizer_get_estimated_allocated_size(uptr size) { return size; } + +SANITIZER_INTERFACE_ATTRIBUTE +int __sanitizer_get_ownership(const void *p) { return Metadata(p) != 0; } + +SANITIZER_INTERFACE_ATTRIBUTE +uptr __sanitizer_get_allocated_size(const void *p) { + return GetMallocUsableSize(p); +} +} // extern "C" diff --git a/lib/lsan/lsan_allocator.h b/lib/lsan/lsan_allocator.h index 00c55ae02f15..f564601193bd 100644 --- a/lib/lsan/lsan_allocator.h +++ b/lib/lsan/lsan_allocator.h @@ -25,7 +25,7 @@ void *Allocate(const StackTrace &stack, uptr size, uptr alignment, void Deallocate(void *p); void *Reallocate(const StackTrace &stack, void *p, uptr new_size, uptr alignment); -uptr GetMallocUsableSize(void *p); +uptr GetMallocUsableSize(const void *p); template<typename Callable> void ForEachChunk(const Callable &callback); diff --git a/lib/lsan/lsan_common.cc b/lib/lsan/lsan_common.cc index 152588411e2f..c2ba52e46911 100644 --- a/lib/lsan/lsan_common.cc +++ b/lib/lsan/lsan_common.cc @@ -17,6 +17,7 @@ #include "sanitizer_common/sanitizer_common.h" #include "sanitizer_common/sanitizer_flags.h" #include "sanitizer_common/sanitizer_placement_new.h" +#include "sanitizer_common/sanitizer_procmaps.h" #include "sanitizer_common/sanitizer_stackdepot.h" #include "sanitizer_common/sanitizer_stacktrace.h" #include "sanitizer_common/sanitizer_stoptheworld.h" @@ -26,7 +27,8 @@ #if CAN_SANITIZE_LEAKS namespace __lsan { -// This mutex is used to prevent races between DoLeakCheck and IgnoreObject. +// This mutex is used to prevent races between DoLeakCheck and IgnoreObject, and +// also to protect the global list of root regions. BlockingMutex global_mutex(LINKER_INITIALIZED); THREADLOCAL int disable_counter; @@ -34,65 +36,90 @@ bool DisabledInThisThread() { return disable_counter > 0; } Flags lsan_flags; -static void InitializeFlags() { +static void InitializeFlags(bool standalone) { Flags *f = flags(); // Default values. f->report_objects = false; f->resolution = 0; f->max_leaks = 0; f->exitcode = 23; - f->suppressions=""; f->use_registers = true; f->use_globals = true; f->use_stacks = true; f->use_tls = true; + f->use_root_regions = true; f->use_unaligned = false; - f->verbosity = 0; + f->use_poisoned = false; f->log_pointers = false; f->log_threads = false; const char *options = GetEnv("LSAN_OPTIONS"); if (options) { - ParseFlag(options, &f->use_registers, "use_registers"); - ParseFlag(options, &f->use_globals, "use_globals"); - ParseFlag(options, &f->use_stacks, "use_stacks"); - ParseFlag(options, &f->use_tls, "use_tls"); - ParseFlag(options, &f->use_unaligned, "use_unaligned"); - ParseFlag(options, &f->report_objects, "report_objects"); - ParseFlag(options, &f->resolution, "resolution"); + ParseFlag(options, &f->use_registers, "use_registers", ""); + ParseFlag(options, &f->use_globals, "use_globals", ""); + ParseFlag(options, &f->use_stacks, "use_stacks", ""); + ParseFlag(options, &f->use_tls, "use_tls", ""); + ParseFlag(options, &f->use_root_regions, "use_root_regions", ""); + ParseFlag(options, &f->use_unaligned, "use_unaligned", ""); + ParseFlag(options, &f->use_poisoned, "use_poisoned", ""); + ParseFlag(options, &f->report_objects, "report_objects", ""); + ParseFlag(options, &f->resolution, "resolution", ""); CHECK_GE(&f->resolution, 0); - ParseFlag(options, &f->max_leaks, "max_leaks"); + ParseFlag(options, &f->max_leaks, "max_leaks", ""); CHECK_GE(&f->max_leaks, 0); - ParseFlag(options, &f->verbosity, "verbosity"); - ParseFlag(options, &f->log_pointers, "log_pointers"); - ParseFlag(options, &f->log_threads, "log_threads"); - ParseFlag(options, &f->exitcode, "exitcode"); - ParseFlag(options, &f->suppressions, "suppressions"); + ParseFlag(options, &f->log_pointers, "log_pointers", ""); + ParseFlag(options, &f->log_threads, "log_threads", ""); + ParseFlag(options, &f->exitcode, "exitcode", ""); } + + // Set defaults for common flags (only in standalone mode) and parse + // them from LSAN_OPTIONS. + CommonFlags *cf = common_flags(); + if (standalone) { + SetCommonFlagsDefaults(cf); + cf->external_symbolizer_path = GetEnv("LSAN_SYMBOLIZER_PATH"); + cf->malloc_context_size = 30; + cf->detect_leaks = true; + } + ParseCommonFlagsFromString(cf, options); } -SuppressionContext *suppression_ctx; +#define LOG_POINTERS(...) \ + do { \ + if (flags()->log_pointers) Report(__VA_ARGS__); \ + } while (0); + +#define LOG_THREADS(...) \ + do { \ + if (flags()->log_threads) Report(__VA_ARGS__); \ + } while (0); + +static bool suppressions_inited = false; void InitializeSuppressions() { - CHECK(!suppression_ctx); - ALIGNED(64) static char placeholder_[sizeof(SuppressionContext)]; - suppression_ctx = new(placeholder_) SuppressionContext; - char *suppressions_from_file; - uptr buffer_size; - if (ReadFileToBuffer(flags()->suppressions, &suppressions_from_file, - &buffer_size, 1 << 26 /* max_len */)) - suppression_ctx->Parse(suppressions_from_file); - if (flags()->suppressions[0] && !buffer_size) { - Printf("LeakSanitizer: failed to read suppressions file '%s'\n", - flags()->suppressions); - Die(); - } + CHECK(!suppressions_inited); + SuppressionContext::InitIfNecessary(); if (&__lsan_default_suppressions) - suppression_ctx->Parse(__lsan_default_suppressions()); + SuppressionContext::Get()->Parse(__lsan_default_suppressions()); + suppressions_inited = true; +} + +struct RootRegion { + const void *begin; + uptr size; +}; + +InternalMmapVector<RootRegion> *root_regions; + +void InitializeRootRegions() { + CHECK(!root_regions); + ALIGNED(64) static char placeholder[sizeof(InternalMmapVector<RootRegion>)]; + root_regions = new(placeholder) InternalMmapVector<RootRegion>(1); } -void InitCommonLsan() { - InitializeFlags(); +void InitCommonLsan(bool standalone) { + InitializeFlags(standalone); + InitializeRootRegions(); if (common_flags()->detect_leaks) { // Initialization which can fail or print warnings should only be done if // LSan is actually enabled. @@ -101,9 +128,9 @@ void InitCommonLsan() { } } -class Decorator: private __sanitizer::AnsiColorDecorator { +class Decorator: public __sanitizer::SanitizerCommonDecorator { public: - Decorator() : __sanitizer::AnsiColorDecorator(PrintsToTtyCached()) { } + Decorator() : SanitizerCommonDecorator() { } const char *Error() { return Red(); } const char *Leak() { return Blue(); } const char *End() { return Default(); } @@ -132,8 +159,7 @@ void ScanRangeForPointers(uptr begin, uptr end, Frontier *frontier, const char *region_type, ChunkTag tag) { const uptr alignment = flags()->pointer_alignment(); - if (flags()->log_pointers) - Report("Scanning %s range %p-%p.\n", region_type, begin, end); + LOG_POINTERS("Scanning %s range %p-%p.\n", region_type, begin, end); uptr pp = begin; if (pp % alignment) pp = pp + alignment - pp % alignment; @@ -148,10 +174,19 @@ void ScanRangeForPointers(uptr begin, uptr end, // Reachable beats ignored beats leaked. if (m.tag() == kReachable) continue; if (m.tag() == kIgnored && tag != kReachable) continue; + + // Do this check relatively late so we can log only the interesting cases. + if (!flags()->use_poisoned && WordIsPoisoned(pp)) { + LOG_POINTERS( + "%p is poisoned: ignoring %p pointing into chunk %p-%p of size " + "%zu.\n", + pp, p, chunk, chunk + m.requested_size(), m.requested_size()); + continue; + } + m.set_tag(tag); - if (flags()->log_pointers) - Report("%p: found %p pointing into chunk %p-%p of size %zu.\n", pp, p, - chunk, chunk + m.requested_size(), m.requested_size()); + LOG_POINTERS("%p: found %p pointing into chunk %p-%p of size %zu.\n", pp, p, + chunk, chunk + m.requested_size(), m.requested_size()); if (frontier) frontier->push_back(chunk); } @@ -170,7 +205,7 @@ static void ProcessThreads(SuspendedThreadsList const &suspended_threads, uptr registers_end = registers_begin + registers.size(); for (uptr i = 0; i < suspended_threads.thread_count(); i++) { uptr os_id = static_cast<uptr>(suspended_threads.GetThreadID(i)); - if (flags()->log_threads) Report("Processing thread %d.\n", os_id); + LOG_THREADS("Processing thread %d.\n", os_id); uptr stack_begin, stack_end, tls_begin, tls_end, cache_begin, cache_end; bool thread_found = GetThreadRangesLocked(os_id, &stack_begin, &stack_end, &tls_begin, &tls_end, @@ -178,8 +213,7 @@ static void ProcessThreads(SuspendedThreadsList const &suspended_threads, if (!thread_found) { // If a thread can't be found in the thread registry, it's probably in the // process of destruction. Log this event and move on. - if (flags()->log_threads) - Report("Thread %d not found in registry.\n", os_id); + LOG_THREADS("Thread %d not found in registry.\n", os_id); continue; } uptr sp; @@ -196,14 +230,12 @@ static void ProcessThreads(SuspendedThreadsList const &suspended_threads, "REGISTERS", kReachable); if (flags()->use_stacks) { - if (flags()->log_threads) - Report("Stack at %p-%p, SP = %p.\n", stack_begin, stack_end, sp); + LOG_THREADS("Stack at %p-%p (SP = %p).\n", stack_begin, stack_end, sp); if (sp < stack_begin || sp >= stack_end) { // SP is outside the recorded stack range (e.g. the thread is running a // signal handler on alternate stack). Again, consider the entire stack // range to be reachable. - if (flags()->log_threads) - Report("WARNING: stack pointer not in stack range.\n"); + LOG_THREADS("WARNING: stack pointer not in stack range.\n"); } else { // Shrink the stack range to ignore out-of-scope values. stack_begin = sp; @@ -214,7 +246,7 @@ static void ProcessThreads(SuspendedThreadsList const &suspended_threads, } if (flags()->use_tls) { - if (flags()->log_threads) Report("TLS at %p-%p.\n", tls_begin, tls_end); + LOG_THREADS("TLS at %p-%p.\n", tls_begin, tls_end); if (cache_begin == cache_end) { ScanRangeForPointers(tls_begin, tls_end, frontier, "TLS", kReachable); } else { @@ -232,6 +264,37 @@ static void ProcessThreads(SuspendedThreadsList const &suspended_threads, } } +static void ProcessRootRegion(Frontier *frontier, uptr root_begin, + uptr root_end) { + MemoryMappingLayout proc_maps(/*cache_enabled*/true); + uptr begin, end, prot; + while (proc_maps.Next(&begin, &end, + /*offset*/ 0, /*filename*/ 0, /*filename_size*/ 0, + &prot)) { + uptr intersection_begin = Max(root_begin, begin); + uptr intersection_end = Min(end, root_end); + if (intersection_begin >= intersection_end) continue; + bool is_readable = prot & MemoryMappingLayout::kProtectionRead; + LOG_POINTERS("Root region %p-%p intersects with mapped region %p-%p (%s)\n", + root_begin, root_end, begin, end, + is_readable ? "readable" : "unreadable"); + if (is_readable) + ScanRangeForPointers(intersection_begin, intersection_end, frontier, + "ROOT", kReachable); + } +} + +// Scans root regions for heap pointers. +static void ProcessRootRegions(Frontier *frontier) { + if (!flags()->use_root_regions) return; + CHECK(root_regions); + for (uptr i = 0; i < root_regions->size(); i++) { + RootRegion region = (*root_regions)[i]; + uptr begin_addr = reinterpret_cast<uptr>(region.begin); + ProcessRootRegion(frontier, begin_addr, begin_addr + region.size); + } +} + static void FloodFillTag(Frontier *frontier, ChunkTag tag) { while (frontier->size()) { uptr next_chunk = frontier->back(); @@ -266,41 +329,37 @@ static void CollectIgnoredCb(uptr chunk, void *arg) { // Sets the appropriate tag on each chunk. static void ClassifyAllChunks(SuspendedThreadsList const &suspended_threads) { // Holds the flood fill frontier. - Frontier frontier(GetPageSizeCached()); + Frontier frontier(1); - if (flags()->use_globals) - ProcessGlobalRegions(&frontier); + ProcessGlobalRegions(&frontier); ProcessThreads(suspended_threads, &frontier); + ProcessRootRegions(&frontier); FloodFillTag(&frontier, kReachable); // The check here is relatively expensive, so we do this in a separate flood // fill. That way we can skip the check for chunks that are reachable // otherwise. - if (flags()->log_pointers) - Report("Processing platform-specific allocations.\n"); + LOG_POINTERS("Processing platform-specific allocations.\n"); ProcessPlatformSpecificAllocations(&frontier); FloodFillTag(&frontier, kReachable); - if (flags()->log_pointers) - Report("Scanning ignored chunks.\n"); + LOG_POINTERS("Scanning ignored chunks.\n"); CHECK_EQ(0, frontier.size()); ForEachChunk(CollectIgnoredCb, &frontier); FloodFillTag(&frontier, kIgnored); // Iterate over leaked chunks and mark those that are reachable from other // leaked chunks. - if (flags()->log_pointers) - Report("Scanning leaked chunks.\n"); + LOG_POINTERS("Scanning leaked chunks.\n"); ForEachChunk(MarkIndirectlyLeakedCb, 0 /* arg */); } static void PrintStackTraceById(u32 stack_trace_id) { CHECK(stack_trace_id); - uptr size = 0; - const uptr *trace = StackDepotGet(stack_trace_id, &size); - StackTrace::PrintStack(trace, size); + StackDepotGet(stack_trace_id).Print(); } -// ForEachChunk callback. Aggregates unreachable chunks into a LeakReport. +// ForEachChunk callback. Aggregates information about unreachable chunks into +// a LeakReport. static void CollectLeaksCb(uptr chunk, void *arg) { CHECK(arg); LeakReport *leak_report = reinterpret_cast<LeakReport *>(arg); @@ -309,32 +368,22 @@ static void CollectLeaksCb(uptr chunk, void *arg) { if (!m.allocated()) return; if (m.tag() == kDirectlyLeaked || m.tag() == kIndirectlyLeaked) { uptr resolution = flags()->resolution; + u32 stack_trace_id = 0; if (resolution > 0) { - uptr size = 0; - const uptr *trace = StackDepotGet(m.stack_trace_id(), &size); - size = Min(size, resolution); - leak_report->Add(StackDepotPut(trace, size), m.requested_size(), m.tag()); + StackTrace stack = StackDepotGet(m.stack_trace_id()); + stack.size = Min(stack.size, resolution); + stack_trace_id = StackDepotPut(stack); } else { - leak_report->Add(m.stack_trace_id(), m.requested_size(), m.tag()); + stack_trace_id = m.stack_trace_id(); } - } -} - -// ForEachChunkCallback. Prints addresses of unreachable chunks. -static void PrintLeakedCb(uptr chunk, void *arg) { - chunk = GetUserBegin(chunk); - LsanMetadata m(chunk); - if (!m.allocated()) return; - if (m.tag() == kDirectlyLeaked || m.tag() == kIndirectlyLeaked) { - Printf("%s leaked %zu byte object at %p.\n", - m.tag() == kDirectlyLeaked ? "Directly" : "Indirectly", - m.requested_size(), chunk); + leak_report->AddLeakedChunk(chunk, stack_trace_id, m.requested_size(), + m.tag()); } } static void PrintMatchedSuppressions() { InternalMmapVector<Suppression *> matched(1); - suppression_ctx->GetMatched(&matched); + SuppressionContext::Get()->GetMatched(&matched); if (!matched.size()) return; const char *line = "-----------------------------------------------------"; @@ -347,12 +396,6 @@ static void PrintMatchedSuppressions() { Printf("%s\n\n", line); } -static void PrintLeaked() { - Printf("\n"); - Printf("Reporting individual objects:\n"); - ForEachChunk(PrintLeakedCb, 0 /* arg */); -} - struct DoLeakCheckParam { bool success; LeakReport leak_report; @@ -363,11 +406,8 @@ static void DoLeakCheckCallback(const SuspendedThreadsList &suspended_threads, DoLeakCheckParam *param = reinterpret_cast<DoLeakCheckParam *>(arg); CHECK(param); CHECK(!param->success); - CHECK(param->leak_report.IsEmpty()); ClassifyAllChunks(suspended_threads); ForEachChunk(CollectLeaksCb, ¶m->leak_report); - if (!param->leak_report.IsEmpty() && flags()->report_objects) - PrintLeaked(); param->success = true; } @@ -378,7 +418,7 @@ void DoLeakCheck() { if (already_done) return; already_done = true; if (&__lsan_is_turned_off && __lsan_is_turned_off()) - return; + return; DoLeakCheckParam param; param.success = false; @@ -392,8 +432,9 @@ void DoLeakCheck() { Report("LeakSanitizer has encountered a fatal error.\n"); Die(); } - uptr have_unsuppressed = param.leak_report.ApplySuppressions(); - if (have_unsuppressed) { + param.leak_report.ApplySuppressions(); + uptr unsuppressed_count = param.leak_report.UnsuppressedLeakCount(); + if (unsuppressed_count > 0) { Decorator d; Printf("\n" "=================================================================" @@ -401,38 +442,49 @@ void DoLeakCheck() { Printf("%s", d.Error()); Report("ERROR: LeakSanitizer: detected memory leaks\n"); Printf("%s", d.End()); - param.leak_report.PrintLargest(flags()->max_leaks); + param.leak_report.ReportTopLeaks(flags()->max_leaks); } - if (have_unsuppressed || (flags()->verbosity >= 1)) { + if (common_flags()->print_suppressions) PrintMatchedSuppressions(); + if (unsuppressed_count > 0) { param.leak_report.PrintSummary(); + if (flags()->exitcode) { + if (common_flags()->coverage) + __sanitizer_cov_dump(); + internal__exit(flags()->exitcode); + } } - if (have_unsuppressed && flags()->exitcode) - internal__exit(flags()->exitcode); } static Suppression *GetSuppressionForAddr(uptr addr) { - static const uptr kMaxAddrFrames = 16; - InternalScopedBuffer<AddressInfo> addr_frames(kMaxAddrFrames); - for (uptr i = 0; i < kMaxAddrFrames; i++) new (&addr_frames[i]) AddressInfo(); - uptr addr_frames_num = Symbolizer::Get()->SymbolizeCode( - addr, addr_frames.data(), kMaxAddrFrames); - for (uptr i = 0; i < addr_frames_num; i++) { - Suppression* s; - if (suppression_ctx->Match(addr_frames[i].function, SuppressionLeak, &s) || - suppression_ctx->Match(addr_frames[i].file, SuppressionLeak, &s) || - suppression_ctx->Match(addr_frames[i].module, SuppressionLeak, &s)) - return s; + Suppression *s = nullptr; + + // Suppress by module name. + const char *module_name; + uptr module_offset; + if (Symbolizer::GetOrInit()->GetModuleNameAndOffsetForPC(addr, &module_name, + &module_offset) && + SuppressionContext::Get()->Match(module_name, SuppressionLeak, &s)) + return s; + + // Suppress by file or function name. + SymbolizedStack *frames = Symbolizer::GetOrInit()->SymbolizePC(addr); + for (SymbolizedStack *cur = frames; cur; cur = cur->next) { + if (SuppressionContext::Get()->Match(cur->info.function, SuppressionLeak, + &s) || + SuppressionContext::Get()->Match(cur->info.file, SuppressionLeak, &s)) { + break; + } } - return 0; + frames->ClearAll(); + return s; } static Suppression *GetSuppressionForStack(u32 stack_trace_id) { - uptr size = 0; - const uptr *trace = StackDepotGet(stack_trace_id, &size); - for (uptr i = 0; i < size; i++) { - Suppression *s = - GetSuppressionForAddr(StackTrace::GetPreviousInstructionPc(trace[i])); + StackTrace stack = StackDepotGet(stack_trace_id); + for (uptr i = 0; i < stack.size; i++) { + Suppression *s = GetSuppressionForAddr( + StackTrace::GetPreviousInstructionPc(stack.trace[i])); if (s) return s; } return 0; @@ -441,26 +493,35 @@ static Suppression *GetSuppressionForStack(u32 stack_trace_id) { ///// LeakReport implementation. ///// // A hard limit on the number of distinct leaks, to avoid quadratic complexity -// in LeakReport::Add(). We don't expect to ever see this many leaks in -// real-world applications. +// in LeakReport::AddLeakedChunk(). We don't expect to ever see this many leaks +// in real-world applications. // FIXME: Get rid of this limit by changing the implementation of LeakReport to // use a hash table. const uptr kMaxLeaksConsidered = 5000; -void LeakReport::Add(u32 stack_trace_id, uptr leaked_size, ChunkTag tag) { +void LeakReport::AddLeakedChunk(uptr chunk, u32 stack_trace_id, + uptr leaked_size, ChunkTag tag) { CHECK(tag == kDirectlyLeaked || tag == kIndirectlyLeaked); bool is_directly_leaked = (tag == kDirectlyLeaked); - for (uptr i = 0; i < leaks_.size(); i++) + uptr i; + for (i = 0; i < leaks_.size(); i++) { if (leaks_[i].stack_trace_id == stack_trace_id && leaks_[i].is_directly_leaked == is_directly_leaked) { leaks_[i].hit_count++; leaks_[i].total_size += leaked_size; - return; + break; } - if (leaks_.size() == kMaxLeaksConsidered) return; - Leak leak = { /* hit_count */ 1, leaked_size, stack_trace_id, - is_directly_leaked, /* is_suppressed */ false }; - leaks_.push_back(leak); + } + if (i == leaks_.size()) { + if (leaks_.size() == kMaxLeaksConsidered) return; + Leak leak = { next_id_++, /* hit_count */ 1, leaked_size, stack_trace_id, + is_directly_leaked, /* is_suppressed */ false }; + leaks_.push_back(leak); + } + if (flags()->report_objects) { + LeakedObject obj = {leaks_[i].id, chunk, leaked_size}; + leaked_objects_.push_back(obj); + } } static bool LeakComparator(const Leak &leak1, const Leak &leak2) { @@ -470,7 +531,7 @@ static bool LeakComparator(const Leak &leak1, const Leak &leak2) { return leak1.is_directly_leaked; } -void LeakReport::PrintLargest(uptr num_leaks_to_print) { +void LeakReport::ReportTopLeaks(uptr num_leaks_to_report) { CHECK(leaks_.size() <= kMaxLeaksConsidered); Printf("\n"); if (leaks_.size() == kMaxLeaksConsidered) @@ -478,31 +539,49 @@ void LeakReport::PrintLargest(uptr num_leaks_to_print) { "reported.\n", kMaxLeaksConsidered); - uptr unsuppressed_count = 0; - for (uptr i = 0; i < leaks_.size(); i++) - if (!leaks_[i].is_suppressed) unsuppressed_count++; - if (num_leaks_to_print > 0 && num_leaks_to_print < unsuppressed_count) - Printf("The %zu largest leak(s):\n", num_leaks_to_print); + uptr unsuppressed_count = UnsuppressedLeakCount(); + if (num_leaks_to_report > 0 && num_leaks_to_report < unsuppressed_count) + Printf("The %zu top leak(s):\n", num_leaks_to_report); InternalSort(&leaks_, leaks_.size(), LeakComparator); - uptr leaks_printed = 0; - Decorator d; + uptr leaks_reported = 0; for (uptr i = 0; i < leaks_.size(); i++) { if (leaks_[i].is_suppressed) continue; - Printf("%s", d.Leak()); - Printf("%s leak of %zu byte(s) in %zu object(s) allocated from:\n", - leaks_[i].is_directly_leaked ? "Direct" : "Indirect", - leaks_[i].total_size, leaks_[i].hit_count); - Printf("%s", d.End()); - PrintStackTraceById(leaks_[i].stack_trace_id); - leaks_printed++; - if (leaks_printed == num_leaks_to_print) break; + PrintReportForLeak(i); + leaks_reported++; + if (leaks_reported == num_leaks_to_report) break; } - if (leaks_printed < unsuppressed_count) { - uptr remaining = unsuppressed_count - leaks_printed; + if (leaks_reported < unsuppressed_count) { + uptr remaining = unsuppressed_count - leaks_reported; Printf("Omitting %zu more leak(s).\n", remaining); } } +void LeakReport::PrintReportForLeak(uptr index) { + Decorator d; + Printf("%s", d.Leak()); + Printf("%s leak of %zu byte(s) in %zu object(s) allocated from:\n", + leaks_[index].is_directly_leaked ? "Direct" : "Indirect", + leaks_[index].total_size, leaks_[index].hit_count); + Printf("%s", d.End()); + + PrintStackTraceById(leaks_[index].stack_trace_id); + + if (flags()->report_objects) { + Printf("Objects leaked above:\n"); + PrintLeakedObjectsForLeak(index); + Printf("\n"); + } +} + +void LeakReport::PrintLeakedObjectsForLeak(uptr index) { + u32 leak_id = leaks_[index].id; + for (uptr j = 0; j < leaked_objects_.size(); j++) { + if (leaked_objects_[j].leak_id == leak_id) + Printf("%p (%zu bytes)\n", leaked_objects_[j].addr, + leaked_objects_[j].size); + } +} + void LeakReport::PrintSummary() { CHECK(leaks_.size() <= kMaxLeaksConsidered); uptr bytes = 0, allocations = 0; @@ -511,27 +590,30 @@ void LeakReport::PrintSummary() { bytes += leaks_[i].total_size; allocations += leaks_[i].hit_count; } - InternalScopedBuffer<char> summary(kMaxSummaryLength); - internal_snprintf(summary.data(), summary.size(), - "%zu byte(s) leaked in %zu allocation(s).", bytes, - allocations); + InternalScopedString summary(kMaxSummaryLength); + summary.append("%zu byte(s) leaked in %zu allocation(s).", bytes, + allocations); ReportErrorSummary(summary.data()); } -uptr LeakReport::ApplySuppressions() { - uptr unsuppressed_count = 0; +void LeakReport::ApplySuppressions() { for (uptr i = 0; i < leaks_.size(); i++) { Suppression *s = GetSuppressionForStack(leaks_[i].stack_trace_id); if (s) { s->weight += leaks_[i].total_size; s->hit_count += leaks_[i].hit_count; leaks_[i].is_suppressed = true; - } else { - unsuppressed_count++; } } - return unsuppressed_count; } + +uptr LeakReport::UnsuppressedLeakCount() { + uptr result = 0; + for (uptr i = 0; i < leaks_.size(); i++) + if (!leaks_[i].is_suppressed) result++; + return result; +} + } // namespace __lsan #endif // CAN_SANITIZE_LEAKS @@ -547,13 +629,51 @@ void __lsan_ignore_object(const void *p) { // locked. BlockingMutexLock l(&global_mutex); IgnoreObjectResult res = IgnoreObjectLocked(p); - if (res == kIgnoreObjectInvalid && flags()->verbosity >= 2) - Report("__lsan_ignore_object(): no heap object found at %p", p); - if (res == kIgnoreObjectAlreadyIgnored && flags()->verbosity >= 2) - Report("__lsan_ignore_object(): " + if (res == kIgnoreObjectInvalid) + VReport(1, "__lsan_ignore_object(): no heap object found at %p", p); + if (res == kIgnoreObjectAlreadyIgnored) + VReport(1, "__lsan_ignore_object(): " "heap object at %p is already being ignored\n", p); - if (res == kIgnoreObjectSuccess && flags()->verbosity >= 3) - Report("__lsan_ignore_object(): ignoring heap object at %p\n", p); + if (res == kIgnoreObjectSuccess) + VReport(1, "__lsan_ignore_object(): ignoring heap object at %p\n", p); +#endif // CAN_SANITIZE_LEAKS +} + +SANITIZER_INTERFACE_ATTRIBUTE +void __lsan_register_root_region(const void *begin, uptr size) { +#if CAN_SANITIZE_LEAKS + BlockingMutexLock l(&global_mutex); + CHECK(root_regions); + RootRegion region = {begin, size}; + root_regions->push_back(region); + VReport(1, "Registered root region at %p of size %llu\n", begin, size); +#endif // CAN_SANITIZE_LEAKS +} + +SANITIZER_INTERFACE_ATTRIBUTE +void __lsan_unregister_root_region(const void *begin, uptr size) { +#if CAN_SANITIZE_LEAKS + BlockingMutexLock l(&global_mutex); + CHECK(root_regions); + bool removed = false; + for (uptr i = 0; i < root_regions->size(); i++) { + RootRegion region = (*root_regions)[i]; + if (region.begin == begin && region.size == size) { + removed = true; + uptr last_index = root_regions->size() - 1; + (*root_regions)[i] = (*root_regions)[last_index]; + root_regions->pop_back(); + VReport(1, "Unregistered root region at %p of size %llu\n", begin, size); + break; + } + } + if (!removed) { + Report( + "__lsan_unregister_root_region(): region at %p of size %llu has not " + "been registered.\n", + begin, size); + Die(); + } #endif // CAN_SANITIZE_LEAKS } diff --git a/lib/lsan/lsan_common.h b/lib/lsan/lsan_common.h index d490f8bafd9e..86ff12da6e0f 100644 --- a/lib/lsan/lsan_common.h +++ b/lib/lsan/lsan_common.h @@ -21,7 +21,7 @@ #include "sanitizer_common/sanitizer_platform.h" #include "sanitizer_common/sanitizer_symbolizer.h" -#if SANITIZER_LINUX && defined(__x86_64__) +#if SANITIZER_LINUX && defined(__x86_64__) && (SANITIZER_WORDSIZE == 64) #define CAN_SANITIZE_LEAKS 1 #else #define CAN_SANITIZE_LEAKS 0 @@ -51,8 +51,6 @@ struct Flags { int max_leaks; // If nonzero kill the process with this exit code upon finding leaks. int exitcode; - // Suppressions file name. - const char* suppressions; // Flags controlling the root set of reachable memory. // Global variables (.data and .bss). @@ -63,12 +61,13 @@ struct Flags { bool use_registers; // TLS and thread-specific storage. bool use_tls; + // Regions added via __lsan_register_root_region(). + bool use_root_regions; // Consider unaligned pointers valid. bool use_unaligned; - - // User-visible verbosity. - int verbosity; + // Consider pointers found in poisoned memory to be valid. + bool use_poisoned; // Debug logging. bool log_pointers; @@ -79,6 +78,7 @@ extern Flags lsan_flags; inline Flags *flags() { return &lsan_flags; } struct Leak { + u32 id; uptr hit_count; uptr total_size; u32 stack_trace_id; @@ -86,17 +86,31 @@ struct Leak { bool is_suppressed; }; +struct LeakedObject { + u32 leak_id; + uptr addr; + uptr size; +}; + // Aggregates leaks by stack trace prefix. class LeakReport { public: - LeakReport() : leaks_(1) {} - void Add(u32 stack_trace_id, uptr leaked_size, ChunkTag tag); - void PrintLargest(uptr max_leaks); + LeakReport() : next_id_(0), leaks_(1), leaked_objects_(1) {} + void AddLeakedChunk(uptr chunk, u32 stack_trace_id, uptr leaked_size, + ChunkTag tag); + void ReportTopLeaks(uptr max_leaks); void PrintSummary(); - bool IsEmpty() { return leaks_.size() == 0; } - uptr ApplySuppressions(); + void ApplySuppressions(); + uptr UnsuppressedLeakCount(); + + private: + void PrintReportForLeak(uptr index); + void PrintLeakedObjectsForLeak(uptr index); + + u32 next_id_; InternalMmapVector<Leak> leaks_; + InternalMmapVector<LeakedObject> leaked_objects_; }; typedef InternalMmapVector<uptr> Frontier; @@ -117,10 +131,19 @@ enum IgnoreObjectResult { }; // Functions called from the parent tool. -void InitCommonLsan(); +void InitCommonLsan(bool standalone); void DoLeakCheck(); bool DisabledInThisThread(); +// Special case for "new T[0]" where T is a type with DTOR. +// new T[0] will allocate one word for the array size (0) and store a pointer +// to the end of allocated chunk. +inline bool IsSpecialCaseOfOperatorNew0(uptr chunk_beg, uptr chunk_size, + uptr addr) { + return chunk_size == sizeof(uptr) && chunk_beg + chunk_size == addr && + *reinterpret_cast<uptr *>(chunk_beg) == 0; +} + // The following must be implemented in the parent tool. void ForEachChunk(ForEachChunkCallback callback, void *arg); @@ -129,6 +152,8 @@ void GetAllocatorGlobalRange(uptr *begin, uptr *end); // Wrappers for allocator's ForceLock()/ForceUnlock(). void LockAllocator(); void UnlockAllocator(); +// Returns true if [addr, addr + sizeof(void *)) is poisoned. +bool WordIsPoisoned(uptr addr); // Wrappers for ThreadRegistry access. void LockThreadRegistry(); void UnlockThreadRegistry(); diff --git a/lib/lsan/lsan_common_linux.cc b/lib/lsan/lsan_common_linux.cc index ef8857fe8dbb..ba51868c76e8 100644 --- a/lib/lsan/lsan_common_linux.cc +++ b/lib/lsan/lsan_common_linux.cc @@ -19,6 +19,7 @@ #include <link.h> #include "sanitizer_common/sanitizer_common.h" +#include "sanitizer_common/sanitizer_flags.h" #include "sanitizer_common/sanitizer_linux.h" #include "sanitizer_common/sanitizer_stackdepot.h" @@ -43,11 +44,11 @@ void InitializePlatformSpecificModules() { return; } if (num_matches == 0) - Report("LeakSanitizer: Dynamic linker not found. " - "TLS will not be handled correctly.\n"); + VReport(1, "LeakSanitizer: Dynamic linker not found. " + "TLS will not be handled correctly.\n"); else if (num_matches > 1) - Report("LeakSanitizer: Multiple modules match \"%s\". " - "TLS will not be handled correctly.\n", kLinkerName); + VReport(1, "LeakSanitizer: Multiple modules match \"%s\". " + "TLS will not be handled correctly.\n", kLinkerName); linker = 0; } @@ -83,6 +84,7 @@ static int ProcessGlobalRegionsCallback(struct dl_phdr_info *info, size_t size, // Scans global variables for heap pointers. void ProcessGlobalRegions(Frontier *frontier) { + if (!flags()->use_globals) return; // FIXME: dl_iterate_phdr acquires a linker lock, so we run a risk of // deadlocking by running this under StopTheWorld. However, the lock is // reentrant, so we should be able to fix this by acquiring the lock before @@ -92,11 +94,10 @@ void ProcessGlobalRegions(Frontier *frontier) { static uptr GetCallerPC(u32 stack_id, StackDepotReverseMap *map) { CHECK(stack_id); - uptr size = 0; - const uptr *trace = map->Get(stack_id, &size); + StackTrace stack = map->Get(stack_id); // The top frame is our malloc/calloc/etc. The next frame is the caller. - if (size >= 2) - return trace[1]; + if (stack.size >= 2) + return stack.trace[1]; return 0; } @@ -129,6 +130,21 @@ static void ProcessPlatformSpecificAllocationsCb(uptr chunk, void *arg) { // Handles dynamically allocated TLS blocks by treating all chunks allocated // from ld-linux.so as reachable. +// Dynamic TLS blocks contain the TLS variables of dynamically loaded modules. +// They are allocated with a __libc_memalign() call in allocate_and_init() +// (elf/dl-tls.c). Glibc won't tell us the address ranges occupied by those +// blocks, but we can make sure they come from our own allocator by intercepting +// __libc_memalign(). On top of that, there is no easy way to reach them. Their +// addresses are stored in a dynamically allocated array (the DTV) which is +// referenced from the static TLS. Unfortunately, we can't just rely on the DTV +// being reachable from the static TLS, and the dynamic TLS being reachable from +// the DTV. This is because the initial DTV is allocated before our interception +// mechanism kicks in, and thus we don't recognize it as allocated memory. We +// can't special-case it either, since we don't know its size. +// Our solution is to include in the root set all allocations made from +// ld-linux.so (which is where allocate_and_init() is implemented). This is +// guaranteed to include all dynamic TLS blocks (and possibly other allocations +// which we don't care about). void ProcessPlatformSpecificAllocations(Frontier *frontier) { if (!flags()->use_tls) return; if (!linker) return; diff --git a/lib/lsan/lsan_interceptors.cc b/lib/lsan/lsan_interceptors.cc index 400230b885ff..ba2519dcb25d 100644 --- a/lib/lsan/lsan_interceptors.cc +++ b/lib/lsan/lsan_interceptors.cc @@ -34,21 +34,6 @@ int pthread_key_create(unsigned *key, void (*destructor)(void* v)); int pthread_setspecific(unsigned key, const void *v); } -#define GET_STACK_TRACE \ - StackTrace stack; \ - { \ - uptr stack_top = 0, stack_bottom = 0; \ - ThreadContext *t; \ - bool fast = common_flags()->fast_unwind_on_malloc; \ - if (fast && (t = CurrentThreadContext())) { \ - stack_top = t->stack_end(); \ - stack_bottom = t->stack_begin(); \ - } \ - stack.Unwind(__sanitizer::common_flags()->malloc_context_size, \ - StackTrace::GetCurrentPc(), \ - GET_CURRENT_FRAME(), stack_top, stack_bottom, fast); \ - } - #define ENSURE_LSAN_INITED do { \ CHECK(!lsan_init_is_running); \ if (!lsan_inited) \ @@ -65,7 +50,7 @@ namespace std { INTERCEPTOR(void*, malloc, uptr size) { ENSURE_LSAN_INITED; - GET_STACK_TRACE; + GET_STACK_TRACE_MALLOC; return Allocate(stack, size, 1, kAlwaysClearMemory); } @@ -88,26 +73,32 @@ INTERCEPTOR(void*, calloc, uptr nmemb, uptr size) { } if (CallocShouldReturnNullDueToOverflow(size, nmemb)) return 0; ENSURE_LSAN_INITED; - GET_STACK_TRACE; + GET_STACK_TRACE_MALLOC; size *= nmemb; return Allocate(stack, size, 1, true); } INTERCEPTOR(void*, realloc, void *q, uptr size) { ENSURE_LSAN_INITED; - GET_STACK_TRACE; + GET_STACK_TRACE_MALLOC; return Reallocate(stack, q, size, 1); } INTERCEPTOR(void*, memalign, uptr alignment, uptr size) { ENSURE_LSAN_INITED; - GET_STACK_TRACE; + GET_STACK_TRACE_MALLOC; + return Allocate(stack, size, alignment, kAlwaysClearMemory); +} + +INTERCEPTOR(void*, aligned_alloc, uptr alignment, uptr size) { + ENSURE_LSAN_INITED; + GET_STACK_TRACE_MALLOC; return Allocate(stack, size, alignment, kAlwaysClearMemory); } INTERCEPTOR(int, posix_memalign, void **memptr, uptr alignment, uptr size) { ENSURE_LSAN_INITED; - GET_STACK_TRACE; + GET_STACK_TRACE_MALLOC; *memptr = Allocate(stack, size, alignment, kAlwaysClearMemory); // FIXME: Return ENOMEM if user requested more than max alloc size. return 0; @@ -115,7 +106,7 @@ INTERCEPTOR(int, posix_memalign, void **memptr, uptr alignment, uptr size) { INTERCEPTOR(void*, valloc, uptr size) { ENSURE_LSAN_INITED; - GET_STACK_TRACE; + GET_STACK_TRACE_MALLOC; if (size == 0) size = GetPageSizeCached(); return Allocate(stack, size, GetPageSizeCached(), kAlwaysClearMemory); @@ -142,7 +133,7 @@ INTERCEPTOR(int, mallopt, int cmd, int value) { INTERCEPTOR(void*, pvalloc, uptr size) { ENSURE_LSAN_INITED; - GET_STACK_TRACE; + GET_STACK_TRACE_MALLOC; uptr PageSize = GetPageSizeCached(); size = RoundUpTo(size, PageSize); if (size == 0) { @@ -152,11 +143,11 @@ INTERCEPTOR(void*, pvalloc, uptr size) { return Allocate(stack, size, GetPageSizeCached(), kAlwaysClearMemory); } -INTERCEPTOR(void, cfree, void *p) ALIAS("free"); +INTERCEPTOR(void, cfree, void *p) ALIAS(WRAPPER_NAME(free)); #define OPERATOR_NEW_BODY \ ENSURE_LSAN_INITED; \ - GET_STACK_TRACE; \ + GET_STACK_TRACE_MALLOC; \ return Allocate(stack, size, 1, kAlwaysClearMemory); INTERCEPTOR_ATTRIBUTE @@ -173,9 +164,9 @@ void *operator new[](uptr size, std::nothrow_t const&) { OPERATOR_NEW_BODY; } Deallocate(ptr); INTERCEPTOR_ATTRIBUTE -void operator delete(void *ptr) { OPERATOR_DELETE_BODY; } +void operator delete(void *ptr) throw() { OPERATOR_DELETE_BODY; } INTERCEPTOR_ATTRIBUTE -void operator delete[](void *ptr) { OPERATOR_DELETE_BODY; } +void operator delete[](void *ptr) throw() { OPERATOR_DELETE_BODY; } INTERCEPTOR_ATTRIBUTE void operator delete(void *ptr, std::nothrow_t const&) { OPERATOR_DELETE_BODY; } INTERCEPTOR_ATTRIBUTE @@ -185,7 +176,8 @@ void operator delete[](void *ptr, std::nothrow_t const &) { // We need this to intercept the __libc_memalign calls that are used to // allocate dynamic TLS space in ld-linux.so. -INTERCEPTOR(void *, __libc_memalign, uptr align, uptr s) ALIAS("memalign"); +INTERCEPTOR(void *, __libc_memalign, uptr align, uptr s) + ALIAS(WRAPPER_NAME(memalign)); ///// Thread initialization and finalization. ///// @@ -223,9 +215,9 @@ extern "C" void *__lsan_thread_start_func(void *arg) { int tid = 0; while ((tid = atomic_load(&p->tid, memory_order_acquire)) == 0) internal_sched_yield(); - atomic_store(&p->tid, 0, memory_order_release); SetCurrentThread(tid); ThreadStart(tid, GetTid()); + atomic_store(&p->tid, 0, memory_order_release); return callback(param); } @@ -238,7 +230,7 @@ INTERCEPTOR(int, pthread_create, void *th, void *attr, pthread_attr_init(&myattr); attr = &myattr; } - AdjustStackSizeLinux(attr); + AdjustStackSize(attr); int detached = 0; pthread_attr_getdetachstate(attr, &detached); ThreadParam p; diff --git a/lib/lsan/lsan_preinit.cc b/lib/lsan/lsan_preinit.cc index e6639516dc81..5a190959c15d 100644 --- a/lib/lsan/lsan_preinit.cc +++ b/lib/lsan/lsan_preinit.cc @@ -14,11 +14,7 @@ #include "lsan.h" -#ifndef LSAN_USE_PREINIT_ARRAY -#define LSAN_USE_PREINIT_ARRAY 1 -#endif - -#if LSAN_USE_PREINIT_ARRAY && !defined(PIC) +#if SANITIZER_CAN_USE_PREINIT_ARRAY // We force __lsan_init to be called before anyone else by placing it into // .preinit_array section. __attribute__((section(".preinit_array"), used)) diff --git a/lib/lsan/tests/CMakeLists.txt b/lib/lsan/tests/CMakeLists.txt deleted file mode 100644 index 2221e0650237..000000000000 --- a/lib/lsan/tests/CMakeLists.txt +++ /dev/null @@ -1,58 +0,0 @@ -include(CheckCXXCompilerFlag) -include(CompilerRTCompile) -include(CompilerRTLink) - -include_directories(..) -include_directories(../..) - -set(LSAN_TESTS_SRC - lsan_dummy_unittest.cc) - -set(LSAN_TESTS_CFLAGS - ${SANITIZER_COMMON_CFLAGS} - ${COMPILER_RT_GTEST_INCLUDE_CFLAGS} - -I${COMPILER_RT_SOURCE_DIR}/lib - -I${LSAN_SRC_DIR}) - -set(LSAN_TEST_LINK_FLAGS_COMMON - -lstdc++ -ldl -lpthread -lm) - -add_custom_target(LsanUnitTests) -set_target_properties(LsanUnitTests PROPERTIES - FOLDER "LSan unit tests") - -# Compile source for the given architecture, using compiler -# options in ${ARGN}, and add it to the object list. -macro(lsan_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 ${LSAN_RUNTIME_LIBRARIES}) - list(APPEND ${obj_list} ${output_obj}) -endmacro() - -function(add_lsan_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 ${LSAN_RUNTIME_LIBRARIES} ${ARGN} - LINK_FLAGS ${LSAN_TEST_LINK_FLAGS_COMMON} - ${TARGET_LINK_FLAGS}) -endfunction() - -macro(add_lsan_tests_for_arch arch) - set(LSAN_TESTS_OBJ) - set(LSAN_TEST_SOURCES ${LSAN_TESTS_SRC} - ${COMPILER_RT_GTEST_SOURCE}) - foreach(source ${LSAN_TEST_SOURCES}) - lsan_compile(LSAN_TESTS_OBJ ${source} ${arch} ${LSAN_TESTS_CFLAGS}) - endforeach() - add_lsan_test(LsanUnitTests Lsan-${arch}-Test ${arch} ${LSAN_TESTS_OBJ}) -endmacro() - -# Build tests for 64-bit Linux only. -if(UNIX AND NOT APPLE AND CAN_TARGET_x86_64) - add_lsan_tests_for_arch(x86_64) -endif() diff --git a/lib/lsan/tests/lsan_dummy_unittest.cc b/lib/lsan/tests/lsan_dummy_unittest.cc deleted file mode 100644 index 5468400775a0..000000000000 --- a/lib/lsan/tests/lsan_dummy_unittest.cc +++ /dev/null @@ -1,22 +0,0 @@ -//===-- lsan_dummy_unittest.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 LeakSanitizer runtime. -// -//===----------------------------------------------------------------------===// -#include "gtest/gtest.h" - -TEST(LeakSanitizer, EmptyTest) { - // Empty test to suppress LIT warnings about lack of tests. -} - -int main(int argc, char **argv) { - ::testing::InitGoogleTest(&argc, argv); - return RUN_ALL_TESTS(); -} diff --git a/lib/lsan/tests/lsan_testlib.cc b/lib/lsan/tests/lsan_testlib.cc deleted file mode 100644 index 8db6cf1e2f8c..000000000000 --- a/lib/lsan/tests/lsan_testlib.cc +++ /dev/null @@ -1,25 +0,0 @@ -//===-- lsan_testlib.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 LeakSanitizer. -// Standalone LSan tool as a shared library, to be used with LD_PRELOAD. -// -//===----------------------------------------------------------------------===// -/* Usage: -clang++ ../sanitizer_common/sanitizer_*.cc ../interception/interception_*.cc \ - lsan*.cc tests/lsan_testlib.cc -I. -I.. -g -ldl -lpthread -fPIC -shared -O2 \ - -DLSAN_USE_PREINIT_ARRAY=0 -o lsan.so -LD_PRELOAD=./lsan.so /your/app -*/ -#include "lsan.h" - -__attribute__((constructor)) -void constructor() { - __lsan_init(); -} |