[compiler-rt] r182250 - [lsan] Tests for LeakSanitizer.
Sergey Matveev
earthdok at google.com
Mon May 20 04:09:27 PDT 2013
Author: smatveev
Date: Mon May 20 06:09:27 2013
New Revision: 182250
URL: http://llvm.org/viewvc/llvm-project?rev=182250&view=rev
Log:
[lsan] Tests for LeakSanitizer.
Added:
compiler-rt/trunk/lib/lsan/tests/
compiler-rt/trunk/lib/lsan/tests/lsan_test.cc
compiler-rt/trunk/lib/lsan/tests/lsan_tls_loadable.cc
Modified:
compiler-rt/trunk/lib/sanitizer_common/scripts/check_lint.sh
Added: compiler-rt/trunk/lib/lsan/tests/lsan_test.cc
URL: http://llvm.org/viewvc/llvm-project/compiler-rt/trunk/lib/lsan/tests/lsan_test.cc?rev=182250&view=auto
==============================================================================
--- compiler-rt/trunk/lib/lsan/tests/lsan_test.cc (added)
+++ compiler-rt/trunk/lib/lsan/tests/lsan_test.cc Mon May 20 06:09:27 2013
@@ -0,0 +1,299 @@
+//=-- lsan_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 LeakSanitizer.
+// Tests for leak checking functionality.
+//
+//===----------------------------------------------------------------------===//
+
+#include "sanitizer_common/sanitizer_platform.h"
+#if SANITIZER_LINUX && defined(__x86_64__)
+
+#include <dlfcn.h>
+#include <pthread.h>
+#include <string>
+
+#include "gtest/gtest.h"
+#include "sanitizer_common/sanitizer_atomic.h"
+#include "sanitizer_common/sanitizer_common.h"
+#include "sanitizer_common/tests/sanitizer_test_utils.h"
+
+#include "lsan.h"
+#include "lsan_common.h"
+
+static char **global_argv;
+
+namespace {
+uptr kMagic = 0xBABABABABABABABA;
+#define HIDE(p) ((void *)((uptr)(p) ^ kMagic))
+#define PEEK(p) HIDE(p)
+
+uptr kSmallAllocSize = 10;
+// maxsize in primary allocator is always less than this.
+uptr kLargeAllocSize = 1 << 25;
+
+::testing::AssertionResult IsLeaked(void *hidden_p, uptr sources) {
+ InternalVector<void *> leaked(1);
+ __lsan::ReportLeaked(&leaked, sources);
+ for (uptr i = 0; i < leaked.size(); i++)
+ if (leaked[i] == PEEK(hidden_p))
+ return ::testing::AssertionSuccess() << PEEK(hidden_p) << " is leaked";
+ return ::testing::AssertionFailure() << PEEK(hidden_p) << " is not leaked";
+}
+
+#define EXPECT_LEAKED(p, sources) EXPECT_TRUE(IsLeaked(HIDE((p)), sources))
+#define EXPECT_NOT_LEAKED(p, sources) EXPECT_FALSE(IsLeaked(HIDE((p)), sources))
+
+// Tests for various sources of pointers.
+// Stacks and registers are tricky: pointers sometimes get stuck in them and
+// cause false negatives. This is not considered a bug in LSan, but we don't
+// want it to interfere with our tests, so we disable those sources whenever we
+// can. We don't disable globals though: if a stale pointer somehow makes it
+// into global state and causes a false negative, we want to know.
+
+uptr kAllButStackAndRegisters =
+ __lsan::kSourceAllAligned & ~__lsan::kSourceStacks &
+ ~__lsan::kSourceRegisters;
+
+void TestSource(void **p, uptr source) {
+ uptr baseline = kAllButStackAndRegisters | source;
+ *p = malloc(kSmallAllocSize);
+ EXPECT_NOT_LEAKED(*p, baseline);
+ EXPECT_LEAKED(*p, baseline & ~source);
+ // Check again, in case the first EXPECT_NOT_LEAKED was a false negative.
+ EXPECT_NOT_LEAKED(*p, baseline);
+ free((void *)*p);
+ *p = NULL;
+}
+
+void *data_var = (void*) 1;
+
+TEST(LeakSanitizer, InitializedGlobals) {
+ TestSource(&data_var, __lsan::kSourceGlobals);
+}
+
+void *bss_var;
+
+TEST(LeakSanitizer, UninitializedGlobals) {
+ TestSource(&bss_var, __lsan::kSourceGlobals);
+}
+
+TEST(LeakSanitizer, Stack) {
+ void *local_var;
+ TestSource(&local_var, __lsan::kSourceStacks);
+}
+
+THREADLOCAL void *tl_var;
+
+TEST(LeakSanitizer, StaticTLS) {
+ TestSource(&tl_var, __lsan::kSourceTLS);
+}
+
+// Dynamically allocated TLS space.
+TEST(LeakSanitizer, DynamicTLS) {
+ // Compute the path to our loadable DSO. We assume it's in the same
+ // directory.
+ char **argv = global_argv;
+ const std::string kLoadableSO = "liblsan_tls_loadable-x86_64.so";
+ std::string path = argv[0];
+ size_t last_slash = path.rfind('/');
+ ASSERT_NE(last_slash, std::string::npos);
+ path.erase(last_slash + 1);
+ path.append(kLoadableSO);
+ void *handle = dlopen(path.c_str(), RTLD_LAZY);
+ ASSERT_TRUE(handle != NULL) << "dlerror " << dlerror();
+ typedef void **(* store_t)(void *p);
+ store_t StoreToTLS = (store_t)dlsym(handle, "StoreToTLS");
+ ASSERT_EQ(0, dlerror());
+
+ void *p = malloc(kSmallAllocSize);
+ void **p_in_tls = StoreToTLS(p);
+ EXPECT_NOT_LEAKED(p, kAllButStackAndRegisters);
+ EXPECT_LEAKED(p, kAllButStackAndRegisters & ~__lsan::kSourceTLS);
+ EXPECT_NOT_LEAKED(p, kAllButStackAndRegisters);
+ free(p);
+ *p_in_tls = NULL;
+}
+
+// From glibc: this many keys are stored in the thread descriptor directly.
+const uptr PTHREAD_KEY_2NDLEVEL_SIZE = 32;
+
+// Thread-specific storage that is statically alocated in the thread descriptor.
+TEST(LeakSanitizer, PthreadSpecificStatic) {
+ pthread_key_t key;
+ ASSERT_EQ(0, pthread_key_create(&key, NULL));
+ ASSERT_LT(key, PTHREAD_KEY_2NDLEVEL_SIZE);
+ void *p = malloc(kSmallAllocSize);
+ ASSERT_EQ(0, pthread_setspecific(key, p));
+ EXPECT_NOT_LEAKED(p, kAllButStackAndRegisters);
+ EXPECT_LEAKED(p, kAllButStackAndRegisters & ~__lsan::kSourceTLS);
+ EXPECT_NOT_LEAKED(p, kAllButStackAndRegisters);
+ ASSERT_EQ(0, pthread_setspecific(key, 0));
+ free(p);
+}
+
+// Dynamically allocated thread-specific storage.
+TEST(LeakSanitizer, PthreadSpecificDynamic) {
+ static const uptr kDummyKeysCount = PTHREAD_KEY_2NDLEVEL_SIZE;
+ pthread_key_t dummy_keys[kDummyKeysCount];
+ for (uptr i = 0; i < kDummyKeysCount; i++)
+ ASSERT_EQ(0, pthread_key_create(&dummy_keys[i], NULL));
+ pthread_key_t key;
+ ASSERT_EQ(0, pthread_key_create(&key, NULL));
+ void *p = malloc(kSmallAllocSize);
+ ASSERT_EQ(0, pthread_setspecific(key, p));
+ EXPECT_NOT_LEAKED(p, kAllButStackAndRegisters);
+ EXPECT_LEAKED(p, kAllButStackAndRegisters & ~__lsan::kSourceTLS);
+ EXPECT_NOT_LEAKED(p, kAllButStackAndRegisters);
+ ASSERT_EQ(0, pthread_setspecific(key, NULL));
+ CHECK_EQ(0, pthread_key_delete(key));
+ for (uptr i = 0; i < kDummyKeysCount; i++)
+ CHECK_EQ(0, pthread_key_delete(dummy_keys[i]));
+ free(p);
+}
+
+// Put pointer far enough on the stack that LSan has space to run in without
+// overwriting it.
+NOINLINE uptr PutPointerOnStaleStack(void *p) {
+ void *locals[2048];
+ locals[0] = p;
+ break_optimization(&locals[0]);
+ // Hide the result, just to suppress the compiler warning.
+ return (uptr)HIDE(&locals[0]);
+}
+
+// Local variables that have gone out of scope should be ignored by LSan.
+TEST(LeakSanitizer, StaleLocalsAreUnreachable) {
+ void *p = malloc(kSmallAllocSize);
+ void **stale_var = (void **)PEEK(PutPointerOnStaleStack(p));
+ p = HIDE(p);
+ EXPECT_LEAKED(PEEK(p), __lsan::kSourceAllAligned & ~__lsan::kSourceRegisters);
+ p = PEEK(p);
+ // Make sure LSan didn't overwrite the pointer at some point.
+ EXPECT_EQ(p, *stale_var);
+ free(p);
+}
+
+void *large_alloc;
+
+// Make sure LargeMmapAllocator's chunks aren't reachable via some internal data
+// structure.
+TEST(LeakSanitizer, SimpleLargeAllocationLeaked) {
+ large_alloc = HIDE(malloc(kLargeAllocSize));
+ EXPECT_TRUE(IsLeaked((void *)large_alloc,
+ __lsan::kSourceAllAligned & ~__lsan::kSourceRegisters));
+ large_alloc = PEEK(large_alloc);
+ EXPECT_FALSE(IsLeaked((void *)large_alloc,
+ __lsan::kSourceAllAligned & ~__lsan::kSourceRegisters));
+ free((void *)large_alloc);
+ large_alloc = NULL;
+}
+
+// Multi-threaded tests.
+struct ThreadArgument {
+ void sync_wait(uptr value) {
+ while (atomic_load(&sync, memory_order_seq_cst) != value)
+ pthread_yield();
+ }
+ void sync_store(uptr value) {
+ atomic_store(&sync, value, memory_order_seq_cst);
+ }
+
+ void *hidden_p;
+ atomic_uintptr_t sync;
+};
+
+void *StackThreadFunc(void *param) {
+ ThreadArgument *arg = reinterpret_cast<ThreadArgument *>(param);
+ void *p = malloc(kSmallAllocSize);
+ // Take p's address to ensure it's not optimized into a register.
+ void * volatile *pp = &p;
+ arg->hidden_p = HIDE(*pp);
+ arg->sync_store(1);
+ arg->sync_wait(2);
+ free(p);
+ return NULL;
+}
+
+void *RegistersThreadFunc(void *param) {
+ ThreadArgument *arg = reinterpret_cast<ThreadArgument *>(param);
+ // To store the pointer, choose a register which is unlikely to be reused by
+ // a function call.
+#if defined(__i386__)
+ register void* p asm("esi");
+#elif defined(__x86_64__)
+ register void* p asm("r15");
+#else
+ register void* p;
+#endif
+ p = malloc(kSmallAllocSize);
+ arg->hidden_p = HIDE(p);
+ arg->sync_store(1);
+ arg->sync_wait(2);
+ free(p);
+ return NULL;
+}
+
+void MultiThreadedTest(uptr source) {
+ uptr other_source;
+ void *(*func)(void *arg);
+ if (source == __lsan::kSourceStacks) {
+ func = StackThreadFunc;
+ other_source = __lsan::kSourceRegisters;
+ } else if (source == __lsan::kSourceRegisters) {
+ func = RegistersThreadFunc;
+ other_source = __lsan::kSourceStacks;
+ } else {
+ FAIL();
+ }
+ uptr baseline = __lsan::kSourceAllAligned & ~other_source;
+ ThreadArgument arg;
+ arg.sync_store(0);
+ pthread_t thread_id;
+ ASSERT_EQ(0, pthread_create(&thread_id, NULL, func, &arg));
+ arg.sync_wait(1);
+ EXPECT_NOT_LEAKED(PEEK(arg.hidden_p), baseline);
+ EXPECT_LEAKED(PEEK(arg.hidden_p), baseline & ~source);
+ EXPECT_NOT_LEAKED(PEEK(arg.hidden_p), baseline);
+ arg.sync_store(2);
+ ASSERT_EQ(0, pthread_join(thread_id, NULL));
+}
+
+TEST(LeakSanitizer, ThreadStacks) {
+ MultiThreadedTest(__lsan::kSourceStacks);
+}
+
+TEST(LeakSanitizer, ThreadRegisters) {
+ MultiThreadedTest(__lsan::kSourceRegisters);
+}
+
+// End of tests for pointer sources.
+
+TEST(LeakSanitizer, UnalignedPointers) {
+ // Static so we can disable stack.
+ static uptr arr[2];
+ char *char_arr = (char *)arr;
+ void *p = malloc(kSmallAllocSize);
+ memcpy(char_arr + 1, &p, sizeof(uptr));
+ EXPECT_LEAKED(p, kAllButStackAndRegisters);
+ EXPECT_NOT_LEAKED(p, kAllButStackAndRegisters | __lsan::kSourceUnaligned);
+ EXPECT_LEAKED(p, kAllButStackAndRegisters);
+ free(p);
+}
+
+} // namespace
+
+int main(int argc, char **argv) {
+ global_argv = argv;
+ __lsan::Init();
+ ::testing::InitGoogleTest(&argc, argv);
+ return RUN_ALL_TESTS();
+}
+
+#endif // SANITIZER_LINUX && defined(__x86_64__)
Added: compiler-rt/trunk/lib/lsan/tests/lsan_tls_loadable.cc
URL: http://llvm.org/viewvc/llvm-project/compiler-rt/trunk/lib/lsan/tests/lsan_tls_loadable.cc?rev=182250&view=auto
==============================================================================
--- compiler-rt/trunk/lib/lsan/tests/lsan_tls_loadable.cc (added)
+++ compiler-rt/trunk/lib/lsan/tests/lsan_tls_loadable.cc Mon May 20 06:09:27 2013
@@ -0,0 +1,24 @@
+//=-- lsan_tls_loadable.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.
+// 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 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];
+}
Modified: compiler-rt/trunk/lib/sanitizer_common/scripts/check_lint.sh
URL: http://llvm.org/viewvc/llvm-project/compiler-rt/trunk/lib/sanitizer_common/scripts/check_lint.sh?rev=182250&r1=182249&r2=182250&view=diff
==============================================================================
--- compiler-rt/trunk/lib/sanitizer_common/scripts/check_lint.sh (original)
+++ compiler-rt/trunk/lib/sanitizer_common/scripts/check_lint.sh Mon May 20 06:09:27 2013
@@ -30,6 +30,7 @@ TSAN_RTL_LINT_FILTER=${COMMON_LINT_FILTE
TSAN_TEST_LINT_FILTER=${TSAN_RTL_LINT_FILTER},-runtime/threadsafe_fn,-runtime/int
TSAN_LIT_TEST_LINT_FILTER=${TSAN_TEST_LINT_FILTER},-whitespace/line_length
MSAN_RTL_LINT_FILTER=${COMMON_LINT_FILTER}
+LSAN_RTL_LINT_FILTER=${COMMON_LINT_FILTER}
COMMON_RTL_INC_LINT_FILTER=${COMMON_LINT_FILTER},-runtime/int,-runtime/sizeof,-runtime/printf
SANITIZER_INCLUDES_LINT_FILTER=${COMMON_LINT_FILTER},-runtime/int
@@ -74,6 +75,11 @@ ${CPPLINT} --filter=${TSAN_LIT_TEST_LINT
MSAN_RTL=${COMPILER_RT}/lib/msan
${CPPLINT} --filter=${MSAN_RTL_LINT_FILTER} ${MSAN_RTL}/*.{cc,h}
+# LSan
+LSAN_RTL=${COMPILER_RT}/lib/lsan
+${CPPLINT} --filter=${LSAN_RTL_LINT_FILTER} ${LSAN_RTL}/*.{cc,h}
+${CPPLINT} --filter=${LSAN_RTL_LINT_FILTER} ${LSAN_RTL}/tests/*.{cc,h}
+
set +e
# Misc files
More information about the llvm-commits
mailing list