diff options
author | dim <dim@FreeBSD.org> | 2015-09-10 20:35:47 +0000 |
---|---|---|
committer | dim <dim@FreeBSD.org> | 2015-09-10 20:35:47 +0000 |
commit | 27c642b88ff253dca67f6dc0ca8ef4da0e9b7eb4 (patch) | |
tree | f8f8e6ce180ca5204b650ff6315e6e0893f9a679 /contrib/compiler-rt/lib/sanitizer_common | |
parent | 0c1fa3e6ad5b7fd602181bc094d752a27ea34568 (diff) | |
parent | 3da1400d07e473463df86668e1e50da8b02618fa (diff) | |
download | FreeBSD-src-27c642b88ff253dca67f6dc0ca8ef4da0e9b7eb4.zip FreeBSD-src-27c642b88ff253dca67f6dc0ca8ef4da0e9b7eb4.tar.gz |
Update compiler-rt to 3.7.0 release. This also includes the sanitizer
and profile libraries.
Diffstat (limited to 'contrib/compiler-rt/lib/sanitizer_common')
67 files changed, 3405 insertions, 1552 deletions
diff --git a/contrib/compiler-rt/lib/sanitizer_common/sanitizer_allocator.h b/contrib/compiler-rt/lib/sanitizer_common/sanitizer_allocator.h index b5105f8..deaffef 100644 --- a/contrib/compiler-rt/lib/sanitizer_common/sanitizer_allocator.h +++ b/contrib/compiler-rt/lib/sanitizer_common/sanitizer_allocator.h @@ -323,7 +323,7 @@ class SizeClassAllocator64 { void Init() { CHECK_EQ(kSpaceBeg, - reinterpret_cast<uptr>(Mprotect(kSpaceBeg, kSpaceSize))); + reinterpret_cast<uptr>(MmapNoAccess(kSpaceBeg, kSpaceSize))); MapWithCallback(kSpaceEnd, AdditionalSize()); } diff --git a/contrib/compiler-rt/lib/sanitizer_common/sanitizer_atomic.h b/contrib/compiler-rt/lib/sanitizer_common/sanitizer_atomic.h index 6643c54..7e3374a 100644 --- a/contrib/compiler-rt/lib/sanitizer_common/sanitizer_atomic.h +++ b/contrib/compiler-rt/lib/sanitizer_common/sanitizer_atomic.h @@ -55,7 +55,7 @@ struct atomic_uintptr_t { } // namespace __sanitizer -#if defined(__GNUC__) +#if defined(__clang__) || defined(__GNUC__) # include "sanitizer_atomic_clang.h" #elif defined(_MSC_VER) # include "sanitizer_atomic_msvc.h" diff --git a/contrib/compiler-rt/lib/sanitizer_common/sanitizer_atomic_msvc.h b/contrib/compiler-rt/lib/sanitizer_common/sanitizer_atomic_msvc.h index 12ffef3..24d6f0f 100644 --- a/contrib/compiler-rt/lib/sanitizer_common/sanitizer_atomic_msvc.h +++ b/contrib/compiler-rt/lib/sanitizer_common/sanitizer_atomic_msvc.h @@ -21,6 +21,15 @@ extern "C" void _mm_mfence(); #pragma intrinsic(_mm_mfence) extern "C" void _mm_pause(); #pragma intrinsic(_mm_pause) +extern "C" char _InterlockedExchange8( // NOLINT + char volatile *Addend, char Value); // NOLINT +#pragma intrinsic(_InterlockedExchange8) +extern "C" short _InterlockedExchange16( // NOLINT + short volatile *Addend, short Value); // NOLINT +#pragma intrinsic(_InterlockedExchange16) +extern "C" long _InterlockedExchange( // NOLINT + long volatile *Addend, long Value); // NOLINT +#pragma intrinsic(_InterlockedExchange) extern "C" long _InterlockedExchangeAdd( // NOLINT long volatile * Addend, long Value); // NOLINT #pragma intrinsic(_InterlockedExchangeAdd) @@ -145,28 +154,25 @@ INLINE u8 atomic_exchange(volatile atomic_uint8_t *a, u8 v, memory_order mo) { (void)mo; DCHECK(!((uptr)a % sizeof(*a))); - __asm { - mov eax, a - mov cl, v - xchg [eax], cl // NOLINT - mov v, cl - } - return v; + return (u8)_InterlockedExchange8((volatile char*)&a->val_dont_use, v); } INLINE u16 atomic_exchange(volatile atomic_uint16_t *a, u16 v, memory_order mo) { (void)mo; DCHECK(!((uptr)a % sizeof(*a))); - __asm { - mov eax, a - mov cx, v - xchg [eax], cx // NOLINT - mov v, cx - } - return v; + return (u16)_InterlockedExchange16((volatile short*)&a->val_dont_use, v); +} + +INLINE u32 atomic_exchange(volatile atomic_uint32_t *a, + u32 v, memory_order mo) { + (void)mo; + DCHECK(!((uptr)a % sizeof(*a))); + return (u32)_InterlockedExchange((volatile long*)&a->val_dont_use, v); } +#ifndef _WIN64 + INLINE bool atomic_compare_exchange_strong(volatile atomic_uint8_t *a, u8 *cmp, u8 xchgv, @@ -188,6 +194,8 @@ INLINE bool atomic_compare_exchange_strong(volatile atomic_uint8_t *a, return false; } +#endif + INLINE bool atomic_compare_exchange_strong(volatile atomic_uintptr_t *a, uptr *cmp, uptr xchg, diff --git a/contrib/compiler-rt/lib/sanitizer_common/sanitizer_common.cc b/contrib/compiler-rt/lib/sanitizer_common/sanitizer_common.cc index 4be3c7a..d14e988 100644 --- a/contrib/compiler-rt/lib/sanitizer_common/sanitizer_common.cc +++ b/contrib/compiler-rt/lib/sanitizer_common/sanitizer_common.cc @@ -16,6 +16,8 @@ #include "sanitizer_flags.h" #include "sanitizer_libc.h" #include "sanitizer_placement_new.h" +#include "sanitizer_stacktrace_printer.h" +#include "sanitizer_symbolizer.h" namespace __sanitizer { @@ -52,18 +54,23 @@ void ReportFile::ReopenIfNecessary() { if (fd_pid == pid) return; else - internal_close(fd); + CloseFile(fd); } - internal_snprintf(full_path, kMaxPathLength, "%s.%zu", path_prefix, pid); - uptr openrv = OpenFile(full_path, true); - if (internal_iserror(openrv)) { + const char *exe_name = GetBinaryBasename(); + if (common_flags()->log_exe_name && exe_name) { + internal_snprintf(full_path, kMaxPathLength, "%s.%s.%zu", path_prefix, + exe_name, pid); + } else { + internal_snprintf(full_path, kMaxPathLength, "%s.%zu", path_prefix, pid); + } + fd = OpenFile(full_path, WrOnly); + if (fd == kInvalidFd) { const char *ErrorMsgPrefix = "ERROR: Can't open file: "; - internal_write(kStderrFd, ErrorMsgPrefix, internal_strlen(ErrorMsgPrefix)); - internal_write(kStderrFd, full_path, internal_strlen(full_path)); + WriteToFile(kStderrFd, ErrorMsgPrefix, internal_strlen(ErrorMsgPrefix)); + WriteToFile(kStderrFd, full_path, internal_strlen(full_path)); Die(); } - fd = openrv; fd_pid = pid; } @@ -80,7 +87,7 @@ void ReportFile::SetReportPath(const char *path) { SpinMutexLock l(mu); if (fd != kStdoutFd && fd != kStderrFd && fd != kInvalidFd) - internal_close(fd); + CloseFile(fd); fd = kInvalidFd; if (internal_strcmp(path, "stdout") == 0) { fd = kStdoutFd; @@ -134,7 +141,7 @@ void NORETURN CheckFailed(const char *file, int line, const char *cond, } uptr ReadFileToBuffer(const char *file_name, char **buff, uptr *buff_size, - uptr max_len, int *errno_p) { + uptr max_len, error_t *errno_p) { uptr PageSize = GetPageSizeCached(); uptr kMinFileLen = PageSize; uptr read_len = 0; @@ -142,9 +149,8 @@ uptr ReadFileToBuffer(const char *file_name, char **buff, uptr *buff_size, *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, errno_p)) return 0; - fd_t fd = openrv; + fd_t fd = OpenFile(file_name, RdOnly, errno_p); + if (fd == kInvalidFd) return 0; UnmapOrDie(*buff, *buff_size); *buff = (char*)MmapOrDie(size, __func__); *buff_size = size; @@ -152,8 +158,8 @@ uptr ReadFileToBuffer(const char *file_name, char **buff, uptr *buff_size, read_len = 0; bool reached_eof = false; while (read_len + PageSize <= size) { - uptr just_read = internal_read(fd, *buff + read_len, PageSize); - if (internal_iserror(just_read, errno_p)) { + uptr just_read; + if (!ReadFromFile(fd, *buff + read_len, PageSize, &just_read, errno_p)) { UnmapOrDie(*buff, *buff_size); return 0; } @@ -163,7 +169,7 @@ uptr ReadFileToBuffer(const char *file_name, char **buff, uptr *buff_size, } read_len += just_read; } - internal_close(fd); + CloseFile(fd); if (reached_eof) // We've read the whole file. break; } @@ -206,19 +212,26 @@ 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; + const char *res = filepath; + if (const char *pos = internal_strstr(filepath, strip_path_prefix)) + res = pos + internal_strlen(strip_path_prefix); + if (res[0] == '.' && res[1] == '/') + res += 2; + return res; } const char *StripModuleName(const char *module) { if (module == 0) return 0; - if (const char *slash_pos = internal_strrchr(module, '/')) + if (SANITIZER_WINDOWS) { + // On Windows, both slash and backslash are possible. + // Pick the one that goes last. + if (const char *bslash_pos = internal_strrchr(module, '\\')) + return StripModuleName(bslash_pos + 1); + } + if (const char *slash_pos = internal_strrchr(module, '/')) { return slash_pos + 1; + } return module; } @@ -230,26 +243,27 @@ void ReportErrorSummary(const char *error_message) { __sanitizer_report_error_summary(buff.data()); } -void ReportErrorSummary(const char *error_type, const char *file, - int line, const char *function) { +#ifndef SANITIZER_GO +void ReportErrorSummary(const char *error_type, const AddressInfo &info) { if (!common_flags()->print_summary) return; InternalScopedString buff(kMaxSummaryLength); - buff.append("%s %s:%d %s", error_type, - file ? StripPathPrefix(file, common_flags()->strip_path_prefix) - : "??", - line, function ? function : "??"); + buff.append("%s ", error_type); + RenderFrame(&buff, "%L %F", 0, info, common_flags()->symbolize_vs_style, + common_flags()->strip_path_prefix); ReportErrorSummary(buff.data()); } +#endif -LoadedModule::LoadedModule(const char *module_name, uptr base_address) { +void LoadedModule::set(const char *module_name, uptr base_address) { + clear(); full_name_ = internal_strdup(module_name); base_address_ = base_address; - ranges_.clear(); } void LoadedModule::clear() { InternalFree(full_name_); + full_name_ = nullptr; while (!ranges_.empty()) { AddressRange *r = ranges_.front(); ranges_.pop_front(); @@ -330,6 +344,32 @@ bool TemplateMatch(const char *templ, const char *str) { return true; } +static char binary_name_cache_str[kMaxPathLength]; +static const char *binary_basename_cache_str; + +const char *GetBinaryBasename() { + return binary_basename_cache_str; +} + +// Call once to make sure that binary_name_cache_str is initialized +void CacheBinaryName() { + if (binary_name_cache_str[0] != '\0') + return; + ReadBinaryName(binary_name_cache_str, sizeof(binary_name_cache_str)); + binary_basename_cache_str = StripModuleName(binary_name_cache_str); +} + +uptr ReadBinaryNameCached(/*out*/char *buf, uptr buf_len) { + CacheBinaryName(); + uptr name_len = internal_strlen(binary_name_cache_str); + name_len = (name_len < buf_len - 1) ? name_len : buf_len - 1; + if (buf_len == 0) + return 0; + internal_memcpy(buf, binary_name_cache_str, name_len); + buf[name_len] = '\0'; + return name_len; +} + } // namespace __sanitizer using namespace __sanitizer; // NOLINT diff --git a/contrib/compiler-rt/lib/sanitizer_common/sanitizer_common.h b/contrib/compiler-rt/lib/sanitizer_common/sanitizer_common.h index ff13ef1..2c5a8db 100644 --- a/contrib/compiler-rt/lib/sanitizer_common/sanitizer_common.h +++ b/contrib/compiler-rt/lib/sanitizer_common/sanitizer_common.h @@ -23,8 +23,14 @@ #include "sanitizer_list.h" #include "sanitizer_mutex.h" +#ifdef _MSC_VER +extern "C" void _ReadWriteBarrier(); +#pragma intrinsic(_ReadWriteBarrier) +#endif + namespace __sanitizer { struct StackTrace; +struct AddressInfo; // Constants. const uptr kWordSize = SANITIZER_WORDSIZE / 8; @@ -38,8 +44,15 @@ const uptr kWordSizeInBits = 8 * kWordSize; const uptr kMaxPathLength = 4096; +// 16K loaded modules should be enough for everyone. +static const uptr kMaxNumberOfModules = 1 << 14; + const uptr kMaxThreadStackSize = 1 << 30; // 1Gb +// Denotes fake PC values that come from JIT/JAVA/etc. +// For such PC values __tsan_symbolize_external() will be called. +const u64 kExternalPCBit = 1ULL << 60; + extern const char *SanitizerToolName; // Can be changed by the tool. extern atomic_uint32_t current_verbosity; @@ -65,12 +78,17 @@ void GetThreadStackAndTls(bool main, uptr *stk_addr, uptr *stk_size, // Memory management void *MmapOrDie(uptr size, const char *mem_type); void UnmapOrDie(void *addr, uptr size); -void *MmapFixedNoReserve(uptr fixed_addr, uptr size); +void *MmapFixedNoReserve(uptr fixed_addr, uptr size, + const char *name = nullptr); void *MmapNoReserveOrDie(uptr size, const char *mem_type); void *MmapFixedOrDie(uptr fixed_addr, uptr size); -void *Mprotect(uptr fixed_addr, uptr size); +void *MmapNoAccess(uptr fixed_addr, uptr size, const char *name = nullptr); // Map aligned chunk of address space; size and alignment are powers of two. void *MmapAlignedOrDie(uptr size, uptr alignment, const char *mem_type); +// Disallow access to a memory range. Use MmapNoAccess to allocate an +// unaccessible memory. +bool MprotectNoAccess(uptr addr, uptr size); + // Used to check if we can map shadow memory to a fixed location. bool MemoryRangeIsAvailable(uptr range_start, uptr range_end); void FlushUnneededShadowMemory(uptr addr, uptr size); @@ -159,7 +177,7 @@ extern StaticSpinMutex CommonSanitizerReportMutex; struct ReportFile { void Write(const char *buffer, uptr length); - bool PrintsToTty(); + bool SupportsColors(); void SetReportPath(const char *path); // Don't use fields directly. They are only declared public to allow @@ -186,18 +204,39 @@ extern ReportFile report_file; extern uptr stoptheworld_tracer_pid; extern uptr stoptheworld_tracer_ppid; -uptr OpenFile(const char *filename, bool write); +enum FileAccessMode { + RdOnly, + WrOnly, + RdWr +}; + +// Returns kInvalidFd on error. +fd_t OpenFile(const char *filename, FileAccessMode mode, + error_t *errno_p = nullptr); +void CloseFile(fd_t); + +// Return true on success, false on error. +bool ReadFromFile(fd_t fd, void *buff, uptr buff_size, + uptr *bytes_read = nullptr, error_t *error_p = nullptr); +bool WriteToFile(fd_t fd, const void *buff, uptr buff_size, + uptr *bytes_written = nullptr, error_t *error_p = nullptr); + +bool RenameFile(const char *oldpath, const char *newpath, + error_t *error_p = nullptr); + +bool SupportsColoredOutput(fd_t fd); + // Opens the file 'file_name" and reads up to 'max_len' bytes. // The resulting buffer is mmaped and stored in '*buff'. // The size of the mmaped region is stored in '*buff_size', // Returns the number of read bytes or 0 if file can not be opened. uptr ReadFileToBuffer(const char *file_name, char **buff, uptr *buff_size, - uptr max_len, int *errno_p = nullptr); + uptr max_len, error_t *errno_p = nullptr); // Maps given file to virtual memory, and returns pointer to it -// (or NULL if the mapping failes). Stores the size of mmaped region +// (or NULL if mapping fails). Stores the size of mmaped region // in '*buff_size'. void *MapFileToMemory(const char *file_name, uptr *buff_size); -void *MapWritableFileToMemory(void *addr, uptr size, uptr fd, uptr offset); +void *MapWritableFileToMemory(void *addr, uptr size, fd_t fd, OFF_T offset); bool IsAccessibleMemoryRange(uptr beg, uptr size); @@ -208,6 +247,10 @@ const char *StripPathPrefix(const char *filepath, const char *StripModuleName(const char *module); // OS +uptr ReadBinaryName(/*out*/char *buf, uptr buf_len); +uptr ReadBinaryNameCached(/*out*/char *buf, uptr buf_len); +const char *GetBinaryBasename(); +void CacheBinaryName(); void DisableCoreDumperIfNecessary(); void DumpProcessMap(); bool FileExists(const char *filename); @@ -215,6 +258,9 @@ const char *GetEnv(const char *name); bool SetEnv(const char *name, const char *value); const char *GetPwd(); char *FindPathToBinary(const char *name); +bool IsPathSeparator(const char c); +bool IsAbsolutePath(const char *path); + u32 GetUid(); void ReExec(); bool StackSizeIsUnlimited(); @@ -288,9 +334,9 @@ const int kMaxSummaryLength = 1024; // and pass it to __sanitizer_report_error_summary. void ReportErrorSummary(const char *error_message); // Same as above, but construct error_message as: -// error_type file:line function -void ReportErrorSummary(const char *error_type, const char *file, - int line, const char *function); +// error_type file:line[:column][ function] +void ReportErrorSummary(const char *error_type, const AddressInfo &info); +// Same as above, but obtains AddressInfo by symbolizing top stack trace frame. void ReportErrorSummary(const char *error_type, StackTrace *trace); // Math @@ -309,7 +355,11 @@ INLINE uptr MostSignificantSetBitIndex(uptr x) { CHECK_NE(x, 0U); unsigned long up; // NOLINT #if !SANITIZER_WINDOWS || defined(__clang__) || defined(__GNUC__) +# ifdef _WIN64 + up = SANITIZER_WORDSIZE - 1 - __builtin_clzll(x); +# else up = SANITIZER_WORDSIZE - 1 - __builtin_clzl(x); +# endif #elif defined(_WIN64) _BitScanReverse64(&up, x); #else @@ -322,7 +372,11 @@ INLINE uptr LeastSignificantSetBitIndex(uptr x) { CHECK_NE(x, 0U); unsigned long up; // NOLINT #if !SANITIZER_WINDOWS || defined(__clang__) || defined(__GNUC__) +# ifdef _WIN64 + up = __builtin_ctzll(x); +# else up = __builtin_ctzl(x); +# endif #elif defined(_WIN64) _BitScanForward64(&up, x); #else @@ -342,7 +396,7 @@ INLINE uptr RoundUpToPowerOfTwo(uptr size) { uptr up = MostSignificantSetBitIndex(size); CHECK(size < (1ULL << (up + 1))); CHECK(size > (1ULL << up)); - return 1UL << (up + 1); + return 1ULL << (up + 1); } INLINE uptr RoundUpTo(uptr size, uptr boundary) { @@ -360,17 +414,7 @@ INLINE bool IsAligned(uptr a, uptr alignment) { INLINE uptr Log2(uptr x) { CHECK(IsPowerOfTwo(x)); -#if !SANITIZER_WINDOWS || defined(__clang__) || defined(__GNUC__) - return __builtin_ctzl(x); -#elif defined(_WIN64) - unsigned long ret; // NOLINT - _BitScanForward64(&ret, x); - return ret; -#else - unsigned long ret; // NOLINT - _BitScanForward(&ret, x); - return ret; -#endif + return LeastSignificantSetBitIndex(x); } // Don't use std::min, std::max or std::swap, to minimize dependency @@ -439,11 +483,15 @@ class InternalMmapVectorNoCtor { const T *data() const { return data_; } + T *data() { + return data_; + } uptr capacity() const { return capacity_; } void clear() { size_ = 0; } + bool empty() const { return size() == 0; } private: void Resize(uptr new_capacity) { @@ -532,7 +580,8 @@ uptr InternalBinarySearch(const Container &v, uptr first, uptr last, // executable or a shared object). class LoadedModule { public: - LoadedModule(const char *module_name, uptr base_address); + LoadedModule() : full_name_(nullptr), base_address_(0) { ranges_.clear(); } + void set(const char *module_name, uptr base_address); void clear(); void addAddressRange(uptr beg, uptr end, bool executable); bool containsAddress(uptr address) const; @@ -567,28 +616,41 @@ typedef bool (*string_predicate_t)(const char *); uptr GetListOfModules(LoadedModule *modules, uptr max_modules, string_predicate_t filter); -#if SANITIZER_POSIX -const uptr kPthreadDestructorIterations = 4; -#else -// Unused on Windows. -const uptr kPthreadDestructorIterations = 0; -#endif - // Callback type for iterating over a set of memory ranges. typedef void (*RangeIteratorCallback)(uptr begin, uptr end, void *arg); +enum AndroidApiLevel { + ANDROID_NOT_ANDROID = 0, + ANDROID_KITKAT = 19, + ANDROID_LOLLIPOP_MR1 = 22, + ANDROID_POST_LOLLIPOP = 23 +}; + #if SANITIZER_ANDROID // Initialize Android logging. Any writes before this are silently lost. void AndroidLogInit(); void AndroidLogWrite(const char *buffer); void GetExtraActivationFlags(char *buf, uptr size); void SanitizerInitializeUnwinder(); +AndroidApiLevel AndroidGetApiLevel(); #else INLINE void AndroidLogInit() {} INLINE void AndroidLogWrite(const char *buffer_unused) {} INLINE void GetExtraActivationFlags(char *buf, uptr size) { *buf = '\0'; } INLINE void SanitizerInitializeUnwinder() {} +INLINE AndroidApiLevel AndroidGetApiLevel() { return ANDROID_NOT_ANDROID; } +#endif + +INLINE uptr GetPthreadDestructorIterations() { +#if SANITIZER_ANDROID + return (AndroidGetApiLevel() == ANDROID_LOLLIPOP_MR1) ? 8 : 4; +#elif SANITIZER_POSIX + return 4; +#else +// Unused on Windows. + return 0; #endif +} void *internal_start_thread(void(*func)(void*), void *arg); void internal_join_thread(void *th); @@ -599,14 +661,30 @@ void MaybeStartBackgroudThread(); // compiler from recognising it and turning it into an actual call to // memset/memcpy/etc. static inline void SanitizerBreakOptimization(void *arg) { -#if _MSC_VER - // FIXME: make sure this is actually enough. - __asm; +#if _MSC_VER && !defined(__clang__) + _ReadWriteBarrier(); #else __asm__ __volatile__("" : : "r" (arg) : "memory"); #endif } +struct SignalContext { + void *context; + uptr addr; + uptr pc; + uptr sp; + uptr bp; + + SignalContext(void *context, uptr addr, uptr pc, uptr sp, uptr bp) : + context(context), addr(addr), pc(pc), sp(sp), bp(bp) { + } + + // Creates signal context in a platform-specific manner. + static SignalContext Create(void *siginfo, void *context); +}; + +void GetPcSpBp(void *context, uptr *pc, uptr *sp, uptr *bp); + } // namespace __sanitizer inline void *operator new(__sanitizer::operator_new_size_type size, diff --git a/contrib/compiler-rt/lib/sanitizer_common/sanitizer_common_interceptors.inc b/contrib/compiler-rt/lib/sanitizer_common/sanitizer_common_interceptors.inc index 87c33e1..a7772b7 100644 --- a/contrib/compiler-rt/lib/sanitizer_common/sanitizer_common_interceptors.inc +++ b/contrib/compiler-rt/lib/sanitizer_common/sanitizer_common_interceptors.inc @@ -22,6 +22,7 @@ // COMMON_INTERCEPTOR_FD_RELEASE // COMMON_INTERCEPTOR_FD_ACCESS // COMMON_INTERCEPTOR_SET_THREAD_NAME +// COMMON_INTERCEPTOR_ON_DLOPEN // COMMON_INTERCEPTOR_ON_EXIT // COMMON_INTERCEPTOR_MUTEX_LOCK // COMMON_INTERCEPTOR_MUTEX_UNLOCK @@ -46,6 +47,7 @@ #define pthread_setname_np pthread_set_name_np #define inet_aton __inet_aton #define inet_pton __inet_pton +#define iconv __bsd_iconv #endif #ifndef COMMON_INTERCEPTOR_INITIALIZE_RANGE @@ -101,6 +103,21 @@ #define COMMON_INTERCEPTOR_NOTHING_IS_INITIALIZED (0) #endif +#define COMMON_INTERCEPTOR_READ_STRING_OF_LEN(ctx, s, len, n) \ + COMMON_INTERCEPTOR_READ_RANGE((ctx), (s), \ + common_flags()->strict_string_checks ? (len) + 1 : (n) ) + +#define COMMON_INTERCEPTOR_READ_STRING(ctx, s, n) \ + COMMON_INTERCEPTOR_READ_STRING_OF_LEN((ctx), (s), REAL(strlen)(s), (n)) + +#ifndef COMMON_INTERCEPTOR_ON_DLOPEN +#define COMMON_INTERCEPTOR_ON_DLOPEN(filename, flag) {} +#endif + +#ifndef COMMON_INTERCEPTOR_GET_TLS_RANGE +#define COMMON_INTERCEPTOR_GET_TLS_RANGE(begin, end) *begin = *end = 0; +#endif + struct FileMetadata { // For open_memstream(). char **addr; @@ -154,7 +171,8 @@ UNUSED static void DeleteInterceptorMetadata(void *addr) { INTERCEPTOR(char*, textdomain, const char *domainname) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, textdomain, domainname); - char* domain = REAL(textdomain)(domainname); + COMMON_INTERCEPTOR_READ_STRING(ctx, domainname, 0); + char *domain = REAL(textdomain)(domainname); if (domain) { COMMON_INTERCEPTOR_INITIALIZE_RANGE(domain, REAL(strlen)(domain) + 1); } @@ -180,8 +198,8 @@ INTERCEPTOR(int, strcmp, const char *s1, const char *s2) { c2 = (unsigned char)s2[i]; if (c1 != c2 || c1 == '\0') break; } - COMMON_INTERCEPTOR_READ_RANGE(ctx, s1, i + 1); - COMMON_INTERCEPTOR_READ_RANGE(ctx, s2, i + 1); + COMMON_INTERCEPTOR_READ_STRING(ctx, s1, i + 1); + COMMON_INTERCEPTOR_READ_STRING(ctx, s2, i + 1); return CharCmpX(c1, c2); } @@ -226,8 +244,8 @@ INTERCEPTOR(int, strcasecmp, const char *s1, const char *s2) { c2 = (unsigned char)s2[i]; if (CharCaseCmp(c1, c2) != 0 || c1 == '\0') break; } - COMMON_INTERCEPTOR_READ_RANGE(ctx, s1, i + 1); - COMMON_INTERCEPTOR_READ_RANGE(ctx, s2, i + 1); + COMMON_INTERCEPTOR_READ_STRING(ctx, s1, i + 1); + COMMON_INTERCEPTOR_READ_STRING(ctx, s2, i + 1); return CharCaseCmp(c1, c2); } @@ -253,6 +271,97 @@ INTERCEPTOR(int, strncasecmp, const char *s1, const char *s2, SIZE_T n) { #define INIT_STRNCASECMP #endif +#if SANITIZER_INTERCEPT_STRSTR || SANITIZER_INTERCEPT_STRCASESTR +static inline void StrstrCheck(void *ctx, char *r, const char *s1, + const char *s2) { + uptr len1 = REAL(strlen)(s1); + uptr len2 = REAL(strlen)(s2); + COMMON_INTERCEPTOR_READ_STRING_OF_LEN(ctx, s1, len1, + r ? r - s1 + len2 : len1 + 1); + COMMON_INTERCEPTOR_READ_RANGE(ctx, s2, len2 + 1); +} +#endif + +#if SANITIZER_INTERCEPT_STRSTR +INTERCEPTOR(char*, strstr, const char *s1, const char *s2) { + if (COMMON_INTERCEPTOR_NOTHING_IS_INITIALIZED) + return internal_strstr(s1, s2); + void *ctx; + COMMON_INTERCEPTOR_ENTER(ctx, strstr, s1, s2); + char *r = REAL(strstr)(s1, s2); + if (common_flags()->intercept_strstr) + StrstrCheck(ctx, r, s1, s2); + return r; +} + +#define INIT_STRSTR COMMON_INTERCEPT_FUNCTION(strstr); +#else +#define INIT_STRSTR +#endif + +#if SANITIZER_INTERCEPT_STRCASESTR +INTERCEPTOR(char*, strcasestr, const char *s1, const char *s2) { + void *ctx; + COMMON_INTERCEPTOR_ENTER(ctx, strcasestr, s1, s2); + char *r = REAL(strcasestr)(s1, s2); + if (common_flags()->intercept_strstr) + StrstrCheck(ctx, r, s1, s2); + return r; +} + +#define INIT_STRCASESTR COMMON_INTERCEPT_FUNCTION(strcasestr); +#else +#define INIT_STRCASESTR +#endif + +#if SANITIZER_INTERCEPT_STRSPN +INTERCEPTOR(SIZE_T, strspn, const char *s1, const char *s2) { + void *ctx; + COMMON_INTERCEPTOR_ENTER(ctx, strspn, s1, s2); + SIZE_T r = REAL(strspn)(s1, s2); + if (common_flags()->intercept_strspn) { + COMMON_INTERCEPTOR_READ_RANGE(ctx, s2, REAL(strlen)(s2) + 1); + COMMON_INTERCEPTOR_READ_STRING(ctx, s1, r + 1); + } + return r; +} + +INTERCEPTOR(SIZE_T, strcspn, const char *s1, const char *s2) { + void *ctx; + COMMON_INTERCEPTOR_ENTER(ctx, strcspn, s1, s2); + SIZE_T r = REAL(strcspn)(s1, s2); + if (common_flags()->intercept_strspn) { + COMMON_INTERCEPTOR_READ_RANGE(ctx, s2, REAL(strlen)(s2) + 1); + COMMON_INTERCEPTOR_READ_STRING(ctx, s1, r + 1); + } + return r; +} + +#define INIT_STRSPN \ + COMMON_INTERCEPT_FUNCTION(strspn); \ + COMMON_INTERCEPT_FUNCTION(strcspn); +#else +#define INIT_STRSPN +#endif + +#if SANITIZER_INTERCEPT_STRPBRK +INTERCEPTOR(char *, strpbrk, const char *s1, const char *s2) { + void *ctx; + COMMON_INTERCEPTOR_ENTER(ctx, strpbrk, s1, s2); + char *r = REAL(strpbrk)(s1, s2); + if (common_flags()->intercept_strpbrk) { + COMMON_INTERCEPTOR_READ_RANGE(ctx, s2, REAL(strlen)(s2) + 1); + COMMON_INTERCEPTOR_READ_STRING(ctx, s1, + r ? r - s1 + 1 : REAL(strlen)(s1) + 1); + } + return r; +} + +#define INIT_STRPBRK COMMON_INTERCEPT_FUNCTION(strpbrk); +#else +#define INIT_STRPBRK +#endif + #if SANITIZER_INTERCEPT_MEMCHR INTERCEPTOR(void*, memchr, const void *s, int c, SIZE_T n) { void *ctx; @@ -722,12 +831,12 @@ INTERCEPTOR(char *, strptime, char *s, char *format, __sanitizer_tm *tm) { // its metadata. See // https://code.google.com/p/address-sanitizer/issues/detail?id=321. char *res = REAL(strptime)(s, format, tm); - if (res) { - COMMON_INTERCEPTOR_READ_RANGE(ctx, s, res - s); + COMMON_INTERCEPTOR_READ_STRING(ctx, s, res ? res - s : 0); + if (res && tm) { // Do not call unpoison_tm here, because strptime does not, in fact, // initialize the entire struct tm. For example, tm_zone pointer is left // uninitialized. - if (tm) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, tm, sizeof(*tm)); + COMMON_INTERCEPTOR_WRITE_RANGE(ctx, tm, sizeof(*tm)); } return res; } @@ -1029,6 +1138,12 @@ FORMAT_INTERCEPTOR_IMPL(__isoc99_snprintf, __isoc99_vsnprintf, str, size, #if SANITIZER_INTERCEPT_IOCTL #include "sanitizer_common_interceptors_ioctl.inc" INTERCEPTOR(int, ioctl, int d, unsigned long request, ...) { + // We need a frame pointer, because we call into ioctl_common_[pre|post] which + // can trigger a report and we need to be able to unwind through this + // function. On Mac in debug mode we might not have a frame pointer, because + // ioctl_common_[pre|post] doesn't get inlined here. + ENABLE_FRAME_POINTER; + void *ctx; va_list ap; va_start(ap, request); @@ -1502,6 +1617,7 @@ INTERCEPTOR(int, glob, const char *pattern, int flags, __sanitizer_glob_t *pglob) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, glob, pattern, flags, errfunc, pglob); + COMMON_INTERCEPTOR_READ_STRING(ctx, pattern, 0); __sanitizer_glob_t glob_copy = { 0, 0, 0, 0, wrapped_gl_closedir, wrapped_gl_readdir, @@ -1532,6 +1648,7 @@ INTERCEPTOR(int, glob64, const char *pattern, int flags, __sanitizer_glob_t *pglob) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, glob64, pattern, flags, errfunc, pglob); + COMMON_INTERCEPTOR_READ_STRING(ctx, pattern, 0); __sanitizer_glob_t glob_copy = { 0, 0, 0, 0, wrapped_gl_closedir, wrapped_gl_readdir, @@ -1678,6 +1795,7 @@ INTERCEPTOR(char *, inet_ntop, int af, const void *src, char *dst, u32 size) { INTERCEPTOR(int, inet_pton, int af, const char *src, void *dst) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, inet_pton, af, src, dst); + COMMON_INTERCEPTOR_READ_STRING(ctx, src, 0); // FIXME: figure out read size based on the address family. // FIXME: under ASan the call below may write to freed memory and corrupt // its metadata. See @@ -2348,6 +2466,37 @@ INTERCEPTOR(char *, get_current_dir_name, int fake) { #define INIT_GET_CURRENT_DIR_NAME #endif +UNUSED static inline void FixRealStrtolEndptr(const char *nptr, char **endptr) { + CHECK(endptr); + if (nptr == *endptr) { + // No digits were found at strtol call, we need to find out the last + // symbol accessed by strtoll on our own. + // We get this symbol by skipping leading blanks and optional +/- sign. + while (IsSpace(*nptr)) nptr++; + if (*nptr == '+' || *nptr == '-') nptr++; + *endptr = const_cast<char *>(nptr); + } + CHECK(*endptr >= nptr); +} + +UNUSED static inline void StrtolFixAndCheck(void *ctx, const char *nptr, + char **endptr, char *real_endptr, int base) { + if (endptr != 0) { + *endptr = real_endptr; + COMMON_INTERCEPTOR_WRITE_RANGE(ctx, endptr, sizeof(*endptr)); + } + // If base has unsupported value, strtol can exit with EINVAL + // without reading any characters. So do additional checks only + // if base is valid. + bool is_valid_base = (base == 0) || (2 <= base && base <= 36); + if (is_valid_base) { + FixRealStrtolEndptr(nptr, &real_endptr); + } + COMMON_INTERCEPTOR_READ_STRING(ctx, nptr, is_valid_base ? + (real_endptr - nptr) + 1 : 0); +} + + #if SANITIZER_INTERCEPT_STRTOIMAX INTERCEPTOR(INTMAX_T, strtoimax, const char *nptr, char **endptr, int base) { void *ctx; @@ -2355,8 +2504,9 @@ INTERCEPTOR(INTMAX_T, strtoimax, const char *nptr, char **endptr, int base) { // FIXME: under ASan the call below may write to freed memory and corrupt // its metadata. See // https://code.google.com/p/address-sanitizer/issues/detail?id=321. - INTMAX_T res = REAL(strtoimax)(nptr, endptr, base); - if (endptr) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, endptr, sizeof(*endptr)); + char *real_endptr; + INTMAX_T res = REAL(strtoimax)(nptr, &real_endptr, base); + StrtolFixAndCheck(ctx, nptr, endptr, real_endptr, base); return res; } @@ -2366,8 +2516,9 @@ INTERCEPTOR(INTMAX_T, strtoumax, const char *nptr, char **endptr, int base) { // FIXME: under ASan the call below may write to freed memory and corrupt // its metadata. See // https://code.google.com/p/address-sanitizer/issues/detail?id=321. - INTMAX_T res = REAL(strtoumax)(nptr, endptr, base); - if (endptr) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, endptr, sizeof(*endptr)); + char *real_endptr; + INTMAX_T res = REAL(strtoumax)(nptr, &real_endptr, base); + StrtolFixAndCheck(ctx, nptr, endptr, real_endptr, base); return res; } @@ -3631,6 +3782,7 @@ INTERCEPTOR(char *, tempnam, char *dir, char *pfx) { INTERCEPTOR(int, pthread_setname_np, uptr thread, const char *name) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, pthread_setname_np, thread, name); + COMMON_INTERCEPTOR_READ_STRING(ctx, name, 0); COMMON_INTERCEPTOR_SET_PTHREAD_NAME(ctx, thread, name); return REAL(pthread_setname_np)(thread, name); } @@ -3847,25 +3999,33 @@ INTERCEPTOR(SSIZE_T, getline, char **lineptr, SIZE_T *n, void *stream) { } return res; } -INTERCEPTOR(SSIZE_T, __getdelim, char **lineptr, SIZE_T *n, int delim, - void *stream) { - void *ctx; - COMMON_INTERCEPTOR_ENTER(ctx, __getdelim, lineptr, n, delim, stream); - // FIXME: under ASan the call below may write to freed memory and corrupt - // its metadata. See - // https://code.google.com/p/address-sanitizer/issues/detail?id=321. - SSIZE_T res = REAL(__getdelim)(lineptr, n, delim, stream); - if (res > 0) { - COMMON_INTERCEPTOR_WRITE_RANGE(ctx, lineptr, sizeof(*lineptr)); - COMMON_INTERCEPTOR_WRITE_RANGE(ctx, n, sizeof(*n)); - COMMON_INTERCEPTOR_WRITE_RANGE(ctx, *lineptr, res + 1); + +// FIXME: under ASan the call below may write to freed memory and corrupt its +// metadata. See +// https://code.google.com/p/address-sanitizer/issues/detail?id=321. +#define GETDELIM_INTERCEPTOR_IMPL(vname) \ + { \ + void *ctx; \ + COMMON_INTERCEPTOR_ENTER(ctx, vname, lineptr, n, delim, stream); \ + SSIZE_T res = REAL(vname)(lineptr, n, delim, stream); \ + if (res > 0) { \ + COMMON_INTERCEPTOR_WRITE_RANGE(ctx, lineptr, sizeof(*lineptr)); \ + COMMON_INTERCEPTOR_WRITE_RANGE(ctx, n, sizeof(*n)); \ + COMMON_INTERCEPTOR_WRITE_RANGE(ctx, *lineptr, res + 1); \ + } \ + return res; \ } - return res; -} + +INTERCEPTOR(SSIZE_T, __getdelim, char **lineptr, SIZE_T *n, int delim, + void *stream) +GETDELIM_INTERCEPTOR_IMPL(__getdelim) + +// There's no __getdelim() on FreeBSD so we supply the getdelim() interceptor +// with its own body. INTERCEPTOR(SSIZE_T, getdelim, char **lineptr, SIZE_T *n, int delim, - void *stream) { - return __getdelim(lineptr, n, delim, stream); -} + void *stream) +GETDELIM_INTERCEPTOR_IMPL(getdelim) + #define INIT_GETLINE \ COMMON_INTERCEPT_FUNCTION(getline); \ COMMON_INTERCEPT_FUNCTION(__getdelim); \ @@ -3931,7 +4091,9 @@ INTERCEPTOR(void *, __tls_get_addr, void *arg) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, __tls_get_addr, arg); void *res = REAL(__tls_get_addr)(arg); - DTLS::DTV *dtv = DTLS_on_tls_get_addr(arg, res); + uptr tls_begin, tls_end; + COMMON_INTERCEPTOR_GET_TLS_RANGE(&tls_begin, &tls_end); + DTLS::DTV *dtv = DTLS_on_tls_get_addr(arg, res, tls_begin, tls_end); if (dtv) { // New DTLS block has been allocated. COMMON_INTERCEPTOR_INITIALIZE_RANGE((void *)dtv->beg, dtv->size); @@ -4688,6 +4850,8 @@ INTERCEPTOR(int, fclose, __sanitizer_FILE *fp) { INTERCEPTOR(void*, dlopen, const char *filename, int flag) { void *ctx; COMMON_INTERCEPTOR_ENTER_NOIGNORE(ctx, dlopen, filename, flag); + if (filename) COMMON_INTERCEPTOR_READ_STRING(ctx, filename, 0); + COMMON_INTERCEPTOR_ON_DLOPEN(filename, flag); void *res = REAL(dlopen)(filename, flag); COMMON_INTERCEPTOR_LIBRARY_LOADED(filename, res); return res; @@ -4792,6 +4956,63 @@ INTERCEPTOR(int, munlockall, void) { #define INIT_MLOCKX #endif // SANITIZER_INTERCEPT_MLOCKX +#if SANITIZER_INTERCEPT_FOPENCOOKIE +struct WrappedCookie { + void *real_cookie; + __sanitizer_cookie_io_functions_t real_io_funcs; +}; + +static uptr wrapped_read(void *cookie, char *buf, uptr size) { + COMMON_INTERCEPTOR_UNPOISON_PARAM(3); + WrappedCookie *wrapped_cookie = (WrappedCookie *)cookie; + __sanitizer_cookie_io_read real_read = wrapped_cookie->real_io_funcs.read; + return real_read ? real_read(wrapped_cookie->real_cookie, buf, size) : 0; +} + +static uptr wrapped_write(void *cookie, const char *buf, uptr size) { + COMMON_INTERCEPTOR_UNPOISON_PARAM(3); + WrappedCookie *wrapped_cookie = (WrappedCookie *)cookie; + __sanitizer_cookie_io_write real_write = wrapped_cookie->real_io_funcs.write; + return real_write ? real_write(wrapped_cookie->real_cookie, buf, size) : size; +} + +static int wrapped_seek(void *cookie, u64 *offset, int whence) { + COMMON_INTERCEPTOR_UNPOISON_PARAM(3); + COMMON_INTERCEPTOR_INITIALIZE_RANGE(offset, sizeof(*offset)); + WrappedCookie *wrapped_cookie = (WrappedCookie *)cookie; + __sanitizer_cookie_io_seek real_seek = wrapped_cookie->real_io_funcs.seek; + return real_seek ? real_seek(wrapped_cookie->real_cookie, offset, whence) + : -1; +} + +static int wrapped_close(void *cookie) { + COMMON_INTERCEPTOR_UNPOISON_PARAM(1); + WrappedCookie *wrapped_cookie = (WrappedCookie *)cookie; + __sanitizer_cookie_io_close real_close = wrapped_cookie->real_io_funcs.close; + int res = real_close ? real_close(wrapped_cookie->real_cookie) : 0; + InternalFree(wrapped_cookie); + return res; +} + +INTERCEPTOR(__sanitizer_FILE *, fopencookie, void *cookie, const char *mode, + __sanitizer_cookie_io_functions_t io_funcs) { + void *ctx; + COMMON_INTERCEPTOR_ENTER(ctx, fopencookie, cookie, mode, io_funcs); + WrappedCookie *wrapped_cookie = + (WrappedCookie *)InternalAlloc(sizeof(WrappedCookie)); + wrapped_cookie->real_cookie = cookie; + wrapped_cookie->real_io_funcs = io_funcs; + __sanitizer_FILE *res = + REAL(fopencookie)(wrapped_cookie, mode, {wrapped_read, wrapped_write, + wrapped_seek, wrapped_close}); + return res; +} + +#define INIT_FOPENCOOKIE COMMON_INTERCEPT_FUNCTION(fopencookie); +#else +#define INIT_FOPENCOOKIE +#endif // SANITIZER_INTERCEPT_FOPENCOOKIE + static void InitializeCommonInterceptors() { static u64 metadata_mem[sizeof(MetadataHashMap) / sizeof(u64) + 1]; interceptor_metadata_map = new((void *)&metadata_mem) MetadataHashMap(); @@ -4801,6 +5022,10 @@ static void InitializeCommonInterceptors() { INIT_STRNCMP; INIT_STRCASECMP; INIT_STRNCASECMP; + INIT_STRSTR; + INIT_STRCASESTR; + INIT_STRSPN; + INIT_STRPBRK; INIT_MEMCHR; INIT_MEMRCHR; INIT_READ; @@ -4955,4 +5180,5 @@ static void InitializeCommonInterceptors() { INIT_GETPASS; INIT_TIMERFD; INIT_MLOCKX; + INIT_FOPENCOOKIE; } diff --git a/contrib/compiler-rt/lib/sanitizer_common/sanitizer_common_interceptors_ioctl.inc b/contrib/compiler-rt/lib/sanitizer_common/sanitizer_common_interceptors_ioctl.inc index 69b7ca9..b94c21c 100755 --- a/contrib/compiler-rt/lib/sanitizer_common/sanitizer_common_interceptors_ioctl.inc +++ b/contrib/compiler-rt/lib/sanitizer_common/sanitizer_common_interceptors_ioctl.inc @@ -578,14 +578,10 @@ static void ioctl_common_pre(void *ctx, const ioctl_desc *desc, int d, } if (desc->type != ioctl_desc::CUSTOM) return; - switch (request) { - case 0x00008912: { // SIOCGIFCONF - struct __sanitizer_ifconf *ifc = (__sanitizer_ifconf *)arg; - COMMON_INTERCEPTOR_READ_RANGE(ctx, &ifc->ifc_len, sizeof(ifc->ifc_len)); - break; - } + if (request == IOCTL_SIOCGIFCONF) { + struct __sanitizer_ifconf *ifc = (__sanitizer_ifconf *)arg; + COMMON_INTERCEPTOR_READ_RANGE(ctx, &ifc->ifc_len, sizeof(ifc->ifc_len)); } - return; } static void ioctl_common_post(void *ctx, const ioctl_desc *desc, int res, int d, @@ -597,12 +593,8 @@ static void ioctl_common_post(void *ctx, const ioctl_desc *desc, int res, int d, } if (desc->type != ioctl_desc::CUSTOM) return; - switch (request) { - case 0x00008912: { // SIOCGIFCONF - struct __sanitizer_ifconf *ifc = (__sanitizer_ifconf *)arg; - COMMON_INTERCEPTOR_WRITE_RANGE(ctx, ifc->ifc_ifcu.ifcu_req, ifc->ifc_len); - break; - } + if (request == IOCTL_SIOCGIFCONF) { + struct __sanitizer_ifconf *ifc = (__sanitizer_ifconf *)arg; + COMMON_INTERCEPTOR_WRITE_RANGE(ctx, ifc->ifc_ifcu.ifcu_req, ifc->ifc_len); } - return; } diff --git a/contrib/compiler-rt/lib/sanitizer_common/sanitizer_common_libcdep.cc b/contrib/compiler-rt/lib/sanitizer_common/sanitizer_common_libcdep.cc index 17ef689..1b65bce 100644 --- a/contrib/compiler-rt/lib/sanitizer_common/sanitizer_common_libcdep.cc +++ b/contrib/compiler-rt/lib/sanitizer_common/sanitizer_common_libcdep.cc @@ -17,12 +17,16 @@ #include "sanitizer_stacktrace.h" #include "sanitizer_symbolizer.h" +#if SANITIZER_POSIX +#include "sanitizer_posix.h" +#endif + namespace __sanitizer { -bool ReportFile::PrintsToTty() { +bool ReportFile::SupportsColors() { SpinMutexLock l(mu); ReopenIfNecessary(); - return internal_isatty(fd) != 0; + return SupportsColoredOutput(fd); } bool ColorizeReports() { @@ -33,7 +37,7 @@ bool ColorizeReports() { const char *flag = common_flags()->color; return internal_strcmp(flag, "always") == 0 || - (internal_strcmp(flag, "auto") == 0 && report_file.PrintsToTty()); + (internal_strcmp(flag, "auto") == 0 && report_file.SupportsColors()); } static void (*sandboxing_callback)(); @@ -44,20 +48,16 @@ void SetSandboxingCallback(void (*f)()) { void ReportErrorSummary(const char *error_type, StackTrace *stack) { if (!common_flags()->print_summary) return; -#if !SANITIZER_GO - if (stack->size > 0 && Symbolizer::GetOrInit()->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]); - SymbolizedStack *frame = Symbolizer::GetOrInit()->SymbolizePC(pc); - const AddressInfo &ai = frame->info; - ReportErrorSummary(error_type, ai.file, ai.line, ai.function); - frame->ClearAll(); + if (stack->size == 0) { + ReportErrorSummary(error_type); + return; } -#else - AddressInfo ai; - ReportErrorSummary(error_type, ai.file, ai.line, ai.function); -#endif + // 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]); + SymbolizedStack *frame = Symbolizer::GetOrInit()->SymbolizePC(pc); + ReportErrorSummary(error_type, frame->info); + frame->ClearAll(); } static void (*SoftRssLimitExceededCallback)(bool exceeded); @@ -117,12 +117,13 @@ void BackgroundThread(void *arg) { } void MaybeStartBackgroudThread() { - if (!SANITIZER_LINUX) return; // Need to implement/test on other platforms. +#if SANITIZER_LINUX // Need to implement/test on other platforms. // Start the background thread if one of the rss limits is given. if (!common_flags()->hard_rss_limit_mb && !common_flags()->soft_rss_limit_mb) return; if (!&real_pthread_create) return; // Can't spawn the thread anyway. internal_start_thread(BackgroundThread, nullptr); +#endif } } // namespace __sanitizer diff --git a/contrib/compiler-rt/lib/sanitizer_common/sanitizer_common_syscalls.inc b/contrib/compiler-rt/lib/sanitizer_common/sanitizer_common_syscalls.inc index 7e15d51..5d4840f 100644 --- a/contrib/compiler-rt/lib/sanitizer_common/sanitizer_common_syscalls.inc +++ b/contrib/compiler-rt/lib/sanitizer_common/sanitizer_common_syscalls.inc @@ -1443,6 +1443,7 @@ PRE_SYSCALL(fchown)(long fd, long user, long group) {} POST_SYSCALL(fchown)(long res, long fd, long user, long group) {} +#if SANITIZER_USES_UID16_SYSCALLS PRE_SYSCALL(chown16)(const void *filename, long user, long group) { if (filename) PRE_READ(filename, @@ -1552,6 +1553,7 @@ POST_SYSCALL(getgid16)(long res) {} PRE_SYSCALL(getegid16)() {} POST_SYSCALL(getegid16)(long res) {} +#endif // SANITIZER_USES_UID16_SYSCALLS PRE_SYSCALL(utime)(void *filename, void *times) {} @@ -2298,7 +2300,8 @@ POST_SYSCALL(ni_syscall)(long res) {} PRE_SYSCALL(ptrace)(long request, long pid, long addr, long data) { #if !SANITIZER_ANDROID && \ - (defined(__i386) || defined(__x86_64) || defined(__mips64)) + (defined(__i386) || defined(__x86_64) || defined(__mips64) || \ + defined(__powerpc64__)) if (data) { if (request == ptrace_setregs) { PRE_READ((void *)data, struct_user_regs_struct_sz); @@ -2318,7 +2321,8 @@ PRE_SYSCALL(ptrace)(long request, long pid, long addr, long data) { POST_SYSCALL(ptrace)(long res, long request, long pid, long addr, long data) { #if !SANITIZER_ANDROID && \ - (defined(__i386) || defined(__x86_64) || defined(__mips64)) + (defined(__i386) || defined(__x86_64) || defined(__mips64) || \ + defined(__powerpc64__)) if (res >= 0 && data) { // Note that this is different from the interceptor in // sanitizer_common_interceptors.inc. diff --git a/contrib/compiler-rt/lib/sanitizer_common/sanitizer_coverage_libcdep.cc b/contrib/compiler-rt/lib/sanitizer_common/sanitizer_coverage_libcdep.cc index 49887b1..f511c99 100644 --- a/contrib/compiler-rt/lib/sanitizer_common/sanitizer_coverage_libcdep.cc +++ b/contrib/compiler-rt/lib/sanitizer_common/sanitizer_coverage_libcdep.cc @@ -24,8 +24,12 @@ // and atomically set Guard to -Guard. // - __sanitizer_cov_dump: dump the coverage data to disk. // For every module of the current process that has coverage data -// this will create a file module_name.PID.sancov. The file format is simple: -// it's just a sorted sequence of 4-byte offsets in the module. +// this will create a file module_name.PID.sancov. +// +// The file format is simple: the first 8 bytes is the magic, +// one of 0xC0BFFFFFFFFFFF64 and 0xC0BFFFFFFFFFFF32. The last byte of the +// magic defines the size of the following offsets. +// The rest of the data is the offsets in the module. // // Eventually, this coverage implementation should be obsoleted by a more // powerful general purpose Clang/LLVM coverage instrumentation. @@ -43,6 +47,9 @@ #include "sanitizer_symbolizer.h" #include "sanitizer_flags.h" +static const u64 kMagic64 = 0xC0BFFFFFFFFFFF64ULL; +static const u64 kMagic32 = 0xC0BFFFFFFFFFFF32ULL; + static atomic_uint32_t dump_once_guard; // Ensure that CovDump runs only once. static atomic_uintptr_t coverage_counter; @@ -56,7 +63,7 @@ static atomic_uintptr_t coverage_counter; // dump current memory layout to another file. static bool cov_sandboxed = false; -static int cov_fd = kInvalidFd; +static fd_t cov_fd = kInvalidFd; static unsigned int cov_max_block_size = 0; static bool coverage_enabled = false; static const char *coverage_dir; @@ -77,21 +84,34 @@ class CoverageData { uptr cache_size); void DumpCallerCalleePairs(); void DumpTrace(); + void DumpAsBitSet(); + void DumpCounters(); + void DumpOffsets(); + void DumpAll(); ALWAYS_INLINE void TraceBasicBlock(s32 *id); void InitializeGuardArray(s32 *guards); - void InitializeGuards(s32 *guards, uptr n, const char *module_name); + void InitializeGuards(s32 *guards, uptr n, const char *module_name, + uptr caller_pc); + void InitializeCounters(u8 *counters, uptr n); void ReinitializeGuards(); + uptr GetNumberOf8bitCounters(); + uptr Update8bitCounterBitsetAndClearCounters(u8 *bitset); uptr *data(); uptr size(); private: + void DirectOpen(); + void UpdateModuleNameVec(uptr caller_pc, uptr range_beg, uptr range_end); + // Maximal size pc array may ever grow. // We MmapNoReserve this space to ensure that the array is contiguous. - static const uptr kPcArrayMaxSize = FIRST_32_SECOND_64(1 << 24, 1 << 27); + static const uptr kPcArrayMaxSize = FIRST_32_SECOND_64( + 1 << (SANITIZER_ANDROID ? 24 : (SANITIZER_WINDOWS ? 27 : 26)), + 1 << 27); // The amount file mapping for the pc array is grown by. static const uptr kPcArrayMmapSize = 64 * 1024; @@ -105,13 +125,27 @@ class CoverageData { // Current file mapped size of the pc array. uptr pc_array_mapped_size; // Descriptor of the file mapped pc array. - int pc_fd; + fd_t pc_fd; // Vector of coverage guard arrays, protected by mu. InternalMmapVectorNoCtor<s32*> guard_array_vec; - // Vector of module (compilation unit) names. - InternalMmapVectorNoCtor<const char*> comp_unit_name_vec; + struct NamedPcRange { + const char *copied_module_name; + uptr beg, end; // elements [beg,end) in pc_array. + }; + + // Vector of module and compilation unit pc ranges. + InternalMmapVectorNoCtor<NamedPcRange> comp_unit_name_vec; + InternalMmapVectorNoCtor<NamedPcRange> module_name_vec; + + struct CounterAndSize { + u8 *counters; + uptr n; + }; + + InternalMmapVectorNoCtor<CounterAndSize> counters_vec; + uptr num_8bit_counters; // Caller-Callee (cc) array, size and current index. static const uptr kCcArrayMaxSize = FIRST_32_SECOND_64(1 << 18, 1 << 24); @@ -133,8 +167,6 @@ class CoverageData { static const uptr kTrPcArrayMaxSize = FIRST_32_SECOND_64(1 << 22, 1 << 27); StaticSpinMutex mu; - - void DirectOpen(); }; static CoverageData coverage_data; @@ -145,9 +177,9 @@ void CoverageData::DirectOpen() { InternalScopedString path(kMaxPathLength); internal_snprintf((char *)path.data(), path.size(), "%s/%zd.sancov.raw", coverage_dir, internal_getpid()); - pc_fd = OpenFile(path.data(), true); - if (internal_iserror(pc_fd)) { - Report(" Coverage: failed to open %s for writing\n", path.data()); + pc_fd = OpenFile(path.data(), RdWr); + if (pc_fd == kInvalidFd) { + Report("Coverage: failed to open %s for reading/writing\n", path.data()); Die(); } @@ -180,10 +212,13 @@ void CoverageData::Enable() { tr_event_array = reinterpret_cast<u32 *>(MmapNoReserveOrDie( sizeof(tr_event_array[0]) * kTrEventArrayMaxSize + GetMmapGranularity(), "CovInit::tr_event_array")); - Mprotect(reinterpret_cast<uptr>(&tr_event_array[kTrEventArrayMaxSize]), - GetMmapGranularity()); + MprotectNoAccess( + reinterpret_cast<uptr>(&tr_event_array[kTrEventArrayMaxSize]), + GetMmapGranularity()); tr_event_array_size = kTrEventArrayMaxSize; tr_event_pointer = tr_event_array; + + num_8bit_counters = 0; } void CoverageData::InitializeGuardArray(s32 *guards) { @@ -197,22 +232,22 @@ void CoverageData::InitializeGuardArray(s32 *guards) { void CoverageData::Disable() { if (pc_array) { - internal_munmap(pc_array, sizeof(uptr) * kPcArrayMaxSize); + UnmapOrDie(pc_array, sizeof(uptr) * kPcArrayMaxSize); pc_array = nullptr; } if (cc_array) { - internal_munmap(cc_array, sizeof(uptr *) * kCcArrayMaxSize); + UnmapOrDie(cc_array, sizeof(uptr *) * kCcArrayMaxSize); cc_array = nullptr; } if (tr_event_array) { - internal_munmap(tr_event_array, - sizeof(tr_event_array[0]) * kTrEventArrayMaxSize + - GetMmapGranularity()); + UnmapOrDie(tr_event_array, + sizeof(tr_event_array[0]) * kTrEventArrayMaxSize + + GetMmapGranularity()); tr_event_array = nullptr; tr_event_pointer = nullptr; } if (pc_fd != kInvalidFd) { - internal_close(pc_fd); + CloseFile(pc_fd); pc_fd = kInvalidFd; } } @@ -231,8 +266,9 @@ void CoverageData::ReInit() { // In memory-mapped mode we must extend the new file to the known array // size. uptr size = atomic_load(&pc_array_size, memory_order_relaxed); + uptr npcs = size / sizeof(uptr); Enable(); - if (size) Extend(size); + if (size) Extend(npcs); if (coverage_enabled) CovUpdateMapping(coverage_dir); } else { Enable(); @@ -289,16 +325,69 @@ void CoverageData::Extend(uptr npcs) { atomic_store(&pc_array_size, size, memory_order_release); } +void CoverageData::InitializeCounters(u8 *counters, uptr n) { + if (!counters) return; + CHECK_EQ(reinterpret_cast<uptr>(counters) % 16, 0); + n = RoundUpTo(n, 16); // The compiler must ensure that counters is 16-aligned. + SpinMutexLock l(&mu); + counters_vec.push_back({counters, n}); + num_8bit_counters += n; +} + +void CoverageData::UpdateModuleNameVec(uptr caller_pc, uptr range_beg, + uptr range_end) { + auto sym = Symbolizer::GetOrInit(); + if (!sym) + return; + const char *module_name = sym->GetModuleNameForPc(caller_pc); + if (!module_name) return; + if (module_name_vec.empty() || + module_name_vec.back().copied_module_name != module_name) + module_name_vec.push_back({module_name, range_beg, range_end}); + else + module_name_vec.back().end = range_end; +} + void CoverageData::InitializeGuards(s32 *guards, uptr n, - const char *module_name) { + const char *comp_unit_name, + uptr caller_pc) { // The array 'guards' has n+1 elements, we use the element zero // to store 'n'. CHECK_LT(n, 1 << 30); guards[0] = static_cast<s32>(n); InitializeGuardArray(guards); SpinMutexLock l(&mu); - comp_unit_name_vec.push_back(module_name); + uptr range_end = atomic_load(&pc_array_index, memory_order_relaxed); + uptr range_beg = range_end - n; + comp_unit_name_vec.push_back({comp_unit_name, range_beg, range_end}); guard_array_vec.push_back(guards); + UpdateModuleNameVec(caller_pc, range_beg, range_end); +} + +static const uptr kBundleCounterBits = 16; + +// When coverage_order_pcs==true and SANITIZER_WORDSIZE==64 +// we insert the global counter into the first 16 bits of the PC. +uptr BundlePcAndCounter(uptr pc, uptr counter) { + if (SANITIZER_WORDSIZE != 64 || !common_flags()->coverage_order_pcs) + return pc; + static const uptr kMaxCounter = (1 << kBundleCounterBits) - 1; + if (counter > kMaxCounter) + counter = kMaxCounter; + CHECK_EQ(0, pc >> (SANITIZER_WORDSIZE - kBundleCounterBits)); + return pc | (counter << (SANITIZER_WORDSIZE - kBundleCounterBits)); +} + +uptr UnbundlePc(uptr bundle) { + if (SANITIZER_WORDSIZE != 64 || !common_flags()->coverage_order_pcs) + return bundle; + return (bundle << kBundleCounterBits) >> kBundleCounterBits; +} + +uptr UnbundleCounter(uptr bundle) { + if (SANITIZER_WORDSIZE != 64 || !common_flags()->coverage_order_pcs) + return 0; + return bundle >> (SANITIZER_WORDSIZE - kBundleCounterBits); } // If guard is negative, atomically set it to -guard and store the PC in @@ -316,8 +405,8 @@ void CoverageData::Add(uptr pc, u32 *guard) { return; // May happen after fork when pc_array_index becomes 0. CHECK_LT(idx * sizeof(uptr), atomic_load(&pc_array_size, memory_order_acquire)); - pc_array[idx] = pc; - atomic_fetch_add(&coverage_counter, 1, memory_order_relaxed); + uptr counter = atomic_fetch_add(&coverage_counter, 1, memory_order_relaxed); + pc_array[idx] = BundlePcAndCounter(pc, counter); } // Registers a pair caller=>callee. @@ -354,6 +443,64 @@ void CoverageData::IndirCall(uptr caller, uptr callee, uptr callee_cache[], } } +uptr CoverageData::GetNumberOf8bitCounters() { + return num_8bit_counters; +} + +// Map every 8bit counter to a 8-bit bitset and clear the counter. +uptr CoverageData::Update8bitCounterBitsetAndClearCounters(u8 *bitset) { + uptr num_new_bits = 0; + uptr cur = 0; + // For better speed we map 8 counters to 8 bytes of bitset at once. + static const uptr kBatchSize = 8; + CHECK_EQ(reinterpret_cast<uptr>(bitset) % kBatchSize, 0); + for (uptr i = 0, len = counters_vec.size(); i < len; i++) { + u8 *c = counters_vec[i].counters; + uptr n = counters_vec[i].n; + CHECK_EQ(n % 16, 0); + CHECK_EQ(cur % kBatchSize, 0); + CHECK_EQ(reinterpret_cast<uptr>(c) % kBatchSize, 0); + if (!bitset) { + internal_bzero_aligned16(c, n); + cur += n; + continue; + } + for (uptr j = 0; j < n; j += kBatchSize, cur += kBatchSize) { + CHECK_LT(cur, num_8bit_counters); + u64 *pc64 = reinterpret_cast<u64*>(c + j); + u64 *pb64 = reinterpret_cast<u64*>(bitset + cur); + u64 c64 = *pc64; + u64 old_bits_64 = *pb64; + u64 new_bits_64 = old_bits_64; + if (c64) { + *pc64 = 0; + for (uptr k = 0; k < kBatchSize; k++) { + u64 x = (c64 >> (8 * k)) & 0xff; + if (x) { + u64 bit = 0; + /**/ if (x >= 128) bit = 128; + else if (x >= 32) bit = 64; + else if (x >= 16) bit = 32; + else if (x >= 8) bit = 16; + else if (x >= 4) bit = 8; + else if (x >= 3) bit = 4; + else if (x >= 2) bit = 2; + else if (x >= 1) bit = 1; + u64 mask = bit << (8 * k); + if (!(new_bits_64 & mask)) { + num_new_bits++; + new_bits_64 |= mask; + } + } + } + *pb64 = new_bits_64; + } + } + } + CHECK_EQ(cur, num_8bit_counters); + return num_new_bits; +} + uptr *CoverageData::data() { return pc_array; } @@ -372,15 +519,15 @@ struct CovHeader { static void CovWritePacked(int pid, const char *module, const void *blob, unsigned int blob_size) { - if (cov_fd < 0) return; + if (cov_fd == kInvalidFd) return; unsigned module_name_length = internal_strlen(module); CovHeader header = {pid, module_name_length, blob_size}; if (cov_max_block_size == 0) { // Writing to a file. Just go ahead. - internal_write(cov_fd, &header, sizeof(header)); - internal_write(cov_fd, module, module_name_length); - internal_write(cov_fd, blob, blob_size); + WriteToFile(cov_fd, &header, sizeof(header)); + WriteToFile(cov_fd, module, module_name_length); + WriteToFile(cov_fd, blob, blob_size); } else { // Writing to a socket. We want to split the data into appropriately sized // blocks. @@ -403,8 +550,7 @@ static void CovWritePacked(int pid, const char *module, const void *blob, internal_memcpy(block_data_begin, blob_pos, payload_size); blob_pos += payload_size; ((CovHeader *)block.data())->data_length = payload_size; - internal_write(cov_fd, block.data(), - header_size_with_module + payload_size); + WriteToFile(cov_fd, block.data(), header_size_with_module + payload_size); } } } @@ -413,25 +559,25 @@ static void CovWritePacked(int pid, const char *module, const void *blob, // If packed = true and name == 0: <pid>.<sancov>.<packed>. // If packed = true and name != 0: <name>.<sancov>.<packed> (name is // user-supplied). -static int CovOpenFile(bool packed, const char *name, - const char *extension = "sancov") { - InternalScopedString path(kMaxPathLength); +static fd_t CovOpenFile(InternalScopedString *path, bool packed, + const char *name, const char *extension = "sancov") { + path->clear(); if (!packed) { CHECK(name); - path.append("%s/%s.%zd.%s", coverage_dir, name, internal_getpid(), + path->append("%s/%s.%zd.%s", coverage_dir, name, internal_getpid(), extension); } else { if (!name) - path.append("%s/%zd.%s.packed", coverage_dir, internal_getpid(), + path->append("%s/%zd.%s.packed", coverage_dir, internal_getpid(), extension); else - path.append("%s/%s.%s.packed", coverage_dir, name, extension); - } - uptr fd = OpenFile(path.data(), true); - if (internal_iserror(fd)) { - Report(" SanitizerCoverage: failed to open %s for writing\n", path.data()); - return -1; + path->append("%s/%s.%s.packed", coverage_dir, name, extension); } + error_t err; + fd_t fd = OpenFile(path->data(), WrOnly, &err); + if (fd == kInvalidFd) + Report("SanitizerCoverage: failed to open %s for writing (reason: %d)\n", + path->data(), err); return fd; } @@ -446,38 +592,40 @@ void CoverageData::DumpTrace() { for (uptr i = 0, n = size(); i < n; i++) { const char *module_name = "<unknown>"; uptr module_address = 0; - sym->GetModuleNameAndOffsetForPC(pc_array[i], &module_name, + sym->GetModuleNameAndOffsetForPC(UnbundlePc(pc_array[i]), &module_name, &module_address); out.append("%s 0x%zx\n", module_name, module_address); } - int fd = CovOpenFile(false, "trace-points"); - if (fd < 0) return; - internal_write(fd, out.data(), out.length()); - internal_close(fd); + InternalScopedString path(kMaxPathLength); + fd_t fd = CovOpenFile(&path, false, "trace-points"); + if (fd == kInvalidFd) return; + WriteToFile(fd, out.data(), out.length()); + CloseFile(fd); - fd = CovOpenFile(false, "trace-compunits"); - if (fd < 0) return; + fd = CovOpenFile(&path, false, "trace-compunits"); + if (fd == kInvalidFd) return; out.clear(); for (uptr i = 0; i < comp_unit_name_vec.size(); i++) - out.append("%s\n", comp_unit_name_vec[i]); - internal_write(fd, out.data(), out.length()); - internal_close(fd); + out.append("%s\n", comp_unit_name_vec[i].copied_module_name); + WriteToFile(fd, out.data(), out.length()); + CloseFile(fd); - fd = CovOpenFile(false, "trace-events"); - if (fd < 0) return; + fd = CovOpenFile(&path, false, "trace-events"); + if (fd == kInvalidFd) return; uptr bytes_to_write = max_idx * sizeof(tr_event_array[0]); u8 *event_bytes = reinterpret_cast<u8*>(tr_event_array); // The trace file could be huge, and may not be written with a single syscall. while (bytes_to_write) { - uptr actually_written = internal_write(fd, event_bytes, bytes_to_write); - if (actually_written <= bytes_to_write) { + uptr actually_written; + if (WriteToFile(fd, event_bytes, bytes_to_write, &actually_written) && + actually_written <= bytes_to_write) { bytes_to_write -= actually_written; event_bytes += actually_written; } else { break; } } - internal_close(fd); + CloseFile(fd); VReport(1, " CovDump: Trace: %zd PCs written\n", size()); VReport(1, " CovDump: Trace: %zd Events written\n", max_idx); } @@ -514,10 +662,11 @@ void CoverageData::DumpCallerCalleePairs() { callee_module_address); } } - int fd = CovOpenFile(false, "caller-callee"); - if (fd < 0) return; - internal_write(fd, out.data(), out.length()); - internal_close(fd); + InternalScopedString path(kMaxPathLength); + fd_t fd = CovOpenFile(&path, false, "caller-callee"); + if (fd == kInvalidFd) return; + WriteToFile(fd, out.data(), out.length()); + CloseFile(fd); VReport(1, " CovDump: %zd caller-callee pairs written\n", total); } @@ -534,86 +683,123 @@ void CoverageData::TraceBasicBlock(s32 *id) { tr_event_pointer++; } -static void CovDumpAsBitSet() { +void CoverageData::DumpCounters() { + if (!common_flags()->coverage_counters) return; + uptr n = coverage_data.GetNumberOf8bitCounters(); + if (!n) return; + InternalScopedBuffer<u8> bitset(n); + coverage_data.Update8bitCounterBitsetAndClearCounters(bitset.data()); + InternalScopedString path(kMaxPathLength); + + for (uptr m = 0; m < module_name_vec.size(); m++) { + auto r = module_name_vec[m]; + CHECK(r.copied_module_name); + CHECK_LE(r.beg, r.end); + CHECK_LE(r.end, size()); + const char *base_name = StripModuleName(r.copied_module_name); + fd_t fd = + CovOpenFile(&path, /* packed */ false, base_name, "counters-sancov"); + if (fd == kInvalidFd) return; + WriteToFile(fd, bitset.data() + r.beg, r.end - r.beg); + CloseFile(fd); + VReport(1, " CovDump: %zd counters written for '%s'\n", r.end - r.beg, + base_name); + } +} + +void CoverageData::DumpAsBitSet() { if (!common_flags()->coverage_bitset) return; - if (!coverage_data.size()) return; - int fd = CovOpenFile(/* packed */false, "combined", "bitset-sancov"); - if (fd < 0) return; - uptr n = coverage_data.size(); - uptr n_set_bits = 0; - InternalScopedBuffer<char> out(n); - for (uptr i = 0; i < n; i++) { - uptr pc = coverage_data.data()[i]; - out[i] = pc ? '1' : '0'; - if (pc) - n_set_bits++; + if (!size()) return; + InternalScopedBuffer<char> out(size()); + InternalScopedString path(kMaxPathLength); + for (uptr m = 0; m < module_name_vec.size(); m++) { + uptr n_set_bits = 0; + auto r = module_name_vec[m]; + CHECK(r.copied_module_name); + CHECK_LE(r.beg, r.end); + CHECK_LE(r.end, size()); + for (uptr i = r.beg; i < r.end; i++) { + uptr pc = UnbundlePc(pc_array[i]); + out[i] = pc ? '1' : '0'; + if (pc) + n_set_bits++; + } + const char *base_name = StripModuleName(r.copied_module_name); + fd_t fd = CovOpenFile(&path, /* packed */false, base_name, "bitset-sancov"); + if (fd == kInvalidFd) return; + WriteToFile(fd, out.data() + r.beg, r.end - r.beg); + CloseFile(fd); + VReport(1, + " CovDump: bitset of %zd bits written for '%s', %zd bits are set\n", + r.end - r.beg, base_name, n_set_bits); } - internal_write(fd, out.data(), n); - internal_close(fd); - VReport(1, " CovDump: bitset of %zd bits written, %zd bits are set\n", n, - n_set_bits); } -// Dump the coverage on disk. -static void CovDump() { - if (!coverage_enabled || common_flags()->coverage_direct) return; -#if !SANITIZER_WINDOWS - if (atomic_fetch_add(&dump_once_guard, 1, memory_order_relaxed)) - return; - CovDumpAsBitSet(); - coverage_data.DumpTrace(); +void CoverageData::DumpOffsets() { + auto sym = Symbolizer::GetOrInit(); if (!common_flags()->coverage_pcs) return; - uptr size = coverage_data.size(); - InternalMmapVector<u32> offsets(size); - uptr *vb = coverage_data.data(); - uptr *ve = vb + size; - SortArray(vb, size); - MemoryMappingLayout proc_maps(/*cache_enabled*/true); - uptr mb, me, off, prot; - InternalScopedString module(kMaxPathLength); + CHECK_NE(sym, nullptr); + InternalMmapVector<uptr> offsets(0); InternalScopedString path(kMaxPathLength); - for (int i = 0; - proc_maps.Next(&mb, &me, &off, module.data(), module.size(), &prot); - i++) { - if ((prot & MemoryMappingLayout::kProtectionExecute) == 0) - continue; - while (vb < ve && *vb < mb) vb++; - if (vb >= ve) break; - if (*vb < me) { - offsets.clear(); - const uptr *old_vb = vb; - CHECK_LE(off, *vb); - for (; vb < ve && *vb < me; vb++) { - uptr diff = *vb - (i ? mb : 0) + off; - CHECK_LE(diff, 0xffffffffU); - offsets.push_back(static_cast<u32>(diff)); - } - const char *module_name = StripModuleName(module.data()); - if (cov_sandboxed) { - if (cov_fd >= 0) { - CovWritePacked(internal_getpid(), module_name, offsets.data(), - offsets.size() * sizeof(u32)); - VReport(1, " CovDump: %zd PCs written to packed file\n", vb - old_vb); - } - } else { - // One file per module per process. - path.clear(); - path.append("%s/%s.%zd.sancov", coverage_dir, module_name, - internal_getpid()); - int fd = CovOpenFile(false /* packed */, module_name); - if (fd > 0) { - internal_write(fd, offsets.data(), offsets.size() * sizeof(u32)); - internal_close(fd); - VReport(1, " CovDump: %s: %zd PCs written\n", path.data(), - vb - old_vb); - } + for (uptr m = 0; m < module_name_vec.size(); m++) { + offsets.clear(); + uptr num_words_for_magic = SANITIZER_WORDSIZE == 64 ? 1 : 2; + for (uptr i = 0; i < num_words_for_magic; i++) + offsets.push_back(0); + auto r = module_name_vec[m]; + CHECK(r.copied_module_name); + CHECK_LE(r.beg, r.end); + CHECK_LE(r.end, size()); + for (uptr i = r.beg; i < r.end; i++) { + uptr pc = UnbundlePc(pc_array[i]); + uptr counter = UnbundleCounter(pc_array[i]); + if (!pc) continue; // Not visited. + uptr offset = 0; + sym->GetModuleNameAndOffsetForPC(pc, nullptr, &offset); + offsets.push_back(BundlePcAndCounter(offset, counter)); + } + + CHECK_GE(offsets.size(), num_words_for_magic); + SortArray(offsets.data(), offsets.size()); + for (uptr i = 0; i < offsets.size(); i++) + offsets[i] = UnbundlePc(offsets[i]); + + uptr num_offsets = offsets.size() - num_words_for_magic; + u64 *magic_p = reinterpret_cast<u64*>(offsets.data()); + CHECK_EQ(*magic_p, 0ULL); + // FIXME: we may want to write 32-bit offsets even in 64-mode + // if all the offsets are small enough. + *magic_p = SANITIZER_WORDSIZE == 64 ? kMagic64 : kMagic32; + + const char *module_name = StripModuleName(r.copied_module_name); + if (cov_sandboxed) { + if (cov_fd != kInvalidFd) { + CovWritePacked(internal_getpid(), module_name, offsets.data(), + offsets.size() * sizeof(offsets[0])); + VReport(1, " CovDump: %zd PCs written to packed file\n", num_offsets); } + } else { + // One file per module per process. + fd_t fd = CovOpenFile(&path, false /* packed */, module_name); + if (fd == kInvalidFd) continue; + WriteToFile(fd, offsets.data(), offsets.size() * sizeof(offsets[0])); + CloseFile(fd); + VReport(1, " CovDump: %s: %zd PCs written\n", path.data(), num_offsets); } } - if (cov_fd >= 0) - internal_close(cov_fd); - coverage_data.DumpCallerCalleePairs(); -#endif // !SANITIZER_WINDOWS + if (cov_fd != kInvalidFd) + CloseFile(cov_fd); +} + +void CoverageData::DumpAll() { + if (!coverage_enabled || common_flags()->coverage_direct) return; + if (atomic_fetch_add(&dump_once_guard, 1, memory_order_relaxed)) + return; + DumpAsBitSet(); + DumpCounters(); + DumpTrace(); + DumpOffsets(); + DumpCallerCalleePairs(); } void CovPrepareForSandboxing(__sanitizer_sandbox_arguments *args) { @@ -621,17 +807,21 @@ void CovPrepareForSandboxing(__sanitizer_sandbox_arguments *args) { if (!coverage_enabled) return; cov_sandboxed = args->coverage_sandboxed; if (!cov_sandboxed) return; - cov_fd = args->coverage_fd; cov_max_block_size = args->coverage_max_block_size; - if (cov_fd < 0) + if (args->coverage_fd >= 0) { + cov_fd = (fd_t)args->coverage_fd; + } else { + InternalScopedString path(kMaxPathLength); // Pre-open the file now. The sandbox won't allow us to do it later. - cov_fd = CovOpenFile(true /* packed */, 0); + cov_fd = CovOpenFile(&path, true /* packed */, 0); + } } -int MaybeOpenCovFile(const char *name) { +fd_t MaybeOpenCovFile(const char *name) { CHECK(name); - if (!coverage_enabled) return -1; - return CovOpenFile(true /* packed */, name); + if (!coverage_enabled) return kInvalidFd; + InternalScopedString path(kMaxPathLength); + return CovOpenFile(&path, true /* packed */, name); } void CovBeforeFork() { @@ -649,9 +839,7 @@ void InitializeCoverage(bool enabled, const char *dir) { coverage_dir = dir; coverage_data.Init(); if (enabled) coverage_data.Enable(); -#if !SANITIZER_WINDOWS if (!common_flags()->coverage_direct) Atexit(__sanitizer_cov_dump); -#endif } void ReInitializeCoverage(bool enabled, const char *dir) { @@ -674,7 +862,8 @@ SANITIZER_INTERFACE_ATTRIBUTE void __sanitizer_cov(u32 *guard) { } SANITIZER_INTERFACE_ATTRIBUTE void __sanitizer_cov_with_check(u32 *guard) { atomic_uint32_t *atomic_guard = reinterpret_cast<atomic_uint32_t*>(guard); - if (__sanitizer::atomic_load(atomic_guard, memory_order_relaxed)) + if (static_cast<s32>( + __sanitizer::atomic_load(atomic_guard, memory_order_relaxed)) < 0) __sanitizer_cov(guard); } SANITIZER_INTERFACE_ATTRIBUTE void @@ -687,10 +876,14 @@ SANITIZER_INTERFACE_ATTRIBUTE void __sanitizer_cov_init() { coverage_dir = common_flags()->coverage_dir; coverage_data.Init(); } -SANITIZER_INTERFACE_ATTRIBUTE void __sanitizer_cov_dump() { CovDump(); } +SANITIZER_INTERFACE_ATTRIBUTE void __sanitizer_cov_dump() { + coverage_data.DumpAll(); +} SANITIZER_INTERFACE_ATTRIBUTE void -__sanitizer_cov_module_init(s32 *guards, uptr npcs, const char *module_name) { - coverage_data.InitializeGuards(guards, npcs, module_name); +__sanitizer_cov_module_init(s32 *guards, uptr npcs, u8 *counters, + const char *comp_unit_name) { + coverage_data.InitializeGuards(guards, npcs, comp_unit_name, GET_CALLER_PC()); + coverage_data.InitializeCounters(counters, npcs); if (!common_flags()->coverage_direct) return; if (SANITIZER_ANDROID && coverage_enabled) { // dlopen/dlclose interceptors do not work on Android, so we rely on @@ -701,7 +894,7 @@ __sanitizer_cov_module_init(s32 *guards, uptr npcs, const char *module_name) { } SANITIZER_INTERFACE_ATTRIBUTE sptr __sanitizer_maybe_open_cov_file(const char *name) { - return MaybeOpenCovFile(name); + return (sptr)MaybeOpenCovFile(name); } SANITIZER_INTERFACE_ATTRIBUTE uptr __sanitizer_get_total_unique_coverage() { @@ -728,4 +921,17 @@ uptr __sanitizer_get_coverage_guards(uptr **data) { *data = coverage_data.data(); return coverage_data.size(); } + +SANITIZER_INTERFACE_ATTRIBUTE +uptr __sanitizer_get_number_of_counters() { + return coverage_data.GetNumberOf8bitCounters(); +} + +SANITIZER_INTERFACE_ATTRIBUTE +uptr __sanitizer_update_counter_bitset_and_clear_counters(u8 *bitset) { + return coverage_data.Update8bitCounterBitsetAndClearCounters(bitset); +} +// Default empty implementation (weak). Users should redefine it. +SANITIZER_INTERFACE_ATTRIBUTE SANITIZER_WEAK_ATTRIBUTE +void __sanitizer_cov_trace_cmp() {} } // extern "C" diff --git a/contrib/compiler-rt/lib/sanitizer_common/sanitizer_coverage_mapping_libcdep.cc b/contrib/compiler-rt/lib/sanitizer_common/sanitizer_coverage_mapping_libcdep.cc index 6b5e91f..a3d75ab 100644 --- a/contrib/compiler-rt/lib/sanitizer_common/sanitizer_coverage_mapping_libcdep.cc +++ b/contrib/compiler-rt/lib/sanitizer_common/sanitizer_coverage_mapping_libcdep.cc @@ -35,7 +35,6 @@ namespace __sanitizer { -static const uptr kMaxNumberOfModules = 1 << 14; static const uptr kMaxTextSize = 64 * 1024; struct CachedMapping { @@ -96,32 +95,30 @@ void CovUpdateMapping(const char *coverage_dir, uptr caller_pc) { } } - int err; + error_t err; InternalScopedString tmp_path(64 + internal_strlen(coverage_dir)); uptr res = internal_snprintf((char *)tmp_path.data(), tmp_path.size(), "%s/%zd.sancov.map.tmp", coverage_dir, internal_getpid()); CHECK_LE(res, tmp_path.size()); - uptr map_fd = OpenFile(tmp_path.data(), true); - if (internal_iserror(map_fd, &err)) { - Report(" Coverage: failed to open %s for writing: %d\n", tmp_path.data(), + fd_t map_fd = OpenFile(tmp_path.data(), WrOnly, &err); + if (map_fd == kInvalidFd) { + Report("Coverage: failed to open %s for writing: %d\n", tmp_path.data(), err); Die(); } - res = internal_write(map_fd, text.data(), text.length()); - if (internal_iserror(res, &err)) { + if (!WriteToFile(map_fd, text.data(), text.length(), nullptr, &err)) { Printf("sancov.map write failed: %d\n", err); Die(); } - internal_close(map_fd); + CloseFile(map_fd); InternalScopedString path(64 + internal_strlen(coverage_dir)); res = internal_snprintf((char *)path.data(), path.size(), "%s/%zd.sancov.map", coverage_dir, internal_getpid()); CHECK_LE(res, path.size()); - res = internal_rename(tmp_path.data(), path.data()); - if (internal_iserror(res, &err)) { + if (!RenameFile(tmp_path.data(), path.data(), &err)) { Printf("sancov.map rename failed: %d\n", err); Die(); } diff --git a/contrib/compiler-rt/lib/sanitizer_common/sanitizer_deadlock_detector1.cc b/contrib/compiler-rt/lib/sanitizer_common/sanitizer_deadlock_detector1.cc index b931edc..5890b54 100644 --- a/contrib/compiler-rt/lib/sanitizer_common/sanitizer_deadlock_detector1.cc +++ b/contrib/compiler-rt/lib/sanitizer_common/sanitizer_deadlock_detector1.cc @@ -40,19 +40,20 @@ struct DD : public DDetector { explicit DD(const DDFlags *flags); - DDPhysicalThread* CreatePhysicalThread(); - void DestroyPhysicalThread(DDPhysicalThread *pt); + DDPhysicalThread *CreatePhysicalThread() override; + void DestroyPhysicalThread(DDPhysicalThread *pt) override; - DDLogicalThread* CreateLogicalThread(u64 ctx); - void DestroyLogicalThread(DDLogicalThread *lt); + DDLogicalThread *CreateLogicalThread(u64 ctx) override; + void DestroyLogicalThread(DDLogicalThread *lt) override; - void MutexInit(DDCallback *cb, DDMutex *m); - void MutexBeforeLock(DDCallback *cb, DDMutex *m, bool wlock); - void MutexAfterLock(DDCallback *cb, DDMutex *m, bool wlock, bool trylock); - void MutexBeforeUnlock(DDCallback *cb, DDMutex *m, bool wlock); - void MutexDestroy(DDCallback *cb, DDMutex *m); + void MutexInit(DDCallback *cb, DDMutex *m) override; + void MutexBeforeLock(DDCallback *cb, DDMutex *m, bool wlock) override; + void MutexAfterLock(DDCallback *cb, DDMutex *m, bool wlock, + bool trylock) override; + void MutexBeforeUnlock(DDCallback *cb, DDMutex *m, bool wlock) override; + void MutexDestroy(DDCallback *cb, DDMutex *m) override; - DDReport *GetReport(DDCallback *cb); + DDReport *GetReport(DDCallback *cb) override; void MutexEnsureID(DDLogicalThread *lt, DDMutex *m); void ReportDeadlock(DDCallback *cb, DDMutex *m); diff --git a/contrib/compiler-rt/lib/sanitizer_common/sanitizer_flags.cc b/contrib/compiler-rt/lib/sanitizer_common/sanitizer_flags.cc index e835b46..01098a3 100644 --- a/contrib/compiler-rt/lib/sanitizer_common/sanitizer_flags.cc +++ b/contrib/compiler-rt/lib/sanitizer_common/sanitizer_flags.cc @@ -54,7 +54,7 @@ class FlagHandlerInclude : public FlagHandlerBase { bool Parse(const char *value) final { char *data; uptr data_mapped_size; - int err; + error_t err; uptr len = ReadFileToBuffer(value, &data, &data_mapped_size, Max(kMaxIncludeSize, GetPageSizeCached()), &err); diff --git a/contrib/compiler-rt/lib/sanitizer_common/sanitizer_flags.inc b/contrib/compiler-rt/lib/sanitizer_common/sanitizer_flags.inc index 58f7f37..bbb39c7 100644 --- a/contrib/compiler-rt/lib/sanitizer_common/sanitizer_flags.inc +++ b/contrib/compiler-rt/lib/sanitizer_common/sanitizer_flags.inc @@ -51,6 +51,10 @@ COMMON_FLAG( "Write logs to \"log_path.pid\". The special values are \"stdout\" and " "\"stderr\". The default is \"stderr\".") COMMON_FLAG( + bool, log_exe_name, false, + "Mention name of executable when reporting error and " + "append executable name to logs (as in \"log_path.exe_name.pid\").") +COMMON_FLAG( int, verbosity, 0, "Verbosity level (0 - silent, 1 - a bit of output, 2+ - more output).") COMMON_FLAG(bool, detect_leaks, true, "Enable memory leak detection.") @@ -67,8 +71,9 @@ COMMON_FLAG(bool, print_summary, true, "reports.") COMMON_FLAG(bool, check_printf, true, "Check printf arguments.") COMMON_FLAG(bool, handle_segv, SANITIZER_NEEDS_SEGV, - "If set, registers the tool's custom SEGV handler (both SIGBUS and " - "SIGSEGV on OSX).") + "If set, registers the tool's custom SIGSEGV/SIGBUS handler.") +COMMON_FLAG(bool, handle_abort, false, + "If set, registers the tool's custom SIGABRT handler.") COMMON_FLAG(bool, allow_user_segv_handler, false, "If set, allows user to register a SEGV handler even if the tool " "registers one.") @@ -111,13 +116,18 @@ COMMON_FLAG( bool, coverage, false, "If set, coverage information will be dumped at program shutdown (if the " "coverage instrumentation was enabled at compile time).") -// On by default, but works only if coverage == true. COMMON_FLAG(bool, coverage_pcs, true, "If set (and if 'coverage' is set too), the coverage information " "will be dumped as a set of PC offsets for every module.") +COMMON_FLAG(bool, coverage_order_pcs, false, + "If true, the PCs will be dumped in the order they've" + " appeared during the execution.") COMMON_FLAG(bool, coverage_bitset, false, "If set (and if 'coverage' is set too), the coverage information " "will also be dumped as a bitset to a separate file.") +COMMON_FLAG(bool, coverage_counters, false, + "If set (and if 'coverage' is set too), the bitmap that corresponds" + " to coverage counters will be dumped.") COMMON_FLAG(bool, coverage_direct, SANITIZER_ANDROID, "If set, coverage information will be dumped directly to a memory " "mapped file. This way data is not lost even if the process is " @@ -140,9 +150,26 @@ COMMON_FLAG(bool, use_madv_dontdump, true, "in core file.") COMMON_FLAG(bool, symbolize_inline_frames, true, "Print inlined frames in stacktraces. Defaults to true.") +COMMON_FLAG(bool, symbolize_vs_style, false, + "Print file locations in Visual Studio style (e.g: " + " file(10,42): ...") COMMON_FLAG(const char *, stack_trace_format, "DEFAULT", "Format string used to render stack frames. " "See sanitizer_stacktrace_printer.h for the format description. " "Use DEFAULT to get default format.") COMMON_FLAG(bool, no_huge_pages_for_shadow, true, "If true, the shadow is not allowed to use huge pages. ") +COMMON_FLAG(bool, strict_string_checks, false, + "If set check that string arguments are properly null-terminated") +COMMON_FLAG(bool, intercept_strstr, true, + "If set, uses custom wrappers for strstr and strcasestr functions " + "to find more errors.") +COMMON_FLAG(bool, intercept_strspn, true, + "If set, uses custom wrappers for strspn and strcspn function " + "to find more errors.") +COMMON_FLAG(bool, intercept_strpbrk, true, + "If set, uses custom wrappers for strpbrk function " + "to find more errors.") +COMMON_FLAG(bool, decorate_proc_maps, false, "If set, decorate sanitizer " + "mappings in /proc/self/maps with " + "user-readable names") diff --git a/contrib/compiler-rt/lib/sanitizer_common/sanitizer_internal_defs.h b/contrib/compiler-rt/lib/sanitizer_common/sanitizer_internal_defs.h index a969f30..b76c602 100644 --- a/contrib/compiler-rt/lib/sanitizer_common/sanitizer_internal_defs.h +++ b/contrib/compiler-rt/lib/sanitizer_common/sanitizer_internal_defs.h @@ -32,7 +32,7 @@ # define SANITIZER_WEAK_ATTRIBUTE __attribute__((weak)) #endif -#if SANITIZER_LINUX && !defined(SANITIZER_GO) +#if (SANITIZER_LINUX || SANITIZER_WINDOWS) && !defined(SANITIZER_GO) # define SANITIZER_SUPPORTS_WEAK_HOOKS 1 #else # define SANITIZER_SUPPORTS_WEAK_HOOKS 0 @@ -80,7 +80,15 @@ typedef signed char s8; typedef signed short s16; // NOLINT typedef signed int s32; typedef signed long long s64; // NOLINT +#if SANITIZER_WINDOWS +// On Windows, files are HANDLE, which is a synonim of void*. +// Use void* to avoid including <windows.h> everywhere. +typedef void* fd_t; +typedef unsigned error_t; +#else typedef int fd_t; +typedef int error_t; +#endif // WARNING: OFF_T may be different from OS type off_t, depending on the value of // _FILE_OFFSET_BITS. This definition of OFF_T matches the ABI of system calls @@ -295,6 +303,7 @@ extern "C" void* _ReturnAddress(void); do { \ volatile uptr enable_fp; \ enable_fp = GET_CURRENT_FRAME(); \ + (void)enable_fp; \ } while (0) #endif // SANITIZER_DEFS_H diff --git a/contrib/compiler-rt/lib/sanitizer_common/sanitizer_libc.h b/contrib/compiler-rt/lib/sanitizer_common/sanitizer_libc.h index c086b8a..ae4e938 100644 --- a/contrib/compiler-rt/lib/sanitizer_common/sanitizer_libc.h +++ b/contrib/compiler-rt/lib/sanitizer_common/sanitizer_libc.h @@ -11,6 +11,7 @@ // run-time libraries. // These tools can not use some of the libc functions directly because those // functions are intercepted. Instead, we implement a tiny subset of libc here. +// FIXME: Some of functions declared in this file are in fact POSIX, not libc. //===----------------------------------------------------------------------===// #ifndef SANITIZER_LIBC_H #define SANITIZER_LIBC_H @@ -56,74 +57,26 @@ int internal_snprintf(char *buffer, uptr length, const char *format, ...); // Optimized for the case when the result is true. bool mem_is_zero(const char *mem, uptr size); - -// Memory -uptr internal_mmap(void *addr, uptr length, int prot, int flags, - int fd, u64 offset); -uptr internal_munmap(void *addr, uptr length); - // I/O -const fd_t kInvalidFd = -1; +const fd_t kInvalidFd = (fd_t)-1; const fd_t kStdinFd = 0; -const fd_t kStdoutFd = 1; -const fd_t kStderrFd = 2; -uptr internal_close(fd_t fd); -int internal_isatty(fd_t fd); +const fd_t kStdoutFd = (fd_t)1; +const fd_t kStderrFd = (fd_t)2; -// Use __sanitizer::OpenFile() instead. -uptr internal_open(const char *filename, int flags); -uptr internal_open(const char *filename, int flags, u32 mode); - -uptr internal_read(fd_t fd, void *buf, uptr count); -uptr internal_write(fd_t fd, const void *buf, uptr count); uptr internal_ftruncate(fd_t fd, uptr size); // OS -uptr internal_filesize(fd_t fd); // -1 on error. -uptr internal_stat(const char *path, void *buf); -uptr internal_lstat(const char *path, void *buf); -uptr internal_fstat(fd_t fd, void *buf); -uptr internal_dup2(int oldfd, int newfd); -uptr internal_readlink(const char *path, char *buf, uptr bufsize); -uptr internal_unlink(const char *path); -uptr internal_rename(const char *oldpath, const char *newpath); void NORETURN internal__exit(int exitcode); -uptr internal_lseek(fd_t fd, OFF_T offset, int whence); -uptr internal_ptrace(int request, int pid, void *addr, void *data); -uptr internal_waitpid(int pid, int *status, int options); uptr internal_getpid(); uptr internal_getppid(); -int internal_fork(); - // Threading uptr internal_sched_yield(); -// These functions call appropriate pthread_ functions directly, bypassing -// the interceptor. They are weak and may not be present in some tools. -SANITIZER_WEAK_ATTRIBUTE -int real_pthread_create(void *th, void *attr, void *(*callback)(void *), - void *param); -SANITIZER_WEAK_ATTRIBUTE -int real_pthread_join(void *th, void **ret); - -#define DEFINE_REAL_PTHREAD_FUNCTIONS \ - namespace __sanitizer { \ - int real_pthread_create(void *th, void *attr, void *(*callback)(void *), \ - void *param) { \ - return REAL(pthread_create)(th, attr, callback, param); \ - } \ - int real_pthread_join(void *th, void **ret) { \ - return REAL(pthread_join(th, ret)); \ - } \ - } // namespace __sanitizer - // Error handling bool internal_iserror(uptr retval, int *rverrno = 0); -int internal_sigaction(int signum, const void *act, void *oldact); - } // namespace __sanitizer #endif // SANITIZER_LIBC_H diff --git a/contrib/compiler-rt/lib/sanitizer_common/sanitizer_libignore.cc b/contrib/compiler-rt/lib/sanitizer_common/sanitizer_libignore.cc index cefb1dc..8c4aeff 100644 --- a/contrib/compiler-rt/lib/sanitizer_common/sanitizer_libignore.cc +++ b/contrib/compiler-rt/lib/sanitizer_common/sanitizer_libignore.cc @@ -12,6 +12,7 @@ #include "sanitizer_libignore.h" #include "sanitizer_flags.h" +#include "sanitizer_posix.h" #include "sanitizer_procmaps.h" namespace __sanitizer { diff --git a/contrib/compiler-rt/lib/sanitizer_common/sanitizer_linux.cc b/contrib/compiler-rt/lib/sanitizer_common/sanitizer_linux.cc index 8029181..98e5d12 100644 --- a/contrib/compiler-rt/lib/sanitizer_common/sanitizer_linux.cc +++ b/contrib/compiler-rt/lib/sanitizer_common/sanitizer_linux.cc @@ -36,6 +36,7 @@ // access stat from asm/stat.h, without conflicting with definition in // sys/stat.h, we use this trick. #if defined(__mips64) +#include <asm/unistd.h> #include <sys/types.h> #define stat kernel_stat #include <asm/stat.h> @@ -45,9 +46,7 @@ #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> @@ -57,6 +56,7 @@ #include <sys/syscall.h> #include <sys/time.h> #include <sys/types.h> +#include <ucontext.h> #include <unistd.h> #if SANITIZER_FREEBSD @@ -110,7 +110,7 @@ namespace __sanitizer { // --------------- sanitizer_libc.h uptr internal_mmap(void *addr, uptr length, int prot, int flags, int fd, - u64 offset) { + OFF_T offset) { #if SANITIZER_FREEBSD || SANITIZER_LINUX_USES_64BIT_SYSCALLS return internal_syscall(SYSCALL(mmap), (uptr)addr, length, prot, flags, fd, offset); @@ -126,6 +126,10 @@ uptr internal_munmap(void *addr, uptr length) { return internal_syscall(SYSCALL(munmap), (uptr)addr, length); } +int internal_mprotect(void *addr, uptr length, int prot) { + return internal_syscall(SYSCALL(mprotect), (uptr)addr, length, prot); +} + uptr internal_close(fd_t fd) { return internal_syscall(SYSCALL(close), fd); } @@ -147,11 +151,6 @@ uptr internal_open(const char *filename, int flags, u32 mode) { #endif } -uptr OpenFile(const char *filename, bool write) { - return internal_open(filename, - write ? O_RDWR | 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(SYSCALL(read), fd, (uptr)buf, @@ -168,7 +167,8 @@ uptr internal_write(fd_t fd, const void *buf, uptr count) { uptr internal_ftruncate(fd_t fd, uptr size) { sptr res; - HANDLE_EINTR(res, (sptr)internal_syscall(SYSCALL(ftruncate), fd, size)); + HANDLE_EINTR(res, (sptr)internal_syscall(SYSCALL(ftruncate), fd, + (OFF_T)size)); return res; } @@ -558,6 +558,7 @@ int internal_fork() { } #if SANITIZER_LINUX +#define SA_RESTORER 0x04000000 // 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; @@ -570,7 +571,8 @@ int internal_sigaction_norestorer(int signum, const void *act, void *oldact) { 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; + // Without SA_RESTORER kernel ignores the calls (probably returns EINVAL). + k_act.sa_flags = u_act->sa_flags | SA_RESTORER; // FIXME: most often sa_restorer is unset, however the kernel requires it // to point to a valid signal restorer that calls the rt_sigreturn syscall. // If sa_restorer passed to the kernel is NULL, the program may crash upon @@ -704,47 +706,32 @@ uptr GetPageSize() { #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) { - if (proc_self_exe_cache_len > 0) { - // If available, use the cached module name. - uptr module_name_len = - internal_snprintf(buf, buf_len, "%s", proc_self_exe_cache_str); - CHECK_LT(module_name_len, buf_len); - return module_name_len; - } #if SANITIZER_FREEBSD - const int Mib[4] = { CTL_KERN, KERN_PROC, KERN_PROC_PATHNAME, -1 }; + const int Mib[] = { CTL_KERN, KERN_PROC, KERN_PROC_PATHNAME, -1 }; + const char *default_module_name = "kern.proc.pathname"; size_t Size = buf_len; - bool IsErr = (sysctl(Mib, 4, buf, &Size, NULL, 0) != 0); + bool IsErr = (sysctl(Mib, ARRAY_SIZE(Mib), buf, &Size, NULL, 0) != 0); int readlink_error = IsErr ? errno : 0; uptr module_name_len = Size; #else + const char *default_module_name = "/proc/self/exe"; uptr module_name_len = internal_readlink( - "/proc/self/exe", buf, buf_len); + default_module_name, buf, buf_len); int readlink_error; bool IsErr = internal_iserror(module_name_len, &readlink_error); #endif if (IsErr) { - // We can't read /proc/self/exe for some reason, assume the name of the - // binary is unknown. - Report("WARNING: readlink(\"/proc/self/exe\") failed with errno %d, " + // We can't read binary name for some reason, assume it's unknown. + Report("WARNING: reading executable name failed with errno %d, " "some stack frames may not be symbolized\n", readlink_error); - module_name_len = internal_snprintf(buf, buf_len, "/proc/self/exe"); + module_name_len = internal_snprintf(buf, buf_len, "%s", + default_module_name); CHECK_LT(module_name_len, buf_len); } 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; @@ -861,11 +848,70 @@ uptr internal_clone(int (*fn)(void *), void *child_stack, int flags, void *arg, return res; } #elif defined(__mips__) -// TODO(sagarthakur): clone function is to be rewritten in assembly. uptr internal_clone(int (*fn)(void *), void *child_stack, int flags, void *arg, int *parent_tidptr, void *newtls, int *child_tidptr) { - return clone(fn, child_stack, flags, arg, parent_tidptr, - newtls, child_tidptr); + long long res; + if (!fn || !child_stack) + return -EINVAL; + CHECK_EQ(0, (uptr)child_stack % 16); + child_stack = (char *)child_stack - 2 * sizeof(unsigned long long); + ((unsigned long long *)child_stack)[0] = (uptr)fn; + ((unsigned long long *)child_stack)[1] = (uptr)arg; + register void *a3 __asm__("$7") = newtls; + register int *a4 __asm__("$8") = child_tidptr; + // We don't have proper CFI directives here because it requires alot of code + // for very marginal benefits. + __asm__ __volatile__( + /* $v0 = syscall($v0 = __NR_clone, + * $a0 = flags, + * $a1 = child_stack, + * $a2 = parent_tidptr, + * $a3 = new_tls, + * $a4 = child_tidptr) + */ + ".cprestore 16;\n" + "move $4,%1;\n" + "move $5,%2;\n" + "move $6,%3;\n" + "move $7,%4;\n" + /* Store the fifth argument on stack + * if we are using 32-bit abi. + */ +#if SANITIZER_WORDSIZE == 32 + "lw %5,16($29);\n" +#else + "move $8,%5;\n" +#endif + "li $2,%6;\n" + "syscall;\n" + + /* if ($v0 != 0) + * return; + */ + "bnez $2,1f;\n" + + /* Call "fn(arg)". */ + "ld $25,0($29);\n" + "ld $4,8($29);\n" + "jal $25;\n" + + /* Call _exit($v0). */ + "move $4,$2;\n" + "li $2,%7;\n" + "syscall;\n" + + /* Return to parent. */ + "1:\n" + : "=r" (res) + : "r"(flags), + "r"(child_stack), + "r"(parent_tidptr), + "r"(a3), + "r"(a4), + "i"(__NR_clone), + "i"(__NR_exit) + : "memory", "$29" ); + return res; } #endif // defined(__x86_64__) && SANITIZER_LINUX @@ -901,9 +947,52 @@ void GetExtraActivationFlags(char *buf, uptr size) { CHECK(size > PROP_VALUE_MAX); __system_property_get("asan.options", buf); } + +#if __ANDROID_API__ < 21 +extern "C" __attribute__((weak)) int dl_iterate_phdr( + int (*)(struct dl_phdr_info *, size_t, void *), void *); +#endif + +static int dl_iterate_phdr_test_cb(struct dl_phdr_info *info, size_t size, + void *data) { + // Any name starting with "lib" indicates a bug in L where library base names + // are returned instead of paths. + if (info->dlpi_name && info->dlpi_name[0] == 'l' && + info->dlpi_name[1] == 'i' && info->dlpi_name[2] == 'b') { + *(bool *)data = true; + return 1; + } + return 0; +} + +static atomic_uint32_t android_api_level; + +static AndroidApiLevel AndroidDetectApiLevel() { + if (!&dl_iterate_phdr) + return ANDROID_KITKAT; // K or lower + bool base_name_seen = false; + dl_iterate_phdr(dl_iterate_phdr_test_cb, &base_name_seen); + if (base_name_seen) + return ANDROID_LOLLIPOP_MR1; // L MR1 + return ANDROID_POST_LOLLIPOP; // post-L + // Plain L (API level 21) is completely broken wrt ASan and not very + // interesting to detect. +} + +AndroidApiLevel AndroidGetApiLevel() { + AndroidApiLevel level = + (AndroidApiLevel)atomic_load(&android_api_level, memory_order_relaxed); + if (level) return level; + level = AndroidDetectApiLevel(); + atomic_store(&android_api_level, level, memory_order_relaxed); + return level; +} + #endif bool IsDeadlySignal(int signum) { + if (common_flags()->handle_abort && signum == SIGABRT) + return true; return (signum == SIGSEGV || signum == SIGBUS) && common_flags()->handle_segv; } @@ -912,6 +1001,11 @@ void *internal_start_thread(void(*func)(void *arg), void *arg) { // Start the thread with signals blocked, otherwise it can steal user signals. __sanitizer_sigset_t set, old; internal_sigfillset(&set); +#if SANITIZER_LINUX && !SANITIZER_ANDROID + // Glibc uses SIGSETXID signal during setuid call. If this signal is blocked + // on any thread, setuid call hangs (see test/tsan/setuid.c). + internal_sigdelset(&set, 33); +#endif internal_sigprocmask(SIG_SETMASK, &set, &old); void *th; real_pthread_create(&th, 0, (void*(*)(void *arg))func, arg); @@ -928,6 +1022,78 @@ void *internal_start_thread(void (*func)(void *), void *arg) { return 0; } void internal_join_thread(void *th) {} #endif +void GetPcSpBp(void *context, uptr *pc, uptr *sp, uptr *bp) { +#if defined(__arm__) + ucontext_t *ucontext = (ucontext_t*)context; + *pc = ucontext->uc_mcontext.arm_pc; + *bp = ucontext->uc_mcontext.arm_fp; + *sp = ucontext->uc_mcontext.arm_sp; +#elif defined(__aarch64__) + ucontext_t *ucontext = (ucontext_t*)context; + *pc = ucontext->uc_mcontext.pc; + *bp = ucontext->uc_mcontext.regs[29]; + *sp = ucontext->uc_mcontext.sp; +#elif defined(__hppa__) + ucontext_t *ucontext = (ucontext_t*)context; + *pc = ucontext->uc_mcontext.sc_iaoq[0]; + /* GCC uses %r3 whenever a frame pointer is needed. */ + *bp = ucontext->uc_mcontext.sc_gr[3]; + *sp = ucontext->uc_mcontext.sc_gr[30]; +#elif defined(__x86_64__) +# if SANITIZER_FREEBSD + ucontext_t *ucontext = (ucontext_t*)context; + *pc = ucontext->uc_mcontext.mc_rip; + *bp = ucontext->uc_mcontext.mc_rbp; + *sp = ucontext->uc_mcontext.mc_rsp; +# else + ucontext_t *ucontext = (ucontext_t*)context; + *pc = ucontext->uc_mcontext.gregs[REG_RIP]; + *bp = ucontext->uc_mcontext.gregs[REG_RBP]; + *sp = ucontext->uc_mcontext.gregs[REG_RSP]; +# endif +#elif defined(__i386__) +# if SANITIZER_FREEBSD + ucontext_t *ucontext = (ucontext_t*)context; + *pc = ucontext->uc_mcontext.mc_eip; + *bp = ucontext->uc_mcontext.mc_ebp; + *sp = ucontext->uc_mcontext.mc_esp; +# else + ucontext_t *ucontext = (ucontext_t*)context; + *pc = ucontext->uc_mcontext.gregs[REG_EIP]; + *bp = ucontext->uc_mcontext.gregs[REG_EBP]; + *sp = ucontext->uc_mcontext.gregs[REG_ESP]; +# endif +#elif defined(__powerpc__) || defined(__powerpc64__) + ucontext_t *ucontext = (ucontext_t*)context; + *pc = ucontext->uc_mcontext.regs->nip; + *sp = ucontext->uc_mcontext.regs->gpr[PT_R1]; + // The powerpc{,64}-linux ABIs do not specify r31 as the frame + // pointer, but GCC always uses r31 when we need a frame pointer. + *bp = ucontext->uc_mcontext.regs->gpr[PT_R31]; +#elif defined(__sparc__) + ucontext_t *ucontext = (ucontext_t*)context; + uptr *stk_ptr; +# if defined (__arch64__) + *pc = ucontext->uc_mcontext.mc_gregs[MC_PC]; + *sp = ucontext->uc_mcontext.mc_gregs[MC_O6]; + stk_ptr = (uptr *) (*sp + 2047); + *bp = stk_ptr[15]; +# else + *pc = ucontext->uc_mcontext.gregs[REG_PC]; + *sp = ucontext->uc_mcontext.gregs[REG_O6]; + stk_ptr = (uptr *) *sp; + *bp = stk_ptr[15]; +# endif +#elif defined(__mips__) + ucontext_t *ucontext = (ucontext_t*)context; + *pc = ucontext->uc_mcontext.pc; + *bp = ucontext->uc_mcontext.gregs[30]; + *sp = ucontext->uc_mcontext.gregs[29]; +#else +# error "Unsupported arch" +#endif +} + } // namespace __sanitizer #endif // SANITIZER_FREEBSD || SANITIZER_LINUX diff --git a/contrib/compiler-rt/lib/sanitizer_common/sanitizer_linux.h b/contrib/compiler-rt/lib/sanitizer_common/sanitizer_linux.h index b2e603d..e9fc4ad 100644 --- a/contrib/compiler-rt/lib/sanitizer_common/sanitizer_linux.h +++ b/contrib/compiler-rt/lib/sanitizer_common/sanitizer_linux.h @@ -17,6 +17,7 @@ #if SANITIZER_FREEBSD || SANITIZER_LINUX #include "sanitizer_common.h" #include "sanitizer_internal_defs.h" +#include "sanitizer_posix.h" #include "sanitizer_platform_limits_posix.h" struct link_map; // Opaque type returned by dlopen(). @@ -80,11 +81,6 @@ uptr ThreadSelfOffset(); // information). bool LibraryNameIs(const char *full_name, const char *base_name); -// Read the name of the current binary from /proc/self/exe. -uptr ReadBinaryName(/*out*/char *buf, uptr buf_len); -// Cache the value of /proc/self/exe. -void CacheBinaryName(); - // Call cb for each region mapped by map. void ForEachMappedRegion(link_map *map, void (*cb)(const void *, uptr)); } // namespace __sanitizer diff --git a/contrib/compiler-rt/lib/sanitizer_common/sanitizer_linux_libcdep.cc b/contrib/compiler-rt/lib/sanitizer_common/sanitizer_linux_libcdep.cc index c71b625..39eb1d2 100644 --- a/contrib/compiler-rt/lib/sanitizer_common/sanitizer_linux_libcdep.cc +++ b/contrib/compiler-rt/lib/sanitizer_common/sanitizer_linux_libcdep.cc @@ -15,6 +15,7 @@ #include "sanitizer_platform.h" #if SANITIZER_FREEBSD || SANITIZER_LINUX +#include "sanitizer_atomic.h" #include "sanitizer_common.h" #include "sanitizer_flags.h" #include "sanitizer_freebsd.h" @@ -22,13 +23,12 @@ #include "sanitizer_placement_new.h" #include "sanitizer_procmaps.h" #include "sanitizer_stacktrace.h" -#include "sanitizer_atomic.h" -#include "sanitizer_symbolizer.h" #if SANITIZER_ANDROID || SANITIZER_FREEBSD #include <dlfcn.h> // for dlsym() #endif +#include <link.h> #include <pthread.h> #include <signal.h> #include <sys/resource.h> @@ -43,9 +43,12 @@ #include <sys/prctl.h> #endif +#if SANITIZER_ANDROID +#include <android/api-level.h> +#endif + #if !SANITIZER_ANDROID #include <elf.h> -#include <link.h> #include <unistd.h> #endif @@ -398,13 +401,6 @@ void AdjustStackSize(void *attr_) { } } -#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 # if !SANITIZER_FREEBSD typedef ElfW(Phdr) Elf_Phdr; # elif SANITIZER_WORDSIZE == 32 && __FreeBSD_version <= 902001 // v9.2 @@ -429,7 +425,7 @@ static int dl_iterate_phdr_cb(dl_phdr_info *info, size_t size, void *arg) { if (data->first) { data->first = false; // First module is the binary itself. - ReadBinaryName(module_name.data(), module_name.size()); + ReadBinaryNameCached(module_name.data(), module_name.size()); } else if (info->dlpi_name) { module_name.append("%s", info->dlpi_name); } @@ -437,9 +433,8 @@ static int dl_iterate_phdr_cb(dl_phdr_info *info, size_t size, void *arg) { 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); + LoadedModule *cur_module = &data->modules[data->current_n]; + cur_module->set(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]; @@ -453,27 +448,28 @@ static int dl_iterate_phdr_cb(dl_phdr_info *info, size_t size, void *arg) { return 0; } +#if SANITIZER_ANDROID && __ANDROID_API__ < 21 +extern "C" __attribute__((weak)) int dl_iterate_phdr( + int (*)(struct dl_phdr_info *, size_t, void *), void *); +#endif + uptr GetListOfModules(LoadedModule *modules, uptr max_modules, string_predicate_t filter) { +#if SANITIZER_ANDROID && __ANDROID_API__ < 21 + u32 api_level = AndroidGetApiLevel(); + // Fall back to /proc/maps if dl_iterate_phdr is unavailable or broken. + // The runtime check allows the same library to work with + // both K and L (and future) Android releases. + if (api_level <= ANDROID_LOLLIPOP_MR1) { // L or earlier + MemoryMappingLayout memory_mapping(false); + return memory_mapping.DumpListOfModules(modules, max_modules, filter); + } +#endif 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 - -void PrepareForSandboxing(__sanitizer_sandbox_arguments *args) { - // 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 - Symbolizer::GetOrInit()->PrepareForSandboxing(); - CovPrepareForSandboxing(args); -#endif -} // getrusage does not give us the current RSS, only the max RSS. // Still, this is better than nothing if /proc/self/statm is not available @@ -488,8 +484,8 @@ static uptr GetRSSFromGetrusage() { uptr GetRSS() { if (!common_flags()->can_use_proc_maps_statm) return GetRSSFromGetrusage(); - uptr fd = OpenFile("/proc/self/statm", false); - if ((sptr)fd < 0) + fd_t fd = OpenFile("/proc/self/statm", RdOnly); + if (fd == kInvalidFd) return GetRSSFromGetrusage(); char buf[64]; uptr len = internal_read(fd, buf, sizeof(buf) - 1); diff --git a/contrib/compiler-rt/lib/sanitizer_common/sanitizer_mac.cc b/contrib/compiler-rt/lib/sanitizer_common/sanitizer_mac.cc index 39a5c7e..dddce1c 100644 --- a/contrib/compiler-rt/lib/sanitizer_common/sanitizer_mac.cc +++ b/contrib/compiler-rt/lib/sanitizer_common/sanitizer_mac.cc @@ -27,21 +27,30 @@ #include "sanitizer_libc.h" #include "sanitizer_mac.h" #include "sanitizer_placement_new.h" +#include "sanitizer_platform_limits_posix.h" #include "sanitizer_procmaps.h" +#if !SANITIZER_IOS #include <crt_externs.h> // for _NSGetEnviron +#else +extern char **environ; +#endif + +#include <errno.h> #include <fcntl.h> +#include <libkern/OSAtomic.h> +#include <mach-o/dyld.h> +#include <mach/mach.h> #include <pthread.h> #include <sched.h> #include <signal.h> +#include <stdlib.h> #include <sys/mman.h> #include <sys/resource.h> #include <sys/stat.h> #include <sys/sysctl.h> #include <sys/types.h> #include <unistd.h> -#include <libkern/OSAtomic.h> -#include <errno.h> namespace __sanitizer { @@ -57,6 +66,10 @@ uptr internal_munmap(void *addr, uptr length) { return munmap(addr, length); } +int internal_mprotect(void *addr, uptr length, int prot) { + return mprotect(addr, length, prot); +} + uptr internal_close(fd_t fd) { return close(fd); } @@ -69,11 +82,6 @@ uptr internal_open(const char *filename, int flags, u32 mode) { return open(filename, flags, mode); } -uptr OpenFile(const char *filename, bool write) { - return internal_open(filename, - write ? O_WRONLY | O_CREAT : O_RDONLY, 0660); -} - uptr internal_read(fd_t fd, void *buf, uptr count) { return read(fd, buf, count); } @@ -130,6 +138,13 @@ int internal_sigaction(int signum, const void *act, void *oldact) { (struct sigaction *)act, (struct sigaction *)oldact); } +void internal_sigfillset(__sanitizer_sigset_t *set) { sigfillset(set); } + +uptr internal_sigprocmask(int how, __sanitizer_sigset_t *set, + __sanitizer_sigset_t *oldset) { + return sigprocmask(how, set, oldset); +} + int internal_fork() { // TODO(glider): this may call user's pthread_atfork() handlers which is bad. return fork(); @@ -180,7 +195,8 @@ void GetThreadStackTopAndBottom(bool at_initialization, uptr *stack_top, *stack_bottom = *stack_top - stacksize; } -const char *GetEnv(const char *name) { +char **GetEnviron() { +#if !SANITIZER_IOS char ***env_ptr = _NSGetEnviron(); if (!env_ptr) { Report("_NSGetEnviron() returned NULL. Please make sure __asan_init() is " @@ -188,29 +204,45 @@ const char *GetEnv(const char *name) { CHECK(env_ptr); } char **environ = *env_ptr; +#endif CHECK(environ); + return environ; +} + +const char *GetEnv(const char *name) { + char **env = GetEnviron(); uptr name_len = internal_strlen(name); - while (*environ != 0) { - uptr len = internal_strlen(*environ); + while (*env != 0) { + uptr len = internal_strlen(*env); if (len > name_len) { - const char *p = *environ; + const char *p = *env; if (!internal_memcmp(p, name, name_len) && p[name_len] == '=') { // Match. - return *environ + name_len + 1; // String starting after =. + return *env + name_len + 1; // String starting after =. } } - environ++; + env++; } return 0; } -void ReExec() { - UNIMPLEMENTED(); +uptr ReadBinaryName(/*out*/char *buf, uptr buf_len) { + CHECK_LE(kMaxPathLength, buf_len); + + // On OS X the executable path is saved to the stack by dyld. Reading it + // from there is much faster than calling dladdr, especially for large + // binaries with symbols. + InternalScopedString exe_path(kMaxPathLength); + uint32_t size = exe_path.size(); + if (_NSGetExecutablePath(exe_path.data(), &size) == 0 && + realpath(exe_path.data(), buf) != 0) { + return internal_strlen(buf); + } + return 0; } -void PrepareForSandboxing(__sanitizer_sandbox_arguments *args) { - (void)args; - // Nothing here for now. +void ReExec() { + UNIMPLEMENTED(); } uptr GetPageSize() { @@ -322,12 +354,47 @@ MacosVersion GetMacosVersion() { } uptr GetRSS() { - return 0; + struct task_basic_info info; + unsigned count = TASK_BASIC_INFO_COUNT; + kern_return_t result = + task_info(mach_task_self(), TASK_BASIC_INFO, (task_info_t)&info, &count); + if (UNLIKELY(result != KERN_SUCCESS)) { + Report("Cannot get task info. Error: %d\n", result); + Die(); + } + return info.resident_size; } void *internal_start_thread(void (*func)(void *arg), void *arg) { return 0; } void internal_join_thread(void *th) { } +void GetPcSpBp(void *context, uptr *pc, uptr *sp, uptr *bp) { + ucontext_t *ucontext = (ucontext_t*)context; +# if defined(__aarch64__) + *pc = ucontext->uc_mcontext->__ss.__pc; +# if defined(__IPHONE_8_0) && __IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_8_0 + *bp = ucontext->uc_mcontext->__ss.__fp; +# else + *bp = ucontext->uc_mcontext->__ss.__lr; +# endif + *sp = ucontext->uc_mcontext->__ss.__sp; +# elif defined(__x86_64__) + *pc = ucontext->uc_mcontext->__ss.__rip; + *bp = ucontext->uc_mcontext->__ss.__rbp; + *sp = ucontext->uc_mcontext->__ss.__rsp; +# elif defined(__arm__) + *pc = ucontext->uc_mcontext->__ss.__pc; + *bp = ucontext->uc_mcontext->__ss.__r[7]; + *sp = ucontext->uc_mcontext->__ss.__sp; +# elif defined(__i386__) + *pc = ucontext->uc_mcontext->__ss.__eip; + *bp = ucontext->uc_mcontext->__ss.__ebp; + *sp = ucontext->uc_mcontext->__ss.__esp; +# else +# error "Unknown architecture" +# endif +} + } // namespace __sanitizer #endif // SANITIZER_MAC diff --git a/contrib/compiler-rt/lib/sanitizer_common/sanitizer_mac.h b/contrib/compiler-rt/lib/sanitizer_common/sanitizer_mac.h index 9eed905..50dbe93 100644 --- a/contrib/compiler-rt/lib/sanitizer_common/sanitizer_mac.h +++ b/contrib/compiler-rt/lib/sanitizer_common/sanitizer_mac.h @@ -15,6 +15,7 @@ #include "sanitizer_platform.h" #if SANITIZER_MAC +#include "sanitizer_posix.h" namespace __sanitizer { @@ -32,6 +33,8 @@ enum MacosVersion { MacosVersion GetMacosVersion(); +char **GetEnviron(); + } // namespace __sanitizer #endif // SANITIZER_MAC diff --git a/contrib/compiler-rt/lib/sanitizer_common/sanitizer_platform.h b/contrib/compiler-rt/lib/sanitizer_common/sanitizer_platform.h index 6f8cd30..b47281b 100644 --- a/contrib/compiler-rt/lib/sanitizer_common/sanitizer_platform.h +++ b/contrib/compiler-rt/lib/sanitizer_common/sanitizer_platform.h @@ -38,9 +38,15 @@ # else # define SANITIZER_IOS 0 # endif +# if TARGET_IPHONE_SIMULATOR +# define SANITIZER_IOSSIM 1 +# else +# define SANITIZER_IOSSIM 0 +# endif #else # define SANITIZER_MAC 0 # define SANITIZER_IOS 0 +# define SANITIZER_IOSSIM 0 #endif #if defined(_WIN32) @@ -111,10 +117,29 @@ # endif #endif +// udi16 syscalls can only be used when the following conditions are +// met: +// * target is one of arm32, x86-32, sparc32, sh or m68k +// * libc version is libc5, glibc-2.0, glibc-2.1 or glibc-2.2 to 2.15 +// built against > linux-2.2 kernel headers +// Since we don't want to include libc headers here, we check the +// target only. +#if defined(__arm__) || SANITIZER_X32 || defined(__sparc__) +#define SANITIZER_USES_UID16_SYSCALLS 1 +#else +#define SANITIZER_USES_UID16_SYSCALLS 0 +#endif + #ifdef __mips__ # define SANITIZER_POINTER_FORMAT_LENGTH FIRST_32_SECOND_64(8, 10) #else # define SANITIZER_POINTER_FORMAT_LENGTH FIRST_32_SECOND_64(8, 12) #endif +// Assume obsolete RPC headers are available by default +#if !defined(HAVE_RPC_XDR_H) && !defined(HAVE_TIRPC_RPC_XDR_H) +# define HAVE_RPC_XDR_H (SANITIZER_LINUX && !SANITIZER_ANDROID) +# define HAVE_TIRPC_RPC_XDR_H 0 +#endif + #endif // SANITIZER_PLATFORM_H diff --git a/contrib/compiler-rt/lib/sanitizer_common/sanitizer_platform_interceptors.h b/contrib/compiler-rt/lib/sanitizer_common/sanitizer_platform_interceptors.h index 438ecba..77cc84c 100644 --- a/contrib/compiler-rt/lib/sanitizer_common/sanitizer_platform_interceptors.h +++ b/contrib/compiler-rt/lib/sanitizer_common/sanitizer_platform_interceptors.h @@ -54,6 +54,10 @@ #endif #define SANITIZER_INTERCEPT_STRCMP 1 +#define SANITIZER_INTERCEPT_STRSTR 1 +#define SANITIZER_INTERCEPT_STRCASESTR SI_NOT_WINDOWS +#define SANITIZER_INTERCEPT_STRSPN 1 +#define SANITIZER_INTERCEPT_STRPBRK 1 #define SANITIZER_INTERCEPT_TEXTDOMAIN SI_LINUX_NOT_ANDROID #define SANITIZER_INTERCEPT_STRCASECMP SI_NOT_WINDOWS #define SANITIZER_INTERCEPT_MEMCHR 1 @@ -127,7 +131,8 @@ #define SANITIZER_INTERCEPT_READDIR SI_NOT_WINDOWS #define SANITIZER_INTERCEPT_READDIR64 SI_LINUX_NOT_ANDROID #define SANITIZER_INTERCEPT_PTRACE SI_LINUX_NOT_ANDROID && \ - (defined(__i386) || defined (__x86_64) || defined (__mips64)) // NOLINT + (defined(__i386) || defined(__x86_64) || defined(__mips64) || \ + defined(__powerpc64__)) #define SANITIZER_INTERCEPT_SETLOCALE SI_NOT_WINDOWS #define SANITIZER_INTERCEPT_GETCWD SI_NOT_WINDOWS #define SANITIZER_INTERCEPT_GET_CURRENT_DIR_NAME SI_LINUX_NOT_ANDROID @@ -147,7 +152,8 @@ #define SANITIZER_INTERCEPT_STRERROR SI_NOT_WINDOWS #define SANITIZER_INTERCEPT_STRERROR_R SI_NOT_WINDOWS #define SANITIZER_INTERCEPT_XPG_STRERROR_R SI_LINUX_NOT_ANDROID -#define SANITIZER_INTERCEPT_SCANDIR SI_LINUX_NOT_ANDROID +#define SANITIZER_INTERCEPT_SCANDIR \ + SI_FREEBSD || SI_LINUX_NOT_ANDROID #define SANITIZER_INTERCEPT_SCANDIR64 SI_LINUX_NOT_ANDROID #define SANITIZER_INTERCEPT_GETGROUPS SI_NOT_WINDOWS #define SANITIZER_INTERCEPT_POLL SI_NOT_WINDOWS @@ -161,7 +167,7 @@ SI_FREEBSD || SI_MAC || SI_LINUX_NOT_ANDROID #define SANITIZER_INTERCEPT_SIGPENDING SI_NOT_WINDOWS #define SANITIZER_INTERCEPT_SIGPROCMASK SI_NOT_WINDOWS -#define SANITIZER_INTERCEPT_BACKTRACE SI_LINUX_NOT_ANDROID +#define SANITIZER_INTERCEPT_BACKTRACE SI_FREEBSD || SI_LINUX_NOT_ANDROID #define SANITIZER_INTERCEPT_GETMNTENT SI_LINUX #define SANITIZER_INTERCEPT_GETMNTENT_R SI_LINUX_NOT_ANDROID #define SANITIZER_INTERCEPT_STATFS SI_FREEBSD || SI_MAC || SI_LINUX_NOT_ANDROID @@ -203,12 +209,13 @@ #define SANITIZER_INTERCEPT_LGAMMA_R SI_FREEBSD || SI_LINUX #define SANITIZER_INTERCEPT_LGAMMAL_R SI_LINUX_NOT_ANDROID #define SANITIZER_INTERCEPT_DRAND48_R SI_LINUX_NOT_ANDROID -#define SANITIZER_INTERCEPT_RAND_R SI_MAC || SI_LINUX_NOT_ANDROID -#define SANITIZER_INTERCEPT_ICONV SI_LINUX_NOT_ANDROID +#define SANITIZER_INTERCEPT_RAND_R \ + SI_FREEBSD || SI_MAC || SI_LINUX_NOT_ANDROID +#define SANITIZER_INTERCEPT_ICONV SI_FREEBSD || SI_LINUX_NOT_ANDROID #define SANITIZER_INTERCEPT_TIMES SI_NOT_WINDOWS // FIXME: getline seems to be available on OSX 10.7 -#define SANITIZER_INTERCEPT_GETLINE SI_LINUX_NOT_ANDROID +#define SANITIZER_INTERCEPT_GETLINE SI_FREEBSD || SI_LINUX_NOT_ANDROID #define SANITIZER_INTERCEPT__EXIT SI_LINUX || SI_FREEBSD @@ -222,12 +229,14 @@ #define SANITIZER_INTERCEPT_LISTXATTR SI_LINUX #define SANITIZER_INTERCEPT_GETXATTR SI_LINUX #define SANITIZER_INTERCEPT_GETRESID SI_LINUX -#define SANITIZER_INTERCEPT_GETIFADDRS SI_LINUX_NOT_ANDROID || SI_MAC -#define SANITIZER_INTERCEPT_IF_INDEXTONAME SI_LINUX_NOT_ANDROID || SI_MAC +#define SANITIZER_INTERCEPT_GETIFADDRS \ + SI_FREEBSD || SI_LINUX_NOT_ANDROID || SI_MAC +#define SANITIZER_INTERCEPT_IF_INDEXTONAME \ + SI_FREEBSD || SI_LINUX_NOT_ANDROID || SI_MAC #define SANITIZER_INTERCEPT_CAPGET SI_LINUX_NOT_ANDROID #define SANITIZER_INTERCEPT_AEABI_MEM SI_LINUX && defined(__arm__) #define SANITIZER_INTERCEPT___BZERO SI_MAC -#define SANITIZER_INTERCEPT_FTIME SI_NOT_WINDOWS +#define SANITIZER_INTERCEPT_FTIME !SI_FREEBSD && SI_NOT_WINDOWS #define SANITIZER_INTERCEPT_XDR SI_LINUX_NOT_ANDROID #define SANITIZER_INTERCEPT_TSEARCH SI_LINUX_NOT_ANDROID || SI_MAC #define SANITIZER_INTERCEPT_LIBIO_INTERNALS SI_LINUX_NOT_ANDROID @@ -243,5 +252,6 @@ #define SANITIZER_INTERCEPT_TIMERFD SI_LINUX_NOT_ANDROID #define SANITIZER_INTERCEPT_MLOCKX SI_NOT_WINDOWS +#define SANITIZER_INTERCEPT_FOPENCOOKIE SI_LINUX_NOT_ANDROID #endif // #ifndef SANITIZER_PLATFORM_INTERCEPTORS_H diff --git a/contrib/compiler-rt/lib/sanitizer_common/sanitizer_platform_limits_posix.cc b/contrib/compiler-rt/lib/sanitizer_common/sanitizer_platform_limits_posix.cc index 8824c80..aaa37ed 100644 --- a/contrib/compiler-rt/lib/sanitizer_common/sanitizer_platform_limits_posix.cc +++ b/contrib/compiler-rt/lib/sanitizer_common/sanitizer_platform_limits_posix.cc @@ -34,8 +34,6 @@ #include <grp.h> #include <limits.h> #include <net/if.h> -#include <net/if_arp.h> -#include <net/route.h> #include <netdb.h> #include <poll.h> #include <pthread.h> @@ -54,6 +52,10 @@ #include <time.h> #include <wchar.h> +#if !SANITIZER_IOS +#include <net/route.h> +#endif + #if !SANITIZER_ANDROID #include <sys/mount.h> #include <sys/timeb.h> @@ -75,6 +77,7 @@ #include <linux/sysctl.h> #include <linux/utsname.h> #include <linux/posix_types.h> +#include <net/if_arp.h> #endif #if SANITIZER_FREEBSD @@ -135,7 +138,11 @@ #include <netax25/ax25.h> #include <netipx/ipx.h> #include <netrom/netrom.h> -#include <rpc/xdr.h> +#if HAVE_RPC_XDR_H +# include <rpc/xdr.h> +#elif HAVE_TIRPC_RPC_XDR_H +# include <tirpc/rpc/xdr.h> +#endif #include <scsi/scsi.h> #include <sys/mtio.h> #include <sys/kd.h> @@ -289,19 +296,20 @@ namespace __sanitizer { #endif #if SANITIZER_LINUX && !SANITIZER_ANDROID && \ - (defined(__i386) || defined(__x86_64) || defined(__mips64)) -#if defined(__mips64) + (defined(__i386) || defined(__x86_64) || defined(__mips64) || \ + defined(__powerpc64__)) +#if defined(__mips64) || defined(__powerpc64__) unsigned struct_user_regs_struct_sz = sizeof(struct pt_regs); unsigned struct_user_fpregs_struct_sz = sizeof(elf_fpregset_t); #else unsigned struct_user_regs_struct_sz = sizeof(struct user_regs_struct); unsigned struct_user_fpregs_struct_sz = sizeof(struct user_fpregs_struct); -#endif // __mips64 -#if (defined(__x86_64) || defined(__mips64)) +#endif // __mips64 || __powerpc64__ +#if defined(__x86_64) || defined(__mips64) || defined(__powerpc64__) unsigned struct_user_fpxregs_struct_sz = 0; #else unsigned struct_user_fpxregs_struct_sz = sizeof(struct user_fpxregs_struct); -#endif // __x86_64 || __mips64 +#endif // __x86_64 || __mips64 || __powerpc64__ int ptrace_peektext = PTRACE_PEEKTEXT; int ptrace_peekdata = PTRACE_PEEKDATA; @@ -310,8 +318,13 @@ namespace __sanitizer { int ptrace_setregs = PTRACE_SETREGS; int ptrace_getfpregs = PTRACE_GETFPREGS; int ptrace_setfpregs = PTRACE_SETFPREGS; +#if defined(PTRACE_GETFPXREGS) && defined(PTRACE_SETFPXREGS) int ptrace_getfpxregs = PTRACE_GETFPXREGS; int ptrace_setfpxregs = PTRACE_SETFPXREGS; +#else + int ptrace_getfpxregs = -1; + int ptrace_setfpxregs = -1; +#endif // PTRACE_GETFPXREGS/PTRACE_SETFPXREGS int ptrace_geteventmsg = PTRACE_GETEVENTMSG; #if (defined(PTRACE_GETSIGINFO) && defined(PTRACE_SETSIGINFO)) || \ (defined(PT_GETSIGINFO) && defined(PT_SETSIGINFO)) @@ -333,12 +346,12 @@ namespace __sanitizer { unsigned path_max = PATH_MAX; // ioctl arguments - unsigned struct_arpreq_sz = sizeof(struct arpreq); unsigned struct_ifreq_sz = sizeof(struct ifreq); unsigned struct_termios_sz = sizeof(struct termios); unsigned struct_winsize_sz = sizeof(struct winsize); #if SANITIZER_LINUX + unsigned struct_arpreq_sz = sizeof(struct arpreq); unsigned struct_cdrom_msf_sz = sizeof(struct cdrom_msf); unsigned struct_cdrom_multisession_sz = sizeof(struct cdrom_multisession); unsigned struct_cdrom_read_audio_sz = sizeof(struct cdrom_read_audio); @@ -1016,7 +1029,7 @@ CHECK_SIZE_AND_OFFSET(__sysctl_args, newlen); CHECK_TYPE_SIZE(__kernel_uid_t); CHECK_TYPE_SIZE(__kernel_gid_t); -#if !defined(__aarch64__) +#if SANITIZER_USES_UID16_SYSCALLS CHECK_TYPE_SIZE(__kernel_old_uid_t); CHECK_TYPE_SIZE(__kernel_old_gid_t); #endif @@ -1159,7 +1172,7 @@ CHECK_SIZE_AND_OFFSET(group, gr_passwd); CHECK_SIZE_AND_OFFSET(group, gr_gid); CHECK_SIZE_AND_OFFSET(group, gr_mem); -#if SANITIZER_LINUX && !SANITIZER_ANDROID +#if HAVE_RPC_XDR_H || HAVE_TIRPC_RPC_XDR_H CHECK_TYPE_SIZE(XDR); CHECK_SIZE_AND_OFFSET(XDR, x_op); CHECK_SIZE_AND_OFFSET(XDR, x_ops); @@ -1200,6 +1213,12 @@ CHECK_SIZE_AND_OFFSET(obstack, chunk_size); CHECK_SIZE_AND_OFFSET(obstack, chunk); CHECK_SIZE_AND_OFFSET(obstack, object_base); CHECK_SIZE_AND_OFFSET(obstack, next_free); + +CHECK_TYPE_SIZE(cookie_io_functions_t); +CHECK_SIZE_AND_OFFSET(cookie_io_functions_t, read); +CHECK_SIZE_AND_OFFSET(cookie_io_functions_t, write); +CHECK_SIZE_AND_OFFSET(cookie_io_functions_t, seek); +CHECK_SIZE_AND_OFFSET(cookie_io_functions_t, close); #endif #endif // SANITIZER_LINUX || SANITIZER_FREEBSD || SANITIZER_MAC diff --git a/contrib/compiler-rt/lib/sanitizer_common/sanitizer_platform_limits_posix.h b/contrib/compiler-rt/lib/sanitizer_common/sanitizer_platform_limits_posix.h index bd20bea..4da7c70 100644 --- a/contrib/compiler-rt/lib/sanitizer_common/sanitizer_platform_limits_posix.h +++ b/contrib/compiler-rt/lib/sanitizer_common/sanitizer_platform_limits_posix.h @@ -321,7 +321,7 @@ namespace __sanitizer { long pw_change; char *pw_class; #endif -#if !SANITIZER_ANDROID +#if !(SANITIZER_ANDROID && (SANITIZER_WORDSIZE == 32)) char *pw_gecos; #endif char *pw_dir; @@ -383,7 +383,7 @@ namespace __sanitizer { }; #endif -#if SANITIZER_ANDROID || SANITIZER_MAC || SANITIZER_FREEBSD +#if SANITIZER_MAC || SANITIZER_FREEBSD struct __sanitizer_msghdr { void *msg_name; unsigned msg_namelen; @@ -520,6 +520,27 @@ namespace __sanitizer { #endif // Linux system headers define the 'sa_handler' and 'sa_sigaction' macros. +#if SANITIZER_ANDROID && (SANITIZER_WORDSIZE == 64) + struct __sanitizer_sigaction { + unsigned sa_flags; + union { + void (*sigaction)(int sig, void *siginfo, void *uctx); + void (*handler)(int sig); + }; + __sanitizer_sigset_t sa_mask; + void (*sa_restorer)(); + }; +#elif SANITIZER_ANDROID && (SANITIZER_WORDSIZE == 32) + struct __sanitizer_sigaction { + union { + void (*sigaction)(int sig, void *siginfo, void *uctx); + void (*handler)(int sig); + }; + __sanitizer_sigset_t sa_mask; + uptr sa_flags; + void (*sa_restorer)(); + }; +#else // !SANITIZER_ANDROID struct __sanitizer_sigaction { #if defined(__mips__) && !SANITIZER_FREEBSD unsigned int sa_flags; @@ -544,6 +565,7 @@ namespace __sanitizer { int sa_resv[1]; #endif }; +#endif // !SANITIZER_ANDROID #if SANITIZER_FREEBSD typedef __sanitizer_sigset_t __sanitizer_kernel_sigset_t; @@ -699,7 +721,8 @@ namespace __sanitizer { #endif #if SANITIZER_LINUX && !SANITIZER_ANDROID && \ - (defined(__i386) || defined(__x86_64) || defined(__mips64)) + (defined(__i386) || defined(__x86_64) || defined(__mips64) || \ + defined(__powerpc64__)) extern unsigned struct_user_regs_struct_sz; extern unsigned struct_user_fpregs_struct_sz; extern unsigned struct_user_fpxregs_struct_sz; @@ -756,6 +779,20 @@ struct __sanitizer_obstack { char *next_free; uptr more_fields[7]; }; + +typedef uptr (*__sanitizer_cookie_io_read)(void *cookie, char *buf, uptr size); +typedef uptr (*__sanitizer_cookie_io_write)(void *cookie, const char *buf, + uptr size); +typedef int (*__sanitizer_cookie_io_seek)(void *cookie, u64 *offset, + int whence); +typedef int (*__sanitizer_cookie_io_close)(void *cookie); + +struct __sanitizer_cookie_io_functions_t { + __sanitizer_cookie_io_read read; + __sanitizer_cookie_io_write write; + __sanitizer_cookie_io_seek seek; + __sanitizer_cookie_io_close close; +}; #endif #define IOC_NRBITS 8 @@ -792,12 +829,12 @@ struct __sanitizer_obstack { #define IOC_NR(nr) (((nr) >> IOC_NRSHIFT) & IOC_NRMASK) #define IOC_SIZE(nr) (((nr) >> IOC_SIZESHIFT) & IOC_SIZEMASK) - extern unsigned struct_arpreq_sz; extern unsigned struct_ifreq_sz; extern unsigned struct_termios_sz; extern unsigned struct_winsize_sz; #if SANITIZER_LINUX + extern unsigned struct_arpreq_sz; extern unsigned struct_cdrom_msf_sz; extern unsigned struct_cdrom_multisession_sz; extern unsigned struct_cdrom_read_audio_sz; diff --git a/contrib/compiler-rt/lib/sanitizer_common/sanitizer_posix.cc b/contrib/compiler-rt/lib/sanitizer_common/sanitizer_posix.cc index 5bc41c2..abf6738 100644 --- a/contrib/compiler-rt/lib/sanitizer_common/sanitizer_posix.cc +++ b/contrib/compiler-rt/lib/sanitizer_common/sanitizer_posix.cc @@ -9,7 +9,7 @@ // // This file is shared between AddressSanitizer and ThreadSanitizer // run-time libraries and implements POSIX-specific functions from -// sanitizer_libc.h. +// sanitizer_posix.h. //===----------------------------------------------------------------------===// #include "sanitizer_platform.h" @@ -17,9 +17,12 @@ #include "sanitizer_common.h" #include "sanitizer_libc.h" +#include "sanitizer_posix.h" #include "sanitizer_procmaps.h" #include "sanitizer_stacktrace.h" +#include <fcntl.h> +#include <signal.h> #include <sys/mman.h> #if SANITIZER_LINUX @@ -47,7 +50,7 @@ uptr GetMmapGranularity() { #if SANITIZER_WORDSIZE == 32 // Take care of unusable kernel area in top gigabyte. static uptr GetKernelAreaSize() { -#if SANITIZER_LINUX +#if SANITIZER_LINUX && !SANITIZER_X32 const uptr gbyte = 1UL << 30; // Firstly check if there are writable segments @@ -79,7 +82,7 @@ static uptr GetKernelAreaSize() { return gbyte; #else return 0; -#endif // SANITIZER_LINUX +#endif // SANITIZER_LINUX && !SANITIZER_X32 } #endif // SANITIZER_WORDSIZE == 32 @@ -162,22 +165,6 @@ void *MmapNoReserveOrDie(uptr size, const char *mem_type) { 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 %zx (errno: %d)\n", - SanitizerToolName, size, size, fixed_addr, reserrno); - IncreaseTotalMmap(size); - return (void *)p; -} - void *MmapFixedOrDie(uptr fixed_addr, uptr size) { uptr PageSize = GetPageSizeCached(); uptr p = internal_mmap((void*)(fixed_addr & ~(PageSize - 1)), @@ -196,17 +183,55 @@ void *MmapFixedOrDie(uptr fixed_addr, uptr size) { 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); +bool MprotectNoAccess(uptr addr, uptr size) { + return 0 == internal_mprotect((void*)addr, size, PROT_NONE); +} + +fd_t OpenFile(const char *filename, FileAccessMode mode, error_t *errno_p) { + int flags; + switch (mode) { + case RdOnly: flags = O_RDONLY; break; + case WrOnly: flags = O_WRONLY | O_CREAT; break; + case RdWr: flags = O_RDWR | O_CREAT; break; + } + fd_t res = internal_open(filename, flags, 0660); + if (internal_iserror(res, errno_p)) + return kInvalidFd; + return res; +} + +void CloseFile(fd_t fd) { + internal_close(fd); +} + +bool ReadFromFile(fd_t fd, void *buff, uptr buff_size, uptr *bytes_read, + error_t *error_p) { + uptr res = internal_read(fd, buff, buff_size); + if (internal_iserror(res, error_p)) + return false; + if (bytes_read) + *bytes_read = res; + return true; +} + +bool WriteToFile(fd_t fd, const void *buff, uptr buff_size, uptr *bytes_written, + error_t *error_p) { + uptr res = internal_write(fd, buff, buff_size); + if (internal_iserror(res, error_p)) + return false; + if (bytes_written) + *bytes_written = res; + return true; +} + +bool RenameFile(const char *oldpath, const char *newpath, error_t *error_p) { + uptr res = internal_rename(oldpath, newpath); + return !internal_iserror(res, error_p); } void *MapFileToMemory(const char *file_name, uptr *buff_size) { - uptr openrv = OpenFile(file_name, false); - CHECK(!internal_iserror(openrv)); - fd_t fd = openrv; + fd_t fd = OpenFile(file_name, RdOnly); + CHECK(fd != kInvalidFd); uptr fsize = internal_filesize(fd); CHECK_NE(fsize, (uptr)-1); CHECK_GT(fsize, 0); @@ -215,13 +240,14 @@ void *MapFileToMemory(const char *file_name, uptr *buff_size) { return internal_iserror(map) ? 0 : (void *)map; } -void *MapWritableFileToMemory(void *addr, uptr size, uptr fd, uptr offset) { +void *MapWritableFileToMemory(void *addr, uptr size, fd_t fd, OFF_T offset) { uptr flags = MAP_SHARED; if (addr) flags |= MAP_FIXED; uptr p = internal_mmap(addr, size, PROT_READ | PROT_WRITE, flags, fd, offset); - if (internal_iserror(p)) { - Printf("could not map writable file (%zd, %zu, %zu): %zd\n", fd, offset, - size, p); + int mmap_errno = 0; + if (internal_iserror(p, &mmap_errno)) { + Printf("could not map writable file (%d, %lld, %zu): %zd, errno: %d\n", + fd, (long long)offset, size, p, mmap_errno); return 0; } return (void *)p; @@ -244,6 +270,7 @@ bool MemoryRangeIsAvailable(uptr range_start, uptr range_end) { while (proc_maps.Next(&start, &end, /*offset*/0, /*filename*/0, /*filename_size*/0, /*protection*/0)) { + if (start == end) continue; // Empty range. CHECK_NE(0, end); if (!IntervalsAreSeparate(start, end - 1, range_start, range_end)) return false; @@ -293,6 +320,14 @@ char *FindPathToBinary(const char *name) { return 0; } +bool IsPathSeparator(const char c) { + return c == '/'; +} + +bool IsAbsolutePath(const char *path) { + return path != nullptr && IsPathSeparator(path[0]); +} + void ReportFile::Write(const char *buffer, uptr length) { SpinMutexLock l(mu); static const char *kWriteError = @@ -319,6 +354,13 @@ bool GetCodeRangeForFile(const char *module, uptr *start, uptr *end) { return false; } +SignalContext SignalContext::Create(void *siginfo, void *context) { + uptr addr = (uptr)((siginfo_t*)siginfo)->si_addr; + uptr pc, sp, bp; + GetPcSpBp(context, &pc, &sp, &bp); + return SignalContext(context, addr, pc, sp, bp); +} + } // namespace __sanitizer #endif // SANITIZER_POSIX diff --git a/contrib/compiler-rt/lib/sanitizer_common/sanitizer_posix.h b/contrib/compiler-rt/lib/sanitizer_common/sanitizer_posix.h new file mode 100644 index 0000000..5a9e97d --- /dev/null +++ b/contrib/compiler-rt/lib/sanitizer_common/sanitizer_posix.h @@ -0,0 +1,81 @@ +//===-- sanitizer_posix.h -------------------------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file is shared between AddressSanitizer and ThreadSanitizer +// run-time libraries and declares some useful POSIX-specific functions. +//===----------------------------------------------------------------------===// +#ifndef SANITIZER_POSIX_H +#define SANITIZER_POSIX_H + +// ----------- ATTENTION ------------- +// This header should NOT include any other headers from sanitizer runtime. +#include "sanitizer_internal_defs.h" + +#if !SANITIZER_POSIX +// Make it hard to accidentally use any of functions declared in this file: +#error This file should only be included on POSIX +#endif + +namespace __sanitizer { + +// I/O +// Don't use directly, use __sanitizer::OpenFile() instead. +uptr internal_open(const char *filename, int flags); +uptr internal_open(const char *filename, int flags, u32 mode); +uptr internal_close(fd_t fd); + +uptr internal_read(fd_t fd, void *buf, uptr count); +uptr internal_write(fd_t fd, const void *buf, uptr count); + +// Memory +uptr internal_mmap(void *addr, uptr length, int prot, int flags, + int fd, OFF_T offset); +uptr internal_munmap(void *addr, uptr length); +int internal_mprotect(void *addr, uptr length, int prot); + +// OS +uptr internal_filesize(fd_t fd); // -1 on error. +uptr internal_stat(const char *path, void *buf); +uptr internal_lstat(const char *path, void *buf); +uptr internal_fstat(fd_t fd, void *buf); +uptr internal_dup2(int oldfd, int newfd); +uptr internal_readlink(const char *path, char *buf, uptr bufsize); +uptr internal_unlink(const char *path); +uptr internal_rename(const char *oldpath, const char *newpath); +uptr internal_lseek(fd_t fd, OFF_T offset, int whence); + +uptr internal_ptrace(int request, int pid, void *addr, void *data); +uptr internal_waitpid(int pid, int *status, int options); + +int internal_fork(); + +// These functions call appropriate pthread_ functions directly, bypassing +// the interceptor. They are weak and may not be present in some tools. +SANITIZER_WEAK_ATTRIBUTE +int real_pthread_create(void *th, void *attr, void *(*callback)(void *), + void *param); +SANITIZER_WEAK_ATTRIBUTE +int real_pthread_join(void *th, void **ret); + +#define DEFINE_REAL_PTHREAD_FUNCTIONS \ + namespace __sanitizer { \ + int real_pthread_create(void *th, void *attr, void *(*callback)(void *), \ + void *param) { \ + return REAL(pthread_create)(th, attr, callback, param); \ + } \ + int real_pthread_join(void *th, void **ret) { \ + return REAL(pthread_join(th, ret)); \ + } \ + } // namespace __sanitizer + +int internal_sigaction(int signum, const void *act, void *oldact); + +} // namespace __sanitizer + +#endif // SANITIZER_POSIX_H diff --git a/contrib/compiler-rt/lib/sanitizer_common/sanitizer_posix_libcdep.cc b/contrib/compiler-rt/lib/sanitizer_common/sanitizer_posix_libcdep.cc index 11828e6..3f0a4f4 100644 --- a/contrib/compiler-rt/lib/sanitizer_common/sanitizer_posix_libcdep.cc +++ b/contrib/compiler-rt/lib/sanitizer_common/sanitizer_posix_libcdep.cc @@ -18,9 +18,13 @@ #include "sanitizer_common.h" #include "sanitizer_flags.h" #include "sanitizer_platform_limits_posix.h" +#include "sanitizer_posix.h" +#include "sanitizer_procmaps.h" #include "sanitizer_stacktrace.h" +#include "sanitizer_symbolizer.h" #include <errno.h> +#include <fcntl.h> #include <pthread.h> #include <signal.h> #include <stdlib.h> @@ -28,8 +32,16 @@ #include <sys/resource.h> #include <sys/time.h> #include <sys/types.h> +#include <sys/stat.h> #include <unistd.h> +#if SANITIZER_FREEBSD +// The MAP_NORESERVE define has been removed in FreeBSD 11.x, and even before +// that, it was never implemented. So just define it to zero. +#undef MAP_NORESERVE +#define MAP_NORESERVE 0 +#endif + namespace __sanitizer { u32 GetUid() { @@ -119,8 +131,8 @@ int Atexit(void (*function)(void)) { #endif } -int internal_isatty(fd_t fd) { - return isatty(fd); +bool SupportsColoredOutput(fd_t fd) { + return isatty(fd) != 0; } #ifndef SANITIZER_GO @@ -175,6 +187,7 @@ void InstallDeadlySignalHandlers(SignalHandlerType handler) { if (common_flags()->use_sigaltstack) SetAlternateSignalStack(); MaybeInstallSigaction(SIGSEGV, handler); MaybeInstallSigaction(SIGBUS, handler); + MaybeInstallSigaction(SIGABRT, handler); } #endif // SANITIZER_GO @@ -200,6 +213,68 @@ bool IsAccessibleMemoryRange(uptr beg, uptr size) { return result; } +void PrepareForSandboxing(__sanitizer_sandbox_arguments *args) { + // 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 + Symbolizer::GetOrInit()->PrepareForSandboxing(); + CovPrepareForSandboxing(args); +#endif +} + +#if SANITIZER_ANDROID +int GetNamedMappingFd(const char *name, uptr size) { + return -1; +} +#else +int GetNamedMappingFd(const char *name, uptr size) { + if (!common_flags()->decorate_proc_maps) + return -1; + char shmname[200]; + CHECK(internal_strlen(name) < sizeof(shmname) - 10); + internal_snprintf(shmname, sizeof(shmname), "%zu [%s]", internal_getpid(), + name); + int fd = shm_open(shmname, O_RDWR | O_CREAT | O_TRUNC, S_IRWXU); + CHECK_GE(fd, 0); + int res = internal_ftruncate(fd, size); + CHECK_EQ(0, res); + res = shm_unlink(shmname); + CHECK_EQ(0, res); + return fd; +} +#endif + +void *MmapFixedNoReserve(uptr fixed_addr, uptr size, const char *name) { + int fd = name ? GetNamedMappingFd(name, size) : -1; + unsigned flags = MAP_PRIVATE | MAP_FIXED | MAP_NORESERVE; + if (fd == -1) flags |= MAP_ANON; + + uptr PageSize = GetPageSizeCached(); + uptr p = internal_mmap((void *)(fixed_addr & ~(PageSize - 1)), + RoundUpTo(size, PageSize), PROT_READ | PROT_WRITE, + flags, fd, 0); + int reserrno; + if (internal_iserror(p, &reserrno)) + Report("ERROR: %s failed to " + "allocate 0x%zx (%zd) bytes at address %zx (errno: %d)\n", + SanitizerToolName, size, size, fixed_addr, reserrno); + IncreaseTotalMmap(size); + return (void *)p; +} + +void *MmapNoAccess(uptr fixed_addr, uptr size, const char *name) { + int fd = name ? GetNamedMappingFd(name, size) : -1; + unsigned flags = MAP_PRIVATE | MAP_FIXED | MAP_NORESERVE; + if (fd == -1) flags |= MAP_ANON; + + return (void *)internal_mmap((void *)fixed_addr, size, PROT_NONE, flags, fd, + 0); +} + } // namespace __sanitizer #endif // SANITIZER_POSIX diff --git a/contrib/compiler-rt/lib/sanitizer_common/sanitizer_printf.cc b/contrib/compiler-rt/lib/sanitizer_common/sanitizer_printf.cc index 3be6723..e4f67f5 100644 --- a/contrib/compiler-rt/lib/sanitizer_common/sanitizer_printf.cc +++ b/contrib/compiler-rt/lib/sanitizer_common/sanitizer_printf.cc @@ -251,26 +251,32 @@ static void SharedPrintfCode(bool append_pid, const char *format, buffer_size = kLen; } needed_length = 0; + // Check that data fits into the current buffer. +# define CHECK_NEEDED_LENGTH \ + if (needed_length >= buffer_size) { \ + if (!use_mmap) continue; \ + RAW_CHECK_MSG(needed_length < kLen, \ + "Buffer in Report is too short!\n"); \ + } 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"); + const char *exe_name = GetBinaryBasename(); + if (common_flags()->log_exe_name && exe_name) { + needed_length += internal_snprintf(buffer, buffer_size, + "==%s", exe_name); + CHECK_NEEDED_LENGTH } + needed_length += internal_snprintf(buffer + needed_length, + buffer_size - needed_length, + "==%d==", pid); + CHECK_NEEDED_LENGTH } 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"); - } + CHECK_NEEDED_LENGTH // If the message fit into the buffer, print it and exit. break; +# undef CHECK_NEEDED_LENGTH } RawWrite(buffer); AndroidLogWrite(buffer); diff --git a/contrib/compiler-rt/lib/sanitizer_common/sanitizer_procmaps_common.cc b/contrib/compiler-rt/lib/sanitizer_common/sanitizer_procmaps_common.cc index 2ec08d7..2c6ce8e 100644 --- a/contrib/compiler-rt/lib/sanitizer_common/sanitizer_procmaps_common.cc +++ b/contrib/compiler-rt/lib/sanitizer_common/sanitizer_procmaps_common.cc @@ -130,7 +130,6 @@ uptr MemoryMappingLayout::DumpListOfModules(LoadedModule *modules, 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 @@ -143,7 +142,8 @@ uptr MemoryMappingLayout::DumpListOfModules(LoadedModule *modules, // 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); + LoadedModule *cur_module = &modules[n_modules]; + cur_module->set(cur_name, base_address); cur_module->addAddressRange(cur_beg, cur_end, prot & kProtectionExecute); n_modules++; } diff --git a/contrib/compiler-rt/lib/sanitizer_common/sanitizer_procmaps_mac.cc b/contrib/compiler-rt/lib/sanitizer_common/sanitizer_procmaps_mac.cc index c0a8614..29d6996 100644 --- a/contrib/compiler-rt/lib/sanitizer_common/sanitizer_procmaps_mac.cc +++ b/contrib/compiler-rt/lib/sanitizer_common/sanitizer_procmaps_mac.cc @@ -171,13 +171,13 @@ uptr MemoryMappingLayout::DumpListOfModules(LoadedModule *modules, continue; if (filter && !filter(cur_name)) continue; - LoadedModule *cur_module = 0; + LoadedModule *cur_module = nullptr; if (n_modules > 0 && 0 == internal_strcmp(cur_name, modules[n_modules - 1].full_name())) { cur_module = &modules[n_modules - 1]; } else { - void *mem = &modules[n_modules]; - cur_module = new(mem) LoadedModule(cur_name, cur_beg); + cur_module = &modules[n_modules]; + cur_module->set(cur_name, cur_beg); n_modules++; } cur_module->addAddressRange(cur_beg, cur_end, prot & kProtectionExecute); diff --git a/contrib/compiler-rt/lib/sanitizer_common/sanitizer_stacktrace.cc b/contrib/compiler-rt/lib/sanitizer_common/sanitizer_stacktrace.cc index 2deadb6..84ff9d9 100644 --- a/contrib/compiler-rt/lib/sanitizer_common/sanitizer_stacktrace.cc +++ b/contrib/compiler-rt/lib/sanitizer_common/sanitizer_stacktrace.cc @@ -20,6 +20,8 @@ namespace __sanitizer { uptr StackTrace::GetNextInstructionPc(uptr pc) { #if defined(__mips__) return pc + 8; +#elif defined(__powerpc__) + return pc + 4; #else return pc + 1; #endif diff --git a/contrib/compiler-rt/lib/sanitizer_common/sanitizer_stacktrace_libcdep.cc b/contrib/compiler-rt/lib/sanitizer_common/sanitizer_stacktrace_libcdep.cc index 0f98c7d..f66fa79 100644 --- a/contrib/compiler-rt/lib/sanitizer_common/sanitizer_stacktrace_libcdep.cc +++ b/contrib/compiler-rt/lib/sanitizer_common/sanitizer_stacktrace_libcdep.cc @@ -35,7 +35,8 @@ void StackTrace::Print() const { for (SymbolizedStack *cur = frames; cur; cur = cur->next) { frame_desc.clear(); RenderFrame(&frame_desc, common_flags()->stack_trace_format, frame_num++, - cur->info, common_flags()->strip_path_prefix); + cur->info, common_flags()->symbolize_vs_style, + common_flags()->strip_path_prefix); Printf("%s\n", frame_desc.data()); } frames->ClearAll(); @@ -59,10 +60,14 @@ void BufferedStackTrace::Unwind(u32 max_depth, uptr pc, uptr bp, void *context, return; } if (!WillUseFastUnwind(request_fast_unwind)) { +#if SANITIZER_CAN_SLOW_UNWIND if (context) SlowUnwindStackWithContext(pc, context, max_depth); else SlowUnwindStack(pc, max_depth); +#else + UNREACHABLE("slow unwind requested but not available"); +#endif } else { FastUnwindStack(pc, bp, stack_top, stack_bottom, max_depth); } diff --git a/contrib/compiler-rt/lib/sanitizer_common/sanitizer_stacktrace_printer.cc b/contrib/compiler-rt/lib/sanitizer_common/sanitizer_stacktrace_printer.cc index 7b37dbc..3574fa3 100644 --- a/contrib/compiler-rt/lib/sanitizer_common/sanitizer_stacktrace_printer.cc +++ b/contrib/compiler-rt/lib/sanitizer_common/sanitizer_stacktrace_printer.cc @@ -26,8 +26,8 @@ static const char *StripFunctionName(const char *function, const char *prefix) { static const char kDefaultFormat[] = " #%n %p %F %L"; void RenderFrame(InternalScopedString *buffer, const char *format, int frame_no, - const AddressInfo &info, const char *strip_path_prefix, - const char *strip_func_prefix) { + const AddressInfo &info, bool vs_style, + const char *strip_path_prefix, const char *strip_func_prefix) { if (0 == internal_strcmp(format, "DEFAULT")) format = kDefaultFormat; for (const char *p = format; *p != '\0'; p++) { @@ -82,14 +82,14 @@ void RenderFrame(InternalScopedString *buffer, const char *format, int frame_no, break; case 'S': // File/line information. - RenderSourceLocation(buffer, info.file, info.line, info.column, + RenderSourceLocation(buffer, info.file, info.line, info.column, vs_style, strip_path_prefix); break; case 'L': // Source location, or module location. if (info.file) { RenderSourceLocation(buffer, info.file, info.line, info.column, - strip_path_prefix); + vs_style, strip_path_prefix); } else if (info.module) { RenderModuleLocation(buffer, info.module, info.module_offset, strip_path_prefix); @@ -99,22 +99,33 @@ void RenderFrame(InternalScopedString *buffer, const char *format, int frame_no, break; case 'M': // Module basename and offset, or PC. - if (info.module) + if (info.address & kExternalPCBit) + {} // There PCs are not meaningful. + else if (info.module) buffer->append("(%s+%p)", StripModuleName(info.module), (void *)info.module_offset); else buffer->append("(%p)", (void *)info.address); break; default: - Report("Unsupported specifier in stack frame format: %c (0x%zx)!\n", - *p, *p); + Report("Unsupported specifier in stack frame format: %c (0x%zx)!\n", *p, + *p); Die(); } } } void RenderSourceLocation(InternalScopedString *buffer, const char *file, - int line, int column, const char *strip_path_prefix) { + int line, int column, bool vs_style, + const char *strip_path_prefix) { + if (vs_style && line > 0) { + buffer->append("%s(%d", StripPathPrefix(file, strip_path_prefix), line); + if (column > 0) + buffer->append(",%d", column); + buffer->append(")"); + return; + } + buffer->append("%s", StripPathPrefix(file, strip_path_prefix)); if (line > 0) { buffer->append(":%d", line); diff --git a/contrib/compiler-rt/lib/sanitizer_common/sanitizer_stacktrace_printer.h b/contrib/compiler-rt/lib/sanitizer_common/sanitizer_stacktrace_printer.h index 9356988..7f6c5c7 100644 --- a/contrib/compiler-rt/lib/sanitizer_common/sanitizer_stacktrace_printer.h +++ b/contrib/compiler-rt/lib/sanitizer_common/sanitizer_stacktrace_printer.h @@ -48,11 +48,13 @@ namespace __sanitizer { // module+offset if it is known, or (<unknown module>) string. // %M - prints module basename and offset, if it is known, or PC. void RenderFrame(InternalScopedString *buffer, const char *format, int frame_no, - const AddressInfo &info, const char *strip_path_prefix = "", + const AddressInfo &info, bool vs_style, + const char *strip_path_prefix = "", const char *strip_func_prefix = ""); void RenderSourceLocation(InternalScopedString *buffer, const char *file, - int line, int column, const char *strip_path_prefix); + int line, int column, bool vs_style, + const char *strip_path_prefix); void RenderModuleLocation(InternalScopedString *buffer, const char *module, uptr offset, const char *strip_path_prefix); diff --git a/contrib/compiler-rt/lib/sanitizer_common/sanitizer_stoptheworld.h b/contrib/compiler-rt/lib/sanitizer_common/sanitizer_stoptheworld.h index a326467..aa6f5d8 100644 --- a/contrib/compiler-rt/lib/sanitizer_common/sanitizer_stoptheworld.h +++ b/contrib/compiler-rt/lib/sanitizer_common/sanitizer_stoptheworld.h @@ -59,7 +59,8 @@ typedef void (*StopTheWorldCallback)( // Suspend all threads in the current process and run the callback on the list // of suspended threads. This function will resume the threads before returning. -// The callback should not call any libc functions. +// The callback should not call any libc functions. The callback must not call +// exit() nor _exit() and instead return to the caller. // This function should NOT be called from multiple threads simultaneously. void StopTheWorld(StopTheWorldCallback callback, void *argument); diff --git a/contrib/compiler-rt/lib/sanitizer_common/sanitizer_stoptheworld_linux_libcdep.cc b/contrib/compiler-rt/lib/sanitizer_common/sanitizer_stoptheworld_linux_libcdep.cc index ad20e39..47b27e7 100644 --- a/contrib/compiler-rt/lib/sanitizer_common/sanitizer_stoptheworld_linux_libcdep.cc +++ b/contrib/compiler-rt/lib/sanitizer_common/sanitizer_stoptheworld_linux_libcdep.cc @@ -19,6 +19,7 @@ #include "sanitizer_stoptheworld.h" #include "sanitizer_platform_limits_posix.h" +#include "sanitizer_atomic.h" #include <errno.h> #include <sched.h> // for CLONE_* definitions @@ -70,11 +71,25 @@ COMPILER_CHECK(sizeof(SuspendedThreadID) == sizeof(pid_t)); namespace __sanitizer { + +// Structure for passing arguments into the tracer thread. +struct TracerThreadArgument { + StopTheWorldCallback callback; + void *callback_argument; + // The tracer thread waits on this mutex while the parent finishes its + // preparations. + BlockingMutex mutex; + // Tracer thread signals its completion by setting done. + atomic_uintptr_t done; + uptr parent_pid; +}; + // This class handles thread suspending/unsuspending in the tracer thread. class ThreadSuspender { public: - explicit ThreadSuspender(pid_t pid) - : pid_(pid) { + explicit ThreadSuspender(pid_t pid, TracerThreadArgument *arg) + : arg(arg) + , pid_(pid) { CHECK_GE(pid, 0); } bool SuspendAllThreads(); @@ -83,6 +98,7 @@ class ThreadSuspender { SuspendedThreadsList &suspended_threads_list() { return suspended_threads_list_; } + TracerThreadArgument *arg; private: SuspendedThreadsList suspended_threads_list_; pid_t pid_; @@ -103,7 +119,7 @@ bool ThreadSuspender::SuspendThread(SuspendedThreadID tid) { VReport(1, "Could not attach to thread %d (errno %d).\n", tid, pterrno); return false; } else { - VReport(1, "Attached to thread %d.\n", tid); + VReport(2, "Attached to thread %d.\n", tid); // The thread is not guaranteed to stop before ptrace returns, so we must // wait on it. Note: if the thread receives a signal concurrently, // we can get notification about the signal before notification about stop. @@ -143,7 +159,7 @@ void ThreadSuspender::ResumeAllThreads() { int pterrno; if (!internal_iserror(internal_ptrace(PTRACE_DETACH, tid, NULL, NULL), &pterrno)) { - VReport(1, "Detached from thread %d.\n", tid); + VReport(2, "Detached from thread %d.\n", tid); } else { // Either the thread is dead, or we are already detached. // The latter case is possible, for instance, if this function was called @@ -188,25 +204,23 @@ static ThreadSuspender *thread_suspender_instance = NULL; static const int kSyncSignals[] = { 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. -static void TracerThreadSignalHandler(int signum, void *siginfo, void *) { - if (thread_suspender_instance != NULL) { +static void TracerThreadSignalHandler(int signum, void *siginfo, void *uctx) { + SignalContext ctx = SignalContext::Create(siginfo, uctx); + VPrintf(1, "Tracer caught signal %d: addr=0x%zx pc=0x%zx sp=0x%zx\n", + signum, ctx.addr, ctx.pc, ctx.sp); + ThreadSuspender *inst = thread_suspender_instance; + if (inst != NULL) { if (signum == SIGABRT) - thread_suspender_instance->KillAllThreads(); + inst->KillAllThreads(); else - thread_suspender_instance->ResumeAllThreads(); + inst->ResumeAllThreads(); + SetDieCallback(old_die_callback); + old_die_callback = NULL; + thread_suspender_instance = NULL; + atomic_store(&inst->arg->done, 1, memory_order_relaxed); } internal__exit((signum == SIGABRT) ? 1 : 2); } @@ -218,10 +232,15 @@ static void TracerThreadDieCallback() { // 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(); + ThreadSuspender *inst = thread_suspender_instance; + if (inst != NULL && stoptheworld_tracer_pid == internal_getpid()) { + inst->KillAllThreads(); + thread_suspender_instance = NULL; + } if (old_die_callback) old_die_callback(); + SetDieCallback(old_die_callback); + old_die_callback = NULL; } // Size of alternative stack for signal handlers in the tracer thread. @@ -244,7 +263,7 @@ static int TracerThread(void* argument) { old_die_callback = GetDieCallback(); SetDieCallback(TracerThreadDieCallback); - ThreadSuspender thread_suspender(internal_getppid()); + ThreadSuspender thread_suspender(internal_getppid(), tracer_thread_argument); // Global pointer for the signal handler. thread_suspender_instance = &thread_suspender; @@ -276,11 +295,9 @@ static int TracerThread(void* argument) { thread_suspender.ResumeAllThreads(); exit_code = 0; } - // Note, this is a bad race. If TracerThreadDieCallback is already started - // in another thread and observed that thread_suspender_instance != 0, - // it can call KillAllThreads on the destroyed variable. SetDieCallback(old_die_callback); thread_suspender_instance = NULL; + atomic_store(&tracer_thread_argument->done, 1, memory_order_relaxed); return exit_code; } @@ -293,7 +310,7 @@ class ScopedStackSpaceWithGuard { // in the future. guard_start_ = (uptr)MmapOrDie(stack_size_ + guard_size_, "ScopedStackWithGuard"); - CHECK_EQ(guard_start_, (uptr)Mprotect((uptr)guard_start_, guard_size_)); + CHECK(MprotectNoAccess((uptr)guard_start_, guard_size_)); } ~ScopedStackSpaceWithGuard() { UnmapOrDie((void *)guard_start_, stack_size_ + guard_size_); @@ -354,6 +371,7 @@ void StopTheWorld(StopTheWorldCallback callback, void *argument) { tracer_thread_argument.callback = callback; tracer_thread_argument.callback_argument = argument; tracer_thread_argument.parent_pid = internal_getpid(); + atomic_store(&tracer_thread_argument.done, 0, memory_order_relaxed); const uptr kTracerStackSize = 2 * 1024 * 1024; ScopedStackSpaceWithGuard tracer_stack(kTracerStackSize); // Block the execution of TracerThread until after we have set ptrace @@ -402,14 +420,27 @@ void StopTheWorld(StopTheWorldCallback callback, void *argument) { #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)) + // NOTE: errno is shared between this thread and the tracer thread. + // internal_waitpid() may call syscall() which can access/spoil errno, + // so we can't call it now. Instead we for the tracer thread to finish using + // the spin loop below. Man page for sched_yield() says "In the Linux + // implementation, sched_yield() always succeeds", so let's hope it does not + // spoil errno. Note that this spin loop runs only for brief periods before + // the tracer thread has suspended us and when it starts unblocking threads. + while (atomic_load(&tracer_thread_argument.done, memory_order_relaxed) == 0) + sched_yield(); + // Now the tracer thread is about to exit and does not touch errno, + // wait for it. + for (;;) { + uptr waitpid_status = internal_waitpid(tracer_pid, NULL, __WALL); + if (!internal_iserror(waitpid_status, &local_errno)) + break; + if (local_errno == EINTR) + continue; VReport(1, "Waiting on the tracer thread failed (errno %d).\n", local_errno); + break; + } } } diff --git a/contrib/compiler-rt/lib/sanitizer_common/sanitizer_suppressions.cc b/contrib/compiler-rt/lib/sanitizer_common/sanitizer_suppressions.cc index 2b697e9..08cb497 100644 --- a/contrib/compiler-rt/lib/sanitizer_common/sanitizer_suppressions.cc +++ b/contrib/compiler-rt/lib/sanitizer_common/sanitizer_suppressions.cc @@ -30,18 +30,50 @@ SuppressionContext::SuppressionContext(const char *suppression_types[], internal_memset(has_suppression_type_, 0, suppression_types_num_); } +static bool GetPathAssumingFileIsRelativeToExec(const char *file_path, + /*out*/char *new_file_path, + uptr new_file_path_size) { + InternalScopedString exec(kMaxPathLength); + if (ReadBinaryNameCached(exec.data(), exec.size())) { + const char *file_name_pos = StripModuleName(exec.data()); + uptr path_to_exec_len = file_name_pos - exec.data(); + internal_strncat(new_file_path, exec.data(), + Min(path_to_exec_len, new_file_path_size - 1)); + internal_strncat(new_file_path, file_path, + new_file_path_size - internal_strlen(new_file_path) - 1); + return true; + } + return false; +} + void SuppressionContext::ParseFromFile(const char *filename) { if (filename[0] == '\0') return; + + // If we cannot find the file, check if its location is relative to + // the location of the executable. + InternalScopedString new_file_path(kMaxPathLength); + if (!FileExists(filename) && !IsAbsolutePath(filename) && + GetPathAssumingFileIsRelativeToExec(filename, new_file_path.data(), + new_file_path.size())) { + filename = new_file_path.data(); + } + + // Read the file. char *file_contents; uptr buffer_size; - uptr contents_size = ReadFileToBuffer(filename, &file_contents, &buffer_size, - 1 << 26 /* max_len */); + const uptr max_len = 1 << 26; + uptr contents_size = + ReadFileToBuffer(filename, &file_contents, &buffer_size, max_len); + VPrintf(1, "%s: reading suppressions file at %s\n", + SanitizerToolName, filename); + if (contents_size == 0) { Printf("%s: failed to read suppressions file '%s'\n", SanitizerToolName, filename); Die(); } + Parse(file_contents); } diff --git a/contrib/compiler-rt/lib/sanitizer_common/sanitizer_symbolizer.cc b/contrib/compiler-rt/lib/sanitizer_common/sanitizer_symbolizer.cc index 135720e..8b2496a 100644 --- a/contrib/compiler-rt/lib/sanitizer_common/sanitizer_symbolizer.cc +++ b/contrib/compiler-rt/lib/sanitizer_common/sanitizer_symbolizer.cc @@ -16,7 +16,7 @@ #include "sanitizer_internal_defs.h" #include "sanitizer_libc.h" #include "sanitizer_placement_new.h" -#include "sanitizer_symbolizer.h" +#include "sanitizer_symbolizer_internal.h" namespace __sanitizer { @@ -33,9 +33,7 @@ void AddressInfo::Clear() { function_offset = kUnknown; } -void AddressInfo::FillAddressAndModuleInfo(uptr addr, const char *mod_name, - uptr mod_offset) { - address = addr; +void AddressInfo::FillModuleInfo(const char *mod_name, uptr mod_offset) { module = internal_strdup(mod_name); module_offset = mod_offset; } @@ -70,13 +68,6 @@ Symbolizer *Symbolizer::symbolizer_; StaticSpinMutex Symbolizer::init_mu_; LowLevelAllocator Symbolizer::symbolizer_allocator_; -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); @@ -84,7 +75,29 @@ void Symbolizer::AddHooks(Symbolizer::StartSymbolizationHook start_hook, end_hook_ = end_hook; } -Symbolizer::Symbolizer() : start_hook_(0), end_hook_(0) {} +const char *Symbolizer::ModuleNameOwner::GetOwnedCopy(const char *str) { + mu_->CheckLocked(); + + // 'str' will be the same string multiple times in a row, optimize this case. + if (last_match_ && !internal_strcmp(last_match_, str)) + return last_match_; + + // FIXME: this is linear search. + // We should optimize this further if this turns out to be a bottleneck later. + for (uptr i = 0; i < storage_.size(); ++i) { + if (!internal_strcmp(storage_[i], str)) { + last_match_ = storage_[i]; + return last_match_; + } + } + last_match_ = internal_strdup(str); + storage_.push_back(last_match_); + return last_match_; +} + +Symbolizer::Symbolizer(IntrusiveList<SymbolizerTool> tools) + : module_names_(&mu_), n_modules_(0), modules_fresh_(false), tools_(tools), + start_hook_(0), end_hook_(0) {} Symbolizer::SymbolizerScope::SymbolizerScope(const Symbolizer *sym) : sym_(sym) { diff --git a/contrib/compiler-rt/lib/sanitizer_common/sanitizer_symbolizer.h b/contrib/compiler-rt/lib/sanitizer_common/sanitizer_symbolizer.h index 3ccfce9..9233223 100644 --- a/contrib/compiler-rt/lib/sanitizer_common/sanitizer_symbolizer.h +++ b/contrib/compiler-rt/lib/sanitizer_common/sanitizer_symbolizer.h @@ -43,8 +43,7 @@ struct AddressInfo { AddressInfo(); // Deletes all strings and resets all fields. void Clear(); - void FillAddressAndModuleInfo(uptr addr, const char *mod_name, - uptr mod_offset); + void FillModuleInfo(const char *mod_name, uptr mod_offset); }; // Linked list of symbolized frames (each frame is described by AddressInfo). @@ -74,33 +73,35 @@ struct DataInfo { void Clear(); }; -class Symbolizer { +class SymbolizerTool; + +class Symbolizer final { public: /// Initialize and return platform-specific implementation of symbolizer /// (if it wasn't already initialized). static Symbolizer *GetOrInit(); // Returns a list of symbolized frames for a given address (containing // all inlined functions, if necessary). - virtual SymbolizedStack *SymbolizePC(uptr address) { - return SymbolizedStack::New(address); - } - virtual bool SymbolizeData(uptr address, DataInfo *info) { - return false; - } - virtual bool GetModuleNameAndOffsetForPC(uptr pc, const char **module_name, - uptr *module_address) { - return false; - } - virtual bool CanReturnFileLineInfo() { - return false; + SymbolizedStack *SymbolizePC(uptr address); + bool SymbolizeData(uptr address, DataInfo *info); + + // The module names Symbolizer returns are stable and unique for every given + // module. It is safe to store and compare them as pointers. + bool GetModuleNameAndOffsetForPC(uptr pc, const char **module_name, + uptr *module_address); + const char *GetModuleNameForPc(uptr pc) { + const char *module_name = nullptr; + uptr unused; + if (GetModuleNameAndOffsetForPC(pc, &module_name, &unused)) + return module_name; + return nullptr; } + // Release internal caches (if any). - virtual void Flush() {} + void Flush(); // Attempts to demangle the provided C++ mangled name. - virtual const char *Demangle(const char *name) { - return name; - } - virtual void PrepareForSandboxing() {} + const char *Demangle(const char *name); + void PrepareForSandboxing(); // Allow user to install hooks that would be called before/after Symbolizer // does the actual file/line info fetching. Specific sanitizers may need this @@ -113,16 +114,53 @@ class Symbolizer { EndSymbolizationHook end_hook); private: + // GetModuleNameAndOffsetForPC has to return a string to the caller. + // Since the corresponding module might get unloaded later, we should create + // our owned copies of the strings that we can safely return. + // ModuleNameOwner does not provide any synchronization, thus calls to + // its method should be protected by |mu_|. + class ModuleNameOwner { + public: + explicit ModuleNameOwner(BlockingMutex *synchronized_by) + : storage_(kInitialCapacity), last_match_(nullptr), + mu_(synchronized_by) {} + const char *GetOwnedCopy(const char *str); + + private: + static const uptr kInitialCapacity = 1000; + InternalMmapVector<const char*> storage_; + const char *last_match_; + + BlockingMutex *mu_; + } module_names_; + /// Platform-specific function for creating a Symbolizer object. static Symbolizer *PlatformInit(); - /// Initialize the symbolizer in a disabled state. Not thread safe. - static Symbolizer *Disable(); + + bool FindModuleNameAndOffsetForAddress(uptr address, const char **module_name, + uptr *module_offset); + LoadedModule *FindModuleForAddress(uptr address); + LoadedModule modules_[kMaxNumberOfModules]; + uptr n_modules_; + // If stale, need to reload the modules before looking up addresses. + bool modules_fresh_; + + // Platform-specific default demangler, must not return nullptr. + const char *PlatformDemangle(const char *name); + void PlatformPrepareForSandboxing(); static Symbolizer *symbolizer_; static StaticSpinMutex init_mu_; - protected: - Symbolizer(); + // Mutex locked from public methods of |Symbolizer|, so that the internals + // (including individual symbolizer tools and platform-specific methods) are + // always synchronized. + BlockingMutex mu_; + + typedef IntrusiveList<SymbolizerTool>::Iterator Iterator; + IntrusiveList<SymbolizerTool> tools_; + + explicit Symbolizer(IntrusiveList<SymbolizerTool> tools); static LowLevelAllocator symbolizer_allocator_; diff --git a/contrib/compiler-rt/lib/sanitizer_common/sanitizer_symbolizer_internal.h b/contrib/compiler-rt/lib/sanitizer_common/sanitizer_symbolizer_internal.h new file mode 100644 index 0000000..66ae809 --- /dev/null +++ b/contrib/compiler-rt/lib/sanitizer_common/sanitizer_symbolizer_internal.h @@ -0,0 +1,109 @@ +//===-- sanitizer_symbolizer_internal.h -------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// Header for internal classes and functions to be used by implementations of +// symbolizers. +// +//===----------------------------------------------------------------------===// +#ifndef SANITIZER_SYMBOLIZER_INTERNAL_H +#define SANITIZER_SYMBOLIZER_INTERNAL_H + +#include "sanitizer_symbolizer.h" + +namespace __sanitizer { + +// Parsing helpers, 'str' is searched for delimiter(s) and a string or uptr +// is extracted. When extracting a string, a newly allocated (using +// InternalAlloc) and null-terminataed buffer is returned. They return a pointer +// to the next characted after the found delimiter. +const char *ExtractToken(const char *str, const char *delims, char **result); +const char *ExtractInt(const char *str, const char *delims, int *result); +const char *ExtractUptr(const char *str, const char *delims, uptr *result); +const char *ExtractTokenUpToDelimiter(const char *str, const char *delimiter, + char **result); + +const char *DemangleCXXABI(const char *name); + +// SymbolizerTool is an interface that is implemented by individual "tools" +// that can perform symbolication (external llvm-symbolizer, libbacktrace, +// Windows DbgHelp symbolizer, etc.). +class SymbolizerTool { + public: + // The main |Symbolizer| class implements a "fallback chain" of symbolizer + // tools. In a request to symbolize an address, if one tool returns false, + // the next tool in the chain will be tried. + SymbolizerTool *next; + + SymbolizerTool() : next(nullptr) { } + + // Can't declare pure virtual functions in sanitizer runtimes: + // __cxa_pure_virtual might be unavailable. + + // The |stack| parameter is inout. It is pre-filled with the address, + // module base and module offset values and is to be used to construct + // other stack frames. + virtual bool SymbolizePC(uptr addr, SymbolizedStack *stack) { + UNIMPLEMENTED(); + } + + // The |info| parameter is inout. It is pre-filled with the module base + // and module offset values. + virtual bool SymbolizeData(uptr addr, DataInfo *info) { + UNIMPLEMENTED(); + } + + virtual void Flush() {} + + // Return nullptr to fallback to the default platform-specific demangler. + virtual const char *Demangle(const char *name) { + return nullptr; + } +}; + +// 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: + explicit SymbolizerProcess(const char *path, bool use_forkpty = false); + const char *SendCommand(const char *command); + + private: + bool Restart(); + const char *SendCommandImpl(const char *command); + bool ReadFromSymbolizer(char *buffer, uptr max_length); + bool WriteToSymbolizer(const char *buffer, uptr length); + bool StartSymbolizerSubprocess(); + + 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_; + bool use_forkpty_; +}; + +} // namespace __sanitizer + +#endif // SANITIZER_SYMBOLIZER_INTERNAL_H diff --git a/contrib/compiler-rt/lib/sanitizer_common/sanitizer_symbolizer_libbacktrace.cc b/contrib/compiler-rt/lib/sanitizer_common/sanitizer_symbolizer_libbacktrace.cc index 9317a78..5735466 100644 --- a/contrib/compiler-rt/lib/sanitizer_common/sanitizer_symbolizer_libbacktrace.cc +++ b/contrib/compiler-rt/lib/sanitizer_common/sanitizer_symbolizer_libbacktrace.cc @@ -33,6 +33,8 @@ namespace __sanitizer { +static char *DemangleAlloc(const char *name, bool always_alloc); + #if SANITIZER_LIBBACKTRACE namespace { @@ -86,17 +88,20 @@ char *CplusV3Demangle(const char *name) { struct SymbolizeCodeCallbackArg { SymbolizedStack *first; SymbolizedStack *last; - const char *module_name; - uptr module_offset; - - void append(SymbolizedStack *f) { - if (last != nullptr) { - last->next = f; - last = f; - } else { - first = f; - last = f; + uptr frames_symbolized; + + AddressInfo *get_new_frame(uintptr_t addr) { + CHECK(last); + if (frames_symbolized > 0) { + SymbolizedStack *cur = SymbolizedStack::New(addr); + AddressInfo *info = &cur->info; + info->FillModuleInfo(first->info.module, first->info.module_offset); + last->next = cur; + last = cur; } + CHECK_EQ(addr, first->info.address); + CHECK_EQ(addr, last->info.address); + return &last->info; } }; @@ -106,15 +111,12 @@ static int SymbolizeCodePCInfoCallback(void *vdata, uintptr_t addr, const char *function) { SymbolizeCodeCallbackArg *cdata = (SymbolizeCodeCallbackArg *)vdata; if (function) { - SymbolizedStack *cur = SymbolizedStack::New(addr); - cdata->append(cur); - AddressInfo *info = &cur->info; - info->FillAddressAndModuleInfo(addr, cdata->module_name, - cdata->module_offset); - info->function = LibbacktraceSymbolizer::Demangle(function, true); + AddressInfo *info = cdata->get_new_frame(addr); + info->function = DemangleAlloc(function, /*always_alloc*/ true); if (filename) info->file = internal_strdup(filename); info->line = lineno; + cdata->frames_symbolized++; } return 0; } @@ -123,12 +125,9 @@ static void SymbolizeCodeCallback(void *vdata, uintptr_t addr, const char *symname, uintptr_t, uintptr_t) { SymbolizeCodeCallbackArg *cdata = (SymbolizeCodeCallbackArg *)vdata; if (symname) { - SymbolizedStack *cur = SymbolizedStack::New(addr); - cdata->append(cur); - AddressInfo *info = &cur->info; - info->FillAddressAndModuleInfo(addr, cdata->module_name, - cdata->module_offset); - info->function = LibbacktraceSymbolizer::Demangle(symname, true); + AddressInfo *info = cdata->get_new_frame(addr); + info->function = DemangleAlloc(symname, /*always_alloc*/ true); + cdata->frames_symbolized++; } } @@ -136,7 +135,7 @@ 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->name = DemangleAlloc(symname, /*always_alloc*/ true); info->start = symval; info->size = symsize; } @@ -156,21 +155,18 @@ LibbacktraceSymbolizer *LibbacktraceSymbolizer::get(LowLevelAllocator *alloc) { return new(*alloc) LibbacktraceSymbolizer(state); } -SymbolizedStack *LibbacktraceSymbolizer::SymbolizeCode(uptr addr, - const char *module_name, - uptr module_offset) { +bool LibbacktraceSymbolizer::SymbolizePC(uptr addr, SymbolizedStack *stack) { SymbolizeCodeCallbackArg data; - data.first = nullptr; - data.last = nullptr; - data.module_name = module_name; - data.module_offset = module_offset; + data.first = stack; + data.last = stack; + data.frames_symbolized = 0; backtrace_pcinfo((backtrace_state *)state_, addr, SymbolizeCodePCInfoCallback, ErrorCallback, &data); - if (data.first) - return data.first; + if (data.frames_symbolized > 0) + return true; backtrace_syminfo((backtrace_state *)state_, addr, SymbolizeCodeCallback, ErrorCallback, &data); - return data.first; + return (data.frames_symbolized > 0); } bool LibbacktraceSymbolizer::SymbolizeData(uptr addr, DataInfo *info) { @@ -185,11 +181,9 @@ LibbacktraceSymbolizer *LibbacktraceSymbolizer::get(LowLevelAllocator *alloc) { return 0; } -SymbolizedStack *LibbacktraceSymbolizer::SymbolizeCode(uptr addr, - const char *module_name, - uptr module_offset) { +bool LibbacktraceSymbolizer::SymbolizePC(uptr addr, SymbolizedStack *stack) { (void)state_; - return nullptr; + return false; } bool LibbacktraceSymbolizer::SymbolizeData(uptr addr, DataInfo *info) { @@ -198,7 +192,7 @@ bool LibbacktraceSymbolizer::SymbolizeData(uptr addr, DataInfo *info) { #endif // SANITIZER_LIBBACKTRACE -char *LibbacktraceSymbolizer::Demangle(const char *name, bool always_alloc) { +static char *DemangleAlloc(const char *name, bool always_alloc) { #if SANITIZER_LIBBACKTRACE && SANITIZER_CP_DEMANGLE if (char *demangled = CplusV3Demangle(name)) return demangled; @@ -208,4 +202,8 @@ char *LibbacktraceSymbolizer::Demangle(const char *name, bool always_alloc) { return 0; } +const char *LibbacktraceSymbolizer::Demangle(const char *name) { + return DemangleAlloc(name, /*always_alloc*/ false); +} + } // namespace __sanitizer diff --git a/contrib/compiler-rt/lib/sanitizer_common/sanitizer_symbolizer_libbacktrace.h b/contrib/compiler-rt/lib/sanitizer_common/sanitizer_symbolizer_libbacktrace.h index 1ff0050..00b465a 100644 --- a/contrib/compiler-rt/lib/sanitizer_common/sanitizer_symbolizer_libbacktrace.h +++ b/contrib/compiler-rt/lib/sanitizer_common/sanitizer_symbolizer_libbacktrace.h @@ -16,7 +16,7 @@ #include "sanitizer_platform.h" #include "sanitizer_common.h" -#include "sanitizer_symbolizer.h" +#include "sanitizer_symbolizer_internal.h" #ifndef SANITIZER_LIBBACKTRACE # define SANITIZER_LIBBACKTRACE 0 @@ -28,17 +28,16 @@ namespace __sanitizer { -class LibbacktraceSymbolizer { +class LibbacktraceSymbolizer : public SymbolizerTool { public: static LibbacktraceSymbolizer *get(LowLevelAllocator *alloc); - SymbolizedStack *SymbolizeCode(uptr addr, const char *module_name, - uptr module_offset); + bool SymbolizePC(uptr addr, SymbolizedStack *stack) override; - bool SymbolizeData(uptr addr, DataInfo *info); + bool SymbolizeData(uptr addr, DataInfo *info) override; // May return NULL if demangling failed. - static char *Demangle(const char *name, bool always_alloc = false); + const char *Demangle(const char *name) override; private: explicit LibbacktraceSymbolizer(void *state) : state_(state) {} diff --git a/contrib/compiler-rt/lib/sanitizer_common/sanitizer_symbolizer_libcdep.cc b/contrib/compiler-rt/lib/sanitizer_common/sanitizer_symbolizer_libcdep.cc index 63c9356..160f55d 100644 --- a/contrib/compiler-rt/lib/sanitizer_common/sanitizer_symbolizer_libcdep.cc +++ b/contrib/compiler-rt/lib/sanitizer_common/sanitizer_symbolizer_libcdep.cc @@ -11,18 +11,177 @@ // run-time libraries. //===----------------------------------------------------------------------===// +#include "sanitizer_allocator_internal.h" #include "sanitizer_internal_defs.h" -#include "sanitizer_symbolizer.h" +#include "sanitizer_symbolizer_internal.h" namespace __sanitizer { +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; +} + +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; +} + +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; +} + +const char *ExtractTokenUpToDelimiter(const char *str, const char *delimiter, + char **result) { + const char *found_delimiter = internal_strstr(str, delimiter); + uptr prefix_len = + found_delimiter ? found_delimiter - str : internal_strlen(str); + *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 += internal_strlen(delimiter); + return prefix_end; +} + +SymbolizedStack *Symbolizer::SymbolizePC(uptr addr) { + BlockingMutexLock l(&mu_); + const char *module_name; + uptr module_offset; + SymbolizedStack *res = SymbolizedStack::New(addr); + if (!FindModuleNameAndOffsetForAddress(addr, &module_name, &module_offset)) + return res; + // Always fill data about module name and offset. + res->info.FillModuleInfo(module_name, module_offset); + for (auto iter = Iterator(&tools_); iter.hasNext();) { + auto *tool = iter.next(); + SymbolizerScope sym_scope(this); + if (tool->SymbolizePC(addr, res)) { + return res; + } + } + return res; +} + +bool Symbolizer::SymbolizeData(uptr addr, DataInfo *info) { + BlockingMutexLock l(&mu_); + const char *module_name; + uptr module_offset; + if (!FindModuleNameAndOffsetForAddress(addr, &module_name, &module_offset)) + return false; + info->Clear(); + info->module = internal_strdup(module_name); + info->module_offset = module_offset; + for (auto iter = Iterator(&tools_); iter.hasNext();) { + auto *tool = iter.next(); + SymbolizerScope sym_scope(this); + if (tool->SymbolizeData(addr, info)) { + return true; + } + } + return true; +} + +bool Symbolizer::GetModuleNameAndOffsetForPC(uptr pc, const char **module_name, + uptr *module_address) { + BlockingMutexLock l(&mu_); + const char *internal_module_name = nullptr; + if (!FindModuleNameAndOffsetForAddress(pc, &internal_module_name, + module_address)) + return false; + + if (module_name) + *module_name = module_names_.GetOwnedCopy(internal_module_name); + return true; +} + +void Symbolizer::Flush() { + BlockingMutexLock l(&mu_); + for (auto iter = Iterator(&tools_); iter.hasNext();) { + auto *tool = iter.next(); + SymbolizerScope sym_scope(this); + tool->Flush(); + } +} + +const char *Symbolizer::Demangle(const char *name) { + BlockingMutexLock l(&mu_); + for (auto iter = Iterator(&tools_); iter.hasNext();) { + auto *tool = iter.next(); + SymbolizerScope sym_scope(this); + if (const char *demangled = tool->Demangle(name)) + return demangled; + } + return PlatformDemangle(name); +} + +void Symbolizer::PrepareForSandboxing() { + BlockingMutexLock l(&mu_); + PlatformPrepareForSandboxing(); +} + +bool Symbolizer::FindModuleNameAndOffsetForAddress(uptr address, + const char **module_name, + uptr *module_offset) { + LoadedModule *module = FindModuleForAddress(address); + if (module == 0) + return false; + *module_name = module->full_name(); + *module_offset = address - module->base_address(); + return true; +} + +LoadedModule *Symbolizer::FindModuleForAddress(uptr address) { + bool modules_were_reloaded = false; + if (!modules_fresh_) { + for (uptr i = 0; i < n_modules_; i++) + modules_[i].clear(); + n_modules_ = + GetListOfModules(modules_, kMaxNumberOfModules, /* filter */ nullptr); + CHECK_GT(n_modules_, 0); + CHECK_LT(n_modules_, kMaxNumberOfModules); + 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; +} + Symbolizer *Symbolizer::GetOrInit() { SpinMutexLock l(&init_mu_); if (symbolizer_) return symbolizer_; - if ((symbolizer_ = PlatformInit())) - return symbolizer_; - return Disable(); + symbolizer_ = PlatformInit(); + CHECK(symbolizer_); + return symbolizer_; } } // namespace __sanitizer diff --git a/contrib/compiler-rt/lib/sanitizer_common/sanitizer_symbolizer_mac.cc b/contrib/compiler-rt/lib/sanitizer_common/sanitizer_symbolizer_mac.cc new file mode 100644 index 0000000..9a64192 --- /dev/null +++ b/contrib/compiler-rt/lib/sanitizer_common/sanitizer_symbolizer_mac.cc @@ -0,0 +1,148 @@ +//===-- sanitizer_symbolizer_mac.cc ---------------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file is shared between various sanitizers' runtime libraries. +// +// Implementation of Mac-specific "atos" symbolizer. +//===----------------------------------------------------------------------===// + +#include "sanitizer_platform.h" +#if SANITIZER_MAC + +#include "sanitizer_allocator_internal.h" +#include "sanitizer_mac.h" +#include "sanitizer_symbolizer_mac.h" + +namespace __sanitizer { + +#include <dlfcn.h> +#include <errno.h> +#include <stdlib.h> +#include <sys/wait.h> +#include <unistd.h> +#include <util.h> + +bool DlAddrSymbolizer::SymbolizePC(uptr addr, SymbolizedStack *stack) { + Dl_info info; + int result = dladdr((const void *)addr, &info); + if (!result) return false; + const char *demangled = DemangleCXXABI(info.dli_sname); + stack->info.function = internal_strdup(demangled); + return true; +} + +bool DlAddrSymbolizer::SymbolizeData(uptr addr, DataInfo *info) { + return false; +} + +class AtosSymbolizerProcess : public SymbolizerProcess { + public: + explicit AtosSymbolizerProcess(const char *path, pid_t parent_pid) + : SymbolizerProcess(path, /*use_forkpty*/ true), + parent_pid_(parent_pid) {} + + private: + bool ReachedEndOfOutput(const char *buffer, uptr length) const override { + return (length >= 1 && buffer[length - 1] == '\n'); + } + + void ExecuteWithDefaultArgs(const char *path_to_binary) const override { + char pid_str[16]; + internal_snprintf(pid_str, sizeof(pid_str), "%d", parent_pid_); + if (GetMacosVersion() == MACOS_VERSION_MAVERICKS) { + // On Mavericks atos prints a deprecation warning which we suppress by + // passing -d. The warning isn't present on other OSX versions, even the + // newer ones. + execl(path_to_binary, path_to_binary, "-p", pid_str, "-d", (char *)0); + } else { + execl(path_to_binary, path_to_binary, "-p", pid_str, (char *)0); + } + } + + pid_t parent_pid_; +}; + +static const char *kAtosErrorMessages[] = { + "atos cannot examine process", + "unable to get permission to examine process", + "An admin user name and password is required", + "could not load inserted library", + "architecture mismatch between analysis process", +}; + +static bool IsAtosErrorMessage(const char *str) { + for (uptr i = 0; i < ARRAY_SIZE(kAtosErrorMessages); i++) { + if (internal_strstr(str, kAtosErrorMessages[i])) { + return true; + } + } + return false; +} + +static bool ParseCommandOutput(const char *str, SymbolizedStack *res) { + // Trim ending newlines. + char *trim; + ExtractTokenUpToDelimiter(str, "\n", &trim); + + // The line from `atos` is in one of these formats: + // myfunction (in library.dylib) (sourcefile.c:17) + // myfunction (in library.dylib) + 0x1fe + // 0xdeadbeef (in library.dylib) + 0x1fe + // 0xdeadbeef (in library.dylib) + // 0xdeadbeef + + if (IsAtosErrorMessage(trim)) { + Report("atos returned an error: %s\n", trim); + InternalFree(trim); + return false; + } + + const char *rest = trim; + char *function_name; + rest = ExtractTokenUpToDelimiter(rest, " (in ", &function_name); + if (internal_strncmp(function_name, "0x", 2) != 0) + res->info.function = function_name; + else + InternalFree(function_name); + rest = ExtractTokenUpToDelimiter(rest, ") ", &res->info.module); + + if (rest[0] == '(') { + rest++; + rest = ExtractTokenUpToDelimiter(rest, ":", &res->info.file); + char *extracted_line_number; + rest = ExtractTokenUpToDelimiter(rest, ")", &extracted_line_number); + res->info.line = internal_atoll(extracted_line_number); + InternalFree(extracted_line_number); + } + + InternalFree(trim); + return true; +} + +AtosSymbolizer::AtosSymbolizer(const char *path, LowLevelAllocator *allocator) + : process_(new(*allocator) AtosSymbolizerProcess(path, getpid())) {} + +bool AtosSymbolizer::SymbolizePC(uptr addr, SymbolizedStack *stack) { + if (!process_) return false; + char command[32]; + internal_snprintf(command, sizeof(command), "0x%zx\n", addr); + const char *buf = process_->SendCommand(command); + if (!buf) return false; + if (!ParseCommandOutput(buf, stack)) { + process_ = nullptr; + return false; + } + return true; +} + +bool AtosSymbolizer::SymbolizeData(uptr addr, DataInfo *info) { return false; } + +} // namespace __sanitizer + +#endif // SANITIZER_MAC diff --git a/contrib/compiler-rt/lib/sanitizer_common/sanitizer_symbolizer_mac.h b/contrib/compiler-rt/lib/sanitizer_common/sanitizer_symbolizer_mac.h new file mode 100644 index 0000000..068644d --- /dev/null +++ b/contrib/compiler-rt/lib/sanitizer_common/sanitizer_symbolizer_mac.h @@ -0,0 +1,48 @@ +//===-- sanitizer_symbolizer_mac.h ------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file is shared between various sanitizers' runtime libraries. +// +// Header for Mac-specific "atos" symbolizer. +//===----------------------------------------------------------------------===// + +#ifndef SANITIZER_SYMBOLIZER_MAC_H +#define SANITIZER_SYMBOLIZER_MAC_H + +#include "sanitizer_platform.h" +#if SANITIZER_MAC + +#include "sanitizer_symbolizer_internal.h" + +namespace __sanitizer { + +class DlAddrSymbolizer : public SymbolizerTool { + public: + bool SymbolizePC(uptr addr, SymbolizedStack *stack) override; + bool SymbolizeData(uptr addr, DataInfo *info) override; +}; + +class AtosSymbolizerProcess; + +class AtosSymbolizer : public SymbolizerTool { + public: + explicit AtosSymbolizer(const char *path, LowLevelAllocator *allocator); + + bool SymbolizePC(uptr addr, SymbolizedStack *stack) override; + bool SymbolizeData(uptr addr, DataInfo *info) override; + + private: + AtosSymbolizerProcess *process_; +}; + +} // namespace __sanitizer + +#endif // SANITIZER_MAC + +#endif // SANITIZER_SYMBOLIZER_MAC_H diff --git a/contrib/compiler-rt/lib/sanitizer_common/sanitizer_symbolizer_posix_libcdep.cc b/contrib/compiler-rt/lib/sanitizer_common/sanitizer_symbolizer_posix_libcdep.cc index 69ac18e..cb8455a 100644 --- a/contrib/compiler-rt/lib/sanitizer_common/sanitizer_symbolizer_posix_libcdep.cc +++ b/contrib/compiler-rt/lib/sanitizer_common/sanitizer_symbolizer_posix_libcdep.cc @@ -21,12 +21,10 @@ #include "sanitizer_linux.h" #include "sanitizer_placement_new.h" #include "sanitizer_procmaps.h" -#include "sanitizer_symbolizer.h" +#include "sanitizer_symbolizer_internal.h" #include "sanitizer_symbolizer_libbacktrace.h" +#include "sanitizer_symbolizer_mac.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, @@ -41,7 +39,7 @@ namespace __cxxabiv1 { namespace __sanitizer { // Attempts to demangle the name via __cxa_demangle from __cxxabiv1. -static const char *DemangleCXXABI(const char *name) { +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 @@ -55,258 +53,67 @@ static const char *DemangleCXXABI(const char *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; +// Parses one or more two-line strings in the following format: +// <function_name> +// <file_name>:<line_number>[:<column_number>] +// Used by LLVMSymbolizer, Addr2LinePool and InternalSymbolizer, since all of +// them use the same output format. +static void ParseSymbolizePCOutput(const char *str, SymbolizedStack *res) { + bool top_frame = true; + SymbolizedStack *last = res; + while (true) { + char *function_name = 0; + str = ExtractToken(str, "\n", &function_name); + CHECK(function_name); + if (function_name[0] == '\0') { + // There are no more frames. + InternalFree(function_name); + break; } - 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; + SymbolizedStack *cur; + if (top_frame) { + cur = res; + top_frame = false; + } else { + cur = SymbolizedStack::New(res->info.address); + cur->info.FillModuleInfo(res->info.module, res->info.module_offset); + last->next = cur; + last = cur; } - 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); - - // Real fork() may call user callbacks registered with pthread_atfork(). - int pid = internal_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 = sysconf(_SC_OPEN_MAX); fd > 2; fd--) - internal_close(fd); - ExecuteWithDefaultArgs(path_); - internal__exit(1); + AddressInfo *info = &cur->info; + 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; } - - // 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; + if (0 == internal_strcmp(info->file, "??")) { + InternalFree(info->file); + info->file = 0; } - - 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_; -}; +// Parses a two-line string in the following format: +// <symbol_name> +// <start_address> <size> +// Used by LLVMSymbolizer and InternalSymbolizer. +static void ParseSymbolizeDataOutput(const char *str, DataInfo *info) { + str = ExtractToken(str, "\n", &info->name); + str = ExtractUptr(str, " ", &info->start); + str = ExtractUptr(str, "\n", &info->size); +} // For now we assume the following protocol: // For each request of the form @@ -323,21 +130,16 @@ class LLVMSymbolizerProcess : public SymbolizerProcess { 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 { + bool ReachedEndOfOutput(const char *buffer, uptr length) const override { // 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__) + void ExecuteWithDefaultArgs(const char *path_to_binary) const override { +#if defined(__x86_64h__) + const char* const kSymbolizerArch = "--default-arch=x86_64h"; +#elif defined(__x86_64__) const char* const kSymbolizerArch = "--default-arch=x86_64"; #elif defined(__i386__) const char* const kSymbolizerArch = "--default-arch=i386"; @@ -357,6 +159,44 @@ class LLVMSymbolizerProcess : public SymbolizerProcess { } }; +class LLVMSymbolizer : public SymbolizerTool { + public: + explicit LLVMSymbolizer(const char *path, LowLevelAllocator *allocator) + : symbolizer_process_(new(*allocator) LLVMSymbolizerProcess(path)) {} + + bool SymbolizePC(uptr addr, SymbolizedStack *stack) override { + if (const char *buf = SendCommand(/*is_data*/ false, stack->info.module, + stack->info.module_offset)) { + ParseSymbolizePCOutput(buf, stack); + return true; + } + return false; + } + + bool SymbolizeData(uptr addr, DataInfo *info) override { + if (const char *buf = + SendCommand(/*is_data*/ true, info->module, info->module_offset)) { + ParseSymbolizeDataOutput(buf, info); + info->start += (addr - info->module_offset); // Add the base address. + return true; + } + return false; + } + + private: + const char *SendCommand(bool is_data, const char *module_name, + uptr module_offset) { + CHECK(module_name); + internal_snprintf(buffer_, kBufferSize, "%s\"%s\" 0x%zx\n", + is_data ? "DATA " : "", module_name, module_offset); + return symbolizer_process_->SendCommand(buffer_); + } + + LLVMSymbolizerProcess *symbolizer_process_; + static const uptr kBufferSize = 16 * 1024; + char buffer_[kBufferSize]; +}; + class Addr2LineProcess : public SymbolizerProcess { public: Addr2LineProcess(const char *path, const char *module_name) @@ -365,16 +205,7 @@ class Addr2LineProcess : public SymbolizerProcess { 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 { + bool ReachedEndOfOutput(const char *buffer, uptr length) const override { // Output should consist of two lines. int num_lines = 0; for (uptr i = 0; i < length; ++i) { @@ -386,23 +217,35 @@ class Addr2LineProcess : public SymbolizerProcess { return false; } - void ExecuteWithDefaultArgs(const char *path_to_binary) const { + void ExecuteWithDefaultArgs(const char *path_to_binary) const override { execl(path_to_binary, path_to_binary, "-Cfe", module_name_, (char *)0); } const char *module_name_; // Owned, leaked. }; -class Addr2LinePool : public ExternalSymbolizerInterface { +class Addr2LinePool : public SymbolizerTool { 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; + bool SymbolizePC(uptr addr, SymbolizedStack *stack) override { + if (const char *buf = + SendCommand(stack->info.module, stack->info.module_offset)) { + ParseSymbolizePCOutput(buf, stack); + return true; + } + return false; + } + + bool SymbolizeData(uptr addr, DataInfo *info) override { + return false; + } + + private: + const char *SendCommand(const char *module_name, uptr module_offset) { Addr2LineProcess *addr2line = 0; for (uptr i = 0; i < addr2line_pool_.size(); ++i) { if (0 == @@ -416,10 +259,13 @@ class Addr2LinePool : public ExternalSymbolizerInterface { new(*allocator_) Addr2LineProcess(addr2line_path_, module_name); addr2line_pool_.push_back(addr2line); } - return addr2line->SendCommand(is_data, module_name, module_offset); + CHECK_EQ(0, internal_strcmp(module_name, addr2line->module_name())); + char buffer_[kBufferSize]; + internal_snprintf(buffer_, kBufferSize, "0x%zx\n", module_offset); + return addr2line->SendCommand(buffer_); } - private: + static const uptr kBufferSize = 32; const char *addr2line_path_; LowLevelAllocator *allocator_; InternalMmapVector<Addr2LineProcess*> addr2line_pool_; @@ -440,10 +286,8 @@ int __sanitizer_symbolize_demangle(const char *Name, char *Buffer, int MaxLength); } // extern "C" -class InternalSymbolizer { +class InternalSymbolizer : public SymbolizerTool { public: - typedef bool (*SanitizerSymbolizeFn)(const char*, u64, char*, int); - static InternalSymbolizer *get(LowLevelAllocator *alloc) { if (__sanitizer_symbolize_code != 0 && __sanitizer_symbolize_data != 0) { @@ -452,20 +296,29 @@ class 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; + bool SymbolizePC(uptr addr, SymbolizedStack *stack) override { + bool result = __sanitizer_symbolize_code( + stack->info.module, stack->info.module_offset, buffer_, kBufferSize); + if (result) ParseSymbolizePCOutput(buffer_, stack); + return result; } - void Flush() { + bool SymbolizeData(uptr addr, DataInfo *info) override { + bool result = __sanitizer_symbolize_data(info->module, info->module_offset, + buffer_, kBufferSize); + if (result) { + ParseSymbolizeDataOutput(buffer_, info); + info->start += (addr - info->module_offset); // Add the base address. + } + return result; + } + + void Flush() override { if (__sanitizer_symbolize_flush) __sanitizer_symbolize_flush(); } - const char *Demangle(const char *name) { + const char *Demangle(const char *name) override { if (__sanitizer_symbolize_demangle) { for (uptr res_length = 1024; res_length <= InternalSizeClassMap::kMaxSize;) { @@ -492,273 +345,101 @@ class InternalSymbolizer { }; #else // SANITIZER_SUPPORTS_WEAK_HOOKS -class InternalSymbolizer { +class InternalSymbolizer : public SymbolizerTool { 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) {} - - SymbolizedStack *SymbolizePC(uptr addr) override { - BlockingMutexLock l(&mu_); - const char *module_name; - uptr module_offset; - if (!FindModuleNameAndOffsetForAddress(addr, &module_name, &module_offset)) - return SymbolizedStack::New(addr); - // First, try to use libbacktrace symbolizer (if it's available). - if (libbacktrace_symbolizer_ != 0) { - mu_.CheckLocked(); - if (SymbolizedStack *res = libbacktrace_symbolizer_->SymbolizeCode( - addr, module_name, module_offset)) - return res; - } - // Always fill data about module name and offset. - SymbolizedStack *res = SymbolizedStack::New(addr); - res->info.FillAddressAndModuleInfo(addr, module_name, module_offset); - - const char *str = SendCommand(false, module_name, module_offset); - if (str == 0) { - // Symbolizer was not initialized or failed. - return res; - } - - bool top_frame = true; - SymbolizedStack *last = res; - while (true) { - char *function_name = 0; - str = ExtractToken(str, "\n", &function_name); - CHECK(function_name); - if (function_name[0] == '\0') { - // There are no more frames. - break; - } - SymbolizedStack *cur; - if (top_frame) { - cur = res; - top_frame = false; - } else { - cur = SymbolizedStack::New(addr); - cur->info.FillAddressAndModuleInfo(addr, module_name, module_offset); - last->next = cur; - last = cur; - } - - AddressInfo *info = &cur->info; - 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; - } - } - return res; - } - - bool SymbolizeData(uptr addr, DataInfo *info) override { - 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(); - info->Clear(); - 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(addr, 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) override { - BlockingMutexLock l(&mu_); - return FindModuleNameAndOffsetForAddress(pc, module_name, module_address); - } - - bool CanReturnFileLineInfo() override { - return internal_symbolizer_ != 0 || external_symbolizer_ != 0 || - libbacktrace_symbolizer_ != 0; - } - - void Flush() override { - BlockingMutexLock l(&mu_); - if (internal_symbolizer_ != 0) { - SymbolizerScope sym_scope(this); - internal_symbolizer_->Flush(); - } - } +const char *Symbolizer::PlatformDemangle(const char *name) { + return DemangleCXXABI(name); +} - const char *Demangle(const char *name) override { - 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; +void Symbolizer::PlatformPrepareForSandboxing() {} + +static SymbolizerTool *ChooseExternalSymbolizer(LowLevelAllocator *allocator) { + const char *path = common_flags()->external_symbolizer_path; + const char *binary_name = path ? StripModuleName(path) : ""; + if (path && path[0] == '\0') { + VReport(2, "External symbolizer is explicitly disabled.\n"); + return nullptr; + } else if (!internal_strcmp(binary_name, "llvm-symbolizer")) { + VReport(2, "Using llvm-symbolizer at user-specified path: %s\n", path); + return new(*allocator) LLVMSymbolizer(path, allocator); + } else if (!internal_strcmp(binary_name, "atos")) { +#if SANITIZER_MAC + VReport(2, "Using atos at user-specified path: %s\n", path); + return new(*allocator) AtosSymbolizer(path, allocator); +#else // SANITIZER_MAC + Report("ERROR: Using `atos` is only supported on Darwin.\n"); + Die(); +#endif // SANITIZER_MAC + } else if (!internal_strcmp(binary_name, "addr2line")) { + VReport(2, "Using addr2line at user-specified path: %s\n", path); + return new(*allocator) Addr2LinePool(path, allocator); + } else if (path) { + Report("ERROR: External symbolizer path is set to '%s' which isn't " + "a known symbolizer. Please set the path to the llvm-symbolizer " + "binary or other known tool.\n", path); + Die(); + } + + // Otherwise symbolizer program is unknown, let's search $PATH + CHECK(path == nullptr); + if (const char *found_path = FindPathToBinary("llvm-symbolizer")) { + VReport(2, "Using llvm-symbolizer found at: %s\n", found_path); + return new(*allocator) LLVMSymbolizer(found_path, allocator); + } +#if SANITIZER_MAC + if (const char *found_path = FindPathToBinary("atos")) { + VReport(2, "Using atos found at: %s\n", found_path); + return new(*allocator) AtosSymbolizer(found_path, allocator); + } +#endif // SANITIZER_MAC + if (common_flags()->allow_addr2line) { + if (const char *found_path = FindPathToBinary("addr2line")) { + VReport(2, "Using addr2line found at: %s\n", found_path); + return new(*allocator) Addr2LinePool(found_path, allocator); } - if (internal_symbolizer_ != 0) - return internal_symbolizer_->Demangle(name); - return DemangleCXXABI(name); } + return nullptr; +} - void PrepareForSandboxing() override { -#if SANITIZER_LINUX && !SANITIZER_ANDROID - BlockingMutexLock l(&mu_); - // Cache /proc/self/exe on Linux. - CacheBinaryName(); -#endif +static void ChooseSymbolizerTools(IntrusiveList<SymbolizerTool> *list, + LowLevelAllocator *allocator) { + if (!common_flags()->symbolize) { + VReport(2, "Symbolizer is disabled.\n"); + return; } - - 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; + if (SymbolizerTool *tool = InternalSymbolizer::get(allocator)) { + VReport(2, "Using internal symbolizer.\n"); + list->push_back(tool); + return; } - - 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; + if (SymbolizerTool *tool = LibbacktraceSymbolizer::get(allocator)) { + VReport(2, "Using libbacktrace symbolizer.\n"); + list->push_back(tool); + return; } - 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; + if (SymbolizerTool *tool = ChooseExternalSymbolizer(allocator)) { + list->push_back(tool); + } else { + VReport(2, "No internal or external symbolizer found.\n"); } - // 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. -}; +#if SANITIZER_MAC + VReport(2, "Using dladdr symbolizer.\n"); + list->push_back(new(*allocator) DlAddrSymbolizer()); +#endif // SANITIZER_MAC +} Symbolizer *Symbolizer::PlatformInit() { - 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) { - const char *path_to_external = common_flags()->external_symbolizer_path; - 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); + IntrusiveList<SymbolizerTool> list; + list.clear(); + ChooseSymbolizerTools(&list, &symbolizer_allocator_); + return new(symbolizer_allocator_) Symbolizer(list); } } // namespace __sanitizer diff --git a/contrib/compiler-rt/lib/sanitizer_common/sanitizer_symbolizer_process_libcdep.cc b/contrib/compiler-rt/lib/sanitizer_common/sanitizer_symbolizer_process_libcdep.cc new file mode 100644 index 0000000..f1c01a3 --- /dev/null +++ b/contrib/compiler-rt/lib/sanitizer_common/sanitizer_symbolizer_process_libcdep.cc @@ -0,0 +1,229 @@ +//===-- sanitizer_symbolizer_process_libcdep.cc ---------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// Implementation of SymbolizerProcess used by external symbolizers. +// +//===----------------------------------------------------------------------===// + +#include "sanitizer_platform.h" +#if SANITIZER_POSIX +#include "sanitizer_posix.h" +#include "sanitizer_symbolizer_internal.h" + +#include <errno.h> +#include <stdlib.h> +#include <sys/wait.h> +#include <unistd.h> + +#if SANITIZER_MAC +#include <util.h> // for forkpty() +#endif // SANITIZER_MAC + +namespace __sanitizer { + +SymbolizerProcess::SymbolizerProcess(const char *path, bool use_forkpty) + : path_(path), + input_fd_(kInvalidFd), + output_fd_(kInvalidFd), + times_restarted_(0), + failed_to_start_(false), + reported_invalid_path_(false), + use_forkpty_(use_forkpty) { + CHECK(path_); + CHECK_NE(path_[0], '\0'); +} + +const char *SymbolizerProcess::SendCommand(const char *command) { + for (; times_restarted_ < kMaxTimesRestarted; times_restarted_++) { + // Start or restart symbolizer if we failed to send command to it. + if (const char *res = SendCommandImpl(command)) + return res; + Restart(); + } + if (!failed_to_start_) { + Report("WARNING: Failed to use and restart external symbolizer!\n"); + failed_to_start_ = true; + } + return 0; +} + +bool SymbolizerProcess::Restart() { + if (input_fd_ != kInvalidFd) + internal_close(input_fd_); + if (output_fd_ != kInvalidFd) + internal_close(output_fd_); + return StartSymbolizerSubprocess(); +} + +const char *SymbolizerProcess::SendCommandImpl(const char *command) { + if (input_fd_ == kInvalidFd || output_fd_ == kInvalidFd) + return 0; + if (!WriteToSymbolizer(command, internal_strlen(command))) + return 0; + if (!ReadFromSymbolizer(buffer_, kBufferSize)) + return 0; + return buffer_; +} + +bool SymbolizerProcess::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 SymbolizerProcess::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 SymbolizerProcess::StartSymbolizerSubprocess() { + if (!FileExists(path_)) { + if (!reported_invalid_path_) { + Report("WARNING: invalid path to external symbolizer!\n"); + reported_invalid_path_ = true; + } + return false; + } + + int pid; + if (use_forkpty_) { +#if SANITIZER_MAC + fd_t fd = kInvalidFd; + // Use forkpty to disable buffering in the new terminal. + pid = forkpty(&fd, 0, 0, 0); + if (pid == -1) { + // forkpty() failed. + Report("WARNING: failed to fork external symbolizer (errno: %d)\n", + errno); + return false; + } else if (pid == 0) { + // Child subprocess. + ExecuteWithDefaultArgs(path_); + internal__exit(1); + } + + // Continue execution in parent process. + input_fd_ = output_fd_ = fd; + + // Disable echo in the new terminal, disable CR. + struct termios termflags; + tcgetattr(fd, &termflags); + termflags.c_oflag &= ~ONLCR; + termflags.c_lflag &= ~ECHO; + tcsetattr(fd, TCSANOW, &termflags); +#else // SANITIZER_MAC + UNIMPLEMENTED(); +#endif // SANITIZER_MAC + } else { + 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); + + // Real fork() may call user callbacks registered with pthread_atfork(). + pid = internal_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 = sysconf(_SC_OPEN_MAX); 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; +} + +} // namespace __sanitizer + +#endif // SANITIZER_POSIX diff --git a/contrib/compiler-rt/lib/sanitizer_common/sanitizer_symbolizer_win.cc b/contrib/compiler-rt/lib/sanitizer_common/sanitizer_symbolizer_win.cc index ed96a3a..31f3746 100644 --- a/contrib/compiler-rt/lib/sanitizer_common/sanitizer_symbolizer_win.cc +++ b/contrib/compiler-rt/lib/sanitizer_common/sanitizer_symbolizer_win.cc @@ -18,139 +18,130 @@ #include <dbghelp.h> #pragma comment(lib, "dbghelp.lib") -#include "sanitizer_symbolizer.h" +#include "sanitizer_symbolizer_win.h" +#include "sanitizer_symbolizer_internal.h" namespace __sanitizer { -class WinSymbolizer : public Symbolizer { - public: - WinSymbolizer() : initialized_(false) {} - - SymbolizedStack *SymbolizePC(uptr addr) override { - SymbolizedStack *frame = SymbolizedStack::New(addr); - - BlockingMutexLock l(&dbghelp_mu_); - InitializeIfNeeded(); - - // See http://msdn.microsoft.com/en-us/library/ms680578(VS.85).aspx - char buffer[sizeof(SYMBOL_INFO) + MAX_SYM_NAME * sizeof(CHAR)]; - PSYMBOL_INFO symbol = (PSYMBOL_INFO)buffer; - symbol->SizeOfStruct = sizeof(SYMBOL_INFO); - symbol->MaxNameLen = MAX_SYM_NAME; - DWORD64 offset = 0; - BOOL got_objname = SymFromAddr(GetCurrentProcess(), - (DWORD64)addr, &offset, symbol); - if (!got_objname) - return frame; - - DWORD unused; - IMAGEHLP_LINE64 line_info; - line_info.SizeOfStruct = sizeof(IMAGEHLP_LINE64); - BOOL got_fileline = SymGetLineFromAddr64(GetCurrentProcess(), (DWORD64)addr, - &unused, &line_info); - frame->info.function = internal_strdup(symbol->Name); - frame->info.function_offset = (uptr)offset; - if (got_fileline) { - frame->info.file = internal_strdup(line_info.FileName); - frame->info.line = line_info.LineNumber; - } - - IMAGEHLP_MODULE64 mod_info; - internal_memset(&mod_info, 0, sizeof(mod_info)); - mod_info.SizeOfStruct = sizeof(mod_info); - if (SymGetModuleInfo64(GetCurrentProcess(), addr, &mod_info)) - frame->info.FillAddressAndModuleInfo(addr, mod_info.ImageName, - addr - (uptr)mod_info.BaseOfImage); - return frame; - } +namespace { - bool CanReturnFileLineInfo() override { - return true; - } +bool is_dbghelp_initialized = false; - const char *Demangle(const char *name) override { - CHECK(initialized_); - static char demangle_buffer[1000]; - if (name[0] == '\01' && - UnDecorateSymbolName(name + 1, demangle_buffer, sizeof(demangle_buffer), - UNDNAME_NAME_ONLY)) - return demangle_buffer; - else - return name; - } +bool TrySymInitialize() { + SymSetOptions(SYMOPT_DEFERRED_LOADS | SYMOPT_UNDNAME | SYMOPT_LOAD_LINES); + return SymInitialize(GetCurrentProcess(), 0, TRUE); + // FIXME: We don't call SymCleanup() on exit yet - should we? +} - // FIXME: Implement GetModuleNameAndOffsetForPC(). - - private: - void InitializeIfNeeded() { - if (initialized_) - return; - if (!TrySymInitialize()) { - // OK, maybe the client app has called SymInitialize already. - // That's a bit unfortunate for us as all the DbgHelp functions are - // single-threaded and we can't coordinate with the app. - // FIXME: Can we stop the other threads at this point? - // Anyways, we have to reconfigure stuff to make sure that SymInitialize - // has all the appropriate options set. - // Cross our fingers and reinitialize DbgHelp. - Report("*** WARNING: Failed to initialize DbgHelp! ***\n"); - Report("*** Most likely this means that the app is already ***\n"); - Report("*** using DbgHelp, possibly with incompatible flags. ***\n"); - Report("*** Due to technical reasons, symbolization might crash ***\n"); - Report("*** or produce wrong results. ***\n"); - SymCleanup(GetCurrentProcess()); - TrySymInitialize(); - } - initialized_ = true; - - // When an executable is run from a location different from the one where it - // was originally built, we may not see the nearby PDB files. - // To work around this, let's append the directory of the main module - // to the symbol search path. All the failures below are not fatal. - const size_t kSymPathSize = 2048; - static wchar_t path_buffer[kSymPathSize + 1 + MAX_PATH]; - if (!SymGetSearchPathW(GetCurrentProcess(), path_buffer, kSymPathSize)) { - Report("*** WARNING: Failed to SymGetSearchPathW ***\n"); - return; - } - size_t sz = wcslen(path_buffer); - if (sz) { - CHECK_EQ(0, wcscat_s(path_buffer, L";")); - sz++; - } - DWORD res = GetModuleFileNameW(NULL, path_buffer + sz, MAX_PATH); - if (res == 0 || res == MAX_PATH) { - Report("*** WARNING: Failed to getting the EXE directory ***\n"); - return; - } - // Write the zero character in place of the last backslash to get the - // directory of the main module at the end of path_buffer. - wchar_t *last_bslash = wcsrchr(path_buffer + sz, L'\\'); - CHECK_NE(last_bslash, 0); - *last_bslash = L'\0'; - if (!SymSetSearchPathW(GetCurrentProcess(), path_buffer)) { - Report("*** WARNING: Failed to SymSetSearchPathW\n"); - return; - } +// Initializes DbgHelp library, if it's not yet initialized. Calls to this +// function should be synchronized with respect to other calls to DbgHelp API +// (e.g. from WinSymbolizerTool). +void InitializeDbgHelpIfNeeded() { + if (is_dbghelp_initialized) + return; + if (!TrySymInitialize()) { + // OK, maybe the client app has called SymInitialize already. + // That's a bit unfortunate for us as all the DbgHelp functions are + // single-threaded and we can't coordinate with the app. + // FIXME: Can we stop the other threads at this point? + // Anyways, we have to reconfigure stuff to make sure that SymInitialize + // has all the appropriate options set. + // Cross our fingers and reinitialize DbgHelp. + Report("*** WARNING: Failed to initialize DbgHelp! ***\n"); + Report("*** Most likely this means that the app is already ***\n"); + Report("*** using DbgHelp, possibly with incompatible flags. ***\n"); + Report("*** Due to technical reasons, symbolization might crash ***\n"); + Report("*** or produce wrong results. ***\n"); + SymCleanup(GetCurrentProcess()); + TrySymInitialize(); + } + is_dbghelp_initialized = true; + + // When an executable is run from a location different from the one where it + // was originally built, we may not see the nearby PDB files. + // To work around this, let's append the directory of the main module + // to the symbol search path. All the failures below are not fatal. + const size_t kSymPathSize = 2048; + static wchar_t path_buffer[kSymPathSize + 1 + MAX_PATH]; + if (!SymGetSearchPathW(GetCurrentProcess(), path_buffer, kSymPathSize)) { + Report("*** WARNING: Failed to SymGetSearchPathW ***\n"); + return; + } + size_t sz = wcslen(path_buffer); + if (sz) { + CHECK_EQ(0, wcscat_s(path_buffer, L";")); + sz++; + } + DWORD res = GetModuleFileNameW(NULL, path_buffer + sz, MAX_PATH); + if (res == 0 || res == MAX_PATH) { + Report("*** WARNING: Failed to getting the EXE directory ***\n"); + return; + } + // Write the zero character in place of the last backslash to get the + // directory of the main module at the end of path_buffer. + wchar_t *last_bslash = wcsrchr(path_buffer + sz, L'\\'); + CHECK_NE(last_bslash, 0); + *last_bslash = L'\0'; + if (!SymSetSearchPathW(GetCurrentProcess(), path_buffer)) { + Report("*** WARNING: Failed to SymSetSearchPathW\n"); + return; } +} - bool TrySymInitialize() { - SymSetOptions(SYMOPT_DEFERRED_LOADS | SYMOPT_UNDNAME | SYMOPT_LOAD_LINES); - return SymInitialize(GetCurrentProcess(), 0, TRUE); - // FIXME: We don't call SymCleanup() on exit yet - should we? +} // namespace + +bool WinSymbolizerTool::SymbolizePC(uptr addr, SymbolizedStack *frame) { + InitializeDbgHelpIfNeeded(); + + // See http://msdn.microsoft.com/en-us/library/ms680578(VS.85).aspx + char buffer[sizeof(SYMBOL_INFO) + MAX_SYM_NAME * sizeof(CHAR)]; + PSYMBOL_INFO symbol = (PSYMBOL_INFO)buffer; + symbol->SizeOfStruct = sizeof(SYMBOL_INFO); + symbol->MaxNameLen = MAX_SYM_NAME; + DWORD64 offset = 0; + BOOL got_objname = SymFromAddr(GetCurrentProcess(), + (DWORD64)addr, &offset, symbol); + if (!got_objname) + return false; + + DWORD unused; + IMAGEHLP_LINE64 line_info; + line_info.SizeOfStruct = sizeof(IMAGEHLP_LINE64); + BOOL got_fileline = SymGetLineFromAddr64(GetCurrentProcess(), (DWORD64)addr, + &unused, &line_info); + frame->info.function = internal_strdup(symbol->Name); + frame->info.function_offset = (uptr)offset; + if (got_fileline) { + frame->info.file = internal_strdup(line_info.FileName); + frame->info.line = line_info.LineNumber; } + return true; +} + +const char *WinSymbolizerTool::Demangle(const char *name) { + CHECK(is_dbghelp_initialized); + static char demangle_buffer[1000]; + if (name[0] == '\01' && + UnDecorateSymbolName(name + 1, demangle_buffer, sizeof(demangle_buffer), + UNDNAME_NAME_ONLY)) + return demangle_buffer; + else + return name; +} - // All DbgHelp functions are single threaded, so we should use a mutex to - // serialize accesses. - BlockingMutex dbghelp_mu_; - bool initialized_; -}; +const char *Symbolizer::PlatformDemangle(const char *name) { + return name; +} + +void Symbolizer::PlatformPrepareForSandboxing() { + // Do nothing. +} Symbolizer *Symbolizer::PlatformInit() { - static bool called_once = false; - CHECK(!called_once && "Shouldn't create more than one symbolizer"); - called_once = true; - return new(symbolizer_allocator_) WinSymbolizer(); + IntrusiveList<SymbolizerTool> list; + list.clear(); + list.push_back(new(symbolizer_allocator_) WinSymbolizerTool()); + return new(symbolizer_allocator_) Symbolizer(list); } } // namespace __sanitizer diff --git a/contrib/compiler-rt/lib/sanitizer_common/sanitizer_symbolizer_win.h b/contrib/compiler-rt/lib/sanitizer_common/sanitizer_symbolizer_win.h new file mode 100644 index 0000000..72ac5e5 --- /dev/null +++ b/contrib/compiler-rt/lib/sanitizer_common/sanitizer_symbolizer_win.h @@ -0,0 +1,31 @@ +//===-- sanitizer_symbolizer_win.h ------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// Header file for the Windows symbolizer tool. +// +//===----------------------------------------------------------------------===// +#ifndef SANITIZER_SYMBOLIZER_WIN_H +#define SANITIZER_SYMBOLIZER_WIN_H + +#include "sanitizer_symbolizer_internal.h" + +namespace __sanitizer { + +class WinSymbolizerTool : public SymbolizerTool { + public: + bool SymbolizePC(uptr addr, SymbolizedStack *stack) override; + bool SymbolizeData(uptr addr, DataInfo *info) override { + return false; + } + const char *Demangle(const char *name) override; +}; + +} // namespace __sanitizer + +#endif // SANITIZER_SYMBOLIZER_WIN_H diff --git a/contrib/compiler-rt/lib/sanitizer_common/sanitizer_tls_get_addr.cc b/contrib/compiler-rt/lib/sanitizer_common/sanitizer_tls_get_addr.cc index 6142ce5..ea03715 100644 --- a/contrib/compiler-rt/lib/sanitizer_common/sanitizer_tls_get_addr.cc +++ b/contrib/compiler-rt/lib/sanitizer_common/sanitizer_tls_get_addr.cc @@ -78,7 +78,8 @@ void DTLS_Destroy() { DTLS_Deallocate(dtls.dtv, s); } -DTLS::DTV *DTLS_on_tls_get_addr(void *arg_void, void *res) { +DTLS::DTV *DTLS_on_tls_get_addr(void *arg_void, void *res, + uptr static_tls_begin, uptr static_tls_end) { if (!common_flags()->intercept_tls_get_addr) return 0; TlsGetAddrParam *arg = reinterpret_cast<TlsGetAddrParam *>(arg_void); uptr dso_id = arg->dso_id; @@ -95,6 +96,11 @@ DTLS::DTV *DTLS_on_tls_get_addr(void *arg_void, void *res) { tls_size = dtls.last_memalign_size; VPrintf(2, "__tls_get_addr: glibc <=2.18 suspected; tls={%p,%p}\n", tls_beg, tls_size); + } else if (tls_beg >= static_tls_begin && tls_beg < static_tls_end) { + // This is the static TLS block which was initialized / unpoisoned at thread + // creation. + VPrintf(2, "__tls_get_addr: static tls: %p\n", tls_beg); + tls_size = 0; } else if ((tls_beg % 4096) == sizeof(Glibc_2_19_tls_header)) { // We may want to check gnu_get_libc_version(). Glibc_2_19_tls_header *header = (Glibc_2_19_tls_header *)tls_beg - 1; diff --git a/contrib/compiler-rt/lib/sanitizer_common/sanitizer_tls_get_addr.h b/contrib/compiler-rt/lib/sanitizer_common/sanitizer_tls_get_addr.h index 0fc9a22..58d4763 100644 --- a/contrib/compiler-rt/lib/sanitizer_common/sanitizer_tls_get_addr.h +++ b/contrib/compiler-rt/lib/sanitizer_common/sanitizer_tls_get_addr.h @@ -50,7 +50,8 @@ struct DTLS { // Returns pointer and size of a linker-allocated TLS block. // Each block is returned exactly once. -DTLS::DTV *DTLS_on_tls_get_addr(void *arg, void *res); +DTLS::DTV *DTLS_on_tls_get_addr(void *arg, void *res, uptr static_tls_begin, + uptr static_tls_end); void DTLS_on_libc_memalign(void *ptr, uptr size); DTLS *DTLS_Get(); void DTLS_Destroy(); // Make sure to call this before the thread is destroyed. diff --git a/contrib/compiler-rt/lib/sanitizer_common/sanitizer_unwind_posix_libcdep.cc b/contrib/compiler-rt/lib/sanitizer_common/sanitizer_unwind_linux_libcdep.cc index 7ab2efb..1082ccf 100644 --- a/contrib/compiler-rt/lib/sanitizer_common/sanitizer_unwind_posix_libcdep.cc +++ b/contrib/compiler-rt/lib/sanitizer_common/sanitizer_unwind_linux_libcdep.cc @@ -1,4 +1,4 @@ -//===-- sanitizer_unwind_posix.cc ----------------------------------------===// +//===-- sanitizer_unwind_linux_libcdep.cc ---------------------------------===// // // The LLVM Compiler Infrastructure // @@ -8,11 +8,11 @@ //===----------------------------------------------------------------------===// // // This file contains the unwind.h-based (aka "slow") stack unwinding routines -// available to the tools on Linux, Android, FreeBSD and OS X. +// available to the tools on Linux, Android, and FreeBSD. //===----------------------------------------------------------------------===// #include "sanitizer_platform.h" -#if SANITIZER_POSIX +#if SANITIZER_FREEBSD || SANITIZER_LINUX #include "sanitizer_common.h" #include "sanitizer_stacktrace.h" @@ -82,7 +82,7 @@ void SanitizerInitializeUnwinder() { #endif uptr Unwind_GetIP(struct _Unwind_Context *ctx) { -#ifdef __arm__ +#if defined(__arm__) && !SANITIZER_MAC uptr val; _Unwind_VRS_Result res = _Unwind_VRS_Get(ctx, _UVRSC_CORE, 15 /* r15 = PC */, _UVRSD_UINT32, &val); @@ -155,4 +155,4 @@ void BufferedStackTrace::SlowUnwindStackWithContext(uptr pc, void *context, } // namespace __sanitizer -#endif // SANITIZER_POSIX +#endif // SANITIZER_FREEBSD || SANITIZER_LINUX diff --git a/contrib/compiler-rt/lib/sanitizer_common/sanitizer_win.cc b/contrib/compiler-rt/lib/sanitizer_common/sanitizer_win.cc index 335ceca..d5bbe45 100644 --- a/contrib/compiler-rt/lib/sanitizer_common/sanitizer_win.cc +++ b/contrib/compiler-rt/lib/sanitizer_common/sanitizer_win.cc @@ -35,7 +35,9 @@ namespace __sanitizer { // --------------------- sanitizer_common.h uptr GetPageSize() { - return 1U << 14; // FIXME: is this configurable? + // FIXME: there is an API for getting the system page size (GetSystemInfo or + // GetNativeSystemInfo), but if we use it here we get test failures elsewhere. + return 1U << 14; } uptr GetMmapGranularity() { @@ -93,6 +95,9 @@ void *MmapOrDie(uptr size, const char *mem_type) { } void UnmapOrDie(void *addr, uptr size) { + if (!size || !addr) + return; + if (VirtualFree(addr, size, MEM_DECOMMIT) == 0) { Report("ERROR: %s failed to " "deallocate 0x%zx (%zd) bytes at address %p (error code: %d)\n", @@ -101,9 +106,10 @@ void UnmapOrDie(void *addr, uptr size) { } } -void *MmapFixedNoReserve(uptr fixed_addr, uptr size) { +void *MmapFixedNoReserve(uptr fixed_addr, uptr size, const char *name) { // FIXME: is this really "NoReserve"? On Win32 this does not matter much, // but on Win64 it does. + (void)name; // unsupported void *p = VirtualAlloc((LPVOID)fixed_addr, size, MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE); if (p == 0) @@ -122,7 +128,8 @@ void *MmapNoReserveOrDie(uptr size, const char *mem_type) { return MmapOrDie(size, mem_type); } -void *Mprotect(uptr fixed_addr, uptr size) { +void *MmapNoAccess(uptr fixed_addr, uptr size, const char *name) { + (void)name; // unsupported void *res = VirtualAlloc((LPVOID)fixed_addr, size, MEM_RESERVE | MEM_COMMIT, PAGE_NOACCESS); if (res == 0) @@ -132,6 +139,12 @@ void *Mprotect(uptr fixed_addr, uptr size) { return res; } +bool MprotectNoAccess(uptr addr, uptr size) { + DWORD old_protection; + return VirtualProtect((LPVOID)addr, size, PAGE_NOACCESS, &old_protection); +} + + void FlushUnneededShadowMemory(uptr addr, uptr size) { // This is almost useless on 32-bits. // FIXME: add madvise-analog when we move to 64-bits. @@ -157,7 +170,7 @@ void *MapFileToMemory(const char *file_name, uptr *buff_size) { UNIMPLEMENTED(); } -void *MapWritableFileToMemory(void *addr, uptr size, uptr fd, uptr offset) { +void *MapWritableFileToMemory(void *addr, uptr size, fd_t fd, OFF_T offset) { UNIMPLEMENTED(); } @@ -206,76 +219,46 @@ u32 GetUid() { namespace { struct ModuleInfo { - HMODULE handle; + const char *filepath; uptr base_address; uptr end_address; }; int CompareModulesBase(const void *pl, const void *pr) { - const ModuleInfo &l = *(ModuleInfo *)pl, &r = *(ModuleInfo *)pr; - if (l.base_address < r.base_address) + const ModuleInfo *l = (ModuleInfo *)pl, *r = (ModuleInfo *)pr; + if (l->base_address < r->base_address) return -1; - return l.base_address > r.base_address; + return l->base_address > r->base_address; } } // namespace #ifndef SANITIZER_GO void DumpProcessMap() { Report("Dumping process modules:\n"); - HANDLE cur_process = GetCurrentProcess(); + InternalScopedBuffer<LoadedModule> modules(kMaxNumberOfModules); + uptr num_modules = + GetListOfModules(modules.data(), kMaxNumberOfModules, nullptr); - // Query the list of modules. Start by assuming there are no more than 256 - // modules and retry if that's not sufficient. - ModuleInfo *modules; - size_t num_modules; - { - HMODULE *hmodules = 0; - uptr modules_buffer_size = sizeof(HMODULE) * 256; - DWORD bytes_required; - while (!hmodules) { - hmodules = (HMODULE *)MmapOrDie(modules_buffer_size, __FUNCTION__); - CHECK(EnumProcessModules(cur_process, hmodules, modules_buffer_size, - &bytes_required)); - if (bytes_required > modules_buffer_size) { - // Either there turned out to be more than 256 hmodules, or new hmodules - // could have loaded since the last try. Retry. - UnmapOrDie(hmodules, modules_buffer_size); - hmodules = 0; - modules_buffer_size = bytes_required; - } - } - - num_modules = bytes_required / sizeof(HMODULE); - modules = - (ModuleInfo *)MmapOrDie(num_modules * sizeof(ModuleInfo), __FUNCTION__); - for (size_t i = 0; i < num_modules; ++i) { - modules[i].handle = hmodules[i]; - MODULEINFO mi; - if (!GetModuleInformation(cur_process, hmodules[i], &mi, sizeof(mi))) - continue; - modules[i].base_address = (uptr)mi.lpBaseOfDll; - modules[i].end_address = (uptr)mi.lpBaseOfDll + mi.SizeOfImage; - } - UnmapOrDie(hmodules, modules_buffer_size); + InternalScopedBuffer<ModuleInfo> module_infos(num_modules); + for (size_t i = 0; i < num_modules; ++i) { + module_infos[i].filepath = modules[i].full_name(); + module_infos[i].base_address = modules[i].base_address(); + module_infos[i].end_address = modules[i].ranges().next()->end; } - - qsort(modules, num_modules, sizeof(ModuleInfo), CompareModulesBase); + qsort(module_infos.data(), num_modules, sizeof(ModuleInfo), + CompareModulesBase); for (size_t i = 0; i < num_modules; ++i) { - const ModuleInfo &mi = modules[i]; - char module_name[MAX_PATH]; - bool got_module_name = GetModuleFileNameA( - mi.handle, module_name, sizeof(module_name)); + const ModuleInfo &mi = module_infos[i]; if (mi.end_address != 0) { Printf("\t%p-%p %s\n", mi.base_address, mi.end_address, - got_module_name ? module_name : "[no name]"); - } else if (got_module_name) { - Printf("\t??\?-??? %s\n", module_name); + mi.filepath[0] ? mi.filepath : "[no name]"); + } else if (mi.filepath[0]) { + Printf("\t??\?-??? %s\n", mi.filepath); } else { Printf("\t???\n"); } } - UnmapOrDie(modules, num_modules * sizeof(ModuleInfo)); } #endif @@ -288,8 +271,9 @@ void ReExec() { } void PrepareForSandboxing(__sanitizer_sandbox_arguments *args) { - (void)args; - // Nothing here for now. +#if !SANITIZER_GO + CovPrepareForSandboxing(args); +#endif } bool StackSizeIsUnlimited() { @@ -313,6 +297,14 @@ char *FindPathToBinary(const char *name) { return 0; } +bool IsPathSeparator(const char c) { + return c == '\\' || c == '/'; +} + +bool IsAbsolutePath(const char *path) { + UNIMPLEMENTED(); +} + void SleepForSeconds(int seconds) { Sleep(seconds * 1000); } @@ -333,117 +325,132 @@ void Abort() { uptr GetListOfModules(LoadedModule *modules, uptr max_modules, string_predicate_t filter) { - UNIMPLEMENTED(); -}; - -#ifndef SANITIZER_GO -int Atexit(void (*function)(void)) { - return atexit(function); -} -#endif + HANDLE cur_process = GetCurrentProcess(); -// ------------------ sanitizer_libc.h -uptr internal_mmap(void *addr, uptr length, int prot, int flags, - int fd, u64 offset) { - UNIMPLEMENTED(); -} + // Query the list of modules. Start by assuming there are no more than 256 + // modules and retry if that's not sufficient. + HMODULE *hmodules = 0; + uptr modules_buffer_size = sizeof(HMODULE) * 256; + DWORD bytes_required; + while (!hmodules) { + hmodules = (HMODULE *)MmapOrDie(modules_buffer_size, __FUNCTION__); + CHECK(EnumProcessModules(cur_process, hmodules, modules_buffer_size, + &bytes_required)); + if (bytes_required > modules_buffer_size) { + // Either there turned out to be more than 256 hmodules, or new hmodules + // could have loaded since the last try. Retry. + UnmapOrDie(hmodules, modules_buffer_size); + hmodules = 0; + modules_buffer_size = bytes_required; + } + } -uptr internal_munmap(void *addr, uptr length) { - UNIMPLEMENTED(); -} + // |num_modules| is the number of modules actually present, + // |count| is the number of modules we return. + size_t nun_modules = bytes_required / sizeof(HMODULE), + count = 0; + for (size_t i = 0; i < nun_modules && count < max_modules; ++i) { + HMODULE handle = hmodules[i]; + MODULEINFO mi; + if (!GetModuleInformation(cur_process, handle, &mi, sizeof(mi))) + continue; -uptr internal_close(fd_t fd) { - UNIMPLEMENTED(); -} + char module_name[MAX_PATH]; + bool got_module_name = + GetModuleFileNameA(handle, module_name, sizeof(module_name)); + if (!got_module_name) + module_name[0] = '\0'; + + if (filter && !filter(module_name)) + continue; + + uptr base_address = (uptr)mi.lpBaseOfDll; + uptr end_address = (uptr)mi.lpBaseOfDll + mi.SizeOfImage; + LoadedModule *cur_module = &modules[count]; + cur_module->set(module_name, base_address); + // We add the whole module as one single address range. + cur_module->addAddressRange(base_address, end_address, /*executable*/ true); + count++; + } + UnmapOrDie(hmodules, modules_buffer_size); -int internal_isatty(fd_t fd) { - return _isatty(fd); -} + return count; +}; -uptr internal_open(const char *filename, int flags) { - UNIMPLEMENTED(); -} +#ifndef SANITIZER_GO +// We can't use atexit() directly at __asan_init time as the CRT is not fully +// initialized at this point. Place the functions into a vector and use +// atexit() as soon as it is ready for use (i.e. after .CRT$XIC initializers). +InternalMmapVectorNoCtor<void (*)(void)> atexit_functions; -uptr internal_open(const char *filename, int flags, u32 mode) { - UNIMPLEMENTED(); +int Atexit(void (*function)(void)) { + atexit_functions.push_back(function); + return 0; } -uptr OpenFile(const char *filename, bool write) { - UNIMPLEMENTED(); +static int RunAtexit() { + int ret = 0; + for (uptr i = 0; i < atexit_functions.size(); ++i) { + ret |= atexit(atexit_functions[i]); + } + return ret; } -uptr internal_read(fd_t fd, void *buf, uptr count) { - UNIMPLEMENTED(); -} +#pragma section(".CRT$XID", long, read) // NOLINT +static __declspec(allocate(".CRT$XID")) int (*__run_atexit)() = RunAtexit; +#endif -uptr internal_write(fd_t fd, const void *buf, uptr count) { - if (fd != kStderrFd) +// ------------------ sanitizer_libc.h +fd_t OpenFile(const char *filename, FileAccessMode mode, error_t *last_error) { + if (mode != WrOnly) UNIMPLEMENTED(); - - static HANDLE output_stream = 0; - // Abort immediately if we know printing is not possible. - if (output_stream == INVALID_HANDLE_VALUE) - return 0; - - // If called for the first time, try to use stderr to output stuff, - // falling back to stdout if anything goes wrong. - bool fallback_to_stdout = false; - if (output_stream == 0) { - output_stream = GetStdHandle(STD_ERROR_HANDLE); - // We don't distinguish "no such handle" from error. - if (output_stream == 0) - output_stream = INVALID_HANDLE_VALUE; - - if (output_stream == INVALID_HANDLE_VALUE) { - // Retry with stdout? - output_stream = GetStdHandle(STD_OUTPUT_HANDLE); - if (output_stream == 0) - output_stream = INVALID_HANDLE_VALUE; - if (output_stream == INVALID_HANDLE_VALUE) - return 0; - } else { - // Successfully got an stderr handle. However, if WriteFile() fails, - // we can still try to fallback to stdout. - fallback_to_stdout = true; - } - } - - DWORD ret; - if (WriteFile(output_stream, buf, count, &ret, 0)) - return ret; - - // Re-try with stdout if using a valid stderr handle fails. - if (fallback_to_stdout) { - output_stream = GetStdHandle(STD_OUTPUT_HANDLE); - if (output_stream == 0) - output_stream = INVALID_HANDLE_VALUE; - if (output_stream != INVALID_HANDLE_VALUE) - return internal_write(fd, buf, count); - } - return 0; + fd_t res = CreateFile(filename, GENERIC_WRITE, 0, nullptr, CREATE_ALWAYS, + FILE_ATTRIBUTE_NORMAL, nullptr); + CHECK(res != kStdoutFd || kStdoutFd == kInvalidFd); + CHECK(res != kStderrFd || kStderrFd == kInvalidFd); + if (res == kInvalidFd && last_error) + *last_error = GetLastError(); + return res; } -uptr internal_stat(const char *path, void *buf) { - UNIMPLEMENTED(); +void CloseFile(fd_t fd) { + CloseHandle(fd); } -uptr internal_lstat(const char *path, void *buf) { +bool ReadFromFile(fd_t fd, void *buff, uptr buff_size, uptr *bytes_read, + error_t *error_p) { UNIMPLEMENTED(); } -uptr internal_fstat(fd_t fd, void *buf) { - UNIMPLEMENTED(); +bool SupportsColoredOutput(fd_t fd) { + // FIXME: support colored output. + return false; } -uptr internal_filesize(fd_t fd) { - UNIMPLEMENTED(); -} +bool WriteToFile(fd_t fd, const void *buff, uptr buff_size, uptr *bytes_written, + error_t *error_p) { + CHECK(fd != kInvalidFd); -uptr internal_dup2(int oldfd, int newfd) { - UNIMPLEMENTED(); + if (fd == kStdoutFd) { + fd = GetStdHandle(STD_OUTPUT_HANDLE); + if (fd == 0) fd = kInvalidFd; + } else if (fd == kStderrFd) { + fd = GetStdHandle(STD_ERROR_HANDLE); + if (fd == 0) fd = kInvalidFd; + } + + DWORD internal_bytes_written; + if (fd == kInvalidFd || + WriteFile(fd, buff, buff_size, &internal_bytes_written, 0)) { + if (error_p) *error_p = GetLastError(); + return false; + } else { + if (bytes_written) *bytes_written = internal_bytes_written; + return true; + } } -uptr internal_readlink(const char *path, char *buf, uptr bufsize) { +bool RenameFile(const char *oldpath, const char *newpath, error_t *error_p) { UNIMPLEMENTED(); } @@ -460,10 +467,6 @@ uptr internal_ftruncate(fd_t fd, uptr size) { UNIMPLEMENTED(); } -uptr internal_rename(const char *oldpath, const char *newpath) { - UNIMPLEMENTED(); -} - uptr GetRSS() { return 0; } @@ -587,7 +590,7 @@ void BufferedStackTrace::SlowUnwindStackWithContext(uptr pc, void *context, void ReportFile::Write(const char *buffer, uptr length) { SpinMutexLock l(mu); ReopenIfNecessary(); - if (length != internal_write(fd, buffer, length)) { + if (!WriteToFile(fd, buffer, length)) { // stderr may be closed, but we may be able to print to the debugger // instead. This is the case when launching a program from Visual Studio, // and the following routine should write to its console. @@ -614,10 +617,54 @@ bool IsDeadlySignal(int signum) { } bool IsAccessibleMemoryRange(uptr beg, uptr size) { - // FIXME: Actually implement this function. + SYSTEM_INFO si; + GetNativeSystemInfo(&si); + uptr page_size = si.dwPageSize; + uptr page_mask = ~(page_size - 1); + + for (uptr page = beg & page_mask, end = (beg + size - 1) & page_mask; + page <= end;) { + MEMORY_BASIC_INFORMATION info; + if (VirtualQuery((LPCVOID)page, &info, sizeof(info)) != sizeof(info)) + return false; + + if (info.Protect == 0 || info.Protect == PAGE_NOACCESS || + info.Protect == PAGE_EXECUTE) + return false; + + if (info.RegionSize == 0) + return false; + + page += info.RegionSize; + } + return true; } +SignalContext SignalContext::Create(void *siginfo, void *context) { + EXCEPTION_RECORD *exception_record = (EXCEPTION_RECORD*)siginfo; + CONTEXT *context_record = (CONTEXT*)context; + + uptr pc = (uptr)exception_record->ExceptionAddress; +#ifdef _WIN64 + uptr bp = (uptr)context_record->Rbp; + uptr sp = (uptr)context_record->Rsp; +#else + uptr bp = (uptr)context_record->Ebp; + uptr sp = (uptr)context_record->Esp; +#endif + uptr access_addr = exception_record->ExceptionInformation[1]; + + return SignalContext(context, access_addr, pc, sp, bp); +} + +uptr ReadBinaryName(/*out*/char *buf, uptr buf_len) { + // FIXME: Actually implement this function. + CHECK_GT(buf_len, 0); + buf[0] = 0; + return 0; +} + } // namespace __sanitizer #endif // _WIN32 diff --git a/contrib/compiler-rt/lib/sanitizer_common/scripts/check_lint.sh b/contrib/compiler-rt/lib/sanitizer_common/scripts/check_lint.sh index 7ed05d7..9108a81 100755 --- a/contrib/compiler-rt/lib/sanitizer_common/scripts/check_lint.sh +++ b/contrib/compiler-rt/lib/sanitizer_common/scripts/check_lint.sh @@ -17,7 +17,6 @@ fi # Filters # TODO: remove some of these filters -LLVM_LINT_FILTER=-,+whitespace COMMON_LINT_FILTER=-build/include,-build/header_guard,-legal/copyright,-whitespace/comments,-readability/casting,\ -build/namespaces ASAN_RTL_LINT_FILTER=${COMMON_LINT_FILTER},-runtime/int @@ -60,9 +59,6 @@ run_lint() { ${LITLINT} "$@" 2>>$ERROR_LOG } -run_lint ${LLVM_LINT_FILTER} --filter=${LLVM_LINT_FILTER} \ - lib/Transforms/Instrumentation/*Sanitizer.cpp & - if [ "${COMPILER_RT}" = "" ]; then COMPILER_RT=projects/compiler-rt fi diff --git a/contrib/compiler-rt/lib/sanitizer_common/scripts/cpplint.py b/contrib/compiler-rt/lib/sanitizer_common/scripts/cpplint.py index 742459a..d45c47f 100755 --- a/contrib/compiler-rt/lib/sanitizer_common/scripts/cpplint.py +++ b/contrib/compiler-rt/lib/sanitizer_common/scripts/cpplint.py @@ -1,4 +1,4 @@ -#!/usr/bin/python +#!/usr/bin/env python # # Copyright (c) 2009 Google Inc. All rights reserved. # diff --git a/contrib/compiler-rt/lib/sanitizer_common/scripts/gen_dynamic_list.py b/contrib/compiler-rt/lib/sanitizer_common/scripts/gen_dynamic_list.py index 7bab230..f055bb4 100755 --- a/contrib/compiler-rt/lib/sanitizer_common/scripts/gen_dynamic_list.py +++ b/contrib/compiler-rt/lib/sanitizer_common/scripts/gen_dynamic_list.py @@ -14,15 +14,24 @@ # gen_dynamic_list.py libclang_rt.*san*.a [ files ... ] # #===------------------------------------------------------------------------===# +import argparse import os import re import subprocess import sys -new_delete = set(['_ZdaPv', '_ZdaPvRKSt9nothrow_t', - '_ZdlPv', '_ZdlPvRKSt9nothrow_t', - '_Znam', '_ZnamRKSt9nothrow_t', - '_Znwm', '_ZnwmRKSt9nothrow_t']) +new_delete = set([ + '_Znam', '_ZnamRKSt9nothrow_t', # operator new[](unsigned long) + '_Znwm', '_ZnwmRKSt9nothrow_t', # operator new(unsigned long) + '_Znaj', '_ZnajRKSt9nothrow_t', # operator new[](unsigned int) + '_Znwj', '_ZnwjRKSt9nothrow_t', # operator new(unsigned int) + '_ZdaPv', '_ZdaPvRKSt9nothrow_t', # operator delete[](void *) + '_ZdlPv', '_ZdlPvRKSt9nothrow_t', # operator delete(void *) + '_ZdaPvm', # operator delete[](void*, unsigned long) + '_ZdlPvm', # operator delete(void*, unsigned long) + '_ZdaPvj', # operator delete[](void*, unsigned int) + '_ZdlPvj', # operator delete(void*, unsigned int) + ]) versioned_functions = set(['memcpy', 'pthread_attr_getaffinity_np', 'pthread_cond_broadcast', @@ -49,10 +58,17 @@ def get_global_functions(library): return functions def main(argv): + parser = argparse.ArgumentParser() + parser.add_argument('--version-list', action='store_true') + parser.add_argument('--extra', default=[], action='append') + parser.add_argument('libraries', default=[], nargs='+') + args = parser.parse_args() + result = [] - library = argv[1] - all_functions = get_global_functions(library) + all_functions = [] + for library in args.libraries: + all_functions.extend(get_global_functions(library)) function_set = set(all_functions) for func in all_functions: # Export new/delete operators. @@ -66,7 +82,7 @@ def main(argv): # We have to avoid exporting the interceptors for versioned library # functions due to gold internal error. orig_name = match.group(1) - if orig_name in function_set and orig_name not in versioned_functions: + if orig_name in function_set and (args.version_list or orig_name not in versioned_functions): result.append(orig_name) continue # Export sanitizer interface functions. @@ -74,15 +90,20 @@ def main(argv): result.append(func) # Additional exported functions from files. - for fname in argv[2:]: + for fname in args.extra: f = open(fname, 'r') for line in f: result.append(line.rstrip()) # Print the resulting list in the format recognized by ld. print('{') + if args.version_list: + print('global:') result.sort() for f in result: - print(' ' + f + ';') + print(' ' + f.encode('utf-8') + ';') + if args.version_list: + print('local:') + print(' *;') print('};') if __name__ == '__main__': diff --git a/contrib/compiler-rt/lib/sanitizer_common/scripts/litlint.py b/contrib/compiler-rt/lib/sanitizer_common/scripts/litlint.py index 1e78448..81b89c2 100755 --- a/contrib/compiler-rt/lib/sanitizer_common/scripts/litlint.py +++ b/contrib/compiler-rt/lib/sanitizer_common/scripts/litlint.py @@ -1,4 +1,4 @@ -#!/usr/bin/python +#!/usr/bin/env python # # litlint # diff --git a/contrib/compiler-rt/lib/sanitizer_common/scripts/sancov.py b/contrib/compiler-rt/lib/sanitizer_common/scripts/sancov.py index 566116e..a5ae957 100755 --- a/contrib/compiler-rt/lib/sanitizer_common/scripts/sancov.py +++ b/contrib/compiler-rt/lib/sanitizer_common/scripts/sancov.py @@ -4,40 +4,85 @@ # We need to merge these integers into a set and then # either print them (as hex) or dump them into another file. import array -import struct -import sys import bisect +import glob import os.path +import struct +import subprocess +import sys -prog_name = ""; +prog_name = "" def Usage(): print >> sys.stderr, "Usage: \n" + \ - " " + prog_name + " merge file1 [file2 ...] > output\n" \ - " " + prog_name + " print file1 [file2 ...]\n" \ - " " + prog_name + " unpack file1 [file2 ...]\n" \ - " " + prog_name + " rawunpack file1 [file2 ...]\n" + " " + prog_name + " merge FILE [FILE...] > OUTPUT\n" \ + " " + prog_name + " print FILE [FILE...]\n" \ + " " + prog_name + " unpack FILE [FILE...]\n" \ + " " + prog_name + " rawunpack FILE [FILE ...]\n" \ + " " + prog_name + " missing BINARY < LIST_OF_PCS\n" exit(1) +def CheckBits(bits): + if bits != 32 and bits != 64: + raise Exception("Wrong bitness: %d" % bits) + +def TypeCodeForBits(bits): + CheckBits(bits) + return 'L' if bits == 64 else 'I' + +kMagic32SecondHalf = 0xFFFFFF32; +kMagic64SecondHalf = 0xFFFFFF64; +kMagicFirstHalf = 0xC0BFFFFF; + +def MagicForBits(bits): + CheckBits(bits) + if sys.byteorder == 'little': + return [kMagic64SecondHalf if bits == 64 else kMagic32SecondHalf, kMagicFirstHalf] + else: + return [kMagicFirstHalf, kMagic64SecondHalf if bits == 64 else kMagic32SecondHalf] + +def ReadMagicAndReturnBitness(f, path): + magic_bytes = f.read(8) + magic_words = struct.unpack('II', magic_bytes); + bits = 0 + idx = 1 if sys.byteorder == 'little' else 0 + if magic_words[idx] == kMagicFirstHalf: + if magic_words[1-idx] == kMagic64SecondHalf: + bits = 64 + elif magic_words[1-idx] == kMagic32SecondHalf: + bits = 32 + if bits == 0: + raise Exception('Bad magic word in %s' % path) + return bits + def ReadOneFile(path): with open(path, mode="rb") as f: f.seek(0, 2) size = f.tell() f.seek(0, 0) - s = set(array.array('I', f.read(size))) - print >>sys.stderr, "%s: read %d PCs from %s" % (prog_name, size / 4, path) + if size < 8: + raise Exception('File %s is short (< 8 bytes)' % path) + bits = ReadMagicAndReturnBitness(f, path) + size -= 8 + s = array.array(TypeCodeForBits(bits), f.read(size)) + print >>sys.stderr, "%s: read %d %d-bit PCs from %s" % (prog_name, size * 8 / bits, bits, path) return s def Merge(files): s = set() for f in files: - s = s.union(ReadOneFile(f)) + s = s.union(set(ReadOneFile(f))) print >> sys.stderr, "%s: %d files merged; %d PCs total" % \ (prog_name, len(files), len(s)) return sorted(s) def PrintFiles(files): - s = Merge(files) + if len(files) > 1: + s = Merge(files) + else: # If there is just on file, print the PCs in order. + s = ReadOneFile(files[0]) + print >> sys.stderr, "%s: 1 file merged; %d PCs total" % \ + (prog_name, len(s)) for i in s: print "0x%x" % i @@ -45,7 +90,11 @@ def MergeAndPrint(files): if sys.stdout.isatty(): Usage() s = Merge(files) - a = array.array('I', s) + bits = 32 + if max(s) > 0xFFFFFFFF: + bits = 64 + array.array('I', MagicForBits(bits)).tofile(sys.stdout) + a = array.array(TypeCodeForBits(bits), s) a.tofile(sys.stdout) @@ -82,6 +131,8 @@ def UnpackOneRawFile(path, map_path): with open(map_path, mode="rt") as f_map: print >> sys.stderr, "%s: reading map %s" % (prog_name, map_path) bits = int(f_map.readline()) + if bits != 32 and bits != 64: + raise Exception('Wrong bits size in the map') for line in f_map: parts = line.rstrip().split() mem_map.append((int(parts[0], 16), @@ -97,11 +148,7 @@ def UnpackOneRawFile(path, map_path): f.seek(0, 2) size = f.tell() f.seek(0, 0) - if bits == 64: - typecode = 'L' - else: - typecode = 'I' - pcs = array.array(typecode, f.read(size)) + pcs = array.array(TypeCodeForBits(bits), f.read(size)) mem_map_pcs = [[] for i in range(0, len(mem_map))] for pc in pcs: @@ -119,9 +166,10 @@ def UnpackOneRawFile(path, map_path): assert path.endswith('.sancov.raw') dst_path = module_path + '.' + os.path.basename(path)[:-4] print >> sys.stderr, "%s: writing %d PCs to %s" % (prog_name, len(pc_list), dst_path) - arr = array.array('I') + arr = array.array(TypeCodeForBits(bits)) arr.fromlist(sorted(pc_list)) with open(dst_path, 'ab') as f2: + array.array('I', MagicForBits(bits)).tofile(f2) arr.tofile(f2) def RawUnpack(files): @@ -131,17 +179,63 @@ def RawUnpack(files): f_map = f[:-3] + 'map' UnpackOneRawFile(f, f_map) +def GetInstrumentedPCs(binary): + # This looks scary, but all it does is extract all offsets where we call: + # - __sanitizer_cov() or __sanitizer_cov_with_check(), + # - with call or callq, + # - directly or via PLT. + cmd = "objdump -d %s | " \ + "grep '^\s\+[0-9a-f]\+:.*\scall\(q\|\)\s\+[0-9a-f]\+ <__sanitizer_cov\(_with_check\|\)\(@plt\|\)>' | " \ + "grep '^\s\+[0-9a-f]\+' -o" % binary + proc = subprocess.Popen(cmd, stdin=subprocess.PIPE, stdout=subprocess.PIPE, + shell=True) + proc.stdin.close() + # The PCs we get from objdump are off by 4 bytes, as they point to the + # beginning of the callq instruction. Empirically this is true on x86 and + # x86_64. + return set(int(line.strip(), 16) + 4 for line in proc.stdout) + +def PrintMissing(binary): + if not os.path.isfile(binary): + raise Exception('File not found: %s' % binary) + instrumented = GetInstrumentedPCs(binary) + print >> sys.stderr, "%s: found %d instrumented PCs in %s" % (prog_name, + len(instrumented), + binary) + covered = set(int(line, 16) for line in sys.stdin) + print >> sys.stderr, "%s: read %d PCs from stdin" % (prog_name, len(covered)) + missing = instrumented - covered + print >> sys.stderr, "%s: %d PCs missing from coverage" % (prog_name, len(missing)) + if (len(missing) > len(instrumented) - len(covered)): + print >> sys.stderr, \ + "%s: WARNING: stdin contains PCs not found in binary" % prog_name + for pc in sorted(missing): + print "0x%x" % pc + if __name__ == '__main__': prog_name = sys.argv[0] if len(sys.argv) <= 2: Usage(); + + if sys.argv[1] == "missing": + if len(sys.argv) != 3: + Usage() + PrintMissing(sys.argv[2]) + exit(0) + + file_list = [] + for f in sys.argv[2:]: + file_list += glob.glob(f) + if not file_list: + Usage() + if sys.argv[1] == "print": - PrintFiles(sys.argv[2:]) + PrintFiles(file_list) elif sys.argv[1] == "merge": - MergeAndPrint(sys.argv[2:]) + MergeAndPrint(file_list) elif sys.argv[1] == "unpack": - Unpack(sys.argv[2:]) + Unpack(file_list) elif sys.argv[1] == "rawunpack": - RawUnpack(sys.argv[2:]) + RawUnpack(file_list) else: Usage() diff --git a/contrib/compiler-rt/lib/sanitizer_common/tests/sanitizer_libc_test.cc b/contrib/compiler-rt/lib/sanitizer_common/tests/sanitizer_libc_test.cc index 8712d2c..3252db7 100644 --- a/contrib/compiler-rt/lib/sanitizer_common/tests/sanitizer_libc_test.cc +++ b/contrib/compiler-rt/lib/sanitizer_common/tests/sanitizer_libc_test.cc @@ -17,6 +17,7 @@ #if SANITIZER_LINUX || SANITIZER_MAC # define SANITIZER_TEST_HAS_STAT_H 1 # include <sys/stat.h> +# include "sanitizer_common/sanitizer_posix.h" #else # define SANITIZER_TEST_HAS_STAT_H 0 #endif @@ -78,16 +79,14 @@ TEST(SanitizerCommon, FileOps) { char tmpfile[128]; temp_file_name(tmpfile, sizeof(tmpfile), "sanitizer_common.fileops.tmp."); - uptr openrv = OpenFile(tmpfile, true); - EXPECT_FALSE(internal_iserror(openrv)); - fd_t fd = openrv; + fd_t fd = OpenFile(tmpfile, WrOnly); + ASSERT_NE(fd, kInvalidFd); EXPECT_EQ(len1, internal_write(fd, str1, len1)); EXPECT_EQ(len2, internal_write(fd, str2, len2)); - internal_close(fd); + CloseFile(fd); - openrv = OpenFile(tmpfile, false); - EXPECT_FALSE(internal_iserror(openrv)); - fd = openrv; + fd = OpenFile(tmpfile, RdOnly); + ASSERT_NE(fd, kInvalidFd); uptr fsize = internal_filesize(fd); EXPECT_EQ(len1 + len2, fsize); @@ -115,7 +114,7 @@ TEST(SanitizerCommon, FileOps) { internal_memset(buf, 0, len1); EXPECT_EQ(len2, internal_read(fd, buf, len2)); EXPECT_EQ(0, internal_memcmp(buf, str2, len2)); - internal_close(fd); + CloseFile(fd); internal_unlink(tmpfile); } #endif @@ -134,12 +133,11 @@ TEST(SanitizerCommon, InternalMmapWithOffset) { char tmpfile[128]; temp_file_name(tmpfile, sizeof(tmpfile), "sanitizer_common.internalmmapwithoffset.tmp."); - uptr res = OpenFile(tmpfile, true); - ASSERT_FALSE(internal_iserror(res)); - fd_t fd = res; + fd_t fd = OpenFile(tmpfile, RdWr); + ASSERT_NE(fd, kInvalidFd); uptr page_size = GetPageSizeCached(); - res = internal_ftruncate(fd, page_size * 2); + uptr res = internal_ftruncate(fd, page_size * 2); ASSERT_FALSE(internal_iserror(res)); res = internal_lseek(fd, page_size, SEEK_SET); @@ -154,8 +152,8 @@ TEST(SanitizerCommon, InternalMmapWithOffset) { ASSERT_EQ('A', p[0]); ASSERT_EQ('B', p[1]); - internal_close(fd); - internal_munmap(p, page_size); + CloseFile(fd); + UnmapOrDie(p, page_size); internal_unlink(tmpfile); } #endif diff --git a/contrib/compiler-rt/lib/sanitizer_common/tests/sanitizer_posix_test.cc b/contrib/compiler-rt/lib/sanitizer_common/tests/sanitizer_posix_test.cc index 56ce416..03ca449 100644 --- a/contrib/compiler-rt/lib/sanitizer_common/tests/sanitizer_posix_test.cc +++ b/contrib/compiler-rt/lib/sanitizer_common/tests/sanitizer_posix_test.cc @@ -52,9 +52,9 @@ static void SpawnThread(uptr iteration) { TEST(SanitizerCommon, PthreadDestructorIterations) { ASSERT_EQ(0, pthread_key_create(&key, &destructor)); - SpawnThread(kPthreadDestructorIterations); + SpawnThread(GetPthreadDestructorIterations()); EXPECT_TRUE(destructor_executed); - SpawnThread(kPthreadDestructorIterations + 1); + SpawnThread(GetPthreadDestructorIterations() + 1); EXPECT_FALSE(destructor_executed); } diff --git a/contrib/compiler-rt/lib/sanitizer_common/tests/sanitizer_procmaps_test.cc b/contrib/compiler-rt/lib/sanitizer_common/tests/sanitizer_procmaps_test.cc index abe4ef4..12bc9e1 100644 --- a/contrib/compiler-rt/lib/sanitizer_common/tests/sanitizer_procmaps_test.cc +++ b/contrib/compiler-rt/lib/sanitizer_common/tests/sanitizer_procmaps_test.cc @@ -37,8 +37,7 @@ TEST(MemoryMappingLayout, DumpListOfModules) { const char *binary_name = last_slash ? last_slash + 1 : argv0; MemoryMappingLayout memory_mapping(false); const uptr kMaxModules = 100; - LoadedModule *modules = - (LoadedModule *)malloc(kMaxModules * sizeof(LoadedModule)); + LoadedModule modules[kMaxModules]; uptr n_modules = memory_mapping.DumpListOfModules(modules, kMaxModules, 0); EXPECT_GT(n_modules, 0U); bool found = false; @@ -51,7 +50,6 @@ TEST(MemoryMappingLayout, DumpListOfModules) { modules[i].clear(); } EXPECT_TRUE(found); - free(modules); } } // namespace __sanitizer diff --git a/contrib/compiler-rt/lib/sanitizer_common/tests/sanitizer_stacktrace_printer_test.cc b/contrib/compiler-rt/lib/sanitizer_common/tests/sanitizer_stacktrace_printer_test.cc index cc9a9ed..05796fc 100644 --- a/contrib/compiler-rt/lib/sanitizer_common/tests/sanitizer_stacktrace_printer_test.cc +++ b/contrib/compiler-rt/lib/sanitizer_common/tests/sanitizer_stacktrace_printer_test.cc @@ -18,20 +18,36 @@ namespace __sanitizer { TEST(SanitizerStacktracePrinter, RenderSourceLocation) { InternalScopedString str(128); - RenderSourceLocation(&str, "/dir/file.cc", 10, 5, ""); + RenderSourceLocation(&str, "/dir/file.cc", 10, 5, false, ""); EXPECT_STREQ("/dir/file.cc:10:5", str.data()); str.clear(); - RenderSourceLocation(&str, "/dir/file.cc", 11, 0, ""); + RenderSourceLocation(&str, "/dir/file.cc", 11, 0, false, ""); EXPECT_STREQ("/dir/file.cc:11", str.data()); str.clear(); - RenderSourceLocation(&str, "/dir/file.cc", 0, 0, ""); + RenderSourceLocation(&str, "/dir/file.cc", 0, 0, false, ""); EXPECT_STREQ("/dir/file.cc", str.data()); str.clear(); - RenderSourceLocation(&str, "/dir/file.cc", 10, 5, "/dir/"); + RenderSourceLocation(&str, "/dir/file.cc", 10, 5, false, "/dir/"); EXPECT_STREQ("file.cc:10:5", str.data()); + + str.clear(); + RenderSourceLocation(&str, "/dir/file.cc", 10, 5, true, ""); + EXPECT_STREQ("/dir/file.cc(10,5)", str.data()); + + str.clear(); + RenderSourceLocation(&str, "/dir/file.cc", 11, 0, true, ""); + EXPECT_STREQ("/dir/file.cc(11)", str.data()); + + str.clear(); + RenderSourceLocation(&str, "/dir/file.cc", 0, 0, true, ""); + EXPECT_STREQ("/dir/file.cc", str.data()); + + str.clear(); + RenderSourceLocation(&str, "/dir/file.cc", 10, 5, true, "/dir/"); + EXPECT_STREQ("file.cc(10,5)", str.data()); } TEST(SanitizerStacktracePrinter, RenderModuleLocation) { @@ -62,7 +78,7 @@ TEST(SanitizerStacktracePrinter, RenderFrame) { RenderFrame(&str, "%% Frame:%n PC:%p Module:%m ModuleOffset:%o " "Function:%f FunctionOffset:%q Source:%s Line:%l " "Column:%c", - frame_no, info, "/path/to/", "function_"); + frame_no, info, false, "/path/to/", "function_"); EXPECT_STREQ("% Frame:42 PC:0x400000 Module:my/module ModuleOffset:0x200 " "Function:foo FunctionOffset:0x100 Source:my/source Line:10 " "Column:5", @@ -72,50 +88,64 @@ TEST(SanitizerStacktracePrinter, RenderFrame) { // Test special format specifiers. info.address = 0x400000; - RenderFrame(&str, "%M", frame_no, info); + RenderFrame(&str, "%M", frame_no, info, false); EXPECT_NE(nullptr, internal_strstr(str.data(), "400000")); str.clear(); - RenderFrame(&str, "%L", frame_no, info); + RenderFrame(&str, "%L", frame_no, info, false); EXPECT_STREQ("(<unknown module>)", str.data()); str.clear(); info.module = internal_strdup("/path/to/module"); info.module_offset = 0x200; - RenderFrame(&str, "%M", frame_no, info); + RenderFrame(&str, "%M", frame_no, info, false); EXPECT_NE(nullptr, internal_strstr(str.data(), "(module+0x")); EXPECT_NE(nullptr, internal_strstr(str.data(), "200")); str.clear(); - RenderFrame(&str, "%L", frame_no, info); + RenderFrame(&str, "%L", frame_no, info, false); EXPECT_STREQ("(/path/to/module+0x200)", str.data()); str.clear(); info.function = internal_strdup("my_function"); - RenderFrame(&str, "%F", frame_no, info); + RenderFrame(&str, "%F", frame_no, info, false); EXPECT_STREQ("in my_function", str.data()); str.clear(); info.function_offset = 0x100; - RenderFrame(&str, "%F %S", frame_no, info); + RenderFrame(&str, "%F %S", frame_no, info, false); EXPECT_STREQ("in my_function+0x100 <null>", str.data()); str.clear(); info.file = internal_strdup("my_file"); - RenderFrame(&str, "%F %S", frame_no, info); + RenderFrame(&str, "%F %S", frame_no, info, false); EXPECT_STREQ("in my_function my_file", str.data()); str.clear(); info.line = 10; - RenderFrame(&str, "%F %S", frame_no, info); + RenderFrame(&str, "%F %S", frame_no, info, false); EXPECT_STREQ("in my_function my_file:10", str.data()); str.clear(); info.column = 5; - RenderFrame(&str, "%S %L", frame_no, info); + RenderFrame(&str, "%S %L", frame_no, info, false); EXPECT_STREQ("my_file:10:5 my_file:10:5", str.data()); str.clear(); + RenderFrame(&str, "%S %L", frame_no, info, true); + EXPECT_STREQ("my_file(10,5) my_file(10,5)", str.data()); + str.clear(); + + info.column = 0; + RenderFrame(&str, "%F %S", frame_no, info, true); + EXPECT_STREQ("in my_function my_file(10)", str.data()); + str.clear(); + + info.line = 0; + RenderFrame(&str, "%F %S", frame_no, info, true); + EXPECT_STREQ("in my_function my_file", str.data()); + str.clear(); + info.Clear(); } diff --git a/contrib/compiler-rt/lib/sanitizer_common/tests/sanitizer_stacktrace_test.cc b/contrib/compiler-rt/lib/sanitizer_common/tests/sanitizer_stacktrace_test.cc index ac820c2..654ea1d 100644 --- a/contrib/compiler-rt/lib/sanitizer_common/tests/sanitizer_stacktrace_test.cc +++ b/contrib/compiler-rt/lib/sanitizer_common/tests/sanitizer_stacktrace_test.cc @@ -30,11 +30,11 @@ class FastUnwindTest : public ::testing::Test { } void *mapping; - uptr *fake_stack; + uhwptr *fake_stack; const uptr fake_stack_size = 10; - uptr start_pc; - uptr fake_top; - uptr fake_bottom; + uhwptr start_pc; + uhwptr fake_top; + uhwptr fake_bottom; BufferedStackTrace trace; }; @@ -45,10 +45,10 @@ static uptr PC(uptr idx) { void FastUnwindTest::SetUp() { size_t ps = GetPageSize(); mapping = MmapOrDie(2 * ps, "FastUnwindTest"); - Mprotect((uptr)mapping, ps); + MprotectNoAccess((uptr)mapping, ps); // Unwinder may peek 1 word down from the starting FP. - fake_stack = (uptr *)((uptr)mapping + ps + sizeof(uptr)); + fake_stack = (uhwptr *)((uptr)mapping + ps + sizeof(uhwptr)); // Fill an array of pointers with fake fp+retaddr pairs. Frame pointers have // even indices. @@ -57,12 +57,12 @@ void FastUnwindTest::SetUp() { fake_stack[i+1] = PC(i + 1); // retaddr } // Mark the last fp point back up to terminate the stack trace. - fake_stack[RoundDownTo(fake_stack_size - 1, 2)] = (uptr)&fake_stack[0]; + fake_stack[RoundDownTo(fake_stack_size - 1, 2)] = (uhwptr)&fake_stack[0]; // Top is two slots past the end because FastUnwindStack subtracts two. - fake_top = (uptr)&fake_stack[fake_stack_size + 2]; + fake_top = (uhwptr)&fake_stack[fake_stack_size + 2]; // Bottom is one slot before the start because FastUnwindStack uses >. - fake_bottom = (uptr)mapping; + fake_bottom = (uhwptr)mapping; start_pc = PC(0); } @@ -85,7 +85,7 @@ TEST_F(FastUnwindTest, Basic) { // From: http://code.google.com/p/address-sanitizer/issues/detail?id=162 TEST_F(FastUnwindTest, FramePointerLoop) { // Make one fp point to itself. - fake_stack[4] = (uptr)&fake_stack[4]; + fake_stack[4] = (uhwptr)&fake_stack[4]; if (!TryFastUnwind(kStackTraceMax)) return; // Should get all on-stack retaddrs up to the 4th slot and start_pc. @@ -114,7 +114,7 @@ TEST_F(FastUnwindTest, OneFrameStackTrace) { return; EXPECT_EQ(1U, trace.size); EXPECT_EQ(start_pc, trace.trace[0]); - EXPECT_EQ((uptr)&fake_stack[0], trace.top_frame_bp); + EXPECT_EQ((uhwptr)&fake_stack[0], trace.top_frame_bp); } TEST_F(FastUnwindTest, ZeroFramesStackTrace) { @@ -127,7 +127,7 @@ TEST_F(FastUnwindTest, ZeroFramesStackTrace) { TEST_F(FastUnwindTest, FPBelowPrevFP) { // The next FP points to unreadable memory inside the stack limits, but below // current FP. - fake_stack[0] = (uptr)&fake_stack[-50]; + fake_stack[0] = (uhwptr)&fake_stack[-50]; fake_stack[1] = PC(1); if (!TryFastUnwind(3)) return; diff --git a/contrib/compiler-rt/lib/sanitizer_common/tests/sanitizer_stoptheworld_test.cc b/contrib/compiler-rt/lib/sanitizer_common/tests/sanitizer_stoptheworld_test.cc index b6786ba..802af39 100644 --- a/contrib/compiler-rt/lib/sanitizer_common/tests/sanitizer_stoptheworld_test.cc +++ b/contrib/compiler-rt/lib/sanitizer_common/tests/sanitizer_stoptheworld_test.cc @@ -189,6 +189,16 @@ TEST(StopTheWorld, SuspendThreadsAdvanced) { pthread_mutex_destroy(&advanced_incrementer_thread_exit_mutex); } +static void SegvCallback(const SuspendedThreadsList &suspended_threads_list, + void *argument) { + *(volatile int*)0x1234 = 0; +} + +TEST(StopTheWorld, SegvInCallback) { + // Test that tracer thread catches SIGSEGV. + StopTheWorld(&SegvCallback, NULL); +} + } // namespace __sanitizer #endif // SANITIZER_LINUX && defined(__x86_64__) diff --git a/contrib/compiler-rt/lib/sanitizer_common/tests/sanitizer_symbolizer_test.cc b/contrib/compiler-rt/lib/sanitizer_common/tests/sanitizer_symbolizer_test.cc new file mode 100644 index 0000000..429ac59 --- /dev/null +++ b/contrib/compiler-rt/lib/sanitizer_common/tests/sanitizer_symbolizer_test.cc @@ -0,0 +1,58 @@ +//===-- sanitizer_symbolizer_test.cc --------------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// Tests for sanitizer_symbolizer.h and sanitizer_symbolizer_internal.h +// +//===----------------------------------------------------------------------===// + +#include "sanitizer_common/sanitizer_allocator_internal.h" +#include "sanitizer_common/sanitizer_symbolizer_internal.h" +#include "gtest/gtest.h" + +namespace __sanitizer { + +TEST(Symbolizer, ExtractToken) { + char *token; + const char *rest; + + rest = ExtractToken("a;b;c", ";", &token); + EXPECT_STREQ("a", token); + EXPECT_STREQ("b;c", rest); + InternalFree(token); + + rest = ExtractToken("aaa-bbb.ccc", ";.-*", &token); + EXPECT_STREQ("aaa", token); + EXPECT_STREQ("bbb.ccc", rest); + InternalFree(token); +} + +TEST(Symbolizer, ExtractInt) { + int token; + const char *rest = ExtractInt("123,456;789", ";,", &token); + EXPECT_EQ(123, token); + EXPECT_STREQ("456;789", rest); +} + +TEST(Symbolizer, ExtractUptr) { + uptr token; + const char *rest = ExtractUptr("123,456;789", ";,", &token); + EXPECT_EQ(123U, token); + EXPECT_STREQ("456;789", rest); +} + +TEST(Symbolizer, ExtractTokenUpToDelimiter) { + char *token; + const char *rest = + ExtractTokenUpToDelimiter("aaa-+-bbb-+-ccc", "-+-", &token); + EXPECT_STREQ("aaa", token); + EXPECT_STREQ("bbb-+-ccc", rest); + InternalFree(token); +} + +} // namespace __sanitizer |