forked from OSchip/llvm-project
parent
b5483be858
commit
48c1d1acad
|
|
@ -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__)
|
||||
|
|
@ -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];
|
||||
}
|
||||
|
|
@ -30,6 +30,7 @@ TSAN_RTL_LINT_FILTER=${COMMON_LINT_FILTER}
|
|||
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_FILTER} ${TSAN_RTL}/lit_tests/*.cc
|
|||
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
|
||||
|
|
|
|||
Loading…
Reference in New Issue