[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