| //===-- asan_thread.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 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 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); |
| |
| 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), GetPageSizeCached()); |
| UnmapOrDie(this, size); |
| } |
| |
| void AsanThread::Init() { |
| SetThreadStackTopAndBottom(); |
| CHECK(AddrIsInMem(stack_bottom_)); |
| CHECK(AddrIsInMem(stack_top_ - 1)); |
| 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; |
| if (AddrIsInStack(addr)) { |
| bottom = stack_bottom(); |
| } else { |
| bottom = fake_stack().AddrIsInFakeStack(addr); |
| CHECK(bottom); |
| *offset = addr - bottom; |
| return (const char *)((uptr*)bottom)[1]; |
| } |
| uptr aligned_addr = addr & ~(SANITIZER_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); |
| *offset = addr - (uptr)ptr; |
| return (const char*)ptr[1]; |
| } |
| |
| } // namespace __asan |