diff options
89 files changed, 2142 insertions, 1027 deletions
diff --git a/gcc/ChangeLog b/gcc/ChangeLog index 11cad1a82d0..8dff131c7ee 100644 --- a/gcc/ChangeLog +++ b/gcc/ChangeLog @@ -1,3 +1,9 @@ +2013-02-13 Kostya Serebryany <kcc@google.com> + + * config/i386/i386.c: Use 0x7fff8000 as asan_shadow_offset on x86_64 + linux. + * sanitizer.def: Rename __asan_init to __asan_init_v1. + 2013-02-12 Dodji Seketeli <dodji@redhat.com> Avoid instrumenting duplicated memory access in the same basic block diff --git a/gcc/config/i386/i386.c b/gcc/config/i386/i386.c index caf4894f605..b8ef1c33efc 100644 --- a/gcc/config/i386/i386.c +++ b/gcc/config/i386/i386.c @@ -5436,7 +5436,9 @@ ix86_legitimate_combined_insn (rtx insn) static unsigned HOST_WIDE_INT ix86_asan_shadow_offset (void) { - return (unsigned HOST_WIDE_INT) 1 << (TARGET_LP64 ? 44 : 29); + return TARGET_LP64 ? (TARGET_MACHO ? (HOST_WIDE_INT_1 << 44) + : HOST_WIDE_INT_C (0x7fff8000)) + : (HOST_WIDE_INT_1 << 29); } /* Argument support functions. */ diff --git a/gcc/sanitizer.def b/gcc/sanitizer.def index ebc0b729d66..99f87e5c84b 100644 --- a/gcc/sanitizer.def +++ b/gcc/sanitizer.def @@ -27,7 +27,7 @@ along with GCC; see the file COPYING3. If not see for other FEs by asan.c. */ /* Address Sanitizer */ -DEF_SANITIZER_BUILTIN(BUILT_IN_ASAN_INIT, "__asan_init", +DEF_SANITIZER_BUILTIN(BUILT_IN_ASAN_INIT, "__asan_init_v1", BT_FN_VOID, ATTR_NOTHROW_LEAF_LIST) /* Do not reorder the BUILT_IN_ASAN_REPORT* builtins, e.g. cfgcleanup.c relies on this order. */ diff --git a/gcc/testsuite/ChangeLog b/gcc/testsuite/ChangeLog index ee182b0dda8..b9d72a1cbd9 100644 --- a/gcc/testsuite/ChangeLog +++ b/gcc/testsuite/ChangeLog @@ -1,3 +1,9 @@ +2013-02-13 Kostya Serebryany <kcc@google.com> + + * c-c++-common/asan/strncpy-overflow-1.c: Update the test + to match the fresh asan run-time. + * c-c++-common/asan/rlimit-mmap-test-1.c: Ditto. + 2013-02-12 Dodji Seketeli <dodji@redhat.com> Avoid instrumenting duplicated memory access in the same basic block diff --git a/gcc/testsuite/c-c++-common/asan/rlimit-mmap-test-1.c b/gcc/testsuite/c-c++-common/asan/rlimit-mmap-test-1.c index 212db3babcb..0b3d2ae6fa0 100644 --- a/gcc/testsuite/c-c++-common/asan/rlimit-mmap-test-1.c +++ b/gcc/testsuite/c-c++-common/asan/rlimit-mmap-test-1.c @@ -18,4 +18,4 @@ int main(int argc, char **argv) { return 0; } -/* { dg-output "AddressSanitizer is unable to mmap" } */ +/* { dg-output "ERROR: Failed to mmap" } */ diff --git a/gcc/testsuite/c-c++-common/asan/strncpy-overflow-1.c b/gcc/testsuite/c-c++-common/asan/strncpy-overflow-1.c index c8c288359c7..10b3ad76d04 100644 --- a/gcc/testsuite/c-c++-common/asan/strncpy-overflow-1.c +++ b/gcc/testsuite/c-c++-common/asan/strncpy-overflow-1.c @@ -12,7 +12,7 @@ int main(int argc, char **argv) { return short_buffer[8]; } -/* { dg-output "WRITE of size 1 at 0x\[0-9a-f\]+ thread T0\[^\n\r]*(\n|\r\n|\r)" } */ +/* { dg-output "WRITE of size \[0-9\]* at 0x\[0-9a-f\]+ thread T0\[^\n\r]*(\n|\r\n|\r)" } */ /* { dg-output " #0 0x\[0-9a-f\]+ (in _*(interceptor_|)strncpy|\[(\])\[^\n\r]*(\n|\r\n|\r)" } */ /* { dg-output " #1 0x\[0-9a-f\]+ (in _*main (\[^\n\r]*strncpy-overflow-1.c:11|\[^\n\r]*:0)|\[(\]).*(\n|\r\n|\r)" } */ /* { dg-output "0x\[0-9a-f\]+ is located 0 bytes to the right of 9-byte region\[^\n\r]*(\n|\r\n|\r)" } */ diff --git a/libsanitizer/ChangeLog b/libsanitizer/ChangeLog index 891ba01306f..db1e6e119de 100644 --- a/libsanitizer/ChangeLog +++ b/libsanitizer/ChangeLog @@ -1,3 +1,10 @@ +2013-02-13 Kostya Serebryany <kcc@google.com> + + PR sanitizer/56128 + * All source files: Merge from upstream r175049. + * interception/Makefile.am: added include path. + * interception/Makefile.in: Regenerated. + 2013-02-11 Jack Howarth <howarth@bromo.med.uc.edu> * configure.tgt: Disable build on darwin9 and earlier. diff --git a/libsanitizer/MERGE b/libsanitizer/MERGE index c8e733f1594..02d2bfcf512 100644 --- a/libsanitizer/MERGE +++ b/libsanitizer/MERGE @@ -1,4 +1,4 @@ -173241 +175049 The first line of this file holds the svn revision number of the last merge done from the master library sources. diff --git a/libsanitizer/asan/asan_allocator.cc b/libsanitizer/asan/asan_allocator.cc index f01d2db9dbc..4e97ff57530 100644 --- a/libsanitizer/asan/asan_allocator.cc +++ b/libsanitizer/asan/asan_allocator.cc @@ -32,7 +32,7 @@ #include "asan_report.h" #include "asan_thread.h" #include "asan_thread_registry.h" -#include "sanitizer/asan_interface.h" +#include "sanitizer_common/sanitizer_allocator.h" #include "sanitizer_common/sanitizer_atomic.h" #include "sanitizer_common/sanitizer_mutex.h" @@ -367,7 +367,7 @@ class MallocInfo { left_chunk->chunk_state != CHUNK_AVAILABLE) return left_chunk; // Choose based on offset. - uptr l_offset = 0, r_offset = 0; + sptr l_offset = 0, r_offset = 0; CHECK(AsanChunkView(left_chunk).AddrIsAtRight(addr, 1, &l_offset)); CHECK(AsanChunkView(right_chunk).AddrIsAtLeft(addr, 1, &r_offset)); if (l_offset < r_offset) @@ -387,7 +387,7 @@ class MallocInfo { CHECK(m->chunk_state == CHUNK_ALLOCATED || m->chunk_state == CHUNK_AVAILABLE || m->chunk_state == CHUNK_QUARANTINE); - uptr offset = 0; + sptr offset = 0; AsanChunkView m_view(m); if (m_view.AddrIsInside(addr, 1, &offset)) return m; @@ -685,6 +685,8 @@ void __asan_free_hook(void *ptr) { namespace __asan { +void InitializeAllocator() { } + void PrintInternalAllocatorStats() { } @@ -710,6 +712,7 @@ void *asan_malloc(uptr size, StackTrace *stack) { } void *asan_calloc(uptr nmemb, uptr size, StackTrace *stack) { + if (__sanitizer::CallocShouldReturnNullDueToOverflow(size, nmemb)) return 0; void *ptr = (void*)Allocate(0, nmemb * size, stack, FROM_MALLOC); if (ptr) REAL(memset)(ptr, 0, nmemb * size); diff --git a/libsanitizer/asan/asan_allocator.h b/libsanitizer/asan/asan_allocator.h index cc16ce85497..df2f520c41a 100644 --- a/libsanitizer/asan/asan_allocator.h +++ b/libsanitizer/asan/asan_allocator.h @@ -22,7 +22,7 @@ // will co-exist in the source base for a while. The actual allocator is chosen // at build time by redefining this macro. #ifndef ASAN_ALLOCATOR_VERSION -# if ASAN_LINUX && !ASAN_ANDROID +# if (ASAN_LINUX && !ASAN_ANDROID) || ASAN_MAC || ASAN_WINDOWS # define ASAN_ALLOCATOR_VERSION 2 # else # define ASAN_ALLOCATOR_VERSION 1 @@ -40,6 +40,8 @@ enum AllocType { static const uptr kNumberOfSizeClasses = 255; struct AsanChunk; +void InitializeAllocator(); + class AsanChunkView { public: explicit AsanChunkView(AsanChunk *chunk) : chunk_(chunk) {} @@ -51,14 +53,14 @@ class AsanChunkView { uptr FreeTid(); void GetAllocStack(StackTrace *stack); void GetFreeStack(StackTrace *stack); - bool AddrIsInside(uptr addr, uptr access_size, uptr *offset) { + bool AddrIsInside(uptr addr, uptr access_size, sptr *offset) { if (addr >= Beg() && (addr + access_size) <= End()) { *offset = addr - Beg(); return true; } return false; } - bool AddrIsAtLeft(uptr addr, uptr access_size, uptr *offset) { + bool AddrIsAtLeft(uptr addr, uptr access_size, sptr *offset) { (void)access_size; if (addr < Beg()) { *offset = Beg() - addr; @@ -66,12 +68,9 @@ class AsanChunkView { } return false; } - bool AddrIsAtRight(uptr addr, uptr access_size, uptr *offset) { - if (addr + access_size >= End()) { - if (addr <= End()) - *offset = 0; - else - *offset = addr - End(); + bool AddrIsAtRight(uptr addr, uptr access_size, sptr *offset) { + if (addr + access_size > End()) { + *offset = addr - End(); return true; } return false; @@ -225,50 +224,5 @@ void asan_mz_force_unlock(); void PrintInternalAllocatorStats(); -// Log2 and RoundUpToPowerOfTwo should be inlined for performance. -#if defined(_WIN32) && !defined(__clang__) -extern "C" { -unsigned char _BitScanForward(unsigned long *index, unsigned long mask); // NOLINT -unsigned char _BitScanReverse(unsigned long *index, unsigned long mask); // NOLINT -#if defined(_WIN64) -unsigned char _BitScanForward64(unsigned long *index, unsigned __int64 mask); // NOLINT -unsigned char _BitScanReverse64(unsigned long *index, unsigned __int64 mask); // NOLINT -#endif -} -#endif - -static inline uptr Log2(uptr x) { - CHECK(IsPowerOfTwo(x)); -#if !defined(_WIN32) || defined(__clang__) - 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 -} - -static inline uptr RoundUpToPowerOfTwo(uptr size) { - CHECK(size); - if (IsPowerOfTwo(size)) return size; - - unsigned long up; // NOLINT -#if !defined(_WIN32) || defined(__clang__) - up = SANITIZER_WORDSIZE - 1 - __builtin_clzl(size); -#elif defined(_WIN64) - _BitScanReverse64(&up, size); -#else - _BitScanReverse(&up, size); -#endif - CHECK(size < (1ULL << (up + 1))); - CHECK(size > (1ULL << up)); - return 1UL << (up + 1); -} - - } // namespace __asan #endif // ASAN_ALLOCATOR_H diff --git a/libsanitizer/asan/asan_allocator2.cc b/libsanitizer/asan/asan_allocator2.cc index 4aa5141f2a7..1ff120e555c 100644 --- a/libsanitizer/asan/asan_allocator2.cc +++ b/libsanitizer/asan/asan_allocator2.cc @@ -20,7 +20,6 @@ #include "asan_report.h" #include "asan_thread.h" #include "asan_thread_registry.h" -#include "sanitizer/asan_interface.h" #include "sanitizer_common/sanitizer_allocator.h" #include "sanitizer_common/sanitizer_internal_defs.h" #include "sanitizer_common/sanitizer_list.h" @@ -55,7 +54,11 @@ struct AsanMapUnmapCallback { }; #if SANITIZER_WORDSIZE == 64 +#if defined(__powerpc64__) +const uptr kAllocatorSpace = 0xa0000000000ULL; +#else const uptr kAllocatorSpace = 0x600000000000ULL; +#endif const uptr kAllocatorSize = 0x10000000000ULL; // 1T. typedef DefaultSizeClassMap SizeClassMap; typedef SizeClassAllocator64<kAllocatorSpace, kAllocatorSize, 0 /*metadata*/, @@ -89,8 +92,6 @@ static const uptr kMaxAllowedMallocSize = static const uptr kMaxThreadLocalQuarantine = FIRST_32_SECOND_64(1 << 18, 1 << 20); -static const uptr kReturnOnZeroMalloc = 2048; // Zero page is protected. - // Every chunk of memory allocated by this allocator can be in one of 3 states: // CHUNK_AVAILABLE: the chunk is in the free list and ready to be allocated. // CHUNK_ALLOCATED: the chunk is allocated and not yet freed. @@ -112,7 +113,7 @@ static u32 RZSize2Log(u32 rz_size) { CHECK_GE(rz_size, 16); CHECK_LE(rz_size, 2048); CHECK(IsPowerOfTwo(rz_size)); - u32 res = __builtin_ctz(rz_size) - 4; + u32 res = Log2(rz_size) - 4; CHECK_EQ(rz_size, RZLog2Size(res)); return res; } @@ -289,27 +290,26 @@ struct QuarantineCallback { AllocatorCache *cache_; }; -static void Init() { - static int inited = 0; - if (inited) return; - __asan_init(); - inited = true; // this must happen before any threads are created. +void InitializeAllocator() { allocator.Init(); quarantine.Init((uptr)flags()->quarantine_size, kMaxThreadLocalQuarantine); } static void *Allocate(uptr size, uptr alignment, StackTrace *stack, AllocType alloc_type) { - Init(); + if (!asan_inited) + __asan_init(); CHECK(stack); const uptr min_alignment = SHADOW_GRANULARITY; if (alignment < min_alignment) alignment = min_alignment; if (size == 0) { - if (alignment <= kReturnOnZeroMalloc) - return reinterpret_cast<void *>(kReturnOnZeroMalloc); - else - return 0; // 0 bytes with large alignment requested. Just return 0. + // We'd be happy to avoid allocating memory for zero-size requests, but + // some programs/tests depend on this behavior and assume that malloc would + // not return NULL even for zero-size allocations. Moreover, it looks like + // operator new should never return NULL, and results of consecutive "new" + // calls must be different even if the allocated size is zero. + size = 1; } CHECK(IsPowerOfTwo(alignment)); uptr rz_log = ComputeRZLog(size); @@ -415,7 +415,8 @@ static void *Allocate(uptr size, uptr alignment, StackTrace *stack, static void Deallocate(void *ptr, StackTrace *stack, AllocType alloc_type) { uptr p = reinterpret_cast<uptr>(ptr); - if (p == 0 || p == kReturnOnZeroMalloc) return; + if (p == 0) return; + ASAN_FREE_HOOK(ptr); uptr chunk_beg = p - kChunkHeaderSize; AsanChunk *m = reinterpret_cast<AsanChunk *>(chunk_beg); @@ -465,8 +466,6 @@ static void Deallocate(void *ptr, StackTrace *stack, AllocType alloc_type) { quarantine.Put(&fallback_quarantine_cache, QuarantineCallback(ac), m, m->UsedSize()); } - - ASAN_FREE_HOOK(ptr); } static void *Reallocate(void *old_ptr, uptr new_size, StackTrace *stack) { @@ -546,7 +545,7 @@ AsanChunk *ChooseChunk(uptr addr, return right_chunk; } // Same chunk_state: choose based on offset. - uptr l_offset = 0, r_offset = 0; + sptr l_offset = 0, r_offset = 0; CHECK(AsanChunkView(left_chunk).AddrIsAtRight(addr, 1, &l_offset)); CHECK(AsanChunkView(right_chunk).AddrIsAtLeft(addr, 1, &r_offset)); if (l_offset < r_offset) @@ -557,7 +556,7 @@ AsanChunk *ChooseChunk(uptr addr, AsanChunkView FindHeapChunkByAddress(uptr addr) { AsanChunk *m1 = GetAsanChunkByAddr(addr); if (!m1) return AsanChunkView(m1); - uptr offset = 0; + sptr offset = 0; if (AsanChunkView(m1).AddrIsAtLeft(addr, 1, &offset)) { // The address is in the chunk's left redzone, so maybe it is actually // a right buffer overflow from the other chunk to the left. @@ -601,6 +600,7 @@ void *asan_malloc(uptr size, StackTrace *stack) { } void *asan_calloc(uptr nmemb, uptr size, StackTrace *stack) { + if (CallocShouldReturnNullDueToOverflow(size, nmemb)) return 0; void *ptr = Allocate(nmemb * size, 8, stack, FROM_MALLOC); if (ptr) REAL(memset)(ptr, 0, nmemb * size); @@ -649,16 +649,17 @@ uptr asan_malloc_usable_size(void *ptr, StackTrace *stack) { } uptr asan_mz_size(const void *ptr) { - UNIMPLEMENTED(); - return 0; + return AllocationSize(reinterpret_cast<uptr>(ptr)); } void asan_mz_force_lock() { - UNIMPLEMENTED(); + allocator.ForceLock(); + fallback_mutex.Lock(); } void asan_mz_force_unlock() { - UNIMPLEMENTED(); + fallback_mutex.Unlock(); + allocator.ForceUnlock(); } } // namespace __asan @@ -674,7 +675,7 @@ uptr __asan_get_estimated_allocated_size(uptr size) { bool __asan_get_ownership(const void *p) { uptr ptr = reinterpret_cast<uptr>(p); - return (ptr == kReturnOnZeroMalloc) || (AllocationSize(ptr) > 0); + return (AllocationSize(ptr) > 0); } uptr __asan_get_allocated_size(const void *p) { @@ -682,7 +683,7 @@ uptr __asan_get_allocated_size(const void *p) { uptr ptr = reinterpret_cast<uptr>(p); uptr allocated_size = AllocationSize(ptr); // Die if p is not malloced or if it is already freed. - if (allocated_size == 0 && ptr != kReturnOnZeroMalloc) { + if (allocated_size == 0) { GET_STACK_TRACE_FATAL_HERE; ReportAsanGetAllocatedSizeNotOwned(ptr, &stack); } diff --git a/libsanitizer/asan/asan_fake_stack.cc b/libsanitizer/asan/asan_fake_stack.cc index e8d1e78488b..1fc0415750b 100644 --- a/libsanitizer/asan/asan_fake_stack.cc +++ b/libsanitizer/asan/asan_fake_stack.cc @@ -12,7 +12,6 @@ #include "asan_allocator.h" #include "asan_thread.h" #include "asan_thread_registry.h" -#include "sanitizer/asan_interface.h" namespace __asan { diff --git a/libsanitizer/asan/asan_flags.h b/libsanitizer/asan/asan_flags.h index 49a90730084..b05fdc3acc6 100644 --- a/libsanitizer/asan/asan_flags.h +++ b/libsanitizer/asan/asan_flags.h @@ -13,7 +13,7 @@ #ifndef ASAN_FLAGS_H #define ASAN_FLAGS_H -#include "sanitizer/common_interface_defs.h" +#include "sanitizer_common/sanitizer_internal_defs.h" // ASan flag values can be defined in three ways: // 1) initialized with default values at startup. @@ -50,8 +50,6 @@ struct Flags { bool replace_str; // If set, uses custom wrappers for memset/memcpy/memmove intinsics. bool replace_intrin; - // Used on Mac only. See comments in asan_mac.cc and asan_malloc_mac.cc. - bool replace_cfallocator; // Used on Mac only. bool mac_ignore_invalid_free; // ASan allocator flag. See asan_allocator.cc. @@ -77,6 +75,10 @@ struct Flags { bool unmap_shadow_on_exit; // If set, calls abort() instead of _exit() after printing an error report. bool abort_on_error; + // Print various statistics after printing an error message or if atexit=1. + bool print_stats; + // Print the legend for the shadow bytes. + bool print_legend; // If set, prints ASan exit stats even after program terminates successfully. bool atexit; // By default, disable core dumper on 64-bit - it makes little sense diff --git a/libsanitizer/asan/asan_globals.cc b/libsanitizer/asan/asan_globals.cc index 88aeefa8fe5..7093c445588 100644 --- a/libsanitizer/asan/asan_globals.cc +++ b/libsanitizer/asan/asan_globals.cc @@ -16,7 +16,6 @@ #include "asan_stack.h" #include "asan_stats.h" #include "asan_thread.h" -#include "sanitizer/asan_interface.h" #include "sanitizer_common/sanitizer_mutex.h" namespace __asan { @@ -34,26 +33,20 @@ static ListOfGlobals *list_of_all_globals; static ListOfGlobals *list_of_dynamic_init_globals; void PoisonRedZones(const Global &g) { - uptr shadow_rz_size = kGlobalAndStackRedzone >> SHADOW_SCALE; - CHECK(shadow_rz_size == 1 || shadow_rz_size == 2 || shadow_rz_size == 4); - // full right redzone - uptr g_aligned_size = kGlobalAndStackRedzone * - ((g.size + kGlobalAndStackRedzone - 1) / kGlobalAndStackRedzone); - PoisonShadow(g.beg + g_aligned_size, - kGlobalAndStackRedzone, kAsanGlobalRedzoneMagic); - if ((g.size % kGlobalAndStackRedzone) != 0) { + uptr aligned_size = RoundUpTo(g.size, SHADOW_GRANULARITY); + PoisonShadow(g.beg + aligned_size, g.size_with_redzone - aligned_size, + kAsanGlobalRedzoneMagic); + if (g.size != aligned_size) { // partial right redzone - u64 g_aligned_down_size = kGlobalAndStackRedzone * - (g.size / kGlobalAndStackRedzone); - CHECK(g_aligned_down_size == g_aligned_size - kGlobalAndStackRedzone); - PoisonShadowPartialRightRedzone(g.beg + g_aligned_down_size, - g.size % kGlobalAndStackRedzone, - kGlobalAndStackRedzone, - kAsanGlobalRedzoneMagic); + PoisonShadowPartialRightRedzone( + g.beg + RoundDownTo(g.size, SHADOW_GRANULARITY), + g.size % SHADOW_GRANULARITY, + SHADOW_GRANULARITY, + kAsanGlobalRedzoneMagic); } } -bool DescribeAddressIfGlobal(uptr addr) { +bool DescribeAddressIfGlobal(uptr addr, uptr size) { if (!flags()->report_globals) return false; BlockingMutexLock lock(&mu_for_globals); bool res = false; @@ -62,7 +55,7 @@ bool DescribeAddressIfGlobal(uptr addr) { if (flags()->report_globals >= 2) Report("Search Global: beg=%p size=%zu name=%s\n", (void*)g.beg, g.size, (char*)g.name); - res |= DescribeAddressRelativeToGlobal(addr, g); + res |= DescribeAddressRelativeToGlobal(addr, size, g); } return res; } diff --git a/libsanitizer/asan/asan_intercepted_functions.h b/libsanitizer/asan/asan_intercepted_functions.h index 2d678ab7000..45c913c1894 100644 --- a/libsanitizer/asan/asan_intercepted_functions.h +++ b/libsanitizer/asan/asan_intercepted_functions.h @@ -17,6 +17,7 @@ #include "sanitizer_common/sanitizer_platform_interceptors.h" #include <stdarg.h> +#include <stddef.h> using __sanitizer::uptr; @@ -64,9 +65,7 @@ using __sanitizer::uptr; # define ASAN_INTERCEPT_SIGNAL_AND_SIGACTION 0 #endif -// On Darwin siglongjmp tailcalls longjmp, so we don't want to intercept it -// there. -#if !defined(_WIN32) && (!defined(__APPLE__) || MAC_INTERPOSE_FUNCTIONS) +#if !defined(_WIN32) # define ASAN_INTERCEPT_SIGLONGJMP 1 #else # define ASAN_INTERCEPT_SIGLONGJMP 0 @@ -169,7 +168,8 @@ DECLARE_FUNCTION_AND_WRAPPER(SSIZE_T, pread64, int fd, void *buf, DECLARE_FUNCTION_AND_WRAPPER(SSIZE_T, write, int fd, void *ptr, SIZE_T count); # endif # if SANITIZER_INTERCEPT_PWRITE -DECLARE_FUNCTION_AND_WRAPPER(SSIZE_T, pwrite, int fd, void *ptr, SIZE_T count); +DECLARE_FUNCTION_AND_WRAPPER(SSIZE_T, pwrite, + int fd, void *ptr, SIZE_T count, OFF_T offset); # endif # if ASAN_INTERCEPT_MLOCKX @@ -193,6 +193,8 @@ DECLARE_FUNCTION_AND_WRAPPER(int, pthread_create, void *(*start_routine)(void*), void *arg); # endif +// stdio.h +# if SANITIZER_INTERCEPT_SCANF DECLARE_FUNCTION_AND_WRAPPER(int, vscanf, const char *format, va_list ap); DECLARE_FUNCTION_AND_WRAPPER(int, vsscanf, const char *str, const char *format, va_list ap); @@ -203,6 +205,18 @@ DECLARE_FUNCTION_AND_WRAPPER(int, fscanf, void* stream, const char *format, ...); DECLARE_FUNCTION_AND_WRAPPER(int, sscanf, // NOLINT const char *str, const char *format, ...); +DECLARE_FUNCTION_AND_WRAPPER(int, __isoc99_vscanf, const char *format, + va_list ap); +DECLARE_FUNCTION_AND_WRAPPER(int, __isoc99_vsscanf, const char *str, + const char *format, va_list ap); +DECLARE_FUNCTION_AND_WRAPPER(int, __isoc99_vfscanf, void *stream, + const char *format, va_list ap); +DECLARE_FUNCTION_AND_WRAPPER(int, __isoc99_scanf, const char *format, ...); +DECLARE_FUNCTION_AND_WRAPPER(int, __isoc99_fscanf, + void* stream, const char *format, ...); +DECLARE_FUNCTION_AND_WRAPPER(int, __isoc99_sscanf, // NOLINT + const char *str, const char *format, ...); +# endif # if defined(__APPLE__) typedef void* pthread_workqueue_t; @@ -231,7 +245,7 @@ DECLARE_FUNCTION_AND_WRAPPER(void, dispatch_group_async_f, dispatch_group_t group, dispatch_queue_t dq, void *ctxt, dispatch_function_t func); -# if MAC_INTERPOSE_FUNCTIONS && !defined(MISSING_BLOCKS_SUPPORT) +# if !defined(MISSING_BLOCKS_SUPPORT) DECLARE_FUNCTION_AND_WRAPPER(void, dispatch_group_async, dispatch_group_t dg, dispatch_queue_t dq, void (^work)(void)); @@ -243,7 +257,7 @@ DECLARE_FUNCTION_AND_WRAPPER(void, dispatch_source_set_event_handler, dispatch_source_t ds, void (^work)(void)); DECLARE_FUNCTION_AND_WRAPPER(void, dispatch_source_set_cancel_handler, dispatch_source_t ds, void (^work)(void)); -# endif // MAC_INTERPOSE_FUNCTIONS +# endif // MISSING_BLOCKS_SUPPORT typedef void malloc_zone_t; typedef size_t vm_size_t; diff --git a/libsanitizer/asan/asan_interceptors.cc b/libsanitizer/asan/asan_interceptors.cc index 98329f38e61..f4c56830d8a 100644 --- a/libsanitizer/asan/asan_interceptors.cc +++ b/libsanitizer/asan/asan_interceptors.cc @@ -20,7 +20,6 @@ #include "asan_stats.h" #include "asan_thread_registry.h" #include "interception/interception.h" -#include "sanitizer/asan_interface.h" #include "sanitizer_common/sanitizer_libc.h" namespace __asan { @@ -30,12 +29,14 @@ namespace __asan { // that no extra frames are created, and stack trace contains // relevant information only. // We check all shadow bytes. -#define ACCESS_MEMORY_RANGE(offset, size, isWrite) do { \ - if (uptr __ptr = __asan_region_is_poisoned((uptr)(offset), size)) { \ - GET_CURRENT_PC_BP_SP; \ - __asan_report_error(pc, bp, sp, __ptr, isWrite, /* access_size */1); \ - } \ -} while (0) +#define ACCESS_MEMORY_RANGE(offset, size, isWrite) do { \ + uptr __offset = (uptr)(offset); \ + uptr __size = (uptr)(size); \ + if (__asan_region_is_poisoned(__offset, __size)) { \ + GET_CURRENT_PC_BP_SP; \ + __asan_report_error(pc, bp, sp, __offset, isWrite, __size); \ + } \ + } while (0) #define ASAN_READ_RANGE(offset, size) ACCESS_MEMORY_RANGE(offset, size, false) #define ASAN_WRITE_RANGE(offset, size) ACCESS_MEMORY_RANGE(offset, size, true); @@ -275,13 +276,9 @@ INTERCEPTOR(void*, memcpy, void *to, const void *from, uptr size) { ASAN_READ_RANGE(from, size); ASAN_WRITE_RANGE(to, size); } -#if MAC_INTERPOSE_FUNCTIONS // Interposing of resolver functions is broken on Mac OS 10.7 and 10.8. // See also http://code.google.com/p/address-sanitizer/issues/detail?id=116. return internal_memcpy(to, from, size); -#else - return REAL(memcpy)(to, from, size); -#endif } INTERCEPTOR(void*, memmove, void *to, const void *from, uptr size) { @@ -294,13 +291,9 @@ INTERCEPTOR(void*, memmove, void *to, const void *from, uptr size) { ASAN_READ_RANGE(from, size); ASAN_WRITE_RANGE(to, size); } -#if MAC_INTERPOSE_FUNCTIONS // Interposing of resolver functions is broken on Mac OS 10.7 and 10.8. // See also http://code.google.com/p/address-sanitizer/issues/detail?id=116. return internal_memmove(to, from, size); -#else - return REAL(memmove)(to, from, size); -#endif } INTERCEPTOR(void*, memset, void *block, int c, uptr size) { @@ -398,7 +391,7 @@ INTERCEPTOR(int, strcmp, const char *s1, const char *s2) { } INTERCEPTOR(char*, strcpy, char *to, const char *from) { // NOLINT -#if MAC_INTERPOSE_FUNCTIONS +#if defined(__APPLE__) if (!asan_inited) return REAL(strcpy)(to, from); // NOLINT #endif // strcpy is called from malloc_default_purgeable_zone() @@ -418,7 +411,7 @@ INTERCEPTOR(char*, strcpy, char *to, const char *from) { // NOLINT #if ASAN_INTERCEPT_STRDUP INTERCEPTOR(char*, strdup, const char *s) { -#if MAC_INTERPOSE_FUNCTIONS +#if defined(__APPLE__) // FIXME: because internal_strdup() uses InternalAlloc(), which currently // just calls malloc() on Mac, we can't use internal_strdup() with the // dynamic runtime. We can remove the call to REAL(strdup) once InternalAlloc @@ -559,7 +552,7 @@ INTERCEPTOR(long, strtol, const char *nptr, // NOLINT } INTERCEPTOR(int, atoi, const char *nptr) { -#if MAC_INTERPOSE_FUNCTIONS +#if defined(__APPLE__) if (!asan_inited) return REAL(atoi)(nptr); #endif ENSURE_ASAN_INITED(); @@ -578,7 +571,7 @@ INTERCEPTOR(int, atoi, const char *nptr) { } INTERCEPTOR(long, atol, const char *nptr) { // NOLINT -#if MAC_INTERPOSE_FUNCTIONS +#if defined(__APPLE__) if (!asan_inited) return REAL(atol)(nptr); #endif ENSURE_ASAN_INITED(); @@ -659,10 +652,9 @@ void InitializeAsanInterceptors() { static bool was_called_once; CHECK(was_called_once == false); was_called_once = true; -#if MAC_INTERPOSE_FUNCTIONS +#if defined(__APPLE__) return; -#endif - +#else SANITIZER_COMMON_INTERCEPTORS_INIT; // Intercept mem* functions. @@ -671,12 +663,6 @@ void InitializeAsanInterceptors() { ASAN_INTERCEPT_FUNC(memset); if (PLATFORM_HAS_DIFFERENT_MEMCPY_AND_MEMMOVE) { ASAN_INTERCEPT_FUNC(memcpy); - } else { -#if !MAC_INTERPOSE_FUNCTIONS - // If we're using dynamic interceptors on Mac, these two are just plain - // functions. - internal_memcpy(&REAL(memcpy), &REAL(memmove), sizeof(REAL(memmove))); -#endif } // Intercept str* functions. @@ -698,12 +684,8 @@ void InitializeAsanInterceptors() { #if ASAN_INTERCEPT_STRNLEN ASAN_INTERCEPT_FUNC(strnlen); #endif -#if ASAN_INTERCEPT_INDEX -# if ASAN_USE_ALIAS_ATTRIBUTE_FOR_INDEX +#if ASAN_INTERCEPT_INDEX && ASAN_USE_ALIAS_ATTRIBUTE_FOR_INDEX ASAN_INTERCEPT_FUNC(index); -# else - CHECK(OVERRIDE_FUNCTION(index, WRAP(strchr))); -# endif #endif ASAN_INTERCEPT_FUNC(atoi); @@ -753,14 +735,10 @@ void InitializeAsanInterceptors() { InitializeWindowsInterceptors(); #endif - // Some Mac-specific interceptors. -#if defined(__APPLE__) - InitializeMacInterceptors(); -#endif - if (flags()->verbosity > 0) { Report("AddressSanitizer: libc interceptors initialized\n"); } +#endif // __APPLE__ } } // namespace __asan diff --git a/libsanitizer/asan/asan_interceptors.h b/libsanitizer/asan/asan_interceptors.h index a6ab0309bff..cae4c7f0125 100644 --- a/libsanitizer/asan/asan_interceptors.h +++ b/libsanitizer/asan/asan_interceptors.h @@ -30,9 +30,6 @@ DECLARE_REAL(int, sigaction, int signum, const struct sigaction *act, namespace __asan { void InitializeAsanInterceptors(); -#if defined(__APPLE__) -void InitializeMacInterceptors(); -#endif // __APPLE__ } // namespace __asan diff --git a/libsanitizer/asan/asan_interface_internal.h b/libsanitizer/asan/asan_interface_internal.h new file mode 100644 index 00000000000..2fd58b856bc --- /dev/null +++ b/libsanitizer/asan/asan_interface_internal.h @@ -0,0 +1,133 @@ +//===-- asan_interface_internal.h -------------------------------*- C++ -*-===// +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file is a part of AddressSanitizer, an address sanity checker. +// +// This header can be included by the instrumented program to fetch +// data (mostly allocator statistics) from ASan runtime library. +//===----------------------------------------------------------------------===// +#ifndef ASAN_INTERFACE_INTERNAL_H +#define ASAN_INTERFACE_INTERNAL_H + +#include "sanitizer_common/sanitizer_internal_defs.h" + +using __sanitizer::uptr; + +extern "C" { + // This function should be called at the very beginning of the process, + // before any instrumented code is executed and before any call to malloc. + // Everytime the asan ABI changes we also change the version number in this + // name. Objects build with incompatible asan ABI version + // will not link with run-time. + void __asan_init_v1() SANITIZER_INTERFACE_ATTRIBUTE; + #define __asan_init __asan_init_v1 + + // This structure describes an instrumented global variable. + struct __asan_global { + uptr beg; // The address of the global. + uptr size; // The original size of the global. + uptr size_with_redzone; // The size with the redzone. + const char *name; // Name as a C string. + uptr has_dynamic_init; // Non-zero if the global has dynamic initializer. + }; + + // These two functions should be called by the instrumented code. + // 'globals' is an array of structures describing 'n' globals. + void __asan_register_globals(__asan_global *globals, uptr n) + SANITIZER_INTERFACE_ATTRIBUTE; + void __asan_unregister_globals(__asan_global *globals, uptr n) + SANITIZER_INTERFACE_ATTRIBUTE; + + // These two functions should be called before and after dynamic initializers + // run, respectively. They should be called with parameters describing all + // dynamically initialized globals defined in the calling TU. + void __asan_before_dynamic_init(uptr first_addr, uptr last_addr) + SANITIZER_INTERFACE_ATTRIBUTE; + void __asan_after_dynamic_init() + SANITIZER_INTERFACE_ATTRIBUTE; + + // These two functions are used by the instrumented code in the + // use-after-return mode. __asan_stack_malloc allocates size bytes of + // fake stack and __asan_stack_free poisons it. real_stack is a pointer to + // the real stack region. + uptr __asan_stack_malloc(uptr size, uptr real_stack) + SANITIZER_INTERFACE_ATTRIBUTE; + void __asan_stack_free(uptr ptr, uptr size, uptr real_stack) + SANITIZER_INTERFACE_ATTRIBUTE; + + // These two functions are used by instrumented code in the + // use-after-scope mode. They mark memory for local variables as + // unaddressable when they leave scope and addressable before the + // function exits. + void __asan_poison_stack_memory(uptr addr, uptr size) + SANITIZER_INTERFACE_ATTRIBUTE; + void __asan_unpoison_stack_memory(uptr addr, uptr size) + SANITIZER_INTERFACE_ATTRIBUTE; + + // Performs cleanup before a NoReturn function. Must be called before things + // like _exit and execl to avoid false positives on stack. + void __asan_handle_no_return() SANITIZER_INTERFACE_ATTRIBUTE; + + void __asan_poison_memory_region(void const volatile *addr, uptr size) + SANITIZER_INTERFACE_ATTRIBUTE; + void __asan_unpoison_memory_region(void const volatile *addr, uptr size) + SANITIZER_INTERFACE_ATTRIBUTE; + + bool __asan_address_is_poisoned(void const volatile *addr) + SANITIZER_INTERFACE_ATTRIBUTE; + + uptr __asan_region_is_poisoned(uptr beg, uptr size) + SANITIZER_INTERFACE_ATTRIBUTE; + + void __asan_describe_address(uptr addr) + SANITIZER_INTERFACE_ATTRIBUTE; + + void __asan_report_error(uptr pc, uptr bp, uptr sp, + uptr addr, bool is_write, uptr access_size) + SANITIZER_INTERFACE_ATTRIBUTE; + + int __asan_set_error_exit_code(int exit_code) + SANITIZER_INTERFACE_ATTRIBUTE; + void __asan_set_death_callback(void (*callback)(void)) + SANITIZER_INTERFACE_ATTRIBUTE; + void __asan_set_error_report_callback(void (*callback)(const char*)) + SANITIZER_INTERFACE_ATTRIBUTE; + + /* OPTIONAL */ void __asan_on_error() + SANITIZER_WEAK_ATTRIBUTE SANITIZER_INTERFACE_ATTRIBUTE; + + /* OPTIONAL */ bool __asan_symbolize(const void *pc, char *out_buffer, + int out_size) + SANITIZER_WEAK_ATTRIBUTE SANITIZER_INTERFACE_ATTRIBUTE; + + uptr __asan_get_estimated_allocated_size(uptr size) + SANITIZER_INTERFACE_ATTRIBUTE; + bool __asan_get_ownership(const void *p) + SANITIZER_INTERFACE_ATTRIBUTE; + uptr __asan_get_allocated_size(const void *p) + SANITIZER_INTERFACE_ATTRIBUTE; + uptr __asan_get_current_allocated_bytes() + SANITIZER_INTERFACE_ATTRIBUTE; + uptr __asan_get_heap_size() + SANITIZER_INTERFACE_ATTRIBUTE; + uptr __asan_get_free_bytes() + SANITIZER_INTERFACE_ATTRIBUTE; + uptr __asan_get_unmapped_bytes() + SANITIZER_INTERFACE_ATTRIBUTE; + void __asan_print_accumulated_stats() + SANITIZER_INTERFACE_ATTRIBUTE; + + /* OPTIONAL */ const char* __asan_default_options() + SANITIZER_WEAK_ATTRIBUTE SANITIZER_INTERFACE_ATTRIBUTE; + + /* OPTIONAL */ void __asan_malloc_hook(void *ptr, uptr size) + SANITIZER_WEAK_ATTRIBUTE SANITIZER_INTERFACE_ATTRIBUTE; + /* OPTIONAL */ void __asan_free_hook(void *ptr) + SANITIZER_WEAK_ATTRIBUTE SANITIZER_INTERFACE_ATTRIBUTE; +} // extern "C" + +#endif // ASAN_INTERFACE_INTERNAL_H diff --git a/libsanitizer/asan/asan_internal.h b/libsanitizer/asan/asan_internal.h index a9c6c0f9022..0fe620e2e4c 100644 --- a/libsanitizer/asan/asan_internal.h +++ b/libsanitizer/asan/asan_internal.h @@ -13,6 +13,7 @@ #define ASAN_INTERNAL_H #include "asan_flags.h" +#include "asan_interface_internal.h" #include "sanitizer_common/sanitizer_common.h" #include "sanitizer_common/sanitizer_internal_defs.h" #include "sanitizer_common/sanitizer_stacktrace.h" diff --git a/libsanitizer/asan/asan_mac.cc b/libsanitizer/asan/asan_mac.cc index a49d5c5ab8f..c5fc7de10c0 100644 --- a/libsanitizer/asan/asan_mac.cc +++ b/libsanitizer/asan/asan_mac.cc @@ -89,10 +89,9 @@ static const char kDyldInsertLibraries[] = "DYLD_INSERT_LIBRARIES"; void MaybeReexec() { if (!flags()->allow_reexec) return; -#if MAC_INTERPOSE_FUNCTIONS - // If the program is linked with the dynamic ASan runtime library, make sure - // the library is preloaded so that the wrappers work. If it is not, set - // DYLD_INSERT_LIBRARIES and re-exec ourselves. + // Make sure the dynamic ASan runtime library is preloaded so that the + // wrappers work. If it is not, set DYLD_INSERT_LIBRARIES and re-exec + // ourselves. Dl_info info; CHECK(dladdr((void*)((uptr)__asan_init), &info)); const char *dyld_insert_libraries = GetEnv(kDyldInsertLibraries); @@ -114,8 +113,6 @@ void MaybeReexec() { } execv(program_name, *_NSGetArgv()); } -#endif // MAC_INTERPOSE_FUNCTIONS - // If we're not using the dynamic runtime, do nothing. } // No-op. Mac does not support static linkage anyway. @@ -146,57 +143,6 @@ void ReadContextStack(void *context, uptr *stack, uptr *ssize) { UNIMPLEMENTED(); } -// The range of pages to be used for escape islands. -// TODO(glider): instead of mapping a fixed range we must find a range of -// unmapped pages in vmmap and take them. -// These constants were chosen empirically and may not work if the shadow -// memory layout changes. Unfortunately they do necessarily depend on -// kHighMemBeg or kHighMemEnd. -static void *island_allocator_pos = 0; - -#if SANITIZER_WORDSIZE == 32 -# define kIslandEnd (0xffdf0000 - GetPageSizeCached()) -# define kIslandBeg (kIslandEnd - 256 * GetPageSizeCached()) -#else -# define kIslandEnd (0x7fffffdf0000 - GetPageSizeCached()) -# define kIslandBeg (kIslandEnd - 256 * GetPageSizeCached()) -#endif - -extern "C" -mach_error_t __interception_allocate_island(void **ptr, - uptr unused_size, - void *unused_hint) { - if (!island_allocator_pos) { - island_allocator_pos = - internal_mmap((void*)kIslandBeg, kIslandEnd - kIslandBeg, - PROT_READ | PROT_WRITE | PROT_EXEC, - MAP_PRIVATE | MAP_ANON | MAP_FIXED, - -1, 0); - if (island_allocator_pos != (void*)kIslandBeg) { - return KERN_NO_SPACE; - } - if (flags()->verbosity) { - Report("Mapped pages %p--%p for branch islands.\n", - (void*)kIslandBeg, (void*)kIslandEnd); - } - // Should not be very performance-critical. - internal_memset(island_allocator_pos, 0xCC, kIslandEnd - kIslandBeg); - }; - *ptr = island_allocator_pos; - island_allocator_pos = (char*)island_allocator_pos + GetPageSizeCached(); - if (flags()->verbosity) { - Report("Branch island allocated at %p\n", *ptr); - } - return err_none; -} - -extern "C" -mach_error_t __interception_deallocate_island(void *ptr) { - // Do nothing. - // TODO(glider): allow to free and reuse the island memory. - return err_none; -} - // Support for the following functions from libdispatch on Mac OS: // dispatch_async_f() // dispatch_async() @@ -350,14 +296,7 @@ INTERCEPTOR(void, dispatch_group_async_f, dispatch_group_t group, asan_dispatch_call_block_and_release); } -#if MAC_INTERPOSE_FUNCTIONS && !defined(MISSING_BLOCKS_SUPPORT) -// dispatch_async, dispatch_group_async and others tailcall the corresponding -// dispatch_*_f functions. When wrapping functions with mach_override, those -// dispatch_*_f are intercepted automatically. But with dylib interposition -// this does not work, because the calls within the same library are not -// interposed. -// Therefore we need to re-implement dispatch_async and friends. - +#if !defined(MISSING_BLOCKS_SUPPORT) extern "C" { // FIXME: consolidate these declarations with asan_intercepted_functions.h. void dispatch_async(dispatch_queue_t dq, void(^work)(void)); @@ -410,16 +349,4 @@ INTERCEPTOR(void, dispatch_source_set_event_handler, } #endif -namespace __asan { - -void InitializeMacInterceptors() { - CHECK(INTERCEPT_FUNCTION(dispatch_async_f)); - CHECK(INTERCEPT_FUNCTION(dispatch_sync_f)); - CHECK(INTERCEPT_FUNCTION(dispatch_after_f)); - CHECK(INTERCEPT_FUNCTION(dispatch_barrier_async_f)); - CHECK(INTERCEPT_FUNCTION(dispatch_group_async_f)); -} - -} // namespace __asan - #endif // __APPLE__ diff --git a/libsanitizer/asan/asan_malloc_linux.cc b/libsanitizer/asan/asan_malloc_linux.cc index e33d0c0d4d3..18e6a3be865 100644 --- a/libsanitizer/asan/asan_malloc_linux.cc +++ b/libsanitizer/asan/asan_malloc_linux.cc @@ -18,7 +18,6 @@ #include "asan_internal.h" #include "asan_stack.h" #include "asan_thread_registry.h" -#include "sanitizer/asan_interface.h" #if ASAN_ANDROID DECLARE_REAL_AND_INTERCEPTOR(void*, malloc, uptr size) diff --git a/libsanitizer/asan/asan_mapping.h b/libsanitizer/asan/asan_mapping.h index be645c4afcd..3e7fc6bd43b 100644 --- a/libsanitizer/asan/asan_mapping.h +++ b/libsanitizer/asan/asan_mapping.h @@ -1,5 +1,7 @@ //===-- asan_mapping.h ------------------------------------------*- C++ -*-===// // +// The LLVM Compiler Infrastructure +// // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // @@ -34,27 +36,20 @@ extern SANITIZER_INTERFACE_ATTRIBUTE uptr __asan_mapping_offset; # if defined(__powerpc64__) # define SHADOW_OFFSET (1ULL << 41) # else -# define SHADOW_OFFSET (1ULL << 44) +# if ASAN_MAC +# define SHADOW_OFFSET (1ULL << 44) +# else +# define SHADOW_OFFSET 0x7fff8000ULL +# endif # endif # endif # endif #endif // ASAN_FLEXIBLE_MAPPING_AND_OFFSET #define SHADOW_GRANULARITY (1ULL << SHADOW_SCALE) -#define MEM_TO_SHADOW(mem) (((mem) >> SHADOW_SCALE) | (SHADOW_OFFSET)) +#define MEM_TO_SHADOW(mem) (((mem) >> SHADOW_SCALE) + (SHADOW_OFFSET)) #define SHADOW_TO_MEM(shadow) (((shadow) - SHADOW_OFFSET) << SHADOW_SCALE) -#if SANITIZER_WORDSIZE == 64 -# if defined(__powerpc64__) - static const uptr kHighMemEnd = 0x00000fffffffffffUL; -# else - static const uptr kHighMemEnd = 0x00007fffffffffffUL; -# endif -#else // SANITIZER_WORDSIZE == 32 - static const uptr kHighMemEnd = 0xffffffff; -#endif // SANITIZER_WORDSIZE - - #define kLowMemBeg 0 #define kLowMemEnd (SHADOW_OFFSET ? SHADOW_OFFSET - 1 : 0) @@ -74,11 +69,11 @@ extern SANITIZER_INTERFACE_ATTRIBUTE uptr __asan_mapping_offset; : kZeroBaseShadowStart) #define kShadowGapEnd (kHighShadowBeg - 1) -#define kGlobalAndStackRedzone \ - (SHADOW_GRANULARITY < 32 ? 32 : SHADOW_GRANULARITY) - namespace __asan { +SANITIZER_INTERFACE_ATTRIBUTE +extern uptr kHighMemEnd; // Initialized in __asan_init. + static inline bool AddrIsInLowMem(uptr a) { return a < kLowMemEnd; } diff --git a/libsanitizer/asan/asan_new_delete.cc b/libsanitizer/asan/asan_new_delete.cc index e41135d0e51..fd47eee4205 100644 --- a/libsanitizer/asan/asan_new_delete.cc +++ b/libsanitizer/asan/asan_new_delete.cc @@ -25,9 +25,9 @@ void ReplaceOperatorsNewAndDelete() { } using namespace __asan; // NOLINT -// On Mac and Android new() goes through malloc interceptors. +// On Android new() goes through malloc interceptors. // See also https://code.google.com/p/address-sanitizer/issues/detail?id=131. -#if !ASAN_ANDROID && !ASAN_MAC +#if !ASAN_ANDROID // Fake std::nothrow_t to avoid including <new>. namespace std { diff --git a/libsanitizer/asan/asan_poisoning.cc b/libsanitizer/asan/asan_poisoning.cc index 11edca57ae0..7e930034bef 100644 --- a/libsanitizer/asan/asan_poisoning.cc +++ b/libsanitizer/asan/asan_poisoning.cc @@ -13,7 +13,6 @@ #include "asan_interceptors.h" #include "asan_internal.h" #include "asan_mapping.h" -#include "sanitizer/asan_interface.h" #include "sanitizer_common/sanitizer_libc.h" namespace __asan { diff --git a/libsanitizer/asan/asan_report.cc b/libsanitizer/asan/asan_report.cc index ed18ab25098..663e8f3b748 100644 --- a/libsanitizer/asan/asan_report.cc +++ b/libsanitizer/asan/asan_report.cc @@ -118,19 +118,7 @@ static void PrintShadowBytes(const char *before, u8 *bytes, Printf("\n"); } -static void PrintShadowMemoryForAddress(uptr addr) { - if (!AddrIsInMem(addr)) - return; - uptr shadow_addr = MemToShadow(addr); - const uptr n_bytes_per_row = 16; - uptr aligned_shadow = shadow_addr & ~(n_bytes_per_row - 1); - Printf("Shadow bytes around the buggy address:\n"); - for (int i = -5; i <= 5; i++) { - const char *prefix = (i == 0) ? "=>" : " "; - PrintShadowBytes(prefix, - (u8*)(aligned_shadow + i * n_bytes_per_row), - (u8*)shadow_addr, n_bytes_per_row); - } +static void PrintLegend() { Printf("Shadow byte legend (one shadow byte represents %d " "application bytes):\n", (int)SHADOW_GRANULARITY); PrintShadowByte(" Addressable: ", 0); @@ -153,6 +141,23 @@ static void PrintShadowMemoryForAddress(uptr addr) { PrintShadowByte(" ASan internal: ", kAsanInternalHeapMagic); } +static void PrintShadowMemoryForAddress(uptr addr) { + if (!AddrIsInMem(addr)) + return; + uptr shadow_addr = MemToShadow(addr); + const uptr n_bytes_per_row = 16; + uptr aligned_shadow = shadow_addr & ~(n_bytes_per_row - 1); + Printf("Shadow bytes around the buggy address:\n"); + for (int i = -5; i <= 5; i++) { + const char *prefix = (i == 0) ? "=>" : " "; + PrintShadowBytes(prefix, + (u8*)(aligned_shadow + i * n_bytes_per_row), + (u8*)shadow_addr, n_bytes_per_row); + } + if (flags()->print_legend) + PrintLegend(); +} + static void PrintZoneForPointer(uptr ptr, uptr zone_ptr, const char *zone_name) { if (zone_ptr) { @@ -183,18 +188,23 @@ static void PrintGlobalNameIfASCII(const __asan_global &g) { Printf(" '%s' is ascii string '%s'\n", g.name, (char*)g.beg); } -bool DescribeAddressRelativeToGlobal(uptr addr, const __asan_global &g) { - if (addr < g.beg - kGlobalAndStackRedzone) return false; +bool DescribeAddressRelativeToGlobal(uptr addr, uptr size, + const __asan_global &g) { + static const uptr kMinimalDistanceFromAnotherGlobal = 64; + if (addr <= g.beg - kMinimalDistanceFromAnotherGlobal) return false; if (addr >= g.beg + g.size_with_redzone) return false; Decorator d; Printf("%s", d.Location()); - Printf("%p is located ", (void*)addr); if (addr < g.beg) { - Printf("%zd bytes to the left", g.beg - addr); - } else if (addr >= g.beg + g.size) { - Printf("%zd bytes to the right", addr - (g.beg + g.size)); + Printf("%p is located %zd bytes to the left", (void*)addr, g.beg - addr); + } else if (addr + size > g.beg + g.size) { + if (addr < g.beg + g.size) + addr = g.beg + g.size; + Printf("%p is located %zd bytes to the right", (void*)addr, + addr - (g.beg + g.size)); } else { - Printf("%zd bytes inside", addr - g.beg); // Can it happen? + // Can it happen? + Printf("%p is located %zd bytes inside", (void*)addr, addr - g.beg); } Printf(" of global variable '%s' (0x%zx) of size %zu\n", g.name, g.beg, g.size); @@ -280,18 +290,22 @@ bool DescribeAddressIfStack(uptr addr, uptr access_size) { static void DescribeAccessToHeapChunk(AsanChunkView chunk, uptr addr, uptr access_size) { - uptr offset; + sptr offset; Decorator d; Printf("%s", d.Location()); - Printf("%p is located ", (void*)addr); - if (chunk.AddrIsInside(addr, access_size, &offset)) { - Printf("%zu bytes inside of", offset); - } else if (chunk.AddrIsAtLeft(addr, access_size, &offset)) { - Printf("%zu bytes to the left of", offset); + if (chunk.AddrIsAtLeft(addr, access_size, &offset)) { + Printf("%p is located %zd bytes to the left of", (void*)addr, offset); } else if (chunk.AddrIsAtRight(addr, access_size, &offset)) { - Printf("%zu bytes to the right of", offset); + if (offset < 0) { + addr -= offset; + offset = 0; + } + Printf("%p is located %zd bytes to the right of", (void*)addr, offset); + } else if (chunk.AddrIsInside(addr, access_size, &offset)) { + Printf("%p is located %zd bytes inside of", (void*)addr, offset); } else { - Printf(" somewhere around (this is AddressSanitizer bug!)"); + Printf("%p is located somewhere around (this is AddressSanitizer bug!)", + (void*)addr); } Printf(" %zu-byte region [%p,%p)\n", chunk.UsedSize(), (void*)(chunk.Beg()), (void*)(chunk.End())); @@ -364,7 +378,7 @@ void DescribeAddress(uptr addr, uptr access_size) { if (DescribeAddressIfShadow(addr)) return; CHECK(AddrIsInMem(addr)); - if (DescribeAddressIfGlobal(addr)) + if (DescribeAddressIfGlobal(addr, access_size)) return; if (DescribeAddressIfStack(addr, access_size)) return; @@ -443,7 +457,8 @@ class ScopedInErrorReport { DescribeThread(curr_thread->summary()); } // Print memory stats. - __asan_print_accumulated_stats(); + if (flags()->print_stats) + __asan_print_accumulated_stats(); if (error_report_callback) { error_report_callback(error_message_buffer); } @@ -452,6 +467,20 @@ class ScopedInErrorReport { } }; +static void ReportSummary(const char *error_type, StackTrace *stack) { + if (!stack->size) return; + if (IsSymbolizerAvailable()) { + AddressInfo ai; + // Currently, we include the first stack frame into the report summary. + // Maybe sometimes we need to choose another frame (e.g. skip memcpy/etc). + SymbolizeCode(stack->trace[0], &ai, 1); + ReportErrorSummary(error_type, + StripPathPrefix(ai.file, flags()->strip_path_prefix), + ai.line, ai.function); + } + // FIXME: do we need to print anything at all if there is no symbolizer? +} + void ReportSIGSEGV(uptr pc, uptr sp, uptr bp, uptr addr) { ScopedInErrorReport in_report; Decorator d; @@ -464,6 +493,7 @@ void ReportSIGSEGV(uptr pc, uptr sp, uptr bp, uptr addr) { Printf("AddressSanitizer can not provide additional info.\n"); GET_STACK_TRACE_FATAL(pc, bp); PrintStack(&stack); + ReportSummary("SEGV", &stack); } void ReportDoubleFree(uptr addr, StackTrace *stack) { @@ -474,6 +504,7 @@ void ReportDoubleFree(uptr addr, StackTrace *stack) { Printf("%s", d.EndWarning()); PrintStack(stack); DescribeHeapAddress(addr, 1); + ReportSummary("double-free", stack); } void ReportFreeNotMalloced(uptr addr, StackTrace *stack) { @@ -485,6 +516,7 @@ void ReportFreeNotMalloced(uptr addr, StackTrace *stack) { Printf("%s", d.EndWarning()); PrintStack(stack); DescribeHeapAddress(addr, 1); + ReportSummary("bad-free", stack); } void ReportAllocTypeMismatch(uptr addr, StackTrace *stack, @@ -503,6 +535,7 @@ void ReportAllocTypeMismatch(uptr addr, StackTrace *stack, Printf("%s", d.EndWarning()); PrintStack(stack); DescribeHeapAddress(addr, 1); + ReportSummary("alloc-dealloc-mismatch", stack); Report("HINT: if you don't care about these warnings you may set " "ASAN_OPTIONS=alloc_dealloc_mismatch=0\n"); } @@ -517,6 +550,7 @@ void ReportMallocUsableSizeNotOwned(uptr addr, StackTrace *stack) { Printf("%s", d.EndWarning()); PrintStack(stack); DescribeHeapAddress(addr, 1); + ReportSummary("bad-malloc_usable_size", stack); } void ReportAsanGetAllocatedSizeNotOwned(uptr addr, StackTrace *stack) { @@ -529,6 +563,7 @@ void ReportAsanGetAllocatedSizeNotOwned(uptr addr, StackTrace *stack) { Printf("%s", d.EndWarning()); PrintStack(stack); DescribeHeapAddress(addr, 1); + ReportSummary("bad-__asan_get_allocated_size", stack); } void ReportStringFunctionMemoryRangesOverlap( @@ -536,14 +571,17 @@ void ReportStringFunctionMemoryRangesOverlap( const char *offset2, uptr length2, StackTrace *stack) { ScopedInErrorReport in_report; Decorator d; + char bug_type[100]; + internal_snprintf(bug_type, sizeof(bug_type), "%s-param-overlap", function); Printf("%s", d.Warning()); - Report("ERROR: AddressSanitizer: %s-param-overlap: " + Report("ERROR: AddressSanitizer: %s: " "memory ranges [%p,%p) and [%p, %p) overlap\n", \ - function, offset1, offset1 + length1, offset2, offset2 + length2); + bug_type, offset1, offset1 + length1, offset2, offset2 + length2); Printf("%s", d.EndWarning()); PrintStack(stack); DescribeAddress((uptr)offset1, length1); DescribeAddress((uptr)offset2, length2); + ReportSummary(bug_type, stack); } // ----------------------- Mac-specific reports ----------------- {{{1 @@ -653,7 +691,7 @@ void __asan_report_error(uptr pc, uptr bp, uptr sp, PrintStack(&stack); DescribeAddress(addr, access_size); - + ReportSummary(bug_descr, &stack); PrintShadowMemoryForAddress(addr); } diff --git a/libsanitizer/asan/asan_report.h b/libsanitizer/asan/asan_report.h index a7e0e5816b7..13724dab9ee 100644 --- a/libsanitizer/asan/asan_report.h +++ b/libsanitizer/asan/asan_report.h @@ -13,15 +13,15 @@ #include "asan_allocator.h" #include "asan_internal.h" #include "asan_thread.h" -#include "sanitizer/asan_interface.h" namespace __asan { // The following functions prints address description depending // on the memory type (shadow/heap/stack/global). void DescribeHeapAddress(uptr addr, uptr access_size); -bool DescribeAddressIfGlobal(uptr addr); -bool DescribeAddressRelativeToGlobal(uptr addr, const __asan_global &g); +bool DescribeAddressIfGlobal(uptr addr, uptr access_size); +bool DescribeAddressRelativeToGlobal(uptr addr, uptr access_size, + const __asan_global &g); bool DescribeAddressIfShadow(uptr addr); bool DescribeAddressIfStack(uptr addr, uptr access_size); // Determines memory type on its own. diff --git a/libsanitizer/asan/asan_rtl.cc b/libsanitizer/asan/asan_rtl.cc index 04d2d7af63f..175d37788c2 100644 --- a/libsanitizer/asan/asan_rtl.cc +++ b/libsanitizer/asan/asan_rtl.cc @@ -18,7 +18,6 @@ #include "asan_stats.h" #include "asan_thread.h" #include "asan_thread_registry.h" -#include "sanitizer/asan_interface.h" #include "sanitizer_common/sanitizer_atomic.h" #include "sanitizer_common/sanitizer_flags.h" #include "sanitizer_common/sanitizer_libc.h" @@ -83,7 +82,6 @@ static void ParseFlagsFromString(Flags *f, const char *str) { ParseFlag(str, &f->replace_str, "replace_str"); ParseFlag(str, &f->replace_intrin, "replace_intrin"); - ParseFlag(str, &f->replace_cfallocator, "replace_cfallocator"); ParseFlag(str, &f->mac_ignore_invalid_free, "mac_ignore_invalid_free"); ParseFlag(str, &f->use_fake_stack, "use_fake_stack"); ParseFlag(str, &f->max_malloc_fill_size, "max_malloc_fill_size"); @@ -95,6 +93,8 @@ static void ParseFlagsFromString(Flags *f, const char *str) { ParseFlag(str, &f->check_malloc_usable_size, "check_malloc_usable_size"); ParseFlag(str, &f->unmap_shadow_on_exit, "unmap_shadow_on_exit"); ParseFlag(str, &f->abort_on_error, "abort_on_error"); + ParseFlag(str, &f->print_stats, "print_stats"); + ParseFlag(str, &f->print_legend, "print_legend"); ParseFlag(str, &f->atexit, "atexit"); ParseFlag(str, &f->disable_core, "disable_core"); ParseFlag(str, &f->strip_path_prefix, "strip_path_prefix"); @@ -121,7 +121,6 @@ void InitializeFlags(Flags *f, const char *env) { f->malloc_context_size = kDeafultMallocContextSize; f->replace_str = true; f->replace_intrin = true; - f->replace_cfallocator = true; f->mac_ignore_invalid_free = false; f->use_fake_stack = true; f->max_malloc_fill_size = 0; @@ -133,6 +132,8 @@ void InitializeFlags(Flags *f, const char *env) { f->check_malloc_usable_size = true; f->unmap_shadow_on_exit = false; f->abort_on_error = false; + f->print_stats = false; + f->print_legend = true; f->atexit = false; f->disable_core = (SANITIZER_WORDSIZE == 64); f->strip_path_prefix = ""; @@ -142,9 +143,7 @@ void InitializeFlags(Flags *f, const char *env) { f->fast_unwind_on_fatal = false; f->fast_unwind_on_malloc = true; f->poison_heap = true; - // Turn off alloc/dealloc mismatch checker on Mac for now. - // TODO(glider): Fix known issues and enable this back. - f->alloc_dealloc_mismatch = (ASAN_MAC == 0); + f->alloc_dealloc_mismatch = true; f->use_stack_depot = true; // Only affects allocator2. // Override from user-specified string. @@ -162,6 +161,7 @@ void InitializeFlags(Flags *f, const char *env) { int asan_inited; bool asan_init_is_running; void (*death_callback)(void); +uptr kHighMemEnd; // -------------------------- Misc ---------------- {{{1 void ShowStatsAndAbort() { @@ -261,6 +261,24 @@ static void asan_atexit() { __asan_print_accumulated_stats(); } +static void InitializeHighMemEnd() { +#if SANITIZER_WORDSIZE == 64 +# if defined(__powerpc64__) + // FIXME: + // On PowerPC64 we have two different address space layouts: 44- and 46-bit. + // We somehow need to figure our which one we are using now and choose + // one of 0x00000fffffffffffUL and 0x00003fffffffffffUL. + // Note that with 'ulimit -s unlimited' the stack is moved away from the top + // of the address space, so simply checking the stack address is not enough. + kHighMemEnd = (1ULL << 44) - 1; // 0x00000fffffffffffUL +# else + kHighMemEnd = (1ULL << 47) - 1; // 0x00007fffffffffffUL; +# endif +#else // SANITIZER_WORDSIZE == 32 + kHighMemEnd = (1ULL << 32) - 1; // 0xffffffff; +#endif // SANITIZER_WORDSIZE +} + } // namespace __asan // ---------------------- Interface ---------------- {{{1 @@ -295,8 +313,10 @@ void NOINLINE __asan_set_death_callback(void (*callback)(void)) { void __asan_init() { if (asan_inited) return; + SanitizerToolName = "AddressSanitizer"; CHECK(!asan_init_is_running && "ASan init calls itself!"); asan_init_is_running = true; + InitializeHighMemEnd(); // Make sure we are not statically linked. AsanDoesNotSupportStaticLinkage(); @@ -400,6 +420,8 @@ void __asan_init() { asanThreadRegistry().GetMain()->ThreadStart(); force_interface_symbols(); // no-op. + InitializeAllocator(); + if (flags()->verbosity) { Report("AddressSanitizer Init done\n"); } diff --git a/libsanitizer/asan/asan_stack.cc b/libsanitizer/asan/asan_stack.cc index 9b6a28e8082..999cbfba757 100644 --- a/libsanitizer/asan/asan_stack.cc +++ b/libsanitizer/asan/asan_stack.cc @@ -9,9 +9,9 @@ // // Code for ASan stack trace. //===----------------------------------------------------------------------===// +#include "asan_internal.h" #include "asan_flags.h" #include "asan_stack.h" -#include "sanitizer/asan_interface.h" namespace __asan { diff --git a/libsanitizer/asan/asan_stats.cc b/libsanitizer/asan/asan_stats.cc index 1187bd92f97..935b33e20ac 100644 --- a/libsanitizer/asan/asan_stats.cc +++ b/libsanitizer/asan/asan_stats.cc @@ -13,7 +13,6 @@ #include "asan_internal.h" #include "asan_stats.h" #include "asan_thread_registry.h" -#include "sanitizer/asan_interface.h" #include "sanitizer_common/sanitizer_stackdepot.h" namespace __asan { diff --git a/libsanitizer/asan/dynamic/asan_interceptors_dynamic.cc b/libsanitizer/asan/dynamic/asan_interceptors_dynamic.cc index 757c704bd55..727edf2b43b 100644 --- a/libsanitizer/asan/dynamic/asan_interceptors_dynamic.cc +++ b/libsanitizer/asan/dynamic/asan_interceptors_dynamic.cc @@ -17,11 +17,6 @@ namespace __asan { -#if !MAC_INTERPOSE_FUNCTIONS -# error \ - Dynamic interposing library should be built with -DMAC_INTERPOSE_FUNCTIONS -#endif - #define INTERPOSE_FUNCTION(function) \ { reinterpret_cast<const uptr>(WRAP(function)), \ reinterpret_cast<const uptr>(function) } diff --git a/libsanitizer/include/sanitizer/asan_interface.h b/libsanitizer/include/sanitizer/asan_interface.h index 9f9f12cbfa2..18696a681ed 100644 --- a/libsanitizer/include/sanitizer/asan_interface.h +++ b/libsanitizer/include/sanitizer/asan_interface.h @@ -5,69 +5,18 @@ // //===----------------------------------------------------------------------===// // -// This file is a part of AddressSanitizer, an address sanity checker. +// This file is a part of AddressSanitizer. // -// This header can be included by the instrumented program to fetch -// data (mostly allocator statistics) from ASan runtime library. +// Public interface header. //===----------------------------------------------------------------------===// #ifndef SANITIZER_ASAN_INTERFACE_H #define SANITIZER_ASAN_INTERFACE_H #include <sanitizer/common_interface_defs.h> -// ----------- ATTENTION ------------- -// This header should NOT include any other headers from ASan runtime. -// All functions in this header are extern "C" and start with __asan_. - -using __sanitizer::uptr; - +#ifdef __cplusplus extern "C" { - // This function should be called at the very beginning of the process, - // before any instrumented code is executed and before any call to malloc. - void __asan_init() SANITIZER_INTERFACE_ATTRIBUTE; - - // This structure describes an instrumented global variable. - struct __asan_global { - uptr beg; // The address of the global. - uptr size; // The original size of the global. - uptr size_with_redzone; // The size with the redzone. - const char *name; // Name as a C string. - uptr has_dynamic_init; // Non-zero if the global has dynamic initializer. - }; - - // These two functions should be called by the instrumented code. - // 'globals' is an array of structures describing 'n' globals. - void __asan_register_globals(__asan_global *globals, uptr n) - SANITIZER_INTERFACE_ATTRIBUTE; - void __asan_unregister_globals(__asan_global *globals, uptr n) - SANITIZER_INTERFACE_ATTRIBUTE; - - // These two functions should be called before and after dynamic initializers - // run, respectively. They should be called with parameters describing all - // dynamically initialized globals defined in the calling TU. - void __asan_before_dynamic_init(uptr first_addr, uptr last_addr) - SANITIZER_INTERFACE_ATTRIBUTE; - void __asan_after_dynamic_init() - SANITIZER_INTERFACE_ATTRIBUTE; - - // These two functions are used by the instrumented code in the - // use-after-return mode. __asan_stack_malloc allocates size bytes of - // fake stack and __asan_stack_free poisons it. real_stack is a pointer to - // the real stack region. - uptr __asan_stack_malloc(uptr size, uptr real_stack) - SANITIZER_INTERFACE_ATTRIBUTE; - void __asan_stack_free(uptr ptr, uptr size, uptr real_stack) - SANITIZER_INTERFACE_ATTRIBUTE; - - // These two functions are used by instrumented code in the - // use-after-scope mode. They mark memory for local variables as - // unaddressable when they leave scope and addressable before the - // function exits. - void __asan_poison_stack_memory(uptr addr, uptr size) - SANITIZER_INTERFACE_ATTRIBUTE; - void __asan_unpoison_stack_memory(uptr addr, uptr size) - SANITIZER_INTERFACE_ATTRIBUTE; - +#endif // Marks memory region [addr, addr+size) as unaddressable. // This memory must be previously allocated by the user program. Accessing // addresses in this region from instrumented code is forbidden until @@ -76,8 +25,7 @@ extern "C" { // to ASan alignment restrictions. // Method is NOT thread-safe in the sense that no two threads can // (un)poison memory in the same memory region simultaneously. - void __asan_poison_memory_region(void const volatile *addr, uptr size) - SANITIZER_INTERFACE_ATTRIBUTE; + void __asan_poison_memory_region(void const volatile *addr, size_t size); // Marks memory region [addr, addr+size) as addressable. // This memory must be previously allocated by the user program. Accessing // addresses in this region is allowed until this region is poisoned again. @@ -85,14 +33,9 @@ extern "C" { // ASan alignment restrictions. // Method is NOT thread-safe in the sense that no two threads can // (un)poison memory in the same memory region simultaneously. - void __asan_unpoison_memory_region(void const volatile *addr, uptr size) - SANITIZER_INTERFACE_ATTRIBUTE; - - // Performs cleanup before a NoReturn function. Must be called before things - // like _exit and execl to avoid false positives on stack. - void __asan_handle_no_return() SANITIZER_INTERFACE_ATTRIBUTE; + void __asan_unpoison_memory_region(void const volatile *addr, size_t size); -// User code should use macro instead of functions. + // User code should use macro instead of functions. #if __has_feature(address_sanitizer) #define ASAN_POISON_MEMORY_REGION(addr, size) \ __asan_poison_memory_region((addr), (size)) @@ -107,104 +50,86 @@ extern "C" { // Returns true iff addr is poisoned (i.e. 1-byte read/write access to this // address will result in error report from AddressSanitizer). - bool __asan_address_is_poisoned(void const volatile *addr) - SANITIZER_INTERFACE_ATTRIBUTE; + bool __asan_address_is_poisoned(void const volatile *addr); // If at least on byte in [beg, beg+size) is poisoned, return the address // of the first such byte. Otherwise return 0. - uptr __asan_region_is_poisoned(uptr beg, uptr size) - SANITIZER_INTERFACE_ATTRIBUTE; + void *__asan_region_is_poisoned(void *beg, size_t size); // Print the description of addr (useful when debugging in gdb). - void __asan_describe_address(uptr addr) - SANITIZER_INTERFACE_ATTRIBUTE; + void __asan_describe_address(void *addr); // This is an internal function that is called to report an error. // However it is still a part of the interface because users may want to // set a breakpoint on this function in a debugger. - void __asan_report_error(uptr pc, uptr bp, uptr sp, - uptr addr, bool is_write, uptr access_size) - SANITIZER_INTERFACE_ATTRIBUTE; + void __asan_report_error(void *pc, void *bp, void *sp, + void *addr, bool is_write, size_t access_size); // Sets the exit code to use when reporting an error. // Returns the old value. - int __asan_set_error_exit_code(int exit_code) - SANITIZER_INTERFACE_ATTRIBUTE; + int __asan_set_error_exit_code(int exit_code); // Sets the callback to be called right before death on error. // Passing 0 will unset the callback. - void __asan_set_death_callback(void (*callback)(void)) - SANITIZER_INTERFACE_ATTRIBUTE; + void __asan_set_death_callback(void (*callback)(void)); - void __asan_set_error_report_callback(void (*callback)(const char*)) - SANITIZER_INTERFACE_ATTRIBUTE; + void __asan_set_error_report_callback(void (*callback)(const char*)); // User may provide function that would be called right when ASan detects // an error. This can be used to notice cases when ASan detects an error, but // the program crashes before ASan report is printed. - /* OPTIONAL */ void __asan_on_error() - SANITIZER_WEAK_ATTRIBUTE SANITIZER_INTERFACE_ATTRIBUTE; + void __asan_on_error(); // User may provide its own implementation for symbolization function. // It should print the description of instruction at address "pc" to // "out_buffer". Description should be at most "out_size" bytes long. // User-specified function should return true if symbolization was // successful. - /* OPTIONAL */ bool __asan_symbolize(const void *pc, char *out_buffer, - int out_size) - SANITIZER_WEAK_ATTRIBUTE SANITIZER_INTERFACE_ATTRIBUTE; + bool __asan_symbolize(const void *pc, char *out_buffer, + int out_size); // Returns the estimated number of bytes that will be reserved by allocator // for request of "size" bytes. If ASan allocator can't allocate that much // memory, returns the maximal possible allocation size, otherwise returns // "size". - uptr __asan_get_estimated_allocated_size(uptr size) - SANITIZER_INTERFACE_ATTRIBUTE; + size_t __asan_get_estimated_allocated_size(size_t size); // Returns true if p was returned by the ASan allocator and // is not yet freed. - bool __asan_get_ownership(const void *p) - SANITIZER_INTERFACE_ATTRIBUTE; + bool __asan_get_ownership(const void *p); // Returns the number of bytes reserved for the pointer p. // Requires (get_ownership(p) == true) or (p == 0). - uptr __asan_get_allocated_size(const void *p) - SANITIZER_INTERFACE_ATTRIBUTE; + size_t __asan_get_allocated_size(const void *p); // Number of bytes, allocated and not yet freed by the application. - uptr __asan_get_current_allocated_bytes() - SANITIZER_INTERFACE_ATTRIBUTE; + size_t __asan_get_current_allocated_bytes(); // Number of bytes, mmaped by asan allocator to fulfill allocation requests. // Generally, for request of X bytes, allocator can reserve and add to free // lists a large number of chunks of size X to use them for future requests. // All these chunks count toward the heap size. Currently, allocator never // releases memory to OS (instead, it just puts freed chunks to free lists). - uptr __asan_get_heap_size() - SANITIZER_INTERFACE_ATTRIBUTE; + size_t __asan_get_heap_size(); // Number of bytes, mmaped by asan allocator, which can be used to fulfill // allocation requests. When a user program frees memory chunk, it can first // fall into quarantine and will count toward __asan_get_free_bytes() later. - uptr __asan_get_free_bytes() - SANITIZER_INTERFACE_ATTRIBUTE; + size_t __asan_get_free_bytes(); // Number of bytes in unmapped pages, that are released to OS. Currently, // always returns 0. - uptr __asan_get_unmapped_bytes() - SANITIZER_INTERFACE_ATTRIBUTE; + size_t __asan_get_unmapped_bytes(); // Prints accumulated stats to stderr. Used for debugging. - void __asan_print_accumulated_stats() - SANITIZER_INTERFACE_ATTRIBUTE; + void __asan_print_accumulated_stats(); // This function may be optionally provided by user and should return // a string containing ASan runtime options. See asan_flags.h for details. - /* OPTIONAL */ const char* __asan_default_options() - SANITIZER_WEAK_ATTRIBUTE SANITIZER_INTERFACE_ATTRIBUTE; + const char* __asan_default_options(); // Malloc hooks that may be optionally provided by user. // __asan_malloc_hook(ptr, size) is called immediately after // allocation of "size" bytes, which returned "ptr". // __asan_free_hook(ptr) is called immediately before // deallocation of "ptr". - /* OPTIONAL */ void __asan_malloc_hook(void *ptr, uptr size) - SANITIZER_WEAK_ATTRIBUTE SANITIZER_INTERFACE_ATTRIBUTE; - /* OPTIONAL */ void __asan_free_hook(void *ptr) - SANITIZER_WEAK_ATTRIBUTE SANITIZER_INTERFACE_ATTRIBUTE; + void __asan_malloc_hook(void *ptr, size_t size); + void __asan_free_hook(void *ptr); +#ifdef __cplusplus } // extern "C" +#endif #endif // SANITIZER_ASAN_INTERFACE_H diff --git a/libsanitizer/include/sanitizer/common_interface_defs.h b/libsanitizer/include/sanitizer/common_interface_defs.h index 9fba976a041..b61b8a1a636 100644 --- a/libsanitizer/include/sanitizer/common_interface_defs.h +++ b/libsanitizer/include/sanitizer/common_interface_defs.h @@ -5,86 +5,37 @@ // //===----------------------------------------------------------------------===// // -// This file is shared between AddressSanitizer and ThreadSanitizer. -// It contains basic macro and types. -// NOTE: This file may be included into user code. +// Common part of the public sanitizer interface. //===----------------------------------------------------------------------===// #ifndef SANITIZER_COMMON_INTERFACE_DEFS_H #define SANITIZER_COMMON_INTERFACE_DEFS_H -// ----------- ATTENTION ------------- -// This header should NOT include any other headers to avoid portability issues. - -#if defined(_WIN32) -// FIXME find out what we need on Windows. __declspec(dllexport) ? -# define SANITIZER_INTERFACE_ATTRIBUTE -# define SANITIZER_WEAK_ATTRIBUTE -#elif defined(SANITIZER_GO) -# define SANITIZER_INTERFACE_ATTRIBUTE -# define SANITIZER_WEAK_ATTRIBUTE -#else -# define SANITIZER_INTERFACE_ATTRIBUTE __attribute__((visibility("default"))) -# define SANITIZER_WEAK_ATTRIBUTE __attribute__((weak)) -#endif - -#ifdef __linux__ -# define SANITIZER_SUPPORTS_WEAK_HOOKS 1 -#else -# define SANITIZER_SUPPORTS_WEAK_HOOKS 0 -#endif - -// __has_feature -#if !defined(__has_feature) -# define __has_feature(x) 0 -#endif - -// For portability reasons we do not include stddef.h, stdint.h or any other -// system header, but we do need some basic types that are not defined -// in a portable way by the language itself. -namespace __sanitizer { - -#if defined(_WIN64) -// 64-bit Windows uses LLP64 data model. -typedef unsigned long long uptr; // NOLINT -typedef signed long long sptr; // NOLINT -#else -typedef unsigned long uptr; // NOLINT -typedef signed long sptr; // NOLINT -#endif // defined(_WIN64) -#if defined(__x86_64__) -// Since x32 uses ILP32 data model in 64-bit hardware mode, we must use -// 64-bit pointer to unwind stack frame. -typedef unsigned long long uhwptr; // NOLINT -#else -typedef uptr uhwptr; // NOLINT -#endif -typedef unsigned char u8; -typedef unsigned short u16; // NOLINT -typedef unsigned int u32; -typedef unsigned long long u64; // NOLINT -typedef signed char s8; -typedef signed short s16; // NOLINT -typedef signed int s32; -typedef signed long long s64; // NOLINT - -} // namespace __sanitizer +#include <stddef.h> +#include <stdint.h> +#ifdef __cplusplus extern "C" { +#endif // Tell the tools to write their reports to "path.<pid>" instead of stderr. - void __sanitizer_set_report_path(const char *path) - SANITIZER_INTERFACE_ATTRIBUTE; + void __sanitizer_set_report_path(const char *path); // Tell the tools to write their reports to given file descriptor instead of // stderr. - void __sanitizer_set_report_fd(int fd) - SANITIZER_INTERFACE_ATTRIBUTE; + void __sanitizer_set_report_fd(int fd); // Notify the tools that the sandbox is going to be turned on. The reserved // parameter will be used in the future to hold a structure with functions // that the tools may call to bypass the sandbox. - void __sanitizer_sandbox_on_notify(void *reserved) - SANITIZER_WEAK_ATTRIBUTE SANITIZER_INTERFACE_ATTRIBUTE; + void __sanitizer_sandbox_on_notify(void *reserved); + + // This function is called by the tool when it has just finished reporting + // an error. 'error_summary' is a one-line string that summarizes + // the error message. This function can be overridden by the client. + void __sanitizer_report_error_summary(const char *error_summary); + +#ifdef __cplusplus } // extern "C" +#endif #endif // SANITIZER_COMMON_INTERFACE_DEFS_H diff --git a/libsanitizer/interception/Makefile.am b/libsanitizer/interception/Makefile.am index 8c6fbbf9b4b..4218983160b 100644 --- a/libsanitizer/interception/Makefile.am +++ b/libsanitizer/interception/Makefile.am @@ -1,4 +1,4 @@ -AM_CPPFLAGS = -I $(top_srcdir)/include +AM_CPPFLAGS = -I $(top_srcdir)/include -I $(top_srcdir) # May be used by toolexeclibdir. gcc_version := $(shell cat $(top_srcdir)/../gcc/BASE-VER) @@ -13,7 +13,8 @@ noinst_LTLIBRARIES = libinterception.la interception_files = \ interception_linux.cc \ interception_mac.cc \ - interception_win.cc + interception_win.cc \ + interception_type_test.cc libinterception_la_SOURCES = $(interception_files) diff --git a/libsanitizer/interception/Makefile.in b/libsanitizer/interception/Makefile.in index cc07bd1a987..f7c715b5c90 100644 --- a/libsanitizer/interception/Makefile.in +++ b/libsanitizer/interception/Makefile.in @@ -56,7 +56,7 @@ CONFIG_CLEAN_VPATH_FILES = LTLIBRARIES = $(noinst_LTLIBRARIES) libinterception_la_LIBADD = am__objects_1 = interception_linux.lo interception_mac.lo \ - interception_win.lo + interception_win.lo interception_type_test.lo am_libinterception_la_OBJECTS = $(am__objects_1) libinterception_la_OBJECTS = $(am_libinterception_la_OBJECTS) DEFAULT_INCLUDES = -I.@am__isrc@ @@ -208,7 +208,7 @@ toolexeclibdir = @toolexeclibdir@ top_build_prefix = @top_build_prefix@ top_builddir = @top_builddir@ top_srcdir = @top_srcdir@ -AM_CPPFLAGS = -I $(top_srcdir)/include +AM_CPPFLAGS = -I $(top_srcdir)/include -I $(top_srcdir) # May be used by toolexeclibdir. gcc_version := $(shell cat $(top_srcdir)/../gcc/BASE-VER) @@ -221,7 +221,8 @@ noinst_LTLIBRARIES = libinterception.la interception_files = \ interception_linux.cc \ interception_mac.cc \ - interception_win.cc + interception_win.cc \ + interception_type_test.cc libinterception_la_SOURCES = $(interception_files) @@ -318,6 +319,7 @@ distclean-compile: @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/interception_linux.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/interception_mac.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/interception_type_test.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/interception_win.Plo@am__quote@ .cc.o: diff --git a/libsanitizer/interception/interception.h b/libsanitizer/interception/interception.h index ccf5e1b1ec0..b4c4137e88e 100644 --- a/libsanitizer/interception/interception.h +++ b/libsanitizer/interception/interception.h @@ -17,7 +17,7 @@ # error "Interception doesn't work on this operating system." #endif -#include "sanitizer/common_interface_defs.h" +#include "sanitizer_common/sanitizer_internal_defs.h" // These typedefs should be used only in the interceptor definitions to replace // the standard system types (e.g. SSIZE_T instead of ssize_t) @@ -25,21 +25,17 @@ typedef __sanitizer::uptr SIZE_T; typedef __sanitizer::sptr SSIZE_T; typedef __sanitizer::sptr PTRDIFF_T; typedef __sanitizer::s64 INTMAX_T; -typedef __sanitizer::u64 OFF_T; +// WARNING: OFF_T may be different from OS type off_t, depending on the value of +// _FILE_OFFSET_BITS. This definition of OFF_T matches the ABI of system calls +// like pread and mmap, as opposed to pread64 and mmap64. +// Mac is special. +#ifdef __APPLE__ +typedef __sanitizer::u64 OFF_T; +#else +typedef __sanitizer::uptr OFF_T; +#endif typedef __sanitizer::u64 OFF64_T; -// How to use this library: -// 1) Include this header to define your own interceptors -// (see details below). -// 2) Build all *.cc files and link against them. -// On Mac you will also need to: -// 3) Provide your own implementation for the following functions: -// mach_error_t __interception::allocate_island(void **ptr, -// size_t size, -// void *hint); -// mach_error_t __interception::deallocate_island(void *ptr); -// See "interception_mac.h" for more details. - // How to add an interceptor: // Suppose you need to wrap/replace system function (generally, from libc): // int foo(const char *bar, double baz); @@ -80,20 +76,12 @@ typedef __sanitizer::u64 OFF64_T; // This is not so on Mac OS, where the two-level namespace makes // our replacement functions invisible to other libraries. This may be overcomed // using the DYLD_FORCE_FLAT_NAMESPACE, but some errors loading the shared -// libraries in Chromium were noticed when doing so. Instead we use -// mach_override, a handy framework for patching functions at runtime. -// To avoid possible name clashes, our replacement functions have -// the "wrap_" prefix on Mac. -// An alternative to function patching is to create a dylib containing a -// __DATA,__interpose section that associates library functions with their -// wrappers. When this dylib is preloaded before an executable using -// DYLD_INSERT_LIBRARIES, it routes all the calls to interposed functions done -// through stubs to the wrapper functions. Such a library is built with -// -DMAC_INTERPOSE_FUNCTIONS=1. - -#if !defined(MAC_INTERPOSE_FUNCTIONS) || !defined(__APPLE__) -# define MAC_INTERPOSE_FUNCTIONS 0 -#endif +// libraries in Chromium were noticed when doing so. +// Instead we create a dylib containing a __DATA,__interpose section that +// associates library functions with their wrappers. When this dylib is +// preloaded before an executable using DYLD_INSERT_LIBRARIES, it routes all +// the calls to interposed functions done through stubs to the wrapper +// functions. #if defined(__APPLE__) # define WRAP(x) wrap_##x @@ -120,7 +108,7 @@ typedef __sanitizer::u64 OFF64_T; __attribute__((weak, alias("__interceptor_" #func), visibility("default"))); #endif -#if !MAC_INTERPOSE_FUNCTIONS +#if !defined(__APPLE__) # define PTR_TO_REAL(x) real_##x # define REAL(x) __interception::PTR_TO_REAL(x) # define FUNC_TYPE(x) x##_f @@ -130,11 +118,11 @@ typedef __sanitizer::u64 OFF64_T; namespace __interception { \ extern FUNC_TYPE(func) PTR_TO_REAL(func); \ } -#else // MAC_INTERPOSE_FUNCTIONS +#else // __APPLE__ # define REAL(x) x # define DECLARE_REAL(ret_type, func, ...) \ extern "C" ret_type func(__VA_ARGS__); -#endif // MAC_INTERPOSE_FUNCTIONS +#endif // __APPLE__ #define DECLARE_REAL_AND_INTERCEPTOR(ret_type, func, ...) \ DECLARE_REAL(ret_type, func, __VA_ARGS__) \ @@ -144,7 +132,7 @@ typedef __sanitizer::u64 OFF64_T; // macros does its job. In exceptional cases you may need to call REAL(foo) // without defining INTERCEPTOR(..., foo, ...). For example, if you override // foo with an interceptor for other function. -#if !MAC_INTERPOSE_FUNCTIONS +#if !defined(__APPLE__) # define DEFINE_REAL(ret_type, func, ...) \ typedef ret_type (*FUNC_TYPE(func))(__VA_ARGS__); \ namespace __interception { \ diff --git a/libsanitizer/interception/interception_mac.cc b/libsanitizer/interception/interception_mac.cc index c9b7dd32d0f..801dcba3d13 100644 --- a/libsanitizer/interception/interception_mac.cc +++ b/libsanitizer/interception/interception_mac.cc @@ -13,17 +13,6 @@ #ifdef __APPLE__ #include "interception.h" -#include "mach_override/mach_override.h" -namespace __interception { -bool OverrideFunction(uptr old_func, uptr new_func, uptr *orig_old_func) { - *orig_old_func = 0; - int res = __asan_mach_override_ptr_custom((void*)old_func, (void*)new_func, - (void**)orig_old_func, - __interception_allocate_island, - __interception_deallocate_island); - return (res == 0) && (*orig_old_func != 0); -} -} // namespace __interception #endif // __APPLE__ diff --git a/libsanitizer/interception/interception_mac.h b/libsanitizer/interception/interception_mac.h index 244b457d738..1b11182da98 100644 --- a/libsanitizer/interception/interception_mac.h +++ b/libsanitizer/interception/interception_mac.h @@ -19,29 +19,7 @@ #ifndef INTERCEPTION_MAC_H #define INTERCEPTION_MAC_H -#include <mach/mach_error.h> -#include <stddef.h> - -// Allocate memory for the escape island. This cannot be moved to -// mach_override, because each user of interceptors may specify its -// own memory range for escape islands. -extern "C" { -mach_error_t __interception_allocate_island(void **ptr, size_t unused_size, - void *unused_hint); -mach_error_t __interception_deallocate_island(void *ptr); -} // extern "C" - -namespace __interception { -// returns true if the old function existed. -bool OverrideFunction(uptr old_func, uptr new_func, uptr *orig_old_func); -} // namespace __interception - -# define OVERRIDE_FUNCTION_MAC(old_func, new_func) \ - ::__interception::OverrideFunction( \ - (::__interception::uptr)old_func, \ - (::__interception::uptr)new_func, \ - (::__interception::uptr*)((::__interception::uptr)&REAL(old_func))) -# define INTERCEPT_FUNCTION_MAC(func) OVERRIDE_FUNCTION_MAC(func, WRAP(func)) +#define INTERCEPT_FUNCTION_MAC(func) #endif // INTERCEPTION_MAC_H #endif // __APPLE__ diff --git a/libsanitizer/interception/interception_type_test.cc b/libsanitizer/interception/interception_type_test.cc new file mode 100644 index 00000000000..f664eeeb04f --- /dev/null +++ b/libsanitizer/interception/interception_type_test.cc @@ -0,0 +1,37 @@ +//===-- interception_type_test.cc -------------------------------*- C++ -*-===// +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file is a part of AddressSanitizer, an address sanity checker. +// +// Compile-time tests of the internal type definitions. +//===----------------------------------------------------------------------===// + +#if defined(__linux__) || defined(__APPLE__) + +#include "interception.h" +#include <sys/types.h> +#include <stddef.h> +#include <stdint.h> + +COMPILER_CHECK(sizeof(SIZE_T) == sizeof(size_t)); +COMPILER_CHECK(sizeof(SSIZE_T) == sizeof(ssize_t)); +COMPILER_CHECK(sizeof(PTRDIFF_T) == sizeof(ptrdiff_t)); +COMPILER_CHECK(sizeof(INTMAX_T) == sizeof(intmax_t)); + +#ifndef __APPLE__ +COMPILER_CHECK(sizeof(OFF64_T) == sizeof(off64_t)); +#endif + +// The following are the cases when pread (and friends) is used instead of +// pread64. In those cases we need OFF_T to match off_t. We don't care about the +// rest (they depend on _FILE_OFFSET_BITS setting when building an application). +# if defined(__ANDROID__) || !defined _FILE_OFFSET_BITS || \ + _FILE_OFFSET_BITS != 64 +COMPILER_CHECK(sizeof(OFF_T) == sizeof(off_t)); +# endif + +#endif diff --git a/libsanitizer/sanitizer_common/sanitizer_allocator.cc b/libsanitizer/sanitizer_common/sanitizer_allocator.cc index d09112705f5..a54de9d6f9a 100644 --- a/libsanitizer/sanitizer_common/sanitizer_allocator.cc +++ b/libsanitizer/sanitizer_common/sanitizer_allocator.cc @@ -73,4 +73,10 @@ void SetLowLevelAllocateCallback(LowLevelAllocateCallback callback) { low_level_alloc_callback = callback; } +bool CallocShouldReturnNullDueToOverflow(uptr size, uptr n) { + if (!size) return false; + uptr max = (uptr)-1L; + return (max / size) < n; +} + } // namespace __sanitizer diff --git a/libsanitizer/sanitizer_common/sanitizer_allocator.h b/libsanitizer/sanitizer_common/sanitizer_allocator.h index 1c9852e5003..889281216fc 100644 --- a/libsanitizer/sanitizer_common/sanitizer_allocator.h +++ b/libsanitizer/sanitizer_common/sanitizer_allocator.h @@ -23,9 +23,9 @@ namespace __sanitizer { // SizeClassMap maps allocation sizes into size classes and back. // Class 0 corresponds to size 0. -// Classes 1 - 16 correspond to sizes 8 - 128 (size = class_id * 8). -// Next 8 classes: 128 + i * 16 (i = 1 to 8). +// Classes 1 - 16 correspond to sizes 16 to 256 (size = class_id * 16). // Next 8 classes: 256 + i * 32 (i = 1 to 8). +// Next 8 classes: 512 + i * 64 (i = 1 to 8). // ... // Next 8 classes: 2^k + i * 2^(k-3) (i = 1 to 8). // Last class corresponds to kMaxSize = 1 << kMaxSizeLog. @@ -40,33 +40,48 @@ namespace __sanitizer { // - (1 << kMaxBytesCachedLog) is the maximal number of bytes per size class. // // Part of output of SizeClassMap::Print(): -// c00 => s: 0 diff: +0 00% l 0 cached: 0 0; id 0 -// c01 => s: 8 diff: +8 00% l 3 cached: 256 2048; id 1 -// c02 => s: 16 diff: +8 100% l 4 cached: 256 4096; id 2 -// ... -// c07 => s: 56 diff: +8 16% l 5 cached: 256 14336; id 7 +// c00 => s: 0 diff: +0 00% l 0 cached: 0 0; id 0 +// c01 => s: 16 diff: +16 00% l 4 cached: 256 4096; id 1 +// c02 => s: 32 diff: +16 100% l 5 cached: 256 8192; id 2 +// c03 => s: 48 diff: +16 50% l 5 cached: 256 12288; id 3 +// c04 => s: 64 diff: +16 33% l 6 cached: 256 16384; id 4 +// c05 => s: 80 diff: +16 25% l 6 cached: 256 20480; id 5 +// c06 => s: 96 diff: +16 20% l 6 cached: 256 24576; id 6 +// c07 => s: 112 diff: +16 16% l 6 cached: 256 28672; id 7 // -// c08 => s: 64 diff: +8 14% l 6 cached: 256 16384; id 8 -// ... -// c15 => s: 120 diff: +8 07% l 6 cached: 256 30720; id 15 +// c08 => s: 128 diff: +16 14% l 7 cached: 256 32768; id 8 +// c09 => s: 144 diff: +16 12% l 7 cached: 256 36864; id 9 +// c10 => s: 160 diff: +16 11% l 7 cached: 256 40960; id 10 +// c11 => s: 176 diff: +16 10% l 7 cached: 256 45056; id 11 +// c12 => s: 192 diff: +16 09% l 7 cached: 256 49152; id 12 +// c13 => s: 208 diff: +16 08% l 7 cached: 256 53248; id 13 +// c14 => s: 224 diff: +16 07% l 7 cached: 256 57344; id 14 +// c15 => s: 240 diff: +16 07% l 7 cached: 256 61440; id 15 // -// c16 => s: 128 diff: +8 06% l 7 cached: 256 32768; id 16 -// c17 => s: 144 diff: +16 12% l 7 cached: 227 32688; id 17 -// ... -// c23 => s: 240 diff: +16 07% l 7 cached: 136 32640; id 23 +// c16 => s: 256 diff: +16 06% l 8 cached: 256 65536; id 16 +// c17 => s: 288 diff: +32 12% l 8 cached: 227 65376; id 17 +// c18 => s: 320 diff: +32 11% l 8 cached: 204 65280; id 18 +// c19 => s: 352 diff: +32 10% l 8 cached: 186 65472; id 19 +// c20 => s: 384 diff: +32 09% l 8 cached: 170 65280; id 20 +// c21 => s: 416 diff: +32 08% l 8 cached: 157 65312; id 21 +// c22 => s: 448 diff: +32 07% l 8 cached: 146 65408; id 22 +// c23 => s: 480 diff: +32 07% l 8 cached: 136 65280; id 23 // -// c24 => s: 256 diff: +16 06% l 8 cached: 128 32768; id 24 -// c25 => s: 288 diff: +32 12% l 8 cached: 113 32544; id 25 -// ... -// c31 => s: 480 diff: +32 07% l 8 cached: 68 32640; id 31 +// c24 => s: 512 diff: +32 06% l 9 cached: 128 65536; id 24 +// c25 => s: 576 diff: +64 12% l 9 cached: 113 65088; id 25 +// c26 => s: 640 diff: +64 11% l 9 cached: 102 65280; id 26 +// c27 => s: 704 diff: +64 10% l 9 cached: 93 65472; id 27 +// c28 => s: 768 diff: +64 09% l 9 cached: 85 65280; id 28 +// c29 => s: 832 diff: +64 08% l 9 cached: 78 64896; id 29 +// c30 => s: 896 diff: +64 07% l 9 cached: 73 65408; id 30 +// c31 => s: 960 diff: +64 07% l 9 cached: 68 65280; id 31 // -// c32 => s: 512 diff: +32 06% l 9 cached: 64 32768; id 32 - +// c32 => s: 1024 diff: +64 06% l 10 cached: 64 65536; id 32 template <uptr kMaxSizeLog, uptr kMaxNumCachedT, uptr kMaxBytesCachedLog, uptr kMinBatchClassT> class SizeClassMap { - static const uptr kMinSizeLog = 3; + static const uptr kMinSizeLog = 4; static const uptr kMidSizeLog = kMinSizeLog + 4; static const uptr kMinSize = 1 << kMinSizeLog; static const uptr kMidSize = 1 << kMidSizeLog; @@ -104,7 +119,7 @@ class SizeClassMap { if (size <= kMidSize) return (size + kMinSize - 1) >> kMinSizeLog; if (size > kMaxSize) return 0; - uptr l = SANITIZER_WORDSIZE - 1 - __builtin_clzl(size); + uptr l = MostSignificantSetBitIndex(size); uptr hbits = (size >> (l - S)) & M; uptr lbits = size & ((1 << (l - S)) - 1); uptr l1 = l - kMidSizeLog; @@ -114,7 +129,7 @@ class SizeClassMap { static uptr MaxCached(uptr class_id) { if (class_id == 0) return 0; uptr n = (1UL << kMaxBytesCachedLog) / Size(class_id); - return Max(1UL, Min(kMaxNumCached, n)); + return Max<uptr>(1, Min(kMaxNumCached, n)); } static void Print() { @@ -126,7 +141,7 @@ class SizeClassMap { Printf("\n"); uptr d = s - prev_s; uptr p = prev_s ? (d * 100 / prev_s) : 0; - uptr l = SANITIZER_WORDSIZE - 1 - __builtin_clzl(s); + uptr l = MostSignificantSetBitIndex(s); uptr cached = MaxCached(i) * s; Printf("c%02zd => s: %zd diff: +%zd %02zd%% l %zd " "cached: %zd %zd; id %zd\n", @@ -172,12 +187,92 @@ class SizeClassMap { } }; -typedef SizeClassMap<17, 256, 16, FIRST_32_SECOND_64(33, 36)> +typedef SizeClassMap<17, 256, 16, FIRST_32_SECOND_64(25, 28)> DefaultSizeClassMap; -typedef SizeClassMap<17, 64, 14, FIRST_32_SECOND_64(25, 28)> +typedef SizeClassMap<17, 64, 14, FIRST_32_SECOND_64(17, 20)> CompactSizeClassMap; template<class SizeClassAllocator> struct SizeClassAllocatorLocalCache; +// Memory allocator statistics +enum AllocatorStat { + AllocatorStatMalloced, + AllocatorStatFreed, + AllocatorStatMmapped, + AllocatorStatUnmapped, + AllocatorStatCount +}; + +typedef u64 AllocatorStatCounters[AllocatorStatCount]; + +// Per-thread stats, live in per-thread cache. +class AllocatorStats { + public: + void Init() { + internal_memset(this, 0, sizeof(*this)); + } + + void Add(AllocatorStat i, u64 v) { + v += atomic_load(&stats_[i], memory_order_relaxed); + atomic_store(&stats_[i], v, memory_order_relaxed); + } + + void Set(AllocatorStat i, u64 v) { + atomic_store(&stats_[i], v, memory_order_relaxed); + } + + u64 Get(AllocatorStat i) const { + return atomic_load(&stats_[i], memory_order_relaxed); + } + + private: + friend class AllocatorGlobalStats; + AllocatorStats *next_; + AllocatorStats *prev_; + atomic_uint64_t stats_[AllocatorStatCount]; +}; + +// Global stats, used for aggregation and querying. +class AllocatorGlobalStats : public AllocatorStats { + public: + void Init() { + internal_memset(this, 0, sizeof(*this)); + next_ = this; + prev_ = this; + } + + void Register(AllocatorStats *s) { + SpinMutexLock l(&mu_); + s->next_ = next_; + s->prev_ = this; + next_->prev_ = s; + next_ = s; + } + + void Unregister(AllocatorStats *s) { + SpinMutexLock l(&mu_); + s->prev_->next_ = s->next_; + s->next_->prev_ = s->prev_; + for (int i = 0; i < AllocatorStatCount; i++) + Add(AllocatorStat(i), s->Get(AllocatorStat(i))); + } + + void Get(AllocatorStatCounters s) const { + internal_memset(s, 0, AllocatorStatCount * sizeof(u64)); + SpinMutexLock l(&mu_); + const AllocatorStats *stats = this; + for (;;) { + for (int i = 0; i < AllocatorStatCount; i++) + s[i] += stats->Get(AllocatorStat(i)); + stats = stats->next_; + if (stats == this) + break; + } + } + + private: + mutable SpinMutex mu_; +}; + // Allocators call these callbacks on mmap/munmap. struct NoOpMapUnmapCallback { void OnMap(uptr p, uptr size) const { } @@ -231,17 +326,18 @@ class SizeClassAllocator64 { alignment <= SizeClassMap::kMaxSize; } - Batch *NOINLINE AllocateBatch(AllocatorCache *c, uptr class_id) { + NOINLINE Batch* AllocateBatch(AllocatorStats *stat, AllocatorCache *c, + uptr class_id) { CHECK_LT(class_id, kNumClasses); RegionInfo *region = GetRegionInfo(class_id); Batch *b = region->free_list.Pop(); if (b == 0) - b = PopulateFreeList(c, class_id, region); + b = PopulateFreeList(stat, c, class_id, region); region->n_allocated += b->count; return b; } - void NOINLINE DeallocateBatch(uptr class_id, Batch *b) { + NOINLINE void DeallocateBatch(AllocatorStats *stat, uptr class_id, Batch *b) { RegionInfo *region = GetRegionInfo(class_id); region->free_list.Push(b); region->n_freed += b->count; @@ -320,6 +416,20 @@ class SizeClassAllocator64 { } } + // ForceLock() and ForceUnlock() are needed to implement Darwin malloc zone + // introspection API. + void ForceLock() { + for (uptr i = 0; i < kNumClasses; i++) { + GetRegionInfo(i)->mutex.Lock(); + } + } + + void ForceUnlock() { + for (int i = (int)kNumClasses - 1; i >= 0; i--) { + GetRegionInfo(i)->mutex.Unlock(); + } + } + typedef SizeClassMap SizeClassMapT; static const uptr kNumClasses = SizeClassMap::kNumClasses; static const uptr kNumClassesRounded = SizeClassMap::kNumClassesRounded; @@ -334,7 +444,7 @@ class SizeClassAllocator64 { // or with one element if its size is greater. static const uptr kPopulateSize = 1 << 14; // Call mmap for user memory with at least this size. - static const uptr kUserMapSize = 1 << 15; + static const uptr kUserMapSize = 1 << 16; // Call mmap for metadata memory with at least this size. static const uptr kMetaMapSize = 1 << 16; @@ -368,8 +478,8 @@ class SizeClassAllocator64 { return offset / (u32)size; } - Batch *NOINLINE PopulateFreeList(AllocatorCache *c, uptr class_id, - RegionInfo *region) { + NOINLINE Batch* PopulateFreeList(AllocatorStats *stat, AllocatorCache *c, + uptr class_id, RegionInfo *region) { BlockingMutexLock l(®ion->mutex); Batch *b = region->free_list.Pop(); if (b) @@ -386,6 +496,7 @@ class SizeClassAllocator64 { map_size += kUserMapSize; CHECK_GE(region->mapped_user + map_size, end_idx); MapWithCallback(region_beg + region->mapped_user, map_size); + stat->Add(AllocatorStatMmapped, map_size); region->mapped_user += map_size; } uptr total_count = (region->mapped_user - beg_idx - size) @@ -467,6 +578,7 @@ class SizeClassAllocator32 { MapUnmapCallback().OnMap((uptr)res, size); return res; } + void UnmapWithCallback(uptr beg, uptr size) { MapUnmapCallback().OnUnmap(beg, size); UnmapOrDie(reinterpret_cast<void *>(beg), size); @@ -488,19 +600,20 @@ class SizeClassAllocator32 { return reinterpret_cast<void*>(meta); } - Batch *NOINLINE AllocateBatch(AllocatorCache *c, uptr class_id) { + NOINLINE Batch* AllocateBatch(AllocatorStats *stat, AllocatorCache *c, + uptr class_id) { CHECK_LT(class_id, kNumClasses); SizeClassInfo *sci = GetSizeClassInfo(class_id); SpinMutexLock l(&sci->mutex); if (sci->free_list.empty()) - PopulateFreeList(c, sci, class_id); + PopulateFreeList(stat, c, sci, class_id); CHECK(!sci->free_list.empty()); Batch *b = sci->free_list.front(); sci->free_list.pop_front(); return b; } - void NOINLINE DeallocateBatch(uptr class_id, Batch *b) { + NOINLINE void DeallocateBatch(AllocatorStats *stat, uptr class_id, Batch *b) { CHECK_LT(class_id, kNumClasses); SizeClassInfo *sci = GetSizeClassInfo(class_id); SpinMutexLock l(&sci->mutex); @@ -549,6 +662,20 @@ class SizeClassAllocator32 { UnmapWithCallback(reinterpret_cast<uptr>(state_), sizeof(State)); } + // ForceLock() and ForceUnlock() are needed to implement Darwin malloc zone + // introspection API. + void ForceLock() { + for (uptr i = 0; i < kNumClasses; i++) { + GetSizeClassInfo(i)->mutex.Lock(); + } + } + + void ForceUnlock() { + for (int i = kNumClasses - 1; i >= 0; i--) { + GetSizeClassInfo(i)->mutex.Unlock(); + } + } + void PrintStats() { } @@ -577,11 +704,12 @@ class SizeClassAllocator32 { return mem & ~(kRegionSize - 1); } - uptr AllocateRegion(uptr class_id) { + uptr AllocateRegion(AllocatorStats *stat, uptr class_id) { CHECK_LT(class_id, kNumClasses); uptr res = reinterpret_cast<uptr>(MmapAlignedOrDie(kRegionSize, kRegionSize, "SizeClassAllocator32")); MapUnmapCallback().OnMap(res, kRegionSize); + stat->Add(AllocatorStatMmapped, kRegionSize); CHECK_EQ(0U, (res & (kRegionSize - 1))); CHECK_EQ(0U, state_->possible_regions[ComputeRegionId(res)]); state_->possible_regions[ComputeRegionId(res)] = class_id; @@ -593,9 +721,10 @@ class SizeClassAllocator32 { return &state_->size_class_info_array[class_id]; } - void PopulateFreeList(AllocatorCache *c, SizeClassInfo *sci, uptr class_id) { + void PopulateFreeList(AllocatorStats *stat, AllocatorCache *c, + SizeClassInfo *sci, uptr class_id) { uptr size = SizeClassMap::Size(class_id); - uptr reg = AllocateRegion(class_id); + uptr reg = AllocateRegion(stat, class_id); uptr n_chunks = kRegionSize / (size + kMetadataSize); uptr max_count = SizeClassMap::MaxCached(class_id); Batch *b = 0; @@ -632,14 +761,22 @@ struct SizeClassAllocatorLocalCache { typedef SizeClassAllocator Allocator; static const uptr kNumClasses = SizeClassAllocator::kNumClasses; - // Don't need to call Init if the object is a global (i.e. zero-initialized). - void Init() { - internal_memset(this, 0, sizeof(*this)); + void Init(AllocatorGlobalStats *s) { + stats_.Init(); + if (s) + s->Register(&stats_); + } + + void Destroy(SizeClassAllocator *allocator, AllocatorGlobalStats *s) { + Drain(allocator); + if (s) + s->Unregister(&stats_); } void *Allocate(SizeClassAllocator *allocator, uptr class_id) { CHECK_NE(class_id, 0UL); CHECK_LT(class_id, kNumClasses); + stats_.Add(AllocatorStatMalloced, SizeClassMap::Size(class_id)); PerClass *c = &per_class_[class_id]; if (UNLIKELY(c->count == 0)) Refill(allocator, class_id); @@ -651,6 +788,7 @@ struct SizeClassAllocatorLocalCache { void Deallocate(SizeClassAllocator *allocator, uptr class_id, void *p) { CHECK_NE(class_id, 0UL); CHECK_LT(class_id, kNumClasses); + stats_.Add(AllocatorStatFreed, SizeClassMap::Size(class_id)); PerClass *c = &per_class_[class_id]; if (UNLIKELY(c->count == c->max_count)) Drain(allocator, class_id); @@ -674,6 +812,7 @@ struct SizeClassAllocatorLocalCache { void *batch[2 * SizeClassMap::kMaxNumCached]; }; PerClass per_class_[kNumClasses]; + AllocatorStats stats_; void InitCache() { if (per_class_[0].max_count) @@ -684,10 +823,11 @@ struct SizeClassAllocatorLocalCache { } } - void NOINLINE Refill(SizeClassAllocator *allocator, uptr class_id) { + NOINLINE void Refill(SizeClassAllocator *allocator, uptr class_id) { InitCache(); PerClass *c = &per_class_[class_id]; - Batch *b = allocator->AllocateBatch(this, class_id); + Batch *b = allocator->AllocateBatch(&stats_, this, class_id); + CHECK_GT(b->count, 0); for (uptr i = 0; i < b->count; i++) c->batch[i] = b->batch[i]; c->count = b->count; @@ -695,7 +835,7 @@ struct SizeClassAllocatorLocalCache { Deallocate(allocator, SizeClassMap::ClassID(sizeof(Batch)), b); } - void NOINLINE Drain(SizeClassAllocator *allocator, uptr class_id) { + NOINLINE void Drain(SizeClassAllocator *allocator, uptr class_id) { InitCache(); PerClass *c = &per_class_[class_id]; Batch *b; @@ -710,7 +850,7 @@ struct SizeClassAllocatorLocalCache { } b->count = cnt; c->count -= cnt; - allocator->DeallocateBatch(class_id, b); + allocator->DeallocateBatch(&stats_, class_id, b); } }; @@ -725,7 +865,7 @@ class LargeMmapAllocator { page_size_ = GetPageSizeCached(); } - void *Allocate(uptr size, uptr alignment) { + void *Allocate(AllocatorStats *stat, uptr size, uptr alignment) { CHECK(IsPowerOfTwo(alignment)); uptr map_size = RoundUpMapSize(size); if (alignment > page_size_) @@ -744,7 +884,7 @@ class LargeMmapAllocator { h->size = size; h->map_beg = map_beg; h->map_size = map_size; - uptr size_log = SANITIZER_WORDSIZE - __builtin_clzl(map_size) - 1; + uptr size_log = MostSignificantSetBitIndex(map_size); CHECK_LT(size_log, ARRAY_SIZE(stats.by_size_log)); { SpinMutexLock l(&mutex_); @@ -756,11 +896,13 @@ class LargeMmapAllocator { stats.currently_allocated += map_size; stats.max_allocated = Max(stats.max_allocated, stats.currently_allocated); stats.by_size_log[size_log]++; + stat->Add(AllocatorStatMalloced, map_size); + stat->Add(AllocatorStatMmapped, map_size); } return reinterpret_cast<void*>(res); } - void Deallocate(void *p) { + void Deallocate(AllocatorStats *stat, void *p) { Header *h = GetHeader(p); { SpinMutexLock l(&mutex_); @@ -772,6 +914,8 @@ class LargeMmapAllocator { n_chunks_--; stats.n_frees++; stats.currently_allocated -= h->map_size; + stat->Add(AllocatorStatFreed, h->map_size); + stat->Add(AllocatorStatUnmapped, h->map_size); } MapUnmapCallback().OnUnmap(h->map_beg, h->map_size); UnmapOrDie(reinterpret_cast<void*>(h->map_beg), h->map_size); @@ -838,6 +982,16 @@ class LargeMmapAllocator { Printf("\n"); } + // ForceLock() and ForceUnlock() are needed to implement Darwin malloc zone + // introspection API. + void ForceLock() { + mutex_.Lock(); + } + + void ForceUnlock() { + mutex_.Unlock(); + } + private: static const int kMaxNumChunks = 1 << FIRST_32_SECOND_64(15, 18); struct Header { @@ -884,6 +1038,7 @@ class CombinedAllocator { void Init() { primary_.Init(); secondary_.Init(); + stats_.Init(); } void *Allocate(AllocatorCache *cache, uptr size, uptr alignment, @@ -899,7 +1054,7 @@ class CombinedAllocator { if (primary_.CanAllocate(size, alignment)) res = cache->Allocate(&primary_, primary_.ClassID(size)); else - res = secondary_.Allocate(size, alignment); + res = secondary_.Allocate(&stats_, size, alignment); if (alignment > 8) CHECK_EQ(reinterpret_cast<uptr>(res) & (alignment - 1), 0); if (cleared && res) @@ -912,7 +1067,7 @@ class CombinedAllocator { if (primary_.PointerIsMine(p)) cache->Deallocate(&primary_, primary_.GetSizeClass(p), p); else - secondary_.Deallocate(p); + secondary_.Deallocate(&stats_, p); } void *Reallocate(AllocatorCache *cache, void *p, uptr new_size, @@ -967,20 +1122,48 @@ class CombinedAllocator { void TestOnlyUnmap() { primary_.TestOnlyUnmap(); } + void InitCache(AllocatorCache *cache) { + cache->Init(&stats_); + } + + void DestroyCache(AllocatorCache *cache) { + cache->Destroy(&primary_, &stats_); + } + void SwallowCache(AllocatorCache *cache) { cache->Drain(&primary_); } + void GetStats(AllocatorStatCounters s) const { + stats_.Get(s); + } + void PrintStats() { primary_.PrintStats(); secondary_.PrintStats(); } + // ForceLock() and ForceUnlock() are needed to implement Darwin malloc zone + // introspection API. + void ForceLock() { + primary_.ForceLock(); + secondary_.ForceLock(); + } + + void ForceUnlock() { + secondary_.ForceUnlock(); + primary_.ForceUnlock(); + } + private: PrimaryAllocator primary_; SecondaryAllocator secondary_; + AllocatorGlobalStats stats_; }; +// Returns true if calloc(size, n) should return 0 due to overflow in size*n. +bool CallocShouldReturnNullDueToOverflow(uptr size, uptr n); + } // namespace __sanitizer #endif // SANITIZER_ALLOCATOR_H diff --git a/libsanitizer/sanitizer_common/sanitizer_common.cc b/libsanitizer/sanitizer_common/sanitizer_common.cc index 9b70ee0eb54..4447c346eba 100644 --- a/libsanitizer/sanitizer_common/sanitizer_common.cc +++ b/libsanitizer/sanitizer_common/sanitizer_common.cc @@ -14,6 +14,8 @@ namespace __sanitizer { +const char *SanitizerToolName = "SanitizerTool"; + uptr GetPageSizeCached() { static uptr PageSize; if (!PageSize) @@ -64,7 +66,7 @@ static void MaybeOpenReportFile() { InternalScopedBuffer<char> report_path_full(4096); internal_snprintf(report_path_full.data(), report_path_full.size(), "%s.%d", report_path_prefix, GetPid()); - fd_t fd = internal_open(report_path_full.data(), true); + fd_t fd = OpenFile(report_path_full.data(), true); if (fd == kInvalidFd) { report_fd = kStderrFd; log_to_file = false; @@ -103,7 +105,7 @@ uptr ReadFileToBuffer(const char *file_name, char **buff, *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) { - fd_t fd = internal_open(file_name, /*write*/ false); + fd_t fd = OpenFile(file_name, /*write*/ false); if (fd == kInvalidFd) return 0; UnmapOrDie(*buff, *buff_size); *buff = (char*)MmapOrDie(size, __FUNCTION__); @@ -188,6 +190,16 @@ void *MmapAlignedOrDie(uptr size, uptr alignment, const char *mem_type) { return (void*)res; } +void ReportErrorSummary(const char *error_type, const char *file, + int line, const char *function) { + const int kMaxSize = 1024; // We don't want a summary too long. + InternalScopedBuffer<char> buff(kMaxSize); + internal_snprintf(buff.data(), kMaxSize, "%s: %s %s:%d %s", + SanitizerToolName, error_type, + file ? file : "??", line, function ? function : "??"); + __sanitizer_report_error_summary(buff.data()); +} + } // namespace __sanitizer using namespace __sanitizer; // NOLINT @@ -220,4 +232,8 @@ void NOINLINE __sanitizer_sandbox_on_notify(void *reserved) { (void)reserved; PrepareForSandboxing(); } + +void __sanitizer_report_error_summary(const char *error_summary) { + Printf("SUMMARY: %s\n", error_summary); +} } // extern "C" diff --git a/libsanitizer/sanitizer_common/sanitizer_common.h b/libsanitizer/sanitizer_common/sanitizer_common.h index 6b104884342..109966ba360 100644 --- a/libsanitizer/sanitizer_common/sanitizer_common.h +++ b/libsanitizer/sanitizer_common/sanitizer_common.h @@ -17,6 +17,7 @@ #include "sanitizer_internal_defs.h" namespace __sanitizer { +struct StackTrace; // Constants. const uptr kWordSize = SANITIZER_WORDSIZE / 8; @@ -28,6 +29,8 @@ const uptr kCacheLineSize = 128; const uptr kCacheLineSize = 64; #endif +extern const char *SanitizerToolName; // Can be changed by the tool. + uptr GetPageSize(); uptr GetPageSizeCached(); uptr GetMmapGranularity(); @@ -103,6 +106,7 @@ void Printf(const char *format, ...); void Report(const char *format, ...); void SetPrintfAndReportCallback(void (*callback)(const char *)); +fd_t OpenFile(const char *filename, bool write); // 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', @@ -152,20 +156,79 @@ typedef void (*CheckFailedCallbackType)(const char *, int, const char *, u64, u64); void SetCheckFailedCallback(CheckFailedCallbackType callback); +// Construct a one-line string like +// SanitizerToolName: error_type file:line function +// and call __sanitizer_report_error_summary on it. +void ReportErrorSummary(const char *error_type, const char *file, + int line, const char *function); + // Math +#if defined(_WIN32) && !defined(__clang__) +extern "C" { +unsigned char _BitScanForward(unsigned long *index, unsigned long mask); // NOLINT +unsigned char _BitScanReverse(unsigned long *index, unsigned long mask); // NOLINT +#if defined(_WIN64) +unsigned char _BitScanForward64(unsigned long *index, unsigned __int64 mask); // NOLINT +unsigned char _BitScanReverse64(unsigned long *index, unsigned __int64 mask); // NOLINT +#endif +} +#endif + +INLINE uptr MostSignificantSetBitIndex(uptr x) { + CHECK(x != 0); + unsigned long up; // NOLINT +#if !defined(_WIN32) || defined(__clang__) + up = SANITIZER_WORDSIZE - 1 - __builtin_clzl(x); +#elif defined(_WIN64) + _BitScanReverse64(&up, x); +#else + _BitScanReverse(&up, x); +#endif + return up; +} + INLINE bool IsPowerOfTwo(uptr x) { return (x & (x - 1)) == 0; } + +INLINE uptr RoundUpToPowerOfTwo(uptr size) { + CHECK(size); + if (IsPowerOfTwo(size)) return size; + + uptr up = MostSignificantSetBitIndex(size); + CHECK(size < (1ULL << (up + 1))); + CHECK(size > (1ULL << up)); + return 1UL << (up + 1); +} + INLINE uptr RoundUpTo(uptr size, uptr boundary) { CHECK(IsPowerOfTwo(boundary)); return (size + boundary - 1) & ~(boundary - 1); } + INLINE uptr RoundDownTo(uptr x, uptr boundary) { return x & ~(boundary - 1); } + INLINE bool IsAligned(uptr a, uptr alignment) { return (a & (alignment - 1)) == 0; } + +INLINE uptr Log2(uptr x) { + CHECK(IsPowerOfTwo(x)); +#if !defined(_WIN32) || defined(__clang__) + 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 +} + // Don't use std::min, std::max or std::swap, to minimize dependency // on libstdc++. template<class T> T Min(T a, T b) { return a < b ? a : b; } diff --git a/libsanitizer/sanitizer_common/sanitizer_common_interceptors.inc b/libsanitizer/sanitizer_common/sanitizer_common_interceptors.inc index 724a326ef4c..4ba7b8fee9f 100644 --- a/libsanitizer/sanitizer_common/sanitizer_common_interceptors.inc +++ b/libsanitizer/sanitizer_common/sanitizer_common_interceptors.inc @@ -22,9 +22,13 @@ #include <stdarg.h> +#ifdef _WIN32 +#define va_copy(dst, src) ((dst) = (src)) +#endif // _WIN32 + #if SANITIZER_INTERCEPT_READ INTERCEPTOR(SSIZE_T, read, int fd, void *ptr, SIZE_T count) { - void* ctx; + void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, read, fd, ptr, count); SSIZE_T res = REAL(read)(fd, ptr, count); if (res > 0) @@ -33,14 +37,14 @@ INTERCEPTOR(SSIZE_T, read, int fd, void *ptr, SIZE_T count) { COMMON_INTERCEPTOR_FD_ACQUIRE(ctx, fd); return res; } -# define INIT_READ INTERCEPT_FUNCTION(read) +#define INIT_READ INTERCEPT_FUNCTION(read) #else -# define INIT_READ +#define INIT_READ #endif #if SANITIZER_INTERCEPT_PREAD INTERCEPTOR(SSIZE_T, pread, int fd, void *ptr, SIZE_T count, OFF_T offset) { - void* ctx; + void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, pread, fd, ptr, count, offset); SSIZE_T res = REAL(pread)(fd, ptr, count, offset); if (res > 0) @@ -49,14 +53,14 @@ INTERCEPTOR(SSIZE_T, pread, int fd, void *ptr, SIZE_T count, OFF_T offset) { COMMON_INTERCEPTOR_FD_ACQUIRE(ctx, fd); return res; } -# define INIT_PREAD INTERCEPT_FUNCTION(pread) +#define INIT_PREAD INTERCEPT_FUNCTION(pread) #else -# define INIT_PREAD +#define INIT_PREAD #endif #if SANITIZER_INTERCEPT_PREAD64 INTERCEPTOR(SSIZE_T, pread64, int fd, void *ptr, SIZE_T count, OFF64_T offset) { - void* ctx; + void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, pread64, fd, ptr, count, offset); SSIZE_T res = REAL(pread64)(fd, ptr, count, offset); if (res > 0) @@ -65,14 +69,14 @@ INTERCEPTOR(SSIZE_T, pread64, int fd, void *ptr, SIZE_T count, OFF64_T offset) { COMMON_INTERCEPTOR_FD_ACQUIRE(ctx, fd); return res; } -# define INIT_PREAD64 INTERCEPT_FUNCTION(pread64) +#define INIT_PREAD64 INTERCEPT_FUNCTION(pread64) #else -# define INIT_PREAD64 +#define INIT_PREAD64 #endif #if SANITIZER_INTERCEPT_WRITE INTERCEPTOR(SSIZE_T, write, int fd, void *ptr, SIZE_T count) { - void* ctx; + void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, write, fd, ptr, count); if (fd >= 0) COMMON_INTERCEPTOR_FD_RELEASE(ctx, fd); @@ -81,142 +85,154 @@ INTERCEPTOR(SSIZE_T, write, int fd, void *ptr, SIZE_T count) { COMMON_INTERCEPTOR_READ_RANGE(ctx, ptr, res); return res; } -# define INIT_WRITE INTERCEPT_FUNCTION(write) +#define INIT_WRITE INTERCEPT_FUNCTION(write) #else -# define INIT_WRITE +#define INIT_WRITE #endif #if SANITIZER_INTERCEPT_PWRITE -INTERCEPTOR(SSIZE_T, pwrite, int fd, void *ptr, SIZE_T count) { - void* ctx; - COMMON_INTERCEPTOR_ENTER(ctx, pwrite, fd, ptr, count); +INTERCEPTOR(SSIZE_T, pwrite, int fd, void *ptr, SIZE_T count, OFF_T offset) { + void *ctx; + COMMON_INTERCEPTOR_ENTER(ctx, pwrite, fd, ptr, count, offset); if (fd >= 0) COMMON_INTERCEPTOR_FD_RELEASE(ctx, fd); - SSIZE_T res = REAL(pwrite)(fd, ptr, count); + SSIZE_T res = REAL(pwrite)(fd, ptr, count, offset); if (res > 0) COMMON_INTERCEPTOR_READ_RANGE(ctx, ptr, res); return res; } -# define INIT_PWRITE INTERCEPT_FUNCTION(pwrite) +#define INIT_PWRITE INTERCEPT_FUNCTION(pwrite) #else -# define INIT_PWRITE +#define INIT_PWRITE #endif #if SANITIZER_INTERCEPT_PWRITE64 -INTERCEPTOR(SSIZE_T, pwrite64, int fd, void *ptr, OFF64_T count) { - void* ctx; - COMMON_INTERCEPTOR_ENTER(ctx, pwrite64, fd, ptr, count); +INTERCEPTOR(SSIZE_T, pwrite64, int fd, void *ptr, OFF64_T count, + OFF64_T offset) { + void *ctx; + COMMON_INTERCEPTOR_ENTER(ctx, pwrite64, fd, ptr, count, offset); if (fd >= 0) COMMON_INTERCEPTOR_FD_RELEASE(ctx, fd); - SSIZE_T res = REAL(pwrite64)(fd, ptr, count); + SSIZE_T res = REAL(pwrite64)(fd, ptr, count, offset); if (res > 0) COMMON_INTERCEPTOR_READ_RANGE(ctx, ptr, res); return res; } -# define INIT_PWRITE64 INTERCEPT_FUNCTION(pwrite64) +#define INIT_PWRITE64 INTERCEPT_FUNCTION(pwrite64) #else -# define INIT_PWRITE64 +#define INIT_PWRITE64 #endif #if SANITIZER_INTERCEPT_PRCTL -INTERCEPTOR(int, prctl, int option, - unsigned long arg2, unsigned long arg3, // NOLINT - unsigned long arg4, unsigned long arg5) { // NOLINT - void* ctx; +INTERCEPTOR(int, prctl, int option, unsigned long arg2, + unsigned long arg3, // NOLINT + unsigned long arg4, unsigned long arg5) { // NOLINT + void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, prctl, option, arg2, arg3, arg4, arg5); static const int PR_SET_NAME = 15; int res = REAL(prctl(option, arg2, arg3, arg4, arg5)); if (option == PR_SET_NAME) { char buff[16]; - internal_strncpy(buff, (char*)arg2, 15); + internal_strncpy(buff, (char *)arg2, 15); buff[15] = 0; COMMON_INTERCEPTOR_SET_THREAD_NAME(ctx, buff); } return res; } -# define INIT_PRCTL INTERCEPT_FUNCTION(prctl) +#define INIT_PRCTL INTERCEPT_FUNCTION(prctl) #else -# define INIT_PRCTL -#endif // SANITIZER_INTERCEPT_PRCTL - +#define INIT_PRCTL +#endif // SANITIZER_INTERCEPT_PRCTL #if SANITIZER_INTERCEPT_SCANF #include "sanitizer_common_interceptors_scanf.inc" -INTERCEPTOR(int, vscanf, const char *format, va_list ap) { // NOLINT - void* ctx; - COMMON_INTERCEPTOR_ENTER(ctx, vscanf, format, ap); - scanf_common(ctx, format, ap); - int res = REAL(vscanf)(format, ap); // NOLINT - return res; -} +#define VSCANF_INTERCEPTOR_IMPL(vname, allowGnuMalloc, ...) \ + { \ + void *ctx; \ + COMMON_INTERCEPTOR_ENTER(ctx, vname, __VA_ARGS__); \ + va_list aq; \ + va_copy(aq, ap); \ + int res = REAL(vname)(__VA_ARGS__); \ + if (res > 0) \ + scanf_common(ctx, res, allowGnuMalloc, format, aq); \ + va_end(aq); \ + return res; \ + } -INTERCEPTOR(int, vsscanf, const char *str, const char *format, // NOLINT - va_list ap) { - void* ctx; - COMMON_INTERCEPTOR_ENTER(ctx, vsscanf, str, format, ap); - scanf_common(ctx, format, ap); - int res = REAL(vsscanf)(str, format, ap); // NOLINT - // FIXME: read of str - return res; -} +INTERCEPTOR(int, vscanf, const char *format, va_list ap) +VSCANF_INTERCEPTOR_IMPL(vscanf, true, format, ap) -INTERCEPTOR(int, vfscanf, void *stream, const char *format, // NOLINT - va_list ap) { - void* ctx; - COMMON_INTERCEPTOR_ENTER(ctx, vfscanf, stream, format, ap); - scanf_common(ctx, format, ap); - int res = REAL(vfscanf)(stream, format, ap); // NOLINT - return res; -} +INTERCEPTOR(int, vsscanf, const char *str, const char *format, va_list ap) +VSCANF_INTERCEPTOR_IMPL(vsscanf, true, str, format, ap) -INTERCEPTOR(int, scanf, const char *format, ...) { // NOLINT - void* ctx; - COMMON_INTERCEPTOR_ENTER(ctx, scanf, format); - va_list ap; - va_start(ap, format); - int res = vscanf(format, ap); // NOLINT - va_end(ap); - return res; -} +INTERCEPTOR(int, vfscanf, void *stream, const char *format, va_list ap) +VSCANF_INTERCEPTOR_IMPL(vfscanf, true, stream, format, ap) -INTERCEPTOR(int, fscanf, void* stream, const char *format, ...) { // NOLINT - void* ctx; - COMMON_INTERCEPTOR_ENTER(ctx, fscanf, stream, format); - va_list ap; - va_start(ap, format); - int res = vfscanf(stream, format, ap); // NOLINT - va_end(ap); - return res; -} +INTERCEPTOR(int, __isoc99_vscanf, const char *format, va_list ap) +VSCANF_INTERCEPTOR_IMPL(__isoc99_vscanf, false, format, ap) -INTERCEPTOR(int, sscanf, const char *str, const char *format, ...) { // NOLINT - void* ctx; - COMMON_INTERCEPTOR_ENTER(ctx, sscanf, str, format); // NOLINT - va_list ap; - va_start(ap, format); - int res = vsscanf(str, format, ap); // NOLINT - va_end(ap); - return res; -} +INTERCEPTOR(int, __isoc99_vsscanf, const char *str, const char *format, + va_list ap) +VSCANF_INTERCEPTOR_IMPL(__isoc99_vsscanf, false, str, format, ap) + +INTERCEPTOR(int, __isoc99_vfscanf, void *stream, const char *format, va_list ap) +VSCANF_INTERCEPTOR_IMPL(__isoc99_vfscanf, false, stream, format, ap) + +#define SCANF_INTERCEPTOR_IMPL(name, vname, ...) \ + { \ + void *ctx; \ + COMMON_INTERCEPTOR_ENTER(ctx, name, __VA_ARGS__); \ + va_list ap; \ + va_start(ap, format); \ + int res = vname(__VA_ARGS__, ap); \ + va_end(ap); \ + return res; \ + } + +INTERCEPTOR(int, scanf, const char *format, ...) +SCANF_INTERCEPTOR_IMPL(scanf, vscanf, format) + +INTERCEPTOR(int, fscanf, void *stream, const char *format, ...) +SCANF_INTERCEPTOR_IMPL(fscanf, vfscanf, stream, format) + +INTERCEPTOR(int, sscanf, const char *str, const char *format, ...) +SCANF_INTERCEPTOR_IMPL(sscanf, vsscanf, str, format) + +INTERCEPTOR(int, __isoc99_scanf, const char *format, ...) +SCANF_INTERCEPTOR_IMPL(__isoc99_scanf, __isoc99_vscanf, format) + +INTERCEPTOR(int, __isoc99_fscanf, void *stream, const char *format, ...) +SCANF_INTERCEPTOR_IMPL(__isoc99_fscanf, __isoc99_vfscanf, stream, format) + +INTERCEPTOR(int, __isoc99_sscanf, const char *str, const char *format, ...) +SCANF_INTERCEPTOR_IMPL(__isoc99_sscanf, __isoc99_vsscanf, str, format) -#define INIT_SCANF \ - INTERCEPT_FUNCTION(scanf); \ - INTERCEPT_FUNCTION(sscanf); /* NOLINT */ \ - INTERCEPT_FUNCTION(fscanf); \ - INTERCEPT_FUNCTION(vscanf); \ - INTERCEPT_FUNCTION(vsscanf); \ - INTERCEPT_FUNCTION(vfscanf) +#define INIT_SCANF \ + INTERCEPT_FUNCTION(scanf); \ + INTERCEPT_FUNCTION(sscanf); \ + INTERCEPT_FUNCTION(fscanf); \ + INTERCEPT_FUNCTION(vscanf); \ + INTERCEPT_FUNCTION(vsscanf); \ + INTERCEPT_FUNCTION(vfscanf); \ + INTERCEPT_FUNCTION(__isoc99_scanf); \ + INTERCEPT_FUNCTION(__isoc99_sscanf); \ + INTERCEPT_FUNCTION(__isoc99_fscanf); \ + INTERCEPT_FUNCTION(__isoc99_vscanf); \ + INTERCEPT_FUNCTION(__isoc99_vsscanf); \ + INTERCEPT_FUNCTION(__isoc99_vfscanf); #else #define INIT_SCANF #endif -#define SANITIZER_COMMON_INTERCEPTORS_INIT \ - INIT_READ; \ - INIT_PREAD; \ - INIT_PREAD64; \ - INIT_PRCTL; \ - INIT_WRITE; \ +#define SANITIZER_COMMON_INTERCEPTORS_INIT \ + INIT_READ; \ + INIT_PREAD; \ + INIT_PREAD64; \ + INIT_PRCTL; \ + INIT_WRITE; \ + INIT_PWRITE; \ + INIT_PWRITE64; \ INIT_SCANF; diff --git a/libsanitizer/sanitizer_common/sanitizer_common_interceptors_scanf.inc b/libsanitizer/sanitizer_common/sanitizer_common_interceptors_scanf.inc index f7cab5f0dbb..5b761382d3e 100644 --- a/libsanitizer/sanitizer_common/sanitizer_common_interceptors_scanf.inc +++ b/libsanitizer/sanitizer_common/sanitizer_common_interceptors_scanf.inc @@ -6,87 +6,41 @@ //===----------------------------------------------------------------------===// // // Scanf implementation for use in *Sanitizer interceptors. +// Follows http://pubs.opengroup.org/onlinepubs/9699919799/functions/fscanf.html +// with a few common GNU extensions. // //===----------------------------------------------------------------------===// #include <stdarg.h> -#ifdef _WIN32 -#define va_copy(dst, src) ((dst) = (src)) -#endif // _WIN32 - -struct ScanfSpec { - char c; - unsigned size; -}; - -// One-letter specs. -static const ScanfSpec scanf_specs[] = { - {'p', sizeof(void *)}, - {'e', sizeof(float)}, - {'E', sizeof(float)}, - {'a', sizeof(float)}, - {'f', sizeof(float)}, - {'g', sizeof(float)}, - {'d', sizeof(int)}, - {'i', sizeof(int)}, - {'o', sizeof(int)}, - {'u', sizeof(int)}, - {'x', sizeof(int)}, - {'X', sizeof(int)}, - {'n', sizeof(int)}, - {'t', sizeof(PTRDIFF_T)}, - {'z', sizeof(SIZE_T)}, - {'j', sizeof(INTMAX_T)}, - {'h', sizeof(short)} -}; - -static const unsigned scanf_specs_cnt = - sizeof(scanf_specs) / sizeof(scanf_specs[0]); - -// %ll?, %L?, %q? specs -static const ScanfSpec scanf_llspecs[] = { - {'e', sizeof(long double)}, - {'f', sizeof(long double)}, - {'g', sizeof(long double)}, - {'d', sizeof(long long)}, - {'i', sizeof(long long)}, - {'o', sizeof(long long)}, - {'u', sizeof(long long)}, - {'x', sizeof(long long)} +struct ScanfDirective { + int argIdx; // argument index, or -1 of not specified ("%n$") + int fieldWidth; + bool suppressed; // suppress assignment ("*") + bool allocate; // allocate space ("m") + char lengthModifier[2]; + char convSpecifier; + bool maybeGnuMalloc; }; -static const unsigned scanf_llspecs_cnt = - sizeof(scanf_llspecs) / sizeof(scanf_llspecs[0]); - -// %l? specs -static const ScanfSpec scanf_lspecs[] = { - {'e', sizeof(double)}, - {'f', sizeof(double)}, - {'g', sizeof(double)}, - {'d', sizeof(long)}, - {'i', sizeof(long)}, - {'o', sizeof(long)}, - {'u', sizeof(long)}, - {'x', sizeof(long)}, - {'X', sizeof(long)}, -}; - -static const unsigned scanf_lspecs_cnt = - sizeof(scanf_lspecs) / sizeof(scanf_lspecs[0]); - -static unsigned match_spec(const struct ScanfSpec *spec, unsigned n, char c) { - for (unsigned i = 0; i < n; ++i) - if (spec[i].c == c) - return spec[i].size; - return 0; +static const char *parse_number(const char *p, int *out) { + *out = internal_atoll(p); + while (*p >= '0' && *p <= '9') + ++p; + return p; } -static void scanf_common(void *ctx, const char *format, va_list ap_const) { - va_list aq; - va_copy(aq, ap_const); +static bool char_is_one_of(char c, const char *s) { + return !!internal_strchr(s, c); +} - const char *p = format; - unsigned size; +// Parse scanf format string. If a valid directive in encountered, it is +// returned in dir. This function returns the pointer to the first +// unprocessed character, or 0 in case of error. +// In case of the end-of-string, a pointer to the closing \0 is returned. +static const char *scanf_parse_next(const char *p, bool allowGnuMalloc, + ScanfDirective *dir) { + internal_memset(dir, 0, sizeof(*dir)); + dir->argIdx = -1; while (*p) { if (*p != '%') { @@ -94,51 +48,260 @@ static void scanf_common(void *ctx, const char *format, va_list ap_const) { continue; } ++p; - if (*p == '*' || *p == '%' || *p == 0) { + // %% + if (*p == '%') { ++p; continue; } - if (*p == '0' || (*p >= '1' && *p <= '9')) { - size = internal_atoll(p); - // +1 for the \0 at the end - COMMON_INTERCEPTOR_WRITE_RANGE(ctx, va_arg(aq, void *), size + 1); + if (*p == '\0') { + return 0; + } + // %n$ + if (*p >= '0' && *p <= '9') { + int number; + const char *q = parse_number(p, &number); + if (*q == '$') { + dir->argIdx = number; + p = q + 1; + } + // Otherwise, do not change p. This will be re-parsed later as the field + // width. + } + // * + if (*p == '*') { + dir->suppressed = true; ++p; - continue; } - - if (*p == 'L' || *p == 'q') { + // Field width. + if (*p >= '0' && *p <= '9') { + p = parse_number(p, &dir->fieldWidth); + if (dir->fieldWidth <= 0) + return 0; + } + // m + if (*p == 'm') { + dir->allocate = true; ++p; - size = match_spec(scanf_llspecs, scanf_llspecs_cnt, *p); - COMMON_INTERCEPTOR_WRITE_RANGE(ctx, va_arg(aq, void *), size); - continue; } - - if (*p == 'l') { + // Length modifier. + if (char_is_one_of(*p, "jztLq")) { + dir->lengthModifier[0] = *p; + ++p; + } else if (*p == 'h') { + dir->lengthModifier[0] = 'h'; + ++p; + if (*p == 'h') { + dir->lengthModifier[1] = 'h'; + ++p; + } + } else if (*p == 'l') { + dir->lengthModifier[0] = 'l'; ++p; if (*p == 'l') { + dir->lengthModifier[1] = 'l'; ++p; - size = match_spec(scanf_llspecs, scanf_llspecs_cnt, *p); - COMMON_INTERCEPTOR_WRITE_RANGE(ctx, va_arg(aq, void *), size); - continue; - } else { - size = match_spec(scanf_lspecs, scanf_lspecs_cnt, *p); - COMMON_INTERCEPTOR_WRITE_RANGE(ctx, va_arg(aq, void *), size); - continue; } } + // Conversion specifier. + dir->convSpecifier = *p++; + // Consume %[...] expression. + if (dir->convSpecifier == '[') { + if (*p == '^') + ++p; + if (*p == ']') + ++p; + while (*p && *p != ']') + ++p; + if (*p == 0) + return 0; // unexpected end of string + // Consume the closing ']'. + ++p; + } + // This is unfortunately ambiguous between old GNU extension + // of %as, %aS and %a[...] and newer POSIX %a followed by + // letters s, S or [. + if (allowGnuMalloc && dir->convSpecifier == 'a' && + !dir->lengthModifier[0]) { + if (*p == 's' || *p == 'S') { + dir->maybeGnuMalloc = true; + ++p; + } else if (*p == '[') { + // Watch for %a[h-j%d], if % appears in the + // [...] range, then we need to give up, we don't know + // if scanf will parse it as POSIX %a [h-j %d ] or + // GNU allocation of string with range dh-j plus %. + const char *q = p + 1; + if (*q == '^') + ++q; + if (*q == ']') + ++q; + while (*q && *q != ']' && *q != '%') + ++q; + if (*q == 0 || *q == '%') + return 0; + p = q + 1; // Consume the closing ']'. + dir->maybeGnuMalloc = true; + } + } + break; + } + return p; +} - if (*p == 'h' && *(p + 1) == 'h') { - COMMON_INTERCEPTOR_WRITE_RANGE(ctx, va_arg(aq, void *), sizeof(char)); - p += 2; - continue; +// Returns true if the character is an integer conversion specifier. +static bool scanf_is_integer_conv(char c) { + return char_is_one_of(c, "diouxXn"); +} + +// Returns true if the character is an floating point conversion specifier. +static bool scanf_is_float_conv(char c) { + return char_is_one_of(c, "aAeEfFgG"); +} + +// Returns string output character size for string-like conversions, +// or 0 if the conversion is invalid. +static int scanf_get_char_size(ScanfDirective *dir) { + if (char_is_one_of(dir->convSpecifier, "CS")) { + // wchar_t + return 0; + } + + if (char_is_one_of(dir->convSpecifier, "cs[")) { + if (dir->lengthModifier[0] == 'l') + // wchar_t + return 0; + else if (dir->lengthModifier[0] == 0) + return sizeof(char); + else + return 0; + } + + return 0; +} + +enum ScanfStoreSize { + // Store size not known in advance; can be calculated as strlen() of the + // destination buffer. + SSS_STRLEN = -1, + // Invalid conversion specifier. + SSS_INVALID = 0 +}; + +// Returns the store size of a scanf directive (if >0), or a value of +// ScanfStoreSize. +static int scanf_get_store_size(ScanfDirective *dir) { + if (dir->allocate) { + if (!char_is_one_of(dir->convSpecifier, "cCsS[")) + return SSS_INVALID; + return sizeof(char *); + } + + if (dir->maybeGnuMalloc) { + if (dir->convSpecifier != 'a' || dir->lengthModifier[0]) + return SSS_INVALID; + // This is ambiguous, so check the smaller size of char * (if it is + // a GNU extension of %as, %aS or %a[...]) and float (if it is + // POSIX %a followed by s, S or [ letters). + return sizeof(char *) < sizeof(float) ? sizeof(char *) : sizeof(float); + } + + if (scanf_is_integer_conv(dir->convSpecifier)) { + switch (dir->lengthModifier[0]) { + case 'h': + return dir->lengthModifier[1] == 'h' ? sizeof(char) : sizeof(short); + case 'l': + return dir->lengthModifier[1] == 'l' ? sizeof(long long) : sizeof(long); + case 'L': + return sizeof(long long); + case 'j': + return sizeof(INTMAX_T); + case 'z': + return sizeof(SIZE_T); + case 't': + return sizeof(PTRDIFF_T); + case 0: + return sizeof(int); + default: + return SSS_INVALID; } + } - size = match_spec(scanf_specs, scanf_specs_cnt, *p); - if (size) { - COMMON_INTERCEPTOR_WRITE_RANGE(ctx, va_arg(aq, void *), size); - ++p; + if (scanf_is_float_conv(dir->convSpecifier)) { + switch (dir->lengthModifier[0]) { + case 'L': + case 'q': + return sizeof(long double); + case 'l': + return dir->lengthModifier[1] == 'l' ? sizeof(long double) + : sizeof(double); + case 0: + return sizeof(float); + default: + return SSS_INVALID; + } + } + + if (char_is_one_of(dir->convSpecifier, "sS[")) { + unsigned charSize = scanf_get_char_size(dir); + if (charSize == 0) + return SSS_INVALID; + if (dir->fieldWidth == 0) + return SSS_STRLEN; + return (dir->fieldWidth + 1) * charSize; + } + + if (char_is_one_of(dir->convSpecifier, "cC")) { + unsigned charSize = scanf_get_char_size(dir); + if (charSize == 0) + return SSS_INVALID; + if (dir->fieldWidth == 0) + return charSize; + return dir->fieldWidth * charSize; + } + + if (dir->convSpecifier == 'p') { + if (dir->lengthModifier[1] != 0) + return SSS_INVALID; + return sizeof(void *); + } + + return SSS_INVALID; +} + +// Common part of *scanf interceptors. +// Process format string and va_list, and report all store ranges. +// Stops when "consuming" n_inputs input items. +static void scanf_common(void *ctx, int n_inputs, bool allowGnuMalloc, + const char *format, va_list aq) { + CHECK_GT(n_inputs, 0); + const char *p = format; + + while (*p && n_inputs) { + ScanfDirective dir; + p = scanf_parse_next(p, allowGnuMalloc, &dir); + if (!p) + break; + if (dir.convSpecifier == 0) { + // This can only happen at the end of the format string. + CHECK_EQ(*p, 0); + break; + } + // Here the directive is valid. Do what it says. + if (dir.argIdx != -1) { + // Unsupported. + break; + } + if (dir.suppressed) continue; + int size = scanf_get_store_size(&dir); + if (size == SSS_INVALID) + break; + void *argp = va_arg(aq, void *); + if (dir.convSpecifier != 'n') + --n_inputs; + if (size == SSS_STRLEN) { + size = internal_strlen((const char *)argp) + 1; } + COMMON_INTERCEPTOR_WRITE_RANGE(ctx, argp, size); } - va_end(aq); } diff --git a/libsanitizer/sanitizer_common/sanitizer_internal_defs.h b/libsanitizer/sanitizer_common/sanitizer_internal_defs.h index 01f08f57801..1a25b70c23e 100644 --- a/libsanitizer/sanitizer_common/sanitizer_internal_defs.h +++ b/libsanitizer/sanitizer_common/sanitizer_internal_defs.h @@ -11,7 +11,85 @@ #ifndef SANITIZER_DEFS_H #define SANITIZER_DEFS_H -#include "sanitizer/common_interface_defs.h" +#if defined(_WIN32) +// FIXME find out what we need on Windows. __declspec(dllexport) ? +# define SANITIZER_INTERFACE_ATTRIBUTE +# define SANITIZER_WEAK_ATTRIBUTE +#elif defined(SANITIZER_GO) +# define SANITIZER_INTERFACE_ATTRIBUTE +# define SANITIZER_WEAK_ATTRIBUTE +#else +# define SANITIZER_INTERFACE_ATTRIBUTE __attribute__((visibility("default"))) +# define SANITIZER_WEAK_ATTRIBUTE __attribute__((weak)) +#endif + +#ifdef __linux__ +# define SANITIZER_SUPPORTS_WEAK_HOOKS 1 +#else +# define SANITIZER_SUPPORTS_WEAK_HOOKS 0 +#endif + +// __has_feature +#if !defined(__has_feature) +# define __has_feature(x) 0 +#endif + +// For portability reasons we do not include stddef.h, stdint.h or any other +// system header, but we do need some basic types that are not defined +// in a portable way by the language itself. +namespace __sanitizer { + +#if defined(_WIN64) +// 64-bit Windows uses LLP64 data model. +typedef unsigned long long uptr; // NOLINT +typedef signed long long sptr; // NOLINT +#else +typedef unsigned long uptr; // NOLINT +typedef signed long sptr; // NOLINT +#endif // defined(_WIN64) +#if defined(__x86_64__) +// Since x32 uses ILP32 data model in 64-bit hardware mode, we must use +// 64-bit pointer to unwind stack frame. +typedef unsigned long long uhwptr; // NOLINT +#else +typedef uptr uhwptr; // NOLINT +#endif +typedef unsigned char u8; +typedef unsigned short u16; // NOLINT +typedef unsigned int u32; +typedef unsigned long long u64; // NOLINT +typedef signed char s8; +typedef signed short s16; // NOLINT +typedef signed int s32; +typedef signed long long s64; // NOLINT +typedef int fd_t; + +} // namespace __sanitizer + +extern "C" { + // Tell the tools to write their reports to "path.<pid>" instead of stderr. + void __sanitizer_set_report_path(const char *path) + SANITIZER_INTERFACE_ATTRIBUTE; + + // Tell the tools to write their reports to given file descriptor instead of + // stderr. + void __sanitizer_set_report_fd(int fd) + SANITIZER_INTERFACE_ATTRIBUTE; + + // Notify the tools that the sandbox is going to be turned on. The reserved + // parameter will be used in the future to hold a structure with functions + // that the tools may call to bypass the sandbox. + void __sanitizer_sandbox_on_notify(void *reserved) + SANITIZER_WEAK_ATTRIBUTE SANITIZER_INTERFACE_ATTRIBUTE; + + // This function is called by the tool when it has just finished reporting + // an error. 'error_summary' is a one-line string that summarizes + // the error message. This function can be overridden by the client. + void __sanitizer_report_error_summary(const char *error_summary) + SANITIZER_WEAK_ATTRIBUTE SANITIZER_INTERFACE_ATTRIBUTE; +} // extern "C" + + using namespace __sanitizer; // NOLINT // ----------- ATTENTION ------------- // This header should NOT include any other headers to avoid portability issues. diff --git a/libsanitizer/sanitizer_common/sanitizer_lfstack.h b/libsanitizer/sanitizer_common/sanitizer_lfstack.h index 63fbf066943..8033b9a7f9c 100644 --- a/libsanitizer/sanitizer_common/sanitizer_lfstack.h +++ b/libsanitizer/sanitizer_common/sanitizer_lfstack.h @@ -66,6 +66,6 @@ struct LFStack { atomic_uint64_t head_; }; -} +} // namespace __sanitizer #endif // #ifndef SANITIZER_LFSTACK_H diff --git a/libsanitizer/sanitizer_common/sanitizer_libc.cc b/libsanitizer/sanitizer_common/sanitizer_libc.cc index b02cbd4aced..c57128a54cb 100644 --- a/libsanitizer/sanitizer_common/sanitizer_libc.cc +++ b/libsanitizer/sanitizer_common/sanitizer_libc.cc @@ -204,7 +204,7 @@ s64 internal_simple_strtoll(const char *nptr, char **endptr, int base) { } bool mem_is_zero(const char *beg, uptr size) { - CHECK_LE(size, 1UL << FIRST_32_SECOND_64(30, 40)); // Sanity check. + CHECK_LE(size, 1ULL << FIRST_32_SECOND_64(30, 40)); // Sanity check. const char *end = beg + size; uptr *aligned_beg = (uptr *)RoundUpTo((uptr)beg, sizeof(uptr)); uptr *aligned_end = (uptr *)RoundDownTo((uptr)end, sizeof(uptr)); diff --git a/libsanitizer/sanitizer_common/sanitizer_libc.h b/libsanitizer/sanitizer_common/sanitizer_libc.h index f193017f953..7a9774406fa 100644 --- a/libsanitizer/sanitizer_common/sanitizer_libc.h +++ b/libsanitizer/sanitizer_common/sanitizer_libc.h @@ -9,14 +9,13 @@ // 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. -// NOTE: This file may be included into user code. //===----------------------------------------------------------------------===// #ifndef SANITIZER_LIBC_H #define SANITIZER_LIBC_H // ----------- ATTENTION ------------- // This header should NOT include any other headers from sanitizer runtime. -#include "sanitizer/common_interface_defs.h" +#include "sanitizer_internal_defs.h" namespace __sanitizer { @@ -56,17 +55,24 @@ void *internal_mmap(void *addr, uptr length, int prot, int flags, int internal_munmap(void *addr, uptr length); // I/O -typedef int fd_t; const fd_t kInvalidFd = -1; const fd_t kStdinFd = 0; const fd_t kStdoutFd = 1; const fd_t kStderrFd = 2; int internal_close(fd_t fd); int internal_isatty(fd_t fd); -fd_t internal_open(const char *filename, bool write); + +// Use __sanitizer::OpenFile() instead. +fd_t internal_open(const char *filename, int flags); +fd_t 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_filesize(fd_t fd); // -1 on error. +int internal_stat(const char *path, void *buf); +int internal_lstat(const char *path, void *buf); +int internal_fstat(fd_t fd, void *buf); + int internal_dup2(int oldfd, int newfd); uptr internal_readlink(const char *path, char *buf, uptr bufsize); int internal_snprintf(char *buffer, uptr length, const char *format, ...); diff --git a/libsanitizer/sanitizer_common/sanitizer_linux.cc b/libsanitizer/sanitizer_common/sanitizer_linux.cc index dc2148f7fc4..3d3aa0b00bd 100644 --- a/libsanitizer/sanitizer_common/sanitizer_linux.cc +++ b/libsanitizer/sanitizer_common/sanitizer_linux.cc @@ -28,11 +28,14 @@ #include <sys/syscall.h> #include <sys/time.h> #include <sys/types.h> +#include <sys/prctl.h> #include <unistd.h> #include <unwind.h> #include <errno.h> -#include <sys/prctl.h> -#include <linux/futex.h> + +// <linux/futex.h> is broken on some linux distributions. +const int FUTEX_WAIT = 0; +const int FUTEX_WAKE = 1; // Are we using 32-bit or 64-bit syscalls? // x32 (which defines __x86_64__) has SANITIZER_WORDSIZE == 32 @@ -63,8 +66,16 @@ int internal_close(fd_t fd) { return syscall(__NR_close, fd); } -fd_t internal_open(const char *filename, bool write) { - return syscall(__NR_open, filename, +fd_t internal_open(const char *filename, int flags) { + return syscall(__NR_open, filename, flags); +} + +fd_t internal_open(const char *filename, int flags, u32 mode) { + return syscall(__NR_open, filename, flags, mode); +} + +fd_t OpenFile(const char *filename, bool write) { + return internal_open(filename, write ? O_WRONLY | O_CREAT /*| O_CLOEXEC*/ : O_RDONLY, 0660); } @@ -80,16 +91,38 @@ uptr internal_write(fd_t fd, const void *buf, uptr count) { return res; } +int internal_stat(const char *path, void *buf) { +#if SANITIZER_LINUX_USES_64BIT_SYSCALLS + return syscall(__NR_stat, path, buf); +#else + return syscall(__NR_stat64, path, buf); +#endif +} + +int internal_lstat(const char *path, void *buf) { +#if SANITIZER_LINUX_USES_64BIT_SYSCALLS + return syscall(__NR_lstat, path, buf); +#else + return syscall(__NR_lstat64, path, buf); +#endif +} + +int internal_fstat(fd_t fd, void *buf) { +#if SANITIZER_LINUX_USES_64BIT_SYSCALLS + return syscall(__NR_fstat, fd, buf); +#else + return syscall(__NR_fstat64, fd, buf); +#endif +} + uptr internal_filesize(fd_t fd) { #if SANITIZER_LINUX_USES_64BIT_SYSCALLS struct stat st; - if (syscall(__NR_fstat, fd, &st)) - return -1; #else struct stat64 st; - if (syscall(__NR_fstat64, fd, &st)) - return -1; #endif + if (internal_fstat(fd, &st)) + return -1; return (uptr)st.st_size; } diff --git a/libsanitizer/sanitizer_common/sanitizer_mac.cc b/libsanitizer/sanitizer_common/sanitizer_mac.cc index 76bf8670870..309b5a94005 100644 --- a/libsanitizer/sanitizer_common/sanitizer_mac.cc +++ b/libsanitizer/sanitizer_common/sanitizer_mac.cc @@ -11,6 +11,12 @@ //===----------------------------------------------------------------------===// #ifdef __APPLE__ +// Use 64-bit inodes in file operations. ASan does not support OS X 10.5, so +// the clients will most certainly use 64-bit ones as well. +#ifndef _DARWIN_USE_64_BIT_INODE +#define _DARWIN_USE_64_BIT_INODE 1 +#endif +#include <stdio.h> #include "sanitizer_common.h" #include "sanitizer_internal_defs.h" @@ -46,9 +52,17 @@ int internal_close(fd_t fd) { return close(fd); } -fd_t internal_open(const char *filename, bool write) { - return open(filename, - write ? O_WRONLY | O_CREAT : O_RDONLY, 0660); +fd_t internal_open(const char *filename, int flags) { + return open(filename, flags); +} + +fd_t internal_open(const char *filename, int flags, u32 mode) { + return open(filename, flags, mode); +} + +fd_t 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) { @@ -59,9 +73,21 @@ uptr internal_write(fd_t fd, const void *buf, uptr count) { return write(fd, buf, count); } +int internal_stat(const char *path, void *buf) { + return stat(path, (struct stat *)buf); +} + +int internal_lstat(const char *path, void *buf) { + return lstat(path, (struct stat *)buf); +} + +int internal_fstat(fd_t fd, void *buf) { + return fstat(fd, (struct stat *)buf); +} + uptr internal_filesize(fd_t fd) { struct stat st; - if (fstat(fd, &st)) + if (internal_fstat(fd, &st)) return -1; return (uptr)st.st_size; } diff --git a/libsanitizer/sanitizer_common/sanitizer_platform_interceptors.h b/libsanitizer/sanitizer_common/sanitizer_platform_interceptors.h index 0ca9444fcb8..2c60253c533 100644 --- a/libsanitizer/sanitizer_common/sanitizer_platform_interceptors.h +++ b/libsanitizer/sanitizer_common/sanitizer_platform_interceptors.h @@ -33,4 +33,4 @@ # define SANITIZER_INTERCEPT_PWRITE64 SI_LINUX_NOT_ANDROID # define SANITIZER_INTERCEPT_PRCTL SI_LINUX_NOT_ANDROID -# define SANITIZER_INTERCEPT_SCANF 0 +# define SANITIZER_INTERCEPT_SCANF SI_NOT_WINDOWS diff --git a/libsanitizer/sanitizer_common/sanitizer_posix.cc b/libsanitizer/sanitizer_common/sanitizer_posix.cc index 17287cd950e..48c5ebaef3e 100644 --- a/libsanitizer/sanitizer_common/sanitizer_posix.cc +++ b/libsanitizer/sanitizer_common/sanitizer_posix.cc @@ -56,12 +56,12 @@ void *MmapOrDie(uptr size, const char *mem_type) { if (recursion_count) { // The Report() and CHECK calls below may call mmap recursively and fail. // If we went into recursion, just die. - RawWrite("AddressSanitizer is unable to mmap\n"); + RawWrite("ERROR: Failed to mmap\n"); Die(); } recursion_count++; - Report("ERROR: Failed to allocate 0x%zx (%zd) bytes of %s: %s\n", - size, size, mem_type, strerror(errno)); + Report("ERROR: %s failed to allocate 0x%zx (%zd) bytes of %s: %s\n", + SanitizerToolName, size, size, mem_type, strerror(errno)); DumpProcessMap(); CHECK("unable to mmap" && 0); } @@ -72,8 +72,8 @@ void UnmapOrDie(void *addr, uptr size) { if (!addr || !size) return; int res = internal_munmap(addr, size); if (res != 0) { - Report("ERROR: Failed to deallocate 0x%zx (%zd) bytes at address %p\n", - size, size, addr); + Report("ERROR: %s failed to deallocate 0x%zx (%zd) bytes at address %p\n", + SanitizerToolName, size, size, addr); CHECK("unable to unmap" && 0); } } @@ -86,8 +86,9 @@ void *MmapFixedNoReserve(uptr fixed_addr, uptr size) { MAP_PRIVATE | MAP_ANON | MAP_FIXED | MAP_NORESERVE, -1, 0); if (p == (void*)-1) - Report("ERROR: Failed to allocate 0x%zx (%zd) bytes at address %p (%d)\n", - size, size, fixed_addr, errno); + Report("ERROR: " + "%s failed to allocate 0x%zx (%zd) bytes at address %p (%d)\n", + SanitizerToolName, size, size, fixed_addr, errno); return p; } @@ -99,8 +100,9 @@ void *MmapFixedOrDie(uptr fixed_addr, uptr size) { MAP_PRIVATE | MAP_ANON | MAP_FIXED, -1, 0); if (p == (void*)-1) { - Report("ERROR: Failed to allocate 0x%zx (%zd) bytes at address %p (%d)\n", - size, size, fixed_addr, errno); + Report("ERROR:" + " %s failed to allocate 0x%zx (%zd) bytes at address %p (%d)\n", + SanitizerToolName, size, size, fixed_addr, errno); CHECK("unable to mmap" && 0); } return p; @@ -118,7 +120,7 @@ void FlushUnneededShadowMemory(uptr addr, uptr size) { } void *MapFileToMemory(const char *file_name, uptr *buff_size) { - fd_t fd = internal_open(file_name, false); + fd_t fd = OpenFile(file_name, false); CHECK_NE(fd, kInvalidFd); uptr fsize = internal_filesize(fd); CHECK_NE(fsize, (uptr)-1); @@ -187,7 +189,7 @@ void SetStackSizeLimitInBytes(uptr limit) { rlim.rlim_cur = limit; rlim.rlim_max = limit; if (setrlimit(RLIMIT_STACK, &rlim)) { - Report("setrlimit() failed %d\n", errno); + Report("ERROR: %s setrlimit() failed %d\n", SanitizerToolName, errno); Die(); } CHECK(!StackSizeIsUnlimited()); diff --git a/libsanitizer/sanitizer_common/sanitizer_quarantine.h b/libsanitizer/sanitizer_common/sanitizer_quarantine.h index 042fba7c1da..f3ad91a9cc2 100644 --- a/libsanitizer/sanitizer_common/sanitizer_quarantine.h +++ b/libsanitizer/sanitizer_common/sanitizer_quarantine.h @@ -157,7 +157,7 @@ class QuarantineCache { atomic_store(&size_, Size() + add, memory_order_relaxed); } - QuarantineBatch *NOINLINE AllocBatch(Callback cb) { + NOINLINE QuarantineBatch* AllocBatch(Callback cb) { QuarantineBatch *b = (QuarantineBatch *)cb.Allocate(sizeof(*b)); b->count = 0; b->size = 0; @@ -165,6 +165,6 @@ class QuarantineCache { return b; } }; -} +} // namespace __sanitizer #endif // #ifndef SANITIZER_QUARANTINE_H diff --git a/libsanitizer/sanitizer_common/sanitizer_stackdepot.h b/libsanitizer/sanitizer_common/sanitizer_stackdepot.h index 1e917eb53bb..bf73cf14aad 100644 --- a/libsanitizer/sanitizer_common/sanitizer_stackdepot.h +++ b/libsanitizer/sanitizer_common/sanitizer_stackdepot.h @@ -11,7 +11,7 @@ #ifndef SANITIZER_STACKDEPOT_H #define SANITIZER_STACKDEPOT_H -#include "sanitizer/common_interface_defs.h" +#include "sanitizer_internal_defs.h" namespace __sanitizer { diff --git a/libsanitizer/sanitizer_common/sanitizer_stacktrace.cc b/libsanitizer/sanitizer_common/sanitizer_stacktrace.cc index 59af1c35292..259da0082e3 100644 --- a/libsanitizer/sanitizer_common/sanitizer_stacktrace.cc +++ b/libsanitizer/sanitizer_common/sanitizer_stacktrace.cc @@ -15,8 +15,9 @@ #include "sanitizer_symbolizer.h" namespace __sanitizer { -static const char *StripPathPrefix(const char *filepath, - const char *strip_file_prefix) { +const char *StripPathPrefix(const char *filepath, + const char *strip_file_prefix) { + if (filepath == 0) return 0; if (filepath == internal_strstr(filepath, strip_file_prefix)) return filepath + internal_strlen(strip_file_prefix); return filepath; diff --git a/libsanitizer/sanitizer_common/sanitizer_stacktrace.h b/libsanitizer/sanitizer_common/sanitizer_stacktrace.h index c939644401c..fd0c4671a61 100644 --- a/libsanitizer/sanitizer_common/sanitizer_stacktrace.h +++ b/libsanitizer/sanitizer_common/sanitizer_stacktrace.h @@ -55,6 +55,10 @@ struct StackTrace { u32 *compressed, uptr size); }; + +const char *StripPathPrefix(const char *filepath, + const char *strip_file_prefix); + } // namespace __sanitizer // Use this macro if you want to print stack trace with the caller diff --git a/libsanitizer/sanitizer_common/sanitizer_symbolizer.cc b/libsanitizer/sanitizer_common/sanitizer_symbolizer.cc index f62acf35f8f..2c9cb2b0a55 100644 --- a/libsanitizer/sanitizer_common/sanitizer_symbolizer.cc +++ b/libsanitizer/sanitizer_common/sanitizer_symbolizer.cc @@ -174,6 +174,53 @@ class ExternalSymbolizer { static LowLevelAllocator symbolizer_allocator; // Linker initialized. +#if SANITIZER_SUPPORTS_WEAK_HOOKS +extern "C" { +SANITIZER_WEAK_ATTRIBUTE SANITIZER_INTERFACE_ATTRIBUTE +bool __sanitizer_symbolize_code(const char *ModuleName, u64 ModuleOffset, + char *Buffer, int MaxLength); +SANITIZER_WEAK_ATTRIBUTE SANITIZER_INTERFACE_ATTRIBUTE +bool __sanitizer_symbolize_data(const char *ModuleName, u64 ModuleOffset, + char *Buffer, int MaxLength); +} // extern "C" + +class InternalSymbolizer { + public: + typedef bool (*SanitizerSymbolizeFn)(const char*, u64, char*, int); + static InternalSymbolizer *get() { + if (__sanitizer_symbolize_code != 0 && + __sanitizer_symbolize_data != 0) { + void *mem = symbolizer_allocator.Allocate(sizeof(InternalSymbolizer)); + return new(mem) 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; + } + + private: + InternalSymbolizer() { } + + static const int kBufferSize = 16 * 1024; + char buffer_[kBufferSize]; +}; +#else // SANITIZER_SUPPORTS_WEAK_HOOKS + +class InternalSymbolizer { + public: + static InternalSymbolizer *get() { return 0; } + char *SendCommand(bool is_data, const char *module_name, uptr module_offset) { + return 0; + } +}; + +#endif // SANITIZER_SUPPORTS_WEAK_HOOKS + class Symbolizer { public: uptr SymbolizeCode(uptr addr, AddressInfo *frames, uptr max_frames) { @@ -266,8 +313,23 @@ class Symbolizer { return true; } + bool IsSymbolizerAvailable() { + if (internal_symbolizer_ == 0) + internal_symbolizer_ = InternalSymbolizer::get(); + return internal_symbolizer_ || external_symbolizer_; + } + private: char *SendCommand(bool is_data, const char *module_name, uptr module_offset) { + // First, try to use internal symbolizer. + if (internal_symbolizer_ == 0) { + internal_symbolizer_ = InternalSymbolizer::get(); + } + if (internal_symbolizer_) { + return internal_symbolizer_->SendCommand(is_data, module_name, + module_offset); + } + // Otherwise, fall back to external symbolizer. if (external_symbolizer_ == 0) { ReportExternalSymbolizerError( "WARNING: Trying to symbolize code, but external " @@ -322,6 +384,7 @@ class Symbolizer { uptr n_modules_; ExternalSymbolizer *external_symbolizer_; // Leaked. + InternalSymbolizer *internal_symbolizer_; // Leaked. }; static Symbolizer symbolizer; // Linker initialized. @@ -338,4 +401,8 @@ bool InitializeExternalSymbolizer(const char *path_to_symbolizer) { return symbolizer.InitializeExternalSymbolizer(path_to_symbolizer); } +bool IsSymbolizerAvailable() { + return symbolizer.IsSymbolizerAvailable(); +} + } // namespace __sanitizer diff --git a/libsanitizer/sanitizer_common/sanitizer_symbolizer.h b/libsanitizer/sanitizer_common/sanitizer_symbolizer.h index b88fa3f655a..751806e8472 100644 --- a/libsanitizer/sanitizer_common/sanitizer_symbolizer.h +++ b/libsanitizer/sanitizer_common/sanitizer_symbolizer.h @@ -67,6 +67,8 @@ struct DataInfo { uptr SymbolizeCode(uptr address, AddressInfo *frames, uptr max_frames); bool SymbolizeData(uptr address, DataInfo *info); +bool IsSymbolizerAvailable(); + // Attempts to demangle the provided C++ mangled name. const char *Demangle(const char *Name); diff --git a/libsanitizer/sanitizer_common/sanitizer_win.cc b/libsanitizer/sanitizer_common/sanitizer_win.cc index 242b4429bd7..3d5cde11cf8 100644 --- a/libsanitizer/sanitizer_common/sanitizer_win.cc +++ b/libsanitizer/sanitizer_common/sanitizer_win.cc @@ -95,6 +95,11 @@ void *Mprotect(uptr fixed_addr, uptr size) { MEM_RESERVE | MEM_COMMIT, PAGE_NOACCESS); } +void FlushUnneededShadowMemory(uptr addr, uptr size) { + // This is almost useless on 32-bits. + // FIXME: add madvice-analog when we move to 64-bits. +} + bool MemoryRangeIsAvailable(uptr range_start, uptr range_end) { // FIXME: shall we do anything here on Windows? return true; @@ -189,7 +194,15 @@ int internal_isatty(fd_t fd) { return _isatty(fd); } -fd_t internal_open(const char *filename, bool write) { +fd_t internal_open(const char *filename, int flags) { + UNIMPLEMENTED(); +} + +fd_t internal_open(const char *filename, int flags, u32 mode) { + UNIMPLEMENTED(); +} + +fd_t OpenFile(const char *filename, bool write) { UNIMPLEMENTED(); } @@ -209,6 +222,18 @@ uptr internal_write(fd_t fd, const void *buf, uptr count) { return ret; } +int internal_stat(const char *path, void *buf) { + UNIMPLEMENTED(); +} + +int internal_lstat(const char *path, void *buf) { + UNIMPLEMENTED(); +} + +int internal_fstat(fd_t fd, void *buf) { + UNIMPLEMENTED(); +} + uptr internal_filesize(fd_t fd) { UNIMPLEMENTED(); } @@ -227,10 +252,8 @@ int internal_sched_yield() { } // ---------------------- BlockingMutex ---------------- {{{1 -enum LockState { - LOCK_UNINITIALIZED = 0, - LOCK_READY = -1, -}; +const uptr LOCK_UNINITIALIZED = 0; +const uptr LOCK_READY = (uptr)-1; BlockingMutex::BlockingMutex(LinkerInitialized li) { // FIXME: see comments in BlockingMutex::Lock() for the details. @@ -252,12 +275,12 @@ void BlockingMutex::Lock() { // locks while we're starting in one thread to avoid double-init races. } EnterCriticalSection((LPCRITICAL_SECTION)opaque_storage_); - CHECK(owner_ == LOCK_READY); + CHECK_EQ(owner_, LOCK_READY); owner_ = GetThreadSelf(); } void BlockingMutex::Unlock() { - CHECK(owner_ == GetThreadSelf()); + CHECK_EQ(owner_, GetThreadSelf()); owner_ = LOCK_READY; LeaveCriticalSection((LPCRITICAL_SECTION)opaque_storage_); } diff --git a/libsanitizer/tsan/tsan_defs.h b/libsanitizer/tsan/tsan_defs.h index 6683a4e1abb..b2937a428f3 100644 --- a/libsanitizer/tsan/tsan_defs.h +++ b/libsanitizer/tsan/tsan_defs.h @@ -26,16 +26,19 @@ namespace __tsan { const bool kGoMode = true; const bool kCppMode = false; const char *const kTsanOptionsEnv = "GORACE"; +// Go linker does not support weak symbols. +#define CPP_WEAK #else const bool kGoMode = false; const bool kCppMode = true; const char *const kTsanOptionsEnv = "TSAN_OPTIONS"; +#define CPP_WEAK WEAK #endif const int kTidBits = 13; const unsigned kMaxTid = 1 << kTidBits; const unsigned kMaxTidInClock = kMaxTid * 2; // This includes msb 'freed' bit. -const int kClkBits = 43; +const int kClkBits = 42; #ifndef TSAN_GO const int kShadowStackSize = 4 * 1024; const int kTraceStackSize = 256; diff --git a/libsanitizer/tsan/tsan_fd.cc b/libsanitizer/tsan/tsan_fd.cc index f640c4f893e..a75d9bde08a 100644 --- a/libsanitizer/tsan/tsan_fd.cc +++ b/libsanitizer/tsan/tsan_fd.cc @@ -148,7 +148,7 @@ void FdAcquire(ThreadState *thr, uptr pc, int fd) { FdDesc *d = fddesc(thr, pc, fd); FdSync *s = d->sync; DPrintf("#%d: FdAcquire(%d) -> %p\n", thr->tid, fd, s); - MemoryRead8Byte(thr, pc, (uptr)d); + MemoryRead(thr, pc, (uptr)d, kSizeLog8); if (s) Acquire(thr, pc, (uptr)s); } @@ -159,20 +159,20 @@ void FdRelease(ThreadState *thr, uptr pc, int fd) { DPrintf("#%d: FdRelease(%d) -> %p\n", thr->tid, fd, s); if (s) Release(thr, pc, (uptr)s); - MemoryRead8Byte(thr, pc, (uptr)d); + MemoryRead(thr, pc, (uptr)d, kSizeLog8); } void FdAccess(ThreadState *thr, uptr pc, int fd) { DPrintf("#%d: FdAccess(%d)\n", thr->tid, fd); FdDesc *d = fddesc(thr, pc, fd); - MemoryRead8Byte(thr, pc, (uptr)d); + MemoryRead(thr, pc, (uptr)d, kSizeLog8); } void FdClose(ThreadState *thr, uptr pc, int fd) { DPrintf("#%d: FdClose(%d)\n", thr->tid, fd); FdDesc *d = fddesc(thr, pc, fd); // To catch races between fd usage and close. - MemoryWrite8Byte(thr, pc, (uptr)d); + MemoryWrite(thr, pc, (uptr)d, kSizeLog8); // We need to clear it, because if we do not intercept any call out there // that creates fd, we will hit false postives. MemoryResetRange(thr, pc, (uptr)d, 8); @@ -191,7 +191,7 @@ void FdDup(ThreadState *thr, uptr pc, int oldfd, int newfd) { DPrintf("#%d: FdDup(%d, %d)\n", thr->tid, oldfd, newfd); // Ignore the case when user dups not yet connected socket. FdDesc *od = fddesc(thr, pc, oldfd); - MemoryRead8Byte(thr, pc, (uptr)od); + MemoryRead(thr, pc, (uptr)od, kSizeLog8); FdClose(thr, pc, newfd); init(thr, pc, newfd, ref(od->sync)); } diff --git a/libsanitizer/tsan/tsan_flags.cc b/libsanitizer/tsan/tsan_flags.cc index 630bd75769b..ae748a13e15 100644 --- a/libsanitizer/tsan/tsan_flags.cc +++ b/libsanitizer/tsan/tsan_flags.cc @@ -43,6 +43,7 @@ void InitializeFlags(Flags *f, const char *env) { f->report_thread_leaks = true; f->report_destroy_locked = true; f->report_signal_unsafe = true; + f->report_atomic_races = true; f->force_seq_cst_atomics = false; f->strip_path_prefix = ""; f->suppressions = ""; @@ -70,6 +71,7 @@ void InitializeFlags(Flags *f, const char *env) { ParseFlag(env, &f->report_thread_leaks, "report_thread_leaks"); ParseFlag(env, &f->report_destroy_locked, "report_destroy_locked"); ParseFlag(env, &f->report_signal_unsafe, "report_signal_unsafe"); + ParseFlag(env, &f->report_atomic_races, "report_atomic_races"); ParseFlag(env, &f->force_seq_cst_atomics, "force_seq_cst_atomics"); ParseFlag(env, &f->strip_path_prefix, "strip_path_prefix"); ParseFlag(env, &f->suppressions, "suppressions"); diff --git a/libsanitizer/tsan/tsan_flags.h b/libsanitizer/tsan/tsan_flags.h index ed27363c2ff..480b41538f9 100644 --- a/libsanitizer/tsan/tsan_flags.h +++ b/libsanitizer/tsan/tsan_flags.h @@ -41,6 +41,8 @@ struct Flags { // Report violations of async signal-safety // (e.g. malloc() call from a signal handler). bool report_signal_unsafe; + // Report races between atomic and plain memory accesses. + bool report_atomic_races; // If set, all atomics are effectively sequentially consistent (seq_cst), // regardless of what user actually specified. bool force_seq_cst_atomics; @@ -84,6 +86,6 @@ struct Flags { Flags *flags(); void InitializeFlags(Flags *flags, const char *env); -} +} // namespace __tsan #endif // TSAN_FLAGS_H diff --git a/libsanitizer/tsan/tsan_interceptors.cc b/libsanitizer/tsan/tsan_interceptors.cc index d8f66de2327..8a54511b6e0 100644 --- a/libsanitizer/tsan/tsan_interceptors.cc +++ b/libsanitizer/tsan/tsan_interceptors.cc @@ -51,9 +51,12 @@ extern "C" int pthread_sigmask(int how, const sigset_t *set, sigset_t *oldset); extern "C" int sigfillset(sigset_t *set); extern "C" void *pthread_self(); extern "C" void _exit(int status); -extern "C" int __cxa_atexit(void (*func)(void *arg), void *arg, void *dso); extern "C" int *__errno_location(); extern "C" int fileno_unlocked(void *stream); +extern "C" void *__libc_malloc(uptr size); +extern "C" void *__libc_calloc(uptr size, uptr n); +extern "C" void *__libc_realloc(void *ptr, uptr size); +extern "C" void __libc_free(void *ptr); const int PTHREAD_MUTEX_RECURSIVE = 1; const int PTHREAD_MUTEX_RECURSIVE_NP = 1; const int kPthreadAttrSize = 56; @@ -122,7 +125,7 @@ struct SignalContext { int pending_signal_count; SignalDesc pending_signals[kSigCount]; }; -} +} // namespace __tsan static SignalContext *SigCtx(ThreadState *thr) { SignalContext *ctx = (SignalContext*)thr->signal_ctx; @@ -238,12 +241,15 @@ class AtExitContext { typedef void(*atexit_t)(); - int atexit(ThreadState *thr, uptr pc, atexit_t f) { + int atexit(ThreadState *thr, uptr pc, bool is_on_exit, + atexit_t f, void *arg) { Lock l(&mtx_); if (pos_ == kMaxAtExit) return 1; Release(thr, pc, (uptr)this); stack_[pos_] = f; + args_[pos_] = arg; + is_on_exits_[pos_] = is_on_exit; pos_++; return 0; } @@ -252,11 +258,15 @@ class AtExitContext { CHECK_EQ(thr->in_rtl, 0); for (;;) { atexit_t f = 0; + void *arg = 0; + bool is_on_exit = false; { Lock l(&mtx_); if (pos_) { pos_--; f = stack_[pos_]; + arg = args_[pos_]; + is_on_exit = is_on_exits_[pos_]; ScopedInRtl in_rtl; Acquire(thr, pc, (uptr)this); } @@ -265,7 +275,10 @@ class AtExitContext { break; DPrintf("#%d: executing atexit func %p\n", thr->tid, f); CHECK_EQ(thr->in_rtl, 0); - f(); + if (is_on_exit) + ((void(*)(int status, void *arg))f)(0, arg); + else + ((void(*)(void *arg, void *dso))f)(arg, 0); } } @@ -273,6 +286,8 @@ class AtExitContext { static const int kMaxAtExit = 128; Mutex mtx_; atexit_t stack_[kMaxAtExit]; + void *args_[kMaxAtExit]; + bool is_on_exits_[kMaxAtExit]; int pos_; }; @@ -282,18 +297,32 @@ static void finalize(void *arg) { ThreadState * thr = cur_thread(); uptr pc = 0; atexit_ctx->exit(thr, pc); - { - ScopedInRtl in_rtl; - DestroyAndFree(atexit_ctx); - } int status = Finalize(cur_thread()); if (status) _exit(status); } TSAN_INTERCEPTOR(int, atexit, void (*f)()) { + if (cur_thread()->in_symbolizer) + return 0; SCOPED_TSAN_INTERCEPTOR(atexit, f); - return atexit_ctx->atexit(thr, pc, f); + return atexit_ctx->atexit(thr, pc, false, (void(*)())f, 0); +} + +TSAN_INTERCEPTOR(int, on_exit, void(*f)(int, void*), void *arg) { + if (cur_thread()->in_symbolizer) + return 0; + SCOPED_TSAN_INTERCEPTOR(on_exit, f, arg); + return atexit_ctx->atexit(thr, pc, true, (void(*)())f, arg); +} + +TSAN_INTERCEPTOR(int, __cxa_atexit, void (*f)(void *a), void *arg, void *dso) { + if (cur_thread()->in_symbolizer) + return 0; + SCOPED_TSAN_INTERCEPTOR(__cxa_atexit, f, arg, dso); + if (dso) + return REAL(__cxa_atexit)(f, arg, dso); + return atexit_ctx->atexit(thr, pc, false, (void(*)())f, arg); } TSAN_INTERCEPTOR(void, longjmp, void *env, int val) { @@ -309,6 +338,8 @@ TSAN_INTERCEPTOR(void, siglongjmp, void *env, int val) { } TSAN_INTERCEPTOR(void*, malloc, uptr size) { + if (cur_thread()->in_symbolizer) + return __libc_malloc(size); void *p = 0; { SCOPED_INTERCEPTOR_RAW(malloc, size); @@ -324,6 +355,9 @@ TSAN_INTERCEPTOR(void*, __libc_memalign, uptr align, uptr sz) { } TSAN_INTERCEPTOR(void*, calloc, uptr size, uptr n) { + if (cur_thread()->in_symbolizer) + return __libc_calloc(size, n); + if (__sanitizer::CallocShouldReturnNullDueToOverflow(size, n)) return 0; void *p = 0; { SCOPED_INTERCEPTOR_RAW(calloc, size, n); @@ -335,6 +369,8 @@ TSAN_INTERCEPTOR(void*, calloc, uptr size, uptr n) { } TSAN_INTERCEPTOR(void*, realloc, void *p, uptr size) { + if (cur_thread()->in_symbolizer) + return __libc_realloc(p, size); if (p) invoke_free_hook(p); { @@ -348,6 +384,8 @@ TSAN_INTERCEPTOR(void*, realloc, void *p, uptr size) { TSAN_INTERCEPTOR(void, free, void *p) { if (p == 0) return; + if (cur_thread()->in_symbolizer) + return __libc_free(p); invoke_free_hook(p); SCOPED_INTERCEPTOR_RAW(free, p); user_free(thr, pc, p); @@ -356,12 +394,16 @@ TSAN_INTERCEPTOR(void, free, void *p) { TSAN_INTERCEPTOR(void, cfree, void *p) { if (p == 0) return; + if (cur_thread()->in_symbolizer) + return __libc_free(p); invoke_free_hook(p); SCOPED_INTERCEPTOR_RAW(cfree, p); user_free(thr, pc, p); } #define OPERATOR_NEW_BODY(mangled_name) \ + if (cur_thread()->in_symbolizer) \ + return __libc_malloc(size); \ void *p = 0; \ { \ SCOPED_INTERCEPTOR_RAW(mangled_name, size); \ @@ -385,6 +427,8 @@ void *operator new[](__sanitizer::uptr size, std::nothrow_t const&) { #define OPERATOR_DELETE_BODY(mangled_name) \ if (ptr == 0) return; \ + if (cur_thread()->in_symbolizer) \ + return __libc_free(ptr); \ invoke_free_hook(ptr); \ SCOPED_INTERCEPTOR_RAW(mangled_name, ptr); \ user_free(thr, pc, ptr); @@ -549,6 +593,8 @@ TSAN_INTERCEPTOR(void*, mmap, void *addr, long_t sz, int prot, return MAP_FAILED; void *res = REAL(mmap)(addr, sz, prot, flags, fd, off); if (res != MAP_FAILED) { + if (fd > 0) + FdAccess(thr, pc, fd); MemoryResetRange(thr, pc, (uptr)res, sz); } return res; @@ -561,6 +607,8 @@ TSAN_INTERCEPTOR(void*, mmap64, void *addr, long_t sz, int prot, return MAP_FAILED; void *res = REAL(mmap64)(addr, sz, prot, flags, fd, off); if (res != MAP_FAILED) { + if (fd > 0) + FdAccess(thr, pc, fd); MemoryResetRange(thr, pc, (uptr)res, sz); } return res; @@ -958,14 +1006,14 @@ TSAN_INTERCEPTOR(int, pthread_cond_timedwait, void *c, void *m, void *abstime) { TSAN_INTERCEPTOR(int, pthread_barrier_init, void *b, void *a, unsigned count) { SCOPED_TSAN_INTERCEPTOR(pthread_barrier_init, b, a, count); - MemoryWrite1Byte(thr, pc, (uptr)b); + MemoryWrite(thr, pc, (uptr)b, kSizeLog1); int res = REAL(pthread_barrier_init)(b, a, count); return res; } TSAN_INTERCEPTOR(int, pthread_barrier_destroy, void *b) { SCOPED_TSAN_INTERCEPTOR(pthread_barrier_destroy, b); - MemoryWrite1Byte(thr, pc, (uptr)b); + MemoryWrite(thr, pc, (uptr)b, kSizeLog1); int res = REAL(pthread_barrier_destroy)(b); return res; } @@ -973,9 +1021,9 @@ TSAN_INTERCEPTOR(int, pthread_barrier_destroy, void *b) { TSAN_INTERCEPTOR(int, pthread_barrier_wait, void *b) { SCOPED_TSAN_INTERCEPTOR(pthread_barrier_wait, b); Release(thr, pc, (uptr)b); - MemoryRead1Byte(thr, pc, (uptr)b); + MemoryRead(thr, pc, (uptr)b, kSizeLog1); int res = REAL(pthread_barrier_wait)(b); - MemoryRead1Byte(thr, pc, (uptr)b); + MemoryRead(thr, pc, (uptr)b, kSizeLog1); if (res == 0 || res == PTHREAD_BARRIER_SERIAL_THREAD) { Acquire(thr, pc, (uptr)b); } @@ -1062,6 +1110,74 @@ TSAN_INTERCEPTOR(int, sem_getvalue, void *s, int *sval) { return res; } +TSAN_INTERCEPTOR(int, __xstat, int version, const char *path, void *buf) { + SCOPED_TSAN_INTERCEPTOR(__xstat, version, path, buf); + return REAL(__xstat)(version, path, buf); +} + +TSAN_INTERCEPTOR(int, stat, const char *path, void *buf) { + SCOPED_TSAN_INTERCEPTOR(__xstat, 0, path, buf); + return REAL(__xstat)(0, path, buf); +} + +TSAN_INTERCEPTOR(int, __xstat64, int version, const char *path, void *buf) { + SCOPED_TSAN_INTERCEPTOR(__xstat64, version, path, buf); + return REAL(__xstat64)(version, path, buf); +} + +TSAN_INTERCEPTOR(int, stat64, const char *path, void *buf) { + SCOPED_TSAN_INTERCEPTOR(__xstat64, 0, path, buf); + return REAL(__xstat64)(0, path, buf); +} + +TSAN_INTERCEPTOR(int, __lxstat, int version, const char *path, void *buf) { + SCOPED_TSAN_INTERCEPTOR(__lxstat, version, path, buf); + return REAL(__lxstat)(version, path, buf); +} + +TSAN_INTERCEPTOR(int, lstat, const char *path, void *buf) { + SCOPED_TSAN_INTERCEPTOR(__lxstat, 0, path, buf); + return REAL(__lxstat)(0, path, buf); +} + +TSAN_INTERCEPTOR(int, __lxstat64, int version, const char *path, void *buf) { + SCOPED_TSAN_INTERCEPTOR(__lxstat64, version, path, buf); + return REAL(__lxstat64)(version, path, buf); +} + +TSAN_INTERCEPTOR(int, lstat64, const char *path, void *buf) { + SCOPED_TSAN_INTERCEPTOR(__lxstat64, 0, path, buf); + return REAL(__lxstat64)(0, path, buf); +} + +TSAN_INTERCEPTOR(int, __fxstat, int version, int fd, void *buf) { + SCOPED_TSAN_INTERCEPTOR(__fxstat, version, fd, buf); + if (fd > 0) + FdAccess(thr, pc, fd); + return REAL(__fxstat)(version, fd, buf); +} + +TSAN_INTERCEPTOR(int, fstat, int fd, void *buf) { + SCOPED_TSAN_INTERCEPTOR(__fxstat, 0, fd, buf); + if (fd > 0) + FdAccess(thr, pc, fd); + return REAL(__fxstat)(0, fd, buf); +} + +TSAN_INTERCEPTOR(int, __fxstat64, int version, int fd, void *buf) { + SCOPED_TSAN_INTERCEPTOR(__fxstat64, version, fd, buf); + if (fd > 0) + FdAccess(thr, pc, fd); + return REAL(__fxstat64)(version, fd, buf); +} + +TSAN_INTERCEPTOR(int, fstat64, int fd, void *buf) { + SCOPED_TSAN_INTERCEPTOR(__fxstat64, 0, fd, buf); + if (fd > 0) + FdAccess(thr, pc, fd); + return REAL(__fxstat64)(0, fd, buf); +} + TSAN_INTERCEPTOR(int, open, const char *name, int flags, int mode) { SCOPED_TSAN_INTERCEPTOR(open, name, flags, mode); int fd = REAL(open)(name, flags, mode); @@ -1177,6 +1293,22 @@ TSAN_INTERCEPTOR(int, connect, int fd, void *addr, unsigned addrlen) { return res; } +TSAN_INTERCEPTOR(int, bind, int fd, void *addr, unsigned addrlen) { + SCOPED_TSAN_INTERCEPTOR(bind, fd, addr, addrlen); + int res = REAL(bind)(fd, addr, addrlen); + if (fd > 0 && res == 0) + FdAccess(thr, pc, fd); + return res; +} + +TSAN_INTERCEPTOR(int, listen, int fd, int backlog) { + SCOPED_TSAN_INTERCEPTOR(listen, fd, backlog); + int res = REAL(listen)(fd, backlog); + if (fd > 0 && res == 0) + FdAccess(thr, pc, fd); + return res; +} + TSAN_INTERCEPTOR(int, accept, int fd, void *addr, unsigned *addrlen) { SCOPED_TSAN_INTERCEPTOR(accept, fd, addr, addrlen); int fd2 = REAL(accept)(fd, addr, addrlen); @@ -1223,6 +1355,18 @@ TSAN_INTERCEPTOR(int, __close, int fd) { return REAL(__close)(fd); } +// glibc guts +TSAN_INTERCEPTOR(void, __res_iclose, void *state, bool free_addr) { + SCOPED_TSAN_INTERCEPTOR(__res_iclose, state, free_addr); + int fds[64]; + int cnt = ExtractResolvFDs(state, fds, ARRAY_SIZE(fds)); + for (int i = 0; i < cnt; i++) { + if (fds[i] > 0) + FdClose(thr, pc, fds[i]); + } + REAL(__res_iclose)(state, free_addr); +} + TSAN_INTERCEPTOR(int, pipe, int *pipefd) { SCOPED_TSAN_INTERCEPTOR(pipe, pipefd); int res = REAL(pipe)(pipefd); @@ -1765,6 +1909,18 @@ void InitializeInterceptors() { TSAN_INTERCEPT(sem_post); TSAN_INTERCEPT(sem_getvalue); + TSAN_INTERCEPT(stat); + TSAN_INTERCEPT(__xstat); + TSAN_INTERCEPT(stat64); + TSAN_INTERCEPT(__xstat64); + TSAN_INTERCEPT(lstat); + TSAN_INTERCEPT(__lxstat); + TSAN_INTERCEPT(lstat64); + TSAN_INTERCEPT(__lxstat64); + TSAN_INTERCEPT(fstat); + TSAN_INTERCEPT(__fxstat); + TSAN_INTERCEPT(fstat64); + TSAN_INTERCEPT(__fxstat64); TSAN_INTERCEPT(open); TSAN_INTERCEPT(open64); TSAN_INTERCEPT(creat); @@ -1779,11 +1935,15 @@ void InitializeInterceptors() { TSAN_INTERCEPT(socket); TSAN_INTERCEPT(socketpair); TSAN_INTERCEPT(connect); + TSAN_INTERCEPT(bind); + TSAN_INTERCEPT(listen); TSAN_INTERCEPT(accept); TSAN_INTERCEPT(accept4); TSAN_INTERCEPT(epoll_create); TSAN_INTERCEPT(epoll_create1); TSAN_INTERCEPT(close); + TSAN_INTERCEPT(__close); + TSAN_INTERCEPT(__res_iclose); TSAN_INTERCEPT(pipe); TSAN_INTERCEPT(pipe2); @@ -1826,6 +1986,8 @@ void InitializeInterceptors() { TSAN_INTERCEPT(munlockall); TSAN_INTERCEPT(fork); + TSAN_INTERCEPT(on_exit); + TSAN_INTERCEPT(__cxa_atexit); // Need to setup it, because interceptors check that the function is resolved. // But atexit is emitted directly into the module, so can't be resolved. @@ -1833,7 +1995,7 @@ void InitializeInterceptors() { atexit_ctx = new(internal_alloc(MBlockAtExit, sizeof(AtExitContext))) AtExitContext(); - if (__cxa_atexit(&finalize, 0, 0)) { + if (REAL(__cxa_atexit)(&finalize, 0, 0)) { Printf("ThreadSanitizer: failed to setup atexit callback\n"); Die(); } diff --git a/libsanitizer/tsan/tsan_interface.cc b/libsanitizer/tsan/tsan_interface.cc index 08a51651114..992e3834aae 100644 --- a/libsanitizer/tsan/tsan_interface.cc +++ b/libsanitizer/tsan/tsan_interface.cc @@ -22,13 +22,13 @@ void __tsan_init() { } void __tsan_read16(void *addr) { - MemoryRead8Byte(cur_thread(), CALLERPC, (uptr)addr); - MemoryRead8Byte(cur_thread(), CALLERPC, (uptr)addr + 8); + MemoryRead(cur_thread(), CALLERPC, (uptr)addr, kSizeLog8); + MemoryRead(cur_thread(), CALLERPC, (uptr)addr + 8, kSizeLog8); } void __tsan_write16(void *addr) { - MemoryWrite8Byte(cur_thread(), CALLERPC, (uptr)addr); - MemoryWrite8Byte(cur_thread(), CALLERPC, (uptr)addr + 8); + MemoryWrite(cur_thread(), CALLERPC, (uptr)addr, kSizeLog8); + MemoryWrite(cur_thread(), CALLERPC, (uptr)addr + 8, kSizeLog8); } void __tsan_acquire(void *addr) { diff --git a/libsanitizer/tsan/tsan_interface.h b/libsanitizer/tsan/tsan_interface.h index 8f265ca27b5..2cfd7684183 100644 --- a/libsanitizer/tsan/tsan_interface.h +++ b/libsanitizer/tsan/tsan_interface.h @@ -14,7 +14,7 @@ #ifndef TSAN_INTERFACE_H #define TSAN_INTERFACE_H -#include <sanitizer/common_interface_defs.h> +#include <sanitizer_common/sanitizer_internal_defs.h> // This header should NOT include any other headers. // All functions in this header are extern "C" and start with __tsan_. diff --git a/libsanitizer/tsan/tsan_interface_ann.h b/libsanitizer/tsan/tsan_interface_ann.h index b10264cdd6f..b6500329428 100644 --- a/libsanitizer/tsan/tsan_interface_ann.h +++ b/libsanitizer/tsan/tsan_interface_ann.h @@ -12,7 +12,7 @@ #ifndef TSAN_INTERFACE_ANN_H #define TSAN_INTERFACE_ANN_H -#include <sanitizer/common_interface_defs.h> +#include <sanitizer_common/sanitizer_internal_defs.h> // This header should NOT include any other headers. // All functions in this header are extern "C" and start with __tsan_. diff --git a/libsanitizer/tsan/tsan_interface_atomic.cc b/libsanitizer/tsan/tsan_interface_atomic.cc index 770f8bd1014..2c8b2ab049c 100644 --- a/libsanitizer/tsan/tsan_interface_atomic.cc +++ b/libsanitizer/tsan/tsan_interface_atomic.cc @@ -18,25 +18,42 @@ // http://www.hpl.hp.com/personal/Hans_Boehm/c++mm/ #include "sanitizer_common/sanitizer_placement_new.h" +#include "sanitizer_common/sanitizer_stacktrace.h" #include "tsan_interface_atomic.h" #include "tsan_flags.h" #include "tsan_rtl.h" using namespace __tsan; // NOLINT +#define SCOPED_ATOMIC(func, ...) \ + const uptr callpc = (uptr)__builtin_return_address(0); \ + uptr pc = __sanitizer::StackTrace::GetCurrentPc(); \ + pc = __sanitizer::StackTrace::GetPreviousInstructionPc(pc); \ + mo = ConvertOrder(mo); \ + mo = flags()->force_seq_cst_atomics ? (morder)mo_seq_cst : mo; \ + ThreadState *const thr = cur_thread(); \ + AtomicStatInc(thr, sizeof(*a), mo, StatAtomic##func); \ + ScopedAtomic sa(thr, callpc, __FUNCTION__); \ + return Atomic##func(thr, pc, __VA_ARGS__); \ +/**/ + class ScopedAtomic { public: ScopedAtomic(ThreadState *thr, uptr pc, const char *func) : thr_(thr) { - CHECK_EQ(thr_->in_rtl, 1); // 1 due to our own ScopedInRtl member. + CHECK_EQ(thr_->in_rtl, 0); + ProcessPendingSignals(thr); + FuncEntry(thr_, pc); DPrintf("#%d: %s\n", thr_->tid, func); + thr_->in_rtl++; } ~ScopedAtomic() { - CHECK_EQ(thr_->in_rtl, 1); + thr_->in_rtl--; + CHECK_EQ(thr_->in_rtl, 0); + FuncExit(thr_); } private: ThreadState *thr_; - ScopedInRtl in_rtl_; }; // Some shortcuts. @@ -210,16 +227,19 @@ a128 func_cas(volatile a128 *v, a128 cmp, a128 xch) { } #endif -#define SCOPED_ATOMIC(func, ...) \ - mo = ConvertOrder(mo); \ - mo = flags()->force_seq_cst_atomics ? (morder)mo_seq_cst : mo; \ - ThreadState *const thr = cur_thread(); \ - ProcessPendingSignals(thr); \ - const uptr pc = (uptr)__builtin_return_address(0); \ - AtomicStatInc(thr, sizeof(*a), mo, StatAtomic##func); \ - ScopedAtomic sa(thr, pc, __FUNCTION__); \ - return Atomic##func(thr, pc, __VA_ARGS__); \ -/**/ +template<typename T> +static int SizeLog() { + if (sizeof(T) <= 1) + return kSizeLog1; + else if (sizeof(T) <= 2) + return kSizeLog2; + else if (sizeof(T) <= 4) + return kSizeLog4; + else + return kSizeLog8; + // For 16-byte atomics we also use 8-byte memory access, + // this leads to false negatives only in very obscure cases. +} template<typename T> static T AtomicLoad(ThreadState *thr, uptr pc, const volatile T *a, @@ -227,14 +247,17 @@ static T AtomicLoad(ThreadState *thr, uptr pc, const volatile T *a, CHECK(IsLoadOrder(mo)); // This fast-path is critical for performance. // Assume the access is atomic. - if (!IsAcquireOrder(mo) && sizeof(T) <= sizeof(a)) + if (!IsAcquireOrder(mo) && sizeof(T) <= sizeof(a)) { + MemoryReadAtomic(thr, pc, (uptr)a, SizeLog<T>()); return *a; + } SyncVar *s = CTX()->synctab.GetOrCreateAndLock(thr, pc, (uptr)a, false); thr->clock.set(thr->tid, thr->fast_state.epoch()); thr->clock.acquire(&s->clock); T v = *a; s->mtx.ReadUnlock(); __sync_synchronize(); + MemoryReadAtomic(thr, pc, (uptr)a, SizeLog<T>()); return v; } @@ -242,6 +265,7 @@ template<typename T> static void AtomicStore(ThreadState *thr, uptr pc, volatile T *a, T v, morder mo) { CHECK(IsStoreOrder(mo)); + MemoryWriteAtomic(thr, pc, (uptr)a, SizeLog<T>()); // This fast-path is critical for performance. // Assume the access is atomic. // Strictly saying even relaxed store cuts off release sequence, @@ -263,6 +287,7 @@ static void AtomicStore(ThreadState *thr, uptr pc, volatile T *a, T v, template<typename T, T (*F)(volatile T *v, T op)> static T AtomicRMW(ThreadState *thr, uptr pc, volatile T *a, T v, morder mo) { + MemoryWriteAtomic(thr, pc, (uptr)a, SizeLog<T>()); SyncVar *s = CTX()->synctab.GetOrCreateAndLock(thr, pc, (uptr)a, true); thr->clock.set(thr->tid, thr->fast_state.epoch()); if (IsAcqRelOrder(mo)) @@ -322,6 +347,7 @@ template<typename T> static bool AtomicCAS(ThreadState *thr, uptr pc, volatile T *a, T *c, T v, morder mo, morder fmo) { (void)fmo; // Unused because llvm does not pass it yet. + MemoryWriteAtomic(thr, pc, (uptr)a, SizeLog<T>()); SyncVar *s = CTX()->synctab.GetOrCreateAndLock(thr, pc, (uptr)a, true); thr->clock.set(thr->tid, thr->fast_state.epoch()); if (IsAcqRelOrder(mo)) diff --git a/libsanitizer/tsan/tsan_interface_inl.h b/libsanitizer/tsan/tsan_interface_inl.h index 133348a942c..92796d1178f 100644 --- a/libsanitizer/tsan/tsan_interface_inl.h +++ b/libsanitizer/tsan/tsan_interface_inl.h @@ -17,41 +17,41 @@ using namespace __tsan; // NOLINT void __tsan_read1(void *addr) { - MemoryAccess(cur_thread(), CALLERPC, (uptr)addr, 0, 0); + MemoryRead(cur_thread(), CALLERPC, (uptr)addr, kSizeLog1); } void __tsan_read2(void *addr) { - MemoryAccess(cur_thread(), CALLERPC, (uptr)addr, 1, 0); + MemoryRead(cur_thread(), CALLERPC, (uptr)addr, kSizeLog2); } void __tsan_read4(void *addr) { - MemoryAccess(cur_thread(), CALLERPC, (uptr)addr, 2, 0); + MemoryRead(cur_thread(), CALLERPC, (uptr)addr, kSizeLog4); } void __tsan_read8(void *addr) { - MemoryAccess(cur_thread(), CALLERPC, (uptr)addr, 3, 0); + MemoryRead(cur_thread(), CALLERPC, (uptr)addr, kSizeLog8); } void __tsan_write1(void *addr) { - MemoryAccess(cur_thread(), CALLERPC, (uptr)addr, 0, 1); + MemoryWrite(cur_thread(), CALLERPC, (uptr)addr, kSizeLog1); } void __tsan_write2(void *addr) { - MemoryAccess(cur_thread(), CALLERPC, (uptr)addr, 1, 1); + MemoryWrite(cur_thread(), CALLERPC, (uptr)addr, kSizeLog2); } void __tsan_write4(void *addr) { - MemoryAccess(cur_thread(), CALLERPC, (uptr)addr, 2, 1); + MemoryWrite(cur_thread(), CALLERPC, (uptr)addr, kSizeLog4); } void __tsan_write8(void *addr) { - MemoryAccess(cur_thread(), CALLERPC, (uptr)addr, 3, 1); + MemoryWrite(cur_thread(), CALLERPC, (uptr)addr, kSizeLog8); } void __tsan_vptr_update(void **vptr_p, void *new_val) { CHECK_EQ(sizeof(vptr_p), 8); if (*vptr_p != new_val) - MemoryAccess(cur_thread(), CALLERPC, (uptr)vptr_p, 3, 1); + MemoryWrite(cur_thread(), CALLERPC, (uptr)vptr_p, kSizeLog8); } void __tsan_func_entry(void *pc) { diff --git a/libsanitizer/tsan/tsan_interface_java.cc b/libsanitizer/tsan/tsan_interface_java.cc index d7325dcb2c4..f8c0b4eb635 100644 --- a/libsanitizer/tsan/tsan_interface_java.cc +++ b/libsanitizer/tsan/tsan_interface_java.cc @@ -150,7 +150,7 @@ SyncVar* GetAndRemoveJavaSync(ThreadState *thr, uptr pc, uptr addr) { return 0; } -} // namespace __tsan { +} // namespace __tsan #define SCOPED_JAVA_FUNC(func) \ ThreadState *thr = cur_thread(); \ diff --git a/libsanitizer/tsan/tsan_md5.cc b/libsanitizer/tsan/tsan_md5.cc index 6df823dae98..883239c2e71 100644 --- a/libsanitizer/tsan/tsan_md5.cc +++ b/libsanitizer/tsan/tsan_md5.cc @@ -240,4 +240,4 @@ MD5Hash md5_hash(const void *data, uptr size) { MD5_Final((unsigned char*)&res.hash[0], &ctx); return res; } -} +} // namespace __tsan diff --git a/libsanitizer/tsan/tsan_mman.cc b/libsanitizer/tsan/tsan_mman.cc index f4fafaf77f0..23c73c50cc1 100644 --- a/libsanitizer/tsan/tsan_mman.cc +++ b/libsanitizer/tsan/tsan_mman.cc @@ -36,8 +36,16 @@ void InitializeAllocator() { allocator()->Init(); } -void AlloctorThreadFinish(ThreadState *thr) { - allocator()->SwallowCache(&thr->alloc_cache); +void AllocatorThreadStart(ThreadState *thr) { + allocator()->InitCache(&thr->alloc_cache); +} + +void AllocatorThreadFinish(ThreadState *thr) { + allocator()->DestroyCache(&thr->alloc_cache); +} + +void AllocatorPrintStats() { + allocator()->PrintStats(); } static void SignalUnsafeCall(ThreadState *thr, uptr pc) { @@ -162,3 +170,49 @@ void internal_free(void *p) { } } // namespace __tsan + +using namespace __tsan; + +extern "C" { +uptr __tsan_get_current_allocated_bytes() { + u64 stats[AllocatorStatCount]; + allocator()->GetStats(stats); + u64 m = stats[AllocatorStatMalloced]; + u64 f = stats[AllocatorStatFreed]; + return m >= f ? m - f : 1; +} + +uptr __tsan_get_heap_size() { + u64 stats[AllocatorStatCount]; + allocator()->GetStats(stats); + u64 m = stats[AllocatorStatMmapped]; + u64 f = stats[AllocatorStatUnmapped]; + return m >= f ? m - f : 1; +} + +uptr __tsan_get_free_bytes() { + return 1; +} + +uptr __tsan_get_unmapped_bytes() { + return 1; +} + +uptr __tsan_get_estimated_allocated_size(uptr size) { + return size; +} + +bool __tsan_get_ownership(void *p) { + return allocator()->GetBlockBegin(p) != 0; +} + +uptr __tsan_get_allocated_size(void *p) { + if (p == 0) + return 0; + p = allocator()->GetBlockBegin(p); + if (p == 0) + return 0; + MBlock *b = (MBlock*)allocator()->GetMetaData(p); + return b->size; +} +} // extern "C" diff --git a/libsanitizer/tsan/tsan_mman.h b/libsanitizer/tsan/tsan_mman.h index 8697d228730..7a657e124bf 100644 --- a/libsanitizer/tsan/tsan_mman.h +++ b/libsanitizer/tsan/tsan_mman.h @@ -18,7 +18,9 @@ namespace __tsan { const uptr kDefaultAlignment = 16; void InitializeAllocator(); -void AlloctorThreadFinish(ThreadState *thr); +void AllocatorThreadStart(ThreadState *thr); +void AllocatorThreadFinish(ThreadState *thr); +void AllocatorPrintStats(); // For user allocations. void *user_alloc(ThreadState *thr, uptr pc, uptr sz, diff --git a/libsanitizer/tsan/tsan_platform.h b/libsanitizer/tsan/tsan_platform.h index 9fdc4dd46e7..78c1a7d8195 100644 --- a/libsanitizer/tsan/tsan_platform.h +++ b/libsanitizer/tsan/tsan_platform.h @@ -149,6 +149,7 @@ bool IsGlobalVar(uptr addr); uptr GetTlsSize(); void GetThreadStackAndTls(bool main, uptr *stk_addr, uptr *stk_size, uptr *tls_addr, uptr *tls_size); +int ExtractResolvFDs(void *state, int *fds, int nfd); } // namespace __tsan diff --git a/libsanitizer/tsan/tsan_platform_linux.cc b/libsanitizer/tsan/tsan_platform_linux.cc index 2e7cd5138d6..def91559d5e 100644 --- a/libsanitizer/tsan/tsan_platform_linux.cc +++ b/libsanitizer/tsan/tsan_platform_linux.cc @@ -38,6 +38,8 @@ #include <errno.h> #include <sched.h> #include <dlfcn.h> +#define __need_res_state +#include <resolv.h> extern "C" int arch_prctl(int code, __sanitizer::uptr *addr); @@ -287,6 +289,19 @@ bool IsGlobalVar(uptr addr) { return g_data_start && addr >= g_data_start && addr < g_data_end; } +#ifndef TSAN_GO +int ExtractResolvFDs(void *state, int *fds, int nfd) { + int cnt = 0; + __res_state *statp = (__res_state*)state; + for (int i = 0; i < MAXNS && cnt < nfd; i++) { + if (statp->_u._ext.nsaddrs[i] && statp->_u._ext.nssocks[i] != -1) + fds[cnt++] = statp->_u._ext.nssocks[i]; + } + return cnt; +} +#endif + + } // namespace __tsan #endif // #ifdef __linux__ diff --git a/libsanitizer/tsan/tsan_report.cc b/libsanitizer/tsan/tsan_report.cc index f99fd2ea105..098d8262ba1 100644 --- a/libsanitizer/tsan/tsan_report.cc +++ b/libsanitizer/tsan/tsan_report.cc @@ -41,23 +41,20 @@ const char *thread_name(char *buf, int tid) { return buf; } -static void PrintHeader(ReportType typ) { - Printf("WARNING: ThreadSanitizer: "); - +static const char *ReportTypeString(ReportType typ) { if (typ == ReportTypeRace) - Printf("data race"); - else if (typ == ReportTypeUseAfterFree) - Printf("heap-use-after-free"); - else if (typ == ReportTypeThreadLeak) - Printf("thread leak"); - else if (typ == ReportTypeMutexDestroyLocked) - Printf("destroy of a locked mutex"); - else if (typ == ReportTypeSignalUnsafe) - Printf("signal-unsafe call inside of a signal"); - else if (typ == ReportTypeErrnoInSignal) - Printf("signal handler spoils errno"); - - Printf(" (pid=%d)\n", GetPid()); + return "data race"; + if (typ == ReportTypeUseAfterFree) + return "heap-use-after-free"; + if (typ == ReportTypeThreadLeak) + return "thread leak"; + if (typ == ReportTypeMutexDestroyLocked) + return "destroy of a locked mutex"; + if (typ == ReportTypeSignalUnsafe) + return "signal-unsafe call inside of a signal"; + if (typ == ReportTypeErrnoInSignal) + return "signal handler spoils errno"; + return ""; } void PrintStack(const ReportStack *ent) { @@ -87,11 +84,17 @@ static void PrintMutexSet(Vector<ReportMopMutex> const& mset) { } } +static const char *MopDesc(bool first, bool write, bool atomic) { + return atomic ? (first ? (write ? "Atomic write" : "Atomic read") + : (write ? "Previous atomic write" : "Previous atomic read")) + : (first ? (write ? "Write" : "Read") + : (write ? "Previous write" : "Previous read")); +} + static void PrintMop(const ReportMop *mop, bool first) { char thrbuf[kThreadBufSize]; Printf(" %s of size %d at %p by %s", - (first ? (mop->write ? "Write" : "Read") - : (mop->write ? "Previous write" : "Previous read")), + MopDesc(first, mop->write, mop->atomic), mop->size, (void*)mop->addr, thread_name(thrbuf, mop->tid)); PrintMutexSet(mop->mset); @@ -150,9 +153,28 @@ static void PrintSleep(const ReportStack *s) { PrintStack(s); } +static ReportStack *ChooseSummaryStack(const ReportDesc *rep) { + if (rep->mops.Size()) + return rep->mops[0]->stack; + if (rep->stacks.Size()) + return rep->stacks[0]; + if (rep->mutexes.Size()) + return rep->mutexes[0]->stack; + if (rep->threads.Size()) + return rep->threads[0]->stack; + return 0; +} + +ReportStack *SkipTsanInternalFrames(ReportStack *ent) { + while (FrameIsInternal(ent) && ent->next) + ent = ent->next; + return ent; +} + void PrintReport(const ReportDesc *rep) { Printf("==================\n"); - PrintHeader(rep->typ); + const char *rep_typ_str = ReportTypeString(rep->typ); + Printf("WARNING: ThreadSanitizer: %s (pid=%d)\n", rep_typ_str, GetPid()); for (uptr i = 0; i < rep->stacks.Size(); i++) { if (i) @@ -175,6 +197,9 @@ void PrintReport(const ReportDesc *rep) { for (uptr i = 0; i < rep->threads.Size(); i++) PrintThread(rep->threads[i]); + if (ReportStack *ent = SkipTsanInternalFrames(ChooseSummaryStack(rep))) + ReportErrorSummary(rep_typ_str, ent->file, ent->line, ent->func); + Printf("==================\n"); } diff --git a/libsanitizer/tsan/tsan_report.h b/libsanitizer/tsan/tsan_report.h index 42f52af9e37..eae2b3c721f 100644 --- a/libsanitizer/tsan/tsan_report.h +++ b/libsanitizer/tsan/tsan_report.h @@ -46,6 +46,7 @@ struct ReportMop { uptr addr; int size; bool write; + bool atomic; Vector<ReportMopMutex> mset; ReportStack *stack; diff --git a/libsanitizer/tsan/tsan_rtl.cc b/libsanitizer/tsan/tsan_rtl.cc index 3615a7a9c2f..673a355f1dc 100644 --- a/libsanitizer/tsan/tsan_rtl.cc +++ b/libsanitizer/tsan/tsan_rtl.cc @@ -35,6 +35,11 @@ THREADLOCAL char cur_thread_placeholder[sizeof(ThreadState)] ALIGNED(64); #endif static char ctx_placeholder[sizeof(Context)] ALIGNED(64); +// Can be overriden by a front-end. +bool CPP_WEAK OnFinalize(bool failed) { + return failed; +} + static Context *ctx; Context *CTX() { return ctx; @@ -136,7 +141,7 @@ static void InitializeMemoryProfile() { InternalScopedBuffer<char> filename(4096); internal_snprintf(filename.data(), filename.size(), "%s.%d", flags()->profile_memory, GetPid()); - fd_t fd = internal_open(filename.data(), true); + fd_t fd = OpenFile(filename.data(), true); if (fd == kInvalidFd) { Printf("Failed to open memory profile file '%s'\n", &filename[0]); Die(); @@ -180,6 +185,7 @@ void Initialize(ThreadState *thr) { if (is_initialized) return; is_initialized = true; + SanitizerToolName = "ThreadSanitizer"; // Install tool-specific callbacks in sanitizer_common. SetCheckFailedCallback(TsanCheckFailed); @@ -237,7 +243,7 @@ void Initialize(ThreadState *thr) { Printf("ThreadSanitizer is suspended at startup (pid %d)." " Call __tsan_resume().\n", GetPid()); - while (__tsan_resumed == 0); + while (__tsan_resumed == 0) {} } } @@ -253,6 +259,11 @@ int Finalize(ThreadState *thr) { ctx->report_mtx.Lock(); ctx->report_mtx.Unlock(); +#ifndef TSAN_GO + if (ctx->flags.verbosity) + AllocatorPrintStats(); +#endif + ThreadFinalize(thr); if (ctx->nreported) { @@ -270,6 +281,8 @@ int Finalize(ThreadState *thr) { ctx->nmissed_expected); } + failed = OnFinalize(failed); + StatAggregate(ctx->stat, thr->stat); StatOutput(ctx->stat); return failed ? flags()->exitcode : 0; @@ -356,18 +369,6 @@ static inline void HandleRace(ThreadState *thr, u64 *shadow_mem, #endif } -static inline bool BothReads(Shadow s, int kAccessIsWrite) { - return !kAccessIsWrite && !s.is_write(); -} - -static inline bool OldIsRWNotWeaker(Shadow old, int kAccessIsWrite) { - return old.is_write() || !kAccessIsWrite; -} - -static inline bool OldIsRWWeakerOrEqual(Shadow old, int kAccessIsWrite) { - return !old.is_write() || kAccessIsWrite; -} - static inline bool OldIsInSameSynchEpoch(Shadow old, ThreadState *thr) { return old.epoch() >= thr->fast_synch_epoch; } @@ -378,7 +379,7 @@ static inline bool HappensBefore(Shadow old, ThreadState *thr) { ALWAYS_INLINE void MemoryAccessImpl(ThreadState *thr, uptr addr, - int kAccessSizeLog, bool kAccessIsWrite, + int kAccessSizeLog, bool kAccessIsWrite, bool kIsAtomic, u64 *shadow_mem, Shadow cur) { StatInc(thr, StatMop); StatInc(thr, kAccessIsWrite ? StatMopWrite : StatMopRead); @@ -452,7 +453,7 @@ void MemoryAccessImpl(ThreadState *thr, uptr addr, ALWAYS_INLINE void MemoryAccess(ThreadState *thr, uptr pc, uptr addr, - int kAccessSizeLog, bool kAccessIsWrite) { + int kAccessSizeLog, bool kAccessIsWrite, bool kIsAtomic) { u64 *shadow_mem = (u64*)MemToShadow(addr); DPrintf2("#%d: MemoryAccess: @%p %p size=%d" " is_write=%d shadow_mem=%p {%zx, %zx, %zx, %zx}\n", @@ -479,12 +480,13 @@ void MemoryAccess(ThreadState *thr, uptr pc, uptr addr, Shadow cur(fast_state); cur.SetAddr0AndSizeLog(addr & 7, kAccessSizeLog); cur.SetWrite(kAccessIsWrite); + cur.SetAtomic(kIsAtomic); // We must not store to the trace if we do not store to the shadow. // That is, this call must be moved somewhere below. TraceAddEvent(thr, fast_state, EventTypeMop, pc); - MemoryAccessImpl(thr, addr, kAccessSizeLog, kAccessIsWrite, + MemoryAccessImpl(thr, addr, kAccessSizeLog, kAccessIsWrite, kIsAtomic, shadow_mem, cur); } @@ -531,7 +533,10 @@ void MemoryResetRange(ThreadState *thr, uptr pc, uptr addr, uptr size) { } void MemoryRangeFreed(ThreadState *thr, uptr pc, uptr addr, uptr size) { + CHECK_EQ(thr->is_freeing, false); + thr->is_freeing = true; MemoryAccessRange(thr, pc, addr, size, true); + thr->is_freeing = false; Shadow s(thr->fast_state); s.ClearIgnoreBit(); s.MarkAsFreed(); diff --git a/libsanitizer/tsan/tsan_rtl.h b/libsanitizer/tsan/tsan_rtl.h index bb2fe56bfe7..717f4599705 100644 --- a/libsanitizer/tsan/tsan_rtl.h +++ b/libsanitizer/tsan/tsan_rtl.h @@ -171,7 +171,8 @@ class FastState { // freed : 1 // tid : kTidBits // epoch : kClkBits -// is_write : 1 +// is_atomic : 1 +// is_read : 1 // size_log : 2 // addr0 : 3 class Shadow : public FastState { @@ -195,13 +196,26 @@ class Shadow : public FastState { } void SetWrite(unsigned kAccessIsWrite) { - DCHECK_EQ(x_ & 32, 0); - if (kAccessIsWrite) - x_ |= 32; - DCHECK_EQ(kAccessIsWrite, is_write()); + DCHECK_EQ(x_ & kReadBit, 0); + if (!kAccessIsWrite) + x_ |= kReadBit; + DCHECK_EQ(kAccessIsWrite, IsWrite()); } - bool IsZero() const { return x_ == 0; } + void SetAtomic(bool kIsAtomic) { + DCHECK(!IsAtomic()); + if (kIsAtomic) + x_ |= kAtomicBit; + DCHECK_EQ(IsAtomic(), kIsAtomic); + } + + bool IsAtomic() const { + return x_ & kAtomicBit; + } + + bool IsZero() const { + return x_ == 0; + } static inline bool TidsAreEqual(const Shadow s1, const Shadow s2) { u64 shifted_xor = (s1.x_ ^ s2.x_) >> kTidShift; @@ -248,7 +262,8 @@ class Shadow : public FastState { } u64 addr0() const { return x_ & 7; } u64 size() const { return 1ull << size_log(); } - bool is_write() const { return x_ & 32; } + bool IsWrite() const { return !IsRead(); } + bool IsRead() const { return x_ & kReadBit; } // The idea behind the freed bit is as follows. // When the memory is freed (or otherwise unaccessible) we write to the shadow @@ -263,13 +278,46 @@ class Shadow : public FastState { x_ |= kFreedBit; } + bool IsFreed() const { + return x_ & kFreedBit; + } + bool GetFreedAndReset() { bool res = x_ & kFreedBit; x_ &= ~kFreedBit; return res; } + bool IsBothReadsOrAtomic(bool kIsWrite, bool kIsAtomic) const { + // analyzes 5-th bit (is_read) and 6-th bit (is_atomic) + bool v = x_ & u64(((kIsWrite ^ 1) << kReadShift) + | (kIsAtomic << kAtomicShift)); + DCHECK_EQ(v, (!IsWrite() && !kIsWrite) || (IsAtomic() && kIsAtomic)); + return v; + } + + bool IsRWNotWeaker(bool kIsWrite, bool kIsAtomic) const { + bool v = ((x_ >> kReadShift) & 3) + <= u64((kIsWrite ^ 1) | (kIsAtomic << 1)); + DCHECK_EQ(v, (IsAtomic() < kIsAtomic) || + (IsAtomic() == kIsAtomic && !IsWrite() <= !kIsWrite)); + return v; + } + + bool IsRWWeakerOrEqual(bool kIsWrite, bool kIsAtomic) const { + bool v = ((x_ >> kReadShift) & 3) + >= u64((kIsWrite ^ 1) | (kIsAtomic << 1)); + DCHECK_EQ(v, (IsAtomic() > kIsAtomic) || + (IsAtomic() == kIsAtomic && !IsWrite() >= !kIsWrite)); + return v; + } + private: + static const u64 kReadShift = 5; + static const u64 kReadBit = 1ull << kReadShift; + static const u64 kAtomicShift = 6; + static const u64 kAtomicBit = 1ull << kAtomicShift; + u64 size_log() const { return (x_ >> 3) & 3; } static bool TwoRangesIntersectSLOW(const Shadow s1, const Shadow s2) { @@ -324,7 +372,9 @@ struct ThreadState { const int tid; const int unique_id; int in_rtl; + bool in_symbolizer; bool is_alive; + bool is_freeing; const uptr stk_addr; const uptr stk_size; const uptr tls_addr; @@ -501,11 +551,14 @@ void InitializeDynamicAnnotations(); void ReportRace(ThreadState *thr); bool OutputReport(Context *ctx, const ScopedReport &srep, - const ReportStack *suppress_stack = 0); + const ReportStack *suppress_stack1 = 0, + const ReportStack *suppress_stack2 = 0); bool IsFiredSuppression(Context *ctx, const ScopedReport &srep, const StackTrace &trace); bool IsExpectedReport(uptr addr, uptr size); +bool FrameIsInternal(const ReportStack *frame); +ReportStack *SkipTsanInternalFrames(ReportStack *ent); #if defined(TSAN_DEBUG_OUTPUT) && TSAN_DEBUG_OUTPUT >= 1 # define DPrintf Printf @@ -521,6 +574,7 @@ bool IsExpectedReport(uptr addr, uptr size); u32 CurrentStackId(ThreadState *thr, uptr pc); void PrintCurrentStack(ThreadState *thr, uptr pc); +void PrintCurrentStackSlow(); // uses libunwind void Initialize(ThreadState *thr); int Finalize(ThreadState *thr); @@ -530,16 +584,38 @@ SyncVar* GetJavaSync(ThreadState *thr, uptr pc, uptr addr, SyncVar* GetAndRemoveJavaSync(ThreadState *thr, uptr pc, uptr addr); void MemoryAccess(ThreadState *thr, uptr pc, uptr addr, - int kAccessSizeLog, bool kAccessIsWrite); + int kAccessSizeLog, bool kAccessIsWrite, bool kIsAtomic); void MemoryAccessImpl(ThreadState *thr, uptr addr, - int kAccessSizeLog, bool kAccessIsWrite, + int kAccessSizeLog, bool kAccessIsWrite, bool kIsAtomic, u64 *shadow_mem, Shadow cur); -void MemoryRead1Byte(ThreadState *thr, uptr pc, uptr addr); -void MemoryWrite1Byte(ThreadState *thr, uptr pc, uptr addr); -void MemoryRead8Byte(ThreadState *thr, uptr pc, uptr addr); -void MemoryWrite8Byte(ThreadState *thr, uptr pc, uptr addr); void MemoryAccessRange(ThreadState *thr, uptr pc, uptr addr, - uptr size, bool is_write); + uptr size, bool is_write); + +const int kSizeLog1 = 0; +const int kSizeLog2 = 1; +const int kSizeLog4 = 2; +const int kSizeLog8 = 3; + +void ALWAYS_INLINE INLINE MemoryRead(ThreadState *thr, uptr pc, + uptr addr, int kAccessSizeLog) { + MemoryAccess(thr, pc, addr, kAccessSizeLog, false, false); +} + +void ALWAYS_INLINE INLINE MemoryWrite(ThreadState *thr, uptr pc, + uptr addr, int kAccessSizeLog) { + MemoryAccess(thr, pc, addr, kAccessSizeLog, true, false); +} + +void ALWAYS_INLINE INLINE MemoryReadAtomic(ThreadState *thr, uptr pc, + uptr addr, int kAccessSizeLog) { + MemoryAccess(thr, pc, addr, kAccessSizeLog, false, true); +} + +void ALWAYS_INLINE INLINE MemoryWriteAtomic(ThreadState *thr, uptr pc, + uptr addr, int kAccessSizeLog) { + MemoryAccess(thr, pc, addr, kAccessSizeLog, true, true); +} + void MemoryResetRange(ThreadState *thr, uptr pc, uptr addr, uptr size); void MemoryRangeFreed(ThreadState *thr, uptr pc, uptr addr, uptr size); void MemoryRangeImitateWrite(ThreadState *thr, uptr pc, uptr addr, uptr size); diff --git a/libsanitizer/tsan/tsan_rtl_mutex.cc b/libsanitizer/tsan/tsan_rtl_mutex.cc index db97e1d9853..22a71503c5c 100644 --- a/libsanitizer/tsan/tsan_rtl_mutex.cc +++ b/libsanitizer/tsan/tsan_rtl_mutex.cc @@ -24,8 +24,12 @@ void MutexCreate(ThreadState *thr, uptr pc, uptr addr, CHECK_GT(thr->in_rtl, 0); DPrintf("#%d: MutexCreate %zx\n", thr->tid, addr); StatInc(thr, StatMutexCreate); - if (!linker_init && IsAppMem(addr)) - MemoryWrite1Byte(thr, pc, addr); + if (!linker_init && IsAppMem(addr)) { + CHECK(!thr->is_freeing); + thr->is_freeing = true; + MemoryWrite(thr, pc, addr, kSizeLog1); + thr->is_freeing = false; + } SyncVar *s = ctx->synctab.GetOrCreateAndLock(thr, pc, addr, true); s->is_rw = rw; s->is_recursive = recursive; @@ -47,8 +51,12 @@ void MutexDestroy(ThreadState *thr, uptr pc, uptr addr) { SyncVar *s = ctx->synctab.GetAndRemove(thr, pc, addr); if (s == 0) return; - if (IsAppMem(addr)) - MemoryWrite1Byte(thr, pc, addr); + if (IsAppMem(addr)) { + CHECK(!thr->is_freeing); + thr->is_freeing = true; + MemoryWrite(thr, pc, addr, kSizeLog1); + thr->is_freeing = false; + } if (flags()->report_destroy_locked && s->owner_tid != SyncVar::kInvalidTid && !s->is_broken) { @@ -73,7 +81,7 @@ void MutexLock(ThreadState *thr, uptr pc, uptr addr) { CHECK_GT(thr->in_rtl, 0); DPrintf("#%d: MutexLock %zx\n", thr->tid, addr); if (IsAppMem(addr)) - MemoryRead1Byte(thr, pc, addr); + MemoryReadAtomic(thr, pc, addr, kSizeLog1); SyncVar *s = CTX()->synctab.GetOrCreateAndLock(thr, pc, addr, true); thr->fast_state.IncrementEpoch(); TraceAddEvent(thr, thr->fast_state, EventTypeLock, s->GetId()); @@ -106,7 +114,7 @@ void MutexUnlock(ThreadState *thr, uptr pc, uptr addr) { CHECK_GT(thr->in_rtl, 0); DPrintf("#%d: MutexUnlock %zx\n", thr->tid, addr); if (IsAppMem(addr)) - MemoryRead1Byte(thr, pc, addr); + MemoryReadAtomic(thr, pc, addr, kSizeLog1); SyncVar *s = CTX()->synctab.GetOrCreateAndLock(thr, pc, addr, true); thr->fast_state.IncrementEpoch(); TraceAddEvent(thr, thr->fast_state, EventTypeUnlock, s->GetId()); @@ -144,7 +152,7 @@ void MutexReadLock(ThreadState *thr, uptr pc, uptr addr) { DPrintf("#%d: MutexReadLock %zx\n", thr->tid, addr); StatInc(thr, StatMutexReadLock); if (IsAppMem(addr)) - MemoryRead1Byte(thr, pc, addr); + MemoryReadAtomic(thr, pc, addr, kSizeLog1); SyncVar *s = CTX()->synctab.GetOrCreateAndLock(thr, pc, addr, false); thr->fast_state.IncrementEpoch(); TraceAddEvent(thr, thr->fast_state, EventTypeRLock, s->GetId()); @@ -165,7 +173,7 @@ void MutexReadUnlock(ThreadState *thr, uptr pc, uptr addr) { DPrintf("#%d: MutexReadUnlock %zx\n", thr->tid, addr); StatInc(thr, StatMutexReadUnlock); if (IsAppMem(addr)) - MemoryRead1Byte(thr, pc, addr); + MemoryReadAtomic(thr, pc, addr, kSizeLog1); SyncVar *s = CTX()->synctab.GetOrCreateAndLock(thr, pc, addr, true); thr->fast_state.IncrementEpoch(); TraceAddEvent(thr, thr->fast_state, EventTypeRUnlock, s->GetId()); @@ -186,7 +194,7 @@ void MutexReadOrWriteUnlock(ThreadState *thr, uptr pc, uptr addr) { CHECK_GT(thr->in_rtl, 0); DPrintf("#%d: MutexReadOrWriteUnlock %zx\n", thr->tid, addr); if (IsAppMem(addr)) - MemoryRead1Byte(thr, pc, addr); + MemoryReadAtomic(thr, pc, addr, kSizeLog1); SyncVar *s = CTX()->synctab.GetOrCreateAndLock(thr, pc, addr, true); bool write = true; if (s->owner_tid == SyncVar::kInvalidTid) { diff --git a/libsanitizer/tsan/tsan_rtl_report.cc b/libsanitizer/tsan/tsan_rtl_report.cc index c4256da412a..ff1d43bc9e8 100644 --- a/libsanitizer/tsan/tsan_rtl_report.cc +++ b/libsanitizer/tsan/tsan_rtl_report.cc @@ -13,6 +13,7 @@ #include "sanitizer_common/sanitizer_placement_new.h" #include "sanitizer_common/sanitizer_stackdepot.h" #include "sanitizer_common/sanitizer_common.h" +#include "sanitizer_common/sanitizer_stacktrace.h" #include "tsan_platform.h" #include "tsan_rtl.h" #include "tsan_suppressions.h" @@ -27,12 +28,15 @@ namespace __tsan { using namespace __sanitizer; // NOLINT +static ReportStack *SymbolizeStack(const StackTrace& trace); + void TsanCheckFailed(const char *file, int line, const char *cond, u64 v1, u64 v2) { ScopedInRtl in_rtl; Printf("FATAL: ThreadSanitizer CHECK failed: " "%s:%d \"%s\" (0x%zx, 0x%zx)\n", file, line, cond, (uptr)v1, (uptr)v2); + PrintCurrentStackSlow(); Die(); } @@ -144,7 +148,8 @@ void ScopedReport::AddMemoryAccess(uptr addr, Shadow s, mop->tid = s.tid(); mop->addr = addr + s.addr0(); mop->size = s.size(); - mop->write = s.is_write(); + mop->write = s.IsWrite(); + mop->atomic = s.IsAtomic(); mop->stack = SymbolizeStack(*stack); for (uptr i = 0; i < mset->Size(); i++) { MutexSet::Desc d = mset->Get(i); @@ -458,9 +463,12 @@ static void AddRacyStacks(ThreadState *thr, const StackTrace (&traces)[2], bool OutputReport(Context *ctx, const ScopedReport &srep, - const ReportStack *suppress_stack) { + const ReportStack *suppress_stack1, + const ReportStack *suppress_stack2) { const ReportDesc *rep = srep.GetReport(); - const uptr suppress_pc = IsSuppressed(rep->typ, suppress_stack); + uptr suppress_pc = IsSuppressed(rep->typ, suppress_stack1); + if (suppress_pc == 0) + suppress_pc = IsSuppressed(rep->typ, suppress_stack2); if (suppress_pc != 0) { FiredSuppression supp = {srep.GetReport()->typ, suppress_pc}; ctx->fired_suppressions.PushBack(supp); @@ -486,6 +494,13 @@ bool IsFiredSuppression(Context *ctx, return false; } +bool FrameIsInternal(const ReportStack *frame) { + return frame != 0 && frame->file != 0 + && (internal_strstr(frame->file, "tsan_interceptors.cc") || + internal_strstr(frame->file, "sanitizer_common_interceptors.inc") || + internal_strstr(frame->file, "tsan_interface_")); +} + // On programs that use Java we see weird reports like: // WARNING: ThreadSanitizer: data race (pid=22512) // Read of size 8 at 0x7d2b00084318 by thread 100: @@ -495,22 +510,20 @@ bool IsFiredSuppression(Context *ctx, // #0 strncpy tsan_interceptors.cc:501 (foo+0x00000d8e0919) // #1 <null> <null>:0 (0x7f7ad9b42707) static bool IsJavaNonsense(const ReportDesc *rep) { +#ifndef TSAN_GO for (uptr i = 0; i < rep->mops.Size(); i++) { ReportMop *mop = rep->mops[i]; ReportStack *frame = mop->stack; - if (frame != 0 && frame->func != 0 - && (internal_strcmp(frame->func, "memset") == 0 - || internal_strcmp(frame->func, "memcpy") == 0 - || internal_strcmp(frame->func, "memmove") == 0 - || internal_strcmp(frame->func, "strcmp") == 0 - || internal_strcmp(frame->func, "strncpy") == 0 - || internal_strcmp(frame->func, "strlen") == 0 - || internal_strcmp(frame->func, "free") == 0 - || internal_strcmp(frame->func, "pthread_mutex_lock") == 0)) { + if (frame == 0 + || (frame->func == 0 && frame->file == 0 && frame->line == 0 + && frame->module == 0)) { + return true; + } + if (FrameIsInternal(frame)) { frame = frame->next; if (frame == 0 || (frame->func == 0 && frame->file == 0 && frame->line == 0 - && frame->module == 0)) { + && frame->module == 0)) { if (frame) { FiredSuppression supp = {rep->typ, frame->pc}; CTX()->fired_suppressions.PushBack(supp); @@ -519,6 +532,20 @@ static bool IsJavaNonsense(const ReportDesc *rep) { } } } +#endif + return false; +} + +static bool RaceBetweenAtomicAndFree(ThreadState *thr) { + Shadow s0(thr->racy_state[0]); + Shadow s1(thr->racy_state[1]); + CHECK(!(s0.IsAtomic() && s1.IsAtomic())); + if (!s0.IsAtomic() && !s1.IsAtomic()) + return true; + if (s0.IsAtomic() && s1.IsFreed()) + return true; + if (s1.IsAtomic() && thr->is_freeing) + return true; return false; } @@ -527,6 +554,9 @@ void ReportRace(ThreadState *thr) { return; ScopedInRtl in_rtl; + if (!flags()->report_atomic_races && !RaceBetweenAtomicAndFree(thr)) + return; + if (thr->in_signal_handler) Printf("ThreadSanitizer: printing report from signal handler." " Can crash or hang.\n"); @@ -597,7 +627,8 @@ void ReportRace(ThreadState *thr) { } #endif - if (!OutputReport(ctx, rep, rep.GetReport()->mops[0]->stack)) + if (!OutputReport(ctx, rep, rep.GetReport()->mops[0]->stack, + rep.GetReport()->mops[1]->stack)) return; AddRacyStacks(thr, traces, addr_min, addr_max); @@ -609,4 +640,16 @@ void PrintCurrentStack(ThreadState *thr, uptr pc) { PrintStack(SymbolizeStack(trace)); } +void PrintCurrentStackSlow() { +#ifndef TSAN_GO + __sanitizer::StackTrace *ptrace = new(internal_alloc(MBlockStackTrace, + sizeof(__sanitizer::StackTrace))) __sanitizer::StackTrace; + ptrace->SlowUnwindStack(__sanitizer::StackTrace::GetCurrentPc(), + kStackTraceMax); + StackTrace trace; + trace.Init(ptrace->trace, ptrace->size); + PrintStack(SymbolizeStack(trace)); +#endif +} + } // namespace __tsan diff --git a/libsanitizer/tsan/tsan_rtl_thread.cc b/libsanitizer/tsan/tsan_rtl_thread.cc index d5b3444be6d..9c89417ad68 100644 --- a/libsanitizer/tsan/tsan_rtl_thread.cc +++ b/libsanitizer/tsan/tsan_rtl_thread.cc @@ -207,6 +207,9 @@ void ThreadStart(ThreadState *thr, int tid, uptr os_id) { thr->shadow_stack_pos = thr->shadow_stack; thr->shadow_stack_end = thr->shadow_stack + kInitStackSize; #endif +#ifndef TSAN_GO + AllocatorThreadStart(thr); +#endif tctx->thr = thr; thr->fast_synch_epoch = tctx->epoch0; thr->clock.set(tid, tctx->epoch0); @@ -267,7 +270,7 @@ void ThreadFinish(ThreadState *thr) { tctx->epoch1 = thr->fast_state.epoch(); #ifndef TSAN_GO - AlloctorThreadFinish(thr); + AllocatorThreadFinish(thr); #endif thr->~ThreadState(); StatAggregate(ctx->stat, thr->stat); @@ -392,7 +395,7 @@ void MemoryAccessRange(ThreadState *thr, uptr pc, uptr addr, Shadow cur(fast_state); cur.SetWrite(is_write); cur.SetAddr0AndSizeLog(addr & (kShadowCell - 1), kAccessSizeLog); - MemoryAccessImpl(thr, addr, kAccessSizeLog, is_write, + MemoryAccessImpl(thr, addr, kAccessSizeLog, is_write, false, shadow_mem, cur); } if (unaligned) @@ -403,7 +406,7 @@ void MemoryAccessRange(ThreadState *thr, uptr pc, uptr addr, Shadow cur(fast_state); cur.SetWrite(is_write); cur.SetAddr0AndSizeLog(0, kAccessSizeLog); - MemoryAccessImpl(thr, addr, kAccessSizeLog, is_write, + MemoryAccessImpl(thr, addr, kAccessSizeLog, is_write, false, shadow_mem, cur); shadow_mem += kShadowCnt; } @@ -413,24 +416,8 @@ void MemoryAccessRange(ThreadState *thr, uptr pc, uptr addr, Shadow cur(fast_state); cur.SetWrite(is_write); cur.SetAddr0AndSizeLog(addr & (kShadowCell - 1), kAccessSizeLog); - MemoryAccessImpl(thr, addr, kAccessSizeLog, is_write, + MemoryAccessImpl(thr, addr, kAccessSizeLog, is_write, false, shadow_mem, cur); } } - -void MemoryRead1Byte(ThreadState *thr, uptr pc, uptr addr) { - MemoryAccess(thr, pc, addr, 0, 0); -} - -void MemoryWrite1Byte(ThreadState *thr, uptr pc, uptr addr) { - MemoryAccess(thr, pc, addr, 0, 1); -} - -void MemoryRead8Byte(ThreadState *thr, uptr pc, uptr addr) { - MemoryAccess(thr, pc, addr, 3, 0); -} - -void MemoryWrite8Byte(ThreadState *thr, uptr pc, uptr addr) { - MemoryAccess(thr, pc, addr, 3, 1); -} } // namespace __tsan diff --git a/libsanitizer/tsan/tsan_stat.cc b/libsanitizer/tsan/tsan_stat.cc index 105d0bc2375..cd88d2df928 100644 --- a/libsanitizer/tsan/tsan_stat.cc +++ b/libsanitizer/tsan/tsan_stat.cc @@ -179,6 +179,18 @@ void StatOutput(u64 *stat) { name[StatInt_sem_timedwait] = " sem_timedwait "; name[StatInt_sem_post] = " sem_post "; name[StatInt_sem_getvalue] = " sem_getvalue "; + name[StatInt_stat] = " stat "; + name[StatInt___xstat] = " __xstat "; + name[StatInt_stat64] = " stat64 "; + name[StatInt___xstat64] = " __xstat64 "; + name[StatInt_lstat] = " lstat "; + name[StatInt___lxstat] = " __lxstat "; + name[StatInt_lstat64] = " lstat64 "; + name[StatInt___lxstat64] = " __lxstat64 "; + name[StatInt_fstat] = " fstat "; + name[StatInt___fxstat] = " __fxstat "; + name[StatInt_fstat64] = " fstat64 "; + name[StatInt___fxstat64] = " __fxstat64 "; name[StatInt_open] = " open "; name[StatInt_open64] = " open64 "; name[StatInt_creat] = " creat "; @@ -193,12 +205,15 @@ void StatOutput(u64 *stat) { name[StatInt_socket] = " socket "; name[StatInt_socketpair] = " socketpair "; name[StatInt_connect] = " connect "; + name[StatInt_bind] = " bind "; + name[StatInt_listen] = " listen "; name[StatInt_accept] = " accept "; name[StatInt_accept4] = " accept4 "; name[StatInt_epoll_create] = " epoll_create "; name[StatInt_epoll_create1] = " epoll_create1 "; name[StatInt_close] = " close "; name[StatInt___close] = " __close "; + name[StatInt___res_iclose] = " __res_iclose "; name[StatInt_pipe] = " pipe "; name[StatInt_pipe2] = " pipe2 "; name[StatInt_read] = " read "; @@ -240,6 +255,14 @@ void StatOutput(u64 *stat) { name[StatInt_scanf] = " scanf "; name[StatInt_sscanf] = " sscanf "; name[StatInt_fscanf] = " fscanf "; + name[StatInt___isoc99_vscanf] = " vscanf "; + name[StatInt___isoc99_vsscanf] = " vsscanf "; + name[StatInt___isoc99_vfscanf] = " vfscanf "; + name[StatInt___isoc99_scanf] = " scanf "; + name[StatInt___isoc99_sscanf] = " sscanf "; + name[StatInt___isoc99_fscanf] = " fscanf "; + name[StatInt_on_exit] = " on_exit "; + name[StatInt___cxa_atexit] = " __cxa_atexit "; name[StatAnnotation] = "Dynamic annotations "; name[StatAnnotateHappensBefore] = " HappensBefore "; @@ -285,6 +308,7 @@ void StatOutput(u64 *stat) { name[StatMtxAnnotations] = " Annotations "; name[StatMtxMBlock] = " MBlock "; name[StatMtxJavaMBlock] = " JavaMBlock "; + name[StatMtxFD] = " FD "; Printf("Statistics:\n"); for (int i = 0; i < StatCnt; i++) diff --git a/libsanitizer/tsan/tsan_stat.h b/libsanitizer/tsan/tsan_stat.h index f40d3a2ac5e..1d6c54cdd4f 100644 --- a/libsanitizer/tsan/tsan_stat.h +++ b/libsanitizer/tsan/tsan_stat.h @@ -174,6 +174,18 @@ enum StatType { StatInt_sem_timedwait, StatInt_sem_post, StatInt_sem_getvalue, + StatInt_stat, + StatInt___xstat, + StatInt_stat64, + StatInt___xstat64, + StatInt_lstat, + StatInt___lxstat, + StatInt_lstat64, + StatInt___lxstat64, + StatInt_fstat, + StatInt___fxstat, + StatInt_fstat64, + StatInt___fxstat64, StatInt_open, StatInt_open64, StatInt_creat, @@ -188,12 +200,15 @@ enum StatType { StatInt_socket, StatInt_socketpair, StatInt_connect, + StatInt_bind, + StatInt_listen, StatInt_accept, StatInt_accept4, StatInt_epoll_create, StatInt_epoll_create1, StatInt_close, StatInt___close, + StatInt___res_iclose, StatInt_pipe, StatInt_pipe2, StatInt_read, @@ -239,6 +254,14 @@ enum StatType { StatInt_scanf, StatInt_sscanf, StatInt_fscanf, + StatInt___isoc99_vscanf, + StatInt___isoc99_vsscanf, + StatInt___isoc99_vfscanf, + StatInt___isoc99_scanf, + StatInt___isoc99_sscanf, + StatInt___isoc99_fscanf, + StatInt_on_exit, + StatInt___cxa_atexit, // Dynamic annotations. StatAnnotation, @@ -287,6 +310,7 @@ enum StatType { StatMtxAtExit, StatMtxMBlock, StatMtxJavaMBlock, + StatMtxFD, // This must be the last. StatCnt diff --git a/libsanitizer/tsan/tsan_suppressions.cc b/libsanitizer/tsan/tsan_suppressions.cc index 42ba9b509eb..b6c54db2c51 100644 --- a/libsanitizer/tsan/tsan_suppressions.cc +++ b/libsanitizer/tsan/tsan_suppressions.cc @@ -17,6 +17,13 @@ #include "tsan_mman.h" #include "tsan_platform.h" +// Can be overriden in frontend. +#ifndef TSAN_GO +extern "C" const char *WEAK __tsan_default_suppressions() { + return 0; +} +#endif + namespace __tsan { static Suppression *g_suppressions; @@ -29,7 +36,7 @@ static char *ReadFile(const char *filename) { internal_snprintf(tmp.data(), tmp.size(), "%s", filename); else internal_snprintf(tmp.data(), tmp.size(), "%s/%s", GetPwd(), filename); - fd_t fd = internal_open(tmp.data(), false); + fd_t fd = OpenFile(tmp.data(), false); if (fd == kInvalidFd) { Printf("ThreadSanitizer: failed to open suppressions file '%s'\n", tmp.data()); @@ -78,8 +85,7 @@ bool SuppressionMatch(char *templ, const char *str) { return true; } -Suppression *SuppressionParse(const char* supp) { - Suppression *head = 0; +Suppression *SuppressionParse(Suppression *head, const char* supp) { const char *line = supp; while (line) { while (line[0] == ' ' || line[0] == '\t') @@ -128,8 +134,12 @@ Suppression *SuppressionParse(const char* supp) { } void InitializeSuppressions() { - char *supp = ReadFile(flags()->suppressions); - g_suppressions = SuppressionParse(supp); + const char *supp = ReadFile(flags()->suppressions); + g_suppressions = SuppressionParse(0, supp); +#ifndef TSAN_GO + supp = __tsan_default_suppressions(); + g_suppressions = SuppressionParse(0, supp); +#endif } uptr IsSuppressed(ReportType typ, const ReportStack *stack) { @@ -150,7 +160,8 @@ uptr IsSuppressed(ReportType typ, const ReportStack *stack) { for (Suppression *supp = g_suppressions; supp; supp = supp->next) { if (stype == supp->type && (SuppressionMatch(supp->templ, frame->func) || - SuppressionMatch(supp->templ, frame->file))) { + SuppressionMatch(supp->templ, frame->file) || + SuppressionMatch(supp->templ, frame->module))) { DPrintf("ThreadSanitizer: matched suppression '%s'\n", supp->templ); return frame->pc; } diff --git a/libsanitizer/tsan/tsan_suppressions.h b/libsanitizer/tsan/tsan_suppressions.h index 4761eaa0cee..d44d8dd3529 100644 --- a/libsanitizer/tsan/tsan_suppressions.h +++ b/libsanitizer/tsan/tsan_suppressions.h @@ -33,7 +33,7 @@ struct Suppression { char *templ; }; -Suppression *SuppressionParse(const char* supp); +Suppression *SuppressionParse(Suppression *head, const char* supp); bool SuppressionMatch(char *templ, const char *str); } // namespace __tsan diff --git a/libsanitizer/tsan/tsan_symbolize.cc b/libsanitizer/tsan/tsan_symbolize.cc index 015b98717f1..65a994670b5 100644 --- a/libsanitizer/tsan/tsan_symbolize.cc +++ b/libsanitizer/tsan/tsan_symbolize.cc @@ -16,9 +16,24 @@ #include "sanitizer_common/sanitizer_symbolizer.h" #include "tsan_flags.h" #include "tsan_report.h" +#include "tsan_rtl.h" namespace __tsan { +struct ScopedInSymbolizer { + ScopedInSymbolizer() { + ThreadState *thr = cur_thread(); + CHECK(!thr->in_symbolizer); + thr->in_symbolizer = true; + } + + ~ScopedInSymbolizer() { + ThreadState *thr = cur_thread(); + CHECK(thr->in_symbolizer); + thr->in_symbolizer = false; + } +}; + ReportStack *NewReportStackEntry(uptr addr) { ReportStack *ent = (ReportStack*)internal_alloc(MBlockReportStack, sizeof(ReportStack)); @@ -53,35 +68,36 @@ static ReportStack *NewReportStackEntry(const AddressInfo &info) { } ReportStack *SymbolizeCode(uptr addr) { - if (flags()->external_symbolizer_path[0]) { - static const uptr kMaxAddrFrames = 16; - InternalScopedBuffer<AddressInfo> addr_frames(kMaxAddrFrames); - for (uptr i = 0; i < kMaxAddrFrames; i++) - new(&addr_frames[i]) AddressInfo(); - uptr addr_frames_num = __sanitizer::SymbolizeCode(addr, addr_frames.data(), - kMaxAddrFrames); - if (addr_frames_num == 0) - return NewReportStackEntry(addr); - ReportStack *top = 0; - ReportStack *bottom = 0; - for (uptr i = 0; i < addr_frames_num; i++) { - ReportStack *cur_entry = NewReportStackEntry(addr_frames[i]); - CHECK(cur_entry); - addr_frames[i].Clear(); - if (i == 0) - top = cur_entry; - else - bottom->next = cur_entry; - bottom = cur_entry; - } - return top; + if (!IsSymbolizerAvailable()) + return SymbolizeCodeAddr2Line(addr); + ScopedInSymbolizer in_symbolizer; + static const uptr kMaxAddrFrames = 16; + InternalScopedBuffer<AddressInfo> addr_frames(kMaxAddrFrames); + for (uptr i = 0; i < kMaxAddrFrames; i++) + new(&addr_frames[i]) AddressInfo(); + uptr addr_frames_num = __sanitizer::SymbolizeCode(addr, addr_frames.data(), + kMaxAddrFrames); + if (addr_frames_num == 0) + return NewReportStackEntry(addr); + ReportStack *top = 0; + ReportStack *bottom = 0; + for (uptr i = 0; i < addr_frames_num; i++) { + ReportStack *cur_entry = NewReportStackEntry(addr_frames[i]); + CHECK(cur_entry); + addr_frames[i].Clear(); + if (i == 0) + top = cur_entry; + else + bottom->next = cur_entry; + bottom = cur_entry; } - return SymbolizeCodeAddr2Line(addr); + return top; } ReportLocation *SymbolizeData(uptr addr) { - if (flags()->external_symbolizer_path[0] == 0) + if (!IsSymbolizerAvailable()) return 0; + ScopedInSymbolizer in_symbolizer; DataInfo info; if (!__sanitizer::SymbolizeData(addr, &info)) return 0; diff --git a/libsanitizer/tsan/tsan_update_shadow_word_inl.h b/libsanitizer/tsan/tsan_update_shadow_word_inl.h index e22859c1dde..b9aa51c7957 100644 --- a/libsanitizer/tsan/tsan_update_shadow_word_inl.h +++ b/libsanitizer/tsan/tsan_update_shadow_word_inl.h @@ -32,7 +32,7 @@ do { if (Shadow::TidsAreEqual(old, cur)) { StatInc(thr, StatShadowSameThread); if (OldIsInSameSynchEpoch(old, thr)) { - if (OldIsRWNotWeaker(old, kAccessIsWrite)) { + if (old.IsRWNotWeaker(kAccessIsWrite, kIsAtomic)) { // found a slot that holds effectively the same info // (that is, same tid, same sync epoch and same size) StatInc(thr, StatMopSame); @@ -41,7 +41,7 @@ do { StoreIfNotYetStored(sp, &store_word); break; } - if (OldIsRWWeakerOrEqual(old, kAccessIsWrite)) + if (old.IsRWWeakerOrEqual(kAccessIsWrite, kIsAtomic)) StoreIfNotYetStored(sp, &store_word); break; } @@ -50,25 +50,23 @@ do { StoreIfNotYetStored(sp, &store_word); break; } - if (BothReads(old, kAccessIsWrite)) + if (old.IsBothReadsOrAtomic(kAccessIsWrite, kIsAtomic)) break; goto RACE; } - // Do the memory access intersect? - if (Shadow::TwoRangesIntersect(old, cur, kAccessSize)) { + // In Go all memory accesses are 1 byte, so there can be no intersections. + if (kCppMode && Shadow::TwoRangesIntersect(old, cur, kAccessSize)) { StatInc(thr, StatShadowIntersect); if (Shadow::TidsAreEqual(old, cur)) { StatInc(thr, StatShadowSameThread); break; } StatInc(thr, StatShadowAnotherThread); - if (HappensBefore(old, thr)) + if (old.IsBothReadsOrAtomic(kAccessIsWrite, kIsAtomic)) break; - - if (BothReads(old, kAccessIsWrite)) + if (HappensBefore(old, thr)) break; - goto RACE; } // The accesses do not intersect. diff --git a/libsanitizer/tsan/tsan_vector.h b/libsanitizer/tsan/tsan_vector.h index d6bb7076a45..99e9f792c20 100644 --- a/libsanitizer/tsan/tsan_vector.h +++ b/libsanitizer/tsan/tsan_vector.h @@ -103,6 +103,6 @@ class Vector { Vector(const Vector&); void operator=(const Vector&); }; -} +} // namespace __tsan #endif // #ifndef TSAN_VECTOR_H |