summaryrefslogtreecommitdiff
path: root/libsanitizer/asan/asan_thread.cc
diff options
context:
space:
mode:
Diffstat (limited to 'libsanitizer/asan/asan_thread.cc')
-rw-r--r--libsanitizer/asan/asan_thread.cc245
1 files changed, 207 insertions, 38 deletions
diff --git a/libsanitizer/asan/asan_thread.cc b/libsanitizer/asan/asan_thread.cc
index 02f49dd59ef..1da714c6013 100644
--- a/libsanitizer/asan/asan_thread.cc
+++ b/libsanitizer/asan/asan_thread.cc
@@ -11,46 +11,82 @@
//===----------------------------------------------------------------------===//
#include "asan_allocator.h"
#include "asan_interceptors.h"
+#include "asan_poisoning.h"
#include "asan_stack.h"
#include "asan_thread.h"
-#include "asan_thread_registry.h"
#include "asan_mapping.h"
#include "sanitizer_common/sanitizer_common.h"
+#include "sanitizer_common/sanitizer_placement_new.h"
+#include "lsan/lsan_common.h"
namespace __asan {
-AsanThread::AsanThread(LinkerInitialized x)
- : fake_stack_(x),
- malloc_storage_(x),
- stats_(x) { }
+// AsanThreadContext implementation.
-AsanThread *AsanThread::Create(u32 parent_tid, thread_callback_t start_routine,
- void *arg, StackTrace *stack) {
+void AsanThreadContext::OnCreated(void *arg) {
+ CreateThreadContextArgs *args = static_cast<CreateThreadContextArgs*>(arg);
+ if (args->stack) {
+ internal_memcpy(&stack, args->stack, sizeof(stack));
+ }
+ thread = args->thread;
+ thread->set_context(this);
+}
+
+void AsanThreadContext::OnFinished() {
+ // Drop the link to the AsanThread object.
+ thread = 0;
+}
+
+// MIPS requires aligned address
+static ALIGNED(16) char thread_registry_placeholder[sizeof(ThreadRegistry)];
+static ThreadRegistry *asan_thread_registry;
+
+static ThreadContextBase *GetAsanThreadContext(u32 tid) {
+ void *mem = MmapOrDie(sizeof(AsanThreadContext), "AsanThreadContext");
+ return new(mem) AsanThreadContext(tid);
+}
+
+ThreadRegistry &asanThreadRegistry() {
+ static bool initialized;
+ // Don't worry about thread_safety - this should be called when there is
+ // a single thread.
+ if (!initialized) {
+ // Never reuse ASan threads: we store pointer to AsanThreadContext
+ // in TSD and can't reliably tell when no more TSD destructors will
+ // be called. It would be wrong to reuse AsanThreadContext for another
+ // thread before all TSD destructors will be called for it.
+ asan_thread_registry = new(thread_registry_placeholder) ThreadRegistry(
+ GetAsanThreadContext, kMaxNumberOfThreads, kMaxNumberOfThreads);
+ initialized = true;
+ }
+ return *asan_thread_registry;
+}
+
+AsanThreadContext *GetThreadContextByTidLocked(u32 tid) {
+ return static_cast<AsanThreadContext *>(
+ asanThreadRegistry().GetThreadLocked(tid));
+}
+
+// AsanThread implementation.
+
+AsanThread *AsanThread::Create(thread_callback_t start_routine,
+ void *arg) {
uptr PageSize = GetPageSizeCached();
uptr size = RoundUpTo(sizeof(AsanThread), PageSize);
AsanThread *thread = (AsanThread*)MmapOrDie(size, __FUNCTION__);
thread->start_routine_ = start_routine;
thread->arg_ = arg;
-
- const uptr kSummaryAllocSize = PageSize;
- CHECK_LE(sizeof(AsanThreadSummary), kSummaryAllocSize);
- AsanThreadSummary *summary =
- (AsanThreadSummary*)MmapOrDie(PageSize, "AsanThreadSummary");
- summary->Init(parent_tid, stack);
- summary->set_thread(thread);
- thread->set_summary(summary);
+ thread->context_ = 0;
return thread;
}
-void AsanThreadSummary::TSDDtor(void *tsd) {
- AsanThreadSummary *summary = (AsanThreadSummary*)tsd;
- if (flags()->verbosity >= 1) {
- Report("T%d TSDDtor\n", summary->tid());
- }
- if (summary->thread()) {
- summary->thread()->Destroy();
- }
+void AsanThread::TSDDtor(void *tsd) {
+ AsanThreadContext *context = (AsanThreadContext*)tsd;
+ if (flags()->verbosity >= 1)
+ Report("T%d TSDDtor\n", context->tid);
+ if (context->thread)
+ context->thread->Destroy();
}
void AsanThread::Destroy() {
@@ -58,41 +94,68 @@ void AsanThread::Destroy() {
Report("T%d exited\n", tid());
}
- asanThreadRegistry().UnregisterThread(this);
- CHECK(summary()->thread() == 0);
+ asanThreadRegistry().FinishThread(tid());
+ FlushToDeadThreadStats(&stats_);
// We also clear the shadow on thread destruction because
// some code may still be executing in later TSD destructors
// and we don't want it to have any poisoned stack.
- ClearShadowForThreadStack();
- fake_stack().Cleanup();
+ ClearShadowForThreadStackAndTLS();
+ DeleteFakeStack();
uptr size = RoundUpTo(sizeof(AsanThread), GetPageSizeCached());
UnmapOrDie(this, size);
}
+// We want to create the FakeStack lazyly on the first use, but not eralier
+// than the stack size is known and the procedure has to be async-signal safe.
+FakeStack *AsanThread::AsyncSignalSafeLazyInitFakeStack() {
+ uptr stack_size = this->stack_size();
+ if (stack_size == 0) // stack_size is not yet available, don't use FakeStack.
+ return 0;
+ uptr old_val = 0;
+ // fake_stack_ has 3 states:
+ // 0 -- not initialized
+ // 1 -- being initialized
+ // ptr -- initialized
+ // This CAS checks if the state was 0 and if so changes it to state 1,
+ // if that was successfull, it initilizes the pointer.
+ if (atomic_compare_exchange_strong(
+ reinterpret_cast<atomic_uintptr_t *>(&fake_stack_), &old_val, 1UL,
+ memory_order_relaxed)) {
+ uptr stack_size_log = Log2(RoundUpToPowerOfTwo(stack_size));
+ if (flags()->uar_stack_size_log)
+ stack_size_log = static_cast<uptr>(flags()->uar_stack_size_log);
+ fake_stack_ = FakeStack::Create(stack_size_log);
+ SetTLSFakeStack(fake_stack_);
+ return fake_stack_;
+ }
+ return 0;
+}
+
void AsanThread::Init() {
- SetThreadStackTopAndBottom();
+ SetThreadStackAndTls();
CHECK(AddrIsInMem(stack_bottom_));
CHECK(AddrIsInMem(stack_top_ - 1));
- ClearShadowForThreadStack();
+ ClearShadowForThreadStackAndTLS();
if (flags()->verbosity >= 1) {
int local = 0;
Report("T%d: stack [%p,%p) size 0x%zx; local=%p\n",
tid(), (void*)stack_bottom_, (void*)stack_top_,
stack_top_ - stack_bottom_, &local);
}
- fake_stack_.Init(stack_size());
+ fake_stack_ = 0; // Will be initialized lazily if needed.
AsanPlatformThreadInit();
}
-thread_return_t AsanThread::ThreadStart() {
+thread_return_t AsanThread::ThreadStart(uptr os_id) {
Init();
+ asanThreadRegistry().StartThread(tid(), os_id, 0);
if (flags()->use_sigaltstack) SetAlternateSignalStack();
if (!start_routine_) {
// start_routine_ == 0 if we're on the main thread or on one of the
// OS X libdispatch worker threads. But nobody is supposed to call
// ThreadStart() for the worker threads.
- CHECK(tid() == 0);
+ CHECK_EQ(tid(), 0);
return 0;
}
@@ -105,24 +168,33 @@ thread_return_t AsanThread::ThreadStart() {
return res;
}
-void AsanThread::SetThreadStackTopAndBottom() {
- GetThreadStackTopAndBottom(tid() == 0, &stack_top_, &stack_bottom_);
+void AsanThread::SetThreadStackAndTls() {
+ uptr tls_size = 0;
+ GetThreadStackAndTls(tid() == 0, &stack_bottom_, &stack_size_, &tls_begin_,
+ &tls_size);
+ stack_top_ = stack_bottom_ + stack_size_;
+ tls_end_ = tls_begin_ + tls_size;
+
int local;
CHECK(AddrIsInStack((uptr)&local));
}
-void AsanThread::ClearShadowForThreadStack() {
+void AsanThread::ClearShadowForThreadStackAndTLS() {
PoisonShadow(stack_bottom_, stack_top_ - stack_bottom_, 0);
+ if (tls_begin_ != tls_end_)
+ PoisonShadow(tls_begin_, tls_end_ - tls_begin_, 0);
}
-const char *AsanThread::GetFrameNameByAddr(uptr addr, uptr *offset) {
+const char *AsanThread::GetFrameNameByAddr(uptr addr, uptr *offset,
+ uptr *frame_pc) {
uptr bottom = 0;
if (AddrIsInStack(addr)) {
bottom = stack_bottom();
- } else {
- bottom = fake_stack().AddrIsInFakeStack(addr);
+ } else if (has_fake_stack()) {
+ bottom = fake_stack()->AddrIsInFakeStack(addr);
CHECK(bottom);
*offset = addr - bottom;
+ *frame_pc = ((uptr*)bottom)[2];
return (const char *)((uptr*)bottom)[1];
}
uptr aligned_addr = addr & ~(SANITIZER_WORDSIZE/8 - 1); // align addr.
@@ -147,7 +219,104 @@ const char *AsanThread::GetFrameNameByAddr(uptr addr, uptr *offset) {
uptr* ptr = (uptr*)SHADOW_TO_MEM((uptr)(shadow_ptr + 1));
CHECK(ptr[0] == kCurrentStackFrameMagic);
*offset = addr - (uptr)ptr;
+ *frame_pc = ptr[2];
return (const char*)ptr[1];
}
+static bool ThreadStackContainsAddress(ThreadContextBase *tctx_base,
+ void *addr) {
+ AsanThreadContext *tctx = static_cast<AsanThreadContext*>(tctx_base);
+ AsanThread *t = tctx->thread;
+ if (!t) return false;
+ if (t->AddrIsInStack((uptr)addr)) return true;
+ if (t->has_fake_stack() && t->fake_stack()->AddrIsInFakeStack((uptr)addr))
+ return true;
+ return false;
+}
+
+AsanThread *GetCurrentThread() {
+ AsanThreadContext *context =
+ reinterpret_cast<AsanThreadContext *>(AsanTSDGet());
+ if (!context) {
+ if (SANITIZER_ANDROID) {
+ // On Android, libc constructor is called _after_ asan_init, and cleans up
+ // TSD. Try to figure out if this is still the main thread by the stack
+ // address. We are not entirely sure that we have correct main thread
+ // limits, so only do this magic on Android, and only if the found thread
+ // is the main thread.
+ AsanThreadContext *tctx = GetThreadContextByTidLocked(0);
+ if (ThreadStackContainsAddress(tctx, &context)) {
+ SetCurrentThread(tctx->thread);
+ return tctx->thread;
+ }
+ }
+ return 0;
+ }
+ return context->thread;
+}
+
+void SetCurrentThread(AsanThread *t) {
+ CHECK(t->context());
+ if (flags()->verbosity >= 2) {
+ Report("SetCurrentThread: %p for thread %p\n",
+ t->context(), (void*)GetThreadSelf());
+ }
+ // Make sure we do not reset the current AsanThread.
+ CHECK_EQ(0, AsanTSDGet());
+ AsanTSDSet(t->context());
+ CHECK_EQ(t->context(), AsanTSDGet());
+}
+
+u32 GetCurrentTidOrInvalid() {
+ AsanThread *t = GetCurrentThread();
+ return t ? t->tid() : kInvalidTid;
+}
+
+AsanThread *FindThreadByStackAddress(uptr addr) {
+ asanThreadRegistry().CheckLocked();
+ AsanThreadContext *tctx = static_cast<AsanThreadContext *>(
+ asanThreadRegistry().FindThreadContextLocked(ThreadStackContainsAddress,
+ (void *)addr));
+ return tctx ? tctx->thread : 0;
+}
+
+void EnsureMainThreadIDIsCorrect() {
+ AsanThreadContext *context =
+ reinterpret_cast<AsanThreadContext *>(AsanTSDGet());
+ if (context && (context->tid == 0))
+ context->os_id = GetTid();
+}
} // namespace __asan
+
+// --- Implementation of LSan-specific functions --- {{{1
+namespace __lsan {
+bool GetThreadRangesLocked(uptr os_id, uptr *stack_begin, uptr *stack_end,
+ uptr *tls_begin, uptr *tls_end,
+ uptr *cache_begin, uptr *cache_end) {
+ __asan::AsanThreadContext *context = static_cast<__asan::AsanThreadContext *>(
+ __asan::asanThreadRegistry().FindThreadContextByOsIDLocked(os_id));
+ if (!context) return false;
+ __asan::AsanThread *t = context->thread;
+ if (!t) return false;
+ *stack_begin = t->stack_bottom();
+ *stack_end = t->stack_top();
+ *tls_begin = t->tls_begin();
+ *tls_end = t->tls_end();
+ // ASan doesn't keep allocator caches in TLS, so these are unused.
+ *cache_begin = 0;
+ *cache_end = 0;
+ return true;
+}
+
+void LockThreadRegistry() {
+ __asan::asanThreadRegistry().Lock();
+}
+
+void UnlockThreadRegistry() {
+ __asan::asanThreadRegistry().Unlock();
+}
+
+void EnsureMainThreadIDIsCorrect() {
+ __asan::EnsureMainThreadIDIsCorrect();
+}
+} // namespace __lsan