Index: vendor/compiler-rt/dist/include/sanitizer/coverage_interface.h =================================================================== --- vendor/compiler-rt/dist/include/sanitizer/coverage_interface.h (revision 317221) +++ vendor/compiler-rt/dist/include/sanitizer/coverage_interface.h (revision 317222) @@ -1,71 +1,42 @@ //===-- sanitizer/coverage_interface.h --------------------------*- C++ -*-===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// // // Public interface for sanitizer coverage. //===----------------------------------------------------------------------===// #ifndef SANITIZER_COVERAG_INTERFACE_H #define SANITIZER_COVERAG_INTERFACE_H #include #ifdef __cplusplus extern "C" { #endif // Initialize coverage. void __sanitizer_cov_init(); // Record and dump coverage info. void __sanitizer_cov_dump(); // Dump collected coverage info. Sorts pcs by module into individual // .sancov files. void __sanitizer_dump_coverage(const uintptr_t *pcs, uintptr_t len); // Open .sancov.packed in the coverage directory and return the file // descriptor. Returns -1 on failure, or if coverage dumping is disabled. // This is intended for use by sandboxing code. intptr_t __sanitizer_maybe_open_cov_file(const char *name); // Get the number of unique covered blocks (or edges). // This can be useful for coverage-directed in-process fuzzers. uintptr_t __sanitizer_get_total_unique_coverage(); - // Get the number of unique indirect caller-callee pairs. - uintptr_t __sanitizer_get_total_unique_caller_callee_pairs(); - - // Reset the basic-block (edge) coverage to the initial state. - // Useful for in-process fuzzing to start collecting coverage from scratch. - // Experimental, will likely not work for multi-threaded process. - void __sanitizer_reset_coverage(); - // Set *data to the array of covered PCs and return the size of that array. - // Some of the entries in *data will be zero. - uintptr_t __sanitizer_get_coverage_guards(uintptr_t **data); - - // The coverage instrumentation may optionally provide imprecise counters. - // Rather than exposing the counter values to the user we instead map - // the counters to a bitset. - // Every counter is associated with 8 bits in the bitset. - // We define 8 value ranges: 1, 2, 3, 4-7, 8-15, 16-31, 32-127, 128+ - // The i-th bit is set to 1 if the counter value is in the i-th range. - // This counter-based coverage implementation is *not* thread-safe. - - // Returns the number of registered coverage counters. - uintptr_t __sanitizer_get_number_of_counters(); - // Updates the counter 'bitset', clears the counters and returns the number of - // new bits in 'bitset'. - // If 'bitset' is nullptr, only clears the counters. - // Otherwise 'bitset' should be at least - // __sanitizer_get_number_of_counters bytes long and 8-aligned. - uintptr_t - __sanitizer_update_counter_bitset_and_clear_counters(uint8_t *bitset); - #ifdef __cplusplus } // extern "C" #endif #endif // SANITIZER_COVERAG_INTERFACE_H Index: vendor/compiler-rt/dist/lib/asan/asan_thread.cc =================================================================== --- vendor/compiler-rt/dist/lib/asan/asan_thread.cc (revision 317221) +++ vendor/compiler-rt/dist/lib/asan/asan_thread.cc (revision 317222) @@ -1,472 +1,472 @@ //===-- 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_poisoning.h" #include "asan_stack.h" #include "asan_thread.h" #include "asan_mapping.h" #include "sanitizer_common/sanitizer_common.h" #include "sanitizer_common/sanitizer_placement_new.h" #include "sanitizer_common/sanitizer_stackdepot.h" #include "sanitizer_common/sanitizer_tls_get_addr.h" #include "lsan/lsan_common.h" namespace __asan { // AsanThreadContext implementation. struct CreateThreadContextArgs { AsanThread *thread; StackTrace *stack; }; void AsanThreadContext::OnCreated(void *arg) { CreateThreadContextArgs *args = static_cast(arg); if (args->stack) stack_id = StackDepotPut(*args->stack); thread = args->thread; thread->set_context(this); } void AsanThreadContext::OnFinished() { // Drop the link to the AsanThread object. thread = nullptr; } // MIPS requires aligned address static ALIGNED(16) char thread_registry_placeholder[sizeof(ThreadRegistry)]; static ThreadRegistry *asan_thread_registry; static BlockingMutex mu_for_thread_context(LINKER_INITIALIZED); static LowLevelAllocator allocator_for_thread_context; static ThreadContextBase *GetAsanThreadContext(u32 tid) { BlockingMutexLock lock(&mu_for_thread_context); return new(allocator_for_thread_context) 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( asanThreadRegistry().GetThreadLocked(tid)); } // AsanThread implementation. AsanThread *AsanThread::Create(thread_callback_t start_routine, void *arg, u32 parent_tid, StackTrace *stack, bool detached) { uptr PageSize = GetPageSizeCached(); uptr size = RoundUpTo(sizeof(AsanThread), PageSize); AsanThread *thread = (AsanThread*)MmapOrDie(size, __func__); thread->start_routine_ = start_routine; thread->arg_ = arg; CreateThreadContextArgs args = { thread, stack }; asanThreadRegistry().CreateThread(*reinterpret_cast(thread), detached, parent_tid, &args); return thread; } void AsanThread::TSDDtor(void *tsd) { AsanThreadContext *context = (AsanThreadContext*)tsd; VReport(1, "T%d TSDDtor\n", context->tid); if (context->thread) context->thread->Destroy(); } void AsanThread::Destroy() { int tid = this->tid(); VReport(1, "T%d exited\n", tid); malloc_storage().CommitBack(); if (common_flags()->use_sigaltstack) UnsetAlternateSignalStack(); 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. ClearShadowForThreadStackAndTLS(); DeleteFakeStack(tid); uptr size = RoundUpTo(sizeof(AsanThread), GetPageSizeCached()); UnmapOrDie(this, size); DTLS_Destroy(); } void AsanThread::StartSwitchFiber(FakeStack **fake_stack_save, uptr bottom, uptr size) { if (atomic_load(&stack_switching_, memory_order_relaxed)) { Report("ERROR: starting fiber switch while in fiber switch\n"); Die(); } next_stack_bottom_ = bottom; next_stack_top_ = bottom + size; atomic_store(&stack_switching_, 1, memory_order_release); FakeStack *current_fake_stack = fake_stack_; if (fake_stack_save) *fake_stack_save = fake_stack_; fake_stack_ = nullptr; SetTLSFakeStack(nullptr); // if fake_stack_save is null, the fiber will die, delete the fakestack if (!fake_stack_save && current_fake_stack) current_fake_stack->Destroy(this->tid()); } void AsanThread::FinishSwitchFiber(FakeStack *fake_stack_save, uptr *bottom_old, uptr *size_old) { if (!atomic_load(&stack_switching_, memory_order_relaxed)) { Report("ERROR: finishing a fiber switch that has not started\n"); Die(); } if (fake_stack_save) { SetTLSFakeStack(fake_stack_save); fake_stack_ = fake_stack_save; } if (bottom_old) *bottom_old = stack_bottom_; if (size_old) *size_old = stack_top_ - stack_bottom_; stack_bottom_ = next_stack_bottom_; stack_top_ = next_stack_top_; atomic_store(&stack_switching_, 0, memory_order_release); next_stack_top_ = 0; next_stack_bottom_ = 0; } inline AsanThread::StackBounds AsanThread::GetStackBounds() const { if (!atomic_load(&stack_switching_, memory_order_acquire)) return StackBounds{stack_bottom_, stack_top_}; // NOLINT char local; const uptr cur_stack = (uptr)&local; // Note: need to check next stack first, because FinishSwitchFiber // may be in process of overwriting stack_top_/bottom_. But in such case // we are already on the next stack. if (cur_stack >= next_stack_bottom_ && cur_stack < next_stack_top_) return StackBounds{next_stack_bottom_, next_stack_top_}; // NOLINT return StackBounds{stack_bottom_, stack_top_}; // NOLINT } uptr AsanThread::stack_top() { return GetStackBounds().top; } uptr AsanThread::stack_bottom() { return GetStackBounds().bottom; } uptr AsanThread::stack_size() { const auto bounds = GetStackBounds(); return bounds.top - bounds.bottom; } // 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 nullptr; 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 successful, it initializes the pointer. if (atomic_compare_exchange_strong( reinterpret_cast(&fake_stack_), &old_val, 1UL, memory_order_relaxed)) { uptr stack_size_log = Log2(RoundUpToPowerOfTwo(stack_size)); CHECK_LE(flags()->min_uar_stack_size_log, flags()->max_uar_stack_size_log); stack_size_log = Min(stack_size_log, static_cast(flags()->max_uar_stack_size_log)); stack_size_log = Max(stack_size_log, static_cast(flags()->min_uar_stack_size_log)); fake_stack_ = FakeStack::Create(stack_size_log); SetTLSFakeStack(fake_stack_); return fake_stack_; } return nullptr; } void AsanThread::Init() { next_stack_top_ = next_stack_bottom_ = 0; atomic_store(&stack_switching_, false, memory_order_release); fake_stack_ = nullptr; // Will be initialized lazily if needed. CHECK_EQ(this->stack_size(), 0U); SetThreadStackAndTls(); CHECK_GT(this->stack_size(), 0U); CHECK(AddrIsInMem(stack_bottom_)); CHECK(AddrIsInMem(stack_top_ - 1)); ClearShadowForThreadStackAndTLS(); int local = 0; VReport(1, "T%d: stack [%p,%p) size 0x%zx; local=%p\n", tid(), (void *)stack_bottom_, (void *)stack_top_, stack_top_ - stack_bottom_, &local); } thread_return_t AsanThread::ThreadStart( - uptr os_id, atomic_uintptr_t *signal_thread_is_registered) { + tid_t os_id, atomic_uintptr_t *signal_thread_is_registered) { Init(); asanThreadRegistry().StartThread(tid(), os_id, /*workerthread*/ false, nullptr); if (signal_thread_is_registered) atomic_store(signal_thread_is_registered, 1, memory_order_release); if (common_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_EQ(tid(), 0); return 0; } thread_return_t res = start_routine_(arg_); // On POSIX systems we defer this to the TSD destructor. LSan will consider // the thread's memory as non-live from the moment we call Destroy(), even // though that memory might contain pointers to heap objects which will be // cleaned up by a user-defined TSD destructor. Thus, calling Destroy() before // the TSD destructors have run might cause false positives in LSan. if (!SANITIZER_POSIX) this->Destroy(); return res; } void AsanThread::SetThreadStackAndTls() { uptr tls_size = 0; uptr stack_size = 0; GetThreadStackAndTls(tid() == 0, const_cast(&stack_bottom_), const_cast(&stack_size), &tls_begin_, &tls_size); stack_top_ = stack_bottom_ + stack_size; tls_end_ = tls_begin_ + tls_size; dtls_ = DTLS_Get(); int local; CHECK(AddrIsInStack((uptr)&local)); } void AsanThread::ClearShadowForThreadStackAndTLS() { PoisonShadow(stack_bottom_, stack_top_ - stack_bottom_, 0); if (tls_begin_ != tls_end_) PoisonShadow(tls_begin_, tls_end_ - tls_begin_, 0); } bool AsanThread::GetStackFrameAccessByAddr(uptr addr, StackFrameAccess *access) { uptr bottom = 0; if (AddrIsInStack(addr)) { bottom = stack_bottom(); } else if (has_fake_stack()) { bottom = fake_stack()->AddrIsInFakeStack(addr); CHECK(bottom); access->offset = addr - bottom; access->frame_pc = ((uptr*)bottom)[2]; access->frame_descr = (const char *)((uptr*)bottom)[1]; return true; } uptr aligned_addr = addr & ~(SANITIZER_WORDSIZE/8 - 1); // align addr. uptr mem_ptr = RoundDownTo(aligned_addr, SHADOW_GRANULARITY); u8 *shadow_ptr = (u8*)MemToShadow(aligned_addr); u8 *shadow_bottom = (u8*)MemToShadow(bottom); while (shadow_ptr >= shadow_bottom && *shadow_ptr != kAsanStackLeftRedzoneMagic) { shadow_ptr--; mem_ptr -= SHADOW_GRANULARITY; } while (shadow_ptr >= shadow_bottom && *shadow_ptr == kAsanStackLeftRedzoneMagic) { shadow_ptr--; mem_ptr -= SHADOW_GRANULARITY; } if (shadow_ptr < shadow_bottom) { return false; } uptr* ptr = (uptr*)(mem_ptr + SHADOW_GRANULARITY); CHECK(ptr[0] == kCurrentStackFrameMagic); access->offset = addr - (uptr)ptr; access->frame_pc = ptr[2]; access->frame_descr = (const char*)ptr[1]; return true; } bool AsanThread::AddrIsInStack(uptr addr) { const auto bounds = GetStackBounds(); return addr >= bounds.bottom && addr < bounds.top; } static bool ThreadStackContainsAddress(ThreadContextBase *tctx_base, void *addr) { AsanThreadContext *tctx = static_cast(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(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 (tctx && ThreadStackContainsAddress(tctx, &context)) { SetCurrentThread(tctx->thread); return tctx->thread; } } return nullptr; } return context->thread; } void SetCurrentThread(AsanThread *t) { CHECK(t->context()); VReport(2, "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( asanThreadRegistry().FindThreadContextLocked(ThreadStackContainsAddress, (void *)addr)); return tctx ? tctx->thread : nullptr; } void EnsureMainThreadIDIsCorrect() { AsanThreadContext *context = reinterpret_cast(AsanTSDGet()); if (context && (context->tid == 0)) context->os_id = GetTid(); } -__asan::AsanThread *GetAsanThreadByOsIDLocked(uptr os_id) { +__asan::AsanThread *GetAsanThreadByOsIDLocked(tid_t os_id) { __asan::AsanThreadContext *context = static_cast<__asan::AsanThreadContext *>( __asan::asanThreadRegistry().FindThreadContextByOsIDLocked(os_id)); if (!context) return nullptr; return context->thread; } } // namespace __asan // --- Implementation of LSan-specific functions --- {{{1 namespace __lsan { -bool GetThreadRangesLocked(uptr os_id, uptr *stack_begin, uptr *stack_end, +bool GetThreadRangesLocked(tid_t os_id, uptr *stack_begin, uptr *stack_end, uptr *tls_begin, uptr *tls_end, uptr *cache_begin, uptr *cache_end, DTLS **dtls) { __asan::AsanThread *t = __asan::GetAsanThreadByOsIDLocked(os_id); 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; *dtls = t->dtls(); return true; } -void ForEachExtraStackRange(uptr os_id, RangeIteratorCallback callback, +void ForEachExtraStackRange(tid_t os_id, RangeIteratorCallback callback, void *arg) { __asan::AsanThread *t = __asan::GetAsanThreadByOsIDLocked(os_id); if (t && t->has_fake_stack()) t->fake_stack()->ForEachFakeFrame(callback, arg); } void LockThreadRegistry() { __asan::asanThreadRegistry().Lock(); } void UnlockThreadRegistry() { __asan::asanThreadRegistry().Unlock(); } void EnsureMainThreadIDIsCorrect() { __asan::EnsureMainThreadIDIsCorrect(); } } // namespace __lsan // ---------------------- Interface ---------------- {{{1 using namespace __asan; // NOLINT extern "C" { SANITIZER_INTERFACE_ATTRIBUTE void __sanitizer_start_switch_fiber(void **fakestacksave, const void *bottom, uptr size) { AsanThread *t = GetCurrentThread(); if (!t) { VReport(1, "__asan_start_switch_fiber called from unknown thread\n"); return; } t->StartSwitchFiber((FakeStack**)fakestacksave, (uptr)bottom, size); } SANITIZER_INTERFACE_ATTRIBUTE void __sanitizer_finish_switch_fiber(void* fakestack, const void **bottom_old, uptr *size_old) { AsanThread *t = GetCurrentThread(); if (!t) { VReport(1, "__asan_finish_switch_fiber called from unknown thread\n"); return; } t->FinishSwitchFiber((FakeStack*)fakestack, (uptr*)bottom_old, (uptr*)size_old); } } Index: vendor/compiler-rt/dist/lib/asan/asan_thread.h =================================================================== --- vendor/compiler-rt/dist/lib/asan/asan_thread.h (revision 317221) +++ vendor/compiler-rt/dist/lib/asan/asan_thread.h (revision 317222) @@ -1,206 +1,206 @@ //===-- asan_thread.h -------------------------------------------*- C++ -*-===// // // 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. // // ASan-private header for asan_thread.cc. //===----------------------------------------------------------------------===// #ifndef ASAN_THREAD_H #define ASAN_THREAD_H #include "asan_allocator.h" #include "asan_internal.h" #include "asan_fake_stack.h" #include "asan_stats.h" #include "sanitizer_common/sanitizer_common.h" #include "sanitizer_common/sanitizer_libc.h" #include "sanitizer_common/sanitizer_thread_registry.h" namespace __sanitizer { struct DTLS; } // namespace __sanitizer namespace __asan { const u32 kInvalidTid = 0xffffff; // Must fit into 24 bits. const u32 kMaxNumberOfThreads = (1 << 22); // 4M class AsanThread; // These objects are created for every thread and are never deleted, // so we can find them by tid even if the thread is long dead. class AsanThreadContext : public ThreadContextBase { public: explicit AsanThreadContext(int tid) : ThreadContextBase(tid), announced(false), destructor_iterations(GetPthreadDestructorIterations()), stack_id(0), thread(nullptr) {} bool announced; u8 destructor_iterations; u32 stack_id; AsanThread *thread; void OnCreated(void *arg) override; void OnFinished() override; }; // AsanThreadContext objects are never freed, so we need many of them. COMPILER_CHECK(sizeof(AsanThreadContext) <= 256); // AsanThread are stored in TSD and destroyed when the thread dies. class AsanThread { public: static AsanThread *Create(thread_callback_t start_routine, void *arg, u32 parent_tid, StackTrace *stack, bool detached); static void TSDDtor(void *tsd); void Destroy(); void Init(); // Should be called from the thread itself. - thread_return_t ThreadStart(uptr os_id, + thread_return_t ThreadStart(tid_t os_id, atomic_uintptr_t *signal_thread_is_registered); uptr stack_top(); uptr stack_bottom(); uptr stack_size(); uptr tls_begin() { return tls_begin_; } uptr tls_end() { return tls_end_; } DTLS *dtls() { return dtls_; } u32 tid() { return context_->tid; } AsanThreadContext *context() { return context_; } void set_context(AsanThreadContext *context) { context_ = context; } struct StackFrameAccess { uptr offset; uptr frame_pc; const char *frame_descr; }; bool GetStackFrameAccessByAddr(uptr addr, StackFrameAccess *access); bool AddrIsInStack(uptr addr); void DeleteFakeStack(int tid) { if (!fake_stack_) return; FakeStack *t = fake_stack_; fake_stack_ = nullptr; SetTLSFakeStack(nullptr); t->Destroy(tid); } void StartSwitchFiber(FakeStack **fake_stack_save, uptr bottom, uptr size); void FinishSwitchFiber(FakeStack *fake_stack_save, uptr *bottom_old, uptr *size_old); bool has_fake_stack() { return !atomic_load(&stack_switching_, memory_order_relaxed) && (reinterpret_cast(fake_stack_) > 1); } FakeStack *fake_stack() { if (!__asan_option_detect_stack_use_after_return) return nullptr; if (atomic_load(&stack_switching_, memory_order_relaxed)) return nullptr; if (!has_fake_stack()) return AsyncSignalSafeLazyInitFakeStack(); return fake_stack_; } // True is this thread is currently unwinding stack (i.e. collecting a stack // trace). Used to prevent deadlocks on platforms where libc unwinder calls // malloc internally. See PR17116 for more details. bool isUnwinding() const { return unwinding_; } void setUnwinding(bool b) { unwinding_ = b; } // True if we are in a deadly signal handler. bool isInDeadlySignal() const { return in_deadly_signal_; } void setInDeadlySignal(bool b) { in_deadly_signal_ = b; } AsanThreadLocalMallocStorage &malloc_storage() { return malloc_storage_; } AsanStats &stats() { return stats_; } private: // NOTE: There is no AsanThread constructor. It is allocated // via mmap() and *must* be valid in zero-initialized state. void SetThreadStackAndTls(); void ClearShadowForThreadStackAndTLS(); FakeStack *AsyncSignalSafeLazyInitFakeStack(); struct StackBounds { uptr bottom; uptr top; }; StackBounds GetStackBounds() const; AsanThreadContext *context_; thread_callback_t start_routine_; void *arg_; uptr stack_top_; uptr stack_bottom_; // these variables are used when the thread is about to switch stack uptr next_stack_top_; uptr next_stack_bottom_; // true if switching is in progress atomic_uint8_t stack_switching_; uptr tls_begin_; uptr tls_end_; DTLS *dtls_; FakeStack *fake_stack_; AsanThreadLocalMallocStorage malloc_storage_; AsanStats stats_; bool unwinding_; bool in_deadly_signal_; }; // ScopedUnwinding is a scope for stacktracing member of a context class ScopedUnwinding { public: explicit ScopedUnwinding(AsanThread *t) : thread(t) { t->setUnwinding(true); } ~ScopedUnwinding() { thread->setUnwinding(false); } private: AsanThread *thread; }; // ScopedDeadlySignal is a scope for handling deadly signals. class ScopedDeadlySignal { public: explicit ScopedDeadlySignal(AsanThread *t) : thread(t) { if (thread) thread->setInDeadlySignal(true); } ~ScopedDeadlySignal() { if (thread) thread->setInDeadlySignal(false); } private: AsanThread *thread; }; // Returns a single instance of registry. ThreadRegistry &asanThreadRegistry(); // Must be called under ThreadRegistryLock. AsanThreadContext *GetThreadContextByTidLocked(u32 tid); // Get the current thread. May return 0. AsanThread *GetCurrentThread(); void SetCurrentThread(AsanThread *t); u32 GetCurrentTidOrInvalid(); AsanThread *FindThreadByStackAddress(uptr addr); // Used to handle fork(). void EnsureMainThreadIDIsCorrect(); } // namespace __asan #endif // ASAN_THREAD_H Index: vendor/compiler-rt/dist/lib/asan/tests/asan_test_main.cc =================================================================== --- vendor/compiler-rt/dist/lib/asan/tests/asan_test_main.cc (revision 317221) +++ vendor/compiler-rt/dist/lib/asan/tests/asan_test_main.cc (revision 317222) @@ -1,48 +1,56 @@ //===-- asan_test_main.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. // //===----------------------------------------------------------------------===// #include "asan_test_utils.h" #include "sanitizer_common/sanitizer_platform.h" -// Default ASAN_OPTIONS for the unit tests. Let's turn symbolication off to -// speed up testing (unit tests don't use it anyway). +// Default ASAN_OPTIONS for the unit tests. extern "C" const char* __asan_default_options() { #if SANITIZER_MAC // On Darwin, we default to `abort_on_error=1`, which would make tests run - // much slower. Let's override this and run lit tests with 'abort_on_error=0'. - // Also, make sure we do not overwhelm the syslog while testing. + // much slower. Let's override this and run lit tests with 'abort_on_error=0' + // and make sure we do not overwhelm the syslog while testing. Also, let's + // turn symbolization off to speed up testing, especially when not running + // with llvm-symbolizer but with atos. return "symbolize=false:abort_on_error=0:log_to_syslog=0"; +#elif SANITIZER_SUPPRESS_LEAK_ON_PTHREAD_EXIT + // On PowerPC and ARM Thumb, a couple tests involving pthread_exit fail due to + // leaks detected by LSan. Symbolized leak report is required to apply a + // suppression for this known problem. + return ""; #else + // Let's turn symbolization off to speed up testing (more than 3 times speedup + // observed). return "symbolize=false"; #endif } namespace __sanitizer { bool ReexecDisabled() { #if __has_feature(address_sanitizer) && SANITIZER_MAC // Allow re-exec in instrumented unit tests on Darwin. Technically, we only // need this for 10.10 and below, where re-exec is required for the // interceptors to work, but to avoid duplicating the version detection logic, // let's just allow re-exec for all Darwin versions. On newer OS versions, // returning 'false' doesn't do anything anyway, because we don't re-exec. return false; #else return true; #endif } } // namespace __sanitizer int main(int argc, char **argv) { testing::GTEST_FLAG(death_test_style) = "threadsafe"; testing::InitGoogleTest(&argc, argv); return RUN_ALL_TESTS(); } Index: vendor/compiler-rt/dist/lib/dfsan/done_abilist.txt =================================================================== --- vendor/compiler-rt/dist/lib/dfsan/done_abilist.txt (revision 317221) +++ vendor/compiler-rt/dist/lib/dfsan/done_abilist.txt (revision 317222) @@ -1,314 +1,300 @@ fun:main=uninstrumented fun:main=discard ############################################################################### # DFSan interface functions ############################################################################### fun:dfsan_union=uninstrumented fun:dfsan_union=discard fun:dfsan_create_label=uninstrumented fun:dfsan_create_label=discard fun:dfsan_set_label=uninstrumented fun:dfsan_set_label=discard fun:dfsan_add_label=uninstrumented fun:dfsan_add_label=discard fun:dfsan_get_label=uninstrumented fun:dfsan_get_label=custom fun:dfsan_read_label=uninstrumented fun:dfsan_read_label=discard fun:dfsan_get_label_count=uninstrumented fun:dfsan_get_label_count=discard fun:dfsan_get_label_info=uninstrumented fun:dfsan_get_label_info=discard fun:dfsan_has_label=uninstrumented fun:dfsan_has_label=discard fun:dfsan_has_label_with_desc=uninstrumented fun:dfsan_has_label_with_desc=discard fun:dfsan_set_write_callback=uninstrumented fun:dfsan_set_write_callback=custom ############################################################################### # glibc ############################################################################### fun:malloc=discard fun:realloc=discard fun:free=discard # Functions that return a value that depends on the input, but the output might # not be necessarily data-dependent on the input. fun:isalpha=functional fun:isdigit=functional fun:isprint=functional fun:isxdigit=functional fun:isalnum=functional fun:ispunct=functional fun:isspace=functional fun:tolower=functional fun:toupper=functional # Functions that return a value that is data-dependent on the input. fun:btowc=functional fun:exp=functional fun:exp2=functional fun:fabs=functional fun:finite=functional fun:floor=functional fun:fmod=functional fun:isinf=functional fun:isnan=functional fun:log=functional fun:modf=functional fun:pow=functional fun:round=functional fun:sqrt=functional fun:wctob=functional # Functions that produce an output that does not depend on the input (shadow is # zeroed automatically). fun:__assert_fail=discard fun:__ctype_b_loc=discard fun:__cxa_atexit=discard fun:__errno_location=discard fun:__newlocale=discard fun:__sbrk=discard fun:__sigsetjmp=discard fun:__uselocale=discard fun:__wctype_l=discard fun:access=discard fun:alarm=discard fun:atexit=discard fun:bind=discard fun:chdir=discard fun:close=discard fun:closedir=discard fun:connect=discard fun:dladdr=discard fun:dlclose=discard fun:fclose=discard fun:feof=discard fun:ferror=discard fun:fflush=discard fun:fileno=discard fun:fopen=discard fun:fprintf=discard fun:fputc=discard fun:fputc=discard fun:fputs=discard fun:fputs=discard fun:fseek=discard fun:ftell=discard fun:fwrite=discard fun:getenv=discard fun:getuid=discard fun:geteuid=discard fun:getpagesize=discard fun:getpid=discard fun:kill=discard fun:listen=discard fun:lseek=discard fun:mkdir=discard fun:mmap=discard fun:munmap=discard fun:open=discard fun:pipe=discard fun:posix_fadvise=discard fun:posix_memalign=discard fun:prctl=discard fun:printf=discard fun:pthread_sigmask=discard fun:putc=discard fun:putchar=discard fun:puts=discard fun:rand=discard fun:random=discard fun:remove=discard fun:sched_getcpu=discard fun:sched_get_priority_max=discard fun:sched_setaffinity=discard fun:sched_yield=discard fun:sem_destroy=discard fun:sem_init=discard fun:sem_post=discard fun:sem_wait=discard fun:send=discard fun:sendmsg=discard fun:sendto=discard fun:setsockopt=discard fun:shutdown=discard fun:sleep=discard fun:socket=discard fun:strerror=discard fun:strspn=discard fun:strcspn=discard fun:symlink=discard fun:syscall=discard fun:unlink=discard fun:uselocale=discard # Functions that produce output does not depend on the input (need to zero the # shadow manually). fun:calloc=custom fun:clock_gettime=custom fun:dlopen=custom fun:fgets=custom fun:fstat=custom fun:getcwd=custom fun:get_current_dir_name=custom fun:gethostname=custom fun:getrlimit=custom fun:getrusage=custom fun:nanosleep=custom fun:pread=custom fun:read=custom fun:socketpair=custom fun:stat=custom fun:time=custom # Functions that produce an output that depend on the input (propagate the # shadow manually). fun:ctime_r=custom fun:inet_pton=custom fun:localtime_r=custom fun:memcpy=custom fun:memset=custom fun:strcpy=custom fun:strdup=custom fun:strncpy=custom fun:strtod=custom fun:strtol=custom fun:strtoll=custom fun:strtoul=custom fun:strtoull=custom # Functions that produce an output that is computed from the input, but is not # necessarily data dependent. fun:memchr=custom fun:memcmp=custom fun:strcasecmp=custom fun:strchr=custom fun:strcmp=custom fun:strlen=custom fun:strncasecmp=custom fun:strncmp=custom fun:strrchr=custom fun:strstr=custom # Functions which take action based on global state, such as running a callback # set by a sepperate function. fun:write=custom # Functions that take a callback (wrap the callback manually). fun:dl_iterate_phdr=custom fun:getpwuid_r=custom fun:poll=custom fun:sched_getaffinity=custom fun:select=custom fun:sigemptyset=custom fun:sigaction=custom fun:gettimeofday=custom # sprintf-like fun:sprintf=custom fun:snprintf=custom # TODO: custom fun:asprintf=discard fun:qsort=discard ############################################################################### # pthread ############################################################################### fun:pthread_equal=discard fun:pthread_getspecific=discard fun:pthread_key_create=discard fun:pthread_key_delete=discard fun:pthread_mutex_destroy=discard fun:pthread_mutex_init=discard fun:pthread_mutex_lock=discard fun:pthread_mutex_trylock=discard fun:pthread_mutex_unlock=discard fun:pthread_mutexattr_destroy=discard fun:pthread_mutexattr_init=discard fun:pthread_mutexattr_settype=discard fun:pthread_once=discard fun:pthread_self=discard fun:pthread_setspecific=discard # Functions that take a callback (wrap the callback manually). fun:pthread_create=custom ############################################################################### # libffi/libgo ############################################################################### # Functions that are written in asm or are called from asm. fun:ffi_call_unix64=uninstrumented fun:ffi_call_unix64=discard fun:ffi_closure_unix64_inner=uninstrumented fun:ffi_closure_unix64_inner=discard fun:ffi_closure_unix64=uninstrumented fun:ffi_closure_unix64=discard fun:__go_get_closure=uninstrumented fun:__go_get_closure=discard fun:__go_makefunc_can_recover=uninstrumented fun:__go_makefunc_can_recover=discard fun:__go_makefunc_returning=uninstrumented fun:__go_makefunc_returning=discard fun:reflect.MakeFuncStubGo=uninstrumented fun:reflect.MakeFuncStubGo=discard fun:reflect.makeFuncStub=uninstrumented fun:reflect.makeFuncStub=discard ############################################################################### # lib/Fuzzer ############################################################################### # Replaces __sanitizer_cov_trace_cmp with __dfsw___sanitizer_cov_trace_cmp fun:__sanitizer_cov_trace_cmp=custom fun:__sanitizer_cov_trace_cmp=uninstrumented fun:__sanitizer_cov_trace_cmp1=custom fun:__sanitizer_cov_trace_cmp1=uninstrumented fun:__sanitizer_cov_trace_cmp2=custom fun:__sanitizer_cov_trace_cmp2=uninstrumented fun:__sanitizer_cov_trace_cmp4=custom fun:__sanitizer_cov_trace_cmp4=uninstrumented fun:__sanitizer_cov_trace_cmp8=custom fun:__sanitizer_cov_trace_cmp8=uninstrumented # Similar for __sanitizer_cov_trace_switch fun:__sanitizer_cov_trace_switch=custom fun:__sanitizer_cov_trace_switch=uninstrumented # Ignores all other __sanitizer callbacks. fun:__sanitizer_cov=uninstrumented fun:__sanitizer_cov=discard fun:__sanitizer_cov_module_init=uninstrumented fun:__sanitizer_cov_module_init=discard fun:__sanitizer_cov_with_check=uninstrumented fun:__sanitizer_cov_with_check=discard -fun:__sanitizer_cov_indir_call16=uninstrumented -fun:__sanitizer_cov_indir_call16=discard -fun:__sanitizer_cov_indir_call16=uninstrumented -fun:__sanitizer_cov_indir_call16=discard -fun:__sanitizer_reset_coverage=uninstrumented -fun:__sanitizer_reset_coverage=discard fun:__sanitizer_set_death_callback=uninstrumented fun:__sanitizer_set_death_callback=discard -fun:__sanitizer_get_coverage_guards=uninstrumented -fun:__sanitizer_get_coverage_guards=discard -fun:__sanitizer_get_number_of_counters=uninstrumented -fun:__sanitizer_get_number_of_counters=discard -fun:__sanitizer_update_counter_bitset_and_clear_counters=uninstrumented -fun:__sanitizer_update_counter_bitset_and_clear_counters=discard -fun:__sanitizer_get_total_unique_coverage=uninstrumented -fun:__sanitizer_get_total_unique_coverage=discard fun:__sanitizer_get_total_unique_coverage=uninstrumented fun:__sanitizer_get_total_unique_coverage=discard fun:__sanitizer_update_counter_bitset_and_clear_counters=uninstrumented fun:__sanitizer_update_counter_bitset_and_clear_counters=discard # Ignores the dfsan wrappers. fun:__dfsw_*=uninstrumented fun:__dfsw_*=discard # Don't add extra parameters to the Fuzzer callback. fun:LLVMFuzzerTestOneInput=uninstrumented Index: vendor/compiler-rt/dist/lib/lsan/lsan_common.cc =================================================================== --- vendor/compiler-rt/dist/lib/lsan/lsan_common.cc (revision 317221) +++ vendor/compiler-rt/dist/lib/lsan/lsan_common.cc (revision 317222) @@ -1,785 +1,863 @@ //=-- lsan_common.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. // Implementation of common leak checking functionality. // //===----------------------------------------------------------------------===// #include "lsan_common.h" #include "sanitizer_common/sanitizer_common.h" #include "sanitizer_common/sanitizer_flags.h" #include "sanitizer_common/sanitizer_flag_parser.h" #include "sanitizer_common/sanitizer_placement_new.h" #include "sanitizer_common/sanitizer_procmaps.h" #include "sanitizer_common/sanitizer_stackdepot.h" #include "sanitizer_common/sanitizer_stacktrace.h" #include "sanitizer_common/sanitizer_suppressions.h" #include "sanitizer_common/sanitizer_report_decorator.h" #include "sanitizer_common/sanitizer_tls_get_addr.h" #if CAN_SANITIZE_LEAKS namespace __lsan { // This mutex is used to prevent races between DoLeakCheck and IgnoreObject, and // also to protect the global list of root regions. BlockingMutex global_mutex(LINKER_INITIALIZED); Flags lsan_flags; void DisableCounterUnderflow() { if (common_flags()->detect_leaks) { Report("Unmatched call to __lsan_enable().\n"); Die(); } } void Flags::SetDefaults() { #define LSAN_FLAG(Type, Name, DefaultValue, Description) Name = DefaultValue; #include "lsan_flags.inc" #undef LSAN_FLAG } void RegisterLsanFlags(FlagParser *parser, Flags *f) { #define LSAN_FLAG(Type, Name, DefaultValue, Description) \ RegisterFlag(parser, #Name, Description, &f->Name); #include "lsan_flags.inc" #undef LSAN_FLAG } #define LOG_POINTERS(...) \ do { \ if (flags()->log_pointers) Report(__VA_ARGS__); \ } while (0); #define LOG_THREADS(...) \ do { \ if (flags()->log_threads) Report(__VA_ARGS__); \ } while (0); ALIGNED(64) static char suppression_placeholder[sizeof(SuppressionContext)]; static SuppressionContext *suppression_ctx = nullptr; static const char kSuppressionLeak[] = "leak"; static const char *kSuppressionTypes[] = { kSuppressionLeak }; +static const char kStdSuppressions[] = +#if SANITIZER_SUPPRESS_LEAK_ON_PTHREAD_EXIT + // The actual string allocation happens here (for more details refer to the + // SANITIZER_SUPPRESS_LEAK_ON_PTHREAD_EXIT definition). + "leak:*_dl_map_object_deps*"; +#else + ""; +#endif // SANITIZER_SUPPRESS_LEAK_ON_PTHREAD_EXIT void InitializeSuppressions() { CHECK_EQ(nullptr, suppression_ctx); suppression_ctx = new (suppression_placeholder) // NOLINT SuppressionContext(kSuppressionTypes, ARRAY_SIZE(kSuppressionTypes)); suppression_ctx->ParseFromFile(flags()->suppressions); if (&__lsan_default_suppressions) suppression_ctx->Parse(__lsan_default_suppressions()); + suppression_ctx->Parse(kStdSuppressions); } static SuppressionContext *GetSuppressionContext() { CHECK(suppression_ctx); return suppression_ctx; } -struct RootRegion { - const void *begin; - uptr size; -}; +static InternalMmapVector *root_regions; -InternalMmapVector *root_regions; +InternalMmapVector const *GetRootRegions() { return root_regions; } void InitializeRootRegions() { CHECK(!root_regions); ALIGNED(64) static char placeholder[sizeof(InternalMmapVector)]; root_regions = new(placeholder) InternalMmapVector(1); } void InitCommonLsan() { InitializeRootRegions(); if (common_flags()->detect_leaks) { // Initialization which can fail or print warnings should only be done if // LSan is actually enabled. InitializeSuppressions(); InitializePlatformSpecificModules(); } } class Decorator: public __sanitizer::SanitizerCommonDecorator { public: Decorator() : SanitizerCommonDecorator() { } const char *Error() { return Red(); } const char *Leak() { return Blue(); } const char *End() { return Default(); } }; static inline bool CanBeAHeapPointer(uptr p) { // Since our heap is located in mmap-ed memory, we can assume a sensible lower // bound on heap addresses. const uptr kMinAddress = 4 * 4096; if (p < kMinAddress) return false; #if defined(__x86_64__) // Accept only canonical form user-space addresses. return ((p >> 47) == 0); #elif defined(__mips64) return ((p >> 40) == 0); #elif defined(__aarch64__) unsigned runtimeVMA = (MostSignificantSetBitIndex(GET_CURRENT_FRAME()) + 1); return ((p >> runtimeVMA) == 0); #else return true; #endif } // Scans the memory range, looking for byte patterns that point into allocator // chunks. Marks those chunks with |tag| and adds them to |frontier|. // There are two usage modes for this function: finding reachable chunks // (|tag| = kReachable) and finding indirectly leaked chunks // (|tag| = kIndirectlyLeaked). In the second case, there's no flood fill, // so |frontier| = 0. void ScanRangeForPointers(uptr begin, uptr end, Frontier *frontier, const char *region_type, ChunkTag tag) { CHECK(tag == kReachable || tag == kIndirectlyLeaked); const uptr alignment = flags()->pointer_alignment(); LOG_POINTERS("Scanning %s range %p-%p.\n", region_type, begin, end); uptr pp = begin; if (pp % alignment) pp = pp + alignment - pp % alignment; for (; pp + sizeof(void *) <= end; pp += alignment) { // NOLINT void *p = *reinterpret_cast(pp); if (!CanBeAHeapPointer(reinterpret_cast(p))) continue; uptr chunk = PointsIntoChunk(p); if (!chunk) continue; // Pointers to self don't count. This matters when tag == kIndirectlyLeaked. if (chunk == begin) continue; LsanMetadata m(chunk); if (m.tag() == kReachable || m.tag() == kIgnored) continue; // Do this check relatively late so we can log only the interesting cases. if (!flags()->use_poisoned && WordIsPoisoned(pp)) { LOG_POINTERS( "%p is poisoned: ignoring %p pointing into chunk %p-%p of size " "%zu.\n", pp, p, chunk, chunk + m.requested_size(), m.requested_size()); continue; } m.set_tag(tag); LOG_POINTERS("%p: found %p pointing into chunk %p-%p of size %zu.\n", pp, p, chunk, chunk + m.requested_size(), m.requested_size()); if (frontier) frontier->push_back(chunk); } } // Scans a global range for pointers void ScanGlobalRange(uptr begin, uptr end, Frontier *frontier) { uptr allocator_begin = 0, allocator_end = 0; GetAllocatorGlobalRange(&allocator_begin, &allocator_end); if (begin <= allocator_begin && allocator_begin < end) { CHECK_LE(allocator_begin, allocator_end); CHECK_LE(allocator_end, end); if (begin < allocator_begin) ScanRangeForPointers(begin, allocator_begin, frontier, "GLOBAL", kReachable); if (allocator_end < end) ScanRangeForPointers(allocator_end, end, frontier, "GLOBAL", kReachable); } else { ScanRangeForPointers(begin, end, frontier, "GLOBAL", kReachable); } } void ForEachExtraStackRangeCb(uptr begin, uptr end, void* arg) { Frontier *frontier = reinterpret_cast(arg); ScanRangeForPointers(begin, end, frontier, "FAKE STACK", kReachable); } // Scans thread data (stacks and TLS) for heap pointers. static void ProcessThreads(SuspendedThreadsList const &suspended_threads, Frontier *frontier) { - InternalScopedBuffer registers(SuspendedThreadsList::RegisterCount()); + InternalScopedBuffer registers(suspended_threads.RegisterCount()); uptr registers_begin = reinterpret_cast(registers.data()); uptr registers_end = registers_begin + registers.size(); - for (uptr i = 0; i < suspended_threads.thread_count(); i++) { - uptr os_id = static_cast(suspended_threads.GetThreadID(i)); + for (uptr i = 0; i < suspended_threads.ThreadCount(); i++) { + tid_t os_id = static_cast(suspended_threads.GetThreadID(i)); LOG_THREADS("Processing thread %d.\n", os_id); uptr stack_begin, stack_end, tls_begin, tls_end, cache_begin, cache_end; DTLS *dtls; bool thread_found = GetThreadRangesLocked(os_id, &stack_begin, &stack_end, &tls_begin, &tls_end, &cache_begin, &cache_end, &dtls); if (!thread_found) { // If a thread can't be found in the thread registry, it's probably in the // process of destruction. Log this event and move on. LOG_THREADS("Thread %d not found in registry.\n", os_id); continue; } uptr sp; PtraceRegistersStatus have_registers = suspended_threads.GetRegistersAndSP(i, registers.data(), &sp); if (have_registers != REGISTERS_AVAILABLE) { Report("Unable to get registers from thread %d.\n", os_id); // If unable to get SP, consider the entire stack to be reachable unless // GetRegistersAndSP failed with ESRCH. if (have_registers == REGISTERS_UNAVAILABLE_FATAL) continue; sp = stack_begin; } if (flags()->use_registers && have_registers) ScanRangeForPointers(registers_begin, registers_end, frontier, "REGISTERS", kReachable); if (flags()->use_stacks) { LOG_THREADS("Stack at %p-%p (SP = %p).\n", stack_begin, stack_end, sp); if (sp < stack_begin || sp >= stack_end) { // SP is outside the recorded stack range (e.g. the thread is running a // signal handler on alternate stack, or swapcontext was used). // Again, consider the entire stack range to be reachable. LOG_THREADS("WARNING: stack pointer not in stack range.\n"); uptr page_size = GetPageSizeCached(); int skipped = 0; while (stack_begin < stack_end && !IsAccessibleMemoryRange(stack_begin, 1)) { skipped++; stack_begin += page_size; } LOG_THREADS("Skipped %d guard page(s) to obtain stack %p-%p.\n", skipped, stack_begin, stack_end); } else { // Shrink the stack range to ignore out-of-scope values. stack_begin = sp; } ScanRangeForPointers(stack_begin, stack_end, frontier, "STACK", kReachable); ForEachExtraStackRange(os_id, ForEachExtraStackRangeCb, frontier); } if (flags()->use_tls) { LOG_THREADS("TLS at %p-%p.\n", tls_begin, tls_end); if (cache_begin == cache_end) { ScanRangeForPointers(tls_begin, tls_end, frontier, "TLS", kReachable); } else { // Because LSan should not be loaded with dlopen(), we can assume // that allocator cache will be part of static TLS image. CHECK_LE(tls_begin, cache_begin); CHECK_GE(tls_end, cache_end); if (tls_begin < cache_begin) ScanRangeForPointers(tls_begin, cache_begin, frontier, "TLS", kReachable); if (tls_end > cache_end) ScanRangeForPointers(cache_end, tls_end, frontier, "TLS", kReachable); } if (dtls && !DTLSInDestruction(dtls)) { for (uptr j = 0; j < dtls->dtv_size; ++j) { uptr dtls_beg = dtls->dtv[j].beg; uptr dtls_end = dtls_beg + dtls->dtv[j].size; if (dtls_beg < dtls_end) { LOG_THREADS("DTLS %zu at %p-%p.\n", j, dtls_beg, dtls_end); ScanRangeForPointers(dtls_beg, dtls_end, frontier, "DTLS", kReachable); } } } else { // We are handling a thread with DTLS under destruction. Log about // this and continue. LOG_THREADS("Thread %d has DTLS under destruction.\n", os_id); } } } } -static void ProcessRootRegion(Frontier *frontier, uptr root_begin, - uptr root_end) { - MemoryMappingLayout proc_maps(/*cache_enabled*/true); +void ScanRootRegion(Frontier *frontier, const RootRegion &root_region, + uptr region_begin, uptr region_end, uptr prot) { + uptr intersection_begin = Max(root_region.begin, region_begin); + uptr intersection_end = Min(region_end, root_region.begin + root_region.size); + if (intersection_begin >= intersection_end) return; + bool is_readable = prot & MemoryMappingLayout::kProtectionRead; + LOG_POINTERS("Root region %p-%p intersects with mapped region %p-%p (%s)\n", + root_region.begin, root_region.begin + root_region.size, + region_begin, region_end, + is_readable ? "readable" : "unreadable"); + if (is_readable) + ScanRangeForPointers(intersection_begin, intersection_end, frontier, "ROOT", + kReachable); +} + +static void ProcessRootRegion(Frontier *frontier, + const RootRegion &root_region) { + MemoryMappingLayout proc_maps(/*cache_enabled*/ true); uptr begin, end, prot; while (proc_maps.Next(&begin, &end, /*offset*/ nullptr, /*filename*/ nullptr, /*filename_size*/ 0, &prot)) { - uptr intersection_begin = Max(root_begin, begin); - uptr intersection_end = Min(end, root_end); - if (intersection_begin >= intersection_end) continue; - bool is_readable = prot & MemoryMappingLayout::kProtectionRead; - LOG_POINTERS("Root region %p-%p intersects with mapped region %p-%p (%s)\n", - root_begin, root_end, begin, end, - is_readable ? "readable" : "unreadable"); - if (is_readable) - ScanRangeForPointers(intersection_begin, intersection_end, frontier, - "ROOT", kReachable); + ScanRootRegion(frontier, root_region, begin, end, prot); } } // Scans root regions for heap pointers. static void ProcessRootRegions(Frontier *frontier) { if (!flags()->use_root_regions) return; CHECK(root_regions); for (uptr i = 0; i < root_regions->size(); i++) { - RootRegion region = (*root_regions)[i]; - uptr begin_addr = reinterpret_cast(region.begin); - ProcessRootRegion(frontier, begin_addr, begin_addr + region.size); + ProcessRootRegion(frontier, (*root_regions)[i]); } } static void FloodFillTag(Frontier *frontier, ChunkTag tag) { while (frontier->size()) { uptr next_chunk = frontier->back(); frontier->pop_back(); LsanMetadata m(next_chunk); ScanRangeForPointers(next_chunk, next_chunk + m.requested_size(), frontier, "HEAP", tag); } } // ForEachChunk callback. If the chunk is marked as leaked, marks all chunks // which are reachable from it as indirectly leaked. static void MarkIndirectlyLeakedCb(uptr chunk, void *arg) { chunk = GetUserBegin(chunk); LsanMetadata m(chunk); if (m.allocated() && m.tag() != kReachable) { ScanRangeForPointers(chunk, chunk + m.requested_size(), /* frontier */ nullptr, "HEAP", kIndirectlyLeaked); } } // ForEachChunk callback. If chunk is marked as ignored, adds its address to // frontier. static void CollectIgnoredCb(uptr chunk, void *arg) { CHECK(arg); chunk = GetUserBegin(chunk); LsanMetadata m(chunk); if (m.allocated() && m.tag() == kIgnored) { LOG_POINTERS("Ignored: chunk %p-%p of size %zu.\n", chunk, chunk + m.requested_size(), m.requested_size()); reinterpret_cast(arg)->push_back(chunk); } } +static uptr GetCallerPC(u32 stack_id, StackDepotReverseMap *map) { + CHECK(stack_id); + StackTrace stack = map->Get(stack_id); + // The top frame is our malloc/calloc/etc. The next frame is the caller. + if (stack.size >= 2) + return stack.trace[1]; + return 0; +} + +struct InvalidPCParam { + Frontier *frontier; + StackDepotReverseMap *stack_depot_reverse_map; + bool skip_linker_allocations; +}; + +// ForEachChunk callback. If the caller pc is invalid or is within the linker, +// mark as reachable. Called by ProcessPlatformSpecificAllocations. +static void MarkInvalidPCCb(uptr chunk, void *arg) { + CHECK(arg); + InvalidPCParam *param = reinterpret_cast(arg); + chunk = GetUserBegin(chunk); + LsanMetadata m(chunk); + if (m.allocated() && m.tag() != kReachable && m.tag() != kIgnored) { + u32 stack_id = m.stack_trace_id(); + uptr caller_pc = 0; + if (stack_id > 0) + caller_pc = GetCallerPC(stack_id, param->stack_depot_reverse_map); + // If caller_pc is unknown, this chunk may be allocated in a coroutine. Mark + // it as reachable, as we can't properly report its allocation stack anyway. + if (caller_pc == 0 || (param->skip_linker_allocations && + GetLinker()->containsAddress(caller_pc))) { + m.set_tag(kReachable); + param->frontier->push_back(chunk); + } + } +} + +// On Linux, handles dynamically allocated TLS blocks by treating all chunks +// allocated from ld-linux.so as reachable. +// Dynamic TLS blocks contain the TLS variables of dynamically loaded modules. +// They are allocated with a __libc_memalign() call in allocate_and_init() +// (elf/dl-tls.c). Glibc won't tell us the address ranges occupied by those +// blocks, but we can make sure they come from our own allocator by intercepting +// __libc_memalign(). On top of that, there is no easy way to reach them. Their +// addresses are stored in a dynamically allocated array (the DTV) which is +// referenced from the static TLS. Unfortunately, we can't just rely on the DTV +// being reachable from the static TLS, and the dynamic TLS being reachable from +// the DTV. This is because the initial DTV is allocated before our interception +// mechanism kicks in, and thus we don't recognize it as allocated memory. We +// can't special-case it either, since we don't know its size. +// Our solution is to include in the root set all allocations made from +// ld-linux.so (which is where allocate_and_init() is implemented). This is +// guaranteed to include all dynamic TLS blocks (and possibly other allocations +// which we don't care about). +// On all other platforms, this simply checks to ensure that the caller pc is +// valid before reporting chunks as leaked. +void ProcessPC(Frontier *frontier) { + StackDepotReverseMap stack_depot_reverse_map; + InvalidPCParam arg; + arg.frontier = frontier; + arg.stack_depot_reverse_map = &stack_depot_reverse_map; + arg.skip_linker_allocations = + flags()->use_tls && flags()->use_ld_allocations && GetLinker() != nullptr; + ForEachChunk(MarkInvalidPCCb, &arg); +} + // Sets the appropriate tag on each chunk. static void ClassifyAllChunks(SuspendedThreadsList const &suspended_threads) { // Holds the flood fill frontier. Frontier frontier(1); ForEachChunk(CollectIgnoredCb, &frontier); ProcessGlobalRegions(&frontier); ProcessThreads(suspended_threads, &frontier); ProcessRootRegions(&frontier); FloodFillTag(&frontier, kReachable); + CHECK_EQ(0, frontier.size()); + ProcessPC(&frontier); + // The check here is relatively expensive, so we do this in a separate flood // fill. That way we can skip the check for chunks that are reachable // otherwise. LOG_POINTERS("Processing platform-specific allocations.\n"); - CHECK_EQ(0, frontier.size()); ProcessPlatformSpecificAllocations(&frontier); FloodFillTag(&frontier, kReachable); // Iterate over leaked chunks and mark those that are reachable from other // leaked chunks. LOG_POINTERS("Scanning leaked chunks.\n"); ForEachChunk(MarkIndirectlyLeakedCb, nullptr); } // ForEachChunk callback. Resets the tags to pre-leak-check state. static void ResetTagsCb(uptr chunk, void *arg) { (void)arg; chunk = GetUserBegin(chunk); LsanMetadata m(chunk); if (m.allocated() && m.tag() != kIgnored) m.set_tag(kDirectlyLeaked); } static void PrintStackTraceById(u32 stack_trace_id) { CHECK(stack_trace_id); StackDepotGet(stack_trace_id).Print(); } // ForEachChunk callback. Aggregates information about unreachable chunks into // a LeakReport. static void CollectLeaksCb(uptr chunk, void *arg) { CHECK(arg); LeakReport *leak_report = reinterpret_cast(arg); chunk = GetUserBegin(chunk); LsanMetadata m(chunk); if (!m.allocated()) return; if (m.tag() == kDirectlyLeaked || m.tag() == kIndirectlyLeaked) { u32 resolution = flags()->resolution; u32 stack_trace_id = 0; if (resolution > 0) { StackTrace stack = StackDepotGet(m.stack_trace_id()); stack.size = Min(stack.size, resolution); stack_trace_id = StackDepotPut(stack); } else { stack_trace_id = m.stack_trace_id(); } leak_report->AddLeakedChunk(chunk, stack_trace_id, m.requested_size(), m.tag()); } } static void PrintMatchedSuppressions() { InternalMmapVector matched(1); GetSuppressionContext()->GetMatched(&matched); if (!matched.size()) return; const char *line = "-----------------------------------------------------"; Printf("%s\n", line); Printf("Suppressions used:\n"); Printf(" count bytes template\n"); for (uptr i = 0; i < matched.size(); i++) Printf("%7zu %10zu %s\n", static_cast(atomic_load_relaxed( &matched[i]->hit_count)), matched[i]->weight, matched[i]->templ); Printf("%s\n\n", line); } struct CheckForLeaksParam { bool success; LeakReport leak_report; }; static void CheckForLeaksCallback(const SuspendedThreadsList &suspended_threads, void *arg) { CheckForLeaksParam *param = reinterpret_cast(arg); CHECK(param); CHECK(!param->success); ClassifyAllChunks(suspended_threads); ForEachChunk(CollectLeaksCb, ¶m->leak_report); // Clean up for subsequent leak checks. This assumes we did not overwrite any // kIgnored tags. ForEachChunk(ResetTagsCb, nullptr); param->success = true; } static bool CheckForLeaks() { if (&__lsan_is_turned_off && __lsan_is_turned_off()) return false; EnsureMainThreadIDIsCorrect(); CheckForLeaksParam param; param.success = false; LockThreadRegistry(); LockAllocator(); DoStopTheWorld(CheckForLeaksCallback, ¶m); UnlockAllocator(); UnlockThreadRegistry(); if (!param.success) { Report("LeakSanitizer has encountered a fatal error.\n"); Report( "HINT: For debugging, try setting environment variable " "LSAN_OPTIONS=verbosity=1:log_threads=1\n"); Report( "HINT: LeakSanitizer does not work under ptrace (strace, gdb, etc)\n"); Die(); } param.leak_report.ApplySuppressions(); uptr unsuppressed_count = param.leak_report.UnsuppressedLeakCount(); if (unsuppressed_count > 0) { Decorator d; Printf("\n" "=================================================================" "\n"); Printf("%s", d.Error()); Report("ERROR: LeakSanitizer: detected memory leaks\n"); Printf("%s", d.End()); param.leak_report.ReportTopLeaks(flags()->max_leaks); } if (common_flags()->print_suppressions) PrintMatchedSuppressions(); if (unsuppressed_count > 0) { param.leak_report.PrintSummary(); return true; } return false; } void DoLeakCheck() { BlockingMutexLock l(&global_mutex); static bool already_done; if (already_done) return; already_done = true; bool have_leaks = CheckForLeaks(); if (!have_leaks) { return; } if (common_flags()->exitcode) { Die(); } } static int DoRecoverableLeakCheck() { BlockingMutexLock l(&global_mutex); bool have_leaks = CheckForLeaks(); return have_leaks ? 1 : 0; } static Suppression *GetSuppressionForAddr(uptr addr) { Suppression *s = nullptr; // Suppress by module name. SuppressionContext *suppressions = GetSuppressionContext(); if (const char *module_name = Symbolizer::GetOrInit()->GetModuleNameForPc(addr)) if (suppressions->Match(module_name, kSuppressionLeak, &s)) return s; // Suppress by file or function name. SymbolizedStack *frames = Symbolizer::GetOrInit()->SymbolizePC(addr); for (SymbolizedStack *cur = frames; cur; cur = cur->next) { if (suppressions->Match(cur->info.function, kSuppressionLeak, &s) || suppressions->Match(cur->info.file, kSuppressionLeak, &s)) { break; } } frames->ClearAll(); return s; } static Suppression *GetSuppressionForStack(u32 stack_trace_id) { StackTrace stack = StackDepotGet(stack_trace_id); for (uptr i = 0; i < stack.size; i++) { Suppression *s = GetSuppressionForAddr( StackTrace::GetPreviousInstructionPc(stack.trace[i])); if (s) return s; } return nullptr; } ///// LeakReport implementation. ///// // A hard limit on the number of distinct leaks, to avoid quadratic complexity // in LeakReport::AddLeakedChunk(). We don't expect to ever see this many leaks // in real-world applications. // FIXME: Get rid of this limit by changing the implementation of LeakReport to // use a hash table. const uptr kMaxLeaksConsidered = 5000; void LeakReport::AddLeakedChunk(uptr chunk, u32 stack_trace_id, uptr leaked_size, ChunkTag tag) { CHECK(tag == kDirectlyLeaked || tag == kIndirectlyLeaked); bool is_directly_leaked = (tag == kDirectlyLeaked); uptr i; for (i = 0; i < leaks_.size(); i++) { if (leaks_[i].stack_trace_id == stack_trace_id && leaks_[i].is_directly_leaked == is_directly_leaked) { leaks_[i].hit_count++; leaks_[i].total_size += leaked_size; break; } } if (i == leaks_.size()) { if (leaks_.size() == kMaxLeaksConsidered) return; Leak leak = { next_id_++, /* hit_count */ 1, leaked_size, stack_trace_id, is_directly_leaked, /* is_suppressed */ false }; leaks_.push_back(leak); } if (flags()->report_objects) { LeakedObject obj = {leaks_[i].id, chunk, leaked_size}; leaked_objects_.push_back(obj); } } static bool LeakComparator(const Leak &leak1, const Leak &leak2) { if (leak1.is_directly_leaked == leak2.is_directly_leaked) return leak1.total_size > leak2.total_size; else return leak1.is_directly_leaked; } void LeakReport::ReportTopLeaks(uptr num_leaks_to_report) { CHECK(leaks_.size() <= kMaxLeaksConsidered); Printf("\n"); if (leaks_.size() == kMaxLeaksConsidered) Printf("Too many leaks! Only the first %zu leaks encountered will be " "reported.\n", kMaxLeaksConsidered); uptr unsuppressed_count = UnsuppressedLeakCount(); if (num_leaks_to_report > 0 && num_leaks_to_report < unsuppressed_count) Printf("The %zu top leak(s):\n", num_leaks_to_report); InternalSort(&leaks_, leaks_.size(), LeakComparator); uptr leaks_reported = 0; for (uptr i = 0; i < leaks_.size(); i++) { if (leaks_[i].is_suppressed) continue; PrintReportForLeak(i); leaks_reported++; if (leaks_reported == num_leaks_to_report) break; } if (leaks_reported < unsuppressed_count) { uptr remaining = unsuppressed_count - leaks_reported; Printf("Omitting %zu more leak(s).\n", remaining); } } void LeakReport::PrintReportForLeak(uptr index) { Decorator d; Printf("%s", d.Leak()); Printf("%s leak of %zu byte(s) in %zu object(s) allocated from:\n", leaks_[index].is_directly_leaked ? "Direct" : "Indirect", leaks_[index].total_size, leaks_[index].hit_count); Printf("%s", d.End()); PrintStackTraceById(leaks_[index].stack_trace_id); if (flags()->report_objects) { Printf("Objects leaked above:\n"); PrintLeakedObjectsForLeak(index); Printf("\n"); } } void LeakReport::PrintLeakedObjectsForLeak(uptr index) { u32 leak_id = leaks_[index].id; for (uptr j = 0; j < leaked_objects_.size(); j++) { if (leaked_objects_[j].leak_id == leak_id) Printf("%p (%zu bytes)\n", leaked_objects_[j].addr, leaked_objects_[j].size); } } void LeakReport::PrintSummary() { CHECK(leaks_.size() <= kMaxLeaksConsidered); uptr bytes = 0, allocations = 0; for (uptr i = 0; i < leaks_.size(); i++) { if (leaks_[i].is_suppressed) continue; bytes += leaks_[i].total_size; allocations += leaks_[i].hit_count; } InternalScopedString summary(kMaxSummaryLength); summary.append("%zu byte(s) leaked in %zu allocation(s).", bytes, allocations); ReportErrorSummary(summary.data()); } void LeakReport::ApplySuppressions() { for (uptr i = 0; i < leaks_.size(); i++) { Suppression *s = GetSuppressionForStack(leaks_[i].stack_trace_id); if (s) { s->weight += leaks_[i].total_size; atomic_store_relaxed(&s->hit_count, atomic_load_relaxed(&s->hit_count) + leaks_[i].hit_count); leaks_[i].is_suppressed = true; } } } uptr LeakReport::UnsuppressedLeakCount() { uptr result = 0; for (uptr i = 0; i < leaks_.size(); i++) if (!leaks_[i].is_suppressed) result++; return result; } } // namespace __lsan #else // CAN_SANITIZE_LEAKS namespace __lsan { void InitCommonLsan() { } void DoLeakCheck() { } void DisableInThisThread() { } void EnableInThisThread() { } } #endif // CAN_SANITIZE_LEAKS using namespace __lsan; // NOLINT extern "C" { SANITIZER_INTERFACE_ATTRIBUTE void __lsan_ignore_object(const void *p) { #if CAN_SANITIZE_LEAKS if (!common_flags()->detect_leaks) return; // Cannot use PointsIntoChunk or LsanMetadata here, since the allocator is not // locked. BlockingMutexLock l(&global_mutex); IgnoreObjectResult res = IgnoreObjectLocked(p); if (res == kIgnoreObjectInvalid) VReport(1, "__lsan_ignore_object(): no heap object found at %p", p); if (res == kIgnoreObjectAlreadyIgnored) VReport(1, "__lsan_ignore_object(): " "heap object at %p is already being ignored\n", p); if (res == kIgnoreObjectSuccess) VReport(1, "__lsan_ignore_object(): ignoring heap object at %p\n", p); #endif // CAN_SANITIZE_LEAKS } SANITIZER_INTERFACE_ATTRIBUTE void __lsan_register_root_region(const void *begin, uptr size) { #if CAN_SANITIZE_LEAKS BlockingMutexLock l(&global_mutex); CHECK(root_regions); - RootRegion region = {begin, size}; + RootRegion region = {reinterpret_cast(begin), size}; root_regions->push_back(region); VReport(1, "Registered root region at %p of size %llu\n", begin, size); #endif // CAN_SANITIZE_LEAKS } SANITIZER_INTERFACE_ATTRIBUTE void __lsan_unregister_root_region(const void *begin, uptr size) { #if CAN_SANITIZE_LEAKS BlockingMutexLock l(&global_mutex); CHECK(root_regions); bool removed = false; for (uptr i = 0; i < root_regions->size(); i++) { RootRegion region = (*root_regions)[i]; - if (region.begin == begin && region.size == size) { + if (region.begin == reinterpret_cast(begin) && region.size == size) { removed = true; uptr last_index = root_regions->size() - 1; (*root_regions)[i] = (*root_regions)[last_index]; root_regions->pop_back(); VReport(1, "Unregistered root region at %p of size %llu\n", begin, size); break; } } if (!removed) { Report( "__lsan_unregister_root_region(): region at %p of size %llu has not " "been registered.\n", begin, size); Die(); } #endif // CAN_SANITIZE_LEAKS } SANITIZER_INTERFACE_ATTRIBUTE void __lsan_disable() { #if CAN_SANITIZE_LEAKS __lsan::DisableInThisThread(); #endif } SANITIZER_INTERFACE_ATTRIBUTE void __lsan_enable() { #if CAN_SANITIZE_LEAKS __lsan::EnableInThisThread(); #endif } SANITIZER_INTERFACE_ATTRIBUTE void __lsan_do_leak_check() { #if CAN_SANITIZE_LEAKS if (common_flags()->detect_leaks) __lsan::DoLeakCheck(); #endif // CAN_SANITIZE_LEAKS } SANITIZER_INTERFACE_ATTRIBUTE int __lsan_do_recoverable_leak_check() { #if CAN_SANITIZE_LEAKS if (common_flags()->detect_leaks) return __lsan::DoRecoverableLeakCheck(); #endif // CAN_SANITIZE_LEAKS return 0; } #if !SANITIZER_SUPPORTS_WEAK_HOOKS SANITIZER_INTERFACE_ATTRIBUTE SANITIZER_WEAK_ATTRIBUTE int __lsan_is_turned_off() { return 0; } SANITIZER_INTERFACE_ATTRIBUTE SANITIZER_WEAK_ATTRIBUTE const char *__lsan_default_suppressions() { return ""; } #endif } // extern "C" Index: vendor/compiler-rt/dist/lib/lsan/lsan_common.h =================================================================== --- vendor/compiler-rt/dist/lib/lsan/lsan_common.h (revision 317221) +++ vendor/compiler-rt/dist/lib/lsan/lsan_common.h (revision 317222) @@ -1,239 +1,252 @@ //=-- lsan_common.h -------------------------------------------------------===// // // 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. // Private LSan header. // //===----------------------------------------------------------------------===// #ifndef LSAN_COMMON_H #define LSAN_COMMON_H #include "sanitizer_common/sanitizer_allocator.h" #include "sanitizer_common/sanitizer_common.h" #include "sanitizer_common/sanitizer_internal_defs.h" #include "sanitizer_common/sanitizer_platform.h" #include "sanitizer_common/sanitizer_stoptheworld.h" #include "sanitizer_common/sanitizer_symbolizer.h" // LeakSanitizer relies on some Glibc's internals (e.g. TLS machinery) thus // supported for Linux only. Also, LSan doesn't like 32 bit architectures // because of "small" (4 bytes) pointer size that leads to high false negative // ratio on large leaks. But we still want to have it for some 32 bit arches // (e.g. x86), see https://github.com/google/sanitizers/issues/403. // To enable LeakSanitizer on new architecture, one need to implement // internal_clone function as well as (probably) adjust TLS machinery for // new architecture inside sanitizer library. #if (SANITIZER_LINUX && !SANITIZER_ANDROID || SANITIZER_MAC) && \ (SANITIZER_WORDSIZE == 64) && \ (defined(__x86_64__) || defined(__mips64) || defined(__aarch64__)) #define CAN_SANITIZE_LEAKS 1 #elif defined(__i386__) && \ (SANITIZER_LINUX && !SANITIZER_ANDROID || SANITIZER_MAC) #define CAN_SANITIZE_LEAKS 1 #elif defined(__arm__) && \ SANITIZER_LINUX && !SANITIZER_ANDROID #define CAN_SANITIZE_LEAKS 1 #else #define CAN_SANITIZE_LEAKS 0 #endif namespace __sanitizer { class FlagParser; struct DTLS; } namespace __lsan { // Chunk tags. enum ChunkTag { kDirectlyLeaked = 0, // default kIndirectlyLeaked = 1, kReachable = 2, kIgnored = 3 }; const u32 kInvalidTid = (u32) -1; struct Flags { #define LSAN_FLAG(Type, Name, DefaultValue, Description) Type Name; #include "lsan_flags.inc" #undef LSAN_FLAG void SetDefaults(); uptr pointer_alignment() const { return use_unaligned ? 1 : sizeof(uptr); } }; extern Flags lsan_flags; inline Flags *flags() { return &lsan_flags; } void RegisterLsanFlags(FlagParser *parser, Flags *f); struct Leak { u32 id; uptr hit_count; uptr total_size; u32 stack_trace_id; bool is_directly_leaked; bool is_suppressed; }; struct LeakedObject { u32 leak_id; uptr addr; uptr size; }; // Aggregates leaks by stack trace prefix. class LeakReport { public: LeakReport() : next_id_(0), leaks_(1), leaked_objects_(1) {} void AddLeakedChunk(uptr chunk, u32 stack_trace_id, uptr leaked_size, ChunkTag tag); void ReportTopLeaks(uptr max_leaks); void PrintSummary(); void ApplySuppressions(); uptr UnsuppressedLeakCount(); private: void PrintReportForLeak(uptr index); void PrintLeakedObjectsForLeak(uptr index); u32 next_id_; InternalMmapVector leaks_; InternalMmapVector leaked_objects_; }; typedef InternalMmapVector Frontier; // Platform-specific functions. void InitializePlatformSpecificModules(); void ProcessGlobalRegions(Frontier *frontier); void ProcessPlatformSpecificAllocations(Frontier *frontier); + +struct RootRegion { + uptr begin; + uptr size; +}; + +InternalMmapVector const *GetRootRegions(); +void ScanRootRegion(Frontier *frontier, RootRegion const ®ion, + uptr region_begin, uptr region_end, uptr prot); // Run stoptheworld while holding any platform-specific locks. void DoStopTheWorld(StopTheWorldCallback callback, void* argument); void ScanRangeForPointers(uptr begin, uptr end, Frontier *frontier, const char *region_type, ChunkTag tag); void ScanGlobalRange(uptr begin, uptr end, Frontier *frontier); enum IgnoreObjectResult { kIgnoreObjectSuccess, kIgnoreObjectAlreadyIgnored, kIgnoreObjectInvalid }; // Functions called from the parent tool. void InitCommonLsan(); void DoLeakCheck(); void DisableCounterUnderflow(); bool DisabledInThisThread(); // Used to implement __lsan::ScopedDisabler. void DisableInThisThread(); void EnableInThisThread(); // Can be used to ignore memory allocated by an intercepted // function. struct ScopedInterceptorDisabler { ScopedInterceptorDisabler() { DisableInThisThread(); } ~ScopedInterceptorDisabler() { EnableInThisThread(); } }; // According to Itanium C++ ABI array cookie is a one word containing // size of allocated array. static inline bool IsItaniumABIArrayCookie(uptr chunk_beg, uptr chunk_size, uptr addr) { return chunk_size == sizeof(uptr) && chunk_beg + chunk_size == addr && *reinterpret_cast(chunk_beg) == 0; } // According to ARM C++ ABI array cookie consists of two words: // struct array_cookie { // std::size_t element_size; // element_size != 0 // std::size_t element_count; // }; static inline bool IsARMABIArrayCookie(uptr chunk_beg, uptr chunk_size, uptr addr) { return chunk_size == 2 * sizeof(uptr) && chunk_beg + chunk_size == addr && *reinterpret_cast(chunk_beg + sizeof(uptr)) == 0; } // Special case for "new T[0]" where T is a type with DTOR. // new T[0] will allocate a cookie (one or two words) for the array size (0) // and store a pointer to the end of allocated chunk. The actual cookie layout // varies between platforms according to their C++ ABI implementation. inline bool IsSpecialCaseOfOperatorNew0(uptr chunk_beg, uptr chunk_size, uptr addr) { #if defined(__arm__) return IsARMABIArrayCookie(chunk_beg, chunk_size, addr); #else return IsItaniumABIArrayCookie(chunk_beg, chunk_size, addr); #endif } // The following must be implemented in the parent tool. void ForEachChunk(ForEachChunkCallback callback, void *arg); // Returns the address range occupied by the global allocator object. void GetAllocatorGlobalRange(uptr *begin, uptr *end); // Wrappers for allocator's ForceLock()/ForceUnlock(). void LockAllocator(); void UnlockAllocator(); // Returns true if [addr, addr + sizeof(void *)) is poisoned. bool WordIsPoisoned(uptr addr); // Wrappers for ThreadRegistry access. void LockThreadRegistry(); void UnlockThreadRegistry(); -bool GetThreadRangesLocked(uptr os_id, uptr *stack_begin, uptr *stack_end, +bool GetThreadRangesLocked(tid_t os_id, uptr *stack_begin, uptr *stack_end, uptr *tls_begin, uptr *tls_end, uptr *cache_begin, uptr *cache_end, DTLS **dtls); -void ForEachExtraStackRange(uptr os_id, RangeIteratorCallback callback, +void ForEachExtraStackRange(tid_t os_id, RangeIteratorCallback callback, void *arg); // If called from the main thread, updates the main thread's TID in the thread // registry. We need this to handle processes that fork() without a subsequent // exec(), which invalidates the recorded TID. To update it, we must call // gettid() from the main thread. Our solution is to call this function before // leak checking and also before every call to pthread_create() (to handle cases // where leak checking is initiated from a non-main thread). void EnsureMainThreadIDIsCorrect(); // If p points into a chunk that has been allocated to the user, returns its // user-visible address. Otherwise, returns 0. uptr PointsIntoChunk(void *p); // Returns address of user-visible chunk contained in this allocator chunk. uptr GetUserBegin(uptr chunk); // Helper for __lsan_ignore_object(). IgnoreObjectResult IgnoreObjectLocked(const void *p); + +// Return the linker module, if valid for the platform. +LoadedModule *GetLinker(); + // Wrapper for chunk metadata operations. class LsanMetadata { public: // Constructor accepts address of user-visible chunk. explicit LsanMetadata(uptr chunk); bool allocated() const; ChunkTag tag() const; void set_tag(ChunkTag value); uptr requested_size() const; u32 stack_trace_id() const; private: void *metadata_; }; } // namespace __lsan extern "C" { SANITIZER_INTERFACE_ATTRIBUTE SANITIZER_WEAK_ATTRIBUTE int __lsan_is_turned_off(); SANITIZER_INTERFACE_ATTRIBUTE SANITIZER_WEAK_ATTRIBUTE const char *__lsan_default_suppressions(); } // extern "C" #endif // LSAN_COMMON_H Index: vendor/compiler-rt/dist/lib/lsan/lsan_common_linux.cc =================================================================== --- vendor/compiler-rt/dist/lib/lsan/lsan_common_linux.cc (revision 317221) +++ vendor/compiler-rt/dist/lib/lsan/lsan_common_linux.cc (revision 317222) @@ -1,184 +1,123 @@ //=-- lsan_common_linux.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. // Implementation of common leak checking functionality. Linux-specific code. // //===----------------------------------------------------------------------===// #include "sanitizer_common/sanitizer_platform.h" #include "lsan_common.h" #if CAN_SANITIZE_LEAKS && SANITIZER_LINUX #include #include "sanitizer_common/sanitizer_common.h" #include "sanitizer_common/sanitizer_flags.h" #include "sanitizer_common/sanitizer_linux.h" #include "sanitizer_common/sanitizer_stackdepot.h" namespace __lsan { static const char kLinkerName[] = "ld"; static char linker_placeholder[sizeof(LoadedModule)] ALIGNED(64); static LoadedModule *linker = nullptr; static bool IsLinker(const char* full_name) { return LibraryNameIs(full_name, kLinkerName); } __attribute__((tls_model("initial-exec"))) THREADLOCAL int disable_counter; bool DisabledInThisThread() { return disable_counter > 0; } void DisableInThisThread() { disable_counter++; } void EnableInThisThread() { if (disable_counter == 0) { DisableCounterUnderflow(); } disable_counter--; } void InitializePlatformSpecificModules() { ListOfModules modules; modules.init(); for (LoadedModule &module : modules) { if (!IsLinker(module.full_name())) continue; if (linker == nullptr) { linker = reinterpret_cast(linker_placeholder); *linker = module; module = LoadedModule(); } else { VReport(1, "LeakSanitizer: Multiple modules match \"%s\". " "TLS will not be handled correctly.\n", kLinkerName); linker->clear(); linker = nullptr; return; } } VReport(1, "LeakSanitizer: Dynamic linker not found. " "TLS will not be handled correctly.\n"); } static int ProcessGlobalRegionsCallback(struct dl_phdr_info *info, size_t size, void *data) { Frontier *frontier = reinterpret_cast(data); for (uptr j = 0; j < info->dlpi_phnum; j++) { const ElfW(Phdr) *phdr = &(info->dlpi_phdr[j]); // We're looking for .data and .bss sections, which reside in writeable, // loadable segments. if (!(phdr->p_flags & PF_W) || (phdr->p_type != PT_LOAD) || (phdr->p_memsz == 0)) continue; uptr begin = info->dlpi_addr + phdr->p_vaddr; uptr end = begin + phdr->p_memsz; ScanGlobalRange(begin, end, frontier); } return 0; } // Scans global variables for heap pointers. void ProcessGlobalRegions(Frontier *frontier) { if (!flags()->use_globals) return; dl_iterate_phdr(ProcessGlobalRegionsCallback, frontier); } -static uptr GetCallerPC(u32 stack_id, StackDepotReverseMap *map) { - CHECK(stack_id); - StackTrace stack = map->Get(stack_id); - // The top frame is our malloc/calloc/etc. The next frame is the caller. - if (stack.size >= 2) - return stack.trace[1]; - return 0; -} +LoadedModule *GetLinker() { return linker; } -struct ProcessPlatformAllocParam { - Frontier *frontier; - StackDepotReverseMap *stack_depot_reverse_map; - bool skip_linker_allocations; -}; - -// ForEachChunk callback. Identifies unreachable chunks which must be treated as -// reachable. Marks them as reachable and adds them to the frontier. -static void ProcessPlatformSpecificAllocationsCb(uptr chunk, void *arg) { - CHECK(arg); - ProcessPlatformAllocParam *param = - reinterpret_cast(arg); - chunk = GetUserBegin(chunk); - LsanMetadata m(chunk); - if (m.allocated() && m.tag() != kReachable && m.tag() != kIgnored) { - u32 stack_id = m.stack_trace_id(); - uptr caller_pc = 0; - if (stack_id > 0) - caller_pc = GetCallerPC(stack_id, param->stack_depot_reverse_map); - // If caller_pc is unknown, this chunk may be allocated in a coroutine. Mark - // it as reachable, as we can't properly report its allocation stack anyway. - if (caller_pc == 0 || (param->skip_linker_allocations && - linker->containsAddress(caller_pc))) { - m.set_tag(kReachable); - param->frontier->push_back(chunk); - } - } -} - -// Handles dynamically allocated TLS blocks by treating all chunks allocated -// from ld-linux.so as reachable. -// Dynamic TLS blocks contain the TLS variables of dynamically loaded modules. -// They are allocated with a __libc_memalign() call in allocate_and_init() -// (elf/dl-tls.c). Glibc won't tell us the address ranges occupied by those -// blocks, but we can make sure they come from our own allocator by intercepting -// __libc_memalign(). On top of that, there is no easy way to reach them. Their -// addresses are stored in a dynamically allocated array (the DTV) which is -// referenced from the static TLS. Unfortunately, we can't just rely on the DTV -// being reachable from the static TLS, and the dynamic TLS being reachable from -// the DTV. This is because the initial DTV is allocated before our interception -// mechanism kicks in, and thus we don't recognize it as allocated memory. We -// can't special-case it either, since we don't know its size. -// Our solution is to include in the root set all allocations made from -// ld-linux.so (which is where allocate_and_init() is implemented). This is -// guaranteed to include all dynamic TLS blocks (and possibly other allocations -// which we don't care about). -void ProcessPlatformSpecificAllocations(Frontier *frontier) { - StackDepotReverseMap stack_depot_reverse_map; - ProcessPlatformAllocParam arg; - arg.frontier = frontier; - arg.stack_depot_reverse_map = &stack_depot_reverse_map; - arg.skip_linker_allocations = - flags()->use_tls && flags()->use_ld_allocations && linker != nullptr; - ForEachChunk(ProcessPlatformSpecificAllocationsCb, &arg); -} +void ProcessPlatformSpecificAllocations(Frontier *frontier) {} struct DoStopTheWorldParam { StopTheWorldCallback callback; void *argument; }; static int DoStopTheWorldCallback(struct dl_phdr_info *info, size_t size, void *data) { DoStopTheWorldParam *param = reinterpret_cast(data); StopTheWorld(param->callback, param->argument); return 1; } // LSan calls dl_iterate_phdr() from the tracer task. This may deadlock: if one // of the threads is frozen while holding the libdl lock, the tracer will hang // in dl_iterate_phdr() forever. // Luckily, (a) the lock is reentrant and (b) libc can't distinguish between the // tracer task and the thread that spawned it. Thus, if we run the tracer task // while holding the libdl lock in the parent thread, we can safely reenter it // in the tracer. The solution is to run stoptheworld from a dl_iterate_phdr() // callback in the parent thread. void DoStopTheWorld(StopTheWorldCallback callback, void *argument) { DoStopTheWorldParam param = {callback, argument}; dl_iterate_phdr(DoStopTheWorldCallback, ¶m); } } // namespace __lsan #endif // CAN_SANITIZE_LEAKS && SANITIZER_LINUX Index: vendor/compiler-rt/dist/lib/lsan/lsan_common_mac.cc =================================================================== --- vendor/compiler-rt/dist/lib/lsan/lsan_common_mac.cc (revision 317221) +++ vendor/compiler-rt/dist/lib/lsan/lsan_common_mac.cc (revision 317222) @@ -1,126 +1,173 @@ //=-- lsan_common_mac.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. // Implementation of common leak checking functionality. Darwin-specific code. // //===----------------------------------------------------------------------===// #include "sanitizer_common/sanitizer_platform.h" #include "lsan_common.h" #if CAN_SANITIZE_LEAKS && SANITIZER_MAC #include "sanitizer_common/sanitizer_allocator_internal.h" #include "lsan_allocator.h" #include +#include + namespace __lsan { typedef struct { int disable_counter; u32 current_thread_id; AllocatorCache cache; } thread_local_data_t; static pthread_key_t key; static pthread_once_t key_once = PTHREAD_ONCE_INIT; // The main thread destructor requires the current thread id, // so we can't destroy it until it's been used and reset to invalid tid void restore_tid_data(void *ptr) { thread_local_data_t *data = (thread_local_data_t *)ptr; if (data->current_thread_id != kInvalidTid) pthread_setspecific(key, data); } static void make_tls_key() { CHECK_EQ(pthread_key_create(&key, restore_tid_data), 0); } static thread_local_data_t *get_tls_val(bool alloc) { pthread_once(&key_once, make_tls_key); thread_local_data_t *ptr = (thread_local_data_t *)pthread_getspecific(key); if (ptr == NULL && alloc) { ptr = (thread_local_data_t *)InternalAlloc(sizeof(*ptr)); ptr->disable_counter = 0; ptr->current_thread_id = kInvalidTid; ptr->cache = AllocatorCache(); pthread_setspecific(key, ptr); } return ptr; } bool DisabledInThisThread() { thread_local_data_t *data = get_tls_val(false); return data ? data->disable_counter > 0 : false; } void DisableInThisThread() { ++get_tls_val(true)->disable_counter; } void EnableInThisThread() { int *disable_counter = &get_tls_val(true)->disable_counter; if (*disable_counter == 0) { DisableCounterUnderflow(); } --*disable_counter; } u32 GetCurrentThread() { thread_local_data_t *data = get_tls_val(false); CHECK(data); return data->current_thread_id; } void SetCurrentThread(u32 tid) { get_tls_val(true)->current_thread_id = tid; } AllocatorCache *GetAllocatorCache() { return &get_tls_val(true)->cache; } +LoadedModule *GetLinker() { return nullptr; } + // Required on Linux for initialization of TLS behavior, but should not be // required on Darwin. void InitializePlatformSpecificModules() { if (flags()->use_tls) { Report("use_tls=1 is not supported on Darwin.\n"); Die(); } } // Scans global variables for heap pointers. void ProcessGlobalRegions(Frontier *frontier) { MemoryMappingLayout memory_mapping(false); InternalMmapVector modules(/*initial_capacity*/ 128); memory_mapping.DumpListOfModules(&modules); for (uptr i = 0; i < modules.size(); ++i) { // Even when global scanning is disabled, we still need to scan // system libraries for stashed pointers if (!flags()->use_globals && modules[i].instrumented()) continue; for (const __sanitizer::LoadedModule::AddressRange &range : modules[i].ranges()) { - if (range.executable) continue; + if (range.executable || !range.readable) continue; ScanGlobalRange(range.beg, range.end, frontier); } } } void ProcessPlatformSpecificAllocations(Frontier *frontier) { - CHECK(0 && "unimplemented"); + mach_port_name_t port; + if (task_for_pid(mach_task_self(), internal_getpid(), &port) + != KERN_SUCCESS) { + return; + } + + unsigned depth = 1; + vm_size_t size = 0; + vm_address_t address = 0; + kern_return_t err = KERN_SUCCESS; + mach_msg_type_number_t count = VM_REGION_SUBMAP_INFO_COUNT_64; + + InternalMmapVector const *root_regions = GetRootRegions(); + + while (err == KERN_SUCCESS) { + struct vm_region_submap_info_64 info; + err = vm_region_recurse_64(port, &address, &size, &depth, + (vm_region_info_t)&info, &count); + + uptr end_address = address + size; + + // libxpc stashes some pointers in the Kernel Alloc Once page, + // make sure not to report those as leaks. + if (info.user_tag == VM_MEMORY_OS_ALLOC_ONCE) { + ScanRangeForPointers(address, end_address, frontier, "GLOBAL", + kReachable); + } + + // This additional root region scan is required on Darwin in order to + // detect root regions contained within mmap'd memory regions, because + // the Darwin implementation of sanitizer_procmaps traverses images + // as loaded by dyld, and not the complete set of all memory regions. + // + // TODO(fjricci) - remove this once sanitizer_procmaps_mac has the same + // behavior as sanitizer_procmaps_linux and traverses all memory regions + if (flags()->use_root_regions) { + for (uptr i = 0; i < root_regions->size(); i++) { + ScanRootRegion(frontier, (*root_regions)[i], address, end_address, + info.protection); + } + } + + address = end_address; + } } void DoStopTheWorld(StopTheWorldCallback callback, void *argument) { - CHECK(0 && "unimplemented"); + StopTheWorld(callback, argument); } } // namespace __lsan #endif // CAN_SANITIZE_LEAKS && SANITIZER_MAC Index: vendor/compiler-rt/dist/lib/lsan/lsan_thread.cc =================================================================== --- vendor/compiler-rt/dist/lib/lsan/lsan_thread.cc (revision 317221) +++ vendor/compiler-rt/dist/lib/lsan/lsan_thread.cc (revision 317222) @@ -1,158 +1,158 @@ //=-- lsan_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 LeakSanitizer. // See lsan_thread.h for details. // //===----------------------------------------------------------------------===// #include "lsan_thread.h" #include "sanitizer_common/sanitizer_common.h" #include "sanitizer_common/sanitizer_placement_new.h" #include "sanitizer_common/sanitizer_thread_registry.h" #include "sanitizer_common/sanitizer_tls_get_addr.h" #include "lsan_allocator.h" #include "lsan_common.h" namespace __lsan { static ThreadRegistry *thread_registry; static ThreadContextBase *CreateThreadContext(u32 tid) { void *mem = MmapOrDie(sizeof(ThreadContext), "ThreadContext"); return new(mem) ThreadContext(tid); } static const uptr kMaxThreads = 1 << 13; static const uptr kThreadQuarantineSize = 64; void InitializeThreadRegistry() { static ALIGNED(64) char thread_registry_placeholder[sizeof(ThreadRegistry)]; thread_registry = new(thread_registry_placeholder) ThreadRegistry(CreateThreadContext, kMaxThreads, kThreadQuarantineSize); } ThreadContext::ThreadContext(int tid) : ThreadContextBase(tid), stack_begin_(0), stack_end_(0), cache_begin_(0), cache_end_(0), tls_begin_(0), tls_end_(0), dtls_(nullptr) {} struct OnStartedArgs { uptr stack_begin, stack_end, cache_begin, cache_end, tls_begin, tls_end; DTLS *dtls; }; void ThreadContext::OnStarted(void *arg) { OnStartedArgs *args = reinterpret_cast(arg); stack_begin_ = args->stack_begin; stack_end_ = args->stack_end; tls_begin_ = args->tls_begin; tls_end_ = args->tls_end; cache_begin_ = args->cache_begin; cache_end_ = args->cache_end; dtls_ = args->dtls; } void ThreadContext::OnFinished() { AllocatorThreadFinish(); DTLS_Destroy(); } u32 ThreadCreate(u32 parent_tid, uptr user_id, bool detached) { return thread_registry->CreateThread(user_id, detached, parent_tid, /* arg */ nullptr); } -void ThreadStart(u32 tid, uptr os_id) { +void ThreadStart(u32 tid, tid_t os_id) { OnStartedArgs args; uptr stack_size = 0; uptr tls_size = 0; GetThreadStackAndTls(tid == 0, &args.stack_begin, &stack_size, &args.tls_begin, &tls_size); args.stack_end = args.stack_begin + stack_size; args.tls_end = args.tls_begin + tls_size; GetAllocatorCacheRange(&args.cache_begin, &args.cache_end); args.dtls = DTLS_Get(); thread_registry->StartThread(tid, os_id, /*workerthread*/ false, &args); } void ThreadFinish() { thread_registry->FinishThread(GetCurrentThread()); SetCurrentThread(kInvalidTid); } ThreadContext *CurrentThreadContext() { if (!thread_registry) return nullptr; if (GetCurrentThread() == kInvalidTid) return nullptr; // No lock needed when getting current thread. return (ThreadContext *)thread_registry->GetThreadLocked(GetCurrentThread()); } static bool FindThreadByUid(ThreadContextBase *tctx, void *arg) { uptr uid = (uptr)arg; if (tctx->user_id == uid && tctx->status != ThreadStatusInvalid) { return true; } return false; } u32 ThreadTid(uptr uid) { return thread_registry->FindThread(FindThreadByUid, (void*)uid); } void ThreadJoin(u32 tid) { CHECK_NE(tid, kInvalidTid); thread_registry->JoinThread(tid, /* arg */nullptr); } void EnsureMainThreadIDIsCorrect() { if (GetCurrentThread() == 0) CurrentThreadContext()->os_id = GetTid(); } ///// Interface to the common LSan module. ///// -bool GetThreadRangesLocked(uptr os_id, uptr *stack_begin, uptr *stack_end, +bool GetThreadRangesLocked(tid_t os_id, uptr *stack_begin, uptr *stack_end, uptr *tls_begin, uptr *tls_end, uptr *cache_begin, uptr *cache_end, DTLS **dtls) { ThreadContext *context = static_cast( thread_registry->FindThreadContextByOsIDLocked(os_id)); if (!context) return false; *stack_begin = context->stack_begin(); *stack_end = context->stack_end(); *tls_begin = context->tls_begin(); *tls_end = context->tls_end(); *cache_begin = context->cache_begin(); *cache_end = context->cache_end(); *dtls = context->dtls(); return true; } -void ForEachExtraStackRange(uptr os_id, RangeIteratorCallback callback, +void ForEachExtraStackRange(tid_t os_id, RangeIteratorCallback callback, void *arg) { } void LockThreadRegistry() { thread_registry->Lock(); } void UnlockThreadRegistry() { thread_registry->Unlock(); } } // namespace __lsan Index: vendor/compiler-rt/dist/lib/lsan/lsan_thread.h =================================================================== --- vendor/compiler-rt/dist/lib/lsan/lsan_thread.h (revision 317221) +++ vendor/compiler-rt/dist/lib/lsan/lsan_thread.h (revision 317222) @@ -1,60 +1,60 @@ //=-- lsan_thread.h -------------------------------------------------------===// // // 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. // Thread registry for standalone LSan. // //===----------------------------------------------------------------------===// #ifndef LSAN_THREAD_H #define LSAN_THREAD_H #include "sanitizer_common/sanitizer_thread_registry.h" namespace __sanitizer { struct DTLS; } namespace __lsan { class ThreadContext : public ThreadContextBase { public: explicit ThreadContext(int tid); void OnStarted(void *arg) override; void OnFinished() override; uptr stack_begin() { return stack_begin_; } uptr stack_end() { return stack_end_; } uptr tls_begin() { return tls_begin_; } uptr tls_end() { return tls_end_; } uptr cache_begin() { return cache_begin_; } uptr cache_end() { return cache_end_; } DTLS *dtls() { return dtls_; } private: uptr stack_begin_, stack_end_, cache_begin_, cache_end_, tls_begin_, tls_end_; DTLS *dtls_; }; void InitializeThreadRegistry(); -void ThreadStart(u32 tid, uptr os_id); +void ThreadStart(u32 tid, tid_t os_id); void ThreadFinish(); u32 ThreadCreate(u32 tid, uptr uid, bool detached); void ThreadJoin(u32 tid); u32 ThreadTid(uptr uid); u32 GetCurrentThread(); void SetCurrentThread(u32 tid); ThreadContext *CurrentThreadContext(); void EnsureMainThreadIDIsCorrect(); } // namespace __lsan #endif // LSAN_THREAD_H Index: vendor/compiler-rt/dist/lib/sanitizer_common/sanitizer_common.cc =================================================================== --- vendor/compiler-rt/dist/lib/sanitizer_common/sanitizer_common.cc (revision 317221) +++ vendor/compiler-rt/dist/lib/sanitizer_common/sanitizer_common.cc (revision 317222) @@ -1,509 +1,510 @@ //===-- sanitizer_common.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 shared between AddressSanitizer and ThreadSanitizer // run-time libraries. //===----------------------------------------------------------------------===// #include "sanitizer_common.h" #include "sanitizer_allocator_interface.h" #include "sanitizer_allocator_internal.h" #include "sanitizer_flags.h" #include "sanitizer_libc.h" #include "sanitizer_placement_new.h" #include "sanitizer_stacktrace_printer.h" #include "sanitizer_symbolizer.h" namespace __sanitizer { const char *SanitizerToolName = "SanitizerTool"; atomic_uint32_t current_verbosity; uptr PageSizeCached; StaticSpinMutex report_file_mu; ReportFile report_file = {&report_file_mu, kStderrFd, "", "", 0}; void RawWrite(const char *buffer) { report_file.Write(buffer, internal_strlen(buffer)); } void ReportFile::ReopenIfNecessary() { mu->CheckLocked(); if (fd == kStdoutFd || fd == kStderrFd) return; uptr pid = internal_getpid(); // If in tracer, use the parent's file. if (pid == stoptheworld_tracer_pid) pid = stoptheworld_tracer_ppid; if (fd != kInvalidFd) { // If the report file is already opened by the current process, // do nothing. Otherwise the report file was opened by the parent // process, close it now. if (fd_pid == pid) return; else CloseFile(fd); } const char *exe_name = GetProcessName(); if (common_flags()->log_exe_name && exe_name) { internal_snprintf(full_path, kMaxPathLength, "%s.%s.%zu", path_prefix, exe_name, pid); } else { internal_snprintf(full_path, kMaxPathLength, "%s.%zu", path_prefix, pid); } fd = OpenFile(full_path, WrOnly); if (fd == kInvalidFd) { const char *ErrorMsgPrefix = "ERROR: Can't open file: "; WriteToFile(kStderrFd, ErrorMsgPrefix, internal_strlen(ErrorMsgPrefix)); WriteToFile(kStderrFd, full_path, internal_strlen(full_path)); Die(); } fd_pid = pid; } void ReportFile::SetReportPath(const char *path) { if (!path) return; uptr len = internal_strlen(path); if (len > sizeof(path_prefix) - 100) { Report("ERROR: Path is too long: %c%c%c%c%c%c%c%c...\n", path[0], path[1], path[2], path[3], path[4], path[5], path[6], path[7]); Die(); } SpinMutexLock l(mu); if (fd != kStdoutFd && fd != kStderrFd && fd != kInvalidFd) CloseFile(fd); fd = kInvalidFd; if (internal_strcmp(path, "stdout") == 0) { fd = kStdoutFd; } else if (internal_strcmp(path, "stderr") == 0) { fd = kStderrFd; } else { internal_snprintf(path_prefix, kMaxPathLength, "%s", path); } } // PID of the tracer task in StopTheWorld. It shares the address space with the // main process, but has a different PID and thus requires special handling. uptr stoptheworld_tracer_pid = 0; // Cached pid of parent process - if the parent process dies, we want to keep // writing to the same log file. uptr stoptheworld_tracer_ppid = 0; void NORETURN ReportMmapFailureAndDie(uptr size, const char *mem_type, const char *mmap_type, error_t err, bool raw_report) { static int recursion_count; if (raw_report || recursion_count) { // If raw report is requested or we went into recursion, just die. // The Report() and CHECK calls below may call mmap recursively and fail. RawWrite("ERROR: Failed to mmap\n"); Die(); } recursion_count++; Report("ERROR: %s failed to " "%s 0x%zx (%zd) bytes of %s (error code: %d)\n", SanitizerToolName, mmap_type, size, size, mem_type, err); #if !SANITIZER_GO DumpProcessMap(); #endif UNREACHABLE("unable to mmap"); } bool ReadFileToBuffer(const char *file_name, char **buff, uptr *buff_size, uptr *read_len, uptr max_len, error_t *errno_p) { uptr PageSize = GetPageSizeCached(); uptr kMinFileLen = PageSize; *buff = nullptr; *buff_size = 0; *read_len = 0; // The files we usually open are not seekable, so try different buffer sizes. for (uptr size = kMinFileLen; size <= max_len; size *= 2) { fd_t fd = OpenFile(file_name, RdOnly, errno_p); if (fd == kInvalidFd) return false; UnmapOrDie(*buff, *buff_size); *buff = (char*)MmapOrDie(size, __func__); *buff_size = size; *read_len = 0; // Read up to one page at a time. bool reached_eof = false; while (*read_len + PageSize <= size) { uptr just_read; if (!ReadFromFile(fd, *buff + *read_len, PageSize, &just_read, errno_p)) { UnmapOrDie(*buff, *buff_size); return false; } if (just_read == 0) { reached_eof = true; break; } *read_len += just_read; } CloseFile(fd); if (reached_eof) // We've read the whole file. break; } return true; } typedef bool UptrComparisonFunction(const uptr &a, const uptr &b); typedef bool U32ComparisonFunction(const u32 &a, const u32 &b); template static inline bool CompareLess(const T &a, const T &b) { return a < b; } void SortArray(uptr *array, uptr size) { InternalSort(&array, size, CompareLess); } void SortArray(u32 *array, uptr size) { InternalSort(&array, size, CompareLess); } const char *StripPathPrefix(const char *filepath, const char *strip_path_prefix) { if (!filepath) return nullptr; if (!strip_path_prefix) return filepath; const char *res = filepath; if (const char *pos = internal_strstr(filepath, strip_path_prefix)) res = pos + internal_strlen(strip_path_prefix); if (res[0] == '.' && res[1] == '/') res += 2; return res; } const char *StripModuleName(const char *module) { if (!module) return nullptr; if (SANITIZER_WINDOWS) { // On Windows, both slash and backslash are possible. // Pick the one that goes last. if (const char *bslash_pos = internal_strrchr(module, '\\')) return StripModuleName(bslash_pos + 1); } if (const char *slash_pos = internal_strrchr(module, '/')) { return slash_pos + 1; } return module; } void ReportErrorSummary(const char *error_message, const char *alt_tool_name) { if (!common_flags()->print_summary) return; InternalScopedString buff(kMaxSummaryLength); buff.append("SUMMARY: %s: %s", alt_tool_name ? alt_tool_name : SanitizerToolName, error_message); __sanitizer_report_error_summary(buff.data()); } #if !SANITIZER_GO void ReportErrorSummary(const char *error_type, const AddressInfo &info, const char *alt_tool_name) { if (!common_flags()->print_summary) return; InternalScopedString buff(kMaxSummaryLength); buff.append("%s ", error_type); RenderFrame(&buff, "%L %F", 0, info, common_flags()->symbolize_vs_style, common_flags()->strip_path_prefix); ReportErrorSummary(buff.data(), alt_tool_name); } #endif // Removes the ANSI escape sequences from the input string (in-place). void RemoveANSIEscapeSequencesFromString(char *str) { if (!str) return; // We are going to remove the escape sequences in place. char *s = str; char *z = str; while (*s != '\0') { CHECK_GE(s, z); // Skip over ANSI escape sequences with pointer 's'. if (*s == '\033' && *(s + 1) == '[') { s = internal_strchrnul(s, 'm'); if (*s == '\0') { break; } s++; continue; } // 's' now points at a character we want to keep. Copy over the buffer // content if the escape sequence has been perviously skipped andadvance // both pointers. if (s != z) *z = *s; // If we have not seen an escape sequence, just advance both pointers. z++; s++; } // Null terminate the string. *z = '\0'; } void LoadedModule::set(const char *module_name, uptr base_address) { clear(); full_name_ = internal_strdup(module_name); base_address_ = base_address; } void LoadedModule::set(const char *module_name, uptr base_address, ModuleArch arch, u8 uuid[kModuleUUIDSize], bool instrumented) { set(module_name, base_address); arch_ = arch; internal_memcpy(uuid_, uuid, sizeof(uuid_)); instrumented_ = instrumented; } void LoadedModule::clear() { InternalFree(full_name_); base_address_ = 0; max_executable_address_ = 0; full_name_ = nullptr; arch_ = kModuleArchUnknown; internal_memset(uuid_, 0, kModuleUUIDSize); instrumented_ = false; while (!ranges_.empty()) { AddressRange *r = ranges_.front(); ranges_.pop_front(); InternalFree(r); } } -void LoadedModule::addAddressRange(uptr beg, uptr end, bool executable) { +void LoadedModule::addAddressRange(uptr beg, uptr end, bool executable, + bool readable) { void *mem = InternalAlloc(sizeof(AddressRange)); - AddressRange *r = new(mem) AddressRange(beg, end, executable); + AddressRange *r = new(mem) AddressRange(beg, end, executable, readable); ranges_.push_back(r); if (executable && end > max_executable_address_) max_executable_address_ = end; } bool LoadedModule::containsAddress(uptr address) const { for (const AddressRange &r : ranges()) { if (r.beg <= address && address < r.end) return true; } return false; } static atomic_uintptr_t g_total_mmaped; void IncreaseTotalMmap(uptr size) { if (!common_flags()->mmap_limit_mb) return; uptr total_mmaped = atomic_fetch_add(&g_total_mmaped, size, memory_order_relaxed) + size; // Since for now mmap_limit_mb is not a user-facing flag, just kill // a program. Use RAW_CHECK to avoid extra mmaps in reporting. RAW_CHECK((total_mmaped >> 20) < common_flags()->mmap_limit_mb); } void DecreaseTotalMmap(uptr size) { if (!common_flags()->mmap_limit_mb) return; atomic_fetch_sub(&g_total_mmaped, size, memory_order_relaxed); } bool TemplateMatch(const char *templ, const char *str) { if ((!str) || str[0] == 0) return false; bool start = false; if (templ && templ[0] == '^') { start = true; templ++; } bool asterisk = false; while (templ && templ[0]) { if (templ[0] == '*') { templ++; start = false; asterisk = true; continue; } if (templ[0] == '$') return str[0] == 0 || asterisk; if (str[0] == 0) return false; char *tpos = (char*)internal_strchr(templ, '*'); char *tpos1 = (char*)internal_strchr(templ, '$'); if ((!tpos) || (tpos1 && tpos1 < tpos)) tpos = tpos1; if (tpos) tpos[0] = 0; const char *str0 = str; const char *spos = internal_strstr(str, templ); str = spos + internal_strlen(templ); templ = tpos; if (tpos) tpos[0] = tpos == tpos1 ? '$' : '*'; if (!spos) return false; if (start && spos != str0) return false; start = false; asterisk = false; } return true; } static const char kPathSeparator = SANITIZER_WINDOWS ? ';' : ':'; char *FindPathToBinary(const char *name) { if (FileExists(name)) { return internal_strdup(name); } const char *path = GetEnv("PATH"); if (!path) return nullptr; uptr name_len = internal_strlen(name); InternalScopedBuffer buffer(kMaxPathLength); const char *beg = path; while (true) { const char *end = internal_strchrnul(beg, kPathSeparator); uptr prefix_len = end - beg; if (prefix_len + name_len + 2 <= kMaxPathLength) { internal_memcpy(buffer.data(), beg, prefix_len); buffer[prefix_len] = '/'; internal_memcpy(&buffer[prefix_len + 1], name, name_len); buffer[prefix_len + 1 + name_len] = '\0'; if (FileExists(buffer.data())) return internal_strdup(buffer.data()); } if (*end == '\0') break; beg = end + 1; } return nullptr; } static char binary_name_cache_str[kMaxPathLength]; static char process_name_cache_str[kMaxPathLength]; const char *GetProcessName() { return process_name_cache_str; } static uptr ReadProcessName(/*out*/ char *buf, uptr buf_len) { ReadLongProcessName(buf, buf_len); char *s = const_cast(StripModuleName(buf)); uptr len = internal_strlen(s); if (s != buf) { internal_memmove(buf, s, len); buf[len] = '\0'; } return len; } void UpdateProcessName() { ReadProcessName(process_name_cache_str, sizeof(process_name_cache_str)); } // Call once to make sure that binary_name_cache_str is initialized void CacheBinaryName() { if (binary_name_cache_str[0] != '\0') return; ReadBinaryName(binary_name_cache_str, sizeof(binary_name_cache_str)); ReadProcessName(process_name_cache_str, sizeof(process_name_cache_str)); } uptr ReadBinaryNameCached(/*out*/char *buf, uptr buf_len) { CacheBinaryName(); uptr name_len = internal_strlen(binary_name_cache_str); name_len = (name_len < buf_len - 1) ? name_len : buf_len - 1; if (buf_len == 0) return 0; internal_memcpy(buf, binary_name_cache_str, name_len); buf[name_len] = '\0'; return name_len; } void PrintCmdline() { char **argv = GetArgv(); if (!argv) return; Printf("\nCommand: "); for (uptr i = 0; argv[i]; ++i) Printf("%s ", argv[i]); Printf("\n\n"); } // Malloc hooks. static const int kMaxMallocFreeHooks = 5; struct MallocFreeHook { void (*malloc_hook)(const void *, uptr); void (*free_hook)(const void *); }; static MallocFreeHook MFHooks[kMaxMallocFreeHooks]; void RunMallocHooks(const void *ptr, uptr size) { for (int i = 0; i < kMaxMallocFreeHooks; i++) { auto hook = MFHooks[i].malloc_hook; if (!hook) return; hook(ptr, size); } } void RunFreeHooks(const void *ptr) { for (int i = 0; i < kMaxMallocFreeHooks; i++) { auto hook = MFHooks[i].free_hook; if (!hook) return; hook(ptr); } } static int InstallMallocFreeHooks(void (*malloc_hook)(const void *, uptr), void (*free_hook)(const void *)) { if (!malloc_hook || !free_hook) return 0; for (int i = 0; i < kMaxMallocFreeHooks; i++) { if (MFHooks[i].malloc_hook == nullptr) { MFHooks[i].malloc_hook = malloc_hook; MFHooks[i].free_hook = free_hook; return i + 1; } } return 0; } } // namespace __sanitizer using namespace __sanitizer; // NOLINT extern "C" { void __sanitizer_set_report_path(const char *path) { report_file.SetReportPath(path); } void __sanitizer_set_report_fd(void *fd) { report_file.fd = (fd_t)reinterpret_cast(fd); report_file.fd_pid = internal_getpid(); } SANITIZER_INTERFACE_WEAK_DEF(void, __sanitizer_report_error_summary, const char *error_summary) { Printf("%s\n", error_summary); } SANITIZER_INTERFACE_ATTRIBUTE void __sanitizer_set_death_callback(void (*callback)(void)) { SetUserDieCallback(callback); } SANITIZER_INTERFACE_ATTRIBUTE int __sanitizer_install_malloc_and_free_hooks(void (*malloc_hook)(const void *, uptr), void (*free_hook)(const void *)) { return InstallMallocFreeHooks(malloc_hook, free_hook); } } // extern "C" Index: vendor/compiler-rt/dist/lib/sanitizer_common/sanitizer_common.h =================================================================== --- vendor/compiler-rt/dist/lib/sanitizer_common/sanitizer_common.h (revision 317221) +++ vendor/compiler-rt/dist/lib/sanitizer_common/sanitizer_common.h (revision 317222) @@ -1,927 +1,932 @@ //===-- sanitizer_common.h --------------------------------------*- C++ -*-===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// // // This file is shared between run-time libraries of sanitizers. // // It declares common functions and classes that are used in both runtimes. // Implementation of some functions are provided in sanitizer_common, while // others must be defined by run-time library itself. //===----------------------------------------------------------------------===// #ifndef SANITIZER_COMMON_H #define SANITIZER_COMMON_H #include "sanitizer_flags.h" #include "sanitizer_interface_internal.h" #include "sanitizer_internal_defs.h" #include "sanitizer_libc.h" #include "sanitizer_list.h" #include "sanitizer_mutex.h" #if defined(_MSC_VER) && !defined(__clang__) extern "C" void _ReadWriteBarrier(); #pragma intrinsic(_ReadWriteBarrier) #endif namespace __sanitizer { struct StackTrace; struct AddressInfo; // Constants. const uptr kWordSize = SANITIZER_WORDSIZE / 8; const uptr kWordSizeInBits = 8 * kWordSize; #if defined(__powerpc__) || defined(__powerpc64__) const uptr kCacheLineSize = 128; #else const uptr kCacheLineSize = 64; #endif const uptr kMaxPathLength = 4096; const uptr kMaxThreadStackSize = 1 << 30; // 1Gb static const uptr kErrorMessageBufferSize = 1 << 16; // Denotes fake PC values that come from JIT/JAVA/etc. // For such PC values __tsan_symbolize_external() will be called. const u64 kExternalPCBit = 1ULL << 60; extern const char *SanitizerToolName; // Can be changed by the tool. extern atomic_uint32_t current_verbosity; INLINE void SetVerbosity(int verbosity) { atomic_store(¤t_verbosity, verbosity, memory_order_relaxed); } INLINE int Verbosity() { return atomic_load(¤t_verbosity, memory_order_relaxed); } uptr GetPageSize(); extern uptr PageSizeCached; INLINE uptr GetPageSizeCached() { if (!PageSizeCached) PageSizeCached = GetPageSize(); return PageSizeCached; } uptr GetMmapGranularity(); uptr GetMaxVirtualAddress(); // Threads -uptr GetTid(); +tid_t GetTid(); uptr GetThreadSelf(); void GetThreadStackTopAndBottom(bool at_initialization, uptr *stack_top, uptr *stack_bottom); void GetThreadStackAndTls(bool main, uptr *stk_addr, uptr *stk_size, uptr *tls_addr, uptr *tls_size); // Memory management void *MmapOrDie(uptr size, const char *mem_type, bool raw_report = false); INLINE void *MmapOrDieQuietly(uptr size, const char *mem_type) { return MmapOrDie(size, mem_type, /*raw_report*/ true); } void UnmapOrDie(void *addr, uptr size); void *MmapFixedNoReserve(uptr fixed_addr, uptr size, const char *name = nullptr); void *MmapNoReserveOrDie(uptr size, const char *mem_type); void *MmapFixedOrDie(uptr fixed_addr, uptr size); void *MmapFixedNoAccess(uptr fixed_addr, uptr size, const char *name = nullptr); void *MmapNoAccess(uptr size); // Map aligned chunk of address space; size and alignment are powers of two. void *MmapAlignedOrDie(uptr size, uptr alignment, const char *mem_type); // Disallow access to a memory range. Use MmapFixedNoAccess to allocate an // unaccessible memory. bool MprotectNoAccess(uptr addr, uptr size); bool MprotectReadOnly(uptr addr, uptr size); // Find an available address space. uptr FindAvailableMemoryRange(uptr size, uptr alignment, uptr left_padding); // Used to check if we can map shadow memory to a fixed location. bool MemoryRangeIsAvailable(uptr range_start, uptr range_end); // Releases memory pages entirely within the [beg, end] address range. Noop if // the provided range does not contain at least one entire page. void ReleaseMemoryPagesToOS(uptr beg, uptr end); void IncreaseTotalMmap(uptr size); void DecreaseTotalMmap(uptr size); uptr GetRSS(); void NoHugePagesInRegion(uptr addr, uptr length); void DontDumpShadowMemory(uptr addr, uptr length); // Check if the built VMA size matches the runtime one. void CheckVMASize(); void RunMallocHooks(const void *ptr, uptr size); void RunFreeHooks(const void *ptr); // InternalScopedBuffer can be used instead of large stack arrays to // keep frame size low. // FIXME: use InternalAlloc instead of MmapOrDie once // InternalAlloc is made libc-free. template class InternalScopedBuffer { public: explicit InternalScopedBuffer(uptr cnt) { cnt_ = cnt; ptr_ = (T *)MmapOrDie(cnt * sizeof(T), "InternalScopedBuffer"); } ~InternalScopedBuffer() { UnmapOrDie(ptr_, cnt_ * sizeof(T)); } T &operator[](uptr i) { return ptr_[i]; } T *data() { return ptr_; } uptr size() { return cnt_ * sizeof(T); } private: T *ptr_; uptr cnt_; // Disallow copies and moves. InternalScopedBuffer(const InternalScopedBuffer &) = delete; InternalScopedBuffer &operator=(const InternalScopedBuffer &) = delete; InternalScopedBuffer(InternalScopedBuffer &&) = delete; InternalScopedBuffer &operator=(InternalScopedBuffer &&) = delete; }; class InternalScopedString : public InternalScopedBuffer { public: explicit InternalScopedString(uptr max_length) : InternalScopedBuffer(max_length), length_(0) { (*this)[0] = '\0'; } uptr length() { return length_; } void clear() { (*this)[0] = '\0'; length_ = 0; } void append(const char *format, ...); private: uptr length_; }; // Simple low-level (mmap-based) allocator for internal use. Doesn't have // constructor, so all instances of LowLevelAllocator should be // linker initialized. class LowLevelAllocator { public: // Requires an external lock. void *Allocate(uptr size); private: char *allocated_end_; char *allocated_current_; }; typedef void (*LowLevelAllocateCallback)(uptr ptr, uptr size); // Allows to register tool-specific callbacks for LowLevelAllocator. // Passing NULL removes the callback. void SetLowLevelAllocateCallback(LowLevelAllocateCallback callback); // IO void RawWrite(const char *buffer); bool ColorizeReports(); void RemoveANSIEscapeSequencesFromString(char *buffer); void Printf(const char *format, ...); void Report(const char *format, ...); void SetPrintfAndReportCallback(void (*callback)(const char *)); #define VReport(level, ...) \ do { \ if ((uptr)Verbosity() >= (level)) Report(__VA_ARGS__); \ } while (0) #define VPrintf(level, ...) \ do { \ if ((uptr)Verbosity() >= (level)) Printf(__VA_ARGS__); \ } while (0) // Can be used to prevent mixing error reports from different sanitizers. extern StaticSpinMutex CommonSanitizerReportMutex; struct ReportFile { void Write(const char *buffer, uptr length); bool SupportsColors(); void SetReportPath(const char *path); // Don't use fields directly. They are only declared public to allow // aggregate initialization. // Protects fields below. StaticSpinMutex *mu; // Opened file descriptor. Defaults to stderr. It may be equal to // kInvalidFd, in which case new file will be opened when necessary. fd_t fd; // Path prefix of report file, set via __sanitizer_set_report_path. char path_prefix[kMaxPathLength]; // Full path to report, obtained as .PID char full_path[kMaxPathLength]; // PID of the process that opened fd. If a fork() occurs, // the PID of child will be different from fd_pid. uptr fd_pid; private: void ReopenIfNecessary(); }; extern ReportFile report_file; extern uptr stoptheworld_tracer_pid; extern uptr stoptheworld_tracer_ppid; enum FileAccessMode { RdOnly, WrOnly, RdWr }; // Returns kInvalidFd on error. fd_t OpenFile(const char *filename, FileAccessMode mode, error_t *errno_p = nullptr); void CloseFile(fd_t); // Return true on success, false on error. bool ReadFromFile(fd_t fd, void *buff, uptr buff_size, uptr *bytes_read = nullptr, error_t *error_p = nullptr); bool WriteToFile(fd_t fd, const void *buff, uptr buff_size, uptr *bytes_written = nullptr, error_t *error_p = nullptr); bool RenameFile(const char *oldpath, const char *newpath, error_t *error_p = nullptr); // Scoped file handle closer. struct FileCloser { explicit FileCloser(fd_t fd) : fd(fd) {} ~FileCloser() { CloseFile(fd); } fd_t fd; }; bool SupportsColoredOutput(fd_t fd); // Opens the file 'file_name" and reads up to 'max_len' bytes. // The resulting buffer is mmaped and stored in '*buff'. // The size of the mmaped region is stored in '*buff_size'. // The total number of read bytes is stored in '*read_len'. // Returns true if file was successfully opened and read. bool ReadFileToBuffer(const char *file_name, char **buff, uptr *buff_size, uptr *read_len, uptr max_len = 1 << 26, error_t *errno_p = nullptr); // Maps given file to virtual memory, and returns pointer to it // (or NULL if mapping fails). Stores the size of mmaped region // in '*buff_size'. void *MapFileToMemory(const char *file_name, uptr *buff_size); void *MapWritableFileToMemory(void *addr, uptr size, fd_t fd, OFF_T offset); bool IsAccessibleMemoryRange(uptr beg, uptr size); // Error report formatting. const char *StripPathPrefix(const char *filepath, const char *strip_file_prefix); // Strip the directories from the module name. const char *StripModuleName(const char *module); // OS uptr ReadBinaryName(/*out*/char *buf, uptr buf_len); uptr ReadBinaryNameCached(/*out*/char *buf, uptr buf_len); uptr ReadLongProcessName(/*out*/ char *buf, uptr buf_len); const char *GetProcessName(); void UpdateProcessName(); void CacheBinaryName(); void DisableCoreDumperIfNecessary(); void DumpProcessMap(); void PrintModuleMap(); bool FileExists(const char *filename); const char *GetEnv(const char *name); bool SetEnv(const char *name, const char *value); const char *GetPwd(); char *FindPathToBinary(const char *name); bool IsPathSeparator(const char c); bool IsAbsolutePath(const char *path); // Starts a subprocess and returs its pid. // If *_fd parameters are not kInvalidFd their corresponding input/output // streams will be redirect to the file. The files will always be closed // in parent process even in case of an error. // The child process will close all fds after STDERR_FILENO // before passing control to a program. pid_t StartSubprocess(const char *filename, const char *const argv[], fd_t stdin_fd = kInvalidFd, fd_t stdout_fd = kInvalidFd, fd_t stderr_fd = kInvalidFd); // Checks if specified process is still running bool IsProcessRunning(pid_t pid); // Waits for the process to finish and returns its exit code. // Returns -1 in case of an error. int WaitForProcess(pid_t pid); u32 GetUid(); void ReExec(); char **GetArgv(); void PrintCmdline(); bool StackSizeIsUnlimited(); uptr GetStackSizeLimitInBytes(); void SetStackSizeLimitInBytes(uptr limit); bool AddressSpaceIsUnlimited(); void SetAddressSpaceUnlimited(); void AdjustStackSize(void *attr); void PrepareForSandboxing(__sanitizer_sandbox_arguments *args); void CovPrepareForSandboxing(__sanitizer_sandbox_arguments *args); void SetSandboxingCallback(void (*f)()); void CoverageUpdateMapping(); void CovBeforeFork(); void CovAfterFork(int child_pid); void InitializeCoverage(bool enabled, const char *coverage_dir); void ReInitializeCoverage(bool enabled, const char *coverage_dir); void InitTlsSize(); uptr GetTlsSize(); // Other void SleepForSeconds(int seconds); void SleepForMillis(int millis); u64 NanoTime(); int Atexit(void (*function)(void)); void SortArray(uptr *array, uptr size); void SortArray(u32 *array, uptr size); bool TemplateMatch(const char *templ, const char *str); // Exit void NORETURN Abort(); void NORETURN Die(); void NORETURN CheckFailed(const char *file, int line, const char *cond, u64 v1, u64 v2); void NORETURN ReportMmapFailureAndDie(uptr size, const char *mem_type, const char *mmap_type, error_t err, bool raw_report = false); // Set the name of the current thread to 'name', return true on succees. // The name may be truncated to a system-dependent limit. bool SanitizerSetThreadName(const char *name); // Get the name of the current thread (no more than max_len bytes), // return true on succees. name should have space for at least max_len+1 bytes. bool SanitizerGetThreadName(char *name, int max_len); // Specific tools may override behavior of "Die" and "CheckFailed" functions // to do tool-specific job. typedef void (*DieCallbackType)(void); // It's possible to add several callbacks that would be run when "Die" is // called. The callbacks will be run in the opposite order. The tools are // strongly recommended to setup all callbacks during initialization, when there // is only a single thread. bool AddDieCallback(DieCallbackType callback); bool RemoveDieCallback(DieCallbackType callback); void SetUserDieCallback(DieCallbackType callback); typedef void (*CheckFailedCallbackType)(const char *, int, const char *, u64, u64); void SetCheckFailedCallback(CheckFailedCallbackType callback); // Callback will be called if soft_rss_limit_mb is given and the limit is // exceeded (exceeded==true) or if rss went down below the limit // (exceeded==false). // The callback should be registered once at the tool init time. void SetSoftRssLimitExceededCallback(void (*Callback)(bool exceeded)); // Functions related to signal handling. typedef void (*SignalHandlerType)(int, void *, void *); bool IsHandledDeadlySignal(int signum); void InstallDeadlySignalHandlers(SignalHandlerType handler); const char *DescribeSignalOrException(int signo); // Alternative signal stack (POSIX-only). void SetAlternateSignalStack(); void UnsetAlternateSignalStack(); // We don't want a summary too long. const int kMaxSummaryLength = 1024; // Construct a one-line string: // SUMMARY: SanitizerToolName: error_message // and pass it to __sanitizer_report_error_summary. // If alt_tool_name is provided, it's used in place of SanitizerToolName. void ReportErrorSummary(const char *error_message, const char *alt_tool_name = nullptr); // Same as above, but construct error_message as: // error_type file:line[:column][ function] void ReportErrorSummary(const char *error_type, const AddressInfo &info, const char *alt_tool_name = nullptr); // Same as above, but obtains AddressInfo by symbolizing top stack trace frame. void ReportErrorSummary(const char *error_type, const StackTrace *trace, const char *alt_tool_name = nullptr); // Math #if SANITIZER_WINDOWS && !defined(__clang__) && !defined(__GNUC__) extern "C" { unsigned char _BitScanForward(unsigned long *index, unsigned long mask); // NOLINT unsigned char _BitScanReverse(unsigned long *index, unsigned long mask); // NOLINT #if defined(_WIN64) unsigned char _BitScanForward64(unsigned long *index, unsigned __int64 mask); // NOLINT unsigned char _BitScanReverse64(unsigned long *index, unsigned __int64 mask); // NOLINT #endif } #endif INLINE uptr MostSignificantSetBitIndex(uptr x) { CHECK_NE(x, 0U); unsigned long up; // NOLINT #if !SANITIZER_WINDOWS || defined(__clang__) || defined(__GNUC__) # ifdef _WIN64 up = SANITIZER_WORDSIZE - 1 - __builtin_clzll(x); # else up = SANITIZER_WORDSIZE - 1 - __builtin_clzl(x); # endif #elif defined(_WIN64) _BitScanReverse64(&up, x); #else _BitScanReverse(&up, x); #endif return up; } INLINE uptr LeastSignificantSetBitIndex(uptr x) { CHECK_NE(x, 0U); unsigned long up; // NOLINT #if !SANITIZER_WINDOWS || defined(__clang__) || defined(__GNUC__) # ifdef _WIN64 up = __builtin_ctzll(x); # else up = __builtin_ctzl(x); # endif #elif defined(_WIN64) _BitScanForward64(&up, x); #else _BitScanForward(&up, x); #endif return up; } INLINE bool IsPowerOfTwo(uptr x) { return (x & (x - 1)) == 0; } INLINE uptr RoundUpToPowerOfTwo(uptr size) { CHECK(size); if (IsPowerOfTwo(size)) return size; uptr up = MostSignificantSetBitIndex(size); CHECK_LT(size, (1ULL << (up + 1))); CHECK_GT(size, (1ULL << up)); return 1ULL << (up + 1); } INLINE uptr RoundUpTo(uptr size, uptr boundary) { RAW_CHECK(IsPowerOfTwo(boundary)); return (size + boundary - 1) & ~(boundary - 1); } INLINE uptr RoundDownTo(uptr x, uptr boundary) { return x & ~(boundary - 1); } INLINE bool IsAligned(uptr a, uptr alignment) { return (a & (alignment - 1)) == 0; } INLINE uptr Log2(uptr x) { CHECK(IsPowerOfTwo(x)); return LeastSignificantSetBitIndex(x); } // Don't use std::min, std::max or std::swap, to minimize dependency // on libstdc++. template T Min(T a, T b) { return a < b ? a : b; } template T Max(T a, T b) { return a > b ? a : b; } template void Swap(T& a, T& b) { T tmp = a; a = b; b = tmp; } // Char handling INLINE bool IsSpace(int c) { return (c == ' ') || (c == '\n') || (c == '\t') || (c == '\f') || (c == '\r') || (c == '\v'); } INLINE bool IsDigit(int c) { return (c >= '0') && (c <= '9'); } INLINE int ToLower(int c) { return (c >= 'A' && c <= 'Z') ? (c + 'a' - 'A') : c; } // A low-level vector based on mmap. May incur a significant memory overhead for // small vectors. // WARNING: The current implementation supports only POD types. template class InternalMmapVectorNoCtor { public: void Initialize(uptr initial_capacity) { capacity_ = Max(initial_capacity, (uptr)1); size_ = 0; data_ = (T *)MmapOrDie(capacity_ * sizeof(T), "InternalMmapVectorNoCtor"); } void Destroy() { UnmapOrDie(data_, capacity_ * sizeof(T)); } T &operator[](uptr i) { CHECK_LT(i, size_); return data_[i]; } const T &operator[](uptr i) const { CHECK_LT(i, size_); return data_[i]; } void push_back(const T &element) { CHECK_LE(size_, capacity_); if (size_ == capacity_) { uptr new_capacity = RoundUpToPowerOfTwo(size_ + 1); Resize(new_capacity); } internal_memcpy(&data_[size_++], &element, sizeof(T)); } T &back() { CHECK_GT(size_, 0); return data_[size_ - 1]; } void pop_back() { CHECK_GT(size_, 0); size_--; } uptr size() const { return size_; } const T *data() const { return data_; } T *data() { return data_; } uptr capacity() const { return capacity_; } void resize(uptr new_size) { Resize(new_size); if (new_size > size_) { internal_memset(&data_[size_], 0, sizeof(T) * (new_size - size_)); } size_ = new_size; } void clear() { size_ = 0; } bool empty() const { return size() == 0; } const T *begin() const { return data(); } T *begin() { return data(); } const T *end() const { return data() + size(); } T *end() { return data() + size(); } private: void Resize(uptr new_capacity) { CHECK_GT(new_capacity, 0); CHECK_LE(size_, new_capacity); T *new_data = (T *)MmapOrDie(new_capacity * sizeof(T), "InternalMmapVector"); internal_memcpy(new_data, data_, size_ * sizeof(T)); T *old_data = data_; data_ = new_data; UnmapOrDie(old_data, capacity_ * sizeof(T)); capacity_ = new_capacity; } T *data_; uptr capacity_; uptr size_; }; template class InternalMmapVector : public InternalMmapVectorNoCtor { public: explicit InternalMmapVector(uptr initial_capacity) { InternalMmapVectorNoCtor::Initialize(initial_capacity); } ~InternalMmapVector() { InternalMmapVectorNoCtor::Destroy(); } // Disallow evil constructors. InternalMmapVector(const InternalMmapVector&); void operator=(const InternalMmapVector&); }; // HeapSort for arrays and InternalMmapVector. template void InternalSort(Container *v, uptr size, Compare comp) { if (size < 2) return; // Stage 1: insert elements to the heap. for (uptr i = 1; i < size; i++) { uptr j, p; for (j = i; j > 0; j = p) { p = (j - 1) / 2; if (comp((*v)[p], (*v)[j])) Swap((*v)[j], (*v)[p]); else break; } } // Stage 2: swap largest element with the last one, // and sink the new top. for (uptr i = size - 1; i > 0; i--) { Swap((*v)[0], (*v)[i]); uptr j, max_ind; for (j = 0; j < i; j = max_ind) { uptr left = 2 * j + 1; uptr right = 2 * j + 2; max_ind = j; if (left < i && comp((*v)[max_ind], (*v)[left])) max_ind = left; if (right < i && comp((*v)[max_ind], (*v)[right])) max_ind = right; if (max_ind != j) Swap((*v)[j], (*v)[max_ind]); else break; } } } // Works like std::lower_bound: finds the first element that is not less // than the val. template uptr InternalLowerBound(const Container &v, uptr first, uptr last, const Value &val, Compare comp) { while (last > first) { uptr mid = (first + last) / 2; if (comp(v[mid], val)) first = mid + 1; else last = mid; } return first; } enum ModuleArch { kModuleArchUnknown, kModuleArchI386, kModuleArchX86_64, kModuleArchX86_64H, kModuleArchARMV6, kModuleArchARMV7, kModuleArchARMV7S, kModuleArchARMV7K, kModuleArchARM64 }; // When adding a new architecture, don't forget to also update // script/asan_symbolize.py and sanitizer_symbolizer_libcdep.cc. inline const char *ModuleArchToString(ModuleArch arch) { switch (arch) { case kModuleArchUnknown: return ""; case kModuleArchI386: return "i386"; case kModuleArchX86_64: return "x86_64"; case kModuleArchX86_64H: return "x86_64h"; case kModuleArchARMV6: return "armv6"; case kModuleArchARMV7: return "armv7"; case kModuleArchARMV7S: return "armv7s"; case kModuleArchARMV7K: return "armv7k"; case kModuleArchARM64: return "arm64"; } CHECK(0 && "Invalid module arch"); return ""; } const uptr kModuleUUIDSize = 16; // Represents a binary loaded into virtual memory (e.g. this can be an // executable or a shared object). class LoadedModule { public: LoadedModule() : full_name_(nullptr), base_address_(0), max_executable_address_(0), arch_(kModuleArchUnknown), instrumented_(false) { internal_memset(uuid_, 0, kModuleUUIDSize); ranges_.clear(); } void set(const char *module_name, uptr base_address); void set(const char *module_name, uptr base_address, ModuleArch arch, u8 uuid[kModuleUUIDSize], bool instrumented); void clear(); - void addAddressRange(uptr beg, uptr end, bool executable); + void addAddressRange(uptr beg, uptr end, bool executable, bool readable); bool containsAddress(uptr address) const; const char *full_name() const { return full_name_; } uptr base_address() const { return base_address_; } uptr max_executable_address() const { return max_executable_address_; } ModuleArch arch() const { return arch_; } const u8 *uuid() const { return uuid_; } bool instrumented() const { return instrumented_; } struct AddressRange { AddressRange *next; uptr beg; uptr end; bool executable; + bool readable; - AddressRange(uptr beg, uptr end, bool executable) - : next(nullptr), beg(beg), end(end), executable(executable) {} + AddressRange(uptr beg, uptr end, bool executable, bool readable) + : next(nullptr), + beg(beg), + end(end), + executable(executable), + readable(readable) {} }; const IntrusiveList &ranges() const { return ranges_; } private: char *full_name_; // Owned. uptr base_address_; uptr max_executable_address_; ModuleArch arch_; u8 uuid_[kModuleUUIDSize]; bool instrumented_; IntrusiveList ranges_; }; // List of LoadedModules. OS-dependent implementation is responsible for // filling this information. class ListOfModules { public: ListOfModules() : modules_(kInitialCapacity) {} ~ListOfModules() { clear(); } void init(); const LoadedModule *begin() const { return modules_.begin(); } LoadedModule *begin() { return modules_.begin(); } const LoadedModule *end() const { return modules_.end(); } LoadedModule *end() { return modules_.end(); } uptr size() const { return modules_.size(); } const LoadedModule &operator[](uptr i) const { CHECK_LT(i, modules_.size()); return modules_[i]; } private: void clear() { for (auto &module : modules_) module.clear(); modules_.clear(); } InternalMmapVector modules_; // We rarely have more than 16K loaded modules. static const uptr kInitialCapacity = 1 << 14; }; // Callback type for iterating over a set of memory ranges. typedef void (*RangeIteratorCallback)(uptr begin, uptr end, void *arg); enum AndroidApiLevel { ANDROID_NOT_ANDROID = 0, ANDROID_KITKAT = 19, ANDROID_LOLLIPOP_MR1 = 22, ANDROID_POST_LOLLIPOP = 23 }; void WriteToSyslog(const char *buffer); #if SANITIZER_MAC void LogFullErrorReport(const char *buffer); #else INLINE void LogFullErrorReport(const char *buffer) {} #endif #if SANITIZER_LINUX || SANITIZER_MAC void WriteOneLineToSyslog(const char *s); void LogMessageOnPrintf(const char *str); #else INLINE void WriteOneLineToSyslog(const char *s) {} INLINE void LogMessageOnPrintf(const char *str) {} #endif #if SANITIZER_LINUX // Initialize Android logging. Any writes before this are silently lost. void AndroidLogInit(); #else INLINE void AndroidLogInit() {} #endif #if SANITIZER_ANDROID void SanitizerInitializeUnwinder(); AndroidApiLevel AndroidGetApiLevel(); #else INLINE void AndroidLogWrite(const char *buffer_unused) {} INLINE void SanitizerInitializeUnwinder() {} INLINE AndroidApiLevel AndroidGetApiLevel() { return ANDROID_NOT_ANDROID; } #endif INLINE uptr GetPthreadDestructorIterations() { #if SANITIZER_ANDROID return (AndroidGetApiLevel() == ANDROID_LOLLIPOP_MR1) ? 8 : 4; #elif SANITIZER_POSIX return 4; #else // Unused on Windows. return 0; #endif } void *internal_start_thread(void(*func)(void*), void *arg); void internal_join_thread(void *th); void MaybeStartBackgroudThread(); // Make the compiler think that something is going on there. // Use this inside a loop that looks like memset/memcpy/etc to prevent the // compiler from recognising it and turning it into an actual call to // memset/memcpy/etc. static inline void SanitizerBreakOptimization(void *arg) { #if defined(_MSC_VER) && !defined(__clang__) _ReadWriteBarrier(); #else __asm__ __volatile__("" : : "r" (arg) : "memory"); #endif } struct SignalContext { void *context; uptr addr; uptr pc; uptr sp; uptr bp; bool is_memory_access; enum WriteFlag { UNKNOWN, READ, WRITE } write_flag; SignalContext(void *context, uptr addr, uptr pc, uptr sp, uptr bp, bool is_memory_access, WriteFlag write_flag) : context(context), addr(addr), pc(pc), sp(sp), bp(bp), is_memory_access(is_memory_access), write_flag(write_flag) {} static void DumpAllRegisters(void *context); // Creates signal context in a platform-specific manner. static SignalContext Create(void *siginfo, void *context); // Returns true if the "context" indicates a memory write. static WriteFlag GetWriteFlag(void *context); }; void GetPcSpBp(void *context, uptr *pc, uptr *sp, uptr *bp); void MaybeReexec(); template class RunOnDestruction { public: explicit RunOnDestruction(Fn fn) : fn_(fn) {} ~RunOnDestruction() { fn_(); } private: Fn fn_; }; // A simple scope guard. Usage: // auto cleanup = at_scope_exit([]{ do_cleanup; }); template RunOnDestruction at_scope_exit(Fn fn) { return RunOnDestruction(fn); } // Linux on 64-bit s390 had a nasty bug that crashes the whole machine // if a process uses virtual memory over 4TB (as many sanitizers like // to do). This function will abort the process if running on a kernel // that looks vulnerable. #if SANITIZER_LINUX && SANITIZER_S390_64 void AvoidCVE_2016_2143(); #else INLINE void AvoidCVE_2016_2143() {} #endif struct StackDepotStats { uptr n_uniq_ids; uptr allocated; }; // The default value for allocator_release_to_os_interval_ms common flag to // indicate that sanitizer allocator should not attempt to release memory to OS. const s32 kReleaseToOSIntervalNever = -1; void CheckNoDeepBind(const char *filename, int flag); } // namespace __sanitizer inline void *operator new(__sanitizer::operator_new_size_type size, __sanitizer::LowLevelAllocator &alloc) { return alloc.Allocate(size); } #endif // SANITIZER_COMMON_H Index: vendor/compiler-rt/dist/lib/sanitizer_common/sanitizer_common_interceptors.inc =================================================================== --- vendor/compiler-rt/dist/lib/sanitizer_common/sanitizer_common_interceptors.inc (revision 317221) +++ vendor/compiler-rt/dist/lib/sanitizer_common/sanitizer_common_interceptors.inc (revision 317222) @@ -1,6351 +1,6347 @@ //===-- sanitizer_common_interceptors.inc -----------------------*- C++ -*-===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// // // Common function interceptors for tools like AddressSanitizer, // ThreadSanitizer, MemorySanitizer, etc. // // This file should be included into the tool's interceptor file, // which has to define its own macros: // COMMON_INTERCEPTOR_ENTER // COMMON_INTERCEPTOR_ENTER_NOIGNORE // COMMON_INTERCEPTOR_READ_RANGE // COMMON_INTERCEPTOR_WRITE_RANGE // COMMON_INTERCEPTOR_INITIALIZE_RANGE // COMMON_INTERCEPTOR_DIR_ACQUIRE // COMMON_INTERCEPTOR_FD_ACQUIRE // COMMON_INTERCEPTOR_FD_RELEASE // COMMON_INTERCEPTOR_FD_ACCESS // COMMON_INTERCEPTOR_SET_THREAD_NAME // COMMON_INTERCEPTOR_ON_DLOPEN // COMMON_INTERCEPTOR_ON_EXIT // COMMON_INTERCEPTOR_MUTEX_PRE_LOCK // COMMON_INTERCEPTOR_MUTEX_POST_LOCK // COMMON_INTERCEPTOR_MUTEX_UNLOCK // COMMON_INTERCEPTOR_MUTEX_REPAIR // COMMON_INTERCEPTOR_SET_PTHREAD_NAME // COMMON_INTERCEPTOR_HANDLE_RECVMSG // COMMON_INTERCEPTOR_NOTHING_IS_INITIALIZED // COMMON_INTERCEPTOR_MEMSET_IMPL // COMMON_INTERCEPTOR_MEMMOVE_IMPL // COMMON_INTERCEPTOR_MEMCPY_IMPL //===----------------------------------------------------------------------===// #include "interception/interception.h" #include "sanitizer_addrhashmap.h" #include "sanitizer_placement_new.h" #include "sanitizer_platform_interceptors.h" #include "sanitizer_tls_get_addr.h" #include #if SANITIZER_INTERCEPTOR_HOOKS #define CALL_WEAK_INTERCEPTOR_HOOK(f, ...) f(__VA_ARGS__); #define DECLARE_WEAK_INTERCEPTOR_HOOK(f, ...) \ SANITIZER_INTERFACE_WEAK_DEF(void, f, __VA_ARGS__) {} #else #define DECLARE_WEAK_INTERCEPTOR_HOOK(f, ...) #define CALL_WEAK_INTERCEPTOR_HOOK(f, ...) #endif // SANITIZER_INTERCEPTOR_HOOKS #if SANITIZER_WINDOWS && !defined(va_copy) #define va_copy(dst, src) ((dst) = (src)) #endif // _WIN32 #if SANITIZER_FREEBSD #define pthread_setname_np pthread_set_name_np #define inet_aton __inet_aton #define inet_pton __inet_pton #define iconv __bsd_iconv #endif // Platform-specific options. #if SANITIZER_MAC namespace __sanitizer { bool PlatformHasDifferentMemcpyAndMemmove(); } #define PLATFORM_HAS_DIFFERENT_MEMCPY_AND_MEMMOVE \ (__sanitizer::PlatformHasDifferentMemcpyAndMemmove()) #elif SANITIZER_WINDOWS64 #define PLATFORM_HAS_DIFFERENT_MEMCPY_AND_MEMMOVE false #else #define PLATFORM_HAS_DIFFERENT_MEMCPY_AND_MEMMOVE true #endif // SANITIZER_MAC #ifndef COMMON_INTERCEPTOR_INITIALIZE_RANGE #define COMMON_INTERCEPTOR_INITIALIZE_RANGE(p, size) {} #endif #ifndef COMMON_INTERCEPTOR_UNPOISON_PARAM #define COMMON_INTERCEPTOR_UNPOISON_PARAM(count) {} #endif #ifndef COMMON_INTERCEPTOR_FD_ACCESS #define COMMON_INTERCEPTOR_FD_ACCESS(ctx, fd) {} #endif #ifndef COMMON_INTERCEPTOR_MUTEX_PRE_LOCK #define COMMON_INTERCEPTOR_MUTEX_PRE_LOCK(ctx, m) {} #endif #ifndef COMMON_INTERCEPTOR_MUTEX_POST_LOCK #define COMMON_INTERCEPTOR_MUTEX_POST_LOCK(ctx, m) {} #endif #ifndef COMMON_INTERCEPTOR_MUTEX_UNLOCK #define COMMON_INTERCEPTOR_MUTEX_UNLOCK(ctx, m) {} #endif #ifndef COMMON_INTERCEPTOR_MUTEX_REPAIR #define COMMON_INTERCEPTOR_MUTEX_REPAIR(ctx, m) {} #endif #ifndef COMMON_INTERCEPTOR_MUTEX_INVALID #define COMMON_INTERCEPTOR_MUTEX_INVALID(ctx, m) {} #endif #ifndef COMMON_INTERCEPTOR_HANDLE_RECVMSG #define COMMON_INTERCEPTOR_HANDLE_RECVMSG(ctx, msg) ((void)(msg)) #endif #ifndef COMMON_INTERCEPTOR_FILE_OPEN #define COMMON_INTERCEPTOR_FILE_OPEN(ctx, file, path) {} #endif #ifndef COMMON_INTERCEPTOR_FILE_CLOSE #define COMMON_INTERCEPTOR_FILE_CLOSE(ctx, file) {} #endif #ifndef COMMON_INTERCEPTOR_LIBRARY_LOADED #define COMMON_INTERCEPTOR_LIBRARY_LOADED(filename, handle) {} #endif #ifndef COMMON_INTERCEPTOR_LIBRARY_UNLOADED #define COMMON_INTERCEPTOR_LIBRARY_UNLOADED() {} #endif #ifndef COMMON_INTERCEPTOR_ENTER_NOIGNORE #define COMMON_INTERCEPTOR_ENTER_NOIGNORE(ctx, ...) \ COMMON_INTERCEPTOR_ENTER(ctx, __VA_ARGS__) #endif #ifndef COMMON_INTERCEPTOR_NOTHING_IS_INITIALIZED #define COMMON_INTERCEPTOR_NOTHING_IS_INITIALIZED (0) #endif -#define COMMON_INTERCEPTOR_READ_STRING_OF_LEN(ctx, s, len, n) \ +#define COMMON_INTERCEPTOR_READ_STRING(ctx, s, n) \ COMMON_INTERCEPTOR_READ_RANGE((ctx), (s), \ - common_flags()->strict_string_checks ? (len) + 1 : (n) ) + common_flags()->strict_string_checks ? (REAL(strlen)(s)) + 1 : (n) ) -#define COMMON_INTERCEPTOR_READ_STRING(ctx, s, n) \ - COMMON_INTERCEPTOR_READ_STRING_OF_LEN((ctx), (s), REAL(strlen)(s), (n)) - #ifndef COMMON_INTERCEPTOR_ON_DLOPEN #define COMMON_INTERCEPTOR_ON_DLOPEN(filename, flag) \ CheckNoDeepBind(filename, flag); #endif #ifndef COMMON_INTERCEPTOR_GET_TLS_RANGE #define COMMON_INTERCEPTOR_GET_TLS_RANGE(begin, end) *begin = *end = 0; #endif #ifndef COMMON_INTERCEPTOR_ACQUIRE #define COMMON_INTERCEPTOR_ACQUIRE(ctx, u) {} #endif #ifndef COMMON_INTERCEPTOR_RELEASE #define COMMON_INTERCEPTOR_RELEASE(ctx, u) {} #endif #ifndef COMMON_INTERCEPTOR_USER_CALLBACK_START #define COMMON_INTERCEPTOR_USER_CALLBACK_START() {} #endif #ifndef COMMON_INTERCEPTOR_USER_CALLBACK_END #define COMMON_INTERCEPTOR_USER_CALLBACK_END() {} #endif #ifdef SANITIZER_NLDBL_VERSION #define COMMON_INTERCEPT_FUNCTION_LDBL(fn) \ COMMON_INTERCEPT_FUNCTION_VER(fn, SANITIZER_NLDBL_VERSION) #else #define COMMON_INTERCEPT_FUNCTION_LDBL(fn) \ COMMON_INTERCEPT_FUNCTION(fn) #endif #ifndef COMMON_INTERCEPTOR_MEMSET_IMPL #define COMMON_INTERCEPTOR_MEMSET_IMPL(ctx, dst, v, size) \ { \ if (COMMON_INTERCEPTOR_NOTHING_IS_INITIALIZED) \ return internal_memset(dst, v, size); \ COMMON_INTERCEPTOR_ENTER(ctx, memset, dst, v, size); \ if (common_flags()->intercept_intrin) \ COMMON_INTERCEPTOR_WRITE_RANGE(ctx, dst, size); \ return REAL(memset)(dst, v, size); \ } #endif #ifndef COMMON_INTERCEPTOR_MEMMOVE_IMPL #define COMMON_INTERCEPTOR_MEMMOVE_IMPL(ctx, dst, src, size) \ { \ if (COMMON_INTERCEPTOR_NOTHING_IS_INITIALIZED) \ return internal_memmove(dst, src, size); \ COMMON_INTERCEPTOR_ENTER(ctx, memmove, dst, src, size); \ if (common_flags()->intercept_intrin) { \ COMMON_INTERCEPTOR_WRITE_RANGE(ctx, dst, size); \ COMMON_INTERCEPTOR_READ_RANGE(ctx, src, size); \ } \ return REAL(memmove)(dst, src, size); \ } #endif #ifndef COMMON_INTERCEPTOR_MEMCPY_IMPL #define COMMON_INTERCEPTOR_MEMCPY_IMPL(ctx, dst, src, size) \ { \ if (COMMON_INTERCEPTOR_NOTHING_IS_INITIALIZED) { \ return internal_memmove(dst, src, size); \ } \ COMMON_INTERCEPTOR_ENTER(ctx, memcpy, dst, src, size); \ if (common_flags()->intercept_intrin) { \ COMMON_INTERCEPTOR_WRITE_RANGE(ctx, dst, size); \ COMMON_INTERCEPTOR_READ_RANGE(ctx, src, size); \ } \ return REAL(memcpy)(dst, src, size); \ } #endif struct FileMetadata { // For open_memstream(). char **addr; SIZE_T *size; }; struct CommonInterceptorMetadata { enum { CIMT_INVALID = 0, CIMT_FILE } type; union { FileMetadata file; }; }; typedef AddrHashMap MetadataHashMap; static MetadataHashMap *interceptor_metadata_map; #if SI_NOT_WINDOWS UNUSED static void SetInterceptorMetadata(__sanitizer_FILE *addr, const FileMetadata &file) { MetadataHashMap::Handle h(interceptor_metadata_map, (uptr)addr); CHECK(h.created()); h->type = CommonInterceptorMetadata::CIMT_FILE; h->file = file; } UNUSED static const FileMetadata *GetInterceptorMetadata( __sanitizer_FILE *addr) { MetadataHashMap::Handle h(interceptor_metadata_map, (uptr)addr, /* remove */ false, /* create */ false); if (h.exists()) { CHECK(!h.created()); CHECK(h->type == CommonInterceptorMetadata::CIMT_FILE); return &h->file; } else { return 0; } } UNUSED static void DeleteInterceptorMetadata(void *addr) { MetadataHashMap::Handle h(interceptor_metadata_map, (uptr)addr, true); CHECK(h.exists()); } #endif // SI_NOT_WINDOWS #if SANITIZER_INTERCEPT_STRLEN INTERCEPTOR(SIZE_T, strlen, const char *s) { // Sometimes strlen is called prior to InitializeCommonInterceptors, // in which case the REAL(strlen) typically used in // COMMON_INTERCEPTOR_ENTER will fail. We use internal_strlen here // to handle that. if (COMMON_INTERCEPTOR_NOTHING_IS_INITIALIZED) return internal_strlen(s); void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, strlen, s); SIZE_T result = REAL(strlen)(s); if (common_flags()->intercept_strlen) COMMON_INTERCEPTOR_READ_RANGE(ctx, s, result + 1); return result; } #define INIT_STRLEN COMMON_INTERCEPT_FUNCTION(strlen) #else #define INIT_STRLEN #endif #if SANITIZER_INTERCEPT_STRNLEN INTERCEPTOR(SIZE_T, strnlen, const char *s, SIZE_T maxlen) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, strnlen, s, maxlen); SIZE_T length = REAL(strnlen)(s, maxlen); if (common_flags()->intercept_strlen) COMMON_INTERCEPTOR_READ_RANGE(ctx, s, Min(length + 1, maxlen)); return length; } #define INIT_STRNLEN COMMON_INTERCEPT_FUNCTION(strnlen) #else #define INIT_STRNLEN #endif #if SANITIZER_INTERCEPT_TEXTDOMAIN INTERCEPTOR(char*, textdomain, const char *domainname) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, textdomain, domainname); COMMON_INTERCEPTOR_READ_STRING(ctx, domainname, 0); char *domain = REAL(textdomain)(domainname); if (domain) { COMMON_INTERCEPTOR_INITIALIZE_RANGE(domain, REAL(strlen)(domain) + 1); } return domain; } #define INIT_TEXTDOMAIN COMMON_INTERCEPT_FUNCTION(textdomain) #else #define INIT_TEXTDOMAIN #endif #if SANITIZER_INTERCEPT_STRCMP static inline int CharCmpX(unsigned char c1, unsigned char c2) { return (c1 == c2) ? 0 : (c1 < c2) ? -1 : 1; } DECLARE_WEAK_INTERCEPTOR_HOOK(__sanitizer_weak_hook_strcmp, uptr called_pc, const char *s1, const char *s2, int result) INTERCEPTOR(int, strcmp, const char *s1, const char *s2) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, strcmp, s1, s2); unsigned char c1, c2; uptr i; for (i = 0;; i++) { c1 = (unsigned char)s1[i]; c2 = (unsigned char)s2[i]; if (c1 != c2 || c1 == '\0') break; } COMMON_INTERCEPTOR_READ_STRING(ctx, s1, i + 1); COMMON_INTERCEPTOR_READ_STRING(ctx, s2, i + 1); int result = CharCmpX(c1, c2); CALL_WEAK_INTERCEPTOR_HOOK(__sanitizer_weak_hook_strcmp, GET_CALLER_PC(), s1, s2, result); return result; } DECLARE_WEAK_INTERCEPTOR_HOOK(__sanitizer_weak_hook_strncmp, uptr called_pc, const char *s1, const char *s2, uptr n, int result) INTERCEPTOR(int, strncmp, const char *s1, const char *s2, uptr size) { if (COMMON_INTERCEPTOR_NOTHING_IS_INITIALIZED) return internal_strncmp(s1, s2, size); void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, strncmp, s1, s2, size); unsigned char c1 = 0, c2 = 0; uptr i; for (i = 0; i < size; i++) { c1 = (unsigned char)s1[i]; c2 = (unsigned char)s2[i]; if (c1 != c2 || c1 == '\0') break; } uptr i1 = i; uptr i2 = i; if (common_flags()->strict_string_checks) { for (; i1 < size && s1[i1]; i1++) {} for (; i2 < size && s2[i2]; i2++) {} } COMMON_INTERCEPTOR_READ_RANGE((ctx), (s1), Min(i1 + 1, size)); COMMON_INTERCEPTOR_READ_RANGE((ctx), (s2), Min(i2 + 1, size)); int result = CharCmpX(c1, c2); CALL_WEAK_INTERCEPTOR_HOOK(__sanitizer_weak_hook_strncmp, GET_CALLER_PC(), s1, s2, size, result); return result; } #define INIT_STRCMP COMMON_INTERCEPT_FUNCTION(strcmp) #define INIT_STRNCMP COMMON_INTERCEPT_FUNCTION(strncmp) #else #define INIT_STRCMP #define INIT_STRNCMP #endif #if SANITIZER_INTERCEPT_STRCASECMP static inline int CharCaseCmp(unsigned char c1, unsigned char c2) { int c1_low = ToLower(c1); int c2_low = ToLower(c2); return c1_low - c2_low; } DECLARE_WEAK_INTERCEPTOR_HOOK(__sanitizer_weak_hook_strcasecmp, uptr called_pc, const char *s1, const char *s2, int result) INTERCEPTOR(int, strcasecmp, const char *s1, const char *s2) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, strcasecmp, s1, s2); unsigned char c1 = 0, c2 = 0; uptr i; for (i = 0;; i++) { c1 = (unsigned char)s1[i]; c2 = (unsigned char)s2[i]; if (CharCaseCmp(c1, c2) != 0 || c1 == '\0') break; } COMMON_INTERCEPTOR_READ_STRING(ctx, s1, i + 1); COMMON_INTERCEPTOR_READ_STRING(ctx, s2, i + 1); int result = CharCaseCmp(c1, c2); CALL_WEAK_INTERCEPTOR_HOOK(__sanitizer_weak_hook_strcasecmp, GET_CALLER_PC(), s1, s2, result); return result; } DECLARE_WEAK_INTERCEPTOR_HOOK(__sanitizer_weak_hook_strncasecmp, uptr called_pc, const char *s1, const char *s2, uptr size, int result) INTERCEPTOR(int, strncasecmp, const char *s1, const char *s2, SIZE_T size) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, strncasecmp, s1, s2, size); unsigned char c1 = 0, c2 = 0; uptr i; for (i = 0; i < size; i++) { c1 = (unsigned char)s1[i]; c2 = (unsigned char)s2[i]; if (CharCaseCmp(c1, c2) != 0 || c1 == '\0') break; } uptr i1 = i; uptr i2 = i; if (common_flags()->strict_string_checks) { for (; i1 < size && s1[i1]; i1++) {} for (; i2 < size && s2[i2]; i2++) {} } COMMON_INTERCEPTOR_READ_RANGE((ctx), (s1), Min(i1 + 1, size)); COMMON_INTERCEPTOR_READ_RANGE((ctx), (s2), Min(i2 + 1, size)); int result = CharCaseCmp(c1, c2); CALL_WEAK_INTERCEPTOR_HOOK(__sanitizer_weak_hook_strncasecmp, GET_CALLER_PC(), s1, s2, size, result); return result; } #define INIT_STRCASECMP COMMON_INTERCEPT_FUNCTION(strcasecmp) #define INIT_STRNCASECMP COMMON_INTERCEPT_FUNCTION(strncasecmp) #else #define INIT_STRCASECMP #define INIT_STRNCASECMP #endif #if SANITIZER_INTERCEPT_STRSTR || SANITIZER_INTERCEPT_STRCASESTR static inline void StrstrCheck(void *ctx, char *r, const char *s1, const char *s2) { uptr len1 = REAL(strlen)(s1); uptr len2 = REAL(strlen)(s2); - COMMON_INTERCEPTOR_READ_STRING_OF_LEN(ctx, s1, len1, - r ? r - s1 + len2 : len1 + 1); + COMMON_INTERCEPTOR_READ_STRING(ctx, s1, r ? r - s1 + len2 : len1 + 1); COMMON_INTERCEPTOR_READ_RANGE(ctx, s2, len2 + 1); } #endif #if SANITIZER_INTERCEPT_STRSTR DECLARE_WEAK_INTERCEPTOR_HOOK(__sanitizer_weak_hook_strstr, uptr called_pc, const char *s1, const char *s2, char *result) INTERCEPTOR(char*, strstr, const char *s1, const char *s2) { if (COMMON_INTERCEPTOR_NOTHING_IS_INITIALIZED) return internal_strstr(s1, s2); void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, strstr, s1, s2); char *r = REAL(strstr)(s1, s2); if (common_flags()->intercept_strstr) StrstrCheck(ctx, r, s1, s2); CALL_WEAK_INTERCEPTOR_HOOK(__sanitizer_weak_hook_strstr, GET_CALLER_PC(), s1, s2, r); return r; } #define INIT_STRSTR COMMON_INTERCEPT_FUNCTION(strstr); #else #define INIT_STRSTR #endif #if SANITIZER_INTERCEPT_STRCASESTR DECLARE_WEAK_INTERCEPTOR_HOOK(__sanitizer_weak_hook_strcasestr, uptr called_pc, const char *s1, const char *s2, char *result) INTERCEPTOR(char*, strcasestr, const char *s1, const char *s2) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, strcasestr, s1, s2); char *r = REAL(strcasestr)(s1, s2); if (common_flags()->intercept_strstr) StrstrCheck(ctx, r, s1, s2); CALL_WEAK_INTERCEPTOR_HOOK(__sanitizer_weak_hook_strcasestr, GET_CALLER_PC(), s1, s2, r); return r; } #define INIT_STRCASESTR COMMON_INTERCEPT_FUNCTION(strcasestr); #else #define INIT_STRCASESTR #endif #if SANITIZER_INTERCEPT_STRTOK INTERCEPTOR(char*, strtok, char *str, const char *delimiters) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, strtok, str, delimiters); if (!common_flags()->intercept_strtok) { return REAL(strtok)(str, delimiters); } if (common_flags()->strict_string_checks) { // If strict_string_checks is enabled, we check the whole first argument // string on the first call (strtok saves this string in a static buffer // for subsequent calls). We do not need to check strtok's result. // As the delimiters can change, we check them every call. if (str != nullptr) { COMMON_INTERCEPTOR_READ_RANGE(ctx, str, REAL(strlen)(str) + 1); } COMMON_INTERCEPTOR_READ_RANGE(ctx, delimiters, REAL(strlen)(delimiters) + 1); return REAL(strtok)(str, delimiters); } else { // However, when strict_string_checks is disabled we cannot check the // whole string on the first call. Instead, we check the result string // which is guaranteed to be a NULL-terminated substring of the first // argument. We also conservatively check one character of str and the // delimiters. if (str != nullptr) { COMMON_INTERCEPTOR_READ_STRING(ctx, str, 1); } COMMON_INTERCEPTOR_READ_RANGE(ctx, delimiters, 1); char *result = REAL(strtok)(str, delimiters); if (result != nullptr) { COMMON_INTERCEPTOR_READ_RANGE(ctx, result, REAL(strlen)(result) + 1); } else if (str != nullptr) { // No delimiter were found, it's safe to assume that the entire str was // scanned. COMMON_INTERCEPTOR_READ_RANGE(ctx, str, REAL(strlen)(str) + 1); } return result; } } #define INIT_STRTOK COMMON_INTERCEPT_FUNCTION(strtok) #else #define INIT_STRTOK #endif #if SANITIZER_INTERCEPT_MEMMEM DECLARE_WEAK_INTERCEPTOR_HOOK(__sanitizer_weak_hook_memmem, uptr called_pc, const void *s1, SIZE_T len1, const void *s2, SIZE_T len2, void *result) INTERCEPTOR(void*, memmem, const void *s1, SIZE_T len1, const void *s2, SIZE_T len2) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, memmem, s1, len1, s2, len2); void *r = REAL(memmem)(s1, len1, s2, len2); if (common_flags()->intercept_memmem) { COMMON_INTERCEPTOR_READ_RANGE(ctx, s1, len1); COMMON_INTERCEPTOR_READ_RANGE(ctx, s2, len2); } CALL_WEAK_INTERCEPTOR_HOOK(__sanitizer_weak_hook_memmem, GET_CALLER_PC(), s1, len1, s2, len2, r); return r; } #define INIT_MEMMEM COMMON_INTERCEPT_FUNCTION(memmem); #else #define INIT_MEMMEM #endif // SANITIZER_INTERCEPT_MEMMEM #if SANITIZER_INTERCEPT_STRCHR INTERCEPTOR(char*, strchr, const char *s, int c) { void *ctx; if (COMMON_INTERCEPTOR_NOTHING_IS_INITIALIZED) return internal_strchr(s, c); COMMON_INTERCEPTOR_ENTER(ctx, strchr, s, c); char *result = REAL(strchr)(s, c); - uptr len = internal_strlen(s); - uptr n = result ? result - s + 1 : len + 1; - if (common_flags()->intercept_strchr) - COMMON_INTERCEPTOR_READ_STRING_OF_LEN(ctx, s, len, n); + if (common_flags()->intercept_strchr) { + // Keep strlen as macro argument, as macro may ignore it. + COMMON_INTERCEPTOR_READ_STRING(ctx, s, + (result ? result - s : REAL(strlen)(s)) + 1); + } return result; } #define INIT_STRCHR COMMON_INTERCEPT_FUNCTION(strchr) #else #define INIT_STRCHR #endif #if SANITIZER_INTERCEPT_STRCHRNUL INTERCEPTOR(char*, strchrnul, const char *s, int c) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, strchrnul, s, c); char *result = REAL(strchrnul)(s, c); uptr len = result - s + 1; if (common_flags()->intercept_strchr) COMMON_INTERCEPTOR_READ_STRING(ctx, s, len); return result; } #define INIT_STRCHRNUL COMMON_INTERCEPT_FUNCTION(strchrnul) #else #define INIT_STRCHRNUL #endif #if SANITIZER_INTERCEPT_STRRCHR INTERCEPTOR(char*, strrchr, const char *s, int c) { void *ctx; if (COMMON_INTERCEPTOR_NOTHING_IS_INITIALIZED) return internal_strrchr(s, c); COMMON_INTERCEPTOR_ENTER(ctx, strrchr, s, c); - uptr len = internal_strlen(s); if (common_flags()->intercept_strchr) - COMMON_INTERCEPTOR_READ_STRING_OF_LEN(ctx, s, len, len + 1); + COMMON_INTERCEPTOR_READ_RANGE(ctx, s, REAL(strlen)(s) + 1); return REAL(strrchr)(s, c); } #define INIT_STRRCHR COMMON_INTERCEPT_FUNCTION(strrchr) #else #define INIT_STRRCHR #endif #if SANITIZER_INTERCEPT_STRSPN INTERCEPTOR(SIZE_T, strspn, const char *s1, const char *s2) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, strspn, s1, s2); SIZE_T r = REAL(strspn)(s1, s2); if (common_flags()->intercept_strspn) { COMMON_INTERCEPTOR_READ_RANGE(ctx, s2, REAL(strlen)(s2) + 1); COMMON_INTERCEPTOR_READ_STRING(ctx, s1, r + 1); } return r; } INTERCEPTOR(SIZE_T, strcspn, const char *s1, const char *s2) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, strcspn, s1, s2); SIZE_T r = REAL(strcspn)(s1, s2); if (common_flags()->intercept_strspn) { COMMON_INTERCEPTOR_READ_RANGE(ctx, s2, REAL(strlen)(s2) + 1); COMMON_INTERCEPTOR_READ_STRING(ctx, s1, r + 1); } return r; } #define INIT_STRSPN \ COMMON_INTERCEPT_FUNCTION(strspn); \ COMMON_INTERCEPT_FUNCTION(strcspn); #else #define INIT_STRSPN #endif #if SANITIZER_INTERCEPT_STRPBRK INTERCEPTOR(char *, strpbrk, const char *s1, const char *s2) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, strpbrk, s1, s2); char *r = REAL(strpbrk)(s1, s2); if (common_flags()->intercept_strpbrk) { COMMON_INTERCEPTOR_READ_RANGE(ctx, s2, REAL(strlen)(s2) + 1); COMMON_INTERCEPTOR_READ_STRING(ctx, s1, r ? r - s1 + 1 : REAL(strlen)(s1) + 1); } return r; } #define INIT_STRPBRK COMMON_INTERCEPT_FUNCTION(strpbrk); #else #define INIT_STRPBRK #endif #if SANITIZER_INTERCEPT_MEMSET INTERCEPTOR(void *, memset, void *dst, int v, uptr size) { void *ctx; COMMON_INTERCEPTOR_MEMSET_IMPL(ctx, dst, v, size); } #define INIT_MEMSET COMMON_INTERCEPT_FUNCTION(memset) #else #define INIT_MEMSET #endif #if SANITIZER_INTERCEPT_MEMMOVE INTERCEPTOR(void *, memmove, void *dst, const void *src, uptr size) { void *ctx; COMMON_INTERCEPTOR_MEMMOVE_IMPL(ctx, dst, src, size); } #define INIT_MEMMOVE COMMON_INTERCEPT_FUNCTION(memmove) #else #define INIT_MEMMOVE #endif #if SANITIZER_INTERCEPT_MEMCPY INTERCEPTOR(void *, memcpy, void *dst, const void *src, uptr size) { // On OS X, calling internal_memcpy here will cause memory corruptions, // because memcpy and memmove are actually aliases of the same // implementation. We need to use internal_memmove here. // N.B.: If we switch this to internal_ we'll have to use internal_memmove // due to memcpy being an alias of memmove on OS X. void *ctx; if (PLATFORM_HAS_DIFFERENT_MEMCPY_AND_MEMMOVE) { COMMON_INTERCEPTOR_MEMCPY_IMPL(ctx, dst, src, size); } else { COMMON_INTERCEPTOR_MEMMOVE_IMPL(ctx, dst, src, size); } } #define INIT_MEMCPY \ do { \ if (PLATFORM_HAS_DIFFERENT_MEMCPY_AND_MEMMOVE) { \ COMMON_INTERCEPT_FUNCTION(memcpy); \ } else { \ ASSIGN_REAL(memcpy, memmove); \ } \ CHECK(REAL(memcpy)); \ } while (false) #else #define INIT_MEMCPY #endif #if SANITIZER_INTERCEPT_MEMCMP DECLARE_WEAK_INTERCEPTOR_HOOK(__sanitizer_weak_hook_memcmp, uptr called_pc, const void *s1, const void *s2, uptr n, int result) INTERCEPTOR(int, memcmp, const void *a1, const void *a2, uptr size) { if (COMMON_INTERCEPTOR_NOTHING_IS_INITIALIZED) return internal_memcmp(a1, a2, size); void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, memcmp, a1, a2, size); if (common_flags()->intercept_memcmp) { if (common_flags()->strict_memcmp) { // Check the entire regions even if the first bytes of the buffers are // different. COMMON_INTERCEPTOR_READ_RANGE(ctx, a1, size); COMMON_INTERCEPTOR_READ_RANGE(ctx, a2, size); // Fallthrough to REAL(memcmp) below. } else { unsigned char c1 = 0, c2 = 0; const unsigned char *s1 = (const unsigned char*)a1; const unsigned char *s2 = (const unsigned char*)a2; uptr i; for (i = 0; i < size; i++) { c1 = s1[i]; c2 = s2[i]; if (c1 != c2) break; } COMMON_INTERCEPTOR_READ_RANGE(ctx, s1, Min(i + 1, size)); COMMON_INTERCEPTOR_READ_RANGE(ctx, s2, Min(i + 1, size)); int r = CharCmpX(c1, c2); CALL_WEAK_INTERCEPTOR_HOOK(__sanitizer_weak_hook_memcmp, GET_CALLER_PC(), a1, a2, size, r); return r; } } int result = REAL(memcmp(a1, a2, size)); CALL_WEAK_INTERCEPTOR_HOOK(__sanitizer_weak_hook_memcmp, GET_CALLER_PC(), a1, a2, size, result); return result; } #define INIT_MEMCMP COMMON_INTERCEPT_FUNCTION(memcmp) #else #define INIT_MEMCMP #endif #if SANITIZER_INTERCEPT_MEMCHR INTERCEPTOR(void*, memchr, const void *s, int c, SIZE_T n) { if (COMMON_INTERCEPTOR_NOTHING_IS_INITIALIZED) return internal_memchr(s, c, n); void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, memchr, s, c, n); #if SANITIZER_WINDOWS void *res; if (REAL(memchr)) { res = REAL(memchr)(s, c, n); } else { res = internal_memchr(s, c, n); } #else void *res = REAL(memchr)(s, c, n); #endif uptr len = res ? (char *)res - (const char *)s + 1 : n; COMMON_INTERCEPTOR_READ_RANGE(ctx, s, len); return res; } #define INIT_MEMCHR COMMON_INTERCEPT_FUNCTION(memchr) #else #define INIT_MEMCHR #endif #if SANITIZER_INTERCEPT_MEMRCHR INTERCEPTOR(void*, memrchr, const void *s, int c, SIZE_T n) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, memrchr, s, c, n); COMMON_INTERCEPTOR_READ_RANGE(ctx, s, n); return REAL(memrchr)(s, c, n); } #define INIT_MEMRCHR COMMON_INTERCEPT_FUNCTION(memrchr) #else #define INIT_MEMRCHR #endif #if SANITIZER_INTERCEPT_FREXP INTERCEPTOR(double, frexp, double x, int *exp) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, frexp, x, exp); // Assuming frexp() always writes to |exp|. COMMON_INTERCEPTOR_WRITE_RANGE(ctx, exp, sizeof(*exp)); double res = REAL(frexp)(x, exp); return res; } #define INIT_FREXP COMMON_INTERCEPT_FUNCTION(frexp); #else #define INIT_FREXP #endif // SANITIZER_INTERCEPT_FREXP #if SANITIZER_INTERCEPT_FREXPF_FREXPL INTERCEPTOR(float, frexpf, float x, int *exp) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, frexpf, x, exp); // FIXME: under ASan the call below may write to freed memory and corrupt // its metadata. See // https://github.com/google/sanitizers/issues/321. float res = REAL(frexpf)(x, exp); COMMON_INTERCEPTOR_WRITE_RANGE(ctx, exp, sizeof(*exp)); return res; } INTERCEPTOR(long double, frexpl, long double x, int *exp) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, frexpl, x, exp); // FIXME: under ASan the call below may write to freed memory and corrupt // its metadata. See // https://github.com/google/sanitizers/issues/321. long double res = REAL(frexpl)(x, exp); COMMON_INTERCEPTOR_WRITE_RANGE(ctx, exp, sizeof(*exp)); return res; } #define INIT_FREXPF_FREXPL \ COMMON_INTERCEPT_FUNCTION(frexpf); \ COMMON_INTERCEPT_FUNCTION_LDBL(frexpl) #else #define INIT_FREXPF_FREXPL #endif // SANITIZER_INTERCEPT_FREXPF_FREXPL #if SI_NOT_WINDOWS static void write_iovec(void *ctx, struct __sanitizer_iovec *iovec, SIZE_T iovlen, SIZE_T maxlen) { for (SIZE_T i = 0; i < iovlen && maxlen; ++i) { SSIZE_T sz = Min(iovec[i].iov_len, maxlen); COMMON_INTERCEPTOR_WRITE_RANGE(ctx, iovec[i].iov_base, sz); maxlen -= sz; } } static void read_iovec(void *ctx, struct __sanitizer_iovec *iovec, SIZE_T iovlen, SIZE_T maxlen) { COMMON_INTERCEPTOR_READ_RANGE(ctx, iovec, sizeof(*iovec) * iovlen); for (SIZE_T i = 0; i < iovlen && maxlen; ++i) { SSIZE_T sz = Min(iovec[i].iov_len, maxlen); COMMON_INTERCEPTOR_READ_RANGE(ctx, iovec[i].iov_base, sz); maxlen -= sz; } } #endif #if SANITIZER_INTERCEPT_READ INTERCEPTOR(SSIZE_T, read, int fd, void *ptr, SIZE_T count) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, read, fd, ptr, count); COMMON_INTERCEPTOR_FD_ACCESS(ctx, fd); // FIXME: under ASan the call below may write to freed memory and corrupt // its metadata. See // https://github.com/google/sanitizers/issues/321. SSIZE_T res = REAL(read)(fd, ptr, count); if (res > 0) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, ptr, res); if (res >= 0 && fd >= 0) COMMON_INTERCEPTOR_FD_ACQUIRE(ctx, fd); return res; } #define INIT_READ COMMON_INTERCEPT_FUNCTION(read) #else #define INIT_READ #endif #if SANITIZER_INTERCEPT_FREAD INTERCEPTOR(SIZE_T, fread, void *ptr, SIZE_T size, SIZE_T nmemb, void *file) { // libc file streams can call user-supplied functions, see fopencookie. void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, fread, ptr, size, nmemb, file); // FIXME: under ASan the call below may write to freed memory and corrupt // its metadata. See // https://github.com/google/sanitizers/issues/321. SIZE_T res = REAL(fread)(ptr, size, nmemb, file); if (res > 0) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, ptr, res * size); return res; } #define INIT_FREAD COMMON_INTERCEPT_FUNCTION(fread) #else #define INIT_FREAD #endif #if SANITIZER_INTERCEPT_PREAD INTERCEPTOR(SSIZE_T, pread, int fd, void *ptr, SIZE_T count, OFF_T offset) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, pread, fd, ptr, count, offset); COMMON_INTERCEPTOR_FD_ACCESS(ctx, fd); // FIXME: under ASan the call below may write to freed memory and corrupt // its metadata. See // https://github.com/google/sanitizers/issues/321. SSIZE_T res = REAL(pread)(fd, ptr, count, offset); if (res > 0) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, ptr, res); if (res >= 0 && fd >= 0) COMMON_INTERCEPTOR_FD_ACQUIRE(ctx, fd); return res; } #define INIT_PREAD COMMON_INTERCEPT_FUNCTION(pread) #else #define INIT_PREAD #endif #if SANITIZER_INTERCEPT_PREAD64 INTERCEPTOR(SSIZE_T, pread64, int fd, void *ptr, SIZE_T count, OFF64_T offset) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, pread64, fd, ptr, count, offset); COMMON_INTERCEPTOR_FD_ACCESS(ctx, fd); // FIXME: under ASan the call below may write to freed memory and corrupt // its metadata. See // https://github.com/google/sanitizers/issues/321. SSIZE_T res = REAL(pread64)(fd, ptr, count, offset); if (res > 0) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, ptr, res); if (res >= 0 && fd >= 0) COMMON_INTERCEPTOR_FD_ACQUIRE(ctx, fd); return res; } #define INIT_PREAD64 COMMON_INTERCEPT_FUNCTION(pread64) #else #define INIT_PREAD64 #endif #if SANITIZER_INTERCEPT_READV INTERCEPTOR_WITH_SUFFIX(SSIZE_T, readv, int fd, __sanitizer_iovec *iov, int iovcnt) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, readv, fd, iov, iovcnt); COMMON_INTERCEPTOR_FD_ACCESS(ctx, fd); SSIZE_T res = REAL(readv)(fd, iov, iovcnt); if (res > 0) write_iovec(ctx, iov, iovcnt, res); if (res >= 0 && fd >= 0) COMMON_INTERCEPTOR_FD_ACQUIRE(ctx, fd); return res; } #define INIT_READV COMMON_INTERCEPT_FUNCTION(readv) #else #define INIT_READV #endif #if SANITIZER_INTERCEPT_PREADV INTERCEPTOR(SSIZE_T, preadv, int fd, __sanitizer_iovec *iov, int iovcnt, OFF_T offset) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, preadv, fd, iov, iovcnt, offset); COMMON_INTERCEPTOR_FD_ACCESS(ctx, fd); SSIZE_T res = REAL(preadv)(fd, iov, iovcnt, offset); if (res > 0) write_iovec(ctx, iov, iovcnt, res); if (res >= 0 && fd >= 0) COMMON_INTERCEPTOR_FD_ACQUIRE(ctx, fd); return res; } #define INIT_PREADV COMMON_INTERCEPT_FUNCTION(preadv) #else #define INIT_PREADV #endif #if SANITIZER_INTERCEPT_PREADV64 INTERCEPTOR(SSIZE_T, preadv64, int fd, __sanitizer_iovec *iov, int iovcnt, OFF64_T offset) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, preadv64, fd, iov, iovcnt, offset); COMMON_INTERCEPTOR_FD_ACCESS(ctx, fd); SSIZE_T res = REAL(preadv64)(fd, iov, iovcnt, offset); if (res > 0) write_iovec(ctx, iov, iovcnt, res); if (res >= 0 && fd >= 0) COMMON_INTERCEPTOR_FD_ACQUIRE(ctx, fd); return res; } #define INIT_PREADV64 COMMON_INTERCEPT_FUNCTION(preadv64) #else #define INIT_PREADV64 #endif #if SANITIZER_INTERCEPT_WRITE INTERCEPTOR(SSIZE_T, write, int fd, void *ptr, SIZE_T count) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, write, fd, ptr, count); COMMON_INTERCEPTOR_FD_ACCESS(ctx, fd); if (fd >= 0) COMMON_INTERCEPTOR_FD_RELEASE(ctx, fd); SSIZE_T res = REAL(write)(fd, ptr, count); // FIXME: this check should be _before_ the call to REAL(write), not after if (res > 0) COMMON_INTERCEPTOR_READ_RANGE(ctx, ptr, res); return res; } #define INIT_WRITE COMMON_INTERCEPT_FUNCTION(write) #else #define INIT_WRITE #endif #if SANITIZER_INTERCEPT_FWRITE INTERCEPTOR(SIZE_T, fwrite, const void *p, uptr size, uptr nmemb, void *file) { // libc file streams can call user-supplied functions, see fopencookie. void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, fwrite, p, size, nmemb, file); SIZE_T res = REAL(fwrite)(p, size, nmemb, file); if (res > 0) COMMON_INTERCEPTOR_READ_RANGE(ctx, p, res * size); return res; } #define INIT_FWRITE COMMON_INTERCEPT_FUNCTION(fwrite) #else #define INIT_FWRITE #endif #if SANITIZER_INTERCEPT_PWRITE INTERCEPTOR(SSIZE_T, pwrite, int fd, void *ptr, SIZE_T count, OFF_T offset) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, pwrite, fd, ptr, count, offset); COMMON_INTERCEPTOR_FD_ACCESS(ctx, fd); if (fd >= 0) COMMON_INTERCEPTOR_FD_RELEASE(ctx, fd); SSIZE_T res = REAL(pwrite)(fd, ptr, count, offset); if (res > 0) COMMON_INTERCEPTOR_READ_RANGE(ctx, ptr, res); return res; } #define INIT_PWRITE COMMON_INTERCEPT_FUNCTION(pwrite) #else #define INIT_PWRITE #endif #if SANITIZER_INTERCEPT_PWRITE64 INTERCEPTOR(SSIZE_T, pwrite64, int fd, void *ptr, OFF64_T count, OFF64_T offset) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, pwrite64, fd, ptr, count, offset); COMMON_INTERCEPTOR_FD_ACCESS(ctx, fd); if (fd >= 0) COMMON_INTERCEPTOR_FD_RELEASE(ctx, fd); SSIZE_T res = REAL(pwrite64)(fd, ptr, count, offset); if (res > 0) COMMON_INTERCEPTOR_READ_RANGE(ctx, ptr, res); return res; } #define INIT_PWRITE64 COMMON_INTERCEPT_FUNCTION(pwrite64) #else #define INIT_PWRITE64 #endif #if SANITIZER_INTERCEPT_WRITEV INTERCEPTOR_WITH_SUFFIX(SSIZE_T, writev, int fd, __sanitizer_iovec *iov, int iovcnt) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, writev, fd, iov, iovcnt); COMMON_INTERCEPTOR_FD_ACCESS(ctx, fd); if (fd >= 0) COMMON_INTERCEPTOR_FD_RELEASE(ctx, fd); SSIZE_T res = REAL(writev)(fd, iov, iovcnt); if (res > 0) read_iovec(ctx, iov, iovcnt, res); return res; } #define INIT_WRITEV COMMON_INTERCEPT_FUNCTION(writev) #else #define INIT_WRITEV #endif #if SANITIZER_INTERCEPT_PWRITEV INTERCEPTOR(SSIZE_T, pwritev, int fd, __sanitizer_iovec *iov, int iovcnt, OFF_T offset) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, pwritev, fd, iov, iovcnt, offset); COMMON_INTERCEPTOR_FD_ACCESS(ctx, fd); if (fd >= 0) COMMON_INTERCEPTOR_FD_RELEASE(ctx, fd); SSIZE_T res = REAL(pwritev)(fd, iov, iovcnt, offset); if (res > 0) read_iovec(ctx, iov, iovcnt, res); return res; } #define INIT_PWRITEV COMMON_INTERCEPT_FUNCTION(pwritev) #else #define INIT_PWRITEV #endif #if SANITIZER_INTERCEPT_PWRITEV64 INTERCEPTOR(SSIZE_T, pwritev64, int fd, __sanitizer_iovec *iov, int iovcnt, OFF64_T offset) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, pwritev64, fd, iov, iovcnt, offset); COMMON_INTERCEPTOR_FD_ACCESS(ctx, fd); if (fd >= 0) COMMON_INTERCEPTOR_FD_RELEASE(ctx, fd); SSIZE_T res = REAL(pwritev64)(fd, iov, iovcnt, offset); if (res > 0) read_iovec(ctx, iov, iovcnt, res); return res; } #define INIT_PWRITEV64 COMMON_INTERCEPT_FUNCTION(pwritev64) #else #define INIT_PWRITEV64 #endif #if SANITIZER_INTERCEPT_PRCTL INTERCEPTOR(int, prctl, int option, unsigned long arg2, unsigned long arg3, // NOLINT unsigned long arg4, unsigned long arg5) { // NOLINT void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, prctl, option, arg2, arg3, arg4, arg5); static const int PR_SET_NAME = 15; int res = REAL(prctl(option, arg2, arg3, arg4, arg5)); if (option == PR_SET_NAME) { char buff[16]; internal_strncpy(buff, (char *)arg2, 15); buff[15] = 0; COMMON_INTERCEPTOR_SET_THREAD_NAME(ctx, buff); } return res; } #define INIT_PRCTL COMMON_INTERCEPT_FUNCTION(prctl) #else #define INIT_PRCTL #endif // SANITIZER_INTERCEPT_PRCTL #if SANITIZER_INTERCEPT_TIME INTERCEPTOR(unsigned long, time, unsigned long *t) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, time, t); unsigned long local_t; unsigned long res = REAL(time)(&local_t); if (t && res != (unsigned long)-1) { COMMON_INTERCEPTOR_WRITE_RANGE(ctx, t, sizeof(*t)); *t = local_t; } return res; } #define INIT_TIME COMMON_INTERCEPT_FUNCTION(time); #else #define INIT_TIME #endif // SANITIZER_INTERCEPT_TIME #if SANITIZER_INTERCEPT_LOCALTIME_AND_FRIENDS static void unpoison_tm(void *ctx, __sanitizer_tm *tm) { COMMON_INTERCEPTOR_WRITE_RANGE(ctx, tm, sizeof(*tm)); if (tm->tm_zone) { // Can not use COMMON_INTERCEPTOR_WRITE_RANGE here, because tm->tm_zone // can point to shared memory and tsan would report a data race. COMMON_INTERCEPTOR_INITIALIZE_RANGE(tm->tm_zone, REAL(strlen(tm->tm_zone)) + 1); } } INTERCEPTOR(__sanitizer_tm *, localtime, unsigned long *timep) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, localtime, timep); __sanitizer_tm *res = REAL(localtime)(timep); if (res) { COMMON_INTERCEPTOR_READ_RANGE(ctx, timep, sizeof(*timep)); unpoison_tm(ctx, res); } return res; } INTERCEPTOR(__sanitizer_tm *, localtime_r, unsigned long *timep, void *result) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, localtime_r, timep, result); __sanitizer_tm *res = REAL(localtime_r)(timep, result); if (res) { COMMON_INTERCEPTOR_READ_RANGE(ctx, timep, sizeof(*timep)); unpoison_tm(ctx, res); } return res; } INTERCEPTOR(__sanitizer_tm *, gmtime, unsigned long *timep) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, gmtime, timep); __sanitizer_tm *res = REAL(gmtime)(timep); if (res) { COMMON_INTERCEPTOR_READ_RANGE(ctx, timep, sizeof(*timep)); unpoison_tm(ctx, res); } return res; } INTERCEPTOR(__sanitizer_tm *, gmtime_r, unsigned long *timep, void *result) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, gmtime_r, timep, result); __sanitizer_tm *res = REAL(gmtime_r)(timep, result); if (res) { COMMON_INTERCEPTOR_READ_RANGE(ctx, timep, sizeof(*timep)); unpoison_tm(ctx, res); } return res; } INTERCEPTOR(char *, ctime, unsigned long *timep) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, ctime, timep); // FIXME: under ASan the call below may write to freed memory and corrupt // its metadata. See // https://github.com/google/sanitizers/issues/321. char *res = REAL(ctime)(timep); if (res) { COMMON_INTERCEPTOR_READ_RANGE(ctx, timep, sizeof(*timep)); COMMON_INTERCEPTOR_WRITE_RANGE(ctx, res, REAL(strlen)(res) + 1); } return res; } INTERCEPTOR(char *, ctime_r, unsigned long *timep, char *result) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, ctime_r, timep, result); // FIXME: under ASan the call below may write to freed memory and corrupt // its metadata. See // https://github.com/google/sanitizers/issues/321. char *res = REAL(ctime_r)(timep, result); if (res) { COMMON_INTERCEPTOR_READ_RANGE(ctx, timep, sizeof(*timep)); COMMON_INTERCEPTOR_WRITE_RANGE(ctx, res, REAL(strlen)(res) + 1); } return res; } INTERCEPTOR(char *, asctime, __sanitizer_tm *tm) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, asctime, tm); // FIXME: under ASan the call below may write to freed memory and corrupt // its metadata. See // https://github.com/google/sanitizers/issues/321. char *res = REAL(asctime)(tm); if (res) { COMMON_INTERCEPTOR_READ_RANGE(ctx, tm, sizeof(*tm)); COMMON_INTERCEPTOR_WRITE_RANGE(ctx, res, REAL(strlen)(res) + 1); } return res; } INTERCEPTOR(char *, asctime_r, __sanitizer_tm *tm, char *result) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, asctime_r, tm, result); // FIXME: under ASan the call below may write to freed memory and corrupt // its metadata. See // https://github.com/google/sanitizers/issues/321. char *res = REAL(asctime_r)(tm, result); if (res) { COMMON_INTERCEPTOR_READ_RANGE(ctx, tm, sizeof(*tm)); COMMON_INTERCEPTOR_WRITE_RANGE(ctx, res, REAL(strlen)(res) + 1); } return res; } INTERCEPTOR(long, mktime, __sanitizer_tm *tm) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, mktime, tm); COMMON_INTERCEPTOR_READ_RANGE(ctx, &tm->tm_sec, sizeof(tm->tm_sec)); COMMON_INTERCEPTOR_READ_RANGE(ctx, &tm->tm_min, sizeof(tm->tm_min)); COMMON_INTERCEPTOR_READ_RANGE(ctx, &tm->tm_hour, sizeof(tm->tm_hour)); COMMON_INTERCEPTOR_READ_RANGE(ctx, &tm->tm_mday, sizeof(tm->tm_mday)); COMMON_INTERCEPTOR_READ_RANGE(ctx, &tm->tm_mon, sizeof(tm->tm_mon)); COMMON_INTERCEPTOR_READ_RANGE(ctx, &tm->tm_year, sizeof(tm->tm_year)); COMMON_INTERCEPTOR_READ_RANGE(ctx, &tm->tm_isdst, sizeof(tm->tm_isdst)); long res = REAL(mktime)(tm); if (res != -1) unpoison_tm(ctx, tm); return res; } #define INIT_LOCALTIME_AND_FRIENDS \ COMMON_INTERCEPT_FUNCTION(localtime); \ COMMON_INTERCEPT_FUNCTION(localtime_r); \ COMMON_INTERCEPT_FUNCTION(gmtime); \ COMMON_INTERCEPT_FUNCTION(gmtime_r); \ COMMON_INTERCEPT_FUNCTION(ctime); \ COMMON_INTERCEPT_FUNCTION(ctime_r); \ COMMON_INTERCEPT_FUNCTION(asctime); \ COMMON_INTERCEPT_FUNCTION(asctime_r); \ COMMON_INTERCEPT_FUNCTION(mktime); #else #define INIT_LOCALTIME_AND_FRIENDS #endif // SANITIZER_INTERCEPT_LOCALTIME_AND_FRIENDS #if SANITIZER_INTERCEPT_STRPTIME INTERCEPTOR(char *, strptime, char *s, char *format, __sanitizer_tm *tm) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, strptime, s, format, tm); if (format) COMMON_INTERCEPTOR_READ_RANGE(ctx, format, REAL(strlen)(format) + 1); // FIXME: under ASan the call below may write to freed memory and corrupt // its metadata. See // https://github.com/google/sanitizers/issues/321. char *res = REAL(strptime)(s, format, tm); COMMON_INTERCEPTOR_READ_STRING(ctx, s, res ? res - s : 0); if (res && tm) { // Do not call unpoison_tm here, because strptime does not, in fact, // initialize the entire struct tm. For example, tm_zone pointer is left // uninitialized. COMMON_INTERCEPTOR_WRITE_RANGE(ctx, tm, sizeof(*tm)); } return res; } #define INIT_STRPTIME COMMON_INTERCEPT_FUNCTION(strptime); #else #define INIT_STRPTIME #endif #if SANITIZER_INTERCEPT_SCANF || SANITIZER_INTERCEPT_PRINTF #include "sanitizer_common_interceptors_format.inc" #define FORMAT_INTERCEPTOR_IMPL(name, vname, ...) \ { \ void *ctx; \ va_list ap; \ va_start(ap, format); \ COMMON_INTERCEPTOR_ENTER(ctx, vname, __VA_ARGS__, ap); \ int res = WRAP(vname)(__VA_ARGS__, ap); \ va_end(ap); \ return res; \ } #endif #if SANITIZER_INTERCEPT_SCANF #define VSCANF_INTERCEPTOR_IMPL(vname, allowGnuMalloc, ...) \ { \ void *ctx; \ COMMON_INTERCEPTOR_ENTER(ctx, vname, __VA_ARGS__); \ va_list aq; \ va_copy(aq, ap); \ int res = REAL(vname)(__VA_ARGS__); \ if (res > 0) \ scanf_common(ctx, res, allowGnuMalloc, format, aq); \ va_end(aq); \ return res; \ } INTERCEPTOR(int, vscanf, const char *format, va_list ap) VSCANF_INTERCEPTOR_IMPL(vscanf, true, format, ap) INTERCEPTOR(int, vsscanf, const char *str, const char *format, va_list ap) VSCANF_INTERCEPTOR_IMPL(vsscanf, true, str, format, ap) INTERCEPTOR(int, vfscanf, void *stream, const char *format, va_list ap) VSCANF_INTERCEPTOR_IMPL(vfscanf, true, stream, format, ap) #if SANITIZER_INTERCEPT_ISOC99_SCANF INTERCEPTOR(int, __isoc99_vscanf, const char *format, va_list ap) VSCANF_INTERCEPTOR_IMPL(__isoc99_vscanf, false, format, ap) INTERCEPTOR(int, __isoc99_vsscanf, const char *str, const char *format, va_list ap) VSCANF_INTERCEPTOR_IMPL(__isoc99_vsscanf, false, str, format, ap) INTERCEPTOR(int, __isoc99_vfscanf, void *stream, const char *format, va_list ap) VSCANF_INTERCEPTOR_IMPL(__isoc99_vfscanf, false, stream, format, ap) #endif // SANITIZER_INTERCEPT_ISOC99_SCANF INTERCEPTOR(int, scanf, const char *format, ...) FORMAT_INTERCEPTOR_IMPL(scanf, vscanf, format) INTERCEPTOR(int, fscanf, void *stream, const char *format, ...) FORMAT_INTERCEPTOR_IMPL(fscanf, vfscanf, stream, format) INTERCEPTOR(int, sscanf, const char *str, const char *format, ...) FORMAT_INTERCEPTOR_IMPL(sscanf, vsscanf, str, format) #if SANITIZER_INTERCEPT_ISOC99_SCANF INTERCEPTOR(int, __isoc99_scanf, const char *format, ...) FORMAT_INTERCEPTOR_IMPL(__isoc99_scanf, __isoc99_vscanf, format) INTERCEPTOR(int, __isoc99_fscanf, void *stream, const char *format, ...) FORMAT_INTERCEPTOR_IMPL(__isoc99_fscanf, __isoc99_vfscanf, stream, format) INTERCEPTOR(int, __isoc99_sscanf, const char *str, const char *format, ...) FORMAT_INTERCEPTOR_IMPL(__isoc99_sscanf, __isoc99_vsscanf, str, format) #endif #endif #if SANITIZER_INTERCEPT_SCANF #define INIT_SCANF \ COMMON_INTERCEPT_FUNCTION_LDBL(scanf); \ COMMON_INTERCEPT_FUNCTION_LDBL(sscanf); \ COMMON_INTERCEPT_FUNCTION_LDBL(fscanf); \ COMMON_INTERCEPT_FUNCTION_LDBL(vscanf); \ COMMON_INTERCEPT_FUNCTION_LDBL(vsscanf); \ COMMON_INTERCEPT_FUNCTION_LDBL(vfscanf); #else #define INIT_SCANF #endif #if SANITIZER_INTERCEPT_ISOC99_SCANF #define INIT_ISOC99_SCANF \ COMMON_INTERCEPT_FUNCTION(__isoc99_scanf); \ COMMON_INTERCEPT_FUNCTION(__isoc99_sscanf); \ COMMON_INTERCEPT_FUNCTION(__isoc99_fscanf); \ COMMON_INTERCEPT_FUNCTION(__isoc99_vscanf); \ COMMON_INTERCEPT_FUNCTION(__isoc99_vsscanf); \ COMMON_INTERCEPT_FUNCTION(__isoc99_vfscanf); #else #define INIT_ISOC99_SCANF #endif #if SANITIZER_INTERCEPT_PRINTF #define VPRINTF_INTERCEPTOR_ENTER(vname, ...) \ void *ctx; \ COMMON_INTERCEPTOR_ENTER(ctx, vname, __VA_ARGS__); \ va_list aq; \ va_copy(aq, ap); #define VPRINTF_INTERCEPTOR_RETURN() \ va_end(aq); #define VPRINTF_INTERCEPTOR_IMPL(vname, ...) \ { \ VPRINTF_INTERCEPTOR_ENTER(vname, __VA_ARGS__); \ if (common_flags()->check_printf) \ printf_common(ctx, format, aq); \ int res = REAL(vname)(__VA_ARGS__); \ VPRINTF_INTERCEPTOR_RETURN(); \ return res; \ } // FIXME: under ASan the REAL() call below may write to freed memory and // corrupt its metadata. See // https://github.com/google/sanitizers/issues/321. #define VSPRINTF_INTERCEPTOR_IMPL(vname, str, ...) \ { \ VPRINTF_INTERCEPTOR_ENTER(vname, str, __VA_ARGS__) \ if (common_flags()->check_printf) { \ printf_common(ctx, format, aq); \ } \ int res = REAL(vname)(str, __VA_ARGS__); \ if (res >= 0) { \ COMMON_INTERCEPTOR_WRITE_RANGE(ctx, str, res + 1); \ } \ VPRINTF_INTERCEPTOR_RETURN(); \ return res; \ } // FIXME: under ASan the REAL() call below may write to freed memory and // corrupt its metadata. See // https://github.com/google/sanitizers/issues/321. #define VSNPRINTF_INTERCEPTOR_IMPL(vname, str, size, ...) \ { \ VPRINTF_INTERCEPTOR_ENTER(vname, str, size, __VA_ARGS__) \ if (common_flags()->check_printf) { \ printf_common(ctx, format, aq); \ } \ int res = REAL(vname)(str, size, __VA_ARGS__); \ if (res >= 0) { \ COMMON_INTERCEPTOR_WRITE_RANGE(ctx, str, Min(size, (SIZE_T)(res + 1))); \ } \ VPRINTF_INTERCEPTOR_RETURN(); \ return res; \ } // FIXME: under ASan the REAL() call below may write to freed memory and // corrupt its metadata. See // https://github.com/google/sanitizers/issues/321. #define VASPRINTF_INTERCEPTOR_IMPL(vname, strp, ...) \ { \ VPRINTF_INTERCEPTOR_ENTER(vname, strp, __VA_ARGS__) \ COMMON_INTERCEPTOR_WRITE_RANGE(ctx, strp, sizeof(char *)); \ if (common_flags()->check_printf) { \ printf_common(ctx, format, aq); \ } \ int res = REAL(vname)(strp, __VA_ARGS__); \ if (res >= 0) { \ COMMON_INTERCEPTOR_WRITE_RANGE(ctx, *strp, res + 1); \ } \ VPRINTF_INTERCEPTOR_RETURN(); \ return res; \ } INTERCEPTOR(int, vprintf, const char *format, va_list ap) VPRINTF_INTERCEPTOR_IMPL(vprintf, format, ap) INTERCEPTOR(int, vfprintf, __sanitizer_FILE *stream, const char *format, va_list ap) VPRINTF_INTERCEPTOR_IMPL(vfprintf, stream, format, ap) INTERCEPTOR(int, vsnprintf, char *str, SIZE_T size, const char *format, va_list ap) VSNPRINTF_INTERCEPTOR_IMPL(vsnprintf, str, size, format, ap) #if SANITIZER_INTERCEPT_PRINTF_L INTERCEPTOR(int, vsnprintf_l, char *str, SIZE_T size, void *loc, const char *format, va_list ap) VSNPRINTF_INTERCEPTOR_IMPL(vsnprintf_l, str, size, loc, format, ap) INTERCEPTOR(int, snprintf_l, char *str, SIZE_T size, void *loc, const char *format, ...) FORMAT_INTERCEPTOR_IMPL(snprintf_l, vsnprintf_l, str, size, loc, format) #endif // SANITIZER_INTERCEPT_PRINTF_L INTERCEPTOR(int, vsprintf, char *str, const char *format, va_list ap) VSPRINTF_INTERCEPTOR_IMPL(vsprintf, str, format, ap) INTERCEPTOR(int, vasprintf, char **strp, const char *format, va_list ap) VASPRINTF_INTERCEPTOR_IMPL(vasprintf, strp, format, ap) #if SANITIZER_INTERCEPT_ISOC99_PRINTF INTERCEPTOR(int, __isoc99_vprintf, const char *format, va_list ap) VPRINTF_INTERCEPTOR_IMPL(__isoc99_vprintf, format, ap) INTERCEPTOR(int, __isoc99_vfprintf, __sanitizer_FILE *stream, const char *format, va_list ap) VPRINTF_INTERCEPTOR_IMPL(__isoc99_vfprintf, stream, format, ap) INTERCEPTOR(int, __isoc99_vsnprintf, char *str, SIZE_T size, const char *format, va_list ap) VSNPRINTF_INTERCEPTOR_IMPL(__isoc99_vsnprintf, str, size, format, ap) INTERCEPTOR(int, __isoc99_vsprintf, char *str, const char *format, va_list ap) VSPRINTF_INTERCEPTOR_IMPL(__isoc99_vsprintf, str, format, ap) #endif // SANITIZER_INTERCEPT_ISOC99_PRINTF INTERCEPTOR(int, printf, const char *format, ...) FORMAT_INTERCEPTOR_IMPL(printf, vprintf, format) INTERCEPTOR(int, fprintf, __sanitizer_FILE *stream, const char *format, ...) FORMAT_INTERCEPTOR_IMPL(fprintf, vfprintf, stream, format) INTERCEPTOR(int, sprintf, char *str, const char *format, ...) // NOLINT FORMAT_INTERCEPTOR_IMPL(sprintf, vsprintf, str, format) // NOLINT INTERCEPTOR(int, snprintf, char *str, SIZE_T size, const char *format, ...) FORMAT_INTERCEPTOR_IMPL(snprintf, vsnprintf, str, size, format) INTERCEPTOR(int, asprintf, char **strp, const char *format, ...) FORMAT_INTERCEPTOR_IMPL(asprintf, vasprintf, strp, format) #if SANITIZER_INTERCEPT_ISOC99_PRINTF INTERCEPTOR(int, __isoc99_printf, const char *format, ...) FORMAT_INTERCEPTOR_IMPL(__isoc99_printf, __isoc99_vprintf, format) INTERCEPTOR(int, __isoc99_fprintf, __sanitizer_FILE *stream, const char *format, ...) FORMAT_INTERCEPTOR_IMPL(__isoc99_fprintf, __isoc99_vfprintf, stream, format) INTERCEPTOR(int, __isoc99_sprintf, char *str, const char *format, ...) FORMAT_INTERCEPTOR_IMPL(__isoc99_sprintf, __isoc99_vsprintf, str, format) INTERCEPTOR(int, __isoc99_snprintf, char *str, SIZE_T size, const char *format, ...) FORMAT_INTERCEPTOR_IMPL(__isoc99_snprintf, __isoc99_vsnprintf, str, size, format) #endif // SANITIZER_INTERCEPT_ISOC99_PRINTF #endif // SANITIZER_INTERCEPT_PRINTF #if SANITIZER_INTERCEPT_PRINTF #define INIT_PRINTF \ COMMON_INTERCEPT_FUNCTION_LDBL(printf); \ COMMON_INTERCEPT_FUNCTION_LDBL(sprintf); \ COMMON_INTERCEPT_FUNCTION_LDBL(snprintf); \ COMMON_INTERCEPT_FUNCTION_LDBL(asprintf); \ COMMON_INTERCEPT_FUNCTION_LDBL(fprintf); \ COMMON_INTERCEPT_FUNCTION_LDBL(vprintf); \ COMMON_INTERCEPT_FUNCTION_LDBL(vsprintf); \ COMMON_INTERCEPT_FUNCTION_LDBL(vsnprintf); \ COMMON_INTERCEPT_FUNCTION_LDBL(vasprintf); \ COMMON_INTERCEPT_FUNCTION_LDBL(vfprintf); #else #define INIT_PRINTF #endif #if SANITIZER_INTERCEPT_PRINTF_L #define INIT_PRINTF_L \ COMMON_INTERCEPT_FUNCTION(snprintf_l); \ COMMON_INTERCEPT_FUNCTION(vsnprintf_l); #else #define INIT_PRINTF_L #endif #if SANITIZER_INTERCEPT_ISOC99_PRINTF #define INIT_ISOC99_PRINTF \ COMMON_INTERCEPT_FUNCTION(__isoc99_printf); \ COMMON_INTERCEPT_FUNCTION(__isoc99_sprintf); \ COMMON_INTERCEPT_FUNCTION(__isoc99_snprintf); \ COMMON_INTERCEPT_FUNCTION(__isoc99_fprintf); \ COMMON_INTERCEPT_FUNCTION(__isoc99_vprintf); \ COMMON_INTERCEPT_FUNCTION(__isoc99_vsprintf); \ COMMON_INTERCEPT_FUNCTION(__isoc99_vsnprintf); \ COMMON_INTERCEPT_FUNCTION(__isoc99_vfprintf); #else #define INIT_ISOC99_PRINTF #endif #if SANITIZER_INTERCEPT_IOCTL #include "sanitizer_common_interceptors_ioctl.inc" INTERCEPTOR(int, ioctl, int d, unsigned long request, ...) { // We need a frame pointer, because we call into ioctl_common_[pre|post] which // can trigger a report and we need to be able to unwind through this // function. On Mac in debug mode we might not have a frame pointer, because // ioctl_common_[pre|post] doesn't get inlined here. ENABLE_FRAME_POINTER; void *ctx; va_list ap; va_start(ap, request); void *arg = va_arg(ap, void *); va_end(ap); COMMON_INTERCEPTOR_ENTER(ctx, ioctl, d, request, arg); CHECK(ioctl_initialized); // Note: TSan does not use common flags, and they are zero-initialized. // This effectively disables ioctl handling in TSan. if (!common_flags()->handle_ioctl) return REAL(ioctl)(d, request, arg); // Although request is unsigned long, the rest of the interceptor uses it // as just "unsigned" to save space, because we know that all values fit in // "unsigned" - they are compile-time constants. const ioctl_desc *desc = ioctl_lookup(request); ioctl_desc decoded_desc; if (!desc) { VPrintf(2, "Decoding unknown ioctl 0x%x\n", request); if (!ioctl_decode(request, &decoded_desc)) Printf("WARNING: failed decoding unknown ioctl 0x%x\n", request); else desc = &decoded_desc; } if (desc) ioctl_common_pre(ctx, desc, d, request, arg); int res = REAL(ioctl)(d, request, arg); // FIXME: some ioctls have different return values for success and failure. if (desc && res != -1) ioctl_common_post(ctx, desc, res, d, request, arg); return res; } #define INIT_IOCTL \ ioctl_init(); \ COMMON_INTERCEPT_FUNCTION(ioctl); #else #define INIT_IOCTL #endif #if SANITIZER_INTERCEPT_GETPWNAM_AND_FRIENDS || \ SANITIZER_INTERCEPT_GETPWENT || SANITIZER_INTERCEPT_FGETPWENT || \ SANITIZER_INTERCEPT_GETPWENT_R || SANITIZER_INTERCEPT_GETPWNAM_R_AND_FRIENDS static void unpoison_passwd(void *ctx, __sanitizer_passwd *pwd) { if (pwd) { COMMON_INTERCEPTOR_WRITE_RANGE(ctx, pwd, sizeof(*pwd)); if (pwd->pw_name) COMMON_INTERCEPTOR_INITIALIZE_RANGE(pwd->pw_name, REAL(strlen)(pwd->pw_name) + 1); if (pwd->pw_passwd) COMMON_INTERCEPTOR_INITIALIZE_RANGE(pwd->pw_passwd, REAL(strlen)(pwd->pw_passwd) + 1); #if !SANITIZER_ANDROID if (pwd->pw_gecos) COMMON_INTERCEPTOR_INITIALIZE_RANGE(pwd->pw_gecos, REAL(strlen)(pwd->pw_gecos) + 1); #endif #if SANITIZER_MAC if (pwd->pw_class) COMMON_INTERCEPTOR_INITIALIZE_RANGE(pwd->pw_class, REAL(strlen)(pwd->pw_class) + 1); #endif if (pwd->pw_dir) COMMON_INTERCEPTOR_INITIALIZE_RANGE(pwd->pw_dir, REAL(strlen)(pwd->pw_dir) + 1); if (pwd->pw_shell) COMMON_INTERCEPTOR_INITIALIZE_RANGE(pwd->pw_shell, REAL(strlen)(pwd->pw_shell) + 1); } } static void unpoison_group(void *ctx, __sanitizer_group *grp) { if (grp) { COMMON_INTERCEPTOR_WRITE_RANGE(ctx, grp, sizeof(*grp)); if (grp->gr_name) COMMON_INTERCEPTOR_INITIALIZE_RANGE(grp->gr_name, REAL(strlen)(grp->gr_name) + 1); if (grp->gr_passwd) COMMON_INTERCEPTOR_INITIALIZE_RANGE(grp->gr_passwd, REAL(strlen)(grp->gr_passwd) + 1); char **p = grp->gr_mem; for (; *p; ++p) { COMMON_INTERCEPTOR_INITIALIZE_RANGE(*p, REAL(strlen)(*p) + 1); } COMMON_INTERCEPTOR_INITIALIZE_RANGE(grp->gr_mem, (p - grp->gr_mem + 1) * sizeof(*p)); } } #endif // SANITIZER_INTERCEPT_GETPWNAM_AND_FRIENDS || // SANITIZER_INTERCEPT_GETPWENT || SANITIZER_INTERCEPT_FGETPWENT || // SANITIZER_INTERCEPT_GETPWENT_R || // SANITIZER_INTERCEPT_GETPWNAM_R_AND_FRIENDS #if SANITIZER_INTERCEPT_GETPWNAM_AND_FRIENDS INTERCEPTOR(__sanitizer_passwd *, getpwnam, const char *name) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, getpwnam, name); COMMON_INTERCEPTOR_READ_RANGE(ctx, name, REAL(strlen)(name) + 1); __sanitizer_passwd *res = REAL(getpwnam)(name); if (res) unpoison_passwd(ctx, res); return res; } INTERCEPTOR(__sanitizer_passwd *, getpwuid, u32 uid) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, getpwuid, uid); __sanitizer_passwd *res = REAL(getpwuid)(uid); if (res) unpoison_passwd(ctx, res); return res; } INTERCEPTOR(__sanitizer_group *, getgrnam, const char *name) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, getgrnam, name); COMMON_INTERCEPTOR_READ_RANGE(ctx, name, REAL(strlen)(name) + 1); __sanitizer_group *res = REAL(getgrnam)(name); if (res) unpoison_group(ctx, res); return res; } INTERCEPTOR(__sanitizer_group *, getgrgid, u32 gid) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, getgrgid, gid); __sanitizer_group *res = REAL(getgrgid)(gid); if (res) unpoison_group(ctx, res); return res; } #define INIT_GETPWNAM_AND_FRIENDS \ COMMON_INTERCEPT_FUNCTION(getpwnam); \ COMMON_INTERCEPT_FUNCTION(getpwuid); \ COMMON_INTERCEPT_FUNCTION(getgrnam); \ COMMON_INTERCEPT_FUNCTION(getgrgid); #else #define INIT_GETPWNAM_AND_FRIENDS #endif #if SANITIZER_INTERCEPT_GETPWNAM_R_AND_FRIENDS INTERCEPTOR(int, getpwnam_r, const char *name, __sanitizer_passwd *pwd, char *buf, SIZE_T buflen, __sanitizer_passwd **result) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, getpwnam_r, name, pwd, buf, buflen, result); COMMON_INTERCEPTOR_READ_RANGE(ctx, name, REAL(strlen)(name) + 1); // FIXME: under ASan the call below may write to freed memory and corrupt // its metadata. See // https://github.com/google/sanitizers/issues/321. int res = REAL(getpwnam_r)(name, pwd, buf, buflen, result); if (!res) { if (result && *result) unpoison_passwd(ctx, *result); COMMON_INTERCEPTOR_WRITE_RANGE(ctx, buf, buflen); } if (result) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, result, sizeof(*result)); return res; } INTERCEPTOR(int, getpwuid_r, u32 uid, __sanitizer_passwd *pwd, char *buf, SIZE_T buflen, __sanitizer_passwd **result) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, getpwuid_r, uid, pwd, buf, buflen, result); // FIXME: under ASan the call below may write to freed memory and corrupt // its metadata. See // https://github.com/google/sanitizers/issues/321. int res = REAL(getpwuid_r)(uid, pwd, buf, buflen, result); if (!res) { if (result && *result) unpoison_passwd(ctx, *result); COMMON_INTERCEPTOR_WRITE_RANGE(ctx, buf, buflen); } if (result) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, result, sizeof(*result)); return res; } INTERCEPTOR(int, getgrnam_r, const char *name, __sanitizer_group *grp, char *buf, SIZE_T buflen, __sanitizer_group **result) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, getgrnam_r, name, grp, buf, buflen, result); COMMON_INTERCEPTOR_READ_RANGE(ctx, name, REAL(strlen)(name) + 1); // FIXME: under ASan the call below may write to freed memory and corrupt // its metadata. See // https://github.com/google/sanitizers/issues/321. int res = REAL(getgrnam_r)(name, grp, buf, buflen, result); if (!res) { if (result && *result) unpoison_group(ctx, *result); COMMON_INTERCEPTOR_WRITE_RANGE(ctx, buf, buflen); } if (result) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, result, sizeof(*result)); return res; } INTERCEPTOR(int, getgrgid_r, u32 gid, __sanitizer_group *grp, char *buf, SIZE_T buflen, __sanitizer_group **result) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, getgrgid_r, gid, grp, buf, buflen, result); // FIXME: under ASan the call below may write to freed memory and corrupt // its metadata. See // https://github.com/google/sanitizers/issues/321. int res = REAL(getgrgid_r)(gid, grp, buf, buflen, result); if (!res) { if (result && *result) unpoison_group(ctx, *result); COMMON_INTERCEPTOR_WRITE_RANGE(ctx, buf, buflen); } if (result) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, result, sizeof(*result)); return res; } #define INIT_GETPWNAM_R_AND_FRIENDS \ COMMON_INTERCEPT_FUNCTION(getpwnam_r); \ COMMON_INTERCEPT_FUNCTION(getpwuid_r); \ COMMON_INTERCEPT_FUNCTION(getgrnam_r); \ COMMON_INTERCEPT_FUNCTION(getgrgid_r); #else #define INIT_GETPWNAM_R_AND_FRIENDS #endif #if SANITIZER_INTERCEPT_GETPWENT INTERCEPTOR(__sanitizer_passwd *, getpwent, int dummy) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, getpwent, dummy); __sanitizer_passwd *res = REAL(getpwent)(dummy); if (res) unpoison_passwd(ctx, res); return res; } INTERCEPTOR(__sanitizer_group *, getgrent, int dummy) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, getgrent, dummy); __sanitizer_group *res = REAL(getgrent)(dummy); if (res) unpoison_group(ctx, res);; return res; } #define INIT_GETPWENT \ COMMON_INTERCEPT_FUNCTION(getpwent); \ COMMON_INTERCEPT_FUNCTION(getgrent); #else #define INIT_GETPWENT #endif #if SANITIZER_INTERCEPT_FGETPWENT INTERCEPTOR(__sanitizer_passwd *, fgetpwent, void *fp) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, fgetpwent, fp); __sanitizer_passwd *res = REAL(fgetpwent)(fp); if (res) unpoison_passwd(ctx, res); return res; } INTERCEPTOR(__sanitizer_group *, fgetgrent, void *fp) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, fgetgrent, fp); __sanitizer_group *res = REAL(fgetgrent)(fp); if (res) unpoison_group(ctx, res); return res; } #define INIT_FGETPWENT \ COMMON_INTERCEPT_FUNCTION(fgetpwent); \ COMMON_INTERCEPT_FUNCTION(fgetgrent); #else #define INIT_FGETPWENT #endif #if SANITIZER_INTERCEPT_GETPWENT_R INTERCEPTOR(int, getpwent_r, __sanitizer_passwd *pwbuf, char *buf, SIZE_T buflen, __sanitizer_passwd **pwbufp) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, getpwent_r, pwbuf, buf, buflen, pwbufp); // FIXME: under ASan the call below may write to freed memory and corrupt // its metadata. See // https://github.com/google/sanitizers/issues/321. int res = REAL(getpwent_r)(pwbuf, buf, buflen, pwbufp); if (!res) { if (pwbufp && *pwbufp) unpoison_passwd(ctx, *pwbufp); COMMON_INTERCEPTOR_WRITE_RANGE(ctx, buf, buflen); } if (pwbufp) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, pwbufp, sizeof(*pwbufp)); return res; } INTERCEPTOR(int, fgetpwent_r, void *fp, __sanitizer_passwd *pwbuf, char *buf, SIZE_T buflen, __sanitizer_passwd **pwbufp) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, fgetpwent_r, fp, pwbuf, buf, buflen, pwbufp); // FIXME: under ASan the call below may write to freed memory and corrupt // its metadata. See // https://github.com/google/sanitizers/issues/321. int res = REAL(fgetpwent_r)(fp, pwbuf, buf, buflen, pwbufp); if (!res) { if (pwbufp && *pwbufp) unpoison_passwd(ctx, *pwbufp); COMMON_INTERCEPTOR_WRITE_RANGE(ctx, buf, buflen); } if (pwbufp) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, pwbufp, sizeof(*pwbufp)); return res; } INTERCEPTOR(int, getgrent_r, __sanitizer_group *pwbuf, char *buf, SIZE_T buflen, __sanitizer_group **pwbufp) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, getgrent_r, pwbuf, buf, buflen, pwbufp); // FIXME: under ASan the call below may write to freed memory and corrupt // its metadata. See // https://github.com/google/sanitizers/issues/321. int res = REAL(getgrent_r)(pwbuf, buf, buflen, pwbufp); if (!res) { if (pwbufp && *pwbufp) unpoison_group(ctx, *pwbufp); COMMON_INTERCEPTOR_WRITE_RANGE(ctx, buf, buflen); } if (pwbufp) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, pwbufp, sizeof(*pwbufp)); return res; } INTERCEPTOR(int, fgetgrent_r, void *fp, __sanitizer_group *pwbuf, char *buf, SIZE_T buflen, __sanitizer_group **pwbufp) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, fgetgrent_r, fp, pwbuf, buf, buflen, pwbufp); // FIXME: under ASan the call below may write to freed memory and corrupt // its metadata. See // https://github.com/google/sanitizers/issues/321. int res = REAL(fgetgrent_r)(fp, pwbuf, buf, buflen, pwbufp); if (!res) { if (pwbufp && *pwbufp) unpoison_group(ctx, *pwbufp); COMMON_INTERCEPTOR_WRITE_RANGE(ctx, buf, buflen); } if (pwbufp) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, pwbufp, sizeof(*pwbufp)); return res; } #define INIT_GETPWENT_R \ COMMON_INTERCEPT_FUNCTION(getpwent_r); \ COMMON_INTERCEPT_FUNCTION(fgetpwent_r); \ COMMON_INTERCEPT_FUNCTION(getgrent_r); \ COMMON_INTERCEPT_FUNCTION(fgetgrent_r); #else #define INIT_GETPWENT_R #endif #if SANITIZER_INTERCEPT_SETPWENT // The only thing these interceptors do is disable any nested interceptors. // These functions may open nss modules and call uninstrumented functions from // them, and we don't want things like strlen() to trigger. INTERCEPTOR(void, setpwent, int dummy) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, setpwent, dummy); REAL(setpwent)(dummy); } INTERCEPTOR(void, endpwent, int dummy) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, endpwent, dummy); REAL(endpwent)(dummy); } INTERCEPTOR(void, setgrent, int dummy) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, setgrent, dummy); REAL(setgrent)(dummy); } INTERCEPTOR(void, endgrent, int dummy) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, endgrent, dummy); REAL(endgrent)(dummy); } #define INIT_SETPWENT \ COMMON_INTERCEPT_FUNCTION(setpwent); \ COMMON_INTERCEPT_FUNCTION(endpwent); \ COMMON_INTERCEPT_FUNCTION(setgrent); \ COMMON_INTERCEPT_FUNCTION(endgrent); #else #define INIT_SETPWENT #endif #if SANITIZER_INTERCEPT_CLOCK_GETTIME INTERCEPTOR(int, clock_getres, u32 clk_id, void *tp) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, clock_getres, clk_id, tp); // FIXME: under ASan the call below may write to freed memory and corrupt // its metadata. See // https://github.com/google/sanitizers/issues/321. int res = REAL(clock_getres)(clk_id, tp); if (!res && tp) { COMMON_INTERCEPTOR_WRITE_RANGE(ctx, tp, struct_timespec_sz); } return res; } INTERCEPTOR(int, clock_gettime, u32 clk_id, void *tp) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, clock_gettime, clk_id, tp); // FIXME: under ASan the call below may write to freed memory and corrupt // its metadata. See // https://github.com/google/sanitizers/issues/321. int res = REAL(clock_gettime)(clk_id, tp); if (!res) { COMMON_INTERCEPTOR_WRITE_RANGE(ctx, tp, struct_timespec_sz); } return res; } INTERCEPTOR(int, clock_settime, u32 clk_id, const void *tp) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, clock_settime, clk_id, tp); COMMON_INTERCEPTOR_READ_RANGE(ctx, tp, struct_timespec_sz); return REAL(clock_settime)(clk_id, tp); } #define INIT_CLOCK_GETTIME \ COMMON_INTERCEPT_FUNCTION(clock_getres); \ COMMON_INTERCEPT_FUNCTION(clock_gettime); \ COMMON_INTERCEPT_FUNCTION(clock_settime); #else #define INIT_CLOCK_GETTIME #endif #if SANITIZER_INTERCEPT_GETITIMER INTERCEPTOR(int, getitimer, int which, void *curr_value) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, getitimer, which, curr_value); // FIXME: under ASan the call below may write to freed memory and corrupt // its metadata. See // https://github.com/google/sanitizers/issues/321. int res = REAL(getitimer)(which, curr_value); if (!res && curr_value) { COMMON_INTERCEPTOR_WRITE_RANGE(ctx, curr_value, struct_itimerval_sz); } return res; } INTERCEPTOR(int, setitimer, int which, const void *new_value, void *old_value) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, setitimer, which, new_value, old_value); if (new_value) COMMON_INTERCEPTOR_READ_RANGE(ctx, new_value, struct_itimerval_sz); // FIXME: under ASan the call below may write to freed memory and corrupt // its metadata. See // https://github.com/google/sanitizers/issues/321. int res = REAL(setitimer)(which, new_value, old_value); if (!res && old_value) { COMMON_INTERCEPTOR_WRITE_RANGE(ctx, old_value, struct_itimerval_sz); } return res; } #define INIT_GETITIMER \ COMMON_INTERCEPT_FUNCTION(getitimer); \ COMMON_INTERCEPT_FUNCTION(setitimer); #else #define INIT_GETITIMER #endif #if SANITIZER_INTERCEPT_GLOB static void unpoison_glob_t(void *ctx, __sanitizer_glob_t *pglob) { COMMON_INTERCEPTOR_WRITE_RANGE(ctx, pglob, sizeof(*pglob)); // +1 for NULL pointer at the end. if (pglob->gl_pathv) COMMON_INTERCEPTOR_WRITE_RANGE( ctx, pglob->gl_pathv, (pglob->gl_pathc + 1) * sizeof(*pglob->gl_pathv)); for (SIZE_T i = 0; i < pglob->gl_pathc; ++i) { char *p = pglob->gl_pathv[i]; COMMON_INTERCEPTOR_WRITE_RANGE(ctx, p, REAL(strlen)(p) + 1); } } static THREADLOCAL __sanitizer_glob_t *pglob_copy; static void wrapped_gl_closedir(void *dir) { COMMON_INTERCEPTOR_UNPOISON_PARAM(1); pglob_copy->gl_closedir(dir); } static void *wrapped_gl_readdir(void *dir) { COMMON_INTERCEPTOR_UNPOISON_PARAM(1); return pglob_copy->gl_readdir(dir); } static void *wrapped_gl_opendir(const char *s) { COMMON_INTERCEPTOR_UNPOISON_PARAM(1); COMMON_INTERCEPTOR_INITIALIZE_RANGE(s, REAL(strlen)(s) + 1); return pglob_copy->gl_opendir(s); } static int wrapped_gl_lstat(const char *s, void *st) { COMMON_INTERCEPTOR_UNPOISON_PARAM(2); COMMON_INTERCEPTOR_INITIALIZE_RANGE(s, REAL(strlen)(s) + 1); return pglob_copy->gl_lstat(s, st); } static int wrapped_gl_stat(const char *s, void *st) { COMMON_INTERCEPTOR_UNPOISON_PARAM(2); COMMON_INTERCEPTOR_INITIALIZE_RANGE(s, REAL(strlen)(s) + 1); return pglob_copy->gl_stat(s, st); } static const __sanitizer_glob_t kGlobCopy = { 0, 0, 0, 0, wrapped_gl_closedir, wrapped_gl_readdir, wrapped_gl_opendir, wrapped_gl_lstat, wrapped_gl_stat}; INTERCEPTOR(int, glob, const char *pattern, int flags, int (*errfunc)(const char *epath, int eerrno), __sanitizer_glob_t *pglob) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, glob, pattern, flags, errfunc, pglob); COMMON_INTERCEPTOR_READ_STRING(ctx, pattern, 0); __sanitizer_glob_t glob_copy; internal_memcpy(&glob_copy, &kGlobCopy, sizeof(glob_copy)); if (flags & glob_altdirfunc) { Swap(pglob->gl_closedir, glob_copy.gl_closedir); Swap(pglob->gl_readdir, glob_copy.gl_readdir); Swap(pglob->gl_opendir, glob_copy.gl_opendir); Swap(pglob->gl_lstat, glob_copy.gl_lstat); Swap(pglob->gl_stat, glob_copy.gl_stat); pglob_copy = &glob_copy; } int res = REAL(glob)(pattern, flags, errfunc, pglob); if (flags & glob_altdirfunc) { Swap(pglob->gl_closedir, glob_copy.gl_closedir); Swap(pglob->gl_readdir, glob_copy.gl_readdir); Swap(pglob->gl_opendir, glob_copy.gl_opendir); Swap(pglob->gl_lstat, glob_copy.gl_lstat); Swap(pglob->gl_stat, glob_copy.gl_stat); } pglob_copy = 0; if ((!res || res == glob_nomatch) && pglob) unpoison_glob_t(ctx, pglob); return res; } INTERCEPTOR(int, glob64, const char *pattern, int flags, int (*errfunc)(const char *epath, int eerrno), __sanitizer_glob_t *pglob) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, glob64, pattern, flags, errfunc, pglob); COMMON_INTERCEPTOR_READ_STRING(ctx, pattern, 0); __sanitizer_glob_t glob_copy; internal_memcpy(&glob_copy, &kGlobCopy, sizeof(glob_copy)); if (flags & glob_altdirfunc) { Swap(pglob->gl_closedir, glob_copy.gl_closedir); Swap(pglob->gl_readdir, glob_copy.gl_readdir); Swap(pglob->gl_opendir, glob_copy.gl_opendir); Swap(pglob->gl_lstat, glob_copy.gl_lstat); Swap(pglob->gl_stat, glob_copy.gl_stat); pglob_copy = &glob_copy; } int res = REAL(glob64)(pattern, flags, errfunc, pglob); if (flags & glob_altdirfunc) { Swap(pglob->gl_closedir, glob_copy.gl_closedir); Swap(pglob->gl_readdir, glob_copy.gl_readdir); Swap(pglob->gl_opendir, glob_copy.gl_opendir); Swap(pglob->gl_lstat, glob_copy.gl_lstat); Swap(pglob->gl_stat, glob_copy.gl_stat); } pglob_copy = 0; if ((!res || res == glob_nomatch) && pglob) unpoison_glob_t(ctx, pglob); return res; } #define INIT_GLOB \ COMMON_INTERCEPT_FUNCTION(glob); \ COMMON_INTERCEPT_FUNCTION(glob64); #else // SANITIZER_INTERCEPT_GLOB #define INIT_GLOB #endif // SANITIZER_INTERCEPT_GLOB #if SANITIZER_INTERCEPT_WAIT // According to sys/wait.h, wait(), waitid(), waitpid() may have symbol version // suffixes on Darwin. See the declaration of INTERCEPTOR_WITH_SUFFIX for // details. INTERCEPTOR_WITH_SUFFIX(int, wait, int *status) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, wait, status); // FIXME: under ASan the call below may write to freed memory and corrupt // its metadata. See // https://github.com/google/sanitizers/issues/321. int res = REAL(wait)(status); if (res != -1 && status) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, status, sizeof(*status)); return res; } // On FreeBSD id_t is always 64-bit wide. #if SANITIZER_FREEBSD && (SANITIZER_WORDSIZE == 32) INTERCEPTOR_WITH_SUFFIX(int, waitid, int idtype, long long id, void *infop, int options) { #else INTERCEPTOR_WITH_SUFFIX(int, waitid, int idtype, int id, void *infop, int options) { #endif void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, waitid, idtype, id, infop, options); // FIXME: under ASan the call below may write to freed memory and corrupt // its metadata. See // https://github.com/google/sanitizers/issues/321. int res = REAL(waitid)(idtype, id, infop, options); if (res != -1 && infop) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, infop, siginfo_t_sz); return res; } INTERCEPTOR_WITH_SUFFIX(int, waitpid, int pid, int *status, int options) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, waitpid, pid, status, options); // FIXME: under ASan the call below may write to freed memory and corrupt // its metadata. See // https://github.com/google/sanitizers/issues/321. int res = REAL(waitpid)(pid, status, options); if (res != -1 && status) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, status, sizeof(*status)); return res; } INTERCEPTOR(int, wait3, int *status, int options, void *rusage) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, wait3, status, options, rusage); // FIXME: under ASan the call below may write to freed memory and corrupt // its metadata. See // https://github.com/google/sanitizers/issues/321. int res = REAL(wait3)(status, options, rusage); if (res != -1) { if (status) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, status, sizeof(*status)); if (rusage) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, rusage, struct_rusage_sz); } return res; } #if SANITIZER_ANDROID INTERCEPTOR(int, __wait4, int pid, int *status, int options, void *rusage) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, __wait4, pid, status, options, rusage); // FIXME: under ASan the call below may write to freed memory and corrupt // its metadata. See // https://github.com/google/sanitizers/issues/321. int res = REAL(__wait4)(pid, status, options, rusage); if (res != -1) { if (status) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, status, sizeof(*status)); if (rusage) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, rusage, struct_rusage_sz); } return res; } #define INIT_WAIT4 COMMON_INTERCEPT_FUNCTION(__wait4); #else INTERCEPTOR(int, wait4, int pid, int *status, int options, void *rusage) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, wait4, pid, status, options, rusage); // FIXME: under ASan the call below may write to freed memory and corrupt // its metadata. See // https://github.com/google/sanitizers/issues/321. int res = REAL(wait4)(pid, status, options, rusage); if (res != -1) { if (status) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, status, sizeof(*status)); if (rusage) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, rusage, struct_rusage_sz); } return res; } #define INIT_WAIT4 COMMON_INTERCEPT_FUNCTION(wait4); #endif // SANITIZER_ANDROID #define INIT_WAIT \ COMMON_INTERCEPT_FUNCTION(wait); \ COMMON_INTERCEPT_FUNCTION(waitid); \ COMMON_INTERCEPT_FUNCTION(waitpid); \ COMMON_INTERCEPT_FUNCTION(wait3); #else #define INIT_WAIT #define INIT_WAIT4 #endif #if SANITIZER_INTERCEPT_INET INTERCEPTOR(char *, inet_ntop, int af, const void *src, char *dst, u32 size) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, inet_ntop, af, src, dst, size); uptr sz = __sanitizer_in_addr_sz(af); if (sz) COMMON_INTERCEPTOR_READ_RANGE(ctx, src, sz); // FIXME: figure out read size based on the address family. // FIXME: under ASan the call below may write to freed memory and corrupt // its metadata. See // https://github.com/google/sanitizers/issues/321. char *res = REAL(inet_ntop)(af, src, dst, size); if (res) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, res, REAL(strlen)(res) + 1); return res; } INTERCEPTOR(int, inet_pton, int af, const char *src, void *dst) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, inet_pton, af, src, dst); COMMON_INTERCEPTOR_READ_STRING(ctx, src, 0); // FIXME: figure out read size based on the address family. // FIXME: under ASan the call below may write to freed memory and corrupt // its metadata. See // https://github.com/google/sanitizers/issues/321. int res = REAL(inet_pton)(af, src, dst); if (res == 1) { uptr sz = __sanitizer_in_addr_sz(af); if (sz) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, dst, sz); } return res; } #define INIT_INET \ COMMON_INTERCEPT_FUNCTION(inet_ntop); \ COMMON_INTERCEPT_FUNCTION(inet_pton); #else #define INIT_INET #endif #if SANITIZER_INTERCEPT_INET INTERCEPTOR(int, inet_aton, const char *cp, void *dst) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, inet_aton, cp, dst); if (cp) COMMON_INTERCEPTOR_READ_RANGE(ctx, cp, REAL(strlen)(cp) + 1); // FIXME: under ASan the call below may write to freed memory and corrupt // its metadata. See // https://github.com/google/sanitizers/issues/321. int res = REAL(inet_aton)(cp, dst); if (res != 0) { uptr sz = __sanitizer_in_addr_sz(af_inet); if (sz) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, dst, sz); } return res; } #define INIT_INET_ATON COMMON_INTERCEPT_FUNCTION(inet_aton); #else #define INIT_INET_ATON #endif #if SANITIZER_INTERCEPT_PTHREAD_GETSCHEDPARAM INTERCEPTOR(int, pthread_getschedparam, uptr thread, int *policy, int *param) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, pthread_getschedparam, thread, policy, param); // FIXME: under ASan the call below may write to freed memory and corrupt // its metadata. See // https://github.com/google/sanitizers/issues/321. int res = REAL(pthread_getschedparam)(thread, policy, param); if (res == 0) { if (policy) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, policy, sizeof(*policy)); if (param) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, param, sizeof(*param)); } return res; } #define INIT_PTHREAD_GETSCHEDPARAM \ COMMON_INTERCEPT_FUNCTION(pthread_getschedparam); #else #define INIT_PTHREAD_GETSCHEDPARAM #endif #if SANITIZER_INTERCEPT_GETADDRINFO INTERCEPTOR(int, getaddrinfo, char *node, char *service, struct __sanitizer_addrinfo *hints, struct __sanitizer_addrinfo **out) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, getaddrinfo, node, service, hints, out); if (node) COMMON_INTERCEPTOR_READ_RANGE(ctx, node, REAL(strlen)(node) + 1); if (service) COMMON_INTERCEPTOR_READ_RANGE(ctx, service, REAL(strlen)(service) + 1); if (hints) COMMON_INTERCEPTOR_READ_RANGE(ctx, hints, sizeof(__sanitizer_addrinfo)); // FIXME: under ASan the call below may write to freed memory and corrupt // its metadata. See // https://github.com/google/sanitizers/issues/321. int res = REAL(getaddrinfo)(node, service, hints, out); if (res == 0 && out) { COMMON_INTERCEPTOR_WRITE_RANGE(ctx, out, sizeof(*out)); struct __sanitizer_addrinfo *p = *out; while (p) { COMMON_INTERCEPTOR_WRITE_RANGE(ctx, p, sizeof(*p)); if (p->ai_addr) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, p->ai_addr, p->ai_addrlen); if (p->ai_canonname) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, p->ai_canonname, REAL(strlen)(p->ai_canonname) + 1); p = p->ai_next; } } return res; } #define INIT_GETADDRINFO COMMON_INTERCEPT_FUNCTION(getaddrinfo); #else #define INIT_GETADDRINFO #endif #if SANITIZER_INTERCEPT_GETNAMEINFO INTERCEPTOR(int, getnameinfo, void *sockaddr, unsigned salen, char *host, unsigned hostlen, char *serv, unsigned servlen, int flags) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, getnameinfo, sockaddr, salen, host, hostlen, serv, servlen, flags); // FIXME: consider adding READ_RANGE(sockaddr, salen) // There is padding in in_addr that may make this too noisy // FIXME: under ASan the call below may write to freed memory and corrupt // its metadata. See // https://github.com/google/sanitizers/issues/321. int res = REAL(getnameinfo)(sockaddr, salen, host, hostlen, serv, servlen, flags); if (res == 0) { if (host && hostlen) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, host, REAL(strlen)(host) + 1); if (serv && servlen) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, serv, REAL(strlen)(serv) + 1); } return res; } #define INIT_GETNAMEINFO COMMON_INTERCEPT_FUNCTION(getnameinfo); #else #define INIT_GETNAMEINFO #endif #if SANITIZER_INTERCEPT_GETSOCKNAME INTERCEPTOR(int, getsockname, int sock_fd, void *addr, int *addrlen) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, getsockname, sock_fd, addr, addrlen); COMMON_INTERCEPTOR_READ_RANGE(ctx, addrlen, sizeof(*addrlen)); int addrlen_in = *addrlen; // FIXME: under ASan the call below may write to freed memory and corrupt // its metadata. See // https://github.com/google/sanitizers/issues/321. int res = REAL(getsockname)(sock_fd, addr, addrlen); if (res == 0) { COMMON_INTERCEPTOR_WRITE_RANGE(ctx, addr, Min(addrlen_in, *addrlen)); } return res; } #define INIT_GETSOCKNAME COMMON_INTERCEPT_FUNCTION(getsockname); #else #define INIT_GETSOCKNAME #endif #if SANITIZER_INTERCEPT_GETHOSTBYNAME || SANITIZER_INTERCEPT_GETHOSTBYNAME_R static void write_hostent(void *ctx, struct __sanitizer_hostent *h) { COMMON_INTERCEPTOR_WRITE_RANGE(ctx, h, sizeof(__sanitizer_hostent)); if (h->h_name) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, h->h_name, REAL(strlen)(h->h_name) + 1); char **p = h->h_aliases; while (*p) { COMMON_INTERCEPTOR_WRITE_RANGE(ctx, *p, REAL(strlen)(*p) + 1); ++p; } COMMON_INTERCEPTOR_WRITE_RANGE( ctx, h->h_aliases, (p - h->h_aliases + 1) * sizeof(*h->h_aliases)); p = h->h_addr_list; while (*p) { COMMON_INTERCEPTOR_WRITE_RANGE(ctx, *p, h->h_length); ++p; } COMMON_INTERCEPTOR_WRITE_RANGE( ctx, h->h_addr_list, (p - h->h_addr_list + 1) * sizeof(*h->h_addr_list)); } #endif #if SANITIZER_INTERCEPT_GETHOSTBYNAME INTERCEPTOR(struct __sanitizer_hostent *, gethostbyname, char *name) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, gethostbyname, name); struct __sanitizer_hostent *res = REAL(gethostbyname)(name); if (res) write_hostent(ctx, res); return res; } INTERCEPTOR(struct __sanitizer_hostent *, gethostbyaddr, void *addr, int len, int type) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, gethostbyaddr, addr, len, type); COMMON_INTERCEPTOR_READ_RANGE(ctx, addr, len); struct __sanitizer_hostent *res = REAL(gethostbyaddr)(addr, len, type); if (res) write_hostent(ctx, res); return res; } INTERCEPTOR(struct __sanitizer_hostent *, gethostent, int fake) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, gethostent, fake); struct __sanitizer_hostent *res = REAL(gethostent)(fake); if (res) write_hostent(ctx, res); return res; } INTERCEPTOR(struct __sanitizer_hostent *, gethostbyname2, char *name, int af) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, gethostbyname2, name, af); struct __sanitizer_hostent *res = REAL(gethostbyname2)(name, af); if (res) write_hostent(ctx, res); return res; } #define INIT_GETHOSTBYNAME \ COMMON_INTERCEPT_FUNCTION(gethostent); \ COMMON_INTERCEPT_FUNCTION(gethostbyaddr); \ COMMON_INTERCEPT_FUNCTION(gethostbyname); \ COMMON_INTERCEPT_FUNCTION(gethostbyname2); #else #define INIT_GETHOSTBYNAME #endif #if SANITIZER_INTERCEPT_GETHOSTBYNAME_R INTERCEPTOR(int, gethostbyname_r, char *name, struct __sanitizer_hostent *ret, char *buf, SIZE_T buflen, __sanitizer_hostent **result, int *h_errnop) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, gethostbyname_r, name, ret, buf, buflen, result, h_errnop); // FIXME: under ASan the call below may write to freed memory and corrupt // its metadata. See // https://github.com/google/sanitizers/issues/321. int res = REAL(gethostbyname_r)(name, ret, buf, buflen, result, h_errnop); if (result) { COMMON_INTERCEPTOR_WRITE_RANGE(ctx, result, sizeof(*result)); if (res == 0 && *result) write_hostent(ctx, *result); } if (h_errnop) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, h_errnop, sizeof(*h_errnop)); return res; } #define INIT_GETHOSTBYNAME_R COMMON_INTERCEPT_FUNCTION(gethostbyname_r); #else #define INIT_GETHOSTBYNAME_R #endif #if SANITIZER_INTERCEPT_GETHOSTENT_R INTERCEPTOR(int, gethostent_r, struct __sanitizer_hostent *ret, char *buf, SIZE_T buflen, __sanitizer_hostent **result, int *h_errnop) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, gethostent_r, ret, buf, buflen, result, h_errnop); // FIXME: under ASan the call below may write to freed memory and corrupt // its metadata. See // https://github.com/google/sanitizers/issues/321. int res = REAL(gethostent_r)(ret, buf, buflen, result, h_errnop); if (result) { COMMON_INTERCEPTOR_WRITE_RANGE(ctx, result, sizeof(*result)); if (res == 0 && *result) write_hostent(ctx, *result); } if (h_errnop) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, h_errnop, sizeof(*h_errnop)); return res; } #define INIT_GETHOSTENT_R \ COMMON_INTERCEPT_FUNCTION(gethostent_r); #else #define INIT_GETHOSTENT_R #endif #if SANITIZER_INTERCEPT_GETHOSTBYADDR_R INTERCEPTOR(int, gethostbyaddr_r, void *addr, int len, int type, struct __sanitizer_hostent *ret, char *buf, SIZE_T buflen, __sanitizer_hostent **result, int *h_errnop) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, gethostbyaddr_r, addr, len, type, ret, buf, buflen, result, h_errnop); COMMON_INTERCEPTOR_READ_RANGE(ctx, addr, len); // FIXME: under ASan the call below may write to freed memory and corrupt // its metadata. See // https://github.com/google/sanitizers/issues/321. int res = REAL(gethostbyaddr_r)(addr, len, type, ret, buf, buflen, result, h_errnop); if (result) { COMMON_INTERCEPTOR_WRITE_RANGE(ctx, result, sizeof(*result)); if (res == 0 && *result) write_hostent(ctx, *result); } if (h_errnop) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, h_errnop, sizeof(*h_errnop)); return res; } #define INIT_GETHOSTBYADDR_R \ COMMON_INTERCEPT_FUNCTION(gethostbyaddr_r); #else #define INIT_GETHOSTBYADDR_R #endif #if SANITIZER_INTERCEPT_GETHOSTBYNAME2_R INTERCEPTOR(int, gethostbyname2_r, char *name, int af, struct __sanitizer_hostent *ret, char *buf, SIZE_T buflen, __sanitizer_hostent **result, int *h_errnop) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, gethostbyname2_r, name, af, ret, buf, buflen, result, h_errnop); // FIXME: under ASan the call below may write to freed memory and corrupt // its metadata. See // https://github.com/google/sanitizers/issues/321. int res = REAL(gethostbyname2_r)(name, af, ret, buf, buflen, result, h_errnop); if (result) { COMMON_INTERCEPTOR_WRITE_RANGE(ctx, result, sizeof(*result)); if (res == 0 && *result) write_hostent(ctx, *result); } if (h_errnop) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, h_errnop, sizeof(*h_errnop)); return res; } #define INIT_GETHOSTBYNAME2_R \ COMMON_INTERCEPT_FUNCTION(gethostbyname2_r); #else #define INIT_GETHOSTBYNAME2_R #endif #if SANITIZER_INTERCEPT_GETSOCKOPT INTERCEPTOR(int, getsockopt, int sockfd, int level, int optname, void *optval, int *optlen) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, getsockopt, sockfd, level, optname, optval, optlen); if (optlen) COMMON_INTERCEPTOR_READ_RANGE(ctx, optlen, sizeof(*optlen)); // FIXME: under ASan the call below may write to freed memory and corrupt // its metadata. See // https://github.com/google/sanitizers/issues/321. int res = REAL(getsockopt)(sockfd, level, optname, optval, optlen); if (res == 0) if (optval && optlen) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, optval, *optlen); return res; } #define INIT_GETSOCKOPT COMMON_INTERCEPT_FUNCTION(getsockopt); #else #define INIT_GETSOCKOPT #endif #if SANITIZER_INTERCEPT_ACCEPT INTERCEPTOR(int, accept, int fd, void *addr, unsigned *addrlen) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, accept, fd, addr, addrlen); unsigned addrlen0 = 0; if (addrlen) { COMMON_INTERCEPTOR_READ_RANGE(ctx, addrlen, sizeof(*addrlen)); addrlen0 = *addrlen; } int fd2 = REAL(accept)(fd, addr, addrlen); if (fd2 >= 0) { if (fd >= 0) COMMON_INTERCEPTOR_FD_SOCKET_ACCEPT(ctx, fd, fd2); if (addr && addrlen) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, addr, Min(*addrlen, addrlen0)); } return fd2; } #define INIT_ACCEPT COMMON_INTERCEPT_FUNCTION(accept); #else #define INIT_ACCEPT #endif #if SANITIZER_INTERCEPT_ACCEPT4 INTERCEPTOR(int, accept4, int fd, void *addr, unsigned *addrlen, int f) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, accept4, fd, addr, addrlen, f); unsigned addrlen0 = 0; if (addrlen) { COMMON_INTERCEPTOR_READ_RANGE(ctx, addrlen, sizeof(*addrlen)); addrlen0 = *addrlen; } // FIXME: under ASan the call below may write to freed memory and corrupt // its metadata. See // https://github.com/google/sanitizers/issues/321. int fd2 = REAL(accept4)(fd, addr, addrlen, f); if (fd2 >= 0) { if (fd >= 0) COMMON_INTERCEPTOR_FD_SOCKET_ACCEPT(ctx, fd, fd2); if (addr && addrlen) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, addr, Min(*addrlen, addrlen0)); } return fd2; } #define INIT_ACCEPT4 COMMON_INTERCEPT_FUNCTION(accept4); #else #define INIT_ACCEPT4 #endif #if SANITIZER_INTERCEPT_MODF INTERCEPTOR(double, modf, double x, double *iptr) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, modf, x, iptr); // FIXME: under ASan the call below may write to freed memory and corrupt // its metadata. See // https://github.com/google/sanitizers/issues/321. double res = REAL(modf)(x, iptr); if (iptr) { COMMON_INTERCEPTOR_WRITE_RANGE(ctx, iptr, sizeof(*iptr)); } return res; } INTERCEPTOR(float, modff, float x, float *iptr) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, modff, x, iptr); // FIXME: under ASan the call below may write to freed memory and corrupt // its metadata. See // https://github.com/google/sanitizers/issues/321. float res = REAL(modff)(x, iptr); if (iptr) { COMMON_INTERCEPTOR_WRITE_RANGE(ctx, iptr, sizeof(*iptr)); } return res; } INTERCEPTOR(long double, modfl, long double x, long double *iptr) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, modfl, x, iptr); // FIXME: under ASan the call below may write to freed memory and corrupt // its metadata. See // https://github.com/google/sanitizers/issues/321. long double res = REAL(modfl)(x, iptr); if (iptr) { COMMON_INTERCEPTOR_WRITE_RANGE(ctx, iptr, sizeof(*iptr)); } return res; } #define INIT_MODF \ COMMON_INTERCEPT_FUNCTION(modf); \ COMMON_INTERCEPT_FUNCTION(modff); \ COMMON_INTERCEPT_FUNCTION_LDBL(modfl); #else #define INIT_MODF #endif #if SANITIZER_INTERCEPT_RECVMSG static void write_msghdr(void *ctx, struct __sanitizer_msghdr *msg, SSIZE_T maxlen) { COMMON_INTERCEPTOR_WRITE_RANGE(ctx, msg, sizeof(*msg)); if (msg->msg_name && msg->msg_namelen) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, msg->msg_name, msg->msg_namelen); if (msg->msg_iov && msg->msg_iovlen) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, msg->msg_iov, sizeof(*msg->msg_iov) * msg->msg_iovlen); write_iovec(ctx, msg->msg_iov, msg->msg_iovlen, maxlen); if (msg->msg_control && msg->msg_controllen) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, msg->msg_control, msg->msg_controllen); } INTERCEPTOR(SSIZE_T, recvmsg, int fd, struct __sanitizer_msghdr *msg, int flags) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, recvmsg, fd, msg, flags); // FIXME: under ASan the call below may write to freed memory and corrupt // its metadata. See // https://github.com/google/sanitizers/issues/321. SSIZE_T res = REAL(recvmsg)(fd, msg, flags); if (res >= 0) { if (fd >= 0) COMMON_INTERCEPTOR_FD_ACQUIRE(ctx, fd); if (msg) { write_msghdr(ctx, msg, res); COMMON_INTERCEPTOR_HANDLE_RECVMSG(ctx, msg); } } return res; } #define INIT_RECVMSG COMMON_INTERCEPT_FUNCTION(recvmsg); #else #define INIT_RECVMSG #endif #if SANITIZER_INTERCEPT_SENDMSG static void read_msghdr_control(void *ctx, void *control, uptr controllen) { const unsigned kCmsgDataOffset = RoundUpTo(sizeof(__sanitizer_cmsghdr), sizeof(uptr)); char *p = (char *)control; char *const control_end = p + controllen; while (true) { if (p + sizeof(__sanitizer_cmsghdr) > control_end) break; __sanitizer_cmsghdr *cmsg = (__sanitizer_cmsghdr *)p; COMMON_INTERCEPTOR_READ_RANGE(ctx, &cmsg->cmsg_len, sizeof(cmsg->cmsg_len)); if (p + RoundUpTo(cmsg->cmsg_len, sizeof(uptr)) > control_end) break; COMMON_INTERCEPTOR_READ_RANGE(ctx, &cmsg->cmsg_level, sizeof(cmsg->cmsg_level)); COMMON_INTERCEPTOR_READ_RANGE(ctx, &cmsg->cmsg_type, sizeof(cmsg->cmsg_type)); if (cmsg->cmsg_len > kCmsgDataOffset) { char *data = p + kCmsgDataOffset; unsigned data_len = cmsg->cmsg_len - kCmsgDataOffset; if (data_len > 0) COMMON_INTERCEPTOR_READ_RANGE(ctx, data, data_len); } p += RoundUpTo(cmsg->cmsg_len, sizeof(uptr)); } } static void read_msghdr(void *ctx, struct __sanitizer_msghdr *msg, SSIZE_T maxlen) { #define R(f) \ COMMON_INTERCEPTOR_READ_RANGE(ctx, &msg->msg_##f, sizeof(msg->msg_##f)) R(name); R(namelen); R(iov); R(iovlen); R(control); R(controllen); R(flags); #undef R if (msg->msg_name && msg->msg_namelen) COMMON_INTERCEPTOR_READ_RANGE(ctx, msg->msg_name, msg->msg_namelen); if (msg->msg_iov && msg->msg_iovlen) COMMON_INTERCEPTOR_READ_RANGE(ctx, msg->msg_iov, sizeof(*msg->msg_iov) * msg->msg_iovlen); read_iovec(ctx, msg->msg_iov, msg->msg_iovlen, maxlen); if (msg->msg_control && msg->msg_controllen) read_msghdr_control(ctx, msg->msg_control, msg->msg_controllen); } INTERCEPTOR(SSIZE_T, sendmsg, int fd, struct __sanitizer_msghdr *msg, int flags) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, sendmsg, fd, msg, flags); if (fd >= 0) { COMMON_INTERCEPTOR_FD_ACCESS(ctx, fd); COMMON_INTERCEPTOR_FD_RELEASE(ctx, fd); } SSIZE_T res = REAL(sendmsg)(fd, msg, flags); if (common_flags()->intercept_send && res >= 0 && msg) read_msghdr(ctx, msg, res); return res; } #define INIT_SENDMSG COMMON_INTERCEPT_FUNCTION(sendmsg); #else #define INIT_SENDMSG #endif #if SANITIZER_INTERCEPT_GETPEERNAME INTERCEPTOR(int, getpeername, int sockfd, void *addr, unsigned *addrlen) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, getpeername, sockfd, addr, addrlen); unsigned addr_sz; if (addrlen) addr_sz = *addrlen; // FIXME: under ASan the call below may write to freed memory and corrupt // its metadata. See // https://github.com/google/sanitizers/issues/321. int res = REAL(getpeername)(sockfd, addr, addrlen); if (!res && addr && addrlen) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, addr, Min(addr_sz, *addrlen)); return res; } #define INIT_GETPEERNAME COMMON_INTERCEPT_FUNCTION(getpeername); #else #define INIT_GETPEERNAME #endif #if SANITIZER_INTERCEPT_SYSINFO INTERCEPTOR(int, sysinfo, void *info) { void *ctx; // FIXME: under ASan the call below may write to freed memory and corrupt // its metadata. See // https://github.com/google/sanitizers/issues/321. COMMON_INTERCEPTOR_ENTER(ctx, sysinfo, info); int res = REAL(sysinfo)(info); if (!res && info) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, info, struct_sysinfo_sz); return res; } #define INIT_SYSINFO COMMON_INTERCEPT_FUNCTION(sysinfo); #else #define INIT_SYSINFO #endif #if SANITIZER_INTERCEPT_READDIR INTERCEPTOR(__sanitizer_dirent *, opendir, const char *path) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, opendir, path); COMMON_INTERCEPTOR_READ_RANGE(ctx, path, REAL(strlen)(path) + 1); __sanitizer_dirent *res = REAL(opendir)(path); if (res) COMMON_INTERCEPTOR_DIR_ACQUIRE(ctx, path); return res; } INTERCEPTOR(__sanitizer_dirent *, readdir, void *dirp) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, readdir, dirp); // FIXME: under ASan the call below may write to freed memory and corrupt // its metadata. See // https://github.com/google/sanitizers/issues/321. __sanitizer_dirent *res = REAL(readdir)(dirp); if (res) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, res, res->d_reclen); return res; } INTERCEPTOR(int, readdir_r, void *dirp, __sanitizer_dirent *entry, __sanitizer_dirent **result) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, readdir_r, dirp, entry, result); // FIXME: under ASan the call below may write to freed memory and corrupt // its metadata. See // https://github.com/google/sanitizers/issues/321. int res = REAL(readdir_r)(dirp, entry, result); if (!res) { COMMON_INTERCEPTOR_WRITE_RANGE(ctx, result, sizeof(*result)); if (*result) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, *result, (*result)->d_reclen); } return res; } #define INIT_READDIR \ COMMON_INTERCEPT_FUNCTION(opendir); \ COMMON_INTERCEPT_FUNCTION(readdir); \ COMMON_INTERCEPT_FUNCTION(readdir_r); #else #define INIT_READDIR #endif #if SANITIZER_INTERCEPT_READDIR64 INTERCEPTOR(__sanitizer_dirent64 *, readdir64, void *dirp) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, readdir64, dirp); // FIXME: under ASan the call below may write to freed memory and corrupt // its metadata. See // https://github.com/google/sanitizers/issues/321. __sanitizer_dirent64 *res = REAL(readdir64)(dirp); if (res) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, res, res->d_reclen); return res; } INTERCEPTOR(int, readdir64_r, void *dirp, __sanitizer_dirent64 *entry, __sanitizer_dirent64 **result) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, readdir64_r, dirp, entry, result); // FIXME: under ASan the call below may write to freed memory and corrupt // its metadata. See // https://github.com/google/sanitizers/issues/321. int res = REAL(readdir64_r)(dirp, entry, result); if (!res) { COMMON_INTERCEPTOR_WRITE_RANGE(ctx, result, sizeof(*result)); if (*result) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, *result, (*result)->d_reclen); } return res; } #define INIT_READDIR64 \ COMMON_INTERCEPT_FUNCTION(readdir64); \ COMMON_INTERCEPT_FUNCTION(readdir64_r); #else #define INIT_READDIR64 #endif #if SANITIZER_INTERCEPT_PTRACE INTERCEPTOR(uptr, ptrace, int request, int pid, void *addr, void *data) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, ptrace, request, pid, addr, data); __sanitizer_iovec local_iovec; if (data) { if (request == ptrace_setregs) COMMON_INTERCEPTOR_READ_RANGE(ctx, data, struct_user_regs_struct_sz); else if (request == ptrace_setfpregs) COMMON_INTERCEPTOR_READ_RANGE(ctx, data, struct_user_fpregs_struct_sz); else if (request == ptrace_setfpxregs) COMMON_INTERCEPTOR_READ_RANGE(ctx, data, struct_user_fpxregs_struct_sz); else if (request == ptrace_setvfpregs) COMMON_INTERCEPTOR_READ_RANGE(ctx, data, struct_user_vfpregs_struct_sz); else if (request == ptrace_setsiginfo) COMMON_INTERCEPTOR_READ_RANGE(ctx, data, siginfo_t_sz); // Some kernel might zero the iovec::iov_base in case of invalid // write access. In this case copy the invalid address for further // inspection. else if (request == ptrace_setregset || request == ptrace_getregset) { __sanitizer_iovec *iovec = (__sanitizer_iovec*)data; COMMON_INTERCEPTOR_READ_RANGE(ctx, iovec, sizeof(*iovec)); local_iovec = *iovec; if (request == ptrace_setregset) COMMON_INTERCEPTOR_READ_RANGE(ctx, iovec->iov_base, iovec->iov_len); } } // FIXME: under ASan the call below may write to freed memory and corrupt // its metadata. See // https://github.com/google/sanitizers/issues/321. uptr res = REAL(ptrace)(request, pid, addr, data); if (!res && data) { // Note that PEEK* requests assign different meaning to the return value. // This function does not handle them (nor does it need to). if (request == ptrace_getregs) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, data, struct_user_regs_struct_sz); else if (request == ptrace_getfpregs) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, data, struct_user_fpregs_struct_sz); else if (request == ptrace_getfpxregs) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, data, struct_user_fpxregs_struct_sz); else if (request == ptrace_getvfpregs) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, data, struct_user_vfpregs_struct_sz); else if (request == ptrace_getsiginfo) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, data, siginfo_t_sz); else if (request == ptrace_geteventmsg) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, data, sizeof(unsigned long)); else if (request == ptrace_getregset) { __sanitizer_iovec *iovec = (__sanitizer_iovec*)data; COMMON_INTERCEPTOR_WRITE_RANGE(ctx, iovec, sizeof(*iovec)); COMMON_INTERCEPTOR_WRITE_RANGE(ctx, local_iovec.iov_base, local_iovec.iov_len); } } return res; } #define INIT_PTRACE COMMON_INTERCEPT_FUNCTION(ptrace); #else #define INIT_PTRACE #endif #if SANITIZER_INTERCEPT_SETLOCALE INTERCEPTOR(char *, setlocale, int category, char *locale) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, setlocale, category, locale); if (locale) COMMON_INTERCEPTOR_READ_RANGE(ctx, locale, REAL(strlen)(locale) + 1); char *res = REAL(setlocale)(category, locale); if (res) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, res, REAL(strlen)(res) + 1); return res; } #define INIT_SETLOCALE COMMON_INTERCEPT_FUNCTION(setlocale); #else #define INIT_SETLOCALE #endif #if SANITIZER_INTERCEPT_GETCWD INTERCEPTOR(char *, getcwd, char *buf, SIZE_T size) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, getcwd, buf, size); // FIXME: under ASan the call below may write to freed memory and corrupt // its metadata. See // https://github.com/google/sanitizers/issues/321. char *res = REAL(getcwd)(buf, size); if (res) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, res, REAL(strlen)(res) + 1); return res; } #define INIT_GETCWD COMMON_INTERCEPT_FUNCTION(getcwd); #else #define INIT_GETCWD #endif #if SANITIZER_INTERCEPT_GET_CURRENT_DIR_NAME INTERCEPTOR(char *, get_current_dir_name, int fake) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, get_current_dir_name, fake); // FIXME: under ASan the call below may write to freed memory and corrupt // its metadata. See // https://github.com/google/sanitizers/issues/321. char *res = REAL(get_current_dir_name)(fake); if (res) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, res, REAL(strlen)(res) + 1); return res; } #define INIT_GET_CURRENT_DIR_NAME \ COMMON_INTERCEPT_FUNCTION(get_current_dir_name); #else #define INIT_GET_CURRENT_DIR_NAME #endif UNUSED static inline void FixRealStrtolEndptr(const char *nptr, char **endptr) { CHECK(endptr); if (nptr == *endptr) { // No digits were found at strtol call, we need to find out the last // symbol accessed by strtoll on our own. // We get this symbol by skipping leading blanks and optional +/- sign. while (IsSpace(*nptr)) nptr++; if (*nptr == '+' || *nptr == '-') nptr++; *endptr = const_cast(nptr); } CHECK(*endptr >= nptr); } UNUSED static inline void StrtolFixAndCheck(void *ctx, const char *nptr, char **endptr, char *real_endptr, int base) { if (endptr) { *endptr = real_endptr; COMMON_INTERCEPTOR_WRITE_RANGE(ctx, endptr, sizeof(*endptr)); } // If base has unsupported value, strtol can exit with EINVAL // without reading any characters. So do additional checks only // if base is valid. bool is_valid_base = (base == 0) || (2 <= base && base <= 36); if (is_valid_base) { FixRealStrtolEndptr(nptr, &real_endptr); } COMMON_INTERCEPTOR_READ_STRING(ctx, nptr, is_valid_base ? (real_endptr - nptr) + 1 : 0); } #if SANITIZER_INTERCEPT_STRTOIMAX INTERCEPTOR(INTMAX_T, strtoimax, const char *nptr, char **endptr, int base) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, strtoimax, nptr, endptr, base); // FIXME: under ASan the call below may write to freed memory and corrupt // its metadata. See // https://github.com/google/sanitizers/issues/321. char *real_endptr; INTMAX_T res = REAL(strtoimax)(nptr, &real_endptr, base); StrtolFixAndCheck(ctx, nptr, endptr, real_endptr, base); return res; } INTERCEPTOR(INTMAX_T, strtoumax, const char *nptr, char **endptr, int base) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, strtoumax, nptr, endptr, base); // FIXME: under ASan the call below may write to freed memory and corrupt // its metadata. See // https://github.com/google/sanitizers/issues/321. char *real_endptr; INTMAX_T res = REAL(strtoumax)(nptr, &real_endptr, base); StrtolFixAndCheck(ctx, nptr, endptr, real_endptr, base); return res; } #define INIT_STRTOIMAX \ COMMON_INTERCEPT_FUNCTION(strtoimax); \ COMMON_INTERCEPT_FUNCTION(strtoumax); #else #define INIT_STRTOIMAX #endif #if SANITIZER_INTERCEPT_MBSTOWCS INTERCEPTOR(SIZE_T, mbstowcs, wchar_t *dest, const char *src, SIZE_T len) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, mbstowcs, dest, src, len); // FIXME: under ASan the call below may write to freed memory and corrupt // its metadata. See // https://github.com/google/sanitizers/issues/321. SIZE_T res = REAL(mbstowcs)(dest, src, len); if (res != (SIZE_T) - 1 && dest) { SIZE_T write_cnt = res + (res < len); COMMON_INTERCEPTOR_WRITE_RANGE(ctx, dest, write_cnt * sizeof(wchar_t)); } return res; } INTERCEPTOR(SIZE_T, mbsrtowcs, wchar_t *dest, const char **src, SIZE_T len, void *ps) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, mbsrtowcs, dest, src, len, ps); if (src) COMMON_INTERCEPTOR_READ_RANGE(ctx, src, sizeof(*src)); if (ps) COMMON_INTERCEPTOR_READ_RANGE(ctx, ps, mbstate_t_sz); // FIXME: under ASan the call below may write to freed memory and corrupt // its metadata. See // https://github.com/google/sanitizers/issues/321. SIZE_T res = REAL(mbsrtowcs)(dest, src, len, ps); if (res != (SIZE_T)(-1) && dest && src) { // This function, and several others, may or may not write the terminating // \0 character. They write it iff they clear *src. SIZE_T write_cnt = res + !*src; COMMON_INTERCEPTOR_WRITE_RANGE(ctx, dest, write_cnt * sizeof(wchar_t)); } return res; } #define INIT_MBSTOWCS \ COMMON_INTERCEPT_FUNCTION(mbstowcs); \ COMMON_INTERCEPT_FUNCTION(mbsrtowcs); #else #define INIT_MBSTOWCS #endif #if SANITIZER_INTERCEPT_MBSNRTOWCS INTERCEPTOR(SIZE_T, mbsnrtowcs, wchar_t *dest, const char **src, SIZE_T nms, SIZE_T len, void *ps) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, mbsnrtowcs, dest, src, nms, len, ps); if (src) { COMMON_INTERCEPTOR_READ_RANGE(ctx, src, sizeof(*src)); if (nms) COMMON_INTERCEPTOR_READ_RANGE(ctx, *src, nms); } if (ps) COMMON_INTERCEPTOR_READ_RANGE(ctx, ps, mbstate_t_sz); // FIXME: under ASan the call below may write to freed memory and corrupt // its metadata. See // https://github.com/google/sanitizers/issues/321. SIZE_T res = REAL(mbsnrtowcs)(dest, src, nms, len, ps); if (res != (SIZE_T)(-1) && dest && src) { SIZE_T write_cnt = res + !*src; COMMON_INTERCEPTOR_WRITE_RANGE(ctx, dest, write_cnt * sizeof(wchar_t)); } return res; } #define INIT_MBSNRTOWCS COMMON_INTERCEPT_FUNCTION(mbsnrtowcs); #else #define INIT_MBSNRTOWCS #endif #if SANITIZER_INTERCEPT_WCSTOMBS INTERCEPTOR(SIZE_T, wcstombs, char *dest, const wchar_t *src, SIZE_T len) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, wcstombs, dest, src, len); // FIXME: under ASan the call below may write to freed memory and corrupt // its metadata. See // https://github.com/google/sanitizers/issues/321. SIZE_T res = REAL(wcstombs)(dest, src, len); if (res != (SIZE_T) - 1 && dest) { SIZE_T write_cnt = res + (res < len); COMMON_INTERCEPTOR_WRITE_RANGE(ctx, dest, write_cnt); } return res; } INTERCEPTOR(SIZE_T, wcsrtombs, char *dest, const wchar_t **src, SIZE_T len, void *ps) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, wcsrtombs, dest, src, len, ps); if (src) COMMON_INTERCEPTOR_READ_RANGE(ctx, src, sizeof(*src)); if (ps) COMMON_INTERCEPTOR_READ_RANGE(ctx, ps, mbstate_t_sz); // FIXME: under ASan the call below may write to freed memory and corrupt // its metadata. See // https://github.com/google/sanitizers/issues/321. SIZE_T res = REAL(wcsrtombs)(dest, src, len, ps); if (res != (SIZE_T) - 1 && dest && src) { SIZE_T write_cnt = res + !*src; COMMON_INTERCEPTOR_WRITE_RANGE(ctx, dest, write_cnt); } return res; } #define INIT_WCSTOMBS \ COMMON_INTERCEPT_FUNCTION(wcstombs); \ COMMON_INTERCEPT_FUNCTION(wcsrtombs); #else #define INIT_WCSTOMBS #endif #if SANITIZER_INTERCEPT_WCSNRTOMBS INTERCEPTOR(SIZE_T, wcsnrtombs, char *dest, const wchar_t **src, SIZE_T nms, SIZE_T len, void *ps) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, wcsnrtombs, dest, src, nms, len, ps); if (src) { COMMON_INTERCEPTOR_READ_RANGE(ctx, src, sizeof(*src)); if (nms) COMMON_INTERCEPTOR_READ_RANGE(ctx, *src, nms); } if (ps) COMMON_INTERCEPTOR_READ_RANGE(ctx, ps, mbstate_t_sz); // FIXME: under ASan the call below may write to freed memory and corrupt // its metadata. See // https://github.com/google/sanitizers/issues/321. SIZE_T res = REAL(wcsnrtombs)(dest, src, nms, len, ps); if (res != ((SIZE_T)-1) && dest && src) { SIZE_T write_cnt = res + !*src; COMMON_INTERCEPTOR_WRITE_RANGE(ctx, dest, write_cnt); } return res; } #define INIT_WCSNRTOMBS COMMON_INTERCEPT_FUNCTION(wcsnrtombs); #else #define INIT_WCSNRTOMBS #endif #if SANITIZER_INTERCEPT_WCRTOMB INTERCEPTOR(SIZE_T, wcrtomb, char *dest, wchar_t src, void *ps) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, wcrtomb, dest, src, ps); if (ps) COMMON_INTERCEPTOR_READ_RANGE(ctx, ps, mbstate_t_sz); // FIXME: under ASan the call below may write to freed memory and corrupt // its metadata. See // https://github.com/google/sanitizers/issues/321. SIZE_T res = REAL(wcrtomb)(dest, src, ps); if (res != ((SIZE_T)-1) && dest) { SIZE_T write_cnt = res; COMMON_INTERCEPTOR_WRITE_RANGE(ctx, dest, write_cnt); } return res; } #define INIT_WCRTOMB COMMON_INTERCEPT_FUNCTION(wcrtomb); #else #define INIT_WCRTOMB #endif #if SANITIZER_INTERCEPT_TCGETATTR INTERCEPTOR(int, tcgetattr, int fd, void *termios_p) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, tcgetattr, fd, termios_p); // FIXME: under ASan the call below may write to freed memory and corrupt // its metadata. See // https://github.com/google/sanitizers/issues/321. int res = REAL(tcgetattr)(fd, termios_p); if (!res && termios_p) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, termios_p, struct_termios_sz); return res; } #define INIT_TCGETATTR COMMON_INTERCEPT_FUNCTION(tcgetattr); #else #define INIT_TCGETATTR #endif #if SANITIZER_INTERCEPT_REALPATH INTERCEPTOR(char *, realpath, const char *path, char *resolved_path) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, realpath, path, resolved_path); if (path) COMMON_INTERCEPTOR_READ_RANGE(ctx, path, REAL(strlen)(path) + 1); // Workaround a bug in glibc where dlsym(RTLD_NEXT, ...) returns the oldest // version of a versioned symbol. For realpath(), this gives us something // (called __old_realpath) that does not handle NULL in the second argument. // Handle it as part of the interceptor. char *allocated_path = nullptr; if (!resolved_path) allocated_path = resolved_path = (char *)WRAP(malloc)(path_max + 1); char *res = REAL(realpath)(path, resolved_path); if (allocated_path && !res) WRAP(free)(allocated_path); if (res) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, res, REAL(strlen)(res) + 1); return res; } #define INIT_REALPATH COMMON_INTERCEPT_FUNCTION(realpath); #else #define INIT_REALPATH #endif #if SANITIZER_INTERCEPT_CANONICALIZE_FILE_NAME INTERCEPTOR(char *, canonicalize_file_name, const char *path) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, canonicalize_file_name, path); if (path) COMMON_INTERCEPTOR_READ_RANGE(ctx, path, REAL(strlen)(path) + 1); char *res = REAL(canonicalize_file_name)(path); if (res) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, res, REAL(strlen)(res) + 1); return res; } #define INIT_CANONICALIZE_FILE_NAME \ COMMON_INTERCEPT_FUNCTION(canonicalize_file_name); #else #define INIT_CANONICALIZE_FILE_NAME #endif #if SANITIZER_INTERCEPT_CONFSTR INTERCEPTOR(SIZE_T, confstr, int name, char *buf, SIZE_T len) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, confstr, name, buf, len); // FIXME: under ASan the call below may write to freed memory and corrupt // its metadata. See // https://github.com/google/sanitizers/issues/321. SIZE_T res = REAL(confstr)(name, buf, len); if (buf && res) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, buf, res < len ? res : len); return res; } #define INIT_CONFSTR COMMON_INTERCEPT_FUNCTION(confstr); #else #define INIT_CONFSTR #endif #if SANITIZER_INTERCEPT_SCHED_GETAFFINITY INTERCEPTOR(int, sched_getaffinity, int pid, SIZE_T cpusetsize, void *mask) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, sched_getaffinity, pid, cpusetsize, mask); // FIXME: under ASan the call below may write to freed memory and corrupt // its metadata. See // https://github.com/google/sanitizers/issues/321. int res = REAL(sched_getaffinity)(pid, cpusetsize, mask); if (mask && !res) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, mask, cpusetsize); return res; } #define INIT_SCHED_GETAFFINITY COMMON_INTERCEPT_FUNCTION(sched_getaffinity); #else #define INIT_SCHED_GETAFFINITY #endif #if SANITIZER_INTERCEPT_SCHED_GETPARAM INTERCEPTOR(int, sched_getparam, int pid, void *param) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, sched_getparam, pid, param); int res = REAL(sched_getparam)(pid, param); if (!res) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, param, struct_sched_param_sz); return res; } #define INIT_SCHED_GETPARAM COMMON_INTERCEPT_FUNCTION(sched_getparam); #else #define INIT_SCHED_GETPARAM #endif #if SANITIZER_INTERCEPT_STRERROR INTERCEPTOR(char *, strerror, int errnum) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, strerror, errnum); char *res = REAL(strerror)(errnum); if (res) COMMON_INTERCEPTOR_INITIALIZE_RANGE(res, REAL(strlen)(res) + 1); return res; } #define INIT_STRERROR COMMON_INTERCEPT_FUNCTION(strerror); #else #define INIT_STRERROR #endif #if SANITIZER_INTERCEPT_STRERROR_R // There are 2 versions of strerror_r: // * POSIX version returns 0 on success, negative error code on failure, // writes message to buf. // * GNU version returns message pointer, which points to either buf or some // static storage. #if ((_POSIX_C_SOURCE >= 200112L || _XOPEN_SOURCE >= 600) && !_GNU_SOURCE) || \ SANITIZER_MAC // POSIX version. Spec is not clear on whether buf is NULL-terminated. // At least on OSX, buf contents are valid even when the call fails. INTERCEPTOR(int, strerror_r, int errnum, char *buf, SIZE_T buflen) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, strerror_r, errnum, buf, buflen); // FIXME: under ASan the call below may write to freed memory and corrupt // its metadata. See // https://github.com/google/sanitizers/issues/321. int res = REAL(strerror_r)(errnum, buf, buflen); SIZE_T sz = internal_strnlen(buf, buflen); if (sz < buflen) ++sz; COMMON_INTERCEPTOR_WRITE_RANGE(ctx, buf, sz); return res; } #else // GNU version. INTERCEPTOR(char *, strerror_r, int errnum, char *buf, SIZE_T buflen) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, strerror_r, errnum, buf, buflen); // FIXME: under ASan the call below may write to freed memory and corrupt // its metadata. See // https://github.com/google/sanitizers/issues/321. char *res = REAL(strerror_r)(errnum, buf, buflen); COMMON_INTERCEPTOR_WRITE_RANGE(ctx, res, REAL(strlen)(res) + 1); return res; } #endif //(_POSIX_C_SOURCE >= 200112L || _XOPEN_SOURCE >= 600) && !_GNU_SOURCE || //SANITIZER_MAC #define INIT_STRERROR_R COMMON_INTERCEPT_FUNCTION(strerror_r); #else #define INIT_STRERROR_R #endif #if SANITIZER_INTERCEPT_XPG_STRERROR_R INTERCEPTOR(int, __xpg_strerror_r, int errnum, char *buf, SIZE_T buflen) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, __xpg_strerror_r, errnum, buf, buflen); // FIXME: under ASan the call below may write to freed memory and corrupt // its metadata. See // https://github.com/google/sanitizers/issues/321. int res = REAL(__xpg_strerror_r)(errnum, buf, buflen); // This version always returns a null-terminated string. if (buf && buflen) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, buf, REAL(strlen)(buf) + 1); return res; } #define INIT_XPG_STRERROR_R COMMON_INTERCEPT_FUNCTION(__xpg_strerror_r); #else #define INIT_XPG_STRERROR_R #endif #if SANITIZER_INTERCEPT_SCANDIR typedef int (*scandir_filter_f)(const struct __sanitizer_dirent *); typedef int (*scandir_compar_f)(const struct __sanitizer_dirent **, const struct __sanitizer_dirent **); static THREADLOCAL scandir_filter_f scandir_filter; static THREADLOCAL scandir_compar_f scandir_compar; static int wrapped_scandir_filter(const struct __sanitizer_dirent *dir) { COMMON_INTERCEPTOR_UNPOISON_PARAM(1); COMMON_INTERCEPTOR_INITIALIZE_RANGE(dir, dir->d_reclen); return scandir_filter(dir); } static int wrapped_scandir_compar(const struct __sanitizer_dirent **a, const struct __sanitizer_dirent **b) { COMMON_INTERCEPTOR_UNPOISON_PARAM(2); COMMON_INTERCEPTOR_INITIALIZE_RANGE(a, sizeof(*a)); COMMON_INTERCEPTOR_INITIALIZE_RANGE(*a, (*a)->d_reclen); COMMON_INTERCEPTOR_INITIALIZE_RANGE(b, sizeof(*b)); COMMON_INTERCEPTOR_INITIALIZE_RANGE(*b, (*b)->d_reclen); return scandir_compar(a, b); } INTERCEPTOR(int, scandir, char *dirp, __sanitizer_dirent ***namelist, scandir_filter_f filter, scandir_compar_f compar) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, scandir, dirp, namelist, filter, compar); if (dirp) COMMON_INTERCEPTOR_READ_RANGE(ctx, dirp, REAL(strlen)(dirp) + 1); scandir_filter = filter; scandir_compar = compar; // FIXME: under ASan the call below may write to freed memory and corrupt // its metadata. See // https://github.com/google/sanitizers/issues/321. int res = REAL(scandir)(dirp, namelist, filter ? wrapped_scandir_filter : nullptr, compar ? wrapped_scandir_compar : nullptr); scandir_filter = nullptr; scandir_compar = nullptr; if (namelist && res > 0) { COMMON_INTERCEPTOR_WRITE_RANGE(ctx, namelist, sizeof(*namelist)); COMMON_INTERCEPTOR_WRITE_RANGE(ctx, *namelist, sizeof(**namelist) * res); for (int i = 0; i < res; ++i) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, (*namelist)[i], (*namelist)[i]->d_reclen); } return res; } #define INIT_SCANDIR COMMON_INTERCEPT_FUNCTION(scandir); #else #define INIT_SCANDIR #endif #if SANITIZER_INTERCEPT_SCANDIR64 typedef int (*scandir64_filter_f)(const struct __sanitizer_dirent64 *); typedef int (*scandir64_compar_f)(const struct __sanitizer_dirent64 **, const struct __sanitizer_dirent64 **); static THREADLOCAL scandir64_filter_f scandir64_filter; static THREADLOCAL scandir64_compar_f scandir64_compar; static int wrapped_scandir64_filter(const struct __sanitizer_dirent64 *dir) { COMMON_INTERCEPTOR_UNPOISON_PARAM(1); COMMON_INTERCEPTOR_INITIALIZE_RANGE(dir, dir->d_reclen); return scandir64_filter(dir); } static int wrapped_scandir64_compar(const struct __sanitizer_dirent64 **a, const struct __sanitizer_dirent64 **b) { COMMON_INTERCEPTOR_UNPOISON_PARAM(2); COMMON_INTERCEPTOR_INITIALIZE_RANGE(a, sizeof(*a)); COMMON_INTERCEPTOR_INITIALIZE_RANGE(*a, (*a)->d_reclen); COMMON_INTERCEPTOR_INITIALIZE_RANGE(b, sizeof(*b)); COMMON_INTERCEPTOR_INITIALIZE_RANGE(*b, (*b)->d_reclen); return scandir64_compar(a, b); } INTERCEPTOR(int, scandir64, char *dirp, __sanitizer_dirent64 ***namelist, scandir64_filter_f filter, scandir64_compar_f compar) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, scandir64, dirp, namelist, filter, compar); if (dirp) COMMON_INTERCEPTOR_READ_RANGE(ctx, dirp, REAL(strlen)(dirp) + 1); scandir64_filter = filter; scandir64_compar = compar; // FIXME: under ASan the call below may write to freed memory and corrupt // its metadata. See // https://github.com/google/sanitizers/issues/321. int res = REAL(scandir64)(dirp, namelist, filter ? wrapped_scandir64_filter : nullptr, compar ? wrapped_scandir64_compar : nullptr); scandir64_filter = nullptr; scandir64_compar = nullptr; if (namelist && res > 0) { COMMON_INTERCEPTOR_WRITE_RANGE(ctx, namelist, sizeof(*namelist)); COMMON_INTERCEPTOR_WRITE_RANGE(ctx, *namelist, sizeof(**namelist) * res); for (int i = 0; i < res; ++i) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, (*namelist)[i], (*namelist)[i]->d_reclen); } return res; } #define INIT_SCANDIR64 COMMON_INTERCEPT_FUNCTION(scandir64); #else #define INIT_SCANDIR64 #endif #if SANITIZER_INTERCEPT_GETGROUPS INTERCEPTOR(int, getgroups, int size, u32 *lst) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, getgroups, size, lst); // FIXME: under ASan the call below may write to freed memory and corrupt // its metadata. See // https://github.com/google/sanitizers/issues/321. int res = REAL(getgroups)(size, lst); if (res >= 0 && lst && size > 0) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, lst, res * sizeof(*lst)); return res; } #define INIT_GETGROUPS COMMON_INTERCEPT_FUNCTION(getgroups); #else #define INIT_GETGROUPS #endif #if SANITIZER_INTERCEPT_POLL static void read_pollfd(void *ctx, __sanitizer_pollfd *fds, __sanitizer_nfds_t nfds) { for (unsigned i = 0; i < nfds; ++i) { COMMON_INTERCEPTOR_READ_RANGE(ctx, &fds[i].fd, sizeof(fds[i].fd)); COMMON_INTERCEPTOR_READ_RANGE(ctx, &fds[i].events, sizeof(fds[i].events)); } } static void write_pollfd(void *ctx, __sanitizer_pollfd *fds, __sanitizer_nfds_t nfds) { for (unsigned i = 0; i < nfds; ++i) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, &fds[i].revents, sizeof(fds[i].revents)); } INTERCEPTOR(int, poll, __sanitizer_pollfd *fds, __sanitizer_nfds_t nfds, int timeout) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, poll, fds, nfds, timeout); if (fds && nfds) read_pollfd(ctx, fds, nfds); int res = COMMON_INTERCEPTOR_BLOCK_REAL(poll)(fds, nfds, timeout); if (fds && nfds) write_pollfd(ctx, fds, nfds); return res; } #define INIT_POLL COMMON_INTERCEPT_FUNCTION(poll); #else #define INIT_POLL #endif #if SANITIZER_INTERCEPT_PPOLL INTERCEPTOR(int, ppoll, __sanitizer_pollfd *fds, __sanitizer_nfds_t nfds, void *timeout_ts, __sanitizer_sigset_t *sigmask) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, ppoll, fds, nfds, timeout_ts, sigmask); if (fds && nfds) read_pollfd(ctx, fds, nfds); if (timeout_ts) COMMON_INTERCEPTOR_READ_RANGE(ctx, timeout_ts, struct_timespec_sz); // FIXME: read sigmask when all of sigemptyset, etc are intercepted. int res = COMMON_INTERCEPTOR_BLOCK_REAL(ppoll)(fds, nfds, timeout_ts, sigmask); if (fds && nfds) write_pollfd(ctx, fds, nfds); return res; } #define INIT_PPOLL COMMON_INTERCEPT_FUNCTION(ppoll); #else #define INIT_PPOLL #endif #if SANITIZER_INTERCEPT_WORDEXP INTERCEPTOR(int, wordexp, char *s, __sanitizer_wordexp_t *p, int flags) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, wordexp, s, p, flags); if (s) COMMON_INTERCEPTOR_READ_RANGE(ctx, s, REAL(strlen)(s) + 1); // FIXME: under ASan the call below may write to freed memory and corrupt // its metadata. See // https://github.com/google/sanitizers/issues/321. int res = REAL(wordexp)(s, p, flags); if (!res && p) { COMMON_INTERCEPTOR_WRITE_RANGE(ctx, p, sizeof(*p)); if (p->we_wordc) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, p->we_wordv, sizeof(*p->we_wordv) * p->we_wordc); for (uptr i = 0; i < p->we_wordc; ++i) { char *w = p->we_wordv[i]; if (w) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, w, REAL(strlen)(w) + 1); } } return res; } #define INIT_WORDEXP COMMON_INTERCEPT_FUNCTION(wordexp); #else #define INIT_WORDEXP #endif #if SANITIZER_INTERCEPT_SIGWAIT INTERCEPTOR(int, sigwait, __sanitizer_sigset_t *set, int *sig) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, sigwait, set, sig); // FIXME: read sigset_t when all of sigemptyset, etc are intercepted // FIXME: under ASan the call below may write to freed memory and corrupt // its metadata. See // https://github.com/google/sanitizers/issues/321. int res = REAL(sigwait)(set, sig); if (!res && sig) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, sig, sizeof(*sig)); return res; } #define INIT_SIGWAIT COMMON_INTERCEPT_FUNCTION(sigwait); #else #define INIT_SIGWAIT #endif #if SANITIZER_INTERCEPT_SIGWAITINFO INTERCEPTOR(int, sigwaitinfo, __sanitizer_sigset_t *set, void *info) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, sigwaitinfo, set, info); // FIXME: read sigset_t when all of sigemptyset, etc are intercepted // FIXME: under ASan the call below may write to freed memory and corrupt // its metadata. See // https://github.com/google/sanitizers/issues/321. int res = REAL(sigwaitinfo)(set, info); if (res > 0 && info) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, info, siginfo_t_sz); return res; } #define INIT_SIGWAITINFO COMMON_INTERCEPT_FUNCTION(sigwaitinfo); #else #define INIT_SIGWAITINFO #endif #if SANITIZER_INTERCEPT_SIGTIMEDWAIT INTERCEPTOR(int, sigtimedwait, __sanitizer_sigset_t *set, void *info, void *timeout) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, sigtimedwait, set, info, timeout); if (timeout) COMMON_INTERCEPTOR_READ_RANGE(ctx, timeout, struct_timespec_sz); // FIXME: read sigset_t when all of sigemptyset, etc are intercepted // FIXME: under ASan the call below may write to freed memory and corrupt // its metadata. See // https://github.com/google/sanitizers/issues/321. int res = REAL(sigtimedwait)(set, info, timeout); if (res > 0 && info) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, info, siginfo_t_sz); return res; } #define INIT_SIGTIMEDWAIT COMMON_INTERCEPT_FUNCTION(sigtimedwait); #else #define INIT_SIGTIMEDWAIT #endif #if SANITIZER_INTERCEPT_SIGSETOPS INTERCEPTOR(int, sigemptyset, __sanitizer_sigset_t *set) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, sigemptyset, set); // FIXME: under ASan the call below may write to freed memory and corrupt // its metadata. See // https://github.com/google/sanitizers/issues/321. int res = REAL(sigemptyset)(set); if (!res && set) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, set, sizeof(*set)); return res; } INTERCEPTOR(int, sigfillset, __sanitizer_sigset_t *set) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, sigfillset, set); // FIXME: under ASan the call below may write to freed memory and corrupt // its metadata. See // https://github.com/google/sanitizers/issues/321. int res = REAL(sigfillset)(set); if (!res && set) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, set, sizeof(*set)); return res; } #define INIT_SIGSETOPS \ COMMON_INTERCEPT_FUNCTION(sigemptyset); \ COMMON_INTERCEPT_FUNCTION(sigfillset); #else #define INIT_SIGSETOPS #endif #if SANITIZER_INTERCEPT_SIGPENDING INTERCEPTOR(int, sigpending, __sanitizer_sigset_t *set) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, sigpending, set); // FIXME: under ASan the call below may write to freed memory and corrupt // its metadata. See // https://github.com/google/sanitizers/issues/321. int res = REAL(sigpending)(set); if (!res && set) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, set, sizeof(*set)); return res; } #define INIT_SIGPENDING COMMON_INTERCEPT_FUNCTION(sigpending); #else #define INIT_SIGPENDING #endif #if SANITIZER_INTERCEPT_SIGPROCMASK INTERCEPTOR(int, sigprocmask, int how, __sanitizer_sigset_t *set, __sanitizer_sigset_t *oldset) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, sigprocmask, how, set, oldset); // FIXME: read sigset_t when all of sigemptyset, etc are intercepted // FIXME: under ASan the call below may write to freed memory and corrupt // its metadata. See // https://github.com/google/sanitizers/issues/321. int res = REAL(sigprocmask)(how, set, oldset); if (!res && oldset) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, oldset, sizeof(*oldset)); return res; } #define INIT_SIGPROCMASK COMMON_INTERCEPT_FUNCTION(sigprocmask); #else #define INIT_SIGPROCMASK #endif #if SANITIZER_INTERCEPT_BACKTRACE INTERCEPTOR(int, backtrace, void **buffer, int size) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, backtrace, buffer, size); // FIXME: under ASan the call below may write to freed memory and corrupt // its metadata. See // https://github.com/google/sanitizers/issues/321. int res = REAL(backtrace)(buffer, size); if (res && buffer) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, buffer, res * sizeof(*buffer)); return res; } INTERCEPTOR(char **, backtrace_symbols, void **buffer, int size) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, backtrace_symbols, buffer, size); if (buffer && size) COMMON_INTERCEPTOR_READ_RANGE(ctx, buffer, size * sizeof(*buffer)); // FIXME: under ASan the call below may write to freed memory and corrupt // its metadata. See // https://github.com/google/sanitizers/issues/321. char **res = REAL(backtrace_symbols)(buffer, size); if (res && size) { COMMON_INTERCEPTOR_WRITE_RANGE(ctx, res, size * sizeof(*res)); for (int i = 0; i < size; ++i) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, res[i], REAL(strlen(res[i])) + 1); } return res; } #define INIT_BACKTRACE \ COMMON_INTERCEPT_FUNCTION(backtrace); \ COMMON_INTERCEPT_FUNCTION(backtrace_symbols); #else #define INIT_BACKTRACE #endif #if SANITIZER_INTERCEPT__EXIT INTERCEPTOR(void, _exit, int status) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, _exit, status); COMMON_INTERCEPTOR_USER_CALLBACK_START(); int status1 = COMMON_INTERCEPTOR_ON_EXIT(ctx); COMMON_INTERCEPTOR_USER_CALLBACK_END(); if (status == 0) status = status1; REAL(_exit)(status); } #define INIT__EXIT COMMON_INTERCEPT_FUNCTION(_exit); #else #define INIT__EXIT #endif #if SANITIZER_INTERCEPT_PHTREAD_MUTEX INTERCEPTOR(int, pthread_mutex_lock, void *m) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, pthread_mutex_lock, m); COMMON_INTERCEPTOR_MUTEX_PRE_LOCK(ctx, m); int res = REAL(pthread_mutex_lock)(m); if (res == errno_EOWNERDEAD) COMMON_INTERCEPTOR_MUTEX_REPAIR(ctx, m); if (res == 0 || res == errno_EOWNERDEAD) COMMON_INTERCEPTOR_MUTEX_POST_LOCK(ctx, m); if (res == errno_EINVAL) COMMON_INTERCEPTOR_MUTEX_INVALID(ctx, m); return res; } INTERCEPTOR(int, pthread_mutex_unlock, void *m) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, pthread_mutex_unlock, m); COMMON_INTERCEPTOR_MUTEX_UNLOCK(ctx, m); int res = REAL(pthread_mutex_unlock)(m); if (res == errno_EINVAL) COMMON_INTERCEPTOR_MUTEX_INVALID(ctx, m); return res; } #define INIT_PTHREAD_MUTEX_LOCK COMMON_INTERCEPT_FUNCTION(pthread_mutex_lock) #define INIT_PTHREAD_MUTEX_UNLOCK \ COMMON_INTERCEPT_FUNCTION(pthread_mutex_unlock) #else #define INIT_PTHREAD_MUTEX_LOCK #define INIT_PTHREAD_MUTEX_UNLOCK #endif #if SANITIZER_INTERCEPT_GETMNTENT || SANITIZER_INTERCEPT_GETMNTENT_R static void write_mntent(void *ctx, __sanitizer_mntent *mnt) { COMMON_INTERCEPTOR_WRITE_RANGE(ctx, mnt, sizeof(*mnt)); if (mnt->mnt_fsname) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, mnt->mnt_fsname, REAL(strlen)(mnt->mnt_fsname) + 1); if (mnt->mnt_dir) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, mnt->mnt_dir, REAL(strlen)(mnt->mnt_dir) + 1); if (mnt->mnt_type) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, mnt->mnt_type, REAL(strlen)(mnt->mnt_type) + 1); if (mnt->mnt_opts) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, mnt->mnt_opts, REAL(strlen)(mnt->mnt_opts) + 1); } #endif #if SANITIZER_INTERCEPT_GETMNTENT INTERCEPTOR(__sanitizer_mntent *, getmntent, void *fp) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, getmntent, fp); __sanitizer_mntent *res = REAL(getmntent)(fp); if (res) write_mntent(ctx, res); return res; } #define INIT_GETMNTENT COMMON_INTERCEPT_FUNCTION(getmntent); #else #define INIT_GETMNTENT #endif #if SANITIZER_INTERCEPT_GETMNTENT_R INTERCEPTOR(__sanitizer_mntent *, getmntent_r, void *fp, __sanitizer_mntent *mntbuf, char *buf, int buflen) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, getmntent_r, fp, mntbuf, buf, buflen); __sanitizer_mntent *res = REAL(getmntent_r)(fp, mntbuf, buf, buflen); if (res) write_mntent(ctx, res); return res; } #define INIT_GETMNTENT_R COMMON_INTERCEPT_FUNCTION(getmntent_r); #else #define INIT_GETMNTENT_R #endif #if SANITIZER_INTERCEPT_STATFS INTERCEPTOR(int, statfs, char *path, void *buf) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, statfs, path, buf); if (path) COMMON_INTERCEPTOR_READ_RANGE(ctx, path, REAL(strlen)(path) + 1); // FIXME: under ASan the call below may write to freed memory and corrupt // its metadata. See // https://github.com/google/sanitizers/issues/321. int res = REAL(statfs)(path, buf); if (!res) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, buf, struct_statfs_sz); return res; } INTERCEPTOR(int, fstatfs, int fd, void *buf) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, fstatfs, fd, buf); // FIXME: under ASan the call below may write to freed memory and corrupt // its metadata. See // https://github.com/google/sanitizers/issues/321. int res = REAL(fstatfs)(fd, buf); if (!res) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, buf, struct_statfs_sz); return res; } #define INIT_STATFS \ COMMON_INTERCEPT_FUNCTION(statfs); \ COMMON_INTERCEPT_FUNCTION(fstatfs); #else #define INIT_STATFS #endif #if SANITIZER_INTERCEPT_STATFS64 INTERCEPTOR(int, statfs64, char *path, void *buf) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, statfs64, path, buf); if (path) COMMON_INTERCEPTOR_READ_RANGE(ctx, path, REAL(strlen)(path) + 1); // FIXME: under ASan the call below may write to freed memory and corrupt // its metadata. See // https://github.com/google/sanitizers/issues/321. int res = REAL(statfs64)(path, buf); if (!res) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, buf, struct_statfs64_sz); return res; } INTERCEPTOR(int, fstatfs64, int fd, void *buf) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, fstatfs64, fd, buf); // FIXME: under ASan the call below may write to freed memory and corrupt // its metadata. See // https://github.com/google/sanitizers/issues/321. int res = REAL(fstatfs64)(fd, buf); if (!res) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, buf, struct_statfs64_sz); return res; } #define INIT_STATFS64 \ COMMON_INTERCEPT_FUNCTION(statfs64); \ COMMON_INTERCEPT_FUNCTION(fstatfs64); #else #define INIT_STATFS64 #endif #if SANITIZER_INTERCEPT_STATVFS INTERCEPTOR(int, statvfs, char *path, void *buf) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, statvfs, path, buf); if (path) COMMON_INTERCEPTOR_READ_RANGE(ctx, path, REAL(strlen)(path) + 1); // FIXME: under ASan the call below may write to freed memory and corrupt // its metadata. See // https://github.com/google/sanitizers/issues/321. int res = REAL(statvfs)(path, buf); if (!res) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, buf, struct_statvfs_sz); return res; } INTERCEPTOR(int, fstatvfs, int fd, void *buf) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, fstatvfs, fd, buf); // FIXME: under ASan the call below may write to freed memory and corrupt // its metadata. See // https://github.com/google/sanitizers/issues/321. int res = REAL(fstatvfs)(fd, buf); if (!res) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, buf, struct_statvfs_sz); return res; } #define INIT_STATVFS \ COMMON_INTERCEPT_FUNCTION(statvfs); \ COMMON_INTERCEPT_FUNCTION(fstatvfs); #else #define INIT_STATVFS #endif #if SANITIZER_INTERCEPT_STATVFS64 INTERCEPTOR(int, statvfs64, char *path, void *buf) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, statvfs64, path, buf); if (path) COMMON_INTERCEPTOR_READ_RANGE(ctx, path, REAL(strlen)(path) + 1); // FIXME: under ASan the call below may write to freed memory and corrupt // its metadata. See // https://github.com/google/sanitizers/issues/321. int res = REAL(statvfs64)(path, buf); if (!res) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, buf, struct_statvfs64_sz); return res; } INTERCEPTOR(int, fstatvfs64, int fd, void *buf) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, fstatvfs64, fd, buf); // FIXME: under ASan the call below may write to freed memory and corrupt // its metadata. See // https://github.com/google/sanitizers/issues/321. int res = REAL(fstatvfs64)(fd, buf); if (!res) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, buf, struct_statvfs64_sz); return res; } #define INIT_STATVFS64 \ COMMON_INTERCEPT_FUNCTION(statvfs64); \ COMMON_INTERCEPT_FUNCTION(fstatvfs64); #else #define INIT_STATVFS64 #endif #if SANITIZER_INTERCEPT_INITGROUPS INTERCEPTOR(int, initgroups, char *user, u32 group) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, initgroups, user, group); if (user) COMMON_INTERCEPTOR_READ_RANGE(ctx, user, REAL(strlen)(user) + 1); int res = REAL(initgroups)(user, group); return res; } #define INIT_INITGROUPS COMMON_INTERCEPT_FUNCTION(initgroups); #else #define INIT_INITGROUPS #endif #if SANITIZER_INTERCEPT_ETHER_NTOA_ATON INTERCEPTOR(char *, ether_ntoa, __sanitizer_ether_addr *addr) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, ether_ntoa, addr); if (addr) COMMON_INTERCEPTOR_READ_RANGE(ctx, addr, sizeof(*addr)); char *res = REAL(ether_ntoa)(addr); if (res) COMMON_INTERCEPTOR_INITIALIZE_RANGE(res, REAL(strlen)(res) + 1); return res; } INTERCEPTOR(__sanitizer_ether_addr *, ether_aton, char *buf) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, ether_aton, buf); if (buf) COMMON_INTERCEPTOR_READ_RANGE(ctx, buf, REAL(strlen)(buf) + 1); __sanitizer_ether_addr *res = REAL(ether_aton)(buf); if (res) COMMON_INTERCEPTOR_INITIALIZE_RANGE(res, sizeof(*res)); return res; } #define INIT_ETHER_NTOA_ATON \ COMMON_INTERCEPT_FUNCTION(ether_ntoa); \ COMMON_INTERCEPT_FUNCTION(ether_aton); #else #define INIT_ETHER_NTOA_ATON #endif #if SANITIZER_INTERCEPT_ETHER_HOST INTERCEPTOR(int, ether_ntohost, char *hostname, __sanitizer_ether_addr *addr) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, ether_ntohost, hostname, addr); if (addr) COMMON_INTERCEPTOR_READ_RANGE(ctx, addr, sizeof(*addr)); // FIXME: under ASan the call below may write to freed memory and corrupt // its metadata. See // https://github.com/google/sanitizers/issues/321. int res = REAL(ether_ntohost)(hostname, addr); if (!res && hostname) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, hostname, REAL(strlen)(hostname) + 1); return res; } INTERCEPTOR(int, ether_hostton, char *hostname, __sanitizer_ether_addr *addr) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, ether_hostton, hostname, addr); if (hostname) COMMON_INTERCEPTOR_READ_RANGE(ctx, hostname, REAL(strlen)(hostname) + 1); // FIXME: under ASan the call below may write to freed memory and corrupt // its metadata. See // https://github.com/google/sanitizers/issues/321. int res = REAL(ether_hostton)(hostname, addr); if (!res && addr) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, addr, sizeof(*addr)); return res; } INTERCEPTOR(int, ether_line, char *line, __sanitizer_ether_addr *addr, char *hostname) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, ether_line, line, addr, hostname); if (line) COMMON_INTERCEPTOR_READ_RANGE(ctx, line, REAL(strlen)(line) + 1); // FIXME: under ASan the call below may write to freed memory and corrupt // its metadata. See // https://github.com/google/sanitizers/issues/321. int res = REAL(ether_line)(line, addr, hostname); if (!res) { if (addr) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, addr, sizeof(*addr)); if (hostname) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, hostname, REAL(strlen)(hostname) + 1); } return res; } #define INIT_ETHER_HOST \ COMMON_INTERCEPT_FUNCTION(ether_ntohost); \ COMMON_INTERCEPT_FUNCTION(ether_hostton); \ COMMON_INTERCEPT_FUNCTION(ether_line); #else #define INIT_ETHER_HOST #endif #if SANITIZER_INTERCEPT_ETHER_R INTERCEPTOR(char *, ether_ntoa_r, __sanitizer_ether_addr *addr, char *buf) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, ether_ntoa_r, addr, buf); if (addr) COMMON_INTERCEPTOR_READ_RANGE(ctx, addr, sizeof(*addr)); // FIXME: under ASan the call below may write to freed memory and corrupt // its metadata. See // https://github.com/google/sanitizers/issues/321. char *res = REAL(ether_ntoa_r)(addr, buf); if (res) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, res, REAL(strlen)(res) + 1); return res; } INTERCEPTOR(__sanitizer_ether_addr *, ether_aton_r, char *buf, __sanitizer_ether_addr *addr) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, ether_aton_r, buf, addr); if (buf) COMMON_INTERCEPTOR_READ_RANGE(ctx, buf, REAL(strlen)(buf) + 1); // FIXME: under ASan the call below may write to freed memory and corrupt // its metadata. See // https://github.com/google/sanitizers/issues/321. __sanitizer_ether_addr *res = REAL(ether_aton_r)(buf, addr); if (res) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, res, sizeof(*res)); return res; } #define INIT_ETHER_R \ COMMON_INTERCEPT_FUNCTION(ether_ntoa_r); \ COMMON_INTERCEPT_FUNCTION(ether_aton_r); #else #define INIT_ETHER_R #endif #if SANITIZER_INTERCEPT_SHMCTL INTERCEPTOR(int, shmctl, int shmid, int cmd, void *buf) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, shmctl, shmid, cmd, buf); // FIXME: under ASan the call below may write to freed memory and corrupt // its metadata. See // https://github.com/google/sanitizers/issues/321. int res = REAL(shmctl)(shmid, cmd, buf); if (res >= 0) { unsigned sz = 0; if (cmd == shmctl_ipc_stat || cmd == shmctl_shm_stat) sz = sizeof(__sanitizer_shmid_ds); else if (cmd == shmctl_ipc_info) sz = struct_shminfo_sz; else if (cmd == shmctl_shm_info) sz = struct_shm_info_sz; if (sz) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, buf, sz); } return res; } #define INIT_SHMCTL COMMON_INTERCEPT_FUNCTION(shmctl); #else #define INIT_SHMCTL #endif #if SANITIZER_INTERCEPT_RANDOM_R INTERCEPTOR(int, random_r, void *buf, u32 *result) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, random_r, buf, result); // FIXME: under ASan the call below may write to freed memory and corrupt // its metadata. See // https://github.com/google/sanitizers/issues/321. int res = REAL(random_r)(buf, result); if (!res && result) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, result, sizeof(*result)); return res; } #define INIT_RANDOM_R COMMON_INTERCEPT_FUNCTION(random_r); #else #define INIT_RANDOM_R #endif // FIXME: under ASan the REAL() call below may write to freed memory and corrupt // its metadata. See // https://github.com/google/sanitizers/issues/321. #if SANITIZER_INTERCEPT_PTHREAD_ATTR_GET || \ SANITIZER_INTERCEPT_PTHREAD_ATTR_GETINHERITSSCHED || \ SANITIZER_INTERCEPT_PTHREAD_MUTEXATTR_GET || \ SANITIZER_INTERCEPT_PTHREAD_RWLOCKATTR_GET || \ SANITIZER_INTERCEPT_PTHREAD_CONDATTR_GET || \ SANITIZER_INTERCEPT_PTHREAD_BARRIERATTR_GET #define INTERCEPTOR_PTHREAD_OBJECT_ATTR_GET(fn, sz) \ INTERCEPTOR(int, fn, void *attr, void *r) { \ void *ctx; \ COMMON_INTERCEPTOR_ENTER(ctx, fn, attr, r); \ int res = REAL(fn)(attr, r); \ if (!res && r) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, r, sz); \ return res; \ } #define INTERCEPTOR_PTHREAD_ATTR_GET(what, sz) \ INTERCEPTOR_PTHREAD_OBJECT_ATTR_GET(pthread_attr_get##what, sz) #define INTERCEPTOR_PTHREAD_MUTEXATTR_GET(what, sz) \ INTERCEPTOR_PTHREAD_OBJECT_ATTR_GET(pthread_mutexattr_get##what, sz) #define INTERCEPTOR_PTHREAD_RWLOCKATTR_GET(what, sz) \ INTERCEPTOR_PTHREAD_OBJECT_ATTR_GET(pthread_rwlockattr_get##what, sz) #define INTERCEPTOR_PTHREAD_CONDATTR_GET(what, sz) \ INTERCEPTOR_PTHREAD_OBJECT_ATTR_GET(pthread_condattr_get##what, sz) #define INTERCEPTOR_PTHREAD_BARRIERATTR_GET(what, sz) \ INTERCEPTOR_PTHREAD_OBJECT_ATTR_GET(pthread_barrierattr_get##what, sz) #endif #if SANITIZER_INTERCEPT_PTHREAD_ATTR_GET INTERCEPTOR_PTHREAD_ATTR_GET(detachstate, sizeof(int)) INTERCEPTOR_PTHREAD_ATTR_GET(guardsize, sizeof(SIZE_T)) INTERCEPTOR_PTHREAD_ATTR_GET(schedparam, struct_sched_param_sz) INTERCEPTOR_PTHREAD_ATTR_GET(schedpolicy, sizeof(int)) INTERCEPTOR_PTHREAD_ATTR_GET(scope, sizeof(int)) INTERCEPTOR_PTHREAD_ATTR_GET(stacksize, sizeof(SIZE_T)) INTERCEPTOR(int, pthread_attr_getstack, void *attr, void **addr, SIZE_T *size) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, pthread_attr_getstack, attr, addr, size); // FIXME: under ASan the call below may write to freed memory and corrupt // its metadata. See // https://github.com/google/sanitizers/issues/321. int res = REAL(pthread_attr_getstack)(attr, addr, size); if (!res) { if (addr) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, addr, sizeof(*addr)); if (size) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, size, sizeof(*size)); } return res; } // We may need to call the real pthread_attr_getstack from the run-time // in sanitizer_common, but we don't want to include the interception headers // there. So, just define this function here. namespace __sanitizer { extern "C" { int real_pthread_attr_getstack(void *attr, void **addr, SIZE_T *size) { return REAL(pthread_attr_getstack)(attr, addr, size); } } // extern "C" } // namespace __sanitizer #define INIT_PTHREAD_ATTR_GET \ COMMON_INTERCEPT_FUNCTION(pthread_attr_getdetachstate); \ COMMON_INTERCEPT_FUNCTION(pthread_attr_getguardsize); \ COMMON_INTERCEPT_FUNCTION(pthread_attr_getschedparam); \ COMMON_INTERCEPT_FUNCTION(pthread_attr_getschedpolicy); \ COMMON_INTERCEPT_FUNCTION(pthread_attr_getscope); \ COMMON_INTERCEPT_FUNCTION(pthread_attr_getstacksize); \ COMMON_INTERCEPT_FUNCTION(pthread_attr_getstack); #else #define INIT_PTHREAD_ATTR_GET #endif #if SANITIZER_INTERCEPT_PTHREAD_ATTR_GETINHERITSCHED INTERCEPTOR_PTHREAD_ATTR_GET(inheritsched, sizeof(int)) #define INIT_PTHREAD_ATTR_GETINHERITSCHED \ COMMON_INTERCEPT_FUNCTION(pthread_attr_getinheritsched); #else #define INIT_PTHREAD_ATTR_GETINHERITSCHED #endif #if SANITIZER_INTERCEPT_PTHREAD_ATTR_GETAFFINITY_NP INTERCEPTOR(int, pthread_attr_getaffinity_np, void *attr, SIZE_T cpusetsize, void *cpuset) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, pthread_attr_getaffinity_np, attr, cpusetsize, cpuset); // FIXME: under ASan the call below may write to freed memory and corrupt // its metadata. See // https://github.com/google/sanitizers/issues/321. int res = REAL(pthread_attr_getaffinity_np)(attr, cpusetsize, cpuset); if (!res && cpusetsize && cpuset) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, cpuset, cpusetsize); return res; } #define INIT_PTHREAD_ATTR_GETAFFINITY_NP \ COMMON_INTERCEPT_FUNCTION(pthread_attr_getaffinity_np); #else #define INIT_PTHREAD_ATTR_GETAFFINITY_NP #endif #if SANITIZER_INTERCEPT_PTHREAD_MUTEXATTR_GETPSHARED INTERCEPTOR_PTHREAD_MUTEXATTR_GET(pshared, sizeof(int)) #define INIT_PTHREAD_MUTEXATTR_GETPSHARED \ COMMON_INTERCEPT_FUNCTION(pthread_mutexattr_getpshared); #else #define INIT_PTHREAD_MUTEXATTR_GETPSHARED #endif #if SANITIZER_INTERCEPT_PTHREAD_MUTEXATTR_GETTYPE INTERCEPTOR_PTHREAD_MUTEXATTR_GET(type, sizeof(int)) #define INIT_PTHREAD_MUTEXATTR_GETTYPE \ COMMON_INTERCEPT_FUNCTION(pthread_mutexattr_gettype); #else #define INIT_PTHREAD_MUTEXATTR_GETTYPE #endif #if SANITIZER_INTERCEPT_PTHREAD_MUTEXATTR_GETPROTOCOL INTERCEPTOR_PTHREAD_MUTEXATTR_GET(protocol, sizeof(int)) #define INIT_PTHREAD_MUTEXATTR_GETPROTOCOL \ COMMON_INTERCEPT_FUNCTION(pthread_mutexattr_getprotocol); #else #define INIT_PTHREAD_MUTEXATTR_GETPROTOCOL #endif #if SANITIZER_INTERCEPT_PTHREAD_MUTEXATTR_GETPRIOCEILING INTERCEPTOR_PTHREAD_MUTEXATTR_GET(prioceiling, sizeof(int)) #define INIT_PTHREAD_MUTEXATTR_GETPRIOCEILING \ COMMON_INTERCEPT_FUNCTION(pthread_mutexattr_getprioceiling); #else #define INIT_PTHREAD_MUTEXATTR_GETPRIOCEILING #endif #if SANITIZER_INTERCEPT_PTHREAD_MUTEXATTR_GETROBUST INTERCEPTOR_PTHREAD_MUTEXATTR_GET(robust, sizeof(int)) #define INIT_PTHREAD_MUTEXATTR_GETROBUST \ COMMON_INTERCEPT_FUNCTION(pthread_mutexattr_getrobust); #else #define INIT_PTHREAD_MUTEXATTR_GETROBUST #endif #if SANITIZER_INTERCEPT_PTHREAD_MUTEXATTR_GETROBUST_NP INTERCEPTOR_PTHREAD_MUTEXATTR_GET(robust_np, sizeof(int)) #define INIT_PTHREAD_MUTEXATTR_GETROBUST_NP \ COMMON_INTERCEPT_FUNCTION(pthread_mutexattr_getrobust_np); #else #define INIT_PTHREAD_MUTEXATTR_GETROBUST_NP #endif #if SANITIZER_INTERCEPT_PTHREAD_RWLOCKATTR_GETPSHARED INTERCEPTOR_PTHREAD_RWLOCKATTR_GET(pshared, sizeof(int)) #define INIT_PTHREAD_RWLOCKATTR_GETPSHARED \ COMMON_INTERCEPT_FUNCTION(pthread_rwlockattr_getpshared); #else #define INIT_PTHREAD_RWLOCKATTR_GETPSHARED #endif #if SANITIZER_INTERCEPT_PTHREAD_RWLOCKATTR_GETKIND_NP INTERCEPTOR_PTHREAD_RWLOCKATTR_GET(kind_np, sizeof(int)) #define INIT_PTHREAD_RWLOCKATTR_GETKIND_NP \ COMMON_INTERCEPT_FUNCTION(pthread_rwlockattr_getkind_np); #else #define INIT_PTHREAD_RWLOCKATTR_GETKIND_NP #endif #if SANITIZER_INTERCEPT_PTHREAD_CONDATTR_GETPSHARED INTERCEPTOR_PTHREAD_CONDATTR_GET(pshared, sizeof(int)) #define INIT_PTHREAD_CONDATTR_GETPSHARED \ COMMON_INTERCEPT_FUNCTION(pthread_condattr_getpshared); #else #define INIT_PTHREAD_CONDATTR_GETPSHARED #endif #if SANITIZER_INTERCEPT_PTHREAD_CONDATTR_GETCLOCK INTERCEPTOR_PTHREAD_CONDATTR_GET(clock, sizeof(int)) #define INIT_PTHREAD_CONDATTR_GETCLOCK \ COMMON_INTERCEPT_FUNCTION(pthread_condattr_getclock); #else #define INIT_PTHREAD_CONDATTR_GETCLOCK #endif #if SANITIZER_INTERCEPT_PTHREAD_BARRIERATTR_GETPSHARED INTERCEPTOR_PTHREAD_BARRIERATTR_GET(pshared, sizeof(int)) // !mac !android #define INIT_PTHREAD_BARRIERATTR_GETPSHARED \ COMMON_INTERCEPT_FUNCTION(pthread_barrierattr_getpshared); #else #define INIT_PTHREAD_BARRIERATTR_GETPSHARED #endif #if SANITIZER_INTERCEPT_TMPNAM INTERCEPTOR(char *, tmpnam, char *s) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, tmpnam, s); char *res = REAL(tmpnam)(s); if (res) { if (s) // FIXME: under ASan the call below may write to freed memory and corrupt // its metadata. See // https://github.com/google/sanitizers/issues/321. COMMON_INTERCEPTOR_WRITE_RANGE(ctx, s, REAL(strlen)(s) + 1); else COMMON_INTERCEPTOR_INITIALIZE_RANGE(res, REAL(strlen)(res) + 1); } return res; } #define INIT_TMPNAM COMMON_INTERCEPT_FUNCTION(tmpnam); #else #define INIT_TMPNAM #endif #if SANITIZER_INTERCEPT_TMPNAM_R INTERCEPTOR(char *, tmpnam_r, char *s) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, tmpnam_r, s); // FIXME: under ASan the call below may write to freed memory and corrupt // its metadata. See // https://github.com/google/sanitizers/issues/321. char *res = REAL(tmpnam_r)(s); if (res && s) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, s, REAL(strlen)(s) + 1); return res; } #define INIT_TMPNAM_R COMMON_INTERCEPT_FUNCTION(tmpnam_r); #else #define INIT_TMPNAM_R #endif #if SANITIZER_INTERCEPT_TTYNAME_R INTERCEPTOR(int, ttyname_r, int fd, char *name, SIZE_T namesize) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, ttyname_r, fd, name, namesize); int res = REAL(ttyname_r)(fd, name, namesize); if (res == 0) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, name, REAL(strlen)(name) + 1); return res; } #define INIT_TTYNAME_R COMMON_INTERCEPT_FUNCTION(ttyname_r); #else #define INIT_TTYNAME_R #endif #if SANITIZER_INTERCEPT_TEMPNAM INTERCEPTOR(char *, tempnam, char *dir, char *pfx) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, tempnam, dir, pfx); if (dir) COMMON_INTERCEPTOR_READ_RANGE(ctx, dir, REAL(strlen)(dir) + 1); if (pfx) COMMON_INTERCEPTOR_READ_RANGE(ctx, pfx, REAL(strlen)(pfx) + 1); char *res = REAL(tempnam)(dir, pfx); if (res) COMMON_INTERCEPTOR_INITIALIZE_RANGE(res, REAL(strlen)(res) + 1); return res; } #define INIT_TEMPNAM COMMON_INTERCEPT_FUNCTION(tempnam); #else #define INIT_TEMPNAM #endif #if SANITIZER_INTERCEPT_PTHREAD_SETNAME_NP INTERCEPTOR(int, pthread_setname_np, uptr thread, const char *name) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, pthread_setname_np, thread, name); COMMON_INTERCEPTOR_READ_STRING(ctx, name, 0); COMMON_INTERCEPTOR_SET_PTHREAD_NAME(ctx, thread, name); return REAL(pthread_setname_np)(thread, name); } #define INIT_PTHREAD_SETNAME_NP COMMON_INTERCEPT_FUNCTION(pthread_setname_np); #else #define INIT_PTHREAD_SETNAME_NP #endif #if SANITIZER_INTERCEPT_SINCOS INTERCEPTOR(void, sincos, double x, double *sin, double *cos) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, sincos, x, sin, cos); // FIXME: under ASan the call below may write to freed memory and corrupt // its metadata. See // https://github.com/google/sanitizers/issues/321. REAL(sincos)(x, sin, cos); if (sin) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, sin, sizeof(*sin)); if (cos) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, cos, sizeof(*cos)); } INTERCEPTOR(void, sincosf, float x, float *sin, float *cos) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, sincosf, x, sin, cos); // FIXME: under ASan the call below may write to freed memory and corrupt // its metadata. See // https://github.com/google/sanitizers/issues/321. REAL(sincosf)(x, sin, cos); if (sin) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, sin, sizeof(*sin)); if (cos) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, cos, sizeof(*cos)); } INTERCEPTOR(void, sincosl, long double x, long double *sin, long double *cos) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, sincosl, x, sin, cos); // FIXME: under ASan the call below may write to freed memory and corrupt // its metadata. See // https://github.com/google/sanitizers/issues/321. REAL(sincosl)(x, sin, cos); if (sin) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, sin, sizeof(*sin)); if (cos) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, cos, sizeof(*cos)); } #define INIT_SINCOS \ COMMON_INTERCEPT_FUNCTION(sincos); \ COMMON_INTERCEPT_FUNCTION(sincosf); \ COMMON_INTERCEPT_FUNCTION_LDBL(sincosl); #else #define INIT_SINCOS #endif #if SANITIZER_INTERCEPT_REMQUO INTERCEPTOR(double, remquo, double x, double y, int *quo) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, remquo, x, y, quo); // FIXME: under ASan the call below may write to freed memory and corrupt // its metadata. See // https://github.com/google/sanitizers/issues/321. double res = REAL(remquo)(x, y, quo); if (quo) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, quo, sizeof(*quo)); return res; } INTERCEPTOR(float, remquof, float x, float y, int *quo) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, remquof, x, y, quo); // FIXME: under ASan the call below may write to freed memory and corrupt // its metadata. See // https://github.com/google/sanitizers/issues/321. float res = REAL(remquof)(x, y, quo); if (quo) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, quo, sizeof(*quo)); return res; } INTERCEPTOR(long double, remquol, long double x, long double y, int *quo) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, remquol, x, y, quo); // FIXME: under ASan the call below may write to freed memory and corrupt // its metadata. See // https://github.com/google/sanitizers/issues/321. long double res = REAL(remquol)(x, y, quo); if (quo) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, quo, sizeof(*quo)); return res; } #define INIT_REMQUO \ COMMON_INTERCEPT_FUNCTION(remquo); \ COMMON_INTERCEPT_FUNCTION(remquof); \ COMMON_INTERCEPT_FUNCTION_LDBL(remquol); #else #define INIT_REMQUO #endif #if SANITIZER_INTERCEPT_LGAMMA extern int signgam; INTERCEPTOR(double, lgamma, double x) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, lgamma, x); double res = REAL(lgamma)(x); COMMON_INTERCEPTOR_WRITE_RANGE(ctx, &signgam, sizeof(signgam)); return res; } INTERCEPTOR(float, lgammaf, float x) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, lgammaf, x); float res = REAL(lgammaf)(x); COMMON_INTERCEPTOR_WRITE_RANGE(ctx, &signgam, sizeof(signgam)); return res; } INTERCEPTOR(long double, lgammal, long double x) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, lgammal, x); long double res = REAL(lgammal)(x); COMMON_INTERCEPTOR_WRITE_RANGE(ctx, &signgam, sizeof(signgam)); return res; } #define INIT_LGAMMA \ COMMON_INTERCEPT_FUNCTION(lgamma); \ COMMON_INTERCEPT_FUNCTION(lgammaf); \ COMMON_INTERCEPT_FUNCTION_LDBL(lgammal); #else #define INIT_LGAMMA #endif #if SANITIZER_INTERCEPT_LGAMMA_R INTERCEPTOR(double, lgamma_r, double x, int *signp) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, lgamma_r, x, signp); // FIXME: under ASan the call below may write to freed memory and corrupt // its metadata. See // https://github.com/google/sanitizers/issues/321. double res = REAL(lgamma_r)(x, signp); if (signp) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, signp, sizeof(*signp)); return res; } INTERCEPTOR(float, lgammaf_r, float x, int *signp) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, lgammaf_r, x, signp); // FIXME: under ASan the call below may write to freed memory and corrupt // its metadata. See // https://github.com/google/sanitizers/issues/321. float res = REAL(lgammaf_r)(x, signp); if (signp) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, signp, sizeof(*signp)); return res; } #define INIT_LGAMMA_R \ COMMON_INTERCEPT_FUNCTION(lgamma_r); \ COMMON_INTERCEPT_FUNCTION(lgammaf_r); #else #define INIT_LGAMMA_R #endif #if SANITIZER_INTERCEPT_LGAMMAL_R INTERCEPTOR(long double, lgammal_r, long double x, int *signp) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, lgammal_r, x, signp); // FIXME: under ASan the call below may write to freed memory and corrupt // its metadata. See // https://github.com/google/sanitizers/issues/321. long double res = REAL(lgammal_r)(x, signp); if (signp) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, signp, sizeof(*signp)); return res; } #define INIT_LGAMMAL_R COMMON_INTERCEPT_FUNCTION_LDBL(lgammal_r); #else #define INIT_LGAMMAL_R #endif #if SANITIZER_INTERCEPT_DRAND48_R INTERCEPTOR(int, drand48_r, void *buffer, double *result) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, drand48_r, buffer, result); // FIXME: under ASan the call below may write to freed memory and corrupt // its metadata. See // https://github.com/google/sanitizers/issues/321. int res = REAL(drand48_r)(buffer, result); if (result) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, result, sizeof(*result)); return res; } INTERCEPTOR(int, lrand48_r, void *buffer, long *result) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, lrand48_r, buffer, result); // FIXME: under ASan the call below may write to freed memory and corrupt // its metadata. See // https://github.com/google/sanitizers/issues/321. int res = REAL(lrand48_r)(buffer, result); if (result) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, result, sizeof(*result)); return res; } #define INIT_DRAND48_R \ COMMON_INTERCEPT_FUNCTION(drand48_r); \ COMMON_INTERCEPT_FUNCTION(lrand48_r); #else #define INIT_DRAND48_R #endif #if SANITIZER_INTERCEPT_RAND_R INTERCEPTOR(int, rand_r, unsigned *seedp) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, rand_r, seedp); COMMON_INTERCEPTOR_READ_RANGE(ctx, seedp, sizeof(*seedp)); return REAL(rand_r)(seedp); } #define INIT_RAND_R COMMON_INTERCEPT_FUNCTION(rand_r); #else #define INIT_RAND_R #endif #if SANITIZER_INTERCEPT_GETLINE INTERCEPTOR(SSIZE_T, getline, char **lineptr, SIZE_T *n, void *stream) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, getline, lineptr, n, stream); // FIXME: under ASan the call below may write to freed memory and corrupt // its metadata. See // https://github.com/google/sanitizers/issues/321. SSIZE_T res = REAL(getline)(lineptr, n, stream); if (res > 0) { COMMON_INTERCEPTOR_WRITE_RANGE(ctx, lineptr, sizeof(*lineptr)); COMMON_INTERCEPTOR_WRITE_RANGE(ctx, n, sizeof(*n)); COMMON_INTERCEPTOR_WRITE_RANGE(ctx, *lineptr, res + 1); } return res; } // FIXME: under ASan the call below may write to freed memory and corrupt its // metadata. See // https://github.com/google/sanitizers/issues/321. #define GETDELIM_INTERCEPTOR_IMPL(vname) \ { \ void *ctx; \ COMMON_INTERCEPTOR_ENTER(ctx, vname, lineptr, n, delim, stream); \ SSIZE_T res = REAL(vname)(lineptr, n, delim, stream); \ if (res > 0) { \ COMMON_INTERCEPTOR_WRITE_RANGE(ctx, lineptr, sizeof(*lineptr)); \ COMMON_INTERCEPTOR_WRITE_RANGE(ctx, n, sizeof(*n)); \ COMMON_INTERCEPTOR_WRITE_RANGE(ctx, *lineptr, res + 1); \ } \ return res; \ } INTERCEPTOR(SSIZE_T, __getdelim, char **lineptr, SIZE_T *n, int delim, void *stream) GETDELIM_INTERCEPTOR_IMPL(__getdelim) // There's no __getdelim() on FreeBSD so we supply the getdelim() interceptor // with its own body. INTERCEPTOR(SSIZE_T, getdelim, char **lineptr, SIZE_T *n, int delim, void *stream) GETDELIM_INTERCEPTOR_IMPL(getdelim) #define INIT_GETLINE \ COMMON_INTERCEPT_FUNCTION(getline); \ COMMON_INTERCEPT_FUNCTION(__getdelim); \ COMMON_INTERCEPT_FUNCTION(getdelim); #else #define INIT_GETLINE #endif #if SANITIZER_INTERCEPT_ICONV INTERCEPTOR(SIZE_T, iconv, void *cd, char **inbuf, SIZE_T *inbytesleft, char **outbuf, SIZE_T *outbytesleft) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, iconv, cd, inbuf, inbytesleft, outbuf, outbytesleft); if (inbytesleft) COMMON_INTERCEPTOR_READ_RANGE(ctx, inbytesleft, sizeof(*inbytesleft)); if (inbuf && inbytesleft) COMMON_INTERCEPTOR_READ_RANGE(ctx, *inbuf, *inbytesleft); if (outbytesleft) COMMON_INTERCEPTOR_READ_RANGE(ctx, outbytesleft, sizeof(*outbytesleft)); void *outbuf_orig = outbuf ? *outbuf : nullptr; // FIXME: under ASan the call below may write to freed memory and corrupt // its metadata. See // https://github.com/google/sanitizers/issues/321. SIZE_T res = REAL(iconv)(cd, inbuf, inbytesleft, outbuf, outbytesleft); if (outbuf && *outbuf > outbuf_orig) { SIZE_T sz = (char *)*outbuf - (char *)outbuf_orig; COMMON_INTERCEPTOR_WRITE_RANGE(ctx, outbuf_orig, sz); } return res; } #define INIT_ICONV COMMON_INTERCEPT_FUNCTION(iconv); #else #define INIT_ICONV #endif #if SANITIZER_INTERCEPT_TIMES INTERCEPTOR(__sanitizer_clock_t, times, void *tms) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, times, tms); // FIXME: under ASan the call below may write to freed memory and corrupt // its metadata. See // https://github.com/google/sanitizers/issues/321. __sanitizer_clock_t res = REAL(times)(tms); if (res != (__sanitizer_clock_t)-1 && tms) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, tms, struct_tms_sz); return res; } #define INIT_TIMES COMMON_INTERCEPT_FUNCTION(times); #else #define INIT_TIMES #endif #if SANITIZER_INTERCEPT_TLS_GET_ADDR #if !SANITIZER_S390 #define INIT_TLS_GET_ADDR COMMON_INTERCEPT_FUNCTION(__tls_get_addr) // If you see any crashes around this functions, there are 2 known issues with // it: 1. __tls_get_addr can be called with mis-aligned stack due to: // https://gcc.gnu.org/bugzilla/show_bug.cgi?id=58066 // 2. It can be called recursively if sanitizer code uses __tls_get_addr // to access thread local variables (it should not happen normally, // because sanitizers use initial-exec tls model). INTERCEPTOR(void *, __tls_get_addr, void *arg) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, __tls_get_addr, arg); void *res = REAL(__tls_get_addr)(arg); uptr tls_begin, tls_end; COMMON_INTERCEPTOR_GET_TLS_RANGE(&tls_begin, &tls_end); DTLS::DTV *dtv = DTLS_on_tls_get_addr(arg, res, tls_begin, tls_end); if (dtv) { // New DTLS block has been allocated. COMMON_INTERCEPTOR_INITIALIZE_RANGE((void *)dtv->beg, dtv->size); } return res; } #if SANITIZER_PPC // On PowerPC, we also need to intercept __tls_get_addr_opt, which has // mostly the same semantics as __tls_get_addr, but its presence enables // some optimizations in linker (which are safe to ignore here). extern "C" __attribute__((alias("__interceptor___tls_get_addr"), visibility("default"))) void *__tls_get_addr_opt(void *arg); #endif #else // SANITIZER_S390 // On s390, we have to intercept two functions here: // - __tls_get_addr_internal, which is a glibc-internal function that is like // the usual __tls_get_addr, but returns a TP-relative offset instead of // a proper pointer. It is used by dlsym for TLS symbols. // - __tls_get_offset, which is like the above, but also takes a GOT-relative // descriptor offset as an argument instead of a pointer. GOT address // is passed in r12, so it's necessary to write it in assembly. This is // the function used by the compiler. extern "C" uptr __tls_get_offset_wrapper(void *arg, uptr (*fn)(void *arg)); #define INIT_TLS_GET_ADDR COMMON_INTERCEPT_FUNCTION(__tls_get_offset) DEFINE_REAL(uptr, __tls_get_offset, void *arg) extern "C" uptr __tls_get_offset(void *arg); extern "C" uptr __interceptor___tls_get_offset(void *arg); INTERCEPTOR(uptr, __tls_get_addr_internal, void *arg) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, __tls_get_addr_internal, arg); uptr res = __tls_get_offset_wrapper(arg, REAL(__tls_get_offset)); uptr tp = reinterpret_cast(__builtin_thread_pointer()); void *ptr = reinterpret_cast(res + tp); uptr tls_begin, tls_end; COMMON_INTERCEPTOR_GET_TLS_RANGE(&tls_begin, &tls_end); DTLS::DTV *dtv = DTLS_on_tls_get_addr(arg, ptr, tls_begin, tls_end); if (dtv) { // New DTLS block has been allocated. COMMON_INTERCEPTOR_INITIALIZE_RANGE((void *)dtv->beg, dtv->size); } return res; } // We need a hidden symbol aliasing the above, so that we can jump // directly to it from the assembly below. extern "C" __attribute__((alias("__interceptor___tls_get_addr_internal"), visibility("hidden"))) uptr __tls_get_addr_hidden(void *arg); // Now carefully intercept __tls_get_offset. asm( ".text\n" // The __intercept_ version has to exist, so that gen_dynamic_list.py // exports our symbol. ".weak __tls_get_offset\n" ".type __tls_get_offset, @function\n" "__tls_get_offset:\n" ".global __interceptor___tls_get_offset\n" ".type __interceptor___tls_get_offset, @function\n" "__interceptor___tls_get_offset:\n" #ifdef __s390x__ "la %r2, 0(%r2,%r12)\n" "jg __tls_get_addr_hidden\n" #else "basr %r3,0\n" "0: la %r2,0(%r2,%r12)\n" "l %r4,1f-0b(%r3)\n" "b 0(%r4,%r3)\n" "1: .long __tls_get_addr_hidden - 0b\n" #endif ".size __interceptor___tls_get_offset, .-__interceptor___tls_get_offset\n" // Assembly wrapper to call REAL(__tls_get_offset)(arg) ".type __tls_get_offset_wrapper, @function\n" "__tls_get_offset_wrapper:\n" #ifdef __s390x__ "sgr %r2,%r12\n" #else "sr %r2,%r12\n" #endif "br %r3\n" ".size __tls_get_offset_wrapper, .-__tls_get_offset_wrapper\n" ); #endif // SANITIZER_S390 #else #define INIT_TLS_GET_ADDR #endif #if SANITIZER_INTERCEPT_LISTXATTR INTERCEPTOR(SSIZE_T, listxattr, const char *path, char *list, SIZE_T size) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, listxattr, path, list, size); if (path) COMMON_INTERCEPTOR_READ_RANGE(ctx, path, REAL(strlen)(path) + 1); // FIXME: under ASan the call below may write to freed memory and corrupt // its metadata. See // https://github.com/google/sanitizers/issues/321. SSIZE_T res = REAL(listxattr)(path, list, size); // Here and below, size == 0 is a special case where nothing is written to the // buffer, and res contains the desired buffer size. if (size && res > 0 && list) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, list, res); return res; } INTERCEPTOR(SSIZE_T, llistxattr, const char *path, char *list, SIZE_T size) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, llistxattr, path, list, size); if (path) COMMON_INTERCEPTOR_READ_RANGE(ctx, path, REAL(strlen)(path) + 1); // FIXME: under ASan the call below may write to freed memory and corrupt // its metadata. See // https://github.com/google/sanitizers/issues/321. SSIZE_T res = REAL(llistxattr)(path, list, size); if (size && res > 0 && list) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, list, res); return res; } INTERCEPTOR(SSIZE_T, flistxattr, int fd, char *list, SIZE_T size) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, flistxattr, fd, list, size); // FIXME: under ASan the call below may write to freed memory and corrupt // its metadata. See // https://github.com/google/sanitizers/issues/321. SSIZE_T res = REAL(flistxattr)(fd, list, size); if (size && res > 0 && list) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, list, res); return res; } #define INIT_LISTXATTR \ COMMON_INTERCEPT_FUNCTION(listxattr); \ COMMON_INTERCEPT_FUNCTION(llistxattr); \ COMMON_INTERCEPT_FUNCTION(flistxattr); #else #define INIT_LISTXATTR #endif #if SANITIZER_INTERCEPT_GETXATTR INTERCEPTOR(SSIZE_T, getxattr, const char *path, const char *name, char *value, SIZE_T size) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, getxattr, path, name, value, size); if (path) COMMON_INTERCEPTOR_READ_RANGE(ctx, path, REAL(strlen)(path) + 1); if (name) COMMON_INTERCEPTOR_READ_RANGE(ctx, name, REAL(strlen)(name) + 1); // FIXME: under ASan the call below may write to freed memory and corrupt // its metadata. See // https://github.com/google/sanitizers/issues/321. SSIZE_T res = REAL(getxattr)(path, name, value, size); if (size && res > 0 && value) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, value, res); return res; } INTERCEPTOR(SSIZE_T, lgetxattr, const char *path, const char *name, char *value, SIZE_T size) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, lgetxattr, path, name, value, size); if (path) COMMON_INTERCEPTOR_READ_RANGE(ctx, path, REAL(strlen)(path) + 1); if (name) COMMON_INTERCEPTOR_READ_RANGE(ctx, name, REAL(strlen)(name) + 1); // FIXME: under ASan the call below may write to freed memory and corrupt // its metadata. See // https://github.com/google/sanitizers/issues/321. SSIZE_T res = REAL(lgetxattr)(path, name, value, size); if (size && res > 0 && value) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, value, res); return res; } INTERCEPTOR(SSIZE_T, fgetxattr, int fd, const char *name, char *value, SIZE_T size) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, fgetxattr, fd, name, value, size); if (name) COMMON_INTERCEPTOR_READ_RANGE(ctx, name, REAL(strlen)(name) + 1); // FIXME: under ASan the call below may write to freed memory and corrupt // its metadata. See // https://github.com/google/sanitizers/issues/321. SSIZE_T res = REAL(fgetxattr)(fd, name, value, size); if (size && res > 0 && value) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, value, res); return res; } #define INIT_GETXATTR \ COMMON_INTERCEPT_FUNCTION(getxattr); \ COMMON_INTERCEPT_FUNCTION(lgetxattr); \ COMMON_INTERCEPT_FUNCTION(fgetxattr); #else #define INIT_GETXATTR #endif #if SANITIZER_INTERCEPT_GETRESID INTERCEPTOR(int, getresuid, void *ruid, void *euid, void *suid) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, getresuid, ruid, euid, suid); // FIXME: under ASan the call below may write to freed memory and corrupt // its metadata. See // https://github.com/google/sanitizers/issues/321. int res = REAL(getresuid)(ruid, euid, suid); if (res >= 0) { if (ruid) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, ruid, uid_t_sz); if (euid) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, euid, uid_t_sz); if (suid) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, suid, uid_t_sz); } return res; } INTERCEPTOR(int, getresgid, void *rgid, void *egid, void *sgid) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, getresgid, rgid, egid, sgid); // FIXME: under ASan the call below may write to freed memory and corrupt // its metadata. See // https://github.com/google/sanitizers/issues/321. int res = REAL(getresgid)(rgid, egid, sgid); if (res >= 0) { if (rgid) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, rgid, gid_t_sz); if (egid) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, egid, gid_t_sz); if (sgid) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, sgid, gid_t_sz); } return res; } #define INIT_GETRESID \ COMMON_INTERCEPT_FUNCTION(getresuid); \ COMMON_INTERCEPT_FUNCTION(getresgid); #else #define INIT_GETRESID #endif #if SANITIZER_INTERCEPT_GETIFADDRS // As long as getifaddrs()/freeifaddrs() use calloc()/free(), we don't need to // intercept freeifaddrs(). If that ceases to be the case, we might need to // intercept it to poison the memory again. INTERCEPTOR(int, getifaddrs, __sanitizer_ifaddrs **ifap) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, getifaddrs, ifap); // FIXME: under ASan the call below may write to freed memory and corrupt // its metadata. See // https://github.com/google/sanitizers/issues/321. int res = REAL(getifaddrs)(ifap); if (res == 0 && ifap) { COMMON_INTERCEPTOR_WRITE_RANGE(ctx, ifap, sizeof(void *)); __sanitizer_ifaddrs *p = *ifap; while (p) { COMMON_INTERCEPTOR_WRITE_RANGE(ctx, p, sizeof(__sanitizer_ifaddrs)); if (p->ifa_name) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, p->ifa_name, REAL(strlen)(p->ifa_name) + 1); if (p->ifa_addr) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, p->ifa_addr, struct_sockaddr_sz); if (p->ifa_netmask) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, p->ifa_netmask, struct_sockaddr_sz); // On Linux this is a union, but the other member also points to a // struct sockaddr, so the following is sufficient. if (p->ifa_dstaddr) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, p->ifa_dstaddr, struct_sockaddr_sz); // FIXME(smatveev): Unpoison p->ifa_data as well. p = p->ifa_next; } } return res; } #define INIT_GETIFADDRS \ COMMON_INTERCEPT_FUNCTION(getifaddrs); #else #define INIT_GETIFADDRS #endif #if SANITIZER_INTERCEPT_IF_INDEXTONAME INTERCEPTOR(char *, if_indextoname, unsigned int ifindex, char* ifname) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, if_indextoname, ifindex, ifname); // FIXME: under ASan the call below may write to freed memory and corrupt // its metadata. See // https://github.com/google/sanitizers/issues/321. char *res = REAL(if_indextoname)(ifindex, ifname); if (res && ifname) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, ifname, REAL(strlen)(ifname) + 1); return res; } INTERCEPTOR(unsigned int, if_nametoindex, const char* ifname) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, if_nametoindex, ifname); if (ifname) COMMON_INTERCEPTOR_READ_RANGE(ctx, ifname, REAL(strlen)(ifname) + 1); return REAL(if_nametoindex)(ifname); } #define INIT_IF_INDEXTONAME \ COMMON_INTERCEPT_FUNCTION(if_indextoname); \ COMMON_INTERCEPT_FUNCTION(if_nametoindex); #else #define INIT_IF_INDEXTONAME #endif #if SANITIZER_INTERCEPT_CAPGET INTERCEPTOR(int, capget, void *hdrp, void *datap) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, capget, hdrp, datap); if (hdrp) COMMON_INTERCEPTOR_READ_RANGE(ctx, hdrp, __user_cap_header_struct_sz); // FIXME: under ASan the call below may write to freed memory and corrupt // its metadata. See // https://github.com/google/sanitizers/issues/321. int res = REAL(capget)(hdrp, datap); if (res == 0 && datap) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, datap, __user_cap_data_struct_sz); // We can also return -1 and write to hdrp->version if the version passed in // hdrp->version is unsupported. But that's not a trivial condition to check, // and anyway COMMON_INTERCEPTOR_READ_RANGE protects us to some extent. return res; } INTERCEPTOR(int, capset, void *hdrp, const void *datap) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, capset, hdrp, datap); if (hdrp) COMMON_INTERCEPTOR_READ_RANGE(ctx, hdrp, __user_cap_header_struct_sz); if (datap) COMMON_INTERCEPTOR_READ_RANGE(ctx, datap, __user_cap_data_struct_sz); return REAL(capset)(hdrp, datap); } #define INIT_CAPGET \ COMMON_INTERCEPT_FUNCTION(capget); \ COMMON_INTERCEPT_FUNCTION(capset); #else #define INIT_CAPGET #endif #if SANITIZER_INTERCEPT_AEABI_MEM INTERCEPTOR(void *, __aeabi_memmove, void *to, const void *from, uptr size) { void *ctx; COMMON_INTERCEPTOR_MEMMOVE_IMPL(ctx, to, from, size); } INTERCEPTOR(void *, __aeabi_memmove4, void *to, const void *from, uptr size) { void *ctx; COMMON_INTERCEPTOR_MEMMOVE_IMPL(ctx, to, from, size); } INTERCEPTOR(void *, __aeabi_memmove8, void *to, const void *from, uptr size) { void *ctx; COMMON_INTERCEPTOR_MEMMOVE_IMPL(ctx, to, from, size); } INTERCEPTOR(void *, __aeabi_memcpy, void *to, const void *from, uptr size) { void *ctx; COMMON_INTERCEPTOR_MEMCPY_IMPL(ctx, to, from, size); } INTERCEPTOR(void *, __aeabi_memcpy4, void *to, const void *from, uptr size) { void *ctx; COMMON_INTERCEPTOR_MEMCPY_IMPL(ctx, to, from, size); } INTERCEPTOR(void *, __aeabi_memcpy8, void *to, const void *from, uptr size) { void *ctx; COMMON_INTERCEPTOR_MEMCPY_IMPL(ctx, to, from, size); } // Note the argument order. INTERCEPTOR(void *, __aeabi_memset, void *block, uptr size, int c) { void *ctx; COMMON_INTERCEPTOR_MEMSET_IMPL(ctx, block, c, size); } INTERCEPTOR(void *, __aeabi_memset4, void *block, uptr size, int c) { void *ctx; COMMON_INTERCEPTOR_MEMSET_IMPL(ctx, block, c, size); } INTERCEPTOR(void *, __aeabi_memset8, void *block, uptr size, int c) { void *ctx; COMMON_INTERCEPTOR_MEMSET_IMPL(ctx, block, c, size); } INTERCEPTOR(void *, __aeabi_memclr, void *block, uptr size) { void *ctx; COMMON_INTERCEPTOR_MEMSET_IMPL(ctx, block, 0, size); } INTERCEPTOR(void *, __aeabi_memclr4, void *block, uptr size) { void *ctx; COMMON_INTERCEPTOR_MEMSET_IMPL(ctx, block, 0, size); } INTERCEPTOR(void *, __aeabi_memclr8, void *block, uptr size) { void *ctx; COMMON_INTERCEPTOR_MEMSET_IMPL(ctx, block, 0, size); } #define INIT_AEABI_MEM \ COMMON_INTERCEPT_FUNCTION(__aeabi_memmove); \ COMMON_INTERCEPT_FUNCTION(__aeabi_memmove4); \ COMMON_INTERCEPT_FUNCTION(__aeabi_memmove8); \ COMMON_INTERCEPT_FUNCTION(__aeabi_memcpy); \ COMMON_INTERCEPT_FUNCTION(__aeabi_memcpy4); \ COMMON_INTERCEPT_FUNCTION(__aeabi_memcpy8); \ COMMON_INTERCEPT_FUNCTION(__aeabi_memset); \ COMMON_INTERCEPT_FUNCTION(__aeabi_memset4); \ COMMON_INTERCEPT_FUNCTION(__aeabi_memset8); \ COMMON_INTERCEPT_FUNCTION(__aeabi_memclr); \ COMMON_INTERCEPT_FUNCTION(__aeabi_memclr4); \ COMMON_INTERCEPT_FUNCTION(__aeabi_memclr8); #else #define INIT_AEABI_MEM #endif // SANITIZER_INTERCEPT_AEABI_MEM #if SANITIZER_INTERCEPT___BZERO INTERCEPTOR(void *, __bzero, void *block, uptr size) { void *ctx; COMMON_INTERCEPTOR_MEMSET_IMPL(ctx, block, 0, size); } #define INIT___BZERO COMMON_INTERCEPT_FUNCTION(__bzero); #else #define INIT___BZERO #endif // SANITIZER_INTERCEPT___BZERO #if SANITIZER_INTERCEPT_FTIME INTERCEPTOR(int, ftime, __sanitizer_timeb *tp) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, ftime, tp); // FIXME: under ASan the call below may write to freed memory and corrupt // its metadata. See // https://github.com/google/sanitizers/issues/321. int res = REAL(ftime)(tp); if (tp) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, tp, sizeof(*tp)); return res; } #define INIT_FTIME COMMON_INTERCEPT_FUNCTION(ftime); #else #define INIT_FTIME #endif // SANITIZER_INTERCEPT_FTIME #if SANITIZER_INTERCEPT_XDR INTERCEPTOR(void, xdrmem_create, __sanitizer_XDR *xdrs, uptr addr, unsigned size, int op) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, xdrmem_create, xdrs, addr, size, op); // FIXME: under ASan the call below may write to freed memory and corrupt // its metadata. See // https://github.com/google/sanitizers/issues/321. REAL(xdrmem_create)(xdrs, addr, size, op); COMMON_INTERCEPTOR_WRITE_RANGE(ctx, xdrs, sizeof(*xdrs)); if (op == __sanitizer_XDR_ENCODE) { // It's not obvious how much data individual xdr_ routines write. // Simply unpoison the entire target buffer in advance. COMMON_INTERCEPTOR_WRITE_RANGE(ctx, (void *)addr, size); } } INTERCEPTOR(void, xdrstdio_create, __sanitizer_XDR *xdrs, void *file, int op) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, xdrstdio_create, xdrs, file, op); // FIXME: under ASan the call below may write to freed memory and corrupt // its metadata. See // https://github.com/google/sanitizers/issues/321. REAL(xdrstdio_create)(xdrs, file, op); COMMON_INTERCEPTOR_WRITE_RANGE(ctx, xdrs, sizeof(*xdrs)); } // FIXME: under ASan the call below may write to freed memory and corrupt // its metadata. See // https://github.com/google/sanitizers/issues/321. #define XDR_INTERCEPTOR(F, T) \ INTERCEPTOR(int, F, __sanitizer_XDR *xdrs, T *p) { \ void *ctx; \ COMMON_INTERCEPTOR_ENTER(ctx, F, xdrs, p); \ if (p && xdrs->x_op == __sanitizer_XDR_ENCODE) \ COMMON_INTERCEPTOR_READ_RANGE(ctx, p, sizeof(*p)); \ int res = REAL(F)(xdrs, p); \ if (res && p && xdrs->x_op == __sanitizer_XDR_DECODE) \ COMMON_INTERCEPTOR_WRITE_RANGE(ctx, p, sizeof(*p)); \ return res; \ } XDR_INTERCEPTOR(xdr_short, short) XDR_INTERCEPTOR(xdr_u_short, unsigned short) XDR_INTERCEPTOR(xdr_int, int) XDR_INTERCEPTOR(xdr_u_int, unsigned) XDR_INTERCEPTOR(xdr_long, long) XDR_INTERCEPTOR(xdr_u_long, unsigned long) XDR_INTERCEPTOR(xdr_hyper, long long) XDR_INTERCEPTOR(xdr_u_hyper, unsigned long long) XDR_INTERCEPTOR(xdr_longlong_t, long long) XDR_INTERCEPTOR(xdr_u_longlong_t, unsigned long long) XDR_INTERCEPTOR(xdr_int8_t, u8) XDR_INTERCEPTOR(xdr_uint8_t, u8) XDR_INTERCEPTOR(xdr_int16_t, u16) XDR_INTERCEPTOR(xdr_uint16_t, u16) XDR_INTERCEPTOR(xdr_int32_t, u32) XDR_INTERCEPTOR(xdr_uint32_t, u32) XDR_INTERCEPTOR(xdr_int64_t, u64) XDR_INTERCEPTOR(xdr_uint64_t, u64) XDR_INTERCEPTOR(xdr_quad_t, long long) XDR_INTERCEPTOR(xdr_u_quad_t, unsigned long long) XDR_INTERCEPTOR(xdr_bool, bool) XDR_INTERCEPTOR(xdr_enum, int) XDR_INTERCEPTOR(xdr_char, char) XDR_INTERCEPTOR(xdr_u_char, unsigned char) XDR_INTERCEPTOR(xdr_float, float) XDR_INTERCEPTOR(xdr_double, double) // FIXME: intercept xdr_array, opaque, union, vector, reference, pointer, // wrapstring, sizeof INTERCEPTOR(int, xdr_bytes, __sanitizer_XDR *xdrs, char **p, unsigned *sizep, unsigned maxsize) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, xdr_bytes, xdrs, p, sizep, maxsize); if (p && sizep && xdrs->x_op == __sanitizer_XDR_ENCODE) { COMMON_INTERCEPTOR_READ_RANGE(ctx, p, sizeof(*p)); COMMON_INTERCEPTOR_READ_RANGE(ctx, sizep, sizeof(*sizep)); COMMON_INTERCEPTOR_READ_RANGE(ctx, *p, *sizep); } // FIXME: under ASan the call below may write to freed memory and corrupt // its metadata. See // https://github.com/google/sanitizers/issues/321. int res = REAL(xdr_bytes)(xdrs, p, sizep, maxsize); if (p && sizep && xdrs->x_op == __sanitizer_XDR_DECODE) { COMMON_INTERCEPTOR_WRITE_RANGE(ctx, p, sizeof(*p)); COMMON_INTERCEPTOR_WRITE_RANGE(ctx, sizep, sizeof(*sizep)); if (res && *p && *sizep) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, *p, *sizep); } return res; } INTERCEPTOR(int, xdr_string, __sanitizer_XDR *xdrs, char **p, unsigned maxsize) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, xdr_string, xdrs, p, maxsize); if (p && xdrs->x_op == __sanitizer_XDR_ENCODE) { COMMON_INTERCEPTOR_READ_RANGE(ctx, p, sizeof(*p)); COMMON_INTERCEPTOR_READ_RANGE(ctx, *p, REAL(strlen)(*p) + 1); } // FIXME: under ASan the call below may write to freed memory and corrupt // its metadata. See // https://github.com/google/sanitizers/issues/321. int res = REAL(xdr_string)(xdrs, p, maxsize); if (p && xdrs->x_op == __sanitizer_XDR_DECODE) { COMMON_INTERCEPTOR_WRITE_RANGE(ctx, p, sizeof(*p)); if (res && *p) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, *p, REAL(strlen)(*p) + 1); } return res; } #define INIT_XDR \ COMMON_INTERCEPT_FUNCTION(xdrmem_create); \ COMMON_INTERCEPT_FUNCTION(xdrstdio_create); \ COMMON_INTERCEPT_FUNCTION(xdr_short); \ COMMON_INTERCEPT_FUNCTION(xdr_u_short); \ COMMON_INTERCEPT_FUNCTION(xdr_int); \ COMMON_INTERCEPT_FUNCTION(xdr_u_int); \ COMMON_INTERCEPT_FUNCTION(xdr_long); \ COMMON_INTERCEPT_FUNCTION(xdr_u_long); \ COMMON_INTERCEPT_FUNCTION(xdr_hyper); \ COMMON_INTERCEPT_FUNCTION(xdr_u_hyper); \ COMMON_INTERCEPT_FUNCTION(xdr_longlong_t); \ COMMON_INTERCEPT_FUNCTION(xdr_u_longlong_t); \ COMMON_INTERCEPT_FUNCTION(xdr_int8_t); \ COMMON_INTERCEPT_FUNCTION(xdr_uint8_t); \ COMMON_INTERCEPT_FUNCTION(xdr_int16_t); \ COMMON_INTERCEPT_FUNCTION(xdr_uint16_t); \ COMMON_INTERCEPT_FUNCTION(xdr_int32_t); \ COMMON_INTERCEPT_FUNCTION(xdr_uint32_t); \ COMMON_INTERCEPT_FUNCTION(xdr_int64_t); \ COMMON_INTERCEPT_FUNCTION(xdr_uint64_t); \ COMMON_INTERCEPT_FUNCTION(xdr_quad_t); \ COMMON_INTERCEPT_FUNCTION(xdr_u_quad_t); \ COMMON_INTERCEPT_FUNCTION(xdr_bool); \ COMMON_INTERCEPT_FUNCTION(xdr_enum); \ COMMON_INTERCEPT_FUNCTION(xdr_char); \ COMMON_INTERCEPT_FUNCTION(xdr_u_char); \ COMMON_INTERCEPT_FUNCTION(xdr_float); \ COMMON_INTERCEPT_FUNCTION(xdr_double); \ COMMON_INTERCEPT_FUNCTION(xdr_bytes); \ COMMON_INTERCEPT_FUNCTION(xdr_string); #else #define INIT_XDR #endif // SANITIZER_INTERCEPT_XDR #if SANITIZER_INTERCEPT_TSEARCH INTERCEPTOR(void *, tsearch, void *key, void **rootp, int (*compar)(const void *, const void *)) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, tsearch, key, rootp, compar); // FIXME: under ASan the call below may write to freed memory and corrupt // its metadata. See // https://github.com/google/sanitizers/issues/321. void *res = REAL(tsearch)(key, rootp, compar); if (res && *(void **)res == key) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, res, sizeof(void *)); return res; } #define INIT_TSEARCH COMMON_INTERCEPT_FUNCTION(tsearch); #else #define INIT_TSEARCH #endif #if SANITIZER_INTERCEPT_LIBIO_INTERNALS || SANITIZER_INTERCEPT_FOPEN || \ SANITIZER_INTERCEPT_OPEN_MEMSTREAM void unpoison_file(__sanitizer_FILE *fp) { #if SANITIZER_HAS_STRUCT_FILE COMMON_INTERCEPTOR_INITIALIZE_RANGE(fp, sizeof(*fp)); if (fp->_IO_read_base && fp->_IO_read_base < fp->_IO_read_end) COMMON_INTERCEPTOR_INITIALIZE_RANGE(fp->_IO_read_base, fp->_IO_read_end - fp->_IO_read_base); #endif // SANITIZER_HAS_STRUCT_FILE } #endif #if SANITIZER_INTERCEPT_LIBIO_INTERNALS // These guys are called when a .c source is built with -O2. INTERCEPTOR(int, __uflow, __sanitizer_FILE *fp) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, __uflow, fp); int res = REAL(__uflow)(fp); unpoison_file(fp); return res; } INTERCEPTOR(int, __underflow, __sanitizer_FILE *fp) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, __underflow, fp); int res = REAL(__underflow)(fp); unpoison_file(fp); return res; } INTERCEPTOR(int, __overflow, __sanitizer_FILE *fp, int ch) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, __overflow, fp, ch); int res = REAL(__overflow)(fp, ch); unpoison_file(fp); return res; } INTERCEPTOR(int, __wuflow, __sanitizer_FILE *fp) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, __wuflow, fp); int res = REAL(__wuflow)(fp); unpoison_file(fp); return res; } INTERCEPTOR(int, __wunderflow, __sanitizer_FILE *fp) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, __wunderflow, fp); int res = REAL(__wunderflow)(fp); unpoison_file(fp); return res; } INTERCEPTOR(int, __woverflow, __sanitizer_FILE *fp, int ch) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, __woverflow, fp, ch); int res = REAL(__woverflow)(fp, ch); unpoison_file(fp); return res; } #define INIT_LIBIO_INTERNALS \ COMMON_INTERCEPT_FUNCTION(__uflow); \ COMMON_INTERCEPT_FUNCTION(__underflow); \ COMMON_INTERCEPT_FUNCTION(__overflow); \ COMMON_INTERCEPT_FUNCTION(__wuflow); \ COMMON_INTERCEPT_FUNCTION(__wunderflow); \ COMMON_INTERCEPT_FUNCTION(__woverflow); #else #define INIT_LIBIO_INTERNALS #endif #if SANITIZER_INTERCEPT_FOPEN INTERCEPTOR(__sanitizer_FILE *, fopen, const char *path, const char *mode) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, fopen, path, mode); if (path) COMMON_INTERCEPTOR_READ_RANGE(ctx, path, REAL(strlen)(path) + 1); COMMON_INTERCEPTOR_READ_RANGE(ctx, mode, REAL(strlen)(mode) + 1); __sanitizer_FILE *res = REAL(fopen)(path, mode); COMMON_INTERCEPTOR_FILE_OPEN(ctx, res, path); if (res) unpoison_file(res); return res; } INTERCEPTOR(__sanitizer_FILE *, fdopen, int fd, const char *mode) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, fdopen, fd, mode); COMMON_INTERCEPTOR_READ_RANGE(ctx, mode, REAL(strlen)(mode) + 1); __sanitizer_FILE *res = REAL(fdopen)(fd, mode); if (res) unpoison_file(res); return res; } INTERCEPTOR(__sanitizer_FILE *, freopen, const char *path, const char *mode, __sanitizer_FILE *fp) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, freopen, path, mode, fp); if (path) COMMON_INTERCEPTOR_READ_RANGE(ctx, path, REAL(strlen)(path) + 1); COMMON_INTERCEPTOR_READ_RANGE(ctx, mode, REAL(strlen)(mode) + 1); COMMON_INTERCEPTOR_FILE_CLOSE(ctx, fp); __sanitizer_FILE *res = REAL(freopen)(path, mode, fp); COMMON_INTERCEPTOR_FILE_OPEN(ctx, res, path); if (res) unpoison_file(res); return res; } #define INIT_FOPEN \ COMMON_INTERCEPT_FUNCTION(fopen); \ COMMON_INTERCEPT_FUNCTION(fdopen); \ COMMON_INTERCEPT_FUNCTION(freopen); #else #define INIT_FOPEN #endif #if SANITIZER_INTERCEPT_FOPEN64 INTERCEPTOR(__sanitizer_FILE *, fopen64, const char *path, const char *mode) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, fopen64, path, mode); COMMON_INTERCEPTOR_READ_RANGE(ctx, path, REAL(strlen)(path) + 1); COMMON_INTERCEPTOR_READ_RANGE(ctx, mode, REAL(strlen)(mode) + 1); __sanitizer_FILE *res = REAL(fopen64)(path, mode); COMMON_INTERCEPTOR_FILE_OPEN(ctx, res, path); if (res) unpoison_file(res); return res; } INTERCEPTOR(__sanitizer_FILE *, freopen64, const char *path, const char *mode, __sanitizer_FILE *fp) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, freopen64, path, mode, fp); if (path) COMMON_INTERCEPTOR_READ_RANGE(ctx, path, REAL(strlen)(path) + 1); COMMON_INTERCEPTOR_READ_RANGE(ctx, mode, REAL(strlen)(mode) + 1); COMMON_INTERCEPTOR_FILE_CLOSE(ctx, fp); __sanitizer_FILE *res = REAL(freopen64)(path, mode, fp); COMMON_INTERCEPTOR_FILE_OPEN(ctx, res, path); if (res) unpoison_file(res); return res; } #define INIT_FOPEN64 \ COMMON_INTERCEPT_FUNCTION(fopen64); \ COMMON_INTERCEPT_FUNCTION(freopen64); #else #define INIT_FOPEN64 #endif #if SANITIZER_INTERCEPT_OPEN_MEMSTREAM INTERCEPTOR(__sanitizer_FILE *, open_memstream, char **ptr, SIZE_T *sizeloc) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, open_memstream, ptr, sizeloc); // FIXME: under ASan the call below may write to freed memory and corrupt // its metadata. See // https://github.com/google/sanitizers/issues/321. __sanitizer_FILE *res = REAL(open_memstream)(ptr, sizeloc); if (res) { COMMON_INTERCEPTOR_WRITE_RANGE(ctx, ptr, sizeof(*ptr)); COMMON_INTERCEPTOR_WRITE_RANGE(ctx, sizeloc, sizeof(*sizeloc)); unpoison_file(res); FileMetadata file = {ptr, sizeloc}; SetInterceptorMetadata(res, file); } return res; } INTERCEPTOR(__sanitizer_FILE *, open_wmemstream, wchar_t **ptr, SIZE_T *sizeloc) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, open_wmemstream, ptr, sizeloc); __sanitizer_FILE *res = REAL(open_wmemstream)(ptr, sizeloc); if (res) { COMMON_INTERCEPTOR_WRITE_RANGE(ctx, ptr, sizeof(*ptr)); COMMON_INTERCEPTOR_WRITE_RANGE(ctx, sizeloc, sizeof(*sizeloc)); unpoison_file(res); FileMetadata file = {(char **)ptr, sizeloc}; SetInterceptorMetadata(res, file); } return res; } INTERCEPTOR(__sanitizer_FILE *, fmemopen, void *buf, SIZE_T size, const char *mode) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, fmemopen, buf, size, mode); // FIXME: under ASan the call below may write to freed memory and corrupt // its metadata. See // https://github.com/google/sanitizers/issues/321. __sanitizer_FILE *res = REAL(fmemopen)(buf, size, mode); if (res) unpoison_file(res); return res; } #define INIT_OPEN_MEMSTREAM \ COMMON_INTERCEPT_FUNCTION(open_memstream); \ COMMON_INTERCEPT_FUNCTION(open_wmemstream); \ COMMON_INTERCEPT_FUNCTION(fmemopen); #else #define INIT_OPEN_MEMSTREAM #endif #if SANITIZER_INTERCEPT_OBSTACK static void initialize_obstack(__sanitizer_obstack *obstack) { COMMON_INTERCEPTOR_INITIALIZE_RANGE(obstack, sizeof(*obstack)); if (obstack->chunk) COMMON_INTERCEPTOR_INITIALIZE_RANGE(obstack->chunk, sizeof(*obstack->chunk)); } INTERCEPTOR(int, _obstack_begin_1, __sanitizer_obstack *obstack, int sz, int align, void *(*alloc_fn)(uptr arg, uptr sz), void (*free_fn)(uptr arg, void *p)) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, _obstack_begin_1, obstack, sz, align, alloc_fn, free_fn); int res = REAL(_obstack_begin_1)(obstack, sz, align, alloc_fn, free_fn); if (res) initialize_obstack(obstack); return res; } INTERCEPTOR(int, _obstack_begin, __sanitizer_obstack *obstack, int sz, int align, void *(*alloc_fn)(uptr sz), void (*free_fn)(void *p)) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, _obstack_begin, obstack, sz, align, alloc_fn, free_fn); int res = REAL(_obstack_begin)(obstack, sz, align, alloc_fn, free_fn); if (res) initialize_obstack(obstack); return res; } INTERCEPTOR(void, _obstack_newchunk, __sanitizer_obstack *obstack, int length) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, _obstack_newchunk, obstack, length); REAL(_obstack_newchunk)(obstack, length); if (obstack->chunk) COMMON_INTERCEPTOR_INITIALIZE_RANGE( obstack->chunk, obstack->next_free - (char *)obstack->chunk); } #define INIT_OBSTACK \ COMMON_INTERCEPT_FUNCTION(_obstack_begin_1); \ COMMON_INTERCEPT_FUNCTION(_obstack_begin); \ COMMON_INTERCEPT_FUNCTION(_obstack_newchunk); #else #define INIT_OBSTACK #endif #if SANITIZER_INTERCEPT_FFLUSH INTERCEPTOR(int, fflush, __sanitizer_FILE *fp) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, fflush, fp); int res = REAL(fflush)(fp); // FIXME: handle fp == NULL if (fp) { const FileMetadata *m = GetInterceptorMetadata(fp); if (m) COMMON_INTERCEPTOR_INITIALIZE_RANGE(*m->addr, *m->size); } return res; } #define INIT_FFLUSH COMMON_INTERCEPT_FUNCTION(fflush); #else #define INIT_FFLUSH #endif #if SANITIZER_INTERCEPT_FCLOSE INTERCEPTOR(int, fclose, __sanitizer_FILE *fp) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, fclose, fp); COMMON_INTERCEPTOR_FILE_CLOSE(ctx, fp); const FileMetadata *m = GetInterceptorMetadata(fp); int res = REAL(fclose)(fp); if (m) { COMMON_INTERCEPTOR_INITIALIZE_RANGE(*m->addr, *m->size); DeleteInterceptorMetadata(fp); } return res; } #define INIT_FCLOSE COMMON_INTERCEPT_FUNCTION(fclose); #else #define INIT_FCLOSE #endif #if SANITIZER_INTERCEPT_DLOPEN_DLCLOSE INTERCEPTOR(void*, dlopen, const char *filename, int flag) { void *ctx; COMMON_INTERCEPTOR_ENTER_NOIGNORE(ctx, dlopen, filename, flag); if (filename) COMMON_INTERCEPTOR_READ_STRING(ctx, filename, 0); COMMON_INTERCEPTOR_ON_DLOPEN(filename, flag); void *res = REAL(dlopen)(filename, flag); COMMON_INTERCEPTOR_LIBRARY_LOADED(filename, res); return res; } INTERCEPTOR(int, dlclose, void *handle) { void *ctx; COMMON_INTERCEPTOR_ENTER_NOIGNORE(ctx, dlclose, handle); int res = REAL(dlclose)(handle); COMMON_INTERCEPTOR_LIBRARY_UNLOADED(); return res; } #define INIT_DLOPEN_DLCLOSE \ COMMON_INTERCEPT_FUNCTION(dlopen); \ COMMON_INTERCEPT_FUNCTION(dlclose); #else #define INIT_DLOPEN_DLCLOSE #endif #if SANITIZER_INTERCEPT_GETPASS INTERCEPTOR(char *, getpass, const char *prompt) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, getpass, prompt); if (prompt) COMMON_INTERCEPTOR_READ_RANGE(ctx, prompt, REAL(strlen)(prompt)+1); char *res = REAL(getpass)(prompt); if (res) COMMON_INTERCEPTOR_INITIALIZE_RANGE(res, REAL(strlen)(res)+1); return res; } #define INIT_GETPASS COMMON_INTERCEPT_FUNCTION(getpass); #else #define INIT_GETPASS #endif #if SANITIZER_INTERCEPT_TIMERFD INTERCEPTOR(int, timerfd_settime, int fd, int flags, void *new_value, void *old_value) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, timerfd_settime, fd, flags, new_value, old_value); COMMON_INTERCEPTOR_READ_RANGE(ctx, new_value, struct_itimerspec_sz); int res = REAL(timerfd_settime)(fd, flags, new_value, old_value); if (res != -1 && old_value) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, old_value, struct_itimerspec_sz); return res; } INTERCEPTOR(int, timerfd_gettime, int fd, void *curr_value) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, timerfd_gettime, fd, curr_value); int res = REAL(timerfd_gettime)(fd, curr_value); if (res != -1 && curr_value) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, curr_value, struct_itimerspec_sz); return res; } #define INIT_TIMERFD \ COMMON_INTERCEPT_FUNCTION(timerfd_settime); \ COMMON_INTERCEPT_FUNCTION(timerfd_gettime); #else #define INIT_TIMERFD #endif #if SANITIZER_INTERCEPT_MLOCKX // Linux kernel has a bug that leads to kernel deadlock if a process // maps TBs of memory and then calls mlock(). static void MlockIsUnsupported() { static atomic_uint8_t printed; if (atomic_exchange(&printed, 1, memory_order_relaxed)) return; VPrintf(1, "%s ignores mlock/mlockall/munlock/munlockall\n", SanitizerToolName); } INTERCEPTOR(int, mlock, const void *addr, uptr len) { MlockIsUnsupported(); return 0; } INTERCEPTOR(int, munlock, const void *addr, uptr len) { MlockIsUnsupported(); return 0; } INTERCEPTOR(int, mlockall, int flags) { MlockIsUnsupported(); return 0; } INTERCEPTOR(int, munlockall, void) { MlockIsUnsupported(); return 0; } #define INIT_MLOCKX \ COMMON_INTERCEPT_FUNCTION(mlock); \ COMMON_INTERCEPT_FUNCTION(munlock); \ COMMON_INTERCEPT_FUNCTION(mlockall); \ COMMON_INTERCEPT_FUNCTION(munlockall); #else #define INIT_MLOCKX #endif // SANITIZER_INTERCEPT_MLOCKX #if SANITIZER_INTERCEPT_FOPENCOOKIE struct WrappedCookie { void *real_cookie; __sanitizer_cookie_io_functions_t real_io_funcs; }; static uptr wrapped_read(void *cookie, char *buf, uptr size) { COMMON_INTERCEPTOR_UNPOISON_PARAM(3); WrappedCookie *wrapped_cookie = (WrappedCookie *)cookie; __sanitizer_cookie_io_read real_read = wrapped_cookie->real_io_funcs.read; return real_read ? real_read(wrapped_cookie->real_cookie, buf, size) : 0; } static uptr wrapped_write(void *cookie, const char *buf, uptr size) { COMMON_INTERCEPTOR_UNPOISON_PARAM(3); WrappedCookie *wrapped_cookie = (WrappedCookie *)cookie; __sanitizer_cookie_io_write real_write = wrapped_cookie->real_io_funcs.write; return real_write ? real_write(wrapped_cookie->real_cookie, buf, size) : size; } static int wrapped_seek(void *cookie, u64 *offset, int whence) { COMMON_INTERCEPTOR_UNPOISON_PARAM(3); COMMON_INTERCEPTOR_INITIALIZE_RANGE(offset, sizeof(*offset)); WrappedCookie *wrapped_cookie = (WrappedCookie *)cookie; __sanitizer_cookie_io_seek real_seek = wrapped_cookie->real_io_funcs.seek; return real_seek ? real_seek(wrapped_cookie->real_cookie, offset, whence) : -1; } static int wrapped_close(void *cookie) { COMMON_INTERCEPTOR_UNPOISON_PARAM(1); WrappedCookie *wrapped_cookie = (WrappedCookie *)cookie; __sanitizer_cookie_io_close real_close = wrapped_cookie->real_io_funcs.close; int res = real_close ? real_close(wrapped_cookie->real_cookie) : 0; InternalFree(wrapped_cookie); return res; } INTERCEPTOR(__sanitizer_FILE *, fopencookie, void *cookie, const char *mode, __sanitizer_cookie_io_functions_t io_funcs) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, fopencookie, cookie, mode, io_funcs); WrappedCookie *wrapped_cookie = (WrappedCookie *)InternalAlloc(sizeof(WrappedCookie)); wrapped_cookie->real_cookie = cookie; wrapped_cookie->real_io_funcs = io_funcs; __sanitizer_FILE *res = REAL(fopencookie)(wrapped_cookie, mode, {wrapped_read, wrapped_write, wrapped_seek, wrapped_close}); return res; } #define INIT_FOPENCOOKIE COMMON_INTERCEPT_FUNCTION(fopencookie); #else #define INIT_FOPENCOOKIE #endif // SANITIZER_INTERCEPT_FOPENCOOKIE #if SANITIZER_INTERCEPT_SEM INTERCEPTOR(int, sem_init, __sanitizer_sem_t *s, int pshared, unsigned value) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, sem_init, s, pshared, value); // Workaround a bug in glibc's "old" semaphore implementation by // zero-initializing the sem_t contents. This has to be done here because // interceptors bind to the lowest symbols version by default, hitting the // buggy code path while the non-sanitized build of the same code works fine. REAL(memset)(s, 0, sizeof(*s)); int res = REAL(sem_init)(s, pshared, value); return res; } INTERCEPTOR(int, sem_destroy, __sanitizer_sem_t *s) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, sem_destroy, s); int res = REAL(sem_destroy)(s); return res; } INTERCEPTOR(int, sem_wait, __sanitizer_sem_t *s) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, sem_wait, s); int res = COMMON_INTERCEPTOR_BLOCK_REAL(sem_wait)(s); if (res == 0) { COMMON_INTERCEPTOR_ACQUIRE(ctx, (uptr)s); } return res; } INTERCEPTOR(int, sem_trywait, __sanitizer_sem_t *s) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, sem_trywait, s); int res = COMMON_INTERCEPTOR_BLOCK_REAL(sem_trywait)(s); if (res == 0) { COMMON_INTERCEPTOR_ACQUIRE(ctx, (uptr)s); } return res; } INTERCEPTOR(int, sem_timedwait, __sanitizer_sem_t *s, void *abstime) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, sem_timedwait, s, abstime); COMMON_INTERCEPTOR_READ_RANGE(ctx, abstime, struct_timespec_sz); int res = COMMON_INTERCEPTOR_BLOCK_REAL(sem_timedwait)(s, abstime); if (res == 0) { COMMON_INTERCEPTOR_ACQUIRE(ctx, (uptr)s); } return res; } INTERCEPTOR(int, sem_post, __sanitizer_sem_t *s) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, sem_post, s); COMMON_INTERCEPTOR_RELEASE(ctx, (uptr)s); int res = REAL(sem_post)(s); return res; } INTERCEPTOR(int, sem_getvalue, __sanitizer_sem_t *s, int *sval) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, sem_getvalue, s, sval); int res = REAL(sem_getvalue)(s, sval); if (res == 0) { COMMON_INTERCEPTOR_ACQUIRE(ctx, (uptr)s); COMMON_INTERCEPTOR_WRITE_RANGE(ctx, sval, sizeof(*sval)); } return res; } #define INIT_SEM \ COMMON_INTERCEPT_FUNCTION(sem_init); \ COMMON_INTERCEPT_FUNCTION(sem_destroy); \ COMMON_INTERCEPT_FUNCTION(sem_wait); \ COMMON_INTERCEPT_FUNCTION(sem_trywait); \ COMMON_INTERCEPT_FUNCTION(sem_timedwait); \ COMMON_INTERCEPT_FUNCTION(sem_post); \ COMMON_INTERCEPT_FUNCTION(sem_getvalue); #else #define INIT_SEM #endif // SANITIZER_INTERCEPT_SEM #if SANITIZER_INTERCEPT_PTHREAD_SETCANCEL INTERCEPTOR(int, pthread_setcancelstate, int state, int *oldstate) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, pthread_setcancelstate, state, oldstate); int res = REAL(pthread_setcancelstate)(state, oldstate); if (res == 0) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, oldstate, sizeof(*oldstate)); return res; } INTERCEPTOR(int, pthread_setcanceltype, int type, int *oldtype) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, pthread_setcanceltype, type, oldtype); int res = REAL(pthread_setcanceltype)(type, oldtype); if (res == 0) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, oldtype, sizeof(*oldtype)); return res; } #define INIT_PTHREAD_SETCANCEL \ COMMON_INTERCEPT_FUNCTION(pthread_setcancelstate); \ COMMON_INTERCEPT_FUNCTION(pthread_setcanceltype); #else #define INIT_PTHREAD_SETCANCEL #endif #if SANITIZER_INTERCEPT_MINCORE INTERCEPTOR(int, mincore, void *addr, uptr length, unsigned char *vec) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, mincore, addr, length, vec); int res = REAL(mincore)(addr, length, vec); if (res == 0) { uptr page_size = GetPageSizeCached(); uptr vec_size = ((length + page_size - 1) & (~(page_size - 1))) / page_size; COMMON_INTERCEPTOR_WRITE_RANGE(ctx, vec, vec_size); } return res; } #define INIT_MINCORE COMMON_INTERCEPT_FUNCTION(mincore); #else #define INIT_MINCORE #endif #if SANITIZER_INTERCEPT_PROCESS_VM_READV INTERCEPTOR(SSIZE_T, process_vm_readv, int pid, __sanitizer_iovec *local_iov, uptr liovcnt, __sanitizer_iovec *remote_iov, uptr riovcnt, uptr flags) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, process_vm_readv, pid, local_iov, liovcnt, remote_iov, riovcnt, flags); SSIZE_T res = REAL(process_vm_readv)(pid, local_iov, liovcnt, remote_iov, riovcnt, flags); if (res > 0) write_iovec(ctx, local_iov, liovcnt, res); return res; } INTERCEPTOR(SSIZE_T, process_vm_writev, int pid, __sanitizer_iovec *local_iov, uptr liovcnt, __sanitizer_iovec *remote_iov, uptr riovcnt, uptr flags) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, process_vm_writev, pid, local_iov, liovcnt, remote_iov, riovcnt, flags); SSIZE_T res = REAL(process_vm_writev)(pid, local_iov, liovcnt, remote_iov, riovcnt, flags); if (res > 0) read_iovec(ctx, local_iov, liovcnt, res); return res; } #define INIT_PROCESS_VM_READV \ COMMON_INTERCEPT_FUNCTION(process_vm_readv); \ COMMON_INTERCEPT_FUNCTION(process_vm_writev); #else #define INIT_PROCESS_VM_READV #endif #if SANITIZER_INTERCEPT_CTERMID INTERCEPTOR(char *, ctermid, char *s) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, ctermid, s); char *res = REAL(ctermid)(s); if (res) { COMMON_INTERCEPTOR_INITIALIZE_RANGE(res, REAL(strlen)(res) + 1); } return res; } #define INIT_CTERMID COMMON_INTERCEPT_FUNCTION(ctermid); #else #define INIT_CTERMID #endif #if SANITIZER_INTERCEPT_CTERMID_R INTERCEPTOR(char *, ctermid_r, char *s) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, ctermid_r, s); char *res = REAL(ctermid_r)(s); if (res) { COMMON_INTERCEPTOR_INITIALIZE_RANGE(res, REAL(strlen)(res) + 1); } return res; } #define INIT_CTERMID_R COMMON_INTERCEPT_FUNCTION(ctermid_r); #else #define INIT_CTERMID_R #endif #if SANITIZER_INTERCEPT_RECV_RECVFROM INTERCEPTOR(SSIZE_T, recv, int fd, void *buf, SIZE_T len, int flags) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, recv, fd, buf, len, flags); COMMON_INTERCEPTOR_FD_ACCESS(ctx, fd); SSIZE_T res = REAL(recv)(fd, buf, len, flags); if (res > 0) { COMMON_INTERCEPTOR_WRITE_RANGE(ctx, buf, Min((SIZE_T)res, len)); } if (res >= 0 && fd >= 0) COMMON_INTERCEPTOR_FD_ACQUIRE(ctx, fd); return res; } INTERCEPTOR(SSIZE_T, recvfrom, int fd, void *buf, SIZE_T len, int flags, void *srcaddr, int *addrlen) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, recvfrom, fd, buf, len, flags, srcaddr, addrlen); COMMON_INTERCEPTOR_FD_ACCESS(ctx, fd); SIZE_T srcaddr_sz; if (srcaddr) srcaddr_sz = *addrlen; (void)srcaddr_sz; // prevent "set but not used" warning SSIZE_T res = REAL(recvfrom)(fd, buf, len, flags, srcaddr, addrlen); if (res > 0) { COMMON_INTERCEPTOR_WRITE_RANGE(ctx, buf, Min((SIZE_T)res, len)); if (srcaddr) COMMON_INTERCEPTOR_INITIALIZE_RANGE(srcaddr, Min((SIZE_T)*addrlen, srcaddr_sz)); } return res; } #define INIT_RECV_RECVFROM \ COMMON_INTERCEPT_FUNCTION(recv); \ COMMON_INTERCEPT_FUNCTION(recvfrom); #else #define INIT_RECV_RECVFROM #endif #if SANITIZER_INTERCEPT_SEND_SENDTO INTERCEPTOR(SSIZE_T, send, int fd, void *buf, SIZE_T len, int flags) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, send, fd, buf, len, flags); if (fd >= 0) { COMMON_INTERCEPTOR_FD_ACCESS(ctx, fd); COMMON_INTERCEPTOR_FD_RELEASE(ctx, fd); } SSIZE_T res = REAL(send)(fd, buf, len, flags); if (common_flags()->intercept_send && res > 0) COMMON_INTERCEPTOR_READ_RANGE(ctx, buf, Min((SIZE_T)res, len)); return res; } INTERCEPTOR(SSIZE_T, sendto, int fd, void *buf, SIZE_T len, int flags, void *dstaddr, int addrlen) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, sendto, fd, buf, len, flags, dstaddr, addrlen); if (fd >= 0) { COMMON_INTERCEPTOR_FD_ACCESS(ctx, fd); COMMON_INTERCEPTOR_FD_RELEASE(ctx, fd); } // Can't check dstaddr as it may have uninitialized padding at the end. SSIZE_T res = REAL(sendto)(fd, buf, len, flags, dstaddr, addrlen); if (common_flags()->intercept_send && res > 0) COMMON_INTERCEPTOR_READ_RANGE(ctx, buf, Min((SIZE_T)res, len)); return res; } #define INIT_SEND_SENDTO \ COMMON_INTERCEPT_FUNCTION(send); \ COMMON_INTERCEPT_FUNCTION(sendto); #else #define INIT_SEND_SENDTO #endif #if SANITIZER_INTERCEPT_EVENTFD_READ_WRITE INTERCEPTOR(int, eventfd_read, int fd, u64 *value) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, eventfd_read, fd, value); COMMON_INTERCEPTOR_FD_ACCESS(ctx, fd); int res = REAL(eventfd_read)(fd, value); if (res == 0) { COMMON_INTERCEPTOR_WRITE_RANGE(ctx, value, sizeof(*value)); if (fd >= 0) COMMON_INTERCEPTOR_FD_ACQUIRE(ctx, fd); } return res; } INTERCEPTOR(int, eventfd_write, int fd, u64 value) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, eventfd_write, fd, value); if (fd >= 0) { COMMON_INTERCEPTOR_FD_ACCESS(ctx, fd); COMMON_INTERCEPTOR_FD_RELEASE(ctx, fd); } int res = REAL(eventfd_write)(fd, value); return res; } #define INIT_EVENTFD_READ_WRITE \ COMMON_INTERCEPT_FUNCTION(eventfd_read); \ COMMON_INTERCEPT_FUNCTION(eventfd_write) #else #define INIT_EVENTFD_READ_WRITE #endif #if SANITIZER_INTERCEPT_STAT INTERCEPTOR(int, stat, const char *path, void *buf) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, stat, path, buf); if (common_flags()->intercept_stat) COMMON_INTERCEPTOR_READ_STRING(ctx, path, 0); int res = REAL(stat)(path, buf); if (!res) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, buf, __sanitizer::struct_stat_sz); return res; } #define INIT_STAT COMMON_INTERCEPT_FUNCTION(stat) #else #define INIT_STAT #endif #if SANITIZER_INTERCEPT___XSTAT INTERCEPTOR(int, __xstat, int version, const char *path, void *buf) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, __xstat, version, path, buf); if (common_flags()->intercept_stat) COMMON_INTERCEPTOR_READ_STRING(ctx, path, 0); int res = REAL(__xstat)(version, path, buf); if (!res) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, buf, __sanitizer::struct_stat_sz); return res; } #define INIT___XSTAT COMMON_INTERCEPT_FUNCTION(__xstat) #else #define INIT___XSTAT #endif #if SANITIZER_INTERCEPT___XSTAT64 INTERCEPTOR(int, __xstat64, int version, const char *path, void *buf) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, __xstat64, version, path, buf); if (common_flags()->intercept_stat) COMMON_INTERCEPTOR_READ_STRING(ctx, path, 0); int res = REAL(__xstat64)(version, path, buf); if (!res) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, buf, __sanitizer::struct_stat64_sz); return res; } #define INIT___XSTAT64 COMMON_INTERCEPT_FUNCTION(__xstat64) #else #define INIT___XSTAT64 #endif #if SANITIZER_INTERCEPT___LXSTAT INTERCEPTOR(int, __lxstat, int version, const char *path, void *buf) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, __lxstat, version, path, buf); if (common_flags()->intercept_stat) COMMON_INTERCEPTOR_READ_STRING(ctx, path, 0); int res = REAL(__lxstat)(version, path, buf); if (!res) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, buf, __sanitizer::struct_stat_sz); return res; } #define INIT___LXSTAT COMMON_INTERCEPT_FUNCTION(__lxstat) #else #define INIT___LXSTAT #endif #if SANITIZER_INTERCEPT___LXSTAT64 INTERCEPTOR(int, __lxstat64, int version, const char *path, void *buf) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, __lxstat64, version, path, buf); if (common_flags()->intercept_stat) COMMON_INTERCEPTOR_READ_STRING(ctx, path, 0); int res = REAL(__lxstat64)(version, path, buf); if (!res) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, buf, __sanitizer::struct_stat64_sz); return res; } #define INIT___LXSTAT64 COMMON_INTERCEPT_FUNCTION(__lxstat64) #else #define INIT___LXSTAT64 #endif // FIXME: add other *stat interceptor #if SANITIZER_INTERCEPT_UTMP INTERCEPTOR(void *, getutent, int dummy) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, getutent, dummy); void *res = REAL(getutent)(dummy); if (res) COMMON_INTERCEPTOR_INITIALIZE_RANGE(res, __sanitizer::struct_utmp_sz); return res; } INTERCEPTOR(void *, getutid, void *ut) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, getutid, ut); void *res = REAL(getutid)(ut); if (res) COMMON_INTERCEPTOR_INITIALIZE_RANGE(res, __sanitizer::struct_utmp_sz); return res; } INTERCEPTOR(void *, getutline, void *ut) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, getutline, ut); void *res = REAL(getutline)(ut); if (res) COMMON_INTERCEPTOR_INITIALIZE_RANGE(res, __sanitizer::struct_utmp_sz); return res; } #define INIT_UTMP \ COMMON_INTERCEPT_FUNCTION(getutent); \ COMMON_INTERCEPT_FUNCTION(getutid); \ COMMON_INTERCEPT_FUNCTION(getutline); #else #define INIT_UTMP #endif #if SANITIZER_INTERCEPT_UTMPX INTERCEPTOR(void *, getutxent, int dummy) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, getutxent, dummy); void *res = REAL(getutxent)(dummy); if (res) COMMON_INTERCEPTOR_INITIALIZE_RANGE(res, __sanitizer::struct_utmpx_sz); return res; } INTERCEPTOR(void *, getutxid, void *ut) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, getutxid, ut); void *res = REAL(getutxid)(ut); if (res) COMMON_INTERCEPTOR_INITIALIZE_RANGE(res, __sanitizer::struct_utmpx_sz); return res; } INTERCEPTOR(void *, getutxline, void *ut) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, getutxline, ut); void *res = REAL(getutxline)(ut); if (res) COMMON_INTERCEPTOR_INITIALIZE_RANGE(res, __sanitizer::struct_utmpx_sz); return res; } #define INIT_UTMPX \ COMMON_INTERCEPT_FUNCTION(getutxent); \ COMMON_INTERCEPT_FUNCTION(getutxid); \ COMMON_INTERCEPT_FUNCTION(getutxline); #else #define INIT_UTMPX #endif #if SANITIZER_INTERCEPT_GETLOADAVG INTERCEPTOR(int, getloadavg, double *loadavg, int nelem) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, getloadavg, loadavg, nelem); int res = REAL(getloadavg)(loadavg, nelem); if (res > 0) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, loadavg, res * sizeof(*loadavg)); return res; } #define INIT_GETLOADAVG \ COMMON_INTERCEPT_FUNCTION(getloadavg); #else #define INIT_GETLOADAVG #endif static void InitializeCommonInterceptors() { static u64 metadata_mem[sizeof(MetadataHashMap) / sizeof(u64) + 1]; interceptor_metadata_map = new((void *)&metadata_mem) MetadataHashMap(); INIT_TEXTDOMAIN; INIT_STRLEN; INIT_STRNLEN; INIT_STRCMP; INIT_STRNCMP; INIT_STRCASECMP; INIT_STRNCASECMP; INIT_STRSTR; INIT_STRCASESTR; INIT_STRCHR; INIT_STRCHRNUL; INIT_STRRCHR; INIT_STRSPN; INIT_STRTOK; INIT_STRPBRK; INIT_MEMSET; INIT_MEMMOVE; INIT_MEMCPY; INIT_MEMCHR; INIT_MEMCMP; INIT_MEMRCHR; INIT_MEMMEM; INIT_READ; INIT_FREAD; INIT_PREAD; INIT_PREAD64; INIT_READV; INIT_PREADV; INIT_PREADV64; INIT_WRITE; INIT_FWRITE; INIT_PWRITE; INIT_PWRITE64; INIT_WRITEV; INIT_PWRITEV; INIT_PWRITEV64; INIT_PRCTL; INIT_LOCALTIME_AND_FRIENDS; INIT_STRPTIME; INIT_SCANF; INIT_ISOC99_SCANF; INIT_PRINTF; INIT_PRINTF_L; INIT_ISOC99_PRINTF; INIT_FREXP; INIT_FREXPF_FREXPL; INIT_GETPWNAM_AND_FRIENDS; INIT_GETPWNAM_R_AND_FRIENDS; INIT_GETPWENT; INIT_FGETPWENT; INIT_GETPWENT_R; INIT_SETPWENT; INIT_CLOCK_GETTIME; INIT_GETITIMER; INIT_TIME; INIT_GLOB; INIT_WAIT; INIT_WAIT4; INIT_INET; INIT_PTHREAD_GETSCHEDPARAM; INIT_GETADDRINFO; INIT_GETNAMEINFO; INIT_GETSOCKNAME; INIT_GETHOSTBYNAME; INIT_GETHOSTBYNAME_R; INIT_GETHOSTBYNAME2_R; INIT_GETHOSTBYADDR_R; INIT_GETHOSTENT_R; INIT_GETSOCKOPT; INIT_ACCEPT; INIT_ACCEPT4; INIT_MODF; INIT_RECVMSG; INIT_SENDMSG; INIT_GETPEERNAME; INIT_IOCTL; INIT_INET_ATON; INIT_SYSINFO; INIT_READDIR; INIT_READDIR64; INIT_PTRACE; INIT_SETLOCALE; INIT_GETCWD; INIT_GET_CURRENT_DIR_NAME; INIT_STRTOIMAX; INIT_MBSTOWCS; INIT_MBSNRTOWCS; INIT_WCSTOMBS; INIT_WCSNRTOMBS; INIT_WCRTOMB; INIT_TCGETATTR; INIT_REALPATH; INIT_CANONICALIZE_FILE_NAME; INIT_CONFSTR; INIT_SCHED_GETAFFINITY; INIT_SCHED_GETPARAM; INIT_STRERROR; INIT_STRERROR_R; INIT_XPG_STRERROR_R; INIT_SCANDIR; INIT_SCANDIR64; INIT_GETGROUPS; INIT_POLL; INIT_PPOLL; INIT_WORDEXP; INIT_SIGWAIT; INIT_SIGWAITINFO; INIT_SIGTIMEDWAIT; INIT_SIGSETOPS; INIT_SIGPENDING; INIT_SIGPROCMASK; INIT_BACKTRACE; INIT__EXIT; INIT_PTHREAD_MUTEX_LOCK; INIT_PTHREAD_MUTEX_UNLOCK; INIT_GETMNTENT; INIT_GETMNTENT_R; INIT_STATFS; INIT_STATFS64; INIT_STATVFS; INIT_STATVFS64; INIT_INITGROUPS; INIT_ETHER_NTOA_ATON; INIT_ETHER_HOST; INIT_ETHER_R; INIT_SHMCTL; INIT_RANDOM_R; INIT_PTHREAD_ATTR_GET; INIT_PTHREAD_ATTR_GETINHERITSCHED; INIT_PTHREAD_ATTR_GETAFFINITY_NP; INIT_PTHREAD_MUTEXATTR_GETPSHARED; INIT_PTHREAD_MUTEXATTR_GETTYPE; INIT_PTHREAD_MUTEXATTR_GETPROTOCOL; INIT_PTHREAD_MUTEXATTR_GETPRIOCEILING; INIT_PTHREAD_MUTEXATTR_GETROBUST; INIT_PTHREAD_MUTEXATTR_GETROBUST_NP; INIT_PTHREAD_RWLOCKATTR_GETPSHARED; INIT_PTHREAD_RWLOCKATTR_GETKIND_NP; INIT_PTHREAD_CONDATTR_GETPSHARED; INIT_PTHREAD_CONDATTR_GETCLOCK; INIT_PTHREAD_BARRIERATTR_GETPSHARED; INIT_TMPNAM; INIT_TMPNAM_R; INIT_TTYNAME_R; INIT_TEMPNAM; INIT_PTHREAD_SETNAME_NP; INIT_SINCOS; INIT_REMQUO; INIT_LGAMMA; INIT_LGAMMA_R; INIT_LGAMMAL_R; INIT_DRAND48_R; INIT_RAND_R; INIT_GETLINE; INIT_ICONV; INIT_TIMES; INIT_TLS_GET_ADDR; INIT_LISTXATTR; INIT_GETXATTR; INIT_GETRESID; INIT_GETIFADDRS; INIT_IF_INDEXTONAME; INIT_CAPGET; INIT_AEABI_MEM; INIT___BZERO; INIT_FTIME; INIT_XDR; INIT_TSEARCH; INIT_LIBIO_INTERNALS; INIT_FOPEN; INIT_FOPEN64; INIT_OPEN_MEMSTREAM; INIT_OBSTACK; INIT_FFLUSH; INIT_FCLOSE; INIT_DLOPEN_DLCLOSE; INIT_GETPASS; INIT_TIMERFD; INIT_MLOCKX; INIT_FOPENCOOKIE; INIT_SEM; INIT_PTHREAD_SETCANCEL; INIT_MINCORE; INIT_PROCESS_VM_READV; INIT_CTERMID; INIT_CTERMID_R; INIT_RECV_RECVFROM; INIT_SEND_SENDTO; INIT_STAT; INIT_EVENTFD_READ_WRITE; INIT___XSTAT; INIT___XSTAT64; INIT___LXSTAT; INIT___LXSTAT64; // FIXME: add other *stat interceptors. INIT_UTMP; INIT_UTMPX; INIT_GETLOADAVG; } Index: vendor/compiler-rt/dist/lib/sanitizer_common/sanitizer_coverage_interface.inc =================================================================== --- vendor/compiler-rt/dist/lib/sanitizer_common/sanitizer_coverage_interface.inc (revision 317221) +++ vendor/compiler-rt/dist/lib/sanitizer_common/sanitizer_coverage_interface.inc (revision 317222) @@ -1,40 +1,32 @@ //===-- sanitizer_coverage_interface.inc ----------------------------------===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// // Sanitizer Coverage interface list. //===----------------------------------------------------------------------===// INTERFACE_FUNCTION(__sanitizer_cov) INTERFACE_FUNCTION(__sanitizer_cov_dump) -INTERFACE_FUNCTION(__sanitizer_cov_indir_call16) INTERFACE_FUNCTION(__sanitizer_cov_init) INTERFACE_FUNCTION(__sanitizer_cov_module_init) -INTERFACE_FUNCTION(__sanitizer_cov_trace_basic_block) -INTERFACE_FUNCTION(__sanitizer_cov_trace_func_enter) INTERFACE_FUNCTION(__sanitizer_cov_with_check) INTERFACE_FUNCTION(__sanitizer_dump_coverage) INTERFACE_FUNCTION(__sanitizer_dump_trace_pc_guard_coverage) -INTERFACE_FUNCTION(__sanitizer_get_coverage_guards) -INTERFACE_FUNCTION(__sanitizer_get_number_of_counters) -INTERFACE_FUNCTION(__sanitizer_get_total_unique_caller_callee_pairs) INTERFACE_FUNCTION(__sanitizer_get_total_unique_coverage) INTERFACE_FUNCTION(__sanitizer_maybe_open_cov_file) -INTERFACE_FUNCTION(__sanitizer_reset_coverage) -INTERFACE_FUNCTION(__sanitizer_update_counter_bitset_and_clear_counters) INTERFACE_WEAK_FUNCTION(__sancov_default_options) INTERFACE_WEAK_FUNCTION(__sanitizer_cov_trace_cmp) INTERFACE_WEAK_FUNCTION(__sanitizer_cov_trace_cmp1) INTERFACE_WEAK_FUNCTION(__sanitizer_cov_trace_cmp2) INTERFACE_WEAK_FUNCTION(__sanitizer_cov_trace_cmp4) INTERFACE_WEAK_FUNCTION(__sanitizer_cov_trace_cmp8) INTERFACE_WEAK_FUNCTION(__sanitizer_cov_trace_div4) INTERFACE_WEAK_FUNCTION(__sanitizer_cov_trace_div8) INTERFACE_WEAK_FUNCTION(__sanitizer_cov_trace_gep) INTERFACE_WEAK_FUNCTION(__sanitizer_cov_trace_pc_guard) INTERFACE_WEAK_FUNCTION(__sanitizer_cov_trace_pc_guard_init) INTERFACE_WEAK_FUNCTION(__sanitizer_cov_trace_pc_indir) INTERFACE_WEAK_FUNCTION(__sanitizer_cov_trace_switch) Index: vendor/compiler-rt/dist/lib/sanitizer_common/sanitizer_coverage_libcdep.cc =================================================================== --- vendor/compiler-rt/dist/lib/sanitizer_common/sanitizer_coverage_libcdep.cc (revision 317221) +++ vendor/compiler-rt/dist/lib/sanitizer_common/sanitizer_coverage_libcdep.cc (revision 317222) @@ -1,1035 +1,684 @@ //===-- sanitizer_coverage.cc ---------------------------------------------===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// // // Sanitizer Coverage. // This file implements run-time support for a poor man's coverage tool. // // Compiler instrumentation: // For every interesting basic block the compiler injects the following code: // if (Guard < 0) { // __sanitizer_cov(&Guard); // } // At the module start up time __sanitizer_cov_module_init sets the guards // to consecutive negative numbers (-1, -2, -3, ...). // It's fine to call __sanitizer_cov more than once for a given block. // // Run-time: // - __sanitizer_cov(): record that we've executed the PC (GET_CALLER_PC). // and atomically set Guard to -Guard. // - __sanitizer_cov_dump: dump the coverage data to disk. // For every module of the current process that has coverage data // this will create a file module_name.PID.sancov. // // The file format is simple: the first 8 bytes is the magic, // one of 0xC0BFFFFFFFFFFF64 and 0xC0BFFFFFFFFFFF32. The last byte of the // magic defines the size of the following offsets. // The rest of the data is the offsets in the module. // // Eventually, this coverage implementation should be obsoleted by a more // powerful general purpose Clang/LLVM coverage instrumentation. // Consider this implementation as prototype. // // FIXME: support (or at least test with) dlclose. //===----------------------------------------------------------------------===// #include "sanitizer_allocator_internal.h" #include "sanitizer_common.h" #include "sanitizer_libc.h" #include "sanitizer_mutex.h" #include "sanitizer_procmaps.h" #include "sanitizer_stacktrace.h" #include "sanitizer_symbolizer.h" #include "sanitizer_flags.h" using namespace __sanitizer; static const u64 kMagic64 = 0xC0BFFFFFFFFFFF64ULL; static const u64 kMagic32 = 0xC0BFFFFFFFFFFF32ULL; static const uptr kNumWordsForMagic = SANITIZER_WORDSIZE == 64 ? 1 : 2; static const u64 kMagic = SANITIZER_WORDSIZE == 64 ? kMagic64 : kMagic32; static atomic_uint32_t dump_once_guard; // Ensure that CovDump runs only once. static atomic_uintptr_t coverage_counter; -static atomic_uintptr_t caller_callee_counter; -static void ResetGlobalCounters() { - return atomic_store(&coverage_counter, 0, memory_order_relaxed); - return atomic_store(&caller_callee_counter, 0, memory_order_relaxed); -} - // pc_array is the array containing the covered PCs. // To make the pc_array thread- and async-signal-safe it has to be large enough. // 128M counters "ought to be enough for anybody" (4M on 32-bit). // With coverage_direct=1 in ASAN_OPTIONS, pc_array memory is mapped to a file. // In this mode, __sanitizer_cov_dump does nothing, and CovUpdateMapping() // dump current memory layout to another file. static bool cov_sandboxed = false; static fd_t cov_fd = kInvalidFd; static unsigned int cov_max_block_size = 0; static bool coverage_enabled = false; static const char *coverage_dir; namespace __sanitizer { class CoverageData { public: void Init(); void Enable(); void Disable(); void ReInit(); void BeforeFork(); void AfterFork(int child_pid); void Extend(uptr npcs); void Add(uptr pc, u32 *guard); - void IndirCall(uptr caller, uptr callee, uptr callee_cache[], - uptr cache_size); - void DumpCallerCalleePairs(); - void DumpTrace(); void DumpAsBitSet(); - void DumpCounters(); void DumpOffsets(); void DumpAll(); - ALWAYS_INLINE - void TraceBasicBlock(u32 *id); - void InitializeGuardArray(s32 *guards); void InitializeGuards(s32 *guards, uptr n, const char *module_name, uptr caller_pc); - void InitializeCounters(u8 *counters, uptr n); void ReinitializeGuards(); - uptr GetNumberOf8bitCounters(); - uptr Update8bitCounterBitsetAndClearCounters(u8 *bitset); uptr *data(); uptr size() const; private: struct NamedPcRange { const char *copied_module_name; uptr beg, end; // elements [beg,end) in pc_array. }; void DirectOpen(); void UpdateModuleNameVec(uptr caller_pc, uptr range_beg, uptr range_end); void GetRangeOffsets(const NamedPcRange& r, Symbolizer* s, InternalMmapVector* offsets) const; // Maximal size pc array may ever grow. // We MmapNoReserve this space to ensure that the array is contiguous. static const uptr kPcArrayMaxSize = FIRST_32_SECOND_64(1 << (SANITIZER_ANDROID ? 24 : 26), 1 << 27); // The amount file mapping for the pc array is grown by. static const uptr kPcArrayMmapSize = 64 * 1024; // pc_array is allocated with MmapNoReserveOrDie and so it uses only as // much RAM as it really needs. uptr *pc_array; // Index of the first available pc_array slot. atomic_uintptr_t pc_array_index; // Array size. atomic_uintptr_t pc_array_size; // Current file mapped size of the pc array. uptr pc_array_mapped_size; // Descriptor of the file mapped pc array. fd_t pc_fd; // Vector of coverage guard arrays, protected by mu. InternalMmapVectorNoCtor guard_array_vec; // Vector of module and compilation unit pc ranges. InternalMmapVectorNoCtor comp_unit_name_vec; InternalMmapVectorNoCtor module_name_vec; - struct CounterAndSize { - u8 *counters; - uptr n; - }; - - InternalMmapVectorNoCtor counters_vec; - uptr num_8bit_counters; - - // Caller-Callee (cc) array, size and current index. - static const uptr kCcArrayMaxSize = FIRST_32_SECOND_64(1 << 18, 1 << 24); - uptr **cc_array; - atomic_uintptr_t cc_array_index; - atomic_uintptr_t cc_array_size; - - // Tracing event array, size and current pointer. - // We record all events (basic block entries) in a global buffer of u32 - // values. Each such value is the index in pc_array. - // So far the tracing is highly experimental: - // - not thread-safe; - // - does not support long traces; - // - not tuned for performance. - // Windows doesn't do overcommit (committed virtual memory costs swap), so - // programs can't reliably map such large amounts of virtual memory. - // TODO(etienneb): Find a way to support coverage of larger executable -static const uptr kTrEventArrayMaxSize = - (SANITIZER_WORDSIZE == 32 || SANITIZER_WINDOWS) ? 1 << 22 : 1 << 30; - u32 *tr_event_array; - uptr tr_event_array_size; - u32 *tr_event_pointer; - static const uptr kTrPcArrayMaxSize = FIRST_32_SECOND_64(1 << 22, 1 << 27); - StaticSpinMutex mu; }; static CoverageData coverage_data; void CovUpdateMapping(const char *path, uptr caller_pc = 0); void CoverageData::DirectOpen() { InternalScopedString path(kMaxPathLength); internal_snprintf((char *)path.data(), path.size(), "%s/%zd.sancov.raw", coverage_dir, internal_getpid()); pc_fd = OpenFile(path.data(), RdWr); if (pc_fd == kInvalidFd) { Report("Coverage: failed to open %s for reading/writing\n", path.data()); Die(); } pc_array_mapped_size = 0; CovUpdateMapping(coverage_dir); } void CoverageData::Init() { pc_fd = kInvalidFd; } void CoverageData::Enable() { if (pc_array) return; pc_array = reinterpret_cast( MmapNoReserveOrDie(sizeof(uptr) * kPcArrayMaxSize, "CovInit")); atomic_store(&pc_array_index, 0, memory_order_relaxed); if (common_flags()->coverage_direct) { atomic_store(&pc_array_size, 0, memory_order_relaxed); } else { atomic_store(&pc_array_size, kPcArrayMaxSize, memory_order_relaxed); } - - cc_array = reinterpret_cast(MmapNoReserveOrDie( - sizeof(uptr *) * kCcArrayMaxSize, "CovInit::cc_array")); - atomic_store(&cc_array_size, kCcArrayMaxSize, memory_order_relaxed); - atomic_store(&cc_array_index, 0, memory_order_relaxed); - - // Allocate tr_event_array with a guard page at the end. - tr_event_array = reinterpret_cast(MmapNoReserveOrDie( - sizeof(tr_event_array[0]) * kTrEventArrayMaxSize + GetMmapGranularity(), - "CovInit::tr_event_array")); - MprotectNoAccess( - reinterpret_cast(&tr_event_array[kTrEventArrayMaxSize]), - GetMmapGranularity()); - tr_event_array_size = kTrEventArrayMaxSize; - tr_event_pointer = tr_event_array; - - num_8bit_counters = 0; } void CoverageData::InitializeGuardArray(s32 *guards) { Enable(); // Make sure coverage is enabled at this point. s32 n = guards[0]; for (s32 j = 1; j <= n; j++) { uptr idx = atomic_load_relaxed(&pc_array_index); atomic_store_relaxed(&pc_array_index, idx + 1); guards[j] = -static_cast(idx + 1); } } void CoverageData::Disable() { if (pc_array) { UnmapOrDie(pc_array, sizeof(uptr) * kPcArrayMaxSize); pc_array = nullptr; } - if (cc_array) { - UnmapOrDie(cc_array, sizeof(uptr *) * kCcArrayMaxSize); - cc_array = nullptr; - } - if (tr_event_array) { - UnmapOrDie(tr_event_array, - sizeof(tr_event_array[0]) * kTrEventArrayMaxSize + - GetMmapGranularity()); - tr_event_array = nullptr; - tr_event_pointer = nullptr; - } if (pc_fd != kInvalidFd) { CloseFile(pc_fd); pc_fd = kInvalidFd; } } void CoverageData::ReinitializeGuards() { // Assuming single thread. atomic_store(&pc_array_index, 0, memory_order_relaxed); for (uptr i = 0; i < guard_array_vec.size(); i++) InitializeGuardArray(guard_array_vec[i]); } void CoverageData::ReInit() { Disable(); if (coverage_enabled) { if (common_flags()->coverage_direct) { // In memory-mapped mode we must extend the new file to the known array // size. uptr size = atomic_load(&pc_array_size, memory_order_relaxed); uptr npcs = size / sizeof(uptr); Enable(); if (size) Extend(npcs); if (coverage_enabled) CovUpdateMapping(coverage_dir); } else { Enable(); } } // Re-initialize the guards. // We are single-threaded now, no need to grab any lock. CHECK_EQ(atomic_load(&pc_array_index, memory_order_relaxed), 0); ReinitializeGuards(); } void CoverageData::BeforeFork() { mu.Lock(); } void CoverageData::AfterFork(int child_pid) { // We are single-threaded so it's OK to release the lock early. mu.Unlock(); if (child_pid == 0) ReInit(); } // Extend coverage PC array to fit additional npcs elements. void CoverageData::Extend(uptr npcs) { if (!common_flags()->coverage_direct) return; SpinMutexLock l(&mu); uptr size = atomic_load(&pc_array_size, memory_order_relaxed); size += npcs * sizeof(uptr); if (coverage_enabled && size > pc_array_mapped_size) { if (pc_fd == kInvalidFd) DirectOpen(); CHECK_NE(pc_fd, kInvalidFd); uptr new_mapped_size = pc_array_mapped_size; while (size > new_mapped_size) new_mapped_size += kPcArrayMmapSize; CHECK_LE(new_mapped_size, sizeof(uptr) * kPcArrayMaxSize); // Extend the file and map the new space at the end of pc_array. uptr res = internal_ftruncate(pc_fd, new_mapped_size); int err; if (internal_iserror(res, &err)) { Printf("failed to extend raw coverage file: %d\n", err); Die(); } uptr next_map_base = ((uptr)pc_array) + pc_array_mapped_size; void *p = MapWritableFileToMemory((void *)next_map_base, new_mapped_size - pc_array_mapped_size, pc_fd, pc_array_mapped_size); CHECK_EQ((uptr)p, next_map_base); pc_array_mapped_size = new_mapped_size; } atomic_store(&pc_array_size, size, memory_order_release); } -void CoverageData::InitializeCounters(u8 *counters, uptr n) { - if (!counters) return; - CHECK_EQ(reinterpret_cast(counters) % 16, 0); - n = RoundUpTo(n, 16); // The compiler must ensure that counters is 16-aligned. - SpinMutexLock l(&mu); - counters_vec.push_back({counters, n}); - num_8bit_counters += n; -} - void CoverageData::UpdateModuleNameVec(uptr caller_pc, uptr range_beg, uptr range_end) { auto sym = Symbolizer::GetOrInit(); if (!sym) return; const char *module_name = sym->GetModuleNameForPc(caller_pc); if (!module_name) return; if (module_name_vec.empty() || module_name_vec.back().copied_module_name != module_name) module_name_vec.push_back({module_name, range_beg, range_end}); else module_name_vec.back().end = range_end; } void CoverageData::InitializeGuards(s32 *guards, uptr n, const char *comp_unit_name, uptr caller_pc) { // The array 'guards' has n+1 elements, we use the element zero // to store 'n'. CHECK_LT(n, 1 << 30); guards[0] = static_cast(n); InitializeGuardArray(guards); SpinMutexLock l(&mu); uptr range_end = atomic_load(&pc_array_index, memory_order_relaxed); uptr range_beg = range_end - n; comp_unit_name_vec.push_back({comp_unit_name, range_beg, range_end}); guard_array_vec.push_back(guards); UpdateModuleNameVec(caller_pc, range_beg, range_end); } static const uptr kBundleCounterBits = 16; // When coverage_order_pcs==true and SANITIZER_WORDSIZE==64 // we insert the global counter into the first 16 bits of the PC. uptr BundlePcAndCounter(uptr pc, uptr counter) { if (SANITIZER_WORDSIZE != 64 || !common_flags()->coverage_order_pcs) return pc; static const uptr kMaxCounter = (1 << kBundleCounterBits) - 1; if (counter > kMaxCounter) counter = kMaxCounter; CHECK_EQ(0, pc >> (SANITIZER_WORDSIZE - kBundleCounterBits)); return pc | (counter << (SANITIZER_WORDSIZE - kBundleCounterBits)); } uptr UnbundlePc(uptr bundle) { if (SANITIZER_WORDSIZE != 64 || !common_flags()->coverage_order_pcs) return bundle; return (bundle << kBundleCounterBits) >> kBundleCounterBits; } uptr UnbundleCounter(uptr bundle) { if (SANITIZER_WORDSIZE != 64 || !common_flags()->coverage_order_pcs) return 0; return bundle >> (SANITIZER_WORDSIZE - kBundleCounterBits); } // If guard is negative, atomically set it to -guard and store the PC in // pc_array. void CoverageData::Add(uptr pc, u32 *guard) { atomic_uint32_t *atomic_guard = reinterpret_cast(guard); s32 guard_value = atomic_load(atomic_guard, memory_order_relaxed); if (guard_value >= 0) return; atomic_store(atomic_guard, -guard_value, memory_order_relaxed); if (!pc_array) return; uptr idx = -guard_value - 1; if (idx >= atomic_load(&pc_array_index, memory_order_acquire)) return; // May happen after fork when pc_array_index becomes 0. CHECK_LT(idx, atomic_load(&pc_array_size, memory_order_acquire)); uptr counter = atomic_fetch_add(&coverage_counter, 1, memory_order_relaxed); pc_array[idx] = BundlePcAndCounter(pc, counter); } -// Registers a pair caller=>callee. -// When a given caller is seen for the first time, the callee_cache is added -// to the global array cc_array, callee_cache[0] is set to caller and -// callee_cache[1] is set to cache_size. -// Then we are trying to add callee to callee_cache [2,cache_size) if it is -// not there yet. -// If the cache is full we drop the callee (may want to fix this later). -void CoverageData::IndirCall(uptr caller, uptr callee, uptr callee_cache[], - uptr cache_size) { - if (!cc_array) return; - atomic_uintptr_t *atomic_callee_cache = - reinterpret_cast(callee_cache); - uptr zero = 0; - if (atomic_compare_exchange_strong(&atomic_callee_cache[0], &zero, caller, - memory_order_seq_cst)) { - uptr idx = atomic_fetch_add(&cc_array_index, 1, memory_order_relaxed); - CHECK_LT(idx * sizeof(uptr), - atomic_load(&cc_array_size, memory_order_acquire)); - callee_cache[1] = cache_size; - cc_array[idx] = callee_cache; - } - CHECK_EQ(atomic_load(&atomic_callee_cache[0], memory_order_relaxed), caller); - for (uptr i = 2; i < cache_size; i++) { - uptr was = 0; - if (atomic_compare_exchange_strong(&atomic_callee_cache[i], &was, callee, - memory_order_seq_cst)) { - atomic_fetch_add(&caller_callee_counter, 1, memory_order_relaxed); - return; - } - if (was == callee) // Already have this callee. - return; - } -} - -uptr CoverageData::GetNumberOf8bitCounters() { - return num_8bit_counters; -} - -// Map every 8bit counter to a 8-bit bitset and clear the counter. -uptr CoverageData::Update8bitCounterBitsetAndClearCounters(u8 *bitset) { - uptr num_new_bits = 0; - uptr cur = 0; - // For better speed we map 8 counters to 8 bytes of bitset at once. - static const uptr kBatchSize = 8; - CHECK_EQ(reinterpret_cast(bitset) % kBatchSize, 0); - for (uptr i = 0, len = counters_vec.size(); i < len; i++) { - u8 *c = counters_vec[i].counters; - uptr n = counters_vec[i].n; - CHECK_EQ(n % 16, 0); - CHECK_EQ(cur % kBatchSize, 0); - CHECK_EQ(reinterpret_cast(c) % kBatchSize, 0); - if (!bitset) { - internal_bzero_aligned16(c, n); - cur += n; - continue; - } - for (uptr j = 0; j < n; j += kBatchSize, cur += kBatchSize) { - CHECK_LT(cur, num_8bit_counters); - u64 *pc64 = reinterpret_cast(c + j); - u64 *pb64 = reinterpret_cast(bitset + cur); - u64 c64 = *pc64; - u64 old_bits_64 = *pb64; - u64 new_bits_64 = old_bits_64; - if (c64) { - *pc64 = 0; - for (uptr k = 0; k < kBatchSize; k++) { - u64 x = (c64 >> (8 * k)) & 0xff; - if (x) { - u64 bit = 0; - /**/ if (x >= 128) bit = 128; - else if (x >= 32) bit = 64; - else if (x >= 16) bit = 32; - else if (x >= 8) bit = 16; - else if (x >= 4) bit = 8; - else if (x >= 3) bit = 4; - else if (x >= 2) bit = 2; - else if (x >= 1) bit = 1; - u64 mask = bit << (8 * k); - if (!(new_bits_64 & mask)) { - num_new_bits++; - new_bits_64 |= mask; - } - } - } - *pb64 = new_bits_64; - } - } - } - CHECK_EQ(cur, num_8bit_counters); - return num_new_bits; -} - uptr *CoverageData::data() { return pc_array; } uptr CoverageData::size() const { return atomic_load(&pc_array_index, memory_order_relaxed); } // Block layout for packed file format: header, followed by module name (no // trailing zero), followed by data blob. struct CovHeader { int pid; unsigned int module_name_length; unsigned int data_length; }; static void CovWritePacked(int pid, const char *module, const void *blob, unsigned int blob_size) { if (cov_fd == kInvalidFd) return; unsigned module_name_length = internal_strlen(module); CovHeader header = {pid, module_name_length, blob_size}; if (cov_max_block_size == 0) { // Writing to a file. Just go ahead. WriteToFile(cov_fd, &header, sizeof(header)); WriteToFile(cov_fd, module, module_name_length); WriteToFile(cov_fd, blob, blob_size); } else { // Writing to a socket. We want to split the data into appropriately sized // blocks. InternalScopedBuffer block(cov_max_block_size); CHECK_EQ((uptr)block.data(), (uptr)(CovHeader *)block.data()); uptr header_size_with_module = sizeof(header) + module_name_length; CHECK_LT(header_size_with_module, cov_max_block_size); unsigned int max_payload_size = cov_max_block_size - header_size_with_module; char *block_pos = block.data(); internal_memcpy(block_pos, &header, sizeof(header)); block_pos += sizeof(header); internal_memcpy(block_pos, module, module_name_length); block_pos += module_name_length; char *block_data_begin = block_pos; const char *blob_pos = (const char *)blob; while (blob_size > 0) { unsigned int payload_size = Min(blob_size, max_payload_size); blob_size -= payload_size; internal_memcpy(block_data_begin, blob_pos, payload_size); blob_pos += payload_size; ((CovHeader *)block.data())->data_length = payload_size; WriteToFile(cov_fd, block.data(), header_size_with_module + payload_size); } } } // If packed = false: .. (name = module name). // If packed = true and name == 0: ... // If packed = true and name != 0: .. (name is // user-supplied). static fd_t CovOpenFile(InternalScopedString *path, bool packed, const char *name, const char *extension = "sancov") { path->clear(); if (!packed) { CHECK(name); path->append("%s/%s.%zd.%s", coverage_dir, name, internal_getpid(), extension); } else { if (!name) path->append("%s/%zd.%s.packed", coverage_dir, internal_getpid(), extension); else path->append("%s/%s.%s.packed", coverage_dir, name, extension); } error_t err; fd_t fd = OpenFile(path->data(), WrOnly, &err); if (fd == kInvalidFd) Report("SanitizerCoverage: failed to open %s for writing (reason: %d)\n", path->data(), err); return fd; } -// Dump trace PCs and trace events into two separate files. -void CoverageData::DumpTrace() { - uptr max_idx = tr_event_pointer - tr_event_array; - if (!max_idx) return; - auto sym = Symbolizer::GetOrInit(); - if (!sym) - return; - InternalScopedString out(32 << 20); - for (uptr i = 0, n = size(); i < n; i++) { - const char *module_name = ""; - uptr module_address = 0; - sym->GetModuleNameAndOffsetForPC(UnbundlePc(pc_array[i]), &module_name, - &module_address); - out.append("%s 0x%zx\n", module_name, module_address); - } - InternalScopedString path(kMaxPathLength); - fd_t fd = CovOpenFile(&path, false, "trace-points"); - if (fd == kInvalidFd) return; - WriteToFile(fd, out.data(), out.length()); - CloseFile(fd); - - fd = CovOpenFile(&path, false, "trace-compunits"); - if (fd == kInvalidFd) return; - out.clear(); - for (uptr i = 0; i < comp_unit_name_vec.size(); i++) - out.append("%s\n", comp_unit_name_vec[i].copied_module_name); - WriteToFile(fd, out.data(), out.length()); - CloseFile(fd); - - fd = CovOpenFile(&path, false, "trace-events"); - if (fd == kInvalidFd) return; - uptr bytes_to_write = max_idx * sizeof(tr_event_array[0]); - u8 *event_bytes = reinterpret_cast(tr_event_array); - // The trace file could be huge, and may not be written with a single syscall. - while (bytes_to_write) { - uptr actually_written; - if (WriteToFile(fd, event_bytes, bytes_to_write, &actually_written) && - actually_written <= bytes_to_write) { - bytes_to_write -= actually_written; - event_bytes += actually_written; - } else { - break; - } - } - CloseFile(fd); - VReport(1, " CovDump: Trace: %zd PCs written\n", size()); - VReport(1, " CovDump: Trace: %zd Events written\n", max_idx); -} - -// This function dumps the caller=>callee pairs into a file as a sequence of -// lines like "module_name offset". -void CoverageData::DumpCallerCalleePairs() { - uptr max_idx = atomic_load(&cc_array_index, memory_order_relaxed); - if (!max_idx) return; - auto sym = Symbolizer::GetOrInit(); - if (!sym) - return; - InternalScopedString out(32 << 20); - uptr total = 0; - for (uptr i = 0; i < max_idx; i++) { - uptr *cc_cache = cc_array[i]; - CHECK(cc_cache); - uptr caller = cc_cache[0]; - uptr n_callees = cc_cache[1]; - const char *caller_module_name = ""; - uptr caller_module_address = 0; - sym->GetModuleNameAndOffsetForPC(caller, &caller_module_name, - &caller_module_address); - for (uptr j = 2; j < n_callees; j++) { - uptr callee = cc_cache[j]; - if (!callee) break; - total++; - const char *callee_module_name = ""; - uptr callee_module_address = 0; - sym->GetModuleNameAndOffsetForPC(callee, &callee_module_name, - &callee_module_address); - out.append("%s 0x%zx\n%s 0x%zx\n", caller_module_name, - caller_module_address, callee_module_name, - callee_module_address); - } - } - InternalScopedString path(kMaxPathLength); - fd_t fd = CovOpenFile(&path, false, "caller-callee"); - if (fd == kInvalidFd) return; - WriteToFile(fd, out.data(), out.length()); - CloseFile(fd); - VReport(1, " CovDump: %zd caller-callee pairs written\n", total); -} - -// Record the current PC into the event buffer. -// Every event is a u32 value (index in tr_pc_array_index) so we compute -// it once and then cache in the provided 'cache' storage. -// -// This function will eventually be inlined by the compiler. -void CoverageData::TraceBasicBlock(u32 *id) { - // Will trap here if - // 1. coverage is not enabled at run-time. - // 2. The array tr_event_array is full. - *tr_event_pointer = *id - 1; - tr_event_pointer++; -} - -void CoverageData::DumpCounters() { - if (!common_flags()->coverage_counters) return; - uptr n = coverage_data.GetNumberOf8bitCounters(); - if (!n) return; - InternalScopedBuffer bitset(n); - coverage_data.Update8bitCounterBitsetAndClearCounters(bitset.data()); - InternalScopedString path(kMaxPathLength); - - for (uptr m = 0; m < module_name_vec.size(); m++) { - auto r = module_name_vec[m]; - CHECK(r.copied_module_name); - CHECK_LE(r.beg, r.end); - CHECK_LE(r.end, size()); - const char *base_name = StripModuleName(r.copied_module_name); - fd_t fd = - CovOpenFile(&path, /* packed */ false, base_name, "counters-sancov"); - if (fd == kInvalidFd) return; - WriteToFile(fd, bitset.data() + r.beg, r.end - r.beg); - CloseFile(fd); - VReport(1, " CovDump: %zd counters written for '%s'\n", r.end - r.beg, - base_name); - } -} - void CoverageData::DumpAsBitSet() { if (!common_flags()->coverage_bitset) return; if (!size()) return; InternalScopedBuffer out(size()); InternalScopedString path(kMaxPathLength); for (uptr m = 0; m < module_name_vec.size(); m++) { uptr n_set_bits = 0; auto r = module_name_vec[m]; CHECK(r.copied_module_name); CHECK_LE(r.beg, r.end); CHECK_LE(r.end, size()); for (uptr i = r.beg; i < r.end; i++) { uptr pc = UnbundlePc(pc_array[i]); out[i] = pc ? '1' : '0'; if (pc) n_set_bits++; } const char *base_name = StripModuleName(r.copied_module_name); fd_t fd = CovOpenFile(&path, /* packed */false, base_name, "bitset-sancov"); if (fd == kInvalidFd) return; WriteToFile(fd, out.data() + r.beg, r.end - r.beg); CloseFile(fd); VReport(1, " CovDump: bitset of %zd bits written for '%s', %zd bits are set\n", r.end - r.beg, base_name, n_set_bits); } } void CoverageData::GetRangeOffsets(const NamedPcRange& r, Symbolizer* sym, InternalMmapVector* offsets) const { offsets->clear(); for (uptr i = 0; i < kNumWordsForMagic; i++) offsets->push_back(0); CHECK(r.copied_module_name); CHECK_LE(r.beg, r.end); CHECK_LE(r.end, size()); for (uptr i = r.beg; i < r.end; i++) { uptr pc = UnbundlePc(pc_array[i]); uptr counter = UnbundleCounter(pc_array[i]); if (!pc) continue; // Not visited. uptr offset = 0; sym->GetModuleNameAndOffsetForPC(pc, nullptr, &offset); offsets->push_back(BundlePcAndCounter(offset, counter)); } CHECK_GE(offsets->size(), kNumWordsForMagic); SortArray(offsets->data(), offsets->size()); for (uptr i = 0; i < offsets->size(); i++) (*offsets)[i] = UnbundlePc((*offsets)[i]); } static void GenerateHtmlReport(const InternalMmapVector &cov_files) { if (!common_flags()->html_cov_report) { return; } char *sancov_path = FindPathToBinary(common_flags()->sancov_path); if (sancov_path == nullptr) { return; } InternalMmapVector sancov_argv(cov_files.size() * 2 + 3); sancov_argv.push_back(sancov_path); sancov_argv.push_back(internal_strdup("-html-report")); auto argv_deleter = at_scope_exit([&] { for (uptr i = 0; i < sancov_argv.size(); ++i) { InternalFree(sancov_argv[i]); } }); for (const auto &cov_file : cov_files) { sancov_argv.push_back(internal_strdup(cov_file)); } { ListOfModules modules; modules.init(); for (const LoadedModule &module : modules) { sancov_argv.push_back(internal_strdup(module.full_name())); } } InternalScopedString report_path(kMaxPathLength); fd_t report_fd = CovOpenFile(&report_path, false /* packed */, GetProcessName(), "html"); int pid = StartSubprocess(sancov_argv[0], sancov_argv.data(), kInvalidFd /* stdin */, report_fd /* std_out */); if (pid > 0) { int result = WaitForProcess(pid); if (result == 0) Printf("coverage report generated to %s\n", report_path.data()); } } void CoverageData::DumpOffsets() { auto sym = Symbolizer::GetOrInit(); if (!common_flags()->coverage_pcs) return; CHECK_NE(sym, nullptr); InternalMmapVector offsets(0); InternalScopedString path(kMaxPathLength); InternalMmapVector cov_files(module_name_vec.size()); auto cov_files_deleter = at_scope_exit([&] { for (uptr i = 0; i < cov_files.size(); ++i) { InternalFree(cov_files[i]); } }); for (uptr m = 0; m < module_name_vec.size(); m++) { auto r = module_name_vec[m]; GetRangeOffsets(r, sym, &offsets); uptr num_offsets = offsets.size() - kNumWordsForMagic; u64 *magic_p = reinterpret_cast(offsets.data()); CHECK_EQ(*magic_p, 0ULL); // FIXME: we may want to write 32-bit offsets even in 64-mode // if all the offsets are small enough. *magic_p = kMagic; const char *module_name = StripModuleName(r.copied_module_name); if (cov_sandboxed) { if (cov_fd != kInvalidFd) { CovWritePacked(internal_getpid(), module_name, offsets.data(), offsets.size() * sizeof(offsets[0])); VReport(1, " CovDump: %zd PCs written to packed file\n", num_offsets); } } else { // One file per module per process. fd_t fd = CovOpenFile(&path, false /* packed */, module_name); if (fd == kInvalidFd) continue; WriteToFile(fd, offsets.data(), offsets.size() * sizeof(offsets[0])); CloseFile(fd); cov_files.push_back(internal_strdup(path.data())); VReport(1, " CovDump: %s: %zd PCs written\n", path.data(), num_offsets); } } if (cov_fd != kInvalidFd) CloseFile(cov_fd); GenerateHtmlReport(cov_files); } void CoverageData::DumpAll() { if (!coverage_enabled || common_flags()->coverage_direct) return; if (atomic_fetch_add(&dump_once_guard, 1, memory_order_relaxed)) return; DumpAsBitSet(); - DumpCounters(); - DumpTrace(); DumpOffsets(); - DumpCallerCalleePairs(); } void CovPrepareForSandboxing(__sanitizer_sandbox_arguments *args) { if (!args) return; if (!coverage_enabled) return; cov_sandboxed = args->coverage_sandboxed; if (!cov_sandboxed) return; cov_max_block_size = args->coverage_max_block_size; if (args->coverage_fd >= 0) { cov_fd = (fd_t)args->coverage_fd; } else { InternalScopedString path(kMaxPathLength); // Pre-open the file now. The sandbox won't allow us to do it later. cov_fd = CovOpenFile(&path, true /* packed */, nullptr); } } fd_t MaybeOpenCovFile(const char *name) { CHECK(name); if (!coverage_enabled) return kInvalidFd; InternalScopedString path(kMaxPathLength); return CovOpenFile(&path, true /* packed */, name); } void CovBeforeFork() { coverage_data.BeforeFork(); } void CovAfterFork(int child_pid) { coverage_data.AfterFork(child_pid); } static void MaybeDumpCoverage() { if (common_flags()->coverage) __sanitizer_cov_dump(); } void InitializeCoverage(bool enabled, const char *dir) { if (coverage_enabled) return; // May happen if two sanitizer enable coverage in the same process. coverage_enabled = enabled; coverage_dir = dir; coverage_data.Init(); if (enabled) coverage_data.Enable(); if (!common_flags()->coverage_direct) Atexit(__sanitizer_cov_dump); AddDieCallback(MaybeDumpCoverage); } void ReInitializeCoverage(bool enabled, const char *dir) { coverage_enabled = enabled; coverage_dir = dir; coverage_data.ReInit(); } void CoverageUpdateMapping() { if (coverage_enabled) CovUpdateMapping(coverage_dir); } } // namespace __sanitizer extern "C" { SANITIZER_INTERFACE_ATTRIBUTE void __sanitizer_cov(u32 *guard) { coverage_data.Add(StackTrace::GetPreviousInstructionPc(GET_CALLER_PC()), guard); } SANITIZER_INTERFACE_ATTRIBUTE void __sanitizer_cov_with_check(u32 *guard) { atomic_uint32_t *atomic_guard = reinterpret_cast(guard); if (static_cast( __sanitizer::atomic_load(atomic_guard, memory_order_relaxed)) < 0) coverage_data.Add(StackTrace::GetPreviousInstructionPc(GET_CALLER_PC()), guard); } -SANITIZER_INTERFACE_ATTRIBUTE void -__sanitizer_cov_indir_call16(uptr callee, uptr callee_cache16[]) { - coverage_data.IndirCall(StackTrace::GetPreviousInstructionPc(GET_CALLER_PC()), - callee, callee_cache16, 16); -} SANITIZER_INTERFACE_ATTRIBUTE void __sanitizer_cov_init() { coverage_enabled = true; coverage_dir = common_flags()->coverage_dir; coverage_data.Init(); } SANITIZER_INTERFACE_ATTRIBUTE void __sanitizer_cov_dump() { coverage_data.DumpAll(); __sanitizer_dump_trace_pc_guard_coverage(); } SANITIZER_INTERFACE_ATTRIBUTE void __sanitizer_cov_module_init(s32 *guards, uptr npcs, u8 *counters, const char *comp_unit_name) { coverage_data.InitializeGuards(guards, npcs, comp_unit_name, GET_CALLER_PC()); - coverage_data.InitializeCounters(counters, npcs); if (!common_flags()->coverage_direct) return; if (SANITIZER_ANDROID && coverage_enabled) { // dlopen/dlclose interceptors do not work on Android, so we rely on // Extend() calls to update .sancov.map. CovUpdateMapping(coverage_dir, GET_CALLER_PC()); } coverage_data.Extend(npcs); } SANITIZER_INTERFACE_ATTRIBUTE sptr __sanitizer_maybe_open_cov_file(const char *name) { return (sptr)MaybeOpenCovFile(name); } SANITIZER_INTERFACE_ATTRIBUTE uptr __sanitizer_get_total_unique_coverage() { return atomic_load(&coverage_counter, memory_order_relaxed); -} - -SANITIZER_INTERFACE_ATTRIBUTE -uptr __sanitizer_get_total_unique_caller_callee_pairs() { - return atomic_load(&caller_callee_counter, memory_order_relaxed); -} - -SANITIZER_INTERFACE_ATTRIBUTE -void __sanitizer_cov_trace_func_enter(u32 *id) { - __sanitizer_cov_with_check(id); - coverage_data.TraceBasicBlock(id); -} -SANITIZER_INTERFACE_ATTRIBUTE -void __sanitizer_cov_trace_basic_block(u32 *id) { - __sanitizer_cov_with_check(id); - coverage_data.TraceBasicBlock(id); -} -SANITIZER_INTERFACE_ATTRIBUTE -void __sanitizer_reset_coverage() { - ResetGlobalCounters(); - coverage_data.ReinitializeGuards(); - internal_bzero_aligned16( - coverage_data.data(), - RoundUpTo(coverage_data.size() * sizeof(coverage_data.data()[0]), 16)); -} -SANITIZER_INTERFACE_ATTRIBUTE -uptr __sanitizer_get_coverage_guards(uptr **data) { - *data = coverage_data.data(); - return coverage_data.size(); -} - -SANITIZER_INTERFACE_ATTRIBUTE -uptr __sanitizer_get_number_of_counters() { - return coverage_data.GetNumberOf8bitCounters(); -} - -SANITIZER_INTERFACE_ATTRIBUTE -uptr __sanitizer_update_counter_bitset_and_clear_counters(u8 *bitset) { - return coverage_data.Update8bitCounterBitsetAndClearCounters(bitset); } // Default empty implementations (weak). Users should redefine them. SANITIZER_INTERFACE_WEAK_DEF(void, __sanitizer_cov_trace_cmp, void) {} SANITIZER_INTERFACE_WEAK_DEF(void, __sanitizer_cov_trace_cmp1, void) {} SANITIZER_INTERFACE_WEAK_DEF(void, __sanitizer_cov_trace_cmp2, void) {} SANITIZER_INTERFACE_WEAK_DEF(void, __sanitizer_cov_trace_cmp4, void) {} SANITIZER_INTERFACE_WEAK_DEF(void, __sanitizer_cov_trace_cmp8, void) {} SANITIZER_INTERFACE_WEAK_DEF(void, __sanitizer_cov_trace_switch, void) {} SANITIZER_INTERFACE_WEAK_DEF(void, __sanitizer_cov_trace_div4, void) {} SANITIZER_INTERFACE_WEAK_DEF(void, __sanitizer_cov_trace_div8, void) {} SANITIZER_INTERFACE_WEAK_DEF(void, __sanitizer_cov_trace_gep, void) {} SANITIZER_INTERFACE_WEAK_DEF(void, __sanitizer_cov_trace_pc_indir, void) {} } // extern "C" Index: vendor/compiler-rt/dist/lib/sanitizer_common/sanitizer_internal_defs.h =================================================================== --- vendor/compiler-rt/dist/lib/sanitizer_common/sanitizer_internal_defs.h (revision 317221) +++ vendor/compiler-rt/dist/lib/sanitizer_common/sanitizer_internal_defs.h (revision 317222) @@ -1,388 +1,394 @@ //===-- sanitizer_internal_defs.h -------------------------------*- C++ -*-===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// // // This file is shared between AddressSanitizer and ThreadSanitizer. // It contains macro used in run-time libraries code. //===----------------------------------------------------------------------===// #ifndef SANITIZER_DEFS_H #define SANITIZER_DEFS_H #include "sanitizer_platform.h" #ifndef SANITIZER_DEBUG # define SANITIZER_DEBUG 0 #endif // Only use SANITIZER_*ATTRIBUTE* before the function return type! #if SANITIZER_WINDOWS #if SANITIZER_IMPORT_INTERFACE # define SANITIZER_INTERFACE_ATTRIBUTE __declspec(dllimport) #else # define SANITIZER_INTERFACE_ATTRIBUTE __declspec(dllexport) #endif # define SANITIZER_WEAK_ATTRIBUTE #elif SANITIZER_GO # define SANITIZER_INTERFACE_ATTRIBUTE # define SANITIZER_WEAK_ATTRIBUTE #else # define SANITIZER_INTERFACE_ATTRIBUTE __attribute__((visibility("default"))) # define SANITIZER_WEAK_ATTRIBUTE __attribute__((weak)) #endif //--------------------------- WEAK FUNCTIONS ---------------------------------// // When working with weak functions, to simplify the code and make it more // portable, when possible define a default implementation using this macro: // // SANITIZER_INTERFACE_WEAK_DEF(, , ) // // For example: // SANITIZER_INTERFACE_WEAK_DEF(bool, compare, int a, int b) { return a > b; } // #if SANITIZER_WINDOWS #include "sanitizer_win_defs.h" # define SANITIZER_INTERFACE_WEAK_DEF(ReturnType, Name, ...) \ WIN_WEAK_EXPORT_DEF(ReturnType, Name, __VA_ARGS__) #else # define SANITIZER_INTERFACE_WEAK_DEF(ReturnType, Name, ...) \ extern "C" SANITIZER_INTERFACE_ATTRIBUTE SANITIZER_WEAK_ATTRIBUTE \ ReturnType Name(__VA_ARGS__) #endif // SANITIZER_SUPPORTS_WEAK_HOOKS means that we support real weak functions that // will evaluate to a null pointer when not defined. #if (SANITIZER_LINUX || SANITIZER_MAC) && !SANITIZER_GO # define SANITIZER_SUPPORTS_WEAK_HOOKS 1 #else # define SANITIZER_SUPPORTS_WEAK_HOOKS 0 #endif // For some weak hooks that will be called very often and we want to avoid the // overhead of executing the default implementation when it is not necessary, // we can use the flag SANITIZER_SUPPORTS_WEAK_HOOKS to only define the default // implementation for platforms that doesn't support weak symbols. For example: // // #if !SANITIZER_SUPPORT_WEAK_HOOKS // SANITIZER_INTERFACE_WEAK_DEF(bool, compare_hook, int a, int b) { // return a > b; // } // #endif // // And then use it as: if (compare_hook) compare_hook(a, b); //----------------------------------------------------------------------------// // We can use .preinit_array section on Linux to call sanitizer initialization // functions very early in the process startup (unless PIC macro is defined). // FIXME: do we have anything like this on Mac? #if SANITIZER_LINUX && !SANITIZER_ANDROID && !defined(PIC) # define SANITIZER_CAN_USE_PREINIT_ARRAY 1 #else # define SANITIZER_CAN_USE_PREINIT_ARRAY 0 #endif // GCC does not understand __has_feature #if !defined(__has_feature) # define __has_feature(x) 0 #endif // For portability reasons we do not include stddef.h, stdint.h or any other // system header, but we do need some basic types that are not defined // in a portable way by the language itself. namespace __sanitizer { #if defined(_WIN64) // 64-bit Windows uses LLP64 data model. typedef unsigned long long uptr; // NOLINT typedef signed long long sptr; // NOLINT #else typedef unsigned long uptr; // NOLINT typedef signed long sptr; // NOLINT #endif // defined(_WIN64) #if defined(__x86_64__) // Since x32 uses ILP32 data model in 64-bit hardware mode, we must use // 64-bit pointer to unwind stack frame. typedef unsigned long long uhwptr; // NOLINT #else typedef uptr uhwptr; // NOLINT #endif typedef unsigned char u8; typedef unsigned short u16; // NOLINT typedef unsigned int u32; typedef unsigned long long u64; // NOLINT typedef signed char s8; typedef signed short s16; // NOLINT typedef signed int s32; typedef signed long long s64; // NOLINT #if SANITIZER_WINDOWS // On Windows, files are HANDLE, which is a synonim of void*. // Use void* to avoid including everywhere. typedef void* fd_t; typedef unsigned error_t; #else typedef int fd_t; typedef int error_t; #endif typedef int pid_t; // WARNING: OFF_T may be different from OS type off_t, depending on the value of // _FILE_OFFSET_BITS. This definition of OFF_T matches the ABI of system calls // like pread and mmap, as opposed to pread64 and mmap64. // FreeBSD, Mac and Linux/x86-64 are special. #if SANITIZER_FREEBSD || SANITIZER_MAC || \ (SANITIZER_LINUX && defined(__x86_64__)) typedef u64 OFF_T; #else typedef uptr OFF_T; #endif typedef u64 OFF64_T; #if (SANITIZER_WORDSIZE == 64) || SANITIZER_MAC typedef uptr operator_new_size_type; #else # if defined(__s390__) && !defined(__s390x__) // Special case: 31-bit s390 has unsigned long as size_t. typedef unsigned long operator_new_size_type; # else typedef u32 operator_new_size_type; # endif #endif +#if SANITIZER_MAC +// On Darwin, thread IDs are 64-bit even on 32-bit systems. +typedef u64 tid_t; +#else +typedef uptr tid_t; +#endif // ----------- ATTENTION ------------- // This header should NOT include any other headers to avoid portability issues. // Common defs. #define INLINE inline #define INTERFACE_ATTRIBUTE SANITIZER_INTERFACE_ATTRIBUTE #define SANITIZER_WEAK_DEFAULT_IMPL \ extern "C" SANITIZER_INTERFACE_ATTRIBUTE SANITIZER_WEAK_ATTRIBUTE NOINLINE #define SANITIZER_WEAK_CXX_DEFAULT_IMPL \ extern "C++" SANITIZER_INTERFACE_ATTRIBUTE SANITIZER_WEAK_ATTRIBUTE NOINLINE // Platform-specific defs. #if defined(_MSC_VER) # define ALWAYS_INLINE __forceinline // FIXME(timurrrr): do we need this on Windows? # define ALIAS(x) # define ALIGNED(x) __declspec(align(x)) # define FORMAT(f, a) # define NOINLINE __declspec(noinline) # define NORETURN __declspec(noreturn) # define THREADLOCAL __declspec(thread) # define LIKELY(x) (x) # define UNLIKELY(x) (x) # define PREFETCH(x) /* _mm_prefetch(x, _MM_HINT_NTA) */ (void)0 #else // _MSC_VER # define ALWAYS_INLINE inline __attribute__((always_inline)) # define ALIAS(x) __attribute__((alias(x))) // Please only use the ALIGNED macro before the type. // Using ALIGNED after the variable declaration is not portable! # define ALIGNED(x) __attribute__((aligned(x))) # define FORMAT(f, a) __attribute__((format(printf, f, a))) # define NOINLINE __attribute__((noinline)) # define NORETURN __attribute__((noreturn)) # define THREADLOCAL __thread # define LIKELY(x) __builtin_expect(!!(x), 1) # define UNLIKELY(x) __builtin_expect(!!(x), 0) # if defined(__i386__) || defined(__x86_64__) // __builtin_prefetch(x) generates prefetchnt0 on x86 # define PREFETCH(x) __asm__("prefetchnta (%0)" : : "r" (x)) # else # define PREFETCH(x) __builtin_prefetch(x) # endif #endif // _MSC_VER #if !defined(_MSC_VER) || defined(__clang__) # define UNUSED __attribute__((unused)) # define USED __attribute__((used)) #else # define UNUSED # define USED #endif #if !defined(_MSC_VER) || defined(__clang__) || MSC_PREREQ(1900) # define NOEXCEPT noexcept #else # define NOEXCEPT throw() #endif // Unaligned versions of basic types. typedef ALIGNED(1) u16 uu16; typedef ALIGNED(1) u32 uu32; typedef ALIGNED(1) u64 uu64; typedef ALIGNED(1) s16 us16; typedef ALIGNED(1) s32 us32; typedef ALIGNED(1) s64 us64; #if SANITIZER_WINDOWS } // namespace __sanitizer typedef unsigned long DWORD; // NOLINT namespace __sanitizer { typedef DWORD thread_return_t; # define THREAD_CALLING_CONV __stdcall #else // _WIN32 typedef void* thread_return_t; # define THREAD_CALLING_CONV #endif // _WIN32 typedef thread_return_t (THREAD_CALLING_CONV *thread_callback_t)(void* arg); // NOTE: Functions below must be defined in each run-time. void NORETURN Die(); // FIXME: No, this shouldn't be in the sanitizer interface. SANITIZER_INTERFACE_ATTRIBUTE void NORETURN CheckFailed(const char *file, int line, const char *cond, u64 v1, u64 v2); // Check macro #define RAW_CHECK_MSG(expr, msg) do { \ if (UNLIKELY(!(expr))) { \ RawWrite(msg); \ Die(); \ } \ } while (0) #define RAW_CHECK(expr) RAW_CHECK_MSG(expr, #expr) #define CHECK_IMPL(c1, op, c2) \ do { \ __sanitizer::u64 v1 = (u64)(c1); \ __sanitizer::u64 v2 = (u64)(c2); \ if (UNLIKELY(!(v1 op v2))) \ __sanitizer::CheckFailed(__FILE__, __LINE__, \ "(" #c1 ") " #op " (" #c2 ")", v1, v2); \ } while (false) \ /**/ #define CHECK(a) CHECK_IMPL((a), !=, 0) #define CHECK_EQ(a, b) CHECK_IMPL((a), ==, (b)) #define CHECK_NE(a, b) CHECK_IMPL((a), !=, (b)) #define CHECK_LT(a, b) CHECK_IMPL((a), <, (b)) #define CHECK_LE(a, b) CHECK_IMPL((a), <=, (b)) #define CHECK_GT(a, b) CHECK_IMPL((a), >, (b)) #define CHECK_GE(a, b) CHECK_IMPL((a), >=, (b)) #if SANITIZER_DEBUG #define DCHECK(a) CHECK(a) #define DCHECK_EQ(a, b) CHECK_EQ(a, b) #define DCHECK_NE(a, b) CHECK_NE(a, b) #define DCHECK_LT(a, b) CHECK_LT(a, b) #define DCHECK_LE(a, b) CHECK_LE(a, b) #define DCHECK_GT(a, b) CHECK_GT(a, b) #define DCHECK_GE(a, b) CHECK_GE(a, b) #else #define DCHECK(a) #define DCHECK_EQ(a, b) #define DCHECK_NE(a, b) #define DCHECK_LT(a, b) #define DCHECK_LE(a, b) #define DCHECK_GT(a, b) #define DCHECK_GE(a, b) #endif #define UNREACHABLE(msg) do { \ CHECK(0 && msg); \ Die(); \ } while (0) #define UNIMPLEMENTED() UNREACHABLE("unimplemented") #define COMPILER_CHECK(pred) IMPL_COMPILER_ASSERT(pred, __LINE__) #define ARRAY_SIZE(a) (sizeof(a)/sizeof((a)[0])) #define IMPL_PASTE(a, b) a##b #define IMPL_COMPILER_ASSERT(pred, line) \ typedef char IMPL_PASTE(assertion_failed_##_, line)[2*(int)(pred)-1] // Limits for integral types. We have to redefine it in case we don't // have stdint.h (like in Visual Studio 9). #undef __INT64_C #undef __UINT64_C #if SANITIZER_WORDSIZE == 64 # define __INT64_C(c) c ## L # define __UINT64_C(c) c ## UL #else # define __INT64_C(c) c ## LL # define __UINT64_C(c) c ## ULL #endif // SANITIZER_WORDSIZE == 64 #undef INT32_MIN #define INT32_MIN (-2147483647-1) #undef INT32_MAX #define INT32_MAX (2147483647) #undef UINT32_MAX #define UINT32_MAX (4294967295U) #undef INT64_MIN #define INT64_MIN (-__INT64_C(9223372036854775807)-1) #undef INT64_MAX #define INT64_MAX (__INT64_C(9223372036854775807)) #undef UINT64_MAX #define UINT64_MAX (__UINT64_C(18446744073709551615)) enum LinkerInitialized { LINKER_INITIALIZED = 0 }; #if !defined(_MSC_VER) || defined(__clang__) #if SANITIZER_S390_31 #define GET_CALLER_PC() \ (__sanitizer::uptr) __builtin_extract_return_addr(__builtin_return_address(0)) #else #define GET_CALLER_PC() (__sanitizer::uptr) __builtin_return_address(0) #endif #define GET_CURRENT_FRAME() (__sanitizer::uptr) __builtin_frame_address(0) inline void Trap() { __builtin_trap(); } #else extern "C" void* _ReturnAddress(void); extern "C" void* _AddressOfReturnAddress(void); # pragma intrinsic(_ReturnAddress) # pragma intrinsic(_AddressOfReturnAddress) #define GET_CALLER_PC() (__sanitizer::uptr) _ReturnAddress() // CaptureStackBackTrace doesn't need to know BP on Windows. #define GET_CURRENT_FRAME() \ (((__sanitizer::uptr)_AddressOfReturnAddress()) + sizeof(__sanitizer::uptr)) extern "C" void __ud2(void); # pragma intrinsic(__ud2) inline void Trap() { __ud2(); } #endif #define HANDLE_EINTR(res, f) \ { \ int rverrno; \ do { \ res = (f); \ } while (internal_iserror(res, &rverrno) && rverrno == EINTR); \ } // Forces the compiler to generate a frame pointer in the function. #define ENABLE_FRAME_POINTER \ do { \ volatile __sanitizer::uptr enable_fp; \ enable_fp = GET_CURRENT_FRAME(); \ (void)enable_fp; \ } while (0) } // namespace __sanitizer namespace __asan { using namespace __sanitizer; } // NOLINT namespace __dsan { using namespace __sanitizer; } // NOLINT namespace __dfsan { using namespace __sanitizer; } // NOLINT namespace __esan { using namespace __sanitizer; } // NOLINT namespace __lsan { using namespace __sanitizer; } // NOLINT namespace __msan { using namespace __sanitizer; } // NOLINT namespace __tsan { using namespace __sanitizer; } // NOLINT namespace __scudo { using namespace __sanitizer; } // NOLINT namespace __ubsan { using namespace __sanitizer; } // NOLINT namespace __xray { using namespace __sanitizer; } // NOLINT namespace __interception { using namespace __sanitizer; } // NOLINT #endif // SANITIZER_DEFS_H Index: vendor/compiler-rt/dist/lib/sanitizer_common/sanitizer_linux.cc =================================================================== --- vendor/compiler-rt/dist/lib/sanitizer_common/sanitizer_linux.cc (revision 317221) +++ vendor/compiler-rt/dist/lib/sanitizer_common/sanitizer_linux.cc (revision 317222) @@ -1,1596 +1,1596 @@ //===-- sanitizer_linux.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 shared between AddressSanitizer and ThreadSanitizer // run-time libraries and implements linux-specific functions from // sanitizer_libc.h. //===----------------------------------------------------------------------===// #include "sanitizer_platform.h" #if SANITIZER_FREEBSD || SANITIZER_LINUX #include "sanitizer_common.h" #include "sanitizer_flags.h" #include "sanitizer_internal_defs.h" #include "sanitizer_libc.h" #include "sanitizer_linux.h" #include "sanitizer_mutex.h" #include "sanitizer_placement_new.h" #include "sanitizer_procmaps.h" #include "sanitizer_stacktrace.h" #include "sanitizer_symbolizer.h" #if !SANITIZER_FREEBSD #include #endif // For mips64, syscall(__NR_stat) fills the buffer in the 'struct kernel_stat' // format. Struct kernel_stat is defined as 'struct stat' in asm/stat.h. To // access stat from asm/stat.h, without conflicting with definition in // sys/stat.h, we use this trick. #if defined(__mips64) #include #include #define stat kernel_stat #include #undef stat #endif #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #if SANITIZER_FREEBSD #include #include #include #include #include extern "C" { // must be included after and on // FreeBSD 9.2 and 10.0. #include } extern char **environ; // provided by crt1 #endif // SANITIZER_FREEBSD #if !SANITIZER_ANDROID #include #endif #ifndef __GLIBC_PREREQ #define __GLIBC_PREREQ(x, y) 0 #endif #if SANITIZER_LINUX && __GLIBC_PREREQ(2, 16) # define SANITIZER_USE_GETAUXVAL 1 #else # define SANITIZER_USE_GETAUXVAL 0 #endif #if SANITIZER_USE_GETAUXVAL #include #endif #if SANITIZER_LINUX // struct kernel_timeval { long tv_sec; long tv_usec; }; // is broken on some linux distributions. const int FUTEX_WAIT = 0; const int FUTEX_WAKE = 1; #endif // SANITIZER_LINUX // Are we using 32-bit or 64-bit Linux syscalls? // x32 (which defines __x86_64__) has SANITIZER_WORDSIZE == 32 // but it still needs to use 64-bit syscalls. #if SANITIZER_LINUX && (defined(__x86_64__) || defined(__powerpc64__) || \ SANITIZER_WORDSIZE == 64) # define SANITIZER_LINUX_USES_64BIT_SYSCALLS 1 #else # define SANITIZER_LINUX_USES_64BIT_SYSCALLS 0 #endif #if defined(__x86_64__) || SANITIZER_MIPS64 extern "C" { extern void internal_sigreturn(); } #endif namespace __sanitizer { #if SANITIZER_LINUX && defined(__x86_64__) #include "sanitizer_syscall_linux_x86_64.inc" #elif SANITIZER_LINUX && defined(__aarch64__) #include "sanitizer_syscall_linux_aarch64.inc" #else #include "sanitizer_syscall_generic.inc" #endif // --------------- sanitizer_libc.h #if !SANITIZER_S390 uptr internal_mmap(void *addr, uptr length, int prot, int flags, int fd, OFF_T offset) { #if SANITIZER_FREEBSD || SANITIZER_LINUX_USES_64BIT_SYSCALLS return internal_syscall(SYSCALL(mmap), (uptr)addr, length, prot, flags, fd, offset); #else // mmap2 specifies file offset in 4096-byte units. CHECK(IsAligned(offset, 4096)); return internal_syscall(SYSCALL(mmap2), addr, length, prot, flags, fd, offset / 4096); #endif } #endif // !SANITIZER_S390 uptr internal_munmap(void *addr, uptr length) { return internal_syscall(SYSCALL(munmap), (uptr)addr, length); } int internal_mprotect(void *addr, uptr length, int prot) { return internal_syscall(SYSCALL(mprotect), (uptr)addr, length, prot); } uptr internal_close(fd_t fd) { return internal_syscall(SYSCALL(close), fd); } uptr internal_open(const char *filename, int flags) { #if SANITIZER_USES_CANONICAL_LINUX_SYSCALLS return internal_syscall(SYSCALL(openat), AT_FDCWD, (uptr)filename, flags); #else return internal_syscall(SYSCALL(open), (uptr)filename, flags); #endif } uptr internal_open(const char *filename, int flags, u32 mode) { #if SANITIZER_USES_CANONICAL_LINUX_SYSCALLS return internal_syscall(SYSCALL(openat), AT_FDCWD, (uptr)filename, flags, mode); #else return internal_syscall(SYSCALL(open), (uptr)filename, flags, mode); #endif } uptr internal_read(fd_t fd, void *buf, uptr count) { sptr res; HANDLE_EINTR(res, (sptr)internal_syscall(SYSCALL(read), fd, (uptr)buf, count)); return res; } uptr internal_write(fd_t fd, const void *buf, uptr count) { sptr res; HANDLE_EINTR(res, (sptr)internal_syscall(SYSCALL(write), fd, (uptr)buf, count)); return res; } uptr internal_ftruncate(fd_t fd, uptr size) { sptr res; HANDLE_EINTR(res, (sptr)internal_syscall(SYSCALL(ftruncate), fd, (OFF_T)size)); return res; } #if !SANITIZER_LINUX_USES_64BIT_SYSCALLS && !SANITIZER_FREEBSD static void stat64_to_stat(struct stat64 *in, struct stat *out) { internal_memset(out, 0, sizeof(*out)); out->st_dev = in->st_dev; out->st_ino = in->st_ino; out->st_mode = in->st_mode; out->st_nlink = in->st_nlink; out->st_uid = in->st_uid; out->st_gid = in->st_gid; out->st_rdev = in->st_rdev; out->st_size = in->st_size; out->st_blksize = in->st_blksize; out->st_blocks = in->st_blocks; out->st_atime = in->st_atime; out->st_mtime = in->st_mtime; out->st_ctime = in->st_ctime; out->st_ino = in->st_ino; } #endif #if defined(__mips64) static void kernel_stat_to_stat(struct kernel_stat *in, struct stat *out) { internal_memset(out, 0, sizeof(*out)); out->st_dev = in->st_dev; out->st_ino = in->st_ino; out->st_mode = in->st_mode; out->st_nlink = in->st_nlink; out->st_uid = in->st_uid; out->st_gid = in->st_gid; out->st_rdev = in->st_rdev; out->st_size = in->st_size; out->st_blksize = in->st_blksize; out->st_blocks = in->st_blocks; out->st_atime = in->st_atime_nsec; out->st_mtime = in->st_mtime_nsec; out->st_ctime = in->st_ctime_nsec; out->st_ino = in->st_ino; } #endif uptr internal_stat(const char *path, void *buf) { #if SANITIZER_FREEBSD return internal_syscall(SYSCALL(stat), path, buf); #elif SANITIZER_USES_CANONICAL_LINUX_SYSCALLS return internal_syscall(SYSCALL(newfstatat), AT_FDCWD, (uptr)path, (uptr)buf, 0); #elif SANITIZER_LINUX_USES_64BIT_SYSCALLS # if defined(__mips64) // For mips64, stat syscall fills buffer in the format of kernel_stat struct kernel_stat kbuf; int res = internal_syscall(SYSCALL(stat), path, &kbuf); kernel_stat_to_stat(&kbuf, (struct stat *)buf); return res; # else return internal_syscall(SYSCALL(stat), (uptr)path, (uptr)buf); # endif #else struct stat64 buf64; int res = internal_syscall(SYSCALL(stat64), path, &buf64); stat64_to_stat(&buf64, (struct stat *)buf); return res; #endif } uptr internal_lstat(const char *path, void *buf) { #if SANITIZER_FREEBSD return internal_syscall(SYSCALL(lstat), path, buf); #elif SANITIZER_USES_CANONICAL_LINUX_SYSCALLS return internal_syscall(SYSCALL(newfstatat), AT_FDCWD, (uptr)path, (uptr)buf, AT_SYMLINK_NOFOLLOW); #elif SANITIZER_LINUX_USES_64BIT_SYSCALLS # if SANITIZER_MIPS64 // For mips64, lstat syscall fills buffer in the format of kernel_stat struct kernel_stat kbuf; int res = internal_syscall(SYSCALL(lstat), path, &kbuf); kernel_stat_to_stat(&kbuf, (struct stat *)buf); return res; # else return internal_syscall(SYSCALL(lstat), (uptr)path, (uptr)buf); # endif #else struct stat64 buf64; int res = internal_syscall(SYSCALL(lstat64), path, &buf64); stat64_to_stat(&buf64, (struct stat *)buf); return res; #endif } uptr internal_fstat(fd_t fd, void *buf) { #if SANITIZER_FREEBSD || SANITIZER_LINUX_USES_64BIT_SYSCALLS # if SANITIZER_MIPS64 // For mips64, fstat syscall fills buffer in the format of kernel_stat struct kernel_stat kbuf; int res = internal_syscall(SYSCALL(fstat), fd, &kbuf); kernel_stat_to_stat(&kbuf, (struct stat *)buf); return res; # else return internal_syscall(SYSCALL(fstat), fd, (uptr)buf); # endif #else struct stat64 buf64; int res = internal_syscall(SYSCALL(fstat64), fd, &buf64); stat64_to_stat(&buf64, (struct stat *)buf); return res; #endif } uptr internal_filesize(fd_t fd) { struct stat st; if (internal_fstat(fd, &st)) return -1; return (uptr)st.st_size; } uptr internal_dup2(int oldfd, int newfd) { #if SANITIZER_USES_CANONICAL_LINUX_SYSCALLS return internal_syscall(SYSCALL(dup3), oldfd, newfd, 0); #else return internal_syscall(SYSCALL(dup2), oldfd, newfd); #endif } uptr internal_readlink(const char *path, char *buf, uptr bufsize) { #if SANITIZER_USES_CANONICAL_LINUX_SYSCALLS return internal_syscall(SYSCALL(readlinkat), AT_FDCWD, (uptr)path, (uptr)buf, bufsize); #else return internal_syscall(SYSCALL(readlink), (uptr)path, (uptr)buf, bufsize); #endif } uptr internal_unlink(const char *path) { #if SANITIZER_USES_CANONICAL_LINUX_SYSCALLS return internal_syscall(SYSCALL(unlinkat), AT_FDCWD, (uptr)path, 0); #else return internal_syscall(SYSCALL(unlink), (uptr)path); #endif } uptr internal_rename(const char *oldpath, const char *newpath) { #if SANITIZER_USES_CANONICAL_LINUX_SYSCALLS return internal_syscall(SYSCALL(renameat), AT_FDCWD, (uptr)oldpath, AT_FDCWD, (uptr)newpath); #else return internal_syscall(SYSCALL(rename), (uptr)oldpath, (uptr)newpath); #endif } uptr internal_sched_yield() { return internal_syscall(SYSCALL(sched_yield)); } void internal__exit(int exitcode) { #if SANITIZER_FREEBSD internal_syscall(SYSCALL(exit), exitcode); #else internal_syscall(SYSCALL(exit_group), exitcode); #endif Die(); // Unreachable. } unsigned int internal_sleep(unsigned int seconds) { struct timespec ts; ts.tv_sec = 1; ts.tv_nsec = 0; int res = internal_syscall(SYSCALL(nanosleep), &ts, &ts); if (res) return ts.tv_sec; return 0; } uptr internal_execve(const char *filename, char *const argv[], char *const envp[]) { return internal_syscall(SYSCALL(execve), (uptr)filename, (uptr)argv, (uptr)envp); } // ----------------- sanitizer_common.h bool FileExists(const char *filename) { struct stat st; #if SANITIZER_USES_CANONICAL_LINUX_SYSCALLS if (internal_syscall(SYSCALL(newfstatat), AT_FDCWD, filename, &st, 0)) #else if (internal_stat(filename, &st)) #endif return false; // Sanity check: filename is a regular file. return S_ISREG(st.st_mode); } -uptr GetTid() { +tid_t GetTid() { #if SANITIZER_FREEBSD return (uptr)pthread_self(); #else return internal_syscall(SYSCALL(gettid)); #endif } u64 NanoTime() { #if SANITIZER_FREEBSD timeval tv; #else kernel_timeval tv; #endif internal_memset(&tv, 0, sizeof(tv)); internal_syscall(SYSCALL(gettimeofday), (uptr)&tv, 0); return (u64)tv.tv_sec * 1000*1000*1000 + tv.tv_usec * 1000; } // Like getenv, but reads env directly from /proc (on Linux) or parses the // 'environ' array (on FreeBSD) and does not use libc. This function should be // called first inside __asan_init. const char *GetEnv(const char *name) { #if SANITIZER_FREEBSD if (::environ != 0) { uptr NameLen = internal_strlen(name); for (char **Env = ::environ; *Env != 0; Env++) { if (internal_strncmp(*Env, name, NameLen) == 0 && (*Env)[NameLen] == '=') return (*Env) + NameLen + 1; } } return 0; // Not found. #elif SANITIZER_LINUX static char *environ; static uptr len; static bool inited; if (!inited) { inited = true; uptr environ_size; if (!ReadFileToBuffer("/proc/self/environ", &environ, &environ_size, &len)) environ = nullptr; } if (!environ || len == 0) return nullptr; uptr namelen = internal_strlen(name); const char *p = environ; while (*p != '\0') { // will happen at the \0\0 that terminates the buffer // proc file has the format NAME=value\0NAME=value\0NAME=value\0... const char* endp = (char*)internal_memchr(p, '\0', len - (p - environ)); if (!endp) // this entry isn't NUL terminated return nullptr; else if (!internal_memcmp(p, name, namelen) && p[namelen] == '=') // Match. return p + namelen + 1; // point after = p = endp + 1; } return nullptr; // Not found. #else #error "Unsupported platform" #endif } #if !SANITIZER_FREEBSD extern "C" { SANITIZER_WEAK_ATTRIBUTE extern void *__libc_stack_end; } #endif #if !SANITIZER_GO && !SANITIZER_FREEBSD static void ReadNullSepFileToArray(const char *path, char ***arr, int arr_size) { char *buff; uptr buff_size; uptr buff_len; *arr = (char **)MmapOrDie(arr_size * sizeof(char *), "NullSepFileArray"); if (!ReadFileToBuffer(path, &buff, &buff_size, &buff_len, 1024 * 1024)) { (*arr)[0] = nullptr; return; } (*arr)[0] = buff; int count, i; for (count = 1, i = 1; ; i++) { if (buff[i] == 0) { if (buff[i+1] == 0) break; (*arr)[count] = &buff[i+1]; CHECK_LE(count, arr_size - 1); // FIXME: make this more flexible. count++; } } (*arr)[count] = nullptr; } #endif static void GetArgsAndEnv(char ***argv, char ***envp) { #if !SANITIZER_FREEBSD #if !SANITIZER_GO if (&__libc_stack_end) { #endif uptr* stack_end = (uptr*)__libc_stack_end; int argc = *stack_end; *argv = (char**)(stack_end + 1); *envp = (char**)(stack_end + argc + 2); #if !SANITIZER_GO } else { static const int kMaxArgv = 2000, kMaxEnvp = 2000; ReadNullSepFileToArray("/proc/self/cmdline", argv, kMaxArgv); ReadNullSepFileToArray("/proc/self/environ", envp, kMaxEnvp); } #endif #else // On FreeBSD, retrieving the argument and environment arrays is done via the // kern.ps_strings sysctl, which returns a pointer to a structure containing // this information. See also . ps_strings *pss; size_t sz = sizeof(pss); if (sysctlbyname("kern.ps_strings", &pss, &sz, NULL, 0) == -1) { Printf("sysctl kern.ps_strings failed\n"); Die(); } *argv = pss->ps_argvstr; *envp = pss->ps_envstr; #endif } char **GetArgv() { char **argv, **envp; GetArgsAndEnv(&argv, &envp); return argv; } void ReExec() { char **argv, **envp; GetArgsAndEnv(&argv, &envp); uptr rv = internal_execve("/proc/self/exe", argv, envp); int rverrno; CHECK_EQ(internal_iserror(rv, &rverrno), true); Printf("execve failed, errno %d\n", rverrno); Die(); } enum MutexState { MtxUnlocked = 0, MtxLocked = 1, MtxSleeping = 2 }; BlockingMutex::BlockingMutex() { internal_memset(this, 0, sizeof(*this)); } void BlockingMutex::Lock() { CHECK_EQ(owner_, 0); atomic_uint32_t *m = reinterpret_cast(&opaque_storage_); if (atomic_exchange(m, MtxLocked, memory_order_acquire) == MtxUnlocked) return; while (atomic_exchange(m, MtxSleeping, memory_order_acquire) != MtxUnlocked) { #if SANITIZER_FREEBSD _umtx_op(m, UMTX_OP_WAIT_UINT, MtxSleeping, 0, 0); #else internal_syscall(SYSCALL(futex), (uptr)m, FUTEX_WAIT, MtxSleeping, 0, 0, 0); #endif } } void BlockingMutex::Unlock() { atomic_uint32_t *m = reinterpret_cast(&opaque_storage_); u32 v = atomic_exchange(m, MtxUnlocked, memory_order_relaxed); CHECK_NE(v, MtxUnlocked); if (v == MtxSleeping) { #if SANITIZER_FREEBSD _umtx_op(m, UMTX_OP_WAKE, 1, 0, 0); #else internal_syscall(SYSCALL(futex), (uptr)m, FUTEX_WAKE, 1, 0, 0, 0); #endif } } void BlockingMutex::CheckLocked() { atomic_uint32_t *m = reinterpret_cast(&opaque_storage_); CHECK_NE(MtxUnlocked, atomic_load(m, memory_order_relaxed)); } // ----------------- sanitizer_linux.h // The actual size of this structure is specified by d_reclen. // Note that getdents64 uses a different structure format. We only provide the // 32-bit syscall here. struct linux_dirent { #if SANITIZER_X32 || defined(__aarch64__) u64 d_ino; u64 d_off; #else unsigned long d_ino; unsigned long d_off; #endif unsigned short d_reclen; #ifdef __aarch64__ unsigned char d_type; #endif char d_name[256]; }; // Syscall wrappers. uptr internal_ptrace(int request, int pid, void *addr, void *data) { return internal_syscall(SYSCALL(ptrace), request, pid, (uptr)addr, (uptr)data); } uptr internal_waitpid(int pid, int *status, int options) { return internal_syscall(SYSCALL(wait4), pid, (uptr)status, options, 0 /* rusage */); } uptr internal_getpid() { return internal_syscall(SYSCALL(getpid)); } uptr internal_getppid() { return internal_syscall(SYSCALL(getppid)); } uptr internal_getdents(fd_t fd, struct linux_dirent *dirp, unsigned int count) { #if SANITIZER_USES_CANONICAL_LINUX_SYSCALLS return internal_syscall(SYSCALL(getdents64), fd, (uptr)dirp, count); #else return internal_syscall(SYSCALL(getdents), fd, (uptr)dirp, count); #endif } uptr internal_lseek(fd_t fd, OFF_T offset, int whence) { return internal_syscall(SYSCALL(lseek), fd, offset, whence); } #if SANITIZER_LINUX uptr internal_prctl(int option, uptr arg2, uptr arg3, uptr arg4, uptr arg5) { return internal_syscall(SYSCALL(prctl), option, arg2, arg3, arg4, arg5); } #endif uptr internal_sigaltstack(const struct sigaltstack *ss, struct sigaltstack *oss) { return internal_syscall(SYSCALL(sigaltstack), (uptr)ss, (uptr)oss); } int internal_fork() { #if SANITIZER_USES_CANONICAL_LINUX_SYSCALLS return internal_syscall(SYSCALL(clone), SIGCHLD, 0); #else return internal_syscall(SYSCALL(fork)); #endif } #if SANITIZER_LINUX #define SA_RESTORER 0x04000000 // Doesn't set sa_restorer if the caller did not set it, so use with caution //(see below). int internal_sigaction_norestorer(int signum, const void *act, void *oldact) { __sanitizer_kernel_sigaction_t k_act, k_oldact; internal_memset(&k_act, 0, sizeof(__sanitizer_kernel_sigaction_t)); internal_memset(&k_oldact, 0, sizeof(__sanitizer_kernel_sigaction_t)); const __sanitizer_sigaction *u_act = (const __sanitizer_sigaction *)act; __sanitizer_sigaction *u_oldact = (__sanitizer_sigaction *)oldact; if (u_act) { k_act.handler = u_act->handler; k_act.sigaction = u_act->sigaction; internal_memcpy(&k_act.sa_mask, &u_act->sa_mask, sizeof(__sanitizer_kernel_sigset_t)); // Without SA_RESTORER kernel ignores the calls (probably returns EINVAL). k_act.sa_flags = u_act->sa_flags | SA_RESTORER; // FIXME: most often sa_restorer is unset, however the kernel requires it // to point to a valid signal restorer that calls the rt_sigreturn syscall. // If sa_restorer passed to the kernel is NULL, the program may crash upon // signal delivery or fail to unwind the stack in the signal handler. // libc implementation of sigaction() passes its own restorer to // rt_sigaction, so we need to do the same (we'll need to reimplement the // restorers; for x86_64 the restorer address can be obtained from // oldact->sa_restorer upon a call to sigaction(xxx, NULL, oldact). #if !SANITIZER_ANDROID || !SANITIZER_MIPS32 k_act.sa_restorer = u_act->sa_restorer; #endif } uptr result = internal_syscall(SYSCALL(rt_sigaction), (uptr)signum, (uptr)(u_act ? &k_act : nullptr), (uptr)(u_oldact ? &k_oldact : nullptr), (uptr)sizeof(__sanitizer_kernel_sigset_t)); if ((result == 0) && u_oldact) { u_oldact->handler = k_oldact.handler; u_oldact->sigaction = k_oldact.sigaction; internal_memcpy(&u_oldact->sa_mask, &k_oldact.sa_mask, sizeof(__sanitizer_kernel_sigset_t)); u_oldact->sa_flags = k_oldact.sa_flags; #if !SANITIZER_ANDROID || !SANITIZER_MIPS32 u_oldact->sa_restorer = k_oldact.sa_restorer; #endif } return result; } // Invokes sigaction via a raw syscall with a restorer, but does not support // all platforms yet. // We disable for Go simply because we have not yet added to buildgo.sh. #if (defined(__x86_64__) || SANITIZER_MIPS64) && !SANITIZER_GO int internal_sigaction_syscall(int signum, const void *act, void *oldact) { if (act == nullptr) return internal_sigaction_norestorer(signum, act, oldact); __sanitizer_sigaction u_adjust; internal_memcpy(&u_adjust, act, sizeof(u_adjust)); #if !SANITIZER_ANDROID || !SANITIZER_MIPS32 if (u_adjust.sa_restorer == nullptr) { u_adjust.sa_restorer = internal_sigreturn; } #endif return internal_sigaction_norestorer(signum, (const void *)&u_adjust, oldact); } #endif // defined(__x86_64__) && !SANITIZER_GO #endif // SANITIZER_LINUX uptr internal_sigprocmask(int how, __sanitizer_sigset_t *set, __sanitizer_sigset_t *oldset) { #if SANITIZER_FREEBSD return internal_syscall(SYSCALL(sigprocmask), how, set, oldset); #else __sanitizer_kernel_sigset_t *k_set = (__sanitizer_kernel_sigset_t *)set; __sanitizer_kernel_sigset_t *k_oldset = (__sanitizer_kernel_sigset_t *)oldset; return internal_syscall(SYSCALL(rt_sigprocmask), (uptr)how, (uptr)&k_set->sig[0], (uptr)&k_oldset->sig[0], sizeof(__sanitizer_kernel_sigset_t)); #endif } void internal_sigfillset(__sanitizer_sigset_t *set) { internal_memset(set, 0xff, sizeof(*set)); } void internal_sigemptyset(__sanitizer_sigset_t *set) { internal_memset(set, 0, sizeof(*set)); } #if SANITIZER_LINUX void internal_sigdelset(__sanitizer_sigset_t *set, int signum) { signum -= 1; CHECK_GE(signum, 0); CHECK_LT(signum, sizeof(*set) * 8); __sanitizer_kernel_sigset_t *k_set = (__sanitizer_kernel_sigset_t *)set; const uptr idx = signum / (sizeof(k_set->sig[0]) * 8); const uptr bit = signum % (sizeof(k_set->sig[0]) * 8); k_set->sig[idx] &= ~(1 << bit); } bool internal_sigismember(__sanitizer_sigset_t *set, int signum) { signum -= 1; CHECK_GE(signum, 0); CHECK_LT(signum, sizeof(*set) * 8); __sanitizer_kernel_sigset_t *k_set = (__sanitizer_kernel_sigset_t *)set; const uptr idx = signum / (sizeof(k_set->sig[0]) * 8); const uptr bit = signum % (sizeof(k_set->sig[0]) * 8); return k_set->sig[idx] & (1 << bit); } #endif // SANITIZER_LINUX // ThreadLister implementation. ThreadLister::ThreadLister(int pid) : pid_(pid), descriptor_(-1), buffer_(4096), error_(true), entry_((struct linux_dirent *)buffer_.data()), bytes_read_(0) { char task_directory_path[80]; internal_snprintf(task_directory_path, sizeof(task_directory_path), "/proc/%d/task/", pid); uptr openrv = internal_open(task_directory_path, O_RDONLY | O_DIRECTORY); if (internal_iserror(openrv)) { error_ = true; Report("Can't open /proc/%d/task for reading.\n", pid); } else { error_ = false; descriptor_ = openrv; } } int ThreadLister::GetNextTID() { int tid = -1; do { if (error_) return -1; if ((char *)entry_ >= &buffer_[bytes_read_] && !GetDirectoryEntries()) return -1; if (entry_->d_ino != 0 && entry_->d_name[0] >= '0' && entry_->d_name[0] <= '9') { // Found a valid tid. tid = (int)internal_atoll(entry_->d_name); } entry_ = (struct linux_dirent *)(((char *)entry_) + entry_->d_reclen); } while (tid < 0); return tid; } void ThreadLister::Reset() { if (error_ || descriptor_ < 0) return; internal_lseek(descriptor_, 0, SEEK_SET); } ThreadLister::~ThreadLister() { if (descriptor_ >= 0) internal_close(descriptor_); } bool ThreadLister::error() { return error_; } bool ThreadLister::GetDirectoryEntries() { CHECK_GE(descriptor_, 0); CHECK_NE(error_, true); bytes_read_ = internal_getdents(descriptor_, (struct linux_dirent *)buffer_.data(), buffer_.size()); if (internal_iserror(bytes_read_)) { Report("Can't read directory entries from /proc/%d/task.\n", pid_); error_ = true; return false; } else if (bytes_read_ == 0) { return false; } entry_ = (struct linux_dirent *)buffer_.data(); return true; } uptr GetPageSize() { // Android post-M sysconf(_SC_PAGESIZE) crashes if called from .preinit_array. #if SANITIZER_ANDROID return 4096; #elif SANITIZER_LINUX && (defined(__x86_64__) || defined(__i386__)) return EXEC_PAGESIZE; #elif SANITIZER_USE_GETAUXVAL return getauxval(AT_PAGESZ); #else return sysconf(_SC_PAGESIZE); // EXEC_PAGESIZE may not be trustworthy. #endif } uptr ReadBinaryName(/*out*/char *buf, uptr buf_len) { #if SANITIZER_FREEBSD const int Mib[] = { CTL_KERN, KERN_PROC, KERN_PROC_PATHNAME, -1 }; const char *default_module_name = "kern.proc.pathname"; size_t Size = buf_len; bool IsErr = (sysctl(Mib, ARRAY_SIZE(Mib), buf, &Size, NULL, 0) != 0); int readlink_error = IsErr ? errno : 0; uptr module_name_len = Size; #else const char *default_module_name = "/proc/self/exe"; uptr module_name_len = internal_readlink( default_module_name, buf, buf_len); int readlink_error; bool IsErr = internal_iserror(module_name_len, &readlink_error); #endif if (IsErr) { // We can't read binary name for some reason, assume it's unknown. Report("WARNING: reading executable name failed with errno %d, " "some stack frames may not be symbolized\n", readlink_error); module_name_len = internal_snprintf(buf, buf_len, "%s", default_module_name); CHECK_LT(module_name_len, buf_len); } return module_name_len; } uptr ReadLongProcessName(/*out*/ char *buf, uptr buf_len) { #if SANITIZER_LINUX char *tmpbuf; uptr tmpsize; uptr tmplen; if (ReadFileToBuffer("/proc/self/cmdline", &tmpbuf, &tmpsize, &tmplen, 1024 * 1024)) { internal_strncpy(buf, tmpbuf, buf_len); UnmapOrDie(tmpbuf, tmpsize); return internal_strlen(buf); } #endif return ReadBinaryName(buf, buf_len); } // Match full names of the form /path/to/base_name{-,.}* bool LibraryNameIs(const char *full_name, const char *base_name) { const char *name = full_name; // Strip path. while (*name != '\0') name++; while (name > full_name && *name != '/') name--; if (*name == '/') name++; uptr base_name_length = internal_strlen(base_name); if (internal_strncmp(name, base_name, base_name_length)) return false; return (name[base_name_length] == '-' || name[base_name_length] == '.'); } #if !SANITIZER_ANDROID // Call cb for each region mapped by map. void ForEachMappedRegion(link_map *map, void (*cb)(const void *, uptr)) { CHECK_NE(map, nullptr); #if !SANITIZER_FREEBSD typedef ElfW(Phdr) Elf_Phdr; typedef ElfW(Ehdr) Elf_Ehdr; #endif // !SANITIZER_FREEBSD char *base = (char *)map->l_addr; Elf_Ehdr *ehdr = (Elf_Ehdr *)base; char *phdrs = base + ehdr->e_phoff; char *phdrs_end = phdrs + ehdr->e_phnum * ehdr->e_phentsize; // Find the segment with the minimum base so we can "relocate" the p_vaddr // fields. Typically ET_DYN objects (DSOs) have base of zero and ET_EXEC // objects have a non-zero base. uptr preferred_base = (uptr)-1; for (char *iter = phdrs; iter != phdrs_end; iter += ehdr->e_phentsize) { Elf_Phdr *phdr = (Elf_Phdr *)iter; if (phdr->p_type == PT_LOAD && preferred_base > (uptr)phdr->p_vaddr) preferred_base = (uptr)phdr->p_vaddr; } // Compute the delta from the real base to get a relocation delta. sptr delta = (uptr)base - preferred_base; // Now we can figure out what the loader really mapped. for (char *iter = phdrs; iter != phdrs_end; iter += ehdr->e_phentsize) { Elf_Phdr *phdr = (Elf_Phdr *)iter; if (phdr->p_type == PT_LOAD) { uptr seg_start = phdr->p_vaddr + delta; uptr seg_end = seg_start + phdr->p_memsz; // None of these values are aligned. We consider the ragged edges of the // load command as defined, since they are mapped from the file. seg_start = RoundDownTo(seg_start, GetPageSizeCached()); seg_end = RoundUpTo(seg_end, GetPageSizeCached()); cb((void *)seg_start, seg_end - seg_start); } } } #endif #if defined(__x86_64__) && SANITIZER_LINUX // We cannot use glibc's clone wrapper, because it messes with the child // task's TLS. It writes the PID and TID of the child task to its thread // descriptor, but in our case the child task shares the thread descriptor with // the parent (because we don't know how to allocate a new thread // descriptor to keep glibc happy). So the stock version of clone(), when // used with CLONE_VM, would end up corrupting the parent's thread descriptor. uptr internal_clone(int (*fn)(void *), void *child_stack, int flags, void *arg, int *parent_tidptr, void *newtls, int *child_tidptr) { long long res; if (!fn || !child_stack) return -EINVAL; CHECK_EQ(0, (uptr)child_stack % 16); child_stack = (char *)child_stack - 2 * sizeof(unsigned long long); ((unsigned long long *)child_stack)[0] = (uptr)fn; ((unsigned long long *)child_stack)[1] = (uptr)arg; register void *r8 __asm__("r8") = newtls; register int *r10 __asm__("r10") = child_tidptr; __asm__ __volatile__( /* %rax = syscall(%rax = SYSCALL(clone), * %rdi = flags, * %rsi = child_stack, * %rdx = parent_tidptr, * %r8 = new_tls, * %r10 = child_tidptr) */ "syscall\n" /* if (%rax != 0) * return; */ "testq %%rax,%%rax\n" "jnz 1f\n" /* In the child. Terminate unwind chain. */ // XXX: We should also terminate the CFI unwind chain // here. Unfortunately clang 3.2 doesn't support the // necessary CFI directives, so we skip that part. "xorq %%rbp,%%rbp\n" /* Call "fn(arg)". */ "popq %%rax\n" "popq %%rdi\n" "call *%%rax\n" /* Call _exit(%rax). */ "movq %%rax,%%rdi\n" "movq %2,%%rax\n" "syscall\n" /* Return to parent. */ "1:\n" : "=a" (res) : "a"(SYSCALL(clone)), "i"(SYSCALL(exit)), "S"(child_stack), "D"(flags), "d"(parent_tidptr), "r"(r8), "r"(r10) : "rsp", "memory", "r11", "rcx"); return res; } #elif defined(__mips__) uptr internal_clone(int (*fn)(void *), void *child_stack, int flags, void *arg, int *parent_tidptr, void *newtls, int *child_tidptr) { long long res; if (!fn || !child_stack) return -EINVAL; CHECK_EQ(0, (uptr)child_stack % 16); child_stack = (char *)child_stack - 2 * sizeof(unsigned long long); ((unsigned long long *)child_stack)[0] = (uptr)fn; ((unsigned long long *)child_stack)[1] = (uptr)arg; register void *a3 __asm__("$7") = newtls; register int *a4 __asm__("$8") = child_tidptr; // We don't have proper CFI directives here because it requires alot of code // for very marginal benefits. __asm__ __volatile__( /* $v0 = syscall($v0 = __NR_clone, * $a0 = flags, * $a1 = child_stack, * $a2 = parent_tidptr, * $a3 = new_tls, * $a4 = child_tidptr) */ ".cprestore 16;\n" "move $4,%1;\n" "move $5,%2;\n" "move $6,%3;\n" "move $7,%4;\n" /* Store the fifth argument on stack * if we are using 32-bit abi. */ #if SANITIZER_WORDSIZE == 32 "lw %5,16($29);\n" #else "move $8,%5;\n" #endif "li $2,%6;\n" "syscall;\n" /* if ($v0 != 0) * return; */ "bnez $2,1f;\n" /* Call "fn(arg)". */ #if SANITIZER_WORDSIZE == 32 #ifdef __BIG_ENDIAN__ "lw $25,4($29);\n" "lw $4,12($29);\n" #else "lw $25,0($29);\n" "lw $4,8($29);\n" #endif #else "ld $25,0($29);\n" "ld $4,8($29);\n" #endif "jal $25;\n" /* Call _exit($v0). */ "move $4,$2;\n" "li $2,%7;\n" "syscall;\n" /* Return to parent. */ "1:\n" : "=r" (res) : "r"(flags), "r"(child_stack), "r"(parent_tidptr), "r"(a3), "r"(a4), "i"(__NR_clone), "i"(__NR_exit) : "memory", "$29" ); return res; } #elif defined(__aarch64__) uptr internal_clone(int (*fn)(void *), void *child_stack, int flags, void *arg, int *parent_tidptr, void *newtls, int *child_tidptr) { long long res; if (!fn || !child_stack) return -EINVAL; CHECK_EQ(0, (uptr)child_stack % 16); child_stack = (char *)child_stack - 2 * sizeof(unsigned long long); ((unsigned long long *)child_stack)[0] = (uptr)fn; ((unsigned long long *)child_stack)[1] = (uptr)arg; register int (*__fn)(void *) __asm__("x0") = fn; register void *__stack __asm__("x1") = child_stack; register int __flags __asm__("x2") = flags; register void *__arg __asm__("x3") = arg; register int *__ptid __asm__("x4") = parent_tidptr; register void *__tls __asm__("x5") = newtls; register int *__ctid __asm__("x6") = child_tidptr; __asm__ __volatile__( "mov x0,x2\n" /* flags */ "mov x2,x4\n" /* ptid */ "mov x3,x5\n" /* tls */ "mov x4,x6\n" /* ctid */ "mov x8,%9\n" /* clone */ "svc 0x0\n" /* if (%r0 != 0) * return %r0; */ "cmp x0, #0\n" "bne 1f\n" /* In the child, now. Call "fn(arg)". */ "ldp x1, x0, [sp], #16\n" "blr x1\n" /* Call _exit(%r0). */ "mov x8, %10\n" "svc 0x0\n" "1:\n" : "=r" (res) : "i"(-EINVAL), "r"(__fn), "r"(__stack), "r"(__flags), "r"(__arg), "r"(__ptid), "r"(__tls), "r"(__ctid), "i"(__NR_clone), "i"(__NR_exit) : "x30", "memory"); return res; } #elif defined(__powerpc64__) uptr internal_clone(int (*fn)(void *), void *child_stack, int flags, void *arg, int *parent_tidptr, void *newtls, int *child_tidptr) { long long res; // Stack frame structure. #if SANITIZER_PPC64V1 // Back chain == 0 (SP + 112) // Frame (112 bytes): // Parameter save area (SP + 48), 8 doublewords // TOC save area (SP + 40) // Link editor doubleword (SP + 32) // Compiler doubleword (SP + 24) // LR save area (SP + 16) // CR save area (SP + 8) // Back chain (SP + 0) # define FRAME_SIZE 112 # define FRAME_TOC_SAVE_OFFSET 40 #elif SANITIZER_PPC64V2 // Back chain == 0 (SP + 32) // Frame (32 bytes): // TOC save area (SP + 24) // LR save area (SP + 16) // CR save area (SP + 8) // Back chain (SP + 0) # define FRAME_SIZE 32 # define FRAME_TOC_SAVE_OFFSET 24 #else # error "Unsupported PPC64 ABI" #endif if (!fn || !child_stack) return -EINVAL; CHECK_EQ(0, (uptr)child_stack % 16); register int (*__fn)(void *) __asm__("r3") = fn; register void *__cstack __asm__("r4") = child_stack; register int __flags __asm__("r5") = flags; register void *__arg __asm__("r6") = arg; register int *__ptidptr __asm__("r7") = parent_tidptr; register void *__newtls __asm__("r8") = newtls; register int *__ctidptr __asm__("r9") = child_tidptr; __asm__ __volatile__( /* fn and arg are saved across the syscall */ "mr 28, %5\n\t" "mr 27, %8\n\t" /* syscall r0 == __NR_clone r3 == flags r4 == child_stack r5 == parent_tidptr r6 == newtls r7 == child_tidptr */ "mr 3, %7\n\t" "mr 5, %9\n\t" "mr 6, %10\n\t" "mr 7, %11\n\t" "li 0, %3\n\t" "sc\n\t" /* Test if syscall was successful */ "cmpdi cr1, 3, 0\n\t" "crandc cr1*4+eq, cr1*4+eq, cr0*4+so\n\t" "bne- cr1, 1f\n\t" /* Set up stack frame */ "li 29, 0\n\t" "stdu 29, -8(1)\n\t" "stdu 1, -%12(1)\n\t" /* Do the function call */ "std 2, %13(1)\n\t" #if SANITIZER_PPC64V1 "ld 0, 0(28)\n\t" "ld 2, 8(28)\n\t" "mtctr 0\n\t" #elif SANITIZER_PPC64V2 "mr 12, 28\n\t" "mtctr 12\n\t" #else # error "Unsupported PPC64 ABI" #endif "mr 3, 27\n\t" "bctrl\n\t" "ld 2, %13(1)\n\t" /* Call _exit(r3) */ "li 0, %4\n\t" "sc\n\t" /* Return to parent */ "1:\n\t" "mr %0, 3\n\t" : "=r" (res) : "0" (-1), "i" (EINVAL), "i" (__NR_clone), "i" (__NR_exit), "r" (__fn), "r" (__cstack), "r" (__flags), "r" (__arg), "r" (__ptidptr), "r" (__newtls), "r" (__ctidptr), "i" (FRAME_SIZE), "i" (FRAME_TOC_SAVE_OFFSET) : "cr0", "cr1", "memory", "ctr", "r0", "r27", "r28", "r29"); return res; } #elif defined(__i386__) && SANITIZER_LINUX uptr internal_clone(int (*fn)(void *), void *child_stack, int flags, void *arg, int *parent_tidptr, void *newtls, int *child_tidptr) { int res; if (!fn || !child_stack) return -EINVAL; CHECK_EQ(0, (uptr)child_stack % 16); child_stack = (char *)child_stack - 7 * sizeof(unsigned int); ((unsigned int *)child_stack)[0] = (uptr)flags; ((unsigned int *)child_stack)[1] = (uptr)0; ((unsigned int *)child_stack)[2] = (uptr)fn; ((unsigned int *)child_stack)[3] = (uptr)arg; __asm__ __volatile__( /* %eax = syscall(%eax = SYSCALL(clone), * %ebx = flags, * %ecx = child_stack, * %edx = parent_tidptr, * %esi = new_tls, * %edi = child_tidptr) */ /* Obtain flags */ "movl (%%ecx), %%ebx\n" /* Do the system call */ "pushl %%ebx\n" "pushl %%esi\n" "pushl %%edi\n" /* Remember the flag value. */ "movl %%ebx, (%%ecx)\n" "int $0x80\n" "popl %%edi\n" "popl %%esi\n" "popl %%ebx\n" /* if (%eax != 0) * return; */ "test %%eax,%%eax\n" "jnz 1f\n" /* terminate the stack frame */ "xorl %%ebp,%%ebp\n" /* Call FN. */ "call *%%ebx\n" #ifdef PIC "call here\n" "here:\n" "popl %%ebx\n" "addl $_GLOBAL_OFFSET_TABLE_+[.-here], %%ebx\n" #endif /* Call exit */ "movl %%eax, %%ebx\n" "movl %2, %%eax\n" "int $0x80\n" "1:\n" : "=a" (res) : "a"(SYSCALL(clone)), "i"(SYSCALL(exit)), "c"(child_stack), "d"(parent_tidptr), "S"(newtls), "D"(child_tidptr) : "memory"); return res; } #elif defined(__arm__) && SANITIZER_LINUX uptr internal_clone(int (*fn)(void *), void *child_stack, int flags, void *arg, int *parent_tidptr, void *newtls, int *child_tidptr) { unsigned int res; if (!fn || !child_stack) return -EINVAL; child_stack = (char *)child_stack - 2 * sizeof(unsigned int); ((unsigned int *)child_stack)[0] = (uptr)fn; ((unsigned int *)child_stack)[1] = (uptr)arg; register int r0 __asm__("r0") = flags; register void *r1 __asm__("r1") = child_stack; register int *r2 __asm__("r2") = parent_tidptr; register void *r3 __asm__("r3") = newtls; register int *r4 __asm__("r4") = child_tidptr; register int r7 __asm__("r7") = __NR_clone; #if __ARM_ARCH > 4 || defined (__ARM_ARCH_4T__) # define ARCH_HAS_BX #endif #if __ARM_ARCH > 4 # define ARCH_HAS_BLX #endif #ifdef ARCH_HAS_BX # ifdef ARCH_HAS_BLX # define BLX(R) "blx " #R "\n" # else # define BLX(R) "mov lr, pc; bx " #R "\n" # endif #else # define BLX(R) "mov lr, pc; mov pc," #R "\n" #endif __asm__ __volatile__( /* %r0 = syscall(%r7 = SYSCALL(clone), * %r0 = flags, * %r1 = child_stack, * %r2 = parent_tidptr, * %r3 = new_tls, * %r4 = child_tidptr) */ /* Do the system call */ "swi 0x0\n" /* if (%r0 != 0) * return %r0; */ "cmp r0, #0\n" "bne 1f\n" /* In the child, now. Call "fn(arg)". */ "ldr r0, [sp, #4]\n" "ldr ip, [sp], #8\n" BLX(ip) /* Call _exit(%r0). */ "mov r7, %7\n" "swi 0x0\n" "1:\n" "mov %0, r0\n" : "=r"(res) : "r"(r0), "r"(r1), "r"(r2), "r"(r3), "r"(r4), "r"(r7), "i"(__NR_exit) : "memory"); return res; } #endif // defined(__x86_64__) && SANITIZER_LINUX #if SANITIZER_ANDROID #if __ANDROID_API__ < 21 extern "C" __attribute__((weak)) int dl_iterate_phdr( int (*)(struct dl_phdr_info *, size_t, void *), void *); #endif static int dl_iterate_phdr_test_cb(struct dl_phdr_info *info, size_t size, void *data) { // Any name starting with "lib" indicates a bug in L where library base names // are returned instead of paths. if (info->dlpi_name && info->dlpi_name[0] == 'l' && info->dlpi_name[1] == 'i' && info->dlpi_name[2] == 'b') { *(bool *)data = true; return 1; } return 0; } static atomic_uint32_t android_api_level; static AndroidApiLevel AndroidDetectApiLevel() { if (!&dl_iterate_phdr) return ANDROID_KITKAT; // K or lower bool base_name_seen = false; dl_iterate_phdr(dl_iterate_phdr_test_cb, &base_name_seen); if (base_name_seen) return ANDROID_LOLLIPOP_MR1; // L MR1 return ANDROID_POST_LOLLIPOP; // post-L // Plain L (API level 21) is completely broken wrt ASan and not very // interesting to detect. } AndroidApiLevel AndroidGetApiLevel() { AndroidApiLevel level = (AndroidApiLevel)atomic_load(&android_api_level, memory_order_relaxed); if (level) return level; level = AndroidDetectApiLevel(); atomic_store(&android_api_level, level, memory_order_relaxed); return level; } #endif bool IsHandledDeadlySignal(int signum) { if (common_flags()->handle_abort && signum == SIGABRT) return true; if (common_flags()->handle_sigill && signum == SIGILL) return true; if (common_flags()->handle_sigfpe && signum == SIGFPE) return true; if (common_flags()->handle_segv && signum == SIGSEGV) return true; return common_flags()->handle_sigbus && signum == SIGBUS; } #if !SANITIZER_GO void *internal_start_thread(void(*func)(void *arg), void *arg) { // Start the thread with signals blocked, otherwise it can steal user signals. __sanitizer_sigset_t set, old; internal_sigfillset(&set); #if SANITIZER_LINUX && !SANITIZER_ANDROID // Glibc uses SIGSETXID signal during setuid call. If this signal is blocked // on any thread, setuid call hangs (see test/tsan/setuid.c). internal_sigdelset(&set, 33); #endif internal_sigprocmask(SIG_SETMASK, &set, &old); void *th; real_pthread_create(&th, nullptr, (void*(*)(void *arg))func, arg); internal_sigprocmask(SIG_SETMASK, &old, nullptr); return th; } void internal_join_thread(void *th) { real_pthread_join(th, nullptr); } #else void *internal_start_thread(void (*func)(void *), void *arg) { return 0; } void internal_join_thread(void *th) {} #endif #if defined(__aarch64__) // Android headers in the older NDK releases miss this definition. struct __sanitizer_esr_context { struct _aarch64_ctx head; uint64_t esr; }; static bool Aarch64GetESR(ucontext_t *ucontext, u64 *esr) { static const u32 kEsrMagic = 0x45535201; u8 *aux = ucontext->uc_mcontext.__reserved; while (true) { _aarch64_ctx *ctx = (_aarch64_ctx *)aux; if (ctx->size == 0) break; if (ctx->magic == kEsrMagic) { *esr = ((__sanitizer_esr_context *)ctx)->esr; return true; } aux += ctx->size; } return false; } #endif SignalContext::WriteFlag SignalContext::GetWriteFlag(void *context) { ucontext_t *ucontext = (ucontext_t *)context; #if defined(__x86_64__) || defined(__i386__) static const uptr PF_WRITE = 1U << 1; #if SANITIZER_FREEBSD uptr err = ucontext->uc_mcontext.mc_err; #else uptr err = ucontext->uc_mcontext.gregs[REG_ERR]; #endif return err & PF_WRITE ? WRITE : READ; #elif defined(__arm__) static const uptr FSR_WRITE = 1U << 11; uptr fsr = ucontext->uc_mcontext.error_code; return fsr & FSR_WRITE ? WRITE : READ; #elif defined(__aarch64__) static const u64 ESR_ELx_WNR = 1U << 6; u64 esr; if (!Aarch64GetESR(ucontext, &esr)) return UNKNOWN; return esr & ESR_ELx_WNR ? WRITE : READ; #else (void)ucontext; return UNKNOWN; // FIXME: Implement. #endif } void SignalContext::DumpAllRegisters(void *context) { // FIXME: Implement this. } void GetPcSpBp(void *context, uptr *pc, uptr *sp, uptr *bp) { #if defined(__arm__) ucontext_t *ucontext = (ucontext_t*)context; *pc = ucontext->uc_mcontext.arm_pc; *bp = ucontext->uc_mcontext.arm_fp; *sp = ucontext->uc_mcontext.arm_sp; #elif defined(__aarch64__) ucontext_t *ucontext = (ucontext_t*)context; *pc = ucontext->uc_mcontext.pc; *bp = ucontext->uc_mcontext.regs[29]; *sp = ucontext->uc_mcontext.sp; #elif defined(__hppa__) ucontext_t *ucontext = (ucontext_t*)context; *pc = ucontext->uc_mcontext.sc_iaoq[0]; /* GCC uses %r3 whenever a frame pointer is needed. */ *bp = ucontext->uc_mcontext.sc_gr[3]; *sp = ucontext->uc_mcontext.sc_gr[30]; #elif defined(__x86_64__) # if SANITIZER_FREEBSD ucontext_t *ucontext = (ucontext_t*)context; *pc = ucontext->uc_mcontext.mc_rip; *bp = ucontext->uc_mcontext.mc_rbp; *sp = ucontext->uc_mcontext.mc_rsp; # else ucontext_t *ucontext = (ucontext_t*)context; *pc = ucontext->uc_mcontext.gregs[REG_RIP]; *bp = ucontext->uc_mcontext.gregs[REG_RBP]; *sp = ucontext->uc_mcontext.gregs[REG_RSP]; # endif #elif defined(__i386__) # if SANITIZER_FREEBSD ucontext_t *ucontext = (ucontext_t*)context; *pc = ucontext->uc_mcontext.mc_eip; *bp = ucontext->uc_mcontext.mc_ebp; *sp = ucontext->uc_mcontext.mc_esp; # else ucontext_t *ucontext = (ucontext_t*)context; *pc = ucontext->uc_mcontext.gregs[REG_EIP]; *bp = ucontext->uc_mcontext.gregs[REG_EBP]; *sp = ucontext->uc_mcontext.gregs[REG_ESP]; # endif #elif defined(__powerpc__) || defined(__powerpc64__) ucontext_t *ucontext = (ucontext_t*)context; *pc = ucontext->uc_mcontext.regs->nip; *sp = ucontext->uc_mcontext.regs->gpr[PT_R1]; // The powerpc{,64}-linux ABIs do not specify r31 as the frame // pointer, but GCC always uses r31 when we need a frame pointer. *bp = ucontext->uc_mcontext.regs->gpr[PT_R31]; #elif defined(__sparc__) ucontext_t *ucontext = (ucontext_t*)context; uptr *stk_ptr; # if defined (__arch64__) *pc = ucontext->uc_mcontext.mc_gregs[MC_PC]; *sp = ucontext->uc_mcontext.mc_gregs[MC_O6]; stk_ptr = (uptr *) (*sp + 2047); *bp = stk_ptr[15]; # else *pc = ucontext->uc_mcontext.gregs[REG_PC]; *sp = ucontext->uc_mcontext.gregs[REG_O6]; stk_ptr = (uptr *) *sp; *bp = stk_ptr[15]; # endif #elif defined(__mips__) ucontext_t *ucontext = (ucontext_t*)context; *pc = ucontext->uc_mcontext.pc; *bp = ucontext->uc_mcontext.gregs[30]; *sp = ucontext->uc_mcontext.gregs[29]; #elif defined(__s390__) ucontext_t *ucontext = (ucontext_t*)context; # if defined(__s390x__) *pc = ucontext->uc_mcontext.psw.addr; # else *pc = ucontext->uc_mcontext.psw.addr & 0x7fffffff; # endif *bp = ucontext->uc_mcontext.gregs[11]; *sp = ucontext->uc_mcontext.gregs[15]; #else # error "Unsupported arch" #endif } void MaybeReexec() { // No need to re-exec on Linux. } void PrintModuleMap() { } void CheckNoDeepBind(const char *filename, int flag) { #ifdef RTLD_DEEPBIND if (flag & RTLD_DEEPBIND) { Report( "You are trying to dlopen a %s shared library with RTLD_DEEPBIND flag" " which is incompatibe with sanitizer runtime " "(see https://github.com/google/sanitizers/issues/611 for details" "). If you want to run %s library under sanitizers please remove " "RTLD_DEEPBIND from dlopen flags.\n", filename, filename); Die(); } #endif } uptr FindAvailableMemoryRange(uptr size, uptr alignment, uptr left_padding) { UNREACHABLE("FindAvailableMemoryRange is not available"); return 0; } } // namespace __sanitizer #endif // SANITIZER_FREEBSD || SANITIZER_LINUX Index: vendor/compiler-rt/dist/lib/sanitizer_common/sanitizer_linux_libcdep.cc =================================================================== --- vendor/compiler-rt/dist/lib/sanitizer_common/sanitizer_linux_libcdep.cc (revision 317221) +++ vendor/compiler-rt/dist/lib/sanitizer_common/sanitizer_linux_libcdep.cc (revision 317222) @@ -1,557 +1,559 @@ //===-- sanitizer_linux_libcdep.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 shared between AddressSanitizer and ThreadSanitizer // run-time libraries and implements linux-specific functions from // sanitizer_libc.h. //===----------------------------------------------------------------------===// #include "sanitizer_platform.h" #if SANITIZER_FREEBSD || SANITIZER_LINUX #include "sanitizer_allocator_internal.h" #include "sanitizer_atomic.h" #include "sanitizer_common.h" #include "sanitizer_flags.h" #include "sanitizer_freebsd.h" #include "sanitizer_linux.h" #include "sanitizer_placement_new.h" #include "sanitizer_procmaps.h" #include "sanitizer_stacktrace.h" #include // for dlsym() #include #include #include #include #include #if SANITIZER_FREEBSD #include #include #define pthread_getattr_np pthread_attr_get_np #endif #if SANITIZER_LINUX #include #endif #if SANITIZER_ANDROID #include #endif #if SANITIZER_ANDROID && __ANDROID_API__ < 21 #include #endif #if !SANITIZER_ANDROID #include #include #endif namespace __sanitizer { SANITIZER_WEAK_ATTRIBUTE int real_sigaction(int signum, const void *act, void *oldact); int internal_sigaction(int signum, const void *act, void *oldact) { #if !SANITIZER_GO if (&real_sigaction) return real_sigaction(signum, act, oldact); #endif return sigaction(signum, (const struct sigaction *)act, (struct sigaction *)oldact); } void GetThreadStackTopAndBottom(bool at_initialization, uptr *stack_top, uptr *stack_bottom) { CHECK(stack_top); CHECK(stack_bottom); if (at_initialization) { // This is the main thread. Libpthread may not be initialized yet. struct rlimit rl; CHECK_EQ(getrlimit(RLIMIT_STACK, &rl), 0); // Find the mapping that contains a stack variable. MemoryMappingLayout proc_maps(/*cache_enabled*/true); uptr start, end, offset; uptr prev_end = 0; while (proc_maps.Next(&start, &end, &offset, nullptr, 0, /* protection */nullptr)) { if ((uptr)&rl < end) break; prev_end = end; } CHECK((uptr)&rl >= start && (uptr)&rl < end); // Get stacksize from rlimit, but clip it so that it does not overlap // with other mappings. uptr stacksize = rl.rlim_cur; if (stacksize > end - prev_end) stacksize = end - prev_end; // When running with unlimited stack size, we still want to set some limit. // The unlimited stack size is caused by 'ulimit -s unlimited'. // Also, for some reason, GNU make spawns subprocesses with unlimited stack. if (stacksize > kMaxThreadStackSize) stacksize = kMaxThreadStackSize; *stack_top = end; *stack_bottom = end - stacksize; return; } pthread_attr_t attr; pthread_attr_init(&attr); CHECK_EQ(pthread_getattr_np(pthread_self(), &attr), 0); uptr stacksize = 0; void *stackaddr = nullptr; my_pthread_attr_getstack(&attr, &stackaddr, &stacksize); pthread_attr_destroy(&attr); CHECK_LE(stacksize, kMaxThreadStackSize); // Sanity check. *stack_top = (uptr)stackaddr + stacksize; *stack_bottom = (uptr)stackaddr; } #if !SANITIZER_GO bool SetEnv(const char *name, const char *value) { void *f = dlsym(RTLD_NEXT, "setenv"); if (!f) return false; typedef int(*setenv_ft)(const char *name, const char *value, int overwrite); setenv_ft setenv_f; CHECK_EQ(sizeof(setenv_f), sizeof(f)); internal_memcpy(&setenv_f, &f, sizeof(f)); return setenv_f(name, value, 1) == 0; } #endif bool SanitizerSetThreadName(const char *name) { #ifdef PR_SET_NAME return 0 == prctl(PR_SET_NAME, (unsigned long)name, 0, 0, 0); // NOLINT #else return false; #endif } bool SanitizerGetThreadName(char *name, int max_len) { #ifdef PR_GET_NAME char buff[17]; if (prctl(PR_GET_NAME, (unsigned long)buff, 0, 0, 0)) // NOLINT return false; internal_strncpy(name, buff, max_len); name[max_len] = 0; return true; #else return false; #endif } #if !SANITIZER_FREEBSD && !SANITIZER_ANDROID && !SANITIZER_GO static uptr g_tls_size; #ifdef __i386__ # define DL_INTERNAL_FUNCTION __attribute__((regparm(3), stdcall)) #else # define DL_INTERNAL_FUNCTION #endif void InitTlsSize() { // all current supported platforms have 16 bytes stack alignment const size_t kStackAlign = 16; typedef void (*get_tls_func)(size_t*, size_t*) DL_INTERNAL_FUNCTION; get_tls_func get_tls; void *get_tls_static_info_ptr = dlsym(RTLD_NEXT, "_dl_get_tls_static_info"); CHECK_EQ(sizeof(get_tls), sizeof(get_tls_static_info_ptr)); internal_memcpy(&get_tls, &get_tls_static_info_ptr, sizeof(get_tls_static_info_ptr)); CHECK_NE(get_tls, 0); size_t tls_size = 0; size_t tls_align = 0; get_tls(&tls_size, &tls_align); if (tls_align < kStackAlign) tls_align = kStackAlign; g_tls_size = RoundUpTo(tls_size, tls_align); } #else void InitTlsSize() { } #endif // !SANITIZER_FREEBSD && !SANITIZER_ANDROID && !SANITIZER_GO #if (defined(__x86_64__) || defined(__i386__) || defined(__mips__) \ || defined(__aarch64__) || defined(__powerpc64__) || defined(__s390__) \ || defined(__arm__)) && SANITIZER_LINUX && !SANITIZER_ANDROID // sizeof(struct pthread) from glibc. static atomic_uintptr_t kThreadDescriptorSize; uptr ThreadDescriptorSize() { uptr val = atomic_load(&kThreadDescriptorSize, memory_order_relaxed); if (val) return val; #if defined(__x86_64__) || defined(__i386__) || defined(__arm__) #ifdef _CS_GNU_LIBC_VERSION char buf[64]; uptr len = confstr(_CS_GNU_LIBC_VERSION, buf, sizeof(buf)); if (len < sizeof(buf) && internal_strncmp(buf, "glibc 2.", 8) == 0) { char *end; int minor = internal_simple_strtoll(buf + 8, &end, 10); if (end != buf + 8 && (*end == '\0' || *end == '.' || *end == '-')) { int patch = 0; if (*end == '.') // strtoll will return 0 if no valid conversion could be performed patch = internal_simple_strtoll(end + 1, nullptr, 10); /* sizeof(struct pthread) values from various glibc versions. */ if (SANITIZER_X32) val = 1728; // Assume only one particular version for x32. // For ARM sizeof(struct pthread) changed in Glibc 2.23. else if (SANITIZER_ARM) val = minor <= 22 ? 1120 : 1216; else if (minor <= 3) val = FIRST_32_SECOND_64(1104, 1696); else if (minor == 4) val = FIRST_32_SECOND_64(1120, 1728); else if (minor == 5) val = FIRST_32_SECOND_64(1136, 1728); else if (minor <= 9) val = FIRST_32_SECOND_64(1136, 1712); else if (minor == 10) val = FIRST_32_SECOND_64(1168, 1776); else if (minor == 11 || (minor == 12 && patch == 1)) val = FIRST_32_SECOND_64(1168, 2288); else if (minor <= 13) val = FIRST_32_SECOND_64(1168, 2304); else val = FIRST_32_SECOND_64(1216, 2304); } if (val) atomic_store(&kThreadDescriptorSize, val, memory_order_relaxed); return val; } #endif #elif defined(__mips__) // TODO(sagarthakur): add more values as per different glibc versions. val = FIRST_32_SECOND_64(1152, 1776); if (val) atomic_store(&kThreadDescriptorSize, val, memory_order_relaxed); return val; #elif defined(__aarch64__) // The sizeof (struct pthread) is the same from GLIBC 2.17 to 2.22. val = 1776; atomic_store(&kThreadDescriptorSize, val, memory_order_relaxed); return val; #elif defined(__powerpc64__) val = 1776; // from glibc.ppc64le 2.20-8.fc21 atomic_store(&kThreadDescriptorSize, val, memory_order_relaxed); return val; #elif defined(__s390__) val = FIRST_32_SECOND_64(1152, 1776); // valid for glibc 2.22 atomic_store(&kThreadDescriptorSize, val, memory_order_relaxed); #endif return 0; } // The offset at which pointer to self is located in the thread descriptor. const uptr kThreadSelfOffset = FIRST_32_SECOND_64(8, 16); uptr ThreadSelfOffset() { return kThreadSelfOffset; } #if defined(__mips__) || defined(__powerpc64__) // TlsPreTcbSize includes size of struct pthread_descr and size of tcb // head structure. It lies before the static tls blocks. static uptr TlsPreTcbSize() { # if defined(__mips__) const uptr kTcbHead = 16; // sizeof (tcbhead_t) # elif defined(__powerpc64__) const uptr kTcbHead = 88; // sizeof (tcbhead_t) # endif const uptr kTlsAlign = 16; const uptr kTlsPreTcbSize = RoundUpTo(ThreadDescriptorSize() + kTcbHead, kTlsAlign); return kTlsPreTcbSize; } #endif uptr ThreadSelf() { uptr descr_addr; # if defined(__i386__) asm("mov %%gs:%c1,%0" : "=r"(descr_addr) : "i"(kThreadSelfOffset)); # elif defined(__x86_64__) asm("mov %%fs:%c1,%0" : "=r"(descr_addr) : "i"(kThreadSelfOffset)); # elif defined(__mips__) // MIPS uses TLS variant I. The thread pointer (in hardware register $29) // points to the end of the TCB + 0x7000. The pthread_descr structure is // immediately in front of the TCB. TlsPreTcbSize() includes the size of the // TCB and the size of pthread_descr. const uptr kTlsTcbOffset = 0x7000; uptr thread_pointer; asm volatile(".set push;\ .set mips64r2;\ rdhwr %0,$29;\ .set pop" : "=r" (thread_pointer)); descr_addr = thread_pointer - kTlsTcbOffset - TlsPreTcbSize(); # elif defined(__aarch64__) || defined(__arm__) descr_addr = reinterpret_cast(__builtin_thread_pointer()) - ThreadDescriptorSize(); # elif defined(__s390__) descr_addr = reinterpret_cast(__builtin_thread_pointer()); # elif defined(__powerpc64__) // PPC64LE uses TLS variant I. The thread pointer (in GPR 13) // points to the end of the TCB + 0x7000. The pthread_descr structure is // immediately in front of the TCB. TlsPreTcbSize() includes the size of the // TCB and the size of pthread_descr. const uptr kTlsTcbOffset = 0x7000; uptr thread_pointer; asm("addi %0,13,%1" : "=r"(thread_pointer) : "I"(-kTlsTcbOffset)); descr_addr = thread_pointer - TlsPreTcbSize(); # else # error "unsupported CPU arch" # endif return descr_addr; } #endif // (x86_64 || i386 || MIPS) && SANITIZER_LINUX #if SANITIZER_FREEBSD static void **ThreadSelfSegbase() { void **segbase = 0; # if defined(__i386__) // sysarch(I386_GET_GSBASE, segbase); __asm __volatile("mov %%gs:0, %0" : "=r" (segbase)); # elif defined(__x86_64__) // sysarch(AMD64_GET_FSBASE, segbase); __asm __volatile("movq %%fs:0, %0" : "=r" (segbase)); # else # error "unsupported CPU arch for FreeBSD platform" # endif return segbase; } uptr ThreadSelf() { return (uptr)ThreadSelfSegbase()[2]; } #endif // SANITIZER_FREEBSD #if !SANITIZER_GO static void GetTls(uptr *addr, uptr *size) { #if SANITIZER_LINUX && !SANITIZER_ANDROID # if defined(__x86_64__) || defined(__i386__) || defined(__s390__) *addr = ThreadSelf(); *size = GetTlsSize(); *addr -= *size; *addr += ThreadDescriptorSize(); # elif defined(__mips__) || defined(__aarch64__) || defined(__powerpc64__) \ || defined(__arm__) *addr = ThreadSelf(); *size = GetTlsSize(); # else *addr = 0; *size = 0; # endif #elif SANITIZER_FREEBSD void** segbase = ThreadSelfSegbase(); *addr = 0; *size = 0; if (segbase != 0) { // tcbalign = 16 // tls_size = round(tls_static_space, tcbalign); // dtv = segbase[1]; // dtv[2] = segbase - tls_static_space; void **dtv = (void**) segbase[1]; *addr = (uptr) dtv[2]; *size = (*addr == 0) ? 0 : ((uptr) segbase[0] - (uptr) dtv[2]); } #elif SANITIZER_ANDROID *addr = 0; *size = 0; #else # error "Unknown OS" #endif } #endif #if !SANITIZER_GO uptr GetTlsSize() { #if SANITIZER_FREEBSD || SANITIZER_ANDROID uptr addr, size; GetTls(&addr, &size); return size; #elif defined(__mips__) || defined(__powerpc64__) return RoundUpTo(g_tls_size + TlsPreTcbSize(), 16); #else return g_tls_size; #endif } #endif void GetThreadStackAndTls(bool main, uptr *stk_addr, uptr *stk_size, uptr *tls_addr, uptr *tls_size) { #if SANITIZER_GO // Stub implementation for Go. *stk_addr = *stk_size = *tls_addr = *tls_size = 0; #else GetTls(tls_addr, tls_size); uptr stack_top, stack_bottom; GetThreadStackTopAndBottom(main, &stack_top, &stack_bottom); *stk_addr = stack_bottom; *stk_size = stack_top - stack_bottom; if (!main) { // If stack and tls intersect, make them non-intersecting. if (*tls_addr > *stk_addr && *tls_addr < *stk_addr + *stk_size) { CHECK_GT(*tls_addr + *tls_size, *stk_addr); CHECK_LE(*tls_addr + *tls_size, *stk_addr + *stk_size); *stk_size -= *tls_size; *tls_addr = *stk_addr + *stk_size; } } #endif } # if !SANITIZER_FREEBSD typedef ElfW(Phdr) Elf_Phdr; # elif SANITIZER_WORDSIZE == 32 && __FreeBSD_version <= 902001 // v9.2 # define Elf_Phdr XElf32_Phdr # define dl_phdr_info xdl_phdr_info # define dl_iterate_phdr(c, b) xdl_iterate_phdr((c), (b)) # endif struct DlIteratePhdrData { InternalMmapVector *modules; bool first; }; static int dl_iterate_phdr_cb(dl_phdr_info *info, size_t size, void *arg) { DlIteratePhdrData *data = (DlIteratePhdrData*)arg; InternalScopedString module_name(kMaxPathLength); if (data->first) { data->first = false; // First module is the binary itself. ReadBinaryNameCached(module_name.data(), module_name.size()); } else if (info->dlpi_name) { module_name.append("%s", info->dlpi_name); } if (module_name[0] == '\0') return 0; LoadedModule cur_module; cur_module.set(module_name.data(), info->dlpi_addr); for (int i = 0; i < info->dlpi_phnum; i++) { const Elf_Phdr *phdr = &info->dlpi_phdr[i]; if (phdr->p_type == PT_LOAD) { uptr cur_beg = info->dlpi_addr + phdr->p_vaddr; uptr cur_end = cur_beg + phdr->p_memsz; bool executable = phdr->p_flags & PF_X; - cur_module.addAddressRange(cur_beg, cur_end, executable); + bool readable = phdr->p_flags & PF_R; + cur_module.addAddressRange(cur_beg, cur_end, executable, + readable); } } data->modules->push_back(cur_module); return 0; } #if SANITIZER_ANDROID && __ANDROID_API__ < 21 extern "C" __attribute__((weak)) int dl_iterate_phdr( int (*)(struct dl_phdr_info *, size_t, void *), void *); #endif void ListOfModules::init() { clear(); #if SANITIZER_ANDROID && __ANDROID_API__ <= 22 u32 api_level = AndroidGetApiLevel(); // Fall back to /proc/maps if dl_iterate_phdr is unavailable or broken. // The runtime check allows the same library to work with // both K and L (and future) Android releases. if (api_level <= ANDROID_LOLLIPOP_MR1) { // L or earlier MemoryMappingLayout memory_mapping(false); memory_mapping.DumpListOfModules(&modules_); return; } #endif DlIteratePhdrData data = {&modules_, true}; dl_iterate_phdr(dl_iterate_phdr_cb, &data); } // getrusage does not give us the current RSS, only the max RSS. // Still, this is better than nothing if /proc/self/statm is not available // for some reason, e.g. due to a sandbox. static uptr GetRSSFromGetrusage() { struct rusage usage; if (getrusage(RUSAGE_SELF, &usage)) // Failed, probably due to a sandbox. return 0; return usage.ru_maxrss << 10; // ru_maxrss is in Kb. } uptr GetRSS() { if (!common_flags()->can_use_proc_maps_statm) return GetRSSFromGetrusage(); fd_t fd = OpenFile("/proc/self/statm", RdOnly); if (fd == kInvalidFd) return GetRSSFromGetrusage(); char buf[64]; uptr len = internal_read(fd, buf, sizeof(buf) - 1); internal_close(fd); if ((sptr)len <= 0) return 0; buf[len] = 0; // The format of the file is: // 1084 89 69 11 0 79 0 // We need the second number which is RSS in pages. char *pos = buf; // Skip the first number. while (*pos >= '0' && *pos <= '9') pos++; // Skip whitespaces. while (!(*pos >= '0' && *pos <= '9') && *pos != 0) pos++; // Read the number. uptr rss = 0; while (*pos >= '0' && *pos <= '9') rss = rss * 10 + *pos++ - '0'; return rss * GetPageSizeCached(); } // 64-bit Android targets don't provide the deprecated __android_log_write. // Starting with the L release, syslog() works and is preferable to // __android_log_write. #if SANITIZER_LINUX #if SANITIZER_ANDROID static atomic_uint8_t android_log_initialized; void AndroidLogInit() { openlog(GetProcessName(), 0, LOG_USER); atomic_store(&android_log_initialized, 1, memory_order_release); } static bool ShouldLogAfterPrintf() { return atomic_load(&android_log_initialized, memory_order_acquire); } #else void AndroidLogInit() {} static bool ShouldLogAfterPrintf() { return true; } #endif // SANITIZER_ANDROID void WriteOneLineToSyslog(const char *s) { #if SANITIZER_ANDROID &&__ANDROID_API__ < 21 __android_log_write(ANDROID_LOG_INFO, NULL, s); #else syslog(LOG_INFO, "%s", s); #endif } void LogMessageOnPrintf(const char *str) { if (common_flags()->log_to_syslog && ShouldLogAfterPrintf()) WriteToSyslog(str); } #endif // SANITIZER_LINUX } // namespace __sanitizer #endif // SANITIZER_FREEBSD || SANITIZER_LINUX Index: vendor/compiler-rt/dist/lib/sanitizer_common/sanitizer_mac.cc =================================================================== --- vendor/compiler-rt/dist/lib/sanitizer_common/sanitizer_mac.cc (revision 317221) +++ vendor/compiler-rt/dist/lib/sanitizer_common/sanitizer_mac.cc (revision 317222) @@ -1,897 +1,896 @@ //===-- sanitizer_mac.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 shared between various sanitizers' runtime libraries and // implements OSX-specific functions. //===----------------------------------------------------------------------===// #include "sanitizer_platform.h" #if SANITIZER_MAC #include "sanitizer_mac.h" // Use 64-bit inodes in file operations. ASan does not support OS X 10.5, so // the clients will most certainly use 64-bit ones as well. #ifndef _DARWIN_USE_64_BIT_INODE #define _DARWIN_USE_64_BIT_INODE 1 #endif #include #include "sanitizer_common.h" #include "sanitizer_flags.h" #include "sanitizer_internal_defs.h" #include "sanitizer_libc.h" #include "sanitizer_placement_new.h" #include "sanitizer_platform_limits_posix.h" #include "sanitizer_procmaps.h" #if !SANITIZER_IOS #include // for _NSGetEnviron #else extern char **environ; #endif #if defined(__has_include) && __has_include() #define SANITIZER_OS_TRACE 1 #include #else #define SANITIZER_OS_TRACE 0 #endif #if !SANITIZER_IOS #include // for _NSGetArgv and _NSGetEnviron #else extern "C" { extern char ***_NSGetArgv(void); } #endif #include #include // for dladdr() #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include // From , but we don't have that file on iOS. extern "C" { extern char ***_NSGetArgv(void); extern char ***_NSGetEnviron(void); } // From , but we don't have that file on iOS. extern "C" { extern kern_return_t mach_vm_region_recurse( vm_map_t target_task, mach_vm_address_t *address, mach_vm_size_t *size, natural_t *nesting_depth, vm_region_recurse_info_t info, mach_msg_type_number_t *infoCnt); } namespace __sanitizer { #include "sanitizer_syscall_generic.inc" // Direct syscalls, don't call libmalloc hooks (but not available on 10.6). extern "C" void *__mmap(void *addr, size_t len, int prot, int flags, int fildes, off_t off) SANITIZER_WEAK_ATTRIBUTE; extern "C" int __munmap(void *, size_t) SANITIZER_WEAK_ATTRIBUTE; // ---------------------- sanitizer_libc.h uptr internal_mmap(void *addr, size_t length, int prot, int flags, int fd, u64 offset) { if (fd == -1) fd = VM_MAKE_TAG(VM_MEMORY_ANALYSIS_TOOL); if (&__mmap) return (uptr)__mmap(addr, length, prot, flags, fd, offset); return (uptr)mmap(addr, length, prot, flags, fd, offset); } uptr internal_munmap(void *addr, uptr length) { if (&__munmap) return __munmap(addr, length); return munmap(addr, length); } int internal_mprotect(void *addr, uptr length, int prot) { return mprotect(addr, length, prot); } uptr internal_close(fd_t fd) { return close(fd); } uptr internal_open(const char *filename, int flags) { return open(filename, flags); } uptr internal_open(const char *filename, int flags, u32 mode) { return open(filename, flags, mode); } uptr internal_read(fd_t fd, void *buf, uptr count) { return read(fd, buf, count); } uptr internal_write(fd_t fd, const void *buf, uptr count) { return write(fd, buf, count); } uptr internal_stat(const char *path, void *buf) { return stat(path, (struct stat *)buf); } uptr internal_lstat(const char *path, void *buf) { return lstat(path, (struct stat *)buf); } uptr internal_fstat(fd_t fd, void *buf) { return fstat(fd, (struct stat *)buf); } uptr internal_filesize(fd_t fd) { struct stat st; if (internal_fstat(fd, &st)) return -1; return (uptr)st.st_size; } uptr internal_dup2(int oldfd, int newfd) { return dup2(oldfd, newfd); } uptr internal_readlink(const char *path, char *buf, uptr bufsize) { return readlink(path, buf, bufsize); } uptr internal_unlink(const char *path) { return unlink(path); } uptr internal_sched_yield() { return sched_yield(); } void internal__exit(int exitcode) { _exit(exitcode); } unsigned int internal_sleep(unsigned int seconds) { return sleep(seconds); } uptr internal_getpid() { return getpid(); } int internal_sigaction(int signum, const void *act, void *oldact) { return sigaction(signum, (struct sigaction *)act, (struct sigaction *)oldact); } void internal_sigfillset(__sanitizer_sigset_t *set) { sigfillset(set); } uptr internal_sigprocmask(int how, __sanitizer_sigset_t *set, __sanitizer_sigset_t *oldset) { return sigprocmask(how, set, oldset); } // Doesn't call pthread_atfork() handlers (but not available on 10.6). extern "C" pid_t __fork(void) SANITIZER_WEAK_ATTRIBUTE; int internal_fork() { if (&__fork) return __fork(); return fork(); } int internal_forkpty(int *amaster) { int master, slave; if (openpty(&master, &slave, nullptr, nullptr, nullptr) == -1) return -1; int pid = internal_fork(); if (pid == -1) { close(master); close(slave); return -1; } if (pid == 0) { close(master); if (login_tty(slave) != 0) { // We already forked, there's not much we can do. Let's quit. Report("login_tty failed (errno %d)\n", errno); internal__exit(1); } } else { *amaster = master; close(slave); } return pid; } uptr internal_rename(const char *oldpath, const char *newpath) { return rename(oldpath, newpath); } uptr internal_ftruncate(fd_t fd, uptr size) { return ftruncate(fd, size); } uptr internal_execve(const char *filename, char *const argv[], char *const envp[]) { return execve(filename, argv, envp); } uptr internal_waitpid(int pid, int *status, int options) { return waitpid(pid, status, options); } // ----------------- sanitizer_common.h bool FileExists(const char *filename) { struct stat st; if (stat(filename, &st)) return false; // Sanity check: filename is a regular file. return S_ISREG(st.st_mode); } -uptr GetTid() { - // FIXME: This can potentially get truncated on 32-bit, where uptr is 4 bytes. - uint64_t tid; +tid_t GetTid() { + tid_t tid; pthread_threadid_np(nullptr, &tid); return tid; } void GetThreadStackTopAndBottom(bool at_initialization, uptr *stack_top, uptr *stack_bottom) { CHECK(stack_top); CHECK(stack_bottom); uptr stacksize = pthread_get_stacksize_np(pthread_self()); // pthread_get_stacksize_np() returns an incorrect stack size for the main // thread on Mavericks. See // https://github.com/google/sanitizers/issues/261 if ((GetMacosVersion() >= MACOS_VERSION_MAVERICKS) && at_initialization && stacksize == (1 << 19)) { struct rlimit rl; CHECK_EQ(getrlimit(RLIMIT_STACK, &rl), 0); // Most often rl.rlim_cur will be the desired 8M. if (rl.rlim_cur < kMaxThreadStackSize) { stacksize = rl.rlim_cur; } else { stacksize = kMaxThreadStackSize; } } void *stackaddr = pthread_get_stackaddr_np(pthread_self()); *stack_top = (uptr)stackaddr; *stack_bottom = *stack_top - stacksize; } char **GetEnviron() { #if !SANITIZER_IOS char ***env_ptr = _NSGetEnviron(); if (!env_ptr) { Report("_NSGetEnviron() returned NULL. Please make sure __asan_init() is " "called after libSystem_initializer().\n"); CHECK(env_ptr); } char **environ = *env_ptr; #endif CHECK(environ); return environ; } const char *GetEnv(const char *name) { char **env = GetEnviron(); uptr name_len = internal_strlen(name); while (*env != 0) { uptr len = internal_strlen(*env); if (len > name_len) { const char *p = *env; if (!internal_memcmp(p, name, name_len) && p[name_len] == '=') { // Match. return *env + name_len + 1; // String starting after =. } } env++; } return 0; } uptr ReadBinaryName(/*out*/char *buf, uptr buf_len) { CHECK_LE(kMaxPathLength, buf_len); // On OS X the executable path is saved to the stack by dyld. Reading it // from there is much faster than calling dladdr, especially for large // binaries with symbols. InternalScopedString exe_path(kMaxPathLength); uint32_t size = exe_path.size(); if (_NSGetExecutablePath(exe_path.data(), &size) == 0 && realpath(exe_path.data(), buf) != 0) { return internal_strlen(buf); } return 0; } uptr ReadLongProcessName(/*out*/char *buf, uptr buf_len) { return ReadBinaryName(buf, buf_len); } void ReExec() { UNIMPLEMENTED(); } uptr GetPageSize() { return sysconf(_SC_PAGESIZE); } BlockingMutex::BlockingMutex() { internal_memset(this, 0, sizeof(*this)); } void BlockingMutex::Lock() { CHECK(sizeof(OSSpinLock) <= sizeof(opaque_storage_)); CHECK_EQ(OS_SPINLOCK_INIT, 0); CHECK_EQ(owner_, 0); OSSpinLockLock((OSSpinLock*)&opaque_storage_); } void BlockingMutex::Unlock() { OSSpinLockUnlock((OSSpinLock*)&opaque_storage_); } void BlockingMutex::CheckLocked() { CHECK_NE(*(OSSpinLock*)&opaque_storage_, 0); } u64 NanoTime() { return 0; } uptr GetTlsSize() { return 0; } void InitTlsSize() { } void GetThreadStackAndTls(bool main, uptr *stk_addr, uptr *stk_size, uptr *tls_addr, uptr *tls_size) { #if !SANITIZER_GO uptr stack_top, stack_bottom; GetThreadStackTopAndBottom(main, &stack_top, &stack_bottom); *stk_addr = stack_bottom; *stk_size = stack_top - stack_bottom; *tls_addr = 0; *tls_size = 0; #else *stk_addr = 0; *stk_size = 0; *tls_addr = 0; *tls_size = 0; #endif } void ListOfModules::init() { clear(); MemoryMappingLayout memory_mapping(false); memory_mapping.DumpListOfModules(&modules_); } bool IsHandledDeadlySignal(int signum) { if ((SANITIZER_WATCHOS || SANITIZER_TVOS) && !(SANITIZER_IOSSIM)) // Handling fatal signals on watchOS and tvOS devices is disallowed. return false; if (common_flags()->handle_abort && signum == SIGABRT) return true; if (common_flags()->handle_sigill && signum == SIGILL) return true; if (common_flags()->handle_sigfpe && signum == SIGFPE) return true; if (common_flags()->handle_segv && signum == SIGSEGV) return true; return common_flags()->handle_sigbus && signum == SIGBUS; } MacosVersion cached_macos_version = MACOS_VERSION_UNINITIALIZED; MacosVersion GetMacosVersionInternal() { int mib[2] = { CTL_KERN, KERN_OSRELEASE }; char version[100]; uptr len = 0, maxlen = sizeof(version) / sizeof(version[0]); for (uptr i = 0; i < maxlen; i++) version[i] = '\0'; // Get the version length. CHECK_NE(sysctl(mib, 2, 0, &len, 0, 0), -1); CHECK_LT(len, maxlen); CHECK_NE(sysctl(mib, 2, version, &len, 0, 0), -1); switch (version[0]) { case '9': return MACOS_VERSION_LEOPARD; case '1': { switch (version[1]) { case '0': return MACOS_VERSION_SNOW_LEOPARD; case '1': return MACOS_VERSION_LION; case '2': return MACOS_VERSION_MOUNTAIN_LION; case '3': return MACOS_VERSION_MAVERICKS; case '4': return MACOS_VERSION_YOSEMITE; default: if (IsDigit(version[1])) return MACOS_VERSION_UNKNOWN_NEWER; else return MACOS_VERSION_UNKNOWN; } } default: return MACOS_VERSION_UNKNOWN; } } MacosVersion GetMacosVersion() { atomic_uint32_t *cache = reinterpret_cast(&cached_macos_version); MacosVersion result = static_cast(atomic_load(cache, memory_order_acquire)); if (result == MACOS_VERSION_UNINITIALIZED) { result = GetMacosVersionInternal(); atomic_store(cache, result, memory_order_release); } return result; } bool PlatformHasDifferentMemcpyAndMemmove() { // On OS X 10.7 memcpy() and memmove() are both resolved // into memmove$VARIANT$sse42. // See also https://github.com/google/sanitizers/issues/34. // TODO(glider): need to check dynamically that memcpy() and memmove() are // actually the same function. return GetMacosVersion() == MACOS_VERSION_SNOW_LEOPARD; } uptr GetRSS() { struct task_basic_info info; unsigned count = TASK_BASIC_INFO_COUNT; kern_return_t result = task_info(mach_task_self(), TASK_BASIC_INFO, (task_info_t)&info, &count); if (UNLIKELY(result != KERN_SUCCESS)) { Report("Cannot get task info. Error: %d\n", result); Die(); } return info.resident_size; } void *internal_start_thread(void(*func)(void *arg), void *arg) { // Start the thread with signals blocked, otherwise it can steal user signals. __sanitizer_sigset_t set, old; internal_sigfillset(&set); internal_sigprocmask(SIG_SETMASK, &set, &old); pthread_t th; pthread_create(&th, 0, (void*(*)(void *arg))func, arg); internal_sigprocmask(SIG_SETMASK, &old, 0); return th; } void internal_join_thread(void *th) { pthread_join((pthread_t)th, 0); } #if !SANITIZER_GO static BlockingMutex syslog_lock(LINKER_INITIALIZED); #endif void WriteOneLineToSyslog(const char *s) { #if !SANITIZER_GO syslog_lock.CheckLocked(); asl_log(nullptr, nullptr, ASL_LEVEL_ERR, "%s", s); #endif } void LogMessageOnPrintf(const char *str) { // Log all printf output to CrashLog. if (common_flags()->abort_on_error) CRAppendCrashLogMessage(str); } void LogFullErrorReport(const char *buffer) { #if !SANITIZER_GO // Log with os_trace. This will make it into the crash log. #if SANITIZER_OS_TRACE if (GetMacosVersion() >= MACOS_VERSION_YOSEMITE) { // os_trace requires the message (format parameter) to be a string literal. if (internal_strncmp(SanitizerToolName, "AddressSanitizer", sizeof("AddressSanitizer") - 1) == 0) os_trace("Address Sanitizer reported a failure."); else if (internal_strncmp(SanitizerToolName, "UndefinedBehaviorSanitizer", sizeof("UndefinedBehaviorSanitizer") - 1) == 0) os_trace("Undefined Behavior Sanitizer reported a failure."); else if (internal_strncmp(SanitizerToolName, "ThreadSanitizer", sizeof("ThreadSanitizer") - 1) == 0) os_trace("Thread Sanitizer reported a failure."); else os_trace("Sanitizer tool reported a failure."); if (common_flags()->log_to_syslog) os_trace("Consult syslog for more information."); } #endif // Log to syslog. // The logging on OS X may call pthread_create so we need the threading // environment to be fully initialized. Also, this should never be called when // holding the thread registry lock since that may result in a deadlock. If // the reporting thread holds the thread registry mutex, and asl_log waits // for GCD to dispatch a new thread, the process will deadlock, because the // pthread_create wrapper needs to acquire the lock as well. BlockingMutexLock l(&syslog_lock); if (common_flags()->log_to_syslog) WriteToSyslog(buffer); // The report is added to CrashLog as part of logging all of Printf output. #endif } SignalContext::WriteFlag SignalContext::GetWriteFlag(void *context) { #if defined(__x86_64__) || defined(__i386__) ucontext_t *ucontext = static_cast(context); return ucontext->uc_mcontext->__es.__err & 2 /*T_PF_WRITE*/ ? WRITE : READ; #else return UNKNOWN; #endif } void GetPcSpBp(void *context, uptr *pc, uptr *sp, uptr *bp) { ucontext_t *ucontext = (ucontext_t*)context; # if defined(__aarch64__) *pc = ucontext->uc_mcontext->__ss.__pc; # if defined(__IPHONE_8_0) && __IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_8_0 *bp = ucontext->uc_mcontext->__ss.__fp; # else *bp = ucontext->uc_mcontext->__ss.__lr; # endif *sp = ucontext->uc_mcontext->__ss.__sp; # elif defined(__x86_64__) *pc = ucontext->uc_mcontext->__ss.__rip; *bp = ucontext->uc_mcontext->__ss.__rbp; *sp = ucontext->uc_mcontext->__ss.__rsp; # elif defined(__arm__) *pc = ucontext->uc_mcontext->__ss.__pc; *bp = ucontext->uc_mcontext->__ss.__r[7]; *sp = ucontext->uc_mcontext->__ss.__sp; # elif defined(__i386__) *pc = ucontext->uc_mcontext->__ss.__eip; *bp = ucontext->uc_mcontext->__ss.__ebp; *sp = ucontext->uc_mcontext->__ss.__esp; # else # error "Unknown architecture" # endif } #if !SANITIZER_GO static const char kDyldInsertLibraries[] = "DYLD_INSERT_LIBRARIES"; LowLevelAllocator allocator_for_env; // Change the value of the env var |name|, leaking the original value. // If |name_value| is NULL, the variable is deleted from the environment, // otherwise the corresponding "NAME=value" string is replaced with // |name_value|. void LeakyResetEnv(const char *name, const char *name_value) { char **env = GetEnviron(); uptr name_len = internal_strlen(name); while (*env != 0) { uptr len = internal_strlen(*env); if (len > name_len) { const char *p = *env; if (!internal_memcmp(p, name, name_len) && p[name_len] == '=') { // Match. if (name_value) { // Replace the old value with the new one. *env = const_cast(name_value); } else { // Shift the subsequent pointers back. char **del = env; do { del[0] = del[1]; } while (*del++); } } } env++; } } SANITIZER_WEAK_CXX_DEFAULT_IMPL bool ReexecDisabled() { return false; } extern "C" SANITIZER_WEAK_ATTRIBUTE double dyldVersionNumber; static const double kMinDyldVersionWithAutoInterposition = 360.0; bool DyldNeedsEnvVariable() { // Although sanitizer support was added to LLVM on OS X 10.7+, GCC users // still may want use them on older systems. On older Darwin platforms, dyld // doesn't export dyldVersionNumber symbol and we simply return true. if (!&dyldVersionNumber) return true; // If running on OS X 10.11+ or iOS 9.0+, dyld will interpose even if // DYLD_INSERT_LIBRARIES is not set. However, checking OS version via // GetMacosVersion() doesn't work for the simulator. Let's instead check // `dyldVersionNumber`, which is exported by dyld, against a known version // number from the first OS release where this appeared. return dyldVersionNumber < kMinDyldVersionWithAutoInterposition; } void MaybeReexec() { if (ReexecDisabled()) return; // Make sure the dynamic runtime library is preloaded so that the // wrappers work. If it is not, set DYLD_INSERT_LIBRARIES and re-exec // ourselves. Dl_info info; RAW_CHECK(dladdr((void*)((uptr)&__sanitizer_report_error_summary), &info)); char *dyld_insert_libraries = const_cast(GetEnv(kDyldInsertLibraries)); uptr old_env_len = dyld_insert_libraries ? internal_strlen(dyld_insert_libraries) : 0; uptr fname_len = internal_strlen(info.dli_fname); const char *dylib_name = StripModuleName(info.dli_fname); uptr dylib_name_len = internal_strlen(dylib_name); bool lib_is_in_env = dyld_insert_libraries && internal_strstr(dyld_insert_libraries, dylib_name); if (DyldNeedsEnvVariable() && !lib_is_in_env) { // DYLD_INSERT_LIBRARIES is not set or does not contain the runtime // library. InternalScopedString program_name(1024); uint32_t buf_size = program_name.size(); _NSGetExecutablePath(program_name.data(), &buf_size); char *new_env = const_cast(info.dli_fname); if (dyld_insert_libraries) { // Append the runtime dylib name to the existing value of // DYLD_INSERT_LIBRARIES. new_env = (char*)allocator_for_env.Allocate(old_env_len + fname_len + 2); internal_strncpy(new_env, dyld_insert_libraries, old_env_len); new_env[old_env_len] = ':'; // Copy fname_len and add a trailing zero. internal_strncpy(new_env + old_env_len + 1, info.dli_fname, fname_len + 1); // Ok to use setenv() since the wrappers don't depend on the value of // asan_inited. setenv(kDyldInsertLibraries, new_env, /*overwrite*/1); } else { // Set DYLD_INSERT_LIBRARIES equal to the runtime dylib name. setenv(kDyldInsertLibraries, info.dli_fname, /*overwrite*/0); } VReport(1, "exec()-ing the program with\n"); VReport(1, "%s=%s\n", kDyldInsertLibraries, new_env); VReport(1, "to enable wrappers.\n"); execv(program_name.data(), *_NSGetArgv()); // We get here only if execv() failed. Report("ERROR: The process is launched without DYLD_INSERT_LIBRARIES, " "which is required for the sanitizer to work. We tried to set the " "environment variable and re-execute itself, but execv() failed, " "possibly because of sandbox restrictions. Make sure to launch the " "executable with:\n%s=%s\n", kDyldInsertLibraries, new_env); RAW_CHECK("execv failed" && 0); } // Verify that interceptors really work. We'll use dlsym to locate // "pthread_create", if interceptors are working, it should really point to // "wrap_pthread_create" within our own dylib. Dl_info info_pthread_create; void *dlopen_addr = dlsym(RTLD_DEFAULT, "pthread_create"); RAW_CHECK(dladdr(dlopen_addr, &info_pthread_create)); if (internal_strcmp(info.dli_fname, info_pthread_create.dli_fname) != 0) { Report( "ERROR: Interceptors are not working. This may be because %s is " "loaded too late (e.g. via dlopen). Please launch the executable " "with:\n%s=%s\n", SanitizerToolName, kDyldInsertLibraries, info.dli_fname); RAW_CHECK("interceptors not installed" && 0); } if (!lib_is_in_env) return; // DYLD_INSERT_LIBRARIES is set and contains the runtime library. Let's remove // the dylib from the environment variable, because interceptors are installed // and we don't want our children to inherit the variable. uptr env_name_len = internal_strlen(kDyldInsertLibraries); // Allocate memory to hold the previous env var name, its value, the '=' // sign and the '\0' char. char *new_env = (char*)allocator_for_env.Allocate( old_env_len + 2 + env_name_len); RAW_CHECK(new_env); internal_memset(new_env, '\0', old_env_len + 2 + env_name_len); internal_strncpy(new_env, kDyldInsertLibraries, env_name_len); new_env[env_name_len] = '='; char *new_env_pos = new_env + env_name_len + 1; // Iterate over colon-separated pieces of |dyld_insert_libraries|. char *piece_start = dyld_insert_libraries; char *piece_end = NULL; char *old_env_end = dyld_insert_libraries + old_env_len; do { if (piece_start[0] == ':') piece_start++; piece_end = internal_strchr(piece_start, ':'); if (!piece_end) piece_end = dyld_insert_libraries + old_env_len; if ((uptr)(piece_start - dyld_insert_libraries) > old_env_len) break; uptr piece_len = piece_end - piece_start; char *filename_start = (char *)internal_memrchr(piece_start, '/', piece_len); uptr filename_len = piece_len; if (filename_start) { filename_start += 1; filename_len = piece_len - (filename_start - piece_start); } else { filename_start = piece_start; } // If the current piece isn't the runtime library name, // append it to new_env. if ((dylib_name_len != filename_len) || (internal_memcmp(filename_start, dylib_name, dylib_name_len) != 0)) { if (new_env_pos != new_env + env_name_len + 1) { new_env_pos[0] = ':'; new_env_pos++; } internal_strncpy(new_env_pos, piece_start, piece_len); new_env_pos += piece_len; } // Move on to the next piece. piece_start = piece_end; } while (piece_start < old_env_end); // Can't use setenv() here, because it requires the allocator to be // initialized. // FIXME: instead of filtering DYLD_INSERT_LIBRARIES here, do it in // a separate function called after InitializeAllocator(). if (new_env_pos == new_env + env_name_len + 1) new_env = NULL; LeakyResetEnv(kDyldInsertLibraries, new_env); } #endif // SANITIZER_GO char **GetArgv() { return *_NSGetArgv(); } uptr FindAvailableMemoryRange(uptr shadow_size, uptr alignment, uptr left_padding) { typedef vm_region_submap_short_info_data_64_t RegionInfo; enum { kRegionInfoSize = VM_REGION_SUBMAP_SHORT_INFO_COUNT_64 }; // Start searching for available memory region past PAGEZERO, which is // 4KB on 32-bit and 4GB on 64-bit. mach_vm_address_t start_address = (SANITIZER_WORDSIZE == 32) ? 0x000000001000 : 0x000100000000; mach_vm_address_t address = start_address; mach_vm_address_t free_begin = start_address; kern_return_t kr = KERN_SUCCESS; while (kr == KERN_SUCCESS) { mach_vm_size_t vmsize = 0; natural_t depth = 0; RegionInfo vminfo; mach_msg_type_number_t count = kRegionInfoSize; kr = mach_vm_region_recurse(mach_task_self(), &address, &vmsize, &depth, (vm_region_info_t)&vminfo, &count); if (free_begin != address) { // We found a free region [free_begin..address-1]. uptr shadow_address = RoundUpTo((uptr)free_begin + left_padding, alignment); if (shadow_address + shadow_size < (uptr)address) { return shadow_address; } } // Move to the next region. address += vmsize; free_begin = address; } // We looked at all free regions and could not find one large enough. return 0; } // FIXME implement on this platform. void GetMemoryProfile(fill_profile_f cb, uptr *stats, uptr stats_size) { } void SignalContext::DumpAllRegisters(void *context) { Report("Register values:\n"); ucontext_t *ucontext = (ucontext_t*)context; # define DUMPREG64(r) \ Printf("%s = 0x%016llx ", #r, ucontext->uc_mcontext->__ss.__ ## r); # define DUMPREG32(r) \ Printf("%s = 0x%08x ", #r, ucontext->uc_mcontext->__ss.__ ## r); # define DUMPREG_(r) Printf(" "); DUMPREG(r); # define DUMPREG__(r) Printf(" "); DUMPREG(r); # define DUMPREG___(r) Printf(" "); DUMPREG(r); # if defined(__x86_64__) # define DUMPREG(r) DUMPREG64(r) DUMPREG(rax); DUMPREG(rbx); DUMPREG(rcx); DUMPREG(rdx); Printf("\n"); DUMPREG(rdi); DUMPREG(rsi); DUMPREG(rbp); DUMPREG(rsp); Printf("\n"); DUMPREG_(r8); DUMPREG_(r9); DUMPREG(r10); DUMPREG(r11); Printf("\n"); DUMPREG(r12); DUMPREG(r13); DUMPREG(r14); DUMPREG(r15); Printf("\n"); # elif defined(__i386__) # define DUMPREG(r) DUMPREG32(r) DUMPREG(eax); DUMPREG(ebx); DUMPREG(ecx); DUMPREG(edx); Printf("\n"); DUMPREG(edi); DUMPREG(esi); DUMPREG(ebp); DUMPREG(esp); Printf("\n"); # elif defined(__aarch64__) # define DUMPREG(r) DUMPREG64(r) DUMPREG_(x[0]); DUMPREG_(x[1]); DUMPREG_(x[2]); DUMPREG_(x[3]); Printf("\n"); DUMPREG_(x[4]); DUMPREG_(x[5]); DUMPREG_(x[6]); DUMPREG_(x[7]); Printf("\n"); DUMPREG_(x[8]); DUMPREG_(x[9]); DUMPREG(x[10]); DUMPREG(x[11]); Printf("\n"); DUMPREG(x[12]); DUMPREG(x[13]); DUMPREG(x[14]); DUMPREG(x[15]); Printf("\n"); DUMPREG(x[16]); DUMPREG(x[17]); DUMPREG(x[18]); DUMPREG(x[19]); Printf("\n"); DUMPREG(x[20]); DUMPREG(x[21]); DUMPREG(x[22]); DUMPREG(x[23]); Printf("\n"); DUMPREG(x[24]); DUMPREG(x[25]); DUMPREG(x[26]); DUMPREG(x[27]); Printf("\n"); DUMPREG(x[28]); DUMPREG___(fp); DUMPREG___(lr); DUMPREG___(sp); Printf("\n"); # elif defined(__arm__) # define DUMPREG(r) DUMPREG32(r) DUMPREG_(r[0]); DUMPREG_(r[1]); DUMPREG_(r[2]); DUMPREG_(r[3]); Printf("\n"); DUMPREG_(r[4]); DUMPREG_(r[5]); DUMPREG_(r[6]); DUMPREG_(r[7]); Printf("\n"); DUMPREG_(r[8]); DUMPREG_(r[9]); DUMPREG(r[10]); DUMPREG(r[11]); Printf("\n"); DUMPREG(r[12]); DUMPREG___(sp); DUMPREG___(lr); DUMPREG___(pc); Printf("\n"); # else # error "Unknown architecture" # endif # undef DUMPREG64 # undef DUMPREG32 # undef DUMPREG_ # undef DUMPREG__ # undef DUMPREG___ # undef DUMPREG } static inline bool CompareBaseAddress(const LoadedModule &a, const LoadedModule &b) { return a.base_address() < b.base_address(); } void FormatUUID(char *out, uptr size, const u8 *uuid) { internal_snprintf(out, size, "<%02X%02X%02X%02X-%02X%02X-%02X%02X-%02X%02X-" "%02X%02X%02X%02X%02X%02X>", uuid[0], uuid[1], uuid[2], uuid[3], uuid[4], uuid[5], uuid[6], uuid[7], uuid[8], uuid[9], uuid[10], uuid[11], uuid[12], uuid[13], uuid[14], uuid[15]); } void PrintModuleMap() { Printf("Process module map:\n"); MemoryMappingLayout memory_mapping(false); InternalMmapVector modules(/*initial_capacity*/ 128); memory_mapping.DumpListOfModules(&modules); InternalSort(&modules, modules.size(), CompareBaseAddress); for (uptr i = 0; i < modules.size(); ++i) { char uuid_str[128]; FormatUUID(uuid_str, sizeof(uuid_str), modules[i].uuid()); Printf("0x%zx-0x%zx %s (%s) %s\n", modules[i].base_address(), modules[i].max_executable_address(), modules[i].full_name(), ModuleArchToString(modules[i].arch()), uuid_str); } Printf("End of module map.\n"); } void CheckNoDeepBind(const char *filename, int flag) { // Do nothing. } } // namespace __sanitizer #endif // SANITIZER_MAC Index: vendor/compiler-rt/dist/lib/sanitizer_common/sanitizer_platform.h =================================================================== --- vendor/compiler-rt/dist/lib/sanitizer_common/sanitizer_platform.h (revision 317221) +++ vendor/compiler-rt/dist/lib/sanitizer_common/sanitizer_platform.h (revision 317222) @@ -1,262 +1,273 @@ //===-- sanitizer_platform.h ------------------------------------*- C++ -*-===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// // // Common platform macros. //===----------------------------------------------------------------------===// #ifndef SANITIZER_PLATFORM_H #define SANITIZER_PLATFORM_H #if !defined(__linux__) && !defined(__FreeBSD__) && \ !defined(__APPLE__) && !defined(_WIN32) # error "This operating system is not supported" #endif #if defined(__linux__) # define SANITIZER_LINUX 1 #else # define SANITIZER_LINUX 0 #endif #if defined(__FreeBSD__) # define SANITIZER_FREEBSD 1 #else # define SANITIZER_FREEBSD 0 #endif #if defined(__APPLE__) # define SANITIZER_MAC 1 # include # if TARGET_OS_IPHONE # define SANITIZER_IOS 1 # else # define SANITIZER_IOS 0 # endif # if TARGET_IPHONE_SIMULATOR # define SANITIZER_IOSSIM 1 # else # define SANITIZER_IOSSIM 0 # endif #else # define SANITIZER_MAC 0 # define SANITIZER_IOS 0 # define SANITIZER_IOSSIM 0 #endif #if defined(__APPLE__) && TARGET_OS_IPHONE && TARGET_OS_WATCH # define SANITIZER_WATCHOS 1 #else # define SANITIZER_WATCHOS 0 #endif #if defined(__APPLE__) && TARGET_OS_IPHONE && TARGET_OS_TV # define SANITIZER_TVOS 1 #else # define SANITIZER_TVOS 0 #endif #if defined(_WIN32) # define SANITIZER_WINDOWS 1 #else # define SANITIZER_WINDOWS 0 #endif #if defined(_WIN64) # define SANITIZER_WINDOWS64 1 #else # define SANITIZER_WINDOWS64 0 #endif #if defined(__ANDROID__) # define SANITIZER_ANDROID 1 #else # define SANITIZER_ANDROID 0 #endif #define SANITIZER_POSIX (SANITIZER_FREEBSD || SANITIZER_LINUX || SANITIZER_MAC) #if __LP64__ || defined(_WIN64) # define SANITIZER_WORDSIZE 64 #else # define SANITIZER_WORDSIZE 32 #endif #if SANITIZER_WORDSIZE == 64 # define FIRST_32_SECOND_64(a, b) (b) #else # define FIRST_32_SECOND_64(a, b) (a) #endif #if defined(__x86_64__) && !defined(_LP64) # define SANITIZER_X32 1 #else # define SANITIZER_X32 0 #endif #if defined(__mips__) # define SANITIZER_MIPS 1 # if defined(__mips64) # define SANITIZER_MIPS32 0 # define SANITIZER_MIPS64 1 # else # define SANITIZER_MIPS32 1 # define SANITIZER_MIPS64 0 # endif #else # define SANITIZER_MIPS 0 # define SANITIZER_MIPS32 0 # define SANITIZER_MIPS64 0 #endif #if defined(__s390__) # define SANITIZER_S390 1 # if defined(__s390x__) # define SANITIZER_S390_31 0 # define SANITIZER_S390_64 1 # else # define SANITIZER_S390_31 1 # define SANITIZER_S390_64 0 # endif #else # define SANITIZER_S390 0 # define SANITIZER_S390_31 0 # define SANITIZER_S390_64 0 #endif #if defined(__powerpc__) # define SANITIZER_PPC 1 # if defined(__powerpc64__) # define SANITIZER_PPC32 0 # define SANITIZER_PPC64 1 // 64-bit PPC has two ABIs (v1 and v2). The old powerpc64 target is // big-endian, and uses v1 ABI (known for its function descriptors), // while the new powerpc64le target is little-endian and uses v2. // In theory, you could convince gcc to compile for their evil twins // (eg. big-endian v2), but you won't find such combinations in the wild // (it'd require bootstrapping a whole system, which would be quite painful // - there's no target triple for that). LLVM doesn't support them either. # if _CALL_ELF == 2 # define SANITIZER_PPC64V1 0 # define SANITIZER_PPC64V2 1 # else # define SANITIZER_PPC64V1 1 # define SANITIZER_PPC64V2 0 # endif # else # define SANITIZER_PPC32 1 # define SANITIZER_PPC64 0 # define SANITIZER_PPC64V1 0 # define SANITIZER_PPC64V2 0 # endif #else # define SANITIZER_PPC 0 # define SANITIZER_PPC32 0 # define SANITIZER_PPC64 0 # define SANITIZER_PPC64V1 0 # define SANITIZER_PPC64V2 0 #endif #if defined(__arm__) # define SANITIZER_ARM 1 #else # define SANITIZER_ARM 0 #endif // By default we allow to use SizeClassAllocator64 on 64-bit platform. // But in some cases (e.g. AArch64's 39-bit address space) SizeClassAllocator64 // does not work well and we need to fallback to SizeClassAllocator32. // For such platforms build this code with -DSANITIZER_CAN_USE_ALLOCATOR64=0 or // change the definition of SANITIZER_CAN_USE_ALLOCATOR64 here. #ifndef SANITIZER_CAN_USE_ALLOCATOR64 # if SANITIZER_ANDROID && defined(__aarch64__) # define SANITIZER_CAN_USE_ALLOCATOR64 1 # elif defined(__mips64) || defined(__aarch64__) # define SANITIZER_CAN_USE_ALLOCATOR64 0 # else # define SANITIZER_CAN_USE_ALLOCATOR64 (SANITIZER_WORDSIZE == 64) # endif #endif // The range of addresses which can be returned my mmap. // FIXME: this value should be different on different platforms. Larger values // will still work but will consume more memory for TwoLevelByteMap. #if defined(__mips__) # define SANITIZER_MMAP_RANGE_SIZE FIRST_32_SECOND_64(1ULL << 32, 1ULL << 40) #elif defined(__aarch64__) # define SANITIZER_MMAP_RANGE_SIZE FIRST_32_SECOND_64(1ULL << 32, 1ULL << 48) #else # define SANITIZER_MMAP_RANGE_SIZE FIRST_32_SECOND_64(1ULL << 32, 1ULL << 47) #endif // The AArch64 linux port uses the canonical syscall set as mandated by // the upstream linux community for all new ports. Other ports may still // use legacy syscalls. #ifndef SANITIZER_USES_CANONICAL_LINUX_SYSCALLS # if defined(__aarch64__) && SANITIZER_LINUX # define SANITIZER_USES_CANONICAL_LINUX_SYSCALLS 1 # else # define SANITIZER_USES_CANONICAL_LINUX_SYSCALLS 0 # endif #endif // udi16 syscalls can only be used when the following conditions are // met: // * target is one of arm32, x86-32, sparc32, sh or m68k // * libc version is libc5, glibc-2.0, glibc-2.1 or glibc-2.2 to 2.15 // built against > linux-2.2 kernel headers // Since we don't want to include libc headers here, we check the // target only. #if defined(__arm__) || SANITIZER_X32 || defined(__sparc__) #define SANITIZER_USES_UID16_SYSCALLS 1 #else #define SANITIZER_USES_UID16_SYSCALLS 0 #endif #if defined(__mips__) # define SANITIZER_POINTER_FORMAT_LENGTH FIRST_32_SECOND_64(8, 10) #else # define SANITIZER_POINTER_FORMAT_LENGTH FIRST_32_SECOND_64(8, 12) #endif // Assume obsolete RPC headers are available by default #if !defined(HAVE_RPC_XDR_H) && !defined(HAVE_TIRPC_RPC_XDR_H) # define HAVE_RPC_XDR_H (SANITIZER_LINUX && !SANITIZER_ANDROID) # define HAVE_TIRPC_RPC_XDR_H 0 #endif /// \macro MSC_PREREQ /// \brief Is the compiler MSVC of at least the specified version? /// The common \param version values to check for are: /// * 1800: Microsoft Visual Studio 2013 / 12.0 /// * 1900: Microsoft Visual Studio 2015 / 14.0 #ifdef _MSC_VER # define MSC_PREREQ(version) (_MSC_VER >= (version)) #else # define MSC_PREREQ(version) 0 #endif #if defined(__arm64__) && SANITIZER_IOS # define SANITIZER_NON_UNIQUE_TYPEINFO 1 #else # define SANITIZER_NON_UNIQUE_TYPEINFO 0 #endif // On linux, some architectures had an ABI transition from 64-bit long double // (ie. same as double) to 128-bit long double. On those, glibc symbols // involving long doubles come in two versions, and we need to pass the // correct one to dlvsym when intercepting them. #if SANITIZER_LINUX && (SANITIZER_S390 || SANITIZER_PPC32 || SANITIZER_PPC64V1) #define SANITIZER_NLDBL_VERSION "GLIBC_2.4" #endif #if SANITIZER_GO == 0 # define SANITIZER_GO 0 #endif +// On PowerPC and ARM Thumb, calling pthread_exit() causes LSan to detect leaks. +// pthread_exit() performs unwinding that leads to dlopen'ing libgcc_s.so. +// dlopen mallocs "libgcc_s.so" string which confuses LSan, it fails to realize +// that this allocation happens in dynamic linker and should be ignored. +#if SANITIZER_PPC || defined(__thumb__) +# define SANITIZER_SUPPRESS_LEAK_ON_PTHREAD_EXIT 1 +#else +# define SANITIZER_SUPPRESS_LEAK_ON_PTHREAD_EXIT 0 +#endif + + #endif // SANITIZER_PLATFORM_H Index: vendor/compiler-rt/dist/lib/sanitizer_common/sanitizer_procmaps_common.cc =================================================================== --- vendor/compiler-rt/dist/lib/sanitizer_common/sanitizer_procmaps_common.cc (revision 317221) +++ vendor/compiler-rt/dist/lib/sanitizer_common/sanitizer_procmaps_common.cc (revision 317222) @@ -1,175 +1,176 @@ //===-- sanitizer_procmaps_common.cc --------------------------------------===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// // // Information about the process mappings (common parts). //===----------------------------------------------------------------------===// #include "sanitizer_platform.h" #if SANITIZER_FREEBSD || SANITIZER_LINUX #include "sanitizer_common.h" #include "sanitizer_placement_new.h" #include "sanitizer_procmaps.h" namespace __sanitizer { // Linker initialized. ProcSelfMapsBuff MemoryMappingLayout::cached_proc_self_maps_; StaticSpinMutex MemoryMappingLayout::cache_lock_; // Linker initialized. static int TranslateDigit(char c) { if (c >= '0' && c <= '9') return c - '0'; if (c >= 'a' && c <= 'f') return c - 'a' + 10; if (c >= 'A' && c <= 'F') return c - 'A' + 10; return -1; } // Parse a number and promote 'p' up to the first non-digit character. static uptr ParseNumber(const char **p, int base) { uptr n = 0; int d; CHECK(base >= 2 && base <= 16); while ((d = TranslateDigit(**p)) >= 0 && d < base) { n = n * base + d; (*p)++; } return n; } bool IsDecimal(char c) { int d = TranslateDigit(c); return d >= 0 && d < 10; } uptr ParseDecimal(const char **p) { return ParseNumber(p, 10); } bool IsHex(char c) { int d = TranslateDigit(c); return d >= 0 && d < 16; } uptr ParseHex(const char **p) { return ParseNumber(p, 16); } MemoryMappingLayout::MemoryMappingLayout(bool cache_enabled) { ReadProcMaps(&proc_self_maps_); if (cache_enabled) { if (proc_self_maps_.mmaped_size == 0) { LoadFromCache(); CHECK_GT(proc_self_maps_.len, 0); } } else { CHECK_GT(proc_self_maps_.mmaped_size, 0); } Reset(); // FIXME: in the future we may want to cache the mappings on demand only. if (cache_enabled) CacheMemoryMappings(); } MemoryMappingLayout::~MemoryMappingLayout() { // Only unmap the buffer if it is different from the cached one. Otherwise // it will be unmapped when the cache is refreshed. if (proc_self_maps_.data != cached_proc_self_maps_.data) { UnmapOrDie(proc_self_maps_.data, proc_self_maps_.mmaped_size); } } void MemoryMappingLayout::Reset() { current_ = proc_self_maps_.data; } // static void MemoryMappingLayout::CacheMemoryMappings() { SpinMutexLock l(&cache_lock_); // Don't invalidate the cache if the mappings are unavailable. ProcSelfMapsBuff old_proc_self_maps; old_proc_self_maps = cached_proc_self_maps_; ReadProcMaps(&cached_proc_self_maps_); if (cached_proc_self_maps_.mmaped_size == 0) { cached_proc_self_maps_ = old_proc_self_maps; } else { if (old_proc_self_maps.mmaped_size) { UnmapOrDie(old_proc_self_maps.data, old_proc_self_maps.mmaped_size); } } } void MemoryMappingLayout::LoadFromCache() { SpinMutexLock l(&cache_lock_); if (cached_proc_self_maps_.data) { proc_self_maps_ = cached_proc_self_maps_; } } void MemoryMappingLayout::DumpListOfModules( InternalMmapVector *modules) { Reset(); uptr cur_beg, cur_end, cur_offset, prot; InternalScopedString module_name(kMaxPathLength); for (uptr i = 0; Next(&cur_beg, &cur_end, &cur_offset, module_name.data(), module_name.size(), &prot); i++) { const char *cur_name = module_name.data(); if (cur_name[0] == '\0') continue; // Don't subtract 'cur_beg' from the first entry: // * If a binary is compiled w/o -pie, then the first entry in // process maps is likely the binary itself (all dynamic libs // are mapped higher in address space). For such a binary, // instruction offset in binary coincides with the actual // instruction address in virtual memory (as code section // is mapped to a fixed memory range). // * If a binary is compiled with -pie, all the modules are // mapped high at address space (in particular, higher than // shadow memory of the tool), so the module can't be the // first entry. uptr base_address = (i ? cur_beg : 0) - cur_offset; LoadedModule cur_module; cur_module.set(cur_name, base_address); - cur_module.addAddressRange(cur_beg, cur_end, prot & kProtectionExecute); + cur_module.addAddressRange(cur_beg, cur_end, prot & kProtectionExecute, + prot & kProtectionRead); modules->push_back(cur_module); } } void GetMemoryProfile(fill_profile_f cb, uptr *stats, uptr stats_size) { char *smaps = nullptr; uptr smaps_cap = 0; uptr smaps_len = 0; if (!ReadFileToBuffer("/proc/self/smaps", &smaps, &smaps_cap, &smaps_len)) return; uptr start = 0; bool file = false; const char *pos = smaps; while (pos < smaps + smaps_len) { if (IsHex(pos[0])) { start = ParseHex(&pos); for (; *pos != '/' && *pos > '\n'; pos++) {} file = *pos == '/'; } else if (internal_strncmp(pos, "Rss:", 4) == 0) { while (!IsDecimal(*pos)) pos++; uptr rss = ParseDecimal(&pos) * 1024; cb(start, rss, file, stats, stats_size); } while (*pos++ != '\n') {} } UnmapOrDie(smaps, smaps_cap); } } // namespace __sanitizer #endif // SANITIZER_FREEBSD || SANITIZER_LINUX Index: vendor/compiler-rt/dist/lib/sanitizer_common/sanitizer_procmaps_mac.cc =================================================================== --- vendor/compiler-rt/dist/lib/sanitizer_common/sanitizer_procmaps_mac.cc (revision 317221) +++ vendor/compiler-rt/dist/lib/sanitizer_common/sanitizer_procmaps_mac.cc (revision 317222) @@ -1,271 +1,272 @@ //===-- sanitizer_procmaps_mac.cc -----------------------------------------===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// // // Information about the process mappings (Mac-specific parts). //===----------------------------------------------------------------------===// #include "sanitizer_platform.h" #if SANITIZER_MAC #include "sanitizer_common.h" #include "sanitizer_placement_new.h" #include "sanitizer_procmaps.h" #include #include // These are not available in older macOS SDKs. #ifndef CPU_SUBTYPE_X86_64_H #define CPU_SUBTYPE_X86_64_H ((cpu_subtype_t)8) /* Haswell */ #endif #ifndef CPU_SUBTYPE_ARM_V7S #define CPU_SUBTYPE_ARM_V7S ((cpu_subtype_t)11) /* Swift */ #endif #ifndef CPU_SUBTYPE_ARM_V7K #define CPU_SUBTYPE_ARM_V7K ((cpu_subtype_t)12) #endif #ifndef CPU_TYPE_ARM64 #define CPU_TYPE_ARM64 (CPU_TYPE_ARM | CPU_ARCH_ABI64) #endif namespace __sanitizer { MemoryMappingLayout::MemoryMappingLayout(bool cache_enabled) { Reset(); } MemoryMappingLayout::~MemoryMappingLayout() { } // More information about Mach-O headers can be found in mach-o/loader.h // Each Mach-O image has a header (mach_header or mach_header_64) starting with // a magic number, and a list of linker load commands directly following the // header. // A load command is at least two 32-bit words: the command type and the // command size in bytes. We're interested only in segment load commands // (LC_SEGMENT and LC_SEGMENT_64), which tell that a part of the file is mapped // into the task's address space. // The |vmaddr|, |vmsize| and |fileoff| fields of segment_command or // segment_command_64 correspond to the memory address, memory size and the // file offset of the current memory segment. // Because these fields are taken from the images as is, one needs to add // _dyld_get_image_vmaddr_slide() to get the actual addresses at runtime. void MemoryMappingLayout::Reset() { // Count down from the top. // TODO(glider): as per man 3 dyld, iterating over the headers with // _dyld_image_count is thread-unsafe. We need to register callbacks for // adding and removing images which will invalidate the MemoryMappingLayout // state. current_image_ = _dyld_image_count(); current_load_cmd_count_ = -1; current_load_cmd_addr_ = 0; current_magic_ = 0; current_filetype_ = 0; current_arch_ = kModuleArchUnknown; internal_memset(current_uuid_, 0, kModuleUUIDSize); } // static void MemoryMappingLayout::CacheMemoryMappings() { // No-op on Mac for now. } void MemoryMappingLayout::LoadFromCache() { // No-op on Mac for now. } // Next and NextSegmentLoad were inspired by base/sysinfo.cc in // Google Perftools, https://github.com/gperftools/gperftools. // NextSegmentLoad scans the current image for the next segment load command // and returns the start and end addresses and file offset of the corresponding // segment. // Note that the segment addresses are not necessarily sorted. template bool MemoryMappingLayout::NextSegmentLoad(uptr *start, uptr *end, uptr *offset, char filename[], uptr filename_size, ModuleArch *arch, u8 *uuid, uptr *protection) { const char *lc = current_load_cmd_addr_; current_load_cmd_addr_ += ((const load_command *)lc)->cmdsize; if (((const load_command *)lc)->cmd == kLCSegment) { const sptr dlloff = _dyld_get_image_vmaddr_slide(current_image_); const SegmentCommand* sc = (const SegmentCommand *)lc; if (start) *start = sc->vmaddr + dlloff; if (protection) { // Return the initial protection. *protection = sc->initprot; } if (end) *end = sc->vmaddr + sc->vmsize + dlloff; if (offset) { if (current_filetype_ == /*MH_EXECUTE*/ 0x2) { *offset = sc->vmaddr; } else { *offset = sc->fileoff; } } if (filename) { internal_strncpy(filename, _dyld_get_image_name(current_image_), filename_size); } if (arch) { *arch = current_arch_; } if (uuid) { internal_memcpy(uuid, current_uuid_, kModuleUUIDSize); } return true; } return false; } ModuleArch ModuleArchFromCpuType(cpu_type_t cputype, cpu_subtype_t cpusubtype) { cpusubtype = cpusubtype & ~CPU_SUBTYPE_MASK; switch (cputype) { case CPU_TYPE_I386: return kModuleArchI386; case CPU_TYPE_X86_64: if (cpusubtype == CPU_SUBTYPE_X86_64_ALL) return kModuleArchX86_64; if (cpusubtype == CPU_SUBTYPE_X86_64_H) return kModuleArchX86_64H; CHECK(0 && "Invalid subtype of x86_64"); return kModuleArchUnknown; case CPU_TYPE_ARM: if (cpusubtype == CPU_SUBTYPE_ARM_V6) return kModuleArchARMV6; if (cpusubtype == CPU_SUBTYPE_ARM_V7) return kModuleArchARMV7; if (cpusubtype == CPU_SUBTYPE_ARM_V7S) return kModuleArchARMV7S; if (cpusubtype == CPU_SUBTYPE_ARM_V7K) return kModuleArchARMV7K; CHECK(0 && "Invalid subtype of ARM"); return kModuleArchUnknown; case CPU_TYPE_ARM64: return kModuleArchARM64; default: CHECK(0 && "Invalid CPU type"); return kModuleArchUnknown; } } static const load_command *NextCommand(const load_command *lc) { return (const load_command *)((char *)lc + lc->cmdsize); } static void FindUUID(const load_command *first_lc, u8 *uuid_output) { for (const load_command *lc = first_lc; lc->cmd != 0; lc = NextCommand(lc)) { if (lc->cmd != LC_UUID) continue; const uuid_command *uuid_lc = (const uuid_command *)lc; const uint8_t *uuid = &uuid_lc->uuid[0]; internal_memcpy(uuid_output, uuid, kModuleUUIDSize); return; } } static bool IsModuleInstrumented(const load_command *first_lc) { for (const load_command *lc = first_lc; lc->cmd != 0; lc = NextCommand(lc)) { if (lc->cmd != LC_LOAD_DYLIB) continue; const dylib_command *dylib_lc = (const dylib_command *)lc; uint32_t dylib_name_offset = dylib_lc->dylib.name.offset; const char *dylib_name = ((const char *)dylib_lc) + dylib_name_offset; dylib_name = StripModuleName(dylib_name); if (dylib_name != 0 && (internal_strstr(dylib_name, "libclang_rt."))) { return true; } } return false; } bool MemoryMappingLayout::Next(uptr *start, uptr *end, uptr *offset, char filename[], uptr filename_size, uptr *protection, ModuleArch *arch, u8 *uuid) { for (; current_image_ >= 0; current_image_--) { const mach_header* hdr = _dyld_get_image_header(current_image_); if (!hdr) continue; if (current_load_cmd_count_ < 0) { // Set up for this image; current_load_cmd_count_ = hdr->ncmds; current_magic_ = hdr->magic; current_filetype_ = hdr->filetype; current_arch_ = ModuleArchFromCpuType(hdr->cputype, hdr->cpusubtype); switch (current_magic_) { #ifdef MH_MAGIC_64 case MH_MAGIC_64: { current_load_cmd_addr_ = (char*)hdr + sizeof(mach_header_64); break; } #endif case MH_MAGIC: { current_load_cmd_addr_ = (char*)hdr + sizeof(mach_header); break; } default: { continue; } } FindUUID((const load_command *)current_load_cmd_addr_, ¤t_uuid_[0]); current_instrumented_ = IsModuleInstrumented((const load_command *)current_load_cmd_addr_); } for (; current_load_cmd_count_ >= 0; current_load_cmd_count_--) { switch (current_magic_) { // current_magic_ may be only one of MH_MAGIC, MH_MAGIC_64. #ifdef MH_MAGIC_64 case MH_MAGIC_64: { if (NextSegmentLoad( start, end, offset, filename, filename_size, arch, uuid, protection)) return true; break; } #endif case MH_MAGIC: { if (NextSegmentLoad( start, end, offset, filename, filename_size, arch, uuid, protection)) return true; break; } } } // If we get here, no more load_cmd's in this image talk about // segments. Go on to the next image. } return false; } void MemoryMappingLayout::DumpListOfModules( InternalMmapVector *modules) { Reset(); uptr cur_beg, cur_end, prot; ModuleArch cur_arch; u8 cur_uuid[kModuleUUIDSize]; InternalScopedString module_name(kMaxPathLength); for (uptr i = 0; Next(&cur_beg, &cur_end, 0, module_name.data(), module_name.size(), &prot, &cur_arch, &cur_uuid[0]); i++) { const char *cur_name = module_name.data(); if (cur_name[0] == '\0') continue; LoadedModule *cur_module = nullptr; if (!modules->empty() && 0 == internal_strcmp(cur_name, modules->back().full_name())) { cur_module = &modules->back(); } else { modules->push_back(LoadedModule()); cur_module = &modules->back(); cur_module->set(cur_name, cur_beg, cur_arch, cur_uuid, current_instrumented_); } - cur_module->addAddressRange(cur_beg, cur_end, prot & kProtectionExecute); + cur_module->addAddressRange(cur_beg, cur_end, prot & kProtectionExecute, + prot & kProtectionRead); } } } // namespace __sanitizer #endif // SANITIZER_MAC Index: vendor/compiler-rt/dist/lib/sanitizer_common/sanitizer_stoptheworld.h =================================================================== --- vendor/compiler-rt/dist/lib/sanitizer_common/sanitizer_stoptheworld.h (revision 317221) +++ vendor/compiler-rt/dist/lib/sanitizer_common/sanitizer_stoptheworld.h (revision 317222) @@ -1,76 +1,65 @@ //===-- sanitizer_stoptheworld.h --------------------------------*- C++ -*-===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// // // Defines the StopTheWorld function which suspends the execution of the current // process and runs the user-supplied callback in the same address space. // //===----------------------------------------------------------------------===// #ifndef SANITIZER_STOPTHEWORLD_H #define SANITIZER_STOPTHEWORLD_H #include "sanitizer_internal_defs.h" #include "sanitizer_common.h" namespace __sanitizer { -typedef int SuspendedThreadID; enum PtraceRegistersStatus { REGISTERS_UNAVAILABLE_FATAL = -1, REGISTERS_UNAVAILABLE = 0, REGISTERS_AVAILABLE = 1 }; // Holds the list of suspended threads and provides an interface to dump their // register contexts. class SuspendedThreadsList { public: - SuspendedThreadsList() - : thread_ids_(1024) {} - SuspendedThreadID GetThreadID(uptr index) const { - CHECK_LT(index, thread_ids_.size()); - return thread_ids_[index]; + SuspendedThreadsList() = default; + + // Can't declare pure virtual functions in sanitizer runtimes: + // __cxa_pure_virtual might be unavailable. Use UNIMPLEMENTED() instead. + virtual PtraceRegistersStatus GetRegistersAndSP(uptr index, uptr *buffer, + uptr *sp) const { + UNIMPLEMENTED(); } - PtraceRegistersStatus GetRegistersAndSP(uptr index, uptr *buffer, - uptr *sp) const; + // The buffer in GetRegistersAndSP should be at least this big. - static uptr RegisterCount(); - uptr thread_count() const { return thread_ids_.size(); } - bool Contains(SuspendedThreadID thread_id) const { - for (uptr i = 0; i < thread_ids_.size(); i++) { - if (thread_ids_[i] == thread_id) - return true; - } - return false; - } - void Append(SuspendedThreadID thread_id) { - thread_ids_.push_back(thread_id); - } + virtual uptr RegisterCount() const { UNIMPLEMENTED(); } + virtual uptr ThreadCount() const { UNIMPLEMENTED(); } + virtual tid_t GetThreadID(uptr index) const { UNIMPLEMENTED(); } private: - InternalMmapVector thread_ids_; - // Prohibit copy and assign. SuspendedThreadsList(const SuspendedThreadsList&); void operator=(const SuspendedThreadsList&); }; typedef void (*StopTheWorldCallback)( const SuspendedThreadsList &suspended_threads_list, void *argument); // Suspend all threads in the current process and run the callback on the list // of suspended threads. This function will resume the threads before returning. // The callback should not call any libc functions. The callback must not call // exit() nor _exit() and instead return to the caller. // This function should NOT be called from multiple threads simultaneously. void StopTheWorld(StopTheWorldCallback callback, void *argument); } // namespace __sanitizer #endif // SANITIZER_STOPTHEWORLD_H Index: vendor/compiler-rt/dist/lib/sanitizer_common/sanitizer_stoptheworld_linux_libcdep.cc =================================================================== --- vendor/compiler-rt/dist/lib/sanitizer_common/sanitizer_stoptheworld_linux_libcdep.cc (revision 317221) +++ vendor/compiler-rt/dist/lib/sanitizer_common/sanitizer_stoptheworld_linux_libcdep.cc (revision 317222) @@ -1,536 +1,566 @@ //===-- sanitizer_stoptheworld_linux_libcdep.cc ---------------------------===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// // // See sanitizer_stoptheworld.h for details. // This implementation was inspired by Markus Gutschke's linuxthreads.cc. // //===----------------------------------------------------------------------===// #include "sanitizer_platform.h" #if SANITIZER_LINUX && (defined(__x86_64__) || defined(__mips__) || \ defined(__aarch64__) || defined(__powerpc64__) || \ defined(__s390__) || defined(__i386__) || \ defined(__arm__)) #include "sanitizer_stoptheworld.h" #include "sanitizer_platform_limits_posix.h" #include "sanitizer_atomic.h" #include #include // for CLONE_* definitions #include #include // for PR_* definitions #include // for PTRACE_* definitions #include // for pid_t #include // for iovec #include // for NT_PRSTATUS -#if SANITIZER_ANDROID && defined(__arm__) -# include // for pt_regs -#else -# ifdef __aarch64__ +#if defined(__aarch64__) && !SANITIZER_ANDROID // GLIBC 2.20+ sys/user does not include asm/ptrace.h -# include -# endif -# include // for user_regs_struct -# if SANITIZER_ANDROID && SANITIZER_MIPS -# include // for mips SP register in sys/user.h -# endif +# include #endif +#include // for user_regs_struct +#if SANITIZER_ANDROID && SANITIZER_MIPS +# include // for mips SP register in sys/user.h +#endif #include // for signal-related stuff #ifdef sa_handler # undef sa_handler #endif #ifdef sa_sigaction # undef sa_sigaction #endif #include "sanitizer_common.h" #include "sanitizer_flags.h" #include "sanitizer_libc.h" #include "sanitizer_linux.h" #include "sanitizer_mutex.h" #include "sanitizer_placement_new.h" // This module works by spawning a Linux task which then attaches to every // thread in the caller process with ptrace. This suspends the threads, and // PTRACE_GETREGS can then be used to obtain their register state. The callback // supplied to StopTheWorld() is run in the tracer task while the threads are // suspended. // The tracer task must be placed in a different thread group for ptrace to // work, so it cannot be spawned as a pthread. Instead, we use the low-level // clone() interface (we want to share the address space with the caller // process, so we prefer clone() over fork()). // // We don't use any libc functions, relying instead on direct syscalls. There // are two reasons for this: // 1. calling a library function while threads are suspended could cause a // deadlock, if one of the treads happens to be holding a libc lock; // 2. it's generally not safe to call libc functions from the tracer task, // because clone() does not set up a thread-local storage for it. Any // thread-local variables used by libc will be shared between the tracer task // and the thread which spawned it. namespace __sanitizer { -COMPILER_CHECK(sizeof(SuspendedThreadID) == sizeof(pid_t)); +class SuspendedThreadsListLinux : public SuspendedThreadsList { + public: + SuspendedThreadsListLinux() : thread_ids_(1024) {} + tid_t GetThreadID(uptr index) const; + uptr ThreadCount() const; + bool ContainsTid(tid_t thread_id) const; + void Append(tid_t tid); + + PtraceRegistersStatus GetRegistersAndSP(uptr index, uptr *buffer, + uptr *sp) const; + uptr RegisterCount() const; + + private: + InternalMmapVector thread_ids_; +}; + // Structure for passing arguments into the tracer thread. struct TracerThreadArgument { StopTheWorldCallback callback; void *callback_argument; // The tracer thread waits on this mutex while the parent finishes its // preparations. BlockingMutex mutex; // Tracer thread signals its completion by setting done. atomic_uintptr_t done; uptr parent_pid; }; // This class handles thread suspending/unsuspending in the tracer thread. class ThreadSuspender { public: explicit ThreadSuspender(pid_t pid, TracerThreadArgument *arg) : arg(arg) , pid_(pid) { CHECK_GE(pid, 0); } bool SuspendAllThreads(); void ResumeAllThreads(); void KillAllThreads(); - SuspendedThreadsList &suspended_threads_list() { + SuspendedThreadsListLinux &suspended_threads_list() { return suspended_threads_list_; } TracerThreadArgument *arg; private: - SuspendedThreadsList suspended_threads_list_; + SuspendedThreadsListLinux suspended_threads_list_; pid_t pid_; - bool SuspendThread(SuspendedThreadID thread_id); + bool SuspendThread(tid_t thread_id); }; -bool ThreadSuspender::SuspendThread(SuspendedThreadID tid) { +bool ThreadSuspender::SuspendThread(tid_t tid) { // Are we already attached to this thread? // Currently this check takes linear time, however the number of threads is // usually small. - if (suspended_threads_list_.Contains(tid)) - return false; + if (suspended_threads_list_.ContainsTid(tid)) return false; int pterrno; if (internal_iserror(internal_ptrace(PTRACE_ATTACH, tid, nullptr, nullptr), &pterrno)) { // Either the thread is dead, or something prevented us from attaching. // Log this event and move on. - VReport(1, "Could not attach to thread %d (errno %d).\n", tid, pterrno); + VReport(1, "Could not attach to thread %zu (errno %d).\n", (uptr)tid, + pterrno); return false; } else { - VReport(2, "Attached to thread %d.\n", tid); + VReport(2, "Attached to thread %zu.\n", (uptr)tid); // The thread is not guaranteed to stop before ptrace returns, so we must // wait on it. Note: if the thread receives a signal concurrently, // we can get notification about the signal before notification about stop. // In such case we need to forward the signal to the thread, otherwise // the signal will be missed (as we do PTRACE_DETACH with arg=0) and // any logic relying on signals will break. After forwarding we need to // continue to wait for stopping, because the thread is not stopped yet. // We do ignore delivery of SIGSTOP, because we want to make stop-the-world // as invisible as possible. for (;;) { int status; uptr waitpid_status; HANDLE_EINTR(waitpid_status, internal_waitpid(tid, &status, __WALL)); int wperrno; if (internal_iserror(waitpid_status, &wperrno)) { // Got a ECHILD error. I don't think this situation is possible, but it // doesn't hurt to report it. - VReport(1, "Waiting on thread %d failed, detaching (errno %d).\n", - tid, wperrno); + VReport(1, "Waiting on thread %zu failed, detaching (errno %d).\n", + (uptr)tid, wperrno); internal_ptrace(PTRACE_DETACH, tid, nullptr, nullptr); return false; } if (WIFSTOPPED(status) && WSTOPSIG(status) != SIGSTOP) { internal_ptrace(PTRACE_CONT, tid, nullptr, (void*)(uptr)WSTOPSIG(status)); continue; } break; } suspended_threads_list_.Append(tid); return true; } } void ThreadSuspender::ResumeAllThreads() { - for (uptr i = 0; i < suspended_threads_list_.thread_count(); i++) { + for (uptr i = 0; i < suspended_threads_list_.ThreadCount(); i++) { pid_t tid = suspended_threads_list_.GetThreadID(i); int pterrno; if (!internal_iserror(internal_ptrace(PTRACE_DETACH, tid, nullptr, nullptr), &pterrno)) { VReport(2, "Detached from thread %d.\n", tid); } else { // Either the thread is dead, or we are already detached. // The latter case is possible, for instance, if this function was called // from a signal handler. VReport(1, "Could not detach from thread %d (errno %d).\n", tid, pterrno); } } } void ThreadSuspender::KillAllThreads() { - for (uptr i = 0; i < suspended_threads_list_.thread_count(); i++) + for (uptr i = 0; i < suspended_threads_list_.ThreadCount(); i++) internal_ptrace(PTRACE_KILL, suspended_threads_list_.GetThreadID(i), nullptr, nullptr); } bool ThreadSuspender::SuspendAllThreads() { ThreadLister thread_lister(pid_); bool added_threads; bool first_iteration = true; do { // Run through the directory entries once. added_threads = false; pid_t tid = thread_lister.GetNextTID(); while (tid >= 0) { if (SuspendThread(tid)) added_threads = true; tid = thread_lister.GetNextTID(); } if (thread_lister.error() || (first_iteration && !added_threads)) { // Detach threads and fail. ResumeAllThreads(); return false; } thread_lister.Reset(); first_iteration = false; } while (added_threads); return true; } // Pointer to the ThreadSuspender instance for use in signal handler. static ThreadSuspender *thread_suspender_instance = nullptr; // Synchronous signals that should not be blocked. static const int kSyncSignals[] = { SIGABRT, SIGILL, SIGFPE, SIGSEGV, SIGBUS, SIGXCPU, SIGXFSZ }; static void TracerThreadDieCallback() { // Generally a call to Die() in the tracer thread should be fatal to the // parent process as well, because they share the address space. // This really only works correctly if all the threads are suspended at this // point. So we correctly handle calls to Die() from within the callback, but // not those that happen before or after the callback. Hopefully there aren't // a lot of opportunities for that to happen... ThreadSuspender *inst = thread_suspender_instance; if (inst && stoptheworld_tracer_pid == internal_getpid()) { inst->KillAllThreads(); thread_suspender_instance = nullptr; } } // Signal handler to wake up suspended threads when the tracer thread dies. static void TracerThreadSignalHandler(int signum, void *siginfo, void *uctx) { SignalContext ctx = SignalContext::Create(siginfo, uctx); Printf("Tracer caught signal %d: addr=0x%zx pc=0x%zx sp=0x%zx\n", signum, ctx.addr, ctx.pc, ctx.sp); ThreadSuspender *inst = thread_suspender_instance; if (inst) { if (signum == SIGABRT) inst->KillAllThreads(); else inst->ResumeAllThreads(); RAW_CHECK(RemoveDieCallback(TracerThreadDieCallback)); thread_suspender_instance = nullptr; atomic_store(&inst->arg->done, 1, memory_order_relaxed); } internal__exit((signum == SIGABRT) ? 1 : 2); } // Size of alternative stack for signal handlers in the tracer thread. static const int kHandlerStackSize = 4096; // This function will be run as a cloned task. static int TracerThread(void* argument) { TracerThreadArgument *tracer_thread_argument = (TracerThreadArgument *)argument; internal_prctl(PR_SET_PDEATHSIG, SIGKILL, 0, 0, 0); // Check if parent is already dead. if (internal_getppid() != tracer_thread_argument->parent_pid) internal__exit(4); // Wait for the parent thread to finish preparations. tracer_thread_argument->mutex.Lock(); tracer_thread_argument->mutex.Unlock(); RAW_CHECK(AddDieCallback(TracerThreadDieCallback)); ThreadSuspender thread_suspender(internal_getppid(), tracer_thread_argument); // Global pointer for the signal handler. thread_suspender_instance = &thread_suspender; // Alternate stack for signal handling. InternalScopedBuffer handler_stack_memory(kHandlerStackSize); struct sigaltstack handler_stack; internal_memset(&handler_stack, 0, sizeof(handler_stack)); handler_stack.ss_sp = handler_stack_memory.data(); handler_stack.ss_size = kHandlerStackSize; internal_sigaltstack(&handler_stack, nullptr); // Install our handler for synchronous signals. Other signals should be // blocked by the mask we inherited from the parent thread. for (uptr i = 0; i < ARRAY_SIZE(kSyncSignals); i++) { __sanitizer_sigaction act; internal_memset(&act, 0, sizeof(act)); act.sigaction = TracerThreadSignalHandler; act.sa_flags = SA_ONSTACK | SA_SIGINFO; internal_sigaction_norestorer(kSyncSignals[i], &act, 0); } int exit_code = 0; if (!thread_suspender.SuspendAllThreads()) { VReport(1, "Failed suspending threads.\n"); exit_code = 3; } else { tracer_thread_argument->callback(thread_suspender.suspended_threads_list(), tracer_thread_argument->callback_argument); thread_suspender.ResumeAllThreads(); exit_code = 0; } RAW_CHECK(RemoveDieCallback(TracerThreadDieCallback)); thread_suspender_instance = nullptr; atomic_store(&tracer_thread_argument->done, 1, memory_order_relaxed); return exit_code; } class ScopedStackSpaceWithGuard { public: explicit ScopedStackSpaceWithGuard(uptr stack_size) { stack_size_ = stack_size; guard_size_ = GetPageSizeCached(); // FIXME: Omitting MAP_STACK here works in current kernels but might break // in the future. guard_start_ = (uptr)MmapOrDie(stack_size_ + guard_size_, "ScopedStackWithGuard"); CHECK(MprotectNoAccess((uptr)guard_start_, guard_size_)); } ~ScopedStackSpaceWithGuard() { UnmapOrDie((void *)guard_start_, stack_size_ + guard_size_); } void *Bottom() const { return (void *)(guard_start_ + stack_size_ + guard_size_); } private: uptr stack_size_; uptr guard_size_; uptr guard_start_; }; // We have a limitation on the stack frame size, so some stuff had to be moved // into globals. static __sanitizer_sigset_t blocked_sigset; static __sanitizer_sigset_t old_sigset; class StopTheWorldScope { public: StopTheWorldScope() { // Make this process dumpable. Processes that are not dumpable cannot be // attached to. process_was_dumpable_ = internal_prctl(PR_GET_DUMPABLE, 0, 0, 0, 0); if (!process_was_dumpable_) internal_prctl(PR_SET_DUMPABLE, 1, 0, 0, 0); } ~StopTheWorldScope() { // Restore the dumpable flag. if (!process_was_dumpable_) internal_prctl(PR_SET_DUMPABLE, 0, 0, 0, 0); } private: int process_was_dumpable_; }; // When sanitizer output is being redirected to file (i.e. by using log_path), // the tracer should write to the parent's log instead of trying to open a new // file. Alert the logging code to the fact that we have a tracer. struct ScopedSetTracerPID { explicit ScopedSetTracerPID(uptr tracer_pid) { stoptheworld_tracer_pid = tracer_pid; stoptheworld_tracer_ppid = internal_getpid(); } ~ScopedSetTracerPID() { stoptheworld_tracer_pid = 0; stoptheworld_tracer_ppid = 0; } }; void StopTheWorld(StopTheWorldCallback callback, void *argument) { StopTheWorldScope in_stoptheworld; // Prepare the arguments for TracerThread. struct TracerThreadArgument tracer_thread_argument; tracer_thread_argument.callback = callback; tracer_thread_argument.callback_argument = argument; tracer_thread_argument.parent_pid = internal_getpid(); atomic_store(&tracer_thread_argument.done, 0, memory_order_relaxed); const uptr kTracerStackSize = 2 * 1024 * 1024; ScopedStackSpaceWithGuard tracer_stack(kTracerStackSize); // Block the execution of TracerThread until after we have set ptrace // permissions. tracer_thread_argument.mutex.Lock(); // Signal handling story. // We don't want async signals to be delivered to the tracer thread, // so we block all async signals before creating the thread. An async signal // handler can temporary modify errno, which is shared with this thread. // We ought to use pthread_sigmask here, because sigprocmask has undefined // behavior in multithreaded programs. However, on linux sigprocmask is // equivalent to pthread_sigmask with the exception that pthread_sigmask // does not allow to block some signals used internally in pthread // implementation. We are fine with blocking them here, we are really not // going to pthread_cancel the thread. // The tracer thread should not raise any synchronous signals. But in case it // does, we setup a special handler for sync signals that properly kills the // parent as well. Note: we don't pass CLONE_SIGHAND to clone, so handlers // in the tracer thread won't interfere with user program. Double note: if a // user does something along the lines of 'kill -11 pid', that can kill the // process even if user setup own handler for SEGV. // Thing to watch out for: this code should not change behavior of user code // in any observable way. In particular it should not override user signal // handlers. internal_sigfillset(&blocked_sigset); for (uptr i = 0; i < ARRAY_SIZE(kSyncSignals); i++) internal_sigdelset(&blocked_sigset, kSyncSignals[i]); int rv = internal_sigprocmask(SIG_BLOCK, &blocked_sigset, &old_sigset); CHECK_EQ(rv, 0); uptr tracer_pid = internal_clone( TracerThread, tracer_stack.Bottom(), CLONE_VM | CLONE_FS | CLONE_FILES | CLONE_UNTRACED, &tracer_thread_argument, nullptr /* parent_tidptr */, nullptr /* newtls */, nullptr /* child_tidptr */); internal_sigprocmask(SIG_SETMASK, &old_sigset, 0); int local_errno = 0; if (internal_iserror(tracer_pid, &local_errno)) { VReport(1, "Failed spawning a tracer thread (errno %d).\n", local_errno); tracer_thread_argument.mutex.Unlock(); } else { ScopedSetTracerPID scoped_set_tracer_pid(tracer_pid); // On some systems we have to explicitly declare that we want to be traced // by the tracer thread. #ifdef PR_SET_PTRACER internal_prctl(PR_SET_PTRACER, tracer_pid, 0, 0, 0); #endif // Allow the tracer thread to start. tracer_thread_argument.mutex.Unlock(); // NOTE: errno is shared between this thread and the tracer thread. // internal_waitpid() may call syscall() which can access/spoil errno, // so we can't call it now. Instead we for the tracer thread to finish using // the spin loop below. Man page for sched_yield() says "In the Linux // implementation, sched_yield() always succeeds", so let's hope it does not // spoil errno. Note that this spin loop runs only for brief periods before // the tracer thread has suspended us and when it starts unblocking threads. while (atomic_load(&tracer_thread_argument.done, memory_order_relaxed) == 0) sched_yield(); // Now the tracer thread is about to exit and does not touch errno, // wait for it. for (;;) { uptr waitpid_status = internal_waitpid(tracer_pid, nullptr, __WALL); if (!internal_iserror(waitpid_status, &local_errno)) break; if (local_errno == EINTR) continue; VReport(1, "Waiting on the tracer thread failed (errno %d).\n", local_errno); break; } } } // Platform-specific methods from SuspendedThreadsList. #if SANITIZER_ANDROID && defined(__arm__) typedef pt_regs regs_struct; #define REG_SP ARM_sp #elif SANITIZER_LINUX && defined(__arm__) typedef user_regs regs_struct; #define REG_SP uregs[13] #elif defined(__i386__) || defined(__x86_64__) typedef user_regs_struct regs_struct; #if defined(__i386__) #define REG_SP esp #else #define REG_SP rsp #endif #elif defined(__powerpc__) || defined(__powerpc64__) typedef pt_regs regs_struct; #define REG_SP gpr[PT_R1] #elif defined(__mips__) typedef struct user regs_struct; # if SANITIZER_ANDROID # define REG_SP regs[EF_R29] # else # define REG_SP regs[EF_REG29] # endif #elif defined(__aarch64__) typedef struct user_pt_regs regs_struct; #define REG_SP sp #define ARCH_IOVEC_FOR_GETREGSET #elif defined(__s390__) typedef _user_regs_struct regs_struct; #define REG_SP gprs[15] #define ARCH_IOVEC_FOR_GETREGSET #else #error "Unsupported architecture" #endif // SANITIZER_ANDROID && defined(__arm__) -PtraceRegistersStatus SuspendedThreadsList::GetRegistersAndSP(uptr index, - uptr *buffer, - uptr *sp) const { +tid_t SuspendedThreadsListLinux::GetThreadID(uptr index) const { + CHECK_LT(index, thread_ids_.size()); + return thread_ids_[index]; +} + +uptr SuspendedThreadsListLinux::ThreadCount() const { + return thread_ids_.size(); +} + +bool SuspendedThreadsListLinux::ContainsTid(tid_t thread_id) const { + for (uptr i = 0; i < thread_ids_.size(); i++) { + if (thread_ids_[i] == thread_id) return true; + } + return false; +} + +void SuspendedThreadsListLinux::Append(tid_t tid) { + thread_ids_.push_back(tid); +} + +PtraceRegistersStatus SuspendedThreadsListLinux::GetRegistersAndSP( + uptr index, uptr *buffer, uptr *sp) const { pid_t tid = GetThreadID(index); regs_struct regs; int pterrno; #ifdef ARCH_IOVEC_FOR_GETREGSET struct iovec regset_io; regset_io.iov_base = ®s; regset_io.iov_len = sizeof(regs_struct); bool isErr = internal_iserror(internal_ptrace(PTRACE_GETREGSET, tid, (void*)NT_PRSTATUS, (void*)®set_io), &pterrno); #else bool isErr = internal_iserror(internal_ptrace(PTRACE_GETREGS, tid, nullptr, ®s), &pterrno); #endif if (isErr) { VReport(1, "Could not get registers from thread %d (errno %d).\n", tid, pterrno); // ESRCH means that the given thread is not suspended or already dead. // Therefore it's unsafe to inspect its data (e.g. walk through stack) and // we should notify caller about this. return pterrno == ESRCH ? REGISTERS_UNAVAILABLE_FATAL : REGISTERS_UNAVAILABLE; } *sp = regs.REG_SP; internal_memcpy(buffer, ®s, sizeof(regs)); return REGISTERS_AVAILABLE; } -uptr SuspendedThreadsList::RegisterCount() { +uptr SuspendedThreadsListLinux::RegisterCount() const { return sizeof(regs_struct) / sizeof(uptr); } } // namespace __sanitizer #endif // SANITIZER_LINUX && (defined(__x86_64__) || defined(__mips__) // || defined(__aarch64__) || defined(__powerpc64__) // || defined(__s390__) || defined(__i386__) || defined(__arm__) Index: vendor/compiler-rt/dist/lib/sanitizer_common/sanitizer_stoptheworld_mac.cc =================================================================== --- vendor/compiler-rt/dist/lib/sanitizer_common/sanitizer_stoptheworld_mac.cc (revision 317221) +++ vendor/compiler-rt/dist/lib/sanitizer_common/sanitizer_stoptheworld_mac.cc (revision 317222) @@ -1,40 +1,182 @@ //===-- sanitizer_stoptheworld_mac.cc -------------------------------------===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// // // See sanitizer_stoptheworld.h for details. // //===----------------------------------------------------------------------===// #include "sanitizer_platform.h" #if SANITIZER_MAC && (defined(__x86_64__) || defined(__aarch64__) || \ - defined(__mips64) || defined(__i386)) + defined(__i386)) +#include +#include +#include + #include "sanitizer_stoptheworld.h" namespace __sanitizer { +typedef struct { + tid_t tid; + thread_t thread; +} SuspendedThreadInfo; + +class SuspendedThreadsListMac : public SuspendedThreadsList { + public: + SuspendedThreadsListMac() : threads_(1024) {} + + tid_t GetThreadID(uptr index) const; + thread_t GetThread(uptr index) const; + uptr ThreadCount() const; + bool ContainsThread(thread_t thread) const; + void Append(thread_t thread); + + PtraceRegistersStatus GetRegistersAndSP(uptr index, uptr *buffer, + uptr *sp) const; + uptr RegisterCount() const; + + private: + InternalMmapVector threads_; +}; + +struct RunThreadArgs { + StopTheWorldCallback callback; + void *argument; +}; + +void RunThread(void *arg) { + struct RunThreadArgs *run_args = (struct RunThreadArgs *)arg; + SuspendedThreadsListMac suspended_threads_list; + + mach_port_t task; + kern_return_t err = task_for_pid(mach_task_self(), internal_getpid(), &task); + if (err != KERN_SUCCESS) { + VReport(1, "Getting task from pid failed (errno %d).\n", err); + return; + } + + thread_array_t threads; + mach_msg_type_number_t num_threads; + + err = task_threads(task, &threads, &num_threads); + if (err != KERN_SUCCESS) { + VReport(1, "Failed to get threads for task (errno %d).\n", err); + return; + } + + thread_t thread_self = mach_thread_self(); + for (unsigned int i = 0; i < num_threads; ++i) { + if (threads[i] == thread_self) continue; + + thread_suspend(threads[i]); + suspended_threads_list.Append(threads[i]); + } + + run_args->callback(suspended_threads_list, run_args->argument); + + uptr num_suspended = suspended_threads_list.ThreadCount(); + for (unsigned int i = 0; i < num_suspended; ++i) { + thread_resume(suspended_threads_list.GetThread(i)); + } +} + void StopTheWorld(StopTheWorldCallback callback, void *argument) { - CHECK(0 && "unimplemented"); + struct RunThreadArgs arg = {callback, argument}; + pthread_t run_thread = (pthread_t)internal_start_thread(RunThread, &arg); + internal_join_thread(run_thread); } -PtraceRegistersStatus SuspendedThreadsList::GetRegistersAndSP(uptr index, - uptr *buffer, - uptr *sp) const { - CHECK(0 && "unimplemented"); - return REGISTERS_UNAVAILABLE_FATAL; +#if defined(__x86_64__) +typedef x86_thread_state64_t regs_struct; + +#define SP_REG __rsp + +#elif defined(__aarch64__) +typedef arm_thread_state64_t regs_struct; + +# if __DARWIN_UNIX03 +# define SP_REG __sp +# else +# define SP_REG sp +# endif + +#elif defined(__i386) +typedef x86_thread_state32_t regs_struct; + +#define SP_REG __esp + +#else +#error "Unsupported architecture" +#endif + +tid_t SuspendedThreadsListMac::GetThreadID(uptr index) const { + CHECK_LT(index, threads_.size()); + return threads_[index].tid; } -uptr SuspendedThreadsList::RegisterCount() { - CHECK(0 && "unimplemented"); - return 0; +thread_t SuspendedThreadsListMac::GetThread(uptr index) const { + CHECK_LT(index, threads_.size()); + return threads_[index].thread; } + +uptr SuspendedThreadsListMac::ThreadCount() const { + return threads_.size(); +} + +bool SuspendedThreadsListMac::ContainsThread(thread_t thread) const { + for (uptr i = 0; i < threads_.size(); i++) { + if (threads_[i].thread == thread) return true; + } + return false; +} + +void SuspendedThreadsListMac::Append(thread_t thread) { + thread_identifier_info_data_t info; + mach_msg_type_number_t info_count = THREAD_IDENTIFIER_INFO_COUNT; + kern_return_t err = thread_info(thread, THREAD_IDENTIFIER_INFO, + (thread_info_t)&info, &info_count); + if (err != KERN_SUCCESS) { + VReport(1, "Error - unable to get thread ident for a thread\n"); + return; + } + threads_.push_back({info.thread_id, thread}); +} + +PtraceRegistersStatus SuspendedThreadsListMac::GetRegistersAndSP( + uptr index, uptr *buffer, uptr *sp) const { + thread_t thread = GetThread(index); + regs_struct regs; + int err; + mach_msg_type_number_t reg_count = MACHINE_THREAD_STATE_COUNT; + err = thread_get_state(thread, MACHINE_THREAD_STATE, (thread_state_t)®s, + ®_count); + if (err != KERN_SUCCESS) { + VReport(1, "Error - unable to get registers for a thread\n"); + // KERN_INVALID_ARGUMENT indicates that either the flavor is invalid, + // or the thread does not exist. The other possible error case, + // MIG_ARRAY_TOO_LARGE, means that the state is too large, but it's + // still safe to proceed. + return err == KERN_INVALID_ARGUMENT ? REGISTERS_UNAVAILABLE_FATAL + : REGISTERS_UNAVAILABLE; + } + + internal_memcpy(buffer, ®s, sizeof(regs)); + *sp = regs.SP_REG; + + return REGISTERS_AVAILABLE; +} + +uptr SuspendedThreadsListMac::RegisterCount() const { + return MACHINE_THREAD_STATE_COUNT; +} } // namespace __sanitizer #endif // SANITIZER_MAC && (defined(__x86_64__) || defined(__aarch64__)) || - // defined(__mips64) || defined(__i386)) + // defined(__i386)) Index: vendor/compiler-rt/dist/lib/sanitizer_common/sanitizer_thread_registry.cc =================================================================== --- vendor/compiler-rt/dist/lib/sanitizer_common/sanitizer_thread_registry.cc (revision 317221) +++ vendor/compiler-rt/dist/lib/sanitizer_common/sanitizer_thread_registry.cc (revision 317222) @@ -1,305 +1,306 @@ //===-- sanitizer_thread_registry.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 shared between sanitizer tools. // // General thread bookkeeping functionality. //===----------------------------------------------------------------------===// #include "sanitizer_thread_registry.h" namespace __sanitizer { ThreadContextBase::ThreadContextBase(u32 tid) : tid(tid), unique_id(0), reuse_count(), os_id(0), user_id(0), status(ThreadStatusInvalid), detached(false), workerthread(false), parent_tid(0), next(0) { name[0] = '\0'; } ThreadContextBase::~ThreadContextBase() { // ThreadContextBase should never be deleted. CHECK(0); } void ThreadContextBase::SetName(const char *new_name) { name[0] = '\0'; if (new_name) { internal_strncpy(name, new_name, sizeof(name)); name[sizeof(name) - 1] = '\0'; } } void ThreadContextBase::SetDead() { CHECK(status == ThreadStatusRunning || status == ThreadStatusFinished); status = ThreadStatusDead; user_id = 0; OnDead(); } void ThreadContextBase::SetJoined(void *arg) { // FIXME(dvyukov): print message and continue (it's user error). CHECK_EQ(false, detached); CHECK_EQ(ThreadStatusFinished, status); status = ThreadStatusDead; user_id = 0; OnJoined(arg); } void ThreadContextBase::SetFinished() { if (!detached) status = ThreadStatusFinished; OnFinished(); } -void ThreadContextBase::SetStarted(uptr _os_id, bool _workerthread, void *arg) { +void ThreadContextBase::SetStarted(tid_t _os_id, bool _workerthread, + void *arg) { status = ThreadStatusRunning; os_id = _os_id; workerthread = _workerthread; OnStarted(arg); } void ThreadContextBase::SetCreated(uptr _user_id, u64 _unique_id, bool _detached, u32 _parent_tid, void *arg) { status = ThreadStatusCreated; user_id = _user_id; unique_id = _unique_id; detached = _detached; // Parent tid makes no sense for the main thread. if (tid != 0) parent_tid = _parent_tid; OnCreated(arg); } void ThreadContextBase::Reset() { status = ThreadStatusInvalid; SetName(0); OnReset(); } // ThreadRegistry implementation. const u32 ThreadRegistry::kUnknownTid = ~0U; ThreadRegistry::ThreadRegistry(ThreadContextFactory factory, u32 max_threads, u32 thread_quarantine_size, u32 max_reuse) : context_factory_(factory), max_threads_(max_threads), thread_quarantine_size_(thread_quarantine_size), max_reuse_(max_reuse), mtx_(), n_contexts_(0), total_threads_(0), alive_threads_(0), max_alive_threads_(0), running_threads_(0) { threads_ = (ThreadContextBase **)MmapOrDie(max_threads_ * sizeof(threads_[0]), "ThreadRegistry"); dead_threads_.clear(); invalid_threads_.clear(); } void ThreadRegistry::GetNumberOfThreads(uptr *total, uptr *running, uptr *alive) { BlockingMutexLock l(&mtx_); if (total) *total = n_contexts_; if (running) *running = running_threads_; if (alive) *alive = alive_threads_; } uptr ThreadRegistry::GetMaxAliveThreads() { BlockingMutexLock l(&mtx_); return max_alive_threads_; } u32 ThreadRegistry::CreateThread(uptr user_id, bool detached, u32 parent_tid, void *arg) { BlockingMutexLock l(&mtx_); u32 tid = kUnknownTid; ThreadContextBase *tctx = QuarantinePop(); if (tctx) { tid = tctx->tid; } else if (n_contexts_ < max_threads_) { // Allocate new thread context and tid. tid = n_contexts_++; tctx = context_factory_(tid); threads_[tid] = tctx; } else { #if !SANITIZER_GO Report("%s: Thread limit (%u threads) exceeded. Dying.\n", SanitizerToolName, max_threads_); #else Printf("race: limit on %u simultaneously alive goroutines is exceeded," " dying\n", max_threads_); #endif Die(); } CHECK_NE(tctx, 0); CHECK_NE(tid, kUnknownTid); CHECK_LT(tid, max_threads_); CHECK_EQ(tctx->status, ThreadStatusInvalid); alive_threads_++; if (max_alive_threads_ < alive_threads_) { max_alive_threads_++; CHECK_EQ(alive_threads_, max_alive_threads_); } tctx->SetCreated(user_id, total_threads_++, detached, parent_tid, arg); return tid; } void ThreadRegistry::RunCallbackForEachThreadLocked(ThreadCallback cb, void *arg) { CheckLocked(); for (u32 tid = 0; tid < n_contexts_; tid++) { ThreadContextBase *tctx = threads_[tid]; if (tctx == 0) continue; cb(tctx, arg); } } u32 ThreadRegistry::FindThread(FindThreadCallback cb, void *arg) { BlockingMutexLock l(&mtx_); for (u32 tid = 0; tid < n_contexts_; tid++) { ThreadContextBase *tctx = threads_[tid]; if (tctx != 0 && cb(tctx, arg)) return tctx->tid; } return kUnknownTid; } ThreadContextBase * ThreadRegistry::FindThreadContextLocked(FindThreadCallback cb, void *arg) { CheckLocked(); for (u32 tid = 0; tid < n_contexts_; tid++) { ThreadContextBase *tctx = threads_[tid]; if (tctx != 0 && cb(tctx, arg)) return tctx; } return 0; } static bool FindThreadContextByOsIdCallback(ThreadContextBase *tctx, void *arg) { return (tctx->os_id == (uptr)arg && tctx->status != ThreadStatusInvalid && tctx->status != ThreadStatusDead); } -ThreadContextBase *ThreadRegistry::FindThreadContextByOsIDLocked(uptr os_id) { +ThreadContextBase *ThreadRegistry::FindThreadContextByOsIDLocked(tid_t os_id) { return FindThreadContextLocked(FindThreadContextByOsIdCallback, (void *)os_id); } void ThreadRegistry::SetThreadName(u32 tid, const char *name) { BlockingMutexLock l(&mtx_); CHECK_LT(tid, n_contexts_); ThreadContextBase *tctx = threads_[tid]; CHECK_NE(tctx, 0); CHECK_EQ(ThreadStatusRunning, tctx->status); tctx->SetName(name); } void ThreadRegistry::SetThreadNameByUserId(uptr user_id, const char *name) { BlockingMutexLock l(&mtx_); for (u32 tid = 0; tid < n_contexts_; tid++) { ThreadContextBase *tctx = threads_[tid]; if (tctx != 0 && tctx->user_id == user_id && tctx->status != ThreadStatusInvalid) { tctx->SetName(name); return; } } } void ThreadRegistry::DetachThread(u32 tid, void *arg) { BlockingMutexLock l(&mtx_); CHECK_LT(tid, n_contexts_); ThreadContextBase *tctx = threads_[tid]; CHECK_NE(tctx, 0); if (tctx->status == ThreadStatusInvalid) { Report("%s: Detach of non-existent thread\n", SanitizerToolName); return; } tctx->OnDetached(arg); if (tctx->status == ThreadStatusFinished) { tctx->SetDead(); QuarantinePush(tctx); } else { tctx->detached = true; } } void ThreadRegistry::JoinThread(u32 tid, void *arg) { BlockingMutexLock l(&mtx_); CHECK_LT(tid, n_contexts_); ThreadContextBase *tctx = threads_[tid]; CHECK_NE(tctx, 0); if (tctx->status == ThreadStatusInvalid) { Report("%s: Join of non-existent thread\n", SanitizerToolName); return; } tctx->SetJoined(arg); QuarantinePush(tctx); } void ThreadRegistry::FinishThread(u32 tid) { BlockingMutexLock l(&mtx_); CHECK_GT(alive_threads_, 0); alive_threads_--; CHECK_GT(running_threads_, 0); running_threads_--; CHECK_LT(tid, n_contexts_); ThreadContextBase *tctx = threads_[tid]; CHECK_NE(tctx, 0); CHECK_EQ(ThreadStatusRunning, tctx->status); tctx->SetFinished(); if (tctx->detached) { tctx->SetDead(); QuarantinePush(tctx); } } -void ThreadRegistry::StartThread(u32 tid, uptr os_id, bool workerthread, +void ThreadRegistry::StartThread(u32 tid, tid_t os_id, bool workerthread, void *arg) { BlockingMutexLock l(&mtx_); running_threads_++; CHECK_LT(tid, n_contexts_); ThreadContextBase *tctx = threads_[tid]; CHECK_NE(tctx, 0); CHECK_EQ(ThreadStatusCreated, tctx->status); tctx->SetStarted(os_id, workerthread, arg); } void ThreadRegistry::QuarantinePush(ThreadContextBase *tctx) { if (tctx->tid == 0) return; // Don't reuse the main thread. It's a special snowflake. dead_threads_.push_back(tctx); if (dead_threads_.size() <= thread_quarantine_size_) return; tctx = dead_threads_.front(); dead_threads_.pop_front(); CHECK_EQ(tctx->status, ThreadStatusDead); tctx->Reset(); tctx->reuse_count++; if (max_reuse_ > 0 && tctx->reuse_count >= max_reuse_) return; invalid_threads_.push_back(tctx); } ThreadContextBase *ThreadRegistry::QuarantinePop() { if (invalid_threads_.size() == 0) return 0; ThreadContextBase *tctx = invalid_threads_.front(); invalid_threads_.pop_front(); return tctx; } } // namespace __sanitizer Index: vendor/compiler-rt/dist/lib/sanitizer_common/sanitizer_thread_registry.h =================================================================== --- vendor/compiler-rt/dist/lib/sanitizer_common/sanitizer_thread_registry.h (revision 317221) +++ vendor/compiler-rt/dist/lib/sanitizer_common/sanitizer_thread_registry.h (revision 317222) @@ -1,149 +1,149 @@ //===-- sanitizer_thread_registry.h -----------------------------*- C++ -*-===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// // // This file is shared between sanitizer tools. // // General thread bookkeeping functionality. //===----------------------------------------------------------------------===// #ifndef SANITIZER_THREAD_REGISTRY_H #define SANITIZER_THREAD_REGISTRY_H #include "sanitizer_common.h" #include "sanitizer_list.h" #include "sanitizer_mutex.h" namespace __sanitizer { enum ThreadStatus { ThreadStatusInvalid, // Non-existent thread, data is invalid. ThreadStatusCreated, // Created but not yet running. ThreadStatusRunning, // The thread is currently running. ThreadStatusFinished, // Joinable thread is finished but not yet joined. ThreadStatusDead // Joined, but some info is still available. }; // Generic thread context. Specific sanitizer tools may inherit from it. // If thread is dead, context may optionally be reused for a new thread. class ThreadContextBase { public: explicit ThreadContextBase(u32 tid); ~ThreadContextBase(); // Should never be called. const u32 tid; // Thread ID. Main thread should have tid = 0. u64 unique_id; // Unique thread ID. u32 reuse_count; // Number of times this tid was reused. - uptr os_id; // PID (used for reporting). + tid_t os_id; // PID (used for reporting). uptr user_id; // Some opaque user thread id (e.g. pthread_t). char name[64]; // As annotated by user. ThreadStatus status; bool detached; bool workerthread; u32 parent_tid; ThreadContextBase *next; // For storing thread contexts in a list. void SetName(const char *new_name); void SetDead(); void SetJoined(void *arg); void SetFinished(); - void SetStarted(uptr _os_id, bool _workerthread, void *arg); + void SetStarted(tid_t _os_id, bool _workerthread, void *arg); void SetCreated(uptr _user_id, u64 _unique_id, bool _detached, u32 _parent_tid, void *arg); void Reset(); // The following methods may be overriden by subclasses. // Some of them take opaque arg that may be optionally be used // by subclasses. virtual void OnDead() {} virtual void OnJoined(void *arg) {} virtual void OnFinished() {} virtual void OnStarted(void *arg) {} virtual void OnCreated(void *arg) {} virtual void OnReset() {} virtual void OnDetached(void *arg) {} }; typedef ThreadContextBase* (*ThreadContextFactory)(u32 tid); class ThreadRegistry { public: static const u32 kUnknownTid; ThreadRegistry(ThreadContextFactory factory, u32 max_threads, u32 thread_quarantine_size, u32 max_reuse = 0); void GetNumberOfThreads(uptr *total = nullptr, uptr *running = nullptr, uptr *alive = nullptr); uptr GetMaxAliveThreads(); void Lock() { mtx_.Lock(); } void CheckLocked() { mtx_.CheckLocked(); } void Unlock() { mtx_.Unlock(); } // Should be guarded by ThreadRegistryLock. ThreadContextBase *GetThreadLocked(u32 tid) { DCHECK_LT(tid, n_contexts_); return threads_[tid]; } u32 CreateThread(uptr user_id, bool detached, u32 parent_tid, void *arg); typedef void (*ThreadCallback)(ThreadContextBase *tctx, void *arg); // Invokes callback with a specified arg for each thread context. // Should be guarded by ThreadRegistryLock. void RunCallbackForEachThreadLocked(ThreadCallback cb, void *arg); typedef bool (*FindThreadCallback)(ThreadContextBase *tctx, void *arg); // Finds a thread using the provided callback. Returns kUnknownTid if no // thread is found. u32 FindThread(FindThreadCallback cb, void *arg); // Should be guarded by ThreadRegistryLock. Return 0 if no thread // is found. ThreadContextBase *FindThreadContextLocked(FindThreadCallback cb, void *arg); - ThreadContextBase *FindThreadContextByOsIDLocked(uptr os_id); + ThreadContextBase *FindThreadContextByOsIDLocked(tid_t os_id); void SetThreadName(u32 tid, const char *name); void SetThreadNameByUserId(uptr user_id, const char *name); void DetachThread(u32 tid, void *arg); void JoinThread(u32 tid, void *arg); void FinishThread(u32 tid); - void StartThread(u32 tid, uptr os_id, bool workerthread, void *arg); + void StartThread(u32 tid, tid_t os_id, bool workerthread, void *arg); private: const ThreadContextFactory context_factory_; const u32 max_threads_; const u32 thread_quarantine_size_; const u32 max_reuse_; BlockingMutex mtx_; u32 n_contexts_; // Number of created thread contexts, // at most max_threads_. u64 total_threads_; // Total number of created threads. May be greater than // max_threads_ if contexts were reused. uptr alive_threads_; // Created or running. uptr max_alive_threads_; uptr running_threads_; ThreadContextBase **threads_; // Array of thread contexts is leaked. IntrusiveList dead_threads_; IntrusiveList invalid_threads_; void QuarantinePush(ThreadContextBase *tctx); ThreadContextBase *QuarantinePop(); }; typedef GenericScopedLock ThreadRegistryLock; } // namespace __sanitizer #endif // SANITIZER_THREAD_REGISTRY_H Index: vendor/compiler-rt/dist/lib/sanitizer_common/sanitizer_win.cc =================================================================== --- vendor/compiler-rt/dist/lib/sanitizer_common/sanitizer_win.cc (revision 317221) +++ vendor/compiler-rt/dist/lib/sanitizer_common/sanitizer_win.cc (revision 317222) @@ -1,999 +1,1000 @@ //===-- sanitizer_win.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 shared between AddressSanitizer and ThreadSanitizer // run-time libraries and implements windows-specific functions from // sanitizer_libc.h. //===----------------------------------------------------------------------===// #include "sanitizer_platform.h" #if SANITIZER_WINDOWS #define WIN32_LEAN_AND_MEAN #define NOGDI #include #include #include #include #include "sanitizer_common.h" #include "sanitizer_dbghelp.h" #include "sanitizer_libc.h" #include "sanitizer_mutex.h" #include "sanitizer_placement_new.h" #include "sanitizer_procmaps.h" #include "sanitizer_stacktrace.h" #include "sanitizer_symbolizer.h" #include "sanitizer_win_defs.h" // A macro to tell the compiler that this part of the code cannot be reached, // if the compiler supports this feature. Since we're using this in // code that is called when terminating the process, the expansion of the // macro should not terminate the process to avoid infinite recursion. #if defined(__clang__) # define BUILTIN_UNREACHABLE() __builtin_unreachable() #elif defined(__GNUC__) && \ (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 5)) # define BUILTIN_UNREACHABLE() __builtin_unreachable() #elif defined(_MSC_VER) # define BUILTIN_UNREACHABLE() __assume(0) #else # define BUILTIN_UNREACHABLE() #endif namespace __sanitizer { #include "sanitizer_syscall_generic.inc" // --------------------- sanitizer_common.h uptr GetPageSize() { SYSTEM_INFO si; GetSystemInfo(&si); return si.dwPageSize; } uptr GetMmapGranularity() { SYSTEM_INFO si; GetSystemInfo(&si); return si.dwAllocationGranularity; } uptr GetMaxVirtualAddress() { SYSTEM_INFO si; GetSystemInfo(&si); return (uptr)si.lpMaximumApplicationAddress; } bool FileExists(const char *filename) { return ::GetFileAttributesA(filename) != INVALID_FILE_ATTRIBUTES; } uptr internal_getpid() { return GetProcessId(GetCurrentProcess()); } // In contrast to POSIX, on Windows GetCurrentThreadId() // returns a system-unique identifier. -uptr GetTid() { +tid_t GetTid() { return GetCurrentThreadId(); } uptr GetThreadSelf() { return GetTid(); } #if !SANITIZER_GO void GetThreadStackTopAndBottom(bool at_initialization, uptr *stack_top, uptr *stack_bottom) { CHECK(stack_top); CHECK(stack_bottom); MEMORY_BASIC_INFORMATION mbi; CHECK_NE(VirtualQuery(&mbi /* on stack */, &mbi, sizeof(mbi)), 0); // FIXME: is it possible for the stack to not be a single allocation? // Are these values what ASan expects to get (reserved, not committed; // including stack guard page) ? *stack_top = (uptr)mbi.BaseAddress + mbi.RegionSize; *stack_bottom = (uptr)mbi.AllocationBase; } #endif // #if !SANITIZER_GO void *MmapOrDie(uptr size, const char *mem_type, bool raw_report) { void *rv = VirtualAlloc(0, size, MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE); if (rv == 0) ReportMmapFailureAndDie(size, mem_type, "allocate", GetLastError(), raw_report); return rv; } void UnmapOrDie(void *addr, uptr size) { if (!size || !addr) return; MEMORY_BASIC_INFORMATION mbi; CHECK(VirtualQuery(addr, &mbi, sizeof(mbi))); // MEM_RELEASE can only be used to unmap whole regions previously mapped with // VirtualAlloc. So we first try MEM_RELEASE since it is better, and if that // fails try MEM_DECOMMIT. if (VirtualFree(addr, 0, MEM_RELEASE) == 0) { if (VirtualFree(addr, size, MEM_DECOMMIT) == 0) { Report("ERROR: %s failed to " "deallocate 0x%zx (%zd) bytes at address %p (error code: %d)\n", SanitizerToolName, size, size, addr, GetLastError()); CHECK("unable to unmap" && 0); } } } // We want to map a chunk of address space aligned to 'alignment'. void *MmapAlignedOrDie(uptr size, uptr alignment, const char *mem_type) { CHECK(IsPowerOfTwo(size)); CHECK(IsPowerOfTwo(alignment)); // Windows will align our allocations to at least 64K. alignment = Max(alignment, GetMmapGranularity()); uptr mapped_addr = (uptr)VirtualAlloc(0, size, MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE); if (!mapped_addr) ReportMmapFailureAndDie(size, mem_type, "allocate aligned", GetLastError()); // If we got it right on the first try, return. Otherwise, unmap it and go to // the slow path. if (IsAligned(mapped_addr, alignment)) return (void*)mapped_addr; if (VirtualFree((void *)mapped_addr, 0, MEM_RELEASE) == 0) ReportMmapFailureAndDie(size, mem_type, "deallocate", GetLastError()); // If we didn't get an aligned address, overallocate, find an aligned address, // unmap, and try to allocate at that aligned address. int retries = 0; const int kMaxRetries = 10; for (; retries < kMaxRetries && (mapped_addr == 0 || !IsAligned(mapped_addr, alignment)); retries++) { // Overallocate size + alignment bytes. mapped_addr = (uptr)VirtualAlloc(0, size + alignment, MEM_RESERVE, PAGE_NOACCESS); if (!mapped_addr) ReportMmapFailureAndDie(size, mem_type, "allocate aligned", GetLastError()); // Find the aligned address. uptr aligned_addr = RoundUpTo(mapped_addr, alignment); // Free the overallocation. if (VirtualFree((void *)mapped_addr, 0, MEM_RELEASE) == 0) ReportMmapFailureAndDie(size, mem_type, "deallocate", GetLastError()); // Attempt to allocate exactly the number of bytes we need at the aligned // address. This may fail for a number of reasons, in which case we continue // the loop. mapped_addr = (uptr)VirtualAlloc((void *)aligned_addr, size, MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE); } // Fail if we can't make this work quickly. if (retries == kMaxRetries && mapped_addr == 0) ReportMmapFailureAndDie(size, mem_type, "allocate aligned", GetLastError()); return (void *)mapped_addr; } void *MmapFixedNoReserve(uptr fixed_addr, uptr size, const char *name) { // FIXME: is this really "NoReserve"? On Win32 this does not matter much, // but on Win64 it does. (void)name; // unsupported #if !SANITIZER_GO && SANITIZER_WINDOWS64 // On asan/Windows64, use MEM_COMMIT would result in error // 1455:ERROR_COMMITMENT_LIMIT. // Asan uses exception handler to commit page on demand. void *p = VirtualAlloc((LPVOID)fixed_addr, size, MEM_RESERVE, PAGE_READWRITE); #else void *p = VirtualAlloc((LPVOID)fixed_addr, size, MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE); #endif if (p == 0) Report("ERROR: %s failed to " "allocate %p (%zd) bytes at %p (error code: %d)\n", SanitizerToolName, size, size, fixed_addr, GetLastError()); return p; } // Memory space mapped by 'MmapFixedOrDie' must have been reserved by // 'MmapFixedNoAccess'. void *MmapFixedOrDie(uptr fixed_addr, uptr size) { void *p = VirtualAlloc((LPVOID)fixed_addr, size, MEM_COMMIT, PAGE_READWRITE); if (p == 0) { char mem_type[30]; internal_snprintf(mem_type, sizeof(mem_type), "memory at address 0x%zx", fixed_addr); ReportMmapFailureAndDie(size, mem_type, "allocate", GetLastError()); } return p; } void *MmapNoReserveOrDie(uptr size, const char *mem_type) { // FIXME: make this really NoReserve? return MmapOrDie(size, mem_type); } void *MmapFixedNoAccess(uptr fixed_addr, uptr size, const char *name) { (void)name; // unsupported void *res = VirtualAlloc((LPVOID)fixed_addr, size, MEM_RESERVE, PAGE_NOACCESS); if (res == 0) Report("WARNING: %s failed to " "mprotect %p (%zd) bytes at %p (error code: %d)\n", SanitizerToolName, size, size, fixed_addr, GetLastError()); return res; } void *MmapNoAccess(uptr size) { void *res = VirtualAlloc(nullptr, size, MEM_RESERVE, PAGE_NOACCESS); if (res == 0) Report("WARNING: %s failed to " "mprotect %p (%zd) bytes (error code: %d)\n", SanitizerToolName, size, size, GetLastError()); return res; } bool MprotectNoAccess(uptr addr, uptr size) { DWORD old_protection; return VirtualProtect((LPVOID)addr, size, PAGE_NOACCESS, &old_protection); } void ReleaseMemoryPagesToOS(uptr beg, uptr end) { // This is almost useless on 32-bits. // FIXME: add madvise-analog when we move to 64-bits. } void NoHugePagesInRegion(uptr addr, uptr size) { // FIXME: probably similar to ReleaseMemoryToOS. } void DontDumpShadowMemory(uptr addr, uptr length) { // This is almost useless on 32-bits. // FIXME: add madvise-analog when we move to 64-bits. } uptr FindAvailableMemoryRange(uptr size, uptr alignment, uptr left_padding) { uptr address = 0; while (true) { MEMORY_BASIC_INFORMATION info; if (!::VirtualQuery((void*)address, &info, sizeof(info))) return 0; if (info.State == MEM_FREE) { uptr shadow_address = RoundUpTo((uptr)info.BaseAddress + left_padding, alignment); if (shadow_address + size < (uptr)info.BaseAddress + info.RegionSize) return shadow_address; } // Move to the next region. address = (uptr)info.BaseAddress + info.RegionSize; } return 0; } bool MemoryRangeIsAvailable(uptr range_start, uptr range_end) { MEMORY_BASIC_INFORMATION mbi; CHECK(VirtualQuery((void *)range_start, &mbi, sizeof(mbi))); return mbi.Protect == PAGE_NOACCESS && (uptr)mbi.BaseAddress + mbi.RegionSize >= range_end; } void *MapFileToMemory(const char *file_name, uptr *buff_size) { UNIMPLEMENTED(); } void *MapWritableFileToMemory(void *addr, uptr size, fd_t fd, OFF_T offset) { UNIMPLEMENTED(); } static const int kMaxEnvNameLength = 128; static const DWORD kMaxEnvValueLength = 32767; namespace { struct EnvVariable { char name[kMaxEnvNameLength]; char value[kMaxEnvValueLength]; }; } // namespace static const int kEnvVariables = 5; static EnvVariable env_vars[kEnvVariables]; static int num_env_vars; const char *GetEnv(const char *name) { // Note: this implementation caches the values of the environment variables // and limits their quantity. for (int i = 0; i < num_env_vars; i++) { if (0 == internal_strcmp(name, env_vars[i].name)) return env_vars[i].value; } CHECK_LT(num_env_vars, kEnvVariables); DWORD rv = GetEnvironmentVariableA(name, env_vars[num_env_vars].value, kMaxEnvValueLength); if (rv > 0 && rv < kMaxEnvValueLength) { CHECK_LT(internal_strlen(name), kMaxEnvNameLength); internal_strncpy(env_vars[num_env_vars].name, name, kMaxEnvNameLength); num_env_vars++; return env_vars[num_env_vars - 1].value; } return 0; } const char *GetPwd() { UNIMPLEMENTED(); } u32 GetUid() { UNIMPLEMENTED(); } namespace { struct ModuleInfo { const char *filepath; uptr base_address; uptr end_address; }; #if !SANITIZER_GO int CompareModulesBase(const void *pl, const void *pr) { const ModuleInfo *l = (ModuleInfo *)pl, *r = (ModuleInfo *)pr; if (l->base_address < r->base_address) return -1; return l->base_address > r->base_address; } #endif } // namespace #if !SANITIZER_GO void DumpProcessMap() { Report("Dumping process modules:\n"); ListOfModules modules; modules.init(); uptr num_modules = modules.size(); InternalScopedBuffer module_infos(num_modules); for (size_t i = 0; i < num_modules; ++i) { module_infos[i].filepath = modules[i].full_name(); module_infos[i].base_address = modules[i].ranges().front()->beg; module_infos[i].end_address = modules[i].ranges().back()->end; } qsort(module_infos.data(), num_modules, sizeof(ModuleInfo), CompareModulesBase); for (size_t i = 0; i < num_modules; ++i) { const ModuleInfo &mi = module_infos[i]; if (mi.end_address != 0) { Printf("\t%p-%p %s\n", mi.base_address, mi.end_address, mi.filepath[0] ? mi.filepath : "[no name]"); } else if (mi.filepath[0]) { Printf("\t??\?-??? %s\n", mi.filepath); } else { Printf("\t???\n"); } } } #endif void PrintModuleMap() { } void DisableCoreDumperIfNecessary() { // Do nothing. } void ReExec() { UNIMPLEMENTED(); } void PrepareForSandboxing(__sanitizer_sandbox_arguments *args) { #if !SANITIZER_GO CovPrepareForSandboxing(args); #endif } bool StackSizeIsUnlimited() { UNIMPLEMENTED(); } void SetStackSizeLimitInBytes(uptr limit) { UNIMPLEMENTED(); } bool AddressSpaceIsUnlimited() { UNIMPLEMENTED(); } void SetAddressSpaceUnlimited() { UNIMPLEMENTED(); } bool IsPathSeparator(const char c) { return c == '\\' || c == '/'; } bool IsAbsolutePath(const char *path) { UNIMPLEMENTED(); } void SleepForSeconds(int seconds) { Sleep(seconds * 1000); } void SleepForMillis(int millis) { Sleep(millis); } u64 NanoTime() { return 0; } void Abort() { internal__exit(3); } #if !SANITIZER_GO // Read the file to extract the ImageBase field from the PE header. If ASLR is // disabled and this virtual address is available, the loader will typically // load the image at this address. Therefore, we call it the preferred base. Any // addresses in the DWARF typically assume that the object has been loaded at // this address. static uptr GetPreferredBase(const char *modname) { fd_t fd = OpenFile(modname, RdOnly, nullptr); if (fd == kInvalidFd) return 0; FileCloser closer(fd); // Read just the DOS header. IMAGE_DOS_HEADER dos_header; uptr bytes_read; if (!ReadFromFile(fd, &dos_header, sizeof(dos_header), &bytes_read) || bytes_read != sizeof(dos_header)) return 0; // The file should start with the right signature. if (dos_header.e_magic != IMAGE_DOS_SIGNATURE) return 0; // The layout at e_lfanew is: // "PE\0\0" // IMAGE_FILE_HEADER // IMAGE_OPTIONAL_HEADER // Seek to e_lfanew and read all that data. char buf[4 + sizeof(IMAGE_FILE_HEADER) + sizeof(IMAGE_OPTIONAL_HEADER)]; if (::SetFilePointer(fd, dos_header.e_lfanew, nullptr, FILE_BEGIN) == INVALID_SET_FILE_POINTER) return 0; if (!ReadFromFile(fd, &buf[0], sizeof(buf), &bytes_read) || bytes_read != sizeof(buf)) return 0; // Check for "PE\0\0" before the PE header. char *pe_sig = &buf[0]; if (internal_memcmp(pe_sig, "PE\0\0", 4) != 0) return 0; // Skip over IMAGE_FILE_HEADER. We could do more validation here if we wanted. IMAGE_OPTIONAL_HEADER *pe_header = (IMAGE_OPTIONAL_HEADER *)(pe_sig + 4 + sizeof(IMAGE_FILE_HEADER)); // Check for more magic in the PE header. if (pe_header->Magic != IMAGE_NT_OPTIONAL_HDR_MAGIC) return 0; // Finally, return the ImageBase. return (uptr)pe_header->ImageBase; } void ListOfModules::init() { clear(); HANDLE cur_process = GetCurrentProcess(); // Query the list of modules. Start by assuming there are no more than 256 // modules and retry if that's not sufficient. HMODULE *hmodules = 0; uptr modules_buffer_size = sizeof(HMODULE) * 256; DWORD bytes_required; while (!hmodules) { hmodules = (HMODULE *)MmapOrDie(modules_buffer_size, __FUNCTION__); CHECK(EnumProcessModules(cur_process, hmodules, modules_buffer_size, &bytes_required)); if (bytes_required > modules_buffer_size) { // Either there turned out to be more than 256 hmodules, or new hmodules // could have loaded since the last try. Retry. UnmapOrDie(hmodules, modules_buffer_size); hmodules = 0; modules_buffer_size = bytes_required; } } // |num_modules| is the number of modules actually present, size_t num_modules = bytes_required / sizeof(HMODULE); for (size_t i = 0; i < num_modules; ++i) { HMODULE handle = hmodules[i]; MODULEINFO mi; if (!GetModuleInformation(cur_process, handle, &mi, sizeof(mi))) continue; // Get the UTF-16 path and convert to UTF-8. wchar_t modname_utf16[kMaxPathLength]; int modname_utf16_len = GetModuleFileNameW(handle, modname_utf16, kMaxPathLength); if (modname_utf16_len == 0) modname_utf16[0] = '\0'; char module_name[kMaxPathLength]; int module_name_len = ::WideCharToMultiByte(CP_UTF8, 0, modname_utf16, modname_utf16_len + 1, &module_name[0], kMaxPathLength, NULL, NULL); module_name[module_name_len] = '\0'; uptr base_address = (uptr)mi.lpBaseOfDll; uptr end_address = (uptr)mi.lpBaseOfDll + mi.SizeOfImage; // Adjust the base address of the module so that we get a VA instead of an // RVA when computing the module offset. This helps llvm-symbolizer find the // right DWARF CU. In the common case that the image is loaded at it's // preferred address, we will now print normal virtual addresses. uptr preferred_base = GetPreferredBase(&module_name[0]); uptr adjusted_base = base_address - preferred_base; LoadedModule cur_module; cur_module.set(module_name, adjusted_base); // We add the whole module as one single address range. - cur_module.addAddressRange(base_address, end_address, /*executable*/ true); + cur_module.addAddressRange(base_address, end_address, /*executable*/ true, + /*readable*/ true); modules_.push_back(cur_module); } UnmapOrDie(hmodules, modules_buffer_size); }; // We can't use atexit() directly at __asan_init time as the CRT is not fully // initialized at this point. Place the functions into a vector and use // atexit() as soon as it is ready for use (i.e. after .CRT$XIC initializers). InternalMmapVectorNoCtor atexit_functions; int Atexit(void (*function)(void)) { atexit_functions.push_back(function); return 0; } static int RunAtexit() { int ret = 0; for (uptr i = 0; i < atexit_functions.size(); ++i) { ret |= atexit(atexit_functions[i]); } return ret; } #pragma section(".CRT$XID", long, read) // NOLINT __declspec(allocate(".CRT$XID")) int (*__run_atexit)() = RunAtexit; #endif // ------------------ sanitizer_libc.h fd_t OpenFile(const char *filename, FileAccessMode mode, error_t *last_error) { // FIXME: Use the wide variants to handle Unicode filenames. fd_t res; if (mode == RdOnly) { res = CreateFileA(filename, GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, nullptr, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, nullptr); } else if (mode == WrOnly) { res = CreateFileA(filename, GENERIC_WRITE, 0, nullptr, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, nullptr); } else { UNIMPLEMENTED(); } CHECK(res != kStdoutFd || kStdoutFd == kInvalidFd); CHECK(res != kStderrFd || kStderrFd == kInvalidFd); if (res == kInvalidFd && last_error) *last_error = GetLastError(); return res; } void CloseFile(fd_t fd) { CloseHandle(fd); } bool ReadFromFile(fd_t fd, void *buff, uptr buff_size, uptr *bytes_read, error_t *error_p) { CHECK(fd != kInvalidFd); // bytes_read can't be passed directly to ReadFile: // uptr is unsigned long long on 64-bit Windows. unsigned long num_read_long; bool success = ::ReadFile(fd, buff, buff_size, &num_read_long, nullptr); if (!success && error_p) *error_p = GetLastError(); if (bytes_read) *bytes_read = num_read_long; return success; } bool SupportsColoredOutput(fd_t fd) { // FIXME: support colored output. return false; } bool WriteToFile(fd_t fd, const void *buff, uptr buff_size, uptr *bytes_written, error_t *error_p) { CHECK(fd != kInvalidFd); // Handle null optional parameters. error_t dummy_error; error_p = error_p ? error_p : &dummy_error; uptr dummy_bytes_written; bytes_written = bytes_written ? bytes_written : &dummy_bytes_written; // Initialize output parameters in case we fail. *error_p = 0; *bytes_written = 0; // Map the conventional Unix fds 1 and 2 to Windows handles. They might be // closed, in which case this will fail. if (fd == kStdoutFd || fd == kStderrFd) { fd = GetStdHandle(fd == kStdoutFd ? STD_OUTPUT_HANDLE : STD_ERROR_HANDLE); if (fd == 0) { *error_p = ERROR_INVALID_HANDLE; return false; } } DWORD bytes_written_32; if (!WriteFile(fd, buff, buff_size, &bytes_written_32, 0)) { *error_p = GetLastError(); return false; } else { *bytes_written = bytes_written_32; return true; } } bool RenameFile(const char *oldpath, const char *newpath, error_t *error_p) { UNIMPLEMENTED(); } uptr internal_sched_yield() { Sleep(0); return 0; } void internal__exit(int exitcode) { // ExitProcess runs some finalizers, so use TerminateProcess to avoid that. // The debugger doesn't stop on TerminateProcess like it does on ExitProcess, // so add our own breakpoint here. if (::IsDebuggerPresent()) __debugbreak(); TerminateProcess(GetCurrentProcess(), exitcode); BUILTIN_UNREACHABLE(); } uptr internal_ftruncate(fd_t fd, uptr size) { UNIMPLEMENTED(); } uptr GetRSS() { return 0; } void *internal_start_thread(void (*func)(void *arg), void *arg) { return 0; } void internal_join_thread(void *th) { } // ---------------------- BlockingMutex ---------------- {{{1 const uptr LOCK_UNINITIALIZED = 0; const uptr LOCK_READY = (uptr)-1; BlockingMutex::BlockingMutex(LinkerInitialized li) { // FIXME: see comments in BlockingMutex::Lock() for the details. CHECK(li == LINKER_INITIALIZED || owner_ == LOCK_UNINITIALIZED); CHECK(sizeof(CRITICAL_SECTION) <= sizeof(opaque_storage_)); InitializeCriticalSection((LPCRITICAL_SECTION)opaque_storage_); owner_ = LOCK_READY; } BlockingMutex::BlockingMutex() { CHECK(sizeof(CRITICAL_SECTION) <= sizeof(opaque_storage_)); InitializeCriticalSection((LPCRITICAL_SECTION)opaque_storage_); owner_ = LOCK_READY; } void BlockingMutex::Lock() { if (owner_ == LOCK_UNINITIALIZED) { // FIXME: hm, global BlockingMutex objects are not initialized?!? // This might be a side effect of the clang+cl+link Frankenbuild... new(this) BlockingMutex((LinkerInitialized)(LINKER_INITIALIZED + 1)); // FIXME: If it turns out the linker doesn't invoke our // constructors, we should probably manually Lock/Unlock all the global // locks while we're starting in one thread to avoid double-init races. } EnterCriticalSection((LPCRITICAL_SECTION)opaque_storage_); CHECK_EQ(owner_, LOCK_READY); owner_ = GetThreadSelf(); } void BlockingMutex::Unlock() { CHECK_EQ(owner_, GetThreadSelf()); owner_ = LOCK_READY; LeaveCriticalSection((LPCRITICAL_SECTION)opaque_storage_); } void BlockingMutex::CheckLocked() { CHECK_EQ(owner_, GetThreadSelf()); } uptr GetTlsSize() { return 0; } void InitTlsSize() { } void GetThreadStackAndTls(bool main, uptr *stk_addr, uptr *stk_size, uptr *tls_addr, uptr *tls_size) { #if SANITIZER_GO *stk_addr = 0; *stk_size = 0; *tls_addr = 0; *tls_size = 0; #else uptr stack_top, stack_bottom; GetThreadStackTopAndBottom(main, &stack_top, &stack_bottom); *stk_addr = stack_bottom; *stk_size = stack_top - stack_bottom; *tls_addr = 0; *tls_size = 0; #endif } #if !SANITIZER_GO void BufferedStackTrace::SlowUnwindStack(uptr pc, u32 max_depth) { CHECK_GE(max_depth, 2); // FIXME: CaptureStackBackTrace might be too slow for us. // FIXME: Compare with StackWalk64. // FIXME: Look at LLVMUnhandledExceptionFilter in Signals.inc size = CaptureStackBackTrace(1, Min(max_depth, kStackTraceMax), (void**)trace, 0); if (size == 0) return; // Skip the RTL frames by searching for the PC in the stacktrace. uptr pc_location = LocatePcInTrace(pc); PopStackFrames(pc_location); } void BufferedStackTrace::SlowUnwindStackWithContext(uptr pc, void *context, u32 max_depth) { CONTEXT ctx = *(CONTEXT *)context; STACKFRAME64 stack_frame; memset(&stack_frame, 0, sizeof(stack_frame)); InitializeDbgHelpIfNeeded(); size = 0; #if defined(_WIN64) int machine_type = IMAGE_FILE_MACHINE_AMD64; stack_frame.AddrPC.Offset = ctx.Rip; stack_frame.AddrFrame.Offset = ctx.Rbp; stack_frame.AddrStack.Offset = ctx.Rsp; #else int machine_type = IMAGE_FILE_MACHINE_I386; stack_frame.AddrPC.Offset = ctx.Eip; stack_frame.AddrFrame.Offset = ctx.Ebp; stack_frame.AddrStack.Offset = ctx.Esp; #endif stack_frame.AddrPC.Mode = AddrModeFlat; stack_frame.AddrFrame.Mode = AddrModeFlat; stack_frame.AddrStack.Mode = AddrModeFlat; while (StackWalk64(machine_type, GetCurrentProcess(), GetCurrentThread(), &stack_frame, &ctx, NULL, SymFunctionTableAccess64, SymGetModuleBase64, NULL) && size < Min(max_depth, kStackTraceMax)) { trace_buffer[size++] = (uptr)stack_frame.AddrPC.Offset; } } #endif // #if !SANITIZER_GO void ReportFile::Write(const char *buffer, uptr length) { SpinMutexLock l(mu); ReopenIfNecessary(); if (!WriteToFile(fd, buffer, length)) { // stderr may be closed, but we may be able to print to the debugger // instead. This is the case when launching a program from Visual Studio, // and the following routine should write to its console. OutputDebugStringA(buffer); } } void SetAlternateSignalStack() { // FIXME: Decide what to do on Windows. } void UnsetAlternateSignalStack() { // FIXME: Decide what to do on Windows. } void InstallDeadlySignalHandlers(SignalHandlerType handler) { (void)handler; // FIXME: Decide what to do on Windows. } bool IsHandledDeadlySignal(int signum) { // FIXME: Decide what to do on Windows. return false; } // Check based on flags if we should handle this exception. bool IsHandledDeadlyException(DWORD exceptionCode) { switch (exceptionCode) { case EXCEPTION_ACCESS_VIOLATION: case EXCEPTION_ARRAY_BOUNDS_EXCEEDED: case EXCEPTION_STACK_OVERFLOW: case EXCEPTION_DATATYPE_MISALIGNMENT: case EXCEPTION_IN_PAGE_ERROR: return common_flags()->handle_segv; case EXCEPTION_ILLEGAL_INSTRUCTION: case EXCEPTION_PRIV_INSTRUCTION: case EXCEPTION_BREAKPOINT: return common_flags()->handle_sigill; case EXCEPTION_FLT_DENORMAL_OPERAND: case EXCEPTION_FLT_DIVIDE_BY_ZERO: case EXCEPTION_FLT_INEXACT_RESULT: case EXCEPTION_FLT_INVALID_OPERATION: case EXCEPTION_FLT_OVERFLOW: case EXCEPTION_FLT_STACK_CHECK: case EXCEPTION_FLT_UNDERFLOW: case EXCEPTION_INT_DIVIDE_BY_ZERO: case EXCEPTION_INT_OVERFLOW: return common_flags()->handle_sigfpe; } return false; } const char *DescribeSignalOrException(int signo) { unsigned code = signo; // Get the string description of the exception if this is a known deadly // exception. switch (code) { case EXCEPTION_ACCESS_VIOLATION: return "access-violation"; case EXCEPTION_ARRAY_BOUNDS_EXCEEDED: return "array-bounds-exceeded"; case EXCEPTION_STACK_OVERFLOW: return "stack-overflow"; case EXCEPTION_DATATYPE_MISALIGNMENT: return "datatype-misalignment"; case EXCEPTION_IN_PAGE_ERROR: return "in-page-error"; case EXCEPTION_ILLEGAL_INSTRUCTION: return "illegal-instruction"; case EXCEPTION_PRIV_INSTRUCTION: return "priv-instruction"; case EXCEPTION_BREAKPOINT: return "breakpoint"; case EXCEPTION_FLT_DENORMAL_OPERAND: return "flt-denormal-operand"; case EXCEPTION_FLT_DIVIDE_BY_ZERO: return "flt-divide-by-zero"; case EXCEPTION_FLT_INEXACT_RESULT: return "flt-inexact-result"; case EXCEPTION_FLT_INVALID_OPERATION: return "flt-invalid-operation"; case EXCEPTION_FLT_OVERFLOW: return "flt-overflow"; case EXCEPTION_FLT_STACK_CHECK: return "flt-stack-check"; case EXCEPTION_FLT_UNDERFLOW: return "flt-underflow"; case EXCEPTION_INT_DIVIDE_BY_ZERO: return "int-divide-by-zero"; case EXCEPTION_INT_OVERFLOW: return "int-overflow"; } return "unknown exception"; } bool IsAccessibleMemoryRange(uptr beg, uptr size) { SYSTEM_INFO si; GetNativeSystemInfo(&si); uptr page_size = si.dwPageSize; uptr page_mask = ~(page_size - 1); for (uptr page = beg & page_mask, end = (beg + size - 1) & page_mask; page <= end;) { MEMORY_BASIC_INFORMATION info; if (VirtualQuery((LPCVOID)page, &info, sizeof(info)) != sizeof(info)) return false; if (info.Protect == 0 || info.Protect == PAGE_NOACCESS || info.Protect == PAGE_EXECUTE) return false; if (info.RegionSize == 0) return false; page += info.RegionSize; } return true; } SignalContext SignalContext::Create(void *siginfo, void *context) { EXCEPTION_RECORD *exception_record = (EXCEPTION_RECORD *)siginfo; CONTEXT *context_record = (CONTEXT *)context; uptr pc = (uptr)exception_record->ExceptionAddress; #ifdef _WIN64 uptr bp = (uptr)context_record->Rbp; uptr sp = (uptr)context_record->Rsp; #else uptr bp = (uptr)context_record->Ebp; uptr sp = (uptr)context_record->Esp; #endif uptr access_addr = exception_record->ExceptionInformation[1]; // The contents of this array are documented at // https://msdn.microsoft.com/en-us/library/windows/desktop/aa363082(v=vs.85).aspx // The first element indicates read as 0, write as 1, or execute as 8. The // second element is the faulting address. WriteFlag write_flag = SignalContext::UNKNOWN; switch (exception_record->ExceptionInformation[0]) { case 0: write_flag = SignalContext::READ; break; case 1: write_flag = SignalContext::WRITE; break; case 8: write_flag = SignalContext::UNKNOWN; break; } bool is_memory_access = write_flag != SignalContext::UNKNOWN; return SignalContext(context, access_addr, pc, sp, bp, is_memory_access, write_flag); } void SignalContext::DumpAllRegisters(void *context) { // FIXME: Implement this. } uptr ReadBinaryName(/*out*/char *buf, uptr buf_len) { // FIXME: Actually implement this function. CHECK_GT(buf_len, 0); buf[0] = 0; return 0; } uptr ReadLongProcessName(/*out*/char *buf, uptr buf_len) { return ReadBinaryName(buf, buf_len); } void CheckVMASize() { // Do nothing. } void MaybeReexec() { // No need to re-exec on Windows. } char **GetArgv() { // FIXME: Actually implement this function. return 0; } pid_t StartSubprocess(const char *program, const char *const argv[], fd_t stdin_fd, fd_t stdout_fd, fd_t stderr_fd) { // FIXME: implement on this platform // Should be implemented based on // SymbolizerProcess::StarAtSymbolizerSubprocess // from lib/sanitizer_common/sanitizer_symbolizer_win.cc. return -1; } bool IsProcessRunning(pid_t pid) { // FIXME: implement on this platform. return false; } int WaitForProcess(pid_t pid) { return -1; } // FIXME implement on this platform. void GetMemoryProfile(fill_profile_f cb, uptr *stats, uptr stats_size) { } void CheckNoDeepBind(const char *filename, int flag) { // Do nothing. } } // namespace __sanitizer #endif // _WIN32 Index: vendor/compiler-rt/dist/lib/scudo/scudo_allocator.cpp =================================================================== --- vendor/compiler-rt/dist/lib/scudo/scudo_allocator.cpp (revision 317221) +++ vendor/compiler-rt/dist/lib/scudo/scudo_allocator.cpp (revision 317222) @@ -1,702 +1,724 @@ //===-- scudo_allocator.cpp -------------------------------------*- C++ -*-===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// /// /// Scudo Hardened Allocator implementation. /// It uses the sanitizer_common allocator as a base and aims at mitigating /// heap corruption vulnerabilities. It provides a checksum-guarded chunk /// header, a delayed free list, and additional sanity checks. /// //===----------------------------------------------------------------------===// #include "scudo_allocator.h" #include "scudo_utils.h" #include "sanitizer_common/sanitizer_allocator_interface.h" #include "sanitizer_common/sanitizer_quarantine.h" #include #include +#include -#include - namespace __scudo { #if SANITIZER_CAN_USE_ALLOCATOR64 const uptr AllocatorSpace = ~0ULL; const uptr AllocatorSize = 0x40000000000ULL; typedef DefaultSizeClassMap SizeClassMap; struct AP { static const uptr kSpaceBeg = AllocatorSpace; static const uptr kSpaceSize = AllocatorSize; static const uptr kMetadataSize = 0; typedef __scudo::SizeClassMap SizeClassMap; typedef NoOpMapUnmapCallback MapUnmapCallback; static const uptr kFlags = SizeClassAllocator64FlagMasks::kRandomShuffleChunks; }; typedef SizeClassAllocator64 PrimaryAllocator; #else // Currently, the 32-bit Sanitizer allocator has not yet benefited from all the // security improvements brought to the 64-bit one. This makes the 32-bit // version of Scudo slightly less toughened. static const uptr RegionSizeLog = 20; static const uptr NumRegions = SANITIZER_MMAP_RANGE_SIZE >> RegionSizeLog; # if SANITIZER_WORDSIZE == 32 typedef FlatByteMap ByteMap; # elif SANITIZER_WORDSIZE == 64 typedef TwoLevelByteMap<(NumRegions >> 12), 1 << 12> ByteMap; # endif // SANITIZER_WORDSIZE typedef DefaultSizeClassMap SizeClassMap; typedef SizeClassAllocator32<0, SANITIZER_MMAP_RANGE_SIZE, 0, SizeClassMap, RegionSizeLog, ByteMap> PrimaryAllocator; #endif // SANITIZER_CAN_USE_ALLOCATOR64 typedef SizeClassAllocatorLocalCache AllocatorCache; typedef ScudoLargeMmapAllocator SecondaryAllocator; typedef CombinedAllocator - ScudoAllocator; + ScudoBackendAllocator; -static ScudoAllocator &getAllocator(); +static ScudoBackendAllocator &getBackendAllocator(); static thread_local Xorshift128Plus Prng; // Global static cookie, initialized at start-up. static uptr Cookie; // We default to software CRC32 if the alternatives are not supported, either // at compilation or at runtime. static atomic_uint8_t HashAlgorithm = { CRC32Software }; SANITIZER_WEAK_ATTRIBUTE u32 computeHardwareCRC32(u32 Crc, uptr Data); INLINE u32 computeCRC32(u32 Crc, uptr Data, u8 HashType) { // If SSE4.2 is defined here, it was enabled everywhere, as opposed to only // for scudo_crc32.cpp. This means that other SSE instructions were likely // emitted at other places, and as a result there is no reason to not use // the hardware version of the CRC32. #if defined(__SSE4_2__) || defined(__ARM_FEATURE_CRC32) return computeHardwareCRC32(Crc, Data); #else if (computeHardwareCRC32 && HashType == CRC32Hardware) return computeHardwareCRC32(Crc, Data); else return computeSoftwareCRC32(Crc, Data); #endif // defined(__SSE4_2__) } struct ScudoChunk : UnpackedHeader { // We can't use the offset member of the chunk itself, as we would double // fetch it without any warranty that it wouldn't have been tampered. To // prevent this, we work with a local copy of the header. void *getAllocBeg(UnpackedHeader *Header) { return reinterpret_cast( reinterpret_cast(this) - (Header->Offset << MinAlignmentLog)); } // Returns the usable size for a chunk, meaning the amount of bytes from the // beginning of the user data to the end of the backend allocated chunk. uptr getUsableSize(UnpackedHeader *Header) { - uptr Size = getAllocator().GetActuallyAllocatedSize(getAllocBeg(Header)); + uptr Size = getBackendAllocator().GetActuallyAllocatedSize( + getAllocBeg(Header)); if (Size == 0) - return Size; + return 0; return Size - AlignedChunkHeaderSize - (Header->Offset << MinAlignmentLog); } // Compute the checksum of the Chunk pointer and its ChunkHeader. u16 computeChecksum(UnpackedHeader *Header) const { UnpackedHeader ZeroChecksumHeader = *Header; ZeroChecksumHeader.Checksum = 0; uptr HeaderHolder[sizeof(UnpackedHeader) / sizeof(uptr)]; memcpy(&HeaderHolder, &ZeroChecksumHeader, sizeof(HeaderHolder)); u8 HashType = atomic_load_relaxed(&HashAlgorithm); u32 Crc = computeCRC32(Cookie, reinterpret_cast(this), HashType); for (uptr i = 0; i < ARRAY_SIZE(HeaderHolder); i++) Crc = computeCRC32(Crc, HeaderHolder[i], HashType); return static_cast(Crc); } - // Checks the validity of a chunk by verifying its checksum. + // Checks the validity of a chunk by verifying its checksum. It doesn't + // incur termination in the event of an invalid chunk. bool isValid() { UnpackedHeader NewUnpackedHeader; const AtomicPackedHeader *AtomicHeader = reinterpret_cast(this); PackedHeader NewPackedHeader = atomic_load_relaxed(AtomicHeader); NewUnpackedHeader = bit_cast(NewPackedHeader); return (NewUnpackedHeader.Checksum == computeChecksum(&NewUnpackedHeader)); } + // Nulls out a chunk header. When returning the chunk to the backend, there + // is no need to store a valid ChunkAvailable header, as this would be + // computationally expensive. Zeroing out serves the same purpose by making + // the header invalid. In the extremely rare event where 0 would be a valid + // checksum for the chunk, the state of the chunk is ChunkAvailable anyway. + COMPILER_CHECK(ChunkAvailable == 0); + void eraseHeader() { + PackedHeader NullPackedHeader = 0; + AtomicPackedHeader *AtomicHeader = + reinterpret_cast(this); + atomic_store_relaxed(AtomicHeader, NullPackedHeader); + } + // Loads and unpacks the header, verifying the checksum in the process. void loadHeader(UnpackedHeader *NewUnpackedHeader) const { const AtomicPackedHeader *AtomicHeader = reinterpret_cast(this); PackedHeader NewPackedHeader = atomic_load_relaxed(AtomicHeader); *NewUnpackedHeader = bit_cast(NewPackedHeader); - if (NewUnpackedHeader->Checksum != computeChecksum(NewUnpackedHeader)) { + if (UNLIKELY(NewUnpackedHeader->Checksum != + computeChecksum(NewUnpackedHeader))) { dieWithMessage("ERROR: corrupted chunk header at address %p\n", this); } } // Packs and stores the header, computing the checksum in the process. void storeHeader(UnpackedHeader *NewUnpackedHeader) { NewUnpackedHeader->Checksum = computeChecksum(NewUnpackedHeader); PackedHeader NewPackedHeader = bit_cast(*NewUnpackedHeader); AtomicPackedHeader *AtomicHeader = reinterpret_cast(this); atomic_store_relaxed(AtomicHeader, NewPackedHeader); } // Packs and stores the header, computing the checksum in the process. We // compare the current header with the expected provided one to ensure that // we are not being raced by a corruption occurring in another thread. void compareExchangeHeader(UnpackedHeader *NewUnpackedHeader, UnpackedHeader *OldUnpackedHeader) { NewUnpackedHeader->Checksum = computeChecksum(NewUnpackedHeader); PackedHeader NewPackedHeader = bit_cast(*NewUnpackedHeader); PackedHeader OldPackedHeader = bit_cast(*OldUnpackedHeader); AtomicPackedHeader *AtomicHeader = reinterpret_cast(this); - if (!atomic_compare_exchange_strong(AtomicHeader, - &OldPackedHeader, - NewPackedHeader, - memory_order_relaxed)) { + if (UNLIKELY(!atomic_compare_exchange_strong(AtomicHeader, + &OldPackedHeader, + NewPackedHeader, + memory_order_relaxed))) { dieWithMessage("ERROR: race on chunk header at address %p\n", this); } } }; +ScudoChunk *getScudoChunk(uptr UserBeg) { + return reinterpret_cast(UserBeg - AlignedChunkHeaderSize); +} + static bool ScudoInitIsRunning = false; static pthread_once_t GlobalInited = PTHREAD_ONCE_INIT; static pthread_key_t PThreadKey; static thread_local bool ThreadInited = false; static thread_local bool ThreadTornDown = false; static thread_local AllocatorCache Cache; static void teardownThread(void *p) { uptr v = reinterpret_cast(p); // The glibc POSIX thread-local-storage deallocation routine calls user // provided destructors in a loop of PTHREAD_DESTRUCTOR_ITERATIONS. // We want to be called last since other destructors might call free and the // like, so we wait until PTHREAD_DESTRUCTOR_ITERATIONS before draining the // quarantine and swallowing the cache. if (v < PTHREAD_DESTRUCTOR_ITERATIONS) { pthread_setspecific(PThreadKey, reinterpret_cast(v + 1)); return; } drainQuarantine(); - getAllocator().DestroyCache(&Cache); + getBackendAllocator().DestroyCache(&Cache); ThreadTornDown = true; } static void initInternal() { SanitizerToolName = "Scudo"; CHECK(!ScudoInitIsRunning && "Scudo init calls itself!"); ScudoInitIsRunning = true; // Check is SSE4.2 is supported, if so, opt for the CRC32 hardware version. if (testCPUFeature(CRC32CPUFeature)) { atomic_store_relaxed(&HashAlgorithm, CRC32Hardware); } initFlags(); AllocatorOptions Options; Options.setFrom(getFlags(), common_flags()); initAllocator(Options); MaybeStartBackgroudThread(); ScudoInitIsRunning = false; } static void initGlobal() { pthread_key_create(&PThreadKey, teardownThread); initInternal(); } static void NOINLINE initThread() { pthread_once(&GlobalInited, initGlobal); pthread_setspecific(PThreadKey, reinterpret_cast(1)); - getAllocator().InitCache(&Cache); + getBackendAllocator().InitCache(&Cache); ThreadInited = true; } struct QuarantineCallback { explicit QuarantineCallback(AllocatorCache *Cache) : Cache_(Cache) {} // Chunk recycling function, returns a quarantined chunk to the backend. void Recycle(ScudoChunk *Chunk) { UnpackedHeader Header; Chunk->loadHeader(&Header); - if (Header.State != ChunkQuarantine) { + if (UNLIKELY(Header.State != ChunkQuarantine)) { dieWithMessage("ERROR: invalid chunk state when recycling address %p\n", Chunk); } + Chunk->eraseHeader(); void *Ptr = Chunk->getAllocBeg(&Header); - getAllocator().Deallocate(Cache_, Ptr); + getBackendAllocator().Deallocate(Cache_, Ptr); } /// Internal quarantine allocation and deallocation functions. void *Allocate(uptr Size) { - // The internal quarantine memory cannot be protected by us. But the only - // structures allocated are QuarantineBatch, that are 8KB for x64. So we - // will use mmap for those, and given that Deallocate doesn't pass a size - // in, we enforce the size of the allocation to be sizeof(QuarantineBatch). - // TODO(kostyak): switching to mmap impacts greatly performances, we have - // to find another solution - // CHECK_EQ(Size, sizeof(QuarantineBatch)); - // return MmapOrDie(Size, "QuarantineBatch"); - return getAllocator().Allocate(Cache_, Size, 1, false); + // TODO(kostyak): figure out the best way to protect the batches. + return getBackendAllocator().Allocate(Cache_, Size, MinAlignment); } void Deallocate(void *Ptr) { - // UnmapOrDie(Ptr, sizeof(QuarantineBatch)); - getAllocator().Deallocate(Cache_, Ptr); + getBackendAllocator().Deallocate(Cache_, Ptr); } AllocatorCache *Cache_; }; typedef Quarantine ScudoQuarantine; -typedef ScudoQuarantine::Cache QuarantineCache; -static thread_local QuarantineCache ThreadQuarantineCache; +typedef ScudoQuarantine::Cache ScudoQuarantineCache; +static thread_local ScudoQuarantineCache ThreadQuarantineCache; void AllocatorOptions::setFrom(const Flags *f, const CommonFlags *cf) { MayReturnNull = cf->allocator_may_return_null; ReleaseToOSIntervalMs = cf->allocator_release_to_os_interval_ms; QuarantineSizeMb = f->QuarantineSizeMb; ThreadLocalQuarantineSizeKb = f->ThreadLocalQuarantineSizeKb; DeallocationTypeMismatch = f->DeallocationTypeMismatch; DeleteSizeMismatch = f->DeleteSizeMismatch; ZeroContents = f->ZeroContents; } void AllocatorOptions::copyTo(Flags *f, CommonFlags *cf) const { cf->allocator_may_return_null = MayReturnNull; cf->allocator_release_to_os_interval_ms = ReleaseToOSIntervalMs; f->QuarantineSizeMb = QuarantineSizeMb; f->ThreadLocalQuarantineSizeKb = ThreadLocalQuarantineSizeKb; f->DeallocationTypeMismatch = DeallocationTypeMismatch; f->DeleteSizeMismatch = DeleteSizeMismatch; f->ZeroContents = ZeroContents; } -struct Allocator { +struct ScudoAllocator { static const uptr MaxAllowedMallocSize = FIRST_32_SECOND_64(2UL << 30, 1ULL << 40); - ScudoAllocator BackendAllocator; + ScudoBackendAllocator BackendAllocator; ScudoQuarantine AllocatorQuarantine; // The fallback caches are used when the thread local caches have been // 'detroyed' on thread tear-down. They are protected by a Mutex as they can // be accessed by different threads. StaticSpinMutex FallbackMutex; AllocatorCache FallbackAllocatorCache; - QuarantineCache FallbackQuarantineCache; + ScudoQuarantineCache FallbackQuarantineCache; bool DeallocationTypeMismatch; bool ZeroContents; bool DeleteSizeMismatch; - explicit Allocator(LinkerInitialized) + explicit ScudoAllocator(LinkerInitialized) : AllocatorQuarantine(LINKER_INITIALIZED), FallbackQuarantineCache(LINKER_INITIALIZED) {} void init(const AllocatorOptions &Options) { // Verify that the header offset field can hold the maximum offset. In the // case of the Secondary allocator, it takes care of alignment and the // offset will always be 0. In the case of the Primary, the worst case // scenario happens in the last size class, when the backend allocation // would already be aligned on the requested alignment, which would happen // to be the maximum alignment that would fit in that size class. As a // result, the maximum offset will be at most the maximum alignment for the // last size class minus the header size, in multiples of MinAlignment. UnpackedHeader Header = {}; uptr MaxPrimaryAlignment = 1 << MostSignificantSetBitIndex( SizeClassMap::kMaxSize - MinAlignment); uptr MaxOffset = (MaxPrimaryAlignment - AlignedChunkHeaderSize) >> MinAlignmentLog; Header.Offset = MaxOffset; if (Header.Offset != MaxOffset) { dieWithMessage("ERROR: the maximum possible offset doesn't fit in the " "header\n"); } - // Verify that we can fit the maximum amount of unused bytes in the header. - // Given that the Secondary fits the allocation to a page, the worst case - // scenario happens in the Primary. It will depend on the second to last - // and last class sizes, as well as the dynamic base for the Primary. The - // following is an over-approximation that works for our needs. - uptr MaxUnusedBytes = SizeClassMap::kMaxSize - 1 - AlignedChunkHeaderSize; - Header.UnusedBytes = MaxUnusedBytes; - if (Header.UnusedBytes != MaxUnusedBytes) { + // Verify that we can fit the maximum size or amount of unused bytes in the + // header. Given that the Secondary fits the allocation to a page, the worst + // case scenario happens in the Primary. It will depend on the second to + // last and last class sizes, as well as the dynamic base for the Primary. + // The following is an over-approximation that works for our needs. + uptr MaxSizeOrUnusedBytes = SizeClassMap::kMaxSize - 1; + Header.SizeOrUnusedBytes = MaxSizeOrUnusedBytes; + if (Header.SizeOrUnusedBytes != MaxSizeOrUnusedBytes) { dieWithMessage("ERROR: the maximum possible unused bytes doesn't fit in " "the header\n"); } DeallocationTypeMismatch = Options.DeallocationTypeMismatch; DeleteSizeMismatch = Options.DeleteSizeMismatch; ZeroContents = Options.ZeroContents; BackendAllocator.Init(Options.MayReturnNull, Options.ReleaseToOSIntervalMs); AllocatorQuarantine.Init( static_cast(Options.QuarantineSizeMb) << 20, static_cast(Options.ThreadLocalQuarantineSizeKb) << 10); BackendAllocator.InitCache(&FallbackAllocatorCache); - Cookie = Prng.Next(); + Cookie = Prng.getNext(); } - // Helper function that checks for a valid Scudo chunk. + // Helper function that checks for a valid Scudo chunk. nullptr isn't. bool isValidPointer(const void *UserPtr) { if (UNLIKELY(!ThreadInited)) initThread(); - uptr ChunkBeg = reinterpret_cast(UserPtr); - if (!IsAligned(ChunkBeg, MinAlignment)) { + if (!UserPtr) return false; - } - ScudoChunk *Chunk = - reinterpret_cast(ChunkBeg - AlignedChunkHeaderSize); - return Chunk->isValid(); + uptr UserBeg = reinterpret_cast(UserPtr); + if (!IsAligned(UserBeg, MinAlignment)) + return false; + return getScudoChunk(UserBeg)->isValid(); } // Allocates a chunk. - void *allocate(uptr Size, uptr Alignment, AllocType Type) { + void *allocate(uptr Size, uptr Alignment, AllocType Type, + bool ForceZeroContents = false) { if (UNLIKELY(!ThreadInited)) initThread(); - if (!IsPowerOfTwo(Alignment)) { + if (UNLIKELY(!IsPowerOfTwo(Alignment))) { dieWithMessage("ERROR: alignment is not a power of 2\n"); } if (Alignment > MaxAlignment) return BackendAllocator.ReturnNullOrDieOnBadRequest(); if (Alignment < MinAlignment) Alignment = MinAlignment; - if (Size == 0) - Size = 1; if (Size >= MaxAllowedMallocSize) return BackendAllocator.ReturnNullOrDieOnBadRequest(); + if (Size == 0) + Size = 1; uptr NeededSize = RoundUpTo(Size, MinAlignment) + AlignedChunkHeaderSize; if (Alignment > MinAlignment) NeededSize += Alignment; if (NeededSize >= MaxAllowedMallocSize) return BackendAllocator.ReturnNullOrDieOnBadRequest(); // Primary backed and Secondary backed allocations have a different // treatment. We deal with alignment requirements of Primary serviced // allocations here, but the Secondary will take care of its own alignment // needs, which means we also have to work around some limitations of the // combined allocator to accommodate the situation. bool FromPrimary = PrimaryAllocator::CanAllocate(NeededSize, MinAlignment); void *Ptr; + uptr AllocationAlignment = FromPrimary ? MinAlignment : Alignment; if (LIKELY(!ThreadTornDown)) { - Ptr = BackendAllocator.Allocate(&Cache, NeededSize, - FromPrimary ? MinAlignment : Alignment); + Ptr = BackendAllocator.Allocate(&Cache, NeededSize, AllocationAlignment); } else { SpinMutexLock l(&FallbackMutex); Ptr = BackendAllocator.Allocate(&FallbackAllocatorCache, NeededSize, - FromPrimary ? MinAlignment : Alignment); + AllocationAlignment); } if (!Ptr) return BackendAllocator.ReturnNullOrDieOnOOM(); uptr AllocBeg = reinterpret_cast(Ptr); // If the allocation was serviced by the secondary, the returned pointer // accounts for ChunkHeaderSize to pass the alignment check of the combined // allocator. Adjust it here. if (!FromPrimary) { AllocBeg -= AlignedChunkHeaderSize; if (Alignment > MinAlignment) NeededSize -= Alignment; } - uptr ActuallyAllocatedSize = BackendAllocator.GetActuallyAllocatedSize( - reinterpret_cast(AllocBeg)); // If requested, we will zero out the entire contents of the returned chunk. - if (ZeroContents && FromPrimary) - memset(Ptr, 0, ActuallyAllocatedSize); + if ((ForceZeroContents || ZeroContents) && FromPrimary) + memset(Ptr, 0, BackendAllocator.GetActuallyAllocatedSize(Ptr)); - uptr ChunkBeg = AllocBeg + AlignedChunkHeaderSize; - if (!IsAligned(ChunkBeg, Alignment)) - ChunkBeg = RoundUpTo(ChunkBeg, Alignment); - CHECK_LE(ChunkBeg + Size, AllocBeg + NeededSize); - ScudoChunk *Chunk = - reinterpret_cast(ChunkBeg - AlignedChunkHeaderSize); + uptr UserBeg = AllocBeg + AlignedChunkHeaderSize; + if (!IsAligned(UserBeg, Alignment)) + UserBeg = RoundUpTo(UserBeg, Alignment); + CHECK_LE(UserBeg + Size, AllocBeg + NeededSize); UnpackedHeader Header = {}; Header.State = ChunkAllocated; - uptr Offset = ChunkBeg - AlignedChunkHeaderSize - AllocBeg; + uptr Offset = UserBeg - AlignedChunkHeaderSize - AllocBeg; Header.Offset = Offset >> MinAlignmentLog; Header.AllocType = Type; - Header.UnusedBytes = ActuallyAllocatedSize - Offset - - AlignedChunkHeaderSize - Size; - Header.Salt = static_cast(Prng.Next()); - Chunk->storeHeader(&Header); - void *UserPtr = reinterpret_cast(ChunkBeg); - // TODO(kostyak): hooks sound like a terrible idea security wise but might - // be needed for things to work properly? + if (FromPrimary) { + Header.FromPrimary = FromPrimary; + Header.SizeOrUnusedBytes = Size; + } else { + // The secondary fits the allocations to a page, so the amount of unused + // bytes is the difference between the end of the user allocation and the + // next page boundary. + uptr PageSize = GetPageSizeCached(); + uptr TrailingBytes = (UserBeg + Size) & (PageSize - 1); + if (TrailingBytes) + Header.SizeOrUnusedBytes = PageSize - TrailingBytes; + } + Header.Salt = static_cast(Prng.getNext()); + getScudoChunk(UserBeg)->storeHeader(&Header); + void *UserPtr = reinterpret_cast(UserBeg); // if (&__sanitizer_malloc_hook) __sanitizer_malloc_hook(UserPtr, Size); return UserPtr; } // Deallocates a Chunk, which means adding it to the delayed free list (or // Quarantine). void deallocate(void *UserPtr, uptr DeleteSize, AllocType Type) { if (UNLIKELY(!ThreadInited)) initThread(); - // TODO(kostyak): see hook comment above // if (&__sanitizer_free_hook) __sanitizer_free_hook(UserPtr); if (!UserPtr) return; - uptr ChunkBeg = reinterpret_cast(UserPtr); - if (!IsAligned(ChunkBeg, MinAlignment)) { + uptr UserBeg = reinterpret_cast(UserPtr); + if (UNLIKELY(!IsAligned(UserBeg, MinAlignment))) { dieWithMessage("ERROR: attempted to deallocate a chunk not properly " "aligned at address %p\n", UserPtr); } - ScudoChunk *Chunk = - reinterpret_cast(ChunkBeg - AlignedChunkHeaderSize); + ScudoChunk *Chunk = getScudoChunk(UserBeg); UnpackedHeader OldHeader; Chunk->loadHeader(&OldHeader); - if (OldHeader.State != ChunkAllocated) { + if (UNLIKELY(OldHeader.State != ChunkAllocated)) { dieWithMessage("ERROR: invalid chunk state when deallocating address " "%p\n", UserPtr); } - uptr UsableSize = Chunk->getUsableSize(&OldHeader); - UnpackedHeader NewHeader = OldHeader; - NewHeader.State = ChunkQuarantine; - Chunk->compareExchangeHeader(&NewHeader, &OldHeader); if (DeallocationTypeMismatch) { // The deallocation type has to match the allocation one. - if (NewHeader.AllocType != Type) { + if (OldHeader.AllocType != Type) { // With the exception of memalign'd Chunks, that can be still be free'd. - if (NewHeader.AllocType != FromMemalign || Type != FromMalloc) { + if (OldHeader.AllocType != FromMemalign || Type != FromMalloc) { dieWithMessage("ERROR: allocation type mismatch on address %p\n", - Chunk); + UserPtr); } } } - uptr Size = UsableSize - OldHeader.UnusedBytes; + uptr Size = OldHeader.FromPrimary ? OldHeader.SizeOrUnusedBytes : + Chunk->getUsableSize(&OldHeader) - OldHeader.SizeOrUnusedBytes; if (DeleteSizeMismatch) { if (DeleteSize && DeleteSize != Size) { dieWithMessage("ERROR: invalid sized delete on chunk at address %p\n", - Chunk); + UserPtr); } } + UnpackedHeader NewHeader = OldHeader; + NewHeader.State = ChunkQuarantine; + Chunk->compareExchangeHeader(&NewHeader, &OldHeader); + + // If a small memory amount was allocated with a larger alignment, we want + // to take that into account. Otherwise the Quarantine would be filled with + // tiny chunks, taking a lot of VA memory. This an approximation of the + // usable size, that allows us to not call GetActuallyAllocatedSize. + uptr LiableSize = Size + (OldHeader.Offset << MinAlignment); if (LIKELY(!ThreadTornDown)) { AllocatorQuarantine.Put(&ThreadQuarantineCache, - QuarantineCallback(&Cache), Chunk, UsableSize); + QuarantineCallback(&Cache), Chunk, LiableSize); } else { SpinMutexLock l(&FallbackMutex); AllocatorQuarantine.Put(&FallbackQuarantineCache, QuarantineCallback(&FallbackAllocatorCache), - Chunk, UsableSize); + Chunk, LiableSize); } } // Reallocates a chunk. We can save on a new allocation if the new requested // size still fits in the chunk. void *reallocate(void *OldPtr, uptr NewSize) { if (UNLIKELY(!ThreadInited)) initThread(); - uptr ChunkBeg = reinterpret_cast(OldPtr); - ScudoChunk *Chunk = - reinterpret_cast(ChunkBeg - AlignedChunkHeaderSize); + uptr UserBeg = reinterpret_cast(OldPtr); + if (UNLIKELY(!IsAligned(UserBeg, MinAlignment))) { + dieWithMessage("ERROR: attempted to reallocate a chunk not properly " + "aligned at address %p\n", OldPtr); + } + ScudoChunk *Chunk = getScudoChunk(UserBeg); UnpackedHeader OldHeader; Chunk->loadHeader(&OldHeader); - if (OldHeader.State != ChunkAllocated) { + if (UNLIKELY(OldHeader.State != ChunkAllocated)) { dieWithMessage("ERROR: invalid chunk state when reallocating address " "%p\n", OldPtr); } - uptr Size = Chunk->getUsableSize(&OldHeader); - if (OldHeader.AllocType != FromMalloc) { + if (UNLIKELY(OldHeader.AllocType != FromMalloc)) { dieWithMessage("ERROR: invalid chunk type when reallocating address %p\n", - Chunk); + OldPtr); } + uptr UsableSize = Chunk->getUsableSize(&OldHeader); UnpackedHeader NewHeader = OldHeader; - // The new size still fits in the current chunk. - if (NewSize <= Size) { - NewHeader.UnusedBytes = Size - NewSize; + // The new size still fits in the current chunk, and the size difference + // is reasonable. + if (NewSize <= UsableSize && + (UsableSize - NewSize) < (SizeClassMap::kMaxSize / 2)) { + NewHeader.SizeOrUnusedBytes = + OldHeader.FromPrimary ? NewSize : UsableSize - NewSize; Chunk->compareExchangeHeader(&NewHeader, &OldHeader); return OldPtr; } // Otherwise, we have to allocate a new chunk and copy the contents of the // old one. void *NewPtr = allocate(NewSize, MinAlignment, FromMalloc); if (NewPtr) { - uptr OldSize = Size - OldHeader.UnusedBytes; + uptr OldSize = OldHeader.FromPrimary ? OldHeader.SizeOrUnusedBytes : + UsableSize - OldHeader.SizeOrUnusedBytes; memcpy(NewPtr, OldPtr, Min(NewSize, OldSize)); NewHeader.State = ChunkQuarantine; Chunk->compareExchangeHeader(&NewHeader, &OldHeader); if (LIKELY(!ThreadTornDown)) { AllocatorQuarantine.Put(&ThreadQuarantineCache, - QuarantineCallback(&Cache), Chunk, Size); + QuarantineCallback(&Cache), Chunk, UsableSize); } else { SpinMutexLock l(&FallbackMutex); AllocatorQuarantine.Put(&FallbackQuarantineCache, QuarantineCallback(&FallbackAllocatorCache), - Chunk, Size); + Chunk, UsableSize); } } return NewPtr; } // Helper function that returns the actual usable size of a chunk. uptr getUsableSize(const void *Ptr) { if (UNLIKELY(!ThreadInited)) initThread(); if (!Ptr) return 0; - uptr ChunkBeg = reinterpret_cast(Ptr); - ScudoChunk *Chunk = - reinterpret_cast(ChunkBeg - AlignedChunkHeaderSize); + uptr UserBeg = reinterpret_cast(Ptr); + ScudoChunk *Chunk = getScudoChunk(UserBeg); UnpackedHeader Header; Chunk->loadHeader(&Header); // Getting the usable size of a chunk only makes sense if it's allocated. - if (Header.State != ChunkAllocated) { + if (UNLIKELY(Header.State != ChunkAllocated)) { dieWithMessage("ERROR: invalid chunk state when sizing address %p\n", Ptr); } return Chunk->getUsableSize(&Header); } void *calloc(uptr NMemB, uptr Size) { if (UNLIKELY(!ThreadInited)) initThread(); uptr Total = NMemB * Size; - if (Size != 0 && Total / Size != NMemB) // Overflow check + if (Size != 0 && Total / Size != NMemB) // Overflow check return BackendAllocator.ReturnNullOrDieOnBadRequest(); - void *Ptr = allocate(Total, MinAlignment, FromMalloc); - // If ZeroContents, the content of the chunk has already been zero'd out. - if (!ZeroContents && Ptr && BackendAllocator.FromPrimary(Ptr)) - memset(Ptr, 0, getUsableSize(Ptr)); - return Ptr; + return allocate(Total, MinAlignment, FromMalloc, true); } void drainQuarantine() { AllocatorQuarantine.Drain(&ThreadQuarantineCache, QuarantineCallback(&Cache)); } uptr getStats(AllocatorStat StatType) { if (UNLIKELY(!ThreadInited)) initThread(); uptr stats[AllocatorStatCount]; BackendAllocator.GetStats(stats); return stats[StatType]; } }; -static Allocator Instance(LINKER_INITIALIZED); +static ScudoAllocator Instance(LINKER_INITIALIZED); -static ScudoAllocator &getAllocator() { +static ScudoBackendAllocator &getBackendAllocator() { return Instance.BackendAllocator; } void initAllocator(const AllocatorOptions &Options) { Instance.init(Options); } void drainQuarantine() { Instance.drainQuarantine(); } void *scudoMalloc(uptr Size, AllocType Type) { return Instance.allocate(Size, MinAlignment, Type); } void scudoFree(void *Ptr, AllocType Type) { Instance.deallocate(Ptr, 0, Type); } void scudoSizedFree(void *Ptr, uptr Size, AllocType Type) { Instance.deallocate(Ptr, Size, Type); } void *scudoRealloc(void *Ptr, uptr Size) { if (!Ptr) return Instance.allocate(Size, MinAlignment, FromMalloc); if (Size == 0) { Instance.deallocate(Ptr, 0, FromMalloc); return nullptr; } return Instance.reallocate(Ptr, Size); } void *scudoCalloc(uptr NMemB, uptr Size) { return Instance.calloc(NMemB, Size); } void *scudoValloc(uptr Size) { return Instance.allocate(Size, GetPageSizeCached(), FromMemalign); } void *scudoMemalign(uptr Alignment, uptr Size) { return Instance.allocate(Size, Alignment, FromMemalign); } void *scudoPvalloc(uptr Size) { uptr PageSize = GetPageSizeCached(); Size = RoundUpTo(Size, PageSize); if (Size == 0) { // pvalloc(0) should allocate one page. Size = PageSize; } return Instance.allocate(Size, PageSize, FromMemalign); } int scudoPosixMemalign(void **MemPtr, uptr Alignment, uptr Size) { *MemPtr = Instance.allocate(Size, Alignment, FromMemalign); return 0; } void *scudoAlignedAlloc(uptr Alignment, uptr Size) { // size must be a multiple of the alignment. To avoid a division, we first // make sure that alignment is a power of 2. CHECK(IsPowerOfTwo(Alignment)); CHECK_EQ((Size & (Alignment - 1)), 0); return Instance.allocate(Size, Alignment, FromMalloc); } uptr scudoMallocUsableSize(void *Ptr) { return Instance.getUsableSize(Ptr); } } // namespace __scudo using namespace __scudo; // MallocExtension helper functions uptr __sanitizer_get_current_allocated_bytes() { return Instance.getStats(AllocatorStatAllocated); } uptr __sanitizer_get_heap_size() { return Instance.getStats(AllocatorStatMapped); } uptr __sanitizer_get_free_bytes() { return 1; } uptr __sanitizer_get_unmapped_bytes() { return 1; } uptr __sanitizer_get_estimated_allocated_size(uptr size) { return size; } int __sanitizer_get_ownership(const void *Ptr) { return Instance.isValidPointer(Ptr); } uptr __sanitizer_get_allocated_size(const void *Ptr) { return Instance.getUsableSize(Ptr); } Index: vendor/compiler-rt/dist/lib/scudo/scudo_allocator.h =================================================================== --- vendor/compiler-rt/dist/lib/scudo/scudo_allocator.h (revision 317221) +++ vendor/compiler-rt/dist/lib/scudo/scudo_allocator.h (revision 317222) @@ -1,104 +1,105 @@ //===-- scudo_allocator.h ---------------------------------------*- C++ -*-===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// /// /// Header for scudo_allocator.cpp. /// //===----------------------------------------------------------------------===// #ifndef SCUDO_ALLOCATOR_H_ #define SCUDO_ALLOCATOR_H_ #include "scudo_flags.h" #include "sanitizer_common/sanitizer_allocator.h" #if !SANITIZER_LINUX # error "The Scudo hardened allocator is currently only supported on Linux." #endif namespace __scudo { enum AllocType : u8 { FromMalloc = 0, // Memory block came from malloc, realloc, calloc, etc. FromNew = 1, // Memory block came from operator new. FromNewArray = 2, // Memory block came from operator new []. FromMemalign = 3, // Memory block came from memalign, posix_memalign, etc. }; enum ChunkState : u8 { ChunkAvailable = 0, ChunkAllocated = 1, ChunkQuarantine = 2 }; // Our header requires 64 bits of storage. Having the offset saves us from // using functions such as GetBlockBegin, that is fairly costly. Our first // implementation used the MetaData as well, which offers the advantage of // being stored away from the chunk itself, but accessing it was costly as -// well. The header will be atomically loaded and stored using the 16-byte -// primitives offered by the platform (likely requires cmpxchg16b support). +// well. The header will be atomically loaded and stored. typedef u64 PackedHeader; struct UnpackedHeader { - u64 Checksum : 16; - u64 UnusedBytes : 20; // Needed for reallocation purposes. - u64 State : 2; // available, allocated, or quarantined - u64 AllocType : 2; // malloc, new, new[], or memalign - u64 Offset : 16; // Offset from the beginning of the backend - // allocation to the beginning of the chunk itself, - // in multiples of MinAlignment. See comment about - // its maximum value and test in init(). - u64 Salt : 8; + u64 Checksum : 16; + u64 SizeOrUnusedBytes : 19; // Size for Primary backed allocations, amount of + // unused bytes in the chunk for Secondary ones. + u64 FromPrimary : 1; + u64 State : 2; // available, allocated, or quarantined + u64 AllocType : 2; // malloc, new, new[], or memalign + u64 Offset : 16; // Offset from the beginning of the backend + // allocation to the beginning of the chunk + // itself, in multiples of MinAlignment. See + /// comment about its maximum value and in init(). + u64 Salt : 8; }; typedef atomic_uint64_t AtomicPackedHeader; COMPILER_CHECK(sizeof(UnpackedHeader) == sizeof(PackedHeader)); // Minimum alignment of 8 bytes for 32-bit, 16 for 64-bit const uptr MinAlignmentLog = FIRST_32_SECOND_64(3, 4); const uptr MaxAlignmentLog = 24; // 16 MB const uptr MinAlignment = 1 << MinAlignmentLog; const uptr MaxAlignment = 1 << MaxAlignmentLog; const uptr ChunkHeaderSize = sizeof(PackedHeader); const uptr AlignedChunkHeaderSize = (ChunkHeaderSize + MinAlignment - 1) & ~(MinAlignment - 1); struct AllocatorOptions { u32 QuarantineSizeMb; u32 ThreadLocalQuarantineSizeKb; bool MayReturnNull; s32 ReleaseToOSIntervalMs; bool DeallocationTypeMismatch; bool DeleteSizeMismatch; bool ZeroContents; void setFrom(const Flags *f, const CommonFlags *cf); void copyTo(Flags *f, CommonFlags *cf) const; }; void initAllocator(const AllocatorOptions &options); void drainQuarantine(); void *scudoMalloc(uptr Size, AllocType Type); void scudoFree(void *Ptr, AllocType Type); void scudoSizedFree(void *Ptr, uptr Size, AllocType Type); void *scudoRealloc(void *Ptr, uptr Size); void *scudoCalloc(uptr NMemB, uptr Size); void *scudoMemalign(uptr Alignment, uptr Size); void *scudoValloc(uptr Size); void *scudoPvalloc(uptr Size); int scudoPosixMemalign(void **MemPtr, uptr Alignment, uptr Size); void *scudoAlignedAlloc(uptr Alignment, uptr Size); uptr scudoMallocUsableSize(void *Ptr); #include "scudo_allocator_secondary.h" } // namespace __scudo #endif // SCUDO_ALLOCATOR_H_ Index: vendor/compiler-rt/dist/lib/scudo/scudo_allocator_secondary.h =================================================================== --- vendor/compiler-rt/dist/lib/scudo/scudo_allocator_secondary.h (revision 317221) +++ vendor/compiler-rt/dist/lib/scudo/scudo_allocator_secondary.h (revision 317222) @@ -1,188 +1,195 @@ //===-- scudo_allocator_secondary.h -----------------------------*- C++ -*-===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// /// /// Scudo Secondary Allocator. /// This services allocation that are too large to be serviced by the Primary /// Allocator. It is directly backed by the memory mapping functions of the /// operating system. /// //===----------------------------------------------------------------------===// #ifndef SCUDO_ALLOCATOR_SECONDARY_H_ #define SCUDO_ALLOCATOR_SECONDARY_H_ #ifndef SCUDO_ALLOCATOR_H_ # error "This file must be included inside scudo_allocator.h." #endif class ScudoLargeMmapAllocator { public: void Init(bool AllocatorMayReturnNull) { PageSize = GetPageSizeCached(); atomic_store(&MayReturnNull, AllocatorMayReturnNull, memory_order_relaxed); } void *Allocate(AllocatorStats *Stats, uptr Size, uptr Alignment) { // The Scudo frontend prevents us from allocating more than // MaxAllowedMallocSize, so integer overflow checks would be superfluous. uptr MapSize = Size + SecondaryHeaderSize; MapSize = RoundUpTo(MapSize, PageSize); // Account for 2 guard pages, one before and one after the chunk. MapSize += 2 * PageSize; // The size passed to the Secondary comprises the alignment, if large // enough. Subtract it here to get the requested size, including header. if (Alignment > MinAlignment) Size -= Alignment; uptr MapBeg = reinterpret_cast(MmapNoAccess(MapSize)); if (MapBeg == ~static_cast(0)) return ReturnNullOrDieOnOOM(); // A page-aligned pointer is assumed after that, so check it now. CHECK(IsAligned(MapBeg, PageSize)); uptr MapEnd = MapBeg + MapSize; // The beginning of the user area for that allocation comes after the // initial guard page, and both headers. This is the pointer that has to // abide by alignment requirements. uptr UserBeg = MapBeg + PageSize + HeadersSize; // In the rare event of larger alignments, we will attempt to fit the mmap // area better and unmap extraneous memory. This will also ensure that the // offset and unused bytes field of the header stay small. if (Alignment > MinAlignment) { if (UserBeg & (Alignment - 1)) UserBeg += Alignment - (UserBeg & (Alignment - 1)); CHECK_GE(UserBeg, MapBeg); uptr NewMapBeg = RoundDownTo(UserBeg - HeadersSize, PageSize) - PageSize; CHECK_GE(NewMapBeg, MapBeg); uptr NewMapEnd = RoundUpTo(UserBeg + (Size - AlignedChunkHeaderSize), PageSize) + PageSize; CHECK_LE(NewMapEnd, MapEnd); // Unmap the extra memory if it's large enough, on both sides. uptr Diff = NewMapBeg - MapBeg; if (Diff > PageSize) UnmapOrDie(reinterpret_cast(MapBeg), Diff); Diff = MapEnd - NewMapEnd; if (Diff > PageSize) UnmapOrDie(reinterpret_cast(NewMapEnd), Diff); MapBeg = NewMapBeg; MapEnd = NewMapEnd; MapSize = NewMapEnd - NewMapBeg; } uptr UserEnd = UserBeg + (Size - AlignedChunkHeaderSize); CHECK_LE(UserEnd, MapEnd - PageSize); // Actually mmap the memory, preserving the guard pages on either side. CHECK_EQ(MapBeg + PageSize, reinterpret_cast( MmapFixedOrDie(MapBeg + PageSize, MapSize - 2 * PageSize))); uptr Ptr = UserBeg - AlignedChunkHeaderSize; SecondaryHeader *Header = getHeader(Ptr); Header->MapBeg = MapBeg; Header->MapSize = MapSize; // The primary adds the whole class size to the stats when allocating a // chunk, so we will do something similar here. But we will not account for // the guard pages. - Stats->Add(AllocatorStatAllocated, MapSize - 2 * PageSize); - Stats->Add(AllocatorStatMapped, MapSize - 2 * PageSize); + { + SpinMutexLock l(&StatsMutex); + Stats->Add(AllocatorStatAllocated, MapSize - 2 * PageSize); + Stats->Add(AllocatorStatMapped, MapSize - 2 * PageSize); + } return reinterpret_cast(UserBeg); } void *ReturnNullOrDieOnBadRequest() { if (atomic_load(&MayReturnNull, memory_order_acquire)) return nullptr; ReportAllocatorCannotReturnNull(false); } void *ReturnNullOrDieOnOOM() { if (atomic_load(&MayReturnNull, memory_order_acquire)) return nullptr; ReportAllocatorCannotReturnNull(true); } void SetMayReturnNull(bool AllocatorMayReturnNull) { atomic_store(&MayReturnNull, AllocatorMayReturnNull, memory_order_release); } void Deallocate(AllocatorStats *Stats, void *Ptr) { SecondaryHeader *Header = getHeader(Ptr); - Stats->Sub(AllocatorStatAllocated, Header->MapSize - 2 * PageSize); - Stats->Sub(AllocatorStatMapped, Header->MapSize - 2 * PageSize); + { + SpinMutexLock l(&StatsMutex); + Stats->Sub(AllocatorStatAllocated, Header->MapSize - 2 * PageSize); + Stats->Sub(AllocatorStatMapped, Header->MapSize - 2 * PageSize); + } UnmapOrDie(reinterpret_cast(Header->MapBeg), Header->MapSize); } uptr TotalMemoryUsed() { UNIMPLEMENTED(); } bool PointerIsMine(const void *Ptr) { UNIMPLEMENTED(); } uptr GetActuallyAllocatedSize(void *Ptr) { SecondaryHeader *Header = getHeader(Ptr); - // Deduct PageSize as MapEnd includes the trailing guard page. + // Deduct PageSize as MapSize includes the trailing guard page. uptr MapEnd = Header->MapBeg + Header->MapSize - PageSize; return MapEnd - reinterpret_cast(Ptr); } void *GetMetaData(const void *Ptr) { UNIMPLEMENTED(); } void *GetBlockBegin(const void *Ptr) { UNIMPLEMENTED(); } void *GetBlockBeginFastLocked(void *Ptr) { UNIMPLEMENTED(); } void PrintStats() { UNIMPLEMENTED(); } void ForceLock() { UNIMPLEMENTED(); } void ForceUnlock() { UNIMPLEMENTED(); } void ForEachChunk(ForEachChunkCallback Callback, void *Arg) { UNIMPLEMENTED(); } private: // A Secondary allocated chunk header contains the base of the mapping and // its size. Currently, the base is always a page before the header, but // we might want to extend that number in the future based on the size of // the allocation. struct SecondaryHeader { uptr MapBeg; uptr MapSize; }; // Check that sizeof(SecondaryHeader) is a multiple of MinAlignment. COMPILER_CHECK((sizeof(SecondaryHeader) & (MinAlignment - 1)) == 0); SecondaryHeader *getHeader(uptr Ptr) { return reinterpret_cast(Ptr - sizeof(SecondaryHeader)); } SecondaryHeader *getHeader(const void *Ptr) { return getHeader(reinterpret_cast(Ptr)); } const uptr SecondaryHeaderSize = sizeof(SecondaryHeader); const uptr HeadersSize = SecondaryHeaderSize + AlignedChunkHeaderSize; uptr PageSize; + SpinMutex StatsMutex; atomic_uint8_t MayReturnNull; }; #endif // SCUDO_ALLOCATOR_SECONDARY_H_ Index: vendor/compiler-rt/dist/lib/scudo/scudo_utils.cpp =================================================================== --- vendor/compiler-rt/dist/lib/scudo/scudo_utils.cpp (revision 317221) +++ vendor/compiler-rt/dist/lib/scudo/scudo_utils.cpp (revision 317222) @@ -1,216 +1,162 @@ //===-- scudo_utils.cpp -----------------------------------------*- C++ -*-===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// /// /// Platform specific utility functions. /// //===----------------------------------------------------------------------===// #include "scudo_utils.h" #include #include #include #include #if defined(__x86_64__) || defined(__i386__) # include #endif #if defined(__arm__) || defined(__aarch64__) # include #endif // TODO(kostyak): remove __sanitizer *Printf uses in favor for our own less // complicated string formatting code. The following is a // temporary workaround to be able to use __sanitizer::VSNPrintf. namespace __sanitizer { extern int VSNPrintf(char *buff, int buff_length, const char *format, va_list args); } // namespace __sanitizer namespace __scudo { FORMAT(1, 2) void NORETURN dieWithMessage(const char *Format, ...) { // Our messages are tiny, 256 characters is more than enough. char Message[256]; va_list Args; va_start(Args, Format); __sanitizer::VSNPrintf(Message, sizeof(Message), Format, Args); va_end(Args); RawWrite(Message); Die(); } #if defined(__x86_64__) || defined(__i386__) // i386 and x86_64 specific code to detect CRC32 hardware support via CPUID. // CRC32 requires the SSE 4.2 instruction set. typedef struct { u32 Eax; u32 Ebx; u32 Ecx; u32 Edx; } CPUIDRegs; static void getCPUID(CPUIDRegs *Regs, u32 Level) { __get_cpuid(Level, &Regs->Eax, &Regs->Ebx, &Regs->Ecx, &Regs->Edx); } CPUIDRegs getCPUFeatures() { CPUIDRegs VendorRegs = {}; getCPUID(&VendorRegs, 0); bool IsIntel = (VendorRegs.Ebx == signature_INTEL_ebx) && (VendorRegs.Edx == signature_INTEL_edx) && (VendorRegs.Ecx == signature_INTEL_ecx); bool IsAMD = (VendorRegs.Ebx == signature_AMD_ebx) && (VendorRegs.Edx == signature_AMD_edx) && (VendorRegs.Ecx == signature_AMD_ecx); // Default to an empty feature set if not on a supported CPU. CPUIDRegs FeaturesRegs = {}; if (IsIntel || IsAMD) { getCPUID(&FeaturesRegs, 1); } return FeaturesRegs; } #ifndef bit_SSE4_2 # define bit_SSE4_2 bit_SSE42 // clang and gcc have different defines. #endif bool testCPUFeature(CPUFeature Feature) { CPUIDRegs FeaturesRegs = getCPUFeatures(); switch (Feature) { case CRC32CPUFeature: // CRC32 is provided by SSE 4.2. return !!(FeaturesRegs.Ecx & bit_SSE4_2); default: break; } return false; } #elif defined(__arm__) || defined(__aarch64__) // For ARM and AArch64, hardware CRC32 support is indicated in the // AT_HWVAL auxiliary vector. #ifndef HWCAP_CRC32 # define HWCAP_CRC32 (1<<7) // HWCAP_CRC32 is missing on older platforms. #endif bool testCPUFeature(CPUFeature Feature) { uptr HWCap = getauxval(AT_HWCAP); switch (Feature) { case CRC32CPUFeature: return !!(HWCap & HWCAP_CRC32); default: break; } return false; } #else bool testCPUFeature(CPUFeature Feature) { return false; } #endif // defined(__x86_64__) || defined(__i386__) // readRetry will attempt to read Count bytes from the Fd specified, and if // interrupted will retry to read additional bytes to reach Count. static ssize_t readRetry(int Fd, u8 *Buffer, size_t Count) { ssize_t AmountRead = 0; while (static_cast(AmountRead) < Count) { ssize_t Result = read(Fd, Buffer + AmountRead, Count - AmountRead); if (Result > 0) AmountRead += Result; else if (!Result) break; else if (errno != EINTR) { AmountRead = -1; break; } } return AmountRead; } static void fillRandom(u8 *Data, ssize_t Size) { int Fd = open("/dev/urandom", O_RDONLY); if (Fd < 0) { dieWithMessage("ERROR: failed to open /dev/urandom.\n"); } bool Success = readRetry(Fd, Data, Size) == Size; close(Fd); if (!Success) { dieWithMessage("ERROR: failed to read enough data from /dev/urandom.\n"); } } // Default constructor for Xorshift128Plus seeds the state with /dev/urandom. // TODO(kostyak): investigate using getrandom() if available. Xorshift128Plus::Xorshift128Plus() { fillRandom(reinterpret_cast(State), sizeof(State)); } -const static u32 CRC32Table[] = { - 0x00000000, 0x77073096, 0xee0e612c, 0x990951ba, 0x076dc419, 0x706af48f, - 0xe963a535, 0x9e6495a3, 0x0edb8832, 0x79dcb8a4, 0xe0d5e91e, 0x97d2d988, - 0x09b64c2b, 0x7eb17cbd, 0xe7b82d07, 0x90bf1d91, 0x1db71064, 0x6ab020f2, - 0xf3b97148, 0x84be41de, 0x1adad47d, 0x6ddde4eb, 0xf4d4b551, 0x83d385c7, - 0x136c9856, 0x646ba8c0, 0xfd62f97a, 0x8a65c9ec, 0x14015c4f, 0x63066cd9, - 0xfa0f3d63, 0x8d080df5, 0x3b6e20c8, 0x4c69105e, 0xd56041e4, 0xa2677172, - 0x3c03e4d1, 0x4b04d447, 0xd20d85fd, 0xa50ab56b, 0x35b5a8fa, 0x42b2986c, - 0xdbbbc9d6, 0xacbcf940, 0x32d86ce3, 0x45df5c75, 0xdcd60dcf, 0xabd13d59, - 0x26d930ac, 0x51de003a, 0xc8d75180, 0xbfd06116, 0x21b4f4b5, 0x56b3c423, - 0xcfba9599, 0xb8bda50f, 0x2802b89e, 0x5f058808, 0xc60cd9b2, 0xb10be924, - 0x2f6f7c87, 0x58684c11, 0xc1611dab, 0xb6662d3d, 0x76dc4190, 0x01db7106, - 0x98d220bc, 0xefd5102a, 0x71b18589, 0x06b6b51f, 0x9fbfe4a5, 0xe8b8d433, - 0x7807c9a2, 0x0f00f934, 0x9609a88e, 0xe10e9818, 0x7f6a0dbb, 0x086d3d2d, - 0x91646c97, 0xe6635c01, 0x6b6b51f4, 0x1c6c6162, 0x856530d8, 0xf262004e, - 0x6c0695ed, 0x1b01a57b, 0x8208f4c1, 0xf50fc457, 0x65b0d9c6, 0x12b7e950, - 0x8bbeb8ea, 0xfcb9887c, 0x62dd1ddf, 0x15da2d49, 0x8cd37cf3, 0xfbd44c65, - 0x4db26158, 0x3ab551ce, 0xa3bc0074, 0xd4bb30e2, 0x4adfa541, 0x3dd895d7, - 0xa4d1c46d, 0xd3d6f4fb, 0x4369e96a, 0x346ed9fc, 0xad678846, 0xda60b8d0, - 0x44042d73, 0x33031de5, 0xaa0a4c5f, 0xdd0d7cc9, 0x5005713c, 0x270241aa, - 0xbe0b1010, 0xc90c2086, 0x5768b525, 0x206f85b3, 0xb966d409, 0xce61e49f, - 0x5edef90e, 0x29d9c998, 0xb0d09822, 0xc7d7a8b4, 0x59b33d17, 0x2eb40d81, - 0xb7bd5c3b, 0xc0ba6cad, 0xedb88320, 0x9abfb3b6, 0x03b6e20c, 0x74b1d29a, - 0xead54739, 0x9dd277af, 0x04db2615, 0x73dc1683, 0xe3630b12, 0x94643b84, - 0x0d6d6a3e, 0x7a6a5aa8, 0xe40ecf0b, 0x9309ff9d, 0x0a00ae27, 0x7d079eb1, - 0xf00f9344, 0x8708a3d2, 0x1e01f268, 0x6906c2fe, 0xf762575d, 0x806567cb, - 0x196c3671, 0x6e6b06e7, 0xfed41b76, 0x89d32be0, 0x10da7a5a, 0x67dd4acc, - 0xf9b9df6f, 0x8ebeeff9, 0x17b7be43, 0x60b08ed5, 0xd6d6a3e8, 0xa1d1937e, - 0x38d8c2c4, 0x4fdff252, 0xd1bb67f1, 0xa6bc5767, 0x3fb506dd, 0x48b2364b, - 0xd80d2bda, 0xaf0a1b4c, 0x36034af6, 0x41047a60, 0xdf60efc3, 0xa867df55, - 0x316e8eef, 0x4669be79, 0xcb61b38c, 0xbc66831a, 0x256fd2a0, 0x5268e236, - 0xcc0c7795, 0xbb0b4703, 0x220216b9, 0x5505262f, 0xc5ba3bbe, 0xb2bd0b28, - 0x2bb45a92, 0x5cb36a04, 0xc2d7ffa7, 0xb5d0cf31, 0x2cd99e8b, 0x5bdeae1d, - 0x9b64c2b0, 0xec63f226, 0x756aa39c, 0x026d930a, 0x9c0906a9, 0xeb0e363f, - 0x72076785, 0x05005713, 0x95bf4a82, 0xe2b87a14, 0x7bb12bae, 0x0cb61b38, - 0x92d28e9b, 0xe5d5be0d, 0x7cdcefb7, 0x0bdbdf21, 0x86d3d2d4, 0xf1d4e242, - 0x68ddb3f8, 0x1fda836e, 0x81be16cd, 0xf6b9265b, 0x6fb077e1, 0x18b74777, - 0x88085ae6, 0xff0f6a70, 0x66063bca, 0x11010b5c, 0x8f659eff, 0xf862ae69, - 0x616bffd3, 0x166ccf45, 0xa00ae278, 0xd70dd2ee, 0x4e048354, 0x3903b3c2, - 0xa7672661, 0xd06016f7, 0x4969474d, 0x3e6e77db, 0xaed16a4a, 0xd9d65adc, - 0x40df0b66, 0x37d83bf0, 0xa9bcae53, 0xdebb9ec5, 0x47b2cf7f, 0x30b5ffe9, - 0xbdbdf21c, 0xcabac28a, 0x53b39330, 0x24b4a3a6, 0xbad03605, 0xcdd70693, - 0x54de5729, 0x23d967bf, 0xb3667a2e, 0xc4614ab8, 0x5d681b02, 0x2a6f2b94, - 0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, 0x2d02ef8d -}; - -u32 computeSoftwareCRC32(u32 Crc, uptr Data) { - for (uptr i = 0; i < sizeof(Data); i++) { - Crc = CRC32Table[(Crc ^ Data) & 0xff] ^ (Crc >> 8); - Data >>= 8; - } - return Crc; -} - } // namespace __scudo Index: vendor/compiler-rt/dist/lib/scudo/scudo_utils.h =================================================================== --- vendor/compiler-rt/dist/lib/scudo/scudo_utils.h (revision 317221) +++ vendor/compiler-rt/dist/lib/scudo/scudo_utils.h (revision 317222) @@ -1,65 +1,117 @@ //===-- scudo_utils.h -------------------------------------------*- C++ -*-===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// /// /// Header for scudo_utils.cpp. /// //===----------------------------------------------------------------------===// #ifndef SCUDO_UTILS_H_ #define SCUDO_UTILS_H_ #include #include "sanitizer_common/sanitizer_common.h" namespace __scudo { template inline Dest bit_cast(const Source& source) { static_assert(sizeof(Dest) == sizeof(Source), "Sizes are not equal!"); Dest dest; memcpy(&dest, &source, sizeof(dest)); return dest; } void NORETURN dieWithMessage(const char *Format, ...); enum CPUFeature { CRC32CPUFeature = 0, MaxCPUFeature, }; bool testCPUFeature(CPUFeature feature); // Tiny PRNG based on https://en.wikipedia.org/wiki/Xorshift#xorshift.2B // The state (128 bits) will be stored in thread local storage. struct Xorshift128Plus { public: Xorshift128Plus(); - u64 Next() { + u64 getNext() { u64 x = State[0]; const u64 y = State[1]; State[0] = y; x ^= x << 23; State[1] = x ^ y ^ (x >> 17) ^ (y >> 26); return State[1] + y; } private: u64 State[2]; }; enum : u8 { CRC32Software = 0, CRC32Hardware = 1, }; -u32 computeSoftwareCRC32(u32 Crc, uptr Data); +const static u32 CRC32Table[] = { + 0x00000000, 0x77073096, 0xee0e612c, 0x990951ba, 0x076dc419, 0x706af48f, + 0xe963a535, 0x9e6495a3, 0x0edb8832, 0x79dcb8a4, 0xe0d5e91e, 0x97d2d988, + 0x09b64c2b, 0x7eb17cbd, 0xe7b82d07, 0x90bf1d91, 0x1db71064, 0x6ab020f2, + 0xf3b97148, 0x84be41de, 0x1adad47d, 0x6ddde4eb, 0xf4d4b551, 0x83d385c7, + 0x136c9856, 0x646ba8c0, 0xfd62f97a, 0x8a65c9ec, 0x14015c4f, 0x63066cd9, + 0xfa0f3d63, 0x8d080df5, 0x3b6e20c8, 0x4c69105e, 0xd56041e4, 0xa2677172, + 0x3c03e4d1, 0x4b04d447, 0xd20d85fd, 0xa50ab56b, 0x35b5a8fa, 0x42b2986c, + 0xdbbbc9d6, 0xacbcf940, 0x32d86ce3, 0x45df5c75, 0xdcd60dcf, 0xabd13d59, + 0x26d930ac, 0x51de003a, 0xc8d75180, 0xbfd06116, 0x21b4f4b5, 0x56b3c423, + 0xcfba9599, 0xb8bda50f, 0x2802b89e, 0x5f058808, 0xc60cd9b2, 0xb10be924, + 0x2f6f7c87, 0x58684c11, 0xc1611dab, 0xb6662d3d, 0x76dc4190, 0x01db7106, + 0x98d220bc, 0xefd5102a, 0x71b18589, 0x06b6b51f, 0x9fbfe4a5, 0xe8b8d433, + 0x7807c9a2, 0x0f00f934, 0x9609a88e, 0xe10e9818, 0x7f6a0dbb, 0x086d3d2d, + 0x91646c97, 0xe6635c01, 0x6b6b51f4, 0x1c6c6162, 0x856530d8, 0xf262004e, + 0x6c0695ed, 0x1b01a57b, 0x8208f4c1, 0xf50fc457, 0x65b0d9c6, 0x12b7e950, + 0x8bbeb8ea, 0xfcb9887c, 0x62dd1ddf, 0x15da2d49, 0x8cd37cf3, 0xfbd44c65, + 0x4db26158, 0x3ab551ce, 0xa3bc0074, 0xd4bb30e2, 0x4adfa541, 0x3dd895d7, + 0xa4d1c46d, 0xd3d6f4fb, 0x4369e96a, 0x346ed9fc, 0xad678846, 0xda60b8d0, + 0x44042d73, 0x33031de5, 0xaa0a4c5f, 0xdd0d7cc9, 0x5005713c, 0x270241aa, + 0xbe0b1010, 0xc90c2086, 0x5768b525, 0x206f85b3, 0xb966d409, 0xce61e49f, + 0x5edef90e, 0x29d9c998, 0xb0d09822, 0xc7d7a8b4, 0x59b33d17, 0x2eb40d81, + 0xb7bd5c3b, 0xc0ba6cad, 0xedb88320, 0x9abfb3b6, 0x03b6e20c, 0x74b1d29a, + 0xead54739, 0x9dd277af, 0x04db2615, 0x73dc1683, 0xe3630b12, 0x94643b84, + 0x0d6d6a3e, 0x7a6a5aa8, 0xe40ecf0b, 0x9309ff9d, 0x0a00ae27, 0x7d079eb1, + 0xf00f9344, 0x8708a3d2, 0x1e01f268, 0x6906c2fe, 0xf762575d, 0x806567cb, + 0x196c3671, 0x6e6b06e7, 0xfed41b76, 0x89d32be0, 0x10da7a5a, 0x67dd4acc, + 0xf9b9df6f, 0x8ebeeff9, 0x17b7be43, 0x60b08ed5, 0xd6d6a3e8, 0xa1d1937e, + 0x38d8c2c4, 0x4fdff252, 0xd1bb67f1, 0xa6bc5767, 0x3fb506dd, 0x48b2364b, + 0xd80d2bda, 0xaf0a1b4c, 0x36034af6, 0x41047a60, 0xdf60efc3, 0xa867df55, + 0x316e8eef, 0x4669be79, 0xcb61b38c, 0xbc66831a, 0x256fd2a0, 0x5268e236, + 0xcc0c7795, 0xbb0b4703, 0x220216b9, 0x5505262f, 0xc5ba3bbe, 0xb2bd0b28, + 0x2bb45a92, 0x5cb36a04, 0xc2d7ffa7, 0xb5d0cf31, 0x2cd99e8b, 0x5bdeae1d, + 0x9b64c2b0, 0xec63f226, 0x756aa39c, 0x026d930a, 0x9c0906a9, 0xeb0e363f, + 0x72076785, 0x05005713, 0x95bf4a82, 0xe2b87a14, 0x7bb12bae, 0x0cb61b38, + 0x92d28e9b, 0xe5d5be0d, 0x7cdcefb7, 0x0bdbdf21, 0x86d3d2d4, 0xf1d4e242, + 0x68ddb3f8, 0x1fda836e, 0x81be16cd, 0xf6b9265b, 0x6fb077e1, 0x18b74777, + 0x88085ae6, 0xff0f6a70, 0x66063bca, 0x11010b5c, 0x8f659eff, 0xf862ae69, + 0x616bffd3, 0x166ccf45, 0xa00ae278, 0xd70dd2ee, 0x4e048354, 0x3903b3c2, + 0xa7672661, 0xd06016f7, 0x4969474d, 0x3e6e77db, 0xaed16a4a, 0xd9d65adc, + 0x40df0b66, 0x37d83bf0, 0xa9bcae53, 0xdebb9ec5, 0x47b2cf7f, 0x30b5ffe9, + 0xbdbdf21c, 0xcabac28a, 0x53b39330, 0x24b4a3a6, 0xbad03605, 0xcdd70693, + 0x54de5729, 0x23d967bf, 0xb3667a2e, 0xc4614ab8, 0x5d681b02, 0x2a6f2b94, + 0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, 0x2d02ef8d +}; + +INLINE u32 computeSoftwareCRC32(u32 Crc, uptr Data) { + for (uptr i = 0; i < sizeof(Data); i++) { + Crc = CRC32Table[(Crc ^ Data) & 0xff] ^ (Crc >> 8); + Data >>= 8; + } + return Crc; +} } // namespace __scudo #endif // SCUDO_UTILS_H_ Index: vendor/compiler-rt/dist/lib/tsan/rtl/tsan_debugging.cc =================================================================== --- vendor/compiler-rt/dist/lib/tsan/rtl/tsan_debugging.cc (revision 317221) +++ vendor/compiler-rt/dist/lib/tsan/rtl/tsan_debugging.cc (revision 317222) @@ -1,250 +1,250 @@ //===-- tsan_debugging.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 ThreadSanitizer (TSan), a race detector. // // TSan debugging API implementation. //===----------------------------------------------------------------------===// #include "tsan_interface.h" #include "tsan_report.h" #include "tsan_rtl.h" #include "sanitizer_common/sanitizer_stackdepot.h" using namespace __tsan; static const char *ReportTypeDescription(ReportType typ) { if (typ == ReportTypeRace) return "data-race"; if (typ == ReportTypeVptrRace) return "data-race-vptr"; if (typ == ReportTypeUseAfterFree) return "heap-use-after-free"; if (typ == ReportTypeVptrUseAfterFree) return "heap-use-after-free-vptr"; if (typ == ReportTypeExternalRace) return "external-race"; if (typ == ReportTypeThreadLeak) return "thread-leak"; if (typ == ReportTypeMutexDestroyLocked) return "locked-mutex-destroy"; if (typ == ReportTypeMutexDoubleLock) return "mutex-double-lock"; if (typ == ReportTypeMutexInvalidAccess) return "mutex-invalid-access"; if (typ == ReportTypeMutexBadUnlock) return "mutex-bad-unlock"; if (typ == ReportTypeMutexBadReadLock) return "mutex-bad-read-lock"; if (typ == ReportTypeMutexBadReadUnlock) return "mutex-bad-read-unlock"; if (typ == ReportTypeSignalUnsafe) return "signal-unsafe-call"; if (typ == ReportTypeErrnoInSignal) return "errno-in-signal-handler"; if (typ == ReportTypeDeadlock) return "lock-order-inversion"; return ""; } static const char *ReportLocationTypeDescription(ReportLocationType typ) { if (typ == ReportLocationGlobal) return "global"; if (typ == ReportLocationHeap) return "heap"; if (typ == ReportLocationStack) return "stack"; if (typ == ReportLocationTLS) return "tls"; if (typ == ReportLocationFD) return "fd"; return ""; } static void CopyTrace(SymbolizedStack *first_frame, void **trace, uptr trace_size) { uptr i = 0; for (SymbolizedStack *frame = first_frame; frame != nullptr; frame = frame->next) { trace[i++] = (void *)frame->info.address; if (i >= trace_size) break; } } // Meant to be called by the debugger. SANITIZER_INTERFACE_ATTRIBUTE void *__tsan_get_current_report() { return const_cast(cur_thread()->current_report); } SANITIZER_INTERFACE_ATTRIBUTE int __tsan_get_report_data(void *report, const char **description, int *count, int *stack_count, int *mop_count, int *loc_count, int *mutex_count, int *thread_count, int *unique_tid_count, void **sleep_trace, uptr trace_size) { const ReportDesc *rep = (ReportDesc *)report; *description = ReportTypeDescription(rep->typ); *count = rep->count; *stack_count = rep->stacks.Size(); *mop_count = rep->mops.Size(); *loc_count = rep->locs.Size(); *mutex_count = rep->mutexes.Size(); *thread_count = rep->threads.Size(); *unique_tid_count = rep->unique_tids.Size(); if (rep->sleep) CopyTrace(rep->sleep->frames, sleep_trace, trace_size); return 1; } SANITIZER_INTERFACE_ATTRIBUTE int __tsan_get_report_stack(void *report, uptr idx, void **trace, uptr trace_size) { const ReportDesc *rep = (ReportDesc *)report; CHECK_LT(idx, rep->stacks.Size()); ReportStack *stack = rep->stacks[idx]; if (stack) CopyTrace(stack->frames, trace, trace_size); return stack ? 1 : 0; } SANITIZER_INTERFACE_ATTRIBUTE int __tsan_get_report_mop(void *report, uptr idx, int *tid, void **addr, int *size, int *write, int *atomic, void **trace, uptr trace_size) { const ReportDesc *rep = (ReportDesc *)report; CHECK_LT(idx, rep->mops.Size()); ReportMop *mop = rep->mops[idx]; *tid = mop->tid; *addr = (void *)mop->addr; *size = mop->size; *write = mop->write ? 1 : 0; *atomic = mop->atomic ? 1 : 0; if (mop->stack) CopyTrace(mop->stack->frames, trace, trace_size); return 1; } SANITIZER_INTERFACE_ATTRIBUTE int __tsan_get_report_loc(void *report, uptr idx, const char **type, void **addr, uptr *start, uptr *size, int *tid, int *fd, int *suppressable, void **trace, uptr trace_size) { const ReportDesc *rep = (ReportDesc *)report; CHECK_LT(idx, rep->locs.Size()); ReportLocation *loc = rep->locs[idx]; *type = ReportLocationTypeDescription(loc->type); *addr = (void *)loc->global.start; *start = loc->heap_chunk_start; *size = loc->heap_chunk_size; *tid = loc->tid; *fd = loc->fd; *suppressable = loc->suppressable; if (loc->stack) CopyTrace(loc->stack->frames, trace, trace_size); return 1; } SANITIZER_INTERFACE_ATTRIBUTE int __tsan_get_report_loc_object_type(void *report, uptr idx, const char **object_type) { const ReportDesc *rep = (ReportDesc *)report; CHECK_LT(idx, rep->locs.Size()); ReportLocation *loc = rep->locs[idx]; *object_type = GetObjectTypeFromTag(loc->external_tag); return 1; } SANITIZER_INTERFACE_ATTRIBUTE int __tsan_get_report_mutex(void *report, uptr idx, uptr *mutex_id, void **addr, int *destroyed, void **trace, uptr trace_size) { const ReportDesc *rep = (ReportDesc *)report; CHECK_LT(idx, rep->mutexes.Size()); ReportMutex *mutex = rep->mutexes[idx]; *mutex_id = mutex->id; *addr = (void *)mutex->addr; *destroyed = mutex->destroyed; if (mutex->stack) CopyTrace(mutex->stack->frames, trace, trace_size); return 1; } SANITIZER_INTERFACE_ATTRIBUTE -int __tsan_get_report_thread(void *report, uptr idx, int *tid, uptr *os_id, +int __tsan_get_report_thread(void *report, uptr idx, int *tid, tid_t *os_id, int *running, const char **name, int *parent_tid, void **trace, uptr trace_size) { const ReportDesc *rep = (ReportDesc *)report; CHECK_LT(idx, rep->threads.Size()); ReportThread *thread = rep->threads[idx]; *tid = thread->id; *os_id = thread->os_id; *running = thread->running; *name = thread->name; *parent_tid = thread->parent_tid; if (thread->stack) CopyTrace(thread->stack->frames, trace, trace_size); return 1; } SANITIZER_INTERFACE_ATTRIBUTE int __tsan_get_report_unique_tid(void *report, uptr idx, int *tid) { const ReportDesc *rep = (ReportDesc *)report; CHECK_LT(idx, rep->unique_tids.Size()); *tid = rep->unique_tids[idx]; return 1; } SANITIZER_INTERFACE_ATTRIBUTE const char *__tsan_locate_address(uptr addr, char *name, uptr name_size, uptr *region_address_ptr, uptr *region_size_ptr) { uptr region_address = 0; uptr region_size = 0; const char *region_kind = nullptr; if (name && name_size > 0) name[0] = 0; if (IsMetaMem(addr)) { region_kind = "meta shadow"; } else if (IsShadowMem(addr)) { region_kind = "shadow"; } else { bool is_stack = false; MBlock *b = 0; Allocator *a = allocator(); if (a->PointerIsMine((void *)addr)) { void *block_begin = a->GetBlockBegin((void *)addr); if (block_begin) b = ctx->metamap.GetBlock((uptr)block_begin); } if (b != 0) { region_address = (uptr)allocator()->GetBlockBegin((void *)addr); region_size = b->siz; region_kind = "heap"; } else { // TODO(kuba.brecka): We should not lock. This is supposed to be called // from within the debugger when other threads are stopped. ctx->thread_registry->Lock(); ThreadContext *tctx = IsThreadStackOrTls(addr, &is_stack); ctx->thread_registry->Unlock(); if (tctx) { region_kind = is_stack ? "stack" : "tls"; } else { region_kind = "global"; DataInfo info; if (Symbolizer::GetOrInit()->SymbolizeData(addr, &info)) { internal_strncpy(name, info.name, name_size); region_address = info.start; region_size = info.size; } } } } CHECK(region_kind); if (region_address_ptr) *region_address_ptr = region_address; if (region_size_ptr) *region_size_ptr = region_size; return region_kind; } SANITIZER_INTERFACE_ATTRIBUTE int __tsan_get_alloc_stack(uptr addr, uptr *trace, uptr size, int *thread_id, - uptr *os_id) { + tid_t *os_id) { MBlock *b = 0; Allocator *a = allocator(); if (a->PointerIsMine((void *)addr)) { void *block_begin = a->GetBlockBegin((void *)addr); if (block_begin) b = ctx->metamap.GetBlock((uptr)block_begin); } if (b == 0) return 0; *thread_id = b->tid; // No locking. This is supposed to be called from within the debugger when // other threads are stopped. ThreadContextBase *tctx = ctx->thread_registry->GetThreadLocked(b->tid); *os_id = tctx->os_id; StackTrace stack = StackDepotGet(b->stk); size = Min(size, (uptr)stack.size); for (uptr i = 0; i < size; i++) trace[i] = stack.trace[stack.size - i - 1]; return size; } Index: vendor/compiler-rt/dist/lib/tsan/rtl/tsan_interface.h =================================================================== --- vendor/compiler-rt/dist/lib/tsan/rtl/tsan_interface.h (revision 317221) +++ vendor/compiler-rt/dist/lib/tsan/rtl/tsan_interface.h (revision 317222) @@ -1,402 +1,403 @@ //===-- tsan_interface.h ----------------------------------------*- C++ -*-===// // // 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 ThreadSanitizer (TSan), a race detector. // // The functions declared in this header will be inserted by the instrumentation // module. // This header can be included by the instrumented program or by TSan tests. //===----------------------------------------------------------------------===// #ifndef TSAN_INTERFACE_H #define TSAN_INTERFACE_H #include using __sanitizer::uptr; +using __sanitizer::tid_t; // This header should NOT include any other headers. // All functions in this header are extern "C" and start with __tsan_. #ifdef __cplusplus extern "C" { #endif #if !SANITIZER_GO // This function should be called at the very beginning of the process, // before any instrumented code is executed and before any call to malloc. SANITIZER_INTERFACE_ATTRIBUTE void __tsan_init(); SANITIZER_INTERFACE_ATTRIBUTE void __tsan_flush_memory(); SANITIZER_INTERFACE_ATTRIBUTE void __tsan_read1(void *addr); SANITIZER_INTERFACE_ATTRIBUTE void __tsan_read2(void *addr); SANITIZER_INTERFACE_ATTRIBUTE void __tsan_read4(void *addr); SANITIZER_INTERFACE_ATTRIBUTE void __tsan_read8(void *addr); SANITIZER_INTERFACE_ATTRIBUTE void __tsan_read16(void *addr); SANITIZER_INTERFACE_ATTRIBUTE void __tsan_write1(void *addr); SANITIZER_INTERFACE_ATTRIBUTE void __tsan_write2(void *addr); SANITIZER_INTERFACE_ATTRIBUTE void __tsan_write4(void *addr); SANITIZER_INTERFACE_ATTRIBUTE void __tsan_write8(void *addr); SANITIZER_INTERFACE_ATTRIBUTE void __tsan_write16(void *addr); SANITIZER_INTERFACE_ATTRIBUTE void __tsan_unaligned_read2(const void *addr); SANITIZER_INTERFACE_ATTRIBUTE void __tsan_unaligned_read4(const void *addr); SANITIZER_INTERFACE_ATTRIBUTE void __tsan_unaligned_read8(const void *addr); SANITIZER_INTERFACE_ATTRIBUTE void __tsan_unaligned_read16(const void *addr); SANITIZER_INTERFACE_ATTRIBUTE void __tsan_unaligned_write2(void *addr); SANITIZER_INTERFACE_ATTRIBUTE void __tsan_unaligned_write4(void *addr); SANITIZER_INTERFACE_ATTRIBUTE void __tsan_unaligned_write8(void *addr); SANITIZER_INTERFACE_ATTRIBUTE void __tsan_unaligned_write16(void *addr); SANITIZER_INTERFACE_ATTRIBUTE void __tsan_read1_pc(void *addr, void *pc); SANITIZER_INTERFACE_ATTRIBUTE void __tsan_read2_pc(void *addr, void *pc); SANITIZER_INTERFACE_ATTRIBUTE void __tsan_read4_pc(void *addr, void *pc); SANITIZER_INTERFACE_ATTRIBUTE void __tsan_read8_pc(void *addr, void *pc); SANITIZER_INTERFACE_ATTRIBUTE void __tsan_read16_pc(void *addr, void *pc); SANITIZER_INTERFACE_ATTRIBUTE void __tsan_write1_pc(void *addr, void *pc); SANITIZER_INTERFACE_ATTRIBUTE void __tsan_write2_pc(void *addr, void *pc); SANITIZER_INTERFACE_ATTRIBUTE void __tsan_write4_pc(void *addr, void *pc); SANITIZER_INTERFACE_ATTRIBUTE void __tsan_write8_pc(void *addr, void *pc); SANITIZER_INTERFACE_ATTRIBUTE void __tsan_write16_pc(void *addr, void *pc); SANITIZER_INTERFACE_ATTRIBUTE void __tsan_vptr_read(void **vptr_p); SANITIZER_INTERFACE_ATTRIBUTE void __tsan_vptr_update(void **vptr_p, void *new_val); SANITIZER_INTERFACE_ATTRIBUTE void __tsan_func_entry(void *call_pc); SANITIZER_INTERFACE_ATTRIBUTE void __tsan_func_exit(); SANITIZER_INTERFACE_ATTRIBUTE void __tsan_ignore_thread_begin(); SANITIZER_INTERFACE_ATTRIBUTE void __tsan_ignore_thread_end(); SANITIZER_INTERFACE_ATTRIBUTE void *__tsan_external_register_tag(const char *object_type); SANITIZER_INTERFACE_ATTRIBUTE void __tsan_external_assign_tag(void *addr, void *tag); SANITIZER_INTERFACE_ATTRIBUTE void __tsan_external_read(void *addr, void *caller_pc, void *tag); SANITIZER_INTERFACE_ATTRIBUTE void __tsan_external_write(void *addr, void *caller_pc, void *tag); SANITIZER_INTERFACE_ATTRIBUTE void __tsan_read_range(void *addr, unsigned long size); // NOLINT SANITIZER_INTERFACE_ATTRIBUTE void __tsan_write_range(void *addr, unsigned long size); // NOLINT // User may provide function that would be called right when TSan detects // an error. The argument 'report' is an opaque pointer that can be used to // gather additional information using other TSan report API functions. SANITIZER_INTERFACE_ATTRIBUTE void __tsan_on_report(void *report); // If TSan is currently reporting a detected issue on the current thread, // returns an opaque pointer to the current report. Otherwise returns NULL. SANITIZER_INTERFACE_ATTRIBUTE void *__tsan_get_current_report(); // Returns a report's description (issue type), number of duplicate issues // found, counts of array data (stack traces, memory operations, locations, // mutexes, threads, unique thread IDs) and a stack trace of a sleep() call (if // one was involved in the issue). SANITIZER_INTERFACE_ATTRIBUTE int __tsan_get_report_data(void *report, const char **description, int *count, int *stack_count, int *mop_count, int *loc_count, int *mutex_count, int *thread_count, int *unique_tid_count, void **sleep_trace, uptr trace_size); // Returns information about stack traces included in the report. SANITIZER_INTERFACE_ATTRIBUTE int __tsan_get_report_stack(void *report, uptr idx, void **trace, uptr trace_size); // Returns information about memory operations included in the report. SANITIZER_INTERFACE_ATTRIBUTE int __tsan_get_report_mop(void *report, uptr idx, int *tid, void **addr, int *size, int *write, int *atomic, void **trace, uptr trace_size); // Returns information about locations included in the report. SANITIZER_INTERFACE_ATTRIBUTE int __tsan_get_report_loc(void *report, uptr idx, const char **type, void **addr, uptr *start, uptr *size, int *tid, int *fd, int *suppressable, void **trace, uptr trace_size); SANITIZER_INTERFACE_ATTRIBUTE int __tsan_get_report_loc_object_type(void *report, uptr idx, const char **object_type); // Returns information about mutexes included in the report. SANITIZER_INTERFACE_ATTRIBUTE int __tsan_get_report_mutex(void *report, uptr idx, uptr *mutex_id, void **addr, int *destroyed, void **trace, uptr trace_size); // Returns information about threads included in the report. SANITIZER_INTERFACE_ATTRIBUTE -int __tsan_get_report_thread(void *report, uptr idx, int *tid, uptr *os_id, +int __tsan_get_report_thread(void *report, uptr idx, int *tid, tid_t *os_id, int *running, const char **name, int *parent_tid, void **trace, uptr trace_size); // Returns information about unique thread IDs included in the report. SANITIZER_INTERFACE_ATTRIBUTE int __tsan_get_report_unique_tid(void *report, uptr idx, int *tid); // Returns the type of the pointer (heap, stack, global, ...) and if possible // also the starting address (e.g. of a heap allocation) and size. SANITIZER_INTERFACE_ATTRIBUTE const char *__tsan_locate_address(uptr addr, char *name, uptr name_size, uptr *region_address, uptr *region_size); // Returns the allocation stack for a heap pointer. SANITIZER_INTERFACE_ATTRIBUTE int __tsan_get_alloc_stack(uptr addr, uptr *trace, uptr size, int *thread_id, - uptr *os_id); + tid_t *os_id); #endif // SANITIZER_GO #ifdef __cplusplus } // extern "C" #endif namespace __tsan { // These should match declarations from public tsan_interface_atomic.h header. typedef unsigned char a8; typedef unsigned short a16; // NOLINT typedef unsigned int a32; typedef unsigned long long a64; // NOLINT #if !SANITIZER_GO && (defined(__SIZEOF_INT128__) \ || (__clang_major__ * 100 + __clang_minor__ >= 302)) && !defined(__mips64) __extension__ typedef __int128 a128; # define __TSAN_HAS_INT128 1 #else # define __TSAN_HAS_INT128 0 #endif // Part of ABI, do not change. // http://llvm.org/viewvc/llvm-project/libcxx/trunk/include/atomic?view=markup typedef enum { mo_relaxed, mo_consume, mo_acquire, mo_release, mo_acq_rel, mo_seq_cst } morder; struct ThreadState; extern "C" { SANITIZER_INTERFACE_ATTRIBUTE a8 __tsan_atomic8_load(const volatile a8 *a, morder mo); SANITIZER_INTERFACE_ATTRIBUTE a16 __tsan_atomic16_load(const volatile a16 *a, morder mo); SANITIZER_INTERFACE_ATTRIBUTE a32 __tsan_atomic32_load(const volatile a32 *a, morder mo); SANITIZER_INTERFACE_ATTRIBUTE a64 __tsan_atomic64_load(const volatile a64 *a, morder mo); #if __TSAN_HAS_INT128 SANITIZER_INTERFACE_ATTRIBUTE a128 __tsan_atomic128_load(const volatile a128 *a, morder mo); #endif SANITIZER_INTERFACE_ATTRIBUTE void __tsan_atomic8_store(volatile a8 *a, a8 v, morder mo); SANITIZER_INTERFACE_ATTRIBUTE void __tsan_atomic16_store(volatile a16 *a, a16 v, morder mo); SANITIZER_INTERFACE_ATTRIBUTE void __tsan_atomic32_store(volatile a32 *a, a32 v, morder mo); SANITIZER_INTERFACE_ATTRIBUTE void __tsan_atomic64_store(volatile a64 *a, a64 v, morder mo); #if __TSAN_HAS_INT128 SANITIZER_INTERFACE_ATTRIBUTE void __tsan_atomic128_store(volatile a128 *a, a128 v, morder mo); #endif SANITIZER_INTERFACE_ATTRIBUTE a8 __tsan_atomic8_exchange(volatile a8 *a, a8 v, morder mo); SANITIZER_INTERFACE_ATTRIBUTE a16 __tsan_atomic16_exchange(volatile a16 *a, a16 v, morder mo); SANITIZER_INTERFACE_ATTRIBUTE a32 __tsan_atomic32_exchange(volatile a32 *a, a32 v, morder mo); SANITIZER_INTERFACE_ATTRIBUTE a64 __tsan_atomic64_exchange(volatile a64 *a, a64 v, morder mo); #if __TSAN_HAS_INT128 SANITIZER_INTERFACE_ATTRIBUTE a128 __tsan_atomic128_exchange(volatile a128 *a, a128 v, morder mo); #endif SANITIZER_INTERFACE_ATTRIBUTE a8 __tsan_atomic8_fetch_add(volatile a8 *a, a8 v, morder mo); SANITIZER_INTERFACE_ATTRIBUTE a16 __tsan_atomic16_fetch_add(volatile a16 *a, a16 v, morder mo); SANITIZER_INTERFACE_ATTRIBUTE a32 __tsan_atomic32_fetch_add(volatile a32 *a, a32 v, morder mo); SANITIZER_INTERFACE_ATTRIBUTE a64 __tsan_atomic64_fetch_add(volatile a64 *a, a64 v, morder mo); #if __TSAN_HAS_INT128 SANITIZER_INTERFACE_ATTRIBUTE a128 __tsan_atomic128_fetch_add(volatile a128 *a, a128 v, morder mo); #endif SANITIZER_INTERFACE_ATTRIBUTE a8 __tsan_atomic8_fetch_sub(volatile a8 *a, a8 v, morder mo); SANITIZER_INTERFACE_ATTRIBUTE a16 __tsan_atomic16_fetch_sub(volatile a16 *a, a16 v, morder mo); SANITIZER_INTERFACE_ATTRIBUTE a32 __tsan_atomic32_fetch_sub(volatile a32 *a, a32 v, morder mo); SANITIZER_INTERFACE_ATTRIBUTE a64 __tsan_atomic64_fetch_sub(volatile a64 *a, a64 v, morder mo); #if __TSAN_HAS_INT128 SANITIZER_INTERFACE_ATTRIBUTE a128 __tsan_atomic128_fetch_sub(volatile a128 *a, a128 v, morder mo); #endif SANITIZER_INTERFACE_ATTRIBUTE a8 __tsan_atomic8_fetch_and(volatile a8 *a, a8 v, morder mo); SANITIZER_INTERFACE_ATTRIBUTE a16 __tsan_atomic16_fetch_and(volatile a16 *a, a16 v, morder mo); SANITIZER_INTERFACE_ATTRIBUTE a32 __tsan_atomic32_fetch_and(volatile a32 *a, a32 v, morder mo); SANITIZER_INTERFACE_ATTRIBUTE a64 __tsan_atomic64_fetch_and(volatile a64 *a, a64 v, morder mo); #if __TSAN_HAS_INT128 SANITIZER_INTERFACE_ATTRIBUTE a128 __tsan_atomic128_fetch_and(volatile a128 *a, a128 v, morder mo); #endif SANITIZER_INTERFACE_ATTRIBUTE a8 __tsan_atomic8_fetch_or(volatile a8 *a, a8 v, morder mo); SANITIZER_INTERFACE_ATTRIBUTE a16 __tsan_atomic16_fetch_or(volatile a16 *a, a16 v, morder mo); SANITIZER_INTERFACE_ATTRIBUTE a32 __tsan_atomic32_fetch_or(volatile a32 *a, a32 v, morder mo); SANITIZER_INTERFACE_ATTRIBUTE a64 __tsan_atomic64_fetch_or(volatile a64 *a, a64 v, morder mo); #if __TSAN_HAS_INT128 SANITIZER_INTERFACE_ATTRIBUTE a128 __tsan_atomic128_fetch_or(volatile a128 *a, a128 v, morder mo); #endif SANITIZER_INTERFACE_ATTRIBUTE a8 __tsan_atomic8_fetch_xor(volatile a8 *a, a8 v, morder mo); SANITIZER_INTERFACE_ATTRIBUTE a16 __tsan_atomic16_fetch_xor(volatile a16 *a, a16 v, morder mo); SANITIZER_INTERFACE_ATTRIBUTE a32 __tsan_atomic32_fetch_xor(volatile a32 *a, a32 v, morder mo); SANITIZER_INTERFACE_ATTRIBUTE a64 __tsan_atomic64_fetch_xor(volatile a64 *a, a64 v, morder mo); #if __TSAN_HAS_INT128 SANITIZER_INTERFACE_ATTRIBUTE a128 __tsan_atomic128_fetch_xor(volatile a128 *a, a128 v, morder mo); #endif SANITIZER_INTERFACE_ATTRIBUTE a8 __tsan_atomic8_fetch_nand(volatile a8 *a, a8 v, morder mo); SANITIZER_INTERFACE_ATTRIBUTE a16 __tsan_atomic16_fetch_nand(volatile a16 *a, a16 v, morder mo); SANITIZER_INTERFACE_ATTRIBUTE a32 __tsan_atomic32_fetch_nand(volatile a32 *a, a32 v, morder mo); SANITIZER_INTERFACE_ATTRIBUTE a64 __tsan_atomic64_fetch_nand(volatile a64 *a, a64 v, morder mo); #if __TSAN_HAS_INT128 SANITIZER_INTERFACE_ATTRIBUTE a128 __tsan_atomic128_fetch_nand(volatile a128 *a, a128 v, morder mo); #endif SANITIZER_INTERFACE_ATTRIBUTE int __tsan_atomic8_compare_exchange_strong(volatile a8 *a, a8 *c, a8 v, morder mo, morder fmo); SANITIZER_INTERFACE_ATTRIBUTE int __tsan_atomic16_compare_exchange_strong(volatile a16 *a, a16 *c, a16 v, morder mo, morder fmo); SANITIZER_INTERFACE_ATTRIBUTE int __tsan_atomic32_compare_exchange_strong(volatile a32 *a, a32 *c, a32 v, morder mo, morder fmo); SANITIZER_INTERFACE_ATTRIBUTE int __tsan_atomic64_compare_exchange_strong(volatile a64 *a, a64 *c, a64 v, morder mo, morder fmo); #if __TSAN_HAS_INT128 SANITIZER_INTERFACE_ATTRIBUTE int __tsan_atomic128_compare_exchange_strong(volatile a128 *a, a128 *c, a128 v, morder mo, morder fmo); #endif SANITIZER_INTERFACE_ATTRIBUTE int __tsan_atomic8_compare_exchange_weak(volatile a8 *a, a8 *c, a8 v, morder mo, morder fmo); SANITIZER_INTERFACE_ATTRIBUTE int __tsan_atomic16_compare_exchange_weak(volatile a16 *a, a16 *c, a16 v, morder mo, morder fmo); SANITIZER_INTERFACE_ATTRIBUTE int __tsan_atomic32_compare_exchange_weak(volatile a32 *a, a32 *c, a32 v, morder mo, morder fmo); SANITIZER_INTERFACE_ATTRIBUTE int __tsan_atomic64_compare_exchange_weak(volatile a64 *a, a64 *c, a64 v, morder mo, morder fmo); #if __TSAN_HAS_INT128 SANITIZER_INTERFACE_ATTRIBUTE int __tsan_atomic128_compare_exchange_weak(volatile a128 *a, a128 *c, a128 v, morder mo, morder fmo); #endif SANITIZER_INTERFACE_ATTRIBUTE a8 __tsan_atomic8_compare_exchange_val(volatile a8 *a, a8 c, a8 v, morder mo, morder fmo); SANITIZER_INTERFACE_ATTRIBUTE a16 __tsan_atomic16_compare_exchange_val(volatile a16 *a, a16 c, a16 v, morder mo, morder fmo); SANITIZER_INTERFACE_ATTRIBUTE a32 __tsan_atomic32_compare_exchange_val(volatile a32 *a, a32 c, a32 v, morder mo, morder fmo); SANITIZER_INTERFACE_ATTRIBUTE a64 __tsan_atomic64_compare_exchange_val(volatile a64 *a, a64 c, a64 v, morder mo, morder fmo); #if __TSAN_HAS_INT128 SANITIZER_INTERFACE_ATTRIBUTE a128 __tsan_atomic128_compare_exchange_val(volatile a128 *a, a128 c, a128 v, morder mo, morder fmo); #endif SANITIZER_INTERFACE_ATTRIBUTE void __tsan_atomic_thread_fence(morder mo); SANITIZER_INTERFACE_ATTRIBUTE void __tsan_atomic_signal_fence(morder mo); SANITIZER_INTERFACE_ATTRIBUTE void __tsan_go_atomic32_load(ThreadState *thr, uptr cpc, uptr pc, u8 *a); SANITIZER_INTERFACE_ATTRIBUTE void __tsan_go_atomic64_load(ThreadState *thr, uptr cpc, uptr pc, u8 *a); SANITIZER_INTERFACE_ATTRIBUTE void __tsan_go_atomic32_store(ThreadState *thr, uptr cpc, uptr pc, u8 *a); SANITIZER_INTERFACE_ATTRIBUTE void __tsan_go_atomic64_store(ThreadState *thr, uptr cpc, uptr pc, u8 *a); SANITIZER_INTERFACE_ATTRIBUTE void __tsan_go_atomic32_fetch_add(ThreadState *thr, uptr cpc, uptr pc, u8 *a); SANITIZER_INTERFACE_ATTRIBUTE void __tsan_go_atomic64_fetch_add(ThreadState *thr, uptr cpc, uptr pc, u8 *a); SANITIZER_INTERFACE_ATTRIBUTE void __tsan_go_atomic32_exchange(ThreadState *thr, uptr cpc, uptr pc, u8 *a); SANITIZER_INTERFACE_ATTRIBUTE void __tsan_go_atomic64_exchange(ThreadState *thr, uptr cpc, uptr pc, u8 *a); SANITIZER_INTERFACE_ATTRIBUTE void __tsan_go_atomic32_compare_exchange(ThreadState *thr, uptr cpc, uptr pc, u8 *a); SANITIZER_INTERFACE_ATTRIBUTE void __tsan_go_atomic64_compare_exchange(ThreadState *thr, uptr cpc, uptr pc, u8 *a); } // extern "C" } // namespace __tsan #endif // TSAN_INTERFACE_H Index: vendor/compiler-rt/dist/lib/tsan/rtl/tsan_report.h =================================================================== --- vendor/compiler-rt/dist/lib/tsan/rtl/tsan_report.h (revision 317221) +++ vendor/compiler-rt/dist/lib/tsan/rtl/tsan_report.h (revision 317222) @@ -1,134 +1,134 @@ //===-- tsan_report.h -------------------------------------------*- C++ -*-===// // // 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 ThreadSanitizer (TSan), a race detector. // //===----------------------------------------------------------------------===// #ifndef TSAN_REPORT_H #define TSAN_REPORT_H #include "sanitizer_common/sanitizer_symbolizer.h" #include "tsan_defs.h" #include "tsan_vector.h" namespace __tsan { enum ReportType { ReportTypeRace, ReportTypeVptrRace, ReportTypeUseAfterFree, ReportTypeVptrUseAfterFree, ReportTypeExternalRace, ReportTypeThreadLeak, ReportTypeMutexDestroyLocked, ReportTypeMutexDoubleLock, ReportTypeMutexInvalidAccess, ReportTypeMutexBadUnlock, ReportTypeMutexBadReadLock, ReportTypeMutexBadReadUnlock, ReportTypeSignalUnsafe, ReportTypeErrnoInSignal, ReportTypeDeadlock }; struct ReportStack { SymbolizedStack *frames; bool suppressable; static ReportStack *New(); private: ReportStack(); }; struct ReportMopMutex { u64 id; bool write; }; struct ReportMop { int tid; uptr addr; int size; bool write; bool atomic; uptr external_tag; Vector mset; ReportStack *stack; ReportMop(); }; enum ReportLocationType { ReportLocationGlobal, ReportLocationHeap, ReportLocationStack, ReportLocationTLS, ReportLocationFD }; struct ReportLocation { ReportLocationType type; DataInfo global; uptr heap_chunk_start; uptr heap_chunk_size; uptr external_tag; int tid; int fd; bool suppressable; ReportStack *stack; static ReportLocation *New(ReportLocationType type); private: explicit ReportLocation(ReportLocationType type); }; struct ReportThread { int id; - uptr os_id; + tid_t os_id; bool running; bool workerthread; char *name; u32 parent_tid; ReportStack *stack; }; struct ReportMutex { u64 id; uptr addr; bool destroyed; ReportStack *stack; }; class ReportDesc { public: ReportType typ; Vector stacks; Vector mops; Vector locs; Vector mutexes; Vector threads; Vector unique_tids; ReportStack *sleep; int count; ReportDesc(); ~ReportDesc(); private: ReportDesc(const ReportDesc&); void operator = (const ReportDesc&); }; // Format and output the report to the console/log. No additional logic. void PrintReport(const ReportDesc *rep); void PrintStack(const ReportStack *stack); } // namespace __tsan #endif // TSAN_REPORT_H Index: vendor/compiler-rt/dist/lib/tsan/rtl/tsan_rtl.h =================================================================== --- vendor/compiler-rt/dist/lib/tsan/rtl/tsan_rtl.h (revision 317221) +++ vendor/compiler-rt/dist/lib/tsan/rtl/tsan_rtl.h (revision 317222) @@ -1,827 +1,827 @@ //===-- tsan_rtl.h ----------------------------------------------*- C++ -*-===// // // 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 ThreadSanitizer (TSan), a race detector. // // Main internal TSan header file. // // Ground rules: // - C++ run-time should not be used (static CTORs, RTTI, exceptions, static // function-scope locals) // - All functions/classes/etc reside in namespace __tsan, except for those // declared in tsan_interface.h. // - Platform-specific files should be used instead of ifdefs (*). // - No system headers included in header files (*). // - Platform specific headres included only into platform-specific files (*). // // (*) Except when inlining is critical for performance. //===----------------------------------------------------------------------===// #ifndef TSAN_RTL_H #define TSAN_RTL_H #include "sanitizer_common/sanitizer_allocator.h" #include "sanitizer_common/sanitizer_allocator_internal.h" #include "sanitizer_common/sanitizer_asm.h" #include "sanitizer_common/sanitizer_common.h" #include "sanitizer_common/sanitizer_deadlock_detector_interface.h" #include "sanitizer_common/sanitizer_libignore.h" #include "sanitizer_common/sanitizer_suppressions.h" #include "sanitizer_common/sanitizer_thread_registry.h" #include "tsan_clock.h" #include "tsan_defs.h" #include "tsan_flags.h" #include "tsan_sync.h" #include "tsan_trace.h" #include "tsan_vector.h" #include "tsan_report.h" #include "tsan_platform.h" #include "tsan_mutexset.h" #include "tsan_ignoreset.h" #include "tsan_stack_trace.h" #if SANITIZER_WORDSIZE != 64 # error "ThreadSanitizer is supported only on 64-bit platforms" #endif namespace __tsan { #if !SANITIZER_GO struct MapUnmapCallback; #if defined(__mips64) || defined(__aarch64__) || defined(__powerpc__) static const uptr kAllocatorSpace = 0; static const uptr kAllocatorSize = SANITIZER_MMAP_RANGE_SIZE; static const uptr kAllocatorRegionSizeLog = 20; static const uptr kAllocatorNumRegions = kAllocatorSize >> kAllocatorRegionSizeLog; typedef TwoLevelByteMap<(kAllocatorNumRegions >> 12), 1 << 12, MapUnmapCallback> ByteMap; typedef SizeClassAllocator32 PrimaryAllocator; #else struct AP64 { // Allocator64 parameters. Deliberately using a short name. static const uptr kSpaceBeg = Mapping::kHeapMemBeg; static const uptr kSpaceSize = Mapping::kHeapMemEnd - Mapping::kHeapMemBeg; static const uptr kMetadataSize = 0; typedef DefaultSizeClassMap SizeClassMap; typedef __tsan::MapUnmapCallback MapUnmapCallback; static const uptr kFlags = 0; }; typedef SizeClassAllocator64 PrimaryAllocator; #endif typedef SizeClassAllocatorLocalCache AllocatorCache; typedef LargeMmapAllocator SecondaryAllocator; typedef CombinedAllocator Allocator; Allocator *allocator(); #endif void TsanCheckFailed(const char *file, int line, const char *cond, u64 v1, u64 v2); const u64 kShadowRodata = (u64)-1; // .rodata shadow marker // FastState (from most significant bit): // ignore : 1 // tid : kTidBits // unused : - // history_size : 3 // epoch : kClkBits class FastState { public: FastState(u64 tid, u64 epoch) { x_ = tid << kTidShift; x_ |= epoch; DCHECK_EQ(tid, this->tid()); DCHECK_EQ(epoch, this->epoch()); DCHECK_EQ(GetIgnoreBit(), false); } explicit FastState(u64 x) : x_(x) { } u64 raw() const { return x_; } u64 tid() const { u64 res = (x_ & ~kIgnoreBit) >> kTidShift; return res; } u64 TidWithIgnore() const { u64 res = x_ >> kTidShift; return res; } u64 epoch() const { u64 res = x_ & ((1ull << kClkBits) - 1); return res; } void IncrementEpoch() { u64 old_epoch = epoch(); x_ += 1; DCHECK_EQ(old_epoch + 1, epoch()); (void)old_epoch; } void SetIgnoreBit() { x_ |= kIgnoreBit; } void ClearIgnoreBit() { x_ &= ~kIgnoreBit; } bool GetIgnoreBit() const { return (s64)x_ < 0; } void SetHistorySize(int hs) { CHECK_GE(hs, 0); CHECK_LE(hs, 7); x_ = (x_ & ~(kHistoryMask << kHistoryShift)) | (u64(hs) << kHistoryShift); } ALWAYS_INLINE int GetHistorySize() const { return (int)((x_ >> kHistoryShift) & kHistoryMask); } void ClearHistorySize() { SetHistorySize(0); } ALWAYS_INLINE u64 GetTracePos() const { const int hs = GetHistorySize(); // When hs == 0, the trace consists of 2 parts. const u64 mask = (1ull << (kTracePartSizeBits + hs + 1)) - 1; return epoch() & mask; } private: friend class Shadow; static const int kTidShift = 64 - kTidBits - 1; static const u64 kIgnoreBit = 1ull << 63; static const u64 kFreedBit = 1ull << 63; static const u64 kHistoryShift = kClkBits; static const u64 kHistoryMask = 7; u64 x_; }; // Shadow (from most significant bit): // freed : 1 // tid : kTidBits // is_atomic : 1 // is_read : 1 // size_log : 2 // addr0 : 3 // epoch : kClkBits class Shadow : public FastState { public: explicit Shadow(u64 x) : FastState(x) { } explicit Shadow(const FastState &s) : FastState(s.x_) { ClearHistorySize(); } void SetAddr0AndSizeLog(u64 addr0, unsigned kAccessSizeLog) { DCHECK_EQ((x_ >> kClkBits) & 31, 0); DCHECK_LE(addr0, 7); DCHECK_LE(kAccessSizeLog, 3); x_ |= ((kAccessSizeLog << 3) | addr0) << kClkBits; DCHECK_EQ(kAccessSizeLog, size_log()); DCHECK_EQ(addr0, this->addr0()); } void SetWrite(unsigned kAccessIsWrite) { DCHECK_EQ(x_ & kReadBit, 0); if (!kAccessIsWrite) x_ |= kReadBit; DCHECK_EQ(kAccessIsWrite, IsWrite()); } void SetAtomic(bool kIsAtomic) { DCHECK(!IsAtomic()); if (kIsAtomic) x_ |= kAtomicBit; DCHECK_EQ(IsAtomic(), kIsAtomic); } bool IsAtomic() const { return x_ & kAtomicBit; } bool IsZero() const { return x_ == 0; } static inline bool TidsAreEqual(const Shadow s1, const Shadow s2) { u64 shifted_xor = (s1.x_ ^ s2.x_) >> kTidShift; DCHECK_EQ(shifted_xor == 0, s1.TidWithIgnore() == s2.TidWithIgnore()); return shifted_xor == 0; } static ALWAYS_INLINE bool Addr0AndSizeAreEqual(const Shadow s1, const Shadow s2) { u64 masked_xor = ((s1.x_ ^ s2.x_) >> kClkBits) & 31; return masked_xor == 0; } static ALWAYS_INLINE bool TwoRangesIntersect(Shadow s1, Shadow s2, unsigned kS2AccessSize) { bool res = false; u64 diff = s1.addr0() - s2.addr0(); if ((s64)diff < 0) { // s1.addr0 < s2.addr0 // NOLINT // if (s1.addr0() + size1) > s2.addr0()) return true; if (s1.size() > -diff) res = true; } else { // if (s2.addr0() + kS2AccessSize > s1.addr0()) return true; if (kS2AccessSize > diff) res = true; } DCHECK_EQ(res, TwoRangesIntersectSlow(s1, s2)); DCHECK_EQ(res, TwoRangesIntersectSlow(s2, s1)); return res; } u64 ALWAYS_INLINE addr0() const { return (x_ >> kClkBits) & 7; } u64 ALWAYS_INLINE size() const { return 1ull << size_log(); } bool ALWAYS_INLINE IsWrite() const { return !IsRead(); } bool ALWAYS_INLINE IsRead() const { return x_ & kReadBit; } // The idea behind the freed bit is as follows. // When the memory is freed (or otherwise unaccessible) we write to the shadow // values with tid/epoch related to the free and the freed bit set. // During memory accesses processing the freed bit is considered // as msb of tid. So any access races with shadow with freed bit set // (it is as if write from a thread with which we never synchronized before). // This allows us to detect accesses to freed memory w/o additional // overheads in memory access processing and at the same time restore // tid/epoch of free. void MarkAsFreed() { x_ |= kFreedBit; } bool IsFreed() const { return x_ & kFreedBit; } bool GetFreedAndReset() { bool res = x_ & kFreedBit; x_ &= ~kFreedBit; return res; } bool ALWAYS_INLINE IsBothReadsOrAtomic(bool kIsWrite, bool kIsAtomic) const { bool v = x_ & ((u64(kIsWrite ^ 1) << kReadShift) | (u64(kIsAtomic) << kAtomicShift)); DCHECK_EQ(v, (!IsWrite() && !kIsWrite) || (IsAtomic() && kIsAtomic)); return v; } bool ALWAYS_INLINE IsRWNotWeaker(bool kIsWrite, bool kIsAtomic) const { bool v = ((x_ >> kReadShift) & 3) <= u64((kIsWrite ^ 1) | (kIsAtomic << 1)); DCHECK_EQ(v, (IsAtomic() < kIsAtomic) || (IsAtomic() == kIsAtomic && !IsWrite() <= !kIsWrite)); return v; } bool ALWAYS_INLINE IsRWWeakerOrEqual(bool kIsWrite, bool kIsAtomic) const { bool v = ((x_ >> kReadShift) & 3) >= u64((kIsWrite ^ 1) | (kIsAtomic << 1)); DCHECK_EQ(v, (IsAtomic() > kIsAtomic) || (IsAtomic() == kIsAtomic && !IsWrite() >= !kIsWrite)); return v; } private: static const u64 kReadShift = 5 + kClkBits; static const u64 kReadBit = 1ull << kReadShift; static const u64 kAtomicShift = 6 + kClkBits; static const u64 kAtomicBit = 1ull << kAtomicShift; u64 size_log() const { return (x_ >> (3 + kClkBits)) & 3; } static bool TwoRangesIntersectSlow(const Shadow s1, const Shadow s2) { if (s1.addr0() == s2.addr0()) return true; if (s1.addr0() < s2.addr0() && s1.addr0() + s1.size() > s2.addr0()) return true; if (s2.addr0() < s1.addr0() && s2.addr0() + s2.size() > s1.addr0()) return true; return false; } }; struct ThreadSignalContext; struct JmpBuf { uptr sp; uptr mangled_sp; int int_signal_send; bool in_blocking_func; uptr in_signal_handler; uptr *shadow_stack_pos; }; // A Processor represents a physical thread, or a P for Go. // It is used to store internal resources like allocate cache, and does not // participate in race-detection logic (invisible to end user). // In C++ it is tied to an OS thread just like ThreadState, however ideally // it should be tied to a CPU (this way we will have fewer allocator caches). // In Go it is tied to a P, so there are significantly fewer Processor's than // ThreadState's (which are tied to Gs). // A ThreadState must be wired with a Processor to handle events. struct Processor { ThreadState *thr; // currently wired thread, or nullptr #if !SANITIZER_GO AllocatorCache alloc_cache; InternalAllocatorCache internal_alloc_cache; #endif DenseSlabAllocCache block_cache; DenseSlabAllocCache sync_cache; DenseSlabAllocCache clock_cache; DDPhysicalThread *dd_pt; }; #if !SANITIZER_GO // ScopedGlobalProcessor temporary setups a global processor for the current // thread, if it does not have one. Intended for interceptors that can run // at the very thread end, when we already destroyed the thread processor. struct ScopedGlobalProcessor { ScopedGlobalProcessor(); ~ScopedGlobalProcessor(); }; #endif // This struct is stored in TLS. struct ThreadState { FastState fast_state; // Synch epoch represents the threads's epoch before the last synchronization // action. It allows to reduce number of shadow state updates. // For example, fast_synch_epoch=100, last write to addr X was at epoch=150, // if we are processing write to X from the same thread at epoch=200, // we do nothing, because both writes happen in the same 'synch epoch'. // That is, if another memory access does not race with the former write, // it does not race with the latter as well. // QUESTION: can we can squeeze this into ThreadState::Fast? // E.g. ThreadState::Fast is a 44-bit, 32 are taken by synch_epoch and 12 are // taken by epoch between synchs. // This way we can save one load from tls. u64 fast_synch_epoch; // This is a slow path flag. On fast path, fast_state.GetIgnoreBit() is read. // We do not distinguish beteween ignoring reads and writes // for better performance. int ignore_reads_and_writes; int ignore_sync; // Go does not support ignores. #if !SANITIZER_GO IgnoreSet mop_ignore_set; IgnoreSet sync_ignore_set; #endif // C/C++ uses fixed size shadow stack embed into Trace. // Go uses malloc-allocated shadow stack with dynamic size. uptr *shadow_stack; uptr *shadow_stack_end; uptr *shadow_stack_pos; u64 *racy_shadow_addr; u64 racy_state[2]; MutexSet mset; ThreadClock clock; #if !SANITIZER_GO Vector jmp_bufs; int ignore_interceptors; #endif #if TSAN_COLLECT_STATS u64 stat[StatCnt]; #endif const int tid; const int unique_id; bool in_symbolizer; bool in_ignored_lib; bool is_inited; bool is_dead; bool is_freeing; bool is_vptr_access; uptr external_tag; const uptr stk_addr; const uptr stk_size; const uptr tls_addr; const uptr tls_size; ThreadContext *tctx; #if SANITIZER_DEBUG && !SANITIZER_GO InternalDeadlockDetector internal_deadlock_detector; #endif DDLogicalThread *dd_lt; // Current wired Processor, or nullptr. Required to handle any events. Processor *proc1; #if !SANITIZER_GO Processor *proc() { return proc1; } #else Processor *proc(); #endif atomic_uintptr_t in_signal_handler; ThreadSignalContext *signal_ctx; #if !SANITIZER_GO u32 last_sleep_stack_id; ThreadClock last_sleep_clock; #endif // Set in regions of runtime that must be signal-safe and fork-safe. // If set, malloc must not be called. int nomalloc; const ReportDesc *current_report; explicit ThreadState(Context *ctx, int tid, int unique_id, u64 epoch, unsigned reuse_count, uptr stk_addr, uptr stk_size, uptr tls_addr, uptr tls_size); }; #if !SANITIZER_GO #if SANITIZER_MAC || SANITIZER_ANDROID ThreadState *cur_thread(); void cur_thread_finalize(); #else __attribute__((tls_model("initial-exec"))) extern THREADLOCAL char cur_thread_placeholder[]; INLINE ThreadState *cur_thread() { return reinterpret_cast(&cur_thread_placeholder); } INLINE void cur_thread_finalize() { } #endif // SANITIZER_MAC || SANITIZER_ANDROID #endif // SANITIZER_GO class ThreadContext : public ThreadContextBase { public: explicit ThreadContext(int tid); ~ThreadContext(); ThreadState *thr; u32 creation_stack_id; SyncClock sync; // Epoch at which the thread had started. // If we see an event from the thread stamped by an older epoch, // the event is from a dead thread that shared tid with this thread. u64 epoch0; u64 epoch1; // Override superclass callbacks. void OnDead() override; void OnJoined(void *arg) override; void OnFinished() override; void OnStarted(void *arg) override; void OnCreated(void *arg) override; void OnReset() override; void OnDetached(void *arg) override; }; struct RacyStacks { MD5Hash hash[2]; bool operator==(const RacyStacks &other) const { if (hash[0] == other.hash[0] && hash[1] == other.hash[1]) return true; if (hash[0] == other.hash[1] && hash[1] == other.hash[0]) return true; return false; } }; struct RacyAddress { uptr addr_min; uptr addr_max; }; struct FiredSuppression { ReportType type; uptr pc_or_addr; Suppression *supp; }; struct Context { Context(); bool initialized; bool after_multithreaded_fork; MetaMap metamap; Mutex report_mtx; int nreported; int nmissed_expected; atomic_uint64_t last_symbolize_time_ns; void *background_thread; atomic_uint32_t stop_background_thread; ThreadRegistry *thread_registry; Mutex racy_mtx; Vector racy_stacks; Vector racy_addresses; // Number of fired suppressions may be large enough. Mutex fired_suppressions_mtx; InternalMmapVector fired_suppressions; DDetector *dd; ClockAlloc clock_alloc; Flags flags; u64 stat[StatCnt]; u64 int_alloc_cnt[MBlockTypeCount]; u64 int_alloc_siz[MBlockTypeCount]; }; extern Context *ctx; // The one and the only global runtime context. ALWAYS_INLINE Flags *flags() { return &ctx->flags; } struct ScopedIgnoreInterceptors { ScopedIgnoreInterceptors() { #if !SANITIZER_GO cur_thread()->ignore_interceptors++; #endif } ~ScopedIgnoreInterceptors() { #if !SANITIZER_GO cur_thread()->ignore_interceptors--; #endif } }; class ScopedReport { public: explicit ScopedReport(ReportType typ); ~ScopedReport(); void AddMemoryAccess(uptr addr, uptr external_tag, Shadow s, StackTrace stack, const MutexSet *mset); void AddStack(StackTrace stack, bool suppressable = false); void AddThread(const ThreadContext *tctx, bool suppressable = false); void AddThread(int unique_tid, bool suppressable = false); void AddUniqueTid(int unique_tid); void AddMutex(const SyncVar *s); u64 AddMutex(u64 id); void AddLocation(uptr addr, uptr size); void AddSleep(u32 stack_id); void SetCount(int count); const ReportDesc *GetReport() const; private: ReportDesc *rep_; // Symbolizer makes lots of intercepted calls. If we try to process them, // at best it will cause deadlocks on internal mutexes. ScopedIgnoreInterceptors ignore_interceptors_; void AddDeadMutex(u64 id); ScopedReport(const ScopedReport&); void operator = (const ScopedReport&); }; ThreadContext *IsThreadStackOrTls(uptr addr, bool *is_stack); void RestoreStack(int tid, const u64 epoch, VarSizeStackTrace *stk, MutexSet *mset); template void ObtainCurrentStack(ThreadState *thr, uptr toppc, StackTraceTy *stack) { uptr size = thr->shadow_stack_pos - thr->shadow_stack; uptr start = 0; if (size + !!toppc > kStackTraceMax) { start = size + !!toppc - kStackTraceMax; size = kStackTraceMax - !!toppc; } stack->Init(&thr->shadow_stack[start], size, toppc); } #if TSAN_COLLECT_STATS void StatAggregate(u64 *dst, u64 *src); void StatOutput(u64 *stat); #endif void ALWAYS_INLINE StatInc(ThreadState *thr, StatType typ, u64 n = 1) { #if TSAN_COLLECT_STATS thr->stat[typ] += n; #endif } void ALWAYS_INLINE StatSet(ThreadState *thr, StatType typ, u64 n) { #if TSAN_COLLECT_STATS thr->stat[typ] = n; #endif } void MapShadow(uptr addr, uptr size); void MapThreadTrace(uptr addr, uptr size, const char *name); void DontNeedShadowFor(uptr addr, uptr size); void InitializeShadowMemory(); void InitializeInterceptors(); void InitializeLibIgnore(); void InitializeDynamicAnnotations(); void ForkBefore(ThreadState *thr, uptr pc); void ForkParentAfter(ThreadState *thr, uptr pc); void ForkChildAfter(ThreadState *thr, uptr pc); void ReportRace(ThreadState *thr); bool OutputReport(ThreadState *thr, const ScopedReport &srep); bool IsFiredSuppression(Context *ctx, ReportType type, StackTrace trace); bool IsExpectedReport(uptr addr, uptr size); void PrintMatchedBenignRaces(); const char *GetObjectTypeFromTag(uptr tag); #if defined(TSAN_DEBUG_OUTPUT) && TSAN_DEBUG_OUTPUT >= 1 # define DPrintf Printf #else # define DPrintf(...) #endif #if defined(TSAN_DEBUG_OUTPUT) && TSAN_DEBUG_OUTPUT >= 2 # define DPrintf2 Printf #else # define DPrintf2(...) #endif u32 CurrentStackId(ThreadState *thr, uptr pc); ReportStack *SymbolizeStackId(u32 stack_id); void PrintCurrentStack(ThreadState *thr, uptr pc); void PrintCurrentStackSlow(uptr pc); // uses libunwind void Initialize(ThreadState *thr); int Finalize(ThreadState *thr); void OnUserAlloc(ThreadState *thr, uptr pc, uptr p, uptr sz, bool write); void OnUserFree(ThreadState *thr, uptr pc, uptr p, bool write); void MemoryAccess(ThreadState *thr, uptr pc, uptr addr, int kAccessSizeLog, bool kAccessIsWrite, bool kIsAtomic); void MemoryAccessImpl(ThreadState *thr, uptr addr, int kAccessSizeLog, bool kAccessIsWrite, bool kIsAtomic, u64 *shadow_mem, Shadow cur); void MemoryAccessRange(ThreadState *thr, uptr pc, uptr addr, uptr size, bool is_write); void MemoryAccessRangeStep(ThreadState *thr, uptr pc, uptr addr, uptr size, uptr step, bool is_write); void UnalignedMemoryAccess(ThreadState *thr, uptr pc, uptr addr, int size, bool kAccessIsWrite, bool kIsAtomic); const int kSizeLog1 = 0; const int kSizeLog2 = 1; const int kSizeLog4 = 2; const int kSizeLog8 = 3; void ALWAYS_INLINE MemoryRead(ThreadState *thr, uptr pc, uptr addr, int kAccessSizeLog) { MemoryAccess(thr, pc, addr, kAccessSizeLog, false, false); } void ALWAYS_INLINE MemoryWrite(ThreadState *thr, uptr pc, uptr addr, int kAccessSizeLog) { MemoryAccess(thr, pc, addr, kAccessSizeLog, true, false); } void ALWAYS_INLINE MemoryReadAtomic(ThreadState *thr, uptr pc, uptr addr, int kAccessSizeLog) { MemoryAccess(thr, pc, addr, kAccessSizeLog, false, true); } void ALWAYS_INLINE MemoryWriteAtomic(ThreadState *thr, uptr pc, uptr addr, int kAccessSizeLog) { MemoryAccess(thr, pc, addr, kAccessSizeLog, true, true); } void MemoryResetRange(ThreadState *thr, uptr pc, uptr addr, uptr size); void MemoryRangeFreed(ThreadState *thr, uptr pc, uptr addr, uptr size); void MemoryRangeImitateWrite(ThreadState *thr, uptr pc, uptr addr, uptr size); void ThreadIgnoreBegin(ThreadState *thr, uptr pc, bool save_stack = true); void ThreadIgnoreEnd(ThreadState *thr, uptr pc); void ThreadIgnoreSyncBegin(ThreadState *thr, uptr pc, bool save_stack = true); void ThreadIgnoreSyncEnd(ThreadState *thr, uptr pc); void FuncEntry(ThreadState *thr, uptr pc); void FuncExit(ThreadState *thr); int ThreadCreate(ThreadState *thr, uptr pc, uptr uid, bool detached); -void ThreadStart(ThreadState *thr, int tid, uptr os_id, bool workerthread); +void ThreadStart(ThreadState *thr, int tid, tid_t os_id, bool workerthread); void ThreadFinish(ThreadState *thr); int ThreadTid(ThreadState *thr, uptr pc, uptr uid); void ThreadJoin(ThreadState *thr, uptr pc, int tid); void ThreadDetach(ThreadState *thr, uptr pc, int tid); void ThreadFinalize(ThreadState *thr); void ThreadSetName(ThreadState *thr, const char *name); int ThreadCount(ThreadState *thr); void ProcessPendingSignals(ThreadState *thr); Processor *ProcCreate(); void ProcDestroy(Processor *proc); void ProcWire(Processor *proc, ThreadState *thr); void ProcUnwire(Processor *proc, ThreadState *thr); // Note: the parameter is called flagz, because flags is already taken // by the global function that returns flags. void MutexCreate(ThreadState *thr, uptr pc, uptr addr, u32 flagz = 0); void MutexDestroy(ThreadState *thr, uptr pc, uptr addr); void MutexPreLock(ThreadState *thr, uptr pc, uptr addr, u32 flagz = 0); void MutexPostLock(ThreadState *thr, uptr pc, uptr addr, u32 flagz = 0, int rec = 1); int MutexUnlock(ThreadState *thr, uptr pc, uptr addr, u32 flagz = 0); void MutexPreReadLock(ThreadState *thr, uptr pc, uptr addr, u32 flagz = 0); void MutexPostReadLock(ThreadState *thr, uptr pc, uptr addr, u32 flagz = 0); void MutexReadUnlock(ThreadState *thr, uptr pc, uptr addr); void MutexReadOrWriteUnlock(ThreadState *thr, uptr pc, uptr addr); void MutexRepair(ThreadState *thr, uptr pc, uptr addr); // call on EOWNERDEAD void MutexInvalidAccess(ThreadState *thr, uptr pc, uptr addr); void Acquire(ThreadState *thr, uptr pc, uptr addr); // AcquireGlobal synchronizes the current thread with all other threads. // In terms of happens-before relation, it draws a HB edge from all threads // (where they happen to execute right now) to the current thread. We use it to // handle Go finalizers. Namely, finalizer goroutine executes AcquireGlobal // right before executing finalizers. This provides a coarse, but simple // approximation of the actual required synchronization. void AcquireGlobal(ThreadState *thr, uptr pc); void Release(ThreadState *thr, uptr pc, uptr addr); void ReleaseStore(ThreadState *thr, uptr pc, uptr addr); void AfterSleep(ThreadState *thr, uptr pc); void AcquireImpl(ThreadState *thr, uptr pc, SyncClock *c); void ReleaseImpl(ThreadState *thr, uptr pc, SyncClock *c); void ReleaseStoreImpl(ThreadState *thr, uptr pc, SyncClock *c); void AcquireReleaseImpl(ThreadState *thr, uptr pc, SyncClock *c); // The hacky call uses custom calling convention and an assembly thunk. // It is considerably faster that a normal call for the caller // if it is not executed (it is intended for slow paths from hot functions). // The trick is that the call preserves all registers and the compiler // does not treat it as a call. // If it does not work for you, use normal call. #if !SANITIZER_DEBUG && defined(__x86_64__) && !SANITIZER_MAC // The caller may not create the stack frame for itself at all, // so we create a reserve stack frame for it (1024b must be enough). #define HACKY_CALL(f) \ __asm__ __volatile__("sub $1024, %%rsp;" \ CFI_INL_ADJUST_CFA_OFFSET(1024) \ ".hidden " #f "_thunk;" \ "call " #f "_thunk;" \ "add $1024, %%rsp;" \ CFI_INL_ADJUST_CFA_OFFSET(-1024) \ ::: "memory", "cc"); #else #define HACKY_CALL(f) f() #endif void TraceSwitch(ThreadState *thr); uptr TraceTopPC(ThreadState *thr); uptr TraceSize(); uptr TraceParts(); Trace *ThreadTrace(int tid); extern "C" void __tsan_trace_switch(); void ALWAYS_INLINE TraceAddEvent(ThreadState *thr, FastState fs, EventType typ, u64 addr) { if (!kCollectHistory) return; DCHECK_GE((int)typ, 0); DCHECK_LE((int)typ, 7); DCHECK_EQ(GetLsb(addr, 61), addr); StatInc(thr, StatEvents); u64 pos = fs.GetTracePos(); if (UNLIKELY((pos % kTracePartSize) == 0)) { #if !SANITIZER_GO HACKY_CALL(__tsan_trace_switch); #else TraceSwitch(thr); #endif } Event *trace = (Event*)GetThreadTrace(fs.tid()); Event *evp = &trace[pos]; Event ev = (u64)addr | ((u64)typ << 61); *evp = ev; } #if !SANITIZER_GO uptr ALWAYS_INLINE HeapEnd() { return HeapMemEnd() + PrimaryAllocator::AdditionalSize(); } #endif } // namespace __tsan #endif // TSAN_RTL_H Index: vendor/compiler-rt/dist/lib/tsan/rtl/tsan_rtl_thread.cc =================================================================== --- vendor/compiler-rt/dist/lib/tsan/rtl/tsan_rtl_thread.cc (revision 317221) +++ vendor/compiler-rt/dist/lib/tsan/rtl/tsan_rtl_thread.cc (revision 317222) @@ -1,408 +1,408 @@ //===-- tsan_rtl_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 ThreadSanitizer (TSan), a race detector. // //===----------------------------------------------------------------------===// #include "sanitizer_common/sanitizer_placement_new.h" #include "tsan_rtl.h" #include "tsan_mman.h" #include "tsan_platform.h" #include "tsan_report.h" #include "tsan_sync.h" namespace __tsan { // ThreadContext implementation. ThreadContext::ThreadContext(int tid) : ThreadContextBase(tid) , thr() , sync() , epoch0() , epoch1() { } #if !SANITIZER_GO ThreadContext::~ThreadContext() { } #endif void ThreadContext::OnDead() { CHECK_EQ(sync.size(), 0); } void ThreadContext::OnJoined(void *arg) { ThreadState *caller_thr = static_cast(arg); AcquireImpl(caller_thr, 0, &sync); sync.Reset(&caller_thr->proc()->clock_cache); } struct OnCreatedArgs { ThreadState *thr; uptr pc; }; void ThreadContext::OnCreated(void *arg) { thr = 0; if (tid == 0) return; OnCreatedArgs *args = static_cast(arg); if (!args->thr) // GCD workers don't have a parent thread. return; args->thr->fast_state.IncrementEpoch(); // Can't increment epoch w/o writing to the trace as well. TraceAddEvent(args->thr, args->thr->fast_state, EventTypeMop, 0); ReleaseImpl(args->thr, 0, &sync); creation_stack_id = CurrentStackId(args->thr, args->pc); if (reuse_count == 0) StatInc(args->thr, StatThreadMaxTid); } void ThreadContext::OnReset() { CHECK_EQ(sync.size(), 0); uptr trace_p = GetThreadTrace(tid); ReleaseMemoryPagesToOS(trace_p, trace_p + TraceSize() * sizeof(Event)); //!!! ReleaseMemoryToOS(GetThreadTraceHeader(tid), sizeof(Trace)); } void ThreadContext::OnDetached(void *arg) { ThreadState *thr1 = static_cast(arg); sync.Reset(&thr1->proc()->clock_cache); } struct OnStartedArgs { ThreadState *thr; uptr stk_addr; uptr stk_size; uptr tls_addr; uptr tls_size; }; void ThreadContext::OnStarted(void *arg) { OnStartedArgs *args = static_cast(arg); thr = args->thr; // RoundUp so that one trace part does not contain events // from different threads. epoch0 = RoundUp(epoch1 + 1, kTracePartSize); epoch1 = (u64)-1; new(thr) ThreadState(ctx, tid, unique_id, epoch0, reuse_count, args->stk_addr, args->stk_size, args->tls_addr, args->tls_size); #if !SANITIZER_GO thr->shadow_stack = &ThreadTrace(thr->tid)->shadow_stack[0]; thr->shadow_stack_pos = thr->shadow_stack; thr->shadow_stack_end = thr->shadow_stack + kShadowStackSize; #else // Setup dynamic shadow stack. const int kInitStackSize = 8; thr->shadow_stack = (uptr*)internal_alloc(MBlockShadowStack, kInitStackSize * sizeof(uptr)); thr->shadow_stack_pos = thr->shadow_stack; thr->shadow_stack_end = thr->shadow_stack + kInitStackSize; #endif if (common_flags()->detect_deadlocks) thr->dd_lt = ctx->dd->CreateLogicalThread(unique_id); thr->fast_state.SetHistorySize(flags()->history_size); // Commit switch to the new part of the trace. // TraceAddEvent will reset stack0/mset0 in the new part for us. TraceAddEvent(thr, thr->fast_state, EventTypeMop, 0); thr->fast_synch_epoch = epoch0; AcquireImpl(thr, 0, &sync); StatInc(thr, StatSyncAcquire); sync.Reset(&thr->proc()->clock_cache); thr->is_inited = true; DPrintf("#%d: ThreadStart epoch=%zu stk_addr=%zx stk_size=%zx " "tls_addr=%zx tls_size=%zx\n", tid, (uptr)epoch0, args->stk_addr, args->stk_size, args->tls_addr, args->tls_size); } void ThreadContext::OnFinished() { #if SANITIZER_GO internal_free(thr->shadow_stack); thr->shadow_stack = nullptr; thr->shadow_stack_pos = nullptr; thr->shadow_stack_end = nullptr; #endif if (!detached) { thr->fast_state.IncrementEpoch(); // Can't increment epoch w/o writing to the trace as well. TraceAddEvent(thr, thr->fast_state, EventTypeMop, 0); ReleaseImpl(thr, 0, &sync); } epoch1 = thr->fast_state.epoch(); if (common_flags()->detect_deadlocks) ctx->dd->DestroyLogicalThread(thr->dd_lt); thr->~ThreadState(); #if TSAN_COLLECT_STATS StatAggregate(ctx->stat, thr->stat); #endif thr = 0; } #if !SANITIZER_GO struct ThreadLeak { ThreadContext *tctx; int count; }; static void MaybeReportThreadLeak(ThreadContextBase *tctx_base, void *arg) { Vector &leaks = *(Vector*)arg; ThreadContext *tctx = static_cast(tctx_base); if (tctx->detached || tctx->status != ThreadStatusFinished) return; for (uptr i = 0; i < leaks.Size(); i++) { if (leaks[i].tctx->creation_stack_id == tctx->creation_stack_id) { leaks[i].count++; return; } } ThreadLeak leak = {tctx, 1}; leaks.PushBack(leak); } #endif #if !SANITIZER_GO static void ReportIgnoresEnabled(ThreadContext *tctx, IgnoreSet *set) { if (tctx->tid == 0) { Printf("ThreadSanitizer: main thread finished with ignores enabled\n"); } else { Printf("ThreadSanitizer: thread T%d %s finished with ignores enabled," " created at:\n", tctx->tid, tctx->name); PrintStack(SymbolizeStackId(tctx->creation_stack_id)); } Printf(" One of the following ignores was not ended" " (in order of probability)\n"); for (uptr i = 0; i < set->Size(); i++) { Printf(" Ignore was enabled at:\n"); PrintStack(SymbolizeStackId(set->At(i))); } Die(); } static void ThreadCheckIgnore(ThreadState *thr) { if (ctx->after_multithreaded_fork) return; if (thr->ignore_reads_and_writes) ReportIgnoresEnabled(thr->tctx, &thr->mop_ignore_set); if (thr->ignore_sync) ReportIgnoresEnabled(thr->tctx, &thr->sync_ignore_set); } #else static void ThreadCheckIgnore(ThreadState *thr) {} #endif void ThreadFinalize(ThreadState *thr) { ThreadCheckIgnore(thr); #if !SANITIZER_GO if (!flags()->report_thread_leaks) return; ThreadRegistryLock l(ctx->thread_registry); Vector leaks(MBlockScopedBuf); ctx->thread_registry->RunCallbackForEachThreadLocked( MaybeReportThreadLeak, &leaks); for (uptr i = 0; i < leaks.Size(); i++) { ScopedReport rep(ReportTypeThreadLeak); rep.AddThread(leaks[i].tctx, true); rep.SetCount(leaks[i].count); OutputReport(thr, rep); } #endif } int ThreadCount(ThreadState *thr) { uptr result; ctx->thread_registry->GetNumberOfThreads(0, 0, &result); return (int)result; } int ThreadCreate(ThreadState *thr, uptr pc, uptr uid, bool detached) { StatInc(thr, StatThreadCreate); OnCreatedArgs args = { thr, pc }; u32 parent_tid = thr ? thr->tid : kInvalidTid; // No parent for GCD workers. int tid = ctx->thread_registry->CreateThread(uid, detached, parent_tid, &args); DPrintf("#%d: ThreadCreate tid=%d uid=%zu\n", parent_tid, tid, uid); StatSet(thr, StatThreadMaxAlive, ctx->thread_registry->GetMaxAliveThreads()); return tid; } -void ThreadStart(ThreadState *thr, int tid, uptr os_id, bool workerthread) { +void ThreadStart(ThreadState *thr, int tid, tid_t os_id, bool workerthread) { uptr stk_addr = 0; uptr stk_size = 0; uptr tls_addr = 0; uptr tls_size = 0; #if !SANITIZER_GO GetThreadStackAndTls(tid == 0, &stk_addr, &stk_size, &tls_addr, &tls_size); if (tid) { if (stk_addr && stk_size) MemoryRangeImitateWrite(thr, /*pc=*/ 1, stk_addr, stk_size); if (tls_addr && tls_size) { // Check that the thr object is in tls; const uptr thr_beg = (uptr)thr; const uptr thr_end = (uptr)thr + sizeof(*thr); CHECK_GE(thr_beg, tls_addr); CHECK_LE(thr_beg, tls_addr + tls_size); CHECK_GE(thr_end, tls_addr); CHECK_LE(thr_end, tls_addr + tls_size); // Since the thr object is huge, skip it. MemoryRangeImitateWrite(thr, /*pc=*/ 2, tls_addr, thr_beg - tls_addr); MemoryRangeImitateWrite(thr, /*pc=*/ 2, thr_end, tls_addr + tls_size - thr_end); } } #endif ThreadRegistry *tr = ctx->thread_registry; OnStartedArgs args = { thr, stk_addr, stk_size, tls_addr, tls_size }; tr->StartThread(tid, os_id, workerthread, &args); tr->Lock(); thr->tctx = (ThreadContext*)tr->GetThreadLocked(tid); tr->Unlock(); #if !SANITIZER_GO if (ctx->after_multithreaded_fork) { thr->ignore_interceptors++; ThreadIgnoreBegin(thr, 0); ThreadIgnoreSyncBegin(thr, 0); } #endif } void ThreadFinish(ThreadState *thr) { ThreadCheckIgnore(thr); StatInc(thr, StatThreadFinish); if (thr->stk_addr && thr->stk_size) DontNeedShadowFor(thr->stk_addr, thr->stk_size); if (thr->tls_addr && thr->tls_size) DontNeedShadowFor(thr->tls_addr, thr->tls_size); thr->is_dead = true; ctx->thread_registry->FinishThread(thr->tid); } static bool FindThreadByUid(ThreadContextBase *tctx, void *arg) { uptr uid = (uptr)arg; if (tctx->user_id == uid && tctx->status != ThreadStatusInvalid) { tctx->user_id = 0; return true; } return false; } int ThreadTid(ThreadState *thr, uptr pc, uptr uid) { int res = ctx->thread_registry->FindThread(FindThreadByUid, (void*)uid); DPrintf("#%d: ThreadTid uid=%zu tid=%d\n", thr->tid, uid, res); return res; } void ThreadJoin(ThreadState *thr, uptr pc, int tid) { CHECK_GT(tid, 0); CHECK_LT(tid, kMaxTid); DPrintf("#%d: ThreadJoin tid=%d\n", thr->tid, tid); ctx->thread_registry->JoinThread(tid, thr); } void ThreadDetach(ThreadState *thr, uptr pc, int tid) { CHECK_GT(tid, 0); CHECK_LT(tid, kMaxTid); ctx->thread_registry->DetachThread(tid, thr); } void ThreadSetName(ThreadState *thr, const char *name) { ctx->thread_registry->SetThreadName(thr->tid, name); } void MemoryAccessRange(ThreadState *thr, uptr pc, uptr addr, uptr size, bool is_write) { if (size == 0) return; u64 *shadow_mem = (u64*)MemToShadow(addr); DPrintf2("#%d: MemoryAccessRange: @%p %p size=%d is_write=%d\n", thr->tid, (void*)pc, (void*)addr, (int)size, is_write); #if SANITIZER_DEBUG if (!IsAppMem(addr)) { Printf("Access to non app mem %zx\n", addr); DCHECK(IsAppMem(addr)); } if (!IsAppMem(addr + size - 1)) { Printf("Access to non app mem %zx\n", addr + size - 1); DCHECK(IsAppMem(addr + size - 1)); } if (!IsShadowMem((uptr)shadow_mem)) { Printf("Bad shadow addr %p (%zx)\n", shadow_mem, addr); DCHECK(IsShadowMem((uptr)shadow_mem)); } if (!IsShadowMem((uptr)(shadow_mem + size * kShadowCnt / 8 - 1))) { Printf("Bad shadow addr %p (%zx)\n", shadow_mem + size * kShadowCnt / 8 - 1, addr + size - 1); DCHECK(IsShadowMem((uptr)(shadow_mem + size * kShadowCnt / 8 - 1))); } #endif StatInc(thr, StatMopRange); if (*shadow_mem == kShadowRodata) { // Access to .rodata section, no races here. // Measurements show that it can be 10-20% of all memory accesses. StatInc(thr, StatMopRangeRodata); return; } FastState fast_state = thr->fast_state; if (fast_state.GetIgnoreBit()) return; fast_state.IncrementEpoch(); thr->fast_state = fast_state; TraceAddEvent(thr, fast_state, EventTypeMop, pc); bool unaligned = (addr % kShadowCell) != 0; // Handle unaligned beginning, if any. for (; addr % kShadowCell && size; addr++, size--) { int const kAccessSizeLog = 0; Shadow cur(fast_state); cur.SetWrite(is_write); cur.SetAddr0AndSizeLog(addr & (kShadowCell - 1), kAccessSizeLog); MemoryAccessImpl(thr, addr, kAccessSizeLog, is_write, false, shadow_mem, cur); } if (unaligned) shadow_mem += kShadowCnt; // Handle middle part, if any. for (; size >= kShadowCell; addr += kShadowCell, size -= kShadowCell) { int const kAccessSizeLog = 3; Shadow cur(fast_state); cur.SetWrite(is_write); cur.SetAddr0AndSizeLog(0, kAccessSizeLog); MemoryAccessImpl(thr, addr, kAccessSizeLog, is_write, false, shadow_mem, cur); shadow_mem += kShadowCnt; } // Handle ending, if any. for (; size; addr++, size--) { int const kAccessSizeLog = 0; Shadow cur(fast_state); cur.SetWrite(is_write); cur.SetAddr0AndSizeLog(addr & (kShadowCell - 1), kAccessSizeLog); MemoryAccessImpl(thr, addr, kAccessSizeLog, is_write, false, shadow_mem, cur); } } } // namespace __tsan Index: vendor/compiler-rt/dist/lib/ubsan/ubsan_flags.cc =================================================================== --- vendor/compiler-rt/dist/lib/ubsan/ubsan_flags.cc (revision 317221) +++ vendor/compiler-rt/dist/lib/ubsan/ubsan_flags.cc (revision 317222) @@ -1,74 +1,75 @@ //===-- ubsan_flags.cc ----------------------------------------------------===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// // // Runtime flags for UndefinedBehaviorSanitizer. // //===----------------------------------------------------------------------===// #include "ubsan_platform.h" #if CAN_SANITIZE_UB #include "ubsan_flags.h" #include "sanitizer_common/sanitizer_common.h" #include "sanitizer_common/sanitizer_flags.h" #include "sanitizer_common/sanitizer_flag_parser.h" namespace __ubsan { const char *MaybeCallUbsanDefaultOptions() { return (&__ubsan_default_options) ? __ubsan_default_options() : ""; } Flags ubsan_flags; void Flags::SetDefaults() { #define UBSAN_FLAG(Type, Name, DefaultValue, Description) Name = DefaultValue; #include "ubsan_flags.inc" #undef UBSAN_FLAG } void RegisterUbsanFlags(FlagParser *parser, Flags *f) { #define UBSAN_FLAG(Type, Name, DefaultValue, Description) \ RegisterFlag(parser, #Name, Description, &f->Name); #include "ubsan_flags.inc" #undef UBSAN_FLAG } void InitializeFlags() { SetCommonFlagsDefaults(); { CommonFlags cf; cf.CopyFrom(*common_flags()); cf.print_summary = false; + cf.external_symbolizer_path = GetEnv("UBSAN_SYMBOLIZER_PATH"); OverrideCommonFlags(cf); } Flags *f = flags(); f->SetDefaults(); FlagParser parser; RegisterCommonFlags(&parser); RegisterUbsanFlags(&parser, f); // Override from user-specified string. parser.ParseString(MaybeCallUbsanDefaultOptions()); // Override from environment variable. parser.ParseString(GetEnv("UBSAN_OPTIONS")); InitializeCommonFlags(); if (Verbosity()) ReportUnrecognizedFlags(); if (common_flags()->help) parser.PrintFlagDescriptions(); } } // namespace __ubsan SANITIZER_INTERFACE_WEAK_DEF(const char *, __ubsan_default_options, void) { return ""; } #endif // CAN_SANITIZE_UB Index: vendor/compiler-rt/dist/lib/xray/xray_fdr_logging.cc =================================================================== --- vendor/compiler-rt/dist/lib/xray/xray_fdr_logging.cc (revision 317221) +++ vendor/compiler-rt/dist/lib/xray/xray_fdr_logging.cc (revision 317222) @@ -1,229 +1,236 @@ //===-- xray_fdr_logging.cc ------------------------------------*- C++ -*-===// // // 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 XRay, a dynamic runtime instrumentation system. // // Here we implement the Flight Data Recorder mode for XRay, where we use // compact structures to store records in memory as well as when writing out the // data to files. // //===----------------------------------------------------------------------===// #include "xray_fdr_logging.h" #include #include #include #include #include #include #include #include #include #include "sanitizer_common/sanitizer_atomic.h" #include "sanitizer_common/sanitizer_common.h" #include "xray/xray_interface.h" #include "xray/xray_records.h" #include "xray_buffer_queue.h" #include "xray_defs.h" #include "xray_fdr_logging_impl.h" #include "xray_flags.h" #include "xray_tsc.h" #include "xray_utils.h" namespace __xray { // Global BufferQueue. std::shared_ptr BQ; __sanitizer::atomic_sint32_t LoggingStatus = { XRayLogInitStatus::XRAY_LOG_UNINITIALIZED}; __sanitizer::atomic_sint32_t LogFlushStatus = { XRayLogFlushStatus::XRAY_LOG_NOT_FLUSHING}; std::unique_ptr FDROptions; XRayLogInitStatus fdrLoggingInit(std::size_t BufferSize, std::size_t BufferMax, void *Options, size_t OptionsSize) XRAY_NEVER_INSTRUMENT { if (OptionsSize != sizeof(FDRLoggingOptions)) return static_cast(__sanitizer::atomic_load( &LoggingStatus, __sanitizer::memory_order_acquire)); s32 CurrentStatus = XRayLogInitStatus::XRAY_LOG_UNINITIALIZED; if (!__sanitizer::atomic_compare_exchange_strong( &LoggingStatus, &CurrentStatus, XRayLogInitStatus::XRAY_LOG_INITIALIZING, __sanitizer::memory_order_release)) return static_cast(CurrentStatus); FDROptions.reset(new FDRLoggingOptions()); memcpy(FDROptions.get(), Options, OptionsSize); bool Success = false; BQ = std::make_shared(BufferSize, BufferMax, Success); if (!Success) { Report("BufferQueue init failed.\n"); return XRayLogInitStatus::XRAY_LOG_UNINITIALIZED; } // Install the actual handleArg0 handler after initialising the buffers. __xray_set_handler(fdrLoggingHandleArg0); __sanitizer::atomic_store(&LoggingStatus, XRayLogInitStatus::XRAY_LOG_INITIALIZED, __sanitizer::memory_order_release); Report("XRay FDR init successful.\n"); return XRayLogInitStatus::XRAY_LOG_INITIALIZED; } // Must finalize before flushing. XRayLogFlushStatus fdrLoggingFlush() XRAY_NEVER_INSTRUMENT { if (__sanitizer::atomic_load(&LoggingStatus, __sanitizer::memory_order_acquire) != XRayLogInitStatus::XRAY_LOG_FINALIZED) return XRayLogFlushStatus::XRAY_LOG_NOT_FLUSHING; s32 Result = XRayLogFlushStatus::XRAY_LOG_NOT_FLUSHING; if (!__sanitizer::atomic_compare_exchange_strong( &LogFlushStatus, &Result, XRayLogFlushStatus::XRAY_LOG_FLUSHING, __sanitizer::memory_order_release)) return static_cast(Result); // Make a copy of the BufferQueue pointer to prevent other threads that may be // resetting it from blowing away the queue prematurely while we're dealing // with it. auto LocalBQ = BQ; // We write out the file in the following format: // // 1) We write down the XRay file header with version 1, type FDR_LOG. // 2) Then we use the 'apply' member of the BufferQueue that's live, to // ensure that at this point in time we write down the buffers that have // been released (and marked "used") -- we dump the full buffer for now // (fixed-sized) and let the tools reading the buffers deal with the data // afterwards. // int Fd = FDROptions->Fd; if (Fd == -1) Fd = getLogFD(); if (Fd == -1) { auto Result = XRayLogFlushStatus::XRAY_LOG_NOT_FLUSHING; __sanitizer::atomic_store(&LogFlushStatus, Result, __sanitizer::memory_order_release); return Result; } + // Test for required CPU features and cache the cycle frequency + static bool TSCSupported = probeRequiredCPUFeatures(); + static uint64_t CycleFrequency = TSCSupported ? getTSCFrequency() + : __xray::NanosecondsPerSecond; + XRayFileHeader Header; Header.Version = 1; Header.Type = FileTypes::FDR_LOG; - Header.CycleFrequency = probeRequiredCPUFeatures() - ? getTSCFrequency() : __xray::NanosecondsPerSecond; + Header.CycleFrequency = CycleFrequency; // FIXME: Actually check whether we have 'constant_tsc' and 'nonstop_tsc' // before setting the values in the header. Header.ConstantTSC = 1; Header.NonstopTSC = 1; Header.FdrData = FdrAdditionalHeaderData{LocalBQ->ConfiguredBufferSize()}; retryingWriteAll(Fd, reinterpret_cast(&Header), reinterpret_cast(&Header) + sizeof(Header)); LocalBQ->apply([&](const BufferQueue::Buffer &B) { uint64_t BufferSize = B.Size; if (BufferSize > 0) { retryingWriteAll(Fd, reinterpret_cast(B.Buffer), reinterpret_cast(B.Buffer) + B.Size); } }); __sanitizer::atomic_store(&LogFlushStatus, XRayLogFlushStatus::XRAY_LOG_FLUSHED, __sanitizer::memory_order_release); return XRayLogFlushStatus::XRAY_LOG_FLUSHED; } XRayLogInitStatus fdrLoggingFinalize() XRAY_NEVER_INSTRUMENT { s32 CurrentStatus = XRayLogInitStatus::XRAY_LOG_INITIALIZED; if (!__sanitizer::atomic_compare_exchange_strong( &LoggingStatus, &CurrentStatus, XRayLogInitStatus::XRAY_LOG_FINALIZING, __sanitizer::memory_order_release)) return static_cast(CurrentStatus); // Do special things to make the log finalize itself, and not allow any more // operations to be performed until re-initialized. BQ->finalize(); __sanitizer::atomic_store(&LoggingStatus, XRayLogInitStatus::XRAY_LOG_FINALIZED, __sanitizer::memory_order_release); return XRayLogInitStatus::XRAY_LOG_FINALIZED; } XRayLogInitStatus fdrLoggingReset() XRAY_NEVER_INSTRUMENT { s32 CurrentStatus = XRayLogInitStatus::XRAY_LOG_FINALIZED; if (__sanitizer::atomic_compare_exchange_strong( &LoggingStatus, &CurrentStatus, XRayLogInitStatus::XRAY_LOG_INITIALIZED, __sanitizer::memory_order_release)) return static_cast(CurrentStatus); // Release the in-memory buffer queue. BQ.reset(); // Spin until the flushing status is flushed. s32 CurrentFlushingStatus = XRayLogFlushStatus::XRAY_LOG_FLUSHED; while (__sanitizer::atomic_compare_exchange_weak( &LogFlushStatus, &CurrentFlushingStatus, XRayLogFlushStatus::XRAY_LOG_NOT_FLUSHING, __sanitizer::memory_order_release)) { if (CurrentFlushingStatus == XRayLogFlushStatus::XRAY_LOG_NOT_FLUSHING) break; CurrentFlushingStatus = XRayLogFlushStatus::XRAY_LOG_FLUSHED; } // At this point, we know that the status is flushed, and that we can assume return XRayLogInitStatus::XRAY_LOG_UNINITIALIZED; } void fdrLoggingHandleArg0(int32_t FuncId, XRayEntryType Entry) XRAY_NEVER_INSTRUMENT { // We want to get the TSC as early as possible, so that we can check whether // we've seen this CPU before. We also do it before we load anything else, to // allow for forward progress with the scheduling. unsigned char CPU; uint64_t TSC; - if(probeRequiredCPUFeatures()) { + // Test once for required CPU features + static bool TSCSupported = probeRequiredCPUFeatures(); + + if(TSCSupported) { TSC = __xray::readTSC(CPU); } else { // FIXME: This code needs refactoring as it appears in multiple locations timespec TS; int result = clock_gettime(CLOCK_REALTIME, &TS); if (result != 0) { Report("clock_gettime(2) return %d, errno=%d", result, int(errno)); TS = {0, 0}; } CPU = 0; TSC = TS.tv_sec * __xray::NanosecondsPerSecond + TS.tv_nsec; } __xray_fdr_internal::processFunctionHook(FuncId, Entry, TSC, CPU, clock_gettime, LoggingStatus, BQ); } } // namespace __xray static auto UNUSED Unused = [] { using namespace __xray; if (flags()->xray_fdr_log) { XRayLogImpl Impl{ fdrLoggingInit, fdrLoggingFinalize, fdrLoggingHandleArg0, fdrLoggingFlush, }; __xray_set_log_impl(Impl); } return true; }(); Index: vendor/compiler-rt/dist/lib/xray/xray_inmemory_log.cc =================================================================== --- vendor/compiler-rt/dist/lib/xray/xray_inmemory_log.cc (revision 317221) +++ vendor/compiler-rt/dist/lib/xray/xray_inmemory_log.cc (revision 317222) @@ -1,162 +1,166 @@ //===-- xray_inmemory_log.cc ------------------------------------*- C++ -*-===// // // 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 XRay, a dynamic runtime instrumentation system. // // Implementation of a simple in-memory log of XRay events. This defines a // logging function that's compatible with the XRay handler interface, and // routines for exporting data to files. // //===----------------------------------------------------------------------===// #include #include #include #include #include #include #include #include #include "sanitizer_common/sanitizer_libc.h" #include "xray/xray_records.h" #include "xray_defs.h" #include "xray_flags.h" #include "xray_interface_internal.h" #include "xray_tsc.h" #include "xray_utils.h" // __xray_InMemoryRawLog will use a thread-local aligned buffer capped to a // certain size (32kb by default) and use it as if it were a circular buffer for // events. We store simple fixed-sized entries in the log for external analysis. extern "C" { void __xray_InMemoryRawLog(int32_t FuncId, XRayEntryType Type) XRAY_NEVER_INSTRUMENT; } namespace __xray { std::mutex LogMutex; class ThreadExitFlusher { int Fd; XRayRecord *Start; size_t &Offset; public: explicit ThreadExitFlusher(int Fd, XRayRecord *Start, size_t &Offset) XRAY_NEVER_INSTRUMENT : Fd(Fd), Start(Start), Offset(Offset) {} ~ThreadExitFlusher() XRAY_NEVER_INSTRUMENT { std::lock_guard L(LogMutex); if (Fd > 0 && Start != nullptr) { retryingWriteAll(Fd, reinterpret_cast(Start), reinterpret_cast(Start + Offset)); // Because this thread's exit could be the last one trying to write to the // file and that we're not able to close out the file properly, we sync // instead and hope that the pending writes are flushed as the thread // exits. fsync(Fd); } } }; } // namespace __xray using namespace __xray; static int __xray_OpenLogFile() XRAY_NEVER_INSTRUMENT { int F = getLogFD(); if (F == -1) return -1; + + // Test for required CPU features and cache the cycle frequency + static bool TSCSupported = probeRequiredCPUFeatures(); + static uint64_t CycleFrequency = TSCSupported ? getTSCFrequency() + : __xray::NanosecondsPerSecond; + // Since we're here, we get to write the header. We set it up so that the // header will only be written once, at the start, and let the threads // logging do writes which just append. XRayFileHeader Header; Header.Version = 1; Header.Type = FileTypes::NAIVE_LOG; - Header.CycleFrequency = probeRequiredCPUFeatures() - ? getTSCFrequency() - : __xray::NanosecondsPerSecond; + Header.CycleFrequency = CycleFrequency; // FIXME: Actually check whether we have 'constant_tsc' and 'nonstop_tsc' // before setting the values in the header. Header.ConstantTSC = 1; Header.NonstopTSC = 1; retryingWriteAll(F, reinterpret_cast(&Header), reinterpret_cast(&Header) + sizeof(Header)); return F; } template void __xray_InMemoryRawLog(int32_t FuncId, XRayEntryType Type, RDTSC ReadTSC) XRAY_NEVER_INSTRUMENT { using Buffer = std::aligned_storage::type; static constexpr size_t BuffLen = 1024; thread_local static Buffer InMemoryBuffer[BuffLen] = {}; thread_local static size_t Offset = 0; static int Fd = __xray_OpenLogFile(); if (Fd == -1) return; thread_local __xray::ThreadExitFlusher Flusher( Fd, reinterpret_cast<__xray::XRayRecord *>(InMemoryBuffer), Offset); thread_local pid_t TId = syscall(SYS_gettid); // First we get the useful data, and stuff it into the already aligned buffer // through a pointer offset. auto &R = reinterpret_cast<__xray::XRayRecord *>(InMemoryBuffer)[Offset]; R.RecordType = RecordTypes::NORMAL; R.TSC = ReadTSC(R.CPU); R.TId = TId; R.Type = Type; R.FuncId = FuncId; ++Offset; if (Offset == BuffLen) { std::lock_guard L(LogMutex); auto RecordBuffer = reinterpret_cast<__xray::XRayRecord *>(InMemoryBuffer); retryingWriteAll(Fd, reinterpret_cast(RecordBuffer), reinterpret_cast(RecordBuffer + Offset)); Offset = 0; } } void __xray_InMemoryRawLogRealTSC(int32_t FuncId, XRayEntryType Type) XRAY_NEVER_INSTRUMENT { __xray_InMemoryRawLog(FuncId, Type, __xray::readTSC); } void __xray_InMemoryEmulateTSC(int32_t FuncId, XRayEntryType Type) XRAY_NEVER_INSTRUMENT { __xray_InMemoryRawLog(FuncId, Type, [](uint8_t &CPU) XRAY_NEVER_INSTRUMENT { timespec TS; int result = clock_gettime(CLOCK_REALTIME, &TS); if (result != 0) { Report("clock_gettimg(2) return %d, errno=%d.", result, int(errno)); TS = {0, 0}; } CPU = 0; return TS.tv_sec * __xray::NanosecondsPerSecond + TS.tv_nsec; }); } static auto UNUSED Unused = [] { auto UseRealTSC = probeRequiredCPUFeatures(); if (!UseRealTSC) Report("WARNING: Required CPU features missing for XRay instrumentation, " "using emulation instead.\n"); if (flags()->xray_naive_log) __xray_set_handler(UseRealTSC ? __xray_InMemoryRawLogRealTSC : __xray_InMemoryEmulateTSC); return true; }(); Index: vendor/compiler-rt/dist/lib/xray/xray_trampoline_x86_64.S =================================================================== --- vendor/compiler-rt/dist/lib/xray/xray_trampoline_x86_64.S (revision 317221) +++ vendor/compiler-rt/dist/lib/xray/xray_trampoline_x86_64.S (revision 317222) @@ -1,194 +1,192 @@ //===-- xray_trampoline_x86.s -----------------------------------*- ASM -*-===// // // 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 XRay, a dynamic runtime instrumentation system. // // This implements the X86-specific assembler for the trampolines. // //===----------------------------------------------------------------------===// #include "../builtins/assembly.h" .macro SAVE_REGISTERS - subq $200, %rsp - movupd %xmm0, 184(%rsp) - movupd %xmm1, 168(%rsp) - movupd %xmm2, 152(%rsp) - movupd %xmm3, 136(%rsp) - movupd %xmm4, 120(%rsp) - movupd %xmm5, 104(%rsp) - movupd %xmm6, 88(%rsp) - movupd %xmm7, 72(%rsp) - movq %rdi, 64(%rsp) - movq %rax, 56(%rsp) - movq %rdx, 48(%rsp) - movq %rsi, 40(%rsp) - movq %rcx, 32(%rsp) - movq %r8, 24(%rsp) - movq %r9, 16(%rsp) + subq $192, %rsp + .cfi_def_cfa_offset 200 + // At this point, the stack pointer should be aligned to an 8-byte boundary, + // because any call instructions that come after this will add another 8 + // bytes and therefore align it to 16-bytes. + movq %rbp, 184(%rsp) + movupd %xmm0, 168(%rsp) + movupd %xmm1, 152(%rsp) + movupd %xmm2, 136(%rsp) + movupd %xmm3, 120(%rsp) + movupd %xmm4, 104(%rsp) + movupd %xmm5, 88(%rsp) + movupd %xmm6, 72(%rsp) + movupd %xmm7, 56(%rsp) + movq %rdi, 48(%rsp) + movq %rax, 40(%rsp) + movq %rdx, 32(%rsp) + movq %rsi, 24(%rsp) + movq %rcx, 16(%rsp) + movq %r8, 8(%rsp) + movq %r9, 0(%rsp) .endm .macro RESTORE_REGISTERS - movupd 184(%rsp), %xmm0 - movupd 168(%rsp), %xmm1 - movupd 152(%rsp), %xmm2 - movupd 136(%rsp), %xmm3 - movupd 120(%rsp), %xmm4 - movupd 104(%rsp), %xmm5 - movupd 88(%rsp) , %xmm6 - movupd 72(%rsp) , %xmm7 - movq 64(%rsp), %rdi - movq 56(%rsp), %rax - movq 48(%rsp), %rdx - movq 40(%rsp), %rsi - movq 32(%rsp), %rcx - movq 24(%rsp), %r8 - movq 16(%rsp), %r9 - addq $200, %rsp + movq 184(%rsp), %rbp + movupd 168(%rsp), %xmm0 + movupd 152(%rsp), %xmm1 + movupd 136(%rsp), %xmm2 + movupd 120(%rsp), %xmm3 + movupd 104(%rsp), %xmm4 + movupd 88(%rsp), %xmm5 + movupd 72(%rsp) , %xmm6 + movupd 56(%rsp) , %xmm7 + movq 48(%rsp), %rdi + movq 40(%rsp), %rax + movq 32(%rsp), %rdx + movq 24(%rsp), %rsi + movq 16(%rsp), %rcx + movq 8(%rsp), %r8 + movq 0(%rsp), %r9 + addq $192, %rsp + .cfi_def_cfa_offset 8 .endm .text .file "xray_trampoline_x86.S" //===----------------------------------------------------------------------===// .globl __xray_FunctionEntry .align 16, 0x90 .type __xray_FunctionEntry,@function __xray_FunctionEntry: .cfi_startproc - pushq %rbp - .cfi_def_cfa_offset 16 SAVE_REGISTERS // This load has to be atomic, it's concurrent with __xray_patch(). // On x86/amd64, a simple (type-aligned) MOV instruction is enough. movq _ZN6__xray19XRayPatchedFunctionE(%rip), %rax testq %rax, %rax je .Ltmp0 // The patched function prolog puts its xray_instr_map index into %r10d. movl %r10d, %edi xor %esi,%esi callq *%rax .Ltmp0: RESTORE_REGISTERS - popq %rbp retq .Ltmp1: .size __xray_FunctionEntry, .Ltmp1-__xray_FunctionEntry .cfi_endproc //===----------------------------------------------------------------------===// .globl __xray_FunctionExit .align 16, 0x90 .type __xray_FunctionExit,@function __xray_FunctionExit: .cfi_startproc // Save the important registers first. Since we're assuming that this // function is only jumped into, we only preserve the registers for // returning. - pushq %rbp - .cfi_def_cfa_offset 16 subq $56, %rsp - .cfi_def_cfa_offset 32 - movupd %xmm0, 40(%rsp) - movupd %xmm1, 24(%rsp) - movq %rax, 16(%rsp) - movq %rdx, 8(%rsp) + .cfi_def_cfa_offset 64 + movq %rbp, 48(%rsp) + movupd %xmm0, 32(%rsp) + movupd %xmm1, 16(%rsp) + movq %rax, 8(%rsp) + movq %rdx, 0(%rsp) movq _ZN6__xray19XRayPatchedFunctionE(%rip), %rax testq %rax,%rax je .Ltmp2 movl %r10d, %edi movl $1, %esi callq *%rax .Ltmp2: // Restore the important registers. - movupd 40(%rsp), %xmm0 - movupd 24(%rsp), %xmm1 - movq 16(%rsp), %rax - movq 8(%rsp), %rdx + movq 48(%rsp), %rbp + movupd 32(%rsp), %xmm0 + movupd 16(%rsp), %xmm1 + movq 8(%rsp), %rax + movq 0(%rsp), %rdx addq $56, %rsp - popq %rbp + .cfi_def_cfa_offset 8 retq .Ltmp3: .size __xray_FunctionExit, .Ltmp3-__xray_FunctionExit .cfi_endproc //===----------------------------------------------------------------------===// .global __xray_FunctionTailExit .align 16, 0x90 .type __xray_FunctionTailExit,@function __xray_FunctionTailExit: .cfi_startproc // Save the important registers as in the entry trampoline, but indicate that // this is an exit. In the future, we will introduce a new entry type that // differentiates between a normal exit and a tail exit, but we'd have to do // this and increment the version number for the header. - pushq %rbp - .cfi_def_cfa_offset 16 SAVE_REGISTERS movq _ZN6__xray19XRayPatchedFunctionE(%rip), %rax testq %rax,%rax je .Ltmp4 movl %r10d, %edi movl $1, %esi callq *%rax .Ltmp4: RESTORE_REGISTERS - popq %rbp retq .Ltmp5: .size __xray_FunctionTailExit, .Ltmp5-__xray_FunctionTailExit .cfi_endproc //===----------------------------------------------------------------------===// .globl __xray_ArgLoggerEntry .align 16, 0x90 .type __xray_ArgLoggerEntry,@function __xray_ArgLoggerEntry: .cfi_startproc - pushq %rbp - .cfi_def_cfa_offset 16 SAVE_REGISTERS // Again, these function pointer loads must be atomic; MOV is fine. movq _ZN6__xray13XRayArgLoggerE(%rip), %rax testq %rax, %rax jne .Larg1entryLog // If [arg1 logging handler] not set, defer to no-arg logging. movq _ZN6__xray19XRayPatchedFunctionE(%rip), %rax testq %rax, %rax je .Larg1entryFail .Larg1entryLog: movq %rdi, %rdx // first argument will become the third xorq %rsi, %rsi // XRayEntryType::ENTRY into the second movl %r10d, %edi // 32-bit function ID becomes the first callq *%rax .Larg1entryFail: RESTORE_REGISTERS - popq %rbp retq .Larg1entryEnd: .size __xray_ArgLoggerEntry, .Larg1entryEnd-__xray_ArgLoggerEntry .cfi_endproc NO_EXEC_STACK_DIRECTIVE Index: vendor/compiler-rt/dist/lib/xray/xray_x86_64.cc =================================================================== --- vendor/compiler-rt/dist/lib/xray/xray_x86_64.cc (revision 317221) +++ vendor/compiler-rt/dist/lib/xray/xray_x86_64.cc (revision 317222) @@ -1,220 +1,226 @@ #include "cpuid.h" #include "sanitizer_common/sanitizer_common.h" #include "xray_defs.h" #include "xray_interface_internal.h" #include #include #include #include #include #include #include #include namespace __xray { static std::pair retryingReadSome(int Fd, char *Begin, char *End) XRAY_NEVER_INSTRUMENT { auto BytesToRead = std::distance(Begin, End); ssize_t BytesRead; ssize_t TotalBytesRead = 0; while (BytesToRead && (BytesRead = read(Fd, Begin, BytesToRead))) { if (BytesRead == -1) { if (errno == EINTR) continue; Report("Read error; errno = %d\n", errno); return std::make_pair(TotalBytesRead, false); } TotalBytesRead += BytesRead; BytesToRead -= BytesRead; Begin += BytesRead; } return std::make_pair(TotalBytesRead, true); } static bool readValueFromFile(const char *Filename, long long *Value) XRAY_NEVER_INSTRUMENT { int Fd = open(Filename, O_RDONLY | O_CLOEXEC); if (Fd == -1) return false; static constexpr size_t BufSize = 256; char Line[BufSize] = {}; ssize_t BytesRead; bool Success; std::tie(BytesRead, Success) = retryingReadSome(Fd, Line, Line + BufSize); close(Fd); if (!Success) return false; char *End = nullptr; long long Tmp = internal_simple_strtoll(Line, &End, 10); bool Result = false; if (Line[0] != '\0' && (*End == '\n' || *End == '\0')) { *Value = Tmp; Result = true; } return Result; } uint64_t getTSCFrequency() XRAY_NEVER_INSTRUMENT { long long TSCFrequency = -1; if (readValueFromFile("/sys/devices/system/cpu/cpu0/tsc_freq_khz", &TSCFrequency)) { TSCFrequency *= 1000; } else if (readValueFromFile( "/sys/devices/system/cpu/cpu0/cpufreq/cpuinfo_max_freq", &TSCFrequency)) { TSCFrequency *= 1000; } else { Report("Unable to determine CPU frequency for TSC accounting.\n"); } return TSCFrequency == -1 ? 0 : static_cast(TSCFrequency); } static constexpr uint8_t CallOpCode = 0xe8; static constexpr uint16_t MovR10Seq = 0xba41; static constexpr uint16_t Jmp9Seq = 0x09eb; static constexpr uint8_t JmpOpCode = 0xe9; static constexpr uint8_t RetOpCode = 0xc3; static constexpr int64_t MinOffset{std::numeric_limits::min()}; static constexpr int64_t MaxOffset{std::numeric_limits::max()}; bool patchFunctionEntry(const bool Enable, const uint32_t FuncId, const XRaySledEntry &Sled, void (*Trampoline)()) XRAY_NEVER_INSTRUMENT { // Here we do the dance of replacing the following sled: // // xray_sled_n: // jmp +9 // <9 byte nop> // // With the following: // // mov r10d, // call // // We need to do this in the following order: // // 1. Put the function id first, 2 bytes from the start of the sled (just // after the 2-byte jmp instruction). // 2. Put the call opcode 6 bytes from the start of the sled. // 3. Put the relative offset 7 bytes from the start of the sled. // 4. Do an atomic write over the jmp instruction for the "mov r10d" // opcode and first operand. // // Prerequisite is to compute the relative offset to the trampoline's address. int64_t TrampolineOffset = reinterpret_cast(Trampoline) - (static_cast(Sled.Address) + 11); if (TrampolineOffset < MinOffset || TrampolineOffset > MaxOffset) { Report("XRay Entry trampoline (%p) too far from sled (%p)\n", Trampoline, reinterpret_cast(Sled.Address)); return false; } if (Enable) { *reinterpret_cast(Sled.Address + 2) = FuncId; *reinterpret_cast(Sled.Address + 6) = CallOpCode; *reinterpret_cast(Sled.Address + 7) = TrampolineOffset; std::atomic_store_explicit( reinterpret_cast *>(Sled.Address), MovR10Seq, std::memory_order_release); } else { std::atomic_store_explicit( reinterpret_cast *>(Sled.Address), Jmp9Seq, std::memory_order_release); // FIXME: Write out the nops still? } return true; } bool patchFunctionExit(const bool Enable, const uint32_t FuncId, const XRaySledEntry &Sled) XRAY_NEVER_INSTRUMENT { // Here we do the dance of replacing the following sled: // // xray_sled_n: // ret // <10 byte nop> // // With the following: // // mov r10d, // jmp // // 1. Put the function id first, 2 bytes from the start of the sled (just // after the 1-byte ret instruction). // 2. Put the jmp opcode 6 bytes from the start of the sled. // 3. Put the relative offset 7 bytes from the start of the sled. // 4. Do an atomic write over the jmp instruction for the "mov r10d" // opcode and first operand. // // Prerequisite is to compute the relative offset fo the // __xray_FunctionExit function's address. int64_t TrampolineOffset = reinterpret_cast(__xray_FunctionExit) - (static_cast(Sled.Address) + 11); if (TrampolineOffset < MinOffset || TrampolineOffset > MaxOffset) { Report("XRay Exit trampoline (%p) too far from sled (%p)\n", __xray_FunctionExit, reinterpret_cast(Sled.Address)); return false; } if (Enable) { *reinterpret_cast(Sled.Address + 2) = FuncId; *reinterpret_cast(Sled.Address + 6) = JmpOpCode; *reinterpret_cast(Sled.Address + 7) = TrampolineOffset; std::atomic_store_explicit( reinterpret_cast *>(Sled.Address), MovR10Seq, std::memory_order_release); } else { std::atomic_store_explicit( reinterpret_cast *>(Sled.Address), RetOpCode, std::memory_order_release); // FIXME: Write out the nops still? } return true; } bool patchFunctionTailExit(const bool Enable, const uint32_t FuncId, const XRaySledEntry &Sled) XRAY_NEVER_INSTRUMENT { // Here we do the dance of replacing the tail call sled with a similar // sequence as the entry sled, but calls the tail exit sled instead. int64_t TrampolineOffset = reinterpret_cast(__xray_FunctionTailExit) - (static_cast(Sled.Address) + 11); if (TrampolineOffset < MinOffset || TrampolineOffset > MaxOffset) { Report("XRay Exit trampoline (%p) too far from sled (%p)\n", __xray_FunctionExit, reinterpret_cast(Sled.Address)); return false; } if (Enable) { *reinterpret_cast(Sled.Address + 2) = FuncId; *reinterpret_cast(Sled.Address + 6) = CallOpCode; *reinterpret_cast(Sled.Address + 7) = TrampolineOffset; std::atomic_store_explicit( reinterpret_cast *>(Sled.Address), MovR10Seq, std::memory_order_release); } else { std::atomic_store_explicit( reinterpret_cast *>(Sled.Address), Jmp9Seq, std::memory_order_release); // FIXME: Write out the nops still? } return true; } // We determine whether the CPU we're running on has the correct features we // need. In x86_64 this will be rdtscp support. bool probeRequiredCPUFeatures() XRAY_NEVER_INSTRUMENT { unsigned int EAX, EBX, ECX, EDX; // We check whether rdtscp support is enabled. According to the x86_64 manual, // level should be set at 0x80000001, and we should have a look at bit 27 in // EDX. That's 0x8000000 (or 1u << 26). __get_cpuid(0x80000001, &EAX, &EBX, &ECX, &EDX); if (!(EDX & (1u << 26))) { Report("Missing rdtscp support.\n"); return false; } + // Also check whether we can determine the CPU frequency, since if we cannot, + // we should use the emulated TSC instead. + if (!getTSCFrequency()) { + Report("Unable to determine CPU frequency.\n"); + return false; + } return true; } } // namespace __xray Index: vendor/compiler-rt/dist/test/asan/TestCases/coverage-tracing.cc =================================================================== --- vendor/compiler-rt/dist/test/asan/TestCases/coverage-tracing.cc (revision 317221) +++ vendor/compiler-rt/dist/test/asan/TestCases/coverage-tracing.cc (nonexistent) @@ -1,51 +0,0 @@ -// Test -fsanitize-coverage=trace-bb -// -// RUN: %clangxx_asan -O1 -fsanitize-coverage=func,trace-bb %s -o %t -// RUN: rm -rf %T/coverage-tracing -// RUN: mkdir %T/coverage-tracing -// RUN: cd %T/coverage-tracing -// RUN: A=x; %env_asan_opts=coverage=1:verbosity=1 %run %t $A 1 2>&1 | FileCheck %s --check-prefix=CHECK --check-prefix=CHECK1; mv trace-points.*.sancov $A.points -// RUN: A=f; %env_asan_opts=coverage=1:verbosity=1 %run %t $A 1 2>&1 | FileCheck %s --check-prefix=CHECK --check-prefix=CHECK2; mv trace-points.*.sancov $A.points -// RUN: A=b; %env_asan_opts=coverage=1:verbosity=1 %run %t $A 1 2>&1 | FileCheck %s --check-prefix=CHECK --check-prefix=CHECK2; mv trace-points.*.sancov $A.points -// RUN: A=bf; %env_asan_opts=coverage=1:verbosity=1 %run %t $A 1 2>&1 | FileCheck %s --check-prefix=CHECK --check-prefix=CHECK3; mv trace-points.*.sancov $A.points -// RUN: A=fb; %env_asan_opts=coverage=1:verbosity=1 %run %t $A 1 2>&1 | FileCheck %s --check-prefix=CHECK --check-prefix=CHECK3; mv trace-points.*.sancov $A.points -// RUN: A=ffb; %env_asan_opts=coverage=1:verbosity=1 %run %t $A 1 2>&1 | FileCheck %s --check-prefix=CHECK --check-prefix=CHECK4; mv trace-points.*.sancov $A.points -// RUN: A=fff; %env_asan_opts=coverage=1:verbosity=1 %run %t $A 1 2>&1 | FileCheck %s --check-prefix=CHECK --check-prefix=CHECK4; mv trace-points.*.sancov $A.points -// RUN: A=bbf; %env_asan_opts=coverage=1:verbosity=1 %run %t $A 100 2>&1 | FileCheck %s --check-prefix=CHECK --check-prefix=CHECK301; mv trace-points.*.sancov $A.points -// RUN: diff f.points fff.points -// RUN: diff bf.points fb.points -// RUN: diff bf.points ffb.points -// RUN: diff bf.points bbf.points -// RUN: not diff x.points f.points -// RUN: not diff x.points b.points -// RUN: not diff x.points bf.points -// RUN: not diff f.points b.points -// RUN: not diff f.points bf.points -// RUN: not diff b.points bf.points -// RUN: rm -rf %T/coverage-tracing -// -// REQUIRES: asan-64-bits, shell -// UNSUPPORTED: android - -#include -volatile int sink; -__attribute__((noinline)) void foo() { sink++; } -__attribute__((noinline)) void bar() { sink++; } - -int main(int argc, char **argv) { - if (argc != 3) return 0; - int n = strtol(argv[2], 0, 10); - while (n-- > 0) { - for (int i = 0; argv[1][i]; i++) { - if (argv[1][i] == 'f') foo(); - else if (argv[1][i] == 'b') bar(); - } - } -} - -// CHECK: CovDump: Trace: 3 PCs written -// CHECK1: CovDump: Trace: 1 Events written -// CHECK2: CovDump: Trace: 2 Events written -// CHECK3: CovDump: Trace: 3 Events written -// CHECK4: CovDump: Trace: 4 Events written -// CHECK301: CovDump: Trace: 301 Events written Property changes on: vendor/compiler-rt/dist/test/asan/TestCases/coverage-tracing.cc ___________________________________________________________________ Deleted: svn:eol-style ## -1 +0,0 ## -native \ No newline at end of property Deleted: svn:keywords ## -1 +0,0 ## -FreeBSD=%H \ No newline at end of property Deleted: svn:mime-type ## -1 +0,0 ## -text/plain \ No newline at end of property Index: vendor/compiler-rt/dist/test/asan/TestCases/coverage-caller-callee-total-count.cc =================================================================== --- vendor/compiler-rt/dist/test/asan/TestCases/coverage-caller-callee-total-count.cc (revision 317221) +++ vendor/compiler-rt/dist/test/asan/TestCases/coverage-caller-callee-total-count.cc (nonexistent) @@ -1,42 +0,0 @@ -// Test __sanitizer_get_total_unique_coverage for caller-callee coverage - -// RUN: %clangxx_asan -fsanitize-coverage=edge,indirect-calls %s -o %t -// RUN: %env_asan_opts=coverage=1 %run %t -// RUN: rm -f caller-callee*.sancov -// -// REQUIRES: asan-64-bits - -#include -#include -#include -int P = 0; -struct Foo {virtual void f() {if (P) printf("Foo::f()\n");}}; -struct Foo1 : Foo {virtual void f() {if (P) printf("%d\n", __LINE__);}}; -struct Foo2 : Foo {virtual void f() {if (P) printf("%d\n", __LINE__);}}; - -Foo *foo[3] = {new Foo, new Foo1, new Foo2}; - -uintptr_t CheckNewTotalUniqueCoverageIsLargerAndReturnIt(uintptr_t old_total) { - uintptr_t new_total = __sanitizer_get_total_unique_caller_callee_pairs(); - fprintf(stderr, "Caller-Callee: old %zd new %zd\n", old_total, new_total); - assert(new_total > old_total); - return new_total; -} - -int main(int argc, char **argv) { - uintptr_t total = __sanitizer_get_total_unique_caller_callee_pairs(); - foo[0]->f(); - total = CheckNewTotalUniqueCoverageIsLargerAndReturnIt(total); - foo[1]->f(); - total = CheckNewTotalUniqueCoverageIsLargerAndReturnIt(total); - foo[2]->f(); - total = CheckNewTotalUniqueCoverageIsLargerAndReturnIt(total); - // Ok, called every function once. - // Now call them again from another call site. Should get new coverage. - foo[0]->f(); - total = CheckNewTotalUniqueCoverageIsLargerAndReturnIt(total); - foo[1]->f(); - total = CheckNewTotalUniqueCoverageIsLargerAndReturnIt(total); - foo[2]->f(); - total = CheckNewTotalUniqueCoverageIsLargerAndReturnIt(total); -} Property changes on: vendor/compiler-rt/dist/test/asan/TestCases/coverage-caller-callee-total-count.cc ___________________________________________________________________ Deleted: svn:eol-style ## -1 +0,0 ## -native \ No newline at end of property Deleted: svn:keywords ## -1 +0,0 ## -FreeBSD=%H \ No newline at end of property Deleted: svn:mime-type ## -1 +0,0 ## -text/plain \ No newline at end of property Index: vendor/compiler-rt/dist/test/asan/TestCases/coverage-reset.cc =================================================================== --- vendor/compiler-rt/dist/test/asan/TestCases/coverage-reset.cc (revision 317221) +++ vendor/compiler-rt/dist/test/asan/TestCases/coverage-reset.cc (nonexistent) @@ -1,63 +0,0 @@ -// Test __sanitizer_reset_coverage(). - -// RUN: %clangxx_asan -fsanitize-coverage=func %s -o %t -// RUN: %env_asan_opts=coverage=1 %run %t - -// https://github.com/google/sanitizers/issues/618 -// UNSUPPORTED: android - -#include -#include -#include -static volatile int sink; -__attribute__((noinline)) void bar() { sink = 2; } -__attribute__((noinline)) void foo() { sink = 1; } - -// In MSVC 2015, printf is an inline function, which causes this test to fail as -// it introduces an extra coverage point. Define away printf on that platform to -// avoid the issue. -#if _MSC_VER >= 1900 -# define printf(arg, ...) -#endif - -#define GET_AND_PRINT_COVERAGE() \ - bitset = 0; \ - for (size_t i = 0; i < n_guards; i++) \ - if (guards[i]) bitset |= 1U << i; \ - printf("line %d: bitset %zd total: %zd\n", __LINE__, bitset, \ - __sanitizer_get_total_unique_coverage()); - -#define IS_POWER_OF_TWO(a) ((a & ((a) - 1)) == 0) - -int main() { - size_t *guards = 0; - size_t bitset; - size_t n_guards = __sanitizer_get_coverage_guards(&guards); - - GET_AND_PRINT_COVERAGE(); - size_t main_bit = bitset; - assert(IS_POWER_OF_TWO(main_bit)); - - foo(); - GET_AND_PRINT_COVERAGE(); - size_t foo_bit = bitset & ~main_bit; - assert(IS_POWER_OF_TWO(foo_bit)); - - bar(); - GET_AND_PRINT_COVERAGE(); - size_t bar_bit = bitset & ~(main_bit | foo_bit); - assert(IS_POWER_OF_TWO(bar_bit)); - - __sanitizer_reset_coverage(); - assert(__sanitizer_get_total_unique_coverage() == 0); - GET_AND_PRINT_COVERAGE(); - assert(bitset == 0); - - foo(); - GET_AND_PRINT_COVERAGE(); - assert(bitset == foo_bit); - - bar(); - GET_AND_PRINT_COVERAGE(); - assert(bitset == (foo_bit | bar_bit)); -} Property changes on: vendor/compiler-rt/dist/test/asan/TestCases/coverage-reset.cc ___________________________________________________________________ Deleted: svn:eol-style ## -1 +0,0 ## -native \ No newline at end of property Deleted: svn:keywords ## -1 +0,0 ## -FreeBSD=%H \ No newline at end of property Deleted: svn:mime-type ## -1 +0,0 ## -text/plain \ No newline at end of property Index: vendor/compiler-rt/dist/test/asan/TestCases/Posix/coverage-caller-callee.cc =================================================================== --- vendor/compiler-rt/dist/test/asan/TestCases/Posix/coverage-caller-callee.cc (revision 317221) +++ vendor/compiler-rt/dist/test/asan/TestCases/Posix/coverage-caller-callee.cc (nonexistent) @@ -1,75 +0,0 @@ -// Test caller-callee coverage with large number of threads -// and various numbers of callers and callees. - -// RUN: %clangxx_asan -fsanitize-coverage=edge,indirect-calls %s -o %t -// RUN: %env_asan_opts=coverage=1:verbosity=1 %run %t 10 1 2>&1 | FileCheck %s --check-prefix=CHECK-10-1 -// RUN: %env_asan_opts=coverage=1:verbosity=1 %run %t 9 2 2>&1 | FileCheck %s --check-prefix=CHECK-9-2 -// RUN: %env_asan_opts=coverage=1:verbosity=1 %run %t 7 3 2>&1 | FileCheck %s --check-prefix=CHECK-7-3 -// RUN: %env_asan_opts=coverage=1:verbosity=1 %run %t 17 1 2>&1 | FileCheck %s --check-prefix=CHECK-17-1 -// RUN: %env_asan_opts=coverage=1:verbosity=1 %run %t 15 2 2>&1 | FileCheck %s --check-prefix=CHECK-15-2 -// RUN: %env_asan_opts=coverage=1:verbosity=1 %run %t 18 3 2>&1 | FileCheck %s --check-prefix=CHECK-18-3 -// RUN: rm -f caller-callee*.sancov -// -// REQUIRES: asan-64-bits -// UNSUPPORTED: android -// -// CHECK-10-1: CovDump: 10 caller-callee pairs written -// CHECK-9-2: CovDump: 18 caller-callee pairs written -// CHECK-7-3: CovDump: 21 caller-callee pairs written -// CHECK-17-1: CovDump: 14 caller-callee pairs written -// CHECK-15-2: CovDump: 28 caller-callee pairs written -// CHECK-18-3: CovDump: 42 caller-callee pairs written - -#include -#include -#include -int P = 0; -struct Foo {virtual void f() {if (P) printf("Foo::f()\n");}}; -struct Foo1 : Foo {virtual void f() {if (P) printf("%d\n", __LINE__);}}; -struct Foo2 : Foo {virtual void f() {if (P) printf("%d\n", __LINE__);}}; -struct Foo3 : Foo {virtual void f() {if (P) printf("%d\n", __LINE__);}}; -struct Foo4 : Foo {virtual void f() {if (P) printf("%d\n", __LINE__);}}; -struct Foo5 : Foo {virtual void f() {if (P) printf("%d\n", __LINE__);}}; -struct Foo6 : Foo {virtual void f() {if (P) printf("%d\n", __LINE__);}}; -struct Foo7 : Foo {virtual void f() {if (P) printf("%d\n", __LINE__);}}; -struct Foo8 : Foo {virtual void f() {if (P) printf("%d\n", __LINE__);}}; -struct Foo9 : Foo {virtual void f() {if (P) printf("%d\n", __LINE__);}}; -struct Foo10 : Foo {virtual void f() {if (P) printf("%d\n", __LINE__);}}; -struct Foo11 : Foo {virtual void f() {if (P) printf("%d\n", __LINE__);}}; -struct Foo12 : Foo {virtual void f() {if (P) printf("%d\n", __LINE__);}}; -struct Foo13 : Foo {virtual void f() {if (P) printf("%d\n", __LINE__);}}; -struct Foo14 : Foo {virtual void f() {if (P) printf("%d\n", __LINE__);}}; -struct Foo15 : Foo {virtual void f() {if (P) printf("%d\n", __LINE__);}}; -struct Foo16 : Foo {virtual void f() {if (P) printf("%d\n", __LINE__);}}; -struct Foo17 : Foo {virtual void f() {if (P) printf("%d\n", __LINE__);}}; -struct Foo18 : Foo {virtual void f() {if (P) printf("%d\n", __LINE__);}}; -struct Foo19 : Foo {virtual void f() {if (P) printf("%d\n", __LINE__);}}; - -Foo *foo[20] = { - new Foo, new Foo1, new Foo2, new Foo3, new Foo4, new Foo5, new Foo6, - new Foo7, new Foo8, new Foo9, new Foo10, new Foo11, new Foo12, new Foo13, - new Foo14, new Foo15, new Foo16, new Foo17, new Foo18, new Foo19, -}; - -int n_functions = 10; -int n_callers = 2; - -void *Thread(void *arg) { - if (n_callers >= 1) for (int i = 0; i < 2000; i++) foo[i % n_functions]->f(); - if (n_callers >= 2) for (int i = 0; i < 2000; i++) foo[i % n_functions]->f(); - if (n_callers >= 3) for (int i = 0; i < 2000; i++) foo[i % n_functions]->f(); - return arg; -} - -int main(int argc, char **argv) { - if (argc >= 2) - n_functions = atoi(argv[1]); - if (argc >= 3) - n_callers = atoi(argv[2]); - const int kNumThreads = 16; - pthread_t t[kNumThreads]; - for (int i = 0; i < kNumThreads; i++) - pthread_create(&t[i], 0, Thread, 0); - for (int i = 0; i < kNumThreads; i++) - pthread_join(t[i], 0); -} Property changes on: vendor/compiler-rt/dist/test/asan/TestCases/Posix/coverage-caller-callee.cc ___________________________________________________________________ Deleted: svn:eol-style ## -1 +0,0 ## -native \ No newline at end of property Deleted: svn:keywords ## -1 +0,0 ## -FreeBSD=%H \ No newline at end of property Deleted: svn:mime-type ## -1 +0,0 ## -text/plain \ No newline at end of property Index: vendor/compiler-rt/dist/test/asan/TestCases/Posix/strchr.c =================================================================== --- vendor/compiler-rt/dist/test/asan/TestCases/Posix/strchr.c (nonexistent) +++ vendor/compiler-rt/dist/test/asan/TestCases/Posix/strchr.c (revision 317222) @@ -0,0 +1,36 @@ +// Test strchr for strict_string_checks=false does not look beyond necessary +// char. +// RUN: %clang_asan %s -o %t +// RUN: %env_asan_opts=strict_string_checks=false %run %t 2>&1 +// RUN: %env_asan_opts=strict_string_checks=true not %run %t 2>&1 | FileCheck %s + +#include +#include +#include +#include +#include +#include + +int main(int argc, char **argv) { + size_t page_size = sysconf(_SC_PAGE_SIZE); + size_t size = 2 * page_size; + char *s = (char *)mmap(0, size, PROT_READ | PROT_WRITE, + MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); + assert(s); + assert(((uintptr_t)s & (page_size - 1)) == 0); + memset(s, 'o', size); + s[size - 1] = 0; + + char *p = s + page_size - 1; + *p = 'x'; + + if (mprotect(p + 1, 1, PROT_NONE)) + return 1; + char *r = strchr(s, 'x'); + // CHECK: AddressSanitizer: SEGV on unknown address + // CHECK: The signal is caused by a READ memory access + // CHECK: strchr.c:[[@LINE-3]] + assert(r == p); + + return 0; +} Property changes on: vendor/compiler-rt/dist/test/asan/TestCases/Posix/strchr.c ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: vendor/compiler-rt/dist/test/asan/TestCases/coverage-levels.cc =================================================================== --- vendor/compiler-rt/dist/test/asan/TestCases/coverage-levels.cc (revision 317221) +++ vendor/compiler-rt/dist/test/asan/TestCases/coverage-levels.cc (revision 317222) @@ -1,34 +1,31 @@ // Test various levels of coverage // // RUN: %clangxx_asan -O1 -fsanitize-coverage=func %s -o %t // RUN: %env_asan_opts=coverage=1:coverage_bitset=1:verbosity=1 %run %t 2>&1 | FileCheck %s --check-prefix=CHECK1 // RUN: %clangxx_asan -O1 -fsanitize-coverage=bb %s -o %t // RUN: %env_asan_opts=coverage=1:coverage_bitset=1:verbosity=1 %run %t 2>&1 | FileCheck %s --check-prefix=CHECK2 // RUN: %clangxx_asan -O1 -fsanitize-coverage=edge %s -o %t // RUN: %env_asan_opts=coverage=1:coverage_bitset=1:verbosity=1 %run %t 2>&1 | FileCheck %s --check-prefix=CHECK3 // RUN: %clangxx_asan -O1 -fsanitize-coverage=edge -mllvm -sanitizer-coverage-block-threshold=0 %s -o %t // RUN: %env_asan_opts=coverage=1:coverage_bitset=1:verbosity=1 %run %t 2>&1 | FileCheck %s --check-prefix=CHECK3 -// RUN: %clangxx_asan -O1 -fsanitize-coverage=edge,8bit-counters %s -o %t -// RUN: %env_asan_opts=coverage=1:coverage_counters=1:verbosity=1 %run %t 2>&1 | FileCheck %s --check-prefix=CHECK_COUNTERS // RUN: %env_asan_opts=coverage=1:coverage_bitset=0:verbosity=1 %run %t 2>&1 | FileCheck %s --check-prefix=CHECK3_NOBITSET // RUN: %env_asan_opts=coverage=1:verbosity=1 %run %t 2>&1 | FileCheck %s --check-prefix=CHECK3_NOBITSET // RUN: %env_asan_opts=coverage=1:coverage_pcs=0:verbosity=1 %run %t 2>&1 | FileCheck %s --check-prefix=CHECK3_NOPCS // // REQUIRES: asan-64-bits // UNSUPPORTED: android volatile int sink; int main(int argc, char **argv) { if (argc == 0) sink = 0; } // CHECK1: CovDump: bitset of 1 bits written for '{{.*}}', 1 bits are set // CHECK1: 1 PCs written // CHECK2: CovDump: bitset of 2 bits written for '{{.*}}', 1 bits are set // CHECK2: 1 PCs written // CHECK3: CovDump: bitset of 3 bits written for '{{.*}}', 2 bits are set // CHECK3: 2 PCs written // CHECK3_NOBITSET-NOT: bitset of // CHECK3_NOPCS-NOT: PCs written -// CHECK_COUNTERS: CovDump: 3 counters written for Index: vendor/compiler-rt/dist/test/lsan/TestCases/Linux/cleanup_in_tsd_destructor.c =================================================================== --- vendor/compiler-rt/dist/test/lsan/TestCases/Linux/cleanup_in_tsd_destructor.c (revision 317221) +++ vendor/compiler-rt/dist/test/lsan/TestCases/Linux/cleanup_in_tsd_destructor.c (revision 317222) @@ -1,46 +1,46 @@ // Regression test for thread lifetime tracking. Thread data should be // considered live during the thread's termination, at least until the // user-installed TSD destructors have finished running (since they may contain // additional cleanup tasks). LSan doesn't actually meet that goal 100%, but it // makes its best effort. // RUN: LSAN_BASE="report_objects=1:use_registers=0:use_stacks=0:use_globals=0" // RUN: %clang_lsan %s -o %t -// RUN: LSAN_OPTIONS=$LSAN_BASE:use_tls=1 %run %t -// RUN: LSAN_OPTIONS=$LSAN_BASE:use_tls=0 not %run %t 2>&1 | FileCheck %s +// RUN: %env_lsan_opts=$LSAN_BASE:use_tls=1 %run %t +// RUN: %env_lsan_opts=$LSAN_BASE:use_tls=0 not %run %t 2>&1 | FileCheck %s #include #include #include #include #include "sanitizer/lsan_interface.h" #include "sanitizer_common/print_address.h" pthread_key_t key; __thread void *p; void key_destructor(void *arg) { // Generally this may happen on a different thread. __lsan_do_leak_check(); } void *thread_func(void *arg) { p = malloc(1337); print_address("Test alloc: ", 1, p); int res = pthread_setspecific(key, (void*)1); assert(res == 0); return 0; } int main() { int res = pthread_key_create(&key, &key_destructor); assert(res == 0); pthread_t thread_id; res = pthread_create(&thread_id, 0, thread_func, 0); assert(res == 0); res = pthread_join(thread_id, 0); assert(res == 0); return 0; } // CHECK: Test alloc: [[ADDR:0x[0-9,a-f]+]] // CHECK: [[ADDR]] (1337 bytes) Index: vendor/compiler-rt/dist/test/lsan/TestCases/Linux/disabler_in_tsd_destructor.c =================================================================== --- vendor/compiler-rt/dist/test/lsan/TestCases/Linux/disabler_in_tsd_destructor.c (revision 317221) +++ vendor/compiler-rt/dist/test/lsan/TestCases/Linux/disabler_in_tsd_destructor.c (revision 317222) @@ -1,39 +1,39 @@ // Regression test. Disabler should not depend on TSD validity. // RUN: LSAN_BASE="report_objects=1:use_registers=0:use_stacks=0:use_globals=0:use_tls=1:use_ld_allocations=0" // RUN: %clang_lsan %s -o %t -// RUN: LSAN_OPTIONS=$LSAN_BASE %run %t +// RUN: %env_lsan_opts=$LSAN_BASE %run %t #include #include #include #include #include "sanitizer/lsan_interface.h" pthread_key_t key; void key_destructor(void *arg) { __lsan_disable(); void *p = malloc(1337); // Break optimization. fprintf(stderr, "Test alloc: %p.\n", p); pthread_setspecific(key, 0); __lsan_enable(); } void *thread_func(void *arg) { int res = pthread_setspecific(key, (void*)1); assert(res == 0); return 0; } int main() { int res = pthread_key_create(&key, &key_destructor); assert(res == 0); pthread_t thread_id; res = pthread_create(&thread_id, 0, thread_func, 0); assert(res == 0); res = pthread_join(thread_id, 0); assert(res == 0); return 0; } Index: vendor/compiler-rt/dist/test/lsan/TestCases/Linux/use_tls_dynamic.cc =================================================================== --- vendor/compiler-rt/dist/test/lsan/TestCases/Linux/use_tls_dynamic.cc (revision 317221) +++ vendor/compiler-rt/dist/test/lsan/TestCases/Linux/use_tls_dynamic.cc (revision 317222) @@ -1,52 +1,52 @@ // Test that dynamically allocated TLS space is included in the root set. -// RUN: LSAN_BASE="detect_leaks=1:report_objects=1:use_stacks=0:use_registers=0:use_ld_allocations=0" +// RUN: LSAN_BASE="report_objects=1:use_stacks=0:use_registers=0:use_ld_allocations=0" // RUN: %clangxx %s -DBUILD_DSO -fPIC -shared -o %t-so.so // RUN: %clangxx_lsan %s -o %t -// RUN: LSAN_OPTIONS=$LSAN_BASE:"use_tls=0" not %run %t 2>&1 | FileCheck %s -// RUN: LSAN_OPTIONS=$LSAN_BASE:"use_tls=1" %run %t 2>&1 -// RUN: LSAN_OPTIONS="" %run %t 2>&1 +// RUN: %env_lsan_opts=$LSAN_BASE:"use_tls=0" not %run %t 2>&1 | FileCheck %s +// RUN: %env_lsan_opts=$LSAN_BASE:"use_tls=1" %run %t 2>&1 +// RUN: %env_lsan_opts="" %run %t 2>&1 // UNSUPPORTED: i386-linux,i686-linux,arm #ifndef BUILD_DSO #include #include #include #include #include #include "sanitizer_common/print_address.h" int main(int argc, char *argv[]) { std::string path = std::string(argv[0]) + "-so.so"; void *handle = dlopen(path.c_str(), RTLD_LAZY); assert(handle != 0); typedef void **(* store_t)(void *p); store_t StoreToTLS = (store_t)dlsym(handle, "StoreToTLS"); assert(dlerror() == 0); void *p = malloc(1337); // If we don't know about dynamic TLS, we will return a false leak above. void **p_in_tls = StoreToTLS(p); assert(*p_in_tls == p); print_address("Test alloc: ", 1, p); return 0; } // CHECK: Test alloc: [[ADDR:0x[0-9,a-f]+]] // CHECK: LeakSanitizer: detected memory leaks // CHECK: [[ADDR]] (1337 bytes) // CHECK: SUMMARY: {{(Leak|Address)}}Sanitizer: #else // BUILD_DSO // 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 the 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]; } #endif // BUILD_DSO Index: vendor/compiler-rt/dist/test/lsan/TestCases/Linux/use_tls_pthread_specific_dynamic.cc =================================================================== --- vendor/compiler-rt/dist/test/lsan/TestCases/Linux/use_tls_pthread_specific_dynamic.cc (revision 317221) +++ vendor/compiler-rt/dist/test/lsan/TestCases/Linux/use_tls_pthread_specific_dynamic.cc (revision 317222) @@ -1,38 +1,38 @@ // Test that dynamically allocated thread-specific storage is included in the root set. -// RUN: LSAN_BASE="detect_leaks=1:report_objects=1:use_stacks=0:use_registers=0" +// RUN: LSAN_BASE="report_objects=1:use_stacks=0:use_registers=0" // RUN: %clangxx_lsan %s -o %t -// RUN: LSAN_OPTIONS=$LSAN_BASE:"use_tls=0" not %run %t 2>&1 | FileCheck %s -// RUN: LSAN_OPTIONS=$LSAN_BASE:"use_tls=1" %run %t 2>&1 -// RUN: LSAN_OPTIONS="" %run %t 2>&1 +// RUN: %env_lsan_opts=$LSAN_BASE:"use_tls=0" not %run %t 2>&1 | FileCheck %s +// RUN: %env_lsan_opts=$LSAN_BASE:"use_tls=1" %run %t 2>&1 +// RUN: %env_lsan_opts="" %run %t 2>&1 #include #include #include #include #include "sanitizer_common/print_address.h" // From glibc: this many keys are stored in the thread descriptor directly. const unsigned PTHREAD_KEY_2NDLEVEL_SIZE = 32; int main() { static const unsigned kDummyKeysCount = PTHREAD_KEY_2NDLEVEL_SIZE; int res; pthread_key_t dummy_keys[kDummyKeysCount]; for (unsigned i = 0; i < kDummyKeysCount; i++) { res = pthread_key_create(&dummy_keys[i], NULL); assert(res == 0); } pthread_key_t key; res = pthread_key_create(&key, NULL); assert(key >= PTHREAD_KEY_2NDLEVEL_SIZE); assert(res == 0); void *p = malloc(1337); res = pthread_setspecific(key, p); assert(res == 0); print_address("Test alloc: ", 1, p); return 0; } // CHECK: Test alloc: [[ADDR:0x[0-9,a-f]+]] // CHECK: LeakSanitizer: detected memory leaks // CHECK: [[ADDR]] (1337 bytes) // CHECK: SUMMARY: {{(Leak|Address)}}Sanitizer: Index: vendor/compiler-rt/dist/test/lsan/TestCases/Linux/use_tls_pthread_specific_static.cc =================================================================== --- vendor/compiler-rt/dist/test/lsan/TestCases/Linux/use_tls_pthread_specific_static.cc (revision 317221) +++ vendor/compiler-rt/dist/test/lsan/TestCases/Linux/use_tls_pthread_specific_static.cc (revision 317222) @@ -1,32 +1,32 @@ // Test that statically allocated thread-specific storage is included in the root set. -// RUN: LSAN_BASE="detect_leaks=1:report_objects=1:use_stacks=0:use_registers=0" +// RUN: LSAN_BASE="report_objects=1:use_stacks=0:use_registers=0" // RUN: %clangxx_lsan %s -o %t -// RUN: LSAN_OPTIONS=$LSAN_BASE:"use_tls=0" not %run %t 2>&1 | FileCheck %s -// RUN: LSAN_OPTIONS=$LSAN_BASE:"use_tls=1" %run %t 2>&1 -// RUN: LSAN_OPTIONS="" %run %t 2>&1 +// RUN: %env_lsan_opts=$LSAN_BASE:"use_tls=0" not %run %t 2>&1 | FileCheck %s +// RUN: %env_lsan_opts=$LSAN_BASE:"use_tls=1" %run %t 2>&1 +// RUN: %env_lsan_opts="" %run %t 2>&1 #include #include #include #include #include "sanitizer_common/print_address.h" // From glibc: this many keys are stored in the thread descriptor directly. const unsigned PTHREAD_KEY_2NDLEVEL_SIZE = 32; int main() { pthread_key_t key; int res; res = pthread_key_create(&key, NULL); assert(res == 0); assert(key < PTHREAD_KEY_2NDLEVEL_SIZE); void *p = malloc(1337); res = pthread_setspecific(key, p); assert(res == 0); print_address("Test alloc: ", 1, p); return 0; } // CHECK: Test alloc: [[ADDR:0x[0-9,a-f]+]] // CHECK: LeakSanitizer: detected memory leaks // CHECK: [[ADDR]] (1337 bytes) // CHECK: SUMMARY: {{(Leak|Address)}}Sanitizer: Index: vendor/compiler-rt/dist/test/lsan/TestCases/Linux/use_tls_static.cc =================================================================== --- vendor/compiler-rt/dist/test/lsan/TestCases/Linux/use_tls_static.cc (revision 317221) +++ vendor/compiler-rt/dist/test/lsan/TestCases/Linux/use_tls_static.cc (revision 317222) @@ -1,22 +1,22 @@ // Test that statically allocated TLS space is included in the root set. -// RUN: LSAN_BASE="detect_leaks=1:report_objects=1:use_stacks=0:use_registers=0" +// RUN: LSAN_BASE="report_objects=1:use_stacks=0:use_registers=0" // RUN: %clangxx_lsan %s -o %t -// RUN: LSAN_OPTIONS=$LSAN_BASE:"use_tls=0" not %run %t 2>&1 | FileCheck %s -// RUN: LSAN_OPTIONS=$LSAN_BASE:"use_tls=1" %run %t 2>&1 -// RUN: LSAN_OPTIONS="" %run %t 2>&1 +// RUN: %env_lsan_opts=$LSAN_BASE:"use_tls=0" not %run %t 2>&1 | FileCheck %s +// RUN: %env_lsan_opts=$LSAN_BASE:"use_tls=1" %run %t 2>&1 +// RUN: %env_lsan_opts="" %run %t 2>&1 #include #include #include "sanitizer_common/print_address.h" __thread void *tls_var; int main() { tls_var = malloc(1337); print_address("Test alloc: ", 1, tls_var); return 0; } // CHECK: Test alloc: [[ADDR:0x[0-9,a-f]+]] // CHECK: LeakSanitizer: detected memory leaks // CHECK: [[ADDR]] (1337 bytes) // CHECK: SUMMARY: {{(Leak|Address)}}Sanitizer: Index: vendor/compiler-rt/dist/test/lsan/TestCases/disabler.c =================================================================== --- vendor/compiler-rt/dist/test/lsan/TestCases/disabler.c (revision 317221) +++ vendor/compiler-rt/dist/test/lsan/TestCases/disabler.c (revision 317222) @@ -1,24 +1,24 @@ // Test for __lsan_disable() / __lsan_enable(). // RUN: LSAN_BASE="report_objects=1:use_registers=0:use_stacks=0:use_globals=0:use_tls=0" // RUN: %clang_lsan %s -o %t -// RUN: LSAN_OPTIONS=$LSAN_BASE not %run %t 2>&1 | FileCheck %s +// RUN: %env_lsan_opts=$LSAN_BASE not %run %t 2>&1 | FileCheck %s #include #include #include "sanitizer/lsan_interface.h" int main() { void **p; { __lsan_disable(); p = malloc(sizeof(void *)); __lsan_enable(); } *p = malloc(666); void *q = malloc(1337); // Break optimization. fprintf(stderr, "Test alloc: %p.\n", q); return 0; } // CHECK: SUMMARY: {{(Leak|Address)}}Sanitizer: 1337 byte(s) leaked in 1 allocation(s) Index: vendor/compiler-rt/dist/test/lsan/TestCases/disabler.cc =================================================================== --- vendor/compiler-rt/dist/test/lsan/TestCases/disabler.cc (revision 317221) +++ vendor/compiler-rt/dist/test/lsan/TestCases/disabler.cc (revision 317222) @@ -1,25 +1,25 @@ // Test for ScopedDisabler. -// RUN: LSAN_BASE="detect_leaks=1:report_objects=1:use_registers=0:use_stacks=0:use_globals=0:use_tls=0" +// RUN: LSAN_BASE="report_objects=1:use_registers=0:use_stacks=0:use_globals=0:use_tls=0" // RUN: %clangxx_lsan %s -o %t -// RUN: LSAN_OPTIONS=$LSAN_BASE not %run %t 2>&1 | FileCheck %s +// RUN: %env_lsan_opts=$LSAN_BASE not %run %t 2>&1 | FileCheck %s #include #include #include "sanitizer/lsan_interface.h" int main() { void **p; { __lsan::ScopedDisabler d; p = new void *; fprintf(stderr, "Test alloc p: %p.\n", p); } *p = malloc(666); void *q = malloc(1337); fprintf(stderr, "Test alloc q: %p.\n", q); return 0; } // CHECK: Test alloc p: [[ADDR:.*]]. // CHECK-NOT: [[ADDR]] Index: vendor/compiler-rt/dist/test/lsan/TestCases/do_leak_check_override.cc =================================================================== --- vendor/compiler-rt/dist/test/lsan/TestCases/do_leak_check_override.cc (revision 317221) +++ vendor/compiler-rt/dist/test/lsan/TestCases/do_leak_check_override.cc (revision 317222) @@ -1,36 +1,36 @@ // Test for __lsan_do_leak_check(). We test it by making the leak check run // before global destructors, which also tests compatibility with HeapChecker's // "normal" mode (LSan runs in "strict" mode by default). -// RUN: LSAN_BASE="detect_leaks=1:use_stacks=0:use_registers=0" +// RUN: LSAN_BASE="use_stacks=0:use_registers=0" // RUN: %clangxx_lsan %s -o %t -// RUN: LSAN_OPTIONS=$LSAN_BASE not %run %t 2>&1 | FileCheck --check-prefix=CHECK-strict %s -// RUN: LSAN_OPTIONS=$LSAN_BASE not %run %t foo 2>&1 | FileCheck --check-prefix=CHECK-normal %s +// RUN: %env_lsan_opts=$LSAN_BASE not %run %t 2>&1 | FileCheck --check-prefix=CHECK-strict %s +// RUN: %env_lsan_opts=$LSAN_BASE not %run %t foo 2>&1 | FileCheck --check-prefix=CHECK-normal %s #include #include #include struct LeakyGlobal { LeakyGlobal() { p = malloc(1337); } ~LeakyGlobal() { p = 0; } void *p; }; LeakyGlobal leaky_global; int main(int argc, char *argv[]) { // Register leak check to run before global destructors. if (argc > 1) atexit(&__lsan_do_leak_check); void *p = malloc(666); printf("Test alloc: %p\n", p); printf("Test alloc in leaky global: %p\n", leaky_global.p); return 0; } // CHECK-strict: SUMMARY: {{(Leak|Address)}}Sanitizer: 2003 byte(s) leaked in 2 allocation(s) // CHECK-normal: SUMMARY: {{(Leak|Address)}}Sanitizer: 666 byte(s) leaked in 1 allocation(s) Index: vendor/compiler-rt/dist/test/lsan/TestCases/high_allocator_contention.cc =================================================================== --- vendor/compiler-rt/dist/test/lsan/TestCases/high_allocator_contention.cc (revision 317221) +++ vendor/compiler-rt/dist/test/lsan/TestCases/high_allocator_contention.cc (revision 317222) @@ -1,49 +1,49 @@ // A benchmark that executes malloc/free pairs in parallel. // Usage: ./a.out number_of_threads total_number_of_allocations -// RUN: LSAN_BASE="detect_leaks=1:use_ld_allocations=0" +// RUN: LSAN_BASE="use_ld_allocations=0" // RUN: %clangxx_lsan %s -o %t -// RUN: LSAN_OPTIONS=$LSAN_BASE %run %t 5 1000000 2>&1 +// RUN: %env_lsan_opts=$LSAN_BASE %run %t 5 1000000 2>&1 #include #include #include #include int num_threads; int total_num_alloc; const int kMaxNumThreads = 5000; pthread_t tid[kMaxNumThreads]; pthread_cond_t cond = PTHREAD_COND_INITIALIZER; pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER; bool go = false; void *thread_fun(void *arg) { pthread_mutex_lock(&mutex); while (!go) pthread_cond_wait(&cond, &mutex); pthread_mutex_unlock(&mutex); for (int i = 0; i < total_num_alloc / num_threads; i++) { void *p = malloc(10); __asm__ __volatile__("" : : "r"(p) : "memory"); free((void *)p); } return 0; } int main(int argc, char** argv) { assert(argc == 3); num_threads = atoi(argv[1]); assert(num_threads > 0); assert(num_threads <= kMaxNumThreads); total_num_alloc = atoi(argv[2]); assert(total_num_alloc > 0); printf("%d threads, %d allocations in each\n", num_threads, total_num_alloc / num_threads); for (int i = 0; i < num_threads; i++) pthread_create(&tid[i], 0, thread_fun, 0); pthread_mutex_lock(&mutex); go = true; pthread_cond_broadcast(&cond); pthread_mutex_unlock(&mutex); for (int i = 0; i < num_threads; i++) pthread_join(tid[i], 0); return 0; } Index: vendor/compiler-rt/dist/test/lsan/TestCases/ignore_object.c =================================================================== --- vendor/compiler-rt/dist/test/lsan/TestCases/ignore_object.c (revision 317221) +++ vendor/compiler-rt/dist/test/lsan/TestCases/ignore_object.c (revision 317222) @@ -1,23 +1,23 @@ // Test for __lsan_ignore_object(). // RUN: LSAN_BASE="report_objects=1:use_registers=0:use_stacks=0:use_globals=0:use_tls=0" // RUN: %clang_lsan %s -o %t -// RUN: LSAN_OPTIONS=$LSAN_BASE not %run %t 2>&1 | FileCheck %s +// RUN: %env_lsan_opts=$LSAN_BASE not %run %t 2>&1 | FileCheck %s #include #include #include "sanitizer/lsan_interface.h" int main() { // Explicitly ignored object. void **p = malloc(sizeof(void *)); // Transitively ignored object. *p = malloc(666); // Non-ignored object. volatile void *q = malloc(1337); fprintf(stderr, "Test alloc: %p.\n", p); __lsan_ignore_object(p); return 0; } // CHECK: Test alloc: [[ADDR:.*]]. // CHECK: SUMMARY: {{(Leak|Address)}}Sanitizer: 1337 byte(s) leaked in 1 allocation(s) Index: vendor/compiler-rt/dist/test/lsan/TestCases/ignore_object_errors.cc =================================================================== --- vendor/compiler-rt/dist/test/lsan/TestCases/ignore_object_errors.cc (revision 317221) +++ vendor/compiler-rt/dist/test/lsan/TestCases/ignore_object_errors.cc (revision 317222) @@ -1,20 +1,20 @@ // Test for incorrect use of __lsan_ignore_object(). // RUN: %clangxx_lsan %s -o %t -// RUN: LSAN_OPTIONS=$LSAN_BASE %run %t 2>&1 | FileCheck %s +// RUN: %env_lsan_opts=$LSAN_BASE %run %t 2>&1 | FileCheck %s #include #include #include "sanitizer/lsan_interface.h" int main() { void *p = malloc(1337); fprintf(stderr, "Test alloc: %p.\n", p); __lsan_ignore_object(p); __lsan_ignore_object(p); free(p); __lsan_ignore_object(p); return 0; } // CHECK: Test alloc: [[ADDR:.*]]. // CHECK-NOT: SUMMARY: {{.*}} leaked Index: vendor/compiler-rt/dist/test/lsan/TestCases/large_allocation_leak.cc =================================================================== --- vendor/compiler-rt/dist/test/lsan/TestCases/large_allocation_leak.cc (revision 317221) +++ vendor/compiler-rt/dist/test/lsan/TestCases/large_allocation_leak.cc (revision 317222) @@ -1,23 +1,23 @@ // Test that LargeMmapAllocator's chunks aren't reachable via some internal data structure. -// RUN: LSAN_BASE="detect_leaks=1:report_objects=1:use_stacks=0:use_registers=0" +// RUN: LSAN_BASE="report_objects=1:use_stacks=0:use_registers=0" // RUN: %clangxx_lsan %s -o %t -// RUN: LSAN_OPTIONS=$LSAN_BASE not %run %t 2>&1 | FileCheck %s +// RUN: %env_lsan_opts=$LSAN_BASE not %run %t 2>&1 | FileCheck %s // For 32 bit LSan it's pretty likely that large chunks are "reachable" from some // internal data structures (e.g. Glibc global data). // UNSUPPORTED: x86, arm #include #include #include "sanitizer_common/print_address.h" int main() { // maxsize in primary allocator is always less than this (1 << 25). void *large_alloc = malloc(33554432); print_address("Test alloc: ", 1, large_alloc); return 0; } // CHECK: Test alloc: [[ADDR:0x[0-9,a-f]+]] // CHECK: LeakSanitizer: detected memory leaks // CHECK: [[ADDR]] (33554432 bytes) // CHECK: SUMMARY: {{(Leak|Address)}}Sanitizer: Index: vendor/compiler-rt/dist/test/lsan/TestCases/leak_check_at_exit.cc =================================================================== --- vendor/compiler-rt/dist/test/lsan/TestCases/leak_check_at_exit.cc (revision 317221) +++ vendor/compiler-rt/dist/test/lsan/TestCases/leak_check_at_exit.cc (revision 317222) @@ -1,21 +1,21 @@ // Test for the leak_check_at_exit flag. -// RUN: LSAN_BASE="detect_leaks=1:use_stacks=0:use_registers=0" +// RUN: LSAN_BASE="use_stacks=0:use_registers=0" // RUN: %clangxx_lsan %s -o %t -// RUN: LSAN_OPTIONS=$LSAN_BASE not %run %t foo 2>&1 | FileCheck %s --check-prefix=CHECK-do -// RUN: LSAN_OPTIONS=$LSAN_BASE not %run %t 2>&1 | FileCheck %s --check-prefix=CHECK-do -// RUN: LSAN_OPTIONS=$LSAN_BASE:"leak_check_at_exit=0" not %run %t foo 2>&1 | FileCheck %s --check-prefix=CHECK-do -// RUN: LSAN_OPTIONS=$LSAN_BASE:"leak_check_at_exit=0" %run %t 2>&1 | FileCheck %s --check-prefix=CHECK-dont +// RUN: %env_lsan_opts=$LSAN_BASE not %run %t foo 2>&1 | FileCheck %s --check-prefix=CHECK-do +// RUN: %env_lsan_opts=$LSAN_BASE not %run %t 2>&1 | FileCheck %s --check-prefix=CHECK-do +// RUN: %env_lsan_opts=$LSAN_BASE:"leak_check_at_exit=0" not %run %t foo 2>&1 | FileCheck %s --check-prefix=CHECK-do +// RUN: %env_lsan_opts=$LSAN_BASE:"leak_check_at_exit=0" %run %t 2>&1 | FileCheck %s --check-prefix=CHECK-dont #include #include #include int main(int argc, char *argv[]) { fprintf(stderr, "Test alloc: %p.\n", malloc(1337)); if (argc > 1) __lsan_do_leak_check(); return 0; } // CHECK-do: SUMMARY: {{(Leak|Address)}}Sanitizer: // CHECK-dont-NOT: SUMMARY: {{(Leak|Address)}}Sanitizer: Index: vendor/compiler-rt/dist/test/lsan/TestCases/leak_check_before_thread_started.cc =================================================================== --- vendor/compiler-rt/dist/test/lsan/TestCases/leak_check_before_thread_started.cc (revision 317221) +++ vendor/compiler-rt/dist/test/lsan/TestCases/leak_check_before_thread_started.cc (revision 317222) @@ -1,40 +1,40 @@ // Regression test for http://llvm.org/bugs/show_bug.cgi?id=21621 // This test relies on timing between threads, so any failures will be flaky. // RUN: %clangxx_lsan %s -o %t -// RUN: LSAN_OPTIONS="log_pointers=1:log_threads=1" %run %t +// RUN: %env_lsan_opts="log_pointers=1:log_threads=1" %run %t #include #include #include #include pthread_cond_t cond = PTHREAD_COND_INITIALIZER; pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER; bool flag = false; void *func(void *arg) { // This mutex will never be grabbed. fprintf(stderr, "entered func()\n"); pthread_mutex_lock(&mutex); free(arg); pthread_mutex_unlock(&mutex); return 0; } void create_detached_thread() { pthread_t thread_id; pthread_attr_t attr; pthread_attr_init(&attr); pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); void *arg = malloc(1337); assert(arg); // This mutex is never unlocked by the main thread. pthread_mutex_lock(&mutex); int res = pthread_create(&thread_id, &attr, func, arg); assert(res == 0); } int main() { create_detached_thread(); } Index: vendor/compiler-rt/dist/test/lsan/TestCases/link_turned_off.cc =================================================================== --- vendor/compiler-rt/dist/test/lsan/TestCases/link_turned_off.cc (revision 317221) +++ vendor/compiler-rt/dist/test/lsan/TestCases/link_turned_off.cc (revision 317222) @@ -1,24 +1,24 @@ // Test for disabling LSan at link-time. -// RUN: LSAN_BASE="detect_leaks=1:use_stacks=0:use_registers=0" +// RUN: LSAN_BASE="use_stacks=0:use_registers=0" // RUN: %clangxx_lsan %s -o %t -// RUN: LSAN_OPTIONS=$LSAN_BASE %run %t -// RUN: LSAN_OPTIONS=$LSAN_BASE not %run %t foo 2>&1 | FileCheck %s +// RUN: %env_lsan_opts=$LSAN_BASE %run %t +// RUN: %env_lsan_opts=$LSAN_BASE not %run %t foo 2>&1 | FileCheck %s #include int argc_copy; extern "C" { int __lsan_is_turned_off() { return (argc_copy == 1); } } int main(int argc, char *argv[]) { volatile int *x = new int; *x = 42; argc_copy = argc; return 0; } // CHECK: SUMMARY: {{(Leak|Address)}}Sanitizer: 4 byte(s) leaked in 1 allocation(s) Index: vendor/compiler-rt/dist/test/lsan/TestCases/pointer_to_self.cc =================================================================== --- vendor/compiler-rt/dist/test/lsan/TestCases/pointer_to_self.cc (revision 317221) +++ vendor/compiler-rt/dist/test/lsan/TestCases/pointer_to_self.cc (revision 317222) @@ -1,19 +1,19 @@ // Regression test: pointers to self should not confuse LSan into thinking the // object is indirectly leaked. Only external pointers count. -// RUN: LSAN_BASE="detect_leaks=1:report_objects=1:use_registers=0" +// RUN: LSAN_BASE="report_objects=1:use_registers=0" // RUN: %clangxx_lsan %s -o %t -// RUN: LSAN_OPTIONS=$LSAN_BASE:"use_stacks=0" not %run %t 2>&1 | FileCheck %s +// RUN: %env_lsan_opts=$LSAN_BASE:"use_stacks=0" not %run %t 2>&1 | FileCheck %s #include #include #include "sanitizer_common/print_address.h" int main() { void *p = malloc(1337); *reinterpret_cast(p) = p; print_address("Test alloc: ", 1, p); } // CHECK: Test alloc: [[ADDR:0x[0-9,a-f]+]] // CHECK: LeakSanitizer: detected memory leaks // CHECK: [[ADDR]] (1337 bytes) // CHECK: SUMMARY: {{(Leak|Address)}}Sanitizer: Index: vendor/compiler-rt/dist/test/lsan/TestCases/print_suppressions.cc =================================================================== --- vendor/compiler-rt/dist/test/lsan/TestCases/print_suppressions.cc (revision 317221) +++ vendor/compiler-rt/dist/test/lsan/TestCases/print_suppressions.cc (revision 317222) @@ -1,33 +1,33 @@ // Print matched suppressions only if print_suppressions=1 AND at least one is // matched. Default is print_suppressions=true. -// RUN: LSAN_BASE="detect_leaks=1:use_registers=0:use_stacks=0" +// RUN: LSAN_BASE="use_registers=0:use_stacks=0" // RUN: %clangxx_lsan %s -o %t -// RUN: LSAN_OPTIONS=$LSAN_BASE:print_suppressions=0 %run %t 2>&1 | FileCheck %s --check-prefix=CHECK-dont-print -// RUN: LSAN_OPTIONS=$LSAN_BASE %run %t 2>&1 | FileCheck %s --check-prefix=CHECK-dont-print -// RUN: LSAN_OPTIONS=$LSAN_BASE:print_suppressions=0 %run %t foo 2>&1 | FileCheck %s --check-prefix=CHECK-dont-print -// RUN: LSAN_OPTIONS=$LSAN_BASE %run %t foo 2>&1 | FileCheck %s --check-prefix=CHECK-print +// RUN: %env_lsan_opts=$LSAN_BASE:print_suppressions=0 %run %t 2>&1 | FileCheck %s --check-prefix=CHECK-dont-print +// RUN: %env_lsan_opts=$LSAN_BASE %run %t 2>&1 | FileCheck %s --check-prefix=CHECK-dont-print +// RUN: %env_lsan_opts=$LSAN_BASE:print_suppressions=0 %run %t foo 2>&1 | FileCheck %s --check-prefix=CHECK-dont-print +// RUN: %env_lsan_opts=$LSAN_BASE %run %t foo 2>&1 | FileCheck %s --check-prefix=CHECK-print #include #include #include "sanitizer/lsan_interface.h" extern "C" const char *__lsan_default_suppressions() { return "leak:*LSanTestLeakingFunc*"; } void LSanTestLeakingFunc() { void *p = malloc(666); fprintf(stderr, "Test alloc: %p.\n", p); } int main(int argc, char **argv) { printf("print for nonempty output\n"); if (argc > 1) LSanTestLeakingFunc(); return 0; } // CHECK-print: Suppressions used: // CHECK-print: 1 666 *LSanTestLeakingFunc* // CHECK-dont-print-NOT: Suppressions used: Index: vendor/compiler-rt/dist/test/lsan/TestCases/recoverable_leak_check.cc =================================================================== --- vendor/compiler-rt/dist/test/lsan/TestCases/recoverable_leak_check.cc (revision 317221) +++ vendor/compiler-rt/dist/test/lsan/TestCases/recoverable_leak_check.cc (revision 317222) @@ -1,32 +1,32 @@ // Test for on-demand leak checking. -// RUN: LSAN_BASE="detect_leaks=1:use_stacks=0:use_registers=0" +// RUN: LSAN_BASE="use_stacks=0:use_registers=0" // RUN: %clangxx_lsan %s -o %t -// RUN: LSAN_OPTIONS=$LSAN_BASE %run %t foo 2>&1 | FileCheck %s -// RUN: LSAN_OPTIONS=$LSAN_BASE %run %t 2>&1 | FileCheck %s +// RUN: %env_lsan_opts=$LSAN_BASE %run %t foo 2>&1 | FileCheck %s +// RUN: %env_lsan_opts=$LSAN_BASE %run %t 2>&1 | FileCheck %s #include #include #include #include #include void *p; int main(int argc, char *argv[]) { p = malloc(23); assert(__lsan_do_recoverable_leak_check() == 0); fprintf(stderr, "Test alloc: %p.\n", malloc(1337)); // CHECK: Test alloc: assert(__lsan_do_recoverable_leak_check() == 1); // CHECK: SUMMARY: {{(Leak|Address)}}Sanitizer: 1337 byte // Test that we correctly reset chunk tags. p = 0; assert(__lsan_do_recoverable_leak_check() == 1); // CHECK: SUMMARY: {{(Leak|Address)}}Sanitizer: 1360 byte _exit(0); } Index: vendor/compiler-rt/dist/test/lsan/TestCases/register_root_region.cc =================================================================== --- vendor/compiler-rt/dist/test/lsan/TestCases/register_root_region.cc (revision 317221) +++ vendor/compiler-rt/dist/test/lsan/TestCases/register_root_region.cc (revision 317222) @@ -1,32 +1,32 @@ // Test for __lsan_(un)register_root_region(). -// RUN: LSAN_BASE="detect_leaks=1:use_stacks=0:use_registers=0" +// RUN: LSAN_BASE="use_stacks=0:use_registers=0" // RUN: %clangxx_lsan %s -o %t -// RUN: LSAN_OPTIONS=$LSAN_BASE %run %t -// RUN: LSAN_OPTIONS=$LSAN_BASE not %run %t foo 2>&1 | FileCheck %s -// RUN: LSAN_OPTIONS=$LSAN_BASE:use_root_regions=0 not %run %t 2>&1 | FileCheck %s +// RUN: %env_lsan_opts=$LSAN_BASE %run %t +// RUN: %env_lsan_opts=$LSAN_BASE not %run %t foo 2>&1 | FileCheck %s +// RUN: %env_lsan_opts=$LSAN_BASE:use_root_regions=0 not %run %t 2>&1 | FileCheck %s #include #include #include #include #include #include int main(int argc, char *argv[]) { size_t size = getpagesize() * 2; void *p = mmap(0, size, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANON, -1, 0); assert(p); // Make half of the memory inaccessible. LSan must not crash trying to read it. assert(0 == mprotect((char *)p + size / 2, size / 2, PROT_NONE)); __lsan_register_root_region(p, size); *((void **)p) = malloc(1337); fprintf(stderr, "Test alloc: %p.\n", p); if (argc > 1) __lsan_unregister_root_region(p, size); return 0; } // CHECK: Test alloc: [[ADDR:.*]]. // CHECK: SUMMARY: {{(Leak|Address)}}Sanitizer: 1337 byte(s) leaked in 1 allocation(s) Index: vendor/compiler-rt/dist/test/lsan/TestCases/stale_stack_leak.cc =================================================================== --- vendor/compiler-rt/dist/test/lsan/TestCases/stale_stack_leak.cc (revision 317221) +++ vendor/compiler-rt/dist/test/lsan/TestCases/stale_stack_leak.cc (revision 317222) @@ -1,47 +1,47 @@ // Test that out-of-scope local variables are ignored by LSan. -// RUN: LSAN_BASE="detect_leaks=1:report_objects=1:use_registers=0:use_stacks=1" +// RUN: LSAN_BASE="report_objects=1:use_registers=0:use_stacks=1" // RUN: %clangxx_lsan %s -o %t -// RUN: LSAN_OPTIONS=$LSAN_BASE not %run %t 2>&1 | FileCheck %s -// RUN: LSAN_OPTIONS=$LSAN_BASE":exitcode=0" %run %t 2>&1 | FileCheck --check-prefix=CHECK-sanity %s +// RUN: %env_lsan_opts=$LSAN_BASE not %run %t 2>&1 | FileCheck %s +// RUN: %env_lsan_opts=$LSAN_BASE":exitcode=0" %run %t 2>&1 | FileCheck --check-prefix=CHECK-sanity %s // // x86 passes parameters through stack that may lead to false negatives // UNSUPPORTED: x86 #include #include #include "sanitizer_common/print_address.h" void **pp; // Put pointer far enough on the stack that LSan has space to run in without // overwriting it. // Hopefully the argument p will be passed on a register, saving us from false // negatives. __attribute__((noinline)) void *PutPointerOnStaleStack(void *p) { void *locals[2048]; locals[0] = p; pp = &locals[0]; print_address("Test alloc: ", 1, locals[0]); return 0; } int main() { PutPointerOnStaleStack(malloc(1337)); return 0; } // This must run after LSan, to ensure LSan didn't overwrite the pointer before // it had a chance to see it. If LSan is invoked with atexit(), this works. // Otherwise, we need a different method. __attribute__((destructor)) __attribute__((no_sanitize_address)) void ConfirmPointerHasSurvived() { print_address("Value after LSan: ", 1, *pp); } // CHECK: Test alloc: [[ADDR:0x[0-9,a-f]+]] // CHECK-sanity: Test alloc: [[ADDR:0x[0-9,a-f]+]] // CHECK: LeakSanitizer: detected memory leaks // CHECK: [[ADDR]] (1337 bytes) // CHECK: SUMMARY: {{(Leak|Address)}}Sanitizer: // CHECK-sanity: Value after LSan: [[ADDR]] Index: vendor/compiler-rt/dist/test/lsan/TestCases/suppressions_default.cc =================================================================== --- vendor/compiler-rt/dist/test/lsan/TestCases/suppressions_default.cc (revision 317221) +++ vendor/compiler-rt/dist/test/lsan/TestCases/suppressions_default.cc (revision 317222) @@ -1,28 +1,28 @@ -// RUN: LSAN_BASE="detect_leaks=1:use_registers=0:use_stacks=0" +// RUN: LSAN_BASE="use_registers=0:use_stacks=0" // RUN: %clangxx_lsan %s -o %t -// RUN: LSAN_OPTIONS=$LSAN_BASE not %run %t 2>&1 | FileCheck %s +// RUN: %env_lsan_opts=$LSAN_BASE not %run %t 2>&1 | FileCheck %s #include #include #include "sanitizer/lsan_interface.h" extern "C" const char *__lsan_default_suppressions() { return "leak:*LSanTestLeakingFunc*"; } void LSanTestLeakingFunc() { void *p = malloc(666); fprintf(stderr, "Test alloc: %p.\n", p); } int main() { LSanTestLeakingFunc(); void *q = malloc(1337); fprintf(stderr, "Test alloc: %p.\n", q); return 0; } // CHECK: Suppressions used: // CHECK: 1 666 *LSanTestLeakingFunc* // CHECK: SUMMARY: {{(Leak|Address)}}Sanitizer: 1337 byte(s) leaked in 1 allocation(s) Index: vendor/compiler-rt/dist/test/lsan/TestCases/suppressions_file.cc =================================================================== --- vendor/compiler-rt/dist/test/lsan/TestCases/suppressions_file.cc (revision 317221) +++ vendor/compiler-rt/dist/test/lsan/TestCases/suppressions_file.cc (revision 317222) @@ -1,32 +1,32 @@ -// RUN: LSAN_BASE="detect_leaks=1:use_registers=0:use_stacks=0" +// RUN: LSAN_BASE="use_registers=0:use_stacks=0" // RUN: %clangxx_lsan %s -o %t // RUN: rm -f %t.supp // RUN: touch %t.supp -// RUN: LSAN_OPTIONS="$LSAN_BASE:suppressions='%t.supp'" not %run %t 2>&1 | FileCheck %s --check-prefix=NOSUPP +// RUN: %env_lsan_opts="$LSAN_BASE:suppressions='%t.supp'" not %run %t 2>&1 | FileCheck %s --check-prefix=NOSUPP // RUN: echo "leak:*LSanTestLeakingFunc*" > %t.supp -// RUN: LSAN_OPTIONS="$LSAN_BASE:suppressions='%t.supp'" not %run %t 2>&1 | FileCheck %s +// RUN: %env_lsan_opts="$LSAN_BASE:suppressions='%t.supp'" not %run %t 2>&1 | FileCheck %s // RUN: echo "leak:%t" > %t.supp -// RUN: LSAN_OPTIONS="$LSAN_BASE:suppressions='%t.supp':symbolize=false" %run %t +// RUN: %env_lsan_opts="$LSAN_BASE:suppressions='%t.supp':symbolize=false" %run %t #include #include void LSanTestLeakingFunc() { void *p = malloc(666); fprintf(stderr, "Test alloc: %p.\n", p); } int main() { LSanTestLeakingFunc(); void *q = malloc(1337); fprintf(stderr, "Test alloc: %p.\n", q); return 0; } // CHECK: Suppressions used: // CHECK: 1 666 *LSanTestLeakingFunc* // CHECK: SUMMARY: {{(Leak|Address)}}Sanitizer: 1337 byte(s) leaked in 1 allocation(s) // NOSUPP: SUMMARY: {{(Leak|Address)}}Sanitizer: 2003 byte(s) leaked in 2 allocation(s). Index: vendor/compiler-rt/dist/test/lsan/TestCases/swapcontext.cc =================================================================== --- vendor/compiler-rt/dist/test/lsan/TestCases/swapcontext.cc (revision 317221) +++ vendor/compiler-rt/dist/test/lsan/TestCases/swapcontext.cc (revision 317222) @@ -1,49 +1,48 @@ // We can't unwind stack if we're running coroutines on heap-allocated // memory. Make sure we don't report these leaks. // RUN: %clangxx_lsan %s -o %t -// RUN: LSAN_BASE="detect_leaks=1" -// RUN: LSAN_OPTIONS=$LSAN_BASE %run %t 2>&1 -// RUN: LSAN_OPTIONS=$LSAN_BASE not %run %t foo 2>&1 | FileCheck %s +// RUN: %env_lsan_opts= %run %t 2>&1 +// RUN: %env_lsan_opts= not %run %t foo 2>&1 | FileCheck %s // UNSUPPORTED: arm #include #if defined(__APPLE__) // Note: ucontext.h is deprecated on OSX, so this test may stop working // someday. We define _XOPEN_SOURCE to keep using ucontext.h for now. #define _XOPEN_SOURCE 1 #endif #include #include const int kStackSize = 1 << 20; void Child() { int child_stack; printf("Child: %p\n", &child_stack); int *leaked = new int[666]; } int main(int argc, char *argv[]) { char stack_memory[kStackSize + 1] __attribute__((aligned(16))); char *heap_memory = new char[kStackSize + 1]; char *child_stack = (argc > 1) ? stack_memory : heap_memory; printf("Child stack: %p\n", child_stack); ucontext_t orig_context; ucontext_t child_context; getcontext(&child_context); child_context.uc_stack.ss_sp = child_stack; child_context.uc_stack.ss_size = kStackSize / 2; child_context.uc_link = &orig_context; makecontext(&child_context, Child, 0); if (swapcontext(&orig_context, &child_context) < 0) { perror("swapcontext"); return 1; } delete[] heap_memory; return 0; } // CHECK: SUMMARY: {{(Leak|Address)}}Sanitizer: 2664 byte(s) leaked in 1 allocation(s) Index: vendor/compiler-rt/dist/test/lsan/TestCases/use_after_return.cc =================================================================== --- vendor/compiler-rt/dist/test/lsan/TestCases/use_after_return.cc (revision 317221) +++ vendor/compiler-rt/dist/test/lsan/TestCases/use_after_return.cc (revision 317222) @@ -1,24 +1,24 @@ // Test that fake stack (introduced by ASan's use-after-return mode) is included // in the root set. -// RUN: LSAN_BASE="detect_leaks=1:report_objects=1:use_registers=0" +// RUN: LSAN_BASE="report_objects=1:use_registers=0" // RUN: %clangxx_lsan %s -O2 -o %t -// RUN: ASAN_OPTIONS=$ASAN_OPTIONS:detect_stack_use_after_return=1 LSAN_OPTIONS=$LSAN_BASE:"use_stacks=0" not %run %t 2>&1 | FileCheck %s -// RUN: ASAN_OPTIONS=$ASAN_OPTIONS:detect_stack_use_after_return=1 LSAN_OPTIONS=$LSAN_BASE:"use_stacks=1" %run %t 2>&1 -// RUN: ASAN_OPTIONS=$ASAN_OPTIONS:detect_stack_use_after_return=1 LSAN_OPTIONS="" %run %t 2>&1 +// RUN: ASAN_OPTIONS=$ASAN_OPTIONS:detect_stack_use_after_return=1 %env_lsan_opts=$LSAN_BASE:"use_stacks=0" not %run %t 2>&1 | FileCheck %s +// RUN: ASAN_OPTIONS=$ASAN_OPTIONS:detect_stack_use_after_return=1 %env_lsan_opts=$LSAN_BASE:"use_stacks=1" %run %t 2>&1 +// RUN: ASAN_OPTIONS=$ASAN_OPTIONS:detect_stack_use_after_return=1 %env_lsan_opts="" %run %t 2>&1 #include #include #include "sanitizer_common/print_address.h" int main() { void *stack_var = malloc(1337); print_address("Test alloc: ", 1, stack_var); // Take pointer to variable, to ensure it's not optimized into a register. print_address("Stack var at: ", 1, &stack_var); // Do not return from main to prevent the pointer from going out of scope. exit(0); } // CHECK: Test alloc: [[ADDR:0x[0-9,a-f]+]] // CHECK: LeakSanitizer: detected memory leaks // CHECK: [[ADDR]] (1337 bytes) // CHECK: SUMMARY: {{(Leak|Address)}}Sanitizer: Index: vendor/compiler-rt/dist/test/lsan/TestCases/use_globals_initialized.cc =================================================================== --- vendor/compiler-rt/dist/test/lsan/TestCases/use_globals_initialized.cc (revision 317221) +++ vendor/compiler-rt/dist/test/lsan/TestCases/use_globals_initialized.cc (revision 317222) @@ -1,22 +1,22 @@ // Test that initialized globals are included in the root set. -// RUN: LSAN_BASE="detect_leaks=1:report_objects=1:use_stacks=0:use_registers=0" +// RUN: LSAN_BASE="report_objects=1:use_stacks=0:use_registers=0" // RUN: %clangxx_lsan %s -o %t -// RUN: LSAN_OPTIONS=$LSAN_BASE:"use_globals=0" not %run %t 2>&1 | FileCheck %s -// RUN: LSAN_OPTIONS=$LSAN_BASE:"use_globals=1" %run %t 2>&1 -// RUN: LSAN_OPTIONS="" %run %t 2>&1 +// RUN: %env_lsan_opts=$LSAN_BASE:"use_globals=0" not %run %t 2>&1 | FileCheck %s +// RUN: %env_lsan_opts=$LSAN_BASE:"use_globals=1" %run %t 2>&1 +// RUN: %env_lsan_opts="" %run %t 2>&1 #include #include #include "sanitizer_common/print_address.h" void *data_var = (void *)1; int main() { data_var = malloc(1337); print_address("Test alloc: ", 1, data_var); return 0; } // CHECK: Test alloc: [[ADDR:0x[0-9,a-f]+]] // CHECK: LeakSanitizer: detected memory leaks // CHECK: [[ADDR]] (1337 bytes) // CHECK: SUMMARY: {{(Leak|Address)}}Sanitizer: Index: vendor/compiler-rt/dist/test/lsan/TestCases/use_globals_uninitialized.cc =================================================================== --- vendor/compiler-rt/dist/test/lsan/TestCases/use_globals_uninitialized.cc (revision 317221) +++ vendor/compiler-rt/dist/test/lsan/TestCases/use_globals_uninitialized.cc (revision 317222) @@ -1,22 +1,22 @@ // Test that uninitialized globals are included in the root set. -// RUN: LSAN_BASE="detect_leaks=1:report_objects=1:use_stacks=0:use_registers=0" +// RUN: LSAN_BASE="report_objects=1:use_stacks=0:use_registers=0" // RUN: %clangxx_lsan %s -o %t -// RUN: LSAN_OPTIONS=$LSAN_BASE:"use_globals=0" not %run %t 2>&1 | FileCheck %s -// RUN: LSAN_OPTIONS=$LSAN_BASE:"use_globals=1" %run %t 2>&1 -// RUN: LSAN_OPTIONS="" %run %t 2>&1 +// RUN: %env_lsan_opts=$LSAN_BASE:"use_globals=0" not %run %t 2>&1 | FileCheck %s +// RUN: %env_lsan_opts=$LSAN_BASE:"use_globals=1" %run %t 2>&1 +// RUN: %env_lsan_opts="" %run %t 2>&1 #include #include #include "sanitizer_common/print_address.h" void *bss_var; int main() { bss_var = malloc(1337); print_address("Test alloc: ", 1, bss_var); return 0; } // CHECK: Test alloc: [[ADDR:0x[0-9,a-f]+]] // CHECK: LeakSanitizer: detected memory leaks // CHECK: [[ADDR]] (1337 bytes) // CHECK: SUMMARY: {{(Leak|Address)}}Sanitizer: Index: vendor/compiler-rt/dist/test/lsan/TestCases/use_poisoned_asan.cc =================================================================== --- vendor/compiler-rt/dist/test/lsan/TestCases/use_poisoned_asan.cc (revision 317221) +++ vendor/compiler-rt/dist/test/lsan/TestCases/use_poisoned_asan.cc (revision 317222) @@ -1,26 +1,26 @@ // ASan-poisoned memory should be ignored if use_poisoned is false. // REQUIRES: asan -// RUN: LSAN_BASE="detect_leaks=1:report_objects=1:use_stacks=0:use_registers=0" +// RUN: LSAN_BASE="report_objects=1:use_stacks=0:use_registers=0" // RUN: %clangxx_lsan %s -o %t -// RUN: LSAN_OPTIONS=$LSAN_BASE:"use_poisoned=0" not %run %t 2>&1 | FileCheck %s -// RUN: LSAN_OPTIONS=$LSAN_BASE:"use_poisoned=1" %run %t 2>&1 +// RUN: %env_lsan_opts=$LSAN_BASE:"use_poisoned=0" not %run %t 2>&1 | FileCheck %s +// RUN: %env_lsan_opts=$LSAN_BASE:"use_poisoned=1" %run %t 2>&1 #include #include #include #include #include "sanitizer_common/print_address.h" void **p; int main() { p = new void *; *p = malloc(1337); print_address("Test alloc: ", 1, *p); __asan_poison_memory_region(p, sizeof(*p)); return 0; } // CHECK: Test alloc: [[ADDR:0x[0-9,a-f]+]] // CHECK: LeakSanitizer: detected memory leaks // CHECK: [[ADDR]] (1337 bytes) // CHECK: SUMMARY: AddressSanitizer: Index: vendor/compiler-rt/dist/test/lsan/TestCases/use_registers.cc =================================================================== --- vendor/compiler-rt/dist/test/lsan/TestCases/use_registers.cc (revision 317221) +++ vendor/compiler-rt/dist/test/lsan/TestCases/use_registers.cc (revision 317222) @@ -1,63 +1,63 @@ // Test that registers of running threads are included in the root set. -// RUN: LSAN_BASE="detect_leaks=1:report_objects=1:use_stacks=0" +// RUN: LSAN_BASE="report_objects=1:use_stacks=0" // RUN: %clangxx_lsan -pthread %s -o %t -// RUN: LSAN_OPTIONS=$LSAN_BASE:"use_registers=0" not %run %t 2>&1 | FileCheck %s -// RUN: LSAN_OPTIONS=$LSAN_BASE:"use_registers=1" %run %t 2>&1 -// RUN: LSAN_OPTIONS="" %run %t 2>&1 +// RUN: %env_lsan_opts=$LSAN_BASE:"use_registers=0" not %run %t 2>&1 | FileCheck %s +// RUN: %env_lsan_opts=$LSAN_BASE:"use_registers=1" %run %t 2>&1 +// RUN: %env_lsan_opts="" %run %t 2>&1 #include #include #include #include #include #include "sanitizer_common/print_address.h" extern "C" void *registers_thread_func(void *arg) { int *sync = reinterpret_cast(arg); void *p = malloc(1337); // To store the pointer, choose a register which is unlikely to be reused by // a function call. #if defined(__i386__) asm ( "mov %0, %%esi" : : "r" (p) ); #elif defined(__x86_64__) asm ( "mov %0, %%r15" : : "r" (p) ); #elif defined(__mips__) asm ( "move $16, %0" : : "r" (p) ); #elif defined(__arm__) asm ( "mov r5, %0" : : "r" (p) ); #else #error "Test is not supported on this architecture." #endif print_address("Test alloc: ", 1, p); fflush(stderr); __sync_fetch_and_xor(sync, 1); while (true) sched_yield(); } int main() { int sync = 0; pthread_t thread_id; int res = pthread_create(&thread_id, 0, registers_thread_func, &sync); assert(res == 0); while (!__sync_fetch_and_xor(&sync, 0)) sched_yield(); return 0; } // CHECK: Test alloc: [[ADDR:0x[0-9,a-f]+]] // CHECK: LeakSanitizer: detected memory leaks // CHECK: [[ADDR]] (1337 bytes) // CHECK: SUMMARY: {{(Leak|Address)}}Sanitizer: Index: vendor/compiler-rt/dist/test/lsan/TestCases/use_stacks.cc =================================================================== --- vendor/compiler-rt/dist/test/lsan/TestCases/use_stacks.cc (revision 317221) +++ vendor/compiler-rt/dist/test/lsan/TestCases/use_stacks.cc (revision 317222) @@ -1,21 +1,21 @@ // Test that stack of main thread is included in the root set. -// RUN: LSAN_BASE="detect_leaks=1:report_objects=1:use_registers=0" +// RUN: LSAN_BASE="report_objects=1:use_registers=0" // RUN: %clangxx_lsan %s -o %t -// RUN: LSAN_OPTIONS=$LSAN_BASE:"use_stacks=0" not %run %t 2>&1 | FileCheck %s -// RUN: LSAN_OPTIONS=$LSAN_BASE:"use_stacks=1" %run %t 2>&1 -// RUN: LSAN_OPTIONS="" %run %t 2>&1 +// RUN: %env_lsan_opts=$LSAN_BASE:"use_stacks=0" not %run %t 2>&1 | FileCheck %s +// RUN: %env_lsan_opts=$LSAN_BASE:"use_stacks=1" %run %t 2>&1 +// RUN: %env_lsan_opts="" %run %t 2>&1 #include #include #include "sanitizer_common/print_address.h" int main() { void *stack_var = malloc(1337); print_address("Test alloc: ", 1, stack_var); // Do not return from main to prevent the pointer from going out of scope. exit(0); } // CHECK: Test alloc: [[ADDR:0x[0-9,a-f]+]] // CHECK: LeakSanitizer: detected memory leaks // CHECK: [[ADDR]] (1337 bytes) // CHECK: SUMMARY: {{(Leak|Address)}}Sanitizer: Index: vendor/compiler-rt/dist/test/lsan/TestCases/use_stacks_threaded.cc =================================================================== --- vendor/compiler-rt/dist/test/lsan/TestCases/use_stacks_threaded.cc (revision 317221) +++ vendor/compiler-rt/dist/test/lsan/TestCases/use_stacks_threaded.cc (revision 317222) @@ -1,38 +1,38 @@ // Test that stacks of non-main threads are included in the root set. -// RUN: LSAN_BASE="detect_leaks=1:report_objects=1:use_registers=0" +// RUN: LSAN_BASE="report_objects=1:use_registers=0" // RUN: %clangxx_lsan -pthread %s -o %t -// RUN: LSAN_OPTIONS=$LSAN_BASE:"use_stacks=0" not %run %t 2>&1 | FileCheck %s -// RUN: LSAN_OPTIONS=$LSAN_BASE:"use_stacks=1" %run %t 2>&1 -// RUN: LSAN_OPTIONS="" %run %t 2>&1 +// RUN: %env_lsan_opts=$LSAN_BASE:"use_stacks=0" not %run %t 2>&1 | FileCheck %s +// RUN: %env_lsan_opts=$LSAN_BASE:"use_stacks=1" %run %t 2>&1 +// RUN: %env_lsan_opts="" %run %t 2>&1 #include #include #include #include #include #include "sanitizer_common/print_address.h" extern "C" void *stacks_thread_func(void *arg) { int *sync = reinterpret_cast(arg); void *p = malloc(1337); print_address("Test alloc: ", 1, p); fflush(stderr); __sync_fetch_and_xor(sync, 1); while (true) sched_yield(); } int main() { int sync = 0; pthread_t thread_id; int res = pthread_create(&thread_id, 0, stacks_thread_func, &sync); assert(res == 0); while (!__sync_fetch_and_xor(&sync, 0)) sched_yield(); return 0; } // CHECK: Test alloc: [[ADDR:0x[0-9,a-f]+]] // CHECK: LeakSanitizer: detected memory leaks // CHECK: [[ADDR]] (1337 bytes) // CHECK: SUMMARY: {{(Leak|Address)}}Sanitizer: Index: vendor/compiler-rt/dist/test/lsan/TestCases/use_unaligned.cc =================================================================== --- vendor/compiler-rt/dist/test/lsan/TestCases/use_unaligned.cc (revision 317221) +++ vendor/compiler-rt/dist/test/lsan/TestCases/use_unaligned.cc (revision 317222) @@ -1,24 +1,24 @@ // Test that unaligned pointers are detected correctly. -// RUN: LSAN_BASE="detect_leaks=1:report_objects=1:use_stacks=0:use_registers=0" +// RUN: LSAN_BASE="report_objects=1:use_stacks=0:use_registers=0" // RUN: %clangxx_lsan %s -o %t -// RUN: LSAN_OPTIONS=$LSAN_BASE:"use_unaligned=0" not %run %t 2>&1 | FileCheck %s -// RUN: LSAN_OPTIONS=$LSAN_BASE:"use_unaligned=1" %run %t 2>&1 +// RUN: %env_lsan_opts=$LSAN_BASE:"use_unaligned=0" not %run %t 2>&1 | FileCheck %s +// RUN: %env_lsan_opts=$LSAN_BASE:"use_unaligned=1" %run %t 2>&1 #include #include #include #include "sanitizer_common/print_address.h" void *arr[2]; int main() { void *p = malloc(1337); print_address("Test alloc: ", 1, p); char *char_arr = (char *)arr; memcpy(char_arr + 1, &p, sizeof(p)); return 0; } // CHECK: Test alloc: [[ADDR:0x[0-9,a-f]+]] // CHECK: LeakSanitizer: detected memory leaks // CHECK: [[ADDR]] (1337 bytes) // CHECK: SUMMARY: {{(Leak|Address)}}Sanitizer: Index: vendor/compiler-rt/dist/test/lsan/lit.common.cfg =================================================================== --- vendor/compiler-rt/dist/test/lsan/lit.common.cfg (revision 317221) +++ vendor/compiler-rt/dist/test/lsan/lit.common.cfg (revision 317222) @@ -1,63 +1,78 @@ # -*- Python -*- # Common configuration for running leak detection tests under LSan/ASan. import os import re import lit.util def get_required_attr(config, attr_name): attr_value = getattr(config, attr_name, None) if attr_value == None: lit_config.fatal( "No attribute %r in test configuration! You may need to run " "tests from your build directory or add this attribute " "to lit.site.cfg " % attr_name) return attr_value # Setup source root. config.test_source_root = os.path.dirname(__file__) # Choose between standalone and LSan+ASan modes. lsan_lit_test_mode = get_required_attr(config, 'lsan_lit_test_mode') if lsan_lit_test_mode == "Standalone": config.name = "LeakSanitizer-Standalone" lsan_cflags = ["-fsanitize=leak"] elif lsan_lit_test_mode == "AddressSanitizer": config.name = "LeakSanitizer-AddressSanitizer" lsan_cflags = ["-fsanitize=address"] config.available_features.add('asan') else: lit_config.fatal("Unknown LSan test mode: %r" % lsan_lit_test_mode) config.name += config.name_suffix +# Platform-specific default LSAN_OPTIONS for lit tests. +default_lsan_opts = 'detect_leaks=1' +if config.host_os == 'Darwin': + # On Darwin, we default to `abort_on_error=1`, which would make tests run + # much slower. Let's override this and run lit tests with 'abort_on_error=0'. + # Also, make sure we do not overwhelm the syslog while testing. + default_lsan_opts += ':abort_on_error=0' + default_lsan_opts += ':log_to_syslog=0' + +if default_lsan_opts: + config.environment['LSAN_OPTIONS'] = default_lsan_opts + default_lsan_opts += ':' +config.substitutions.append(('%env_lsan_opts=', + 'env LSAN_OPTIONS=' + default_lsan_opts)) + if lit.util.which('strace'): config.available_features.add('strace') clang_cflags = ["-O0", config.target_cflags] + config.debug_info_flags clang_cxxflags = config.cxx_mode_flags + clang_cflags lsan_incdir = config.test_source_root + "/../" clang_lsan_cflags = clang_cflags + lsan_cflags + ["-I%s" % lsan_incdir] clang_lsan_cxxflags = clang_cxxflags + lsan_cflags + ["-I%s" % lsan_incdir] config.clang_cflags = clang_cflags config.clang_cxxflags = clang_cxxflags def build_invocation(compile_flags): return " " + " ".join([config.clang] + compile_flags) + " " config.substitutions.append( ("%clang ", build_invocation(clang_cflags)) ) config.substitutions.append( ("%clangxx ", build_invocation(clang_cxxflags)) ) config.substitutions.append( ("%clang_lsan ", build_invocation(clang_lsan_cflags)) ) config.substitutions.append( ("%clangxx_lsan ", build_invocation(clang_lsan_cxxflags)) ) # LeakSanitizer tests are currently supported on x86-64 Linux, arm Linux and mips64 Linux only. if config.host_os not in ['Linux'] or config.host_arch not in ['x86_64', 'mips64', 'arm', 'armhf', 'armv7l']: config.unsupported = True # Don't support Thumb due to broken fast unwinder if re.search('mthumb', config.target_cflags) is not None: config.unsupported = True config.suffixes = ['.c', '.cc', '.cpp'] Index: vendor/compiler-rt/dist/test/sanitizer_common/lit.common.cfg =================================================================== --- vendor/compiler-rt/dist/test/sanitizer_common/lit.common.cfg (revision 317221) +++ vendor/compiler-rt/dist/test/sanitizer_common/lit.common.cfg (revision 317222) @@ -1,57 +1,57 @@ # -*- Python -*- # Setup source root. config.test_source_root = os.path.join(os.path.dirname(__file__), "TestCases") config.name = "SanitizerCommon-" + config.name_suffix default_tool_options = [] if config.tool_name == "asan": tool_cflags = ["-fsanitize=address"] tool_options = "ASAN_OPTIONS" elif config.tool_name == "tsan": tool_cflags = ["-fsanitize=thread"] tool_options = "TSAN_OPTIONS" elif config.tool_name == "msan": tool_cflags = ["-fsanitize=memory"] tool_options = "MSAN_OPTIONS" elif config.tool_name == "lsan": tool_cflags = ["-fsanitize=leak"] tool_options = "LSAN_OPTIONS" else: lit_config.fatal("Unknown tool for sanitizer_common tests: %r" % config.tool_name) config.available_features.add(config.tool_name) if config.target_arch not in ['arm', 'armhf', 'aarch64']: config.available_features.add('stable-runtime') -if config.host_os == 'Linux' and config.target_arch == 'i386' and config.tool_name == "lsan": +if config.host_os == 'Linux' and config.tool_name == "lsan" and (config.target_arch == 'i386' or config.target_arch == 'i686'): config.available_features.add("lsan-x86") if config.host_os == 'Darwin': # On Darwin, we default to `abort_on_error=1`, which would make tests run # much slower. Let's override this and run lit tests with 'abort_on_error=0'. default_tool_options += ['abort_on_error=0'] default_tool_options_str = ':'.join(default_tool_options) if default_tool_options_str: config.environment[tool_options] = default_tool_options_str default_tool_options_str += ':' clang_cflags = config.debug_info_flags + tool_cflags + [config.target_cflags] clang_cxxflags = config.cxx_mode_flags + clang_cflags def build_invocation(compile_flags): return " " + " ".join([config.clang] + compile_flags) + " " config.substitutions.append( ("%clang ", build_invocation(clang_cflags)) ) config.substitutions.append( ("%clangxx ", build_invocation(clang_cxxflags)) ) config.substitutions.append( ("%tool_name", config.tool_name) ) config.substitutions.append( ("%tool_options", tool_options) ) config.substitutions.append( ('%env_tool_opts=', 'env ' + tool_options + '=' + default_tool_options_str)) config.suffixes = ['.c', '.cc', '.cpp'] if config.host_os not in ['Linux', 'Darwin']: config.unsupported = True Index: vendor/compiler-rt/dist/test/tsan/Darwin/main_tid.mm =================================================================== --- vendor/compiler-rt/dist/test/tsan/Darwin/main_tid.mm (revision 317221) +++ vendor/compiler-rt/dist/test/tsan/Darwin/main_tid.mm (revision 317222) @@ -1,46 +1,46 @@ // RUN: %clang_tsan %s -o %t -framework Foundation // RUN: %deflake %run %t 2>&1 | FileCheck %s #import #import #import extern "C" { void __tsan_on_report(void *report); int __tsan_get_report_thread(void *report, unsigned long idx, int *tid, - unsigned long *os_id, int *running, + uint64_t *os_id, int *running, const char **name, int *parent_tid, void **trace, unsigned long trace_size); } void __tsan_on_report(void *report) { fprintf(stderr, "__tsan_on_report(%p)\n", report); int tid; - unsigned long os_id; + uint64_t os_id; int running; const char *name; int parent_tid; void *trace[16] = {0}; __tsan_get_report_thread(report, 0, &tid, &os_id, &running, &name, &parent_tid, trace, 16); fprintf(stderr, "tid = %d, os_id = %lu\n", tid, os_id); } int main() { fprintf(stderr, "Hello world.\n"); uint64_t threadid; pthread_threadid_np(NULL, &threadid); fprintf(stderr, "pthread_threadid_np = %llu\n", threadid); pthread_mutex_t m; pthread_mutex_init(&m, NULL); pthread_mutex_unlock(&m); fprintf(stderr, "Done.\n"); } // CHECK: Hello world. // CHECK: pthread_threadid_np = [[ADDR:[0-9]+]] // CHECK: WARNING: ThreadSanitizer // CHECK: tid = 0, os_id = [[ADDR]] // CHECK: Done. Index: vendor/compiler-rt/dist/test/tsan/debug_alloc_stack.cc =================================================================== --- vendor/compiler-rt/dist/test/tsan/debug_alloc_stack.cc (revision 317221) +++ vendor/compiler-rt/dist/test/tsan/debug_alloc_stack.cc (revision 317222) @@ -1,84 +1,84 @@ // RUN: %clangxx_tsan -O0 %s -o %t // RUN: env %env_tsan_opts=stack_trace_format=DEFAULT %deflake %run %t 2>&1 | FileCheck %s // Until I figure out how to make this test work on Linux // REQUIRES: system-darwin #include "test.h" #include #include #include #include #ifndef __APPLE__ #include #endif extern "C" int __tsan_get_alloc_stack(void *addr, void **trace, size_t size, - int *thread_id, void *os_id); + int *thread_id, uint64_t *os_id); char *mem; void alloc_func() { mem = (char *)malloc(10); } void *AllocThread(void *context) { uint64_t tid; #ifdef __APPLE__ pthread_threadid_np(NULL, &tid); #else tid = gettid(); #endif fprintf(stderr, "alloc stack thread os id = 0x%llx\n", tid); // CHECK: alloc stack thread os id = [[THREAD_OS_ID:0x[0-9a-f]+]] alloc_func(); return NULL; } void *RaceThread(void *context) { *mem = 'a'; barrier_wait(&barrier); return NULL; } int main() { pthread_t t; barrier_init(&barrier, 2); pthread_create(&t, NULL, AllocThread, NULL); pthread_join(t, NULL); void *trace[100]; size_t num_frames = 100; int thread_id; - void *thread_os_id; + uint64_t *thread_os_id; num_frames = __tsan_get_alloc_stack(mem, trace, num_frames, &thread_id, &thread_os_id); fprintf(stderr, "alloc stack retval %s\n", (num_frames > 0 && num_frames < 10) ? "ok" : ""); // CHECK: alloc stack retval ok fprintf(stderr, "thread id = %d\n", thread_id); // CHECK: thread id = 1 - fprintf(stderr, "thread os id = 0x%llx\n", (uint64_t)thread_os_id); + fprintf(stderr, "thread os id = 0x%llx\n", thread_os_id); // CHECK: thread os id = [[THREAD_OS_ID]] fprintf(stderr, "%p\n", trace[0]); // CHECK: [[ALLOC_FRAME_0:0x[0-9a-f]+]] fprintf(stderr, "%p\n", trace[1]); // CHECK: [[ALLOC_FRAME_1:0x[0-9a-f]+]] fprintf(stderr, "%p\n", trace[2]); // CHECK: [[ALLOC_FRAME_2:0x[0-9a-f]+]] pthread_create(&t, NULL, RaceThread, NULL); barrier_wait(&barrier); mem[0] = 'b'; pthread_join(t, NULL); free(mem); return 0; } // CHECK: WARNING: ThreadSanitizer: data race // CHECK: Location is heap block of size 10 at {{.*}} allocated by thread T1 // CHECK: #0 [[ALLOC_FRAME_0]] // CHECK: #1 [[ALLOC_FRAME_1]] in alloc_func // CHECK: #2 [[ALLOC_FRAME_2]] in AllocThread Index: vendor/compiler-rt/dist/test/tsan/debugging.cc =================================================================== --- vendor/compiler-rt/dist/test/tsan/debugging.cc (revision 317221) +++ vendor/compiler-rt/dist/test/tsan/debugging.cc (revision 317222) @@ -1,108 +1,109 @@ // RUN: %clangxx_tsan -O1 %s -o %t // RUN: %deflake %run %t 2>&1 | FileCheck %s #include +#include #include #include #include #include "test.h" extern "C" { void __tsan_on_report(void *report); void *__tsan_get_current_report(); int __tsan_get_report_data(void *report, const char **description, int *count, int *stack_count, int *mop_count, int *loc_count, int *mutex_count, int *thread_count, int *unique_tid_count, void **sleep_trace, unsigned long trace_size); int __tsan_get_report_mop(void *report, unsigned long idx, int *tid, void **addr, int *size, int *write, int *atomic, void **trace, unsigned long trace_size); int __tsan_get_report_thread(void *report, unsigned long idx, int *tid, - unsigned long *os_id, int *running, + uint64_t *os_id, int *running, const char **name, int *parent_tid, void **trace, unsigned long trace_size); } long my_global; void *Thread(void *a) { barrier_wait(&barrier); my_global = 42; return NULL; } int main() { barrier_init(&barrier, 2); fprintf(stderr, "&my_global = %p\n", &my_global); // CHECK: &my_global = [[GLOBAL:0x[0-9a-f]+]] pthread_t t; pthread_create(&t, 0, Thread, 0); my_global = 41; barrier_wait(&barrier); pthread_join(t, 0); fprintf(stderr, "Done.\n"); } void __tsan_on_report(void *report) { fprintf(stderr, "__tsan_on_report(%p)\n", report); fprintf(stderr, "__tsan_get_current_report() = %p\n", __tsan_get_current_report()); // CHECK: __tsan_on_report([[REPORT:0x[0-9a-f]+]]) // CHECK: __tsan_get_current_report() = [[REPORT]] const char *description; int count; int stack_count, mop_count, loc_count, mutex_count, thread_count, unique_tid_count; void *sleep_trace[16] = {0}; __tsan_get_report_data(report, &description, &count, &stack_count, &mop_count, &loc_count, &mutex_count, &thread_count, &unique_tid_count, sleep_trace, 16); fprintf(stderr, "report type = '%s', count = %d\n", description, count); // CHECK: report type = 'data-race', count = 0 fprintf(stderr, "mop_count = %d\n", mop_count); // CHECK: mop_count = 2 int tid; void *addr; int size, write, atomic; void *trace[16] = {0}; __tsan_get_report_mop(report, 0, &tid, &addr, &size, &write, &atomic, trace, 16); fprintf(stderr, "tid = %d, addr = %p, size = %d, write = %d, atomic = %d\n", tid, addr, size, write, atomic); // CHECK: tid = 1, addr = [[GLOBAL]], size = 8, write = 1, atomic = 0 fprintf(stderr, "trace[0] = %p, trace[1] = %p\n", trace[0], trace[1]); // CHECK: trace[0] = 0x{{[0-9a-f]+}}, trace[1] = {{0x0|\(nil\)|\(null\)}} __tsan_get_report_mop(report, 1, &tid, &addr, &size, &write, &atomic, trace, 16); fprintf(stderr, "tid = %d, addr = %p, size = %d, write = %d, atomic = %d\n", tid, addr, size, write, atomic); // CHECK: tid = 0, addr = [[GLOBAL]], size = 8, write = 1, atomic = 0 fprintf(stderr, "trace[0] = %p, trace[1] = %p\n", trace[0], trace[1]); // CHECK: trace[0] = 0x{{[0-9a-f]+}}, trace[1] = {{0x0|\(nil\)|\(null\)}} fprintf(stderr, "thread_count = %d\n", thread_count); // CHECK: thread_count = 2 - unsigned long os_id; + uint64_t os_id; int running; const char *name; int parent_tid; __tsan_get_report_thread(report, 0, &tid, &os_id, &running, &name, &parent_tid, trace, 16); fprintf(stderr, "tid = %d\n", tid); // CHECK: tid = 1 __tsan_get_report_thread(report, 1, &tid, &os_id, &running, &name, &parent_tid, trace, 16); fprintf(stderr, "tid = %d\n", tid); // CHECK: tid = 0 } // CHECK: Done. // CHECK: ThreadSanitizer: reported 1 warnings Index: vendor/compiler-rt/dist/test/xray/TestCases/Linux/fdr-mode.cc =================================================================== --- vendor/compiler-rt/dist/test/xray/TestCases/Linux/fdr-mode.cc (revision 317221) +++ vendor/compiler-rt/dist/test/xray/TestCases/Linux/fdr-mode.cc (revision 317222) @@ -1,89 +1,92 @@ // RUN: %clangxx_xray -g -std=c++11 %s -o %t +// RUN: rm fdr-logging-test-* || true +// RUN: rm fdr-unwrite-test-* || true // RUN: XRAY_OPTIONS="patch_premain=false xray_naive_log=false xray_logfile_base=fdr-logging-test- xray_fdr_log=true verbosity=1 xray_fdr_log_func_duration_threshold_us=0" %run %t 2>&1 | FileCheck %s // RUN: XRAY_OPTIONS="patch_premain=false xray_naive_log=false xray_logfile_base=fdr-unwrite-test- xray_fdr_log=true verbosity=1 xray_fdr_log_func_duration_threshold_us=5000" %run %t 2>&1 | FileCheck %s // RUN: %llvm_xray convert --symbolize --output-format=yaml -instr_map=%t "`ls fdr-logging-test-* | head -1`" | FileCheck %s --check-prefix=TRACE // RUN: %llvm_xray convert --symbolize --output-format=yaml -instr_map=%t "`ls fdr-unwrite-test-* | head -1`" | FileCheck %s --check-prefix=UNWRITE // RUN: rm fdr-logging-test-* // RUN: rm fdr-unwrite-test-* // FIXME: Make llvm-xray work on non-x86_64 as well. // REQUIRES: x86_64-linux +// REQUIRES: built-in-llvm-tree #include "xray/xray_log_interface.h" #include #include #include #include #include #include #include constexpr auto kBufferSize = 16384; constexpr auto kBufferMax = 10; thread_local uint64_t var = 0; [[clang::xray_always_instrument]] void __attribute__((noinline)) fC() { ++var; } [[clang::xray_always_instrument]] void __attribute__((noinline)) fB() { fC(); } [[clang::xray_always_instrument]] void __attribute__((noinline)) fA() { fB(); } int main(int argc, char *argv[]) { using namespace __xray; FDRLoggingOptions Options; std::cout << "Logging before init." << std::endl; // CHECK: Logging before init. auto status = __xray_log_init(kBufferSize, kBufferMax, &Options, sizeof(FDRLoggingOptions)); assert(status == XRayLogInitStatus::XRAY_LOG_INITIALIZED); std::cout << "Init status " << status << std::endl; // CHECK: Init status {{.*}} std::cout << "Patching..." << std::endl; // CHECK: Patching... __xray_patch(); fA(); fC(); fB(); fA(); fC(); std::thread other_thread([]() { fC(); fB(); fA(); }); other_thread.join(); std::cout << "Joined" << std::endl; // CHECK: Joined std::cout << "Finalize status " << __xray_log_finalize() << std::endl; // CHECK: Finalize status {{.*}} fC(); std::cout << "Main execution var = " << var << std::endl; // CHECK: Main execution var = 6 std::cout << "Flush status " << __xray_log_flushLog() << std::endl; // CHECK: Flush status {{.*}} __xray_unpatch(); return 0; } // Check that we're able to see two threads, each entering and exiting fA(). // TRACE-DAG: - { type: 0, func-id: [[FIDA:[0-9]+]], function: {{.*fA.*}}, cpu: {{.*}}, thread: [[THREAD1:[0-9]+]], kind: function-enter, tsc: {{[0-9]+}} } // TRACE: - { type: 0, func-id: [[FIDA]], function: {{.*fA.*}}, cpu: {{.*}}, thread: [[THREAD1]], kind: function-exit, tsc: {{[0-9]+}} } // TRACE-DAG: - { type: 0, func-id: [[FIDA]], function: {{.*fA.*}}, cpu: {{.*}}, thread: [[THREAD2:[0-9]+]], kind: function-enter, tsc: {{[0-9]+}} } // TRACE: - { type: 0, func-id: [[FIDA]], function: {{.*fA.*}}, cpu: {{.*}}, thread: [[THREAD2]], kind: function-exit, tsc: {{[0-9]+}} } // // Do the same as above for fC() // TRACE-DAG: - { type: 0, func-id: [[FIDC:[0-9]+]], function: {{.*fC.*}}, cpu: {{.*}}, thread: [[THREAD1:[0-9]+]], kind: function-enter, tsc: {{[0-9]+}} } // TRACE: - { type: 0, func-id: [[FIDC]], function: {{.*fC.*}}, cpu: {{.*}}, thread: [[THREAD1]], kind: function-exit, tsc: {{[0-9]+}} } // TRACE-DAG: - { type: 0, func-id: [[FIDC]], function: {{.*fC.*}}, cpu: {{.*}}, thread: [[THREAD2:[0-9]+]], kind: function-enter, tsc: {{[0-9]+}} } // TRACE: - { type: 0, func-id: [[FIDC]], function: {{.*fC.*}}, cpu: {{.*}}, thread: [[THREAD2]], kind: function-exit, tsc: {{[0-9]+}} } // Do the same as above for fB() // TRACE-DAG: - { type: 0, func-id: [[FIDB:[0-9]+]], function: {{.*fB.*}}, cpu: {{.*}}, thread: [[THREAD1:[0-9]+]], kind: function-enter, tsc: {{[0-9]+}} } // TRACE: - { type: 0, func-id: [[FIDB]], function: {{.*fB.*}}, cpu: {{.*}}, thread: [[THREAD1]], kind: function-exit, tsc: {{[0-9]+}} } // TRACE-DAG: - { type: 0, func-id: [[FIDB]], function: {{.*fB.*}}, cpu: {{.*}}, thread: [[THREAD2:[0-9]+]], kind: function-enter, tsc: {{[0-9]+}} } // TRACE: - { type: 0, func-id: [[FIDB]], function: {{.*fB.*}}, cpu: {{.*}}, thread: [[THREAD2]], kind: function-exit, tsc: {{[0-9]+}} } // Assert that when unwriting is enabled with a high threshold time, all the function records are erased. A CPU switch could erroneously fail this test, but // is unlikely given the test program. // UNWRITE: header // UNWRITE-NOT: function-enter // UNWRITE-NOT: function-exit Index: vendor/compiler-rt/dist/test/xray/TestCases/Linux/fdr-thread-order.cc =================================================================== --- vendor/compiler-rt/dist/test/xray/TestCases/Linux/fdr-thread-order.cc (revision 317221) +++ vendor/compiler-rt/dist/test/xray/TestCases/Linux/fdr-thread-order.cc (revision 317222) @@ -1,38 +1,41 @@ // RUN: %clangxx_xray -g -std=c++11 %s -o %t -// RUN: XRAY_OPTIONS="patch_premain=false xray_naive_log=false xray_logfile_base=fdr-thread-order. xray_fdr_log=true verbosity=1" %run %t 2>&1 | FileCheck %s +// RUN: rm fdr-thread-order.* || true +// RUN: XRAY_OPTIONS="patch_premain=false xray_naive_log=false xray_logfile_base=fdr-thread-order. xray_fdr_log=true verbosity=1 xray_fdr_log_func_duration_threshold_us=0" %run %t 2>&1 | FileCheck %s // RUN: %llvm_xray convert --symbolize --output-format=yaml -instr_map=%t "`ls fdr-thread-order.* | head -1`" | FileCheck %s --check-prefix TRACE // RUN: rm fdr-thread-order.* // FIXME: Make llvm-xray work on non-x86_64 as well. // REQUIRES: x86_64-linux +// REQUIRES: built-in-llvm-tree + #include "xray/xray_log_interface.h" #include #include constexpr auto kBufferSize = 16384; constexpr auto kBufferMax = 10; thread_local uint64_t var = 0; [[clang::xray_always_instrument]] void __attribute__((noinline)) f1() { ++var; } [[clang::xray_always_instrument]] void __attribute__((noinline)) f2() { ++var; } int main(int argc, char *argv[]) { using namespace __xray; FDRLoggingOptions Options; assert(__xray_log_init(kBufferSize, kBufferMax, &Options, sizeof(FDRLoggingOptions)) == XRayLogInitStatus::XRAY_LOG_INITIALIZED); __xray_patch(); std::thread t1([] { f1(); }); std::thread t2([] { f2(); }); t1.join(); t2.join(); __xray_log_finalize(); __xray_log_flushLog(); // CHECK: =={{[0-9]+}}==XRay: Log file in '{{.*}}' } // We want to make sure that the order of the function log doesn't matter. // TRACE-DAG: - { type: 0, func-id: [[FID1:[0-9]+]], function: {{.*f1.*}}, cpu: {{.*}}, thread: [[THREAD1:[0-9]+]], kind: function-enter, tsc: {{[0-9]+}} } // TRACE-DAG: - { type: 0, func-id: [[FID2:[0-9]+]], function: {{.*f2.*}}, cpu: {{.*}}, thread: [[THREAD2:[0-9]+]], kind: function-enter, tsc: {{[0-9]+}} } // TRACE-DAG: - { type: 0, func-id: [[FID1]], function: {{.*f1.*}}, cpu: {{.*}}, thread: [[THREAD1]], kind: function-exit, tsc: {{[0-9]+}} } // TRACE-DAG: - { type: 0, func-id: [[FID2]], function: {{.*f2.*}}, cpu: {{.*}}, thread: [[THREAD2]], kind: function-exit, tsc: {{[0-9]+}} } Index: vendor/compiler-rt/dist/test/xray/lit.site.cfg.in =================================================================== --- vendor/compiler-rt/dist/test/xray/lit.site.cfg.in (revision 317221) +++ vendor/compiler-rt/dist/test/xray/lit.site.cfg.in (revision 317222) @@ -1,13 +1,20 @@ @LIT_SITE_CFG_IN_HEADER@ # Tool-specific config options. config.name_suffix = "@XRAY_TEST_CONFIG_SUFFIX@" config.xray_lit_source_dir = "@XRAY_LIT_SOURCE_DIR@" config.target_cflags = "@XRAY_TEST_TARGET_CFLAGS@" config.target_arch = "@XRAY_TEST_TARGET_ARCH@" +config.built_with_llvm = ("@COMPILER_RT_STANDALONE_BUILD@" != "TRUE") + +# TODO: Look into whether we can run a capability test on the standalone build to +# see whether it can run 'llvm-xray convert' instead of turning off tests for a +# standalone build. +if config.built_with_llvm: + config.available_features.add('built-in-llvm-tree') # Load common config for all compiler-rt lit tests lit_config.load_config(config, "@COMPILER_RT_BINARY_DIR@/test/lit.common.configured") # Load tool-specific config that would do the real work. lit_config.load_config(config, "@XRAY_LIT_SOURCE_DIR@/lit.cfg")