diff options
Diffstat (limited to 'libsanitizer/asan/asan_thread.cc')
-rw-r--r-- | libsanitizer/asan/asan_thread.cc | 153 |
1 files changed, 153 insertions, 0 deletions
diff --git a/libsanitizer/asan/asan_thread.cc b/libsanitizer/asan/asan_thread.cc new file mode 100644 index 00000000000..9295c1570ec --- /dev/null +++ b/libsanitizer/asan/asan_thread.cc @@ -0,0 +1,153 @@ +//===-- asan_thread.cc ----------------------------------------------------===// +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file is a part of AddressSanitizer, an address sanity checker. +// +// Thread-related code. +//===----------------------------------------------------------------------===// +#include "asan_allocator.h" +#include "asan_interceptors.h" +#include "asan_stack.h" +#include "asan_thread.h" +#include "asan_thread_registry.h" +#include "asan_mapping.h" +#include "sanitizer_common/sanitizer_common.h" + +namespace __asan { + +AsanThread::AsanThread(LinkerInitialized x) + : fake_stack_(x), + malloc_storage_(x), + stats_(x) { } + +AsanThread *AsanThread::Create(u32 parent_tid, thread_callback_t start_routine, + void *arg, StackTrace *stack) { + uptr size = RoundUpTo(sizeof(AsanThread), kPageSize); + AsanThread *thread = (AsanThread*)MmapOrDie(size, __FUNCTION__); + thread->start_routine_ = start_routine; + thread->arg_ = arg; + + const uptr kSummaryAllocSize = kPageSize; + CHECK_LE(sizeof(AsanThreadSummary), kSummaryAllocSize); + AsanThreadSummary *summary = + (AsanThreadSummary*)MmapOrDie(kPageSize, "AsanThreadSummary"); + summary->Init(parent_tid, stack); + summary->set_thread(thread); + thread->set_summary(summary); + + 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::Destroy() { + if (flags()->verbosity >= 1) { + Report("T%d exited\n", tid()); + } + + asanThreadRegistry().UnregisterThread(this); + CHECK(summary()->thread() == 0); + // 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(); + uptr size = RoundUpTo(sizeof(AsanThread), kPageSize); + UnmapOrDie(this, size); +} + +void AsanThread::Init() { + SetThreadStackTopAndBottom(); + CHECK(AddrIsInMem(stack_bottom_)); + CHECK(AddrIsInMem(stack_top_)); + ClearShadowForThreadStack(); + 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()); + AsanPlatformThreadInit(); +} + +thread_return_t AsanThread::ThreadStart() { + Init(); + 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); + return 0; + } + + thread_return_t res = start_routine_(arg_); + malloc_storage().CommitBack(); + if (flags()->use_sigaltstack) UnsetAlternateSignalStack(); + + this->Destroy(); + + return res; +} + +void AsanThread::SetThreadStackTopAndBottom() { + GetThreadStackTopAndBottom(tid() == 0, &stack_top_, &stack_bottom_); + int local; + CHECK(AddrIsInStack((uptr)&local)); +} + +void AsanThread::ClearShadowForThreadStack() { + PoisonShadow(stack_bottom_, stack_top_ - stack_bottom_, 0); +} + +const char *AsanThread::GetFrameNameByAddr(uptr addr, uptr *offset) { + uptr bottom = 0; + bool is_fake_stack = false; + if (AddrIsInStack(addr)) { + bottom = stack_bottom(); + } else { + bottom = fake_stack().AddrIsInFakeStack(addr); + CHECK(bottom); + is_fake_stack = true; + } + uptr aligned_addr = addr & ~(__WORDSIZE/8 - 1); // align addr. + u8 *shadow_ptr = (u8*)MemToShadow(aligned_addr); + u8 *shadow_bottom = (u8*)MemToShadow(bottom); + + while (shadow_ptr >= shadow_bottom && + *shadow_ptr != kAsanStackLeftRedzoneMagic) { + shadow_ptr--; + } + + while (shadow_ptr >= shadow_bottom && + *shadow_ptr == kAsanStackLeftRedzoneMagic) { + shadow_ptr--; + } + + if (shadow_ptr < shadow_bottom) { + *offset = 0; + return "UNKNOWN"; + } + + uptr* ptr = (uptr*)SHADOW_TO_MEM((uptr)(shadow_ptr + 1)); + CHECK((ptr[0] == kCurrentStackFrameMagic) || + (is_fake_stack && ptr[0] == kRetiredStackFrameMagic)); + *offset = addr - (uptr)ptr; + return (const char*)ptr[1]; +} + +} // namespace __asan |