diff options
author | Dmitry Vyukov <dvyukov@google.com> | 2014-02-28 14:52:20 +0000 |
---|---|---|
committer | Dmitry Vyukov <dvyukov@google.com> | 2014-02-28 14:52:20 +0000 |
commit | 8467eeb64062221a2b8d36629f907d072336d989 (patch) | |
tree | c1862fba6ff454061e3bd2d25ab229685220d3ef | |
parent | 41b4b08c6ecf42eee1612ee4d1932b3bc95c8e06 (diff) |
tsan: add standalone deadlock detector
git-svn-id: https://llvm.org/svn/llvm-project/compiler-rt/trunk@202505 91177308-0d34-0410-b5e6-96231b3b80d8
-rwxr-xr-x | lib/tsan/dd/build.sh | 45 | ||||
-rw-r--r-- | lib/tsan/dd/dd.cc | 5877 | ||||
-rw-r--r-- | lib/tsan/dd/dd_interceptors.cc | 68 | ||||
-rw-r--r-- | lib/tsan/dd/dd_rtl.cc | 128 | ||||
-rw-r--r-- | lib/tsan/dd/dd_rtl.h | 50 |
5 files changed, 6168 insertions, 0 deletions
diff --git a/lib/tsan/dd/build.sh b/lib/tsan/dd/build.sh new file mode 100755 index 000000000..2dae37800 --- /dev/null +++ b/lib/tsan/dd/build.sh @@ -0,0 +1,45 @@ +#!/bin/bash +set -e + +SRCS=" + dd_rtl.cc + dd_interceptors.cc + ../../sanitizer_common/sanitizer_allocator.cc + ../../sanitizer_common/sanitizer_common.cc + ../../sanitizer_common/sanitizer_deadlock_detector1.cc + ../../sanitizer_common/sanitizer_flags.cc + ../../sanitizer_common/sanitizer_libc.cc + ../../sanitizer_common/sanitizer_printf.cc + ../../sanitizer_common/sanitizer_suppressions.cc + ../../sanitizer_common/sanitizer_thread_registry.cc + ../../sanitizer_common/sanitizer_posix.cc + ../../sanitizer_common/sanitizer_posix_libcdep.cc + ../../sanitizer_common/sanitizer_procmaps_linux.cc + ../../sanitizer_common/sanitizer_linux.cc + ../../sanitizer_common/sanitizer_linux_libcdep.cc + ../../sanitizer_common/sanitizer_stoptheworld_linux_libcdep.cc + ../../sanitizer_common/sanitizer_stackdepot.cc + ../../sanitizer_common/sanitizer_stacktrace.cc + ../../sanitizer_common/sanitizer_stacktrace_libcdep.cc + ../../sanitizer_common/sanitizer_symbolizer.cc + ../../sanitizer_common/sanitizer_symbolizer_libcdep.cc + ../../sanitizer_common/sanitizer_symbolizer_posix_libcdep.cc + ../../sanitizer_common/sanitizer_symbolizer_libbacktrace.cc + ../../interception/interception_linux.cc +" + +FLAGS=" -I../.. -I../../sanitizer_common -I../../interception -Wall -fno-exceptions -fno-rtti -DSANITIZER_USE_MALLOC" +if [ "$DEBUG" == "" ]; then + FLAGS+=" -DDEBUG=0 -O3 -fomit-frame-pointer" +else + FLAGS+=" -DDEBUG=1 -g" +fi + +rm -f dd.cc +for F in $SRCS; do + g++ $F -c -o dd.o $FLAGS + cat $F >> dd.cc +done + +g++ dd.cc -c -o dd.o $FLAGS + diff --git a/lib/tsan/dd/dd.cc b/lib/tsan/dd/dd.cc new file mode 100644 index 000000000..b11589c7d --- /dev/null +++ b/lib/tsan/dd/dd.cc @@ -0,0 +1,5877 @@ +//===-- dd_rtl.cc ---------------------------------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "dd_rtl.h" +#include "sanitizer_common/sanitizer_common.h" +#include "sanitizer_common/sanitizer_flags.h" +#include "sanitizer_common/sanitizer_stacktrace.h" +#include "sanitizer_common/sanitizer_stackdepot.h" + +namespace __dsan { + +static Context ctx0; +static Context * const ctx = &ctx0; + +void Initialize() { + InitializeInterceptors(); + //common_flags()->allow_addr2line = true; + common_flags()->symbolize = true; + ctx->dd = DDetector::Create(); +} + +void ThreadInit(Thread *thr) { + thr->dd_pt = ctx->dd->CreatePhysicalThread(); + thr->dd_lt = ctx->dd->CreateLogicalThread(0); +} + +void ThreadDestroy(Thread *thr) { + ctx->dd->DestroyPhysicalThread(thr->dd_pt); + ctx->dd->DestroyLogicalThread(thr->dd_lt); +} + +static u32 CurrentStackTrace(Thread *thr) { + StackTrace trace; + thr->in_symbolizer = true; + trace.Unwind(1000, 0, 0, 0, 0, 0, false); + thr->in_symbolizer = false; + const uptr skip = 4; + if (trace.size <= skip) + return 0; + return StackDepotPut(trace.trace + skip, trace.size - skip); +} + +static void PrintStackTrace(Thread *thr, u32 stk) { + uptr size = 0; + const uptr *trace = StackDepotGet(stk, &size); + thr->in_symbolizer = true; + StackTrace::PrintStack(trace, size); + thr->in_symbolizer = false; +} + +static Mutex *FindMutex(Thread *thr, uptr m) { + SpinMutexLock l(&ctx->mutex_mtx); + for (Mutex *mtx = ctx->mutex_list; mtx; mtx = mtx->link) { + if (mtx->addr == m) + return mtx; + } + Mutex *mtx = (Mutex*)InternalAlloc(sizeof(*mtx)); + internal_memset(mtx, 0, sizeof(*mtx)); + mtx->addr = m; + ctx->dd->MutexInit(&mtx->dd, CurrentStackTrace(thr), ctx->mutex_seq++); + mtx->link = ctx->mutex_list; + ctx->mutex_list = mtx; + return mtx; +} + +static Mutex *FindMutexAndRemove(uptr m) { + SpinMutexLock l(&ctx->mutex_mtx); + Mutex **prev = &ctx->mutex_list; + for (;;) { + Mutex *mtx = *prev; + if (mtx == 0) + return 0; + if (mtx->addr == m) { + *prev = mtx->link; + return mtx; + } + prev = &mtx->link; + } +} + +static void ReportDeadlock(Thread *thr, DDReport *rep) { + Printf("==============================\n"); + Printf("DEADLOCK\n"); + PrintStackTrace(thr, CurrentStackTrace(thr)); + for (int i = 0; i < rep->n; i++) { + Printf("Mutex %llu created at:\n", rep->loop[i].mtx_ctx0); + PrintStackTrace(thr, rep->loop[i].stk); + } + Printf("==============================\n"); +} + +void MutexLock(Thread *thr, uptr m, bool writelock, bool trylock) { + if (thr->in_symbolizer) + return; + Mutex *mtx = FindMutex(thr, m); + DDReport *rep = ctx->dd->MutexLock(thr->dd_pt, thr->dd_lt, &mtx->dd, + writelock, trylock); + if (rep) + ReportDeadlock(thr, rep); +} + +void MutexUnlock(Thread *thr, uptr m, bool writelock) { + if (thr->in_symbolizer) + return; + Mutex *mtx = FindMutex(thr, m); + ctx->dd->MutexUnlock(thr->dd_pt, thr->dd_lt, &mtx->dd, writelock); +} + +void MutexDestroy(Thread *thr, uptr m) { + if (thr->in_symbolizer) + return; + Mutex *mtx = FindMutexAndRemove(m); + if (mtx == 0) + return; + ctx->dd->MutexDestroy(thr->dd_pt, thr->dd_lt, &mtx->dd); + InternalFree(mtx); +} + +} // namespace __dsan + +__attribute__((section(".preinit_array"), used)) +void (*__local_dsan_preinit)(void) = __dsan::Initialize; +//===-- dd_interceptors.cc ------------------------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "dd_rtl.h" +#include "interception/interception.h" +#include <pthread.h> + +using namespace __dsan; + +extern "C" void *__libc_malloc(uptr size); +extern "C" void __libc_free(void *ptr); + +static __thread Thread *thr; + +static void InitThread() { + if (thr != 0) + return; + thr = (Thread*)InternalAlloc(sizeof(*thr)); + internal_memset(thr, 0, sizeof(*thr)); + ThreadInit(thr); +} + +INTERCEPTOR(int, pthread_mutex_destroy, pthread_mutex_t *m) { + InitThread(); + int res = REAL(pthread_mutex_destroy)(m); + MutexDestroy(thr, (uptr)m); + return res; +} + +INTERCEPTOR(int, pthread_mutex_lock, pthread_mutex_t *m) { + InitThread(); + int res = REAL(pthread_mutex_lock)(m); + if (res == 0) + MutexLock(thr, (uptr)m, true, false); + return res; +} + +INTERCEPTOR(int, pthread_mutex_trylock, pthread_mutex_t *m) { + InitThread(); + int res = REAL(pthread_mutex_trylock)(m); + if (res == 0) + MutexLock(thr, (uptr)m, true, true); + return res; +} + +INTERCEPTOR(int, pthread_mutex_unlock, pthread_mutex_t *m) { + InitThread(); + MutexUnlock(thr, (uptr)m, true); + int res = REAL(pthread_mutex_unlock)(m); + return res; +} + +namespace __dsan { + +void InitializeInterceptors() { + INTERCEPT_FUNCTION(pthread_mutex_destroy); + INTERCEPT_FUNCTION(pthread_mutex_lock); + INTERCEPT_FUNCTION(pthread_mutex_trylock); + INTERCEPT_FUNCTION(pthread_mutex_unlock); +} + +} // namespace __dsan +//===-- sanitizer_allocator.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. +// This allocator is used inside run-times. +//===----------------------------------------------------------------------===// +#include "sanitizer_allocator.h" +#include "sanitizer_allocator_internal.h" +#include "sanitizer_common.h" +#include "sanitizer_flags.h" + +namespace __sanitizer { + +// ThreadSanitizer for Go uses libc malloc/free. +#if defined(SANITIZER_GO) || defined(SANITIZER_USE_MALLOC) +# if SANITIZER_LINUX && !SANITIZER_ANDROID +extern "C" void *__libc_malloc(uptr size); +extern "C" void __libc_free(void *ptr); +# define LIBC_MALLOC __libc_malloc +# define LIBC_FREE __libc_free +# else +# include <stdlib.h> +# define LIBC_MALLOC malloc +# define LIBC_FREE free +# endif + +static void *RawInternalAlloc(uptr size, InternalAllocatorCache *cache) { + (void)cache; + return LIBC_MALLOC(size); +} + +static void RawInternalFree(void *ptr, InternalAllocatorCache *cache) { + (void)cache; + LIBC_FREE(ptr); +} + +InternalAllocator *internal_allocator() { + return 0; +} + +#else // SANITIZER_GO + +static ALIGNED(64) char internal_alloc_placeholder[sizeof(InternalAllocator)]; +static atomic_uint8_t internal_allocator_initialized; +static StaticSpinMutex internal_alloc_init_mu; + +static InternalAllocatorCache internal_allocator_cache; +static StaticSpinMutex internal_allocator_cache_mu; + +InternalAllocator *internal_allocator() { + InternalAllocator *internal_allocator_instance = + reinterpret_cast<InternalAllocator *>(&internal_alloc_placeholder); + if (atomic_load(&internal_allocator_initialized, memory_order_acquire) == 0) { + SpinMutexLock l(&internal_alloc_init_mu); + if (atomic_load(&internal_allocator_initialized, memory_order_relaxed) == + 0) { + internal_allocator_instance->Init(); + atomic_store(&internal_allocator_initialized, 1, memory_order_release); + } + } + return internal_allocator_instance; +} + +static void *RawInternalAlloc(uptr size, InternalAllocatorCache *cache) { + if (cache == 0) { + SpinMutexLock l(&internal_allocator_cache_mu); + return internal_allocator()->Allocate(&internal_allocator_cache, size, 8, + false); + } + return internal_allocator()->Allocate(cache, size, 8, false); +} + +static void RawInternalFree(void *ptr, InternalAllocatorCache *cache) { + if (cache == 0) { + SpinMutexLock l(&internal_allocator_cache_mu); + return internal_allocator()->Deallocate(&internal_allocator_cache, ptr); + } + internal_allocator()->Deallocate(cache, ptr); +} + +#endif // SANITIZER_GO + +const u64 kBlockMagic = 0x6A6CB03ABCEBC041ull; + +void *InternalAlloc(uptr size, InternalAllocatorCache *cache) { + if (size + sizeof(u64) < size) + return 0; + void *p = RawInternalAlloc(size + sizeof(u64), cache); + if (p == 0) + return 0; + ((u64*)p)[0] = kBlockMagic; + return (char*)p + sizeof(u64); +} + +void InternalFree(void *addr, InternalAllocatorCache *cache) { + if (addr == 0) + return; + addr = (char*)addr - sizeof(u64); + CHECK_EQ(kBlockMagic, ((u64*)addr)[0]); + ((u64*)addr)[0] = 0; + RawInternalFree(addr, cache); +} + +// LowLevelAllocator +static LowLevelAllocateCallback low_level_alloc_callback; + +void *LowLevelAllocator::Allocate(uptr size) { + // Align allocation size. + size = RoundUpTo(size, 8); + if (allocated_end_ - allocated_current_ < (sptr)size) { + uptr size_to_allocate = Max(size, GetPageSizeCached()); + allocated_current_ = + (char*)MmapOrDie(size_to_allocate, __func__); + allocated_end_ = allocated_current_ + size_to_allocate; + if (low_level_alloc_callback) { + low_level_alloc_callback((uptr)allocated_current_, + size_to_allocate); + } + } + CHECK(allocated_end_ - allocated_current_ >= (sptr)size); + void *res = allocated_current_; + allocated_current_ += size; + return res; +} + +void SetLowLevelAllocateCallback(LowLevelAllocateCallback callback) { + low_level_alloc_callback = callback; +} + +bool CallocShouldReturnNullDueToOverflow(uptr size, uptr n) { + if (!size) return false; + uptr max = (uptr)-1L; + return (max / size) < n; +} + +void *AllocatorReturnNull() { + if (common_flags()->allocator_may_return_null) + return 0; + Report("%s's allocator is terminating the process instead of returning 0\n", + SanitizerToolName); + Report("If you don't like this behavior set allocator_may_return_null=1\n"); + CHECK(0); + return 0; +} + +} // namespace __sanitizer +//===-- 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_flags.h" +#include "sanitizer_libc.h" +#include "sanitizer_stacktrace.h" +#include "sanitizer_symbolizer.h" + +namespace __sanitizer { + +const char *SanitizerToolName = "SanitizerTool"; + +uptr GetPageSizeCached() { + static uptr PageSize; + if (!PageSize) + PageSize = GetPageSize(); + return PageSize; +} + + +// By default, dump to stderr. If |log_to_file| is true and |report_fd_pid| +// isn't equal to the current PID, try to obtain file descriptor by opening +// file "report_path_prefix.<PID>". +fd_t report_fd = kStderrFd; + +// Set via __sanitizer_set_report_path. +bool log_to_file = false; +char report_path_prefix[sizeof(report_path_prefix)]; + +// PID of process that opened |report_fd|. If a fork() occurs, the PID of the +// child thread will be different from |report_fd_pid|. +uptr report_fd_pid = 0; + +// 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; + +static DieCallbackType DieCallback; +void SetDieCallback(DieCallbackType callback) { + DieCallback = callback; +} + +DieCallbackType GetDieCallback() { + return DieCallback; +} + +void NORETURN Die() { + if (DieCallback) { + DieCallback(); + } + internal__exit(1); +} + +static CheckFailedCallbackType CheckFailedCallback; +void SetCheckFailedCallback(CheckFailedCallbackType callback) { + CheckFailedCallback = callback; +} + +void NORETURN CheckFailed(const char *file, int line, const char *cond, + u64 v1, u64 v2) { + if (CheckFailedCallback) { + CheckFailedCallback(file, line, cond, v1, v2); + } + Report("Sanitizer CHECK failed: %s:%d %s (%lld, %lld)\n", file, line, cond, + v1, v2); + Die(); +} + +uptr ReadFileToBuffer(const char *file_name, char **buff, + uptr *buff_size, uptr max_len) { + uptr PageSize = GetPageSizeCached(); + uptr kMinFileLen = PageSize; + uptr read_len = 0; + *buff = 0; + *buff_size = 0; + // The files we usually open are not seekable, so try different buffer sizes. + for (uptr size = kMinFileLen; size <= max_len; size *= 2) { + uptr openrv = OpenFile(file_name, /*write*/ false); + if (internal_iserror(openrv)) return 0; + fd_t fd = openrv; + UnmapOrDie(*buff, *buff_size); + *buff = (char*)MmapOrDie(size, __func__); + *buff_size = size; + // Read up to one page at a time. + read_len = 0; + bool reached_eof = false; + while (read_len + PageSize <= size) { + uptr just_read = internal_read(fd, *buff + read_len, PageSize); + if (just_read == 0) { + reached_eof = true; + break; + } + read_len += just_read; + } + internal_close(fd); + if (reached_eof) // We've read the whole file. + break; + } + return read_len; +} + +typedef bool UptrComparisonFunction(const uptr &a, const uptr &b); + +template<class T> +static inline bool CompareLess(const T &a, const T &b) { + return a < b; +} + +void SortArray(uptr *array, uptr size) { + InternalSort<uptr*, UptrComparisonFunction>(&array, size, CompareLess); +} + +// We want to map a chunk of address space aligned to 'alignment'. +// We do it by maping a bit more and then unmaping redundant pieces. +// We probably can do it with fewer syscalls in some OS-dependent way. +void *MmapAlignedOrDie(uptr size, uptr alignment, const char *mem_type) { +// uptr PageSize = GetPageSizeCached(); + CHECK(IsPowerOfTwo(size)); + CHECK(IsPowerOfTwo(alignment)); + uptr map_size = size + alignment; + uptr map_res = (uptr)MmapOrDie(map_size, mem_type); + uptr map_end = map_res + map_size; + uptr res = map_res; + if (res & (alignment - 1)) // Not aligned. + res = (map_res + alignment) & ~(alignment - 1); + uptr end = res + size; + if (res != map_res) + UnmapOrDie((void*)map_res, res - map_res); + if (end != map_end) + UnmapOrDie((void*)end, map_end - end); + return (void*)res; +} + +const char *StripPathPrefix(const char *filepath, + const char *strip_path_prefix) { + if (filepath == 0) return 0; + if (strip_path_prefix == 0) return filepath; + const char *pos = internal_strstr(filepath, strip_path_prefix); + if (pos == 0) return filepath; + pos += internal_strlen(strip_path_prefix); + if (pos[0] == '.' && pos[1] == '/') + pos += 2; + return pos; +} + +void PrintSourceLocation(InternalScopedString *buffer, const char *file, + int line, int column) { + CHECK(file); + buffer->append("%s", + StripPathPrefix(file, common_flags()->strip_path_prefix)); + if (line > 0) { + buffer->append(":%d", line); + if (column > 0) + buffer->append(":%d", column); + } +} + +void PrintModuleAndOffset(InternalScopedString *buffer, const char *module, + uptr offset) { + buffer->append("(%s+0x%zx)", + StripPathPrefix(module, common_flags()->strip_path_prefix), + offset); +} + +void ReportErrorSummary(const char *error_message) { + if (!common_flags()->print_summary) + return; + InternalScopedBuffer<char> buff(kMaxSummaryLength); + internal_snprintf(buff.data(), buff.size(), + "SUMMARY: %s: %s", SanitizerToolName, error_message); + __sanitizer_report_error_summary(buff.data()); +} + +void ReportErrorSummary(const char *error_type, const char *file, + int line, const char *function) { + if (!common_flags()->print_summary) + return; + InternalScopedBuffer<char> buff(kMaxSummaryLength); + internal_snprintf( + buff.data(), buff.size(), "%s %s:%d %s", error_type, + file ? StripPathPrefix(file, common_flags()->strip_path_prefix) : "??", + line, function ? function : "??"); + ReportErrorSummary(buff.data()); +} + +void ReportErrorSummary(const char *error_type, StackTrace *stack) { + if (!common_flags()->print_summary) + return; + AddressInfo ai; +#if !SANITIZER_GO + if (stack->size > 0 && Symbolizer::Get()->CanReturnFileLineInfo()) { + // Currently, we include the first stack frame into the report summary. + // Maybe sometimes we need to choose another frame (e.g. skip memcpy/etc). + uptr pc = StackTrace::GetPreviousInstructionPc(stack->trace[0]); + Symbolizer::Get()->SymbolizePC(pc, &ai, 1); + } +#endif + ReportErrorSummary(error_type, ai.file, ai.line, ai.function); +} + +LoadedModule::LoadedModule(const char *module_name, uptr base_address) { + full_name_ = internal_strdup(module_name); + base_address_ = base_address; + n_ranges_ = 0; +} + +void LoadedModule::addAddressRange(uptr beg, uptr end) { + CHECK_LT(n_ranges_, kMaxNumberOfAddressRanges); + ranges_[n_ranges_].beg = beg; + ranges_[n_ranges_].end = end; + n_ranges_++; +} + +bool LoadedModule::containsAddress(uptr address) const { + for (uptr i = 0; i < n_ranges_; i++) { + if (ranges_[i].beg <= address && address < ranges_[i].end) + return true; + } + return false; +} + +char *StripModuleName(const char *module) { + if (module == 0) + return 0; + const char *short_module_name = internal_strrchr(module, '/'); + if (short_module_name) + short_module_name += 1; + else + short_module_name = module; + return internal_strdup(short_module_name); +} + +} // namespace __sanitizer + +using namespace __sanitizer; // NOLINT + +extern "C" { +void __sanitizer_set_report_path(const char *path) { + if (!path) + return; + uptr len = internal_strlen(path); + if (len > sizeof(report_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(); + } + if (report_fd != kStdoutFd && + report_fd != kStderrFd && + report_fd != kInvalidFd) + internal_close(report_fd); + report_fd = kInvalidFd; + log_to_file = false; + if (internal_strcmp(path, "stdout") == 0) { + report_fd = kStdoutFd; + } else if (internal_strcmp(path, "stderr") == 0) { + report_fd = kStderrFd; + } else { + internal_strncpy(report_path_prefix, path, sizeof(report_path_prefix)); + report_path_prefix[len] = '\0'; + log_to_file = true; + } +} + +void NOINLINE __sanitizer_sandbox_on_notify(void *reserved) { + (void)reserved; + PrepareForSandboxing(); +} + +void __sanitizer_report_error_summary(const char *error_summary) { + Printf("%s\n", error_summary); +} +} // extern "C" +//===-- sanitizer_deadlock_detector1.cc -----------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// Deadlock detector implementation based on NxN adjacency bit matrix. +// +//===----------------------------------------------------------------------===// + +#include "sanitizer_deadlock_detector_interface.h" +#include "sanitizer_deadlock_detector.h" +#include "sanitizer_allocator_internal.h" +#include "sanitizer_placement_new.h" +#include "sanitizer_mutex.h" + +namespace __sanitizer { + +typedef TwoLevelBitVector<> DDBV; // DeadlockDetector's bit vector. + +struct DDPhysicalThread { +}; + +struct DDLogicalThread { + u64 ctx; + DeadlockDetectorTLS<DDBV> dd; + DDReport rep; +}; + +struct DDetectorImpl : public DDetector { + SpinMutex mtx; + DeadlockDetector<DDBV> dd; + + DDetectorImpl(); + + virtual DDPhysicalThread* CreatePhysicalThread(); + virtual void DestroyPhysicalThread(DDPhysicalThread *pt); + + virtual DDLogicalThread* CreateLogicalThread(u64 ctx); + virtual void DestroyLogicalThread(DDLogicalThread *lt); + + virtual void MutexInit(DDMutex *m, u32 stk, u64 ctx); + virtual DDReport *MutexLock(DDPhysicalThread *pt, DDLogicalThread *lt, + DDMutex *m, bool writelock, bool trylock); + virtual DDReport *MutexUnlock(DDPhysicalThread *pt, DDLogicalThread *lt, + DDMutex *m, bool writelock); + virtual void MutexDestroy(DDPhysicalThread *pt, DDLogicalThread *lt, + DDMutex *m); + + void MutexEnsureID(DDLogicalThread *lt, DDMutex *m); +}; + +DDetector *DDetector::Create() { + void *mem = MmapOrDie(sizeof(DDetectorImpl), "deadlock detector"); + return new(mem) DDetectorImpl(); +} + +DDetectorImpl::DDetectorImpl() { + dd.clear(); +} + +DDPhysicalThread* DDetectorImpl::CreatePhysicalThread() { + return 0; +} + +void DDetectorImpl::DestroyPhysicalThread(DDPhysicalThread *pt) { +} + +DDLogicalThread* DDetectorImpl::CreateLogicalThread(u64 ctx) { + DDLogicalThread *lt = (DDLogicalThread*)InternalAlloc(sizeof(*lt)); + lt->ctx = ctx; + lt->dd.clear(); + return lt; +} + +void DDetectorImpl::DestroyLogicalThread(DDLogicalThread *lt) { + lt->~DDLogicalThread(); + InternalFree(lt); +} + +void DDetectorImpl::MutexInit(DDMutex *m, u32 stk, u64 ctx) { + m->id = 0; + m->stk = stk; + m->ctx = ctx; +} + +void DDetectorImpl::MutexEnsureID(DDLogicalThread *lt, DDMutex *m) { + if (!dd.nodeBelongsToCurrentEpoch(m->id)) + m->id = dd.newNode(reinterpret_cast<uptr>(m)); + dd.ensureCurrentEpoch(<->dd); +} + +DDReport *DDetectorImpl::MutexLock(DDPhysicalThread *pt, DDLogicalThread *lt, + DDMutex *m, bool writelock, bool trylock) { + if (dd.onFirstLock(<->dd, m->id)) + return 0; + SpinMutexLock lk(&mtx); + MutexEnsureID(lt, m); + CHECK(!dd.isHeld(<->dd, m->id)); + // Printf("T%d MutexLock: %zx\n", thr->tid, s->deadlock_detector_id); + bool has_deadlock = trylock + ? dd.onTryLock(<->dd, m->id) + : dd.onLock(<->dd, m->id); + DDReport *rep = 0; + if (has_deadlock) { + uptr path[10]; + uptr len = dd.findPathToHeldLock(<->dd, m->id, + path, ARRAY_SIZE(path)); + CHECK_GT(len, 0U); // Hm.. cycle of 10 locks? I'd like to see that. + rep = <->rep; + rep->n = len; + for (uptr i = 0; i < len; i++) { + DDMutex *m0 = (DDMutex*)dd.getData(path[i]); + DDMutex *m1 = (DDMutex*)dd.getData(path[i < len - 1 ? i + 1 : 0]); + rep->loop[i].thr_ctx = 0; // don't know + rep->loop[i].mtx_ctx0 = m0->ctx; + rep->loop[i].mtx_ctx1 = m1->ctx; + rep->loop[i].stk = m0->stk; + } + } + return rep; +} + +DDReport *DDetectorImpl::MutexUnlock(DDPhysicalThread *pt, DDLogicalThread *lt, + DDMutex *m, bool writelock) { + // Printf("T%d MutexUnlock: %zx; recursion %d\n", thr->tid, + // s->deadlock_detector_id, s->recursion); + dd.onUnlock(<->dd, m->id); + return 0; +} + +void DDetectorImpl::MutexDestroy(DDPhysicalThread *pt, DDLogicalThread *lt, + DDMutex *m) { + if (!m->id) return; + SpinMutexLock lk(&mtx); + if (dd.nodeBelongsToCurrentEpoch(m->id)) + dd.removeNode(m->id); + m->id = 0; +} + +} // namespace __sanitizer +//===-- sanitizer_flags.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/AddressSanitizer runtime. +// +//===----------------------------------------------------------------------===// + +#include "sanitizer_flags.h" + +#include "sanitizer_common.h" +#include "sanitizer_libc.h" + +namespace __sanitizer { + +CommonFlags common_flags_dont_use; + +void SetCommonFlagsDefaults(CommonFlags *f) { + f->symbolize = true; + f->external_symbolizer_path = 0; + f->allow_addr2line = false; + f->strip_path_prefix = ""; + f->fast_unwind_on_fatal = false; + f->fast_unwind_on_malloc = true; + f->handle_ioctl = false; + f->malloc_context_size = 1; + f->log_path = "stderr"; + f->verbosity = 0; + f->detect_leaks = false; + f->leak_check_at_exit = true; + f->allocator_may_return_null = false; + f->print_summary = true; + f->check_printf = false; + // TODO(glider): tools may want to set different defaults for handle_segv. + f->handle_segv = SANITIZER_NEEDS_SEGV; + f->allow_user_segv_handler = false; + f->use_sigaltstack = false; + f->detect_deadlocks = false; + f->clear_shadow_mmap_threshold = 64 * 1024; + f->color = "auto"; +} + +void ParseCommonFlagsFromString(CommonFlags *f, const char *str) { + ParseFlag(str, &f->symbolize, "symbolize"); + ParseFlag(str, &f->external_symbolizer_path, "external_symbolizer_path"); + ParseFlag(str, &f->allow_addr2line, "allow_addr2line"); + ParseFlag(str, &f->strip_path_prefix, "strip_path_prefix"); + ParseFlag(str, &f->fast_unwind_on_fatal, "fast_unwind_on_fatal"); + ParseFlag(str, &f->fast_unwind_on_malloc, "fast_unwind_on_malloc"); + ParseFlag(str, &f->handle_ioctl, "handle_ioctl"); + ParseFlag(str, &f->malloc_context_size, "malloc_context_size"); + ParseFlag(str, &f->log_path, "log_path"); + ParseFlag(str, &f->verbosity, "verbosity"); + ParseFlag(str, &f->detect_leaks, "detect_leaks"); + ParseFlag(str, &f->leak_check_at_exit, "leak_check_at_exit"); + ParseFlag(str, &f->allocator_may_return_null, "allocator_may_return_null"); + ParseFlag(str, &f->print_summary, "print_summary"); + ParseFlag(str, &f->check_printf, "check_printf"); + ParseFlag(str, &f->handle_segv, "handle_segv"); + ParseFlag(str, &f->allow_user_segv_handler, "allow_user_segv_handler"); + ParseFlag(str, &f->use_sigaltstack, "use_sigaltstack"); + ParseFlag(str, &f->detect_deadlocks, "detect_deadlocks"); + ParseFlag(str, &f->clear_shadow_mmap_threshold, + "clear_shadow_mmap_threshold"); + ParseFlag(str, &f->color, "color"); + + // Do a sanity check for certain flags. + if (f->malloc_context_size < 1) + f->malloc_context_size = 1; +} + +static bool GetFlagValue(const char *env, const char *name, + const char **value, int *value_length) { + if (env == 0) + return false; + const char *pos = 0; + for (;;) { + pos = internal_strstr(env, name); + if (pos == 0) + return false; + if (pos != env && ((pos[-1] >= 'a' && pos[-1] <= 'z') || pos[-1] == '_')) { + // Seems to be middle of another flag name or value. + env = pos + 1; + continue; + } + break; + } + pos += internal_strlen(name); + const char *end; + if (pos[0] != '=') { + end = pos; + } else { + pos += 1; + if (pos[0] == '"') { + pos += 1; + end = internal_strchr(pos, '"'); + } else if (pos[0] == '\'') { + pos += 1; + end = internal_strchr(pos, '\''); + } else { + // Read until the next space or colon. + end = pos + internal_strcspn(pos, " :"); + } + if (end == 0) + end = pos + internal_strlen(pos); + } + *value = pos; + *value_length = end - pos; + return true; +} + +static bool StartsWith(const char *flag, int flag_length, const char *value) { + if (!flag || !value) + return false; + int value_length = internal_strlen(value); + return (flag_length >= value_length) && + (0 == internal_strncmp(flag, value, value_length)); +} + +void ParseFlag(const char *env, bool *flag, const char *name) { + const char *value; + int value_length; + if (!GetFlagValue(env, name, &value, &value_length)) + return; + if (StartsWith(value, value_length, "0") || + StartsWith(value, value_length, "no") || + StartsWith(value, value_length, "false")) + *flag = false; + if (StartsWith(value, value_length, "1") || + StartsWith(value, value_length, "yes") || + StartsWith(value, value_length, "true")) + *flag = true; +} + +void ParseFlag(const char *env, int *flag, const char *name) { + const char *value; + int value_length; + if (!GetFlagValue(env, name, &value, &value_length)) + return; + *flag = static_cast<int>(internal_atoll(value)); +} + +void ParseFlag(const char *env, uptr *flag, const char *name) { + const char *value; + int value_length; + if (!GetFlagValue(env, name, &value, &value_length)) + return; + *flag = static_cast<uptr>(internal_atoll(value)); +} + +static LowLevelAllocator allocator_for_flags; + +void ParseFlag(const char *env, const char **flag, const char *name) { + const char *value; + int value_length; + if (!GetFlagValue(env, name, &value, &value_length)) + return; + // Copy the flag value. Don't use locks here, as flags are parsed at + // tool startup. + char *value_copy = (char*)(allocator_for_flags.Allocate(value_length + 1)); + internal_memcpy(value_copy, value, value_length); + value_copy[value_length] = '\0'; + *flag = value_copy; +} + +} // namespace __sanitizer +//===-- sanitizer_libc.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. See sanitizer_libc.h for details. +//===----------------------------------------------------------------------===// +#include "sanitizer_allocator_internal.h" +#include "sanitizer_common.h" +#include "sanitizer_libc.h" + +namespace __sanitizer { + +// Make the compiler think that something is going on there. +static inline void break_optimization(void *arg) { +#if _MSC_VER + // FIXME: make sure this is actually enough. + __asm; +#else + __asm__ __volatile__("" : : "r" (arg) : "memory"); +#endif +} + +s64 internal_atoll(const char *nptr) { + return internal_simple_strtoll(nptr, (char**)0, 10); +} + +void *internal_memchr(const void *s, int c, uptr n) { + const char* t = (char*)s; + for (uptr i = 0; i < n; ++i, ++t) + if (*t == c) + return (void*)t; + return 0; +} + +int internal_memcmp(const void* s1, const void* s2, uptr n) { + const char* t1 = (char*)s1; + const char* t2 = (char*)s2; + for (uptr i = 0; i < n; ++i, ++t1, ++t2) + if (*t1 != *t2) + return *t1 < *t2 ? -1 : 1; + return 0; +} + +void *internal_memcpy(void *dest, const void *src, uptr n) { + char *d = (char*)dest; + char *s = (char*)src; + for (uptr i = 0; i < n; ++i) + d[i] = s[i]; + return dest; +} + +void *internal_memmove(void *dest, const void *src, uptr n) { + char *d = (char*)dest; + char *s = (char*)src; + sptr i, signed_n = (sptr)n; + CHECK_GE(signed_n, 0); + if (d < s) { + for (i = 0; i < signed_n; ++i) + d[i] = s[i]; + } else { + if (d > s && signed_n > 0) + for (i = signed_n - 1; i >= 0 ; --i) { + d[i] = s[i]; + } + } + return dest; +} + +// Semi-fast bzero for 16-aligned data. Still far from peak performance. +void internal_bzero_aligned16(void *s, uptr n) { + struct S16 { u64 a, b; } ALIGNED(16); + CHECK_EQ((reinterpret_cast<uptr>(s) | n) & 15, 0); + for (S16 *p = reinterpret_cast<S16*>(s), *end = p + n / 16; p < end; p++) { + p->a = p->b = 0; + break_optimization(0); // Make sure this does not become memset. + } +} + +void *internal_memset(void* s, int c, uptr n) { + // The next line prevents Clang from making a call to memset() instead of the + // loop below. + // FIXME: building the runtime with -ffreestanding is a better idea. However + // there currently are linktime problems due to PR12396. + char volatile *t = (char*)s; + for (uptr i = 0; i < n; ++i, ++t) { + *t = c; + } + return s; +} + +uptr internal_strcspn(const char *s, const char *reject) { + uptr i; + for (i = 0; s[i]; i++) { + if (internal_strchr(reject, s[i]) != 0) + return i; + } + return i; +} + +char* internal_strdup(const char *s) { + uptr len = internal_strlen(s); + char *s2 = (char*)InternalAlloc(len + 1); + internal_memcpy(s2, s, len); + s2[len] = 0; + return s2; +} + +int internal_strcmp(const char *s1, const char *s2) { + while (true) { + unsigned c1 = *s1; + unsigned c2 = *s2; + if (c1 != c2) return (c1 < c2) ? -1 : 1; + if (c1 == 0) break; + s1++; + s2++; + } + return 0; +} + +int internal_strncmp(const char *s1, const char *s2, uptr n) { + for (uptr i = 0; i < n; i++) { + unsigned c1 = *s1; + unsigned c2 = *s2; + if (c1 != c2) return (c1 < c2) ? -1 : 1; + if (c1 == 0) break; + s1++; + s2++; + } + return 0; +} + +char* internal_strchr(const char *s, int c) { + while (true) { + if (*s == (char)c) + return (char*)s; + if (*s == 0) + return 0; + s++; + } +} + +char *internal_strchrnul(const char *s, int c) { + char *res = internal_strchr(s, c); + if (!res) + res = (char*)s + internal_strlen(s); + return res; +} + +char *internal_strrchr(const char *s, int c) { + const char *res = 0; + for (uptr i = 0; s[i]; i++) { + if (s[i] == c) res = s + i; + } + return (char*)res; +} + +uptr internal_strlen(const char *s) { + uptr i = 0; + while (s[i]) i++; + return i; +} + +char *internal_strncat(char *dst, const char *src, uptr n) { + uptr len = internal_strlen(dst); + uptr i; + for (i = 0; i < n && src[i]; i++) + dst[len + i] = src[i]; + dst[len + i] = 0; + return dst; +} + +char *internal_strncpy(char *dst, const char *src, uptr n) { + uptr i; + for (i = 0; i < n && src[i]; i++) + dst[i] = src[i]; + internal_memset(dst + i, '\0', n - i); + return dst; +} + +uptr internal_strnlen(const char *s, uptr maxlen) { + uptr i = 0; + while (i < maxlen && s[i]) i++; + return i; +} + +char *internal_strstr(const char *haystack, const char *needle) { + // This is O(N^2), but we are not using it in hot places. + uptr len1 = internal_strlen(haystack); + uptr len2 = internal_strlen(needle); + if (len1 < len2) return 0; + for (uptr pos = 0; pos <= len1 - len2; pos++) { + if (internal_memcmp(haystack + pos, needle, len2) == 0) + return (char*)haystack + pos; + } + return 0; +} + +s64 internal_simple_strtoll(const char *nptr, char **endptr, int base) { + CHECK_EQ(base, 10); + while (IsSpace(*nptr)) nptr++; + int sgn = 1; + u64 res = 0; + bool have_digits = false; + char *old_nptr = (char*)nptr; + if (*nptr == '+') { + sgn = 1; + nptr++; + } else if (*nptr == '-') { + sgn = -1; + nptr++; + } + while (IsDigit(*nptr)) { + res = (res <= UINT64_MAX / 10) ? res * 10 : UINT64_MAX; + int digit = ((*nptr) - '0'); + res = (res <= UINT64_MAX - digit) ? res + digit : UINT64_MAX; + have_digits = true; + nptr++; + } + if (endptr != 0) { + *endptr = (have_digits) ? (char*)nptr : old_nptr; + } + if (sgn > 0) { + return (s64)(Min((u64)INT64_MAX, res)); + } else { + return (res > INT64_MAX) ? INT64_MIN : ((s64)res * -1); + } +} + +bool mem_is_zero(const char *beg, uptr size) { + CHECK_LE(size, 1ULL << FIRST_32_SECOND_64(30, 40)); // Sanity check. + const char *end = beg + size; + uptr *aligned_beg = (uptr *)RoundUpTo((uptr)beg, sizeof(uptr)); + uptr *aligned_end = (uptr *)RoundDownTo((uptr)end, sizeof(uptr)); + uptr all = 0; + // Prologue. + for (const char *mem = beg; mem < (char*)aligned_beg && mem < end; mem++) + all |= *mem; + // Aligned loop. + for (; aligned_beg < aligned_end; aligned_beg++) + all |= *aligned_beg; + // Epilogue. + if ((char*)aligned_end >= beg) + for (const char *mem = (char*)aligned_end; mem < end; mem++) + all |= *mem; + return all == 0; +} + +} // namespace __sanitizer +//===-- sanitizer_printf.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. +// +// Internal printf function, used inside run-time libraries. +// We can't use libc printf because we intercept some of the functions used +// inside it. +//===----------------------------------------------------------------------===// + + +#include "sanitizer_common.h" +#include "sanitizer_flags.h" +#include "sanitizer_libc.h" + +#include <stdio.h> +#include <stdarg.h> + +#if SANITIZER_WINDOWS && !defined(va_copy) +# define va_copy(dst, src) ((dst) = (src)) +#endif + +namespace __sanitizer { + +StaticSpinMutex CommonSanitizerReportMutex; + +static int AppendChar(char **buff, const char *buff_end, char c) { + if (*buff < buff_end) { + **buff = c; + (*buff)++; + } + return 1; +} + +// Appends number in a given base to buffer. If its length is less than +// |minimal_num_length|, it is padded with leading zeroes or spaces, depending +// on the value of |pad_with_zero|. +static int AppendNumber(char **buff, const char *buff_end, u64 absolute_value, + u8 base, u8 minimal_num_length, bool pad_with_zero, + bool negative) { + uptr const kMaxLen = 30; + RAW_CHECK(base == 10 || base == 16); + RAW_CHECK(base == 10 || !negative); + RAW_CHECK(absolute_value || !negative); + RAW_CHECK(minimal_num_length < kMaxLen); + int result = 0; + if (negative && minimal_num_length) + --minimal_num_length; + if (negative && pad_with_zero) + result += AppendChar(buff, buff_end, '-'); + uptr num_buffer[kMaxLen]; + int pos = 0; + do { + RAW_CHECK_MSG((uptr)pos < kMaxLen, "AppendNumber buffer overflow"); + num_buffer[pos++] = absolute_value % base; + absolute_value /= base; + } while (absolute_value > 0); + if (pos < minimal_num_length) { + // Make sure compiler doesn't insert call to memset here. + internal_memset(&num_buffer[pos], 0, + sizeof(num_buffer[0]) * (minimal_num_length - pos)); + pos = minimal_num_length; + } + RAW_CHECK(pos > 0); + pos--; + for (; pos >= 0 && num_buffer[pos] == 0; pos--) { + char c = (pad_with_zero || pos == 0) ? '0' : ' '; + result += AppendChar(buff, buff_end, c); + } + if (negative && !pad_with_zero) result += AppendChar(buff, buff_end, '-'); + for (; pos >= 0; pos--) { + char digit = static_cast<char>(num_buffer[pos]); + result += AppendChar(buff, buff_end, (digit < 10) ? '0' + digit + : 'a' + digit - 10); + } + return result; +} + +static int AppendUnsigned(char **buff, const char *buff_end, u64 num, u8 base, + u8 minimal_num_length, bool pad_with_zero) { + return AppendNumber(buff, buff_end, num, base, minimal_num_length, + pad_with_zero, false /* negative */); +} + +static int AppendSignedDecimal(char **buff, const char *buff_end, s64 num, + u8 minimal_num_length, bool pad_with_zero) { + bool negative = (num < 0); + return AppendNumber(buff, buff_end, (u64)(negative ? -num : num), 10, + minimal_num_length, pad_with_zero, negative); +} + +static int AppendString(char **buff, const char *buff_end, int precision, + const char *s) { + if (s == 0) + s = "<null>"; + int result = 0; + for (; *s; s++) { + if (precision >= 0 && result >= precision) + break; + result += AppendChar(buff, buff_end, *s); + } + return result; +} + +static int AppendPointer(char **buff, const char *buff_end, u64 ptr_value) { + int result = 0; + result += AppendString(buff, buff_end, -1, "0x"); + result += AppendUnsigned(buff, buff_end, ptr_value, 16, + (SANITIZER_WORDSIZE == 64) ? 12 : 8, true); + return result; +} + +int VSNPrintf(char *buff, int buff_length, + const char *format, va_list args) { + static const char *kPrintfFormatsHelp = + "Supported Printf formats: %([0-9]*)?(z|ll)?{d,u,x}; %p; %(\\.\\*)?s; %c\n"; + RAW_CHECK(format); + RAW_CHECK(buff_length > 0); + const char *buff_end = &buff[buff_length - 1]; + const char *cur = format; + int result = 0; + for (; *cur; cur++) { + if (*cur != '%') { + result += AppendChar(&buff, buff_end, *cur); + continue; + } + cur++; + bool have_width = (*cur >= '0' && *cur <= '9'); + bool pad_with_zero = (*cur == '0'); + int width = 0; + if (have_width) { + while (*cur >= '0' && *cur <= '9') { + width = width * 10 + *cur++ - '0'; + } + } + bool have_precision = (cur[0] == '.' && cur[1] == '*'); + int precision = -1; + if (have_precision) { + cur += 2; + precision = va_arg(args, int); + } + bool have_z = (*cur == 'z'); + cur += have_z; + bool have_ll = !have_z && (cur[0] == 'l' && cur[1] == 'l'); + cur += have_ll * 2; + s64 dval; + u64 uval; + bool have_flags = have_width | have_z | have_ll; + // Only %s supports precision for now + CHECK(!(precision >= 0 && *cur != 's')); + switch (*cur) { + case 'd': { + dval = have_ll ? va_arg(args, s64) + : have_z ? va_arg(args, sptr) + : va_arg(args, int); + result += AppendSignedDecimal(&buff, buff_end, dval, width, + pad_with_zero); + break; + } + case 'u': + case 'x': { + uval = have_ll ? va_arg(args, u64) + : have_z ? va_arg(args, uptr) + : va_arg(args, unsigned); + result += AppendUnsigned(&buff, buff_end, uval, + (*cur == 'u') ? 10 : 16, width, pad_with_zero); + break; + } + case 'p': { + RAW_CHECK_MSG(!have_flags, kPrintfFormatsHelp); + result += AppendPointer(&buff, buff_end, va_arg(args, uptr)); + break; + } + case 's': { + RAW_CHECK_MSG(!have_flags, kPrintfFormatsHelp); + result += AppendString(&buff, buff_end, precision, va_arg(args, char*)); + break; + } + case 'c': { + RAW_CHECK_MSG(!have_flags, kPrintfFormatsHelp); + result += AppendChar(&buff, buff_end, va_arg(args, int)); + break; + } + case '%' : { + RAW_CHECK_MSG(!have_flags, kPrintfFormatsHelp); + result += AppendChar(&buff, buff_end, '%'); + break; + } + default: { + RAW_CHECK_MSG(false, kPrintfFormatsHelp); + } + } + } + RAW_CHECK(buff <= buff_end); + AppendChar(&buff, buff_end + 1, '\0'); + return result; +} + +static void (*PrintfAndReportCallback)(const char *); +void SetPrintfAndReportCallback(void (*callback)(const char *)) { + PrintfAndReportCallback = callback; +} + +// Can be overriden in frontend. +#if SANITIZER_SUPPORTS_WEAK_HOOKS +SANITIZER_INTERFACE_ATTRIBUTE SANITIZER_WEAK_ATTRIBUTE +void OnPrint(const char *str) { + (void)str; +} +#elif defined(SANITIZER_GO) && defined(TSAN_EXTERNAL_HOOKS) +void OnPrint(const char *str); +#else +void OnPrint(const char *str) { + (void)str; +} +#endif + +static void CallPrintfAndReportCallback(const char *str) { + OnPrint(str); + if (PrintfAndReportCallback) + PrintfAndReportCallback(str); +} + +static void SharedPrintfCode(bool append_pid, const char *format, + va_list args) { + va_list args2; + va_copy(args2, args); + const int kLen = 16 * 1024; + // |local_buffer| is small enough not to overflow the stack and/or violate + // the stack limit enforced by TSan (-Wframe-larger-than=512). On the other + // hand, the bigger the buffer is, the more the chance the error report will + // fit into it. + char local_buffer[400]; + int needed_length; + char *buffer = local_buffer; + int buffer_size = ARRAY_SIZE(local_buffer); + // First try to print a message using a local buffer, and then fall back to + // mmaped buffer. + for (int use_mmap = 0; use_mmap < 2; use_mmap++) { + if (use_mmap) { + va_end(args); + va_copy(args, args2); + buffer = (char*)MmapOrDie(kLen, "Report"); + buffer_size = kLen; + } + needed_length = 0; + if (append_pid) { + int pid = internal_getpid(); + needed_length += internal_snprintf(buffer, buffer_size, "==%d==", pid); + if (needed_length >= buffer_size) { + // The pid doesn't fit into the current buffer. + if (!use_mmap) + continue; + RAW_CHECK_MSG(needed_length < kLen, "Buffer in Report is too short!\n"); + } + } + needed_length += VSNPrintf(buffer + needed_length, + buffer_size - needed_length, format, args); + if (needed_length >= buffer_size) { + // The message doesn't fit into the current buffer. + if (!use_mmap) + continue; + RAW_CHECK_MSG(needed_length < kLen, "Buffer in Report is too short!\n"); + } + // If the message fit into the buffer, print it and exit. + break; + } + RawWrite(buffer); + AndroidLogWrite(buffer); + CallPrintfAndReportCallback(buffer); + // If we had mapped any memory, clean up. + if (buffer != local_buffer) + UnmapOrDie((void *)buffer, buffer_size); + va_end(args2); +} + +FORMAT(1, 2) +void Printf(const char *format, ...) { + va_list args; + va_start(args, format); + SharedPrintfCode(false, format, args); + va_end(args); +} + +// Like Printf, but prints the current PID before the output string. +FORMAT(1, 2) +void Report(const char *format, ...) { + va_list args; + va_start(args, format); + SharedPrintfCode(true, format, args); + va_end(args); +} + +// Writes at most "length" symbols to "buffer" (including trailing '\0'). +// Returns the number of symbols that should have been written to buffer +// (not including trailing '\0'). Thus, the string is truncated +// iff return value is not less than "length". +FORMAT(3, 4) +int internal_snprintf(char *buffer, uptr length, const char *format, ...) { + va_list args; + va_start(args, format); + int needed_length = VSNPrintf(buffer, length, format, args); + va_end(args); + return needed_length; +} + +FORMAT(2, 3) +void InternalScopedString::append(const char *format, ...) { + CHECK_LT(length_, size()); + va_list args; + va_start(args, format); + VSNPrintf(data() + length_, size() - length_, format, args); + va_end(args); + length_ += internal_strlen(data() + length_); + CHECK_LT(length_, size()); +} + +} // namespace __sanitizer +//===-- sanitizer_suppressions.cc -----------------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// Suppression parsing/matching code shared between TSan and LSan. +// +//===----------------------------------------------------------------------===// + +#include "sanitizer_suppressions.h" + +#include "sanitizer_allocator_internal.h" +#include "sanitizer_common.h" +#include "sanitizer_libc.h" + +namespace __sanitizer { + +static const char *const kTypeStrings[SuppressionTypeCount] = { + "none", "race", "mutex", "thread", "signal", "leak", "called_from_lib" +}; + +bool TemplateMatch(char *templ, const char *str) { + if (str == 0 || 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 == 0 || (tpos1 && tpos1 < tpos)) + tpos = tpos1; + if (tpos != 0) + 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 == 0) + return false; + if (start && spos != str0) + return false; + start = false; + asterisk = false; + } + return true; +} + +bool SuppressionContext::Match(const char *str, SuppressionType type, + Suppression **s) { + can_parse_ = false; + uptr i; + for (i = 0; i < suppressions_.size(); i++) + if (type == suppressions_[i].type && + TemplateMatch(suppressions_[i].templ, str)) + break; + if (i == suppressions_.size()) return false; + *s = &suppressions_[i]; + return true; +} + +static const char *StripPrefix(const char *str, const char *prefix) { + while (str && *str == *prefix) { + str++; + prefix++; + } + if (!*prefix) + return str; + return 0; +} + +void SuppressionContext::Parse(const char *str) { + // Context must not mutate once Match has been called. + CHECK(can_parse_); + const char *line = str; + while (line) { + while (line[0] == ' ' || line[0] == '\t') + line++; + const char *end = internal_strchr(line, '\n'); + if (end == 0) + end = line + internal_strlen(line); + if (line != end && line[0] != '#') { + const char *end2 = end; + while (line != end2 && (end2[-1] == ' ' || end2[-1] == '\t')) + end2--; + int type; + for (type = 0; type < SuppressionTypeCount; type++) { + const char *next_char = StripPrefix(line, kTypeStrings[type]); + if (next_char && *next_char == ':') { + line = ++next_char; + break; + } + } + if (type == SuppressionTypeCount) { + Printf("%s: failed to parse suppressions\n", SanitizerToolName); + Die(); + } + Suppression s; + s.type = static_cast<SuppressionType>(type); + s.templ = (char*)InternalAlloc(end2 - line + 1); + internal_memcpy(s.templ, line, end2 - line); + s.templ[end2 - line] = 0; + s.hit_count = 0; + s.weight = 0; + suppressions_.push_back(s); + } + if (end[0] == 0) + break; + line = end + 1; + } +} + +uptr SuppressionContext::SuppressionCount() const { + return suppressions_.size(); +} + +const Suppression *SuppressionContext::SuppressionAt(uptr i) const { + CHECK_LT(i, suppressions_.size()); + return &suppressions_[i]; +} + +void SuppressionContext::GetMatched( + InternalMmapVector<Suppression *> *matched) { + for (uptr i = 0; i < suppressions_.size(); i++) + if (suppressions_[i].hit_count) + matched->push_back(&suppressions_[i]); +} + +const char *SuppressionTypeString(SuppressionType t) { + CHECK(t < SuppressionTypeCount); + return kTypeStrings[t]; +} + +} // namespace __sanitizer +//===-- 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), os_id(0), user_id(0), status(ThreadStatusInvalid), + detached(false), reuse_count(0), 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, void *arg) { + status = ThreadStatusRunning; + os_id = _os_id; + 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; + reuse_count++; + SetName(0); + OnReset(); +} + +// ThreadRegistry implementation. + +const u32 ThreadRegistry::kUnknownTid = ~0U; + +ThreadRegistry::ThreadRegistry(ThreadContextFactory factory, u32 max_threads, + u32 thread_quarantine_size) + : context_factory_(factory), + max_threads_(max_threads), + thread_quarantine_size_(thread_quarantine_size), + 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 { +#ifndef 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) { + 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) { + 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; + } + 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, 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, arg); +} + +void ThreadRegistry::QuarantinePush(ThreadContextBase *tctx) { + 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(); + 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 +//===-- sanitizer_posix.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 POSIX-specific functions from +// sanitizer_libc.h. +//===----------------------------------------------------------------------===// + +#include "sanitizer_platform.h" +#if SANITIZER_LINUX || SANITIZER_MAC + +#include "sanitizer_common.h" +#include "sanitizer_libc.h" +#include "sanitizer_procmaps.h" +#include "sanitizer_stacktrace.h" + +#include <sys/mman.h> + +namespace __sanitizer { + +// ------------- sanitizer_common.h +uptr GetMmapGranularity() { + return GetPageSize(); +} + +uptr GetMaxVirtualAddress() { +#if SANITIZER_WORDSIZE == 64 +# if defined(__powerpc64__) + // On PowerPC64 we have two different address space layouts: 44- and 46-bit. + // We somehow need to figure our which one we are using now and choose + // one of 0x00000fffffffffffUL and 0x00003fffffffffffUL. + // Note that with 'ulimit -s unlimited' the stack is moved away from the top + // of the address space, so simply checking the stack address is not enough. + return (1ULL << 44) - 1; // 0x00000fffffffffffUL +# elif defined(__aarch64__) + return (1ULL << 39) - 1; +# else + return (1ULL << 47) - 1; // 0x00007fffffffffffUL; +# endif +#else // SANITIZER_WORDSIZE == 32 + // FIXME: We can probably lower this on Android? + return (1ULL << 32) - 1; // 0xffffffff; +#endif // SANITIZER_WORDSIZE +} + +void *MmapOrDie(uptr size, const char *mem_type) { + size = RoundUpTo(size, GetPageSizeCached()); + uptr res = internal_mmap(0, size, + PROT_READ | PROT_WRITE, + MAP_PRIVATE | MAP_ANON, -1, 0); + int reserrno; + if (internal_iserror(res, &reserrno)) { + static int recursion_count; + if (recursion_count) { + // The Report() and CHECK calls below may call mmap recursively and fail. + // If we went into recursion, just die. + RawWrite("ERROR: Failed to mmap\n"); + Die(); + } + recursion_count++; + Report("ERROR: %s failed to allocate 0x%zx (%zd) bytes of %s: %d\n", + SanitizerToolName, size, size, mem_type, reserrno); + DumpProcessMap(); + CHECK("unable to mmap" && 0); + } + return (void *)res; +} + +void UnmapOrDie(void *addr, uptr size) { + if (!addr || !size) return; + uptr res = internal_munmap(addr, size); + if (internal_iserror(res)) { + Report("ERROR: %s failed to deallocate 0x%zx (%zd) bytes at address %p\n", + SanitizerToolName, size, size, addr); + CHECK("unable to unmap" && 0); + } +} + +void *MmapNoReserveOrDie(uptr size, const char *mem_type) { + uptr PageSize = GetPageSizeCached(); + uptr p = internal_mmap(0, + RoundUpTo(size, PageSize), + PROT_READ | PROT_WRITE, + MAP_PRIVATE | MAP_ANON | MAP_NORESERVE, + -1, 0); + int reserrno; + if (internal_iserror(p, &reserrno)) { + Report("ERROR: " + "%s failed to allocate noreserve 0x%zx (%zd) bytes for '%s' (%d)\n", + SanitizerToolName, size, size, mem_type, reserrno); + CHECK("unable to mmap" && 0); + } + return (void *)p; +} + +void *MmapFixedNoReserve(uptr fixed_addr, uptr size) { + uptr PageSize = GetPageSizeCached(); + uptr p = internal_mmap((void*)(fixed_addr & ~(PageSize - 1)), + RoundUpTo(size, PageSize), + PROT_READ | PROT_WRITE, + MAP_PRIVATE | MAP_ANON | MAP_FIXED | MAP_NORESERVE, + -1, 0); + int reserrno; + if (internal_iserror(p, &reserrno)) + Report("ERROR: " + "%s failed to allocate 0x%zx (%zd) bytes at address %zu (%d)\n", + SanitizerToolName, size, size, fixed_addr, reserrno); + return (void *)p; +} + +void *MmapFixedOrDie(uptr fixed_addr, uptr size) { + uptr PageSize = GetPageSizeCached(); + uptr p = internal_mmap((void*)(fixed_addr & ~(PageSize - 1)), + RoundUpTo(size, PageSize), + PROT_READ | PROT_WRITE, + MAP_PRIVATE | MAP_ANON | MAP_FIXED, + -1, 0); + int reserrno; + if (internal_iserror(p, &reserrno)) { + Report("ERROR:" + " %s failed to allocate 0x%zx (%zd) bytes at address %zu (%d)\n", + SanitizerToolName, size, size, fixed_addr, reserrno); + CHECK("unable to mmap" && 0); + } + return (void *)p; +} + +void *Mprotect(uptr fixed_addr, uptr size) { + return (void *)internal_mmap((void*)fixed_addr, size, + PROT_NONE, + MAP_PRIVATE | MAP_ANON | MAP_FIXED | + MAP_NORESERVE, -1, 0); +} + +void *MapFileToMemory(const char *file_name, uptr *buff_size) { + uptr openrv = OpenFile(file_name, false); + CHECK(!internal_iserror(openrv)); + fd_t fd = openrv; + uptr fsize = internal_filesize(fd); + CHECK_NE(fsize, (uptr)-1); + CHECK_GT(fsize, 0); + *buff_size = RoundUpTo(fsize, GetPageSizeCached()); + uptr map = internal_mmap(0, *buff_size, PROT_READ, MAP_PRIVATE, fd, 0); + return internal_iserror(map) ? 0 : (void *)map; +} + + +static inline bool IntervalsAreSeparate(uptr start1, uptr end1, + uptr start2, uptr end2) { + CHECK(start1 <= end1); + CHECK(start2 <= end2); + return (end1 < start2) || (end2 < start1); +} + +// FIXME: this is thread-unsafe, but should not cause problems most of the time. +// When the shadow is mapped only a single thread usually exists (plus maybe +// several worker threads on Mac, which aren't expected to map big chunks of +// memory). +bool MemoryRangeIsAvailable(uptr range_start, uptr range_end) { + MemoryMappingLayout proc_maps(/*cache_enabled*/true); + uptr start, end; + while (proc_maps.Next(&start, &end, + /*offset*/0, /*filename*/0, /*filename_size*/0, + /*protection*/0)) { + if (!IntervalsAreSeparate(start, end, range_start, range_end)) + return false; + } + return true; +} + +void DumpProcessMap() { + MemoryMappingLayout proc_maps(/*cache_enabled*/true); + uptr start, end; + const sptr kBufSize = 4095; + char *filename = (char*)MmapOrDie(kBufSize, __func__); + Report("Process memory map follows:\n"); + while (proc_maps.Next(&start, &end, /* file_offset */0, + filename, kBufSize, /* protection */0)) { + Printf("\t%p-%p\t%s\n", (void*)start, (void*)end, filename); + } + Report("End of process memory map.\n"); + UnmapOrDie(filename, kBufSize); +} + +const char *GetPwd() { + return GetEnv("PWD"); +} + +char *FindPathToBinary(const char *name) { + const char *path = GetEnv("PATH"); + if (!path) + return 0; + uptr name_len = internal_strlen(name); + InternalScopedBuffer<char> buffer(kMaxPathLength); + const char *beg = path; + while (true) { + const char *end = internal_strchrnul(beg, ':'); + 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 0; +} + +void MaybeOpenReportFile() { + if (!log_to_file) return; + uptr pid = internal_getpid(); + // If in tracer, use the parent's file. + if (pid == stoptheworld_tracer_pid) + pid = stoptheworld_tracer_ppid; + if (report_fd_pid == pid) return; + InternalScopedBuffer<char> report_path_full(4096); + internal_snprintf(report_path_full.data(), report_path_full.size(), + "%s.%zu", report_path_prefix, pid); + uptr openrv = OpenFile(report_path_full.data(), true); + if (internal_iserror(openrv)) { + report_fd = kStderrFd; + log_to_file = false; + Report("ERROR: Can't open file: %s\n", report_path_full.data()); + Die(); + } + if (report_fd != kInvalidFd) { + // We're in the child. Close the parent's log. + internal_close(report_fd); + } + report_fd = openrv; + report_fd_pid = pid; +} + +void RawWrite(const char *buffer) { + static const char *kRawWriteError = + "RawWrite can't output requested buffer!\n"; + uptr length = (uptr)internal_strlen(buffer); + MaybeOpenReportFile(); + if (length != internal_write(report_fd, buffer, length)) { + internal_write(report_fd, kRawWriteError, internal_strlen(kRawWriteError)); + Die(); + } +} + +bool GetCodeRangeForFile(const char *module, uptr *start, uptr *end) { + uptr s, e, off, prot; + InternalScopedString buff(4096); + MemoryMappingLayout proc_maps(/*cache_enabled*/false); + while (proc_maps.Next(&s, &e, &off, buff.data(), buff.size(), &prot)) { + if ((prot & MemoryMappingLayout::kProtectionExecute) != 0 + && internal_strcmp(module, buff.data()) == 0) { + *start = s; + *end = e; + return true; + } + } + return false; +} + +} // namespace __sanitizer + +#endif // SANITIZER_LINUX || SANITIZER_MAC +//===-- sanitizer_posix_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 libc-dependent POSIX-specific functions +// from sanitizer_libc.h. +//===----------------------------------------------------------------------===// + +#include "sanitizer_platform.h" + +#if SANITIZER_LINUX || SANITIZER_MAC +#include "sanitizer_common.h" +#include "sanitizer_flags.h" +#include "sanitizer_platform_limits_posix.h" +#include "sanitizer_stacktrace.h" + +#include <errno.h> +#include <pthread.h> +#include <signal.h> +#include <stdlib.h> +#include <sys/mman.h> +#include <sys/resource.h> +#include <sys/time.h> +#include <sys/types.h> +#include <unistd.h> + +namespace __sanitizer { + +u32 GetUid() { + return getuid(); +} + +uptr GetThreadSelf() { + return (uptr)pthread_self(); +} + +void FlushUnneededShadowMemory(uptr addr, uptr size) { + madvise((void*)addr, size, MADV_DONTNEED); +} + +void DisableCoreDumper() { + struct rlimit nocore; + nocore.rlim_cur = 0; + nocore.rlim_max = 0; + setrlimit(RLIMIT_CORE, &nocore); +} + +bool StackSizeIsUnlimited() { + struct rlimit rlim; + CHECK_EQ(0, getrlimit(RLIMIT_STACK, &rlim)); + return (rlim.rlim_cur == (uptr)-1); +} + +void SetStackSizeLimitInBytes(uptr limit) { + struct rlimit rlim; + rlim.rlim_cur = limit; + rlim.rlim_max = limit; + if (setrlimit(RLIMIT_STACK, &rlim)) { + Report("ERROR: %s setrlimit() failed %d\n", SanitizerToolName, errno); + Die(); + } + CHECK(!StackSizeIsUnlimited()); +} + +void SleepForSeconds(int seconds) { + sleep(seconds); +} + +void SleepForMillis(int millis) { + usleep(millis * 1000); +} + +void Abort() { + abort(); +} + +int Atexit(void (*function)(void)) { +#ifndef SANITIZER_GO + return atexit(function); +#else + return 0; +#endif +} + +int internal_isatty(fd_t fd) { + return isatty(fd); +} + +#ifndef SANITIZER_GO +// TODO(glider): different tools may require different altstack size. +static const uptr kAltStackSize = SIGSTKSZ * 4; // SIGSTKSZ is not enough. + +void SetAlternateSignalStack() { + stack_t altstack, oldstack; + CHECK_EQ(0, sigaltstack(0, &oldstack)); + // If the alternate stack is already in place, do nothing. + // Android always sets an alternate stack, but it's too small for us. + if (!SANITIZER_ANDROID && !(oldstack.ss_flags & SS_DISABLE)) return; + // TODO(glider): the mapped stack should have the MAP_STACK flag in the + // future. It is not required by man 2 sigaltstack now (they're using + // malloc()). + void* base = MmapOrDie(kAltStackSize, __func__); + altstack.ss_sp = base; + altstack.ss_flags = 0; + altstack.ss_size = kAltStackSize; + CHECK_EQ(0, sigaltstack(&altstack, 0)); +} + +void UnsetAlternateSignalStack() { + stack_t altstack, oldstack; + altstack.ss_sp = 0; + altstack.ss_flags = SS_DISABLE; + altstack.ss_size = 0; + CHECK_EQ(0, sigaltstack(&altstack, &oldstack)); + UnmapOrDie(oldstack.ss_sp, oldstack.ss_size); +} + +typedef void (*sa_sigaction_t)(int, siginfo_t *, void *); +static void MaybeInstallSigaction(int signum, + SignalHandlerType handler) { + if (!IsDeadlySignal(signum)) + return; + struct sigaction sigact; + internal_memset(&sigact, 0, sizeof(sigact)); + sigact.sa_sigaction = (sa_sigaction_t)handler; + sigact.sa_flags = SA_SIGINFO; + if (common_flags()->use_sigaltstack) sigact.sa_flags |= SA_ONSTACK; + CHECK_EQ(0, internal_sigaction(signum, &sigact, 0)); + VReport(1, "Installed the sigaction for signal %d\n", signum); +} + +void InstallDeadlySignalHandlers(SignalHandlerType handler) { + // Set the alternate signal stack for the main thread. + // This will cause SetAlternateSignalStack to be called twice, but the stack + // will be actually set only once. + if (common_flags()->use_sigaltstack) SetAlternateSignalStack(); + MaybeInstallSigaction(SIGSEGV, handler); + MaybeInstallSigaction(SIGBUS, handler); +} +#endif // SANITIZER_GO + +} // namespace __sanitizer + +#endif +//===-- sanitizer_procmaps_linux.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 (Linux-specific parts). +//===----------------------------------------------------------------------===// + +#include "sanitizer_platform.h" +#if 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. + +MemoryMappingLayout::MemoryMappingLayout(bool cache_enabled) { + proc_self_maps_.len = + ReadFileToBuffer("/proc/self/maps", &proc_self_maps_.data, + &proc_self_maps_.mmaped_size, 1 << 26); + 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_; + cached_proc_self_maps_.len = + ReadFileToBuffer("/proc/self/maps", &cached_proc_self_maps_.data, + &cached_proc_self_maps_.mmaped_size, 1 << 26); + 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_; + } +} + +// Parse a hex value in str and update str. +static uptr ParseHex(char **str) { + uptr x = 0; + char *s; + for (s = *str; ; s++) { + char c = *s; + uptr v = 0; + if (c >= '0' && c <= '9') + v = c - '0'; + else if (c >= 'a' && c <= 'f') + v = c - 'a' + 10; + else if (c >= 'A' && c <= 'F') + v = c - 'A' + 10; + else + break; + x = x * 16 + v; + } + *str = s; + return x; +} + +static bool IsOneOf(char c, char c1, char c2) { + return c == c1 || c == c2; +} + +static bool IsDecimal(char c) { + return c >= '0' && c <= '9'; +} + +static bool IsHex(char c) { + return (c >= '0' && c <= '9') + || (c >= 'a' && c <= 'f'); +} + +static uptr ReadHex(const char *p) { + uptr v = 0; + for (; IsHex(p[0]); p++) { + if (p[0] >= '0' && p[0] <= '9') + v = v * 16 + p[0] - '0'; + else + v = v * 16 + p[0] - 'a' + 10; + } + return v; +} + +static uptr ReadDecimal(const char *p) { + uptr v = 0; + for (; IsDecimal(p[0]); p++) + v = v * 10 + p[0] - '0'; + return v; +} + +bool MemoryMappingLayout::Next(uptr *start, uptr *end, uptr *offset, + char filename[], uptr filename_size, + uptr *protection) { + char *last = proc_self_maps_.data + proc_self_maps_.len; + if (current_ >= last) return false; + uptr dummy; + if (!start) start = &dummy; + if (!end) end = &dummy; + if (!offset) offset = &dummy; + char *next_line = (char*)internal_memchr(current_, '\n', last - current_); + if (next_line == 0) + next_line = last; + // Example: 08048000-08056000 r-xp 00000000 03:0c 64593 /foo/bar + *start = ParseHex(¤t_); + CHECK_EQ(*current_++, '-'); + *end = ParseHex(¤t_); + CHECK_EQ(*current_++, ' '); + uptr local_protection = 0; + CHECK(IsOneOf(*current_, '-', 'r')); + if (*current_++ == 'r') + local_protection |= kProtectionRead; + CHECK(IsOneOf(*current_, '-', 'w')); + if (*current_++ == 'w') + local_protection |= kProtectionWrite; + CHECK(IsOneOf(*current_, '-', 'x')); + if (*current_++ == 'x') + local_protection |= kProtectionExecute; + CHECK(IsOneOf(*current_, 's', 'p')); + if (*current_++ == 's') + local_protection |= kProtectionShared; + if (protection) { + *protection = local_protection; + } + CHECK_EQ(*current_++, ' '); + *offset = ParseHex(¤t_); + CHECK_EQ(*current_++, ' '); + ParseHex(¤t_); + CHECK_EQ(*current_++, ':'); + ParseHex(¤t_); + CHECK_EQ(*current_++, ' '); + while (IsDecimal(*current_)) + current_++; + // Qemu may lack the trailing space. + // http://code.google.com/p/address-sanitizer/issues/detail?id=160 + // CHECK_EQ(*current_++, ' '); + // Skip spaces. + while (current_ < next_line && *current_ == ' ') + current_++; + // Fill in the filename. + uptr i = 0; + while (current_ < next_line) { + if (filename && i < filename_size - 1) + filename[i++] = *current_; + current_++; + } + if (filename && i < filename_size) + filename[i] = 0; + current_ = next_line + 1; + return true; +} + +uptr MemoryMappingLayout::DumpListOfModules(LoadedModule *modules, + uptr max_modules, + string_predicate_t filter) { + Reset(); + uptr cur_beg, cur_end, cur_offset; + InternalScopedBuffer<char> module_name(kMaxPathLength); + uptr n_modules = 0; + for (uptr i = 0; n_modules < max_modules && + Next(&cur_beg, &cur_end, &cur_offset, module_name.data(), + module_name.size(), 0); + i++) { + const char *cur_name = module_name.data(); + if (cur_name[0] == '\0') + continue; + if (filter && !filter(cur_name)) + continue; + void *mem = &modules[n_modules]; + // 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 = new(mem) LoadedModule(cur_name, base_address); + cur_module->addAddressRange(cur_beg, cur_end); + n_modules++; + } + return n_modules; +} + +void GetMemoryProfile(fill_profile_f cb, uptr *stats, uptr stats_size) { + char *smaps = 0; + uptr smaps_cap = 0; + uptr smaps_len = ReadFileToBuffer("/proc/self/smaps", + &smaps, &smaps_cap, 64<<20); + uptr start = 0; + bool file = false; + const char *pos = smaps; + while (pos < smaps + smaps_len) { + if (IsHex(pos[0])) { + start = ReadHex(pos); + for (; *pos != '/' && *pos > '\n'; pos++) {} + file = *pos == '/'; + } else if (internal_strncmp(pos, "Rss:", 4) == 0) { + for (; *pos < '0' || *pos > '9'; pos++) {} + uptr rss = ReadDecimal(pos) * 1024; + cb(start, rss, file, stats, stats_size); + } + while (*pos++ != '\n') {} + } + UnmapOrDie(smaps, smaps_cap); +} + +} // namespace __sanitizer + +#endif // SANITIZER_LINUX +//===-- 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_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" + +#include <asm/param.h> +#include <dlfcn.h> +#include <errno.h> +#include <fcntl.h> +#if !SANITIZER_ANDROID +#include <link.h> +#endif +#include <pthread.h> +#include <sched.h> +#include <sys/mman.h> +#include <sys/ptrace.h> +#include <sys/resource.h> +#include <sys/stat.h> +#include <sys/syscall.h> +#include <sys/time.h> +#include <sys/types.h> +#include <unistd.h> +#include <unwind.h> + +#if !SANITIZER_ANDROID +#include <sys/signal.h> +#endif + +#if SANITIZER_ANDROID +#include <android/log.h> +#include <sys/system_properties.h> +#endif + +// <linux/time.h> +struct kernel_timeval { + long tv_sec; + long tv_usec; +}; + +// <linux/futex.h> is broken on some linux distributions. +const int FUTEX_WAIT = 0; +const int FUTEX_WAKE = 1; + +// Are we using 32-bit or 64-bit syscalls? +// x32 (which defines __x86_64__) has SANITIZER_WORDSIZE == 32 +// but it still needs to use 64-bit syscalls. +#if defined(__x86_64__) || SANITIZER_WORDSIZE == 64 +# define SANITIZER_LINUX_USES_64BIT_SYSCALLS 1 +#else +# define SANITIZER_LINUX_USES_64BIT_SYSCALLS 0 +#endif + +namespace __sanitizer { + +#ifdef __x86_64__ +#include "sanitizer_syscall_linux_x86_64.inc" +#else +#include "sanitizer_syscall_generic.inc" +#endif + +// --------------- sanitizer_libc.h +uptr internal_mmap(void *addr, uptr length, int prot, int flags, + int fd, u64 offset) { +#if SANITIZER_LINUX_USES_64BIT_SYSCALLS + return internal_syscall(__NR_mmap, (uptr)addr, length, prot, flags, fd, + offset); +#else + return internal_syscall(__NR_mmap2, addr, length, prot, flags, fd, offset); +#endif +} + +uptr internal_munmap(void *addr, uptr length) { + return internal_syscall(__NR_munmap, (uptr)addr, length); +} + +uptr internal_close(fd_t fd) { + return internal_syscall(__NR_close, fd); +} + +uptr internal_open(const char *filename, int flags) { +#if SANITIZER_USES_CANONICAL_LINUX_SYSCALLS + return internal_syscall(__NR_openat, AT_FDCWD, (uptr)filename, flags); +#else + return internal_syscall(__NR_open, (uptr)filename, flags); +#endif +} + +uptr internal_open(const char *filename, int flags, u32 mode) { +#if SANITIZER_USES_CANONICAL_LINUX_SYSCALLS + return internal_syscall(__NR_openat, AT_FDCWD, (uptr)filename, flags, mode); +#else + return internal_syscall(__NR_open, (uptr)filename, flags, mode); +#endif +} + +uptr OpenFile(const char *filename, bool write) { + return internal_open(filename, + write ? O_WRONLY | O_CREAT /*| O_CLOEXEC*/ : O_RDONLY, 0660); +} + +uptr internal_read(fd_t fd, void *buf, uptr count) { + sptr res; + HANDLE_EINTR(res, (sptr)internal_syscall(__NR_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(__NR_write, fd, (uptr)buf, count)); + return res; +} + +#if !SANITIZER_LINUX_USES_64BIT_SYSCALLS +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 + +uptr internal_stat(const char *path, void *buf) { +#if SANITIZER_USES_CANONICAL_LINUX_SYSCALLS + return internal_syscall(__NR_newfstatat, AT_FDCWD, (uptr)path, (uptr)buf, 0); +#elif SANITIZER_LINUX_USES_64BIT_SYSCALLS + return internal_syscall(__NR_stat, (uptr)path, (uptr)buf); +#else + struct stat64 buf64; + int res = internal_syscall(__NR_stat64, path, &buf64); + stat64_to_stat(&buf64, (struct stat *)buf); + return res; +#endif +} + +uptr internal_lstat(const char *path, void *buf) { +#if SANITIZER_USES_CANONICAL_LINUX_SYSCALLS + return internal_syscall(__NR_newfstatat, AT_FDCWD, (uptr)path, + (uptr)buf, AT_SYMLINK_NOFOLLOW); +#elif SANITIZER_LINUX_USES_64BIT_SYSCALLS + return internal_syscall(__NR_lstat, (uptr)path, (uptr)buf); +#else + struct stat64 buf64; + int res = internal_syscall(__NR_lstat64, path, &buf64); + stat64_to_stat(&buf64, (struct stat *)buf); + return res; +#endif +} + +uptr internal_fstat(fd_t fd, void *buf) { +#if SANITIZER_LINUX_USES_64BIT_SYSCALLS + return internal_syscall(__NR_fstat, fd, (uptr)buf); +#else + struct stat64 buf64; + int res = internal_syscall(__NR_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(__NR_dup3, oldfd, newfd, 0); +#else + return internal_syscall(__NR_dup2, oldfd, newfd); +#endif +} + +uptr internal_readlink(const char *path, char *buf, uptr bufsize) { +#if SANITIZER_USES_CANONICAL_LINUX_SYSCALLS + return internal_syscall(__NR_readlinkat, AT_FDCWD, + (uptr)path, (uptr)buf, bufsize); +#else + return internal_syscall(__NR_readlink, (uptr)path, (uptr)buf, bufsize); +#endif +} + +uptr internal_unlink(const char *path) { +#if SANITIZER_USES_CANONICAL_LINUX_SYSCALLS + return internal_syscall(__NR_unlinkat, AT_FDCWD, (uptr)path, 0); +#else + return internal_syscall(__NR_unlink, (uptr)path); +#endif +} + +uptr internal_sched_yield() { + return internal_syscall(__NR_sched_yield); +} + +void internal__exit(int exitcode) { + internal_syscall(__NR_exit_group, exitcode); + Die(); // Unreachable. +} + +uptr internal_execve(const char *filename, char *const argv[], + char *const envp[]) { + return internal_syscall(__NR_execve, (uptr)filename, (uptr)argv, (uptr)envp); +} + +// ----------------- sanitizer_common.h +bool FileExists(const char *filename) { +#if SANITIZER_USES_CANONICAL_LINUX_SYSCALLS + struct stat st; + if (internal_syscall(__NR_newfstatat, AT_FDCWD, filename, &st, 0)) + return false; +#else + struct stat st; + if (internal_stat(filename, &st)) + return false; + // Sanity check: filename is a regular file. + return S_ISREG(st.st_mode); +#endif +} + +uptr GetTid() { + return internal_syscall(__NR_gettid); +} + +u64 NanoTime() { + kernel_timeval tv; + internal_memset(&tv, 0, sizeof(tv)); + internal_syscall(__NR_gettimeofday, (uptr)&tv, 0); + return (u64)tv.tv_sec * 1000*1000*1000 + tv.tv_usec * 1000; +} + +// Like getenv, but reads env directly from /proc and does not use libc. +// This function should be called first inside __asan_init. +const char *GetEnv(const char *name) { + static char *environ; + static uptr len; + static bool inited; + if (!inited) { + inited = true; + uptr environ_size; + len = ReadFileToBuffer("/proc/self/environ", + &environ, &environ_size, 1 << 26); + } + if (!environ || len == 0) return 0; + 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 == 0) // this entry isn't NUL terminated + return 0; + else if (!internal_memcmp(p, name, namelen) && p[namelen] == '=') // Match. + return p + namelen + 1; // point after = + p = endp + 1; + } + return 0; // Not found. +} + +extern "C" { + SANITIZER_WEAK_ATTRIBUTE extern void *__libc_stack_end; +} + +#if !SANITIZER_GO +static void ReadNullSepFileToArray(const char *path, char ***arr, + int arr_size) { + char *buff; + uptr buff_size = 0; + *arr = (char **)MmapOrDie(arr_size * sizeof(char *), "NullSepFileArray"); + ReadFileToBuffer(path, &buff, &buff_size, 1024 * 1024); + (*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] = 0; +} +#endif + +static void GetArgsAndEnv(char*** argv, char*** envp) { +#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 +} + +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(); +} + +void PrepareForSandboxing() { + // Some kinds of sandboxes may forbid filesystem access, so we won't be able + // to read the file mappings from /proc/self/maps. Luckily, neither the + // process will be able to load additional libraries, so it's fine to use the + // cached mappings. + MemoryMappingLayout::CacheMemoryMappings(); + // Same for /proc/self/exe in the symbolizer. +#if !SANITIZER_GO + if (Symbolizer *sym = Symbolizer::GetOrNull()) + sym->PrepareForSandboxing(); +#endif +} + +enum MutexState { + MtxUnlocked = 0, + MtxLocked = 1, + MtxSleeping = 2 +}; + +BlockingMutex::BlockingMutex(LinkerInitialized) { + CHECK_EQ(owner_, 0); +} + +BlockingMutex::BlockingMutex() { + internal_memset(this, 0, sizeof(*this)); +} + +void BlockingMutex::Lock() { + atomic_uint32_t *m = reinterpret_cast<atomic_uint32_t *>(&opaque_storage_); + if (atomic_exchange(m, MtxLocked, memory_order_acquire) == MtxUnlocked) + return; + while (atomic_exchange(m, MtxSleeping, memory_order_acquire) != MtxUnlocked) + internal_syscall(__NR_futex, (uptr)m, FUTEX_WAIT, MtxSleeping, 0, 0, 0); +} + +void BlockingMutex::Unlock() { + atomic_uint32_t *m = reinterpret_cast<atomic_uint32_t *>(&opaque_storage_); + u32 v = atomic_exchange(m, MtxUnlocked, memory_order_relaxed); + CHECK_NE(v, MtxUnlocked); + if (v == MtxSleeping) + internal_syscall(__NR_futex, (uptr)m, FUTEX_WAKE, 1, 0, 0, 0); +} + +void BlockingMutex::CheckLocked() { + atomic_uint32_t *m = reinterpret_cast<atomic_uint32_t *>(&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 { + unsigned long d_ino; + unsigned long d_off; + unsigned short d_reclen; + char d_name[256]; +}; + +// Syscall wrappers. +uptr internal_ptrace(int request, int pid, void *addr, void *data) { + return internal_syscall(__NR_ptrace, request, pid, (uptr)addr, (uptr)data); +} + +uptr internal_waitpid(int pid, int *status, int options) { + return internal_syscall(__NR_wait4, pid, (uptr)status, options, + 0 /* rusage */); +} + +uptr internal_getpid() { + return internal_syscall(__NR_getpid); +} + +uptr internal_getppid() { + return internal_syscall(__NR_getppid); +} + +uptr internal_getdents(fd_t fd, struct linux_dirent *dirp, unsigned int count) { +#if SANITIZER_USES_CANONICAL_LINUX_SYSCALLS + return internal_syscall(__NR_getdents64, fd, (uptr)dirp, count); +#else + return internal_syscall(__NR_getdents, fd, (uptr)dirp, count); +#endif +} + +uptr internal_lseek(fd_t fd, OFF_T offset, int whence) { + return internal_syscall(__NR_lseek, fd, offset, whence); +} + +uptr internal_prctl(int option, uptr arg2, uptr arg3, uptr arg4, uptr arg5) { + return internal_syscall(__NR_prctl, option, arg2, arg3, arg4, arg5); +} + +uptr internal_sigaltstack(const struct sigaltstack *ss, + struct sigaltstack *oss) { + return internal_syscall(__NR_sigaltstack, (uptr)ss, (uptr)oss); +} + +// Doesn't set sa_restorer, 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 = (__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)); + k_act.sa_flags = u_act->sa_flags; + // 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). + k_act.sa_restorer = u_act->sa_restorer; + } + + uptr result = internal_syscall(__NR_rt_sigaction, (uptr)signum, + (uptr)(u_act ? &k_act : NULL), + (uptr)(u_oldact ? &k_oldact : NULL), + (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; + u_oldact->sa_restorer = k_oldact.sa_restorer; + } + return result; +} + +uptr internal_sigprocmask(int how, __sanitizer_sigset_t *set, + __sanitizer_sigset_t *oldset) { + __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(__NR_rt_sigprocmask, (uptr)how, &k_set->sig[0], + &k_oldset->sig[0], sizeof(__sanitizer_kernel_sigset_t)); +} + +void internal_sigfillset(__sanitizer_sigset_t *set) { + internal_memset(set, 0xff, sizeof(*set)); +} + +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); +} + +// 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() { +#if defined(__x86_64__) || defined(__i386__) + return EXEC_PAGESIZE; +#else + return sysconf(_SC_PAGESIZE); // EXEC_PAGESIZE may not be trustworthy. +#endif +} + +static char proc_self_exe_cache_str[kMaxPathLength]; +static uptr proc_self_exe_cache_len = 0; + +uptr ReadBinaryName(/*out*/char *buf, uptr buf_len) { + uptr module_name_len = internal_readlink( + "/proc/self/exe", buf, buf_len); + int readlink_error; + if (internal_iserror(module_name_len, &readlink_error)) { + if (proc_self_exe_cache_len) { + // If available, use the cached module name. + CHECK_LE(proc_self_exe_cache_len, buf_len); + internal_strncpy(buf, proc_self_exe_cache_str, buf_len); + module_name_len = internal_strlen(proc_self_exe_cache_str); + } else { + // We can't read /proc/self/exe for some reason, assume the name of the + // binary is unknown. + Report("WARNING: readlink(\"/proc/self/exe\") failed with errno %d, " + "some stack frames may not be symbolized\n", readlink_error); + module_name_len = internal_snprintf(buf, buf_len, "/proc/self/exe"); + } + CHECK_LT(module_name_len, buf_len); + buf[module_name_len] = '\0'; + } + return module_name_len; +} + +void CacheBinaryName() { + if (!proc_self_exe_cache_len) { + proc_self_exe_cache_len = + ReadBinaryName(proc_self_exe_cache_str, kMaxPathLength); + } +} + +// 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)) { + typedef ElfW(Phdr) Elf_Phdr; + typedef ElfW(Ehdr) Elf_Ehdr; + 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__) +// 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 = __NR_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"(__NR_clone), "i"(__NR_exit), + "S"(child_stack), + "D"(flags), + "d"(parent_tidptr), + "r"(r8), + "r"(r10) + : "rsp", "memory", "r11", "rcx"); + return res; +} +#endif // defined(__x86_64__) + +#if SANITIZER_ANDROID +// This thing is not, strictly speaking, async signal safe, but it does not seem +// to cause any issues. Alternative is writing to log devices directly, but +// their location and message format might change in the future, so we'd really +// like to avoid that. +void AndroidLogWrite(const char *buffer) { + char *copy = internal_strdup(buffer); + char *p = copy; + char *q; + // __android_log_write has an implicit message length limit. + // Print one line at a time. + do { + q = internal_strchr(p, '\n'); + if (q) *q = '\0'; + __android_log_write(ANDROID_LOG_INFO, NULL, p); + if (q) p = q + 1; + } while (q); + InternalFree(copy); +} + +void GetExtraActivationFlags(char *buf, uptr size) { + CHECK(size > PROP_VALUE_MAX); + __system_property_get("asan.options", buf); +} +#endif + +bool IsDeadlySignal(int signum) { + return (signum == SIGSEGV) && common_flags()->handle_segv; +} + +} // namespace __sanitizer + +#endif // SANITIZER_LINUX +//===-- 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_LINUX + +#include "sanitizer_common.h" +#include "sanitizer_flags.h" +#include "sanitizer_linux.h" +#include "sanitizer_placement_new.h" +#include "sanitizer_procmaps.h" +#include "sanitizer_stacktrace.h" +#include "sanitizer_atomic.h" + +#include <dlfcn.h> +#include <pthread.h> +#include <signal.h> +#include <sys/prctl.h> +#include <sys/resource.h> +#include <unwind.h> + +#if !SANITIZER_ANDROID +#include <elf.h> +#include <link.h> +#include <unistd.h> +#endif + +namespace __sanitizer { + +#ifndef SANITIZER_GO +// This function is defined elsewhere if we intercepted pthread_attr_getstack. +SANITIZER_WEAK_ATTRIBUTE int +real_pthread_attr_getstack(void *attr, void **addr, size_t *size); + +static int my_pthread_attr_getstack(void *attr, void **addr, size_t *size) { + if (real_pthread_attr_getstack) + return real_pthread_attr_getstack((pthread_attr_t *)attr, addr, size); + return pthread_attr_getstack((pthread_attr_t *)attr, addr, size); +} + +SANITIZER_WEAK_ATTRIBUTE int +real_sigaction(int signum, const void *act, void *oldact); + +int internal_sigaction(int signum, const void *act, void *oldact) { + if (real_sigaction) + return real_sigaction(signum, act, oldact); + return sigaction(signum, (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, 0, 0, /* protection */0)) { + 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; + CHECK_EQ(pthread_getattr_np(pthread_self(), &attr), 0); + uptr stacksize = 0; + void *stackaddr = 0; + my_pthread_attr_getstack(&attr, &stackaddr, (size_t*)&stacksize); + pthread_attr_destroy(&attr); + + CHECK_LE(stacksize, kMaxThreadStackSize); // Sanity check. + *stack_top = (uptr)stackaddr + stacksize; + *stack_bottom = (uptr)stackaddr; +} +#endif // #ifndef SANITIZER_GO + +// Does not compile for Go because dlsym() requires -ldl +#ifndef SANITIZER_GO +bool SetEnv(const char *name, const char *value) { + void *f = dlsym(RTLD_NEXT, "setenv"); + if (f == 0) + 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 IndirectExternCall(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 +} + +#ifndef SANITIZER_GO +//------------------------- SlowUnwindStack ----------------------------------- + +typedef struct { + uptr absolute_pc; + uptr stack_top; + uptr stack_size; +} backtrace_frame_t; + +extern "C" { +typedef void *(*acquire_my_map_info_list_func)(); +typedef void (*release_my_map_info_list_func)(void *map); +typedef sptr (*unwind_backtrace_signal_arch_func)( + void *siginfo, void *sigcontext, void *map_info_list, + backtrace_frame_t *backtrace, uptr ignore_depth, uptr max_depth); +acquire_my_map_info_list_func acquire_my_map_info_list; +release_my_map_info_list_func release_my_map_info_list; +unwind_backtrace_signal_arch_func unwind_backtrace_signal_arch; +} // extern "C" + +#if SANITIZER_ANDROID +void SanitizerInitializeUnwinder() { + void *p = dlopen("libcorkscrew.so", RTLD_LAZY); + if (!p) { + VReport(1, + "Failed to open libcorkscrew.so. You may see broken stack traces " + "in SEGV reports."); + return; + } + acquire_my_map_info_list = + (acquire_my_map_info_list_func)(uptr)dlsym(p, "acquire_my_map_info_list"); + release_my_map_info_list = + (release_my_map_info_list_func)(uptr)dlsym(p, "release_my_map_info_list"); + unwind_backtrace_signal_arch = (unwind_backtrace_signal_arch_func)(uptr)dlsym( + p, "unwind_backtrace_signal_arch"); + if (!acquire_my_map_info_list || !release_my_map_info_list || + !unwind_backtrace_signal_arch) { + VReport(1, + "Failed to find one of the required symbols in libcorkscrew.so. " + "You may see broken stack traces in SEGV reports."); + acquire_my_map_info_list = NULL; + unwind_backtrace_signal_arch = NULL; + release_my_map_info_list = NULL; + } +} +#endif + +#ifdef __arm__ +#define UNWIND_STOP _URC_END_OF_STACK +#define UNWIND_CONTINUE _URC_NO_REASON +#else +#define UNWIND_STOP _URC_NORMAL_STOP +#define UNWIND_CONTINUE _URC_NO_REASON +#endif + +uptr Unwind_GetIP(struct _Unwind_Context *ctx) { +#ifdef __arm__ + uptr val; + _Unwind_VRS_Result res = _Unwind_VRS_Get(ctx, _UVRSC_CORE, + 15 /* r15 = PC */, _UVRSD_UINT32, &val); + CHECK(res == _UVRSR_OK && "_Unwind_VRS_Get failed"); + // Clear the Thumb bit. + return val & ~(uptr)1; +#else + return _Unwind_GetIP(ctx); +#endif +} + +struct UnwindTraceArg { + StackTrace *stack; + uptr max_depth; +}; + +_Unwind_Reason_Code Unwind_Trace(struct _Unwind_Context *ctx, void *param) { + UnwindTraceArg *arg = (UnwindTraceArg*)param; + CHECK_LT(arg->stack->size, arg->max_depth); + uptr pc = Unwind_GetIP(ctx); + arg->stack->trace[arg->stack->size++] = pc; + if (arg->stack->size == arg->max_depth) return UNWIND_STOP; + return UNWIND_CONTINUE; +} + +void StackTrace::SlowUnwindStack(uptr pc, uptr max_depth) { + size = 0; + if (max_depth == 0) + return; + UnwindTraceArg arg = {this, Min(max_depth + 1, kStackTraceMax)}; + _Unwind_Backtrace(Unwind_Trace, &arg); + // We need to pop a few frames so that pc is on top. + uptr to_pop = LocatePcInTrace(pc); + // trace[0] belongs to the current function so we always pop it. + if (to_pop == 0) + to_pop = 1; + PopStackFrames(to_pop); + trace[0] = pc; +} + +void StackTrace::SlowUnwindStackWithContext(uptr pc, void *context, + uptr max_depth) { + if (!unwind_backtrace_signal_arch) { + SlowUnwindStack(pc, max_depth); + return; + } + + size = 0; + if (max_depth == 0) return; + + void *map = acquire_my_map_info_list(); + CHECK(map); + InternalScopedBuffer<backtrace_frame_t> frames(kStackTraceMax); + // siginfo argument appears to be unused. + sptr res = unwind_backtrace_signal_arch(/* siginfo */ NULL, context, map, + frames.data(), + /* ignore_depth */ 0, max_depth); + release_my_map_info_list(map); + if (res < 0) return; + CHECK((uptr)res <= kStackTraceMax); + + // +2 compensate for libcorkscrew unwinder returning addresses of call + // instructions instead of raw return addresses. + for (sptr i = 0; i < res; ++i) + trace[size++] = frames[i].absolute_pc + 2; +} + +#endif // !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() { +#if !defined(SANITIZER_GO) && !SANITIZER_ANDROID + 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; + IndirectExternCall(get_tls)(&tls_size, &tls_align); + g_tls_size = tls_size; +#endif +} + +uptr GetTlsSize() { + return g_tls_size; +} + +#if defined(__x86_64__) || defined(__i386__) +// sizeof(struct thread) from glibc. +static atomic_uintptr_t kThreadDescriptorSize; + +uptr ThreadDescriptorSize() { + char buf[64]; + uptr val = atomic_load(&kThreadDescriptorSize, memory_order_relaxed); + if (val) + return val; +#ifdef _CS_GNU_LIBC_VERSION + 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 == '.')) { + /* sizeof(struct thread) values from various glibc versions. */ + 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 <= 12) + val = FIRST_32_SECOND_64(1168, 2288); + else + val = FIRST_32_SECOND_64(1216, 2304); + } + if (val) + atomic_store(&kThreadDescriptorSize, val, memory_order_relaxed); + return val; + } +#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; +} + +uptr ThreadSelf() { + uptr descr_addr; +#ifdef __i386__ + asm("mov %%gs:%c1,%0" : "=r"(descr_addr) : "i"(kThreadSelfOffset)); +#else + asm("mov %%fs:%c1,%0" : "=r"(descr_addr) : "i"(kThreadSelfOffset)); +#endif + return descr_addr; +} +#endif // defined(__x86_64__) || defined(__i386__) + +void GetThreadStackAndTls(bool main, uptr *stk_addr, uptr *stk_size, + uptr *tls_addr, uptr *tls_size) { +#ifndef SANITIZER_GO +#if defined(__x86_64__) || defined(__i386__) + *tls_addr = ThreadSelf(); + *tls_size = GetTlsSize(); + *tls_addr -= *tls_size; + *tls_addr += ThreadDescriptorSize(); +#else + *tls_addr = 0; + *tls_size = 0; +#endif + + 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; + } + } +#else // SANITIZER_GO + *stk_addr = 0; + *stk_size = 0; + *tls_addr = 0; + *tls_size = 0; +#endif // SANITIZER_GO +} + +#ifndef SANITIZER_GO +void AdjustStackSize(void *attr_) { + pthread_attr_t *attr = (pthread_attr_t *)attr_; + uptr stackaddr = 0; + size_t stacksize = 0; + my_pthread_attr_getstack(attr, (void**)&stackaddr, &stacksize); + // GLibC will return (0 - stacksize) as the stack address in the case when + // stacksize is set, but stackaddr is not. + bool stack_set = (stackaddr != 0) && (stackaddr + stacksize != 0); + // We place a lot of tool data into TLS, account for that. + const uptr minstacksize = GetTlsSize() + 128*1024; + if (stacksize < minstacksize) { + if (!stack_set) { + if (stacksize != 0) + VPrintf(1, "Sanitizer: increasing stacksize %zu->%zu\n", stacksize, + minstacksize); + pthread_attr_setstacksize(attr, minstacksize); + } else { + Printf("Sanitizer: pre-allocated stack size is insufficient: " + "%zu < %zu\n", stacksize, minstacksize); + Printf("Sanitizer: pthread_create is likely to fail.\n"); + } + } +} +#endif // SANITIZER_GO + +#if SANITIZER_ANDROID +uptr GetListOfModules(LoadedModule *modules, uptr max_modules, + string_predicate_t filter) { + MemoryMappingLayout memory_mapping(false); + return memory_mapping.DumpListOfModules(modules, max_modules, filter); +} +#else // SANITIZER_ANDROID +typedef ElfW(Phdr) Elf_Phdr; + +struct DlIteratePhdrData { + LoadedModule *modules; + uptr current_n; + bool first; + uptr max_n; + string_predicate_t filter; +}; + +static int dl_iterate_phdr_cb(dl_phdr_info *info, size_t size, void *arg) { + DlIteratePhdrData *data = (DlIteratePhdrData*)arg; + if (data->current_n == data->max_n) + return 0; + InternalScopedBuffer<char> module_name(kMaxPathLength); + module_name.data()[0] = '\0'; + if (data->first) { + data->first = false; + // First module is the binary itself. + ReadBinaryName(module_name.data(), module_name.size()); + } else if (info->dlpi_name) { + internal_strncpy(module_name.data(), info->dlpi_name, module_name.size()); + } + if (module_name.data()[0] == '\0') + return 0; + if (data->filter && !data->filter(module_name.data())) + return 0; + void *mem = &data->modules[data->current_n]; + LoadedModule *cur_module = new(mem) LoadedModule(module_name.data(), + info->dlpi_addr); + data->current_n++; + 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; + cur_module->addAddressRange(cur_beg, cur_end); + } + } + return 0; +} + +uptr GetListOfModules(LoadedModule *modules, uptr max_modules, + string_predicate_t filter) { + CHECK(modules); + DlIteratePhdrData data = {modules, 0, true, max_modules, filter}; + dl_iterate_phdr(dl_iterate_phdr_cb, &data); + return data.current_n; +} +#endif // SANITIZER_ANDROID + +#ifndef SANITIZER_GO +uptr indirect_call_wrapper; + +void SetIndirectCallWrapper(uptr wrapper) { + CHECK(!indirect_call_wrapper); + CHECK(wrapper); + indirect_call_wrapper = wrapper; +} +#endif + +} // namespace __sanitizer + +#endif // SANITIZER_LINUX +//===-- 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__) + +#include "sanitizer_stoptheworld.h" + +#include "sanitizer_platform_limits_posix.h" + +#include <errno.h> +#include <sched.h> // for CLONE_* definitions +#include <stddef.h> +#include <sys/prctl.h> // for PR_* definitions +#include <sys/ptrace.h> // for PTRACE_* definitions +#include <sys/types.h> // for pid_t +#if SANITIZER_ANDROID && defined(__arm__) +# include <linux/user.h> // for pt_regs +#else +# include <sys/user.h> // for user_regs_struct +#endif +#include <sys/wait.h> // 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. + +COMPILER_CHECK(sizeof(SuspendedThreadID) == sizeof(pid_t)); + +namespace __sanitizer { +// This class handles thread suspending/unsuspending in the tracer thread. +class ThreadSuspender { + public: + explicit ThreadSuspender(pid_t pid) + : pid_(pid) { + CHECK_GE(pid, 0); + } + bool SuspendAllThreads(); + void ResumeAllThreads(); + void KillAllThreads(); + SuspendedThreadsList &suspended_threads_list() { + return suspended_threads_list_; + } + private: + SuspendedThreadsList suspended_threads_list_; + pid_t pid_; + bool SuspendThread(SuspendedThreadID thread_id); +}; + +bool ThreadSuspender::SuspendThread(SuspendedThreadID thread_id) { + // 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(thread_id)) + return false; + int pterrno; + if (internal_iserror(internal_ptrace(PTRACE_ATTACH, thread_id, NULL, NULL), + &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", thread_id, + pterrno); + return false; + } else { + VReport(1, "Attached to thread %d.\n", thread_id); + // The thread is not guaranteed to stop before ptrace returns, so we must + // wait on it. + uptr waitpid_status; + HANDLE_EINTR(waitpid_status, internal_waitpid(thread_id, NULL, __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", + thread_id, wperrno); + internal_ptrace(PTRACE_DETACH, thread_id, NULL, NULL); + return false; + } + suspended_threads_list_.Append(thread_id); + return true; + } +} + +void ThreadSuspender::ResumeAllThreads() { + for (uptr i = 0; i < suspended_threads_list_.thread_count(); i++) { + pid_t tid = suspended_threads_list_.GetThreadID(i); + int pterrno; + if (!internal_iserror(internal_ptrace(PTRACE_DETACH, tid, NULL, NULL), + &pterrno)) { + VReport(1, "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++) + internal_ptrace(PTRACE_KILL, suspended_threads_list_.GetThreadID(i), + NULL, NULL); +} + +bool ThreadSuspender::SuspendAllThreads() { + ThreadLister thread_lister(pid_); + bool added_threads; + 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()) { + // Detach threads and fail. + ResumeAllThreads(); + return false; + } + thread_lister.Reset(); + } while (added_threads); + return true; +} + +// Pointer to the ThreadSuspender instance for use in signal handler. +static ThreadSuspender *thread_suspender_instance = NULL; + +// Signals that should not be blocked (this is used in the parent thread as well +// as the tracer thread). +static const int kUnblockedSignals[] = { SIGABRT, SIGILL, SIGFPE, SIGSEGV, + SIGBUS, SIGXCPU, SIGXFSZ }; + +// 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; + uptr parent_pid; +}; + +static DieCallbackType old_die_callback; + +// Signal handler to wake up suspended threads when the tracer thread dies. +void TracerThreadSignalHandler(int signum, void *siginfo, void *) { + if (thread_suspender_instance != NULL) { + if (signum == SIGABRT) + thread_suspender_instance->KillAllThreads(); + else + thread_suspender_instance->ResumeAllThreads(); + } + internal__exit((signum == SIGABRT) ? 1 : 2); +} + +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... + if (thread_suspender_instance) + thread_suspender_instance->KillAllThreads(); + if (old_die_callback) + old_die_callback(); +} + +// 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(); + + SetDieCallback(TracerThreadDieCallback); + + ThreadSuspender thread_suspender(internal_getppid()); + // Global pointer for the signal handler. + thread_suspender_instance = &thread_suspender; + + // Alternate stack for signal handling. + InternalScopedBuffer<char> 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, NULL); + + // Install our handler for fatal signals. Other signals should be blocked by + // the mask we inherited from the caller thread. + for (uptr signal_index = 0; signal_index < ARRAY_SIZE(kUnblockedSignals); + signal_index++) { + __sanitizer_sigaction new_sigaction; + internal_memset(&new_sigaction, 0, sizeof(new_sigaction)); + new_sigaction.sigaction = TracerThreadSignalHandler; + new_sigaction.sa_flags = SA_ONSTACK | SA_SIGINFO; + internal_sigfillset(&new_sigaction.sa_mask); + internal_sigaction_norestorer(kUnblockedSignals[signal_index], + &new_sigaction, NULL); + } + + 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; + } + thread_suspender_instance = NULL; + handler_stack.ss_flags = SS_DISABLE; + internal_sigaltstack(&handler_stack, NULL); + 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_EQ(guard_start_, (uptr)Mprotect((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; +static __sanitizer_sigaction old_sigactions + [ARRAY_SIZE(kUnblockedSignals)]; + +class StopTheWorldScope { + public: + StopTheWorldScope() { + // Block all signals that can be blocked safely, and install + // default handlers for the remaining signals. + // We cannot allow user-defined handlers to run while the ThreadSuspender + // thread is active, because they could conceivably call some libc functions + // which modify errno (which is shared between the two threads). + internal_sigfillset(&blocked_sigset); + for (uptr signal_index = 0; signal_index < ARRAY_SIZE(kUnblockedSignals); + signal_index++) { + // Remove the signal from the set of blocked signals. + internal_sigdelset(&blocked_sigset, kUnblockedSignals[signal_index]); + // Install the default handler. + __sanitizer_sigaction new_sigaction; + internal_memset(&new_sigaction, 0, sizeof(new_sigaction)); + new_sigaction.handler = SIG_DFL; + internal_sigfillset(&new_sigaction.sa_mask); + internal_sigaction_norestorer(kUnblockedSignals[signal_index], + &new_sigaction, &old_sigactions[signal_index]); + } + int sigprocmask_status = + internal_sigprocmask(SIG_BLOCK, &blocked_sigset, &old_sigset); + CHECK_EQ(sigprocmask_status, 0); // sigprocmask should never fail + // 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); + old_die_callback = GetDieCallback(); + } + + ~StopTheWorldScope() { + SetDieCallback(old_die_callback); + // Restore the dumpable flag. + if (!process_was_dumpable_) + internal_prctl(PR_SET_DUMPABLE, 0, 0, 0, 0); + // Restore the signal handlers. + for (uptr signal_index = 0; signal_index < ARRAY_SIZE(kUnblockedSignals); + signal_index++) { + internal_sigaction_norestorer(kUnblockedSignals[signal_index], + &old_sigactions[signal_index], NULL); + } + internal_sigprocmask(SIG_SETMASK, &old_sigset, &old_sigset); + } + + 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(); + 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(); + uptr tracer_pid = internal_clone( + TracerThread, tracer_stack.Bottom(), + CLONE_VM | CLONE_FS | CLONE_FILES | CLONE_UNTRACED, + &tracer_thread_argument, 0 /* parent_tidptr */, 0 /* newtls */, 0 + /* child_tidptr */); + 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(); + // Since errno is shared between this thread and the tracer thread, we + // must avoid using errno while the tracer thread is running. + // At this point, any signal will either be blocked or kill us, so waitpid + // should never return (and set errno) while the tracer thread is alive. + uptr waitpid_status = internal_waitpid(tracer_pid, NULL, __WALL); + if (internal_iserror(waitpid_status, &local_errno)) + VReport(1, "Waiting on the tracer thread failed (errno %d).\n", + local_errno); + } +} + +// 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; +#define REG_SP regs[EF_REG29] + +#else +#error "Unsupported architecture" +#endif // SANITIZER_ANDROID && defined(__arm__) + +int SuspendedThreadsList::GetRegistersAndSP(uptr index, + uptr *buffer, + uptr *sp) const { + pid_t tid = GetThreadID(index); + regs_struct regs; + int pterrno; + if (internal_iserror(internal_ptrace(PTRACE_GETREGS, tid, NULL, ®s), + &pterrno)) { + VReport(1, "Could not get registers from thread %d (errno %d).\n", tid, + pterrno); + return -1; + } + + *sp = regs.REG_SP; + internal_memcpy(buffer, ®s, sizeof(regs)); + return 0; +} + +uptr SuspendedThreadsList::RegisterCount() { + return sizeof(regs_struct) / sizeof(uptr); +} +} // namespace __sanitizer + +#endif // SANITIZER_LINUX && defined(__x86_64__) +//===-- sanitizer_stackdepot.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_stackdepot.h" +#include "sanitizer_common.h" +#include "sanitizer_internal_defs.h" +#include "sanitizer_mutex.h" +#include "sanitizer_atomic.h" + +namespace __sanitizer { + +const int kTabSize = 1024 * 1024; // Hash table size. +const int kPartBits = 8; +const int kPartShift = sizeof(u32) * 8 - kPartBits - 1; +const int kPartCount = 1 << kPartBits; // Number of subparts in the table. +const int kPartSize = kTabSize / kPartCount; +const int kMaxId = 1 << kPartShift; + +struct StackDesc { + StackDesc *link; + u32 id; + u32 hash; + uptr size; + uptr stack[1]; // [size] +}; + +static struct { + StaticSpinMutex mtx; // Protects alloc of new blocks for region allocator. + atomic_uintptr_t region_pos; // Region allocator for StackDesc's. + atomic_uintptr_t region_end; + atomic_uintptr_t tab[kTabSize]; // Hash table of StackDesc's. + atomic_uint32_t seq[kPartCount]; // Unique id generators. +} depot; + +static StackDepotStats stats; + +StackDepotStats *StackDepotGetStats() { + return &stats; +} + +static u32 hash(const uptr *stack, uptr size) { + // murmur2 + const u32 m = 0x5bd1e995; + const u32 seed = 0x9747b28c; + const u32 r = 24; + u32 h = seed ^ (size * sizeof(uptr)); + for (uptr i = 0; i < size; i++) { + u32 k = stack[i]; + k *= m; + k ^= k >> r; + k *= m; + h *= m; + h ^= k; + } + h ^= h >> 13; + h *= m; + h ^= h >> 15; + return h; +} + +static StackDesc *tryallocDesc(uptr memsz) { + // Optimisic lock-free allocation, essentially try to bump the region ptr. + for (;;) { + uptr cmp = atomic_load(&depot.region_pos, memory_order_acquire); + uptr end = atomic_load(&depot.region_end, memory_order_acquire); + if (cmp == 0 || cmp + memsz > end) + return 0; + if (atomic_compare_exchange_weak( + &depot.region_pos, &cmp, cmp + memsz, + memory_order_acquire)) + return (StackDesc*)cmp; + } +} + +static StackDesc *allocDesc(uptr size) { + // First, try to allocate optimisitically. + uptr memsz = sizeof(StackDesc) + (size - 1) * sizeof(uptr); + StackDesc *s = tryallocDesc(memsz); + if (s) + return s; + // If failed, lock, retry and alloc new superblock. + SpinMutexLock l(&depot.mtx); + for (;;) { + s = tryallocDesc(memsz); + if (s) + return s; + atomic_store(&depot.region_pos, 0, memory_order_relaxed); + uptr allocsz = 64 * 1024; + if (allocsz < memsz) + allocsz = memsz; + uptr mem = (uptr)MmapOrDie(allocsz, "stack depot"); + stats.mapped += allocsz; + atomic_store(&depot.region_end, mem + allocsz, memory_order_release); + atomic_store(&depot.region_pos, mem, memory_order_release); + } +} + +static u32 find(StackDesc *s, const uptr *stack, uptr size, u32 hash) { + // Searches linked list s for the stack, returns its id. + for (; s; s = s->link) { + if (s->hash == hash && s->size == size) { + uptr i = 0; + for (; i < size; i++) { + if (stack[i] != s->stack[i]) + break; + } + if (i == size) + return s->id; + } + } + return 0; +} + +static StackDesc *lock(atomic_uintptr_t *p) { + // Uses the pointer lsb as mutex. + for (int i = 0;; i++) { + uptr cmp = atomic_load(p, memory_order_relaxed); + if ((cmp & 1) == 0 + && atomic_compare_exchange_weak(p, &cmp, cmp | 1, + memory_order_acquire)) + return (StackDesc*)cmp; + if (i < 10) + proc_yield(10); + else + internal_sched_yield(); + } +} + +static void unlock(atomic_uintptr_t *p, StackDesc *s) { + DCHECK_EQ((uptr)s & 1, 0); + atomic_store(p, (uptr)s, memory_order_release); +} + +u32 StackDepotPut(const uptr *stack, uptr size) { + if (stack == 0 || size == 0) + return 0; + uptr h = hash(stack, size); + atomic_uintptr_t *p = &depot.tab[h % kTabSize]; + uptr v = atomic_load(p, memory_order_consume); + StackDesc *s = (StackDesc*)(v & ~1); + // First, try to find the existing stack. + u32 id = find(s, stack, size, h); + if (id) + return id; + // If failed, lock, retry and insert new. + StackDesc *s2 = lock(p); + if (s2 != s) { + id = find(s2, stack, size, h); + if (id) { + unlock(p, s2); + return id; + } + } + uptr part = (h % kTabSize) / kPartSize; + id = atomic_fetch_add(&depot.seq[part], 1, memory_order_relaxed) + 1; + stats.n_uniq_ids++; + CHECK_LT(id, kMaxId); + id |= part << kPartShift; + CHECK_NE(id, 0); + CHECK_EQ(id & (1u << 31), 0); + s = allocDesc(size); + s->id = id; + s->hash = h; + s->size = size; + internal_memcpy(s->stack, stack, size * sizeof(uptr)); + s->link = s2; + unlock(p, s); + return id; +} + +const uptr *StackDepotGet(u32 id, uptr *size) { + if (id == 0) + return 0; + CHECK_EQ(id & (1u << 31), 0); + // High kPartBits contain part id, so we need to scan at most kPartSize lists. + uptr part = id >> kPartShift; + for (int i = 0; i != kPartSize; i++) { + uptr idx = part * kPartSize + i; + CHECK_LT(idx, kTabSize); + atomic_uintptr_t *p = &depot.tab[idx]; + uptr v = atomic_load(p, memory_order_consume); + StackDesc *s = (StackDesc*)(v & ~1); + for (; s; s = s->link) { + if (s->id == id) { + *size = s->size; + return s->stack; + } + } + } + *size = 0; + return 0; +} + +bool StackDepotReverseMap::IdDescPair::IdComparator( + const StackDepotReverseMap::IdDescPair &a, + const StackDepotReverseMap::IdDescPair &b) { + return a.id < b.id; +} + +StackDepotReverseMap::StackDepotReverseMap() + : map_(StackDepotGetStats()->n_uniq_ids + 100) { + for (int idx = 0; idx < kTabSize; idx++) { + atomic_uintptr_t *p = &depot.tab[idx]; + uptr v = atomic_load(p, memory_order_consume); + StackDesc *s = (StackDesc*)(v & ~1); + for (; s; s = s->link) { + IdDescPair pair = {s->id, s}; + map_.push_back(pair); + } + } + InternalSort(&map_, map_.size(), IdDescPair::IdComparator); +} + +const uptr *StackDepotReverseMap::Get(u32 id, uptr *size) { + if (!map_.size()) return 0; + IdDescPair pair = {id, 0}; + uptr idx = InternalBinarySearch(map_, 0, map_.size(), pair, + IdDescPair::IdComparator); + if (idx > map_.size()) { + *size = 0; + return 0; + } + StackDesc *desc = map_[idx].desc; + *size = desc->size; + return desc->stack; +} + +} // namespace __sanitizer +//===-- sanitizer_stacktrace.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_flags.h" +#include "sanitizer_stacktrace.h" + +namespace __sanitizer { + +uptr StackTrace::GetPreviousInstructionPc(uptr pc) { +#ifdef __arm__ + // Cancel Thumb bit. + pc = pc & (~1); +#endif +#if defined(__sparc__) + return pc - 8; +#else + return pc - 1; +#endif +} + +uptr StackTrace::GetCurrentPc() { + return GET_CALLER_PC(); +} + +void StackTrace::FastUnwindStack(uptr pc, uptr bp, + uptr stack_top, uptr stack_bottom, + uptr max_depth) { + if (max_depth == 0) { + size = 0; + return; + } + trace[0] = pc; + size = 1; + uptr *frame = (uptr *)bp; + uptr *prev_frame = frame - 1; + if (stack_top < 4096) return; // Sanity check for stack top. + // Avoid infinite loop when frame == frame[0] by using frame > prev_frame. + while (frame > prev_frame && + frame < (uptr *)stack_top - 2 && + frame > (uptr *)stack_bottom && + IsAligned((uptr)frame, sizeof(*frame)) && + size < max_depth) { + uptr pc1 = frame[1]; + if (pc1 != pc) { + trace[size++] = pc1; + } + prev_frame = frame; + frame = (uptr*)frame[0]; + } +} + +static bool MatchPc(uptr cur_pc, uptr trace_pc, uptr threshold) { + return cur_pc - trace_pc <= threshold || trace_pc - cur_pc <= threshold; +} + +void StackTrace::PopStackFrames(uptr count) { + CHECK_LT(count, size); + size -= count; + for (uptr i = 0; i < size; ++i) { + trace[i] = trace[i + count]; + } +} + +uptr StackTrace::LocatePcInTrace(uptr pc) { + // Use threshold to find PC in stack trace, as PC we want to unwind from may + // slightly differ from return address in the actual unwinded stack trace. + const int kPcThreshold = 192; + for (uptr i = 0; i < size; ++i) { + if (MatchPc(pc, trace[i], kPcThreshold)) + return i; + } + return 0; +} + +} // namespace __sanitizer +//===-- sanitizer_stacktrace_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. +//===----------------------------------------------------------------------===// + +#include "sanitizer_common.h" +#include "sanitizer_stacktrace.h" +#include "sanitizer_symbolizer.h" + +namespace __sanitizer { + +static void PrintStackFramePrefix(InternalScopedString *buffer, uptr frame_num, + uptr pc) { + buffer->append(" #%zu 0x%zx", frame_num, pc); +} + +void StackTrace::PrintStack(const uptr *addr, uptr size) { + if (addr == 0 || size == 0) { + Printf(" <empty stack>\n\n"); + return; + } + InternalScopedBuffer<char> buff(GetPageSizeCached() * 2); + InternalScopedBuffer<AddressInfo> addr_frames(64); + InternalScopedString frame_desc(GetPageSizeCached() * 2); + uptr frame_num = 0; + for (uptr i = 0; i < size && addr[i]; i++) { + // PCs in stack traces are actually the return addresses, that is, + // addresses of the next instructions after the call. + uptr pc = GetPreviousInstructionPc(addr[i]); + uptr addr_frames_num = Symbolizer::GetOrInit()->SymbolizePC( + pc, addr_frames.data(), addr_frames.size()); + for (uptr j = 0; j < addr_frames_num; j++) { + AddressInfo &info = addr_frames[j]; + frame_desc.clear(); + PrintStackFramePrefix(&frame_desc, frame_num, pc); + if (info.function) { + frame_desc.append(" in %s", info.function); + // Print offset in function if we don't know the source file. + if (!info.file && info.function_offset != AddressInfo::kUnknown) + frame_desc.append("+0x%zx", info.function_offset); + } + if (info.file) { + frame_desc.append(" "); + PrintSourceLocation(&frame_desc, info.file, info.line, info.column); + } else if (info.module) { + frame_desc.append(" "); + PrintModuleAndOffset(&frame_desc, info.module, info.module_offset); + } + Printf("%s\n", frame_desc.data()); + frame_num++; + info.Clear(); + } + } + // Always print a trailing empty line after stack trace. + Printf("\n"); +} + +void StackTrace::Unwind(uptr max_depth, uptr pc, uptr bp, void *context, + uptr stack_top, uptr stack_bottom, + bool request_fast_unwind) { + if (!WillUseFastUnwind(request_fast_unwind)) { + if (context) + SlowUnwindStackWithContext(pc, context, max_depth); + else + SlowUnwindStack(pc, max_depth); + } else { + FastUnwindStack(pc, bp, stack_top, stack_bottom, max_depth); + } + + top_frame_bp = size ? bp : 0; +} + +} // namespace __sanitizer +//===-- sanitizer_symbolizer.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_platform.h" +#include "sanitizer_internal_defs.h" +#include "sanitizer_placement_new.h" +#include "sanitizer_symbolizer.h" + +namespace __sanitizer { + +Symbolizer *Symbolizer::symbolizer_; +StaticSpinMutex Symbolizer::init_mu_; +LowLevelAllocator Symbolizer::symbolizer_allocator_; + +Symbolizer *Symbolizer::GetOrNull() { + SpinMutexLock l(&init_mu_); + return symbolizer_; +} + +Symbolizer *Symbolizer::Get() { + SpinMutexLock l(&init_mu_); + RAW_CHECK_MSG(symbolizer_ != 0, "Using uninitialized symbolizer!"); + return symbolizer_; +} + +Symbolizer *Symbolizer::Disable() { + CHECK_EQ(0, symbolizer_); + // Initialize a dummy symbolizer. + symbolizer_ = new(symbolizer_allocator_) Symbolizer; + return symbolizer_; +} + +void Symbolizer::AddHooks(Symbolizer::StartSymbolizationHook start_hook, + Symbolizer::EndSymbolizationHook end_hook) { + CHECK(start_hook_ == 0 && end_hook_ == 0); + start_hook_ = start_hook; + end_hook_ = end_hook; +} + +Symbolizer::Symbolizer() : start_hook_(0), end_hook_(0) {} + +Symbolizer::SymbolizerScope::SymbolizerScope(const Symbolizer *sym) + : sym_(sym) { + if (sym_->start_hook_) + sym_->start_hook_(); +} + +Symbolizer::SymbolizerScope::~SymbolizerScope() { + if (sym_->end_hook_) + sym_->end_hook_(); +} + +} // namespace __sanitizer +//===-- sanitizer_symbolizer_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. +//===----------------------------------------------------------------------===// + +#include "sanitizer_internal_defs.h" +#include "sanitizer_symbolizer.h" + +namespace __sanitizer { + +Symbolizer *Symbolizer::CreateAndStore(const char *path_to_external) { + Symbolizer *platform_symbolizer = PlatformInit(path_to_external); + if (!platform_symbolizer) + return Disable(); + symbolizer_ = platform_symbolizer; + return platform_symbolizer; +} + +Symbolizer *Symbolizer::Init(const char *path_to_external) { + CHECK_EQ(0, symbolizer_); + return CreateAndStore(path_to_external); +} + +Symbolizer *Symbolizer::GetOrInit() { + SpinMutexLock l(&init_mu_); + if (symbolizer_ == 0) + return CreateAndStore(0); + return symbolizer_; +} + +} // namespace __sanitizer +//===-- sanitizer_symbolizer_posix_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. +// POSIX-specific implementation of symbolizer parts. +//===----------------------------------------------------------------------===// + +#include "sanitizer_platform.h" +#if SANITIZER_POSIX +#include "sanitizer_allocator_internal.h" +#include "sanitizer_common.h" +#include "sanitizer_flags.h" +#include "sanitizer_internal_defs.h" +#include "sanitizer_linux.h" +#include "sanitizer_placement_new.h" +#include "sanitizer_procmaps.h" +#include "sanitizer_symbolizer.h" +#include "sanitizer_symbolizer_libbacktrace.h" + +#include <errno.h> +#include <stdlib.h> +#include <sys/wait.h> +#include <unistd.h> + +// C++ demangling function, as required by Itanium C++ ABI. This is weak, +// because we do not require a C++ ABI library to be linked to a program +// using sanitizers; if it's not present, we'll just use the mangled name. +// +// On Android, this is not weak, because we are using shared runtime library +// AND static libstdc++, and there is no good way to conditionally export +// __cxa_demangle. By making this a non-weak symbol, we statically link +// __cxa_demangle into ASan runtime library. +namespace __cxxabiv1 { + extern "C" +#if !SANITIZER_ANDROID + SANITIZER_WEAK_ATTRIBUTE +#endif + char *__cxa_demangle(const char *mangled, char *buffer, size_t *length, + int *status); +} + +namespace __sanitizer { + +// Attempts to demangle the name via __cxa_demangle from __cxxabiv1. +static const char *DemangleCXXABI(const char *name) { + // FIXME: __cxa_demangle aggressively insists on allocating memory. + // There's not much we can do about that, short of providing our + // own demangler (libc++abi's implementation could be adapted so that + // it does not allocate). For now, we just call it anyway, and we leak + // the returned value. + if (SANITIZER_ANDROID || &__cxxabiv1::__cxa_demangle) + if (const char *demangled_name = + __cxxabiv1::__cxa_demangle(name, 0, 0, 0)) + return demangled_name; + + return name; +} + +// Extracts the prefix of "str" that consists of any characters not +// present in "delims" string, and copies this prefix to "result", allocating +// space for it. +// Returns a pointer to "str" after skipping extracted prefix and first +// delimiter char. +static const char *ExtractToken(const char *str, const char *delims, + char **result) { + uptr prefix_len = internal_strcspn(str, delims); + *result = (char*)InternalAlloc(prefix_len + 1); + internal_memcpy(*result, str, prefix_len); + (*result)[prefix_len] = '\0'; + const char *prefix_end = str + prefix_len; + if (*prefix_end != '\0') prefix_end++; + return prefix_end; +} + +// Same as ExtractToken, but converts extracted token to integer. +static const char *ExtractInt(const char *str, const char *delims, + int *result) { + char *buff; + const char *ret = ExtractToken(str, delims, &buff); + if (buff != 0) { + *result = (int)internal_atoll(buff); + } + InternalFree(buff); + return ret; +} + +static const char *ExtractUptr(const char *str, const char *delims, + uptr *result) { + char *buff; + const char *ret = ExtractToken(str, delims, &buff); + if (buff != 0) { + *result = (uptr)internal_atoll(buff); + } + InternalFree(buff); + return ret; +} + +class ExternalSymbolizerInterface { + public: + // Can't declare pure virtual functions in sanitizer runtimes: + // __cxa_pure_virtual might be unavailable. + virtual char *SendCommand(bool is_data, const char *module_name, + uptr module_offset) { + UNIMPLEMENTED(); + } +}; + +// SymbolizerProcess encapsulates communication between the tool and +// external symbolizer program, running in a different subprocess. +// SymbolizerProcess may not be used from two threads simultaneously. +class SymbolizerProcess : public ExternalSymbolizerInterface { + public: + explicit SymbolizerProcess(const char *path) + : path_(path), + input_fd_(kInvalidFd), + output_fd_(kInvalidFd), + times_restarted_(0), + failed_to_start_(false), + reported_invalid_path_(false) { + CHECK(path_); + CHECK_NE(path_[0], '\0'); + } + + char *SendCommand(bool is_data, const char *module_name, uptr module_offset) { + for (; times_restarted_ < kMaxTimesRestarted; times_restarted_++) { + // Start or restart symbolizer if we failed to send command to it. + if (char *res = SendCommandImpl(is_data, module_name, module_offset)) + return res; + Restart(); + } + if (!failed_to_start_) { + Report("WARNING: Failed to use and restart external symbolizer!\n"); + failed_to_start_ = true; + } + return 0; + } + + private: + bool Restart() { + if (input_fd_ != kInvalidFd) + internal_close(input_fd_); + if (output_fd_ != kInvalidFd) + internal_close(output_fd_); + return StartSymbolizerSubprocess(); + } + + char *SendCommandImpl(bool is_data, const char *module_name, + uptr module_offset) { + if (input_fd_ == kInvalidFd || output_fd_ == kInvalidFd) + return 0; + CHECK(module_name); + if (!RenderInputCommand(buffer_, kBufferSize, is_data, module_name, + module_offset)) + return 0; + if (!writeToSymbolizer(buffer_, internal_strlen(buffer_))) + return 0; + if (!readFromSymbolizer(buffer_, kBufferSize)) + return 0; + return buffer_; + } + + bool readFromSymbolizer(char *buffer, uptr max_length) { + if (max_length == 0) + return true; + uptr read_len = 0; + while (true) { + uptr just_read = internal_read(input_fd_, buffer + read_len, + max_length - read_len - 1); + // We can't read 0 bytes, as we don't expect external symbolizer to close + // its stdout. + if (just_read == 0 || just_read == (uptr)-1) { + Report("WARNING: Can't read from symbolizer at fd %d\n", input_fd_); + return false; + } + read_len += just_read; + if (ReachedEndOfOutput(buffer, read_len)) + break; + } + buffer[read_len] = '\0'; + return true; + } + + bool writeToSymbolizer(const char *buffer, uptr length) { + if (length == 0) + return true; + uptr write_len = internal_write(output_fd_, buffer, length); + if (write_len == 0 || write_len == (uptr)-1) { + Report("WARNING: Can't write to symbolizer at fd %d\n", output_fd_); + return false; + } + return true; + } + + bool StartSymbolizerSubprocess() { + if (!FileExists(path_)) { + if (!reported_invalid_path_) { + Report("WARNING: invalid path to external symbolizer!\n"); + reported_invalid_path_ = true; + } + return false; + } + + int *infd = NULL; + int *outfd = NULL; + // The client program may close its stdin and/or stdout and/or stderr + // thus allowing socketpair to reuse file descriptors 0, 1 or 2. + // In this case the communication between the forked processes may be + // broken if either the parent or the child tries to close or duplicate + // these descriptors. The loop below produces two pairs of file + // descriptors, each greater than 2 (stderr). + int sock_pair[5][2]; + for (int i = 0; i < 5; i++) { + if (pipe(sock_pair[i]) == -1) { + for (int j = 0; j < i; j++) { + internal_close(sock_pair[j][0]); + internal_close(sock_pair[j][1]); + } + Report("WARNING: Can't create a socket pair to start " + "external symbolizer (errno: %d)\n", errno); + return false; + } else if (sock_pair[i][0] > 2 && sock_pair[i][1] > 2) { + if (infd == NULL) { + infd = sock_pair[i]; + } else { + outfd = sock_pair[i]; + for (int j = 0; j < i; j++) { + if (sock_pair[j] == infd) continue; + internal_close(sock_pair[j][0]); + internal_close(sock_pair[j][1]); + } + break; + } + } + } + CHECK(infd); + CHECK(outfd); + + int pid = fork(); + if (pid == -1) { + // Fork() failed. + internal_close(infd[0]); + internal_close(infd[1]); + internal_close(outfd[0]); + internal_close(outfd[1]); + Report("WARNING: failed to fork external symbolizer " + " (errno: %d)\n", errno); + return false; + } else if (pid == 0) { + // Child subprocess. + internal_close(STDOUT_FILENO); + internal_close(STDIN_FILENO); + internal_dup2(outfd[0], STDIN_FILENO); + internal_dup2(infd[1], STDOUT_FILENO); + internal_close(outfd[0]); + internal_close(outfd[1]); + internal_close(infd[0]); + internal_close(infd[1]); + for (int fd = getdtablesize(); fd > 2; fd--) + internal_close(fd); + ExecuteWithDefaultArgs(path_); + internal__exit(1); + } + + // Continue execution in parent process. + internal_close(outfd[0]); + internal_close(infd[1]); + input_fd_ = infd[0]; + output_fd_ = outfd[1]; + + // Check that symbolizer subprocess started successfully. + int pid_status; + SleepForMillis(kSymbolizerStartupTimeMillis); + int exited_pid = waitpid(pid, &pid_status, WNOHANG); + if (exited_pid != 0) { + // Either waitpid failed, or child has already exited. + Report("WARNING: external symbolizer didn't start up correctly!\n"); + return false; + } + + return true; + } + + virtual bool RenderInputCommand(char *buffer, uptr max_length, bool is_data, + const char *module_name, + uptr module_offset) const { + UNIMPLEMENTED(); + } + + virtual bool ReachedEndOfOutput(const char *buffer, uptr length) const { + UNIMPLEMENTED(); + } + + virtual void ExecuteWithDefaultArgs(const char *path_to_binary) const { + UNIMPLEMENTED(); + } + + const char *path_; + int input_fd_; + int output_fd_; + + static const uptr kBufferSize = 16 * 1024; + char buffer_[kBufferSize]; + + static const uptr kMaxTimesRestarted = 5; + static const int kSymbolizerStartupTimeMillis = 10; + uptr times_restarted_; + bool failed_to_start_; + bool reported_invalid_path_; +}; + +// For now we assume the following protocol: +// For each request of the form +// <module_name> <module_offset> +// passed to STDIN, external symbolizer prints to STDOUT response: +// <function_name> +// <file_name>:<line_number>:<column_number> +// <function_name> +// <file_name>:<line_number>:<column_number> +// ... +// <empty line> +class LLVMSymbolizerProcess : public SymbolizerProcess { + public: + explicit LLVMSymbolizerProcess(const char *path) : SymbolizerProcess(path) {} + + private: + bool RenderInputCommand(char *buffer, uptr max_length, bool is_data, + const char *module_name, uptr module_offset) const { + internal_snprintf(buffer, max_length, "%s\"%s\" 0x%zx\n", + is_data ? "DATA " : "", module_name, module_offset); + return true; + } + + bool ReachedEndOfOutput(const char *buffer, uptr length) const { + // Empty line marks the end of llvm-symbolizer output. + return length >= 2 && buffer[length - 1] == '\n' && + buffer[length - 2] == '\n'; + } + + void ExecuteWithDefaultArgs(const char *path_to_binary) const { +#if defined(__x86_64__) + const char* const kSymbolizerArch = "--default-arch=x86_64"; +#elif defined(__i386__) + const char* const kSymbolizerArch = "--default-arch=i386"; +#elif defined(__powerpc64__) + const char* const kSymbolizerArch = "--default-arch=powerpc64"; +#else + const char* const kSymbolizerArch = "--default-arch=unknown"; +#endif + execl(path_to_binary, path_to_binary, kSymbolizerArch, (char *)0); + } +}; + +class Addr2LineProcess : public SymbolizerProcess { + public: + Addr2LineProcess(const char *path, const char *module_name) + : SymbolizerProcess(path), module_name_(internal_strdup(module_name)) {} + + const char *module_name() const { return module_name_; } + + private: + bool RenderInputCommand(char *buffer, uptr max_length, bool is_data, + const char *module_name, uptr module_offset) const { + if (is_data) + return false; + CHECK_EQ(0, internal_strcmp(module_name, module_name_)); + internal_snprintf(buffer, max_length, "0x%zx\n", module_offset); + return true; + } + + bool ReachedEndOfOutput(const char *buffer, uptr length) const { + // Output should consist of two lines. + int num_lines = 0; + for (uptr i = 0; i < length; ++i) { + if (buffer[i] == '\n') + num_lines++; + if (num_lines >= 2) + return true; + } + return false; + } + + void ExecuteWithDefaultArgs(const char *path_to_binary) const { + execl(path_to_binary, path_to_binary, "-Cfe", module_name_, (char *)0); + } + + const char *module_name_; // Owned, leaked. +}; + +class Addr2LinePool : public ExternalSymbolizerInterface { + public: + explicit Addr2LinePool(const char *addr2line_path, + LowLevelAllocator *allocator) + : addr2line_path_(addr2line_path), allocator_(allocator), + addr2line_pool_(16) {} + + char *SendCommand(bool is_data, const char *module_name, uptr module_offset) { + if (is_data) + return 0; + Addr2LineProcess *addr2line = 0; + for (uptr i = 0; i < addr2line_pool_.size(); ++i) { + if (0 == + internal_strcmp(module_name, addr2line_pool_[i]->module_name())) { + addr2line = addr2line_pool_[i]; + break; + } + } + if (!addr2line) { + addr2line = + new(*allocator_) Addr2LineProcess(addr2line_path_, module_name); + addr2line_pool_.push_back(addr2line); + } + return addr2line->SendCommand(is_data, module_name, module_offset); + } + + private: + const char *addr2line_path_; + LowLevelAllocator *allocator_; + InternalMmapVector<Addr2LineProcess*> addr2line_pool_; +}; + +#if SANITIZER_SUPPORTS_WEAK_HOOKS +extern "C" { +SANITIZER_INTERFACE_ATTRIBUTE SANITIZER_WEAK_ATTRIBUTE +bool __sanitizer_symbolize_code(const char *ModuleName, u64 ModuleOffset, + char *Buffer, int MaxLength); +SANITIZER_INTERFACE_ATTRIBUTE SANITIZER_WEAK_ATTRIBUTE +bool __sanitizer_symbolize_data(const char *ModuleName, u64 ModuleOffset, + char *Buffer, int MaxLength); +SANITIZER_INTERFACE_ATTRIBUTE SANITIZER_WEAK_ATTRIBUTE +void __sanitizer_symbolize_flush(); +SANITIZER_INTERFACE_ATTRIBUTE SANITIZER_WEAK_ATTRIBUTE +int __sanitizer_symbolize_demangle(const char *Name, char *Buffer, + int MaxLength); +} // extern "C" + +class InternalSymbolizer { + public: + typedef bool (*SanitizerSymbolizeFn)(const char*, u64, char*, int); + + static InternalSymbolizer *get(LowLevelAllocator *alloc) { + if (__sanitizer_symbolize_code != 0 && + __sanitizer_symbolize_data != 0) { + return new(*alloc) InternalSymbolizer(); + } + return 0; + } + + char *SendCommand(bool is_data, const char *module_name, uptr module_offset) { + SanitizerSymbolizeFn symbolize_fn = is_data ? __sanitizer_symbolize_data + : __sanitizer_symbolize_code; + if (symbolize_fn(module_name, module_offset, buffer_, kBufferSize)) + return buffer_; + return 0; + } + + void Flush() { + if (__sanitizer_symbolize_flush) + __sanitizer_symbolize_flush(); + } + + const char *Demangle(const char *name) { + if (__sanitizer_symbolize_demangle) { + for (uptr res_length = 1024; + res_length <= InternalSizeClassMap::kMaxSize;) { + char *res_buff = static_cast<char*>(InternalAlloc(res_length)); + uptr req_length = + __sanitizer_symbolize_demangle(name, res_buff, res_length); + if (req_length > res_length) { + res_length = req_length + 1; + InternalFree(res_buff); + continue; + } + return res_buff; + } + } + return name; + } + + private: + InternalSymbolizer() { } + + static const int kBufferSize = 16 * 1024; + static const int kMaxDemangledNameSize = 1024; + char buffer_[kBufferSize]; +}; +#else // SANITIZER_SUPPORTS_WEAK_HOOKS + +class InternalSymbolizer { + public: + static InternalSymbolizer *get(LowLevelAllocator *alloc) { return 0; } + char *SendCommand(bool is_data, const char *module_name, uptr module_offset) { + return 0; + } + void Flush() { } + const char *Demangle(const char *name) { return name; } +}; + +#endif // SANITIZER_SUPPORTS_WEAK_HOOKS + +class POSIXSymbolizer : public Symbolizer { + public: + POSIXSymbolizer(ExternalSymbolizerInterface *external_symbolizer, + InternalSymbolizer *internal_symbolizer, + LibbacktraceSymbolizer *libbacktrace_symbolizer) + : Symbolizer(), + external_symbolizer_(external_symbolizer), + internal_symbolizer_(internal_symbolizer), + libbacktrace_symbolizer_(libbacktrace_symbolizer) {} + + uptr SymbolizePC(uptr addr, AddressInfo *frames, uptr max_frames) { + BlockingMutexLock l(&mu_); + if (max_frames == 0) + return 0; + const char *module_name; + uptr module_offset; + if (!FindModuleNameAndOffsetForAddress(addr, &module_name, &module_offset)) + return 0; + // First, try to use libbacktrace symbolizer (if it's available). + if (libbacktrace_symbolizer_ != 0) { + mu_.CheckLocked(); + uptr res = libbacktrace_symbolizer_->SymbolizeCode( + addr, frames, max_frames, module_name, module_offset); + if (res > 0) + return res; + } + const char *str = SendCommand(false, module_name, module_offset); + if (str == 0) { + // Symbolizer was not initialized or failed. Fill only data + // about module name and offset. + AddressInfo *info = &frames[0]; + info->Clear(); + info->FillAddressAndModuleInfo(addr, module_name, module_offset); + return 1; + } + uptr frame_id = 0; + for (frame_id = 0; frame_id < max_frames; frame_id++) { + AddressInfo *info = &frames[frame_id]; + char *function_name = 0; + str = ExtractToken(str, "\n", &function_name); + CHECK(function_name); + if (function_name[0] == '\0') { + // There are no more frames. + break; + } + info->Clear(); + info->FillAddressAndModuleInfo(addr, module_name, module_offset); + info->function = function_name; + // Parse <file>:<line>:<column> buffer. + char *file_line_info = 0; + str = ExtractToken(str, "\n", &file_line_info); + CHECK(file_line_info); + const char *line_info = ExtractToken(file_line_info, ":", &info->file); + line_info = ExtractInt(line_info, ":", &info->line); + line_info = ExtractInt(line_info, "", &info->column); + InternalFree(file_line_info); + + // Functions and filenames can be "??", in which case we write 0 + // to address info to mark that names are unknown. + if (0 == internal_strcmp(info->function, "??")) { + InternalFree(info->function); + info->function = 0; + } + if (0 == internal_strcmp(info->file, "??")) { + InternalFree(info->file); + info->file = 0; + } + } + if (frame_id == 0) { + // Make sure we return at least one frame. + AddressInfo *info = &frames[0]; + info->Clear(); + info->FillAddressAndModuleInfo(addr, module_name, module_offset); + frame_id = 1; + } + return frame_id; + } + + bool SymbolizeData(uptr addr, DataInfo *info) { + BlockingMutexLock l(&mu_); + LoadedModule *module = FindModuleForAddress(addr); + if (module == 0) + return false; + const char *module_name = module->full_name(); + uptr module_offset = addr - module->base_address(); + internal_memset(info, 0, sizeof(*info)); + info->address = addr; + info->module = internal_strdup(module_name); + info->module_offset = module_offset; + // First, try to use libbacktrace symbolizer (if it's available). + if (libbacktrace_symbolizer_ != 0) { + mu_.CheckLocked(); + if (libbacktrace_symbolizer_->SymbolizeData(info)) + return true; + } + const char *str = SendCommand(true, module_name, module_offset); + if (str == 0) + return true; + str = ExtractToken(str, "\n", &info->name); + str = ExtractUptr(str, " ", &info->start); + str = ExtractUptr(str, "\n", &info->size); + info->start += module->base_address(); + return true; + } + + bool GetModuleNameAndOffsetForPC(uptr pc, const char **module_name, + uptr *module_address) { + BlockingMutexLock l(&mu_); + return FindModuleNameAndOffsetForAddress(pc, module_name, module_address); + } + + bool CanReturnFileLineInfo() { + return internal_symbolizer_ != 0 || external_symbolizer_ != 0 || + libbacktrace_symbolizer_ != 0; + } + + void Flush() { + BlockingMutexLock l(&mu_); + if (internal_symbolizer_ != 0) { + SymbolizerScope sym_scope(this); + internal_symbolizer_->Flush(); + } + } + + const char *Demangle(const char *name) { + BlockingMutexLock l(&mu_); + // Run hooks even if we don't use internal symbolizer, as cxxabi + // demangle may call system functions. + SymbolizerScope sym_scope(this); + // Try to use libbacktrace demangler (if available). + if (libbacktrace_symbolizer_ != 0) { + if (const char *demangled = libbacktrace_symbolizer_->Demangle(name)) + return demangled; + } + if (internal_symbolizer_ != 0) + return internal_symbolizer_->Demangle(name); + return DemangleCXXABI(name); + } + + void PrepareForSandboxing() { +#if SANITIZER_LINUX && !SANITIZER_ANDROID + BlockingMutexLock l(&mu_); + // Cache /proc/self/exe on Linux. + CacheBinaryName(); +#endif + } + + private: + char *SendCommand(bool is_data, const char *module_name, uptr module_offset) { + mu_.CheckLocked(); + // First, try to use internal symbolizer. + if (internal_symbolizer_) { + SymbolizerScope sym_scope(this); + return internal_symbolizer_->SendCommand(is_data, module_name, + module_offset); + } + // Otherwise, fall back to external symbolizer. + if (external_symbolizer_) { + SymbolizerScope sym_scope(this); + return external_symbolizer_->SendCommand(is_data, module_name, + module_offset); + } + return 0; + } + + LoadedModule *FindModuleForAddress(uptr address) { + mu_.CheckLocked(); + bool modules_were_reloaded = false; + if (modules_ == 0 || !modules_fresh_) { + modules_ = (LoadedModule*)(symbolizer_allocator_.Allocate( + kMaxNumberOfModuleContexts * sizeof(LoadedModule))); + CHECK(modules_); + n_modules_ = GetListOfModules(modules_, kMaxNumberOfModuleContexts, + /* filter */ 0); + CHECK_GT(n_modules_, 0); + CHECK_LT(n_modules_, kMaxNumberOfModuleContexts); + modules_fresh_ = true; + modules_were_reloaded = true; + } + for (uptr i = 0; i < n_modules_; i++) { + if (modules_[i].containsAddress(address)) { + return &modules_[i]; + } + } + // Reload the modules and look up again, if we haven't tried it yet. + if (!modules_were_reloaded) { + // FIXME: set modules_fresh_ from dlopen()/dlclose() interceptors. + // It's too aggressive to reload the list of modules each time we fail + // to find a module for a given address. + modules_fresh_ = false; + return FindModuleForAddress(address); + } + return 0; + } + + bool FindModuleNameAndOffsetForAddress(uptr address, const char **module_name, + uptr *module_offset) { + mu_.CheckLocked(); + LoadedModule *module = FindModuleForAddress(address); + if (module == 0) + return false; + *module_name = module->full_name(); + *module_offset = address - module->base_address(); + return true; + } + + // 16K loaded modules should be enough for everyone. + static const uptr kMaxNumberOfModuleContexts = 1 << 14; + LoadedModule *modules_; // Array of module descriptions is leaked. + uptr n_modules_; + // If stale, need to reload the modules before looking up addresses. + bool modules_fresh_; + BlockingMutex mu_; + + ExternalSymbolizerInterface *external_symbolizer_; // Leaked. + InternalSymbolizer *const internal_symbolizer_; // Leaked. + LibbacktraceSymbolizer *libbacktrace_symbolizer_; // Leaked. +}; + +Symbolizer *Symbolizer::PlatformInit(const char *path_to_external) { + if (!common_flags()->symbolize) { + return new(symbolizer_allocator_) POSIXSymbolizer(0, 0, 0); + } + InternalSymbolizer* internal_symbolizer = + InternalSymbolizer::get(&symbolizer_allocator_); + ExternalSymbolizerInterface *external_symbolizer = 0; + LibbacktraceSymbolizer *libbacktrace_symbolizer = 0; + + if (!internal_symbolizer) { + libbacktrace_symbolizer = + LibbacktraceSymbolizer::get(&symbolizer_allocator_); + if (!libbacktrace_symbolizer) { + if (path_to_external && path_to_external[0] == '\0') { + // External symbolizer is explicitly disabled. Do nothing. + } else { + // Find path to llvm-symbolizer if it's not provided. + if (!path_to_external) + path_to_external = FindPathToBinary("llvm-symbolizer"); + if (path_to_external) { + external_symbolizer = new(symbolizer_allocator_) + LLVMSymbolizerProcess(path_to_external); + } else if (common_flags()->allow_addr2line) { + // If llvm-symbolizer is not found, try to use addr2line. + if (const char *addr2line_path = FindPathToBinary("addr2line")) { + external_symbolizer = new(symbolizer_allocator_) + Addr2LinePool(addr2line_path, &symbolizer_allocator_); + } + } + } + } + } + + return new(symbolizer_allocator_) POSIXSymbolizer( + external_symbolizer, internal_symbolizer, libbacktrace_symbolizer); +} + +} // namespace __sanitizer + +#endif // SANITIZER_POSIX +//===-- sanitizer_symbolizer_libbacktrace.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. +// Libbacktrace implementation of symbolizer parts. +//===----------------------------------------------------------------------===// + +#include "sanitizer_platform.h" + +#include "sanitizer_internal_defs.h" +#include "sanitizer_symbolizer.h" +#include "sanitizer_symbolizer_libbacktrace.h" + +#if SANITIZER_LIBBACKTRACE +# include "backtrace-supported.h" +# if SANITIZER_POSIX && BACKTRACE_SUPPORTED && !BACKTRACE_USES_MALLOC +# include "backtrace.h" +# if SANITIZER_CP_DEMANGLE +# undef ARRAY_SIZE +# include "demangle.h" +# endif +# else +# define SANITIZER_LIBBACKTRACE 0 +# endif +#endif + +namespace __sanitizer { + +#if SANITIZER_LIBBACKTRACE + +namespace { + +# if SANITIZER_CP_DEMANGLE +struct CplusV3DemangleData { + char *buf; + uptr size, allocated; +}; + +extern "C" { +static void CplusV3DemangleCallback(const char *s, size_t l, void *vdata) { + CplusV3DemangleData *data = (CplusV3DemangleData *)vdata; + uptr needed = data->size + l + 1; + if (needed > data->allocated) { + data->allocated *= 2; + if (needed > data->allocated) + data->allocated = needed; + char *buf = (char *)InternalAlloc(data->allocated); + if (data->buf) { + internal_memcpy(buf, data->buf, data->size); + InternalFree(data->buf); + } + data->buf = buf; + } + internal_memcpy(data->buf + data->size, s, l); + data->buf[data->size + l] = '\0'; + data->size += l; +} +} // extern "C" + +char *CplusV3Demangle(const char *name) { + CplusV3DemangleData data; + data.buf = 0; + data.size = 0; + data.allocated = 0; + if (cplus_demangle_v3_callback(name, DMGL_PARAMS | DMGL_ANSI, + CplusV3DemangleCallback, &data)) { + if (data.size + 64 > data.allocated) + return data.buf; + char *buf = internal_strdup(data.buf); + InternalFree(data.buf); + return buf; + } + if (data.buf) + InternalFree(data.buf); + return 0; +} +# endif // SANITIZER_CP_DEMANGLE + +struct SymbolizeCodeData { + AddressInfo *frames; + uptr n_frames; + uptr max_frames; + const char *module_name; + uptr module_offset; +}; + +extern "C" { +static int SymbolizeCodePCInfoCallback(void *vdata, uintptr_t addr, + const char *filename, int lineno, + const char *function) { + SymbolizeCodeData *cdata = (SymbolizeCodeData *)vdata; + if (function) { + AddressInfo *info = &cdata->frames[cdata->n_frames++]; + info->Clear(); + info->FillAddressAndModuleInfo(addr, cdata->module_name, + cdata->module_offset); + info->function = LibbacktraceSymbolizer::Demangle(function, true); + if (filename) + info->file = internal_strdup(filename); + info->line = lineno; + if (cdata->n_frames == cdata->max_frames) + return 1; + } + return 0; +} + +static void SymbolizeCodeCallback(void *vdata, uintptr_t addr, + const char *symname, uintptr_t, uintptr_t) { + SymbolizeCodeData *cdata = (SymbolizeCodeData *)vdata; + if (symname) { + AddressInfo *info = &cdata->frames[0]; + info->Clear(); + info->FillAddressAndModuleInfo(addr, cdata->module_name, + cdata->module_offset); + info->function = LibbacktraceSymbolizer::Demangle(symname, true); + cdata->n_frames = 1; + } +} + +static void SymbolizeDataCallback(void *vdata, uintptr_t, const char *symname, + uintptr_t symval, uintptr_t symsize) { + DataInfo *info = (DataInfo *)vdata; + if (symname && symval) { + info->name = LibbacktraceSymbolizer::Demangle(symname, true); + info->start = symval; + info->size = symsize; + } +} + +static void ErrorCallback(void *, const char *, int) {} +} // extern "C" + +} // namespace + +LibbacktraceSymbolizer *LibbacktraceSymbolizer::get(LowLevelAllocator *alloc) { + // State created in backtrace_create_state is leaked. + void *state = (void *)(backtrace_create_state("/proc/self/exe", 0, + ErrorCallback, NULL)); + if (!state) + return 0; + return new(*alloc) LibbacktraceSymbolizer(state); +} + +uptr LibbacktraceSymbolizer::SymbolizeCode(uptr addr, AddressInfo *frames, + uptr max_frames, + const char *module_name, + uptr module_offset) { + SymbolizeCodeData data; + data.frames = frames; + data.n_frames = 0; + data.max_frames = max_frames; + data.module_name = module_name; + data.module_offset = module_offset; + backtrace_pcinfo((backtrace_state *)state_, addr, SymbolizeCodePCInfoCallback, + ErrorCallback, &data); + if (data.n_frames) + return data.n_frames; + backtrace_syminfo((backtrace_state *)state_, addr, SymbolizeCodeCallback, + ErrorCallback, &data); + return data.n_frames; +} + +bool LibbacktraceSymbolizer::SymbolizeData(DataInfo *info) { + backtrace_syminfo((backtrace_state *)state_, info->address, + SymbolizeDataCallback, ErrorCallback, info); + return true; +} + +#else // SANITIZER_LIBBACKTRACE + +LibbacktraceSymbolizer *LibbacktraceSymbolizer::get(LowLevelAllocator *alloc) { + return 0; +} + +uptr LibbacktraceSymbolizer::SymbolizeCode(uptr addr, AddressInfo *frames, + uptr max_frames, + const char *module_name, + uptr module_offset) { + (void)state_; + return 0; +} + +bool LibbacktraceSymbolizer::SymbolizeData(DataInfo *info) { + return false; +} + +#endif // SANITIZER_LIBBACKTRACE + +char *LibbacktraceSymbolizer::Demangle(const char *name, bool always_alloc) { +#if SANITIZER_LIBBACKTRACE && SANITIZER_CP_DEMANGLE + if (char *demangled = CplusV3Demangle(name)) + return demangled; +#endif + if (always_alloc) + return internal_strdup(name); + return 0; +} + +} // namespace __sanitizer +//===-- interception_linux.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 AddressSanitizer, an address sanity checker. +// +// Linux-specific interception methods. +//===----------------------------------------------------------------------===// + +#if defined(__linux__) || defined(__FreeBSD__) +#include "interception.h" + +#include <dlfcn.h> // for dlsym() and dlvsym() + +namespace __interception { +bool GetRealFunctionAddress(const char *func_name, uptr *func_addr, + uptr real, uptr wrapper) { + *func_addr = (uptr)dlsym(RTLD_NEXT, func_name); + return real == wrapper; +} + +#if !defined(__ANDROID__) // android does not have dlvsym +void *GetFuncAddrVer(const char *func_name, const char *ver) { + return dlvsym(RTLD_NEXT, func_name, ver); +} +#endif // !defined(__ANDROID__) + +} // namespace __interception + + +#endif // __linux__ || __FreeBSD__ diff --git a/lib/tsan/dd/dd_interceptors.cc b/lib/tsan/dd/dd_interceptors.cc new file mode 100644 index 000000000..cd3c51aaa --- /dev/null +++ b/lib/tsan/dd/dd_interceptors.cc @@ -0,0 +1,68 @@ +//===-- dd_interceptors.cc ------------------------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "dd_rtl.h" +#include "interception/interception.h" +#include <pthread.h> + +using namespace __dsan; + +extern "C" void *__libc_malloc(uptr size); +extern "C" void __libc_free(void *ptr); + +static __thread Thread *thr; + +static void InitThread() { + if (thr != 0) + return; + thr = (Thread*)InternalAlloc(sizeof(*thr)); + internal_memset(thr, 0, sizeof(*thr)); + ThreadInit(thr); +} + +INTERCEPTOR(int, pthread_mutex_destroy, pthread_mutex_t *m) { + InitThread(); + int res = REAL(pthread_mutex_destroy)(m); + MutexDestroy(thr, (uptr)m); + return res; +} + +INTERCEPTOR(int, pthread_mutex_lock, pthread_mutex_t *m) { + InitThread(); + int res = REAL(pthread_mutex_lock)(m); + if (res == 0) + MutexLock(thr, (uptr)m, true, false); + return res; +} + +INTERCEPTOR(int, pthread_mutex_trylock, pthread_mutex_t *m) { + InitThread(); + int res = REAL(pthread_mutex_trylock)(m); + if (res == 0) + MutexLock(thr, (uptr)m, true, true); + return res; +} + +INTERCEPTOR(int, pthread_mutex_unlock, pthread_mutex_t *m) { + InitThread(); + MutexUnlock(thr, (uptr)m, true); + int res = REAL(pthread_mutex_unlock)(m); + return res; +} + +namespace __dsan { + +void InitializeInterceptors() { + INTERCEPT_FUNCTION(pthread_mutex_destroy); + INTERCEPT_FUNCTION(pthread_mutex_lock); + INTERCEPT_FUNCTION(pthread_mutex_trylock); + INTERCEPT_FUNCTION(pthread_mutex_unlock); +} + +} // namespace __dsan diff --git a/lib/tsan/dd/dd_rtl.cc b/lib/tsan/dd/dd_rtl.cc new file mode 100644 index 000000000..09ea4fceb --- /dev/null +++ b/lib/tsan/dd/dd_rtl.cc @@ -0,0 +1,128 @@ +//===-- dd_rtl.cc ---------------------------------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "dd_rtl.h" +#include "sanitizer_common/sanitizer_common.h" +#include "sanitizer_common/sanitizer_flags.h" +#include "sanitizer_common/sanitizer_stacktrace.h" +#include "sanitizer_common/sanitizer_stackdepot.h" + +namespace __dsan { + +static Context ctx0; +static Context * const ctx = &ctx0; + +void Initialize() { + InitializeInterceptors(); + //common_flags()->allow_addr2line = true; + common_flags()->symbolize = true; + ctx->dd = DDetector::Create(); +} + +void ThreadInit(Thread *thr) { + thr->dd_pt = ctx->dd->CreatePhysicalThread(); + thr->dd_lt = ctx->dd->CreateLogicalThread(0); +} + +void ThreadDestroy(Thread *thr) { + ctx->dd->DestroyPhysicalThread(thr->dd_pt); + ctx->dd->DestroyLogicalThread(thr->dd_lt); +} + +static u32 CurrentStackTrace(Thread *thr) { + StackTrace trace; + thr->in_symbolizer = true; + trace.Unwind(1000, 0, 0, 0, 0, 0, false); + thr->in_symbolizer = false; + const uptr skip = 4; + if (trace.size <= skip) + return 0; + return StackDepotPut(trace.trace + skip, trace.size - skip); +} + +static void PrintStackTrace(Thread *thr, u32 stk) { + uptr size = 0; + const uptr *trace = StackDepotGet(stk, &size); + thr->in_symbolizer = true; + StackTrace::PrintStack(trace, size); + thr->in_symbolizer = false; +} + +static Mutex *FindMutex(Thread *thr, uptr m) { + SpinMutexLock l(&ctx->mutex_mtx); + for (Mutex *mtx = ctx->mutex_list; mtx; mtx = mtx->link) { + if (mtx->addr == m) + return mtx; + } + Mutex *mtx = (Mutex*)InternalAlloc(sizeof(*mtx)); + internal_memset(mtx, 0, sizeof(*mtx)); + mtx->addr = m; + ctx->dd->MutexInit(&mtx->dd, CurrentStackTrace(thr), ctx->mutex_seq++); + mtx->link = ctx->mutex_list; + ctx->mutex_list = mtx; + return mtx; +} + +static Mutex *FindMutexAndRemove(uptr m) { + SpinMutexLock l(&ctx->mutex_mtx); + Mutex **prev = &ctx->mutex_list; + for (;;) { + Mutex *mtx = *prev; + if (mtx == 0) + return 0; + if (mtx->addr == m) { + *prev = mtx->link; + return mtx; + } + prev = &mtx->link; + } +} + +static void ReportDeadlock(Thread *thr, DDReport *rep) { + Printf("==============================\n"); + Printf("DEADLOCK\n"); + PrintStackTrace(thr, CurrentStackTrace(thr)); + for (int i = 0; i < rep->n; i++) { + Printf("Mutex %llu created at:\n", rep->loop[i].mtx_ctx0); + PrintStackTrace(thr, rep->loop[i].stk); + } + Printf("==============================\n"); +} + +void MutexLock(Thread *thr, uptr m, bool writelock, bool trylock) { + if (thr->in_symbolizer) + return; + Mutex *mtx = FindMutex(thr, m); + DDReport *rep = ctx->dd->MutexLock(thr->dd_pt, thr->dd_lt, &mtx->dd, + writelock, trylock); + if (rep) + ReportDeadlock(thr, rep); +} + +void MutexUnlock(Thread *thr, uptr m, bool writelock) { + if (thr->in_symbolizer) + return; + Mutex *mtx = FindMutex(thr, m); + ctx->dd->MutexUnlock(thr->dd_pt, thr->dd_lt, &mtx->dd, writelock); +} + +void MutexDestroy(Thread *thr, uptr m) { + if (thr->in_symbolizer) + return; + Mutex *mtx = FindMutexAndRemove(m); + if (mtx == 0) + return; + ctx->dd->MutexDestroy(thr->dd_pt, thr->dd_lt, &mtx->dd); + InternalFree(mtx); +} + +} // namespace __dsan + +__attribute__((section(".preinit_array"), used)) +void (*__local_dsan_preinit)(void) = __dsan::Initialize; diff --git a/lib/tsan/dd/dd_rtl.h b/lib/tsan/dd/dd_rtl.h new file mode 100644 index 000000000..016200900 --- /dev/null +++ b/lib/tsan/dd/dd_rtl.h @@ -0,0 +1,50 @@ +//===-- dd_rtl.h ----------------------------------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +#ifndef DD_RTL_H +#define DD_RTL_H + +#include "sanitizer_common/sanitizer_internal_defs.h" +#include "sanitizer_common/sanitizer_deadlock_detector_interface.h" +#include "sanitizer_common/sanitizer_allocator_internal.h" +#include "sanitizer_common/sanitizer_mutex.h" + +namespace __dsan { + +struct Mutex { + Mutex *link; + uptr addr; + DDMutex dd; +}; + +struct Thread { + DDPhysicalThread *dd_pt; + DDLogicalThread *dd_lt; + + bool in_symbolizer; +}; + +struct Context { + DDetector *dd; + + SpinMutex mutex_mtx; + Mutex *mutex_list; + u64 mutex_seq; +}; + +void InitializeInterceptors(); + +void ThreadInit(Thread *thr); +void ThreadDestroy(Thread *thr); + +void MutexLock(Thread *thr, uptr m, bool writelock, bool trylock); +void MutexUnlock(Thread *thr, uptr m, bool writelock); +void MutexDestroy(Thread *thr, uptr m); + +} // namespace __dsan +#endif // DD_RTL_H |