diff options
Diffstat (limited to 'contrib')
349 files changed, 10418 insertions, 26209 deletions
diff --git a/contrib/compiler-rt/include/sanitizer/asan_interface.h b/contrib/compiler-rt/include/sanitizer/asan_interface.h index 7763389ab257..97ba0ceb0b23 100644 --- a/contrib/compiler-rt/include/sanitizer/asan_interface.h +++ b/contrib/compiler-rt/include/sanitizer/asan_interface.h @@ -110,10 +110,6 @@ extern "C" { void __asan_report_error(void *pc, void *bp, void *sp, void *addr, int 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); - // Deprecated. Call __sanitizer_set_death_callback instead. void __asan_set_death_callback(void (*callback)(void)); diff --git a/contrib/compiler-rt/include/sanitizer/common_interface_defs.h b/contrib/compiler-rt/include/sanitizer/common_interface_defs.h index ef645e527119..b2a4bb7b89ee 100644 --- a/contrib/compiler-rt/include/sanitizer/common_interface_defs.h +++ b/contrib/compiler-rt/include/sanitizer/common_interface_defs.h @@ -105,12 +105,31 @@ extern "C" { int __sanitizer_verify_contiguous_container(const void *beg, const void *mid, const void *end); + // Similar to __sanitizer_verify_contiguous_container but returns the address + // of the first improperly poisoned byte otherwise. Returns null if the area + // is poisoned properly. + const void *__sanitizer_contiguous_container_find_bad_address( + const void *beg, const void *mid, const void *end); + // Print the stack trace leading to this call. Useful for debugging user code. void __sanitizer_print_stack_trace(); // Sets the callback to be called right before death on error. // Passing 0 will unset the callback. void __sanitizer_set_death_callback(void (*callback)(void)); + + // Interceptor hooks. + // Whenever a libc function interceptor is called it checks if the + // corresponding weak hook is defined, and it so -- calls it. + // The primary use case is data-flow-guided fuzzing, where the fuzzer needs + // to know what is being passed to libc functions, e.g. memcmp. + // FIXME: implement more hooks. + void __sanitizer_weak_hook_memcmp(void *called_pc, const void *s1, + const void *s2, size_t n, int result); + void __sanitizer_weak_hook_strncmp(void *called_pc, const char *s1, + const char *s2, size_t n, int result); + void __sanitizer_weak_hook_strcmp(void *called_pc, const char *s1, + const char *s2, int result); #ifdef __cplusplus } // extern "C" #endif diff --git a/contrib/compiler-rt/include/sanitizer/coverage_interface.h b/contrib/compiler-rt/include/sanitizer/coverage_interface.h index 404b71e3086f..2dcc09fc8499 100644 --- a/contrib/compiler-rt/include/sanitizer/coverage_interface.h +++ b/contrib/compiler-rt/include/sanitizer/coverage_interface.h @@ -27,9 +27,11 @@ extern "C" { // descriptor. Returns -1 on failure, or if coverage dumping is disabled. // This is intended for use by sandboxing code. intptr_t __sanitizer_maybe_open_cov_file(const char *name); - // Get the number of total unique covered entities (blocks, edges, calls). + // Get the number of unique covered blocks (or edges). // This can be useful for coverage-directed in-process fuzzers. uintptr_t __sanitizer_get_total_unique_coverage(); + // Get the number of unique indirect caller-callee pairs. + uintptr_t __sanitizer_get_total_unique_caller_callee_pairs(); // Reset the basic-block (edge) coverage to the initial state. // Useful for in-process fuzzing to start collecting coverage from scratch. @@ -39,6 +41,13 @@ extern "C" { // Some of the entries in *data will be zero. uintptr_t __sanitizer_get_coverage_guards(uintptr_t **data); + // Set *data to the growing buffer with covered PCs and return the size + // of the buffer. The entries are never zero. + // When only unique pcs are collected, the size is equal to + // __sanitizer_get_total_unique_coverage. + // WARNING: EXPERIMENTAL API. + uintptr_t __sanitizer_get_coverage_pc_buffer(uintptr_t **data); + // The coverage instrumentation may optionally provide imprecise counters. // Rather than exposing the counter values to the user we instead map // the counters to a bitset. diff --git a/contrib/compiler-rt/include/sanitizer/dfsan_interface.h b/contrib/compiler-rt/include/sanitizer/dfsan_interface.h index 84ffd49f8afe..05666f736718 100644 --- a/contrib/compiler-rt/include/sanitizer/dfsan_interface.h +++ b/contrib/compiler-rt/include/sanitizer/dfsan_interface.h @@ -91,16 +91,18 @@ void dfsan_set_write_callback(dfsan_write_callback_t labeled_write_callback); /// <label> <parent label 1> <parent label 2> <label description if any> void dfsan_dump_labels(int fd); +/// Interceptor hooks. /// Whenever a dfsan's custom function is called the corresponding /// hook is called it non-zero. The hooks should be defined by the user. /// The primary use case is taint-guided fuzzing, where the fuzzer /// needs to see the parameters of the function and the labels. /// FIXME: implement more hooks. - -/// memcmp hook. void dfsan_weak_hook_memcmp(void *caller_pc, const void *s1, const void *s2, size_t n, dfsan_label s1_label, dfsan_label s2_label, dfsan_label n_label); +void dfsan_weak_hook_strncmp(void *caller_pc, const char *s1, const char *s2, + size_t n, dfsan_label s1_label, + dfsan_label s2_label, dfsan_label n_label); #ifdef __cplusplus } // extern "C" diff --git a/contrib/compiler-rt/include/sanitizer/lsan_interface.h b/contrib/compiler-rt/include/sanitizer/lsan_interface.h index db017c4de1a3..8fb8e756da26 100644 --- a/contrib/compiler-rt/include/sanitizer/lsan_interface.h +++ b/contrib/compiler-rt/include/sanitizer/lsan_interface.h @@ -43,7 +43,7 @@ extern "C" { // Check for leaks now. This function behaves identically to the default // end-of-process leak check. In particular, it will terminate the process if - // leaks are found and the exit_code flag is non-zero. + // leaks are found and the exitcode runtime flag is non-zero. // Subsequent calls to this function will have no effect and end-of-process // leak check will not run. Effectively, end-of-process leak check is moved to // the time of first invocation of this function. diff --git a/contrib/compiler-rt/include/sanitizer/msan_interface.h b/contrib/compiler-rt/include/sanitizer/msan_interface.h index f54bcaa3e157..6d6a3765241b 100644 --- a/contrib/compiler-rt/include/sanitizer/msan_interface.h +++ b/contrib/compiler-rt/include/sanitizer/msan_interface.h @@ -61,10 +61,6 @@ extern "C" { * is not. */ void __msan_check_mem_is_initialized(const volatile void *x, size_t size); - /* Set exit code when error(s) were detected. - Value of 0 means don't change the program exit code. */ - void __msan_set_exit_code(int exit_code); - /* For testing: __msan_set_expect_umr(1); ... some buggy code ... @@ -92,14 +88,22 @@ extern "C" { Memory will be marked uninitialized, with origin at the call site. */ void __msan_allocated_memory(const volatile void* data, size_t size); + /* Tell MSan about newly destroyed memory. Mark memory as uninitialized. */ + void __sanitizer_dtor_callback(const volatile void* data, size_t size); + /* This function may be optionally provided by user and should return a string containing Msan runtime options. See msan_flags.h for details. */ const char* __msan_default_options(); - /* Sets the callback to be called right before death on error. - Passing 0 will unset the callback. */ + /* Deprecated. Call __sanitizer_set_death_callback instead. */ void __msan_set_death_callback(void (*callback)(void)); + /* Update shadow for the application copy of size bytes from src to dst. + Src and dst are application addresses. This function does not copy the + actual application memory, it only updates shadow and origin for such + copy. Source and destination regions can overlap. */ + void __msan_copy_shadow(const volatile void *dst, const volatile void *src, + size_t size); #ifdef __cplusplus } // extern "C" #endif diff --git a/contrib/compiler-rt/lib/asan/README.txt b/contrib/compiler-rt/lib/asan/README.txt index 8cc9bb17b59d..bb6ff42c5cde 100644 --- a/contrib/compiler-rt/lib/asan/README.txt +++ b/contrib/compiler-rt/lib/asan/README.txt @@ -23,4 +23,4 @@ from the root of your CMake build tree: make check-asan For more instructions see: -http://code.google.com/p/address-sanitizer/wiki/HowToBuild +https://github.com/google/sanitizers/wiki/AddressSanitizerHowToBuild diff --git a/contrib/compiler-rt/lib/asan/asan_activation.cc b/contrib/compiler-rt/lib/asan/asan_activation.cc index 3bc01984898d..9df3b977ea1b 100644 --- a/contrib/compiler-rt/lib/asan/asan_activation.cc +++ b/contrib/compiler-rt/lib/asan/asan_activation.cc @@ -38,7 +38,7 @@ static struct AsanDeactivatedFlags { #undef ASAN_ACTIVATION_FLAG #undef COMMON_ACTIVATION_FLAG - RegisterIncludeFlag(parser, cf); + RegisterIncludeFlags(parser, cf); } void OverrideFromActivationFlags() { @@ -61,11 +61,6 @@ static struct AsanDeactivatedFlags { parser.ParseString(env); } - // Override from getprop asan.options. - char buf[100]; - GetExtraActivationFlags(buf, sizeof(buf)); - parser.ParseString(buf); - SetVerbosity(cf.verbosity); if (Verbosity()) ReportUnrecognizedFlags(); @@ -124,6 +119,8 @@ void AsanActivate() { if (!asan_is_deactivated) return; VReport(1, "Activating ASan\n"); + UpdateProcessName(); + asan_deactivated_flags.OverrideFromActivationFlags(); SetCanPoisonMemory(asan_deactivated_flags.poison_heap); diff --git a/contrib/compiler-rt/lib/asan/asan_allocator.cc b/contrib/compiler-rt/lib/asan/asan_allocator.cc index 2df9a510bd9a..56f184a36651 100644 --- a/contrib/compiler-rt/lib/asan/asan_allocator.cc +++ b/contrib/compiler-rt/lib/asan/asan_allocator.cc @@ -14,8 +14,8 @@ // with ThreadSanitizer and MemorySanitizer. // //===----------------------------------------------------------------------===// -#include "asan_allocator.h" +#include "asan_allocator.h" #include "asan_mapping.h" #include "asan_poisoning.h" #include "asan_report.h" @@ -541,7 +541,7 @@ struct Allocator { u8 chunk_state = m->chunk_state; if (chunk_state != CHUNK_ALLOCATED) ReportInvalidFree(old_ptr, chunk_state, stack); - CHECK_NE(REAL(memcpy), (void*)0); + CHECK_NE(REAL(memcpy), nullptr); uptr memcpy_size = Min(new_size, m->UsedSize()); // If realloc() races with free(), we may start copying freed memory. // However, we will report racy double-free later anyway. @@ -579,7 +579,7 @@ struct Allocator { // Assumes alloc_beg == allocator.GetBlockBegin(alloc_beg). AsanChunk *GetAsanChunk(void *alloc_beg) { - if (!alloc_beg) return 0; + if (!alloc_beg) return nullptr; if (!allocator.FromPrimary(alloc_beg)) { uptr *meta = reinterpret_cast<uptr *>(allocator.GetMetaData(alloc_beg)); AsanChunk *m = reinterpret_cast<AsanChunk *>(meta[1]); @@ -619,7 +619,7 @@ struct Allocator { // 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. // Search a bit to the left to see if there is another chunk. - AsanChunk *m2 = 0; + AsanChunk *m2 = nullptr; for (uptr l = 1; l < GetPageSizeCached(); l++) { m2 = GetAsanChunkByAddr(addr - l); if (m2 == m1) continue; // Still the same chunk. @@ -653,7 +653,7 @@ static AsanAllocator &get_allocator() { } bool AsanChunkView::IsValid() { - return chunk_ != 0 && chunk_->chunk_state != CHUNK_AVAILABLE; + return chunk_ && chunk_->chunk_state != CHUNK_AVAILABLE; } uptr AsanChunkView::Beg() { return chunk_->Beg(); } uptr AsanChunkView::End() { return Beg() + UsedSize(); } @@ -723,11 +723,11 @@ void *asan_calloc(uptr nmemb, uptr size, BufferedStackTrace *stack) { } void *asan_realloc(void *p, uptr size, BufferedStackTrace *stack) { - if (p == 0) + if (!p) return instance.Allocate(size, 8, stack, FROM_MALLOC, true); if (size == 0) { instance.Deallocate(p, 0, stack, FROM_MALLOC); - return 0; + return nullptr; } return instance.Reallocate(p, size, stack); } @@ -755,7 +755,7 @@ int asan_posix_memalign(void **memptr, uptr alignment, uptr size, } uptr asan_malloc_usable_size(void *ptr, uptr pc, uptr bp) { - if (ptr == 0) return 0; + if (!ptr) return 0; uptr usable_size = instance.AllocationSize(reinterpret_cast<uptr>(ptr)); if (flags()->check_malloc_usable_size && (usable_size == 0)) { GET_STACK_TRACE_FATAL(pc, bp); @@ -780,7 +780,7 @@ void AsanSoftRssLimitExceededCallback(bool exceeded) { instance.allocator.SetRssLimitIsExceeded(exceeded); } -} // namespace __asan +} // namespace __asan // --- Implementation of LSan-specific functions --- {{{1 namespace __lsan { @@ -881,7 +881,7 @@ int __sanitizer_get_ownership(const void *p) { } uptr __sanitizer_get_allocated_size(const void *p) { - if (p == 0) return 0; + if (!p) return 0; uptr ptr = reinterpret_cast<uptr>(p); uptr allocated_size = instance.AllocationSize(ptr); // Die if p is not malloced or if it is already freed. @@ -904,5 +904,5 @@ SANITIZER_INTERFACE_ATTRIBUTE SANITIZER_WEAK_ATTRIBUTE void __sanitizer_free_hook(void *ptr) { (void)ptr; } -} // extern "C" +} // extern "C" #endif diff --git a/contrib/compiler-rt/lib/asan/asan_allocator.h b/contrib/compiler-rt/lib/asan/asan_allocator.h index 5ccd00c97bab..e3d53330cd2f 100644 --- a/contrib/compiler-rt/lib/asan/asan_allocator.h +++ b/contrib/compiler-rt/lib/asan/asan_allocator.h @@ -114,6 +114,11 @@ struct AsanMapUnmapCallback { # if defined(__powerpc64__) const uptr kAllocatorSpace = 0xa0000000000ULL; const uptr kAllocatorSize = 0x20000000000ULL; // 2T. +# elif defined(__aarch64__) +// AArch64/SANITIZIER_CAN_USER_ALLOCATOR64 is only for 42-bit VMA +// so no need to different values for different VMA. +const uptr kAllocatorSpace = 0x10000000000ULL; +const uptr kAllocatorSize = 0x10000000000ULL; // 3T. # else const uptr kAllocatorSpace = 0x600000000000ULL; const uptr kAllocatorSize = 0x40000000000ULL; // 4T. diff --git a/contrib/compiler-rt/lib/asan/asan_debugging.cc b/contrib/compiler-rt/lib/asan/asan_debugging.cc index 6fc5b690de99..7c3a8a73bd4e 100644 --- a/contrib/compiler-rt/lib/asan/asan_debugging.cc +++ b/contrib/compiler-rt/lib/asan/asan_debugging.cc @@ -108,14 +108,14 @@ static uptr AsanGetStack(uptr addr, uptr *trace, u32 size, u32 *thread_id, return 0; } -} // namespace __asan +} // namespace __asan using namespace __asan; SANITIZER_INTERFACE_ATTRIBUTE const char *__asan_locate_address(uptr addr, char *name, uptr name_size, uptr *region_address, uptr *region_size) { - AddressDescription descr = { name, name_size, 0, 0, 0 }; + AddressDescription descr = { name, name_size, 0, 0, nullptr }; AsanLocateAddress(addr, &descr); if (region_address) *region_address = descr.region_address; if (region_size) *region_size = descr.region_size; diff --git a/contrib/compiler-rt/lib/asan/asan_fake_stack.cc b/contrib/compiler-rt/lib/asan/asan_fake_stack.cc index d20641155b88..91fdf0aa1dca 100644 --- a/contrib/compiler-rt/lib/asan/asan_fake_stack.cc +++ b/contrib/compiler-rt/lib/asan/asan_fake_stack.cc @@ -11,6 +11,7 @@ // // FakeStack is used to detect use-after-return bugs. //===----------------------------------------------------------------------===// + #include "asan_allocator.h" #include "asan_poisoning.h" #include "asan_thread.h" @@ -32,7 +33,8 @@ ALWAYS_INLINE void SetShadow(uptr ptr, uptr size, uptr class_id, u64 magic) { if (class_id <= 6) { for (uptr i = 0; i < (1U << class_id); i++) { shadow[i] = magic; - SanitizerBreakOptimization(0); // Make sure this does not become memset. + // Make sure this does not become memset. + SanitizerBreakOptimization(nullptr); } } else { // The size class is too big, it's cheaper to poison only size bytes. @@ -80,7 +82,9 @@ void FakeStack::PoisonAll(u8 magic) { magic); } +#if !defined(_MSC_VER) || defined(__clang__) ALWAYS_INLINE USED +#endif FakeFrame *FakeStack::Allocate(uptr stack_size_log, uptr class_id, uptr real_stack) { CHECK_LT(class_id, kNumberOfSizeClasses); @@ -106,7 +110,7 @@ FakeFrame *FakeStack::Allocate(uptr stack_size_log, uptr class_id, *SavedFlagPtr(reinterpret_cast<uptr>(res), class_id) = &flags[pos]; return res; } - return 0; // We are out of fake stack. + return nullptr; // We are out of fake stack. } uptr FakeStack::AddrIsInFakeStack(uptr ptr, uptr *frame_beg, uptr *frame_end) { @@ -183,7 +187,7 @@ void SetTLSFakeStack(FakeStack *fs) { } static FakeStack *GetFakeStack() { AsanThread *t = GetCurrentThread(); - if (!t) return 0; + if (!t) return nullptr; return t->fake_stack(); } @@ -191,7 +195,7 @@ static FakeStack *GetFakeStackFast() { if (FakeStack *fs = GetTLSFakeStack()) return fs; if (!__asan_option_detect_stack_use_after_return) - return 0; + return nullptr; return GetFakeStack(); } @@ -212,7 +216,7 @@ ALWAYS_INLINE void OnFree(uptr ptr, uptr class_id, uptr size) { SetShadow(ptr, size, class_id, kMagic8); } -} // namespace __asan +} // namespace __asan // ---------------------- Interface ---------------- {{{1 using namespace __asan; @@ -245,13 +249,13 @@ SANITIZER_INTERFACE_ATTRIBUTE void *__asan_addr_is_in_fake_stack(void *fake_stack, void *addr, void **beg, void **end) { FakeStack *fs = reinterpret_cast<FakeStack*>(fake_stack); - if (!fs) return 0; + if (!fs) return nullptr; uptr frame_beg, frame_end; FakeFrame *frame = reinterpret_cast<FakeFrame *>(fs->AddrIsInFakeStack( reinterpret_cast<uptr>(addr), &frame_beg, &frame_end)); - if (!frame) return 0; + if (!frame) return nullptr; if (frame->magic != kCurrentStackFrameMagic) - return 0; + return nullptr; if (beg) *beg = reinterpret_cast<void*>(frame_beg); if (end) *end = reinterpret_cast<void*>(frame_end); return reinterpret_cast<void*>(frame->real_stack); @@ -276,4 +280,4 @@ void __asan_allocas_unpoison(uptr top, uptr bottom) { REAL(memset)(reinterpret_cast<void*>(MemToShadow(top)), 0, (bottom - top) / SHADOW_GRANULARITY); } -} // extern "C" +} // extern "C" diff --git a/contrib/compiler-rt/lib/asan/asan_flags.cc b/contrib/compiler-rt/lib/asan/asan_flags.cc index e8ea549b62e3..363ee67e77c6 100644 --- a/contrib/compiler-rt/lib/asan/asan_flags.cc +++ b/contrib/compiler-rt/lib/asan/asan_flags.cc @@ -65,6 +65,7 @@ void InitializeFlags() { cf.external_symbolizer_path = GetEnv("ASAN_SYMBOLIZER_PATH"); cf.malloc_context_size = kDefaultMallocContextSize; cf.intercept_tls_get_addr = true; + cf.exitcode = 1; OverrideCommonFlags(cf); } Flags *f = flags(); @@ -115,14 +116,6 @@ void InitializeFlags() { ubsan_parser.ParseString(GetEnv("UBSAN_OPTIONS")); #endif - // Let activation flags override current settings. On Android they come - // from a system property. On other platforms this is no-op. - if (!flags()->start_deactivated) { - char buf[100]; - GetExtraActivationFlags(buf, sizeof(buf)); - asan_parser.ParseString(buf); - } - SetVerbosity(common_flags()->verbosity); // TODO(eugenis): dump all flags at verbosity>=2? diff --git a/contrib/compiler-rt/lib/asan/asan_flags.inc b/contrib/compiler-rt/lib/asan/asan_flags.inc index 53a8a4039e7e..5e69242fb8e9 100644 --- a/contrib/compiler-rt/lib/asan/asan_flags.inc +++ b/contrib/compiler-rt/lib/asan/asan_flags.inc @@ -44,9 +44,6 @@ ASAN_FLAG( "to find more errors.") ASAN_FLAG(bool, replace_intrin, true, "If set, uses custom wrappers for memset/memcpy/memmove intinsics.") -ASAN_FLAG(bool, mac_ignore_invalid_free, false, - "Ignore invalid free() calls to work around some bugs. Used on OS X " - "only.") ASAN_FLAG(bool, detect_stack_use_after_return, false, "Enables stack-use-after-return checking at run-time.") ASAN_FLAG(int, min_uar_stack_size_log, 16, // We can't do smaller anyway. @@ -62,8 +59,6 @@ ASAN_FLAG( "bytes that will be filled with malloc_fill_byte on malloc.") ASAN_FLAG(int, malloc_fill_byte, 0xbe, "Value used to fill the newly allocated memory.") -ASAN_FLAG(int, exitcode, ASAN_DEFAULT_FAILURE_EXITCODE, - "Override the program exit status if the tool found an error.") ASAN_FLAG(bool, allow_user_poisoning, true, "If set, user may manually mark memory regions as poisoned or " "unpoisoned.") @@ -77,10 +72,7 @@ ASAN_FLAG(bool, check_malloc_usable_size, true, "295.*.") ASAN_FLAG(bool, unmap_shadow_on_exit, false, "If set, explicitly unmaps the (huge) shadow at exit.") -ASAN_FLAG( - bool, abort_on_error, false, - "If set, the tool calls abort() instead of _exit() after printing the " - "error report.") +ASAN_FLAG(bool, protect_shadow_gap, true, "If set, mprotect the shadow gap") ASAN_FLAG(bool, print_stats, false, "Print various statistics after printing an error message or if " "atexit=1.") @@ -104,8 +96,8 @@ ASAN_FLAG(bool, poison_array_cookie, true, "Poison (or not) the array cookie after operator new[].") // Turn off alloc/dealloc mismatch checker on Mac and Windows for now. -// https://code.google.com/p/address-sanitizer/issues/detail?id=131 -// https://code.google.com/p/address-sanitizer/issues/detail?id=309 +// https://github.com/google/sanitizers/issues/131 +// https://github.com/google/sanitizers/issues/309 // TODO(glider,timurrrr): Fix known issues and enable this back. ASAN_FLAG(bool, alloc_dealloc_mismatch, (SANITIZER_MAC == 0) && (SANITIZER_WINDOWS == 0), @@ -113,9 +105,6 @@ ASAN_FLAG(bool, alloc_dealloc_mismatch, ASAN_FLAG(bool, new_delete_type_mismatch, true, "Report errors on mismatch betwen size of new and delete.") -ASAN_FLAG(bool, strict_memcmp, true, - "If true, assume that memcmp(p1, p2, n) always reads n bytes before " - "comparing p1 and p2.") ASAN_FLAG( bool, strict_init_order, false, "If true, assume that dynamic initializers can never access globals from " @@ -134,8 +123,8 @@ ASAN_FLAG( "The bigger the value the harder we try.") ASAN_FLAG( bool, detect_container_overflow, true, - "If true, honor the container overflow annotations. " - "See https://code.google.com/p/address-sanitizer/wiki/ContainerOverflow") + "If true, honor the container overflow annotations. See " + "https://github.com/google/sanitizers/wiki/AddressSanitizerContainerOverflow") ASAN_FLAG(int, detect_odr_violation, 2, "If >=2, detect violation of One-Definition-Rule (ODR); " "If ==1, detect ODR-violation only if the two variables " @@ -143,3 +132,6 @@ ASAN_FLAG(int, detect_odr_violation, 2, ASAN_FLAG(bool, dump_instruction_bytes, false, "If true, dump 16 bytes starting at the instruction that caused SEGV") ASAN_FLAG(const char *, suppressions, "", "Suppressions file name.") +ASAN_FLAG(bool, halt_on_error, true, + "Crash the program after printing the first error report " + "(WARNING: USE AT YOUR OWN RISK!)") diff --git a/contrib/compiler-rt/lib/asan/asan_globals.cc b/contrib/compiler-rt/lib/asan/asan_globals.cc index c34b1d3cedf2..eb9f1bfefec2 100644 --- a/contrib/compiler-rt/lib/asan/asan_globals.cc +++ b/contrib/compiler-rt/lib/asan/asan_globals.cc @@ -11,6 +11,7 @@ // // Handle globals. //===----------------------------------------------------------------------===// + #include "asan_interceptors.h" #include "asan_internal.h" #include "asan_mapping.h" @@ -167,7 +168,7 @@ static void RegisterGlobal(const Global *g) { l->next = list_of_all_globals; list_of_all_globals = l; if (g->has_dynamic_init) { - if (dynamic_init_globals == 0) { + if (!dynamic_init_globals) { dynamic_init_globals = new(allocator_for_globals) VectorOfGlobals(kDynamicInitGlobalsInitialCapacity); } @@ -206,7 +207,7 @@ void StopInitOrderChecking() { } } -} // namespace __asan +} // namespace __asan // ---------------------- Interface ---------------- {{{1 using namespace __asan; // NOLINT diff --git a/contrib/compiler-rt/lib/asan/asan_init_version.h b/contrib/compiler-rt/lib/asan/asan_init_version.h index 6cf57c4aa2a8..bc8a622f5bb1 100644 --- a/contrib/compiler-rt/lib/asan/asan_init_version.h +++ b/contrib/compiler-rt/lib/asan/asan_init_version.h @@ -27,8 +27,8 @@ extern "C" { // v3=>v4: added '__asan_global_source_location' to __asan_global. // v4=>v5: changed the semantics and format of __asan_stack_malloc_ and // __asan_stack_free_ functions. - #define __asan_init __asan_init_v5 - #define __asan_init_name "__asan_init_v5" + // v5=>v6: changed the name of the version check symbol + #define __asan_version_mismatch_check __asan_version_mismatch_check_v6 } #endif // ASAN_INIT_VERSION_H diff --git a/contrib/compiler-rt/lib/asan/asan_interceptors.cc b/contrib/compiler-rt/lib/asan/asan_interceptors.cc index d8b48d391ab8..d9a0c71a002d 100644 --- a/contrib/compiler-rt/lib/asan/asan_interceptors.cc +++ b/contrib/compiler-rt/lib/asan/asan_interceptors.cc @@ -11,8 +11,8 @@ // // Intercept various libc functions. //===----------------------------------------------------------------------===// -#include "asan_interceptors.h" +#include "asan_interceptors.h" #include "asan_allocator.h" #include "asan_internal.h" #include "asan_mapping.h" @@ -27,6 +27,12 @@ #include "sanitizer_common/sanitizer_posix.h" #endif +#if defined(__i386) && SANITIZER_LINUX +#define ASAN_PTHREAD_CREATE_VERSION "GLIBC_2.1" +#elif defined(__mips__) && SANITIZER_LINUX +#define ASAN_PTHREAD_CREATE_VERSION "GLIBC_2.2" +#endif + namespace __asan { // Return true if we can quickly decide that the region is unpoisoned. @@ -69,7 +75,7 @@ struct AsanInterceptorContext { } \ if (!suppressed) { \ GET_CURRENT_PC_BP_SP; \ - __asan_report_error(pc, bp, sp, __bad, isWrite, __size, 0); \ + ReportGenericError(pc, bp, sp, __bad, isWrite, __size, 0, false);\ } \ } \ } while (0) @@ -105,7 +111,7 @@ static inline bool RangesOverlap(const char *offset1, uptr length1, static inline uptr MaybeRealStrnlen(const char *s, uptr maxlen) { #if ASAN_INTERCEPT_STRNLEN - if (REAL(strnlen) != 0) { + if (REAL(strnlen)) { return REAL(strnlen)(s, maxlen); } #endif @@ -123,7 +129,7 @@ int OnExit() { return 0; } -} // namespace __asan +} // namespace __asan // ---------------------- Wrappers ---------------- {{{1 using namespace __asan; // NOLINT @@ -172,7 +178,7 @@ DECLARE_REAL_AND_INTERCEPTOR(void, free, void *) } while (false) #define COMMON_INTERCEPTOR_BLOCK_REAL(name) REAL(name) // Strict init-order checking is dlopen-hostile: -// https://code.google.com/p/address-sanitizer/issues/detail?id=178 +// https://github.com/google/sanitizers/issues/178 #define COMMON_INTERCEPTOR_ON_DLOPEN(filename, flag) \ if (flags()->strict_init_order) { \ StopInitOrderChecking(); \ @@ -216,7 +222,7 @@ static thread_return_t THREAD_CALLING_CONV asan_thread_start(void *arg) { ThreadStartParam *param = reinterpret_cast<ThreadStartParam *>(arg); AsanThread *t = nullptr; while ((t = reinterpret_cast<AsanThread *>( - atomic_load(¶m->t, memory_order_acquire))) == 0) + atomic_load(¶m->t, memory_order_acquire))) == nullptr) internal_sched_yield(); SetCurrentThread(t); return t->ThreadStart(GetTid(), ¶m->is_registered); @@ -231,7 +237,7 @@ INTERCEPTOR(int, pthread_create, void *thread, StopInitOrderChecking(); GET_STACK_TRACE_THREAD; int detached = 0; - if (attr != 0) + if (attr) REAL(pthread_attr_getdetachstate)(attr, &detached); ThreadStartParam param; atomic_store(¶m.t, 0, memory_order_relaxed); @@ -270,14 +276,14 @@ INTERCEPTOR(void*, bsd_signal, int signum, void *handler) { } return 0; } -#else +#endif + INTERCEPTOR(void*, signal, int signum, void *handler) { if (!IsDeadlySignal(signum) || common_flags()->allow_user_segv_handler) { return REAL(signal)(signum, handler); } - return 0; + return nullptr; } -#endif INTERCEPTOR(int, sigaction, int signum, const struct sigaction *act, struct sigaction *oldact) { @@ -292,7 +298,7 @@ int real_sigaction(int signum, const void *act, void *oldact) { return REAL(sigaction)(signum, (const struct sigaction *)act, (struct sigaction *)oldact); } -} // namespace __sanitizer +} // namespace __sanitizer #elif SANITIZER_POSIX // We need to have defined REAL(sigaction) on posix systems. @@ -363,40 +369,6 @@ INTERCEPTOR(void, __cxa_throw, void *a, void *b, void *c) { } #endif -static inline int CharCmp(unsigned char c1, unsigned char c2) { - return (c1 == c2) ? 0 : (c1 < c2) ? -1 : 1; -} - -INTERCEPTOR(int, memcmp, const void *a1, const void *a2, uptr size) { - void *ctx; - ASAN_INTERCEPTOR_ENTER(ctx, memcmp); - if (UNLIKELY(!asan_inited)) return internal_memcmp(a1, a2, size); - ENSURE_ASAN_INITED(); - if (flags()->replace_intrin) { - if (flags()->strict_memcmp) { - // Check the entire regions even if the first bytes of the buffers are - // different. - ASAN_READ_RANGE(ctx, a1, size); - ASAN_READ_RANGE(ctx, a2, size); - // Fallthrough to REAL(memcmp) below. - } else { - unsigned char c1 = 0, c2 = 0; - const unsigned char *s1 = (const unsigned char*)a1; - const unsigned char *s2 = (const unsigned char*)a2; - uptr i; - for (i = 0; i < size; i++) { - c1 = s1[i]; - c2 = s2[i]; - if (c1 != c2) break; - } - ASAN_READ_RANGE(ctx, s1, Min(i + 1, size)); - ASAN_READ_RANGE(ctx, s2, Min(i + 1, size)); - return CharCmp(c1, c2); - } - } - return REAL(memcmp(a1, a2, size)); -} - // memcpy is called during __asan_init() from the internals of printf(...). // We do not treat memcpy with to==from as a bug. // See http://llvm.org/bugs/show_bug.cgi?id=11763. @@ -743,7 +715,7 @@ INTERCEPTOR(int, __cxa_atexit, void (*func)(void *), void *arg, #endif ENSURE_ASAN_INITED(); int res = REAL(__cxa_atexit)(func, arg, dso_handle); - REAL(__cxa_atexit)(AtCxaAtexit, 0, 0); + REAL(__cxa_atexit)(AtCxaAtexit, nullptr, nullptr); return res; } #endif // ASAN_INTERCEPT___CXA_ATEXIT @@ -767,7 +739,6 @@ void InitializeAsanInterceptors() { InitializeCommonInterceptors(); // Intercept mem* functions. - ASAN_INTERCEPT_FUNC(memcmp); ASAN_INTERCEPT_FUNC(memmove); ASAN_INTERCEPT_FUNC(memset); if (PLATFORM_HAS_DIFFERENT_MEMCPY_AND_MEMMOVE) { @@ -806,9 +777,8 @@ void InitializeAsanInterceptors() { ASAN_INTERCEPT_FUNC(sigaction); #if SANITIZER_ANDROID ASAN_INTERCEPT_FUNC(bsd_signal); -#else - ASAN_INTERCEPT_FUNC(signal); #endif + ASAN_INTERCEPT_FUNC(signal); #endif #if ASAN_INTERCEPT_SWAPCONTEXT ASAN_INTERCEPT_FUNC(swapcontext); @@ -827,7 +797,11 @@ void InitializeAsanInterceptors() { // Intercept threading-related functions #if ASAN_INTERCEPT_PTHREAD_CREATE +#if defined(ASAN_PTHREAD_CREATE_VERSION) + ASAN_INTERCEPT_FUNC_VER(pthread_create, ASAN_PTHREAD_CREATE_VERSION); +#else ASAN_INTERCEPT_FUNC(pthread_create); +#endif ASAN_INTERCEPT_FUNC(pthread_join); #endif @@ -845,4 +819,4 @@ void InitializeAsanInterceptors() { VReport(1, "AddressSanitizer: libc interceptors initialized\n"); } -} // namespace __asan +} // namespace __asan diff --git a/contrib/compiler-rt/lib/asan/asan_interceptors.h b/contrib/compiler-rt/lib/asan/asan_interceptors.h index 488ada78ab8b..279c5f38451f 100644 --- a/contrib/compiler-rt/lib/asan/asan_interceptors.h +++ b/contrib/compiler-rt/lib/asan/asan_interceptors.h @@ -98,6 +98,12 @@ DECLARE_REAL(int, sigaction, int signum, const struct sigaction *act, if ((!INTERCEPT_FUNCTION(name) || !REAL(name))) \ VReport(1, "AddressSanitizer: failed to intercept '" #name "'\n"); \ } while (0) +#define ASAN_INTERCEPT_FUNC_VER(name, ver) \ + do { \ + if ((!INTERCEPT_FUNCTION_VER(name, ver) || !REAL(name))) \ + VReport( \ + 1, "AddressSanitizer: failed to intercept '" #name "@@" #ver "'\n"); \ + } while (0) #else // OS X interceptors don't need to be initialized with INTERCEPT_FUNCTION. #define ASAN_INTERCEPT_FUNC(name) diff --git a/contrib/compiler-rt/lib/asan/asan_interface_internal.h b/contrib/compiler-rt/lib/asan/asan_interface_internal.h index ad8ebcd91ad9..9efddcbd42b2 100644 --- a/contrib/compiler-rt/lib/asan/asan_interface_internal.h +++ b/contrib/compiler-rt/lib/asan/asan_interface_internal.h @@ -27,10 +27,14 @@ 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. - // Please note that __asan_init is a macro that is replaced with - // __asan_init_vXXX at compile-time. SANITIZER_INTERFACE_ATTRIBUTE void __asan_init(); + // This function exists purely to get a linker/loader error when using + // incompatible versions of instrumentation and runtime library. Please note + // that __asan_version_mismatch_check is a macro that is replaced with + // __asan_version_mismatch_check_vXXX at compile-time. + SANITIZER_INTERFACE_ATTRIBUTE void __asan_version_mismatch_check(); + // This structure is used to describe the source location of a place where // global was defined. struct __asan_global_source_location { @@ -131,8 +135,6 @@ extern "C" { uptr addr, int is_write, uptr access_size, u32 exp); 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*)); @@ -165,6 +167,19 @@ extern "C" { SANITIZER_INTERFACE_ATTRIBUTE void __asan_loadN(uptr p, uptr size); SANITIZER_INTERFACE_ATTRIBUTE void __asan_storeN(uptr p, uptr size); + SANITIZER_INTERFACE_ATTRIBUTE void __asan_load1_noabort(uptr p); + SANITIZER_INTERFACE_ATTRIBUTE void __asan_load2_noabort(uptr p); + SANITIZER_INTERFACE_ATTRIBUTE void __asan_load4_noabort(uptr p); + SANITIZER_INTERFACE_ATTRIBUTE void __asan_load8_noabort(uptr p); + SANITIZER_INTERFACE_ATTRIBUTE void __asan_load16_noabort(uptr p); + SANITIZER_INTERFACE_ATTRIBUTE void __asan_store1_noabort(uptr p); + SANITIZER_INTERFACE_ATTRIBUTE void __asan_store2_noabort(uptr p); + SANITIZER_INTERFACE_ATTRIBUTE void __asan_store4_noabort(uptr p); + SANITIZER_INTERFACE_ATTRIBUTE void __asan_store8_noabort(uptr p); + SANITIZER_INTERFACE_ATTRIBUTE void __asan_store16_noabort(uptr p); + SANITIZER_INTERFACE_ATTRIBUTE void __asan_loadN_noabort(uptr p, uptr size); + SANITIZER_INTERFACE_ATTRIBUTE void __asan_storeN_noabort(uptr p, uptr size); + SANITIZER_INTERFACE_ATTRIBUTE void __asan_exp_load1(uptr p, u32 exp); SANITIZER_INTERFACE_ATTRIBUTE void __asan_exp_load2(uptr p, u32 exp); SANITIZER_INTERFACE_ATTRIBUTE void __asan_exp_load4(uptr p, u32 exp); diff --git a/contrib/compiler-rt/lib/asan/asan_internal.h b/contrib/compiler-rt/lib/asan/asan_internal.h index 107e16ee31b9..0ef0d0eb5263 100644 --- a/contrib/compiler-rt/lib/asan/asan_internal.h +++ b/contrib/compiler-rt/lib/asan/asan_internal.h @@ -21,8 +21,6 @@ #include "sanitizer_common/sanitizer_stacktrace.h" #include "sanitizer_common/sanitizer_libc.h" -#define ASAN_DEFAULT_FAILURE_EXITCODE 1 - #if __has_feature(address_sanitizer) || defined(__SANITIZE_ADDRESS__) # error "The AddressSanitizer run-time should not be" " instrumented by AddressSanitizer" @@ -75,12 +73,9 @@ void *AsanDoesNotSupportStaticLinkage(); void AsanCheckDynamicRTPrereqs(); void AsanCheckIncompatibleRT(); -void AsanOnSIGSEGV(int, void *siginfo, void *context); +void AsanOnDeadlySignal(int, void *siginfo, void *context); -void DisableReexec(); -void MaybeReexec(); void ReadContextStack(void *context, uptr *stack, uptr *ssize); -void AsanPlatformThreadInit(); void StopInitOrderChecking(); // Wrapper for TLS/TSD. diff --git a/contrib/compiler-rt/lib/asan/asan_linux.cc b/contrib/compiler-rt/lib/asan/asan_linux.cc index 9580fc7c06d4..e26b400562df 100644 --- a/contrib/compiler-rt/lib/asan/asan_linux.cc +++ b/contrib/compiler-rt/lib/asan/asan_linux.cc @@ -70,14 +70,6 @@ namespace __asan { void InitializePlatformInterceptors() {} -void DisableReexec() { - // No need to re-exec on Linux. -} - -void MaybeReexec() { - // No need to re-exec on Linux. -} - void *AsanDoesNotSupportStaticLinkage() { // This will fail to link with -static. return &_DYNAMIC; // defined in link.h @@ -117,7 +109,7 @@ void AsanCheckDynamicRTPrereqs() { return; // Ensure that dynamic RT is the first DSO in the list - const char *first_dso_name = 0; + const char *first_dso_name = nullptr; dl_iterate_phdr(FindFirstDSOCallback, &first_dso_name); if (first_dso_name && !IsDynamicRTName(first_dso_name)) { Report("ASan runtime does not come first in initial library list; " @@ -142,7 +134,8 @@ void AsanCheckIncompatibleRT() { // system libraries, causing crashes later in ASan initialization. MemoryMappingLayout proc_maps(/*cache_enabled*/true); char filename[128]; - while (proc_maps.Next(0, 0, 0, filename, sizeof(filename), 0)) { + while (proc_maps.Next(nullptr, nullptr, nullptr, filename, + sizeof(filename), nullptr)) { if (IsDynamicRTName(filename)) { Report("Your application is linked against " "incompatible ASan runtimes.\n"); @@ -155,11 +148,7 @@ void AsanCheckIncompatibleRT() { } } } -#endif // SANITIZER_ANDROID - -void AsanPlatformThreadInit() { - // Nothing here for now. -} +#endif // SANITIZER_ANDROID #if !SANITIZER_ANDROID void ReadContextStack(void *context, uptr *stack, uptr *ssize) { @@ -177,6 +166,6 @@ void *AsanDlSymNext(const char *sym) { return dlsym(RTLD_NEXT, sym); } -} // namespace __asan +} // namespace __asan -#endif // SANITIZER_FREEBSD || SANITIZER_LINUX +#endif // SANITIZER_FREEBSD || SANITIZER_LINUX diff --git a/contrib/compiler-rt/lib/asan/asan_mac.cc b/contrib/compiler-rt/lib/asan/asan_mac.cc index 3e028378df28..f00d98f8e5e6 100644 --- a/contrib/compiler-rt/lib/asan/asan_mac.cc +++ b/contrib/compiler-rt/lib/asan/asan_mac.cc @@ -24,26 +24,17 @@ #include "sanitizer_common/sanitizer_libc.h" #include "sanitizer_common/sanitizer_mac.h" -#if !SANITIZER_IOS -#include <crt_externs.h> // for _NSGetArgv and _NSGetEnviron -#else -extern "C" { - extern char ***_NSGetArgv(void); -} -#endif - -#include <dlfcn.h> // for dladdr() +#include <fcntl.h> +#include <libkern/OSAtomic.h> #include <mach-o/dyld.h> #include <mach-o/loader.h> +#include <pthread.h> +#include <stdlib.h> // for free() #include <sys/mman.h> #include <sys/resource.h> #include <sys/sysctl.h> #include <sys/ucontext.h> -#include <fcntl.h> -#include <pthread.h> -#include <stdlib.h> // for free() #include <unistd.h> -#include <libkern/OSAtomic.h> namespace __asan { @@ -52,187 +43,12 @@ void InitializePlatformInterceptors() {} bool PlatformHasDifferentMemcpyAndMemmove() { // On OS X 10.7 memcpy() and memmove() are both resolved // into memmove$VARIANT$sse42. - // See also http://code.google.com/p/address-sanitizer/issues/detail?id=34. + // See also https://github.com/google/sanitizers/issues/34. // TODO(glider): need to check dynamically that memcpy() and memmove() are // actually the same function. return GetMacosVersion() == MACOS_VERSION_SNOW_LEOPARD; } -extern "C" -void __asan_init(); - -static const char kDyldInsertLibraries[] = "DYLD_INSERT_LIBRARIES"; -LowLevelAllocator allocator_for_env; - -// Change the value of the env var |name|, leaking the original value. -// If |name_value| is NULL, the variable is deleted from the environment, -// otherwise the corresponding "NAME=value" string is replaced with -// |name_value|. -void LeakyResetEnv(const char *name, const char *name_value) { - char **env = GetEnviron(); - uptr name_len = internal_strlen(name); - while (*env != 0) { - uptr len = internal_strlen(*env); - if (len > name_len) { - const char *p = *env; - if (!internal_memcmp(p, name, name_len) && p[name_len] == '=') { - // Match. - if (name_value) { - // Replace the old value with the new one. - *env = const_cast<char*>(name_value); - } else { - // Shift the subsequent pointers back. - char **del = env; - do { - del[0] = del[1]; - } while (*del++); - } - } - } - env++; - } -} - -static bool reexec_disabled = false; - -void DisableReexec() { - reexec_disabled = true; -} - -bool DyldNeedsEnvVariable() { -// If running on OS X 10.11+ or iOS 9.0+, dyld will interpose even if -// DYLD_INSERT_LIBRARIES is not set. - -#if SANITIZER_IOSSIM - // GetMacosVersion will not work for the simulator, whose kernel version - // is tied to the host. Use a weak linking hack for the simulator. - // This API was introduced in the same version of the OS as the dyld - // optimization. - - // Check for presence of a symbol that is available on OS X 10.11+, iOS 9.0+. - return (dlsym(RTLD_NEXT, "mach_memory_info") == nullptr); -#else - return (GetMacosVersion() <= MACOS_VERSION_YOSEMITE); -#endif -} - -void MaybeReexec() { - if (reexec_disabled) return; - - // 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)); - char *dyld_insert_libraries = - const_cast<char*>(GetEnv(kDyldInsertLibraries)); - uptr old_env_len = dyld_insert_libraries ? - internal_strlen(dyld_insert_libraries) : 0; - uptr fname_len = internal_strlen(info.dli_fname); - const char *dylib_name = StripModuleName(info.dli_fname); - uptr dylib_name_len = internal_strlen(dylib_name); - - bool lib_is_in_env = - dyld_insert_libraries && REAL(strstr)(dyld_insert_libraries, dylib_name); - if (DyldNeedsEnvVariable() && !lib_is_in_env) { - // DYLD_INSERT_LIBRARIES is not set or does not contain the runtime - // library. - char program_name[1024]; - uint32_t buf_size = sizeof(program_name); - _NSGetExecutablePath(program_name, &buf_size); - char *new_env = const_cast<char*>(info.dli_fname); - if (dyld_insert_libraries) { - // Append the runtime dylib name to the existing value of - // DYLD_INSERT_LIBRARIES. - new_env = (char*)allocator_for_env.Allocate(old_env_len + fname_len + 2); - internal_strncpy(new_env, dyld_insert_libraries, old_env_len); - new_env[old_env_len] = ':'; - // Copy fname_len and add a trailing zero. - internal_strncpy(new_env + old_env_len + 1, info.dli_fname, - fname_len + 1); - // Ok to use setenv() since the wrappers don't depend on the value of - // asan_inited. - setenv(kDyldInsertLibraries, new_env, /*overwrite*/1); - } else { - // Set DYLD_INSERT_LIBRARIES equal to the runtime dylib name. - setenv(kDyldInsertLibraries, info.dli_fname, /*overwrite*/0); - } - VReport(1, "exec()-ing the program with\n"); - VReport(1, "%s=%s\n", kDyldInsertLibraries, new_env); - VReport(1, "to enable ASan wrappers.\n"); - execv(program_name, *_NSGetArgv()); - - // We get here only if execv() failed. - Report("ERROR: The process is launched without DYLD_INSERT_LIBRARIES, " - "which is required for ASan to work. ASan tried to set the " - "environment variable and re-execute itself, but execv() failed, " - "possibly because of sandbox restrictions. Make sure to launch the " - "executable with:\n%s=%s\n", kDyldInsertLibraries, new_env); - CHECK("execv failed" && 0); - } - - if (!lib_is_in_env) - return; - - // DYLD_INSERT_LIBRARIES is set and contains the runtime library. Let's remove - // the dylib from the environment variable, because interceptors are installed - // and we don't want our children to inherit the variable. - - uptr env_name_len = internal_strlen(kDyldInsertLibraries); - // Allocate memory to hold the previous env var name, its value, the '=' - // sign and the '\0' char. - char *new_env = (char*)allocator_for_env.Allocate( - old_env_len + 2 + env_name_len); - CHECK(new_env); - internal_memset(new_env, '\0', old_env_len + 2 + env_name_len); - internal_strncpy(new_env, kDyldInsertLibraries, env_name_len); - new_env[env_name_len] = '='; - char *new_env_pos = new_env + env_name_len + 1; - - // Iterate over colon-separated pieces of |dyld_insert_libraries|. - char *piece_start = dyld_insert_libraries; - char *piece_end = NULL; - char *old_env_end = dyld_insert_libraries + old_env_len; - do { - if (piece_start[0] == ':') piece_start++; - piece_end = REAL(strchr)(piece_start, ':'); - if (!piece_end) piece_end = dyld_insert_libraries + old_env_len; - if ((uptr)(piece_start - dyld_insert_libraries) > old_env_len) break; - uptr piece_len = piece_end - piece_start; - - char *filename_start = - (char *)internal_memrchr(piece_start, '/', piece_len); - uptr filename_len = piece_len; - if (filename_start) { - filename_start += 1; - filename_len = piece_len - (filename_start - piece_start); - } else { - filename_start = piece_start; - } - - // If the current piece isn't the runtime library name, - // append it to new_env. - if ((dylib_name_len != filename_len) || - (internal_memcmp(filename_start, dylib_name, dylib_name_len) != 0)) { - if (new_env_pos != new_env + env_name_len + 1) { - new_env_pos[0] = ':'; - new_env_pos++; - } - internal_strncpy(new_env_pos, piece_start, piece_len); - new_env_pos += piece_len; - } - // Move on to the next piece. - piece_start = piece_end; - } while (piece_start < old_env_end); - - // Can't use setenv() here, because it requires the allocator to be - // initialized. - // FIXME: instead of filtering DYLD_INSERT_LIBRARIES here, do it in - // a separate function called after InitializeAllocator(). - if (new_env_pos == new_env + env_name_len + 1) new_env = NULL; - LeakyResetEnv(kDyldInsertLibraries, new_env); -} - // No-op. Mac does not support static linkage anyway. void *AsanDoesNotSupportStaticLinkage() { return 0; @@ -244,9 +60,6 @@ void AsanCheckDynamicRTPrereqs() {} // No-op. Mac does not support static linkage anyway. void AsanCheckIncompatibleRT() {} -void AsanPlatformThreadInit() { -} - void ReadContextStack(void *context, uptr *stack, uptr *ssize) { UNIMPLEMENTED(); } diff --git a/contrib/compiler-rt/lib/asan/asan_malloc_linux.cc b/contrib/compiler-rt/lib/asan/asan_malloc_linux.cc index 46a6a9db4a81..d5089f9f7b36 100644 --- a/contrib/compiler-rt/lib/asan/asan_malloc_linux.cc +++ b/contrib/compiler-rt/lib/asan/asan_malloc_linux.cc @@ -26,13 +26,25 @@ // ---------------------- Replacement functions ---------------- {{{1 using namespace __asan; // NOLINT +static const uptr kCallocPoolSize = 1024; +static uptr calloc_memory_for_dlsym[kCallocPoolSize]; + +static bool IsInCallocPool(const void *ptr) { + sptr off = (sptr)ptr - (sptr)calloc_memory_for_dlsym; + return 0 <= off && off < (sptr)kCallocPoolSize; +} + INTERCEPTOR(void, free, void *ptr) { GET_STACK_TRACE_FREE; + if (UNLIKELY(IsInCallocPool(ptr))) + return; asan_free(ptr, &stack, FROM_MALLOC); } INTERCEPTOR(void, cfree, void *ptr) { GET_STACK_TRACE_FREE; + if (UNLIKELY(IsInCallocPool(ptr))) + return; asan_free(ptr, &stack, FROM_MALLOC); } @@ -44,8 +56,6 @@ INTERCEPTOR(void*, malloc, uptr size) { INTERCEPTOR(void*, calloc, uptr nmemb, uptr size) { if (UNLIKELY(!asan_inited)) { // Hack: dlsym calls calloc before REAL(calloc) is retrieved from dlsym. - const uptr kCallocPoolSize = 1024; - static uptr calloc_memory_for_dlsym[kCallocPoolSize]; static uptr allocated; uptr size_in_words = ((nmemb * size) + kWordSize - 1) / kWordSize; void *mem = (void*)&calloc_memory_for_dlsym[allocated]; @@ -59,6 +69,13 @@ INTERCEPTOR(void*, calloc, uptr nmemb, uptr size) { INTERCEPTOR(void*, realloc, void *ptr, uptr size) { GET_STACK_TRACE_MALLOC; + if (UNLIKELY(IsInCallocPool(ptr))) { + uptr offset = (uptr)ptr - (uptr)calloc_memory_for_dlsym; + uptr copy_size = Min(size, kCallocPoolSize - offset); + void *new_ptr = asan_malloc(size, &stack); + internal_memcpy(new_ptr, ptr, copy_size); + return new_ptr; + } return asan_realloc(ptr, size, &stack); } diff --git a/contrib/compiler-rt/lib/asan/asan_malloc_mac.cc b/contrib/compiler-rt/lib/asan/asan_malloc_mac.cc index d7a6307c9bdc..744728d40df5 100644 --- a/contrib/compiler-rt/lib/asan/asan_malloc_mac.cc +++ b/contrib/compiler-rt/lib/asan/asan_malloc_mac.cc @@ -15,348 +15,47 @@ #include "sanitizer_common/sanitizer_platform.h" #if SANITIZER_MAC -#include <AvailabilityMacros.h> -#include <CoreFoundation/CFBase.h> -#include <dlfcn.h> -#include <malloc/malloc.h> -#include <sys/mman.h> - -#include "asan_allocator.h" #include "asan_interceptors.h" -#include "asan_internal.h" #include "asan_report.h" #include "asan_stack.h" #include "asan_stats.h" -#include "sanitizer_common/sanitizer_mac.h" - -// Similar code is used in Google Perftools, -// http://code.google.com/p/google-perftools. - -// ---------------------- Replacement functions ---------------- {{{1 -using namespace __asan; // NOLINT - -// TODO(glider): do we need both zones? -static malloc_zone_t *system_malloc_zone = 0; -static malloc_zone_t asan_zone; - -INTERCEPTOR(malloc_zone_t *, malloc_create_zone, - vm_size_t start_size, unsigned zone_flags) { - ENSURE_ASAN_INITED(); - GET_STACK_TRACE_MALLOC; - uptr page_size = GetPageSizeCached(); - uptr allocated_size = RoundUpTo(sizeof(asan_zone), page_size); - malloc_zone_t *new_zone = - (malloc_zone_t*)asan_memalign(page_size, allocated_size, - &stack, FROM_MALLOC); - internal_memcpy(new_zone, &asan_zone, sizeof(asan_zone)); - new_zone->zone_name = NULL; // The name will be changed anyway. - if (GetMacosVersion() >= MACOS_VERSION_LION) { - // Prevent the client app from overwriting the zone contents. - // Library functions that need to modify the zone will set PROT_WRITE on it. - // This matches the behavior of malloc_create_zone() on OSX 10.7 and higher. - mprotect(new_zone, allocated_size, PROT_READ); - } - return new_zone; -} - -INTERCEPTOR(malloc_zone_t *, malloc_default_zone, void) { - ENSURE_ASAN_INITED(); - return &asan_zone; -} - -INTERCEPTOR(malloc_zone_t *, malloc_default_purgeable_zone, void) { - // FIXME: ASan should support purgeable allocations. - // https://code.google.com/p/address-sanitizer/issues/detail?id=139 - ENSURE_ASAN_INITED(); - return &asan_zone; -} - -INTERCEPTOR(void, malloc_make_purgeable, void *ptr) { - // FIXME: ASan should support purgeable allocations. Ignoring them is fine - // for now. - ENSURE_ASAN_INITED(); -} - -INTERCEPTOR(int, malloc_make_nonpurgeable, void *ptr) { - // FIXME: ASan should support purgeable allocations. Ignoring them is fine - // for now. - ENSURE_ASAN_INITED(); - // Must return 0 if the contents were not purged since the last call to - // malloc_make_purgeable(). - return 0; -} - -INTERCEPTOR(void, malloc_set_zone_name, malloc_zone_t *zone, const char *name) { - ENSURE_ASAN_INITED(); - // Allocate |strlen("asan-") + 1 + internal_strlen(name)| bytes. - size_t buflen = 6 + (name ? internal_strlen(name) : 0); - InternalScopedString new_name(buflen); - if (name && zone->introspect == asan_zone.introspect) { - new_name.append("asan-%s", name); - name = new_name.data(); - } - - // Call the system malloc's implementation for both external and our zones, - // since that appropriately changes VM region protections on the zone. - REAL(malloc_set_zone_name)(zone, name); -} - -INTERCEPTOR(void *, malloc, size_t size) { - ENSURE_ASAN_INITED(); - GET_STACK_TRACE_MALLOC; - void *res = asan_malloc(size, &stack); - return res; -} -INTERCEPTOR(void, free, void *ptr) { - ENSURE_ASAN_INITED(); - if (!ptr) return; - GET_STACK_TRACE_FREE; +using namespace __asan; +#define COMMON_MALLOC_ZONE_NAME "asan" +#define COMMON_MALLOC_ENTER() ENSURE_ASAN_INITED() +#define COMMON_MALLOC_SANITIZER_INITIALIZED asan_inited +#define COMMON_MALLOC_FORCE_LOCK() asan_mz_force_lock() +#define COMMON_MALLOC_FORCE_UNLOCK() asan_mz_force_unlock() +#define COMMON_MALLOC_MEMALIGN(alignment, size) \ + GET_STACK_TRACE_MALLOC; \ + void *p = asan_memalign(alignment, size, &stack, FROM_MALLOC) +#define COMMON_MALLOC_MALLOC(size) \ + GET_STACK_TRACE_MALLOC; \ + void *p = asan_malloc(size, &stack) +#define COMMON_MALLOC_REALLOC(ptr, size) \ + GET_STACK_TRACE_MALLOC; \ + void *p = asan_realloc(ptr, size, &stack); +#define COMMON_MALLOC_CALLOC(count, size) \ + GET_STACK_TRACE_MALLOC; \ + void *p = asan_calloc(count, size, &stack); +#define COMMON_MALLOC_VALLOC(size) \ + GET_STACK_TRACE_MALLOC; \ + void *p = asan_memalign(GetPageSizeCached(), size, &stack, FROM_MALLOC); +#define COMMON_MALLOC_FREE(ptr) \ + GET_STACK_TRACE_FREE; \ asan_free(ptr, &stack, FROM_MALLOC); -} - -INTERCEPTOR(void *, realloc, void *ptr, size_t size) { - ENSURE_ASAN_INITED(); - GET_STACK_TRACE_MALLOC; - return asan_realloc(ptr, size, &stack); -} - -INTERCEPTOR(void *, calloc, size_t nmemb, size_t size) { - ENSURE_ASAN_INITED(); - GET_STACK_TRACE_MALLOC; - return asan_calloc(nmemb, size, &stack); -} - -INTERCEPTOR(void *, valloc, size_t size) { - ENSURE_ASAN_INITED(); - GET_STACK_TRACE_MALLOC; - return asan_memalign(GetPageSizeCached(), size, &stack, FROM_MALLOC); -} - -INTERCEPTOR(size_t, malloc_good_size, size_t size) { - ENSURE_ASAN_INITED(); - return asan_zone.introspect->good_size(&asan_zone, size); -} - -INTERCEPTOR(int, posix_memalign, void **memptr, size_t alignment, size_t size) { - ENSURE_ASAN_INITED(); - CHECK(memptr); - GET_STACK_TRACE_MALLOC; - void *result = asan_memalign(alignment, size, &stack, FROM_MALLOC); - if (result) { - *memptr = result; - return 0; - } - return -1; -} - -namespace { - -// TODO(glider): the __asan_mz_* functions should be united with the Linux -// wrappers, as they are basically copied from there. -extern "C" -SANITIZER_INTERFACE_ATTRIBUTE -size_t __asan_mz_size(malloc_zone_t* zone, const void* ptr) { - return asan_mz_size(ptr); -} - -extern "C" -SANITIZER_INTERFACE_ATTRIBUTE -void *__asan_mz_malloc(malloc_zone_t *zone, uptr size) { - if (UNLIKELY(!asan_inited)) { - CHECK(system_malloc_zone); - return malloc_zone_malloc(system_malloc_zone, size); - } - GET_STACK_TRACE_MALLOC; - return asan_malloc(size, &stack); -} - -extern "C" -SANITIZER_INTERFACE_ATTRIBUTE -void *__asan_mz_calloc(malloc_zone_t *zone, size_t nmemb, size_t size) { - if (UNLIKELY(!asan_inited)) { - // Hack: dlsym calls calloc before REAL(calloc) is retrieved from dlsym. - const size_t kCallocPoolSize = 1024; - static uptr calloc_memory_for_dlsym[kCallocPoolSize]; - static size_t allocated; - size_t size_in_words = ((nmemb * size) + kWordSize - 1) / kWordSize; - void *mem = (void*)&calloc_memory_for_dlsym[allocated]; - allocated += size_in_words; - CHECK(allocated < kCallocPoolSize); - return mem; - } - GET_STACK_TRACE_MALLOC; - return asan_calloc(nmemb, size, &stack); -} - -extern "C" -SANITIZER_INTERFACE_ATTRIBUTE -void *__asan_mz_valloc(malloc_zone_t *zone, size_t size) { - if (UNLIKELY(!asan_inited)) { - CHECK(system_malloc_zone); - return malloc_zone_valloc(system_malloc_zone, size); - } - GET_STACK_TRACE_MALLOC; - return asan_memalign(GetPageSizeCached(), size, &stack, FROM_MALLOC); -} - -#define GET_ZONE_FOR_PTR(ptr) \ - malloc_zone_t *zone_ptr = malloc_zone_from_ptr(ptr); \ - const char *zone_name = (zone_ptr == 0) ? 0 : zone_ptr->zone_name - -void ALWAYS_INLINE free_common(void *context, void *ptr) { - if (!ptr) return; - GET_STACK_TRACE_FREE; - // FIXME: need to retire this flag. - if (!flags()->mac_ignore_invalid_free) { - asan_free(ptr, &stack, FROM_MALLOC); - } else { - GET_ZONE_FOR_PTR(ptr); - WarnMacFreeUnallocated((uptr)ptr, (uptr)zone_ptr, zone_name, &stack); - return; - } -} - -// TODO(glider): the allocation callbacks need to be refactored. -extern "C" -SANITIZER_INTERFACE_ATTRIBUTE -void __asan_mz_free(malloc_zone_t *zone, void *ptr) { - free_common(zone, ptr); -} - -extern "C" -SANITIZER_INTERFACE_ATTRIBUTE -void *__asan_mz_realloc(malloc_zone_t *zone, void *ptr, size_t size) { - if (!ptr) { - GET_STACK_TRACE_MALLOC; - return asan_malloc(size, &stack); - } else { - if (asan_mz_size(ptr)) { - GET_STACK_TRACE_MALLOC; - return asan_realloc(ptr, size, &stack); - } else { - // We can't recover from reallocating an unknown address, because - // this would require reading at most |size| bytes from - // potentially unaccessible memory. - GET_STACK_TRACE_FREE; - GET_ZONE_FOR_PTR(ptr); - ReportMacMzReallocUnknown((uptr)ptr, (uptr)zone_ptr, zone_name, &stack); - } - } -} - -extern "C" -SANITIZER_INTERFACE_ATTRIBUTE -void __asan_mz_destroy(malloc_zone_t* zone) { - // A no-op -- we will not be destroyed! - Report("__asan_mz_destroy() called -- ignoring\n"); -} - -extern "C" -SANITIZER_INTERFACE_ATTRIBUTE -void *__asan_mz_memalign(malloc_zone_t *zone, size_t align, size_t size) { - if (UNLIKELY(!asan_inited)) { - CHECK(system_malloc_zone); - return malloc_zone_memalign(system_malloc_zone, align, size); - } - GET_STACK_TRACE_MALLOC; - return asan_memalign(align, size, &stack, FROM_MALLOC); -} - -// This function is currently unused, and we build with -Werror. -#if 0 -void __asan_mz_free_definite_size( - malloc_zone_t* zone, void *ptr, size_t size) { - // TODO(glider): check that |size| is valid. - UNIMPLEMENTED(); -} -#endif - -kern_return_t mi_enumerator(task_t task, void *, - unsigned type_mask, vm_address_t zone_address, - memory_reader_t reader, - vm_range_recorder_t recorder) { - // Should enumerate all the pointers we have. Seems like a lot of work. - return KERN_FAILURE; -} - -size_t mi_good_size(malloc_zone_t *zone, size_t size) { - // I think it's always safe to return size, but we maybe could do better. - return size; -} - -boolean_t mi_check(malloc_zone_t *zone) { - UNIMPLEMENTED(); -} - -void mi_print(malloc_zone_t *zone, boolean_t verbose) { - UNIMPLEMENTED(); -} - -void mi_log(malloc_zone_t *zone, void *address) { - // I don't think we support anything like this -} - -void mi_force_lock(malloc_zone_t *zone) { - asan_mz_force_lock(); -} - -void mi_force_unlock(malloc_zone_t *zone) { - asan_mz_force_unlock(); -} - -void mi_statistics(malloc_zone_t *zone, malloc_statistics_t *stats) { - AsanMallocStats malloc_stats; - FillMallocStatistics(&malloc_stats); - CHECK(sizeof(malloc_statistics_t) == sizeof(AsanMallocStats)); +#define COMMON_MALLOC_SIZE(ptr) \ + uptr size = asan_mz_size(ptr); +#define COMMON_MALLOC_FILL_STATS(zone, stats) \ + AsanMallocStats malloc_stats; \ + FillMallocStatistics(&malloc_stats); \ + CHECK(sizeof(malloc_statistics_t) == sizeof(AsanMallocStats)); \ internal_memcpy(stats, &malloc_stats, sizeof(malloc_statistics_t)); -} - -boolean_t mi_zone_locked(malloc_zone_t *zone) { - // UNIMPLEMENTED(); - return false; -} - -} // unnamed namespace - -namespace __asan { +#define COMMON_MALLOC_REPORT_UNKNOWN_REALLOC(ptr, zone_ptr, zone_name) \ + GET_STACK_TRACE_FREE; \ + ReportMacMzReallocUnknown((uptr)ptr, (uptr)zone_ptr, zone_name, &stack); +#define COMMON_MALLOC_NAMESPACE __asan -void ReplaceSystemMalloc() { - static malloc_introspection_t asan_introspection; - // Ok to use internal_memset, these places are not performance-critical. - internal_memset(&asan_introspection, 0, sizeof(asan_introspection)); +#include "sanitizer_common/sanitizer_malloc_mac.inc" - asan_introspection.enumerator = &mi_enumerator; - asan_introspection.good_size = &mi_good_size; - asan_introspection.check = &mi_check; - asan_introspection.print = &mi_print; - asan_introspection.log = &mi_log; - asan_introspection.force_lock = &mi_force_lock; - asan_introspection.force_unlock = &mi_force_unlock; - asan_introspection.statistics = &mi_statistics; - asan_introspection.zone_locked = &mi_zone_locked; - - internal_memset(&asan_zone, 0, sizeof(malloc_zone_t)); - - // Use version 6 for OSX >= 10.6. - asan_zone.version = 6; - asan_zone.zone_name = "asan"; - asan_zone.size = &__asan_mz_size; - asan_zone.malloc = &__asan_mz_malloc; - asan_zone.calloc = &__asan_mz_calloc; - asan_zone.valloc = &__asan_mz_valloc; - asan_zone.free = &__asan_mz_free; - asan_zone.realloc = &__asan_mz_realloc; - asan_zone.destroy = &__asan_mz_destroy; - asan_zone.batch_malloc = 0; - asan_zone.batch_free = 0; - asan_zone.free_definite_size = 0; - asan_zone.memalign = &__asan_mz_memalign; - asan_zone.introspect = &asan_introspection; - - // Register the ASan zone. - malloc_zone_register(&asan_zone); -} -} // namespace __asan - -#endif // SANITIZER_MAC +#endif diff --git a/contrib/compiler-rt/lib/asan/asan_mapping.h b/contrib/compiler-rt/lib/asan/asan_mapping.h index f9e1a527de18..8fe347c8bad0 100644 --- a/contrib/compiler-rt/lib/asan/asan_mapping.h +++ b/contrib/compiler-rt/lib/asan/asan_mapping.h @@ -17,7 +17,7 @@ #include "asan_internal.h" // The full explanation of the memory mapping could be found here: -// http://code.google.com/p/address-sanitizer/wiki/AddressSanitizerAlgorithm +// https://github.com/google/sanitizers/wiki/AddressSanitizerAlgorithm // // Typical shadow mapping on Linux/x86_64 with SHADOW_OFFSET == 0x00007fff8000: // || `[0x10007fff8000, 0x7fffffffffff]` || HighMem || @@ -73,6 +73,20 @@ // || `[0x2000000000, 0x23ffffffff]` || LowShadow || // || `[0x0000000000, 0x1fffffffff]` || LowMem || // +// Default Linux/AArch64 (39-bit VMA) mapping: +// || `[0x2000000000, 0x7fffffffff]` || highmem || +// || `[0x1400000000, 0x1fffffffff]` || highshadow || +// || `[0x1200000000, 0x13ffffffff]` || shadowgap || +// || `[0x1000000000, 0x11ffffffff]` || lowshadow || +// || `[0x0000000000, 0x0fffffffff]` || lowmem || +// +// Default Linux/AArch64 (42-bit VMA) mapping: +// || `[0x10000000000, 0x3ffffffffff]` || highmem || +// || `[0x0a000000000, 0x0ffffffffff]` || highshadow || +// || `[0x09000000000, 0x09fffffffff]` || shadowgap || +// || `[0x08000000000, 0x08fffffffff]` || lowshadow || +// || `[0x00000000000, 0x07fffffffff]` || lowmem || +// // Shadow mapping on FreeBSD/x86-64 with SHADOW_OFFSET == 0x400000000000: // || `[0x500000000000, 0x7fffffffffff]` || HighMem || // || `[0x4a0000000000, 0x4fffffffffff]` || HighShadow || @@ -113,11 +127,12 @@ static const u64 kFreeBSD_ShadowOffset64 = 1ULL << 46; // 0x400000000000 static const u64 kWindowsShadowOffset32 = 3ULL << 28; // 0x30000000 #define SHADOW_SCALE kDefaultShadowScale -#if SANITIZER_ANDROID -# define SHADOW_OFFSET (0) -#else -# if SANITIZER_WORDSIZE == 32 -# if defined(__mips__) + + +#if SANITIZER_WORDSIZE == 32 +# if SANITIZER_ANDROID +# define SHADOW_OFFSET (0) +# elif defined(__mips__) # define SHADOW_OFFSET kMIPS32_ShadowOffset32 # elif SANITIZER_FREEBSD # define SHADOW_OFFSET kFreeBSD_ShadowOffset32 @@ -130,7 +145,7 @@ static const u64 kWindowsShadowOffset32 = 3ULL << 28; // 0x30000000 # else # define SHADOW_OFFSET kDefaultShadowOffset32 # endif -# else +#else # if defined(__aarch64__) # define SHADOW_OFFSET kAArch64_ShadowOffset64 # elif defined(__powerpc64__) @@ -148,7 +163,6 @@ static const u64 kWindowsShadowOffset32 = 3ULL << 28; // 0x30000000 # else # define SHADOW_OFFSET kDefaultShort64bitShadowOffset # endif -# endif #endif #define SHADOW_GRANULARITY (1ULL << SHADOW_SCALE) @@ -171,7 +185,8 @@ static const u64 kWindowsShadowOffset32 = 3ULL << 28; // 0x30000000 // With the zero shadow base we can not actually map pages starting from 0. // This constant is somewhat arbitrary. -#define kZeroBaseShadowStart (1 << 18) +#define kZeroBaseShadowStart 0 +#define kZeroBaseMaxShadowStart (1 << 18) #define kShadowGapBeg (kLowShadowEnd ? kLowShadowEnd + 1 \ : kZeroBaseShadowStart) diff --git a/contrib/compiler-rt/lib/asan/asan_new_delete.cc b/contrib/compiler-rt/lib/asan/asan_new_delete.cc index e48bdaf03dd3..b5ba13ef4055 100644 --- a/contrib/compiler-rt/lib/asan/asan_new_delete.cc +++ b/contrib/compiler-rt/lib/asan/asan_new_delete.cc @@ -30,7 +30,7 @@ using namespace __asan; // NOLINT // This code has issues on OSX. -// See https://code.google.com/p/address-sanitizer/issues/detail?id=131. +// See https://github.com/google/sanitizers/issues/131. // Fake std::nothrow_t to avoid including <new>. namespace std { @@ -90,11 +90,11 @@ INTERCEPTOR(void *, _ZnamRKSt9nothrow_t, size_t size, std::nothrow_t const&) { #if !SANITIZER_MAC CXX_OPERATOR_ATTRIBUTE -void operator delete(void *ptr) throw() { +void operator delete(void *ptr) NOEXCEPT { OPERATOR_DELETE_BODY(FROM_NEW); } CXX_OPERATOR_ATTRIBUTE -void operator delete[](void *ptr) throw() { +void operator delete[](void *ptr) NOEXCEPT { OPERATOR_DELETE_BODY(FROM_NEW_BR); } CXX_OPERATOR_ATTRIBUTE @@ -106,12 +106,12 @@ void operator delete[](void *ptr, std::nothrow_t const&) { OPERATOR_DELETE_BODY(FROM_NEW_BR); } CXX_OPERATOR_ATTRIBUTE -void operator delete(void *ptr, size_t size) throw() { +void operator delete(void *ptr, size_t size) NOEXCEPT { GET_STACK_TRACE_FREE; asan_sized_free(ptr, size, &stack, FROM_NEW); } CXX_OPERATOR_ATTRIBUTE -void operator delete[](void *ptr, size_t size) throw() { +void operator delete[](void *ptr, size_t size) NOEXCEPT { GET_STACK_TRACE_FREE; asan_sized_free(ptr, size, &stack, FROM_NEW_BR); } diff --git a/contrib/compiler-rt/lib/asan/asan_poisoning.cc b/contrib/compiler-rt/lib/asan/asan_poisoning.cc index 569d359aa425..f77ab8780bb7 100644 --- a/contrib/compiler-rt/lib/asan/asan_poisoning.cc +++ b/contrib/compiler-rt/lib/asan/asan_poisoning.cc @@ -102,7 +102,7 @@ using namespace __asan; // NOLINT // that user program (un)poisons the memory it owns. It poisons memory // conservatively, and unpoisons progressively to make sure asan shadow // mapping invariant is preserved (see detailed mapping description here: -// http://code.google.com/p/address-sanitizer/wiki/AddressSanitizerAlgorithm). +// https://github.com/google/sanitizers/wiki/AddressSanitizerAlgorithm). // // * if user asks to poison region [left, right), the program poisons // at least [left, AlignDown(right)). @@ -354,7 +354,7 @@ void __sanitizer_annotate_contiguous_container(const void *beg_p, // Make a quick sanity check that we are indeed in this state. // // FIXME: Two of these three checks are disabled until we fix - // https://code.google.com/p/address-sanitizer/issues/detail?id=258. + // https://github.com/google/sanitizers/issues/258. // if (d1 != d2) // CHECK_EQ(*(u8*)MemToShadow(d1), old_mid - d1); if (a + granularity <= d1) @@ -375,10 +375,10 @@ void __sanitizer_annotate_contiguous_container(const void *beg_p, } } -int __sanitizer_verify_contiguous_container(const void *beg_p, - const void *mid_p, - const void *end_p) { - if (!flags()->detect_container_overflow) return 1; +const void *__sanitizer_contiguous_container_find_bad_address( + const void *beg_p, const void *mid_p, const void *end_p) { + if (!flags()->detect_container_overflow) + return nullptr; uptr beg = reinterpret_cast<uptr>(beg_p); uptr end = reinterpret_cast<uptr>(end_p); uptr mid = reinterpret_cast<uptr>(mid_p); @@ -395,17 +395,24 @@ int __sanitizer_verify_contiguous_container(const void *beg_p, uptr r3_end = end; for (uptr i = r1_beg; i < r1_end; i++) if (AddressIsPoisoned(i)) - return 0; + return reinterpret_cast<const void *>(i); for (uptr i = r2_beg; i < mid; i++) if (AddressIsPoisoned(i)) - return 0; + return reinterpret_cast<const void *>(i); for (uptr i = mid; i < r2_end; i++) if (!AddressIsPoisoned(i)) - return 0; + return reinterpret_cast<const void *>(i); for (uptr i = r3_beg; i < r3_end; i++) if (!AddressIsPoisoned(i)) - return 0; - return 1; + return reinterpret_cast<const void *>(i); + return nullptr; +} + +int __sanitizer_verify_contiguous_container(const void *beg_p, + const void *mid_p, + const void *end_p) { + return __sanitizer_contiguous_container_find_bad_address(beg_p, mid_p, + end_p) == nullptr; } extern "C" SANITIZER_INTERFACE_ATTRIBUTE diff --git a/contrib/compiler-rt/lib/asan/asan_posix.cc b/contrib/compiler-rt/lib/asan/asan_posix.cc index 2e857f6f624c..9e01bcd091bf 100644 --- a/contrib/compiler-rt/lib/asan/asan_posix.cc +++ b/contrib/compiler-rt/lib/asan/asan_posix.cc @@ -33,11 +33,11 @@ namespace __asan { -void AsanOnSIGSEGV(int, void *siginfo, void *context) { +void AsanOnDeadlySignal(int signo, void *siginfo, void *context) { ScopedDeadlySignal signal_scope(GetCurrentThread()); int code = (int)((siginfo_t*)siginfo)->si_code; // Write the first message using the bullet-proof write. - if (13 != internal_write(2, "ASAN:SIGSEGV\n", 13)) Die(); + if (18 != internal_write(2, "ASAN:DEADLYSIGNAL\n", 18)) Die(); SignalContext sig = SignalContext::Create(siginfo, context); // Access at a reasonable offset above SP, or slightly below it (to account @@ -75,8 +75,12 @@ void AsanOnSIGSEGV(int, void *siginfo, void *context) { // unaligned memory access. if (IsStackAccess && (code == si_SEGV_MAPERR || code == si_SEGV_ACCERR)) ReportStackOverflow(sig); + else if (signo == SIGFPE) + ReportDeadlySignal("FPE", sig); + else if (signo == SIGILL) + ReportDeadlySignal("ILL", sig); else - ReportSIGSEGV("SEGV", sig); + ReportDeadlySignal("SEGV", sig); } // ---------------------- TSD ---------------- {{{1 diff --git a/contrib/compiler-rt/lib/asan/asan_report.cc b/contrib/compiler-rt/lib/asan/asan_report.cc index c1681e644464..bb7e36e84652 100644 --- a/contrib/compiler-rt/lib/asan/asan_report.cc +++ b/contrib/compiler-rt/lib/asan/asan_report.cc @@ -11,6 +11,7 @@ // // This file contains error reporting code. //===----------------------------------------------------------------------===// + #include "asan_flags.h" #include "asan_internal.h" #include "asan_mapping.h" @@ -27,9 +28,11 @@ namespace __asan { // -------------------- User-specified callbacks ----------------- {{{1 static void (*error_report_callback)(const char*); -static char *error_message_buffer = 0; +static char *error_message_buffer = nullptr; static uptr error_message_buffer_pos = 0; -static uptr error_message_buffer_size = 0; +static BlockingMutex error_message_buf_mutex(LINKER_INITIALIZED); +static const unsigned kAsanBuggyPcPoolSize = 25; +static __sanitizer::atomic_uintptr_t AsanBuggyPcPool[kAsanBuggyPcPoolSize]; struct ReportData { uptr pc; @@ -45,16 +48,20 @@ static bool report_happened = false; static ReportData report_data = {}; void AppendToErrorMessageBuffer(const char *buffer) { - if (error_message_buffer) { - uptr length = internal_strlen(buffer); - CHECK_GE(error_message_buffer_size, error_message_buffer_pos); - uptr remaining = error_message_buffer_size - error_message_buffer_pos; - internal_strncpy(error_message_buffer + error_message_buffer_pos, - buffer, remaining); - error_message_buffer[error_message_buffer_size - 1] = '\0'; - // FIXME: reallocate the buffer instead of truncating the message. - error_message_buffer_pos += Min(remaining, length); + BlockingMutexLock l(&error_message_buf_mutex); + if (!error_message_buffer) { + error_message_buffer = + (char*)MmapOrDieQuietly(kErrorMessageBufferSize, __func__); + error_message_buffer_pos = 0; } + uptr length = internal_strlen(buffer); + RAW_CHECK(kErrorMessageBufferSize >= error_message_buffer_pos); + uptr remaining = kErrorMessageBufferSize - error_message_buffer_pos; + internal_strncpy(error_message_buffer + error_message_buffer_pos, + buffer, remaining); + error_message_buffer[kErrorMessageBufferSize - 1] = '\0'; + // FIXME: reallocate the buffer instead of truncating the message. + error_message_buffer_pos += Min(remaining, length); } // ---------------------- Decorator ------------------------------ {{{1 @@ -373,7 +380,7 @@ static void PrintAccessAndVarIntersection(const StackVarDescr &var, uptr addr, uptr next_var_beg) { uptr var_end = var.beg + var.size; uptr addr_end = addr + access_size; - const char *pos_descr = 0; + const char *pos_descr = nullptr; // If the variable [var.beg, var_end) is the nearest variable to the // current memory access, indicate it in the log. if (addr >= var.beg) { @@ -544,7 +551,7 @@ void DescribeHeapAddress(uptr addr, uptr access_size) { StackTrace alloc_stack = chunk.GetAllocStack(); char tname[128]; Decorator d; - AsanThreadContext *free_thread = 0; + AsanThreadContext *free_thread = nullptr; if (chunk.FreeTid() != kInvalidTid) { free_thread = GetThreadContextByTidLocked(chunk.FreeTid()); Printf("%sfreed by thread T%d%s here:%s\n", d.Allocation(), @@ -621,26 +628,90 @@ void DescribeThread(AsanThreadContext *context) { // immediately after printing error report. class ScopedInErrorReport { public: - explicit ScopedInErrorReport(ReportData *report = nullptr) { - static atomic_uint32_t num_calls; - static u32 reporting_thread_tid; - if (atomic_fetch_add(&num_calls, 1, memory_order_relaxed) != 0) { + explicit ScopedInErrorReport(ReportData *report = nullptr, + bool fatal = false) { + halt_on_error_ = fatal || flags()->halt_on_error; + + if (lock_.TryLock()) { + StartReporting(report); + return; + } + + // ASan found two bugs in different threads simultaneously. + + u32 current_tid = GetCurrentTidOrInvalid(); + if (reporting_thread_tid_ == current_tid || + reporting_thread_tid_ == kInvalidTid) { + // This is either asynch signal or nested error during error reporting. + // Fail simple to avoid deadlocks in Report(). + + // Can't use Report() here because of potential deadlocks + // in nested signal handlers. + const char msg[] = "AddressSanitizer: nested bug in the same thread, " + "aborting.\n"; + WriteToFile(kStderrFd, msg, sizeof(msg)); + + internal__exit(common_flags()->exitcode); + } + + if (halt_on_error_) { // Do not print more than one report, otherwise they will mix up. // Error reporting functions shouldn't return at this situation, as - // they are defined as no-return. + // they are effectively no-returns. + Report("AddressSanitizer: while reporting a bug found another one. " - "Ignoring.\n"); - u32 current_tid = GetCurrentTidOrInvalid(); - if (current_tid != reporting_thread_tid) { - // ASan found two bugs in different threads simultaneously. Sleep - // long enough to make sure that the thread which started to print - // an error report will finish doing it. - SleepForSeconds(Max(100, flags()->sleep_before_dying + 1)); - } + "Ignoring.\n"); + + // Sleep long enough to make sure that the thread which started + // to print an error report will finish doing it. + SleepForSeconds(Max(100, flags()->sleep_before_dying + 1)); + // If we're still not dead for some reason, use raw _exit() instead of // Die() to bypass any additional checks. - internal__exit(flags()->exitcode); + internal__exit(common_flags()->exitcode); + } else { + // The other thread will eventually finish reporting + // so it's safe to wait + lock_.Lock(); } + + StartReporting(report); + } + + ~ScopedInErrorReport() { + // Make sure the current thread is announced. + DescribeThread(GetCurrentThread()); + // We may want to grab this lock again when printing stats. + asanThreadRegistry().Unlock(); + // Print memory stats. + if (flags()->print_stats) + __asan_print_accumulated_stats(); + + // Copy the message buffer so that we could start logging without holding a + // lock that gets aquired during printing. + InternalScopedBuffer<char> buffer_copy(kErrorMessageBufferSize); + { + BlockingMutexLock l(&error_message_buf_mutex); + internal_memcpy(buffer_copy.data(), + error_message_buffer, kErrorMessageBufferSize); + } + + LogFullErrorReport(buffer_copy.data()); + + if (error_report_callback) { + error_report_callback(buffer_copy.data()); + } + CommonSanitizerReportMutex.Unlock(); + reporting_thread_tid_ = kInvalidTid; + lock_.Unlock(); + if (halt_on_error_) { + Report("ABORTING\n"); + Die(); + } + } + + private: + void StartReporting(ReportData *report) { if (report) report_data = *report; report_happened = true; ASAN_ON_ERROR(); @@ -650,27 +721,19 @@ class ScopedInErrorReport { // recursive reports. asanThreadRegistry().Lock(); CommonSanitizerReportMutex.Lock(); - reporting_thread_tid = GetCurrentTidOrInvalid(); + reporting_thread_tid_ = GetCurrentTidOrInvalid(); Printf("====================================================" "=============\n"); } - // Destructor is NORETURN, as functions that report errors are. - NORETURN ~ScopedInErrorReport() { - // Make sure the current thread is announced. - DescribeThread(GetCurrentThread()); - // We may want to grab this lock again when printing stats. - asanThreadRegistry().Unlock(); - // Print memory stats. - if (flags()->print_stats) - __asan_print_accumulated_stats(); - if (error_report_callback) { - error_report_callback(error_message_buffer); - } - Report("ABORTING\n"); - Die(); - } + + static StaticSpinMutex lock_; + static u32 reporting_thread_tid_; + bool halt_on_error_; }; +StaticSpinMutex ScopedInErrorReport::lock_; +u32 ScopedInErrorReport::reporting_thread_tid_; + void ReportStackOverflow(const SignalContext &sig) { ScopedInErrorReport in_report; Decorator d; @@ -686,8 +749,8 @@ void ReportStackOverflow(const SignalContext &sig) { ReportErrorSummary("stack-overflow", &stack); } -void ReportSIGSEGV(const char *description, const SignalContext &sig) { - ScopedInErrorReport in_report; +void ReportDeadlySignal(const char *description, const SignalContext &sig) { + ScopedInErrorReport in_report(/*report*/nullptr, /*fatal*/true); Decorator d; Printf("%s", d.Warning()); Report( @@ -703,7 +766,7 @@ void ReportSIGSEGV(const char *description, const SignalContext &sig) { stack.Print(); MaybeDumpInstructionBytes(sig.pc); Printf("AddressSanitizer can not provide additional info.\n"); - ReportErrorSummary("SEGV", &stack); + ReportErrorSummary(description, &stack); } void ReportDoubleFree(uptr addr, BufferedStackTrace *free_stack) { @@ -744,7 +807,7 @@ void ReportNewDeleteSizeMismatch(uptr addr, uptr delete_size, stack.Print(); DescribeHeapAddress(addr, 1); ReportErrorSummary("new-delete-type-mismatch", &stack); - Report("HINT: if you don't care about these warnings you may set " + Report("HINT: if you don't care about these errors you may set " "ASAN_OPTIONS=new_delete_type_mismatch=0\n"); } @@ -784,7 +847,7 @@ void ReportAllocTypeMismatch(uptr addr, BufferedStackTrace *free_stack, stack.Print(); DescribeHeapAddress(addr, 1); ReportErrorSummary("alloc-dealloc-mismatch", &stack); - Report("HINT: if you don't care about these warnings you may set " + Report("HINT: if you don't care about these errors you may set " "ASAN_OPTIONS=alloc_dealloc_mismatch=0\n"); } @@ -886,7 +949,7 @@ void ReportODRViolation(const __asan_global *g1, u32 stack_id1, Printf(" [2]:\n"); StackDepotGet(stack_id2).Print(); } - Report("HINT: if you don't care about these warnings you may set " + Report("HINT: if you don't care about these errors you may set " "ASAN_OPTIONS=detect_odr_violation=0\n"); InternalScopedString error_msg(256); error_msg.append("odr-violation: global '%s' at %s", @@ -925,17 +988,6 @@ static INLINE void CheckForInvalidPointerPair(void *p1, void *p2) { } // ----------------------- Mac-specific reports ----------------- {{{1 -void WarnMacFreeUnallocated(uptr addr, uptr zone_ptr, const char *zone_name, - BufferedStackTrace *stack) { - // Just print a warning here. - Printf("free_common(%p) -- attempting to free unallocated memory.\n" - "AddressSanitizer is ignoring this error on Mac OS now.\n", - addr); - PrintZoneForPointer(addr, zone_ptr, zone_name); - stack->Print(); - DescribeHeapAddress(addr, 1); -} - void ReportMacMzReallocUnknown(uptr addr, uptr zone_ptr, const char *zone_name, BufferedStackTrace *stack) { ScopedInErrorReport in_report; @@ -947,24 +999,23 @@ void ReportMacMzReallocUnknown(uptr addr, uptr zone_ptr, const char *zone_name, DescribeHeapAddress(addr, 1); } -void ReportMacCfReallocUnknown(uptr addr, uptr zone_ptr, const char *zone_name, - BufferedStackTrace *stack) { - ScopedInErrorReport in_report; - Printf("cf_realloc(%p) -- attempting to realloc unallocated memory.\n" - "This is an unrecoverable problem, exiting now.\n", - addr); - PrintZoneForPointer(addr, zone_ptr, zone_name); - stack->Print(); - DescribeHeapAddress(addr, 1); +// -------------- SuppressErrorReport -------------- {{{1 +// Avoid error reports duplicating for ASan recover mode. +static bool SuppressErrorReport(uptr pc) { + if (!common_flags()->suppress_equal_pcs) return false; + for (unsigned i = 0; i < kAsanBuggyPcPoolSize; i++) { + uptr cmp = atomic_load_relaxed(&AsanBuggyPcPool[i]); + if (cmp == 0 && atomic_compare_exchange_strong(&AsanBuggyPcPool[i], &cmp, + pc, memory_order_relaxed)) + return false; + if (cmp == pc) return true; + } + Die(); } -} // namespace __asan - -// --------------------------- Interface --------------------- {{{1 -using namespace __asan; // NOLINT - -void __asan_report_error(uptr pc, uptr bp, uptr sp, uptr addr, int is_write, - uptr access_size, u32 exp) { +void ReportGenericError(uptr pc, uptr bp, uptr sp, uptr addr, bool is_write, + uptr access_size, u32 exp, bool fatal) { + if (!fatal && SuppressErrorReport(pc)) return; ENABLE_FRAME_POINTER; // Optimization experiments. @@ -1033,7 +1084,7 @@ void __asan_report_error(uptr pc, uptr bp, uptr sp, uptr addr, int is_write, ReportData report = { pc, sp, bp, addr, (bool)is_write, access_size, bug_descr }; - ScopedInErrorReport in_report(&report); + ScopedInErrorReport in_report(&report, fatal); Decorator d; Printf("%s", d.Warning()); @@ -1059,14 +1110,21 @@ void __asan_report_error(uptr pc, uptr bp, uptr sp, uptr addr, int is_write, PrintShadowMemoryForAddress(addr); } +} // namespace __asan + +// --------------------------- Interface --------------------- {{{1 +using namespace __asan; // NOLINT + +void __asan_report_error(uptr pc, uptr bp, uptr sp, uptr addr, int is_write, + uptr access_size, u32 exp) { + ENABLE_FRAME_POINTER; + bool fatal = flags()->halt_on_error; + ReportGenericError(pc, bp, sp, addr, is_write, access_size, exp, fatal); +} + void NOINLINE __asan_set_error_report_callback(void (*callback)(const char*)) { + BlockingMutexLock l(&error_message_buf_mutex); error_report_callback = callback; - if (callback) { - error_message_buffer_size = 1 << 16; - error_message_buffer = - (char*)MmapOrDie(error_message_buffer_size, __func__); - error_message_buffer_pos = 0; - } } void __asan_describe_address(uptr addr) { @@ -1117,7 +1175,7 @@ SANITIZER_INTERFACE_ATTRIBUTE void __sanitizer_ptr_cmp(void *a, void *b) { CheckForInvalidPointerPair(a, b); } -} // extern "C" +} // extern "C" #if !SANITIZER_SUPPORTS_WEAK_HOOKS // Provide default implementation of __asan_on_error that does nothing diff --git a/contrib/compiler-rt/lib/asan/asan_report.h b/contrib/compiler-rt/lib/asan/asan_report.h index e2786b0f260c..559b8adfd51d 100644 --- a/contrib/compiler-rt/lib/asan/asan_report.h +++ b/contrib/compiler-rt/lib/asan/asan_report.h @@ -49,44 +49,39 @@ bool DescribeAddressIfStack(uptr addr, uptr access_size); void DescribeThread(AsanThreadContext *context); // Different kinds of error reports. -void NORETURN ReportStackOverflow(const SignalContext &sig); -void NORETURN ReportSIGSEGV(const char *description, const SignalContext &sig); -void NORETURN ReportNewDeleteSizeMismatch(uptr addr, uptr delete_size, - BufferedStackTrace *free_stack); -void NORETURN ReportDoubleFree(uptr addr, BufferedStackTrace *free_stack); -void NORETURN ReportFreeNotMalloced(uptr addr, BufferedStackTrace *free_stack); -void NORETURN ReportAllocTypeMismatch(uptr addr, BufferedStackTrace *free_stack, - AllocType alloc_type, - AllocType dealloc_type); -void NORETURN - ReportMallocUsableSizeNotOwned(uptr addr, BufferedStackTrace *stack); -void NORETURN - ReportSanitizerGetAllocatedSizeNotOwned(uptr addr, - BufferedStackTrace *stack); -void NORETURN - ReportStringFunctionMemoryRangesOverlap(const char *function, - const char *offset1, uptr length1, - const char *offset2, uptr length2, - BufferedStackTrace *stack); -void NORETURN ReportStringFunctionSizeOverflow(uptr offset, uptr size, - BufferedStackTrace *stack); -void NORETURN - ReportBadParamsToAnnotateContiguousContainer(uptr beg, uptr end, - uptr old_mid, uptr new_mid, - BufferedStackTrace *stack); +void ReportGenericError(uptr pc, uptr bp, uptr sp, uptr addr, bool is_write, + uptr access_size, u32 exp, bool fatal); +void ReportStackOverflow(const SignalContext &sig); +void ReportDeadlySignal(const char *description, const SignalContext &sig); +void ReportNewDeleteSizeMismatch(uptr addr, uptr delete_size, + BufferedStackTrace *free_stack); +void ReportDoubleFree(uptr addr, BufferedStackTrace *free_stack); +void ReportFreeNotMalloced(uptr addr, BufferedStackTrace *free_stack); +void ReportAllocTypeMismatch(uptr addr, BufferedStackTrace *free_stack, + AllocType alloc_type, + AllocType dealloc_type); +void ReportMallocUsableSizeNotOwned(uptr addr, BufferedStackTrace *stack); +void ReportSanitizerGetAllocatedSizeNotOwned(uptr addr, + BufferedStackTrace *stack); +void ReportStringFunctionMemoryRangesOverlap(const char *function, + const char *offset1, uptr length1, + const char *offset2, uptr length2, + BufferedStackTrace *stack); +void ReportStringFunctionSizeOverflow(uptr offset, uptr size, + BufferedStackTrace *stack); +void ReportBadParamsToAnnotateContiguousContainer(uptr beg, uptr end, + uptr old_mid, uptr new_mid, + BufferedStackTrace *stack); -void NORETURN -ReportODRViolation(const __asan_global *g1, u32 stack_id1, - const __asan_global *g2, u32 stack_id2); +void ReportODRViolation(const __asan_global *g1, u32 stack_id1, + const __asan_global *g2, u32 stack_id2); // Mac-specific errors and warnings. -void WarnMacFreeUnallocated(uptr addr, uptr zone_ptr, const char *zone_name, - BufferedStackTrace *stack); -void NORETURN ReportMacMzReallocUnknown(uptr addr, uptr zone_ptr, - const char *zone_name, - BufferedStackTrace *stack); -void NORETURN ReportMacCfReallocUnknown(uptr addr, uptr zone_ptr, - const char *zone_name, - BufferedStackTrace *stack); +void ReportMacMzReallocUnknown(uptr addr, uptr zone_ptr, + const char *zone_name, + BufferedStackTrace *stack); +void ReportMacCfReallocUnknown(uptr addr, uptr zone_ptr, + const char *zone_name, + BufferedStackTrace *stack); } // namespace __asan diff --git a/contrib/compiler-rt/lib/asan/asan_rtl.cc b/contrib/compiler-rt/lib/asan/asan_rtl.cc index a8d92b915a9a..7b8b5dd9be1b 100644 --- a/contrib/compiler-rt/lib/asan/asan_rtl.cc +++ b/contrib/compiler-rt/lib/asan/asan_rtl.cc @@ -11,6 +11,7 @@ // // Main file of the ASan run-time library. //===----------------------------------------------------------------------===// + #include "asan_activation.h" #include "asan_allocator.h" #include "asan_interceptors.h" @@ -56,11 +57,6 @@ static void AsanDie() { UnmapOrDie((void*)kLowShadowBeg, kHighShadowEnd - kLowShadowBeg); } } - if (common_flags()->coverage) - __sanitizer_cov_dump(); - if (flags()->abort_on_error) - Abort(); - internal__exit(flags()->exitcode); } static void AsanCheckFailed(const char *file, int line, const char *cond, @@ -117,13 +113,18 @@ static void OnLowLevelAllocate(uptr ptr, uptr size) { extern "C" NOINLINE INTERFACE_ATTRIBUTE \ void __asan_report_ ## type ## size(uptr addr) { \ GET_CALLER_PC_BP_SP; \ - __asan_report_error(pc, bp, sp, addr, is_write, size, 0); \ + ReportGenericError(pc, bp, sp, addr, is_write, size, 0, true); \ } \ extern "C" NOINLINE INTERFACE_ATTRIBUTE \ void __asan_report_exp_ ## type ## size(uptr addr, u32 exp) { \ GET_CALLER_PC_BP_SP; \ - __asan_report_error(pc, bp, sp, addr, is_write, size, exp); \ -} + ReportGenericError(pc, bp, sp, addr, is_write, size, exp, true); \ +} \ +extern "C" NOINLINE INTERFACE_ATTRIBUTE \ +void __asan_report_ ## type ## size ## _noabort(uptr addr) { \ + GET_CALLER_PC_BP_SP; \ + ReportGenericError(pc, bp, sp, addr, is_write, size, 0, false); \ +} \ ASAN_REPORT_ERROR(load, false, 1) ASAN_REPORT_ERROR(load, false, 2) @@ -136,22 +137,27 @@ ASAN_REPORT_ERROR(store, true, 4) ASAN_REPORT_ERROR(store, true, 8) ASAN_REPORT_ERROR(store, true, 16) -#define ASAN_REPORT_ERROR_N(type, is_write) \ -extern "C" NOINLINE INTERFACE_ATTRIBUTE \ -void __asan_report_ ## type ## _n(uptr addr, uptr size) { \ - GET_CALLER_PC_BP_SP; \ - __asan_report_error(pc, bp, sp, addr, is_write, size, 0); \ -} \ -extern "C" NOINLINE INTERFACE_ATTRIBUTE \ +#define ASAN_REPORT_ERROR_N(type, is_write) \ +extern "C" NOINLINE INTERFACE_ATTRIBUTE \ +void __asan_report_ ## type ## _n(uptr addr, uptr size) { \ + GET_CALLER_PC_BP_SP; \ + ReportGenericError(pc, bp, sp, addr, is_write, size, 0, true); \ +} \ +extern "C" NOINLINE INTERFACE_ATTRIBUTE \ void __asan_report_exp_ ## type ## _n(uptr addr, uptr size, u32 exp) { \ GET_CALLER_PC_BP_SP; \ - __asan_report_error(pc, bp, sp, addr, is_write, size, exp); \ -} + ReportGenericError(pc, bp, sp, addr, is_write, size, exp, true); \ +} \ +extern "C" NOINLINE INTERFACE_ATTRIBUTE \ +void __asan_report_ ## type ## _n_noabort(uptr addr, uptr size) { \ + GET_CALLER_PC_BP_SP; \ + ReportGenericError(pc, bp, sp, addr, is_write, size, 0, false); \ +} \ ASAN_REPORT_ERROR_N(load, false) ASAN_REPORT_ERROR_N(store, true) -#define ASAN_MEMORY_ACCESS_CALLBACK_BODY(type, is_write, size, exp_arg) \ +#define ASAN_MEMORY_ACCESS_CALLBACK_BODY(type, is_write, size, exp_arg, fatal) \ uptr sp = MEM_TO_SHADOW(addr); \ uptr s = size <= SHADOW_GRANULARITY ? *reinterpret_cast<u8 *>(sp) \ : *reinterpret_cast<u16 *>(sp); \ @@ -163,7 +169,8 @@ ASAN_REPORT_ERROR_N(store, true) *__asan_test_only_reported_buggy_pointer = addr; \ } else { \ GET_CALLER_PC_BP_SP; \ - __asan_report_error(pc, bp, sp, addr, is_write, size, exp_arg); \ + ReportGenericError(pc, bp, sp, addr, is_write, size, exp_arg, \ + fatal); \ } \ } \ } @@ -171,12 +178,16 @@ ASAN_REPORT_ERROR_N(store, true) #define ASAN_MEMORY_ACCESS_CALLBACK(type, is_write, size) \ extern "C" NOINLINE INTERFACE_ATTRIBUTE \ void __asan_##type##size(uptr addr) { \ - ASAN_MEMORY_ACCESS_CALLBACK_BODY(type, is_write, size, 0) \ + ASAN_MEMORY_ACCESS_CALLBACK_BODY(type, is_write, size, 0, true) \ } \ extern "C" NOINLINE INTERFACE_ATTRIBUTE \ void __asan_exp_##type##size(uptr addr, u32 exp) { \ - ASAN_MEMORY_ACCESS_CALLBACK_BODY(type, is_write, size, exp) \ - } + ASAN_MEMORY_ACCESS_CALLBACK_BODY(type, is_write, size, exp, true) \ + } \ + extern "C" NOINLINE INTERFACE_ATTRIBUTE \ + void __asan_##type##size ## _noabort(uptr addr) { \ + ASAN_MEMORY_ACCESS_CALLBACK_BODY(type, is_write, size, 0, false) \ + } \ ASAN_MEMORY_ACCESS_CALLBACK(load, false, 1) ASAN_MEMORY_ACCESS_CALLBACK(load, false, 2) @@ -194,7 +205,7 @@ NOINLINE INTERFACE_ATTRIBUTE void __asan_loadN(uptr addr, uptr size) { if (__asan_region_is_poisoned(addr, size)) { GET_CALLER_PC_BP_SP; - __asan_report_error(pc, bp, sp, addr, false, size, 0); + ReportGenericError(pc, bp, sp, addr, false, size, 0, true); } } @@ -203,7 +214,16 @@ NOINLINE INTERFACE_ATTRIBUTE void __asan_exp_loadN(uptr addr, uptr size, u32 exp) { if (__asan_region_is_poisoned(addr, size)) { GET_CALLER_PC_BP_SP; - __asan_report_error(pc, bp, sp, addr, false, size, exp); + ReportGenericError(pc, bp, sp, addr, false, size, exp, true); + } +} + +extern "C" +NOINLINE INTERFACE_ATTRIBUTE +void __asan_loadN_noabort(uptr addr, uptr size) { + if (__asan_region_is_poisoned(addr, size)) { + GET_CALLER_PC_BP_SP; + ReportGenericError(pc, bp, sp, addr, false, size, 0, false); } } @@ -212,7 +232,7 @@ NOINLINE INTERFACE_ATTRIBUTE void __asan_storeN(uptr addr, uptr size) { if (__asan_region_is_poisoned(addr, size)) { GET_CALLER_PC_BP_SP; - __asan_report_error(pc, bp, sp, addr, true, size, 0); + ReportGenericError(pc, bp, sp, addr, true, size, 0, true); } } @@ -221,7 +241,16 @@ NOINLINE INTERFACE_ATTRIBUTE void __asan_exp_storeN(uptr addr, uptr size, u32 exp) { if (__asan_region_is_poisoned(addr, size)) { GET_CALLER_PC_BP_SP; - __asan_report_error(pc, bp, sp, addr, true, size, exp); + ReportGenericError(pc, bp, sp, addr, true, size, exp, true); + } +} + +extern "C" +NOINLINE INTERFACE_ATTRIBUTE +void __asan_storeN_noabort(uptr addr, uptr size) { + if (__asan_region_is_poisoned(addr, size)) { + GET_CALLER_PC_BP_SP; + ReportGenericError(pc, bp, sp, addr, true, size, 0, false); } } @@ -259,16 +288,15 @@ static NOINLINE void force_interface_symbols() { case 22: __asan_report_exp_store8(0, 0); break; case 23: __asan_report_exp_store16(0, 0); break; case 24: __asan_report_exp_store_n(0, 0, 0); break; - case 25: __asan_register_globals(0, 0); break; - case 26: __asan_unregister_globals(0, 0); break; - case 27: __asan_set_death_callback(0); break; - case 28: __asan_set_error_report_callback(0); break; + case 25: __asan_register_globals(nullptr, 0); break; + case 26: __asan_unregister_globals(nullptr, 0); break; + case 27: __asan_set_death_callback(nullptr); break; + case 28: __asan_set_error_report_callback(nullptr); break; case 29: __asan_handle_no_return(); break; - case 30: __asan_address_is_poisoned(0); break; - case 31: __asan_poison_memory_region(0, 0); break; - case 32: __asan_unpoison_memory_region(0, 0); break; - case 33: __asan_set_error_exit_code(0); break; - case 34: __asan_before_dynamic_init(0); break; + case 30: __asan_address_is_poisoned(nullptr); break; + case 31: __asan_poison_memory_region(nullptr, 0); break; + case 32: __asan_unpoison_memory_region(nullptr, 0); break; + case 34: __asan_before_dynamic_init(nullptr); break; case 35: __asan_after_dynamic_init(); break; case 36: __asan_poison_stack_memory(0, 0); break; case 37: __asan_unpoison_stack_memory(0, 0); break; @@ -298,9 +326,25 @@ static void InitializeHighMemEnd() { } static void ProtectGap(uptr addr, uptr size) { + if (!flags()->protect_shadow_gap) + return; void *res = MmapNoAccess(addr, size, "shadow gap"); if (addr == (uptr)res) return; + // A few pages at the start of the address space can not be protected. + // But we really want to protect as much as possible, to prevent this memory + // being returned as a result of a non-FIXED mmap(). + if (addr == kZeroBaseShadowStart) { + uptr step = GetPageSizeCached(); + while (size > step && addr < kZeroBaseMaxShadowStart) { + addr += step; + size -= step; + void *res = MmapNoAccess(addr, size, "shadow gap"); + if (addr == (uptr)res) + return; + } + } + Report("ERROR: Failed to protect the shadow gap. " "ASan cannot proceed correctly. ABORTING.\n"); DumpProcessMap(); @@ -363,12 +407,12 @@ static void AsanInitInternal() { CHECK(!asan_init_is_running && "ASan init calls itself!"); asan_init_is_running = true; + CacheBinaryName(); + // Initialize flags. This must be done early, because most of the // initialization steps look at flags(). InitializeFlags(); - CacheBinaryName(); - AsanCheckIncompatibleRT(); AsanCheckDynamicRTPrereqs(); @@ -381,7 +425,7 @@ static void AsanInitInternal() { AsanDoesNotSupportStaticLinkage(); // Install tool-specific callbacks in sanitizer_common. - SetDieCallback(AsanDie); + AddDieCallback(AsanDie); SetCheckFailedCallback(AsanCheckFailed); SetPrintfAndReportCallback(AppendToErrorMessageBuffer); @@ -457,7 +501,7 @@ static void AsanInitInternal() { } AsanTSDInit(PlatformTSDDtor); - InstallDeadlySignalHandlers(AsanOnSIGSEGV); + InstallDeadlySignalHandlers(AsanOnDeadlySignal); AllocatorOptions allocator_options; allocator_options.SetFrom(flags(), common_flags()); @@ -531,24 +575,26 @@ public: // NOLINT static AsanInitializer asan_initializer; #endif // ASAN_DYNAMIC -} // namespace __asan +} // namespace __asan // ---------------------- Interface ---------------- {{{1 using namespace __asan; // NOLINT -int NOINLINE __asan_set_error_exit_code(int exit_code) { - int old = flags()->exitcode; - flags()->exitcode = exit_code; - return old; -} - void NOINLINE __asan_handle_no_return() { int local_stack; AsanThread *curr_thread = GetCurrentThread(); - CHECK(curr_thread); uptr PageSize = GetPageSizeCached(); - uptr top = curr_thread->stack_top(); - uptr bottom = ((uptr)&local_stack - PageSize) & ~(PageSize-1); + uptr top, bottom; + if (curr_thread) { + top = curr_thread->stack_top(); + bottom = ((uptr)&local_stack - PageSize) & ~(PageSize - 1); + } else { + // If we haven't seen this thread, try asking the OS for stack bounds. + uptr tls_addr, tls_size, stack_size; + GetThreadStackAndTls(/*main=*/false, &bottom, &stack_size, &tls_addr, + &tls_size); + top = bottom + stack_size; + } static const uptr kMaxExpectedCleanupSize = 64 << 20; // 64M if (top - bottom > kMaxExpectedCleanupSize) { static bool reported_warning = false; @@ -559,12 +605,12 @@ void NOINLINE __asan_handle_no_return() { "stack top: %p; bottom %p; size: %p (%zd)\n" "False positive error reports may follow\n" "For details see " - "http://code.google.com/p/address-sanitizer/issues/detail?id=189\n", + "https://github.com/google/sanitizers/issues/189\n", top, bottom, top - bottom, top - bottom); return; } PoisonShadow(bottom, top - bottom, 0); - if (curr_thread->has_fake_stack()) + if (curr_thread && curr_thread->has_fake_stack()) curr_thread->fake_stack()->HandleNoReturn(); } @@ -578,3 +624,7 @@ void __asan_init() { AsanActivate(); AsanInitInternal(); } + +void __asan_version_mismatch_check() { + // Do nothing. +} diff --git a/contrib/compiler-rt/lib/asan/asan_stack.h b/contrib/compiler-rt/lib/asan/asan_stack.h index 122967a152f8..5c5181509801 100644 --- a/contrib/compiler-rt/lib/asan/asan_stack.h +++ b/contrib/compiler-rt/lib/asan/asan_stack.h @@ -11,6 +11,7 @@ // // ASan-private header for asan_stack.cc. //===----------------------------------------------------------------------===// + #ifndef ASAN_STACK_H #define ASAN_STACK_H @@ -48,15 +49,15 @@ void GetStackTraceWithPcBpAndContext(BufferedStackTrace *stack, uptr max_depth, uptr stack_bottom = t->stack_bottom(); ScopedUnwinding unwind_scope(t); stack->Unwind(max_depth, pc, bp, context, stack_top, stack_bottom, fast); - } else if (t == 0 && !fast) { + } else if (!t && !fast) { /* If GetCurrentThread() has failed, try to do slow unwind anyways. */ stack->Unwind(max_depth, pc, bp, context, 0, 0, false); } } -#endif // SANITIZER_WINDOWS +#endif // SANITIZER_WINDOWS } -} // namespace __asan +} // namespace __asan // NOTE: A Rule of thumb is to retrieve stack trace in the interceptors // as early as possible (in functions exposed to the user), as we generally @@ -115,4 +116,4 @@ void GetStackTraceWithPcBpAndContext(BufferedStackTrace *stack, uptr max_depth, stack.Print(); \ } -#endif // ASAN_STACK_H +#endif // ASAN_STACK_H diff --git a/contrib/compiler-rt/lib/asan/asan_thread.cc b/contrib/compiler-rt/lib/asan/asan_thread.cc index 9af5706d86d0..69813546f551 100644 --- a/contrib/compiler-rt/lib/asan/asan_thread.cc +++ b/contrib/compiler-rt/lib/asan/asan_thread.cc @@ -42,7 +42,7 @@ void AsanThreadContext::OnCreated(void *arg) { void AsanThreadContext::OnFinished() { // Drop the link to the AsanThread object. - thread = 0; + thread = nullptr; } // MIPS requires aligned address @@ -125,7 +125,7 @@ void AsanThread::Destroy() { FakeStack *AsanThread::AsyncSignalSafeLazyInitFakeStack() { uptr stack_size = this->stack_size(); if (stack_size == 0) // stack_size is not yet available, don't use FakeStack. - return 0; + return nullptr; uptr old_val = 0; // fake_stack_ has 3 states: // 0 -- not initialized @@ -146,11 +146,11 @@ FakeStack *AsanThread::AsyncSignalSafeLazyInitFakeStack() { SetTLSFakeStack(fake_stack_); return fake_stack_; } - return 0; + return nullptr; } void AsanThread::Init() { - fake_stack_ = 0; // Will be initialized lazily if needed. + fake_stack_ = nullptr; // Will be initialized lazily if needed. CHECK_EQ(this->stack_size(), 0U); SetThreadStackAndTls(); CHECK_GT(this->stack_size(), 0U); @@ -161,13 +161,12 @@ void AsanThread::Init() { VReport(1, "T%d: stack [%p,%p) size 0x%zx; local=%p\n", tid(), (void *)stack_bottom_, (void *)stack_top_, stack_top_ - stack_bottom_, &local); - AsanPlatformThreadInit(); } thread_return_t AsanThread::ThreadStart( uptr os_id, atomic_uintptr_t *signal_thread_is_registered) { Init(); - asanThreadRegistry().StartThread(tid(), os_id, 0); + asanThreadRegistry().StartThread(tid(), os_id, nullptr); if (signal_thread_is_registered) atomic_store(signal_thread_is_registered, 1, memory_order_release); @@ -277,7 +276,7 @@ AsanThread *GetCurrentThread() { return tctx->thread; } } - return 0; + return nullptr; } return context->thread; } @@ -302,7 +301,7 @@ AsanThread *FindThreadByStackAddress(uptr addr) { AsanThreadContext *tctx = static_cast<AsanThreadContext *>( asanThreadRegistry().FindThreadContextLocked(ThreadStackContainsAddress, (void *)addr)); - return tctx ? tctx->thread : 0; + return tctx ? tctx->thread : nullptr; } void EnsureMainThreadIDIsCorrect() { @@ -315,10 +314,10 @@ void EnsureMainThreadIDIsCorrect() { __asan::AsanThread *GetAsanThreadByOsIDLocked(uptr os_id) { __asan::AsanThreadContext *context = static_cast<__asan::AsanThreadContext *>( __asan::asanThreadRegistry().FindThreadContextByOsIDLocked(os_id)); - if (!context) return 0; + if (!context) return nullptr; return context->thread; } -} // namespace __asan +} // namespace __asan // --- Implementation of LSan-specific functions --- {{{1 namespace __lsan { @@ -355,4 +354,4 @@ void UnlockThreadRegistry() { void EnsureMainThreadIDIsCorrect() { __asan::EnsureMainThreadIDIsCorrect(); } -} // namespace __lsan +} // namespace __lsan diff --git a/contrib/compiler-rt/lib/asan/asan_thread.h b/contrib/compiler-rt/lib/asan/asan_thread.h index 50acfc42d6a2..ac35711f5794 100644 --- a/contrib/compiler-rt/lib/asan/asan_thread.h +++ b/contrib/compiler-rt/lib/asan/asan_thread.h @@ -11,6 +11,7 @@ // // ASan-private header for asan_thread.cc. //===----------------------------------------------------------------------===// + #ifndef ASAN_THREAD_H #define ASAN_THREAD_H @@ -36,7 +37,7 @@ class AsanThreadContext : public ThreadContextBase { explicit AsanThreadContext(int tid) : ThreadContextBase(tid), announced(false), destructor_iterations(GetPthreadDestructorIterations()), stack_id(0), - thread(0) {} + thread(nullptr) {} bool announced; u8 destructor_iterations; u32 stack_id; @@ -84,8 +85,8 @@ class AsanThread { void DeleteFakeStack(int tid) { if (!fake_stack_) return; FakeStack *t = fake_stack_; - fake_stack_ = 0; - SetTLSFakeStack(0); + fake_stack_ = nullptr; + SetTLSFakeStack(nullptr); t->Destroy(tid); } @@ -95,7 +96,7 @@ class AsanThread { FakeStack *fake_stack() { if (!__asan_option_detect_stack_use_after_return) - return 0; + return nullptr; if (!has_fake_stack()) return AsyncSignalSafeLazyInitFakeStack(); return fake_stack_; @@ -179,6 +180,6 @@ AsanThread *FindThreadByStackAddress(uptr addr); // Used to handle fork(). void EnsureMainThreadIDIsCorrect(); -} // namespace __asan +} // namespace __asan -#endif // ASAN_THREAD_H +#endif // ASAN_THREAD_H diff --git a/contrib/compiler-rt/lib/asan/asan_win.cc b/contrib/compiler-rt/lib/asan/asan_win.cc index addb3d40a696..92bd893d10ef 100644 --- a/contrib/compiler-rt/lib/asan/asan_win.cc +++ b/contrib/compiler-rt/lib/asan/asan_win.cc @@ -14,9 +14,9 @@ #include "sanitizer_common/sanitizer_platform.h" #if SANITIZER_WINDOWS +#define WIN32_LEAN_AND_MEAN #include <windows.h> -#include <dbghelp.h> #include <stdlib.h> #include "asan_interceptors.h" @@ -175,14 +175,6 @@ void PlatformTSDDtor(void *tsd) { // }}} // ---------------------- Various stuff ---------------- {{{ -void DisableReexec() { - // No need to re-exec on Windows. -} - -void MaybeReexec() { - // No need to re-exec on Windows. -} - void *AsanDoesNotSupportStaticLinkage() { #if defined(_DEBUG) #error Please build the runtime with a non-debug CRT: /MD or /MT @@ -194,15 +186,11 @@ void AsanCheckDynamicRTPrereqs() {} void AsanCheckIncompatibleRT() {} -void AsanPlatformThreadInit() { - // Nothing here for now. -} - void ReadContextStack(void *context, uptr *stack, uptr *ssize) { UNIMPLEMENTED(); } -void AsanOnSIGSEGV(int, void *siginfo, void *context) { +void AsanOnDeadlySignal(int, void *siginfo, void *context) { UNIMPLEMENTED(); } @@ -219,7 +207,7 @@ static long WINAPI SEHHandler(EXCEPTION_POINTERS *info) { ? "access-violation" : "in-page-error"; SignalContext sig = SignalContext::Create(exception_record, context); - ReportSIGSEGV(description, sig); + ReportDeadlySignal(description, sig); } // FIXME: Handle EXCEPTION_STACK_OVERFLOW here. @@ -257,7 +245,7 @@ int __asan_set_seh_filter() { // Put a pointer to __asan_set_seh_filter at the end of the global list // of C initializers, after the default EH is set by the CRT. #pragma section(".CRT$XIZ", long, read) // NOLINT -static __declspec(allocate(".CRT$XIZ")) +__declspec(allocate(".CRT$XIZ")) int (*__intercept_seh)() = __asan_set_seh_filter; #endif // }}} diff --git a/contrib/compiler-rt/lib/asan/asan_win_dll_thunk.cc b/contrib/compiler-rt/lib/asan/asan_win_dll_thunk.cc index b77f18168ae5..672cabf43ec1 100644 --- a/contrib/compiler-rt/lib/asan/asan_win_dll_thunk.cc +++ b/contrib/compiler-rt/lib/asan/asan_win_dll_thunk.cc @@ -12,8 +12,7 @@ // This file defines a family of thunks that should be statically linked into // the DLLs that have ASan instrumentation in order to delegate the calls to the // shared runtime that lives in the main binary. -// See https://code.google.com/p/address-sanitizer/issues/detail?id=209 for the -// details. +// See https://github.com/google/sanitizers/issues/209 for the details. //===----------------------------------------------------------------------===// // Only compile this code when buidling asan_dll_thunk.lib @@ -30,8 +29,9 @@ void *__stdcall GetProcAddress(void *module, const char *proc_name); void abort(); } -static void *getRealProcAddressOrDie(const char *name) { - void *ret = GetProcAddress(GetModuleHandleA(0), name); +static uptr getRealProcAddressOrDie(const char *name) { + uptr ret = + __interception::InternalGetProcAddress((void *)GetModuleHandleA(0), name); if (!ret) abort(); return ret; @@ -62,13 +62,12 @@ struct FunctionInterceptor<0> { }; #define INTERCEPT_WHEN_POSSIBLE(main_function, dll_function) \ - template<> struct FunctionInterceptor<__LINE__> { \ + template <> struct FunctionInterceptor<__LINE__> { \ static void Execute() { \ - void *wrapper = getRealProcAddressOrDie(main_function); \ - if (!__interception::OverrideFunction((uptr)dll_function, \ - (uptr)wrapper, 0)) \ + uptr wrapper = getRealProcAddressOrDie(main_function); \ + if (!__interception::OverrideFunction((uptr)dll_function, wrapper, 0)) \ abort(); \ - FunctionInterceptor<__LINE__-1>::Execute(); \ + FunctionInterceptor<__LINE__ - 1>::Execute(); \ } \ }; @@ -210,7 +209,7 @@ extern "C" { // __asan_init is expected to be called by only one thread. if (fn) return; - fn = (fntype)getRealProcAddressOrDie(__asan_init_name); + fn = (fntype)getRealProcAddressOrDie("__asan_init"); fn(); __asan_option_detect_stack_use_after_return = (__asan_should_detect_stack_use_after_return() != 0); @@ -219,6 +218,10 @@ extern "C" { } } +extern "C" void __asan_version_mismatch_check() { + // Do nothing. +} + INTERFACE_FUNCTION(__asan_handle_no_return) INTERFACE_FUNCTION(__asan_report_store1) @@ -253,6 +256,9 @@ INTERFACE_FUNCTION(__asan_memcpy); INTERFACE_FUNCTION(__asan_memset); INTERFACE_FUNCTION(__asan_memmove); +INTERFACE_FUNCTION(__asan_alloca_poison); +INTERFACE_FUNCTION(__asan_allocas_unpoison); + INTERFACE_FUNCTION(__asan_register_globals) INTERFACE_FUNCTION(__asan_unregister_globals) @@ -296,6 +302,7 @@ INTERFACE_FUNCTION(__asan_stack_free_10) // FIXME: we might want to have a sanitizer_win_dll_thunk? INTERFACE_FUNCTION(__sanitizer_annotate_contiguous_container) +INTERFACE_FUNCTION(__sanitizer_contiguous_container_find_bad_address) INTERFACE_FUNCTION(__sanitizer_cov) INTERFACE_FUNCTION(__sanitizer_cov_dump) INTERFACE_FUNCTION(__sanitizer_cov_indir_call16) @@ -304,14 +311,17 @@ INTERFACE_FUNCTION(__sanitizer_cov_module_init) INTERFACE_FUNCTION(__sanitizer_cov_trace_basic_block) INTERFACE_FUNCTION(__sanitizer_cov_trace_func_enter) INTERFACE_FUNCTION(__sanitizer_cov_trace_cmp) +INTERFACE_FUNCTION(__sanitizer_cov_trace_switch) INTERFACE_FUNCTION(__sanitizer_cov_with_check) INTERFACE_FUNCTION(__sanitizer_get_allocated_size) INTERFACE_FUNCTION(__sanitizer_get_coverage_guards) +INTERFACE_FUNCTION(__sanitizer_get_coverage_pc_buffer) INTERFACE_FUNCTION(__sanitizer_get_current_allocated_bytes) INTERFACE_FUNCTION(__sanitizer_get_estimated_allocated_size) INTERFACE_FUNCTION(__sanitizer_get_free_bytes) INTERFACE_FUNCTION(__sanitizer_get_heap_size) INTERFACE_FUNCTION(__sanitizer_get_ownership) +INTERFACE_FUNCTION(__sanitizer_get_total_unique_caller_callee_pairs) INTERFACE_FUNCTION(__sanitizer_get_total_unique_coverage) INTERFACE_FUNCTION(__sanitizer_get_unmapped_bytes) INTERFACE_FUNCTION(__sanitizer_maybe_open_cov_file) diff --git a/contrib/compiler-rt/lib/asan/asan_win_dynamic_runtime_thunk.cc b/contrib/compiler-rt/lib/asan/asan_win_dynamic_runtime_thunk.cc index d59f9f5768a0..73e5207bb334 100644 --- a/contrib/compiler-rt/lib/asan/asan_win_dynamic_runtime_thunk.cc +++ b/contrib/compiler-rt/lib/asan/asan_win_dynamic_runtime_thunk.cc @@ -24,6 +24,7 @@ // Using #ifdef rather than relying on Makefiles etc. // simplifies the build procedure. #ifdef ASAN_DYNAMIC_RUNTIME_THUNK +#define WIN32_LEAN_AND_MEAN #include <windows.h> // First, declare CRT sections we'll be using in this file @@ -58,6 +59,7 @@ int __asan_option_detect_stack_use_after_return = // using atexit() that calls a small subset of C terminators // where LLVM global_dtors is placed. Fingers crossed, no other C terminators // are there. +extern "C" int __cdecl atexit(void (__cdecl *f)(void)); extern "C" void __cdecl _initterm(void *a, void *b); namespace { diff --git a/contrib/compiler-rt/lib/asan/scripts/asan_device_setup b/contrib/compiler-rt/lib/asan/scripts/asan_device_setup deleted file mode 100755 index 104e07b722ca..000000000000 --- a/contrib/compiler-rt/lib/asan/scripts/asan_device_setup +++ /dev/null @@ -1,344 +0,0 @@ -#!/bin/bash -#===- lib/asan/scripts/asan_device_setup -----------------------------------===# -# -# The LLVM Compiler Infrastructure -# -# This file is distributed under the University of Illinois Open Source -# License. See LICENSE.TXT for details. -# -# Prepare Android device to run ASan applications. -# -#===------------------------------------------------------------------------===# - -set -e - -HERE="$(cd "$(dirname "$0")" && pwd)" - -revert=no -extra_options= -device= -lib= -use_su=0 - -function usage { - echo "usage: $0 [--revert] [--device device-id] [--lib path] [--extra-options options]" - echo " --revert: Uninstall ASan from the device." - echo " --lib: Path to ASan runtime library." - echo " --extra-options: Extra ASAN_OPTIONS." - echo " --device: Install to the given device. Use 'adb devices' to find" - echo " device-id." - echo " --use-su: Use 'su -c' prefix for every adb command instead of using" - echo " 'adb root' once." - echo - exit 1 -} - -function adb_push { - if [ $use_su -eq 0 ]; then - $ADB push "$1" "$2" - else - local FILENAME=$(basename $1) - $ADB push "$1" "/data/local/tmp/$FILENAME" - $ADB shell su -c "rm \\\"$2/$FILENAME\\\"" >&/dev/null - $ADB shell su -c "cat \\\"/data/local/tmp/$FILENAME\\\" > \\\"$2/$FILENAME\\\"" - $ADB shell su -c "rm \\\"/data/local/tmp/$FILENAME\\\"" - fi -} - -function adb_remount { - if [ $use_su -eq 0 ]; then - $ADB remount - else - local STORAGE=`$ADB shell mount | grep /system | cut -d ' ' -f1` - if [ "$STORAGE" != "" ]; then - echo Remounting $STORAGE at /system - $ADB shell su -c "mount -o remount,rw $STORAGE /system" - else - echo Failed to get storage device name for "/system" mount point - fi - fi -} - -function adb_shell { - if [ $use_su -eq 0 ]; then - $ADB shell $@ - else - $ADB shell su -c "$*" - fi -} - -function adb_root { - if [ $use_su -eq 0 ]; then - $ADB root - fi -} - -function adb_wait_for_device { - $ADB wait-for-device -} - -function adb_pull { - if [ $use_su -eq 0 ]; then - $ADB pull "$1" "$2" - else - local FILENAME=$(basename $1) - $ADB shell rm "/data/local/tmp/$FILENAME" >&/dev/null - $ADB shell su -c "[ -f \\\"$1\\\" ] && cat \\\"$1\\\" > \\\"/data/local/tmp/$FILENAME\\\" && chown root.shell \\\"/data/local/tmp/$FILENAME\\\" && chmod 755 \\\"/data/local/tmp/$FILENAME\\\"" && - $ADB pull "/data/local/tmp/$FILENAME" "$2" >&/dev/null && $ADB shell "rm \"/data/local/tmp/$FILENAME\"" - fi -} - -function get_device_arch { # OUTVAR - local _outvar=$1 - local _ABI=$(adb_shell getprop ro.product.cpu.abi) - local _ARCH= - if [[ $_ABI == x86* ]]; then - _ARCH=i686 - elif [[ $_ABI == armeabi* ]]; then - _ARCH=arm - else - echo "Unrecognized device ABI: $_ABI" - exit 1 - fi - eval $_outvar=\$_ARCH -} - -while [[ $# > 0 ]]; do - case $1 in - --revert) - revert=yes - ;; - --extra-options) - shift - if [[ $# == 0 ]]; then - echo "--extra-options requires an argument." - exit 1 - fi - extra_options="$1" - ;; - --lib) - shift - if [[ $# == 0 ]]; then - echo "--lib requires an argument." - exit 1 - fi - lib="$1" - ;; - --device) - shift - if [[ $# == 0 ]]; then - echo "--device requires an argument." - exit 1 - fi - device="$1" - ;; - --use-su) - use_su=1 - ;; - *) - usage - ;; - esac - shift -done - -ADB=${ADB:-adb} -if [[ x$device != x ]]; then - ADB="$ADB -s $device" -fi - -if [ $use_su -eq 1 ]; then - # Test if 'su' is present on the device - SU_TEST_OUT=`$ADB shell su -c "echo foo" 2>&1 | sed 's/\r$//'` - if [ $? != 0 -o "$SU_TEST_OUT" != "foo" ]; then - echo "ERROR: Cannot use 'su -c':" - echo "$ adb shell su -c \"echo foo\"" - echo $SU_TEST_OUT - echo "Check that 'su' binary is correctly installed on the device or omit" - echo " --use-su flag" - exit 1 - fi -fi - -echo '>> Remounting /system rw' -adb_wait_for_device -adb_root -adb_wait_for_device -adb_remount -adb_wait_for_device - -get_device_arch ARCH -echo "Target architecture: $ARCH" -ASAN_RT="libclang_rt.asan-$ARCH-android.so" - -if [[ x$revert == xyes ]]; then - echo '>> Uninstalling ASan' - - if ! adb_shell ls -l /system/bin/app_process | grep -o '\->.*app_process' >&/dev/null; then - echo '>> Pre-L device detected.' - adb_shell mv /system/bin/app_process.real /system/bin/app_process - adb_shell rm /system/bin/asanwrapper - else - adb_shell rm /system/bin/app_process.wrap - adb_shell rm /system/bin/asanwrapper - adb_shell rm /system/bin/app_process - adb_shell ln -s /system/bin/app_process32 /system/bin/app_process - fi - - echo '>> Restarting shell' - adb_shell stop - adb_shell start - - # Remove the library on the last step to give a chance to the 'su' binary to - # be executed without problem. - adb_shell rm /system/lib/$ASAN_RT - - echo '>> Done' - exit 0 -fi - -if [[ -d "$lib" ]]; then - ASAN_RT_PATH="$lib" -elif [[ -f "$lib" && "$lib" == *"$ASAN_RT" ]]; then - ASAN_RT_PATH=$(dirname "$lib") -elif [[ -f "$HERE/$ASAN_RT" ]]; then - ASAN_RT_PATH="$HERE" -elif [[ $(basename "$HERE") == "bin" ]]; then - # We could be in the toolchain's base directory. - # Consider ../lib, ../lib/asan, ../lib/linux and ../lib/clang/$VERSION/lib/linux. - P=$(ls "$HERE"/../lib/"$ASAN_RT" "$HERE"/../lib/asan/"$ASAN_RT" "$HERE"/../lib/linux/"$ASAN_RT" "$HERE"/../lib/clang/*/lib/linux/"$ASAN_RT" 2>/dev/null | sort | tail -1) - if [[ -n "$P" ]]; then - ASAN_RT_PATH="$(dirname "$P")" - fi -fi - -if [[ -z "$ASAN_RT_PATH" || ! -f "$ASAN_RT_PATH/$ASAN_RT" ]]; then - echo ">> ASan runtime library not found" - exit 1 -fi - -TMPDIRBASE=$(mktemp -d) -TMPDIROLD="$TMPDIRBASE/old" -TMPDIR="$TMPDIRBASE/new" -mkdir "$TMPDIROLD" - -RELEASE=$(adb_shell getprop ro.build.version.release) -PRE_L=0 -if echo "$RELEASE" | grep '^4\.' >&/dev/null; then - PRE_L=1 -fi - -if ! adb_shell ls -l /system/bin/app_process | grep -o '\->.*app_process' >&/dev/null; then - - if adb_pull /system/bin/app_process.real /dev/null >&/dev/null; then - echo '>> Old-style ASan installation detected. Reverting.' - adb_shell mv /system/bin/app_process.real /system/bin/app_process - fi - - echo '>> Pre-L device detected. Setting up app_process symlink.' - adb_shell mv /system/bin/app_process /system/bin/app_process32 - adb_shell ln -s /system/bin/app_process32 /system/bin/app_process -fi - -echo '>> Copying files from the device' -adb_pull /system/bin/app_process.wrap "$TMPDIROLD" || true -adb_pull /system/bin/asanwrapper "$TMPDIROLD" || true -adb_pull /system/lib/"$ASAN_RT" "$TMPDIROLD" || true -cp -r "$TMPDIROLD" "$TMPDIR" - -if [[ -f "$TMPDIR/app_process.wrap" ]]; then - echo ">> Previous installation detected" -else - echo ">> New installation" -fi - -echo '>> Generating wrappers' - -cp "$ASAN_RT_PATH/$ASAN_RT" "$TMPDIR/" - -# FIXME: alloc_dealloc_mismatch=0 prevents a failure in libdvm startup, -# which may or may not be a real bug (probably not). -ASAN_OPTIONS=start_deactivated=1,alloc_dealloc_mismatch=0 - -# On Android-L not allowing user segv handler breaks some applications. -if [[ PRE_L -eq 0 ]]; then - ASAN_OPTIONS="$ASAN_OPTIONS,allow_user_segv_handler=1" -fi - -if [[ x$extra_options != x ]] ; then - ASAN_OPTIONS="$ASAN_OPTIONS,$extra_options" -fi - -# Zygote wrapper. -cat <<EOF >"$TMPDIR/app_process.wrap" -#!/system/bin/sh-from-zygote -ASAN_OPTIONS=$ASAN_OPTIONS \\ -LD_PRELOAD=\$LD_PRELOAD:$ASAN_RT \\ -exec /system/bin/app_process32 \$@ - -EOF - -# General command-line tool wrapper (use for anything that's not started as -# zygote). -cat <<EOF >"$TMPDIR/asanwrapper" -#!/system/bin/sh -LD_PRELOAD=$ASAN_RT \\ -exec \$@ - -EOF - -if ! ( cd "$TMPDIRBASE" && diff -qr old/ new/ ) ; then - echo '>> Pushing files to the device' - adb_push "$TMPDIR/$ASAN_RT" /system/lib/ - adb_push "$TMPDIR/app_process.wrap" /system/bin - adb_push "$TMPDIR/asanwrapper" /system/bin - - adb_shell rm /system/bin/app_process - adb_shell ln -s /system/bin/app_process.wrap /system/bin/app_process - - adb_shell chown root.shell \ - /system/lib/"$ASAN_RT" \ - /system/bin/app_process.wrap \ - /system/bin/asanwrapper - adb_shell chmod 644 \ - /system/lib/"$ASAN_RT" - adb_shell chmod 755 \ - /system/bin/app_process.wrap \ - /system/bin/asanwrapper - - # Make SELinux happy by keeping app_process wrapper and the shell - # it runs on in zygote domain. - ENFORCING=0 - if adb_shell getenforce | grep Enforcing >/dev/null; then - # Sometimes shell is not allowed to change file contexts. - # Temporarily switch to permissive. - ENFORCING=1 - adb_shell setenforce 0 - fi - - adb_shell cp /system/bin/sh /system/bin/sh-from-zygote - - if [[ PRE_L -eq 1 ]]; then - CTX=u:object_r:system_file:s0 - else - CTX=u:object_r:zygote_exec:s0 - fi - adb_shell chcon $CTX \ - /system/bin/sh-from-zygote \ - /system/bin/app_process.wrap \ - /system/bin/app_process32 - - if [ $ENFORCING == 1 ]; then - adb_shell setenforce 1 - fi - - echo '>> Restarting shell (asynchronous)' - adb_shell stop - adb_shell start - - echo '>> Please wait until the device restarts' -else - echo '>> Device is up to date' -fi - -rm -r "$TMPDIRBASE" diff --git a/contrib/compiler-rt/lib/asan/scripts/asan_symbolize.py b/contrib/compiler-rt/lib/asan/scripts/asan_symbolize.py deleted file mode 100755 index b9d3ad3ad2fe..000000000000 --- a/contrib/compiler-rt/lib/asan/scripts/asan_symbolize.py +++ /dev/null @@ -1,482 +0,0 @@ -#!/usr/bin/env python -#===- lib/asan/scripts/asan_symbolize.py -----------------------------------===# -# -# The LLVM Compiler Infrastructure -# -# This file is distributed under the University of Illinois Open Source -# License. See LICENSE.TXT for details. -# -#===------------------------------------------------------------------------===# -import argparse -import bisect -import getopt -import os -import re -import subprocess -import sys - -symbolizers = {} -DEBUG = False -demangle = False -binutils_prefix = None -sysroot_path = None -binary_name_filter = None -fix_filename_patterns = None -logfile = sys.stdin -allow_system_symbolizer = True - -# FIXME: merge the code that calls fix_filename(). -def fix_filename(file_name): - if fix_filename_patterns: - for path_to_cut in fix_filename_patterns: - file_name = re.sub('.*' + path_to_cut, '', file_name) - file_name = re.sub('.*asan_[a-z_]*.cc:[0-9]*', '_asan_rtl_', file_name) - file_name = re.sub('.*crtstuff.c:0', '???:0', file_name) - return file_name - -def sysroot_path_filter(binary_name): - return sysroot_path + binary_name - -def guess_arch(addr): - # Guess which arch we're running. 10 = len('0x') + 8 hex digits. - if len(addr) > 10: - return 'x86_64' - else: - return 'i386' - -class Symbolizer(object): - def __init__(self): - pass - - def symbolize(self, addr, binary, offset): - """Symbolize the given address (pair of binary and offset). - - Overriden in subclasses. - Args: - addr: virtual address of an instruction. - binary: path to executable/shared object containing this instruction. - offset: instruction offset in the @binary. - Returns: - list of strings (one string for each inlined frame) describing - the code locations for this instruction (that is, function name, file - name, line and column numbers). - """ - return None - - -class LLVMSymbolizer(Symbolizer): - def __init__(self, symbolizer_path, default_arch, system, dsym_hints=[]): - super(LLVMSymbolizer, self).__init__() - self.symbolizer_path = symbolizer_path - self.default_arch = default_arch - self.system = system - self.dsym_hints = dsym_hints - self.pipe = self.open_llvm_symbolizer() - - def open_llvm_symbolizer(self): - cmd = [self.symbolizer_path, - '--use-symbol-table=true', - '--demangle=%s' % demangle, - '--functions=short', - '--inlining=true', - '--default-arch=%s' % self.default_arch] - if self.system == 'Darwin': - for hint in self.dsym_hints: - cmd.append('--dsym-hint=%s' % hint) - if DEBUG: - print ' '.join(cmd) - try: - result = subprocess.Popen(cmd, stdin=subprocess.PIPE, - stdout=subprocess.PIPE) - except OSError: - result = None - return result - - def symbolize(self, addr, binary, offset): - """Overrides Symbolizer.symbolize.""" - if not self.pipe: - return None - result = [] - try: - symbolizer_input = '"%s" %s' % (binary, offset) - if DEBUG: - print symbolizer_input - print >> self.pipe.stdin, symbolizer_input - while True: - function_name = self.pipe.stdout.readline().rstrip() - if not function_name: - break - file_name = self.pipe.stdout.readline().rstrip() - file_name = fix_filename(file_name) - if (not function_name.startswith('??') or - not file_name.startswith('??')): - # Append only non-trivial frames. - result.append('%s in %s %s' % (addr, function_name, - file_name)) - except Exception: - result = [] - if not result: - result = None - return result - - -def LLVMSymbolizerFactory(system, default_arch, dsym_hints=[]): - symbolizer_path = os.getenv('LLVM_SYMBOLIZER_PATH') - if not symbolizer_path: - symbolizer_path = os.getenv('ASAN_SYMBOLIZER_PATH') - if not symbolizer_path: - # Assume llvm-symbolizer is in PATH. - symbolizer_path = 'llvm-symbolizer' - return LLVMSymbolizer(symbolizer_path, default_arch, system, dsym_hints) - - -class Addr2LineSymbolizer(Symbolizer): - def __init__(self, binary): - super(Addr2LineSymbolizer, self).__init__() - self.binary = binary - self.pipe = self.open_addr2line() - - def open_addr2line(self): - addr2line_tool = 'addr2line' - if binutils_prefix: - addr2line_tool = binutils_prefix + addr2line_tool - cmd = [addr2line_tool, '-f'] - if demangle: - cmd += ['--demangle'] - cmd += ['-e', self.binary] - if DEBUG: - print ' '.join(cmd) - return subprocess.Popen(cmd, - stdin=subprocess.PIPE, stdout=subprocess.PIPE) - - def symbolize(self, addr, binary, offset): - """Overrides Symbolizer.symbolize.""" - if self.binary != binary: - return None - try: - print >> self.pipe.stdin, offset - function_name = self.pipe.stdout.readline().rstrip() - file_name = self.pipe.stdout.readline().rstrip() - except Exception: - function_name = '' - file_name = '' - file_name = fix_filename(file_name) - return ['%s in %s %s' % (addr, function_name, file_name)] - - -class UnbufferedLineConverter(object): - """ - Wrap a child process that responds to each line of input with one line of - output. Uses pty to trick the child into providing unbuffered output. - """ - def __init__(self, args, close_stderr=False): - # Local imports so that the script can start on Windows. - import pty - import termios - pid, fd = pty.fork() - if pid == 0: - # We're the child. Transfer control to command. - if close_stderr: - dev_null = os.open('/dev/null', 0) - os.dup2(dev_null, 2) - os.execvp(args[0], args) - else: - # Disable echoing. - attr = termios.tcgetattr(fd) - attr[3] = attr[3] & ~termios.ECHO - termios.tcsetattr(fd, termios.TCSANOW, attr) - # Set up a file()-like interface to the child process - self.r = os.fdopen(fd, "r", 1) - self.w = os.fdopen(os.dup(fd), "w", 1) - - def convert(self, line): - self.w.write(line + "\n") - return self.readline() - - def readline(self): - return self.r.readline().rstrip() - - -class DarwinSymbolizer(Symbolizer): - def __init__(self, addr, binary): - super(DarwinSymbolizer, self).__init__() - self.binary = binary - self.arch = guess_arch(addr) - self.open_atos() - - def open_atos(self): - if DEBUG: - print 'atos -o %s -arch %s' % (self.binary, self.arch) - cmdline = ['atos', '-o', self.binary, '-arch', self.arch] - self.atos = UnbufferedLineConverter(cmdline, close_stderr=True) - - def symbolize(self, addr, binary, offset): - """Overrides Symbolizer.symbolize.""" - if self.binary != binary: - return None - atos_line = self.atos.convert('0x%x' % int(offset, 16)) - while "got symbolicator for" in atos_line: - atos_line = self.atos.readline() - # A well-formed atos response looks like this: - # foo(type1, type2) (in object.name) (filename.cc:80) - match = re.match('^(.*) \(in (.*)\) \((.*:\d*)\)$', atos_line) - if DEBUG: - print 'atos_line: ', atos_line - if match: - function_name = match.group(1) - function_name = re.sub('\(.*?\)', '', function_name) - file_name = fix_filename(match.group(3)) - return ['%s in %s %s' % (addr, function_name, file_name)] - else: - return ['%s in %s' % (addr, atos_line)] - - -# Chain several symbolizers so that if one symbolizer fails, we fall back -# to the next symbolizer in chain. -class ChainSymbolizer(Symbolizer): - def __init__(self, symbolizer_list): - super(ChainSymbolizer, self).__init__() - self.symbolizer_list = symbolizer_list - - def symbolize(self, addr, binary, offset): - """Overrides Symbolizer.symbolize.""" - for symbolizer in self.symbolizer_list: - if symbolizer: - result = symbolizer.symbolize(addr, binary, offset) - if result: - return result - return None - - def append_symbolizer(self, symbolizer): - self.symbolizer_list.append(symbolizer) - - -def BreakpadSymbolizerFactory(binary): - suffix = os.getenv('BREAKPAD_SUFFIX') - if suffix: - filename = binary + suffix - if os.access(filename, os.F_OK): - return BreakpadSymbolizer(filename) - return None - - -def SystemSymbolizerFactory(system, addr, binary): - if system == 'Darwin': - return DarwinSymbolizer(addr, binary) - elif system == 'Linux': - return Addr2LineSymbolizer(binary) - - -class BreakpadSymbolizer(Symbolizer): - def __init__(self, filename): - super(BreakpadSymbolizer, self).__init__() - self.filename = filename - lines = file(filename).readlines() - self.files = [] - self.symbols = {} - self.address_list = [] - self.addresses = {} - # MODULE mac x86_64 A7001116478B33F18FF9BEDE9F615F190 t - fragments = lines[0].rstrip().split() - self.arch = fragments[2] - self.debug_id = fragments[3] - self.binary = ' '.join(fragments[4:]) - self.parse_lines(lines[1:]) - - def parse_lines(self, lines): - cur_function_addr = '' - for line in lines: - fragments = line.split() - if fragments[0] == 'FILE': - assert int(fragments[1]) == len(self.files) - self.files.append(' '.join(fragments[2:])) - elif fragments[0] == 'PUBLIC': - self.symbols[int(fragments[1], 16)] = ' '.join(fragments[3:]) - elif fragments[0] in ['CFI', 'STACK']: - pass - elif fragments[0] == 'FUNC': - cur_function_addr = int(fragments[1], 16) - if not cur_function_addr in self.symbols.keys(): - self.symbols[cur_function_addr] = ' '.join(fragments[4:]) - else: - # Line starting with an address. - addr = int(fragments[0], 16) - self.address_list.append(addr) - # Tuple of symbol address, size, line, file number. - self.addresses[addr] = (cur_function_addr, - int(fragments[1], 16), - int(fragments[2]), - int(fragments[3])) - self.address_list.sort() - - def get_sym_file_line(self, addr): - key = None - if addr in self.addresses.keys(): - key = addr - else: - index = bisect.bisect_left(self.address_list, addr) - if index == 0: - return None - else: - key = self.address_list[index - 1] - sym_id, size, line_no, file_no = self.addresses[key] - symbol = self.symbols[sym_id] - filename = self.files[file_no] - if addr < key + size: - return symbol, filename, line_no - else: - return None - - def symbolize(self, addr, binary, offset): - if self.binary != binary: - return None - res = self.get_sym_file_line(int(offset, 16)) - if res: - function_name, file_name, line_no = res - result = ['%s in %s %s:%d' % ( - addr, function_name, file_name, line_no)] - print result - return result - else: - return None - - -class SymbolizationLoop(object): - def __init__(self, binary_name_filter=None, dsym_hint_producer=None): - if sys.platform == 'win32': - # ASan on Windows uses dbghelp.dll to symbolize in-process, which works - # even in sandboxed processes. Nothing needs to be done here. - self.process_line = self.process_line_echo - else: - # Used by clients who may want to supply a different binary name. - # E.g. in Chrome several binaries may share a single .dSYM. - self.binary_name_filter = binary_name_filter - self.dsym_hint_producer = dsym_hint_producer - self.system = os.uname()[0] - if self.system not in ['Linux', 'Darwin', 'FreeBSD']: - raise Exception('Unknown system') - self.llvm_symbolizers = {} - self.last_llvm_symbolizer = None - self.dsym_hints = set([]) - self.frame_no = 0 - self.process_line = self.process_line_posix - - def symbolize_address(self, addr, binary, offset): - # On non-Darwin (i.e. on platforms without .dSYM debug info) always use - # a single symbolizer binary. - # On Darwin, if the dsym hint producer is present: - # 1. check whether we've seen this binary already; if so, - # use |llvm_symbolizers[binary]|, which has already loaded the debug - # info for this binary (might not be the case for - # |last_llvm_symbolizer|); - # 2. otherwise check if we've seen all the hints for this binary already; - # if so, reuse |last_llvm_symbolizer| which has the full set of hints; - # 3. otherwise create a new symbolizer and pass all currently known - # .dSYM hints to it. - if not binary in self.llvm_symbolizers: - use_new_symbolizer = True - if self.system == 'Darwin' and self.dsym_hint_producer: - dsym_hints_for_binary = set(self.dsym_hint_producer(binary)) - use_new_symbolizer = bool(dsym_hints_for_binary - self.dsym_hints) - self.dsym_hints |= dsym_hints_for_binary - if self.last_llvm_symbolizer and not use_new_symbolizer: - self.llvm_symbolizers[binary] = self.last_llvm_symbolizer - else: - self.last_llvm_symbolizer = LLVMSymbolizerFactory( - self.system, guess_arch(addr), self.dsym_hints) - self.llvm_symbolizers[binary] = self.last_llvm_symbolizer - # Use the chain of symbolizers: - # Breakpad symbolizer -> LLVM symbolizer -> addr2line/atos - # (fall back to next symbolizer if the previous one fails). - if not binary in symbolizers: - symbolizers[binary] = ChainSymbolizer( - [BreakpadSymbolizerFactory(binary), self.llvm_symbolizers[binary]]) - result = symbolizers[binary].symbolize(addr, binary, offset) - if result is None: - if not allow_system_symbolizer: - raise Exception('Failed to launch or use llvm-symbolizer.') - # Initialize system symbolizer only if other symbolizers failed. - symbolizers[binary].append_symbolizer( - SystemSymbolizerFactory(self.system, addr, binary)) - result = symbolizers[binary].symbolize(addr, binary, offset) - # The system symbolizer must produce some result. - assert result - return result - - def get_symbolized_lines(self, symbolized_lines): - if not symbolized_lines: - return [self.current_line] - else: - result = [] - for symbolized_frame in symbolized_lines: - result.append(' #%s %s' % (str(self.frame_no), symbolized_frame.rstrip())) - self.frame_no += 1 - return result - - def process_logfile(self): - self.frame_no = 0 - for line in logfile: - processed = self.process_line(line) - print '\n'.join(processed) - - def process_line_echo(self, line): - return [line.rstrip()] - - def process_line_posix(self, line): - self.current_line = line.rstrip() - #0 0x7f6e35cf2e45 (/blah/foo.so+0x11fe45) - stack_trace_line_format = ( - '^( *#([0-9]+) *)(0x[0-9a-f]+) *\((.*)\+(0x[0-9a-f]+)\)') - match = re.match(stack_trace_line_format, line) - if not match: - return [self.current_line] - if DEBUG: - print line - _, frameno_str, addr, binary, offset = match.groups() - if frameno_str == '0': - # Assume that frame #0 is the first frame of new stack trace. - self.frame_no = 0 - original_binary = binary - if self.binary_name_filter: - binary = self.binary_name_filter(binary) - symbolized_line = self.symbolize_address(addr, binary, offset) - if not symbolized_line: - if original_binary != binary: - symbolized_line = self.symbolize_address(addr, binary, offset) - return self.get_symbolized_lines(symbolized_line) - - -if __name__ == '__main__': - parser = argparse.ArgumentParser( - formatter_class=argparse.RawDescriptionHelpFormatter, - description='ASan symbolization script', - epilog='Example of use:\n' - 'asan_symbolize.py -c "$HOME/opt/cross/bin/arm-linux-gnueabi-" ' - '-s "$HOME/SymbolFiles" < asan.log') - parser.add_argument('path_to_cut', nargs='*', - help='pattern to be cut from the result file path ') - parser.add_argument('-d','--demangle', action='store_true', - help='demangle function names') - parser.add_argument('-s', metavar='SYSROOT', - help='set path to sysroot for sanitized binaries') - parser.add_argument('-c', metavar='CROSS_COMPILE', - help='set prefix for binutils') - parser.add_argument('-l','--logfile', default=sys.stdin, - type=argparse.FileType('r'), - help='set log file name to parse, default is stdin') - args = parser.parse_args() - if args.path_to_cut: - fix_filename_patterns = args.path_to_cut - if args.demangle: - demangle = True - if args.s: - binary_name_filter = sysroot_path_filter - sysroot_path = args.s - if args.c: - binutils_prefix = args.c - if args.logfile: - logfile = args.logfile - else: - logfile = sys.stdin - loop = SymbolizationLoop(binary_name_filter) - loop.process_logfile() diff --git a/contrib/compiler-rt/lib/asan/tests/asan_asm_test.cc b/contrib/compiler-rt/lib/asan/tests/asan_asm_test.cc deleted file mode 100644 index 200de2c137a5..000000000000 --- a/contrib/compiler-rt/lib/asan/tests/asan_asm_test.cc +++ /dev/null @@ -1,267 +0,0 @@ -//===-- asan_asm_test.cc --------------------------------------------------===// -// -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. -// -//===----------------------------------------------------------------------===// -// -// This file is a part of AddressSanitizer, an address sanity checker. -// -//===----------------------------------------------------------------------===// -#include "asan_test_utils.h" - -#if defined(__linux__) - -#if defined(__x86_64__) || (defined(__i386__) && defined(__SSE2__)) - -#include <emmintrin.h> - -namespace { - -template<typename T> void asm_write(T *ptr, T val); -template<typename T> T asm_read(T *ptr); -template<typename T> void asm_rep_movs(T *dst, T *src, size_t n); - -} // End of anonymous namespace - -#endif // defined(__x86_64__) || (defined(__i386__) && defined(__SSE2__)) - -#if defined(__x86_64__) - -namespace { - -#define DECLARE_ASM_WRITE(Type, Size, Mov, Reg) \ -template<> void asm_write<Type>(Type *ptr, Type val) { \ - __asm__( \ - Mov " %[val], (%[ptr]) \n\t" \ - : \ - : [ptr] "r" (ptr), [val] Reg (val) \ - : "memory" \ - ); \ -} - -#define DECLARE_ASM_READ(Type, Size, Mov, Reg) \ -template<> Type asm_read<Type>(Type *ptr) { \ - Type res; \ - __asm__( \ - Mov " (%[ptr]), %[res] \n\t" \ - : [res] Reg (res) \ - : [ptr] "r" (ptr) \ - : "memory" \ - ); \ - return res; \ -} - -#define DECLARE_ASM_REP_MOVS(Type, Movs) \ - template <> void asm_rep_movs<Type>(Type * dst, Type * src, size_t size) { \ - __asm__("rep " Movs " \n\t" \ - : \ - : "D"(dst), "S"(src), "c"(size) \ - : "rsi", "rdi", "rcx", "memory"); \ - } - -DECLARE_ASM_WRITE(U8, "8", "movq", "r"); -DECLARE_ASM_READ(U8, "8", "movq", "=r"); -DECLARE_ASM_REP_MOVS(U8, "movsq"); - -} // End of anonymous namespace - -#endif // defined(__x86_64__) - -#if defined(__i386__) && defined(__SSE2__) - -namespace { - -#define DECLARE_ASM_WRITE(Type, Size, Mov, Reg) \ -template<> void asm_write<Type>(Type *ptr, Type val) { \ - __asm__( \ - Mov " %[val], (%[ptr]) \n\t" \ - : \ - : [ptr] "r" (ptr), [val] Reg (val) \ - : "memory" \ - ); \ -} - -#define DECLARE_ASM_READ(Type, Size, Mov, Reg) \ -template<> Type asm_read<Type>(Type *ptr) { \ - Type res; \ - __asm__( \ - Mov " (%[ptr]), %[res] \n\t" \ - : [res] Reg (res) \ - : [ptr] "r" (ptr) \ - : "memory" \ - ); \ - return res; \ -} - -#define DECLARE_ASM_REP_MOVS(Type, Movs) \ - template <> void asm_rep_movs<Type>(Type * dst, Type * src, size_t size) { \ - __asm__("rep " Movs " \n\t" \ - : \ - : "D"(dst), "S"(src), "c"(size) \ - : "esi", "edi", "ecx", "memory"); \ - } - -} // End of anonymous namespace - -#endif // defined(__i386__) && defined(__SSE2__) - -#if defined(__x86_64__) || (defined(__i386__) && defined(__SSE2__)) - -namespace { - -DECLARE_ASM_WRITE(U1, "1", "movb", "r"); -DECLARE_ASM_WRITE(U2, "2", "movw", "r"); -DECLARE_ASM_WRITE(U4, "4", "movl", "r"); -DECLARE_ASM_WRITE(__m128i, "16", "movaps", "x"); - -DECLARE_ASM_READ(U1, "1", "movb", "=r"); -DECLARE_ASM_READ(U2, "2", "movw", "=r"); -DECLARE_ASM_READ(U4, "4", "movl", "=r"); -DECLARE_ASM_READ(__m128i, "16", "movaps", "=x"); - -DECLARE_ASM_REP_MOVS(U1, "movsb"); -DECLARE_ASM_REP_MOVS(U2, "movsw"); -DECLARE_ASM_REP_MOVS(U4, "movsl"); - -template<typename T> void TestAsmWrite(const char *DeathPattern) { - T *buf = new T; - EXPECT_DEATH(asm_write(&buf[1], static_cast<T>(0)), DeathPattern); - T var = 0x12; - asm_write(&var, static_cast<T>(0x21)); - ASSERT_EQ(static_cast<T>(0x21), var); - delete buf; -} - -template<> void TestAsmWrite<__m128i>(const char *DeathPattern) { - char *buf = new char[16]; - char *p = buf + 16; - if (((uintptr_t) p % 16) != 0) - p = buf + 8; - assert(((uintptr_t) p % 16) == 0); - __m128i val = _mm_set1_epi16(0x1234); - EXPECT_DEATH(asm_write<__m128i>((__m128i*) p, val), DeathPattern); - __m128i var = _mm_set1_epi16(0x4321); - asm_write(&var, val); - ASSERT_EQ(0x1234, _mm_extract_epi16(var, 0)); - delete [] buf; -} - -template<typename T> void TestAsmRead(const char *DeathPattern) { - T *buf = new T; - EXPECT_DEATH(asm_read(&buf[1]), DeathPattern); - T var = 0x12; - ASSERT_EQ(static_cast<T>(0x12), asm_read(&var)); - delete buf; -} - -template<> void TestAsmRead<__m128i>(const char *DeathPattern) { - char *buf = new char[16]; - char *p = buf + 16; - if (((uintptr_t) p % 16) != 0) - p = buf + 8; - assert(((uintptr_t) p % 16) == 0); - EXPECT_DEATH(asm_read<__m128i>((__m128i*) p), DeathPattern); - __m128i val = _mm_set1_epi16(0x1234); - ASSERT_EQ(0x1234, _mm_extract_epi16(asm_read(&val), 0)); - delete [] buf; -} - -U4 AsmLoad(U4 *a) { - U4 r; - __asm__("movl (%[a]), %[r] \n\t" : [r] "=r" (r) : [a] "r" (a) : "memory"); - return r; -} - -void AsmStore(U4 r, U4 *a) { - __asm__("movl %[r], (%[a]) \n\t" : : [a] "r" (a), [r] "r" (r) : "memory"); -} - -template <typename T> -void TestAsmRepMovs(const char *DeathPatternRead, - const char *DeathPatternWrite) { - T src_good[4] = { 0x0, 0x1, 0x2, 0x3 }; - T dst_good[4] = {}; - asm_rep_movs(dst_good, src_good, 4); - ASSERT_EQ(static_cast<T>(0x0), dst_good[0]); - ASSERT_EQ(static_cast<T>(0x1), dst_good[1]); - ASSERT_EQ(static_cast<T>(0x2), dst_good[2]); - ASSERT_EQ(static_cast<T>(0x3), dst_good[3]); - - T dst_bad[3]; - EXPECT_DEATH(asm_rep_movs(dst_bad, src_good, 4), DeathPatternWrite); - - T src_bad[3] = { 0x0, 0x1, 0x2 }; - EXPECT_DEATH(asm_rep_movs(dst_good, src_bad, 4), DeathPatternRead); - - T* dp = dst_bad + 4; - T* sp = src_bad + 4; - asm_rep_movs(dp, sp, 0); -} - -} // End of anonymous namespace - -TEST(AddressSanitizer, asm_load_store) { - U4* buf = new U4[2]; - EXPECT_DEATH(AsmLoad(&buf[3]), "READ of size 4"); - EXPECT_DEATH(AsmStore(0x1234, &buf[3]), "WRITE of size 4"); - delete [] buf; -} - -TEST(AddressSanitizer, asm_rw) { - TestAsmWrite<U1>("WRITE of size 1"); - TestAsmWrite<U2>("WRITE of size 2"); - TestAsmWrite<U4>("WRITE of size 4"); -#if defined(__x86_64__) - TestAsmWrite<U8>("WRITE of size 8"); -#endif // defined(__x86_64__) - TestAsmWrite<__m128i>("WRITE of size 16"); - - TestAsmRead<U1>("READ of size 1"); - TestAsmRead<U2>("READ of size 2"); - TestAsmRead<U4>("READ of size 4"); -#if defined(__x86_64__) - TestAsmRead<U8>("READ of size 8"); -#endif // defined(__x86_64__) - TestAsmRead<__m128i>("READ of size 16"); -} - -TEST(AddressSanitizer, asm_flags) { - long magic = 0x1234; - long r = 0x0; - -#if defined(__x86_64__) && !defined(__ILP32__) - __asm__("xorq %%rax, %%rax \n\t" - "movq (%[p]), %%rax \n\t" - "sete %%al \n\t" - "movzbq %%al, %[r] \n\t" - : [r] "=r"(r) - : [p] "r"(&magic) - : "rax", "memory"); -#else - __asm__("xorl %%eax, %%eax \n\t" - "movl (%[p]), %%eax \n\t" - "sete %%al \n\t" - "movzbl %%al, %[r] \n\t" - : [r] "=r"(r) - : [p] "r"(&magic) - : "eax", "memory"); -#endif // defined(__x86_64__) && !defined(__ILP32__) - - ASSERT_EQ(0x1, r); -} - -TEST(AddressSanitizer, asm_rep_movs) { - TestAsmRepMovs<U1>("READ of size 1", "WRITE of size 1"); - TestAsmRepMovs<U2>("READ of size 2", "WRITE of size 2"); - TestAsmRepMovs<U4>("READ of size 4", "WRITE of size 4"); -#if defined(__x86_64__) - TestAsmRepMovs<U8>("READ of size 8", "WRITE of size 8"); -#endif // defined(__x86_64__) -} - -#endif // defined(__x86_64__) || (defined(__i386__) && defined(__SSE2__)) - -#endif // defined(__linux__) diff --git a/contrib/compiler-rt/lib/asan/tests/asan_benchmarks_test.cc b/contrib/compiler-rt/lib/asan/tests/asan_benchmarks_test.cc deleted file mode 100644 index fc522de475fa..000000000000 --- a/contrib/compiler-rt/lib/asan/tests/asan_benchmarks_test.cc +++ /dev/null @@ -1,85 +0,0 @@ -//===-- asan_benchmarks_test.cc ----------------------===// -// -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. -// -//===----------------------------------------------------------------------===// -// -// This file is a part of AddressSanitizer, an address sanity checker. -// -// Some benchmarks for the instrumented code. -//===----------------------------------------------------------------------===// - -#include "asan_test_utils.h" - -template<class T> -__attribute__((noinline)) -static void ManyAccessFunc(T *x, size_t n_elements, size_t n_iter) { - for (size_t iter = 0; iter < n_iter; iter++) { - break_optimization(0); - // hand unroll the loop to stress the reg alloc. - for (size_t i = 0; i <= n_elements - 16; i += 16) { - x[i + 0] = i; - x[i + 1] = i; - x[i + 2] = i; - x[i + 3] = i; - x[i + 4] = i; - x[i + 5] = i; - x[i + 6] = i; - x[i + 7] = i; - x[i + 8] = i; - x[i + 9] = i; - x[i + 10] = i; - x[i + 11] = i; - x[i + 12] = i; - x[i + 13] = i; - x[i + 14] = i; - x[i + 15] = i; - } - } -} - -TEST(AddressSanitizer, ManyAccessBenchmark) { - size_t kLen = 1024; - int *int_array = new int[kLen]; - ManyAccessFunc(int_array, kLen, 1 << 24); - delete [] int_array; -} - -// access 7 char elements in a 7 byte array (i.e. on the border). -__attribute__((noinline)) -static void BorderAccessFunc(char *x, size_t n_iter) { - for (size_t iter = 0; iter < n_iter; iter++) { - break_optimization(x); - x[0] = 0; - x[1] = 0; - x[2] = 0; - x[3] = 0; - x[4] = 0; - x[5] = 0; - x[6] = 0; - } -} - -TEST(AddressSanitizer, BorderAccessBenchmark) { - char *char_7_array = new char[7]; - BorderAccessFunc(char_7_array, 1 << 30); - delete [] char_7_array; -} - -static void FunctionWithLargeStack() { - int stack[1000]; - Ident(stack); -} - -TEST(AddressSanitizer, FakeStackBenchmark) { - for (int i = 0; i < 10000000; i++) - Ident(&FunctionWithLargeStack)(); -} - -int main(int argc, char **argv) { - testing::InitGoogleTest(&argc, argv); - return RUN_ALL_TESTS(); -} diff --git a/contrib/compiler-rt/lib/asan/tests/asan_exceptions_test.cc b/contrib/compiler-rt/lib/asan/tests/asan_exceptions_test.cc deleted file mode 100644 index ecd406de7561..000000000000 --- a/contrib/compiler-rt/lib/asan/tests/asan_exceptions_test.cc +++ /dev/null @@ -1,27 +0,0 @@ -// See http://llvm.org/bugs/show_bug.cgi?id=11468 -#include <stdio.h> -#include <string> - -class Action { - public: - Action() {} - void PrintString(const std::string& msg) const { - fprintf(stderr, "%s\n", msg.c_str()); - } - void Throw(const char& arg) const { - PrintString("PrintString called!"); // this line is important - throw arg; - } -}; - -int main() { - const Action a; - fprintf(stderr, "&a before = %p\n", &a); - try { - a.Throw('c'); - } catch(const char&) { - fprintf(stderr, "&a in catch = %p\n", &a); - } - fprintf(stderr, "&a final = %p\n", &a); - return 0; -} diff --git a/contrib/compiler-rt/lib/asan/tests/asan_fake_stack_test.cc b/contrib/compiler-rt/lib/asan/tests/asan_fake_stack_test.cc deleted file mode 100644 index 516142f0c3b7..000000000000 --- a/contrib/compiler-rt/lib/asan/tests/asan_fake_stack_test.cc +++ /dev/null @@ -1,152 +0,0 @@ -//===-- asan_fake_stack_test.cc -------------------------------------------===// -// -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. -// -//===----------------------------------------------------------------------===// -// -// This file is a part of AddressSanitizer, an address sanity checker. -// -// Tests for FakeStack. -// This test file should be compiled w/o asan instrumentation. -//===----------------------------------------------------------------------===// - -#include "asan_fake_stack.h" -#include "asan_test_utils.h" -#include "sanitizer_common/sanitizer_common.h" - -#include <assert.h> -#include <stdlib.h> -#include <stdio.h> - -#include <map> - -namespace __asan { - -TEST(FakeStack, FlagsSize) { - EXPECT_EQ(FakeStack::SizeRequiredForFlags(10), 1U << 5); - EXPECT_EQ(FakeStack::SizeRequiredForFlags(11), 1U << 6); - EXPECT_EQ(FakeStack::SizeRequiredForFlags(20), 1U << 15); -} - -TEST(FakeStack, RequiredSize) { - // for (int i = 15; i < 20; i++) { - // uptr alloc_size = FakeStack::RequiredSize(i); - // printf("%zdK ==> %zd\n", 1 << (i - 10), alloc_size); - // } - EXPECT_EQ(FakeStack::RequiredSize(15), 365568U); - EXPECT_EQ(FakeStack::RequiredSize(16), 727040U); - EXPECT_EQ(FakeStack::RequiredSize(17), 1449984U); - EXPECT_EQ(FakeStack::RequiredSize(18), 2895872U); - EXPECT_EQ(FakeStack::RequiredSize(19), 5787648U); -} - -TEST(FakeStack, FlagsOffset) { - for (uptr stack_size_log = 15; stack_size_log <= 20; stack_size_log++) { - uptr stack_size = 1UL << stack_size_log; - uptr offset = 0; - for (uptr class_id = 0; class_id < FakeStack::kNumberOfSizeClasses; - class_id++) { - uptr frame_size = FakeStack::BytesInSizeClass(class_id); - uptr num_flags = stack_size / frame_size; - EXPECT_EQ(offset, FakeStack::FlagsOffset(stack_size_log, class_id)); - // printf("%zd: %zd => %zd %zd\n", stack_size_log, class_id, offset, - // FakeStack::FlagsOffset(stack_size_log, class_id)); - offset += num_flags; - } - } -} - -#if !defined(_WIN32) // FIXME: Fails due to OOM on Windows. -TEST(FakeStack, CreateDestroy) { - for (int i = 0; i < 1000; i++) { - for (uptr stack_size_log = 20; stack_size_log <= 22; stack_size_log++) { - FakeStack *fake_stack = FakeStack::Create(stack_size_log); - fake_stack->Destroy(0); - } - } -} -#endif - -TEST(FakeStack, ModuloNumberOfFrames) { - EXPECT_EQ(FakeStack::ModuloNumberOfFrames(15, 0, 0), 0U); - EXPECT_EQ(FakeStack::ModuloNumberOfFrames(15, 0, (1<<15)), 0U); - EXPECT_EQ(FakeStack::ModuloNumberOfFrames(15, 0, (1<<10)), 0U); - EXPECT_EQ(FakeStack::ModuloNumberOfFrames(15, 0, (1<<9)), 0U); - EXPECT_EQ(FakeStack::ModuloNumberOfFrames(15, 0, (1<<8)), 1U<<8); - EXPECT_EQ(FakeStack::ModuloNumberOfFrames(15, 0, (1<<15) + 1), 1U); - - EXPECT_EQ(FakeStack::ModuloNumberOfFrames(15, 1, 0), 0U); - EXPECT_EQ(FakeStack::ModuloNumberOfFrames(15, 1, 1<<9), 0U); - EXPECT_EQ(FakeStack::ModuloNumberOfFrames(15, 1, 1<<8), 0U); - EXPECT_EQ(FakeStack::ModuloNumberOfFrames(15, 1, 1<<7), 1U<<7); - - EXPECT_EQ(FakeStack::ModuloNumberOfFrames(15, 5, 0), 0U); - EXPECT_EQ(FakeStack::ModuloNumberOfFrames(15, 5, 1), 1U); - EXPECT_EQ(FakeStack::ModuloNumberOfFrames(15, 5, 15), 15U); - EXPECT_EQ(FakeStack::ModuloNumberOfFrames(15, 5, 16), 0U); - EXPECT_EQ(FakeStack::ModuloNumberOfFrames(15, 5, 17), 1U); -} - -TEST(FakeStack, GetFrame) { - const uptr stack_size_log = 20; - const uptr stack_size = 1 << stack_size_log; - FakeStack *fs = FakeStack::Create(stack_size_log); - u8 *base = fs->GetFrame(stack_size_log, 0, 0); - EXPECT_EQ(base, reinterpret_cast<u8 *>(fs) + - fs->SizeRequiredForFlags(stack_size_log) + 4096); - EXPECT_EQ(base + 0*stack_size + 64 * 7, fs->GetFrame(stack_size_log, 0, 7U)); - EXPECT_EQ(base + 1*stack_size + 128 * 3, fs->GetFrame(stack_size_log, 1, 3U)); - EXPECT_EQ(base + 2*stack_size + 256 * 5, fs->GetFrame(stack_size_log, 2, 5U)); - fs->Destroy(0); -} - -TEST(FakeStack, Allocate) { - const uptr stack_size_log = 19; - FakeStack *fs = FakeStack::Create(stack_size_log); - std::map<FakeFrame *, uptr> s; - for (int iter = 0; iter < 2; iter++) { - s.clear(); - for (uptr cid = 0; cid < FakeStack::kNumberOfSizeClasses; cid++) { - uptr n = FakeStack::NumberOfFrames(stack_size_log, cid); - uptr bytes_in_class = FakeStack::BytesInSizeClass(cid); - for (uptr j = 0; j < n; j++) { - FakeFrame *ff = fs->Allocate(stack_size_log, cid, 0); - uptr x = reinterpret_cast<uptr>(ff); - EXPECT_TRUE(s.insert(std::make_pair(ff, cid)).second); - EXPECT_EQ(x, fs->AddrIsInFakeStack(x)); - EXPECT_EQ(x, fs->AddrIsInFakeStack(x + 1)); - EXPECT_EQ(x, fs->AddrIsInFakeStack(x + bytes_in_class - 1)); - EXPECT_NE(x, fs->AddrIsInFakeStack(x + bytes_in_class)); - } - // We are out of fake stack, so Allocate should return 0. - EXPECT_EQ(0UL, fs->Allocate(stack_size_log, cid, 0)); - } - for (std::map<FakeFrame *, uptr>::iterator it = s.begin(); it != s.end(); - ++it) { - fs->Deallocate(reinterpret_cast<uptr>(it->first), it->second); - } - } - fs->Destroy(0); -} - -static void RecursiveFunction(FakeStack *fs, int depth) { - uptr class_id = depth / 3; - FakeFrame *ff = fs->Allocate(fs->stack_size_log(), class_id, 0); - if (depth) { - RecursiveFunction(fs, depth - 1); - RecursiveFunction(fs, depth - 1); - } - fs->Deallocate(reinterpret_cast<uptr>(ff), class_id); -} - -TEST(FakeStack, RecursiveStressTest) { - const uptr stack_size_log = 16; - FakeStack *fs = FakeStack::Create(stack_size_log); - RecursiveFunction(fs, 22); // with 26 runs for 2-3 seconds. - fs->Destroy(0); -} - -} // namespace __asan diff --git a/contrib/compiler-rt/lib/asan/tests/asan_globals_test.cc b/contrib/compiler-rt/lib/asan/tests/asan_globals_test.cc deleted file mode 100644 index 5042ef07378d..000000000000 --- a/contrib/compiler-rt/lib/asan/tests/asan_globals_test.cc +++ /dev/null @@ -1,45 +0,0 @@ -//===-- asan_globals_test.cc ----------------------------------------------===// -// -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. -// -//===----------------------------------------------------------------------===// -// -// This file is a part of AddressSanitizer, an address sanity checker. -// -// Some globals in a separate file. -//===----------------------------------------------------------------------===// -#include "asan_test_utils.h" - -char glob1[1]; -char glob2[2]; -char glob3[3]; -char glob4[4]; -char glob5[5]; -char glob6[6]; -char glob7[7]; -char glob8[8]; -char glob9[9]; -char glob10[10]; -char glob11[11]; -char glob12[12]; -char glob13[13]; -char glob14[14]; -char glob15[15]; -char glob16[16]; -char glob17[17]; -char glob1000[1000]; -char glob10000[10000]; -char glob100000[100000]; - -static char static10[10]; - -int GlobalsTest(int zero) { - static char func_static15[15]; - glob5[zero] = 0; - static10[zero] = 0; - func_static15[zero] = 0; - return glob5[1] + func_static15[2]; -} diff --git a/contrib/compiler-rt/lib/asan/tests/asan_interface_test.cc b/contrib/compiler-rt/lib/asan/tests/asan_interface_test.cc deleted file mode 100644 index a34c8528eae0..000000000000 --- a/contrib/compiler-rt/lib/asan/tests/asan_interface_test.cc +++ /dev/null @@ -1,433 +0,0 @@ -//===-- asan_interface_test.cc --------------------------------------------===// -// -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. -// -//===----------------------------------------------------------------------===// -// -// This file is a part of AddressSanitizer, an address sanity checker. -// -//===----------------------------------------------------------------------===// -#include "asan_test_utils.h" -#include <sanitizer/allocator_interface.h> -#include <sanitizer/asan_interface.h> - -TEST(AddressSanitizerInterface, GetEstimatedAllocatedSize) { - EXPECT_EQ(0U, __sanitizer_get_estimated_allocated_size(0)); - const size_t sizes[] = { 1, 30, 1<<30 }; - for (size_t i = 0; i < 3; i++) { - EXPECT_EQ(sizes[i], __sanitizer_get_estimated_allocated_size(sizes[i])); - } -} - -static const char* kGetAllocatedSizeErrorMsg = - "attempting to call __sanitizer_get_allocated_size"; - -TEST(AddressSanitizerInterface, GetAllocatedSizeAndOwnershipTest) { - const size_t kArraySize = 100; - char *array = Ident((char*)malloc(kArraySize)); - int *int_ptr = Ident(new int); - - // Allocated memory is owned by allocator. Allocated size should be - // equal to requested size. - EXPECT_EQ(true, __sanitizer_get_ownership(array)); - EXPECT_EQ(kArraySize, __sanitizer_get_allocated_size(array)); - EXPECT_EQ(true, __sanitizer_get_ownership(int_ptr)); - EXPECT_EQ(sizeof(int), __sanitizer_get_allocated_size(int_ptr)); - - // We cannot call GetAllocatedSize from the memory we didn't map, - // and from the interior pointers (not returned by previous malloc). - void *wild_addr = (void*)0x1; - EXPECT_FALSE(__sanitizer_get_ownership(wild_addr)); - EXPECT_DEATH(__sanitizer_get_allocated_size(wild_addr), - kGetAllocatedSizeErrorMsg); - EXPECT_FALSE(__sanitizer_get_ownership(array + kArraySize / 2)); - EXPECT_DEATH(__sanitizer_get_allocated_size(array + kArraySize / 2), - kGetAllocatedSizeErrorMsg); - - // NULL is not owned, but is a valid argument for - // __sanitizer_get_allocated_size(). - EXPECT_FALSE(__sanitizer_get_ownership(NULL)); - EXPECT_EQ(0U, __sanitizer_get_allocated_size(NULL)); - - // When memory is freed, it's not owned, and call to GetAllocatedSize - // is forbidden. - free(array); - EXPECT_FALSE(__sanitizer_get_ownership(array)); - EXPECT_DEATH(__sanitizer_get_allocated_size(array), - kGetAllocatedSizeErrorMsg); - delete int_ptr; - - void *zero_alloc = Ident(malloc(0)); - if (zero_alloc != 0) { - // If malloc(0) is not null, this pointer is owned and should have valid - // allocated size. - EXPECT_TRUE(__sanitizer_get_ownership(zero_alloc)); - // Allocated size is 0 or 1 depending on the allocator used. - EXPECT_LT(__sanitizer_get_allocated_size(zero_alloc), 2U); - } - free(zero_alloc); -} - -TEST(AddressSanitizerInterface, GetCurrentAllocatedBytesTest) { - size_t before_malloc, after_malloc, after_free; - char *array; - const size_t kMallocSize = 100; - before_malloc = __sanitizer_get_current_allocated_bytes(); - - array = Ident((char*)malloc(kMallocSize)); - after_malloc = __sanitizer_get_current_allocated_bytes(); - EXPECT_EQ(before_malloc + kMallocSize, after_malloc); - - free(array); - after_free = __sanitizer_get_current_allocated_bytes(); - EXPECT_EQ(before_malloc, after_free); -} - -TEST(AddressSanitizerInterface, GetHeapSizeTest) { - // ASan allocator does not keep huge chunks in free list, but unmaps them. - // The chunk should be greater than the quarantine size, - // otherwise it will be stuck in quarantine instead of being unmaped. - static const size_t kLargeMallocSize = (1 << 28) + 1; // 256M - free(Ident(malloc(kLargeMallocSize))); // Drain quarantine. - size_t old_heap_size = __sanitizer_get_heap_size(); - for (int i = 0; i < 3; i++) { - // fprintf(stderr, "allocating %zu bytes:\n", kLargeMallocSize); - free(Ident(malloc(kLargeMallocSize))); - EXPECT_EQ(old_heap_size, __sanitizer_get_heap_size()); - } -} - -static const size_t kManyThreadsMallocSizes[] = {5, 1UL<<10, 1UL<<14, 357}; -static const size_t kManyThreadsIterations = 250; -static const size_t kManyThreadsNumThreads = - (SANITIZER_WORDSIZE == 32) ? 40 : 200; - -static void *ManyThreadsWithStatsWorker(void *arg) { - (void)arg; - for (size_t iter = 0; iter < kManyThreadsIterations; iter++) { - for (size_t size_index = 0; size_index < 4; size_index++) { - free(Ident(malloc(kManyThreadsMallocSizes[size_index]))); - } - } - // Just one large allocation. - free(Ident(malloc(1 << 20))); - return 0; -} - -TEST(AddressSanitizerInterface, ManyThreadsWithStatsStressTest) { - size_t before_test, after_test, i; - pthread_t threads[kManyThreadsNumThreads]; - before_test = __sanitizer_get_current_allocated_bytes(); - for (i = 0; i < kManyThreadsNumThreads; i++) { - PTHREAD_CREATE(&threads[i], 0, - (void* (*)(void *x))ManyThreadsWithStatsWorker, (void*)i); - } - for (i = 0; i < kManyThreadsNumThreads; i++) { - PTHREAD_JOIN(threads[i], 0); - } - after_test = __sanitizer_get_current_allocated_bytes(); - // ASan stats also reflect memory usage of internal ASan RTL structs, - // so we can't check for equality here. - EXPECT_LT(after_test, before_test + (1UL<<20)); -} - -static void DoDoubleFree() { - int *x = Ident(new int); - delete Ident(x); - delete Ident(x); -} - -TEST(AddressSanitizerInterface, ExitCode) { - int original_exit_code = __asan_set_error_exit_code(7); - EXPECT_EXIT(DoDoubleFree(), ::testing::ExitedWithCode(7), ""); - EXPECT_EQ(7, __asan_set_error_exit_code(8)); - EXPECT_EXIT(DoDoubleFree(), ::testing::ExitedWithCode(8), ""); - EXPECT_EQ(8, __asan_set_error_exit_code(original_exit_code)); - EXPECT_EXIT(DoDoubleFree(), - ::testing::ExitedWithCode(original_exit_code), ""); -} - -static void MyDeathCallback() { - fprintf(stderr, "MyDeathCallback\n"); - fflush(0); // On Windows, stderr doesn't flush on crash. -} - -TEST(AddressSanitizerInterface, DeathCallbackTest) { - __asan_set_death_callback(MyDeathCallback); - EXPECT_DEATH(DoDoubleFree(), "MyDeathCallback"); - __asan_set_death_callback(NULL); -} - -static const char* kUseAfterPoisonErrorMessage = "use-after-poison"; - -#define GOOD_ACCESS(ptr, offset) \ - EXPECT_FALSE(__asan_address_is_poisoned(ptr + offset)) - -#define BAD_ACCESS(ptr, offset) \ - EXPECT_TRUE(__asan_address_is_poisoned(ptr + offset)) - -TEST(AddressSanitizerInterface, SimplePoisonMemoryRegionTest) { - char *array = Ident((char*)malloc(120)); - // poison array[40..80) - __asan_poison_memory_region(array + 40, 40); - GOOD_ACCESS(array, 39); - GOOD_ACCESS(array, 80); - BAD_ACCESS(array, 40); - BAD_ACCESS(array, 60); - BAD_ACCESS(array, 79); - char value; - EXPECT_DEATH(value = Ident(array[40]), kUseAfterPoisonErrorMessage); - __asan_unpoison_memory_region(array + 40, 40); - // access previously poisoned memory. - GOOD_ACCESS(array, 40); - GOOD_ACCESS(array, 79); - free(array); -} - -TEST(AddressSanitizerInterface, OverlappingPoisonMemoryRegionTest) { - char *array = Ident((char*)malloc(120)); - // Poison [0..40) and [80..120) - __asan_poison_memory_region(array, 40); - __asan_poison_memory_region(array + 80, 40); - BAD_ACCESS(array, 20); - GOOD_ACCESS(array, 60); - BAD_ACCESS(array, 100); - // Poison whole array - [0..120) - __asan_poison_memory_region(array, 120); - BAD_ACCESS(array, 60); - // Unpoison [24..96) - __asan_unpoison_memory_region(array + 24, 72); - BAD_ACCESS(array, 23); - GOOD_ACCESS(array, 24); - GOOD_ACCESS(array, 60); - GOOD_ACCESS(array, 95); - BAD_ACCESS(array, 96); - free(array); -} - -TEST(AddressSanitizerInterface, PushAndPopWithPoisoningTest) { - // Vector of capacity 20 - char *vec = Ident((char*)malloc(20)); - __asan_poison_memory_region(vec, 20); - for (size_t i = 0; i < 7; i++) { - // Simulate push_back. - __asan_unpoison_memory_region(vec + i, 1); - GOOD_ACCESS(vec, i); - BAD_ACCESS(vec, i + 1); - } - for (size_t i = 7; i > 0; i--) { - // Simulate pop_back. - __asan_poison_memory_region(vec + i - 1, 1); - BAD_ACCESS(vec, i - 1); - if (i > 1) GOOD_ACCESS(vec, i - 2); - } - free(vec); -} - -// Make sure that each aligned block of size "2^granularity" doesn't have -// "true" value before "false" value. -static void MakeShadowValid(bool *shadow, int length, int granularity) { - bool can_be_poisoned = true; - for (int i = length - 1; i >= 0; i--) { - if (!shadow[i]) - can_be_poisoned = false; - if (!can_be_poisoned) - shadow[i] = false; - if (i % (1 << granularity) == 0) { - can_be_poisoned = true; - } - } -} - -TEST(AddressSanitizerInterface, PoisoningStressTest) { - const size_t kSize = 24; - bool expected[kSize]; - char *arr = Ident((char*)malloc(kSize)); - for (size_t l1 = 0; l1 < kSize; l1++) { - for (size_t s1 = 1; l1 + s1 <= kSize; s1++) { - for (size_t l2 = 0; l2 < kSize; l2++) { - for (size_t s2 = 1; l2 + s2 <= kSize; s2++) { - // Poison [l1, l1+s1), [l2, l2+s2) and check result. - __asan_unpoison_memory_region(arr, kSize); - __asan_poison_memory_region(arr + l1, s1); - __asan_poison_memory_region(arr + l2, s2); - memset(expected, false, kSize); - memset(expected + l1, true, s1); - MakeShadowValid(expected, kSize, /*granularity*/ 3); - memset(expected + l2, true, s2); - MakeShadowValid(expected, kSize, /*granularity*/ 3); - for (size_t i = 0; i < kSize; i++) { - ASSERT_EQ(expected[i], __asan_address_is_poisoned(arr + i)); - } - // Unpoison [l1, l1+s1) and [l2, l2+s2) and check result. - __asan_poison_memory_region(arr, kSize); - __asan_unpoison_memory_region(arr + l1, s1); - __asan_unpoison_memory_region(arr + l2, s2); - memset(expected, true, kSize); - memset(expected + l1, false, s1); - MakeShadowValid(expected, kSize, /*granularity*/ 3); - memset(expected + l2, false, s2); - MakeShadowValid(expected, kSize, /*granularity*/ 3); - for (size_t i = 0; i < kSize; i++) { - ASSERT_EQ(expected[i], __asan_address_is_poisoned(arr + i)); - } - } - } - } - } - free(arr); -} - -TEST(AddressSanitizerInterface, GlobalRedzones) { - GOOD_ACCESS(glob1, 1 - 1); - GOOD_ACCESS(glob2, 2 - 1); - GOOD_ACCESS(glob3, 3 - 1); - GOOD_ACCESS(glob4, 4 - 1); - GOOD_ACCESS(glob5, 5 - 1); - GOOD_ACCESS(glob6, 6 - 1); - GOOD_ACCESS(glob7, 7 - 1); - GOOD_ACCESS(glob8, 8 - 1); - GOOD_ACCESS(glob9, 9 - 1); - GOOD_ACCESS(glob10, 10 - 1); - GOOD_ACCESS(glob11, 11 - 1); - GOOD_ACCESS(glob12, 12 - 1); - GOOD_ACCESS(glob13, 13 - 1); - GOOD_ACCESS(glob14, 14 - 1); - GOOD_ACCESS(glob15, 15 - 1); - GOOD_ACCESS(glob16, 16 - 1); - GOOD_ACCESS(glob17, 17 - 1); - GOOD_ACCESS(glob1000, 1000 - 1); - GOOD_ACCESS(glob10000, 10000 - 1); - GOOD_ACCESS(glob100000, 100000 - 1); - - BAD_ACCESS(glob1, 1); - BAD_ACCESS(glob2, 2); - BAD_ACCESS(glob3, 3); - BAD_ACCESS(glob4, 4); - BAD_ACCESS(glob5, 5); - BAD_ACCESS(glob6, 6); - BAD_ACCESS(glob7, 7); - BAD_ACCESS(glob8, 8); - BAD_ACCESS(glob9, 9); - BAD_ACCESS(glob10, 10); - BAD_ACCESS(glob11, 11); - BAD_ACCESS(glob12, 12); - BAD_ACCESS(glob13, 13); - BAD_ACCESS(glob14, 14); - BAD_ACCESS(glob15, 15); - BAD_ACCESS(glob16, 16); - BAD_ACCESS(glob17, 17); - BAD_ACCESS(glob1000, 1000); - BAD_ACCESS(glob1000, 1100); // Redzone is at least 101 bytes. - BAD_ACCESS(glob10000, 10000); - BAD_ACCESS(glob10000, 11000); // Redzone is at least 1001 bytes. - BAD_ACCESS(glob100000, 100000); - BAD_ACCESS(glob100000, 110000); // Redzone is at least 10001 bytes. -} - -TEST(AddressSanitizerInterface, PoisonedRegion) { - size_t rz = 16; - for (size_t size = 1; size <= 64; size++) { - char *p = new char[size]; - for (size_t beg = 0; beg < size + rz; beg++) { - for (size_t end = beg; end < size + rz; end++) { - void *first_poisoned = __asan_region_is_poisoned(p + beg, end - beg); - if (beg == end) { - EXPECT_FALSE(first_poisoned); - } else if (beg < size && end <= size) { - EXPECT_FALSE(first_poisoned); - } else if (beg >= size) { - EXPECT_EQ(p + beg, first_poisoned); - } else { - EXPECT_GT(end, size); - EXPECT_EQ(p + size, first_poisoned); - } - } - } - delete [] p; - } -} - -// This is a performance benchmark for manual runs. -// asan's memset interceptor calls mem_is_zero for the entire shadow region. -// the profile should look like this: -// 89.10% [.] __memset_sse2 -// 10.50% [.] __sanitizer::mem_is_zero -// I.e. mem_is_zero should consume ~ SHADOW_GRANULARITY less CPU cycles -// than memset itself. -TEST(AddressSanitizerInterface, DISABLED_StressLargeMemset) { - size_t size = 1 << 20; - char *x = new char[size]; - for (int i = 0; i < 100000; i++) - Ident(memset)(x, 0, size); - delete [] x; -} - -// Same here, but we run memset with small sizes. -TEST(AddressSanitizerInterface, DISABLED_StressSmallMemset) { - size_t size = 32; - char *x = new char[size]; - for (int i = 0; i < 100000000; i++) - Ident(memset)(x, 0, size); - delete [] x; -} -static const char *kInvalidPoisonMessage = "invalid-poison-memory-range"; -static const char *kInvalidUnpoisonMessage = "invalid-unpoison-memory-range"; - -TEST(AddressSanitizerInterface, DISABLED_InvalidPoisonAndUnpoisonCallsTest) { - char *array = Ident((char*)malloc(120)); - __asan_unpoison_memory_region(array, 120); - // Try to unpoison not owned memory - EXPECT_DEATH(__asan_unpoison_memory_region(array, 121), - kInvalidUnpoisonMessage); - EXPECT_DEATH(__asan_unpoison_memory_region(array - 1, 120), - kInvalidUnpoisonMessage); - - __asan_poison_memory_region(array, 120); - // Try to poison not owned memory. - EXPECT_DEATH(__asan_poison_memory_region(array, 121), kInvalidPoisonMessage); - EXPECT_DEATH(__asan_poison_memory_region(array - 1, 120), - kInvalidPoisonMessage); - free(array); -} - -#if !defined(_WIN32) // FIXME: This should really be a lit test. -static void ErrorReportCallbackOneToZ(const char *report) { - int report_len = strlen(report); - ASSERT_EQ(6, write(2, "ABCDEF", 6)); - ASSERT_EQ(report_len, write(2, report, report_len)); - ASSERT_EQ(6, write(2, "ABCDEF", 6)); - _exit(1); -} - -TEST(AddressSanitizerInterface, SetErrorReportCallbackTest) { - __asan_set_error_report_callback(ErrorReportCallbackOneToZ); - EXPECT_DEATH(__asan_report_error(0, 0, 0, 0, true, 1), - ASAN_PCRE_DOTALL "ABCDEF.*AddressSanitizer.*WRITE.*ABCDEF"); - __asan_set_error_report_callback(NULL); -} -#endif - -TEST(AddressSanitizerInterface, GetOwnershipStressTest) { - std::vector<char *> pointers; - std::vector<size_t> sizes; - const size_t kNumMallocs = 1 << 9; - for (size_t i = 0; i < kNumMallocs; i++) { - size_t size = i * 100 + 1; - pointers.push_back((char*)malloc(size)); - sizes.push_back(size); - } - for (size_t i = 0; i < 4000000; i++) { - EXPECT_FALSE(__sanitizer_get_ownership(&pointers)); - EXPECT_FALSE(__sanitizer_get_ownership((void*)0x1234)); - size_t idx = i % kNumMallocs; - EXPECT_TRUE(__sanitizer_get_ownership(pointers[idx])); - EXPECT_EQ(sizes[idx], __sanitizer_get_allocated_size(pointers[idx])); - } - for (size_t i = 0, n = pointers.size(); i < n; i++) - free(pointers[i]); -} - diff --git a/contrib/compiler-rt/lib/asan/tests/asan_mac_test.cc b/contrib/compiler-rt/lib/asan/tests/asan_mac_test.cc deleted file mode 100644 index cabdfd711ea2..000000000000 --- a/contrib/compiler-rt/lib/asan/tests/asan_mac_test.cc +++ /dev/null @@ -1,236 +0,0 @@ -//===-- asan_test_mac.cc --------------------------------------------------===// -// -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. -// -//===----------------------------------------------------------------------===// -// -// This file is a part of AddressSanitizer, an address sanity checker. -// -//===----------------------------------------------------------------------===// - -#include "asan_test_utils.h" - -#include "asan_mac_test.h" - -#include <malloc/malloc.h> -#include <AvailabilityMacros.h> // For MAC_OS_X_VERSION_* -#include <CoreFoundation/CFString.h> - -TEST(AddressSanitizerMac, CFAllocatorDefaultDoubleFree) { - EXPECT_DEATH( - CFAllocatorDefaultDoubleFree(NULL), - "attempting double-free"); -} - -void CFAllocator_DoubleFreeOnPthread() { - pthread_t child; - PTHREAD_CREATE(&child, NULL, CFAllocatorDefaultDoubleFree, NULL); - PTHREAD_JOIN(child, NULL); // Shouldn't be reached. -} - -TEST(AddressSanitizerMac, CFAllocatorDefaultDoubleFree_ChildPhread) { - EXPECT_DEATH(CFAllocator_DoubleFreeOnPthread(), "attempting double-free"); -} - -namespace { - -void *GLOB; - -void *CFAllocatorAllocateToGlob(void *unused) { - GLOB = CFAllocatorAllocate(NULL, 100, /*hint*/0); - return NULL; -} - -void *CFAllocatorDeallocateFromGlob(void *unused) { - char *p = (char*)GLOB; - p[100] = 'A'; // ASan should report an error here. - CFAllocatorDeallocate(NULL, GLOB); - return NULL; -} - -void CFAllocator_PassMemoryToAnotherThread() { - pthread_t th1, th2; - PTHREAD_CREATE(&th1, NULL, CFAllocatorAllocateToGlob, NULL); - PTHREAD_JOIN(th1, NULL); - PTHREAD_CREATE(&th2, NULL, CFAllocatorDeallocateFromGlob, NULL); - PTHREAD_JOIN(th2, NULL); -} - -TEST(AddressSanitizerMac, CFAllocator_PassMemoryToAnotherThread) { - EXPECT_DEATH(CFAllocator_PassMemoryToAnotherThread(), - "heap-buffer-overflow"); -} - -} // namespace - -// TODO(glider): figure out whether we still need these tests. Is it correct -// to intercept the non-default CFAllocators? -TEST(AddressSanitizerMac, DISABLED_CFAllocatorSystemDefaultDoubleFree) { - EXPECT_DEATH( - CFAllocatorSystemDefaultDoubleFree(), - "attempting double-free"); -} - -// We're intercepting malloc, so kCFAllocatorMalloc is routed to ASan. -TEST(AddressSanitizerMac, CFAllocatorMallocDoubleFree) { - EXPECT_DEATH(CFAllocatorMallocDoubleFree(), "attempting double-free"); -} - -TEST(AddressSanitizerMac, DISABLED_CFAllocatorMallocZoneDoubleFree) { - EXPECT_DEATH(CFAllocatorMallocZoneDoubleFree(), "attempting double-free"); -} - -// For libdispatch tests below we check that ASan got to the shadow byte -// legend, i.e. managed to print the thread stacks (this almost certainly -// means that the libdispatch task creation has been intercepted correctly). -TEST(AddressSanitizerMac, GCDDispatchAsync) { - // Make sure the whole ASan report is printed, i.e. that we don't die - // on a CHECK. - EXPECT_DEATH(TestGCDDispatchAsync(), "Shadow byte legend"); -} - -TEST(AddressSanitizerMac, GCDDispatchSync) { - // Make sure the whole ASan report is printed, i.e. that we don't die - // on a CHECK. - EXPECT_DEATH(TestGCDDispatchSync(), "Shadow byte legend"); -} - - -TEST(AddressSanitizerMac, GCDReuseWqthreadsAsync) { - // Make sure the whole ASan report is printed, i.e. that we don't die - // on a CHECK. - EXPECT_DEATH(TestGCDReuseWqthreadsAsync(), "Shadow byte legend"); -} - -TEST(AddressSanitizerMac, GCDReuseWqthreadsSync) { - // Make sure the whole ASan report is printed, i.e. that we don't die - // on a CHECK. - EXPECT_DEATH(TestGCDReuseWqthreadsSync(), "Shadow byte legend"); -} - -TEST(AddressSanitizerMac, GCDDispatchAfter) { - // Make sure the whole ASan report is printed, i.e. that we don't die - // on a CHECK. - EXPECT_DEATH(TestGCDDispatchAfter(), "Shadow byte legend"); -} - -TEST(AddressSanitizerMac, GCDSourceEvent) { - // Make sure the whole ASan report is printed, i.e. that we don't die - // on a CHECK. - EXPECT_DEATH(TestGCDSourceEvent(), "Shadow byte legend"); -} - -TEST(AddressSanitizerMac, GCDSourceCancel) { - // Make sure the whole ASan report is printed, i.e. that we don't die - // on a CHECK. - EXPECT_DEATH(TestGCDSourceCancel(), "Shadow byte legend"); -} - -TEST(AddressSanitizerMac, GCDGroupAsync) { - // Make sure the whole ASan report is printed, i.e. that we don't die - // on a CHECK. - EXPECT_DEATH(TestGCDGroupAsync(), "Shadow byte legend"); -} - -void *MallocIntrospectionLockWorker(void *_) { - const int kNumPointers = 100; - int i; - void *pointers[kNumPointers]; - for (i = 0; i < kNumPointers; i++) { - pointers[i] = malloc(i + 1); - } - for (i = 0; i < kNumPointers; i++) { - free(pointers[i]); - } - - return NULL; -} - -void *MallocIntrospectionLockForker(void *_) { - pid_t result = fork(); - if (result == -1) { - perror("fork"); - } - assert(result != -1); - if (result == 0) { - // Call malloc in the child process to make sure we won't deadlock. - void *ptr = malloc(42); - free(ptr); - exit(0); - } else { - // Return in the parent process. - return NULL; - } -} - -TEST(AddressSanitizerMac, MallocIntrospectionLock) { - // Incorrect implementation of force_lock and force_unlock in our malloc zone - // will cause forked processes to deadlock. - // TODO(glider): need to detect that none of the child processes deadlocked. - const int kNumWorkers = 5, kNumIterations = 100; - int i, iter; - for (iter = 0; iter < kNumIterations; iter++) { - pthread_t workers[kNumWorkers], forker; - for (i = 0; i < kNumWorkers; i++) { - PTHREAD_CREATE(&workers[i], 0, MallocIntrospectionLockWorker, 0); - } - PTHREAD_CREATE(&forker, 0, MallocIntrospectionLockForker, 0); - for (i = 0; i < kNumWorkers; i++) { - PTHREAD_JOIN(workers[i], 0); - } - PTHREAD_JOIN(forker, 0); - } -} - -void *TSDAllocWorker(void *test_key) { - if (test_key) { - void *mem = malloc(10); - pthread_setspecific(*(pthread_key_t*)test_key, mem); - } - return NULL; -} - -TEST(AddressSanitizerMac, DISABLED_TSDWorkqueueTest) { - pthread_t th; - pthread_key_t test_key; - pthread_key_create(&test_key, CallFreeOnWorkqueue); - PTHREAD_CREATE(&th, NULL, TSDAllocWorker, &test_key); - PTHREAD_JOIN(th, NULL); - pthread_key_delete(test_key); -} - -// Test that CFStringCreateCopy does not copy constant strings. -TEST(AddressSanitizerMac, CFStringCreateCopy) { - CFStringRef str = CFSTR("Hello world!\n"); - CFStringRef str2 = CFStringCreateCopy(0, str); - EXPECT_EQ(str, str2); -} - -TEST(AddressSanitizerMac, NSObjectOOB) { - // Make sure that our allocators are used for NSObjects. - EXPECT_DEATH(TestOOBNSObjects(), "heap-buffer-overflow"); -} - -// Make sure that correct pointer is passed to free() when deallocating a -// NSURL object. -// See http://code.google.com/p/address-sanitizer/issues/detail?id=70. -TEST(AddressSanitizerMac, NSURLDeallocation) { - TestNSURLDeallocation(); -} - -// See http://code.google.com/p/address-sanitizer/issues/detail?id=109. -TEST(AddressSanitizerMac, Mstats) { - malloc_statistics_t stats1, stats2; - malloc_zone_statistics(/*all zones*/NULL, &stats1); - const size_t kMallocSize = 100000; - void *alloc = Ident(malloc(kMallocSize)); - malloc_zone_statistics(/*all zones*/NULL, &stats2); - EXPECT_GT(stats2.blocks_in_use, stats1.blocks_in_use); - EXPECT_GE(stats2.size_in_use - stats1.size_in_use, kMallocSize); - free(alloc); - // Even the default OSX allocator may not change the stats after free(). -} - diff --git a/contrib/compiler-rt/lib/asan/tests/asan_mac_test.h b/contrib/compiler-rt/lib/asan/tests/asan_mac_test.h deleted file mode 100644 index 441547a5a3dc..000000000000 --- a/contrib/compiler-rt/lib/asan/tests/asan_mac_test.h +++ /dev/null @@ -1,19 +0,0 @@ -extern "C" { - void *CFAllocatorDefaultDoubleFree(void *unused); - void CFAllocatorSystemDefaultDoubleFree(); - void CFAllocatorMallocDoubleFree(); - void CFAllocatorMallocZoneDoubleFree(); - void CallFreeOnWorkqueue(void *mem); - void TestGCDDispatchAsync(); - void TestGCDDispatchSync(); - void TestGCDReuseWqthreadsAsync(); - void TestGCDReuseWqthreadsSync(); - void TestGCDDispatchAfter(); - void TestGCDInTSDDestructor(); - void TestGCDSourceEvent(); - void TestGCDSourceCancel(); - void TestGCDGroupAsync(); - void TestOOBNSObjects(); - void TestNSURLDeallocation(); - void TestPassCFMemoryToAnotherThread(); -} diff --git a/contrib/compiler-rt/lib/asan/tests/asan_mac_test_helpers.mm b/contrib/compiler-rt/lib/asan/tests/asan_mac_test_helpers.mm deleted file mode 100644 index a7e4b9d1928b..000000000000 --- a/contrib/compiler-rt/lib/asan/tests/asan_mac_test_helpers.mm +++ /dev/null @@ -1,240 +0,0 @@ -// Mac OS X 10.6 or higher only. -#include <dispatch/dispatch.h> -#include <pthread.h> // for pthread_yield_np() -#include <stdio.h> -#include <stdlib.h> -#include <string.h> -#include <unistd.h> - -#import <CoreFoundation/CFBase.h> -#import <Foundation/NSObject.h> -#import <Foundation/NSURL.h> - -// This is a (void*)(void*) function so it can be passed to pthread_create. -void *CFAllocatorDefaultDoubleFree(void *unused) { - void *mem = CFAllocatorAllocate(kCFAllocatorDefault, 5, 0); - CFAllocatorDeallocate(kCFAllocatorDefault, mem); - CFAllocatorDeallocate(kCFAllocatorDefault, mem); - return 0; -} - -void CFAllocatorSystemDefaultDoubleFree() { - void *mem = CFAllocatorAllocate(kCFAllocatorSystemDefault, 5, 0); - CFAllocatorDeallocate(kCFAllocatorSystemDefault, mem); - CFAllocatorDeallocate(kCFAllocatorSystemDefault, mem); -} - -void CFAllocatorMallocDoubleFree() { - void *mem = CFAllocatorAllocate(kCFAllocatorMalloc, 5, 0); - CFAllocatorDeallocate(kCFAllocatorMalloc, mem); - CFAllocatorDeallocate(kCFAllocatorMalloc, mem); -} - -void CFAllocatorMallocZoneDoubleFree() { - void *mem = CFAllocatorAllocate(kCFAllocatorMallocZone, 5, 0); - CFAllocatorDeallocate(kCFAllocatorMallocZone, mem); - CFAllocatorDeallocate(kCFAllocatorMallocZone, mem); -} - -__attribute__((noinline)) -void access_memory(char *a) { - *a = 0; -} - -// Test the +load instrumentation. -// Because the +load methods are invoked before anything else is initialized, -// it makes little sense to wrap the code below into a gTest test case. -// If AddressSanitizer doesn't instrument the +load method below correctly, -// everything will just crash. - -char kStartupStr[] = - "If your test didn't crash, AddressSanitizer is instrumenting " - "the +load methods correctly."; - -@interface LoadSomething : NSObject { -} -@end - -@implementation LoadSomething - -+(void) load { - for (size_t i = 0; i < strlen(kStartupStr); i++) { - access_memory(&kStartupStr[i]); // make sure no optimizations occur. - } - // Don't print anything here not to interfere with the death tests. -} - -@end - -void worker_do_alloc(int size) { - char * volatile mem = (char * volatile)malloc(size); - mem[0] = 0; // Ok - free(mem); -} - -void worker_do_crash(int size) { - char * volatile mem = (char * volatile)malloc(size); - access_memory(&mem[size]); // BOOM - free(mem); -} - -// Used by the GCD tests to avoid a race between the worker thread reporting a -// memory error and the main thread which may exit with exit code 0 before -// that. -void wait_forever() { - volatile bool infinite = true; - while (infinite) pthread_yield_np(); -} - -// Tests for the Grand Central Dispatch. See -// http://developer.apple.com/library/mac/#documentation/Performance/Reference/GCD_libdispatch_Ref/Reference/reference.html -// for the reference. -void TestGCDDispatchAsync() { - dispatch_queue_t queue = dispatch_get_global_queue(0, 0); - dispatch_block_t block = ^{ worker_do_crash(1024); }; - // dispatch_async() runs the task on a worker thread that does not go through - // pthread_create(). We need to verify that AddressSanitizer notices that the - // thread has started. - dispatch_async(queue, block); - wait_forever(); -} - -void TestGCDDispatchSync() { - dispatch_queue_t queue = dispatch_get_global_queue(2, 0); - dispatch_block_t block = ^{ worker_do_crash(1024); }; - // dispatch_sync() runs the task on a worker thread that does not go through - // pthread_create(). We need to verify that AddressSanitizer notices that the - // thread has started. - dispatch_sync(queue, block); - wait_forever(); -} - -// libdispatch spawns a rather small number of threads and reuses them. We need -// to make sure AddressSanitizer handles the reusing correctly. -void TestGCDReuseWqthreadsAsync() { - dispatch_queue_t queue = dispatch_get_global_queue(0, 0); - dispatch_block_t block_alloc = ^{ worker_do_alloc(1024); }; - dispatch_block_t block_crash = ^{ worker_do_crash(1024); }; - for (int i = 0; i < 100; i++) { - dispatch_async(queue, block_alloc); - } - dispatch_async(queue, block_crash); - wait_forever(); -} - -// Try to trigger abnormal behaviour of dispatch_sync() being unhandled by us. -void TestGCDReuseWqthreadsSync() { - dispatch_queue_t queue[4]; - queue[0] = dispatch_get_global_queue(2, 0); - queue[1] = dispatch_get_global_queue(0, 0); - queue[2] = dispatch_get_global_queue(-2, 0); - queue[3] = dispatch_queue_create("my_queue", NULL); - dispatch_block_t block_alloc = ^{ worker_do_alloc(1024); }; - dispatch_block_t block_crash = ^{ worker_do_crash(1024); }; - for (int i = 0; i < 1000; i++) { - dispatch_sync(queue[i % 4], block_alloc); - } - dispatch_sync(queue[3], block_crash); - wait_forever(); -} - -void TestGCDDispatchAfter() { - dispatch_queue_t queue = dispatch_get_global_queue(0, 0); - dispatch_block_t block_crash = ^{ worker_do_crash(1024); }; - // Schedule the event one second from the current time. - dispatch_time_t milestone = - dispatch_time(DISPATCH_TIME_NOW, 1LL * NSEC_PER_SEC); - dispatch_after(milestone, queue, block_crash); - wait_forever(); -} - -void worker_do_deallocate(void *ptr) { - free(ptr); -} - -void CallFreeOnWorkqueue(void *tsd) { - dispatch_queue_t queue = dispatch_get_global_queue(0, 0); - dispatch_block_t block_dealloc = ^{ worker_do_deallocate(tsd); }; - dispatch_async(queue, block_dealloc); - // Do not wait for the worker to free the memory -- nobody is going to touch - // it. -} - -void TestGCDSourceEvent() { - dispatch_queue_t queue = dispatch_get_global_queue(0, 0); - dispatch_source_t timer = - dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, queue); - // Schedule the timer one second from the current time. - dispatch_time_t milestone = - dispatch_time(DISPATCH_TIME_NOW, 1LL * NSEC_PER_SEC); - - dispatch_source_set_timer(timer, milestone, DISPATCH_TIME_FOREVER, 0); - char * volatile mem = (char * volatile)malloc(10); - dispatch_source_set_event_handler(timer, ^{ - access_memory(&mem[10]); - }); - dispatch_resume(timer); - wait_forever(); -} - -void TestGCDSourceCancel() { - dispatch_queue_t queue = dispatch_get_global_queue(0, 0); - dispatch_source_t timer = - dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, queue); - // Schedule the timer one second from the current time. - dispatch_time_t milestone = - dispatch_time(DISPATCH_TIME_NOW, 1LL * NSEC_PER_SEC); - - dispatch_source_set_timer(timer, milestone, DISPATCH_TIME_FOREVER, 0); - char * volatile mem = (char * volatile)malloc(10); - // Both dispatch_source_set_cancel_handler() and - // dispatch_source_set_event_handler() use dispatch_barrier_async_f(). - // It's tricky to test dispatch_source_set_cancel_handler() separately, - // so we test both here. - dispatch_source_set_event_handler(timer, ^{ - dispatch_source_cancel(timer); - }); - dispatch_source_set_cancel_handler(timer, ^{ - access_memory(&mem[10]); - }); - dispatch_resume(timer); - wait_forever(); -} - -void TestGCDGroupAsync() { - dispatch_queue_t queue = dispatch_get_global_queue(0, 0); - dispatch_group_t group = dispatch_group_create(); - char * volatile mem = (char * volatile)malloc(10); - dispatch_group_async(group, queue, ^{ - access_memory(&mem[10]); - }); - dispatch_group_wait(group, DISPATCH_TIME_FOREVER); - wait_forever(); -} - -@interface FixedArray : NSObject { - int items[10]; -} -@end - -@implementation FixedArray --(int) access: (int)index { - return items[index]; -} -@end - -void TestOOBNSObjects() { - id anObject = [FixedArray new]; - [anObject access:1]; - [anObject access:11]; - [anObject release]; -} - -void TestNSURLDeallocation() { - NSURL *base = - [[NSURL alloc] initWithString:@"file://localhost/Users/glider/Library/"]; - volatile NSURL *u = - [[NSURL alloc] initWithString:@"Saved Application State" - relativeToURL:base]; - [u release]; -} diff --git a/contrib/compiler-rt/lib/asan/tests/asan_mem_test.cc b/contrib/compiler-rt/lib/asan/tests/asan_mem_test.cc deleted file mode 100644 index 4a941faa0430..000000000000 --- a/contrib/compiler-rt/lib/asan/tests/asan_mem_test.cc +++ /dev/null @@ -1,241 +0,0 @@ -//===-- asan_mem_test.cc --------------------------------------------------===// -// -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. -// -//===----------------------------------------------------------------------===// -// -// This file is a part of AddressSanitizer, an address sanity checker. -// -//===----------------------------------------------------------------------===// -#include "asan_test_utils.h" - -template<typename T> -void MemSetOOBTestTemplate(size_t length) { - if (length == 0) return; - size_t size = Ident(sizeof(T) * length); - T *array = Ident((T*)malloc(size)); - int element = Ident(42); - int zero = Ident(0); - void *(*MEMSET)(void *s, int c, size_t n) = Ident(memset); - // memset interval inside array - MEMSET(array, element, size); - MEMSET(array, element, size - 1); - MEMSET(array + length - 1, element, sizeof(T)); - MEMSET(array, element, 1); - - // memset 0 bytes - MEMSET(array - 10, element, zero); - MEMSET(array - 1, element, zero); - MEMSET(array, element, zero); - MEMSET(array + length, 0, zero); - MEMSET(array + length + 1, 0, zero); - - // try to memset bytes to the right of array - EXPECT_DEATH(MEMSET(array, 0, size + 1), - RightOOBWriteMessage(0)); - EXPECT_DEATH(MEMSET((char*)(array + length) - 1, element, 6), - RightOOBWriteMessage(0)); - EXPECT_DEATH(MEMSET(array + 1, element, size + sizeof(T)), - RightOOBWriteMessage(0)); - // whole interval is to the right - EXPECT_DEATH(MEMSET(array + length + 1, 0, 10), - RightOOBWriteMessage(sizeof(T))); - - // try to memset bytes to the left of array - EXPECT_DEATH(MEMSET((char*)array - 1, element, size), - LeftOOBWriteMessage(1)); - EXPECT_DEATH(MEMSET((char*)array - 5, 0, 6), - LeftOOBWriteMessage(5)); - if (length >= 100) { - // Large OOB, we find it only if the redzone is large enough. - EXPECT_DEATH(memset(array - 5, element, size + 5 * sizeof(T)), - LeftOOBWriteMessage(5 * sizeof(T))); - } - // whole interval is to the left - EXPECT_DEATH(MEMSET(array - 2, 0, sizeof(T)), - LeftOOBWriteMessage(2 * sizeof(T))); - - // try to memset bytes both to the left & to the right - EXPECT_DEATH(MEMSET((char*)array - 2, element, size + 4), - LeftOOBWriteMessage(2)); - - free(array); -} - -TEST(AddressSanitizer, MemSetOOBTest) { - MemSetOOBTestTemplate<char>(100); - MemSetOOBTestTemplate<int>(5); - MemSetOOBTestTemplate<double>(256); - // We can test arrays of structres/classes here, but what for? -} - -// Try to allocate two arrays of 'size' bytes that are near each other. -// Strictly speaking we are not guaranteed to find such two pointers, -// but given the structure of asan's allocator we will. -static bool AllocateTwoAdjacentArrays(char **x1, char **x2, size_t size) { - vector<uintptr_t> v; - bool res = false; - for (size_t i = 0; i < 1000U && !res; i++) { - v.push_back(reinterpret_cast<uintptr_t>(new char[size])); - if (i == 0) continue; - sort(v.begin(), v.end()); - for (size_t j = 1; j < v.size(); j++) { - assert(v[j] > v[j-1]); - if ((size_t)(v[j] - v[j-1]) < size * 2) { - *x2 = reinterpret_cast<char*>(v[j]); - *x1 = reinterpret_cast<char*>(v[j-1]); - res = true; - break; - } - } - } - - for (size_t i = 0; i < v.size(); i++) { - char *p = reinterpret_cast<char *>(v[i]); - if (res && p == *x1) continue; - if (res && p == *x2) continue; - delete [] p; - } - return res; -} - -TEST(AddressSanitizer, LargeOOBInMemset) { - for (size_t size = 200; size < 100000; size += size / 2) { - char *x1, *x2; - if (!Ident(AllocateTwoAdjacentArrays)(&x1, &x2, size)) - continue; - // fprintf(stderr, " large oob memset: %p %p %zd\n", x1, x2, size); - // Do a memset on x1 with huge out-of-bound access that will end up in x2. - EXPECT_DEATH(Ident(memset)(x1, 0, size * 2), - "is located 0 bytes to the right"); - delete [] x1; - delete [] x2; - return; - } - assert(0 && "Did not find two adjacent malloc-ed pointers"); -} - -// Same test for memcpy and memmove functions -template <typename T, class M> -void MemTransferOOBTestTemplate(size_t length) { - if (length == 0) return; - size_t size = Ident(sizeof(T) * length); - T *src = Ident((T*)malloc(size)); - T *dest = Ident((T*)malloc(size)); - int zero = Ident(0); - - // valid transfer of bytes between arrays - M::transfer(dest, src, size); - M::transfer(dest + 1, src, size - sizeof(T)); - M::transfer(dest, src + length - 1, sizeof(T)); - M::transfer(dest, src, 1); - - // transfer zero bytes - M::transfer(dest - 1, src, 0); - M::transfer(dest + length, src, zero); - M::transfer(dest, src - 1, zero); - M::transfer(dest, src, zero); - - // try to change mem to the right of dest - EXPECT_DEATH(M::transfer(dest + 1, src, size), - RightOOBWriteMessage(0)); - EXPECT_DEATH(M::transfer((char*)(dest + length) - 1, src, 5), - RightOOBWriteMessage(0)); - - // try to change mem to the left of dest - EXPECT_DEATH(M::transfer(dest - 2, src, size), - LeftOOBWriteMessage(2 * sizeof(T))); - EXPECT_DEATH(M::transfer((char*)dest - 3, src, 4), - LeftOOBWriteMessage(3)); - - // try to access mem to the right of src - EXPECT_DEATH(M::transfer(dest, src + 2, size), - RightOOBReadMessage(0)); - EXPECT_DEATH(M::transfer(dest, (char*)(src + length) - 3, 6), - RightOOBReadMessage(0)); - - // try to access mem to the left of src - EXPECT_DEATH(M::transfer(dest, src - 1, size), - LeftOOBReadMessage(sizeof(T))); - EXPECT_DEATH(M::transfer(dest, (char*)src - 6, 7), - LeftOOBReadMessage(6)); - - // Generally we don't need to test cases where both accessing src and writing - // to dest address to poisoned memory. - - T *big_src = Ident((T*)malloc(size * 2)); - T *big_dest = Ident((T*)malloc(size * 2)); - // try to change mem to both sides of dest - EXPECT_DEATH(M::transfer(dest - 1, big_src, size * 2), - LeftOOBWriteMessage(sizeof(T))); - // try to access mem to both sides of src - EXPECT_DEATH(M::transfer(big_dest, src - 2, size * 2), - LeftOOBReadMessage(2 * sizeof(T))); - - free(src); - free(dest); - free(big_src); - free(big_dest); -} - -class MemCpyWrapper { - public: - static void* transfer(void *to, const void *from, size_t size) { - return Ident(memcpy)(to, from, size); - } -}; - -TEST(AddressSanitizer, MemCpyOOBTest) { - MemTransferOOBTestTemplate<char, MemCpyWrapper>(100); - MemTransferOOBTestTemplate<int, MemCpyWrapper>(1024); -} - -class MemMoveWrapper { - public: - static void* transfer(void *to, const void *from, size_t size) { - return Ident(memmove)(to, from, size); - } -}; - -TEST(AddressSanitizer, MemMoveOOBTest) { - MemTransferOOBTestTemplate<char, MemMoveWrapper>(100); - MemTransferOOBTestTemplate<int, MemMoveWrapper>(1024); -} - - -TEST(AddressSanitizer, MemCmpOOBTest) { - size_t size = Ident(100); - char *s1 = MallocAndMemsetString(size); - char *s2 = MallocAndMemsetString(size); - // Normal memcmp calls. - Ident(memcmp(s1, s2, size)); - Ident(memcmp(s1 + size - 1, s2 + size - 1, 1)); - Ident(memcmp(s1 - 1, s2 - 1, 0)); - // One of arguments points to not allocated memory. - EXPECT_DEATH(Ident(memcmp)(s1 - 1, s2, 1), LeftOOBReadMessage(1)); - EXPECT_DEATH(Ident(memcmp)(s1, s2 - 1, 1), LeftOOBReadMessage(1)); - EXPECT_DEATH(Ident(memcmp)(s1 + size, s2, 1), RightOOBReadMessage(0)); - EXPECT_DEATH(Ident(memcmp)(s1, s2 + size, 1), RightOOBReadMessage(0)); - // Hit unallocated memory and die. - EXPECT_DEATH(Ident(memcmp)(s1 + 1, s2 + 1, size), RightOOBReadMessage(0)); - EXPECT_DEATH(Ident(memcmp)(s1 + size - 1, s2, 2), RightOOBReadMessage(0)); - // Zero bytes are not terminators and don't prevent from OOB. - s1[size - 1] = '\0'; - s2[size - 1] = '\0'; - EXPECT_DEATH(Ident(memcmp)(s1, s2, size + 1), RightOOBReadMessage(0)); - - // Even if the buffers differ in the first byte, we still assume that - // memcmp may access the whole buffer and thus reporting the overflow here: - s1[0] = 1; - s2[0] = 123; - EXPECT_DEATH(Ident(memcmp)(s1, s2, size + 1), RightOOBReadMessage(0)); - - free(s1); - free(s2); -} - - - diff --git a/contrib/compiler-rt/lib/asan/tests/asan_noinst_test.cc b/contrib/compiler-rt/lib/asan/tests/asan_noinst_test.cc deleted file mode 100644 index 6a428fbbc2b9..000000000000 --- a/contrib/compiler-rt/lib/asan/tests/asan_noinst_test.cc +++ /dev/null @@ -1,263 +0,0 @@ -//===-- asan_noinst_test.cc -----------------------------------------------===// -// -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. -// -//===----------------------------------------------------------------------===// -// -// This file is a part of AddressSanitizer, an address sanity checker. -// -// This test file should be compiled w/o asan instrumentation. -//===----------------------------------------------------------------------===// - -#include "asan_allocator.h" -#include "asan_internal.h" -#include "asan_mapping.h" -#include "asan_test_utils.h" -#include <sanitizer/allocator_interface.h> - -#include <assert.h> -#include <stdio.h> -#include <stdlib.h> -#include <string.h> // for memset() -#include <algorithm> -#include <vector> -#include <limits> - -// ATTENTION! -// Please don't call intercepted functions (including malloc() and friends) -// in this test. The static runtime library is linked explicitly (without -// -fsanitize=address), thus the interceptors do not work correctly on OS X. - -// Make sure __asan_init is called before any test case is run. -struct AsanInitCaller { - AsanInitCaller() { - __asan::DisableReexec(); - __asan_init(); - } -}; -static AsanInitCaller asan_init_caller; - -TEST(AddressSanitizer, InternalSimpleDeathTest) { - EXPECT_DEATH(exit(1), ""); -} - -static void MallocStress(size_t n) { - u32 seed = my_rand(); - BufferedStackTrace stack1; - stack1.trace_buffer[0] = 0xa123; - stack1.trace_buffer[1] = 0xa456; - stack1.size = 2; - - BufferedStackTrace stack2; - stack2.trace_buffer[0] = 0xb123; - stack2.trace_buffer[1] = 0xb456; - stack2.size = 2; - - BufferedStackTrace stack3; - stack3.trace_buffer[0] = 0xc123; - stack3.trace_buffer[1] = 0xc456; - stack3.size = 2; - - std::vector<void *> vec; - for (size_t i = 0; i < n; i++) { - if ((i % 3) == 0) { - if (vec.empty()) continue; - size_t idx = my_rand_r(&seed) % vec.size(); - void *ptr = vec[idx]; - vec[idx] = vec.back(); - vec.pop_back(); - __asan::asan_free(ptr, &stack1, __asan::FROM_MALLOC); - } else { - size_t size = my_rand_r(&seed) % 1000 + 1; - switch ((my_rand_r(&seed) % 128)) { - case 0: size += 1024; break; - case 1: size += 2048; break; - case 2: size += 4096; break; - } - size_t alignment = 1 << (my_rand_r(&seed) % 10 + 1); - char *ptr = (char*)__asan::asan_memalign(alignment, size, - &stack2, __asan::FROM_MALLOC); - EXPECT_EQ(size, __asan::asan_malloc_usable_size(ptr, 0, 0)); - vec.push_back(ptr); - ptr[0] = 0; - ptr[size-1] = 0; - ptr[size/2] = 0; - } - } - for (size_t i = 0; i < vec.size(); i++) - __asan::asan_free(vec[i], &stack3, __asan::FROM_MALLOC); -} - - -TEST(AddressSanitizer, NoInstMallocTest) { - MallocStress(ASAN_LOW_MEMORY ? 300000 : 1000000); -} - -TEST(AddressSanitizer, ThreadedMallocStressTest) { - const int kNumThreads = 4; - const int kNumIterations = (ASAN_LOW_MEMORY) ? 10000 : 100000; - pthread_t t[kNumThreads]; - for (int i = 0; i < kNumThreads; i++) { - PTHREAD_CREATE(&t[i], 0, (void* (*)(void *x))MallocStress, - (void*)kNumIterations); - } - for (int i = 0; i < kNumThreads; i++) { - PTHREAD_JOIN(t[i], 0); - } -} - -static void PrintShadow(const char *tag, uptr ptr, size_t size) { - fprintf(stderr, "%s shadow: %lx size % 3ld: ", tag, (long)ptr, (long)size); - uptr prev_shadow = 0; - for (sptr i = -32; i < (sptr)size + 32; i++) { - uptr shadow = __asan::MemToShadow(ptr + i); - if (i == 0 || i == (sptr)size) - fprintf(stderr, "."); - if (shadow != prev_shadow) { - prev_shadow = shadow; - fprintf(stderr, "%02x", (int)*(u8*)shadow); - } - } - fprintf(stderr, "\n"); -} - -TEST(AddressSanitizer, DISABLED_InternalPrintShadow) { - for (size_t size = 1; size <= 513; size++) { - char *ptr = new char[size]; - PrintShadow("m", (uptr)ptr, size); - delete [] ptr; - PrintShadow("f", (uptr)ptr, size); - } -} - -TEST(AddressSanitizer, QuarantineTest) { - BufferedStackTrace stack; - stack.trace_buffer[0] = 0x890; - stack.size = 1; - - const int size = 1024; - void *p = __asan::asan_malloc(size, &stack); - __asan::asan_free(p, &stack, __asan::FROM_MALLOC); - size_t i; - size_t max_i = 1 << 30; - for (i = 0; i < max_i; i++) { - void *p1 = __asan::asan_malloc(size, &stack); - __asan::asan_free(p1, &stack, __asan::FROM_MALLOC); - if (p1 == p) break; - } - EXPECT_GE(i, 10000U); - EXPECT_LT(i, max_i); -} - -void *ThreadedQuarantineTestWorker(void *unused) { - (void)unused; - u32 seed = my_rand(); - BufferedStackTrace stack; - stack.trace_buffer[0] = 0x890; - stack.size = 1; - - for (size_t i = 0; i < 1000; i++) { - void *p = __asan::asan_malloc(1 + (my_rand_r(&seed) % 4000), &stack); - __asan::asan_free(p, &stack, __asan::FROM_MALLOC); - } - return NULL; -} - -// Check that the thread local allocators are flushed when threads are -// destroyed. -TEST(AddressSanitizer, ThreadedQuarantineTest) { - const int n_threads = 3000; - size_t mmaped1 = __sanitizer_get_heap_size(); - for (int i = 0; i < n_threads; i++) { - pthread_t t; - PTHREAD_CREATE(&t, NULL, ThreadedQuarantineTestWorker, 0); - PTHREAD_JOIN(t, 0); - size_t mmaped2 = __sanitizer_get_heap_size(); - EXPECT_LT(mmaped2 - mmaped1, 320U * (1 << 20)); - } -} - -void *ThreadedOneSizeMallocStress(void *unused) { - (void)unused; - BufferedStackTrace stack; - stack.trace_buffer[0] = 0x890; - stack.size = 1; - const size_t kNumMallocs = 1000; - for (int iter = 0; iter < 1000; iter++) { - void *p[kNumMallocs]; - for (size_t i = 0; i < kNumMallocs; i++) { - p[i] = __asan::asan_malloc(32, &stack); - } - for (size_t i = 0; i < kNumMallocs; i++) { - __asan::asan_free(p[i], &stack, __asan::FROM_MALLOC); - } - } - return NULL; -} - -TEST(AddressSanitizer, ThreadedOneSizeMallocStressTest) { - const int kNumThreads = 4; - pthread_t t[kNumThreads]; - for (int i = 0; i < kNumThreads; i++) { - PTHREAD_CREATE(&t[i], 0, ThreadedOneSizeMallocStress, 0); - } - for (int i = 0; i < kNumThreads; i++) { - PTHREAD_JOIN(t[i], 0); - } -} - -TEST(AddressSanitizer, ShadowRegionIsPoisonedTest) { - using __asan::kHighMemEnd; - // Check that __asan_region_is_poisoned works for shadow regions. - uptr ptr = kLowShadowBeg + 200; - EXPECT_EQ(ptr, __asan_region_is_poisoned(ptr, 100)); - ptr = kShadowGapBeg + 200; - EXPECT_EQ(ptr, __asan_region_is_poisoned(ptr, 100)); - ptr = kHighShadowBeg + 200; - EXPECT_EQ(ptr, __asan_region_is_poisoned(ptr, 100)); -} - -// Test __asan_load1 & friends. -TEST(AddressSanitizer, LoadStoreCallbacks) { - typedef void (*CB)(uptr p); - CB cb[2][5] = { - { - __asan_load1, __asan_load2, __asan_load4, __asan_load8, __asan_load16, - }, { - __asan_store1, __asan_store2, __asan_store4, __asan_store8, - __asan_store16, - } - }; - - uptr buggy_ptr; - - __asan_test_only_reported_buggy_pointer = &buggy_ptr; - BufferedStackTrace stack; - stack.trace_buffer[0] = 0x890; - stack.size = 1; - - for (uptr len = 16; len <= 32; len++) { - char *ptr = (char*) __asan::asan_malloc(len, &stack); - uptr p = reinterpret_cast<uptr>(ptr); - for (uptr is_write = 0; is_write <= 1; is_write++) { - for (uptr size_log = 0; size_log <= 4; size_log++) { - uptr size = 1 << size_log; - CB call = cb[is_write][size_log]; - // Iterate only size-aligned offsets. - for (uptr offset = 0; offset <= len; offset += size) { - buggy_ptr = 0; - call(p + offset); - if (offset + size <= len) - EXPECT_EQ(buggy_ptr, 0U); - else - EXPECT_EQ(buggy_ptr, p + offset); - } - } - } - __asan::asan_free(ptr, &stack, __asan::FROM_MALLOC); - } - __asan_test_only_reported_buggy_pointer = 0; -} diff --git a/contrib/compiler-rt/lib/asan/tests/asan_oob_test.cc b/contrib/compiler-rt/lib/asan/tests/asan_oob_test.cc deleted file mode 100644 index 0c6bea285864..000000000000 --- a/contrib/compiler-rt/lib/asan/tests/asan_oob_test.cc +++ /dev/null @@ -1,128 +0,0 @@ -//===-- asan_oob_test.cc --------------------------------------------------===// -// -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. -// -//===----------------------------------------------------------------------===// -// -// This file is a part of AddressSanitizer, an address sanity checker. -// -//===----------------------------------------------------------------------===// -#include "asan_test_utils.h" - -NOINLINE void asan_write_sized_aligned(uint8_t *p, size_t size) { - EXPECT_EQ(0U, ((uintptr_t)p % size)); - if (size == 1) asan_write((uint8_t*)p); - else if (size == 2) asan_write((uint16_t*)p); - else if (size == 4) asan_write((uint32_t*)p); - else if (size == 8) asan_write((uint64_t*)p); -} - -template<typename T> -NOINLINE void oob_test(int size, int off) { - char *p = (char*)malloc_aaa(size); - // fprintf(stderr, "writing %d byte(s) into [%p,%p) with offset %d\n", - // sizeof(T), p, p + size, off); - asan_write((T*)(p + off)); - free_aaa(p); -} - -template<typename T> -void OOBTest() { - char expected_str[100]; - for (int size = sizeof(T); size < 20; size += 5) { - for (int i = -5; i < 0; i++) { - const char *str = - "is located.*%d byte.*to the left"; - sprintf(expected_str, str, abs(i)); - EXPECT_DEATH(oob_test<T>(size, i), expected_str); - } - - for (int i = 0; i < (int)(size - sizeof(T) + 1); i++) - oob_test<T>(size, i); - - for (int i = size - sizeof(T) + 1; i <= (int)(size + 2 * sizeof(T)); i++) { - const char *str = - "is located.*%d byte.*to the right"; - int off = i >= size ? (i - size) : 0; - // we don't catch unaligned partially OOB accesses. - if (i % sizeof(T)) continue; - sprintf(expected_str, str, off); - EXPECT_DEATH(oob_test<T>(size, i), expected_str); - } - } - - EXPECT_DEATH(oob_test<T>(kLargeMalloc, -1), - "is located.*1 byte.*to the left"); - EXPECT_DEATH(oob_test<T>(kLargeMalloc, kLargeMalloc), - "is located.*0 byte.*to the right"); -} - -// TODO(glider): the following tests are EXTREMELY slow on Darwin: -// AddressSanitizer.OOB_char (125503 ms) -// AddressSanitizer.OOB_int (126890 ms) -// AddressSanitizer.OOBRightTest (315605 ms) -// AddressSanitizer.SimpleStackTest (366559 ms) - -TEST(AddressSanitizer, OOB_char) { - OOBTest<U1>(); -} - -TEST(AddressSanitizer, OOB_int) { - OOBTest<U4>(); -} - -TEST(AddressSanitizer, OOBRightTest) { - size_t max_access_size = SANITIZER_WORDSIZE == 64 ? 8 : 4; - for (size_t access_size = 1; access_size <= max_access_size; - access_size *= 2) { - for (size_t alloc_size = 1; alloc_size <= 8; alloc_size++) { - for (size_t offset = 0; offset <= 8; offset += access_size) { - void *p = malloc(alloc_size); - // allocated: [p, p + alloc_size) - // accessed: [p + offset, p + offset + access_size) - uint8_t *addr = (uint8_t*)p + offset; - if (offset + access_size <= alloc_size) { - asan_write_sized_aligned(addr, access_size); - } else { - int outside_bytes = offset > alloc_size ? (offset - alloc_size) : 0; - const char *str = - "is located.%d *byte.*to the right"; - char expected_str[100]; - sprintf(expected_str, str, outside_bytes); - EXPECT_DEATH(asan_write_sized_aligned(addr, access_size), - expected_str); - } - free(p); - } - } - } -} - -TEST(AddressSanitizer, LargeOOBRightTest) { - size_t large_power_of_two = 1 << 19; - for (size_t i = 16; i <= 256; i *= 2) { - size_t size = large_power_of_two - i; - char *p = Ident(new char[size]); - EXPECT_DEATH(p[size] = 0, "is located 0 bytes to the right"); - delete [] p; - } -} - -TEST(AddressSanitizer, DISABLED_DemoOOBLeftLow) { - oob_test<U1>(10, -1); -} - -TEST(AddressSanitizer, DISABLED_DemoOOBLeftHigh) { - oob_test<U1>(kLargeMalloc, -1); -} - -TEST(AddressSanitizer, DISABLED_DemoOOBRightLow) { - oob_test<U1>(10, 10); -} - -TEST(AddressSanitizer, DISABLED_DemoOOBRightHigh) { - oob_test<U1>(kLargeMalloc, kLargeMalloc); -} diff --git a/contrib/compiler-rt/lib/asan/tests/asan_racy_double_free_test.cc b/contrib/compiler-rt/lib/asan/tests/asan_racy_double_free_test.cc deleted file mode 100644 index 23240e714d56..000000000000 --- a/contrib/compiler-rt/lib/asan/tests/asan_racy_double_free_test.cc +++ /dev/null @@ -1,32 +0,0 @@ -#include <pthread.h> -#include <stdlib.h> -#include <stdio.h> - -const int N = 1000; -void *x[N]; - -void *Thread1(void *unused) { - for (int i = 0; i < N; i++) { - fprintf(stderr, "%s %d\n", __func__, i); - free(x[i]); - } - return NULL; -} - -void *Thread2(void *unused) { - for (int i = 0; i < N; i++) { - fprintf(stderr, "%s %d\n", __func__, i); - free(x[i]); - } - return NULL; -} - -int main() { - for (int i = 0; i < N; i++) - x[i] = malloc(128); - pthread_t t[2]; - pthread_create(&t[0], 0, Thread1, 0); - pthread_create(&t[1], 0, Thread2, 0); - pthread_join(t[0], 0); - pthread_join(t[1], 0); -} diff --git a/contrib/compiler-rt/lib/asan/tests/asan_str_test.cc b/contrib/compiler-rt/lib/asan/tests/asan_str_test.cc deleted file mode 100644 index 89b0d3d2d0f9..000000000000 --- a/contrib/compiler-rt/lib/asan/tests/asan_str_test.cc +++ /dev/null @@ -1,573 +0,0 @@ -//=-- asan_str_test.cc ----------------------------------------------------===// -// -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. -// -//===----------------------------------------------------------------------===// -// -// This file is a part of AddressSanitizer, an address sanity checker. -// -//===----------------------------------------------------------------------===// -#include "asan_test_utils.h" - -#if defined(__APPLE__) -#include <AvailabilityMacros.h> // For MAC_OS_X_VERSION_* -#endif - -// Used for string functions tests -static char global_string[] = "global"; -static size_t global_string_length = 6; - -// Input to a test is a zero-terminated string str with given length -// Accesses to the bytes to the left and to the right of str -// are presumed to produce OOB errors -void StrLenOOBTestTemplate(char *str, size_t length, bool is_global) { - // Normal strlen calls - EXPECT_EQ(strlen(str), length); - if (length > 0) { - EXPECT_EQ(length - 1, strlen(str + 1)); - EXPECT_EQ(0U, strlen(str + length)); - } - // Arg of strlen is not malloced, OOB access - if (!is_global) { - // We don't insert RedZones to the left of global variables - EXPECT_DEATH(Ident(strlen(str - 1)), LeftOOBReadMessage(1)); - EXPECT_DEATH(Ident(strlen(str - 5)), LeftOOBReadMessage(5)); - } - EXPECT_DEATH(Ident(strlen(str + length + 1)), RightOOBReadMessage(0)); - // Overwrite terminator - str[length] = 'a'; - // String is not zero-terminated, strlen will lead to OOB access - EXPECT_DEATH(Ident(strlen(str)), RightOOBReadMessage(0)); - EXPECT_DEATH(Ident(strlen(str + length)), RightOOBReadMessage(0)); - // Restore terminator - str[length] = 0; -} -TEST(AddressSanitizer, StrLenOOBTest) { - // Check heap-allocated string - size_t length = Ident(10); - char *heap_string = Ident((char*)malloc(length + 1)); - char stack_string[10 + 1]; - break_optimization(&stack_string); - for (size_t i = 0; i < length; i++) { - heap_string[i] = 'a'; - stack_string[i] = 'b'; - } - heap_string[length] = 0; - stack_string[length] = 0; - StrLenOOBTestTemplate(heap_string, length, false); - // TODO(samsonov): Fix expected messages in StrLenOOBTestTemplate to - // make test for stack_string work. Or move it to output tests. - // StrLenOOBTestTemplate(stack_string, length, false); - StrLenOOBTestTemplate(global_string, global_string_length, true); - free(heap_string); -} - -TEST(AddressSanitizer, WcsLenTest) { - EXPECT_EQ(0U, wcslen(Ident(L""))); - size_t hello_len = 13; - size_t hello_size = (hello_len + 1) * sizeof(wchar_t); - EXPECT_EQ(hello_len, wcslen(Ident(L"Hello, World!"))); - wchar_t *heap_string = Ident((wchar_t*)malloc(hello_size)); - memcpy(heap_string, L"Hello, World!", hello_size); - EXPECT_EQ(hello_len, Ident(wcslen(heap_string))); - EXPECT_DEATH(Ident(wcslen(heap_string + 14)), RightOOBReadMessage(0)); - free(heap_string); -} - -#if SANITIZER_TEST_HAS_STRNLEN -TEST(AddressSanitizer, StrNLenOOBTest) { - size_t size = Ident(123); - char *str = MallocAndMemsetString(size); - // Normal strnlen calls. - Ident(strnlen(str - 1, 0)); - Ident(strnlen(str, size)); - Ident(strnlen(str + size - 1, 1)); - str[size - 1] = '\0'; - Ident(strnlen(str, 2 * size)); - // Argument points to not allocated memory. - EXPECT_DEATH(Ident(strnlen(str - 1, 1)), LeftOOBReadMessage(1)); - EXPECT_DEATH(Ident(strnlen(str + size, 1)), RightOOBReadMessage(0)); - // Overwrite the terminating '\0' and hit unallocated memory. - str[size - 1] = 'z'; - EXPECT_DEATH(Ident(strnlen(str, size + 1)), RightOOBReadMessage(0)); - free(str); -} -#endif // SANITIZER_TEST_HAS_STRNLEN - -TEST(AddressSanitizer, StrDupOOBTest) { - size_t size = Ident(42); - char *str = MallocAndMemsetString(size); - char *new_str; - // Normal strdup calls. - str[size - 1] = '\0'; - new_str = strdup(str); - free(new_str); - new_str = strdup(str + size - 1); - free(new_str); - // Argument points to not allocated memory. - EXPECT_DEATH(Ident(strdup(str - 1)), LeftOOBReadMessage(1)); - EXPECT_DEATH(Ident(strdup(str + size)), RightOOBReadMessage(0)); - // Overwrite the terminating '\0' and hit unallocated memory. - str[size - 1] = 'z'; - EXPECT_DEATH(Ident(strdup(str)), RightOOBReadMessage(0)); - free(str); -} - -TEST(AddressSanitizer, StrCpyOOBTest) { - size_t to_size = Ident(30); - size_t from_size = Ident(6); // less than to_size - char *to = Ident((char*)malloc(to_size)); - char *from = Ident((char*)malloc(from_size)); - // Normal strcpy calls. - strcpy(from, "hello"); - strcpy(to, from); - strcpy(to + to_size - from_size, from); - // Length of "from" is too small. - EXPECT_DEATH(Ident(strcpy(from, "hello2")), RightOOBWriteMessage(0)); - // "to" or "from" points to not allocated memory. - EXPECT_DEATH(Ident(strcpy(to - 1, from)), LeftOOBWriteMessage(1)); - EXPECT_DEATH(Ident(strcpy(to, from - 1)), LeftOOBReadMessage(1)); - EXPECT_DEATH(Ident(strcpy(to, from + from_size)), RightOOBReadMessage(0)); - EXPECT_DEATH(Ident(strcpy(to + to_size, from)), RightOOBWriteMessage(0)); - // Overwrite the terminating '\0' character and hit unallocated memory. - from[from_size - 1] = '!'; - EXPECT_DEATH(Ident(strcpy(to, from)), RightOOBReadMessage(0)); - free(to); - free(from); -} - -TEST(AddressSanitizer, StrNCpyOOBTest) { - size_t to_size = Ident(20); - size_t from_size = Ident(6); // less than to_size - char *to = Ident((char*)malloc(to_size)); - // From is a zero-terminated string "hello\0" of length 6 - char *from = Ident((char*)malloc(from_size)); - strcpy(from, "hello"); - // copy 0 bytes - strncpy(to, from, 0); - strncpy(to - 1, from - 1, 0); - // normal strncpy calls - strncpy(to, from, from_size); - strncpy(to, from, to_size); - strncpy(to, from + from_size - 1, to_size); - strncpy(to + to_size - 1, from, 1); - // One of {to, from} points to not allocated memory - EXPECT_DEATH(Ident(strncpy(to, from - 1, from_size)), - LeftOOBReadMessage(1)); - EXPECT_DEATH(Ident(strncpy(to - 1, from, from_size)), - LeftOOBWriteMessage(1)); - EXPECT_DEATH(Ident(strncpy(to, from + from_size, 1)), - RightOOBReadMessage(0)); - EXPECT_DEATH(Ident(strncpy(to + to_size, from, 1)), - RightOOBWriteMessage(0)); - // Length of "to" is too small - EXPECT_DEATH(Ident(strncpy(to + to_size - from_size + 1, from, from_size)), - RightOOBWriteMessage(0)); - EXPECT_DEATH(Ident(strncpy(to + 1, from, to_size)), - RightOOBWriteMessage(0)); - // Overwrite terminator in from - from[from_size - 1] = '!'; - // normal strncpy call - strncpy(to, from, from_size); - // Length of "from" is too small - EXPECT_DEATH(Ident(strncpy(to, from, to_size)), - RightOOBReadMessage(0)); - free(to); - free(from); -} - -// Users may have different definitions of "strchr" and "index", so provide -// function pointer typedefs and overload RunStrChrTest implementation. -// We can't use macro for RunStrChrTest body here, as this macro would -// confuse EXPECT_DEATH gtest macro. -typedef char*(*PointerToStrChr1)(const char*, int); -typedef char*(*PointerToStrChr2)(char*, int); - -UNUSED static void RunStrChrTest(PointerToStrChr1 StrChr) { - size_t size = Ident(100); - char *str = MallocAndMemsetString(size); - str[10] = 'q'; - str[11] = '\0'; - EXPECT_EQ(str, StrChr(str, 'z')); - EXPECT_EQ(str + 10, StrChr(str, 'q')); - EXPECT_EQ(NULL, StrChr(str, 'a')); - // StrChr argument points to not allocated memory. - EXPECT_DEATH(Ident(StrChr(str - 1, 'z')), LeftOOBReadMessage(1)); - EXPECT_DEATH(Ident(StrChr(str + size, 'z')), RightOOBReadMessage(0)); - // Overwrite the terminator and hit not allocated memory. - str[11] = 'z'; - EXPECT_DEATH(Ident(StrChr(str, 'a')), RightOOBReadMessage(0)); - free(str); -} -UNUSED static void RunStrChrTest(PointerToStrChr2 StrChr) { - size_t size = Ident(100); - char *str = MallocAndMemsetString(size); - str[10] = 'q'; - str[11] = '\0'; - EXPECT_EQ(str, StrChr(str, 'z')); - EXPECT_EQ(str + 10, StrChr(str, 'q')); - EXPECT_EQ(NULL, StrChr(str, 'a')); - // StrChr argument points to not allocated memory. - EXPECT_DEATH(Ident(StrChr(str - 1, 'z')), LeftOOBReadMessage(1)); - EXPECT_DEATH(Ident(StrChr(str + size, 'z')), RightOOBReadMessage(0)); - // Overwrite the terminator and hit not allocated memory. - str[11] = 'z'; - EXPECT_DEATH(Ident(StrChr(str, 'a')), RightOOBReadMessage(0)); - free(str); -} - -TEST(AddressSanitizer, StrChrAndIndexOOBTest) { - RunStrChrTest(&strchr); -// No index() on Windows and on Android L. -#if !defined(_WIN32) && !defined(__ANDROID__) - RunStrChrTest(&index); -#endif -} - -TEST(AddressSanitizer, StrCmpAndFriendsLogicTest) { - // strcmp - EXPECT_EQ(0, strcmp("", "")); - EXPECT_EQ(0, strcmp("abcd", "abcd")); - EXPECT_GT(0, strcmp("ab", "ac")); - EXPECT_GT(0, strcmp("abc", "abcd")); - EXPECT_LT(0, strcmp("acc", "abc")); - EXPECT_LT(0, strcmp("abcd", "abc")); - - // strncmp - EXPECT_EQ(0, strncmp("a", "b", 0)); - EXPECT_EQ(0, strncmp("abcd", "abcd", 10)); - EXPECT_EQ(0, strncmp("abcd", "abcef", 3)); - EXPECT_GT(0, strncmp("abcde", "abcfa", 4)); - EXPECT_GT(0, strncmp("a", "b", 5)); - EXPECT_GT(0, strncmp("bc", "bcde", 4)); - EXPECT_LT(0, strncmp("xyz", "xyy", 10)); - EXPECT_LT(0, strncmp("baa", "aaa", 1)); - EXPECT_LT(0, strncmp("zyx", "", 2)); - -#if !defined(_WIN32) // no str[n]casecmp on Windows. - // strcasecmp - EXPECT_EQ(0, strcasecmp("", "")); - EXPECT_EQ(0, strcasecmp("zzz", "zzz")); - EXPECT_EQ(0, strcasecmp("abCD", "ABcd")); - EXPECT_GT(0, strcasecmp("aB", "Ac")); - EXPECT_GT(0, strcasecmp("ABC", "ABCd")); - EXPECT_LT(0, strcasecmp("acc", "abc")); - EXPECT_LT(0, strcasecmp("ABCd", "abc")); - - // strncasecmp - EXPECT_EQ(0, strncasecmp("a", "b", 0)); - EXPECT_EQ(0, strncasecmp("abCD", "ABcd", 10)); - EXPECT_EQ(0, strncasecmp("abCd", "ABcef", 3)); - EXPECT_GT(0, strncasecmp("abcde", "ABCfa", 4)); - EXPECT_GT(0, strncasecmp("a", "B", 5)); - EXPECT_GT(0, strncasecmp("bc", "BCde", 4)); - EXPECT_LT(0, strncasecmp("xyz", "xyy", 10)); - EXPECT_LT(0, strncasecmp("Baa", "aaa", 1)); - EXPECT_LT(0, strncasecmp("zyx", "", 2)); -#endif - - // memcmp - EXPECT_EQ(0, memcmp("a", "b", 0)); - EXPECT_EQ(0, memcmp("ab\0c", "ab\0c", 4)); - EXPECT_GT(0, memcmp("\0ab", "\0ac", 3)); - EXPECT_GT(0, memcmp("abb\0", "abba", 4)); - EXPECT_LT(0, memcmp("ab\0cd", "ab\0c\0", 5)); - EXPECT_LT(0, memcmp("zza", "zyx", 3)); -} - -typedef int(*PointerToStrCmp)(const char*, const char*); -void RunStrCmpTest(PointerToStrCmp StrCmp) { - size_t size = Ident(100); - int fill = 'o'; - char *s1 = MallocAndMemsetString(size, fill); - char *s2 = MallocAndMemsetString(size, fill); - s1[size - 1] = '\0'; - s2[size - 1] = '\0'; - // Normal StrCmp calls - Ident(StrCmp(s1, s2)); - Ident(StrCmp(s1, s2 + size - 1)); - Ident(StrCmp(s1 + size - 1, s2 + size - 1)); - // One of arguments points to not allocated memory. - EXPECT_DEATH(Ident(StrCmp)(s1 - 1, s2), LeftOOBReadMessage(1)); - EXPECT_DEATH(Ident(StrCmp)(s1, s2 - 1), LeftOOBReadMessage(1)); - EXPECT_DEATH(Ident(StrCmp)(s1 + size, s2), RightOOBReadMessage(0)); - EXPECT_DEATH(Ident(StrCmp)(s1, s2 + size), RightOOBReadMessage(0)); - // Hit unallocated memory and die. - s1[size - 1] = fill; - EXPECT_DEATH(Ident(StrCmp)(s1, s1), RightOOBReadMessage(0)); - EXPECT_DEATH(Ident(StrCmp)(s1 + size - 1, s2), RightOOBReadMessage(0)); - free(s1); - free(s2); -} - -TEST(AddressSanitizer, StrCmpOOBTest) { - RunStrCmpTest(&strcmp); -} - -#if !defined(_WIN32) // no str[n]casecmp on Windows. -TEST(AddressSanitizer, StrCaseCmpOOBTest) { - RunStrCmpTest(&strcasecmp); -} -#endif - -typedef int(*PointerToStrNCmp)(const char*, const char*, size_t); -void RunStrNCmpTest(PointerToStrNCmp StrNCmp) { - size_t size = Ident(100); - char *s1 = MallocAndMemsetString(size); - char *s2 = MallocAndMemsetString(size); - s1[size - 1] = '\0'; - s2[size - 1] = '\0'; - // Normal StrNCmp calls - Ident(StrNCmp(s1, s2, size + 2)); - s1[size - 1] = 'z'; - s2[size - 1] = 'x'; - Ident(StrNCmp(s1 + size - 2, s2 + size - 2, size)); - s2[size - 1] = 'z'; - Ident(StrNCmp(s1 - 1, s2 - 1, 0)); - Ident(StrNCmp(s1 + size - 1, s2 + size - 1, 1)); - // One of arguments points to not allocated memory. - EXPECT_DEATH(Ident(StrNCmp)(s1 - 1, s2, 1), LeftOOBReadMessage(1)); - EXPECT_DEATH(Ident(StrNCmp)(s1, s2 - 1, 1), LeftOOBReadMessage(1)); - EXPECT_DEATH(Ident(StrNCmp)(s1 + size, s2, 1), RightOOBReadMessage(0)); - EXPECT_DEATH(Ident(StrNCmp)(s1, s2 + size, 1), RightOOBReadMessage(0)); - // Hit unallocated memory and die. - EXPECT_DEATH(Ident(StrNCmp)(s1 + 1, s2 + 1, size), RightOOBReadMessage(0)); - EXPECT_DEATH(Ident(StrNCmp)(s1 + size - 1, s2, 2), RightOOBReadMessage(0)); - free(s1); - free(s2); -} - -TEST(AddressSanitizer, StrNCmpOOBTest) { - RunStrNCmpTest(&strncmp); -} - -#if !defined(_WIN32) // no str[n]casecmp on Windows. -TEST(AddressSanitizer, StrNCaseCmpOOBTest) { - RunStrNCmpTest(&strncasecmp); -} -#endif - -TEST(AddressSanitizer, StrCatOOBTest) { - // strcat() reads strlen(to) bytes from |to| before concatenating. - size_t to_size = Ident(100); - char *to = MallocAndMemsetString(to_size); - to[0] = '\0'; - size_t from_size = Ident(20); - char *from = MallocAndMemsetString(from_size); - from[from_size - 1] = '\0'; - // Normal strcat calls. - strcat(to, from); - strcat(to, from); - strcat(to + from_size, from + from_size - 2); - // Passing an invalid pointer is an error even when concatenating an empty - // string. - EXPECT_DEATH(strcat(to - 1, from + from_size - 1), LeftOOBAccessMessage(1)); - // One of arguments points to not allocated memory. - EXPECT_DEATH(strcat(to - 1, from), LeftOOBAccessMessage(1)); - EXPECT_DEATH(strcat(to, from - 1), LeftOOBReadMessage(1)); - EXPECT_DEATH(strcat(to, from + from_size), RightOOBReadMessage(0)); - - // "from" is not zero-terminated. - from[from_size - 1] = 'z'; - EXPECT_DEATH(strcat(to, from), RightOOBReadMessage(0)); - from[from_size - 1] = '\0'; - // "to" is too short to fit "from". - memset(to, 'z', to_size); - to[to_size - from_size + 1] = '\0'; - EXPECT_DEATH(strcat(to, from), RightOOBWriteMessage(0)); - // length of "to" is just enough. - strcat(to, from + 1); - - free(to); - free(from); -} - -TEST(AddressSanitizer, StrNCatOOBTest) { - // strncat() reads strlen(to) bytes from |to| before concatenating. - size_t to_size = Ident(100); - char *to = MallocAndMemsetString(to_size); - to[0] = '\0'; - size_t from_size = Ident(20); - char *from = MallocAndMemsetString(from_size); - // Normal strncat calls. - strncat(to, from, 0); - strncat(to, from, from_size); - from[from_size - 1] = '\0'; - strncat(to, from, 2 * from_size); - // Catenating empty string with an invalid string is still an error. - EXPECT_DEATH(strncat(to - 1, from, 0), LeftOOBAccessMessage(1)); - strncat(to, from + from_size - 1, 10); - // One of arguments points to not allocated memory. - EXPECT_DEATH(strncat(to - 1, from, 2), LeftOOBAccessMessage(1)); - EXPECT_DEATH(strncat(to, from - 1, 2), LeftOOBReadMessage(1)); - EXPECT_DEATH(strncat(to, from + from_size, 2), RightOOBReadMessage(0)); - - memset(from, 'z', from_size); - memset(to, 'z', to_size); - to[0] = '\0'; - // "from" is too short. - EXPECT_DEATH(strncat(to, from, from_size + 1), RightOOBReadMessage(0)); - // "to" is too short to fit "from". - to[0] = 'z'; - to[to_size - from_size + 1] = '\0'; - EXPECT_DEATH(strncat(to, from, from_size - 1), RightOOBWriteMessage(0)); - // "to" is just enough. - strncat(to, from, from_size - 2); - - free(to); - free(from); -} - -static string OverlapErrorMessage(const string &func) { - return func + "-param-overlap"; -} - -TEST(AddressSanitizer, StrArgsOverlapTest) { - size_t size = Ident(100); - char *str = Ident((char*)malloc(size)); - -// Do not check memcpy() on OS X 10.7 and later, where it actually aliases -// memmove(). -#if !defined(__APPLE__) || !defined(MAC_OS_X_VERSION_10_7) || \ - (MAC_OS_X_VERSION_MAX_ALLOWED < MAC_OS_X_VERSION_10_7) - // Check "memcpy". Use Ident() to avoid inlining. - memset(str, 'z', size); - Ident(memcpy)(str + 1, str + 11, 10); - Ident(memcpy)(str, str, 0); - EXPECT_DEATH(Ident(memcpy)(str, str + 14, 15), OverlapErrorMessage("memcpy")); - EXPECT_DEATH(Ident(memcpy)(str + 14, str, 15), OverlapErrorMessage("memcpy")); -#endif - - // We do not treat memcpy with to==from as a bug. - // See http://llvm.org/bugs/show_bug.cgi?id=11763. - // EXPECT_DEATH(Ident(memcpy)(str + 20, str + 20, 1), - // OverlapErrorMessage("memcpy")); - - // Check "strcpy". - memset(str, 'z', size); - str[9] = '\0'; - strcpy(str + 10, str); - EXPECT_DEATH(strcpy(str + 9, str), OverlapErrorMessage("strcpy")); - EXPECT_DEATH(strcpy(str, str + 4), OverlapErrorMessage("strcpy")); - strcpy(str, str + 5); - - // Check "strncpy". - memset(str, 'z', size); - strncpy(str, str + 10, 10); - EXPECT_DEATH(strncpy(str, str + 9, 10), OverlapErrorMessage("strncpy")); - EXPECT_DEATH(strncpy(str + 9, str, 10), OverlapErrorMessage("strncpy")); - str[10] = '\0'; - strncpy(str + 11, str, 20); - EXPECT_DEATH(strncpy(str + 10, str, 20), OverlapErrorMessage("strncpy")); - - // Check "strcat". - memset(str, 'z', size); - str[10] = '\0'; - str[20] = '\0'; - strcat(str, str + 10); - EXPECT_DEATH(strcat(str, str + 11), OverlapErrorMessage("strcat")); - str[10] = '\0'; - strcat(str + 11, str); - EXPECT_DEATH(strcat(str, str + 9), OverlapErrorMessage("strcat")); - EXPECT_DEATH(strcat(str + 9, str), OverlapErrorMessage("strcat")); - EXPECT_DEATH(strcat(str + 10, str), OverlapErrorMessage("strcat")); - - // Check "strncat". - memset(str, 'z', size); - str[10] = '\0'; - strncat(str, str + 10, 10); // from is empty - EXPECT_DEATH(strncat(str, str + 11, 10), OverlapErrorMessage("strncat")); - str[10] = '\0'; - str[20] = '\0'; - strncat(str + 5, str, 5); - str[10] = '\0'; - EXPECT_DEATH(strncat(str + 5, str, 6), OverlapErrorMessage("strncat")); - EXPECT_DEATH(strncat(str, str + 9, 10), OverlapErrorMessage("strncat")); - - free(str); -} - -typedef void(*PointerToCallAtoi)(const char*); - -void RunAtoiOOBTest(PointerToCallAtoi Atoi) { - char *array = MallocAndMemsetString(10, '1'); - // Invalid pointer to the string. - EXPECT_DEATH(Atoi(array + 11), RightOOBReadMessage(1)); - EXPECT_DEATH(Atoi(array - 1), LeftOOBReadMessage(1)); - // Die if a buffer doesn't have terminating NULL. - EXPECT_DEATH(Atoi(array), RightOOBReadMessage(0)); - // Make last symbol a terminating NULL - array[9] = '\0'; - Atoi(array); - // Sometimes we need to detect overflow if no digits are found. - memset(array, ' ', 10); - EXPECT_DEATH(Atoi(array), RightOOBReadMessage(0)); - array[9] = '-'; - EXPECT_DEATH(Atoi(array), RightOOBReadMessage(0)); - EXPECT_DEATH(Atoi(array + 9), RightOOBReadMessage(0)); - free(array); -} - -#if !defined(_WIN32) // FIXME: Fix and enable on Windows. -void CallAtoi(const char *nptr) { - Ident(atoi(nptr)); -} -void CallAtol(const char *nptr) { - Ident(atol(nptr)); -} -void CallAtoll(const char *nptr) { - Ident(atoll(nptr)); -} -TEST(AddressSanitizer, AtoiAndFriendsOOBTest) { - RunAtoiOOBTest(&CallAtoi); - RunAtoiOOBTest(&CallAtol); - RunAtoiOOBTest(&CallAtoll); -} -#endif - -typedef void(*PointerToCallStrtol)(const char*, char**, int); - -void RunStrtolOOBTest(PointerToCallStrtol Strtol) { - char *array = MallocAndMemsetString(3); - array[0] = '1'; - array[1] = '2'; - array[2] = '3'; - // Invalid pointer to the string. - EXPECT_DEATH(Strtol(array + 3, NULL, 0), RightOOBReadMessage(0)); - EXPECT_DEATH(Strtol(array - 1, NULL, 0), LeftOOBReadMessage(1)); - // Buffer overflow if there is no terminating null (depends on base). - EXPECT_DEATH(Strtol(array, NULL, 0), RightOOBReadMessage(0)); - array[2] = 'z'; - EXPECT_DEATH(Strtol(array, NULL, 36), RightOOBReadMessage(0)); - // Add terminating zero to get rid of overflow. - array[2] = '\0'; - Strtol(array, NULL, 36); - // Sometimes we need to detect overflow if no digits are found. - array[0] = array[1] = array[2] = ' '; - EXPECT_DEATH(Strtol(array, NULL, 0), RightOOBReadMessage(0)); - array[2] = '+'; - EXPECT_DEATH(Strtol(array, NULL, 0), RightOOBReadMessage(0)); - array[2] = '-'; - EXPECT_DEATH(Strtol(array, NULL, 0), RightOOBReadMessage(0)); - free(array); -} - -#if !defined(_WIN32) // FIXME: Fix and enable on Windows. -void CallStrtol(const char *nptr, char **endptr, int base) { - Ident(strtol(nptr, endptr, base)); -} -void CallStrtoll(const char *nptr, char **endptr, int base) { - Ident(strtoll(nptr, endptr, base)); -} -TEST(AddressSanitizer, StrtollOOBTest) { - RunStrtolOOBTest(&CallStrtoll); -} -TEST(AddressSanitizer, StrtolOOBTest) { - RunStrtolOOBTest(&CallStrtol); -} -#endif - - diff --git a/contrib/compiler-rt/lib/asan/tests/asan_test.cc b/contrib/compiler-rt/lib/asan/tests/asan_test.cc deleted file mode 100644 index 07d59e09a72f..000000000000 --- a/contrib/compiler-rt/lib/asan/tests/asan_test.cc +++ /dev/null @@ -1,1319 +0,0 @@ -//===-- asan_test.cc ------------------------------------------------------===// -// -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. -// -//===----------------------------------------------------------------------===// -// -// This file is a part of AddressSanitizer, an address sanity checker. -// -//===----------------------------------------------------------------------===// -#include "asan_test_utils.h" - -NOINLINE void *malloc_fff(size_t size) { - void *res = malloc/**/(size); break_optimization(0); return res;} -NOINLINE void *malloc_eee(size_t size) { - void *res = malloc_fff(size); break_optimization(0); return res;} -NOINLINE void *malloc_ddd(size_t size) { - void *res = malloc_eee(size); break_optimization(0); return res;} -NOINLINE void *malloc_ccc(size_t size) { - void *res = malloc_ddd(size); break_optimization(0); return res;} -NOINLINE void *malloc_bbb(size_t size) { - void *res = malloc_ccc(size); break_optimization(0); return res;} -NOINLINE void *malloc_aaa(size_t size) { - void *res = malloc_bbb(size); break_optimization(0); return res;} - -NOINLINE void free_ccc(void *p) { free(p); break_optimization(0);} -NOINLINE void free_bbb(void *p) { free_ccc(p); break_optimization(0);} -NOINLINE void free_aaa(void *p) { free_bbb(p); break_optimization(0);} - -template<typename T> -NOINLINE void uaf_test(int size, int off) { - void *p = malloc_aaa(size); - free_aaa(p); - for (int i = 1; i < 100; i++) - free_aaa(malloc_aaa(i)); - fprintf(stderr, "writing %ld byte(s) at %p with offset %d\n", - (long)sizeof(T), p, off); - asan_write((T *)((char *)p + off)); -} - -TEST(AddressSanitizer, HasFeatureAddressSanitizerTest) { -#if defined(__has_feature) && __has_feature(address_sanitizer) - bool asan = 1; -#elif defined(__SANITIZE_ADDRESS__) - bool asan = 1; -#else - bool asan = 0; -#endif - EXPECT_EQ(true, asan); -} - -TEST(AddressSanitizer, SimpleDeathTest) { - EXPECT_DEATH(exit(1), ""); -} - -TEST(AddressSanitizer, VariousMallocsTest) { - int *a = (int*)malloc(100 * sizeof(int)); - a[50] = 0; - free(a); - - int *r = (int*)malloc(10); - r = (int*)realloc(r, 2000 * sizeof(int)); - r[1000] = 0; - free(r); - - int *b = new int[100]; - b[50] = 0; - delete [] b; - - int *c = new int; - *c = 0; - delete c; - -#if SANITIZER_TEST_HAS_POSIX_MEMALIGN - int *pm; - int pm_res = posix_memalign((void**)&pm, kPageSize, kPageSize); - EXPECT_EQ(0, pm_res); - free(pm); -#endif // SANITIZER_TEST_HAS_POSIX_MEMALIGN - -#if SANITIZER_TEST_HAS_MEMALIGN - int *ma = (int*)memalign(kPageSize, kPageSize); - EXPECT_EQ(0U, (uintptr_t)ma % kPageSize); - ma[123] = 0; - free(ma); -#endif // SANITIZER_TEST_HAS_MEMALIGN -} - -TEST(AddressSanitizer, CallocTest) { - int *a = (int*)calloc(100, sizeof(int)); - EXPECT_EQ(0, a[10]); - free(a); -} - -TEST(AddressSanitizer, CallocReturnsZeroMem) { - size_t sizes[] = {16, 1000, 10000, 100000, 2100000}; - for (size_t s = 0; s < sizeof(sizes)/sizeof(sizes[0]); s++) { - size_t size = sizes[s]; - for (size_t iter = 0; iter < 5; iter++) { - char *x = Ident((char*)calloc(1, size)); - EXPECT_EQ(x[0], 0); - EXPECT_EQ(x[size - 1], 0); - EXPECT_EQ(x[size / 2], 0); - EXPECT_EQ(x[size / 3], 0); - EXPECT_EQ(x[size / 4], 0); - memset(x, 0x42, size); - free(Ident(x)); -#if !defined(_WIN32) - // FIXME: OOM on Windows. We should just make this a lit test - // with quarantine size set to 1. - free(Ident(malloc(Ident(1 << 27)))); // Try to drain the quarantine. -#endif - } - } -} - -// No valloc on Windows or Android. -#if !defined(_WIN32) && !defined(__ANDROID__) -TEST(AddressSanitizer, VallocTest) { - void *a = valloc(100); - EXPECT_EQ(0U, (uintptr_t)a % kPageSize); - free(a); -} -#endif - -#if SANITIZER_TEST_HAS_PVALLOC -TEST(AddressSanitizer, PvallocTest) { - char *a = (char*)pvalloc(kPageSize + 100); - EXPECT_EQ(0U, (uintptr_t)a % kPageSize); - a[kPageSize + 101] = 1; // we should not report an error here. - free(a); - - a = (char*)pvalloc(0); // pvalloc(0) should allocate at least one page. - EXPECT_EQ(0U, (uintptr_t)a % kPageSize); - a[101] = 1; // we should not report an error here. - free(a); -} -#endif // SANITIZER_TEST_HAS_PVALLOC - -#if !defined(_WIN32) -// FIXME: Use an equivalent of pthread_setspecific on Windows. -void *TSDWorker(void *test_key) { - if (test_key) { - pthread_setspecific(*(pthread_key_t*)test_key, (void*)0xfeedface); - } - return NULL; -} - -void TSDDestructor(void *tsd) { - // Spawning a thread will check that the current thread id is not -1. - pthread_t th; - PTHREAD_CREATE(&th, NULL, TSDWorker, NULL); - PTHREAD_JOIN(th, NULL); -} - -// This tests triggers the thread-specific data destruction fiasco which occurs -// if we don't manage the TSD destructors ourselves. We create a new pthread -// key with a non-NULL destructor which is likely to be put after the destructor -// of AsanThread in the list of destructors. -// In this case the TSD for AsanThread will be destroyed before TSDDestructor -// is called for the child thread, and a CHECK will fail when we call -// pthread_create() to spawn the grandchild. -TEST(AddressSanitizer, DISABLED_TSDTest) { - pthread_t th; - pthread_key_t test_key; - pthread_key_create(&test_key, TSDDestructor); - PTHREAD_CREATE(&th, NULL, TSDWorker, &test_key); - PTHREAD_JOIN(th, NULL); - pthread_key_delete(test_key); -} -#endif - -TEST(AddressSanitizer, UAF_char) { - const char *uaf_string = "AddressSanitizer:.*heap-use-after-free"; - EXPECT_DEATH(uaf_test<U1>(1, 0), uaf_string); - EXPECT_DEATH(uaf_test<U1>(10, 0), uaf_string); - EXPECT_DEATH(uaf_test<U1>(10, 10), uaf_string); - EXPECT_DEATH(uaf_test<U1>(kLargeMalloc, 0), uaf_string); - EXPECT_DEATH(uaf_test<U1>(kLargeMalloc, kLargeMalloc / 2), uaf_string); -} - -TEST(AddressSanitizer, UAF_long_double) { - if (sizeof(long double) == sizeof(double)) return; - long double *p = Ident(new long double[10]); - EXPECT_DEATH(Ident(p)[12] = 0, "WRITE of size 1[026]"); - EXPECT_DEATH(Ident(p)[0] = Ident(p)[12], "READ of size 1[026]"); - delete [] Ident(p); -} - -#if !defined(_WIN32) -struct Packed5 { - int x; - char c; -} __attribute__((packed)); -#else -# pragma pack(push, 1) -struct Packed5 { - int x; - char c; -}; -# pragma pack(pop) -#endif - -TEST(AddressSanitizer, UAF_Packed5) { - static_assert(sizeof(Packed5) == 5, "Please check the keywords used"); - Packed5 *p = Ident(new Packed5[2]); - EXPECT_DEATH(p[0] = p[3], "READ of size 5"); - EXPECT_DEATH(p[3] = p[0], "WRITE of size 5"); - delete [] Ident(p); -} - -#if ASAN_HAS_BLACKLIST -TEST(AddressSanitizer, IgnoreTest) { - int *x = Ident(new int); - delete Ident(x); - *x = 0; -} -#endif // ASAN_HAS_BLACKLIST - -struct StructWithBitField { - int bf1:1; - int bf2:1; - int bf3:1; - int bf4:29; -}; - -TEST(AddressSanitizer, BitFieldPositiveTest) { - StructWithBitField *x = new StructWithBitField; - delete Ident(x); - EXPECT_DEATH(x->bf1 = 0, "use-after-free"); - EXPECT_DEATH(x->bf2 = 0, "use-after-free"); - EXPECT_DEATH(x->bf3 = 0, "use-after-free"); - EXPECT_DEATH(x->bf4 = 0, "use-after-free"); -} - -struct StructWithBitFields_8_24 { - int a:8; - int b:24; -}; - -TEST(AddressSanitizer, BitFieldNegativeTest) { - StructWithBitFields_8_24 *x = Ident(new StructWithBitFields_8_24); - x->a = 0; - x->b = 0; - delete Ident(x); -} - -#if ASAN_NEEDS_SEGV -namespace { - -const char kUnknownCrash[] = "AddressSanitizer: SEGV on unknown address"; -const char kOverriddenHandler[] = "ASan signal handler has been overridden\n"; - -TEST(AddressSanitizer, WildAddressTest) { - char *c = (char*)0x123; - EXPECT_DEATH(*c = 0, kUnknownCrash); -} - -void my_sigaction_sighandler(int, siginfo_t*, void*) { - fprintf(stderr, kOverriddenHandler); - exit(1); -} - -void my_signal_sighandler(int signum) { - fprintf(stderr, kOverriddenHandler); - exit(1); -} - -TEST(AddressSanitizer, SignalTest) { - struct sigaction sigact; - memset(&sigact, 0, sizeof(sigact)); - sigact.sa_sigaction = my_sigaction_sighandler; - sigact.sa_flags = SA_SIGINFO; - // ASan should silently ignore sigaction()... - EXPECT_EQ(0, sigaction(SIGSEGV, &sigact, 0)); -#ifdef __APPLE__ - EXPECT_EQ(0, sigaction(SIGBUS, &sigact, 0)); -#endif - char *c = (char*)0x123; - EXPECT_DEATH(*c = 0, kUnknownCrash); - // ... and signal(). - EXPECT_EQ(0, signal(SIGSEGV, my_signal_sighandler)); - EXPECT_DEATH(*c = 0, kUnknownCrash); -} -} // namespace -#endif - -static void TestLargeMalloc(size_t size) { - char buff[1024]; - sprintf(buff, "is located 1 bytes to the left of %lu-byte", (long)size); - EXPECT_DEATH(Ident((char*)malloc(size))[-1] = 0, buff); -} - -TEST(AddressSanitizer, LargeMallocTest) { - const int max_size = (SANITIZER_WORDSIZE == 32) ? 1 << 26 : 1 << 28; - for (int i = 113; i < max_size; i = i * 2 + 13) { - TestLargeMalloc(i); - } -} - -TEST(AddressSanitizer, HugeMallocTest) { - if (SANITIZER_WORDSIZE != 64 || ASAN_AVOID_EXPENSIVE_TESTS) return; - size_t n_megs = 4100; - EXPECT_DEATH(Ident((char*)malloc(n_megs << 20))[-1] = 0, - "is located 1 bytes to the left|" - "AddressSanitizer failed to allocate"); -} - -#if SANITIZER_TEST_HAS_MEMALIGN -void MemalignRun(size_t align, size_t size, int idx) { - char *p = (char *)memalign(align, size); - Ident(p)[idx] = 0; - free(p); -} - -TEST(AddressSanitizer, memalign) { - for (int align = 16; align <= (1 << 23); align *= 2) { - size_t size = align * 5; - EXPECT_DEATH(MemalignRun(align, size, -1), - "is located 1 bytes to the left"); - EXPECT_DEATH(MemalignRun(align, size, size + 1), - "is located 1 bytes to the right"); - } -} -#endif // SANITIZER_TEST_HAS_MEMALIGN - -void *ManyThreadsWorker(void *a) { - for (int iter = 0; iter < 100; iter++) { - for (size_t size = 100; size < 2000; size *= 2) { - free(Ident(malloc(size))); - } - } - return 0; -} - -TEST(AddressSanitizer, ManyThreadsTest) { - const size_t kNumThreads = - (SANITIZER_WORDSIZE == 32 || ASAN_AVOID_EXPENSIVE_TESTS) ? 30 : 1000; - pthread_t t[kNumThreads]; - for (size_t i = 0; i < kNumThreads; i++) { - PTHREAD_CREATE(&t[i], 0, ManyThreadsWorker, (void*)i); - } - for (size_t i = 0; i < kNumThreads; i++) { - PTHREAD_JOIN(t[i], 0); - } -} - -TEST(AddressSanitizer, ReallocTest) { - const int kMinElem = 5; - int *ptr = (int*)malloc(sizeof(int) * kMinElem); - ptr[3] = 3; - for (int i = 0; i < 10000; i++) { - ptr = (int*)realloc(ptr, - (my_rand() % 1000 + kMinElem) * sizeof(int)); - EXPECT_EQ(3, ptr[3]); - } - free(ptr); - // Realloc pointer returned by malloc(0). - int *ptr2 = Ident((int*)malloc(0)); - ptr2 = Ident((int*)realloc(ptr2, sizeof(*ptr2))); - *ptr2 = 42; - EXPECT_EQ(42, *ptr2); - free(ptr2); -} - -TEST(AddressSanitizer, ReallocFreedPointerTest) { - void *ptr = Ident(malloc(42)); - ASSERT_TRUE(NULL != ptr); - free(ptr); - EXPECT_DEATH(ptr = realloc(ptr, 77), "attempting double-free"); -} - -TEST(AddressSanitizer, ReallocInvalidPointerTest) { - void *ptr = Ident(malloc(42)); - EXPECT_DEATH(ptr = realloc((int*)ptr + 1, 77), "attempting free.*not malloc"); - free(ptr); -} - -TEST(AddressSanitizer, ZeroSizeMallocTest) { - // Test that malloc(0) and similar functions don't return NULL. - void *ptr = Ident(malloc(0)); - EXPECT_TRUE(NULL != ptr); - free(ptr); -#if SANITIZER_TEST_HAS_POSIX_MEMALIGN - int pm_res = posix_memalign(&ptr, 1<<20, 0); - EXPECT_EQ(0, pm_res); - EXPECT_TRUE(NULL != ptr); - free(ptr); -#endif // SANITIZER_TEST_HAS_POSIX_MEMALIGN - int *int_ptr = new int[0]; - int *int_ptr2 = new int[0]; - EXPECT_TRUE(NULL != int_ptr); - EXPECT_TRUE(NULL != int_ptr2); - EXPECT_NE(int_ptr, int_ptr2); - delete[] int_ptr; - delete[] int_ptr2; -} - -#if SANITIZER_TEST_HAS_MALLOC_USABLE_SIZE -static const char *kMallocUsableSizeErrorMsg = - "AddressSanitizer: attempting to call malloc_usable_size()"; - -TEST(AddressSanitizer, MallocUsableSizeTest) { - const size_t kArraySize = 100; - char *array = Ident((char*)malloc(kArraySize)); - int *int_ptr = Ident(new int); - EXPECT_EQ(0U, malloc_usable_size(NULL)); - EXPECT_EQ(kArraySize, malloc_usable_size(array)); - EXPECT_EQ(sizeof(int), malloc_usable_size(int_ptr)); - EXPECT_DEATH(malloc_usable_size((void*)0x123), kMallocUsableSizeErrorMsg); - EXPECT_DEATH(malloc_usable_size(array + kArraySize / 2), - kMallocUsableSizeErrorMsg); - free(array); - EXPECT_DEATH(malloc_usable_size(array), kMallocUsableSizeErrorMsg); - delete int_ptr; -} -#endif // SANITIZER_TEST_HAS_MALLOC_USABLE_SIZE - -void WrongFree() { - int *x = (int*)malloc(100 * sizeof(int)); - // Use the allocated memory, otherwise Clang will optimize it out. - Ident(x); - free(x + 1); -} - -#if !defined(_WIN32) // FIXME: This should be a lit test. -TEST(AddressSanitizer, WrongFreeTest) { - EXPECT_DEATH(WrongFree(), ASAN_PCRE_DOTALL - "ERROR: AddressSanitizer: attempting free.*not malloc" - ".*is located 4 bytes inside of 400-byte region" - ".*allocated by thread"); -} -#endif - -void DoubleFree() { - int *x = (int*)malloc(100 * sizeof(int)); - fprintf(stderr, "DoubleFree: x=%p\n", (void *)x); - free(x); - free(x); - fprintf(stderr, "should have failed in the second free(%p)\n", (void *)x); - abort(); -} - -#if !defined(_WIN32) // FIXME: This should be a lit test. -TEST(AddressSanitizer, DoubleFreeTest) { - EXPECT_DEATH(DoubleFree(), ASAN_PCRE_DOTALL - "ERROR: AddressSanitizer: attempting double-free" - ".*is located 0 bytes inside of 400-byte region" - ".*freed by thread T0 here" - ".*previously allocated by thread T0 here"); -} -#endif - -template<int kSize> -NOINLINE void SizedStackTest() { - char a[kSize]; - char *A = Ident((char*)&a); - const char *expected_death = "AddressSanitizer: stack-buffer-"; - for (size_t i = 0; i < kSize; i++) - A[i] = i; - EXPECT_DEATH(A[-1] = 0, expected_death); - EXPECT_DEATH(A[-5] = 0, expected_death); - EXPECT_DEATH(A[kSize] = 0, expected_death); - EXPECT_DEATH(A[kSize + 1] = 0, expected_death); - EXPECT_DEATH(A[kSize + 5] = 0, expected_death); - if (kSize > 16) - EXPECT_DEATH(A[kSize + 31] = 0, expected_death); -} - -TEST(AddressSanitizer, SimpleStackTest) { - SizedStackTest<1>(); - SizedStackTest<2>(); - SizedStackTest<3>(); - SizedStackTest<4>(); - SizedStackTest<5>(); - SizedStackTest<6>(); - SizedStackTest<7>(); - SizedStackTest<16>(); - SizedStackTest<25>(); - SizedStackTest<34>(); - SizedStackTest<43>(); - SizedStackTest<51>(); - SizedStackTest<62>(); - SizedStackTest<64>(); - SizedStackTest<128>(); -} - -#if !defined(_WIN32) -// FIXME: It's a bit hard to write multi-line death test expectations -// in a portable way. Anyways, this should just be turned into a lit test. -TEST(AddressSanitizer, ManyStackObjectsTest) { - char XXX[10]; - char YYY[20]; - char ZZZ[30]; - Ident(XXX); - Ident(YYY); - EXPECT_DEATH(Ident(ZZZ)[-1] = 0, ASAN_PCRE_DOTALL "XXX.*YYY.*ZZZ"); -} -#endif - -#if 0 // This test requires online symbolizer. -// Moved to lit_tests/stack-oob-frames.cc. -// Reenable here once we have online symbolizer by default. -NOINLINE static void Frame0(int frame, char *a, char *b, char *c) { - char d[4] = {0}; - char *D = Ident(d); - switch (frame) { - case 3: a[5]++; break; - case 2: b[5]++; break; - case 1: c[5]++; break; - case 0: D[5]++; break; - } -} -NOINLINE static void Frame1(int frame, char *a, char *b) { - char c[4] = {0}; Frame0(frame, a, b, c); - break_optimization(0); -} -NOINLINE static void Frame2(int frame, char *a) { - char b[4] = {0}; Frame1(frame, a, b); - break_optimization(0); -} -NOINLINE static void Frame3(int frame) { - char a[4] = {0}; Frame2(frame, a); - break_optimization(0); -} - -TEST(AddressSanitizer, GuiltyStackFrame0Test) { - EXPECT_DEATH(Frame3(0), "located .*in frame <.*Frame0"); -} -TEST(AddressSanitizer, GuiltyStackFrame1Test) { - EXPECT_DEATH(Frame3(1), "located .*in frame <.*Frame1"); -} -TEST(AddressSanitizer, GuiltyStackFrame2Test) { - EXPECT_DEATH(Frame3(2), "located .*in frame <.*Frame2"); -} -TEST(AddressSanitizer, GuiltyStackFrame3Test) { - EXPECT_DEATH(Frame3(3), "located .*in frame <.*Frame3"); -} -#endif - -NOINLINE void LongJmpFunc1(jmp_buf buf) { - // create three red zones for these two stack objects. - int a; - int b; - - int *A = Ident(&a); - int *B = Ident(&b); - *A = *B; - longjmp(buf, 1); -} - -NOINLINE void TouchStackFunc() { - int a[100]; // long array will intersect with redzones from LongJmpFunc1. - int *A = Ident(a); - for (int i = 0; i < 100; i++) - A[i] = i*i; -} - -// Test that we handle longjmp and do not report false positives on stack. -TEST(AddressSanitizer, LongJmpTest) { - static jmp_buf buf; - if (!setjmp(buf)) { - LongJmpFunc1(buf); - } else { - TouchStackFunc(); - } -} - -#if !defined(_WIN32) // Only basic longjmp is available on Windows. -NOINLINE void UnderscopeLongJmpFunc1(jmp_buf buf) { - // create three red zones for these two stack objects. - int a; - int b; - - int *A = Ident(&a); - int *B = Ident(&b); - *A = *B; - _longjmp(buf, 1); -} - -NOINLINE void SigLongJmpFunc1(sigjmp_buf buf) { - // create three red zones for these two stack objects. - int a; - int b; - - int *A = Ident(&a); - int *B = Ident(&b); - *A = *B; - siglongjmp(buf, 1); -} - -#if !defined(__ANDROID__) && !defined(__arm__) && \ - !defined(__powerpc64__) && !defined(__powerpc__) && \ - !defined(__aarch64__) && !defined(__mips__) && \ - !defined(__mips64) -NOINLINE void BuiltinLongJmpFunc1(jmp_buf buf) { - // create three red zones for these two stack objects. - int a; - int b; - - int *A = Ident(&a); - int *B = Ident(&b); - *A = *B; - __builtin_longjmp((void**)buf, 1); -} - -// Does not work on Power and ARM: -// https://code.google.com/p/address-sanitizer/issues/detail?id=185 -TEST(AddressSanitizer, BuiltinLongJmpTest) { - static jmp_buf buf; - if (!__builtin_setjmp((void**)buf)) { - BuiltinLongJmpFunc1(buf); - } else { - TouchStackFunc(); - } -} -#endif // !defined(__ANDROID__) && !defined(__powerpc64__) && - // !defined(__powerpc__) && !defined(__arm__) && - // !defined(__mips__) && !defined(__mips64) - -TEST(AddressSanitizer, UnderscopeLongJmpTest) { - static jmp_buf buf; - if (!_setjmp(buf)) { - UnderscopeLongJmpFunc1(buf); - } else { - TouchStackFunc(); - } -} - -TEST(AddressSanitizer, SigLongJmpTest) { - static sigjmp_buf buf; - if (!sigsetjmp(buf, 1)) { - SigLongJmpFunc1(buf); - } else { - TouchStackFunc(); - } -} -#endif - -// FIXME: Why does clang-cl define __EXCEPTIONS? -#if defined(__EXCEPTIONS) && !defined(_WIN32) -NOINLINE void ThrowFunc() { - // create three red zones for these two stack objects. - int a; - int b; - - int *A = Ident(&a); - int *B = Ident(&b); - *A = *B; - ASAN_THROW(1); -} - -TEST(AddressSanitizer, CxxExceptionTest) { - if (ASAN_UAR) return; - // TODO(kcc): this test crashes on 32-bit for some reason... - if (SANITIZER_WORDSIZE == 32) return; - try { - ThrowFunc(); - } catch(...) {} - TouchStackFunc(); -} -#endif - -void *ThreadStackReuseFunc1(void *unused) { - // create three red zones for these two stack objects. - int a; - int b; - - int *A = Ident(&a); - int *B = Ident(&b); - *A = *B; - pthread_exit(0); - return 0; -} - -void *ThreadStackReuseFunc2(void *unused) { - TouchStackFunc(); - return 0; -} - -TEST(AddressSanitizer, ThreadStackReuseTest) { - pthread_t t; - PTHREAD_CREATE(&t, 0, ThreadStackReuseFunc1, 0); - PTHREAD_JOIN(t, 0); - PTHREAD_CREATE(&t, 0, ThreadStackReuseFunc2, 0); - PTHREAD_JOIN(t, 0); -} - -#if defined(__i686__) || defined(__x86_64__) -#include <emmintrin.h> -TEST(AddressSanitizer, Store128Test) { - char *a = Ident((char*)malloc(Ident(12))); - char *p = a; - if (((uintptr_t)a % 16) != 0) - p = a + 8; - assert(((uintptr_t)p % 16) == 0); - __m128i value_wide = _mm_set1_epi16(0x1234); - EXPECT_DEATH(_mm_store_si128((__m128i*)p, value_wide), - "AddressSanitizer: heap-buffer-overflow"); - EXPECT_DEATH(_mm_store_si128((__m128i*)p, value_wide), - "WRITE of size 16"); - EXPECT_DEATH(_mm_store_si128((__m128i*)p, value_wide), - "located 0 bytes to the right of 12-byte"); - free(a); -} -#endif - -// FIXME: All tests that use this function should be turned into lit tests. -string RightOOBErrorMessage(int oob_distance, bool is_write) { - assert(oob_distance >= 0); - char expected_str[100]; - sprintf(expected_str, ASAN_PCRE_DOTALL -#if !GTEST_USES_SIMPLE_RE - "buffer-overflow.*%s.*" -#endif - "located %d bytes to the right", -#if !GTEST_USES_SIMPLE_RE - is_write ? "WRITE" : "READ", -#endif - oob_distance); - return string(expected_str); -} - -string RightOOBWriteMessage(int oob_distance) { - return RightOOBErrorMessage(oob_distance, /*is_write*/true); -} - -string RightOOBReadMessage(int oob_distance) { - return RightOOBErrorMessage(oob_distance, /*is_write*/false); -} - -// FIXME: All tests that use this function should be turned into lit tests. -string LeftOOBErrorMessage(int oob_distance, bool is_write) { - assert(oob_distance > 0); - char expected_str[100]; - sprintf(expected_str, -#if !GTEST_USES_SIMPLE_RE - ASAN_PCRE_DOTALL "%s.*" -#endif - "located %d bytes to the left", -#if !GTEST_USES_SIMPLE_RE - is_write ? "WRITE" : "READ", -#endif - oob_distance); - return string(expected_str); -} - -string LeftOOBWriteMessage(int oob_distance) { - return LeftOOBErrorMessage(oob_distance, /*is_write*/true); -} - -string LeftOOBReadMessage(int oob_distance) { - return LeftOOBErrorMessage(oob_distance, /*is_write*/false); -} - -string LeftOOBAccessMessage(int oob_distance) { - assert(oob_distance > 0); - char expected_str[100]; - sprintf(expected_str, "located %d bytes to the left", oob_distance); - return string(expected_str); -} - -char* MallocAndMemsetString(size_t size, char ch) { - char *s = Ident((char*)malloc(size)); - memset(s, ch, size); - return s; -} - -char* MallocAndMemsetString(size_t size) { - return MallocAndMemsetString(size, 'z'); -} - -#if defined(__linux__) && !defined(__ANDROID__) -#define READ_TEST(READ_N_BYTES) \ - char *x = new char[10]; \ - int fd = open("/proc/self/stat", O_RDONLY); \ - ASSERT_GT(fd, 0); \ - EXPECT_DEATH(READ_N_BYTES, \ - ASAN_PCRE_DOTALL \ - "AddressSanitizer: heap-buffer-overflow" \ - ".* is located 0 bytes to the right of 10-byte region"); \ - close(fd); \ - delete [] x; \ - -TEST(AddressSanitizer, pread) { - READ_TEST(pread(fd, x, 15, 0)); -} - -TEST(AddressSanitizer, pread64) { - READ_TEST(pread64(fd, x, 15, 0)); -} - -TEST(AddressSanitizer, read) { - READ_TEST(read(fd, x, 15)); -} -#endif // defined(__linux__) && !defined(__ANDROID__) - -// This test case fails -// Clang optimizes memcpy/memset calls which lead to unaligned access -TEST(AddressSanitizer, DISABLED_MemIntrinsicUnalignedAccessTest) { - int size = Ident(4096); - char *s = Ident((char*)malloc(size)); - EXPECT_DEATH(memset(s + size - 1, 0, 2), RightOOBWriteMessage(0)); - free(s); -} - -// TODO(samsonov): Add a test with malloc(0) -// TODO(samsonov): Add tests for str* and mem* functions. - -NOINLINE static int LargeFunction(bool do_bad_access) { - int *x = new int[100]; - x[0]++; - x[1]++; - x[2]++; - x[3]++; - x[4]++; - x[5]++; - x[6]++; - x[7]++; - x[8]++; - x[9]++; - - x[do_bad_access ? 100 : 0]++; int res = __LINE__; - - x[10]++; - x[11]++; - x[12]++; - x[13]++; - x[14]++; - x[15]++; - x[16]++; - x[17]++; - x[18]++; - x[19]++; - - delete[] x; - return res; -} - -// Test the we have correct debug info for the failing instruction. -// This test requires the in-process symbolizer to be enabled by default. -TEST(AddressSanitizer, DISABLED_LargeFunctionSymbolizeTest) { - int failing_line = LargeFunction(false); - char expected_warning[128]; - sprintf(expected_warning, "LargeFunction.*asan_test.*:%d", failing_line); - EXPECT_DEATH(LargeFunction(true), expected_warning); -} - -// Check that we unwind and symbolize correctly. -TEST(AddressSanitizer, DISABLED_MallocFreeUnwindAndSymbolizeTest) { - int *a = (int*)malloc_aaa(sizeof(int)); - *a = 1; - free_aaa(a); - EXPECT_DEATH(*a = 1, "free_ccc.*free_bbb.*free_aaa.*" - "malloc_fff.*malloc_eee.*malloc_ddd"); -} - -static bool TryToSetThreadName(const char *name) { -#if defined(__linux__) && defined(PR_SET_NAME) - return 0 == prctl(PR_SET_NAME, (unsigned long)name, 0, 0, 0); -#else - return false; -#endif -} - -void *ThreadedTestAlloc(void *a) { - EXPECT_EQ(true, TryToSetThreadName("AllocThr")); - int **p = (int**)a; - *p = new int; - return 0; -} - -void *ThreadedTestFree(void *a) { - EXPECT_EQ(true, TryToSetThreadName("FreeThr")); - int **p = (int**)a; - delete *p; - return 0; -} - -void *ThreadedTestUse(void *a) { - EXPECT_EQ(true, TryToSetThreadName("UseThr")); - int **p = (int**)a; - **p = 1; - return 0; -} - -void ThreadedTestSpawn() { - pthread_t t; - int *x; - PTHREAD_CREATE(&t, 0, ThreadedTestAlloc, &x); - PTHREAD_JOIN(t, 0); - PTHREAD_CREATE(&t, 0, ThreadedTestFree, &x); - PTHREAD_JOIN(t, 0); - PTHREAD_CREATE(&t, 0, ThreadedTestUse, &x); - PTHREAD_JOIN(t, 0); -} - -#if !defined(_WIN32) // FIXME: This should be a lit test. -TEST(AddressSanitizer, ThreadedTest) { - EXPECT_DEATH(ThreadedTestSpawn(), - ASAN_PCRE_DOTALL - "Thread T.*created" - ".*Thread T.*created" - ".*Thread T.*created"); -} -#endif - -void *ThreadedTestFunc(void *unused) { - // Check if prctl(PR_SET_NAME) is supported. Return if not. - if (!TryToSetThreadName("TestFunc")) - return 0; - EXPECT_DEATH(ThreadedTestSpawn(), - ASAN_PCRE_DOTALL - "WRITE .*thread T. .UseThr." - ".*freed by thread T. .FreeThr. here:" - ".*previously allocated by thread T. .AllocThr. here:" - ".*Thread T. .UseThr. created by T.*TestFunc" - ".*Thread T. .FreeThr. created by T" - ".*Thread T. .AllocThr. created by T" - ""); - return 0; -} - -TEST(AddressSanitizer, ThreadNamesTest) { - // Run ThreadedTestFunc in a separate thread because it tries to set a - // thread name and we don't want to change the main thread's name. - pthread_t t; - PTHREAD_CREATE(&t, 0, ThreadedTestFunc, 0); - PTHREAD_JOIN(t, 0); -} - -#if ASAN_NEEDS_SEGV -TEST(AddressSanitizer, ShadowGapTest) { -#if SANITIZER_WORDSIZE == 32 - char *addr = (char*)0x22000000; -#else -# if defined(__powerpc64__) - char *addr = (char*)0x024000800000; -# else - char *addr = (char*)0x0000100000080000; -# endif -#endif - EXPECT_DEATH(*addr = 1, "AddressSanitizer: SEGV on unknown"); -} -#endif // ASAN_NEEDS_SEGV - -extern "C" { -NOINLINE static void UseThenFreeThenUse() { - char *x = Ident((char*)malloc(8)); - *x = 1; - free_aaa(x); - *x = 2; -} -} - -TEST(AddressSanitizer, UseThenFreeThenUseTest) { - EXPECT_DEATH(UseThenFreeThenUse(), "freed by thread"); -} - -TEST(AddressSanitizer, StrDupTest) { - free(strdup(Ident("123"))); -} - -// Currently we create and poison redzone at right of global variables. -static char static110[110]; -const char ConstGlob[7] = {1, 2, 3, 4, 5, 6, 7}; -static const char StaticConstGlob[3] = {9, 8, 7}; - -TEST(AddressSanitizer, GlobalTest) { - static char func_static15[15]; - - static char fs1[10]; - static char fs2[10]; - static char fs3[10]; - - glob5[Ident(0)] = 0; - glob5[Ident(1)] = 0; - glob5[Ident(2)] = 0; - glob5[Ident(3)] = 0; - glob5[Ident(4)] = 0; - - EXPECT_DEATH(glob5[Ident(5)] = 0, - "0 bytes to the right of global variable.*glob5.* size 5"); - EXPECT_DEATH(glob5[Ident(5+6)] = 0, - "6 bytes to the right of global variable.*glob5.* size 5"); - Ident(static110); // avoid optimizations - static110[Ident(0)] = 0; - static110[Ident(109)] = 0; - EXPECT_DEATH(static110[Ident(110)] = 0, - "0 bytes to the right of global variable"); - EXPECT_DEATH(static110[Ident(110+7)] = 0, - "7 bytes to the right of global variable"); - - Ident(func_static15); // avoid optimizations - func_static15[Ident(0)] = 0; - EXPECT_DEATH(func_static15[Ident(15)] = 0, - "0 bytes to the right of global variable"); - EXPECT_DEATH(func_static15[Ident(15 + 9)] = 0, - "9 bytes to the right of global variable"); - - Ident(fs1); - Ident(fs2); - Ident(fs3); - - // We don't create left redzones, so this is not 100% guaranteed to fail. - // But most likely will. - EXPECT_DEATH(fs2[Ident(-1)] = 0, "is located.*of global variable"); - - EXPECT_DEATH(Ident(Ident(ConstGlob)[8]), - "is located 1 bytes to the right of .*ConstGlob"); - EXPECT_DEATH(Ident(Ident(StaticConstGlob)[5]), - "is located 2 bytes to the right of .*StaticConstGlob"); - - // call stuff from another file. - GlobalsTest(0); -} - -TEST(AddressSanitizer, GlobalStringConstTest) { - static const char *zoo = "FOOBAR123"; - const char *p = Ident(zoo); - EXPECT_DEATH(Ident(p[15]), "is ascii string 'FOOBAR123'"); -} - -TEST(AddressSanitizer, FileNameInGlobalReportTest) { - static char zoo[10]; - const char *p = Ident(zoo); - // The file name should be present in the report. - EXPECT_DEATH(Ident(p[15]), "zoo.*asan_test."); -} - -int *ReturnsPointerToALocalObject() { - int a = 0; - return Ident(&a); -} - -#if ASAN_UAR == 1 -TEST(AddressSanitizer, LocalReferenceReturnTest) { - int *(*f)() = Ident(ReturnsPointerToALocalObject); - int *p = f(); - // Call 'f' a few more times, 'p' should still be poisoned. - for (int i = 0; i < 32; i++) - f(); - EXPECT_DEATH(*p = 1, "AddressSanitizer: stack-use-after-return"); - EXPECT_DEATH(*p = 1, "is located.*in frame .*ReturnsPointerToALocal"); -} -#endif - -template <int kSize> -NOINLINE static void FuncWithStack() { - char x[kSize]; - Ident(x)[0] = 0; - Ident(x)[kSize-1] = 0; -} - -static void LotsOfStackReuse() { - int LargeStack[10000]; - Ident(LargeStack)[0] = 0; - for (int i = 0; i < 10000; i++) { - FuncWithStack<128 * 1>(); - FuncWithStack<128 * 2>(); - FuncWithStack<128 * 4>(); - FuncWithStack<128 * 8>(); - FuncWithStack<128 * 16>(); - FuncWithStack<128 * 32>(); - FuncWithStack<128 * 64>(); - FuncWithStack<128 * 128>(); - FuncWithStack<128 * 256>(); - FuncWithStack<128 * 512>(); - Ident(LargeStack)[0] = 0; - } -} - -TEST(AddressSanitizer, StressStackReuseTest) { - LotsOfStackReuse(); -} - -TEST(AddressSanitizer, ThreadedStressStackReuseTest) { - const int kNumThreads = 20; - pthread_t t[kNumThreads]; - for (int i = 0; i < kNumThreads; i++) { - PTHREAD_CREATE(&t[i], 0, (void* (*)(void *x))LotsOfStackReuse, 0); - } - for (int i = 0; i < kNumThreads; i++) { - PTHREAD_JOIN(t[i], 0); - } -} - -static void *PthreadExit(void *a) { - pthread_exit(0); - return 0; -} - -TEST(AddressSanitizer, PthreadExitTest) { - pthread_t t; - for (int i = 0; i < 1000; i++) { - PTHREAD_CREATE(&t, 0, PthreadExit, 0); - PTHREAD_JOIN(t, 0); - } -} - -// FIXME: Why does clang-cl define __EXCEPTIONS? -#if defined(__EXCEPTIONS) && !defined(_WIN32) -NOINLINE static void StackReuseAndException() { - int large_stack[1000]; - Ident(large_stack); - ASAN_THROW(1); -} - -// TODO(kcc): support exceptions with use-after-return. -TEST(AddressSanitizer, DISABLED_StressStackReuseAndExceptionsTest) { - for (int i = 0; i < 10000; i++) { - try { - StackReuseAndException(); - } catch(...) { - } - } -} -#endif - -#if !defined(_WIN32) -TEST(AddressSanitizer, MlockTest) { - EXPECT_EQ(0, mlockall(MCL_CURRENT)); - EXPECT_EQ(0, mlock((void*)0x12345, 0x5678)); - EXPECT_EQ(0, munlockall()); - EXPECT_EQ(0, munlock((void*)0x987, 0x654)); -} -#endif - -struct LargeStruct { - int foo[100]; -}; - -// Test for bug http://llvm.org/bugs/show_bug.cgi?id=11763. -// Struct copy should not cause asan warning even if lhs == rhs. -TEST(AddressSanitizer, LargeStructCopyTest) { - LargeStruct a; - *Ident(&a) = *Ident(&a); -} - -ATTRIBUTE_NO_SANITIZE_ADDRESS -static void NoSanitizeAddress() { - char *foo = new char[10]; - Ident(foo)[10] = 0; - delete [] foo; -} - -TEST(AddressSanitizer, AttributeNoSanitizeAddressTest) { - Ident(NoSanitizeAddress)(); -} - -// The new/delete/etc mismatch checks don't work on Android, -// as calls to new/delete go through malloc/free. -// OS X support is tracked here: -// https://code.google.com/p/address-sanitizer/issues/detail?id=131 -// Windows support is tracked here: -// https://code.google.com/p/address-sanitizer/issues/detail?id=309 -#if !defined(__ANDROID__) && \ - !defined(__APPLE__) && \ - !defined(_WIN32) -static string MismatchStr(const string &str) { - return string("AddressSanitizer: alloc-dealloc-mismatch \\(") + str; -} - -TEST(AddressSanitizer, AllocDeallocMismatch) { - EXPECT_DEATH(free(Ident(new int)), - MismatchStr("operator new vs free")); - EXPECT_DEATH(free(Ident(new int[2])), - MismatchStr("operator new \\[\\] vs free")); - EXPECT_DEATH(delete (Ident(new int[2])), - MismatchStr("operator new \\[\\] vs operator delete")); - EXPECT_DEATH(delete (Ident((int*)malloc(2 * sizeof(int)))), - MismatchStr("malloc vs operator delete")); - EXPECT_DEATH(delete [] (Ident(new int)), - MismatchStr("operator new vs operator delete \\[\\]")); - EXPECT_DEATH(delete [] (Ident((int*)malloc(2 * sizeof(int)))), - MismatchStr("malloc vs operator delete \\[\\]")); -} -#endif - -// ------------------ demo tests; run each one-by-one ------------- -// e.g. --gtest_filter=*DemoOOBLeftHigh --gtest_also_run_disabled_tests -TEST(AddressSanitizer, DISABLED_DemoThreadedTest) { - ThreadedTestSpawn(); -} - -void *SimpleBugOnSTack(void *x = 0) { - char a[20]; - Ident(a)[20] = 0; - return 0; -} - -TEST(AddressSanitizer, DISABLED_DemoStackTest) { - SimpleBugOnSTack(); -} - -TEST(AddressSanitizer, DISABLED_DemoThreadStackTest) { - pthread_t t; - PTHREAD_CREATE(&t, 0, SimpleBugOnSTack, 0); - PTHREAD_JOIN(t, 0); -} - -TEST(AddressSanitizer, DISABLED_DemoUAFLowIn) { - uaf_test<U1>(10, 0); -} -TEST(AddressSanitizer, DISABLED_DemoUAFLowLeft) { - uaf_test<U1>(10, -2); -} -TEST(AddressSanitizer, DISABLED_DemoUAFLowRight) { - uaf_test<U1>(10, 10); -} - -TEST(AddressSanitizer, DISABLED_DemoUAFHigh) { - uaf_test<U1>(kLargeMalloc, 0); -} - -TEST(AddressSanitizer, DISABLED_DemoOOM) { - size_t size = SANITIZER_WORDSIZE == 64 ? (size_t)(1ULL << 40) : (0xf0000000); - printf("%p\n", malloc(size)); -} - -TEST(AddressSanitizer, DISABLED_DemoDoubleFreeTest) { - DoubleFree(); -} - -TEST(AddressSanitizer, DISABLED_DemoNullDerefTest) { - int *a = 0; - Ident(a)[10] = 0; -} - -TEST(AddressSanitizer, DISABLED_DemoFunctionStaticTest) { - static char a[100]; - static char b[100]; - static char c[100]; - Ident(a); - Ident(b); - Ident(c); - Ident(a)[5] = 0; - Ident(b)[105] = 0; - Ident(a)[5] = 0; -} - -TEST(AddressSanitizer, DISABLED_DemoTooMuchMemoryTest) { - const size_t kAllocSize = (1 << 28) - 1024; - size_t total_size = 0; - while (true) { - void *x = malloc(kAllocSize); - memset(x, 0, kAllocSize); - total_size += kAllocSize; - fprintf(stderr, "total: %ldM %p\n", (long)total_size >> 20, x); - } -} - -// http://code.google.com/p/address-sanitizer/issues/detail?id=66 -TEST(AddressSanitizer, BufferOverflowAfterManyFrees) { - for (int i = 0; i < 1000000; i++) { - delete [] (Ident(new char [8644])); - } - char *x = new char[8192]; - EXPECT_DEATH(x[Ident(8192)] = 0, "AddressSanitizer: heap-buffer-overflow"); - delete [] Ident(x); -} - - -// Test that instrumentation of stack allocations takes into account -// AllocSize of a type, and not its StoreSize (16 vs 10 bytes for long double). -// See http://llvm.org/bugs/show_bug.cgi?id=12047 for more details. -TEST(AddressSanitizer, LongDoubleNegativeTest) { - long double a, b; - static long double c; - memcpy(Ident(&a), Ident(&b), sizeof(long double)); - memcpy(Ident(&c), Ident(&b), sizeof(long double)); -} - -#if !defined(_WIN32) -TEST(AddressSanitizer, pthread_getschedparam) { - int policy; - struct sched_param param; - EXPECT_DEATH( - pthread_getschedparam(pthread_self(), &policy, Ident(¶m) + 2), - "AddressSanitizer: stack-buffer-.*flow"); - EXPECT_DEATH( - pthread_getschedparam(pthread_self(), Ident(&policy) - 1, ¶m), - "AddressSanitizer: stack-buffer-.*flow"); - int res = pthread_getschedparam(pthread_self(), &policy, ¶m); - ASSERT_EQ(0, res); -} -#endif - -#if SANITIZER_TEST_HAS_PRINTF_L -static int vsnprintf_l_wrapper(char *s, size_t n, - locale_t l, const char *format, ...) { - va_list va; - va_start(va, format); - int res = vsnprintf_l(s, n , l, format, va); - va_end(va); - return res; -} - -TEST(AddressSanitizer, snprintf_l) { - char buff[5]; - // Check that snprintf_l() works fine with Asan. - int res = snprintf_l(buff, 5, - _LIBCPP_GET_C_LOCALE, "%s", "snprintf_l()"); - EXPECT_EQ(12, res); - // Check that vsnprintf_l() works fine with Asan. - res = vsnprintf_l_wrapper(buff, 5, - _LIBCPP_GET_C_LOCALE, "%s", "vsnprintf_l()"); - EXPECT_EQ(13, res); - - EXPECT_DEATH(snprintf_l(buff, 10, - _LIBCPP_GET_C_LOCALE, "%s", "snprintf_l()"), - "AddressSanitizer: stack-buffer-overflow"); - EXPECT_DEATH(vsnprintf_l_wrapper(buff, 10, - _LIBCPP_GET_C_LOCALE, "%s", "vsnprintf_l()"), - "AddressSanitizer: stack-buffer-overflow"); -} -#endif diff --git a/contrib/compiler-rt/lib/asan/tests/asan_test.ignore b/contrib/compiler-rt/lib/asan/tests/asan_test.ignore deleted file mode 100644 index ea5c26099e75..000000000000 --- a/contrib/compiler-rt/lib/asan/tests/asan_test.ignore +++ /dev/null @@ -1,3 +0,0 @@ -# blacklisted functions for instrumented ASan unit test -fun:*IgnoreTest* -fun:*SomeOtherFunc* diff --git a/contrib/compiler-rt/lib/asan/tests/asan_test_config.h b/contrib/compiler-rt/lib/asan/tests/asan_test_config.h deleted file mode 100644 index 92f276307481..000000000000 --- a/contrib/compiler-rt/lib/asan/tests/asan_test_config.h +++ /dev/null @@ -1,54 +0,0 @@ -//===-- asan_test_config.h --------------------------------------*- C++ -*-===// -// -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. -// -//===----------------------------------------------------------------------===// -// -// This file is a part of AddressSanitizer, an address sanity checker. -// -//===----------------------------------------------------------------------===// -#if !defined(INCLUDED_FROM_ASAN_TEST_UTILS_H) -# error "This file should be included into asan_test_utils.h only" -#endif - -#ifndef ASAN_TEST_CONFIG_H -#define ASAN_TEST_CONFIG_H - -#include <vector> -#include <string> -#include <map> - -using std::string; -using std::vector; -using std::map; - -#ifndef ASAN_UAR -# error "please define ASAN_UAR" -#endif - -#ifndef ASAN_HAS_EXCEPTIONS -# error "please define ASAN_HAS_EXCEPTIONS" -#endif - -#ifndef ASAN_HAS_BLACKLIST -# error "please define ASAN_HAS_BLACKLIST" -#endif - -#ifndef ASAN_NEEDS_SEGV -# if defined(_WIN32) -# define ASAN_NEEDS_SEGV 0 -# else -# define ASAN_NEEDS_SEGV 1 -# endif -#endif - -#ifndef ASAN_AVOID_EXPENSIVE_TESTS -# define ASAN_AVOID_EXPENSIVE_TESTS 0 -#endif - -#define ASAN_PCRE_DOTALL "" - -#endif // ASAN_TEST_CONFIG_H diff --git a/contrib/compiler-rt/lib/asan/tests/asan_test_main.cc b/contrib/compiler-rt/lib/asan/tests/asan_test_main.cc deleted file mode 100644 index 1746c5f4837b..000000000000 --- a/contrib/compiler-rt/lib/asan/tests/asan_test_main.cc +++ /dev/null @@ -1,19 +0,0 @@ -//===-- asan_test_main.cc -------------------------------------------------===// -// -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. -// -//===----------------------------------------------------------------------===// -// -// This file is a part of AddressSanitizer, an address sanity checker. -// -//===----------------------------------------------------------------------===// -#include "asan_test_utils.h" - -int main(int argc, char **argv) { - testing::GTEST_FLAG(death_test_style) = "threadsafe"; - testing::InitGoogleTest(&argc, argv); - return RUN_ALL_TESTS(); -} diff --git a/contrib/compiler-rt/lib/asan/tests/asan_test_utils.h b/contrib/compiler-rt/lib/asan/tests/asan_test_utils.h deleted file mode 100644 index 03d17cfb26a7..000000000000 --- a/contrib/compiler-rt/lib/asan/tests/asan_test_utils.h +++ /dev/null @@ -1,107 +0,0 @@ -//===-- asan_test_utils.h ---------------------------------------*- C++ -*-===// -// -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. -// -//===----------------------------------------------------------------------===// -// -// This file is a part of AddressSanitizer, an address sanity checker. -// -//===----------------------------------------------------------------------===// - -#ifndef ASAN_TEST_UTILS_H -#define ASAN_TEST_UTILS_H - -#if !defined(SANITIZER_EXTERNAL_TEST_CONFIG) -# define INCLUDED_FROM_ASAN_TEST_UTILS_H -# include "asan_test_config.h" -# undef INCLUDED_FROM_ASAN_TEST_UTILS_H -#endif - -#include "sanitizer_test_utils.h" -#include "sanitizer_pthread_wrappers.h" - -#include <stdio.h> -#include <signal.h> -#include <stdlib.h> -#include <string.h> -#include <stdint.h> -#include <assert.h> -#include <algorithm> - -#if !defined(_WIN32) -# include <strings.h> -# include <sys/mman.h> -# include <setjmp.h> -#endif - -#ifdef __linux__ -# include <sys/prctl.h> -# include <sys/types.h> -# include <sys/stat.h> -# include <fcntl.h> -#include <unistd.h> -#endif - -#if !defined(__APPLE__) && !defined(__FreeBSD__) -#include <malloc.h> -#endif - -#if ASAN_HAS_EXCEPTIONS -# define ASAN_THROW(x) throw (x) -#else -# define ASAN_THROW(x) -#endif - -typedef uint8_t U1; -typedef uint16_t U2; -typedef uint32_t U4; -typedef uint64_t U8; - -static const int kPageSize = 4096; - -const size_t kLargeMalloc = 1 << 24; - -extern void free_aaa(void *p); -extern void *malloc_aaa(size_t size); - -template<typename T> -NOINLINE void asan_write(T *a) { - *a = 0; -} - -string RightOOBErrorMessage(int oob_distance, bool is_write); -string RightOOBWriteMessage(int oob_distance); -string RightOOBReadMessage(int oob_distance); -string LeftOOBErrorMessage(int oob_distance, bool is_write); -string LeftOOBWriteMessage(int oob_distance); -string LeftOOBReadMessage(int oob_distance); -string LeftOOBAccessMessage(int oob_distance); -char* MallocAndMemsetString(size_t size, char ch); -char* MallocAndMemsetString(size_t size); - -extern char glob1[1]; -extern char glob2[2]; -extern char glob3[3]; -extern char glob4[4]; -extern char glob5[5]; -extern char glob6[6]; -extern char glob7[7]; -extern char glob8[8]; -extern char glob9[9]; -extern char glob10[10]; -extern char glob11[11]; -extern char glob12[12]; -extern char glob13[13]; -extern char glob14[14]; -extern char glob15[15]; -extern char glob16[16]; -extern char glob17[17]; -extern char glob1000[1000]; -extern char glob10000[10000]; -extern char glob100000[100000]; -extern int GlobalsTest(int x); - -#endif // ASAN_TEST_UTILS_H diff --git a/contrib/compiler-rt/lib/builtins/README.txt b/contrib/compiler-rt/lib/builtins/README.txt index 1c08e7415e64..ad36e4e5279a 100644 --- a/contrib/compiler-rt/lib/builtins/README.txt +++ b/contrib/compiler-rt/lib/builtins/README.txt @@ -220,7 +220,9 @@ _Unwind_Reason_Code __gcc_personality_v0(int version, _Unwind_Action actions, // for use with some implementations of assert() in <assert.h> void __eprintf(const char* format, const char* assertion_expression, const char* line, const char* file); - + +// for systems with emulated thread local storage +void* __emutls_get_address(struct __emutls_control*); // Power PC specific functions diff --git a/contrib/compiler-rt/lib/builtins/arm/aeabi_cdcmp.S b/contrib/compiler-rt/lib/builtins/arm/aeabi_cdcmp.S new file mode 100644 index 000000000000..036a6f542f79 --- /dev/null +++ b/contrib/compiler-rt/lib/builtins/arm/aeabi_cdcmp.S @@ -0,0 +1,96 @@ +//===-- aeabi_cdcmp.S - EABI cdcmp* implementation ------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is dual licensed under the MIT and the University of Illinois Open +// Source Licenses. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "../assembly.h" + +#if __BYTE_ORDER__ != __ORDER_LITTLE_ENDIAN__ +#error big endian support not implemented +#endif + +#define APSR_Z (1 << 30) +#define APSR_C (1 << 29) + +// void __aeabi_cdcmpeq(double a, double b) { +// if (isnan(a) || isnan(b)) { +// Z = 0; C = 1; +// } else { +// __aeabi_cdcmple(a, b); +// } +// } + + .syntax unified + .p2align 2 +DEFINE_COMPILERRT_FUNCTION(__aeabi_cdcmpeq) + push {r0-r3, lr} + bl __aeabi_cdcmpeq_check_nan + cmp r0, #1 + pop {r0-r3, lr} + + // NaN has been ruled out, so __aeabi_cdcmple can't trap + bne __aeabi_cdcmple + + msr CPSR_f, #APSR_C + JMP(lr) +END_COMPILERRT_FUNCTION(__aeabi_cdcmpeq) + + +// void __aeabi_cdcmple(double a, double b) { +// if (__aeabi_dcmplt(a, b)) { +// Z = 0; C = 0; +// } else if (__aeabi_dcmpeq(a, b)) { +// Z = 1; C = 1; +// } else { +// Z = 0; C = 1; +// } +// } + + .syntax unified + .p2align 2 +DEFINE_COMPILERRT_FUNCTION(__aeabi_cdcmple) + // Per the RTABI, this function must preserve r0-r11. + // Save lr in the same instruction for compactness + push {r0-r3, lr} + + bl __aeabi_dcmplt + cmp r0, #1 + moveq ip, #0 + beq 1f + + ldm sp, {r0-r3} + bl __aeabi_dcmpeq + cmp r0, #1 + moveq ip, #(APSR_C | APSR_Z) + movne ip, #(APSR_C) + +1: + msr CPSR_f, ip + pop {r0-r3} + POP_PC() +END_COMPILERRT_FUNCTION(__aeabi_cdcmple) + +// int __aeabi_cdrcmple(double a, double b) { +// return __aeabi_cdcmple(b, a); +// } + + .syntax unified + .p2align 2 +DEFINE_COMPILERRT_FUNCTION(__aeabi_cdrcmple) + // Swap r0 and r2 + mov ip, r0 + mov r0, r2 + mov r2, ip + + // Swap r1 and r3 + mov ip, r1 + mov r1, r3 + mov r3, ip + + b __aeabi_cdcmple +END_COMPILERRT_FUNCTION(__aeabi_cdrcmple) + diff --git a/contrib/compiler-rt/lib/builtins/arm/aeabi_cdcmpeq_check_nan.c b/contrib/compiler-rt/lib/builtins/arm/aeabi_cdcmpeq_check_nan.c new file mode 100644 index 000000000000..577f6b2c5535 --- /dev/null +++ b/contrib/compiler-rt/lib/builtins/arm/aeabi_cdcmpeq_check_nan.c @@ -0,0 +1,16 @@ +//===-- lib/arm/aeabi_cdcmpeq_helper.c - Helper for cdcmpeq ---------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is dual licensed under the MIT and the University of Illinois Open +// Source Licenses. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include <stdint.h> + +__attribute__((pcs("aapcs"))) +__attribute__((visibility("hidden"))) +int __aeabi_cdcmpeq_check_nan(double a, double b) { + return __builtin_isnan(a) || __builtin_isnan(b); +} diff --git a/contrib/compiler-rt/lib/builtins/arm/aeabi_cfcmp.S b/contrib/compiler-rt/lib/builtins/arm/aeabi_cfcmp.S new file mode 100644 index 000000000000..43594e5c3936 --- /dev/null +++ b/contrib/compiler-rt/lib/builtins/arm/aeabi_cfcmp.S @@ -0,0 +1,91 @@ +//===-- aeabi_cfcmp.S - EABI cfcmp* implementation ------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is dual licensed under the MIT and the University of Illinois Open +// Source Licenses. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "../assembly.h" + +#if __BYTE_ORDER__ != __ORDER_LITTLE_ENDIAN__ +#error big endian support not implemented +#endif + +#define APSR_Z (1 << 30) +#define APSR_C (1 << 29) + +// void __aeabi_cfcmpeq(float a, float b) { +// if (isnan(a) || isnan(b)) { +// Z = 0; C = 1; +// } else { +// __aeabi_cfcmple(a, b); +// } +// } + + .syntax unified + .p2align 2 +DEFINE_COMPILERRT_FUNCTION(__aeabi_cfcmpeq) + push {r0-r3, lr} + bl __aeabi_cfcmpeq_check_nan + cmp r0, #1 + pop {r0-r3, lr} + + // NaN has been ruled out, so __aeabi_cfcmple can't trap + bne __aeabi_cfcmple + + msr CPSR_f, #APSR_C + JMP(lr) +END_COMPILERRT_FUNCTION(__aeabi_cfcmpeq) + + +// void __aeabi_cfcmple(float a, float b) { +// if (__aeabi_fcmplt(a, b)) { +// Z = 0; C = 0; +// } else if (__aeabi_fcmpeq(a, b)) { +// Z = 1; C = 1; +// } else { +// Z = 0; C = 1; +// } +// } + + .syntax unified + .p2align 2 +DEFINE_COMPILERRT_FUNCTION(__aeabi_cfcmple) + // Per the RTABI, this function must preserve r0-r11. + // Save lr in the same instruction for compactness + push {r0-r3, lr} + + bl __aeabi_fcmplt + cmp r0, #1 + moveq ip, #0 + beq 1f + + ldm sp, {r0-r3} + bl __aeabi_fcmpeq + cmp r0, #1 + moveq ip, #(APSR_C | APSR_Z) + movne ip, #(APSR_C) + +1: + msr CPSR_f, ip + pop {r0-r3} + POP_PC() +END_COMPILERRT_FUNCTION(__aeabi_cfcmple) + +// int __aeabi_cfrcmple(float a, float b) { +// return __aeabi_cfcmple(b, a); +// } + + .syntax unified + .p2align 2 +DEFINE_COMPILERRT_FUNCTION(__aeabi_cfrcmple) + // Swap r0 and r1 + mov ip, r0 + mov r0, r1 + mov r1, ip + + b __aeabi_cfcmple +END_COMPILERRT_FUNCTION(__aeabi_cfrcmple) + diff --git a/contrib/compiler-rt/lib/builtins/arm/aeabi_cfcmpeq_check_nan.c b/contrib/compiler-rt/lib/builtins/arm/aeabi_cfcmpeq_check_nan.c new file mode 100644 index 000000000000..992e31fbd8d6 --- /dev/null +++ b/contrib/compiler-rt/lib/builtins/arm/aeabi_cfcmpeq_check_nan.c @@ -0,0 +1,16 @@ +//===-- lib/arm/aeabi_cfcmpeq_helper.c - Helper for cdcmpeq ---------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is dual licensed under the MIT and the University of Illinois Open +// Source Licenses. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include <stdint.h> + +__attribute__((pcs("aapcs"))) +__attribute__((visibility("hidden"))) +int __aeabi_cfcmpeq_check_nan(float a, float b) { + return __builtin_isnan(a) || __builtin_isnan(b); +} diff --git a/contrib/compiler-rt/lib/builtins/arm/aeabi_drsub.c b/contrib/compiler-rt/lib/builtins/arm/aeabi_drsub.c new file mode 100644 index 000000000000..fc17d5a4cc76 --- /dev/null +++ b/contrib/compiler-rt/lib/builtins/arm/aeabi_drsub.c @@ -0,0 +1,19 @@ +//===-- lib/arm/aeabi_drsub.c - Double-precision subtraction --------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is dual licensed under the MIT and the University of Illinois Open +// Source Licenses. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#define DOUBLE_PRECISION +#include "../fp_lib.h" + +COMPILER_RT_ABI fp_t +__aeabi_dsub(fp_t, fp_t); + +COMPILER_RT_ABI fp_t +__aeabi_drsub(fp_t a, fp_t b) { + return __aeabi_dsub(b, a); +} diff --git a/contrib/compiler-rt/lib/builtins/arm/aeabi_frsub.c b/contrib/compiler-rt/lib/builtins/arm/aeabi_frsub.c new file mode 100644 index 000000000000..64258dc7e070 --- /dev/null +++ b/contrib/compiler-rt/lib/builtins/arm/aeabi_frsub.c @@ -0,0 +1,19 @@ +//===-- lib/arm/aeabi_frsub.c - Single-precision subtraction --------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is dual licensed under the MIT and the University of Illinois Open +// Source Licenses. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#define SINGLE_PRECISION +#include "../fp_lib.h" + +COMPILER_RT_ABI fp_t +__aeabi_fsub(fp_t, fp_t); + +COMPILER_RT_ABI fp_t +__aeabi_frsub(fp_t a, fp_t b) { + return __aeabi_fsub(b, a); +} diff --git a/contrib/compiler-rt/lib/builtins/assembly.h b/contrib/compiler-rt/lib/builtins/assembly.h index 8bb0ddc106bd..c28970534cc4 100644 --- a/contrib/compiler-rt/lib/builtins/assembly.h +++ b/contrib/compiler-rt/lib/builtins/assembly.h @@ -73,6 +73,15 @@ #define JMPc(r, c) mov##c pc, r #endif +// pop {pc} can't switch Thumb mode on ARMv4T +#if __ARM_ARCH >= 5 +#define POP_PC() pop {pc} +#else +#define POP_PC() \ + pop {ip}; \ + JMP(ip) +#endif + #if __ARM_ARCH_ISA_THUMB == 2 #define IT(cond) it cond #define ITT(cond) itt cond diff --git a/contrib/compiler-rt/lib/builtins/atomic.c b/contrib/compiler-rt/lib/builtins/atomic.c index 35c8837dcecf..f1ddc3e0c522 100644 --- a/contrib/compiler-rt/lib/builtins/atomic.c +++ b/contrib/compiler-rt/lib/builtins/atomic.c @@ -56,13 +56,13 @@ static const long SPINLOCK_MASK = SPINLOCK_COUNT - 1; #include <machine/atomic.h> #include <sys/umtx.h> typedef struct _usem Lock; -inline static void unlock(Lock *l) { +__inline static void unlock(Lock *l) { __c11_atomic_store((_Atomic(uint32_t)*)&l->_count, 1, __ATOMIC_RELEASE); __c11_atomic_thread_fence(__ATOMIC_SEQ_CST); if (l->_has_waiters) _umtx_op(l, UMTX_OP_SEM_WAKE, 1, 0, 0); } -inline static void lock(Lock *l) { +__inline static void lock(Lock *l) { uint32_t old = 1; while (!__c11_atomic_compare_exchange_weak((_Atomic(uint32_t)*)&l->_count, &old, 0, __ATOMIC_ACQUIRE, __ATOMIC_RELAXED)) { @@ -76,12 +76,12 @@ static Lock locks[SPINLOCK_COUNT] = { [0 ... SPINLOCK_COUNT-1] = {0,1,0} }; #elif defined(__APPLE__) #include <libkern/OSAtomic.h> typedef OSSpinLock Lock; -inline static void unlock(Lock *l) { +__inline static void unlock(Lock *l) { OSSpinLockUnlock(l); } /// Locks a lock. In the current implementation, this is potentially /// unbounded in the contended case. -inline static void lock(Lock *l) { +__inline static void lock(Lock *l) { OSSpinLockLock(l); } static Lock locks[SPINLOCK_COUNT]; // initialized to OS_SPINLOCK_INIT which is 0 @@ -89,12 +89,12 @@ static Lock locks[SPINLOCK_COUNT]; // initialized to OS_SPINLOCK_INIT which is 0 #else typedef _Atomic(uintptr_t) Lock; /// Unlock a lock. This is a release operation. -inline static void unlock(Lock *l) { +__inline static void unlock(Lock *l) { __c11_atomic_store(l, 0, __ATOMIC_RELEASE); } /// Locks a lock. In the current implementation, this is potentially /// unbounded in the contended case. -inline static void lock(Lock *l) { +__inline static void lock(Lock *l) { uintptr_t old = 0; while (!__c11_atomic_compare_exchange_weak(l, &old, 1, __ATOMIC_ACQUIRE, __ATOMIC_RELAXED)) @@ -106,7 +106,7 @@ static Lock locks[SPINLOCK_COUNT]; /// Returns a lock to use for a given pointer. -static inline Lock *lock_for_pointer(void *ptr) { +static __inline Lock *lock_for_pointer(void *ptr) { intptr_t hash = (intptr_t)ptr; // Disregard the lowest 4 bits. We want all values that may be part of the // same memory operation to hash to the same value and therefore use the same diff --git a/contrib/compiler-rt/lib/builtins/atomic_flag_clear.c b/contrib/compiler-rt/lib/builtins/atomic_flag_clear.c index 15984cd5267d..da912af64312 100644 --- a/contrib/compiler-rt/lib/builtins/atomic_flag_clear.c +++ b/contrib/compiler-rt/lib/builtins/atomic_flag_clear.c @@ -12,8 +12,16 @@ *===------------------------------------------------------------------------=== */ +#ifndef __has_include +#define __has_include(inc) 0 +#endif + +#if __has_include(<stdatomic.h>) + #include <stdatomic.h> #undef atomic_flag_clear void atomic_flag_clear(volatile atomic_flag *object) { - return __c11_atomic_store(&(object)->_Value, 0, __ATOMIC_SEQ_CST); + __c11_atomic_store(&(object)->_Value, 0, __ATOMIC_SEQ_CST); } + +#endif diff --git a/contrib/compiler-rt/lib/builtins/atomic_flag_clear_explicit.c b/contrib/compiler-rt/lib/builtins/atomic_flag_clear_explicit.c index 0f7859c2cd82..1059b787f169 100644 --- a/contrib/compiler-rt/lib/builtins/atomic_flag_clear_explicit.c +++ b/contrib/compiler-rt/lib/builtins/atomic_flag_clear_explicit.c @@ -12,9 +12,17 @@ *===------------------------------------------------------------------------=== */ +#ifndef __has_include +#define __has_include(inc) 0 +#endif + +#if __has_include(<stdatomic.h>) + #include <stdatomic.h> #undef atomic_flag_clear_explicit void atomic_flag_clear_explicit(volatile atomic_flag *object, memory_order order) { - return __c11_atomic_store(&(object)->_Value, 0, order); + __c11_atomic_store(&(object)->_Value, 0, order); } + +#endif diff --git a/contrib/compiler-rt/lib/builtins/atomic_flag_test_and_set.c b/contrib/compiler-rt/lib/builtins/atomic_flag_test_and_set.c index 07209fc02d5e..e8811d39ef25 100644 --- a/contrib/compiler-rt/lib/builtins/atomic_flag_test_and_set.c +++ b/contrib/compiler-rt/lib/builtins/atomic_flag_test_and_set.c @@ -12,8 +12,16 @@ *===------------------------------------------------------------------------=== */ +#ifndef __has_include +#define __has_include(inc) 0 +#endif + +#if __has_include(<stdatomic.h>) + #include <stdatomic.h> #undef atomic_flag_test_and_set _Bool atomic_flag_test_and_set(volatile atomic_flag *object) { return __c11_atomic_exchange(&(object)->_Value, 1, __ATOMIC_SEQ_CST); } + +#endif diff --git a/contrib/compiler-rt/lib/builtins/atomic_flag_test_and_set_explicit.c b/contrib/compiler-rt/lib/builtins/atomic_flag_test_and_set_explicit.c index eaa5be08df46..5c8c2df90543 100644 --- a/contrib/compiler-rt/lib/builtins/atomic_flag_test_and_set_explicit.c +++ b/contrib/compiler-rt/lib/builtins/atomic_flag_test_and_set_explicit.c @@ -12,9 +12,17 @@ *===------------------------------------------------------------------------=== */ +#ifndef __has_include +#define __has_include(inc) 0 +#endif + +#if __has_include(<stdatomic.h>) + #include <stdatomic.h> #undef atomic_flag_test_and_set_explicit _Bool atomic_flag_test_and_set_explicit(volatile atomic_flag *object, memory_order order) { return __c11_atomic_exchange(&(object)->_Value, 1, order); } + +#endif diff --git a/contrib/compiler-rt/lib/builtins/atomic_signal_fence.c b/contrib/compiler-rt/lib/builtins/atomic_signal_fence.c index ad292d2f1c72..9ccc2ae60ad8 100644 --- a/contrib/compiler-rt/lib/builtins/atomic_signal_fence.c +++ b/contrib/compiler-rt/lib/builtins/atomic_signal_fence.c @@ -12,8 +12,16 @@ *===------------------------------------------------------------------------=== */ +#ifndef __has_include +#define __has_include(inc) 0 +#endif + +#if __has_include(<stdatomic.h>) + #include <stdatomic.h> #undef atomic_signal_fence void atomic_signal_fence(memory_order order) { __c11_atomic_signal_fence(order); } + +#endif diff --git a/contrib/compiler-rt/lib/builtins/atomic_thread_fence.c b/contrib/compiler-rt/lib/builtins/atomic_thread_fence.c index 71f698c9de75..d22560151bc8 100644 --- a/contrib/compiler-rt/lib/builtins/atomic_thread_fence.c +++ b/contrib/compiler-rt/lib/builtins/atomic_thread_fence.c @@ -12,8 +12,16 @@ *===------------------------------------------------------------------------=== */ +#ifndef __has_include +#define __has_include(inc) 0 +#endif + +#if __has_include(<stdatomic.h>) + #include <stdatomic.h> #undef atomic_thread_fence void atomic_thread_fence(memory_order order) { __c11_atomic_thread_fence(order); } + +#endif diff --git a/contrib/compiler-rt/lib/builtins/comparedf2.c b/contrib/compiler-rt/lib/builtins/comparedf2.c index 64eea1249055..9e29752231e9 100644 --- a/contrib/compiler-rt/lib/builtins/comparedf2.c +++ b/contrib/compiler-rt/lib/builtins/comparedf2.c @@ -80,6 +80,11 @@ __ledf2(fp_t a, fp_t b) { } } +#if defined(__ELF__) +// Alias for libgcc compatibility +FNALIAS(__cmpdf2, __ledf2); +#endif + enum GE_RESULT { GE_LESS = -1, GE_EQUAL = 0, diff --git a/contrib/compiler-rt/lib/builtins/comparesf2.c b/contrib/compiler-rt/lib/builtins/comparesf2.c index 442289c1004e..1fd50636abaf 100644 --- a/contrib/compiler-rt/lib/builtins/comparesf2.c +++ b/contrib/compiler-rt/lib/builtins/comparesf2.c @@ -80,6 +80,11 @@ __lesf2(fp_t a, fp_t b) { } } +#if defined(__ELF__) +// Alias for libgcc compatibility +FNALIAS(__cmpsf2, __lesf2); +#endif + enum GE_RESULT { GE_LESS = -1, GE_EQUAL = 0, diff --git a/contrib/compiler-rt/lib/builtins/comparetf2.c b/contrib/compiler-rt/lib/builtins/comparetf2.c index a6436de89e76..c0ad8ed0aecd 100644 --- a/contrib/compiler-rt/lib/builtins/comparetf2.c +++ b/contrib/compiler-rt/lib/builtins/comparetf2.c @@ -79,6 +79,11 @@ COMPILER_RT_ABI enum LE_RESULT __letf2(fp_t a, fp_t b) { } } +#if defined(__ELF__) +// Alias for libgcc compatibility +FNALIAS(__cmptf2, __letf2); +#endif + enum GE_RESULT { GE_LESS = -1, GE_EQUAL = 0, diff --git a/contrib/compiler-rt/lib/builtins/divdc3.c b/contrib/compiler-rt/lib/builtins/divdc3.c index 7de78c8711e1..3c88390b5e77 100644 --- a/contrib/compiler-rt/lib/builtins/divdc3.c +++ b/contrib/compiler-rt/lib/builtins/divdc3.c @@ -17,7 +17,7 @@ /* Returns: the quotient of (a + ib) / (c + id) */ -COMPILER_RT_ABI double _Complex +COMPILER_RT_ABI Dcomplex __divdc3(double __a, double __b, double __c, double __d) { int __ilogbw = 0; @@ -29,31 +29,31 @@ __divdc3(double __a, double __b, double __c, double __d) __d = crt_scalbn(__d, -__ilogbw); } double __denom = __c * __c + __d * __d; - double _Complex z; - __real__ z = crt_scalbn((__a * __c + __b * __d) / __denom, -__ilogbw); - __imag__ z = crt_scalbn((__b * __c - __a * __d) / __denom, -__ilogbw); - if (crt_isnan(__real__ z) && crt_isnan(__imag__ z)) + Dcomplex z; + COMPLEX_REAL(z) = crt_scalbn((__a * __c + __b * __d) / __denom, -__ilogbw); + COMPLEX_IMAGINARY(z) = crt_scalbn((__b * __c - __a * __d) / __denom, -__ilogbw); + if (crt_isnan(COMPLEX_REAL(z)) && crt_isnan(COMPLEX_IMAGINARY(z))) { if ((__denom == 0.0) && (!crt_isnan(__a) || !crt_isnan(__b))) { - __real__ z = crt_copysign(CRT_INFINITY, __c) * __a; - __imag__ z = crt_copysign(CRT_INFINITY, __c) * __b; + COMPLEX_REAL(z) = crt_copysign(CRT_INFINITY, __c) * __a; + COMPLEX_IMAGINARY(z) = crt_copysign(CRT_INFINITY, __c) * __b; } else if ((crt_isinf(__a) || crt_isinf(__b)) && crt_isfinite(__c) && crt_isfinite(__d)) { __a = crt_copysign(crt_isinf(__a) ? 1.0 : 0.0, __a); __b = crt_copysign(crt_isinf(__b) ? 1.0 : 0.0, __b); - __real__ z = CRT_INFINITY * (__a * __c + __b * __d); - __imag__ z = CRT_INFINITY * (__b * __c - __a * __d); + COMPLEX_REAL(z) = CRT_INFINITY * (__a * __c + __b * __d); + COMPLEX_IMAGINARY(z) = CRT_INFINITY * (__b * __c - __a * __d); } else if (crt_isinf(__logbw) && __logbw > 0.0 && crt_isfinite(__a) && crt_isfinite(__b)) { __c = crt_copysign(crt_isinf(__c) ? 1.0 : 0.0, __c); __d = crt_copysign(crt_isinf(__d) ? 1.0 : 0.0, __d); - __real__ z = 0.0 * (__a * __c + __b * __d); - __imag__ z = 0.0 * (__b * __c - __a * __d); + COMPLEX_REAL(z) = 0.0 * (__a * __c + __b * __d); + COMPLEX_IMAGINARY(z) = 0.0 * (__b * __c - __a * __d); } } return z; diff --git a/contrib/compiler-rt/lib/builtins/divsc3.c b/contrib/compiler-rt/lib/builtins/divsc3.c index 710d5320803f..42a48315e66d 100644 --- a/contrib/compiler-rt/lib/builtins/divsc3.c +++ b/contrib/compiler-rt/lib/builtins/divsc3.c @@ -17,7 +17,7 @@ /* Returns: the quotient of (a + ib) / (c + id) */ -COMPILER_RT_ABI float _Complex +COMPILER_RT_ABI Fcomplex __divsc3(float __a, float __b, float __c, float __d) { int __ilogbw = 0; @@ -29,31 +29,31 @@ __divsc3(float __a, float __b, float __c, float __d) __d = crt_scalbnf(__d, -__ilogbw); } float __denom = __c * __c + __d * __d; - float _Complex z; - __real__ z = crt_scalbnf((__a * __c + __b * __d) / __denom, -__ilogbw); - __imag__ z = crt_scalbnf((__b * __c - __a * __d) / __denom, -__ilogbw); - if (crt_isnan(__real__ z) && crt_isnan(__imag__ z)) + Fcomplex z; + COMPLEX_REAL(z) = crt_scalbnf((__a * __c + __b * __d) / __denom, -__ilogbw); + COMPLEX_IMAGINARY(z) = crt_scalbnf((__b * __c - __a * __d) / __denom, -__ilogbw); + if (crt_isnan(COMPLEX_REAL(z)) && crt_isnan(COMPLEX_IMAGINARY(z))) { if ((__denom == 0) && (!crt_isnan(__a) || !crt_isnan(__b))) { - __real__ z = crt_copysignf(CRT_INFINITY, __c) * __a; - __imag__ z = crt_copysignf(CRT_INFINITY, __c) * __b; + COMPLEX_REAL(z) = crt_copysignf(CRT_INFINITY, __c) * __a; + COMPLEX_IMAGINARY(z) = crt_copysignf(CRT_INFINITY, __c) * __b; } else if ((crt_isinf(__a) || crt_isinf(__b)) && crt_isfinite(__c) && crt_isfinite(__d)) { __a = crt_copysignf(crt_isinf(__a) ? 1 : 0, __a); __b = crt_copysignf(crt_isinf(__b) ? 1 : 0, __b); - __real__ z = CRT_INFINITY * (__a * __c + __b * __d); - __imag__ z = CRT_INFINITY * (__b * __c - __a * __d); + COMPLEX_REAL(z) = CRT_INFINITY * (__a * __c + __b * __d); + COMPLEX_IMAGINARY(z) = CRT_INFINITY * (__b * __c - __a * __d); } else if (crt_isinf(__logbw) && __logbw > 0 && crt_isfinite(__a) && crt_isfinite(__b)) { __c = crt_copysignf(crt_isinf(__c) ? 1 : 0, __c); __d = crt_copysignf(crt_isinf(__d) ? 1 : 0, __d); - __real__ z = 0 * (__a * __c + __b * __d); - __imag__ z = 0 * (__b * __c - __a * __d); + COMPLEX_REAL(z) = 0 * (__a * __c + __b * __d); + COMPLEX_IMAGINARY(z) = 0 * (__b * __c - __a * __d); } } return z; diff --git a/contrib/compiler-rt/lib/builtins/divtc3.c b/contrib/compiler-rt/lib/builtins/divtc3.c new file mode 100644 index 000000000000..04693df471ff --- /dev/null +++ b/contrib/compiler-rt/lib/builtins/divtc3.c @@ -0,0 +1,60 @@ +/*===-- divtc3.c - Implement __divtc3 -------------------------------------=== + * + * The LLVM Compiler Infrastructure + * + * This file is dual licensed under the MIT and the University of Illinois Open + * Source Licenses. See LICENSE.TXT for details. + * + * ===----------------------------------------------------------------------=== + * + * This file implements __divtc3 for the compiler_rt library. + * + *===----------------------------------------------------------------------=== + */ + +#include "int_lib.h" +#include "int_math.h" + +/* Returns: the quotient of (a + ib) / (c + id) */ + +COMPILER_RT_ABI long double _Complex +__divtc3(long double __a, long double __b, long double __c, long double __d) +{ + int __ilogbw = 0; + long double __logbw = crt_logbl(crt_fmaxl(crt_fabsl(__c), crt_fabsl(__d))); + if (crt_isfinite(__logbw)) + { + __ilogbw = (int)__logbw; + __c = crt_scalbnl(__c, -__ilogbw); + __d = crt_scalbnl(__d, -__ilogbw); + } + long double __denom = __c * __c + __d * __d; + long double _Complex z; + __real__ z = crt_scalbnl((__a * __c + __b * __d) / __denom, -__ilogbw); + __imag__ z = crt_scalbnl((__b * __c - __a * __d) / __denom, -__ilogbw); + if (crt_isnan(__real__ z) && crt_isnan(__imag__ z)) + { + if ((__denom == 0.0) && (!crt_isnan(__a) || !crt_isnan(__b))) + { + __real__ z = crt_copysignl(CRT_INFINITY, __c) * __a; + __imag__ z = crt_copysignl(CRT_INFINITY, __c) * __b; + } + else if ((crt_isinf(__a) || crt_isinf(__b)) && + crt_isfinite(__c) && crt_isfinite(__d)) + { + __a = crt_copysignl(crt_isinf(__a) ? 1.0 : 0.0, __a); + __b = crt_copysignl(crt_isinf(__b) ? 1.0 : 0.0, __b); + __real__ z = CRT_INFINITY * (__a * __c + __b * __d); + __imag__ z = CRT_INFINITY * (__b * __c - __a * __d); + } + else if (crt_isinf(__logbw) && __logbw > 0.0 && + crt_isfinite(__a) && crt_isfinite(__b)) + { + __c = crt_copysignl(crt_isinf(__c) ? 1.0 : 0.0, __c); + __d = crt_copysignl(crt_isinf(__d) ? 1.0 : 0.0, __d); + __real__ z = 0.0 * (__a * __c + __b * __d); + __imag__ z = 0.0 * (__b * __c - __a * __d); + } + } + return z; +} diff --git a/contrib/compiler-rt/lib/builtins/divxc3.c b/contrib/compiler-rt/lib/builtins/divxc3.c index 175ae3cf4aee..6f49280e5f61 100644 --- a/contrib/compiler-rt/lib/builtins/divxc3.c +++ b/contrib/compiler-rt/lib/builtins/divxc3.c @@ -18,7 +18,7 @@ /* Returns: the quotient of (a + ib) / (c + id) */ -COMPILER_RT_ABI long double _Complex +COMPILER_RT_ABI Lcomplex __divxc3(long double __a, long double __b, long double __c, long double __d) { int __ilogbw = 0; @@ -30,31 +30,31 @@ __divxc3(long double __a, long double __b, long double __c, long double __d) __d = crt_scalbnl(__d, -__ilogbw); } long double __denom = __c * __c + __d * __d; - long double _Complex z; - __real__ z = crt_scalbnl((__a * __c + __b * __d) / __denom, -__ilogbw); - __imag__ z = crt_scalbnl((__b * __c - __a * __d) / __denom, -__ilogbw); - if (crt_isnan(__real__ z) && crt_isnan(__imag__ z)) + Lcomplex z; + COMPLEX_REAL(z) = crt_scalbnl((__a * __c + __b * __d) / __denom, -__ilogbw); + COMPLEX_IMAGINARY(z) = crt_scalbnl((__b * __c - __a * __d) / __denom, -__ilogbw); + if (crt_isnan(COMPLEX_REAL(z)) && crt_isnan(COMPLEX_IMAGINARY(z))) { if ((__denom == 0) && (!crt_isnan(__a) || !crt_isnan(__b))) { - __real__ z = crt_copysignl(CRT_INFINITY, __c) * __a; - __imag__ z = crt_copysignl(CRT_INFINITY, __c) * __b; + COMPLEX_REAL(z) = crt_copysignl(CRT_INFINITY, __c) * __a; + COMPLEX_IMAGINARY(z) = crt_copysignl(CRT_INFINITY, __c) * __b; } else if ((crt_isinf(__a) || crt_isinf(__b)) && crt_isfinite(__c) && crt_isfinite(__d)) { __a = crt_copysignl(crt_isinf(__a) ? 1 : 0, __a); __b = crt_copysignl(crt_isinf(__b) ? 1 : 0, __b); - __real__ z = CRT_INFINITY * (__a * __c + __b * __d); - __imag__ z = CRT_INFINITY * (__b * __c - __a * __d); + COMPLEX_REAL(z) = CRT_INFINITY * (__a * __c + __b * __d); + COMPLEX_IMAGINARY(z) = CRT_INFINITY * (__b * __c - __a * __d); } else if (crt_isinf(__logbw) && __logbw > 0 && crt_isfinite(__a) && crt_isfinite(__b)) { __c = crt_copysignl(crt_isinf(__c) ? 1 : 0, __c); __d = crt_copysignl(crt_isinf(__d) ? 1 : 0, __d); - __real__ z = 0 * (__a * __c + __b * __d); - __imag__ z = 0 * (__b * __c - __a * __d); + COMPLEX_REAL(z) = 0 * (__a * __c + __b * __d); + COMPLEX_IMAGINARY(z) = 0 * (__b * __c - __a * __d); } } return z; diff --git a/contrib/compiler-rt/lib/builtins/emutls.c b/contrib/compiler-rt/lib/builtins/emutls.c new file mode 100644 index 000000000000..09e79568bd56 --- /dev/null +++ b/contrib/compiler-rt/lib/builtins/emutls.c @@ -0,0 +1,183 @@ +/* ===---------- emutls.c - Implements __emutls_get_address ---------------=== + * + * The LLVM Compiler Infrastructure + * + * This file is dual licensed under the MIT and the University of Illinois Open + * Source Licenses. See LICENSE.TXT for details. + * + * ===----------------------------------------------------------------------=== + */ +#include <pthread.h> +#include <stdint.h> +#include <stdlib.h> +#include <string.h> + +#include "int_lib.h" +#include "int_util.h" + +/* Default is not to use posix_memalign, so systems like Android + * can use thread local data without heavier POSIX memory allocators. + */ +#ifndef EMUTLS_USE_POSIX_MEMALIGN +#define EMUTLS_USE_POSIX_MEMALIGN 0 +#endif + +/* For every TLS variable xyz, + * there is one __emutls_control variable named __emutls_v.xyz. + * If xyz has non-zero initial value, __emutls_v.xyz's "value" + * will point to __emutls_t.xyz, which has the initial value. + */ +typedef struct __emutls_control { + size_t size; /* size of the object in bytes */ + size_t align; /* alignment of the object in bytes */ + union { + uintptr_t index; /* data[index-1] is the object address */ + void* address; /* object address, when in single thread env */ + } object; + void* value; /* null or non-zero initial value for the object */ +} __emutls_control; + +static __inline void *emutls_memalign_alloc(size_t align, size_t size) { + void *base; +#if EMUTLS_USE_POSIX_MEMALIGN + if (posix_memalign(&base, align, size) != 0) + abort(); +#else + #define EXTRA_ALIGN_PTR_BYTES (align - 1 + sizeof(void*)) + char* object; + if ((object = malloc(EXTRA_ALIGN_PTR_BYTES + size)) == NULL) + abort(); + base = (void*)(((uintptr_t)(object + EXTRA_ALIGN_PTR_BYTES)) + & ~(uintptr_t)(align - 1)); + + ((void**)base)[-1] = object; +#endif + return base; +} + +static __inline void emutls_memalign_free(void *base) { +#if EMUTLS_USE_POSIX_MEMALIGN + free(base); +#else + /* The mallocated address is in ((void**)base)[-1] */ + free(((void**)base)[-1]); +#endif +} + +/* Emulated TLS objects are always allocated at run-time. */ +static __inline void *emutls_allocate_object(__emutls_control *control) { + /* Use standard C types, check with gcc's emutls.o. */ + typedef unsigned int gcc_word __attribute__((mode(word))); + typedef unsigned int gcc_pointer __attribute__((mode(pointer))); + COMPILE_TIME_ASSERT(sizeof(size_t) == sizeof(gcc_word)); + COMPILE_TIME_ASSERT(sizeof(uintptr_t) == sizeof(gcc_pointer)); + COMPILE_TIME_ASSERT(sizeof(uintptr_t) == sizeof(void*)); + + size_t size = control->size; + size_t align = control->align; + if (align < sizeof(void*)) + align = sizeof(void*); + /* Make sure that align is power of 2. */ + if ((align & (align - 1)) != 0) + abort(); + + void* base = emutls_memalign_alloc(align, size); + if (control->value) + memcpy(base, control->value, size); + else + memset(base, 0, size); + return base; +} + +static pthread_mutex_t emutls_mutex = PTHREAD_MUTEX_INITIALIZER; + +static size_t emutls_num_object = 0; /* number of allocated TLS objects */ + +typedef struct emutls_address_array { + uintptr_t size; /* number of elements in the 'data' array */ + void* data[]; +} emutls_address_array; + +static pthread_key_t emutls_pthread_key; + +static void emutls_key_destructor(void* ptr) { + emutls_address_array* array = (emutls_address_array*)ptr; + uintptr_t i; + for (i = 0; i < array->size; ++i) { + if (array->data[i]) + emutls_memalign_free(array->data[i]); + } + free(ptr); +} + +static void emutls_init(void) { + if (pthread_key_create(&emutls_pthread_key, emutls_key_destructor) != 0) + abort(); +} + +/* Returns control->object.index; set index if not allocated yet. */ +static __inline uintptr_t emutls_get_index(__emutls_control *control) { + uintptr_t index = __atomic_load_n(&control->object.index, __ATOMIC_ACQUIRE); + if (!index) { + static pthread_once_t once = PTHREAD_ONCE_INIT; + pthread_once(&once, emutls_init); + pthread_mutex_lock(&emutls_mutex); + index = control->object.index; + if (!index) { + index = ++emutls_num_object; + __atomic_store_n(&control->object.index, index, __ATOMIC_RELEASE); + } + pthread_mutex_unlock(&emutls_mutex); + } + return index; +} + +/* Updates newly allocated thread local emutls_address_array. */ +static __inline void emutls_check_array_set_size(emutls_address_array *array, + uintptr_t size) { + if (array == NULL) + abort(); + array->size = size; + pthread_setspecific(emutls_pthread_key, (void*)array); +} + +/* Returns the new 'data' array size, number of elements, + * which must be no smaller than the given index. + */ +static __inline uintptr_t emutls_new_data_array_size(uintptr_t index) { + /* Need to allocate emutls_address_array with one extra slot + * to store the data array size. + * Round up the emutls_address_array size to multiple of 16. + */ + return ((index + 1 + 15) & ~((uintptr_t)15)) - 1; +} + +/* Returns the thread local emutls_address_array. + * Extends its size if necessary to hold address at index. + */ +static __inline emutls_address_array * +emutls_get_address_array(uintptr_t index) { + emutls_address_array* array = pthread_getspecific(emutls_pthread_key); + if (array == NULL) { + uintptr_t new_size = emutls_new_data_array_size(index); + array = calloc(new_size + 1, sizeof(void*)); + emutls_check_array_set_size(array, new_size); + } else if (index > array->size) { + uintptr_t orig_size = array->size; + uintptr_t new_size = emutls_new_data_array_size(index); + array = realloc(array, (new_size + 1) * sizeof(void*)); + if (array) + memset(array->data + orig_size, 0, + (new_size - orig_size) * sizeof(void*)); + emutls_check_array_set_size(array, new_size); + } + return array; +} + +void* __emutls_get_address(__emutls_control* control) { + uintptr_t index = emutls_get_index(control); + emutls_address_array* array = emutls_get_address_array(index); + if (array->data[index - 1] == NULL) + array->data[index - 1] = emutls_allocate_object(control); + return array->data[index - 1]; +} diff --git a/contrib/compiler-rt/lib/builtins/enable_execute_stack.c b/contrib/compiler-rt/lib/builtins/enable_execute_stack.c index 23e494051adf..0dc3482c4467 100644 --- a/contrib/compiler-rt/lib/builtins/enable_execute_stack.c +++ b/contrib/compiler-rt/lib/builtins/enable_execute_stack.c @@ -21,8 +21,8 @@ #define HAVE_SYSCONF 1 #ifdef _WIN32 -#include <windef.h> -#include <winbase.h> +#define WIN32_LEAN_AND_MEAN +#include <Windows.h> #else #ifndef __APPLE__ #include <unistd.h> diff --git a/contrib/compiler-rt/lib/builtins/extendhfsf2.c b/contrib/compiler-rt/lib/builtins/extendhfsf2.c index 7524e2ea7ed6..27115a48c184 100644 --- a/contrib/compiler-rt/lib/builtins/extendhfsf2.c +++ b/contrib/compiler-rt/lib/builtins/extendhfsf2.c @@ -12,9 +12,11 @@ #define DST_SINGLE #include "fp_extend_impl.inc" +ARM_EABI_FNALIAS(h2f, extendhfsf2) + // Use a forwarding definition and noinline to implement a poor man's alias, // as there isn't a good cross-platform way of defining one. -COMPILER_RT_ABI __attribute__((noinline)) float __extendhfsf2(uint16_t a) { +COMPILER_RT_ABI NOINLINE float __extendhfsf2(uint16_t a) { return __extendXfYf2__(a); } diff --git a/contrib/compiler-rt/lib/builtins/fixunsdfdi.c b/contrib/compiler-rt/lib/builtins/fixunsdfdi.c index 2e0d87eacf05..4b0bc9e1d051 100644 --- a/contrib/compiler-rt/lib/builtins/fixunsdfdi.c +++ b/contrib/compiler-rt/lib/builtins/fixunsdfdi.c @@ -22,8 +22,8 @@ COMPILER_RT_ABI du_int __fixunsdfdi(double a) { if (a <= 0.0) return 0; - su_int high = a/0x1p32f; - su_int low = a - (double)high*0x1p32f; + su_int high = a / 4294967296.f; /* a / 0x1p32f; */ + su_int low = a - (double)high * 4294967296.f; /* high * 0x1p32f; */ return ((du_int)high << 32) | low; } diff --git a/contrib/compiler-rt/lib/builtins/fixunssfdi.c b/contrib/compiler-rt/lib/builtins/fixunssfdi.c index 5a154e82cff4..f8ebab854f95 100644 --- a/contrib/compiler-rt/lib/builtins/fixunssfdi.c +++ b/contrib/compiler-rt/lib/builtins/fixunssfdi.c @@ -23,8 +23,8 @@ __fixunssfdi(float a) { if (a <= 0.0f) return 0; double da = a; - su_int high = da/0x1p32f; - su_int low = da - (double)high*0x1p32f; + su_int high = da / 4294967296.f; /* da / 0x1p32f; */ + su_int low = da - (double)high * 4294967296.f; /* high * 0x1p32f; */ return ((du_int)high << 32) | low; } diff --git a/contrib/compiler-rt/lib/builtins/floatdidf.c b/contrib/compiler-rt/lib/builtins/floatdidf.c index e53fa2580f6e..a300c9f312d2 100644 --- a/contrib/compiler-rt/lib/builtins/floatdidf.c +++ b/contrib/compiler-rt/lib/builtins/floatdidf.c @@ -32,8 +32,8 @@ ARM_EABI_FNALIAS(l2d, floatdidf) COMPILER_RT_ABI double __floatdidf(di_int a) { - static const double twop52 = 0x1.0p52; - static const double twop32 = 0x1.0p32; + static const double twop52 = 4503599627370496.0; // 0x1.0p52 + static const double twop32 = 4294967296.0; // 0x1.0p32 union { int64_t x; double d; } low = { .d = twop52 }; diff --git a/contrib/compiler-rt/lib/builtins/floatditf.c b/contrib/compiler-rt/lib/builtins/floatditf.c index 1a5f8e539470..cd51dd8aade4 100644 --- a/contrib/compiler-rt/lib/builtins/floatditf.c +++ b/contrib/compiler-rt/lib/builtins/floatditf.c @@ -27,19 +27,17 @@ COMPILER_RT_ABI fp_t __floatditf(di_int a) { // All other cases begin by extracting the sign and absolute value of a rep_t sign = 0; - unsigned aAbs = (unsigned)a; + du_int aAbs = (du_int)a; if (a < 0) { sign = signBit; - aAbs += 0x80000000; + aAbs = ~(du_int)a + 1U; } // Exponent of (fp_t)a is the width of abs(a). - const int exponent = (aWidth - 1) - __builtin_clzll(a); + const int exponent = (aWidth - 1) - __builtin_clzll(aAbs); rep_t result; - // Shift a into the significand field and clear the implicit bit. Extra - // cast to unsigned int is necessary to get the correct behavior for - // the input INT_MIN. + // Shift a into the significand field, rounding if it is a right-shift const int shift = significandBits - exponent; result = (rep_t)aAbs << shift ^ implicitBit; diff --git a/contrib/compiler-rt/lib/builtins/floatsitf.c b/contrib/compiler-rt/lib/builtins/floatsitf.c index 85346933f81e..f0abca363b5e 100644 --- a/contrib/compiler-rt/lib/builtins/floatsitf.c +++ b/contrib/compiler-rt/lib/builtins/floatsitf.c @@ -30,16 +30,14 @@ COMPILER_RT_ABI fp_t __floatsitf(int a) { unsigned aAbs = (unsigned)a; if (a < 0) { sign = signBit; - aAbs += 0x80000000; + aAbs = ~(unsigned)a + 1U; } // Exponent of (fp_t)a is the width of abs(a). - const int exponent = (aWidth - 1) - __builtin_clz(a); + const int exponent = (aWidth - 1) - __builtin_clz(aAbs); rep_t result; - // Shift a into the significand field and clear the implicit bit. Extra - // cast to unsigned int is necessary to get the correct behavior for - // the input INT_MIN. + // Shift a into the significand field and clear the implicit bit. const int shift = significandBits - exponent; result = (rep_t)aAbs << shift ^ implicitBit; diff --git a/contrib/compiler-rt/lib/builtins/floatundidf.c b/contrib/compiler-rt/lib/builtins/floatundidf.c index 73b8bac1c1a1..67aa86e5e5b8 100644 --- a/contrib/compiler-rt/lib/builtins/floatundidf.c +++ b/contrib/compiler-rt/lib/builtins/floatundidf.c @@ -32,9 +32,9 @@ ARM_EABI_FNALIAS(ul2d, floatundidf) COMPILER_RT_ABI double __floatundidf(du_int a) { - static const double twop52 = 0x1.0p52; - static const double twop84 = 0x1.0p84; - static const double twop84_plus_twop52 = 0x1.00000001p84; + static const double twop52 = 4503599627370496.0; // 0x1.0p52 + static const double twop84 = 19342813113834066795298816.0; // 0x1.0p84 + static const double twop84_plus_twop52 = 19342813118337666422669312.0; // 0x1.00000001p84 union { uint64_t x; double d; } high = { .d = twop84 }; union { uint64_t x; double d; } low = { .d = twop52 }; diff --git a/contrib/compiler-rt/lib/builtins/fp_add_impl.inc b/contrib/compiler-rt/lib/builtins/fp_add_impl.inc index 5741889728cd..b47be1b648e6 100644 --- a/contrib/compiler-rt/lib/builtins/fp_add_impl.inc +++ b/contrib/compiler-rt/lib/builtins/fp_add_impl.inc @@ -14,7 +14,7 @@ #include "fp_lib.h" -static inline fp_t __addXf3__(fp_t a, fp_t b) { +static __inline fp_t __addXf3__(fp_t a, fp_t b) { rep_t aRep = toRep(a); rep_t bRep = toRep(b); const rep_t aAbs = aRep & absMask; diff --git a/contrib/compiler-rt/lib/builtins/fp_extend.h b/contrib/compiler-rt/lib/builtins/fp_extend.h index 5c2b92310df1..6d95a0680709 100644 --- a/contrib/compiler-rt/lib/builtins/fp_extend.h +++ b/contrib/compiler-rt/lib/builtins/fp_extend.h @@ -28,7 +28,7 @@ typedef double src_t; typedef uint64_t src_rep_t; #define SRC_REP_C UINT64_C static const int srcSigBits = 52; -static inline int src_rep_t_clz(src_rep_t a) { +static __inline int src_rep_t_clz(src_rep_t a) { #if defined __LP64__ return __builtin_clzl(a); #else @@ -75,12 +75,12 @@ static const int dstSigBits = 112; // End of specialization parameters. Two helper routines for conversion to and // from the representation of floating-point data as integer values follow. -static inline src_rep_t srcToRep(src_t x) { +static __inline src_rep_t srcToRep(src_t x) { const union { src_t f; src_rep_t i; } rep = {.f = x}; return rep.i; } -static inline dst_t dstFromRep(dst_rep_t x) { +static __inline dst_t dstFromRep(dst_rep_t x) { const union { dst_t f; dst_rep_t i; } rep = {.i = x}; return rep.f; } diff --git a/contrib/compiler-rt/lib/builtins/fp_extend_impl.inc b/contrib/compiler-rt/lib/builtins/fp_extend_impl.inc index edcfa8d2329d..b785cc7687ad 100644 --- a/contrib/compiler-rt/lib/builtins/fp_extend_impl.inc +++ b/contrib/compiler-rt/lib/builtins/fp_extend_impl.inc @@ -38,7 +38,7 @@ #include "fp_extend.h" -static inline dst_t __extendXfYf2__(src_t a) { +static __inline dst_t __extendXfYf2__(src_t a) { // Various constants whose values follow from the type parameters. // Any reasonable optimizer will fold and propagate all of these. const int srcBits = sizeof(src_t)*CHAR_BIT; diff --git a/contrib/compiler-rt/lib/builtins/fp_fixint_impl.inc b/contrib/compiler-rt/lib/builtins/fp_fixint_impl.inc index 035e87ca10e0..da70d4d39301 100644 --- a/contrib/compiler-rt/lib/builtins/fp_fixint_impl.inc +++ b/contrib/compiler-rt/lib/builtins/fp_fixint_impl.inc @@ -14,7 +14,7 @@ #include "fp_lib.h" -static inline fixint_t __fixint(fp_t a) { +static __inline fixint_t __fixint(fp_t a) { const fixint_t fixint_max = (fixint_t)((~(fixuint_t)0) / 2); const fixint_t fixint_min = -fixint_max - 1; // Break a into sign, exponent, significand diff --git a/contrib/compiler-rt/lib/builtins/fp_fixuint_impl.inc b/contrib/compiler-rt/lib/builtins/fp_fixuint_impl.inc index 5fefab0e2d8a..d68ccf27a79c 100644 --- a/contrib/compiler-rt/lib/builtins/fp_fixuint_impl.inc +++ b/contrib/compiler-rt/lib/builtins/fp_fixuint_impl.inc @@ -14,7 +14,7 @@ #include "fp_lib.h" -static inline fixuint_t __fixuint(fp_t a) { +static __inline fixuint_t __fixuint(fp_t a) { // Break a into sign, exponent, significand const rep_t aRep = toRep(a); const rep_t aAbs = aRep & absMask; @@ -27,7 +27,7 @@ static inline fixuint_t __fixuint(fp_t a) { return 0; // If the value is too large for the integer type, saturate. - if ((unsigned)exponent > sizeof(fixuint_t) * CHAR_BIT) + if ((unsigned)exponent >= sizeof(fixuint_t) * CHAR_BIT) return ~(fixuint_t)0; // If 0 <= exponent < significandBits, right shift to get the result. diff --git a/contrib/compiler-rt/lib/builtins/fp_lib.h b/contrib/compiler-rt/lib/builtins/fp_lib.h index faebb99ecd5e..223fb980aaed 100644 --- a/contrib/compiler-rt/lib/builtins/fp_lib.h +++ b/contrib/compiler-rt/lib/builtins/fp_lib.h @@ -46,12 +46,12 @@ typedef float fp_t; #define REP_C UINT32_C #define significandBits 23 -static inline int rep_clz(rep_t a) { +static __inline int rep_clz(rep_t a) { return __builtin_clz(a); } // 32x32 --> 64 bit multiply -static inline void wideMultiply(rep_t a, rep_t b, rep_t *hi, rep_t *lo) { +static __inline void wideMultiply(rep_t a, rep_t b, rep_t *hi, rep_t *lo) { const uint64_t product = (uint64_t)a*b; *hi = product >> 32; *lo = product; @@ -66,7 +66,7 @@ typedef double fp_t; #define REP_C UINT64_C #define significandBits 52 -static inline int rep_clz(rep_t a) { +static __inline int rep_clz(rep_t a) { #if defined __LP64__ return __builtin_clzl(a); #else @@ -83,7 +83,7 @@ static inline int rep_clz(rep_t a) { // 64x64 -> 128 wide multiply for platforms that don't have such an operation; // many 64-bit platforms have this operation, but they tend to have hardware // floating-point, so we don't bother with a special case for them here. -static inline void wideMultiply(rep_t a, rep_t b, rep_t *hi, rep_t *lo) { +static __inline void wideMultiply(rep_t a, rep_t b, rep_t *hi, rep_t *lo) { // Each of the component 32x32 -> 64 products const uint64_t plolo = loWord(a) * loWord(b); const uint64_t plohi = loWord(a) * hiWord(b); @@ -112,7 +112,7 @@ typedef long double fp_t; // 128-bit integer, we let the constant be casted to 128-bit integer #define significandBits 112 -static inline int rep_clz(rep_t a) { +static __inline int rep_clz(rep_t a) { const union { __uint128_t ll; @@ -148,7 +148,7 @@ static inline int rep_clz(rep_t a) { // 128x128 -> 256 wide multiply for platforms that don't have such an operation; // many 64-bit platforms have this operation, but they tend to have hardware // floating-point, so we don't bother with a special case for them here. -static inline void wideMultiply(rep_t a, rep_t b, rep_t *hi, rep_t *lo) { +static __inline void wideMultiply(rep_t a, rep_t b, rep_t *hi, rep_t *lo) { const uint64_t product11 = Word_1(a) * Word_1(b); const uint64_t product12 = Word_1(a) * Word_2(b); @@ -228,28 +228,28 @@ static inline void wideMultiply(rep_t a, rep_t b, rep_t *hi, rep_t *lo) { #define quietBit (implicitBit >> 1) #define qnanRep (exponentMask | quietBit) -static inline rep_t toRep(fp_t x) { +static __inline rep_t toRep(fp_t x) { const union { fp_t f; rep_t i; } rep = {.f = x}; return rep.i; } -static inline fp_t fromRep(rep_t x) { +static __inline fp_t fromRep(rep_t x) { const union { fp_t f; rep_t i; } rep = {.i = x}; return rep.f; } -static inline int normalize(rep_t *significand) { +static __inline int normalize(rep_t *significand) { const int shift = rep_clz(*significand) - rep_clz(implicitBit); *significand <<= shift; return 1 - shift; } -static inline void wideLeftShift(rep_t *hi, rep_t *lo, int count) { +static __inline void wideLeftShift(rep_t *hi, rep_t *lo, int count) { *hi = *hi << count | *lo >> (typeWidth - count); *lo = *lo << count; } -static inline void wideRightShiftWithSticky(rep_t *hi, rep_t *lo, unsigned int count) { +static __inline void wideRightShiftWithSticky(rep_t *hi, rep_t *lo, unsigned int count) { if (count < typeWidth) { const bool sticky = *lo << (typeWidth - count); *lo = *hi << (typeWidth - count) | *lo >> count | sticky; diff --git a/contrib/compiler-rt/lib/builtins/fp_mul_impl.inc b/contrib/compiler-rt/lib/builtins/fp_mul_impl.inc index ca8a0bb98b10..b34aa1b8f544 100644 --- a/contrib/compiler-rt/lib/builtins/fp_mul_impl.inc +++ b/contrib/compiler-rt/lib/builtins/fp_mul_impl.inc @@ -14,7 +14,7 @@ #include "fp_lib.h" -static inline fp_t __mulXf3__(fp_t a, fp_t b) { +static __inline fp_t __mulXf3__(fp_t a, fp_t b) { const unsigned int aExponent = toRep(a) >> significandBits & maxExponent; const unsigned int bExponent = toRep(b) >> significandBits & maxExponent; const rep_t productSign = (toRep(a) ^ toRep(b)) & signBit; diff --git a/contrib/compiler-rt/lib/builtins/fp_trunc.h b/contrib/compiler-rt/lib/builtins/fp_trunc.h index 373ba1b0411d..d5e79bb5b863 100644 --- a/contrib/compiler-rt/lib/builtins/fp_trunc.h +++ b/contrib/compiler-rt/lib/builtins/fp_trunc.h @@ -63,12 +63,12 @@ static const int dstSigBits = 10; // End of specialization parameters. Two helper routines for conversion to and // from the representation of floating-point data as integer values follow. -static inline src_rep_t srcToRep(src_t x) { +static __inline src_rep_t srcToRep(src_t x) { const union { src_t f; src_rep_t i; } rep = {.f = x}; return rep.i; } -static inline dst_t dstFromRep(dst_rep_t x) { +static __inline dst_t dstFromRep(dst_rep_t x) { const union { dst_t f; dst_rep_t i; } rep = {.i = x}; return rep.f; } diff --git a/contrib/compiler-rt/lib/builtins/fp_trunc_impl.inc b/contrib/compiler-rt/lib/builtins/fp_trunc_impl.inc index 372e8d6014dd..d88ae060913f 100644 --- a/contrib/compiler-rt/lib/builtins/fp_trunc_impl.inc +++ b/contrib/compiler-rt/lib/builtins/fp_trunc_impl.inc @@ -39,7 +39,7 @@ #include "fp_trunc.h" -static inline dst_t __truncXfYf2__(src_t a) { +static __inline dst_t __truncXfYf2__(src_t a) { // Various constants whose values follow from the type parameters. // Any reasonable optimizer will fold and propagate all of these. const int srcBits = sizeof(src_t)*CHAR_BIT; diff --git a/contrib/compiler-rt/lib/builtins/gcc_personality_v0.c b/contrib/compiler-rt/lib/builtins/gcc_personality_v0.c index 4b95cfd43b05..ed544d30b809 100644 --- a/contrib/compiler-rt/lib/builtins/gcc_personality_v0.c +++ b/contrib/compiler-rt/lib/builtins/gcc_personality_v0.c @@ -141,7 +141,8 @@ static uintptr_t readEncodedPointer(const uint8_t** data, uint8_t encoding) * throw through a C function compiled with -fexceptions. */ #if __USING_SJLJ_EXCEPTIONS__ -// the setjump-longjump based exceptions personality routine has a different name +/* the setjump-longjump based exceptions personality routine has a + * different name */ COMPILER_RT_ABI _Unwind_Reason_Code __gcc_personality_sj0(int version, _Unwind_Action actions, uint64_t exceptionClass, struct _Unwind_Exception* exceptionObject, @@ -194,15 +195,15 @@ __gcc_personality_v0(int version, _Unwind_Action actions, * Set Instruction Pointer to so we re-enter function * at landing pad. The landing pad is created by the compiler * to take two parameters in registers. - */ - _Unwind_SetGR(context, __builtin_eh_return_data_regno(0), - (uintptr_t)exceptionObject); + */ + _Unwind_SetGR(context, __builtin_eh_return_data_regno(0), + (uintptr_t)exceptionObject); _Unwind_SetGR(context, __builtin_eh_return_data_regno(1), 0); - _Unwind_SetIP(context, funcStart+landingPad); + _Unwind_SetIP(context, (funcStart + landingPad)); return _URC_INSTALL_CONTEXT; } } - + /* No landing pad found, continue unwinding. */ return _URC_CONTINUE_UNWIND; } diff --git a/contrib/compiler-rt/lib/builtins/i386/chkstk.S b/contrib/compiler-rt/lib/builtins/i386/chkstk.S index 3733d722ef19..b59974868f21 100644 --- a/contrib/compiler-rt/lib/builtins/i386/chkstk.S +++ b/contrib/compiler-rt/lib/builtins/i386/chkstk.S @@ -19,13 +19,13 @@ DEFINE_COMPILERRT_FUNCTION(__chkstk_ms) jb 1f 2: sub $0x1000,%ecx - orl $0,(%ecx) + test %ecx,(%ecx) sub $0x1000,%eax cmp $0x1000,%eax ja 2b 1: sub %eax,%ecx - orl $0,(%ecx) + test %ecx,(%ecx) pop %eax pop %ecx ret diff --git a/contrib/compiler-rt/lib/builtins/i386/chkstk2.S b/contrib/compiler-rt/lib/builtins/i386/chkstk2.S new file mode 100644 index 000000000000..7d65bb088928 --- /dev/null +++ b/contrib/compiler-rt/lib/builtins/i386/chkstk2.S @@ -0,0 +1,40 @@ +// This file is dual licensed under the MIT and the University of Illinois Open +// Source Licenses. See LICENSE.TXT for details. + +#include "../assembly.h" + +#ifdef __i386__ + +// _chkstk (_alloca) routine - probe stack between %esp and (%esp-%eax) in 4k increments, +// then decrement %esp by %eax. Preserves all registers except %esp and flags. +// This routine is windows specific +// http://msdn.microsoft.com/en-us/library/ms648426.aspx + +.text +.balign 4 +DEFINE_COMPILERRT_FUNCTION(_alloca) // _chkstk and _alloca are the same function +DEFINE_COMPILERRT_FUNCTION(__chkstk) + push %ecx + cmp $0x1000,%eax + lea 8(%esp),%ecx // esp before calling this routine -> ecx + jb 1f +2: + sub $0x1000,%ecx + test %ecx,(%ecx) + sub $0x1000,%eax + cmp $0x1000,%eax + ja 2b +1: + sub %eax,%ecx + test %ecx,(%ecx) + + lea 4(%esp),%eax // load pointer to the return address into eax + mov %ecx,%esp // install the new top of stack pointer into esp + mov -4(%eax),%ecx // restore ecx + push (%eax) // push return address onto the stack + sub %esp,%eax // restore the original value in eax + ret +END_COMPILERRT_FUNCTION(__chkstk) +END_COMPILERRT_FUNCTION(_alloca) + +#endif // __i386__ diff --git a/contrib/compiler-rt/lib/builtins/int_lib.h b/contrib/compiler-rt/lib/builtins/int_lib.h index 985534ddfa07..0fb03ff0e31f 100644 --- a/contrib/compiler-rt/lib/builtins/int_lib.h +++ b/contrib/compiler-rt/lib/builtins/int_lib.h @@ -20,6 +20,13 @@ /* Assumption: Right shift of signed negative is arithmetic shift. */ /* Assumption: Endianness is little or big (not mixed). */ +#if defined(__ELF__) +#define FNALIAS(alias_name, original_name) \ + void alias_name() __attribute__((alias(#original_name))) +#else +#define FNALIAS(alias, name) _Pragma("GCC error(\"alias unsupported on this file format\")") +#endif + /* ABI macro definitions */ #if __ARM_EABI__ @@ -36,13 +43,25 @@ #else # define ARM_EABI_FNALIAS(aeabi_name, name) -# if defined(__arm__) && defined(_WIN32) +# if defined(__arm__) && defined(_WIN32) && (!defined(_MSC_VER) || defined(__clang__)) # define COMPILER_RT_ABI __attribute__((pcs("aapcs"))) # else # define COMPILER_RT_ABI # endif #endif +#ifdef _MSC_VER +#define ALWAYS_INLINE __forceinline +#define NOINLINE __declspec(noinline) +#define NORETURN __declspec(noreturn) +#define UNUSED +#else +#define ALWAYS_INLINE __attribute__((always_inline)) +#define NOINLINE __attribute__((noinline)) +#define NORETURN __attribute__((noreturn)) +#define UNUSED __attribute__((unused)) +#endif + #if defined(__NetBSD__) && (defined(_KERNEL) || defined(_STANDALONE)) /* * Kernel and boot environment can't use normal headers, @@ -101,4 +120,44 @@ COMPILER_RT_ABI si_int __clzti2(ti_int a); COMPILER_RT_ABI tu_int __udivmodti4(tu_int a, tu_int b, tu_int* rem); #endif +/* Definitions for builtins unavailable on MSVC */ +#if defined(_MSC_VER) && !defined(__clang__) +#include <intrin.h> + +uint32_t __inline __builtin_ctz(uint32_t value) { + uint32_t trailing_zero = 0; + if (_BitScanForward(&trailing_zero, value)) + return trailing_zero; + return 32; +} + +uint32_t __inline __builtin_clz(uint32_t value) { + uint32_t leading_zero = 0; + if (_BitScanReverse(&leading_zero, value)) + return 31 - leading_zero; + return 32; +} + +#if defined(_M_ARM) || defined(_M_X64) +uint32_t __inline __builtin_clzll(uint64_t value) { + uint32_t leading_zero = 0; + if (_BitScanReverse64(&leading_zero, value)) + return 63 - leading_zero; + return 64; +} +#else +uint32_t __inline __builtin_clzll(uint64_t value) { + if (value == 0) + return 64; + uint32_t msh = (uint32_t)(value >> 32); + uint32_t lsh = (uint32_t)(value & 0xFFFFFFFF); + if (msh != 0) + return __builtin_clz(msh); + return 32 + __builtin_clz(lsh); +} +#endif + +#define __builtin_clzl __builtin_clzll +#endif /* defined(_MSC_VER) && !defined(__clang__) */ + #endif /* INT_LIB_H */ diff --git a/contrib/compiler-rt/lib/builtins/int_math.h b/contrib/compiler-rt/lib/builtins/int_math.h index d6b4bdae162b..fc81fb7f0220 100644 --- a/contrib/compiler-rt/lib/builtins/int_math.h +++ b/contrib/compiler-rt/lib/builtins/int_math.h @@ -25,43 +25,90 @@ # define __has_builtin(x) 0 #endif -#define CRT_INFINITY __builtin_huge_valf() +#if defined(_MSC_VER) && !defined(__clang__) +#include <math.h> +#include <stdlib.h> +#include <ymath.h> +#endif -#define crt_isinf(x) __builtin_isinf((x)) -#define crt_isnan(x) __builtin_isnan((x)) +#if defined(_MSC_VER) && !defined(__clang__) +#define CRT_INFINITY INFINITY +#else +#define CRT_INFINITY __builtin_huge_valf() +#endif +#if defined(_MSC_VER) && !defined(__clang__) +#define crt_isfinite(x) _finite((x)) +#define crt_isinf(x) !_finite((x)) +#define crt_isnan(x) _isnan((x)) +#else /* Define crt_isfinite in terms of the builtin if available, otherwise provide * an alternate version in terms of our other functions. This supports some * versions of GCC which didn't have __builtin_isfinite. */ #if __has_builtin(__builtin_isfinite) # define crt_isfinite(x) __builtin_isfinite((x)) -#else +#elif defined(__GNUC__) # define crt_isfinite(x) \ __extension__(({ \ __typeof((x)) x_ = (x); \ !crt_isinf(x_) && !crt_isnan(x_); \ })) -#endif +#else +# error "Do not know how to check for infinity" +#endif /* __has_builtin(__builtin_isfinite) */ +#define crt_isinf(x) __builtin_isinf((x)) +#define crt_isnan(x) __builtin_isnan((x)) +#endif /* _MSC_VER */ +#if defined(_MSC_VER) && !defined(__clang__) +#define crt_copysign(x, y) copysign((x), (y)) +#define crt_copysignf(x, y) copysignf((x), (y)) +#define crt_copysignl(x, y) copysignl((x), (y)) +#else #define crt_copysign(x, y) __builtin_copysign((x), (y)) #define crt_copysignf(x, y) __builtin_copysignf((x), (y)) #define crt_copysignl(x, y) __builtin_copysignl((x), (y)) +#endif +#if defined(_MSC_VER) && !defined(__clang__) +#define crt_fabs(x) fabs((x)) +#define crt_fabsf(x) fabsf((x)) +#define crt_fabsl(x) fabs((x)) +#else #define crt_fabs(x) __builtin_fabs((x)) #define crt_fabsf(x) __builtin_fabsf((x)) #define crt_fabsl(x) __builtin_fabsl((x)) +#endif +#if defined(_MSC_VER) && !defined(__clang__) +#define crt_fmax(x, y) __max((x), (y)) +#define crt_fmaxf(x, y) __max((x), (y)) +#define crt_fmaxl(x, y) __max((x), (y)) +#else #define crt_fmax(x, y) __builtin_fmax((x), (y)) #define crt_fmaxf(x, y) __builtin_fmaxf((x), (y)) #define crt_fmaxl(x, y) __builtin_fmaxl((x), (y)) +#endif +#if defined(_MSC_VER) && !defined(__clang__) +#define crt_logb(x) logb((x)) +#define crt_logbf(x) logbf((x)) +#define crt_logbl(x) logbl((x)) +#else #define crt_logb(x) __builtin_logb((x)) #define crt_logbf(x) __builtin_logbf((x)) #define crt_logbl(x) __builtin_logbl((x)) +#endif +#if defined(_MSC_VER) && !defined(__clang__) +#define crt_scalbn(x, y) scalbn((x), (y)) +#define crt_scalbnf(x, y) scalbnf((x), (y)) +#define crt_scalbnl(x, y) scalbnl((x), (y)) +#else #define crt_scalbn(x, y) __builtin_scalbn((x), (y)) #define crt_scalbnf(x, y) __builtin_scalbnf((x), (y)) #define crt_scalbnl(x, y) __builtin_scalbnl((x), (y)) +#endif #endif /* INT_MATH_H */ diff --git a/contrib/compiler-rt/lib/builtins/int_types.h b/contrib/compiler-rt/lib/builtins/int_types.h index aedae14b2046..660385ecd6ae 100644 --- a/contrib/compiler-rt/lib/builtins/int_types.h +++ b/contrib/compiler-rt/lib/builtins/int_types.h @@ -20,6 +20,10 @@ #include "int_endianness.h" +/* si_int is defined in Linux sysroot's asm-generic/siginfo.h */ +#ifdef si_int +#undef si_int +#endif typedef int si_int; typedef unsigned su_int; @@ -57,7 +61,8 @@ typedef union } udwords; /* MIPS64 issue: PR 20098 */ -#if defined(__LP64__) && !(defined(__mips__) && defined(__clang__)) +#if (defined(__LP64__) || defined(__wasm__)) && \ + !(defined(__mips__) && defined(__clang__)) #define CRT_HAS_128BIT #endif @@ -95,14 +100,14 @@ typedef union }s; } utwords; -static inline ti_int make_ti(di_int h, di_int l) { +static __inline ti_int make_ti(di_int h, di_int l) { twords r; r.s.high = h; r.s.low = l; return r.all; } -static inline tu_int make_tu(du_int h, du_int l) { +static __inline tu_int make_tu(du_int h, du_int l) { utwords r; r.s.high = h; r.s.low = l; @@ -140,5 +145,22 @@ typedef union long double f; } long_double_bits; +#if __STDC_VERSION__ >= 199901L +typedef float _Complex Fcomplex; +typedef double _Complex Dcomplex; +typedef long double _Complex Lcomplex; + +#define COMPLEX_REAL(x) __real__(x) +#define COMPLEX_IMAGINARY(x) __imag__(x) +#else +typedef struct { float real, imaginary; } Fcomplex; + +typedef struct { double real, imaginary; } Dcomplex; + +typedef struct { long double real, imaginary; } Lcomplex; + +#define COMPLEX_REAL(x) (x).real +#define COMPLEX_IMAGINARY(x) (x).imaginary +#endif #endif /* INT_TYPES_H */ diff --git a/contrib/compiler-rt/lib/builtins/int_util.c b/contrib/compiler-rt/lib/builtins/int_util.c index 323e46179e6c..420d1e237aae 100644 --- a/contrib/compiler-rt/lib/builtins/int_util.c +++ b/contrib/compiler-rt/lib/builtins/int_util.c @@ -8,8 +8,8 @@ * ===----------------------------------------------------------------------=== */ -#include "int_util.h" #include "int_lib.h" +#include "int_util.h" /* NOTE: The definitions in this file are declared weak because we clients to be * able to arbitrarily package individual functions into separate .a files. If @@ -23,7 +23,7 @@ #ifdef KERNEL_USE -extern void panic(const char *, ...) __attribute__((noreturn)); +NORETURN extern void panic(const char *, ...); #ifndef _WIN32 __attribute__((visibility("hidden"))) #endif @@ -34,8 +34,8 @@ void compilerrt_abort_impl(const char *file, int line, const char *function) { #elif __APPLE__ /* from libSystem.dylib */ -extern void __assert_rtn(const char *func, const char *file, - int line, const char * message) __attribute__((noreturn)); +NORETURN extern void __assert_rtn(const char *func, const char *file, int line, + const char *message); #ifndef _WIN32 __attribute__((weak)) diff --git a/contrib/compiler-rt/lib/builtins/int_util.h b/contrib/compiler-rt/lib/builtins/int_util.h index a9b595db8d0f..a7b20ed66244 100644 --- a/contrib/compiler-rt/lib/builtins/int_util.h +++ b/contrib/compiler-rt/lib/builtins/int_util.h @@ -20,10 +20,14 @@ #define INT_UTIL_H /** \brief Trigger a program abort (or panic for kernel code). */ -#define compilerrt_abort() compilerrt_abort_impl(__FILE__, __LINE__, \ - __func__) +#define compilerrt_abort() compilerrt_abort_impl(__FILE__, __LINE__, __func__) -void compilerrt_abort_impl(const char *file, int line, - const char *function) __attribute__((noreturn)); +NORETURN void compilerrt_abort_impl(const char *file, int line, + const char *function); + +#define COMPILE_TIME_ASSERT(expr) COMPILE_TIME_ASSERT1(expr, __COUNTER__) +#define COMPILE_TIME_ASSERT1(expr, cnt) COMPILE_TIME_ASSERT2(expr, cnt) +#define COMPILE_TIME_ASSERT2(expr, cnt) \ + typedef char ct_assert_##cnt[(expr) ? 1 : -1] UNUSED #endif /* INT_UTIL_H */ diff --git a/contrib/compiler-rt/lib/builtins/muldc3.c b/contrib/compiler-rt/lib/builtins/muldc3.c index 3bfae2c52224..16d8e98390a3 100644 --- a/contrib/compiler-rt/lib/builtins/muldc3.c +++ b/contrib/compiler-rt/lib/builtins/muldc3.c @@ -17,17 +17,17 @@ /* Returns: the product of a + ib and c + id */ -COMPILER_RT_ABI double _Complex +COMPILER_RT_ABI Dcomplex __muldc3(double __a, double __b, double __c, double __d) { double __ac = __a * __c; double __bd = __b * __d; double __ad = __a * __d; double __bc = __b * __c; - double _Complex z; - __real__ z = __ac - __bd; - __imag__ z = __ad + __bc; - if (crt_isnan(__real__ z) && crt_isnan(__imag__ z)) + Dcomplex z; + COMPLEX_REAL(z) = __ac - __bd; + COMPLEX_IMAGINARY(z) = __ad + __bc; + if (crt_isnan(COMPLEX_REAL(z)) && crt_isnan(COMPLEX_IMAGINARY(z))) { int __recalc = 0; if (crt_isinf(__a) || crt_isinf(__b)) @@ -65,8 +65,8 @@ __muldc3(double __a, double __b, double __c, double __d) } if (__recalc) { - __real__ z = CRT_INFINITY * (__a * __c - __b * __d); - __imag__ z = CRT_INFINITY * (__a * __d + __b * __c); + COMPLEX_REAL(z) = CRT_INFINITY * (__a * __c - __b * __d); + COMPLEX_IMAGINARY(z) = CRT_INFINITY * (__a * __d + __b * __c); } } return z; diff --git a/contrib/compiler-rt/lib/builtins/mulsc3.c b/contrib/compiler-rt/lib/builtins/mulsc3.c index 29d46c63a799..c89cfd247a15 100644 --- a/contrib/compiler-rt/lib/builtins/mulsc3.c +++ b/contrib/compiler-rt/lib/builtins/mulsc3.c @@ -17,17 +17,17 @@ /* Returns: the product of a + ib and c + id */ -COMPILER_RT_ABI float _Complex +COMPILER_RT_ABI Fcomplex __mulsc3(float __a, float __b, float __c, float __d) { float __ac = __a * __c; float __bd = __b * __d; float __ad = __a * __d; float __bc = __b * __c; - float _Complex z; - __real__ z = __ac - __bd; - __imag__ z = __ad + __bc; - if (crt_isnan(__real__ z) && crt_isnan(__imag__ z)) + Fcomplex z; + COMPLEX_REAL(z) = __ac - __bd; + COMPLEX_IMAGINARY(z) = __ad + __bc; + if (crt_isnan(COMPLEX_REAL(z)) && crt_isnan(COMPLEX_IMAGINARY(z))) { int __recalc = 0; if (crt_isinf(__a) || crt_isinf(__b)) @@ -65,8 +65,8 @@ __mulsc3(float __a, float __b, float __c, float __d) } if (__recalc) { - __real__ z = CRT_INFINITY * (__a * __c - __b * __d); - __imag__ z = CRT_INFINITY * (__a * __d + __b * __c); + COMPLEX_REAL(z) = CRT_INFINITY * (__a * __c - __b * __d); + COMPLEX_IMAGINARY(z) = CRT_INFINITY * (__a * __d + __b * __c); } } return z; diff --git a/contrib/compiler-rt/lib/builtins/mulxc3.c b/contrib/compiler-rt/lib/builtins/mulxc3.c index 161fd0ce0dd4..ba3221691821 100644 --- a/contrib/compiler-rt/lib/builtins/mulxc3.c +++ b/contrib/compiler-rt/lib/builtins/mulxc3.c @@ -19,17 +19,17 @@ /* Returns: the product of a + ib and c + id */ -COMPILER_RT_ABI long double _Complex +COMPILER_RT_ABI Lcomplex __mulxc3(long double __a, long double __b, long double __c, long double __d) { long double __ac = __a * __c; long double __bd = __b * __d; long double __ad = __a * __d; long double __bc = __b * __c; - long double _Complex z; - __real__ z = __ac - __bd; - __imag__ z = __ad + __bc; - if (crt_isnan(__real__ z) && crt_isnan(__imag__ z)) + Lcomplex z; + COMPLEX_REAL(z) = __ac - __bd; + COMPLEX_IMAGINARY(z) = __ad + __bc; + if (crt_isnan(COMPLEX_REAL(z)) && crt_isnan(COMPLEX_IMAGINARY(z))) { int __recalc = 0; if (crt_isinf(__a) || crt_isinf(__b)) @@ -67,8 +67,8 @@ __mulxc3(long double __a, long double __b, long double __c, long double __d) } if (__recalc) { - __real__ z = CRT_INFINITY * (__a * __c - __b * __d); - __imag__ z = CRT_INFINITY * (__a * __d + __b * __c); + COMPLEX_REAL(z) = CRT_INFINITY * (__a * __c - __b * __d); + COMPLEX_IMAGINARY(z) = CRT_INFINITY * (__a * __d + __b * __c); } } return z; diff --git a/contrib/compiler-rt/lib/builtins/ppc/DD.h b/contrib/compiler-rt/lib/builtins/ppc/DD.h index fc3e41cbe07e..3e5f9e58c138 100644 --- a/contrib/compiler-rt/lib/builtins/ppc/DD.h +++ b/contrib/compiler-rt/lib/builtins/ppc/DD.h @@ -1,5 +1,5 @@ -#ifndef __DD_HEADER -#define __DD_HEADER +#ifndef COMPILERRT_DD_HEADER +#define COMPILERRT_DD_HEADER #include "../int_lib.h" @@ -9,7 +9,7 @@ typedef union { double hi; double lo; }s; -}DD; +} DD; typedef union { double d; @@ -19,28 +19,27 @@ typedef union { #define LOWORDER(xy,xHi,xLo,yHi,yLo) \ (((((xHi)*(yHi) - (xy)) + (xHi)*(yLo)) + (xLo)*(yHi)) + (xLo)*(yLo)) -static inline double __attribute__((always_inline)) -local_fabs(double x) -{ - doublebits result = { .d = x }; - result.x &= UINT64_C(0x7fffffffffffffff); - return result.d; +static __inline ALWAYS_INLINE double local_fabs(double x) { + doublebits result = {.d = x}; + result.x &= UINT64_C(0x7fffffffffffffff); + return result.d; } -static inline double __attribute__((always_inline)) -high26bits(double x) -{ - doublebits result = { .d = x }; - result.x &= UINT64_C(0xfffffffff8000000); - return result.d; +static __inline ALWAYS_INLINE double high26bits(double x) { + doublebits result = {.d = x}; + result.x &= UINT64_C(0xfffffffff8000000); + return result.d; } -static inline int __attribute__((always_inline)) -different_sign(double x, double y) -{ - doublebits xsignbit = { .d = x }, ysignbit = { .d = y }; - int result = (int)(xsignbit.x >> 63) ^ (int)(ysignbit.x >> 63); - return result; +static __inline ALWAYS_INLINE int different_sign(double x, double y) { + doublebits xsignbit = {.d = x}, ysignbit = {.d = y}; + int result = (int)(xsignbit.x >> 63) ^ (int)(ysignbit.x >> 63); + return result; } -#endif /* __DD_HEADER */ +long double __gcc_qadd(long double, long double); +long double __gcc_qsub(long double, long double); +long double __gcc_qmul(long double, long double); +long double __gcc_qdiv(long double, long double); + +#endif /* COMPILERRT_DD_HEADER */ diff --git a/contrib/compiler-rt/lib/builtins/ppc/divtc3.c b/contrib/compiler-rt/lib/builtins/ppc/divtc3.c index 299128186312..8ec41c528ab9 100644 --- a/contrib/compiler-rt/lib/builtins/ppc/divtc3.c +++ b/contrib/compiler-rt/lib/builtins/ppc/divtc3.c @@ -14,11 +14,6 @@ (x).s.lo = 0.0; \ } -long double __gcc_qadd(long double, long double); -long double __gcc_qsub(long double, long double); -long double __gcc_qmul(long double, long double); -long double __gcc_qdiv(long double, long double); - long double _Complex __divtc3(long double a, long double b, long double c, long double d) { diff --git a/contrib/compiler-rt/lib/builtins/ppc/multc3.c b/contrib/compiler-rt/lib/builtins/ppc/multc3.c index 738b65a83b03..9dd79c975dde 100644 --- a/contrib/compiler-rt/lib/builtins/ppc/multc3.c +++ b/contrib/compiler-rt/lib/builtins/ppc/multc3.c @@ -17,10 +17,6 @@ } \ } -long double __gcc_qadd(long double, long double); -long double __gcc_qsub(long double, long double); -long double __gcc_qmul(long double, long double); - long double _Complex __multc3(long double a, long double b, long double c, long double d) { diff --git a/contrib/compiler-rt/lib/builtins/subdf3.c b/contrib/compiler-rt/lib/builtins/subdf3.c index 089e062415b7..7a79e5e7765d 100644 --- a/contrib/compiler-rt/lib/builtins/subdf3.c +++ b/contrib/compiler-rt/lib/builtins/subdf3.c @@ -23,4 +23,3 @@ __subdf3(fp_t a, fp_t b) { return __adddf3(a, fromRep(toRep(b) ^ signBit)); } -/* FIXME: rsub for ARM EABI */ diff --git a/contrib/compiler-rt/lib/builtins/subsf3.c b/contrib/compiler-rt/lib/builtins/subsf3.c index 47f5e5e46ea8..c3b85144af48 100644 --- a/contrib/compiler-rt/lib/builtins/subsf3.c +++ b/contrib/compiler-rt/lib/builtins/subsf3.c @@ -23,4 +23,3 @@ __subsf3(fp_t a, fp_t b) { return __addsf3(a, fromRep(toRep(b) ^ signBit)); } -/* FIXME: rsub for ARM EABI */ diff --git a/contrib/compiler-rt/lib/builtins/truncdfhf2.c b/contrib/compiler-rt/lib/builtins/truncdfhf2.c index 0852df369625..17195cd9e799 100644 --- a/contrib/compiler-rt/lib/builtins/truncdfhf2.c +++ b/contrib/compiler-rt/lib/builtins/truncdfhf2.c @@ -11,6 +11,8 @@ #define DST_HALF #include "fp_trunc_impl.inc" +ARM_EABI_FNALIAS(d2h, truncdfhf2) + COMPILER_RT_ABI uint16_t __truncdfhf2(double a) { return __truncXfYf2__(a); } diff --git a/contrib/compiler-rt/lib/builtins/truncsfhf2.c b/contrib/compiler-rt/lib/builtins/truncsfhf2.c index 381e590c342f..9d61895bfd88 100644 --- a/contrib/compiler-rt/lib/builtins/truncsfhf2.c +++ b/contrib/compiler-rt/lib/builtins/truncsfhf2.c @@ -11,9 +11,11 @@ #define DST_HALF #include "fp_trunc_impl.inc" +ARM_EABI_FNALIAS(f2h, truncsfhf2) + // Use a forwarding definition and noinline to implement a poor man's alias, // as there isn't a good cross-platform way of defining one. -COMPILER_RT_ABI __attribute__((noinline)) uint16_t __truncsfhf2(float a) { +COMPILER_RT_ABI NOINLINE uint16_t __truncsfhf2(float a) { return __truncXfYf2__(a); } diff --git a/contrib/compiler-rt/lib/builtins/x86_64/chkstk.S b/contrib/compiler-rt/lib/builtins/x86_64/chkstk.S index 5759e84498c6..4149ac63d9d0 100644 --- a/contrib/compiler-rt/lib/builtins/x86_64/chkstk.S +++ b/contrib/compiler-rt/lib/builtins/x86_64/chkstk.S @@ -24,13 +24,13 @@ DEFINE_COMPILERRT_FUNCTION(___chkstk_ms) jb 1f 2: sub $0x1000,%rcx - orl $0,(%rcx) + test %rcx,(%rcx) sub $0x1000,%rax cmp $0x1000,%rax ja 2b 1: sub %rax,%rcx - orl $0,(%rcx) + test %rcx,(%rcx) pop %rax pop %rcx ret diff --git a/contrib/compiler-rt/lib/builtins/x86_64/chkstk2.S b/contrib/compiler-rt/lib/builtins/x86_64/chkstk2.S new file mode 100644 index 000000000000..ac1eb920e0e8 --- /dev/null +++ b/contrib/compiler-rt/lib/builtins/x86_64/chkstk2.S @@ -0,0 +1,42 @@ +// This file is dual licensed under the MIT and the University of Illinois Open +// Source Licenses. See LICENSE.TXT for details. + +#include "../assembly.h" + +#ifdef __x86_64__ + +// _chkstk (_alloca) routine - probe stack between %rsp and (%rsp-%rax) in 4k increments, +// then decrement %rsp by %rax. Preserves all registers except %rsp and flags. +// This routine is windows specific +// http://msdn.microsoft.com/en-us/library/ms648426.aspx + +.text +.balign 4 +DEFINE_COMPILERRT_FUNCTION(__alloca) + mov %rcx,%rax // x64 _alloca is a normal function with parameter in rcx + // fallthrough +DEFINE_COMPILERRT_FUNCTION(___chkstk) + push %rcx + cmp $0x1000,%rax + lea 16(%rsp),%rcx // rsp before calling this routine -> rcx + jb 1f +2: + sub $0x1000,%rcx + test %rcx,(%rcx) + sub $0x1000,%rax + cmp $0x1000,%rax + ja 2b +1: + sub %rax,%rcx + test %rcx,(%rcx) + + lea 8(%rsp),%rax // load pointer to the return address into rax + mov %rcx,%rsp // install the new top of stack pointer into rsp + mov -8(%rax),%rcx // restore rcx + push (%rax) // push return address onto the stack + sub %rsp,%rax // restore the original value in rax + ret +END_COMPILERRT_FUNCTION(___chkstk) +END_COMPILERRT_FUNCTION(__alloca) + +#endif // __x86_64__ diff --git a/contrib/compiler-rt/lib/cfi/cfi.cc b/contrib/compiler-rt/lib/cfi/cfi.cc new file mode 100644 index 000000000000..711866f3fa0c --- /dev/null +++ b/contrib/compiler-rt/lib/cfi/cfi.cc @@ -0,0 +1,271 @@ +//===-------- cfi.cc ------------------------------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file implements the runtime support for the cross-DSO CFI. +// +//===----------------------------------------------------------------------===// + +// FIXME: Intercept dlopen/dlclose. +// FIXME: Support diagnostic mode. +// FIXME: Harden: +// * mprotect shadow, use mremap for updates +// * something else equally important + +#include <assert.h> +#include <elf.h> +#include <link.h> +#include <string.h> + +typedef ElfW(Phdr) Elf_Phdr; +typedef ElfW(Ehdr) Elf_Ehdr; + +#include "interception/interception.h" +#include "sanitizer_common/sanitizer_common.h" +#include "sanitizer_common/sanitizer_flag_parser.h" +#include "ubsan/ubsan_init.h" +#include "ubsan/ubsan_flags.h" + +static uptr __cfi_shadow; +static constexpr uptr kShadowGranularity = 12; +static constexpr uptr kShadowAlign = 1UL << kShadowGranularity; // 4096 + +static constexpr uint16_t kInvalidShadow = 0; +static constexpr uint16_t kUncheckedShadow = 0xFFFFU; + +static uint16_t *mem_to_shadow(uptr x) { + return (uint16_t *)(__cfi_shadow + ((x >> kShadowGranularity) << 1)); +} + +typedef int (*CFICheckFn)(u64, void *); + +class ShadowValue { + uptr addr; + uint16_t v; + explicit ShadowValue(uptr addr, uint16_t v) : addr(addr), v(v) {} + +public: + bool is_invalid() const { return v == kInvalidShadow; } + + bool is_unchecked() const { return v == kUncheckedShadow; } + + CFICheckFn get_cfi_check() const { + assert(!is_invalid() && !is_unchecked()); + uptr aligned_addr = addr & ~(kShadowAlign - 1); + uptr p = aligned_addr - (((uptr)v - 1) << kShadowGranularity); + return reinterpret_cast<CFICheckFn>(p); + } + + // Load a shadow valud for the given application memory address. + static const ShadowValue load(uptr addr) { + return ShadowValue(addr, *mem_to_shadow(addr)); + } +}; + +static void fill_shadow_constant(uptr begin, uptr end, uint16_t v) { + assert(v == kInvalidShadow || v == kUncheckedShadow); + uint16_t *shadow_begin = mem_to_shadow(begin); + uint16_t *shadow_end = mem_to_shadow(end - 1) + 1; + memset(shadow_begin, v, (shadow_end - shadow_begin) * sizeof(*shadow_begin)); +} + +static void fill_shadow(uptr begin, uptr end, uptr cfi_check) { + assert((cfi_check & (kShadowAlign - 1)) == 0); + + // Don't fill anything below cfi_check. We can not represent those addresses + // in the shadow, and must make sure at codegen to place all valid call + // targets above cfi_check. + uptr p = Max(begin, cfi_check); + uint16_t *s = mem_to_shadow(p); + uint16_t *s_end = mem_to_shadow(end - 1) + 1; + uint16_t sv = ((p - cfi_check) >> kShadowGranularity) + 1; + for (; s < s_end; s++, sv++) + *s = sv; + + // Sanity checks. + uptr q = p & ~(kShadowAlign - 1); + for (; q < end; q += kShadowAlign) { + assert((uptr)ShadowValue::load(q).get_cfi_check() == cfi_check); + assert((uptr)ShadowValue::load(q + kShadowAlign / 2).get_cfi_check() == + cfi_check); + assert((uptr)ShadowValue::load(q + kShadowAlign - 1).get_cfi_check() == + cfi_check); + } +} + +// This is a workaround for a glibc bug: +// https://sourceware.org/bugzilla/show_bug.cgi?id=15199 +// Other platforms can, hopefully, just do +// dlopen(RTLD_NOLOAD | RTLD_LAZY) +// dlsym("__cfi_check"). +static uptr find_cfi_check_in_dso(dl_phdr_info *info) { + const ElfW(Dyn) *dynamic = nullptr; + for (int i = 0; i < info->dlpi_phnum; ++i) { + if (info->dlpi_phdr[i].p_type == PT_DYNAMIC) { + dynamic = + (const ElfW(Dyn) *)(info->dlpi_addr + info->dlpi_phdr[i].p_vaddr); + break; + } + } + if (!dynamic) return 0; + uptr strtab = 0, symtab = 0; + for (const ElfW(Dyn) *p = dynamic; p->d_tag != PT_NULL; ++p) { + if (p->d_tag == DT_SYMTAB) + symtab = p->d_un.d_ptr; + else if (p->d_tag == DT_STRTAB) + strtab = p->d_un.d_ptr; + } + + if (symtab > strtab) { + VReport(1, "Can not handle: symtab > strtab (%p > %zx)\n", symtab, strtab); + return 0; + } + + // Verify that strtab and symtab are inside of the same LOAD segment. + // This excludes VDSO, which has (very high) bogus strtab and symtab pointers. + int phdr_idx; + for (phdr_idx = 0; phdr_idx < info->dlpi_phnum; phdr_idx++) { + const Elf_Phdr *phdr = &info->dlpi_phdr[phdr_idx]; + if (phdr->p_type == PT_LOAD) { + uptr beg = info->dlpi_addr + phdr->p_vaddr; + uptr end = beg + phdr->p_memsz; + if (strtab >= beg && strtab < end && symtab >= beg && symtab < end) + break; + } + } + if (phdr_idx == info->dlpi_phnum) { + // Nope, either different segments or just bogus pointers. + // Can not handle this. + VReport(1, "Can not handle: symtab %p, strtab %zx\n", symtab, strtab); + return 0; + } + + for (const ElfW(Sym) *p = (const ElfW(Sym) *)symtab; (ElfW(Addr))p < strtab; + ++p) { + char *name = (char*)(strtab + p->st_name); + if (strcmp(name, "__cfi_check") == 0) { + assert(p->st_info == ELF32_ST_INFO(STB_GLOBAL, STT_FUNC)); + uptr addr = info->dlpi_addr + p->st_value; + return addr; + } + } + return 0; +} + +static int dl_iterate_phdr_cb(dl_phdr_info *info, size_t size, void *data) { + uptr cfi_check = find_cfi_check_in_dso(info); + if (cfi_check) + VReport(1, "Module '%s' __cfi_check %zx\n", info->dlpi_name, cfi_check); + + for (int i = 0; i < info->dlpi_phnum; i++) { + const Elf_Phdr *phdr = &info->dlpi_phdr[i]; + if (phdr->p_type == PT_LOAD) { + // Jump tables are in the executable segment. + // VTables are in the non-executable one. + // Need to fill shadow for both. + // FIXME: reject writable if vtables are in the r/o segment. Depend on + // PT_RELRO? + uptr cur_beg = info->dlpi_addr + phdr->p_vaddr; + uptr cur_end = cur_beg + phdr->p_memsz; + if (cfi_check) { + VReport(1, " %zx .. %zx\n", cur_beg, cur_end); + fill_shadow(cur_beg, cur_end, cfi_check ? cfi_check : (uptr)(-1)); + } else { + fill_shadow_constant(cur_beg, cur_end, kUncheckedShadow); + } + } + } + return 0; +} + +// Fill shadow for the initial libraries. +static void init_shadow() { + dl_iterate_phdr(dl_iterate_phdr_cb, nullptr); +} + +extern "C" SANITIZER_INTERFACE_ATTRIBUTE +void __cfi_slowpath(u64 CallSiteTypeId, void *Ptr) { + uptr Addr = (uptr)Ptr; + VReport(3, "__cfi_slowpath: %llx, %p\n", CallSiteTypeId, Ptr); + ShadowValue sv = ShadowValue::load(Addr); + if (sv.is_invalid()) { + VReport(2, "CFI: invalid memory region for a function pointer (shadow==0): %p\n", Ptr); + Die(); + } + if (sv.is_unchecked()) { + VReport(2, "CFI: unchecked call (shadow=FFFF): %p\n", Ptr); + return; + } + CFICheckFn cfi_check = sv.get_cfi_check(); + VReport(2, "__cfi_check at %p\n", cfi_check); + cfi_check(CallSiteTypeId, Ptr); +} + +static void InitializeFlags() { + SetCommonFlagsDefaults(); +#ifdef CFI_ENABLE_DIAG + __ubsan::Flags *uf = __ubsan::flags(); + uf->SetDefaults(); +#endif + + FlagParser cfi_parser; + RegisterCommonFlags(&cfi_parser); + cfi_parser.ParseString(GetEnv("CFI_OPTIONS")); + +#ifdef CFI_ENABLE_DIAG + FlagParser ubsan_parser; + __ubsan::RegisterUbsanFlags(&ubsan_parser, uf); + RegisterCommonFlags(&ubsan_parser); + + const char *ubsan_default_options = __ubsan::MaybeCallUbsanDefaultOptions(); + ubsan_parser.ParseString(ubsan_default_options); + ubsan_parser.ParseString(GetEnv("UBSAN_OPTIONS")); +#endif + + SetVerbosity(common_flags()->verbosity); + + if (Verbosity()) ReportUnrecognizedFlags(); + + if (common_flags()->help) { + cfi_parser.PrintFlagDescriptions(); + } +} + +extern "C" SANITIZER_INTERFACE_ATTRIBUTE +#if !SANITIZER_CAN_USE_PREINIT_ARRAY +// On ELF platforms, the constructor is invoked using .preinit_array (see below) +__attribute__((constructor(0))) +#endif +void __cfi_init() { + SanitizerToolName = "CFI"; + InitializeFlags(); + + uptr vma = GetMaxVirtualAddress(); + // Shadow is 2 -> 2**kShadowGranularity. + uptr shadow_size = (vma >> (kShadowGranularity - 1)) + 1; + VReport(1, "CFI: VMA size %zx, shadow size %zx\n", vma, shadow_size); + void *shadow = MmapNoReserveOrDie(shadow_size, "CFI shadow"); + VReport(1, "CFI: shadow at %zx .. %zx\n", shadow, + reinterpret_cast<uptr>(shadow) + shadow_size); + __cfi_shadow = (uptr)shadow; + init_shadow(); + +#ifdef CFI_ENABLE_DIAG + __ubsan::InitAsPlugin(); +#endif +} + +#if SANITIZER_CAN_USE_PREINIT_ARRAY +// On ELF platforms, run cfi initialization before any other constructors. +// On other platforms we use the constructor attribute to arrange to run our +// initialization early. +extern "C" { +__attribute__((section(".preinit_array"), + used)) void (*__cfi_preinit)(void) = __cfi_init; +} +#endif diff --git a/contrib/compiler-rt/lib/cfi/cfi_blacklist.txt b/contrib/compiler-rt/lib/cfi/cfi_blacklist.txt new file mode 100644 index 000000000000..1f0eeb355617 --- /dev/null +++ b/contrib/compiler-rt/lib/cfi/cfi_blacklist.txt @@ -0,0 +1,26 @@ +# Standard library types. +type:std::* + +# The stdext namespace contains Microsoft standard library extensions. +type:stdext::* + +# Types with a uuid attribute, i.e. COM types. +type:attr:uuid + +# STL allocators (T *allocator<T *>::allocate(size_type, const void*)). +# The type signature mandates a cast from uninitialized void* to T*. +# size_type can either be unsigned int (j) or unsigned long (m). +fun:*8allocateEjPKv +fun:*8allocateEmPKv + +# std::get_temporary_buffer, likewise (libstdc++, libc++). +fun:_ZSt20get_temporary_buffer* +fun:_ZNSt3__120get_temporary_buffer* + +# STL address-of magic (libstdc++, libc++). +fun:*__addressof* +fun:_ZNSt3__19addressof* + +# Windows C++ stdlib headers that contain bad unrelated casts. +src:*xmemory0 +src:*xstddef diff --git a/contrib/compiler-rt/lib/dfsan/dfsan.cc b/contrib/compiler-rt/lib/dfsan/dfsan.cc index d2e137e129c1..7285f202d060 100644 --- a/contrib/compiler-rt/lib/dfsan/dfsan.cc +++ b/contrib/compiler-rt/lib/dfsan/dfsan.cc @@ -42,6 +42,8 @@ Flags __dfsan::flags_data; SANITIZER_INTERFACE_ATTRIBUTE THREADLOCAL dfsan_label __dfsan_retval_tls; SANITIZER_INTERFACE_ATTRIBUTE THREADLOCAL dfsan_label __dfsan_arg_tls[64]; +SANITIZER_INTERFACE_ATTRIBUTE uptr __dfsan_shadow_ptr_mask; + // On Linux/x86_64, memory is laid out as follows: // // +--------------------+ 0x800000000000 (top of memory) @@ -80,24 +82,52 @@ SANITIZER_INTERFACE_ATTRIBUTE THREADLOCAL dfsan_label __dfsan_arg_tls[64]; // | reserved by kernel | // +--------------------+ 0x0000000000 +// On Linux/AArch64 (39-bit VMA), memory is laid out as follow: +// +// +--------------------+ 0x8000000000 (top of memory) +// | application memory | +// +--------------------+ 0x7000008000 (kAppAddr) +// | | +// | unused | +// | | +// +--------------------+ 0x1200000000 (kUnusedAddr) +// | union table | +// +--------------------+ 0x1000000000 (kUnionTableAddr) +// | shadow memory | +// +--------------------+ 0x0000010000 (kShadowAddr) +// | reserved by kernel | +// +--------------------+ 0x0000000000 + +// On Linux/AArch64 (42-bit VMA), memory is laid out as follow: +// +// +--------------------+ 0x40000000000 (top of memory) +// | application memory | +// +--------------------+ 0x3ff00008000 (kAppAddr) +// | | +// | unused | +// | | +// +--------------------+ 0x1200000000 (kUnusedAddr) +// | union table | +// +--------------------+ 0x8000000000 (kUnionTableAddr) +// | shadow memory | +// +--------------------+ 0x0000010000 (kShadowAddr) +// | reserved by kernel | +// +--------------------+ 0x0000000000 + typedef atomic_dfsan_label dfsan_union_table_t[kNumLabels][kNumLabels]; -#if defined(__x86_64__) -static const uptr kShadowAddr = 0x10000; -static const uptr kUnionTableAddr = 0x200000000000; -static const uptr kUnusedAddr = kUnionTableAddr + sizeof(dfsan_union_table_t); -static const uptr kAppAddr = 0x700000008000; -#elif defined(__mips64) -static const uptr kShadowAddr = 0x10000; -static const uptr kUnionTableAddr = 0x2000000000; -static const uptr kUnusedAddr = kUnionTableAddr + sizeof(dfsan_union_table_t); -static const uptr kAppAddr = 0xF000008000; -#else -# error "DFSan not supported for this platform!" +#ifdef DFSAN_RUNTIME_VMA +// Runtime detected VMA size. +int __dfsan::vmaSize; #endif +static uptr UnusedAddr() { + return MappingArchImpl<MAPPING_UNION_TABLE_ADDR>() + + sizeof(dfsan_union_table_t); +} + static atomic_dfsan_label *union_table(dfsan_label l1, dfsan_label l2) { - return &(*(dfsan_union_table_t *) kUnionTableAddr)[l1][l2]; + return &(*(dfsan_union_table_t *) UnionTableAddr())[l1][l2]; } // Checks we do not run out of labels. @@ -325,10 +355,30 @@ static void RegisterDfsanFlags(FlagParser *parser, Flags *f) { } static void InitializeFlags() { + SetCommonFlagsDefaults(); + flags().SetDefaults(); + FlagParser parser; + RegisterCommonFlags(&parser); RegisterDfsanFlags(&parser, &flags()); - flags().SetDefaults(); parser.ParseString(GetEnv("DFSAN_OPTIONS")); + SetVerbosity(common_flags()->verbosity); + if (Verbosity()) ReportUnrecognizedFlags(); + if (common_flags()->help) parser.PrintFlagDescriptions(); +} + +static void InitializePlatformEarly() { +#ifdef DFSAN_RUNTIME_VMA + __dfsan::vmaSize = + (MostSignificantSetBitIndex(GET_CURRENT_FRAME()) + 1); + if (__dfsan::vmaSize == 39 || __dfsan::vmaSize == 42) { + __dfsan_shadow_ptr_mask = ShadowMask(); + } else { + Printf("FATAL: DataFlowSanitizer: unsupported VMA range\n"); + Printf("FATAL: Found %d - Supported 39 and 42\n", __dfsan::vmaSize); + Die(); + } +#endif } static void dfsan_fini() { @@ -347,12 +397,12 @@ static void dfsan_fini() { } } -#ifdef DFSAN_NOLIBC -extern "C" void dfsan_init() { -#else static void dfsan_init(int argc, char **argv, char **envp) { -#endif - MmapFixedNoReserve(kShadowAddr, kUnusedAddr - kShadowAddr); + InitializeFlags(); + + InitializePlatformEarly(); + + MmapFixedNoReserve(ShadowAddr(), UnusedAddr() - ShadowAddr()); // Protect the region of memory we don't use, to preserve the one-to-one // mapping from application to shadow memory. But if ASLR is disabled, Linux @@ -360,21 +410,20 @@ static void dfsan_init(int argc, char **argv, char **envp) { // works so long as the program doesn't use too much memory. We support this // case by disabling memory protection when ASLR is disabled. uptr init_addr = (uptr)&dfsan_init; - if (!(init_addr >= kUnusedAddr && init_addr < kAppAddr)) - MmapNoAccess(kUnusedAddr, kAppAddr - kUnusedAddr); + if (!(init_addr >= UnusedAddr() && init_addr < AppAddr())) + MmapNoAccess(UnusedAddr(), AppAddr() - UnusedAddr()); - InitializeFlags(); InitializeInterceptors(); // Register the fini callback to run when the program terminates successfully // or it is killed by the runtime. Atexit(dfsan_fini); - SetDieCallback(dfsan_fini); + AddDieCallback(dfsan_fini); __dfsan_label_info[kInitializingLabel].desc = "<init label>"; } -#if !defined(DFSAN_NOLIBC) && SANITIZER_CAN_USE_PREINIT_ARRAY +#if SANITIZER_CAN_USE_PREINIT_ARRAY __attribute__((section(".preinit_array"), used)) static void (*dfsan_init_ptr)(int, char **, char **) = dfsan_init; #endif diff --git a/contrib/compiler-rt/lib/dfsan/dfsan.h b/contrib/compiler-rt/lib/dfsan/dfsan.h index ceba3533a233..81f949e3019e 100644 --- a/contrib/compiler-rt/lib/dfsan/dfsan.h +++ b/contrib/compiler-rt/lib/dfsan/dfsan.h @@ -16,6 +16,7 @@ #define DFSAN_H #include "sanitizer_common/sanitizer_internal_defs.h" +#include "dfsan_platform.h" // Copy declarations from public sanitizer/dfsan_interface.h header here. typedef u16 dfsan_label; @@ -44,11 +45,7 @@ namespace __dfsan { void InitializeInterceptors(); inline dfsan_label *shadow_for(void *ptr) { -#if defined(__x86_64__) - return (dfsan_label *) ((((uptr) ptr) & ~0x700000000000) << 1); -#elif defined(__mips64) - return (dfsan_label *) ((((uptr) ptr) & ~0xF000000000) << 1); -#endif + return (dfsan_label *) ((((uptr) ptr) & ShadowMask()) << 1); } inline const dfsan_label *shadow_for(const void *ptr) { diff --git a/contrib/compiler-rt/lib/dfsan/dfsan_custom.cc b/contrib/compiler-rt/lib/dfsan/dfsan_custom.cc index c58b471db53c..e0cd16ab695c 100644 --- a/contrib/compiler-rt/lib/dfsan/dfsan_custom.cc +++ b/contrib/compiler-rt/lib/dfsan/dfsan_custom.cc @@ -43,6 +43,14 @@ using namespace __dfsan; +#define CALL_WEAK_INTERCEPTOR_HOOK(f, ...) \ + do { \ + if (f) \ + f(__VA_ARGS__); \ + } while (false) +#define DECLARE_WEAK_INTERCEPTOR_HOOK(f, ...) \ +SANITIZER_INTERFACE_ATTRIBUTE SANITIZER_WEAK_ATTRIBUTE void f(__VA_ARGS__); + extern "C" { SANITIZER_INTERFACE_ATTRIBUTE int __dfsw_stat(const char *path, struct stat *buf, dfsan_label path_label, @@ -77,25 +85,23 @@ SANITIZER_INTERFACE_ATTRIBUTE char *__dfsw_strchr(const char *s, int c, *ret_label = dfsan_union(dfsan_read_label(s, i + 1), dfsan_union(s_label, c_label)); } - return s[i] == 0 ? 0 : const_cast<char *>(s+i); + return s[i] == 0 ? nullptr : const_cast<char *>(s+i); } } } -SANITIZER_INTERFACE_ATTRIBUTE SANITIZER_WEAK_ATTRIBUTE -void -dfsan_weak_hook_memcmp(uptr caller_pc, const void *s1, const void *s2, size_t n, - dfsan_label s1_label, dfsan_label s2_label, - dfsan_label n_label); +DECLARE_WEAK_INTERCEPTOR_HOOK(dfsan_weak_hook_memcmp, uptr caller_pc, + const void *s1, const void *s2, size_t n, + dfsan_label s1_label, dfsan_label s2_label, + dfsan_label n_label) SANITIZER_INTERFACE_ATTRIBUTE int __dfsw_memcmp(const void *s1, const void *s2, size_t n, dfsan_label s1_label, dfsan_label s2_label, dfsan_label n_label, dfsan_label *ret_label) { - if (dfsan_weak_hook_memcmp) - dfsan_weak_hook_memcmp(GET_CALLER_PC(), s1, s2, n, s1_label, s2_label, - n_label); + CALL_WEAK_INTERCEPTOR_HOOK(dfsan_weak_hook_memcmp, GET_CALLER_PC(), s1, s2, n, + s1_label, s2_label, n_label); const char *cs1 = (const char *) s1, *cs2 = (const char *) s2; for (size_t i = 0; i != n; ++i) { if (cs1[i] != cs2[i]) { @@ -118,10 +124,16 @@ SANITIZER_INTERFACE_ATTRIBUTE int __dfsw_memcmp(const void *s1, const void *s2, return 0; } +DECLARE_WEAK_INTERCEPTOR_HOOK(dfsan_weak_hook_strcmp, uptr caller_pc, + const char *s1, const char *s2, + dfsan_label s1_label, dfsan_label s2_label) + SANITIZER_INTERFACE_ATTRIBUTE int __dfsw_strcmp(const char *s1, const char *s2, dfsan_label s1_label, dfsan_label s2_label, dfsan_label *ret_label) { + CALL_WEAK_INTERCEPTOR_HOOK(dfsan_weak_hook_strcmp, GET_CALLER_PC(), s1, s2, + s1_label, s2_label); for (size_t i = 0;; ++i) { if (s1[i] != s2[i] || s1[i] == 0 || s2[i] == 0) { if (flags().strict_data_dependencies) { @@ -153,6 +165,11 @@ __dfsw_strcasecmp(const char *s1, const char *s2, dfsan_label s1_label, return 0; } +DECLARE_WEAK_INTERCEPTOR_HOOK(dfsan_weak_hook_strncmp, uptr caller_pc, + const char *s1, const char *s2, size_t n, + dfsan_label s1_label, dfsan_label s2_label, + dfsan_label n_label) + SANITIZER_INTERFACE_ATTRIBUTE int __dfsw_strncmp(const char *s1, const char *s2, size_t n, dfsan_label s1_label, dfsan_label s2_label, @@ -163,6 +180,9 @@ SANITIZER_INTERFACE_ATTRIBUTE int __dfsw_strncmp(const char *s1, const char *s2, return 0; } + CALL_WEAK_INTERCEPTOR_HOOK(dfsan_weak_hook_strncmp, GET_CALLER_PC(), s1, s2, + n, s1_label, s2_label, n_label); + for (size_t i = 0;; ++i) { if (s1[i] != s2[i] || s1[i] == 0 || s2[i] == 0 || i == n - 1) { if (flags().strict_data_dependencies) { @@ -828,8 +848,8 @@ typedef void (*write_trampoline_t)( // Calls to dfsan_set_write_callback() set the values in this struct. // Calls to the custom version of write() read (and invoke) them. static struct { - write_trampoline_t write_callback_trampoline = NULL; - void *write_callback = NULL; + write_trampoline_t write_callback_trampoline = nullptr; + void *write_callback = nullptr; } write_callback_info; SANITIZER_INTERFACE_ATTRIBUTE void @@ -846,7 +866,7 @@ SANITIZER_INTERFACE_ATTRIBUTE int __dfsw_write(int fd, const void *buf, size_t count, dfsan_label fd_label, dfsan_label buf_label, dfsan_label count_label, dfsan_label *ret_label) { - if (write_callback_info.write_callback != NULL) { + if (write_callback_info.write_callback) { write_callback_info.write_callback_trampoline( write_callback_info.write_callback, fd, buf, count, @@ -856,7 +876,7 @@ __dfsw_write(int fd, const void *buf, size_t count, *ret_label = 0; return write(fd, buf, count); } -} +} // namespace __dfsan // Type used to extract a dfsan_label with va_arg() typedef int dfsan_label_va; @@ -1112,4 +1132,4 @@ int __dfsw_snprintf(char *str, size_t size, const char *format, va_end(ap); return ret; } -} +} // extern "C" diff --git a/contrib/compiler-rt/lib/dfsan/dfsan_platform.h b/contrib/compiler-rt/lib/dfsan/dfsan_platform.h new file mode 100644 index 000000000000..f1d9f108e908 --- /dev/null +++ b/contrib/compiler-rt/lib/dfsan/dfsan_platform.h @@ -0,0 +1,107 @@ +//===-- dfsan_platform.h ----------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file is a part of DataFlowSanitizer. +// +// Platform specific information for DFSan. +//===----------------------------------------------------------------------===// + +#ifndef DFSAN_PLATFORM_H +#define DFSAN_PLATFORM_H + +namespace __dfsan { + +#if defined(__x86_64__) +struct Mapping { + static const uptr kShadowAddr = 0x10000; + static const uptr kUnionTableAddr = 0x200000000000; + static const uptr kAppAddr = 0x700000008000; + static const uptr kShadowMask = ~0x700000000000; +}; +#elif defined(__mips64) +struct Mapping { + static const uptr kShadowAddr = 0x10000; + static const uptr kUnionTableAddr = 0x2000000000; + static const uptr kAppAddr = 0xF000008000; + static const uptr kShadowMask = ~0xF000000000; +}; +#elif defined(__aarch64__) +struct Mapping39 { + static const uptr kShadowAddr = 0x10000; + static const uptr kUnionTableAddr = 0x1000000000; + static const uptr kAppAddr = 0x7000008000; + static const uptr kShadowMask = ~0x7800000000; +}; + +struct Mapping42 { + static const uptr kShadowAddr = 0x10000; + static const uptr kUnionTableAddr = 0x8000000000; + static const uptr kAppAddr = 0x3ff00008000; + static const uptr kShadowMask = ~0x3c000000000; +}; + +extern int vmaSize; +# define DFSAN_RUNTIME_VMA 1 +#else +# error "DFSan not supported for this platform!" +#endif + +enum MappingType { + MAPPING_SHADOW_ADDR, + MAPPING_UNION_TABLE_ADDR, + MAPPING_APP_ADDR, + MAPPING_SHADOW_MASK +}; + +template<typename Mapping, int Type> +uptr MappingImpl(void) { + switch (Type) { + case MAPPING_SHADOW_ADDR: return Mapping::kShadowAddr; + case MAPPING_UNION_TABLE_ADDR: return Mapping::kUnionTableAddr; + case MAPPING_APP_ADDR: return Mapping::kAppAddr; + case MAPPING_SHADOW_MASK: return Mapping::kShadowMask; + } +} + +template<int Type> +uptr MappingArchImpl(void) { +#ifdef __aarch64__ + if (vmaSize == 39) + return MappingImpl<Mapping39, Type>(); + else + return MappingImpl<Mapping42, Type>(); + DCHECK(0); +#else + return MappingImpl<Mapping, Type>(); +#endif +} + +ALWAYS_INLINE +uptr ShadowAddr() { + return MappingArchImpl<MAPPING_SHADOW_ADDR>(); +} + +ALWAYS_INLINE +uptr UnionTableAddr() { + return MappingArchImpl<MAPPING_UNION_TABLE_ADDR>(); +} + +ALWAYS_INLINE +uptr AppAddr() { + return MappingArchImpl<MAPPING_APP_ADDR>(); +} + +ALWAYS_INLINE +uptr ShadowMask() { + return MappingArchImpl<MAPPING_SHADOW_MASK>(); +} + +} // namespace __dfsan + +#endif diff --git a/contrib/compiler-rt/lib/dfsan/done_abilist.txt b/contrib/compiler-rt/lib/dfsan/done_abilist.txt index e6c077ff1208..7ca8aeba32fe 100644 --- a/contrib/compiler-rt/lib/dfsan/done_abilist.txt +++ b/contrib/compiler-rt/lib/dfsan/done_abilist.txt @@ -266,10 +266,41 @@ fun:reflect.makeFuncStub=discard # Replaces __sanitizer_cov_trace_cmp with __dfsw___sanitizer_cov_trace_cmp fun:__sanitizer_cov_trace_cmp=custom fun:__sanitizer_cov_trace_cmp=uninstrumented +# Similar for __sanitizer_cov_trace_switch +fun:__sanitizer_cov_trace_switch=custom +fun:__sanitizer_cov_trace_switch=uninstrumented # Ignores all other __sanitizer callbacks. -fun:__sanitizer_*=uninstrumented -fun:__sanitizer_*=discard +fun:__sanitizer_cov=uninstrumented +fun:__sanitizer_cov=discard +fun:__sanitizer_cov_module_init=uninstrumented +fun:__sanitizer_cov_module_init=discard +fun:__sanitizer_cov_with_check=uninstrumented +fun:__sanitizer_cov_with_check=discard +fun:__sanitizer_cov_indir_call16=uninstrumented +fun:__sanitizer_cov_indir_call16=discard +fun:__sanitizer_cov_indir_call16=uninstrumented +fun:__sanitizer_cov_indir_call16=discard +fun:__sanitizer_reset_coverage=uninstrumented +fun:__sanitizer_reset_coverage=discard +fun:__sanitizer_set_death_callback=uninstrumented +fun:__sanitizer_set_death_callback=discard +fun:__sanitizer_get_coverage_guards=uninstrumented +fun:__sanitizer_get_coverage_guards=discard +fun:__sanitizer_get_number_of_counters=uninstrumented +fun:__sanitizer_get_number_of_counters=discard +fun:__sanitizer_update_counter_bitset_and_clear_counters=uninstrumented +fun:__sanitizer_update_counter_bitset_and_clear_counters=discard +fun:__sanitizer_get_total_unique_coverage=uninstrumented +fun:__sanitizer_get_total_unique_coverage=discard +fun:__sanitizer_get_total_unique_coverage=uninstrumented +fun:__sanitizer_get_total_unique_coverage=discard +fun:__sanitizer_update_counter_bitset_and_clear_counters=uninstrumented +fun:__sanitizer_update_counter_bitset_and_clear_counters=discard + +# Ignores the dfsan wrappers. +fun:__dfsw_*=uninstrumented +fun:__dfsw_*=discard # Don't add extra parameters to the Fuzzer callback. fun:LLVMFuzzerTestOneInput=uninstrumented diff --git a/contrib/compiler-rt/lib/dfsan/scripts/build-libc-list.py b/contrib/compiler-rt/lib/dfsan/scripts/build-libc-list.py deleted file mode 100755 index eddb6c07e99d..000000000000 --- a/contrib/compiler-rt/lib/dfsan/scripts/build-libc-list.py +++ /dev/null @@ -1,96 +0,0 @@ -#!/usr/bin/env python -#===- lib/dfsan/scripts/build-libc-list.py ---------------------------------===# -# -# The LLVM Compiler Infrastructure -# -# This file is distributed under the University of Illinois Open Source -# License. See LICENSE.TXT for details. -# -#===------------------------------------------------------------------------===# -# The purpose of this script is to identify every function symbol in a set of -# libraries (in this case, libc and libgcc) so that they can be marked as -# uninstrumented, thus allowing the instrumentation pass to treat calls to those -# functions correctly. - -import os -import subprocess -import sys -from optparse import OptionParser - -def defined_function_list(object): - functions = [] - readelf_proc = subprocess.Popen(['readelf', '-s', '-W', object], - stdout=subprocess.PIPE) - readelf = readelf_proc.communicate()[0].split('\n') - if readelf_proc.returncode != 0: - raise subprocess.CalledProcessError(readelf_proc.returncode, 'readelf') - for line in readelf: - if (line[31:35] == 'FUNC' or line[31:36] == 'IFUNC') and \ - line[39:44] != 'LOCAL' and \ - line[55:58] != 'UND': - function_name = line[59:].split('@')[0] - functions.append(function_name) - return functions - -p = OptionParser() - -p.add_option('--libc-dso-path', metavar='PATH', - help='path to libc DSO directory', - default='/lib/x86_64-linux-gnu') -p.add_option('--libc-archive-path', metavar='PATH', - help='path to libc archive directory', - default='/usr/lib/x86_64-linux-gnu') - -p.add_option('--libgcc-dso-path', metavar='PATH', - help='path to libgcc DSO directory', - default='/lib/x86_64-linux-gnu') -p.add_option('--libgcc-archive-path', metavar='PATH', - help='path to libgcc archive directory', - default='/usr/lib/gcc/x86_64-linux-gnu/4.6') - -p.add_option('--with-libstdcxx', action='store_true', - dest='with_libstdcxx', - help='include libstdc++ in the list (inadvisable)') -p.add_option('--libstdcxx-dso-path', metavar='PATH', - help='path to libstdc++ DSO directory', - default='/usr/lib/x86_64-linux-gnu') - -(options, args) = p.parse_args() - -libs = [os.path.join(options.libc_dso_path, name) for name in - ['ld-linux-x86-64.so.2', - 'libanl.so.1', - 'libBrokenLocale.so.1', - 'libcidn.so.1', - 'libcrypt.so.1', - 'libc.so.6', - 'libdl.so.2', - 'libm.so.6', - 'libnsl.so.1', - 'libpthread.so.0', - 'libresolv.so.2', - 'librt.so.1', - 'libthread_db.so.1', - 'libutil.so.1']] -libs += [os.path.join(options.libc_archive_path, name) for name in - ['libc_nonshared.a', - 'libpthread_nonshared.a']] - -libs.append(os.path.join(options.libgcc_dso_path, 'libgcc_s.so.1')) -libs.append(os.path.join(options.libgcc_archive_path, 'libgcc.a')) - -if options.with_libstdcxx: - libs.append(os.path.join(options.libstdcxx_dso_path, 'libstdc++.so.6')) - -functions = [] -for l in libs: - if os.path.exists(l): - functions += defined_function_list(l) - else: - print >> sys.stderr, 'warning: library %s not found' % l - -functions = list(set(functions)) -functions.sort() - -for f in functions: - print 'fun:%s=uninstrumented' % f diff --git a/contrib/compiler-rt/lib/dfsan/scripts/check_custom_wrappers.sh b/contrib/compiler-rt/lib/dfsan/scripts/check_custom_wrappers.sh deleted file mode 100755 index 99bf50cbbd08..000000000000 --- a/contrib/compiler-rt/lib/dfsan/scripts/check_custom_wrappers.sh +++ /dev/null @@ -1,52 +0,0 @@ -#!/bin/sh - -DFSAN_DIR=$(dirname "$0")/../ -DFSAN_CUSTOM_TESTS=${DFSAN_DIR}/../../test/dfsan/custom.cc -DFSAN_CUSTOM_WRAPPERS=${DFSAN_DIR}/dfsan_custom.cc -DFSAN_ABI_LIST=${DFSAN_DIR}/done_abilist.txt - -DIFFOUT=$(mktemp -q /tmp/tmp.XXXXXXXXXX) -ERRORLOG=$(mktemp -q /tmp/tmp.XXXXXXXXXX) -DIFF_A=$(mktemp -q /tmp/tmp.XXXXXXXXXX) -DIFF_B=$(mktemp -q /tmp/tmp.XXXXXXXXXX) - -on_exit() { - rm -f ${DIFFOUT} 2> /dev/null - rm -f ${ERRORLOG} 2> /dev/null - rm -f ${DIFF_A} 2> /dev/null - rm -f ${DIFF_B} 2> /dev/null -} - -# Ignore __sanitizer_cov_trace* because they are implemented elsewhere. -trap on_exit EXIT -grep -E "^fun:.*=custom" ${DFSAN_ABI_LIST} \ - | grep -v "dfsan_get_label\|__sanitizer_cov_trace" \ - | sed "s/^fun:\(.*\)=custom.*/\1/" | sort > $DIFF_A -grep -E "__dfsw.*\(" ${DFSAN_CUSTOM_WRAPPERS} \ - | sed "s/.*__dfsw_\(.*\)(.*/\1/" | sort > $DIFF_B -diff -u $DIFF_A $DIFF_B > ${DIFFOUT} -if [ $? -ne 0 ] -then - echo -n "The following differences between the ABI list and ">> ${ERRORLOG} - echo "the implemented custom wrappers have been found:" >> ${ERRORLOG} - cat ${DIFFOUT} >> ${ERRORLOG} -fi - -grep -E __dfsw_ ${DFSAN_CUSTOM_WRAPPERS} \ - | sed "s/.*__dfsw_\([^(]*\).*/\1/" | sort > $DIFF_A -grep -E "^[[:space:]]*test_.*\(\);" ${DFSAN_CUSTOM_TESTS} \ - | sed "s/.*test_\(.*\)();/\1/" | sort > $DIFF_B -diff -u $DIFF_A $DIFF_B > ${DIFFOUT} -if [ $? -ne 0 ] -then - echo -n "The following differences between the implemented " >> ${ERRORLOG} - echo "custom wrappers and the tests have been found:" >> ${ERRORLOG} - cat ${DIFFOUT} >> ${ERRORLOG} -fi - -if [ -s ${ERRORLOG} ] -then - cat ${ERRORLOG} - exit 1 -fi - diff --git a/contrib/compiler-rt/lib/interception/interception_linux.h b/contrib/compiler-rt/lib/interception/interception_linux.h index d3f774bede9f..27a66c882041 100644 --- a/contrib/compiler-rt/lib/interception/interception_linux.h +++ b/contrib/compiler-rt/lib/interception/interception_linux.h @@ -35,12 +35,12 @@ void *GetFuncAddrVer(const char *func_name, const char *ver); (::__interception::uptr) & WRAP(func)) #if !defined(__ANDROID__) // android does not have dlvsym -# define INTERCEPT_FUNCTION_VER_LINUX_OR_FREEBSD(func, symver) \ - ::__interception::real_##func = (func##_f)(unsigned long) \ - ::__interception::GetFuncAddrVer(#func, symver) +#define INTERCEPT_FUNCTION_VER_LINUX_OR_FREEBSD(func, symver) \ + (::__interception::real_##func = (func##_f)( \ + unsigned long)::__interception::GetFuncAddrVer(#func, symver)) #else -# define INTERCEPT_FUNCTION_VER_LINUX_OR_FREEBSD(func, symver) \ - INTERCEPT_FUNCTION_LINUX_OR_FREEBSD(func) +#define INTERCEPT_FUNCTION_VER_LINUX_OR_FREEBSD(func, symver) \ + INTERCEPT_FUNCTION_LINUX_OR_FREEBSD(func) #endif // !defined(__ANDROID__) #endif // INTERCEPTION_LINUX_H diff --git a/contrib/compiler-rt/lib/interception/interception_win.cc b/contrib/compiler-rt/lib/interception/interception_win.cc index 19cf184948b9..4c04c83b982b 100644 --- a/contrib/compiler-rt/lib/interception/interception_win.cc +++ b/contrib/compiler-rt/lib/interception/interception_win.cc @@ -15,6 +15,7 @@ #ifdef _WIN32 #include "interception.h" +#define WIN32_LEAN_AND_MEAN #include <windows.h> namespace __interception { @@ -182,7 +183,7 @@ bool OverrideFunction(uptr old_func, uptr new_func, uptr *orig_old_func) { return true; } -static const void **InterestingDLLsAvailable() { +static void **InterestingDLLsAvailable() { const char *InterestingDLLs[] = { "kernel32.dll", "msvcr110.dll", // VS2012 @@ -198,14 +199,65 @@ static const void **InterestingDLLsAvailable() { result[j++] = (void *)h; } } - return (const void **)&result[0]; + return &result[0]; +} + +namespace { +// Utility for reading loaded PE images. +template <typename T> class RVAPtr { + public: + RVAPtr(void *module, uptr rva) + : ptr_(reinterpret_cast<T *>(reinterpret_cast<char *>(module) + rva)) {} + operator T *() { return ptr_; } + T *operator->() { return ptr_; } + T *operator++() { return ++ptr_; } + + private: + T *ptr_; +}; +} // namespace + +// Internal implementation of GetProcAddress. At least since Windows 8, +// GetProcAddress appears to initialize DLLs before returning function pointers +// into them. This is problematic for the sanitizers, because they typically +// want to intercept malloc *before* MSVCRT initializes. Our internal +// implementation walks the export list manually without doing initialization. +uptr InternalGetProcAddress(void *module, const char *func_name) { + // Check that the module header is full and present. + RVAPtr<IMAGE_DOS_HEADER> dos_stub(module, 0); + RVAPtr<IMAGE_NT_HEADERS> headers(module, dos_stub->e_lfanew); + if (!module || dos_stub->e_magic != IMAGE_DOS_SIGNATURE || // "MZ" + headers->Signature != IMAGE_NT_SIGNATURE || // "PE\0\0" + headers->FileHeader.SizeOfOptionalHeader < + sizeof(IMAGE_OPTIONAL_HEADER)) { + return 0; + } + + IMAGE_DATA_DIRECTORY *export_directory = + &headers->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT]; + RVAPtr<IMAGE_EXPORT_DIRECTORY> exports(module, + export_directory->VirtualAddress); + RVAPtr<DWORD> functions(module, exports->AddressOfFunctions); + RVAPtr<DWORD> names(module, exports->AddressOfNames); + RVAPtr<WORD> ordinals(module, exports->AddressOfNameOrdinals); + + for (DWORD i = 0; i < exports->NumberOfNames; i++) { + RVAPtr<char> name(module, names[i]); + if (!strcmp(func_name, name)) { + DWORD index = ordinals[i]; + RVAPtr<char> func(module, functions[index]); + return (uptr)(char *)func; + } + } + + return 0; } static bool GetFunctionAddressInDLLs(const char *func_name, uptr *func_addr) { *func_addr = 0; - const void **DLLs = InterestingDLLsAvailable(); + void **DLLs = InterestingDLLsAvailable(); for (size_t i = 0; *func_addr == 0 && DLLs[i]; ++i) - *func_addr = (uptr)GetProcAddress((HMODULE)DLLs[i], func_name); + *func_addr = InternalGetProcAddress(DLLs[i], func_name); return (*func_addr != 0); } diff --git a/contrib/compiler-rt/lib/interception/interception_win.h b/contrib/compiler-rt/lib/interception/interception_win.h index ba768a7233f9..96c4a0c0f5a3 100644 --- a/contrib/compiler-rt/lib/interception/interception_win.h +++ b/contrib/compiler-rt/lib/interception/interception_win.h @@ -30,6 +30,10 @@ bool OverrideFunction(uptr old_func, uptr new_func, uptr *orig_old_func = 0); // Overrides a function in a system DLL or DLL CRT by its exported name. bool OverrideFunction(const char *name, uptr new_func, uptr *orig_old_func = 0); + +// Windows-only replacement for GetProcAddress. Useful for some sanitizers. +uptr InternalGetProcAddress(void *module, const char *func_name); + } // namespace __interception #if defined(INTERCEPTION_DYNAMIC_CRT) diff --git a/contrib/compiler-rt/lib/lsan/lsan.cc b/contrib/compiler-rt/lib/lsan/lsan.cc index 6018f7bf6f49..f3e6ad7c9cba 100644 --- a/contrib/compiler-rt/lib/lsan/lsan.cc +++ b/contrib/compiler-rt/lib/lsan/lsan.cc @@ -44,6 +44,7 @@ static void InitializeFlags() { cf.external_symbolizer_path = GetEnv("LSAN_SYMBOLIZER_PATH"); cf.malloc_context_size = 30; cf.detect_leaks = true; + cf.exitcode = 23; OverrideCommonFlags(cf); } @@ -69,6 +70,7 @@ extern "C" void __lsan_init() { return; lsan_init_is_running = true; SanitizerToolName = "LeakSanitizer"; + CacheBinaryName(); InitializeFlags(); InitCommonLsan(); InitializeAllocator(); diff --git a/contrib/compiler-rt/lib/lsan/lsan_allocator.cc b/contrib/compiler-rt/lib/lsan/lsan_allocator.cc index 67125dbb3e45..0a3678132ae1 100644 --- a/contrib/compiler-rt/lib/lsan/lsan_allocator.cc +++ b/contrib/compiler-rt/lib/lsan/lsan_allocator.cc @@ -26,13 +26,13 @@ extern "C" void *memset(void *ptr, int value, uptr num); namespace __lsan { struct ChunkMetadata { - bool allocated : 8; // Must be first. + u8 allocated : 8; // Must be first. ChunkTag tag : 2; uptr requested_size : 54; u32 stack_trace_id; }; -#if defined(__mips64) +#if defined(__mips64) || defined(__aarch64__) static const uptr kMaxAllowedMallocSize = 4UL << 30; static const uptr kRegionSizeLog = 20; static const uptr kNumRegions = SANITIZER_MMAP_RANGE_SIZE >> kRegionSizeLog; @@ -91,7 +91,7 @@ void *Allocate(const StackTrace &stack, uptr size, uptr alignment, size = 1; if (size > kMaxAllowedMallocSize) { Report("WARNING: LeakSanitizer failed to allocate %zu bytes\n", size); - return 0; + return nullptr; } void *p = allocator.Allocate(&cache, size, alignment, false); // Do not rely on the allocator to clear the memory (it's slow). @@ -114,7 +114,7 @@ void *Reallocate(const StackTrace &stack, void *p, uptr new_size, if (new_size > kMaxAllowedMallocSize) { Report("WARNING: LeakSanitizer failed to allocate %zu bytes\n", new_size); allocator.Deallocate(&cache, p); - return 0; + return nullptr; } p = allocator.Reallocate(&cache, p, new_size, alignment); RegisterAllocation(stack, p, new_size); @@ -212,7 +212,7 @@ IgnoreObjectResult IgnoreObjectLocked(const void *p) { return kIgnoreObjectInvalid; } } -} // namespace __lsan +} // namespace __lsan using namespace __lsan; @@ -241,10 +241,10 @@ SANITIZER_INTERFACE_ATTRIBUTE uptr __sanitizer_get_estimated_allocated_size(uptr size) { return size; } SANITIZER_INTERFACE_ATTRIBUTE -int __sanitizer_get_ownership(const void *p) { return Metadata(p) != 0; } +int __sanitizer_get_ownership(const void *p) { return Metadata(p) != nullptr; } SANITIZER_INTERFACE_ATTRIBUTE uptr __sanitizer_get_allocated_size(const void *p) { return GetMallocUsableSize(p); } -} // extern "C" +} // extern "C" diff --git a/contrib/compiler-rt/lib/lsan/lsan_common.cc b/contrib/compiler-rt/lib/lsan/lsan_common.cc index 0ffba505cc70..1cffac44395c 100644 --- a/contrib/compiler-rt/lib/lsan/lsan_common.cc +++ b/contrib/compiler-rt/lib/lsan/lsan_common.cc @@ -119,6 +119,10 @@ static inline bool CanBeAHeapPointer(uptr p) { return ((p >> 47) == 0); #elif defined(__mips64) return ((p >> 40) == 0); +#elif defined(__aarch64__) + unsigned runtimeVMA = + (MostSignificantSetBitIndex(GET_CURRENT_FRAME()) + 1); + return ((p >> runtimeVMA) == 0); #else return true; #endif @@ -243,8 +247,8 @@ static void ProcessRootRegion(Frontier *frontier, uptr root_begin, MemoryMappingLayout proc_maps(/*cache_enabled*/true); uptr begin, end, prot; while (proc_maps.Next(&begin, &end, - /*offset*/ 0, /*filename*/ 0, /*filename_size*/ 0, - &prot)) { + /*offset*/ nullptr, /*filename*/ nullptr, + /*filename_size*/ 0, &prot)) { uptr intersection_begin = Max(root_begin, begin); uptr intersection_end = Min(end, root_end); if (intersection_begin >= intersection_end) continue; @@ -375,8 +379,8 @@ static void PrintMatchedSuppressions() { Printf("Suppressions used:\n"); Printf(" count bytes template\n"); for (uptr i = 0; i < matched.size(); i++) - Printf("%7zu %10zu %s\n", static_cast<uptr>(matched[i]->hit_count), - matched[i]->weight, matched[i]->templ); + Printf("%7zu %10zu %s\n", static_cast<uptr>(atomic_load_relaxed( + &matched[i]->hit_count)), matched[i]->weight, matched[i]->templ); Printf("%s\n\n", line); } @@ -444,10 +448,8 @@ void DoLeakCheck() { if (!have_leaks) { return; } - if (flags()->exitcode) { - if (common_flags()->coverage) - __sanitizer_cov_dump(); - internal__exit(flags()->exitcode); + if (common_flags()->exitcode) { + Die(); } } @@ -486,7 +488,7 @@ static Suppression *GetSuppressionForStack(u32 stack_trace_id) { StackTrace::GetPreviousInstructionPc(stack.trace[i])); if (s) return s; } - return 0; + return nullptr; } ///// LeakReport implementation. ///// @@ -600,7 +602,8 @@ void LeakReport::ApplySuppressions() { Suppression *s = GetSuppressionForStack(leaks_[i].stack_trace_id); if (s) { s->weight += leaks_[i].total_size; - s->hit_count += leaks_[i].hit_count; + atomic_store_relaxed(&s->hit_count, atomic_load_relaxed(&s->hit_count) + + leaks_[i].hit_count); leaks_[i].is_suppressed = true; } } @@ -613,8 +616,8 @@ uptr LeakReport::UnsuppressedLeakCount() { return result; } -} // namespace __lsan -#endif // CAN_SANITIZE_LEAKS +} // namespace __lsan +#endif // CAN_SANITIZE_LEAKS using namespace __lsan; // NOLINT @@ -635,7 +638,7 @@ void __lsan_ignore_object(const void *p) { "heap object at %p is already being ignored\n", p); if (res == kIgnoreObjectSuccess) VReport(1, "__lsan_ignore_object(): ignoring heap object at %p\n", p); -#endif // CAN_SANITIZE_LEAKS +#endif // CAN_SANITIZE_LEAKS } SANITIZER_INTERFACE_ATTRIBUTE @@ -646,7 +649,7 @@ void __lsan_register_root_region(const void *begin, uptr size) { RootRegion region = {begin, size}; root_regions->push_back(region); VReport(1, "Registered root region at %p of size %llu\n", begin, size); -#endif // CAN_SANITIZE_LEAKS +#endif // CAN_SANITIZE_LEAKS } SANITIZER_INTERFACE_ATTRIBUTE @@ -673,7 +676,7 @@ void __lsan_unregister_root_region(const void *begin, uptr size) { begin, size); Die(); } -#endif // CAN_SANITIZE_LEAKS +#endif // CAN_SANITIZE_LEAKS } SANITIZER_INTERFACE_ATTRIBUTE @@ -699,7 +702,7 @@ void __lsan_do_leak_check() { #if CAN_SANITIZE_LEAKS if (common_flags()->detect_leaks) __lsan::DoLeakCheck(); -#endif // CAN_SANITIZE_LEAKS +#endif // CAN_SANITIZE_LEAKS } SANITIZER_INTERFACE_ATTRIBUTE @@ -707,7 +710,7 @@ int __lsan_do_recoverable_leak_check() { #if CAN_SANITIZE_LEAKS if (common_flags()->detect_leaks) return __lsan::DoRecoverableLeakCheck(); -#endif // CAN_SANITIZE_LEAKS +#endif // CAN_SANITIZE_LEAKS return 0; } @@ -717,4 +720,4 @@ int __lsan_is_turned_off() { return 0; } #endif -} // extern "C" +} // extern "C" diff --git a/contrib/compiler-rt/lib/lsan/lsan_common.h b/contrib/compiler-rt/lib/lsan/lsan_common.h index 4f9d24fb3ab9..0dfd0d4c9890 100644 --- a/contrib/compiler-rt/lib/lsan/lsan_common.h +++ b/contrib/compiler-rt/lib/lsan/lsan_common.h @@ -22,8 +22,8 @@ #include "sanitizer_common/sanitizer_stoptheworld.h" #include "sanitizer_common/sanitizer_symbolizer.h" -#if SANITIZER_LINUX && (defined(__x86_64__) || defined(__mips64)) \ - && (SANITIZER_WORDSIZE == 64) +#if (SANITIZER_LINUX && !SANITIZER_ANDROID) && (SANITIZER_WORDSIZE == 64) \ + && (defined(__x86_64__) || defined(__mips64) || defined(__aarch64__)) #define CAN_SANITIZE_LEAKS 1 #else #define CAN_SANITIZE_LEAKS 0 diff --git a/contrib/compiler-rt/lib/lsan/lsan_common_linux.cc b/contrib/compiler-rt/lib/lsan/lsan_common_linux.cc index 2955343e1f0b..1dc0561dab71 100644 --- a/contrib/compiler-rt/lib/lsan/lsan_common_linux.cc +++ b/contrib/compiler-rt/lib/lsan/lsan_common_linux.cc @@ -29,7 +29,7 @@ static const char kLinkerName[] = "ld"; // We request 2 modules matching "ld", so we can print a warning if there's more // than one match. But only the first one is actually used. static char linker_placeholder[2 * sizeof(LoadedModule)] ALIGNED(64); -static LoadedModule *linker = 0; +static LoadedModule *linker = nullptr; static bool IsLinker(const char* full_name) { return LibraryNameIs(full_name, kLinkerName); @@ -49,7 +49,7 @@ void InitializePlatformSpecificModules() { else if (num_matches > 1) VReport(1, "LeakSanitizer: Multiple modules match \"%s\". " "TLS will not be handled correctly.\n", kLinkerName); - linker = 0; + linker = nullptr; } static int ProcessGlobalRegionsCallback(struct dl_phdr_info *info, size_t size, @@ -174,5 +174,6 @@ void DoStopTheWorld(StopTheWorldCallback callback, void *argument) { dl_iterate_phdr(DoStopTheWorldCallback, ¶m); } -} // namespace __lsan -#endif // CAN_SANITIZE_LEAKS && SANITIZER_LINUX +} // namespace __lsan + +#endif // CAN_SANITIZE_LEAKS && SANITIZER_LINUX diff --git a/contrib/compiler-rt/lib/lsan/lsan_flags.inc b/contrib/compiler-rt/lib/lsan/lsan_flags.inc index b19b3452b2fc..c405005deed5 100644 --- a/contrib/compiler-rt/lib/lsan/lsan_flags.inc +++ b/contrib/compiler-rt/lib/lsan/lsan_flags.inc @@ -24,8 +24,6 @@ LSAN_FLAG( "Aggregate two objects into one leak if this many stack frames match. If " "zero, the entire stack trace must match.") LSAN_FLAG(int, max_leaks, 0, "The number of leaks reported.") -LSAN_FLAG(int, exitcode, 23, - "If nonzero kill the process with this exit code upon finding leaks.") // Flags controlling the root set of reachable memory. LSAN_FLAG(bool, use_globals, true, diff --git a/contrib/compiler-rt/lib/lsan/lsan_interceptors.cc b/contrib/compiler-rt/lib/lsan/lsan_interceptors.cc index 61a92154d95e..be0d0ddc282e 100644 --- a/contrib/compiler-rt/lib/lsan/lsan_interceptors.cc +++ b/contrib/compiler-rt/lib/lsan/lsan_interceptors.cc @@ -71,7 +71,7 @@ INTERCEPTOR(void*, calloc, uptr nmemb, uptr size) { CHECK(allocated < kCallocPoolSize); return mem; } - if (CallocShouldReturnNullDueToOverflow(size, nmemb)) return 0; + if (CallocShouldReturnNullDueToOverflow(size, nmemb)) return nullptr; ENSURE_LSAN_INITED; GET_STACK_TRACE_MALLOC; size *= nmemb; @@ -164,9 +164,9 @@ void *operator new[](uptr size, std::nothrow_t const&) { OPERATOR_NEW_BODY; } Deallocate(ptr); INTERCEPTOR_ATTRIBUTE -void operator delete(void *ptr) throw() { OPERATOR_DELETE_BODY; } +void operator delete(void *ptr) NOEXCEPT { OPERATOR_DELETE_BODY; } INTERCEPTOR_ATTRIBUTE -void operator delete[](void *ptr) throw() { OPERATOR_DELETE_BODY; } +void operator delete[](void *ptr) NOEXCEPT { OPERATOR_DELETE_BODY; } INTERCEPTOR_ATTRIBUTE void operator delete(void *ptr, std::nothrow_t const&) { OPERATOR_DELETE_BODY; } INTERCEPTOR_ATTRIBUTE @@ -226,7 +226,7 @@ INTERCEPTOR(int, pthread_create, void *th, void *attr, ENSURE_LSAN_INITED; EnsureMainThreadIDIsCorrect(); __sanitizer_pthread_attr_t myattr; - if (attr == 0) { + if (!attr) { pthread_attr_init(&myattr); attr = &myattr; } @@ -284,4 +284,4 @@ void InitializeInterceptors() { } } -} // namespace __lsan +} // namespace __lsan diff --git a/contrib/compiler-rt/lib/lsan/lsan_thread.cc b/contrib/compiler-rt/lib/lsan/lsan_thread.cc index 0f8efc093b56..10ac2c9f499d 100644 --- a/contrib/compiler-rt/lib/lsan/lsan_thread.cc +++ b/contrib/compiler-rt/lib/lsan/lsan_thread.cc @@ -79,7 +79,7 @@ void ThreadContext::OnFinished() { u32 ThreadCreate(u32 parent_tid, uptr user_id, bool detached) { return thread_registry->CreateThread(user_id, detached, parent_tid, - /* arg */ 0); + /* arg */ nullptr); } void ThreadStart(u32 tid, uptr os_id) { @@ -99,9 +99,9 @@ void ThreadFinish() { } ThreadContext *CurrentThreadContext() { - if (!thread_registry) return 0; + if (!thread_registry) return nullptr; if (GetCurrentThread() == kInvalidTid) - return 0; + return nullptr; // No lock needed when getting current thread. return (ThreadContext *)thread_registry->GetThreadLocked(GetCurrentThread()); } @@ -120,7 +120,7 @@ u32 ThreadTid(uptr uid) { void ThreadJoin(u32 tid) { CHECK_NE(tid, kInvalidTid); - thread_registry->JoinThread(tid, /* arg */0); + thread_registry->JoinThread(tid, /* arg */nullptr); } void EnsureMainThreadIDIsCorrect() { @@ -157,4 +157,4 @@ void UnlockThreadRegistry() { thread_registry->Unlock(); } -} // namespace __lsan +} // namespace __lsan diff --git a/contrib/compiler-rt/lib/msan/msan.cc b/contrib/compiler-rt/lib/msan/msan.cc index 163d59dabfa8..9949db4c13a0 100644 --- a/contrib/compiler-rt/lib/msan/msan.cc +++ b/contrib/compiler-rt/lib/msan/msan.cc @@ -55,7 +55,7 @@ SANITIZER_INTERFACE_ATTRIBUTE THREADLOCAL u32 __msan_retval_origin_tls; SANITIZER_INTERFACE_ATTRIBUTE -THREADLOCAL u64 __msan_va_arg_tls[kMsanParamTlsSize / sizeof(u64)]; +ALIGNED(16) THREADLOCAL u64 __msan_va_arg_tls[kMsanParamTlsSize / sizeof(u64)]; SANITIZER_INTERFACE_ATTRIBUTE THREADLOCAL u64 __msan_va_arg_overflow_size_tls; @@ -90,8 +90,6 @@ bool msan_init_is_running; int msan_report_count = 0; -void (*death_callback)(void); - // Array of stack origins. // FIXME: make it resizable. static const uptr kNumStackOriginDescrs = 1024 * 1024; @@ -145,6 +143,7 @@ static void InitializeFlags() { // FIXME: test and enable. cf.check_printf = false; cf.intercept_tls_get_addr = true; + cf.exitcode = 77; OverrideCommonFlags(cf); } @@ -185,11 +184,18 @@ static void InitializeFlags() { if (common_flags()->help) parser.PrintFlagDescriptions(); - // Check flag values: - if (f->exit_code < 0 || f->exit_code > 127) { - Printf("Exit code not in [0, 128) range: %d\n", f->exit_code); - Die(); + // Check if deprecated exit_code MSan flag is set. + if (f->exit_code != -1) { + if (Verbosity()) + Printf("MSAN_OPTIONS=exit_code is deprecated! " + "Please use MSAN_OPTIONS=exitcode instead.\n"); + CommonFlags cf; + cf.CopyFrom(*common_flags()); + cf.exitcode = f->exit_code; + OverrideCommonFlags(cf); } + + // Check flag values: if (f->origin_history_size < 0 || f->origin_history_size > Origin::kMaxDepth) { Printf( @@ -217,9 +223,9 @@ void GetStackTrace(BufferedStackTrace *stack, uptr max_s, uptr pc, uptr bp, if (!t || !StackTrace::WillUseFastUnwind(request_fast_unwind)) { // Block reports from our interceptors during _Unwind_Backtrace. SymbolizerScope sym_scope; - return stack->Unwind(max_s, pc, bp, 0, 0, 0, request_fast_unwind); + return stack->Unwind(max_s, pc, bp, nullptr, 0, 0, request_fast_unwind); } - stack->Unwind(max_s, pc, bp, 0, t->stack_top(), t->stack_bottom(), + stack->Unwind(max_s, pc, bp, nullptr, t->stack_top(), t->stack_bottom(), request_fast_unwind); } @@ -299,7 +305,7 @@ u32 ChainOrigin(u32 id, StackTrace *stack) { return chained.raw_id(); } -} // namespace __msan +} // namespace __msan // Interface. @@ -369,11 +375,11 @@ void __msan_init() { msan_init_is_running = 1; SanitizerToolName = "MemorySanitizer"; - SetDieCallback(MsanDie); InitTlsSize(); - InitializeFlags(); CacheBinaryName(); + InitializeFlags(); + __sanitizer_set_report_path(common_flags()->log_path); InitializeInterceptors(); @@ -407,7 +413,9 @@ void __msan_init() { MsanTSDInit(MsanTSDDtor); - MsanThread *main_thread = MsanThread::Create(0, 0); + MsanAllocatorInit(); + + MsanThread *main_thread = MsanThread::Create(nullptr, nullptr); SetCurrentThread(main_thread); main_thread->ThreadStart(); @@ -421,10 +429,6 @@ void __msan_init() { msan_inited = 1; } -void __msan_set_exit_code(int exit_code) { - flags()->exit_code = exit_code; -} - void __msan_set_keep_going(int keep_going) { flags()->halt_on_error = !keep_going; } @@ -511,7 +515,7 @@ void __msan_partial_poison(const void* data, void* shadow, uptr size) { internal_memcpy((void*)MEM_TO_SHADOW((uptr)data), shadow, size); } -void __msan_load_unpoisoned(void *src, uptr size, void *dst) { +void __msan_load_unpoisoned(const void *src, uptr size, void *dst) { internal_memcpy(dst, src, size); __msan_unpoison(dst, size); } @@ -619,7 +623,7 @@ void __sanitizer_unaligned_store64(uu64 *p, u64 x) { } void __msan_set_death_callback(void (*callback)(void)) { - death_callback = callback; + SetUserDieCallback(callback); } #if !SANITIZER_SUPPORTS_WEAK_HOOKS @@ -635,4 +639,4 @@ void __sanitizer_print_stack_trace() { GET_FATAL_STACK_TRACE_PC_BP(StackTrace::GetCurrentPc(), GET_CURRENT_FRAME()); stack.Print(); } -} // extern "C" +} // extern "C" diff --git a/contrib/compiler-rt/lib/msan/msan.h b/contrib/compiler-rt/lib/msan/msan.h index cd8bc19f51ef..2079a592b7b9 100644 --- a/contrib/compiler-rt/lib/msan/msan.h +++ b/contrib/compiler-rt/lib/msan/msan.h @@ -52,6 +52,61 @@ const MappingDesc kMemoryLayout[] = { #define MEM_TO_SHADOW(mem) (((uptr)(mem)) & ~0x4000000000ULL) #define SHADOW_TO_ORIGIN(shadow) (((uptr)(shadow)) + 0x002000000000) +#elif SANITIZER_LINUX && defined(__aarch64__) + +// The mapping describes both 39-bits and 42-bits. AArch64 maps: +// - 0x00000000000-0x00010000000: 39/42-bits program own segments +// - 0x05500000000-0x05600000000: 39-bits PIE program segments +// - 0x07f80000000-0x07fffffffff: 39-bits libraries segments +// - 0x2aa00000000-0x2ab00000000: 42-bits PIE program segments +// - 0x3ff00000000-0x3ffffffffff: 42-bits libraries segments +// It is fragmented in multiples segments to increase the memory available +// on 42-bits (12.21% of total VMA available for 42-bits and 13.28 for +// 39 bits). +const MappingDesc kMemoryLayout[] = { + {0x00000000000ULL, 0x01000000000ULL, MappingDesc::INVALID, "invalid"}, + {0x01000000000ULL, 0x02000000000ULL, MappingDesc::SHADOW, "shadow-2"}, + {0x02000000000ULL, 0x03000000000ULL, MappingDesc::ORIGIN, "origin-2"}, + {0x03000000000ULL, 0x04000000000ULL, MappingDesc::SHADOW, "shadow-1"}, + {0x04000000000ULL, 0x05000000000ULL, MappingDesc::ORIGIN, "origin-1"}, + {0x05000000000ULL, 0x06000000000ULL, MappingDesc::APP, "app-1"}, + {0x06000000000ULL, 0x07000000000ULL, MappingDesc::INVALID, "invalid"}, + {0x07000000000ULL, 0x08000000000ULL, MappingDesc::APP, "app-2"}, + {0x08000000000ULL, 0x09000000000ULL, MappingDesc::INVALID, "invalid"}, + // The mappings below are used only for 42-bits VMA. + {0x09000000000ULL, 0x0A000000000ULL, MappingDesc::SHADOW, "shadow-3"}, + {0x0A000000000ULL, 0x0B000000000ULL, MappingDesc::ORIGIN, "origin-3"}, + {0x0B000000000ULL, 0x0F000000000ULL, MappingDesc::INVALID, "invalid"}, + {0x0F000000000ULL, 0x10000000000ULL, MappingDesc::APP, "app-3"}, + {0x10000000000ULL, 0x11000000000ULL, MappingDesc::INVALID, "invalid"}, + {0x11000000000ULL, 0x12000000000ULL, MappingDesc::APP, "app-4"}, + {0x12000000000ULL, 0x17000000000ULL, MappingDesc::INVALID, "invalid"}, + {0x17000000000ULL, 0x18000000000ULL, MappingDesc::SHADOW, "shadow-4"}, + {0x18000000000ULL, 0x19000000000ULL, MappingDesc::ORIGIN, "origin-4"}, + {0x19000000000ULL, 0x20000000000ULL, MappingDesc::INVALID, "invalid"}, + {0x20000000000ULL, 0x21000000000ULL, MappingDesc::APP, "app-5"}, + {0x21000000000ULL, 0x26000000000ULL, MappingDesc::INVALID, "invalid"}, + {0x26000000000ULL, 0x27000000000ULL, MappingDesc::SHADOW, "shadow-5"}, + {0x27000000000ULL, 0x28000000000ULL, MappingDesc::ORIGIN, "origin-5"}, + {0x28000000000ULL, 0x29000000000ULL, MappingDesc::SHADOW, "shadow-7"}, + {0x29000000000ULL, 0x2A000000000ULL, MappingDesc::ORIGIN, "origin-7"}, + {0x2A000000000ULL, 0x2B000000000ULL, MappingDesc::APP, "app-6"}, + {0x2B000000000ULL, 0x2C000000000ULL, MappingDesc::INVALID, "invalid"}, + {0x2C000000000ULL, 0x2D000000000ULL, MappingDesc::SHADOW, "shadow-6"}, + {0x2D000000000ULL, 0x2E000000000ULL, MappingDesc::ORIGIN, "origin-6"}, + {0x2E000000000ULL, 0x2F000000000ULL, MappingDesc::APP, "app-7"}, + {0x2F000000000ULL, 0x39000000000ULL, MappingDesc::INVALID, "invalid"}, + {0x39000000000ULL, 0x3A000000000ULL, MappingDesc::SHADOW, "shadow-9"}, + {0x3A000000000ULL, 0x3B000000000ULL, MappingDesc::ORIGIN, "origin-9"}, + {0x3B000000000ULL, 0x3C000000000ULL, MappingDesc::APP, "app-8"}, + {0x3C000000000ULL, 0x3D000000000ULL, MappingDesc::INVALID, "invalid"}, + {0x3D000000000ULL, 0x3E000000000ULL, MappingDesc::SHADOW, "shadow-8"}, + {0x3E000000000ULL, 0x3F000000000ULL, MappingDesc::ORIGIN, "origin-8"}, + {0x3F000000000ULL, 0x40000000000ULL, MappingDesc::APP, "app-9"}, +}; +# define MEM_TO_SHADOW(mem) ((uptr)mem ^ 0x6000000000ULL) +# define SHADOW_TO_ORIGIN(shadow) (((uptr)(shadow)) + 0x1000000000ULL) + #elif SANITIZER_LINUX && defined(__powerpc64__) const MappingDesc kMemoryLayout[] = { @@ -94,6 +149,7 @@ const MappingDesc kMemoryLayout[] = { #elif SANITIZER_LINUX && SANITIZER_WORDSIZE == 64 +#ifdef MSAN_LINUX_X86_64_OLD_MAPPING // Requries PIE binary and ASLR enabled. // Main thread stack and DSOs at 0x7f0000000000 (sometimes 0x7e0000000000). // Heap at 0x600000000000. @@ -105,6 +161,28 @@ const MappingDesc kMemoryLayout[] = { #define MEM_TO_SHADOW(mem) (((uptr)(mem)) & ~0x400000000000ULL) #define SHADOW_TO_ORIGIN(mem) (((uptr)(mem)) + 0x200000000000ULL) +#else // MSAN_LINUX_X86_64_OLD_MAPPING +// All of the following configurations are supported. +// ASLR disabled: main executable and DSOs at 0x555550000000 +// PIE and ASLR: main executable and DSOs at 0x7f0000000000 +// non-PIE: main executable below 0x100000000, DSOs at 0x7f0000000000 +// Heap at 0x700000000000. +const MappingDesc kMemoryLayout[] = { + {0x000000000000ULL, 0x010000000000ULL, MappingDesc::APP, "app-1"}, + {0x010000000000ULL, 0x100000000000ULL, MappingDesc::SHADOW, "shadow-2"}, + {0x100000000000ULL, 0x110000000000ULL, MappingDesc::INVALID, "invalid"}, + {0x110000000000ULL, 0x200000000000ULL, MappingDesc::ORIGIN, "origin-2"}, + {0x200000000000ULL, 0x300000000000ULL, MappingDesc::SHADOW, "shadow-3"}, + {0x300000000000ULL, 0x400000000000ULL, MappingDesc::ORIGIN, "origin-3"}, + {0x400000000000ULL, 0x500000000000ULL, MappingDesc::INVALID, "invalid"}, + {0x500000000000ULL, 0x510000000000ULL, MappingDesc::SHADOW, "shadow-1"}, + {0x510000000000ULL, 0x600000000000ULL, MappingDesc::APP, "app-2"}, + {0x600000000000ULL, 0x610000000000ULL, MappingDesc::ORIGIN, "origin-1"}, + {0x610000000000ULL, 0x700000000000ULL, MappingDesc::INVALID, "invalid"}, + {0x700000000000ULL, 0x800000000000ULL, MappingDesc::APP, "app-3"}}; +#define MEM_TO_SHADOW(mem) (((uptr)(mem)) ^ 0x500000000000ULL) +#define SHADOW_TO_ORIGIN(mem) (((uptr)(mem)) + 0x100000000000ULL) +#endif // MSAN_LINUX_X86_64_OLD_MAPPING #else #error "Unsupported platform" @@ -148,6 +226,7 @@ bool InitShadow(bool init_origins); char *GetProcSelfMaps(); void InitializeInterceptors(); +void MsanAllocatorInit(); void MsanAllocatorThreadFinish(); void *MsanCalloc(StackTrace *stack, uptr nmemb, uptr size); void *MsanReallocate(StackTrace *stack, void *oldp, uptr size, @@ -167,7 +246,6 @@ struct SymbolizerScope { ~SymbolizerScope() { ExitSymbolizer(); } }; -void MsanDie(); void PrintWarning(uptr pc, uptr bp); void PrintWarningWithOrigin(uptr pc, uptr bp, u32 origin); @@ -224,8 +302,6 @@ class ScopedThreadLocalStateBackup { u64 va_arg_overflow_size_tls; }; -extern void (*death_callback)(void); - void MsanTSDInit(void (*destructor)(void *tsd)); void *MsanTSDGet(); void MsanTSDSet(void *tsd); diff --git a/contrib/compiler-rt/lib/msan/msan_allocator.cc b/contrib/compiler-rt/lib/msan/msan_allocator.cc index 6df35664279f..b7d394729bfc 100644 --- a/contrib/compiler-rt/lib/msan/msan_allocator.cc +++ b/contrib/compiler-rt/lib/msan/msan_allocator.cc @@ -49,15 +49,21 @@ struct MsanMapUnmapCallback { typedef SizeClassAllocator32<0, SANITIZER_MMAP_RANGE_SIZE, sizeof(Metadata), SizeClassMap, kRegionSizeLog, ByteMap, MsanMapUnmapCallback> PrimaryAllocator; + #elif defined(__x86_64__) +#if SANITIZER_LINUX && !defined(MSAN_LINUX_X86_64_OLD_MAPPING) + static const uptr kAllocatorSpace = 0x700000000000ULL; +#else static const uptr kAllocatorSpace = 0x600000000000ULL; - static const uptr kAllocatorSize = 0x80000000000; // 8T. +#endif + static const uptr kAllocatorSize = 0x80000000000; // 8T. static const uptr kMetadataSize = sizeof(Metadata); static const uptr kMaxAllowedMallocSize = 8UL << 30; typedef SizeClassAllocator64<kAllocatorSpace, kAllocatorSize, kMetadataSize, DefaultSizeClassMap, MsanMapUnmapCallback> PrimaryAllocator; + #elif defined(__powerpc64__) static const uptr kAllocatorSpace = 0x300000000000; static const uptr kAllocatorSize = 0x020000000000; // 2T @@ -67,6 +73,16 @@ struct MsanMapUnmapCallback { typedef SizeClassAllocator64<kAllocatorSpace, kAllocatorSize, kMetadataSize, DefaultSizeClassMap, MsanMapUnmapCallback> PrimaryAllocator; +#elif defined(__aarch64__) + static const uptr kMaxAllowedMallocSize = 2UL << 30; // 2G + static const uptr kRegionSizeLog = 20; + static const uptr kNumRegions = SANITIZER_MMAP_RANGE_SIZE >> kRegionSizeLog; + typedef TwoLevelByteMap<(kNumRegions >> 12), 1 << 12> ByteMap; + typedef CompactSizeClassMap SizeClassMap; + + typedef SizeClassAllocator32<0, SANITIZER_MMAP_RANGE_SIZE, sizeof(Metadata), + SizeClassMap, kRegionSizeLog, ByteMap, + MsanMapUnmapCallback> PrimaryAllocator; #endif typedef SizeClassAllocatorLocalCache<PrimaryAllocator> AllocatorCache; typedef LargeMmapAllocator<MsanMapUnmapCallback> SecondaryAllocator; @@ -77,12 +93,7 @@ static Allocator allocator; static AllocatorCache fallback_allocator_cache; static SpinMutex fallback_mutex; -static int inited = 0; - -static inline void Init() { - if (inited) return; - __msan_init(); - inited = true; // this must happen before any threads are created. +void MsanAllocatorInit() { allocator.Init(common_flags()->allocator_may_return_null); } @@ -98,7 +109,6 @@ void MsanThreadLocalMallocStorage::CommitBack() { static void *MsanAllocate(StackTrace *stack, uptr size, uptr alignment, bool zeroise) { - Init(); if (size > kMaxAllowedMallocSize) { Report("WARNING: MemorySanitizer failed to allocate %p bytes\n", (void *)size); @@ -133,7 +143,6 @@ static void *MsanAllocate(StackTrace *stack, uptr size, uptr alignment, void MsanDeallocate(StackTrace *stack, void *p) { CHECK(p); - Init(); MSAN_FREE_HOOK(p); Metadata *meta = reinterpret_cast<Metadata *>(allocator.GetMetaData(p)); uptr size = meta->requested_size; @@ -160,10 +169,9 @@ void MsanDeallocate(StackTrace *stack, void *p) { } void *MsanCalloc(StackTrace *stack, uptr nmemb, uptr size) { - Init(); if (CallocShouldReturnNullDueToOverflow(size, nmemb)) return allocator.ReturnNullOrDie(); - return MsanReallocate(stack, 0, nmemb * size, sizeof(u64), true); + return MsanReallocate(stack, nullptr, nmemb * size, sizeof(u64), true); } void *MsanReallocate(StackTrace *stack, void *old_p, uptr new_size, @@ -172,7 +180,7 @@ void *MsanReallocate(StackTrace *stack, void *old_p, uptr new_size, return MsanAllocate(stack, new_size, alignment, zeroise); if (!new_size) { MsanDeallocate(stack, old_p); - return 0; + return nullptr; } Metadata *meta = reinterpret_cast<Metadata*>(allocator.GetMetaData(old_p)); uptr old_size = meta->requested_size; @@ -202,14 +210,14 @@ void *MsanReallocate(StackTrace *stack, void *old_p, uptr new_size, } static uptr AllocationSize(const void *p) { - if (p == 0) return 0; + if (!p) return 0; const void *beg = allocator.GetBlockBegin(p); if (beg != p) return 0; Metadata *b = (Metadata *)allocator.GetMetaData(p); return b->requested_size; } -} // namespace __msan +} // namespace __msan using namespace __msan; diff --git a/contrib/compiler-rt/lib/msan/msan_chained_origin_depot.cc b/contrib/compiler-rt/lib/msan/msan_chained_origin_depot.cc index c21e8e82746a..e2796fd46464 100644 --- a/contrib/compiler-rt/lib/msan/msan_chained_origin_depot.cc +++ b/contrib/compiler-rt/lib/msan/msan_chained_origin_depot.cc @@ -28,12 +28,15 @@ struct ChainedOriginDepotNode { u32 prev_id; typedef ChainedOriginDepotDesc args_type; + bool eq(u32 hash, const args_type &args) const { return here_id == args.here_id && prev_id == args.prev_id; } + static uptr storage_size(const args_type &args) { return sizeof(ChainedOriginDepotNode); } + /* This is murmur2 hash for the 64->32 bit case. It does not behave all that well because the keys have a very biased distribution (I've seen 7-element buckets with the table only 14% full). @@ -76,19 +79,22 @@ struct ChainedOriginDepotNode { here_id = args.here_id; prev_id = args.prev_id; } + args_type load() const { args_type ret = {here_id, prev_id}; return ret; } + struct Handle { ChainedOriginDepotNode *node_; - Handle() : node_(0) {} + Handle() : node_(nullptr) {} explicit Handle(ChainedOriginDepotNode *node) : node_(node) {} bool valid() { return node_; } u32 id() { return node_->id; } int here_id() { return node_->here_id; } int prev_id() { return node_->prev_id; } }; + Handle get_handle() { return Handle(this); } typedef Handle handle_type; @@ -123,4 +129,4 @@ void ChainedOriginDepotUnlockAll() { chainedOriginDepot.UnlockAll(); } -} // namespace __msan +} // namespace __msan diff --git a/contrib/compiler-rt/lib/msan/msan_flags.inc b/contrib/compiler-rt/lib/msan/msan_flags.inc index cb58ffc4aba7..a7ff6c586071 100644 --- a/contrib/compiler-rt/lib/msan/msan_flags.inc +++ b/contrib/compiler-rt/lib/msan/msan_flags.inc @@ -17,13 +17,15 @@ // MSAN_FLAG(Type, Name, DefaultValue, Description) // See COMMON_FLAG in sanitizer_flags.inc for more details. -MSAN_FLAG(int, exit_code, 77, "") +MSAN_FLAG(int, exit_code, -1, + "DEPRECATED. Use exitcode from common flags instead.") MSAN_FLAG(int, origin_history_size, Origin::kMaxDepth, "") MSAN_FLAG(int, origin_history_per_stack_limit, 20000, "") MSAN_FLAG(bool, poison_heap_with_zeroes, false, "") MSAN_FLAG(bool, poison_stack_with_zeroes, false, "") MSAN_FLAG(bool, poison_in_malloc, true, "") MSAN_FLAG(bool, poison_in_free, true, "") +MSAN_FLAG(bool, poison_in_dtor, false, "") MSAN_FLAG(bool, report_umrs, true, "") MSAN_FLAG(bool, wrap_signals, true, "") MSAN_FLAG(bool, print_stats, false, "") diff --git a/contrib/compiler-rt/lib/msan/msan_interceptors.cc b/contrib/compiler-rt/lib/msan/msan_interceptors.cc index 6d5a056a3bb3..fc28e080f262 100644 --- a/contrib/compiler-rt/lib/msan/msan_interceptors.cc +++ b/contrib/compiler-rt/lib/msan/msan_interceptors.cc @@ -166,7 +166,7 @@ INTERCEPTOR(int, posix_memalign, void **memptr, SIZE_T alignment, SIZE_T size) { GET_MALLOC_STACK_TRACE; CHECK_EQ(alignment & (alignment - 1), 0); CHECK_NE(memptr, 0); - *memptr = MsanReallocate(&stack, 0, size, alignment, false); + *memptr = MsanReallocate(&stack, nullptr, size, alignment, false); CHECK_NE(*memptr, 0); __msan_unpoison(memptr, sizeof(*memptr)); return 0; @@ -176,7 +176,7 @@ INTERCEPTOR(int, posix_memalign, void **memptr, SIZE_T alignment, SIZE_T size) { INTERCEPTOR(void *, memalign, SIZE_T boundary, SIZE_T size) { GET_MALLOC_STACK_TRACE; CHECK_EQ(boundary & (boundary - 1), 0); - void *ptr = MsanReallocate(&stack, 0, size, boundary, false); + void *ptr = MsanReallocate(&stack, nullptr, size, boundary, false); return ptr; } #define MSAN_MAYBE_INTERCEPT_MEMALIGN INTERCEPT_FUNCTION(memalign) @@ -187,21 +187,21 @@ INTERCEPTOR(void *, memalign, SIZE_T boundary, SIZE_T size) { INTERCEPTOR(void *, aligned_alloc, SIZE_T boundary, SIZE_T size) { GET_MALLOC_STACK_TRACE; CHECK_EQ(boundary & (boundary - 1), 0); - void *ptr = MsanReallocate(&stack, 0, size, boundary, false); + void *ptr = MsanReallocate(&stack, nullptr, size, boundary, false); return ptr; } INTERCEPTOR(void *, __libc_memalign, SIZE_T boundary, SIZE_T size) { GET_MALLOC_STACK_TRACE; CHECK_EQ(boundary & (boundary - 1), 0); - void *ptr = MsanReallocate(&stack, 0, size, boundary, false); + void *ptr = MsanReallocate(&stack, nullptr, size, boundary, false); DTLS_on_libc_memalign(ptr, size * boundary); return ptr; } INTERCEPTOR(void *, valloc, SIZE_T size) { GET_MALLOC_STACK_TRACE; - void *ptr = MsanReallocate(&stack, 0, size, GetPageSizeCached(), false); + void *ptr = MsanReallocate(&stack, nullptr, size, GetPageSizeCached(), false); return ptr; } @@ -214,7 +214,7 @@ INTERCEPTOR(void *, pvalloc, SIZE_T size) { // pvalloc(0) should allocate one page. size = PageSize; } - void *ptr = MsanReallocate(&stack, 0, size, PageSize, false); + void *ptr = MsanReallocate(&stack, nullptr, size, PageSize, false); return ptr; } #define MSAN_MAYBE_INTERCEPT_PVALLOC INTERCEPT_FUNCTION(pvalloc) @@ -224,14 +224,14 @@ INTERCEPTOR(void *, pvalloc, SIZE_T size) { INTERCEPTOR(void, free, void *ptr) { GET_MALLOC_STACK_TRACE; - if (ptr == 0) return; + if (!ptr) return; MsanDeallocate(&stack, ptr); } #if !SANITIZER_FREEBSD INTERCEPTOR(void, cfree, void *ptr) { GET_MALLOC_STACK_TRACE; - if (ptr == 0) return; + if (!ptr) return; MsanDeallocate(&stack, ptr); } #define MSAN_MAYBE_INTERCEPT_CFREE INTERCEPT_FUNCTION(cfree) @@ -245,9 +245,15 @@ INTERCEPTOR(uptr, malloc_usable_size, void *ptr) { #if !SANITIZER_FREEBSD // This function actually returns a struct by value, but we can't unpoison a -// temporary! The following is equivalent on all supported platforms, and we -// have a test to confirm that. +// temporary! The following is equivalent on all supported platforms but +// aarch64 (which uses a different register for sret value). We have a test +// to confirm that. INTERCEPTOR(void, mallinfo, __sanitizer_mallinfo *sret) { +#ifdef __aarch64__ + uptr r8; + asm volatile("mov %0,x8" : "=r" (r8)); + sret = reinterpret_cast<__sanitizer_mallinfo*>(r8); +#endif REAL(memset)(sret, 0, sizeof(*sret)); __msan_unpoison(sret, sizeof(*sret)); } @@ -994,7 +1000,7 @@ INTERCEPTOR(void *, realloc, void *ptr, SIZE_T size) { INTERCEPTOR(void *, malloc, SIZE_T size) { GET_MALLOC_STACK_TRACE; - return MsanReallocate(&stack, 0, size, sizeof(u64), false); + return MsanReallocate(&stack, nullptr, size, sizeof(u64), false); } void __msan_allocated_memory(const void *data, uptr size) { @@ -1005,6 +1011,19 @@ void __msan_allocated_memory(const void *data, uptr size) { } } +void __msan_copy_shadow(void *dest, const void *src, uptr n) { + GET_STORE_STACK_TRACE; + MoveShadowAndOrigin(dest, src, n, &stack); +} + +void __sanitizer_dtor_callback(const void *data, uptr size) { + GET_MALLOC_STACK_TRACE; + if (flags()->poison_in_dtor) { + stack.tag = STACK_TRACE_TAG_POISON; + PoisonMemory(data, size, &stack); + } +} + INTERCEPTOR(void *, mmap, void *addr, SIZE_T length, int prot, int flags, int fd, OFF_T offset) { if (msan_init_is_running) @@ -1015,7 +1034,7 @@ INTERCEPTOR(void *, mmap, void *addr, SIZE_T length, int prot, int flags, *__errno_location() = errno_EINVAL; return (void *)-1; } else { - addr = 0; + addr = nullptr; } } void *res = REAL(mmap)(addr, length, prot, flags, fd, offset); @@ -1033,7 +1052,7 @@ INTERCEPTOR(void *, mmap64, void *addr, SIZE_T length, int prot, int flags, *__errno_location() = errno_EINVAL; return (void *)-1; } else { - addr = 0; + addr = nullptr; } } void *res = REAL(mmap64)(addr, length, prot, flags, fd, offset); @@ -1069,7 +1088,7 @@ INTERCEPTOR(int, dladdr, void *addr, dlinfo *info) { INTERCEPTOR(char *, dlerror, int fake) { ENSURE_MSAN_INITED(); char *res = REAL(dlerror)(fake); - if (res != 0) __msan_unpoison(res, REAL(strlen)(res) + 1); + if (res) __msan_unpoison(res, REAL(strlen)(res) + 1); return res; } @@ -1084,6 +1103,8 @@ static int msan_dl_iterate_phdr_cb(__sanitizer_dl_phdr_info *info, SIZE_T size, void *data) { if (info) { __msan_unpoison(info, size); + if (info->dlpi_phdr && info->dlpi_phnum) + __msan_unpoison(info->dlpi_phdr, struct_ElfW_Phdr_sz * info->dlpi_phnum); if (info->dlpi_name) __msan_unpoison(info->dlpi_name, REAL(strlen)(info->dlpi_name) + 1); } @@ -1164,7 +1185,7 @@ INTERCEPTOR(int, sigaction, int signo, const __sanitizer_sigaction *act, CHECK_LT(signo, kMaxSignals); uptr old_cb = atomic_load(&sigactions[signo], memory_order_relaxed); __sanitizer_sigaction new_act; - __sanitizer_sigaction *pnew_act = act ? &new_act : 0; + __sanitizer_sigaction *pnew_act = act ? &new_act : nullptr; if (act) { REAL(memcpy)(pnew_act, act, sizeof(__sanitizer_sigaction)); uptr cb = (uptr)pnew_act->sigaction; @@ -1221,7 +1242,7 @@ INTERCEPTOR(int, pthread_create, void *th, void *attr, void *(*callback)(void*), void * param) { ENSURE_MSAN_INITED(); // for GetTlsSize() __sanitizer_pthread_attr_t myattr; - if (attr == 0) { + if (!attr) { pthread_attr_init(&myattr); attr = &myattr; } @@ -1327,6 +1348,28 @@ INTERCEPTOR(int, fork, void) { return pid; } +INTERCEPTOR(int, openpty, int *amaster, int *aslave, char *name, + const void *termp, const void *winp) { + ENSURE_MSAN_INITED(); + InterceptorScope interceptor_scope; + int res = REAL(openpty)(amaster, aslave, name, termp, winp); + if (!res) { + __msan_unpoison(amaster, sizeof(*amaster)); + __msan_unpoison(aslave, sizeof(*aslave)); + } + return res; +} + +INTERCEPTOR(int, forkpty, int *amaster, char *name, const void *termp, + const void *winp) { + ENSURE_MSAN_INITED(); + InterceptorScope interceptor_scope; + int res = REAL(forkpty)(amaster, name, termp, winp); + if (res != -1) + __msan_unpoison(amaster, sizeof(*amaster)); + return res; +} + struct MSanInterceptorContext { bool in_interceptor_scope; }; @@ -1338,7 +1381,7 @@ int OnExit() { return 0; } -} // namespace __msan +} // namespace __msan // A version of CHECK_UNPOISONED using a saved scope value. Used in common // interceptors. @@ -1391,10 +1434,11 @@ int OnExit() { } while (false) // FIXME #define COMMON_INTERCEPTOR_BLOCK_REAL(name) REAL(name) #define COMMON_INTERCEPTOR_ON_EXIT(ctx) OnExit() -#define COMMON_INTERCEPTOR_LIBRARY_LOADED(filename, handle) \ - do { \ - link_map *map = GET_LINK_MAP_BY_DLOPEN_HANDLE((handle)); \ - if (map) ForEachMappedRegion(map, __msan_unpoison); \ +#define COMMON_INTERCEPTOR_LIBRARY_LOADED(filename, handle) \ + do { \ + link_map *map = GET_LINK_MAP_BY_DLOPEN_HANDLE((handle)); \ + if (filename && map) \ + ForEachMappedRegion(map, __msan_unpoison); \ } while (false) #define COMMON_INTERCEPTOR_GET_TLS_RANGE(begin, end) \ @@ -1591,7 +1635,9 @@ void InitializeInterceptors() { INTERCEPT_FUNCTION(__cxa_atexit); INTERCEPT_FUNCTION(shmat); INTERCEPT_FUNCTION(fork); + INTERCEPT_FUNCTION(openpty); + INTERCEPT_FUNCTION(forkpty); inited = 1; } -} // namespace __msan +} // namespace __msan diff --git a/contrib/compiler-rt/lib/msan/msan_interface_internal.h b/contrib/compiler-rt/lib/msan/msan_interface_internal.h index f4d37d96c5b5..c1e02ce72bf4 100644 --- a/contrib/compiler-rt/lib/msan/msan_interface_internal.h +++ b/contrib/compiler-rt/lib/msan/msan_interface_internal.h @@ -27,7 +27,7 @@ SANITIZER_INTERFACE_ATTRIBUTE void __msan_init(); // Print a warning and maybe return. -// This function can die based on flags()->exit_code. +// This function can die based on common_flags()->exitcode. SANITIZER_INTERFACE_ATTRIBUTE void __msan_warning(); @@ -106,10 +106,6 @@ int __msan_origin_is_descendant_or_same(u32 this_id, u32 prev_id); SANITIZER_INTERFACE_ATTRIBUTE void __msan_clear_on_return(); -// Default: -1 (don't exit on error). -SANITIZER_INTERFACE_ATTRIBUTE -void __msan_set_exit_code(int exit_code); - SANITIZER_INTERFACE_ATTRIBUTE void __msan_set_keep_going(int keep_going); @@ -140,6 +136,11 @@ void __msan_partial_poison(const void* data, void* shadow, uptr size); SANITIZER_INTERFACE_ATTRIBUTE void __msan_allocated_memory(const void* data, uptr size); +// Tell MSan about newly destroyed memory. Memory will be marked +// uninitialized. +SANITIZER_INTERFACE_ATTRIBUTE +void __sanitizer_dtor_callback(const void* data, uptr size); + SANITIZER_INTERFACE_ATTRIBUTE u16 __sanitizer_unaligned_load16(const uu16 *p); @@ -160,6 +161,9 @@ void __sanitizer_unaligned_store64(uu64 *p, u64 x); SANITIZER_INTERFACE_ATTRIBUTE void __msan_set_death_callback(void (*callback)(void)); + +SANITIZER_INTERFACE_ATTRIBUTE +void __msan_copy_shadow(void *dst, const void *src, uptr size); } // extern "C" #endif // MSAN_INTERFACE_INTERNAL_H diff --git a/contrib/compiler-rt/lib/msan/msan_linux.cc b/contrib/compiler-rt/lib/msan/msan_linux.cc index 7025ef6c812d..ab3be91fcf8d 100644 --- a/contrib/compiler-rt/lib/msan/msan_linux.cc +++ b/contrib/compiler-rt/lib/msan/msan_linux.cc @@ -56,7 +56,7 @@ static bool CheckMemoryRangeAvailability(uptr beg, uptr size) { static bool ProtectMemoryRange(uptr beg, uptr size, const char *name) { if (size > 0) { void *addr = MmapNoAccess(beg, size, name); - if (beg == 0 && addr != 0) { + if (beg == 0 && addr) { // Depending on the kernel configuration, we may not be able to protect // the page at address zero. uptr gap = 16 * GetPageSizeCached(); @@ -119,12 +119,18 @@ bool InitShadow(bool init_origins) { return false; } + const uptr maxVirtualAddress = GetMaxVirtualAddress(); + for (unsigned i = 0; i < kMemoryLayoutSize; ++i) { uptr start = kMemoryLayout[i].start; uptr end = kMemoryLayout[i].end; uptr size= end - start; MappingDesc::Type type = kMemoryLayout[i].type; + // Check if the segment should be mapped based on platform constraints. + if (start >= maxVirtualAddress) + continue; + bool map = type == MappingDesc::SHADOW || (init_origins && type == MappingDesc::ORIGIN); bool protect = type == MappingDesc::INVALID || @@ -151,20 +157,13 @@ bool InitShadow(bool init_origins) { return true; } -void MsanDie() { - if (common_flags()->coverage) - __sanitizer_cov_dump(); - if (death_callback) - death_callback(); - internal__exit(flags()->exit_code); -} - static void MsanAtExit(void) { if (flags()->print_stats && (flags()->atexit || msan_report_count > 0)) ReportStats(); if (msan_report_count > 0) { ReportAtExitStatistics(); - if (flags()->exit_code) _exit(flags()->exit_code); + if (common_flags()->exitcode) + internal__exit(common_flags()->exitcode); } } @@ -211,6 +210,6 @@ void MsanTSDDtor(void *tsd) { MsanThread::TSDDtor(tsd); } -} // namespace __msan +} // namespace __msan -#endif // SANITIZER_FREEBSD || SANITIZER_LINUX +#endif // SANITIZER_FREEBSD || SANITIZER_LINUX diff --git a/contrib/compiler-rt/lib/msan/msan_new_delete.cc b/contrib/compiler-rt/lib/msan/msan_new_delete.cc index c8bc0651b507..540100316693 100644 --- a/contrib/compiler-rt/lib/msan/msan_new_delete.cc +++ b/contrib/compiler-rt/lib/msan/msan_new_delete.cc @@ -45,9 +45,9 @@ void *operator new[](size_t size, std::nothrow_t const&) { OPERATOR_NEW_BODY; } if (ptr) MsanDeallocate(&stack, ptr) INTERCEPTOR_ATTRIBUTE -void operator delete(void *ptr) throw() { OPERATOR_DELETE_BODY; } +void operator delete(void *ptr) NOEXCEPT { OPERATOR_DELETE_BODY; } INTERCEPTOR_ATTRIBUTE -void operator delete[](void *ptr) throw() { OPERATOR_DELETE_BODY; } +void operator delete[](void *ptr) NOEXCEPT { OPERATOR_DELETE_BODY; } INTERCEPTOR_ATTRIBUTE void operator delete(void *ptr, std::nothrow_t const&) { OPERATOR_DELETE_BODY; } INTERCEPTOR_ATTRIBUTE diff --git a/contrib/compiler-rt/lib/msan/msan_thread.h b/contrib/compiler-rt/lib/msan/msan_thread.h index bc605b89a505..ed22e67edd50 100644 --- a/contrib/compiler-rt/lib/msan/msan_thread.h +++ b/contrib/compiler-rt/lib/msan/msan_thread.h @@ -32,7 +32,7 @@ class MsanThread { uptr stack_bottom() { return stack_bottom_; } uptr tls_begin() { return tls_begin_; } uptr tls_end() { return tls_end_; } - bool IsMainThread() { return start_routine_ == 0; } + bool IsMainThread() { return start_routine_ == nullptr; } bool AddrIsInStack(uptr addr) { return addr >= stack_bottom_ && addr < stack_top_; diff --git a/contrib/compiler-rt/lib/msan/tests/msan_loadable.cc b/contrib/compiler-rt/lib/msan/tests/msan_loadable.cc deleted file mode 100644 index 06e880f90dee..000000000000 --- a/contrib/compiler-rt/lib/msan/tests/msan_loadable.cc +++ /dev/null @@ -1,27 +0,0 @@ -//===-- msan_loadable.cc --------------------------------------------------===// -// -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. -// -//===----------------------------------------------------------------------===// -// -// This file is a part of MemorySanitizer. -// -// MemorySanitizer unit tests. -//===----------------------------------------------------------------------===// - -#include "msan/msan_interface_internal.h" -#include <stdlib.h> - -static void *dso_global; - -// No name mangling. -extern "C" { - -void **get_dso_global() { - return &dso_global; -} - -} diff --git a/contrib/compiler-rt/lib/msan/tests/msan_test.cc b/contrib/compiler-rt/lib/msan/tests/msan_test.cc deleted file mode 100644 index 00dd20a3d775..000000000000 --- a/contrib/compiler-rt/lib/msan/tests/msan_test.cc +++ /dev/null @@ -1,4287 +0,0 @@ -//===-- msan_test.cc ------------------------------------------------------===// -// -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. -// -//===----------------------------------------------------------------------===// -// -// This file is a part of MemorySanitizer. -// -// MemorySanitizer unit tests. -//===----------------------------------------------------------------------===// - -#ifndef MSAN_EXTERNAL_TEST_CONFIG -#include "msan_test_config.h" -#endif // MSAN_EXTERNAL_TEST_CONFIG - -#include "sanitizer_common/tests/sanitizer_test_utils.h" - -#include "sanitizer/allocator_interface.h" -#include "sanitizer/msan_interface.h" - -#if defined(__FreeBSD__) -# define _KERNEL // To declare 'shminfo' structure. -# include <sys/shm.h> -# undef _KERNEL -extern "C" { -// <sys/shm.h> doesn't declare these functions in _KERNEL mode. -void *shmat(int, const void *, int); -int shmget(key_t, size_t, int); -int shmctl(int, int, struct shmid_ds *); -int shmdt(const void *); -} -#endif - -#include <inttypes.h> -#include <stdlib.h> -#include <stdarg.h> -#include <stdio.h> -#include <wchar.h> -#include <math.h> - -#include <arpa/inet.h> -#include <dlfcn.h> -#include <grp.h> -#include <unistd.h> -#include <link.h> -#include <limits.h> -#include <sys/time.h> -#include <poll.h> -#include <sys/types.h> -#include <sys/stat.h> -#include <fcntl.h> -#include <sys/resource.h> -#include <sys/ioctl.h> -#include <sys/statvfs.h> -#include <sys/utsname.h> -#include <sys/mman.h> -#include <dirent.h> -#include <pwd.h> -#include <sys/socket.h> -#include <netdb.h> -#include <wordexp.h> -#include <sys/ipc.h> -#include <sys/shm.h> - -#if !defined(__FreeBSD__) -# include <malloc.h> -# include <sys/sysinfo.h> -# include <sys/vfs.h> -# include <mntent.h> -# include <netinet/ether.h> -#else -# include <signal.h> -# include <netinet/in.h> -# include <pthread_np.h> -# include <sys/uio.h> -# include <sys/mount.h> -# include <sys/sysctl.h> -# include <net/ethernet.h> -# define f_namelen f_namemax // FreeBSD names this statfs field so. -# define cpu_set_t cpuset_t -extern "C" { -// FreeBSD's <ssp/string.h> defines mempcpy() to be a macro expanding into -// a __builtin___mempcpy_chk() call, but since Msan RTL defines it as an -// ordinary function, we can declare it here to complete the tests. -void *mempcpy(void *dest, const void *src, size_t n); -} -#endif - -#if defined(__i386__) || defined(__x86_64__) -# include <emmintrin.h> -# define MSAN_HAS_M128 1 -#else -# define MSAN_HAS_M128 0 -#endif - -#ifdef __AVX2__ -# include <immintrin.h> -#endif - -// On FreeBSD procfs is not enabled by default. -#if defined(__FreeBSD__) -# define FILE_TO_READ "/bin/cat" -# define DIR_TO_READ "/bin" -# define SUBFILE_TO_READ "cat" -# define SYMLINK_TO_READ "/usr/bin/tar" -# define SUPERUSER_GROUP "wheel" -#else -# define FILE_TO_READ "/proc/self/stat" -# define DIR_TO_READ "/proc/self" -# define SUBFILE_TO_READ "stat" -# define SYMLINK_TO_READ "/proc/self/exe" -# define SUPERUSER_GROUP "root" -#endif - -const size_t kPageSize = 4096; -const size_t kMaxPathLength = 4096; - -typedef unsigned char U1; -typedef unsigned short U2; // NOLINT -typedef unsigned int U4; -typedef unsigned long long U8; // NOLINT -typedef signed char S1; -typedef signed short S2; // NOLINT -typedef signed int S4; -typedef signed long long S8; // NOLINT -#define NOINLINE __attribute__((noinline)) -#define INLINE __attribute__((always_inline)) - -static bool TrackingOrigins() { - S8 x; - __msan_set_origin(&x, sizeof(x), 0x1234); - U4 origin = __msan_get_origin(&x); - __msan_set_origin(&x, sizeof(x), 0); - return __msan_origin_is_descendant_or_same(origin, 0x1234); -} - -#define EXPECT_ORIGIN(expected, origin) \ - EXPECT_TRUE(__msan_origin_is_descendant_or_same((origin), (expected))) - -#define EXPECT_UMR(action) \ - do { \ - __msan_set_expect_umr(1); \ - action; \ - __msan_set_expect_umr(0); \ - } while (0) - -#define EXPECT_UMR_O(action, origin) \ - do { \ - __msan_set_expect_umr(1); \ - action; \ - __msan_set_expect_umr(0); \ - if (TrackingOrigins()) EXPECT_ORIGIN(origin, __msan_get_umr_origin()); \ - } while (0) - -#define EXPECT_POISONED(x) ExpectPoisoned(x) - -template<typename T> -void ExpectPoisoned(const T& t) { - EXPECT_NE(-1, __msan_test_shadow((void*)&t, sizeof(t))); -} - -#define EXPECT_POISONED_O(x, origin) \ - ExpectPoisonedWithOrigin(x, origin) - -template<typename T> -void ExpectPoisonedWithOrigin(const T& t, unsigned origin) { - EXPECT_NE(-1, __msan_test_shadow((void*)&t, sizeof(t))); - if (TrackingOrigins()) EXPECT_ORIGIN(origin, __msan_get_origin((void *)&t)); -} - -#define EXPECT_NOT_POISONED(x) EXPECT_EQ(true, TestForNotPoisoned((x))) - -template<typename T> -bool TestForNotPoisoned(const T& t) { - return __msan_test_shadow((void*)&t, sizeof(t)) == -1; -} - -static U8 poisoned_array[100]; -template<class T> -T *GetPoisoned(int i = 0, T val = 0) { - T *res = (T*)&poisoned_array[i]; - *res = val; - __msan_poison(&poisoned_array[i], sizeof(T)); - return res; -} - -template<class T> -T *GetPoisonedO(int i, U4 origin, T val = 0) { - T *res = (T*)&poisoned_array[i]; - *res = val; - __msan_poison(&poisoned_array[i], sizeof(T)); - __msan_set_origin(&poisoned_array[i], sizeof(T), origin); - return res; -} - -template<typename T> -T Poisoned(T v = 0, T s = (T)(-1)) { - __msan_partial_poison(&v, &s, sizeof(T)); - return v; -} - -template<class T> NOINLINE T ReturnPoisoned() { return *GetPoisoned<T>(); } - -static volatile int g_one = 1; -static volatile int g_zero = 0; -static volatile int g_0 = 0; -static volatile int g_1 = 1; - -S4 a_s4[100]; -S8 a_s8[100]; - -// Check that malloc poisons memory. -// A lot of tests below depend on this. -TEST(MemorySanitizerSanity, PoisonInMalloc) { - int *x = (int*)malloc(sizeof(int)); - EXPECT_POISONED(*x); - free(x); -} - -TEST(MemorySanitizer, NegativeTest1) { - S4 *x = GetPoisoned<S4>(); - if (g_one) - *x = 0; - EXPECT_NOT_POISONED(*x); -} - -TEST(MemorySanitizer, PositiveTest1) { - // Load to store. - EXPECT_POISONED(*GetPoisoned<S1>()); - EXPECT_POISONED(*GetPoisoned<S2>()); - EXPECT_POISONED(*GetPoisoned<S4>()); - EXPECT_POISONED(*GetPoisoned<S8>()); - - // S->S conversions. - EXPECT_POISONED(*GetPoisoned<S1>()); - EXPECT_POISONED(*GetPoisoned<S1>()); - EXPECT_POISONED(*GetPoisoned<S1>()); - - EXPECT_POISONED(*GetPoisoned<S2>()); - EXPECT_POISONED(*GetPoisoned<S2>()); - EXPECT_POISONED(*GetPoisoned<S2>()); - - EXPECT_POISONED(*GetPoisoned<S4>()); - EXPECT_POISONED(*GetPoisoned<S4>()); - EXPECT_POISONED(*GetPoisoned<S4>()); - - EXPECT_POISONED(*GetPoisoned<S8>()); - EXPECT_POISONED(*GetPoisoned<S8>()); - EXPECT_POISONED(*GetPoisoned<S8>()); - - // ZExt - EXPECT_POISONED(*GetPoisoned<U1>()); - EXPECT_POISONED(*GetPoisoned<U1>()); - EXPECT_POISONED(*GetPoisoned<U1>()); - EXPECT_POISONED(*GetPoisoned<U2>()); - EXPECT_POISONED(*GetPoisoned<U2>()); - EXPECT_POISONED(*GetPoisoned<U4>()); - - // Unary ops. - EXPECT_POISONED(- *GetPoisoned<S4>()); - - EXPECT_UMR(a_s4[g_zero] = 100 / *GetPoisoned<S4>(0, 1)); - - - a_s4[g_zero] = 1 - *GetPoisoned<S4>(); - a_s4[g_zero] = 1 + *GetPoisoned<S4>(); -} - -TEST(MemorySanitizer, Phi1) { - S4 c; - if (g_one) { - c = *GetPoisoned<S4>(); - } else { - break_optimization(0); - c = 0; - } - EXPECT_POISONED(c); -} - -TEST(MemorySanitizer, Phi2) { - S4 i = *GetPoisoned<S4>(); - S4 n = g_one; - EXPECT_UMR(for (; i < g_one; i++);); - EXPECT_POISONED(i); -} - -NOINLINE void Arg1ExpectUMR(S4 a1) { EXPECT_POISONED(a1); } -NOINLINE void Arg2ExpectUMR(S4 a1, S4 a2) { EXPECT_POISONED(a2); } -NOINLINE void Arg3ExpectUMR(S1 a1, S4 a2, S8 a3) { EXPECT_POISONED(a3); } - -TEST(MemorySanitizer, ArgTest) { - Arg1ExpectUMR(*GetPoisoned<S4>()); - Arg2ExpectUMR(0, *GetPoisoned<S4>()); - Arg3ExpectUMR(0, 1, *GetPoisoned<S8>()); -} - - -TEST(MemorySanitizer, CallAndRet) { - ReturnPoisoned<S1>(); - ReturnPoisoned<S2>(); - ReturnPoisoned<S4>(); - ReturnPoisoned<S8>(); - - EXPECT_POISONED(ReturnPoisoned<S1>()); - EXPECT_POISONED(ReturnPoisoned<S2>()); - EXPECT_POISONED(ReturnPoisoned<S4>()); - EXPECT_POISONED(ReturnPoisoned<S8>()); -} - -// malloc() in the following test may be optimized to produce a compile-time -// undef value. Check that we trap on the volatile assignment anyway. -TEST(MemorySanitizer, DISABLED_MallocNoIdent) { - S4 *x = (int*)malloc(sizeof(S4)); - EXPECT_POISONED(*x); - free(x); -} - -TEST(MemorySanitizer, Malloc) { - S4 *x = (int*)Ident(malloc(sizeof(S4))); - EXPECT_POISONED(*x); - free(x); -} - -TEST(MemorySanitizer, Realloc) { - S4 *x = (int*)Ident(realloc(0, sizeof(S4))); - EXPECT_POISONED(x[0]); - x[0] = 1; - x = (int*)Ident(realloc(x, 2 * sizeof(S4))); - EXPECT_NOT_POISONED(x[0]); // Ok, was inited before. - EXPECT_POISONED(x[1]); - x = (int*)Ident(realloc(x, 3 * sizeof(S4))); - EXPECT_NOT_POISONED(x[0]); // Ok, was inited before. - EXPECT_POISONED(x[2]); - EXPECT_POISONED(x[1]); - x[2] = 1; // Init this here. Check that after realloc it is poisoned again. - x = (int*)Ident(realloc(x, 2 * sizeof(S4))); - EXPECT_NOT_POISONED(x[0]); // Ok, was inited before. - EXPECT_POISONED(x[1]); - x = (int*)Ident(realloc(x, 3 * sizeof(S4))); - EXPECT_POISONED(x[1]); - EXPECT_POISONED(x[2]); - free(x); -} - -TEST(MemorySanitizer, Calloc) { - S4 *x = (int*)Ident(calloc(1, sizeof(S4))); - EXPECT_NOT_POISONED(*x); // Should not be poisoned. - EXPECT_EQ(0, *x); - free(x); -} - -TEST(MemorySanitizer, CallocReturnsZeroMem) { - size_t sizes[] = {16, 1000, 10000, 100000, 2100000}; - for (size_t s = 0; s < sizeof(sizes)/sizeof(sizes[0]); s++) { - size_t size = sizes[s]; - for (size_t iter = 0; iter < 5; iter++) { - char *x = Ident((char*)calloc(1, size)); - EXPECT_EQ(x[0], 0); - EXPECT_EQ(x[size - 1], 0); - EXPECT_EQ(x[size / 2], 0); - EXPECT_EQ(x[size / 3], 0); - EXPECT_EQ(x[size / 4], 0); - memset(x, 0x42, size); - free(Ident(x)); - } - } -} - -TEST(MemorySanitizer, AndOr) { - U4 *p = GetPoisoned<U4>(); - // We poison two bytes in the midle of a 4-byte word to make the test - // correct regardless of endianness. - ((U1*)p)[1] = 0; - ((U1*)p)[2] = 0xff; - EXPECT_NOT_POISONED(*p & 0x00ffff00); - EXPECT_NOT_POISONED(*p & 0x00ff0000); - EXPECT_NOT_POISONED(*p & 0x0000ff00); - EXPECT_POISONED(*p & 0xff000000); - EXPECT_POISONED(*p & 0x000000ff); - EXPECT_POISONED(*p & 0x0000ffff); - EXPECT_POISONED(*p & 0xffff0000); - - EXPECT_NOT_POISONED(*p | 0xff0000ff); - EXPECT_NOT_POISONED(*p | 0xff00ffff); - EXPECT_NOT_POISONED(*p | 0xffff00ff); - EXPECT_POISONED(*p | 0xff000000); - EXPECT_POISONED(*p | 0x000000ff); - EXPECT_POISONED(*p | 0x0000ffff); - EXPECT_POISONED(*p | 0xffff0000); - - EXPECT_POISONED(*GetPoisoned<bool>() & *GetPoisoned<bool>()); -} - -template<class T> -static bool applyNot(T value, T shadow) { - __msan_partial_poison(&value, &shadow, sizeof(T)); - return !value; -} - -TEST(MemorySanitizer, Not) { - EXPECT_NOT_POISONED(applyNot<U4>(0x0, 0x0)); - EXPECT_NOT_POISONED(applyNot<U4>(0xFFFFFFFF, 0x0)); - EXPECT_POISONED(applyNot<U4>(0xFFFFFFFF, 0xFFFFFFFF)); - EXPECT_NOT_POISONED(applyNot<U4>(0xFF000000, 0x0FFFFFFF)); - EXPECT_NOT_POISONED(applyNot<U4>(0xFF000000, 0x00FFFFFF)); - EXPECT_NOT_POISONED(applyNot<U4>(0xFF000000, 0x0000FFFF)); - EXPECT_NOT_POISONED(applyNot<U4>(0xFF000000, 0x00000000)); - EXPECT_POISONED(applyNot<U4>(0xFF000000, 0xFF000000)); - EXPECT_NOT_POISONED(applyNot<U4>(0xFF800000, 0xFF000000)); - EXPECT_POISONED(applyNot<U4>(0x00008000, 0x00008000)); - - EXPECT_NOT_POISONED(applyNot<U1>(0x0, 0x0)); - EXPECT_NOT_POISONED(applyNot<U1>(0xFF, 0xFE)); - EXPECT_NOT_POISONED(applyNot<U1>(0xFF, 0x0)); - EXPECT_POISONED(applyNot<U1>(0xFF, 0xFF)); - - EXPECT_POISONED(applyNot<void*>((void*)0xFFFFFF, (void*)(-1))); - EXPECT_NOT_POISONED(applyNot<void*>((void*)0xFFFFFF, (void*)(-2))); -} - -TEST(MemorySanitizer, Shift) { - U4 *up = GetPoisoned<U4>(); - ((U1*)up)[0] = 0; - ((U1*)up)[3] = 0xff; - EXPECT_NOT_POISONED(*up >> 30); - EXPECT_NOT_POISONED(*up >> 24); - EXPECT_POISONED(*up >> 23); - EXPECT_POISONED(*up >> 10); - - EXPECT_NOT_POISONED(*up << 30); - EXPECT_NOT_POISONED(*up << 24); - EXPECT_POISONED(*up << 23); - EXPECT_POISONED(*up << 10); - - S4 *sp = (S4*)up; - EXPECT_NOT_POISONED(*sp >> 30); - EXPECT_NOT_POISONED(*sp >> 24); - EXPECT_POISONED(*sp >> 23); - EXPECT_POISONED(*sp >> 10); - - sp = GetPoisoned<S4>(); - ((S1*)sp)[1] = 0; - ((S1*)sp)[2] = 0; - EXPECT_POISONED(*sp >> 31); - - EXPECT_POISONED(100 >> *GetPoisoned<S4>()); - EXPECT_POISONED(100U >> *GetPoisoned<S4>()); -} - -NOINLINE static int GetPoisonedZero() { - int *zero = new int; - *zero = 0; - __msan_poison(zero, sizeof(*zero)); - int res = *zero; - delete zero; - return res; -} - -TEST(MemorySanitizer, LoadFromDirtyAddress) { - int *a = new int; - *a = 0; - EXPECT_UMR(break_optimization((void*)(U8)a[GetPoisonedZero()])); - delete a; -} - -TEST(MemorySanitizer, StoreToDirtyAddress) { - int *a = new int; - EXPECT_UMR(a[GetPoisonedZero()] = 0); - break_optimization(a); - delete a; -} - - -NOINLINE void StackTestFunc() { - S4 p4; - S4 ok4 = 1; - S2 p2; - S2 ok2 = 1; - S1 p1; - S1 ok1 = 1; - break_optimization(&p4); - break_optimization(&ok4); - break_optimization(&p2); - break_optimization(&ok2); - break_optimization(&p1); - break_optimization(&ok1); - - EXPECT_POISONED(p4); - EXPECT_POISONED(p2); - EXPECT_POISONED(p1); - EXPECT_NOT_POISONED(ok1); - EXPECT_NOT_POISONED(ok2); - EXPECT_NOT_POISONED(ok4); -} - -TEST(MemorySanitizer, StackTest) { - StackTestFunc(); -} - -NOINLINE void StackStressFunc() { - int foo[10000]; - break_optimization(foo); -} - -TEST(MemorySanitizer, DISABLED_StackStressTest) { - for (int i = 0; i < 1000000; i++) - StackStressFunc(); -} - -template<class T> -void TestFloatingPoint() { - static volatile T v; - static T g[100]; - break_optimization(&g); - T *x = GetPoisoned<T>(); - T *y = GetPoisoned<T>(1); - EXPECT_POISONED(*x); - EXPECT_POISONED((long long)*x); - EXPECT_POISONED((int)*x); - g[0] = *x; - g[1] = *x + *y; - g[2] = *x - *y; - g[3] = *x * *y; -} - -TEST(MemorySanitizer, FloatingPointTest) { - TestFloatingPoint<float>(); - TestFloatingPoint<double>(); -} - -TEST(MemorySanitizer, DynMem) { - S4 x = 0; - S4 *y = GetPoisoned<S4>(); - memcpy(y, &x, g_one * sizeof(S4)); - EXPECT_NOT_POISONED(*y); -} - -static char *DynRetTestStr; - -TEST(MemorySanitizer, DynRet) { - ReturnPoisoned<S8>(); - EXPECT_NOT_POISONED(atoi("0")); -} - -TEST(MemorySanitizer, DynRet1) { - ReturnPoisoned<S8>(); -} - -struct LargeStruct { - S4 x[10]; -}; - -NOINLINE -LargeStruct LargeRetTest() { - LargeStruct res; - res.x[0] = *GetPoisoned<S4>(); - res.x[1] = *GetPoisoned<S4>(); - res.x[2] = *GetPoisoned<S4>(); - res.x[3] = *GetPoisoned<S4>(); - res.x[4] = *GetPoisoned<S4>(); - res.x[5] = *GetPoisoned<S4>(); - res.x[6] = *GetPoisoned<S4>(); - res.x[7] = *GetPoisoned<S4>(); - res.x[8] = *GetPoisoned<S4>(); - res.x[9] = *GetPoisoned<S4>(); - return res; -} - -TEST(MemorySanitizer, strcmp) { - char s1[10]; - char s2[10]; - strncpy(s1, "foo", 10); - s2[0] = 'f'; - s2[1] = 'n'; - EXPECT_GT(strcmp(s1, s2), 0); - s2[1] = 'o'; - int res; - EXPECT_UMR(res = strcmp(s1, s2)); - EXPECT_NOT_POISONED(res); - EXPECT_EQ(strncmp(s1, s2, 1), 0); -} - -TEST(MemorySanitizer, LargeRet) { - LargeStruct a = LargeRetTest(); - EXPECT_POISONED(a.x[0]); - EXPECT_POISONED(a.x[9]); -} - -TEST(MemorySanitizer, strerror) { - char *buf = strerror(EINVAL); - EXPECT_NOT_POISONED(strlen(buf)); - buf = strerror(123456); - EXPECT_NOT_POISONED(strlen(buf)); -} - -TEST(MemorySanitizer, strerror_r) { - errno = 0; - char buf[1000]; - char *res = (char*) (size_t) strerror_r(EINVAL, buf, sizeof(buf)); - ASSERT_EQ(0, errno); - if (!res) res = buf; // POSIX version success. - EXPECT_NOT_POISONED(strlen(res)); -} - -TEST(MemorySanitizer, fread) { - char *x = new char[32]; - FILE *f = fopen(FILE_TO_READ, "r"); - ASSERT_TRUE(f != NULL); - fread(x, 1, 32, f); - EXPECT_NOT_POISONED(x[0]); - EXPECT_NOT_POISONED(x[16]); - EXPECT_NOT_POISONED(x[31]); - fclose(f); - delete[] x; -} - -TEST(MemorySanitizer, read) { - char *x = new char[32]; - int fd = open(FILE_TO_READ, O_RDONLY); - ASSERT_GT(fd, 0); - int sz = read(fd, x, 32); - ASSERT_EQ(sz, 32); - EXPECT_NOT_POISONED(x[0]); - EXPECT_NOT_POISONED(x[16]); - EXPECT_NOT_POISONED(x[31]); - close(fd); - delete[] x; -} - -TEST(MemorySanitizer, pread) { - char *x = new char[32]; - int fd = open(FILE_TO_READ, O_RDONLY); - ASSERT_GT(fd, 0); - int sz = pread(fd, x, 32, 0); - ASSERT_EQ(sz, 32); - EXPECT_NOT_POISONED(x[0]); - EXPECT_NOT_POISONED(x[16]); - EXPECT_NOT_POISONED(x[31]); - close(fd); - delete[] x; -} - -TEST(MemorySanitizer, readv) { - char buf[2011]; - struct iovec iov[2]; - iov[0].iov_base = buf + 1; - iov[0].iov_len = 5; - iov[1].iov_base = buf + 10; - iov[1].iov_len = 2000; - int fd = open(FILE_TO_READ, O_RDONLY); - ASSERT_GT(fd, 0); - int sz = readv(fd, iov, 2); - ASSERT_GE(sz, 0); - ASSERT_LE(sz, 5 + 2000); - ASSERT_GT((size_t)sz, iov[0].iov_len); - EXPECT_POISONED(buf[0]); - EXPECT_NOT_POISONED(buf[1]); - EXPECT_NOT_POISONED(buf[5]); - EXPECT_POISONED(buf[6]); - EXPECT_POISONED(buf[9]); - EXPECT_NOT_POISONED(buf[10]); - EXPECT_NOT_POISONED(buf[10 + (sz - 1) - 5]); - EXPECT_POISONED(buf[11 + (sz - 1) - 5]); - close(fd); -} - -TEST(MemorySanitizer, preadv) { - char buf[2011]; - struct iovec iov[2]; - iov[0].iov_base = buf + 1; - iov[0].iov_len = 5; - iov[1].iov_base = buf + 10; - iov[1].iov_len = 2000; - int fd = open(FILE_TO_READ, O_RDONLY); - ASSERT_GT(fd, 0); - int sz = preadv(fd, iov, 2, 3); - ASSERT_GE(sz, 0); - ASSERT_LE(sz, 5 + 2000); - ASSERT_GT((size_t)sz, iov[0].iov_len); - EXPECT_POISONED(buf[0]); - EXPECT_NOT_POISONED(buf[1]); - EXPECT_NOT_POISONED(buf[5]); - EXPECT_POISONED(buf[6]); - EXPECT_POISONED(buf[9]); - EXPECT_NOT_POISONED(buf[10]); - EXPECT_NOT_POISONED(buf[10 + (sz - 1) - 5]); - EXPECT_POISONED(buf[11 + (sz - 1) - 5]); - close(fd); -} - -// FIXME: fails now. -TEST(MemorySanitizer, DISABLED_ioctl) { - struct winsize ws; - EXPECT_EQ(ioctl(2, TIOCGWINSZ, &ws), 0); - EXPECT_NOT_POISONED(ws.ws_col); -} - -TEST(MemorySanitizer, readlink) { - char *x = new char[1000]; - readlink(SYMLINK_TO_READ, x, 1000); - EXPECT_NOT_POISONED(x[0]); - delete [] x; -} - -TEST(MemorySanitizer, stat) { - struct stat* st = new struct stat; - int res = stat(FILE_TO_READ, st); - ASSERT_EQ(0, res); - EXPECT_NOT_POISONED(st->st_dev); - EXPECT_NOT_POISONED(st->st_mode); - EXPECT_NOT_POISONED(st->st_size); -} - -TEST(MemorySanitizer, fstatat) { - struct stat* st = new struct stat; - int dirfd = open(DIR_TO_READ, O_RDONLY); - ASSERT_GT(dirfd, 0); - int res = fstatat(dirfd, SUBFILE_TO_READ, st, 0); - ASSERT_EQ(0, res); - EXPECT_NOT_POISONED(st->st_dev); - EXPECT_NOT_POISONED(st->st_mode); - EXPECT_NOT_POISONED(st->st_size); - close(dirfd); -} - -TEST(MemorySanitizer, statfs) { - struct statfs st; - int res = statfs("/", &st); - ASSERT_EQ(0, res); - EXPECT_NOT_POISONED(st.f_type); - EXPECT_NOT_POISONED(st.f_bfree); - EXPECT_NOT_POISONED(st.f_namelen); -} - -TEST(MemorySanitizer, statvfs) { - struct statvfs st; - int res = statvfs("/", &st); - ASSERT_EQ(0, res); - EXPECT_NOT_POISONED(st.f_bsize); - EXPECT_NOT_POISONED(st.f_blocks); - EXPECT_NOT_POISONED(st.f_bfree); - EXPECT_NOT_POISONED(st.f_namemax); -} - -TEST(MemorySanitizer, fstatvfs) { - struct statvfs st; - int fd = open("/", O_RDONLY | O_DIRECTORY); - int res = fstatvfs(fd, &st); - ASSERT_EQ(0, res); - EXPECT_NOT_POISONED(st.f_bsize); - EXPECT_NOT_POISONED(st.f_blocks); - EXPECT_NOT_POISONED(st.f_bfree); - EXPECT_NOT_POISONED(st.f_namemax); - close(fd); -} - -TEST(MemorySanitizer, pipe) { - int* pipefd = new int[2]; - int res = pipe(pipefd); - ASSERT_EQ(0, res); - EXPECT_NOT_POISONED(pipefd[0]); - EXPECT_NOT_POISONED(pipefd[1]); - close(pipefd[0]); - close(pipefd[1]); -} - -TEST(MemorySanitizer, pipe2) { - int* pipefd = new int[2]; - int res = pipe2(pipefd, O_NONBLOCK); - ASSERT_EQ(0, res); - EXPECT_NOT_POISONED(pipefd[0]); - EXPECT_NOT_POISONED(pipefd[1]); - close(pipefd[0]); - close(pipefd[1]); -} - -TEST(MemorySanitizer, socketpair) { - int sv[2]; - int res = socketpair(AF_UNIX, SOCK_STREAM, 0, sv); - ASSERT_EQ(0, res); - EXPECT_NOT_POISONED(sv[0]); - EXPECT_NOT_POISONED(sv[1]); - close(sv[0]); - close(sv[1]); -} - -TEST(MemorySanitizer, poll) { - int* pipefd = new int[2]; - int res = pipe(pipefd); - ASSERT_EQ(0, res); - - char data = 42; - res = write(pipefd[1], &data, 1); - ASSERT_EQ(1, res); - - pollfd fds[2]; - fds[0].fd = pipefd[0]; - fds[0].events = POLLIN; - fds[1].fd = pipefd[1]; - fds[1].events = POLLIN; - res = poll(fds, 2, 500); - ASSERT_EQ(1, res); - EXPECT_NOT_POISONED(fds[0].revents); - EXPECT_NOT_POISONED(fds[1].revents); - - close(pipefd[0]); - close(pipefd[1]); -} - -// There is no ppoll() on FreeBSD. -#if !defined (__FreeBSD__) -TEST(MemorySanitizer, ppoll) { - int* pipefd = new int[2]; - int res = pipe(pipefd); - ASSERT_EQ(0, res); - - char data = 42; - res = write(pipefd[1], &data, 1); - ASSERT_EQ(1, res); - - pollfd fds[2]; - fds[0].fd = pipefd[0]; - fds[0].events = POLLIN; - fds[1].fd = pipefd[1]; - fds[1].events = POLLIN; - sigset_t ss; - sigemptyset(&ss); - res = ppoll(fds, 2, NULL, &ss); - ASSERT_EQ(1, res); - EXPECT_NOT_POISONED(fds[0].revents); - EXPECT_NOT_POISONED(fds[1].revents); - - close(pipefd[0]); - close(pipefd[1]); -} -#endif - -TEST(MemorySanitizer, poll_positive) { - int* pipefd = new int[2]; - int res = pipe(pipefd); - ASSERT_EQ(0, res); - - pollfd fds[2]; - fds[0].fd = pipefd[0]; - fds[0].events = POLLIN; - // fds[1].fd uninitialized - fds[1].events = POLLIN; - EXPECT_UMR(poll(fds, 2, 0)); - - close(pipefd[0]); - close(pipefd[1]); -} - -TEST(MemorySanitizer, bind_getsockname) { - int sock = socket(AF_UNIX, SOCK_STREAM, 0); - - struct sockaddr_in sai; - memset(&sai, 0, sizeof(sai)); - sai.sin_family = AF_UNIX; - int res = bind(sock, (struct sockaddr *)&sai, sizeof(sai)); - - ASSERT_EQ(0, res); - char buf[200]; - socklen_t addrlen; - EXPECT_UMR(getsockname(sock, (struct sockaddr *)&buf, &addrlen)); - - addrlen = sizeof(buf); - res = getsockname(sock, (struct sockaddr *)&buf, &addrlen); - EXPECT_NOT_POISONED(addrlen); - EXPECT_NOT_POISONED(buf[0]); - EXPECT_NOT_POISONED(buf[addrlen - 1]); - EXPECT_POISONED(buf[addrlen]); - close(sock); -} - -TEST(MemorySanitizer, accept) { - int listen_socket = socket(AF_INET, SOCK_STREAM, 0); - ASSERT_LT(0, listen_socket); - - struct sockaddr_in sai; - memset(&sai, 0, sizeof(sai)); - sai.sin_family = AF_INET; - sai.sin_port = 0; - sai.sin_addr.s_addr = htonl(INADDR_LOOPBACK); - int res = bind(listen_socket, (struct sockaddr *)&sai, sizeof(sai)); - ASSERT_EQ(0, res); - - res = listen(listen_socket, 1); - ASSERT_EQ(0, res); - - socklen_t sz = sizeof(sai); - res = getsockname(listen_socket, (struct sockaddr *)&sai, &sz); - ASSERT_EQ(0, res); - ASSERT_EQ(sizeof(sai), sz); - - int connect_socket = socket(AF_INET, SOCK_STREAM, 0); - ASSERT_LT(0, connect_socket); - res = fcntl(connect_socket, F_SETFL, O_NONBLOCK); - ASSERT_EQ(0, res); - res = connect(connect_socket, (struct sockaddr *)&sai, sizeof(sai)); - // On FreeBSD this connection completes immediately. - if (res != 0) { - ASSERT_EQ(-1, res); - ASSERT_EQ(EINPROGRESS, errno); - } - - __msan_poison(&sai, sizeof(sai)); - int new_sock = accept(listen_socket, (struct sockaddr *)&sai, &sz); - ASSERT_LT(0, new_sock); - ASSERT_EQ(sizeof(sai), sz); - EXPECT_NOT_POISONED(sai); - - __msan_poison(&sai, sizeof(sai)); - res = getpeername(new_sock, (struct sockaddr *)&sai, &sz); - ASSERT_EQ(0, res); - ASSERT_EQ(sizeof(sai), sz); - EXPECT_NOT_POISONED(sai); - - close(new_sock); - close(connect_socket); - close(listen_socket); -} - -TEST(MemorySanitizer, getaddrinfo) { - struct addrinfo *ai; - struct addrinfo hints; - memset(&hints, 0, sizeof(hints)); - hints.ai_family = AF_INET; - int res = getaddrinfo("localhost", NULL, &hints, &ai); - ASSERT_EQ(0, res); - EXPECT_NOT_POISONED(*ai); - ASSERT_EQ(sizeof(sockaddr_in), ai->ai_addrlen); - EXPECT_NOT_POISONED(*(sockaddr_in*)ai->ai_addr); -} - -TEST(MemorySanitizer, getnameinfo) { - struct sockaddr_in sai; - memset(&sai, 0, sizeof(sai)); - sai.sin_family = AF_INET; - sai.sin_port = 80; - sai.sin_addr.s_addr = htonl(INADDR_LOOPBACK); - char host[500]; - char serv[500]; - int res = getnameinfo((struct sockaddr *)&sai, sizeof(sai), host, - sizeof(host), serv, sizeof(serv), 0); - ASSERT_EQ(0, res); - EXPECT_NOT_POISONED(host[0]); - EXPECT_POISONED(host[sizeof(host) - 1]); - - ASSERT_NE(0U, strlen(host)); - EXPECT_NOT_POISONED(serv[0]); - EXPECT_POISONED(serv[sizeof(serv) - 1]); - ASSERT_NE(0U, strlen(serv)); -} - -#define EXPECT_HOSTENT_NOT_POISONED(he) \ - do { \ - EXPECT_NOT_POISONED(*(he)); \ - ASSERT_NE((void *) 0, (he)->h_name); \ - ASSERT_NE((void *) 0, (he)->h_aliases); \ - ASSERT_NE((void *) 0, (he)->h_addr_list); \ - EXPECT_NOT_POISONED(strlen((he)->h_name)); \ - char **p = (he)->h_aliases; \ - while (*p) { \ - EXPECT_NOT_POISONED(strlen(*p)); \ - ++p; \ - } \ - char **q = (he)->h_addr_list; \ - while (*q) { \ - EXPECT_NOT_POISONED(*q[0]); \ - ++q; \ - } \ - EXPECT_NOT_POISONED(*q); \ - } while (0) - -TEST(MemorySanitizer, gethostent) { - struct hostent *he = gethostent(); - ASSERT_NE((void *)NULL, he); - EXPECT_HOSTENT_NOT_POISONED(he); -} - -#ifndef MSAN_TEST_DISABLE_GETHOSTBYNAME - -TEST(MemorySanitizer, gethostbyname) { - struct hostent *he = gethostbyname("localhost"); - ASSERT_NE((void *)NULL, he); - EXPECT_HOSTENT_NOT_POISONED(he); -} - -#endif // MSAN_TEST_DISABLE_GETHOSTBYNAME - -TEST(MemorySanitizer, recvmsg) { - int server_socket = socket(AF_INET, SOCK_DGRAM, 0); - ASSERT_LT(0, server_socket); - - struct sockaddr_in sai; - memset(&sai, 0, sizeof(sai)); - sai.sin_family = AF_INET; - sai.sin_port = 0; - sai.sin_addr.s_addr = htonl(INADDR_LOOPBACK); - int res = bind(server_socket, (struct sockaddr *)&sai, sizeof(sai)); - ASSERT_EQ(0, res); - - socklen_t sz = sizeof(sai); - res = getsockname(server_socket, (struct sockaddr *)&sai, &sz); - ASSERT_EQ(0, res); - ASSERT_EQ(sizeof(sai), sz); - - - int client_socket = socket(AF_INET, SOCK_DGRAM, 0); - ASSERT_LT(0, client_socket); - - struct sockaddr_in client_sai; - memset(&client_sai, 0, sizeof(client_sai)); - client_sai.sin_family = AF_INET; - client_sai.sin_port = 0; - client_sai.sin_addr.s_addr = htonl(INADDR_LOOPBACK); - res = bind(client_socket, (struct sockaddr *)&client_sai, sizeof(client_sai)); - ASSERT_EQ(0, res); - - sz = sizeof(client_sai); - res = getsockname(client_socket, (struct sockaddr *)&client_sai, &sz); - ASSERT_EQ(0, res); - ASSERT_EQ(sizeof(client_sai), sz); - - const char *s = "message text"; - struct iovec iov; - iov.iov_base = (void *)s; - iov.iov_len = strlen(s) + 1; - struct msghdr msg; - memset(&msg, 0, sizeof(msg)); - msg.msg_name = &sai; - msg.msg_namelen = sizeof(sai); - msg.msg_iov = &iov; - msg.msg_iovlen = 1; - res = sendmsg(client_socket, &msg, 0); - ASSERT_LT(0, res); - - - char buf[1000]; - struct iovec recv_iov; - recv_iov.iov_base = (void *)&buf; - recv_iov.iov_len = sizeof(buf); - struct sockaddr_in recv_sai; - struct msghdr recv_msg; - memset(&recv_msg, 0, sizeof(recv_msg)); - recv_msg.msg_name = &recv_sai; - recv_msg.msg_namelen = sizeof(recv_sai); - recv_msg.msg_iov = &recv_iov; - recv_msg.msg_iovlen = 1; - res = recvmsg(server_socket, &recv_msg, 0); - ASSERT_LT(0, res); - - ASSERT_EQ(sizeof(recv_sai), recv_msg.msg_namelen); - EXPECT_NOT_POISONED(*(struct sockaddr_in *)recv_msg.msg_name); - EXPECT_STREQ(s, buf); - - close(server_socket); - close(client_socket); -} - -TEST(MemorySanitizer, gethostbyname2) { - struct hostent *he = gethostbyname2("localhost", AF_INET); - ASSERT_NE((void *)NULL, he); - EXPECT_HOSTENT_NOT_POISONED(he); -} - -TEST(MemorySanitizer, gethostbyaddr) { - in_addr_t addr = inet_addr("127.0.0.1"); - EXPECT_NOT_POISONED(addr); - struct hostent *he = gethostbyaddr(&addr, sizeof(addr), AF_INET); - ASSERT_NE((void *)NULL, he); - EXPECT_HOSTENT_NOT_POISONED(he); -} - -TEST(MemorySanitizer, gethostent_r) { - char buf[2000]; - struct hostent he; - struct hostent *result; - int err; - int res = gethostent_r(&he, buf, sizeof(buf), &result, &err); - ASSERT_EQ(0, res); - EXPECT_NOT_POISONED(result); - ASSERT_NE((void *)NULL, result); - EXPECT_HOSTENT_NOT_POISONED(result); - EXPECT_NOT_POISONED(err); -} - -TEST(MemorySanitizer, gethostbyname_r) { - char buf[2000]; - struct hostent he; - struct hostent *result; - int err; - int res = gethostbyname_r("localhost", &he, buf, sizeof(buf), &result, &err); - ASSERT_EQ(0, res); - EXPECT_NOT_POISONED(result); - ASSERT_NE((void *)NULL, result); - EXPECT_HOSTENT_NOT_POISONED(result); - EXPECT_NOT_POISONED(err); -} - -TEST(MemorySanitizer, gethostbyname_r_bad_host_name) { - char buf[2000]; - struct hostent he; - struct hostent *result; - int err; - int res = gethostbyname_r("bad-host-name", &he, buf, sizeof(buf), &result, &err); - ASSERT_EQ((struct hostent *)0, result); - EXPECT_NOT_POISONED(err); -} - -TEST(MemorySanitizer, gethostbyname_r_erange) { - char buf[5]; - struct hostent he; - struct hostent *result; - int err; - int res = gethostbyname_r("localhost", &he, buf, sizeof(buf), &result, &err); - ASSERT_EQ(ERANGE, res); - EXPECT_NOT_POISONED(err); -} - -TEST(MemorySanitizer, gethostbyname2_r) { - char buf[2000]; - struct hostent he; - struct hostent *result; - int err; - int res = gethostbyname2_r("localhost", AF_INET, &he, buf, sizeof(buf), - &result, &err); - ASSERT_EQ(0, res); - EXPECT_NOT_POISONED(result); - ASSERT_NE((void *)NULL, result); - EXPECT_HOSTENT_NOT_POISONED(result); - EXPECT_NOT_POISONED(err); -} - -TEST(MemorySanitizer, gethostbyaddr_r) { - char buf[2000]; - struct hostent he; - struct hostent *result; - int err; - in_addr_t addr = inet_addr("127.0.0.1"); - EXPECT_NOT_POISONED(addr); - int res = gethostbyaddr_r(&addr, sizeof(addr), AF_INET, &he, buf, sizeof(buf), - &result, &err); - ASSERT_EQ(0, res); - EXPECT_NOT_POISONED(result); - ASSERT_NE((void *)NULL, result); - EXPECT_HOSTENT_NOT_POISONED(result); - EXPECT_NOT_POISONED(err); -} - -TEST(MemorySanitizer, getsockopt) { - int sock = socket(AF_UNIX, SOCK_STREAM, 0); - struct linger l[2]; - socklen_t sz = sizeof(l[0]); - int res = getsockopt(sock, SOL_SOCKET, SO_LINGER, &l[0], &sz); - ASSERT_EQ(0, res); - ASSERT_EQ(sizeof(l[0]), sz); - EXPECT_NOT_POISONED(l[0]); - EXPECT_POISONED(*(char *)(l + 1)); -} - -TEST(MemorySanitizer, getcwd) { - char path[PATH_MAX + 1]; - char* res = getcwd(path, sizeof(path)); - ASSERT_TRUE(res != NULL); - EXPECT_NOT_POISONED(path[0]); -} - -TEST(MemorySanitizer, getcwd_gnu) { - char* res = getcwd(NULL, 0); - ASSERT_TRUE(res != NULL); - EXPECT_NOT_POISONED(res[0]); - free(res); -} - -// There's no get_current_dir_name() on FreeBSD. -#if !defined(__FreeBSD__) -TEST(MemorySanitizer, get_current_dir_name) { - char* res = get_current_dir_name(); - ASSERT_TRUE(res != NULL); - EXPECT_NOT_POISONED(res[0]); - free(res); -} -#endif - -TEST(MemorySanitizer, shmctl) { - int id = shmget(IPC_PRIVATE, 4096, 0644 | IPC_CREAT); - ASSERT_GT(id, -1); - - struct shmid_ds ds; - int res = shmctl(id, IPC_STAT, &ds); - ASSERT_GT(res, -1); - EXPECT_NOT_POISONED(ds); - - // FreeBSD does not support shmctl(IPC_INFO) and shmctl(SHM_INFO). -#if !defined(__FreeBSD__) - struct shminfo si; - res = shmctl(id, IPC_INFO, (struct shmid_ds *)&si); - ASSERT_GT(res, -1); - EXPECT_NOT_POISONED(si); - - struct shm_info s_i; - res = shmctl(id, SHM_INFO, (struct shmid_ds *)&s_i); - ASSERT_GT(res, -1); - EXPECT_NOT_POISONED(s_i); -#endif - - res = shmctl(id, IPC_RMID, 0); - ASSERT_GT(res, -1); -} - -TEST(MemorySanitizer, shmat) { - void *p = mmap(NULL, 4096, PROT_READ | PROT_WRITE, - MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); - ASSERT_NE(MAP_FAILED, p); - - ((char *)p)[10] = *GetPoisoned<U1>(); - ((char *)p)[4095] = *GetPoisoned<U1>(); - - int res = munmap(p, 4096); - ASSERT_EQ(0, res); - - int id = shmget(IPC_PRIVATE, 4096, 0644 | IPC_CREAT); - ASSERT_GT(id, -1); - - void *q = shmat(id, p, 0); - ASSERT_EQ(p, q); - - EXPECT_NOT_POISONED(((char *)q)[0]); - EXPECT_NOT_POISONED(((char *)q)[10]); - EXPECT_NOT_POISONED(((char *)q)[4095]); - - res = shmdt(q); - ASSERT_EQ(0, res); - - res = shmctl(id, IPC_RMID, 0); - ASSERT_GT(res, -1); -} - -// There's no random_r() on FreeBSD. -#if !defined(__FreeBSD__) -TEST(MemorySanitizer, random_r) { - int32_t x; - char z[64]; - memset(z, 0, sizeof(z)); - - struct random_data buf; - memset(&buf, 0, sizeof(buf)); - - int res = initstate_r(0, z, sizeof(z), &buf); - ASSERT_EQ(0, res); - - res = random_r(&buf, &x); - ASSERT_EQ(0, res); - EXPECT_NOT_POISONED(x); -} -#endif - -TEST(MemorySanitizer, confstr) { - char buf[3]; - size_t res = confstr(_CS_PATH, buf, sizeof(buf)); - ASSERT_GT(res, sizeof(buf)); - EXPECT_NOT_POISONED(buf[0]); - EXPECT_NOT_POISONED(buf[sizeof(buf) - 1]); - - char buf2[1000]; - res = confstr(_CS_PATH, buf2, sizeof(buf2)); - ASSERT_LT(res, sizeof(buf2)); - EXPECT_NOT_POISONED(buf2[0]); - EXPECT_NOT_POISONED(buf2[res - 1]); - EXPECT_POISONED(buf2[res]); - ASSERT_EQ(res, strlen(buf2) + 1); -} - -TEST(MemorySanitizer, opendir) { - DIR *dir = opendir("."); - closedir(dir); - - char name[10] = "."; - __msan_poison(name, sizeof(name)); - EXPECT_UMR(dir = opendir(name)); - closedir(dir); -} - -TEST(MemorySanitizer, readdir) { - DIR *dir = opendir("."); - struct dirent *d = readdir(dir); - ASSERT_TRUE(d != NULL); - EXPECT_NOT_POISONED(d->d_name[0]); - closedir(dir); -} - -TEST(MemorySanitizer, readdir_r) { - DIR *dir = opendir("."); - struct dirent d; - struct dirent *pd; - int res = readdir_r(dir, &d, &pd); - ASSERT_EQ(0, res); - EXPECT_NOT_POISONED(pd); - EXPECT_NOT_POISONED(d.d_name[0]); - closedir(dir); -} - -TEST(MemorySanitizer, realpath) { - const char* relpath = "."; - char path[PATH_MAX + 1]; - char* res = realpath(relpath, path); - ASSERT_TRUE(res != NULL); - EXPECT_NOT_POISONED(path[0]); -} - -TEST(MemorySanitizer, realpath_null) { - const char* relpath = "."; - char* res = realpath(relpath, NULL); - printf("%d, %s\n", errno, strerror(errno)); - ASSERT_TRUE(res != NULL); - EXPECT_NOT_POISONED(res[0]); - free(res); -} - -// There's no canonicalize_file_name() on FreeBSD. -#if !defined(__FreeBSD__) -TEST(MemorySanitizer, canonicalize_file_name) { - const char* relpath = "."; - char* res = canonicalize_file_name(relpath); - ASSERT_TRUE(res != NULL); - EXPECT_NOT_POISONED(res[0]); - free(res); -} -#endif - -extern char **environ; - -TEST(MemorySanitizer, setenv) { - setenv("AAA", "BBB", 1); - for (char **envp = environ; *envp; ++envp) { - EXPECT_NOT_POISONED(*envp); - EXPECT_NOT_POISONED(*envp[0]); - } -} - -TEST(MemorySanitizer, putenv) { - char s[] = "AAA=BBB"; - putenv(s); - for (char **envp = environ; *envp; ++envp) { - EXPECT_NOT_POISONED(*envp); - EXPECT_NOT_POISONED(*envp[0]); - } -} - -TEST(MemorySanitizer, memcpy) { - char* x = new char[2]; - char* y = new char[2]; - x[0] = 1; - x[1] = *GetPoisoned<char>(); - memcpy(y, x, 2); - EXPECT_NOT_POISONED(y[0]); - EXPECT_POISONED(y[1]); -} - -void TestUnalignedMemcpy(unsigned left, unsigned right, bool src_is_aligned, - bool src_is_poisoned, bool dst_is_poisoned) { - fprintf(stderr, "%s(%d, %d, %d, %d, %d)\n", __func__, left, right, - src_is_aligned, src_is_poisoned, dst_is_poisoned); - - const unsigned sz = 20; - U4 dst_origin, src_origin; - char *dst = (char *)malloc(sz); - if (dst_is_poisoned) - dst_origin = __msan_get_origin(dst); - else - memset(dst, 0, sz); - - char *src = (char *)malloc(sz); - if (src_is_poisoned) - src_origin = __msan_get_origin(src); - else - memset(src, 0, sz); - - memcpy(dst + left, src_is_aligned ? src + left : src, sz - left - right); - - for (unsigned i = 0; i < (left & (~3U)); ++i) - if (dst_is_poisoned) - EXPECT_POISONED_O(dst[i], dst_origin); - else - EXPECT_NOT_POISONED(dst[i]); - - for (unsigned i = 0; i < (right & (~3U)); ++i) - if (dst_is_poisoned) - EXPECT_POISONED_O(dst[sz - i - 1], dst_origin); - else - EXPECT_NOT_POISONED(dst[sz - i - 1]); - - for (unsigned i = left; i < sz - right; ++i) - if (src_is_poisoned) - EXPECT_POISONED_O(dst[i], src_origin); - else - EXPECT_NOT_POISONED(dst[i]); - - free(dst); - free(src); -} - -TEST(MemorySanitizer, memcpy_unaligned) { - for (int i = 0; i < 10; ++i) - for (int j = 0; j < 10; ++j) - for (int aligned = 0; aligned < 2; ++aligned) - for (int srcp = 0; srcp < 2; ++srcp) - for (int dstp = 0; dstp < 2; ++dstp) - TestUnalignedMemcpy(i, j, aligned, srcp, dstp); -} - -TEST(MemorySanitizer, memmove) { - char* x = new char[2]; - char* y = new char[2]; - x[0] = 1; - x[1] = *GetPoisoned<char>(); - memmove(y, x, 2); - EXPECT_NOT_POISONED(y[0]); - EXPECT_POISONED(y[1]); -} - -TEST(MemorySanitizer, memccpy_nomatch) { - char* x = new char[5]; - char* y = new char[5]; - strcpy(x, "abc"); - memccpy(y, x, 'd', 4); - EXPECT_NOT_POISONED(y[0]); - EXPECT_NOT_POISONED(y[1]); - EXPECT_NOT_POISONED(y[2]); - EXPECT_NOT_POISONED(y[3]); - EXPECT_POISONED(y[4]); - delete[] x; - delete[] y; -} - -TEST(MemorySanitizer, memccpy_match) { - char* x = new char[5]; - char* y = new char[5]; - strcpy(x, "abc"); - memccpy(y, x, 'b', 4); - EXPECT_NOT_POISONED(y[0]); - EXPECT_NOT_POISONED(y[1]); - EXPECT_POISONED(y[2]); - EXPECT_POISONED(y[3]); - EXPECT_POISONED(y[4]); - delete[] x; - delete[] y; -} - -TEST(MemorySanitizer, memccpy_nomatch_positive) { - char* x = new char[5]; - char* y = new char[5]; - strcpy(x, "abc"); - EXPECT_UMR(memccpy(y, x, 'd', 5)); - delete[] x; - delete[] y; -} - -TEST(MemorySanitizer, memccpy_match_positive) { - char* x = new char[5]; - char* y = new char[5]; - x[0] = 'a'; - x[2] = 'b'; - EXPECT_UMR(memccpy(y, x, 'b', 5)); - delete[] x; - delete[] y; -} - -TEST(MemorySanitizer, bcopy) { - char* x = new char[2]; - char* y = new char[2]; - x[0] = 1; - x[1] = *GetPoisoned<char>(); - bcopy(x, y, 2); - EXPECT_NOT_POISONED(y[0]); - EXPECT_POISONED(y[1]); -} - -TEST(MemorySanitizer, strdup) { - char buf[4] = "abc"; - __msan_poison(buf + 2, sizeof(*buf)); - char *x = strdup(buf); - EXPECT_NOT_POISONED(x[0]); - EXPECT_NOT_POISONED(x[1]); - EXPECT_POISONED(x[2]); - EXPECT_NOT_POISONED(x[3]); - free(x); -} - -TEST(MemorySanitizer, strndup) { - char buf[4] = "abc"; - __msan_poison(buf + 2, sizeof(*buf)); - char *x = strndup(buf, 3); - EXPECT_NOT_POISONED(x[0]); - EXPECT_NOT_POISONED(x[1]); - EXPECT_POISONED(x[2]); - EXPECT_NOT_POISONED(x[3]); - free(x); -} - -TEST(MemorySanitizer, strndup_short) { - char buf[4] = "abc"; - __msan_poison(buf + 1, sizeof(*buf)); - __msan_poison(buf + 2, sizeof(*buf)); - char *x = strndup(buf, 2); - EXPECT_NOT_POISONED(x[0]); - EXPECT_POISONED(x[1]); - EXPECT_NOT_POISONED(x[2]); - free(x); -} - - -template<class T, int size> -void TestOverlapMemmove() { - T *x = new T[size]; - ASSERT_GE(size, 3); - x[2] = 0; - memmove(x, x + 1, (size - 1) * sizeof(T)); - EXPECT_NOT_POISONED(x[1]); - EXPECT_POISONED(x[0]); - EXPECT_POISONED(x[2]); - delete [] x; -} - -TEST(MemorySanitizer, overlap_memmove) { - TestOverlapMemmove<U1, 10>(); - TestOverlapMemmove<U1, 1000>(); - TestOverlapMemmove<U8, 4>(); - TestOverlapMemmove<U8, 1000>(); -} - -TEST(MemorySanitizer, strcpy) { // NOLINT - char* x = new char[3]; - char* y = new char[3]; - x[0] = 'a'; - x[1] = *GetPoisoned<char>(1, 1); - x[2] = 0; - strcpy(y, x); // NOLINT - EXPECT_NOT_POISONED(y[0]); - EXPECT_POISONED(y[1]); - EXPECT_NOT_POISONED(y[2]); -} - -TEST(MemorySanitizer, strncpy) { // NOLINT - char* x = new char[3]; - char* y = new char[5]; - x[0] = 'a'; - x[1] = *GetPoisoned<char>(1, 1); - x[2] = '\0'; - strncpy(y, x, 4); // NOLINT - EXPECT_NOT_POISONED(y[0]); - EXPECT_POISONED(y[1]); - EXPECT_NOT_POISONED(y[2]); - EXPECT_NOT_POISONED(y[3]); - EXPECT_POISONED(y[4]); -} - -TEST(MemorySanitizer, stpcpy) { // NOLINT - char* x = new char[3]; - char* y = new char[3]; - x[0] = 'a'; - x[1] = *GetPoisoned<char>(1, 1); - x[2] = 0; - char *res = stpcpy(y, x); // NOLINT - ASSERT_EQ(res, y + 2); - EXPECT_NOT_POISONED(y[0]); - EXPECT_POISONED(y[1]); - EXPECT_NOT_POISONED(y[2]); -} - -TEST(MemorySanitizer, strcat) { // NOLINT - char a[10]; - char b[] = "def"; - strcpy(a, "abc"); - __msan_poison(b + 1, 1); - strcat(a, b); - EXPECT_NOT_POISONED(a[3]); - EXPECT_POISONED(a[4]); - EXPECT_NOT_POISONED(a[5]); - EXPECT_NOT_POISONED(a[6]); - EXPECT_POISONED(a[7]); -} - -TEST(MemorySanitizer, strncat) { // NOLINT - char a[10]; - char b[] = "def"; - strcpy(a, "abc"); - __msan_poison(b + 1, 1); - strncat(a, b, 5); - EXPECT_NOT_POISONED(a[3]); - EXPECT_POISONED(a[4]); - EXPECT_NOT_POISONED(a[5]); - EXPECT_NOT_POISONED(a[6]); - EXPECT_POISONED(a[7]); -} - -TEST(MemorySanitizer, strncat_overflow) { // NOLINT - char a[10]; - char b[] = "def"; - strcpy(a, "abc"); - __msan_poison(b + 1, 1); - strncat(a, b, 2); - EXPECT_NOT_POISONED(a[3]); - EXPECT_POISONED(a[4]); - EXPECT_NOT_POISONED(a[5]); - EXPECT_POISONED(a[6]); - EXPECT_POISONED(a[7]); -} - -#define TEST_STRTO_INT(func_name, char_type, str_prefix) \ - TEST(MemorySanitizer, func_name) { \ - char_type *e; \ - EXPECT_EQ(1U, func_name(str_prefix##"1", &e, 10)); \ - EXPECT_NOT_POISONED((S8)e); \ - } - -#define TEST_STRTO_FLOAT(func_name, char_type, str_prefix) \ - TEST(MemorySanitizer, func_name) { \ - char_type *e; \ - EXPECT_NE(0, func_name(str_prefix##"1.5", &e)); \ - EXPECT_NOT_POISONED((S8)e); \ - } - -#define TEST_STRTO_FLOAT_LOC(func_name, char_type, str_prefix) \ - TEST(MemorySanitizer, func_name) { \ - locale_t loc = newlocale(LC_NUMERIC_MASK, "C", (locale_t)0); \ - char_type *e; \ - EXPECT_NE(0, func_name(str_prefix##"1.5", &e, loc)); \ - EXPECT_NOT_POISONED((S8)e); \ - freelocale(loc); \ - } - -#define TEST_STRTO_INT_LOC(func_name, char_type, str_prefix) \ - TEST(MemorySanitizer, func_name) { \ - locale_t loc = newlocale(LC_NUMERIC_MASK, "C", (locale_t)0); \ - char_type *e; \ - ASSERT_EQ(1U, func_name(str_prefix##"1", &e, 10, loc)); \ - EXPECT_NOT_POISONED((S8)e); \ - freelocale(loc); \ - } - -TEST_STRTO_INT(strtol, char, ) -TEST_STRTO_INT(strtoll, char, ) -TEST_STRTO_INT(strtoul, char, ) -TEST_STRTO_INT(strtoull, char, ) - -TEST_STRTO_FLOAT(strtof, char, ) -TEST_STRTO_FLOAT(strtod, char, ) -TEST_STRTO_FLOAT(strtold, char, ) - -TEST_STRTO_FLOAT_LOC(strtof_l, char, ) -TEST_STRTO_FLOAT_LOC(strtod_l, char, ) -TEST_STRTO_FLOAT_LOC(strtold_l, char, ) - -TEST_STRTO_INT_LOC(strtol_l, char, ) -TEST_STRTO_INT_LOC(strtoll_l, char, ) -TEST_STRTO_INT_LOC(strtoul_l, char, ) -TEST_STRTO_INT_LOC(strtoull_l, char, ) - -TEST_STRTO_INT(wcstol, wchar_t, L) -TEST_STRTO_INT(wcstoll, wchar_t, L) -TEST_STRTO_INT(wcstoul, wchar_t, L) -TEST_STRTO_INT(wcstoull, wchar_t, L) - -TEST_STRTO_FLOAT(wcstof, wchar_t, L) -TEST_STRTO_FLOAT(wcstod, wchar_t, L) -TEST_STRTO_FLOAT(wcstold, wchar_t, L) - -TEST_STRTO_FLOAT_LOC(wcstof_l, wchar_t, L) -TEST_STRTO_FLOAT_LOC(wcstod_l, wchar_t, L) -TEST_STRTO_FLOAT_LOC(wcstold_l, wchar_t, L) - -TEST_STRTO_INT_LOC(wcstol_l, wchar_t, L) -TEST_STRTO_INT_LOC(wcstoll_l, wchar_t, L) -TEST_STRTO_INT_LOC(wcstoul_l, wchar_t, L) -TEST_STRTO_INT_LOC(wcstoull_l, wchar_t, L) - - -TEST(MemorySanitizer, strtoimax) { - char *e; - ASSERT_EQ(1, strtoimax("1", &e, 10)); - EXPECT_NOT_POISONED((S8) e); -} - -TEST(MemorySanitizer, strtoumax) { - char *e; - ASSERT_EQ(1U, strtoumax("1", &e, 10)); - EXPECT_NOT_POISONED((S8) e); -} - -#ifdef __GLIBC__ -extern "C" float __strtof_l(const char *nptr, char **endptr, locale_t loc); -TEST_STRTO_FLOAT_LOC(__strtof_l, char, ) -extern "C" double __strtod_l(const char *nptr, char **endptr, locale_t loc); -TEST_STRTO_FLOAT_LOC(__strtod_l, char, ) -extern "C" long double __strtold_l(const char *nptr, char **endptr, - locale_t loc); -TEST_STRTO_FLOAT_LOC(__strtold_l, char, ) - -extern "C" float __wcstof_l(const wchar_t *nptr, wchar_t **endptr, locale_t loc); -TEST_STRTO_FLOAT_LOC(__wcstof_l, wchar_t, L) -extern "C" double __wcstod_l(const wchar_t *nptr, wchar_t **endptr, locale_t loc); -TEST_STRTO_FLOAT_LOC(__wcstod_l, wchar_t, L) -extern "C" long double __wcstold_l(const wchar_t *nptr, wchar_t **endptr, - locale_t loc); -TEST_STRTO_FLOAT_LOC(__wcstold_l, wchar_t, L) -#endif // __GLIBC__ - -TEST(MemorySanitizer, modf) { - double x, y; - x = modf(2.1, &y); - EXPECT_NOT_POISONED(y); -} - -TEST(MemorySanitizer, modff) { - float x, y; - x = modff(2.1, &y); - EXPECT_NOT_POISONED(y); -} - -TEST(MemorySanitizer, modfl) { - long double x, y; - x = modfl(2.1, &y); - EXPECT_NOT_POISONED(y); -} - -// There's no sincos() on FreeBSD. -#if !defined(__FreeBSD__) -TEST(MemorySanitizer, sincos) { - double s, c; - sincos(0.2, &s, &c); - EXPECT_NOT_POISONED(s); - EXPECT_NOT_POISONED(c); -} -#endif - -// There's no sincosf() on FreeBSD. -#if !defined(__FreeBSD__) -TEST(MemorySanitizer, sincosf) { - float s, c; - sincosf(0.2, &s, &c); - EXPECT_NOT_POISONED(s); - EXPECT_NOT_POISONED(c); -} -#endif - -// There's no sincosl() on FreeBSD. -#if !defined(__FreeBSD__) -TEST(MemorySanitizer, sincosl) { - long double s, c; - sincosl(0.2, &s, &c); - EXPECT_NOT_POISONED(s); - EXPECT_NOT_POISONED(c); -} -#endif - -TEST(MemorySanitizer, remquo) { - int quo; - double res = remquo(29.0, 3.0, &quo); - ASSERT_NE(0.0, res); - EXPECT_NOT_POISONED(quo); -} - -TEST(MemorySanitizer, remquof) { - int quo; - float res = remquof(29.0, 3.0, &quo); - ASSERT_NE(0.0, res); - EXPECT_NOT_POISONED(quo); -} - -TEST(MemorySanitizer, remquol) { - int quo; - long double res = remquof(29.0, 3.0, &quo); - ASSERT_NE(0.0, res); - EXPECT_NOT_POISONED(quo); -} - -TEST(MemorySanitizer, lgamma) { - double res = lgamma(1.1); - ASSERT_NE(0.0, res); - EXPECT_NOT_POISONED(signgam); -} - -TEST(MemorySanitizer, lgammaf) { - float res = lgammaf(1.1); - ASSERT_NE(0.0, res); - EXPECT_NOT_POISONED(signgam); -} - -TEST(MemorySanitizer, lgammal) { - long double res = lgammal(1.1); - ASSERT_NE(0.0, res); - EXPECT_NOT_POISONED(signgam); -} - -TEST(MemorySanitizer, lgamma_r) { - int sgn; - double res = lgamma_r(1.1, &sgn); - ASSERT_NE(0.0, res); - EXPECT_NOT_POISONED(sgn); -} - -TEST(MemorySanitizer, lgammaf_r) { - int sgn; - float res = lgammaf_r(1.1, &sgn); - ASSERT_NE(0.0, res); - EXPECT_NOT_POISONED(sgn); -} - -// There's no lgammal_r() on FreeBSD. -#if !defined(__FreeBSD__) -TEST(MemorySanitizer, lgammal_r) { - int sgn; - long double res = lgammal_r(1.1, &sgn); - ASSERT_NE(0.0, res); - EXPECT_NOT_POISONED(sgn); -} -#endif - -// There's no drand48_r() on FreeBSD. -#if !defined(__FreeBSD__) -TEST(MemorySanitizer, drand48_r) { - struct drand48_data buf; - srand48_r(0, &buf); - double d; - drand48_r(&buf, &d); - EXPECT_NOT_POISONED(d); -} -#endif - -// There's no lrand48_r() on FreeBSD. -#if !defined(__FreeBSD__) -TEST(MemorySanitizer, lrand48_r) { - struct drand48_data buf; - srand48_r(0, &buf); - long d; - lrand48_r(&buf, &d); - EXPECT_NOT_POISONED(d); -} -#endif - -TEST(MemorySanitizer, sprintf) { // NOLINT - char buff[10]; - break_optimization(buff); - EXPECT_POISONED(buff[0]); - int res = sprintf(buff, "%d", 1234567); // NOLINT - ASSERT_EQ(res, 7); - ASSERT_EQ(buff[0], '1'); - ASSERT_EQ(buff[1], '2'); - ASSERT_EQ(buff[2], '3'); - ASSERT_EQ(buff[6], '7'); - ASSERT_EQ(buff[7], 0); - EXPECT_POISONED(buff[8]); -} - -TEST(MemorySanitizer, snprintf) { - char buff[10]; - break_optimization(buff); - EXPECT_POISONED(buff[0]); - int res = snprintf(buff, sizeof(buff), "%d", 1234567); - ASSERT_EQ(res, 7); - ASSERT_EQ(buff[0], '1'); - ASSERT_EQ(buff[1], '2'); - ASSERT_EQ(buff[2], '3'); - ASSERT_EQ(buff[6], '7'); - ASSERT_EQ(buff[7], 0); - EXPECT_POISONED(buff[8]); -} - -TEST(MemorySanitizer, swprintf) { - wchar_t buff[10]; - ASSERT_EQ(4U, sizeof(wchar_t)); - break_optimization(buff); - EXPECT_POISONED(buff[0]); - int res = swprintf(buff, 9, L"%d", 1234567); - ASSERT_EQ(res, 7); - ASSERT_EQ(buff[0], '1'); - ASSERT_EQ(buff[1], '2'); - ASSERT_EQ(buff[2], '3'); - ASSERT_EQ(buff[6], '7'); - ASSERT_EQ(buff[7], 0); - EXPECT_POISONED(buff[8]); -} - -TEST(MemorySanitizer, asprintf) { // NOLINT - char *pbuf; - EXPECT_POISONED(pbuf); - int res = asprintf(&pbuf, "%d", 1234567); // NOLINT - ASSERT_EQ(res, 7); - EXPECT_NOT_POISONED(pbuf); - ASSERT_EQ(pbuf[0], '1'); - ASSERT_EQ(pbuf[1], '2'); - ASSERT_EQ(pbuf[2], '3'); - ASSERT_EQ(pbuf[6], '7'); - ASSERT_EQ(pbuf[7], 0); - free(pbuf); -} - -TEST(MemorySanitizer, mbstowcs) { - const char *x = "abc"; - wchar_t buff[10]; - int res = mbstowcs(buff, x, 2); - EXPECT_EQ(2, res); - EXPECT_EQ(L'a', buff[0]); - EXPECT_EQ(L'b', buff[1]); - EXPECT_POISONED(buff[2]); - res = mbstowcs(buff, x, 10); - EXPECT_EQ(3, res); - EXPECT_NOT_POISONED(buff[3]); -} - -TEST(MemorySanitizer, wcstombs) { - const wchar_t *x = L"abc"; - char buff[10]; - int res = wcstombs(buff, x, 4); - EXPECT_EQ(res, 3); - EXPECT_EQ(buff[0], 'a'); - EXPECT_EQ(buff[1], 'b'); - EXPECT_EQ(buff[2], 'c'); -} - -TEST(MemorySanitizer, wcsrtombs) { - const wchar_t *x = L"abc"; - const wchar_t *p = x; - char buff[10]; - mbstate_t mbs; - memset(&mbs, 0, sizeof(mbs)); - int res = wcsrtombs(buff, &p, 4, &mbs); - EXPECT_EQ(res, 3); - EXPECT_EQ(buff[0], 'a'); - EXPECT_EQ(buff[1], 'b'); - EXPECT_EQ(buff[2], 'c'); - EXPECT_EQ(buff[3], '\0'); - EXPECT_POISONED(buff[4]); -} - -TEST(MemorySanitizer, wcsnrtombs) { - const wchar_t *x = L"abc"; - const wchar_t *p = x; - char buff[10]; - mbstate_t mbs; - memset(&mbs, 0, sizeof(mbs)); - int res = wcsnrtombs(buff, &p, 2, 4, &mbs); - EXPECT_EQ(res, 2); - EXPECT_EQ(buff[0], 'a'); - EXPECT_EQ(buff[1], 'b'); - EXPECT_POISONED(buff[2]); -} - -TEST(MemorySanitizer, wmemset) { - wchar_t x[25]; - break_optimization(x); - EXPECT_POISONED(x[0]); - wmemset(x, L'A', 10); - EXPECT_EQ(x[0], L'A'); - EXPECT_EQ(x[9], L'A'); - EXPECT_POISONED(x[10]); -} - -TEST(MemorySanitizer, mbtowc) { - const char *x = "abc"; - wchar_t wx; - int res = mbtowc(&wx, x, 3); - EXPECT_GT(res, 0); - EXPECT_NOT_POISONED(wx); -} - -TEST(MemorySanitizer, mbrtowc) { - const char *x = "abc"; - wchar_t wx; - mbstate_t mbs; - memset(&mbs, 0, sizeof(mbs)); - int res = mbrtowc(&wx, x, 3, &mbs); - EXPECT_GT(res, 0); - EXPECT_NOT_POISONED(wx); -} - -TEST(MemorySanitizer, wcsftime) { - wchar_t x[100]; - time_t t = time(NULL); - struct tm tms; - struct tm *tmres = localtime_r(&t, &tms); - ASSERT_NE((void *)0, tmres); - size_t res = wcsftime(x, sizeof(x) / sizeof(x[0]), L"%Y-%m-%d", tmres); - EXPECT_GT(res, 0UL); - EXPECT_EQ(res, wcslen(x)); -} - -TEST(MemorySanitizer, gettimeofday) { - struct timeval tv; - struct timezone tz; - break_optimization(&tv); - break_optimization(&tz); - ASSERT_EQ(16U, sizeof(tv)); - ASSERT_EQ(8U, sizeof(tz)); - EXPECT_POISONED(tv.tv_sec); - EXPECT_POISONED(tv.tv_usec); - EXPECT_POISONED(tz.tz_minuteswest); - EXPECT_POISONED(tz.tz_dsttime); - ASSERT_EQ(0, gettimeofday(&tv, &tz)); - EXPECT_NOT_POISONED(tv.tv_sec); - EXPECT_NOT_POISONED(tv.tv_usec); - EXPECT_NOT_POISONED(tz.tz_minuteswest); - EXPECT_NOT_POISONED(tz.tz_dsttime); -} - -TEST(MemorySanitizer, clock_gettime) { - struct timespec tp; - EXPECT_POISONED(tp.tv_sec); - EXPECT_POISONED(tp.tv_nsec); - ASSERT_EQ(0, clock_gettime(CLOCK_REALTIME, &tp)); - EXPECT_NOT_POISONED(tp.tv_sec); - EXPECT_NOT_POISONED(tp.tv_nsec); -} - -TEST(MemorySanitizer, clock_getres) { - struct timespec tp; - EXPECT_POISONED(tp.tv_sec); - EXPECT_POISONED(tp.tv_nsec); - ASSERT_EQ(0, clock_getres(CLOCK_REALTIME, 0)); - EXPECT_POISONED(tp.tv_sec); - EXPECT_POISONED(tp.tv_nsec); - ASSERT_EQ(0, clock_getres(CLOCK_REALTIME, &tp)); - EXPECT_NOT_POISONED(tp.tv_sec); - EXPECT_NOT_POISONED(tp.tv_nsec); -} - -TEST(MemorySanitizer, getitimer) { - struct itimerval it1, it2; - int res; - EXPECT_POISONED(it1.it_interval.tv_sec); - EXPECT_POISONED(it1.it_interval.tv_usec); - EXPECT_POISONED(it1.it_value.tv_sec); - EXPECT_POISONED(it1.it_value.tv_usec); - res = getitimer(ITIMER_VIRTUAL, &it1); - ASSERT_EQ(0, res); - EXPECT_NOT_POISONED(it1.it_interval.tv_sec); - EXPECT_NOT_POISONED(it1.it_interval.tv_usec); - EXPECT_NOT_POISONED(it1.it_value.tv_sec); - EXPECT_NOT_POISONED(it1.it_value.tv_usec); - - it1.it_interval.tv_sec = it1.it_value.tv_sec = 10000; - it1.it_interval.tv_usec = it1.it_value.tv_usec = 0; - - res = setitimer(ITIMER_VIRTUAL, &it1, &it2); - ASSERT_EQ(0, res); - EXPECT_NOT_POISONED(it2.it_interval.tv_sec); - EXPECT_NOT_POISONED(it2.it_interval.tv_usec); - EXPECT_NOT_POISONED(it2.it_value.tv_sec); - EXPECT_NOT_POISONED(it2.it_value.tv_usec); - - // Check that old_value can be 0, and disable the timer. - memset(&it1, 0, sizeof(it1)); - res = setitimer(ITIMER_VIRTUAL, &it1, 0); - ASSERT_EQ(0, res); -} - -TEST(MemorySanitizer, setitimer_null) { - setitimer(ITIMER_VIRTUAL, 0, 0); - // Not testing the return value, since it the behaviour seems to differ - // between libc implementations and POSIX. - // Should never crash, though. -} - -TEST(MemorySanitizer, time) { - time_t t; - EXPECT_POISONED(t); - time_t t2 = time(&t); - ASSERT_NE(t2, (time_t)-1); - EXPECT_NOT_POISONED(t); -} - -TEST(MemorySanitizer, strptime) { - struct tm time; - char *p = strptime("11/1/2013-05:39", "%m/%d/%Y-%H:%M", &time); - ASSERT_TRUE(p != NULL); - EXPECT_NOT_POISONED(time.tm_sec); - EXPECT_NOT_POISONED(time.tm_hour); - EXPECT_NOT_POISONED(time.tm_year); -} - -TEST(MemorySanitizer, localtime) { - time_t t = 123; - struct tm *time = localtime(&t); - ASSERT_TRUE(time != NULL); - EXPECT_NOT_POISONED(time->tm_sec); - EXPECT_NOT_POISONED(time->tm_hour); - EXPECT_NOT_POISONED(time->tm_year); - EXPECT_NOT_POISONED(time->tm_isdst); - EXPECT_NE(0U, strlen(time->tm_zone)); -} - -TEST(MemorySanitizer, localtime_r) { - time_t t = 123; - struct tm time; - struct tm *res = localtime_r(&t, &time); - ASSERT_TRUE(res != NULL); - EXPECT_NOT_POISONED(time.tm_sec); - EXPECT_NOT_POISONED(time.tm_hour); - EXPECT_NOT_POISONED(time.tm_year); - EXPECT_NOT_POISONED(time.tm_isdst); - EXPECT_NE(0U, strlen(time.tm_zone)); -} - -// There's no getmntent() on FreeBSD. -#if !defined(__FreeBSD__) -TEST(MemorySanitizer, getmntent) { - FILE *fp = setmntent("/etc/fstab", "r"); - struct mntent *mnt = getmntent(fp); - ASSERT_TRUE(mnt != NULL); - ASSERT_NE(0U, strlen(mnt->mnt_fsname)); - ASSERT_NE(0U, strlen(mnt->mnt_dir)); - ASSERT_NE(0U, strlen(mnt->mnt_type)); - ASSERT_NE(0U, strlen(mnt->mnt_opts)); - EXPECT_NOT_POISONED(mnt->mnt_freq); - EXPECT_NOT_POISONED(mnt->mnt_passno); - fclose(fp); -} -#endif - -// There's no getmntent_r() on FreeBSD. -#if !defined(__FreeBSD__) -TEST(MemorySanitizer, getmntent_r) { - FILE *fp = setmntent("/etc/fstab", "r"); - struct mntent mntbuf; - char buf[1000]; - struct mntent *mnt = getmntent_r(fp, &mntbuf, buf, sizeof(buf)); - ASSERT_TRUE(mnt != NULL); - ASSERT_NE(0U, strlen(mnt->mnt_fsname)); - ASSERT_NE(0U, strlen(mnt->mnt_dir)); - ASSERT_NE(0U, strlen(mnt->mnt_type)); - ASSERT_NE(0U, strlen(mnt->mnt_opts)); - EXPECT_NOT_POISONED(mnt->mnt_freq); - EXPECT_NOT_POISONED(mnt->mnt_passno); - fclose(fp); -} -#endif - -TEST(MemorySanitizer, ether) { - const char *asc = "11:22:33:44:55:66"; - struct ether_addr *paddr = ether_aton(asc); - EXPECT_NOT_POISONED(*paddr); - - struct ether_addr addr; - paddr = ether_aton_r(asc, &addr); - ASSERT_EQ(paddr, &addr); - EXPECT_NOT_POISONED(addr); - - char *s = ether_ntoa(&addr); - ASSERT_NE(0U, strlen(s)); - - char buf[100]; - s = ether_ntoa_r(&addr, buf); - ASSERT_EQ(s, buf); - ASSERT_NE(0U, strlen(buf)); -} - -TEST(MemorySanitizer, mmap) { - const int size = 4096; - void *p1, *p2; - p1 = mmap(0, size, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANON, -1, 0); - __msan_poison(p1, size); - munmap(p1, size); - for (int i = 0; i < 1000; i++) { - p2 = mmap(0, size, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANON, -1, 0); - if (p2 == p1) - break; - else - munmap(p2, size); - } - if (p1 == p2) { - EXPECT_NOT_POISONED(*(char*)p2); - munmap(p2, size); - } -} - -// There's no fcvt() on FreeBSD. -#if !defined(__FreeBSD__) -// FIXME: enable and add ecvt. -// FIXME: check why msandr does nt handle fcvt. -TEST(MemorySanitizer, fcvt) { - int a, b; - break_optimization(&a); - break_optimization(&b); - EXPECT_POISONED(a); - EXPECT_POISONED(b); - char *str = fcvt(12345.6789, 10, &a, &b); - EXPECT_NOT_POISONED(a); - EXPECT_NOT_POISONED(b); - ASSERT_NE(nullptr, str); - EXPECT_NOT_POISONED(str[0]); - ASSERT_NE(0U, strlen(str)); -} -#endif - -// There's no fcvt_long() on FreeBSD. -#if !defined(__FreeBSD__) -TEST(MemorySanitizer, fcvt_long) { - int a, b; - break_optimization(&a); - break_optimization(&b); - EXPECT_POISONED(a); - EXPECT_POISONED(b); - char *str = fcvt(111111112345.6789, 10, &a, &b); - EXPECT_NOT_POISONED(a); - EXPECT_NOT_POISONED(b); - ASSERT_NE(nullptr, str); - EXPECT_NOT_POISONED(str[0]); - ASSERT_NE(0U, strlen(str)); -} -#endif - -TEST(MemorySanitizer, memchr) { - char x[10]; - break_optimization(x); - EXPECT_POISONED(x[0]); - x[2] = '2'; - void *res; - EXPECT_UMR(res = memchr(x, '2', 10)); - EXPECT_NOT_POISONED(res); - x[0] = '0'; - x[1] = '1'; - res = memchr(x, '2', 10); - EXPECT_EQ(&x[2], res); - EXPECT_UMR(res = memchr(x, '3', 10)); - EXPECT_NOT_POISONED(res); -} - -TEST(MemorySanitizer, memrchr) { - char x[10]; - break_optimization(x); - EXPECT_POISONED(x[0]); - x[9] = '9'; - void *res; - EXPECT_UMR(res = memrchr(x, '9', 10)); - EXPECT_NOT_POISONED(res); - x[0] = '0'; - x[1] = '1'; - res = memrchr(x, '0', 2); - EXPECT_EQ(&x[0], res); - EXPECT_UMR(res = memrchr(x, '7', 10)); - EXPECT_NOT_POISONED(res); -} - -TEST(MemorySanitizer, frexp) { - int x; - x = *GetPoisoned<int>(); - double r = frexp(1.1, &x); - EXPECT_NOT_POISONED(r); - EXPECT_NOT_POISONED(x); - - x = *GetPoisoned<int>(); - float rf = frexpf(1.1, &x); - EXPECT_NOT_POISONED(rf); - EXPECT_NOT_POISONED(x); - - x = *GetPoisoned<int>(); - double rl = frexpl(1.1, &x); - EXPECT_NOT_POISONED(rl); - EXPECT_NOT_POISONED(x); -} - -namespace { - -static int cnt; - -void SigactionHandler(int signo, siginfo_t* si, void* uc) { - ASSERT_EQ(signo, SIGPROF); - ASSERT_TRUE(si != NULL); - EXPECT_NOT_POISONED(si->si_errno); - EXPECT_NOT_POISONED(si->si_pid); -#if __linux__ -# if defined(__x86_64__) - EXPECT_NOT_POISONED(((ucontext_t*)uc)->uc_mcontext.gregs[REG_RIP]); -# elif defined(__i386__) - EXPECT_NOT_POISONED(((ucontext_t*)uc)->uc_mcontext.gregs[REG_EIP]); -# endif -#endif - ++cnt; -} - -TEST(MemorySanitizer, sigaction) { - struct sigaction act = {}; - struct sigaction oldact = {}; - struct sigaction origact = {}; - - sigaction(SIGPROF, 0, &origact); - - act.sa_flags |= SA_SIGINFO; - act.sa_sigaction = &SigactionHandler; - sigaction(SIGPROF, &act, 0); - - kill(getpid(), SIGPROF); - - act.sa_flags &= ~SA_SIGINFO; - act.sa_handler = SIG_DFL; - sigaction(SIGPROF, &act, 0); - - act.sa_flags &= ~SA_SIGINFO; - act.sa_handler = SIG_IGN; - sigaction(SIGPROF, &act, &oldact); - EXPECT_FALSE(oldact.sa_flags & SA_SIGINFO); - EXPECT_EQ(SIG_DFL, oldact.sa_handler); - kill(getpid(), SIGPROF); - - act.sa_flags |= SA_SIGINFO; - act.sa_sigaction = &SigactionHandler; - sigaction(SIGPROF, &act, &oldact); - EXPECT_FALSE(oldact.sa_flags & SA_SIGINFO); - EXPECT_EQ(SIG_IGN, oldact.sa_handler); - kill(getpid(), SIGPROF); - - act.sa_flags &= ~SA_SIGINFO; - act.sa_handler = SIG_DFL; - sigaction(SIGPROF, &act, &oldact); - EXPECT_TRUE(oldact.sa_flags & SA_SIGINFO); - EXPECT_EQ(&SigactionHandler, oldact.sa_sigaction); - EXPECT_EQ(2, cnt); - - sigaction(SIGPROF, &origact, 0); -} - -} // namespace - - -TEST(MemorySanitizer, sigemptyset) { - sigset_t s; - EXPECT_POISONED(s); - int res = sigemptyset(&s); - ASSERT_EQ(0, res); - EXPECT_NOT_POISONED(s); -} - -TEST(MemorySanitizer, sigfillset) { - sigset_t s; - EXPECT_POISONED(s); - int res = sigfillset(&s); - ASSERT_EQ(0, res); - EXPECT_NOT_POISONED(s); -} - -TEST(MemorySanitizer, sigpending) { - sigset_t s; - EXPECT_POISONED(s); - int res = sigpending(&s); - ASSERT_EQ(0, res); - EXPECT_NOT_POISONED(s); -} - -TEST(MemorySanitizer, sigprocmask) { - sigset_t s; - EXPECT_POISONED(s); - int res = sigprocmask(SIG_BLOCK, 0, &s); - ASSERT_EQ(0, res); - EXPECT_NOT_POISONED(s); -} - -struct StructWithDtor { - ~StructWithDtor(); -}; - -NOINLINE StructWithDtor::~StructWithDtor() { - break_optimization(0); -} - -TEST(MemorySanitizer, Invoke) { - StructWithDtor s; // Will cause the calls to become invokes. - EXPECT_NOT_POISONED(0); - EXPECT_POISONED(*GetPoisoned<int>()); - EXPECT_NOT_POISONED(0); - EXPECT_POISONED(*GetPoisoned<int>()); - EXPECT_POISONED(ReturnPoisoned<S4>()); -} - -TEST(MemorySanitizer, ptrtoint) { - // Test that shadow is propagated through pointer-to-integer conversion. - void* p = (void*)0xABCD; - __msan_poison(((char*)&p) + 1, sizeof(p)); - EXPECT_NOT_POISONED((((uintptr_t)p) & 0xFF) == 0); - - void* q = (void*)0xABCD; - __msan_poison(&q, sizeof(q) - 1); - EXPECT_POISONED((((uintptr_t)q) & 0xFF) == 0); -} - -static void vaargsfn2(int guard, ...) { - va_list vl; - va_start(vl, guard); - EXPECT_NOT_POISONED(va_arg(vl, int)); - EXPECT_NOT_POISONED(va_arg(vl, int)); - EXPECT_NOT_POISONED(va_arg(vl, int)); - EXPECT_POISONED(va_arg(vl, double)); - va_end(vl); -} - -static void vaargsfn(int guard, ...) { - va_list vl; - va_start(vl, guard); - EXPECT_NOT_POISONED(va_arg(vl, int)); - EXPECT_POISONED(va_arg(vl, int)); - // The following call will overwrite __msan_param_tls. - // Checks after it test that arg shadow was somehow saved across the call. - vaargsfn2(1, 2, 3, 4, *GetPoisoned<double>()); - EXPECT_NOT_POISONED(va_arg(vl, int)); - EXPECT_POISONED(va_arg(vl, int)); - va_end(vl); -} - -TEST(MemorySanitizer, VAArgTest) { - int* x = GetPoisoned<int>(); - int* y = GetPoisoned<int>(4); - vaargsfn(1, 13, *x, 42, *y); -} - -static void vaargsfn_many(int guard, ...) { - va_list vl; - va_start(vl, guard); - EXPECT_NOT_POISONED(va_arg(vl, int)); - EXPECT_POISONED(va_arg(vl, int)); - EXPECT_NOT_POISONED(va_arg(vl, int)); - EXPECT_NOT_POISONED(va_arg(vl, int)); - EXPECT_NOT_POISONED(va_arg(vl, int)); - EXPECT_NOT_POISONED(va_arg(vl, int)); - EXPECT_NOT_POISONED(va_arg(vl, int)); - EXPECT_NOT_POISONED(va_arg(vl, int)); - EXPECT_NOT_POISONED(va_arg(vl, int)); - EXPECT_POISONED(va_arg(vl, int)); - va_end(vl); -} - -TEST(MemorySanitizer, VAArgManyTest) { - int* x = GetPoisoned<int>(); - int* y = GetPoisoned<int>(4); - vaargsfn_many(1, 2, *x, 3, 4, 5, 6, 7, 8, 9, *y); -} - -static void vaargsfn_pass2(va_list vl) { - EXPECT_NOT_POISONED(va_arg(vl, int)); - EXPECT_NOT_POISONED(va_arg(vl, int)); - EXPECT_POISONED(va_arg(vl, int)); -} - -static void vaargsfn_pass(int guard, ...) { - va_list vl; - va_start(vl, guard); - EXPECT_POISONED(va_arg(vl, int)); - vaargsfn_pass2(vl); - va_end(vl); -} - -TEST(MemorySanitizer, VAArgPass) { - int* x = GetPoisoned<int>(); - int* y = GetPoisoned<int>(4); - vaargsfn_pass(1, *x, 2, 3, *y); -} - -static void vaargsfn_copy2(va_list vl) { - EXPECT_NOT_POISONED(va_arg(vl, int)); - EXPECT_POISONED(va_arg(vl, int)); -} - -static void vaargsfn_copy(int guard, ...) { - va_list vl; - va_start(vl, guard); - EXPECT_NOT_POISONED(va_arg(vl, int)); - EXPECT_POISONED(va_arg(vl, int)); - va_list vl2; - va_copy(vl2, vl); - vaargsfn_copy2(vl2); - EXPECT_NOT_POISONED(va_arg(vl, int)); - EXPECT_POISONED(va_arg(vl, int)); - va_end(vl); -} - -TEST(MemorySanitizer, VAArgCopy) { - int* x = GetPoisoned<int>(); - int* y = GetPoisoned<int>(4); - vaargsfn_copy(1, 2, *x, 3, *y); -} - -static void vaargsfn_ptr(int guard, ...) { - va_list vl; - va_start(vl, guard); - EXPECT_NOT_POISONED(va_arg(vl, int*)); - EXPECT_POISONED(va_arg(vl, int*)); - EXPECT_NOT_POISONED(va_arg(vl, int*)); - EXPECT_POISONED(va_arg(vl, double*)); - va_end(vl); -} - -TEST(MemorySanitizer, VAArgPtr) { - int** x = GetPoisoned<int*>(); - double** y = GetPoisoned<double*>(8); - int z; - vaargsfn_ptr(1, &z, *x, &z, *y); -} - -static void vaargsfn_overflow(int guard, ...) { - va_list vl; - va_start(vl, guard); - EXPECT_NOT_POISONED(va_arg(vl, int)); - EXPECT_NOT_POISONED(va_arg(vl, int)); - EXPECT_POISONED(va_arg(vl, int)); - EXPECT_NOT_POISONED(va_arg(vl, int)); - EXPECT_NOT_POISONED(va_arg(vl, int)); - EXPECT_NOT_POISONED(va_arg(vl, int)); - - EXPECT_NOT_POISONED(va_arg(vl, double)); - EXPECT_NOT_POISONED(va_arg(vl, double)); - EXPECT_NOT_POISONED(va_arg(vl, double)); - EXPECT_POISONED(va_arg(vl, double)); - EXPECT_NOT_POISONED(va_arg(vl, double)); - EXPECT_POISONED(va_arg(vl, int*)); - EXPECT_NOT_POISONED(va_arg(vl, double)); - EXPECT_NOT_POISONED(va_arg(vl, double)); - - EXPECT_POISONED(va_arg(vl, int)); - EXPECT_POISONED(va_arg(vl, double)); - EXPECT_POISONED(va_arg(vl, int*)); - - EXPECT_NOT_POISONED(va_arg(vl, int)); - EXPECT_NOT_POISONED(va_arg(vl, double)); - EXPECT_NOT_POISONED(va_arg(vl, int*)); - - EXPECT_POISONED(va_arg(vl, int)); - EXPECT_POISONED(va_arg(vl, double)); - EXPECT_POISONED(va_arg(vl, int*)); - - va_end(vl); -} - -TEST(MemorySanitizer, VAArgOverflow) { - int* x = GetPoisoned<int>(); - double* y = GetPoisoned<double>(8); - int** p = GetPoisoned<int*>(16); - int z; - vaargsfn_overflow(1, - 1, 2, *x, 4, 5, 6, - 1.1, 2.2, 3.3, *y, 5.5, *p, 7.7, 8.8, - // the following args will overflow for sure - *x, *y, *p, - 7, 9.9, &z, - *x, *y, *p); -} - -static void vaargsfn_tlsoverwrite2(int guard, ...) { - va_list vl; - va_start(vl, guard); - for (int i = 0; i < 20; ++i) - EXPECT_NOT_POISONED(va_arg(vl, int)); - va_end(vl); -} - -static void vaargsfn_tlsoverwrite(int guard, ...) { - // This call will overwrite TLS contents unless it's backed up somewhere. - vaargsfn_tlsoverwrite2(2, - 42, 42, 42, 42, 42, - 42, 42, 42, 42, 42, - 42, 42, 42, 42, 42, - 42, 42, 42, 42, 42); // 20x - va_list vl; - va_start(vl, guard); - for (int i = 0; i < 20; ++i) - EXPECT_POISONED(va_arg(vl, int)); - va_end(vl); -} - -TEST(MemorySanitizer, VAArgTLSOverwrite) { - int* x = GetPoisoned<int>(); - vaargsfn_tlsoverwrite(1, - *x, *x, *x, *x, *x, - *x, *x, *x, *x, *x, - *x, *x, *x, *x, *x, - *x, *x, *x, *x, *x); // 20x - -} - -struct StructByVal { - int a, b, c, d, e, f; -}; - -static void vaargsfn_structbyval(int guard, ...) { - va_list vl; - va_start(vl, guard); - { - StructByVal s = va_arg(vl, StructByVal); - EXPECT_NOT_POISONED(s.a); - EXPECT_POISONED(s.b); - EXPECT_NOT_POISONED(s.c); - EXPECT_POISONED(s.d); - EXPECT_NOT_POISONED(s.e); - EXPECT_POISONED(s.f); - } - { - StructByVal s = va_arg(vl, StructByVal); - EXPECT_NOT_POISONED(s.a); - EXPECT_POISONED(s.b); - EXPECT_NOT_POISONED(s.c); - EXPECT_POISONED(s.d); - EXPECT_NOT_POISONED(s.e); - EXPECT_POISONED(s.f); - } - va_end(vl); -} - -TEST(MemorySanitizer, VAArgStructByVal) { - StructByVal s; - s.a = 1; - s.b = *GetPoisoned<int>(); - s.c = 2; - s.d = *GetPoisoned<int>(); - s.e = 3; - s.f = *GetPoisoned<int>(); - vaargsfn_structbyval(0, s, s); -} - -NOINLINE void StructByValTestFunc(struct StructByVal s) { - EXPECT_NOT_POISONED(s.a); - EXPECT_POISONED(s.b); - EXPECT_NOT_POISONED(s.c); - EXPECT_POISONED(s.d); - EXPECT_NOT_POISONED(s.e); - EXPECT_POISONED(s.f); -} - -NOINLINE void StructByValTestFunc1(struct StructByVal s) { - StructByValTestFunc(s); -} - -NOINLINE void StructByValTestFunc2(int z, struct StructByVal s) { - StructByValTestFunc(s); -} - -TEST(MemorySanitizer, StructByVal) { - // Large aggregates are passed as "byval" pointer argument in LLVM. - struct StructByVal s; - s.a = 1; - s.b = *GetPoisoned<int>(); - s.c = 2; - s.d = *GetPoisoned<int>(); - s.e = 3; - s.f = *GetPoisoned<int>(); - StructByValTestFunc(s); - StructByValTestFunc1(s); - StructByValTestFunc2(0, s); -} - - -#if MSAN_HAS_M128 -NOINLINE __m128i m128Eq(__m128i *a, __m128i *b) { return _mm_cmpeq_epi16(*a, *b); } -NOINLINE __m128i m128Lt(__m128i *a, __m128i *b) { return _mm_cmplt_epi16(*a, *b); } -TEST(MemorySanitizer, m128) { - __m128i a = _mm_set1_epi16(0x1234); - __m128i b = _mm_set1_epi16(0x7890); - EXPECT_NOT_POISONED(m128Eq(&a, &b)); - EXPECT_NOT_POISONED(m128Lt(&a, &b)); -} -// FIXME: add more tests for __m128i. -#endif // MSAN_HAS_M128 - -// We should not complain when copying this poisoned hole. -struct StructWithHole { - U4 a; - // 4-byte hole. - U8 b; -}; - -NOINLINE StructWithHole ReturnStructWithHole() { - StructWithHole res; - __msan_poison(&res, sizeof(res)); - res.a = 1; - res.b = 2; - return res; -} - -TEST(MemorySanitizer, StructWithHole) { - StructWithHole a = ReturnStructWithHole(); - break_optimization(&a); -} - -template <class T> -NOINLINE T ReturnStruct() { - T res; - __msan_poison(&res, sizeof(res)); - res.a = 1; - return res; -} - -template <class T> -NOINLINE void TestReturnStruct() { - T s1 = ReturnStruct<T>(); - EXPECT_NOT_POISONED(s1.a); - EXPECT_POISONED(s1.b); -} - -struct SSS1 { - int a, b, c; -}; -struct SSS2 { - int b, a, c; -}; -struct SSS3 { - int b, c, a; -}; -struct SSS4 { - int c, b, a; -}; - -struct SSS5 { - int a; - float b; -}; -struct SSS6 { - int a; - double b; -}; -struct SSS7 { - S8 b; - int a; -}; -struct SSS8 { - S2 b; - S8 a; -}; - -TEST(MemorySanitizer, IntStruct3) { - TestReturnStruct<SSS1>(); - TestReturnStruct<SSS2>(); - TestReturnStruct<SSS3>(); - TestReturnStruct<SSS4>(); - TestReturnStruct<SSS5>(); - TestReturnStruct<SSS6>(); - TestReturnStruct<SSS7>(); - TestReturnStruct<SSS8>(); -} - -struct LongStruct { - U1 a1, b1; - U2 a2, b2; - U4 a4, b4; - U8 a8, b8; -}; - -NOINLINE LongStruct ReturnLongStruct1() { - LongStruct res; - __msan_poison(&res, sizeof(res)); - res.a1 = res.a2 = res.a4 = res.a8 = 111; - // leaves b1, .., b8 poisoned. - return res; -} - -NOINLINE LongStruct ReturnLongStruct2() { - LongStruct res; - __msan_poison(&res, sizeof(res)); - res.b1 = res.b2 = res.b4 = res.b8 = 111; - // leaves a1, .., a8 poisoned. - return res; -} - -TEST(MemorySanitizer, LongStruct) { - LongStruct s1 = ReturnLongStruct1(); - __msan_print_shadow(&s1, sizeof(s1)); - EXPECT_NOT_POISONED(s1.a1); - EXPECT_NOT_POISONED(s1.a2); - EXPECT_NOT_POISONED(s1.a4); - EXPECT_NOT_POISONED(s1.a8); - - EXPECT_POISONED(s1.b1); - EXPECT_POISONED(s1.b2); - EXPECT_POISONED(s1.b4); - EXPECT_POISONED(s1.b8); - - LongStruct s2 = ReturnLongStruct2(); - __msan_print_shadow(&s2, sizeof(s2)); - EXPECT_NOT_POISONED(s2.b1); - EXPECT_NOT_POISONED(s2.b2); - EXPECT_NOT_POISONED(s2.b4); - EXPECT_NOT_POISONED(s2.b8); - - EXPECT_POISONED(s2.a1); - EXPECT_POISONED(s2.a2); - EXPECT_POISONED(s2.a4); - EXPECT_POISONED(s2.a8); -} - -TEST(MemorySanitizer, getrlimit) { - struct rlimit limit; - __msan_poison(&limit, sizeof(limit)); - int result = getrlimit(RLIMIT_DATA, &limit); - ASSERT_EQ(result, 0); - EXPECT_NOT_POISONED(limit.rlim_cur); - EXPECT_NOT_POISONED(limit.rlim_max); -} - -TEST(MemorySanitizer, getrusage) { - struct rusage usage; - __msan_poison(&usage, sizeof(usage)); - int result = getrusage(RUSAGE_SELF, &usage); - ASSERT_EQ(result, 0); - EXPECT_NOT_POISONED(usage.ru_utime.tv_sec); - EXPECT_NOT_POISONED(usage.ru_utime.tv_usec); - EXPECT_NOT_POISONED(usage.ru_stime.tv_sec); - EXPECT_NOT_POISONED(usage.ru_stime.tv_usec); - EXPECT_NOT_POISONED(usage.ru_maxrss); - EXPECT_NOT_POISONED(usage.ru_minflt); - EXPECT_NOT_POISONED(usage.ru_majflt); - EXPECT_NOT_POISONED(usage.ru_inblock); - EXPECT_NOT_POISONED(usage.ru_oublock); - EXPECT_NOT_POISONED(usage.ru_nvcsw); - EXPECT_NOT_POISONED(usage.ru_nivcsw); -} - -#if defined(__FreeBSD__) -static void GetProgramPath(char *buf, size_t sz) { - int mib[4] = { CTL_KERN, KERN_PROC, KERN_PROC_PATHNAME, -1 }; - int res = sysctl(mib, 4, buf, &sz, NULL, 0); - ASSERT_EQ(0, res); -} -#elif defined(__GLIBC__) -static void GetProgramPath(char *buf, size_t sz) { - extern char *program_invocation_name; - int res = snprintf(buf, sz, "%s", program_invocation_name); - ASSERT_GE(res, 0); - ASSERT_LT((size_t)res, sz); -} -#else -# error "TODO: port this" -#endif - -static void dladdr_testfn() {} - -TEST(MemorySanitizer, dladdr) { - Dl_info info; - __msan_poison(&info, sizeof(info)); - int result = dladdr((const void*)dladdr_testfn, &info); - ASSERT_NE(result, 0); - EXPECT_NOT_POISONED((unsigned long)info.dli_fname); - if (info.dli_fname) - EXPECT_NOT_POISONED(strlen(info.dli_fname)); - EXPECT_NOT_POISONED((unsigned long)info.dli_fbase); - EXPECT_NOT_POISONED((unsigned long)info.dli_sname); - if (info.dli_sname) - EXPECT_NOT_POISONED(strlen(info.dli_sname)); - EXPECT_NOT_POISONED((unsigned long)info.dli_saddr); -} - -#ifndef MSAN_TEST_DISABLE_DLOPEN - -static int dl_phdr_callback(struct dl_phdr_info *info, size_t size, void *data) { - (*(int *)data)++; - EXPECT_NOT_POISONED(info->dlpi_addr); - EXPECT_NOT_POISONED(strlen(info->dlpi_name)); - EXPECT_NOT_POISONED(info->dlpi_phnum); - for (int i = 0; i < info->dlpi_phnum; ++i) - EXPECT_NOT_POISONED(info->dlpi_phdr[i]); - return 0; -} - -// Compute the path to our loadable DSO. We assume it's in the same -// directory. Only use string routines that we intercept so far to do this. -static void GetPathToLoadable(char *buf, size_t sz) { - char program_path[kMaxPathLength]; - GetProgramPath(program_path, sizeof(program_path)); - - const char *last_slash = strrchr(program_path, '/'); - ASSERT_NE(nullptr, last_slash); - size_t dir_len = (size_t)(last_slash - program_path); -#if defined(__x86_64__) - static const char basename[] = "libmsan_loadable.x86_64.so"; -#elif defined(__MIPSEB__) || defined(MIPSEB) - static const char basename[] = "libmsan_loadable.mips64.so"; -#elif defined(__mips64) - static const char basename[] = "libmsan_loadable.mips64el.so"; -#endif - int res = snprintf(buf, sz, "%.*s/%s", - (int)dir_len, program_path, basename); - ASSERT_GE(res, 0); - ASSERT_LT((size_t)res, sz); -} - -TEST(MemorySanitizer, dl_iterate_phdr) { - char path[kMaxPathLength]; - GetPathToLoadable(path, sizeof(path)); - - // Having at least one dlopen'ed library in the process makes this more - // entertaining. - void *lib = dlopen(path, RTLD_LAZY); - ASSERT_NE((void*)0, lib); - - int count = 0; - int result = dl_iterate_phdr(dl_phdr_callback, &count); - ASSERT_GT(count, 0); - - dlclose(lib); -} - -TEST(MemorySanitizer, dlopen) { - char path[kMaxPathLength]; - GetPathToLoadable(path, sizeof(path)); - - // We need to clear shadow for globals when doing dlopen. In order to test - // this, we have to poison the shadow for the DSO before we load it. In - // general this is difficult, but the loader tends to reload things in the - // same place, so we open, close, and then reopen. The global should always - // start out clean after dlopen. - for (int i = 0; i < 2; i++) { - void *lib = dlopen(path, RTLD_LAZY); - if (lib == NULL) { - printf("dlerror: %s\n", dlerror()); - ASSERT_TRUE(lib != NULL); - } - void **(*get_dso_global)() = (void **(*)())dlsym(lib, "get_dso_global"); - ASSERT_TRUE(get_dso_global != NULL); - void **dso_global = get_dso_global(); - EXPECT_NOT_POISONED(*dso_global); - __msan_poison(dso_global, sizeof(*dso_global)); - EXPECT_POISONED(*dso_global); - dlclose(lib); - } -} - -// Regression test for a crash in dlopen() interceptor. -TEST(MemorySanitizer, dlopenFailed) { - const char *path = "/libmsan_loadable_does_not_exist.so"; - void *lib = dlopen(path, RTLD_LAZY); - ASSERT_TRUE(lib == NULL); -} - -#endif // MSAN_TEST_DISABLE_DLOPEN - -// There's no sched_getaffinity() on FreeBSD. -#if !defined(__FreeBSD__) -TEST(MemorySanitizer, sched_getaffinity) { - cpu_set_t mask; - int res = sched_getaffinity(getpid(), sizeof(mask), &mask); - ASSERT_EQ(0, res); - EXPECT_NOT_POISONED(mask); -} -#endif - -TEST(MemorySanitizer, scanf) { - const char *input = "42 hello"; - int* d = new int; - char* s = new char[7]; - int res = sscanf(input, "%d %5s", d, s); - printf("res %d\n", res); - ASSERT_EQ(res, 2); - EXPECT_NOT_POISONED(*d); - EXPECT_NOT_POISONED(s[0]); - EXPECT_NOT_POISONED(s[1]); - EXPECT_NOT_POISONED(s[2]); - EXPECT_NOT_POISONED(s[3]); - EXPECT_NOT_POISONED(s[4]); - EXPECT_NOT_POISONED(s[5]); - EXPECT_POISONED(s[6]); - delete[] s; - delete d; -} - -static void *SimpleThread_threadfn(void* data) { - return new int; -} - -TEST(MemorySanitizer, SimpleThread) { - pthread_t t; - void *p; - int res = pthread_create(&t, NULL, SimpleThread_threadfn, NULL); - ASSERT_EQ(0, res); - EXPECT_NOT_POISONED(t); - res = pthread_join(t, &p); - ASSERT_EQ(0, res); - EXPECT_NOT_POISONED(p); - delete (int*)p; -} - -static void *SmallStackThread_threadfn(void* data) { - return 0; -} - -TEST(MemorySanitizer, SmallStackThread) { - pthread_attr_t attr; - pthread_t t; - void *p; - int res; - res = pthread_attr_init(&attr); - ASSERT_EQ(0, res); - res = pthread_attr_setstacksize(&attr, 64 * 1024); - ASSERT_EQ(0, res); - res = pthread_create(&t, &attr, SmallStackThread_threadfn, NULL); - ASSERT_EQ(0, res); - res = pthread_join(t, &p); - ASSERT_EQ(0, res); - res = pthread_attr_destroy(&attr); - ASSERT_EQ(0, res); -} - -TEST(MemorySanitizer, SmallPreAllocatedStackThread) { - pthread_attr_t attr; - pthread_t t; - int res; - res = pthread_attr_init(&attr); - ASSERT_EQ(0, res); - void *stack; - const size_t kStackSize = 16 * 1024; - res = posix_memalign(&stack, 4096, kStackSize); - ASSERT_EQ(0, res); - res = pthread_attr_setstack(&attr, stack, kStackSize); - ASSERT_EQ(0, res); - res = pthread_create(&t, &attr, SmallStackThread_threadfn, NULL); - EXPECT_EQ(0, res); - res = pthread_join(t, NULL); - ASSERT_EQ(0, res); - res = pthread_attr_destroy(&attr); - ASSERT_EQ(0, res); -} - -TEST(MemorySanitizer, pthread_attr_get) { - pthread_attr_t attr; - int res; - res = pthread_attr_init(&attr); - ASSERT_EQ(0, res); - { - int v; - res = pthread_attr_getdetachstate(&attr, &v); - ASSERT_EQ(0, res); - EXPECT_NOT_POISONED(v); - } - { - size_t v; - res = pthread_attr_getguardsize(&attr, &v); - ASSERT_EQ(0, res); - EXPECT_NOT_POISONED(v); - } - { - struct sched_param v; - res = pthread_attr_getschedparam(&attr, &v); - ASSERT_EQ(0, res); - EXPECT_NOT_POISONED(v); - } - { - int v; - res = pthread_attr_getschedpolicy(&attr, &v); - ASSERT_EQ(0, res); - EXPECT_NOT_POISONED(v); - } - { - int v; - res = pthread_attr_getinheritsched(&attr, &v); - ASSERT_EQ(0, res); - EXPECT_NOT_POISONED(v); - } - { - int v; - res = pthread_attr_getscope(&attr, &v); - ASSERT_EQ(0, res); - EXPECT_NOT_POISONED(v); - } - { - size_t v; - res = pthread_attr_getstacksize(&attr, &v); - ASSERT_EQ(0, res); - EXPECT_NOT_POISONED(v); - } - { - void *v; - size_t w; - res = pthread_attr_getstack(&attr, &v, &w); - ASSERT_EQ(0, res); - EXPECT_NOT_POISONED(v); - EXPECT_NOT_POISONED(w); - } - { - cpu_set_t v; - res = pthread_attr_getaffinity_np(&attr, sizeof(v), &v); - ASSERT_EQ(0, res); - EXPECT_NOT_POISONED(v); - } - res = pthread_attr_destroy(&attr); - ASSERT_EQ(0, res); -} - -TEST(MemorySanitizer, pthread_getschedparam) { - int policy; - struct sched_param param; - int res = pthread_getschedparam(pthread_self(), &policy, ¶m); - ASSERT_EQ(0, res); - EXPECT_NOT_POISONED(policy); - EXPECT_NOT_POISONED(param.sched_priority); -} - -TEST(MemorySanitizer, pthread_key_create) { - pthread_key_t key; - int res = pthread_key_create(&key, NULL); - ASSERT_EQ(0, res); - EXPECT_NOT_POISONED(key); - res = pthread_key_delete(key); - ASSERT_EQ(0, res); -} - -namespace { -struct SignalCondArg { - pthread_cond_t* cond; - pthread_mutex_t* mu; - bool broadcast; -}; - -void *SignalCond(void *param) { - SignalCondArg *arg = reinterpret_cast<SignalCondArg *>(param); - pthread_mutex_lock(arg->mu); - if (arg->broadcast) - pthread_cond_broadcast(arg->cond); - else - pthread_cond_signal(arg->cond); - pthread_mutex_unlock(arg->mu); - return 0; -} -} // namespace - -TEST(MemorySanitizer, pthread_cond_wait) { - pthread_cond_t cond; - pthread_mutex_t mu; - SignalCondArg args = {&cond, &mu, false}; - pthread_cond_init(&cond, 0); - pthread_mutex_init(&mu, 0); - pthread_mutex_lock(&mu); - - // signal - pthread_t thr; - pthread_create(&thr, 0, SignalCond, &args); - int res = pthread_cond_wait(&cond, &mu); - ASSERT_EQ(0, res); - pthread_join(thr, 0); - - // broadcast - args.broadcast = true; - pthread_create(&thr, 0, SignalCond, &args); - res = pthread_cond_wait(&cond, &mu); - ASSERT_EQ(0, res); - pthread_join(thr, 0); - - pthread_mutex_unlock(&mu); - pthread_mutex_destroy(&mu); - pthread_cond_destroy(&cond); -} - -TEST(MemorySanitizer, tmpnam) { - char s[L_tmpnam]; - char *res = tmpnam(s); - ASSERT_EQ(s, res); - EXPECT_NOT_POISONED(strlen(res)); -} - -TEST(MemorySanitizer, tempnam) { - char *res = tempnam(NULL, "zzz"); - EXPECT_NOT_POISONED(strlen(res)); - free(res); -} - -TEST(MemorySanitizer, posix_memalign) { - void *p; - EXPECT_POISONED(p); - int res = posix_memalign(&p, 4096, 13); - ASSERT_EQ(0, res); - EXPECT_NOT_POISONED(p); - EXPECT_EQ(0U, (uintptr_t)p % 4096); - free(p); -} - -// There's no memalign() on FreeBSD. -#if !defined(__FreeBSD__) -TEST(MemorySanitizer, memalign) { - void *p = memalign(4096, 13); - EXPECT_EQ(0U, (uintptr_t)p % kPageSize); - free(p); -} -#endif - -TEST(MemorySanitizer, valloc) { - void *a = valloc(100); - EXPECT_EQ(0U, (uintptr_t)a % kPageSize); - free(a); -} - -// There's no pvalloc() on FreeBSD. -#if !defined(__FreeBSD__) -TEST(MemorySanitizer, pvalloc) { - void *p = pvalloc(kPageSize + 100); - EXPECT_EQ(0U, (uintptr_t)p % kPageSize); - EXPECT_EQ(2 * kPageSize, __sanitizer_get_allocated_size(p)); - free(p); - - p = pvalloc(0); // pvalloc(0) should allocate at least one page. - EXPECT_EQ(0U, (uintptr_t)p % kPageSize); - EXPECT_EQ(kPageSize, __sanitizer_get_allocated_size(p)); - free(p); -} -#endif - -TEST(MemorySanitizer, inet_pton) { - const char *s = "1:0:0:0:0:0:0:8"; - unsigned char buf[sizeof(struct in6_addr)]; - int res = inet_pton(AF_INET6, s, buf); - ASSERT_EQ(1, res); - EXPECT_NOT_POISONED(buf[0]); - EXPECT_NOT_POISONED(buf[sizeof(struct in6_addr) - 1]); - - char s_out[INET6_ADDRSTRLEN]; - EXPECT_POISONED(s_out[3]); - const char *q = inet_ntop(AF_INET6, buf, s_out, INET6_ADDRSTRLEN); - ASSERT_NE((void*)0, q); - EXPECT_NOT_POISONED(s_out[3]); -} - -TEST(MemorySanitizer, inet_aton) { - const char *s = "127.0.0.1"; - struct in_addr in[2]; - int res = inet_aton(s, in); - ASSERT_NE(0, res); - EXPECT_NOT_POISONED(in[0]); - EXPECT_POISONED(*(char *)(in + 1)); -} - -TEST(MemorySanitizer, uname) { - struct utsname u; - int res = uname(&u); - ASSERT_EQ(0, res); - EXPECT_NOT_POISONED(strlen(u.sysname)); - EXPECT_NOT_POISONED(strlen(u.nodename)); - EXPECT_NOT_POISONED(strlen(u.release)); - EXPECT_NOT_POISONED(strlen(u.version)); - EXPECT_NOT_POISONED(strlen(u.machine)); -} - -TEST(MemorySanitizer, gethostname) { - char buf[100]; - int res = gethostname(buf, 100); - ASSERT_EQ(0, res); - EXPECT_NOT_POISONED(strlen(buf)); -} - -// There's no sysinfo() on FreeBSD. -#if !defined(__FreeBSD__) -TEST(MemorySanitizer, sysinfo) { - struct sysinfo info; - int res = sysinfo(&info); - ASSERT_EQ(0, res); - EXPECT_NOT_POISONED(info); -} -#endif - -TEST(MemorySanitizer, getpwuid) { - struct passwd *p = getpwuid(0); // root - ASSERT_TRUE(p != NULL); - EXPECT_NOT_POISONED(p->pw_name); - ASSERT_TRUE(p->pw_name != NULL); - EXPECT_NOT_POISONED(p->pw_name[0]); - EXPECT_NOT_POISONED(p->pw_uid); - ASSERT_EQ(0U, p->pw_uid); -} - -TEST(MemorySanitizer, getpwuid_r) { - struct passwd pwd; - struct passwd *pwdres; - char buf[10000]; - int res = getpwuid_r(0, &pwd, buf, sizeof(buf), &pwdres); - ASSERT_EQ(0, res); - EXPECT_NOT_POISONED(pwd.pw_name); - ASSERT_TRUE(pwd.pw_name != NULL); - EXPECT_NOT_POISONED(pwd.pw_name[0]); - EXPECT_NOT_POISONED(pwd.pw_uid); - ASSERT_EQ(0U, pwd.pw_uid); - EXPECT_NOT_POISONED(pwdres); -} - -TEST(MemorySanitizer, getpwnam_r) { - struct passwd pwd; - struct passwd *pwdres; - char buf[10000]; - int res = getpwnam_r("root", &pwd, buf, sizeof(buf), &pwdres); - ASSERT_EQ(0, res); - EXPECT_NOT_POISONED(pwd.pw_name); - ASSERT_TRUE(pwd.pw_name != NULL); - EXPECT_NOT_POISONED(pwd.pw_name[0]); - EXPECT_NOT_POISONED(pwd.pw_uid); - ASSERT_EQ(0U, pwd.pw_uid); - EXPECT_NOT_POISONED(pwdres); -} - -TEST(MemorySanitizer, getpwnam_r_positive) { - struct passwd pwd; - struct passwd *pwdres; - char s[5]; - strncpy(s, "abcd", 5); - __msan_poison(s, 5); - char buf[10000]; - int res; - EXPECT_UMR(res = getpwnam_r(s, &pwd, buf, sizeof(buf), &pwdres)); -} - -TEST(MemorySanitizer, getgrnam_r) { - struct group grp; - struct group *grpres; - char buf[10000]; - int res = getgrnam_r(SUPERUSER_GROUP, &grp, buf, sizeof(buf), &grpres); - ASSERT_EQ(0, res); - // Note that getgrnam_r() returns 0 if the matching group is not found. - ASSERT_NE(nullptr, grpres); - EXPECT_NOT_POISONED(grp.gr_name); - ASSERT_TRUE(grp.gr_name != NULL); - EXPECT_NOT_POISONED(grp.gr_name[0]); - EXPECT_NOT_POISONED(grp.gr_gid); - EXPECT_NOT_POISONED(grpres); -} - -TEST(MemorySanitizer, getpwent) { - setpwent(); - struct passwd *p = getpwent(); - ASSERT_TRUE(p != NULL); - EXPECT_NOT_POISONED(p->pw_name); - ASSERT_TRUE(p->pw_name != NULL); - EXPECT_NOT_POISONED(p->pw_name[0]); - EXPECT_NOT_POISONED(p->pw_uid); -} - -TEST(MemorySanitizer, getpwent_r) { - struct passwd pwd; - struct passwd *pwdres; - char buf[10000]; - setpwent(); - int res = getpwent_r(&pwd, buf, sizeof(buf), &pwdres); - ASSERT_EQ(0, res); - EXPECT_NOT_POISONED(pwd.pw_name); - ASSERT_TRUE(pwd.pw_name != NULL); - EXPECT_NOT_POISONED(pwd.pw_name[0]); - EXPECT_NOT_POISONED(pwd.pw_uid); - EXPECT_NOT_POISONED(pwdres); -} - -// There's no fgetpwent() on FreeBSD. -#if !defined(__FreeBSD__) -TEST(MemorySanitizer, fgetpwent) { - FILE *fp = fopen("/etc/passwd", "r"); - struct passwd *p = fgetpwent(fp); - ASSERT_TRUE(p != NULL); - EXPECT_NOT_POISONED(p->pw_name); - ASSERT_TRUE(p->pw_name != NULL); - EXPECT_NOT_POISONED(p->pw_name[0]); - EXPECT_NOT_POISONED(p->pw_uid); - fclose(fp); -} -#endif - -TEST(MemorySanitizer, getgrent) { - setgrent(); - struct group *p = getgrent(); - ASSERT_TRUE(p != NULL); - EXPECT_NOT_POISONED(p->gr_name); - ASSERT_TRUE(p->gr_name != NULL); - EXPECT_NOT_POISONED(p->gr_name[0]); - EXPECT_NOT_POISONED(p->gr_gid); -} - -// There's no fgetgrent() on FreeBSD. -#if !defined(__FreeBSD__) -TEST(MemorySanitizer, fgetgrent) { - FILE *fp = fopen("/etc/group", "r"); - struct group *grp = fgetgrent(fp); - ASSERT_TRUE(grp != NULL); - EXPECT_NOT_POISONED(grp->gr_name); - ASSERT_TRUE(grp->gr_name != NULL); - EXPECT_NOT_POISONED(grp->gr_name[0]); - EXPECT_NOT_POISONED(grp->gr_gid); - for (char **p = grp->gr_mem; *p; ++p) { - EXPECT_NOT_POISONED((*p)[0]); - EXPECT_TRUE(strlen(*p) > 0); - } - fclose(fp); -} -#endif - -TEST(MemorySanitizer, getgrent_r) { - struct group grp; - struct group *grpres; - char buf[10000]; - setgrent(); - int res = getgrent_r(&grp, buf, sizeof(buf), &grpres); - ASSERT_EQ(0, res); - EXPECT_NOT_POISONED(grp.gr_name); - ASSERT_TRUE(grp.gr_name != NULL); - EXPECT_NOT_POISONED(grp.gr_name[0]); - EXPECT_NOT_POISONED(grp.gr_gid); - EXPECT_NOT_POISONED(grpres); -} - -// There's no fgetgrent_r() on FreeBSD. -#if !defined(__FreeBSD__) -TEST(MemorySanitizer, fgetgrent_r) { - FILE *fp = fopen("/etc/group", "r"); - struct group grp; - struct group *grpres; - char buf[10000]; - setgrent(); - int res = fgetgrent_r(fp, &grp, buf, sizeof(buf), &grpres); - ASSERT_EQ(0, res); - EXPECT_NOT_POISONED(grp.gr_name); - ASSERT_TRUE(grp.gr_name != NULL); - EXPECT_NOT_POISONED(grp.gr_name[0]); - EXPECT_NOT_POISONED(grp.gr_gid); - EXPECT_NOT_POISONED(grpres); - fclose(fp); -} -#endif - -TEST(MemorySanitizer, getgroups) { - int n = getgroups(0, 0); - gid_t *gids = new gid_t[n]; - int res = getgroups(n, gids); - ASSERT_EQ(n, res); - for (int i = 0; i < n; ++i) - EXPECT_NOT_POISONED(gids[i]); -} - -TEST(MemorySanitizer, wordexp) { - wordexp_t w; - int res = wordexp("a b c", &w, 0); - ASSERT_EQ(0, res); - ASSERT_EQ(3U, w.we_wordc); - ASSERT_STREQ("a", w.we_wordv[0]); - ASSERT_STREQ("b", w.we_wordv[1]); - ASSERT_STREQ("c", w.we_wordv[2]); -} - -template<class T> -static bool applySlt(T value, T shadow) { - __msan_partial_poison(&value, &shadow, sizeof(T)); - volatile bool zzz = true; - // This "|| zzz" trick somehow makes LLVM emit "icmp slt" instead of - // a shift-and-trunc to get at the highest bit. - volatile bool v = value < 0 || zzz; - return v; -} - -TEST(MemorySanitizer, SignedCompareWithZero) { - EXPECT_NOT_POISONED(applySlt<S4>(0xF, 0xF)); - EXPECT_NOT_POISONED(applySlt<S4>(0xF, 0xFF)); - EXPECT_NOT_POISONED(applySlt<S4>(0xF, 0xFFFFFF)); - EXPECT_NOT_POISONED(applySlt<S4>(0xF, 0x7FFFFFF)); - EXPECT_UMR(applySlt<S4>(0xF, 0x80FFFFFF)); - EXPECT_UMR(applySlt<S4>(0xF, 0xFFFFFFFF)); -} - -template <class T, class S> -static T poisoned(T Va, S Sa) { - char SIZE_CHECK1[(ssize_t)sizeof(T) - (ssize_t)sizeof(S)]; - char SIZE_CHECK2[(ssize_t)sizeof(S) - (ssize_t)sizeof(T)]; - T a; - a = Va; - __msan_partial_poison(&a, &Sa, sizeof(T)); - return a; -} - -TEST(MemorySanitizer, ICmpRelational) { - EXPECT_NOT_POISONED(poisoned(0, 0) < poisoned(0, 0)); - EXPECT_NOT_POISONED(poisoned(0U, 0) < poisoned(0U, 0)); - EXPECT_NOT_POISONED(poisoned(0LL, 0LLU) < poisoned(0LL, 0LLU)); - EXPECT_NOT_POISONED(poisoned(0LLU, 0LLU) < poisoned(0LLU, 0LLU)); - EXPECT_POISONED(poisoned(0xFF, 0xFF) < poisoned(0xFF, 0xFF)); - EXPECT_POISONED(poisoned(0xFFFFFFFFU, 0xFFFFFFFFU) < - poisoned(0xFFFFFFFFU, 0xFFFFFFFFU)); - EXPECT_POISONED(poisoned(-1, 0xFFFFFFFFU) < - poisoned(-1, 0xFFFFFFFFU)); - - EXPECT_NOT_POISONED(poisoned(0, 0) <= poisoned(0, 0)); - EXPECT_NOT_POISONED(poisoned(0U, 0) <= poisoned(0U, 0)); - EXPECT_NOT_POISONED(poisoned(0LL, 0LLU) <= poisoned(0LL, 0LLU)); - EXPECT_NOT_POISONED(poisoned(0LLU, 0LLU) <= poisoned(0LLU, 0LLU)); - EXPECT_POISONED(poisoned(0xFF, 0xFF) <= poisoned(0xFF, 0xFF)); - EXPECT_POISONED(poisoned(0xFFFFFFFFU, 0xFFFFFFFFU) <= - poisoned(0xFFFFFFFFU, 0xFFFFFFFFU)); - EXPECT_POISONED(poisoned(-1, 0xFFFFFFFFU) <= - poisoned(-1, 0xFFFFFFFFU)); - - EXPECT_NOT_POISONED(poisoned(0, 0) > poisoned(0, 0)); - EXPECT_NOT_POISONED(poisoned(0U, 0) > poisoned(0U, 0)); - EXPECT_NOT_POISONED(poisoned(0LL, 0LLU) > poisoned(0LL, 0LLU)); - EXPECT_NOT_POISONED(poisoned(0LLU, 0LLU) > poisoned(0LLU, 0LLU)); - EXPECT_POISONED(poisoned(0xFF, 0xFF) > poisoned(0xFF, 0xFF)); - EXPECT_POISONED(poisoned(0xFFFFFFFFU, 0xFFFFFFFFU) > - poisoned(0xFFFFFFFFU, 0xFFFFFFFFU)); - EXPECT_POISONED(poisoned(-1, 0xFFFFFFFFU) > - poisoned(-1, 0xFFFFFFFFU)); - - EXPECT_NOT_POISONED(poisoned(0, 0) >= poisoned(0, 0)); - EXPECT_NOT_POISONED(poisoned(0U, 0) >= poisoned(0U, 0)); - EXPECT_NOT_POISONED(poisoned(0LL, 0LLU) >= poisoned(0LL, 0LLU)); - EXPECT_NOT_POISONED(poisoned(0LLU, 0LLU) >= poisoned(0LLU, 0LLU)); - EXPECT_POISONED(poisoned(0xFF, 0xFF) >= poisoned(0xFF, 0xFF)); - EXPECT_POISONED(poisoned(0xFFFFFFFFU, 0xFFFFFFFFU) >= - poisoned(0xFFFFFFFFU, 0xFFFFFFFFU)); - EXPECT_POISONED(poisoned(-1, 0xFFFFFFFFU) >= - poisoned(-1, 0xFFFFFFFFU)); - - EXPECT_POISONED(poisoned(6, 0xF) > poisoned(7, 0)); - EXPECT_POISONED(poisoned(0xF, 0xF) > poisoned(7, 0)); - - EXPECT_NOT_POISONED(poisoned(-1, 0x80000000U) >= poisoned(-1, 0U)); -} - -#if MSAN_HAS_M128 -TEST(MemorySanitizer, ICmpVectorRelational) { - EXPECT_NOT_POISONED( - _mm_cmplt_epi16(poisoned(_mm_set1_epi16(0), _mm_set1_epi16(0)), - poisoned(_mm_set1_epi16(0), _mm_set1_epi16(0)))); - EXPECT_NOT_POISONED( - _mm_cmplt_epi16(poisoned(_mm_set1_epi32(0), _mm_set1_epi32(0)), - poisoned(_mm_set1_epi32(0), _mm_set1_epi32(0)))); - EXPECT_POISONED( - _mm_cmplt_epi16(poisoned(_mm_set1_epi16(0), _mm_set1_epi16(0xFFFF)), - poisoned(_mm_set1_epi16(0), _mm_set1_epi16(0xFFFF)))); - EXPECT_POISONED(_mm_cmpgt_epi16(poisoned(_mm_set1_epi16(6), _mm_set1_epi16(0xF)), - poisoned(_mm_set1_epi16(7), _mm_set1_epi16(0)))); -} -#endif - -// Volatile bitfield store is implemented as load-mask-store -// Test that we don't warn on the store of (uninitialized) padding. -struct VolatileBitfieldStruct { - volatile unsigned x : 1; - unsigned y : 1; -}; - -TEST(MemorySanitizer, VolatileBitfield) { - VolatileBitfieldStruct *S = new VolatileBitfieldStruct; - S->x = 1; - EXPECT_NOT_POISONED((unsigned)S->x); - EXPECT_POISONED((unsigned)S->y); -} - -TEST(MemorySanitizer, UnalignedLoad) { - char x[32] __attribute__((aligned(8))); - U4 origin = __LINE__; - for (unsigned i = 0; i < sizeof(x) / 4; ++i) - __msan_set_origin(x + 4 * i, 4, origin + i); - - memset(x + 8, 0, 16); - EXPECT_POISONED_O(__sanitizer_unaligned_load16(x + 6), origin + 1); - EXPECT_POISONED_O(__sanitizer_unaligned_load16(x + 7), origin + 1); - EXPECT_NOT_POISONED(__sanitizer_unaligned_load16(x + 8)); - EXPECT_NOT_POISONED(__sanitizer_unaligned_load16(x + 9)); - EXPECT_NOT_POISONED(__sanitizer_unaligned_load16(x + 22)); - EXPECT_POISONED_O(__sanitizer_unaligned_load16(x + 23), origin + 6); - EXPECT_POISONED_O(__sanitizer_unaligned_load16(x + 24), origin + 6); - - EXPECT_POISONED_O(__sanitizer_unaligned_load32(x + 4), origin + 1); - EXPECT_POISONED_O(__sanitizer_unaligned_load32(x + 7), origin + 1); - EXPECT_NOT_POISONED(__sanitizer_unaligned_load32(x + 8)); - EXPECT_NOT_POISONED(__sanitizer_unaligned_load32(x + 9)); - EXPECT_NOT_POISONED(__sanitizer_unaligned_load32(x + 20)); - EXPECT_POISONED_O(__sanitizer_unaligned_load32(x + 21), origin + 6); - EXPECT_POISONED_O(__sanitizer_unaligned_load32(x + 24), origin + 6); - - EXPECT_POISONED_O(__sanitizer_unaligned_load64(x), origin); - EXPECT_POISONED_O(__sanitizer_unaligned_load64(x + 1), origin); - EXPECT_POISONED_O(__sanitizer_unaligned_load64(x + 7), origin + 1); - EXPECT_NOT_POISONED(__sanitizer_unaligned_load64(x + 8)); - EXPECT_NOT_POISONED(__sanitizer_unaligned_load64(x + 9)); - EXPECT_NOT_POISONED(__sanitizer_unaligned_load64(x + 16)); - EXPECT_POISONED_O(__sanitizer_unaligned_load64(x + 17), origin + 6); - EXPECT_POISONED_O(__sanitizer_unaligned_load64(x + 21), origin + 6); - EXPECT_POISONED_O(__sanitizer_unaligned_load64(x + 24), origin + 6); -} - -TEST(MemorySanitizer, UnalignedStore16) { - char x[5] __attribute__((aligned(4))); - U2 y2 = 0; - U4 origin = __LINE__; - __msan_poison(&y2, 1); - __msan_set_origin(&y2, 1, origin); - - __sanitizer_unaligned_store16(x + 1, y2); - EXPECT_POISONED_O(x[0], origin); - EXPECT_POISONED_O(x[1], origin); - EXPECT_NOT_POISONED(x[2]); - EXPECT_POISONED_O(x[3], origin); -} - -TEST(MemorySanitizer, UnalignedStore32) { - char x[8] __attribute__((aligned(4))); - U4 y4 = 0; - U4 origin = __LINE__; - __msan_poison(&y4, 2); - __msan_set_origin(&y4, 2, origin); - - __sanitizer_unaligned_store32(x + 3, y4); - EXPECT_POISONED_O(x[0], origin); - EXPECT_POISONED_O(x[1], origin); - EXPECT_POISONED_O(x[2], origin); - EXPECT_POISONED_O(x[3], origin); - EXPECT_POISONED_O(x[4], origin); - EXPECT_NOT_POISONED(x[5]); - EXPECT_NOT_POISONED(x[6]); - EXPECT_POISONED_O(x[7], origin); -} - -TEST(MemorySanitizer, UnalignedStore64) { - char x[16] __attribute__((aligned(8))); - U8 y8 = 0; - U4 origin = __LINE__; - __msan_poison(&y8, 3); - __msan_poison(((char *)&y8) + sizeof(y8) - 2, 1); - __msan_set_origin(&y8, 8, origin); - - __sanitizer_unaligned_store64(x + 3, y8); - EXPECT_POISONED_O(x[0], origin); - EXPECT_POISONED_O(x[1], origin); - EXPECT_POISONED_O(x[2], origin); - EXPECT_POISONED_O(x[3], origin); - EXPECT_POISONED_O(x[4], origin); - EXPECT_POISONED_O(x[5], origin); - EXPECT_NOT_POISONED(x[6]); - EXPECT_NOT_POISONED(x[7]); - EXPECT_NOT_POISONED(x[8]); - EXPECT_POISONED_O(x[9], origin); - EXPECT_NOT_POISONED(x[10]); - EXPECT_POISONED_O(x[11], origin); -} - -TEST(MemorySanitizer, UnalignedStore16_precise) { - char x[8] __attribute__((aligned(4))); - U2 y = 0; - U4 originx1 = __LINE__; - U4 originx2 = __LINE__; - U4 originy = __LINE__; - __msan_poison(x, sizeof(x)); - __msan_set_origin(x, 4, originx1); - __msan_set_origin(x + 4, 4, originx2); - __msan_poison(((char *)&y) + 1, 1); - __msan_set_origin(&y, sizeof(y), originy); - - __sanitizer_unaligned_store16(x + 3, y); - EXPECT_POISONED_O(x[0], originx1); - EXPECT_POISONED_O(x[1], originx1); - EXPECT_POISONED_O(x[2], originx1); - EXPECT_NOT_POISONED(x[3]); - EXPECT_POISONED_O(x[4], originy); - EXPECT_POISONED_O(x[5], originy); - EXPECT_POISONED_O(x[6], originy); - EXPECT_POISONED_O(x[7], originy); -} - -TEST(MemorySanitizer, UnalignedStore16_precise2) { - char x[8] __attribute__((aligned(4))); - U2 y = 0; - U4 originx1 = __LINE__; - U4 originx2 = __LINE__; - U4 originy = __LINE__; - __msan_poison(x, sizeof(x)); - __msan_set_origin(x, 4, originx1); - __msan_set_origin(x + 4, 4, originx2); - __msan_poison(((char *)&y), 1); - __msan_set_origin(&y, sizeof(y), originy); - - __sanitizer_unaligned_store16(x + 3, y); - EXPECT_POISONED_O(x[0], originy); - EXPECT_POISONED_O(x[1], originy); - EXPECT_POISONED_O(x[2], originy); - EXPECT_POISONED_O(x[3], originy); - EXPECT_NOT_POISONED(x[4]); - EXPECT_POISONED_O(x[5], originx2); - EXPECT_POISONED_O(x[6], originx2); - EXPECT_POISONED_O(x[7], originx2); -} - -TEST(MemorySanitizer, UnalignedStore64_precise) { - char x[12] __attribute__((aligned(8))); - U8 y = 0; - U4 originx1 = __LINE__; - U4 originx2 = __LINE__; - U4 originx3 = __LINE__; - U4 originy = __LINE__; - __msan_poison(x, sizeof(x)); - __msan_set_origin(x, 4, originx1); - __msan_set_origin(x + 4, 4, originx2); - __msan_set_origin(x + 8, 4, originx3); - __msan_poison(((char *)&y) + 1, 1); - __msan_poison(((char *)&y) + 7, 1); - __msan_set_origin(&y, sizeof(y), originy); - - __sanitizer_unaligned_store64(x + 2, y); - EXPECT_POISONED_O(x[0], originy); - EXPECT_POISONED_O(x[1], originy); - EXPECT_NOT_POISONED(x[2]); - EXPECT_POISONED_O(x[3], originy); - - EXPECT_NOT_POISONED(x[4]); - EXPECT_NOT_POISONED(x[5]); - EXPECT_NOT_POISONED(x[6]); - EXPECT_NOT_POISONED(x[7]); - - EXPECT_NOT_POISONED(x[8]); - EXPECT_POISONED_O(x[9], originy); - EXPECT_POISONED_O(x[10], originy); - EXPECT_POISONED_O(x[11], originy); -} - -TEST(MemorySanitizer, UnalignedStore64_precise2) { - char x[12] __attribute__((aligned(8))); - U8 y = 0; - U4 originx1 = __LINE__; - U4 originx2 = __LINE__; - U4 originx3 = __LINE__; - U4 originy = __LINE__; - __msan_poison(x, sizeof(x)); - __msan_set_origin(x, 4, originx1); - __msan_set_origin(x + 4, 4, originx2); - __msan_set_origin(x + 8, 4, originx3); - __msan_poison(((char *)&y) + 3, 3); - __msan_set_origin(&y, sizeof(y), originy); - - __sanitizer_unaligned_store64(x + 2, y); - EXPECT_POISONED_O(x[0], originx1); - EXPECT_POISONED_O(x[1], originx1); - EXPECT_NOT_POISONED(x[2]); - EXPECT_NOT_POISONED(x[3]); - - EXPECT_NOT_POISONED(x[4]); - EXPECT_POISONED_O(x[5], originy); - EXPECT_POISONED_O(x[6], originy); - EXPECT_POISONED_O(x[7], originy); - - EXPECT_NOT_POISONED(x[8]); - EXPECT_NOT_POISONED(x[9]); - EXPECT_POISONED_O(x[10], originx3); - EXPECT_POISONED_O(x[11], originx3); -} - -#if (defined(__x86_64__) && defined(__clang__)) -namespace { -typedef U1 V16x8 __attribute__((__vector_size__(16))); -typedef U2 V8x16 __attribute__((__vector_size__(16))); -typedef U4 V4x32 __attribute__((__vector_size__(16))); -typedef U8 V2x64 __attribute__((__vector_size__(16))); -typedef U4 V8x32 __attribute__((__vector_size__(32))); -typedef U8 V4x64 __attribute__((__vector_size__(32))); -typedef U4 V2x32 __attribute__((__vector_size__(8))); -typedef U2 V4x16 __attribute__((__vector_size__(8))); -typedef U1 V8x8 __attribute__((__vector_size__(8))); - - -V8x16 shift_sse2_left_scalar(V8x16 x, U4 y) { - return _mm_slli_epi16(x, y); -} - -V8x16 shift_sse2_left(V8x16 x, V8x16 y) { - return _mm_sll_epi16(x, y); -} - -TEST(VectorShiftTest, sse2_left_scalar) { - V8x16 v = {Poisoned<U2>(0, 3), Poisoned<U2>(0, 7), 2, 3, 4, 5, 6, 7}; - V8x16 u = shift_sse2_left_scalar(v, 2); - EXPECT_POISONED(u[0]); - EXPECT_POISONED(u[1]); - EXPECT_NOT_POISONED(u[0] | (3U << 2)); - EXPECT_NOT_POISONED(u[1] | (7U << 2)); - u[0] = u[1] = 0; - EXPECT_NOT_POISONED(u); -} - -TEST(VectorShiftTest, sse2_left_scalar_by_uninit) { - V8x16 v = {0, 1, 2, 3, 4, 5, 6, 7}; - V8x16 u = shift_sse2_left_scalar(v, Poisoned<U4>()); - EXPECT_POISONED(u[0]); - EXPECT_POISONED(u[1]); - EXPECT_POISONED(u[2]); - EXPECT_POISONED(u[3]); - EXPECT_POISONED(u[4]); - EXPECT_POISONED(u[5]); - EXPECT_POISONED(u[6]); - EXPECT_POISONED(u[7]); -} - -TEST(VectorShiftTest, sse2_left) { - V8x16 v = {Poisoned<U2>(0, 3), Poisoned<U2>(0, 7), 2, 3, 4, 5, 6, 7}; - // Top 64 bits of shift count don't affect the result. - V2x64 s = {2, Poisoned<U8>()}; - V8x16 u = shift_sse2_left(v, s); - EXPECT_POISONED(u[0]); - EXPECT_POISONED(u[1]); - EXPECT_NOT_POISONED(u[0] | (3U << 2)); - EXPECT_NOT_POISONED(u[1] | (7U << 2)); - u[0] = u[1] = 0; - EXPECT_NOT_POISONED(u); -} - -TEST(VectorShiftTest, sse2_left_by_uninit) { - V8x16 v = {Poisoned<U2>(0, 3), Poisoned<U2>(0, 7), 2, 3, 4, 5, 6, 7}; - V2x64 s = {Poisoned<U8>(), Poisoned<U8>()}; - V8x16 u = shift_sse2_left(v, s); - EXPECT_POISONED(u[0]); - EXPECT_POISONED(u[1]); - EXPECT_POISONED(u[2]); - EXPECT_POISONED(u[3]); - EXPECT_POISONED(u[4]); - EXPECT_POISONED(u[5]); - EXPECT_POISONED(u[6]); - EXPECT_POISONED(u[7]); -} - -#ifdef __AVX2__ -V4x32 shift_avx2_left(V4x32 x, V4x32 y) { - return _mm_sllv_epi32(x, y); -} -// This is variable vector shift that's only available starting with AVX2. -// V4x32 shift_avx2_left(V4x32 x, V4x32 y) { -TEST(VectorShiftTest, avx2_left) { - V4x32 v = {Poisoned<U2>(0, 3), Poisoned<U2>(0, 7), 2, 3}; - V4x32 s = {2, Poisoned<U4>(), 3, Poisoned<U4>()}; - V4x32 u = shift_avx2_left(v, s); - EXPECT_POISONED(u[0]); - EXPECT_NOT_POISONED(u[0] | (~7U)); - EXPECT_POISONED(u[1]); - EXPECT_POISONED(u[1] | (~31U)); - EXPECT_NOT_POISONED(u[2]); - EXPECT_POISONED(u[3]); - EXPECT_POISONED(u[3] | (~31U)); -} -#endif // __AVX2__ -} // namespace - -TEST(VectorPackTest, sse2_packssdw_128) { - const unsigned S2_max = (1 << 15) - 1; - V4x32 a = {Poisoned<U4>(0, 0xFF0000), Poisoned<U4>(0, 0xFFFF0000), - S2_max + 100, 4}; - V4x32 b = {Poisoned<U4>(0, 0xFF), S2_max + 10000, Poisoned<U4>(0, 0xFF00), - S2_max}; - - V8x16 c = _mm_packs_epi32(a, b); - - EXPECT_POISONED(c[0]); - EXPECT_POISONED(c[1]); - EXPECT_NOT_POISONED(c[2]); - EXPECT_NOT_POISONED(c[3]); - EXPECT_POISONED(c[4]); - EXPECT_NOT_POISONED(c[5]); - EXPECT_POISONED(c[6]); - EXPECT_NOT_POISONED(c[7]); - - EXPECT_EQ(c[2], S2_max); - EXPECT_EQ(c[3], 4); - EXPECT_EQ(c[5], S2_max); - EXPECT_EQ(c[7], S2_max); -} - -TEST(VectorPackTest, mmx_packuswb) { - const unsigned U1_max = (1 << 8) - 1; - V4x16 a = {Poisoned<U2>(0, 0xFF00), Poisoned<U2>(0, 0xF000U), U1_max + 100, - 4}; - V4x16 b = {Poisoned<U2>(0, 0xFF), U1_max - 1, Poisoned<U2>(0, 0xF), U1_max}; - V8x8 c = _mm_packs_pu16(a, b); - - EXPECT_POISONED(c[0]); - EXPECT_POISONED(c[1]); - EXPECT_NOT_POISONED(c[2]); - EXPECT_NOT_POISONED(c[3]); - EXPECT_POISONED(c[4]); - EXPECT_NOT_POISONED(c[5]); - EXPECT_POISONED(c[6]); - EXPECT_NOT_POISONED(c[7]); - - EXPECT_EQ(c[2], U1_max); - EXPECT_EQ(c[3], 4); - EXPECT_EQ(c[5], U1_max - 1); - EXPECT_EQ(c[7], U1_max); -} - -TEST(VectorSadTest, sse2_psad_bw) { - V16x8 a = {Poisoned<U1>(), 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}; - V16x8 b = {100, 101, 102, 103, 104, 105, 106, 107, - 108, 109, 110, 111, 112, 113, 114, 115}; - V2x64 c = _mm_sad_epu8(a, b); - - EXPECT_POISONED(c[0]); - EXPECT_NOT_POISONED(c[1]); - - EXPECT_EQ(800U, c[1]); -} - -TEST(VectorMaddTest, mmx_pmadd_wd) { - V4x16 a = {Poisoned<U2>(), 1, 2, 3}; - V4x16 b = {100, 101, 102, 103}; - V2x32 c = _mm_madd_pi16(a, b); - - EXPECT_POISONED(c[0]); - EXPECT_NOT_POISONED(c[1]); - - EXPECT_EQ((unsigned)(2 * 102 + 3 * 103), c[1]); -} -#endif // defined(__clang__) - -TEST(MemorySanitizerOrigins, SetGet) { - EXPECT_EQ(TrackingOrigins(), !!__msan_get_track_origins()); - if (!TrackingOrigins()) return; - int x; - __msan_set_origin(&x, sizeof(x), 1234); - EXPECT_ORIGIN(1234U, __msan_get_origin(&x)); - __msan_set_origin(&x, sizeof(x), 5678); - EXPECT_ORIGIN(5678U, __msan_get_origin(&x)); - __msan_set_origin(&x, sizeof(x), 0); - EXPECT_ORIGIN(0U, __msan_get_origin(&x)); -} - -namespace { -struct S { - U4 dummy; - U2 a; - U2 b; -}; - -TEST(MemorySanitizerOrigins, InitializedStoreDoesNotChangeOrigin) { - if (!TrackingOrigins()) return; - - S s; - U4 origin = rand(); // NOLINT - s.a = *GetPoisonedO<U2>(0, origin); - EXPECT_ORIGIN(origin, __msan_get_origin(&s.a)); - EXPECT_ORIGIN(origin, __msan_get_origin(&s.b)); - - s.b = 42; - EXPECT_ORIGIN(origin, __msan_get_origin(&s.a)); - EXPECT_ORIGIN(origin, __msan_get_origin(&s.b)); -} -} // namespace - -template<class T, class BinaryOp> -INLINE -void BinaryOpOriginTest(BinaryOp op) { - U4 ox = rand(); //NOLINT - U4 oy = rand(); //NOLINT - T *x = GetPoisonedO<T>(0, ox, 0); - T *y = GetPoisonedO<T>(1, oy, 0); - T *z = GetPoisonedO<T>(2, 0, 0); - - *z = op(*x, *y); - U4 origin = __msan_get_origin(z); - EXPECT_POISONED_O(*z, origin); - EXPECT_EQ(true, __msan_origin_is_descendant_or_same(origin, ox) || - __msan_origin_is_descendant_or_same(origin, oy)); - - // y is poisoned, x is not. - *x = 10101; - *y = *GetPoisonedO<T>(1, oy); - break_optimization(x); - __msan_set_origin(z, sizeof(*z), 0); - *z = op(*x, *y); - EXPECT_POISONED_O(*z, oy); - EXPECT_ORIGIN(oy, __msan_get_origin(z)); - - // x is poisoned, y is not. - *x = *GetPoisonedO<T>(0, ox); - *y = 10101010; - break_optimization(y); - __msan_set_origin(z, sizeof(*z), 0); - *z = op(*x, *y); - EXPECT_POISONED_O(*z, ox); - EXPECT_ORIGIN(ox, __msan_get_origin(z)); -} - -template<class T> INLINE T XOR(const T &a, const T&b) { return a ^ b; } -template<class T> INLINE T ADD(const T &a, const T&b) { return a + b; } -template<class T> INLINE T SUB(const T &a, const T&b) { return a - b; } -template<class T> INLINE T MUL(const T &a, const T&b) { return a * b; } -template<class T> INLINE T AND(const T &a, const T&b) { return a & b; } -template<class T> INLINE T OR (const T &a, const T&b) { return a | b; } - -TEST(MemorySanitizerOrigins, BinaryOp) { - if (!TrackingOrigins()) return; - BinaryOpOriginTest<S8>(XOR<S8>); - BinaryOpOriginTest<U8>(ADD<U8>); - BinaryOpOriginTest<S4>(SUB<S4>); - BinaryOpOriginTest<S4>(MUL<S4>); - BinaryOpOriginTest<U4>(OR<U4>); - BinaryOpOriginTest<U4>(AND<U4>); - BinaryOpOriginTest<double>(ADD<U4>); - BinaryOpOriginTest<float>(ADD<S4>); - BinaryOpOriginTest<double>(ADD<double>); - BinaryOpOriginTest<float>(ADD<double>); -} - -TEST(MemorySanitizerOrigins, Unary) { - if (!TrackingOrigins()) return; - EXPECT_POISONED_O(*GetPoisonedO<S8>(0, __LINE__), __LINE__); - EXPECT_POISONED_O(*GetPoisonedO<S8>(0, __LINE__), __LINE__); - EXPECT_POISONED_O(*GetPoisonedO<S8>(0, __LINE__), __LINE__); - EXPECT_POISONED_O(*GetPoisonedO<S8>(0, __LINE__), __LINE__); - - EXPECT_POISONED_O(*GetPoisonedO<S4>(0, __LINE__), __LINE__); - EXPECT_POISONED_O(*GetPoisonedO<S4>(0, __LINE__), __LINE__); - EXPECT_POISONED_O(*GetPoisonedO<S4>(0, __LINE__), __LINE__); - EXPECT_POISONED_O(*GetPoisonedO<S4>(0, __LINE__), __LINE__); - - EXPECT_POISONED_O(*GetPoisonedO<U4>(0, __LINE__), __LINE__); - EXPECT_POISONED_O(*GetPoisonedO<U4>(0, __LINE__), __LINE__); - EXPECT_POISONED_O(*GetPoisonedO<U4>(0, __LINE__), __LINE__); - EXPECT_POISONED_O(*GetPoisonedO<U4>(0, __LINE__), __LINE__); - - EXPECT_POISONED_O(*GetPoisonedO<S4>(0, __LINE__), __LINE__); - EXPECT_POISONED_O(*GetPoisonedO<S4>(0, __LINE__), __LINE__); - EXPECT_POISONED_O(*GetPoisonedO<S4>(0, __LINE__), __LINE__); - EXPECT_POISONED_O(*GetPoisonedO<S4>(0, __LINE__), __LINE__); - - EXPECT_POISONED_O((void*)*GetPoisonedO<S8>(0, __LINE__), __LINE__); - EXPECT_POISONED_O((U8)*GetPoisonedO<void*>(0, __LINE__), __LINE__); -} - -TEST(MemorySanitizerOrigins, EQ) { - if (!TrackingOrigins()) return; - EXPECT_POISONED_O(*GetPoisonedO<S4>(0, __LINE__) <= 11, __LINE__); - EXPECT_POISONED_O(*GetPoisonedO<S4>(0, __LINE__) == 11, __LINE__); - EXPECT_POISONED_O(*GetPoisonedO<float>(0, __LINE__) == 1.1, __LINE__); -} - -TEST(MemorySanitizerOrigins, DIV) { - if (!TrackingOrigins()) return; - EXPECT_POISONED_O(*GetPoisonedO<U8>(0, __LINE__) / 100, __LINE__); - unsigned o = __LINE__; - EXPECT_UMR_O(volatile unsigned y = 100 / *GetPoisonedO<S4>(0, o, 1), o); -} - -TEST(MemorySanitizerOrigins, SHIFT) { - if (!TrackingOrigins()) return; - EXPECT_POISONED_O(*GetPoisonedO<U8>(0, __LINE__) >> 10, __LINE__); - EXPECT_POISONED_O(*GetPoisonedO<S8>(0, __LINE__) >> 10, __LINE__); - EXPECT_POISONED_O(*GetPoisonedO<S8>(0, __LINE__) << 10, __LINE__); - EXPECT_POISONED_O(10U << *GetPoisonedO<U8>(0, __LINE__), __LINE__); - EXPECT_POISONED_O(-10 >> *GetPoisonedO<S8>(0, __LINE__), __LINE__); - EXPECT_POISONED_O(-10 << *GetPoisonedO<S8>(0, __LINE__), __LINE__); -} - -template<class T, int N> -void MemCpyTest() { - int ox = __LINE__; - T *x = new T[N]; - T *y = new T[N]; - T *z = new T[N]; - T *q = new T[N]; - __msan_poison(x, N * sizeof(T)); - __msan_set_origin(x, N * sizeof(T), ox); - __msan_set_origin(y, N * sizeof(T), 777777); - __msan_set_origin(z, N * sizeof(T), 888888); - EXPECT_NOT_POISONED(x); - memcpy(y, x, N * sizeof(T)); - EXPECT_POISONED_O(y[0], ox); - EXPECT_POISONED_O(y[N/2], ox); - EXPECT_POISONED_O(y[N-1], ox); - EXPECT_NOT_POISONED(x); - void *res = mempcpy(q, x, N * sizeof(T)); - ASSERT_EQ(q + N, res); - EXPECT_POISONED_O(q[0], ox); - EXPECT_POISONED_O(q[N/2], ox); - EXPECT_POISONED_O(q[N-1], ox); - EXPECT_NOT_POISONED(x); - memmove(z, x, N * sizeof(T)); - EXPECT_POISONED_O(z[0], ox); - EXPECT_POISONED_O(z[N/2], ox); - EXPECT_POISONED_O(z[N-1], ox); -} - -TEST(MemorySanitizerOrigins, LargeMemCpy) { - if (!TrackingOrigins()) return; - MemCpyTest<U1, 10000>(); - MemCpyTest<U8, 10000>(); -} - -TEST(MemorySanitizerOrigins, SmallMemCpy) { - if (!TrackingOrigins()) return; - MemCpyTest<U8, 1>(); - MemCpyTest<U8, 2>(); - MemCpyTest<U8, 3>(); -} - -TEST(MemorySanitizerOrigins, Select) { - if (!TrackingOrigins()) return; - EXPECT_NOT_POISONED(g_one ? 1 : *GetPoisonedO<S4>(0, __LINE__)); - EXPECT_POISONED_O(*GetPoisonedO<S4>(0, __LINE__), __LINE__); - S4 x; - break_optimization(&x); - x = g_1 ? *GetPoisonedO<S4>(0, __LINE__) : 0; - - EXPECT_POISONED_O(g_1 ? *GetPoisonedO<S4>(0, __LINE__) : 1, __LINE__); - EXPECT_POISONED_O(g_0 ? 1 : *GetPoisonedO<S4>(0, __LINE__), __LINE__); -} - -NOINLINE int RetvalOriginTest(U4 origin) { - int *a = new int; - break_optimization(a); - __msan_set_origin(a, sizeof(*a), origin); - int res = *a; - delete a; - return res; -} - -TEST(MemorySanitizerOrigins, Retval) { - if (!TrackingOrigins()) return; - EXPECT_POISONED_O(RetvalOriginTest(__LINE__), __LINE__); -} - -NOINLINE void ParamOriginTest(int param, U4 origin) { - EXPECT_POISONED_O(param, origin); -} - -TEST(MemorySanitizerOrigins, Param) { - if (!TrackingOrigins()) return; - int *a = new int; - U4 origin = __LINE__; - break_optimization(a); - __msan_set_origin(a, sizeof(*a), origin); - ParamOriginTest(*a, origin); - delete a; -} - -TEST(MemorySanitizerOrigins, Invoke) { - if (!TrackingOrigins()) return; - StructWithDtor s; // Will cause the calls to become invokes. - EXPECT_POISONED_O(RetvalOriginTest(__LINE__), __LINE__); -} - -TEST(MemorySanitizerOrigins, strlen) { - S8 alignment; - break_optimization(&alignment); - char x[4] = {'a', 'b', 0, 0}; - __msan_poison(&x[2], 1); - U4 origin = __LINE__; - __msan_set_origin(x, sizeof(x), origin); - EXPECT_UMR_O(volatile unsigned y = strlen(x), origin); -} - -TEST(MemorySanitizerOrigins, wcslen) { - wchar_t w[3] = {'a', 'b', 0}; - U4 origin = __LINE__; - __msan_set_origin(w, sizeof(w), origin); - __msan_poison(&w[2], sizeof(wchar_t)); - EXPECT_UMR_O(volatile unsigned y = wcslen(w), origin); -} - -#if MSAN_HAS_M128 -TEST(MemorySanitizerOrigins, StoreIntrinsic) { - __m128 x, y; - U4 origin = __LINE__; - __msan_set_origin(&x, sizeof(x), origin); - __msan_poison(&x, sizeof(x)); - __builtin_ia32_storeups((float*)&y, x); - EXPECT_POISONED_O(y, origin); -} -#endif - -NOINLINE void RecursiveMalloc(int depth) { - static int count; - count++; - if ((count % (1024 * 1024)) == 0) - printf("RecursiveMalloc: %d\n", count); - int *x1 = new int; - int *x2 = new int; - break_optimization(x1); - break_optimization(x2); - if (depth > 0) { - RecursiveMalloc(depth-1); - RecursiveMalloc(depth-1); - } - delete x1; - delete x2; -} - -TEST(MemorySanitizer, Select) { - int x; - int volatile* p = &x; - int z = *p ? 1 : 0; - EXPECT_POISONED(z); -} - -TEST(MemorySanitizer, SelectPartial) { - // Precise instrumentation of select. - // Some bits of the result do not depend on select condition, and must stay - // initialized even if select condition is not. These are the bits that are - // equal and initialized in both left and right select arguments. - U4 x = 0xFFFFABCDU; - U4 x_s = 0xFFFF0000U; - __msan_partial_poison(&x, &x_s, sizeof(x)); - U4 y = 0xAB00U; - U1 cond = true; - __msan_poison(&cond, sizeof(cond)); - U4 z = cond ? x : y; - __msan_print_shadow(&z, sizeof(z)); - EXPECT_POISONED(z & 0xFFU); - EXPECT_NOT_POISONED(z & 0xFF00U); - EXPECT_POISONED(z & 0xFF0000U); - EXPECT_POISONED(z & 0xFF000000U); - EXPECT_EQ(0xAB00U, z & 0xFF00U); -} - -TEST(MemorySanitizerStress, DISABLED_MallocStackTrace) { - RecursiveMalloc(22); -} - -TEST(MemorySanitizerAllocator, get_estimated_allocated_size) { - size_t sizes[] = {0, 20, 5000, 1<<20}; - for (size_t i = 0; i < sizeof(sizes) / sizeof(*sizes); ++i) { - size_t alloc_size = __sanitizer_get_estimated_allocated_size(sizes[i]); - EXPECT_EQ(alloc_size, sizes[i]); - } -} - -TEST(MemorySanitizerAllocator, get_allocated_size_and_ownership) { - char *array = reinterpret_cast<char*>(malloc(100)); - int *int_ptr = new int; - - EXPECT_TRUE(__sanitizer_get_ownership(array)); - EXPECT_EQ(100U, __sanitizer_get_allocated_size(array)); - - EXPECT_TRUE(__sanitizer_get_ownership(int_ptr)); - EXPECT_EQ(sizeof(*int_ptr), __sanitizer_get_allocated_size(int_ptr)); - - void *wild_addr = reinterpret_cast<void*>(0x1); - EXPECT_FALSE(__sanitizer_get_ownership(wild_addr)); - EXPECT_EQ(0U, __sanitizer_get_allocated_size(wild_addr)); - - EXPECT_FALSE(__sanitizer_get_ownership(array + 50)); - EXPECT_EQ(0U, __sanitizer_get_allocated_size(array + 50)); - - // NULL is a valid argument for GetAllocatedSize but is not owned. - EXPECT_FALSE(__sanitizer_get_ownership(NULL)); - EXPECT_EQ(0U, __sanitizer_get_allocated_size(NULL)); - - free(array); - EXPECT_FALSE(__sanitizer_get_ownership(array)); - EXPECT_EQ(0U, __sanitizer_get_allocated_size(array)); - - delete int_ptr; -} - -TEST(MemorySanitizer, MlockTest) { - EXPECT_EQ(0, mlockall(MCL_CURRENT)); - EXPECT_EQ(0, mlock((void*)0x12345, 0x5678)); - EXPECT_EQ(0, munlockall()); - EXPECT_EQ(0, munlock((void*)0x987, 0x654)); -} - -// Test that LargeAllocator unpoisons memory before releasing it to the OS. -TEST(MemorySanitizer, LargeAllocatorUnpoisonsOnFree) { - void *p = malloc(1024 * 1024); - free(p); - - typedef void *(*mmap_fn)(void *, size_t, int, int, int, off_t); - mmap_fn real_mmap = (mmap_fn)dlsym(RTLD_NEXT, "mmap"); - - // Allocate the page that was released to the OS in free() with the real mmap, - // bypassing the interceptor. - char *q = (char *)real_mmap(p, 4096, PROT_READ | PROT_WRITE, - MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); - ASSERT_NE((char *)0, q); - - ASSERT_TRUE(q <= p); - ASSERT_TRUE(q + 4096 > p); - - EXPECT_NOT_POISONED(q[0]); - EXPECT_NOT_POISONED(q[10]); - EXPECT_NOT_POISONED(q[100]); - - munmap(q, 4096); -} - -#if SANITIZER_TEST_HAS_MALLOC_USABLE_SIZE -TEST(MemorySanitizer, MallocUsableSizeTest) { - const size_t kArraySize = 100; - char *array = Ident((char*)malloc(kArraySize)); - int *int_ptr = Ident(new int); - EXPECT_EQ(0U, malloc_usable_size(NULL)); - EXPECT_EQ(kArraySize, malloc_usable_size(array)); - EXPECT_EQ(sizeof(int), malloc_usable_size(int_ptr)); - free(array); - delete int_ptr; -} -#endif // SANITIZER_TEST_HAS_MALLOC_USABLE_SIZE diff --git a/contrib/compiler-rt/lib/msan/tests/msan_test_config.h b/contrib/compiler-rt/lib/msan/tests/msan_test_config.h deleted file mode 100644 index 5404c434d09f..000000000000 --- a/contrib/compiler-rt/lib/msan/tests/msan_test_config.h +++ /dev/null @@ -1,20 +0,0 @@ -//===-- msan_test_config.h ----------------------------------------*- C++ -*-===// -// -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. -// -//===----------------------------------------------------------------------===// -// -// This file is a part of MemorySanitizer. -// -// MemorySanitizer unit tests. -//===----------------------------------------------------------------------===// - -#ifndef MSAN_TEST_CONFIG_H -#define MSAN_TEST_CONFIG_H - -#include "gtest/gtest.h" - -#endif // MSAN_TEST_CONFIG_H diff --git a/contrib/compiler-rt/lib/msan/tests/msan_test_main.cc b/contrib/compiler-rt/lib/msan/tests/msan_test_main.cc deleted file mode 100644 index c8c5fefb19f5..000000000000 --- a/contrib/compiler-rt/lib/msan/tests/msan_test_main.cc +++ /dev/null @@ -1,21 +0,0 @@ -//===-- msan_test_main.cc -------------------------------------------------===// -// -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. -// -//===----------------------------------------------------------------------===// -// -// This file is a part of MemorySanitizer. -// -//===----------------------------------------------------------------------===// -#ifndef MSAN_EXTERNAL_TEST_CONFIG -#include "msan_test_config.h" -#endif // MSAN_EXTERNAL_TEST_CONFIG - -int main(int argc, char **argv) { - testing::GTEST_FLAG(death_test_style) = "threadsafe"; - testing::InitGoogleTest(&argc, argv); - return RUN_ALL_TESTS(); -} diff --git a/contrib/compiler-rt/lib/profile/GCDAProfiling.c b/contrib/compiler-rt/lib/profile/GCDAProfiling.c index aec232856e74..2338761ae1ab 100644 --- a/contrib/compiler-rt/lib/profile/GCDAProfiling.c +++ b/contrib/compiler-rt/lib/profile/GCDAProfiling.c @@ -27,8 +27,13 @@ #include <stdio.h> #include <stdlib.h> #include <string.h> + +#if defined(_WIN32) +#include "WindowsMMap.h" +#else #include <sys/mman.h> #include <sys/file.h> +#endif #define I386_FREEBSD (defined(__FreeBSD__) && defined(__i386__)) @@ -37,6 +42,7 @@ #endif #if defined(_MSC_VER) +typedef unsigned char uint8_t; typedef unsigned int uint32_t; typedef unsigned long long uint64_t; #elif I386_FREEBSD diff --git a/contrib/compiler-rt/lib/profile/InstrProfData.inc b/contrib/compiler-rt/lib/profile/InstrProfData.inc new file mode 100644 index 000000000000..33c7d94aea2a --- /dev/null +++ b/contrib/compiler-rt/lib/profile/InstrProfData.inc @@ -0,0 +1,767 @@ +/*===-- InstrProfData.inc - instr profiling runtime structures -*- C++ -*-=== *\ +|* +|* The LLVM Compiler Infrastructure +|* +|* This file is distributed under the University of Illinois Open Source +|* License. See LICENSE.TXT for details. +|* +\*===----------------------------------------------------------------------===*/ +/* + * This is the master file that defines all the data structure, signature, + * constant literals that are shared across profiling runtime library, + * compiler (instrumentation), and host tools (reader/writer). The entities + * defined in this file affect the profile runtime ABI, the raw profile format, + * or both. + * + * The file has two identical copies. The master copy lives in LLVM and + * the other one sits in compiler-rt/lib/profile directory. To make changes + * in this file, first modify the master copy and copy it over to compiler-rt. + * Testing of any change in this file can start only after the two copies are + * synced up. + * + * The first part of the file includes macros that defines types, names, and + * initializers for the member fields of the core data structures. The field + * declarations for one structure is enabled by defining the field activation + * macro associated with that structure. Only one field activation record + * can be defined at one time and the rest definitions will be filtered out by + * the preprocessor. + * + * Examples of how the template is used to instantiate structure definition: + * 1. To declare a structure: + * + * struct ProfData { + * #define INSTR_PROF_DATA(Type, LLVMType, Name, Initializer) \ + * Type Name; + * #include "llvm/ProfileData/InstrProfData.inc" + * }; + * + * 2. To construct LLVM type arrays for the struct type: + * + * Type *DataTypes[] = { + * #define INSTR_PROF_DATA(Type, LLVMType, Name, Initializer) \ + * LLVMType, + * #include "llvm/ProfileData/InstrProfData.inc" + * }; + * + * 4. To construct constant array for the initializers: + * #define INSTR_PROF_DATA(Type, LLVMType, Name, Initializer) \ + * Initializer, + * Constant *ConstantVals[] = { + * #include "llvm/ProfileData/InstrProfData.inc" + * }; + * + * + * The second part of the file includes definitions all other entities that + * are related to runtime ABI and format. When no field activation macro is + * defined, this file can be included to introduce the definitions. + * +\*===----------------------------------------------------------------------===*/ + +/* INSTR_PROF_DATA start. */ +/* Definition of member fields of the per-function control structure. */ +#ifndef INSTR_PROF_DATA +#define INSTR_PROF_DATA(Type, LLVMType, Name, Initializer) +#else +#define INSTR_PROF_DATA_DEFINED +#endif + +INSTR_PROF_DATA(const uint32_t, llvm::Type::getInt32Ty(Ctx), NameSize, \ + ConstantInt::get(llvm::Type::getInt32Ty(Ctx), \ + NamePtr->getType()->getPointerElementType()->getArrayNumElements())) +INSTR_PROF_DATA(const uint32_t, llvm::Type::getInt32Ty(Ctx), NumCounters, \ + ConstantInt::get(llvm::Type::getInt32Ty(Ctx), NumCounters)) +INSTR_PROF_DATA(const uint64_t, llvm::Type::getInt64Ty(Ctx), FuncHash, \ + ConstantInt::get(llvm::Type::getInt64Ty(Ctx), \ + Inc->getHash()->getZExtValue())) +INSTR_PROF_DATA(const IntPtrT, llvm::Type::getInt8PtrTy(Ctx), NamePtr, \ + ConstantExpr::getBitCast(NamePtr, llvm::Type::getInt8PtrTy(Ctx))) +INSTR_PROF_DATA(const IntPtrT, llvm::Type::getInt64PtrTy(Ctx), CounterPtr, \ + ConstantExpr::getBitCast(CounterPtr, \ + llvm::Type::getInt64PtrTy(Ctx))) +INSTR_PROF_DATA(const IntPtrT, llvm::Type::getInt8PtrTy(Ctx), FunctionPointer, \ + FunctionAddr) +INSTR_PROF_DATA(IntPtrT, llvm::Type::getInt8PtrTy(Ctx), Values, \ + ConstantPointerNull::get(Int8PtrTy)) +INSTR_PROF_DATA(const uint16_t, Int16ArrayTy, NumValueSites[IPVK_Last+1], \ + ConstantArray::get(Int16ArrayTy, Int16ArrayVals)) +#undef INSTR_PROF_DATA +/* INSTR_PROF_DATA end. */ + +/* INSTR_PROF_RAW_HEADER start */ +/* Definition of member fields of the raw profile header data structure. */ +#ifndef INSTR_PROF_RAW_HEADER +#define INSTR_PROF_RAW_HEADER(Type, Name, Initializer) +#else +#define INSTR_PROF_DATA_DEFINED +#endif +INSTR_PROF_RAW_HEADER(uint64_t, Magic, __llvm_profile_get_magic()) +INSTR_PROF_RAW_HEADER(uint64_t, Version, __llvm_profile_get_version()) +INSTR_PROF_RAW_HEADER(uint64_t, DataSize, DataSize) +INSTR_PROF_RAW_HEADER(uint64_t, CountersSize, CountersSize) +INSTR_PROF_RAW_HEADER(uint64_t, NamesSize, NamesSize) +INSTR_PROF_RAW_HEADER(uint64_t, CountersDelta, (uintptr_t)CountersBegin) +INSTR_PROF_RAW_HEADER(uint64_t, NamesDelta, (uintptr_t)NamesBegin) +INSTR_PROF_RAW_HEADER(uint64_t, ValueKindLast, IPVK_Last) +INSTR_PROF_RAW_HEADER(uint64_t, ValueDataSize, ValueDataSize) +INSTR_PROF_RAW_HEADER(uint64_t, ValueDataDelta, (uintptr_t)ValueDataBegin) +#undef INSTR_PROF_RAW_HEADER +/* INSTR_PROF_RAW_HEADER end */ + +/* VALUE_PROF_FUNC_PARAM start */ +/* Definition of parameter types of the runtime API used to do value profiling + * for a given value site. + */ +#ifndef VALUE_PROF_FUNC_PARAM +#define VALUE_PROF_FUNC_PARAM(ArgType, ArgName, ArgLLVMType) +#define INSTR_PROF_COMMA +#else +#define INSTR_PROF_DATA_DEFINED +#define INSTR_PROF_COMMA , +#endif +VALUE_PROF_FUNC_PARAM(uint64_t, TargetValue, Type::getInt64Ty(Ctx)) \ + INSTR_PROF_COMMA +VALUE_PROF_FUNC_PARAM(void *, Data, Type::getInt8PtrTy(Ctx)) INSTR_PROF_COMMA +VALUE_PROF_FUNC_PARAM(uint32_t, CounterIndex, Type::getInt32Ty(Ctx)) +#undef VALUE_PROF_FUNC_PARAM +#undef INSTR_PROF_COMMA +/* VALUE_PROF_FUNC_PARAM end */ + +/* VALUE_PROF_KIND start */ +#ifndef VALUE_PROF_KIND +#define VALUE_PROF_KIND(Enumerator, Value) +#else +#define INSTR_PROF_DATA_DEFINED +#endif +VALUE_PROF_KIND(IPVK_IndirectCallTarget, 0) +/* These two kinds must be the last to be + * declared. This is to make sure the string + * array created with the template can be + * indexed with the kind value. + */ +VALUE_PROF_KIND(IPVK_First, IPVK_IndirectCallTarget) +VALUE_PROF_KIND(IPVK_Last, IPVK_IndirectCallTarget) + +#undef VALUE_PROF_KIND +/* VALUE_PROF_KIND end */ + +/* COVMAP_FUNC_RECORD start */ +/* Definition of member fields of the function record structure in coverage + * map. + */ +#ifndef COVMAP_FUNC_RECORD +#define COVMAP_FUNC_RECORD(Type, LLVMType, Name, Initializer) +#else +#define INSTR_PROF_DATA_DEFINED +#endif +COVMAP_FUNC_RECORD(const IntPtrT, llvm::Type::getInt8PtrTy(Ctx), \ + NamePtr, llvm::ConstantExpr::getBitCast(NamePtr, \ + llvm::Type::getInt8PtrTy(Ctx))) +COVMAP_FUNC_RECORD(const uint32_t, llvm::Type::getInt32Ty(Ctx), NameSize, \ + llvm::ConstantInt::get(llvm::Type::getInt32Ty(Ctx),\ + NameValue.size())) +COVMAP_FUNC_RECORD(const uint32_t, llvm::Type::getInt32Ty(Ctx), DataSize, \ + llvm::ConstantInt::get(llvm::Type::getInt32Ty(Ctx),\ + CoverageMapping.size())) +COVMAP_FUNC_RECORD(const uint64_t, llvm::Type::getInt64Ty(Ctx), FuncHash, \ + llvm::ConstantInt::get(llvm::Type::getInt64Ty(Ctx), FuncHash)) +#undef COVMAP_FUNC_RECORD +/* COVMAP_FUNC_RECORD end. */ + +/* COVMAP_HEADER start */ +/* Definition of member fields of coverage map header. + */ +#ifndef COVMAP_HEADER +#define COVMAP_HEADER(Type, LLVMType, Name, Initializer) +#else +#define INSTR_PROF_DATA_DEFINED +#endif +COVMAP_HEADER(uint32_t, Int32Ty, NRecords, \ + llvm::ConstantInt::get(Int32Ty, FunctionRecords.size())) +COVMAP_HEADER(uint32_t, Int32Ty, FilenamesSize, \ + llvm::ConstantInt::get(Int32Ty, FilenamesSize)) +COVMAP_HEADER(uint32_t, Int32Ty, CoverageSize, \ + llvm::ConstantInt::get(Int32Ty, CoverageMappingSize)) +COVMAP_HEADER(uint32_t, Int32Ty, Version, \ + llvm::ConstantInt::get(Int32Ty, CoverageMappingCurrentVersion)) +#undef COVMAP_HEADER +/* COVMAP_HEADER end. */ + + +#ifdef INSTR_PROF_VALUE_PROF_DATA +#define INSTR_PROF_DATA_DEFINED + +#define INSTR_PROF_MAX_NUM_VAL_PER_SITE 255 +/*! + * This is the header of the data structure that defines the on-disk + * layout of the value profile data of a particular kind for one function. + */ +typedef struct ValueProfRecord { + /* The kind of the value profile record. */ + uint32_t Kind; + /* + * The number of value profile sites. It is guaranteed to be non-zero; + * otherwise the record for this kind won't be emitted. + */ + uint32_t NumValueSites; + /* + * The first element of the array that stores the number of profiled + * values for each value site. The size of the array is NumValueSites. + * Since NumValueSites is greater than zero, there is at least one + * element in the array. + */ + uint8_t SiteCountArray[1]; + + /* + * The fake declaration is for documentation purpose only. + * Align the start of next field to be on 8 byte boundaries. + uint8_t Padding[X]; + */ + + /* The array of value profile data. The size of the array is the sum + * of all elements in SiteCountArray[]. + InstrProfValueData ValueData[]; + */ + +#ifdef __cplusplus + /*! + * \brief Return the number of value sites. + */ + uint32_t getNumValueSites() const { return NumValueSites; } + /*! + * \brief Read data from this record and save it to Record. + */ + void deserializeTo(InstrProfRecord &Record, + InstrProfRecord::ValueMapType *VMap); + /* + * In-place byte swap: + * Do byte swap for this instance. \c Old is the original order before + * the swap, and \c New is the New byte order. + */ + void swapBytes(support::endianness Old, support::endianness New); +#endif +} ValueProfRecord; + +/*! + * Per-function header/control data structure for value profiling + * data in indexed format. + */ +typedef struct ValueProfData { + /* + * Total size in bytes including this field. It must be a multiple + * of sizeof(uint64_t). + */ + uint32_t TotalSize; + /* + *The number of value profile kinds that has value profile data. + * In this implementation, a value profile kind is considered to + * have profile data if the number of value profile sites for the + * kind is not zero. More aggressively, the implementation can + * choose to check the actual data value: if none of the value sites + * has any profiled values, the kind can be skipped. + */ + uint32_t NumValueKinds; + + /* + * Following are a sequence of variable length records. The prefix/header + * of each record is defined by ValueProfRecord type. The number of + * records is NumValueKinds. + * ValueProfRecord Record_1; + * ValueProfRecord Record_N; + */ + +#if __cplusplus + /*! + * Return the total size in bytes of the on-disk value profile data + * given the data stored in Record. + */ + static uint32_t getSize(const InstrProfRecord &Record); + /*! + * Return a pointer to \c ValueProfData instance ready to be streamed. + */ + static std::unique_ptr<ValueProfData> + serializeFrom(const InstrProfRecord &Record); + /*! + * Check the integrity of the record. Return the error code when + * an error is detected, otherwise return instrprof_error::success. + */ + instrprof_error checkIntegrity(); + /*! + * Return a pointer to \c ValueProfileData instance ready to be read. + * All data in the instance are properly byte swapped. The input + * data is assumed to be in little endian order. + */ + static ErrorOr<std::unique_ptr<ValueProfData>> + getValueProfData(const unsigned char *SrcBuffer, + const unsigned char *const SrcBufferEnd, + support::endianness SrcDataEndianness); + /*! + * Swap byte order from \c Endianness order to host byte order. + */ + void swapBytesToHost(support::endianness Endianness); + /*! + * Swap byte order from host byte order to \c Endianness order. + */ + void swapBytesFromHost(support::endianness Endianness); + /*! + * Return the total size of \c ValueProfileData. + */ + uint32_t getSize() const { return TotalSize; } + /*! + * Read data from this data and save it to \c Record. + */ + void deserializeTo(InstrProfRecord &Record, + InstrProfRecord::ValueMapType *VMap); + void operator delete(void *ptr) { ::operator delete(ptr); } +#endif +} ValueProfData; + +/* + * The closure is designed to abstact away two types of value profile data: + * - InstrProfRecord which is the primary data structure used to + * represent profile data in host tools (reader, writer, and profile-use) + * - value profile runtime data structure suitable to be used by C + * runtime library. + * + * Both sources of data need to serialize to disk/memory-buffer in common + * format: ValueProfData. The abstraction allows compiler-rt's raw profiler + * writer to share the same format and code with indexed profile writer. + * + * For documentation of the member methods below, refer to corresponding methods + * in class InstrProfRecord. + */ +typedef struct ValueProfRecordClosure { + const void *Record; + uint32_t (*GetNumValueKinds)(const void *Record); + uint32_t (*GetNumValueSites)(const void *Record, uint32_t VKind); + uint32_t (*GetNumValueData)(const void *Record, uint32_t VKind); + uint32_t (*GetNumValueDataForSite)(const void *R, uint32_t VK, uint32_t S); + + /* + * After extracting the value profile data from the value profile record, + * this method is used to map the in-memory value to on-disk value. If + * the method is null, value will be written out untranslated. + */ + uint64_t (*RemapValueData)(uint32_t, uint64_t Value); + void (*GetValueForSite)(const void *R, InstrProfValueData *Dst, uint32_t K, + uint32_t S, uint64_t (*Mapper)(uint32_t, uint64_t)); + ValueProfData *(*AllocValueProfData)(size_t TotalSizeInBytes); +} ValueProfRecordClosure; + +/* + * A wrapper struct that represents value profile runtime data. + * Like InstrProfRecord class which is used by profiling host tools, + * ValueProfRuntimeRecord also implements the abstract intefaces defined in + * ValueProfRecordClosure so that the runtime data can be serialized using + * shared C implementation. In this structure, NumValueSites and Nodes + * members are the primary fields while other fields hold the derived + * information for fast implementation of closure interfaces. + */ +typedef struct ValueProfRuntimeRecord { + /* Number of sites for each value profile kind. */ + const uint16_t *NumValueSites; + /* An array of linked-list headers. The size of of the array is the + * total number of value profile sites : sum(NumValueSites[*])). Each + * linked-list stores the values profiled for a value profile site. */ + ValueProfNode **Nodes; + + /* Total number of value profile kinds which have at least one + * value profile sites. */ + uint32_t NumValueKinds; + /* An array recording the number of values tracked at each site. + * The size of the array is TotalNumValueSites. */ + uint8_t *SiteCountArray[IPVK_Last + 1]; + ValueProfNode **NodesKind[IPVK_Last + 1]; +} ValueProfRuntimeRecord; + +/* Forward declarations of C interfaces. */ +int initializeValueProfRuntimeRecord(ValueProfRuntimeRecord *RuntimeRecord, + const uint16_t *NumValueSites, + ValueProfNode **Nodes); +void finalizeValueProfRuntimeRecord(ValueProfRuntimeRecord *RuntimeRecord); +uint32_t getValueProfDataSizeRT(const ValueProfRuntimeRecord *Record); +ValueProfData * +serializeValueProfDataFromRT(const ValueProfRuntimeRecord *Record, + ValueProfData *Dst); +uint32_t getNumValueKindsRT(const void *R); + +#undef INSTR_PROF_VALUE_PROF_DATA +#endif /* INSTR_PROF_VALUE_PROF_DATA */ + + +#ifdef INSTR_PROF_COMMON_API_IMPL +#define INSTR_PROF_DATA_DEFINED +#ifdef __cplusplus +#define INSTR_PROF_INLINE inline +#else +#define INSTR_PROF_INLINE +#endif + +#ifndef offsetof +#define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER) +#endif + +/*! + * \brief Return the \c ValueProfRecord header size including the + * padding bytes. + */ +INSTR_PROF_INLINE +uint32_t getValueProfRecordHeaderSize(uint32_t NumValueSites) { + uint32_t Size = offsetof(ValueProfRecord, SiteCountArray) + + sizeof(uint8_t) * NumValueSites; + /* Round the size to multiple of 8 bytes. */ + Size = (Size + 7) & ~7; + return Size; +} + +/*! + * \brief Return the total size of the value profile record including the + * header and the value data. + */ +INSTR_PROF_INLINE +uint32_t getValueProfRecordSize(uint32_t NumValueSites, + uint32_t NumValueData) { + return getValueProfRecordHeaderSize(NumValueSites) + + sizeof(InstrProfValueData) * NumValueData; +} + +/*! + * \brief Return the pointer to the start of value data array. + */ +INSTR_PROF_INLINE +InstrProfValueData *getValueProfRecordValueData(ValueProfRecord *This) { + return (InstrProfValueData *)((char *)This + getValueProfRecordHeaderSize( + This->NumValueSites)); +} + +/*! + * \brief Return the total number of value data for \c This record. + */ +INSTR_PROF_INLINE +uint32_t getValueProfRecordNumValueData(ValueProfRecord *This) { + uint32_t NumValueData = 0; + uint32_t I; + for (I = 0; I < This->NumValueSites; I++) + NumValueData += This->SiteCountArray[I]; + return NumValueData; +} + +/*! + * \brief Use this method to advance to the next \c This \c ValueProfRecord. + */ +INSTR_PROF_INLINE +ValueProfRecord *getValueProfRecordNext(ValueProfRecord *This) { + uint32_t NumValueData = getValueProfRecordNumValueData(This); + return (ValueProfRecord *)((char *)This + + getValueProfRecordSize(This->NumValueSites, + NumValueData)); +} + +/*! + * \brief Return the first \c ValueProfRecord instance. + */ +INSTR_PROF_INLINE +ValueProfRecord *getFirstValueProfRecord(ValueProfData *This) { + return (ValueProfRecord *)((char *)This + sizeof(ValueProfData)); +} + +/* Closure based interfaces. */ + +/*! + * Return the total size in bytes of the on-disk value profile data + * given the data stored in Record. + */ +uint32_t getValueProfDataSize(ValueProfRecordClosure *Closure) { + uint32_t Kind; + uint32_t TotalSize = sizeof(ValueProfData); + const void *Record = Closure->Record; + uint32_t NumValueKinds = Closure->GetNumValueKinds(Record); + if (NumValueKinds == 0) + return TotalSize; + + for (Kind = IPVK_First; Kind <= IPVK_Last; Kind++) { + uint32_t NumValueSites = Closure->GetNumValueSites(Record, Kind); + if (!NumValueSites) + continue; + TotalSize += getValueProfRecordSize(NumValueSites, + Closure->GetNumValueData(Record, Kind)); + } + return TotalSize; +} + +/*! + * Extract value profile data of a function for the profile kind \c ValueKind + * from the \c Closure and serialize the data into \c This record instance. + */ +void serializeValueProfRecordFrom(ValueProfRecord *This, + ValueProfRecordClosure *Closure, + uint32_t ValueKind, uint32_t NumValueSites) { + uint32_t S; + const void *Record = Closure->Record; + This->Kind = ValueKind; + This->NumValueSites = NumValueSites; + InstrProfValueData *DstVD = getValueProfRecordValueData(This); + + for (S = 0; S < NumValueSites; S++) { + uint32_t ND = Closure->GetNumValueDataForSite(Record, ValueKind, S); + This->SiteCountArray[S] = ND; + Closure->GetValueForSite(Record, DstVD, ValueKind, S, + Closure->RemapValueData); + DstVD += ND; + } +} + +/*! + * Extract value profile data of a function from the \c Closure + * and serialize the data into \c DstData if it is not NULL or heap + * memory allocated by the \c Closure's allocator method. + */ +ValueProfData *serializeValueProfDataFrom(ValueProfRecordClosure *Closure, + ValueProfData *DstData) { + uint32_t Kind; + uint32_t TotalSize = getValueProfDataSize(Closure); + + ValueProfData *VPD = + DstData ? DstData : Closure->AllocValueProfData(TotalSize); + + VPD->TotalSize = TotalSize; + VPD->NumValueKinds = Closure->GetNumValueKinds(Closure->Record); + ValueProfRecord *VR = getFirstValueProfRecord(VPD); + for (Kind = IPVK_First; Kind <= IPVK_Last; Kind++) { + uint32_t NumValueSites = Closure->GetNumValueSites(Closure->Record, Kind); + if (!NumValueSites) + continue; + serializeValueProfRecordFrom(VR, Closure, Kind, NumValueSites); + VR = getValueProfRecordNext(VR); + } + return VPD; +} + +/* + * The value profiler runtime library stores the value profile data + * for a given function in \c NumValueSites and \c Nodes structures. + * \c ValueProfRuntimeRecord class is used to encapsulate the runtime + * profile data and provides fast interfaces to retrieve the profile + * information. This interface is used to initialize the runtime record + * and pre-compute the information needed for efficient implementation + * of callbacks required by ValueProfRecordClosure class. + */ +int initializeValueProfRuntimeRecord(ValueProfRuntimeRecord *RuntimeRecord, + const uint16_t *NumValueSites, + ValueProfNode **Nodes) { + unsigned I, J, S = 0, NumValueKinds = 0; + RuntimeRecord->NumValueSites = NumValueSites; + RuntimeRecord->Nodes = Nodes; + for (I = 0; I <= IPVK_Last; I++) { + uint16_t N = NumValueSites[I]; + if (!N) { + RuntimeRecord->SiteCountArray[I] = 0; + continue; + } + NumValueKinds++; + RuntimeRecord->SiteCountArray[I] = (uint8_t *)calloc(N, 1); + if (!RuntimeRecord->SiteCountArray[I]) + return 1; + RuntimeRecord->NodesKind[I] = Nodes ? &Nodes[S] : NULL; + for (J = 0; J < N; J++) { + /* Compute value count for each site. */ + uint32_t C = 0; + ValueProfNode *Site = Nodes ? RuntimeRecord->NodesKind[I][J] : NULL; + while (Site) { + C++; + Site = Site->Next; + } + if (C > UCHAR_MAX) + C = UCHAR_MAX; + RuntimeRecord->SiteCountArray[I][J] = C; + } + S += N; + } + RuntimeRecord->NumValueKinds = NumValueKinds; + return 0; +} + +void finalizeValueProfRuntimeRecord(ValueProfRuntimeRecord *RuntimeRecord) { + unsigned I; + for (I = 0; I <= IPVK_Last; I++) { + if (RuntimeRecord->SiteCountArray[I]) + free(RuntimeRecord->SiteCountArray[I]); + } +} + +/* ValueProfRecordClosure Interface implementation for + * ValueProfDataRuntimeRecord. */ +uint32_t getNumValueKindsRT(const void *R) { + return ((const ValueProfRuntimeRecord *)R)->NumValueKinds; +} + +uint32_t getNumValueSitesRT(const void *R, uint32_t VK) { + return ((const ValueProfRuntimeRecord *)R)->NumValueSites[VK]; +} + +uint32_t getNumValueDataForSiteRT(const void *R, uint32_t VK, uint32_t S) { + const ValueProfRuntimeRecord *Record = (const ValueProfRuntimeRecord *)R; + return Record->SiteCountArray[VK][S]; +} + +uint32_t getNumValueDataRT(const void *R, uint32_t VK) { + unsigned I, S = 0; + const ValueProfRuntimeRecord *Record = (const ValueProfRuntimeRecord *)R; + if (Record->SiteCountArray[VK] == 0) + return 0; + for (I = 0; I < Record->NumValueSites[VK]; I++) + S += Record->SiteCountArray[VK][I]; + return S; +} + +void getValueForSiteRT(const void *R, InstrProfValueData *Dst, uint32_t VK, + uint32_t S, uint64_t (*Mapper)(uint32_t, uint64_t)) { + unsigned I, N = 0; + const ValueProfRuntimeRecord *Record = (const ValueProfRuntimeRecord *)R; + N = getNumValueDataForSiteRT(R, VK, S); + if (N == 0) + return; + ValueProfNode *VNode = Record->NodesKind[VK][S]; + for (I = 0; I < N; I++) { + Dst[I] = VNode->VData; + VNode = VNode->Next; + } +} + +ValueProfData *allocValueProfDataRT(size_t TotalSizeInBytes) { + return (ValueProfData *)calloc(TotalSizeInBytes, 1); +} + +static ValueProfRecordClosure RTRecordClosure = {0, + getNumValueKindsRT, + getNumValueSitesRT, + getNumValueDataRT, + getNumValueDataForSiteRT, + 0, + getValueForSiteRT, + allocValueProfDataRT}; + +/* + * Return the size of ValueProfData structure to store data + * recorded in the runtime record. + */ +uint32_t getValueProfDataSizeRT(const ValueProfRuntimeRecord *Record) { + RTRecordClosure.Record = Record; + return getValueProfDataSize(&RTRecordClosure); +} + +/* + * Return a ValueProfData instance that stores the data collected + * from runtime. If \c DstData is provided by the caller, the value + * profile data will be store in *DstData and DstData is returned, + * otherwise the method will allocate space for the value data and + * return pointer to the newly allocated space. + */ +ValueProfData * +serializeValueProfDataFromRT(const ValueProfRuntimeRecord *Record, + ValueProfData *DstData) { + RTRecordClosure.Record = Record; + return serializeValueProfDataFrom(&RTRecordClosure, DstData); +} + + +#undef INSTR_PROF_COMMON_API_IMPL +#endif /* INSTR_PROF_COMMON_API_IMPL */ + +/*============================================================================*/ + + +#ifndef INSTR_PROF_DATA_DEFINED + +#ifndef INSTR_PROF_DATA_INC_ +#define INSTR_PROF_DATA_INC_ + +/* Helper macros. */ +#define INSTR_PROF_SIMPLE_QUOTE(x) #x +#define INSTR_PROF_QUOTE(x) INSTR_PROF_SIMPLE_QUOTE(x) +#define INSTR_PROF_SIMPLE_CONCAT(x,y) x ## y +#define INSTR_PROF_CONCAT(x,y) INSTR_PROF_SIMPLE_CONCAT(x,y) + +/* Magic number to detect file format and endianness. + * Use 255 at one end, since no UTF-8 file can use that character. Avoid 0, + * so that utilities, like strings, don't grab it as a string. 129 is also + * invalid UTF-8, and high enough to be interesting. + * Use "lprofr" in the centre to stand for "LLVM Profile Raw", or "lprofR" + * for 32-bit platforms. + */ +#define INSTR_PROF_RAW_MAGIC_64 (uint64_t)255 << 56 | (uint64_t)'l' << 48 | \ + (uint64_t)'p' << 40 | (uint64_t)'r' << 32 | (uint64_t)'o' << 24 | \ + (uint64_t)'f' << 16 | (uint64_t)'r' << 8 | (uint64_t)129 +#define INSTR_PROF_RAW_MAGIC_32 (uint64_t)255 << 56 | (uint64_t)'l' << 48 | \ + (uint64_t)'p' << 40 | (uint64_t)'r' << 32 | (uint64_t)'o' << 24 | \ + (uint64_t)'f' << 16 | (uint64_t)'R' << 8 | (uint64_t)129 + +/* Raw profile format version. */ +#define INSTR_PROF_RAW_VERSION 2 +#define INSTR_PROF_INDEX_VERSION 3 +#define INSTR_PROF_COVMAP_VERSION 0 + +/* Profile version is always of type uint_64_t. Reserve the upper 8 bits in the + * version for other variants of profile. We set the lowest bit of the upper 8 + * bits (i.e. bit 56) to 1 to indicate if this is an IR-level instrumentaiton + * generated profile, and 0 if this is a Clang FE generated profile. +*/ +#define VARIANT_MASKS_ALL 0xff00000000000000ULL +#define GET_VERSION(V) ((V) & ~VARIANT_MASKS_ALL) + +/* Runtime section names and name strings. */ +#define INSTR_PROF_DATA_SECT_NAME __llvm_prf_data +#define INSTR_PROF_NAME_SECT_NAME __llvm_prf_names +#define INSTR_PROF_CNTS_SECT_NAME __llvm_prf_cnts +#define INSTR_PROF_COVMAP_SECT_NAME __llvm_covmap + +#define INSTR_PROF_DATA_SECT_NAME_STR \ + INSTR_PROF_QUOTE(INSTR_PROF_DATA_SECT_NAME) +#define INSTR_PROF_NAME_SECT_NAME_STR \ + INSTR_PROF_QUOTE(INSTR_PROF_NAME_SECT_NAME) +#define INSTR_PROF_CNTS_SECT_NAME_STR \ + INSTR_PROF_QUOTE(INSTR_PROF_CNTS_SECT_NAME) +#define INSTR_PROF_COVMAP_SECT_NAME_STR \ + INSTR_PROF_QUOTE(INSTR_PROF_COVMAP_SECT_NAME) + +/* Macros to define start/stop section symbol for a given + * section on Linux. For instance + * INSTR_PROF_SECT_START(INSTR_PROF_DATA_SECT_NAME) will + * expand to __start___llvm_prof_data + */ +#define INSTR_PROF_SECT_START(Sect) \ + INSTR_PROF_CONCAT(__start_,Sect) +#define INSTR_PROF_SECT_STOP(Sect) \ + INSTR_PROF_CONCAT(__stop_,Sect) + +/* Value Profiling API linkage name. */ +#define INSTR_PROF_VALUE_PROF_FUNC __llvm_profile_instrument_target +#define INSTR_PROF_VALUE_PROF_FUNC_STR \ + INSTR_PROF_QUOTE(INSTR_PROF_VALUE_PROF_FUNC) + +/* InstrProfile per-function control data alignment. */ +#define INSTR_PROF_DATA_ALIGNMENT 8 + +/* The data structure that represents a tracked value by the + * value profiler. + */ +typedef struct InstrProfValueData { + /* Profiled value. */ + uint64_t Value; + /* Number of times the value appears in the training run. */ + uint64_t Count; +} InstrProfValueData; + +/* This is an internal data structure used by value profiler. It + * is defined here to allow serialization code sharing by LLVM + * to be used in unit test. + */ +typedef struct ValueProfNode { + InstrProfValueData VData; + struct ValueProfNode *Next; +} ValueProfNode; + +#endif /* INSTR_PROF_DATA_INC_ */ + +#else +#undef INSTR_PROF_DATA_DEFINED +#endif diff --git a/contrib/compiler-rt/lib/profile/InstrProfiling.c b/contrib/compiler-rt/lib/profile/InstrProfiling.c index 8d010df28f18..711f2b608a5f 100644 --- a/contrib/compiler-rt/lib/profile/InstrProfiling.c +++ b/contrib/compiler-rt/lib/profile/InstrProfiling.c @@ -8,41 +8,62 @@ \*===----------------------------------------------------------------------===*/ #include "InstrProfiling.h" +#include "InstrProfilingInternal.h" +#include <limits.h> +#include <stdio.h> +#include <stdlib.h> #include <string.h> +#define INSTR_PROF_VALUE_PROF_DATA +#include "InstrProfData.inc" -__attribute__((visibility("hidden"))) -uint64_t __llvm_profile_get_magic(void) { - /* Magic number to detect file format and endianness. - * - * Use 255 at one end, since no UTF-8 file can use that character. Avoid 0, - * so that utilities, like strings, don't grab it as a string. 129 is also - * invalid UTF-8, and high enough to be interesting. - * - * Use "lprofr" in the centre to stand for "LLVM Profile Raw", or "lprofR" - * for 32-bit platforms. - */ - unsigned char R = sizeof(void *) == sizeof(uint64_t) ? 'r' : 'R'; - return - (uint64_t)255 << 56 | - (uint64_t)'l' << 48 | - (uint64_t)'p' << 40 | - (uint64_t)'r' << 32 | - (uint64_t)'o' << 24 | - (uint64_t)'f' << 16 | - (uint64_t) R << 8 | - (uint64_t)129; +char *(*GetEnvHook)(const char *) = 0; + +COMPILER_RT_WEAK uint64_t __llvm_profile_raw_version = INSTR_PROF_RAW_VERSION; + +COMPILER_RT_VISIBILITY uint64_t __llvm_profile_get_magic(void) { + return sizeof(void *) == sizeof(uint64_t) ? (INSTR_PROF_RAW_MAGIC_64) + : (INSTR_PROF_RAW_MAGIC_32); } -__attribute__((visibility("hidden"))) -uint64_t __llvm_profile_get_version(void) { - /* This should be bumped any time the output format changes. */ - return 1; +/* Return the number of bytes needed to add to SizeInBytes to make it + * the result a multiple of 8. + */ +COMPILER_RT_VISIBILITY uint8_t +__llvm_profile_get_num_padding_bytes(uint64_t SizeInBytes) { + return 7 & (sizeof(uint64_t) - SizeInBytes % sizeof(uint64_t)); } -__attribute__((visibility("hidden"))) -void __llvm_profile_reset_counters(void) { +COMPILER_RT_VISIBILITY uint64_t __llvm_profile_get_version(void) { + return __llvm_profile_raw_version; +} + +COMPILER_RT_VISIBILITY void __llvm_profile_reset_counters(void) { uint64_t *I = __llvm_profile_begin_counters(); uint64_t *E = __llvm_profile_end_counters(); - memset(I, 0, sizeof(uint64_t)*(E - I)); + memset(I, 0, sizeof(uint64_t) * (E - I)); + + const __llvm_profile_data *DataBegin = __llvm_profile_begin_data(); + const __llvm_profile_data *DataEnd = __llvm_profile_end_data(); + const __llvm_profile_data *DI; + for (DI = DataBegin; DI != DataEnd; ++DI) { + uint64_t CurrentVSiteCount = 0; + uint32_t VKI, i; + if (!DI->Values) + continue; + + ValueProfNode **ValueCounters = (ValueProfNode **)DI->Values; + + for (VKI = IPVK_First; VKI <= IPVK_Last; ++VKI) + CurrentVSiteCount += DI->NumValueSites[VKI]; + + for (i = 0; i < CurrentVSiteCount; ++i) { + ValueProfNode *CurrentVNode = ValueCounters[i]; + + while (CurrentVNode) { + CurrentVNode->VData.Count = 0; + CurrentVNode = CurrentVNode->Next; + } + } + } } diff --git a/contrib/compiler-rt/lib/profile/InstrProfiling.h b/contrib/compiler-rt/lib/profile/InstrProfiling.h index 3778a88893e6..d27ca569d535 100644 --- a/contrib/compiler-rt/lib/profile/InstrProfiling.h +++ b/contrib/compiler-rt/lib/profile/InstrProfiling.h @@ -10,32 +10,31 @@ #ifndef PROFILE_INSTRPROFILING_H_ #define PROFILE_INSTRPROFILING_H_ -#if defined(__FreeBSD__) && defined(__i386__) - -/* System headers define 'size_t' incorrectly on x64 FreeBSD (prior to - * FreeBSD 10, r232261) when compiled in 32-bit mode. - */ -#define PRIu64 "llu" -typedef unsigned int uint32_t; -typedef unsigned long long uint64_t; -typedef uint32_t uintptr_t; - -#else /* defined(__FreeBSD__) && defined(__i386__) */ - -#include <inttypes.h> -#include <stdint.h> - -#endif /* defined(__FreeBSD__) && defined(__i386__) */ +#include "InstrProfilingPort.h" +#include "InstrProfData.inc" + +enum ValueKind { +#define VALUE_PROF_KIND(Enumerator, Value) Enumerator = Value, +#include "InstrProfData.inc" +}; + +typedef void *IntPtrT; +typedef struct COMPILER_RT_ALIGNAS(INSTR_PROF_DATA_ALIGNMENT) + __llvm_profile_data { +#define INSTR_PROF_DATA(Type, LLVMType, Name, Initializer) Type Name; +#include "InstrProfData.inc" +} __llvm_profile_data; -#define PROFILE_HEADER_SIZE 7 +typedef struct __llvm_profile_header { +#define INSTR_PROF_RAW_HEADER(Type, Name, Initializer) Type Name; +#include "InstrProfData.inc" +} __llvm_profile_header; -typedef struct __llvm_profile_data { - const uint32_t NameSize; - const uint32_t NumCounters; - const uint64_t FuncHash; - const char *const Name; - uint64_t *const Counters; -} __llvm_profile_data; +/*! + * \brief Get number of bytes necessary to pad the argument to eight + * byte boundary. + */ +uint8_t __llvm_profile_get_num_padding_bytes(uint64_t SizeInBytes); /*! * \brief Get required size for profile buffer. @@ -58,9 +57,37 @@ uint64_t *__llvm_profile_begin_counters(void); uint64_t *__llvm_profile_end_counters(void); /*! + * \brief Clear profile counters to zero. + * + */ +void __llvm_profile_reset_counters(void); + +/*! + * \brief Counts the number of times a target value is seen. + * + * Records the target value for the CounterIndex if not seen before. Otherwise, + * increments the counter associated w/ the target value. + * void __llvm_profile_instrument_target(uint64_t TargetValue, void *Data, + * uint32_t CounterIndex); + */ +void INSTR_PROF_VALUE_PROF_FUNC( +#define VALUE_PROF_FUNC_PARAM(ArgType, ArgName, ArgLLVMType) ArgType ArgName +#include "InstrProfData.inc" +); + +/*! + * \brief Prepares the value profiling data for output. + * + * Returns an array of pointers to value profile data. + */ +struct ValueProfData; +struct ValueProfData **__llvm_profile_gather_value_data(uint64_t *Size); + +/*! * \brief Write instrumentation data to the current file. * - * Writes to the file with the last name given to \a __llvm_profile_set_filename(), + * Writes to the file with the last name given to \a * + * __llvm_profile_set_filename(), * or if it hasn't been called, the \c LLVM_PROFILE_FILE environment variable, * or if that's not set, the last name given to * \a __llvm_profile_override_default_filename(), or if that's not set, diff --git a/contrib/compiler-rt/lib/profile/InstrProfilingBuffer.c b/contrib/compiler-rt/lib/profile/InstrProfilingBuffer.c index 3c429c8a85ea..4227ca6b66ea 100644 --- a/contrib/compiler-rt/lib/profile/InstrProfilingBuffer.c +++ b/contrib/compiler-rt/lib/profile/InstrProfilingBuffer.c @@ -10,9 +10,7 @@ #include "InstrProfiling.h" #include "InstrProfilingInternal.h" -#include <string.h> - -__attribute__((visibility("hidden"))) +COMPILER_RT_VISIBILITY uint64_t __llvm_profile_get_size_for_buffer(void) { const __llvm_profile_data *DataBegin = __llvm_profile_begin_data(); const __llvm_profile_data *DataEnd = __llvm_profile_end_data(); @@ -27,78 +25,28 @@ uint64_t __llvm_profile_get_size_for_buffer(void) { #define PROFILE_RANGE_SIZE(Range) (Range##End - Range##Begin) -__attribute__((visibility("hidden"))) +COMPILER_RT_VISIBILITY uint64_t __llvm_profile_get_size_for_buffer_internal( - const __llvm_profile_data *DataBegin, - const __llvm_profile_data *DataEnd, const uint64_t *CountersBegin, - const uint64_t *CountersEnd, const char *NamesBegin, - const char *NamesEnd) { + const __llvm_profile_data *DataBegin, const __llvm_profile_data *DataEnd, + const uint64_t *CountersBegin, const uint64_t *CountersEnd, + const char *NamesBegin, const char *NamesEnd) { /* Match logic in __llvm_profile_write_buffer(). */ const uint64_t NamesSize = PROFILE_RANGE_SIZE(Names) * sizeof(char); - const uint64_t Padding = sizeof(uint64_t) - NamesSize % sizeof(uint64_t); - return sizeof(uint64_t) * PROFILE_HEADER_SIZE + - PROFILE_RANGE_SIZE(Data) * sizeof(__llvm_profile_data) + - PROFILE_RANGE_SIZE(Counters) * sizeof(uint64_t) + - NamesSize + Padding; + const uint8_t Padding = __llvm_profile_get_num_padding_bytes(NamesSize); + return sizeof(__llvm_profile_header) + + PROFILE_RANGE_SIZE(Data) * sizeof(__llvm_profile_data) + + PROFILE_RANGE_SIZE(Counters) * sizeof(uint64_t) + NamesSize + Padding; } -__attribute__((visibility("hidden"))) -int __llvm_profile_write_buffer(char *Buffer) { - /* Match logic in __llvm_profile_get_size_for_buffer(). - * Match logic in __llvm_profile_write_file(). - */ - const __llvm_profile_data *DataBegin = __llvm_profile_begin_data(); - const __llvm_profile_data *DataEnd = __llvm_profile_end_data(); - const uint64_t *CountersBegin = __llvm_profile_begin_counters(); - const uint64_t *CountersEnd = __llvm_profile_end_counters(); - const char *NamesBegin = __llvm_profile_begin_names(); - const char *NamesEnd = __llvm_profile_end_names(); - - return __llvm_profile_write_buffer_internal(Buffer, DataBegin, DataEnd, - CountersBegin, CountersEnd, - NamesBegin, NamesEnd); +COMPILER_RT_VISIBILITY int __llvm_profile_write_buffer(char *Buffer) { + return llvmWriteProfData(llvmBufferWriter, Buffer, 0, 0); } -__attribute__((visibility("hidden"))) -int __llvm_profile_write_buffer_internal( +COMPILER_RT_VISIBILITY int __llvm_profile_write_buffer_internal( char *Buffer, const __llvm_profile_data *DataBegin, const __llvm_profile_data *DataEnd, const uint64_t *CountersBegin, const uint64_t *CountersEnd, const char *NamesBegin, const char *NamesEnd) { - /* Match logic in __llvm_profile_get_size_for_buffer(). - * Match logic in __llvm_profile_write_file(). - */ - - /* Calculate size of sections. */ - const uint64_t DataSize = DataEnd - DataBegin; - const uint64_t CountersSize = CountersEnd - CountersBegin; - const uint64_t NamesSize = NamesEnd - NamesBegin; - const uint64_t Padding = sizeof(uint64_t) - NamesSize % sizeof(uint64_t); - - /* Enough zeroes for padding. */ - const char Zeroes[sizeof(uint64_t)] = {0}; - - /* Create the header. */ - uint64_t Header[PROFILE_HEADER_SIZE]; - Header[0] = __llvm_profile_get_magic(); - Header[1] = __llvm_profile_get_version(); - Header[2] = DataSize; - Header[3] = CountersSize; - Header[4] = NamesSize; - Header[5] = (uintptr_t)CountersBegin; - Header[6] = (uintptr_t)NamesBegin; - - /* Write the data. */ -#define UPDATE_memcpy(Data, Size) \ - do { \ - memcpy(Buffer, Data, Size); \ - Buffer += Size; \ - } while (0) - UPDATE_memcpy(Header, PROFILE_HEADER_SIZE * sizeof(uint64_t)); - UPDATE_memcpy(DataBegin, DataSize * sizeof(__llvm_profile_data)); - UPDATE_memcpy(CountersBegin, CountersSize * sizeof(uint64_t)); - UPDATE_memcpy(NamesBegin, NamesSize * sizeof(char)); - UPDATE_memcpy(Zeroes, Padding * sizeof(char)); -#undef UPDATE_memcpy - - return 0; + return llvmWriteProfDataImpl(llvmBufferWriter, Buffer, DataBegin, DataEnd, + CountersBegin, CountersEnd, 0, 0, NamesBegin, + NamesEnd); } diff --git a/contrib/compiler-rt/lib/profile/InstrProfilingFile.c b/contrib/compiler-rt/lib/profile/InstrProfilingFile.c index 68e8c7b07871..68d088a1956a 100644 --- a/contrib/compiler-rt/lib/profile/InstrProfilingFile.c +++ b/contrib/compiler-rt/lib/profile/InstrProfilingFile.c @@ -8,6 +8,7 @@ \*===----------------------------------------------------------------------===*/ #include "InstrProfiling.h" +#include "InstrProfilingInternal.h" #include "InstrProfilingUtil.h" #include <errno.h> #include <stdio.h> @@ -16,47 +17,43 @@ #define UNCONST(ptr) ((void *)(uintptr_t)(ptr)) -static int writeFile(FILE *File) { - /* Match logic in __llvm_profile_write_buffer(). */ - const __llvm_profile_data *DataBegin = __llvm_profile_begin_data(); - const __llvm_profile_data *DataEnd = __llvm_profile_end_data(); - const uint64_t *CountersBegin = __llvm_profile_begin_counters(); - const uint64_t *CountersEnd = __llvm_profile_end_counters(); - const char *NamesBegin = __llvm_profile_begin_names(); - const char *NamesEnd = __llvm_profile_end_names(); - - /* Calculate size of sections. */ - const uint64_t DataSize = DataEnd - DataBegin; - const uint64_t CountersSize = CountersEnd - CountersBegin; - const uint64_t NamesSize = NamesEnd - NamesBegin; - const uint64_t Padding = sizeof(uint64_t) - NamesSize % sizeof(uint64_t); - - /* Enough zeroes for padding. */ - const char Zeroes[sizeof(uint64_t)] = {0}; - - /* Create the header. */ - uint64_t Header[PROFILE_HEADER_SIZE]; - Header[0] = __llvm_profile_get_magic(); - Header[1] = __llvm_profile_get_version(); - Header[2] = DataSize; - Header[3] = CountersSize; - Header[4] = NamesSize; - Header[5] = (uintptr_t)CountersBegin; - Header[6] = (uintptr_t)NamesBegin; - - /* Write the data. */ -#define CHECK_fwrite(Data, Size, Length, File) \ - do { if (fwrite(Data, Size, Length, File) != Length) return -1; } while (0) - CHECK_fwrite(Header, sizeof(uint64_t), PROFILE_HEADER_SIZE, File); - CHECK_fwrite(DataBegin, sizeof(__llvm_profile_data), DataSize, File); - CHECK_fwrite(CountersBegin, sizeof(uint64_t), CountersSize, File); - CHECK_fwrite(NamesBegin, sizeof(char), NamesSize, File); - CHECK_fwrite(Zeroes, sizeof(char), Padding, File); -#undef CHECK_fwrite - +#ifdef _MSC_VER +#define snprintf _snprintf +#endif + +/* Return 1 if there is an error, otherwise return 0. */ +static uint32_t fileWriter(ProfDataIOVec *IOVecs, uint32_t NumIOVecs, + void **WriterCtx) { + uint32_t I; + FILE *File = (FILE *)*WriterCtx; + for (I = 0; I < NumIOVecs; I++) { + if (fwrite(IOVecs[I].Data, IOVecs[I].ElmSize, IOVecs[I].NumElm, File) != + IOVecs[I].NumElm) + return 1; + } return 0; } +COMPILER_RT_VISIBILITY ProfBufferIO * +llvmCreateBufferIOInternal(void *File, uint32_t BufferSz) { + CallocHook = calloc; + FreeHook = free; + return llvmCreateBufferIO(fileWriter, File, BufferSz); +} + +static int writeFile(FILE *File) { + const char *BufferSzStr = 0; + uint64_t ValueDataSize = 0; + struct ValueProfData **ValueDataArray = + __llvm_profile_gather_value_data(&ValueDataSize); + FreeHook = &free; + CallocHook = &calloc; + BufferSzStr = getenv("LLVM_VP_BUFFER_SIZE"); + if (BufferSzStr && BufferSzStr[0]) + VPBufferSize = atoi(BufferSzStr); + return llvmWriteProfData(fileWriter, File, ValueDataArray, ValueDataSize); +} + static int writeFileWithName(const char *OutputName) { int RetVal; FILE *OutputFile; @@ -64,7 +61,7 @@ static int writeFileWithName(const char *OutputName) { return -1; /* Append to the file to support profiling multiple shared objects. */ - OutputFile = fopen(OutputName, "a"); + OutputFile = fopen(OutputName, "ab"); if (!OutputFile) return -1; @@ -74,8 +71,8 @@ static int writeFileWithName(const char *OutputName) { return RetVal; } -__attribute__((weak)) int __llvm_profile_OwnsFilename = 0; -__attribute__((weak)) const char *__llvm_profile_CurrentFilename = NULL; +COMPILER_RT_WEAK int __llvm_profile_OwnsFilename = 0; +COMPILER_RT_WEAK const char *__llvm_profile_CurrentFilename = NULL; static void truncateCurrentFile(void) { const char *Filename; @@ -182,7 +179,7 @@ static void setFilenameAutomatically(void) { resetFilenameToDefault(); } -__attribute__((visibility("hidden"))) +COMPILER_RT_VISIBILITY void __llvm_profile_initialize_file(void) { /* Check if the filename has been initialized. */ if (__llvm_profile_CurrentFilename) @@ -192,12 +189,12 @@ void __llvm_profile_initialize_file(void) { setFilenameAutomatically(); } -__attribute__((visibility("hidden"))) +COMPILER_RT_VISIBILITY void __llvm_profile_set_filename(const char *Filename) { setFilenamePossiblyWithPid(Filename); } -__attribute__((visibility("hidden"))) +COMPILER_RT_VISIBILITY void __llvm_profile_override_default_filename(const char *Filename) { /* If the env var is set, skip setting filename from argument. */ const char *Env_Filename = getenv("LLVM_PROFILE_FILE"); @@ -206,27 +203,37 @@ void __llvm_profile_override_default_filename(const char *Filename) { setFilenamePossiblyWithPid(Filename); } -__attribute__((visibility("hidden"))) +COMPILER_RT_VISIBILITY int __llvm_profile_write_file(void) { int rc; + GetEnvHook = &getenv; /* Check the filename. */ - if (!__llvm_profile_CurrentFilename) + if (!__llvm_profile_CurrentFilename) { + PROF_ERR("LLVM Profile: Failed to write file : %s\n", "Filename not set"); + return -1; + } + + /* Check if there is llvm/runtime version mismatch. */ + if (GET_VERSION(__llvm_profile_get_version()) != INSTR_PROF_RAW_VERSION) { + PROF_ERR("LLVM Profile: runtime and instrumentation version mismatch : " + "expected %d, but get %d\n", + INSTR_PROF_RAW_VERSION, + (int)GET_VERSION(__llvm_profile_get_version())); return -1; + } /* Write the file. */ rc = writeFileWithName(__llvm_profile_CurrentFilename); - if (rc && getenv("LLVM_PROFILE_VERBOSE_ERRORS")) - fprintf(stderr, "LLVM Profile: Failed to write file \"%s\": %s\n", + if (rc) + PROF_ERR("LLVM Profile: Failed to write file \"%s\": %s\n", __llvm_profile_CurrentFilename, strerror(errno)); return rc; } -static void writeFileWithoutReturn(void) { - __llvm_profile_write_file(); -} +static void writeFileWithoutReturn(void) { __llvm_profile_write_file(); } -__attribute__((visibility("hidden"))) +COMPILER_RT_VISIBILITY int __llvm_profile_register_write_file_atexit(void) { static int HasBeenRegistered = 0; diff --git a/contrib/compiler-rt/lib/profile/InstrProfilingInternal.h b/contrib/compiler-rt/lib/profile/InstrProfilingInternal.h index ede39cd9d713..4aab78ea509c 100644 --- a/contrib/compiler-rt/lib/profile/InstrProfilingInternal.h +++ b/contrib/compiler-rt/lib/profile/InstrProfilingInternal.h @@ -11,6 +11,7 @@ #define PROFILE_INSTRPROFILING_INTERNALH_ #include "InstrProfiling.h" +#include "stddef.h" /*! * \brief Write instrumentation data to the given buffer, given explicit @@ -37,4 +38,81 @@ int __llvm_profile_write_buffer_internal( const __llvm_profile_data *DataEnd, const uint64_t *CountersBegin, const uint64_t *CountersEnd, const char *NamesBegin, const char *NamesEnd); +/*! + * The data structure describing the data to be written by the + * low level writer callback function. + */ +typedef struct ProfDataIOVec { + const void *Data; + size_t ElmSize; + size_t NumElm; +} ProfDataIOVec; + +typedef uint32_t (*WriterCallback)(ProfDataIOVec *, uint32_t NumIOVecs, + void **WriterCtx); + +/*! + * The data structure for buffered IO of profile data. + */ +typedef struct ProfBufferIO { + /* File handle. */ + void *File; + /* Low level IO callback. */ + WriterCallback FileWriter; + /* The start of the buffer. */ + uint8_t *BufferStart; + /* Total size of the buffer. */ + uint32_t BufferSz; + /* Current byte offset from the start of the buffer. */ + uint32_t CurOffset; +} ProfBufferIO; + +/* The creator interface used by testing. */ +ProfBufferIO *llvmCreateBufferIOInternal(void *File, uint32_t DefaultBufferSz); +/*! + * This is the interface to create a handle for buffered IO. + */ +ProfBufferIO *llvmCreateBufferIO(WriterCallback FileWriter, void *File, + uint32_t DefaultBufferSz); +/*! + * The interface to destroy the bufferIO handle and reclaim + * the memory. + */ +void llvmDeleteBufferIO(ProfBufferIO *BufferIO); + +/*! + * This is the interface to write \c Data of \c Size bytes through + * \c BufferIO. Returns 0 if successful, otherwise return -1. + */ +int llvmBufferIOWrite(ProfBufferIO *BufferIO, const uint8_t *Data, + uint32_t Size); +/*! + * The interface to flush the remaining data in the buffer. + * through the low level writer callback. + */ +int llvmBufferIOFlush(ProfBufferIO *BufferIO); + +/* The low level interface to write data into a buffer. It is used as the + * callback by other high level writer methods such as buffered IO writer + * and profile data writer. */ +uint32_t llvmBufferWriter(ProfDataIOVec *IOVecs, uint32_t NumIOVecs, + void **WriterCtx); + +int llvmWriteProfData(WriterCallback Writer, void *WriterCtx, + struct ValueProfData **ValueDataArray, + const uint64_t ValueDataSize); +int llvmWriteProfDataImpl(WriterCallback Writer, void *WriterCtx, + const __llvm_profile_data *DataBegin, + const __llvm_profile_data *DataEnd, + const uint64_t *CountersBegin, + const uint64_t *CountersEnd, + struct ValueProfData **ValueDataBeginArray, + const uint64_t ValueDataSize, const char *NamesBegin, + const char *NamesEnd); + +extern char *(*GetEnvHook)(const char *); +extern void (*FreeHook)(void *); +extern void* (*CallocHook)(size_t, size_t); +extern uint32_t VPBufferSize; + #endif diff --git a/contrib/compiler-rt/lib/profile/InstrProfilingPlatformDarwin.c b/contrib/compiler-rt/lib/profile/InstrProfilingPlatformDarwin.c index 02299cc4630c..30ddbd2e4982 100644 --- a/contrib/compiler-rt/lib/profile/InstrProfilingPlatformDarwin.c +++ b/contrib/compiler-rt/lib/profile/InstrProfilingPlatformDarwin.c @@ -11,33 +11,36 @@ #if defined(__APPLE__) /* Use linker magic to find the bounds of the Data section. */ -__attribute__((visibility("hidden"))) -extern __llvm_profile_data DataStart __asm("section$start$__DATA$__llvm_prf_data"); -__attribute__((visibility("hidden"))) -extern __llvm_profile_data DataEnd __asm("section$end$__DATA$__llvm_prf_data"); -__attribute__((visibility("hidden"))) -extern char NamesStart __asm("section$start$__DATA$__llvm_prf_names"); -__attribute__((visibility("hidden"))) -extern char NamesEnd __asm("section$end$__DATA$__llvm_prf_names"); -__attribute__((visibility("hidden"))) -extern uint64_t CountersStart __asm("section$start$__DATA$__llvm_prf_cnts"); -__attribute__((visibility("hidden"))) -extern uint64_t CountersEnd __asm("section$end$__DATA$__llvm_prf_cnts"); +COMPILER_RT_VISIBILITY +extern __llvm_profile_data + DataStart __asm("section$start$__DATA$" INSTR_PROF_DATA_SECT_NAME_STR); +COMPILER_RT_VISIBILITY +extern __llvm_profile_data + DataEnd __asm("section$end$__DATA$" INSTR_PROF_DATA_SECT_NAME_STR); +COMPILER_RT_VISIBILITY +extern char + NamesStart __asm("section$start$__DATA$" INSTR_PROF_NAME_SECT_NAME_STR); +COMPILER_RT_VISIBILITY +extern char NamesEnd __asm("section$end$__DATA$" INSTR_PROF_NAME_SECT_NAME_STR); +COMPILER_RT_VISIBILITY +extern uint64_t + CountersStart __asm("section$start$__DATA$" INSTR_PROF_CNTS_SECT_NAME_STR); +COMPILER_RT_VISIBILITY +extern uint64_t + CountersEnd __asm("section$end$__DATA$" INSTR_PROF_CNTS_SECT_NAME_STR); -__attribute__((visibility("hidden"))) +COMPILER_RT_VISIBILITY const __llvm_profile_data *__llvm_profile_begin_data(void) { return &DataStart; } -__attribute__((visibility("hidden"))) -const __llvm_profile_data *__llvm_profile_end_data(void) { - return &DataEnd; -} -__attribute__((visibility("hidden"))) +COMPILER_RT_VISIBILITY +const __llvm_profile_data *__llvm_profile_end_data(void) { return &DataEnd; } +COMPILER_RT_VISIBILITY const char *__llvm_profile_begin_names(void) { return &NamesStart; } -__attribute__((visibility("hidden"))) +COMPILER_RT_VISIBILITY const char *__llvm_profile_end_names(void) { return &NamesEnd; } -__attribute__((visibility("hidden"))) +COMPILER_RT_VISIBILITY uint64_t *__llvm_profile_begin_counters(void) { return &CountersStart; } -__attribute__((visibility("hidden"))) +COMPILER_RT_VISIBILITY uint64_t *__llvm_profile_end_counters(void) { return &CountersEnd; } #endif diff --git a/contrib/compiler-rt/lib/profile/InstrProfilingPlatformLinux.c b/contrib/compiler-rt/lib/profile/InstrProfilingPlatformLinux.c new file mode 100644 index 000000000000..7843f47caa1b --- /dev/null +++ b/contrib/compiler-rt/lib/profile/InstrProfilingPlatformLinux.c @@ -0,0 +1,59 @@ +/*===- InstrProfilingPlatformLinux.c - Profile data Linux platform ------===*\ +|* +|* The LLVM Compiler Infrastructure +|* +|* This file is distributed under the University of Illinois Open Source +|* License. See LICENSE.TXT for details. +|* +\*===----------------------------------------------------------------------===*/ + +#include "InstrProfiling.h" + +#if defined(__linux__) || defined(__FreeBSD__) +#include <stdlib.h> + +#define PROF_DATA_START INSTR_PROF_SECT_START(INSTR_PROF_DATA_SECT_NAME) +#define PROF_DATA_STOP INSTR_PROF_SECT_STOP(INSTR_PROF_DATA_SECT_NAME) +#define PROF_NAME_START INSTR_PROF_SECT_START(INSTR_PROF_NAME_SECT_NAME) +#define PROF_NAME_STOP INSTR_PROF_SECT_STOP(INSTR_PROF_NAME_SECT_NAME) +#define PROF_CNTS_START INSTR_PROF_SECT_START(INSTR_PROF_CNTS_SECT_NAME) +#define PROF_CNTS_STOP INSTR_PROF_SECT_STOP(INSTR_PROF_CNTS_SECT_NAME) + +/* Declare section start and stop symbols for various sections + * generated by compiler instrumentation. + */ +extern __llvm_profile_data PROF_DATA_START COMPILER_RT_VISIBILITY; +extern __llvm_profile_data PROF_DATA_STOP COMPILER_RT_VISIBILITY; +extern uint64_t PROF_CNTS_START COMPILER_RT_VISIBILITY; +extern uint64_t PROF_CNTS_STOP COMPILER_RT_VISIBILITY; +extern char PROF_NAME_START COMPILER_RT_VISIBILITY; +extern char PROF_NAME_STOP COMPILER_RT_VISIBILITY; + +/* Add dummy data to ensure the section is always created. */ +__llvm_profile_data + __prof_data_sect_data[0] COMPILER_RT_SECTION(INSTR_PROF_DATA_SECT_NAME_STR); +uint64_t + __prof_cnts_sect_data[0] COMPILER_RT_SECTION(INSTR_PROF_CNTS_SECT_NAME_STR); +char __prof_nms_sect_data[0] COMPILER_RT_SECTION(INSTR_PROF_NAME_SECT_NAME_STR); + +COMPILER_RT_VISIBILITY const __llvm_profile_data * +__llvm_profile_begin_data(void) { + return &PROF_DATA_START; +} +COMPILER_RT_VISIBILITY const __llvm_profile_data * +__llvm_profile_end_data(void) { + return &PROF_DATA_STOP; +} +COMPILER_RT_VISIBILITY const char *__llvm_profile_begin_names(void) { + return &PROF_NAME_START; +} +COMPILER_RT_VISIBILITY const char *__llvm_profile_end_names(void) { + return &PROF_NAME_STOP; +} +COMPILER_RT_VISIBILITY uint64_t *__llvm_profile_begin_counters(void) { + return &PROF_CNTS_START; +} +COMPILER_RT_VISIBILITY uint64_t *__llvm_profile_end_counters(void) { + return &PROF_CNTS_STOP; +} +#endif diff --git a/contrib/compiler-rt/lib/profile/InstrProfilingPlatformOther.c b/contrib/compiler-rt/lib/profile/InstrProfilingPlatformOther.c index 548d6a396b76..58ceb3458a0a 100644 --- a/contrib/compiler-rt/lib/profile/InstrProfilingPlatformOther.c +++ b/contrib/compiler-rt/lib/profile/InstrProfilingPlatformOther.c @@ -9,7 +9,7 @@ #include "InstrProfiling.h" -#if !defined(__APPLE__) +#if !defined(__APPLE__) && !defined(__linux__) && !defined(__FreeBSD__) #include <stdlib.h> static const __llvm_profile_data *DataFirst = NULL; @@ -26,49 +26,43 @@ static uint64_t *CountersLast = NULL; * calls are only required (and only emitted) on targets where we haven't * implemented linker magic to find the bounds of the sections. */ -__attribute__((visibility("hidden"))) +COMPILER_RT_VISIBILITY void __llvm_profile_register_function(void *Data_) { /* TODO: Only emit this function if we can't use linker magic. */ - const __llvm_profile_data *Data = (__llvm_profile_data*)Data_; + const __llvm_profile_data *Data = (__llvm_profile_data *)Data_; if (!DataFirst) { DataFirst = Data; DataLast = Data + 1; - NamesFirst = Data->Name; - NamesLast = Data->Name + Data->NameSize; - CountersFirst = Data->Counters; - CountersLast = Data->Counters + Data->NumCounters; + NamesFirst = Data->NamePtr; + NamesLast = (const char *)Data->NamePtr + Data->NameSize; + CountersFirst = Data->CounterPtr; + CountersLast = (uint64_t *)Data->CounterPtr + Data->NumCounters; return; } -#define UPDATE_FIRST(First, New) \ - First = New < First ? New : First +#define UPDATE_FIRST(First, New) First = New < First ? New : First UPDATE_FIRST(DataFirst, Data); - UPDATE_FIRST(NamesFirst, Data->Name); - UPDATE_FIRST(CountersFirst, Data->Counters); + UPDATE_FIRST(NamesFirst, (const char *)Data->NamePtr); + UPDATE_FIRST(CountersFirst, (uint64_t *)Data->CounterPtr); #undef UPDATE_FIRST -#define UPDATE_LAST(Last, New) \ - Last = New > Last ? New : Last +#define UPDATE_LAST(Last, New) Last = New > Last ? New : Last UPDATE_LAST(DataLast, Data + 1); - UPDATE_LAST(NamesLast, Data->Name + Data->NameSize); - UPDATE_LAST(CountersLast, Data->Counters + Data->NumCounters); + UPDATE_LAST(NamesLast, (const char *)Data->NamePtr + Data->NameSize); + UPDATE_LAST(CountersLast, (uint64_t *)Data->CounterPtr + Data->NumCounters); #undef UPDATE_LAST } -__attribute__((visibility("hidden"))) -const __llvm_profile_data *__llvm_profile_begin_data(void) { - return DataFirst; -} -__attribute__((visibility("hidden"))) -const __llvm_profile_data *__llvm_profile_end_data(void) { - return DataLast; -} -__attribute__((visibility("hidden"))) +COMPILER_RT_VISIBILITY +const __llvm_profile_data *__llvm_profile_begin_data(void) { return DataFirst; } +COMPILER_RT_VISIBILITY +const __llvm_profile_data *__llvm_profile_end_data(void) { return DataLast; } +COMPILER_RT_VISIBILITY const char *__llvm_profile_begin_names(void) { return NamesFirst; } -__attribute__((visibility("hidden"))) +COMPILER_RT_VISIBILITY const char *__llvm_profile_end_names(void) { return NamesLast; } -__attribute__((visibility("hidden"))) +COMPILER_RT_VISIBILITY uint64_t *__llvm_profile_begin_counters(void) { return CountersFirst; } -__attribute__((visibility("hidden"))) +COMPILER_RT_VISIBILITY uint64_t *__llvm_profile_end_counters(void) { return CountersLast; } #endif diff --git a/contrib/compiler-rt/lib/profile/InstrProfilingPort.h b/contrib/compiler-rt/lib/profile/InstrProfilingPort.h new file mode 100644 index 000000000000..e07f59878730 --- /dev/null +++ b/contrib/compiler-rt/lib/profile/InstrProfilingPort.h @@ -0,0 +1,62 @@ +/*===- InstrProfilingPort.h- Support library for PGO instrumentation ------===*\ +|* +|* The LLVM Compiler Infrastructure +|* +|* This file is distributed under the University of Illinois Open Source +|* License. See LICENSE.TXT for details. +|* +\*===----------------------------------------------------------------------===*/ + +#ifndef PROFILE_INSTRPROFILING_PORT_H_ +#define PROFILE_INSTRPROFILING_PORT_H_ + +#ifdef _MSC_VER +#define COMPILER_RT_ALIGNAS(x) __declspec(align(x)) +#define COMPILER_RT_VISIBILITY +#define COMPILER_RT_WEAK __declspec(selectany) +#elif __GNUC__ +#define COMPILER_RT_ALIGNAS(x) __attribute__((aligned(x))) +#define COMPILER_RT_VISIBILITY __attribute__((visibility("hidden"))) +#define COMPILER_RT_WEAK __attribute__((weak)) +#endif + +#define COMPILER_RT_SECTION(Sect) __attribute__((section(Sect))) + +#if COMPILER_RT_HAS_ATOMICS == 1 +#ifdef _MSC_VER +#include <windows.h> +#if defined(_WIN64) +#define COMPILER_RT_BOOL_CMPXCHG(Ptr, OldV, NewV) \ + (InterlockedCompareExchange64((LONGLONG volatile *)Ptr, (LONGLONG)NewV, \ + (LONGLONG)OldV) == (LONGLONG)OldV) +#else /* !defined(_WIN64) */ +#define COMPILER_RT_BOOL_CMPXCHG(Ptr, OldV, NewV) \ + (InterlockedCompareExchange((LONG volatile *)Ptr, (LONG)NewV, (LONG)OldV) == \ + (LONG)OldV) +#endif +#else /* !defined(_MSC_VER) */ +#define COMPILER_RT_BOOL_CMPXCHG(Ptr, OldV, NewV) \ + __sync_bool_compare_and_swap(Ptr, OldV, NewV) +#endif +#else /* COMPILER_RT_HAS_ATOMICS != 1 */ +#define COMPILER_RT_BOOL_CMPXCHG(Ptr, OldV, NewV) \ + BoolCmpXchg((void **)Ptr, OldV, NewV) +#endif + +#define PROF_ERR(Format, ...) \ + if (GetEnvHook && GetEnvHook("LLVM_PROFILE_VERBOSE_ERRORS")) \ + fprintf(stderr, Format, __VA_ARGS__); + +#if defined(__FreeBSD__) + +#include <inttypes.h> +#include <sys/types.h> + +#else /* defined(__FreeBSD__) */ + +#include <inttypes.h> +#include <stdint.h> + +#endif /* defined(__FreeBSD__) && defined(__i386__) */ + +#endif /* PROFILE_INSTRPROFILING_PORT_H_ */ diff --git a/contrib/compiler-rt/lib/profile/InstrProfilingRuntime.cc b/contrib/compiler-rt/lib/profile/InstrProfilingRuntime.cc index 081ecb29e987..12ad9f1573f4 100644 --- a/contrib/compiler-rt/lib/profile/InstrProfilingRuntime.cc +++ b/contrib/compiler-rt/lib/profile/InstrProfilingRuntime.cc @@ -11,8 +11,7 @@ extern "C" { #include "InstrProfiling.h" -__attribute__((visibility("hidden"))) int __llvm_profile_runtime; - +COMPILER_RT_VISIBILITY int __llvm_profile_runtime; } namespace { diff --git a/contrib/compiler-rt/lib/profile/InstrProfilingUtil.c b/contrib/compiler-rt/lib/profile/InstrProfilingUtil.c index e146dfca83c8..6f0443d3bb5d 100644 --- a/contrib/compiler-rt/lib/profile/InstrProfilingUtil.c +++ b/contrib/compiler-rt/lib/profile/InstrProfilingUtil.c @@ -8,6 +8,7 @@ \*===----------------------------------------------------------------------===*/ #include "InstrProfilingUtil.h" +#include "InstrProfiling.h" #ifdef _WIN32 #include <direct.h> @@ -18,7 +19,7 @@ int mkdir(const char*, unsigned short); #include <sys/types.h> #endif -__attribute__((visibility("hidden"))) +COMPILER_RT_VISIBILITY void __llvm_profile_recursive_mkdir(char *path) { int i; diff --git a/contrib/compiler-rt/lib/profile/InstrProfilingValue.c b/contrib/compiler-rt/lib/profile/InstrProfilingValue.c new file mode 100644 index 000000000000..68e16cff9cbc --- /dev/null +++ b/contrib/compiler-rt/lib/profile/InstrProfilingValue.c @@ -0,0 +1,180 @@ +/*===- InstrProfilingValue.c - Support library for PGO instrumentation ----===*\ +|* +|* The LLVM Compiler Infrastructure +|* +|* This file is distributed under the University of Illinois Open Source +|* License. See LICENSE.TXT for details. +|* +\*===----------------------------------------------------------------------===*/ + +#include "InstrProfiling.h" +#include "InstrProfilingInternal.h" +#include <limits.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#define INSTR_PROF_VALUE_PROF_DATA +#define INSTR_PROF_COMMON_API_IMPL +#include "InstrProfData.inc" + +#define PROF_OOM(Msg) PROF_ERR(Msg ":%s\n", "Out of memory"); +#define PROF_OOM_RETURN(Msg) \ + { \ + PROF_OOM(Msg) \ + free(ValueDataArray); \ + return NULL; \ + } + +#if COMPILER_RT_HAS_ATOMICS != 1 +COMPILER_RT_VISIBILITY +uint32_t BoolCmpXchg(void **Ptr, void *OldV, void *NewV) { + void *R = *Ptr; + if (R == OldV) { + *Ptr = NewV; + return 1; + } + return 0; +} +#endif + +/* This method is only used in value profiler mock testing. */ +COMPILER_RT_VISIBILITY void +__llvm_profile_set_num_value_sites(__llvm_profile_data *Data, + uint32_t ValueKind, uint16_t NumValueSites) { + *((uint16_t *)&Data->NumValueSites[ValueKind]) = NumValueSites; +} + +/* This method is only used in value profiler mock testing. */ +COMPILER_RT_VISIBILITY const __llvm_profile_data * +__llvm_profile_iterate_data(const __llvm_profile_data *Data) { + return Data + 1; +} + +/* This method is only used in value profiler mock testing. */ +COMPILER_RT_VISIBILITY void * +__llvm_get_function_addr(const __llvm_profile_data *Data) { + return Data->FunctionPointer; +} + +/* Allocate an array that holds the pointers to the linked lists of + * value profile counter nodes. The number of element of the array + * is the total number of value profile sites instrumented. Returns + * 0 if allocation fails. + */ + +static int allocateValueProfileCounters(__llvm_profile_data *Data) { + uint64_t NumVSites = 0; + uint32_t VKI; + for (VKI = IPVK_First; VKI <= IPVK_Last; ++VKI) + NumVSites += Data->NumValueSites[VKI]; + + ValueProfNode **Mem = + (ValueProfNode **)calloc(NumVSites, sizeof(ValueProfNode *)); + if (!Mem) + return 0; + if (!COMPILER_RT_BOOL_CMPXCHG(&Data->Values, 0, Mem)) { + free(Mem); + return 0; + } + return 1; +} + +COMPILER_RT_VISIBILITY void +__llvm_profile_instrument_target(uint64_t TargetValue, void *Data, + uint32_t CounterIndex) { + + __llvm_profile_data *PData = (__llvm_profile_data *)Data; + if (!PData) + return; + + if (!PData->Values) { + if (!allocateValueProfileCounters(PData)) + return; + } + + ValueProfNode **ValueCounters = (ValueProfNode **)PData->Values; + ValueProfNode *PrevVNode = NULL; + ValueProfNode *CurrentVNode = ValueCounters[CounterIndex]; + + uint8_t VDataCount = 0; + while (CurrentVNode) { + if (TargetValue == CurrentVNode->VData.Value) { + CurrentVNode->VData.Count++; + return; + } + PrevVNode = CurrentVNode; + CurrentVNode = CurrentVNode->Next; + ++VDataCount; + } + + if (VDataCount >= INSTR_PROF_MAX_NUM_VAL_PER_SITE) + return; + + CurrentVNode = (ValueProfNode *)calloc(1, sizeof(ValueProfNode)); + if (!CurrentVNode) + return; + + CurrentVNode->VData.Value = TargetValue; + CurrentVNode->VData.Count++; + + uint32_t Success = 0; + if (!ValueCounters[CounterIndex]) + Success = + COMPILER_RT_BOOL_CMPXCHG(&ValueCounters[CounterIndex], 0, CurrentVNode); + else if (PrevVNode && !PrevVNode->Next) + Success = COMPILER_RT_BOOL_CMPXCHG(&(PrevVNode->Next), 0, CurrentVNode); + + if (!Success) { + free(CurrentVNode); + return; + } +} + +COMPILER_RT_VISIBILITY ValueProfData ** +__llvm_profile_gather_value_data(uint64_t *ValueDataSize) { + size_t S = 0; + __llvm_profile_data *I; + ValueProfData **ValueDataArray; + + const __llvm_profile_data *DataEnd = __llvm_profile_end_data(); + const __llvm_profile_data *DataBegin = __llvm_profile_begin_data(); + + if (!ValueDataSize) + return NULL; + + ValueDataArray = + (ValueProfData **)calloc(DataEnd - DataBegin, sizeof(void *)); + if (!ValueDataArray) + PROF_OOM_RETURN("Failed to write value profile data "); + + /* + * Compute the total Size of the buffer to hold ValueProfData + * structures for functions with value profile data. + */ + for (I = (__llvm_profile_data *)DataBegin; I != DataEnd; ++I) { + ValueProfRuntimeRecord R; + if (initializeValueProfRuntimeRecord(&R, I->NumValueSites, I->Values)) + PROF_OOM_RETURN("Failed to write value profile data "); + + /* Compute the size of ValueProfData from this runtime record. */ + if (getNumValueKindsRT(&R) != 0) { + ValueProfData *VD = NULL; + uint32_t VS = getValueProfDataSizeRT(&R); + VD = (ValueProfData *)calloc(VS, sizeof(uint8_t)); + if (!VD) + PROF_OOM_RETURN("Failed to write value profile data "); + serializeValueProfDataFromRT(&R, VD); + ValueDataArray[I - DataBegin] = VD; + S += VS; + } + finalizeValueProfRuntimeRecord(&R); + } + + if (!S) { + free(ValueDataArray); + ValueDataArray = NULL; + } + + *ValueDataSize = S; + return ValueDataArray; +} diff --git a/contrib/compiler-rt/lib/profile/InstrProfilingWriter.c b/contrib/compiler-rt/lib/profile/InstrProfilingWriter.c new file mode 100644 index 000000000000..a07bc538ed4b --- /dev/null +++ b/contrib/compiler-rt/lib/profile/InstrProfilingWriter.c @@ -0,0 +1,175 @@ +/*===- InstrProfilingWriter.c - Write instrumentation to a file or buffer -===*\ +|* +|* The LLVM Compiler Infrastructure +|* +|* This file is distributed under the University of Illinois Open Source +|* License. See LICENSE.TXT for details. +|* +\*===----------------------------------------------------------------------===*/ + +#include "InstrProfiling.h" +#include "InstrProfilingInternal.h" +#include <string.h> + +#define INSTR_PROF_VALUE_PROF_DATA +#include "InstrProfData.inc" +void (*FreeHook)(void *) = NULL; +void* (*CallocHook)(size_t, size_t) = NULL; +uint32_t VPBufferSize = 0; + +/* The buffer writer is reponsponsible in keeping writer state + * across the call. + */ +COMPILER_RT_VISIBILITY uint32_t llvmBufferWriter(ProfDataIOVec *IOVecs, + uint32_t NumIOVecs, + void **WriterCtx) { + uint32_t I; + char **Buffer = (char **)WriterCtx; + for (I = 0; I < NumIOVecs; I++) { + size_t Length = IOVecs[I].ElmSize * IOVecs[I].NumElm; + memcpy(*Buffer, IOVecs[I].Data, Length); + *Buffer += Length; + } + return 0; +} + +static void llvmInitBufferIO(ProfBufferIO *BufferIO, WriterCallback FileWriter, + void *File, uint8_t *Buffer, uint32_t BufferSz) { + BufferIO->File = File; + BufferIO->FileWriter = FileWriter; + BufferIO->BufferStart = Buffer; + BufferIO->BufferSz = BufferSz; + BufferIO->CurOffset = 0; +} + +COMPILER_RT_VISIBILITY ProfBufferIO * +llvmCreateBufferIO(WriterCallback FileWriter, void *File, uint32_t BufferSz) { + ProfBufferIO *BufferIO = (ProfBufferIO *)CallocHook(1, sizeof(ProfBufferIO)); + uint8_t *Buffer = (uint8_t *)CallocHook(1, BufferSz); + if (!Buffer) { + FreeHook(BufferIO); + return 0; + } + llvmInitBufferIO(BufferIO, FileWriter, File, Buffer, BufferSz); + return BufferIO; +} + +COMPILER_RT_VISIBILITY void llvmDeleteBufferIO(ProfBufferIO *BufferIO) { + FreeHook(BufferIO->BufferStart); + FreeHook(BufferIO); +} + +COMPILER_RT_VISIBILITY int +llvmBufferIOWrite(ProfBufferIO *BufferIO, const uint8_t *Data, uint32_t Size) { + /* Buffer is not large enough, it is time to flush. */ + if (Size + BufferIO->CurOffset > BufferIO->BufferSz) { + if (llvmBufferIOFlush(BufferIO) != 0) + return -1; + } + /* Special case, bypass the buffer completely. */ + ProfDataIOVec IO[] = {{Data, sizeof(uint8_t), Size}}; + if (Size > BufferIO->BufferSz) { + if (BufferIO->FileWriter(IO, 1, &BufferIO->File)) + return -1; + } else { + /* Write the data to buffer */ + uint8_t *Buffer = BufferIO->BufferStart + BufferIO->CurOffset; + llvmBufferWriter(IO, 1, (void **)&Buffer); + BufferIO->CurOffset = Buffer - BufferIO->BufferStart; + } + return 0; +} + +COMPILER_RT_VISIBILITY int llvmBufferIOFlush(ProfBufferIO *BufferIO) { + if (BufferIO->CurOffset) { + ProfDataIOVec IO[] = { + {BufferIO->BufferStart, sizeof(uint8_t), BufferIO->CurOffset}}; + if (BufferIO->FileWriter(IO, 1, &BufferIO->File)) + return -1; + BufferIO->CurOffset = 0; + } + return 0; +} + +COMPILER_RT_VISIBILITY int llvmWriteProfData(WriterCallback Writer, + void *WriterCtx, + ValueProfData **ValueDataArray, + const uint64_t ValueDataSize) { + /* Match logic in __llvm_profile_write_buffer(). */ + const __llvm_profile_data *DataBegin = __llvm_profile_begin_data(); + const __llvm_profile_data *DataEnd = __llvm_profile_end_data(); + const uint64_t *CountersBegin = __llvm_profile_begin_counters(); + const uint64_t *CountersEnd = __llvm_profile_end_counters(); + const char *NamesBegin = __llvm_profile_begin_names(); + const char *NamesEnd = __llvm_profile_end_names(); + return llvmWriteProfDataImpl(Writer, WriterCtx, DataBegin, DataEnd, + CountersBegin, CountersEnd, ValueDataArray, + ValueDataSize, NamesBegin, NamesEnd); +} + +#define VP_BUFFER_SIZE 8 * 1024 +static int writeValueProfData(WriterCallback Writer, void *WriterCtx, + ValueProfData **ValueDataBegin, + uint64_t NumVData) { + ProfBufferIO *BufferIO; + uint32_t I = 0, BufferSz; + + if (!ValueDataBegin) + return 0; + + BufferSz = VPBufferSize ? VPBufferSize : VP_BUFFER_SIZE; + BufferIO = llvmCreateBufferIO(Writer, WriterCtx, BufferSz); + + for (I = 0; I < NumVData; I++) { + ValueProfData *CurVData = ValueDataBegin[I]; + if (!CurVData) + continue; + if (llvmBufferIOWrite(BufferIO, (const uint8_t *)CurVData, + CurVData->TotalSize) != 0) + return -1; + } + + if (llvmBufferIOFlush(BufferIO) != 0) + return -1; + llvmDeleteBufferIO(BufferIO); + + return 0; +} + +COMPILER_RT_VISIBILITY int llvmWriteProfDataImpl( + WriterCallback Writer, void *WriterCtx, + const __llvm_profile_data *DataBegin, const __llvm_profile_data *DataEnd, + const uint64_t *CountersBegin, const uint64_t *CountersEnd, + ValueProfData **ValueDataBegin, const uint64_t ValueDataSize, + const char *NamesBegin, const char *NamesEnd) { + + /* Calculate size of sections. */ + const uint64_t DataSize = DataEnd - DataBegin; + const uint64_t CountersSize = CountersEnd - CountersBegin; + const uint64_t NamesSize = NamesEnd - NamesBegin; + const uint64_t Padding = __llvm_profile_get_num_padding_bytes(NamesSize); + + /* Enough zeroes for padding. */ + const char Zeroes[sizeof(uint64_t)] = {0}; + + /* Create the header. */ + __llvm_profile_header Header; + + if (!DataSize) + return 0; + + /* Initialize header struture. */ +#define INSTR_PROF_RAW_HEADER(Type, Name, Init) Header.Name = Init; +#include "InstrProfData.inc" + + /* Write the data. */ + ProfDataIOVec IOVec[] = {{&Header, sizeof(__llvm_profile_header), 1}, + {DataBegin, sizeof(__llvm_profile_data), DataSize}, + {CountersBegin, sizeof(uint64_t), CountersSize}, + {NamesBegin, sizeof(uint8_t), NamesSize}, + {Zeroes, sizeof(uint8_t), Padding}}; + if (Writer(IOVec, sizeof(IOVec) / sizeof(*IOVec), &WriterCtx)) + return -1; + + return writeValueProfData(Writer, WriterCtx, ValueDataBegin, DataSize); +} diff --git a/contrib/compiler-rt/lib/profile/WindowsMMap.c b/contrib/compiler-rt/lib/profile/WindowsMMap.c new file mode 100644 index 000000000000..1f7342050032 --- /dev/null +++ b/contrib/compiler-rt/lib/profile/WindowsMMap.c @@ -0,0 +1,128 @@ +/* + * This code is derived from uClibc (original license follows). + * https://git.uclibc.org/uClibc/tree/utils/mmap-windows.c + */ + /* mmap() replacement for Windows + * + * Author: Mike Frysinger <vapier@gentoo.org> + * Placed into the public domain + */ + +/* References: + * CreateFileMapping: http://msdn.microsoft.com/en-us/library/aa366537(VS.85).aspx + * CloseHandle: http://msdn.microsoft.com/en-us/library/ms724211(VS.85).aspx + * MapViewOfFile: http://msdn.microsoft.com/en-us/library/aa366761(VS.85).aspx + * UnmapViewOfFile: http://msdn.microsoft.com/en-us/library/aa366882(VS.85).aspx + */ + +#if defined(_WIN32) + +#include "WindowsMMap.h" +#include "InstrProfiling.h" + +#ifdef __USE_FILE_OFFSET64 +# define DWORD_HI(x) (x >> 32) +# define DWORD_LO(x) ((x) & 0xffffffff) +#else +# define DWORD_HI(x) (0) +# define DWORD_LO(x) (x) +#endif + +COMPILER_RT_VISIBILITY +void *mmap(void *start, size_t length, int prot, int flags, int fd, off_t offset) +{ + if (prot & ~(PROT_READ | PROT_WRITE | PROT_EXEC)) + return MAP_FAILED; + if (fd == -1) { + if (!(flags & MAP_ANON) || offset) + return MAP_FAILED; + } else if (flags & MAP_ANON) + return MAP_FAILED; + + DWORD flProtect; + if (prot & PROT_WRITE) { + if (prot & PROT_EXEC) + flProtect = PAGE_EXECUTE_READWRITE; + else + flProtect = PAGE_READWRITE; + } else if (prot & PROT_EXEC) { + if (prot & PROT_READ) + flProtect = PAGE_EXECUTE_READ; + else if (prot & PROT_EXEC) + flProtect = PAGE_EXECUTE; + } else + flProtect = PAGE_READONLY; + + off_t end = length + offset; + HANDLE mmap_fd, h; + if (fd == -1) + mmap_fd = INVALID_HANDLE_VALUE; + else + mmap_fd = (HANDLE)_get_osfhandle(fd); + h = CreateFileMapping(mmap_fd, NULL, flProtect, DWORD_HI(end), DWORD_LO(end), NULL); + if (h == NULL) + return MAP_FAILED; + + DWORD dwDesiredAccess; + if (prot & PROT_WRITE) + dwDesiredAccess = FILE_MAP_WRITE; + else + dwDesiredAccess = FILE_MAP_READ; + if (prot & PROT_EXEC) + dwDesiredAccess |= FILE_MAP_EXECUTE; + if (flags & MAP_PRIVATE) + dwDesiredAccess |= FILE_MAP_COPY; + void *ret = MapViewOfFile(h, dwDesiredAccess, DWORD_HI(offset), DWORD_LO(offset), length); + if (ret == NULL) { + CloseHandle(h); + ret = MAP_FAILED; + } + return ret; +} + +COMPILER_RT_VISIBILITY +void munmap(void *addr, size_t length) +{ + UnmapViewOfFile(addr); + /* ruh-ro, we leaked handle from CreateFileMapping() ... */ +} + +COMPILER_RT_VISIBILITY +int msync(void *addr, size_t length, int flags) +{ + if (flags & MS_INVALIDATE) + return -1; /* Not supported. */ + + /* Exactly one of MS_ASYNC or MS_SYNC must be specified. */ + switch (flags & (MS_ASYNC | MS_SYNC)) { + case MS_SYNC: + case MS_ASYNC: + break; + default: + return -1; + } + + if (!FlushViewOfFile(addr, length)) + return -1; + + if (flags & MS_SYNC) { + /* FIXME: No longer have access to handle from CreateFileMapping(). */ + /* + * if (!FlushFileBuffers(h)) + * return -1; + */ + } + + return 0; +} + +COMPILER_RT_VISIBILITY +int flock(int fd, int operation) +{ + return -1; /* Not supported. */ +} + +#undef DWORD_HI +#undef DWORD_LO + +#endif /* _WIN32 */ diff --git a/contrib/compiler-rt/lib/profile/WindowsMMap.h b/contrib/compiler-rt/lib/profile/WindowsMMap.h new file mode 100644 index 000000000000..7b94eb28230c --- /dev/null +++ b/contrib/compiler-rt/lib/profile/WindowsMMap.h @@ -0,0 +1,65 @@ +/*===- WindowsMMap.h - Support library for PGO instrumentation ------------===*\ +|* +|* The LLVM Compiler Infrastructure +|* +|* This file is distributed under the University of Illinois Open Source +|* License. See LICENSE.TXT for details. +|* +\*===----------------------------------------------------------------------===*/ + +#ifndef PROFILE_INSTRPROFILING_WINDOWS_MMAP_H +#define PROFILE_INSTRPROFILING_WINDOWS_MMAP_H + +#if defined(_WIN32) + +#include <BaseTsd.h> +#include <io.h> +#include <sys/types.h> + +/* + * mmap() flags + */ +#define PROT_READ 0x1 +#define PROT_WRITE 0x2 +/* This flag is only available in WinXP+ */ +#ifdef FILE_MAP_EXECUTE +#define PROT_EXEC 0x4 +#else +#define PROT_EXEC 0x0 +#define FILE_MAP_EXECUTE 0 +#endif + +#define MAP_FILE 0x00 +#define MAP_SHARED 0x01 +#define MAP_PRIVATE 0x02 +#define MAP_ANONYMOUS 0x20 +#define MAP_ANON MAP_ANONYMOUS +#define MAP_FAILED ((void *) -1) + +/* + * msync() flags + */ +#define MS_ASYNC 0x0001 /* return immediately */ +#define MS_INVALIDATE 0x0002 /* invalidate all cached data */ +#define MS_SYNC 0x0010 /* msync synchronously */ + +/* + * flock() operations + */ +#define LOCK_SH 1 /* shared lock */ +#define LOCK_EX 2 /* exclusive lock */ +#define LOCK_NB 4 /* don't block when locking */ +#define LOCK_UN 8 /* unlock */ + +void *mmap(void *start, size_t length, int prot, int flags, int fd, + off_t offset); + +void munmap(void *addr, size_t length); + +int msync(void *addr, size_t length, int flags); + +int flock(int fd, int operation); + +#endif /* _WIN32 */ + +#endif /* PROFILE_INSTRPROFILING_WINDOWS_MMAP_H */ diff --git a/contrib/compiler-rt/lib/safestack/CMakeLists.txt b/contrib/compiler-rt/lib/safestack/CMakeLists.txt deleted file mode 100644 index 1c15d079dbb5..000000000000 --- a/contrib/compiler-rt/lib/safestack/CMakeLists.txt +++ /dev/null @@ -1,28 +0,0 @@ -add_custom_target(safestack) - -set(SAFESTACK_SOURCES safestack.cc) - -include_directories(..) - -set(SAFESTACK_CFLAGS ${SANITIZER_COMMON_CFLAGS}) - -if(APPLE) - # Build universal binary on APPLE. - add_compiler_rt_osx_static_runtime(clang_rt.safestack_osx - ARCH ${SAFESTACK_SUPPORTED_ARCH} - SOURCES ${SAFESTACK_SOURCES} - $<TARGET_OBJECTS:RTInterception.osx> - $<TARGET_OBJECTS:RTSanitizerCommon.osx> - CFLAGS ${SAFESTACK_CFLAGS}) - add_dependencies(safestack clang_rt.safestack_osx) -else() - # Otherwise, build separate libraries for each target. - foreach(arch ${SAFESTACK_SUPPORTED_ARCH}) - add_compiler_rt_runtime(clang_rt.safestack-${arch} ${arch} STATIC - SOURCES ${SAFESTACK_SOURCES} - $<TARGET_OBJECTS:RTInterception.${arch}> - $<TARGET_OBJECTS:RTSanitizerCommon.${arch}> - CFLAGS ${SAFESTACK_CFLAGS}) - add_dependencies(safestack clang_rt.safestack-${arch}) - endforeach() -endif() diff --git a/contrib/compiler-rt/lib/safestack/safestack.cc b/contrib/compiler-rt/lib/safestack/safestack.cc index 504bd3cd0d99..92c24b35d6d0 100644 --- a/contrib/compiler-rt/lib/safestack/safestack.cc +++ b/contrib/compiler-rt/lib/safestack/safestack.cc @@ -18,6 +18,7 @@ #include <pthread.h> #include <stddef.h> #include <stdint.h> +#include <unistd.h> #include <sys/resource.h> #include <sys/types.h> #include <sys/user.h> @@ -68,6 +69,9 @@ const unsigned kStackAlign = 16; /// size rlimit is set to infinity. const unsigned kDefaultUnsafeStackSize = 0x2800000; +/// Runtime page size obtained through sysconf +static unsigned pageSize; + // TODO: To make accessing the unsafe stack pointer faster, we plan to // eventually store it directly in the thread control block data structure on // platforms where this structure is pointed to by %fs or %gs. This is exactly @@ -171,7 +175,7 @@ INTERCEPTOR(int, pthread_create, pthread_t *thread, size_t size = 0; size_t guard = 0; - if (attr != NULL) { + if (attr) { pthread_attr_getstacksize(attr, &size); pthread_attr_getguardsize(attr, &guard); } else { @@ -185,7 +189,7 @@ INTERCEPTOR(int, pthread_create, pthread_t *thread, CHECK_NE(size, 0); CHECK_EQ((size & (kStackAlign - 1)), 0); - CHECK_EQ((guard & (PAGE_SIZE - 1)), 0); + CHECK_EQ((guard & (pageSize - 1)), 0); void *addr = unsafe_stack_alloc(size, guard); struct tinfo *tinfo = @@ -217,6 +221,7 @@ void __safestack_init() { void *addr = unsafe_stack_alloc(size, guard); unsafe_stack_setup(addr, size, guard); + pageSize = sysconf(_SC_PAGESIZE); // Initialize pthread interceptors for thread allocation INTERCEPT_FUNCTION(pthread_create); diff --git a/contrib/compiler-rt/lib/sanitizer_common/sanitizer_addrhashmap.h b/contrib/compiler-rt/lib/sanitizer_common/sanitizer_addrhashmap.h index acf4ff020939..e55fc4f95a9a 100644 --- a/contrib/compiler-rt/lib/sanitizer_common/sanitizer_addrhashmap.h +++ b/contrib/compiler-rt/lib/sanitizer_common/sanitizer_addrhashmap.h @@ -143,7 +143,7 @@ bool AddrHashMap<T, kSize>::Handle::created() const { template<typename T, uptr kSize> bool AddrHashMap<T, kSize>::Handle::exists() const { - return cell_ != 0; + return cell_ != nullptr; } template<typename T, uptr kSize> @@ -160,7 +160,7 @@ void AddrHashMap<T, kSize>::acquire(Handle *h) { h->created_ = false; h->addidx_ = -1U; h->bucket_ = b; - h->cell_ = 0; + h->cell_ = nullptr; // If we want to remove the element, we need exclusive access to the bucket, // so skip the lock-free phase. @@ -250,7 +250,7 @@ void AddrHashMap<T, kSize>::acquire(Handle *h) { } // Store in the add cells. - if (add == 0) { + if (!add) { // Allocate a new add array. const uptr kInitSize = 64; add = (AddBucket*)InternalAlloc(kInitSize); @@ -282,7 +282,7 @@ void AddrHashMap<T, kSize>::acquire(Handle *h) { template<typename T, uptr kSize> void AddrHashMap<T, kSize>::release(Handle *h) { - if (h->cell_ == 0) + if (!h->cell_) return; Bucket *b = h->bucket_; Cell *c = h->cell_; diff --git a/contrib/compiler-rt/lib/sanitizer_common/sanitizer_allocator.cc b/contrib/compiler-rt/lib/sanitizer_common/sanitizer_allocator.cc index 03b3e83153de..538e2db95d4e 100644 --- a/contrib/compiler-rt/lib/sanitizer_common/sanitizer_allocator.cc +++ b/contrib/compiler-rt/lib/sanitizer_common/sanitizer_allocator.cc @@ -11,6 +11,7 @@ // run-time libraries. // This allocator is used inside run-times. //===----------------------------------------------------------------------===// + #include "sanitizer_allocator.h" #include "sanitizer_allocator_internal.h" #include "sanitizer_common.h" @@ -44,7 +45,7 @@ InternalAllocator *internal_allocator() { return 0; } -#else // SANITIZER_GO +#else // SANITIZER_GO static ALIGNED(64) char internal_alloc_placeholder[sizeof(InternalAllocator)]; static atomic_uint8_t internal_allocator_initialized; @@ -77,29 +78,29 @@ static void *RawInternalAlloc(uptr size, InternalAllocatorCache *cache) { } static void RawInternalFree(void *ptr, InternalAllocatorCache *cache) { - if (cache == 0) { + if (!cache) { SpinMutexLock l(&internal_allocator_cache_mu); return internal_allocator()->Deallocate(&internal_allocator_cache, ptr); } internal_allocator()->Deallocate(cache, ptr); } -#endif // SANITIZER_GO +#endif // SANITIZER_GO const u64 kBlockMagic = 0x6A6CB03ABCEBC041ull; void *InternalAlloc(uptr size, InternalAllocatorCache *cache) { if (size + sizeof(u64) < size) - return 0; + return nullptr; void *p = RawInternalAlloc(size + sizeof(u64), cache); - if (p == 0) - return 0; + if (!p) + return nullptr; ((u64*)p)[0] = kBlockMagic; return (char*)p + sizeof(u64); } void InternalFree(void *addr, InternalAllocatorCache *cache) { - if (addr == 0) + if (!addr) return; addr = (char*)addr - sizeof(u64); CHECK_EQ(kBlockMagic, ((u64*)addr)[0]); @@ -147,4 +148,4 @@ void NORETURN ReportAllocatorCannotReturnNull() { Die(); } -} // namespace __sanitizer +} // namespace __sanitizer diff --git a/contrib/compiler-rt/lib/sanitizer_common/sanitizer_allocator.h b/contrib/compiler-rt/lib/sanitizer_common/sanitizer_allocator.h index deaffef7150d..44d6fce3b291 100644 --- a/contrib/compiler-rt/lib/sanitizer_common/sanitizer_allocator.h +++ b/contrib/compiler-rt/lib/sanitizer_common/sanitizer_allocator.h @@ -347,7 +347,7 @@ class SizeClassAllocator64 { CHECK_LT(class_id, kNumClasses); RegionInfo *region = GetRegionInfo(class_id); Batch *b = region->free_list.Pop(); - if (b == 0) + if (!b) b = PopulateFreeList(stat, c, class_id, region); region->n_allocated += b->count; return b; @@ -371,16 +371,16 @@ class SizeClassAllocator64 { void *GetBlockBegin(const void *p) { uptr class_id = GetSizeClass(p); uptr size = SizeClassMap::Size(class_id); - if (!size) return 0; + if (!size) return nullptr; uptr chunk_idx = GetChunkIdx((uptr)p, size); uptr reg_beg = (uptr)p & ~(kRegionSize - 1); uptr beg = chunk_idx * size; uptr next_beg = beg + size; - if (class_id >= kNumClasses) return 0; + if (class_id >= kNumClasses) return nullptr; RegionInfo *region = GetRegionInfo(class_id); if (region->mapped_user >= next_beg) return reinterpret_cast<void*>(reg_beg + beg); - return 0; + return nullptr; } static uptr GetActuallyAllocatedSize(void *p) { @@ -609,6 +609,7 @@ class TwoLevelByteMap { internal_memset(map1_, 0, sizeof(map1_)); mu_.Init(); } + void TestOnlyUnmap() { for (uptr i = 0; i < kSize1; i++) { u8 *p = Get(i); @@ -822,6 +823,10 @@ class SizeClassAllocator32 { void PrintStats() { } + static uptr AdditionalSize() { + return 0; + } + typedef SizeClassMap SizeClassMapT; static const uptr kNumClasses = SizeClassMap::kNumClasses; @@ -868,9 +873,9 @@ class SizeClassAllocator32 { uptr reg = AllocateRegion(stat, class_id); uptr n_chunks = kRegionSize / (size + kMetadataSize); uptr max_count = SizeClassMap::MaxCached(class_id); - Batch *b = 0; + Batch *b = nullptr; for (uptr i = reg; i < reg + n_chunks * size; i += size) { - if (b == 0) { + if (!b) { if (SizeClassMap::SizeClassRequiresSeparateTransferBatch(class_id)) b = (Batch*)c->Allocate(this, SizeClassMap::ClassID(sizeof(Batch))); else @@ -881,7 +886,7 @@ class SizeClassAllocator32 { if (b->count == max_count) { CHECK_GT(b->count, 0); sci->free_list.push_back(b); - b = 0; + b = nullptr; } } if (b) { @@ -1061,7 +1066,7 @@ class LargeMmapAllocator { void *ReturnNullOrDie() { if (atomic_load(&may_return_null_, memory_order_acquire)) - return 0; + return nullptr; ReportAllocatorCannotReturnNull(); } @@ -1101,7 +1106,7 @@ class LargeMmapAllocator { } bool PointerIsMine(const void *p) { - return GetBlockBegin(p) != 0; + return GetBlockBegin(p) != nullptr; } uptr GetActuallyAllocatedSize(void *p) { @@ -1130,13 +1135,13 @@ class LargeMmapAllocator { nearest_chunk = ch; } if (!nearest_chunk) - return 0; + return nullptr; Header *h = reinterpret_cast<Header *>(nearest_chunk); CHECK_GE(nearest_chunk, h->map_beg); CHECK_LT(nearest_chunk, h->map_beg + h->map_size); CHECK_LE(nearest_chunk, p); if (h->map_beg + h->map_size <= p) - return 0; + return nullptr; return GetUser(h); } @@ -1146,7 +1151,7 @@ class LargeMmapAllocator { mutex_.CheckLocked(); uptr p = reinterpret_cast<uptr>(ptr); uptr n = n_chunks_; - if (!n) return 0; + if (!n) return nullptr; if (!chunks_sorted_) { // Do one-time sort. chunks_sorted_ is reset in Allocate/Deallocate. SortArray(reinterpret_cast<uptr*>(chunks_), n); @@ -1158,7 +1163,7 @@ class LargeMmapAllocator { chunks_[n - 1]->map_size; } if (p < min_mmap_ || p >= max_mmap_) - return 0; + return nullptr; uptr beg = 0, end = n - 1; // This loop is a log(n) lower_bound. It does not check for the exact match // to avoid expensive cache-thrashing loads. @@ -1179,7 +1184,7 @@ class LargeMmapAllocator { Header *h = chunks_[beg]; if (h->map_beg + h->map_size <= p || p < h->map_beg) - return 0; + return nullptr; return GetUser(h); } @@ -1308,7 +1313,7 @@ class CombinedAllocator { void *ReturnNullOrDie() { if (MayReturnNull()) - return 0; + return nullptr; ReportAllocatorCannotReturnNull(); } @@ -1340,7 +1345,7 @@ class CombinedAllocator { return Allocate(cache, new_size, alignment); if (!new_size) { Deallocate(cache, p); - return 0; + return nullptr; } CHECK(PointerIsMine(p)); uptr old_size = GetActuallyAllocatedSize(p); @@ -1445,7 +1450,6 @@ class CombinedAllocator { // 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 +} // namespace __sanitizer +#endif // SANITIZER_ALLOCATOR_H diff --git a/contrib/compiler-rt/lib/sanitizer_common/sanitizer_allocator_internal.h b/contrib/compiler-rt/lib/sanitizer_common/sanitizer_allocator_internal.h index 9b9cfd0b5931..3dcfccd7cba3 100644 --- a/contrib/compiler-rt/lib/sanitizer_common/sanitizer_allocator_internal.h +++ b/contrib/compiler-rt/lib/sanitizer_common/sanitizer_allocator_internal.h @@ -1,4 +1,4 @@ -//===-- sanitizer_allocator_internal.h -------------------------- C++ -----===// +//===-- sanitizer_allocator_internal.h --------------------------*- C++ -*-===// // // The LLVM Compiler Infrastructure // @@ -45,19 +45,19 @@ typedef SizeClassAllocatorLocalCache<PrimaryInternalAllocator> typedef CombinedAllocator<PrimaryInternalAllocator, InternalAllocatorCache, LargeMmapAllocator<> > InternalAllocator; -void *InternalAlloc(uptr size, InternalAllocatorCache *cache = 0); -void InternalFree(void *p, InternalAllocatorCache *cache = 0); +void *InternalAlloc(uptr size, InternalAllocatorCache *cache = nullptr); +void InternalFree(void *p, InternalAllocatorCache *cache = nullptr); InternalAllocator *internal_allocator(); enum InternalAllocEnum { INTERNAL_ALLOC }; -} // namespace __sanitizer +} // namespace __sanitizer inline void *operator new(__sanitizer::operator_new_size_type size, InternalAllocEnum) { return InternalAlloc(size); } -#endif // SANITIZER_ALLOCATOR_INTERNAL_H +#endif // SANITIZER_ALLOCATOR_INTERNAL_H diff --git a/contrib/compiler-rt/lib/sanitizer_common/sanitizer_asm.h b/contrib/compiler-rt/lib/sanitizer_common/sanitizer_asm.h index 906012a96f11..47c2b12a2049 100644 --- a/contrib/compiler-rt/lib/sanitizer_common/sanitizer_asm.h +++ b/contrib/compiler-rt/lib/sanitizer_common/sanitizer_asm.h @@ -23,8 +23,11 @@ # define CFI_STARTPROC .cfi_startproc # define CFI_ENDPROC .cfi_endproc # define CFI_ADJUST_CFA_OFFSET(n) .cfi_adjust_cfa_offset n +# define CFI_DEF_CFA_OFFSET(n) .cfi_def_cfa_offset n # define CFI_REL_OFFSET(reg, n) .cfi_rel_offset reg, n +# define CFI_OFFSET(reg, n) .cfi_offset reg, n # define CFI_DEF_CFA_REGISTER(reg) .cfi_def_cfa_register reg +# define CFI_DEF_CFA(reg, n) .cfi_def_cfa reg, n # define CFI_RESTORE(reg) .cfi_restore reg #else // No CFI @@ -32,9 +35,24 @@ # define CFI_STARTPROC # define CFI_ENDPROC # define CFI_ADJUST_CFA_OFFSET(n) +# define CFI_DEF_CFA_OFFSET(n) # define CFI_REL_OFFSET(reg, n) +# define CFI_OFFSET(reg, n) # define CFI_DEF_CFA_REGISTER(reg) +# define CFI_DEF_CFA(reg, n) # define CFI_RESTORE(reg) #endif - +#if !defined(__APPLE__) +# define ASM_HIDDEN(symbol) .hidden symbol +# define ASM_TYPE_FUNCTION(symbol) .type symbol, @function +# define ASM_SIZE(symbol) .size symbol, .-symbol +# define ASM_TSAN_SYMBOL(symbol) symbol +# define ASM_TSAN_SYMBOL_INTERCEPTOR(symbol) symbol +#else +# define ASM_HIDDEN(symbol) +# define ASM_TYPE_FUNCTION(symbol) +# define ASM_SIZE(symbol) +# define ASM_TSAN_SYMBOL(symbol) _##symbol +# define ASM_TSAN_SYMBOL_INTERCEPTOR(symbol) _wrap_##symbol +#endif diff --git a/contrib/compiler-rt/lib/sanitizer_common/sanitizer_atomic.h b/contrib/compiler-rt/lib/sanitizer_common/sanitizer_atomic.h index 7e3374aadd0c..b26693e24f8d 100644 --- a/contrib/compiler-rt/lib/sanitizer_common/sanitizer_atomic.h +++ b/contrib/compiler-rt/lib/sanitizer_common/sanitizer_atomic.h @@ -63,4 +63,20 @@ struct atomic_uintptr_t { # error "Unsupported compiler" #endif +namespace __sanitizer { + +// Clutter-reducing helpers. + +template<typename T> +INLINE typename T::Type atomic_load_relaxed(const volatile T *a) { + return atomic_load(a, memory_order_relaxed); +} + +template<typename T> +INLINE void atomic_store_relaxed(volatile T *a, typename T::Type v) { + atomic_store(a, v, memory_order_relaxed); +} + +} // namespace __sanitizer + #endif // SANITIZER_ATOMIC_H diff --git a/contrib/compiler-rt/lib/sanitizer_common/sanitizer_common.cc b/contrib/compiler-rt/lib/sanitizer_common/sanitizer_common.cc index d14e98824d99..9b41a3aa0af9 100644 --- a/contrib/compiler-rt/lib/sanitizer_common/sanitizer_common.cc +++ b/contrib/compiler-rt/lib/sanitizer_common/sanitizer_common.cc @@ -57,7 +57,7 @@ void ReportFile::ReopenIfNecessary() { CloseFile(fd); } - const char *exe_name = GetBinaryBasename(); + const char *exe_name = GetProcessName(); if (common_flags()->log_exe_name && exe_name) { internal_snprintf(full_path, kMaxPathLength, "%s.%s.%zu", path_prefix, exe_name, pid); @@ -105,24 +105,47 @@ uptr stoptheworld_tracer_pid = 0; // writing to the same log file. uptr stoptheworld_tracer_ppid = 0; -static DieCallbackType InternalDieCallback, UserDieCallback; -void SetDieCallback(DieCallbackType callback) { - InternalDieCallback = callback; +static const int kMaxNumOfInternalDieCallbacks = 5; +static DieCallbackType InternalDieCallbacks[kMaxNumOfInternalDieCallbacks]; + +bool AddDieCallback(DieCallbackType callback) { + for (int i = 0; i < kMaxNumOfInternalDieCallbacks; i++) { + if (InternalDieCallbacks[i] == nullptr) { + InternalDieCallbacks[i] = callback; + return true; + } + } + return false; } -void SetUserDieCallback(DieCallbackType callback) { - UserDieCallback = callback; + +bool RemoveDieCallback(DieCallbackType callback) { + for (int i = 0; i < kMaxNumOfInternalDieCallbacks; i++) { + if (InternalDieCallbacks[i] == callback) { + internal_memmove(&InternalDieCallbacks[i], &InternalDieCallbacks[i + 1], + sizeof(InternalDieCallbacks[0]) * + (kMaxNumOfInternalDieCallbacks - i - 1)); + InternalDieCallbacks[kMaxNumOfInternalDieCallbacks - 1] = nullptr; + return true; + } + } + return false; } -DieCallbackType GetDieCallback() { - return InternalDieCallback; +static DieCallbackType UserDieCallback; +void SetUserDieCallback(DieCallbackType callback) { + UserDieCallback = callback; } void NORETURN Die() { if (UserDieCallback) UserDieCallback(); - if (InternalDieCallback) - InternalDieCallback(); - internal__exit(1); + for (int i = kMaxNumOfInternalDieCallbacks - 1; i >= 0; i--) { + if (InternalDieCallbacks[i]) + InternalDieCallbacks[i](); + } + if (common_flags()->abort_on_error) + Abort(); + internal__exit(common_flags()->exitcode); } static CheckFailedCallbackType CheckFailedCallback; @@ -140,40 +163,60 @@ void NORETURN CheckFailed(const char *file, int line, const char *cond, Die(); } -uptr ReadFileToBuffer(const char *file_name, char **buff, uptr *buff_size, - uptr max_len, error_t *errno_p) { +void NORETURN ReportMmapFailureAndDie(uptr size, const char *mem_type, + const char *mmap_type, error_t err, + bool raw_report) { + static int recursion_count; + if (raw_report || recursion_count) { + // If raw report is requested or we went into recursion, just die. + // The Report() and CHECK calls below may call mmap recursively and fail. + RawWrite("ERROR: Failed to mmap\n"); + Die(); + } + recursion_count++; + Report("ERROR: %s failed to " + "%s 0x%zx (%zd) bytes of %s (error code: %d)\n", + SanitizerToolName, mmap_type, size, size, mem_type, err); +#ifndef SANITIZER_GO + DumpProcessMap(); +#endif + UNREACHABLE("unable to mmap"); +} + +bool ReadFileToBuffer(const char *file_name, char **buff, uptr *buff_size, + uptr *read_len, uptr max_len, error_t *errno_p) { uptr PageSize = GetPageSizeCached(); uptr kMinFileLen = PageSize; - uptr read_len = 0; - *buff = 0; + *buff = nullptr; *buff_size = 0; + *read_len = 0; // The files we usually open are not seekable, so try different buffer sizes. for (uptr size = kMinFileLen; size <= max_len; size *= 2) { fd_t fd = OpenFile(file_name, RdOnly, errno_p); - if (fd == kInvalidFd) return 0; + if (fd == kInvalidFd) return false; UnmapOrDie(*buff, *buff_size); *buff = (char*)MmapOrDie(size, __func__); *buff_size = size; + *read_len = 0; // Read up to one page at a time. - read_len = 0; bool reached_eof = false; - while (read_len + PageSize <= size) { + while (*read_len + PageSize <= size) { uptr just_read; - if (!ReadFromFile(fd, *buff + read_len, PageSize, &just_read, errno_p)) { + if (!ReadFromFile(fd, *buff + *read_len, PageSize, &just_read, errno_p)) { UnmapOrDie(*buff, *buff_size); - return 0; + return false; } if (just_read == 0) { reached_eof = true; break; } - read_len += just_read; + *read_len += just_read; } CloseFile(fd); if (reached_eof) // We've read the whole file. break; } - return read_len; + return true; } typedef bool UptrComparisonFunction(const uptr &a, const uptr &b); @@ -210,8 +253,8 @@ void *MmapAlignedOrDie(uptr size, uptr alignment, const char *mem_type) { const char *StripPathPrefix(const char *filepath, const char *strip_path_prefix) { - if (filepath == 0) return 0; - if (strip_path_prefix == 0) return filepath; + if (!filepath) return nullptr; + if (!strip_path_prefix) return filepath; const char *res = filepath; if (const char *pos = internal_strstr(filepath, strip_path_prefix)) res = pos + internal_strlen(strip_path_prefix); @@ -221,8 +264,8 @@ const char *StripPathPrefix(const char *filepath, } const char *StripModuleName(const char *module) { - if (module == 0) - return 0; + if (!module) + return nullptr; if (SANITIZER_WINDOWS) { // On Windows, both slash and backslash are possible. // Pick the one that goes last. @@ -255,6 +298,40 @@ void ReportErrorSummary(const char *error_type, const AddressInfo &info) { } #endif +// Removes the ANSI escape sequences from the input string (in-place). +void RemoveANSIEscapeSequencesFromString(char *str) { + if (!str) + return; + + // We are going to remove the escape sequences in place. + char *s = str; + char *z = str; + while (*s != '\0') { + CHECK_GE(s, z); + // Skip over ANSI escape sequences with pointer 's'. + if (*s == '\033' && *(s + 1) == '[') { + s = internal_strchrnul(s, 'm'); + if (*s == '\0') { + break; + } + s++; + continue; + } + // 's' now points at a character we want to keep. Copy over the buffer + // content if the escape sequence has been perviously skipped andadvance + // both pointers. + if (s != z) + *z = *s; + + // If we have not seen an escape sequence, just advance both pointers. + z++; + s++; + } + + // Null terminate the string. + *z = '\0'; +} + void LoadedModule::set(const char *module_name, uptr base_address) { clear(); full_name_ = internal_strdup(module_name); @@ -303,7 +380,7 @@ void DecreaseTotalMmap(uptr size) { } bool TemplateMatch(const char *templ, const char *str) { - if (str == 0 || str[0] == 0) + if ((!str) || str[0] == 0) return false; bool start = false; if (templ && templ[0] == '^') { @@ -324,9 +401,9 @@ bool TemplateMatch(const char *templ, const char *str) { return false; char *tpos = (char*)internal_strchr(templ, '*'); char *tpos1 = (char*)internal_strchr(templ, '$'); - if (tpos == 0 || (tpos1 && tpos1 < tpos)) + if ((!tpos) || (tpos1 && tpos1 < tpos)) tpos = tpos1; - if (tpos != 0) + if (tpos) tpos[0] = 0; const char *str0 = str; const char *spos = internal_strstr(str, templ); @@ -334,7 +411,7 @@ bool TemplateMatch(const char *templ, const char *str) { templ = tpos; if (tpos) tpos[0] = tpos == tpos1 ? '$' : '*'; - if (spos == 0) + if (!spos) return false; if (start && spos != str0) return false; @@ -344,11 +421,52 @@ bool TemplateMatch(const char *templ, const char *str) { return true; } +static const char kPathSeparator = SANITIZER_WINDOWS ? ';' : ':'; + +char *FindPathToBinary(const char *name) { + const char *path = GetEnv("PATH"); + if (!path) + return nullptr; + uptr name_len = internal_strlen(name); + InternalScopedBuffer<char> buffer(kMaxPathLength); + const char *beg = path; + while (true) { + const char *end = internal_strchrnul(beg, kPathSeparator); + uptr prefix_len = end - beg; + if (prefix_len + name_len + 2 <= kMaxPathLength) { + internal_memcpy(buffer.data(), beg, prefix_len); + buffer[prefix_len] = '/'; + internal_memcpy(&buffer[prefix_len + 1], name, name_len); + buffer[prefix_len + 1 + name_len] = '\0'; + if (FileExists(buffer.data())) + return internal_strdup(buffer.data()); + } + if (*end == '\0') break; + beg = end + 1; + } + return nullptr; +} + static char binary_name_cache_str[kMaxPathLength]; -static const char *binary_basename_cache_str; +static char process_name_cache_str[kMaxPathLength]; + +const char *GetProcessName() { + return process_name_cache_str; +} + +static uptr ReadProcessName(/*out*/ char *buf, uptr buf_len) { + ReadLongProcessName(buf, buf_len); + char *s = const_cast<char *>(StripModuleName(buf)); + uptr len = internal_strlen(s); + if (s != buf) { + internal_memmove(buf, s, len); + buf[len] = '\0'; + } + return len; +} -const char *GetBinaryBasename() { - return binary_basename_cache_str; +void UpdateProcessName() { + ReadProcessName(process_name_cache_str, sizeof(process_name_cache_str)); } // Call once to make sure that binary_name_cache_str is initialized @@ -356,7 +474,7 @@ void CacheBinaryName() { if (binary_name_cache_str[0] != '\0') return; ReadBinaryName(binary_name_cache_str, sizeof(binary_name_cache_str)); - binary_basename_cache_str = StripModuleName(binary_name_cache_str); + ReadProcessName(process_name_cache_str, sizeof(process_name_cache_str)); } uptr ReadBinaryNameCached(/*out*/char *buf, uptr buf_len) { @@ -370,7 +488,7 @@ uptr ReadBinaryNameCached(/*out*/char *buf, uptr buf_len) { return name_len; } -} // namespace __sanitizer +} // namespace __sanitizer using namespace __sanitizer; // NOLINT @@ -387,4 +505,4 @@ SANITIZER_INTERFACE_ATTRIBUTE void __sanitizer_set_death_callback(void (*callback)(void)) { SetUserDieCallback(callback); } -} // extern "C" +} // extern "C" diff --git a/contrib/compiler-rt/lib/sanitizer_common/sanitizer_common.h b/contrib/compiler-rt/lib/sanitizer_common/sanitizer_common.h index 2c5a8dbe1238..7e80507ba0cf 100644 --- a/contrib/compiler-rt/lib/sanitizer_common/sanitizer_common.h +++ b/contrib/compiler-rt/lib/sanitizer_common/sanitizer_common.h @@ -49,6 +49,8 @@ static const uptr kMaxNumberOfModules = 1 << 14; const uptr kMaxThreadStackSize = 1 << 30; // 1Gb +static const uptr kErrorMessageBufferSize = 1 << 16; + // Denotes fake PC values that come from JIT/JAVA/etc. // For such PC values __tsan_symbolize_external() will be called. const u64 kExternalPCBit = 1ULL << 60; @@ -76,7 +78,10 @@ void GetThreadStackAndTls(bool main, uptr *stk_addr, uptr *stk_size, uptr *tls_addr, uptr *tls_size); // Memory management -void *MmapOrDie(uptr size, const char *mem_type); +void *MmapOrDie(uptr size, const char *mem_type, bool raw_report = false); +INLINE void *MmapOrDieQuietly(uptr size, const char *mem_type) { + return MmapOrDie(size, mem_type, /*raw_report*/ true); +} void UnmapOrDie(void *addr, uptr size); void *MmapFixedNoReserve(uptr fixed_addr, uptr size, const char *name = nullptr); @@ -97,6 +102,8 @@ void DecreaseTotalMmap(uptr size); uptr GetRSS(); void NoHugePagesInRegion(uptr addr, uptr length); void DontDumpShadowMemory(uptr addr, uptr length); +// Check if the built VMA size matches the runtime one. +void CheckVMASize(); // InternalScopedBuffer can be used instead of large stack arrays to // keep frame size low. @@ -160,6 +167,7 @@ void SetLowLevelAllocateCallback(LowLevelAllocateCallback callback); // IO void RawWrite(const char *buffer); bool ColorizeReports(); +void RemoveANSIEscapeSequencesFromString(char *buffer); void Printf(const char *format, ...); void Report(const char *format, ...); void SetPrintfAndReportCallback(void (*callback)(const char *)); @@ -224,14 +232,23 @@ bool WriteToFile(fd_t fd, const void *buff, uptr buff_size, bool RenameFile(const char *oldpath, const char *newpath, error_t *error_p = nullptr); +// Scoped file handle closer. +struct FileCloser { + explicit FileCloser(fd_t fd) : fd(fd) {} + ~FileCloser() { CloseFile(fd); } + fd_t fd; +}; + bool SupportsColoredOutput(fd_t fd); // Opens the file 'file_name" and reads up to 'max_len' bytes. // The resulting buffer is mmaped and stored in '*buff'. -// The size of the mmaped region is stored in '*buff_size', -// Returns the number of read bytes or 0 if file can not be opened. -uptr ReadFileToBuffer(const char *file_name, char **buff, uptr *buff_size, - uptr max_len, error_t *errno_p = nullptr); +// The size of the mmaped region is stored in '*buff_size'. +// The total number of read bytes is stored in '*read_len'. +// Returns true if file was successfully opened and read. +bool ReadFileToBuffer(const char *file_name, char **buff, uptr *buff_size, + uptr *read_len, uptr max_len = 1 << 26, + error_t *errno_p = nullptr); // Maps given file to virtual memory, and returns pointer to it // (or NULL if mapping fails). Stores the size of mmaped region // in '*buff_size'. @@ -249,7 +266,9 @@ const char *StripModuleName(const char *module); // OS uptr ReadBinaryName(/*out*/char *buf, uptr buf_len); uptr ReadBinaryNameCached(/*out*/char *buf, uptr buf_len); -const char *GetBinaryBasename(); +uptr ReadLongProcessName(/*out*/ char *buf, uptr buf_len); +const char *GetProcessName(); +void UpdateProcessName(); void CacheBinaryName(); void DisableCoreDumperIfNecessary(); void DumpProcessMap(); @@ -295,6 +314,9 @@ void NORETURN Abort(); void NORETURN Die(); void NORETURN CheckFailed(const char *file, int line, const char *cond, u64 v1, u64 v2); +void NORETURN ReportMmapFailureAndDie(uptr size, const char *mem_type, + const char *mmap_type, error_t err, + bool raw_report = false); // Set the name of the current thread to 'name', return true on succees. // The name may be truncated to a system-dependent limit. @@ -306,9 +328,16 @@ bool SanitizerGetThreadName(char *name, int max_len); // Specific tools may override behavior of "Die" and "CheckFailed" functions // to do tool-specific job. typedef void (*DieCallbackType)(void); -void SetDieCallback(DieCallbackType); -void SetUserDieCallback(DieCallbackType); -DieCallbackType GetDieCallback(); + +// It's possible to add several callbacks that would be run when "Die" is +// called. The callbacks will be run in the opposite order. The tools are +// strongly recommended to setup all callbacks during initialization, when there +// is only a single thread. +bool AddDieCallback(DieCallbackType callback); +bool RemoveDieCallback(DieCallbackType callback); + +void SetUserDieCallback(DieCallbackType callback); + typedef void (*CheckFailedCallbackType)(const char *, int, const char *, u64, u64); void SetCheckFailedCallback(CheckFailedCallbackType callback); @@ -400,7 +429,7 @@ INLINE uptr RoundUpToPowerOfTwo(uptr size) { } INLINE uptr RoundUpTo(uptr size, uptr boundary) { - CHECK(IsPowerOfTwo(boundary)); + RAW_CHECK(IsPowerOfTwo(boundary)); return (size + boundary - 1) & ~(boundary - 1); } @@ -626,17 +655,34 @@ enum AndroidApiLevel { ANDROID_POST_LOLLIPOP = 23 }; -#if SANITIZER_ANDROID +void WriteToSyslog(const char *buffer); + +#if SANITIZER_MAC +void LogFullErrorReport(const char *buffer); +#else +INLINE void LogFullErrorReport(const char *buffer) {} +#endif + +#if SANITIZER_LINUX || SANITIZER_MAC +void WriteOneLineToSyslog(const char *s); +void LogMessageOnPrintf(const char *str); +#else +INLINE void WriteOneLineToSyslog(const char *s) {} +INLINE void LogMessageOnPrintf(const char *str) {} +#endif + +#if SANITIZER_LINUX // Initialize Android logging. Any writes before this are silently lost. void AndroidLogInit(); -void AndroidLogWrite(const char *buffer); -void GetExtraActivationFlags(char *buf, uptr size); +#else +INLINE void AndroidLogInit() {} +#endif + +#if SANITIZER_ANDROID void SanitizerInitializeUnwinder(); AndroidApiLevel AndroidGetApiLevel(); #else -INLINE void AndroidLogInit() {} INLINE void AndroidLogWrite(const char *buffer_unused) {} -INLINE void GetExtraActivationFlags(char *buf, uptr size) { *buf = '\0'; } INLINE void SanitizerInitializeUnwinder() {} INLINE AndroidApiLevel AndroidGetApiLevel() { return ANDROID_NOT_ANDROID; } #endif @@ -685,6 +731,9 @@ struct SignalContext { void GetPcSpBp(void *context, uptr *pc, uptr *sp, uptr *bp); +void DisableReexec(); +void MaybeReexec(); + } // namespace __sanitizer inline void *operator new(__sanitizer::operator_new_size_type size, diff --git a/contrib/compiler-rt/lib/sanitizer_common/sanitizer_common_interceptors.inc b/contrib/compiler-rt/lib/sanitizer_common/sanitizer_common_interceptors.inc index a7772b7394a5..2a748cdc6852 100644 --- a/contrib/compiler-rt/lib/sanitizer_common/sanitizer_common_interceptors.inc +++ b/contrib/compiler-rt/lib/sanitizer_common/sanitizer_common_interceptors.inc @@ -31,6 +31,7 @@ // COMMON_INTERCEPTOR_HANDLE_RECVMSG // COMMON_INTERCEPTOR_NOTHING_IS_INITIALIZED //===----------------------------------------------------------------------===// + #include "interception/interception.h" #include "sanitizer_addrhashmap.h" #include "sanitizer_placement_new.h" @@ -39,6 +40,22 @@ #include <stdarg.h> +#if SANITIZER_INTERCEPTOR_HOOKS +#define CALL_WEAK_INTERCEPTOR_HOOK(f, ...) \ + do { \ + if (f) \ + f(__VA_ARGS__); \ + } while (false); +#define DECLARE_WEAK_INTERCEPTOR_HOOK(f, ...) \ + extern "C" { \ + SANITIZER_INTERFACE_ATTRIBUTE SANITIZER_WEAK_ATTRIBUTE void f(__VA_ARGS__); \ + } // extern "C" +#else +#define DECLARE_WEAK_INTERCEPTOR_HOOK(f, ...) +#define CALL_WEAK_INTERCEPTOR_HOOK(f, ...) + +#endif // SANITIZER_INTERCEPTOR_HOOKS + #if SANITIZER_WINDOWS && !defined(va_copy) #define va_copy(dst, src) ((dst) = (src)) #endif // _WIN32 @@ -118,6 +135,14 @@ #define COMMON_INTERCEPTOR_GET_TLS_RANGE(begin, end) *begin = *end = 0; #endif +#ifndef COMMON_INTERCEPTOR_ACQUIRE +#define COMMON_INTERCEPTOR_ACQUIRE(ctx, u) {} +#endif + +#ifndef COMMON_INTERCEPTOR_RELEASE +#define COMMON_INTERCEPTOR_RELEASE(ctx, u) {} +#endif + struct FileMetadata { // For open_memstream(). char **addr; @@ -188,6 +213,9 @@ static inline int CharCmpX(unsigned char c1, unsigned char c2) { return (c1 == c2) ? 0 : (c1 < c2) ? -1 : 1; } +DECLARE_WEAK_INTERCEPTOR_HOOK(__sanitizer_weak_hook_strcmp, uptr called_pc, + const char *s1, const char *s2, int result) + INTERCEPTOR(int, strcmp, const char *s1, const char *s2) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, strcmp, s1, s2); @@ -200,9 +228,16 @@ INTERCEPTOR(int, strcmp, const char *s1, const char *s2) { } COMMON_INTERCEPTOR_READ_STRING(ctx, s1, i + 1); COMMON_INTERCEPTOR_READ_STRING(ctx, s2, i + 1); - return CharCmpX(c1, c2); + int result = CharCmpX(c1, c2); + CALL_WEAK_INTERCEPTOR_HOOK(__sanitizer_weak_hook_strcmp, GET_CALLER_PC(), s1, + s2, result); + return result; } +DECLARE_WEAK_INTERCEPTOR_HOOK(__sanitizer_weak_hook_strncmp, uptr called_pc, + const char *s1, const char *s2, uptr n, + int result) + INTERCEPTOR(int, strncmp, const char *s1, const char *s2, uptr size) { if (COMMON_INTERCEPTOR_NOTHING_IS_INITIALIZED) return internal_strncmp(s1, s2, size); @@ -217,7 +252,10 @@ INTERCEPTOR(int, strncmp, const char *s1, const char *s2, uptr size) { } COMMON_INTERCEPTOR_READ_RANGE(ctx, s1, Min(i + 1, size)); COMMON_INTERCEPTOR_READ_RANGE(ctx, s2, Min(i + 1, size)); - return CharCmpX(c1, c2); + int result = CharCmpX(c1, c2); + CALL_WEAK_INTERCEPTOR_HOOK(__sanitizer_weak_hook_strncmp, GET_CALLER_PC(), s1, + s2, size, result); + return result; } #define INIT_STRCMP COMMON_INTERCEPT_FUNCTION(strcmp) @@ -362,8 +400,57 @@ INTERCEPTOR(char *, strpbrk, const char *s1, const char *s2) { #define INIT_STRPBRK #endif +#if SANITIZER_INTERCEPT_MEMCMP + +DECLARE_WEAK_INTERCEPTOR_HOOK(__sanitizer_weak_hook_memcmp, uptr called_pc, + const void *s1, const void *s2, uptr n, + int result) + +INTERCEPTOR(int, memcmp, const void *a1, const void *a2, uptr size) { + if (COMMON_INTERCEPTOR_NOTHING_IS_INITIALIZED) + return internal_memcmp(a1, a2, size); + void *ctx; + COMMON_INTERCEPTOR_ENTER(ctx, memcmp, a1, a2, size); + if (common_flags()->intercept_memcmp) { + if (common_flags()->strict_memcmp) { + // Check the entire regions even if the first bytes of the buffers are + // different. + COMMON_INTERCEPTOR_READ_RANGE(ctx, a1, size); + COMMON_INTERCEPTOR_READ_RANGE(ctx, a2, size); + // Fallthrough to REAL(memcmp) below. + } else { + unsigned char c1 = 0, c2 = 0; + const unsigned char *s1 = (const unsigned char*)a1; + const unsigned char *s2 = (const unsigned char*)a2; + uptr i; + for (i = 0; i < size; i++) { + c1 = s1[i]; + c2 = s2[i]; + if (c1 != c2) break; + } + COMMON_INTERCEPTOR_READ_RANGE(ctx, s1, Min(i + 1, size)); + COMMON_INTERCEPTOR_READ_RANGE(ctx, s2, Min(i + 1, size)); + int r = CharCmpX(c1, c2); + CALL_WEAK_INTERCEPTOR_HOOK(__sanitizer_weak_hook_memcmp, GET_CALLER_PC(), + a1, a2, size, r); + return r; + } + } + int result = REAL(memcmp(a1, a2, size)); + CALL_WEAK_INTERCEPTOR_HOOK(__sanitizer_weak_hook_memcmp, GET_CALLER_PC(), a1, + a2, size, result); + return result; +} + +#define INIT_MEMCMP COMMON_INTERCEPT_FUNCTION(memcmp) +#else +#define INIT_MEMCMP +#endif + #if SANITIZER_INTERCEPT_MEMCHR INTERCEPTOR(void*, memchr, const void *s, int c, SIZE_T n) { + if (COMMON_INTERCEPTOR_NOTHING_IS_INITIALIZED) + return internal_memchr(s, c, n); void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, memchr, s, c, n); void *res = REAL(memchr)(s, c, n); @@ -411,7 +498,7 @@ INTERCEPTOR(float, frexpf, float x, int *exp) { COMMON_INTERCEPTOR_ENTER(ctx, frexpf, x, exp); // FIXME: under ASan the call below may write to freed memory and corrupt // its metadata. See - // https://code.google.com/p/address-sanitizer/issues/detail?id=321. + // https://github.com/google/sanitizers/issues/321. float res = REAL(frexpf)(x, exp); COMMON_INTERCEPTOR_WRITE_RANGE(ctx, exp, sizeof(*exp)); return res; @@ -422,7 +509,7 @@ INTERCEPTOR(long double, frexpl, long double x, int *exp) { COMMON_INTERCEPTOR_ENTER(ctx, frexpl, x, exp); // FIXME: under ASan the call below may write to freed memory and corrupt // its metadata. See - // https://code.google.com/p/address-sanitizer/issues/detail?id=321. + // https://github.com/google/sanitizers/issues/321. long double res = REAL(frexpl)(x, exp); COMMON_INTERCEPTOR_WRITE_RANGE(ctx, exp, sizeof(*exp)); return res; @@ -463,7 +550,7 @@ INTERCEPTOR(SSIZE_T, read, int fd, void *ptr, SIZE_T count) { COMMON_INTERCEPTOR_FD_ACCESS(ctx, fd); // FIXME: under ASan the call below may write to freed memory and corrupt // its metadata. See - // https://code.google.com/p/address-sanitizer/issues/detail?id=321. + // https://github.com/google/sanitizers/issues/321. SSIZE_T res = REAL(read)(fd, ptr, count); if (res > 0) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, ptr, res); if (res >= 0 && fd >= 0) COMMON_INTERCEPTOR_FD_ACQUIRE(ctx, fd); @@ -481,7 +568,7 @@ INTERCEPTOR(SSIZE_T, pread, int fd, void *ptr, SIZE_T count, OFF_T offset) { COMMON_INTERCEPTOR_FD_ACCESS(ctx, fd); // FIXME: under ASan the call below may write to freed memory and corrupt // its metadata. See - // https://code.google.com/p/address-sanitizer/issues/detail?id=321. + // https://github.com/google/sanitizers/issues/321. SSIZE_T res = REAL(pread)(fd, ptr, count, offset); if (res > 0) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, ptr, res); if (res >= 0 && fd >= 0) COMMON_INTERCEPTOR_FD_ACQUIRE(ctx, fd); @@ -499,7 +586,7 @@ INTERCEPTOR(SSIZE_T, pread64, int fd, void *ptr, SIZE_T count, OFF64_T offset) { COMMON_INTERCEPTOR_FD_ACCESS(ctx, fd); // FIXME: under ASan the call below may write to freed memory and corrupt // its metadata. See - // https://code.google.com/p/address-sanitizer/issues/detail?id=321. + // https://github.com/google/sanitizers/issues/321. SSIZE_T res = REAL(pread64)(fd, ptr, count, offset); if (res > 0) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, ptr, res); if (res >= 0 && fd >= 0) COMMON_INTERCEPTOR_FD_ACQUIRE(ctx, fd); @@ -746,7 +833,7 @@ INTERCEPTOR(char *, ctime, unsigned long *timep) { COMMON_INTERCEPTOR_ENTER(ctx, ctime, timep); // FIXME: under ASan the call below may write to freed memory and corrupt // its metadata. See - // https://code.google.com/p/address-sanitizer/issues/detail?id=321. + // https://github.com/google/sanitizers/issues/321. char *res = REAL(ctime)(timep); if (res) { COMMON_INTERCEPTOR_READ_RANGE(ctx, timep, sizeof(*timep)); @@ -759,7 +846,7 @@ INTERCEPTOR(char *, ctime_r, unsigned long *timep, char *result) { COMMON_INTERCEPTOR_ENTER(ctx, ctime_r, timep, result); // FIXME: under ASan the call below may write to freed memory and corrupt // its metadata. See - // https://code.google.com/p/address-sanitizer/issues/detail?id=321. + // https://github.com/google/sanitizers/issues/321. char *res = REAL(ctime_r)(timep, result); if (res) { COMMON_INTERCEPTOR_READ_RANGE(ctx, timep, sizeof(*timep)); @@ -772,7 +859,7 @@ INTERCEPTOR(char *, asctime, __sanitizer_tm *tm) { COMMON_INTERCEPTOR_ENTER(ctx, asctime, tm); // FIXME: under ASan the call below may write to freed memory and corrupt // its metadata. See - // https://code.google.com/p/address-sanitizer/issues/detail?id=321. + // https://github.com/google/sanitizers/issues/321. char *res = REAL(asctime)(tm); if (res) { COMMON_INTERCEPTOR_READ_RANGE(ctx, tm, sizeof(*tm)); @@ -785,7 +872,7 @@ INTERCEPTOR(char *, asctime_r, __sanitizer_tm *tm, char *result) { COMMON_INTERCEPTOR_ENTER(ctx, asctime_r, tm, result); // FIXME: under ASan the call below may write to freed memory and corrupt // its metadata. See - // https://code.google.com/p/address-sanitizer/issues/detail?id=321. + // https://github.com/google/sanitizers/issues/321. char *res = REAL(asctime_r)(tm, result); if (res) { COMMON_INTERCEPTOR_READ_RANGE(ctx, tm, sizeof(*tm)); @@ -829,7 +916,7 @@ INTERCEPTOR(char *, strptime, char *s, char *format, __sanitizer_tm *tm) { COMMON_INTERCEPTOR_READ_RANGE(ctx, format, REAL(strlen)(format) + 1); // FIXME: under ASan the call below may write to freed memory and corrupt // its metadata. See - // https://code.google.com/p/address-sanitizer/issues/detail?id=321. + // https://github.com/google/sanitizers/issues/321. char *res = REAL(strptime)(s, format, tm); COMMON_INTERCEPTOR_READ_STRING(ctx, s, res ? res - s : 0); if (res && tm) { @@ -966,7 +1053,7 @@ FORMAT_INTERCEPTOR_IMPL(__isoc99_sscanf, __isoc99_vsscanf, str, format) // FIXME: under ASan the REAL() call below may write to freed memory and // corrupt its metadata. See -// https://code.google.com/p/address-sanitizer/issues/detail?id=321. +// https://github.com/google/sanitizers/issues/321. #define VSPRINTF_INTERCEPTOR_IMPL(vname, str, ...) \ { \ VPRINTF_INTERCEPTOR_ENTER(vname, str, __VA_ARGS__) \ @@ -983,7 +1070,7 @@ FORMAT_INTERCEPTOR_IMPL(__isoc99_sscanf, __isoc99_vsscanf, str, format) // FIXME: under ASan the REAL() call below may write to freed memory and // corrupt its metadata. See -// https://code.google.com/p/address-sanitizer/issues/detail?id=321. +// https://github.com/google/sanitizers/issues/321. #define VSNPRINTF_INTERCEPTOR_IMPL(vname, str, size, ...) \ { \ VPRINTF_INTERCEPTOR_ENTER(vname, str, size, __VA_ARGS__) \ @@ -1000,7 +1087,7 @@ FORMAT_INTERCEPTOR_IMPL(__isoc99_sscanf, __isoc99_vsscanf, str, format) // FIXME: under ASan the REAL() call below may write to freed memory and // corrupt its metadata. See -// https://code.google.com/p/address-sanitizer/issues/detail?id=321. +// https://github.com/google/sanitizers/issues/321. #define VASPRINTF_INTERCEPTOR_IMPL(vname, strp, ...) \ { \ VPRINTF_INTERCEPTOR_ENTER(vname, strp, __VA_ARGS__) \ @@ -1243,14 +1330,14 @@ INTERCEPTOR(__sanitizer_passwd *, getpwnam, const char *name) { COMMON_INTERCEPTOR_ENTER(ctx, getpwnam, name); COMMON_INTERCEPTOR_READ_RANGE(ctx, name, REAL(strlen)(name) + 1); __sanitizer_passwd *res = REAL(getpwnam)(name); - if (res != 0) unpoison_passwd(ctx, res); + if (res) unpoison_passwd(ctx, res); return res; } INTERCEPTOR(__sanitizer_passwd *, getpwuid, u32 uid) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, getpwuid, uid); __sanitizer_passwd *res = REAL(getpwuid)(uid); - if (res != 0) unpoison_passwd(ctx, res); + if (res) unpoison_passwd(ctx, res); return res; } INTERCEPTOR(__sanitizer_group *, getgrnam, const char *name) { @@ -1258,14 +1345,14 @@ INTERCEPTOR(__sanitizer_group *, getgrnam, const char *name) { COMMON_INTERCEPTOR_ENTER(ctx, getgrnam, name); COMMON_INTERCEPTOR_READ_RANGE(ctx, name, REAL(strlen)(name) + 1); __sanitizer_group *res = REAL(getgrnam)(name); - if (res != 0) unpoison_group(ctx, res); + if (res) unpoison_group(ctx, res); return res; } INTERCEPTOR(__sanitizer_group *, getgrgid, u32 gid) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, getgrgid, gid); __sanitizer_group *res = REAL(getgrgid)(gid); - if (res != 0) unpoison_group(ctx, res); + if (res) unpoison_group(ctx, res); return res; } #define INIT_GETPWNAM_AND_FRIENDS \ @@ -1285,7 +1372,7 @@ INTERCEPTOR(int, getpwnam_r, const char *name, __sanitizer_passwd *pwd, COMMON_INTERCEPTOR_READ_RANGE(ctx, name, REAL(strlen)(name) + 1); // FIXME: under ASan the call below may write to freed memory and corrupt // its metadata. See - // https://code.google.com/p/address-sanitizer/issues/detail?id=321. + // https://github.com/google/sanitizers/issues/321. int res = REAL(getpwnam_r)(name, pwd, buf, buflen, result); if (!res) { if (result && *result) unpoison_passwd(ctx, *result); @@ -1300,7 +1387,7 @@ INTERCEPTOR(int, getpwuid_r, u32 uid, __sanitizer_passwd *pwd, char *buf, COMMON_INTERCEPTOR_ENTER(ctx, getpwuid_r, uid, pwd, buf, buflen, result); // FIXME: under ASan the call below may write to freed memory and corrupt // its metadata. See - // https://code.google.com/p/address-sanitizer/issues/detail?id=321. + // https://github.com/google/sanitizers/issues/321. int res = REAL(getpwuid_r)(uid, pwd, buf, buflen, result); if (!res) { if (result && *result) unpoison_passwd(ctx, *result); @@ -1316,7 +1403,7 @@ INTERCEPTOR(int, getgrnam_r, const char *name, __sanitizer_group *grp, COMMON_INTERCEPTOR_READ_RANGE(ctx, name, REAL(strlen)(name) + 1); // FIXME: under ASan the call below may write to freed memory and corrupt // its metadata. See - // https://code.google.com/p/address-sanitizer/issues/detail?id=321. + // https://github.com/google/sanitizers/issues/321. int res = REAL(getgrnam_r)(name, grp, buf, buflen, result); if (!res) { if (result && *result) unpoison_group(ctx, *result); @@ -1331,7 +1418,7 @@ INTERCEPTOR(int, getgrgid_r, u32 gid, __sanitizer_group *grp, char *buf, COMMON_INTERCEPTOR_ENTER(ctx, getgrgid_r, gid, grp, buf, buflen, result); // FIXME: under ASan the call below may write to freed memory and corrupt // its metadata. See - // https://code.google.com/p/address-sanitizer/issues/detail?id=321. + // https://github.com/google/sanitizers/issues/321. int res = REAL(getgrgid_r)(gid, grp, buf, buflen, result); if (!res) { if (result && *result) unpoison_group(ctx, *result); @@ -1354,14 +1441,14 @@ INTERCEPTOR(__sanitizer_passwd *, getpwent, int dummy) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, getpwent, dummy); __sanitizer_passwd *res = REAL(getpwent)(dummy); - if (res != 0) unpoison_passwd(ctx, res); + if (res) unpoison_passwd(ctx, res); return res; } INTERCEPTOR(__sanitizer_group *, getgrent, int dummy) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, getgrent, dummy); __sanitizer_group *res = REAL(getgrent)(dummy); - if (res != 0) unpoison_group(ctx, res);; + if (res) unpoison_group(ctx, res);; return res; } #define INIT_GETPWENT \ @@ -1376,14 +1463,14 @@ INTERCEPTOR(__sanitizer_passwd *, fgetpwent, void *fp) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, fgetpwent, fp); __sanitizer_passwd *res = REAL(fgetpwent)(fp); - if (res != 0) unpoison_passwd(ctx, res); + if (res) unpoison_passwd(ctx, res); return res; } INTERCEPTOR(__sanitizer_group *, fgetgrent, void *fp) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, fgetgrent, fp); __sanitizer_group *res = REAL(fgetgrent)(fp); - if (res != 0) unpoison_group(ctx, res); + if (res) unpoison_group(ctx, res); return res; } #define INIT_FGETPWENT \ @@ -1400,7 +1487,7 @@ INTERCEPTOR(int, getpwent_r, __sanitizer_passwd *pwbuf, char *buf, COMMON_INTERCEPTOR_ENTER(ctx, getpwent_r, pwbuf, buf, buflen, pwbufp); // FIXME: under ASan the call below may write to freed memory and corrupt // its metadata. See - // https://code.google.com/p/address-sanitizer/issues/detail?id=321. + // https://github.com/google/sanitizers/issues/321. int res = REAL(getpwent_r)(pwbuf, buf, buflen, pwbufp); if (!res) { if (pwbufp && *pwbufp) unpoison_passwd(ctx, *pwbufp); @@ -1415,7 +1502,7 @@ INTERCEPTOR(int, fgetpwent_r, void *fp, __sanitizer_passwd *pwbuf, char *buf, COMMON_INTERCEPTOR_ENTER(ctx, fgetpwent_r, fp, pwbuf, buf, buflen, pwbufp); // FIXME: under ASan the call below may write to freed memory and corrupt // its metadata. See - // https://code.google.com/p/address-sanitizer/issues/detail?id=321. + // https://github.com/google/sanitizers/issues/321. int res = REAL(fgetpwent_r)(fp, pwbuf, buf, buflen, pwbufp); if (!res) { if (pwbufp && *pwbufp) unpoison_passwd(ctx, *pwbufp); @@ -1430,7 +1517,7 @@ INTERCEPTOR(int, getgrent_r, __sanitizer_group *pwbuf, char *buf, SIZE_T buflen, COMMON_INTERCEPTOR_ENTER(ctx, getgrent_r, pwbuf, buf, buflen, pwbufp); // FIXME: under ASan the call below may write to freed memory and corrupt // its metadata. See - // https://code.google.com/p/address-sanitizer/issues/detail?id=321. + // https://github.com/google/sanitizers/issues/321. int res = REAL(getgrent_r)(pwbuf, buf, buflen, pwbufp); if (!res) { if (pwbufp && *pwbufp) unpoison_group(ctx, *pwbufp); @@ -1445,7 +1532,7 @@ INTERCEPTOR(int, fgetgrent_r, void *fp, __sanitizer_group *pwbuf, char *buf, COMMON_INTERCEPTOR_ENTER(ctx, fgetgrent_r, fp, pwbuf, buf, buflen, pwbufp); // FIXME: under ASan the call below may write to freed memory and corrupt // its metadata. See - // https://code.google.com/p/address-sanitizer/issues/detail?id=321. + // https://github.com/google/sanitizers/issues/321. int res = REAL(fgetgrent_r)(fp, pwbuf, buf, buflen, pwbufp); if (!res) { if (pwbufp && *pwbufp) unpoison_group(ctx, *pwbufp); @@ -1502,7 +1589,7 @@ INTERCEPTOR(int, clock_getres, u32 clk_id, void *tp) { COMMON_INTERCEPTOR_ENTER(ctx, clock_getres, clk_id, tp); // FIXME: under ASan the call below may write to freed memory and corrupt // its metadata. See - // https://code.google.com/p/address-sanitizer/issues/detail?id=321. + // https://github.com/google/sanitizers/issues/321. int res = REAL(clock_getres)(clk_id, tp); if (!res && tp) { COMMON_INTERCEPTOR_WRITE_RANGE(ctx, tp, struct_timespec_sz); @@ -1514,7 +1601,7 @@ INTERCEPTOR(int, clock_gettime, u32 clk_id, void *tp) { COMMON_INTERCEPTOR_ENTER(ctx, clock_gettime, clk_id, tp); // FIXME: under ASan the call below may write to freed memory and corrupt // its metadata. See - // https://code.google.com/p/address-sanitizer/issues/detail?id=321. + // https://github.com/google/sanitizers/issues/321. int res = REAL(clock_gettime)(clk_id, tp); if (!res) { COMMON_INTERCEPTOR_WRITE_RANGE(ctx, tp, struct_timespec_sz); @@ -1541,7 +1628,7 @@ INTERCEPTOR(int, getitimer, int which, void *curr_value) { COMMON_INTERCEPTOR_ENTER(ctx, getitimer, which, curr_value); // FIXME: under ASan the call below may write to freed memory and corrupt // its metadata. See - // https://code.google.com/p/address-sanitizer/issues/detail?id=321. + // https://github.com/google/sanitizers/issues/321. int res = REAL(getitimer)(which, curr_value); if (!res && curr_value) { COMMON_INTERCEPTOR_WRITE_RANGE(ctx, curr_value, struct_itimerval_sz); @@ -1555,7 +1642,7 @@ INTERCEPTOR(int, setitimer, int which, const void *new_value, void *old_value) { COMMON_INTERCEPTOR_READ_RANGE(ctx, new_value, struct_itimerval_sz); // FIXME: under ASan the call below may write to freed memory and corrupt // its metadata. See - // https://code.google.com/p/address-sanitizer/issues/detail?id=321. + // https://github.com/google/sanitizers/issues/321. int res = REAL(setitimer)(which, new_value, old_value); if (!res && old_value) { COMMON_INTERCEPTOR_WRITE_RANGE(ctx, old_value, struct_itimerval_sz); @@ -1612,16 +1699,19 @@ static int wrapped_gl_stat(const char *s, void *st) { return pglob_copy->gl_stat(s, st); } +static const __sanitizer_glob_t kGlobCopy = { + 0, 0, 0, + 0, wrapped_gl_closedir, wrapped_gl_readdir, + wrapped_gl_opendir, wrapped_gl_lstat, wrapped_gl_stat}; + INTERCEPTOR(int, glob, const char *pattern, int flags, int (*errfunc)(const char *epath, int eerrno), __sanitizer_glob_t *pglob) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, glob, pattern, flags, errfunc, pglob); COMMON_INTERCEPTOR_READ_STRING(ctx, pattern, 0); - __sanitizer_glob_t glob_copy = { - 0, 0, 0, - 0, wrapped_gl_closedir, wrapped_gl_readdir, - wrapped_gl_opendir, wrapped_gl_lstat, wrapped_gl_stat}; + __sanitizer_glob_t glob_copy; + internal_memcpy(&glob_copy, &kGlobCopy, sizeof(glob_copy)); if (flags & glob_altdirfunc) { Swap(pglob->gl_closedir, glob_copy.gl_closedir); Swap(pglob->gl_readdir, glob_copy.gl_readdir); @@ -1649,10 +1739,8 @@ INTERCEPTOR(int, glob64, const char *pattern, int flags, void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, glob64, pattern, flags, errfunc, pglob); COMMON_INTERCEPTOR_READ_STRING(ctx, pattern, 0); - __sanitizer_glob_t glob_copy = { - 0, 0, 0, - 0, wrapped_gl_closedir, wrapped_gl_readdir, - wrapped_gl_opendir, wrapped_gl_lstat, wrapped_gl_stat}; + __sanitizer_glob_t glob_copy; + internal_memcpy(&glob_copy, &kGlobCopy, sizeof(glob_copy)); if (flags & glob_altdirfunc) { Swap(pglob->gl_closedir, glob_copy.gl_closedir); Swap(pglob->gl_readdir, glob_copy.gl_readdir); @@ -1689,7 +1777,7 @@ INTERCEPTOR_WITH_SUFFIX(int, wait, int *status) { COMMON_INTERCEPTOR_ENTER(ctx, wait, status); // FIXME: under ASan the call below may write to freed memory and corrupt // its metadata. See - // https://code.google.com/p/address-sanitizer/issues/detail?id=321. + // https://github.com/google/sanitizers/issues/321. int res = REAL(wait)(status); if (res != -1 && status) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, status, sizeof(*status)); @@ -1707,7 +1795,7 @@ INTERCEPTOR_WITH_SUFFIX(int, waitid, int idtype, int id, void *infop, COMMON_INTERCEPTOR_ENTER(ctx, waitid, idtype, id, infop, options); // FIXME: under ASan the call below may write to freed memory and corrupt // its metadata. See - // https://code.google.com/p/address-sanitizer/issues/detail?id=321. + // https://github.com/google/sanitizers/issues/321. int res = REAL(waitid)(idtype, id, infop, options); if (res != -1 && infop) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, infop, siginfo_t_sz); @@ -1718,7 +1806,7 @@ INTERCEPTOR_WITH_SUFFIX(int, waitpid, int pid, int *status, int options) { COMMON_INTERCEPTOR_ENTER(ctx, waitpid, pid, status, options); // FIXME: under ASan the call below may write to freed memory and corrupt // its metadata. See - // https://code.google.com/p/address-sanitizer/issues/detail?id=321. + // https://github.com/google/sanitizers/issues/321. int res = REAL(waitpid)(pid, status, options); if (res != -1 && status) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, status, sizeof(*status)); @@ -1729,7 +1817,7 @@ INTERCEPTOR(int, wait3, int *status, int options, void *rusage) { COMMON_INTERCEPTOR_ENTER(ctx, wait3, status, options, rusage); // FIXME: under ASan the call below may write to freed memory and corrupt // its metadata. See - // https://code.google.com/p/address-sanitizer/issues/detail?id=321. + // https://github.com/google/sanitizers/issues/321. int res = REAL(wait3)(status, options, rusage); if (res != -1) { if (status) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, status, sizeof(*status)); @@ -1743,7 +1831,7 @@ INTERCEPTOR(int, __wait4, int pid, int *status, int options, void *rusage) { COMMON_INTERCEPTOR_ENTER(ctx, __wait4, pid, status, options, rusage); // FIXME: under ASan the call below may write to freed memory and corrupt // its metadata. See - // https://code.google.com/p/address-sanitizer/issues/detail?id=321. + // https://github.com/google/sanitizers/issues/321. int res = REAL(__wait4)(pid, status, options, rusage); if (res != -1) { if (status) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, status, sizeof(*status)); @@ -1758,7 +1846,7 @@ INTERCEPTOR(int, wait4, int pid, int *status, int options, void *rusage) { COMMON_INTERCEPTOR_ENTER(ctx, wait4, pid, status, options, rusage); // FIXME: under ASan the call below may write to freed memory and corrupt // its metadata. See - // https://code.google.com/p/address-sanitizer/issues/detail?id=321. + // https://github.com/google/sanitizers/issues/321. int res = REAL(wait4)(pid, status, options, rusage); if (res != -1) { if (status) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, status, sizeof(*status)); @@ -1787,7 +1875,7 @@ INTERCEPTOR(char *, inet_ntop, int af, const void *src, char *dst, u32 size) { // FIXME: figure out read size based on the address family. // FIXME: under ASan the call below may write to freed memory and corrupt // its metadata. See - // https://code.google.com/p/address-sanitizer/issues/detail?id=321. + // https://github.com/google/sanitizers/issues/321. char *res = REAL(inet_ntop)(af, src, dst, size); if (res) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, res, REAL(strlen)(res) + 1); return res; @@ -1799,7 +1887,7 @@ INTERCEPTOR(int, inet_pton, int af, const char *src, void *dst) { // FIXME: figure out read size based on the address family. // FIXME: under ASan the call below may write to freed memory and corrupt // its metadata. See - // https://code.google.com/p/address-sanitizer/issues/detail?id=321. + // https://github.com/google/sanitizers/issues/321. int res = REAL(inet_pton)(af, src, dst); if (res == 1) { uptr sz = __sanitizer_in_addr_sz(af); @@ -1821,7 +1909,7 @@ INTERCEPTOR(int, inet_aton, const char *cp, void *dst) { if (cp) COMMON_INTERCEPTOR_READ_RANGE(ctx, cp, REAL(strlen)(cp) + 1); // FIXME: under ASan the call below may write to freed memory and corrupt // its metadata. See - // https://code.google.com/p/address-sanitizer/issues/detail?id=321. + // https://github.com/google/sanitizers/issues/321. int res = REAL(inet_aton)(cp, dst); if (res != 0) { uptr sz = __sanitizer_in_addr_sz(af_inet); @@ -1840,7 +1928,7 @@ INTERCEPTOR(int, pthread_getschedparam, uptr thread, int *policy, int *param) { COMMON_INTERCEPTOR_ENTER(ctx, pthread_getschedparam, thread, policy, param); // FIXME: under ASan the call below may write to freed memory and corrupt // its metadata. See - // https://code.google.com/p/address-sanitizer/issues/detail?id=321. + // https://github.com/google/sanitizers/issues/321. int res = REAL(pthread_getschedparam)(thread, policy, param); if (res == 0) { if (policy) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, policy, sizeof(*policy)); @@ -1867,7 +1955,7 @@ INTERCEPTOR(int, getaddrinfo, char *node, char *service, COMMON_INTERCEPTOR_READ_RANGE(ctx, hints, sizeof(__sanitizer_addrinfo)); // FIXME: under ASan the call below may write to freed memory and corrupt // its metadata. See - // https://code.google.com/p/address-sanitizer/issues/detail?id=321. + // https://github.com/google/sanitizers/issues/321. int res = REAL(getaddrinfo)(node, service, hints, out); if (res == 0 && out) { COMMON_INTERCEPTOR_WRITE_RANGE(ctx, out, sizeof(*out)); @@ -1899,7 +1987,7 @@ INTERCEPTOR(int, getnameinfo, void *sockaddr, unsigned salen, char *host, // There is padding in in_addr that may make this too noisy // FIXME: under ASan the call below may write to freed memory and corrupt // its metadata. See - // https://code.google.com/p/address-sanitizer/issues/detail?id=321. + // https://github.com/google/sanitizers/issues/321. int res = REAL(getnameinfo)(sockaddr, salen, host, hostlen, serv, servlen, flags); if (res == 0) { @@ -1923,7 +2011,7 @@ INTERCEPTOR(int, getsockname, int sock_fd, void *addr, int *addrlen) { int addrlen_in = *addrlen; // FIXME: under ASan the call below may write to freed memory and corrupt // its metadata. See - // https://code.google.com/p/address-sanitizer/issues/detail?id=321. + // https://github.com/google/sanitizers/issues/321. int res = REAL(getsockname)(sock_fd, addr, addrlen); if (res == 0) { COMMON_INTERCEPTOR_WRITE_RANGE(ctx, addr, Min(addrlen_in, *addrlen)); @@ -2009,7 +2097,7 @@ INTERCEPTOR(int, gethostbyname_r, char *name, struct __sanitizer_hostent *ret, h_errnop); // FIXME: under ASan the call below may write to freed memory and corrupt // its metadata. See - // https://code.google.com/p/address-sanitizer/issues/detail?id=321. + // https://github.com/google/sanitizers/issues/321. int res = REAL(gethostbyname_r)(name, ret, buf, buflen, result, h_errnop); if (result) { COMMON_INTERCEPTOR_WRITE_RANGE(ctx, result, sizeof(*result)); @@ -2032,7 +2120,7 @@ INTERCEPTOR(int, gethostent_r, struct __sanitizer_hostent *ret, char *buf, h_errnop); // FIXME: under ASan the call below may write to freed memory and corrupt // its metadata. See - // https://code.google.com/p/address-sanitizer/issues/detail?id=321. + // https://github.com/google/sanitizers/issues/321. int res = REAL(gethostent_r)(ret, buf, buflen, result, h_errnop); if (result) { COMMON_INTERCEPTOR_WRITE_RANGE(ctx, result, sizeof(*result)); @@ -2058,7 +2146,7 @@ INTERCEPTOR(int, gethostbyaddr_r, void *addr, int len, int type, COMMON_INTERCEPTOR_READ_RANGE(ctx, addr, len); // FIXME: under ASan the call below may write to freed memory and corrupt // its metadata. See - // https://code.google.com/p/address-sanitizer/issues/detail?id=321. + // https://github.com/google/sanitizers/issues/321. int res = REAL(gethostbyaddr_r)(addr, len, type, ret, buf, buflen, result, h_errnop); if (result) { @@ -2084,7 +2172,7 @@ INTERCEPTOR(int, gethostbyname2_r, char *name, int af, result, h_errnop); // FIXME: under ASan the call below may write to freed memory and corrupt // its metadata. See - // https://code.google.com/p/address-sanitizer/issues/detail?id=321. + // https://github.com/google/sanitizers/issues/321. int res = REAL(gethostbyname2_r)(name, af, ret, buf, buflen, result, h_errnop); if (result) { @@ -2110,7 +2198,7 @@ INTERCEPTOR(int, getsockopt, int sockfd, int level, int optname, void *optval, if (optlen) COMMON_INTERCEPTOR_READ_RANGE(ctx, optlen, sizeof(*optlen)); // FIXME: under ASan the call below may write to freed memory and corrupt // its metadata. See - // https://code.google.com/p/address-sanitizer/issues/detail?id=321. + // https://github.com/google/sanitizers/issues/321. int res = REAL(getsockopt)(sockfd, level, optname, optval, optlen); if (res == 0) if (optval && optlen) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, optval, *optlen); @@ -2154,7 +2242,7 @@ INTERCEPTOR(int, accept4, int fd, void *addr, unsigned *addrlen, int f) { } // FIXME: under ASan the call below may write to freed memory and corrupt // its metadata. See - // https://code.google.com/p/address-sanitizer/issues/detail?id=321. + // https://github.com/google/sanitizers/issues/321. int fd2 = REAL(accept4)(fd, addr, addrlen, f); if (fd2 >= 0) { if (fd >= 0) COMMON_INTERCEPTOR_FD_SOCKET_ACCEPT(ctx, fd, fd2); @@ -2174,7 +2262,7 @@ INTERCEPTOR(double, modf, double x, double *iptr) { COMMON_INTERCEPTOR_ENTER(ctx, modf, x, iptr); // FIXME: under ASan the call below may write to freed memory and corrupt // its metadata. See - // https://code.google.com/p/address-sanitizer/issues/detail?id=321. + // https://github.com/google/sanitizers/issues/321. double res = REAL(modf)(x, iptr); if (iptr) { COMMON_INTERCEPTOR_WRITE_RANGE(ctx, iptr, sizeof(*iptr)); @@ -2186,7 +2274,7 @@ INTERCEPTOR(float, modff, float x, float *iptr) { COMMON_INTERCEPTOR_ENTER(ctx, modff, x, iptr); // FIXME: under ASan the call below may write to freed memory and corrupt // its metadata. See - // https://code.google.com/p/address-sanitizer/issues/detail?id=321. + // https://github.com/google/sanitizers/issues/321. float res = REAL(modff)(x, iptr); if (iptr) { COMMON_INTERCEPTOR_WRITE_RANGE(ctx, iptr, sizeof(*iptr)); @@ -2198,7 +2286,7 @@ INTERCEPTOR(long double, modfl, long double x, long double *iptr) { COMMON_INTERCEPTOR_ENTER(ctx, modfl, x, iptr); // FIXME: under ASan the call below may write to freed memory and corrupt // its metadata. See - // https://code.google.com/p/address-sanitizer/issues/detail?id=321. + // https://github.com/google/sanitizers/issues/321. long double res = REAL(modfl)(x, iptr); if (iptr) { COMMON_INTERCEPTOR_WRITE_RANGE(ctx, iptr, sizeof(*iptr)); @@ -2233,7 +2321,7 @@ INTERCEPTOR(SSIZE_T, recvmsg, int fd, struct __sanitizer_msghdr *msg, COMMON_INTERCEPTOR_ENTER(ctx, recvmsg, fd, msg, flags); // FIXME: under ASan the call below may write to freed memory and corrupt // its metadata. See - // https://code.google.com/p/address-sanitizer/issues/detail?id=321. + // https://github.com/google/sanitizers/issues/321. SSIZE_T res = REAL(recvmsg)(fd, msg, flags); if (res >= 0) { if (fd >= 0) COMMON_INTERCEPTOR_FD_ACQUIRE(ctx, fd); @@ -2257,7 +2345,7 @@ INTERCEPTOR(int, getpeername, int sockfd, void *addr, unsigned *addrlen) { if (addrlen) addr_sz = *addrlen; // FIXME: under ASan the call below may write to freed memory and corrupt // its metadata. See - // https://code.google.com/p/address-sanitizer/issues/detail?id=321. + // https://github.com/google/sanitizers/issues/321. int res = REAL(getpeername)(sockfd, addr, addrlen); if (!res && addr && addrlen) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, addr, Min(addr_sz, *addrlen)); @@ -2273,7 +2361,7 @@ INTERCEPTOR(int, sysinfo, void *info) { void *ctx; // FIXME: under ASan the call below may write to freed memory and corrupt // its metadata. See - // https://code.google.com/p/address-sanitizer/issues/detail?id=321. + // https://github.com/google/sanitizers/issues/321. COMMON_INTERCEPTOR_ENTER(ctx, sysinfo, info); int res = REAL(sysinfo)(info); if (!res && info) @@ -2291,7 +2379,7 @@ INTERCEPTOR(__sanitizer_dirent *, opendir, const char *path) { COMMON_INTERCEPTOR_ENTER(ctx, opendir, path); COMMON_INTERCEPTOR_READ_RANGE(ctx, path, REAL(strlen)(path) + 1); __sanitizer_dirent *res = REAL(opendir)(path); - if (res != 0) + if (res) COMMON_INTERCEPTOR_DIR_ACQUIRE(ctx, path); return res; } @@ -2301,7 +2389,7 @@ INTERCEPTOR(__sanitizer_dirent *, readdir, void *dirp) { COMMON_INTERCEPTOR_ENTER(ctx, readdir, dirp); // FIXME: under ASan the call below may write to freed memory and corrupt // its metadata. See - // https://code.google.com/p/address-sanitizer/issues/detail?id=321. + // https://github.com/google/sanitizers/issues/321. __sanitizer_dirent *res = REAL(readdir)(dirp); if (res) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, res, res->d_reclen); return res; @@ -2313,7 +2401,7 @@ INTERCEPTOR(int, readdir_r, void *dirp, __sanitizer_dirent *entry, COMMON_INTERCEPTOR_ENTER(ctx, readdir_r, dirp, entry, result); // FIXME: under ASan the call below may write to freed memory and corrupt // its metadata. See - // https://code.google.com/p/address-sanitizer/issues/detail?id=321. + // https://github.com/google/sanitizers/issues/321. int res = REAL(readdir_r)(dirp, entry, result); if (!res) { COMMON_INTERCEPTOR_WRITE_RANGE(ctx, result, sizeof(*result)); @@ -2337,7 +2425,7 @@ INTERCEPTOR(__sanitizer_dirent64 *, readdir64, void *dirp) { COMMON_INTERCEPTOR_ENTER(ctx, readdir64, dirp); // FIXME: under ASan the call below may write to freed memory and corrupt // its metadata. See - // https://code.google.com/p/address-sanitizer/issues/detail?id=321. + // https://github.com/google/sanitizers/issues/321. __sanitizer_dirent64 *res = REAL(readdir64)(dirp); if (res) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, res, res->d_reclen); return res; @@ -2349,7 +2437,7 @@ INTERCEPTOR(int, readdir64_r, void *dirp, __sanitizer_dirent64 *entry, COMMON_INTERCEPTOR_ENTER(ctx, readdir64_r, dirp, entry, result); // FIXME: under ASan the call below may write to freed memory and corrupt // its metadata. See - // https://code.google.com/p/address-sanitizer/issues/detail?id=321. + // https://github.com/google/sanitizers/issues/321. int res = REAL(readdir64_r)(dirp, entry, result); if (!res) { COMMON_INTERCEPTOR_WRITE_RANGE(ctx, result, sizeof(*result)); @@ -2369,6 +2457,7 @@ INTERCEPTOR(int, readdir64_r, void *dirp, __sanitizer_dirent64 *entry, INTERCEPTOR(uptr, ptrace, int request, int pid, void *addr, void *data) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, ptrace, request, pid, addr, data); + __sanitizer_iovec local_iovec; if (data) { if (request == ptrace_setregs) @@ -2377,17 +2466,25 @@ INTERCEPTOR(uptr, ptrace, int request, int pid, void *addr, void *data) { COMMON_INTERCEPTOR_READ_RANGE(ctx, data, struct_user_fpregs_struct_sz); else if (request == ptrace_setfpxregs) COMMON_INTERCEPTOR_READ_RANGE(ctx, data, struct_user_fpxregs_struct_sz); + else if (request == ptrace_setvfpregs) + COMMON_INTERCEPTOR_READ_RANGE(ctx, data, struct_user_vfpregs_struct_sz); else if (request == ptrace_setsiginfo) COMMON_INTERCEPTOR_READ_RANGE(ctx, data, siginfo_t_sz); - else if (request == ptrace_setregset) { - __sanitizer_iovec *iov = (__sanitizer_iovec *)data; - COMMON_INTERCEPTOR_READ_RANGE(ctx, iov->iov_base, iov->iov_len); + // Some kernel might zero the iovec::iov_base in case of invalid + // write access. In this case copy the invalid address for further + // inspection. + else if (request == ptrace_setregset || request == ptrace_getregset) { + __sanitizer_iovec *iovec = (__sanitizer_iovec*)data; + COMMON_INTERCEPTOR_READ_RANGE(ctx, iovec, sizeof(*iovec)); + local_iovec = *iovec; + if (request == ptrace_setregset) + COMMON_INTERCEPTOR_READ_RANGE(ctx, iovec->iov_base, iovec->iov_len); } } // FIXME: under ASan the call below may write to freed memory and corrupt // its metadata. See - // https://code.google.com/p/address-sanitizer/issues/detail?id=321. + // https://github.com/google/sanitizers/issues/321. uptr res = REAL(ptrace)(request, pid, addr, data); if (!res && data) { @@ -2399,13 +2496,17 @@ INTERCEPTOR(uptr, ptrace, int request, int pid, void *addr, void *data) { COMMON_INTERCEPTOR_WRITE_RANGE(ctx, data, struct_user_fpregs_struct_sz); else if (request == ptrace_getfpxregs) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, data, struct_user_fpxregs_struct_sz); + else if (request == ptrace_getvfpregs) + COMMON_INTERCEPTOR_WRITE_RANGE(ctx, data, struct_user_vfpregs_struct_sz); else if (request == ptrace_getsiginfo) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, data, siginfo_t_sz); else if (request == ptrace_geteventmsg) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, data, sizeof(unsigned long)); else if (request == ptrace_getregset) { - __sanitizer_iovec *iov = (__sanitizer_iovec *)data; - COMMON_INTERCEPTOR_WRITE_RANGE(ctx, iov->iov_base, iov->iov_len); + __sanitizer_iovec *iovec = (__sanitizer_iovec*)data; + COMMON_INTERCEPTOR_WRITE_RANGE(ctx, iovec, sizeof(*iovec)); + COMMON_INTERCEPTOR_WRITE_RANGE(ctx, local_iovec.iov_base, + local_iovec.iov_len); } } return res; @@ -2438,7 +2539,7 @@ INTERCEPTOR(char *, getcwd, char *buf, SIZE_T size) { COMMON_INTERCEPTOR_ENTER(ctx, getcwd, buf, size); // FIXME: under ASan the call below may write to freed memory and corrupt // its metadata. See - // https://code.google.com/p/address-sanitizer/issues/detail?id=321. + // https://github.com/google/sanitizers/issues/321. char *res = REAL(getcwd)(buf, size); if (res) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, res, REAL(strlen)(res) + 1); return res; @@ -2454,7 +2555,7 @@ INTERCEPTOR(char *, get_current_dir_name, int fake) { COMMON_INTERCEPTOR_ENTER(ctx, get_current_dir_name, fake); // FIXME: under ASan the call below may write to freed memory and corrupt // its metadata. See - // https://code.google.com/p/address-sanitizer/issues/detail?id=321. + // https://github.com/google/sanitizers/issues/321. char *res = REAL(get_current_dir_name)(fake); if (res) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, res, REAL(strlen)(res) + 1); return res; @@ -2481,7 +2582,7 @@ UNUSED static inline void FixRealStrtolEndptr(const char *nptr, char **endptr) { UNUSED static inline void StrtolFixAndCheck(void *ctx, const char *nptr, char **endptr, char *real_endptr, int base) { - if (endptr != 0) { + if (endptr) { *endptr = real_endptr; COMMON_INTERCEPTOR_WRITE_RANGE(ctx, endptr, sizeof(*endptr)); } @@ -2503,7 +2604,7 @@ INTERCEPTOR(INTMAX_T, strtoimax, const char *nptr, char **endptr, int base) { COMMON_INTERCEPTOR_ENTER(ctx, strtoimax, nptr, endptr, base); // FIXME: under ASan the call below may write to freed memory and corrupt // its metadata. See - // https://code.google.com/p/address-sanitizer/issues/detail?id=321. + // https://github.com/google/sanitizers/issues/321. char *real_endptr; INTMAX_T res = REAL(strtoimax)(nptr, &real_endptr, base); StrtolFixAndCheck(ctx, nptr, endptr, real_endptr, base); @@ -2515,7 +2616,7 @@ INTERCEPTOR(INTMAX_T, strtoumax, const char *nptr, char **endptr, int base) { COMMON_INTERCEPTOR_ENTER(ctx, strtoumax, nptr, endptr, base); // FIXME: under ASan the call below may write to freed memory and corrupt // its metadata. See - // https://code.google.com/p/address-sanitizer/issues/detail?id=321. + // https://github.com/google/sanitizers/issues/321. char *real_endptr; INTMAX_T res = REAL(strtoumax)(nptr, &real_endptr, base); StrtolFixAndCheck(ctx, nptr, endptr, real_endptr, base); @@ -2535,7 +2636,7 @@ INTERCEPTOR(SIZE_T, mbstowcs, wchar_t *dest, const char *src, SIZE_T len) { COMMON_INTERCEPTOR_ENTER(ctx, mbstowcs, dest, src, len); // FIXME: under ASan the call below may write to freed memory and corrupt // its metadata. See - // https://code.google.com/p/address-sanitizer/issues/detail?id=321. + // https://github.com/google/sanitizers/issues/321. SIZE_T res = REAL(mbstowcs)(dest, src, len); if (res != (SIZE_T) - 1 && dest) { SIZE_T write_cnt = res + (res < len); @@ -2552,7 +2653,7 @@ INTERCEPTOR(SIZE_T, mbsrtowcs, wchar_t *dest, const char **src, SIZE_T len, if (ps) COMMON_INTERCEPTOR_READ_RANGE(ctx, ps, mbstate_t_sz); // FIXME: under ASan the call below may write to freed memory and corrupt // its metadata. See - // https://code.google.com/p/address-sanitizer/issues/detail?id=321. + // https://github.com/google/sanitizers/issues/321. SIZE_T res = REAL(mbsrtowcs)(dest, src, len, ps); if (res != (SIZE_T)(-1) && dest && src) { // This function, and several others, may or may not write the terminating @@ -2582,7 +2683,7 @@ INTERCEPTOR(SIZE_T, mbsnrtowcs, wchar_t *dest, const char **src, SIZE_T nms, if (ps) COMMON_INTERCEPTOR_READ_RANGE(ctx, ps, mbstate_t_sz); // FIXME: under ASan the call below may write to freed memory and corrupt // its metadata. See - // https://code.google.com/p/address-sanitizer/issues/detail?id=321. + // https://github.com/google/sanitizers/issues/321. SIZE_T res = REAL(mbsnrtowcs)(dest, src, nms, len, ps); if (res != (SIZE_T)(-1) && dest && src) { SIZE_T write_cnt = res + !*src; @@ -2602,7 +2703,7 @@ INTERCEPTOR(SIZE_T, wcstombs, char *dest, const wchar_t *src, SIZE_T len) { COMMON_INTERCEPTOR_ENTER(ctx, wcstombs, dest, src, len); // FIXME: under ASan the call below may write to freed memory and corrupt // its metadata. See - // https://code.google.com/p/address-sanitizer/issues/detail?id=321. + // https://github.com/google/sanitizers/issues/321. SIZE_T res = REAL(wcstombs)(dest, src, len); if (res != (SIZE_T) - 1 && dest) { SIZE_T write_cnt = res + (res < len); @@ -2619,7 +2720,7 @@ INTERCEPTOR(SIZE_T, wcsrtombs, char *dest, const wchar_t **src, SIZE_T len, if (ps) COMMON_INTERCEPTOR_READ_RANGE(ctx, ps, mbstate_t_sz); // FIXME: under ASan the call below may write to freed memory and corrupt // its metadata. See - // https://code.google.com/p/address-sanitizer/issues/detail?id=321. + // https://github.com/google/sanitizers/issues/321. SIZE_T res = REAL(wcsrtombs)(dest, src, len, ps); if (res != (SIZE_T) - 1 && dest && src) { SIZE_T write_cnt = res + !*src; @@ -2647,9 +2748,9 @@ INTERCEPTOR(SIZE_T, wcsnrtombs, char *dest, const wchar_t **src, SIZE_T nms, if (ps) COMMON_INTERCEPTOR_READ_RANGE(ctx, ps, mbstate_t_sz); // FIXME: under ASan the call below may write to freed memory and corrupt // its metadata. See - // https://code.google.com/p/address-sanitizer/issues/detail?id=321. + // https://github.com/google/sanitizers/issues/321. SIZE_T res = REAL(wcsnrtombs)(dest, src, nms, len, ps); - if (res != (SIZE_T) - 1 && dest && src) { + if (res != ((SIZE_T)-1) && dest && src) { SIZE_T write_cnt = res + !*src; COMMON_INTERCEPTOR_WRITE_RANGE(ctx, dest, write_cnt); } @@ -2661,13 +2762,35 @@ INTERCEPTOR(SIZE_T, wcsnrtombs, char *dest, const wchar_t **src, SIZE_T nms, #define INIT_WCSNRTOMBS #endif + +#if SANITIZER_INTERCEPT_WCRTOMB +INTERCEPTOR(SIZE_T, wcrtomb, char *dest, wchar_t src, void *ps) { + void *ctx; + COMMON_INTERCEPTOR_ENTER(ctx, wcrtomb, dest, src, ps); + if (ps) COMMON_INTERCEPTOR_READ_RANGE(ctx, ps, mbstate_t_sz); + // FIXME: under ASan the call below may write to freed memory and corrupt + // its metadata. See + // https://github.com/google/sanitizers/issues/321. + SIZE_T res = REAL(wcrtomb)(dest, src, ps); + if (res != ((SIZE_T)-1) && dest) { + SIZE_T write_cnt = res; + COMMON_INTERCEPTOR_WRITE_RANGE(ctx, dest, write_cnt); + } + return res; +} + +#define INIT_WCRTOMB COMMON_INTERCEPT_FUNCTION(wcrtomb); +#else +#define INIT_WCRTOMB +#endif + #if SANITIZER_INTERCEPT_TCGETATTR INTERCEPTOR(int, tcgetattr, int fd, void *termios_p) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, tcgetattr, fd, termios_p); // FIXME: under ASan the call below may write to freed memory and corrupt // its metadata. See - // https://code.google.com/p/address-sanitizer/issues/detail?id=321. + // https://github.com/google/sanitizers/issues/321. int res = REAL(tcgetattr)(fd, termios_p); if (!res && termios_p) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, termios_p, struct_termios_sz); @@ -2689,7 +2812,7 @@ INTERCEPTOR(char *, realpath, const char *path, char *resolved_path) { // version of a versioned symbol. For realpath(), this gives us something // (called __old_realpath) that does not handle NULL in the second argument. // Handle it as part of the interceptor. - char *allocated_path = 0; + char *allocated_path = nullptr; if (!resolved_path) allocated_path = resolved_path = (char *)WRAP(malloc)(path_max + 1); @@ -2724,7 +2847,7 @@ INTERCEPTOR(SIZE_T, confstr, int name, char *buf, SIZE_T len) { COMMON_INTERCEPTOR_ENTER(ctx, confstr, name, buf, len); // FIXME: under ASan the call below may write to freed memory and corrupt // its metadata. See - // https://code.google.com/p/address-sanitizer/issues/detail?id=321. + // https://github.com/google/sanitizers/issues/321. SIZE_T res = REAL(confstr)(name, buf, len); if (buf && res) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, buf, res < len ? res : len); @@ -2741,7 +2864,7 @@ INTERCEPTOR(int, sched_getaffinity, int pid, SIZE_T cpusetsize, void *mask) { COMMON_INTERCEPTOR_ENTER(ctx, sched_getaffinity, pid, cpusetsize, mask); // FIXME: under ASan the call below may write to freed memory and corrupt // its metadata. See - // https://code.google.com/p/address-sanitizer/issues/detail?id=321. + // https://github.com/google/sanitizers/issues/321. int res = REAL(sched_getaffinity)(pid, cpusetsize, mask); if (mask && !res) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, mask, cpusetsize); return res; @@ -2783,7 +2906,7 @@ INTERCEPTOR(char *, strerror_r, int errnum, char *buf, SIZE_T buflen) { COMMON_INTERCEPTOR_ENTER(ctx, strerror_r, errnum, buf, buflen); // FIXME: under ASan the call below may write to freed memory and corrupt // its metadata. See - // https://code.google.com/p/address-sanitizer/issues/detail?id=321. + // https://github.com/google/sanitizers/issues/321. char *res = REAL(strerror_r)(errnum, buf, buflen); // There are 2 versions of strerror_r: // * POSIX version returns 0 on success, negative error code on failure, @@ -2814,7 +2937,7 @@ INTERCEPTOR(int, __xpg_strerror_r, int errnum, char *buf, SIZE_T buflen) { COMMON_INTERCEPTOR_ENTER(ctx, __xpg_strerror_r, errnum, buf, buflen); // FIXME: under ASan the call below may write to freed memory and corrupt // its metadata. See - // https://code.google.com/p/address-sanitizer/issues/detail?id=321. + // https://github.com/google/sanitizers/issues/321. int res = REAL(__xpg_strerror_r)(errnum, buf, buflen); // This version always returns a null-terminated string. if (buf && buflen) @@ -2859,11 +2982,12 @@ INTERCEPTOR(int, scandir, char *dirp, __sanitizer_dirent ***namelist, scandir_compar = compar; // FIXME: under ASan the call below may write to freed memory and corrupt // its metadata. See - // https://code.google.com/p/address-sanitizer/issues/detail?id=321. - int res = REAL(scandir)(dirp, namelist, filter ? wrapped_scandir_filter : 0, - compar ? wrapped_scandir_compar : 0); - scandir_filter = 0; - scandir_compar = 0; + // https://github.com/google/sanitizers/issues/321. + int res = REAL(scandir)(dirp, namelist, + filter ? wrapped_scandir_filter : nullptr, + compar ? wrapped_scandir_compar : nullptr); + scandir_filter = nullptr; + scandir_compar = nullptr; if (namelist && res > 0) { COMMON_INTERCEPTOR_WRITE_RANGE(ctx, namelist, sizeof(*namelist)); COMMON_INTERCEPTOR_WRITE_RANGE(ctx, *namelist, sizeof(**namelist) * res); @@ -2911,12 +3035,13 @@ INTERCEPTOR(int, scandir64, char *dirp, __sanitizer_dirent64 ***namelist, scandir64_compar = compar; // FIXME: under ASan the call below may write to freed memory and corrupt // its metadata. See - // https://code.google.com/p/address-sanitizer/issues/detail?id=321. + // https://github.com/google/sanitizers/issues/321. int res = - REAL(scandir64)(dirp, namelist, filter ? wrapped_scandir64_filter : 0, - compar ? wrapped_scandir64_compar : 0); - scandir64_filter = 0; - scandir64_compar = 0; + REAL(scandir64)(dirp, namelist, + filter ? wrapped_scandir64_filter : nullptr, + compar ? wrapped_scandir64_compar : nullptr); + scandir64_filter = nullptr; + scandir64_compar = nullptr; if (namelist && res > 0) { COMMON_INTERCEPTOR_WRITE_RANGE(ctx, namelist, sizeof(*namelist)); COMMON_INTERCEPTOR_WRITE_RANGE(ctx, *namelist, sizeof(**namelist) * res); @@ -2937,7 +3062,7 @@ INTERCEPTOR(int, getgroups, int size, u32 *lst) { COMMON_INTERCEPTOR_ENTER(ctx, getgroups, size, lst); // FIXME: under ASan the call below may write to freed memory and corrupt // its metadata. See - // https://code.google.com/p/address-sanitizer/issues/detail?id=321. + // https://github.com/google/sanitizers/issues/321. int res = REAL(getgroups)(size, lst); if (res && lst) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, lst, res * sizeof(*lst)); return res; @@ -3003,7 +3128,7 @@ INTERCEPTOR(int, wordexp, char *s, __sanitizer_wordexp_t *p, int flags) { if (s) COMMON_INTERCEPTOR_READ_RANGE(ctx, s, REAL(strlen)(s) + 1); // FIXME: under ASan the call below may write to freed memory and corrupt // its metadata. See - // https://code.google.com/p/address-sanitizer/issues/detail?id=321. + // https://github.com/google/sanitizers/issues/321. int res = REAL(wordexp)(s, p, flags); if (!res && p) { COMMON_INTERCEPTOR_WRITE_RANGE(ctx, p, sizeof(*p)); @@ -3029,7 +3154,7 @@ INTERCEPTOR(int, sigwait, __sanitizer_sigset_t *set, int *sig) { // FIXME: read sigset_t when all of sigemptyset, etc are intercepted // FIXME: under ASan the call below may write to freed memory and corrupt // its metadata. See - // https://code.google.com/p/address-sanitizer/issues/detail?id=321. + // https://github.com/google/sanitizers/issues/321. int res = REAL(sigwait)(set, sig); if (!res && sig) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, sig, sizeof(*sig)); return res; @@ -3046,7 +3171,7 @@ INTERCEPTOR(int, sigwaitinfo, __sanitizer_sigset_t *set, void *info) { // FIXME: read sigset_t when all of sigemptyset, etc are intercepted // FIXME: under ASan the call below may write to freed memory and corrupt // its metadata. See - // https://code.google.com/p/address-sanitizer/issues/detail?id=321. + // https://github.com/google/sanitizers/issues/321. int res = REAL(sigwaitinfo)(set, info); if (res > 0 && info) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, info, siginfo_t_sz); return res; @@ -3065,7 +3190,7 @@ INTERCEPTOR(int, sigtimedwait, __sanitizer_sigset_t *set, void *info, // FIXME: read sigset_t when all of sigemptyset, etc are intercepted // FIXME: under ASan the call below may write to freed memory and corrupt // its metadata. See - // https://code.google.com/p/address-sanitizer/issues/detail?id=321. + // https://github.com/google/sanitizers/issues/321. int res = REAL(sigtimedwait)(set, info, timeout); if (res > 0 && info) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, info, siginfo_t_sz); return res; @@ -3081,7 +3206,7 @@ INTERCEPTOR(int, sigemptyset, __sanitizer_sigset_t *set) { COMMON_INTERCEPTOR_ENTER(ctx, sigemptyset, set); // FIXME: under ASan the call below may write to freed memory and corrupt // its metadata. See - // https://code.google.com/p/address-sanitizer/issues/detail?id=321. + // https://github.com/google/sanitizers/issues/321. int res = REAL(sigemptyset)(set); if (!res && set) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, set, sizeof(*set)); return res; @@ -3092,7 +3217,7 @@ INTERCEPTOR(int, sigfillset, __sanitizer_sigset_t *set) { COMMON_INTERCEPTOR_ENTER(ctx, sigfillset, set); // FIXME: under ASan the call below may write to freed memory and corrupt // its metadata. See - // https://code.google.com/p/address-sanitizer/issues/detail?id=321. + // https://github.com/google/sanitizers/issues/321. int res = REAL(sigfillset)(set); if (!res && set) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, set, sizeof(*set)); return res; @@ -3110,7 +3235,7 @@ INTERCEPTOR(int, sigpending, __sanitizer_sigset_t *set) { COMMON_INTERCEPTOR_ENTER(ctx, sigpending, set); // FIXME: under ASan the call below may write to freed memory and corrupt // its metadata. See - // https://code.google.com/p/address-sanitizer/issues/detail?id=321. + // https://github.com/google/sanitizers/issues/321. int res = REAL(sigpending)(set); if (!res && set) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, set, sizeof(*set)); return res; @@ -3128,7 +3253,7 @@ INTERCEPTOR(int, sigprocmask, int how, __sanitizer_sigset_t *set, // FIXME: read sigset_t when all of sigemptyset, etc are intercepted // FIXME: under ASan the call below may write to freed memory and corrupt // its metadata. See - // https://code.google.com/p/address-sanitizer/issues/detail?id=321. + // https://github.com/google/sanitizers/issues/321. int res = REAL(sigprocmask)(how, set, oldset); if (!res && oldset) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, oldset, sizeof(*oldset)); @@ -3145,7 +3270,7 @@ INTERCEPTOR(int, backtrace, void **buffer, int size) { COMMON_INTERCEPTOR_ENTER(ctx, backtrace, buffer, size); // FIXME: under ASan the call below may write to freed memory and corrupt // its metadata. See - // https://code.google.com/p/address-sanitizer/issues/detail?id=321. + // https://github.com/google/sanitizers/issues/321. int res = REAL(backtrace)(buffer, size); if (res && buffer) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, buffer, res * sizeof(*buffer)); @@ -3159,7 +3284,7 @@ INTERCEPTOR(char **, backtrace_symbols, void **buffer, int size) { COMMON_INTERCEPTOR_READ_RANGE(ctx, buffer, size * sizeof(*buffer)); // FIXME: under ASan the call below may write to freed memory and corrupt // its metadata. See - // https://code.google.com/p/address-sanitizer/issues/detail?id=321. + // https://github.com/google/sanitizers/issues/321. char **res = REAL(backtrace_symbols)(buffer, size); if (res && size) { COMMON_INTERCEPTOR_WRITE_RANGE(ctx, res, size * sizeof(*res)); @@ -3267,7 +3392,7 @@ INTERCEPTOR(int, statfs, char *path, void *buf) { if (path) COMMON_INTERCEPTOR_READ_RANGE(ctx, path, REAL(strlen)(path) + 1); // FIXME: under ASan the call below may write to freed memory and corrupt // its metadata. See - // https://code.google.com/p/address-sanitizer/issues/detail?id=321. + // https://github.com/google/sanitizers/issues/321. int res = REAL(statfs)(path, buf); if (!res) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, buf, struct_statfs_sz); return res; @@ -3277,7 +3402,7 @@ INTERCEPTOR(int, fstatfs, int fd, void *buf) { COMMON_INTERCEPTOR_ENTER(ctx, fstatfs, fd, buf); // FIXME: under ASan the call below may write to freed memory and corrupt // its metadata. See - // https://code.google.com/p/address-sanitizer/issues/detail?id=321. + // https://github.com/google/sanitizers/issues/321. int res = REAL(fstatfs)(fd, buf); if (!res) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, buf, struct_statfs_sz); return res; @@ -3296,7 +3421,7 @@ INTERCEPTOR(int, statfs64, char *path, void *buf) { if (path) COMMON_INTERCEPTOR_READ_RANGE(ctx, path, REAL(strlen)(path) + 1); // FIXME: under ASan the call below may write to freed memory and corrupt // its metadata. See - // https://code.google.com/p/address-sanitizer/issues/detail?id=321. + // https://github.com/google/sanitizers/issues/321. int res = REAL(statfs64)(path, buf); if (!res) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, buf, struct_statfs64_sz); return res; @@ -3306,7 +3431,7 @@ INTERCEPTOR(int, fstatfs64, int fd, void *buf) { COMMON_INTERCEPTOR_ENTER(ctx, fstatfs64, fd, buf); // FIXME: under ASan the call below may write to freed memory and corrupt // its metadata. See - // https://code.google.com/p/address-sanitizer/issues/detail?id=321. + // https://github.com/google/sanitizers/issues/321. int res = REAL(fstatfs64)(fd, buf); if (!res) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, buf, struct_statfs64_sz); return res; @@ -3325,7 +3450,7 @@ INTERCEPTOR(int, statvfs, char *path, void *buf) { if (path) COMMON_INTERCEPTOR_READ_RANGE(ctx, path, REAL(strlen)(path) + 1); // FIXME: under ASan the call below may write to freed memory and corrupt // its metadata. See - // https://code.google.com/p/address-sanitizer/issues/detail?id=321. + // https://github.com/google/sanitizers/issues/321. int res = REAL(statvfs)(path, buf); if (!res) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, buf, struct_statvfs_sz); return res; @@ -3335,7 +3460,7 @@ INTERCEPTOR(int, fstatvfs, int fd, void *buf) { COMMON_INTERCEPTOR_ENTER(ctx, fstatvfs, fd, buf); // FIXME: under ASan the call below may write to freed memory and corrupt // its metadata. See - // https://code.google.com/p/address-sanitizer/issues/detail?id=321. + // https://github.com/google/sanitizers/issues/321. int res = REAL(fstatvfs)(fd, buf); if (!res) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, buf, struct_statvfs_sz); return res; @@ -3354,7 +3479,7 @@ INTERCEPTOR(int, statvfs64, char *path, void *buf) { if (path) COMMON_INTERCEPTOR_READ_RANGE(ctx, path, REAL(strlen)(path) + 1); // FIXME: under ASan the call below may write to freed memory and corrupt // its metadata. See - // https://code.google.com/p/address-sanitizer/issues/detail?id=321. + // https://github.com/google/sanitizers/issues/321. int res = REAL(statvfs64)(path, buf); if (!res) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, buf, struct_statvfs64_sz); return res; @@ -3364,7 +3489,7 @@ INTERCEPTOR(int, fstatvfs64, int fd, void *buf) { COMMON_INTERCEPTOR_ENTER(ctx, fstatvfs64, fd, buf); // FIXME: under ASan the call below may write to freed memory and corrupt // its metadata. See - // https://code.google.com/p/address-sanitizer/issues/detail?id=321. + // https://github.com/google/sanitizers/issues/321. int res = REAL(fstatvfs64)(fd, buf); if (!res) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, buf, struct_statvfs64_sz); return res; @@ -3420,7 +3545,7 @@ INTERCEPTOR(int, ether_ntohost, char *hostname, __sanitizer_ether_addr *addr) { if (addr) COMMON_INTERCEPTOR_READ_RANGE(ctx, addr, sizeof(*addr)); // FIXME: under ASan the call below may write to freed memory and corrupt // its metadata. See - // https://code.google.com/p/address-sanitizer/issues/detail?id=321. + // https://github.com/google/sanitizers/issues/321. int res = REAL(ether_ntohost)(hostname, addr); if (!res && hostname) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, hostname, REAL(strlen)(hostname) + 1); @@ -3433,7 +3558,7 @@ INTERCEPTOR(int, ether_hostton, char *hostname, __sanitizer_ether_addr *addr) { COMMON_INTERCEPTOR_READ_RANGE(ctx, hostname, REAL(strlen)(hostname) + 1); // FIXME: under ASan the call below may write to freed memory and corrupt // its metadata. See - // https://code.google.com/p/address-sanitizer/issues/detail?id=321. + // https://github.com/google/sanitizers/issues/321. int res = REAL(ether_hostton)(hostname, addr); if (!res && addr) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, addr, sizeof(*addr)); return res; @@ -3445,7 +3570,7 @@ INTERCEPTOR(int, ether_line, char *line, __sanitizer_ether_addr *addr, if (line) COMMON_INTERCEPTOR_READ_RANGE(ctx, line, REAL(strlen)(line) + 1); // FIXME: under ASan the call below may write to freed memory and corrupt // its metadata. See - // https://code.google.com/p/address-sanitizer/issues/detail?id=321. + // https://github.com/google/sanitizers/issues/321. int res = REAL(ether_line)(line, addr, hostname); if (!res) { if (addr) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, addr, sizeof(*addr)); @@ -3469,7 +3594,7 @@ INTERCEPTOR(char *, ether_ntoa_r, __sanitizer_ether_addr *addr, char *buf) { if (addr) COMMON_INTERCEPTOR_READ_RANGE(ctx, addr, sizeof(*addr)); // FIXME: under ASan the call below may write to freed memory and corrupt // its metadata. See - // https://code.google.com/p/address-sanitizer/issues/detail?id=321. + // https://github.com/google/sanitizers/issues/321. char *res = REAL(ether_ntoa_r)(addr, buf); if (res) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, res, REAL(strlen)(res) + 1); return res; @@ -3481,7 +3606,7 @@ INTERCEPTOR(__sanitizer_ether_addr *, ether_aton_r, char *buf, if (buf) COMMON_INTERCEPTOR_READ_RANGE(ctx, buf, REAL(strlen)(buf) + 1); // FIXME: under ASan the call below may write to freed memory and corrupt // its metadata. See - // https://code.google.com/p/address-sanitizer/issues/detail?id=321. + // https://github.com/google/sanitizers/issues/321. __sanitizer_ether_addr *res = REAL(ether_aton_r)(buf, addr); if (res) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, res, sizeof(*res)); return res; @@ -3499,7 +3624,7 @@ INTERCEPTOR(int, shmctl, int shmid, int cmd, void *buf) { COMMON_INTERCEPTOR_ENTER(ctx, shmctl, shmid, cmd, buf); // FIXME: under ASan the call below may write to freed memory and corrupt // its metadata. See - // https://code.google.com/p/address-sanitizer/issues/detail?id=321. + // https://github.com/google/sanitizers/issues/321. int res = REAL(shmctl)(shmid, cmd, buf); if (res >= 0) { unsigned sz = 0; @@ -3524,7 +3649,7 @@ INTERCEPTOR(int, random_r, void *buf, u32 *result) { COMMON_INTERCEPTOR_ENTER(ctx, random_r, buf, result); // FIXME: under ASan the call below may write to freed memory and corrupt // its metadata. See - // https://code.google.com/p/address-sanitizer/issues/detail?id=321. + // https://github.com/google/sanitizers/issues/321. int res = REAL(random_r)(buf, result); if (!res && result) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, result, sizeof(*result)); @@ -3537,7 +3662,7 @@ INTERCEPTOR(int, random_r, void *buf, u32 *result) { // FIXME: under ASan the REAL() call below may write to freed memory and corrupt // its metadata. See -// https://code.google.com/p/address-sanitizer/issues/detail?id=321. +// https://github.com/google/sanitizers/issues/321. #if SANITIZER_INTERCEPT_PTHREAD_ATTR_GET || \ SANITIZER_INTERCEPT_PTHREAD_ATTR_GETINHERITSSCHED || \ SANITIZER_INTERCEPT_PTHREAD_MUTEXATTR_GET || \ @@ -3576,7 +3701,7 @@ INTERCEPTOR(int, pthread_attr_getstack, void *attr, void **addr, SIZE_T *size) { COMMON_INTERCEPTOR_ENTER(ctx, pthread_attr_getstack, attr, addr, size); // FIXME: under ASan the call below may write to freed memory and corrupt // its metadata. See - // https://code.google.com/p/address-sanitizer/issues/detail?id=321. + // https://github.com/google/sanitizers/issues/321. int res = REAL(pthread_attr_getstack)(attr, addr, size); if (!res) { if (addr) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, addr, sizeof(*addr)); @@ -3625,7 +3750,7 @@ INTERCEPTOR(int, pthread_attr_getaffinity_np, void *attr, SIZE_T cpusetsize, cpuset); // FIXME: under ASan the call below may write to freed memory and corrupt // its metadata. See - // https://code.google.com/p/address-sanitizer/issues/detail?id=321. + // https://github.com/google/sanitizers/issues/321. int res = REAL(pthread_attr_getaffinity_np)(attr, cpusetsize, cpuset); if (!res && cpusetsize && cpuset) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, cpuset, cpusetsize); @@ -3735,7 +3860,7 @@ INTERCEPTOR(char *, tmpnam, char *s) { if (s) // FIXME: under ASan the call below may write to freed memory and corrupt // its metadata. See - // https://code.google.com/p/address-sanitizer/issues/detail?id=321. + // https://github.com/google/sanitizers/issues/321. COMMON_INTERCEPTOR_WRITE_RANGE(ctx, s, REAL(strlen)(s) + 1); else COMMON_INTERCEPTOR_INITIALIZE_RANGE(res, REAL(strlen)(res) + 1); @@ -3753,7 +3878,7 @@ INTERCEPTOR(char *, tmpnam_r, char *s) { COMMON_INTERCEPTOR_ENTER(ctx, tmpnam_r, s); // FIXME: under ASan the call below may write to freed memory and corrupt // its metadata. See - // https://code.google.com/p/address-sanitizer/issues/detail?id=321. + // https://github.com/google/sanitizers/issues/321. char *res = REAL(tmpnam_r)(s); if (res && s) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, s, REAL(strlen)(s) + 1); return res; @@ -3797,7 +3922,7 @@ INTERCEPTOR(void, sincos, double x, double *sin, double *cos) { COMMON_INTERCEPTOR_ENTER(ctx, sincos, x, sin, cos); // FIXME: under ASan the call below may write to freed memory and corrupt // its metadata. See - // https://code.google.com/p/address-sanitizer/issues/detail?id=321. + // https://github.com/google/sanitizers/issues/321. REAL(sincos)(x, sin, cos); if (sin) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, sin, sizeof(*sin)); if (cos) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, cos, sizeof(*cos)); @@ -3807,7 +3932,7 @@ INTERCEPTOR(void, sincosf, float x, float *sin, float *cos) { COMMON_INTERCEPTOR_ENTER(ctx, sincosf, x, sin, cos); // FIXME: under ASan the call below may write to freed memory and corrupt // its metadata. See - // https://code.google.com/p/address-sanitizer/issues/detail?id=321. + // https://github.com/google/sanitizers/issues/321. REAL(sincosf)(x, sin, cos); if (sin) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, sin, sizeof(*sin)); if (cos) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, cos, sizeof(*cos)); @@ -3817,7 +3942,7 @@ INTERCEPTOR(void, sincosl, long double x, long double *sin, long double *cos) { COMMON_INTERCEPTOR_ENTER(ctx, sincosl, x, sin, cos); // FIXME: under ASan the call below may write to freed memory and corrupt // its metadata. See - // https://code.google.com/p/address-sanitizer/issues/detail?id=321. + // https://github.com/google/sanitizers/issues/321. REAL(sincosl)(x, sin, cos); if (sin) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, sin, sizeof(*sin)); if (cos) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, cos, sizeof(*cos)); @@ -3836,7 +3961,7 @@ INTERCEPTOR(double, remquo, double x, double y, int *quo) { COMMON_INTERCEPTOR_ENTER(ctx, remquo, x, y, quo); // FIXME: under ASan the call below may write to freed memory and corrupt // its metadata. See - // https://code.google.com/p/address-sanitizer/issues/detail?id=321. + // https://github.com/google/sanitizers/issues/321. double res = REAL(remquo)(x, y, quo); if (quo) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, quo, sizeof(*quo)); return res; @@ -3846,7 +3971,7 @@ INTERCEPTOR(float, remquof, float x, float y, int *quo) { COMMON_INTERCEPTOR_ENTER(ctx, remquof, x, y, quo); // FIXME: under ASan the call below may write to freed memory and corrupt // its metadata. See - // https://code.google.com/p/address-sanitizer/issues/detail?id=321. + // https://github.com/google/sanitizers/issues/321. float res = REAL(remquof)(x, y, quo); if (quo) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, quo, sizeof(*quo)); return res; @@ -3856,7 +3981,7 @@ INTERCEPTOR(long double, remquol, long double x, long double y, int *quo) { COMMON_INTERCEPTOR_ENTER(ctx, remquol, x, y, quo); // FIXME: under ASan the call below may write to freed memory and corrupt // its metadata. See - // https://code.google.com/p/address-sanitizer/issues/detail?id=321. + // https://github.com/google/sanitizers/issues/321. long double res = REAL(remquol)(x, y, quo); if (quo) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, quo, sizeof(*quo)); return res; @@ -3906,7 +4031,7 @@ INTERCEPTOR(double, lgamma_r, double x, int *signp) { COMMON_INTERCEPTOR_ENTER(ctx, lgamma_r, x, signp); // FIXME: under ASan the call below may write to freed memory and corrupt // its metadata. See - // https://code.google.com/p/address-sanitizer/issues/detail?id=321. + // https://github.com/google/sanitizers/issues/321. double res = REAL(lgamma_r)(x, signp); if (signp) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, signp, sizeof(*signp)); return res; @@ -3916,7 +4041,7 @@ INTERCEPTOR(float, lgammaf_r, float x, int *signp) { COMMON_INTERCEPTOR_ENTER(ctx, lgammaf_r, x, signp); // FIXME: under ASan the call below may write to freed memory and corrupt // its metadata. See - // https://code.google.com/p/address-sanitizer/issues/detail?id=321. + // https://github.com/google/sanitizers/issues/321. float res = REAL(lgammaf_r)(x, signp); if (signp) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, signp, sizeof(*signp)); return res; @@ -3934,7 +4059,7 @@ INTERCEPTOR(long double, lgammal_r, long double x, int *signp) { COMMON_INTERCEPTOR_ENTER(ctx, lgammal_r, x, signp); // FIXME: under ASan the call below may write to freed memory and corrupt // its metadata. See - // https://code.google.com/p/address-sanitizer/issues/detail?id=321. + // https://github.com/google/sanitizers/issues/321. long double res = REAL(lgammal_r)(x, signp); if (signp) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, signp, sizeof(*signp)); return res; @@ -3950,7 +4075,7 @@ INTERCEPTOR(int, drand48_r, void *buffer, double *result) { COMMON_INTERCEPTOR_ENTER(ctx, drand48_r, buffer, result); // FIXME: under ASan the call below may write to freed memory and corrupt // its metadata. See - // https://code.google.com/p/address-sanitizer/issues/detail?id=321. + // https://github.com/google/sanitizers/issues/321. int res = REAL(drand48_r)(buffer, result); if (result) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, result, sizeof(*result)); return res; @@ -3960,7 +4085,7 @@ INTERCEPTOR(int, lrand48_r, void *buffer, long *result) { COMMON_INTERCEPTOR_ENTER(ctx, lrand48_r, buffer, result); // FIXME: under ASan the call below may write to freed memory and corrupt // its metadata. See - // https://code.google.com/p/address-sanitizer/issues/detail?id=321. + // https://github.com/google/sanitizers/issues/321. int res = REAL(lrand48_r)(buffer, result); if (result) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, result, sizeof(*result)); return res; @@ -3990,7 +4115,7 @@ INTERCEPTOR(SSIZE_T, getline, char **lineptr, SIZE_T *n, void *stream) { COMMON_INTERCEPTOR_ENTER(ctx, getline, lineptr, n, stream); // FIXME: under ASan the call below may write to freed memory and corrupt // its metadata. See - // https://code.google.com/p/address-sanitizer/issues/detail?id=321. + // https://github.com/google/sanitizers/issues/321. SSIZE_T res = REAL(getline)(lineptr, n, stream); if (res > 0) { COMMON_INTERCEPTOR_WRITE_RANGE(ctx, lineptr, sizeof(*lineptr)); @@ -4002,7 +4127,7 @@ INTERCEPTOR(SSIZE_T, getline, char **lineptr, SIZE_T *n, void *stream) { // FIXME: under ASan the call below may write to freed memory and corrupt its // metadata. See -// https://code.google.com/p/address-sanitizer/issues/detail?id=321. +// https://github.com/google/sanitizers/issues/321. #define GETDELIM_INTERCEPTOR_IMPL(vname) \ { \ void *ctx; \ @@ -4046,10 +4171,10 @@ INTERCEPTOR(SIZE_T, iconv, void *cd, char **inbuf, SIZE_T *inbytesleft, COMMON_INTERCEPTOR_READ_RANGE(ctx, *inbuf, *inbytesleft); if (outbytesleft) COMMON_INTERCEPTOR_READ_RANGE(ctx, outbytesleft, sizeof(*outbytesleft)); - void *outbuf_orig = outbuf ? *outbuf : 0; + void *outbuf_orig = outbuf ? *outbuf : nullptr; // FIXME: under ASan the call below may write to freed memory and corrupt // its metadata. See - // https://code.google.com/p/address-sanitizer/issues/detail?id=321. + // https://github.com/google/sanitizers/issues/321. SIZE_T res = REAL(iconv)(cd, inbuf, inbytesleft, outbuf, outbytesleft); if (res != (SIZE_T) - 1 && outbuf && *outbuf > outbuf_orig) { SIZE_T sz = (char *)*outbuf - (char *)outbuf_orig; @@ -4068,7 +4193,7 @@ INTERCEPTOR(__sanitizer_clock_t, times, void *tms) { COMMON_INTERCEPTOR_ENTER(ctx, times, tms); // FIXME: under ASan the call below may write to freed memory and corrupt // its metadata. See - // https://code.google.com/p/address-sanitizer/issues/detail?id=321. + // https://github.com/google/sanitizers/issues/321. __sanitizer_clock_t res = REAL(times)(tms); if (res != (__sanitizer_clock_t)-1 && tms) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, tms, struct_tms_sz); @@ -4111,7 +4236,7 @@ INTERCEPTOR(SSIZE_T, listxattr, const char *path, char *list, SIZE_T size) { if (path) COMMON_INTERCEPTOR_READ_RANGE(ctx, path, REAL(strlen)(path) + 1); // FIXME: under ASan the call below may write to freed memory and corrupt // its metadata. See - // https://code.google.com/p/address-sanitizer/issues/detail?id=321. + // https://github.com/google/sanitizers/issues/321. SSIZE_T res = REAL(listxattr)(path, list, size); // Here and below, size == 0 is a special case where nothing is written to the // buffer, and res contains the desired buffer size. @@ -4124,7 +4249,7 @@ INTERCEPTOR(SSIZE_T, llistxattr, const char *path, char *list, SIZE_T size) { if (path) COMMON_INTERCEPTOR_READ_RANGE(ctx, path, REAL(strlen)(path) + 1); // FIXME: under ASan the call below may write to freed memory and corrupt // its metadata. See - // https://code.google.com/p/address-sanitizer/issues/detail?id=321. + // https://github.com/google/sanitizers/issues/321. SSIZE_T res = REAL(llistxattr)(path, list, size); if (size && res > 0 && list) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, list, res); return res; @@ -4134,7 +4259,7 @@ INTERCEPTOR(SSIZE_T, flistxattr, int fd, char *list, SIZE_T size) { COMMON_INTERCEPTOR_ENTER(ctx, flistxattr, fd, list, size); // FIXME: under ASan the call below may write to freed memory and corrupt // its metadata. See - // https://code.google.com/p/address-sanitizer/issues/detail?id=321. + // https://github.com/google/sanitizers/issues/321. SSIZE_T res = REAL(flistxattr)(fd, list, size); if (size && res > 0 && list) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, list, res); return res; @@ -4156,7 +4281,7 @@ INTERCEPTOR(SSIZE_T, getxattr, const char *path, const char *name, char *value, if (name) COMMON_INTERCEPTOR_READ_RANGE(ctx, name, REAL(strlen)(name) + 1); // FIXME: under ASan the call below may write to freed memory and corrupt // its metadata. See - // https://code.google.com/p/address-sanitizer/issues/detail?id=321. + // https://github.com/google/sanitizers/issues/321. SSIZE_T res = REAL(getxattr)(path, name, value, size); if (size && res > 0 && value) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, value, res); return res; @@ -4169,7 +4294,7 @@ INTERCEPTOR(SSIZE_T, lgetxattr, const char *path, const char *name, char *value, if (name) COMMON_INTERCEPTOR_READ_RANGE(ctx, name, REAL(strlen)(name) + 1); // FIXME: under ASan the call below may write to freed memory and corrupt // its metadata. See - // https://code.google.com/p/address-sanitizer/issues/detail?id=321. + // https://github.com/google/sanitizers/issues/321. SSIZE_T res = REAL(lgetxattr)(path, name, value, size); if (size && res > 0 && value) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, value, res); return res; @@ -4181,7 +4306,7 @@ INTERCEPTOR(SSIZE_T, fgetxattr, int fd, const char *name, char *value, if (name) COMMON_INTERCEPTOR_READ_RANGE(ctx, name, REAL(strlen)(name) + 1); // FIXME: under ASan the call below may write to freed memory and corrupt // its metadata. See - // https://code.google.com/p/address-sanitizer/issues/detail?id=321. + // https://github.com/google/sanitizers/issues/321. SSIZE_T res = REAL(fgetxattr)(fd, name, value, size); if (size && res > 0 && value) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, value, res); return res; @@ -4200,7 +4325,7 @@ INTERCEPTOR(int, getresuid, void *ruid, void *euid, void *suid) { COMMON_INTERCEPTOR_ENTER(ctx, getresuid, ruid, euid, suid); // FIXME: under ASan the call below may write to freed memory and corrupt // its metadata. See - // https://code.google.com/p/address-sanitizer/issues/detail?id=321. + // https://github.com/google/sanitizers/issues/321. int res = REAL(getresuid)(ruid, euid, suid); if (res >= 0) { if (ruid) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, ruid, uid_t_sz); @@ -4214,7 +4339,7 @@ INTERCEPTOR(int, getresgid, void *rgid, void *egid, void *sgid) { COMMON_INTERCEPTOR_ENTER(ctx, getresgid, rgid, egid, sgid); // FIXME: under ASan the call below may write to freed memory and corrupt // its metadata. See - // https://code.google.com/p/address-sanitizer/issues/detail?id=321. + // https://github.com/google/sanitizers/issues/321. int res = REAL(getresgid)(rgid, egid, sgid); if (res >= 0) { if (rgid) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, rgid, gid_t_sz); @@ -4239,7 +4364,7 @@ INTERCEPTOR(int, getifaddrs, __sanitizer_ifaddrs **ifap) { COMMON_INTERCEPTOR_ENTER(ctx, getifaddrs, ifap); // FIXME: under ASan the call below may write to freed memory and corrupt // its metadata. See - // https://code.google.com/p/address-sanitizer/issues/detail?id=321. + // https://github.com/google/sanitizers/issues/321. int res = REAL(getifaddrs)(ifap); if (res == 0 && ifap) { COMMON_INTERCEPTOR_WRITE_RANGE(ctx, ifap, sizeof(void *)); @@ -4275,7 +4400,7 @@ INTERCEPTOR(char *, if_indextoname, unsigned int ifindex, char* ifname) { COMMON_INTERCEPTOR_ENTER(ctx, if_indextoname, ifindex, ifname); // FIXME: under ASan the call below may write to freed memory and corrupt // its metadata. See - // https://code.google.com/p/address-sanitizer/issues/detail?id=321. + // https://github.com/google/sanitizers/issues/321. char *res = REAL(if_indextoname)(ifindex, ifname); if (res && ifname) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, ifname, REAL(strlen)(ifname) + 1); @@ -4303,7 +4428,7 @@ INTERCEPTOR(int, capget, void *hdrp, void *datap) { COMMON_INTERCEPTOR_READ_RANGE(ctx, hdrp, __user_cap_header_struct_sz); // FIXME: under ASan the call below may write to freed memory and corrupt // its metadata. See - // https://code.google.com/p/address-sanitizer/issues/detail?id=321. + // https://github.com/google/sanitizers/issues/321. int res = REAL(capget)(hdrp, datap); if (res == 0 && datap) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, datap, __user_cap_data_struct_sz); @@ -4329,9 +4454,9 @@ INTERCEPTOR(int, capset, void *hdrp, const void *datap) { #endif #if SANITIZER_INTERCEPT_AEABI_MEM -DECLARE_REAL_AND_INTERCEPTOR(void *, memmove, void *, const void *, uptr); -DECLARE_REAL_AND_INTERCEPTOR(void *, memcpy, void *, const void *, uptr); -DECLARE_REAL_AND_INTERCEPTOR(void *, memset, void *, int, uptr); +DECLARE_REAL_AND_INTERCEPTOR(void *, memmove, void *, const void *, uptr) +DECLARE_REAL_AND_INTERCEPTOR(void *, memcpy, void *, const void *, uptr) +DECLARE_REAL_AND_INTERCEPTOR(void *, memset, void *, int, uptr) INTERCEPTOR(void *, __aeabi_memmove, void *to, const void *from, uptr size) { return WRAP(memmove)(to, from, size); @@ -4404,7 +4529,7 @@ INTERCEPTOR(int, ftime, __sanitizer_timeb *tp) { COMMON_INTERCEPTOR_ENTER(ctx, ftime, tp); // FIXME: under ASan the call below may write to freed memory and corrupt // its metadata. See - // https://code.google.com/p/address-sanitizer/issues/detail?id=321. + // https://github.com/google/sanitizers/issues/321. int res = REAL(ftime)(tp); if (tp) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, tp, sizeof(*tp)); @@ -4422,7 +4547,7 @@ INTERCEPTOR(void, xdrmem_create, __sanitizer_XDR *xdrs, uptr addr, COMMON_INTERCEPTOR_ENTER(ctx, xdrmem_create, xdrs, addr, size, op); // FIXME: under ASan the call below may write to freed memory and corrupt // its metadata. See - // https://code.google.com/p/address-sanitizer/issues/detail?id=321. + // https://github.com/google/sanitizers/issues/321. REAL(xdrmem_create)(xdrs, addr, size, op); COMMON_INTERCEPTOR_WRITE_RANGE(ctx, xdrs, sizeof(*xdrs)); if (op == __sanitizer_XDR_ENCODE) { @@ -4437,14 +4562,14 @@ INTERCEPTOR(void, xdrstdio_create, __sanitizer_XDR *xdrs, void *file, int op) { COMMON_INTERCEPTOR_ENTER(ctx, xdrstdio_create, xdrs, file, op); // FIXME: under ASan the call below may write to freed memory and corrupt // its metadata. See - // https://code.google.com/p/address-sanitizer/issues/detail?id=321. + // https://github.com/google/sanitizers/issues/321. REAL(xdrstdio_create)(xdrs, file, op); COMMON_INTERCEPTOR_WRITE_RANGE(ctx, xdrs, sizeof(*xdrs)); } // FIXME: under ASan the call below may write to freed memory and corrupt // its metadata. See -// https://code.google.com/p/address-sanitizer/issues/detail?id=321. +// https://github.com/google/sanitizers/issues/321. #define XDR_INTERCEPTOR(F, T) \ INTERCEPTOR(int, F, __sanitizer_XDR *xdrs, T *p) { \ void *ctx; \ @@ -4498,7 +4623,7 @@ INTERCEPTOR(int, xdr_bytes, __sanitizer_XDR *xdrs, char **p, unsigned *sizep, } // FIXME: under ASan the call below may write to freed memory and corrupt // its metadata. See - // https://code.google.com/p/address-sanitizer/issues/detail?id=321. + // https://github.com/google/sanitizers/issues/321. int res = REAL(xdr_bytes)(xdrs, p, sizep, maxsize); if (p && sizep && xdrs->x_op == __sanitizer_XDR_DECODE) { COMMON_INTERCEPTOR_WRITE_RANGE(ctx, p, sizeof(*p)); @@ -4518,7 +4643,7 @@ INTERCEPTOR(int, xdr_string, __sanitizer_XDR *xdrs, char **p, } // FIXME: under ASan the call below may write to freed memory and corrupt // its metadata. See - // https://code.google.com/p/address-sanitizer/issues/detail?id=321. + // https://github.com/google/sanitizers/issues/321. int res = REAL(xdr_string)(xdrs, p, maxsize); if (p && xdrs->x_op == __sanitizer_XDR_DECODE) { COMMON_INTERCEPTOR_WRITE_RANGE(ctx, p, sizeof(*p)); @@ -4570,7 +4695,7 @@ INTERCEPTOR(void *, tsearch, void *key, void **rootp, COMMON_INTERCEPTOR_ENTER(ctx, tsearch, key, rootp, compar); // FIXME: under ASan the call below may write to freed memory and corrupt // its metadata. See - // https://code.google.com/p/address-sanitizer/issues/detail?id=321. + // https://github.com/google/sanitizers/issues/321. void *res = REAL(tsearch)(key, rootp, compar); if (res && *(void **)res == key) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, res, sizeof(void *)); @@ -4652,7 +4777,7 @@ INTERCEPTOR(int, __woverflow, __sanitizer_FILE *fp, int ch) { INTERCEPTOR(__sanitizer_FILE *, fopen, const char *path, const char *mode) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, fopen, path, mode); - COMMON_INTERCEPTOR_READ_RANGE(ctx, path, REAL(strlen)(path) + 1); + if (path) COMMON_INTERCEPTOR_READ_RANGE(ctx, path, REAL(strlen)(path) + 1); COMMON_INTERCEPTOR_READ_RANGE(ctx, mode, REAL(strlen)(mode) + 1); __sanitizer_FILE *res = REAL(fopen)(path, mode); COMMON_INTERCEPTOR_FILE_OPEN(ctx, res, path); @@ -4671,7 +4796,7 @@ INTERCEPTOR(__sanitizer_FILE *, freopen, const char *path, const char *mode, __sanitizer_FILE *fp) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, freopen, path, mode, fp); - COMMON_INTERCEPTOR_READ_RANGE(ctx, path, REAL(strlen)(path) + 1); + if (path) COMMON_INTERCEPTOR_READ_RANGE(ctx, path, REAL(strlen)(path) + 1); COMMON_INTERCEPTOR_READ_RANGE(ctx, mode, REAL(strlen)(mode) + 1); COMMON_INTERCEPTOR_FILE_CLOSE(ctx, fp); __sanitizer_FILE *res = REAL(freopen)(path, mode, fp); @@ -4702,7 +4827,7 @@ INTERCEPTOR(__sanitizer_FILE *, freopen64, const char *path, const char *mode, __sanitizer_FILE *fp) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, freopen64, path, mode, fp); - COMMON_INTERCEPTOR_READ_RANGE(ctx, path, REAL(strlen)(path) + 1); + if (path) COMMON_INTERCEPTOR_READ_RANGE(ctx, path, REAL(strlen)(path) + 1); COMMON_INTERCEPTOR_READ_RANGE(ctx, mode, REAL(strlen)(mode) + 1); COMMON_INTERCEPTOR_FILE_CLOSE(ctx, fp); __sanitizer_FILE *res = REAL(freopen64)(path, mode, fp); @@ -4723,7 +4848,7 @@ INTERCEPTOR(__sanitizer_FILE *, open_memstream, char **ptr, SIZE_T *sizeloc) { COMMON_INTERCEPTOR_ENTER(ctx, open_memstream, ptr, sizeloc); // FIXME: under ASan the call below may write to freed memory and corrupt // its metadata. See - // https://code.google.com/p/address-sanitizer/issues/detail?id=321. + // https://github.com/google/sanitizers/issues/321. __sanitizer_FILE *res = REAL(open_memstream)(ptr, sizeloc); if (res) { COMMON_INTERCEPTOR_WRITE_RANGE(ctx, ptr, sizeof(*ptr)); @@ -4754,7 +4879,7 @@ INTERCEPTOR(__sanitizer_FILE *, fmemopen, void *buf, SIZE_T size, COMMON_INTERCEPTOR_ENTER(ctx, fmemopen, buf, size, mode); // FIXME: under ASan the call below may write to freed memory and corrupt // its metadata. See - // https://code.google.com/p/address-sanitizer/issues/detail?id=321. + // https://github.com/google/sanitizers/issues/321. __sanitizer_FILE *res = REAL(fmemopen)(buf, size, mode); if (res) unpoison_file(res); return res; @@ -4831,15 +4956,14 @@ INTERCEPTOR(int, fflush, __sanitizer_FILE *fp) { INTERCEPTOR(int, fclose, __sanitizer_FILE *fp) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, fclose, fp); - if (fp) { - COMMON_INTERCEPTOR_FILE_CLOSE(ctx, fp); - const FileMetadata *m = GetInterceptorMetadata(fp); - if (m) { - COMMON_INTERCEPTOR_INITIALIZE_RANGE(*m->addr, *m->size); - DeleteInterceptorMetadata(fp); - } + COMMON_INTERCEPTOR_FILE_CLOSE(ctx, fp); + const FileMetadata *m = GetInterceptorMetadata(fp); + int res = REAL(fclose)(fp); + if (m) { + COMMON_INTERCEPTOR_INITIALIZE_RANGE(*m->addr, *m->size); + DeleteInterceptorMetadata(fp); } - return REAL(fclose)(fp); + return res; } #define INIT_FCLOSE COMMON_INTERCEPT_FUNCTION(fclose); #else @@ -4922,7 +5046,7 @@ static void MlockIsUnsupported() { static atomic_uint8_t printed; if (atomic_exchange(&printed, 1, memory_order_relaxed)) return; - VPrintf(1, "INFO: %s ignores mlock/mlockall/munlock/munlockall\n", + VPrintf(1, "%s ignores mlock/mlockall/munlock/munlockall\n", SanitizerToolName); } @@ -5013,6 +5137,192 @@ INTERCEPTOR(__sanitizer_FILE *, fopencookie, void *cookie, const char *mode, #define INIT_FOPENCOOKIE #endif // SANITIZER_INTERCEPT_FOPENCOOKIE +#if SANITIZER_INTERCEPT_SEM +INTERCEPTOR(int, sem_init, __sanitizer_sem_t *s, int pshared, unsigned value) { + void *ctx; + COMMON_INTERCEPTOR_ENTER(ctx, sem_init, s, pshared, value); + // Workaround a bug in glibc's "old" semaphore implementation by + // zero-initializing the sem_t contents. This has to be done here because + // interceptors bind to the lowest symbols version by default, hitting the + // buggy code path while the non-sanitized build of the same code works fine. + REAL(memset)(s, 0, sizeof(*s)); + int res = REAL(sem_init)(s, pshared, value); + return res; +} + +INTERCEPTOR(int, sem_destroy, __sanitizer_sem_t *s) { + void *ctx; + COMMON_INTERCEPTOR_ENTER(ctx, sem_destroy, s); + int res = REAL(sem_destroy)(s); + return res; +} + +INTERCEPTOR(int, sem_wait, __sanitizer_sem_t *s) { + void *ctx; + COMMON_INTERCEPTOR_ENTER(ctx, sem_wait, s); + int res = COMMON_INTERCEPTOR_BLOCK_REAL(sem_wait)(s); + if (res == 0) { + COMMON_INTERCEPTOR_ACQUIRE(ctx, (uptr)s); + } + return res; +} + +INTERCEPTOR(int, sem_trywait, __sanitizer_sem_t *s) { + void *ctx; + COMMON_INTERCEPTOR_ENTER(ctx, sem_trywait, s); + int res = COMMON_INTERCEPTOR_BLOCK_REAL(sem_trywait)(s); + if (res == 0) { + COMMON_INTERCEPTOR_ACQUIRE(ctx, (uptr)s); + } + return res; +} + +INTERCEPTOR(int, sem_timedwait, __sanitizer_sem_t *s, void *abstime) { + void *ctx; + COMMON_INTERCEPTOR_ENTER(ctx, sem_timedwait, s, abstime); + COMMON_INTERCEPTOR_READ_RANGE(ctx, abstime, struct_timespec_sz); + int res = COMMON_INTERCEPTOR_BLOCK_REAL(sem_timedwait)(s, abstime); + if (res == 0) { + COMMON_INTERCEPTOR_ACQUIRE(ctx, (uptr)s); + } + return res; +} + +INTERCEPTOR(int, sem_post, __sanitizer_sem_t *s) { + void *ctx; + COMMON_INTERCEPTOR_ENTER(ctx, sem_post, s); + COMMON_INTERCEPTOR_RELEASE(ctx, (uptr)s); + int res = REAL(sem_post)(s); + return res; +} + +INTERCEPTOR(int, sem_getvalue, __sanitizer_sem_t *s, int *sval) { + void *ctx; + COMMON_INTERCEPTOR_ENTER(ctx, sem_getvalue, s, sval); + int res = REAL(sem_getvalue)(s, sval); + if (res == 0) { + COMMON_INTERCEPTOR_ACQUIRE(ctx, (uptr)s); + COMMON_INTERCEPTOR_WRITE_RANGE(ctx, sval, sizeof(*sval)); + } + return res; +} +#define INIT_SEM \ + COMMON_INTERCEPT_FUNCTION(sem_init); \ + COMMON_INTERCEPT_FUNCTION(sem_destroy); \ + COMMON_INTERCEPT_FUNCTION(sem_wait); \ + COMMON_INTERCEPT_FUNCTION(sem_trywait); \ + COMMON_INTERCEPT_FUNCTION(sem_timedwait); \ + COMMON_INTERCEPT_FUNCTION(sem_post); \ + COMMON_INTERCEPT_FUNCTION(sem_getvalue); +#else +#define INIT_SEM +#endif // SANITIZER_INTERCEPT_SEM + +#if SANITIZER_INTERCEPT_PTHREAD_SETCANCEL +INTERCEPTOR(int, pthread_setcancelstate, int state, int *oldstate) { + void *ctx; + COMMON_INTERCEPTOR_ENTER(ctx, pthread_setcancelstate, state, oldstate); + int res = REAL(pthread_setcancelstate)(state, oldstate); + if (res == 0) + COMMON_INTERCEPTOR_WRITE_RANGE(ctx, oldstate, sizeof(*oldstate)); + return res; +} + +INTERCEPTOR(int, pthread_setcanceltype, int type, int *oldtype) { + void *ctx; + COMMON_INTERCEPTOR_ENTER(ctx, pthread_setcanceltype, type, oldtype); + int res = REAL(pthread_setcanceltype)(type, oldtype); + if (res == 0) + COMMON_INTERCEPTOR_WRITE_RANGE(ctx, oldtype, sizeof(*oldtype)); + return res; +} +#define INIT_PTHREAD_SETCANCEL \ + COMMON_INTERCEPT_FUNCTION(pthread_setcancelstate); \ + COMMON_INTERCEPT_FUNCTION(pthread_setcanceltype); +#else +#define INIT_PTHREAD_SETCANCEL +#endif + +#if SANITIZER_INTERCEPT_MINCORE +INTERCEPTOR(int, mincore, void *addr, uptr length, unsigned char *vec) { + void *ctx; + COMMON_INTERCEPTOR_ENTER(ctx, mincore, addr, length, vec); + int res = REAL(mincore)(addr, length, vec); + if (res == 0) { + uptr page_size = GetPageSizeCached(); + uptr vec_size = ((length + page_size - 1) & (~(page_size - 1))) / page_size; + COMMON_INTERCEPTOR_WRITE_RANGE(ctx, vec, vec_size); + } + return res; +} +#define INIT_MINCORE COMMON_INTERCEPT_FUNCTION(mincore); +#else +#define INIT_MINCORE +#endif + +#if SANITIZER_INTERCEPT_PROCESS_VM_READV +INTERCEPTOR(SSIZE_T, process_vm_readv, int pid, __sanitizer_iovec *local_iov, + uptr liovcnt, __sanitizer_iovec *remote_iov, uptr riovcnt, + uptr flags) { + void *ctx; + COMMON_INTERCEPTOR_ENTER(ctx, process_vm_readv, pid, local_iov, liovcnt, + remote_iov, riovcnt, flags); + SSIZE_T res = REAL(process_vm_readv)(pid, local_iov, liovcnt, remote_iov, + riovcnt, flags); + if (res > 0) + write_iovec(ctx, local_iov, liovcnt, res); + return res; +} + +INTERCEPTOR(SSIZE_T, process_vm_writev, int pid, __sanitizer_iovec *local_iov, + uptr liovcnt, __sanitizer_iovec *remote_iov, uptr riovcnt, + uptr flags) { + void *ctx; + COMMON_INTERCEPTOR_ENTER(ctx, process_vm_writev, pid, local_iov, liovcnt, + remote_iov, riovcnt, flags); + SSIZE_T res = REAL(process_vm_writev)(pid, local_iov, liovcnt, remote_iov, + riovcnt, flags); + if (res > 0) + read_iovec(ctx, local_iov, liovcnt, res); + return res; +} +#define INIT_PROCESS_VM_READV \ + COMMON_INTERCEPT_FUNCTION(process_vm_readv); \ + COMMON_INTERCEPT_FUNCTION(process_vm_writev); +#else +#define INIT_PROCESS_VM_READV +#endif + +#if SANITIZER_INTERCEPT_CTERMID +INTERCEPTOR(char *, ctermid, char *s) { + void *ctx; + COMMON_INTERCEPTOR_ENTER(ctx, ctermid, s); + char *res = REAL(ctermid)(s); + if (res) { + COMMON_INTERCEPTOR_INITIALIZE_RANGE(res, REAL(strlen)(res) + 1); + } + return res; +} +#define INIT_CTERMID COMMON_INTERCEPT_FUNCTION(ctermid); +#else +#define INIT_CTERMID +#endif + +#if SANITIZER_INTERCEPT_CTERMID_R +INTERCEPTOR(char *, ctermid_r, char *s) { + void *ctx; + COMMON_INTERCEPTOR_ENTER(ctx, ctermid_r, s); + char *res = REAL(ctermid_r)(s); + if (res) { + COMMON_INTERCEPTOR_INITIALIZE_RANGE(res, REAL(strlen)(res) + 1); + } + return res; +} +#define INIT_CTERMID_R COMMON_INTERCEPT_FUNCTION(ctermid_r); +#else +#define INIT_CTERMID_R +#endif + static void InitializeCommonInterceptors() { static u64 metadata_mem[sizeof(MetadataHashMap) / sizeof(u64) + 1]; interceptor_metadata_map = new((void *)&metadata_mem) MetadataHashMap(); @@ -5027,6 +5337,7 @@ static void InitializeCommonInterceptors() { INIT_STRSPN; INIT_STRPBRK; INIT_MEMCHR; + INIT_MEMCMP; INIT_MEMRCHR; INIT_READ; INIT_PREAD; @@ -5092,6 +5403,7 @@ static void InitializeCommonInterceptors() { INIT_MBSNRTOWCS; INIT_WCSTOMBS; INIT_WCSNRTOMBS; + INIT_WCRTOMB; INIT_TCGETATTR; INIT_REALPATH; INIT_CANONICALIZE_FILE_NAME; @@ -5181,4 +5493,10 @@ static void InitializeCommonInterceptors() { INIT_TIMERFD; INIT_MLOCKX; INIT_FOPENCOOKIE; + INIT_SEM; + INIT_PTHREAD_SETCANCEL; + INIT_MINCORE; + INIT_PROCESS_VM_READV; + INIT_CTERMID; + INIT_CTERMID_R; } diff --git a/contrib/compiler-rt/lib/sanitizer_common/sanitizer_common_interceptors_format.inc b/contrib/compiler-rt/lib/sanitizer_common/sanitizer_common_interceptors_format.inc index 8f94802aeae8..92318cda35fd 100644 --- a/contrib/compiler-rt/lib/sanitizer_common/sanitizer_common_interceptors_format.inc +++ b/contrib/compiler-rt/lib/sanitizer_common/sanitizer_common_interceptors_format.inc @@ -13,6 +13,7 @@ // with a few common GNU extensions. // //===----------------------------------------------------------------------===// + #include <stdarg.h> static const char *parse_number(const char *p, int *out) { @@ -191,7 +192,7 @@ static const char *scanf_parse_next(const char *p, bool allowGnuMalloc, continue; } if (*p == '\0') { - return 0; + return nullptr; } // %n$ p = maybe_parse_param_index(p, &dir->argIdx); @@ -206,7 +207,7 @@ static const char *scanf_parse_next(const char *p, bool allowGnuMalloc, p = parse_number(p, &dir->fieldWidth); CHECK(p); if (dir->fieldWidth <= 0) // Width if at all must be non-zero - return 0; + return nullptr; } // m if (*p == 'm') { @@ -226,8 +227,8 @@ static const char *scanf_parse_next(const char *p, bool allowGnuMalloc, while (*p && *p != ']') ++p; if (*p == 0) - return 0; // unexpected end of string - // Consume the closing ']'. + return nullptr; // unexpected end of string + // Consume the closing ']'. ++p; } // This is unfortunately ambiguous between old GNU extension @@ -251,7 +252,7 @@ static const char *scanf_parse_next(const char *p, bool allowGnuMalloc, while (*q && *q != ']' && *q != '%') ++q; if (*q == 0 || *q == '%') - return 0; + return nullptr; p = q + 1; // Consume the closing ']'. dir->maybeGnuMalloc = true; } @@ -395,7 +396,7 @@ static const char *printf_parse_next(const char *p, PrintfDirective *dir) { continue; } if (*p == '\0') { - return 0; + return nullptr; } // %n$ p = maybe_parse_param_index(p, &dir->precisionIdx); @@ -408,7 +409,7 @@ static const char *printf_parse_next(const char *p, PrintfDirective *dir) { p = maybe_parse_number_or_star(p, &dir->fieldWidth, &dir->starredWidth); if (!p) - return 0; + return nullptr; // Precision if (*p == '.') { ++p; @@ -416,7 +417,7 @@ static const char *printf_parse_next(const char *p, PrintfDirective *dir) { p = maybe_parse_number_or_star(p, &dir->fieldPrecision, &dir->starredPrecision); if (!p) - return 0; + return nullptr; // m$ if (dir->starredPrecision) { p = maybe_parse_param_index(p, &dir->precisionIdx); @@ -556,4 +557,4 @@ static void printf_common(void *ctx, const char *format, va_list aq) { } } -#endif // SANITIZER_INTERCEPT_PRINTF +#endif // SANITIZER_INTERCEPT_PRINTF diff --git a/contrib/compiler-rt/lib/sanitizer_common/sanitizer_common_interceptors_ioctl.inc b/contrib/compiler-rt/lib/sanitizer_common/sanitizer_common_interceptors_ioctl.inc index b94c21c96aa0..fcd0a3d70f52 100755 --- a/contrib/compiler-rt/lib/sanitizer_common/sanitizer_common_interceptors_ioctl.inc +++ b/contrib/compiler-rt/lib/sanitizer_common/sanitizer_common_interceptors_ioctl.inc @@ -520,7 +520,7 @@ static const ioctl_desc *ioctl_table_lookup(unsigned req) { if (left == right && ioctl_table[left].req == req) return ioctl_table + left; else - return 0; + return nullptr; } static bool ioctl_decode(unsigned req, ioctl_desc *desc) { @@ -567,7 +567,7 @@ static const ioctl_desc *ioctl_lookup(unsigned req) { (desc->type == ioctl_desc::READWRITE || desc->type == ioctl_desc::WRITE || desc->type == ioctl_desc::READ)) return desc; - return 0; + return nullptr; } static void ioctl_common_pre(void *ctx, const ioctl_desc *desc, int d, diff --git a/contrib/compiler-rt/lib/sanitizer_common/sanitizer_common_libcdep.cc b/contrib/compiler-rt/lib/sanitizer_common/sanitizer_common_libcdep.cc index 1b65bced75d5..596f5bcd3173 100644 --- a/contrib/compiler-rt/lib/sanitizer_common/sanitizer_common_libcdep.cc +++ b/contrib/compiler-rt/lib/sanitizer_common/sanitizer_common_libcdep.cc @@ -12,6 +12,7 @@ //===----------------------------------------------------------------------===// #include "sanitizer_common.h" + #include "sanitizer_flags.h" #include "sanitizer_stackdepot.h" #include "sanitizer_stacktrace.h" @@ -46,6 +47,7 @@ void SetSandboxingCallback(void (*f)()) { } void ReportErrorSummary(const char *error_type, StackTrace *stack) { +#if !SANITIZER_GO if (!common_flags()->print_summary) return; if (stack->size == 0) { @@ -58,6 +60,7 @@ void ReportErrorSummary(const char *error_type, StackTrace *stack) { SymbolizedStack *frame = Symbolizer::GetOrInit()->SymbolizePC(pc); ReportErrorSummary(error_type, frame->info); frame->ClearAll(); +#endif } static void (*SoftRssLimitExceededCallback)(bool exceeded); @@ -116,8 +119,27 @@ void BackgroundThread(void *arg) { } } +void WriteToSyslog(const char *msg) { + InternalScopedString msg_copy(kErrorMessageBufferSize); + msg_copy.append("%s", msg); + char *p = msg_copy.data(); + char *q; + + // Print one line at a time. + // syslog, at least on Android, has an implicit message length limit. + do { + q = internal_strchr(p, '\n'); + if (q) + *q = '\0'; + WriteOneLineToSyslog(p); + if (q) + p = q + 1; + } while (q); +} + void MaybeStartBackgroudThread() { -#if SANITIZER_LINUX // Need to implement/test on other platforms. +#if SANITIZER_LINUX && \ + !SANITIZER_GO // Need to implement/test on other platforms. // Start the background thread if one of the rss limits is given. if (!common_flags()->hard_rss_limit_mb && !common_flags()->soft_rss_limit_mb) return; diff --git a/contrib/compiler-rt/lib/sanitizer_common/sanitizer_common_nolibc.cc b/contrib/compiler-rt/lib/sanitizer_common/sanitizer_common_nolibc.cc new file mode 100644 index 000000000000..9f4f97224e18 --- /dev/null +++ b/contrib/compiler-rt/lib/sanitizer_common/sanitizer_common_nolibc.cc @@ -0,0 +1,27 @@ +//===-- sanitizer_common_nolibc.cc ----------------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file contains stubs for libc function to facilitate optional use of +// libc in no-libcdep sources. +//===----------------------------------------------------------------------===// + +#include "sanitizer_platform.h" +#include "sanitizer_common.h" +#include "sanitizer_libc.h" + +namespace __sanitizer { + +#if SANITIZER_LINUX +bool ShouldLogAfterPrintf() { return false; } +void LogMessageOnPrintf(const char *str) {} +#endif +void WriteToSyslog(const char *buffer) {} +void Abort() { internal__exit(1); } + +} // namespace __sanitizer diff --git a/contrib/compiler-rt/lib/sanitizer_common/sanitizer_common_syscalls.inc b/contrib/compiler-rt/lib/sanitizer_common/sanitizer_common_syscalls.inc index 5d4840f961ef..008e57745cc5 100644 --- a/contrib/compiler-rt/lib/sanitizer_common/sanitizer_common_syscalls.inc +++ b/contrib/compiler-rt/lib/sanitizer_common/sanitizer_common_syscalls.inc @@ -2301,7 +2301,7 @@ POST_SYSCALL(ni_syscall)(long res) {} PRE_SYSCALL(ptrace)(long request, long pid, long addr, long data) { #if !SANITIZER_ANDROID && \ (defined(__i386) || defined(__x86_64) || defined(__mips64) || \ - defined(__powerpc64__)) + defined(__powerpc64__) || defined(__aarch64__)) if (data) { if (request == ptrace_setregs) { PRE_READ((void *)data, struct_user_regs_struct_sz); @@ -2322,7 +2322,7 @@ PRE_SYSCALL(ptrace)(long request, long pid, long addr, long data) { POST_SYSCALL(ptrace)(long res, long request, long pid, long addr, long data) { #if !SANITIZER_ANDROID && \ (defined(__i386) || defined(__x86_64) || defined(__mips64) || \ - defined(__powerpc64__)) + defined(__powerpc64__) || defined(__aarch64__)) if (res >= 0 && data) { // Note that this is different from the interceptor in // sanitizer_common_interceptors.inc. diff --git a/contrib/compiler-rt/lib/sanitizer_common/sanitizer_coverage_libcdep.cc b/contrib/compiler-rt/lib/sanitizer_common/sanitizer_coverage_libcdep.cc index f511c996d8c2..eaa1446afd44 100644 --- a/contrib/compiler-rt/lib/sanitizer_common/sanitizer_coverage_libcdep.cc +++ b/contrib/compiler-rt/lib/sanitizer_common/sanitizer_coverage_libcdep.cc @@ -53,6 +53,12 @@ static const u64 kMagic32 = 0xC0BFFFFFFFFFFF32ULL; static atomic_uint32_t dump_once_guard; // Ensure that CovDump runs only once. static atomic_uintptr_t coverage_counter; +static atomic_uintptr_t caller_callee_counter; + +static void ResetGlobalCounters() { + return atomic_store(&coverage_counter, 0, memory_order_relaxed); + return atomic_store(&caller_callee_counter, 0, memory_order_relaxed); +} // pc_array is the array containing the covered PCs. // To make the pc_array thread- and async-signal-safe it has to be large enough. @@ -90,7 +96,7 @@ class CoverageData { void DumpAll(); ALWAYS_INLINE - void TraceBasicBlock(s32 *id); + void TraceBasicBlock(u32 *id); void InitializeGuardArray(s32 *guards); void InitializeGuards(s32 *guards, uptr n, const char *module_name, @@ -102,6 +108,7 @@ class CoverageData { uptr *data(); uptr size(); + uptr *buffer() const { return pc_buffer; } private: void DirectOpen(); @@ -127,6 +134,8 @@ class CoverageData { // Descriptor of the file mapped pc array. fd_t pc_fd; + uptr *pc_buffer; + // Vector of coverage guard arrays, protected by mu. InternalMmapVectorNoCtor<s32*> guard_array_vec; @@ -203,6 +212,11 @@ void CoverageData::Enable() { atomic_store(&pc_array_size, kPcArrayMaxSize, memory_order_relaxed); } + pc_buffer = nullptr; + if (common_flags()->coverage_pc_buffer) + pc_buffer = reinterpret_cast<uptr *>(MmapNoReserveOrDie( + sizeof(uptr) * kPcArrayMaxSize, "CovInit::pc_buffer")); + cc_array = reinterpret_cast<uptr **>(MmapNoReserveOrDie( sizeof(uptr *) * kCcArrayMaxSize, "CovInit::cc_array")); atomic_store(&cc_array_size, kCcArrayMaxSize, memory_order_relaxed); @@ -225,7 +239,8 @@ void CoverageData::InitializeGuardArray(s32 *guards) { Enable(); // Make sure coverage is enabled at this point. s32 n = guards[0]; for (s32 j = 1; j <= n; j++) { - uptr idx = atomic_fetch_add(&pc_array_index, 1, memory_order_relaxed); + uptr idx = atomic_load_relaxed(&pc_array_index); + atomic_store_relaxed(&pc_array_index, idx + 1); guards[j] = -static_cast<s32>(idx + 1); } } @@ -239,6 +254,10 @@ void CoverageData::Disable() { UnmapOrDie(cc_array, sizeof(uptr *) * kCcArrayMaxSize); cc_array = nullptr; } + if (pc_buffer) { + UnmapOrDie(pc_buffer, sizeof(uptr) * kPcArrayMaxSize); + pc_buffer = nullptr; + } if (tr_event_array) { UnmapOrDie(tr_event_array, sizeof(tr_event_array[0]) * kTrEventArrayMaxSize + @@ -407,6 +426,7 @@ void CoverageData::Add(uptr pc, u32 *guard) { atomic_load(&pc_array_size, memory_order_acquire)); uptr counter = atomic_fetch_add(&coverage_counter, 1, memory_order_relaxed); pc_array[idx] = BundlePcAndCounter(pc, counter); + if (pc_buffer) pc_buffer[counter] = pc; } // Registers a pair caller=>callee. @@ -435,7 +455,7 @@ void CoverageData::IndirCall(uptr caller, uptr callee, uptr callee_cache[], uptr was = 0; if (atomic_compare_exchange_strong(&atomic_callee_cache[i], &was, callee, memory_order_seq_cst)) { - atomic_fetch_add(&coverage_counter, 1, memory_order_relaxed); + atomic_fetch_add(&caller_callee_counter, 1, memory_order_relaxed); return; } if (was == callee) // Already have this callee. @@ -675,11 +695,11 @@ void CoverageData::DumpCallerCalleePairs() { // it once and then cache in the provided 'cache' storage. // // This function will eventually be inlined by the compiler. -void CoverageData::TraceBasicBlock(s32 *id) { +void CoverageData::TraceBasicBlock(u32 *id) { // Will trap here if // 1. coverage is not enabled at run-time. // 2. The array tr_event_array is full. - *tr_event_pointer = static_cast<u32>(*id - 1); + *tr_event_pointer = *id - 1; tr_event_pointer++; } @@ -813,7 +833,7 @@ void CovPrepareForSandboxing(__sanitizer_sandbox_arguments *args) { } else { InternalScopedString path(kMaxPathLength); // Pre-open the file now. The sandbox won't allow us to do it later. - cov_fd = CovOpenFile(&path, true /* packed */, 0); + cov_fd = CovOpenFile(&path, true /* packed */, nullptr); } } @@ -832,6 +852,11 @@ void CovAfterFork(int child_pid) { coverage_data.AfterFork(child_pid); } +static void MaybeDumpCoverage() { + if (common_flags()->coverage) + __sanitizer_cov_dump(); +} + void InitializeCoverage(bool enabled, const char *dir) { if (coverage_enabled) return; // May happen if two sanitizer enable coverage in the same process. @@ -840,6 +865,7 @@ void InitializeCoverage(bool enabled, const char *dir) { coverage_data.Init(); if (enabled) coverage_data.Enable(); if (!common_flags()->coverage_direct) Atexit(__sanitizer_cov_dump); + AddDieCallback(MaybeDumpCoverage); } void ReInitializeCoverage(bool enabled, const char *dir) { @@ -853,7 +879,7 @@ void CoverageUpdateMapping() { CovUpdateMapping(coverage_dir); } -} // namespace __sanitizer +} // namespace __sanitizer extern "C" { SANITIZER_INTERFACE_ATTRIBUTE void __sanitizer_cov(u32 *guard) { @@ -902,15 +928,23 @@ uptr __sanitizer_get_total_unique_coverage() { } SANITIZER_INTERFACE_ATTRIBUTE -void __sanitizer_cov_trace_func_enter(s32 *id) { +uptr __sanitizer_get_total_unique_caller_callee_pairs() { + return atomic_load(&caller_callee_counter, memory_order_relaxed); +} + +SANITIZER_INTERFACE_ATTRIBUTE +void __sanitizer_cov_trace_func_enter(u32 *id) { + __sanitizer_cov_with_check(id); coverage_data.TraceBasicBlock(id); } SANITIZER_INTERFACE_ATTRIBUTE -void __sanitizer_cov_trace_basic_block(s32 *id) { +void __sanitizer_cov_trace_basic_block(u32 *id) { + __sanitizer_cov_with_check(id); coverage_data.TraceBasicBlock(id); } SANITIZER_INTERFACE_ATTRIBUTE void __sanitizer_reset_coverage() { + ResetGlobalCounters(); coverage_data.ReinitializeGuards(); internal_bzero_aligned16( coverage_data.data(), @@ -923,6 +957,12 @@ uptr __sanitizer_get_coverage_guards(uptr **data) { } SANITIZER_INTERFACE_ATTRIBUTE +uptr __sanitizer_get_coverage_pc_buffer(uptr **data) { + *data = coverage_data.buffer(); + return __sanitizer_get_total_unique_coverage(); +} + +SANITIZER_INTERFACE_ATTRIBUTE uptr __sanitizer_get_number_of_counters() { return coverage_data.GetNumberOf8bitCounters(); } @@ -931,7 +971,9 @@ SANITIZER_INTERFACE_ATTRIBUTE uptr __sanitizer_update_counter_bitset_and_clear_counters(u8 *bitset) { return coverage_data.Update8bitCounterBitsetAndClearCounters(bitset); } -// Default empty implementation (weak). Users should redefine it. +// Default empty implementations (weak). Users should redefine them. SANITIZER_INTERFACE_ATTRIBUTE SANITIZER_WEAK_ATTRIBUTE void __sanitizer_cov_trace_cmp() {} -} // extern "C" +SANITIZER_INTERFACE_ATTRIBUTE SANITIZER_WEAK_ATTRIBUTE +void __sanitizer_cov_trace_switch() {} +} // extern "C" diff --git a/contrib/compiler-rt/lib/sanitizer_common/sanitizer_coverage_mapping_libcdep.cc b/contrib/compiler-rt/lib/sanitizer_common/sanitizer_coverage_mapping_libcdep.cc index a3d75abc0476..c8b5d9014ede 100644 --- a/contrib/compiler-rt/lib/sanitizer_common/sanitizer_coverage_mapping_libcdep.cc +++ b/contrib/compiler-rt/lib/sanitizer_common/sanitizer_coverage_mapping_libcdep.cc @@ -75,7 +75,7 @@ void CovUpdateMapping(const char *coverage_dir, uptr caller_pc) { InternalScopedBuffer<LoadedModule> modules(kMaxNumberOfModules); CHECK(modules.data()); int n_modules = GetListOfModules(modules.data(), kMaxNumberOfModules, - /* filter */ 0); + /* filter */ nullptr); text.append("%d\n", sizeof(uptr) * 8); for (int i = 0; i < n_modules; ++i) { @@ -124,4 +124,4 @@ void CovUpdateMapping(const char *coverage_dir, uptr caller_pc) { } } -} // namespace __sanitizer +} // namespace __sanitizer diff --git a/contrib/compiler-rt/lib/sanitizer_common/sanitizer_deadlock_detector1.cc b/contrib/compiler-rt/lib/sanitizer_common/sanitizer_deadlock_detector1.cc index 5890b54b933b..bd57a403bdcc 100644 --- a/contrib/compiler-rt/lib/sanitizer_common/sanitizer_deadlock_detector1.cc +++ b/contrib/compiler-rt/lib/sanitizer_common/sanitizer_deadlock_detector1.cc @@ -71,7 +71,7 @@ DD::DD(const DDFlags *flags) } DDPhysicalThread* DD::CreatePhysicalThread() { - return 0; + return nullptr; } void DD::DestroyPhysicalThread(DDPhysicalThread *pt) { @@ -181,10 +181,10 @@ void DD::MutexDestroy(DDCallback *cb, DDReport *DD::GetReport(DDCallback *cb) { if (!cb->lt->report_pending) - return 0; + return nullptr; cb->lt->report_pending = false; return &cb->lt->rep; } -} // namespace __sanitizer -#endif // #if SANITIZER_DEADLOCK_DETECTOR_VERSION == 1 +} // namespace __sanitizer +#endif // #if SANITIZER_DEADLOCK_DETECTOR_VERSION == 1 diff --git a/contrib/compiler-rt/lib/sanitizer_common/sanitizer_deadlock_detector_interface.h b/contrib/compiler-rt/lib/sanitizer_common/sanitizer_deadlock_detector_interface.h index 8cf26e0d5528..b6e91a16e257 100644 --- a/contrib/compiler-rt/lib/sanitizer_common/sanitizer_deadlock_detector_interface.h +++ b/contrib/compiler-rt/lib/sanitizer_common/sanitizer_deadlock_detector_interface.h @@ -72,10 +72,10 @@ struct DDCallback { struct DDetector { static DDetector *Create(const DDFlags *flags); - virtual DDPhysicalThread* CreatePhysicalThread() { return 0; } + virtual DDPhysicalThread* CreatePhysicalThread() { return nullptr; } virtual void DestroyPhysicalThread(DDPhysicalThread *pt) {} - virtual DDLogicalThread* CreateLogicalThread(u64 ctx) { return 0; } + virtual DDLogicalThread* CreateLogicalThread(u64 ctx) { return nullptr; } virtual void DestroyLogicalThread(DDLogicalThread *lt) {} virtual void MutexInit(DDCallback *cb, DDMutex *m) {} @@ -85,7 +85,7 @@ struct DDetector { virtual void MutexBeforeUnlock(DDCallback *cb, DDMutex *m, bool wlock) {} virtual void MutexDestroy(DDCallback *cb, DDMutex *m) {} - virtual DDReport *GetReport(DDCallback *cb) { return 0; } + virtual DDReport *GetReport(DDCallback *cb) { return nullptr; } }; } // namespace __sanitizer diff --git a/contrib/compiler-rt/lib/sanitizer_common/sanitizer_flag_parser.cc b/contrib/compiler-rt/lib/sanitizer_common/sanitizer_flag_parser.cc index d125002daf4c..67830b2940bb 100644 --- a/contrib/compiler-rt/lib/sanitizer_common/sanitizer_flag_parser.cc +++ b/contrib/compiler-rt/lib/sanitizer_common/sanitizer_flag_parser.cc @@ -127,6 +127,24 @@ void FlagParser::ParseString(const char *s) { pos_ = old_pos_; } +bool FlagParser::ParseFile(const char *path, bool ignore_missing) { + static const uptr kMaxIncludeSize = 1 << 15; + char *data; + uptr data_mapped_size; + error_t err; + uptr len; + if (!ReadFileToBuffer(path, &data, &data_mapped_size, &len, + Max(kMaxIncludeSize, GetPageSizeCached()), &err)) { + if (ignore_missing) + return true; + Printf("Failed to read options from '%s': error %d\n", path, err); + return false; + } + ParseString(data); + UnmapOrDie(data, data_mapped_size); + return true; +} + bool FlagParser::run_handler(const char *name, const char *value) { for (int i = 0; i < n_flags_; ++i) { if (internal_strcmp(name, flags_[i].name) == 0) diff --git a/contrib/compiler-rt/lib/sanitizer_common/sanitizer_flag_parser.h b/contrib/compiler-rt/lib/sanitizer_common/sanitizer_flag_parser.h index 0ac7634cb876..2477aeddba5f 100644 --- a/contrib/compiler-rt/lib/sanitizer_common/sanitizer_flag_parser.h +++ b/contrib/compiler-rt/lib/sanitizer_common/sanitizer_flag_parser.h @@ -93,6 +93,7 @@ class FlagParser { void RegisterHandler(const char *name, FlagHandlerBase *handler, const char *desc); void ParseString(const char *s); + bool ParseFile(const char *path, bool ignore_missing); void PrintFlagDescriptions(); static LowLevelAllocator Alloc; diff --git a/contrib/compiler-rt/lib/sanitizer_common/sanitizer_flags.cc b/contrib/compiler-rt/lib/sanitizer_common/sanitizer_flags.cc index 01098a3d6b87..18b9ea3df9e1 100644 --- a/contrib/compiler-rt/lib/sanitizer_common/sanitizer_flags.cc +++ b/contrib/compiler-rt/lib/sanitizer_common/sanitizer_flags.cc @@ -45,34 +45,49 @@ void CommonFlags::CopyFrom(const CommonFlags &other) { internal_memcpy(this, &other, sizeof(*this)); } +// Copy the string from "s" to "out", replacing "%b" with the binary basename. +static void SubstituteBinaryName(const char *s, char *out, uptr out_size) { + char *out_end = out + out_size; + while (*s && out < out_end - 1) { + if (s[0] != '%' || s[1] != 'b') { *out++ = *s++; continue; } + const char *base = GetProcessName(); + CHECK(base); + while (*base && out < out_end - 1) + *out++ = *base++; + s += 2; // skip "%b" + } + *out = '\0'; +} + class FlagHandlerInclude : public FlagHandlerBase { - static const uptr kMaxIncludeSize = 1 << 15; FlagParser *parser_; + bool ignore_missing_; public: - explicit FlagHandlerInclude(FlagParser *parser) : parser_(parser) {} + explicit FlagHandlerInclude(FlagParser *parser, bool ignore_missing) + : parser_(parser), ignore_missing_(ignore_missing) {} bool Parse(const char *value) final { - char *data; - uptr data_mapped_size; - error_t err; - uptr len = - ReadFileToBuffer(value, &data, &data_mapped_size, - Max(kMaxIncludeSize, GetPageSizeCached()), &err); - if (!len) { - Printf("Failed to read options from '%s': error %d\n", value, err); - return false; + if (internal_strchr(value, '%')) { + char *buf = (char *)MmapOrDie(kMaxPathLength, "FlagHandlerInclude"); + SubstituteBinaryName(value, buf, kMaxPathLength); + bool res = parser_->ParseFile(buf, ignore_missing_); + UnmapOrDie(buf, kMaxPathLength); + return res; } - parser_->ParseString(data); - UnmapOrDie(data, data_mapped_size); - return true; + return parser_->ParseFile(value, ignore_missing_); } }; -void RegisterIncludeFlag(FlagParser *parser, CommonFlags *cf) { - FlagHandlerInclude *fh_include = - new (FlagParser::Alloc) FlagHandlerInclude(parser); // NOLINT +void RegisterIncludeFlags(FlagParser *parser, CommonFlags *cf) { + FlagHandlerInclude *fh_include = new (FlagParser::Alloc) // NOLINT + FlagHandlerInclude(parser, /*ignore_missing*/ false); parser->RegisterHandler("include", fh_include, "read more options from the given file"); + FlagHandlerInclude *fh_include_if_exists = new (FlagParser::Alloc) // NOLINT + FlagHandlerInclude(parser, /*ignore_missing*/ true); + parser->RegisterHandler( + "include_if_exists", fh_include_if_exists, + "read more options from the given file (if it exists)"); } void RegisterCommonFlags(FlagParser *parser, CommonFlags *cf) { @@ -81,7 +96,7 @@ void RegisterCommonFlags(FlagParser *parser, CommonFlags *cf) { #include "sanitizer_flags.inc" #undef COMMON_FLAG - RegisterIncludeFlag(parser, cf); + RegisterIncludeFlags(parser, cf); } } // namespace __sanitizer diff --git a/contrib/compiler-rt/lib/sanitizer_common/sanitizer_flags.h b/contrib/compiler-rt/lib/sanitizer_common/sanitizer_flags.h index fda6d710757e..33c3c4512725 100644 --- a/contrib/compiler-rt/lib/sanitizer_common/sanitizer_flags.h +++ b/contrib/compiler-rt/lib/sanitizer_common/sanitizer_flags.h @@ -49,7 +49,7 @@ inline void OverrideCommonFlags(const CommonFlags &cf) { class FlagParser; void RegisterCommonFlags(FlagParser *parser, CommonFlags *cf = &common_flags_dont_use); -void RegisterIncludeFlag(FlagParser *parser, CommonFlags *cf); +void RegisterIncludeFlags(FlagParser *parser, CommonFlags *cf); } // namespace __sanitizer #endif // SANITIZER_FLAGS_H diff --git a/contrib/compiler-rt/lib/sanitizer_common/sanitizer_flags.inc b/contrib/compiler-rt/lib/sanitizer_common/sanitizer_flags.inc index bbb39c73e2d0..c28f7f4a6034 100644 --- a/contrib/compiler-rt/lib/sanitizer_common/sanitizer_flags.inc +++ b/contrib/compiler-rt/lib/sanitizer_common/sanitizer_flags.inc @@ -10,6 +10,7 @@ // This file describes common flags available in all sanitizers. // //===----------------------------------------------------------------------===// + #ifndef COMMON_FLAG #error "Define COMMON_FLAG prior to including this file!" #endif @@ -24,7 +25,7 @@ COMMON_FLAG( "If set, use the online symbolizer from common sanitizer runtime to turn " "virtual addresses to file/line locations.") COMMON_FLAG( - const char *, external_symbolizer_path, 0, + const char *, external_symbolizer_path, nullptr, "Path to external symbolizer. If empty, the tool will search $PATH for " "the symbolizer.") COMMON_FLAG( @@ -55,6 +56,10 @@ COMMON_FLAG( "Mention name of executable when reporting error and " "append executable name to logs (as in \"log_path.exe_name.pid\").") COMMON_FLAG( + bool, log_to_syslog, SANITIZER_ANDROID || SANITIZER_MAC, + "Write all sanitizer output to syslog in addition to other means of " + "logging.") +COMMON_FLAG( int, verbosity, 0, "Verbosity level (0 - silent, 1 - a bit of output, 2+ - more output).") COMMON_FLAG(bool, detect_leaks, true, "Enable memory leak detection.") @@ -74,6 +79,10 @@ COMMON_FLAG(bool, handle_segv, SANITIZER_NEEDS_SEGV, "If set, registers the tool's custom SIGSEGV/SIGBUS handler.") COMMON_FLAG(bool, handle_abort, false, "If set, registers the tool's custom SIGABRT handler.") +COMMON_FLAG(bool, handle_sigill, false, + "If set, registers the tool's custom SIGILL handler.") +COMMON_FLAG(bool, handle_sigfpe, true, + "If set, registers the tool's custom SIGFPE handler.") COMMON_FLAG(bool, allow_user_segv_handler, false, "If set, allows user to register a SEGV handler even if the tool " "registers one.") @@ -135,6 +144,9 @@ COMMON_FLAG(bool, coverage_direct, SANITIZER_ANDROID, COMMON_FLAG(const char *, coverage_dir, ".", "Target directory for coverage dumps. Defaults to the current " "directory.") +COMMON_FLAG(bool, coverage_pc_buffer, true, + "If set (and if 'coverage' is set too), the pcs would be collected " + "in a buffer.") COMMON_FLAG(bool, full_address_space, false, "Sanitize complete address space; " "by default kernel area on 32-bit platforms will not be sanitized") @@ -170,6 +182,21 @@ COMMON_FLAG(bool, intercept_strspn, true, COMMON_FLAG(bool, intercept_strpbrk, true, "If set, uses custom wrappers for strpbrk function " "to find more errors.") +COMMON_FLAG(bool, intercept_memcmp, true, + "If set, uses custom wrappers for memcmp function " + "to find more errors.") +COMMON_FLAG(bool, strict_memcmp, true, + "If true, assume that memcmp(p1, p2, n) always reads n bytes before " + "comparing p1 and p2.") COMMON_FLAG(bool, decorate_proc_maps, false, "If set, decorate sanitizer " "mappings in /proc/self/maps with " "user-readable names") +COMMON_FLAG(int, exitcode, 1, "Override the program exit status if the tool " + "found an error") +COMMON_FLAG( + bool, abort_on_error, SANITIZER_MAC, + "If set, the tool calls abort() instead of _exit() after printing the " + "error report.") +COMMON_FLAG(bool, suppress_equal_pcs, true, + "Deduplicate multiple reports for single source location in " + "halt_on_error=false mode (asan only).") diff --git a/contrib/compiler-rt/lib/sanitizer_common/sanitizer_interface_internal.h b/contrib/compiler-rt/lib/sanitizer_common/sanitizer_interface_internal.h index 94d9f4e9524a..b11ae30106f7 100644 --- a/contrib/compiler-rt/lib/sanitizer_common/sanitizer_interface_internal.h +++ b/contrib/compiler-rt/lib/sanitizer_common/sanitizer_interface_internal.h @@ -53,6 +53,9 @@ extern "C" { SANITIZER_INTERFACE_ATTRIBUTE int __sanitizer_verify_contiguous_container(const void *beg, const void *mid, const void *end); -} // extern "C" + SANITIZER_INTERFACE_ATTRIBUTE + const void *__sanitizer_contiguous_container_find_bad_address( + const void *beg, const void *mid, const void *end); + } // extern "C" #endif // SANITIZER_INTERFACE_INTERNAL_H diff --git a/contrib/compiler-rt/lib/sanitizer_common/sanitizer_internal_defs.h b/contrib/compiler-rt/lib/sanitizer_common/sanitizer_internal_defs.h index b76c6023aba2..e83eed0830d8 100644 --- a/contrib/compiler-rt/lib/sanitizer_common/sanitizer_internal_defs.h +++ b/contrib/compiler-rt/lib/sanitizer_common/sanitizer_internal_defs.h @@ -117,7 +117,10 @@ using namespace __sanitizer; // NOLINT // Common defs. #define INLINE inline #define INTERFACE_ATTRIBUTE SANITIZER_INTERFACE_ATTRIBUTE -#define WEAK SANITIZER_WEAK_ATTRIBUTE +#define SANITIZER_WEAK_DEFAULT_IMPL \ + extern "C" SANITIZER_INTERFACE_ATTRIBUTE SANITIZER_WEAK_ATTRIBUTE NOINLINE +#define SANITIZER_WEAK_CXX_DEFAULT_IMPL \ + extern "C++" SANITIZER_INTERFACE_ATTRIBUTE SANITIZER_WEAK_ATTRIBUTE NOINLINE // Platform-specific defs. #if defined(_MSC_VER) @@ -129,7 +132,6 @@ using namespace __sanitizer; // NOLINT # define NOINLINE __declspec(noinline) # define NORETURN __declspec(noreturn) # define THREADLOCAL __declspec(thread) -# define NOTHROW # define LIKELY(x) (x) # define UNLIKELY(x) (x) # define PREFETCH(x) /* _mm_prefetch(x, _MM_HINT_NTA) */ @@ -143,7 +145,6 @@ using namespace __sanitizer; // NOLINT # define NOINLINE __attribute__((noinline)) # define NORETURN __attribute__((noreturn)) # define THREADLOCAL __thread -# define NOTHROW throw() # define LIKELY(x) __builtin_expect(!!(x), 1) # define UNLIKELY(x) __builtin_expect(!!(x), 0) # if defined(__i386__) || defined(__x86_64__) @@ -162,6 +163,12 @@ using namespace __sanitizer; // NOLINT # define USED #endif +#if !defined(_MSC_VER) || defined(__clang__) || MSC_PREREQ(1900) +# define NOEXCEPT noexcept +#else +# define NOEXCEPT throw() +#endif + // Unaligned versions of basic types. typedef ALIGNED(1) u16 uu16; typedef ALIGNED(1) u32 uu32; diff --git a/contrib/compiler-rt/lib/sanitizer_common/sanitizer_lfstack.h b/contrib/compiler-rt/lib/sanitizer_common/sanitizer_lfstack.h index 088413908087..879cc80921c7 100644 --- a/contrib/compiler-rt/lib/sanitizer_common/sanitizer_lfstack.h +++ b/contrib/compiler-rt/lib/sanitizer_common/sanitizer_lfstack.h @@ -49,8 +49,8 @@ struct LFStack { u64 cmp = atomic_load(&head_, memory_order_acquire); for (;;) { T *cur = (T*)(uptr)(cmp & kPtrMask); - if (cur == 0) - return 0; + if (!cur) + return nullptr; T *nxt = cur->next; u64 cnt = (cmp & kCounterMask); u64 xch = (u64)(uptr)nxt | cnt; @@ -68,6 +68,6 @@ struct LFStack { atomic_uint64_t head_; }; -} // namespace __sanitizer +} // namespace __sanitizer -#endif // #ifndef SANITIZER_LFSTACK_H +#endif // SANITIZER_LFSTACK_H diff --git a/contrib/compiler-rt/lib/sanitizer_common/sanitizer_libc.cc b/contrib/compiler-rt/lib/sanitizer_common/sanitizer_libc.cc index cb162a4c4984..cf31e689653f 100644 --- a/contrib/compiler-rt/lib/sanitizer_common/sanitizer_libc.cc +++ b/contrib/compiler-rt/lib/sanitizer_common/sanitizer_libc.cc @@ -10,6 +10,7 @@ // This file is shared between AddressSanitizer and ThreadSanitizer // run-time libraries. See sanitizer_libc.h for details. //===----------------------------------------------------------------------===// + #include "sanitizer_allocator_internal.h" #include "sanitizer_common.h" #include "sanitizer_libc.h" @@ -17,7 +18,7 @@ namespace __sanitizer { s64 internal_atoll(const char *nptr) { - return internal_simple_strtoll(nptr, (char**)0, 10); + return internal_simple_strtoll(nptr, nullptr, 10); } void *internal_memchr(const void *s, int c, uptr n) { @@ -25,7 +26,7 @@ void *internal_memchr(const void *s, int c, uptr n) { for (uptr i = 0; i < n; ++i, ++t) if (*t == c) return reinterpret_cast<void *>(const_cast<char *>(t)); - return 0; + return nullptr; } void *internal_memrchr(const void *s, int c, uptr n) { @@ -77,7 +78,8 @@ void internal_bzero_aligned16(void *s, uptr n) { CHECK_EQ((reinterpret_cast<uptr>(s) | n) & 15, 0); for (S16 *p = reinterpret_cast<S16*>(s), *end = p + n / 16; p < end; p++) { p->a = p->b = 0; - SanitizerBreakOptimization(0); // Make sure this does not become memset. + // Make sure this does not become memset. + SanitizerBreakOptimization(nullptr); } } @@ -96,7 +98,7 @@ void *internal_memset(void* s, int c, uptr n) { uptr internal_strcspn(const char *s, const char *reject) { uptr i; for (i = 0; s[i]; i++) { - if (internal_strchr(reject, s[i]) != 0) + if (internal_strchr(reject, s[i])) return i; } return i; @@ -147,7 +149,7 @@ char* internal_strchr(const char *s, int c) { if (*s == (char)c) return const_cast<char *>(s); if (*s == 0) - return 0; + return nullptr; s++; } } @@ -160,7 +162,7 @@ char *internal_strchrnul(const char *s, int c) { } char *internal_strrchr(const char *s, int c) { - const char *res = 0; + const char *res = nullptr; for (uptr i = 0; s[i]; i++) { if (s[i] == c) res = s + i; } @@ -173,6 +175,19 @@ uptr internal_strlen(const char *s) { return i; } +uptr internal_strlcat(char *dst, const char *src, uptr maxlen) { + const uptr srclen = internal_strlen(src); + const uptr dstlen = internal_strnlen(dst, maxlen); + if (dstlen == maxlen) return maxlen + srclen; + if (srclen < maxlen - dstlen) { + internal_memmove(dst + dstlen, src, srclen + 1); + } else { + internal_memmove(dst + dstlen, src, maxlen - dstlen - 1); + dst[maxlen - 1] = '\0'; + } + return dstlen + srclen; +} + char *internal_strncat(char *dst, const char *src, uptr n) { uptr len = internal_strlen(dst); uptr i; @@ -182,6 +197,17 @@ char *internal_strncat(char *dst, const char *src, uptr n) { return dst; } +uptr internal_strlcpy(char *dst, const char *src, uptr maxlen) { + const uptr srclen = internal_strlen(src); + if (srclen < maxlen) { + internal_memmove(dst, src, srclen + 1); + } else if (maxlen != 0) { + internal_memmove(dst, src, maxlen - 1); + dst[maxlen - 1] = '\0'; + } + return srclen; +} + char *internal_strncpy(char *dst, const char *src, uptr n) { uptr i; for (i = 0; i < n && src[i]; i++) @@ -200,12 +226,12 @@ char *internal_strstr(const char *haystack, const char *needle) { // This is O(N^2), but we are not using it in hot places. uptr len1 = internal_strlen(haystack); uptr len2 = internal_strlen(needle); - if (len1 < len2) return 0; + if (len1 < len2) return nullptr; for (uptr pos = 0; pos <= len1 - len2; pos++) { if (internal_memcmp(haystack + pos, needle, len2) == 0) return const_cast<char *>(haystack) + pos; } - return 0; + return nullptr; } s64 internal_simple_strtoll(const char *nptr, char **endptr, int base) { @@ -229,7 +255,7 @@ s64 internal_simple_strtoll(const char *nptr, char **endptr, int base) { have_digits = true; nptr++; } - if (endptr != 0) { + if (endptr) { *endptr = (have_digits) ? const_cast<char *>(nptr) : old_nptr; } if (sgn > 0) { @@ -258,4 +284,4 @@ bool mem_is_zero(const char *beg, uptr size) { return all == 0; } -} // namespace __sanitizer +} // namespace __sanitizer diff --git a/contrib/compiler-rt/lib/sanitizer_common/sanitizer_libc.h b/contrib/compiler-rt/lib/sanitizer_common/sanitizer_libc.h index ae4e938f4af9..df28677c6700 100644 --- a/contrib/compiler-rt/lib/sanitizer_common/sanitizer_libc.h +++ b/contrib/compiler-rt/lib/sanitizer_common/sanitizer_libc.h @@ -13,6 +13,7 @@ // functions are intercepted. Instead, we implement a tiny subset of libc here. // FIXME: Some of functions declared in this file are in fact POSIX, not libc. //===----------------------------------------------------------------------===// + #ifndef SANITIZER_LIBC_H #define SANITIZER_LIBC_H @@ -42,8 +43,10 @@ uptr internal_strcspn(const char *s, const char *reject); char *internal_strdup(const char *s); char *internal_strndup(const char *s, uptr n); uptr internal_strlen(const char *s); +uptr internal_strlcat(char *dst, const char *src, uptr maxlen); char *internal_strncat(char *dst, const char *src, uptr n); int internal_strncmp(const char *s1, const char *s2, uptr n); +uptr internal_strlcpy(char *dst, const char *src, uptr maxlen); char *internal_strncpy(char *dst, const char *src, uptr n); uptr internal_strnlen(const char *s, uptr maxlen); char *internal_strrchr(const char *s, int c); @@ -75,8 +78,8 @@ uptr internal_getppid(); uptr internal_sched_yield(); // Error handling -bool internal_iserror(uptr retval, int *rverrno = 0); +bool internal_iserror(uptr retval, int *rverrno = nullptr); -} // namespace __sanitizer +} // namespace __sanitizer -#endif // SANITIZER_LIBC_H +#endif // SANITIZER_LIBC_H diff --git a/contrib/compiler-rt/lib/sanitizer_common/sanitizer_libignore.cc b/contrib/compiler-rt/lib/sanitizer_common/sanitizer_libignore.cc index 8c4aeffda45e..545393966b38 100644 --- a/contrib/compiler-rt/lib/sanitizer_common/sanitizer_libignore.cc +++ b/contrib/compiler-rt/lib/sanitizer_common/sanitizer_libignore.cc @@ -8,7 +8,8 @@ //===----------------------------------------------------------------------===// #include "sanitizer_platform.h" -#if SANITIZER_FREEBSD || SANITIZER_LINUX + +#if SANITIZER_FREEBSD || SANITIZER_LINUX || SANITIZER_MAC #include "sanitizer_libignore.h" #include "sanitizer_flags.h" @@ -38,11 +39,11 @@ void LibIgnore::OnLibraryLoaded(const char *name) { BlockingMutexLock lock(&mutex_); // Try to match suppressions with symlink target. InternalScopedString buf(kMaxPathLength); - if (name != 0 && internal_readlink(name, buf.data(), buf.size() - 1) > 0 && + if (name && internal_readlink(name, buf.data(), buf.size() - 1) > 0 && buf[0]) { for (uptr i = 0; i < count_; i++) { Lib *lib = &libs_[i]; - if (!lib->loaded && lib->real_name == 0 && + if (!lib->loaded && (!lib->real_name) && TemplateMatch(lib->templ, name)) lib->real_name = internal_strdup(buf.data()); } @@ -60,7 +61,7 @@ void LibIgnore::OnLibraryLoaded(const char *name) { if ((prot & MemoryMappingLayout::kProtectionExecute) == 0) continue; if (TemplateMatch(lib->templ, module.data()) || - (lib->real_name != 0 && + (lib->real_name && internal_strcmp(lib->real_name, module.data()) == 0)) { if (loaded) { Report("%s: called_from_lib suppression '%s' is matched against" @@ -93,9 +94,9 @@ void LibIgnore::OnLibraryLoaded(const char *name) { } void LibIgnore::OnLibraryUnloaded() { - OnLibraryLoaded(0); + OnLibraryLoaded(nullptr); } -} // namespace __sanitizer +} // namespace __sanitizer -#endif // #if SANITIZER_FREEBSD || SANITIZER_LINUX +#endif // #if SANITIZER_FREEBSD || SANITIZER_LINUX diff --git a/contrib/compiler-rt/lib/sanitizer_common/sanitizer_linux.cc b/contrib/compiler-rt/lib/sanitizer_common/sanitizer_linux.cc index 98e5d122a0f9..cba38c8c3057 100644 --- a/contrib/compiler-rt/lib/sanitizer_common/sanitizer_linux.cc +++ b/contrib/compiler-rt/lib/sanitizer_common/sanitizer_linux.cc @@ -13,9 +13,9 @@ //===----------------------------------------------------------------------===// #include "sanitizer_platform.h" + #if SANITIZER_FREEBSD || SANITIZER_LINUX -#include "sanitizer_allocator_internal.h" #include "sanitizer_common.h" #include "sanitizer_flags.h" #include "sanitizer_internal_defs.h" @@ -74,11 +74,6 @@ extern char **environ; // provided by crt1 #include <sys/signal.h> #endif -#if SANITIZER_ANDROID -#include <android/log.h> -#include <sys/system_properties.h> -#endif - #if SANITIZER_LINUX // <linux/time.h> struct kernel_timeval { @@ -94,7 +89,8 @@ const int FUTEX_WAKE = 1; // Are we using 32-bit or 64-bit Linux syscalls? // x32 (which defines __x86_64__) has SANITIZER_WORDSIZE == 32 // but it still needs to use 64-bit syscalls. -#if SANITIZER_LINUX && (defined(__x86_64__) || SANITIZER_WORDSIZE == 64) +#if SANITIZER_LINUX && (defined(__x86_64__) || defined(__powerpc64__) || \ + SANITIZER_WORDSIZE == 64) # define SANITIZER_LINUX_USES_64BIT_SYSCALLS 1 #else # define SANITIZER_LINUX_USES_64BIT_SYSCALLS 0 @@ -104,6 +100,8 @@ namespace __sanitizer { #if SANITIZER_LINUX && defined(__x86_64__) #include "sanitizer_syscall_linux_x86_64.inc" +#elif SANITIZER_LINUX && defined(__aarch64__) +#include "sanitizer_syscall_linux_aarch64.inc" #else #include "sanitizer_syscall_generic.inc" #endif @@ -375,23 +373,23 @@ const char *GetEnv(const char *name) { if (!inited) { inited = true; uptr environ_size; - len = ReadFileToBuffer("/proc/self/environ", - &environ, &environ_size, 1 << 26); + if (!ReadFileToBuffer("/proc/self/environ", &environ, &environ_size, &len)) + environ = nullptr; } - if (!environ || len == 0) return 0; + if (!environ || len == 0) return nullptr; uptr namelen = internal_strlen(name); const char *p = environ; while (*p != '\0') { // will happen at the \0\0 that terminates the buffer // proc file has the format NAME=value\0NAME=value\0NAME=value\0... const char* endp = (char*)internal_memchr(p, '\0', len - (p - environ)); - if (endp == 0) // this entry isn't NUL terminated - return 0; + if (!endp) // this entry isn't NUL terminated + return nullptr; else if (!internal_memcmp(p, name, namelen) && p[namelen] == '=') // Match. return p + namelen + 1; // point after = p = endp + 1; } - return 0; // Not found. + return nullptr; // Not found. #else #error "Unsupported platform" #endif @@ -405,9 +403,13 @@ extern "C" { static void ReadNullSepFileToArray(const char *path, char ***arr, int arr_size) { char *buff; - uptr buff_size = 0; + uptr buff_size; + uptr buff_len; *arr = (char **)MmapOrDie(arr_size * sizeof(char *), "NullSepFileArray"); - ReadFileToBuffer(path, &buff, &buff_size, 1024 * 1024); + if (!ReadFileToBuffer(path, &buff, &buff_size, &buff_len, 1024 * 1024)) { + (*arr)[0] = nullptr; + return; + } (*arr)[0] = buff; int count, i; for (count = 1, i = 1; ; i++) { @@ -418,7 +420,7 @@ static void ReadNullSepFileToArray(const char *path, char ***arr, count++; } } - (*arr)[count] = 0; + (*arr)[count] = nullptr; } #endif @@ -496,7 +498,7 @@ void BlockingMutex::CheckLocked() { // Note that getdents64 uses a different structure format. We only provide the // 32-bit syscall here. struct linux_dirent { -#if SANITIZER_X32 +#if SANITIZER_X32 || defined(__aarch64__) u64 d_ino; u64 d_off; #else @@ -504,6 +506,9 @@ struct linux_dirent { unsigned long d_off; #endif unsigned short d_reclen; +#ifdef __aarch64__ + unsigned char d_type; +#endif char d_name[256]; }; @@ -585,8 +590,8 @@ int internal_sigaction_norestorer(int signum, const void *act, void *oldact) { } uptr result = internal_syscall(SYSCALL(rt_sigaction), (uptr)signum, - (uptr)(u_act ? &k_act : NULL), - (uptr)(u_oldact ? &k_oldact : NULL), + (uptr)(u_act ? &k_act : nullptr), + (uptr)(u_oldact ? &k_oldact : nullptr), (uptr)sizeof(__sanitizer_kernel_sigset_t)); if ((result == 0) && u_oldact) { @@ -732,6 +737,21 @@ uptr ReadBinaryName(/*out*/char *buf, uptr buf_len) { return module_name_len; } +uptr ReadLongProcessName(/*out*/ char *buf, uptr buf_len) { +#if SANITIZER_LINUX + char *tmpbuf; + uptr tmpsize; + uptr tmplen; + if (ReadFileToBuffer("/proc/self/cmdline", &tmpbuf, &tmpsize, &tmplen, + 1024 * 1024)) { + internal_strncpy(buf, tmpbuf, buf_len); + UnmapOrDie(tmpbuf, tmpsize); + return internal_strlen(buf); + } +#endif + return ReadBinaryName(buf, buf_len); +} + // Match full names of the form /path/to/base_name{-,.}* bool LibraryNameIs(const char *full_name, const char *base_name) { const char *name = full_name; @@ -913,41 +933,142 @@ uptr internal_clone(int (*fn)(void *), void *child_stack, int flags, void *arg, : "memory", "$29" ); return res; } -#endif // defined(__x86_64__) && SANITIZER_LINUX +#elif defined(__aarch64__) +uptr internal_clone(int (*fn)(void *), void *child_stack, int flags, void *arg, + int *parent_tidptr, void *newtls, int *child_tidptr) { + long long res; + if (!fn || !child_stack) + return -EINVAL; + CHECK_EQ(0, (uptr)child_stack % 16); + child_stack = (char *)child_stack - 2 * sizeof(unsigned long long); + ((unsigned long long *)child_stack)[0] = (uptr)fn; + ((unsigned long long *)child_stack)[1] = (uptr)arg; -#if SANITIZER_ANDROID -static atomic_uint8_t android_log_initialized; + register int (*__fn)(void *) __asm__("x0") = fn; + register void *__stack __asm__("x1") = child_stack; + register int __flags __asm__("x2") = flags; + register void *__arg __asm__("x3") = arg; + register int *__ptid __asm__("x4") = parent_tidptr; + register void *__tls __asm__("x5") = newtls; + register int *__ctid __asm__("x6") = child_tidptr; -void AndroidLogInit() { - atomic_store(&android_log_initialized, 1, memory_order_release); -} -// This thing is not, strictly speaking, async signal safe, but it does not seem -// to cause any issues. Alternative is writing to log devices directly, but -// their location and message format might change in the future, so we'd really -// like to avoid that. -void AndroidLogWrite(const char *buffer) { - if (!atomic_load(&android_log_initialized, memory_order_acquire)) - return; + __asm__ __volatile__( + "mov x0,x2\n" /* flags */ + "mov x2,x4\n" /* ptid */ + "mov x3,x5\n" /* tls */ + "mov x4,x6\n" /* ctid */ + "mov x8,%9\n" /* clone */ - char *copy = internal_strdup(buffer); - char *p = copy; - char *q; - // __android_log_write has an implicit message length limit. - // Print one line at a time. - do { - q = internal_strchr(p, '\n'); - if (q) *q = '\0'; - __android_log_write(ANDROID_LOG_INFO, NULL, p); - if (q) p = q + 1; - } while (q); - InternalFree(copy); + "svc 0x0\n" + + /* if (%r0 != 0) + * return %r0; + */ + "cmp x0, #0\n" + "bne 1f\n" + + /* In the child, now. Call "fn(arg)". */ + "ldp x1, x0, [sp], #16\n" + "blr x1\n" + + /* Call _exit(%r0). */ + "mov x8, %10\n" + "svc 0x0\n" + "1:\n" + + : "=r" (res) + : "i"(-EINVAL), + "r"(__fn), "r"(__stack), "r"(__flags), "r"(__arg), + "r"(__ptid), "r"(__tls), "r"(__ctid), + "i"(__NR_clone), "i"(__NR_exit) + : "x30", "memory"); + return res; } +#elif defined(__powerpc64__) +uptr internal_clone(int (*fn)(void *), void *child_stack, int flags, void *arg, + int *parent_tidptr, void *newtls, int *child_tidptr) { + long long res; +/* Stack frame offsets. */ +#if _CALL_ELF != 2 +#define FRAME_MIN_SIZE 112 +#define FRAME_TOC_SAVE 40 +#else +#define FRAME_MIN_SIZE 32 +#define FRAME_TOC_SAVE 24 +#endif + if (!fn || !child_stack) + return -EINVAL; + CHECK_EQ(0, (uptr)child_stack % 16); + child_stack = (char *)child_stack - 2 * sizeof(unsigned long long); + ((unsigned long long *)child_stack)[0] = (uptr)fn; + ((unsigned long long *)child_stack)[1] = (uptr)arg; -void GetExtraActivationFlags(char *buf, uptr size) { - CHECK(size > PROP_VALUE_MAX); - __system_property_get("asan.options", buf); + register int (*__fn)(void *) __asm__("r3") = fn; + register void *__cstack __asm__("r4") = child_stack; + register int __flags __asm__("r5") = flags; + register void * __arg __asm__("r6") = arg; + register int * __ptidptr __asm__("r7") = parent_tidptr; + register void * __newtls __asm__("r8") = newtls; + register int * __ctidptr __asm__("r9") = child_tidptr; + + __asm__ __volatile__( + /* fn, arg, child_stack are saved acrVoss the syscall */ + "mr 28, %5\n\t" + "mr 29, %6\n\t" + "mr 27, %8\n\t" + + /* syscall + r3 == flags + r4 == child_stack + r5 == parent_tidptr + r6 == newtls + r7 == child_tidptr */ + "mr 3, %7\n\t" + "mr 5, %9\n\t" + "mr 6, %10\n\t" + "mr 7, %11\n\t" + "li 0, %3\n\t" + "sc\n\t" + + /* Test if syscall was successful */ + "cmpdi cr1, 3, 0\n\t" + "crandc cr1*4+eq, cr1*4+eq, cr0*4+so\n\t" + "bne- cr1, 1f\n\t" + + /* Do the function call */ + "std 2, %13(1)\n\t" +#if _CALL_ELF != 2 + "ld 0, 0(28)\n\t" + "ld 2, 8(28)\n\t" + "mtctr 0\n\t" +#else + "mr 12, 28\n\t" + "mtctr 12\n\t" +#endif + "mr 3, 27\n\t" + "bctrl\n\t" + "ld 2, %13(1)\n\t" + + /* Call _exit(r3) */ + "li 0, %4\n\t" + "sc\n\t" + + /* Return to parent */ + "1:\n\t" + "mr %0, 3\n\t" + : "=r" (res) + : "0" (-1), "i" (EINVAL), + "i" (__NR_clone), "i" (__NR_exit), + "r" (__fn), "r" (__cstack), "r" (__flags), + "r" (__arg), "r" (__ptidptr), "r" (__newtls), + "r" (__ctidptr), "i" (FRAME_MIN_SIZE), "i" (FRAME_TOC_SAVE) + : "cr0", "cr1", "memory", "ctr", + "r0", "r29", "r27", "r28"); + return res; } +#endif // defined(__x86_64__) && SANITIZER_LINUX +#if SANITIZER_ANDROID #if __ANDROID_API__ < 21 extern "C" __attribute__((weak)) int dl_iterate_phdr( int (*)(struct dl_phdr_info *, size_t, void *), void *); @@ -993,6 +1114,10 @@ AndroidApiLevel AndroidGetApiLevel() { bool IsDeadlySignal(int signum) { if (common_flags()->handle_abort && signum == SIGABRT) return true; + if (common_flags()->handle_sigill && signum == SIGILL) + return true; + if (common_flags()->handle_sigfpe && signum == SIGFPE) + return true; return (signum == SIGSEGV || signum == SIGBUS) && common_flags()->handle_segv; } @@ -1008,13 +1133,13 @@ void *internal_start_thread(void(*func)(void *arg), void *arg) { #endif internal_sigprocmask(SIG_SETMASK, &set, &old); void *th; - real_pthread_create(&th, 0, (void*(*)(void *arg))func, arg); - internal_sigprocmask(SIG_SETMASK, &old, 0); + real_pthread_create(&th, nullptr, (void*(*)(void *arg))func, arg); + internal_sigprocmask(SIG_SETMASK, &old, nullptr); return th; } void internal_join_thread(void *th) { - real_pthread_join(th, 0); + real_pthread_join(th, nullptr); } #else void *internal_start_thread(void (*func)(void *), void *arg) { return 0; } @@ -1094,6 +1219,14 @@ void GetPcSpBp(void *context, uptr *pc, uptr *sp, uptr *bp) { #endif } -} // namespace __sanitizer +void DisableReexec() { + // No need to re-exec on Linux. +} + +void MaybeReexec() { + // No need to re-exec on Linux. +} + +} // namespace __sanitizer -#endif // SANITIZER_FREEBSD || SANITIZER_LINUX +#endif // SANITIZER_FREEBSD || SANITIZER_LINUX diff --git a/contrib/compiler-rt/lib/sanitizer_common/sanitizer_linux.h b/contrib/compiler-rt/lib/sanitizer_common/sanitizer_linux.h index e9fc4ad448d8..77bfbd156815 100644 --- a/contrib/compiler-rt/lib/sanitizer_common/sanitizer_linux.h +++ b/contrib/compiler-rt/lib/sanitizer_common/sanitizer_linux.h @@ -44,7 +44,8 @@ uptr internal_prctl(int option, uptr arg2, uptr arg3, uptr arg4, uptr arg5); // internal_sigaction instead. int internal_sigaction_norestorer(int signum, const void *act, void *oldact); void internal_sigdelset(__sanitizer_sigset_t *set, int signum); -#if defined(__x86_64__) || defined(__mips__) +#if defined(__x86_64__) || defined(__mips__) || defined(__aarch64__) \ + || defined(__powerpc64__) uptr internal_clone(int (*fn)(void *), void *child_stack, int flags, void *arg, int *parent_tidptr, void *newtls, int *child_tidptr); #endif diff --git a/contrib/compiler-rt/lib/sanitizer_common/sanitizer_linux_libcdep.cc b/contrib/compiler-rt/lib/sanitizer_common/sanitizer_linux_libcdep.cc index 39eb1d216b2e..8cf2c73b1d53 100644 --- a/contrib/compiler-rt/lib/sanitizer_common/sanitizer_linux_libcdep.cc +++ b/contrib/compiler-rt/lib/sanitizer_common/sanitizer_linux_libcdep.cc @@ -13,8 +13,10 @@ //===----------------------------------------------------------------------===// #include "sanitizer_platform.h" + #if SANITIZER_FREEBSD || SANITIZER_LINUX +#include "sanitizer_allocator_internal.h" #include "sanitizer_atomic.h" #include "sanitizer_common.h" #include "sanitizer_flags.h" @@ -47,6 +49,12 @@ #include <android/api-level.h> #endif +#if SANITIZER_ANDROID && __ANDROID_API__ < 21 +#include <android/log.h> +#else +#include <syslog.h> +#endif + #if !SANITIZER_ANDROID #include <elf.h> #include <unistd.h> @@ -54,20 +62,6 @@ namespace __sanitizer { -// This function is defined elsewhere if we intercepted pthread_attr_getstack. -extern "C" { -SANITIZER_WEAK_ATTRIBUTE int -real_pthread_attr_getstack(void *attr, void **addr, size_t *size); -} // extern "C" - -static int my_pthread_attr_getstack(void *attr, void **addr, size_t *size) { -#if !SANITIZER_GO - if (&real_pthread_attr_getstack) - return real_pthread_attr_getstack((pthread_attr_t *)attr, addr, size); -#endif - return pthread_attr_getstack((pthread_attr_t *)attr, addr, size); -} - SANITIZER_WEAK_ATTRIBUTE int real_sigaction(int signum, const void *act, void *oldact); @@ -93,7 +87,8 @@ void GetThreadStackTopAndBottom(bool at_initialization, uptr *stack_top, MemoryMappingLayout proc_maps(/*cache_enabled*/true); uptr start, end, offset; uptr prev_end = 0; - while (proc_maps.Next(&start, &end, &offset, 0, 0, /* protection */0)) { + while (proc_maps.Next(&start, &end, &offset, nullptr, 0, + /* protection */nullptr)) { if ((uptr)&rl < end) break; prev_end = end; @@ -118,8 +113,8 @@ void GetThreadStackTopAndBottom(bool at_initialization, uptr *stack_top, pthread_attr_init(&attr); CHECK_EQ(pthread_getattr_np(pthread_self(), &attr), 0); uptr stacksize = 0; - void *stackaddr = 0; - my_pthread_attr_getstack(&attr, &stackaddr, (size_t*)&stacksize); + void *stackaddr = nullptr; + my_pthread_attr_getstack(&attr, &stackaddr, &stacksize); pthread_attr_destroy(&attr); CHECK_LE(stacksize, kMaxThreadStackSize); // Sanity check. @@ -130,7 +125,7 @@ void GetThreadStackTopAndBottom(bool at_initialization, uptr *stack_top, #if !SANITIZER_GO bool SetEnv(const char *name, const char *value) { void *f = dlsym(RTLD_NEXT, "setenv"); - if (f == 0) + if (!f) return false; typedef int(*setenv_ft)(const char *name, const char *value, int overwrite); setenv_ft setenv_f; @@ -161,7 +156,7 @@ bool SanitizerGetThreadName(char *name, int max_len) { #endif } -#if !SANITIZER_FREEBSD +#if !SANITIZER_FREEBSD && !SANITIZER_ANDROID && !SANITIZER_GO static uptr g_tls_size; #endif @@ -171,11 +166,15 @@ static uptr g_tls_size; # define DL_INTERNAL_FUNCTION #endif -#if defined(__mips__) +#if defined(__mips__) || defined(__powerpc64__) // TlsPreTcbSize includes size of struct pthread_descr and size of tcb // head structure. It lies before the static tls blocks. static uptr TlsPreTcbSize() { - const uptr kTcbHead = 16; +# if defined(__mips__) + const uptr kTcbHead = 16; // sizeof (tcbhead_t) +# elif defined(__powerpc64__) + const uptr kTcbHead = 88; // sizeof (tcbhead_t) +# endif const uptr kTlsAlign = 16; const uptr kTlsPreTcbSize = (ThreadDescriptorSize() + kTcbHead + kTlsAlign - 1) & ~(kTlsAlign - 1); @@ -187,6 +186,8 @@ static uptr TlsPreTcbSize() { void InitTlsSize() { #if !SANITIZER_FREEBSD && !SANITIZER_ANDROID && !SANITIZER_GO +// all current supported platforms have 16 bytes stack alignment + const size_t kStackAlign = 16; typedef void (*get_tls_func)(size_t*, size_t*) DL_INTERNAL_FUNCTION; get_tls_func get_tls; void *get_tls_static_info_ptr = dlsym(RTLD_NEXT, "_dl_get_tls_static_info"); @@ -197,13 +198,16 @@ void InitTlsSize() { size_t tls_size = 0; size_t tls_align = 0; get_tls(&tls_size, &tls_align); - g_tls_size = tls_size; -#endif // !SANITIZER_FREEBSD && !SANITIZER_ANDROID + if (tls_align < kStackAlign) + tls_align = kStackAlign; + g_tls_size = RoundUpTo(tls_size, tls_align); +#endif // !SANITIZER_FREEBSD && !SANITIZER_ANDROID && !SANITIZER_GO } -#if (defined(__x86_64__) || defined(__i386__) || defined(__mips__)) \ - && SANITIZER_LINUX -// sizeof(struct thread) from glibc. +#if (defined(__x86_64__) || defined(__i386__) || defined(__mips__) \ + || defined(__aarch64__) || defined(__powerpc64__)) \ + && SANITIZER_LINUX && !SANITIZER_ANDROID +// sizeof(struct pthread) from glibc. static atomic_uintptr_t kThreadDescriptorSize; uptr ThreadDescriptorSize() { @@ -218,7 +222,7 @@ uptr ThreadDescriptorSize() { char *end; int minor = internal_simple_strtoll(buf + 8, &end, 10); if (end != buf + 8 && (*end == '\0' || *end == '.')) { - /* sizeof(struct thread) values from various glibc versions. */ + /* sizeof(struct pthread) values from various glibc versions. */ if (SANITIZER_X32) val = 1728; // Assume only one particular version for x32. else if (minor <= 3) @@ -249,6 +253,15 @@ uptr ThreadDescriptorSize() { if (val) atomic_store(&kThreadDescriptorSize, val, memory_order_relaxed); return val; +#elif defined(__aarch64__) + // The sizeof (struct pthread) is the same from GLIBC 2.17 to 2.22. + val = 1776; + atomic_store(&kThreadDescriptorSize, val, memory_order_relaxed); + return val; +#elif defined(__powerpc64__) + val = 1776; // from glibc.ppc64le 2.20-8.fc21 + atomic_store(&kThreadDescriptorSize, val, memory_order_relaxed); + return val; #endif return 0; } @@ -278,6 +291,17 @@ uptr ThreadSelf() { rdhwr %0,$29;\ .set pop" : "=r" (thread_pointer)); descr_addr = thread_pointer - kTlsTcbOffset - TlsPreTcbSize(); +# elif defined(__aarch64__) + descr_addr = reinterpret_cast<uptr>(__builtin_thread_pointer()); +# elif defined(__powerpc64__) + // PPC64LE uses TLS variant I. The thread pointer (in GPR 13) + // points to the end of the TCB + 0x7000. The pthread_descr structure is + // immediately in front of the TCB. TlsPreTcbSize() includes the size of the + // TCB and the size of pthread_descr. + const uptr kTlsTcbOffset = 0x7000; + uptr thread_pointer; + asm("addi %0,13,%1" : "=r"(thread_pointer) : "I"(-kTlsTcbOffset)); + descr_addr = thread_pointer - TlsPreTcbSize(); # else # error "unsupported CPU arch" # endif @@ -307,13 +331,13 @@ uptr ThreadSelf() { #if !SANITIZER_GO static void GetTls(uptr *addr, uptr *size) { -#if SANITIZER_LINUX +#if SANITIZER_LINUX && !SANITIZER_ANDROID # if defined(__x86_64__) || defined(__i386__) *addr = ThreadSelf(); *size = GetTlsSize(); *addr -= *size; *addr += ThreadDescriptorSize(); -# elif defined(__mips__) +# elif defined(__mips__) || defined(__aarch64__) || defined(__powerpc64__) *addr = ThreadSelf(); *size = GetTlsSize(); # else @@ -333,6 +357,9 @@ static void GetTls(uptr *addr, uptr *size) { *addr = (uptr) dtv[2]; *size = (*addr == 0) ? 0 : ((uptr) segbase[0] - (uptr) dtv[2]); } +#elif SANITIZER_ANDROID + *addr = 0; + *size = 0; #else # error "Unknown OS" #endif @@ -341,7 +368,7 @@ static void GetTls(uptr *addr, uptr *size) { #if !SANITIZER_GO uptr GetTlsSize() { -#if SANITIZER_FREEBSD +#if SANITIZER_FREEBSD || SANITIZER_ANDROID uptr addr, size; GetTls(&addr, &size); return size; @@ -376,31 +403,6 @@ void GetThreadStackAndTls(bool main, uptr *stk_addr, uptr *stk_size, #endif } -void AdjustStackSize(void *attr_) { - pthread_attr_t *attr = (pthread_attr_t *)attr_; - uptr stackaddr = 0; - size_t stacksize = 0; - my_pthread_attr_getstack(attr, (void**)&stackaddr, &stacksize); - // GLibC will return (0 - stacksize) as the stack address in the case when - // stacksize is set, but stackaddr is not. - bool stack_set = (stackaddr != 0) && (stackaddr + stacksize != 0); - // We place a lot of tool data into TLS, account for that. - const uptr minstacksize = GetTlsSize() + 128*1024; - if (stacksize < minstacksize) { - if (!stack_set) { - if (stacksize != 0) { - VPrintf(1, "Sanitizer: increasing stacksize %zu->%zu\n", stacksize, - minstacksize); - pthread_attr_setstacksize(attr, minstacksize); - } - } else { - Printf("Sanitizer: pre-allocated stack size is insufficient: " - "%zu < %zu\n", stacksize, minstacksize); - Printf("Sanitizer: pthread_create is likely to fail.\n"); - } - } -} - # if !SANITIZER_FREEBSD typedef ElfW(Phdr) Elf_Phdr; # elif SANITIZER_WORDSIZE == 32 && __FreeBSD_version <= 902001 // v9.2 @@ -455,7 +457,7 @@ extern "C" __attribute__((weak)) int dl_iterate_phdr( uptr GetListOfModules(LoadedModule *modules, uptr max_modules, string_predicate_t filter) { -#if SANITIZER_ANDROID && __ANDROID_API__ < 21 +#if SANITIZER_ANDROID && __ANDROID_API__ <= 22 u32 api_level = AndroidGetApiLevel(); // Fall back to /proc/maps if dl_iterate_phdr is unavailable or broken. // The runtime check allows the same library to work with @@ -510,6 +512,42 @@ uptr GetRSS() { return rss * GetPageSizeCached(); } -} // namespace __sanitizer +// 64-bit Android targets don't provide the deprecated __android_log_write. +// Starting with the L release, syslog() works and is preferable to +// __android_log_write. +#if SANITIZER_LINUX + +#if SANITIZER_ANDROID +static atomic_uint8_t android_log_initialized; + +void AndroidLogInit() { + atomic_store(&android_log_initialized, 1, memory_order_release); +} + +static bool ShouldLogAfterPrintf() { + return atomic_load(&android_log_initialized, memory_order_acquire); +} +#else +void AndroidLogInit() {} + +static bool ShouldLogAfterPrintf() { return true; } +#endif // SANITIZER_ANDROID + +void WriteOneLineToSyslog(const char *s) { +#if SANITIZER_ANDROID &&__ANDROID_API__ < 21 + __android_log_write(ANDROID_LOG_INFO, NULL, s); +#else + syslog(LOG_INFO, "%s", s); +#endif +} + +void LogMessageOnPrintf(const char *str) { + if (common_flags()->log_to_syslog && ShouldLogAfterPrintf()) + WriteToSyslog(str); +} + +#endif // SANITIZER_LINUX + +} // namespace __sanitizer -#endif // SANITIZER_FREEBSD || SANITIZER_LINUX +#endif // SANITIZER_FREEBSD || SANITIZER_LINUX diff --git a/contrib/compiler-rt/lib/sanitizer_common/sanitizer_list.h b/contrib/compiler-rt/lib/sanitizer_common/sanitizer_list.h index 6dd9c8f7bca1..adbb97dc70dc 100644 --- a/contrib/compiler-rt/lib/sanitizer_common/sanitizer_list.h +++ b/contrib/compiler-rt/lib/sanitizer_common/sanitizer_list.h @@ -11,6 +11,7 @@ // ThreadSanitizer, etc run-times. // //===----------------------------------------------------------------------===// + #ifndef SANITIZER_LIST_H #define SANITIZER_LIST_H @@ -29,7 +30,7 @@ struct IntrusiveList { friend class Iterator; void clear() { - first_ = last_ = 0; + first_ = last_ = nullptr; size_ = 0; } @@ -38,11 +39,11 @@ struct IntrusiveList { void push_back(Item *x) { if (empty()) { - x->next = 0; + x->next = nullptr; first_ = last_ = x; size_ = 1; } else { - x->next = 0; + x->next = nullptr; last_->next = x; last_ = x; size_++; @@ -51,7 +52,7 @@ struct IntrusiveList { void push_front(Item *x) { if (empty()) { - x->next = 0; + x->next = nullptr; first_ = last_ = x; size_ = 1; } else { @@ -64,8 +65,8 @@ struct IntrusiveList { void pop_front() { CHECK(!empty()); first_ = first_->next; - if (first_ == 0) - last_ = 0; + if (!first_) + last_ = nullptr; size_--; } @@ -125,7 +126,7 @@ struct IntrusiveList { if (current_) current_ = current_->next; return ret; } - bool hasNext() const { return current_ != 0; } + bool hasNext() const { return current_ != nullptr; } private: ListTy *list_; ItemTy *current_; @@ -140,6 +141,6 @@ struct IntrusiveList { Item *last_; }; -} // namespace __sanitizer +} // namespace __sanitizer -#endif // SANITIZER_LIST_H +#endif // SANITIZER_LIST_H diff --git a/contrib/compiler-rt/lib/sanitizer_common/sanitizer_mac.cc b/contrib/compiler-rt/lib/sanitizer_common/sanitizer_mac.cc index dddce1c1583c..715509d30746 100644 --- a/contrib/compiler-rt/lib/sanitizer_common/sanitizer_mac.cc +++ b/contrib/compiler-rt/lib/sanitizer_common/sanitizer_mac.cc @@ -13,6 +13,7 @@ #include "sanitizer_platform.h" #if SANITIZER_MAC +#include "sanitizer_mac.h" // Use 64-bit inodes in file operations. ASan does not support OS X 10.5, so // the clients will most certainly use 64-bit ones as well. @@ -25,7 +26,6 @@ #include "sanitizer_flags.h" #include "sanitizer_internal_defs.h" #include "sanitizer_libc.h" -#include "sanitizer_mac.h" #include "sanitizer_placement_new.h" #include "sanitizer_platform_limits_posix.h" #include "sanitizer_procmaps.h" @@ -36,11 +36,29 @@ extern char **environ; #endif +#if defined(__has_include) && __has_include(<os/trace.h>) +#define SANITIZER_OS_TRACE 1 +#include <os/trace.h> +#else +#define SANITIZER_OS_TRACE 0 +#endif + +#if !SANITIZER_IOS +#include <crt_externs.h> // for _NSGetArgv and _NSGetEnviron +#else +extern "C" { + extern char ***_NSGetArgv(void); +} +#endif + +#include <asl.h> +#include <dlfcn.h> // for dladdr() #include <errno.h> #include <fcntl.h> #include <libkern/OSAtomic.h> #include <mach-o/dyld.h> #include <mach/mach.h> +#include <mach/vm_statistics.h> #include <pthread.h> #include <sched.h> #include <signal.h> @@ -51,6 +69,7 @@ extern char **environ; #include <sys/sysctl.h> #include <sys/types.h> #include <unistd.h> +#include <util.h> namespace __sanitizer { @@ -59,6 +78,7 @@ namespace __sanitizer { // ---------------------- sanitizer_libc.h uptr internal_mmap(void *addr, size_t length, int prot, int flags, int fd, u64 offset) { + if (fd == -1) fd = VM_MAKE_TAG(VM_MEMORY_ANALYSIS_TOOL); return (uptr)mmap(addr, length, prot, flags, fd, offset); } @@ -145,9 +165,30 @@ uptr internal_sigprocmask(int how, __sanitizer_sigset_t *set, return sigprocmask(how, set, oldset); } +// Doesn't call pthread_atfork() handlers. +extern "C" pid_t __fork(void); + int internal_fork() { - // TODO(glider): this may call user's pthread_atfork() handlers which is bad. - return fork(); + return __fork(); +} + +int internal_forkpty(int *amaster) { + int master, slave; + if (openpty(&master, &slave, nullptr, nullptr, nullptr) == -1) return -1; + int pid = __fork(); + if (pid == -1) { + close(master); + close(slave); + return -1; + } + if (pid == 0) { + close(master); + CHECK_EQ(login_tty(slave), 0); + } else { + *amaster = master; + close(slave); + } + return pid; } uptr internal_rename(const char *oldpath, const char *newpath) { @@ -178,7 +219,7 @@ void GetThreadStackTopAndBottom(bool at_initialization, uptr *stack_top, uptr stacksize = pthread_get_stacksize_np(pthread_self()); // pthread_get_stacksize_np() returns an incorrect stack size for the main // thread on Mavericks. See - // https://code.google.com/p/address-sanitizer/issues/detail?id=261 + // https://github.com/google/sanitizers/issues/261 if ((GetMacosVersion() >= MACOS_VERSION_MAVERICKS) && at_initialization && stacksize == (1 << 19)) { struct rlimit rl; @@ -241,6 +282,10 @@ uptr ReadBinaryName(/*out*/char *buf, uptr buf_len) { return 0; } +uptr ReadLongProcessName(/*out*/char *buf, uptr buf_len) { + return ReadBinaryName(buf, buf_len); +} + void ReExec() { UNIMPLEMENTED(); } @@ -365,8 +410,67 @@ uptr GetRSS() { return info.resident_size; } -void *internal_start_thread(void (*func)(void *arg), void *arg) { return 0; } -void internal_join_thread(void *th) { } +void *internal_start_thread(void(*func)(void *arg), void *arg) { + // Start the thread with signals blocked, otherwise it can steal user signals. + __sanitizer_sigset_t set, old; + internal_sigfillset(&set); + internal_sigprocmask(SIG_SETMASK, &set, &old); + pthread_t th; + pthread_create(&th, 0, (void*(*)(void *arg))func, arg); + internal_sigprocmask(SIG_SETMASK, &old, 0); + return th; +} + +void internal_join_thread(void *th) { pthread_join((pthread_t)th, 0); } + +static BlockingMutex syslog_lock(LINKER_INITIALIZED); + +void WriteOneLineToSyslog(const char *s) { + syslog_lock.CheckLocked(); + asl_log(nullptr, nullptr, ASL_LEVEL_ERR, "%s", s); +} + +void LogMessageOnPrintf(const char *str) { + // Log all printf output to CrashLog. + if (common_flags()->abort_on_error) + CRAppendCrashLogMessage(str); +} + +void LogFullErrorReport(const char *buffer) { + // Log with os_trace. This will make it into the crash log. +#if SANITIZER_OS_TRACE + if (GetMacosVersion() >= MACOS_VERSION_YOSEMITE) { + // os_trace requires the message (format parameter) to be a string literal. + if (internal_strncmp(SanitizerToolName, "AddressSanitizer", + sizeof("AddressSanitizer") - 1) == 0) + os_trace("Address Sanitizer reported a failure."); + else if (internal_strncmp(SanitizerToolName, "UndefinedBehaviorSanitizer", + sizeof("UndefinedBehaviorSanitizer") - 1) == 0) + os_trace("Undefined Behavior Sanitizer reported a failure."); + else if (internal_strncmp(SanitizerToolName, "ThreadSanitizer", + sizeof("ThreadSanitizer") - 1) == 0) + os_trace("Thread Sanitizer reported a failure."); + else + os_trace("Sanitizer tool reported a failure."); + + if (common_flags()->log_to_syslog) + os_trace("Consult syslog for more information."); + } +#endif + + // Log to syslog. + // The logging on OS X may call pthread_create so we need the threading + // environment to be fully initialized. Also, this should never be called when + // holding the thread registry lock since that may result in a deadlock. If + // the reporting thread holds the thread registry mutex, and asl_log waits + // for GCD to dispatch a new thread, the process will deadlock, because the + // pthread_create wrapper needs to acquire the lock as well. + BlockingMutexLock l(&syslog_lock); + if (common_flags()->log_to_syslog) + WriteToSyslog(buffer); + + // The report is added to CrashLog as part of logging all of Printf output. +} void GetPcSpBp(void *context, uptr *pc, uptr *sp, uptr *bp) { ucontext_t *ucontext = (ucontext_t*)context; @@ -395,6 +499,173 @@ void GetPcSpBp(void *context, uptr *pc, uptr *sp, uptr *bp) { # endif } +static const char kDyldInsertLibraries[] = "DYLD_INSERT_LIBRARIES"; +LowLevelAllocator allocator_for_env; + +// Change the value of the env var |name|, leaking the original value. +// If |name_value| is NULL, the variable is deleted from the environment, +// otherwise the corresponding "NAME=value" string is replaced with +// |name_value|. +void LeakyResetEnv(const char *name, const char *name_value) { + char **env = GetEnviron(); + uptr name_len = internal_strlen(name); + while (*env != 0) { + uptr len = internal_strlen(*env); + if (len > name_len) { + const char *p = *env; + if (!internal_memcmp(p, name, name_len) && p[name_len] == '=') { + // Match. + if (name_value) { + // Replace the old value with the new one. + *env = const_cast<char*>(name_value); + } else { + // Shift the subsequent pointers back. + char **del = env; + do { + del[0] = del[1]; + } while (*del++); + } + } + } + env++; + } +} + +static bool reexec_disabled = false; + +void DisableReexec() { + reexec_disabled = true; +} + +extern "C" double dyldVersionNumber; +static const double kMinDyldVersionWithAutoInterposition = 360.0; + +bool DyldNeedsEnvVariable() { + // If running on OS X 10.11+ or iOS 9.0+, dyld will interpose even if + // DYLD_INSERT_LIBRARIES is not set. However, checking OS version via + // GetMacosVersion() doesn't work for the simulator. Let's instead check + // `dyldVersionNumber`, which is exported by dyld, against a known version + // number from the first OS release where this appeared. + return dyldVersionNumber < kMinDyldVersionWithAutoInterposition; +} + +void MaybeReexec() { + if (reexec_disabled) return; + + // Make sure the dynamic runtime library is preloaded so that the + // wrappers work. If it is not, set DYLD_INSERT_LIBRARIES and re-exec + // ourselves. + Dl_info info; + CHECK(dladdr((void*)((uptr)&__sanitizer_report_error_summary), &info)); + char *dyld_insert_libraries = + const_cast<char*>(GetEnv(kDyldInsertLibraries)); + uptr old_env_len = dyld_insert_libraries ? + internal_strlen(dyld_insert_libraries) : 0; + uptr fname_len = internal_strlen(info.dli_fname); + const char *dylib_name = StripModuleName(info.dli_fname); + uptr dylib_name_len = internal_strlen(dylib_name); + + bool lib_is_in_env = dyld_insert_libraries && + internal_strstr(dyld_insert_libraries, dylib_name); + if (DyldNeedsEnvVariable() && !lib_is_in_env) { + // DYLD_INSERT_LIBRARIES is not set or does not contain the runtime + // library. + InternalScopedString program_name(1024); + uint32_t buf_size = program_name.size(); + _NSGetExecutablePath(program_name.data(), &buf_size); + char *new_env = const_cast<char*>(info.dli_fname); + if (dyld_insert_libraries) { + // Append the runtime dylib name to the existing value of + // DYLD_INSERT_LIBRARIES. + new_env = (char*)allocator_for_env.Allocate(old_env_len + fname_len + 2); + internal_strncpy(new_env, dyld_insert_libraries, old_env_len); + new_env[old_env_len] = ':'; + // Copy fname_len and add a trailing zero. + internal_strncpy(new_env + old_env_len + 1, info.dli_fname, + fname_len + 1); + // Ok to use setenv() since the wrappers don't depend on the value of + // asan_inited. + setenv(kDyldInsertLibraries, new_env, /*overwrite*/1); + } else { + // Set DYLD_INSERT_LIBRARIES equal to the runtime dylib name. + setenv(kDyldInsertLibraries, info.dli_fname, /*overwrite*/0); + } + VReport(1, "exec()-ing the program with\n"); + VReport(1, "%s=%s\n", kDyldInsertLibraries, new_env); + VReport(1, "to enable wrappers.\n"); + execv(program_name.data(), *_NSGetArgv()); + + // We get here only if execv() failed. + Report("ERROR: The process is launched without DYLD_INSERT_LIBRARIES, " + "which is required for the sanitizer to work. We tried to set the " + "environment variable and re-execute itself, but execv() failed, " + "possibly because of sandbox restrictions. Make sure to launch the " + "executable with:\n%s=%s\n", kDyldInsertLibraries, new_env); + CHECK("execv failed" && 0); + } + + if (!lib_is_in_env) + return; + + // DYLD_INSERT_LIBRARIES is set and contains the runtime library. Let's remove + // the dylib from the environment variable, because interceptors are installed + // and we don't want our children to inherit the variable. + + uptr env_name_len = internal_strlen(kDyldInsertLibraries); + // Allocate memory to hold the previous env var name, its value, the '=' + // sign and the '\0' char. + char *new_env = (char*)allocator_for_env.Allocate( + old_env_len + 2 + env_name_len); + CHECK(new_env); + internal_memset(new_env, '\0', old_env_len + 2 + env_name_len); + internal_strncpy(new_env, kDyldInsertLibraries, env_name_len); + new_env[env_name_len] = '='; + char *new_env_pos = new_env + env_name_len + 1; + + // Iterate over colon-separated pieces of |dyld_insert_libraries|. + char *piece_start = dyld_insert_libraries; + char *piece_end = NULL; + char *old_env_end = dyld_insert_libraries + old_env_len; + do { + if (piece_start[0] == ':') piece_start++; + piece_end = internal_strchr(piece_start, ':'); + if (!piece_end) piece_end = dyld_insert_libraries + old_env_len; + if ((uptr)(piece_start - dyld_insert_libraries) > old_env_len) break; + uptr piece_len = piece_end - piece_start; + + char *filename_start = + (char *)internal_memrchr(piece_start, '/', piece_len); + uptr filename_len = piece_len; + if (filename_start) { + filename_start += 1; + filename_len = piece_len - (filename_start - piece_start); + } else { + filename_start = piece_start; + } + + // If the current piece isn't the runtime library name, + // append it to new_env. + if ((dylib_name_len != filename_len) || + (internal_memcmp(filename_start, dylib_name, dylib_name_len) != 0)) { + if (new_env_pos != new_env + env_name_len + 1) { + new_env_pos[0] = ':'; + new_env_pos++; + } + internal_strncpy(new_env_pos, piece_start, piece_len); + new_env_pos += piece_len; + } + // Move on to the next piece. + piece_start = piece_end; + } while (piece_start < old_env_end); + + // Can't use setenv() here, because it requires the allocator to be + // initialized. + // FIXME: instead of filtering DYLD_INSERT_LIBRARIES here, do it in + // a separate function called after InitializeAllocator(). + if (new_env_pos == new_env + env_name_len + 1) new_env = NULL; + LeakyResetEnv(kDyldInsertLibraries, new_env); +} + } // namespace __sanitizer #endif // SANITIZER_MAC diff --git a/contrib/compiler-rt/lib/sanitizer_common/sanitizer_mac.h b/contrib/compiler-rt/lib/sanitizer_common/sanitizer_mac.h index 50dbe93226c2..6e2b84f432e5 100644 --- a/contrib/compiler-rt/lib/sanitizer_common/sanitizer_mac.h +++ b/contrib/compiler-rt/lib/sanitizer_common/sanitizer_mac.h @@ -13,6 +13,7 @@ #ifndef SANITIZER_MAC_H #define SANITIZER_MAC_H +#include "sanitizer_common.h" #include "sanitizer_platform.h" #if SANITIZER_MAC #include "sanitizer_posix.h" @@ -37,5 +38,18 @@ char **GetEnviron(); } // namespace __sanitizer +extern "C" { +static char __crashreporter_info_buff__[kErrorMessageBufferSize] = {}; +static const char *__crashreporter_info__ __attribute__((__used__)) = + &__crashreporter_info_buff__[0]; +asm(".desc ___crashreporter_info__, 0x10"); +} // extern "C" +static BlockingMutex crashreporter_info_mutex(LINKER_INITIALIZED); + +INLINE void CRAppendCrashLogMessage(const char *msg) { + BlockingMutexLock l(&crashreporter_info_mutex); + internal_strlcat(__crashreporter_info_buff__, msg, + sizeof(__crashreporter_info_buff__)); } + #endif // SANITIZER_MAC #endif // SANITIZER_MAC_H diff --git a/contrib/compiler-rt/lib/sanitizer_common/sanitizer_malloc_mac.inc b/contrib/compiler-rt/lib/sanitizer_common/sanitizer_malloc_mac.inc new file mode 100644 index 000000000000..149857c168c6 --- /dev/null +++ b/contrib/compiler-rt/lib/sanitizer_common/sanitizer_malloc_mac.inc @@ -0,0 +1,329 @@ +//===-- sanitizer_malloc_mac.inc --------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file contains Mac-specific malloc interceptors and a custom zone +// implementation, which together replace the system allocator. +// +//===----------------------------------------------------------------------===// + +#include "sanitizer_common/sanitizer_platform.h" +#if !SANITIZER_MAC +#error "This file should only be compiled on Darwin." +#endif + +#include <AvailabilityMacros.h> +#include <CoreFoundation/CFBase.h> +#include <dlfcn.h> +#include <malloc/malloc.h> +#include <sys/mman.h> + +#include "interception/interception.h" +#include "sanitizer_common/sanitizer_mac.h" + +// Similar code is used in Google Perftools, +// https://github.com/gperftools/gperftools. + +static malloc_zone_t sanitizer_zone; + +INTERCEPTOR(malloc_zone_t *, malloc_create_zone, + vm_size_t start_size, unsigned zone_flags) { + COMMON_MALLOC_ENTER(); + uptr page_size = GetPageSizeCached(); + uptr allocated_size = RoundUpTo(sizeof(sanitizer_zone), page_size); + COMMON_MALLOC_MEMALIGN(page_size, allocated_size); + malloc_zone_t *new_zone = (malloc_zone_t *)p; + internal_memcpy(new_zone, &sanitizer_zone, sizeof(sanitizer_zone)); + new_zone->zone_name = NULL; // The name will be changed anyway. + if (GetMacosVersion() >= MACOS_VERSION_LION) { + // Prevent the client app from overwriting the zone contents. + // Library functions that need to modify the zone will set PROT_WRITE on it. + // This matches the behavior of malloc_create_zone() on OSX 10.7 and higher. + mprotect(new_zone, allocated_size, PROT_READ); + } + return new_zone; +} + +INTERCEPTOR(malloc_zone_t *, malloc_default_zone, void) { + COMMON_MALLOC_ENTER(); + return &sanitizer_zone; +} + +INTERCEPTOR(malloc_zone_t *, malloc_default_purgeable_zone, void) { + // FIXME: ASan should support purgeable allocations. + // https://github.com/google/sanitizers/issues/139 + COMMON_MALLOC_ENTER(); + return &sanitizer_zone; +} + +INTERCEPTOR(void, malloc_make_purgeable, void *ptr) { + // FIXME: ASan should support purgeable allocations. Ignoring them is fine + // for now. + COMMON_MALLOC_ENTER(); +} + +INTERCEPTOR(int, malloc_make_nonpurgeable, void *ptr) { + // FIXME: ASan should support purgeable allocations. Ignoring them is fine + // for now. + COMMON_MALLOC_ENTER(); + // Must return 0 if the contents were not purged since the last call to + // malloc_make_purgeable(). + return 0; +} + +INTERCEPTOR(void, malloc_set_zone_name, malloc_zone_t *zone, const char *name) { + COMMON_MALLOC_ENTER(); + // Allocate |sizeof(COMMON_MALLOC_ZONE_NAME "-") + internal_strlen(name)| + // bytes. + size_t buflen = + sizeof(COMMON_MALLOC_ZONE_NAME "-") + (name ? internal_strlen(name) : 0); + InternalScopedString new_name(buflen); + if (name && zone->introspect == sanitizer_zone.introspect) { + new_name.append(COMMON_MALLOC_ZONE_NAME "-%s", name); + name = new_name.data(); + } + + // Call the system malloc's implementation for both external and our zones, + // since that appropriately changes VM region protections on the zone. + REAL(malloc_set_zone_name)(zone, name); +} + +INTERCEPTOR(void *, malloc, size_t size) { + COMMON_MALLOC_ENTER(); + COMMON_MALLOC_MALLOC(size); + return p; +} + +INTERCEPTOR(void, free, void *ptr) { + COMMON_MALLOC_ENTER(); + if (!ptr) return; + COMMON_MALLOC_FREE(ptr); +} + +INTERCEPTOR(void *, realloc, void *ptr, size_t size) { + COMMON_MALLOC_ENTER(); + COMMON_MALLOC_REALLOC(ptr, size); + return p; +} + +INTERCEPTOR(void *, calloc, size_t nmemb, size_t size) { + COMMON_MALLOC_ENTER(); + COMMON_MALLOC_CALLOC(nmemb, size); + return p; +} + +INTERCEPTOR(void *, valloc, size_t size) { + COMMON_MALLOC_ENTER(); + COMMON_MALLOC_VALLOC(size); + return p; +} + +INTERCEPTOR(size_t, malloc_good_size, size_t size) { + COMMON_MALLOC_ENTER(); + return sanitizer_zone.introspect->good_size(&sanitizer_zone, size); +} + +INTERCEPTOR(int, posix_memalign, void **memptr, size_t alignment, size_t size) { + COMMON_MALLOC_ENTER(); + CHECK(memptr); + COMMON_MALLOC_MEMALIGN(alignment, size); + if (p) { + *memptr = p; + return 0; + } + return -1; +} + +namespace { + +// TODO(glider): the __sanitizer_mz_* functions should be united with the Linux +// wrappers, as they are basically copied from there. +extern "C" +SANITIZER_INTERFACE_ATTRIBUTE +size_t __sanitizer_mz_size(malloc_zone_t* zone, const void* ptr) { + COMMON_MALLOC_SIZE(ptr); + return size; +} + +extern "C" +SANITIZER_INTERFACE_ATTRIBUTE +void *__sanitizer_mz_malloc(malloc_zone_t *zone, uptr size) { + COMMON_MALLOC_ENTER(); + COMMON_MALLOC_MALLOC(size); + return p; +} + +extern "C" +SANITIZER_INTERFACE_ATTRIBUTE +void *__sanitizer_mz_calloc(malloc_zone_t *zone, size_t nmemb, size_t size) { + if (UNLIKELY(!COMMON_MALLOC_SANITIZER_INITIALIZED)) { + // Hack: dlsym calls calloc before REAL(calloc) is retrieved from dlsym. + const size_t kCallocPoolSize = 1024; + static uptr calloc_memory_for_dlsym[kCallocPoolSize]; + static size_t allocated; + size_t size_in_words = ((nmemb * size) + kWordSize - 1) / kWordSize; + void *mem = (void*)&calloc_memory_for_dlsym[allocated]; + allocated += size_in_words; + CHECK(allocated < kCallocPoolSize); + return mem; + } + COMMON_MALLOC_CALLOC(nmemb, size); + return p; +} + +extern "C" +SANITIZER_INTERFACE_ATTRIBUTE +void *__sanitizer_mz_valloc(malloc_zone_t *zone, size_t size) { + COMMON_MALLOC_ENTER(); + COMMON_MALLOC_VALLOC(size); + return p; +} + +// TODO(glider): the allocation callbacks need to be refactored. +extern "C" +SANITIZER_INTERFACE_ATTRIBUTE +void __sanitizer_mz_free(malloc_zone_t *zone, void *ptr) { + if (!ptr) return; + COMMON_MALLOC_FREE(ptr); +} + +#define GET_ZONE_FOR_PTR(ptr) \ + malloc_zone_t *zone_ptr = malloc_zone_from_ptr(ptr); \ + const char *zone_name = (zone_ptr == 0) ? 0 : zone_ptr->zone_name + +extern "C" +SANITIZER_INTERFACE_ATTRIBUTE +void *__sanitizer_mz_realloc(malloc_zone_t *zone, void *ptr, size_t new_size) { + if (!ptr) { + COMMON_MALLOC_MALLOC(new_size); + return p; + } else { + COMMON_MALLOC_SIZE(ptr); + if (size) { + COMMON_MALLOC_REALLOC(ptr, new_size); + return p; + } else { + // We can't recover from reallocating an unknown address, because + // this would require reading at most |new_size| bytes from + // potentially unaccessible memory. + GET_ZONE_FOR_PTR(ptr); + COMMON_MALLOC_REPORT_UNKNOWN_REALLOC(ptr, zone_ptr, zone_name); + return nullptr; + } + } +} + +extern "C" +SANITIZER_INTERFACE_ATTRIBUTE +void __sanitizer_mz_destroy(malloc_zone_t* zone) { + // A no-op -- we will not be destroyed! + Report("__sanitizer_mz_destroy() called -- ignoring\n"); +} + +extern "C" +SANITIZER_INTERFACE_ATTRIBUTE +void *__sanitizer_mz_memalign(malloc_zone_t *zone, size_t align, size_t size) { + COMMON_MALLOC_ENTER(); + COMMON_MALLOC_MEMALIGN(align, size); + return p; +} + +// This function is currently unused, and we build with -Werror. +#if 0 +void __sanitizer_mz_free_definite_size( + malloc_zone_t* zone, void *ptr, size_t size) { + // TODO(glider): check that |size| is valid. + UNIMPLEMENTED(); +} +#endif + +kern_return_t mi_enumerator(task_t task, void *, + unsigned type_mask, vm_address_t zone_address, + memory_reader_t reader, + vm_range_recorder_t recorder) { + // Should enumerate all the pointers we have. Seems like a lot of work. + return KERN_FAILURE; +} + +size_t mi_good_size(malloc_zone_t *zone, size_t size) { + // I think it's always safe to return size, but we maybe could do better. + return size; +} + +boolean_t mi_check(malloc_zone_t *zone) { + UNIMPLEMENTED(); +} + +void mi_print(malloc_zone_t *zone, boolean_t verbose) { + UNIMPLEMENTED(); +} + +void mi_log(malloc_zone_t *zone, void *address) { + // I don't think we support anything like this +} + +void mi_force_lock(malloc_zone_t *zone) { + COMMON_MALLOC_FORCE_LOCK(); +} + +void mi_force_unlock(malloc_zone_t *zone) { + COMMON_MALLOC_FORCE_UNLOCK(); +} + +void mi_statistics(malloc_zone_t *zone, malloc_statistics_t *stats) { + COMMON_MALLOC_FILL_STATS(zone, stats); +} + +boolean_t mi_zone_locked(malloc_zone_t *zone) { + // UNIMPLEMENTED(); + return false; +} + +} // unnamed namespace + +namespace COMMON_MALLOC_NAMESPACE { + +void ReplaceSystemMalloc() { + static malloc_introspection_t sanitizer_zone_introspection; + // Ok to use internal_memset, these places are not performance-critical. + internal_memset(&sanitizer_zone_introspection, 0, + sizeof(sanitizer_zone_introspection)); + + sanitizer_zone_introspection.enumerator = &mi_enumerator; + sanitizer_zone_introspection.good_size = &mi_good_size; + sanitizer_zone_introspection.check = &mi_check; + sanitizer_zone_introspection.print = &mi_print; + sanitizer_zone_introspection.log = &mi_log; + sanitizer_zone_introspection.force_lock = &mi_force_lock; + sanitizer_zone_introspection.force_unlock = &mi_force_unlock; + sanitizer_zone_introspection.statistics = &mi_statistics; + sanitizer_zone_introspection.zone_locked = &mi_zone_locked; + + internal_memset(&sanitizer_zone, 0, sizeof(malloc_zone_t)); + + // Use version 6 for OSX >= 10.6. + sanitizer_zone.version = 6; + sanitizer_zone.zone_name = COMMON_MALLOC_ZONE_NAME; + sanitizer_zone.size = &__sanitizer_mz_size; + sanitizer_zone.malloc = &__sanitizer_mz_malloc; + sanitizer_zone.calloc = &__sanitizer_mz_calloc; + sanitizer_zone.valloc = &__sanitizer_mz_valloc; + sanitizer_zone.free = &__sanitizer_mz_free; + sanitizer_zone.realloc = &__sanitizer_mz_realloc; + sanitizer_zone.destroy = &__sanitizer_mz_destroy; + sanitizer_zone.batch_malloc = 0; + sanitizer_zone.batch_free = 0; + sanitizer_zone.free_definite_size = 0; + sanitizer_zone.memalign = &__sanitizer_mz_memalign; + sanitizer_zone.introspect = &sanitizer_zone_introspection; + + // Register the zone. + malloc_zone_register(&sanitizer_zone); +} + +} // namespace COMMON_MALLOC_NAMESPACE diff --git a/contrib/compiler-rt/lib/sanitizer_common/sanitizer_persistent_allocator.h b/contrib/compiler-rt/lib/sanitizer_common/sanitizer_persistent_allocator.h index 326406b12bfb..8e5ce06d4c3d 100644 --- a/contrib/compiler-rt/lib/sanitizer_common/sanitizer_persistent_allocator.h +++ b/contrib/compiler-rt/lib/sanitizer_common/sanitizer_persistent_allocator.h @@ -10,6 +10,7 @@ // A fast memory allocator that does not support free() nor realloc(). // All allocations are forever. //===----------------------------------------------------------------------===// + #ifndef SANITIZER_PERSISTENT_ALLOCATOR_H #define SANITIZER_PERSISTENT_ALLOCATOR_H @@ -36,7 +37,7 @@ inline void *PersistentAllocator::tryAlloc(uptr size) { for (;;) { uptr cmp = atomic_load(®ion_pos, memory_order_acquire); uptr end = atomic_load(®ion_end, memory_order_acquire); - if (cmp == 0 || cmp + size > end) return 0; + if (cmp == 0 || cmp + size > end) return nullptr; if (atomic_compare_exchange_weak(®ion_pos, &cmp, cmp + size, memory_order_acquire)) return (void *)cmp; @@ -68,4 +69,4 @@ inline void *PersistentAlloc(uptr sz) { } // namespace __sanitizer -#endif // SANITIZER_PERSISTENT_ALLOCATOR_H +#endif // SANITIZER_PERSISTENT_ALLOCATOR_H diff --git a/contrib/compiler-rt/lib/sanitizer_common/sanitizer_platform.h b/contrib/compiler-rt/lib/sanitizer_common/sanitizer_platform.h index b47281b589e9..841cceb510a8 100644 --- a/contrib/compiler-rt/lib/sanitizer_common/sanitizer_platform.h +++ b/contrib/compiler-rt/lib/sanitizer_common/sanitizer_platform.h @@ -87,7 +87,7 @@ // For such platforms build this code with -DSANITIZER_CAN_USE_ALLOCATOR64=0 or // change the definition of SANITIZER_CAN_USE_ALLOCATOR64 here. #ifndef SANITIZER_CAN_USE_ALLOCATOR64 -# if defined(__aarch64__) || defined(__mips64) +# if defined(__mips64) || defined(__aarch64__) # define SANITIZER_CAN_USE_ALLOCATOR64 0 # else # define SANITIZER_CAN_USE_ALLOCATOR64 (SANITIZER_WORDSIZE == 64) @@ -95,12 +95,9 @@ #endif // The range of addresses which can be returned my mmap. -// FIXME: this value should be different on different platforms, -// e.g. on AArch64 it is most likely (1ULL << 39). Larger values will still work -// but will consume more memory for TwoLevelByteMap. -#if defined(__aarch64__) -# define SANITIZER_MMAP_RANGE_SIZE FIRST_32_SECOND_64(1ULL << 32, 1ULL << 39) -#elif defined(__mips__) +// FIXME: this value should be different on different platforms. Larger values +// will still work but will consume more memory for TwoLevelByteMap. +#if defined(__mips__) # define SANITIZER_MMAP_RANGE_SIZE FIRST_32_SECOND_64(1ULL << 32, 1ULL << 40) #else # define SANITIZER_MMAP_RANGE_SIZE FIRST_32_SECOND_64(1ULL << 32, 1ULL << 47) @@ -130,7 +127,7 @@ #define SANITIZER_USES_UID16_SYSCALLS 0 #endif -#ifdef __mips__ +#if defined(__mips__) # define SANITIZER_POINTER_FORMAT_LENGTH FIRST_32_SECOND_64(8, 10) #else # define SANITIZER_POINTER_FORMAT_LENGTH FIRST_32_SECOND_64(8, 12) @@ -142,4 +139,15 @@ # define HAVE_TIRPC_RPC_XDR_H 0 #endif +/// \macro MSC_PREREQ +/// \brief Is the compiler MSVC of at least the specified version? +/// The common \param version values to check for are: +/// * 1800: Microsoft Visual Studio 2013 / 12.0 +/// * 1900: Microsoft Visual Studio 2015 / 14.0 +#ifdef _MSC_VER +# define MSC_PREREQ(version) (_MSC_VER >= (version)) +#else +# define MSC_PREREQ(version) 0 +#endif + #endif // SANITIZER_PLATFORM_H diff --git a/contrib/compiler-rt/lib/sanitizer_common/sanitizer_platform_interceptors.h b/contrib/compiler-rt/lib/sanitizer_common/sanitizer_platform_interceptors.h index 77cc84cd03af..430ad4839809 100644 --- a/contrib/compiler-rt/lib/sanitizer_common/sanitizer_platform_interceptors.h +++ b/contrib/compiler-rt/lib/sanitizer_common/sanitizer_platform_interceptors.h @@ -60,6 +60,7 @@ #define SANITIZER_INTERCEPT_STRPBRK 1 #define SANITIZER_INTERCEPT_TEXTDOMAIN SI_LINUX_NOT_ANDROID #define SANITIZER_INTERCEPT_STRCASECMP SI_NOT_WINDOWS +#define SANITIZER_INTERCEPT_MEMCMP 1 #define SANITIZER_INTERCEPT_MEMCHR 1 #define SANITIZER_INTERCEPT_MEMRCHR SI_FREEBSD || SI_LINUX @@ -132,7 +133,7 @@ #define SANITIZER_INTERCEPT_READDIR64 SI_LINUX_NOT_ANDROID #define SANITIZER_INTERCEPT_PTRACE SI_LINUX_NOT_ANDROID && \ (defined(__i386) || defined(__x86_64) || defined(__mips64) || \ - defined(__powerpc64__)) + defined(__powerpc64__) || defined(__aarch64__) || defined(__arm__)) #define SANITIZER_INTERCEPT_SETLOCALE SI_NOT_WINDOWS #define SANITIZER_INTERCEPT_GETCWD SI_NOT_WINDOWS #define SANITIZER_INTERCEPT_GET_CURRENT_DIR_NAME SI_LINUX_NOT_ANDROID @@ -142,6 +143,8 @@ #define SANITIZER_INTERCEPT_WCSTOMBS SI_NOT_WINDOWS #define SANITIZER_INTERCEPT_WCSNRTOMBS \ SI_FREEBSD || SI_MAC || SI_LINUX_NOT_ANDROID +#define SANITIZER_INTERCEPT_WCRTOMB \ + SI_FREEBSD || SI_MAC || SI_LINUX_NOT_ANDROID #define SANITIZER_INTERCEPT_TCGETATTR SI_LINUX_NOT_ANDROID #define SANITIZER_INTERCEPT_REALPATH SI_NOT_WINDOWS #define SANITIZER_INTERCEPT_CANONICALIZE_FILE_NAME SI_LINUX_NOT_ANDROID @@ -217,7 +220,7 @@ // FIXME: getline seems to be available on OSX 10.7 #define SANITIZER_INTERCEPT_GETLINE SI_FREEBSD || SI_LINUX_NOT_ANDROID -#define SANITIZER_INTERCEPT__EXIT SI_LINUX || SI_FREEBSD +#define SANITIZER_INTERCEPT__EXIT SI_LINUX || SI_FREEBSD || SI_MAC #define SANITIZER_INTERCEPT_PHTREAD_MUTEX SI_NOT_WINDOWS #define SANITIZER_INTERCEPT_PTHREAD_SETNAME_NP \ @@ -253,5 +256,13 @@ #define SANITIZER_INTERCEPT_MLOCKX SI_NOT_WINDOWS #define SANITIZER_INTERCEPT_FOPENCOOKIE SI_LINUX_NOT_ANDROID +#define SANITIZER_INTERCEPT_SEM SI_LINUX || SI_FREEBSD +#define SANITIZER_INTERCEPT_PTHREAD_SETCANCEL SI_NOT_WINDOWS +#define SANITIZER_INTERCEPT_MINCORE SI_LINUX +#define SANITIZER_INTERCEPT_PROCESS_VM_READV SI_LINUX +#define SANITIZER_INTERCEPT_CTERMID SI_LINUX || SI_MAC || SI_FREEBSD +#define SANITIZER_INTERCEPT_CTERMID_R SI_MAC || SI_FREEBSD + +#define SANITIZER_INTERCEPTOR_HOOKS SI_LINUX #endif // #ifndef SANITIZER_PLATFORM_INTERCEPTORS_H diff --git a/contrib/compiler-rt/lib/sanitizer_common/sanitizer_platform_limits_posix.cc b/contrib/compiler-rt/lib/sanitizer_common/sanitizer_platform_limits_posix.cc index aaa37ed02ebd..b642cba0fede 100644 --- a/contrib/compiler-rt/lib/sanitizer_common/sanitizer_platform_limits_posix.cc +++ b/contrib/compiler-rt/lib/sanitizer_common/sanitizer_platform_limits_posix.cc @@ -12,8 +12,8 @@ // Sizes and layouts of platform-specific POSIX data structures. //===----------------------------------------------------------------------===// - #include "sanitizer_platform.h" + #if SANITIZER_LINUX || SANITIZER_FREEBSD || SANITIZER_MAC // Tests in this file assume that off_t-dependent data structures match the // libc ABI. For example, struct dirent here is what readdir() function (as @@ -119,9 +119,17 @@ #if SANITIZER_LINUX || SANITIZER_FREEBSD # include <utime.h> # include <sys/ptrace.h> -# if defined(__mips64) +# if defined(__mips64) || defined(__aarch64__) || defined(__arm__) # include <asm/ptrace.h> +# ifdef __arm__ +typedef struct user_fpregs elf_fpregset_t; +# define ARM_VFPREGS_SIZE_ASAN (32 * 8 /*fpregs*/ + 4 /*fpscr*/) +# if !defined(ARM_VFPREGS_SIZE) +# define ARM_VFPREGS_SIZE ARM_VFPREGS_SIZE_ASAN +# endif +# endif # endif +# include <semaphore.h> #endif #if !SANITIZER_ANDROID @@ -195,7 +203,7 @@ namespace __sanitizer { unsigned struct_stat_sz = sizeof(struct stat); #if !SANITIZER_IOS && !SANITIZER_FREEBSD unsigned struct_stat64_sz = sizeof(struct stat64); -#endif // !SANITIZER_IOS && !SANITIZER_FREEBSD +#endif // !SANITIZER_IOS && !SANITIZER_FREEBSD unsigned struct_rusage_sz = sizeof(struct rusage); unsigned struct_tm_sz = sizeof(struct tm); unsigned struct_passwd_sz = sizeof(struct passwd); @@ -236,27 +244,27 @@ namespace __sanitizer { unsigned struct_new_utsname_sz = sizeof(struct new_utsname); unsigned struct_old_utsname_sz = sizeof(struct old_utsname); unsigned struct_oldold_utsname_sz = sizeof(struct oldold_utsname); -#endif // SANITIZER_LINUX +#endif // SANITIZER_LINUX #if SANITIZER_LINUX || SANITIZER_FREEBSD unsigned struct_rlimit_sz = sizeof(struct rlimit); unsigned struct_timespec_sz = sizeof(struct timespec); unsigned struct_utimbuf_sz = sizeof(struct utimbuf); unsigned struct_itimerspec_sz = sizeof(struct itimerspec); -#endif // SANITIZER_LINUX || SANITIZER_FREEBSD +#endif // SANITIZER_LINUX || SANITIZER_FREEBSD #if SANITIZER_LINUX && !SANITIZER_ANDROID unsigned struct_ustat_sz = sizeof(struct ustat); unsigned struct_rlimit64_sz = sizeof(struct rlimit64); unsigned struct_statvfs64_sz = sizeof(struct statvfs64); -#endif // SANITIZER_LINUX && !SANITIZER_ANDROID +#endif // SANITIZER_LINUX && !SANITIZER_ANDROID #if (SANITIZER_LINUX || SANITIZER_FREEBSD) && !SANITIZER_ANDROID unsigned struct_timex_sz = sizeof(struct timex); unsigned struct_msqid_ds_sz = sizeof(struct msqid_ds); unsigned struct_mq_attr_sz = sizeof(struct mq_attr); unsigned struct_statvfs_sz = sizeof(struct statvfs); -#endif // (SANITIZER_LINUX || SANITIZER_FREEBSD) && !SANITIZER_ANDROID +#endif // (SANITIZER_LINUX || SANITIZER_FREEBSD) && !SANITIZER_ANDROID uptr sig_ign = (uptr)SIG_IGN; uptr sig_dfl = (uptr)SIG_DFL; @@ -290,6 +298,12 @@ namespace __sanitizer { return 0; } +#if SANITIZER_LINUX +unsigned struct_ElfW_Phdr_sz = sizeof(ElfW(Phdr)); +#elif SANITIZER_FREEBSD +unsigned struct_ElfW_Phdr_sz = sizeof(Elf_Phdr); +#endif + #if (SANITIZER_LINUX || SANITIZER_FREEBSD) && !SANITIZER_ANDROID int glob_nomatch = GLOB_NOMATCH; int glob_altdirfunc = GLOB_ALTDIRFUNC; @@ -297,34 +311,63 @@ namespace __sanitizer { #if SANITIZER_LINUX && !SANITIZER_ANDROID && \ (defined(__i386) || defined(__x86_64) || defined(__mips64) || \ - defined(__powerpc64__)) -#if defined(__mips64) || defined(__powerpc64__) + defined(__powerpc64__) || defined(__aarch64__) || defined(__arm__)) +#if defined(__mips64) || defined(__powerpc64__) || defined(__arm__) unsigned struct_user_regs_struct_sz = sizeof(struct pt_regs); unsigned struct_user_fpregs_struct_sz = sizeof(elf_fpregset_t); +#elif defined(__aarch64__) + unsigned struct_user_regs_struct_sz = sizeof(struct user_pt_regs); + unsigned struct_user_fpregs_struct_sz = sizeof(struct user_fpsimd_state); #else unsigned struct_user_regs_struct_sz = sizeof(struct user_regs_struct); unsigned struct_user_fpregs_struct_sz = sizeof(struct user_fpregs_struct); -#endif // __mips64 || __powerpc64__ -#if defined(__x86_64) || defined(__mips64) || defined(__powerpc64__) +#endif // __mips64 || __powerpc64__ || __aarch64__ +#if defined(__x86_64) || defined(__mips64) || defined(__powerpc64__) || \ + defined(__aarch64__) || defined(__arm__) unsigned struct_user_fpxregs_struct_sz = 0; #else unsigned struct_user_fpxregs_struct_sz = sizeof(struct user_fpxregs_struct); -#endif // __x86_64 || __mips64 || __powerpc64__ +#endif // __x86_64 || __mips64 || __powerpc64__ || __aarch64__ || __arm__ +#ifdef __arm__ + unsigned struct_user_vfpregs_struct_sz = ARM_VFPREGS_SIZE; +#else + unsigned struct_user_vfpregs_struct_sz = 0; +#endif int ptrace_peektext = PTRACE_PEEKTEXT; int ptrace_peekdata = PTRACE_PEEKDATA; int ptrace_peekuser = PTRACE_PEEKUSER; +#if (defined(PTRACE_GETREGS) && defined(PTRACE_SETREGS)) || \ + (defined(PT_GETREGS) && defined(PT_SETREGS)) int ptrace_getregs = PTRACE_GETREGS; int ptrace_setregs = PTRACE_SETREGS; +#else + int ptrace_getregs = -1; + int ptrace_setregs = -1; +#endif +#if (defined(PTRACE_GETFPREGS) && defined(PTRACE_SETFPREGS)) || \ + (defined(PT_GETFPREGS) && defined(PT_SETFPREGS)) int ptrace_getfpregs = PTRACE_GETFPREGS; int ptrace_setfpregs = PTRACE_SETFPREGS; -#if defined(PTRACE_GETFPXREGS) && defined(PTRACE_SETFPXREGS) +#else + int ptrace_getfpregs = -1; + int ptrace_setfpregs = -1; +#endif +#if (defined(PTRACE_GETFPXREGS) && defined(PTRACE_SETFPXREGS)) || \ + (defined(PT_GETFPXREGS) && defined(PT_SETFPXREGS)) int ptrace_getfpxregs = PTRACE_GETFPXREGS; int ptrace_setfpxregs = PTRACE_SETFPXREGS; #else int ptrace_getfpxregs = -1; int ptrace_setfpxregs = -1; -#endif // PTRACE_GETFPXREGS/PTRACE_SETFPXREGS +#endif // PTRACE_GETFPXREGS/PTRACE_SETFPXREGS +#if defined(PTRACE_GETVFPREGS) && defined(PTRACE_SETVFPREGS) + int ptrace_getvfpregs = PTRACE_GETVFPREGS; + int ptrace_setvfpregs = PTRACE_SETVFPREGS; +#else + int ptrace_getvfpregs = -1; + int ptrace_setvfpregs = -1; +#endif int ptrace_geteventmsg = PTRACE_GETEVENTMSG; #if (defined(PTRACE_GETSIGINFO) && defined(PTRACE_SETSIGINFO)) || \ (defined(PT_GETSIGINFO) && defined(PT_SETSIGINFO)) @@ -333,14 +376,14 @@ namespace __sanitizer { #else int ptrace_getsiginfo = -1; int ptrace_setsiginfo = -1; -#endif // PTRACE_GETSIGINFO/PTRACE_SETSIGINFO +#endif // PTRACE_GETSIGINFO/PTRACE_SETSIGINFO #if defined(PTRACE_GETREGSET) && defined(PTRACE_SETREGSET) int ptrace_getregset = PTRACE_GETREGSET; int ptrace_setregset = PTRACE_SETREGSET; #else int ptrace_getregset = -1; int ptrace_setregset = -1; -#endif // PTRACE_GETREGSET/PTRACE_SETREGSET +#endif // PTRACE_GETREGSET/PTRACE_SETREGSET #endif unsigned path_max = PATH_MAX; @@ -378,7 +421,7 @@ namespace __sanitizer { unsigned struct_vt_consize_sz = sizeof(struct vt_consize); unsigned struct_vt_sizes_sz = sizeof(struct vt_sizes); unsigned struct_vt_stat_sz = sizeof(struct vt_stat); -#endif // SANITIZER_LINUX +#endif // SANITIZER_LINUX #if SANITIZER_LINUX || SANITIZER_FREEBSD #if SOUND_VERSION >= 0x040000 @@ -398,7 +441,7 @@ namespace __sanitizer { unsigned struct_seq_event_rec_sz = sizeof(struct seq_event_rec); unsigned struct_synth_info_sz = sizeof(struct synth_info); unsigned struct_vt_mode_sz = sizeof(struct vt_mode); -#endif // SANITIZER_LINUX || SANITIZER_FREEBSD +#endif // SANITIZER_LINUX || SANITIZER_FREEBSD #if SANITIZER_LINUX && !SANITIZER_ANDROID unsigned struct_ax25_parms_struct_sz = sizeof(struct ax25_parms_struct); @@ -423,12 +466,12 @@ namespace __sanitizer { unsigned struct_sockaddr_ax25_sz = sizeof(struct sockaddr_ax25); unsigned struct_unimapdesc_sz = sizeof(struct unimapdesc); unsigned struct_unimapinit_sz = sizeof(struct unimapinit); -#endif // SANITIZER_LINUX && !SANITIZER_ANDROID +#endif // SANITIZER_LINUX && !SANITIZER_ANDROID #if (SANITIZER_LINUX || SANITIZER_FREEBSD) && !SANITIZER_ANDROID unsigned struct_audio_buf_info_sz = sizeof(struct audio_buf_info); unsigned struct_ppp_stats_sz = sizeof(struct ppp_stats); -#endif // (SANITIZER_LINUX || SANITIZER_FREEBSD) && !SANITIZER_ANDROID +#endif // (SANITIZER_LINUX || SANITIZER_FREEBSD) && !SANITIZER_ANDROID #if !SANITIZER_ANDROID && !SANITIZER_MAC unsigned struct_sioc_sg_req_sz = sizeof(struct sioc_sg_req); @@ -643,7 +686,7 @@ namespace __sanitizer { unsigned IOCTL_SOUND_PCM_READ_RATE = SOUND_PCM_READ_RATE; unsigned IOCTL_SOUND_PCM_WRITE_CHANNELS = SOUND_PCM_WRITE_CHANNELS; unsigned IOCTL_SOUND_PCM_WRITE_FILTER = SOUND_PCM_WRITE_FILTER; -#endif // SOUND_VERSION +#endif // SOUND_VERSION unsigned IOCTL_TCFLSH = TCFLSH; unsigned IOCTL_TCGETA = TCGETA; unsigned IOCTL_TCGETS = TCGETS; @@ -766,7 +809,7 @@ namespace __sanitizer { unsigned IOCTL_VT_RELDISP = VT_RELDISP; unsigned IOCTL_VT_SETMODE = VT_SETMODE; unsigned IOCTL_VT_WAITACTIVE = VT_WAITACTIVE; -#endif // SANITIZER_LINUX || SANITIZER_FREEBSD +#endif // SANITIZER_LINUX || SANITIZER_FREEBSD #if SANITIZER_LINUX && !SANITIZER_ANDROID unsigned IOCTL_CYGETDEFTHRESH = CYGETDEFTHRESH; @@ -857,7 +900,7 @@ namespace __sanitizer { unsigned IOCTL_TIOCSERGETMULTI = TIOCSERGETMULTI; unsigned IOCTL_TIOCSERSETMULTI = TIOCSERSETMULTI; unsigned IOCTL_TIOCSSERIAL = TIOCSSERIAL; -#endif // SANITIZER_LINUX && !SANITIZER_ANDROID +#endif // SANITIZER_LINUX && !SANITIZER_ANDROID #if (SANITIZER_LINUX || SANITIZER_FREEBSD) && !SANITIZER_ANDROID unsigned IOCTL_GIO_SCRNMAP = GIO_SCRNMAP; @@ -875,7 +918,7 @@ namespace __sanitizer { unsigned IOCTL_PIO_SCRNMAP = PIO_SCRNMAP; unsigned IOCTL_SNDCTL_DSP_GETISPACE = SNDCTL_DSP_GETISPACE; unsigned IOCTL_SNDCTL_DSP_GETOSPACE = SNDCTL_DSP_GETOSPACE; -#endif // (SANITIZER_LINUX || SANITIZER_FREEBSD) && !SANITIZER_ANDROID +#endif // (SANITIZER_LINUX || SANITIZER_FREEBSD) && !SANITIZER_ANDROID const int errno_EINVAL = EINVAL; // EOWNERDEAD is not present in some older platforms. @@ -887,7 +930,7 @@ namespace __sanitizer { const int si_SEGV_MAPERR = SEGV_MAPERR; const int si_SEGV_ACCERR = SEGV_ACCERR; -} // namespace __sanitizer +} // namespace __sanitizer COMPILER_CHECK(sizeof(__sanitizer_pthread_attr_t) >= sizeof(pthread_attr_t)); @@ -917,7 +960,7 @@ COMPILER_CHECK(IOC_SIZE(0x12345678) == _IOC_SIZE(0x12345678)); COMPILER_CHECK(IOC_DIR(0x12345678) == _IOC_DIR(0x12345678)); COMPILER_CHECK(IOC_NR(0x12345678) == _IOC_NR(0x12345678)); COMPILER_CHECK(IOC_TYPE(0x12345678) == _IOC_TYPE(0x12345678)); -#endif // SANITIZER_LINUX +#endif // SANITIZER_LINUX #if SANITIZER_LINUX || SANITIZER_FREEBSD // There are more undocumented fields in dl_phdr_info that we are not interested @@ -927,7 +970,7 @@ CHECK_SIZE_AND_OFFSET(dl_phdr_info, dlpi_addr); CHECK_SIZE_AND_OFFSET(dl_phdr_info, dlpi_name); CHECK_SIZE_AND_OFFSET(dl_phdr_info, dlpi_phdr); CHECK_SIZE_AND_OFFSET(dl_phdr_info, dlpi_phnum); -#endif // SANITIZER_LINUX || SANITIZER_FREEBSD +#endif // SANITIZER_LINUX || SANITIZER_FREEBSD #if (SANITIZER_LINUX || SANITIZER_FREEBSD) && !SANITIZER_ANDROID CHECK_TYPE_SIZE(glob_t); @@ -1124,14 +1167,14 @@ CHECK_SIZE_AND_OFFSET(ifaddrs, ifa_netmask); # if SANITIZER_FREEBSD CHECK_SIZE_AND_OFFSET(ifaddrs, ifa_dstaddr); # else -COMPILER_CHECK(sizeof(((__sanitizer_ifaddrs *)NULL)->ifa_dstaddr) == - sizeof(((ifaddrs *)NULL)->ifa_ifu)); +COMPILER_CHECK(sizeof(((__sanitizer_ifaddrs *)nullptr)->ifa_dstaddr) == + sizeof(((ifaddrs *)nullptr)->ifa_ifu)); COMPILER_CHECK(offsetof(__sanitizer_ifaddrs, ifa_dstaddr) == offsetof(ifaddrs, ifa_ifu)); -# endif // SANITIZER_FREEBSD +# endif // SANITIZER_FREEBSD #else CHECK_SIZE_AND_OFFSET(ifaddrs, ifa_dstaddr); -#endif // SANITIZER_LINUX +#endif // SANITIZER_LINUX CHECK_SIZE_AND_OFFSET(ifaddrs, ifa_data); #endif @@ -1221,4 +1264,12 @@ CHECK_SIZE_AND_OFFSET(cookie_io_functions_t, seek); CHECK_SIZE_AND_OFFSET(cookie_io_functions_t, close); #endif -#endif // SANITIZER_LINUX || SANITIZER_FREEBSD || SANITIZER_MAC +#if SANITIZER_LINUX || SANITIZER_FREEBSD +CHECK_TYPE_SIZE(sem_t); +#endif + +#if SANITIZER_LINUX && defined(__arm__) +COMPILER_CHECK(ARM_VFPREGS_SIZE == ARM_VFPREGS_SIZE_ASAN); +#endif + +#endif // SANITIZER_LINUX || SANITIZER_FREEBSD || SANITIZER_MAC diff --git a/contrib/compiler-rt/lib/sanitizer_common/sanitizer_platform_limits_posix.h b/contrib/compiler-rt/lib/sanitizer_common/sanitizer_platform_limits_posix.h index ea95fb6624da..d7ef8495d16a 100644 --- a/contrib/compiler-rt/lib/sanitizer_common/sanitizer_platform_limits_posix.h +++ b/contrib/compiler-rt/lib/sanitizer_common/sanitizer_platform_limits_posix.h @@ -154,6 +154,18 @@ namespace __sanitizer { }; const unsigned old_sigset_t_sz = sizeof(unsigned long); + + struct __sanitizer_sem_t { +#if SANITIZER_ANDROID && defined(_LP64) + int data[4]; +#elif SANITIZER_ANDROID && !defined(_LP64) + int data; +#elif SANITIZER_LINUX + uptr data[4]; +#elif SANITIZER_FREEBSD + u32 data[4]; +#endif + }; #endif // SANITIZER_LINUX || SANITIZER_FREEBSD #if SANITIZER_ANDROID @@ -613,6 +625,8 @@ namespace __sanitizer { const void *dlpi_phdr; short dlpi_phnum; }; + + extern unsigned struct_ElfW_Phdr_sz; #endif struct __sanitizer_addrinfo { @@ -726,10 +740,11 @@ namespace __sanitizer { #if SANITIZER_LINUX && !SANITIZER_ANDROID && \ (defined(__i386) || defined(__x86_64) || defined(__mips64) || \ - defined(__powerpc64__)) + defined(__powerpc64__) || defined(__aarch64__) || defined(__arm__)) extern unsigned struct_user_regs_struct_sz; extern unsigned struct_user_fpregs_struct_sz; extern unsigned struct_user_fpxregs_struct_sz; + extern unsigned struct_user_vfpregs_struct_sz; extern int ptrace_peektext; extern int ptrace_peekdata; @@ -740,6 +755,8 @@ namespace __sanitizer { extern int ptrace_setfpregs; extern int ptrace_getfpxregs; extern int ptrace_setfpxregs; + extern int ptrace_getvfpregs; + extern int ptrace_setvfpregs; extern int ptrace_getsiginfo; extern int ptrace_setsiginfo; extern int ptrace_getregset; diff --git a/contrib/compiler-rt/lib/sanitizer_common/sanitizer_posix.cc b/contrib/compiler-rt/lib/sanitizer_common/sanitizer_posix.cc index abf6738a8daa..5ae68663df0e 100644 --- a/contrib/compiler-rt/lib/sanitizer_common/sanitizer_posix.cc +++ b/contrib/compiler-rt/lib/sanitizer_common/sanitizer_posix.cc @@ -13,6 +13,7 @@ //===----------------------------------------------------------------------===// #include "sanitizer_platform.h" + #if SANITIZER_POSIX #include "sanitizer_common.h" @@ -57,8 +58,8 @@ static uptr GetKernelAreaSize() { // mapped to top gigabyte (e.g. stack). MemoryMappingLayout proc_maps(/*cache_enabled*/true); uptr end, prot; - while (proc_maps.Next(/*start*/0, &end, - /*offset*/0, /*filename*/0, + while (proc_maps.Next(/*start*/nullptr, &end, + /*offset*/nullptr, /*filename*/nullptr, /*filename_size*/0, &prot)) { if ((end >= 3 * gbyte) && (prot & MemoryMappingLayout::kProtectionWrite) != 0) @@ -111,27 +112,14 @@ uptr GetMaxVirtualAddress() { #endif // SANITIZER_WORDSIZE } -void *MmapOrDie(uptr size, const char *mem_type) { +void *MmapOrDie(uptr size, const char *mem_type, bool raw_report) { size = RoundUpTo(size, GetPageSizeCached()); - uptr res = internal_mmap(0, size, - PROT_READ | PROT_WRITE, - MAP_PRIVATE | MAP_ANON, -1, 0); + uptr res = internal_mmap(nullptr, size, + PROT_READ | PROT_WRITE, + MAP_PRIVATE | MAP_ANON, -1, 0); int reserrno; - if (internal_iserror(res, &reserrno)) { - static int recursion_count; - if (recursion_count) { - // The Report() and CHECK calls below may call mmap recursively and fail. - // If we went into recursion, just die. - RawWrite("ERROR: Failed to mmap\n"); - Die(); - } - recursion_count++; - Report("ERROR: %s failed to " - "allocate 0x%zx (%zd) bytes of %s (errno: %d)\n", - SanitizerToolName, size, size, mem_type, reserrno); - DumpProcessMap(); - CHECK("unable to mmap" && 0); - } + if (internal_iserror(res, &reserrno)) + ReportMmapFailureAndDie(size, mem_type, "allocate", reserrno, raw_report); IncreaseTotalMmap(size); return (void *)res; } @@ -149,18 +137,14 @@ void UnmapOrDie(void *addr, uptr size) { void *MmapNoReserveOrDie(uptr size, const char *mem_type) { uptr PageSize = GetPageSizeCached(); - uptr p = internal_mmap(0, - RoundUpTo(size, PageSize), - PROT_READ | PROT_WRITE, - MAP_PRIVATE | MAP_ANON | MAP_NORESERVE, - -1, 0); + uptr p = internal_mmap(nullptr, + RoundUpTo(size, PageSize), + PROT_READ | PROT_WRITE, + MAP_PRIVATE | MAP_ANON | MAP_NORESERVE, + -1, 0); int reserrno; - if (internal_iserror(p, &reserrno)) { - Report("ERROR: %s failed to " - "allocate noreserve 0x%zx (%zd) bytes for '%s' (errno: %d)\n", - SanitizerToolName, size, size, mem_type, reserrno); - CHECK("unable to mmap" && 0); - } + if (internal_iserror(p, &reserrno)) + ReportMmapFailureAndDie(size, mem_type, "allocate noreserve", reserrno); IncreaseTotalMmap(size); return (void *)p; } @@ -174,10 +158,10 @@ void *MmapFixedOrDie(uptr fixed_addr, uptr size) { -1, 0); int reserrno; if (internal_iserror(p, &reserrno)) { - Report("ERROR: %s failed to " - "allocate 0x%zx (%zd) bytes at address %zx (errno: %d)\n", - SanitizerToolName, size, size, fixed_addr, reserrno); - CHECK("unable to mmap" && 0); + char mem_type[30]; + internal_snprintf(mem_type, sizeof(mem_type), "memory at address 0x%zx", + fixed_addr); + ReportMmapFailureAndDie(size, mem_type, "allocate", reserrno); } IncreaseTotalMmap(size); return (void *)p; @@ -236,8 +220,8 @@ void *MapFileToMemory(const char *file_name, uptr *buff_size) { CHECK_NE(fsize, (uptr)-1); CHECK_GT(fsize, 0); *buff_size = RoundUpTo(fsize, GetPageSizeCached()); - uptr map = internal_mmap(0, *buff_size, PROT_READ, MAP_PRIVATE, fd, 0); - return internal_iserror(map) ? 0 : (void *)map; + uptr map = internal_mmap(nullptr, *buff_size, PROT_READ, MAP_PRIVATE, fd, 0); + return internal_iserror(map) ? nullptr : (void *)map; } void *MapWritableFileToMemory(void *addr, uptr size, fd_t fd, OFF_T offset) { @@ -248,7 +232,7 @@ void *MapWritableFileToMemory(void *addr, uptr size, fd_t fd, OFF_T offset) { if (internal_iserror(p, &mmap_errno)) { Printf("could not map writable file (%d, %lld, %zu): %zd, errno: %d\n", fd, (long long)offset, size, p, mmap_errno); - return 0; + return nullptr; } return (void *)p; } @@ -268,8 +252,8 @@ bool MemoryRangeIsAvailable(uptr range_start, uptr range_end) { MemoryMappingLayout proc_maps(/*cache_enabled*/true); uptr start, end; while (proc_maps.Next(&start, &end, - /*offset*/0, /*filename*/0, /*filename_size*/0, - /*protection*/0)) { + /*offset*/nullptr, /*filename*/nullptr, + /*filename_size*/0, /*protection*/nullptr)) { if (start == end) continue; // Empty range. CHECK_NE(0, end); if (!IntervalsAreSeparate(start, end - 1, range_start, range_end)) @@ -284,8 +268,8 @@ void DumpProcessMap() { const sptr kBufSize = 4095; char *filename = (char*)MmapOrDie(kBufSize, __func__); Report("Process memory map follows:\n"); - while (proc_maps.Next(&start, &end, /* file_offset */0, - filename, kBufSize, /* protection */0)) { + while (proc_maps.Next(&start, &end, /* file_offset */nullptr, + filename, kBufSize, /* protection */nullptr)) { Printf("\t%p-%p\t%s\n", (void*)start, (void*)end, filename); } Report("End of process memory map.\n"); @@ -296,30 +280,6 @@ const char *GetPwd() { return GetEnv("PWD"); } -char *FindPathToBinary(const char *name) { - const char *path = GetEnv("PATH"); - if (!path) - return 0; - uptr name_len = internal_strlen(name); - InternalScopedBuffer<char> buffer(kMaxPathLength); - const char *beg = path; - while (true) { - const char *end = internal_strchrnul(beg, ':'); - uptr prefix_len = end - beg; - if (prefix_len + name_len + 2 <= kMaxPathLength) { - internal_memcpy(buffer.data(), beg, prefix_len); - buffer[prefix_len] = '/'; - internal_memcpy(&buffer[prefix_len + 1], name, name_len); - buffer[prefix_len + 1 + name_len] = '\0'; - if (FileExists(buffer.data())) - return internal_strdup(buffer.data()); - } - if (*end == '\0') break; - beg = end + 1; - } - return 0; -} - bool IsPathSeparator(const char c) { return c == '/'; } @@ -361,6 +321,6 @@ SignalContext SignalContext::Create(void *siginfo, void *context) { return SignalContext(context, addr, pc, sp, bp); } -} // namespace __sanitizer +} // namespace __sanitizer -#endif // SANITIZER_POSIX +#endif // SANITIZER_POSIX diff --git a/contrib/compiler-rt/lib/sanitizer_common/sanitizer_posix.h b/contrib/compiler-rt/lib/sanitizer_common/sanitizer_posix.h index 5a9e97d5b5a9..c0426a0b23fa 100644 --- a/contrib/compiler-rt/lib/sanitizer_common/sanitizer_posix.h +++ b/contrib/compiler-rt/lib/sanitizer_common/sanitizer_posix.h @@ -54,6 +54,7 @@ uptr internal_ptrace(int request, int pid, void *addr, void *data); uptr internal_waitpid(int pid, int *status, int options); int internal_fork(); +int internal_forkpty(int *amaster); // These functions call appropriate pthread_ functions directly, bypassing // the interceptor. They are weak and may not be present in some tools. @@ -74,6 +75,8 @@ int real_pthread_join(void *th, void **ret); } \ } // namespace __sanitizer +int my_pthread_attr_getstack(void *attr, void **addr, uptr *size); + int internal_sigaction(int signum, const void *act, void *oldact); } // namespace __sanitizer diff --git a/contrib/compiler-rt/lib/sanitizer_common/sanitizer_posix_libcdep.cc b/contrib/compiler-rt/lib/sanitizer_common/sanitizer_posix_libcdep.cc index 3f0a4f453cb4..c158eedae0e3 100644 --- a/contrib/compiler-rt/lib/sanitizer_common/sanitizer_posix_libcdep.cc +++ b/contrib/compiler-rt/lib/sanitizer_common/sanitizer_posix_libcdep.cc @@ -15,6 +15,7 @@ #include "sanitizer_platform.h" #if SANITIZER_POSIX + #include "sanitizer_common.h" #include "sanitizer_flags.h" #include "sanitizer_platform_limits_posix.h" @@ -30,9 +31,9 @@ #include <stdlib.h> #include <sys/mman.h> #include <sys/resource.h> +#include <sys/stat.h> #include <sys/time.h> #include <sys/types.h> -#include <sys/stat.h> #include <unistd.h> #if SANITIZER_FREEBSD @@ -141,7 +142,7 @@ static const uptr kAltStackSize = SIGSTKSZ * 4; // SIGSTKSZ is not enough. void SetAlternateSignalStack() { stack_t altstack, oldstack; - CHECK_EQ(0, sigaltstack(0, &oldstack)); + CHECK_EQ(0, sigaltstack(nullptr, &oldstack)); // If the alternate stack is already in place, do nothing. // Android always sets an alternate stack, but it's too small for us. if (!SANITIZER_ANDROID && !(oldstack.ss_flags & SS_DISABLE)) return; @@ -152,12 +153,12 @@ void SetAlternateSignalStack() { altstack.ss_sp = (char*) base; altstack.ss_flags = 0; altstack.ss_size = kAltStackSize; - CHECK_EQ(0, sigaltstack(&altstack, 0)); + CHECK_EQ(0, sigaltstack(&altstack, nullptr)); } void UnsetAlternateSignalStack() { stack_t altstack, oldstack; - altstack.ss_sp = 0; + altstack.ss_sp = nullptr; altstack.ss_flags = SS_DISABLE; altstack.ss_size = kAltStackSize; // Some sane value required on Darwin. CHECK_EQ(0, sigaltstack(&altstack, &oldstack)); @@ -176,7 +177,7 @@ static void MaybeInstallSigaction(int signum, // Clients are responsible for handling this correctly. sigact.sa_flags = SA_SIGINFO | SA_NODEFER; if (common_flags()->use_sigaltstack) sigact.sa_flags |= SA_ONSTACK; - CHECK_EQ(0, internal_sigaction(signum, &sigact, 0)); + CHECK_EQ(0, internal_sigaction(signum, &sigact, nullptr)); VReport(1, "Installed the sigaction for signal %d\n", signum); } @@ -188,6 +189,8 @@ void InstallDeadlySignalHandlers(SignalHandlerType handler) { MaybeInstallSigaction(SIGSEGV, handler); MaybeInstallSigaction(SIGBUS, handler); MaybeInstallSigaction(SIGABRT, handler); + MaybeInstallSigaction(SIGFPE, handler); + MaybeInstallSigaction(SIGILL, handler); } #endif // SANITIZER_GO @@ -226,7 +229,7 @@ void PrepareForSandboxing(__sanitizer_sandbox_arguments *args) { #endif } -#if SANITIZER_ANDROID +#if SANITIZER_ANDROID || SANITIZER_GO int GetNamedMappingFd(const char *name, uptr size) { return -1; } @@ -275,6 +278,48 @@ void *MmapNoAccess(uptr fixed_addr, uptr size, const char *name) { 0); } -} // namespace __sanitizer +// This function is defined elsewhere if we intercepted pthread_attr_getstack. +extern "C" { +SANITIZER_WEAK_ATTRIBUTE int +real_pthread_attr_getstack(void *attr, void **addr, size_t *size); +} // extern "C" + +int my_pthread_attr_getstack(void *attr, void **addr, uptr *size) { +#if !SANITIZER_GO && !SANITIZER_MAC + if (&real_pthread_attr_getstack) + return real_pthread_attr_getstack((pthread_attr_t *)attr, addr, + (size_t *)size); +#endif + return pthread_attr_getstack((pthread_attr_t *)attr, addr, (size_t *)size); +} + +#if !SANITIZER_GO +void AdjustStackSize(void *attr_) { + pthread_attr_t *attr = (pthread_attr_t *)attr_; + uptr stackaddr = 0; + uptr stacksize = 0; + my_pthread_attr_getstack(attr, (void**)&stackaddr, &stacksize); + // GLibC will return (0 - stacksize) as the stack address in the case when + // stacksize is set, but stackaddr is not. + bool stack_set = (stackaddr != 0) && (stackaddr + stacksize != 0); + // We place a lot of tool data into TLS, account for that. + const uptr minstacksize = GetTlsSize() + 128*1024; + if (stacksize < minstacksize) { + if (!stack_set) { + if (stacksize != 0) { + VPrintf(1, "Sanitizer: increasing stacksize %zu->%zu\n", stacksize, + minstacksize); + pthread_attr_setstacksize(attr, minstacksize); + } + } else { + Printf("Sanitizer: pre-allocated stack size is insufficient: " + "%zu < %zu\n", stacksize, minstacksize); + Printf("Sanitizer: pthread_create is likely to fail.\n"); + } + } +} +#endif // !SANITIZER_GO + +} // namespace __sanitizer -#endif // SANITIZER_POSIX +#endif // SANITIZER_POSIX diff --git a/contrib/compiler-rt/lib/sanitizer_common/sanitizer_printf.cc b/contrib/compiler-rt/lib/sanitizer_common/sanitizer_printf.cc index e4f67f5e0db5..434ebb93dffa 100644 --- a/contrib/compiler-rt/lib/sanitizer_common/sanitizer_printf.cc +++ b/contrib/compiler-rt/lib/sanitizer_common/sanitizer_printf.cc @@ -14,7 +14,6 @@ // inside it. //===----------------------------------------------------------------------===// - #include "sanitizer_common.h" #include "sanitizer_flags.h" #include "sanitizer_libc.h" @@ -98,7 +97,7 @@ static int AppendSignedDecimal(char **buff, const char *buff_end, s64 num, static int AppendString(char **buff, const char *buff_end, int precision, const char *s) { - if (s == 0) + if (!s) s = "<null>"; int result = 0; for (; *s; s++) { @@ -260,7 +259,7 @@ static void SharedPrintfCode(bool append_pid, const char *format, } if (append_pid) { int pid = internal_getpid(); - const char *exe_name = GetBinaryBasename(); + const char *exe_name = GetProcessName(); if (common_flags()->log_exe_name && exe_name) { needed_length += internal_snprintf(buffer, buffer_size, "==%s", exe_name); @@ -279,8 +278,12 @@ static void SharedPrintfCode(bool append_pid, const char *format, # undef CHECK_NEEDED_LENGTH } RawWrite(buffer); - AndroidLogWrite(buffer); + + // Remove color sequences from the message. + RemoveANSIEscapeSequencesFromString(buffer); CallPrintfAndReportCallback(buffer); + LogMessageOnPrintf(buffer); + // If we had mapped any memory, clean up. if (buffer != local_buffer) UnmapOrDie((void *)buffer, buffer_size); @@ -328,4 +331,4 @@ void InternalScopedString::append(const char *format, ...) { CHECK_LT(length_, size()); } -} // namespace __sanitizer +} // namespace __sanitizer diff --git a/contrib/compiler-rt/lib/sanitizer_common/sanitizer_procmaps_common.cc b/contrib/compiler-rt/lib/sanitizer_common/sanitizer_procmaps_common.cc index 2c6ce8e2b211..d43432cae909 100644 --- a/contrib/compiler-rt/lib/sanitizer_common/sanitizer_procmaps_common.cc +++ b/contrib/compiler-rt/lib/sanitizer_common/sanitizer_procmaps_common.cc @@ -11,7 +11,9 @@ //===----------------------------------------------------------------------===// #include "sanitizer_platform.h" + #if SANITIZER_FREEBSD || SANITIZER_LINUX + #include "sanitizer_common.h" #include "sanitizer_placement_new.h" #include "sanitizer_procmaps.h" @@ -151,10 +153,11 @@ uptr MemoryMappingLayout::DumpListOfModules(LoadedModule *modules, } void GetMemoryProfile(fill_profile_f cb, uptr *stats, uptr stats_size) { - char *smaps = 0; + char *smaps = nullptr; uptr smaps_cap = 0; - uptr smaps_len = ReadFileToBuffer("/proc/self/smaps", - &smaps, &smaps_cap, 64<<20); + uptr smaps_len = 0; + if (!ReadFileToBuffer("/proc/self/smaps", &smaps, &smaps_cap, &smaps_len)) + return; uptr start = 0; bool file = false; const char *pos = smaps; @@ -173,6 +176,6 @@ void GetMemoryProfile(fill_profile_f cb, uptr *stats, uptr stats_size) { UnmapOrDie(smaps, smaps_cap); } -} // namespace __sanitizer +} // namespace __sanitizer -#endif // SANITIZER_FREEBSD || SANITIZER_LINUX +#endif // SANITIZER_FREEBSD || SANITIZER_LINUX diff --git a/contrib/compiler-rt/lib/sanitizer_common/sanitizer_procmaps_linux.cc b/contrib/compiler-rt/lib/sanitizer_common/sanitizer_procmaps_linux.cc index 79ca4dfd8fca..b6fb7034ded4 100644 --- a/contrib/compiler-rt/lib/sanitizer_common/sanitizer_procmaps_linux.cc +++ b/contrib/compiler-rt/lib/sanitizer_common/sanitizer_procmaps_linux.cc @@ -18,8 +18,8 @@ namespace __sanitizer { void ReadProcMaps(ProcSelfMapsBuff *proc_maps) { - proc_maps->len = ReadFileToBuffer("/proc/self/maps", &proc_maps->data, - &proc_maps->mmaped_size, 1 << 26); + CHECK(ReadFileToBuffer("/proc/self/maps", &proc_maps->data, + &proc_maps->mmaped_size, &proc_maps->len)); } static bool IsOneOf(char c, char c1, char c2) { @@ -67,7 +67,7 @@ bool MemoryMappingLayout::Next(uptr *start, uptr *end, uptr *offset, while (IsDecimal(*current_)) current_++; // Qemu may lack the trailing space. - // http://code.google.com/p/address-sanitizer/issues/detail?id=160 + // https://github.com/google/sanitizers/issues/160 // CHECK_EQ(*current_++, ' '); // Skip spaces. while (current_ < next_line && *current_ == ' ') diff --git a/contrib/compiler-rt/lib/sanitizer_common/sanitizer_procmaps_mac.cc b/contrib/compiler-rt/lib/sanitizer_common/sanitizer_procmaps_mac.cc index 29d699609a6e..d10881e8a7f8 100644 --- a/contrib/compiler-rt/lib/sanitizer_common/sanitizer_procmaps_mac.cc +++ b/contrib/compiler-rt/lib/sanitizer_common/sanitizer_procmaps_mac.cc @@ -65,7 +65,7 @@ void MemoryMappingLayout::LoadFromCache() { } // Next and NextSegmentLoad were inspired by base/sysinfo.cc in -// Google Perftools, http://code.google.com/p/google-perftools. +// Google Perftools, https://github.com/gperftools/gperftools. // NextSegmentLoad scans the current image for the next segment load command // and returns the start and end addresses and file offset of the corresponding diff --git a/contrib/compiler-rt/lib/sanitizer_common/sanitizer_quarantine.h b/contrib/compiler-rt/lib/sanitizer_common/sanitizer_quarantine.h index 404d3753f7e9..9e0bf2d18fec 100644 --- a/contrib/compiler-rt/lib/sanitizer_common/sanitizer_quarantine.h +++ b/contrib/compiler-rt/lib/sanitizer_common/sanitizer_quarantine.h @@ -153,7 +153,7 @@ class QuarantineCache { QuarantineBatch *DequeueBatch() { if (list_.empty()) - return 0; + return nullptr; QuarantineBatch *b = list_.front(); list_.pop_front(); SizeSub(b->size); @@ -180,6 +180,6 @@ class QuarantineCache { return b; } }; -} // namespace __sanitizer +} // namespace __sanitizer -#endif // #ifndef SANITIZER_QUARANTINE_H +#endif // SANITIZER_QUARANTINE_H diff --git a/contrib/compiler-rt/lib/sanitizer_common/sanitizer_stackdepot.cc b/contrib/compiler-rt/lib/sanitizer_common/sanitizer_stackdepot.cc index 59b53f4dcd84..985193d1ed5e 100644 --- a/contrib/compiler-rt/lib/sanitizer_common/sanitizer_stackdepot.cc +++ b/contrib/compiler-rt/lib/sanitizer_common/sanitizer_stackdepot.cc @@ -152,7 +152,7 @@ StackDepotReverseMap::StackDepotReverseMap() StackTrace StackDepotReverseMap::Get(u32 id) { if (!map_.size()) return StackTrace(); - IdDescPair pair = {id, 0}; + IdDescPair pair = {id, nullptr}; uptr idx = InternalBinarySearch(map_, 0, map_.size(), pair, IdDescPair::IdComparator); if (idx > map_.size()) @@ -160,4 +160,4 @@ StackTrace StackDepotReverseMap::Get(u32 id) { return map_[idx].desc->load(); } -} // namespace __sanitizer +} // namespace __sanitizer diff --git a/contrib/compiler-rt/lib/sanitizer_common/sanitizer_stackdepot.h b/contrib/compiler-rt/lib/sanitizer_common/sanitizer_stackdepot.h index 5e3a8b783ac7..cb7345002a40 100644 --- a/contrib/compiler-rt/lib/sanitizer_common/sanitizer_stackdepot.h +++ b/contrib/compiler-rt/lib/sanitizer_common/sanitizer_stackdepot.h @@ -10,6 +10,7 @@ // This file is shared between AddressSanitizer and ThreadSanitizer // run-time libraries. //===----------------------------------------------------------------------===// + #ifndef SANITIZER_STACKDEPOT_H #define SANITIZER_STACKDEPOT_H @@ -23,7 +24,7 @@ namespace __sanitizer { struct StackDepotNode; struct StackDepotHandle { StackDepotNode *node_; - StackDepotHandle() : node_(0) {} + StackDepotHandle() : node_(nullptr) {} explicit StackDepotHandle(StackDepotNode *node) : node_(node) {} bool valid() { return node_; } u32 id(); @@ -66,6 +67,6 @@ class StackDepotReverseMap { void operator=(const StackDepotReverseMap&); }; -} // namespace __sanitizer +} // namespace __sanitizer -#endif // SANITIZER_STACKDEPOT_H +#endif // SANITIZER_STACKDEPOT_H diff --git a/contrib/compiler-rt/lib/sanitizer_common/sanitizer_stackdepotbase.h b/contrib/compiler-rt/lib/sanitizer_common/sanitizer_stackdepotbase.h index 5de2e711fe45..4ec77b467b1f 100644 --- a/contrib/compiler-rt/lib/sanitizer_common/sanitizer_stackdepotbase.h +++ b/contrib/compiler-rt/lib/sanitizer_common/sanitizer_stackdepotbase.h @@ -10,6 +10,7 @@ // Implementation of a mapping from arbitrary values to unique 32-bit // identifiers. //===----------------------------------------------------------------------===// + #ifndef SANITIZER_STACKDEPOTBASE_H #define SANITIZER_STACKDEPOTBASE_H @@ -26,7 +27,7 @@ class StackDepotBase { typedef typename Node::args_type args_type; typedef typename Node::handle_type handle_type; // Maps stack trace to an unique id. - handle_type Put(args_type args, bool *inserted = 0); + handle_type Put(args_type args, bool *inserted = nullptr); // Retrieves a stored stack trace by the id. args_type Get(u32 id); @@ -66,7 +67,7 @@ Node *StackDepotBase<Node, kReservedBits, kTabSizeLog>::find(Node *s, return s; } } - return 0; + return nullptr; } template <class Node, int kReservedBits, int kTabSizeLog> @@ -172,5 +173,6 @@ void StackDepotBase<Node, kReservedBits, kTabSizeLog>::UnlockAll() { } } -} // namespace __sanitizer -#endif // SANITIZER_STACKDEPOTBASE_H +} // namespace __sanitizer + +#endif // SANITIZER_STACKDEPOTBASE_H diff --git a/contrib/compiler-rt/lib/sanitizer_common/sanitizer_stacktrace.cc b/contrib/compiler-rt/lib/sanitizer_common/sanitizer_stacktrace.cc index 84ff9d9d9e3b..7862575b37bb 100644 --- a/contrib/compiler-rt/lib/sanitizer_common/sanitizer_stacktrace.cc +++ b/contrib/compiler-rt/lib/sanitizer_common/sanitizer_stacktrace.cc @@ -83,7 +83,18 @@ void BufferedStackTrace::FastUnwindStack(uptr pc, uptr bp, uptr stack_top, while (IsValidFrame((uptr)frame, stack_top, bottom) && IsAligned((uptr)frame, sizeof(*frame)) && size < max_depth) { +#ifdef __powerpc__ + // PowerPC ABIs specify that the return address is saved at offset + // 16 of the *caller's* stack frame. Thus we must dereference the + // back chain to find the caller frame before extracting it. + uhwptr *caller_frame = (uhwptr*)frame[0]; + if (!IsValidFrame((uptr)caller_frame, stack_top, bottom) || + !IsAligned((uptr)caller_frame, sizeof(uhwptr))) + break; + uhwptr pc1 = caller_frame[2]; +#else uhwptr pc1 = frame[1]; +#endif if (pc1 != pc) { trace_buffer[size++] = (uptr) pc1; } @@ -107,7 +118,7 @@ void BufferedStackTrace::PopStackFrames(uptr count) { uptr BufferedStackTrace::LocatePcInTrace(uptr pc) { // Use threshold to find PC in stack trace, as PC we want to unwind from may // slightly differ from return address in the actual unwinded stack trace. - const int kPcThreshold = 304; + const int kPcThreshold = 320; for (uptr i = 0; i < size; ++i) { if (MatchPc(pc, trace[i], kPcThreshold)) return i; diff --git a/contrib/compiler-rt/lib/sanitizer_common/sanitizer_stacktrace.h b/contrib/compiler-rt/lib/sanitizer_common/sanitizer_stacktrace.h index 6c3a1511f337..969cedb165c8 100644 --- a/contrib/compiler-rt/lib/sanitizer_common/sanitizer_stacktrace.h +++ b/contrib/compiler-rt/lib/sanitizer_common/sanitizer_stacktrace.h @@ -19,9 +19,7 @@ namespace __sanitizer { static const u32 kStackTraceMax = 256; -#if SANITIZER_LINUX && (defined(__aarch64__) || defined(__powerpc__) || \ - defined(__powerpc64__) || defined(__sparc__) || \ - defined(__mips__)) +#if SANITIZER_LINUX && (defined(__sparc__) || defined(__mips__)) # define SANITIZER_CAN_FAST_UNWIND 0 #elif SANITIZER_WINDOWS # define SANITIZER_CAN_FAST_UNWIND 0 @@ -31,7 +29,7 @@ static const u32 kStackTraceMax = 256; // Fast unwind is the only option on Mac for now; we will need to // revisit this macro when slow unwind works on Mac, see -// https://code.google.com/p/address-sanitizer/issues/detail?id=137 +// https://github.com/google/sanitizers/issues/137 #if SANITIZER_MAC # define SANITIZER_CAN_SLOW_UNWIND 0 #else diff --git a/contrib/compiler-rt/lib/sanitizer_common/sanitizer_stacktrace_printer.cc b/contrib/compiler-rt/lib/sanitizer_common/sanitizer_stacktrace_printer.cc index 3574fa3782c6..669b0ba28265 100644 --- a/contrib/compiler-rt/lib/sanitizer_common/sanitizer_stacktrace_printer.cc +++ b/contrib/compiler-rt/lib/sanitizer_common/sanitizer_stacktrace_printer.cc @@ -10,13 +10,14 @@ // This file is shared between sanitizers' run-time libraries. // //===----------------------------------------------------------------------===// + #include "sanitizer_stacktrace_printer.h" namespace __sanitizer { static const char *StripFunctionName(const char *function, const char *prefix) { - if (function == 0) return 0; - if (prefix == 0) return function; + if (!function) return nullptr; + if (!prefix) return function; uptr prefix_len = internal_strlen(prefix); if (0 == internal_strncmp(function, prefix, prefix_len)) return function + prefix_len; @@ -140,4 +141,4 @@ void RenderModuleLocation(InternalScopedString *buffer, const char *module, offset); } -} // namespace __sanitizer +} // namespace __sanitizer diff --git a/contrib/compiler-rt/lib/sanitizer_common/sanitizer_stoptheworld_linux_libcdep.cc b/contrib/compiler-rt/lib/sanitizer_common/sanitizer_stoptheworld_linux_libcdep.cc index 47b27e7e5c75..2376ee56e1d7 100644 --- a/contrib/compiler-rt/lib/sanitizer_common/sanitizer_stoptheworld_linux_libcdep.cc +++ b/contrib/compiler-rt/lib/sanitizer_common/sanitizer_stoptheworld_linux_libcdep.cc @@ -12,9 +12,10 @@ // //===----------------------------------------------------------------------===// - #include "sanitizer_platform.h" -#if SANITIZER_LINUX && (defined(__x86_64__) || defined(__mips__)) + +#if SANITIZER_LINUX && (defined(__x86_64__) || defined(__mips__) || \ + defined(__aarch64__) || defined(__powerpc64__)) #include "sanitizer_stoptheworld.h" @@ -27,9 +28,15 @@ #include <sys/prctl.h> // for PR_* definitions #include <sys/ptrace.h> // for PTRACE_* definitions #include <sys/types.h> // for pid_t +#include <sys/uio.h> // for iovec +#include <elf.h> // for NT_PRSTATUS #if SANITIZER_ANDROID && defined(__arm__) # include <linux/user.h> // for pt_regs #else +# ifdef __aarch64__ +// GLIBC 2.20+ sys/user does not include asm/ptrace.h +# include <asm/ptrace.h> +# endif # include <sys/user.h> // for user_regs_struct #endif #include <sys/wait.h> // for signal-related stuff @@ -112,7 +119,7 @@ bool ThreadSuspender::SuspendThread(SuspendedThreadID tid) { if (suspended_threads_list_.Contains(tid)) return false; int pterrno; - if (internal_iserror(internal_ptrace(PTRACE_ATTACH, tid, NULL, NULL), + if (internal_iserror(internal_ptrace(PTRACE_ATTACH, tid, nullptr, nullptr), &pterrno)) { // Either the thread is dead, or something prevented us from attaching. // Log this event and move on. @@ -139,11 +146,12 @@ bool ThreadSuspender::SuspendThread(SuspendedThreadID tid) { // doesn't hurt to report it. VReport(1, "Waiting on thread %d failed, detaching (errno %d).\n", tid, wperrno); - internal_ptrace(PTRACE_DETACH, tid, NULL, NULL); + internal_ptrace(PTRACE_DETACH, tid, nullptr, nullptr); return false; } if (WIFSTOPPED(status) && WSTOPSIG(status) != SIGSTOP) { - internal_ptrace(PTRACE_CONT, tid, 0, (void*)(uptr)WSTOPSIG(status)); + internal_ptrace(PTRACE_CONT, tid, nullptr, + (void*)(uptr)WSTOPSIG(status)); continue; } break; @@ -157,7 +165,7 @@ void ThreadSuspender::ResumeAllThreads() { for (uptr i = 0; i < suspended_threads_list_.thread_count(); i++) { pid_t tid = suspended_threads_list_.GetThreadID(i); int pterrno; - if (!internal_iserror(internal_ptrace(PTRACE_DETACH, tid, NULL, NULL), + if (!internal_iserror(internal_ptrace(PTRACE_DETACH, tid, nullptr, nullptr), &pterrno)) { VReport(2, "Detached from thread %d.\n", tid); } else { @@ -172,7 +180,7 @@ void ThreadSuspender::ResumeAllThreads() { void ThreadSuspender::KillAllThreads() { for (uptr i = 0; i < suspended_threads_list_.thread_count(); i++) internal_ptrace(PTRACE_KILL, suspended_threads_list_.GetThreadID(i), - NULL, NULL); + nullptr, nullptr); } bool ThreadSuspender::SuspendAllThreads() { @@ -198,13 +206,25 @@ bool ThreadSuspender::SuspendAllThreads() { } // Pointer to the ThreadSuspender instance for use in signal handler. -static ThreadSuspender *thread_suspender_instance = NULL; +static ThreadSuspender *thread_suspender_instance = nullptr; // Synchronous signals that should not be blocked. static const int kSyncSignals[] = { SIGABRT, SIGILL, SIGFPE, SIGSEGV, SIGBUS, SIGXCPU, SIGXFSZ }; -static DieCallbackType old_die_callback; +static void TracerThreadDieCallback() { + // Generally a call to Die() in the tracer thread should be fatal to the + // parent process as well, because they share the address space. + // This really only works correctly if all the threads are suspended at this + // point. So we correctly handle calls to Die() from within the callback, but + // not those that happen before or after the callback. Hopefully there aren't + // a lot of opportunities for that to happen... + ThreadSuspender *inst = thread_suspender_instance; + if (inst && stoptheworld_tracer_pid == internal_getpid()) { + inst->KillAllThreads(); + thread_suspender_instance = nullptr; + } +} // Signal handler to wake up suspended threads when the tracer thread dies. static void TracerThreadSignalHandler(int signum, void *siginfo, void *uctx) { @@ -212,37 +232,18 @@ static void TracerThreadSignalHandler(int signum, void *siginfo, void *uctx) { VPrintf(1, "Tracer caught signal %d: addr=0x%zx pc=0x%zx sp=0x%zx\n", signum, ctx.addr, ctx.pc, ctx.sp); ThreadSuspender *inst = thread_suspender_instance; - if (inst != NULL) { + if (inst) { if (signum == SIGABRT) inst->KillAllThreads(); else inst->ResumeAllThreads(); - SetDieCallback(old_die_callback); - old_die_callback = NULL; - thread_suspender_instance = NULL; + RAW_CHECK(RemoveDieCallback(TracerThreadDieCallback)); + thread_suspender_instance = nullptr; atomic_store(&inst->arg->done, 1, memory_order_relaxed); } internal__exit((signum == SIGABRT) ? 1 : 2); } -static void TracerThreadDieCallback() { - // Generally a call to Die() in the tracer thread should be fatal to the - // parent process as well, because they share the address space. - // This really only works correctly if all the threads are suspended at this - // point. So we correctly handle calls to Die() from within the callback, but - // not those that happen before or after the callback. Hopefully there aren't - // a lot of opportunities for that to happen... - ThreadSuspender *inst = thread_suspender_instance; - if (inst != NULL && stoptheworld_tracer_pid == internal_getpid()) { - inst->KillAllThreads(); - thread_suspender_instance = NULL; - } - if (old_die_callback) - old_die_callback(); - SetDieCallback(old_die_callback); - old_die_callback = NULL; -} - // Size of alternative stack for signal handlers in the tracer thread. static const int kHandlerStackSize = 4096; @@ -260,8 +261,7 @@ static int TracerThread(void* argument) { tracer_thread_argument->mutex.Lock(); tracer_thread_argument->mutex.Unlock(); - old_die_callback = GetDieCallback(); - SetDieCallback(TracerThreadDieCallback); + RAW_CHECK(AddDieCallback(TracerThreadDieCallback)); ThreadSuspender thread_suspender(internal_getppid(), tracer_thread_argument); // Global pointer for the signal handler. @@ -273,7 +273,7 @@ static int TracerThread(void* argument) { internal_memset(&handler_stack, 0, sizeof(handler_stack)); handler_stack.ss_sp = handler_stack_memory.data(); handler_stack.ss_size = kHandlerStackSize; - internal_sigaltstack(&handler_stack, NULL); + internal_sigaltstack(&handler_stack, nullptr); // Install our handler for synchronous signals. Other signals should be // blocked by the mask we inherited from the parent thread. @@ -295,8 +295,8 @@ static int TracerThread(void* argument) { thread_suspender.ResumeAllThreads(); exit_code = 0; } - SetDieCallback(old_die_callback); - thread_suspender_instance = NULL; + RAW_CHECK(RemoveDieCallback(TracerThreadDieCallback)); + thread_suspender_instance = nullptr; atomic_store(&tracer_thread_argument->done, 1, memory_order_relaxed); return exit_code; } @@ -404,8 +404,8 @@ void StopTheWorld(StopTheWorldCallback callback, void *argument) { uptr tracer_pid = internal_clone( TracerThread, tracer_stack.Bottom(), CLONE_VM | CLONE_FS | CLONE_FILES | CLONE_UNTRACED, - &tracer_thread_argument, 0 /* parent_tidptr */, 0 /* newtls */, 0 - /* child_tidptr */); + &tracer_thread_argument, nullptr /* parent_tidptr */, + nullptr /* newtls */, nullptr /* child_tidptr */); internal_sigprocmask(SIG_SETMASK, &old_sigset, 0); int local_errno = 0; if (internal_iserror(tracer_pid, &local_errno)) { @@ -432,7 +432,7 @@ void StopTheWorld(StopTheWorldCallback callback, void *argument) { // Now the tracer thread is about to exit and does not touch errno, // wait for it. for (;;) { - uptr waitpid_status = internal_waitpid(tracer_pid, NULL, __WALL); + uptr waitpid_status = internal_waitpid(tracer_pid, nullptr, __WALL); if (!internal_iserror(waitpid_status, &local_errno)) break; if (local_errno == EINTR) @@ -469,6 +469,11 @@ typedef pt_regs regs_struct; typedef struct user regs_struct; #define REG_SP regs[EF_REG29] +#elif defined(__aarch64__) +typedef struct user_pt_regs regs_struct; +#define REG_SP sp +#define ARCH_IOVEC_FOR_GETREGSET + #else #error "Unsupported architecture" #endif // SANITIZER_ANDROID && defined(__arm__) @@ -479,8 +484,18 @@ int SuspendedThreadsList::GetRegistersAndSP(uptr index, pid_t tid = GetThreadID(index); regs_struct regs; int pterrno; - if (internal_iserror(internal_ptrace(PTRACE_GETREGS, tid, NULL, ®s), - &pterrno)) { +#ifdef ARCH_IOVEC_FOR_GETREGSET + struct iovec regset_io; + regset_io.iov_base = ®s; + regset_io.iov_len = sizeof(regs_struct); + bool isErr = internal_iserror(internal_ptrace(PTRACE_GETREGSET, tid, + (void*)NT_PRSTATUS, (void*)®set_io), + &pterrno); +#else + bool isErr = internal_iserror(internal_ptrace(PTRACE_GETREGS, tid, nullptr, + ®s), &pterrno); +#endif + if (isErr) { VReport(1, "Could not get registers from thread %d (errno %d).\n", tid, pterrno); return -1; @@ -494,6 +509,7 @@ int SuspendedThreadsList::GetRegistersAndSP(uptr index, uptr SuspendedThreadsList::RegisterCount() { return sizeof(regs_struct) / sizeof(uptr); } -} // namespace __sanitizer +} // namespace __sanitizer -#endif // SANITIZER_LINUX && (defined(__x86_64__) || defined(__mips__)) +#endif // SANITIZER_LINUX && (defined(__x86_64__) || defined(__mips__) + // || defined(__aarch64__) || defined(__powerpc64__) diff --git a/contrib/compiler-rt/lib/sanitizer_common/sanitizer_suppressions.cc b/contrib/compiler-rt/lib/sanitizer_common/sanitizer_suppressions.cc index 08cb497269bf..f0f2c9c725a1 100644 --- a/contrib/compiler-rt/lib/sanitizer_common/sanitizer_suppressions.cc +++ b/contrib/compiler-rt/lib/sanitizer_common/sanitizer_suppressions.cc @@ -60,15 +60,13 @@ void SuppressionContext::ParseFromFile(const char *filename) { } // Read the file. - char *file_contents; - uptr buffer_size; - const uptr max_len = 1 << 26; - uptr contents_size = - ReadFileToBuffer(filename, &file_contents, &buffer_size, max_len); VPrintf(1, "%s: reading suppressions file at %s\n", SanitizerToolName, filename); - - if (contents_size == 0) { + char *file_contents; + uptr buffer_size; + uptr contents_size; + if (!ReadFileToBuffer(filename, &file_contents, &buffer_size, + &contents_size)) { Printf("%s: failed to read suppressions file '%s'\n", SanitizerToolName, filename); Die(); @@ -114,7 +112,8 @@ void SuppressionContext::Parse(const char *str) { end = line + internal_strlen(line); if (line != end && line[0] != '#') { const char *end2 = end; - while (line != end2 && (end2[-1] == ' ' || end2[-1] == '\t')) + while (line != end2 && + (end2[-1] == ' ' || end2[-1] == '\t' || end2[-1] == '\r')) end2--; int type; for (type = 0; type < suppression_types_num_; type++) { @@ -133,8 +132,6 @@ void SuppressionContext::Parse(const char *str) { s.templ = (char*)InternalAlloc(end2 - line + 1); internal_memcpy(s.templ, line, end2 - line); s.templ[end2 - line] = 0; - s.hit_count = 0; - s.weight = 0; suppressions_.push_back(s); has_suppression_type_[type] = true; } @@ -164,7 +161,7 @@ const Suppression *SuppressionContext::SuppressionAt(uptr i) const { void SuppressionContext::GetMatched( InternalMmapVector<Suppression *> *matched) { for (uptr i = 0; i < suppressions_.size(); i++) - if (suppressions_[i].hit_count) + if (atomic_load_relaxed(&suppressions_[i].hit_count)) matched->push_back(&suppressions_[i]); } diff --git a/contrib/compiler-rt/lib/sanitizer_common/sanitizer_suppressions.h b/contrib/compiler-rt/lib/sanitizer_common/sanitizer_suppressions.h index 02dbf6f9690b..0ca875a2dde6 100644 --- a/contrib/compiler-rt/lib/sanitizer_common/sanitizer_suppressions.h +++ b/contrib/compiler-rt/lib/sanitizer_common/sanitizer_suppressions.h @@ -14,14 +14,16 @@ #define SANITIZER_SUPPRESSIONS_H #include "sanitizer_common.h" +#include "sanitizer_atomic.h" #include "sanitizer_internal_defs.h" namespace __sanitizer { struct Suppression { + Suppression() { internal_memset(this, 0, sizeof(*this)); } const char *type; char *templ; - unsigned hit_count; + atomic_uint32_t hit_count; uptr weight; }; @@ -41,7 +43,7 @@ class SuppressionContext { void GetMatched(InternalMmapVector<Suppression *> *matched); private: - static const int kMaxSuppressionTypes = 16; + static const int kMaxSuppressionTypes = 32; const char **const suppression_types_; const int suppression_types_num_; diff --git a/contrib/compiler-rt/lib/sanitizer_common/sanitizer_symbolizer_internal.h b/contrib/compiler-rt/lib/sanitizer_common/sanitizer_symbolizer_internal.h index 66ae809ed53e..12c70b602e24 100644 --- a/contrib/compiler-rt/lib/sanitizer_common/sanitizer_symbolizer_internal.h +++ b/contrib/compiler-rt/lib/sanitizer_common/sanitizer_symbolizer_internal.h @@ -74,24 +74,31 @@ class SymbolizerProcess { explicit SymbolizerProcess(const char *path, bool use_forkpty = false); const char *SendCommand(const char *command); - private: - bool Restart(); - const char *SendCommandImpl(const char *command); - bool ReadFromSymbolizer(char *buffer, uptr max_length); - bool WriteToSymbolizer(const char *buffer, uptr length); - bool StartSymbolizerSubprocess(); - + protected: virtual bool ReachedEndOfOutput(const char *buffer, uptr length) const { UNIMPLEMENTED(); } - virtual void ExecuteWithDefaultArgs(const char *path_to_binary) const { + /// The maximum number of arguments required to invoke a tool process. + enum { kArgVMax = 6 }; + + /// Fill in an argv array to invoke the child process. + virtual void GetArgV(const char *path_to_binary, + const char *(&argv)[kArgVMax]) const { UNIMPLEMENTED(); } + virtual bool ReadFromSymbolizer(char *buffer, uptr max_length); + + private: + bool Restart(); + const char *SendCommandImpl(const char *command); + bool WriteToSymbolizer(const char *buffer, uptr length); + bool StartSymbolizerSubprocess(); + const char *path_; - int input_fd_; - int output_fd_; + fd_t input_fd_; + fd_t output_fd_; static const uptr kBufferSize = 16 * 1024; char buffer_[kBufferSize]; @@ -104,6 +111,41 @@ class SymbolizerProcess { bool use_forkpty_; }; +class LLVMSymbolizerProcess; + +// This tool invokes llvm-symbolizer in a subprocess. It should be as portable +// as the llvm-symbolizer tool is. +class LLVMSymbolizer : public SymbolizerTool { + public: + explicit LLVMSymbolizer(const char *path, LowLevelAllocator *allocator); + + bool SymbolizePC(uptr addr, SymbolizedStack *stack) override; + + bool SymbolizeData(uptr addr, DataInfo *info) override; + + private: + const char *SendCommand(bool is_data, const char *module_name, + uptr module_offset); + + LLVMSymbolizerProcess *symbolizer_process_; + static const uptr kBufferSize = 16 * 1024; + char buffer_[kBufferSize]; +}; + +// Parses one or more two-line strings in the following format: +// <function_name> +// <file_name>:<line_number>[:<column_number>] +// Used by LLVMSymbolizer, Addr2LinePool and InternalSymbolizer, since all of +// them use the same output format. Returns true if any useful debug +// information was found. +void ParseSymbolizePCOutput(const char *str, SymbolizedStack *res); + +// Parses a two-line string in the following format: +// <symbol_name> +// <start_address> <size> +// Used by LLVMSymbolizer and InternalSymbolizer. +void ParseSymbolizeDataOutput(const char *str, DataInfo *info); + } // namespace __sanitizer #endif // SANITIZER_SYMBOLIZER_INTERNAL_H diff --git a/contrib/compiler-rt/lib/sanitizer_common/sanitizer_symbolizer_libbacktrace.h b/contrib/compiler-rt/lib/sanitizer_common/sanitizer_symbolizer_libbacktrace.h index 00b465a72774..ddfd475592cb 100644 --- a/contrib/compiler-rt/lib/sanitizer_common/sanitizer_symbolizer_libbacktrace.h +++ b/contrib/compiler-rt/lib/sanitizer_common/sanitizer_symbolizer_libbacktrace.h @@ -16,6 +16,7 @@ #include "sanitizer_platform.h" #include "sanitizer_common.h" +#include "sanitizer_allocator_internal.h" #include "sanitizer_symbolizer_internal.h" #ifndef SANITIZER_LIBBACKTRACE diff --git a/contrib/compiler-rt/lib/sanitizer_common/sanitizer_symbolizer_libcdep.cc b/contrib/compiler-rt/lib/sanitizer_common/sanitizer_symbolizer_libcdep.cc index 160f55d422ca..8c3ad81f952a 100644 --- a/contrib/compiler-rt/lib/sanitizer_common/sanitizer_symbolizer_libcdep.cc +++ b/contrib/compiler-rt/lib/sanitizer_common/sanitizer_symbolizer_libcdep.cc @@ -184,4 +184,245 @@ Symbolizer *Symbolizer::GetOrInit() { return symbolizer_; } +// For now we assume the following protocol: +// For each request of the form +// <module_name> <module_offset> +// passed to STDIN, external symbolizer prints to STDOUT response: +// <function_name> +// <file_name>:<line_number>:<column_number> +// <function_name> +// <file_name>:<line_number>:<column_number> +// ... +// <empty line> +class LLVMSymbolizerProcess : public SymbolizerProcess { + public: + explicit LLVMSymbolizerProcess(const char *path) : SymbolizerProcess(path) {} + + private: + bool ReachedEndOfOutput(const char *buffer, uptr length) const override { + // Empty line marks the end of llvm-symbolizer output. + return length >= 2 && buffer[length - 1] == '\n' && + buffer[length - 2] == '\n'; + } + + void GetArgV(const char *path_to_binary, + const char *(&argv)[kArgVMax]) const override { +#if defined(__x86_64h__) + const char* const kSymbolizerArch = "--default-arch=x86_64h"; +#elif defined(__x86_64__) + const char* const kSymbolizerArch = "--default-arch=x86_64"; +#elif defined(__i386__) + const char* const kSymbolizerArch = "--default-arch=i386"; +#elif defined(__powerpc64__) && defined(__BIG_ENDIAN__) + const char* const kSymbolizerArch = "--default-arch=powerpc64"; +#elif defined(__powerpc64__) && defined(__LITTLE_ENDIAN__) + const char* const kSymbolizerArch = "--default-arch=powerpc64le"; +#else + const char* const kSymbolizerArch = "--default-arch=unknown"; +#endif + + const char *const inline_flag = common_flags()->symbolize_inline_frames + ? "--inlining=true" + : "--inlining=false"; + int i = 0; + argv[i++] = path_to_binary; + argv[i++] = inline_flag; + argv[i++] = kSymbolizerArch; + argv[i++] = nullptr; + } +}; + +LLVMSymbolizer::LLVMSymbolizer(const char *path, LowLevelAllocator *allocator) + : symbolizer_process_(new(*allocator) LLVMSymbolizerProcess(path)) {} + +// Parse a <file>:<line>[:<column>] buffer. The file path may contain colons on +// Windows, so extract tokens from the right hand side first. The column info is +// also optional. +static const char *ParseFileLineInfo(AddressInfo *info, const char *str) { + char *file_line_info = 0; + str = ExtractToken(str, "\n", &file_line_info); + CHECK(file_line_info); + // Parse the last :<int>, which must be there. + char *last_colon = internal_strrchr(file_line_info, ':'); + CHECK(last_colon); + int line_or_column = internal_atoll(last_colon + 1); + // Truncate the string at the last colon and find the next-to-last colon. + *last_colon = '\0'; + last_colon = internal_strrchr(file_line_info, ':'); + if (last_colon && IsDigit(last_colon[1])) { + // If the second-to-last colon is followed by a digit, it must be the line + // number, and the previous parsed number was a column. + info->line = internal_atoll(last_colon + 1); + info->column = line_or_column; + *last_colon = '\0'; + } else { + // Otherwise, we have line info but no column info. + info->line = line_or_column; + info->column = 0; + } + ExtractToken(file_line_info, "", &info->file); + InternalFree(file_line_info); + return str; +} + +// Parses one or more two-line strings in the following format: +// <function_name> +// <file_name>:<line_number>[:<column_number>] +// Used by LLVMSymbolizer, Addr2LinePool and InternalSymbolizer, since all of +// them use the same output format. +void ParseSymbolizePCOutput(const char *str, SymbolizedStack *res) { + bool top_frame = true; + SymbolizedStack *last = res; + while (true) { + char *function_name = 0; + str = ExtractToken(str, "\n", &function_name); + CHECK(function_name); + if (function_name[0] == '\0') { + // There are no more frames. + InternalFree(function_name); + break; + } + SymbolizedStack *cur; + if (top_frame) { + cur = res; + top_frame = false; + } else { + cur = SymbolizedStack::New(res->info.address); + cur->info.FillModuleInfo(res->info.module, res->info.module_offset); + last->next = cur; + last = cur; + } + + AddressInfo *info = &cur->info; + info->function = function_name; + str = ParseFileLineInfo(info, str); + + // Functions and filenames can be "??", in which case we write 0 + // to address info to mark that names are unknown. + if (0 == internal_strcmp(info->function, "??")) { + InternalFree(info->function); + info->function = 0; + } + if (0 == internal_strcmp(info->file, "??")) { + InternalFree(info->file); + info->file = 0; + } + } +} + +// Parses a two-line string in the following format: +// <symbol_name> +// <start_address> <size> +// Used by LLVMSymbolizer and InternalSymbolizer. +void ParseSymbolizeDataOutput(const char *str, DataInfo *info) { + str = ExtractToken(str, "\n", &info->name); + str = ExtractUptr(str, " ", &info->start); + str = ExtractUptr(str, "\n", &info->size); +} + +bool LLVMSymbolizer::SymbolizePC(uptr addr, SymbolizedStack *stack) { + if (const char *buf = SendCommand(/*is_data*/ false, stack->info.module, + stack->info.module_offset)) { + ParseSymbolizePCOutput(buf, stack); + return true; + } + return false; +} + +bool LLVMSymbolizer::SymbolizeData(uptr addr, DataInfo *info) { + if (const char *buf = + SendCommand(/*is_data*/ true, info->module, info->module_offset)) { + ParseSymbolizeDataOutput(buf, info); + info->start += (addr - info->module_offset); // Add the base address. + return true; + } + return false; +} + +const char *LLVMSymbolizer::SendCommand(bool is_data, const char *module_name, + uptr module_offset) { + CHECK(module_name); + internal_snprintf(buffer_, kBufferSize, "%s\"%s\" 0x%zx\n", + is_data ? "DATA " : "", module_name, module_offset); + return symbolizer_process_->SendCommand(buffer_); +} + +SymbolizerProcess::SymbolizerProcess(const char *path, bool use_forkpty) + : path_(path), + input_fd_(kInvalidFd), + output_fd_(kInvalidFd), + times_restarted_(0), + failed_to_start_(false), + reported_invalid_path_(false), + use_forkpty_(use_forkpty) { + CHECK(path_); + CHECK_NE(path_[0], '\0'); +} + +const char *SymbolizerProcess::SendCommand(const char *command) { + for (; times_restarted_ < kMaxTimesRestarted; times_restarted_++) { + // Start or restart symbolizer if we failed to send command to it. + if (const char *res = SendCommandImpl(command)) + return res; + Restart(); + } + if (!failed_to_start_) { + Report("WARNING: Failed to use and restart external symbolizer!\n"); + failed_to_start_ = true; + } + return 0; +} + +const char *SymbolizerProcess::SendCommandImpl(const char *command) { + if (input_fd_ == kInvalidFd || output_fd_ == kInvalidFd) + return 0; + if (!WriteToSymbolizer(command, internal_strlen(command))) + return 0; + if (!ReadFromSymbolizer(buffer_, kBufferSize)) + return 0; + return buffer_; +} + +bool SymbolizerProcess::Restart() { + if (input_fd_ != kInvalidFd) + CloseFile(input_fd_); + if (output_fd_ != kInvalidFd) + CloseFile(output_fd_); + return StartSymbolizerSubprocess(); +} + +bool SymbolizerProcess::ReadFromSymbolizer(char *buffer, uptr max_length) { + if (max_length == 0) + return true; + uptr read_len = 0; + while (true) { + uptr just_read = 0; + bool success = ReadFromFile(input_fd_, buffer + read_len, + max_length - read_len - 1, &just_read); + // We can't read 0 bytes, as we don't expect external symbolizer to close + // its stdout. + if (!success || just_read == 0) { + Report("WARNING: Can't read from symbolizer at fd %d\n", input_fd_); + return false; + } + read_len += just_read; + if (ReachedEndOfOutput(buffer, read_len)) + break; + } + buffer[read_len] = '\0'; + return true; +} + +bool SymbolizerProcess::WriteToSymbolizer(const char *buffer, uptr length) { + if (length == 0) + return true; + uptr write_len = 0; + bool success = WriteToFile(output_fd_, buffer, length, &write_len); + if (!success || write_len != length) { + Report("WARNING: Can't write to symbolizer at fd %d\n", output_fd_); + return false; + } + return true; +} + } // namespace __sanitizer diff --git a/contrib/compiler-rt/lib/sanitizer_common/sanitizer_symbolizer_mac.cc b/contrib/compiler-rt/lib/sanitizer_common/sanitizer_symbolizer_mac.cc index 9a64192b0353..64048fa7e58e 100644 --- a/contrib/compiler-rt/lib/sanitizer_common/sanitizer_symbolizer_mac.cc +++ b/contrib/compiler-rt/lib/sanitizer_common/sanitizer_symbolizer_mac.cc @@ -33,39 +33,50 @@ bool DlAddrSymbolizer::SymbolizePC(uptr addr, SymbolizedStack *stack) { int result = dladdr((const void *)addr, &info); if (!result) return false; const char *demangled = DemangleCXXABI(info.dli_sname); - stack->info.function = internal_strdup(demangled); + stack->info.function = demangled ? internal_strdup(demangled) : nullptr; return true; } -bool DlAddrSymbolizer::SymbolizeData(uptr addr, DataInfo *info) { - return false; +bool DlAddrSymbolizer::SymbolizeData(uptr addr, DataInfo *datainfo) { + Dl_info info; + int result = dladdr((const void *)addr, &info); + if (!result) return false; + const char *demangled = DemangleCXXABI(info.dli_sname); + datainfo->name = internal_strdup(demangled); + datainfo->start = (uptr)info.dli_saddr; + return true; } class AtosSymbolizerProcess : public SymbolizerProcess { public: explicit AtosSymbolizerProcess(const char *path, pid_t parent_pid) - : SymbolizerProcess(path, /*use_forkpty*/ true), - parent_pid_(parent_pid) {} + : SymbolizerProcess(path, /*use_forkpty*/ true) { + // Put the string command line argument in the object so that it outlives + // the call to GetArgV. + internal_snprintf(pid_str_, sizeof(pid_str_), "%d", parent_pid); + } private: bool ReachedEndOfOutput(const char *buffer, uptr length) const override { return (length >= 1 && buffer[length - 1] == '\n'); } - void ExecuteWithDefaultArgs(const char *path_to_binary) const override { - char pid_str[16]; - internal_snprintf(pid_str, sizeof(pid_str), "%d", parent_pid_); + void GetArgV(const char *path_to_binary, + const char *(&argv)[kArgVMax]) const override { + int i = 0; + argv[i++] = path_to_binary; + argv[i++] = "-p"; + argv[i++] = &pid_str_[0]; if (GetMacosVersion() == MACOS_VERSION_MAVERICKS) { // On Mavericks atos prints a deprecation warning which we suppress by // passing -d. The warning isn't present on other OSX versions, even the // newer ones. - execl(path_to_binary, path_to_binary, "-p", pid_str, "-d", (char *)0); - } else { - execl(path_to_binary, path_to_binary, "-p", pid_str, (char *)0); + argv[i++] = "-d"; } + argv[i++] = nullptr; } - pid_t parent_pid_; + char pid_str_[16]; }; static const char *kAtosErrorMessages[] = { @@ -85,7 +96,9 @@ static bool IsAtosErrorMessage(const char *str) { return false; } -static bool ParseCommandOutput(const char *str, SymbolizedStack *res) { +static bool ParseCommandOutput(const char *str, uptr addr, char **out_name, + char **out_module, char **out_file, uptr *line, + uptr *start_address) { // Trim ending newlines. char *trim; ExtractTokenUpToDelimiter(str, "\n", &trim); @@ -93,7 +106,9 @@ static bool ParseCommandOutput(const char *str, SymbolizedStack *res) { // The line from `atos` is in one of these formats: // myfunction (in library.dylib) (sourcefile.c:17) // myfunction (in library.dylib) + 0x1fe + // myfunction (in library.dylib) + 15 // 0xdeadbeef (in library.dylib) + 0x1fe + // 0xdeadbeef (in library.dylib) + 15 // 0xdeadbeef (in library.dylib) // 0xdeadbeef @@ -104,21 +119,33 @@ static bool ParseCommandOutput(const char *str, SymbolizedStack *res) { } const char *rest = trim; - char *function_name; - rest = ExtractTokenUpToDelimiter(rest, " (in ", &function_name); - if (internal_strncmp(function_name, "0x", 2) != 0) - res->info.function = function_name; + char *symbol_name; + rest = ExtractTokenUpToDelimiter(rest, " (in ", &symbol_name); + if (rest[0] == '\0') { + InternalFree(symbol_name); + InternalFree(trim); + return false; + } + + if (internal_strncmp(symbol_name, "0x", 2) != 0) + *out_name = symbol_name; else - InternalFree(function_name); - rest = ExtractTokenUpToDelimiter(rest, ") ", &res->info.module); + InternalFree(symbol_name); + rest = ExtractTokenUpToDelimiter(rest, ") ", out_module); if (rest[0] == '(') { - rest++; - rest = ExtractTokenUpToDelimiter(rest, ":", &res->info.file); - char *extracted_line_number; - rest = ExtractTokenUpToDelimiter(rest, ")", &extracted_line_number); - res->info.line = internal_atoll(extracted_line_number); - InternalFree(extracted_line_number); + if (out_file) { + rest++; + rest = ExtractTokenUpToDelimiter(rest, ":", out_file); + char *extracted_line_number; + rest = ExtractTokenUpToDelimiter(rest, ")", &extracted_line_number); + if (line) *line = (uptr)internal_atoll(extracted_line_number); + InternalFree(extracted_line_number); + } + } else if (rest[0] == '+') { + rest += 2; + uptr offset = internal_atoll(rest); + if (start_address) *start_address = addr - offset; } InternalFree(trim); @@ -134,14 +161,29 @@ bool AtosSymbolizer::SymbolizePC(uptr addr, SymbolizedStack *stack) { internal_snprintf(command, sizeof(command), "0x%zx\n", addr); const char *buf = process_->SendCommand(command); if (!buf) return false; - if (!ParseCommandOutput(buf, stack)) { + uptr line; + if (!ParseCommandOutput(buf, addr, &stack->info.function, &stack->info.module, + &stack->info.file, &line, nullptr)) { process_ = nullptr; return false; } + stack->info.line = (int)line; return true; } -bool AtosSymbolizer::SymbolizeData(uptr addr, DataInfo *info) { return false; } +bool AtosSymbolizer::SymbolizeData(uptr addr, DataInfo *info) { + if (!process_) return false; + char command[32]; + internal_snprintf(command, sizeof(command), "0x%zx\n", addr); + const char *buf = process_->SendCommand(command); + if (!buf) return false; + if (!ParseCommandOutput(buf, addr, &info->name, &info->module, nullptr, + nullptr, &info->start)) { + process_ = nullptr; + return false; + } + return true; +} } // namespace __sanitizer diff --git a/contrib/compiler-rt/lib/sanitizer_common/sanitizer_symbolizer_posix_libcdep.cc b/contrib/compiler-rt/lib/sanitizer_common/sanitizer_symbolizer_posix_libcdep.cc index cb8455a21ae2..fc8a7d91ac73 100644 --- a/contrib/compiler-rt/lib/sanitizer_common/sanitizer_symbolizer_posix_libcdep.cc +++ b/contrib/compiler-rt/lib/sanitizer_common/sanitizer_symbolizer_posix_libcdep.cc @@ -20,13 +20,21 @@ #include "sanitizer_internal_defs.h" #include "sanitizer_linux.h" #include "sanitizer_placement_new.h" +#include "sanitizer_posix.h" #include "sanitizer_procmaps.h" #include "sanitizer_symbolizer_internal.h" #include "sanitizer_symbolizer_libbacktrace.h" #include "sanitizer_symbolizer_mac.h" +#include <errno.h> +#include <stdlib.h> +#include <sys/wait.h> #include <unistd.h> +#if SANITIZER_MAC +#include <util.h> // for forkpty() +#endif // SANITIZER_MAC + // C++ demangling function, as required by Itanium C++ ABI. This is weak, // because we do not require a C++ ABI library to be linked to a program // using sanitizers; if it's not present, we'll just use the mangled name. @@ -53,149 +61,130 @@ const char *DemangleCXXABI(const char *name) { return name; } -// Parses one or more two-line strings in the following format: -// <function_name> -// <file_name>:<line_number>[:<column_number>] -// Used by LLVMSymbolizer, Addr2LinePool and InternalSymbolizer, since all of -// them use the same output format. -static void ParseSymbolizePCOutput(const char *str, SymbolizedStack *res) { - bool top_frame = true; - SymbolizedStack *last = res; - while (true) { - char *function_name = 0; - str = ExtractToken(str, "\n", &function_name); - CHECK(function_name); - if (function_name[0] == '\0') { - // There are no more frames. - InternalFree(function_name); - break; - } - SymbolizedStack *cur; - if (top_frame) { - cur = res; - top_frame = false; - } else { - cur = SymbolizedStack::New(res->info.address); - cur->info.FillModuleInfo(res->info.module, res->info.module_offset); - last->next = cur; - last = cur; - } - - AddressInfo *info = &cur->info; - info->function = function_name; - // Parse <file>:<line>:<column> buffer. - char *file_line_info = 0; - str = ExtractToken(str, "\n", &file_line_info); - CHECK(file_line_info); - const char *line_info = ExtractToken(file_line_info, ":", &info->file); - line_info = ExtractInt(line_info, ":", &info->line); - line_info = ExtractInt(line_info, "", &info->column); - InternalFree(file_line_info); - - // Functions and filenames can be "??", in which case we write 0 - // to address info to mark that names are unknown. - if (0 == internal_strcmp(info->function, "??")) { - InternalFree(info->function); - info->function = 0; - } - if (0 == internal_strcmp(info->file, "??")) { - InternalFree(info->file); - info->file = 0; +bool SymbolizerProcess::StartSymbolizerSubprocess() { + if (!FileExists(path_)) { + if (!reported_invalid_path_) { + Report("WARNING: invalid path to external symbolizer!\n"); + reported_invalid_path_ = true; } - } -} - -// Parses a two-line string in the following format: -// <symbol_name> -// <start_address> <size> -// Used by LLVMSymbolizer and InternalSymbolizer. -static void ParseSymbolizeDataOutput(const char *str, DataInfo *info) { - str = ExtractToken(str, "\n", &info->name); - str = ExtractUptr(str, " ", &info->start); - str = ExtractUptr(str, "\n", &info->size); -} - -// For now we assume the following protocol: -// For each request of the form -// <module_name> <module_offset> -// passed to STDIN, external symbolizer prints to STDOUT response: -// <function_name> -// <file_name>:<line_number>:<column_number> -// <function_name> -// <file_name>:<line_number>:<column_number> -// ... -// <empty line> -class LLVMSymbolizerProcess : public SymbolizerProcess { - public: - explicit LLVMSymbolizerProcess(const char *path) : SymbolizerProcess(path) {} - - private: - bool ReachedEndOfOutput(const char *buffer, uptr length) const override { - // Empty line marks the end of llvm-symbolizer output. - return length >= 2 && buffer[length - 1] == '\n' && - buffer[length - 2] == '\n'; + return false; } - void ExecuteWithDefaultArgs(const char *path_to_binary) const override { -#if defined(__x86_64h__) - const char* const kSymbolizerArch = "--default-arch=x86_64h"; -#elif defined(__x86_64__) - const char* const kSymbolizerArch = "--default-arch=x86_64"; -#elif defined(__i386__) - const char* const kSymbolizerArch = "--default-arch=i386"; -#elif defined(__powerpc64__) && defined(__BIG_ENDIAN__) - const char* const kSymbolizerArch = "--default-arch=powerpc64"; -#elif defined(__powerpc64__) && defined(__LITTLE_ENDIAN__) - const char* const kSymbolizerArch = "--default-arch=powerpc64le"; -#else - const char* const kSymbolizerArch = "--default-arch=unknown"; -#endif - - const char *const inline_flag = common_flags()->symbolize_inline_frames - ? "--inlining=true" - : "--inlining=false"; - execl(path_to_binary, path_to_binary, inline_flag, kSymbolizerArch, - (char *)0); - } -}; + int pid; + if (use_forkpty_) { +#if SANITIZER_MAC + fd_t fd = kInvalidFd; + // Use forkpty to disable buffering in the new terminal. + pid = internal_forkpty(&fd); + if (pid == -1) { + // forkpty() failed. + Report("WARNING: failed to fork external symbolizer (errno: %d)\n", + errno); + return false; + } else if (pid == 0) { + // Child subprocess. + const char *argv[kArgVMax]; + GetArgV(path_, argv); + execv(path_, const_cast<char **>(&argv[0])); + internal__exit(1); + } -class LLVMSymbolizer : public SymbolizerTool { - public: - explicit LLVMSymbolizer(const char *path, LowLevelAllocator *allocator) - : symbolizer_process_(new(*allocator) LLVMSymbolizerProcess(path)) {} + // Continue execution in parent process. + input_fd_ = output_fd_ = fd; - bool SymbolizePC(uptr addr, SymbolizedStack *stack) override { - if (const char *buf = SendCommand(/*is_data*/ false, stack->info.module, - stack->info.module_offset)) { - ParseSymbolizePCOutput(buf, stack); - return true; + // Disable echo in the new terminal, disable CR. + struct termios termflags; + tcgetattr(fd, &termflags); + termflags.c_oflag &= ~ONLCR; + termflags.c_lflag &= ~ECHO; + tcsetattr(fd, TCSANOW, &termflags); +#else // SANITIZER_MAC + UNIMPLEMENTED(); +#endif // SANITIZER_MAC + } else { + int *infd = NULL; + int *outfd = NULL; + // The client program may close its stdin and/or stdout and/or stderr + // thus allowing socketpair to reuse file descriptors 0, 1 or 2. + // In this case the communication between the forked processes may be + // broken if either the parent or the child tries to close or duplicate + // these descriptors. The loop below produces two pairs of file + // descriptors, each greater than 2 (stderr). + int sock_pair[5][2]; + for (int i = 0; i < 5; i++) { + if (pipe(sock_pair[i]) == -1) { + for (int j = 0; j < i; j++) { + internal_close(sock_pair[j][0]); + internal_close(sock_pair[j][1]); + } + Report("WARNING: Can't create a socket pair to start " + "external symbolizer (errno: %d)\n", errno); + return false; + } else if (sock_pair[i][0] > 2 && sock_pair[i][1] > 2) { + if (infd == NULL) { + infd = sock_pair[i]; + } else { + outfd = sock_pair[i]; + for (int j = 0; j < i; j++) { + if (sock_pair[j] == infd) continue; + internal_close(sock_pair[j][0]); + internal_close(sock_pair[j][1]); + } + break; + } + } } - return false; - } - - bool SymbolizeData(uptr addr, DataInfo *info) override { - if (const char *buf = - SendCommand(/*is_data*/ true, info->module, info->module_offset)) { - ParseSymbolizeDataOutput(buf, info); - info->start += (addr - info->module_offset); // Add the base address. - return true; + CHECK(infd); + CHECK(outfd); + + // Real fork() may call user callbacks registered with pthread_atfork(). + pid = internal_fork(); + if (pid == -1) { + // Fork() failed. + internal_close(infd[0]); + internal_close(infd[1]); + internal_close(outfd[0]); + internal_close(outfd[1]); + Report("WARNING: failed to fork external symbolizer " + " (errno: %d)\n", errno); + return false; + } else if (pid == 0) { + // Child subprocess. + internal_close(STDOUT_FILENO); + internal_close(STDIN_FILENO); + internal_dup2(outfd[0], STDIN_FILENO); + internal_dup2(infd[1], STDOUT_FILENO); + internal_close(outfd[0]); + internal_close(outfd[1]); + internal_close(infd[0]); + internal_close(infd[1]); + for (int fd = sysconf(_SC_OPEN_MAX); fd > 2; fd--) + internal_close(fd); + const char *argv[kArgVMax]; + GetArgV(path_, argv); + execv(path_, const_cast<char **>(&argv[0])); + internal__exit(1); } - return false; + + // Continue execution in parent process. + internal_close(outfd[0]); + internal_close(infd[1]); + input_fd_ = infd[0]; + output_fd_ = outfd[1]; } - private: - const char *SendCommand(bool is_data, const char *module_name, - uptr module_offset) { - CHECK(module_name); - internal_snprintf(buffer_, kBufferSize, "%s\"%s\" 0x%zx\n", - is_data ? "DATA " : "", module_name, module_offset); - return symbolizer_process_->SendCommand(buffer_); + // Check that symbolizer subprocess started successfully. + int pid_status; + SleepForMillis(kSymbolizerStartupTimeMillis); + int exited_pid = waitpid(pid, &pid_status, WNOHANG); + if (exited_pid != 0) { + // Either waitpid failed, or child has already exited. + Report("WARNING: external symbolizer didn't start up correctly!\n"); + return false; } - LLVMSymbolizerProcess *symbolizer_process_; - static const uptr kBufferSize = 16 * 1024; - char buffer_[kBufferSize]; -}; + return true; +} class Addr2LineProcess : public SymbolizerProcess { public: @@ -205,25 +194,54 @@ class Addr2LineProcess : public SymbolizerProcess { const char *module_name() const { return module_name_; } private: - bool ReachedEndOfOutput(const char *buffer, uptr length) const override { - // Output should consist of two lines. - int num_lines = 0; - for (uptr i = 0; i < length; ++i) { - if (buffer[i] == '\n') - num_lines++; - if (num_lines >= 2) - return true; - } - return false; + void GetArgV(const char *path_to_binary, + const char *(&argv)[kArgVMax]) const override { + int i = 0; + argv[i++] = path_to_binary; + argv[i++] = "-iCfe"; + argv[i++] = module_name_; + argv[i++] = nullptr; } - void ExecuteWithDefaultArgs(const char *path_to_binary) const override { - execl(path_to_binary, path_to_binary, "-Cfe", module_name_, (char *)0); + bool ReachedEndOfOutput(const char *buffer, uptr length) const override; + + bool ReadFromSymbolizer(char *buffer, uptr max_length) override { + if (!SymbolizerProcess::ReadFromSymbolizer(buffer, max_length)) + return false; + // We should cut out output_terminator_ at the end of given buffer, + // appended by addr2line to mark the end of its meaningful output. + // We cannot scan buffer from it's beginning, because it is legal for it + // to start with output_terminator_ in case given offset is invalid. So, + // scanning from second character. + char *garbage = internal_strstr(buffer + 1, output_terminator_); + // This should never be NULL since buffer must end up with + // output_terminator_. + CHECK(garbage); + // Trim the buffer. + garbage[0] = '\0'; + return true; } const char *module_name_; // Owned, leaked. + static const char output_terminator_[]; }; +const char Addr2LineProcess::output_terminator_[] = "??\n??:0\n"; + +bool Addr2LineProcess::ReachedEndOfOutput(const char *buffer, + uptr length) const { + const size_t kTerminatorLen = sizeof(output_terminator_) - 1; + // Skip, if we read just kTerminatorLen bytes, because Addr2Line output + // should consist at least of two pairs of lines: + // 1. First one, corresponding to given offset to be symbolized + // (may be equal to output_terminator_, if offset is not valid). + // 2. Second one for output_terminator_, itself to mark the end of output. + if (length <= kTerminatorLen) return false; + // Addr2Line output should end up with output_terminator_. + return !internal_memcmp(buffer + length - kTerminatorLen, + output_terminator_, kTerminatorLen); +} + class Addr2LinePool : public SymbolizerTool { public: explicit Addr2LinePool(const char *addr2line_path, @@ -260,15 +278,18 @@ class Addr2LinePool : public SymbolizerTool { addr2line_pool_.push_back(addr2line); } CHECK_EQ(0, internal_strcmp(module_name, addr2line->module_name())); - char buffer_[kBufferSize]; - internal_snprintf(buffer_, kBufferSize, "0x%zx\n", module_offset); - return addr2line->SendCommand(buffer_); + char buffer[kBufferSize]; + internal_snprintf(buffer, kBufferSize, "0x%zx\n0x%zx\n", + module_offset, dummy_address_); + return addr2line->SendCommand(buffer); } - static const uptr kBufferSize = 32; + static const uptr kBufferSize = 64; const char *addr2line_path_; LowLevelAllocator *allocator_; InternalMmapVector<Addr2LineProcess*> addr2line_pool_; + static const uptr dummy_address_ = + FIRST_32_SECOND_64(UINT32_MAX, UINT64_MAX); }; #if SANITIZER_SUPPORTS_WEAK_HOOKS @@ -425,8 +446,6 @@ static void ChooseSymbolizerTools(IntrusiveList<SymbolizerTool> *list, if (SymbolizerTool *tool = ChooseExternalSymbolizer(allocator)) { list->push_back(tool); - } else { - VReport(2, "No internal or external symbolizer found.\n"); } #if SANITIZER_MAC diff --git a/contrib/compiler-rt/lib/sanitizer_common/sanitizer_symbolizer_process_libcdep.cc b/contrib/compiler-rt/lib/sanitizer_common/sanitizer_symbolizer_process_libcdep.cc deleted file mode 100644 index f1c01a332499..000000000000 --- a/contrib/compiler-rt/lib/sanitizer_common/sanitizer_symbolizer_process_libcdep.cc +++ /dev/null @@ -1,229 +0,0 @@ -//===-- sanitizer_symbolizer_process_libcdep.cc ---------------------------===// -// -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. -// -//===----------------------------------------------------------------------===// -// -// Implementation of SymbolizerProcess used by external symbolizers. -// -//===----------------------------------------------------------------------===// - -#include "sanitizer_platform.h" -#if SANITIZER_POSIX -#include "sanitizer_posix.h" -#include "sanitizer_symbolizer_internal.h" - -#include <errno.h> -#include <stdlib.h> -#include <sys/wait.h> -#include <unistd.h> - -#if SANITIZER_MAC -#include <util.h> // for forkpty() -#endif // SANITIZER_MAC - -namespace __sanitizer { - -SymbolizerProcess::SymbolizerProcess(const char *path, bool use_forkpty) - : path_(path), - input_fd_(kInvalidFd), - output_fd_(kInvalidFd), - times_restarted_(0), - failed_to_start_(false), - reported_invalid_path_(false), - use_forkpty_(use_forkpty) { - CHECK(path_); - CHECK_NE(path_[0], '\0'); -} - -const char *SymbolizerProcess::SendCommand(const char *command) { - for (; times_restarted_ < kMaxTimesRestarted; times_restarted_++) { - // Start or restart symbolizer if we failed to send command to it. - if (const char *res = SendCommandImpl(command)) - return res; - Restart(); - } - if (!failed_to_start_) { - Report("WARNING: Failed to use and restart external symbolizer!\n"); - failed_to_start_ = true; - } - return 0; -} - -bool SymbolizerProcess::Restart() { - if (input_fd_ != kInvalidFd) - internal_close(input_fd_); - if (output_fd_ != kInvalidFd) - internal_close(output_fd_); - return StartSymbolizerSubprocess(); -} - -const char *SymbolizerProcess::SendCommandImpl(const char *command) { - if (input_fd_ == kInvalidFd || output_fd_ == kInvalidFd) - return 0; - if (!WriteToSymbolizer(command, internal_strlen(command))) - return 0; - if (!ReadFromSymbolizer(buffer_, kBufferSize)) - return 0; - return buffer_; -} - -bool SymbolizerProcess::ReadFromSymbolizer(char *buffer, uptr max_length) { - if (max_length == 0) - return true; - uptr read_len = 0; - while (true) { - uptr just_read = internal_read(input_fd_, buffer + read_len, - max_length - read_len - 1); - // We can't read 0 bytes, as we don't expect external symbolizer to close - // its stdout. - if (just_read == 0 || just_read == (uptr)-1) { - Report("WARNING: Can't read from symbolizer at fd %d\n", input_fd_); - return false; - } - read_len += just_read; - if (ReachedEndOfOutput(buffer, read_len)) - break; - } - buffer[read_len] = '\0'; - return true; -} - -bool SymbolizerProcess::WriteToSymbolizer(const char *buffer, uptr length) { - if (length == 0) - return true; - uptr write_len = internal_write(output_fd_, buffer, length); - if (write_len == 0 || write_len == (uptr)-1) { - Report("WARNING: Can't write to symbolizer at fd %d\n", output_fd_); - return false; - } - return true; -} - -bool SymbolizerProcess::StartSymbolizerSubprocess() { - if (!FileExists(path_)) { - if (!reported_invalid_path_) { - Report("WARNING: invalid path to external symbolizer!\n"); - reported_invalid_path_ = true; - } - return false; - } - - int pid; - if (use_forkpty_) { -#if SANITIZER_MAC - fd_t fd = kInvalidFd; - // Use forkpty to disable buffering in the new terminal. - pid = forkpty(&fd, 0, 0, 0); - if (pid == -1) { - // forkpty() failed. - Report("WARNING: failed to fork external symbolizer (errno: %d)\n", - errno); - return false; - } else if (pid == 0) { - // Child subprocess. - ExecuteWithDefaultArgs(path_); - internal__exit(1); - } - - // Continue execution in parent process. - input_fd_ = output_fd_ = fd; - - // Disable echo in the new terminal, disable CR. - struct termios termflags; - tcgetattr(fd, &termflags); - termflags.c_oflag &= ~ONLCR; - termflags.c_lflag &= ~ECHO; - tcsetattr(fd, TCSANOW, &termflags); -#else // SANITIZER_MAC - UNIMPLEMENTED(); -#endif // SANITIZER_MAC - } else { - int *infd = NULL; - int *outfd = NULL; - // The client program may close its stdin and/or stdout and/or stderr - // thus allowing socketpair to reuse file descriptors 0, 1 or 2. - // In this case the communication between the forked processes may be - // broken if either the parent or the child tries to close or duplicate - // these descriptors. The loop below produces two pairs of file - // descriptors, each greater than 2 (stderr). - int sock_pair[5][2]; - for (int i = 0; i < 5; i++) { - if (pipe(sock_pair[i]) == -1) { - for (int j = 0; j < i; j++) { - internal_close(sock_pair[j][0]); - internal_close(sock_pair[j][1]); - } - Report("WARNING: Can't create a socket pair to start " - "external symbolizer (errno: %d)\n", errno); - return false; - } else if (sock_pair[i][0] > 2 && sock_pair[i][1] > 2) { - if (infd == NULL) { - infd = sock_pair[i]; - } else { - outfd = sock_pair[i]; - for (int j = 0; j < i; j++) { - if (sock_pair[j] == infd) continue; - internal_close(sock_pair[j][0]); - internal_close(sock_pair[j][1]); - } - break; - } - } - } - CHECK(infd); - CHECK(outfd); - - // Real fork() may call user callbacks registered with pthread_atfork(). - pid = internal_fork(); - if (pid == -1) { - // Fork() failed. - internal_close(infd[0]); - internal_close(infd[1]); - internal_close(outfd[0]); - internal_close(outfd[1]); - Report("WARNING: failed to fork external symbolizer " - " (errno: %d)\n", errno); - return false; - } else if (pid == 0) { - // Child subprocess. - internal_close(STDOUT_FILENO); - internal_close(STDIN_FILENO); - internal_dup2(outfd[0], STDIN_FILENO); - internal_dup2(infd[1], STDOUT_FILENO); - internal_close(outfd[0]); - internal_close(outfd[1]); - internal_close(infd[0]); - internal_close(infd[1]); - for (int fd = sysconf(_SC_OPEN_MAX); fd > 2; fd--) - internal_close(fd); - ExecuteWithDefaultArgs(path_); - internal__exit(1); - } - - // Continue execution in parent process. - internal_close(outfd[0]); - internal_close(infd[1]); - input_fd_ = infd[0]; - output_fd_ = outfd[1]; - } - - // Check that symbolizer subprocess started successfully. - int pid_status; - SleepForMillis(kSymbolizerStartupTimeMillis); - int exited_pid = waitpid(pid, &pid_status, WNOHANG); - if (exited_pid != 0) { - // Either waitpid failed, or child has already exited. - Report("WARNING: external symbolizer didn't start up correctly!\n"); - return false; - } - - return true; -} - -} // namespace __sanitizer - -#endif // SANITIZER_POSIX diff --git a/contrib/compiler-rt/lib/sanitizer_common/sanitizer_symbolizer_win.cc b/contrib/compiler-rt/lib/sanitizer_common/sanitizer_symbolizer_win.cc index 31f374687e96..b1dceebf45ce 100644 --- a/contrib/compiler-rt/lib/sanitizer_common/sanitizer_symbolizer_win.cc +++ b/contrib/compiler-rt/lib/sanitizer_common/sanitizer_symbolizer_win.cc @@ -14,17 +14,26 @@ #include "sanitizer_platform.h" #if SANITIZER_WINDOWS +#define WIN32_LEAN_AND_MEAN #include <windows.h> #include <dbghelp.h> #pragma comment(lib, "dbghelp.lib") -#include "sanitizer_symbolizer_win.h" #include "sanitizer_symbolizer_internal.h" namespace __sanitizer { namespace { +class WinSymbolizerTool : public SymbolizerTool { + public: + bool SymbolizePC(uptr addr, SymbolizedStack *stack) override; + bool SymbolizeData(uptr addr, DataInfo *info) override { + return false; + } + const char *Demangle(const char *name) override; +}; + bool is_dbghelp_initialized = false; bool TrySymInitialize() { @@ -115,7 +124,9 @@ bool WinSymbolizerTool::SymbolizePC(uptr addr, SymbolizedStack *frame) { frame->info.file = internal_strdup(line_info.FileName); frame->info.line = line_info.LineNumber; } - return true; + // Only consider this a successful symbolization attempt if we got file info. + // Otherwise, try llvm-symbolizer. + return got_fileline; } const char *WinSymbolizerTool::Demangle(const char *name) { @@ -137,10 +148,134 @@ void Symbolizer::PlatformPrepareForSandboxing() { // Do nothing. } +namespace { +struct ScopedHandle { + ScopedHandle() : h_(nullptr) {} + explicit ScopedHandle(HANDLE h) : h_(h) {} + ~ScopedHandle() { + if (h_) + ::CloseHandle(h_); + } + HANDLE get() { return h_; } + HANDLE *receive() { return &h_; } + HANDLE release() { + HANDLE h = h_; + h_ = nullptr; + return h; + } + HANDLE h_; +}; +} // namespace + +bool SymbolizerProcess::StartSymbolizerSubprocess() { + // Create inherited pipes for stdin and stdout. + ScopedHandle stdin_read, stdin_write; + ScopedHandle stdout_read, stdout_write; + SECURITY_ATTRIBUTES attrs; + attrs.nLength = sizeof(SECURITY_ATTRIBUTES); + attrs.bInheritHandle = TRUE; + attrs.lpSecurityDescriptor = nullptr; + if (!::CreatePipe(stdin_read.receive(), stdin_write.receive(), &attrs, 0) || + !::CreatePipe(stdout_read.receive(), stdout_write.receive(), &attrs, 0)) { + VReport(2, "WARNING: %s CreatePipe failed (error code: %d)\n", + SanitizerToolName, path_, GetLastError()); + return false; + } + + // Don't inherit the writing end of stdin or the reading end of stdout. + if (!SetHandleInformation(stdin_write.get(), HANDLE_FLAG_INHERIT, 0) || + !SetHandleInformation(stdout_read.get(), HANDLE_FLAG_INHERIT, 0)) { + VReport(2, "WARNING: %s SetHandleInformation failed (error code: %d)\n", + SanitizerToolName, path_, GetLastError()); + return false; + } + + // Compute the command line. Wrap double quotes around everything. + const char *argv[kArgVMax]; + GetArgV(path_, argv); + InternalScopedString command_line(kMaxPathLength * 3); + for (int i = 0; argv[i]; i++) { + const char *arg = argv[i]; + int arglen = internal_strlen(arg); + // Check that tool command lines are simple and that complete escaping is + // unnecessary. + CHECK(!internal_strchr(arg, '"') && "quotes in args unsupported"); + CHECK(!internal_strstr(arg, "\\\\") && + "double backslashes in args unsupported"); + CHECK(arglen > 0 && arg[arglen - 1] != '\\' && + "args ending in backslash and empty args unsupported"); + command_line.append("\"%s\" ", arg); + } + VReport(3, "Launching symbolizer command: %s\n", command_line.data()); + + // Launch llvm-symbolizer with stdin and stdout redirected. + STARTUPINFOA si; + memset(&si, 0, sizeof(si)); + si.cb = sizeof(si); + si.dwFlags |= STARTF_USESTDHANDLES; + si.hStdInput = stdin_read.get(); + si.hStdOutput = stdout_write.get(); + PROCESS_INFORMATION pi; + memset(&pi, 0, sizeof(pi)); + if (!CreateProcessA(path_, // Executable + command_line.data(), // Command line + nullptr, // Process handle not inheritable + nullptr, // Thread handle not inheritable + TRUE, // Set handle inheritance to TRUE + 0, // Creation flags + nullptr, // Use parent's environment block + nullptr, // Use parent's starting directory + &si, &pi)) { + VReport(2, "WARNING: %s failed to create process for %s (error code: %d)\n", + SanitizerToolName, path_, GetLastError()); + return false; + } + + // Process creation succeeded, so transfer handle ownership into the fields. + input_fd_ = stdout_read.release(); + output_fd_ = stdin_write.release(); + + // The llvm-symbolizer process is responsible for quitting itself when the + // stdin pipe is closed, so we don't need these handles. Close them to prevent + // leaks. If we ever want to try to kill the symbolizer process from the + // parent, we'll want to hang on to these handles. + CloseHandle(pi.hProcess); + CloseHandle(pi.hThread); + return true; +} + +static void ChooseSymbolizerTools(IntrusiveList<SymbolizerTool> *list, + LowLevelAllocator *allocator) { + if (!common_flags()->symbolize) { + VReport(2, "Symbolizer is disabled.\n"); + return; + } + + // Add llvm-symbolizer in case the binary has dwarf. + const char *user_path = common_flags()->external_symbolizer_path; + const char *path = + user_path ? user_path : FindPathToBinary("llvm-symbolizer.exe"); + if (path) { + VReport(2, "Using llvm-symbolizer at %spath: %s\n", + user_path ? "user-specified " : "", path); + list->push_back(new(*allocator) LLVMSymbolizer(path, allocator)); + } else { + if (user_path && user_path[0] == '\0') { + VReport(2, "External symbolizer is explicitly disabled.\n"); + } else { + VReport(2, "External symbolizer is not present.\n"); + } + } + + // Add the dbghelp based symbolizer. + list->push_back(new(*allocator) WinSymbolizerTool()); +} + Symbolizer *Symbolizer::PlatformInit() { IntrusiveList<SymbolizerTool> list; list.clear(); - list.push_back(new(symbolizer_allocator_) WinSymbolizerTool()); + ChooseSymbolizerTools(&list, &symbolizer_allocator_); + return new(symbolizer_allocator_) Symbolizer(list); } diff --git a/contrib/compiler-rt/lib/sanitizer_common/sanitizer_symbolizer_win.h b/contrib/compiler-rt/lib/sanitizer_common/sanitizer_symbolizer_win.h deleted file mode 100644 index 72ac5e5ee104..000000000000 --- a/contrib/compiler-rt/lib/sanitizer_common/sanitizer_symbolizer_win.h +++ /dev/null @@ -1,31 +0,0 @@ -//===-- sanitizer_symbolizer_win.h ------------------------------*- C++ -*-===// -// -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. -// -//===----------------------------------------------------------------------===// -// -// Header file for the Windows symbolizer tool. -// -//===----------------------------------------------------------------------===// -#ifndef SANITIZER_SYMBOLIZER_WIN_H -#define SANITIZER_SYMBOLIZER_WIN_H - -#include "sanitizer_symbolizer_internal.h" - -namespace __sanitizer { - -class WinSymbolizerTool : public SymbolizerTool { - public: - bool SymbolizePC(uptr addr, SymbolizedStack *stack) override; - bool SymbolizeData(uptr addr, DataInfo *info) override { - return false; - } - const char *Demangle(const char *name) override; -}; - -} // namespace __sanitizer - -#endif // SANITIZER_SYMBOLIZER_WIN_H diff --git a/contrib/compiler-rt/lib/sanitizer_common/sanitizer_syscall_linux_aarch64.inc b/contrib/compiler-rt/lib/sanitizer_common/sanitizer_syscall_linux_aarch64.inc new file mode 100644 index 000000000000..7ab1d7641449 --- /dev/null +++ b/contrib/compiler-rt/lib/sanitizer_common/sanitizer_syscall_linux_aarch64.inc @@ -0,0 +1,138 @@ +//===-- sanitizer_syscall_linux_aarch64.inc --------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// Implementations of internal_syscall and internal_iserror for Linux/aarch64. +// +//===----------------------------------------------------------------------===// + +#define SYSCALL(name) __NR_ ## name + +static uptr __internal_syscall(u64 nr) { + register u64 x8 asm("x8") = nr; + register u64 x0 asm("x0"); + asm volatile("svc 0" + : "=r"(x0) + : "r"(x8) + : "memory", "cc"); + return x0; +} +#define __internal_syscall0(n) \ + (__internal_syscall)(n) + +static uptr __internal_syscall(u64 nr, u64 arg1) { + register u64 x8 asm("x8") = nr; + register u64 x0 asm("x0") = arg1; + asm volatile("svc 0" + : "=r"(x0) + : "r"(x8), "0"(x0) + : "memory", "cc"); + return x0; +} +#define __internal_syscall1(n, a1) \ + (__internal_syscall)(n, (u64)(a1)) + +static uptr __internal_syscall(u64 nr, u64 arg1, long arg2) { + register u64 x8 asm("x8") = nr; + register u64 x0 asm("x0") = arg1; + register u64 x1 asm("x1") = arg2; + asm volatile("svc 0" + : "=r"(x0) + : "r"(x8), "0"(x0), "r"(x1) + : "memory", "cc"); + return x0; +} +#define __internal_syscall2(n, a1, a2) \ + (__internal_syscall)(n, (u64)(a1), (long)(a2)) + +static uptr __internal_syscall(u64 nr, u64 arg1, long arg2, long arg3) { + register u64 x8 asm("x8") = nr; + register u64 x0 asm("x0") = arg1; + register u64 x1 asm("x1") = arg2; + register u64 x2 asm("x2") = arg3; + asm volatile("svc 0" + : "=r"(x0) + : "r"(x8), "0"(x0), "r"(x1), "r"(x2) + : "memory", "cc"); + return x0; +} +#define __internal_syscall3(n, a1, a2, a3) \ + (__internal_syscall)(n, (u64)(a1), (long)(a2), (long)(a3)) + +static uptr __internal_syscall(u64 nr, u64 arg1, long arg2, long arg3, + u64 arg4) { + register u64 x8 asm("x8") = nr; + register u64 x0 asm("x0") = arg1; + register u64 x1 asm("x1") = arg2; + register u64 x2 asm("x2") = arg3; + register u64 x3 asm("x3") = arg4; + asm volatile("svc 0" + : "=r"(x0) + : "r"(x8), "0"(x0), "r"(x1), "r"(x2), "r"(x3) + : "memory", "cc"); + return x0; +} +#define __internal_syscall4(n, a1, a2, a3, a4) \ + (__internal_syscall)(n, (u64)(a1), (long)(a2), (long)(a3), (long)(a4)) + +static uptr __internal_syscall(u64 nr, u64 arg1, long arg2, long arg3, + u64 arg4, long arg5) { + register u64 x8 asm("x8") = nr; + register u64 x0 asm("x0") = arg1; + register u64 x1 asm("x1") = arg2; + register u64 x2 asm("x2") = arg3; + register u64 x3 asm("x3") = arg4; + register u64 x4 asm("x4") = arg5; + asm volatile("svc 0" + : "=r"(x0) + : "r"(x8), "0"(x0), "r"(x1), "r"(x2), "r"(x3), "r"(x4) + : "memory", "cc"); + return x0; +} +#define __internal_syscall5(n, a1, a2, a3, a4, a5) \ + (__internal_syscall)(n, (u64)(a1), (long)(a2), (long)(a3), (long)(a4), \ + (u64)(a5)) + +static uptr __internal_syscall(u64 nr, u64 arg1, long arg2, long arg3, + u64 arg4, long arg5, long arg6) { + register u64 x8 asm("x8") = nr; + register u64 x0 asm("x0") = arg1; + register u64 x1 asm("x1") = arg2; + register u64 x2 asm("x2") = arg3; + register u64 x3 asm("x3") = arg4; + register u64 x4 asm("x4") = arg5; + register u64 x5 asm("x5") = arg6; + asm volatile("svc 0" + : "=r"(x0) + : "r"(x8), "0"(x0), "r"(x1), "r"(x2), "r"(x3), "r"(x4), "r"(x5) + : "memory", "cc"); + return x0; +} +#define __internal_syscall6(n, a1, a2, a3, a4, a5, a6) \ + (__internal_syscall)(n, (u64)(a1), (long)(a2), (long)(a3), (long)(a4), \ + (u64)(a5), (long)(a6)) + +#define __SYSCALL_NARGS_X(a1, a2, a3, a4, a5, a6, a7, a8, n, ...) n +#define __SYSCALL_NARGS(...) \ + __SYSCALL_NARGS_X(__VA_ARGS__, 7, 6, 5, 4, 3, 2, 1, 0, ) +#define __SYSCALL_CONCAT_X(a, b) a##b +#define __SYSCALL_CONCAT(a, b) __SYSCALL_CONCAT_X(a, b) +#define __SYSCALL_DISP(b, ...) \ + __SYSCALL_CONCAT(b, __SYSCALL_NARGS(__VA_ARGS__))(__VA_ARGS__) + +#define internal_syscall(...) __SYSCALL_DISP(__internal_syscall, __VA_ARGS__) + +// Helper function used to avoid cobbler errno. +bool internal_iserror(uptr retval, int *rverrno) { + if (retval >= (uptr)-4095) { + if (rverrno) + *rverrno = -retval; + return true; + } + return false; +} diff --git a/contrib/compiler-rt/lib/sanitizer_common/sanitizer_thread_registry.h b/contrib/compiler-rt/lib/sanitizer_common/sanitizer_thread_registry.h index 5d9c3b9694e8..a27bbb376e85 100644 --- a/contrib/compiler-rt/lib/sanitizer_common/sanitizer_thread_registry.h +++ b/contrib/compiler-rt/lib/sanitizer_common/sanitizer_thread_registry.h @@ -79,7 +79,8 @@ class ThreadRegistry { ThreadRegistry(ThreadContextFactory factory, u32 max_threads, u32 thread_quarantine_size, u32 max_reuse = 0); - void GetNumberOfThreads(uptr *total = 0, uptr *running = 0, uptr *alive = 0); + void GetNumberOfThreads(uptr *total = nullptr, uptr *running = nullptr, + uptr *alive = nullptr); uptr GetMaxAliveThreads(); void Lock() { mtx_.Lock(); } @@ -142,7 +143,6 @@ class ThreadRegistry { typedef GenericScopedLock<ThreadRegistry> ThreadRegistryLock; -} // namespace __sanitizer - -#endif // SANITIZER_THREAD_REGISTRY_H +} // namespace __sanitizer +#endif // SANITIZER_THREAD_REGISTRY_H diff --git a/contrib/compiler-rt/lib/sanitizer_common/sanitizer_tls_get_addr.cc b/contrib/compiler-rt/lib/sanitizer_common/sanitizer_tls_get_addr.cc index ea037159d00b..213aced89da7 100644 --- a/contrib/compiler-rt/lib/sanitizer_common/sanitizer_tls_get_addr.cc +++ b/contrib/compiler-rt/lib/sanitizer_common/sanitizer_tls_get_addr.cc @@ -78,6 +78,15 @@ void DTLS_Destroy() { DTLS_Deallocate(dtls.dtv, s); } +#if defined(__powerpc64__) +// This is glibc's TLS_DTV_OFFSET: +// "Dynamic thread vector pointers point 0x8000 past the start of each +// TLS block." +static const uptr kDtvOffset = 0x8000; +#else +static const uptr kDtvOffset = 0; +#endif + DTLS::DTV *DTLS_on_tls_get_addr(void *arg_void, void *res, uptr static_tls_begin, uptr static_tls_end) { if (!common_flags()->intercept_tls_get_addr) return 0; @@ -87,7 +96,7 @@ DTLS::DTV *DTLS_on_tls_get_addr(void *arg_void, void *res, DTLS_Resize(dso_id + 1); if (dtls.dtv[dso_id].beg) return 0; uptr tls_size = 0; - uptr tls_beg = reinterpret_cast<uptr>(res) - arg->offset; + uptr tls_beg = reinterpret_cast<uptr>(res) - arg->offset - kDtvOffset; VPrintf(2, "__tls_get_addr: %p {%p,%p} => %p; tls_beg: %p; sp: %p " "num_live_dtls %zd\n", arg, arg->dso_id, arg->offset, res, tls_beg, &tls_beg, diff --git a/contrib/compiler-rt/lib/sanitizer_common/sanitizer_win.cc b/contrib/compiler-rt/lib/sanitizer_common/sanitizer_win.cc index d5bbe4565068..861261d8402c 100644 --- a/contrib/compiler-rt/lib/sanitizer_common/sanitizer_win.cc +++ b/contrib/compiler-rt/lib/sanitizer_common/sanitizer_win.cc @@ -51,7 +51,7 @@ uptr GetMaxVirtualAddress() { } bool FileExists(const char *filename) { - UNIMPLEMENTED(); + return ::GetFileAttributesA(filename) != INVALID_FILE_ATTRIBUTES; } uptr internal_getpid() { @@ -83,14 +83,11 @@ void GetThreadStackTopAndBottom(bool at_initialization, uptr *stack_top, } #endif // #if !SANITIZER_GO -void *MmapOrDie(uptr size, const char *mem_type) { +void *MmapOrDie(uptr size, const char *mem_type, bool raw_report) { void *rv = VirtualAlloc(0, size, MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE); - if (rv == 0) { - Report("ERROR: %s failed to " - "allocate 0x%zx (%zd) bytes of %s (error code: %d)\n", - SanitizerToolName, size, size, mem_type, GetLastError()); - CHECK("unable to mmap" && 0); - } + if (rv == 0) + ReportMmapFailureAndDie(size, mem_type, "allocate", + GetLastError(), raw_report); return rv; } @@ -224,12 +221,14 @@ struct ModuleInfo { uptr end_address; }; +#ifndef SANITIZER_GO int CompareModulesBase(const void *pl, const void *pr) { const ModuleInfo *l = (ModuleInfo *)pl, *r = (ModuleInfo *)pr; if (l->base_address < r->base_address) return -1; return l->base_address > r->base_address; } +#endif } // namespace #ifndef SANITIZER_GO @@ -292,11 +291,6 @@ void SetAddressSpaceUnlimited() { UNIMPLEMENTED(); } -char *FindPathToBinary(const char *name) { - // Nothing here for now. - return 0; -} - bool IsPathSeparator(const char c) { return c == '\\' || c == '/'; } @@ -323,6 +317,59 @@ void Abort() { internal__exit(3); } +// Read the file to extract the ImageBase field from the PE header. If ASLR is +// disabled and this virtual address is available, the loader will typically +// load the image at this address. Therefore, we call it the preferred base. Any +// addresses in the DWARF typically assume that the object has been loaded at +// this address. +static uptr GetPreferredBase(const char *modname) { + fd_t fd = OpenFile(modname, RdOnly, nullptr); + if (fd == kInvalidFd) + return 0; + FileCloser closer(fd); + + // Read just the DOS header. + IMAGE_DOS_HEADER dos_header; + uptr bytes_read; + if (!ReadFromFile(fd, &dos_header, sizeof(dos_header), &bytes_read) || + bytes_read != sizeof(dos_header)) + return 0; + + // The file should start with the right signature. + if (dos_header.e_magic != IMAGE_DOS_SIGNATURE) + return 0; + + // The layout at e_lfanew is: + // "PE\0\0" + // IMAGE_FILE_HEADER + // IMAGE_OPTIONAL_HEADER + // Seek to e_lfanew and read all that data. + char buf[4 + sizeof(IMAGE_FILE_HEADER) + sizeof(IMAGE_OPTIONAL_HEADER)]; + if (::SetFilePointer(fd, dos_header.e_lfanew, nullptr, FILE_BEGIN) == + INVALID_SET_FILE_POINTER) + return 0; + if (!ReadFromFile(fd, &buf[0], sizeof(buf), &bytes_read) || + bytes_read != sizeof(buf)) + return 0; + + // Check for "PE\0\0" before the PE header. + char *pe_sig = &buf[0]; + if (internal_memcmp(pe_sig, "PE\0\0", 4) != 0) + return 0; + + // Skip over IMAGE_FILE_HEADER. We could do more validation here if we wanted. + IMAGE_OPTIONAL_HEADER *pe_header = + (IMAGE_OPTIONAL_HEADER *)(pe_sig + 4 + sizeof(IMAGE_FILE_HEADER)); + + // Check for more magic in the PE header. + if (pe_header->Magic != IMAGE_NT_OPTIONAL_HDR_MAGIC) + return 0; + + // Finally, return the ImageBase. + return (uptr)pe_header->ImageBase; +} + +#ifndef SANITIZER_GO uptr GetListOfModules(LoadedModule *modules, uptr max_modules, string_predicate_t filter) { HANDLE cur_process = GetCurrentProcess(); @@ -355,19 +402,33 @@ uptr GetListOfModules(LoadedModule *modules, uptr max_modules, if (!GetModuleInformation(cur_process, handle, &mi, sizeof(mi))) continue; - char module_name[MAX_PATH]; - bool got_module_name = - GetModuleFileNameA(handle, module_name, sizeof(module_name)); - if (!got_module_name) - module_name[0] = '\0'; + // Get the UTF-16 path and convert to UTF-8. + wchar_t modname_utf16[kMaxPathLength]; + int modname_utf16_len = + GetModuleFileNameW(handle, modname_utf16, kMaxPathLength); + if (modname_utf16_len == 0) + modname_utf16[0] = '\0'; + char module_name[kMaxPathLength]; + int module_name_len = + ::WideCharToMultiByte(CP_UTF8, 0, modname_utf16, modname_utf16_len + 1, + &module_name[0], kMaxPathLength, NULL, NULL); + module_name[module_name_len] = '\0'; if (filter && !filter(module_name)) continue; uptr base_address = (uptr)mi.lpBaseOfDll; uptr end_address = (uptr)mi.lpBaseOfDll + mi.SizeOfImage; + + // Adjust the base address of the module so that we get a VA instead of an + // RVA when computing the module offset. This helps llvm-symbolizer find the + // right DWARF CU. In the common case that the image is loaded at it's + // preferred address, we will now print normal virtual addresses. + uptr preferred_base = GetPreferredBase(&module_name[0]); + uptr adjusted_base = base_address - preferred_base; + LoadedModule *cur_module = &modules[count]; - cur_module->set(module_name, base_address); + cur_module->set(module_name, adjusted_base); // We add the whole module as one single address range. cur_module->addAddressRange(base_address, end_address, /*executable*/ true); count++; @@ -377,7 +438,6 @@ uptr GetListOfModules(LoadedModule *modules, uptr max_modules, return count; }; -#ifndef SANITIZER_GO // We can't use atexit() directly at __asan_init time as the CRT is not fully // initialized at this point. Place the functions into a vector and use // atexit() as soon as it is ready for use (i.e. after .CRT$XIC initializers). @@ -397,15 +457,22 @@ static int RunAtexit() { } #pragma section(".CRT$XID", long, read) // NOLINT -static __declspec(allocate(".CRT$XID")) int (*__run_atexit)() = RunAtexit; +__declspec(allocate(".CRT$XID")) int (*__run_atexit)() = RunAtexit; #endif // ------------------ sanitizer_libc.h fd_t OpenFile(const char *filename, FileAccessMode mode, error_t *last_error) { - if (mode != WrOnly) + fd_t res; + if (mode == RdOnly) { + res = CreateFile(filename, GENERIC_READ, + FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, + nullptr, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, nullptr); + } else if (mode == WrOnly) { + res = CreateFile(filename, GENERIC_WRITE, 0, nullptr, CREATE_ALWAYS, + FILE_ATTRIBUTE_NORMAL, nullptr); + } else { UNIMPLEMENTED(); - fd_t res = CreateFile(filename, GENERIC_WRITE, 0, nullptr, CREATE_ALWAYS, - FILE_ATTRIBUTE_NORMAL, nullptr); + } CHECK(res != kStdoutFd || kStdoutFd == kInvalidFd); CHECK(res != kStderrFd || kStderrFd == kInvalidFd); if (res == kInvalidFd && last_error) @@ -419,7 +486,18 @@ void CloseFile(fd_t fd) { bool ReadFromFile(fd_t fd, void *buff, uptr buff_size, uptr *bytes_read, error_t *error_p) { - UNIMPLEMENTED(); + CHECK(fd != kInvalidFd); + + // bytes_read can't be passed directly to ReadFile: + // uptr is unsigned long long on 64-bit Windows. + unsigned long num_read_long; + + bool success = ::ReadFile(fd, buff, buff_size, &num_read_long, nullptr); + if (!success && error_p) + *error_p = GetLastError(); + if (bytes_read) + *bytes_read = num_read_long; + return success; } bool SupportsColoredOutput(fd_t fd) { @@ -431,21 +509,32 @@ bool WriteToFile(fd_t fd, const void *buff, uptr buff_size, uptr *bytes_written, error_t *error_p) { CHECK(fd != kInvalidFd); - if (fd == kStdoutFd) { - fd = GetStdHandle(STD_OUTPUT_HANDLE); - if (fd == 0) fd = kInvalidFd; - } else if (fd == kStderrFd) { - fd = GetStdHandle(STD_ERROR_HANDLE); - if (fd == 0) fd = kInvalidFd; + // Handle null optional parameters. + error_t dummy_error; + error_p = error_p ? error_p : &dummy_error; + uptr dummy_bytes_written; + bytes_written = bytes_written ? bytes_written : &dummy_bytes_written; + + // Initialize output parameters in case we fail. + *error_p = 0; + *bytes_written = 0; + + // Map the conventional Unix fds 1 and 2 to Windows handles. They might be + // closed, in which case this will fail. + if (fd == kStdoutFd || fd == kStderrFd) { + fd = GetStdHandle(fd == kStdoutFd ? STD_OUTPUT_HANDLE : STD_ERROR_HANDLE); + if (fd == 0) { + *error_p = ERROR_INVALID_HANDLE; + return false; + } } - DWORD internal_bytes_written; - if (fd == kInvalidFd || - WriteFile(fd, buff, buff_size, &internal_bytes_written, 0)) { - if (error_p) *error_p = GetLastError(); + DWORD bytes_written_32; + if (!WriteFile(fd, buff, buff_size, &bytes_written_32, 0)) { + *error_p = GetLastError(); return false; } else { - if (bytes_written) *bytes_written = internal_bytes_written; + *bytes_written = bytes_written_32; return true; } } @@ -665,6 +754,22 @@ uptr ReadBinaryName(/*out*/char *buf, uptr buf_len) { return 0; } +uptr ReadLongProcessName(/*out*/char *buf, uptr buf_len) { + return ReadBinaryName(buf, buf_len); +} + +void CheckVMASize() { + // Do nothing. +} + +void DisableReexec() { + // No need to re-exec on Windows. +} + +void MaybeReexec() { + // No need to re-exec on Windows. +} + } // namespace __sanitizer #endif // _WIN32 diff --git a/contrib/compiler-rt/lib/sanitizer_common/scripts/check_lint.sh b/contrib/compiler-rt/lib/sanitizer_common/scripts/check_lint.sh deleted file mode 100755 index 9108a81e26c8..000000000000 --- a/contrib/compiler-rt/lib/sanitizer_common/scripts/check_lint.sh +++ /dev/null @@ -1,136 +0,0 @@ -#!/bin/sh - -SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" - -# Guess path to LLVM_CHECKOUT if not provided -if [ "${LLVM_CHECKOUT}" = "" ]; then - LLVM_CHECKOUT="${SCRIPT_DIR}/../../../../../" -fi - -# python tools setup -CPPLINT=${SCRIPT_DIR}/cpplint.py -LITLINT=${SCRIPT_DIR}/litlint.py -if [ "${PYTHON_EXECUTABLE}" != "" ]; then - CPPLINT="${PYTHON_EXECUTABLE} ${CPPLINT}" - LITLINT="${PYTHON_EXECUTABLE} ${LITLINT}" -fi - -# Filters -# TODO: remove some of these filters -COMMON_LINT_FILTER=-build/include,-build/header_guard,-legal/copyright,-whitespace/comments,-readability/casting,\ --build/namespaces -ASAN_RTL_LINT_FILTER=${COMMON_LINT_FILTER},-runtime/int -ASAN_TEST_LINT_FILTER=${COMMON_LINT_FILTER},-runtime/sizeof,-runtime/int,-runtime/printf,-runtime/threadsafe_fn -ASAN_LIT_TEST_LINT_FILTER=${ASAN_TEST_LINT_FILTER},-whitespace/line_length -TSAN_RTL_LINT_FILTER=${COMMON_LINT_FILTER} -TSAN_TEST_LINT_FILTER=${TSAN_RTL_LINT_FILTER},-runtime/threadsafe_fn,-runtime/int -TSAN_LIT_TEST_LINT_FILTER=${TSAN_TEST_LINT_FILTER},-whitespace/line_length -MSAN_RTL_LINT_FILTER=${COMMON_LINT_FILTER} -LSAN_RTL_LINT_FILTER=${COMMON_LINT_FILTER} -LSAN_LIT_TEST_LINT_FILTER=${LSAN_RTL_LINT_FILTER},-whitespace/line_length -DFSAN_RTL_LINT_FILTER=${COMMON_LINT_FILTER},-runtime/int,-runtime/printf,-runtime/references,-readability/function -COMMON_RTL_INC_LINT_FILTER=${COMMON_LINT_FILTER},-runtime/int,-runtime/sizeof,-runtime/printf,-readability/fn_size -SANITIZER_INCLUDES_LINT_FILTER=${COMMON_LINT_FILTER},-runtime/int - -MKTEMP_DIR=$(mktemp -qd /tmp/check_lint.XXXXXXXXXX) -MKTEMP="mktemp -q ${MKTEMP_DIR}/tmp.XXXXXXXXXX" -cleanup() { - rm -rf $MKTEMP_DIR -} -trap cleanup EXIT - -cd ${LLVM_CHECKOUT} - -EXITSTATUS=0 -ERROR_LOG=$(${MKTEMP}) - -run_lint() { - FILTER=$1 - shift - TASK_LOG=$(${MKTEMP}) - ${CPPLINT} --filter=${FILTER} "$@" 2>$TASK_LOG - if [ "$?" != "0" ]; then - cat $TASK_LOG | grep -v "Done processing" | grep -v "Total errors found" \ - | grep -v "Skipping input" >> $ERROR_LOG - fi - if [ "${SILENT}" != "1" ]; then - cat $TASK_LOG - fi - ${LITLINT} "$@" 2>>$ERROR_LOG -} - -if [ "${COMPILER_RT}" = "" ]; then - COMPILER_RT=projects/compiler-rt -fi -LIT_TESTS=${COMPILER_RT}/test -# Headers -SANITIZER_INCLUDES=${COMPILER_RT}/include/sanitizer -run_lint ${SANITIZER_INCLUDES_LINT_FILTER} ${SANITIZER_INCLUDES}/*.h & - -# Sanitizer_common -COMMON_RTL=${COMPILER_RT}/lib/sanitizer_common -run_lint ${COMMON_RTL_INC_LINT_FILTER} ${COMMON_RTL}/*.cc \ - ${COMMON_RTL}/*.h \ - ${COMMON_RTL}/tests/*.cc & - -# Interception -INTERCEPTION=${COMPILER_RT}/lib/interception -run_lint ${ASAN_RTL_LINT_FILTER} ${INTERCEPTION}/*.cc \ - ${INTERCEPTION}/*.h & - -# ASan -ASAN_RTL=${COMPILER_RT}/lib/asan -run_lint ${ASAN_RTL_LINT_FILTER} ${ASAN_RTL}/*.cc \ - ${ASAN_RTL}/*.h & -run_lint ${ASAN_TEST_LINT_FILTER} ${ASAN_RTL}/tests/*.cc \ - ${ASAN_RTL}/tests/*.h & -run_lint ${ASAN_LIT_TEST_LINT_FILTER} ${LIT_TESTS}/asan/*/*.cc & - -# TSan -TSAN_RTL=${COMPILER_RT}/lib/tsan -run_lint ${TSAN_RTL_LINT_FILTER} ${TSAN_RTL}/rtl/*.cc \ - ${TSAN_RTL}/rtl/*.h & -run_lint ${TSAN_TEST_LINT_FILTER} ${TSAN_RTL}/tests/rtl/*.cc \ - ${TSAN_RTL}/tests/rtl/*.h \ - ${TSAN_RTL}/tests/unit/*.cc & -run_lint ${TSAN_LIT_TEST_LINT_FILTER} ${LIT_TESTS}/tsan/*.cc & - -# MSan -MSAN_RTL=${COMPILER_RT}/lib/msan -run_lint ${MSAN_RTL_LINT_FILTER} ${MSAN_RTL}/*.cc \ - ${MSAN_RTL}/*.h & - -# LSan -LSAN_RTL=${COMPILER_RT}/lib/lsan -run_lint ${LSAN_RTL_LINT_FILTER} ${LSAN_RTL}/*.cc \ - ${LSAN_RTL}/*.h & -run_lint ${LSAN_LIT_TEST_LINT_FILTER} ${LIT_TESTS}/lsan/*/*.cc & - -# DFSan -DFSAN_RTL=${COMPILER_RT}/lib/dfsan -run_lint ${DFSAN_RTL_LINT_FILTER} ${DFSAN_RTL}/*.cc \ - ${DFSAN_RTL}/*.h & -${DFSAN_RTL}/scripts/check_custom_wrappers.sh >> $ERROR_LOG - -# Misc files -FILES=${COMMON_RTL}/*.inc -TMPFILES="" -for FILE in $FILES; do - TMPFILE="$(${MKTEMP}).$(basename ${FILE}).cc" - cp -f $FILE $TMPFILE - run_lint ${COMMON_RTL_INC_LINT_FILTER} $TMPFILE & - TMPFILES="$TMPFILES $TMPFILE" -done - -wait - -for temp in $TMPFILES; do - rm -f $temp -done - -if [ -s $ERROR_LOG ]; then - cat $ERROR_LOG - exit 1 -fi - -exit 0 diff --git a/contrib/compiler-rt/lib/sanitizer_common/scripts/cpplint.py b/contrib/compiler-rt/lib/sanitizer_common/scripts/cpplint.py deleted file mode 100755 index d45c47f7ed0c..000000000000 --- a/contrib/compiler-rt/lib/sanitizer_common/scripts/cpplint.py +++ /dev/null @@ -1,4024 +0,0 @@ -#!/usr/bin/env python -# -# Copyright (c) 2009 Google Inc. All rights reserved. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions are -# met: -# -# * Redistributions of source code must retain the above copyright -# notice, this list of conditions and the following disclaimer. -# * Redistributions in binary form must reproduce the above -# copyright notice, this list of conditions and the following disclaimer -# in the documentation and/or other materials provided with the -# distribution. -# * Neither the name of Google Inc. nor the names of its -# contributors may be used to endorse or promote products derived from -# this software without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -# Here are some issues that I've had people identify in my code during reviews, -# that I think are possible to flag automatically in a lint tool. If these were -# caught by lint, it would save time both for myself and that of my reviewers. -# Most likely, some of these are beyond the scope of the current lint framework, -# but I think it is valuable to retain these wish-list items even if they cannot -# be immediately implemented. -# -# Suggestions -# ----------- -# - Check for no 'explicit' for multi-arg ctor -# - Check for boolean assign RHS in parens -# - Check for ctor initializer-list colon position and spacing -# - Check that if there's a ctor, there should be a dtor -# - Check accessors that return non-pointer member variables are -# declared const -# - Check accessors that return non-const pointer member vars are -# *not* declared const -# - Check for using public includes for testing -# - Check for spaces between brackets in one-line inline method -# - Check for no assert() -# - Check for spaces surrounding operators -# - Check for 0 in pointer context (should be NULL) -# - Check for 0 in char context (should be '\0') -# - Check for camel-case method name conventions for methods -# that are not simple inline getters and setters -# - Do not indent namespace contents -# - Avoid inlining non-trivial constructors in header files -# - Check for old-school (void) cast for call-sites of functions -# ignored return value -# - Check gUnit usage of anonymous namespace -# - Check for class declaration order (typedefs, consts, enums, -# ctor(s?), dtor, friend declarations, methods, member vars) -# - -"""Does google-lint on c++ files. - -The goal of this script is to identify places in the code that *may* -be in non-compliance with google style. It does not attempt to fix -up these problems -- the point is to educate. It does also not -attempt to find all problems, or to ensure that everything it does -find is legitimately a problem. - -In particular, we can get very confused by /* and // inside strings! -We do a small hack, which is to ignore //'s with "'s after them on the -same line, but it is far from perfect (in either direction). -""" - -import codecs -import copy -import getopt -import math # for log -import os -import re -import sre_compile -import string -import sys -import unicodedata - - -_USAGE = """ -Syntax: cpplint.py [--verbose=#] [--output=vs7] [--filter=-x,+y,...] - [--counting=total|toplevel|detailed] - <file> [file] ... - - The style guidelines this tries to follow are those in - http://google-styleguide.googlecode.com/svn/trunk/cppguide.xml - - Every problem is given a confidence score from 1-5, with 5 meaning we are - certain of the problem, and 1 meaning it could be a legitimate construct. - This will miss some errors, and is not a substitute for a code review. - - To suppress false-positive errors of a certain category, add a - 'NOLINT(category)' comment to the line. NOLINT or NOLINT(*) - suppresses errors of all categories on that line. - - The files passed in will be linted; at least one file must be provided. - Linted extensions are .cc, .cpp, and .h. Other file types will be ignored. - - Flags: - - output=vs7 - By default, the output is formatted to ease emacs parsing. Visual Studio - compatible output (vs7) may also be used. Other formats are unsupported. - - verbose=# - Specify a number 0-5 to restrict errors to certain verbosity levels. - - filter=-x,+y,... - Specify a comma-separated list of category-filters to apply: only - error messages whose category names pass the filters will be printed. - (Category names are printed with the message and look like - "[whitespace/indent]".) Filters are evaluated left to right. - "-FOO" and "FOO" means "do not print categories that start with FOO". - "+FOO" means "do print categories that start with FOO". - - Examples: --filter=-whitespace,+whitespace/braces - --filter=whitespace,runtime/printf,+runtime/printf_format - --filter=-,+build/include_what_you_use - - To see a list of all the categories used in cpplint, pass no arg: - --filter= - - counting=total|toplevel|detailed - The total number of errors found is always printed. If - 'toplevel' is provided, then the count of errors in each of - the top-level categories like 'build' and 'whitespace' will - also be printed. If 'detailed' is provided, then a count - is provided for each category like 'build/class'. - - root=subdir - The root directory used for deriving header guard CPP variable. - By default, the header guard CPP variable is calculated as the relative - path to the directory that contains .git, .hg, or .svn. When this flag - is specified, the relative path is calculated from the specified - directory. If the specified directory does not exist, this flag is - ignored. - - Examples: - Assuing that src/.git exists, the header guard CPP variables for - src/chrome/browser/ui/browser.h are: - - No flag => CHROME_BROWSER_UI_BROWSER_H_ - --root=chrome => BROWSER_UI_BROWSER_H_ - --root=chrome/browser => UI_BROWSER_H_ -""" - -# We categorize each error message we print. Here are the categories. -# We want an explicit list so we can list them all in cpplint --filter=. -# If you add a new error message with a new category, add it to the list -# here! cpplint_unittest.py should tell you if you forget to do this. -# \ used for clearer layout -- pylint: disable-msg=C6013 -_ERROR_CATEGORIES = [ - 'build/class', - 'build/deprecated', - 'build/endif_comment', - 'build/explicit_make_pair', - 'build/forward_decl', - 'build/header_guard', - 'build/include', - 'build/include_alpha', - 'build/include_order', - 'build/include_what_you_use', - 'build/namespaces', - 'build/printf_format', - 'build/storage_class', - 'legal/copyright', - 'readability/alt_tokens', - 'readability/braces', - 'readability/casting', - 'readability/check', - 'readability/constructors', - 'readability/fn_size', - 'readability/function', - 'readability/multiline_comment', - 'readability/multiline_string', - 'readability/namespace', - 'readability/nolint', - 'readability/streams', - 'readability/todo', - 'readability/utf8', - 'runtime/arrays', - 'runtime/casting', - 'runtime/explicit', - 'runtime/int', - 'runtime/init', - 'runtime/invalid_increment', - 'runtime/member_string_references', - 'runtime/memset', - 'runtime/operator', - 'runtime/printf', - 'runtime/printf_format', - 'runtime/references', - 'runtime/rtti', - 'runtime/sizeof', - 'runtime/string', - 'runtime/threadsafe_fn', - 'whitespace/blank_line', - 'whitespace/braces', - 'whitespace/comma', - 'whitespace/comments', - 'whitespace/empty_loop_body', - 'whitespace/end_of_line', - 'whitespace/ending_newline', - 'whitespace/forcolon', - 'whitespace/indent', - 'whitespace/labels', - 'whitespace/line_length', - 'whitespace/newline', - 'whitespace/operators', - 'whitespace/parens', - 'whitespace/semicolon', - 'whitespace/tab', - 'whitespace/todo' - ] - -# The default state of the category filter. This is overrided by the --filter= -# flag. By default all errors are on, so only add here categories that should be -# off by default (i.e., categories that must be enabled by the --filter= flags). -# All entries here should start with a '-' or '+', as in the --filter= flag. -_DEFAULT_FILTERS = ['-build/include_alpha'] - -# We used to check for high-bit characters, but after much discussion we -# decided those were OK, as long as they were in UTF-8 and didn't represent -# hard-coded international strings, which belong in a separate i18n file. - -# Headers that we consider STL headers. -_STL_HEADERS = frozenset([ - 'algobase.h', 'algorithm', 'alloc.h', 'bitset', 'deque', 'exception', - 'function.h', 'functional', 'hash_map', 'hash_map.h', 'hash_set', - 'hash_set.h', 'iterator', 'list', 'list.h', 'map', 'memory', 'new', - 'pair.h', 'pthread_alloc', 'queue', 'set', 'set.h', 'sstream', 'stack', - 'stl_alloc.h', 'stl_relops.h', 'type_traits.h', - 'utility', 'vector', 'vector.h', - ]) - - -# Non-STL C++ system headers. -_CPP_HEADERS = frozenset([ - 'algo.h', 'builtinbuf.h', 'bvector.h', 'cassert', 'cctype', - 'cerrno', 'cfloat', 'ciso646', 'climits', 'clocale', 'cmath', - 'complex', 'complex.h', 'csetjmp', 'csignal', 'cstdarg', 'cstddef', - 'cstdio', 'cstdlib', 'cstring', 'ctime', 'cwchar', 'cwctype', - 'defalloc.h', 'deque.h', 'editbuf.h', 'exception', 'fstream', - 'fstream.h', 'hashtable.h', 'heap.h', 'indstream.h', 'iomanip', - 'iomanip.h', 'ios', 'iosfwd', 'iostream', 'iostream.h', 'istream', - 'istream.h', 'iterator.h', 'limits', 'map.h', 'multimap.h', 'multiset.h', - 'numeric', 'ostream', 'ostream.h', 'parsestream.h', 'pfstream.h', - 'PlotFile.h', 'procbuf.h', 'pthread_alloc.h', 'rope', 'rope.h', - 'ropeimpl.h', 'SFile.h', 'slist', 'slist.h', 'stack.h', 'stdexcept', - 'stdiostream.h', 'streambuf', 'streambuf.h', 'stream.h', 'strfile.h', - 'string', 'strstream', 'strstream.h', 'tempbuf.h', 'tree.h', 'typeinfo', - 'valarray', - ]) - - -# Assertion macros. These are defined in base/logging.h and -# testing/base/gunit.h. Note that the _M versions need to come first -# for substring matching to work. -_CHECK_MACROS = [ - 'DCHECK', 'CHECK', - 'EXPECT_TRUE_M', 'EXPECT_TRUE', - 'ASSERT_TRUE_M', 'ASSERT_TRUE', - 'EXPECT_FALSE_M', 'EXPECT_FALSE', - 'ASSERT_FALSE_M', 'ASSERT_FALSE', - ] - -# Replacement macros for CHECK/DCHECK/EXPECT_TRUE/EXPECT_FALSE -_CHECK_REPLACEMENT = dict([(m, {}) for m in _CHECK_MACROS]) - -for op, replacement in [('==', 'EQ'), ('!=', 'NE'), - ('>=', 'GE'), ('>', 'GT'), - ('<=', 'LE'), ('<', 'LT')]: - _CHECK_REPLACEMENT['DCHECK'][op] = 'DCHECK_%s' % replacement - _CHECK_REPLACEMENT['CHECK'][op] = 'CHECK_%s' % replacement - _CHECK_REPLACEMENT['EXPECT_TRUE'][op] = 'EXPECT_%s' % replacement - _CHECK_REPLACEMENT['ASSERT_TRUE'][op] = 'ASSERT_%s' % replacement - _CHECK_REPLACEMENT['EXPECT_TRUE_M'][op] = 'EXPECT_%s_M' % replacement - _CHECK_REPLACEMENT['ASSERT_TRUE_M'][op] = 'ASSERT_%s_M' % replacement - -for op, inv_replacement in [('==', 'NE'), ('!=', 'EQ'), - ('>=', 'LT'), ('>', 'LE'), - ('<=', 'GT'), ('<', 'GE')]: - _CHECK_REPLACEMENT['EXPECT_FALSE'][op] = 'EXPECT_%s' % inv_replacement - _CHECK_REPLACEMENT['ASSERT_FALSE'][op] = 'ASSERT_%s' % inv_replacement - _CHECK_REPLACEMENT['EXPECT_FALSE_M'][op] = 'EXPECT_%s_M' % inv_replacement - _CHECK_REPLACEMENT['ASSERT_FALSE_M'][op] = 'ASSERT_%s_M' % inv_replacement - -# Alternative tokens and their replacements. For full list, see section 2.5 -# Alternative tokens [lex.digraph] in the C++ standard. -# -# Digraphs (such as '%:') are not included here since it's a mess to -# match those on a word boundary. -_ALT_TOKEN_REPLACEMENT = { - 'and': '&&', - 'bitor': '|', - 'or': '||', - 'xor': '^', - 'compl': '~', - 'bitand': '&', - 'and_eq': '&=', - 'or_eq': '|=', - 'xor_eq': '^=', - 'not': '!', - 'not_eq': '!=' - } - -# Compile regular expression that matches all the above keywords. The "[ =()]" -# bit is meant to avoid matching these keywords outside of boolean expressions. -# -# False positives include C-style multi-line comments (http://go/nsiut ) -# and multi-line strings (http://go/beujw ), but those have always been -# troublesome for cpplint. -_ALT_TOKEN_REPLACEMENT_PATTERN = re.compile( - r'[ =()](' + ('|'.join(_ALT_TOKEN_REPLACEMENT.keys())) + r')(?=[ (]|$)') - - -# These constants define types of headers for use with -# _IncludeState.CheckNextIncludeOrder(). -_C_SYS_HEADER = 1 -_CPP_SYS_HEADER = 2 -_LIKELY_MY_HEADER = 3 -_POSSIBLE_MY_HEADER = 4 -_OTHER_HEADER = 5 - -# These constants define the current inline assembly state -_NO_ASM = 0 # Outside of inline assembly block -_INSIDE_ASM = 1 # Inside inline assembly block -_END_ASM = 2 # Last line of inline assembly block -_BLOCK_ASM = 3 # The whole block is an inline assembly block - -# Match start of assembly blocks -_MATCH_ASM = re.compile(r'^\s*(?:asm|_asm|__asm|__asm__)' - r'(?:\s+(volatile|__volatile__))?' - r'\s*[{(]') - - -_regexp_compile_cache = {} - -# Finds occurrences of NOLINT or NOLINT(...). -_RE_SUPPRESSION = re.compile(r'\bNOLINT\b(\([^)]*\))?') - -# {str, set(int)}: a map from error categories to sets of linenumbers -# on which those errors are expected and should be suppressed. -_error_suppressions = {} - -# The root directory used for deriving header guard CPP variable. -# This is set by --root flag. -_root = None - -def ParseNolintSuppressions(filename, raw_line, linenum, error): - """Updates the global list of error-suppressions. - - Parses any NOLINT comments on the current line, updating the global - error_suppressions store. Reports an error if the NOLINT comment - was malformed. - - Args: - filename: str, the name of the input file. - raw_line: str, the line of input text, with comments. - linenum: int, the number of the current line. - error: function, an error handler. - """ - # FIXME(adonovan): "NOLINT(" is misparsed as NOLINT(*). - matched = _RE_SUPPRESSION.search(raw_line) - if matched: - category = matched.group(1) - if category in (None, '(*)'): # => "suppress all" - _error_suppressions.setdefault(None, set()).add(linenum) - else: - if category.startswith('(') and category.endswith(')'): - category = category[1:-1] - if category in _ERROR_CATEGORIES: - _error_suppressions.setdefault(category, set()).add(linenum) - else: - error(filename, linenum, 'readability/nolint', 5, - 'Unknown NOLINT error category: %s' % category) - - -def ResetNolintSuppressions(): - "Resets the set of NOLINT suppressions to empty." - _error_suppressions.clear() - - -def IsErrorSuppressedByNolint(category, linenum): - """Returns true if the specified error category is suppressed on this line. - - Consults the global error_suppressions map populated by - ParseNolintSuppressions/ResetNolintSuppressions. - - Args: - category: str, the category of the error. - linenum: int, the current line number. - Returns: - bool, True iff the error should be suppressed due to a NOLINT comment. - """ - return (linenum in _error_suppressions.get(category, set()) or - linenum in _error_suppressions.get(None, set())) - -def Match(pattern, s): - """Matches the string with the pattern, caching the compiled regexp.""" - # The regexp compilation caching is inlined in both Match and Search for - # performance reasons; factoring it out into a separate function turns out - # to be noticeably expensive. - if not pattern in _regexp_compile_cache: - _regexp_compile_cache[pattern] = sre_compile.compile(pattern) - return _regexp_compile_cache[pattern].match(s) - - -def Search(pattern, s): - """Searches the string for the pattern, caching the compiled regexp.""" - if not pattern in _regexp_compile_cache: - _regexp_compile_cache[pattern] = sre_compile.compile(pattern) - return _regexp_compile_cache[pattern].search(s) - - -class _IncludeState(dict): - """Tracks line numbers for includes, and the order in which includes appear. - - As a dict, an _IncludeState object serves as a mapping between include - filename and line number on which that file was included. - - Call CheckNextIncludeOrder() once for each header in the file, passing - in the type constants defined above. Calls in an illegal order will - raise an _IncludeError with an appropriate error message. - - """ - # self._section will move monotonically through this set. If it ever - # needs to move backwards, CheckNextIncludeOrder will raise an error. - _INITIAL_SECTION = 0 - _MY_H_SECTION = 1 - _C_SECTION = 2 - _CPP_SECTION = 3 - _OTHER_H_SECTION = 4 - - _TYPE_NAMES = { - _C_SYS_HEADER: 'C system header', - _CPP_SYS_HEADER: 'C++ system header', - _LIKELY_MY_HEADER: 'header this file implements', - _POSSIBLE_MY_HEADER: 'header this file may implement', - _OTHER_HEADER: 'other header', - } - _SECTION_NAMES = { - _INITIAL_SECTION: "... nothing. (This can't be an error.)", - _MY_H_SECTION: 'a header this file implements', - _C_SECTION: 'C system header', - _CPP_SECTION: 'C++ system header', - _OTHER_H_SECTION: 'other header', - } - - def __init__(self): - dict.__init__(self) - # The name of the current section. - self._section = self._INITIAL_SECTION - # The path of last found header. - self._last_header = '' - - def CanonicalizeAlphabeticalOrder(self, header_path): - """Returns a path canonicalized for alphabetical comparison. - - - replaces "-" with "_" so they both cmp the same. - - removes '-inl' since we don't require them to be after the main header. - - lowercase everything, just in case. - - Args: - header_path: Path to be canonicalized. - - Returns: - Canonicalized path. - """ - return header_path.replace('-inl.h', '.h').replace('-', '_').lower() - - def IsInAlphabeticalOrder(self, header_path): - """Check if a header is in alphabetical order with the previous header. - - Args: - header_path: Header to be checked. - - Returns: - Returns true if the header is in alphabetical order. - """ - canonical_header = self.CanonicalizeAlphabeticalOrder(header_path) - if self._last_header > canonical_header: - return False - self._last_header = canonical_header - return True - - def CheckNextIncludeOrder(self, header_type): - """Returns a non-empty error message if the next header is out of order. - - This function also updates the internal state to be ready to check - the next include. - - Args: - header_type: One of the _XXX_HEADER constants defined above. - - Returns: - The empty string if the header is in the right order, or an - error message describing what's wrong. - - """ - error_message = ('Found %s after %s' % - (self._TYPE_NAMES[header_type], - self._SECTION_NAMES[self._section])) - - last_section = self._section - - if header_type == _C_SYS_HEADER: - if self._section <= self._C_SECTION: - self._section = self._C_SECTION - else: - self._last_header = '' - return error_message - elif header_type == _CPP_SYS_HEADER: - if self._section <= self._CPP_SECTION: - self._section = self._CPP_SECTION - else: - self._last_header = '' - return error_message - elif header_type == _LIKELY_MY_HEADER: - if self._section <= self._MY_H_SECTION: - self._section = self._MY_H_SECTION - else: - self._section = self._OTHER_H_SECTION - elif header_type == _POSSIBLE_MY_HEADER: - if self._section <= self._MY_H_SECTION: - self._section = self._MY_H_SECTION - else: - # This will always be the fallback because we're not sure - # enough that the header is associated with this file. - self._section = self._OTHER_H_SECTION - else: - assert header_type == _OTHER_HEADER - self._section = self._OTHER_H_SECTION - - if last_section != self._section: - self._last_header = '' - - return '' - - -class _CppLintState(object): - """Maintains module-wide state..""" - - def __init__(self): - self.verbose_level = 1 # global setting. - self.error_count = 0 # global count of reported errors - # filters to apply when emitting error messages - self.filters = _DEFAULT_FILTERS[:] - self.counting = 'total' # In what way are we counting errors? - self.errors_by_category = {} # string to int dict storing error counts - - # output format: - # "emacs" - format that emacs can parse (default) - # "vs7" - format that Microsoft Visual Studio 7 can parse - self.output_format = 'emacs' - - def SetOutputFormat(self, output_format): - """Sets the output format for errors.""" - self.output_format = output_format - - def SetVerboseLevel(self, level): - """Sets the module's verbosity, and returns the previous setting.""" - last_verbose_level = self.verbose_level - self.verbose_level = level - return last_verbose_level - - def SetCountingStyle(self, counting_style): - """Sets the module's counting options.""" - self.counting = counting_style - - def SetFilters(self, filters): - """Sets the error-message filters. - - These filters are applied when deciding whether to emit a given - error message. - - Args: - filters: A string of comma-separated filters (eg "+whitespace/indent"). - Each filter should start with + or -; else we die. - - Raises: - ValueError: The comma-separated filters did not all start with '+' or '-'. - E.g. "-,+whitespace,-whitespace/indent,whitespace/badfilter" - """ - # Default filters always have less priority than the flag ones. - self.filters = _DEFAULT_FILTERS[:] - for filt in filters.split(','): - clean_filt = filt.strip() - if clean_filt: - self.filters.append(clean_filt) - for filt in self.filters: - if not (filt.startswith('+') or filt.startswith('-')): - raise ValueError('Every filter in --filters must start with + or -' - ' (%s does not)' % filt) - - def ResetErrorCounts(self): - """Sets the module's error statistic back to zero.""" - self.error_count = 0 - self.errors_by_category = {} - - def IncrementErrorCount(self, category): - """Bumps the module's error statistic.""" - self.error_count += 1 - if self.counting in ('toplevel', 'detailed'): - if self.counting != 'detailed': - category = category.split('/')[0] - if category not in self.errors_by_category: - self.errors_by_category[category] = 0 - self.errors_by_category[category] += 1 - - def PrintErrorCounts(self): - """Print a summary of errors by category, and the total.""" - for category, count in self.errors_by_category.iteritems(): - sys.stderr.write('Category \'%s\' errors found: %d\n' % - (category, count)) - sys.stderr.write('Total errors found: %d\n' % self.error_count) - -_cpplint_state = _CppLintState() - - -def _OutputFormat(): - """Gets the module's output format.""" - return _cpplint_state.output_format - - -def _SetOutputFormat(output_format): - """Sets the module's output format.""" - _cpplint_state.SetOutputFormat(output_format) - - -def _VerboseLevel(): - """Returns the module's verbosity setting.""" - return _cpplint_state.verbose_level - - -def _SetVerboseLevel(level): - """Sets the module's verbosity, and returns the previous setting.""" - return _cpplint_state.SetVerboseLevel(level) - - -def _SetCountingStyle(level): - """Sets the module's counting options.""" - _cpplint_state.SetCountingStyle(level) - - -def _Filters(): - """Returns the module's list of output filters, as a list.""" - return _cpplint_state.filters - - -def _SetFilters(filters): - """Sets the module's error-message filters. - - These filters are applied when deciding whether to emit a given - error message. - - Args: - filters: A string of comma-separated filters (eg "whitespace/indent"). - Each filter should start with + or -; else we die. - """ - _cpplint_state.SetFilters(filters) - - -class _FunctionState(object): - """Tracks current function name and the number of lines in its body.""" - - _NORMAL_TRIGGER = 250 # for --v=0, 500 for --v=1, etc. - _TEST_TRIGGER = 400 # about 50% more than _NORMAL_TRIGGER. - - def __init__(self): - self.in_a_function = False - self.lines_in_function = 0 - self.current_function = '' - - def Begin(self, function_name): - """Start analyzing function body. - - Args: - function_name: The name of the function being tracked. - """ - self.in_a_function = True - self.lines_in_function = 0 - self.current_function = function_name - - def Count(self): - """Count line in current function body.""" - if self.in_a_function: - self.lines_in_function += 1 - - def Check(self, error, filename, linenum): - """Report if too many lines in function body. - - Args: - error: The function to call with any errors found. - filename: The name of the current file. - linenum: The number of the line to check. - """ - if Match(r'T(EST|est)', self.current_function): - base_trigger = self._TEST_TRIGGER - else: - base_trigger = self._NORMAL_TRIGGER - trigger = base_trigger * 2**_VerboseLevel() - - if self.lines_in_function > trigger: - error_level = int(math.log(self.lines_in_function / base_trigger, 2)) - # 50 => 0, 100 => 1, 200 => 2, 400 => 3, 800 => 4, 1600 => 5, ... - if error_level > 5: - error_level = 5 - error(filename, linenum, 'readability/fn_size', error_level, - 'Small and focused functions are preferred:' - ' %s has %d non-comment lines' - ' (error triggered by exceeding %d lines).' % ( - self.current_function, self.lines_in_function, trigger)) - - def End(self): - """Stop analyzing function body.""" - self.in_a_function = False - - -class _IncludeError(Exception): - """Indicates a problem with the include order in a file.""" - pass - - -class FileInfo: - """Provides utility functions for filenames. - - FileInfo provides easy access to the components of a file's path - relative to the project root. - """ - - def __init__(self, filename): - self._filename = filename - - def FullName(self): - """Make Windows paths like Unix.""" - return os.path.abspath(self._filename).replace('\\', '/') - - def RepositoryName(self): - """FullName after removing the local path to the repository. - - If we have a real absolute path name here we can try to do something smart: - detecting the root of the checkout and truncating /path/to/checkout from - the name so that we get header guards that don't include things like - "C:\Documents and Settings\..." or "/home/username/..." in them and thus - people on different computers who have checked the source out to different - locations won't see bogus errors. - """ - fullname = self.FullName() - - if os.path.exists(fullname): - project_dir = os.path.dirname(fullname) - - if os.path.exists(os.path.join(project_dir, ".svn")): - # If there's a .svn file in the current directory, we recursively look - # up the directory tree for the top of the SVN checkout - root_dir = project_dir - one_up_dir = os.path.dirname(root_dir) - while os.path.exists(os.path.join(one_up_dir, ".svn")): - root_dir = os.path.dirname(root_dir) - one_up_dir = os.path.dirname(one_up_dir) - - prefix = os.path.commonprefix([root_dir, project_dir]) - return fullname[len(prefix) + 1:] - - # Not SVN <= 1.6? Try to find a git, hg, or svn top level directory by - # searching up from the current path. - root_dir = os.path.dirname(fullname) - while (root_dir != os.path.dirname(root_dir) and - not os.path.exists(os.path.join(root_dir, ".git")) and - not os.path.exists(os.path.join(root_dir, ".hg")) and - not os.path.exists(os.path.join(root_dir, ".svn"))): - root_dir = os.path.dirname(root_dir) - - if (os.path.exists(os.path.join(root_dir, ".git")) or - os.path.exists(os.path.join(root_dir, ".hg")) or - os.path.exists(os.path.join(root_dir, ".svn"))): - prefix = os.path.commonprefix([root_dir, project_dir]) - return fullname[len(prefix) + 1:] - - # Don't know what to do; header guard warnings may be wrong... - return fullname - - def Split(self): - """Splits the file into the directory, basename, and extension. - - For 'chrome/browser/browser.cc', Split() would - return ('chrome/browser', 'browser', '.cc') - - Returns: - A tuple of (directory, basename, extension). - """ - - googlename = self.RepositoryName() - project, rest = os.path.split(googlename) - return (project,) + os.path.splitext(rest) - - def BaseName(self): - """File base name - text after the final slash, before the final period.""" - return self.Split()[1] - - def Extension(self): - """File extension - text following the final period.""" - return self.Split()[2] - - def NoExtension(self): - """File has no source file extension.""" - return '/'.join(self.Split()[0:2]) - - def IsSource(self): - """File has a source file extension.""" - return self.Extension()[1:] in ('c', 'cc', 'cpp', 'cxx') - - -def _ShouldPrintError(category, confidence, linenum): - """If confidence >= verbose, category passes filter and is not suppressed.""" - - # There are three ways we might decide not to print an error message: - # a "NOLINT(category)" comment appears in the source, - # the verbosity level isn't high enough, or the filters filter it out. - if IsErrorSuppressedByNolint(category, linenum): - return False - if confidence < _cpplint_state.verbose_level: - return False - - is_filtered = False - for one_filter in _Filters(): - if one_filter.startswith('-'): - if category.startswith(one_filter[1:]): - is_filtered = True - elif one_filter.startswith('+'): - if category.startswith(one_filter[1:]): - is_filtered = False - else: - assert False # should have been checked for in SetFilter. - if is_filtered: - return False - - return True - - -def Error(filename, linenum, category, confidence, message): - """Logs the fact we've found a lint error. - - We log where the error was found, and also our confidence in the error, - that is, how certain we are this is a legitimate style regression, and - not a misidentification or a use that's sometimes justified. - - False positives can be suppressed by the use of - "cpplint(category)" comments on the offending line. These are - parsed into _error_suppressions. - - Args: - filename: The name of the file containing the error. - linenum: The number of the line containing the error. - category: A string used to describe the "category" this bug - falls under: "whitespace", say, or "runtime". Categories - may have a hierarchy separated by slashes: "whitespace/indent". - confidence: A number from 1-5 representing a confidence score for - the error, with 5 meaning that we are certain of the problem, - and 1 meaning that it could be a legitimate construct. - message: The error message. - """ - if _ShouldPrintError(category, confidence, linenum): - _cpplint_state.IncrementErrorCount(category) - if _cpplint_state.output_format == 'vs7': - sys.stderr.write('%s(%s): %s [%s] [%d]\n' % ( - filename, linenum, message, category, confidence)) - elif _cpplint_state.output_format == 'eclipse': - sys.stderr.write('%s:%s: warning: %s [%s] [%d]\n' % ( - filename, linenum, message, category, confidence)) - else: - sys.stderr.write('%s:%s: %s [%s] [%d]\n' % ( - filename, linenum, message, category, confidence)) - - -# Matches standard C++ escape esequences per 2.13.2.3 of the C++ standard. -_RE_PATTERN_CLEANSE_LINE_ESCAPES = re.compile( - r'\\([abfnrtv?"\\\']|\d+|x[0-9a-fA-F]+)') -# Matches strings. Escape codes should already be removed by ESCAPES. -_RE_PATTERN_CLEANSE_LINE_DOUBLE_QUOTES = re.compile(r'"[^"]*"') -# Matches characters. Escape codes should already be removed by ESCAPES. -_RE_PATTERN_CLEANSE_LINE_SINGLE_QUOTES = re.compile(r"'.'") -# Matches multi-line C++ comments. -# This RE is a little bit more complicated than one might expect, because we -# have to take care of space removals tools so we can handle comments inside -# statements better. -# The current rule is: We only clear spaces from both sides when we're at the -# end of the line. Otherwise, we try to remove spaces from the right side, -# if this doesn't work we try on left side but only if there's a non-character -# on the right. -_RE_PATTERN_CLEANSE_LINE_C_COMMENTS = re.compile( - r"""(\s*/\*.*\*/\s*$| - /\*.*\*/\s+| - \s+/\*.*\*/(?=\W)| - /\*.*\*/)""", re.VERBOSE) - - -def IsCppString(line): - """Does line terminate so, that the next symbol is in string constant. - - This function does not consider single-line nor multi-line comments. - - Args: - line: is a partial line of code starting from the 0..n. - - Returns: - True, if next character appended to 'line' is inside a - string constant. - """ - - line = line.replace(r'\\', 'XX') # after this, \\" does not match to \" - return ((line.count('"') - line.count(r'\"') - line.count("'\"'")) & 1) == 1 - - -def FindNextMultiLineCommentStart(lines, lineix): - """Find the beginning marker for a multiline comment.""" - while lineix < len(lines): - if lines[lineix].strip().startswith('/*'): - # Only return this marker if the comment goes beyond this line - if lines[lineix].strip().find('*/', 2) < 0: - return lineix - lineix += 1 - return len(lines) - - -def FindNextMultiLineCommentEnd(lines, lineix): - """We are inside a comment, find the end marker.""" - while lineix < len(lines): - if lines[lineix].strip().endswith('*/'): - return lineix - lineix += 1 - return len(lines) - - -def RemoveMultiLineCommentsFromRange(lines, begin, end): - """Clears a range of lines for multi-line comments.""" - # Having // dummy comments makes the lines non-empty, so we will not get - # unnecessary blank line warnings later in the code. - for i in range(begin, end): - lines[i] = '// dummy' - - -def RemoveMultiLineComments(filename, lines, error): - """Removes multiline (c-style) comments from lines.""" - lineix = 0 - while lineix < len(lines): - lineix_begin = FindNextMultiLineCommentStart(lines, lineix) - if lineix_begin >= len(lines): - return - lineix_end = FindNextMultiLineCommentEnd(lines, lineix_begin) - if lineix_end >= len(lines): - error(filename, lineix_begin + 1, 'readability/multiline_comment', 5, - 'Could not find end of multi-line comment') - return - RemoveMultiLineCommentsFromRange(lines, lineix_begin, lineix_end + 1) - lineix = lineix_end + 1 - - -def CleanseComments(line): - """Removes //-comments and single-line C-style /* */ comments. - - Args: - line: A line of C++ source. - - Returns: - The line with single-line comments removed. - """ - commentpos = line.find('//') - if commentpos != -1 and not IsCppString(line[:commentpos]): - line = line[:commentpos].rstrip() - # get rid of /* ... */ - return _RE_PATTERN_CLEANSE_LINE_C_COMMENTS.sub('', line) - - -class CleansedLines(object): - """Holds 3 copies of all lines with different preprocessing applied to them. - - 1) elided member contains lines without strings and comments, - 2) lines member contains lines without comments, and - 3) raw_lines member contains all the lines without processing. - All these three members are of <type 'list'>, and of the same length. - """ - - def __init__(self, lines): - self.elided = [] - self.lines = [] - self.raw_lines = lines - self.num_lines = len(lines) - for linenum in range(len(lines)): - self.lines.append(CleanseComments(lines[linenum])) - elided = self._CollapseStrings(lines[linenum]) - self.elided.append(CleanseComments(elided)) - - def NumLines(self): - """Returns the number of lines represented.""" - return self.num_lines - - @staticmethod - def _CollapseStrings(elided): - """Collapses strings and chars on a line to simple "" or '' blocks. - - We nix strings first so we're not fooled by text like '"http://"' - - Args: - elided: The line being processed. - - Returns: - The line with collapsed strings. - """ - if not _RE_PATTERN_INCLUDE.match(elided): - # Remove escaped characters first to make quote/single quote collapsing - # basic. Things that look like escaped characters shouldn't occur - # outside of strings and chars. - elided = _RE_PATTERN_CLEANSE_LINE_ESCAPES.sub('', elided) - elided = _RE_PATTERN_CLEANSE_LINE_SINGLE_QUOTES.sub("''", elided) - elided = _RE_PATTERN_CLEANSE_LINE_DOUBLE_QUOTES.sub('""', elided) - return elided - - -def FindEndOfExpressionInLine(line, startpos, depth, startchar, endchar): - """Find the position just after the matching endchar. - - Args: - line: a CleansedLines line. - startpos: start searching at this position. - depth: nesting level at startpos. - startchar: expression opening character. - endchar: expression closing character. - - Returns: - Index just after endchar. - """ - for i in xrange(startpos, len(line)): - if line[i] == startchar: - depth += 1 - elif line[i] == endchar: - depth -= 1 - if depth == 0: - return i + 1 - return -1 - - -def CloseExpression(clean_lines, linenum, pos): - """If input points to ( or { or [, finds the position that closes it. - - If lines[linenum][pos] points to a '(' or '{' or '[', finds the - linenum/pos that correspond to the closing of the expression. - - Args: - clean_lines: A CleansedLines instance containing the file. - linenum: The number of the line to check. - pos: A position on the line. - - Returns: - A tuple (line, linenum, pos) pointer *past* the closing brace, or - (line, len(lines), -1) if we never find a close. Note we ignore - strings and comments when matching; and the line we return is the - 'cleansed' line at linenum. - """ - - line = clean_lines.elided[linenum] - startchar = line[pos] - if startchar not in '({[': - return (line, clean_lines.NumLines(), -1) - if startchar == '(': endchar = ')' - if startchar == '[': endchar = ']' - if startchar == '{': endchar = '}' - - # Check first line - end_pos = FindEndOfExpressionInLine(line, pos, 0, startchar, endchar) - if end_pos > -1: - return (line, linenum, end_pos) - tail = line[pos:] - num_open = tail.count(startchar) - tail.count(endchar) - while linenum < clean_lines.NumLines() - 1: - linenum += 1 - line = clean_lines.elided[linenum] - delta = line.count(startchar) - line.count(endchar) - if num_open + delta <= 0: - return (line, linenum, - FindEndOfExpressionInLine(line, 0, num_open, startchar, endchar)) - num_open += delta - - # Did not find endchar before end of file, give up - return (line, clean_lines.NumLines(), -1) - -def CheckForCopyright(filename, lines, error): - """Logs an error if no Copyright message appears at the top of the file.""" - - # We'll say it should occur by line 10. Don't forget there's a - # dummy line at the front. - for line in xrange(1, min(len(lines), 11)): - if re.search(r'Copyright', lines[line], re.I): break - else: # means no copyright line was found - error(filename, 0, 'legal/copyright', 5, - 'No copyright message found. ' - 'You should have a line: "Copyright [year] <Copyright Owner>"') - - -def GetHeaderGuardCPPVariable(filename): - """Returns the CPP variable that should be used as a header guard. - - Args: - filename: The name of a C++ header file. - - Returns: - The CPP variable that should be used as a header guard in the - named file. - - """ - - # Restores original filename in case that cpplint is invoked from Emacs's - # flymake. - filename = re.sub(r'_flymake\.h$', '.h', filename) - filename = re.sub(r'/\.flymake/([^/]*)$', r'/\1', filename) - - fileinfo = FileInfo(filename) - file_path_from_root = fileinfo.RepositoryName() - if _root: - file_path_from_root = re.sub('^' + _root + os.sep, '', file_path_from_root) - return re.sub(r'[-./\s]', '_', file_path_from_root).upper() + '_' - - -def CheckForHeaderGuard(filename, lines, error): - """Checks that the file contains a header guard. - - Logs an error if no #ifndef header guard is present. For other - headers, checks that the full pathname is used. - - Args: - filename: The name of the C++ header file. - lines: An array of strings, each representing a line of the file. - error: The function to call with any errors found. - """ - - cppvar = GetHeaderGuardCPPVariable(filename) - - ifndef = None - ifndef_linenum = 0 - define = None - endif = None - endif_linenum = 0 - for linenum, line in enumerate(lines): - linesplit = line.split() - if len(linesplit) >= 2: - # find the first occurrence of #ifndef and #define, save arg - if not ifndef and linesplit[0] == '#ifndef': - # set ifndef to the header guard presented on the #ifndef line. - ifndef = linesplit[1] - ifndef_linenum = linenum - if not define and linesplit[0] == '#define': - define = linesplit[1] - # find the last occurrence of #endif, save entire line - if line.startswith('#endif'): - endif = line - endif_linenum = linenum - - if not ifndef: - error(filename, 0, 'build/header_guard', 5, - 'No #ifndef header guard found, suggested CPP variable is: %s' % - cppvar) - return - - if not define: - error(filename, 0, 'build/header_guard', 5, - 'No #define header guard found, suggested CPP variable is: %s' % - cppvar) - return - - # The guard should be PATH_FILE_H_, but we also allow PATH_FILE_H__ - # for backward compatibility. - if ifndef != cppvar: - error_level = 0 - if ifndef != cppvar + '_': - error_level = 5 - - ParseNolintSuppressions(filename, lines[ifndef_linenum], ifndef_linenum, - error) - error(filename, ifndef_linenum, 'build/header_guard', error_level, - '#ifndef header guard has wrong style, please use: %s' % cppvar) - - if define != ifndef: - error(filename, 0, 'build/header_guard', 5, - '#ifndef and #define don\'t match, suggested CPP variable is: %s' % - cppvar) - return - - if endif != ('#endif // %s' % cppvar): - error_level = 0 - if endif != ('#endif // %s' % (cppvar + '_')): - error_level = 5 - - ParseNolintSuppressions(filename, lines[endif_linenum], endif_linenum, - error) - error(filename, endif_linenum, 'build/header_guard', error_level, - '#endif line should be "#endif // %s"' % cppvar) - - -def CheckForUnicodeReplacementCharacters(filename, lines, error): - """Logs an error for each line containing Unicode replacement characters. - - These indicate that either the file contained invalid UTF-8 (likely) - or Unicode replacement characters (which it shouldn't). Note that - it's possible for this to throw off line numbering if the invalid - UTF-8 occurred adjacent to a newline. - - Args: - filename: The name of the current file. - lines: An array of strings, each representing a line of the file. - error: The function to call with any errors found. - """ - for linenum, line in enumerate(lines): - if u'\ufffd' in line: - error(filename, linenum, 'readability/utf8', 5, - 'Line contains invalid UTF-8 (or Unicode replacement character).') - - -def CheckForNewlineAtEOF(filename, lines, error): - """Logs an error if there is no newline char at the end of the file. - - Args: - filename: The name of the current file. - lines: An array of strings, each representing a line of the file. - error: The function to call with any errors found. - """ - - # The array lines() was created by adding two newlines to the - # original file (go figure), then splitting on \n. - # To verify that the file ends in \n, we just have to make sure the - # last-but-two element of lines() exists and is empty. - if len(lines) < 3 or lines[-2]: - error(filename, len(lines) - 2, 'whitespace/ending_newline', 5, - 'Could not find a newline character at the end of the file.') - - -def CheckForMultilineCommentsAndStrings(filename, clean_lines, linenum, error): - """Logs an error if we see /* ... */ or "..." that extend past one line. - - /* ... */ comments are legit inside macros, for one line. - Otherwise, we prefer // comments, so it's ok to warn about the - other. Likewise, it's ok for strings to extend across multiple - lines, as long as a line continuation character (backslash) - terminates each line. Although not currently prohibited by the C++ - style guide, it's ugly and unnecessary. We don't do well with either - in this lint program, so we warn about both. - - Args: - filename: The name of the current file. - clean_lines: A CleansedLines instance containing the file. - linenum: The number of the line to check. - error: The function to call with any errors found. - """ - line = clean_lines.elided[linenum] - - # Remove all \\ (escaped backslashes) from the line. They are OK, and the - # second (escaped) slash may trigger later \" detection erroneously. - line = line.replace('\\\\', '') - - if line.count('/*') > line.count('*/'): - error(filename, linenum, 'readability/multiline_comment', 5, - 'Complex multi-line /*...*/-style comment found. ' - 'Lint may give bogus warnings. ' - 'Consider replacing these with //-style comments, ' - 'with #if 0...#endif, ' - 'or with more clearly structured multi-line comments.') - - if (line.count('"') - line.count('\\"')) % 2: - error(filename, linenum, 'readability/multiline_string', 5, - 'Multi-line string ("...") found. This lint script doesn\'t ' - 'do well with such strings, and may give bogus warnings. They\'re ' - 'ugly and unnecessary, and you should use concatenation instead".') - - -threading_list = ( - ('asctime(', 'asctime_r('), - ('ctime(', 'ctime_r('), - ('getgrgid(', 'getgrgid_r('), - ('getgrnam(', 'getgrnam_r('), - ('getlogin(', 'getlogin_r('), - ('getpwnam(', 'getpwnam_r('), - ('getpwuid(', 'getpwuid_r('), - ('gmtime(', 'gmtime_r('), - ('localtime(', 'localtime_r('), - ('rand(', 'rand_r('), - ('readdir(', 'readdir_r('), - ('strtok(', 'strtok_r('), - ('ttyname(', 'ttyname_r('), - ) - - -def CheckPosixThreading(filename, clean_lines, linenum, error): - """Checks for calls to thread-unsafe functions. - - Much code has been originally written without consideration of - multi-threading. Also, engineers are relying on their old experience; - they have learned posix before threading extensions were added. These - tests guide the engineers to use thread-safe functions (when using - posix directly). - - Args: - filename: The name of the current file. - clean_lines: A CleansedLines instance containing the file. - linenum: The number of the line to check. - error: The function to call with any errors found. - """ - line = clean_lines.elided[linenum] - for single_thread_function, multithread_safe_function in threading_list: - ix = line.find(single_thread_function) - # Comparisons made explicit for clarity -- pylint: disable-msg=C6403 - if ix >= 0 and (ix == 0 or (not line[ix - 1].isalnum() and - line[ix - 1] not in ('_', '.', '>'))): - error(filename, linenum, 'runtime/threadsafe_fn', 2, - 'Consider using ' + multithread_safe_function + - '...) instead of ' + single_thread_function + - '...) for improved thread safety.') - - -# Matches invalid increment: *count++, which moves pointer instead of -# incrementing a value. -_RE_PATTERN_INVALID_INCREMENT = re.compile( - r'^\s*\*\w+(\+\+|--);') - - -def CheckInvalidIncrement(filename, clean_lines, linenum, error): - """Checks for invalid increment *count++. - - For example following function: - void increment_counter(int* count) { - *count++; - } - is invalid, because it effectively does count++, moving pointer, and should - be replaced with ++*count, (*count)++ or *count += 1. - - Args: - filename: The name of the current file. - clean_lines: A CleansedLines instance containing the file. - linenum: The number of the line to check. - error: The function to call with any errors found. - """ - line = clean_lines.elided[linenum] - if _RE_PATTERN_INVALID_INCREMENT.match(line): - error(filename, linenum, 'runtime/invalid_increment', 5, - 'Changing pointer instead of value (or unused value of operator*).') - - -class _BlockInfo(object): - """Stores information about a generic block of code.""" - - def __init__(self, seen_open_brace): - self.seen_open_brace = seen_open_brace - self.open_parentheses = 0 - self.inline_asm = _NO_ASM - - def CheckBegin(self, filename, clean_lines, linenum, error): - """Run checks that applies to text up to the opening brace. - - This is mostly for checking the text after the class identifier - and the "{", usually where the base class is specified. For other - blocks, there isn't much to check, so we always pass. - - Args: - filename: The name of the current file. - clean_lines: A CleansedLines instance containing the file. - linenum: The number of the line to check. - error: The function to call with any errors found. - """ - pass - - def CheckEnd(self, filename, clean_lines, linenum, error): - """Run checks that applies to text after the closing brace. - - This is mostly used for checking end of namespace comments. - - Args: - filename: The name of the current file. - clean_lines: A CleansedLines instance containing the file. - linenum: The number of the line to check. - error: The function to call with any errors found. - """ - pass - - -class _ClassInfo(_BlockInfo): - """Stores information about a class.""" - - def __init__(self, name, class_or_struct, clean_lines, linenum): - _BlockInfo.__init__(self, False) - self.name = name - self.starting_linenum = linenum - self.is_derived = False - if class_or_struct == 'struct': - self.access = 'public' - else: - self.access = 'private' - - # Try to find the end of the class. This will be confused by things like: - # class A { - # } *x = { ... - # - # But it's still good enough for CheckSectionSpacing. - self.last_line = 0 - depth = 0 - for i in range(linenum, clean_lines.NumLines()): - line = clean_lines.elided[i] - depth += line.count('{') - line.count('}') - if not depth: - self.last_line = i - break - - def CheckBegin(self, filename, clean_lines, linenum, error): - # Look for a bare ':' - if Search('(^|[^:]):($|[^:])', clean_lines.elided[linenum]): - self.is_derived = True - - -class _NamespaceInfo(_BlockInfo): - """Stores information about a namespace.""" - - def __init__(self, name, linenum): - _BlockInfo.__init__(self, False) - self.name = name or '' - self.starting_linenum = linenum - - def CheckEnd(self, filename, clean_lines, linenum, error): - """Check end of namespace comments.""" - line = clean_lines.raw_lines[linenum] - - # Check how many lines is enclosed in this namespace. Don't issue - # warning for missing namespace comments if there aren't enough - # lines. However, do apply checks if there is already an end of - # namespace comment and it's incorrect. - # - # TODO(unknown): We always want to check end of namespace comments - # if a namespace is large, but sometimes we also want to apply the - # check if a short namespace contained nontrivial things (something - # other than forward declarations). There is currently no logic on - # deciding what these nontrivial things are, so this check is - # triggered by namespace size only, which works most of the time. - if (linenum - self.starting_linenum < 10 - and not Match(r'};*\s*(//|/\*).*\bnamespace\b', line)): - return - - # Look for matching comment at end of namespace. - # - # Note that we accept C style "/* */" comments for terminating - # namespaces, so that code that terminate namespaces inside - # preprocessor macros can be cpplint clean. Example: http://go/nxpiz - # - # We also accept stuff like "// end of namespace <name>." with the - # period at the end. - # - # Besides these, we don't accept anything else, otherwise we might - # get false negatives when existing comment is a substring of the - # expected namespace. Example: http://go/ldkdc, http://cl/23548205 - if self.name: - # Named namespace - if not Match((r'};*\s*(//|/\*).*\bnamespace\s+' + re.escape(self.name) + - r'[\*/\.\\\s]*$'), - line): - error(filename, linenum, 'readability/namespace', 5, - 'Namespace should be terminated with "// namespace %s"' % - self.name) - else: - # Anonymous namespace - if not Match(r'};*\s*(//|/\*).*\bnamespace[\*/\.\\\s]*$', line): - error(filename, linenum, 'readability/namespace', 5, - 'Namespace should be terminated with "// namespace"') - - -class _PreprocessorInfo(object): - """Stores checkpoints of nesting stacks when #if/#else is seen.""" - - def __init__(self, stack_before_if): - # The entire nesting stack before #if - self.stack_before_if = stack_before_if - - # The entire nesting stack up to #else - self.stack_before_else = [] - - # Whether we have already seen #else or #elif - self.seen_else = False - - -class _NestingState(object): - """Holds states related to parsing braces.""" - - def __init__(self): - # Stack for tracking all braces. An object is pushed whenever we - # see a "{", and popped when we see a "}". Only 3 types of - # objects are possible: - # - _ClassInfo: a class or struct. - # - _NamespaceInfo: a namespace. - # - _BlockInfo: some other type of block. - self.stack = [] - - # Stack of _PreprocessorInfo objects. - self.pp_stack = [] - - def SeenOpenBrace(self): - """Check if we have seen the opening brace for the innermost block. - - Returns: - True if we have seen the opening brace, False if the innermost - block is still expecting an opening brace. - """ - return (not self.stack) or self.stack[-1].seen_open_brace - - def InNamespaceBody(self): - """Check if we are currently one level inside a namespace body. - - Returns: - True if top of the stack is a namespace block, False otherwise. - """ - return self.stack and isinstance(self.stack[-1], _NamespaceInfo) - - def UpdatePreprocessor(self, line): - """Update preprocessor stack. - - We need to handle preprocessors due to classes like this: - #ifdef SWIG - struct ResultDetailsPageElementExtensionPoint { - #else - struct ResultDetailsPageElementExtensionPoint : public Extension { - #endif - (see http://go/qwddn for original example) - - We make the following assumptions (good enough for most files): - - Preprocessor condition evaluates to true from #if up to first - #else/#elif/#endif. - - - Preprocessor condition evaluates to false from #else/#elif up - to #endif. We still perform lint checks on these lines, but - these do not affect nesting stack. - - Args: - line: current line to check. - """ - if Match(r'^\s*#\s*(if|ifdef|ifndef)\b', line): - # Beginning of #if block, save the nesting stack here. The saved - # stack will allow us to restore the parsing state in the #else case. - self.pp_stack.append(_PreprocessorInfo(copy.deepcopy(self.stack))) - elif Match(r'^\s*#\s*(else|elif)\b', line): - # Beginning of #else block - if self.pp_stack: - if not self.pp_stack[-1].seen_else: - # This is the first #else or #elif block. Remember the - # whole nesting stack up to this point. This is what we - # keep after the #endif. - self.pp_stack[-1].seen_else = True - self.pp_stack[-1].stack_before_else = copy.deepcopy(self.stack) - - # Restore the stack to how it was before the #if - self.stack = copy.deepcopy(self.pp_stack[-1].stack_before_if) - else: - # TODO(unknown): unexpected #else, issue warning? - pass - elif Match(r'^\s*#\s*endif\b', line): - # End of #if or #else blocks. - if self.pp_stack: - # If we saw an #else, we will need to restore the nesting - # stack to its former state before the #else, otherwise we - # will just continue from where we left off. - if self.pp_stack[-1].seen_else: - # Here we can just use a shallow copy since we are the last - # reference to it. - self.stack = self.pp_stack[-1].stack_before_else - # Drop the corresponding #if - self.pp_stack.pop() - else: - # TODO(unknown): unexpected #endif, issue warning? - pass - - def Update(self, filename, clean_lines, linenum, error): - """Update nesting state with current line. - - Args: - filename: The name of the current file. - clean_lines: A CleansedLines instance containing the file. - linenum: The number of the line to check. - error: The function to call with any errors found. - """ - line = clean_lines.elided[linenum] - - # Update pp_stack first - self.UpdatePreprocessor(line) - - # Count parentheses. This is to avoid adding struct arguments to - # the nesting stack. - if self.stack: - inner_block = self.stack[-1] - depth_change = line.count('(') - line.count(')') - inner_block.open_parentheses += depth_change - - # Also check if we are starting or ending an inline assembly block. - if inner_block.inline_asm in (_NO_ASM, _END_ASM): - if (depth_change != 0 and - inner_block.open_parentheses == 1 and - _MATCH_ASM.match(line)): - # Enter assembly block - inner_block.inline_asm = _INSIDE_ASM - else: - # Not entering assembly block. If previous line was _END_ASM, - # we will now shift to _NO_ASM state. - inner_block.inline_asm = _NO_ASM - elif (inner_block.inline_asm == _INSIDE_ASM and - inner_block.open_parentheses == 0): - # Exit assembly block - inner_block.inline_asm = _END_ASM - - # Consume namespace declaration at the beginning of the line. Do - # this in a loop so that we catch same line declarations like this: - # namespace proto2 { namespace bridge { class MessageSet; } } - while True: - # Match start of namespace. The "\b\s*" below catches namespace - # declarations even if it weren't followed by a whitespace, this - # is so that we don't confuse our namespace checker. The - # missing spaces will be flagged by CheckSpacing. - namespace_decl_match = Match(r'^\s*namespace\b\s*([:\w]+)?(.*)$', line) - if not namespace_decl_match: - break - - new_namespace = _NamespaceInfo(namespace_decl_match.group(1), linenum) - self.stack.append(new_namespace) - - line = namespace_decl_match.group(2) - if line.find('{') != -1: - new_namespace.seen_open_brace = True - line = line[line.find('{') + 1:] - - # Look for a class declaration in whatever is left of the line - # after parsing namespaces. The regexp accounts for decorated classes - # such as in: - # class LOCKABLE API Object { - # }; - # - # Templates with class arguments may confuse the parser, for example: - # template <class T - # class Comparator = less<T>, - # class Vector = vector<T> > - # class HeapQueue { - # - # Because this parser has no nesting state about templates, by the - # time it saw "class Comparator", it may think that it's a new class. - # Nested templates have a similar problem: - # template < - # typename ExportedType, - # typename TupleType, - # template <typename, typename> class ImplTemplate> - # - # To avoid these cases, we ignore classes that are followed by '=' or '>' - class_decl_match = Match( - r'\s*(template\s*<[\w\s<>,:]*>\s*)?' - '(class|struct)\s+([A-Z_]+\s+)*(\w+(?:::\w+)*)' - '(([^=>]|<[^<>]*>)*)$', line) - if (class_decl_match and - (not self.stack or self.stack[-1].open_parentheses == 0)): - self.stack.append(_ClassInfo( - class_decl_match.group(4), class_decl_match.group(2), - clean_lines, linenum)) - line = class_decl_match.group(5) - - # If we have not yet seen the opening brace for the innermost block, - # run checks here. - if not self.SeenOpenBrace(): - self.stack[-1].CheckBegin(filename, clean_lines, linenum, error) - - # Update access control if we are inside a class/struct - if self.stack and isinstance(self.stack[-1], _ClassInfo): - access_match = Match(r'\s*(public|private|protected)\s*:', line) - if access_match: - self.stack[-1].access = access_match.group(1) - - # Consume braces or semicolons from what's left of the line - while True: - # Match first brace, semicolon, or closed parenthesis. - matched = Match(r'^[^{;)}]*([{;)}])(.*)$', line) - if not matched: - break - - token = matched.group(1) - if token == '{': - # If namespace or class hasn't seen a opening brace yet, mark - # namespace/class head as complete. Push a new block onto the - # stack otherwise. - if not self.SeenOpenBrace(): - self.stack[-1].seen_open_brace = True - else: - self.stack.append(_BlockInfo(True)) - if _MATCH_ASM.match(line): - self.stack[-1].inline_asm = _BLOCK_ASM - elif token == ';' or token == ')': - # If we haven't seen an opening brace yet, but we already saw - # a semicolon, this is probably a forward declaration. Pop - # the stack for these. - # - # Similarly, if we haven't seen an opening brace yet, but we - # already saw a closing parenthesis, then these are probably - # function arguments with extra "class" or "struct" keywords. - # Also pop these stack for these. - if not self.SeenOpenBrace(): - self.stack.pop() - else: # token == '}' - # Perform end of block checks and pop the stack. - if self.stack: - self.stack[-1].CheckEnd(filename, clean_lines, linenum, error) - self.stack.pop() - line = matched.group(2) - - def InnermostClass(self): - """Get class info on the top of the stack. - - Returns: - A _ClassInfo object if we are inside a class, or None otherwise. - """ - for i in range(len(self.stack), 0, -1): - classinfo = self.stack[i - 1] - if isinstance(classinfo, _ClassInfo): - return classinfo - return None - - def CheckClassFinished(self, filename, error): - """Checks that all classes have been completely parsed. - - Call this when all lines in a file have been processed. - Args: - filename: The name of the current file. - error: The function to call with any errors found. - """ - # Note: This test can result in false positives if #ifdef constructs - # get in the way of brace matching. See the testBuildClass test in - # cpplint_unittest.py for an example of this. - for obj in self.stack: - if isinstance(obj, _ClassInfo): - error(filename, obj.starting_linenum, 'build/class', 5, - 'Failed to find complete declaration of class %s' % - obj.name) - - -def CheckForNonStandardConstructs(filename, clean_lines, linenum, - nesting_state, error): - """Logs an error if we see certain non-ANSI constructs ignored by gcc-2. - - Complain about several constructs which gcc-2 accepts, but which are - not standard C++. Warning about these in lint is one way to ease the - transition to new compilers. - - put storage class first (e.g. "static const" instead of "const static"). - - "%lld" instead of %qd" in printf-type functions. - - "%1$d" is non-standard in printf-type functions. - - "\%" is an undefined character escape sequence. - - text after #endif is not allowed. - - invalid inner-style forward declaration. - - >? and <? operators, and their >?= and <?= cousins. - - Additionally, check for constructor/destructor style violations and reference - members, as it is very convenient to do so while checking for - gcc-2 compliance. - - Args: - filename: The name of the current file. - clean_lines: A CleansedLines instance containing the file. - linenum: The number of the line to check. - nesting_state: A _NestingState instance which maintains information about - the current stack of nested blocks being parsed. - error: A callable to which errors are reported, which takes 4 arguments: - filename, line number, error level, and message - """ - - # Remove comments from the line, but leave in strings for now. - line = clean_lines.lines[linenum] - - if Search(r'printf\s*\(.*".*%[-+ ]?\d*q', line): - error(filename, linenum, 'runtime/printf_format', 3, - '%q in format strings is deprecated. Use %ll instead.') - - if Search(r'printf\s*\(.*".*%\d+\$', line): - error(filename, linenum, 'runtime/printf_format', 2, - '%N$ formats are unconventional. Try rewriting to avoid them.') - - # Remove escaped backslashes before looking for undefined escapes. - line = line.replace('\\\\', '') - - if Search(r'("|\').*\\(%|\[|\(|{)', line): - error(filename, linenum, 'build/printf_format', 3, - '%, [, (, and { are undefined character escapes. Unescape them.') - - # For the rest, work with both comments and strings removed. - line = clean_lines.elided[linenum] - - if Search(r'\b(const|volatile|void|char|short|int|long' - r'|float|double|signed|unsigned' - r'|schar|u?int8|u?int16|u?int32|u?int64)' - r'\s+(register|static|extern|typedef)\b', - line): - error(filename, linenum, 'build/storage_class', 5, - 'Storage class (static, extern, typedef, etc) should be first.') - - if Match(r'\s*#\s*endif\s*[^/\s]+', line): - error(filename, linenum, 'build/endif_comment', 5, - 'Uncommented text after #endif is non-standard. Use a comment.') - - if Match(r'\s*class\s+(\w+\s*::\s*)+\w+\s*;', line): - error(filename, linenum, 'build/forward_decl', 5, - 'Inner-style forward declarations are invalid. Remove this line.') - - if Search(r'(\w+|[+-]?\d+(\.\d*)?)\s*(<|>)\?=?\s*(\w+|[+-]?\d+)(\.\d*)?', - line): - error(filename, linenum, 'build/deprecated', 3, - '>? and <? (max and min) operators are non-standard and deprecated.') - - if Search(r'^\s*const\s*string\s*&\s*\w+\s*;', line): - # TODO(unknown): Could it be expanded safely to arbitrary references, - # without triggering too many false positives? The first - # attempt triggered 5 warnings for mostly benign code in the regtest, hence - # the restriction. - # Here's the original regexp, for the reference: - # type_name = r'\w+((\s*::\s*\w+)|(\s*<\s*\w+?\s*>))?' - # r'\s*const\s*' + type_name + '\s*&\s*\w+\s*;' - error(filename, linenum, 'runtime/member_string_references', 2, - 'const string& members are dangerous. It is much better to use ' - 'alternatives, such as pointers or simple constants.') - - # Everything else in this function operates on class declarations. - # Return early if the top of the nesting stack is not a class, or if - # the class head is not completed yet. - classinfo = nesting_state.InnermostClass() - if not classinfo or not classinfo.seen_open_brace: - return - - # The class may have been declared with namespace or classname qualifiers. - # The constructor and destructor will not have those qualifiers. - base_classname = classinfo.name.split('::')[-1] - - # Look for single-argument constructors that aren't marked explicit. - # Technically a valid construct, but against style. - args = Match(r'\s+(?:inline\s+)?%s\s*\(([^,()]+)\)' - % re.escape(base_classname), - line) - if (args and - args.group(1) != 'void' and - not Match(r'(const\s+)?%s\s*(?:<\w+>\s*)?&' % re.escape(base_classname), - args.group(1).strip())): - error(filename, linenum, 'runtime/explicit', 5, - 'Single-argument constructors should be marked explicit.') - - -def CheckSpacingForFunctionCall(filename, line, linenum, error): - """Checks for the correctness of various spacing around function calls. - - Args: - filename: The name of the current file. - line: The text of the line to check. - linenum: The number of the line to check. - error: The function to call with any errors found. - """ - - # Since function calls often occur inside if/for/while/switch - # expressions - which have their own, more liberal conventions - we - # first see if we should be looking inside such an expression for a - # function call, to which we can apply more strict standards. - fncall = line # if there's no control flow construct, look at whole line - for pattern in (r'\bif\s*\((.*)\)\s*{', - r'\bfor\s*\((.*)\)\s*{', - r'\bwhile\s*\((.*)\)\s*[{;]', - r'\bswitch\s*\((.*)\)\s*{'): - match = Search(pattern, line) - if match: - fncall = match.group(1) # look inside the parens for function calls - break - - # Except in if/for/while/switch, there should never be space - # immediately inside parens (eg "f( 3, 4 )"). We make an exception - # for nested parens ( (a+b) + c ). Likewise, there should never be - # a space before a ( when it's a function argument. I assume it's a - # function argument when the char before the whitespace is legal in - # a function name (alnum + _) and we're not starting a macro. Also ignore - # pointers and references to arrays and functions coz they're too tricky: - # we use a very simple way to recognize these: - # " (something)(maybe-something)" or - # " (something)(maybe-something," or - # " (something)[something]" - # Note that we assume the contents of [] to be short enough that - # they'll never need to wrap. - if ( # Ignore control structures. - not Search(r'\b(if|for|while|switch|return|delete)\b', fncall) and - # Ignore pointers/references to functions. - not Search(r' \([^)]+\)\([^)]*(\)|,$)', fncall) and - # Ignore pointers/references to arrays. - not Search(r' \([^)]+\)\[[^\]]+\]', fncall)): - if Search(r'\w\s*\(\s(?!\s*\\$)', fncall): # a ( used for a fn call - error(filename, linenum, 'whitespace/parens', 4, - 'Extra space after ( in function call') - elif Search(r'\(\s+(?!(\s*\\)|\()', fncall): - error(filename, linenum, 'whitespace/parens', 2, - 'Extra space after (') - if (Search(r'\w\s+\(', fncall) and - not Search(r'#\s*define|typedef', fncall) and - not Search(r'\w\s+\((\w+::)?\*\w+\)\(', fncall)): - error(filename, linenum, 'whitespace/parens', 4, - 'Extra space before ( in function call') - # If the ) is followed only by a newline or a { + newline, assume it's - # part of a control statement (if/while/etc), and don't complain - if Search(r'[^)]\s+\)\s*[^{\s]', fncall): - # If the closing parenthesis is preceded by only whitespaces, - # try to give a more descriptive error message. - if Search(r'^\s+\)', fncall): - error(filename, linenum, 'whitespace/parens', 2, - 'Closing ) should be moved to the previous line') - else: - error(filename, linenum, 'whitespace/parens', 2, - 'Extra space before )') - - -def IsBlankLine(line): - """Returns true if the given line is blank. - - We consider a line to be blank if the line is empty or consists of - only white spaces. - - Args: - line: A line of a string. - - Returns: - True, if the given line is blank. - """ - return not line or line.isspace() - - -def CheckForFunctionLengths(filename, clean_lines, linenum, - function_state, error): - """Reports for long function bodies. - - For an overview why this is done, see: - http://google-styleguide.googlecode.com/svn/trunk/cppguide.xml#Write_Short_Functions - - Uses a simplistic algorithm assuming other style guidelines - (especially spacing) are followed. - Only checks unindented functions, so class members are unchecked. - Trivial bodies are unchecked, so constructors with huge initializer lists - may be missed. - Blank/comment lines are not counted so as to avoid encouraging the removal - of vertical space and comments just to get through a lint check. - NOLINT *on the last line of a function* disables this check. - - Args: - filename: The name of the current file. - clean_lines: A CleansedLines instance containing the file. - linenum: The number of the line to check. - function_state: Current function name and lines in body so far. - error: The function to call with any errors found. - """ - lines = clean_lines.lines - line = lines[linenum] - raw = clean_lines.raw_lines - raw_line = raw[linenum] - joined_line = '' - - starting_func = False - regexp = r'(\w(\w|::|\*|\&|\s)*)\(' # decls * & space::name( ... - match_result = Match(regexp, line) - if match_result: - # If the name is all caps and underscores, figure it's a macro and - # ignore it, unless it's TEST or TEST_F. - function_name = match_result.group(1).split()[-1] - if function_name == 'TEST' or function_name == 'TEST_F' or ( - not Match(r'[A-Z_]+$', function_name)): - starting_func = True - - if starting_func: - body_found = False - for start_linenum in xrange(linenum, clean_lines.NumLines()): - start_line = lines[start_linenum] - joined_line += ' ' + start_line.lstrip() - if Search(r'(;|})', start_line): # Declarations and trivial functions - body_found = True - break # ... ignore - elif Search(r'{', start_line): - body_found = True - function = Search(r'((\w|:)*)\(', line).group(1) - if Match(r'TEST', function): # Handle TEST... macros - parameter_regexp = Search(r'(\(.*\))', joined_line) - if parameter_regexp: # Ignore bad syntax - function += parameter_regexp.group(1) - else: - function += '()' - function_state.Begin(function) - break - if not body_found: - # No body for the function (or evidence of a non-function) was found. - error(filename, linenum, 'readability/fn_size', 5, - 'Lint failed to find start of function body.') - elif Match(r'^\}\s*$', line): # function end - function_state.Check(error, filename, linenum) - function_state.End() - elif not Match(r'^\s*$', line): - function_state.Count() # Count non-blank/non-comment lines. - - -_RE_PATTERN_TODO = re.compile(r'^//(\s*)TODO(\(.+?\))?:?(\s|$)?') - - -def CheckComment(comment, filename, linenum, error): - """Checks for common mistakes in TODO comments. - - Args: - comment: The text of the comment from the line in question. - filename: The name of the current file. - linenum: The number of the line to check. - error: The function to call with any errors found. - """ - match = _RE_PATTERN_TODO.match(comment) - if match: - # One whitespace is correct; zero whitespace is handled elsewhere. - leading_whitespace = match.group(1) - if len(leading_whitespace) > 1: - error(filename, linenum, 'whitespace/todo', 2, - 'Too many spaces before TODO') - - username = match.group(2) - if not username: - error(filename, linenum, 'readability/todo', 2, - 'Missing username in TODO; it should look like ' - '"// TODO(my_username): Stuff."') - - middle_whitespace = match.group(3) - # Comparisons made explicit for correctness -- pylint: disable-msg=C6403 - if middle_whitespace != ' ' and middle_whitespace != '': - error(filename, linenum, 'whitespace/todo', 2, - 'TODO(my_username) should be followed by a space') - -def CheckAccess(filename, clean_lines, linenum, nesting_state, error): - """Checks for improper use of DISALLOW* macros. - - Args: - filename: The name of the current file. - clean_lines: A CleansedLines instance containing the file. - linenum: The number of the line to check. - nesting_state: A _NestingState instance which maintains information about - the current stack of nested blocks being parsed. - error: The function to call with any errors found. - """ - line = clean_lines.elided[linenum] # get rid of comments and strings - - matched = Match((r'\s*(DISALLOW_COPY_AND_ASSIGN|' - r'DISALLOW_EVIL_CONSTRUCTORS|' - r'DISALLOW_IMPLICIT_CONSTRUCTORS)'), line) - if not matched: - return - if nesting_state.stack and isinstance(nesting_state.stack[-1], _ClassInfo): - if nesting_state.stack[-1].access != 'private': - error(filename, linenum, 'readability/constructors', 3, - '%s must be in the private: section' % matched.group(1)) - - else: - # Found DISALLOW* macro outside a class declaration, or perhaps it - # was used inside a function when it should have been part of the - # class declaration. We could issue a warning here, but it - # probably resulted in a compiler error already. - pass - - -def FindNextMatchingAngleBracket(clean_lines, linenum, init_suffix): - """Find the corresponding > to close a template. - - Args: - clean_lines: A CleansedLines instance containing the file. - linenum: Current line number. - init_suffix: Remainder of the current line after the initial <. - - Returns: - True if a matching bracket exists. - """ - line = init_suffix - nesting_stack = ['<'] - while True: - # Find the next operator that can tell us whether < is used as an - # opening bracket or as a less-than operator. We only want to - # warn on the latter case. - # - # We could also check all other operators and terminate the search - # early, e.g. if we got something like this "a<b+c", the "<" is - # most likely a less-than operator, but then we will get false - # positives for default arguments (e.g. http://go/prccd) and - # other template expressions (e.g. http://go/oxcjq). - match = Search(r'^[^<>(),;\[\]]*([<>(),;\[\]])(.*)$', line) - if match: - # Found an operator, update nesting stack - operator = match.group(1) - line = match.group(2) - - if nesting_stack[-1] == '<': - # Expecting closing angle bracket - if operator in ('<', '(', '['): - nesting_stack.append(operator) - elif operator == '>': - nesting_stack.pop() - if not nesting_stack: - # Found matching angle bracket - return True - elif operator == ',': - # Got a comma after a bracket, this is most likely a template - # argument. We have not seen a closing angle bracket yet, but - # it's probably a few lines later if we look for it, so just - # return early here. - return True - else: - # Got some other operator. - return False - - else: - # Expecting closing parenthesis or closing bracket - if operator in ('<', '(', '['): - nesting_stack.append(operator) - elif operator in (')', ']'): - # We don't bother checking for matching () or []. If we got - # something like (] or [), it would have been a syntax error. - nesting_stack.pop() - - else: - # Scan the next line - linenum += 1 - if linenum >= len(clean_lines.elided): - break - line = clean_lines.elided[linenum] - - # Exhausted all remaining lines and still no matching angle bracket. - # Most likely the input was incomplete, otherwise we should have - # seen a semicolon and returned early. - return True - - -def FindPreviousMatchingAngleBracket(clean_lines, linenum, init_prefix): - """Find the corresponding < that started a template. - - Args: - clean_lines: A CleansedLines instance containing the file. - linenum: Current line number. - init_prefix: Part of the current line before the initial >. - - Returns: - True if a matching bracket exists. - """ - line = init_prefix - nesting_stack = ['>'] - while True: - # Find the previous operator - match = Search(r'^(.*)([<>(),;\[\]])[^<>(),;\[\]]*$', line) - if match: - # Found an operator, update nesting stack - operator = match.group(2) - line = match.group(1) - - if nesting_stack[-1] == '>': - # Expecting opening angle bracket - if operator in ('>', ')', ']'): - nesting_stack.append(operator) - elif operator == '<': - nesting_stack.pop() - if not nesting_stack: - # Found matching angle bracket - return True - elif operator == ',': - # Got a comma before a bracket, this is most likely a - # template argument. The opening angle bracket is probably - # there if we look for it, so just return early here. - return True - else: - # Got some other operator. - return False - - else: - # Expecting opening parenthesis or opening bracket - if operator in ('>', ')', ']'): - nesting_stack.append(operator) - elif operator in ('(', '['): - nesting_stack.pop() - - else: - # Scan the previous line - linenum -= 1 - if linenum < 0: - break - line = clean_lines.elided[linenum] - - # Exhausted all earlier lines and still no matching angle bracket. - return False - - -def CheckSpacing(filename, clean_lines, linenum, nesting_state, error): - """Checks for the correctness of various spacing issues in the code. - - Things we check for: spaces around operators, spaces after - if/for/while/switch, no spaces around parens in function calls, two - spaces between code and comment, don't start a block with a blank - line, don't end a function with a blank line, don't add a blank line - after public/protected/private, don't have too many blank lines in a row. - - Args: - filename: The name of the current file. - clean_lines: A CleansedLines instance containing the file. - linenum: The number of the line to check. - nesting_state: A _NestingState instance which maintains information about - the current stack of nested blocks being parsed. - error: The function to call with any errors found. - """ - - raw = clean_lines.raw_lines - line = raw[linenum] - - # Before nixing comments, check if the line is blank for no good - # reason. This includes the first line after a block is opened, and - # blank lines at the end of a function (ie, right before a line like '}' - # - # Skip all the blank line checks if we are immediately inside a - # namespace body. In other words, don't issue blank line warnings - # for this block: - # namespace { - # - # } - # - # A warning about missing end of namespace comments will be issued instead. - if IsBlankLine(line) and not nesting_state.InNamespaceBody(): - elided = clean_lines.elided - prev_line = elided[linenum - 1] - prevbrace = prev_line.rfind('{') - # TODO(unknown): Don't complain if line before blank line, and line after, - # both start with alnums and are indented the same amount. - # This ignores whitespace at the start of a namespace block - # because those are not usually indented. - if prevbrace != -1 and prev_line[prevbrace:].find('}') == -1: - # OK, we have a blank line at the start of a code block. Before we - # complain, we check if it is an exception to the rule: The previous - # non-empty line has the parameters of a function header that are indented - # 4 spaces (because they did not fit in a 80 column line when placed on - # the same line as the function name). We also check for the case where - # the previous line is indented 6 spaces, which may happen when the - # initializers of a constructor do not fit into a 80 column line. - exception = False - if Match(r' {6}\w', prev_line): # Initializer list? - # We are looking for the opening column of initializer list, which - # should be indented 4 spaces to cause 6 space indentation afterwards. - search_position = linenum-2 - while (search_position >= 0 - and Match(r' {6}\w', elided[search_position])): - search_position -= 1 - exception = (search_position >= 0 - and elided[search_position][:5] == ' :') - else: - # Search for the function arguments or an initializer list. We use a - # simple heuristic here: If the line is indented 4 spaces; and we have a - # closing paren, without the opening paren, followed by an opening brace - # or colon (for initializer lists) we assume that it is the last line of - # a function header. If we have a colon indented 4 spaces, it is an - # initializer list. - exception = (Match(r' {4}\w[^\(]*\)\s*(const\s*)?(\{\s*$|:)', - prev_line) - or Match(r' {4}:', prev_line)) - - if not exception: - error(filename, linenum, 'whitespace/blank_line', 2, - 'Blank line at the start of a code block. Is this needed?') - # Ignore blank lines at the end of a block in a long if-else - # chain, like this: - # if (condition1) { - # // Something followed by a blank line - # - # } else if (condition2) { - # // Something else - # } - if linenum + 1 < clean_lines.NumLines(): - next_line = raw[linenum + 1] - if (next_line - and Match(r'\s*}', next_line) - and next_line.find('} else ') == -1): - error(filename, linenum, 'whitespace/blank_line', 3, - 'Blank line at the end of a code block. Is this needed?') - - matched = Match(r'\s*(public|protected|private):', prev_line) - if matched: - error(filename, linenum, 'whitespace/blank_line', 3, - 'Do not leave a blank line after "%s:"' % matched.group(1)) - - # Next, we complain if there's a comment too near the text - commentpos = line.find('//') - if commentpos != -1: - # Check if the // may be in quotes. If so, ignore it - # Comparisons made explicit for clarity -- pylint: disable-msg=C6403 - if (line.count('"', 0, commentpos) - - line.count('\\"', 0, commentpos)) % 2 == 0: # not in quotes - # Allow one space for new scopes, two spaces otherwise: - if (not Match(r'^\s*{ //', line) and - ((commentpos >= 1 and - line[commentpos-1] not in string.whitespace) or - (commentpos >= 2 and - line[commentpos-2] not in string.whitespace))): - error(filename, linenum, 'whitespace/comments', 2, - 'At least two spaces is best between code and comments') - # There should always be a space between the // and the comment - commentend = commentpos + 2 - if commentend < len(line) and not line[commentend] == ' ': - # but some lines are exceptions -- e.g. if they're big - # comment delimiters like: - # //---------------------------------------------------------- - # or are an empty C++ style Doxygen comment, like: - # /// - # or they begin with multiple slashes followed by a space: - # //////// Header comment - match = (Search(r'[=/-]{4,}\s*$', line[commentend:]) or - Search(r'^/$', line[commentend:]) or - Search(r'^/+ ', line[commentend:])) - if not match: - error(filename, linenum, 'whitespace/comments', 4, - 'Should have a space between // and comment') - CheckComment(line[commentpos:], filename, linenum, error) - - line = clean_lines.elided[linenum] # get rid of comments and strings - - # Don't try to do spacing checks for operator methods - line = re.sub(r'operator(==|!=|<|<<|<=|>=|>>|>)\(', 'operator\(', line) - - # We allow no-spaces around = within an if: "if ( (a=Foo()) == 0 )". - # Otherwise not. Note we only check for non-spaces on *both* sides; - # sometimes people put non-spaces on one side when aligning ='s among - # many lines (not that this is behavior that I approve of...) - if Search(r'[\w.]=[\w.]', line) and not Search(r'\b(if|while) ', line): - error(filename, linenum, 'whitespace/operators', 4, - 'Missing spaces around =') - - # It's ok not to have spaces around binary operators like + - * /, but if - # there's too little whitespace, we get concerned. It's hard to tell, - # though, so we punt on this one for now. TODO. - - # You should always have whitespace around binary operators. - # - # Check <= and >= first to avoid false positives with < and >, then - # check non-include lines for spacing around < and >. - match = Search(r'[^<>=!\s](==|!=|<=|>=)[^<>=!\s]', line) - if match: - error(filename, linenum, 'whitespace/operators', 3, - 'Missing spaces around %s' % match.group(1)) - # We allow no-spaces around << when used like this: 10<<20, but - # not otherwise (particularly, not when used as streams) - match = Search(r'(\S)(?:L|UL|ULL|l|ul|ull)?<<(\S)', line) - if match and not (match.group(1).isdigit() and match.group(2).isdigit()): - error(filename, linenum, 'whitespace/operators', 3, - 'Missing spaces around <<') - elif not Match(r'#.*include', line): - # Avoid false positives on -> - reduced_line = line.replace('->', '') - - # Look for < that is not surrounded by spaces. This is only - # triggered if both sides are missing spaces, even though - # technically should should flag if at least one side is missing a - # space. This is done to avoid some false positives with shifts. - match = Search(r'[^\s<]<([^\s=<].*)', reduced_line) - if (match and - not FindNextMatchingAngleBracket(clean_lines, linenum, match.group(1))): - error(filename, linenum, 'whitespace/operators', 3, - 'Missing spaces around <') - - # Look for > that is not surrounded by spaces. Similar to the - # above, we only trigger if both sides are missing spaces to avoid - # false positives with shifts. - match = Search(r'^(.*[^\s>])>[^\s=>]', reduced_line) - if (match and - not FindPreviousMatchingAngleBracket(clean_lines, linenum, - match.group(1))): - error(filename, linenum, 'whitespace/operators', 3, - 'Missing spaces around >') - - # We allow no-spaces around >> for almost anything. This is because - # C++11 allows ">>" to close nested templates, which accounts for - # most cases when ">>" is not followed by a space. - # - # We still warn on ">>" followed by alpha character, because that is - # likely due to ">>" being used for right shifts, e.g.: - # value >> alpha - # - # When ">>" is used to close templates, the alphanumeric letter that - # follows would be part of an identifier, and there should still be - # a space separating the template type and the identifier. - # type<type<type>> alpha - match = Search(r'>>[a-zA-Z_]', line) - if match: - error(filename, linenum, 'whitespace/operators', 3, - 'Missing spaces around >>') - - # There shouldn't be space around unary operators - match = Search(r'(!\s|~\s|[\s]--[\s;]|[\s]\+\+[\s;])', line) - if match: - error(filename, linenum, 'whitespace/operators', 4, - 'Extra space for operator %s' % match.group(1)) - - # A pet peeve of mine: no spaces after an if, while, switch, or for - match = Search(r' (if\(|for\(|while\(|switch\()', line) - if match: - error(filename, linenum, 'whitespace/parens', 5, - 'Missing space before ( in %s' % match.group(1)) - - # For if/for/while/switch, the left and right parens should be - # consistent about how many spaces are inside the parens, and - # there should either be zero or one spaces inside the parens. - # We don't want: "if ( foo)" or "if ( foo )". - # Exception: "for ( ; foo; bar)" and "for (foo; bar; )" are allowed. - match = Search(r'\b(if|for|while|switch)\s*' - r'\(([ ]*)(.).*[^ ]+([ ]*)\)\s*{\s*$', - line) - if match: - if len(match.group(2)) != len(match.group(4)): - if not (match.group(3) == ';' and - len(match.group(2)) == 1 + len(match.group(4)) or - not match.group(2) and Search(r'\bfor\s*\(.*; \)', line)): - error(filename, linenum, 'whitespace/parens', 5, - 'Mismatching spaces inside () in %s' % match.group(1)) - if not len(match.group(2)) in [0, 1]: - error(filename, linenum, 'whitespace/parens', 5, - 'Should have zero or one spaces inside ( and ) in %s' % - match.group(1)) - - # You should always have a space after a comma (either as fn arg or operator) - if Search(r',[^\s]', line): - error(filename, linenum, 'whitespace/comma', 3, - 'Missing space after ,') - - # You should always have a space after a semicolon - # except for few corner cases - # TODO(unknown): clarify if 'if (1) { return 1;}' is requires one more - # space after ; - if Search(r';[^\s};\\)/]', line): - error(filename, linenum, 'whitespace/semicolon', 3, - 'Missing space after ;') - - # Next we will look for issues with function calls. - CheckSpacingForFunctionCall(filename, line, linenum, error) - - # Except after an opening paren, or after another opening brace (in case of - # an initializer list, for instance), you should have spaces before your - # braces. And since you should never have braces at the beginning of a line, - # this is an easy test. - if Search(r'[^ ({]{', line): - error(filename, linenum, 'whitespace/braces', 5, - 'Missing space before {') - - # Make sure '} else {' has spaces. - if Search(r'}else', line): - error(filename, linenum, 'whitespace/braces', 5, - 'Missing space before else') - - # You shouldn't have spaces before your brackets, except maybe after - # 'delete []' or 'new char * []'. - if Search(r'\w\s+\[', line) and not Search(r'delete\s+\[', line): - error(filename, linenum, 'whitespace/braces', 5, - 'Extra space before [') - - # You shouldn't have a space before a semicolon at the end of the line. - # There's a special case for "for" since the style guide allows space before - # the semicolon there. - if Search(r':\s*;\s*$', line): - error(filename, linenum, 'whitespace/semicolon', 5, - 'Semicolon defining empty statement. Use {} instead.') - elif Search(r'^\s*;\s*$', line): - error(filename, linenum, 'whitespace/semicolon', 5, - 'Line contains only semicolon. If this should be an empty statement, ' - 'use {} instead.') - elif (Search(r'\s+;\s*$', line) and - not Search(r'\bfor\b', line)): - error(filename, linenum, 'whitespace/semicolon', 5, - 'Extra space before last semicolon. If this should be an empty ' - 'statement, use {} instead.') - - # In range-based for, we wanted spaces before and after the colon, but - # not around "::" tokens that might appear. - if (Search('for *\(.*[^:]:[^: ]', line) or - Search('for *\(.*[^: ]:[^:]', line)): - error(filename, linenum, 'whitespace/forcolon', 2, - 'Missing space around colon in range-based for loop') - - -def CheckSectionSpacing(filename, clean_lines, class_info, linenum, error): - """Checks for additional blank line issues related to sections. - - Currently the only thing checked here is blank line before protected/private. - - Args: - filename: The name of the current file. - clean_lines: A CleansedLines instance containing the file. - class_info: A _ClassInfo objects. - linenum: The number of the line to check. - error: The function to call with any errors found. - """ - # Skip checks if the class is small, where small means 25 lines or less. - # 25 lines seems like a good cutoff since that's the usual height of - # terminals, and any class that can't fit in one screen can't really - # be considered "small". - # - # Also skip checks if we are on the first line. This accounts for - # classes that look like - # class Foo { public: ... }; - # - # If we didn't find the end of the class, last_line would be zero, - # and the check will be skipped by the first condition. - if (class_info.last_line - class_info.starting_linenum <= 24 or - linenum <= class_info.starting_linenum): - return - - matched = Match(r'\s*(public|protected|private):', clean_lines.lines[linenum]) - if matched: - # Issue warning if the line before public/protected/private was - # not a blank line, but don't do this if the previous line contains - # "class" or "struct". This can happen two ways: - # - We are at the beginning of the class. - # - We are forward-declaring an inner class that is semantically - # private, but needed to be public for implementation reasons. - # Also ignores cases where the previous line ends with a backslash as can be - # common when defining classes in C macros. - prev_line = clean_lines.lines[linenum - 1] - if (not IsBlankLine(prev_line) and - not Search(r'\b(class|struct)\b', prev_line) and - not Search(r'\\$', prev_line)): - # Try a bit harder to find the beginning of the class. This is to - # account for multi-line base-specifier lists, e.g.: - # class Derived - # : public Base { - end_class_head = class_info.starting_linenum - for i in range(class_info.starting_linenum, linenum): - if Search(r'\{\s*$', clean_lines.lines[i]): - end_class_head = i - break - if end_class_head < linenum - 1: - error(filename, linenum, 'whitespace/blank_line', 3, - '"%s:" should be preceded by a blank line' % matched.group(1)) - - -def GetPreviousNonBlankLine(clean_lines, linenum): - """Return the most recent non-blank line and its line number. - - Args: - clean_lines: A CleansedLines instance containing the file contents. - linenum: The number of the line to check. - - Returns: - A tuple with two elements. The first element is the contents of the last - non-blank line before the current line, or the empty string if this is the - first non-blank line. The second is the line number of that line, or -1 - if this is the first non-blank line. - """ - - prevlinenum = linenum - 1 - while prevlinenum >= 0: - prevline = clean_lines.elided[prevlinenum] - if not IsBlankLine(prevline): # if not a blank line... - return (prevline, prevlinenum) - prevlinenum -= 1 - return ('', -1) - - -def CheckBraces(filename, clean_lines, linenum, error): - """Looks for misplaced braces (e.g. at the end of line). - - Args: - filename: The name of the current file. - clean_lines: A CleansedLines instance containing the file. - linenum: The number of the line to check. - error: The function to call with any errors found. - """ - - line = clean_lines.elided[linenum] # get rid of comments and strings - - if Match(r'\s*{\s*$', line): - # We allow an open brace to start a line in the case where someone - # is using braces in a block to explicitly create a new scope, - # which is commonly used to control the lifetime of - # stack-allocated variables. We don't detect this perfectly: we - # just don't complain if the last non-whitespace character on the - # previous non-blank line is ';', ':', '{', or '}', or if the previous - # line starts a preprocessor block. - prevline = GetPreviousNonBlankLine(clean_lines, linenum)[0] - if (not Search(r'[;:}{]\s*$', prevline) and - not Match(r'\s*#', prevline)): - error(filename, linenum, 'whitespace/braces', 4, - '{ should almost always be at the end of the previous line') - - # An else clause should be on the same line as the preceding closing brace. - if Match(r'\s*else\s*', line): - prevline = GetPreviousNonBlankLine(clean_lines, linenum)[0] - if Match(r'\s*}\s*$', prevline): - error(filename, linenum, 'whitespace/newline', 4, - 'An else should appear on the same line as the preceding }') - - # If braces come on one side of an else, they should be on both. - # However, we have to worry about "else if" that spans multiple lines! - if Search(r'}\s*else[^{]*$', line) or Match(r'[^}]*else\s*{', line): - if Search(r'}\s*else if([^{]*)$', line): # could be multi-line if - # find the ( after the if - pos = line.find('else if') - pos = line.find('(', pos) - if pos > 0: - (endline, _, endpos) = CloseExpression(clean_lines, linenum, pos) - if endline[endpos:].find('{') == -1: # must be brace after if - error(filename, linenum, 'readability/braces', 5, - 'If an else has a brace on one side, it should have it on both') - else: # common case: else not followed by a multi-line if - error(filename, linenum, 'readability/braces', 5, - 'If an else has a brace on one side, it should have it on both') - - # Likewise, an else should never have the else clause on the same line - if Search(r'\belse [^\s{]', line) and not Search(r'\belse if\b', line): - error(filename, linenum, 'whitespace/newline', 4, - 'Else clause should never be on same line as else (use 2 lines)') - - # In the same way, a do/while should never be on one line - if Match(r'\s*do [^\s{]', line): - error(filename, linenum, 'whitespace/newline', 4, - 'do/while clauses should not be on a single line') - - # Braces shouldn't be followed by a ; unless they're defining a struct - # or initializing an array. - # We can't tell in general, but we can for some common cases. - prevlinenum = linenum - while True: - (prevline, prevlinenum) = GetPreviousNonBlankLine(clean_lines, prevlinenum) - if Match(r'\s+{.*}\s*;', line) and not prevline.count(';'): - line = prevline + line - else: - break - if (Search(r'{.*}\s*;', line) and - line.count('{') == line.count('}') and - not Search(r'struct|class|enum|\s*=\s*{', line)): - error(filename, linenum, 'readability/braces', 4, - "You don't need a ; after a }") - - -def CheckEmptyLoopBody(filename, clean_lines, linenum, error): - """Loop for empty loop body with only a single semicolon. - - Args: - filename: The name of the current file. - clean_lines: A CleansedLines instance containing the file. - linenum: The number of the line to check. - error: The function to call with any errors found. - """ - - # Search for loop keywords at the beginning of the line. Because only - # whitespaces are allowed before the keywords, this will also ignore most - # do-while-loops, since those lines should start with closing brace. - line = clean_lines.elided[linenum] - if Match(r'\s*(for|while)\s*\(', line): - # Find the end of the conditional expression - (end_line, end_linenum, end_pos) = CloseExpression( - clean_lines, linenum, line.find('(')) - - # Output warning if what follows the condition expression is a semicolon. - # No warning for all other cases, including whitespace or newline, since we - # have a separate check for semicolons preceded by whitespace. - if end_pos >= 0 and Match(r';', end_line[end_pos:]): - error(filename, end_linenum, 'whitespace/empty_loop_body', 5, - 'Empty loop bodies should use {} or continue') - - -def ReplaceableCheck(operator, macro, line): - """Determine whether a basic CHECK can be replaced with a more specific one. - - For example suggest using CHECK_EQ instead of CHECK(a == b) and - similarly for CHECK_GE, CHECK_GT, CHECK_LE, CHECK_LT, CHECK_NE. - - Args: - operator: The C++ operator used in the CHECK. - macro: The CHECK or EXPECT macro being called. - line: The current source line. - - Returns: - True if the CHECK can be replaced with a more specific one. - """ - - # This matches decimal and hex integers, strings, and chars (in that order). - match_constant = r'([-+]?(\d+|0[xX][0-9a-fA-F]+)[lLuU]{0,3}|".*"|\'.*\')' - - # Expression to match two sides of the operator with something that - # looks like a literal, since CHECK(x == iterator) won't compile. - # This means we can't catch all the cases where a more specific - # CHECK is possible, but it's less annoying than dealing with - # extraneous warnings. - match_this = (r'\s*' + macro + r'\((\s*' + - match_constant + r'\s*' + operator + r'[^<>].*|' - r'.*[^<>]' + operator + r'\s*' + match_constant + - r'\s*\))') - - # Don't complain about CHECK(x == NULL) or similar because - # CHECK_EQ(x, NULL) won't compile (requires a cast). - # Also, don't complain about more complex boolean expressions - # involving && or || such as CHECK(a == b || c == d). - return Match(match_this, line) and not Search(r'NULL|&&|\|\|', line) - - -def CheckCheck(filename, clean_lines, linenum, error): - """Checks the use of CHECK and EXPECT macros. - - Args: - filename: The name of the current file. - clean_lines: A CleansedLines instance containing the file. - linenum: The number of the line to check. - error: The function to call with any errors found. - """ - - # Decide the set of replacement macros that should be suggested - raw_lines = clean_lines.raw_lines - current_macro = '' - for macro in _CHECK_MACROS: - if raw_lines[linenum].find(macro) >= 0: - current_macro = macro - break - if not current_macro: - # Don't waste time here if line doesn't contain 'CHECK' or 'EXPECT' - return - - line = clean_lines.elided[linenum] # get rid of comments and strings - - # Encourage replacing plain CHECKs with CHECK_EQ/CHECK_NE/etc. - for operator in ['==', '!=', '>=', '>', '<=', '<']: - if ReplaceableCheck(operator, current_macro, line): - error(filename, linenum, 'readability/check', 2, - 'Consider using %s instead of %s(a %s b)' % ( - _CHECK_REPLACEMENT[current_macro][operator], - current_macro, operator)) - break - - -def CheckAltTokens(filename, clean_lines, linenum, error): - """Check alternative keywords being used in boolean expressions. - - Args: - filename: The name of the current file. - clean_lines: A CleansedLines instance containing the file. - linenum: The number of the line to check. - error: The function to call with any errors found. - """ - line = clean_lines.elided[linenum] - - # Avoid preprocessor lines - if Match(r'^\s*#', line): - return - - # Last ditch effort to avoid multi-line comments. This will not help - # if the comment started before the current line or ended after the - # current line, but it catches most of the false positives. At least, - # it provides a way to workaround this warning for people who use - # multi-line comments in preprocessor macros. - # - # TODO(unknown): remove this once cpplint has better support for - # multi-line comments. - if line.find('/*') >= 0 or line.find('*/') >= 0: - return - - for match in _ALT_TOKEN_REPLACEMENT_PATTERN.finditer(line): - error(filename, linenum, 'readability/alt_tokens', 2, - 'Use operator %s instead of %s' % ( - _ALT_TOKEN_REPLACEMENT[match.group(1)], match.group(1))) - - -def GetLineWidth(line): - """Determines the width of the line in column positions. - - Args: - line: A string, which may be a Unicode string. - - Returns: - The width of the line in column positions, accounting for Unicode - combining characters and wide characters. - """ - if isinstance(line, unicode): - width = 0 - for uc in unicodedata.normalize('NFC', line): - if unicodedata.east_asian_width(uc) in ('W', 'F'): - width += 2 - elif not unicodedata.combining(uc): - width += 1 - return width - else: - return len(line) - - -def CheckStyle(filename, clean_lines, linenum, file_extension, nesting_state, - error): - """Checks rules from the 'C++ style rules' section of cppguide.html. - - Most of these rules are hard to test (naming, comment style), but we - do what we can. In particular we check for 2-space indents, line lengths, - tab usage, spaces inside code, etc. - - Args: - filename: The name of the current file. - clean_lines: A CleansedLines instance containing the file. - linenum: The number of the line to check. - file_extension: The extension (without the dot) of the filename. - nesting_state: A _NestingState instance which maintains information about - the current stack of nested blocks being parsed. - error: The function to call with any errors found. - """ - - raw_lines = clean_lines.raw_lines - line = raw_lines[linenum] - - if line.find('\t') != -1: - error(filename, linenum, 'whitespace/tab', 1, - 'Tab found; better to use spaces') - - # One or three blank spaces at the beginning of the line is weird; it's - # hard to reconcile that with 2-space indents. - # NOTE: here are the conditions rob pike used for his tests. Mine aren't - # as sophisticated, but it may be worth becoming so: RLENGTH==initial_spaces - # if(RLENGTH > 20) complain = 0; - # if(match($0, " +(error|private|public|protected):")) complain = 0; - # if(match(prev, "&& *$")) complain = 0; - # if(match(prev, "\\|\\| *$")) complain = 0; - # if(match(prev, "[\",=><] *$")) complain = 0; - # if(match($0, " <<")) complain = 0; - # if(match(prev, " +for \\(")) complain = 0; - # if(prevodd && match(prevprev, " +for \\(")) complain = 0; - initial_spaces = 0 - cleansed_line = clean_lines.elided[linenum] - while initial_spaces < len(line) and line[initial_spaces] == ' ': - initial_spaces += 1 - if line and line[-1].isspace(): - error(filename, linenum, 'whitespace/end_of_line', 4, - 'Line ends in whitespace. Consider deleting these extra spaces.') - # There are certain situations we allow one space, notably for labels - elif ((initial_spaces == 1 or initial_spaces == 3) and - not Match(r'\s*\w+\s*:\s*$', cleansed_line)): - error(filename, linenum, 'whitespace/indent', 3, - 'Weird number of spaces at line-start. ' - 'Are you using a 2-space indent?') - # Labels should always be indented at least one space. - elif not initial_spaces and line[:2] != '//' and Search(r'[^:]:\s*$', - line): - error(filename, linenum, 'whitespace/labels', 4, - 'Labels should always be indented at least one space. ' - 'If this is a member-initializer list in a constructor or ' - 'the base class list in a class definition, the colon should ' - 'be on the following line.') - - - # Check if the line is a header guard. - is_header_guard = False - if file_extension == 'h': - cppvar = GetHeaderGuardCPPVariable(filename) - if (line.startswith('#ifndef %s' % cppvar) or - line.startswith('#define %s' % cppvar) or - line.startswith('#endif // %s' % cppvar)): - is_header_guard = True - # #include lines and header guards can be long, since there's no clean way to - # split them. - # - # URLs can be long too. It's possible to split these, but it makes them - # harder to cut&paste. - # - # The "$Id:...$" comment may also get very long without it being the - # developers fault. - if (not line.startswith('#include') and not is_header_guard and - not Match(r'^\s*//.*http(s?)://\S*$', line) and - not Match(r'^// \$Id:.*#[0-9]+ \$$', line)): - line_width = GetLineWidth(line) - if line_width > 100: - error(filename, linenum, 'whitespace/line_length', 4, - 'Lines should very rarely be longer than 100 characters') - elif line_width > 80: - error(filename, linenum, 'whitespace/line_length', 2, - 'Lines should be <= 80 characters long') - - if (cleansed_line.count(';') > 1 and - # for loops are allowed two ;'s (and may run over two lines). - cleansed_line.find('for') == -1 and - (GetPreviousNonBlankLine(clean_lines, linenum)[0].find('for') == -1 or - GetPreviousNonBlankLine(clean_lines, linenum)[0].find(';') != -1) and - # It's ok to have many commands in a switch case that fits in 1 line - not ((cleansed_line.find('case ') != -1 or - cleansed_line.find('default:') != -1) and - cleansed_line.find('break;') != -1)): - error(filename, linenum, 'whitespace/newline', 0, - 'More than one command on the same line') - - # Some more style checks - CheckBraces(filename, clean_lines, linenum, error) - CheckEmptyLoopBody(filename, clean_lines, linenum, error) - CheckAccess(filename, clean_lines, linenum, nesting_state, error) - CheckSpacing(filename, clean_lines, linenum, nesting_state, error) - CheckCheck(filename, clean_lines, linenum, error) - CheckAltTokens(filename, clean_lines, linenum, error) - classinfo = nesting_state.InnermostClass() - if classinfo: - CheckSectionSpacing(filename, clean_lines, classinfo, linenum, error) - - -_RE_PATTERN_INCLUDE_NEW_STYLE = re.compile(r'#include +"[^/]+\.h"') -_RE_PATTERN_INCLUDE = re.compile(r'^\s*#\s*include\s*([<"])([^>"]*)[>"].*$') -# Matches the first component of a filename delimited by -s and _s. That is: -# _RE_FIRST_COMPONENT.match('foo').group(0) == 'foo' -# _RE_FIRST_COMPONENT.match('foo.cc').group(0) == 'foo' -# _RE_FIRST_COMPONENT.match('foo-bar_baz.cc').group(0) == 'foo' -# _RE_FIRST_COMPONENT.match('foo_bar-baz.cc').group(0) == 'foo' -_RE_FIRST_COMPONENT = re.compile(r'^[^-_.]+') - - -def _DropCommonSuffixes(filename): - """Drops common suffixes like _test.cc or -inl.h from filename. - - For example: - >>> _DropCommonSuffixes('foo/foo-inl.h') - 'foo/foo' - >>> _DropCommonSuffixes('foo/bar/foo.cc') - 'foo/bar/foo' - >>> _DropCommonSuffixes('foo/foo_internal.h') - 'foo/foo' - >>> _DropCommonSuffixes('foo/foo_unusualinternal.h') - 'foo/foo_unusualinternal' - - Args: - filename: The input filename. - - Returns: - The filename with the common suffix removed. - """ - for suffix in ('test.cc', 'regtest.cc', 'unittest.cc', - 'inl.h', 'impl.h', 'internal.h'): - if (filename.endswith(suffix) and len(filename) > len(suffix) and - filename[-len(suffix) - 1] in ('-', '_')): - return filename[:-len(suffix) - 1] - return os.path.splitext(filename)[0] - - -def _IsTestFilename(filename): - """Determines if the given filename has a suffix that identifies it as a test. - - Args: - filename: The input filename. - - Returns: - True if 'filename' looks like a test, False otherwise. - """ - if (filename.endswith('_test.cc') or - filename.endswith('_unittest.cc') or - filename.endswith('_regtest.cc')): - return True - else: - return False - - -def _ClassifyInclude(fileinfo, include, is_system): - """Figures out what kind of header 'include' is. - - Args: - fileinfo: The current file cpplint is running over. A FileInfo instance. - include: The path to a #included file. - is_system: True if the #include used <> rather than "". - - Returns: - One of the _XXX_HEADER constants. - - For example: - >>> _ClassifyInclude(FileInfo('foo/foo.cc'), 'stdio.h', True) - _C_SYS_HEADER - >>> _ClassifyInclude(FileInfo('foo/foo.cc'), 'string', True) - _CPP_SYS_HEADER - >>> _ClassifyInclude(FileInfo('foo/foo.cc'), 'foo/foo.h', False) - _LIKELY_MY_HEADER - >>> _ClassifyInclude(FileInfo('foo/foo_unknown_extension.cc'), - ... 'bar/foo_other_ext.h', False) - _POSSIBLE_MY_HEADER - >>> _ClassifyInclude(FileInfo('foo/foo.cc'), 'foo/bar.h', False) - _OTHER_HEADER - """ - # This is a list of all standard c++ header files, except - # those already checked for above. - is_stl_h = include in _STL_HEADERS - is_cpp_h = is_stl_h or include in _CPP_HEADERS - - if is_system: - if is_cpp_h: - return _CPP_SYS_HEADER - else: - return _C_SYS_HEADER - - # If the target file and the include we're checking share a - # basename when we drop common extensions, and the include - # lives in . , then it's likely to be owned by the target file. - target_dir, target_base = ( - os.path.split(_DropCommonSuffixes(fileinfo.RepositoryName()))) - include_dir, include_base = os.path.split(_DropCommonSuffixes(include)) - if target_base == include_base and ( - include_dir == target_dir or - include_dir == os.path.normpath(target_dir + '/../public')): - return _LIKELY_MY_HEADER - - # If the target and include share some initial basename - # component, it's possible the target is implementing the - # include, so it's allowed to be first, but we'll never - # complain if it's not there. - target_first_component = _RE_FIRST_COMPONENT.match(target_base) - include_first_component = _RE_FIRST_COMPONENT.match(include_base) - if (target_first_component and include_first_component and - target_first_component.group(0) == - include_first_component.group(0)): - return _POSSIBLE_MY_HEADER - - return _OTHER_HEADER - - - -def CheckIncludeLine(filename, clean_lines, linenum, include_state, error): - """Check rules that are applicable to #include lines. - - Strings on #include lines are NOT removed from elided line, to make - certain tasks easier. However, to prevent false positives, checks - applicable to #include lines in CheckLanguage must be put here. - - Args: - filename: The name of the current file. - clean_lines: A CleansedLines instance containing the file. - linenum: The number of the line to check. - include_state: An _IncludeState instance in which the headers are inserted. - error: The function to call with any errors found. - """ - fileinfo = FileInfo(filename) - - line = clean_lines.lines[linenum] - - # "include" should use the new style "foo/bar.h" instead of just "bar.h" - if _RE_PATTERN_INCLUDE_NEW_STYLE.search(line): - error(filename, linenum, 'build/include', 4, - 'Include the directory when naming .h files') - - # we shouldn't include a file more than once. actually, there are a - # handful of instances where doing so is okay, but in general it's - # not. - match = _RE_PATTERN_INCLUDE.search(line) - if match: - include = match.group(2) - is_system = (match.group(1) == '<') - if include in include_state: - error(filename, linenum, 'build/include', 4, - '"%s" already included at %s:%s' % - (include, filename, include_state[include])) - else: - include_state[include] = linenum - - # We want to ensure that headers appear in the right order: - # 1) for foo.cc, foo.h (preferred location) - # 2) c system files - # 3) cpp system files - # 4) for foo.cc, foo.h (deprecated location) - # 5) other google headers - # - # We classify each include statement as one of those 5 types - # using a number of techniques. The include_state object keeps - # track of the highest type seen, and complains if we see a - # lower type after that. - error_message = include_state.CheckNextIncludeOrder( - _ClassifyInclude(fileinfo, include, is_system)) - if error_message: - error(filename, linenum, 'build/include_order', 4, - '%s. Should be: %s.h, c system, c++ system, other.' % - (error_message, fileinfo.BaseName())) - if not include_state.IsInAlphabeticalOrder(include): - error(filename, linenum, 'build/include_alpha', 4, - 'Include "%s" not in alphabetical order' % include) - - # Look for any of the stream classes that are part of standard C++. - match = _RE_PATTERN_INCLUDE.match(line) - if match: - include = match.group(2) - if Match(r'(f|ind|io|i|o|parse|pf|stdio|str|)?stream$', include): - # Many unit tests use cout, so we exempt them. - if not _IsTestFilename(filename): - error(filename, linenum, 'readability/streams', 3, - 'Streams are highly discouraged.') - - -def _GetTextInside(text, start_pattern): - """Retrieves all the text between matching open and close parentheses. - - Given a string of lines and a regular expression string, retrieve all the text - following the expression and between opening punctuation symbols like - (, [, or {, and the matching close-punctuation symbol. This properly nested - occurrences of the punctuations, so for the text like - printf(a(), b(c())); - a call to _GetTextInside(text, r'printf\(') will return 'a(), b(c())'. - start_pattern must match string having an open punctuation symbol at the end. - - Args: - text: The lines to extract text. Its comments and strings must be elided. - It can be single line and can span multiple lines. - start_pattern: The regexp string indicating where to start extracting - the text. - Returns: - The extracted text. - None if either the opening string or ending punctuation could not be found. - """ - # TODO(sugawarayu): Audit cpplint.py to see what places could be profitably - # rewritten to use _GetTextInside (and use inferior regexp matching today). - - # Give opening punctuations to get the matching close-punctuations. - matching_punctuation = {'(': ')', '{': '}', '[': ']'} - closing_punctuation = set(matching_punctuation.itervalues()) - - # Find the position to start extracting text. - match = re.search(start_pattern, text, re.M) - if not match: # start_pattern not found in text. - return None - start_position = match.end(0) - - assert start_position > 0, ( - 'start_pattern must ends with an opening punctuation.') - assert text[start_position - 1] in matching_punctuation, ( - 'start_pattern must ends with an opening punctuation.') - # Stack of closing punctuations we expect to have in text after position. - punctuation_stack = [matching_punctuation[text[start_position - 1]]] - position = start_position - while punctuation_stack and position < len(text): - if text[position] == punctuation_stack[-1]: - punctuation_stack.pop() - elif text[position] in closing_punctuation: - # A closing punctuation without matching opening punctuations. - return None - elif text[position] in matching_punctuation: - punctuation_stack.append(matching_punctuation[text[position]]) - position += 1 - if punctuation_stack: - # Opening punctuations left without matching close-punctuations. - return None - # punctuations match. - return text[start_position:position - 1] - - -def CheckLanguage(filename, clean_lines, linenum, file_extension, include_state, - error): - """Checks rules from the 'C++ language rules' section of cppguide.html. - - Some of these rules are hard to test (function overloading, using - uint32 inappropriately), but we do the best we can. - - Args: - filename: The name of the current file. - clean_lines: A CleansedLines instance containing the file. - linenum: The number of the line to check. - file_extension: The extension (without the dot) of the filename. - include_state: An _IncludeState instance in which the headers are inserted. - error: The function to call with any errors found. - """ - # If the line is empty or consists of entirely a comment, no need to - # check it. - line = clean_lines.elided[linenum] - if not line: - return - - match = _RE_PATTERN_INCLUDE.search(line) - if match: - CheckIncludeLine(filename, clean_lines, linenum, include_state, error) - return - - # Create an extended_line, which is the concatenation of the current and - # next lines, for more effective checking of code that may span more than one - # line. - if linenum + 1 < clean_lines.NumLines(): - extended_line = line + clean_lines.elided[linenum + 1] - else: - extended_line = line - - # Make Windows paths like Unix. - fullname = os.path.abspath(filename).replace('\\', '/') - - # TODO(unknown): figure out if they're using default arguments in fn proto. - - # Check for non-const references in functions. This is tricky because & - # is also used to take the address of something. We allow <> for templates, - # (ignoring whatever is between the braces) and : for classes. - # These are complicated re's. They try to capture the following: - # paren (for fn-prototype start), typename, &, varname. For the const - # version, we're willing for const to be before typename or after - # Don't check the implementation on same line. - fnline = line.split('{', 1)[0] - if (len(re.findall(r'\([^()]*\b(?:[\w:]|<[^()]*>)+(\s?&|&\s?)\w+', fnline)) > - len(re.findall(r'\([^()]*\bconst\s+(?:typename\s+)?(?:struct\s+)?' - r'(?:[\w:]|<[^()]*>)+(\s?&|&\s?)\w+', fnline)) + - len(re.findall(r'\([^()]*\b(?:[\w:]|<[^()]*>)+\s+const(\s?&|&\s?)[\w]+', - fnline))): - - # We allow non-const references in a few standard places, like functions - # called "swap()" or iostream operators like "<<" or ">>". We also filter - # out for loops, which lint otherwise mistakenly thinks are functions. - if not Search( - r'(for|swap|Swap|operator[<>][<>])\s*\(\s*' - r'(?:(?:typename\s*)?[\w:]|<.*>)+\s*&', - fnline): - error(filename, linenum, 'runtime/references', 2, - 'Is this a non-const reference? ' - 'If so, make const or use a pointer.') - - # Check to see if they're using an conversion function cast. - # I just try to capture the most common basic types, though there are more. - # Parameterless conversion functions, such as bool(), are allowed as they are - # probably a member operator declaration or default constructor. - match = Search( - r'(\bnew\s+)?\b' # Grab 'new' operator, if it's there - r'(int|float|double|bool|char|int32|uint32|int64|uint64)\([^)]', line) - if match: - # gMock methods are defined using some variant of MOCK_METHODx(name, type) - # where type may be float(), int(string), etc. Without context they are - # virtually indistinguishable from int(x) casts. Likewise, gMock's - # MockCallback takes a template parameter of the form return_type(arg_type), - # which looks much like the cast we're trying to detect. - if (match.group(1) is None and # If new operator, then this isn't a cast - not (Match(r'^\s*MOCK_(CONST_)?METHOD\d+(_T)?\(', line) or - Match(r'^\s*MockCallback<.*>', line))): - # Try a bit harder to catch gmock lines: the only place where - # something looks like an old-style cast is where we declare the - # return type of the mocked method, and the only time when we - # are missing context is if MOCK_METHOD was split across - # multiple lines (for example http://go/hrfhr ), so we only need - # to check the previous line for MOCK_METHOD. - if (linenum == 0 or - not Match(r'^\s*MOCK_(CONST_)?METHOD\d+(_T)?\(\S+,\s*$', - clean_lines.elided[linenum - 1])): - error(filename, linenum, 'readability/casting', 4, - 'Using deprecated casting style. ' - 'Use static_cast<%s>(...) instead' % - match.group(2)) - - CheckCStyleCast(filename, linenum, line, clean_lines.raw_lines[linenum], - 'static_cast', - r'\((int|float|double|bool|char|u?int(16|32|64))\)', error) - - # This doesn't catch all cases. Consider (const char * const)"hello". - # - # (char *) "foo" should always be a const_cast (reinterpret_cast won't - # compile). - if CheckCStyleCast(filename, linenum, line, clean_lines.raw_lines[linenum], - 'const_cast', r'\((char\s?\*+\s?)\)\s*"', error): - pass - else: - # Check pointer casts for other than string constants - CheckCStyleCast(filename, linenum, line, clean_lines.raw_lines[linenum], - 'reinterpret_cast', r'\((\w+\s?\*+\s?)\)', error) - - # In addition, we look for people taking the address of a cast. This - # is dangerous -- casts can assign to temporaries, so the pointer doesn't - # point where you think. - if Search( - r'(&\([^)]+\)[\w(])|(&(static|dynamic|reinterpret)_cast\b)', line): - error(filename, linenum, 'runtime/casting', 4, - ('Are you taking an address of a cast? ' - 'This is dangerous: could be a temp var. ' - 'Take the address before doing the cast, rather than after')) - - # Check for people declaring static/global STL strings at the top level. - # This is dangerous because the C++ language does not guarantee that - # globals with constructors are initialized before the first access. - match = Match( - r'((?:|static +)(?:|const +))string +([a-zA-Z0-9_:]+)\b(.*)', - line) - # Make sure it's not a function. - # Function template specialization looks like: "string foo<Type>(...". - # Class template definitions look like: "string Foo<Type>::Method(...". - if match and not Match(r'\s*(<.*>)?(::[a-zA-Z0-9_]+)?\s*\(([^"]|$)', - match.group(3)): - error(filename, linenum, 'runtime/string', 4, - 'For a static/global string constant, use a C style string instead: ' - '"%schar %s[]".' % - (match.group(1), match.group(2))) - - # Check that we're not using RTTI outside of testing code. - if Search(r'\bdynamic_cast<', line) and not _IsTestFilename(filename): - error(filename, linenum, 'runtime/rtti', 5, - 'Do not use dynamic_cast<>. If you need to cast within a class ' - "hierarchy, use static_cast<> to upcast. Google doesn't support " - 'RTTI.') - - if Search(r'\b([A-Za-z0-9_]*_)\(\1\)', line): - error(filename, linenum, 'runtime/init', 4, - 'You seem to be initializing a member variable with itself.') - - if file_extension == 'h': - # TODO(unknown): check that 1-arg constructors are explicit. - # How to tell it's a constructor? - # (handled in CheckForNonStandardConstructs for now) - # TODO(unknown): check that classes have DISALLOW_EVIL_CONSTRUCTORS - # (level 1 error) - pass - - # Check if people are using the verboten C basic types. The only exception - # we regularly allow is "unsigned short port" for port. - if Search(r'\bshort port\b', line): - if not Search(r'\bunsigned short port\b', line): - error(filename, linenum, 'runtime/int', 4, - 'Use "unsigned short" for ports, not "short"') - else: - match = Search(r'\b(short|long(?! +double)|long long)\b', line) - if match: - error(filename, linenum, 'runtime/int', 4, - 'Use int16/int64/etc, rather than the C type %s' % match.group(1)) - - # When snprintf is used, the second argument shouldn't be a literal. - match = Search(r'snprintf\s*\(([^,]*),\s*([0-9]*)\s*,', line) - if match and match.group(2) != '0': - # If 2nd arg is zero, snprintf is used to calculate size. - error(filename, linenum, 'runtime/printf', 3, - 'If you can, use sizeof(%s) instead of %s as the 2nd arg ' - 'to snprintf.' % (match.group(1), match.group(2))) - - # Check if some verboten C functions are being used. - if Search(r'\bsprintf\b', line): - error(filename, linenum, 'runtime/printf', 5, - 'Never use sprintf. Use snprintf instead.') - match = Search(r'\b(strcpy|strcat)\b', line) - if match: - error(filename, linenum, 'runtime/printf', 4, - 'Almost always, snprintf is better than %s' % match.group(1)) - - if Search(r'\bsscanf\b', line): - error(filename, linenum, 'runtime/printf', 1, - 'sscanf can be ok, but is slow and can overflow buffers.') - - # Check if some verboten operator overloading is going on - # TODO(unknown): catch out-of-line unary operator&: - # class X {}; - # int operator&(const X& x) { return 42; } // unary operator& - # The trick is it's hard to tell apart from binary operator&: - # class Y { int operator&(const Y& x) { return 23; } }; // binary operator& - if Search(r'\boperator\s*&\s*\(\s*\)', line): - error(filename, linenum, 'runtime/operator', 4, - 'Unary operator& is dangerous. Do not use it.') - - # Check for suspicious usage of "if" like - # } if (a == b) { - if Search(r'\}\s*if\s*\(', line): - error(filename, linenum, 'readability/braces', 4, - 'Did you mean "else if"? If not, start a new line for "if".') - - # Check for potential format string bugs like printf(foo). - # We constrain the pattern not to pick things like DocidForPrintf(foo). - # Not perfect but it can catch printf(foo.c_str()) and printf(foo->c_str()) - # TODO(sugawarayu): Catch the following case. Need to change the calling - # convention of the whole function to process multiple line to handle it. - # printf( - # boy_this_is_a_really_long_variable_that_cannot_fit_on_the_prev_line); - printf_args = _GetTextInside(line, r'(?i)\b(string)?printf\s*\(') - if printf_args: - match = Match(r'([\w.\->()]+)$', printf_args) - if match and match.group(1) != '__VA_ARGS__': - function_name = re.search(r'\b((?:string)?printf)\s*\(', - line, re.I).group(1) - error(filename, linenum, 'runtime/printf', 4, - 'Potential format string bug. Do %s("%%s", %s) instead.' - % (function_name, match.group(1))) - - # Check for potential memset bugs like memset(buf, sizeof(buf), 0). - match = Search(r'memset\s*\(([^,]*),\s*([^,]*),\s*0\s*\)', line) - if match and not Match(r"^''|-?[0-9]+|0x[0-9A-Fa-f]$", match.group(2)): - error(filename, linenum, 'runtime/memset', 4, - 'Did you mean "memset(%s, 0, %s)"?' - % (match.group(1), match.group(2))) - - if Search(r'\busing namespace\b', line): - error(filename, linenum, 'build/namespaces', 5, - 'Do not use namespace using-directives. ' - 'Use using-declarations instead.') - - # Detect variable-length arrays. - match = Match(r'\s*(.+::)?(\w+) [a-z]\w*\[(.+)];', line) - if (match and match.group(2) != 'return' and match.group(2) != 'delete' and - match.group(3).find(']') == -1): - # Split the size using space and arithmetic operators as delimiters. - # If any of the resulting tokens are not compile time constants then - # report the error. - tokens = re.split(r'\s|\+|\-|\*|\/|<<|>>]', match.group(3)) - is_const = True - skip_next = False - for tok in tokens: - if skip_next: - skip_next = False - continue - - if Search(r'sizeof\(.+\)', tok): continue - if Search(r'arraysize\(\w+\)', tok): continue - - tok = tok.lstrip('(') - tok = tok.rstrip(')') - if not tok: continue - if Match(r'\d+', tok): continue - if Match(r'0[xX][0-9a-fA-F]+', tok): continue - if Match(r'k[A-Z0-9]\w*', tok): continue - if Match(r'(.+::)?k[A-Z0-9]\w*', tok): continue - if Match(r'(.+::)?[A-Z][A-Z0-9_]*', tok): continue - # A catch all for tricky sizeof cases, including 'sizeof expression', - # 'sizeof(*type)', 'sizeof(const type)', 'sizeof(struct StructName)' - # requires skipping the next token because we split on ' ' and '*'. - if tok.startswith('sizeof'): - skip_next = True - continue - is_const = False - break - if not is_const: - error(filename, linenum, 'runtime/arrays', 1, - 'Do not use variable-length arrays. Use an appropriately named ' - "('k' followed by CamelCase) compile-time constant for the size.") - - # If DISALLOW_EVIL_CONSTRUCTORS, DISALLOW_COPY_AND_ASSIGN, or - # DISALLOW_IMPLICIT_CONSTRUCTORS is present, then it should be the last thing - # in the class declaration. - match = Match( - (r'\s*' - r'(DISALLOW_(EVIL_CONSTRUCTORS|COPY_AND_ASSIGN|IMPLICIT_CONSTRUCTORS))' - r'\(.*\);$'), - line) - if match and linenum + 1 < clean_lines.NumLines(): - next_line = clean_lines.elided[linenum + 1] - # We allow some, but not all, declarations of variables to be present - # in the statement that defines the class. The [\w\*,\s]* fragment of - # the regular expression below allows users to declare instances of - # the class or pointers to instances, but not less common types such - # as function pointers or arrays. It's a tradeoff between allowing - # reasonable code and avoiding trying to parse more C++ using regexps. - if not Search(r'^\s*}[\w\*,\s]*;', next_line): - error(filename, linenum, 'readability/constructors', 3, - match.group(1) + ' should be the last thing in the class') - - # Check for use of unnamed namespaces in header files. Registration - # macros are typically OK, so we allow use of "namespace {" on lines - # that end with backslashes. - if (file_extension == 'h' - and Search(r'\bnamespace\s*{', line) - and line[-1] != '\\'): - error(filename, linenum, 'build/namespaces', 4, - 'Do not use unnamed namespaces in header files. See ' - 'http://google-styleguide.googlecode.com/svn/trunk/cppguide.xml#Namespaces' - ' for more information.') - - -def CheckCStyleCast(filename, linenum, line, raw_line, cast_type, pattern, - error): - """Checks for a C-style cast by looking for the pattern. - - This also handles sizeof(type) warnings, due to similarity of content. - - Args: - filename: The name of the current file. - linenum: The number of the line to check. - line: The line of code to check. - raw_line: The raw line of code to check, with comments. - cast_type: The string for the C++ cast to recommend. This is either - reinterpret_cast, static_cast, or const_cast, depending. - pattern: The regular expression used to find C-style casts. - error: The function to call with any errors found. - - Returns: - True if an error was emitted. - False otherwise. - """ - match = Search(pattern, line) - if not match: - return False - - # e.g., sizeof(int) - sizeof_match = Match(r'.*sizeof\s*$', line[0:match.start(1) - 1]) - if sizeof_match: - error(filename, linenum, 'runtime/sizeof', 1, - 'Using sizeof(type). Use sizeof(varname) instead if possible') - return True - - # operator++(int) and operator--(int) - if (line[0:match.start(1) - 1].endswith(' operator++') or - line[0:match.start(1) - 1].endswith(' operator--')): - return False - - remainder = line[match.end(0):] - - # The close paren is for function pointers as arguments to a function. - # eg, void foo(void (*bar)(int)); - # The semicolon check is a more basic function check; also possibly a - # function pointer typedef. - # eg, void foo(int); or void foo(int) const; - # The equals check is for function pointer assignment. - # eg, void *(*foo)(int) = ... - # The > is for MockCallback<...> ... - # - # Right now, this will only catch cases where there's a single argument, and - # it's unnamed. It should probably be expanded to check for multiple - # arguments with some unnamed. - function_match = Match(r'\s*(\)|=|(const)?\s*(;|\{|throw\(\)|>))', remainder) - if function_match: - if (not function_match.group(3) or - function_match.group(3) == ';' or - ('MockCallback<' not in raw_line and - '/*' not in raw_line)): - error(filename, linenum, 'readability/function', 3, - 'All parameters should be named in a function') - return True - - # At this point, all that should be left is actual casts. - error(filename, linenum, 'readability/casting', 4, - 'Using C-style cast. Use %s<%s>(...) instead' % - (cast_type, match.group(1))) - - return True - - -_HEADERS_CONTAINING_TEMPLATES = ( - ('<deque>', ('deque',)), - ('<functional>', ('unary_function', 'binary_function', - 'plus', 'minus', 'multiplies', 'divides', 'modulus', - 'negate', - 'equal_to', 'not_equal_to', 'greater', 'less', - 'greater_equal', 'less_equal', - 'logical_and', 'logical_or', 'logical_not', - 'unary_negate', 'not1', 'binary_negate', 'not2', - 'bind1st', 'bind2nd', - 'pointer_to_unary_function', - 'pointer_to_binary_function', - 'ptr_fun', - 'mem_fun_t', 'mem_fun', 'mem_fun1_t', 'mem_fun1_ref_t', - 'mem_fun_ref_t', - 'const_mem_fun_t', 'const_mem_fun1_t', - 'const_mem_fun_ref_t', 'const_mem_fun1_ref_t', - 'mem_fun_ref', - )), - ('<limits>', ('numeric_limits',)), - ('<list>', ('list',)), - ('<map>', ('map', 'multimap',)), - ('<memory>', ('allocator',)), - ('<queue>', ('queue', 'priority_queue',)), - ('<set>', ('set', 'multiset',)), - ('<stack>', ('stack',)), - ('<string>', ('char_traits', 'basic_string',)), - ('<utility>', ('pair',)), - ('<vector>', ('vector',)), - - # gcc extensions. - # Note: std::hash is their hash, ::hash is our hash - ('<hash_map>', ('hash_map', 'hash_multimap',)), - ('<hash_set>', ('hash_set', 'hash_multiset',)), - ('<slist>', ('slist',)), - ) - -_RE_PATTERN_STRING = re.compile(r'\bstring\b') - -_re_pattern_algorithm_header = [] -for _template in ('copy', 'max', 'min', 'min_element', 'sort', 'swap', - 'transform'): - # Match max<type>(..., ...), max(..., ...), but not foo->max, foo.max or - # type::max(). - _re_pattern_algorithm_header.append( - (re.compile(r'[^>.]\b' + _template + r'(<.*?>)?\([^\)]'), - _template, - '<algorithm>')) - -_re_pattern_templates = [] -for _header, _templates in _HEADERS_CONTAINING_TEMPLATES: - for _template in _templates: - _re_pattern_templates.append( - (re.compile(r'(\<|\b)' + _template + r'\s*\<'), - _template + '<>', - _header)) - - -def FilesBelongToSameModule(filename_cc, filename_h): - """Check if these two filenames belong to the same module. - - The concept of a 'module' here is a as follows: - foo.h, foo-inl.h, foo.cc, foo_test.cc and foo_unittest.cc belong to the - same 'module' if they are in the same directory. - some/path/public/xyzzy and some/path/internal/xyzzy are also considered - to belong to the same module here. - - If the filename_cc contains a longer path than the filename_h, for example, - '/absolute/path/to/base/sysinfo.cc', and this file would include - 'base/sysinfo.h', this function also produces the prefix needed to open the - header. This is used by the caller of this function to more robustly open the - header file. We don't have access to the real include paths in this context, - so we need this guesswork here. - - Known bugs: tools/base/bar.cc and base/bar.h belong to the same module - according to this implementation. Because of this, this function gives - some false positives. This should be sufficiently rare in practice. - - Args: - filename_cc: is the path for the .cc file - filename_h: is the path for the header path - - Returns: - Tuple with a bool and a string: - bool: True if filename_cc and filename_h belong to the same module. - string: the additional prefix needed to open the header file. - """ - - if not filename_cc.endswith('.cc'): - return (False, '') - filename_cc = filename_cc[:-len('.cc')] - if filename_cc.endswith('_unittest'): - filename_cc = filename_cc[:-len('_unittest')] - elif filename_cc.endswith('_test'): - filename_cc = filename_cc[:-len('_test')] - filename_cc = filename_cc.replace('/public/', '/') - filename_cc = filename_cc.replace('/internal/', '/') - - if not filename_h.endswith('.h'): - return (False, '') - filename_h = filename_h[:-len('.h')] - if filename_h.endswith('-inl'): - filename_h = filename_h[:-len('-inl')] - filename_h = filename_h.replace('/public/', '/') - filename_h = filename_h.replace('/internal/', '/') - - files_belong_to_same_module = filename_cc.endswith(filename_h) - common_path = '' - if files_belong_to_same_module: - common_path = filename_cc[:-len(filename_h)] - return files_belong_to_same_module, common_path - - -def UpdateIncludeState(filename, include_state, io=codecs): - """Fill up the include_state with new includes found from the file. - - Args: - filename: the name of the header to read. - include_state: an _IncludeState instance in which the headers are inserted. - io: The io factory to use to read the file. Provided for testability. - - Returns: - True if a header was successfully added. False otherwise. - """ - headerfile = None - try: - headerfile = io.open(filename, 'r', 'utf8', 'replace') - except IOError: - return False - linenum = 0 - for line in headerfile: - linenum += 1 - clean_line = CleanseComments(line) - match = _RE_PATTERN_INCLUDE.search(clean_line) - if match: - include = match.group(2) - # The value formatting is cute, but not really used right now. - # What matters here is that the key is in include_state. - include_state.setdefault(include, '%s:%d' % (filename, linenum)) - return True - - -def CheckForIncludeWhatYouUse(filename, clean_lines, include_state, error, - io=codecs): - """Reports for missing stl includes. - - This function will output warnings to make sure you are including the headers - necessary for the stl containers and functions that you use. We only give one - reason to include a header. For example, if you use both equal_to<> and - less<> in a .h file, only one (the latter in the file) of these will be - reported as a reason to include the <functional>. - - Args: - filename: The name of the current file. - clean_lines: A CleansedLines instance containing the file. - include_state: An _IncludeState instance. - error: The function to call with any errors found. - io: The IO factory to use to read the header file. Provided for unittest - injection. - """ - required = {} # A map of header name to linenumber and the template entity. - # Example of required: { '<functional>': (1219, 'less<>') } - - for linenum in xrange(clean_lines.NumLines()): - line = clean_lines.elided[linenum] - if not line or line[0] == '#': - continue - - # String is special -- it is a non-templatized type in STL. - matched = _RE_PATTERN_STRING.search(line) - if matched: - # Don't warn about strings in non-STL namespaces: - # (We check only the first match per line; good enough.) - prefix = line[:matched.start()] - if prefix.endswith('std::') or not prefix.endswith('::'): - required['<string>'] = (linenum, 'string') - - for pattern, template, header in _re_pattern_algorithm_header: - if pattern.search(line): - required[header] = (linenum, template) - - # The following function is just a speed up, no semantics are changed. - if not '<' in line: # Reduces the cpu time usage by skipping lines. - continue - - for pattern, template, header in _re_pattern_templates: - if pattern.search(line): - required[header] = (linenum, template) - - # The policy is that if you #include something in foo.h you don't need to - # include it again in foo.cc. Here, we will look at possible includes. - # Let's copy the include_state so it is only messed up within this function. - include_state = include_state.copy() - - # Did we find the header for this file (if any) and successfully load it? - header_found = False - - # Use the absolute path so that matching works properly. - abs_filename = FileInfo(filename).FullName() - - # For Emacs's flymake. - # If cpplint is invoked from Emacs's flymake, a temporary file is generated - # by flymake and that file name might end with '_flymake.cc'. In that case, - # restore original file name here so that the corresponding header file can be - # found. - # e.g. If the file name is 'foo_flymake.cc', we should search for 'foo.h' - # instead of 'foo_flymake.h' - abs_filename = re.sub(r'_flymake\.cc$', '.cc', abs_filename) - - # include_state is modified during iteration, so we iterate over a copy of - # the keys. - header_keys = include_state.keys() - for header in header_keys: - (same_module, common_path) = FilesBelongToSameModule(abs_filename, header) - fullpath = common_path + header - if same_module and UpdateIncludeState(fullpath, include_state, io): - header_found = True - - # If we can't find the header file for a .cc, assume it's because we don't - # know where to look. In that case we'll give up as we're not sure they - # didn't include it in the .h file. - # TODO(unknown): Do a better job of finding .h files so we are confident that - # not having the .h file means there isn't one. - if filename.endswith('.cc') and not header_found: - return - - # All the lines have been processed, report the errors found. - for required_header_unstripped in required: - template = required[required_header_unstripped][1] - if required_header_unstripped.strip('<>"') not in include_state: - error(filename, required[required_header_unstripped][0], - 'build/include_what_you_use', 4, - 'Add #include ' + required_header_unstripped + ' for ' + template) - - -_RE_PATTERN_EXPLICIT_MAKEPAIR = re.compile(r'\bmake_pair\s*<') - - -def CheckMakePairUsesDeduction(filename, clean_lines, linenum, error): - """Check that make_pair's template arguments are deduced. - - G++ 4.6 in C++0x mode fails badly if make_pair's template arguments are - specified explicitly, and such use isn't intended in any case. - - Args: - filename: The name of the current file. - clean_lines: A CleansedLines instance containing the file. - linenum: The number of the line to check. - error: The function to call with any errors found. - """ - raw = clean_lines.raw_lines - line = raw[linenum] - match = _RE_PATTERN_EXPLICIT_MAKEPAIR.search(line) - if match: - error(filename, linenum, 'build/explicit_make_pair', - 4, # 4 = high confidence - 'For C++11-compatibility, omit template arguments from make_pair' - ' OR use pair directly OR if appropriate, construct a pair directly') - - -def ProcessLine(filename, file_extension, clean_lines, line, - include_state, function_state, nesting_state, error, - extra_check_functions=[]): - """Processes a single line in the file. - - Args: - filename: Filename of the file that is being processed. - file_extension: The extension (dot not included) of the file. - clean_lines: An array of strings, each representing a line of the file, - with comments stripped. - line: Number of line being processed. - include_state: An _IncludeState instance in which the headers are inserted. - function_state: A _FunctionState instance which counts function lines, etc. - nesting_state: A _NestingState instance which maintains information about - the current stack of nested blocks being parsed. - error: A callable to which errors are reported, which takes 4 arguments: - filename, line number, error level, and message - extra_check_functions: An array of additional check functions that will be - run on each source line. Each function takes 4 - arguments: filename, clean_lines, line, error - """ - raw_lines = clean_lines.raw_lines - ParseNolintSuppressions(filename, raw_lines[line], line, error) - nesting_state.Update(filename, clean_lines, line, error) - if nesting_state.stack and nesting_state.stack[-1].inline_asm != _NO_ASM: - return - CheckForFunctionLengths(filename, clean_lines, line, function_state, error) - CheckForMultilineCommentsAndStrings(filename, clean_lines, line, error) - CheckStyle(filename, clean_lines, line, file_extension, nesting_state, error) - CheckLanguage(filename, clean_lines, line, file_extension, include_state, - error) - CheckForNonStandardConstructs(filename, clean_lines, line, - nesting_state, error) - CheckPosixThreading(filename, clean_lines, line, error) - CheckInvalidIncrement(filename, clean_lines, line, error) - CheckMakePairUsesDeduction(filename, clean_lines, line, error) - for check_fn in extra_check_functions: - check_fn(filename, clean_lines, line, error) - -def ProcessFileData(filename, file_extension, lines, error, - extra_check_functions=[]): - """Performs lint checks and reports any errors to the given error function. - - Args: - filename: Filename of the file that is being processed. - file_extension: The extension (dot not included) of the file. - lines: An array of strings, each representing a line of the file, with the - last element being empty if the file is terminated with a newline. - error: A callable to which errors are reported, which takes 4 arguments: - filename, line number, error level, and message - extra_check_functions: An array of additional check functions that will be - run on each source line. Each function takes 4 - arguments: filename, clean_lines, line, error - """ - lines = (['// marker so line numbers and indices both start at 1'] + lines + - ['// marker so line numbers end in a known way']) - - include_state = _IncludeState() - function_state = _FunctionState() - nesting_state = _NestingState() - - ResetNolintSuppressions() - - CheckForCopyright(filename, lines, error) - - if file_extension == 'h': - CheckForHeaderGuard(filename, lines, error) - - RemoveMultiLineComments(filename, lines, error) - clean_lines = CleansedLines(lines) - for line in xrange(clean_lines.NumLines()): - ProcessLine(filename, file_extension, clean_lines, line, - include_state, function_state, nesting_state, error, - extra_check_functions) - nesting_state.CheckClassFinished(filename, error) - - CheckForIncludeWhatYouUse(filename, clean_lines, include_state, error) - - # We check here rather than inside ProcessLine so that we see raw - # lines rather than "cleaned" lines. - CheckForUnicodeReplacementCharacters(filename, lines, error) - - CheckForNewlineAtEOF(filename, lines, error) - -def ProcessFile(filename, vlevel, extra_check_functions=[]): - """Does google-lint on a single file. - - Args: - filename: The name of the file to parse. - - vlevel: The level of errors to report. Every error of confidence - >= verbose_level will be reported. 0 is a good default. - - extra_check_functions: An array of additional check functions that will be - run on each source line. Each function takes 4 - arguments: filename, clean_lines, line, error - """ - - _SetVerboseLevel(vlevel) - - try: - # Support the UNIX convention of using "-" for stdin. Note that - # we are not opening the file with universal newline support - # (which codecs doesn't support anyway), so the resulting lines do - # contain trailing '\r' characters if we are reading a file that - # has CRLF endings. - # If after the split a trailing '\r' is present, it is removed - # below. If it is not expected to be present (i.e. os.linesep != - # '\r\n' as in Windows), a warning is issued below if this file - # is processed. - - if filename == '-': - lines = codecs.StreamReaderWriter(sys.stdin, - codecs.getreader('utf8'), - codecs.getwriter('utf8'), - 'replace').read().split('\n') - else: - lines = codecs.open(filename, 'r', 'utf8', 'replace').read().split('\n') - - carriage_return_found = False - # Remove trailing '\r'. - for linenum in range(len(lines)): - if lines[linenum].endswith('\r'): - lines[linenum] = lines[linenum].rstrip('\r') - carriage_return_found = True - - except IOError: - sys.stderr.write( - "Skipping input '%s': Can't open for reading\n" % filename) - return - - # Note, if no dot is found, this will give the entire filename as the ext. - file_extension = filename[filename.rfind('.') + 1:] - - # When reading from stdin, the extension is unknown, so no cpplint tests - # should rely on the extension. - if (filename != '-' and file_extension != 'cc' and file_extension != 'h' - and file_extension != 'cpp'): - sys.stderr.write('Ignoring %s; not a .cc or .h file\n' % filename) - else: - ProcessFileData(filename, file_extension, lines, Error, - extra_check_functions) - if carriage_return_found and os.linesep != '\r\n': - # Use 0 for linenum since outputting only one error for potentially - # several lines. - Error(filename, 0, 'whitespace/newline', 1, - 'One or more unexpected \\r (^M) found;' - 'better to use only a \\n') - - sys.stderr.write('Done processing %s\n' % filename) - - -def PrintUsage(message): - """Prints a brief usage string and exits, optionally with an error message. - - Args: - message: The optional error message. - """ - sys.stderr.write(_USAGE) - if message: - sys.exit('\nFATAL ERROR: ' + message) - else: - sys.exit(1) - - -def PrintCategories(): - """Prints a list of all the error-categories used by error messages. - - These are the categories used to filter messages via --filter. - """ - sys.stderr.write(''.join(' %s\n' % cat for cat in _ERROR_CATEGORIES)) - sys.exit(0) - - -def ParseArguments(args): - """Parses the command line arguments. - - This may set the output format and verbosity level as side-effects. - - Args: - args: The command line arguments: - - Returns: - The list of filenames to lint. - """ - try: - (opts, filenames) = getopt.getopt(args, '', ['help', 'output=', 'verbose=', - 'counting=', - 'filter=', - 'root=']) - except getopt.GetoptError: - PrintUsage('Invalid arguments.') - - verbosity = _VerboseLevel() - output_format = _OutputFormat() - filters = '' - counting_style = '' - - for (opt, val) in opts: - if opt == '--help': - PrintUsage(None) - elif opt == '--output': - if not val in ('emacs', 'vs7', 'eclipse'): - PrintUsage('The only allowed output formats are emacs, vs7 and eclipse.') - output_format = val - elif opt == '--verbose': - verbosity = int(val) - elif opt == '--filter': - filters = val - if not filters: - PrintCategories() - elif opt == '--counting': - if val not in ('total', 'toplevel', 'detailed'): - PrintUsage('Valid counting options are total, toplevel, and detailed') - counting_style = val - elif opt == '--root': - global _root - _root = val - - if not filenames: - PrintUsage('No files were specified.') - - _SetOutputFormat(output_format) - _SetVerboseLevel(verbosity) - _SetFilters(filters) - _SetCountingStyle(counting_style) - - return filenames - - -def main(): - filenames = ParseArguments(sys.argv[1:]) - - # Change stderr to write with replacement characters so we don't die - # if we try to print something containing non-ASCII characters. - sys.stderr = codecs.StreamReaderWriter(sys.stderr, - codecs.getreader('utf8'), - codecs.getwriter('utf8'), - 'replace') - - _cpplint_state.ResetErrorCounts() - for filename in filenames: - ProcessFile(filename, _cpplint_state.verbose_level) - _cpplint_state.PrintErrorCounts() - - sys.exit(_cpplint_state.error_count > 0) - - -if __name__ == '__main__': - main() diff --git a/contrib/compiler-rt/lib/sanitizer_common/scripts/gen_dynamic_list.py b/contrib/compiler-rt/lib/sanitizer_common/scripts/gen_dynamic_list.py deleted file mode 100755 index f055bb44ba21..000000000000 --- a/contrib/compiler-rt/lib/sanitizer_common/scripts/gen_dynamic_list.py +++ /dev/null @@ -1,110 +0,0 @@ -#!/usr/bin/env python -#===- lib/sanitizer_common/scripts/gen_dynamic_list.py ---------------------===# -# -# The LLVM Compiler Infrastructure -# -# This file is distributed under the University of Illinois Open Source -# License. See LICENSE.TXT for details. -# -#===------------------------------------------------------------------------===# -# -# Generates the list of functions that should be exported from sanitizer -# runtimes. The output format is recognized by --dynamic-list linker option. -# Usage: -# gen_dynamic_list.py libclang_rt.*san*.a [ files ... ] -# -#===------------------------------------------------------------------------===# -import argparse -import os -import re -import subprocess -import sys - -new_delete = set([ - '_Znam', '_ZnamRKSt9nothrow_t', # operator new[](unsigned long) - '_Znwm', '_ZnwmRKSt9nothrow_t', # operator new(unsigned long) - '_Znaj', '_ZnajRKSt9nothrow_t', # operator new[](unsigned int) - '_Znwj', '_ZnwjRKSt9nothrow_t', # operator new(unsigned int) - '_ZdaPv', '_ZdaPvRKSt9nothrow_t', # operator delete[](void *) - '_ZdlPv', '_ZdlPvRKSt9nothrow_t', # operator delete(void *) - '_ZdaPvm', # operator delete[](void*, unsigned long) - '_ZdlPvm', # operator delete(void*, unsigned long) - '_ZdaPvj', # operator delete[](void*, unsigned int) - '_ZdlPvj', # operator delete(void*, unsigned int) - ]) - -versioned_functions = set(['memcpy', 'pthread_attr_getaffinity_np', - 'pthread_cond_broadcast', - 'pthread_cond_destroy', 'pthread_cond_init', - 'pthread_cond_signal', 'pthread_cond_timedwait', - 'pthread_cond_wait', 'realpath', - 'sched_getaffinity']) - -def get_global_functions(library): - functions = [] - nm_proc = subprocess.Popen(['nm', library], stdout=subprocess.PIPE, - stderr=subprocess.PIPE) - nm_out = nm_proc.communicate()[0].decode().split('\n') - if nm_proc.returncode != 0: - raise subprocess.CalledProcessError(nm_proc.returncode, 'nm') - func_symbols = ['T', 'W'] - # On PowerPC, nm prints function descriptors from .data section. - if os.uname()[4] in ["powerpc", "ppc64"]: - func_symbols += ['D'] - for line in nm_out: - cols = line.split(' ') - if len(cols) == 3 and cols[1] in func_symbols : - functions.append(cols[2]) - return functions - -def main(argv): - parser = argparse.ArgumentParser() - parser.add_argument('--version-list', action='store_true') - parser.add_argument('--extra', default=[], action='append') - parser.add_argument('libraries', default=[], nargs='+') - args = parser.parse_args() - - result = [] - - all_functions = [] - for library in args.libraries: - all_functions.extend(get_global_functions(library)) - function_set = set(all_functions) - for func in all_functions: - # Export new/delete operators. - if func in new_delete: - result.append(func) - continue - # Export interceptors. - match = re.match('__interceptor_(.*)', func) - if match: - result.append(func) - # We have to avoid exporting the interceptors for versioned library - # functions due to gold internal error. - orig_name = match.group(1) - if orig_name in function_set and (args.version_list or orig_name not in versioned_functions): - result.append(orig_name) - continue - # Export sanitizer interface functions. - if re.match('__sanitizer_(.*)', func): - result.append(func) - - # Additional exported functions from files. - for fname in args.extra: - f = open(fname, 'r') - for line in f: - result.append(line.rstrip()) - # Print the resulting list in the format recognized by ld. - print('{') - if args.version_list: - print('global:') - result.sort() - for f in result: - print(' ' + f.encode('utf-8') + ';') - if args.version_list: - print('local:') - print(' *;') - print('};') - -if __name__ == '__main__': - main(sys.argv) diff --git a/contrib/compiler-rt/lib/sanitizer_common/scripts/litlint.py b/contrib/compiler-rt/lib/sanitizer_common/scripts/litlint.py deleted file mode 100755 index 81b89c214438..000000000000 --- a/contrib/compiler-rt/lib/sanitizer_common/scripts/litlint.py +++ /dev/null @@ -1,72 +0,0 @@ -#!/usr/bin/env python -# -# litlint -# -# Ensure RUN commands in lit tests are free of common errors. -# -# If any errors are detected, litlint returns a nonzero exit code. -# - -import optparse -import re -import sys - -# Compile regex once for all files -runRegex = re.compile(r'(?<!-o)(?<!%run) %t\s') - -def LintLine(s): - """ Validate a line - - Args: - s: str, the line to validate - - Returns: - Returns an error message and a 1-based column number if an error was - detected, otherwise (None, None). - """ - - # Check that RUN command can be executed with an emulator - m = runRegex.search(s) - if m: - start, end = m.span() - return ('missing %run before %t', start + 2) - - # No errors - return (None, None) - - -def LintFile(p): - """ Check that each RUN command can be executed with an emulator - - Args: - p: str, valid path to a file - - Returns: - The number of errors detected. - """ - errs = 0 - with open(p, 'r') as f: - for i, s in enumerate(f.readlines(), start=1): - msg, col = LintLine(s) - if msg != None: - errs += 1 - errorMsg = 'litlint: {}:{}:{}: error: {}.\n{}{}\n' - arrow = (col-1) * ' ' + '^' - sys.stderr.write(errorMsg.format(p, i, col, msg, s, arrow)) - return errs - - -if __name__ == "__main__": - # Parse args - parser = optparse.OptionParser() - parser.add_option('--filter') # ignored - (options, filenames) = parser.parse_args() - - # Lint each file - errs = 0 - for p in filenames: - errs += LintFile(p) - - # If errors, return nonzero - if errs > 0: - sys.exit(1) diff --git a/contrib/compiler-rt/lib/sanitizer_common/scripts/litlint_test.py b/contrib/compiler-rt/lib/sanitizer_common/scripts/litlint_test.py deleted file mode 100755 index 3ce482d70444..000000000000 --- a/contrib/compiler-rt/lib/sanitizer_common/scripts/litlint_test.py +++ /dev/null @@ -1,23 +0,0 @@ -#!/usr/bin/python - -# Tests for litlint.py -# -# Usage: python litlint_test.py -# -# Returns nonzero if any test fails - -import litlint -import unittest - -class TestLintLine(unittest.TestCase): - def test_missing_run(self): - f = litlint.LintLine - self.assertEqual(f(' %t '), ('missing %run before %t', 2)) - self.assertEqual(f(' %t\n'), ('missing %run before %t', 2)) - self.assertEqual(f(' %t.so '), (None, None)) - self.assertEqual(f(' %t.o '), (None, None)) - self.assertEqual(f('%run %t '), (None, None)) - self.assertEqual(f('-o %t '), (None, None)) - -if __name__ == '__main__': - unittest.main() diff --git a/contrib/compiler-rt/lib/sanitizer_common/scripts/sancov.py b/contrib/compiler-rt/lib/sanitizer_common/scripts/sancov.py deleted file mode 100755 index a5ae9574a26a..000000000000 --- a/contrib/compiler-rt/lib/sanitizer_common/scripts/sancov.py +++ /dev/null @@ -1,241 +0,0 @@ -#!/usr/bin/env python -# Merge or print the coverage data collected by asan's coverage. -# Input files are sequences of 4-byte integers. -# We need to merge these integers into a set and then -# either print them (as hex) or dump them into another file. -import array -import bisect -import glob -import os.path -import struct -import subprocess -import sys - -prog_name = "" - -def Usage(): - print >> sys.stderr, "Usage: \n" + \ - " " + prog_name + " merge FILE [FILE...] > OUTPUT\n" \ - " " + prog_name + " print FILE [FILE...]\n" \ - " " + prog_name + " unpack FILE [FILE...]\n" \ - " " + prog_name + " rawunpack FILE [FILE ...]\n" \ - " " + prog_name + " missing BINARY < LIST_OF_PCS\n" - exit(1) - -def CheckBits(bits): - if bits != 32 and bits != 64: - raise Exception("Wrong bitness: %d" % bits) - -def TypeCodeForBits(bits): - CheckBits(bits) - return 'L' if bits == 64 else 'I' - -kMagic32SecondHalf = 0xFFFFFF32; -kMagic64SecondHalf = 0xFFFFFF64; -kMagicFirstHalf = 0xC0BFFFFF; - -def MagicForBits(bits): - CheckBits(bits) - if sys.byteorder == 'little': - return [kMagic64SecondHalf if bits == 64 else kMagic32SecondHalf, kMagicFirstHalf] - else: - return [kMagicFirstHalf, kMagic64SecondHalf if bits == 64 else kMagic32SecondHalf] - -def ReadMagicAndReturnBitness(f, path): - magic_bytes = f.read(8) - magic_words = struct.unpack('II', magic_bytes); - bits = 0 - idx = 1 if sys.byteorder == 'little' else 0 - if magic_words[idx] == kMagicFirstHalf: - if magic_words[1-idx] == kMagic64SecondHalf: - bits = 64 - elif magic_words[1-idx] == kMagic32SecondHalf: - bits = 32 - if bits == 0: - raise Exception('Bad magic word in %s' % path) - return bits - -def ReadOneFile(path): - with open(path, mode="rb") as f: - f.seek(0, 2) - size = f.tell() - f.seek(0, 0) - if size < 8: - raise Exception('File %s is short (< 8 bytes)' % path) - bits = ReadMagicAndReturnBitness(f, path) - size -= 8 - s = array.array(TypeCodeForBits(bits), f.read(size)) - print >>sys.stderr, "%s: read %d %d-bit PCs from %s" % (prog_name, size * 8 / bits, bits, path) - return s - -def Merge(files): - s = set() - for f in files: - s = s.union(set(ReadOneFile(f))) - print >> sys.stderr, "%s: %d files merged; %d PCs total" % \ - (prog_name, len(files), len(s)) - return sorted(s) - -def PrintFiles(files): - if len(files) > 1: - s = Merge(files) - else: # If there is just on file, print the PCs in order. - s = ReadOneFile(files[0]) - print >> sys.stderr, "%s: 1 file merged; %d PCs total" % \ - (prog_name, len(s)) - for i in s: - print "0x%x" % i - -def MergeAndPrint(files): - if sys.stdout.isatty(): - Usage() - s = Merge(files) - bits = 32 - if max(s) > 0xFFFFFFFF: - bits = 64 - array.array('I', MagicForBits(bits)).tofile(sys.stdout) - a = array.array(TypeCodeForBits(bits), s) - a.tofile(sys.stdout) - - -def UnpackOneFile(path): - with open(path, mode="rb") as f: - print >> sys.stderr, "%s: unpacking %s" % (prog_name, path) - while True: - header = f.read(12) - if not header: return - if len(header) < 12: - break - pid, module_length, blob_size = struct.unpack('iII', header) - module = f.read(module_length) - blob = f.read(blob_size) - assert(len(module) == module_length) - assert(len(blob) == blob_size) - extracted_file = "%s.%d.sancov" % (module, pid) - print >> sys.stderr, "%s: extracting %s" % \ - (prog_name, extracted_file) - # The packed file may contain multiple blobs for the same pid/module - # pair. Append to the end of the file instead of overwriting. - with open(extracted_file, 'ab') as f2: - f2.write(blob) - # fail - raise Exception('Error reading file %s' % path) - - -def Unpack(files): - for f in files: - UnpackOneFile(f) - -def UnpackOneRawFile(path, map_path): - mem_map = [] - with open(map_path, mode="rt") as f_map: - print >> sys.stderr, "%s: reading map %s" % (prog_name, map_path) - bits = int(f_map.readline()) - if bits != 32 and bits != 64: - raise Exception('Wrong bits size in the map') - for line in f_map: - parts = line.rstrip().split() - mem_map.append((int(parts[0], 16), - int(parts[1], 16), - int(parts[2], 16), - ' '.join(parts[3:]))) - mem_map.sort(key=lambda m : m[0]) - mem_map_keys = [m[0] for m in mem_map] - - with open(path, mode="rb") as f: - print >> sys.stderr, "%s: unpacking %s" % (prog_name, path) - - f.seek(0, 2) - size = f.tell() - f.seek(0, 0) - pcs = array.array(TypeCodeForBits(bits), f.read(size)) - mem_map_pcs = [[] for i in range(0, len(mem_map))] - - for pc in pcs: - if pc == 0: continue - map_idx = bisect.bisect(mem_map_keys, pc) - 1 - (start, end, base, module_path) = mem_map[map_idx] - assert pc >= start - if pc >= end: - print >> sys.stderr, "warning: %s: pc %x outside of any known mapping" % (prog_name, pc) - continue - mem_map_pcs[map_idx].append(pc - base) - - for ((start, end, base, module_path), pc_list) in zip(mem_map, mem_map_pcs): - if len(pc_list) == 0: continue - assert path.endswith('.sancov.raw') - dst_path = module_path + '.' + os.path.basename(path)[:-4] - print >> sys.stderr, "%s: writing %d PCs to %s" % (prog_name, len(pc_list), dst_path) - arr = array.array(TypeCodeForBits(bits)) - arr.fromlist(sorted(pc_list)) - with open(dst_path, 'ab') as f2: - array.array('I', MagicForBits(bits)).tofile(f2) - arr.tofile(f2) - -def RawUnpack(files): - for f in files: - if not f.endswith('.sancov.raw'): - raise Exception('Unexpected raw file name %s' % f) - f_map = f[:-3] + 'map' - UnpackOneRawFile(f, f_map) - -def GetInstrumentedPCs(binary): - # This looks scary, but all it does is extract all offsets where we call: - # - __sanitizer_cov() or __sanitizer_cov_with_check(), - # - with call or callq, - # - directly or via PLT. - cmd = "objdump -d %s | " \ - "grep '^\s\+[0-9a-f]\+:.*\scall\(q\|\)\s\+[0-9a-f]\+ <__sanitizer_cov\(_with_check\|\)\(@plt\|\)>' | " \ - "grep '^\s\+[0-9a-f]\+' -o" % binary - proc = subprocess.Popen(cmd, stdin=subprocess.PIPE, stdout=subprocess.PIPE, - shell=True) - proc.stdin.close() - # The PCs we get from objdump are off by 4 bytes, as they point to the - # beginning of the callq instruction. Empirically this is true on x86 and - # x86_64. - return set(int(line.strip(), 16) + 4 for line in proc.stdout) - -def PrintMissing(binary): - if not os.path.isfile(binary): - raise Exception('File not found: %s' % binary) - instrumented = GetInstrumentedPCs(binary) - print >> sys.stderr, "%s: found %d instrumented PCs in %s" % (prog_name, - len(instrumented), - binary) - covered = set(int(line, 16) for line in sys.stdin) - print >> sys.stderr, "%s: read %d PCs from stdin" % (prog_name, len(covered)) - missing = instrumented - covered - print >> sys.stderr, "%s: %d PCs missing from coverage" % (prog_name, len(missing)) - if (len(missing) > len(instrumented) - len(covered)): - print >> sys.stderr, \ - "%s: WARNING: stdin contains PCs not found in binary" % prog_name - for pc in sorted(missing): - print "0x%x" % pc - -if __name__ == '__main__': - prog_name = sys.argv[0] - if len(sys.argv) <= 2: - Usage(); - - if sys.argv[1] == "missing": - if len(sys.argv) != 3: - Usage() - PrintMissing(sys.argv[2]) - exit(0) - - file_list = [] - for f in sys.argv[2:]: - file_list += glob.glob(f) - if not file_list: - Usage() - - if sys.argv[1] == "print": - PrintFiles(file_list) - elif sys.argv[1] == "merge": - MergeAndPrint(file_list) - elif sys.argv[1] == "unpack": - Unpack(file_list) - elif sys.argv[1] == "rawunpack": - RawUnpack(file_list) - else: - Usage() diff --git a/contrib/compiler-rt/lib/sanitizer_common/tests/sanitizer_allocator_test.cc b/contrib/compiler-rt/lib/sanitizer_common/tests/sanitizer_allocator_test.cc deleted file mode 100644 index be8fc91aa861..000000000000 --- a/contrib/compiler-rt/lib/sanitizer_common/tests/sanitizer_allocator_test.cc +++ /dev/null @@ -1,860 +0,0 @@ -//===-- sanitizer_allocator_test.cc ---------------------------------------===// -// -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. -// -//===----------------------------------------------------------------------===// -// -// This file is a part of ThreadSanitizer/AddressSanitizer runtime. -// Tests for sanitizer_allocator.h. -// -//===----------------------------------------------------------------------===// -#include "sanitizer_common/sanitizer_allocator.h" -#include "sanitizer_common/sanitizer_allocator_internal.h" -#include "sanitizer_common/sanitizer_common.h" - -#include "sanitizer_test_utils.h" -#include "sanitizer_pthread_wrappers.h" - -#include "gtest/gtest.h" - -#include <stdlib.h> -#include <algorithm> -#include <vector> -#include <set> - -// Too slow for debug build -#if !SANITIZER_DEBUG - -#if SANITIZER_CAN_USE_ALLOCATOR64 -static const uptr kAllocatorSpace = 0x700000000000ULL; -static const uptr kAllocatorSize = 0x010000000000ULL; // 1T. -static const u64 kAddressSpaceSize = 1ULL << 47; - -typedef SizeClassAllocator64< - kAllocatorSpace, kAllocatorSize, 16, DefaultSizeClassMap> Allocator64; - -typedef SizeClassAllocator64< - kAllocatorSpace, kAllocatorSize, 16, CompactSizeClassMap> Allocator64Compact; -#elif defined(__mips64) -static const u64 kAddressSpaceSize = 1ULL << 40; -#else -static const u64 kAddressSpaceSize = 1ULL << 32; -#endif - -static const uptr kRegionSizeLog = FIRST_32_SECOND_64(20, 24); -static const uptr kFlatByteMapSize = kAddressSpaceSize >> kRegionSizeLog; - -typedef SizeClassAllocator32< - 0, kAddressSpaceSize, - /*kMetadataSize*/16, - CompactSizeClassMap, - kRegionSizeLog, - FlatByteMap<kFlatByteMapSize> > - Allocator32Compact; - -template <class SizeClassMap> -void TestSizeClassMap() { - typedef SizeClassMap SCMap; - // SCMap::Print(); - SCMap::Validate(); -} - -TEST(SanitizerCommon, DefaultSizeClassMap) { - TestSizeClassMap<DefaultSizeClassMap>(); -} - -TEST(SanitizerCommon, CompactSizeClassMap) { - TestSizeClassMap<CompactSizeClassMap>(); -} - -TEST(SanitizerCommon, InternalSizeClassMap) { - TestSizeClassMap<InternalSizeClassMap>(); -} - -template <class Allocator> -void TestSizeClassAllocator() { - Allocator *a = new Allocator; - a->Init(); - SizeClassAllocatorLocalCache<Allocator> cache; - memset(&cache, 0, sizeof(cache)); - cache.Init(0); - - static const uptr sizes[] = {1, 16, 30, 40, 100, 1000, 10000, - 50000, 60000, 100000, 120000, 300000, 500000, 1000000, 2000000}; - - std::vector<void *> allocated; - - uptr last_total_allocated = 0; - for (int i = 0; i < 3; i++) { - // Allocate a bunch of chunks. - for (uptr s = 0; s < ARRAY_SIZE(sizes); s++) { - uptr size = sizes[s]; - if (!a->CanAllocate(size, 1)) continue; - // printf("s = %ld\n", size); - uptr n_iter = std::max((uptr)6, 8000000 / size); - // fprintf(stderr, "size: %ld iter: %ld\n", size, n_iter); - for (uptr i = 0; i < n_iter; i++) { - uptr class_id0 = Allocator::SizeClassMapT::ClassID(size); - char *x = (char*)cache.Allocate(a, class_id0); - x[0] = 0; - x[size - 1] = 0; - x[size / 2] = 0; - allocated.push_back(x); - CHECK_EQ(x, a->GetBlockBegin(x)); - CHECK_EQ(x, a->GetBlockBegin(x + size - 1)); - CHECK(a->PointerIsMine(x)); - CHECK(a->PointerIsMine(x + size - 1)); - CHECK(a->PointerIsMine(x + size / 2)); - CHECK_GE(a->GetActuallyAllocatedSize(x), size); - uptr class_id = a->GetSizeClass(x); - CHECK_EQ(class_id, Allocator::SizeClassMapT::ClassID(size)); - uptr *metadata = reinterpret_cast<uptr*>(a->GetMetaData(x)); - metadata[0] = reinterpret_cast<uptr>(x) + 1; - metadata[1] = 0xABCD; - } - } - // Deallocate all. - for (uptr i = 0; i < allocated.size(); i++) { - void *x = allocated[i]; - uptr *metadata = reinterpret_cast<uptr*>(a->GetMetaData(x)); - CHECK_EQ(metadata[0], reinterpret_cast<uptr>(x) + 1); - CHECK_EQ(metadata[1], 0xABCD); - cache.Deallocate(a, a->GetSizeClass(x), x); - } - allocated.clear(); - uptr total_allocated = a->TotalMemoryUsed(); - if (last_total_allocated == 0) - last_total_allocated = total_allocated; - CHECK_EQ(last_total_allocated, total_allocated); - } - - // Check that GetBlockBegin never crashes. - for (uptr x = 0, step = kAddressSpaceSize / 100000; - x < kAddressSpaceSize - step; x += step) - if (a->PointerIsMine(reinterpret_cast<void *>(x))) - Ident(a->GetBlockBegin(reinterpret_cast<void *>(x))); - - a->TestOnlyUnmap(); - delete a; -} - -#if SANITIZER_CAN_USE_ALLOCATOR64 -TEST(SanitizerCommon, SizeClassAllocator64) { - TestSizeClassAllocator<Allocator64>(); -} - -TEST(SanitizerCommon, SizeClassAllocator64Compact) { - TestSizeClassAllocator<Allocator64Compact>(); -} -#endif - -TEST(SanitizerCommon, SizeClassAllocator32Compact) { - TestSizeClassAllocator<Allocator32Compact>(); -} - -template <class Allocator> -void SizeClassAllocatorMetadataStress() { - Allocator *a = new Allocator; - a->Init(); - SizeClassAllocatorLocalCache<Allocator> cache; - memset(&cache, 0, sizeof(cache)); - cache.Init(0); - - const uptr kNumAllocs = 1 << 13; - void *allocated[kNumAllocs]; - void *meta[kNumAllocs]; - for (uptr i = 0; i < kNumAllocs; i++) { - void *x = cache.Allocate(a, 1 + i % 50); - allocated[i] = x; - meta[i] = a->GetMetaData(x); - } - // Get Metadata kNumAllocs^2 times. - for (uptr i = 0; i < kNumAllocs * kNumAllocs; i++) { - uptr idx = i % kNumAllocs; - void *m = a->GetMetaData(allocated[idx]); - EXPECT_EQ(m, meta[idx]); - } - for (uptr i = 0; i < kNumAllocs; i++) { - cache.Deallocate(a, 1 + i % 50, allocated[i]); - } - - a->TestOnlyUnmap(); - delete a; -} - -#if SANITIZER_CAN_USE_ALLOCATOR64 -TEST(SanitizerCommon, SizeClassAllocator64MetadataStress) { - SizeClassAllocatorMetadataStress<Allocator64>(); -} - -TEST(SanitizerCommon, SizeClassAllocator64CompactMetadataStress) { - SizeClassAllocatorMetadataStress<Allocator64Compact>(); -} -#endif // SANITIZER_CAN_USE_ALLOCATOR64 -TEST(SanitizerCommon, SizeClassAllocator32CompactMetadataStress) { - SizeClassAllocatorMetadataStress<Allocator32Compact>(); -} - -template <class Allocator> -void SizeClassAllocatorGetBlockBeginStress() { - Allocator *a = new Allocator; - a->Init(); - SizeClassAllocatorLocalCache<Allocator> cache; - memset(&cache, 0, sizeof(cache)); - cache.Init(0); - - uptr max_size_class = Allocator::kNumClasses - 1; - uptr size = Allocator::SizeClassMapT::Size(max_size_class); - u64 G8 = 1ULL << 33; - // Make sure we correctly compute GetBlockBegin() w/o overflow. - for (size_t i = 0; i <= G8 / size; i++) { - void *x = cache.Allocate(a, max_size_class); - void *beg = a->GetBlockBegin(x); - // if ((i & (i - 1)) == 0) - // fprintf(stderr, "[%zd] %p %p\n", i, x, beg); - EXPECT_EQ(x, beg); - } - - a->TestOnlyUnmap(); - delete a; -} - -#if SANITIZER_CAN_USE_ALLOCATOR64 -TEST(SanitizerCommon, SizeClassAllocator64GetBlockBegin) { - SizeClassAllocatorGetBlockBeginStress<Allocator64>(); -} -TEST(SanitizerCommon, SizeClassAllocator64CompactGetBlockBegin) { - SizeClassAllocatorGetBlockBeginStress<Allocator64Compact>(); -} -TEST(SanitizerCommon, SizeClassAllocator32CompactGetBlockBegin) { - SizeClassAllocatorGetBlockBeginStress<Allocator32Compact>(); -} -#endif // SANITIZER_CAN_USE_ALLOCATOR64 - -struct TestMapUnmapCallback { - static int map_count, unmap_count; - void OnMap(uptr p, uptr size) const { map_count++; } - void OnUnmap(uptr p, uptr size) const { unmap_count++; } -}; -int TestMapUnmapCallback::map_count; -int TestMapUnmapCallback::unmap_count; - -#if SANITIZER_CAN_USE_ALLOCATOR64 -TEST(SanitizerCommon, SizeClassAllocator64MapUnmapCallback) { - TestMapUnmapCallback::map_count = 0; - TestMapUnmapCallback::unmap_count = 0; - typedef SizeClassAllocator64< - kAllocatorSpace, kAllocatorSize, 16, DefaultSizeClassMap, - TestMapUnmapCallback> Allocator64WithCallBack; - Allocator64WithCallBack *a = new Allocator64WithCallBack; - a->Init(); - EXPECT_EQ(TestMapUnmapCallback::map_count, 1); // Allocator state. - SizeClassAllocatorLocalCache<Allocator64WithCallBack> cache; - memset(&cache, 0, sizeof(cache)); - cache.Init(0); - AllocatorStats stats; - stats.Init(); - a->AllocateBatch(&stats, &cache, 32); - EXPECT_EQ(TestMapUnmapCallback::map_count, 3); // State + alloc + metadata. - a->TestOnlyUnmap(); - EXPECT_EQ(TestMapUnmapCallback::unmap_count, 1); // The whole thing. - delete a; -} -#endif - -TEST(SanitizerCommon, SizeClassAllocator32MapUnmapCallback) { - TestMapUnmapCallback::map_count = 0; - TestMapUnmapCallback::unmap_count = 0; - typedef SizeClassAllocator32< - 0, kAddressSpaceSize, - /*kMetadataSize*/16, - CompactSizeClassMap, - kRegionSizeLog, - FlatByteMap<kFlatByteMapSize>, - TestMapUnmapCallback> - Allocator32WithCallBack; - Allocator32WithCallBack *a = new Allocator32WithCallBack; - a->Init(); - EXPECT_EQ(TestMapUnmapCallback::map_count, 0); - SizeClassAllocatorLocalCache<Allocator32WithCallBack> cache; - memset(&cache, 0, sizeof(cache)); - cache.Init(0); - AllocatorStats stats; - stats.Init(); - a->AllocateBatch(&stats, &cache, 32); - EXPECT_EQ(TestMapUnmapCallback::map_count, 1); - a->TestOnlyUnmap(); - EXPECT_EQ(TestMapUnmapCallback::unmap_count, 1); - delete a; - // fprintf(stderr, "Map: %d Unmap: %d\n", - // TestMapUnmapCallback::map_count, - // TestMapUnmapCallback::unmap_count); -} - -TEST(SanitizerCommon, LargeMmapAllocatorMapUnmapCallback) { - TestMapUnmapCallback::map_count = 0; - TestMapUnmapCallback::unmap_count = 0; - LargeMmapAllocator<TestMapUnmapCallback> a; - a.Init(/* may_return_null */ false); - AllocatorStats stats; - stats.Init(); - void *x = a.Allocate(&stats, 1 << 20, 1); - EXPECT_EQ(TestMapUnmapCallback::map_count, 1); - a.Deallocate(&stats, x); - EXPECT_EQ(TestMapUnmapCallback::unmap_count, 1); -} - -template<class Allocator> -void FailInAssertionOnOOM() { - Allocator a; - a.Init(); - SizeClassAllocatorLocalCache<Allocator> cache; - memset(&cache, 0, sizeof(cache)); - cache.Init(0); - AllocatorStats stats; - stats.Init(); - for (int i = 0; i < 1000000; i++) { - a.AllocateBatch(&stats, &cache, 52); - } - - a.TestOnlyUnmap(); -} - -#if SANITIZER_CAN_USE_ALLOCATOR64 -TEST(SanitizerCommon, SizeClassAllocator64Overflow) { - EXPECT_DEATH(FailInAssertionOnOOM<Allocator64>(), "Out of memory"); -} -#endif - -#if !defined(_WIN32) // FIXME: This currently fails on Windows. -TEST(SanitizerCommon, LargeMmapAllocator) { - LargeMmapAllocator<> a; - a.Init(/* may_return_null */ false); - AllocatorStats stats; - stats.Init(); - - static const int kNumAllocs = 1000; - char *allocated[kNumAllocs]; - static const uptr size = 4000; - // Allocate some. - for (int i = 0; i < kNumAllocs; i++) { - allocated[i] = (char *)a.Allocate(&stats, size, 1); - CHECK(a.PointerIsMine(allocated[i])); - } - // Deallocate all. - CHECK_GT(a.TotalMemoryUsed(), size * kNumAllocs); - for (int i = 0; i < kNumAllocs; i++) { - char *p = allocated[i]; - CHECK(a.PointerIsMine(p)); - a.Deallocate(&stats, p); - } - // Check that non left. - CHECK_EQ(a.TotalMemoryUsed(), 0); - - // Allocate some more, also add metadata. - for (int i = 0; i < kNumAllocs; i++) { - char *x = (char *)a.Allocate(&stats, size, 1); - CHECK_GE(a.GetActuallyAllocatedSize(x), size); - uptr *meta = reinterpret_cast<uptr*>(a.GetMetaData(x)); - *meta = i; - allocated[i] = x; - } - for (int i = 0; i < kNumAllocs * kNumAllocs; i++) { - char *p = allocated[i % kNumAllocs]; - CHECK(a.PointerIsMine(p)); - CHECK(a.PointerIsMine(p + 2000)); - } - CHECK_GT(a.TotalMemoryUsed(), size * kNumAllocs); - // Deallocate all in reverse order. - for (int i = 0; i < kNumAllocs; i++) { - int idx = kNumAllocs - i - 1; - char *p = allocated[idx]; - uptr *meta = reinterpret_cast<uptr*>(a.GetMetaData(p)); - CHECK_EQ(*meta, idx); - CHECK(a.PointerIsMine(p)); - a.Deallocate(&stats, p); - } - CHECK_EQ(a.TotalMemoryUsed(), 0); - - // Test alignments. - uptr max_alignment = SANITIZER_WORDSIZE == 64 ? (1 << 28) : (1 << 24); - for (uptr alignment = 8; alignment <= max_alignment; alignment *= 2) { - const uptr kNumAlignedAllocs = 100; - for (uptr i = 0; i < kNumAlignedAllocs; i++) { - uptr size = ((i % 10) + 1) * 4096; - char *p = allocated[i] = (char *)a.Allocate(&stats, size, alignment); - CHECK_EQ(p, a.GetBlockBegin(p)); - CHECK_EQ(p, a.GetBlockBegin(p + size - 1)); - CHECK_EQ(p, a.GetBlockBegin(p + size / 2)); - CHECK_EQ(0, (uptr)allocated[i] % alignment); - p[0] = p[size - 1] = 0; - } - for (uptr i = 0; i < kNumAlignedAllocs; i++) { - a.Deallocate(&stats, allocated[i]); - } - } - - // Regression test for boundary condition in GetBlockBegin(). - uptr page_size = GetPageSizeCached(); - char *p = (char *)a.Allocate(&stats, page_size, 1); - CHECK_EQ(p, a.GetBlockBegin(p)); - CHECK_EQ(p, (char *)a.GetBlockBegin(p + page_size - 1)); - CHECK_NE(p, (char *)a.GetBlockBegin(p + page_size)); - a.Deallocate(&stats, p); -} -#endif - -template -<class PrimaryAllocator, class SecondaryAllocator, class AllocatorCache> -void TestCombinedAllocator() { - typedef - CombinedAllocator<PrimaryAllocator, AllocatorCache, SecondaryAllocator> - Allocator; - Allocator *a = new Allocator; - a->Init(/* may_return_null */ true); - - AllocatorCache cache; - memset(&cache, 0, sizeof(cache)); - a->InitCache(&cache); - - EXPECT_EQ(a->Allocate(&cache, -1, 1), (void*)0); - EXPECT_EQ(a->Allocate(&cache, -1, 1024), (void*)0); - EXPECT_EQ(a->Allocate(&cache, (uptr)-1 - 1024, 1), (void*)0); - EXPECT_EQ(a->Allocate(&cache, (uptr)-1 - 1024, 1024), (void*)0); - EXPECT_EQ(a->Allocate(&cache, (uptr)-1 - 1023, 1024), (void*)0); - - // Set to false - a->SetMayReturnNull(false); - EXPECT_DEATH(a->Allocate(&cache, -1, 1), - "allocator is terminating the process"); - - const uptr kNumAllocs = 100000; - const uptr kNumIter = 10; - for (uptr iter = 0; iter < kNumIter; iter++) { - std::vector<void*> allocated; - for (uptr i = 0; i < kNumAllocs; i++) { - uptr size = (i % (1 << 14)) + 1; - if ((i % 1024) == 0) - size = 1 << (10 + (i % 14)); - void *x = a->Allocate(&cache, size, 1); - uptr *meta = reinterpret_cast<uptr*>(a->GetMetaData(x)); - CHECK_EQ(*meta, 0); - *meta = size; - allocated.push_back(x); - } - - random_shuffle(allocated.begin(), allocated.end()); - - for (uptr i = 0; i < kNumAllocs; i++) { - void *x = allocated[i]; - uptr *meta = reinterpret_cast<uptr*>(a->GetMetaData(x)); - CHECK_NE(*meta, 0); - CHECK(a->PointerIsMine(x)); - *meta = 0; - a->Deallocate(&cache, x); - } - allocated.clear(); - a->SwallowCache(&cache); - } - a->DestroyCache(&cache); - a->TestOnlyUnmap(); -} - -#if SANITIZER_CAN_USE_ALLOCATOR64 -TEST(SanitizerCommon, CombinedAllocator64) { - TestCombinedAllocator<Allocator64, - LargeMmapAllocator<>, - SizeClassAllocatorLocalCache<Allocator64> > (); -} - -TEST(SanitizerCommon, CombinedAllocator64Compact) { - TestCombinedAllocator<Allocator64Compact, - LargeMmapAllocator<>, - SizeClassAllocatorLocalCache<Allocator64Compact> > (); -} -#endif - -#if !defined(_WIN32) // FIXME: This currently fails on Windows. -TEST(SanitizerCommon, CombinedAllocator32Compact) { - TestCombinedAllocator<Allocator32Compact, - LargeMmapAllocator<>, - SizeClassAllocatorLocalCache<Allocator32Compact> > (); -} -#endif - -template <class AllocatorCache> -void TestSizeClassAllocatorLocalCache() { - AllocatorCache cache; - typedef typename AllocatorCache::Allocator Allocator; - Allocator *a = new Allocator(); - - a->Init(); - memset(&cache, 0, sizeof(cache)); - cache.Init(0); - - const uptr kNumAllocs = 10000; - const int kNumIter = 100; - uptr saved_total = 0; - for (int class_id = 1; class_id <= 5; class_id++) { - for (int it = 0; it < kNumIter; it++) { - void *allocated[kNumAllocs]; - for (uptr i = 0; i < kNumAllocs; i++) { - allocated[i] = cache.Allocate(a, class_id); - } - for (uptr i = 0; i < kNumAllocs; i++) { - cache.Deallocate(a, class_id, allocated[i]); - } - cache.Drain(a); - uptr total_allocated = a->TotalMemoryUsed(); - if (it) - CHECK_EQ(saved_total, total_allocated); - saved_total = total_allocated; - } - } - - a->TestOnlyUnmap(); - delete a; -} - -#if SANITIZER_CAN_USE_ALLOCATOR64 -TEST(SanitizerCommon, SizeClassAllocator64LocalCache) { - TestSizeClassAllocatorLocalCache< - SizeClassAllocatorLocalCache<Allocator64> >(); -} - -TEST(SanitizerCommon, SizeClassAllocator64CompactLocalCache) { - TestSizeClassAllocatorLocalCache< - SizeClassAllocatorLocalCache<Allocator64Compact> >(); -} -#endif - -TEST(SanitizerCommon, SizeClassAllocator32CompactLocalCache) { - TestSizeClassAllocatorLocalCache< - SizeClassAllocatorLocalCache<Allocator32Compact> >(); -} - -#if SANITIZER_CAN_USE_ALLOCATOR64 -typedef SizeClassAllocatorLocalCache<Allocator64> AllocatorCache; -static AllocatorCache static_allocator_cache; - -void *AllocatorLeakTestWorker(void *arg) { - typedef AllocatorCache::Allocator Allocator; - Allocator *a = (Allocator*)(arg); - static_allocator_cache.Allocate(a, 10); - static_allocator_cache.Drain(a); - return 0; -} - -TEST(SanitizerCommon, AllocatorLeakTest) { - typedef AllocatorCache::Allocator Allocator; - Allocator a; - a.Init(); - uptr total_used_memory = 0; - for (int i = 0; i < 100; i++) { - pthread_t t; - PTHREAD_CREATE(&t, 0, AllocatorLeakTestWorker, &a); - PTHREAD_JOIN(t, 0); - if (i == 0) - total_used_memory = a.TotalMemoryUsed(); - EXPECT_EQ(a.TotalMemoryUsed(), total_used_memory); - } - - a.TestOnlyUnmap(); -} - -// Struct which is allocated to pass info to new threads. The new thread frees -// it. -struct NewThreadParams { - AllocatorCache *thread_cache; - AllocatorCache::Allocator *allocator; - uptr class_id; -}; - -// Called in a new thread. Just frees its argument. -static void *DeallocNewThreadWorker(void *arg) { - NewThreadParams *params = reinterpret_cast<NewThreadParams*>(arg); - params->thread_cache->Deallocate(params->allocator, params->class_id, params); - return NULL; -} - -// The allocator cache is supposed to be POD and zero initialized. We should be -// able to call Deallocate on a zeroed cache, and it will self-initialize. -TEST(Allocator, AllocatorCacheDeallocNewThread) { - AllocatorCache::Allocator allocator; - allocator.Init(); - AllocatorCache main_cache; - AllocatorCache child_cache; - memset(&main_cache, 0, sizeof(main_cache)); - memset(&child_cache, 0, sizeof(child_cache)); - - uptr class_id = DefaultSizeClassMap::ClassID(sizeof(NewThreadParams)); - NewThreadParams *params = reinterpret_cast<NewThreadParams*>( - main_cache.Allocate(&allocator, class_id)); - params->thread_cache = &child_cache; - params->allocator = &allocator; - params->class_id = class_id; - pthread_t t; - PTHREAD_CREATE(&t, 0, DeallocNewThreadWorker, params); - PTHREAD_JOIN(t, 0); -} -#endif - -TEST(Allocator, Basic) { - char *p = (char*)InternalAlloc(10); - EXPECT_NE(p, (char*)0); - char *p2 = (char*)InternalAlloc(20); - EXPECT_NE(p2, (char*)0); - EXPECT_NE(p2, p); - InternalFree(p); - InternalFree(p2); -} - -TEST(Allocator, Stress) { - const int kCount = 1000; - char *ptrs[kCount]; - unsigned rnd = 42; - for (int i = 0; i < kCount; i++) { - uptr sz = my_rand_r(&rnd) % 1000; - char *p = (char*)InternalAlloc(sz); - EXPECT_NE(p, (char*)0); - ptrs[i] = p; - } - for (int i = 0; i < kCount; i++) { - InternalFree(ptrs[i]); - } -} - -TEST(Allocator, LargeAlloc) { - void *p = InternalAlloc(10 << 20); - InternalFree(p); -} - -TEST(Allocator, ScopedBuffer) { - const int kSize = 512; - { - InternalScopedBuffer<int> int_buf(kSize); - EXPECT_EQ(sizeof(int) * kSize, int_buf.size()); // NOLINT - } - InternalScopedBuffer<char> char_buf(kSize); - EXPECT_EQ(sizeof(char) * kSize, char_buf.size()); // NOLINT - internal_memset(char_buf.data(), 'c', kSize); - for (int i = 0; i < kSize; i++) { - EXPECT_EQ('c', char_buf[i]); - } -} - -void IterationTestCallback(uptr chunk, void *arg) { - reinterpret_cast<std::set<uptr> *>(arg)->insert(chunk); -} - -template <class Allocator> -void TestSizeClassAllocatorIteration() { - Allocator *a = new Allocator; - a->Init(); - SizeClassAllocatorLocalCache<Allocator> cache; - memset(&cache, 0, sizeof(cache)); - cache.Init(0); - - static const uptr sizes[] = {1, 16, 30, 40, 100, 1000, 10000, - 50000, 60000, 100000, 120000, 300000, 500000, 1000000, 2000000}; - - std::vector<void *> allocated; - - // Allocate a bunch of chunks. - for (uptr s = 0; s < ARRAY_SIZE(sizes); s++) { - uptr size = sizes[s]; - if (!a->CanAllocate(size, 1)) continue; - // printf("s = %ld\n", size); - uptr n_iter = std::max((uptr)6, 80000 / size); - // fprintf(stderr, "size: %ld iter: %ld\n", size, n_iter); - for (uptr j = 0; j < n_iter; j++) { - uptr class_id0 = Allocator::SizeClassMapT::ClassID(size); - void *x = cache.Allocate(a, class_id0); - allocated.push_back(x); - } - } - - std::set<uptr> reported_chunks; - a->ForceLock(); - a->ForEachChunk(IterationTestCallback, &reported_chunks); - a->ForceUnlock(); - - for (uptr i = 0; i < allocated.size(); i++) { - // Don't use EXPECT_NE. Reporting the first mismatch is enough. - ASSERT_NE(reported_chunks.find(reinterpret_cast<uptr>(allocated[i])), - reported_chunks.end()); - } - - a->TestOnlyUnmap(); - delete a; -} - -#if SANITIZER_CAN_USE_ALLOCATOR64 -TEST(SanitizerCommon, SizeClassAllocator64Iteration) { - TestSizeClassAllocatorIteration<Allocator64>(); -} -#endif - -TEST(SanitizerCommon, SizeClassAllocator32Iteration) { - TestSizeClassAllocatorIteration<Allocator32Compact>(); -} - -TEST(SanitizerCommon, LargeMmapAllocatorIteration) { - LargeMmapAllocator<> a; - a.Init(/* may_return_null */ false); - AllocatorStats stats; - stats.Init(); - - static const uptr kNumAllocs = 1000; - char *allocated[kNumAllocs]; - static const uptr size = 40; - // Allocate some. - for (uptr i = 0; i < kNumAllocs; i++) - allocated[i] = (char *)a.Allocate(&stats, size, 1); - - std::set<uptr> reported_chunks; - a.ForceLock(); - a.ForEachChunk(IterationTestCallback, &reported_chunks); - a.ForceUnlock(); - - for (uptr i = 0; i < kNumAllocs; i++) { - // Don't use EXPECT_NE. Reporting the first mismatch is enough. - ASSERT_NE(reported_chunks.find(reinterpret_cast<uptr>(allocated[i])), - reported_chunks.end()); - } - for (uptr i = 0; i < kNumAllocs; i++) - a.Deallocate(&stats, allocated[i]); -} - -TEST(SanitizerCommon, LargeMmapAllocatorBlockBegin) { - LargeMmapAllocator<> a; - a.Init(/* may_return_null */ false); - AllocatorStats stats; - stats.Init(); - - static const uptr kNumAllocs = 1024; - static const uptr kNumExpectedFalseLookups = 10000000; - char *allocated[kNumAllocs]; - static const uptr size = 4096; - // Allocate some. - for (uptr i = 0; i < kNumAllocs; i++) { - allocated[i] = (char *)a.Allocate(&stats, size, 1); - } - - a.ForceLock(); - for (uptr i = 0; i < kNumAllocs * kNumAllocs; i++) { - // if ((i & (i - 1)) == 0) fprintf(stderr, "[%zd]\n", i); - char *p1 = allocated[i % kNumAllocs]; - EXPECT_EQ(p1, a.GetBlockBeginFastLocked(p1)); - EXPECT_EQ(p1, a.GetBlockBeginFastLocked(p1 + size / 2)); - EXPECT_EQ(p1, a.GetBlockBeginFastLocked(p1 + size - 1)); - EXPECT_EQ(p1, a.GetBlockBeginFastLocked(p1 - 100)); - } - - for (uptr i = 0; i < kNumExpectedFalseLookups; i++) { - void *p = reinterpret_cast<void *>(i % 1024); - EXPECT_EQ((void *)0, a.GetBlockBeginFastLocked(p)); - p = reinterpret_cast<void *>(~0L - (i % 1024)); - EXPECT_EQ((void *)0, a.GetBlockBeginFastLocked(p)); - } - a.ForceUnlock(); - - for (uptr i = 0; i < kNumAllocs; i++) - a.Deallocate(&stats, allocated[i]); -} - - -#if SANITIZER_CAN_USE_ALLOCATOR64 -// Regression test for out-of-memory condition in PopulateFreeList(). -TEST(SanitizerCommon, SizeClassAllocator64PopulateFreeListOOM) { - // In a world where regions are small and chunks are huge... - typedef SizeClassMap<63, 128, 16> SpecialSizeClassMap; - typedef SizeClassAllocator64<kAllocatorSpace, kAllocatorSize, 0, - SpecialSizeClassMap> SpecialAllocator64; - const uptr kRegionSize = - kAllocatorSize / SpecialSizeClassMap::kNumClassesRounded; - SpecialAllocator64 *a = new SpecialAllocator64; - a->Init(); - SizeClassAllocatorLocalCache<SpecialAllocator64> cache; - memset(&cache, 0, sizeof(cache)); - cache.Init(0); - - // ...one man is on a mission to overflow a region with a series of - // successive allocations. - const uptr kClassID = 107; - const uptr kAllocationSize = DefaultSizeClassMap::Size(kClassID); - ASSERT_LT(2 * kAllocationSize, kRegionSize); - ASSERT_GT(3 * kAllocationSize, kRegionSize); - cache.Allocate(a, kClassID); - EXPECT_DEATH(cache.Allocate(a, kClassID) && cache.Allocate(a, kClassID), - "The process has exhausted"); - a->TestOnlyUnmap(); - delete a; -} -#endif - -TEST(SanitizerCommon, TwoLevelByteMap) { - const u64 kSize1 = 1 << 6, kSize2 = 1 << 12; - const u64 n = kSize1 * kSize2; - TwoLevelByteMap<kSize1, kSize2> m; - m.TestOnlyInit(); - for (u64 i = 0; i < n; i += 7) { - m.set(i, (i % 100) + 1); - } - for (u64 j = 0; j < n; j++) { - if (j % 7) - EXPECT_EQ(m[j], 0); - else - EXPECT_EQ(m[j], (j % 100) + 1); - } - - m.TestOnlyUnmap(); -} - - -typedef TwoLevelByteMap<1 << 12, 1 << 13, TestMapUnmapCallback> TestByteMap; - -struct TestByteMapParam { - TestByteMap *m; - size_t shard; - size_t num_shards; -}; - -void *TwoLevelByteMapUserThread(void *param) { - TestByteMapParam *p = (TestByteMapParam*)param; - for (size_t i = p->shard; i < p->m->size(); i += p->num_shards) { - size_t val = (i % 100) + 1; - p->m->set(i, val); - EXPECT_EQ((*p->m)[i], val); - } - return 0; -} - -TEST(SanitizerCommon, ThreadedTwoLevelByteMap) { - TestByteMap m; - m.TestOnlyInit(); - TestMapUnmapCallback::map_count = 0; - TestMapUnmapCallback::unmap_count = 0; - static const int kNumThreads = 4; - pthread_t t[kNumThreads]; - TestByteMapParam p[kNumThreads]; - for (int i = 0; i < kNumThreads; i++) { - p[i].m = &m; - p[i].shard = i; - p[i].num_shards = kNumThreads; - PTHREAD_CREATE(&t[i], 0, TwoLevelByteMapUserThread, &p[i]); - } - for (int i = 0; i < kNumThreads; i++) { - PTHREAD_JOIN(t[i], 0); - } - EXPECT_EQ((uptr)TestMapUnmapCallback::map_count, m.size1()); - EXPECT_EQ((uptr)TestMapUnmapCallback::unmap_count, 0UL); - m.TestOnlyUnmap(); - EXPECT_EQ((uptr)TestMapUnmapCallback::map_count, m.size1()); - EXPECT_EQ((uptr)TestMapUnmapCallback::unmap_count, m.size1()); -} - -#endif // #if !SANITIZER_DEBUG diff --git a/contrib/compiler-rt/lib/sanitizer_common/tests/sanitizer_allocator_testlib.cc b/contrib/compiler-rt/lib/sanitizer_common/tests/sanitizer_allocator_testlib.cc deleted file mode 100644 index 0cc3b9ba6944..000000000000 --- a/contrib/compiler-rt/lib/sanitizer_common/tests/sanitizer_allocator_testlib.cc +++ /dev/null @@ -1,162 +0,0 @@ -//===-- sanitizer_allocator_testlib.cc ------------------------------------===// -// -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. -// -//===----------------------------------------------------------------------===// -// Malloc replacement library based on CombinedAllocator. -// The primary purpose of this file is an end-to-end integration test -// for CombinedAllocator. -//===----------------------------------------------------------------------===// -/* Usage: -clang++ -fno-exceptions -g -fPIC -I. -I../include -Isanitizer \ - sanitizer_common/tests/sanitizer_allocator_testlib.cc \ - sanitizer_common/sanitizer_*.cc -shared -lpthread -o testmalloc.so -LD_PRELOAD=`pwd`/testmalloc.so /your/app -*/ -#include "sanitizer_common/sanitizer_allocator.h" -#include "sanitizer_common/sanitizer_common.h" -#include <stddef.h> -#include <stdio.h> -#include <unistd.h> -#include <string.h> -#include <pthread.h> - -#ifndef SANITIZER_MALLOC_HOOK -# define SANITIZER_MALLOC_HOOK(p, s) -#endif - -#ifndef SANITIZER_FREE_HOOK -# define SANITIZER_FREE_HOOK(p) -#endif - -namespace { -static const uptr kAllocatorSpace = 0x600000000000ULL; -static const uptr kAllocatorSize = 0x10000000000ULL; // 1T. - -typedef SizeClassAllocator64<kAllocatorSpace, kAllocatorSize, 0, - CompactSizeClassMap> PrimaryAllocator; -typedef SizeClassAllocatorLocalCache<PrimaryAllocator> AllocatorCache; -typedef LargeMmapAllocator<> SecondaryAllocator; -typedef CombinedAllocator<PrimaryAllocator, AllocatorCache, - SecondaryAllocator> Allocator; - -static Allocator allocator; -static bool global_inited; -static THREADLOCAL AllocatorCache cache; -static THREADLOCAL bool thread_inited; -static pthread_key_t pkey; - -static void thread_dtor(void *v) { - if ((uptr)v != 3) { - pthread_setspecific(pkey, (void*)((uptr)v + 1)); - return; - } - allocator.SwallowCache(&cache); -} - -static void NOINLINE thread_init() { - if (!global_inited) { - global_inited = true; - allocator.Init(); - pthread_key_create(&pkey, thread_dtor); - } - thread_inited = true; - pthread_setspecific(pkey, (void*)1); - cache.Init(); -} -} // namespace - -extern "C" { -void *malloc(size_t size) { - if (UNLIKELY(!thread_inited)) - thread_init(); - void *p = allocator.Allocate(&cache, size, 8); - SANITIZER_MALLOC_HOOK(p, size); - return p; -} - -void free(void *p) { - if (UNLIKELY(!thread_inited)) - thread_init(); - SANITIZER_FREE_HOOK(p); - allocator.Deallocate(&cache, p); -} - -void *calloc(size_t nmemb, size_t size) { - if (UNLIKELY(!thread_inited)) - thread_init(); - size *= nmemb; - void *p = allocator.Allocate(&cache, size, 8, false); - memset(p, 0, size); - SANITIZER_MALLOC_HOOK(p, size); - return p; -} - -void *realloc(void *p, size_t size) { - if (UNLIKELY(!thread_inited)) - thread_init(); - if (p) { - SANITIZER_FREE_HOOK(p); - } - p = allocator.Reallocate(&cache, p, size, 8); - if (p) { - SANITIZER_MALLOC_HOOK(p, size); - } - return p; -} - -void *memalign(size_t alignment, size_t size) { - if (UNLIKELY(!thread_inited)) - thread_init(); - void *p = allocator.Allocate(&cache, size, alignment); - SANITIZER_MALLOC_HOOK(p, size); - return p; -} - -int posix_memalign(void **memptr, size_t alignment, size_t size) { - if (UNLIKELY(!thread_inited)) - thread_init(); - *memptr = allocator.Allocate(&cache, size, alignment); - SANITIZER_MALLOC_HOOK(*memptr, size); - return 0; -} - -void *valloc(size_t size) { - if (UNLIKELY(!thread_inited)) - thread_init(); - if (size == 0) - size = GetPageSizeCached(); - void *p = allocator.Allocate(&cache, size, GetPageSizeCached()); - SANITIZER_MALLOC_HOOK(p, size); - return p; -} - -void cfree(void *p) ALIAS("free"); -void *pvalloc(size_t size) ALIAS("valloc"); -void *__libc_memalign(size_t alignment, size_t size) ALIAS("memalign"); - -void malloc_usable_size() { -} - -void mallinfo() { -} - -void mallopt() { -} -} // extern "C" - -namespace std { - struct nothrow_t; -} - -void *operator new(size_t size) ALIAS("malloc"); -void *operator new[](size_t size) ALIAS("malloc"); -void *operator new(size_t size, std::nothrow_t const&) ALIAS("malloc"); -void *operator new[](size_t size, std::nothrow_t const&) ALIAS("malloc"); -void operator delete(void *ptr) throw() ALIAS("free"); -void operator delete[](void *ptr) throw() ALIAS("free"); -void operator delete(void *ptr, std::nothrow_t const&) ALIAS("free"); -void operator delete[](void *ptr, std::nothrow_t const&) ALIAS("free"); diff --git a/contrib/compiler-rt/lib/sanitizer_common/tests/sanitizer_atomic_test.cc b/contrib/compiler-rt/lib/sanitizer_common/tests/sanitizer_atomic_test.cc deleted file mode 100644 index 56bcd35c826c..000000000000 --- a/contrib/compiler-rt/lib/sanitizer_common/tests/sanitizer_atomic_test.cc +++ /dev/null @@ -1,128 +0,0 @@ -//===-- sanitizer_atomic_test.cc ------------------------------------------===// -// -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. -// -//===----------------------------------------------------------------------===// -// -// This file is a part of ThreadSanitizer/AddressSanitizer runtime. -// -//===----------------------------------------------------------------------===// -#include "sanitizer_common/sanitizer_atomic.h" -#include "gtest/gtest.h" - -namespace __sanitizer { - -template<typename T> -struct ValAndMagic { - typename T::Type magic0; - T a; - typename T::Type magic1; - - static ValAndMagic<T> *sink; -}; - -template<typename T> -ValAndMagic<T> *ValAndMagic<T>::sink; - -template<typename T, memory_order load_mo, memory_order store_mo> -void CheckStoreLoad() { - typedef typename T::Type Type; - ValAndMagic<T> val; - // Prevent the compiler from scalarizing the struct. - ValAndMagic<T>::sink = &val; - // Ensure that surrounding memory is not overwritten. - val.magic0 = val.magic1 = (Type)-3; - for (u64 i = 0; i < 100; i++) { - // Generate a value that occupies all bytes of the variable. - u64 v = i; - v |= v << 8; - v |= v << 16; - v |= v << 32; - val.a.val_dont_use = (Type)v; - EXPECT_EQ(atomic_load(&val.a, load_mo), (Type)v); - val.a.val_dont_use = (Type)-1; - atomic_store(&val.a, (Type)v, store_mo); - EXPECT_EQ(val.a.val_dont_use, (Type)v); - } - EXPECT_EQ(val.magic0, (Type)-3); - EXPECT_EQ(val.magic1, (Type)-3); -} - -TEST(SanitizerCommon, AtomicStoreLoad) { - CheckStoreLoad<atomic_uint8_t, memory_order_relaxed, memory_order_relaxed>(); - CheckStoreLoad<atomic_uint8_t, memory_order_consume, memory_order_relaxed>(); - CheckStoreLoad<atomic_uint8_t, memory_order_acquire, memory_order_relaxed>(); - CheckStoreLoad<atomic_uint8_t, memory_order_relaxed, memory_order_release>(); - CheckStoreLoad<atomic_uint8_t, memory_order_seq_cst, memory_order_seq_cst>(); - - CheckStoreLoad<atomic_uint16_t, memory_order_relaxed, memory_order_relaxed>(); - CheckStoreLoad<atomic_uint16_t, memory_order_consume, memory_order_relaxed>(); - CheckStoreLoad<atomic_uint16_t, memory_order_acquire, memory_order_relaxed>(); - CheckStoreLoad<atomic_uint16_t, memory_order_relaxed, memory_order_release>(); - CheckStoreLoad<atomic_uint16_t, memory_order_seq_cst, memory_order_seq_cst>(); - - CheckStoreLoad<atomic_uint32_t, memory_order_relaxed, memory_order_relaxed>(); - CheckStoreLoad<atomic_uint32_t, memory_order_consume, memory_order_relaxed>(); - CheckStoreLoad<atomic_uint32_t, memory_order_acquire, memory_order_relaxed>(); - CheckStoreLoad<atomic_uint32_t, memory_order_relaxed, memory_order_release>(); - CheckStoreLoad<atomic_uint32_t, memory_order_seq_cst, memory_order_seq_cst>(); - - CheckStoreLoad<atomic_uint64_t, memory_order_relaxed, memory_order_relaxed>(); - CheckStoreLoad<atomic_uint64_t, memory_order_consume, memory_order_relaxed>(); - CheckStoreLoad<atomic_uint64_t, memory_order_acquire, memory_order_relaxed>(); - CheckStoreLoad<atomic_uint64_t, memory_order_relaxed, memory_order_release>(); - CheckStoreLoad<atomic_uint64_t, memory_order_seq_cst, memory_order_seq_cst>(); - - CheckStoreLoad<atomic_uintptr_t, memory_order_relaxed, memory_order_relaxed> - (); - CheckStoreLoad<atomic_uintptr_t, memory_order_consume, memory_order_relaxed> - (); - CheckStoreLoad<atomic_uintptr_t, memory_order_acquire, memory_order_relaxed> - (); - CheckStoreLoad<atomic_uintptr_t, memory_order_relaxed, memory_order_release> - (); - CheckStoreLoad<atomic_uintptr_t, memory_order_seq_cst, memory_order_seq_cst> - (); -} - -// Clang crashes while compiling this test for Android: -// http://llvm.org/bugs/show_bug.cgi?id=15587 -#if !SANITIZER_ANDROID -template<typename T> -void CheckAtomicCompareExchange() { - typedef typename T::Type Type; - { - Type old_val = 42; - Type new_val = 24; - Type var = old_val; - EXPECT_TRUE(atomic_compare_exchange_strong((T*)&var, &old_val, new_val, - memory_order_relaxed)); - EXPECT_FALSE(atomic_compare_exchange_strong((T*)&var, &old_val, new_val, - memory_order_relaxed)); - EXPECT_EQ(new_val, old_val); - } - { - Type old_val = 42; - Type new_val = 24; - Type var = old_val; - EXPECT_TRUE(atomic_compare_exchange_weak((T*)&var, &old_val, new_val, - memory_order_relaxed)); - EXPECT_FALSE(atomic_compare_exchange_weak((T*)&var, &old_val, new_val, - memory_order_relaxed)); - EXPECT_EQ(new_val, old_val); - } -} - -TEST(SanitizerCommon, AtomicCompareExchangeTest) { - CheckAtomicCompareExchange<atomic_uint8_t>(); - CheckAtomicCompareExchange<atomic_uint16_t>(); - CheckAtomicCompareExchange<atomic_uint32_t>(); - CheckAtomicCompareExchange<atomic_uint64_t>(); - CheckAtomicCompareExchange<atomic_uintptr_t>(); -} -#endif //!SANITIZER_ANDROID - -} // namespace __sanitizer diff --git a/contrib/compiler-rt/lib/sanitizer_common/tests/sanitizer_bitvector_test.cc b/contrib/compiler-rt/lib/sanitizer_common/tests/sanitizer_bitvector_test.cc deleted file mode 100644 index 706b4c58968e..000000000000 --- a/contrib/compiler-rt/lib/sanitizer_common/tests/sanitizer_bitvector_test.cc +++ /dev/null @@ -1,176 +0,0 @@ -//===-- sanitizer_bitvector_test.cc ---------------------------------------===// -// -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. -// -//===----------------------------------------------------------------------===// -// -// This file is a part of Sanitizer runtime. -// Tests for sanitizer_bitvector.h. -// -//===----------------------------------------------------------------------===// -#include "sanitizer_common/sanitizer_bitvector.h" - -#include "sanitizer_test_utils.h" - -#include "gtest/gtest.h" - -#include <algorithm> -#include <vector> -#include <set> - -using namespace __sanitizer; -using namespace std; - - -// Check the 'bv' == 's' and that the indexes go in increasing order. -// Also check the BV::Iterator -template <class BV> -static void CheckBV(const BV &bv, const set<uptr> &s) { - BV t; - t.copyFrom(bv); - set<uptr> t_s(s); - uptr last_idx = bv.size(); - uptr count = 0; - for (typename BV::Iterator it(bv); it.hasNext();) { - uptr idx = it.next(); - count++; - if (last_idx != bv.size()) - EXPECT_LT(last_idx, idx); - last_idx = idx; - EXPECT_TRUE(s.count(idx)); - } - EXPECT_EQ(count, s.size()); - - last_idx = bv.size(); - while (!t.empty()) { - uptr idx = t.getAndClearFirstOne(); - if (last_idx != bv.size()) - EXPECT_LT(last_idx, idx); - last_idx = idx; - EXPECT_TRUE(t_s.erase(idx)); - } - EXPECT_TRUE(t_s.empty()); -} - -template <class BV> -void Print(const BV &bv) { - BV t; - t.copyFrom(bv); - while (!t.empty()) { - uptr idx = t.getAndClearFirstOne(); - fprintf(stderr, "%zd ", idx); - } - fprintf(stderr, "\n"); -} - -void Print(const set<uptr> &s) { - for (set<uptr>::iterator it = s.begin(); it != s.end(); ++it) { - fprintf(stderr, "%zd ", *it); - } - fprintf(stderr, "\n"); -} - -template <class BV> -void TestBitVector(uptr expected_size) { - BV bv, bv1, t_bv; - EXPECT_EQ(expected_size, BV::kSize); - bv.clear(); - EXPECT_TRUE(bv.empty()); - bv.setBit(5); - EXPECT_FALSE(bv.empty()); - EXPECT_FALSE(bv.getBit(4)); - EXPECT_FALSE(bv.getBit(6)); - EXPECT_TRUE(bv.getBit(5)); - bv.clearBit(5); - EXPECT_FALSE(bv.getBit(5)); - - // test random bits - bv.clear(); - set<uptr> s; - for (uptr it = 0; it < 1000; it++) { - uptr bit = ((uptr)my_rand() % bv.size()); - EXPECT_EQ(bv.getBit(bit), s.count(bit) == 1); - switch (my_rand() % 2) { - case 0: - EXPECT_EQ(bv.setBit(bit), s.insert(bit).second); - break; - case 1: - size_t old_size = s.size(); - s.erase(bit); - EXPECT_EQ(bv.clearBit(bit), old_size > s.size()); - break; - } - EXPECT_EQ(bv.getBit(bit), s.count(bit) == 1); - } - - vector<uptr>bits(bv.size()); - // Test setUnion, setIntersection, setDifference, - // intersectsWith, and getAndClearFirstOne. - for (uptr it = 0; it < 30; it++) { - // iota - for (size_t j = 0; j < bits.size(); j++) bits[j] = j; - random_shuffle(bits.begin(), bits.end()); - set<uptr> s, s1, t_s; - bv.clear(); - bv1.clear(); - uptr n_bits = ((uptr)my_rand() % bv.size()) + 1; - uptr n_bits1 = (uptr)my_rand() % (bv.size() / 2); - EXPECT_TRUE(n_bits > 0 && n_bits <= bv.size()); - EXPECT_TRUE(n_bits1 < bv.size() / 2); - for (uptr i = 0; i < n_bits; i++) { - bv.setBit(bits[i]); - s.insert(bits[i]); - } - CheckBV(bv, s); - for (uptr i = 0; i < n_bits1; i++) { - bv1.setBit(bits[bv.size() / 2 + i]); - s1.insert(bits[bv.size() / 2 + i]); - } - CheckBV(bv1, s1); - - vector<uptr> vec; - set_intersection(s.begin(), s.end(), s1.begin(), s1.end(), - back_insert_iterator<vector<uptr> >(vec)); - EXPECT_EQ(bv.intersectsWith(bv1), !vec.empty()); - - // setUnion - t_s = s; - t_bv.copyFrom(bv); - t_s.insert(s1.begin(), s1.end()); - EXPECT_EQ(t_bv.setUnion(bv1), s.size() != t_s.size()); - CheckBV(t_bv, t_s); - - // setIntersection - t_s = set<uptr>(vec.begin(), vec.end()); - t_bv.copyFrom(bv); - EXPECT_EQ(t_bv.setIntersection(bv1), s.size() != t_s.size()); - CheckBV(t_bv, t_s); - - // setDifference - vec.clear(); - set_difference(s.begin(), s.end(), s1.begin(), s1.end(), - back_insert_iterator<vector<uptr> >(vec)); - t_s = set<uptr>(vec.begin(), vec.end()); - t_bv.copyFrom(bv); - EXPECT_EQ(t_bv.setDifference(bv1), s.size() != t_s.size()); - CheckBV(t_bv, t_s); - } -} - -TEST(SanitizerCommon, BasicBitVector) { - TestBitVector<BasicBitVector<u8> >(8); - TestBitVector<BasicBitVector<u16> >(16); - TestBitVector<BasicBitVector<> >(SANITIZER_WORDSIZE); -} - -TEST(SanitizerCommon, TwoLevelBitVector) { - uptr ws = SANITIZER_WORDSIZE; - TestBitVector<TwoLevelBitVector<1, BasicBitVector<u8> > >(8 * 8); - TestBitVector<TwoLevelBitVector<> >(ws * ws); - TestBitVector<TwoLevelBitVector<2> >(ws * ws * 2); - TestBitVector<TwoLevelBitVector<3> >(ws * ws * 3); - TestBitVector<TwoLevelBitVector<3, BasicBitVector<u16> > >(16 * 16 * 3); -} diff --git a/contrib/compiler-rt/lib/sanitizer_common/tests/sanitizer_bvgraph_test.cc b/contrib/compiler-rt/lib/sanitizer_common/tests/sanitizer_bvgraph_test.cc deleted file mode 100644 index 3b39f8dd734a..000000000000 --- a/contrib/compiler-rt/lib/sanitizer_common/tests/sanitizer_bvgraph_test.cc +++ /dev/null @@ -1,339 +0,0 @@ -//===-- sanitizer_bvgraph_test.cc -----------------------------------------===// -// -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. -// -//===----------------------------------------------------------------------===// -// -// This file is a part of Sanitizer runtime. -// Tests for sanitizer_bvgraph.h. -// -//===----------------------------------------------------------------------===// -#include "sanitizer_common/sanitizer_bvgraph.h" - -#include "sanitizer_test_utils.h" - -#include "gtest/gtest.h" - -#include <algorithm> -#include <vector> -#include <set> - -using namespace __sanitizer; -using namespace std; - -typedef BasicBitVector<u8> BV1; -typedef BasicBitVector<> BV2; -typedef TwoLevelBitVector<> BV3; -typedef TwoLevelBitVector<3, BasicBitVector<u8> > BV4; - -template<class G> -void PrintGraph(const G &g) { - for (uptr i = 0; i < g.size(); i++) { - for (uptr j = 0; j < g.size(); j++) { - fprintf(stderr, "%d", g.hasEdge(i, j)); - } - fprintf(stderr, "\n"); - } -} - - -class SimpleGraph { - public: - void clear() { s_.clear(); } - bool addEdge(uptr from, uptr to) { - return s_.insert(idx(from, to)).second; - } - bool removeEdge(uptr from, uptr to) { - return s_.erase(idx(from, to)); - } - template <class G> - void checkSameAs(G *g) { - for (set<uptr>::iterator it = s_.begin(); it != s_.end(); ++it) { - uptr from = *it >> 16; - uptr to = *it & ((1 << 16) - 1); - EXPECT_TRUE(g->removeEdge(from, to)); - } - EXPECT_TRUE(g->empty()); - } - private: - uptr idx(uptr from, uptr to) { - CHECK_LE(from|to, 1 << 16); - return (from << 16) + to; - } - set<uptr> s_; -}; - -template <class BV> -void BasicTest() { - BVGraph<BV> g; - g.clear(); - BV target; - SimpleGraph s_g; - set<uptr> s; - set<uptr> s_target; - int num_reachable = 0; - for (int it = 0; it < 1000; it++) { - target.clear(); - s_target.clear(); - for (int t = 0; t < 4; t++) { - uptr idx = (uptr)my_rand() % g.size(); - EXPECT_EQ(target.setBit(idx), s_target.insert(idx).second); - } - uptr from = my_rand() % g.size(); - uptr to = my_rand() % g.size(); - EXPECT_EQ(g.addEdge(from, to), s_g.addEdge(from, to)); - EXPECT_TRUE(g.hasEdge(from, to)); - for (int i = 0; i < 10; i++) { - from = my_rand() % g.size(); - bool is_reachable = g.isReachable(from, target); - if (is_reachable) { - uptr path[BV::kSize]; - uptr len; - for (len = 1; len < BV::kSize; len++) { - if (g.findPath(from, target, path, len) == len) - break; - } - EXPECT_LT(len, BV::kSize); - EXPECT_TRUE(target.getBit(path[len - 1])); - // fprintf(stderr, "reachable: %zd; path %zd {%zd %zd %zd}\n", - // from, len, path[0], path[1], path[2]); - num_reachable++; - } - } - } - EXPECT_GT(num_reachable, 0); -} - -TEST(BVGraph, BasicTest) { - BasicTest<BV1>(); - BasicTest<BV2>(); - BasicTest<BV3>(); - BasicTest<BV4>(); -} - -template <class BV> -void RemoveEdges() { - SimpleGraph s_g; - BVGraph<BV> g; - g.clear(); - BV bv; - set<uptr> s; - for (int it = 0; it < 100; it++) { - s.clear(); - bv.clear(); - s_g.clear(); - g.clear(); - for (uptr j = 0; j < g.size() * 2; j++) { - uptr from = my_rand() % g.size(); - uptr to = my_rand() % g.size(); - EXPECT_EQ(g.addEdge(from, to), s_g.addEdge(from, to)); - } - for (uptr j = 0; j < 5; j++) { - uptr idx = my_rand() % g.size(); - s.insert(idx); - bv.setBit(idx); - } - - if (it % 2) { - g.removeEdgesFrom(bv); - for (set<uptr>::iterator from = s.begin(); from != s.end(); ++from) { - for (uptr to = 0; to < g.size(); to++) - s_g.removeEdge(*from, to); - } - } else { - g.removeEdgesTo(bv); - for (set<uptr>::iterator to = s.begin(); to != s.end(); ++to) { - for (uptr from = 0; from < g.size(); from++) - s_g.removeEdge(from, *to); - } - } - s_g.checkSameAs(&g); - } -} - -TEST(BVGraph, RemoveEdges) { - RemoveEdges<BV1>(); - RemoveEdges<BV2>(); - RemoveEdges<BV3>(); - RemoveEdges<BV4>(); -} - -template <class BV> -void Test_isReachable() { - uptr path[5]; - BVGraph<BV> g; - g.clear(); - BV target; - target.clear(); - uptr t0 = 0; - uptr t1 = g.size() - 1; - target.setBit(t0); - target.setBit(t1); - - uptr f0 = 1; - uptr f1 = 2; - uptr f2 = g.size() / 2; - uptr f3 = g.size() - 2; - - EXPECT_FALSE(g.isReachable(f0, target)); - EXPECT_FALSE(g.isReachable(f1, target)); - EXPECT_FALSE(g.isReachable(f2, target)); - EXPECT_FALSE(g.isReachable(f3, target)); - - g.addEdge(f0, f1); - g.addEdge(f1, f2); - g.addEdge(f2, f3); - EXPECT_FALSE(g.isReachable(f0, target)); - EXPECT_FALSE(g.isReachable(f1, target)); - EXPECT_FALSE(g.isReachable(f2, target)); - EXPECT_FALSE(g.isReachable(f3, target)); - - g.addEdge(f1, t0); - EXPECT_TRUE(g.isReachable(f0, target)); - EXPECT_TRUE(g.isReachable(f1, target)); - EXPECT_FALSE(g.isReachable(f2, target)); - EXPECT_FALSE(g.isReachable(f3, target)); - EXPECT_EQ(g.findPath(f0, target, path, ARRAY_SIZE(path)), 3U); - EXPECT_EQ(path[0], f0); - EXPECT_EQ(path[1], f1); - EXPECT_EQ(path[2], t0); - EXPECT_EQ(g.findPath(f1, target, path, ARRAY_SIZE(path)), 2U); - EXPECT_EQ(path[0], f1); - EXPECT_EQ(path[1], t0); - - g.addEdge(f3, t1); - EXPECT_TRUE(g.isReachable(f0, target)); - EXPECT_TRUE(g.isReachable(f1, target)); - EXPECT_TRUE(g.isReachable(f2, target)); - EXPECT_TRUE(g.isReachable(f3, target)); -} - -TEST(BVGraph, isReachable) { - Test_isReachable<BV1>(); - Test_isReachable<BV2>(); - Test_isReachable<BV3>(); - Test_isReachable<BV4>(); -} - -template <class BV> -void LongCycle() { - BVGraph<BV> g; - g.clear(); - vector<uptr> path_vec(g.size()); - uptr *path = path_vec.data(); - uptr start = 5; - for (uptr i = start; i < g.size() - 1; i++) { - g.addEdge(i, i + 1); - for (uptr j = 0; j < start; j++) - g.addEdge(i, j); - } - // Bad graph that looks like this: - // 00000000000000 - // 00000000000000 - // 00000000000000 - // 00000000000000 - // 00000000000000 - // 11111010000000 - // 11111001000000 - // 11111000100000 - // 11111000010000 - // 11111000001000 - // 11111000000100 - // 11111000000010 - // 11111000000001 - // if (g.size() <= 64) PrintGraph(g); - BV target; - for (uptr i = start + 1; i < g.size(); i += 11) { - // if ((i & (i - 1)) == 0) fprintf(stderr, "Path: : %zd\n", i); - target.clear(); - target.setBit(i); - EXPECT_TRUE(g.isReachable(start, target)); - EXPECT_EQ(g.findPath(start, target, path, g.size()), i - start + 1); - } -} - -TEST(BVGraph, LongCycle) { - LongCycle<BV1>(); - LongCycle<BV2>(); - LongCycle<BV3>(); - LongCycle<BV4>(); -} - -template <class BV> -void ShortestPath() { - uptr path[8]; - BVGraph<BV> g; - g.clear(); - BV t7; - t7.clear(); - t7.setBit(7); - // 1=>2=>3=>4=>5=>6=>7 - // 1=>7 - g.addEdge(1, 2); - g.addEdge(2, 3); - g.addEdge(3, 4); - g.addEdge(4, 5); - g.addEdge(5, 6); - g.addEdge(6, 7); - g.addEdge(1, 7); - EXPECT_TRUE(g.isReachable(1, t7)); - // No path of length 1. - EXPECT_EQ(0U, g.findPath(1, t7, path, 1)); - // Trying to find a path of len 2..6 gives path of len 2. - EXPECT_EQ(2U, g.findPath(1, t7, path, 2)); - EXPECT_EQ(2U, g.findPath(1, t7, path, 3)); - EXPECT_EQ(2U, g.findPath(1, t7, path, 4)); - EXPECT_EQ(2U, g.findPath(1, t7, path, 5)); - EXPECT_EQ(2U, g.findPath(1, t7, path, 6)); - // Trying to find a path of len 7 gives path of len 7, because this is DFS. - EXPECT_EQ(7U, g.findPath(1, t7, path, 7)); - // But findShortestPath will find the shortest path. - EXPECT_EQ(2U, g.findShortestPath(1, t7, path, 2)); - EXPECT_EQ(2U, g.findShortestPath(1, t7, path, 7)); -} - -TEST(BVGraph, ShortestPath) { - ShortestPath<BV1>(); - ShortestPath<BV2>(); - ShortestPath<BV3>(); - ShortestPath<BV4>(); -} - -template <class BV> -void RunAddEdgesTest() { - BVGraph<BV> g; - BV from; - const int kMaxEdges = 10; - uptr added_edges[kMaxEdges]; - g.clear(); - from.clear(); - EXPECT_EQ(0U, g.addEdges(from, 0, added_edges, kMaxEdges)); - EXPECT_EQ(0U, g.addEdges(from, 1, added_edges, kMaxEdges)); - from.setBit(0); - EXPECT_EQ(1U, g.addEdges(from, 1, added_edges, kMaxEdges)); - EXPECT_EQ(0U, added_edges[0]); - EXPECT_EQ(0U, g.addEdges(from, 1, added_edges, kMaxEdges)); - - from.clear(); - from.setBit(1); - EXPECT_EQ(1U, g.addEdges(from, 4, added_edges, kMaxEdges)); - EXPECT_TRUE(g.hasEdge(1, 4)); - EXPECT_FALSE(g.hasEdge(1, 5)); - EXPECT_EQ(1U, added_edges[0]); - from.setBit(2); - from.setBit(3); - EXPECT_EQ(2U, g.addEdges(from, 4, added_edges, kMaxEdges)); - EXPECT_TRUE(g.hasEdge(2, 4)); - EXPECT_FALSE(g.hasEdge(2, 5)); - EXPECT_TRUE(g.hasEdge(3, 4)); - EXPECT_FALSE(g.hasEdge(3, 5)); - EXPECT_EQ(2U, added_edges[0]); - EXPECT_EQ(3U, added_edges[1]); -} - -TEST(BVGraph, AddEdgesTest) { - RunAddEdgesTest<BV2>(); -} diff --git a/contrib/compiler-rt/lib/sanitizer_common/tests/sanitizer_common_test.cc b/contrib/compiler-rt/lib/sanitizer_common/tests/sanitizer_common_test.cc deleted file mode 100644 index e08a38c82450..000000000000 --- a/contrib/compiler-rt/lib/sanitizer_common/tests/sanitizer_common_test.cc +++ /dev/null @@ -1,229 +0,0 @@ -//===-- sanitizer_common_test.cc ------------------------------------------===// -// -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. -// -//===----------------------------------------------------------------------===// -// -// This file is a part of ThreadSanitizer/AddressSanitizer runtime. -// -//===----------------------------------------------------------------------===// -#include "sanitizer_common/sanitizer_allocator_internal.h" -#include "sanitizer_common/sanitizer_common.h" -#include "sanitizer_common/sanitizer_flags.h" -#include "sanitizer_common/sanitizer_libc.h" -#include "sanitizer_common/sanitizer_platform.h" - -#include "sanitizer_pthread_wrappers.h" - -#include "gtest/gtest.h" - -namespace __sanitizer { - -static bool IsSorted(const uptr *array, uptr n) { - for (uptr i = 1; i < n; i++) { - if (array[i] < array[i - 1]) return false; - } - return true; -} - -TEST(SanitizerCommon, SortTest) { - uptr array[100]; - uptr n = 100; - // Already sorted. - for (uptr i = 0; i < n; i++) { - array[i] = i; - } - SortArray(array, n); - EXPECT_TRUE(IsSorted(array, n)); - // Reverse order. - for (uptr i = 0; i < n; i++) { - array[i] = n - 1 - i; - } - SortArray(array, n); - EXPECT_TRUE(IsSorted(array, n)); - // Mixed order. - for (uptr i = 0; i < n; i++) { - array[i] = (i % 2 == 0) ? i : n - 1 - i; - } - SortArray(array, n); - EXPECT_TRUE(IsSorted(array, n)); - // All equal. - for (uptr i = 0; i < n; i++) { - array[i] = 42; - } - SortArray(array, n); - EXPECT_TRUE(IsSorted(array, n)); - // All but one sorted. - for (uptr i = 0; i < n - 1; i++) { - array[i] = i; - } - array[n - 1] = 42; - SortArray(array, n); - EXPECT_TRUE(IsSorted(array, n)); - // Minimal case - sort three elements. - array[0] = 1; - array[1] = 0; - SortArray(array, 2); - EXPECT_TRUE(IsSorted(array, 2)); -} - -TEST(SanitizerCommon, MmapAlignedOrDie) { - uptr PageSize = GetPageSizeCached(); - for (uptr size = 1; size <= 32; size *= 2) { - for (uptr alignment = 1; alignment <= 32; alignment *= 2) { - for (int iter = 0; iter < 100; iter++) { - uptr res = (uptr)MmapAlignedOrDie( - size * PageSize, alignment * PageSize, "MmapAlignedOrDieTest"); - EXPECT_EQ(0U, res % (alignment * PageSize)); - internal_memset((void*)res, 1, size * PageSize); - UnmapOrDie((void*)res, size * PageSize); - } - } - } -} - -#if SANITIZER_LINUX -TEST(SanitizerCommon, SanitizerSetThreadName) { - const char *names[] = { - "0123456789012", - "01234567890123", - "012345678901234", // Larger names will be truncated on linux. - }; - - for (size_t i = 0; i < ARRAY_SIZE(names); i++) { - EXPECT_TRUE(SanitizerSetThreadName(names[i])); - char buff[100]; - EXPECT_TRUE(SanitizerGetThreadName(buff, sizeof(buff) - 1)); - EXPECT_EQ(0, internal_strcmp(buff, names[i])); - } -} -#endif - -TEST(SanitizerCommon, InternalMmapVector) { - InternalMmapVector<uptr> vector(1); - for (uptr i = 0; i < 100; i++) { - EXPECT_EQ(i, vector.size()); - vector.push_back(i); - } - for (uptr i = 0; i < 100; i++) { - EXPECT_EQ(i, vector[i]); - } - for (int i = 99; i >= 0; i--) { - EXPECT_EQ((uptr)i, vector.back()); - vector.pop_back(); - EXPECT_EQ((uptr)i, vector.size()); - } - InternalMmapVector<uptr> empty_vector(0); - CHECK_GT(empty_vector.capacity(), 0U); - CHECK_EQ(0U, empty_vector.size()); -} - -void TestThreadInfo(bool main) { - uptr stk_addr = 0; - uptr stk_size = 0; - uptr tls_addr = 0; - uptr tls_size = 0; - GetThreadStackAndTls(main, &stk_addr, &stk_size, &tls_addr, &tls_size); - - int stack_var; - EXPECT_NE(stk_addr, (uptr)0); - EXPECT_NE(stk_size, (uptr)0); - EXPECT_GT((uptr)&stack_var, stk_addr); - EXPECT_LT((uptr)&stack_var, stk_addr + stk_size); - -#if SANITIZER_LINUX && defined(__x86_64__) - static __thread int thread_var; - EXPECT_NE(tls_addr, (uptr)0); - EXPECT_NE(tls_size, (uptr)0); - EXPECT_GT((uptr)&thread_var, tls_addr); - EXPECT_LT((uptr)&thread_var, tls_addr + tls_size); - - // Ensure that tls and stack do not intersect. - uptr tls_end = tls_addr + tls_size; - EXPECT_TRUE(tls_addr < stk_addr || tls_addr >= stk_addr + stk_size); - EXPECT_TRUE(tls_end < stk_addr || tls_end >= stk_addr + stk_size); - EXPECT_TRUE((tls_addr < stk_addr) == (tls_end < stk_addr)); -#endif -} - -static void *WorkerThread(void *arg) { - TestThreadInfo(false); - return 0; -} - -TEST(SanitizerCommon, ThreadStackTlsMain) { - InitTlsSize(); - TestThreadInfo(true); -} - -TEST(SanitizerCommon, ThreadStackTlsWorker) { - InitTlsSize(); - pthread_t t; - PTHREAD_CREATE(&t, 0, WorkerThread, 0); - PTHREAD_JOIN(t, 0); -} - -bool UptrLess(uptr a, uptr b) { - return a < b; -} - -TEST(SanitizerCommon, InternalBinarySearch) { - static const uptr kSize = 5; - uptr arr[kSize]; - for (uptr i = 0; i < kSize; i++) arr[i] = i * i; - - for (uptr i = 0; i < kSize; i++) - ASSERT_EQ(InternalBinarySearch(arr, 0, kSize, i * i, UptrLess), i); - - ASSERT_EQ(InternalBinarySearch(arr, 0, kSize, 7, UptrLess), kSize + 1); -} - -#if SANITIZER_LINUX && !SANITIZER_ANDROID -TEST(SanitizerCommon, FindPathToBinary) { - char *true_path = FindPathToBinary("true"); - EXPECT_NE((char*)0, internal_strstr(true_path, "/bin/true")); - InternalFree(true_path); - EXPECT_EQ(0, FindPathToBinary("unexisting_binary.ergjeorj")); -} -#endif - -TEST(SanitizerCommon, StripPathPrefix) { - EXPECT_EQ(0, StripPathPrefix(0, "prefix")); - EXPECT_STREQ("foo", StripPathPrefix("foo", 0)); - EXPECT_STREQ("dir/file.cc", - StripPathPrefix("/usr/lib/dir/file.cc", "/usr/lib/")); - EXPECT_STREQ("/file.cc", StripPathPrefix("/usr/myroot/file.cc", "/myroot")); - EXPECT_STREQ("file.h", StripPathPrefix("/usr/lib/./file.h", "/usr/lib/")); -} - -TEST(SanitizerCommon, InternalScopedString) { - InternalScopedString str(10); - EXPECT_EQ(0U, str.length()); - EXPECT_STREQ("", str.data()); - - str.append("foo"); - EXPECT_EQ(3U, str.length()); - EXPECT_STREQ("foo", str.data()); - - int x = 1234; - str.append("%d", x); - EXPECT_EQ(7U, str.length()); - EXPECT_STREQ("foo1234", str.data()); - - str.append("%d", x); - EXPECT_EQ(9U, str.length()); - EXPECT_STREQ("foo123412", str.data()); - - str.clear(); - EXPECT_EQ(0U, str.length()); - EXPECT_STREQ("", str.data()); - - str.append("0123456789"); - EXPECT_EQ(9U, str.length()); - EXPECT_STREQ("012345678", str.data()); -} - -} // namespace __sanitizer diff --git a/contrib/compiler-rt/lib/sanitizer_common/tests/sanitizer_deadlock_detector_test.cc b/contrib/compiler-rt/lib/sanitizer_common/tests/sanitizer_deadlock_detector_test.cc deleted file mode 100644 index 7835eef76d06..000000000000 --- a/contrib/compiler-rt/lib/sanitizer_common/tests/sanitizer_deadlock_detector_test.cc +++ /dev/null @@ -1,496 +0,0 @@ -//===-- sanitizer_deadlock_detector_test.cc -------------------------------===// -// -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. -// -//===----------------------------------------------------------------------===// -// -// This file is a part of Sanitizer runtime. -// Tests for sanitizer_deadlock_detector.h -// -//===----------------------------------------------------------------------===// -#include "sanitizer_common/sanitizer_deadlock_detector.h" - -#include "sanitizer_test_utils.h" - -#include "gtest/gtest.h" - -#include <algorithm> -#include <vector> -#include <set> - -using namespace __sanitizer; -using namespace std; - -typedef BasicBitVector<u8> BV1; -typedef BasicBitVector<> BV2; -typedef TwoLevelBitVector<> BV3; -typedef TwoLevelBitVector<3, BasicBitVector<u8> > BV4; - -// Poor man's unique_ptr. -template<class BV> -struct ScopedDD { - ScopedDD() { - dp = new DeadlockDetector<BV>; - dp->clear(); - dtls.clear(); - } - ~ScopedDD() { delete dp; } - DeadlockDetector<BV> *dp; - DeadlockDetectorTLS<BV> dtls; -}; - -template <class BV> -void RunBasicTest() { - uptr path[10]; - ScopedDD<BV> sdd; - DeadlockDetector<BV> &d = *sdd.dp; - DeadlockDetectorTLS<BV> &dtls = sdd.dtls; - set<uptr> s; - for (size_t i = 0; i < d.size() * 3; i++) { - uptr node = d.newNode(0); - EXPECT_TRUE(s.insert(node).second); - } - - d.clear(); - s.clear(); - // Add size() nodes. - for (size_t i = 0; i < d.size(); i++) { - uptr node = d.newNode(0); - EXPECT_TRUE(s.insert(node).second); - } - // Remove all nodes. - for (set<uptr>::iterator it = s.begin(); it != s.end(); ++it) - d.removeNode(*it); - // The nodes should be reused. - for (size_t i = 0; i < d.size(); i++) { - uptr node = d.newNode(0); - EXPECT_FALSE(s.insert(node).second); - } - - // Cycle: n1->n2->n1 - { - d.clear(); - dtls.clear(); - uptr n1 = d.newNode(1); - uptr n2 = d.newNode(2); - EXPECT_FALSE(d.onLock(&dtls, n1)); - EXPECT_FALSE(d.onLock(&dtls, n2)); - d.onUnlock(&dtls, n2); - d.onUnlock(&dtls, n1); - - EXPECT_FALSE(d.onLock(&dtls, n2)); - EXPECT_EQ(0U, d.findPathToLock(&dtls, n1, path, 1)); - EXPECT_EQ(2U, d.findPathToLock(&dtls, n1, path, 10)); - EXPECT_EQ(2U, d.findPathToLock(&dtls, n1, path, 2)); - EXPECT_TRUE(d.onLock(&dtls, n1)); - EXPECT_EQ(path[0], n1); - EXPECT_EQ(path[1], n2); - EXPECT_EQ(d.getData(n1), 1U); - EXPECT_EQ(d.getData(n2), 2U); - d.onUnlock(&dtls, n1); - d.onUnlock(&dtls, n2); - } - - // Cycle: n1->n2->n3->n1 - { - d.clear(); - dtls.clear(); - uptr n1 = d.newNode(1); - uptr n2 = d.newNode(2); - uptr n3 = d.newNode(3); - - EXPECT_FALSE(d.onLock(&dtls, n1)); - EXPECT_FALSE(d.onLock(&dtls, n2)); - d.onUnlock(&dtls, n2); - d.onUnlock(&dtls, n1); - - EXPECT_FALSE(d.onLock(&dtls, n2)); - EXPECT_FALSE(d.onLock(&dtls, n3)); - d.onUnlock(&dtls, n3); - d.onUnlock(&dtls, n2); - - EXPECT_FALSE(d.onLock(&dtls, n3)); - EXPECT_EQ(0U, d.findPathToLock(&dtls, n1, path, 2)); - EXPECT_EQ(3U, d.findPathToLock(&dtls, n1, path, 10)); - EXPECT_TRUE(d.onLock(&dtls, n1)); - EXPECT_EQ(path[0], n1); - EXPECT_EQ(path[1], n2); - EXPECT_EQ(path[2], n3); - EXPECT_EQ(d.getData(n1), 1U); - EXPECT_EQ(d.getData(n2), 2U); - EXPECT_EQ(d.getData(n3), 3U); - d.onUnlock(&dtls, n1); - d.onUnlock(&dtls, n3); - } -} - -TEST(DeadlockDetector, BasicTest) { - RunBasicTest<BV1>(); - RunBasicTest<BV2>(); - RunBasicTest<BV3>(); - RunBasicTest<BV4>(); -} - -template <class BV> -void RunRemoveNodeTest() { - ScopedDD<BV> sdd; - DeadlockDetector<BV> &d = *sdd.dp; - DeadlockDetectorTLS<BV> &dtls = sdd.dtls; - - uptr l0 = d.newNode(0); - uptr l1 = d.newNode(1); - uptr l2 = d.newNode(2); - uptr l3 = d.newNode(3); - uptr l4 = d.newNode(4); - uptr l5 = d.newNode(5); - - // l0=>l1=>l2 - d.onLock(&dtls, l0); - d.onLock(&dtls, l1); - d.onLock(&dtls, l2); - d.onUnlock(&dtls, l1); - d.onUnlock(&dtls, l0); - d.onUnlock(&dtls, l2); - // l3=>l4=>l5 - d.onLock(&dtls, l3); - d.onLock(&dtls, l4); - d.onLock(&dtls, l5); - d.onUnlock(&dtls, l4); - d.onUnlock(&dtls, l3); - d.onUnlock(&dtls, l5); - - set<uptr> locks; - locks.insert(l0); - locks.insert(l1); - locks.insert(l2); - locks.insert(l3); - locks.insert(l4); - locks.insert(l5); - for (uptr i = 6; i < d.size(); i++) { - uptr lt = d.newNode(i); - locks.insert(lt); - d.onLock(&dtls, lt); - d.onUnlock(&dtls, lt); - d.removeNode(lt); - } - EXPECT_EQ(locks.size(), d.size()); - // l2=>l0 - EXPECT_FALSE(d.onLock(&dtls, l2)); - EXPECT_TRUE(d.onLock(&dtls, l0)); - d.onUnlock(&dtls, l2); - d.onUnlock(&dtls, l0); - // l4=>l3 - EXPECT_FALSE(d.onLock(&dtls, l4)); - EXPECT_TRUE(d.onLock(&dtls, l3)); - d.onUnlock(&dtls, l4); - d.onUnlock(&dtls, l3); - - EXPECT_EQ(d.size(), d.testOnlyGetEpoch()); - - d.removeNode(l2); - d.removeNode(l3); - locks.clear(); - // make sure no edges from or to l0,l1,l4,l5 left. - for (uptr i = 4; i < d.size(); i++) { - uptr lt = d.newNode(i); - locks.insert(lt); - uptr a, b; - // l0 => lt? - a = l0; b = lt; - EXPECT_FALSE(d.onLock(&dtls, a)); - EXPECT_FALSE(d.onLock(&dtls, b)); - d.onUnlock(&dtls, a); - d.onUnlock(&dtls, b); - // l1 => lt? - a = l1; b = lt; - EXPECT_FALSE(d.onLock(&dtls, a)); - EXPECT_FALSE(d.onLock(&dtls, b)); - d.onUnlock(&dtls, a); - d.onUnlock(&dtls, b); - // lt => l4? - a = lt; b = l4; - EXPECT_FALSE(d.onLock(&dtls, a)); - EXPECT_FALSE(d.onLock(&dtls, b)); - d.onUnlock(&dtls, a); - d.onUnlock(&dtls, b); - // lt => l5? - a = lt; b = l5; - EXPECT_FALSE(d.onLock(&dtls, a)); - EXPECT_FALSE(d.onLock(&dtls, b)); - d.onUnlock(&dtls, a); - d.onUnlock(&dtls, b); - - d.removeNode(lt); - } - // Still the same epoch. - EXPECT_EQ(d.size(), d.testOnlyGetEpoch()); - EXPECT_EQ(locks.size(), d.size() - 4); - // l2 and l3 should have ben reused. - EXPECT_EQ(locks.count(l2), 1U); - EXPECT_EQ(locks.count(l3), 1U); -} - -TEST(DeadlockDetector, RemoveNodeTest) { - RunRemoveNodeTest<BV1>(); - RunRemoveNodeTest<BV2>(); - RunRemoveNodeTest<BV3>(); - RunRemoveNodeTest<BV4>(); -} - -template <class BV> -void RunMultipleEpochsTest() { - ScopedDD<BV> sdd; - DeadlockDetector<BV> &d = *sdd.dp; - DeadlockDetectorTLS<BV> &dtls = sdd.dtls; - - set<uptr> locks; - for (uptr i = 0; i < d.size(); i++) { - EXPECT_TRUE(locks.insert(d.newNode(i)).second); - } - EXPECT_EQ(d.testOnlyGetEpoch(), d.size()); - for (uptr i = 0; i < d.size(); i++) { - EXPECT_TRUE(locks.insert(d.newNode(i)).second); - EXPECT_EQ(d.testOnlyGetEpoch(), d.size() * 2); - } - locks.clear(); - - uptr l0 = d.newNode(0); - uptr l1 = d.newNode(0); - d.onLock(&dtls, l0); - d.onLock(&dtls, l1); - d.onUnlock(&dtls, l0); - EXPECT_EQ(d.testOnlyGetEpoch(), 3 * d.size()); - for (uptr i = 0; i < d.size(); i++) { - EXPECT_TRUE(locks.insert(d.newNode(i)).second); - } - EXPECT_EQ(d.testOnlyGetEpoch(), 4 * d.size()); - -#if !SANITIZER_DEBUG - // EXPECT_DEATH clones a thread with 4K stack, - // which is overflown by tsan memory accesses functions in debug mode. - - // Can not handle the locks from the previous epoch. - // The caller should update the lock id. - EXPECT_DEATH(d.onLock(&dtls, l0), "CHECK failed.*current_epoch_"); -#endif -} - -TEST(DeadlockDetector, MultipleEpochsTest) { - RunMultipleEpochsTest<BV1>(); - RunMultipleEpochsTest<BV2>(); - RunMultipleEpochsTest<BV3>(); - RunMultipleEpochsTest<BV4>(); -} - -template <class BV> -void RunCorrectEpochFlush() { - ScopedDD<BV> sdd; - DeadlockDetector<BV> &d = *sdd.dp; - DeadlockDetectorTLS<BV> &dtls = sdd.dtls; - vector<uptr> locks1; - for (uptr i = 0; i < d.size(); i++) - locks1.push_back(d.newNode(i)); - EXPECT_EQ(d.testOnlyGetEpoch(), d.size()); - d.onLock(&dtls, locks1[3]); - d.onLock(&dtls, locks1[4]); - d.onLock(&dtls, locks1[5]); - - // We have a new epoch, old locks in dtls will have to be forgotten. - uptr l0 = d.newNode(0); - EXPECT_EQ(d.testOnlyGetEpoch(), d.size() * 2); - uptr l1 = d.newNode(0); - EXPECT_EQ(d.testOnlyGetEpoch(), d.size() * 2); - d.onLock(&dtls, l0); - d.onLock(&dtls, l1); - EXPECT_TRUE(d.testOnlyHasEdgeRaw(0, 1)); - EXPECT_FALSE(d.testOnlyHasEdgeRaw(1, 0)); - EXPECT_FALSE(d.testOnlyHasEdgeRaw(3, 0)); - EXPECT_FALSE(d.testOnlyHasEdgeRaw(4, 0)); - EXPECT_FALSE(d.testOnlyHasEdgeRaw(5, 0)); -} - -TEST(DeadlockDetector, CorrectEpochFlush) { - RunCorrectEpochFlush<BV1>(); - RunCorrectEpochFlush<BV2>(); -} - -template <class BV> -void RunTryLockTest() { - ScopedDD<BV> sdd; - DeadlockDetector<BV> &d = *sdd.dp; - DeadlockDetectorTLS<BV> &dtls = sdd.dtls; - - uptr l0 = d.newNode(0); - uptr l1 = d.newNode(0); - uptr l2 = d.newNode(0); - EXPECT_FALSE(d.onLock(&dtls, l0)); - EXPECT_FALSE(d.onTryLock(&dtls, l1)); - EXPECT_FALSE(d.onLock(&dtls, l2)); - EXPECT_TRUE(d.isHeld(&dtls, l0)); - EXPECT_TRUE(d.isHeld(&dtls, l1)); - EXPECT_TRUE(d.isHeld(&dtls, l2)); - EXPECT_FALSE(d.testOnlyHasEdge(l0, l1)); - EXPECT_TRUE(d.testOnlyHasEdge(l1, l2)); - d.onUnlock(&dtls, l0); - d.onUnlock(&dtls, l1); - d.onUnlock(&dtls, l2); -} - -TEST(DeadlockDetector, TryLockTest) { - RunTryLockTest<BV1>(); - RunTryLockTest<BV2>(); -} - -template <class BV> -void RunOnFirstLockTest() { - ScopedDD<BV> sdd; - DeadlockDetector<BV> &d = *sdd.dp; - DeadlockDetectorTLS<BV> &dtls = sdd.dtls; - - uptr l0 = d.newNode(0); - uptr l1 = d.newNode(0); - EXPECT_FALSE(d.onFirstLock(&dtls, l0)); // dtls has old epoch. - d.onLock(&dtls, l0); - d.onUnlock(&dtls, l0); - - EXPECT_TRUE(d.onFirstLock(&dtls, l0)); // Ok, same ecpoch, first lock. - EXPECT_FALSE(d.onFirstLock(&dtls, l1)); // Second lock. - d.onLock(&dtls, l1); - d.onUnlock(&dtls, l1); - d.onUnlock(&dtls, l0); - - EXPECT_TRUE(d.onFirstLock(&dtls, l0)); // Ok - d.onUnlock(&dtls, l0); - - vector<uptr> locks1; - for (uptr i = 0; i < d.size(); i++) - locks1.push_back(d.newNode(i)); - - EXPECT_TRUE(d.onFirstLock(&dtls, l0)); // Epoch has changed, but not in dtls. - - uptr l3 = d.newNode(0); - d.onLock(&dtls, l3); - d.onUnlock(&dtls, l3); - - EXPECT_FALSE(d.onFirstLock(&dtls, l0)); // Epoch has changed in dtls. -} - -TEST(DeadlockDetector, onFirstLockTest) { - RunOnFirstLockTest<BV2>(); -} - -template <class BV> -void RunRecusriveLockTest() { - ScopedDD<BV> sdd; - DeadlockDetector<BV> &d = *sdd.dp; - DeadlockDetectorTLS<BV> &dtls = sdd.dtls; - - uptr l0 = d.newNode(0); - uptr l1 = d.newNode(0); - uptr l2 = d.newNode(0); - uptr l3 = d.newNode(0); - - EXPECT_FALSE(d.onLock(&dtls, l0)); - EXPECT_FALSE(d.onLock(&dtls, l1)); - EXPECT_FALSE(d.onLock(&dtls, l0)); // Recurisve. - EXPECT_FALSE(d.onLock(&dtls, l2)); - d.onUnlock(&dtls, l0); - EXPECT_FALSE(d.onLock(&dtls, l3)); - d.onUnlock(&dtls, l0); - d.onUnlock(&dtls, l1); - d.onUnlock(&dtls, l2); - d.onUnlock(&dtls, l3); - EXPECT_TRUE(d.testOnlyHasEdge(l0, l1)); - EXPECT_TRUE(d.testOnlyHasEdge(l0, l2)); - EXPECT_TRUE(d.testOnlyHasEdge(l0, l3)); -} - -TEST(DeadlockDetector, RecusriveLockTest) { - RunRecusriveLockTest<BV2>(); -} - -template <class BV> -void RunLockContextTest() { - ScopedDD<BV> sdd; - DeadlockDetector<BV> &d = *sdd.dp; - DeadlockDetectorTLS<BV> &dtls = sdd.dtls; - - uptr l0 = d.newNode(0); - uptr l1 = d.newNode(0); - uptr l2 = d.newNode(0); - uptr l3 = d.newNode(0); - uptr l4 = d.newNode(0); - EXPECT_FALSE(d.onLock(&dtls, l0, 10)); - EXPECT_FALSE(d.onLock(&dtls, l1, 11)); - EXPECT_FALSE(d.onLock(&dtls, l2, 12)); - EXPECT_FALSE(d.onLock(&dtls, l3, 13)); - EXPECT_EQ(10U, d.findLockContext(&dtls, l0)); - EXPECT_EQ(11U, d.findLockContext(&dtls, l1)); - EXPECT_EQ(12U, d.findLockContext(&dtls, l2)); - EXPECT_EQ(13U, d.findLockContext(&dtls, l3)); - d.onUnlock(&dtls, l0); - EXPECT_EQ(0U, d.findLockContext(&dtls, l0)); - EXPECT_EQ(11U, d.findLockContext(&dtls, l1)); - EXPECT_EQ(12U, d.findLockContext(&dtls, l2)); - EXPECT_EQ(13U, d.findLockContext(&dtls, l3)); - d.onUnlock(&dtls, l2); - EXPECT_EQ(0U, d.findLockContext(&dtls, l0)); - EXPECT_EQ(11U, d.findLockContext(&dtls, l1)); - EXPECT_EQ(0U, d.findLockContext(&dtls, l2)); - EXPECT_EQ(13U, d.findLockContext(&dtls, l3)); - - EXPECT_FALSE(d.onLock(&dtls, l4, 14)); - EXPECT_EQ(14U, d.findLockContext(&dtls, l4)); -} - -TEST(DeadlockDetector, LockContextTest) { - RunLockContextTest<BV2>(); -} - -template <class BV> -void RunRemoveEdgesTest() { - ScopedDD<BV> sdd; - DeadlockDetector<BV> &d = *sdd.dp; - DeadlockDetectorTLS<BV> &dtls = sdd.dtls; - vector<uptr> node(BV::kSize); - u32 stk_from = 0, stk_to = 0; - int unique_tid = 0; - for (size_t i = 0; i < BV::kSize; i++) - node[i] = d.newNode(0); - - for (size_t i = 0; i < BV::kSize; i++) - EXPECT_FALSE(d.onLock(&dtls, node[i], i + 1)); - for (size_t i = 0; i < BV::kSize; i++) { - for (uptr j = i + 1; j < BV::kSize; j++) { - EXPECT_TRUE( - d.findEdge(node[i], node[j], &stk_from, &stk_to, &unique_tid)); - EXPECT_EQ(stk_from, i + 1); - EXPECT_EQ(stk_to, j + 1); - } - } - EXPECT_EQ(d.testOnlyGetEpoch(), d.size()); - // Remove and re-create half of the nodes. - for (uptr i = 1; i < BV::kSize; i += 2) - d.removeNode(node[i]); - for (uptr i = 1; i < BV::kSize; i += 2) - node[i] = d.newNode(0); - EXPECT_EQ(d.testOnlyGetEpoch(), d.size()); - // The edges from or to the removed nodes should be gone. - for (size_t i = 0; i < BV::kSize; i++) { - for (uptr j = i + 1; j < BV::kSize; j++) { - if ((i % 2) || (j % 2)) - EXPECT_FALSE( - d.findEdge(node[i], node[j], &stk_from, &stk_to, &unique_tid)); - else - EXPECT_TRUE( - d.findEdge(node[i], node[j], &stk_from, &stk_to, &unique_tid)); - } - } -} - -TEST(DeadlockDetector, RemoveEdgesTest) { - RunRemoveEdgesTest<BV1>(); -} diff --git a/contrib/compiler-rt/lib/sanitizer_common/tests/sanitizer_flags_test.cc b/contrib/compiler-rt/lib/sanitizer_common/tests/sanitizer_flags_test.cc deleted file mode 100644 index 3e5d8381ed3a..000000000000 --- a/contrib/compiler-rt/lib/sanitizer_common/tests/sanitizer_flags_test.cc +++ /dev/null @@ -1,142 +0,0 @@ -//===-- sanitizer_flags_test.cc -------------------------------------------===// -// -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. -// -//===----------------------------------------------------------------------===// -// -// This file is a part of ThreadSanitizer/AddressSanitizer runtime. -// -//===----------------------------------------------------------------------===// -#include "sanitizer_common/sanitizer_common.h" -#include "sanitizer_common/sanitizer_flags.h" -#include "sanitizer_common/sanitizer_flag_parser.h" -#include "sanitizer_common/sanitizer_libc.h" -#include "sanitizer_common/sanitizer_allocator_internal.h" -#include "gtest/gtest.h" - -#include <string.h> - -namespace __sanitizer { - -static const char kFlagName[] = "flag_name"; -static const char kFlagDesc[] = "flag description"; - -template <typename T> -static void TestFlag(T start_value, const char *env, T final_value) { - T flag = start_value; - - FlagParser parser; - RegisterFlag(&parser, kFlagName, kFlagDesc, &flag); - - parser.ParseString(env); - - EXPECT_EQ(final_value, flag); -} - -template <> -void TestFlag(const char *start_value, const char *env, - const char *final_value) { - const char *flag = start_value; - - FlagParser parser; - RegisterFlag(&parser, kFlagName, kFlagDesc, &flag); - - parser.ParseString(env); - - EXPECT_EQ(0, internal_strcmp(final_value, flag)); -} - -TEST(SanitizerCommon, BooleanFlags) { - TestFlag(false, "flag_name=1", true); - TestFlag(false, "flag_name=yes", true); - TestFlag(false, "flag_name=true", true); - TestFlag(true, "flag_name=0", false); - TestFlag(true, "flag_name=no", false); - TestFlag(true, "flag_name=false", false); -} - -TEST(SanitizerCommon, IntFlags) { - TestFlag(-11, 0, -11); - TestFlag(-11, "flag_name=0", 0); - TestFlag(-11, "flag_name=42", 42); - TestFlag(-11, "flag_name=-42", -42); - - // Unrecognized flags are ignored. - TestFlag(-11, "--flag_name=42", -11); - TestFlag(-11, "zzzzzzz=42", -11); - - EXPECT_DEATH(TestFlag(-11, "flag_name", 0), "expected '='"); - EXPECT_DEATH(TestFlag(-11, "flag_name=42U", 0), - "Invalid value for int option"); -} - -TEST(SanitizerCommon, StrFlags) { - TestFlag("zzz", 0, "zzz"); - TestFlag("zzz", "flag_name=", ""); - TestFlag("zzz", "flag_name=abc", "abc"); - TestFlag("", "flag_name=abc", "abc"); - TestFlag("", "flag_name='abc zxc'", "abc zxc"); - // TestStrFlag("", "flag_name=\"abc qwe\" asd", "abc qwe"); -} - -static void TestTwoFlags(const char *env, bool expected_flag1, - const char *expected_flag2, - const char *name1 = "flag1", - const char *name2 = "flag2") { - bool flag1 = !expected_flag1; - const char *flag2 = ""; - - FlagParser parser; - RegisterFlag(&parser, name1, kFlagDesc, &flag1); - RegisterFlag(&parser, name2, kFlagDesc, &flag2); - - parser.ParseString(env); - - EXPECT_EQ(expected_flag1, flag1); - EXPECT_EQ(0, internal_strcmp(flag2, expected_flag2)); -} - -TEST(SanitizerCommon, MultipleFlags) { - TestTwoFlags("flag1=1 flag2='zzz'", true, "zzz"); - TestTwoFlags("flag2='qxx' flag1=0", false, "qxx"); - TestTwoFlags("flag1=false:flag2='zzz'", false, "zzz"); - TestTwoFlags("flag2=qxx:flag1=yes", true, "qxx"); - TestTwoFlags("flag2=qxx\nflag1=yes", true, "qxx"); - TestTwoFlags("flag2=qxx\r\nflag1=yes", true, "qxx"); - TestTwoFlags("flag2=qxx\tflag1=yes", true, "qxx"); -} - -TEST(SanitizerCommon, CommonSuffixFlags) { - TestTwoFlags("flag=1 other_flag='zzz'", true, "zzz", "flag", "other_flag"); - TestTwoFlags("other_flag='zzz' flag=1", true, "zzz", "flag", "other_flag"); - TestTwoFlags("other_flag=' flag=0 ' flag=1", true, " flag=0 ", "flag", - "other_flag"); - TestTwoFlags("flag=1 other_flag=' flag=0 '", true, " flag=0 ", "flag", - "other_flag"); -} - -TEST(SanitizerCommon, CommonFlags) { - CommonFlags cf; - FlagParser parser; - RegisterCommonFlags(&parser, &cf); - - cf.SetDefaults(); - EXPECT_TRUE(cf.symbolize); - EXPECT_STREQ(".", cf.coverage_dir); - - cf.symbolize = false; - cf.coverage = true; - cf.coverage_direct = true; - cf.log_path = "path/one"; - - parser.ParseString("symbolize=1:coverage_direct=false log_path='path/two'"); - EXPECT_TRUE(cf.symbolize); - EXPECT_TRUE(cf.coverage); - EXPECT_FALSE(cf.coverage_direct); - EXPECT_STREQ("path/two", cf.log_path); -} - -} // namespace __sanitizer diff --git a/contrib/compiler-rt/lib/sanitizer_common/tests/sanitizer_format_interceptor_test.cc b/contrib/compiler-rt/lib/sanitizer_common/tests/sanitizer_format_interceptor_test.cc deleted file mode 100644 index 13918aff1009..000000000000 --- a/contrib/compiler-rt/lib/sanitizer_common/tests/sanitizer_format_interceptor_test.cc +++ /dev/null @@ -1,259 +0,0 @@ -//===-- sanitizer_format_interceptor_test.cc ------------------------------===// -// -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. -// -//===----------------------------------------------------------------------===// -// -// Tests for *scanf interceptors implementation in sanitizer_common. -// -//===----------------------------------------------------------------------===// -#include <algorithm> -#include <vector> - -#include "interception/interception.h" -#include "sanitizer_test_utils.h" -#include "sanitizer_common/sanitizer_libc.h" -#include "sanitizer_common/sanitizer_common.h" -#include "gtest/gtest.h" - -using namespace __sanitizer; - -#define COMMON_INTERCEPTOR_READ_WRITE_RANGE(ctx, ptr, size) \ - do { \ - ((std::vector<unsigned> *)ctx)->push_back(size); \ - ptr = ptr; \ - } while (0) - -#define COMMON_INTERCEPTOR_READ_RANGE(ctx, ptr, size) \ - COMMON_INTERCEPTOR_READ_WRITE_RANGE(ctx, ptr, size) - -#define COMMON_INTERCEPTOR_WRITE_RANGE(ctx, ptr, size) \ - COMMON_INTERCEPTOR_READ_WRITE_RANGE(ctx, ptr, size) - -#define SANITIZER_INTERCEPT_PRINTF 1 -#include "sanitizer_common/sanitizer_common_interceptors_format.inc" - -static const unsigned I = sizeof(int); -static const unsigned L = sizeof(long); -static const unsigned LL = sizeof(long long); -static const unsigned S = sizeof(short); -static const unsigned C = sizeof(char); -static const unsigned LC = sizeof(wchar_t); -static const unsigned D = sizeof(double); -static const unsigned LD = sizeof(long double); -static const unsigned F = sizeof(float); -static const unsigned P = sizeof(char *); - -static void verifyFormatResults(const char *format, unsigned n, - const std::vector<unsigned> &computed_sizes, - va_list expected_sizes) { - // "+ 1" because of format string - ASSERT_EQ(n + 1, - computed_sizes.size()) << "Unexpected number of format arguments: '" - << format << "'"; - for (unsigned i = 0; i < n; ++i) - EXPECT_EQ(va_arg(expected_sizes, unsigned), computed_sizes[i + 1]) - << "Unexpect write size for argument " << i << ", format string '" - << format << "'"; -} - -static const char test_buf[] = "Test string."; -static const size_t test_buf_size = sizeof(test_buf); - -static const unsigned SCANF_ARGS_MAX = 16; - -static void testScanf3(void *ctx, int result, bool allowGnuMalloc, - const char *format, ...) { - va_list ap; - va_start(ap, format); - scanf_common(ctx, result, allowGnuMalloc, format, ap); - va_end(ap); -} - -static void testScanf2(const char *format, int scanf_result, - bool allowGnuMalloc, unsigned n, - va_list expected_sizes) { - std::vector<unsigned> scanf_sizes; - // 16 args should be enough. - testScanf3((void *)&scanf_sizes, scanf_result, allowGnuMalloc, format, - test_buf, test_buf, test_buf, test_buf, test_buf, test_buf, - test_buf, test_buf, test_buf, test_buf, test_buf, test_buf, - test_buf, test_buf, test_buf, test_buf); - verifyFormatResults(format, n, scanf_sizes, expected_sizes); -} - -static void testScanf(const char *format, unsigned n, ...) { - va_list ap; - va_start(ap, n); - testScanf2(format, SCANF_ARGS_MAX, /* allowGnuMalloc */ true, n, ap); - va_end(ap); -} - -static void testScanfPartial(const char *format, int scanf_result, unsigned n, - ...) { - va_list ap; - va_start(ap, n); - testScanf2(format, scanf_result, /* allowGnuMalloc */ true, n, ap); - va_end(ap); -} - -static void testScanfNoGnuMalloc(const char *format, unsigned n, ...) { - va_list ap; - va_start(ap, n); - testScanf2(format, SCANF_ARGS_MAX, /* allowGnuMalloc */ false, n, ap); - va_end(ap); -} - -TEST(SanitizerCommonInterceptors, Scanf) { - testScanf("%d", 1, I); - testScanf("%d%d%d", 3, I, I, I); - testScanf("ab%u%dc", 2, I, I); - testScanf("%ld", 1, L); - testScanf("%llu", 1, LL); - testScanf("%qd", 1, LL); - testScanf("a %hd%hhx", 2, S, C); - testScanf("%c", 1, C); - testScanf("%lc", 1, LC); - - testScanf("%%", 0); - testScanf("a%%", 0); - testScanf("a%%b", 0); - testScanf("a%%%%b", 0); - testScanf("a%%b%%", 0); - testScanf("a%%%%%%b", 0); - testScanf("a%%%%%b", 0); - testScanf("a%%%%%f", 1, F); - testScanf("a%%%lxb", 1, L); - testScanf("a%lf%%%lxb", 2, D, L); - testScanf("%nf", 1, I); - - testScanf("%10s", 1, 11); - testScanf("%10c", 1, 10); - testScanf("%10ls", 1, 11 * LC); - testScanf("%10lc", 1, 10 * LC); - testScanf("%%10s", 0); - testScanf("%*10s", 0); - testScanf("%*d", 0); - - testScanf("%4d%8f%c", 3, I, F, C); - testScanf("%s%d", 2, test_buf_size, I); - testScanf("%[abc]", 1, test_buf_size); - testScanf("%4[bcdef]", 1, 5); - testScanf("%[]]", 1, test_buf_size); - testScanf("%8[^]%d0-9-]%c", 2, 9, C); - - testScanf("%*[^:]%n:%d:%1[ ]%n", 4, I, I, 2, I); - - testScanf("%*d%u", 1, I); - - testScanf("%c%d", 2, C, I); - testScanf("%A%lf", 2, F, D); - - testScanf("%ms %Lf", 2, P, LD); - testScanf("s%Las", 1, LD); - testScanf("%ar", 1, F); - - // In the cases with std::min below the format spec can be interpreted as - // either floating-something, or (GNU extension) callee-allocated string. - // Our conservative implementation reports one of the two possibilities with - // the least store range. - testScanf("%a[", 0); - testScanf("%a[]", 0); - testScanf("%a[]]", 1, std::min(F, P)); - testScanf("%a[abc]", 1, std::min(F, P)); - testScanf("%a[^abc]", 1, std::min(F, P)); - testScanf("%a[ab%c] %d", 0); - testScanf("%a[^ab%c] %d", 0); - testScanf("%as", 1, std::min(F, P)); - testScanf("%aS", 1, std::min(F, P)); - testScanf("%a13S", 1, std::min(F, P)); - testScanf("%alS", 1, std::min(F, P)); - - testScanfNoGnuMalloc("s%Las", 1, LD); - testScanfNoGnuMalloc("%ar", 1, F); - testScanfNoGnuMalloc("%a[", 1, F); - testScanfNoGnuMalloc("%a[]", 1, F); - testScanfNoGnuMalloc("%a[]]", 1, F); - testScanfNoGnuMalloc("%a[abc]", 1, F); - testScanfNoGnuMalloc("%a[^abc]", 1, F); - testScanfNoGnuMalloc("%a[ab%c] %d", 3, F, C, I); - testScanfNoGnuMalloc("%a[^ab%c] %d", 3, F, C, I); - testScanfNoGnuMalloc("%as", 1, F); - testScanfNoGnuMalloc("%aS", 1, F); - testScanfNoGnuMalloc("%a13S", 1, F); - testScanfNoGnuMalloc("%alS", 1, F); - - testScanf("%5$d", 0); - testScanf("%md", 0); - testScanf("%m10s", 0); - - testScanfPartial("%d%d%d%d //1\n", 1, 1, I); - testScanfPartial("%d%d%d%d //2\n", 2, 2, I, I); - testScanfPartial("%d%d%d%d //3\n", 3, 3, I, I, I); - testScanfPartial("%d%d%d%d //4\n", 4, 4, I, I, I, I); - - testScanfPartial("%d%n%n%d //1\n", 1, 3, I, I, I); - testScanfPartial("%d%n%n%d //2\n", 2, 4, I, I, I, I); - - testScanfPartial("%d%n%n%d %s %s", 3, 5, I, I, I, I, test_buf_size); - testScanfPartial("%d%n%n%d %s %s", 4, 6, I, I, I, I, test_buf_size, - test_buf_size); -} - -static void testPrintf3(void *ctx, const char *format, ...) { - va_list ap; - va_start(ap, format); - printf_common(ctx, format, ap); - va_end(ap); -} - -static void testPrintf2(const char *format, unsigned n, - va_list expected_sizes) { - std::vector<unsigned> printf_sizes; - // 16 args should be enough. - testPrintf3((void *)&printf_sizes, format, - test_buf, test_buf, test_buf, test_buf, test_buf, test_buf, - test_buf, test_buf, test_buf, test_buf, test_buf, test_buf, - test_buf, test_buf, test_buf, test_buf); - verifyFormatResults(format, n, printf_sizes, expected_sizes); -} - -static void testPrintf(const char *format, unsigned n, ...) { - va_list ap; - va_start(ap, n); - testPrintf2(format, n, ap); - va_end(ap); -} - -TEST(SanitizerCommonInterceptors, Printf) { - // Only test functionality which differs from scanf - - // Indexed arguments - testPrintf("%5$d", 0); - testPrintf("%.*5$d", 0); - - // errno - testPrintf("%0-m", 0); - - // Dynamic width - testPrintf("%*n", 1, I); - testPrintf("%*.10n", 1, I); - - // Precision - testPrintf("%10.10n", 1, I); - testPrintf("%.3s", 1, 3); - testPrintf("%.20s", 1, test_buf_size); - - // Dynamic precision - testPrintf("%.*n", 1, I); - testPrintf("%10.*n", 1, I); - - // Dynamic precision for strings is not implemented yet. - testPrintf("%.*s", 1, 0); - - // Checks for wide-character strings are not implemented yet. - testPrintf("%ls", 1, 0); -} diff --git a/contrib/compiler-rt/lib/sanitizer_common/tests/sanitizer_ioctl_test.cc b/contrib/compiler-rt/lib/sanitizer_common/tests/sanitizer_ioctl_test.cc deleted file mode 100644 index 22fa5228dfbd..000000000000 --- a/contrib/compiler-rt/lib/sanitizer_common/tests/sanitizer_ioctl_test.cc +++ /dev/null @@ -1,103 +0,0 @@ -//===-- sanitizer_ioctl_test.cc -------------------------------------------===// -// -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. -// -//===----------------------------------------------------------------------===// -// -// Tests for ioctl interceptor implementation in sanitizer_common. -// -//===----------------------------------------------------------------------===// - -#include "sanitizer_common/sanitizer_platform.h" -#if SANITIZER_LINUX - -#include <linux/input.h> -#include <vector> - -#include "interception/interception.h" -#include "sanitizer_test_utils.h" -#include "sanitizer_common/sanitizer_platform_limits_posix.h" -#include "sanitizer_common/sanitizer_common.h" -#include "gtest/gtest.h" - - -using namespace __sanitizer; - -#define COMMON_INTERCEPTOR_READ_RANGE(ctx, ptr, sz) \ - do { \ - (void) ctx; \ - (void) ptr; \ - (void) sz; \ - } while (0) -#define COMMON_INTERCEPTOR_WRITE_RANGE(ctx, ptr, sz) \ - do { \ - (void) ctx; \ - (void) ptr; \ - (void) sz; \ - } while (0) - -#include "sanitizer_common/sanitizer_common_interceptors_ioctl.inc" - -static struct IoctlInit { - IoctlInit() { - ioctl_init(); - // Avoid unused function warnings. - (void)&ioctl_common_pre; - (void)&ioctl_common_post; - (void)&ioctl_decode; - } -} ioctl_static_initializer; - -TEST(SanitizerIoctl, Fixup) { - EXPECT_EQ((unsigned)FIONBIO, ioctl_request_fixup(FIONBIO)); - - EXPECT_EQ(EVIOCGBIT(0, 0), ioctl_request_fixup(EVIOCGBIT(0, 16))); - EXPECT_EQ(EVIOCGBIT(0, 0), ioctl_request_fixup(EVIOCGBIT(1, 16))); - EXPECT_EQ(EVIOCGBIT(0, 0), ioctl_request_fixup(EVIOCGBIT(1, 17))); - EXPECT_EQ(EVIOCGBIT(0, 0), ioctl_request_fixup(EVIOCGBIT(31, 16))); - EXPECT_NE(EVIOCGBIT(0, 0), ioctl_request_fixup(EVIOCGBIT(32, 16))); - - EXPECT_EQ(EVIOCGABS(0), ioctl_request_fixup(EVIOCGABS(0))); - EXPECT_EQ(EVIOCGABS(0), ioctl_request_fixup(EVIOCGABS(5))); - EXPECT_EQ(EVIOCGABS(0), ioctl_request_fixup(EVIOCGABS(63))); - EXPECT_NE(EVIOCGABS(0), ioctl_request_fixup(EVIOCGABS(64))); - - EXPECT_EQ(EVIOCSABS(0), ioctl_request_fixup(EVIOCSABS(0))); - EXPECT_EQ(EVIOCSABS(0), ioctl_request_fixup(EVIOCSABS(5))); - EXPECT_EQ(EVIOCSABS(0), ioctl_request_fixup(EVIOCSABS(63))); - EXPECT_NE(EVIOCSABS(0), ioctl_request_fixup(EVIOCSABS(64))); - - const ioctl_desc *desc = ioctl_lookup(EVIOCGKEY(16)); - EXPECT_NE((void *)0, desc); - EXPECT_EQ(EVIOCGKEY(0), desc->req); -} - -// Test decoding KVM ioctl numbers. -TEST(SanitizerIoctl, KVM_GET_MP_STATE) { - ioctl_desc desc; - bool res = ioctl_decode(0x8004ae98U, &desc); - EXPECT_TRUE(res); - EXPECT_EQ(ioctl_desc::WRITE, desc.type); - EXPECT_EQ(4U, desc.size); -} - -TEST(SanitizerIoctl, KVM_GET_LAPIC) { - ioctl_desc desc; - bool res = ioctl_decode(0x8400ae8eU, &desc); - EXPECT_TRUE(res); - EXPECT_EQ(ioctl_desc::WRITE, desc.type); - EXPECT_EQ(1024U, desc.size); -} - -TEST(SanitizerIoctl, KVM_GET_MSR_INDEX_LIST) { - ioctl_desc desc; - bool res = ioctl_decode(0xc004ae02U, &desc); - EXPECT_TRUE(res); - EXPECT_EQ(ioctl_desc::READWRITE, desc.type); - EXPECT_EQ(4U, desc.size); -} - -#endif // SANITIZER_LINUX diff --git a/contrib/compiler-rt/lib/sanitizer_common/tests/sanitizer_libc_test.cc b/contrib/compiler-rt/lib/sanitizer_common/tests/sanitizer_libc_test.cc deleted file mode 100644 index 3252db77653d..000000000000 --- a/contrib/compiler-rt/lib/sanitizer_common/tests/sanitizer_libc_test.cc +++ /dev/null @@ -1,159 +0,0 @@ -//===-- sanitizer_libc_test.cc --------------------------------------------===// -// -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. -// -//===----------------------------------------------------------------------===// -// Tests for sanitizer_libc.h. -//===----------------------------------------------------------------------===// - -#include "sanitizer_common/sanitizer_common.h" -#include "sanitizer_common/sanitizer_libc.h" -#include "sanitizer_common/sanitizer_platform.h" -#include "gtest/gtest.h" - -#if SANITIZER_LINUX || SANITIZER_MAC -# define SANITIZER_TEST_HAS_STAT_H 1 -# include <sys/stat.h> -# include "sanitizer_common/sanitizer_posix.h" -#else -# define SANITIZER_TEST_HAS_STAT_H 0 -#endif - -// A regression test for internal_memmove() implementation. -TEST(SanitizerCommon, InternalMemmoveRegression) { - char src[] = "Hello World"; - char *dest = src + 6; - __sanitizer::internal_memmove(dest, src, 5); - EXPECT_EQ(dest[0], src[0]); - EXPECT_EQ(dest[4], src[4]); -} - -TEST(SanitizerCommon, mem_is_zero) { - size_t size = 128; - char *x = new char[size]; - memset(x, 0, size); - for (size_t pos = 0; pos < size; pos++) { - x[pos] = 1; - for (size_t beg = 0; beg < size; beg++) { - for (size_t end = beg; end < size; end++) { - // fprintf(stderr, "pos %zd beg %zd end %zd \n", pos, beg, end); - if (beg <= pos && pos < end) - EXPECT_FALSE(__sanitizer::mem_is_zero(x + beg, end - beg)); - else - EXPECT_TRUE(__sanitizer::mem_is_zero(x + beg, end - beg)); - } - } - x[pos] = 0; - } - delete [] x; -} - -struct stat_and_more { - struct stat st; - unsigned char z; -}; - -static void temp_file_name(char *buf, size_t bufsize, const char *prefix) { - const char *tmpdir = "/tmp"; -#if SANITIZER_ANDROID - // I don't know a way to query temp directory location on Android without - // going through Java interfaces. The code below is not ideal, but should - // work. May require "adb root", but it is needed for almost any use of ASan - // on Android already. - tmpdir = GetEnv("EXTERNAL_STORAGE"); -#endif - u32 uid = GetUid(); - internal_snprintf(buf, bufsize, "%s/%s%d", tmpdir, prefix, uid); -} - -// FIXME: File manipulations are not yet supported on Windows -#if !defined(_WIN32) -TEST(SanitizerCommon, FileOps) { - const char *str1 = "qwerty"; - uptr len1 = internal_strlen(str1); - const char *str2 = "zxcv"; - uptr len2 = internal_strlen(str2); - - char tmpfile[128]; - temp_file_name(tmpfile, sizeof(tmpfile), "sanitizer_common.fileops.tmp."); - fd_t fd = OpenFile(tmpfile, WrOnly); - ASSERT_NE(fd, kInvalidFd); - EXPECT_EQ(len1, internal_write(fd, str1, len1)); - EXPECT_EQ(len2, internal_write(fd, str2, len2)); - CloseFile(fd); - - fd = OpenFile(tmpfile, RdOnly); - ASSERT_NE(fd, kInvalidFd); - uptr fsize = internal_filesize(fd); - EXPECT_EQ(len1 + len2, fsize); - -#if SANITIZER_TEST_HAS_STAT_H - struct stat st1, st2, st3; - EXPECT_EQ(0u, internal_stat(tmpfile, &st1)); - EXPECT_EQ(0u, internal_lstat(tmpfile, &st2)); - EXPECT_EQ(0u, internal_fstat(fd, &st3)); - EXPECT_EQ(fsize, (uptr)st3.st_size); - - // Verify that internal_fstat does not write beyond the end of the supplied - // buffer. - struct stat_and_more sam; - memset(&sam, 0xAB, sizeof(sam)); - EXPECT_EQ(0u, internal_fstat(fd, &sam.st)); - EXPECT_EQ(0xAB, sam.z); - EXPECT_NE(0xAB, sam.st.st_size); - EXPECT_NE(0, sam.st.st_size); -#endif - - char buf[64] = {}; - EXPECT_EQ(len1, internal_read(fd, buf, len1)); - EXPECT_EQ(0, internal_memcmp(buf, str1, len1)); - EXPECT_EQ((char)0, buf[len1 + 1]); - internal_memset(buf, 0, len1); - EXPECT_EQ(len2, internal_read(fd, buf, len2)); - EXPECT_EQ(0, internal_memcmp(buf, str2, len2)); - CloseFile(fd); - internal_unlink(tmpfile); -} -#endif - -TEST(SanitizerCommon, InternalStrFunctions) { - const char *haystack = "haystack"; - EXPECT_EQ(haystack + 2, internal_strchr(haystack, 'y')); - EXPECT_EQ(haystack + 2, internal_strchrnul(haystack, 'y')); - EXPECT_EQ(0, internal_strchr(haystack, 'z')); - EXPECT_EQ(haystack + 8, internal_strchrnul(haystack, 'z')); -} - -// FIXME: File manipulations are not yet supported on Windows -#if !defined(_WIN32) && !SANITIZER_MAC -TEST(SanitizerCommon, InternalMmapWithOffset) { - char tmpfile[128]; - temp_file_name(tmpfile, sizeof(tmpfile), - "sanitizer_common.internalmmapwithoffset.tmp."); - fd_t fd = OpenFile(tmpfile, RdWr); - ASSERT_NE(fd, kInvalidFd); - - uptr page_size = GetPageSizeCached(); - uptr res = internal_ftruncate(fd, page_size * 2); - ASSERT_FALSE(internal_iserror(res)); - - res = internal_lseek(fd, page_size, SEEK_SET); - ASSERT_FALSE(internal_iserror(res)); - - res = internal_write(fd, "AB", 2); - ASSERT_FALSE(internal_iserror(res)); - - char *p = (char *)MapWritableFileToMemory(nullptr, page_size, fd, page_size); - ASSERT_NE(nullptr, p); - - ASSERT_EQ('A', p[0]); - ASSERT_EQ('B', p[1]); - - CloseFile(fd); - UnmapOrDie(p, page_size); - internal_unlink(tmpfile); -} -#endif diff --git a/contrib/compiler-rt/lib/sanitizer_common/tests/sanitizer_linux_test.cc b/contrib/compiler-rt/lib/sanitizer_common/tests/sanitizer_linux_test.cc deleted file mode 100644 index 11342b775cc7..000000000000 --- a/contrib/compiler-rt/lib/sanitizer_common/tests/sanitizer_linux_test.cc +++ /dev/null @@ -1,268 +0,0 @@ -//===-- sanitizer_linux_test.cc -------------------------------------------===// -// -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. -// -//===----------------------------------------------------------------------===// -// -// Tests for sanitizer_linux.h -// -//===----------------------------------------------------------------------===// - -#include "sanitizer_common/sanitizer_platform.h" -#if SANITIZER_LINUX - -#include "sanitizer_common/sanitizer_linux.h" - -#include "sanitizer_common/sanitizer_common.h" -#include "gtest/gtest.h" - -#include <pthread.h> -#include <sched.h> -#include <stdlib.h> - -#include <algorithm> -#include <vector> - -namespace __sanitizer { - -struct TidReporterArgument { - TidReporterArgument() { - pthread_mutex_init(&terminate_thread_mutex, NULL); - pthread_mutex_init(&tid_reported_mutex, NULL); - pthread_cond_init(&terminate_thread_cond, NULL); - pthread_cond_init(&tid_reported_cond, NULL); - terminate_thread = false; - } - - ~TidReporterArgument() { - pthread_mutex_destroy(&terminate_thread_mutex); - pthread_mutex_destroy(&tid_reported_mutex); - pthread_cond_destroy(&terminate_thread_cond); - pthread_cond_destroy(&tid_reported_cond); - } - - pid_t reported_tid; - // For signaling to spawned threads that they should terminate. - pthread_cond_t terminate_thread_cond; - pthread_mutex_t terminate_thread_mutex; - bool terminate_thread; - // For signaling to main thread that a child thread has reported its tid. - pthread_cond_t tid_reported_cond; - pthread_mutex_t tid_reported_mutex; - - private: - // Disallow evil constructors - TidReporterArgument(const TidReporterArgument &); - void operator=(const TidReporterArgument &); -}; - -class ThreadListerTest : public ::testing::Test { - protected: - virtual void SetUp() { - pthread_t pthread_id; - pid_t tid; - for (uptr i = 0; i < kThreadCount; i++) { - SpawnTidReporter(&pthread_id, &tid); - pthread_ids_.push_back(pthread_id); - tids_.push_back(tid); - } - } - - virtual void TearDown() { - pthread_mutex_lock(&thread_arg.terminate_thread_mutex); - thread_arg.terminate_thread = true; - pthread_cond_broadcast(&thread_arg.terminate_thread_cond); - pthread_mutex_unlock(&thread_arg.terminate_thread_mutex); - for (uptr i = 0; i < pthread_ids_.size(); i++) - pthread_join(pthread_ids_[i], NULL); - } - - void SpawnTidReporter(pthread_t *pthread_id, pid_t *tid); - - static const uptr kThreadCount = 20; - - std::vector<pthread_t> pthread_ids_; - std::vector<pid_t> tids_; - - TidReporterArgument thread_arg; -}; - -// Writes its TID once to reported_tid and waits until signaled to terminate. -void *TidReporterThread(void *argument) { - TidReporterArgument *arg = reinterpret_cast<TidReporterArgument *>(argument); - pthread_mutex_lock(&arg->tid_reported_mutex); - arg->reported_tid = GetTid(); - pthread_cond_broadcast(&arg->tid_reported_cond); - pthread_mutex_unlock(&arg->tid_reported_mutex); - - pthread_mutex_lock(&arg->terminate_thread_mutex); - while (!arg->terminate_thread) - pthread_cond_wait(&arg->terminate_thread_cond, - &arg->terminate_thread_mutex); - pthread_mutex_unlock(&arg->terminate_thread_mutex); - return NULL; -} - -void ThreadListerTest::SpawnTidReporter(pthread_t *pthread_id, - pid_t *tid) { - pthread_mutex_lock(&thread_arg.tid_reported_mutex); - thread_arg.reported_tid = -1; - ASSERT_EQ(0, pthread_create(pthread_id, NULL, - TidReporterThread, - &thread_arg)); - while (thread_arg.reported_tid == -1) - pthread_cond_wait(&thread_arg.tid_reported_cond, - &thread_arg.tid_reported_mutex); - pthread_mutex_unlock(&thread_arg.tid_reported_mutex); - *tid = thread_arg.reported_tid; -} - -static std::vector<pid_t> ReadTidsToVector(ThreadLister *thread_lister) { - std::vector<pid_t> listed_tids; - pid_t tid; - while ((tid = thread_lister->GetNextTID()) >= 0) - listed_tids.push_back(tid); - EXPECT_FALSE(thread_lister->error()); - return listed_tids; -} - -static bool Includes(std::vector<pid_t> first, std::vector<pid_t> second) { - std::sort(first.begin(), first.end()); - std::sort(second.begin(), second.end()); - return std::includes(first.begin(), first.end(), - second.begin(), second.end()); -} - -static bool HasElement(std::vector<pid_t> vector, pid_t element) { - return std::find(vector.begin(), vector.end(), element) != vector.end(); -} - -// ThreadLister's output should include the current thread's TID and the TID of -// every thread we spawned. -TEST_F(ThreadListerTest, ThreadListerSeesAllSpawnedThreads) { - pid_t self_tid = GetTid(); - ThreadLister thread_lister(getpid()); - std::vector<pid_t> listed_tids = ReadTidsToVector(&thread_lister); - ASSERT_TRUE(HasElement(listed_tids, self_tid)); - ASSERT_TRUE(Includes(listed_tids, tids_)); -} - -// Calling Reset() should not cause ThreadLister to forget any threads it's -// supposed to know about. -TEST_F(ThreadListerTest, ResetDoesNotForgetThreads) { - ThreadLister thread_lister(getpid()); - - // Run the loop body twice, because Reset() might behave differently if called - // on a freshly created object. - for (uptr i = 0; i < 2; i++) { - thread_lister.Reset(); - std::vector<pid_t> listed_tids = ReadTidsToVector(&thread_lister); - ASSERT_TRUE(Includes(listed_tids, tids_)); - } -} - -// If new threads have spawned during ThreadLister object's lifetime, calling -// Reset() should cause ThreadLister to recognize their existence. -TEST_F(ThreadListerTest, ResetMakesNewThreadsKnown) { - ThreadLister thread_lister(getpid()); - std::vector<pid_t> threads_before_extra = ReadTidsToVector(&thread_lister); - - pthread_t extra_pthread_id; - pid_t extra_tid; - SpawnTidReporter(&extra_pthread_id, &extra_tid); - // Register the new thread so it gets terminated in TearDown(). - pthread_ids_.push_back(extra_pthread_id); - - // It would be very bizarre if the new TID had been listed before we even - // spawned that thread, but it would also cause a false success in this test, - // so better check for that. - ASSERT_FALSE(HasElement(threads_before_extra, extra_tid)); - - thread_lister.Reset(); - - std::vector<pid_t> threads_after_extra = ReadTidsToVector(&thread_lister); - ASSERT_TRUE(HasElement(threads_after_extra, extra_tid)); -} - -TEST(SanitizerCommon, SetEnvTest) { - const char kEnvName[] = "ENV_FOO"; - SetEnv(kEnvName, "value"); - EXPECT_STREQ("value", getenv(kEnvName)); - unsetenv(kEnvName); - EXPECT_EQ(0, getenv(kEnvName)); -} - -#if defined(__x86_64__) || defined(__i386__) -void *thread_self_offset_test_func(void *arg) { - bool result = - *(uptr *)((char *)ThreadSelf() + ThreadSelfOffset()) == ThreadSelf(); - return (void *)result; -} - -TEST(SanitizerLinux, ThreadSelfOffset) { - EXPECT_TRUE((bool)thread_self_offset_test_func(0)); - pthread_t tid; - void *result; - ASSERT_EQ(0, pthread_create(&tid, 0, thread_self_offset_test_func, 0)); - ASSERT_EQ(0, pthread_join(tid, &result)); - EXPECT_TRUE((bool)result); -} - -// libpthread puts the thread descriptor at the end of stack space. -void *thread_descriptor_size_test_func(void *arg) { - uptr descr_addr = ThreadSelf(); - pthread_attr_t attr; - pthread_getattr_np(pthread_self(), &attr); - void *stackaddr; - size_t stacksize; - pthread_attr_getstack(&attr, &stackaddr, &stacksize); - return (void *)((uptr)stackaddr + stacksize - descr_addr); -} - -TEST(SanitizerLinux, ThreadDescriptorSize) { - pthread_t tid; - void *result; - ASSERT_EQ(0, pthread_create(&tid, 0, thread_descriptor_size_test_func, 0)); - ASSERT_EQ(0, pthread_join(tid, &result)); - EXPECT_EQ((uptr)result, ThreadDescriptorSize()); -} -#endif - -TEST(SanitizerCommon, LibraryNameIs) { - EXPECT_FALSE(LibraryNameIs("", "")); - - char full_name[256]; - const char *paths[] = { "", "/", "/path/to/" }; - const char *suffixes[] = { "", "-linux", ".1.2", "-linux.1.2" }; - const char *base_names[] = { "lib", "lib.0", "lib-i386" }; - const char *wrong_names[] = { "", "lib.9", "lib-x86_64" }; - for (uptr i = 0; i < ARRAY_SIZE(paths); i++) - for (uptr j = 0; j < ARRAY_SIZE(suffixes); j++) { - for (uptr k = 0; k < ARRAY_SIZE(base_names); k++) { - internal_snprintf(full_name, ARRAY_SIZE(full_name), "%s%s%s.so", - paths[i], base_names[k], suffixes[j]); - EXPECT_TRUE(LibraryNameIs(full_name, base_names[k])) - << "Full name " << full_name - << " doesn't match base name " << base_names[k]; - for (uptr m = 0; m < ARRAY_SIZE(wrong_names); m++) - EXPECT_FALSE(LibraryNameIs(full_name, wrong_names[m])) - << "Full name " << full_name - << " matches base name " << wrong_names[m]; - } - } -} - -#if defined(__mips64) -// Effectively, this is a test for ThreadDescriptorSize() which is used to -// compute ThreadSelf(). -TEST(SanitizerLinux, ThreadSelfTest) { - ASSERT_EQ(pthread_self(), ThreadSelf()); -} -#endif - -} // namespace __sanitizer - -#endif // SANITIZER_LINUX diff --git a/contrib/compiler-rt/lib/sanitizer_common/tests/sanitizer_list_test.cc b/contrib/compiler-rt/lib/sanitizer_common/tests/sanitizer_list_test.cc deleted file mode 100644 index fbe53c0375c0..000000000000 --- a/contrib/compiler-rt/lib/sanitizer_common/tests/sanitizer_list_test.cc +++ /dev/null @@ -1,173 +0,0 @@ -//===-- sanitizer_list_test.cc --------------------------------------------===// -// -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. -// -//===----------------------------------------------------------------------===// -// -// This file is a part of ThreadSanitizer/AddressSanitizer runtime. -// -//===----------------------------------------------------------------------===// -#include "sanitizer_common/sanitizer_list.h" -#include "gtest/gtest.h" - -namespace __sanitizer { - -struct ListItem { - ListItem *next; -}; - -typedef IntrusiveList<ListItem> List; - -static List static_list; - -static void SetList(List *l, ListItem *x = 0, - ListItem *y = 0, ListItem *z = 0) { - l->clear(); - if (x) l->push_back(x); - if (y) l->push_back(y); - if (z) l->push_back(z); -} - -static void CheckList(List *l, ListItem *i1, ListItem *i2 = 0, ListItem *i3 = 0, - ListItem *i4 = 0, ListItem *i5 = 0, ListItem *i6 = 0) { - if (i1) { - CHECK_EQ(l->front(), i1); - l->pop_front(); - } - if (i2) { - CHECK_EQ(l->front(), i2); - l->pop_front(); - } - if (i3) { - CHECK_EQ(l->front(), i3); - l->pop_front(); - } - if (i4) { - CHECK_EQ(l->front(), i4); - l->pop_front(); - } - if (i5) { - CHECK_EQ(l->front(), i5); - l->pop_front(); - } - if (i6) { - CHECK_EQ(l->front(), i6); - l->pop_front(); - } - CHECK(l->empty()); -} - -TEST(SanitizerCommon, IntrusiveList) { - ListItem items[6]; - CHECK_EQ(static_list.size(), 0); - - List l; - l.clear(); - - ListItem *x = &items[0]; - ListItem *y = &items[1]; - ListItem *z = &items[2]; - ListItem *a = &items[3]; - ListItem *b = &items[4]; - ListItem *c = &items[5]; - - CHECK_EQ(l.size(), 0); - l.push_back(x); - CHECK_EQ(l.size(), 1); - CHECK_EQ(l.back(), x); - CHECK_EQ(l.front(), x); - l.pop_front(); - CHECK(l.empty()); - l.CheckConsistency(); - - l.push_front(x); - CHECK_EQ(l.size(), 1); - CHECK_EQ(l.back(), x); - CHECK_EQ(l.front(), x); - l.pop_front(); - CHECK(l.empty()); - l.CheckConsistency(); - - l.push_front(x); - l.push_front(y); - l.push_front(z); - CHECK_EQ(l.size(), 3); - CHECK_EQ(l.front(), z); - CHECK_EQ(l.back(), x); - l.CheckConsistency(); - - l.pop_front(); - CHECK_EQ(l.size(), 2); - CHECK_EQ(l.front(), y); - CHECK_EQ(l.back(), x); - l.pop_front(); - l.pop_front(); - CHECK(l.empty()); - l.CheckConsistency(); - - l.push_back(x); - l.push_back(y); - l.push_back(z); - CHECK_EQ(l.size(), 3); - CHECK_EQ(l.front(), x); - CHECK_EQ(l.back(), z); - l.CheckConsistency(); - - l.pop_front(); - CHECK_EQ(l.size(), 2); - CHECK_EQ(l.front(), y); - CHECK_EQ(l.back(), z); - l.pop_front(); - l.pop_front(); - CHECK(l.empty()); - l.CheckConsistency(); - - List l1, l2; - l1.clear(); - l2.clear(); - - l1.append_front(&l2); - CHECK(l1.empty()); - CHECK(l2.empty()); - - l1.append_back(&l2); - CHECK(l1.empty()); - CHECK(l2.empty()); - - SetList(&l1, x); - CheckList(&l1, x); - - SetList(&l1, x, y, z); - SetList(&l2, a, b, c); - l1.append_back(&l2); - CheckList(&l1, x, y, z, a, b, c); - CHECK(l2.empty()); - - SetList(&l1, x, y); - SetList(&l2); - l1.append_front(&l2); - CheckList(&l1, x, y); - CHECK(l2.empty()); -} - -TEST(SanitizerCommon, IntrusiveListAppendEmpty) { - ListItem i; - List l; - l.clear(); - l.push_back(&i); - List l2; - l2.clear(); - l.append_back(&l2); - CHECK_EQ(l.back(), &i); - CHECK_EQ(l.front(), &i); - CHECK_EQ(l.size(), 1); - l.append_front(&l2); - CHECK_EQ(l.back(), &i); - CHECK_EQ(l.front(), &i); - CHECK_EQ(l.size(), 1); -} - -} // namespace __sanitizer diff --git a/contrib/compiler-rt/lib/sanitizer_common/tests/sanitizer_mutex_test.cc b/contrib/compiler-rt/lib/sanitizer_common/tests/sanitizer_mutex_test.cc deleted file mode 100644 index d14e7c2fba21..000000000000 --- a/contrib/compiler-rt/lib/sanitizer_common/tests/sanitizer_mutex_test.cc +++ /dev/null @@ -1,137 +0,0 @@ -//===-- sanitizer_mutex_test.cc -------------------------------------------===// -// -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. -// -//===----------------------------------------------------------------------===// -// -// This file is a part of ThreadSanitizer/AddressSanitizer runtime. -// -//===----------------------------------------------------------------------===// -#include "sanitizer_common/sanitizer_mutex.h" -#include "sanitizer_common/sanitizer_common.h" - -#include "sanitizer_pthread_wrappers.h" - -#include "gtest/gtest.h" - -#include <string.h> - -namespace __sanitizer { - -template<typename MutexType> -class TestData { - public: - explicit TestData(MutexType *mtx) - : mtx_(mtx) { - for (int i = 0; i < kSize; i++) - data_[i] = 0; - } - - void Write() { - Lock l(mtx_); - T v0 = data_[0]; - for (int i = 0; i < kSize; i++) { - CHECK_EQ(data_[i], v0); - data_[i]++; - } - } - - void TryWrite() { - if (!mtx_->TryLock()) - return; - T v0 = data_[0]; - for (int i = 0; i < kSize; i++) { - CHECK_EQ(data_[i], v0); - data_[i]++; - } - mtx_->Unlock(); - } - - void Backoff() { - volatile T data[kSize] = {}; - for (int i = 0; i < kSize; i++) { - data[i]++; - CHECK_EQ(data[i], 1); - } - } - - private: - typedef GenericScopedLock<MutexType> Lock; - static const int kSize = 64; - typedef u64 T; - MutexType *mtx_; - char pad_[kCacheLineSize]; - T data_[kSize]; -}; - -const int kThreads = 8; -#if SANITIZER_DEBUG -const int kIters = 16*1024; -#else -const int kIters = 64*1024; -#endif - -template<typename MutexType> -static void *lock_thread(void *param) { - TestData<MutexType> *data = (TestData<MutexType>*)param; - for (int i = 0; i < kIters; i++) { - data->Write(); - data->Backoff(); - } - return 0; -} - -template<typename MutexType> -static void *try_thread(void *param) { - TestData<MutexType> *data = (TestData<MutexType>*)param; - for (int i = 0; i < kIters; i++) { - data->TryWrite(); - data->Backoff(); - } - return 0; -} - -template<typename MutexType> -static void check_locked(MutexType *mtx) { - GenericScopedLock<MutexType> l(mtx); - mtx->CheckLocked(); -} - -TEST(SanitizerCommon, SpinMutex) { - SpinMutex mtx; - mtx.Init(); - TestData<SpinMutex> data(&mtx); - pthread_t threads[kThreads]; - for (int i = 0; i < kThreads; i++) - PTHREAD_CREATE(&threads[i], 0, lock_thread<SpinMutex>, &data); - for (int i = 0; i < kThreads; i++) - PTHREAD_JOIN(threads[i], 0); -} - -TEST(SanitizerCommon, SpinMutexTry) { - SpinMutex mtx; - mtx.Init(); - TestData<SpinMutex> data(&mtx); - pthread_t threads[kThreads]; - for (int i = 0; i < kThreads; i++) - PTHREAD_CREATE(&threads[i], 0, try_thread<SpinMutex>, &data); - for (int i = 0; i < kThreads; i++) - PTHREAD_JOIN(threads[i], 0); -} - -TEST(SanitizerCommon, BlockingMutex) { - u64 mtxmem[1024] = {}; - BlockingMutex *mtx = new(mtxmem) BlockingMutex(LINKER_INITIALIZED); - TestData<BlockingMutex> data(mtx); - pthread_t threads[kThreads]; - for (int i = 0; i < kThreads; i++) - PTHREAD_CREATE(&threads[i], 0, lock_thread<BlockingMutex>, &data); - for (int i = 0; i < kThreads; i++) - PTHREAD_JOIN(threads[i], 0); - check_locked(mtx); -} - -} // namespace __sanitizer diff --git a/contrib/compiler-rt/lib/sanitizer_common/tests/sanitizer_nolibc_test.cc b/contrib/compiler-rt/lib/sanitizer_common/tests/sanitizer_nolibc_test.cc deleted file mode 100644 index d0d5a5e13898..000000000000 --- a/contrib/compiler-rt/lib/sanitizer_common/tests/sanitizer_nolibc_test.cc +++ /dev/null @@ -1,31 +0,0 @@ -//===-- sanitizer_nolibc_test.cc ------------------------------------------===// -// -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. -// -//===----------------------------------------------------------------------===// -// -// This file is a part of ThreadSanitizer/AddressSanitizer runtime. -// Tests for libc independence of sanitizer_common. -// -//===----------------------------------------------------------------------===// - -#include "sanitizer_common/sanitizer_platform.h" - -#include "gtest/gtest.h" - -#include <stdlib.h> - -extern const char *argv0; - -#if SANITIZER_LINUX && defined(__x86_64__) -TEST(SanitizerCommon, NolibcMain) { - std::string NolibcTestPath = argv0; - NolibcTestPath += "-Nolibc"; - int status = system(NolibcTestPath.c_str()); - EXPECT_EQ(true, WIFEXITED(status)); - EXPECT_EQ(0, WEXITSTATUS(status)); -} -#endif diff --git a/contrib/compiler-rt/lib/sanitizer_common/tests/sanitizer_nolibc_test_main.cc b/contrib/compiler-rt/lib/sanitizer_common/tests/sanitizer_nolibc_test_main.cc deleted file mode 100644 index 72df621d07ff..000000000000 --- a/contrib/compiler-rt/lib/sanitizer_common/tests/sanitizer_nolibc_test_main.cc +++ /dev/null @@ -1,19 +0,0 @@ -//===-- sanitizer_nolibc_test_main.cc -------------------------------------===// -// -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. -// -//===----------------------------------------------------------------------===// -// -// This file is a part of ThreadSanitizer/AddressSanitizer runtime. -// Tests for libc independence of sanitizer_common. -// -//===----------------------------------------------------------------------===// - -#include "sanitizer_common/sanitizer_libc.h" - -extern "C" void _start() { - internal__exit(0); -} diff --git a/contrib/compiler-rt/lib/sanitizer_common/tests/sanitizer_posix_test.cc b/contrib/compiler-rt/lib/sanitizer_common/tests/sanitizer_posix_test.cc deleted file mode 100644 index 03ca449d3622..000000000000 --- a/contrib/compiler-rt/lib/sanitizer_common/tests/sanitizer_posix_test.cc +++ /dev/null @@ -1,80 +0,0 @@ -//===-- sanitizer_posix_test.cc -------------------------------------------===// -// -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. -// -//===----------------------------------------------------------------------===// -// -// Tests for POSIX-specific code. -// -//===----------------------------------------------------------------------===// - -#include "sanitizer_common/sanitizer_platform.h" -#if SANITIZER_POSIX - -#include "sanitizer_common/sanitizer_common.h" -#include "gtest/gtest.h" - -#include <pthread.h> -#include <sys/mman.h> - -namespace __sanitizer { - -static pthread_key_t key; -static bool destructor_executed; - -extern "C" -void destructor(void *arg) { - uptr iter = reinterpret_cast<uptr>(arg); - if (iter > 1) { - ASSERT_EQ(0, pthread_setspecific(key, reinterpret_cast<void *>(iter - 1))); - return; - } - destructor_executed = true; -} - -extern "C" -void *thread_func(void *arg) { - return reinterpret_cast<void*>(pthread_setspecific(key, arg)); -} - -static void SpawnThread(uptr iteration) { - destructor_executed = false; - pthread_t tid; - ASSERT_EQ(0, pthread_create(&tid, 0, &thread_func, - reinterpret_cast<void *>(iteration))); - void *retval; - ASSERT_EQ(0, pthread_join(tid, &retval)); - ASSERT_EQ(0, retval); -} - -TEST(SanitizerCommon, PthreadDestructorIterations) { - ASSERT_EQ(0, pthread_key_create(&key, &destructor)); - SpawnThread(GetPthreadDestructorIterations()); - EXPECT_TRUE(destructor_executed); - SpawnThread(GetPthreadDestructorIterations() + 1); - EXPECT_FALSE(destructor_executed); -} - -TEST(SanitizerCommon, IsAccessibleMemoryRange) { - const int page_size = GetPageSize(); - uptr mem = (uptr)mmap(0, 3 * page_size, PROT_READ | PROT_WRITE, - MAP_PRIVATE | MAP_ANON, -1, 0); - // Protect the middle page. - mprotect((void *)(mem + page_size), page_size, PROT_NONE); - EXPECT_TRUE(IsAccessibleMemoryRange(mem, page_size - 1)); - EXPECT_TRUE(IsAccessibleMemoryRange(mem, page_size)); - EXPECT_FALSE(IsAccessibleMemoryRange(mem, page_size + 1)); - EXPECT_TRUE(IsAccessibleMemoryRange(mem + page_size - 1, 1)); - EXPECT_FALSE(IsAccessibleMemoryRange(mem + page_size - 1, 2)); - EXPECT_FALSE(IsAccessibleMemoryRange(mem + 2 * page_size - 1, 1)); - EXPECT_TRUE(IsAccessibleMemoryRange(mem + 2 * page_size, page_size)); - EXPECT_FALSE(IsAccessibleMemoryRange(mem, 3 * page_size)); - EXPECT_FALSE(IsAccessibleMemoryRange(0x0, 2)); -} - -} // namespace __sanitizer - -#endif // SANITIZER_POSIX diff --git a/contrib/compiler-rt/lib/sanitizer_common/tests/sanitizer_printf_test.cc b/contrib/compiler-rt/lib/sanitizer_common/tests/sanitizer_printf_test.cc deleted file mode 100644 index 5e39e0a591d6..000000000000 --- a/contrib/compiler-rt/lib/sanitizer_common/tests/sanitizer_printf_test.cc +++ /dev/null @@ -1,153 +0,0 @@ -//===-- sanitizer_printf_test.cc ------------------------------------------===// -// -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. -// -//===----------------------------------------------------------------------===// -// -// Tests for sanitizer_printf.cc -// -//===----------------------------------------------------------------------===// -#include "sanitizer_common/sanitizer_common.h" -#include "sanitizer_common/sanitizer_libc.h" -#include "gtest/gtest.h" - -#include <string.h> -#include <limits.h> - -namespace __sanitizer { - -TEST(Printf, Basic) { - char buf[1024]; - uptr len = internal_snprintf(buf, sizeof(buf), - "a%db%zdc%ue%zuf%xh%zxq%pe%sr", - (int)-1, (long)-2, // NOLINT - (unsigned)-4, (unsigned long)5, // NOLINT - (unsigned)10, (unsigned long)11, // NOLINT - (void*)0x123, "_string_"); - EXPECT_EQ(len, strlen(buf)); - - std::string expectedString = "a-1b-2c4294967292e5fahbq0x"; - expectedString += std::string(SANITIZER_POINTER_FORMAT_LENGTH - 3, '0'); - expectedString += "123e_string_r"; - EXPECT_STREQ(expectedString.c_str(), buf); -} - -TEST(Printf, OverflowStr) { - char buf[] = "123456789"; - uptr len = internal_snprintf(buf, 4, "%s", "abcdef"); // NOLINT - EXPECT_EQ(len, (uptr)6); - EXPECT_STREQ("abc", buf); - EXPECT_EQ(buf[3], 0); - EXPECT_EQ(buf[4], '5'); - EXPECT_EQ(buf[5], '6'); - EXPECT_EQ(buf[6], '7'); - EXPECT_EQ(buf[7], '8'); - EXPECT_EQ(buf[8], '9'); - EXPECT_EQ(buf[9], 0); -} - -TEST(Printf, OverflowInt) { - char buf[] = "123456789"; - internal_snprintf(buf, 4, "%d", -123456789); // NOLINT - EXPECT_STREQ("-12", buf); - EXPECT_EQ(buf[3], 0); - EXPECT_EQ(buf[4], '5'); - EXPECT_EQ(buf[5], '6'); - EXPECT_EQ(buf[6], '7'); - EXPECT_EQ(buf[7], '8'); - EXPECT_EQ(buf[8], '9'); - EXPECT_EQ(buf[9], 0); -} - -TEST(Printf, OverflowUint) { - char buf[] = "123456789"; - uptr val; - if (sizeof(val) == 4) { - val = (uptr)0x12345678; - } else { - val = (uptr)0x123456789ULL; - } - internal_snprintf(buf, 4, "a%zx", val); // NOLINT - EXPECT_STREQ("a12", buf); - EXPECT_EQ(buf[3], 0); - EXPECT_EQ(buf[4], '5'); - EXPECT_EQ(buf[5], '6'); - EXPECT_EQ(buf[6], '7'); - EXPECT_EQ(buf[7], '8'); - EXPECT_EQ(buf[8], '9'); - EXPECT_EQ(buf[9], 0); -} - -TEST(Printf, OverflowPtr) { - char buf[] = "123456789"; - void *p; - if (sizeof(p) == 4) { - p = (void*)0x1234567; - } else { - p = (void*)0x123456789ULL; - } - internal_snprintf(buf, 4, "%p", p); // NOLINT - EXPECT_STREQ("0x0", buf); - EXPECT_EQ(buf[3], 0); - EXPECT_EQ(buf[4], '5'); - EXPECT_EQ(buf[5], '6'); - EXPECT_EQ(buf[6], '7'); - EXPECT_EQ(buf[7], '8'); - EXPECT_EQ(buf[8], '9'); - EXPECT_EQ(buf[9], 0); -} - -#if defined(_WIN32) -// Oh well, MSVS headers don't define snprintf. -# define snprintf _snprintf -#endif - -template<typename T> -static void TestAgainstLibc(const char *fmt, T arg1, T arg2) { - char buf[1024]; - uptr len = internal_snprintf(buf, sizeof(buf), fmt, arg1, arg2); - char buf2[1024]; - snprintf(buf2, sizeof(buf2), fmt, arg1, arg2); - EXPECT_EQ(len, strlen(buf)); - EXPECT_STREQ(buf2, buf); -} - -TEST(Printf, MinMax) { - TestAgainstLibc<int>("%d-%d", INT_MIN, INT_MAX); // NOLINT - TestAgainstLibc<unsigned>("%u-%u", 0, UINT_MAX); // NOLINT - TestAgainstLibc<unsigned>("%x-%x", 0, UINT_MAX); // NOLINT -#if !defined(_WIN32) - // %z* format doesn't seem to be supported by MSVS. - TestAgainstLibc<long>("%zd-%zd", LONG_MIN, LONG_MAX); // NOLINT - TestAgainstLibc<unsigned long>("%zu-%zu", 0, ULONG_MAX); // NOLINT - TestAgainstLibc<unsigned long>("%zx-%zx", 0, ULONG_MAX); // NOLINT -#endif -} - -TEST(Printf, Padding) { - TestAgainstLibc<int>("%3d - %3d", 1, 0); - TestAgainstLibc<int>("%3d - %3d", -1, 123); - TestAgainstLibc<int>("%3d - %3d", -1, -123); - TestAgainstLibc<int>("%3d - %3d", 12, 1234); - TestAgainstLibc<int>("%3d - %3d", -12, -1234); - TestAgainstLibc<int>("%03d - %03d", 1, 0); - TestAgainstLibc<int>("%03d - %03d", -1, 123); - TestAgainstLibc<int>("%03d - %03d", -1, -123); - TestAgainstLibc<int>("%03d - %03d", 12, 1234); - TestAgainstLibc<int>("%03d - %03d", -12, -1234); -} - -TEST(Printf, Precision) { - char buf[1024]; - uptr len = internal_snprintf(buf, sizeof(buf), "%.*s", 3, "12345"); - EXPECT_EQ(3U, len); - EXPECT_STREQ("123", buf); - len = internal_snprintf(buf, sizeof(buf), "%.*s", 6, "12345"); - EXPECT_EQ(5U, len); - EXPECT_STREQ("12345", buf); -} - -} // namespace __sanitizer diff --git a/contrib/compiler-rt/lib/sanitizer_common/tests/sanitizer_procmaps_test.cc b/contrib/compiler-rt/lib/sanitizer_common/tests/sanitizer_procmaps_test.cc deleted file mode 100644 index 12bc9e18193a..000000000000 --- a/contrib/compiler-rt/lib/sanitizer_common/tests/sanitizer_procmaps_test.cc +++ /dev/null @@ -1,56 +0,0 @@ -//===-- sanitizer_procmaps_test.cc ----------------------------------------===// -// -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. -// -//===----------------------------------------------------------------------===// -// -// This file is a part of ThreadSanitizer/AddressSanitizer runtime. -// -//===----------------------------------------------------------------------===// -#if !defined(_WIN32) // There are no /proc/maps on Windows. - -#include "sanitizer_common/sanitizer_procmaps.h" -#include "gtest/gtest.h" - -#include <stdlib.h> - -static void noop() {} -extern const char *argv0; - -namespace __sanitizer { - -#if SANITIZER_LINUX && !SANITIZER_ANDROID -TEST(MemoryMappingLayout, CodeRange) { - uptr start, end; - bool res = GetCodeRangeForFile("[vdso]", &start, &end); - EXPECT_EQ(res, true); - EXPECT_GT(start, 0U); - EXPECT_LT(start, end); -} -#endif - -TEST(MemoryMappingLayout, DumpListOfModules) { - const char *last_slash = strrchr(argv0, '/'); - const char *binary_name = last_slash ? last_slash + 1 : argv0; - MemoryMappingLayout memory_mapping(false); - const uptr kMaxModules = 100; - LoadedModule modules[kMaxModules]; - uptr n_modules = memory_mapping.DumpListOfModules(modules, kMaxModules, 0); - EXPECT_GT(n_modules, 0U); - bool found = false; - for (uptr i = 0; i < n_modules; ++i) { - if (modules[i].containsAddress((uptr)&noop)) { - // Verify that the module name is sane. - if (strstr(modules[i].full_name(), binary_name) != 0) - found = true; - } - modules[i].clear(); - } - EXPECT_TRUE(found); -} - -} // namespace __sanitizer -#endif // !defined(_WIN32) diff --git a/contrib/compiler-rt/lib/sanitizer_common/tests/sanitizer_pthread_wrappers.h b/contrib/compiler-rt/lib/sanitizer_common/tests/sanitizer_pthread_wrappers.h deleted file mode 100644 index 47b0f97de840..000000000000 --- a/contrib/compiler-rt/lib/sanitizer_common/tests/sanitizer_pthread_wrappers.h +++ /dev/null @@ -1,66 +0,0 @@ -//===-- sanitizer_pthread_wrappers.h ----------------------------*- C++ -*-===// -// -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. -// -//===----------------------------------------------------------------------===// -// -// This file is a part of *Sanitizer runtime. -// It provides handy wrappers for thread manipulation, that: -// a) assert on any failure rather than returning an error code -// b) defines pthread-like interface on platforms where where <pthread.h> -// is not supplied by default. -// -//===----------------------------------------------------------------------===// - -#ifndef SANITIZER_PTHREAD_WRAPPERS_H -#define SANITIZER_PTHREAD_WRAPPERS_H - -#include "sanitizer_test_utils.h" - -#if !defined(_WIN32) -# include <pthread.h> -// Simply forward the arguments and check that the pthread functions succeed. -# define PTHREAD_CREATE(a, b, c, d) ASSERT_EQ(0, pthread_create(a, b, c, d)) -# define PTHREAD_JOIN(a, b) ASSERT_EQ(0, pthread_join(a, b)) -#else -typedef HANDLE pthread_t; - -struct PthreadHelperCreateThreadInfo { - void *(*start_routine)(void *); - void *arg; -}; - -inline DWORD WINAPI PthreadHelperThreadProc(void *arg) { - PthreadHelperCreateThreadInfo *start_data = - reinterpret_cast<PthreadHelperCreateThreadInfo*>(arg); - void *ret = (start_data->start_routine)(start_data->arg); - delete start_data; - return (DWORD)ret; -} - -inline void PTHREAD_CREATE(pthread_t *thread, void *attr, - void *(*start_routine)(void *), void *arg) { - ASSERT_EQ(0, attr) << "Thread attributes are not supported yet."; - PthreadHelperCreateThreadInfo *data = new PthreadHelperCreateThreadInfo; - data->start_routine = start_routine; - data->arg = arg; - *thread = CreateThread(0, 0, PthreadHelperThreadProc, data, 0, 0); - ASSERT_NE(nullptr, *thread) << "Failed to create a thread."; -} - -inline void PTHREAD_JOIN(pthread_t thread, void **value_ptr) { - ASSERT_EQ(0, value_ptr) << "Nonzero value_ptr is not supported yet."; - ASSERT_EQ(WAIT_OBJECT_0, WaitForSingleObject(thread, INFINITE)); - ASSERT_NE(0, CloseHandle(thread)); -} - -inline void pthread_exit(void *retval) { - ASSERT_EQ(0, retval) << "Nonzero retval is not supported yet."; - ExitThread((DWORD)retval); -} -#endif // _WIN32 - -#endif // SANITIZER_PTHREAD_WRAPPERS_H diff --git a/contrib/compiler-rt/lib/sanitizer_common/tests/sanitizer_stackdepot_test.cc b/contrib/compiler-rt/lib/sanitizer_common/tests/sanitizer_stackdepot_test.cc deleted file mode 100644 index 513432fac214..000000000000 --- a/contrib/compiler-rt/lib/sanitizer_common/tests/sanitizer_stackdepot_test.cc +++ /dev/null @@ -1,93 +0,0 @@ -//===-- sanitizer_stackdepot_test.cc --------------------------------------===// -// -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. -// -//===----------------------------------------------------------------------===// -// -// This file is a part of ThreadSanitizer/AddressSanitizer runtime. -// -//===----------------------------------------------------------------------===// -#include "sanitizer_common/sanitizer_stackdepot.h" -#include "sanitizer_common/sanitizer_internal_defs.h" -#include "sanitizer_common/sanitizer_libc.h" -#include "gtest/gtest.h" - -namespace __sanitizer { - -TEST(SanitizerCommon, StackDepotBasic) { - uptr array[] = {1, 2, 3, 4, 5}; - StackTrace s1(array, ARRAY_SIZE(array)); - u32 i1 = StackDepotPut(s1); - StackTrace stack = StackDepotGet(i1); - EXPECT_NE(stack.trace, (uptr*)0); - EXPECT_EQ(ARRAY_SIZE(array), stack.size); - EXPECT_EQ(0, internal_memcmp(stack.trace, array, sizeof(array))); -} - -TEST(SanitizerCommon, StackDepotAbsent) { - StackTrace stack = StackDepotGet((1 << 30) - 1); - EXPECT_EQ((uptr*)0, stack.trace); -} - -TEST(SanitizerCommon, StackDepotEmptyStack) { - u32 i1 = StackDepotPut(StackTrace()); - StackTrace stack = StackDepotGet(i1); - EXPECT_EQ((uptr*)0, stack.trace); -} - -TEST(SanitizerCommon, StackDepotZeroId) { - StackTrace stack = StackDepotGet(0); - EXPECT_EQ((uptr*)0, stack.trace); -} - -TEST(SanitizerCommon, StackDepotSame) { - uptr array[] = {1, 2, 3, 4, 6}; - StackTrace s1(array, ARRAY_SIZE(array)); - u32 i1 = StackDepotPut(s1); - u32 i2 = StackDepotPut(s1); - EXPECT_EQ(i1, i2); - StackTrace stack = StackDepotGet(i1); - EXPECT_NE(stack.trace, (uptr*)0); - EXPECT_EQ(ARRAY_SIZE(array), stack.size); - EXPECT_EQ(0, internal_memcmp(stack.trace, array, sizeof(array))); -} - -TEST(SanitizerCommon, StackDepotSeveral) { - uptr array1[] = {1, 2, 3, 4, 7}; - StackTrace s1(array1, ARRAY_SIZE(array1)); - u32 i1 = StackDepotPut(s1); - uptr array2[] = {1, 2, 3, 4, 8, 9}; - StackTrace s2(array2, ARRAY_SIZE(array2)); - u32 i2 = StackDepotPut(s2); - EXPECT_NE(i1, i2); -} - -TEST(SanitizerCommon, StackDepotReverseMap) { - uptr array1[] = {1, 2, 3, 4, 5}; - uptr array2[] = {7, 1, 3, 0}; - uptr array3[] = {10, 2, 5, 3}; - uptr array4[] = {1, 3, 2, 5}; - u32 ids[4] = {0}; - StackTrace s1(array1, ARRAY_SIZE(array1)); - StackTrace s2(array2, ARRAY_SIZE(array2)); - StackTrace s3(array3, ARRAY_SIZE(array3)); - StackTrace s4(array4, ARRAY_SIZE(array4)); - ids[0] = StackDepotPut(s1); - ids[1] = StackDepotPut(s2); - ids[2] = StackDepotPut(s3); - ids[3] = StackDepotPut(s4); - - StackDepotReverseMap map; - - for (uptr i = 0; i < 4; i++) { - StackTrace stack = StackDepotGet(ids[i]); - StackTrace from_map = map.Get(ids[i]); - EXPECT_EQ(stack.size, from_map.size); - EXPECT_EQ(stack.trace, from_map.trace); - } -} - -} // namespace __sanitizer diff --git a/contrib/compiler-rt/lib/sanitizer_common/tests/sanitizer_stacktrace_printer_test.cc b/contrib/compiler-rt/lib/sanitizer_common/tests/sanitizer_stacktrace_printer_test.cc deleted file mode 100644 index 05796fcbff77..000000000000 --- a/contrib/compiler-rt/lib/sanitizer_common/tests/sanitizer_stacktrace_printer_test.cc +++ /dev/null @@ -1,152 +0,0 @@ -//===-- sanitizer_common_printer_test.cc ----------------------------------===// -// -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. -// -//===----------------------------------------------------------------------===// -// -// This file is a part of sanitizer_common test suite. -// -//===----------------------------------------------------------------------===// -#include "sanitizer_common/sanitizer_stacktrace_printer.h" - -#include "gtest/gtest.h" - -namespace __sanitizer { - -TEST(SanitizerStacktracePrinter, RenderSourceLocation) { - InternalScopedString str(128); - RenderSourceLocation(&str, "/dir/file.cc", 10, 5, false, ""); - EXPECT_STREQ("/dir/file.cc:10:5", str.data()); - - str.clear(); - RenderSourceLocation(&str, "/dir/file.cc", 11, 0, false, ""); - EXPECT_STREQ("/dir/file.cc:11", str.data()); - - str.clear(); - RenderSourceLocation(&str, "/dir/file.cc", 0, 0, false, ""); - EXPECT_STREQ("/dir/file.cc", str.data()); - - str.clear(); - RenderSourceLocation(&str, "/dir/file.cc", 10, 5, false, "/dir/"); - EXPECT_STREQ("file.cc:10:5", str.data()); - - str.clear(); - RenderSourceLocation(&str, "/dir/file.cc", 10, 5, true, ""); - EXPECT_STREQ("/dir/file.cc(10,5)", str.data()); - - str.clear(); - RenderSourceLocation(&str, "/dir/file.cc", 11, 0, true, ""); - EXPECT_STREQ("/dir/file.cc(11)", str.data()); - - str.clear(); - RenderSourceLocation(&str, "/dir/file.cc", 0, 0, true, ""); - EXPECT_STREQ("/dir/file.cc", str.data()); - - str.clear(); - RenderSourceLocation(&str, "/dir/file.cc", 10, 5, true, "/dir/"); - EXPECT_STREQ("file.cc(10,5)", str.data()); -} - -TEST(SanitizerStacktracePrinter, RenderModuleLocation) { - InternalScopedString str(128); - RenderModuleLocation(&str, "/dir/exe", 0x123, ""); - EXPECT_STREQ("(/dir/exe+0x123)", str.data()); - - // Check that we strip file prefix if necessary. - str.clear(); - RenderModuleLocation(&str, "/dir/exe", 0x123, "/dir/"); - EXPECT_STREQ("(exe+0x123)", str.data()); -} - -TEST(SanitizerStacktracePrinter, RenderFrame) { - int frame_no = 42; - AddressInfo info; - info.address = 0x400000; - info.module = internal_strdup("/path/to/my/module"); - info.module_offset = 0x200; - info.function = internal_strdup("function_foo"); - info.function_offset = 0x100; - info.file = internal_strdup("/path/to/my/source"); - info.line = 10; - info.column = 5; - InternalScopedString str(256); - - // Dump all the AddressInfo fields. - RenderFrame(&str, "%% Frame:%n PC:%p Module:%m ModuleOffset:%o " - "Function:%f FunctionOffset:%q Source:%s Line:%l " - "Column:%c", - frame_no, info, false, "/path/to/", "function_"); - EXPECT_STREQ("% Frame:42 PC:0x400000 Module:my/module ModuleOffset:0x200 " - "Function:foo FunctionOffset:0x100 Source:my/source Line:10 " - "Column:5", - str.data()); - info.Clear(); - str.clear(); - - // Test special format specifiers. - info.address = 0x400000; - RenderFrame(&str, "%M", frame_no, info, false); - EXPECT_NE(nullptr, internal_strstr(str.data(), "400000")); - str.clear(); - - RenderFrame(&str, "%L", frame_no, info, false); - EXPECT_STREQ("(<unknown module>)", str.data()); - str.clear(); - - info.module = internal_strdup("/path/to/module"); - info.module_offset = 0x200; - RenderFrame(&str, "%M", frame_no, info, false); - EXPECT_NE(nullptr, internal_strstr(str.data(), "(module+0x")); - EXPECT_NE(nullptr, internal_strstr(str.data(), "200")); - str.clear(); - - RenderFrame(&str, "%L", frame_no, info, false); - EXPECT_STREQ("(/path/to/module+0x200)", str.data()); - str.clear(); - - info.function = internal_strdup("my_function"); - RenderFrame(&str, "%F", frame_no, info, false); - EXPECT_STREQ("in my_function", str.data()); - str.clear(); - - info.function_offset = 0x100; - RenderFrame(&str, "%F %S", frame_no, info, false); - EXPECT_STREQ("in my_function+0x100 <null>", str.data()); - str.clear(); - - info.file = internal_strdup("my_file"); - RenderFrame(&str, "%F %S", frame_no, info, false); - EXPECT_STREQ("in my_function my_file", str.data()); - str.clear(); - - info.line = 10; - RenderFrame(&str, "%F %S", frame_no, info, false); - EXPECT_STREQ("in my_function my_file:10", str.data()); - str.clear(); - - info.column = 5; - RenderFrame(&str, "%S %L", frame_no, info, false); - EXPECT_STREQ("my_file:10:5 my_file:10:5", str.data()); - str.clear(); - - RenderFrame(&str, "%S %L", frame_no, info, true); - EXPECT_STREQ("my_file(10,5) my_file(10,5)", str.data()); - str.clear(); - - info.column = 0; - RenderFrame(&str, "%F %S", frame_no, info, true); - EXPECT_STREQ("in my_function my_file(10)", str.data()); - str.clear(); - - info.line = 0; - RenderFrame(&str, "%F %S", frame_no, info, true); - EXPECT_STREQ("in my_function my_file", str.data()); - str.clear(); - - info.Clear(); -} - -} // namespace __sanitizer diff --git a/contrib/compiler-rt/lib/sanitizer_common/tests/sanitizer_stacktrace_test.cc b/contrib/compiler-rt/lib/sanitizer_common/tests/sanitizer_stacktrace_test.cc deleted file mode 100644 index 654ea1db82fb..000000000000 --- a/contrib/compiler-rt/lib/sanitizer_common/tests/sanitizer_stacktrace_test.cc +++ /dev/null @@ -1,154 +0,0 @@ -//===-- sanitizer_stacktrace_test.cc --------------------------------------===// -// -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. -// -//===----------------------------------------------------------------------===// -// -// This file is a part of ThreadSanitizer/AddressSanitizer runtime. -// -//===----------------------------------------------------------------------===// - -#include "sanitizer_common/sanitizer_common.h" -#include "sanitizer_common/sanitizer_stacktrace.h" -#include "gtest/gtest.h" - -namespace __sanitizer { - -class FastUnwindTest : public ::testing::Test { - protected: - virtual void SetUp(); - virtual void TearDown(); - bool TryFastUnwind(uptr max_depth) { - if (!StackTrace::WillUseFastUnwind(true)) - return false; - trace.Unwind(max_depth, start_pc, (uptr)&fake_stack[0], 0, fake_top, - fake_bottom, true); - return true; - } - - void *mapping; - uhwptr *fake_stack; - const uptr fake_stack_size = 10; - uhwptr start_pc; - uhwptr fake_top; - uhwptr fake_bottom; - BufferedStackTrace trace; -}; - -static uptr PC(uptr idx) { - return (1<<20) + idx; -} - -void FastUnwindTest::SetUp() { - size_t ps = GetPageSize(); - mapping = MmapOrDie(2 * ps, "FastUnwindTest"); - MprotectNoAccess((uptr)mapping, ps); - - // Unwinder may peek 1 word down from the starting FP. - fake_stack = (uhwptr *)((uptr)mapping + ps + sizeof(uhwptr)); - - // Fill an array of pointers with fake fp+retaddr pairs. Frame pointers have - // even indices. - for (uptr i = 0; i + 1 < fake_stack_size; i += 2) { - fake_stack[i] = (uptr)&fake_stack[i+2]; // fp - fake_stack[i+1] = PC(i + 1); // retaddr - } - // Mark the last fp point back up to terminate the stack trace. - fake_stack[RoundDownTo(fake_stack_size - 1, 2)] = (uhwptr)&fake_stack[0]; - - // Top is two slots past the end because FastUnwindStack subtracts two. - fake_top = (uhwptr)&fake_stack[fake_stack_size + 2]; - // Bottom is one slot before the start because FastUnwindStack uses >. - fake_bottom = (uhwptr)mapping; - start_pc = PC(0); -} - -void FastUnwindTest::TearDown() { - size_t ps = GetPageSize(); - UnmapOrDie(mapping, 2 * ps); -} - -TEST_F(FastUnwindTest, Basic) { - if (!TryFastUnwind(kStackTraceMax)) - return; - // Should get all on-stack retaddrs and start_pc. - EXPECT_EQ(6U, trace.size); - EXPECT_EQ(start_pc, trace.trace[0]); - for (uptr i = 1; i <= 5; i++) { - EXPECT_EQ(PC(i*2 - 1), trace.trace[i]); - } -} - -// From: http://code.google.com/p/address-sanitizer/issues/detail?id=162 -TEST_F(FastUnwindTest, FramePointerLoop) { - // Make one fp point to itself. - fake_stack[4] = (uhwptr)&fake_stack[4]; - if (!TryFastUnwind(kStackTraceMax)) - return; - // Should get all on-stack retaddrs up to the 4th slot and start_pc. - EXPECT_EQ(4U, trace.size); - EXPECT_EQ(start_pc, trace.trace[0]); - for (uptr i = 1; i <= 3; i++) { - EXPECT_EQ(PC(i*2 - 1), trace.trace[i]); - } -} - -TEST_F(FastUnwindTest, MisalignedFramePointer) { - // Make one fp misaligned. - fake_stack[4] += 3; - if (!TryFastUnwind(kStackTraceMax)) - return; - // Should get all on-stack retaddrs up to the 4th slot and start_pc. - EXPECT_EQ(4U, trace.size); - EXPECT_EQ(start_pc, trace.trace[0]); - for (uptr i = 1; i < 4U; i++) { - EXPECT_EQ(PC(i*2 - 1), trace.trace[i]); - } -} - -TEST_F(FastUnwindTest, OneFrameStackTrace) { - if (!TryFastUnwind(1)) - return; - EXPECT_EQ(1U, trace.size); - EXPECT_EQ(start_pc, trace.trace[0]); - EXPECT_EQ((uhwptr)&fake_stack[0], trace.top_frame_bp); -} - -TEST_F(FastUnwindTest, ZeroFramesStackTrace) { - if (!TryFastUnwind(0)) - return; - EXPECT_EQ(0U, trace.size); - EXPECT_EQ(0U, trace.top_frame_bp); -} - -TEST_F(FastUnwindTest, FPBelowPrevFP) { - // The next FP points to unreadable memory inside the stack limits, but below - // current FP. - fake_stack[0] = (uhwptr)&fake_stack[-50]; - fake_stack[1] = PC(1); - if (!TryFastUnwind(3)) - return; - EXPECT_EQ(2U, trace.size); - EXPECT_EQ(PC(0), trace.trace[0]); - EXPECT_EQ(PC(1), trace.trace[1]); -} - -TEST(SlowUnwindTest, ShortStackTrace) { - if (StackTrace::WillUseFastUnwind(false)) - return; - BufferedStackTrace stack; - uptr pc = StackTrace::GetCurrentPc(); - uptr bp = GET_CURRENT_FRAME(); - stack.Unwind(0, pc, bp, 0, 0, 0, false); - EXPECT_EQ(0U, stack.size); - EXPECT_EQ(0U, stack.top_frame_bp); - stack.Unwind(1, pc, bp, 0, 0, 0, false); - EXPECT_EQ(1U, stack.size); - EXPECT_EQ(pc, stack.trace[0]); - EXPECT_EQ(bp, stack.top_frame_bp); -} - -} // namespace __sanitizer diff --git a/contrib/compiler-rt/lib/sanitizer_common/tests/sanitizer_stoptheworld_test.cc b/contrib/compiler-rt/lib/sanitizer_common/tests/sanitizer_stoptheworld_test.cc deleted file mode 100644 index 802af392c609..000000000000 --- a/contrib/compiler-rt/lib/sanitizer_common/tests/sanitizer_stoptheworld_test.cc +++ /dev/null @@ -1,204 +0,0 @@ -//===-- sanitizer_stoptheworld_test.cc ------------------------------------===// -// -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. -// -//===----------------------------------------------------------------------===// -// -// Tests for sanitizer_stoptheworld.h -// -//===----------------------------------------------------------------------===// - -#include "sanitizer_common/sanitizer_platform.h" -#if SANITIZER_LINUX && defined(__x86_64__) - -#include "sanitizer_common/sanitizer_stoptheworld.h" -#include "gtest/gtest.h" - -#include "sanitizer_common/sanitizer_libc.h" -#include "sanitizer_common/sanitizer_common.h" - -#include <pthread.h> -#include <sched.h> - -namespace __sanitizer { - -static pthread_mutex_t incrementer_thread_exit_mutex; - -struct CallbackArgument { - volatile int counter; - volatile bool threads_stopped; - volatile bool callback_executed; - CallbackArgument() - : counter(0), - threads_stopped(false), - callback_executed(false) {} -}; - -void *IncrementerThread(void *argument) { - CallbackArgument *callback_argument = (CallbackArgument *)argument; - while (true) { - __sync_fetch_and_add(&callback_argument->counter, 1); - if (pthread_mutex_trylock(&incrementer_thread_exit_mutex) == 0) { - pthread_mutex_unlock(&incrementer_thread_exit_mutex); - return NULL; - } else { - sched_yield(); - } - } -} - -// This callback checks that IncrementerThread is suspended at the time of its -// execution. -void Callback(const SuspendedThreadsList &suspended_threads_list, - void *argument) { - CallbackArgument *callback_argument = (CallbackArgument *)argument; - callback_argument->callback_executed = true; - int counter_at_init = __sync_fetch_and_add(&callback_argument->counter, 0); - for (uptr i = 0; i < 1000; i++) { - sched_yield(); - if (__sync_fetch_and_add(&callback_argument->counter, 0) != - counter_at_init) { - callback_argument->threads_stopped = false; - return; - } - } - callback_argument->threads_stopped = true; -} - -TEST(StopTheWorld, SuspendThreadsSimple) { - pthread_mutex_init(&incrementer_thread_exit_mutex, NULL); - CallbackArgument argument; - pthread_t thread_id; - int pthread_create_result; - pthread_mutex_lock(&incrementer_thread_exit_mutex); - pthread_create_result = pthread_create(&thread_id, NULL, IncrementerThread, - &argument); - ASSERT_EQ(0, pthread_create_result); - StopTheWorld(&Callback, &argument); - pthread_mutex_unlock(&incrementer_thread_exit_mutex); - EXPECT_TRUE(argument.callback_executed); - EXPECT_TRUE(argument.threads_stopped); - // argument is on stack, so we have to wait for the incrementer thread to - // terminate before we can return from this function. - ASSERT_EQ(0, pthread_join(thread_id, NULL)); - pthread_mutex_destroy(&incrementer_thread_exit_mutex); -} - -// A more comprehensive test where we spawn a bunch of threads while executing -// StopTheWorld in parallel. -static const uptr kThreadCount = 50; -static const uptr kStopWorldAfter = 10; // let this many threads spawn first - -static pthread_mutex_t advanced_incrementer_thread_exit_mutex; - -struct AdvancedCallbackArgument { - volatile uptr thread_index; - volatile int counters[kThreadCount]; - pthread_t thread_ids[kThreadCount]; - volatile bool threads_stopped; - volatile bool callback_executed; - volatile bool fatal_error; - AdvancedCallbackArgument() - : thread_index(0), - threads_stopped(false), - callback_executed(false), - fatal_error(false) {} -}; - -void *AdvancedIncrementerThread(void *argument) { - AdvancedCallbackArgument *callback_argument = - (AdvancedCallbackArgument *)argument; - uptr this_thread_index = __sync_fetch_and_add( - &callback_argument->thread_index, 1); - // Spawn the next thread. - int pthread_create_result; - if (this_thread_index + 1 < kThreadCount) { - pthread_create_result = - pthread_create(&callback_argument->thread_ids[this_thread_index + 1], - NULL, AdvancedIncrementerThread, argument); - // Cannot use ASSERT_EQ in non-void-returning functions. If there's a - // problem, defer failing to the main thread. - if (pthread_create_result != 0) { - callback_argument->fatal_error = true; - __sync_fetch_and_add(&callback_argument->thread_index, - kThreadCount - callback_argument->thread_index); - } - } - // Do the actual work. - while (true) { - __sync_fetch_and_add(&callback_argument->counters[this_thread_index], 1); - if (pthread_mutex_trylock(&advanced_incrementer_thread_exit_mutex) == 0) { - pthread_mutex_unlock(&advanced_incrementer_thread_exit_mutex); - return NULL; - } else { - sched_yield(); - } - } -} - -void AdvancedCallback(const SuspendedThreadsList &suspended_threads_list, - void *argument) { - AdvancedCallbackArgument *callback_argument = - (AdvancedCallbackArgument *)argument; - callback_argument->callback_executed = true; - - int counters_at_init[kThreadCount]; - for (uptr j = 0; j < kThreadCount; j++) - counters_at_init[j] = __sync_fetch_and_add(&callback_argument->counters[j], - 0); - for (uptr i = 0; i < 10; i++) { - sched_yield(); - for (uptr j = 0; j < kThreadCount; j++) - if (__sync_fetch_and_add(&callback_argument->counters[j], 0) != - counters_at_init[j]) { - callback_argument->threads_stopped = false; - return; - } - } - callback_argument->threads_stopped = true; -} - -TEST(StopTheWorld, SuspendThreadsAdvanced) { - pthread_mutex_init(&advanced_incrementer_thread_exit_mutex, NULL); - AdvancedCallbackArgument argument; - - pthread_mutex_lock(&advanced_incrementer_thread_exit_mutex); - int pthread_create_result; - pthread_create_result = pthread_create(&argument.thread_ids[0], NULL, - AdvancedIncrementerThread, - &argument); - ASSERT_EQ(0, pthread_create_result); - // Wait for several threads to spawn before proceeding. - while (__sync_fetch_and_add(&argument.thread_index, 0) < kStopWorldAfter) - sched_yield(); - StopTheWorld(&AdvancedCallback, &argument); - EXPECT_TRUE(argument.callback_executed); - EXPECT_TRUE(argument.threads_stopped); - - // Wait for all threads to spawn before we start terminating them. - while (__sync_fetch_and_add(&argument.thread_index, 0) < kThreadCount) - sched_yield(); - ASSERT_FALSE(argument.fatal_error); // a pthread_create has failed - // Signal the threads to terminate. - pthread_mutex_unlock(&advanced_incrementer_thread_exit_mutex); - for (uptr i = 0; i < kThreadCount; i++) - ASSERT_EQ(0, pthread_join(argument.thread_ids[i], NULL)); - pthread_mutex_destroy(&advanced_incrementer_thread_exit_mutex); -} - -static void SegvCallback(const SuspendedThreadsList &suspended_threads_list, - void *argument) { - *(volatile int*)0x1234 = 0; -} - -TEST(StopTheWorld, SegvInCallback) { - // Test that tracer thread catches SIGSEGV. - StopTheWorld(&SegvCallback, NULL); -} - -} // namespace __sanitizer - -#endif // SANITIZER_LINUX && defined(__x86_64__) diff --git a/contrib/compiler-rt/lib/sanitizer_common/tests/sanitizer_stoptheworld_testlib.cc b/contrib/compiler-rt/lib/sanitizer_common/tests/sanitizer_stoptheworld_testlib.cc deleted file mode 100644 index d8be2afb19e9..000000000000 --- a/contrib/compiler-rt/lib/sanitizer_common/tests/sanitizer_stoptheworld_testlib.cc +++ /dev/null @@ -1,53 +0,0 @@ -//===-- sanitizer_stoptheworld_testlib.cc ---------------------------------===// -// -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. -// -//===----------------------------------------------------------------------===// -// Dynamic library to test StopTheWorld functionality. -// When loaded with LD_PRELOAD, it will periodically suspend all threads. -//===----------------------------------------------------------------------===// -/* Usage: -clang++ -fno-exceptions -g -fPIC -I. \ - sanitizer_common/tests/sanitizer_stoptheworld_testlib.cc \ - sanitizer_common/sanitizer_*.cc -shared -lpthread -o teststoptheworld.so -LD_PRELOAD=`pwd`/teststoptheworld.so /your/app -*/ - -#include "sanitizer_common/sanitizer_platform.h" -#if SANITIZER_LINUX - -#include <dlfcn.h> -#include <stddef.h> -#include <stdio.h> -#include <pthread.h> -#include <unistd.h> - -#include "sanitizer_common/sanitizer_stoptheworld.h" - -namespace { -const uptr kSuspendDuration = 3; -const uptr kRunDuration = 3; - -void Callback(const SuspendedThreadsList &suspended_threads_list, - void *argument) { - sleep(kSuspendDuration); -} - -void *SuspenderThread(void *argument) { - while (true) { - sleep(kRunDuration); - StopTheWorld(Callback, NULL); - } - return NULL; -} - -__attribute__((constructor)) void StopTheWorldTestLibConstructor(void) { - pthread_t thread_id; - pthread_create(&thread_id, NULL, SuspenderThread, NULL); -} -} // namespace - -#endif // SANITIZER_LINUX diff --git a/contrib/compiler-rt/lib/sanitizer_common/tests/sanitizer_suppressions_test.cc b/contrib/compiler-rt/lib/sanitizer_common/tests/sanitizer_suppressions_test.cc deleted file mode 100644 index e8c30d07e78c..000000000000 --- a/contrib/compiler-rt/lib/sanitizer_common/tests/sanitizer_suppressions_test.cc +++ /dev/null @@ -1,134 +0,0 @@ -//===-- sanitizer_suppressions_test.cc ------------------------------------===// -// -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. -// -//===----------------------------------------------------------------------===// -// -// This file is a part of ThreadSanitizer/AddressSanitizer runtime. -// -//===----------------------------------------------------------------------===// -#include "sanitizer_common/sanitizer_suppressions.h" -#include "gtest/gtest.h" - -#include <string.h> - -namespace __sanitizer { - -static bool MyMatch(const char *templ, const char *func) { - char tmp[1024]; - strcpy(tmp, templ); // NOLINT - return TemplateMatch(tmp, func); -} - -TEST(Suppressions, Match) { - EXPECT_TRUE(MyMatch("foobar$", "foobar")); - - EXPECT_TRUE(MyMatch("foobar", "foobar")); - EXPECT_TRUE(MyMatch("*foobar*", "foobar")); - EXPECT_TRUE(MyMatch("foobar", "prefix_foobar_postfix")); - EXPECT_TRUE(MyMatch("*foobar*", "prefix_foobar_postfix")); - EXPECT_TRUE(MyMatch("foo*bar", "foo_middle_bar")); - EXPECT_TRUE(MyMatch("foo*bar", "foobar")); - EXPECT_TRUE(MyMatch("foo*bar*baz", "foo_middle_bar_another_baz")); - EXPECT_TRUE(MyMatch("foo*bar*baz", "foo_middle_barbaz")); - EXPECT_TRUE(MyMatch("^foobar", "foobar")); - EXPECT_TRUE(MyMatch("^foobar", "foobar_postfix")); - EXPECT_TRUE(MyMatch("^*foobar", "foobar")); - EXPECT_TRUE(MyMatch("^*foobar", "prefix_foobar")); - EXPECT_TRUE(MyMatch("foobar$", "foobar")); - EXPECT_TRUE(MyMatch("foobar$", "prefix_foobar")); - EXPECT_TRUE(MyMatch("*foobar*$", "foobar")); - EXPECT_TRUE(MyMatch("*foobar*$", "foobar_postfix")); - EXPECT_TRUE(MyMatch("^foobar$", "foobar")); - - EXPECT_FALSE(MyMatch("foo", "baz")); - EXPECT_FALSE(MyMatch("foobarbaz", "foobar")); - EXPECT_FALSE(MyMatch("foobarbaz", "barbaz")); - EXPECT_FALSE(MyMatch("foo*bar", "foobaz")); - EXPECT_FALSE(MyMatch("foo*bar", "foo_baz")); - EXPECT_FALSE(MyMatch("^foobar", "prefix_foobar")); - EXPECT_FALSE(MyMatch("foobar$", "foobar_postfix")); - EXPECT_FALSE(MyMatch("^foobar$", "prefix_foobar")); - EXPECT_FALSE(MyMatch("^foobar$", "foobar_postfix")); - EXPECT_FALSE(MyMatch("foo^bar", "foobar")); - EXPECT_FALSE(MyMatch("foo$bar", "foobar")); - EXPECT_FALSE(MyMatch("foo$^bar", "foobar")); -} - -static const char *kTestSuppressionTypes[] = {"race", "thread", "mutex", - "signal"}; - -class SuppressionContextTest : public ::testing::Test { - public: - SuppressionContextTest() - : ctx_(kTestSuppressionTypes, ARRAY_SIZE(kTestSuppressionTypes)) {} - - protected: - SuppressionContext ctx_; - - void CheckSuppressions(unsigned count, std::vector<const char *> types, - std::vector<const char *> templs) const { - EXPECT_EQ(count, ctx_.SuppressionCount()); - for (unsigned i = 0; i < count; i++) { - const Suppression *s = ctx_.SuppressionAt(i); - EXPECT_STREQ(types[i], s->type); - EXPECT_STREQ(templs[i], s->templ); - } - } -}; - -TEST_F(SuppressionContextTest, Parse) { - ctx_.Parse("race:foo\n" - " race:bar\n" // NOLINT - "race:baz \n" // NOLINT - "# a comment\n" - "race:quz\n"); // NOLINT - CheckSuppressions(4, {"race", "race", "race", "race"}, - {"foo", "bar", "baz", "quz"}); -} - -TEST_F(SuppressionContextTest, Parse2) { - ctx_.Parse( - " # first line comment\n" // NOLINT - " race:bar \n" // NOLINT - "race:baz* *baz\n" - "# a comment\n" - "# last line comment\n" - ); // NOLINT - CheckSuppressions(2, {"race", "race"}, {"bar", "baz* *baz"}); -} - -TEST_F(SuppressionContextTest, Parse3) { - ctx_.Parse( - "# last suppression w/o line-feed\n" - "race:foo\n" - "race:bar" - ); // NOLINT - CheckSuppressions(2, {"race", "race"}, {"foo", "bar"}); -} - -TEST_F(SuppressionContextTest, ParseType) { - ctx_.Parse( - "race:foo\n" - "thread:bar\n" - "mutex:baz\n" - "signal:quz\n" - ); // NOLINT - CheckSuppressions(4, {"race", "thread", "mutex", "signal"}, - {"foo", "bar", "baz", "quz"}); -} - -TEST_F(SuppressionContextTest, HasSuppressionType) { - ctx_.Parse( - "race:foo\n" - "thread:bar\n"); - EXPECT_TRUE(ctx_.HasSuppressionType("race")); - EXPECT_TRUE(ctx_.HasSuppressionType("thread")); - EXPECT_FALSE(ctx_.HasSuppressionType("mutex")); - EXPECT_FALSE(ctx_.HasSuppressionType("signal")); -} - -} // namespace __sanitizer diff --git a/contrib/compiler-rt/lib/sanitizer_common/tests/sanitizer_symbolizer_test.cc b/contrib/compiler-rt/lib/sanitizer_common/tests/sanitizer_symbolizer_test.cc deleted file mode 100644 index 429ac591e502..000000000000 --- a/contrib/compiler-rt/lib/sanitizer_common/tests/sanitizer_symbolizer_test.cc +++ /dev/null @@ -1,58 +0,0 @@ -//===-- sanitizer_symbolizer_test.cc --------------------------------------===// -// -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. -// -//===----------------------------------------------------------------------===// -// -// Tests for sanitizer_symbolizer.h and sanitizer_symbolizer_internal.h -// -//===----------------------------------------------------------------------===// - -#include "sanitizer_common/sanitizer_allocator_internal.h" -#include "sanitizer_common/sanitizer_symbolizer_internal.h" -#include "gtest/gtest.h" - -namespace __sanitizer { - -TEST(Symbolizer, ExtractToken) { - char *token; - const char *rest; - - rest = ExtractToken("a;b;c", ";", &token); - EXPECT_STREQ("a", token); - EXPECT_STREQ("b;c", rest); - InternalFree(token); - - rest = ExtractToken("aaa-bbb.ccc", ";.-*", &token); - EXPECT_STREQ("aaa", token); - EXPECT_STREQ("bbb.ccc", rest); - InternalFree(token); -} - -TEST(Symbolizer, ExtractInt) { - int token; - const char *rest = ExtractInt("123,456;789", ";,", &token); - EXPECT_EQ(123, token); - EXPECT_STREQ("456;789", rest); -} - -TEST(Symbolizer, ExtractUptr) { - uptr token; - const char *rest = ExtractUptr("123,456;789", ";,", &token); - EXPECT_EQ(123U, token); - EXPECT_STREQ("456;789", rest); -} - -TEST(Symbolizer, ExtractTokenUpToDelimiter) { - char *token; - const char *rest = - ExtractTokenUpToDelimiter("aaa-+-bbb-+-ccc", "-+-", &token); - EXPECT_STREQ("aaa", token); - EXPECT_STREQ("bbb-+-ccc", rest); - InternalFree(token); -} - -} // namespace __sanitizer diff --git a/contrib/compiler-rt/lib/sanitizer_common/tests/sanitizer_test_config.h b/contrib/compiler-rt/lib/sanitizer_common/tests/sanitizer_test_config.h deleted file mode 100644 index bdf614606d6a..000000000000 --- a/contrib/compiler-rt/lib/sanitizer_common/tests/sanitizer_test_config.h +++ /dev/null @@ -1,30 +0,0 @@ -//===-- sanitizer_test_config.h ---------------------------------*- C++ -*-===// -// -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. -// -//===----------------------------------------------------------------------===// -// -// This file is a part of *Sanitizer runtime. -// -//===----------------------------------------------------------------------===// -#if !defined(INCLUDED_FROM_SANITIZER_TEST_UTILS_H) -# error "This file should be included into sanitizer_test_utils.h only" -#endif - -#ifndef SANITIZER_TEST_CONFIG_H -#define SANITIZER_TEST_CONFIG_H - -#include <vector> -#include <string> -#include <map> - -#if SANITIZER_USE_DEJAGNU_GTEST -# include "dejagnu-gtest.h" -#else -# include "gtest/gtest.h" -#endif - -#endif // SANITIZER_TEST_CONFIG_H diff --git a/contrib/compiler-rt/lib/sanitizer_common/tests/sanitizer_test_main.cc b/contrib/compiler-rt/lib/sanitizer_common/tests/sanitizer_test_main.cc deleted file mode 100644 index b7fd3dafab26..000000000000 --- a/contrib/compiler-rt/lib/sanitizer_common/tests/sanitizer_test_main.cc +++ /dev/null @@ -1,22 +0,0 @@ -//===-- sanitizer_test_main.cc --------------------------------------------===// -// -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. -// -//===----------------------------------------------------------------------===// -// -// This file is a part of ThreadSanitizer/AddressSanitizer runtime. -// -//===----------------------------------------------------------------------===// -#include "gtest/gtest.h" - -const char *argv0; - -int main(int argc, char **argv) { - argv0 = argv[0]; - testing::GTEST_FLAG(death_test_style) = "threadsafe"; - testing::InitGoogleTest(&argc, argv); - return RUN_ALL_TESTS(); -} diff --git a/contrib/compiler-rt/lib/sanitizer_common/tests/sanitizer_test_utils.h b/contrib/compiler-rt/lib/sanitizer_common/tests/sanitizer_test_utils.h deleted file mode 100644 index 9c162a66f547..000000000000 --- a/contrib/compiler-rt/lib/sanitizer_common/tests/sanitizer_test_utils.h +++ /dev/null @@ -1,127 +0,0 @@ -//===-- sanitizer_test_utils.h ----------------------------------*- C++ -*-===// -// -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. -// -//===----------------------------------------------------------------------===// -// -// This file is a part of *Sanitizer runtime. -// Common unit tests utilities. -// -//===----------------------------------------------------------------------===// - -#ifndef SANITIZER_TEST_UTILS_H -#define SANITIZER_TEST_UTILS_H - -#if defined(_WIN32) -// <windows.h> should always be the first include on Windows. -# include <windows.h> -// MSVS headers define max/min as macros, so std::max/min gets crazy. -# undef max -# undef min -#endif - -#if !defined(SANITIZER_EXTERNAL_TEST_CONFIG) -# define INCLUDED_FROM_SANITIZER_TEST_UTILS_H -# include "sanitizer_test_config.h" -# undef INCLUDED_FROM_SANITIZER_TEST_UTILS_H -#endif - -#include <stdint.h> - -#if defined(_MSC_VER) -# define NOINLINE __declspec(noinline) -#else // defined(_MSC_VER) -# define NOINLINE __attribute__((noinline)) -#endif // defined(_MSC_VER) - -#if !defined(_MSC_VER) || defined(__clang__) -# define UNUSED __attribute__((unused)) -# define USED __attribute__((used)) -#else -# define UNUSED -# define USED -#endif - -#if !defined(__has_feature) -#define __has_feature(x) 0 -#endif - -#ifndef ATTRIBUTE_NO_SANITIZE_ADDRESS -# if __has_feature(address_sanitizer) || defined(__SANITIZE_ADDRESS__) -# define ATTRIBUTE_NO_SANITIZE_ADDRESS \ - __attribute__((no_sanitize_address)) -# else -# define ATTRIBUTE_NO_SANITIZE_ADDRESS -# endif -#endif // ATTRIBUTE_NO_SANITIZE_ADDRESS - -#if __LP64__ || defined(_WIN64) -# define SANITIZER_WORDSIZE 64 -#else -# define SANITIZER_WORDSIZE 32 -#endif - -// Make the compiler thinks that something is going on there. -inline void break_optimization(void *arg) { -#if !defined(_WIN32) || defined(__clang__) - __asm__ __volatile__("" : : "r" (arg) : "memory"); -#endif -} - -// This function returns its parameter but in such a way that compiler -// can not prove it. -template<class T> -NOINLINE -static T Ident(T t) { - T ret = t; - break_optimization(&ret); - return ret; -} - -// Simple stand-alone pseudorandom number generator. -// Current algorithm is ANSI C linear congruential PRNG. -static inline uint32_t my_rand_r(uint32_t* state) { - return (*state = *state * 1103515245 + 12345) >> 16; -} - -static uint32_t global_seed = 0; - -static inline uint32_t my_rand() { - return my_rand_r(&global_seed); -} - -// Set availability of platform-specific functions. - -#if !defined(__APPLE__) && !defined(__ANDROID__) && !defined(_WIN32) -# define SANITIZER_TEST_HAS_POSIX_MEMALIGN 1 -#else -# define SANITIZER_TEST_HAS_POSIX_MEMALIGN 0 -#endif - -#if !defined(__APPLE__) && !defined(__FreeBSD__) && \ - !defined(__ANDROID__) && !defined(_WIN32) -# define SANITIZER_TEST_HAS_MEMALIGN 1 -# define SANITIZER_TEST_HAS_PVALLOC 1 -# define SANITIZER_TEST_HAS_MALLOC_USABLE_SIZE 1 -#else -# define SANITIZER_TEST_HAS_MEMALIGN 0 -# define SANITIZER_TEST_HAS_PVALLOC 0 -# define SANITIZER_TEST_HAS_MALLOC_USABLE_SIZE 0 -#endif - -#if !defined(__APPLE__) -# define SANITIZER_TEST_HAS_STRNLEN 1 -#else -# define SANITIZER_TEST_HAS_STRNLEN 0 -#endif - -#if defined(__FreeBSD__) -# define SANITIZER_TEST_HAS_PRINTF_L 1 -#else -# define SANITIZER_TEST_HAS_PRINTF_L 0 -#endif - -#endif // SANITIZER_TEST_UTILS_H diff --git a/contrib/compiler-rt/lib/sanitizer_common/tests/sanitizer_thread_registry_test.cc b/contrib/compiler-rt/lib/sanitizer_common/tests/sanitizer_thread_registry_test.cc deleted file mode 100644 index 58c627a704ff..000000000000 --- a/contrib/compiler-rt/lib/sanitizer_common/tests/sanitizer_thread_registry_test.cc +++ /dev/null @@ -1,232 +0,0 @@ -//===-- sanitizer_thread_registry_test.cc ---------------------------------===// -// -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. -// -//===----------------------------------------------------------------------===// -// -// This file is a part of shared sanitizer runtime. -// -//===----------------------------------------------------------------------===// -#include "sanitizer_common/sanitizer_thread_registry.h" - -#include "sanitizer_pthread_wrappers.h" - -#include "gtest/gtest.h" - -#include <vector> - -namespace __sanitizer { - -static BlockingMutex tctx_allocator_lock(LINKER_INITIALIZED); -static LowLevelAllocator tctx_allocator; - -template<typename TCTX> -static ThreadContextBase *GetThreadContext(u32 tid) { - BlockingMutexLock l(&tctx_allocator_lock); - return new(tctx_allocator) TCTX(tid); -} - -static const u32 kMaxRegistryThreads = 1000; -static const u32 kRegistryQuarantine = 2; - -static void CheckThreadQuantity(ThreadRegistry *registry, uptr exp_total, - uptr exp_running, uptr exp_alive) { - uptr total, running, alive; - registry->GetNumberOfThreads(&total, &running, &alive); - EXPECT_EQ(exp_total, total); - EXPECT_EQ(exp_running, running); - EXPECT_EQ(exp_alive, alive); -} - -static bool is_detached(u32 tid) { - return (tid % 2 == 0); -} - -static uptr get_uid(u32 tid) { - return tid * 2; -} - -static bool HasName(ThreadContextBase *tctx, void *arg) { - char *name = (char*)arg; - return (0 == internal_strcmp(tctx->name, name)); -} - -static bool HasUid(ThreadContextBase *tctx, void *arg) { - uptr uid = (uptr)arg; - return (tctx->user_id == uid); -} - -static void MarkUidAsPresent(ThreadContextBase *tctx, void *arg) { - bool *arr = (bool*)arg; - arr[tctx->tid] = true; -} - -static void TestRegistry(ThreadRegistry *registry, bool has_quarantine) { - // Create and start a main thread. - EXPECT_EQ(0U, registry->CreateThread(get_uid(0), true, -1, 0)); - registry->StartThread(0, 0, 0); - // Create a bunch of threads. - for (u32 i = 1; i <= 10; i++) { - EXPECT_EQ(i, registry->CreateThread(get_uid(i), is_detached(i), 0, 0)); - } - CheckThreadQuantity(registry, 11, 1, 11); - // Start some of them. - for (u32 i = 1; i <= 5; i++) { - registry->StartThread(i, 0, 0); - } - CheckThreadQuantity(registry, 11, 6, 11); - // Finish, create and start more threads. - for (u32 i = 1; i <= 5; i++) { - registry->FinishThread(i); - if (!is_detached(i)) - registry->JoinThread(i, 0); - } - for (u32 i = 6; i <= 10; i++) { - registry->StartThread(i, 0, 0); - } - std::vector<u32> new_tids; - for (u32 i = 11; i <= 15; i++) { - new_tids.push_back( - registry->CreateThread(get_uid(i), is_detached(i), 0, 0)); - } - ASSERT_LE(kRegistryQuarantine, 5U); - u32 exp_total = 16 - (has_quarantine ? 5 - kRegistryQuarantine : 0); - CheckThreadQuantity(registry, exp_total, 6, 11); - // Test SetThreadName and FindThread. - registry->SetThreadName(6, "six"); - registry->SetThreadName(7, "seven"); - EXPECT_EQ(7U, registry->FindThread(HasName, (void*)"seven")); - EXPECT_EQ(ThreadRegistry::kUnknownTid, - registry->FindThread(HasName, (void*)"none")); - EXPECT_EQ(0U, registry->FindThread(HasUid, (void*)get_uid(0))); - EXPECT_EQ(10U, registry->FindThread(HasUid, (void*)get_uid(10))); - EXPECT_EQ(ThreadRegistry::kUnknownTid, - registry->FindThread(HasUid, (void*)0x1234)); - // Detach and finish and join remaining threads. - for (u32 i = 6; i <= 10; i++) { - registry->DetachThread(i, 0); - registry->FinishThread(i); - } - for (u32 i = 0; i < new_tids.size(); i++) { - u32 tid = new_tids[i]; - registry->StartThread(tid, 0, 0); - registry->DetachThread(tid, 0); - registry->FinishThread(tid); - } - CheckThreadQuantity(registry, exp_total, 1, 1); - // Test methods that require the caller to hold a ThreadRegistryLock. - bool has_tid[16]; - internal_memset(&has_tid[0], 0, sizeof(has_tid)); - { - ThreadRegistryLock l(registry); - registry->RunCallbackForEachThreadLocked(MarkUidAsPresent, &has_tid[0]); - } - for (u32 i = 0; i < exp_total; i++) { - EXPECT_TRUE(has_tid[i]); - } - { - ThreadRegistryLock l(registry); - registry->CheckLocked(); - ThreadContextBase *main_thread = registry->GetThreadLocked(0); - EXPECT_EQ(main_thread, registry->FindThreadContextLocked( - HasUid, (void*)get_uid(0))); - } - EXPECT_EQ(11U, registry->GetMaxAliveThreads()); -} - -TEST(SanitizerCommon, ThreadRegistryTest) { - ThreadRegistry quarantine_registry(GetThreadContext<ThreadContextBase>, - kMaxRegistryThreads, - kRegistryQuarantine); - TestRegistry(&quarantine_registry, true); - - ThreadRegistry no_quarantine_registry(GetThreadContext<ThreadContextBase>, - kMaxRegistryThreads, - kMaxRegistryThreads); - TestRegistry(&no_quarantine_registry, false); -} - -static const int kThreadsPerShard = 20; -static const int kNumShards = 25; - -static int num_created[kNumShards + 1]; -static int num_started[kNumShards + 1]; -static int num_joined[kNumShards + 1]; - -namespace { - -struct RunThreadArgs { - ThreadRegistry *registry; - uptr shard; // started from 1. -}; - -class TestThreadContext : public ThreadContextBase { - public: - explicit TestThreadContext(int tid) : ThreadContextBase(tid) {} - void OnJoined(void *arg) { - uptr shard = (uptr)arg; - num_joined[shard]++; - } - void OnStarted(void *arg) { - uptr shard = (uptr)arg; - num_started[shard]++; - } - void OnCreated(void *arg) { - uptr shard = (uptr)arg; - num_created[shard]++; - } -}; - -} // namespace - -void *RunThread(void *arg) { - RunThreadArgs *args = static_cast<RunThreadArgs*>(arg); - std::vector<int> tids; - for (int i = 0; i < kThreadsPerShard; i++) - tids.push_back( - args->registry->CreateThread(0, false, 0, (void*)args->shard)); - for (int i = 0; i < kThreadsPerShard; i++) - args->registry->StartThread(tids[i], 0, (void*)args->shard); - for (int i = 0; i < kThreadsPerShard; i++) - args->registry->FinishThread(tids[i]); - for (int i = 0; i < kThreadsPerShard; i++) - args->registry->JoinThread(tids[i], (void*)args->shard); - return 0; -} - -static void ThreadedTestRegistry(ThreadRegistry *registry) { - // Create and start a main thread. - EXPECT_EQ(0U, registry->CreateThread(0, true, -1, 0)); - registry->StartThread(0, 0, 0); - pthread_t threads[kNumShards]; - RunThreadArgs args[kNumShards]; - for (int i = 0; i < kNumShards; i++) { - args[i].registry = registry; - args[i].shard = i + 1; - PTHREAD_CREATE(&threads[i], 0, RunThread, &args[i]); - } - for (int i = 0; i < kNumShards; i++) { - PTHREAD_JOIN(threads[i], 0); - } - // Check that each thread created/started/joined correct amount - // of "threads" in thread_registry. - EXPECT_EQ(1, num_created[0]); - EXPECT_EQ(1, num_started[0]); - EXPECT_EQ(0, num_joined[0]); - for (int i = 1; i <= kNumShards; i++) { - EXPECT_EQ(kThreadsPerShard, num_created[i]); - EXPECT_EQ(kThreadsPerShard, num_started[i]); - EXPECT_EQ(kThreadsPerShard, num_joined[i]); - } -} - -TEST(SanitizerCommon, ThreadRegistryThreadedTest) { - ThreadRegistry registry(GetThreadContext<TestThreadContext>, - kThreadsPerShard * kNumShards + 1, 10); - ThreadedTestRegistry(®istry); -} - -} // namespace __sanitizer diff --git a/contrib/compiler-rt/lib/sanitizer_common/tests/standalone_malloc_test.cc b/contrib/compiler-rt/lib/sanitizer_common/tests/standalone_malloc_test.cc deleted file mode 100644 index 9e6f7c93b04b..000000000000 --- a/contrib/compiler-rt/lib/sanitizer_common/tests/standalone_malloc_test.cc +++ /dev/null @@ -1,87 +0,0 @@ -#include <stdio.h> -#include <vector> -#include <pthread.h> -#include <malloc.h> -#include <algorithm> - -using namespace std; - -const size_t kNumThreds = 16; -const size_t kNumIters = 1 << 23; - -inline void break_optimization(void *arg) { - __asm__ __volatile__("" : : "r" (arg) : "memory"); -} - -__attribute__((noinline)) -static void *MallocThread(void *t) { - size_t total_malloced = 0, total_freed = 0; - size_t max_in_use = 0; - size_t tid = reinterpret_cast<size_t>(t); - vector<pair<char *, size_t> > allocated; - allocated.reserve(kNumIters); - for (size_t i = 1; i < kNumIters; i++) { - if ((i % (kNumIters / 4)) == 0 && tid == 0) - fprintf(stderr, " T[%ld] iter %ld\n", tid, i); - bool allocate = (i % 5) <= 2; // 60% malloc, 40% free - if (i > kNumIters / 4) - allocate = i % 2; // then switch to 50% malloc, 50% free - if (allocate) { - size_t size = 1 + (i % 200); - if ((i % 10001) == 0) - size *= 4096; - total_malloced += size; - char *x = new char[size]; - x[0] = x[size - 1] = x[size / 2] = 0; - allocated.push_back(make_pair(x, size)); - max_in_use = max(max_in_use, total_malloced - total_freed); - } else { - if (allocated.empty()) continue; - size_t slot = i % allocated.size(); - char *p = allocated[slot].first; - p[0] = 0; // emulate last user touch of the block - size_t size = allocated[slot].second; - total_freed += size; - swap(allocated[slot], allocated.back()); - allocated.pop_back(); - delete [] p; - } - } - if (tid == 0) - fprintf(stderr, " T[%ld] total_malloced: %ldM in use %ldM max %ldM\n", - tid, total_malloced >> 20, (total_malloced - total_freed) >> 20, - max_in_use >> 20); - for (size_t i = 0; i < allocated.size(); i++) - delete [] allocated[i].first; - return 0; -} - -template <int depth> -struct DeepStack { - __attribute__((noinline)) - static void *run(void *t) { - break_optimization(0); - DeepStack<depth - 1>::run(t); - break_optimization(0); - return 0; - } -}; - -template<> -struct DeepStack<0> { - static void *run(void *t) { - MallocThread(t); - return 0; - } -}; - -// Build with -Dstandalone_malloc_test=main to make it a separate program. -int standalone_malloc_test() { - pthread_t t[kNumThreds]; - for (size_t i = 0; i < kNumThreds; i++) - pthread_create(&t[i], 0, DeepStack<200>::run, reinterpret_cast<void *>(i)); - for (size_t i = 0; i < kNumThreds; i++) - pthread_join(t[i], 0); - malloc_stats(); - return 0; -} diff --git a/contrib/compiler-rt/lib/tsan/analyze_libtsan.sh b/contrib/compiler-rt/lib/tsan/analyze_libtsan.sh deleted file mode 100755 index 705e4c5460f2..000000000000 --- a/contrib/compiler-rt/lib/tsan/analyze_libtsan.sh +++ /dev/null @@ -1,43 +0,0 @@ -#!/bin/bash - -set -e -set -u - -get_asm() { - grep __tsan_$1.: -A 10000 libtsan.objdump | \ - awk "/[^:]$/ {print;} />:/ {c++; if (c == 2) {exit}}" -} - -list="write1 \ - write2 \ - write4 \ - write8 \ - read1 \ - read2 \ - read4 \ - read8 \ - func_entry \ - func_exit" - -BIN=`dirname $0`/tsan_test -objdump -d $BIN > libtsan.objdump -nm -S $BIN | grep "__tsan_" > libtsan.nm - -for f in $list; do - file=asm_$f.s - get_asm $f > $file - tot=$(wc -l < $file) - size=$(grep __tsan_$f$ libtsan.nm | awk --non-decimal-data '{print ("0x"$2)+0}') - rsp=$(grep '(%rsp)' $file | wc -l) - push=$(grep 'push' $file | wc -l) - pop=$(grep 'pop' $file | wc -l) - call=$(grep 'call' $file | wc -l) - load=$(egrep 'mov .*\,.*\(.*\)|cmp .*\,.*\(.*\)' $file | wc -l) - store=$(egrep 'mov .*\(.*\),' $file | wc -l) - mov=$(grep 'mov' $file | wc -l) - lea=$(grep 'lea' $file | wc -l) - sh=$(grep 'shr\|shl' $file | wc -l) - cmp=$(grep 'cmp\|test' $file | wc -l) - printf "%10s tot %3d; size %4d; rsp %d; push %d; pop %d; call %d; load %2d; store %2d; sh %3d; mov %3d; lea %3d; cmp %3d\n" \ - $f $tot $size $rsp $push $pop $call $load $store $sh $mov $lea $cmp; -done diff --git a/contrib/compiler-rt/lib/tsan/check_analyze.sh b/contrib/compiler-rt/lib/tsan/check_analyze.sh deleted file mode 100755 index 4b33393ef648..000000000000 --- a/contrib/compiler-rt/lib/tsan/check_analyze.sh +++ /dev/null @@ -1,44 +0,0 @@ -#!/bin/bash -set -u - -RES=$(./analyze_libtsan.sh) -PrintRes() { - printf "%s\n" "$RES" -} - -PrintRes - -check() { - res=$(PrintRes | egrep "$1 .* $2 $3; ") - if [ "$res" == "" ]; then - echo FAILED $1 must contain $2 $3 - exit 1 - fi -} - -for f in write1; do - check $f rsp 1 - check $f push 2 - check $f pop 2 -done - -for f in write2 write4 write8; do - check $f rsp 1 - check $f push 3 - check $f pop 3 -done - -for f in read1 read2 read4 read8; do - check $f rsp 1 - check $f push 5 - check $f pop 5 -done - -for f in func_entry func_exit; do - check $f rsp 0 - check $f push 0 - check $f pop 0 - check $f call 1 # TraceSwitch() -done - -echo LGTM diff --git a/contrib/compiler-rt/lib/tsan/check_cmake.sh b/contrib/compiler-rt/lib/tsan/check_cmake.sh deleted file mode 100755 index 7668c5b49e1a..000000000000 --- a/contrib/compiler-rt/lib/tsan/check_cmake.sh +++ /dev/null @@ -1,18 +0,0 @@ -#!/bin/bash -set -u -set -e - -ROOT="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" -if [ -d "$ROOT/llvm-build" ]; then - cd $ROOT/llvm-build -else - mkdir -p $ROOT/llvm-build - cd $ROOT/llvm-build - CC=clang CXX=clang++ cmake -G Ninja -DLLVM_ENABLE_WERROR=ON -DCMAKE_BUILD_TYPE=Release -DLLVM_ENABLE_ASSERTIONS=ON $ROOT/../../../.. -fi -ninja -ninja check-sanitizer -ninja check-tsan -ninja check-asan -ninja check-msan -ninja check-lsan diff --git a/contrib/compiler-rt/lib/tsan/check_memcpy.sh b/contrib/compiler-rt/lib/tsan/check_memcpy.sh deleted file mode 100755 index 101df1166b7c..000000000000 --- a/contrib/compiler-rt/lib/tsan/check_memcpy.sh +++ /dev/null @@ -1,31 +0,0 @@ -#!/bin/bash - -# Ensure that tsan runtime does not contain compiler-emitted memcpy and memset calls. - -set -eu - -ROOTDIR=$(dirname $0) -TEST_DIR=$ROOTDIR/../../test/tsan - -: ${CXX:=clang++} -CFLAGS="-fsanitize=thread -fPIE -O1 -g" -LDFLAGS="-pie -lpthread -ldl -lrt -lm -Wl,--whole-archive $ROOTDIR/rtl/libtsan.a -Wl,--no-whole-archive" - -SRC=$TEST_DIR/simple_race.cc -OBJ=$SRC.o -EXE=$SRC.exe -$CXX $SRC $CFLAGS -c -o $OBJ -$CXX $OBJ $LDFLAGS -o $EXE - -NCALL=$(objdump -d $EXE | egrep "callq .*<__interceptor_mem(cpy|set)>" | wc -l) -if [ "$NCALL" != "0" ]; then - echo FAIL: found $NCALL memcpy/memset calls - exit 1 -fi - -# tail calls -NCALL=$(objdump -d $EXE | egrep "jmpq .*<__interceptor_mem(cpy|set)>" | wc -l) -if [ "$NCALL" != "0" ]; then - echo FAIL: found $NCALL memcpy/memset calls - exit 1 -fi diff --git a/contrib/compiler-rt/lib/tsan/go/build.bat b/contrib/compiler-rt/lib/tsan/go/build.bat deleted file mode 100644 index 7d393dc0e025..000000000000 --- a/contrib/compiler-rt/lib/tsan/go/build.bat +++ /dev/null @@ -1,4 +0,0 @@ -type tsan_go.cc ..\rtl\tsan_interface_atomic.cc ..\rtl\tsan_clock.cc ..\rtl\tsan_flags.cc ..\rtl\tsan_md5.cc ..\rtl\tsan_mutex.cc ..\rtl\tsan_report.cc ..\rtl\tsan_rtl.cc ..\rtl\tsan_rtl_mutex.cc ..\rtl\tsan_rtl_report.cc ..\rtl\tsan_rtl_thread.cc ..\rtl\tsan_stat.cc ..\rtl\tsan_suppressions.cc ..\rtl\tsan_sync.cc ..\rtl\tsan_stack_trace.cc ..\..\sanitizer_common\sanitizer_allocator.cc ..\..\sanitizer_common\sanitizer_common.cc ..\..\sanitizer_common\sanitizer_flags.cc ..\..\sanitizer_common\sanitizer_stacktrace.cc ..\..\sanitizer_common\sanitizer_libc.cc ..\..\sanitizer_common\sanitizer_printf.cc ..\..\sanitizer_common\sanitizer_suppressions.cc ..\..\sanitizer_common\sanitizer_thread_registry.cc ..\rtl\tsan_platform_windows.cc ..\..\sanitizer_common\sanitizer_win.cc ..\..\sanitizer_common\sanitizer_deadlock_detector1.cc ..\..\sanitizer_common\sanitizer_stackdepot.cc ..\..\sanitizer_common\sanitizer_persistent_allocator.cc ..\..\sanitizer_common\sanitizer_flag_parser.cc ..\..\sanitizer_common\sanitizer_symbolizer.cc > gotsan.cc - -gcc -c -o race_windows_amd64.syso gotsan.cc -I..\rtl -I..\.. -I..\..\sanitizer_common -I..\..\..\include -m64 -Wall -fno-exceptions -fno-rtti -DSANITIZER_GO -Wno-error=attributes -Wno-attributes -Wno-format -Wno-maybe-uninitialized -DSANITIZER_DEBUG=0 -O3 -fomit-frame-pointer -std=c++11 - diff --git a/contrib/compiler-rt/lib/tsan/go/buildgo.sh b/contrib/compiler-rt/lib/tsan/go/buildgo.sh deleted file mode 100755 index 7193b57684f2..000000000000 --- a/contrib/compiler-rt/lib/tsan/go/buildgo.sh +++ /dev/null @@ -1,130 +0,0 @@ -#!/bin/sh - -set -e - -SRCS=" - tsan_go.cc - ../rtl/tsan_clock.cc - ../rtl/tsan_flags.cc - ../rtl/tsan_interface_atomic.cc - ../rtl/tsan_md5.cc - ../rtl/tsan_mutex.cc - ../rtl/tsan_report.cc - ../rtl/tsan_rtl.cc - ../rtl/tsan_rtl_mutex.cc - ../rtl/tsan_rtl_report.cc - ../rtl/tsan_rtl_thread.cc - ../rtl/tsan_stack_trace.cc - ../rtl/tsan_stat.cc - ../rtl/tsan_suppressions.cc - ../rtl/tsan_sync.cc - ../../sanitizer_common/sanitizer_allocator.cc - ../../sanitizer_common/sanitizer_common.cc - ../../sanitizer_common/sanitizer_deadlock_detector2.cc - ../../sanitizer_common/sanitizer_flag_parser.cc - ../../sanitizer_common/sanitizer_flags.cc - ../../sanitizer_common/sanitizer_libc.cc - ../../sanitizer_common/sanitizer_persistent_allocator.cc - ../../sanitizer_common/sanitizer_printf.cc - ../../sanitizer_common/sanitizer_suppressions.cc - ../../sanitizer_common/sanitizer_thread_registry.cc - ../../sanitizer_common/sanitizer_stackdepot.cc - ../../sanitizer_common/sanitizer_stacktrace.cc - ../../sanitizer_common/sanitizer_symbolizer.cc -" - -if [ "`uname -a | grep Linux`" != "" ]; then - SUFFIX="linux_amd64" - OSCFLAGS="-fPIC -ffreestanding -Wno-maybe-uninitialized -Wno-unused-const-variable -Werror -Wno-unknown-warning-option" - OSLDFLAGS="-lpthread -lrt -fPIC -fpie" - SRCS=" - $SRCS - ../rtl/tsan_platform_linux.cc - ../../sanitizer_common/sanitizer_posix.cc - ../../sanitizer_common/sanitizer_posix_libcdep.cc - ../../sanitizer_common/sanitizer_procmaps_common.cc - ../../sanitizer_common/sanitizer_procmaps_linux.cc - ../../sanitizer_common/sanitizer_linux.cc - ../../sanitizer_common/sanitizer_stoptheworld_linux_libcdep.cc - " -elif [ "`uname -a | grep FreeBSD`" != "" ]; then - SUFFIX="freebsd_amd64" - OSCFLAGS="-fno-strict-aliasing -fPIC -Werror" - OSLDFLAGS="-lpthread -fPIC -fpie" - SRCS=" - $SRCS - ../rtl/tsan_platform_linux.cc - ../../sanitizer_common/sanitizer_posix.cc - ../../sanitizer_common/sanitizer_posix_libcdep.cc - ../../sanitizer_common/sanitizer_procmaps_common.cc - ../../sanitizer_common/sanitizer_procmaps_freebsd.cc - ../../sanitizer_common/sanitizer_linux.cc - ../../sanitizer_common/sanitizer_stoptheworld_linux_libcdep.cc - " -elif [ "`uname -a | grep Darwin`" != "" ]; then - SUFFIX="darwin_amd64" - OSCFLAGS="-fPIC -Wno-unused-const-variable -Wno-unknown-warning-option" - OSLDFLAGS="-lpthread -fPIC -fpie" - SRCS=" - $SRCS - ../rtl/tsan_platform_mac.cc - ../../sanitizer_common/sanitizer_mac.cc - ../../sanitizer_common/sanitizer_posix.cc - ../../sanitizer_common/sanitizer_posix_libcdep.cc - ../../sanitizer_common/sanitizer_procmaps_mac.cc - " -elif [ "`uname -a | grep MINGW`" != "" ]; then - SUFFIX="windows_amd64" - OSCFLAGS="-Wno-error=attributes -Wno-attributes -Wno-unused-const-variable -Wno-unknown-warning-option" - OSLDFLAGS="" - SRCS=" - $SRCS - ../rtl/tsan_platform_windows.cc - ../../sanitizer_common/sanitizer_win.cc - " -else - echo Unknown platform - exit 1 -fi - -CC=${CC:-gcc} -IN_TMPDIR=${IN_TMPDIR:-0} -SILENT=${SILENT:-0} - -if [ $IN_TMPDIR != "0" ]; then - DIR=$(mktemp -qd /tmp/gotsan.XXXXXXXXXX) - cleanup() { - rm -rf $DIR - } - trap cleanup EXIT -else - DIR=. -fi - -SRCS="$SRCS $ADD_SRCS" - -rm -f $DIR/gotsan.cc -for F in $SRCS; do - cat $F >> $DIR/gotsan.cc -done - -FLAGS=" -I../rtl -I../.. -I../../sanitizer_common -I../../../include -std=c++11 -m64 -Wall -fno-exceptions -fno-rtti -DSANITIZER_GO -DSANITIZER_DEADLOCK_DETECTOR_VERSION=2 $OSCFLAGS" -if [ "$DEBUG" = "" ]; then - FLAGS="$FLAGS -DSANITIZER_DEBUG=0 -O3 -msse3 -fomit-frame-pointer" -else - FLAGS="$FLAGS -DSANITIZER_DEBUG=1 -g" -fi - -if [ "$SILENT" != "1" ]; then - echo $CC gotsan.cc -c -o $DIR/race_$SUFFIX.syso $FLAGS $CFLAGS -fi -$CC $DIR/gotsan.cc -c -o $DIR/race_$SUFFIX.syso $FLAGS $CFLAGS - -$CC test.c $DIR/race_$SUFFIX.syso -m64 -o $DIR/test $OSLDFLAGS - -export GORACE="exitcode=0 atexit_sleep_ms=0" -if [ "$SILENT" != "1" ]; then - $DIR/test -else - $DIR/test 2>/dev/null -fi diff --git a/contrib/compiler-rt/lib/tsan/rtl/tsan_clock.cc b/contrib/compiler-rt/lib/tsan/rtl/tsan_clock.cc index 59e3de435f1b..1e2050d1f203 100644 --- a/contrib/compiler-rt/lib/tsan/rtl/tsan_clock.cc +++ b/contrib/compiler-rt/lib/tsan/rtl/tsan_clock.cc @@ -90,8 +90,6 @@ namespace __tsan { -const unsigned kInvalidTid = (unsigned)-1; - ThreadClock::ThreadClock(unsigned tid, unsigned reused) : tid_(tid) , reused_(reused + 1) { // 0 has special meaning diff --git a/contrib/compiler-rt/lib/tsan/rtl/tsan_defs.h b/contrib/compiler-rt/lib/tsan/rtl/tsan_defs.h index d869d95e0878..9c7b329dcf00 100644 --- a/contrib/compiler-rt/lib/tsan/rtl/tsan_defs.h +++ b/contrib/compiler-rt/lib/tsan/rtl/tsan_defs.h @@ -38,13 +38,10 @@ 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; @@ -83,6 +80,8 @@ const bool kCollectHistory = false; const bool kCollectHistory = true; #endif +const unsigned kInvalidTid = (unsigned)-1; + // The following "build consistency" machinery ensures that all source files // are built in the same configuration. Inconsistent builds lead to // hard to debug crashes. diff --git a/contrib/compiler-rt/lib/tsan/rtl/tsan_dense_alloc.h b/contrib/compiler-rt/lib/tsan/rtl/tsan_dense_alloc.h index a1cf84b8f166..e9815c90a953 100644 --- a/contrib/compiler-rt/lib/tsan/rtl/tsan_dense_alloc.h +++ b/contrib/compiler-rt/lib/tsan/rtl/tsan_dense_alloc.h @@ -108,7 +108,7 @@ class DenseSlabAlloc { // Reserve 0 as invalid index. IndexT start = fillpos_ == 0 ? 1 : 0; for (IndexT i = start; i < kL2Size; i++) { - new(batch + i) T(); + new(batch + i) T; *(IndexT*)(batch + i) = i + 1 + fillpos_ * kL2Size; } *(IndexT*)(batch + kL2Size - 1) = 0; diff --git a/contrib/compiler-rt/lib/tsan/rtl/tsan_flags.cc b/contrib/compiler-rt/lib/tsan/rtl/tsan_flags.cc index 5de227a42dee..761523171c77 100644 --- a/contrib/compiler-rt/lib/tsan/rtl/tsan_flags.cc +++ b/contrib/compiler-rt/lib/tsan/rtl/tsan_flags.cc @@ -29,8 +29,8 @@ Flags *flags() { #ifdef TSAN_EXTERNAL_HOOKS extern "C" const char* __tsan_default_options(); #else -extern "C" SANITIZER_INTERFACE_ATTRIBUTE -const char *WEAK __tsan_default_options() { +SANITIZER_WEAK_DEFAULT_IMPL +const char *__tsan_default_options() { return ""; } #endif @@ -61,11 +61,16 @@ void InitializeFlags(Flags *f, const char *env) { CommonFlags cf; cf.CopyFrom(*common_flags()); cf.allow_addr2line = true; -#ifndef SANITIZER_GO - cf.detect_deadlocks = true; -#endif + if (kGoMode) { + // Does not work as expected for Go: runtime handles SIGABRT and crashes. + cf.abort_on_error = false; + // Go does not have mutexes. + } else { + cf.detect_deadlocks = true; + } cf.print_suppressions = false; cf.stack_trace_format = " #%n %f %S %M"; + cf.exitcode = 66; OverrideCommonFlags(cf); } diff --git a/contrib/compiler-rt/lib/tsan/rtl/tsan_flags.inc b/contrib/compiler-rt/lib/tsan/rtl/tsan_flags.inc index e4994685fa0d..ab9ca9924936 100644 --- a/contrib/compiler-rt/lib/tsan/rtl/tsan_flags.inc +++ b/contrib/compiler-rt/lib/tsan/rtl/tsan_flags.inc @@ -45,7 +45,6 @@ TSAN_FLAG( "If set, all atomics are effectively sequentially consistent (seq_cst), " "regardless of what user actually specified.") TSAN_FLAG(bool, print_benign, false, "Print matched \"benign\" races at exit.") -TSAN_FLAG(int, exitcode, 66, "Override exit status if something was reported.") TSAN_FLAG(bool, halt_on_error, false, "Exit after first reported error.") TSAN_FLAG(int, atexit_sleep_ms, 1000, "Sleep in main thread before exiting for that many ms " diff --git a/contrib/compiler-rt/lib/tsan/rtl/tsan_interceptors.cc b/contrib/compiler-rt/lib/tsan/rtl/tsan_interceptors.cc index b1a7ae6de328..7c835c6dc7df 100644 --- a/contrib/compiler-rt/lib/tsan/rtl/tsan_interceptors.cc +++ b/contrib/compiler-rt/lib/tsan/rtl/tsan_interceptors.cc @@ -28,16 +28,42 @@ #include "tsan_mman.h" #include "tsan_fd.h" +#if SANITIZER_POSIX +#include "sanitizer_common/sanitizer_posix.h" +#endif + using namespace __tsan; // NOLINT -#if SANITIZER_FREEBSD +#if SANITIZER_FREEBSD || SANITIZER_MAC #define __errno_location __error -#define __libc_realloc __realloc -#define __libc_calloc __calloc #define stdout __stdoutp #define stderr __stderrp #endif +#if SANITIZER_FREEBSD +#define __libc_realloc __realloc +#define __libc_calloc __calloc +#elif SANITIZER_MAC +#define __libc_malloc REAL(malloc) +#define __libc_realloc REAL(realloc) +#define __libc_calloc REAL(calloc) +#define __libc_free REAL(free) +#elif SANITIZER_ANDROID +#define __errno_location __errno +#define __libc_malloc REAL(malloc) +#define __libc_realloc REAL(realloc) +#define __libc_calloc REAL(calloc) +#define __libc_free REAL(free) +#define mallopt(a, b) +#endif + +#if SANITIZER_LINUX || SANITIZER_FREEBSD +#define PTHREAD_CREATE_DETACHED 1 +#elif SANITIZER_MAC +#define PTHREAD_CREATE_DETACHED 2 +#endif + + #ifdef __mips__ const int kSigCount = 129; #else @@ -60,6 +86,14 @@ struct ucontext_t { }; #endif +#if defined(__x86_64__) || defined(__mips__) \ + || (defined(__powerpc64__) && defined(__BIG_ENDIAN__)) +#define PTHREAD_ABI_BASE "GLIBC_2.3.2" +#elif defined(__aarch64__) || (defined(__powerpc64__) \ + && defined(__LITTLE_ENDIAN__)) +#define PTHREAD_ABI_BASE "GLIBC_2.17" +#endif + extern "C" int pthread_attr_init(void *attr); extern "C" int pthread_attr_destroy(void *attr); DECLARE_REAL(int, pthread_attr_getdetachstate, void *, void *) @@ -67,20 +101,23 @@ extern "C" int pthread_attr_setstacksize(void *attr, uptr stacksize); extern "C" int pthread_key_create(unsigned *key, void (*destructor)(void* v)); extern "C" int pthread_setspecific(unsigned key, const void *v); DECLARE_REAL(int, pthread_mutexattr_gettype, void *, void *) -extern "C" int pthread_yield(); extern "C" int pthread_sigmask(int how, const __sanitizer_sigset_t *set, __sanitizer_sigset_t *oldset); // REAL(sigfillset) defined in common interceptors. DECLARE_REAL(int, sigfillset, __sanitizer_sigset_t *set) DECLARE_REAL(int, fflush, __sanitizer_FILE *fp) +DECLARE_REAL_AND_INTERCEPTOR(void *, malloc, uptr size) +DECLARE_REAL_AND_INTERCEPTOR(void, free, void *ptr) extern "C" void *pthread_self(); extern "C" void _exit(int status); extern "C" int *__errno_location(); extern "C" int fileno_unlocked(void *stream); +#if !SANITIZER_ANDROID extern "C" void *__libc_calloc(uptr size, uptr n); extern "C" void *__libc_realloc(void *ptr, uptr size); +#endif extern "C" int dirfd(void *dirp); -#if !SANITIZER_FREEBSD +#if !SANITIZER_FREEBSD && !SANITIZER_ANDROID extern "C" int mallopt(int param, int value); #endif extern __sanitizer_FILE *stdout, *stderr; @@ -89,14 +126,16 @@ const int PTHREAD_MUTEX_RECURSIVE_NP = 1; const int EINVAL = 22; const int EBUSY = 16; const int EOWNERDEAD = 130; +#if !SANITIZER_MAC const int EPOLL_CTL_ADD = 1; +#endif const int SIGILL = 4; const int SIGABRT = 6; const int SIGFPE = 8; const int SIGSEGV = 11; const int SIGPIPE = 13; const int SIGTERM = 15; -#ifdef __mips__ +#if defined(__mips__) || SANITIZER_MAC const int SIGBUS = 10; const int SIGSYS = 12; #else @@ -104,7 +143,9 @@ const int SIGBUS = 7; const int SIGSYS = 31; #endif void *const MAP_FAILED = (void*)-1; +#if !SANITIZER_MAC const int PTHREAD_BARRIER_SERIAL_THREAD = -1; +#endif const int MAP_FIXED = 0x10; typedef long long_t; // NOLINT @@ -119,6 +160,17 @@ typedef long long_t; // NOLINT typedef void (*sighandler_t)(int sig); typedef void (*sigactionhandler_t)(int sig, my_siginfo_t *siginfo, void *uctx); +#if SANITIZER_ANDROID +struct sigaction_t { + u32 sa_flags; + union { + sighandler_t sa_handler; + sigactionhandler_t sa_sgiaction; + }; + __sanitizer_sigset_t sa_mask; + void (*sa_restorer)(); +}; +#else struct sigaction_t { #ifdef __mips__ u32 sa_flags; @@ -130,6 +182,9 @@ struct sigaction_t { #if SANITIZER_FREEBSD int sa_flags; __sanitizer_sigset_t sa_mask; +#elif SANITIZER_MAC + __sanitizer_sigset_t sa_mask; + int sa_flags; #else __sanitizer_sigset_t sa_mask; #ifndef __mips__ @@ -138,11 +193,12 @@ struct sigaction_t { void (*sa_restorer)(); #endif }; +#endif const sighandler_t SIG_DFL = (sighandler_t)0; const sighandler_t SIG_IGN = (sighandler_t)1; const sighandler_t SIG_ERR = (sighandler_t)-1; -#if SANITIZER_FREEBSD +#if SANITIZER_FREEBSD || SANITIZER_MAC const int SA_SIGINFO = 0x40; const int SIG_SETMASK = 3; #elif defined(__mips__) @@ -171,6 +227,9 @@ struct ThreadSignalContext { atomic_uintptr_t in_blocking_func; atomic_uintptr_t have_pending_signals; SignalDesc pending_signals[kSigCount]; + // emptyset and oldset are too big for stack. + __sanitizer_sigset_t emptyset; + __sanitizer_sigset_t oldset; }; // The object is 64-byte aligned, because we want hot data to be located in @@ -203,7 +262,9 @@ static ThreadSignalContext *SigCtx(ThreadState *thr) { return ctx; } +#if !SANITIZER_MAC static unsigned g_thread_finalize_key; +#endif ScopedInterceptor::ScopedInterceptor(ThreadState *thr, const char *fname, uptr pc) @@ -234,17 +295,20 @@ ScopedInterceptor::~ScopedInterceptor() { } } -#define SCOPED_TSAN_INTERCEPTOR(func, ...) \ - SCOPED_INTERCEPTOR_RAW(func, __VA_ARGS__); \ - if (REAL(func) == 0) { \ - Report("FATAL: ThreadSanitizer: failed to intercept %s\n", #func); \ - Die(); \ - } \ - if (thr->ignore_interceptors || thr->in_ignored_lib) \ - return REAL(func)(__VA_ARGS__); \ -/**/ +void ScopedInterceptor::UserCallbackStart() { + if (in_ignored_lib_) { + thr_->in_ignored_lib = false; + ThreadIgnoreEnd(thr_, pc_); + } +} + +void ScopedInterceptor::UserCallbackEnd() { + if (in_ignored_lib_) { + thr_->in_ignored_lib = true; + ThreadIgnoreBegin(thr_, pc_); + } +} -#define TSAN_INTERCEPTOR(ret, func, ...) INTERCEPTOR(ret, func, __VA_ARGS__) #define TSAN_INTERCEPT(func) INTERCEPT_FUNCTION(func) #if SANITIZER_FREEBSD # define TSAN_INTERCEPT_VER(func, ver) INTERCEPT_FUNCTION(func) @@ -329,6 +393,7 @@ static void at_exit_wrapper(void *arg) { static int setup_at_exit_wrapper(ThreadState *thr, uptr pc, void(*f)(), void *arg, void *dso); +#if !SANITIZER_ANDROID TSAN_INTERCEPTOR(int, atexit, void (*f)()) { if (cur_thread()->in_symbolizer) return 0; @@ -337,6 +402,7 @@ TSAN_INTERCEPTOR(int, atexit, void (*f)()) { SCOPED_INTERCEPTOR_RAW(atexit, f); return setup_at_exit_wrapper(thr, pc, (void(*)())f, 0, 0); } +#endif TSAN_INTERCEPTOR(int, __cxa_atexit, void (*f)(void *a), void *arg, void *dso) { if (cur_thread()->in_symbolizer) @@ -359,6 +425,7 @@ static int setup_at_exit_wrapper(ThreadState *thr, uptr pc, void(*f)(), return res; } +#if !SANITIZER_MAC static void on_exit_wrapper(int status, void *arg) { ThreadState *thr = cur_thread(); uptr pc = 0; @@ -383,6 +450,7 @@ TSAN_INTERCEPTOR(int, on_exit, void(*f)(int, void*), void *arg) { ThreadIgnoreEnd(thr, pc); return res; } +#endif // Cleanup old bufs. static void JmpBufGarbageCollect(ThreadState *thr, uptr sp) { @@ -390,7 +458,7 @@ static void JmpBufGarbageCollect(ThreadState *thr, uptr sp) { JmpBuf *buf = &thr->jmp_bufs[i]; if (buf->sp <= sp) { uptr sz = thr->jmp_bufs.Size(); - thr->jmp_bufs[i] = thr->jmp_bufs[sz - 1]; + internal_memcpy(buf, &thr->jmp_bufs[sz - 1], sizeof(*buf)); thr->jmp_bufs.PopBack(); i--; } @@ -417,11 +485,17 @@ static void SetJmp(ThreadState *thr, uptr sp, uptr mangled_sp) { } static void LongJmp(ThreadState *thr, uptr *env) { -#if SANITIZER_FREEBSD +#ifdef __powerpc__ + uptr mangled_sp = env[0]; +#elif SANITIZER_FREEBSD || SANITIZER_MAC uptr mangled_sp = env[2]; -#else +#elif defined(SANITIZER_LINUX) +# ifdef __aarch64__ + uptr mangled_sp = env[13]; +# else uptr mangled_sp = env[6]; -#endif // SANITIZER_FREEBSD +# endif +#endif // Find the saved buf by mangled_sp. for (uptr i = 0; i < thr->jmp_bufs.Size(); i++) { JmpBuf *buf = &thr->jmp_bufs[i]; @@ -451,6 +525,11 @@ extern "C" void __tsan_setjmp(uptr sp, uptr mangled_sp) { SetJmp(cur_thread(), sp, mangled_sp); } +#if SANITIZER_MAC +TSAN_INTERCEPTOR(int, setjmp, void *env); +TSAN_INTERCEPTOR(int, _setjmp, void *env); +TSAN_INTERCEPTOR(int, sigsetjmp, void *env); +#else // SANITIZER_MAC // Not called. Merely to satisfy TSAN_INTERCEPT(). extern "C" SANITIZER_INTERFACE_ATTRIBUTE int __interceptor_setjmp(void *env); @@ -489,6 +568,7 @@ DEFINE_REAL(int, setjmp, void *env) DEFINE_REAL(int, _setjmp, void *env) DEFINE_REAL(int, sigsetjmp, void *env) DEFINE_REAL(int, __sigsetjmp, void *env) +#endif // SANITIZER_MAC TSAN_INTERCEPTOR(void, longjmp, uptr *env, int val) { { @@ -506,6 +586,7 @@ TSAN_INTERCEPTOR(void, siglongjmp, uptr *env, int val) { REAL(siglongjmp)(env, val); } +#if !SANITIZER_MAC TSAN_INTERCEPTOR(void*, malloc, uptr size) { if (cur_thread()->in_symbolizer) return __libc_malloc(size); @@ -572,6 +653,7 @@ TSAN_INTERCEPTOR(uptr, malloc_usable_size, void *p) { SCOPED_INTERCEPTOR_RAW(malloc_usable_size, p); return user_alloc_usable_size(p); } +#endif TSAN_INTERCEPTOR(uptr, strlen, const char *s) { SCOPED_TSAN_INTERCEPTOR(strlen, s); @@ -596,27 +678,18 @@ TSAN_INTERCEPTOR(void*, memcpy, void *dst, const void *src, uptr size) { MemoryAccessRange(thr, pc, (uptr)dst, size, true); MemoryAccessRange(thr, pc, (uptr)src, size, false); } - return internal_memcpy(dst, src, size); -} - -TSAN_INTERCEPTOR(int, memcmp, const void *s1, const void *s2, uptr n) { - SCOPED_TSAN_INTERCEPTOR(memcmp, s1, s2, n); - int res = 0; - uptr len = 0; - for (; len < n; len++) { - if ((res = ((const unsigned char *)s1)[len] - - ((const unsigned char *)s2)[len])) - break; - } - MemoryAccessRange(thr, pc, (uptr)s1, len < n ? len + 1 : n, false); - MemoryAccessRange(thr, pc, (uptr)s2, len < n ? len + 1 : n, false); - return res; + // On OS X, calling internal_memcpy here will cause memory corruptions, + // because memcpy and memmove are actually aliases of the same implementation. + // We need to use internal_memmove here. + return internal_memmove(dst, src, size); } TSAN_INTERCEPTOR(void*, memmove, void *dst, void *src, uptr n) { - SCOPED_TSAN_INTERCEPTOR(memmove, dst, src, n); - MemoryAccessRange(thr, pc, (uptr)dst, n, true); - MemoryAccessRange(thr, pc, (uptr)src, n, false); + if (!COMMON_INTERCEPTOR_NOTHING_IS_INITIALIZED) { + SCOPED_TSAN_INTERCEPTOR(memmove, dst, src, n); + MemoryAccessRange(thr, pc, (uptr)dst, n, true); + MemoryAccessRange(thr, pc, (uptr)src, n, false); + } return REAL(memmove)(dst, src, n); } @@ -629,6 +702,7 @@ TSAN_INTERCEPTOR(char*, strchr, char *s, int c) { return res; } +#if !SANITIZER_MAC TSAN_INTERCEPTOR(char*, strchrnul, char *s, int c) { SCOPED_TSAN_INTERCEPTOR(strchrnul, s, c); char *res = REAL(strchrnul)(s, c); @@ -636,6 +710,7 @@ TSAN_INTERCEPTOR(char*, strchrnul, char *s, int c) { READ_STRING(thr, pc, s, len); return res; } +#endif TSAN_INTERCEPTOR(char*, strrchr, char *s, int c) { SCOPED_TSAN_INTERCEPTOR(strrchr, s, c); @@ -679,8 +754,8 @@ static bool fix_mmap_addr(void **addr, long_t sz, int flags) { return true; } -TSAN_INTERCEPTOR(void*, mmap, void *addr, long_t sz, int prot, - int flags, int fd, unsigned off) { +TSAN_INTERCEPTOR(void *, mmap, void *addr, SIZE_T sz, int prot, int flags, + int fd, OFF_T off) { SCOPED_TSAN_INTERCEPTOR(mmap, addr, sz, prot, flags, fd, off); if (!fix_mmap_addr(&addr, sz, flags)) return MAP_FAILED; @@ -693,9 +768,9 @@ TSAN_INTERCEPTOR(void*, mmap, void *addr, long_t sz, int prot, return res; } -#if !SANITIZER_FREEBSD -TSAN_INTERCEPTOR(void*, mmap64, void *addr, long_t sz, int prot, - int flags, int fd, u64 off) { +#if SANITIZER_LINUX +TSAN_INTERCEPTOR(void *, mmap64, void *addr, SIZE_T sz, int prot, int flags, + int fd, OFF64_T off) { SCOPED_TSAN_INTERCEPTOR(mmap64, addr, sz, prot, flags, fd, off); if (!fix_mmap_addr(&addr, sz, flags)) return MAP_FAILED; @@ -723,7 +798,7 @@ TSAN_INTERCEPTOR(int, munmap, void *addr, long_t sz) { return res; } -#if !SANITIZER_FREEBSD +#if SANITIZER_LINUX TSAN_INTERCEPTOR(void*, memalign, uptr align, uptr sz) { SCOPED_INTERCEPTOR_RAW(memalign, align, sz); return user_alloc(thr, pc, sz, align); @@ -733,6 +808,7 @@ TSAN_INTERCEPTOR(void*, memalign, uptr align, uptr sz) { #define TSAN_MAYBE_INTERCEPT_MEMALIGN #endif +#if !SANITIZER_MAC TSAN_INTERCEPTOR(void*, aligned_alloc, uptr align, uptr sz) { SCOPED_INTERCEPTOR_RAW(memalign, align, sz); return user_alloc(thr, pc, sz, align); @@ -742,8 +818,9 @@ TSAN_INTERCEPTOR(void*, valloc, uptr sz) { SCOPED_INTERCEPTOR_RAW(valloc, sz); return user_alloc(thr, pc, sz, GetPageSizeCached()); } +#endif -#if !SANITIZER_FREEBSD +#if SANITIZER_LINUX TSAN_INTERCEPTOR(void*, pvalloc, uptr sz) { SCOPED_INTERCEPTOR_RAW(pvalloc, sz); sz = RoundUp(sz, GetPageSizeCached()); @@ -754,14 +831,33 @@ TSAN_INTERCEPTOR(void*, pvalloc, uptr sz) { #define TSAN_MAYBE_INTERCEPT_PVALLOC #endif +#if !SANITIZER_MAC TSAN_INTERCEPTOR(int, posix_memalign, void **memptr, uptr align, uptr sz) { SCOPED_INTERCEPTOR_RAW(posix_memalign, memptr, align, sz); *memptr = user_alloc(thr, pc, sz, align); return 0; } +#endif + +// __cxa_guard_acquire and friends need to be intercepted in a special way - +// regular interceptors will break statically-linked libstdc++. Linux +// interceptors are especially defined as weak functions (so that they don't +// cause link errors when user defines them as well). So they silently +// auto-disable themselves when such symbol is already present in the binary. If +// we link libstdc++ statically, it will bring own __cxa_guard_acquire which +// will silently replace our interceptor. That's why on Linux we simply export +// these interceptors with INTERFACE_ATTRIBUTE. +// On OS X, we don't support statically linking, so we just use a regular +// interceptor. +#if SANITIZER_MAC +#define STDCXX_INTERCEPTOR TSAN_INTERCEPTOR +#else +#define STDCXX_INTERCEPTOR(rettype, name, ...) \ + extern "C" rettype INTERFACE_ATTRIBUTE name(__VA_ARGS__) +#endif // Used in thread-safe function static initialization. -extern "C" int INTERFACE_ATTRIBUTE __cxa_guard_acquire(atomic_uint32_t *g) { +STDCXX_INTERCEPTOR(int, __cxa_guard_acquire, atomic_uint32_t *g) { SCOPED_INTERCEPTOR_RAW(__cxa_guard_acquire, g); for (;;) { u32 cmp = atomic_load(g, memory_order_acquire); @@ -777,17 +873,31 @@ extern "C" int INTERFACE_ATTRIBUTE __cxa_guard_acquire(atomic_uint32_t *g) { } } -extern "C" void INTERFACE_ATTRIBUTE __cxa_guard_release(atomic_uint32_t *g) { +STDCXX_INTERCEPTOR(void, __cxa_guard_release, atomic_uint32_t *g) { SCOPED_INTERCEPTOR_RAW(__cxa_guard_release, g); Release(thr, pc, (uptr)g); atomic_store(g, 1, memory_order_release); } -extern "C" void INTERFACE_ATTRIBUTE __cxa_guard_abort(atomic_uint32_t *g) { +STDCXX_INTERCEPTOR(void, __cxa_guard_abort, atomic_uint32_t *g) { SCOPED_INTERCEPTOR_RAW(__cxa_guard_abort, g); atomic_store(g, 0, memory_order_relaxed); } +namespace __tsan { +void DestroyThreadState() { + ThreadState *thr = cur_thread(); + ThreadFinish(thr); + ThreadSignalContext *sctx = thr->signal_ctx; + if (sctx) { + thr->signal_ctx = 0; + UnmapOrDie(sctx, sizeof(*sctx)); + } + cur_thread_finalize(); +} +} // namespace __tsan + +#if !SANITIZER_MAC static void thread_finalize(void *v) { uptr iter = (uptr)v; if (iter > 1) { @@ -797,16 +907,9 @@ static void thread_finalize(void *v) { } return; } - { - ThreadState *thr = cur_thread(); - ThreadFinish(thr); - ThreadSignalContext *sctx = thr->signal_ctx; - if (sctx) { - thr->signal_ctx = 0; - UnmapOrDie(sctx, sizeof(*sctx)); - } - } + DestroyThreadState(); } +#endif struct ThreadParam { @@ -824,6 +927,7 @@ extern "C" void *__tsan_thread_start_func(void *arg) { ThreadState *thr = cur_thread(); // Thread-local state is not initialized yet. ScopedIgnoreInterceptors ignore; +#if !SANITIZER_MAC ThreadIgnoreBegin(thr, 0); if (pthread_setspecific(g_thread_finalize_key, (void *)GetPthreadDestructorIterations())) { @@ -831,8 +935,9 @@ extern "C" void *__tsan_thread_start_func(void *arg) { Die(); } ThreadIgnoreEnd(thr, 0); +#endif while ((tid = atomic_load(&p->tid, memory_order_acquire)) == 0) - pthread_yield(); + internal_sched_yield(); ThreadStart(thr, tid, GetTid()); atomic_store(&p->tid, 0, memory_order_release); } @@ -880,7 +985,8 @@ TSAN_INTERCEPTOR(int, pthread_create, ThreadIgnoreEnd(thr, pc); } if (res == 0) { - int tid = ThreadCreate(thr, pc, *(uptr*)th, detached); + int tid = ThreadCreate(thr, pc, *(uptr*)th, + detached == PTHREAD_CREATE_DETACHED); CHECK_NE(tid, 0); // Synchronization on p.tid serves two purposes: // 1. ThreadCreate must finish before the new thread starts. @@ -891,7 +997,7 @@ TSAN_INTERCEPTOR(int, pthread_create, // before the new thread got a chance to acquire from it in ThreadStart. atomic_store(&p.tid, tid, memory_order_release); while (atomic_load(&p.tid, memory_order_acquire) != 0) - pthread_yield(); + internal_sched_yield(); } if (attr == &myattr) pthread_attr_destroy(&myattr); @@ -1094,6 +1200,7 @@ TSAN_INTERCEPTOR(int, pthread_mutex_trylock, void *m) { return res; } +#if !SANITIZER_MAC TSAN_INTERCEPTOR(int, pthread_mutex_timedlock, void *m, void *abstime) { SCOPED_TSAN_INTERCEPTOR(pthread_mutex_timedlock, m, abstime); int res = REAL(pthread_mutex_timedlock)(m, abstime); @@ -1102,7 +1209,9 @@ TSAN_INTERCEPTOR(int, pthread_mutex_timedlock, void *m, void *abstime) { } return res; } +#endif +#if !SANITIZER_MAC TSAN_INTERCEPTOR(int, pthread_spin_init, void *m, int pshared) { SCOPED_TSAN_INTERCEPTOR(pthread_spin_init, m, pshared); int res = REAL(pthread_spin_init)(m, pshared); @@ -1145,6 +1254,7 @@ TSAN_INTERCEPTOR(int, pthread_spin_unlock, void *m) { int res = REAL(pthread_spin_unlock)(m); return res; } +#endif TSAN_INTERCEPTOR(int, pthread_rwlock_init, void *m, void *a) { SCOPED_TSAN_INTERCEPTOR(pthread_rwlock_init, m, a); @@ -1182,6 +1292,7 @@ TSAN_INTERCEPTOR(int, pthread_rwlock_tryrdlock, void *m) { return res; } +#if !SANITIZER_MAC TSAN_INTERCEPTOR(int, pthread_rwlock_timedrdlock, void *m, void *abstime) { SCOPED_TSAN_INTERCEPTOR(pthread_rwlock_timedrdlock, m, abstime); int res = REAL(pthread_rwlock_timedrdlock)(m, abstime); @@ -1190,6 +1301,7 @@ TSAN_INTERCEPTOR(int, pthread_rwlock_timedrdlock, void *m, void *abstime) { } return res; } +#endif TSAN_INTERCEPTOR(int, pthread_rwlock_wrlock, void *m) { SCOPED_TSAN_INTERCEPTOR(pthread_rwlock_wrlock, m); @@ -1209,6 +1321,7 @@ TSAN_INTERCEPTOR(int, pthread_rwlock_trywrlock, void *m) { return res; } +#if !SANITIZER_MAC TSAN_INTERCEPTOR(int, pthread_rwlock_timedwrlock, void *m, void *abstime) { SCOPED_TSAN_INTERCEPTOR(pthread_rwlock_timedwrlock, m, abstime); int res = REAL(pthread_rwlock_timedwrlock)(m, abstime); @@ -1217,6 +1330,7 @@ TSAN_INTERCEPTOR(int, pthread_rwlock_timedwrlock, void *m, void *abstime) { } return res; } +#endif TSAN_INTERCEPTOR(int, pthread_rwlock_unlock, void *m) { SCOPED_TSAN_INTERCEPTOR(pthread_rwlock_unlock, m); @@ -1225,6 +1339,7 @@ TSAN_INTERCEPTOR(int, pthread_rwlock_unlock, void *m) { return res; } +#if !SANITIZER_MAC TSAN_INTERCEPTOR(int, pthread_barrier_init, void *b, void *a, unsigned count) { SCOPED_TSAN_INTERCEPTOR(pthread_barrier_init, b, a, count); MemoryWrite(thr, pc, (uptr)b, kSizeLog1); @@ -1250,12 +1365,17 @@ TSAN_INTERCEPTOR(int, pthread_barrier_wait, void *b) { } return res; } +#endif TSAN_INTERCEPTOR(int, pthread_once, void *o, void (*f)()) { SCOPED_INTERCEPTOR_RAW(pthread_once, o, f); if (o == 0 || f == 0) return EINVAL; - atomic_uint32_t *a = static_cast<atomic_uint32_t*>(o); + atomic_uint32_t *a; + if (!SANITIZER_MAC) + a = static_cast<atomic_uint32_t*>(o); + else // On OS X, pthread_once_t has a header with a long-sized signature. + a = static_cast<atomic_uint32_t*>((void *)((char *)o + sizeof(long_t))); u32 v = atomic_load(a, memory_order_acquire); if (v == 0 && atomic_compare_exchange_strong(a, &v, 1, memory_order_relaxed)) { @@ -1265,7 +1385,7 @@ TSAN_INTERCEPTOR(int, pthread_once, void *o, void (*f)()) { atomic_store(a, 2, memory_order_release); } else { while (v != 2) { - pthread_yield(); + internal_sched_yield(); v = atomic_load(a, memory_order_acquire); } if (!thr->in_ignored_lib) @@ -1274,62 +1394,7 @@ TSAN_INTERCEPTOR(int, pthread_once, void *o, void (*f)()) { return 0; } -TSAN_INTERCEPTOR(int, sem_init, void *s, int pshared, unsigned value) { - SCOPED_TSAN_INTERCEPTOR(sem_init, s, pshared, value); - int res = REAL(sem_init)(s, pshared, value); - return res; -} - -TSAN_INTERCEPTOR(int, sem_destroy, void *s) { - SCOPED_TSAN_INTERCEPTOR(sem_destroy, s); - int res = REAL(sem_destroy)(s); - return res; -} - -TSAN_INTERCEPTOR(int, sem_wait, void *s) { - SCOPED_TSAN_INTERCEPTOR(sem_wait, s); - int res = BLOCK_REAL(sem_wait)(s); - if (res == 0) { - Acquire(thr, pc, (uptr)s); - } - return res; -} - -TSAN_INTERCEPTOR(int, sem_trywait, void *s) { - SCOPED_TSAN_INTERCEPTOR(sem_trywait, s); - int res = BLOCK_REAL(sem_trywait)(s); - if (res == 0) { - Acquire(thr, pc, (uptr)s); - } - return res; -} - -TSAN_INTERCEPTOR(int, sem_timedwait, void *s, void *abstime) { - SCOPED_TSAN_INTERCEPTOR(sem_timedwait, s, abstime); - int res = BLOCK_REAL(sem_timedwait)(s, abstime); - if (res == 0) { - Acquire(thr, pc, (uptr)s); - } - return res; -} - -TSAN_INTERCEPTOR(int, sem_post, void *s) { - SCOPED_TSAN_INTERCEPTOR(sem_post, s); - Release(thr, pc, (uptr)s); - int res = REAL(sem_post)(s); - return res; -} - -TSAN_INTERCEPTOR(int, sem_getvalue, void *s, int *sval) { - SCOPED_TSAN_INTERCEPTOR(sem_getvalue, s, sval); - int res = REAL(sem_getvalue)(s, sval); - if (res == 0) { - Acquire(thr, pc, (uptr)s); - } - return res; -} - -#if !SANITIZER_FREEBSD +#if SANITIZER_LINUX && !SANITIZER_ANDROID TSAN_INTERCEPTOR(int, __xstat, int version, const char *path, void *buf) { SCOPED_TSAN_INTERCEPTOR(__xstat, version, path, buf); READ_STRING(thr, pc, path, 0); @@ -1341,7 +1406,7 @@ TSAN_INTERCEPTOR(int, __xstat, int version, const char *path, void *buf) { #endif TSAN_INTERCEPTOR(int, stat, const char *path, void *buf) { -#if SANITIZER_FREEBSD +#if SANITIZER_FREEBSD || SANITIZER_MAC || SANITIZER_ANDROID SCOPED_TSAN_INTERCEPTOR(stat, path, buf); READ_STRING(thr, pc, path, 0); return REAL(stat)(path, buf); @@ -1352,7 +1417,7 @@ TSAN_INTERCEPTOR(int, stat, const char *path, void *buf) { #endif } -#if !SANITIZER_FREEBSD +#if SANITIZER_LINUX && !SANITIZER_ANDROID TSAN_INTERCEPTOR(int, __xstat64, int version, const char *path, void *buf) { SCOPED_TSAN_INTERCEPTOR(__xstat64, version, path, buf); READ_STRING(thr, pc, path, 0); @@ -1363,7 +1428,7 @@ TSAN_INTERCEPTOR(int, __xstat64, int version, const char *path, void *buf) { #define TSAN_MAYBE_INTERCEPT___XSTAT64 #endif -#if !SANITIZER_FREEBSD +#if SANITIZER_LINUX && !SANITIZER_ANDROID TSAN_INTERCEPTOR(int, stat64, const char *path, void *buf) { SCOPED_TSAN_INTERCEPTOR(__xstat64, 0, path, buf); READ_STRING(thr, pc, path, 0); @@ -1374,7 +1439,7 @@ TSAN_INTERCEPTOR(int, stat64, const char *path, void *buf) { #define TSAN_MAYBE_INTERCEPT_STAT64 #endif -#if !SANITIZER_FREEBSD +#if SANITIZER_LINUX && !SANITIZER_ANDROID TSAN_INTERCEPTOR(int, __lxstat, int version, const char *path, void *buf) { SCOPED_TSAN_INTERCEPTOR(__lxstat, version, path, buf); READ_STRING(thr, pc, path, 0); @@ -1386,7 +1451,7 @@ TSAN_INTERCEPTOR(int, __lxstat, int version, const char *path, void *buf) { #endif TSAN_INTERCEPTOR(int, lstat, const char *path, void *buf) { -#if SANITIZER_FREEBSD +#if SANITIZER_FREEBSD || SANITIZER_MAC || SANITIZER_ANDROID SCOPED_TSAN_INTERCEPTOR(lstat, path, buf); READ_STRING(thr, pc, path, 0); return REAL(lstat)(path, buf); @@ -1397,7 +1462,7 @@ TSAN_INTERCEPTOR(int, lstat, const char *path, void *buf) { #endif } -#if !SANITIZER_FREEBSD +#if SANITIZER_LINUX && !SANITIZER_ANDROID TSAN_INTERCEPTOR(int, __lxstat64, int version, const char *path, void *buf) { SCOPED_TSAN_INTERCEPTOR(__lxstat64, version, path, buf); READ_STRING(thr, pc, path, 0); @@ -1408,7 +1473,7 @@ TSAN_INTERCEPTOR(int, __lxstat64, int version, const char *path, void *buf) { #define TSAN_MAYBE_INTERCEPT___LXSTAT64 #endif -#if !SANITIZER_FREEBSD +#if SANITIZER_LINUX && !SANITIZER_ANDROID TSAN_INTERCEPTOR(int, lstat64, const char *path, void *buf) { SCOPED_TSAN_INTERCEPTOR(__lxstat64, 0, path, buf); READ_STRING(thr, pc, path, 0); @@ -1419,7 +1484,7 @@ TSAN_INTERCEPTOR(int, lstat64, const char *path, void *buf) { #define TSAN_MAYBE_INTERCEPT_LSTAT64 #endif -#if !SANITIZER_FREEBSD +#if SANITIZER_LINUX && !SANITIZER_ANDROID TSAN_INTERCEPTOR(int, __fxstat, int version, int fd, void *buf) { SCOPED_TSAN_INTERCEPTOR(__fxstat, version, fd, buf); if (fd > 0) @@ -1432,7 +1497,7 @@ TSAN_INTERCEPTOR(int, __fxstat, int version, int fd, void *buf) { #endif TSAN_INTERCEPTOR(int, fstat, int fd, void *buf) { -#if SANITIZER_FREEBSD +#if SANITIZER_FREEBSD || SANITIZER_MAC || SANITIZER_ANDROID SCOPED_TSAN_INTERCEPTOR(fstat, fd, buf); if (fd > 0) FdAccess(thr, pc, fd); @@ -1445,7 +1510,7 @@ TSAN_INTERCEPTOR(int, fstat, int fd, void *buf) { #endif } -#if !SANITIZER_FREEBSD +#if SANITIZER_LINUX && !SANITIZER_ANDROID TSAN_INTERCEPTOR(int, __fxstat64, int version, int fd, void *buf) { SCOPED_TSAN_INTERCEPTOR(__fxstat64, version, fd, buf); if (fd > 0) @@ -1457,7 +1522,7 @@ TSAN_INTERCEPTOR(int, __fxstat64, int version, int fd, void *buf) { #define TSAN_MAYBE_INTERCEPT___FXSTAT64 #endif -#if !SANITIZER_FREEBSD +#if SANITIZER_LINUX && !SANITIZER_ANDROID TSAN_INTERCEPTOR(int, fstat64, int fd, void *buf) { SCOPED_TSAN_INTERCEPTOR(__fxstat64, 0, fd, buf); if (fd > 0) @@ -1478,7 +1543,7 @@ TSAN_INTERCEPTOR(int, open, const char *name, int flags, int mode) { return fd; } -#if !SANITIZER_FREEBSD +#if SANITIZER_LINUX TSAN_INTERCEPTOR(int, open64, const char *name, int flags, int mode) { SCOPED_TSAN_INTERCEPTOR(open64, name, flags, mode); READ_STRING(thr, pc, name, 0); @@ -1501,7 +1566,7 @@ TSAN_INTERCEPTOR(int, creat, const char *name, int mode) { return fd; } -#if !SANITIZER_FREEBSD +#if SANITIZER_LINUX TSAN_INTERCEPTOR(int, creat64, const char *name, int mode) { SCOPED_TSAN_INTERCEPTOR(creat64, name, mode); READ_STRING(thr, pc, name, 0); @@ -1531,6 +1596,7 @@ TSAN_INTERCEPTOR(int, dup2, int oldfd, int newfd) { return newfd2; } +#if !SANITIZER_MAC TSAN_INTERCEPTOR(int, dup3, int oldfd, int newfd, int flags) { SCOPED_TSAN_INTERCEPTOR(dup3, oldfd, newfd, flags); int newfd2 = REAL(dup3)(oldfd, newfd, flags); @@ -1538,8 +1604,9 @@ TSAN_INTERCEPTOR(int, dup3, int oldfd, int newfd, int flags) { FdDup(thr, pc, oldfd, newfd2, false); return newfd2; } +#endif -#if !SANITIZER_FREEBSD +#if SANITIZER_LINUX TSAN_INTERCEPTOR(int, eventfd, unsigned initval, int flags) { SCOPED_TSAN_INTERCEPTOR(eventfd, initval, flags); int fd = REAL(eventfd)(initval, flags); @@ -1552,7 +1619,7 @@ TSAN_INTERCEPTOR(int, eventfd, unsigned initval, int flags) { #define TSAN_MAYBE_INTERCEPT_EVENTFD #endif -#if !SANITIZER_FREEBSD +#if SANITIZER_LINUX TSAN_INTERCEPTOR(int, signalfd, int fd, void *mask, int flags) { SCOPED_TSAN_INTERCEPTOR(signalfd, fd, mask, flags); if (fd >= 0) @@ -1567,7 +1634,7 @@ TSAN_INTERCEPTOR(int, signalfd, int fd, void *mask, int flags) { #define TSAN_MAYBE_INTERCEPT_SIGNALFD #endif -#if !SANITIZER_FREEBSD +#if SANITIZER_LINUX TSAN_INTERCEPTOR(int, inotify_init, int fake) { SCOPED_TSAN_INTERCEPTOR(inotify_init, fake); int fd = REAL(inotify_init)(fake); @@ -1580,7 +1647,7 @@ TSAN_INTERCEPTOR(int, inotify_init, int fake) { #define TSAN_MAYBE_INTERCEPT_INOTIFY_INIT #endif -#if !SANITIZER_FREEBSD +#if SANITIZER_LINUX TSAN_INTERCEPTOR(int, inotify_init1, int flags) { SCOPED_TSAN_INTERCEPTOR(inotify_init1, flags); int fd = REAL(inotify_init1)(flags); @@ -1634,7 +1701,7 @@ TSAN_INTERCEPTOR(int, listen, int fd, int backlog) { return res; } -#if !SANITIZER_FREEBSD +#if SANITIZER_LINUX TSAN_INTERCEPTOR(int, epoll_create, int size) { SCOPED_TSAN_INTERCEPTOR(epoll_create, size); int fd = REAL(epoll_create)(size); @@ -1647,7 +1714,7 @@ TSAN_INTERCEPTOR(int, epoll_create, int size) { #define TSAN_MAYBE_INTERCEPT_EPOLL_CREATE #endif -#if !SANITIZER_FREEBSD +#if SANITIZER_LINUX TSAN_INTERCEPTOR(int, epoll_create1, int flags) { SCOPED_TSAN_INTERCEPTOR(epoll_create1, flags); int fd = REAL(epoll_create1)(flags); @@ -1667,7 +1734,7 @@ TSAN_INTERCEPTOR(int, close, int fd) { return REAL(close)(fd); } -#if !SANITIZER_FREEBSD +#if SANITIZER_LINUX TSAN_INTERCEPTOR(int, __close, int fd) { SCOPED_TSAN_INTERCEPTOR(__close, fd); if (fd >= 0) @@ -1680,7 +1747,7 @@ TSAN_INTERCEPTOR(int, __close, int fd) { #endif // glibc guts -#if !SANITIZER_FREEBSD +#if SANITIZER_LINUX && !SANITIZER_ANDROID TSAN_INTERCEPTOR(void, __res_iclose, void *state, bool free_addr) { SCOPED_TSAN_INTERCEPTOR(__res_iclose, state, free_addr); int fds[64]; @@ -1704,6 +1771,7 @@ TSAN_INTERCEPTOR(int, pipe, int *pipefd) { return res; } +#if !SANITIZER_MAC TSAN_INTERCEPTOR(int, pipe2, int *pipefd, int flags) { SCOPED_TSAN_INTERCEPTOR(pipe2, pipefd, flags); int res = REAL(pipe2)(pipefd, flags); @@ -1711,6 +1779,7 @@ TSAN_INTERCEPTOR(int, pipe2, int *pipefd, int flags) { FdPipeCreate(thr, pc, pipefd[0], pipefd[1]); return res; } +#endif TSAN_INTERCEPTOR(long_t, send, int fd, void *buf, long_t len, int flags) { SCOPED_TSAN_INTERCEPTOR(send, fd, buf, len, flags); @@ -1761,7 +1830,7 @@ TSAN_INTERCEPTOR(void*, tmpfile, int fake) { return res; } -#if !SANITIZER_FREEBSD +#if SANITIZER_LINUX TSAN_INTERCEPTOR(void*, tmpfile64, int fake) { SCOPED_TSAN_INTERCEPTOR(tmpfile64, fake); void *res = REAL(tmpfile64)(fake); @@ -1823,12 +1892,14 @@ TSAN_INTERCEPTOR(int, rmdir, char *path) { TSAN_INTERCEPTOR(int, closedir, void *dirp) { SCOPED_TSAN_INTERCEPTOR(closedir, dirp); - int fd = dirfd(dirp); - FdClose(thr, pc, fd); + if (dirp) { + int fd = dirfd(dirp); + FdClose(thr, pc, fd); + } return REAL(closedir)(dirp); } -#if !SANITIZER_FREEBSD +#if SANITIZER_LINUX TSAN_INTERCEPTOR(int, epoll_ctl, int epfd, int op, int fd, void *ev) { SCOPED_TSAN_INTERCEPTOR(epoll_ctl, epfd, op, fd, ev); if (epfd >= 0) @@ -1845,7 +1916,7 @@ TSAN_INTERCEPTOR(int, epoll_ctl, int epfd, int op, int fd, void *ev) { #define TSAN_MAYBE_INTERCEPT_EPOLL_CTL #endif -#if !SANITIZER_FREEBSD +#if SANITIZER_LINUX TSAN_INTERCEPTOR(int, epoll_wait, int epfd, void *ev, int cnt, int timeout) { SCOPED_TSAN_INTERCEPTOR(epoll_wait, epfd, ev, cnt, timeout); if (epfd >= 0) @@ -1895,7 +1966,7 @@ static void CallUserSignalHandler(ThreadState *thr, bool sync, bool acquire, ObtainCurrentStack(thr, StackTrace::GetNextInstructionPc(pc), &stack); ThreadRegistryLock l(ctx->thread_registry); ScopedReport rep(ReportTypeErrnoInSignal); - if (!IsFiredSuppression(ctx, rep, stack)) { + if (!IsFiredSuppression(ctx, ReportTypeErrnoInSignal, stack)) { rep.AddStack(stack, true); OutputReport(thr, rep); } @@ -1910,10 +1981,8 @@ void ProcessPendingSignals(ThreadState *thr) { return; atomic_store(&sctx->have_pending_signals, 0, memory_order_relaxed); atomic_fetch_add(&thr->in_signal_handler, 1, memory_order_relaxed); - // These are too big for stack. - static THREADLOCAL __sanitizer_sigset_t emptyset, oldset; - CHECK_EQ(0, REAL(sigfillset)(&emptyset)); - CHECK_EQ(0, pthread_sigmask(SIG_SETMASK, &emptyset, &oldset)); + CHECK_EQ(0, REAL(sigfillset)(&sctx->emptyset)); + CHECK_EQ(0, pthread_sigmask(SIG_SETMASK, &sctx->emptyset, &sctx->oldset)); for (int sig = 0; sig < kSigCount; sig++) { SignalDesc *signal = &sctx->pending_signals[sig]; if (signal->armed) { @@ -1922,7 +1991,7 @@ void ProcessPendingSignals(ThreadState *thr) { &signal->siginfo, &signal->ctx); } } - CHECK_EQ(0, pthread_sigmask(SIG_SETMASK, &oldset, 0)); + CHECK_EQ(0, pthread_sigmask(SIG_SETMASK, &sctx->oldset, 0)); atomic_fetch_add(&thr->in_signal_handler, -1, memory_order_relaxed); } @@ -2011,7 +2080,7 @@ TSAN_INTERCEPTOR(int, sigaction, int sig, sigaction_t *act, sigaction_t *old) { sigactions[sig].sa_flags = *(volatile int*)&act->sa_flags; internal_memcpy(&sigactions[sig].sa_mask, &act->sa_mask, sizeof(sigactions[sig].sa_mask)); -#if !SANITIZER_FREEBSD +#if !SANITIZER_FREEBSD && !SANITIZER_MAC sigactions[sig].sa_restorer = act->sa_restorer; #endif sigaction_t newact; @@ -2144,6 +2213,7 @@ TSAN_INTERCEPTOR(int, vfork, int fake) { return WRAP(fork)(fake); } +#if !SANITIZER_MAC && !SANITIZER_ANDROID typedef int (*dl_iterate_phdr_cb_t)(__sanitizer_dl_phdr_info *info, SIZE_T size, void *data); struct dl_iterate_phdr_data { @@ -2187,6 +2257,7 @@ TSAN_INTERCEPTOR(int, dl_iterate_phdr, dl_iterate_phdr_cb_t cb, void *data) { int res = REAL(dl_iterate_phdr)(dl_iterate_phdr_cb, &cbdata); return res; } +#endif static int OnExit(ThreadState *thr) { int status = Finalize(thr); @@ -2200,6 +2271,7 @@ struct TsanInterceptorContext { const uptr pc; }; +#if !SANITIZER_MAC static void HandleRecvmsg(ThreadState *thr, uptr pc, __sanitizer_msghdr *msg) { int fds[64]; @@ -2207,6 +2279,7 @@ static void HandleRecvmsg(ThreadState *thr, uptr pc, for (int i = 0; i < cnt; i++) FdEventCreate(thr, pc, fds[i]); } +#endif #include "sanitizer_common/sanitizer_platform_interceptors.h" // Causes interceptor recursion (getaddrinfo() and fopen()) @@ -2277,6 +2350,12 @@ static void HandleRecvmsg(ThreadState *thr, uptr pc, #define COMMON_INTERCEPTOR_LIBRARY_UNLOADED() \ libignore()->OnLibraryUnloaded() +#define COMMON_INTERCEPTOR_ACQUIRE(ctx, u) \ + Acquire(((TsanInterceptorContext *) ctx)->thr, pc, u) + +#define COMMON_INTERCEPTOR_RELEASE(ctx, u) \ + Release(((TsanInterceptorContext *) ctx)->thr, pc, u) + #define COMMON_INTERCEPTOR_DIR_ACQUIRE(ctx, path) \ Acquire(((TsanInterceptorContext *) ctx)->thr, pc, Dir2addr(path)) @@ -2315,9 +2394,11 @@ static void HandleRecvmsg(ThreadState *thr, uptr pc, MutexRepair(((TsanInterceptorContext *)ctx)->thr, \ ((TsanInterceptorContext *)ctx)->pc, (uptr)m) +#if !SANITIZER_MAC #define COMMON_INTERCEPTOR_HANDLE_RECVMSG(ctx, msg) \ HandleRecvmsg(((TsanInterceptorContext *)ctx)->thr, \ ((TsanInterceptorContext *)ctx)->pc, msg) +#endif #define COMMON_INTERCEPTOR_GET_TLS_RANGE(begin, end) \ if (TsanThread *t = GetCurrentThread()) { \ @@ -2349,6 +2430,7 @@ struct ScopedSyscall { } }; +#if !SANITIZER_MAC static void syscall_access_range(uptr pc, uptr p, uptr s, bool write) { TSAN_SYSCALL(); MemoryAccessRange(thr, pc, p, s, write); @@ -2402,6 +2484,7 @@ static void syscall_post_fork(uptr pc, int pid) { ForkParentAfter(thr, pc); } } +#endif #define COMMON_SYSCALL_PRE_READ_RANGE(p, s) \ syscall_access_range(GET_CALLER_PC(), (uptr)(p), (uptr)(s), false) @@ -2449,28 +2532,32 @@ static void finalize(void *arg) { // Make sure the output is not lost. FlushStreams(); if (status) - REAL(_exit)(status); + Die(); } +#if !SANITIZER_MAC && !SANITIZER_ANDROID static void unreachable() { Report("FATAL: ThreadSanitizer: unreachable called\n"); Die(); } +#endif void InitializeInterceptors() { +#if !SANITIZER_MAC // We need to setup it early, because functions like dlsym() can call it. REAL(memset) = internal_memset; REAL(memcpy) = internal_memcpy; - REAL(memcmp) = internal_memcmp; +#endif // Instruct libc malloc to consume less memory. -#if !SANITIZER_FREEBSD +#if SANITIZER_LINUX mallopt(1, 0); // M_MXFAST mallopt(-3, 32*1024); // M_MMAP_THRESHOLD #endif InitializeCommonInterceptors(); +#if !SANITIZER_MAC // We can not use TSAN_INTERCEPT to get setjmp addr, // because it does &setjmp and setjmp is not present in some versions of libc. using __interception::GetRealFunctionAddress; @@ -2478,6 +2565,7 @@ void InitializeInterceptors() { GetRealFunctionAddress("_setjmp", (uptr*)&REAL(_setjmp), 0, 0); GetRealFunctionAddress("sigsetjmp", (uptr*)&REAL(sigsetjmp), 0, 0); GetRealFunctionAddress("__sigsetjmp", (uptr*)&REAL(__sigsetjmp), 0, 0); +#endif TSAN_INTERCEPT(longjmp); TSAN_INTERCEPT(siglongjmp); @@ -2500,7 +2588,6 @@ void InitializeInterceptors() { TSAN_INTERCEPT(memset); TSAN_INTERCEPT(memcpy); TSAN_INTERCEPT(memmove); - TSAN_INTERCEPT(memcmp); TSAN_INTERCEPT(strchr); TSAN_INTERCEPT(strchrnul); TSAN_INTERCEPT(strrchr); @@ -2512,12 +2599,12 @@ void InitializeInterceptors() { TSAN_INTERCEPT(pthread_join); TSAN_INTERCEPT(pthread_detach); - TSAN_INTERCEPT_VER(pthread_cond_init, "GLIBC_2.3.2"); - TSAN_INTERCEPT_VER(pthread_cond_signal, "GLIBC_2.3.2"); - TSAN_INTERCEPT_VER(pthread_cond_broadcast, "GLIBC_2.3.2"); - TSAN_INTERCEPT_VER(pthread_cond_wait, "GLIBC_2.3.2"); - TSAN_INTERCEPT_VER(pthread_cond_timedwait, "GLIBC_2.3.2"); - TSAN_INTERCEPT_VER(pthread_cond_destroy, "GLIBC_2.3.2"); + TSAN_INTERCEPT_VER(pthread_cond_init, PTHREAD_ABI_BASE); + TSAN_INTERCEPT_VER(pthread_cond_signal, PTHREAD_ABI_BASE); + TSAN_INTERCEPT_VER(pthread_cond_broadcast, PTHREAD_ABI_BASE); + TSAN_INTERCEPT_VER(pthread_cond_wait, PTHREAD_ABI_BASE); + TSAN_INTERCEPT_VER(pthread_cond_timedwait, PTHREAD_ABI_BASE); + TSAN_INTERCEPT_VER(pthread_cond_destroy, PTHREAD_ABI_BASE); TSAN_INTERCEPT(pthread_mutex_init); TSAN_INTERCEPT(pthread_mutex_destroy); @@ -2546,14 +2633,6 @@ void InitializeInterceptors() { TSAN_INTERCEPT(pthread_once); - TSAN_INTERCEPT(sem_init); - TSAN_INTERCEPT(sem_destroy); - TSAN_INTERCEPT(sem_wait); - TSAN_INTERCEPT(sem_trywait); - TSAN_INTERCEPT(sem_timedwait); - TSAN_INTERCEPT(sem_post); - TSAN_INTERCEPT(sem_getvalue); - TSAN_INTERCEPT(stat); TSAN_MAYBE_INTERCEPT___XSTAT; TSAN_MAYBE_INTERCEPT_STAT64; @@ -2621,25 +2700,68 @@ void InitializeInterceptors() { TSAN_INTERCEPT(fork); TSAN_INTERCEPT(vfork); +#if !SANITIZER_ANDROID TSAN_INTERCEPT(dl_iterate_phdr); +#endif TSAN_INTERCEPT(on_exit); TSAN_INTERCEPT(__cxa_atexit); TSAN_INTERCEPT(_exit); +#if !SANITIZER_MAC && !SANITIZER_ANDROID // 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. REAL(atexit) = (int(*)(void(*)()))unreachable; +#endif + if (REAL(__cxa_atexit)(&finalize, 0, 0)) { Printf("ThreadSanitizer: failed to setup atexit callback\n"); Die(); } +#if !SANITIZER_MAC if (pthread_key_create(&g_thread_finalize_key, &thread_finalize)) { Printf("ThreadSanitizer: failed to create thread key\n"); Die(); } +#endif FdInit(); } } // namespace __tsan + +// Invisible barrier for tests. +// There were several unsuccessful iterations for this functionality: +// 1. Initially it was implemented in user code using +// REAL(pthread_barrier_wait). But pthread_barrier_wait is not supported on +// MacOS. Futexes are linux-specific for this matter. +// 2. Then we switched to atomics+usleep(10). But usleep produced parasitic +// "as-if synchronized via sleep" messages in reports which failed some +// output tests. +// 3. Then we switched to atomics+sched_yield. But this produced tons of tsan- +// visible events, which lead to "failed to restore stack trace" failures. +// Note that no_sanitize_thread attribute does not turn off atomic interception +// so attaching it to the function defined in user code does not help. +// That's why we now have what we have. +extern "C" SANITIZER_INTERFACE_ATTRIBUTE +void __tsan_testonly_barrier_init(u64 *barrier, u32 count) { + if (count >= (1 << 8)) { + Printf("barrier_init: count is too large (%d)\n", count); + Die(); + } + // 8 lsb is thread count, the remaining are count of entered threads. + *barrier = count; +} + +extern "C" SANITIZER_INTERFACE_ATTRIBUTE +void __tsan_testonly_barrier_wait(u64 *barrier) { + unsigned old = __atomic_fetch_add(barrier, 1 << 8, __ATOMIC_RELAXED); + unsigned old_epoch = (old >> 8) / (old & 0xff); + for (;;) { + unsigned cur = __atomic_load_n(barrier, __ATOMIC_RELAXED); + unsigned cur_epoch = (cur >> 8) / (cur & 0xff); + if (cur_epoch != old_epoch) + return; + internal_sched_yield(); + } +} diff --git a/contrib/compiler-rt/lib/tsan/rtl/tsan_interceptors.h b/contrib/compiler-rt/lib/tsan/rtl/tsan_interceptors.h index 49b79a7c5f9e..d831620cfafe 100644 --- a/contrib/compiler-rt/lib/tsan/rtl/tsan_interceptors.h +++ b/contrib/compiler-rt/lib/tsan/rtl/tsan_interceptors.h @@ -10,6 +10,8 @@ class ScopedInterceptor { public: ScopedInterceptor(ThreadState *thr, const char *fname, uptr pc); ~ScopedInterceptor(); + void UserCallbackStart(); + void UserCallbackEnd(); private: ThreadState *const thr_; const uptr pc_; @@ -26,6 +28,24 @@ class ScopedInterceptor { (void)pc; \ /**/ +#define SCOPED_TSAN_INTERCEPTOR(func, ...) \ + SCOPED_INTERCEPTOR_RAW(func, __VA_ARGS__); \ + if (REAL(func) == 0) { \ + Report("FATAL: ThreadSanitizer: failed to intercept %s\n", #func); \ + Die(); \ + } \ + if (thr->ignore_interceptors || thr->in_ignored_lib) \ + return REAL(func)(__VA_ARGS__); \ +/**/ + +#define SCOPED_TSAN_INTERCEPTOR_USER_CALLBACK_START() \ + si.UserCallbackStart(); + +#define SCOPED_TSAN_INTERCEPTOR_USER_CALLBACK_END() \ + si.UserCallbackEnd(); + +#define TSAN_INTERCEPTOR(ret, func, ...) INTERCEPTOR(ret, func, __VA_ARGS__) + #if SANITIZER_FREEBSD #define __libc_free __free #define __libc_malloc __malloc diff --git a/contrib/compiler-rt/lib/tsan/rtl/tsan_interceptors_mac.cc b/contrib/compiler-rt/lib/tsan/rtl/tsan_interceptors_mac.cc new file mode 100644 index 000000000000..2bf7ad9861c4 --- /dev/null +++ b/contrib/compiler-rt/lib/tsan/rtl/tsan_interceptors_mac.cc @@ -0,0 +1,91 @@ +//===-- tsan_interceptors_mac.cc ------------------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file is a part of ThreadSanitizer (TSan), a race detector. +// +// Mac-specific interceptors. +//===----------------------------------------------------------------------===// + +#include "sanitizer_common/sanitizer_platform.h" +#if SANITIZER_MAC + +#include "interception/interception.h" +#include "tsan_interceptors.h" + +#include <libkern/OSAtomic.h> + +namespace __tsan { + +TSAN_INTERCEPTOR(void, OSSpinLockLock, volatile OSSpinLock *lock) { + CHECK(!cur_thread()->is_dead); + if (!cur_thread()->is_inited) { + return REAL(OSSpinLockLock)(lock); + } + SCOPED_TSAN_INTERCEPTOR(OSSpinLockLock, lock); + REAL(OSSpinLockLock)(lock); + Acquire(thr, pc, (uptr)lock); +} + +TSAN_INTERCEPTOR(bool, OSSpinLockTry, volatile OSSpinLock *lock) { + CHECK(!cur_thread()->is_dead); + if (!cur_thread()->is_inited) { + return REAL(OSSpinLockTry)(lock); + } + SCOPED_TSAN_INTERCEPTOR(OSSpinLockTry, lock); + bool result = REAL(OSSpinLockTry)(lock); + if (result) + Acquire(thr, pc, (uptr)lock); + return result; +} + +TSAN_INTERCEPTOR(void, OSSpinLockUnlock, volatile OSSpinLock *lock) { + CHECK(!cur_thread()->is_dead); + if (!cur_thread()->is_inited) { + return REAL(OSSpinLockUnlock)(lock); + } + SCOPED_TSAN_INTERCEPTOR(OSSpinLockUnlock, lock); + Release(thr, pc, (uptr)lock); + REAL(OSSpinLockUnlock)(lock); +} + +TSAN_INTERCEPTOR(void, os_lock_lock, void *lock) { + CHECK(!cur_thread()->is_dead); + if (!cur_thread()->is_inited) { + return REAL(os_lock_lock)(lock); + } + SCOPED_TSAN_INTERCEPTOR(os_lock_lock, lock); + REAL(os_lock_lock)(lock); + Acquire(thr, pc, (uptr)lock); +} + +TSAN_INTERCEPTOR(bool, os_lock_trylock, void *lock) { + CHECK(!cur_thread()->is_dead); + if (!cur_thread()->is_inited) { + return REAL(os_lock_trylock)(lock); + } + SCOPED_TSAN_INTERCEPTOR(os_lock_trylock, lock); + bool result = REAL(os_lock_trylock)(lock); + if (result) + Acquire(thr, pc, (uptr)lock); + return result; +} + +TSAN_INTERCEPTOR(void, os_lock_unlock, void *lock) { + CHECK(!cur_thread()->is_dead); + if (!cur_thread()->is_inited) { + return REAL(os_lock_unlock)(lock); + } + SCOPED_TSAN_INTERCEPTOR(os_lock_unlock, lock); + Release(thr, pc, (uptr)lock); + REAL(os_lock_unlock)(lock); +} + +} // namespace __tsan + +#endif // SANITIZER_MAC diff --git a/contrib/compiler-rt/lib/tsan/rtl/tsan_interface_ann.cc b/contrib/compiler-rt/lib/tsan/rtl/tsan_interface_ann.cc index fd3c846678f5..62db79661625 100644 --- a/contrib/compiler-rt/lib/tsan/rtl/tsan_interface_ann.cc +++ b/contrib/compiler-rt/lib/tsan/rtl/tsan_interface_ann.cc @@ -63,8 +63,8 @@ static const int kMaxDescLen = 128; struct ExpectRace { ExpectRace *next; ExpectRace *prev; - int hitcount; - int addcount; + atomic_uintptr_t hitcount; + atomic_uintptr_t addcount; uptr addr; uptr size; char *file; @@ -90,7 +90,8 @@ static void AddExpectRace(ExpectRace *list, ExpectRace *race = list->next; for (; race != list; race = race->next) { if (race->addr == addr && race->size == size) { - race->addcount++; + atomic_store_relaxed(&race->addcount, + atomic_load_relaxed(&race->addcount) + 1); return; } } @@ -100,8 +101,8 @@ static void AddExpectRace(ExpectRace *list, race->file = f; race->line = l; race->desc[0] = 0; - race->hitcount = 0; - race->addcount = 1; + atomic_store_relaxed(&race->hitcount, 0); + atomic_store_relaxed(&race->addcount, 1); if (desc) { int i = 0; for (; i < kMaxDescLen - 1 && desc[i]; i++) @@ -130,7 +131,7 @@ static bool CheckContains(ExpectRace *list, uptr addr, uptr size) { return false; DPrintf("Hit expected/benign race: %s addr=%zx:%d %s:%d\n", race->desc, race->addr, (int)race->size, race->file, race->line); - race->hitcount++; + atomic_fetch_add(&race->hitcount, 1, memory_order_relaxed); return true; } @@ -146,7 +147,7 @@ void InitializeDynamicAnnotations() { } bool IsExpectedReport(uptr addr, uptr size) { - Lock lock(&dyn_ann_ctx->mtx); + ReadLock lock(&dyn_ann_ctx->mtx); if (CheckContains(&dyn_ann_ctx->expect, addr, size)) return true; if (CheckContains(&dyn_ann_ctx->benign, addr, size)) @@ -155,20 +156,21 @@ bool IsExpectedReport(uptr addr, uptr size) { } static void CollectMatchedBenignRaces(Vector<ExpectRace> *matched, - int *unique_count, int *hit_count, int ExpectRace::*counter) { + int *unique_count, int *hit_count, atomic_uintptr_t ExpectRace::*counter) { ExpectRace *list = &dyn_ann_ctx->benign; for (ExpectRace *race = list->next; race != list; race = race->next) { (*unique_count)++; - if (race->*counter == 0) + const uptr cnt = atomic_load_relaxed(&(race->*counter)); + if (cnt == 0) continue; - (*hit_count) += race->*counter; + *hit_count += cnt; uptr i = 0; for (; i < matched->Size(); i++) { ExpectRace *race0 = &(*matched)[i]; if (race->line == race0->line && internal_strcmp(race->file, race0->file) == 0 && internal_strcmp(race->desc, race0->desc) == 0) { - race0->*counter += race->*counter; + atomic_fetch_add(&(race0->*counter), cnt, memory_order_relaxed); break; } } @@ -193,8 +195,8 @@ void PrintMatchedBenignRaces() { hit_count, (int)internal_getpid()); for (uptr i = 0; i < hit_matched.Size(); i++) { Printf("%d %s:%d %s\n", - hit_matched[i].hitcount, hit_matched[i].file, - hit_matched[i].line, hit_matched[i].desc); + atomic_load_relaxed(&hit_matched[i].hitcount), + hit_matched[i].file, hit_matched[i].line, hit_matched[i].desc); } } if (hit_matched.Size()) { @@ -203,8 +205,8 @@ void PrintMatchedBenignRaces() { add_count, unique_count, (int)internal_getpid()); for (uptr i = 0; i < add_matched.Size(); i++) { Printf("%d %s:%d %s\n", - add_matched[i].addcount, add_matched[i].file, - add_matched[i].line, add_matched[i].desc); + atomic_load_relaxed(&add_matched[i].addcount), + add_matched[i].file, add_matched[i].line, add_matched[i].desc); } } } @@ -303,7 +305,7 @@ void INTERFACE_ATTRIBUTE AnnotateFlushExpectedRaces(char *f, int l) { Lock lock(&dyn_ann_ctx->mtx); while (dyn_ann_ctx->expect.next != &dyn_ann_ctx->expect) { ExpectRace *race = dyn_ann_ctx->expect.next; - if (race->hitcount == 0) { + if (atomic_load_relaxed(&race->hitcount) == 0) { ctx->nmissed_expected++; ReportMissedExpectedRace(race); } diff --git a/contrib/compiler-rt/lib/tsan/rtl/tsan_libdispatch_mac.cc b/contrib/compiler-rt/lib/tsan/rtl/tsan_libdispatch_mac.cc new file mode 100644 index 000000000000..617dc91b33d0 --- /dev/null +++ b/contrib/compiler-rt/lib/tsan/rtl/tsan_libdispatch_mac.cc @@ -0,0 +1,284 @@ +//===-- tsan_libdispatch_mac.cc -------------------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file is a part of ThreadSanitizer (TSan), a race detector. +// +// Mac-specific libdispatch (GCD) support. +//===----------------------------------------------------------------------===// + +#include "sanitizer_common/sanitizer_platform.h" +#if SANITIZER_MAC + +#include "sanitizer_common/sanitizer_common.h" +#include "interception/interception.h" +#include "tsan_interceptors.h" +#include "tsan_platform.h" +#include "tsan_rtl.h" + +#include <Block.h> +#include <dispatch/dispatch.h> +#include <pthread.h> + +typedef long long_t; // NOLINT + +namespace __tsan { + +typedef struct { + dispatch_queue_t queue; + void *orig_context; + dispatch_function_t orig_work; + uptr object_to_acquire; + dispatch_object_t object_to_release; +} tsan_block_context_t; + +// The offsets of different fields of the dispatch_queue_t structure, exported +// by libdispatch.dylib. +extern "C" struct dispatch_queue_offsets_s { + const uint16_t dqo_version; + const uint16_t dqo_label; + const uint16_t dqo_label_size; + const uint16_t dqo_flags; + const uint16_t dqo_flags_size; + const uint16_t dqo_serialnum; + const uint16_t dqo_serialnum_size; + const uint16_t dqo_width; + const uint16_t dqo_width_size; + const uint16_t dqo_running; + const uint16_t dqo_running_size; + const uint16_t dqo_suspend_cnt; + const uint16_t dqo_suspend_cnt_size; + const uint16_t dqo_target_queue; + const uint16_t dqo_target_queue_size; + const uint16_t dqo_priority; + const uint16_t dqo_priority_size; +} dispatch_queue_offsets; + +static bool IsQueueSerial(dispatch_queue_t q) { + CHECK_EQ(dispatch_queue_offsets.dqo_width_size, 2); + uptr width = *(uint16_t *)(((uptr)q) + dispatch_queue_offsets.dqo_width); + CHECK_NE(width, 0); + return width == 1; +} + +static tsan_block_context_t *AllocContext(ThreadState *thr, uptr pc, + dispatch_queue_t queue, + void *orig_context, + dispatch_function_t orig_work) { + tsan_block_context_t *new_context = + (tsan_block_context_t *)user_alloc(thr, pc, sizeof(tsan_block_context_t)); + new_context->queue = queue; + new_context->orig_context = orig_context; + new_context->orig_work = orig_work; + new_context->object_to_acquire = (uptr)new_context; + new_context->object_to_release = nullptr; + return new_context; +} + +static void dispatch_callback_wrap_acquire(void *param) { + SCOPED_INTERCEPTOR_RAW(dispatch_async_f_callback_wrap); + tsan_block_context_t *context = (tsan_block_context_t *)param; + Acquire(thr, pc, context->object_to_acquire); + + // Extra retain/release is required for dispatch groups. We use the group + // itself to synchronize, but in a notification (dispatch_group_notify + // callback), it may be disposed already. To solve this, we retain the group + // and release it here. + if (context->object_to_release) dispatch_release(context->object_to_release); + + // In serial queues, work items can be executed on different threads, we need + // to explicitly synchronize on the queue itself. + if (IsQueueSerial(context->queue)) Acquire(thr, pc, (uptr)context->queue); + SCOPED_TSAN_INTERCEPTOR_USER_CALLBACK_START(); + context->orig_work(context->orig_context); + SCOPED_TSAN_INTERCEPTOR_USER_CALLBACK_END(); + if (IsQueueSerial(context->queue)) Release(thr, pc, (uptr)context->queue); + user_free(thr, pc, context); +} + +static void invoke_and_release_block(void *param) { + dispatch_block_t block = (dispatch_block_t)param; + block(); + Block_release(block); +} + +#define DISPATCH_INTERCEPT_B(name) \ + TSAN_INTERCEPTOR(void, name, dispatch_queue_t q, dispatch_block_t block) { \ + SCOPED_TSAN_INTERCEPTOR(name, q, block); \ + SCOPED_TSAN_INTERCEPTOR_USER_CALLBACK_START(); \ + dispatch_block_t heap_block = Block_copy(block); \ + SCOPED_TSAN_INTERCEPTOR_USER_CALLBACK_END(); \ + tsan_block_context_t *new_context = \ + AllocContext(thr, pc, q, heap_block, &invoke_and_release_block); \ + Release(thr, pc, (uptr)new_context); \ + SCOPED_TSAN_INTERCEPTOR_USER_CALLBACK_START(); \ + REAL(name##_f)(q, new_context, dispatch_callback_wrap_acquire); \ + SCOPED_TSAN_INTERCEPTOR_USER_CALLBACK_END(); \ + } + +#define DISPATCH_INTERCEPT_F(name) \ + TSAN_INTERCEPTOR(void, name, dispatch_queue_t q, void *context, \ + dispatch_function_t work) { \ + SCOPED_TSAN_INTERCEPTOR(name, q, context, work); \ + tsan_block_context_t *new_context = \ + AllocContext(thr, pc, q, context, work); \ + Release(thr, pc, (uptr)new_context); \ + SCOPED_TSAN_INTERCEPTOR_USER_CALLBACK_START(); \ + REAL(name)(q, new_context, dispatch_callback_wrap_acquire); \ + SCOPED_TSAN_INTERCEPTOR_USER_CALLBACK_END(); \ + } + +// We wrap dispatch_async, dispatch_sync and friends where we allocate a new +// context, which is used to synchronize (we release the context before +// submitting, and the callback acquires it before executing the original +// callback). +DISPATCH_INTERCEPT_B(dispatch_async) +DISPATCH_INTERCEPT_B(dispatch_barrier_async) +DISPATCH_INTERCEPT_F(dispatch_async_f) +DISPATCH_INTERCEPT_F(dispatch_barrier_async_f) +DISPATCH_INTERCEPT_B(dispatch_sync) +DISPATCH_INTERCEPT_B(dispatch_barrier_sync) +DISPATCH_INTERCEPT_F(dispatch_sync_f) +DISPATCH_INTERCEPT_F(dispatch_barrier_sync_f) + +// GCD's dispatch_once implementation has a fast path that contains a racy read +// and it's inlined into user's code. Furthermore, this fast path doesn't +// establish a proper happens-before relations between the initialization and +// code following the call to dispatch_once. We could deal with this in +// instrumented code, but there's not much we can do about it in system +// libraries. Let's disable the fast path (by never storing the value ~0 to +// predicate), so the interceptor is always called, and let's add proper release +// and acquire semantics. Since TSan does not see its own atomic stores, the +// race on predicate won't be reported - the only accesses to it that TSan sees +// are the loads on the fast path. Loads don't race. Secondly, dispatch_once is +// both a macro and a real function, we want to intercept the function, so we +// need to undefine the macro. +#undef dispatch_once +TSAN_INTERCEPTOR(void, dispatch_once, dispatch_once_t *predicate, + dispatch_block_t block) { + SCOPED_TSAN_INTERCEPTOR(dispatch_once, predicate, block); + atomic_uint32_t *a = reinterpret_cast<atomic_uint32_t *>(predicate); + u32 v = atomic_load(a, memory_order_acquire); + if (v == 0 && + atomic_compare_exchange_strong(a, &v, 1, memory_order_relaxed)) { + SCOPED_TSAN_INTERCEPTOR_USER_CALLBACK_START(); + block(); + SCOPED_TSAN_INTERCEPTOR_USER_CALLBACK_END(); + Release(thr, pc, (uptr)a); + atomic_store(a, 2, memory_order_release); + } else { + while (v != 2) { + internal_sched_yield(); + v = atomic_load(a, memory_order_acquire); + } + Acquire(thr, pc, (uptr)a); + } +} + +#undef dispatch_once_f +TSAN_INTERCEPTOR(void, dispatch_once_f, dispatch_once_t *predicate, + void *context, dispatch_function_t function) { + SCOPED_TSAN_INTERCEPTOR(dispatch_once_f, predicate, context, function); + SCOPED_TSAN_INTERCEPTOR_USER_CALLBACK_START(); + WRAP(dispatch_once)(predicate, ^(void) { + function(context); + }); + SCOPED_TSAN_INTERCEPTOR_USER_CALLBACK_END(); +} + +TSAN_INTERCEPTOR(long_t, dispatch_semaphore_signal, + dispatch_semaphore_t dsema) { + SCOPED_TSAN_INTERCEPTOR(dispatch_semaphore_signal, dsema); + Release(thr, pc, (uptr)dsema); + return REAL(dispatch_semaphore_signal)(dsema); +} + +TSAN_INTERCEPTOR(long_t, dispatch_semaphore_wait, dispatch_semaphore_t dsema, + dispatch_time_t timeout) { + SCOPED_TSAN_INTERCEPTOR(dispatch_semaphore_wait, dsema, timeout); + long_t result = REAL(dispatch_semaphore_wait)(dsema, timeout); + if (result == 0) Acquire(thr, pc, (uptr)dsema); + return result; +} + +TSAN_INTERCEPTOR(long_t, dispatch_group_wait, dispatch_group_t group, + dispatch_time_t timeout) { + SCOPED_TSAN_INTERCEPTOR(dispatch_group_wait, group, timeout); + long_t result = REAL(dispatch_group_wait)(group, timeout); + if (result == 0) Acquire(thr, pc, (uptr)group); + return result; +} + +TSAN_INTERCEPTOR(void, dispatch_group_leave, dispatch_group_t group) { + SCOPED_TSAN_INTERCEPTOR(dispatch_group_leave, group); + Release(thr, pc, (uptr)group); + REAL(dispatch_group_leave)(group); +} + +TSAN_INTERCEPTOR(void, dispatch_group_async, dispatch_group_t group, + dispatch_queue_t queue, dispatch_block_t block) { + SCOPED_TSAN_INTERCEPTOR(dispatch_group_async, group, queue, block); + dispatch_retain(group); + dispatch_group_enter(group); + WRAP(dispatch_async)(queue, ^(void) { + block(); + WRAP(dispatch_group_leave)(group); + dispatch_release(group); + }); +} + +TSAN_INTERCEPTOR(void, dispatch_group_async_f, dispatch_group_t group, + dispatch_queue_t queue, void *context, + dispatch_function_t work) { + SCOPED_TSAN_INTERCEPTOR(dispatch_group_async_f, group, queue, context, work); + dispatch_retain(group); + dispatch_group_enter(group); + WRAP(dispatch_async)(queue, ^(void) { + work(context); + WRAP(dispatch_group_leave)(group); + dispatch_release(group); + }); +} + +TSAN_INTERCEPTOR(void, dispatch_group_notify, dispatch_group_t group, + dispatch_queue_t q, dispatch_block_t block) { + SCOPED_TSAN_INTERCEPTOR(dispatch_group_notify, group, q, block); + SCOPED_TSAN_INTERCEPTOR_USER_CALLBACK_START(); + dispatch_block_t heap_block = Block_copy(block); + SCOPED_TSAN_INTERCEPTOR_USER_CALLBACK_END(); + tsan_block_context_t *new_context = + AllocContext(thr, pc, q, heap_block, &invoke_and_release_block); + new_context->object_to_acquire = (uptr)group; + + // Will be released in dispatch_callback_wrap_acquire. + new_context->object_to_release = group; + dispatch_retain(group); + + Release(thr, pc, (uptr)group); + REAL(dispatch_group_notify_f)(group, q, new_context, + dispatch_callback_wrap_acquire); +} + +TSAN_INTERCEPTOR(void, dispatch_group_notify_f, dispatch_group_t group, + dispatch_queue_t q, void *context, dispatch_function_t work) { + SCOPED_TSAN_INTERCEPTOR(dispatch_group_notify_f, group, q, context, work); + tsan_block_context_t *new_context = AllocContext(thr, pc, q, context, work); + new_context->object_to_acquire = (uptr)group; + + // Will be released in dispatch_callback_wrap_acquire. + new_context->object_to_release = group; + dispatch_retain(group); + + Release(thr, pc, (uptr)group); + REAL(dispatch_group_notify_f)(group, q, new_context, + dispatch_callback_wrap_acquire); +} + +} // namespace __tsan + +#endif // SANITIZER_MAC diff --git a/contrib/compiler-rt/lib/tsan/rtl/tsan_malloc_mac.cc b/contrib/compiler-rt/lib/tsan/rtl/tsan_malloc_mac.cc new file mode 100644 index 000000000000..7fd94273c314 --- /dev/null +++ b/contrib/compiler-rt/lib/tsan/rtl/tsan_malloc_mac.cc @@ -0,0 +1,65 @@ +//===-- tsan_malloc_mac.cc ------------------------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file is a part of ThreadSanitizer (TSan), a race detector. +// +// Mac-specific malloc interception. +//===----------------------------------------------------------------------===// + +#include "sanitizer_common/sanitizer_platform.h" +#if SANITIZER_MAC + +#include "tsan_interceptors.h" +#include "tsan_stack_trace.h" + +using namespace __tsan; +#define COMMON_MALLOC_ZONE_NAME "tsan" +#define COMMON_MALLOC_ENTER() +#define COMMON_MALLOC_SANITIZER_INITIALIZED (cur_thread()->is_inited) +#define COMMON_MALLOC_FORCE_LOCK() +#define COMMON_MALLOC_FORCE_UNLOCK() +#define COMMON_MALLOC_MEMALIGN(alignment, size) \ + void *p = \ + user_alloc(cur_thread(), StackTrace::GetCurrentPc(), size, alignment) +#define COMMON_MALLOC_MALLOC(size) \ + if (cur_thread()->in_symbolizer) \ + return REAL(malloc)(size); \ + SCOPED_INTERCEPTOR_RAW(malloc, size); \ + void *p = user_alloc(thr, pc, size) +#define COMMON_MALLOC_REALLOC(ptr, size) \ + if (cur_thread()->in_symbolizer) \ + return REAL(realloc)(ptr, size); \ + SCOPED_INTERCEPTOR_RAW(realloc, ptr, size); \ + void *p = user_realloc(thr, pc, ptr, size) +#define COMMON_MALLOC_CALLOC(count, size) \ + if (cur_thread()->in_symbolizer) \ + return REAL(calloc)(count, size); \ + SCOPED_INTERCEPTOR_RAW(calloc, size, count); \ + void *p = user_calloc(thr, pc, size, count) +#define COMMON_MALLOC_VALLOC(size) \ + if (cur_thread()->in_symbolizer) \ + return REAL(valloc)(size); \ + SCOPED_INTERCEPTOR_RAW(valloc, size); \ + void *p = user_alloc(thr, pc, size, GetPageSizeCached()) +#define COMMON_MALLOC_FREE(ptr) \ + if (cur_thread()->in_symbolizer) \ + return REAL(free)(ptr); \ + SCOPED_INTERCEPTOR_RAW(free, ptr); \ + user_free(thr, pc, ptr) +#define COMMON_MALLOC_SIZE(ptr) \ + uptr size = user_alloc_usable_size(ptr); +#define COMMON_MALLOC_FILL_STATS(zone, stats) +#define COMMON_MALLOC_REPORT_UNKNOWN_REALLOC(ptr, zone_ptr, zone_name) \ + (void)zone_name; \ + Report("mz_realloc(%p) -- attempting to realloc unallocated memory.\n", ptr); +#define COMMON_MALLOC_NAMESPACE __tsan + +#include "sanitizer_common/sanitizer_malloc_mac.inc" + +#endif diff --git a/contrib/compiler-rt/lib/tsan/rtl/tsan_mman.cc b/contrib/compiler-rt/lib/tsan/rtl/tsan_mman.cc index 12a616ff5ae8..7247c6e00035 100644 --- a/contrib/compiler-rt/lib/tsan/rtl/tsan_mman.cc +++ b/contrib/compiler-rt/lib/tsan/rtl/tsan_mman.cc @@ -19,12 +19,14 @@ #include "tsan_flags.h" // May be overriden by front-end. -extern "C" void WEAK __sanitizer_malloc_hook(void *ptr, uptr size) { +SANITIZER_WEAK_DEFAULT_IMPL +void __sanitizer_malloc_hook(void *ptr, uptr size) { (void)ptr; (void)size; } -extern "C" void WEAK __sanitizer_free_hook(void *ptr) { +SANITIZER_WEAK_DEFAULT_IMPL +void __sanitizer_free_hook(void *ptr) { (void)ptr; } @@ -80,17 +82,17 @@ void AllocatorPrintStats() { } static void SignalUnsafeCall(ThreadState *thr, uptr pc) { - if (atomic_load(&thr->in_signal_handler, memory_order_relaxed) == 0 || + if (atomic_load_relaxed(&thr->in_signal_handler) == 0 || !flags()->report_signal_unsafe) return; VarSizeStackTrace stack; ObtainCurrentStack(thr, pc, &stack); + if (IsFiredSuppression(ctx, ReportTypeSignalUnsafe, stack)) + return; ThreadRegistryLock l(ctx->thread_registry); ScopedReport rep(ReportTypeSignalUnsafe); - if (!IsFiredSuppression(ctx, rep, stack)) { - rep.AddStack(stack, true); - OutputReport(thr, rep); - } + rep.AddStack(stack, true); + OutputReport(thr, rep); } void *user_alloc(ThreadState *thr, uptr pc, uptr sz, uptr align, bool signal) { diff --git a/contrib/compiler-rt/lib/tsan/rtl/tsan_mman.h b/contrib/compiler-rt/lib/tsan/rtl/tsan_mman.h index 5ff956d827f6..b419b58ca457 100644 --- a/contrib/compiler-rt/lib/tsan/rtl/tsan_mman.h +++ b/contrib/compiler-rt/lib/tsan/rtl/tsan_mman.h @@ -20,6 +20,7 @@ namespace __tsan { const uptr kDefaultAlignment = 16; void InitializeAllocator(); +void ReplaceSystemMalloc(); void AllocatorThreadStart(ThreadState *thr); void AllocatorThreadFinish(ThreadState *thr); void AllocatorPrintStats(); diff --git a/contrib/compiler-rt/lib/tsan/rtl/tsan_mutex.cc b/contrib/compiler-rt/lib/tsan/rtl/tsan_mutex.cc index dc5a462a8081..9dd24803b183 100644 --- a/contrib/compiler-rt/lib/tsan/rtl/tsan_mutex.cc +++ b/contrib/compiler-rt/lib/tsan/rtl/tsan_mutex.cc @@ -41,6 +41,8 @@ static MutexType CanLockTab[MutexTypeCount][MutexTypeCount] = { /*9 MutexTypeMBlock*/ {MutexTypeSyncVar}, /*10 MutexTypeJavaMBlock*/ {MutexTypeSyncVar}, /*11 MutexTypeDDetector*/ {}, + /*12 MutexTypeFired*/ {MutexTypeLeaf}, + /*13 MutexTypeRacy*/ {MutexTypeLeaf}, }; static bool CanLockAdj[MutexTypeCount][MutexTypeCount]; diff --git a/contrib/compiler-rt/lib/tsan/rtl/tsan_mutex.h b/contrib/compiler-rt/lib/tsan/rtl/tsan_mutex.h index 88fad57c78a0..27f55385c959 100644 --- a/contrib/compiler-rt/lib/tsan/rtl/tsan_mutex.h +++ b/contrib/compiler-rt/lib/tsan/rtl/tsan_mutex.h @@ -32,6 +32,8 @@ enum MutexType { MutexTypeMBlock, MutexTypeJavaMBlock, MutexTypeDDetector, + MutexTypeFired, + MutexTypeRacy, // This must be the last. MutexTypeCount diff --git a/contrib/compiler-rt/lib/tsan/rtl/tsan_new_delete.cc b/contrib/compiler-rt/lib/tsan/rtl/tsan_new_delete.cc index 2d9d044e42fa..ebb422cf2023 100644 --- a/contrib/compiler-rt/lib/tsan/rtl/tsan_new_delete.cc +++ b/contrib/compiler-rt/lib/tsan/rtl/tsan_new_delete.cc @@ -11,6 +11,7 @@ // // Interceptors for operators new and delete. //===----------------------------------------------------------------------===// +#include "interception/interception.h" #include "sanitizer_common/sanitizer_internal_defs.h" #include "tsan_interceptors.h" @@ -20,6 +21,13 @@ namespace std { struct nothrow_t {}; } // namespace std +DECLARE_REAL(void *, malloc, uptr size) +DECLARE_REAL(void, free, void *ptr) +#if SANITIZER_MAC || SANITIZER_ANDROID +#define __libc_malloc REAL(malloc) +#define __libc_free REAL(free) +#endif + #define OPERATOR_NEW_BODY(mangled_name) \ if (cur_thread()->in_symbolizer) \ return __libc_malloc(size); \ @@ -64,14 +72,14 @@ void *operator new[](__sanitizer::uptr size, std::nothrow_t const&) { user_free(thr, pc, ptr); SANITIZER_INTERFACE_ATTRIBUTE -void operator delete(void *ptr) throw(); -void operator delete(void *ptr) throw() { +void operator delete(void *ptr) NOEXCEPT; +void operator delete(void *ptr) NOEXCEPT { OPERATOR_DELETE_BODY(_ZdlPv); } SANITIZER_INTERFACE_ATTRIBUTE -void operator delete[](void *ptr) throw(); -void operator delete[](void *ptr) throw() { +void operator delete[](void *ptr) NOEXCEPT; +void operator delete[](void *ptr) NOEXCEPT { OPERATOR_DELETE_BODY(_ZdaPv); } diff --git a/contrib/compiler-rt/lib/tsan/rtl/tsan_platform.h b/contrib/compiler-rt/lib/tsan/rtl/tsan_platform.h index 135e16027132..c2b487155300 100644 --- a/contrib/compiler-rt/lib/tsan/rtl/tsan_platform.h +++ b/contrib/compiler-rt/lib/tsan/rtl/tsan_platform.h @@ -41,21 +41,23 @@ C/C++ on linux/x86_64 and freebsd/x86_64 7e00 0000 0000 - 7e80 0000 0000: - 7e80 0000 0000 - 8000 0000 0000: modules and main thread stack */ -const uptr kMetaShadowBeg = 0x300000000000ull; -const uptr kMetaShadowEnd = 0x400000000000ull; -const uptr kTraceMemBeg = 0x600000000000ull; -const uptr kTraceMemEnd = 0x620000000000ull; -const uptr kShadowBeg = 0x020000000000ull; -const uptr kShadowEnd = 0x100000000000ull; -const uptr kHeapMemBeg = 0x7d0000000000ull; -const uptr kHeapMemEnd = 0x7e0000000000ull; -const uptr kLoAppMemBeg = 0x000000001000ull; -const uptr kLoAppMemEnd = 0x010000000000ull; -const uptr kHiAppMemBeg = 0x7e8000000000ull; -const uptr kHiAppMemEnd = 0x800000000000ull; -const uptr kAppMemMsk = 0x7c0000000000ull; -const uptr kAppMemXor = 0x020000000000ull; -const uptr kVdsoBeg = 0xf000000000000000ull; +struct Mapping { + static const uptr kMetaShadowBeg = 0x300000000000ull; + static const uptr kMetaShadowEnd = 0x400000000000ull; + static const uptr kTraceMemBeg = 0x600000000000ull; + static const uptr kTraceMemEnd = 0x620000000000ull; + static const uptr kShadowBeg = 0x020000000000ull; + static const uptr kShadowEnd = 0x100000000000ull; + static const uptr kHeapMemBeg = 0x7d0000000000ull; + static const uptr kHeapMemEnd = 0x7e0000000000ull; + static const uptr kLoAppMemBeg = 0x000000001000ull; + static const uptr kLoAppMemEnd = 0x010000000000ull; + static const uptr kHiAppMemBeg = 0x7e8000000000ull; + static const uptr kHiAppMemEnd = 0x800000000000ull; + static const uptr kAppMemMsk = 0x7c0000000000ull; + static const uptr kAppMemXor = 0x020000000000ull; + static const uptr kVdsoBeg = 0xf000000000000000ull; +}; #elif defined(__mips64) /* C/C++ on linux/mips64 @@ -71,69 +73,181 @@ fe00 0000 00 - ff00 0000 00: heap ff00 0000 00 - ff80 0000 00: - ff80 0000 00 - ffff ffff ff: modules and main thread stack */ -const uptr kMetaShadowBeg = 0x3000000000ull; -const uptr kMetaShadowEnd = 0x4000000000ull; -const uptr kTraceMemBeg = 0x6000000000ull; -const uptr kTraceMemEnd = 0x6200000000ull; -const uptr kShadowBeg = 0x1400000000ull; -const uptr kShadowEnd = 0x2400000000ull; -const uptr kHeapMemBeg = 0xfe00000000ull; -const uptr kHeapMemEnd = 0xff00000000ull; -const uptr kLoAppMemBeg = 0x0100000000ull; -const uptr kLoAppMemEnd = 0x0200000000ull; -const uptr kHiAppMemBeg = 0xff80000000ull; -const uptr kHiAppMemEnd = 0xffffffffffull; -const uptr kAppMemMsk = 0xfc00000000ull; -const uptr kAppMemXor = 0x0400000000ull; -const uptr kVdsoBeg = 0xfffff00000ull; -#endif - -ALWAYS_INLINE -bool IsAppMem(uptr mem) { - return (mem >= kHeapMemBeg && mem < kHeapMemEnd) || - (mem >= kLoAppMemBeg && mem < kLoAppMemEnd) || - (mem >= kHiAppMemBeg && mem < kHiAppMemEnd); -} - -ALWAYS_INLINE -bool IsShadowMem(uptr mem) { - return mem >= kShadowBeg && mem <= kShadowEnd; -} +struct Mapping { + static const uptr kMetaShadowBeg = 0x3000000000ull; + static const uptr kMetaShadowEnd = 0x4000000000ull; + static const uptr kTraceMemBeg = 0x6000000000ull; + static const uptr kTraceMemEnd = 0x6200000000ull; + static const uptr kShadowBeg = 0x1400000000ull; + static const uptr kShadowEnd = 0x2400000000ull; + static const uptr kHeapMemBeg = 0xfe00000000ull; + static const uptr kHeapMemEnd = 0xff00000000ull; + static const uptr kLoAppMemBeg = 0x0100000000ull; + static const uptr kLoAppMemEnd = 0x0200000000ull; + static const uptr kHiAppMemBeg = 0xff80000000ull; + static const uptr kHiAppMemEnd = 0xffffffffffull; + static const uptr kAppMemMsk = 0xfc00000000ull; + static const uptr kAppMemXor = 0x0400000000ull; + static const uptr kVdsoBeg = 0xfffff00000ull; +}; +#elif defined(__aarch64__) +// AArch64 supports multiple VMA which leads to multiple address transformation +// functions. To support these multiple VMAS transformations and mappings TSAN +// runtime for AArch64 uses an external memory read (vmaSize) to select which +// mapping to use. Although slower, it make a same instrumented binary run on +// multiple kernels. -ALWAYS_INLINE -bool IsMetaMem(uptr mem) { - return mem >= kMetaShadowBeg && mem <= kMetaShadowEnd; -} +/* +C/C++ on linux/aarch64 (39-bit VMA) +0000 0010 00 - 0100 0000 00: main binary +0100 0000 00 - 0800 0000 00: - +0800 0000 00 - 2000 0000 00: shadow memory +2000 0000 00 - 3100 0000 00: - +3100 0000 00 - 3400 0000 00: metainfo +3400 0000 00 - 5500 0000 00: - +5500 0000 00 - 5600 0000 00: main binary (PIE) +5600 0000 00 - 6000 0000 00: - +6000 0000 00 - 6200 0000 00: traces +6200 0000 00 - 7d00 0000 00: - +7c00 0000 00 - 7d00 0000 00: heap +7d00 0000 00 - 7fff ffff ff: modules and main thread stack +*/ +struct Mapping39 { + static const uptr kLoAppMemBeg = 0x0000001000ull; + static const uptr kLoAppMemEnd = 0x0100000000ull; + static const uptr kShadowBeg = 0x0800000000ull; + static const uptr kShadowEnd = 0x2000000000ull; + static const uptr kMetaShadowBeg = 0x3100000000ull; + static const uptr kMetaShadowEnd = 0x3400000000ull; + static const uptr kMidAppMemBeg = 0x5500000000ull; + static const uptr kMidAppMemEnd = 0x5600000000ull; + static const uptr kMidShadowOff = 0x5000000000ull; + static const uptr kTraceMemBeg = 0x6000000000ull; + static const uptr kTraceMemEnd = 0x6200000000ull; + static const uptr kHeapMemBeg = 0x7c00000000ull; + static const uptr kHeapMemEnd = 0x7d00000000ull; + static const uptr kHiAppMemBeg = 0x7e00000000ull; + static const uptr kHiAppMemEnd = 0x7fffffffffull; + static const uptr kAppMemMsk = 0x7800000000ull; + static const uptr kAppMemXor = 0x0200000000ull; + static const uptr kVdsoBeg = 0x7f00000000ull; +}; -ALWAYS_INLINE -uptr MemToShadow(uptr x) { - DCHECK(IsAppMem(x)); - return (((x) & ~(kAppMemMsk | (kShadowCell - 1))) - ^ kAppMemXor) * kShadowCnt; -} +/* +C/C++ on linux/aarch64 (42-bit VMA) +00000 0010 00 - 01000 0000 00: main binary +01000 0000 00 - 10000 0000 00: - +10000 0000 00 - 20000 0000 00: shadow memory +20000 0000 00 - 26000 0000 00: - +26000 0000 00 - 28000 0000 00: metainfo +28000 0000 00 - 2aa00 0000 00: - +2aa00 0000 00 - 2ab00 0000 00: main binary (PIE) +2ab00 0000 00 - 36200 0000 00: - +36200 0000 00 - 36240 0000 00: traces +36240 0000 00 - 3e000 0000 00: - +3e000 0000 00 - 3f000 0000 00: heap +3f000 0000 00 - 3ffff ffff ff: modules and main thread stack +*/ +struct Mapping42 { + static const uptr kLoAppMemBeg = 0x00000001000ull; + static const uptr kLoAppMemEnd = 0x01000000000ull; + static const uptr kShadowBeg = 0x10000000000ull; + static const uptr kShadowEnd = 0x20000000000ull; + static const uptr kMetaShadowBeg = 0x26000000000ull; + static const uptr kMetaShadowEnd = 0x28000000000ull; + static const uptr kMidAppMemBeg = 0x2aa00000000ull; + static const uptr kMidAppMemEnd = 0x2ab00000000ull; + static const uptr kMidShadowOff = 0x28000000000ull; + static const uptr kTraceMemBeg = 0x36200000000ull; + static const uptr kTraceMemEnd = 0x36400000000ull; + static const uptr kHeapMemBeg = 0x3e000000000ull; + static const uptr kHeapMemEnd = 0x3f000000000ull; + static const uptr kHiAppMemBeg = 0x3f000000000ull; + static const uptr kHiAppMemEnd = 0x3ffffffffffull; + static const uptr kAppMemMsk = 0x3c000000000ull; + static const uptr kAppMemXor = 0x04000000000ull; + static const uptr kVdsoBeg = 0x37f00000000ull; +}; -ALWAYS_INLINE -u32 *MemToMeta(uptr x) { - DCHECK(IsAppMem(x)); - return (u32*)(((((x) & ~(kAppMemMsk | (kMetaShadowCell - 1))) - ^ kAppMemXor) / kMetaShadowCell * kMetaShadowSize) | kMetaShadowBeg); -} +// Indicates the runtime will define the memory regions at runtime. +#define TSAN_RUNTIME_VMA 1 +// Indicates that mapping defines a mid range memory segment. +#define TSAN_MID_APP_RANGE 1 +#elif defined(__powerpc64__) +// PPC64 supports multiple VMA which leads to multiple address transformation +// functions. To support these multiple VMAS transformations and mappings TSAN +// runtime for PPC64 uses an external memory read (vmaSize) to select which +// mapping to use. Although slower, it make a same instrumented binary run on +// multiple kernels. -ALWAYS_INLINE -uptr ShadowToMem(uptr s) { - CHECK(IsShadowMem(s)); - if (s >= MemToShadow(kLoAppMemBeg) && s <= MemToShadow(kLoAppMemEnd - 1)) - return (s / kShadowCnt) ^ kAppMemXor; - else - return ((s / kShadowCnt) ^ kAppMemXor) | kAppMemMsk; -} +/* +C/C++ on linux/powerpc64 (44-bit VMA) +0000 0000 0100 - 0001 0000 0000: main binary +0001 0000 0000 - 0001 0000 0000: - +0001 0000 0000 - 0b00 0000 0000: shadow +0b00 0000 0000 - 0b00 0000 0000: - +0b00 0000 0000 - 0d00 0000 0000: metainfo (memory blocks and sync objects) +0d00 0000 0000 - 0d00 0000 0000: - +0d00 0000 0000 - 0f00 0000 0000: traces +0f00 0000 0000 - 0f00 0000 0000: - +0f00 0000 0000 - 0f50 0000 0000: heap +0f50 0000 0000 - 0f60 0000 0000: - +0f60 0000 0000 - 1000 0000 0000: modules and main thread stack +*/ +struct Mapping44 { + static const uptr kMetaShadowBeg = 0x0b0000000000ull; + static const uptr kMetaShadowEnd = 0x0d0000000000ull; + static const uptr kTraceMemBeg = 0x0d0000000000ull; + static const uptr kTraceMemEnd = 0x0f0000000000ull; + static const uptr kShadowBeg = 0x000100000000ull; + static const uptr kShadowEnd = 0x0b0000000000ull; + static const uptr kLoAppMemBeg = 0x000000000100ull; + static const uptr kLoAppMemEnd = 0x000100000000ull; + static const uptr kHeapMemBeg = 0x0f0000000000ull; + static const uptr kHeapMemEnd = 0x0f5000000000ull; + static const uptr kHiAppMemBeg = 0x0f6000000000ull; + static const uptr kHiAppMemEnd = 0x100000000000ull; // 44 bits + static const uptr kAppMemMsk = 0x0f0000000000ull; + static const uptr kAppMemXor = 0x002100000000ull; + static const uptr kVdsoBeg = 0x3c0000000000000ull; +}; -static USED uptr UserRegions[] = { - kLoAppMemBeg, kLoAppMemEnd, - kHiAppMemBeg, kHiAppMemEnd, - kHeapMemBeg, kHeapMemEnd, +/* +C/C++ on linux/powerpc64 (46-bit VMA) +0000 0000 1000 - 0100 0000 0000: main binary +0100 0000 0000 - 0200 0000 0000: - +0100 0000 0000 - 1000 0000 0000: shadow +1000 0000 0000 - 1000 0000 0000: - +1000 0000 0000 - 2000 0000 0000: metainfo (memory blocks and sync objects) +2000 0000 0000 - 2000 0000 0000: - +2000 0000 0000 - 2200 0000 0000: traces +2200 0000 0000 - 3d00 0000 0000: - +3d00 0000 0000 - 3e00 0000 0000: heap +3e00 0000 0000 - 3e80 0000 0000: - +3e80 0000 0000 - 4000 0000 0000: modules and main thread stack +*/ +struct Mapping46 { + static const uptr kMetaShadowBeg = 0x100000000000ull; + static const uptr kMetaShadowEnd = 0x200000000000ull; + static const uptr kTraceMemBeg = 0x200000000000ull; + static const uptr kTraceMemEnd = 0x220000000000ull; + static const uptr kShadowBeg = 0x010000000000ull; + static const uptr kShadowEnd = 0x100000000000ull; + static const uptr kHeapMemBeg = 0x3d0000000000ull; + static const uptr kHeapMemEnd = 0x3e0000000000ull; + static const uptr kLoAppMemBeg = 0x000000001000ull; + static const uptr kLoAppMemEnd = 0x010000000000ull; + static const uptr kHiAppMemBeg = 0x3e8000000000ull; + static const uptr kHiAppMemEnd = 0x400000000000ull; // 46 bits + static const uptr kAppMemMsk = 0x3c0000000000ull; + static const uptr kAppMemXor = 0x020000000000ull; + static const uptr kVdsoBeg = 0x7800000000000000ull; }; +// Indicates the runtime will define the memory regions at runtime. +#define TSAN_RUNTIME_VMA 1 +#endif + #elif defined(SANITIZER_GO) && !SANITIZER_WINDOWS /* Go on linux, darwin and freebsd @@ -149,138 +263,495 @@ static USED uptr UserRegions[] = { 6200 0000 0000 - 8000 0000 0000: - */ -const uptr kMetaShadowBeg = 0x300000000000ull; -const uptr kMetaShadowEnd = 0x400000000000ull; -const uptr kTraceMemBeg = 0x600000000000ull; -const uptr kTraceMemEnd = 0x620000000000ull; -const uptr kShadowBeg = 0x200000000000ull; -const uptr kShadowEnd = 0x238000000000ull; -const uptr kAppMemBeg = 0x000000001000ull; -const uptr kAppMemEnd = 0x00e000000000ull; +struct Mapping { + static const uptr kMetaShadowBeg = 0x300000000000ull; + static const uptr kMetaShadowEnd = 0x400000000000ull; + static const uptr kTraceMemBeg = 0x600000000000ull; + static const uptr kTraceMemEnd = 0x620000000000ull; + static const uptr kShadowBeg = 0x200000000000ull; + static const uptr kShadowEnd = 0x238000000000ull; + static const uptr kAppMemBeg = 0x000000001000ull; + static const uptr kAppMemEnd = 0x00e000000000ull; +}; + +#elif defined(SANITIZER_GO) && SANITIZER_WINDOWS + +/* Go on windows +0000 0000 1000 - 0000 1000 0000: executable +0000 1000 0000 - 00f8 0000 0000: - +00c0 0000 0000 - 00e0 0000 0000: heap +00e0 0000 0000 - 0100 0000 0000: - +0100 0000 0000 - 0500 0000 0000: shadow +0500 0000 0000 - 0560 0000 0000: - +0560 0000 0000 - 0760 0000 0000: traces +0760 0000 0000 - 07d0 0000 0000: metainfo (memory blocks and sync objects) +07d0 0000 0000 - 8000 0000 0000: - +*/ + +struct Mapping { + static const uptr kMetaShadowBeg = 0x076000000000ull; + static const uptr kMetaShadowEnd = 0x07d000000000ull; + static const uptr kTraceMemBeg = 0x056000000000ull; + static const uptr kTraceMemEnd = 0x076000000000ull; + static const uptr kShadowBeg = 0x010000000000ull; + static const uptr kShadowEnd = 0x050000000000ull; + static const uptr kAppMemBeg = 0x000000001000ull; + static const uptr kAppMemEnd = 0x00e000000000ull; +} + +#else +# error "Unknown platform" +#endif + + +#ifdef TSAN_RUNTIME_VMA +extern uptr vmaSize; +#endif + + +enum MappingType { + MAPPING_LO_APP_BEG, + MAPPING_LO_APP_END, + MAPPING_HI_APP_BEG, + MAPPING_HI_APP_END, +#ifdef TSAN_MID_APP_RANGE + MAPPING_MID_APP_BEG, + MAPPING_MID_APP_END, +#endif + MAPPING_HEAP_BEG, + MAPPING_HEAP_END, + MAPPING_APP_BEG, + MAPPING_APP_END, + MAPPING_SHADOW_BEG, + MAPPING_SHADOW_END, + MAPPING_META_SHADOW_BEG, + MAPPING_META_SHADOW_END, + MAPPING_TRACE_BEG, + MAPPING_TRACE_END, + MAPPING_VDSO_BEG, +}; + +template<typename Mapping, int Type> +uptr MappingImpl(void) { + switch (Type) { +#ifndef SANITIZER_GO + case MAPPING_LO_APP_BEG: return Mapping::kLoAppMemBeg; + case MAPPING_LO_APP_END: return Mapping::kLoAppMemEnd; +# ifdef TSAN_MID_APP_RANGE + case MAPPING_MID_APP_BEG: return Mapping::kMidAppMemBeg; + case MAPPING_MID_APP_END: return Mapping::kMidAppMemEnd; +# endif + case MAPPING_HI_APP_BEG: return Mapping::kHiAppMemBeg; + case MAPPING_HI_APP_END: return Mapping::kHiAppMemEnd; + case MAPPING_HEAP_BEG: return Mapping::kHeapMemBeg; + case MAPPING_HEAP_END: return Mapping::kHeapMemEnd; + case MAPPING_VDSO_BEG: return Mapping::kVdsoBeg; +#else + case MAPPING_APP_BEG: return Mapping::kAppMemBeg; + case MAPPING_APP_END: return Mapping::kAppMemEnd; +#endif + case MAPPING_SHADOW_BEG: return Mapping::kShadowBeg; + case MAPPING_SHADOW_END: return Mapping::kShadowEnd; + case MAPPING_META_SHADOW_BEG: return Mapping::kMetaShadowBeg; + case MAPPING_META_SHADOW_END: return Mapping::kMetaShadowEnd; + case MAPPING_TRACE_BEG: return Mapping::kTraceMemBeg; + case MAPPING_TRACE_END: return Mapping::kTraceMemEnd; + } +} + +template<int Type> +uptr MappingArchImpl(void) { +#ifdef __aarch64__ + if (vmaSize == 39) + return MappingImpl<Mapping39, Type>(); + else + return MappingImpl<Mapping42, Type>(); + DCHECK(0); +#elif defined(__powerpc64__) + if (vmaSize == 44) + return MappingImpl<Mapping44, Type>(); + else + return MappingImpl<Mapping46, Type>(); + DCHECK(0); +#else + return MappingImpl<Mapping, Type>(); +#endif +} +#ifndef SANITIZER_GO ALWAYS_INLINE -bool IsAppMem(uptr mem) { - return mem >= kAppMemBeg && mem < kAppMemEnd; +uptr LoAppMemBeg(void) { + return MappingArchImpl<MAPPING_LO_APP_BEG>(); +} +ALWAYS_INLINE +uptr LoAppMemEnd(void) { + return MappingArchImpl<MAPPING_LO_APP_END>(); } +#ifdef TSAN_MID_APP_RANGE ALWAYS_INLINE -bool IsShadowMem(uptr mem) { - return mem >= kShadowBeg && mem <= kShadowEnd; +uptr MidAppMemBeg(void) { + return MappingArchImpl<MAPPING_MID_APP_BEG>(); +} +ALWAYS_INLINE +uptr MidAppMemEnd(void) { + return MappingArchImpl<MAPPING_MID_APP_END>(); } +#endif ALWAYS_INLINE -bool IsMetaMem(uptr mem) { - return mem >= kMetaShadowBeg && mem <= kMetaShadowEnd; +uptr HeapMemBeg(void) { + return MappingArchImpl<MAPPING_HEAP_BEG>(); +} +ALWAYS_INLINE +uptr HeapMemEnd(void) { + return MappingArchImpl<MAPPING_HEAP_END>(); } ALWAYS_INLINE -uptr MemToShadow(uptr x) { - DCHECK(IsAppMem(x)); - return ((x & ~(kShadowCell - 1)) * kShadowCnt) | kShadowBeg; +uptr HiAppMemBeg(void) { + return MappingArchImpl<MAPPING_HI_APP_BEG>(); +} +ALWAYS_INLINE +uptr HiAppMemEnd(void) { + return MappingArchImpl<MAPPING_HI_APP_END>(); } ALWAYS_INLINE -u32 *MemToMeta(uptr x) { - DCHECK(IsAppMem(x)); - return (u32*)(((x & ~(kMetaShadowCell - 1)) / \ - kMetaShadowCell * kMetaShadowSize) | kMetaShadowBeg); +uptr VdsoBeg(void) { + return MappingArchImpl<MAPPING_VDSO_BEG>(); } +#else + ALWAYS_INLINE -uptr ShadowToMem(uptr s) { - CHECK(IsShadowMem(s)); - return (s & ~kShadowBeg) / kShadowCnt; +uptr AppMemBeg(void) { + return MappingArchImpl<MAPPING_APP_BEG>(); +} +ALWAYS_INLINE +uptr AppMemEnd(void) { + return MappingArchImpl<MAPPING_APP_END>(); } -static USED uptr UserRegions[] = { - kAppMemBeg, kAppMemEnd, -}; +#endif -#elif defined(SANITIZER_GO) && SANITIZER_WINDOWS +static inline +bool GetUserRegion(int i, uptr *start, uptr *end) { + switch (i) { + default: + return false; +#ifndef SANITIZER_GO + case 0: + *start = LoAppMemBeg(); + *end = LoAppMemEnd(); + return true; + case 1: + *start = HiAppMemBeg(); + *end = HiAppMemEnd(); + return true; + case 2: + *start = HeapMemBeg(); + *end = HeapMemEnd(); + return true; +# ifdef TSAN_MID_APP_RANGE + case 3: + *start = MidAppMemBeg(); + *end = MidAppMemEnd(); + return true; +# endif +#else + case 0: + *start = AppMemBeg(); + *end = AppMemEnd(); + return true; +#endif + } +} -/* Go on windows -0000 0000 1000 - 0000 1000 0000: executable -0000 1000 0000 - 00f8 0000 0000: - -00c0 0000 0000 - 00e0 0000 0000: heap -00e0 0000 0000 - 0100 0000 0000: - -0100 0000 0000 - 0500 0000 0000: shadow -0500 0000 0000 - 0560 0000 0000: - -0560 0000 0000 - 0760 0000 0000: traces -0760 0000 0000 - 07d0 0000 0000: metainfo (memory blocks and sync objects) -07d0 0000 0000 - 8000 0000 0000: - -*/ +ALWAYS_INLINE +uptr ShadowBeg(void) { + return MappingArchImpl<MAPPING_SHADOW_BEG>(); +} +ALWAYS_INLINE +uptr ShadowEnd(void) { + return MappingArchImpl<MAPPING_SHADOW_END>(); +} + +ALWAYS_INLINE +uptr MetaShadowBeg(void) { + return MappingArchImpl<MAPPING_META_SHADOW_BEG>(); +} +ALWAYS_INLINE +uptr MetaShadowEnd(void) { + return MappingArchImpl<MAPPING_META_SHADOW_END>(); +} + +ALWAYS_INLINE +uptr TraceMemBeg(void) { + return MappingArchImpl<MAPPING_TRACE_BEG>(); +} +ALWAYS_INLINE +uptr TraceMemEnd(void) { + return MappingArchImpl<MAPPING_TRACE_END>(); +} -const uptr kMetaShadowBeg = 0x076000000000ull; -const uptr kMetaShadowEnd = 0x07d000000000ull; -const uptr kTraceMemBeg = 0x056000000000ull; -const uptr kTraceMemEnd = 0x076000000000ull; -const uptr kShadowBeg = 0x010000000000ull; -const uptr kShadowEnd = 0x050000000000ull; -const uptr kAppMemBeg = 0x000000001000ull; -const uptr kAppMemEnd = 0x00e000000000ull; + +template<typename Mapping> +bool IsAppMemImpl(uptr mem) { +#ifndef SANITIZER_GO + return (mem >= Mapping::kHeapMemBeg && mem < Mapping::kHeapMemEnd) || +# ifdef TSAN_MID_APP_RANGE + (mem >= Mapping::kMidAppMemBeg && mem < Mapping::kMidAppMemEnd) || +# endif + (mem >= Mapping::kLoAppMemBeg && mem < Mapping::kLoAppMemEnd) || + (mem >= Mapping::kHiAppMemBeg && mem < Mapping::kHiAppMemEnd); +#else + return mem >= Mapping::kAppMemBeg && mem < Mapping::kAppMemEnd; +#endif +} ALWAYS_INLINE bool IsAppMem(uptr mem) { - return mem >= kAppMemBeg && mem < kAppMemEnd; +#ifdef __aarch64__ + if (vmaSize == 39) + return IsAppMemImpl<Mapping39>(mem); + else + return IsAppMemImpl<Mapping42>(mem); + DCHECK(0); +#elif defined(__powerpc64__) + if (vmaSize == 44) + return IsAppMemImpl<Mapping44>(mem); + else + return IsAppMemImpl<Mapping46>(mem); + DCHECK(0); +#else + return IsAppMemImpl<Mapping>(mem); +#endif +} + + +template<typename Mapping> +bool IsShadowMemImpl(uptr mem) { + return mem >= Mapping::kShadowBeg && mem <= Mapping::kShadowEnd; } ALWAYS_INLINE bool IsShadowMem(uptr mem) { - return mem >= kShadowBeg && mem <= kShadowEnd; +#ifdef __aarch64__ + if (vmaSize == 39) + return IsShadowMemImpl<Mapping39>(mem); + else + return IsShadowMemImpl<Mapping42>(mem); + DCHECK(0); +#elif defined(__powerpc64__) + if (vmaSize == 44) + return IsShadowMemImpl<Mapping44>(mem); + else + return IsShadowMemImpl<Mapping46>(mem); + DCHECK(0); +#else + return IsShadowMemImpl<Mapping>(mem); +#endif +} + + +template<typename Mapping> +bool IsMetaMemImpl(uptr mem) { + return mem >= Mapping::kMetaShadowBeg && mem <= Mapping::kMetaShadowEnd; } ALWAYS_INLINE bool IsMetaMem(uptr mem) { - return mem >= kMetaShadowBeg && mem <= kMetaShadowEnd; +#ifdef __aarch64__ + if (vmaSize == 39) + return IsMetaMemImpl<Mapping39>(mem); + else + return IsMetaMemImpl<Mapping42>(mem); + DCHECK(0); +#elif defined(__powerpc64__) + if (vmaSize == 44) + return IsMetaMemImpl<Mapping44>(mem); + else + return IsMetaMemImpl<Mapping46>(mem); + DCHECK(0); +#else + return IsMetaMemImpl<Mapping>(mem); +#endif } -ALWAYS_INLINE -uptr MemToShadow(uptr x) { + +template<typename Mapping> +uptr MemToShadowImpl(uptr x) { DCHECK(IsAppMem(x)); - return ((x & ~(kShadowCell - 1)) * kShadowCnt) + kShadowBeg; +#ifndef SANITIZER_GO + return (((x) & ~(Mapping::kAppMemMsk | (kShadowCell - 1))) + ^ Mapping::kAppMemXor) * kShadowCnt; +#else + return ((x & ~(kShadowCell - 1)) * kShadowCnt) | Mapping::kShadowBeg; +#endif } ALWAYS_INLINE -u32 *MemToMeta(uptr x) { +uptr MemToShadow(uptr x) { +#ifdef __aarch64__ + if (vmaSize == 39) + return MemToShadowImpl<Mapping39>(x); + else + return MemToShadowImpl<Mapping42>(x); + DCHECK(0); +#elif defined(__powerpc64__) + if (vmaSize == 44) + return MemToShadowImpl<Mapping44>(x); + else + return MemToShadowImpl<Mapping46>(x); + DCHECK(0); +#else + return MemToShadowImpl<Mapping>(x); +#endif +} + + +template<typename Mapping> +u32 *MemToMetaImpl(uptr x) { DCHECK(IsAppMem(x)); +#ifndef SANITIZER_GO + return (u32*)(((((x) & ~(Mapping::kAppMemMsk | (kMetaShadowCell - 1))) + ^ Mapping::kAppMemXor) / kMetaShadowCell * kMetaShadowSize) + | Mapping::kMetaShadowBeg); +#else return (u32*)(((x & ~(kMetaShadowCell - 1)) / \ - kMetaShadowCell * kMetaShadowSize) | kMetaShadowBeg); + kMetaShadowCell * kMetaShadowSize) | Mapping::kMetaShadowBeg); +#endif } ALWAYS_INLINE -uptr ShadowToMem(uptr s) { - CHECK(IsShadowMem(s)); - // FIXME(dvyukov): this is most likely wrong as the mapping is not bijection. - return (s - kShadowBeg) / kShadowCnt; +u32 *MemToMeta(uptr x) { +#ifdef __aarch64__ + if (vmaSize == 39) + return MemToMetaImpl<Mapping39>(x); + else + return MemToMetaImpl<Mapping42>(x); + DCHECK(0); +#elif defined(__powerpc64__) + if (vmaSize == 44) + return MemToMetaImpl<Mapping44>(x); + else + return MemToMetaImpl<Mapping46>(x); + DCHECK(0); +#else + return MemToMetaImpl<Mapping>(x); +#endif } -static USED uptr UserRegions[] = { - kAppMemBeg, kAppMemEnd, -}; +template<typename Mapping> +uptr ShadowToMemImpl(uptr s) { + DCHECK(IsShadowMem(s)); +#ifndef SANITIZER_GO + if (s >= MemToShadow(Mapping::kLoAppMemBeg) + && s <= MemToShadow(Mapping::kLoAppMemEnd - 1)) + return (s / kShadowCnt) ^ Mapping::kAppMemXor; +# ifdef TSAN_MID_APP_RANGE + if (s >= MemToShadow(Mapping::kMidAppMemBeg) + && s <= MemToShadow(Mapping::kMidAppMemEnd - 1)) + return ((s / kShadowCnt) ^ Mapping::kAppMemXor) + Mapping::kMidShadowOff; +# endif + else + return ((s / kShadowCnt) ^ Mapping::kAppMemXor) | Mapping::kAppMemMsk; #else -# error "Unknown platform" +# ifndef SANITIZER_WINDOWS + return (s & ~Mapping::kShadowBeg) / kShadowCnt; +# else + // FIXME(dvyukov): this is most likely wrong as the mapping is not bijection. + return (s - Mapping::kShadowBeg) / kShadowCnt; +# endif // SANITIZER_WINDOWS +#endif +} + +ALWAYS_INLINE +uptr ShadowToMem(uptr s) { +#ifdef __aarch64__ + if (vmaSize == 39) + return ShadowToMemImpl<Mapping39>(s); + else + return ShadowToMemImpl<Mapping42>(s); + DCHECK(0); +#elif defined(__powerpc64__) + if (vmaSize == 44) + return ShadowToMemImpl<Mapping44>(s); + else + return ShadowToMemImpl<Mapping46>(s); + DCHECK(0); +#else + return ShadowToMemImpl<Mapping>(s); #endif +} + + // The additional page is to catch shadow stack overflow as paging fault. // Windows wants 64K alignment for mmaps. const uptr kTotalTraceSize = (kTraceSize * sizeof(Event) + sizeof(Trace) + (64 << 10) + (64 << 10) - 1) & ~((64 << 10) - 1); -uptr ALWAYS_INLINE GetThreadTrace(int tid) { - uptr p = kTraceMemBeg + (uptr)tid * kTotalTraceSize; - DCHECK_LT(p, kTraceMemEnd); +template<typename Mapping> +uptr GetThreadTraceImpl(int tid) { + uptr p = Mapping::kTraceMemBeg + (uptr)tid * kTotalTraceSize; + DCHECK_LT(p, Mapping::kTraceMemEnd); return p; } -uptr ALWAYS_INLINE GetThreadTraceHeader(int tid) { - uptr p = kTraceMemBeg + (uptr)tid * kTotalTraceSize +ALWAYS_INLINE +uptr GetThreadTrace(int tid) { +#ifdef __aarch64__ + if (vmaSize == 39) + return GetThreadTraceImpl<Mapping39>(tid); + else + return GetThreadTraceImpl<Mapping42>(tid); + DCHECK(0); +#elif defined(__powerpc64__) + if (vmaSize == 44) + return GetThreadTraceImpl<Mapping44>(tid); + else + return GetThreadTraceImpl<Mapping46>(tid); + DCHECK(0); +#else + return GetThreadTraceImpl<Mapping>(tid); +#endif +} + + +template<typename Mapping> +uptr GetThreadTraceHeaderImpl(int tid) { + uptr p = Mapping::kTraceMemBeg + (uptr)tid * kTotalTraceSize + kTraceSize * sizeof(Event); - DCHECK_LT(p, kTraceMemEnd); + DCHECK_LT(p, Mapping::kTraceMemEnd); return p; } +ALWAYS_INLINE +uptr GetThreadTraceHeader(int tid) { +#ifdef __aarch64__ + if (vmaSize == 39) + return GetThreadTraceHeaderImpl<Mapping39>(tid); + else + return GetThreadTraceHeaderImpl<Mapping42>(tid); + DCHECK(0); +#elif defined(__powerpc64__) + if (vmaSize == 44) + return GetThreadTraceHeaderImpl<Mapping44>(tid); + else + return GetThreadTraceHeaderImpl<Mapping46>(tid); + DCHECK(0); +#else + return GetThreadTraceHeaderImpl<Mapping>(tid); +#endif +} + void InitializePlatform(); +void InitializePlatformEarly(); +void CheckAndProtect(); +void InitializeShadowMemoryPlatform(); void FlushShadowMemory(); void WriteMemoryProfile(char *buf, uptr buf_size, uptr nthread, uptr nlive); @@ -294,6 +765,8 @@ int call_pthread_cancel_with_cleanup(int(*fn)(void *c, void *m, void *abstime), void *c, void *m, void *abstime, void(*cleanup)(void *arg), void *arg); +void DestroyThreadState(); + } // namespace __tsan #endif // TSAN_PLATFORM_H diff --git a/contrib/compiler-rt/lib/tsan/rtl/tsan_platform_linux.cc b/contrib/compiler-rt/lib/tsan/rtl/tsan_platform_linux.cc index 1309058210ce..6602561186ce 100644 --- a/contrib/compiler-rt/lib/tsan/rtl/tsan_platform_linux.cc +++ b/contrib/compiler-rt/lib/tsan/rtl/tsan_platform_linux.cc @@ -67,6 +67,11 @@ namespace __tsan { static uptr g_data_start; static uptr g_data_end; +#ifdef TSAN_RUNTIME_VMA +// Runtime detected VMA size. +uptr vmaSize; +#endif + enum { MemTotal = 0, MemShadow = 1, @@ -82,29 +87,30 @@ enum { void FillProfileCallback(uptr p, uptr rss, bool file, uptr *mem, uptr stats_size) { mem[MemTotal] += rss; - if (p >= kShadowBeg && p < kShadowEnd) + if (p >= ShadowBeg() && p < ShadowEnd()) mem[MemShadow] += rss; - else if (p >= kMetaShadowBeg && p < kMetaShadowEnd) + else if (p >= MetaShadowBeg() && p < MetaShadowEnd()) mem[MemMeta] += rss; #ifndef SANITIZER_GO - else if (p >= kHeapMemBeg && p < kHeapMemEnd) + else if (p >= HeapMemBeg() && p < HeapMemEnd()) mem[MemHeap] += rss; - else if (p >= kLoAppMemBeg && p < kLoAppMemEnd) + else if (p >= LoAppMemBeg() && p < LoAppMemEnd()) mem[file ? MemFile : MemMmap] += rss; - else if (p >= kHiAppMemBeg && p < kHiAppMemEnd) + else if (p >= HiAppMemBeg() && p < HiAppMemEnd()) mem[file ? MemFile : MemMmap] += rss; #else - else if (p >= kAppMemBeg && p < kAppMemEnd) + else if (p >= AppMemBeg() && p < AppMemEnd()) mem[file ? MemFile : MemMmap] += rss; #endif - else if (p >= kTraceMemBeg && p < kTraceMemEnd) + else if (p >= TraceMemBeg() && p < TraceMemEnd()) mem[MemTrace] += rss; else mem[MemOther] += rss; } void WriteMemoryProfile(char *buf, uptr buf_size, uptr nthread, uptr nlive) { - uptr mem[MemCount] = {}; + uptr mem[MemCount]; + internal_memset(mem, 0, sizeof(mem[0]) * MemCount); __sanitizer::GetMemoryProfile(FillProfileCallback, mem, 7); StackDepotStats *stacks = StackDepotGetStats(); internal_snprintf(buf, buf_size, @@ -121,7 +127,7 @@ void WriteMemoryProfile(char *buf, uptr buf_size, uptr nthread, uptr nlive) { void FlushShadowMemoryCallback( const SuspendedThreadsList &suspended_threads_list, void *argument) { - FlushUnneededShadowMemory(kShadowBeg, kShadowEnd - kShadowBeg); + FlushUnneededShadowMemory(ShadowBeg(), ShadowEnd() - ShadowBeg()); } #endif @@ -132,17 +138,6 @@ void FlushShadowMemory() { } #ifndef SANITIZER_GO -static void ProtectRange(uptr beg, uptr end) { - CHECK_LE(beg, end); - if (beg == end) - return; - if (beg != (uptr)MmapNoAccess(beg, end - beg)) { - Printf("FATAL: ThreadSanitizer can not protect [%zx,%zx]\n", beg, end); - Printf("FATAL: Make sure you are not using unlimited stack\n"); - Die(); - } -} - // Mark shadow for .rodata sections with the special kShadowRodata marker. // Accesses to .rodata can't race, so this saves time, memory and trace space. static void MapRodata() { @@ -200,55 +195,7 @@ static void MapRodata() { internal_close(fd); } -void InitializeShadowMemory() { - // Map memory shadow. - uptr shadow = - (uptr)MmapFixedNoReserve(kShadowBeg, kShadowEnd - kShadowBeg, "shadow"); - if (shadow != kShadowBeg) { - Printf("FATAL: ThreadSanitizer can not mmap the shadow memory\n"); - Printf("FATAL: Make sure to compile with -fPIE and " - "to link with -pie (%p, %p).\n", shadow, kShadowBeg); - Die(); - } - // This memory range is used for thread stacks and large user mmaps. - // Frequently a thread uses only a small part of stack and similarly - // a program uses a small part of large mmap. On some programs - // we see 20% memory usage reduction without huge pages for this range. - // FIXME: don't use constants here. -#if defined(__x86_64__) - const uptr kMadviseRangeBeg = 0x7f0000000000ull; - const uptr kMadviseRangeSize = 0x010000000000ull; -#elif defined(__mips64) - const uptr kMadviseRangeBeg = 0xff00000000ull; - const uptr kMadviseRangeSize = 0x0100000000ull; -#endif - NoHugePagesInRegion(MemToShadow(kMadviseRangeBeg), - kMadviseRangeSize * kShadowMultiplier); - // Meta shadow is compressing and we don't flush it, - // so it makes sense to mark it as NOHUGEPAGE to not over-allocate memory. - // On one program it reduces memory consumption from 5GB to 2.5GB. - NoHugePagesInRegion(kMetaShadowBeg, kMetaShadowEnd - kMetaShadowBeg); - if (common_flags()->use_madv_dontdump) - DontDumpShadowMemory(kShadowBeg, kShadowEnd - kShadowBeg); - DPrintf("memory shadow: %zx-%zx (%zuGB)\n", - kShadowBeg, kShadowEnd, - (kShadowEnd - kShadowBeg) >> 30); - - // Map meta shadow. - uptr meta_size = kMetaShadowEnd - kMetaShadowBeg; - uptr meta = - (uptr)MmapFixedNoReserve(kMetaShadowBeg, meta_size, "meta shadow"); - if (meta != kMetaShadowBeg) { - Printf("FATAL: ThreadSanitizer can not mmap the shadow memory\n"); - Printf("FATAL: Make sure to compile with -fPIE and " - "to link with -pie (%p, %p).\n", meta, kMetaShadowBeg); - Die(); - } - if (common_flags()->use_madv_dontdump) - DontDumpShadowMemory(meta, meta_size); - DPrintf("meta shadow: %zx-%zx (%zuGB)\n", - meta, meta + meta_size, meta_size >> 30); - +void InitializeShadowMemoryPlatform() { MapRodata(); } @@ -292,32 +239,27 @@ static void InitDataSeg() { CHECK_LT((uptr)&g_data_start, g_data_end); } -static void CheckAndProtect() { - // Ensure that the binary is indeed compiled with -pie. - MemoryMappingLayout proc_maps(true); - uptr p, end; - while (proc_maps.Next(&p, &end, 0, 0, 0, 0)) { - if (IsAppMem(p)) - continue; - if (p >= kHeapMemEnd && - p < HeapEnd()) - continue; - if (p >= kVdsoBeg) // vdso - break; - Printf("FATAL: ThreadSanitizer: unexpected memory mapping %p-%p\n", p, end); +#endif // #ifndef SANITIZER_GO + +void InitializePlatformEarly() { +#ifdef TSAN_RUNTIME_VMA + vmaSize = + (MostSignificantSetBitIndex(GET_CURRENT_FRAME()) + 1); +#if defined(__aarch64__) + if (vmaSize != 39 && vmaSize != 42) { + Printf("FATAL: ThreadSanitizer: unsupported VMA range\n"); + Printf("FATAL: Found %d - Supported 39 and 42\n", vmaSize); Die(); } - - ProtectRange(kLoAppMemEnd, kShadowBeg); - ProtectRange(kShadowEnd, kMetaShadowBeg); - ProtectRange(kMetaShadowEnd, kTraceMemBeg); - // Memory for traces is mapped lazily in MapThreadTrace. - // Protect the whole range for now, so that user does not map something here. - ProtectRange(kTraceMemBeg, kTraceMemEnd); - ProtectRange(kTraceMemEnd, kHeapMemBeg); - ProtectRange(HeapEnd(), kHiAppMemBeg); +#elif defined(__powerpc64__) + if (vmaSize != 44 && vmaSize != 46) { + Printf("FATAL: ThreadSanitizer: unsupported VMA range\n"); + Printf("FATAL: Found %d - Supported 44 and 46\n", vmaSize); + Die(); + } +#endif +#endif } -#endif // #ifndef SANITIZER_GO void InitializePlatform() { DisableCoreDumperIfNecessary(); @@ -367,7 +309,7 @@ bool IsGlobalVar(uptr addr) { // This is required to properly "close" the fds, because we do not see internal // closes within glibc. The code is a pure hack. int ExtractResolvFDs(void *state, int *fds, int nfd) { -#if SANITIZER_LINUX +#if SANITIZER_LINUX && !SANITIZER_ANDROID int cnt = 0; __res_state *statp = (__res_state*)state; for (int i = 0; i < MAXNS && cnt < nfd; i++) { @@ -415,6 +357,10 @@ int call_pthread_cancel_with_cleanup(int(*fn)(void *c, void *m, } #endif +#ifndef SANITIZER_GO +void ReplaceSystemMalloc() { } +#endif + } // namespace __tsan #endif // SANITIZER_LINUX || SANITIZER_FREEBSD diff --git a/contrib/compiler-rt/lib/tsan/rtl/tsan_platform_mac.cc b/contrib/compiler-rt/lib/tsan/rtl/tsan_platform_mac.cc index b72d9b07ef35..31caf37dee5a 100644 --- a/contrib/compiler-rt/lib/tsan/rtl/tsan_platform_mac.cc +++ b/contrib/compiler-rt/lib/tsan/rtl/tsan_platform_mac.cc @@ -15,8 +15,10 @@ #include "sanitizer_common/sanitizer_platform.h" #if SANITIZER_MAC +#include "sanitizer_common/sanitizer_atomic.h" #include "sanitizer_common/sanitizer_common.h" #include "sanitizer_common/sanitizer_libc.h" +#include "sanitizer_common/sanitizer_posix.h" #include "sanitizer_common/sanitizer_procmaps.h" #include "tsan_platform.h" #include "tsan_rtl.h" @@ -40,6 +42,62 @@ namespace __tsan { +#ifndef SANITIZER_GO +static void *SignalSafeGetOrAllocate(uptr *dst, uptr size) { + atomic_uintptr_t *a = (atomic_uintptr_t *)dst; + void *val = (void *)atomic_load_relaxed(a); + atomic_signal_fence(memory_order_acquire); // Turns the previous load into + // acquire wrt signals. + if (UNLIKELY(val == nullptr)) { + val = (void *)internal_mmap(nullptr, size, PROT_READ | PROT_WRITE, + MAP_PRIVATE | MAP_ANON, -1, 0); + CHECK(val); + void *cmp = nullptr; + if (!atomic_compare_exchange_strong(a, (uintptr_t *)&cmp, (uintptr_t)val, + memory_order_acq_rel)) { + internal_munmap(val, size); + val = cmp; + } + } + return val; +} + +// On OS X, accessing TLVs via __thread or manually by using pthread_key_* is +// problematic, because there are several places where interceptors are called +// when TLVs are not accessible (early process startup, thread cleanup, ...). +// The following provides a "poor man's TLV" implementation, where we use the +// shadow memory of the pointer returned by pthread_self() to store a pointer to +// the ThreadState object. The main thread's ThreadState pointer is stored +// separately in a static variable, because we need to access it even before the +// shadow memory is set up. +static uptr main_thread_identity = 0; +static ThreadState *main_thread_state = nullptr; + +ThreadState *cur_thread() { + ThreadState **fake_tls; + uptr thread_identity = (uptr)pthread_self(); + if (thread_identity == main_thread_identity || main_thread_identity == 0) { + fake_tls = &main_thread_state; + } else { + fake_tls = (ThreadState **)MemToShadow(thread_identity); + } + ThreadState *thr = (ThreadState *)SignalSafeGetOrAllocate( + (uptr *)fake_tls, sizeof(ThreadState)); + return thr; +} + +// TODO(kuba.brecka): This is not async-signal-safe. In particular, we call +// munmap first and then clear `fake_tls`; if we receive a signal in between, +// handler will try to access the unmapped ThreadState. +void cur_thread_finalize() { + uptr thread_identity = (uptr)pthread_self(); + CHECK_NE(thread_identity, main_thread_identity); + ThreadState **fake_tls = (ThreadState **)MemToShadow(thread_identity); + internal_munmap(*fake_tls, sizeof(ThreadState)); + *fake_tls = nullptr; +} +#endif + uptr GetShadowMemoryConsumption() { return 0; } @@ -51,28 +109,62 @@ void WriteMemoryProfile(char *buf, uptr buf_size, uptr nthread, uptr nlive) { } #ifndef SANITIZER_GO -void InitializeShadowMemory() { - uptr shadow = (uptr)MmapFixedNoReserve(kShadowBeg, - kShadowEnd - kShadowBeg); - if (shadow != kShadowBeg) { - Printf("FATAL: ThreadSanitizer can not mmap the shadow memory\n"); - Printf("FATAL: Make sure to compile with -fPIE and " - "to link with -pie.\n"); - Die(); +void InitializeShadowMemoryPlatform() { } + +// On OS X, GCD worker threads are created without a call to pthread_create. We +// need to properly register these threads with ThreadCreate and ThreadStart. +// These threads don't have a parent thread, as they are created "spuriously". +// We're using a libpthread API that notifies us about a newly created thread. +// The `thread == pthread_self()` check indicates this is actually a worker +// thread. If it's just a regular thread, this hook is called on the parent +// thread. +typedef void (*pthread_introspection_hook_t)(unsigned int event, + pthread_t thread, void *addr, + size_t size); +extern "C" pthread_introspection_hook_t pthread_introspection_hook_install( + pthread_introspection_hook_t hook); +static const uptr PTHREAD_INTROSPECTION_THREAD_CREATE = 1; +static const uptr PTHREAD_INTROSPECTION_THREAD_TERMINATE = 3; +static pthread_introspection_hook_t prev_pthread_introspection_hook; +static void my_pthread_introspection_hook(unsigned int event, pthread_t thread, + void *addr, size_t size) { + if (event == PTHREAD_INTROSPECTION_THREAD_CREATE) { + if (thread == pthread_self()) { + // The current thread is a newly created GCD worker thread. + ThreadState *parent_thread_state = nullptr; // No parent. + int tid = ThreadCreate(parent_thread_state, 0, (uptr)thread, true); + CHECK_NE(tid, 0); + ThreadState *thr = cur_thread(); + ThreadStart(thr, tid, GetTid()); + } + } else if (event == PTHREAD_INTROSPECTION_THREAD_TERMINATE) { + if (thread == pthread_self()) { + ThreadState *thr = cur_thread(); + if (thr->tctx) { + DestroyThreadState(); + } + } } - if (common_flags()->use_madv_dontdump) - DontDumpShadowMemory(kShadowBeg, kShadowEnd - kShadowBeg); - DPrintf("kShadow %zx-%zx (%zuGB)\n", - kShadowBeg, kShadowEnd, - (kShadowEnd - kShadowBeg) >> 30); - DPrintf("kAppMem %zx-%zx (%zuGB)\n", - kAppMemBeg, kAppMemEnd, - (kAppMemEnd - kAppMemBeg) >> 30); + + if (prev_pthread_introspection_hook != nullptr) + prev_pthread_introspection_hook(event, thread, addr, size); } #endif +void InitializePlatformEarly() { +} + void InitializePlatform() { DisableCoreDumperIfNecessary(); +#ifndef SANITIZER_GO + CheckAndProtect(); + + CHECK_EQ(main_thread_identity, 0); + main_thread_identity = (uptr)pthread_self(); + + prev_pthread_introspection_hook = + pthread_introspection_hook_install(&my_pthread_introspection_hook); +#endif } #ifndef SANITIZER_GO @@ -91,6 +183,10 @@ int call_pthread_cancel_with_cleanup(int(*fn)(void *c, void *m, } #endif +bool IsGlobalVar(uptr addr) { + return false; +} + } // namespace __tsan #endif // SANITIZER_MAC diff --git a/contrib/compiler-rt/lib/tsan/rtl/tsan_platform_posix.cc b/contrib/compiler-rt/lib/tsan/rtl/tsan_platform_posix.cc new file mode 100644 index 000000000000..90476cbc5fd5 --- /dev/null +++ b/contrib/compiler-rt/lib/tsan/rtl/tsan_platform_posix.cc @@ -0,0 +1,151 @@ +//===-- tsan_platform_posix.cc --------------------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file is a part of ThreadSanitizer (TSan), a race detector. +// +// POSIX-specific code. +//===----------------------------------------------------------------------===// + +#include "sanitizer_common/sanitizer_platform.h" +#if SANITIZER_POSIX + +#include "sanitizer_common/sanitizer_common.h" +#include "sanitizer_common/sanitizer_libc.h" +#include "sanitizer_common/sanitizer_procmaps.h" +#include "tsan_platform.h" +#include "tsan_rtl.h" + +namespace __tsan { + +#ifndef SANITIZER_GO +void InitializeShadowMemory() { + // Map memory shadow. + uptr shadow = + (uptr)MmapFixedNoReserve(ShadowBeg(), ShadowEnd() - ShadowBeg(), + "shadow"); + if (shadow != ShadowBeg()) { + Printf("FATAL: ThreadSanitizer can not mmap the shadow memory\n"); + Printf("FATAL: Make sure to compile with -fPIE and " + "to link with -pie (%p, %p).\n", shadow, ShadowBeg()); + Die(); + } + // This memory range is used for thread stacks and large user mmaps. + // Frequently a thread uses only a small part of stack and similarly + // a program uses a small part of large mmap. On some programs + // we see 20% memory usage reduction without huge pages for this range. + // FIXME: don't use constants here. +#if defined(__x86_64__) + const uptr kMadviseRangeBeg = 0x7f0000000000ull; + const uptr kMadviseRangeSize = 0x010000000000ull; +#elif defined(__mips64) + const uptr kMadviseRangeBeg = 0xff00000000ull; + const uptr kMadviseRangeSize = 0x0100000000ull; +#elif defined(__aarch64__) + uptr kMadviseRangeBeg = 0; + uptr kMadviseRangeSize = 0; + if (vmaSize == 39) { + kMadviseRangeBeg = 0x7d00000000ull; + kMadviseRangeSize = 0x0300000000ull; + } else if (vmaSize == 42) { + kMadviseRangeBeg = 0x3f000000000ull; + kMadviseRangeSize = 0x01000000000ull; + } else { + DCHECK(0); + } +#elif defined(__powerpc64__) + uptr kMadviseRangeBeg = 0; + uptr kMadviseRangeSize = 0; + if (vmaSize == 44) { + kMadviseRangeBeg = 0x0f60000000ull; + kMadviseRangeSize = 0x0010000000ull; + } else if (vmaSize == 46) { + kMadviseRangeBeg = 0x3f0000000000ull; + kMadviseRangeSize = 0x010000000000ull; + } else { + DCHECK(0); + } +#endif + NoHugePagesInRegion(MemToShadow(kMadviseRangeBeg), + kMadviseRangeSize * kShadowMultiplier); + // Meta shadow is compressing and we don't flush it, + // so it makes sense to mark it as NOHUGEPAGE to not over-allocate memory. + // On one program it reduces memory consumption from 5GB to 2.5GB. + NoHugePagesInRegion(MetaShadowBeg(), MetaShadowEnd() - MetaShadowBeg()); + if (common_flags()->use_madv_dontdump) + DontDumpShadowMemory(ShadowBeg(), ShadowEnd() - ShadowBeg()); + DPrintf("memory shadow: %zx-%zx (%zuGB)\n", + ShadowBeg(), ShadowEnd(), + (ShadowEnd() - ShadowBeg()) >> 30); + + // Map meta shadow. + uptr meta_size = MetaShadowEnd() - MetaShadowBeg(); + uptr meta = + (uptr)MmapFixedNoReserve(MetaShadowBeg(), meta_size, "meta shadow"); + if (meta != MetaShadowBeg()) { + Printf("FATAL: ThreadSanitizer can not mmap the shadow memory\n"); + Printf("FATAL: Make sure to compile with -fPIE and " + "to link with -pie (%p, %p).\n", meta, MetaShadowBeg()); + Die(); + } + if (common_flags()->use_madv_dontdump) + DontDumpShadowMemory(meta, meta_size); + DPrintf("meta shadow: %zx-%zx (%zuGB)\n", + meta, meta + meta_size, meta_size >> 30); + + InitializeShadowMemoryPlatform(); +} + +static void ProtectRange(uptr beg, uptr end) { + CHECK_LE(beg, end); + if (beg == end) + return; + if (beg != (uptr)MmapNoAccess(beg, end - beg)) { + Printf("FATAL: ThreadSanitizer can not protect [%zx,%zx]\n", beg, end); + Printf("FATAL: Make sure you are not using unlimited stack\n"); + Die(); + } +} + +void CheckAndProtect() { + // Ensure that the binary is indeed compiled with -pie. + MemoryMappingLayout proc_maps(true); + uptr p, end, prot; + while (proc_maps.Next(&p, &end, 0, 0, 0, &prot)) { + if (IsAppMem(p)) + continue; + if (p >= HeapMemEnd() && + p < HeapEnd()) + continue; + if (prot == 0) // Zero page or mprotected. + continue; + if (p >= VdsoBeg()) // vdso + break; + Printf("FATAL: ThreadSanitizer: unexpected memory mapping %p-%p\n", p, end); + Die(); + } + + ProtectRange(LoAppMemEnd(), ShadowBeg()); + ProtectRange(ShadowEnd(), MetaShadowBeg()); +#ifdef TSAN_MID_APP_RANGE + ProtectRange(MetaShadowEnd(), MidAppMemBeg()); + ProtectRange(MidAppMemEnd(), TraceMemBeg()); +#else + ProtectRange(MetaShadowEnd(), TraceMemBeg()); +#endif + // Memory for traces is mapped lazily in MapThreadTrace. + // Protect the whole range for now, so that user does not map something here. + ProtectRange(TraceMemBeg(), TraceMemEnd()); + ProtectRange(TraceMemEnd(), HeapMemBeg()); + ProtectRange(HeapEnd(), HiAppMemBeg()); +} +#endif + +} // namespace __tsan + +#endif // SANITIZER_POSIX diff --git a/contrib/compiler-rt/lib/tsan/rtl/tsan_platform_windows.cc b/contrib/compiler-rt/lib/tsan/rtl/tsan_platform_windows.cc index cfbe77da2c07..c6d5058d96fc 100644 --- a/contrib/compiler-rt/lib/tsan/rtl/tsan_platform_windows.cc +++ b/contrib/compiler-rt/lib/tsan/rtl/tsan_platform_windows.cc @@ -31,6 +31,9 @@ void FlushShadowMemory() { void WriteMemoryProfile(char *buf, uptr buf_size, uptr nthread, uptr nlive) { } +void InitializePlatformEarly() { +} + void InitializePlatform() { } diff --git a/contrib/compiler-rt/lib/tsan/rtl/tsan_ppc_regs.h b/contrib/compiler-rt/lib/tsan/rtl/tsan_ppc_regs.h new file mode 100644 index 000000000000..5b43f3ddada3 --- /dev/null +++ b/contrib/compiler-rt/lib/tsan/rtl/tsan_ppc_regs.h @@ -0,0 +1,96 @@ +#define r0 0 +#define r1 1 +#define r2 2 +#define r3 3 +#define r4 4 +#define r5 5 +#define r6 6 +#define r7 7 +#define r8 8 +#define r9 9 +#define r10 10 +#define r11 11 +#define r12 12 +#define r13 13 +#define r14 14 +#define r15 15 +#define r16 16 +#define r17 17 +#define r18 18 +#define r19 19 +#define r20 20 +#define r21 21 +#define r22 22 +#define r23 23 +#define r24 24 +#define r25 25 +#define r26 26 +#define r27 27 +#define r28 28 +#define r29 29 +#define r30 30 +#define r31 31 +#define f0 0 +#define f1 1 +#define f2 2 +#define f3 3 +#define f4 4 +#define f5 5 +#define f6 6 +#define f7 7 +#define f8 8 +#define f9 9 +#define f10 10 +#define f11 11 +#define f12 12 +#define f13 13 +#define f14 14 +#define f15 15 +#define f16 16 +#define f17 17 +#define f18 18 +#define f19 19 +#define f20 20 +#define f21 21 +#define f22 22 +#define f23 23 +#define f24 24 +#define f25 25 +#define f26 26 +#define f27 27 +#define f28 28 +#define f29 29 +#define f30 30 +#define f31 31 +#define v0 0 +#define v1 1 +#define v2 2 +#define v3 3 +#define v4 4 +#define v5 5 +#define v6 6 +#define v7 7 +#define v8 8 +#define v9 9 +#define v10 10 +#define v11 11 +#define v12 12 +#define v13 13 +#define v14 14 +#define v15 15 +#define v16 16 +#define v17 17 +#define v18 18 +#define v19 19 +#define v20 20 +#define v21 21 +#define v22 22 +#define v23 23 +#define v24 24 +#define v25 25 +#define v26 26 +#define v27 27 +#define v28 28 +#define v29 29 +#define v30 30 +#define v31 31 diff --git a/contrib/compiler-rt/lib/tsan/rtl/tsan_report.cc b/contrib/compiler-rt/lib/tsan/rtl/tsan_report.cc index f4b06878a58e..c1d2fd07c0d9 100644 --- a/contrib/compiler-rt/lib/tsan/rtl/tsan_report.cc +++ b/contrib/compiler-rt/lib/tsan/rtl/tsan_report.cc @@ -111,6 +111,12 @@ static const char *ReportTypeString(ReportType typ) { return ""; } +#if SANITIZER_MAC +static const char *const kInterposedFunctionPrefix = "wrap_"; +#else +static const char *const kInterposedFunctionPrefix = "__interceptor_"; +#endif + void PrintStack(const ReportStack *ent) { if (ent == 0 || ent->frames == 0) { Printf(" [failed to restore the stack]\n\n"); @@ -121,7 +127,7 @@ void PrintStack(const ReportStack *ent) { InternalScopedString res(2 * GetPageSizeCached()); RenderFrame(&res, common_flags()->stack_trace_format, i, frame->info, common_flags()->symbolize_vs_style, - common_flags()->strip_path_prefix, "__interceptor_"); + common_flags()->strip_path_prefix, kInterposedFunctionPrefix); Printf("%s\n", res.data()); } Printf("\n"); @@ -165,9 +171,14 @@ static void PrintLocation(const ReportLocation *loc) { Printf("%s", d.Location()); if (loc->type == ReportLocationGlobal) { const DataInfo &global = loc->global; - Printf(" Location is global '%s' of size %zu at %p (%s+%p)\n\n", - global.name, global.size, global.start, - StripModuleName(global.module), global.module_offset); + if (global.size != 0) + Printf(" Location is global '%s' of size %zu at %p (%s+%p)\n\n", + global.name, global.size, global.start, + StripModuleName(global.module), global.module_offset); + else + Printf(" Location is global '%s' at %p (%s+%p)\n\n", global.name, + global.start, StripModuleName(global.module), + global.module_offset); } else if (loc->type == ReportLocationHeap) { char thrbuf[kThreadBufSize]; Printf(" Location is heap block of size %zu at %p allocated by %s:\n", @@ -256,10 +267,15 @@ static bool FrameIsInternal(const SymbolizedStack *frame) { if (frame == 0) return false; const char *file = frame->info.file; - return file != 0 && - (internal_strstr(file, "tsan_interceptors.cc") || - internal_strstr(file, "sanitizer_common_interceptors.inc") || - internal_strstr(file, "tsan_interface_")); + const char *module = frame->info.module; + if (file != 0 && + (internal_strstr(file, "tsan_interceptors.cc") || + internal_strstr(file, "sanitizer_common_interceptors.inc") || + internal_strstr(file, "tsan_interface_"))) + return true; + if (module != 0 && (internal_strstr(module, "libclang_rt.tsan_"))) + return true; + return false; } static SymbolizedStack *SkipTsanInternalFrames(SymbolizedStack *frames) { diff --git a/contrib/compiler-rt/lib/tsan/rtl/tsan_rtl.cc b/contrib/compiler-rt/lib/tsan/rtl/tsan_rtl.cc index 63c356b228a4..4df4db557a24 100644 --- a/contrib/compiler-rt/lib/tsan/rtl/tsan_rtl.cc +++ b/contrib/compiler-rt/lib/tsan/rtl/tsan_rtl.cc @@ -44,7 +44,7 @@ extern "C" void __tsan_resume() { namespace __tsan { -#ifndef SANITIZER_GO +#if !defined(SANITIZER_GO) && !SANITIZER_MAC THREADLOCAL char cur_thread_placeholder[sizeof(ThreadState)] ALIGNED(64); #endif static char ctx_placeholder[sizeof(Context)] ALIGNED(64); @@ -55,12 +55,12 @@ Context *ctx; bool OnFinalize(bool failed); void OnInitialize(); #else -SANITIZER_INTERFACE_ATTRIBUTE -bool WEAK OnFinalize(bool failed) { +SANITIZER_WEAK_CXX_DEFAULT_IMPL +bool OnFinalize(bool failed) { return failed; } -SANITIZER_INTERFACE_ATTRIBUTE -void WEAK OnInitialize() {} +SANITIZER_WEAK_CXX_DEFAULT_IMPL +void OnInitialize() {} #endif static char thread_registry_placeholder[sizeof(ThreadRegistry)]; @@ -99,8 +99,10 @@ Context::Context() , nmissed_expected() , thread_registry(new(thread_registry_placeholder) ThreadRegistry( CreateThreadContext, kMaxTid, kThreadQuarantineSize, kMaxTidReuse)) + , racy_mtx(MutexTypeRacy, StatMtxRacy) , racy_stacks(MBlockRacyStacks) , racy_addresses(MBlockRacyAddresses) + , fired_suppressions_mtx(MutexTypeFired, StatMtxFired) , fired_suppressions(8) { } @@ -271,8 +273,8 @@ void MapShadow(uptr addr, uptr size) { void MapThreadTrace(uptr addr, uptr size, const char *name) { DPrintf("#0: Mapping trace at %p-%p(0x%zx)\n", addr, addr + size, size); - CHECK_GE(addr, kTraceMemBeg); - CHECK_LE(addr + size, kTraceMemEnd); + CHECK_GE(addr, TraceMemBeg()); + CHECK_LE(addr + size, TraceMemEnd()); CHECK_EQ(addr, addr & ~((64 << 10) - 1)); // windows wants 64K alignment uptr addr1 = (uptr)MmapFixedNoReserve(addr, size, name); if (addr1 != addr) { @@ -283,9 +285,8 @@ void MapThreadTrace(uptr addr, uptr size, const char *name) { } static void CheckShadowMapping() { - for (uptr i = 0; i < ARRAY_SIZE(UserRegions); i += 2) { - const uptr beg = UserRegions[i]; - const uptr end = UserRegions[i + 1]; + uptr beg, end; + for (int i = 0; GetUserRegion(i, &beg, &end); i++) { VPrintf(3, "checking shadow region %p-%p\n", beg, end); for (uptr p0 = beg; p0 <= end; p0 += (end - beg) / 4) { for (int x = -1; x <= 1; x++) { @@ -318,10 +319,15 @@ void Initialize(ThreadState *thr) { ctx = new(ctx_placeholder) Context; const char *options = GetEnv(kTsanOptionsEnv); - InitializeFlags(&ctx->flags, options); CacheBinaryName(); + InitializeFlags(&ctx->flags, options); + InitializePlatformEarly(); #ifndef SANITIZER_GO + // Re-exec ourselves if we need to set additional env or command line args. + MaybeReexec(); + InitializeAllocator(); + ReplaceSystemMalloc(); #endif InitializeInterceptors(); CheckShadowMapping(); @@ -417,7 +423,7 @@ int Finalize(ThreadState *thr) { StatOutput(ctx->stat); #endif - return failed ? flags()->exitcode : 0; + return failed ? common_flags()->exitcode : 0; } #ifndef SANITIZER_GO diff --git a/contrib/compiler-rt/lib/tsan/rtl/tsan_rtl.h b/contrib/compiler-rt/lib/tsan/rtl/tsan_rtl.h index a13e4b6379f0..04104b162f98 100644 --- a/contrib/compiler-rt/lib/tsan/rtl/tsan_rtl.h +++ b/contrib/compiler-rt/lib/tsan/rtl/tsan_rtl.h @@ -54,7 +54,7 @@ namespace __tsan { #ifndef SANITIZER_GO struct MapUnmapCallback; -#ifdef __mips64 +#if defined(__mips64) || defined(__aarch64__) || defined(__powerpc__) static const uptr kAllocatorSpace = 0; static const uptr kAllocatorSize = SANITIZER_MMAP_RANGE_SIZE; static const uptr kAllocatorRegionSizeLog = 20; @@ -66,7 +66,8 @@ typedef SizeClassAllocator32<kAllocatorSpace, kAllocatorSize, 0, CompactSizeClassMap, kAllocatorRegionSizeLog, ByteMap, MapUnmapCallback> PrimaryAllocator; #else -typedef SizeClassAllocator64<kHeapMemBeg, kHeapMemEnd - kHeapMemBeg, 0, +typedef SizeClassAllocator64<Mapping::kHeapMemBeg, + Mapping::kHeapMemEnd - Mapping::kHeapMemBeg, 0, DefaultSizeClassMap, MapUnmapCallback> PrimaryAllocator; #endif typedef SizeClassAllocatorLocalCache<PrimaryAllocator> AllocatorCache; @@ -410,12 +411,18 @@ struct ThreadState { }; #ifndef SANITIZER_GO +#if SANITIZER_MAC +ThreadState *cur_thread(); +void cur_thread_finalize(); +#else __attribute__((tls_model("initial-exec"))) extern THREADLOCAL char cur_thread_placeholder[]; INLINE ThreadState *cur_thread() { return reinterpret_cast<ThreadState *>(&cur_thread_placeholder); } -#endif +INLINE void cur_thread_finalize() { } +#endif // SANITIZER_MAC +#endif // SANITIZER_GO class ThreadContext : public ThreadContextBase { public: @@ -458,7 +465,7 @@ struct RacyAddress { struct FiredSuppression { ReportType type; - uptr pc; + uptr pc_or_addr; Suppression *supp; }; @@ -480,9 +487,11 @@ struct Context { ThreadRegistry *thread_registry; + Mutex racy_mtx; Vector<RacyStacks> racy_stacks; Vector<RacyAddress> racy_addresses; // Number of fired suppressions may be large enough. + Mutex fired_suppressions_mtx; InternalMmapVector<FiredSuppression> fired_suppressions; DDetector *dd; @@ -587,8 +596,7 @@ void ForkChildAfter(ThreadState *thr, uptr pc); void ReportRace(ThreadState *thr); bool OutputReport(ThreadState *thr, const ScopedReport &srep); -bool IsFiredSuppression(Context *ctx, const ScopedReport &srep, - StackTrace trace); +bool IsFiredSuppression(Context *ctx, ReportType type, StackTrace trace); bool IsExpectedReport(uptr addr, uptr size); void PrintMatchedBenignRaces(); @@ -708,7 +716,7 @@ void AcquireReleaseImpl(ThreadState *thr, uptr pc, SyncClock *c); // The trick is that the call preserves all registers and the compiler // does not treat it as a call. // If it does not work for you, use normal call. -#if !SANITIZER_DEBUG && defined(__x86_64__) +#if !SANITIZER_DEBUG && defined(__x86_64__) && !SANITIZER_MAC // The caller may not create the stack frame for itself at all, // so we create a reserve stack frame for it (1024b must be enough). #define HACKY_CALL(f) \ @@ -754,11 +762,7 @@ void ALWAYS_INLINE TraceAddEvent(ThreadState *thr, FastState fs, #ifndef SANITIZER_GO uptr ALWAYS_INLINE HeapEnd() { -#if SANITIZER_CAN_USE_ALLOCATOR64 - return kHeapMemEnd + PrimaryAllocator::AdditionalSize(); -#else - return kHeapMemEnd; -#endif + return HeapMemEnd() + PrimaryAllocator::AdditionalSize(); } #endif diff --git a/contrib/compiler-rt/lib/tsan/rtl/tsan_rtl_aarch64.S b/contrib/compiler-rt/lib/tsan/rtl/tsan_rtl_aarch64.S new file mode 100644 index 000000000000..9cea3cf02800 --- /dev/null +++ b/contrib/compiler-rt/lib/tsan/rtl/tsan_rtl_aarch64.S @@ -0,0 +1,206 @@ +#include "sanitizer_common/sanitizer_asm.h" +.section .text + +.hidden __tsan_setjmp +.comm _ZN14__interception11real_setjmpE,8,8 +.type setjmp, @function +setjmp: + CFI_STARTPROC + + // save env parameters for function call + stp x29, x30, [sp, -32]! + CFI_DEF_CFA_OFFSET (32) + CFI_OFFSET (29, -32) + CFI_OFFSET (30, -24) + + // Adjust the SP for previous frame + add x29, sp, 0 + CFI_DEF_CFA_REGISTER (29) + + // Save jmp_buf + str x19, [sp, 16] + CFI_OFFSET (19, -16) + mov x19, x0 + + // SP pointer mangling (see glibc setjmp) + adrp x2, :got:__pointer_chk_guard + ldr x2, [x2, #:got_lo12:__pointer_chk_guard] + add x0, x29, 32 + ldr x2, [x2] + eor x1, x2, x0 + + // call tsan interceptor + bl __tsan_setjmp + + // restore env parameter + mov x0, x19 + ldr x19, [sp, 16] + ldp x29, x30, [sp], 32 + CFI_RESTORE (30) + CFI_RESTORE (19) + CFI_DEF_CFA (31, 0) + + // tail jump to libc setjmp + adrp x1, :got:_ZN14__interception11real_setjmpE + ldr x1, [x1, #:got_lo12:_ZN14__interception11real_setjmpE] + ldr x1, [x1] + br x1 + + CFI_ENDPROC +.size setjmp, .-setjmp + +.comm _ZN14__interception12real__setjmpE,8,8 +.globl _setjmp +.type _setjmp, @function +_setjmp: + CFI_STARTPROC + + // save env parameters for function call + stp x29, x30, [sp, -32]! + CFI_DEF_CFA_OFFSET (32) + CFI_OFFSET (29, -32) + CFI_OFFSET (30, -24) + + // Adjust the SP for previous frame + add x29, sp, 0 + CFI_DEF_CFA_REGISTER (29) + + // Save jmp_buf + str x19, [sp, 16] + CFI_OFFSET (19, -16) + mov x19, x0 + + // SP pointer mangling (see glibc setjmp) + adrp x2, :got:__pointer_chk_guard + ldr x2, [x2, #:got_lo12:__pointer_chk_guard] + add x0, x29, 32 + ldr x2, [x2] + eor x1, x2, x0 + + // call tsan interceptor + bl __tsan_setjmp + + // Restore jmp_buf parameter + mov x0, x19 + ldr x19, [sp, 16] + ldp x29, x30, [sp], 32 + CFI_RESTORE (30) + CFI_RESTORE (19) + CFI_DEF_CFA (31, 0) + + // tail jump to libc setjmp + adrp x1, :got:_ZN14__interception12real__setjmpE + ldr x1, [x1, #:got_lo12:_ZN14__interception12real__setjmpE] + ldr x1, [x1] + br x1 + + CFI_ENDPROC +.size _setjmp, .-_setjmp + +.comm _ZN14__interception14real_sigsetjmpE,8,8 +.globl sigsetjmp +.type sigsetjmp, @function +sigsetjmp: + CFI_STARTPROC + + // save env parameters for function call + stp x29, x30, [sp, -32]! + CFI_DEF_CFA_OFFSET (32) + CFI_OFFSET (29, -32) + CFI_OFFSET (30, -24) + + // Adjust the SP for previous frame + add x29, sp, 0 + CFI_DEF_CFA_REGISTER (29) + + // Save jmp_buf and savesigs + stp x19, x20, [sp, 16] + CFI_OFFSET (19, -16) + CFI_OFFSET (20, -8) + mov w20, w1 + mov x19, x0 + + // SP pointer mangling (see glibc setjmp) + adrp x2, :got:__pointer_chk_guard + ldr x2, [x2, #:got_lo12:__pointer_chk_guard] + add x0, x29, 32 + ldr x2, [x2] + eor x1, x2, x0 + + // call tsan interceptor + bl __tsan_setjmp + + // restore env parameter + mov w1, w20 + mov x0, x19 + ldp x19, x20, [sp, 16] + ldp x29, x30, [sp], 32 + CFI_RESTORE (30) + CFI_RESTORE (29) + CFI_RESTORE (19) + CFI_RESTORE (20) + CFI_DEF_CFA (31, 0) + + // tail jump to libc sigsetjmp + adrp x2, :got:_ZN14__interception14real_sigsetjmpE + ldr x2, [x2, #:got_lo12:_ZN14__interception14real_sigsetjmpE] + ldr x2, [x2] + br x2 + CFI_ENDPROC +.size sigsetjmp, .-sigsetjmp + +.comm _ZN14__interception16real___sigsetjmpE,8,8 +.globl __sigsetjmp +.type __sigsetjmp, @function +__sigsetjmp: + CFI_STARTPROC + + // save env parameters for function call + stp x29, x30, [sp, -32]! + CFI_DEF_CFA_OFFSET (32) + CFI_OFFSET (29, -32) + CFI_OFFSET (30, -24) + + // Adjust the SP for previous frame + add x29, sp, 0 + CFI_DEF_CFA_REGISTER (29) + + // Save jmp_buf and savesigs + stp x19, x20, [sp, 16] + CFI_OFFSET (19, -16) + CFI_OFFSET (20, -8) + mov w20, w1 + mov x19, x0 + + // SP pointer mangling (see glibc setjmp) + adrp x2, :got:__pointer_chk_guard + ldr x2, [x2, #:got_lo12:__pointer_chk_guard] + add x0, x29, 32 + ldr x2, [x2] + eor x1, x2, x0 + + // call tsan interceptor + bl __tsan_setjmp + + mov w1, w20 + mov x0, x19 + ldp x19, x20, [sp, 16] + ldp x29, x30, [sp], 32 + CFI_RESTORE (30) + CFI_RESTORE (29) + CFI_RESTORE (19) + CFI_RESTORE (20) + CFI_DEF_CFA (31, 0) + + // tail jump to libc __sigsetjmp + adrp x2, :got:_ZN14__interception16real___sigsetjmpE + ldr x2, [x2, #:got_lo12:_ZN14__interception16real___sigsetjmpE] + ldr x2, [x2] + br x2 + CFI_ENDPROC +.size __sigsetjmp, .-__sigsetjmp + +#if defined(__linux__) +/* We do not need executable stack. */ +.section .note.GNU-stack,"",@progbits +#endif diff --git a/contrib/compiler-rt/lib/tsan/rtl/tsan_rtl_amd64.S b/contrib/compiler-rt/lib/tsan/rtl/tsan_rtl_amd64.S index 8db62f9013a3..caa832375e52 100644 --- a/contrib/compiler-rt/lib/tsan/rtl/tsan_rtl_amd64.S +++ b/contrib/compiler-rt/lib/tsan/rtl/tsan_rtl_amd64.S @@ -1,9 +1,13 @@ #include "sanitizer_common/sanitizer_asm.h" +#if !defined(__APPLE__) .section .text +#else +.section __TEXT,__text +#endif -.hidden __tsan_trace_switch -.globl __tsan_trace_switch_thunk -__tsan_trace_switch_thunk: +ASM_HIDDEN(__tsan_trace_switch) +.globl ASM_TSAN_SYMBOL(__tsan_trace_switch_thunk) +ASM_TSAN_SYMBOL(__tsan_trace_switch_thunk): CFI_STARTPROC # Save scratch registers. push %rax @@ -42,7 +46,7 @@ __tsan_trace_switch_thunk: shr $4, %rsp # clear 4 lsb, align to 16 shl $4, %rsp - call __tsan_trace_switch + call ASM_TSAN_SYMBOL(__tsan_trace_switch) # Unalign stack frame back. mov %rbx, %rsp # restore the original rsp @@ -81,9 +85,9 @@ __tsan_trace_switch_thunk: ret CFI_ENDPROC -.hidden __tsan_report_race -.globl __tsan_report_race_thunk -__tsan_report_race_thunk: +ASM_HIDDEN(__tsan_report_race) +.globl ASM_TSAN_SYMBOL(__tsan_report_race_thunk) +ASM_TSAN_SYMBOL(__tsan_report_race_thunk): CFI_STARTPROC # Save scratch registers. push %rax @@ -122,7 +126,7 @@ __tsan_report_race_thunk: shr $4, %rsp # clear 4 lsb, align to 16 shl $4, %rsp - call __tsan_report_race + call ASM_TSAN_SYMBOL(__tsan_report_race) # Unalign stack frame back. mov %rbx, %rsp # restore the original rsp @@ -161,11 +165,13 @@ __tsan_report_race_thunk: ret CFI_ENDPROC -.hidden __tsan_setjmp +ASM_HIDDEN(__tsan_setjmp) +#if !defined(__APPLE__) .comm _ZN14__interception11real_setjmpE,8,8 -.globl setjmp -.type setjmp, @function -setjmp: +#endif +.globl ASM_TSAN_SYMBOL_INTERCEPTOR(setjmp) +ASM_TYPE_FUNCTION(ASM_TSAN_SYMBOL_INTERCEPTOR(setjmp)) +ASM_TSAN_SYMBOL_INTERCEPTOR(setjmp): CFI_STARTPROC // save env parameter push %rdi @@ -175,29 +181,38 @@ setjmp: #if defined(__FreeBSD__) lea 8(%rsp), %rdi mov %rdi, %rsi -#else +#elif defined(__APPLE__) + lea 16(%rsp), %rdi + mov %rdi, %rsi +#elif defined(__linux__) lea 16(%rsp), %rdi mov %rdi, %rsi xor %fs:0x30, %rsi // magic mangling of rsp (see libc setjmp) rol $0x11, %rsi +#else +# error "Unknown platform" #endif // call tsan interceptor - call __tsan_setjmp + call ASM_TSAN_SYMBOL(__tsan_setjmp) // restore env parameter pop %rdi CFI_ADJUST_CFA_OFFSET(-8) CFI_RESTORE(%rdi) // tail jump to libc setjmp movl $0, %eax +#if !defined(__APPLE__) movq _ZN14__interception11real_setjmpE@GOTPCREL(%rip), %rdx jmp *(%rdx) +#else + jmp ASM_TSAN_SYMBOL(setjmp) +#endif CFI_ENDPROC -.size setjmp, .-setjmp +ASM_SIZE(ASM_TSAN_SYMBOL_INTERCEPTOR(setjmp)) .comm _ZN14__interception12real__setjmpE,8,8 -.globl _setjmp -.type _setjmp, @function -_setjmp: +.globl ASM_TSAN_SYMBOL_INTERCEPTOR(_setjmp) +ASM_TYPE_FUNCTION(ASM_TSAN_SYMBOL_INTERCEPTOR(_setjmp)) +ASM_TSAN_SYMBOL_INTERCEPTOR(_setjmp): CFI_STARTPROC // save env parameter push %rdi @@ -207,29 +222,38 @@ _setjmp: #if defined(__FreeBSD__) lea 8(%rsp), %rdi mov %rdi, %rsi -#else +#elif defined(__APPLE__) + lea 16(%rsp), %rdi + mov %rdi, %rsi +#elif defined(__linux__) lea 16(%rsp), %rdi mov %rdi, %rsi xor %fs:0x30, %rsi // magic mangling of rsp (see libc setjmp) rol $0x11, %rsi +#else +# error "Unknown platform" #endif // call tsan interceptor - call __tsan_setjmp + call ASM_TSAN_SYMBOL(__tsan_setjmp) // restore env parameter pop %rdi CFI_ADJUST_CFA_OFFSET(-8) CFI_RESTORE(%rdi) // tail jump to libc setjmp movl $0, %eax +#if !defined(__APPLE__) movq _ZN14__interception12real__setjmpE@GOTPCREL(%rip), %rdx jmp *(%rdx) +#else + jmp ASM_TSAN_SYMBOL(_setjmp) +#endif CFI_ENDPROC -.size _setjmp, .-_setjmp +ASM_SIZE(ASM_TSAN_SYMBOL_INTERCEPTOR(_setjmp)) .comm _ZN14__interception14real_sigsetjmpE,8,8 -.globl sigsetjmp -.type sigsetjmp, @function -sigsetjmp: +.globl ASM_TSAN_SYMBOL_INTERCEPTOR(sigsetjmp) +ASM_TYPE_FUNCTION(ASM_TSAN_SYMBOL_INTERCEPTOR(sigsetjmp)) +ASM_TSAN_SYMBOL_INTERCEPTOR(sigsetjmp): CFI_STARTPROC // save env parameter push %rdi @@ -246,14 +270,19 @@ sigsetjmp: #if defined(__FreeBSD__) lea 24(%rsp), %rdi mov %rdi, %rsi -#else +#elif defined(__APPLE__) + lea 32(%rsp), %rdi + mov %rdi, %rsi +#elif defined(__linux__) lea 32(%rsp), %rdi mov %rdi, %rsi xor %fs:0x30, %rsi // magic mangling of rsp (see libc setjmp) rol $0x11, %rsi +#else +# error "Unknown platform" #endif // call tsan interceptor - call __tsan_setjmp + call ASM_TSAN_SYMBOL(__tsan_setjmp) // unalign stack frame add $8, %rsp CFI_ADJUST_CFA_OFFSET(-8) @@ -267,15 +296,20 @@ sigsetjmp: CFI_RESTORE(%rdi) // tail jump to libc sigsetjmp movl $0, %eax +#if !defined(__APPLE__) movq _ZN14__interception14real_sigsetjmpE@GOTPCREL(%rip), %rdx jmp *(%rdx) +#else + jmp ASM_TSAN_SYMBOL(sigsetjmp) +#endif CFI_ENDPROC -.size sigsetjmp, .-sigsetjmp +ASM_SIZE(ASM_TSAN_SYMBOL_INTERCEPTOR(sigsetjmp)) +#if !defined(__APPLE__) .comm _ZN14__interception16real___sigsetjmpE,8,8 -.globl __sigsetjmp -.type __sigsetjmp, @function -__sigsetjmp: +.globl ASM_TSAN_SYMBOL_INTERCEPTOR(__sigsetjmp) +ASM_TYPE_FUNCTION(ASM_TSAN_SYMBOL_INTERCEPTOR(__sigsetjmp)) +ASM_TSAN_SYMBOL_INTERCEPTOR(__sigsetjmp): CFI_STARTPROC // save env parameter push %rdi @@ -299,7 +333,7 @@ __sigsetjmp: rol $0x11, %rsi #endif // call tsan interceptor - call __tsan_setjmp + call ASM_TSAN_SYMBOL(__tsan_setjmp) // unalign stack frame add $8, %rsp CFI_ADJUST_CFA_OFFSET(-8) @@ -316,7 +350,8 @@ __sigsetjmp: movq _ZN14__interception16real___sigsetjmpE@GOTPCREL(%rip), %rdx jmp *(%rdx) CFI_ENDPROC -.size __sigsetjmp, .-__sigsetjmp +ASM_SIZE(ASM_TSAN_SYMBOL_INTERCEPTOR(__sigsetjmp)) +#endif // !defined(__APPLE__) #if defined(__FreeBSD__) || defined(__linux__) /* We do not need executable stack. */ diff --git a/contrib/compiler-rt/lib/tsan/rtl/tsan_rtl_mutex.cc b/contrib/compiler-rt/lib/tsan/rtl/tsan_rtl_mutex.cc index 09180d88a6fb..62ab7aa6b2b4 100644 --- a/contrib/compiler-rt/lib/tsan/rtl/tsan_rtl_mutex.cc +++ b/contrib/compiler-rt/lib/tsan/rtl/tsan_rtl_mutex.cc @@ -472,7 +472,7 @@ void ReportDeadlock(ThreadState *thr, uptr pc, DDReport *r) { for (int i = 0; i < r->n; i++) { for (int j = 0; j < (flags()->second_deadlock_stack ? 2 : 1); j++) { u32 stk = r->loop[i].stk[j]; - if (stk) { + if (stk && stk != 0xffffffff) { rep.AddStack(StackDepotGet(stk), true); } else { // Sometimes we fail to extract the stack trace (FIXME: investigate), diff --git a/contrib/compiler-rt/lib/tsan/rtl/tsan_rtl_ppc64.S b/contrib/compiler-rt/lib/tsan/rtl/tsan_rtl_ppc64.S new file mode 100644 index 000000000000..8285e21aa1ec --- /dev/null +++ b/contrib/compiler-rt/lib/tsan/rtl/tsan_rtl_ppc64.S @@ -0,0 +1,288 @@ +#include "tsan_ppc_regs.h" + + .section .text + .hidden __tsan_setjmp + .globl _setjmp + .type _setjmp, @function + .align 4 +#if _CALL_ELF == 2 +_setjmp: +#else + .section ".opd","aw" + .align 3 +_setjmp: + .quad .L._setjmp,.TOC.@tocbase,0 + .previous +#endif +.L._setjmp: + mflr r0 + stdu r1,-48(r1) + std r2,24(r1) + std r3,32(r1) + std r0,40(r1) + // r3 is the original stack pointer. + addi r3,r1,48 + // r4 is the mangled stack pointer (see glibc) + ld r4,-28696(r13) + xor r4,r3,r4 + // Materialize a TOC in case we were called from libc. + // For big-endian, we load the TOC from the OPD. For little- + // endian, we use the .TOC. symbol to find it. + nop + bcl 20,31,0f +0: + mflr r2 +#if _CALL_ELF == 2 + addis r2,r2,.TOC.-0b@ha + addi r2,r2,.TOC.-0b@l +#else + addis r2,r2,_setjmp-0b@ha + addi r2,r2,_setjmp-0b@l + ld r2,8(r2) +#endif + // Call the interceptor. + bl __tsan_setjmp + nop + // Restore regs needed for setjmp. + ld r3,32(r1) + ld r0,40(r1) + // Emulate the real setjmp function. We do this because we can't + // perform a sibcall: The real setjmp function trashes the TOC + // pointer, and with a sibcall we have no way to restore it. + // This way we can make sure our caller's stack pointer and + // link register are saved correctly in the jmpbuf. + ld r6,-28696(r13) + addi r5,r1,48 // original stack ptr of caller + xor r5,r6,r5 + std r5,0(r3) // mangled stack ptr of caller + ld r5,24(r1) + std r5,8(r3) // caller's saved TOC pointer + xor r0,r6,r0 + std r0,16(r3) // caller's mangled return address + mfcr r0 + // Nonvolatiles. + std r14,24(r3) + stfd f14,176(r3) + stw r0,172(r3) // CR + std r15,32(r3) + stfd f15,184(r3) + std r16,40(r3) + stfd f16,192(r3) + std r17,48(r3) + stfd f17,200(r3) + std r18,56(r3) + stfd f18,208(r3) + std r19,64(r3) + stfd f19,216(r3) + std r20,72(r3) + stfd f20,224(r3) + std r21,80(r3) + stfd f21,232(r3) + std r22,88(r3) + stfd f22,240(r3) + std r23,96(r3) + stfd f23,248(r3) + std r24,104(r3) + stfd f24,256(r3) + std r25,112(r3) + stfd f25,264(r3) + std r26,120(r3) + stfd f26,272(r3) + std r27,128(r3) + stfd f27,280(r3) + std r28,136(r3) + stfd f28,288(r3) + std r29,144(r3) + stfd f29,296(r3) + std r30,152(r3) + stfd f30,304(r3) + std r31,160(r3) + stfd f31,312(r3) + addi r5,r3,320 + mfspr r0,256 + stw r0,168(r3) // VRSAVE + addi r6,r5,16 + stvx v20,0,r5 + addi r5,r5,32 + stvx v21,0,r6 + addi r6,r6,32 + stvx v22,0,r5 + addi r5,r5,32 + stvx v23,0,r6 + addi r6,r6,32 + stvx v24,0,r5 + addi r5,r5,32 + stvx v25,0,r6 + addi r6,r6,32 + stvx v26,0,r5 + addi r5,r5,32 + stvx v27,0,r6 + addi r6,r6,32 + stvx v28,0,r5 + addi r5,r5,32 + stvx v29,0,r6 + addi r6,r6,32 + stvx v30,0,r5 + stvx v31,0,r6 + // Clear the "mask-saved" slot. + li r4,0 + stw r4,512(r3) + // Restore TOC, LR, and stack and return to caller. + ld r2,24(r1) + ld r0,40(r1) + addi r1,r1,48 + li r3,0 // This is the setjmp return path + mtlr r0 + blr + .size _setjmp, .-.L._setjmp + + .globl setjmp + .type setjmp, @function + .align 4 +setjmp: + b _setjmp + .size setjmp, .-setjmp + + // sigsetjmp is like setjmp, except that the mask in r4 needs + // to be saved at offset 512 of the jump buffer. + .globl __sigsetjmp + .type __sigsetjmp, @function + .align 4 +#if _CALL_ELF == 2 +__sigsetjmp: +#else + .section ".opd","aw" + .align 3 +__sigsetjmp: + .quad .L.__sigsetjmp,.TOC.@tocbase,0 + .previous +#endif +.L.__sigsetjmp: + mflr r0 + stdu r1,-64(r1) + std r2,24(r1) + std r3,32(r1) + std r4,40(r1) + std r0,48(r1) + // r3 is the original stack pointer. + addi r3,r1,64 + // r4 is the mangled stack pointer (see glibc) + ld r4,-28696(r13) + xor r4,r3,r4 + // Materialize a TOC in case we were called from libc. + // For big-endian, we load the TOC from the OPD. For little- + // endian, we use the .TOC. symbol to find it. + nop + bcl 20,31,1f +1: + mflr r2 +#if _CALL_ELF == 2 + addis r2,r2,.TOC.-1b@ha + addi r2,r2,.TOC.-1b@l +#else + addis r2,r2,_setjmp-1b@ha + addi r2,r2,_setjmp-1b@l + ld r2,8(r2) +#endif + // Call the interceptor. + bl __tsan_setjmp + nop + // Restore regs needed for __sigsetjmp. + ld r3,32(r1) + ld r4,40(r1) + ld r0,48(r1) + // Emulate the real sigsetjmp function. We do this because we can't + // perform a sibcall: The real sigsetjmp function trashes the TOC + // pointer, and with a sibcall we have no way to restore it. + // This way we can make sure our caller's stack pointer and + // link register are saved correctly in the jmpbuf. + ld r6,-28696(r13) + addi r5,r1,64 // original stack ptr of caller + xor r5,r6,r5 + std r5,0(r3) // mangled stack ptr of caller + ld r5,24(r1) + std r5,8(r3) // caller's saved TOC pointer + xor r0,r6,r0 + std r0,16(r3) // caller's mangled return address + mfcr r0 + // Nonvolatiles. + std r14,24(r3) + stfd f14,176(r3) + stw r0,172(r3) // CR + std r15,32(r3) + stfd f15,184(r3) + std r16,40(r3) + stfd f16,192(r3) + std r17,48(r3) + stfd f17,200(r3) + std r18,56(r3) + stfd f18,208(r3) + std r19,64(r3) + stfd f19,216(r3) + std r20,72(r3) + stfd f20,224(r3) + std r21,80(r3) + stfd f21,232(r3) + std r22,88(r3) + stfd f22,240(r3) + std r23,96(r3) + stfd f23,248(r3) + std r24,104(r3) + stfd f24,256(r3) + std r25,112(r3) + stfd f25,264(r3) + std r26,120(r3) + stfd f26,272(r3) + std r27,128(r3) + stfd f27,280(r3) + std r28,136(r3) + stfd f28,288(r3) + std r29,144(r3) + stfd f29,296(r3) + std r30,152(r3) + stfd f30,304(r3) + std r31,160(r3) + stfd f31,312(r3) + addi r5,r3,320 + mfspr r0,256 + stw r0,168(r3) // VRSAVE + addi r6,r5,16 + stvx v20,0,r5 + addi r5,r5,32 + stvx v21,0,r6 + addi r6,r6,32 + stvx v22,0,r5 + addi r5,r5,32 + stvx v23,0,r6 + addi r6,r6,32 + stvx v24,0,r5 + addi r5,r5,32 + stvx v25,0,r6 + addi r6,r6,32 + stvx v26,0,r5 + addi r5,r5,32 + stvx v27,0,r6 + addi r6,r6,32 + stvx v28,0,r5 + addi r5,r5,32 + stvx v29,0,r6 + addi r6,r6,32 + stvx v30,0,r5 + stvx v31,0,r6 + // Save into the "mask-saved" slot. + stw r4,512(r3) + // Restore TOC, LR, and stack and return to caller. + ld r2,24(r1) + ld r0,48(r1) + addi r1,r1,64 + li r3,0 // This is the sigsetjmp return path + mtlr r0 + blr + .size __sigsetjmp, .-.L.__sigsetjmp + + .globl sigsetjmp + .type sigsetjmp, @function + .align 4 +sigsetjmp: + b __sigsetjmp + .size sigsetjmp, .-sigsetjmp diff --git a/contrib/compiler-rt/lib/tsan/rtl/tsan_rtl_report.cc b/contrib/compiler-rt/lib/tsan/rtl/tsan_rtl_report.cc index dc9438e6371b..5aff6ca56adf 100644 --- a/contrib/compiler-rt/lib/tsan/rtl/tsan_rtl_report.cc +++ b/contrib/compiler-rt/lib/tsan/rtl/tsan_rtl_report.cc @@ -49,8 +49,8 @@ void TsanCheckFailed(const char *file, int line, const char *cond, #ifdef TSAN_EXTERNAL_HOOKS bool OnReport(const ReportDesc *rep, bool suppressed); #else -SANITIZER_INTERFACE_ATTRIBUTE -bool WEAK OnReport(const ReportDesc *rep, bool suppressed) { +SANITIZER_WEAK_CXX_DEFAULT_IMPL +bool OnReport(const ReportDesc *rep, bool suppressed) { (void)rep; return suppressed; } @@ -186,7 +186,7 @@ void ScopedReport::AddThread(const ThreadContext *tctx, bool suppressable) { return; } void *mem = internal_alloc(MBlockReportThread, sizeof(ReportThread)); - ReportThread *rt = new(mem) ReportThread(); + ReportThread *rt = new(mem) ReportThread; rep_->threads.PushBack(rt); rt->id = tctx->tid; rt->pid = tctx->os_id; @@ -200,16 +200,16 @@ void ScopedReport::AddThread(const ThreadContext *tctx, bool suppressable) { } #ifndef SANITIZER_GO +static bool FindThreadByUidLockedCallback(ThreadContextBase *tctx, void *arg) { + int unique_id = *(int *)arg; + return tctx->unique_id == (u32)unique_id; +} + static ThreadContext *FindThreadByUidLocked(int unique_id) { ctx->thread_registry->CheckLocked(); - for (unsigned i = 0; i < kMaxTid; i++) { - ThreadContext *tctx = static_cast<ThreadContext*>( - ctx->thread_registry->GetThreadLocked(i)); - if (tctx && tctx->unique_id == (u32)unique_id) { - return tctx; - } - } - return 0; + return static_cast<ThreadContext *>( + ctx->thread_registry->FindThreadContextLocked( + FindThreadByUidLockedCallback, &unique_id)); } static ThreadContext *FindThreadByTidLocked(int tid) { @@ -256,7 +256,7 @@ void ScopedReport::AddMutex(const SyncVar *s) { return; } void *mem = internal_alloc(MBlockReportMutex, sizeof(ReportMutex)); - ReportMutex *rm = new(mem) ReportMutex(); + ReportMutex *rm = new(mem) ReportMutex; rep_->mutexes.PushBack(rm); rm->id = s->uid; rm->addr = s->addr; @@ -289,7 +289,7 @@ void ScopedReport::AddDeadMutex(u64 id) { return; } void *mem = internal_alloc(MBlockReportMutex, sizeof(ReportMutex)); - ReportMutex *rm = new(mem) ReportMutex(); + ReportMutex *rm = new(mem) ReportMutex; rep_->mutexes.PushBack(rm); rm->id = id; rm->addr = 0; @@ -369,27 +369,20 @@ void RestoreStack(int tid, const u64 epoch, VarSizeStackTrace *stk, // This function restores stack trace and mutex set for the thread/epoch. // It does so by getting stack trace and mutex set at the beginning of // trace part, and then replaying the trace till the given epoch. - ctx->thread_registry->CheckLocked(); - ThreadContext *tctx = static_cast<ThreadContext*>( - ctx->thread_registry->GetThreadLocked(tid)); - if (tctx == 0) - return; - if (tctx->status != ThreadStatusRunning - && tctx->status != ThreadStatusFinished - && tctx->status != ThreadStatusDead) - return; - Trace* trace = ThreadTrace(tctx->tid); - Lock l(&trace->mtx); + Trace* trace = ThreadTrace(tid); + ReadLock l(&trace->mtx); const int partidx = (epoch / kTracePartSize) % TraceParts(); TraceHeader* hdr = &trace->headers[partidx]; - if (epoch < hdr->epoch0) + if (epoch < hdr->epoch0 || epoch >= hdr->epoch0 + kTracePartSize) return; + CHECK_EQ(RoundDown(epoch, kTracePartSize), hdr->epoch0); const u64 epoch0 = RoundDown(epoch, TraceSize()); const u64 eend = epoch % TraceSize(); const u64 ebegin = RoundDown(eend, kTracePartSize); DPrintf("#%d: RestoreStack epoch=%zu ebegin=%zu eend=%zu partidx=%d\n", tid, (uptr)epoch, (uptr)ebegin, (uptr)eend, partidx); - InternalScopedBuffer<uptr> stack(kShadowStackSize); + Vector<uptr> stack(MBlockReportStack); + stack.Resize(hdr->stack0.size + 64); for (uptr i = 0; i < hdr->stack0.size; i++) { stack[i] = hdr->stack0.trace[i]; DPrintf2(" #%02zu: pc=%zx\n", i, stack[i]); @@ -406,6 +399,8 @@ void RestoreStack(int tid, const u64 epoch, VarSizeStackTrace *stk, if (typ == EventTypeMop) { stack[pos] = pc; } else if (typ == EventTypeFuncEnter) { + if (stack.Size() < pos + 2) + stack.Resize(pos + 2); stack[pos++] = pc; } else if (typ == EventTypeFuncExit) { if (pos > 0) @@ -428,50 +423,58 @@ void RestoreStack(int tid, const u64 epoch, VarSizeStackTrace *stk, if (pos == 0 && stack[0] == 0) return; pos++; - stk->Init(stack.data(), pos); + stk->Init(&stack[0], pos); } static bool HandleRacyStacks(ThreadState *thr, VarSizeStackTrace traces[2], uptr addr_min, uptr addr_max) { bool equal_stack = false; RacyStacks hash; - if (flags()->suppress_equal_stacks) { - hash.hash[0] = md5_hash(traces[0].trace, traces[0].size * sizeof(uptr)); - hash.hash[1] = md5_hash(traces[1].trace, traces[1].size * sizeof(uptr)); - for (uptr i = 0; i < ctx->racy_stacks.Size(); i++) { - if (hash == ctx->racy_stacks[i]) { - DPrintf("ThreadSanitizer: suppressing report as doubled (stack)\n"); - equal_stack = true; - break; - } - } - } bool equal_address = false; RacyAddress ra0 = {addr_min, addr_max}; - if (flags()->suppress_equal_addresses) { - for (uptr i = 0; i < ctx->racy_addresses.Size(); i++) { - RacyAddress ra2 = ctx->racy_addresses[i]; - uptr maxbeg = max(ra0.addr_min, ra2.addr_min); - uptr minend = min(ra0.addr_max, ra2.addr_max); - if (maxbeg < minend) { - DPrintf("ThreadSanitizer: suppressing report as doubled (addr)\n"); - equal_address = true; - break; + { + ReadLock lock(&ctx->racy_mtx); + if (flags()->suppress_equal_stacks) { + hash.hash[0] = md5_hash(traces[0].trace, traces[0].size * sizeof(uptr)); + hash.hash[1] = md5_hash(traces[1].trace, traces[1].size * sizeof(uptr)); + for (uptr i = 0; i < ctx->racy_stacks.Size(); i++) { + if (hash == ctx->racy_stacks[i]) { + VPrintf(2, + "ThreadSanitizer: suppressing report as doubled (stack)\n"); + equal_stack = true; + break; + } + } + } + if (flags()->suppress_equal_addresses) { + for (uptr i = 0; i < ctx->racy_addresses.Size(); i++) { + RacyAddress ra2 = ctx->racy_addresses[i]; + uptr maxbeg = max(ra0.addr_min, ra2.addr_min); + uptr minend = min(ra0.addr_max, ra2.addr_max); + if (maxbeg < minend) { + VPrintf(2, "ThreadSanitizer: suppressing report as doubled (addr)\n"); + equal_address = true; + break; + } } } } - if (equal_stack || equal_address) { - if (!equal_stack) - ctx->racy_stacks.PushBack(hash); - if (!equal_address) - ctx->racy_addresses.PushBack(ra0); - return true; + if (!equal_stack && !equal_address) + return false; + if (!equal_stack) { + Lock lock(&ctx->racy_mtx); + ctx->racy_stacks.PushBack(hash); } - return false; + if (!equal_address) { + Lock lock(&ctx->racy_mtx); + ctx->racy_addresses.PushBack(ra0); + } + return true; } static void AddRacyStacks(ThreadState *thr, VarSizeStackTrace traces[2], uptr addr_min, uptr addr_max) { + Lock lock(&ctx->racy_mtx); if (flags()->suppress_equal_stacks) { RacyStacks hash; hash.hash[0] = md5_hash(traces[0].trace, traces[0].size * sizeof(uptr)); @@ -485,26 +488,29 @@ static void AddRacyStacks(ThreadState *thr, VarSizeStackTrace traces[2], } bool OutputReport(ThreadState *thr, const ScopedReport &srep) { - atomic_store(&ctx->last_symbolize_time_ns, NanoTime(), memory_order_relaxed); + if (!flags()->report_bugs) + return false; + atomic_store_relaxed(&ctx->last_symbolize_time_ns, NanoTime()); const ReportDesc *rep = srep.GetReport(); Suppression *supp = 0; - uptr suppress_pc = 0; - for (uptr i = 0; suppress_pc == 0 && i < rep->mops.Size(); i++) - suppress_pc = IsSuppressed(rep->typ, rep->mops[i]->stack, &supp); - for (uptr i = 0; suppress_pc == 0 && i < rep->stacks.Size(); i++) - suppress_pc = IsSuppressed(rep->typ, rep->stacks[i], &supp); - for (uptr i = 0; suppress_pc == 0 && i < rep->threads.Size(); i++) - suppress_pc = IsSuppressed(rep->typ, rep->threads[i]->stack, &supp); - for (uptr i = 0; suppress_pc == 0 && i < rep->locs.Size(); i++) - suppress_pc = IsSuppressed(rep->typ, rep->locs[i], &supp); - if (suppress_pc != 0) { - FiredSuppression s = {srep.GetReport()->typ, suppress_pc, supp}; + uptr pc_or_addr = 0; + for (uptr i = 0; pc_or_addr == 0 && i < rep->mops.Size(); i++) + pc_or_addr = IsSuppressed(rep->typ, rep->mops[i]->stack, &supp); + for (uptr i = 0; pc_or_addr == 0 && i < rep->stacks.Size(); i++) + pc_or_addr = IsSuppressed(rep->typ, rep->stacks[i], &supp); + for (uptr i = 0; pc_or_addr == 0 && i < rep->threads.Size(); i++) + pc_or_addr = IsSuppressed(rep->typ, rep->threads[i]->stack, &supp); + for (uptr i = 0; pc_or_addr == 0 && i < rep->locs.Size(); i++) + pc_or_addr = IsSuppressed(rep->typ, rep->locs[i], &supp); + if (pc_or_addr != 0) { + Lock lock(&ctx->fired_suppressions_mtx); + FiredSuppression s = {srep.GetReport()->typ, pc_or_addr, supp}; ctx->fired_suppressions.push_back(s); } { bool old_is_freeing = thr->is_freeing; thr->is_freeing = false; - bool suppressed = OnReport(rep, suppress_pc != 0); + bool suppressed = OnReport(rep, pc_or_addr != 0); thr->is_freeing = old_is_freeing; if (suppressed) return false; @@ -512,20 +518,20 @@ bool OutputReport(ThreadState *thr, const ScopedReport &srep) { PrintReport(rep); ctx->nreported++; if (flags()->halt_on_error) - internal__exit(flags()->exitcode); + Die(); return true; } -bool IsFiredSuppression(Context *ctx, const ScopedReport &srep, - StackTrace trace) { +bool IsFiredSuppression(Context *ctx, ReportType type, StackTrace trace) { + ReadLock lock(&ctx->fired_suppressions_mtx); for (uptr k = 0; k < ctx->fired_suppressions.size(); k++) { - if (ctx->fired_suppressions[k].type != srep.GetReport()->typ) + if (ctx->fired_suppressions[k].type != type) continue; for (uptr j = 0; j < trace.size; j++) { FiredSuppression *s = &ctx->fired_suppressions[k]; - if (trace.trace[j] == s->pc) { + if (trace.trace[j] == s->pc_or_addr) { if (s->supp) - s->supp->hit_count++; + atomic_fetch_add(&s->supp->hit_count, 1, memory_order_relaxed); return true; } } @@ -533,16 +539,15 @@ bool IsFiredSuppression(Context *ctx, const ScopedReport &srep, return false; } -static bool IsFiredSuppression(Context *ctx, - const ScopedReport &srep, - uptr addr) { +static bool IsFiredSuppression(Context *ctx, ReportType type, uptr addr) { + ReadLock lock(&ctx->fired_suppressions_mtx); for (uptr k = 0; k < ctx->fired_suppressions.size(); k++) { - if (ctx->fired_suppressions[k].type != srep.GetReport()->typ) + if (ctx->fired_suppressions[k].type != type) continue; FiredSuppression *s = &ctx->fired_suppressions[k]; - if (addr == s->pc) { + if (addr == s->pc_or_addr) { if (s->supp) - s->supp->hit_count++; + atomic_fetch_add(&s->supp->hit_count, 1, memory_order_relaxed); return true; } } @@ -595,8 +600,6 @@ void ReportRace(ThreadState *thr) { return; } - ThreadRegistryLock l0(ctx->thread_registry); - ReportType typ = ReportTypeRace; if (thr->is_vptr_access && freed) typ = ReportTypeVptrUseAfterFree; @@ -604,29 +607,35 @@ void ReportRace(ThreadState *thr) { typ = ReportTypeVptrRace; else if (freed) typ = ReportTypeUseAfterFree; - ScopedReport rep(typ); - if (IsFiredSuppression(ctx, rep, addr)) + + if (IsFiredSuppression(ctx, typ, addr)) return; + const uptr kMop = 2; VarSizeStackTrace traces[kMop]; const uptr toppc = TraceTopPC(thr); ObtainCurrentStack(thr, toppc, &traces[0]); - if (IsFiredSuppression(ctx, rep, traces[0])) + if (IsFiredSuppression(ctx, typ, traces[0])) return; - InternalScopedBuffer<MutexSet> mset2(1); - new(mset2.data()) MutexSet(); + + // MutexSet is too large to live on stack. + Vector<u64> mset_buffer(MBlockScopedBuf); + mset_buffer.Resize(sizeof(MutexSet) / sizeof(u64) + 1); + MutexSet *mset2 = new(&mset_buffer[0]) MutexSet(); + Shadow s2(thr->racy_state[1]); - RestoreStack(s2.tid(), s2.epoch(), &traces[1], mset2.data()); - if (IsFiredSuppression(ctx, rep, traces[1])) + RestoreStack(s2.tid(), s2.epoch(), &traces[1], mset2); + if (IsFiredSuppression(ctx, typ, traces[1])) return; if (HandleRacyStacks(thr, traces, addr_min, addr_max)) return; + ThreadRegistryLock l0(ctx->thread_registry); + ScopedReport rep(typ); for (uptr i = 0; i < kMop; i++) { Shadow s(thr->racy_state[i]); - rep.AddMemoryAccess(addr, s, traces[i], - i == 0 ? &thr->mset : mset2.data()); + rep.AddMemoryAccess(addr, s, traces[i], i == 0 ? &thr->mset : mset2); } for (uptr i = 0; i < kMop; i++) { diff --git a/contrib/compiler-rt/lib/tsan/rtl/tsan_rtl_thread.cc b/contrib/compiler-rt/lib/tsan/rtl/tsan_rtl_thread.cc index 66c78cfdd7c0..dcae255f7643 100644 --- a/contrib/compiler-rt/lib/tsan/rtl/tsan_rtl_thread.cc +++ b/contrib/compiler-rt/lib/tsan/rtl/tsan_rtl_thread.cc @@ -55,6 +55,8 @@ void ThreadContext::OnCreated(void *arg) { if (tid == 0) return; OnCreatedArgs *args = static_cast<OnCreatedArgs *>(arg); + if (!args->thr) // GCD workers don't have a parent thread. + return; args->thr->fast_state.IncrementEpoch(); // Can't increment epoch w/o writing to the trace as well. TraceAddEvent(args->thr, args->thr->fast_state, EventTypeMop, 0); @@ -231,8 +233,10 @@ int ThreadCount(ThreadState *thr) { int ThreadCreate(ThreadState *thr, uptr pc, uptr uid, bool detached) { StatInc(thr, StatThreadCreate); OnCreatedArgs args = { thr, pc }; - int tid = ctx->thread_registry->CreateThread(uid, detached, thr->tid, &args); - DPrintf("#%d: ThreadCreate tid=%d uid=%zu\n", thr->tid, tid, uid); + u32 parent_tid = thr ? thr->tid : kInvalidTid; // No parent for GCD workers. + int tid = + ctx->thread_registry->CreateThread(uid, detached, parent_tid, &args); + DPrintf("#%d: ThreadCreate tid=%d uid=%zu\n", parent_tid, tid, uid); StatSet(thr, StatThreadMaxAlive, ctx->thread_registry->GetMaxAliveThreads()); return tid; } diff --git a/contrib/compiler-rt/lib/tsan/rtl/tsan_stat.cc b/contrib/compiler-rt/lib/tsan/rtl/tsan_stat.cc index 15fa43d6f8a1..a5cca9679582 100644 --- a/contrib/compiler-rt/lib/tsan/rtl/tsan_stat.cc +++ b/contrib/compiler-rt/lib/tsan/rtl/tsan_stat.cc @@ -164,8 +164,9 @@ void StatOutput(u64 *stat) { name[StatMtxAtExit] = " Atexit "; name[StatMtxAnnotations] = " Annotations "; name[StatMtxMBlock] = " MBlock "; - name[StatMtxJavaMBlock] = " JavaMBlock "; name[StatMtxDeadlockDetector] = " DeadlockDetector "; + name[StatMtxFired] = " FiredSuppressions "; + name[StatMtxRacy] = " RacyStacks "; name[StatMtxFD] = " FD "; Printf("Statistics:\n"); diff --git a/contrib/compiler-rt/lib/tsan/rtl/tsan_stat.h b/contrib/compiler-rt/lib/tsan/rtl/tsan_stat.h index 0bd949ed1563..8ea32048e147 100644 --- a/contrib/compiler-rt/lib/tsan/rtl/tsan_stat.h +++ b/contrib/compiler-rt/lib/tsan/rtl/tsan_stat.h @@ -169,8 +169,9 @@ enum StatType { StatMtxAnnotations, StatMtxAtExit, StatMtxMBlock, - StatMtxJavaMBlock, StatMtxDeadlockDetector, + StatMtxFired, + StatMtxRacy, StatMtxFD, // This must be the last. diff --git a/contrib/compiler-rt/lib/tsan/rtl/tsan_suppressions.cc b/contrib/compiler-rt/lib/tsan/rtl/tsan_suppressions.cc index e382f21f0dff..8754b61c60cd 100644 --- a/contrib/compiler-rt/lib/tsan/rtl/tsan_suppressions.cc +++ b/contrib/compiler-rt/lib/tsan/rtl/tsan_suppressions.cc @@ -34,7 +34,8 @@ static const char *const std_suppressions = "race:std::_Sp_counted_ptr_inplace<std::thread::_Impl\n"; // Can be overriden in frontend. -extern "C" const char *WEAK __tsan_default_suppressions() { +SANITIZER_WEAK_DEFAULT_IMPL +const char *__tsan_default_suppressions() { return 0; } #endif @@ -100,8 +101,8 @@ static uptr IsSuppressed(const char *stype, const AddressInfo &info, if (suppression_ctx->Match(info.function, stype, sp) || suppression_ctx->Match(info.file, stype, sp) || suppression_ctx->Match(info.module, stype, sp)) { - DPrintf("ThreadSanitizer: matched suppression '%s'\n", (*sp)->templ); - (*sp)->hit_count++; + VPrintf(2, "ThreadSanitizer: matched suppression '%s'\n", (*sp)->templ); + atomic_fetch_add(&(*sp)->hit_count, 1, memory_order_relaxed); return info.address; } return 0; @@ -138,8 +139,8 @@ uptr IsSuppressed(ReportType typ, const ReportLocation *loc, Suppression **sp) { const DataInfo &global = loc->global; if (suppression_ctx->Match(global.name, stype, &s) || suppression_ctx->Match(global.module, stype, &s)) { - DPrintf("ThreadSanitizer: matched suppression '%s'\n", s->templ); - s->hit_count++; + VPrintf(2, "ThreadSanitizer: matched suppression '%s'\n", s->templ); + atomic_fetch_add(&s->hit_count, 1, memory_order_relaxed); *sp = s; return global.start; } @@ -154,7 +155,7 @@ void PrintMatchedSuppressions() { return; int hit_count = 0; for (uptr i = 0; i < matched.size(); i++) - hit_count += matched[i]->hit_count; + hit_count += atomic_load_relaxed(&matched[i]->hit_count); Printf("ThreadSanitizer: Matched %d suppressions (pid=%d):\n", hit_count, (int)internal_getpid()); for (uptr i = 0; i < matched.size(); i++) { diff --git a/contrib/compiler-rt/lib/tsan/rtl/tsan_symbolize.cc b/contrib/compiler-rt/lib/tsan/rtl/tsan_symbolize.cc index a6b9bca0501d..b2423951795f 100644 --- a/contrib/compiler-rt/lib/tsan/rtl/tsan_symbolize.cc +++ b/contrib/compiler-rt/lib/tsan/rtl/tsan_symbolize.cc @@ -38,10 +38,10 @@ void ExitSymbolizer() { // May be overriden by JIT/JAVA/etc, // whatever produces PCs marked with kExternalPCBit. -extern "C" bool WEAK __tsan_symbolize_external(uptr pc, - char *func_buf, uptr func_siz, - char *file_buf, uptr file_siz, - int *line, int *col) { +SANITIZER_WEAK_DEFAULT_IMPL +bool __tsan_symbolize_external(uptr pc, char *func_buf, uptr func_siz, + char *file_buf, uptr file_siz, int *line, + int *col) { return false; } @@ -71,7 +71,7 @@ ReportLocation *SymbolizeData(uptr addr) { if (!Symbolizer::GetOrInit()->SymbolizeData(addr, &info)) return 0; ReportLocation *ent = ReportLocation::New(ReportLocationGlobal); - ent->global = info; + internal_memcpy(&ent->global, &info, sizeof(info)); return ent; } diff --git a/contrib/compiler-rt/lib/tsan/rtl/tsan_sync.h b/contrib/compiler-rt/lib/tsan/rtl/tsan_sync.h index 2d12cdff8b2f..f07ea3b9776b 100644 --- a/contrib/compiler-rt/lib/tsan/rtl/tsan_sync.h +++ b/contrib/compiler-rt/lib/tsan/rtl/tsan_sync.h @@ -86,9 +86,9 @@ class MetaMap { void OnThreadIdle(ThreadState *thr); private: - static const u32 kFlagMask = 3 << 30; - static const u32 kFlagBlock = 1 << 30; - static const u32 kFlagSync = 2 << 30; + static const u32 kFlagMask = 3u << 30; + static const u32 kFlagBlock = 1u << 30; + static const u32 kFlagSync = 2u << 30; typedef DenseSlabAlloc<MBlock, 1<<16, 1<<12> BlockAlloc; typedef DenseSlabAlloc<SyncVar, 1<<16, 1<<10> SyncAlloc; BlockAlloc block_alloc_; diff --git a/contrib/compiler-rt/lib/tsan/tests/rtl/tsan_bench.cc b/contrib/compiler-rt/lib/tsan/tests/rtl/tsan_bench.cc deleted file mode 100644 index a3cf22f2c626..000000000000 --- a/contrib/compiler-rt/lib/tsan/tests/rtl/tsan_bench.cc +++ /dev/null @@ -1,105 +0,0 @@ -//===-- tsan_bench.cc -----------------------------------------------------===// -// -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. -// -//===----------------------------------------------------------------------===// -// -// This file is a part of ThreadSanitizer (TSan), a race detector. -// -//===----------------------------------------------------------------------===// -#include "tsan_test_util.h" -#include "tsan_interface.h" -#include "tsan_defs.h" -#include "gtest/gtest.h" -#include <stdint.h> - -const int kSize = 128; -const int kRepeat = 2*1024*1024; - -void noinstr(void *p) {} - -template<typename T, void(*__tsan_mop)(void *p)> -static void Benchmark() { - volatile T data[kSize]; - for (int i = 0; i < kRepeat; i++) { - for (int j = 0; j < kSize; j++) { - __tsan_mop((void*)&data[j]); - data[j]++; - } - } -} - -TEST(DISABLED_BENCH, Mop1) { - Benchmark<uint8_t, noinstr>(); -} - -TEST(DISABLED_BENCH, Mop1Read) { - Benchmark<uint8_t, __tsan_read1>(); -} - -TEST(DISABLED_BENCH, Mop1Write) { - Benchmark<uint8_t, __tsan_write1>(); -} - -TEST(DISABLED_BENCH, Mop2) { - Benchmark<uint16_t, noinstr>(); -} - -TEST(DISABLED_BENCH, Mop2Read) { - Benchmark<uint16_t, __tsan_read2>(); -} - -TEST(DISABLED_BENCH, Mop2Write) { - Benchmark<uint16_t, __tsan_write2>(); -} - -TEST(DISABLED_BENCH, Mop4) { - Benchmark<uint32_t, noinstr>(); -} - -TEST(DISABLED_BENCH, Mop4Read) { - Benchmark<uint32_t, __tsan_read4>(); -} - -TEST(DISABLED_BENCH, Mop4Write) { - Benchmark<uint32_t, __tsan_write4>(); -} - -TEST(DISABLED_BENCH, Mop8) { - Benchmark<uint8_t, noinstr>(); -} - -TEST(DISABLED_BENCH, Mop8Read) { - Benchmark<uint64_t, __tsan_read8>(); -} - -TEST(DISABLED_BENCH, Mop8Write) { - Benchmark<uint64_t, __tsan_write8>(); -} - -TEST(DISABLED_BENCH, FuncCall) { - for (int i = 0; i < kRepeat; i++) { - for (int j = 0; j < kSize; j++) - __tsan_func_entry((void*)(uintptr_t)j); - for (int j = 0; j < kSize; j++) - __tsan_func_exit(); - } -} - -TEST(DISABLED_BENCH, MutexLocal) { - Mutex m; - ScopedThread().Create(m); - for (int i = 0; i < 50; i++) { - ScopedThread t; - t.Lock(m); - t.Unlock(m); - } - for (int i = 0; i < 16*1024*1024; i++) { - m.Lock(); - m.Unlock(); - } - ScopedThread().Destroy(m); -} diff --git a/contrib/compiler-rt/lib/tsan/tests/rtl/tsan_mop.cc b/contrib/compiler-rt/lib/tsan/tests/rtl/tsan_mop.cc deleted file mode 100644 index f21742825050..000000000000 --- a/contrib/compiler-rt/lib/tsan/tests/rtl/tsan_mop.cc +++ /dev/null @@ -1,233 +0,0 @@ -//===-- tsan_mop.cc -------------------------------------------------------===// -// -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. -// -//===----------------------------------------------------------------------===// -// -// This file is a part of ThreadSanitizer (TSan), a race detector. -// -//===----------------------------------------------------------------------===// -#include "tsan_interface.h" -#include "tsan_test_util.h" -#include "gtest/gtest.h" -#include <stddef.h> -#include <stdint.h> - -TEST(ThreadSanitizer, SimpleWrite) { - ScopedThread t; - MemLoc l; - t.Write1(l); -} - -TEST(ThreadSanitizer, SimpleWriteWrite) { - ScopedThread t1, t2; - MemLoc l1, l2; - t1.Write1(l1); - t2.Write1(l2); -} - -TEST(ThreadSanitizer, WriteWriteRace) { - ScopedThread t1, t2; - MemLoc l; - t1.Write1(l); - t2.Write1(l, true); -} - -TEST(ThreadSanitizer, ReadWriteRace) { - ScopedThread t1, t2; - MemLoc l; - t1.Read1(l); - t2.Write1(l, true); -} - -TEST(ThreadSanitizer, WriteReadRace) { - ScopedThread t1, t2; - MemLoc l; - t1.Write1(l); - t2.Read1(l, true); -} - -TEST(ThreadSanitizer, ReadReadNoRace) { - ScopedThread t1, t2; - MemLoc l; - t1.Read1(l); - t2.Read1(l); -} - -TEST(ThreadSanitizer, WriteThenRead) { - MemLoc l; - ScopedThread t1, t2; - t1.Write1(l); - t1.Read1(l); - t2.Read1(l, true); -} - -TEST(ThreadSanitizer, WriteThenLockedRead) { - Mutex m(Mutex::RW); - MainThread t0; - t0.Create(m); - MemLoc l; - { - ScopedThread t1, t2; - - t1.Write8(l); - - t1.Lock(m); - t1.Read8(l); - t1.Unlock(m); - - t2.Read8(l, true); - } - t0.Destroy(m); -} - -TEST(ThreadSanitizer, LockedWriteThenRead) { - Mutex m(Mutex::RW); - MainThread t0; - t0.Create(m); - MemLoc l; - { - ScopedThread t1, t2; - - t1.Lock(m); - t1.Write8(l); - t1.Unlock(m); - - t1.Read8(l); - - t2.Read8(l, true); - } - t0.Destroy(m); -} - - -TEST(ThreadSanitizer, RaceWithOffset) { - ScopedThread t1, t2; - { - MemLoc l; - t1.Access(l.loc(), true, 8, false); - t2.Access((char*)l.loc() + 4, true, 4, true); - } - { - MemLoc l; - t1.Access(l.loc(), true, 8, false); - t2.Access((char*)l.loc() + 7, true, 1, true); - } - { - MemLoc l; - t1.Access((char*)l.loc() + 4, true, 4, false); - t2.Access((char*)l.loc() + 4, true, 2, true); - } - { - MemLoc l; - t1.Access((char*)l.loc() + 4, true, 4, false); - t2.Access((char*)l.loc() + 6, true, 2, true); - } - { - MemLoc l; - t1.Access((char*)l.loc() + 3, true, 2, false); - t2.Access((char*)l.loc() + 4, true, 1, true); - } - { - MemLoc l; - t1.Access((char*)l.loc() + 1, true, 8, false); - t2.Access((char*)l.loc() + 3, true, 1, true); - } -} - -TEST(ThreadSanitizer, RaceWithOffset2) { - ScopedThread t1, t2; - { - MemLoc l; - t1.Access((char*)l.loc(), true, 4, false); - t2.Access((char*)l.loc() + 2, true, 1, true); - } - { - MemLoc l; - t1.Access((char*)l.loc() + 2, true, 1, false); - t2.Access((char*)l.loc(), true, 4, true); - } -} - -TEST(ThreadSanitizer, NoRaceWithOffset) { - ScopedThread t1, t2; - { - MemLoc l; - t1.Access(l.loc(), true, 4, false); - t2.Access((char*)l.loc() + 4, true, 4, false); - } - { - MemLoc l; - t1.Access((char*)l.loc() + 3, true, 2, false); - t2.Access((char*)l.loc() + 1, true, 2, false); - t2.Access((char*)l.loc() + 5, true, 2, false); - } -} - -TEST(ThreadSanitizer, RaceWithDeadThread) { - MemLoc l; - ScopedThread t; - ScopedThread().Write1(l); - t.Write1(l, true); -} - -TEST(ThreadSanitizer, BenignRaceOnVptr) { - void *vptr_storage; - MemLoc vptr(&vptr_storage), val; - vptr_storage = val.loc(); - ScopedThread t1, t2; - t1.VptrUpdate(vptr, val); - t2.Read8(vptr); -} - -TEST(ThreadSanitizer, HarmfulRaceOnVptr) { - void *vptr_storage; - MemLoc vptr(&vptr_storage), val1, val2; - vptr_storage = val1.loc(); - ScopedThread t1, t2; - t1.VptrUpdate(vptr, val2); - t2.Read8(vptr, true); -} - -static void foo() { - volatile int x = 42; - int x2 = x; - (void)x2; -} - -static void bar() { - volatile int x = 43; - int x2 = x; - (void)x2; -} - -TEST(ThreadSanitizer, ReportDeadThread) { - MemLoc l; - ScopedThread t1; - { - ScopedThread t2; - t2.Call(&foo); - t2.Call(&bar); - t2.Write1(l); - } - t1.Write1(l, true); -} - -struct ClassWithStatic { - static int Data[4]; -}; - -int ClassWithStatic::Data[4]; - -static void foobarbaz() {} - -TEST(ThreadSanitizer, ReportRace) { - ScopedThread t1; - MainThread().Access(&ClassWithStatic::Data, true, 4, false); - t1.Call(&foobarbaz); - t1.Access(&ClassWithStatic::Data, true, 2, true); - t1.Return(); -} diff --git a/contrib/compiler-rt/lib/tsan/tests/rtl/tsan_mutex.cc b/contrib/compiler-rt/lib/tsan/tests/rtl/tsan_mutex.cc deleted file mode 100644 index 4d9c77961818..000000000000 --- a/contrib/compiler-rt/lib/tsan/tests/rtl/tsan_mutex.cc +++ /dev/null @@ -1,221 +0,0 @@ -//===-- tsan_mutex.cc -----------------------------------------------------===// -// -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. -// -//===----------------------------------------------------------------------===// -// -// This file is a part of ThreadSanitizer (TSan), a race detector. -// -//===----------------------------------------------------------------------===// -#include "sanitizer_common/sanitizer_atomic.h" -#include "tsan_interface.h" -#include "tsan_interface_ann.h" -#include "tsan_test_util.h" -#include "gtest/gtest.h" -#include <stdint.h> - -namespace __tsan { - -TEST(ThreadSanitizer, BasicMutex) { - ScopedThread t; - Mutex m; - t.Create(m); - - t.Lock(m); - t.Unlock(m); - - CHECK(t.TryLock(m)); - t.Unlock(m); - - t.Lock(m); - CHECK(!t.TryLock(m)); - t.Unlock(m); - - t.Destroy(m); -} - -TEST(ThreadSanitizer, BasicSpinMutex) { - ScopedThread t; - Mutex m(Mutex::Spin); - t.Create(m); - - t.Lock(m); - t.Unlock(m); - - CHECK(t.TryLock(m)); - t.Unlock(m); - - t.Lock(m); - CHECK(!t.TryLock(m)); - t.Unlock(m); - - t.Destroy(m); -} - -TEST(ThreadSanitizer, BasicRwMutex) { - ScopedThread t; - Mutex m(Mutex::RW); - t.Create(m); - - t.Lock(m); - t.Unlock(m); - - CHECK(t.TryLock(m)); - t.Unlock(m); - - t.Lock(m); - CHECK(!t.TryLock(m)); - t.Unlock(m); - - t.ReadLock(m); - t.ReadUnlock(m); - - CHECK(t.TryReadLock(m)); - t.ReadUnlock(m); - - t.Lock(m); - CHECK(!t.TryReadLock(m)); - t.Unlock(m); - - t.ReadLock(m); - CHECK(!t.TryLock(m)); - t.ReadUnlock(m); - - t.ReadLock(m); - CHECK(t.TryReadLock(m)); - t.ReadUnlock(m); - t.ReadUnlock(m); - - t.Destroy(m); -} - -TEST(ThreadSanitizer, Mutex) { - Mutex m; - MainThread t0; - t0.Create(m); - - ScopedThread t1, t2; - MemLoc l; - t1.Lock(m); - t1.Write1(l); - t1.Unlock(m); - t2.Lock(m); - t2.Write1(l); - t2.Unlock(m); - t2.Destroy(m); -} - -TEST(ThreadSanitizer, SpinMutex) { - Mutex m(Mutex::Spin); - MainThread t0; - t0.Create(m); - - ScopedThread t1, t2; - MemLoc l; - t1.Lock(m); - t1.Write1(l); - t1.Unlock(m); - t2.Lock(m); - t2.Write1(l); - t2.Unlock(m); - t2.Destroy(m); -} - -TEST(ThreadSanitizer, RwMutex) { - Mutex m(Mutex::RW); - MainThread t0; - t0.Create(m); - - ScopedThread t1, t2, t3; - MemLoc l; - t1.Lock(m); - t1.Write1(l); - t1.Unlock(m); - t2.Lock(m); - t2.Write1(l); - t2.Unlock(m); - t1.ReadLock(m); - t3.ReadLock(m); - t1.Read1(l); - t3.Read1(l); - t1.ReadUnlock(m); - t3.ReadUnlock(m); - t2.Lock(m); - t2.Write1(l); - t2.Unlock(m); - t2.Destroy(m); -} - -TEST(ThreadSanitizer, StaticMutex) { - // Emulates statically initialized mutex. - Mutex m; - m.StaticInit(); - { - ScopedThread t1, t2; - t1.Lock(m); - t1.Unlock(m); - t2.Lock(m); - t2.Unlock(m); - } - MainThread().Destroy(m); -} - -static void *singleton_thread(void *param) { - atomic_uintptr_t *singleton = (atomic_uintptr_t *)param; - for (int i = 0; i < 4*1024*1024; i++) { - int *val = (int *)atomic_load(singleton, memory_order_acquire); - __tsan_acquire(singleton); - __tsan_read4(val); - CHECK_EQ(*val, 42); - } - return 0; -} - -TEST(DISABLED_BENCH_ThreadSanitizer, Singleton) { - const int kClockSize = 100; - const int kThreadCount = 8; - - // Puff off thread's clock. - for (int i = 0; i < kClockSize; i++) { - ScopedThread t1; - (void)t1; - } - // Create the singleton. - int val = 42; - __tsan_write4(&val); - atomic_uintptr_t singleton; - __tsan_release(&singleton); - atomic_store(&singleton, (uintptr_t)&val, memory_order_release); - // Create reader threads. - pthread_t threads[kThreadCount]; - for (int t = 0; t < kThreadCount; t++) - pthread_create(&threads[t], 0, singleton_thread, &singleton); - for (int t = 0; t < kThreadCount; t++) - pthread_join(threads[t], 0); -} - -TEST(DISABLED_BENCH_ThreadSanitizer, StopFlag) { - const int kClockSize = 100; - const int kIters = 16*1024*1024; - - // Puff off thread's clock. - for (int i = 0; i < kClockSize; i++) { - ScopedThread t1; - (void)t1; - } - // Create the stop flag. - atomic_uintptr_t flag; - __tsan_release(&flag); - atomic_store(&flag, 0, memory_order_release); - // Read it a lot. - for (int i = 0; i < kIters; i++) { - uptr v = atomic_load(&flag, memory_order_acquire); - __tsan_acquire(&flag); - CHECK_EQ(v, 0); - } -} - -} // namespace __tsan diff --git a/contrib/compiler-rt/lib/tsan/tests/rtl/tsan_posix.cc b/contrib/compiler-rt/lib/tsan/tests/rtl/tsan_posix.cc deleted file mode 100644 index 0caedd7207e6..000000000000 --- a/contrib/compiler-rt/lib/tsan/tests/rtl/tsan_posix.cc +++ /dev/null @@ -1,146 +0,0 @@ -//===-- tsan_posix.cc -----------------------------------------------------===// -// -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. -// -//===----------------------------------------------------------------------===// -// -// This file is a part of ThreadSanitizer (TSan), a race detector. -// -//===----------------------------------------------------------------------===// -#include "tsan_interface.h" -#include "tsan_test_util.h" -#include "gtest/gtest.h" -#include <pthread.h> - -struct thread_key { - pthread_key_t key; - pthread_mutex_t *mtx; - int val; - int *cnt; - thread_key(pthread_key_t key, pthread_mutex_t *mtx, int val, int *cnt) - : key(key) - , mtx(mtx) - , val(val) - , cnt(cnt) { - } -}; - -static void thread_secific_dtor(void *v) { - thread_key *k = (thread_key *)v; - EXPECT_EQ(pthread_mutex_lock(k->mtx), 0); - (*k->cnt)++; - __tsan_write4(&k->cnt); - EXPECT_EQ(pthread_mutex_unlock(k->mtx), 0); - if (k->val == 42) { - delete k; - } else if (k->val == 43 || k->val == 44) { - k->val--; - EXPECT_EQ(pthread_setspecific(k->key, k), 0); - } else { - ASSERT_TRUE(false); - } -} - -static void *dtors_thread(void *p) { - thread_key *k = (thread_key *)p; - EXPECT_EQ(pthread_setspecific(k->key, k), 0); - return 0; -} - -TEST(Posix, ThreadSpecificDtors) { - int cnt = 0; - pthread_key_t key; - EXPECT_EQ(pthread_key_create(&key, thread_secific_dtor), 0); - pthread_mutex_t mtx; - EXPECT_EQ(pthread_mutex_init(&mtx, 0), 0); - pthread_t th[3]; - thread_key *k[3]; - k[0] = new thread_key(key, &mtx, 42, &cnt); - k[1] = new thread_key(key, &mtx, 43, &cnt); - k[2] = new thread_key(key, &mtx, 44, &cnt); - EXPECT_EQ(pthread_create(&th[0], 0, dtors_thread, k[0]), 0); - EXPECT_EQ(pthread_create(&th[1], 0, dtors_thread, k[1]), 0); - EXPECT_EQ(pthread_join(th[0], 0), 0); - EXPECT_EQ(pthread_create(&th[2], 0, dtors_thread, k[2]), 0); - EXPECT_EQ(pthread_join(th[1], 0), 0); - EXPECT_EQ(pthread_join(th[2], 0), 0); - EXPECT_EQ(pthread_key_delete(key), 0); - EXPECT_EQ(6, cnt); -} - -static __thread int local_var; - -static void *local_thread(void *p) { - __tsan_write1(&local_var); - __tsan_write1(&p); - if (p == 0) - return 0; - const int kThreads = 4; - pthread_t th[kThreads]; - for (int i = 0; i < kThreads; i++) - EXPECT_EQ(pthread_create(&th[i], 0, local_thread, - (void*)((long)p - 1)), 0); // NOLINT - for (int i = 0; i < kThreads; i++) - EXPECT_EQ(pthread_join(th[i], 0), 0); - return 0; -} - -TEST(Posix, ThreadLocalAccesses) { - local_thread((void*)2); -} - -struct CondContext { - pthread_mutex_t m; - pthread_cond_t c; - int data; -}; - -static void *cond_thread(void *p) { - CondContext &ctx = *static_cast<CondContext*>(p); - - EXPECT_EQ(pthread_mutex_lock(&ctx.m), 0); - EXPECT_EQ(ctx.data, 0); - ctx.data = 1; - EXPECT_EQ(pthread_cond_signal(&ctx.c), 0); - EXPECT_EQ(pthread_mutex_unlock(&ctx.m), 0); - - EXPECT_EQ(pthread_mutex_lock(&ctx.m), 0); - while (ctx.data != 2) - EXPECT_EQ(pthread_cond_wait(&ctx.c, &ctx.m), 0); - EXPECT_EQ(pthread_mutex_unlock(&ctx.m), 0); - - EXPECT_EQ(pthread_mutex_lock(&ctx.m), 0); - ctx.data = 3; - EXPECT_EQ(pthread_cond_broadcast(&ctx.c), 0); - EXPECT_EQ(pthread_mutex_unlock(&ctx.m), 0); - - return 0; -} - -TEST(Posix, CondBasic) { - CondContext ctx; - EXPECT_EQ(pthread_mutex_init(&ctx.m, 0), 0); - EXPECT_EQ(pthread_cond_init(&ctx.c, 0), 0); - ctx.data = 0; - pthread_t th; - EXPECT_EQ(pthread_create(&th, 0, cond_thread, &ctx), 0); - - EXPECT_EQ(pthread_mutex_lock(&ctx.m), 0); - while (ctx.data != 1) - EXPECT_EQ(pthread_cond_wait(&ctx.c, &ctx.m), 0); - ctx.data = 2; - EXPECT_EQ(pthread_mutex_unlock(&ctx.m), 0); - EXPECT_EQ(pthread_cond_broadcast(&ctx.c), 0); - - EXPECT_EQ(pthread_mutex_lock(&ctx.m), 0); - while (ctx.data != 3) - EXPECT_EQ(pthread_cond_wait(&ctx.c, &ctx.m), 0); - EXPECT_EQ(pthread_mutex_unlock(&ctx.m), 0); - - EXPECT_EQ(pthread_join(th, 0), 0); - EXPECT_EQ(pthread_cond_destroy(&ctx.c), 0); - EXPECT_EQ(pthread_mutex_destroy(&ctx.m), 0); -} diff --git a/contrib/compiler-rt/lib/tsan/tests/rtl/tsan_string.cc b/contrib/compiler-rt/lib/tsan/tests/rtl/tsan_string.cc deleted file mode 100644 index 75adc6c85ee9..000000000000 --- a/contrib/compiler-rt/lib/tsan/tests/rtl/tsan_string.cc +++ /dev/null @@ -1,82 +0,0 @@ -//===-- tsan_string.cc ----------------------------------------------------===// -// -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. -// -//===----------------------------------------------------------------------===// -// -// This file is a part of ThreadSanitizer (TSan), a race detector. -// -//===----------------------------------------------------------------------===// -#include "tsan_test_util.h" -#include "gtest/gtest.h" -#include <string.h> - -namespace __tsan { - -TEST(ThreadSanitizer, Memcpy) { - char data0[7] = {1, 2, 3, 4, 5, 6, 7}; - char data[7] = {42, 42, 42, 42, 42, 42, 42}; - MainThread().Memcpy(data+1, data0+1, 5); - EXPECT_EQ(data[0], 42); - EXPECT_EQ(data[1], 2); - EXPECT_EQ(data[2], 3); - EXPECT_EQ(data[3], 4); - EXPECT_EQ(data[4], 5); - EXPECT_EQ(data[5], 6); - EXPECT_EQ(data[6], 42); - MainThread().Memset(data+1, 13, 5); - EXPECT_EQ(data[0], 42); - EXPECT_EQ(data[1], 13); - EXPECT_EQ(data[2], 13); - EXPECT_EQ(data[3], 13); - EXPECT_EQ(data[4], 13); - EXPECT_EQ(data[5], 13); - EXPECT_EQ(data[6], 42); -} - -TEST(ThreadSanitizer, MemcpyRace1) { - char *data = new char[10]; - char *data1 = new char[10]; - char *data2 = new char[10]; - ScopedThread t1, t2; - t1.Memcpy(data, data1, 10); - t2.Memcpy(data, data2, 10, true); -} - -TEST(ThreadSanitizer, MemcpyRace2) { - char *data = new char[10]; - char *data1 = new char[10]; - char *data2 = new char[10]; - ScopedThread t1, t2; - t1.Memcpy(data+5, data1, 1); - t2.Memcpy(data+3, data2, 4, true); -} - -TEST(ThreadSanitizer, MemcpyRace3) { - char *data = new char[10]; - char *data1 = new char[10]; - char *data2 = new char[10]; - ScopedThread t1, t2; - t1.Memcpy(data, data1, 10); - t2.Memcpy(data1, data2, 10, true); -} - -TEST(ThreadSanitizer, MemcpyStack) { - char *data = new char[10]; - char *data1 = new char[10]; - ScopedThread t1, t2; - t1.Memcpy(data, data1, 10); - t2.Memcpy(data, data1, 10, true); -} - -TEST(ThreadSanitizer, MemsetRace1) { - char *data = new char[10]; - ScopedThread t1, t2; - t1.Memset(data, 1, 10); - t2.Memset(data, 2, 10, true); -} - -} // namespace __tsan diff --git a/contrib/compiler-rt/lib/tsan/tests/rtl/tsan_test.cc b/contrib/compiler-rt/lib/tsan/tests/rtl/tsan_test.cc deleted file mode 100644 index b8b9555c2bff..000000000000 --- a/contrib/compiler-rt/lib/tsan/tests/rtl/tsan_test.cc +++ /dev/null @@ -1,53 +0,0 @@ -//===-- tsan_test.cc ------------------------------------------------------===// -// -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. -// -//===----------------------------------------------------------------------===// -// -// This file is a part of ThreadSanitizer (TSan), a race detector. -// -//===----------------------------------------------------------------------===// -#include "tsan_interface.h" -#include "tsan_test_util.h" -#include "gtest/gtest.h" - -static void foo() {} -static void bar() {} - -TEST(ThreadSanitizer, FuncCall) { - ScopedThread t1, t2; - MemLoc l; - t1.Write1(l); - t2.Call(foo); - t2.Call(bar); - t2.Write1(l, true); - t2.Return(); - t2.Return(); -} - -// We use this function instead of main, as ISO C++ forbids taking the address -// of main, which we need to pass inside __tsan_func_entry. -int run_tests(int argc, char **argv) { - TestMutexBeforeInit(); // Mutexes must be usable before __tsan_init(); - __tsan_init(); - __tsan_func_entry(__builtin_return_address(0)); - __tsan_func_entry((void*)((intptr_t)&run_tests + 1)); - - testing::GTEST_FLAG(death_test_style) = "threadsafe"; - testing::InitGoogleTest(&argc, argv); - int res = RUN_ALL_TESTS(); - - __tsan_func_exit(); - __tsan_func_exit(); - return res; -} - -const char *argv0; - -int main(int argc, char **argv) { - argv0 = argv[0]; - return run_tests(argc, argv); -} diff --git a/contrib/compiler-rt/lib/tsan/tests/rtl/tsan_test_util.h b/contrib/compiler-rt/lib/tsan/tests/rtl/tsan_test_util.h deleted file mode 100644 index 84d277b137f0..000000000000 --- a/contrib/compiler-rt/lib/tsan/tests/rtl/tsan_test_util.h +++ /dev/null @@ -1,122 +0,0 @@ -//===-- tsan_test_util.h ----------------------------------------*- C++ -*-===// -// -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. -// -//===----------------------------------------------------------------------===// -// -// This file is a part of ThreadSanitizer (TSan), a race detector. -// -// Test utils. -//===----------------------------------------------------------------------===// -#ifndef TSAN_TEST_UTIL_H -#define TSAN_TEST_UTIL_H - -void TestMutexBeforeInit(); - -// A location of memory on which a race may be detected. -class MemLoc { - public: - explicit MemLoc(int offset_from_aligned = 0); - explicit MemLoc(void *const real_addr) : loc_(real_addr) { } - ~MemLoc(); - void *loc() const { return loc_; } - private: - void *const loc_; - MemLoc(const MemLoc&); - void operator = (const MemLoc&); -}; - -class Mutex { - public: - enum Type { Normal, Spin, RW }; - - explicit Mutex(Type type = Normal); - ~Mutex(); - - void Init(); - void StaticInit(); // Emulates static initialization (tsan invisible). - void Destroy(); - void Lock(); - bool TryLock(); - void Unlock(); - void ReadLock(); - bool TryReadLock(); - void ReadUnlock(); - - private: - // Placeholder for pthread_mutex_t, CRITICAL_SECTION or whatever. - void *mtx_[128]; - bool alive_; - const Type type_; - - Mutex(const Mutex&); - void operator = (const Mutex&); -}; - -// A thread is started in CTOR and joined in DTOR. -class ScopedThread { - public: - explicit ScopedThread(bool detached = false, bool main = false); - ~ScopedThread(); - void Detach(); - - void Access(void *addr, bool is_write, int size, bool expect_race); - void Read(const MemLoc &ml, int size, bool expect_race = false) { - Access(ml.loc(), false, size, expect_race); - } - void Write(const MemLoc &ml, int size, bool expect_race = false) { - Access(ml.loc(), true, size, expect_race); - } - void Read1(const MemLoc &ml, bool expect_race = false) { - Read(ml, 1, expect_race); } - void Read2(const MemLoc &ml, bool expect_race = false) { - Read(ml, 2, expect_race); } - void Read4(const MemLoc &ml, bool expect_race = false) { - Read(ml, 4, expect_race); } - void Read8(const MemLoc &ml, bool expect_race = false) { - Read(ml, 8, expect_race); } - void Write1(const MemLoc &ml, bool expect_race = false) { - Write(ml, 1, expect_race); } - void Write2(const MemLoc &ml, bool expect_race = false) { - Write(ml, 2, expect_race); } - void Write4(const MemLoc &ml, bool expect_race = false) { - Write(ml, 4, expect_race); } - void Write8(const MemLoc &ml, bool expect_race = false) { - Write(ml, 8, expect_race); } - - void VptrUpdate(const MemLoc &vptr, const MemLoc &new_val, - bool expect_race = false); - - void Call(void(*pc)()); - void Return(); - - void Create(const Mutex &m); - void Destroy(const Mutex &m); - void Lock(const Mutex &m); - bool TryLock(const Mutex &m); - void Unlock(const Mutex &m); - void ReadLock(const Mutex &m); - bool TryReadLock(const Mutex &m); - void ReadUnlock(const Mutex &m); - - void Memcpy(void *dst, const void *src, int size, bool expect_race = false); - void Memset(void *dst, int val, int size, bool expect_race = false); - - private: - struct Impl; - Impl *impl_; - ScopedThread(const ScopedThread&); // Not implemented. - void operator = (const ScopedThread&); // Not implemented. -}; - -class MainThread : public ScopedThread { - public: - MainThread() - : ScopedThread(false, true) { - } -}; - -#endif // #ifndef TSAN_TEST_UTIL_H diff --git a/contrib/compiler-rt/lib/tsan/tests/rtl/tsan_test_util_linux.cc b/contrib/compiler-rt/lib/tsan/tests/rtl/tsan_test_util_linux.cc deleted file mode 100644 index 9298bf051af2..000000000000 --- a/contrib/compiler-rt/lib/tsan/tests/rtl/tsan_test_util_linux.cc +++ /dev/null @@ -1,470 +0,0 @@ - -//===-- tsan_test_util_linux.cc -------------------------------------------===// -// -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. -// -//===----------------------------------------------------------------------===// -// -// This file is a part of ThreadSanitizer (TSan), a race detector. -// -// Test utils, Linux and FreeBSD implementation. -//===----------------------------------------------------------------------===// - -#include "sanitizer_common/sanitizer_atomic.h" -#include "tsan_interface.h" -#include "tsan_test_util.h" -#include "tsan_report.h" - -#include "gtest/gtest.h" - -#include <assert.h> -#include <pthread.h> -#include <stdio.h> -#include <stdint.h> -#include <string.h> -#include <unistd.h> -#include <errno.h> - -using namespace __tsan; // NOLINT - -static __thread bool expect_report; -static __thread bool expect_report_reported; -static __thread ReportType expect_report_type; - -extern "C" void *__interceptor_memcpy(void*, const void*, uptr); -extern "C" void *__interceptor_memset(void*, int, uptr); - -static void *BeforeInitThread(void *param) { - (void)param; - return 0; -} - -static void AtExit() { -} - -void TestMutexBeforeInit() { - // Mutexes must be usable before __tsan_init(); - pthread_mutex_t mtx = PTHREAD_MUTEX_INITIALIZER; - pthread_mutex_lock(&mtx); - pthread_mutex_unlock(&mtx); - pthread_mutex_destroy(&mtx); - pthread_t thr; - pthread_create(&thr, 0, BeforeInitThread, 0); - pthread_join(thr, 0); - atexit(AtExit); -} - -namespace __tsan { -bool OnReport(const ReportDesc *rep, bool suppressed) { - if (expect_report) { - if (rep->typ != expect_report_type) { - printf("Expected report of type %d, got type %d\n", - (int)expect_report_type, (int)rep->typ); - EXPECT_FALSE("Wrong report type"); - return false; - } - } else { - EXPECT_FALSE("Unexpected report"); - return false; - } - expect_report_reported = true; - return true; -} -} // namespace __tsan - -static void* allocate_addr(int size, int offset_from_aligned = 0) { - static uintptr_t foo; - static atomic_uintptr_t uniq = {(uintptr_t)&foo}; // Some real address. - const int kAlign = 16; - CHECK(offset_from_aligned < kAlign); - size = (size + 2 * kAlign) & ~(kAlign - 1); - uintptr_t addr = atomic_fetch_add(&uniq, size, memory_order_relaxed); - return (void*)(addr + offset_from_aligned); -} - -MemLoc::MemLoc(int offset_from_aligned) - : loc_(allocate_addr(16, offset_from_aligned)) { -} - -MemLoc::~MemLoc() { -} - -Mutex::Mutex(Type type) - : alive_() - , type_(type) { -} - -Mutex::~Mutex() { - CHECK(!alive_); -} - -void Mutex::Init() { - CHECK(!alive_); - alive_ = true; - if (type_ == Normal) - CHECK_EQ(pthread_mutex_init((pthread_mutex_t*)mtx_, 0), 0); - else if (type_ == Spin) - CHECK_EQ(pthread_spin_init((pthread_spinlock_t*)mtx_, 0), 0); - else if (type_ == RW) - CHECK_EQ(pthread_rwlock_init((pthread_rwlock_t*)mtx_, 0), 0); - else - CHECK(0); -} - -void Mutex::StaticInit() { - CHECK(!alive_); - CHECK(type_ == Normal); - alive_ = true; - pthread_mutex_t tmp = PTHREAD_MUTEX_INITIALIZER; - memcpy(mtx_, &tmp, sizeof(tmp)); -} - -void Mutex::Destroy() { - CHECK(alive_); - alive_ = false; - if (type_ == Normal) - CHECK_EQ(pthread_mutex_destroy((pthread_mutex_t*)mtx_), 0); - else if (type_ == Spin) - CHECK_EQ(pthread_spin_destroy((pthread_spinlock_t*)mtx_), 0); - else if (type_ == RW) - CHECK_EQ(pthread_rwlock_destroy((pthread_rwlock_t*)mtx_), 0); -} - -void Mutex::Lock() { - CHECK(alive_); - if (type_ == Normal) - CHECK_EQ(pthread_mutex_lock((pthread_mutex_t*)mtx_), 0); - else if (type_ == Spin) - CHECK_EQ(pthread_spin_lock((pthread_spinlock_t*)mtx_), 0); - else if (type_ == RW) - CHECK_EQ(pthread_rwlock_wrlock((pthread_rwlock_t*)mtx_), 0); -} - -bool Mutex::TryLock() { - CHECK(alive_); - if (type_ == Normal) - return pthread_mutex_trylock((pthread_mutex_t*)mtx_) == 0; - else if (type_ == Spin) - return pthread_spin_trylock((pthread_spinlock_t*)mtx_) == 0; - else if (type_ == RW) - return pthread_rwlock_trywrlock((pthread_rwlock_t*)mtx_) == 0; - return false; -} - -void Mutex::Unlock() { - CHECK(alive_); - if (type_ == Normal) - CHECK_EQ(pthread_mutex_unlock((pthread_mutex_t*)mtx_), 0); - else if (type_ == Spin) - CHECK_EQ(pthread_spin_unlock((pthread_spinlock_t*)mtx_), 0); - else if (type_ == RW) - CHECK_EQ(pthread_rwlock_unlock((pthread_rwlock_t*)mtx_), 0); -} - -void Mutex::ReadLock() { - CHECK(alive_); - CHECK(type_ == RW); - CHECK_EQ(pthread_rwlock_rdlock((pthread_rwlock_t*)mtx_), 0); -} - -bool Mutex::TryReadLock() { - CHECK(alive_); - CHECK(type_ == RW); - return pthread_rwlock_tryrdlock((pthread_rwlock_t*)mtx_) == 0; -} - -void Mutex::ReadUnlock() { - CHECK(alive_); - CHECK(type_ == RW); - CHECK_EQ(pthread_rwlock_unlock((pthread_rwlock_t*)mtx_), 0); -} - -struct Event { - enum Type { - SHUTDOWN, - READ, - WRITE, - VPTR_UPDATE, - CALL, - RETURN, - MUTEX_CREATE, - MUTEX_DESTROY, - MUTEX_LOCK, - MUTEX_TRYLOCK, - MUTEX_UNLOCK, - MUTEX_READLOCK, - MUTEX_TRYREADLOCK, - MUTEX_READUNLOCK, - MEMCPY, - MEMSET - }; - Type type; - void *ptr; - uptr arg; - uptr arg2; - bool res; - bool expect_report; - ReportType report_type; - - Event(Type type, const void *ptr = 0, uptr arg = 0, uptr arg2 = 0) - : type(type) - , ptr(const_cast<void*>(ptr)) - , arg(arg) - , arg2(arg2) - , res() - , expect_report() - , report_type() { - } - - void ExpectReport(ReportType type) { - expect_report = true; - report_type = type; - } -}; - -struct ScopedThread::Impl { - pthread_t thread; - bool main; - bool detached; - atomic_uintptr_t event; // Event* - - static void *ScopedThreadCallback(void *arg); - void send(Event *ev); - void HandleEvent(Event *ev); -}; - -void ScopedThread::Impl::HandleEvent(Event *ev) { - CHECK_EQ(expect_report, false); - expect_report = ev->expect_report; - expect_report_reported = false; - expect_report_type = ev->report_type; - switch (ev->type) { - case Event::READ: - case Event::WRITE: { - void (*tsan_mop)(void *addr) = 0; - if (ev->type == Event::READ) { - switch (ev->arg /*size*/) { - case 1: tsan_mop = __tsan_read1; break; - case 2: tsan_mop = __tsan_read2; break; - case 4: tsan_mop = __tsan_read4; break; - case 8: tsan_mop = __tsan_read8; break; - case 16: tsan_mop = __tsan_read16; break; - } - } else { - switch (ev->arg /*size*/) { - case 1: tsan_mop = __tsan_write1; break; - case 2: tsan_mop = __tsan_write2; break; - case 4: tsan_mop = __tsan_write4; break; - case 8: tsan_mop = __tsan_write8; break; - case 16: tsan_mop = __tsan_write16; break; - } - } - CHECK_NE(tsan_mop, 0); -#if defined(__FreeBSD__) - const int ErrCode = ESOCKTNOSUPPORT; -#else - const int ErrCode = ECHRNG; -#endif - errno = ErrCode; - tsan_mop(ev->ptr); - CHECK_EQ(ErrCode, errno); // In no case must errno be changed. - break; - } - case Event::VPTR_UPDATE: - __tsan_vptr_update((void**)ev->ptr, (void*)ev->arg); - break; - case Event::CALL: - __tsan_func_entry((void*)((uptr)ev->ptr)); - break; - case Event::RETURN: - __tsan_func_exit(); - break; - case Event::MUTEX_CREATE: - static_cast<Mutex*>(ev->ptr)->Init(); - break; - case Event::MUTEX_DESTROY: - static_cast<Mutex*>(ev->ptr)->Destroy(); - break; - case Event::MUTEX_LOCK: - static_cast<Mutex*>(ev->ptr)->Lock(); - break; - case Event::MUTEX_TRYLOCK: - ev->res = static_cast<Mutex*>(ev->ptr)->TryLock(); - break; - case Event::MUTEX_UNLOCK: - static_cast<Mutex*>(ev->ptr)->Unlock(); - break; - case Event::MUTEX_READLOCK: - static_cast<Mutex*>(ev->ptr)->ReadLock(); - break; - case Event::MUTEX_TRYREADLOCK: - ev->res = static_cast<Mutex*>(ev->ptr)->TryReadLock(); - break; - case Event::MUTEX_READUNLOCK: - static_cast<Mutex*>(ev->ptr)->ReadUnlock(); - break; - case Event::MEMCPY: - __interceptor_memcpy(ev->ptr, (void*)ev->arg, ev->arg2); - break; - case Event::MEMSET: - __interceptor_memset(ev->ptr, ev->arg, ev->arg2); - break; - default: CHECK(0); - } - if (expect_report && !expect_report_reported) { - printf("Missed expected report of type %d\n", (int)ev->report_type); - EXPECT_FALSE("Missed expected race"); - } - expect_report = false; -} - -void *ScopedThread::Impl::ScopedThreadCallback(void *arg) { - __tsan_func_entry(__builtin_return_address(0)); - Impl *impl = (Impl*)arg; - for (;;) { - Event* ev = (Event*)atomic_load(&impl->event, memory_order_acquire); - if (ev == 0) { - pthread_yield(); - continue; - } - if (ev->type == Event::SHUTDOWN) { - atomic_store(&impl->event, 0, memory_order_release); - break; - } - impl->HandleEvent(ev); - atomic_store(&impl->event, 0, memory_order_release); - } - __tsan_func_exit(); - return 0; -} - -void ScopedThread::Impl::send(Event *e) { - if (main) { - HandleEvent(e); - } else { - CHECK_EQ(atomic_load(&event, memory_order_relaxed), 0); - atomic_store(&event, (uintptr_t)e, memory_order_release); - while (atomic_load(&event, memory_order_acquire) != 0) - pthread_yield(); - } -} - -ScopedThread::ScopedThread(bool detached, bool main) { - impl_ = new Impl; - impl_->main = main; - impl_->detached = detached; - atomic_store(&impl_->event, 0, memory_order_relaxed); - if (!main) { - pthread_attr_t attr; - pthread_attr_init(&attr); - pthread_attr_setdetachstate(&attr, detached); - pthread_attr_setstacksize(&attr, 64*1024); - pthread_create(&impl_->thread, &attr, - ScopedThread::Impl::ScopedThreadCallback, impl_); - } -} - -ScopedThread::~ScopedThread() { - if (!impl_->main) { - Event event(Event::SHUTDOWN); - impl_->send(&event); - if (!impl_->detached) - pthread_join(impl_->thread, 0); - } - delete impl_; -} - -void ScopedThread::Detach() { - CHECK(!impl_->main); - CHECK(!impl_->detached); - impl_->detached = true; - pthread_detach(impl_->thread); -} - -void ScopedThread::Access(void *addr, bool is_write, - int size, bool expect_race) { - Event event(is_write ? Event::WRITE : Event::READ, addr, size); - if (expect_race) - event.ExpectReport(ReportTypeRace); - impl_->send(&event); -} - -void ScopedThread::VptrUpdate(const MemLoc &vptr, - const MemLoc &new_val, - bool expect_race) { - Event event(Event::VPTR_UPDATE, vptr.loc(), (uptr)new_val.loc()); - if (expect_race) - event.ExpectReport(ReportTypeRace); - impl_->send(&event); -} - -void ScopedThread::Call(void(*pc)()) { - Event event(Event::CALL, (void*)((uintptr_t)pc)); - impl_->send(&event); -} - -void ScopedThread::Return() { - Event event(Event::RETURN); - impl_->send(&event); -} - -void ScopedThread::Create(const Mutex &m) { - Event event(Event::MUTEX_CREATE, &m); - impl_->send(&event); -} - -void ScopedThread::Destroy(const Mutex &m) { - Event event(Event::MUTEX_DESTROY, &m); - impl_->send(&event); -} - -void ScopedThread::Lock(const Mutex &m) { - Event event(Event::MUTEX_LOCK, &m); - impl_->send(&event); -} - -bool ScopedThread::TryLock(const Mutex &m) { - Event event(Event::MUTEX_TRYLOCK, &m); - impl_->send(&event); - return event.res; -} - -void ScopedThread::Unlock(const Mutex &m) { - Event event(Event::MUTEX_UNLOCK, &m); - impl_->send(&event); -} - -void ScopedThread::ReadLock(const Mutex &m) { - Event event(Event::MUTEX_READLOCK, &m); - impl_->send(&event); -} - -bool ScopedThread::TryReadLock(const Mutex &m) { - Event event(Event::MUTEX_TRYREADLOCK, &m); - impl_->send(&event); - return event.res; -} - -void ScopedThread::ReadUnlock(const Mutex &m) { - Event event(Event::MUTEX_READUNLOCK, &m); - impl_->send(&event); -} - -void ScopedThread::Memcpy(void *dst, const void *src, int size, - bool expect_race) { - Event event(Event::MEMCPY, dst, (uptr)src, size); - if (expect_race) - event.ExpectReport(ReportTypeRace); - impl_->send(&event); -} - -void ScopedThread::Memset(void *dst, int val, int size, - bool expect_race) { - Event event(Event::MEMSET, dst, val, size); - if (expect_race) - event.ExpectReport(ReportTypeRace); - impl_->send(&event); -} diff --git a/contrib/compiler-rt/lib/tsan/tests/rtl/tsan_thread.cc b/contrib/compiler-rt/lib/tsan/tests/rtl/tsan_thread.cc deleted file mode 100644 index 5646415a79b8..000000000000 --- a/contrib/compiler-rt/lib/tsan/tests/rtl/tsan_thread.cc +++ /dev/null @@ -1,59 +0,0 @@ -//===-- tsan_thread.cc ----------------------------------------------------===// -// -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. -// -//===----------------------------------------------------------------------===// -// -// This file is a part of ThreadSanitizer (TSan), a race detector. -// -//===----------------------------------------------------------------------===// -#include "tsan_test_util.h" -#include "gtest/gtest.h" - -TEST(ThreadSanitizer, ThreadSync) { - MainThread t0; - MemLoc l; - t0.Write1(l); - { - ScopedThread t1; - t1.Write1(l); - } - t0.Write1(l); -} - -TEST(ThreadSanitizer, ThreadDetach1) { - ScopedThread t1(true); - MemLoc l; - t1.Write1(l); -} - -TEST(ThreadSanitizer, ThreadDetach2) { - ScopedThread t1; - MemLoc l; - t1.Write1(l); - t1.Detach(); -} - -static void *thread_alot_func(void *arg) { - (void)arg; - int usleep(unsigned); - usleep(50); - return 0; -} - -TEST(DISABLED_SLOW_ThreadSanitizer, ThreadALot) { - const int kThreads = 70000; - const int kAlive = 1000; - pthread_t threads[kAlive] = {}; - for (int i = 0; i < kThreads; i++) { - if (threads[i % kAlive]) - pthread_join(threads[i % kAlive], 0); - pthread_create(&threads[i % kAlive], 0, thread_alot_func, 0); - } - for (int i = 0; i < kAlive; i++) { - pthread_join(threads[i], 0); - } -} diff --git a/contrib/compiler-rt/lib/tsan/tests/unit/tsan_clock_test.cc b/contrib/compiler-rt/lib/tsan/tests/unit/tsan_clock_test.cc deleted file mode 100644 index 92071827d3d8..000000000000 --- a/contrib/compiler-rt/lib/tsan/tests/unit/tsan_clock_test.cc +++ /dev/null @@ -1,432 +0,0 @@ -//===-- tsan_clock_test.cc ------------------------------------------------===// -// -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. -// -//===----------------------------------------------------------------------===// -// -// This file is a part of ThreadSanitizer (TSan), a race detector. -// -//===----------------------------------------------------------------------===// -#include "tsan_clock.h" -#include "tsan_rtl.h" -#include "gtest/gtest.h" -#include <time.h> - -namespace __tsan { - -ClockCache cache; - -TEST(Clock, VectorBasic) { - ThreadClock clk(0); - ASSERT_EQ(clk.size(), 1U); - clk.tick(); - ASSERT_EQ(clk.size(), 1U); - ASSERT_EQ(clk.get(0), 1U); - clk.set(3, clk.get(3) + 1); - ASSERT_EQ(clk.size(), 4U); - ASSERT_EQ(clk.get(0), 1U); - ASSERT_EQ(clk.get(1), 0U); - ASSERT_EQ(clk.get(2), 0U); - ASSERT_EQ(clk.get(3), 1U); - clk.set(3, clk.get(3) + 1); - ASSERT_EQ(clk.get(3), 2U); -} - -TEST(Clock, ChunkedBasic) { - ThreadClock vector(0); - SyncClock chunked; - ASSERT_EQ(vector.size(), 1U); - ASSERT_EQ(chunked.size(), 0U); - vector.acquire(&cache, &chunked); - ASSERT_EQ(vector.size(), 1U); - ASSERT_EQ(chunked.size(), 0U); - vector.release(&cache, &chunked); - ASSERT_EQ(vector.size(), 1U); - ASSERT_EQ(chunked.size(), 1U); - vector.acq_rel(&cache, &chunked); - ASSERT_EQ(vector.size(), 1U); - ASSERT_EQ(chunked.size(), 1U); - chunked.Reset(&cache); -} - -TEST(Clock, AcquireRelease) { - ThreadClock vector1(100); - vector1.tick(); - SyncClock chunked; - vector1.release(&cache, &chunked); - ASSERT_EQ(chunked.size(), 101U); - ThreadClock vector2(0); - vector2.acquire(&cache, &chunked); - ASSERT_EQ(vector2.size(), 101U); - ASSERT_EQ(vector2.get(0), 0U); - ASSERT_EQ(vector2.get(1), 0U); - ASSERT_EQ(vector2.get(99), 0U); - ASSERT_EQ(vector2.get(100), 1U); - chunked.Reset(&cache); -} - -TEST(Clock, RepeatedAcquire) { - ThreadClock thr1(1); - thr1.tick(); - ThreadClock thr2(2); - thr2.tick(); - - SyncClock sync; - thr1.ReleaseStore(&cache, &sync); - - thr2.acquire(&cache, &sync); - thr2.acquire(&cache, &sync); - - sync.Reset(&cache); -} - -TEST(Clock, ManyThreads) { - SyncClock chunked; - for (unsigned i = 0; i < 100; i++) { - ThreadClock vector(0); - vector.tick(); - vector.set(i, 1); - vector.release(&cache, &chunked); - ASSERT_EQ(i + 1, chunked.size()); - vector.acquire(&cache, &chunked); - ASSERT_EQ(i + 1, vector.size()); - } - - for (unsigned i = 0; i < 100; i++) - ASSERT_EQ(1U, chunked.get(i)); - - ThreadClock vector(1); - vector.acquire(&cache, &chunked); - ASSERT_EQ(100U, vector.size()); - for (unsigned i = 0; i < 100; i++) - ASSERT_EQ(1U, vector.get(i)); - - chunked.Reset(&cache); -} - -TEST(Clock, DifferentSizes) { - { - ThreadClock vector1(10); - vector1.tick(); - ThreadClock vector2(20); - vector2.tick(); - { - SyncClock chunked; - vector1.release(&cache, &chunked); - ASSERT_EQ(chunked.size(), 11U); - vector2.release(&cache, &chunked); - ASSERT_EQ(chunked.size(), 21U); - chunked.Reset(&cache); - } - { - SyncClock chunked; - vector2.release(&cache, &chunked); - ASSERT_EQ(chunked.size(), 21U); - vector1.release(&cache, &chunked); - ASSERT_EQ(chunked.size(), 21U); - chunked.Reset(&cache); - } - { - SyncClock chunked; - vector1.release(&cache, &chunked); - vector2.acquire(&cache, &chunked); - ASSERT_EQ(vector2.size(), 21U); - chunked.Reset(&cache); - } - { - SyncClock chunked; - vector2.release(&cache, &chunked); - vector1.acquire(&cache, &chunked); - ASSERT_EQ(vector1.size(), 21U); - chunked.Reset(&cache); - } - } -} - -TEST(Clock, Growth) { - { - ThreadClock vector(10); - vector.tick(); - vector.set(5, 42); - SyncClock sync; - vector.release(&cache, &sync); - ASSERT_EQ(sync.size(), 11U); - ASSERT_EQ(sync.get(0), 0ULL); - ASSERT_EQ(sync.get(1), 0ULL); - ASSERT_EQ(sync.get(5), 42ULL); - ASSERT_EQ(sync.get(9), 0ULL); - ASSERT_EQ(sync.get(10), 1ULL); - sync.Reset(&cache); - } - { - ThreadClock vector1(10); - vector1.tick(); - ThreadClock vector2(20); - vector2.tick(); - SyncClock sync; - vector1.release(&cache, &sync); - vector2.release(&cache, &sync); - ASSERT_EQ(sync.size(), 21U); - ASSERT_EQ(sync.get(0), 0ULL); - ASSERT_EQ(sync.get(10), 1ULL); - ASSERT_EQ(sync.get(19), 0ULL); - ASSERT_EQ(sync.get(20), 1ULL); - sync.Reset(&cache); - } - { - ThreadClock vector(100); - vector.tick(); - vector.set(5, 42); - vector.set(90, 84); - SyncClock sync; - vector.release(&cache, &sync); - ASSERT_EQ(sync.size(), 101U); - ASSERT_EQ(sync.get(0), 0ULL); - ASSERT_EQ(sync.get(1), 0ULL); - ASSERT_EQ(sync.get(5), 42ULL); - ASSERT_EQ(sync.get(60), 0ULL); - ASSERT_EQ(sync.get(70), 0ULL); - ASSERT_EQ(sync.get(90), 84ULL); - ASSERT_EQ(sync.get(99), 0ULL); - ASSERT_EQ(sync.get(100), 1ULL); - sync.Reset(&cache); - } - { - ThreadClock vector1(10); - vector1.tick(); - ThreadClock vector2(100); - vector2.tick(); - SyncClock sync; - vector1.release(&cache, &sync); - vector2.release(&cache, &sync); - ASSERT_EQ(sync.size(), 101U); - ASSERT_EQ(sync.get(0), 0ULL); - ASSERT_EQ(sync.get(10), 1ULL); - ASSERT_EQ(sync.get(99), 0ULL); - ASSERT_EQ(sync.get(100), 1ULL); - sync.Reset(&cache); - } -} - -const uptr kThreads = 4; -const uptr kClocks = 4; - -// SimpleSyncClock and SimpleThreadClock implement the same thing as -// SyncClock and ThreadClock, but in a very simple way. -struct SimpleSyncClock { - u64 clock[kThreads]; - uptr size; - - SimpleSyncClock() { - Reset(); - } - - void Reset() { - size = 0; - for (uptr i = 0; i < kThreads; i++) - clock[i] = 0; - } - - bool verify(const SyncClock *other) const { - for (uptr i = 0; i < min(size, other->size()); i++) { - if (clock[i] != other->get(i)) - return false; - } - for (uptr i = min(size, other->size()); i < max(size, other->size()); i++) { - if (i < size && clock[i] != 0) - return false; - if (i < other->size() && other->get(i) != 0) - return false; - } - return true; - } -}; - -struct SimpleThreadClock { - u64 clock[kThreads]; - uptr size; - unsigned tid; - - explicit SimpleThreadClock(unsigned tid) { - this->tid = tid; - size = tid + 1; - for (uptr i = 0; i < kThreads; i++) - clock[i] = 0; - } - - void tick() { - clock[tid]++; - } - - void acquire(const SimpleSyncClock *src) { - if (size < src->size) - size = src->size; - for (uptr i = 0; i < kThreads; i++) - clock[i] = max(clock[i], src->clock[i]); - } - - void release(SimpleSyncClock *dst) const { - if (dst->size < size) - dst->size = size; - for (uptr i = 0; i < kThreads; i++) - dst->clock[i] = max(dst->clock[i], clock[i]); - } - - void acq_rel(SimpleSyncClock *dst) { - acquire(dst); - release(dst); - } - - void ReleaseStore(SimpleSyncClock *dst) const { - if (dst->size < size) - dst->size = size; - for (uptr i = 0; i < kThreads; i++) - dst->clock[i] = clock[i]; - } - - bool verify(const ThreadClock *other) const { - for (uptr i = 0; i < min(size, other->size()); i++) { - if (clock[i] != other->get(i)) - return false; - } - for (uptr i = min(size, other->size()); i < max(size, other->size()); i++) { - if (i < size && clock[i] != 0) - return false; - if (i < other->size() && other->get(i) != 0) - return false; - } - return true; - } -}; - -static bool ClockFuzzer(bool printing) { - // Create kThreads thread clocks. - SimpleThreadClock *thr0[kThreads]; - ThreadClock *thr1[kThreads]; - unsigned reused[kThreads]; - for (unsigned i = 0; i < kThreads; i++) { - reused[i] = 0; - thr0[i] = new SimpleThreadClock(i); - thr1[i] = new ThreadClock(i, reused[i]); - } - - // Create kClocks sync clocks. - SimpleSyncClock *sync0[kClocks]; - SyncClock *sync1[kClocks]; - for (unsigned i = 0; i < kClocks; i++) { - sync0[i] = new SimpleSyncClock(); - sync1[i] = new SyncClock(); - } - - // Do N random operations (acquire, release, etc) and compare results - // for SimpleThread/SyncClock and real Thread/SyncClock. - for (int i = 0; i < 10000; i++) { - unsigned tid = rand() % kThreads; - unsigned cid = rand() % kClocks; - thr0[tid]->tick(); - thr1[tid]->tick(); - - switch (rand() % 6) { - case 0: - if (printing) - printf("acquire thr%d <- clk%d\n", tid, cid); - thr0[tid]->acquire(sync0[cid]); - thr1[tid]->acquire(&cache, sync1[cid]); - break; - case 1: - if (printing) - printf("release thr%d -> clk%d\n", tid, cid); - thr0[tid]->release(sync0[cid]); - thr1[tid]->release(&cache, sync1[cid]); - break; - case 2: - if (printing) - printf("acq_rel thr%d <> clk%d\n", tid, cid); - thr0[tid]->acq_rel(sync0[cid]); - thr1[tid]->acq_rel(&cache, sync1[cid]); - break; - case 3: - if (printing) - printf("rel_str thr%d >> clk%d\n", tid, cid); - thr0[tid]->ReleaseStore(sync0[cid]); - thr1[tid]->ReleaseStore(&cache, sync1[cid]); - break; - case 4: - if (printing) - printf("reset clk%d\n", cid); - sync0[cid]->Reset(); - sync1[cid]->Reset(&cache); - break; - case 5: - if (printing) - printf("reset thr%d\n", tid); - u64 epoch = thr0[tid]->clock[tid] + 1; - reused[tid]++; - delete thr0[tid]; - thr0[tid] = new SimpleThreadClock(tid); - thr0[tid]->clock[tid] = epoch; - delete thr1[tid]; - thr1[tid] = new ThreadClock(tid, reused[tid]); - thr1[tid]->set(epoch); - break; - } - - if (printing) { - for (unsigned i = 0; i < kThreads; i++) { - printf("thr%d: ", i); - thr1[i]->DebugDump(printf); - printf("\n"); - } - for (unsigned i = 0; i < kClocks; i++) { - printf("clk%d: ", i); - sync1[i]->DebugDump(printf); - printf("\n"); - } - - printf("\n"); - } - - if (!thr0[tid]->verify(thr1[tid]) || !sync0[cid]->verify(sync1[cid])) { - if (!printing) - return false; - printf("differs with model:\n"); - for (unsigned i = 0; i < kThreads; i++) { - printf("thr%d: clock=[", i); - for (uptr j = 0; j < thr0[i]->size; j++) - printf("%s%llu", j == 0 ? "" : ",", thr0[i]->clock[j]); - printf("]\n"); - } - for (unsigned i = 0; i < kClocks; i++) { - printf("clk%d: clock=[", i); - for (uptr j = 0; j < sync0[i]->size; j++) - printf("%s%llu", j == 0 ? "" : ",", sync0[i]->clock[j]); - printf("]\n"); - } - return false; - } - } - - for (unsigned i = 0; i < kClocks; i++) { - sync1[i]->Reset(&cache); - } - return true; -} - -TEST(Clock, Fuzzer) { - timespec ts; - clock_gettime(CLOCK_MONOTONIC, &ts); - int seed = ts.tv_sec + ts.tv_nsec; - printf("seed=%d\n", seed); - srand(seed); - if (!ClockFuzzer(false)) { - // Redo the test with the same seed, but logging operations. - srand(seed); - ClockFuzzer(true); - ASSERT_TRUE(false); - } -} - -} // namespace __tsan diff --git a/contrib/compiler-rt/lib/tsan/tests/unit/tsan_dense_alloc_test.cc b/contrib/compiler-rt/lib/tsan/tests/unit/tsan_dense_alloc_test.cc deleted file mode 100644 index e848e48c6641..000000000000 --- a/contrib/compiler-rt/lib/tsan/tests/unit/tsan_dense_alloc_test.cc +++ /dev/null @@ -1,55 +0,0 @@ -//===-- tsan_dense_alloc_test.cc ------------------------------------------===// -// -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. -// -//===----------------------------------------------------------------------===// -// -// This file is a part of ThreadSanitizer (TSan), a race detector. -// -//===----------------------------------------------------------------------===// -#include "tsan_dense_alloc.h" -#include "tsan_rtl.h" -#include "tsan_mman.h" -#include "gtest/gtest.h" - -#include <stdlib.h> -#include <stdint.h> -#include <map> - -namespace __tsan { - -TEST(DenseSlabAlloc, Basic) { - typedef DenseSlabAlloc<int, 128, 128> Alloc; - typedef Alloc::Cache Cache; - typedef Alloc::IndexT IndexT; - const int N = 1000; - - Alloc alloc; - Cache cache; - alloc.InitCache(&cache); - - IndexT blocks[N]; - for (int ntry = 0; ntry < 3; ntry++) { - for (int i = 0; i < N; i++) { - IndexT idx = alloc.Alloc(&cache); - blocks[i] = idx; - EXPECT_NE(idx, 0U); - int *v = alloc.Map(idx); - *v = i; - } - - for (int i = 0; i < N; i++) { - IndexT idx = blocks[i]; - int *v = alloc.Map(idx); - EXPECT_EQ(*v, i); - alloc.Free(&cache, idx); - } - - alloc.FlushCache(&cache); - } -} - -} // namespace __tsan diff --git a/contrib/compiler-rt/lib/tsan/tests/unit/tsan_flags_test.cc b/contrib/compiler-rt/lib/tsan/tests/unit/tsan_flags_test.cc deleted file mode 100644 index 22610c0dc42f..000000000000 --- a/contrib/compiler-rt/lib/tsan/tests/unit/tsan_flags_test.cc +++ /dev/null @@ -1,180 +0,0 @@ -//===-- tsan_flags_test.cc ------------------------------------------------===// -// -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. -// -//===----------------------------------------------------------------------===// -// -// This file is a part of ThreadSanitizer (TSan), a race detector. -// -//===----------------------------------------------------------------------===// -#include "tsan_flags.h" -#include "tsan_rtl.h" -#include "gtest/gtest.h" -#include <string> - -namespace __tsan { - -TEST(Flags, Basic) { - // At least should not crash. - Flags f; - InitializeFlags(&f, 0); - InitializeFlags(&f, ""); -} - -TEST(Flags, DefaultValues) { - Flags f; - - f.enable_annotations = false; - f.exitcode = -11; - InitializeFlags(&f, ""); - EXPECT_EQ(66, f.exitcode); - EXPECT_EQ(true, f.enable_annotations); -} - -static const char *options1 = - " enable_annotations=0" - " suppress_equal_stacks=0" - " suppress_equal_addresses=0" - " report_bugs=0" - " report_thread_leaks=0" - " report_destroy_locked=0" - " report_mutex_bugs=0" - " report_signal_unsafe=0" - " report_atomic_races=0" - " force_seq_cst_atomics=0" - " print_benign=0" - " exitcode=111" - " halt_on_error=0" - " atexit_sleep_ms=222" - " profile_memory=qqq" - " flush_memory_ms=444" - " flush_symbolizer_ms=555" - " memory_limit_mb=666" - " stop_on_start=0" - " running_on_valgrind=0" - " history_size=5" - " io_sync=1" - " die_after_fork=true" - ""; - -static const char *options2 = - " enable_annotations=true" - " suppress_equal_stacks=true" - " suppress_equal_addresses=true" - " report_bugs=true" - " report_thread_leaks=true" - " report_destroy_locked=true" - " report_mutex_bugs=true" - " report_signal_unsafe=true" - " report_atomic_races=true" - " force_seq_cst_atomics=true" - " print_benign=true" - " exitcode=222" - " halt_on_error=true" - " atexit_sleep_ms=123" - " profile_memory=bbbbb" - " flush_memory_ms=234" - " flush_symbolizer_ms=345" - " memory_limit_mb=456" - " stop_on_start=true" - " running_on_valgrind=true" - " history_size=6" - " io_sync=2" - " die_after_fork=false" - ""; - -void VerifyOptions1(Flags *f) { - EXPECT_EQ(f->enable_annotations, 0); - EXPECT_EQ(f->suppress_equal_stacks, 0); - EXPECT_EQ(f->suppress_equal_addresses, 0); - EXPECT_EQ(f->report_bugs, 0); - EXPECT_EQ(f->report_thread_leaks, 0); - EXPECT_EQ(f->report_destroy_locked, 0); - EXPECT_EQ(f->report_mutex_bugs, 0); - EXPECT_EQ(f->report_signal_unsafe, 0); - EXPECT_EQ(f->report_atomic_races, 0); - EXPECT_EQ(f->force_seq_cst_atomics, 0); - EXPECT_EQ(f->print_benign, 0); - EXPECT_EQ(f->exitcode, 111); - EXPECT_EQ(f->halt_on_error, 0); - EXPECT_EQ(f->atexit_sleep_ms, 222); - EXPECT_EQ(f->profile_memory, std::string("qqq")); - EXPECT_EQ(f->flush_memory_ms, 444); - EXPECT_EQ(f->flush_symbolizer_ms, 555); - EXPECT_EQ(f->memory_limit_mb, 666); - EXPECT_EQ(f->stop_on_start, 0); - EXPECT_EQ(f->running_on_valgrind, 0); - EXPECT_EQ(f->history_size, 5); - EXPECT_EQ(f->io_sync, 1); - EXPECT_EQ(f->die_after_fork, true); -} - -void VerifyOptions2(Flags *f) { - EXPECT_EQ(f->enable_annotations, true); - EXPECT_EQ(f->suppress_equal_stacks, true); - EXPECT_EQ(f->suppress_equal_addresses, true); - EXPECT_EQ(f->report_bugs, true); - EXPECT_EQ(f->report_thread_leaks, true); - EXPECT_EQ(f->report_destroy_locked, true); - EXPECT_EQ(f->report_mutex_bugs, true); - EXPECT_EQ(f->report_signal_unsafe, true); - EXPECT_EQ(f->report_atomic_races, true); - EXPECT_EQ(f->force_seq_cst_atomics, true); - EXPECT_EQ(f->print_benign, true); - EXPECT_EQ(f->exitcode, 222); - EXPECT_EQ(f->halt_on_error, true); - EXPECT_EQ(f->atexit_sleep_ms, 123); - EXPECT_EQ(f->profile_memory, std::string("bbbbb")); - EXPECT_EQ(f->flush_memory_ms, 234); - EXPECT_EQ(f->flush_symbolizer_ms, 345); - EXPECT_EQ(f->memory_limit_mb, 456); - EXPECT_EQ(f->stop_on_start, true); - EXPECT_EQ(f->running_on_valgrind, true); - EXPECT_EQ(f->history_size, 6); - EXPECT_EQ(f->io_sync, 2); - EXPECT_EQ(f->die_after_fork, false); -} - -static const char *test_default_options; -extern "C" const char *__tsan_default_options() { - return test_default_options; -} - -TEST(Flags, ParseDefaultOptions) { - Flags f; - - test_default_options = options1; - InitializeFlags(&f, ""); - VerifyOptions1(&f); - - test_default_options = options2; - InitializeFlags(&f, ""); - VerifyOptions2(&f); -} - -TEST(Flags, ParseEnvOptions) { - Flags f; - - InitializeFlags(&f, options1); - VerifyOptions1(&f); - - InitializeFlags(&f, options2); - VerifyOptions2(&f); -} - -TEST(Flags, ParsePriority) { - Flags f; - - test_default_options = options2; - InitializeFlags(&f, options1); - VerifyOptions1(&f); - - test_default_options = options1; - InitializeFlags(&f, options2); - VerifyOptions2(&f); -} - -} // namespace __tsan diff --git a/contrib/compiler-rt/lib/tsan/tests/unit/tsan_mman_test.cc b/contrib/compiler-rt/lib/tsan/tests/unit/tsan_mman_test.cc deleted file mode 100644 index bfaefe648705..000000000000 --- a/contrib/compiler-rt/lib/tsan/tests/unit/tsan_mman_test.cc +++ /dev/null @@ -1,153 +0,0 @@ -//===-- tsan_mman_test.cc -------------------------------------------------===// -// -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. -// -//===----------------------------------------------------------------------===// -// -// This file is a part of ThreadSanitizer (TSan), a race detector. -// -//===----------------------------------------------------------------------===// -#include <limits> -#include <sanitizer/allocator_interface.h> -#include "tsan_mman.h" -#include "tsan_rtl.h" -#include "gtest/gtest.h" - -namespace __tsan { - -TEST(Mman, Internal) { - char *p = (char*)internal_alloc(MBlockScopedBuf, 10); - EXPECT_NE(p, (char*)0); - char *p2 = (char*)internal_alloc(MBlockScopedBuf, 20); - EXPECT_NE(p2, (char*)0); - EXPECT_NE(p2, p); - for (int i = 0; i < 10; i++) { - p[i] = 42; - } - for (int i = 0; i < 20; i++) { - ((char*)p2)[i] = 42; - } - internal_free(p); - internal_free(p2); -} - -TEST(Mman, User) { - ThreadState *thr = cur_thread(); - uptr pc = 0; - char *p = (char*)user_alloc(thr, pc, 10); - EXPECT_NE(p, (char*)0); - char *p2 = (char*)user_alloc(thr, pc, 20); - EXPECT_NE(p2, (char*)0); - EXPECT_NE(p2, p); - EXPECT_EQ(10U, user_alloc_usable_size(p)); - EXPECT_EQ(20U, user_alloc_usable_size(p2)); - user_free(thr, pc, p); - user_free(thr, pc, p2); -} - -TEST(Mman, UserRealloc) { - ThreadState *thr = cur_thread(); - uptr pc = 0; - { - void *p = user_realloc(thr, pc, 0, 0); - // Strictly saying this is incorrect, realloc(NULL, N) is equivalent to - // malloc(N), thus must return non-NULL pointer. - EXPECT_EQ(p, (void*)0); - } - { - void *p = user_realloc(thr, pc, 0, 100); - EXPECT_NE(p, (void*)0); - memset(p, 0xde, 100); - user_free(thr, pc, p); - } - { - void *p = user_alloc(thr, pc, 100); - EXPECT_NE(p, (void*)0); - memset(p, 0xde, 100); - void *p2 = user_realloc(thr, pc, p, 0); - EXPECT_EQ(p2, (void*)0); - } - { - void *p = user_realloc(thr, pc, 0, 100); - EXPECT_NE(p, (void*)0); - memset(p, 0xde, 100); - void *p2 = user_realloc(thr, pc, p, 10000); - EXPECT_NE(p2, (void*)0); - for (int i = 0; i < 100; i++) - EXPECT_EQ(((char*)p2)[i], (char)0xde); - memset(p2, 0xde, 10000); - user_free(thr, pc, p2); - } - { - void *p = user_realloc(thr, pc, 0, 10000); - EXPECT_NE(p, (void*)0); - memset(p, 0xde, 10000); - void *p2 = user_realloc(thr, pc, p, 10); - EXPECT_NE(p2, (void*)0); - for (int i = 0; i < 10; i++) - EXPECT_EQ(((char*)p2)[i], (char)0xde); - user_free(thr, pc, p2); - } -} - -TEST(Mman, UsableSize) { - ThreadState *thr = cur_thread(); - uptr pc = 0; - char *p = (char*)user_alloc(thr, pc, 10); - char *p2 = (char*)user_alloc(thr, pc, 20); - EXPECT_EQ(0U, user_alloc_usable_size(NULL)); - EXPECT_EQ(10U, user_alloc_usable_size(p)); - EXPECT_EQ(20U, user_alloc_usable_size(p2)); - user_free(thr, pc, p); - user_free(thr, pc, p2); - EXPECT_EQ(0U, user_alloc_usable_size((void*)0x4123)); -} - -TEST(Mman, Stats) { - ThreadState *thr = cur_thread(); - - uptr alloc0 = __sanitizer_get_current_allocated_bytes(); - uptr heap0 = __sanitizer_get_heap_size(); - uptr free0 = __sanitizer_get_free_bytes(); - uptr unmapped0 = __sanitizer_get_unmapped_bytes(); - - EXPECT_EQ(10U, __sanitizer_get_estimated_allocated_size(10)); - EXPECT_EQ(20U, __sanitizer_get_estimated_allocated_size(20)); - EXPECT_EQ(100U, __sanitizer_get_estimated_allocated_size(100)); - - char *p = (char*)user_alloc(thr, 0, 10); - EXPECT_TRUE(__sanitizer_get_ownership(p)); - EXPECT_EQ(10U, __sanitizer_get_allocated_size(p)); - - EXPECT_EQ(alloc0 + 16, __sanitizer_get_current_allocated_bytes()); - EXPECT_GE(__sanitizer_get_heap_size(), heap0); - EXPECT_EQ(free0, __sanitizer_get_free_bytes()); - EXPECT_EQ(unmapped0, __sanitizer_get_unmapped_bytes()); - - user_free(thr, 0, p); - - EXPECT_EQ(alloc0, __sanitizer_get_current_allocated_bytes()); - EXPECT_GE(__sanitizer_get_heap_size(), heap0); - EXPECT_EQ(free0, __sanitizer_get_free_bytes()); - EXPECT_EQ(unmapped0, __sanitizer_get_unmapped_bytes()); -} - -TEST(Mman, CallocOverflow) { -#if SANITIZER_DEBUG - // EXPECT_DEATH clones a thread with 4K stack, - // which is overflown by tsan memory accesses functions in debug mode. - return; -#endif - size_t kArraySize = 4096; - volatile size_t kMaxSizeT = std::numeric_limits<size_t>::max(); - volatile size_t kArraySize2 = kMaxSizeT / kArraySize + 10; - volatile void *p = NULL; - EXPECT_DEATH(p = calloc(kArraySize, kArraySize2), - "allocator is terminating the process instead of returning 0"); - EXPECT_EQ(0L, p); -} - -} // namespace __tsan diff --git a/contrib/compiler-rt/lib/tsan/tests/unit/tsan_mutex_test.cc b/contrib/compiler-rt/lib/tsan/tests/unit/tsan_mutex_test.cc deleted file mode 100644 index cce7f073b92f..000000000000 --- a/contrib/compiler-rt/lib/tsan/tests/unit/tsan_mutex_test.cc +++ /dev/null @@ -1,126 +0,0 @@ -//===-- tsan_mutex_test.cc ------------------------------------------------===// -// -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. -// -//===----------------------------------------------------------------------===// -// -// This file is a part of ThreadSanitizer (TSan), a race detector. -// -//===----------------------------------------------------------------------===// -#include "sanitizer_common/sanitizer_internal_defs.h" -#include "sanitizer_common/sanitizer_atomic.h" -#include "sanitizer_common/sanitizer_common.h" -#include "sanitizer_common/sanitizer_mutex.h" -#include "tsan_mutex.h" -#include "gtest/gtest.h" - -namespace __tsan { - -template<typename MutexType> -class TestData { - public: - explicit TestData(MutexType *mtx) - : mtx_(mtx) { - for (int i = 0; i < kSize; i++) - data_[i] = 0; - } - - void Write() { - Lock l(mtx_); - T v0 = data_[0]; - for (int i = 0; i < kSize; i++) { - CHECK_EQ(data_[i], v0); - data_[i]++; - } - } - - void Read() { - ReadLock l(mtx_); - T v0 = data_[0]; - for (int i = 0; i < kSize; i++) { - CHECK_EQ(data_[i], v0); - } - } - - void Backoff() { - volatile T data[kSize] = {}; - for (int i = 0; i < kSize; i++) { - data[i]++; - CHECK_EQ(data[i], 1); - } - } - - private: - typedef GenericScopedLock<MutexType> Lock; - static const int kSize = 64; - typedef u64 T; - MutexType *mtx_; - char pad_[kCacheLineSize]; - T data_[kSize]; -}; - -const int kThreads = 8; -const int kWriteRate = 1024; -#if SANITIZER_DEBUG -const int kIters = 16*1024; -#else -const int kIters = 64*1024; -#endif - -template<typename MutexType> -static void *write_mutex_thread(void *param) { - TestData<MutexType> *data = (TestData<MutexType>*)param; - for (int i = 0; i < kIters; i++) { - data->Write(); - data->Backoff(); - } - return 0; -} - -template<typename MutexType> -static void *read_mutex_thread(void *param) { - TestData<MutexType> *data = (TestData<MutexType>*)param; - for (int i = 0; i < kIters; i++) { - if ((i % kWriteRate) == 0) - data->Write(); - else - data->Read(); - data->Backoff(); - } - return 0; -} - -TEST(Mutex, Write) { - Mutex mtx(MutexTypeAnnotations, StatMtxAnnotations); - TestData<Mutex> data(&mtx); - pthread_t threads[kThreads]; - for (int i = 0; i < kThreads; i++) - pthread_create(&threads[i], 0, write_mutex_thread<Mutex>, &data); - for (int i = 0; i < kThreads; i++) - pthread_join(threads[i], 0); -} - -TEST(Mutex, ReadWrite) { - Mutex mtx(MutexTypeAnnotations, StatMtxAnnotations); - TestData<Mutex> data(&mtx); - pthread_t threads[kThreads]; - for (int i = 0; i < kThreads; i++) - pthread_create(&threads[i], 0, read_mutex_thread<Mutex>, &data); - for (int i = 0; i < kThreads; i++) - pthread_join(threads[i], 0); -} - -TEST(Mutex, SpinWrite) { - SpinMutex mtx; - TestData<SpinMutex> data(&mtx); - pthread_t threads[kThreads]; - for (int i = 0; i < kThreads; i++) - pthread_create(&threads[i], 0, write_mutex_thread<SpinMutex>, &data); - for (int i = 0; i < kThreads; i++) - pthread_join(threads[i], 0); -} - -} // namespace __tsan diff --git a/contrib/compiler-rt/lib/tsan/tests/unit/tsan_mutexset_test.cc b/contrib/compiler-rt/lib/tsan/tests/unit/tsan_mutexset_test.cc deleted file mode 100644 index 335a7748cc1a..000000000000 --- a/contrib/compiler-rt/lib/tsan/tests/unit/tsan_mutexset_test.cc +++ /dev/null @@ -1,127 +0,0 @@ -//===-- tsan_mutexset_test.cc ---------------------------------------------===// -// -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. -// -//===----------------------------------------------------------------------===// -// -// This file is a part of ThreadSanitizer (TSan), a race detector. -// -//===----------------------------------------------------------------------===// -#include "tsan_mutexset.h" -#include "gtest/gtest.h" - -namespace __tsan { - -static void Expect(const MutexSet &mset, uptr i, u64 id, bool write, u64 epoch, - int count) { - MutexSet::Desc d = mset.Get(i); - EXPECT_EQ(id, d.id); - EXPECT_EQ(write, d.write); - EXPECT_EQ(epoch, d.epoch); - EXPECT_EQ(count, d.count); -} - -TEST(MutexSet, Basic) { - MutexSet mset; - EXPECT_EQ(mset.Size(), (uptr)0); - - mset.Add(1, true, 2); - EXPECT_EQ(mset.Size(), (uptr)1); - Expect(mset, 0, 1, true, 2, 1); - mset.Del(1, true); - EXPECT_EQ(mset.Size(), (uptr)0); - - mset.Add(3, true, 4); - mset.Add(5, false, 6); - EXPECT_EQ(mset.Size(), (uptr)2); - Expect(mset, 0, 3, true, 4, 1); - Expect(mset, 1, 5, false, 6, 1); - mset.Del(3, true); - EXPECT_EQ(mset.Size(), (uptr)1); - mset.Del(5, false); - EXPECT_EQ(mset.Size(), (uptr)0); -} - -TEST(MutexSet, DoubleAdd) { - MutexSet mset; - mset.Add(1, true, 2); - EXPECT_EQ(mset.Size(), (uptr)1); - Expect(mset, 0, 1, true, 2, 1); - - mset.Add(1, true, 2); - EXPECT_EQ(mset.Size(), (uptr)1); - Expect(mset, 0, 1, true, 2, 2); - - mset.Del(1, true); - EXPECT_EQ(mset.Size(), (uptr)1); - Expect(mset, 0, 1, true, 2, 1); - - mset.Del(1, true); - EXPECT_EQ(mset.Size(), (uptr)0); -} - -TEST(MutexSet, DoubleDel) { - MutexSet mset; - mset.Add(1, true, 2); - EXPECT_EQ(mset.Size(), (uptr)1); - mset.Del(1, true); - EXPECT_EQ(mset.Size(), (uptr)0); - mset.Del(1, true); - EXPECT_EQ(mset.Size(), (uptr)0); -} - -TEST(MutexSet, Remove) { - MutexSet mset; - mset.Add(1, true, 2); - mset.Add(1, true, 2); - mset.Add(3, true, 4); - mset.Add(3, true, 4); - EXPECT_EQ(mset.Size(), (uptr)2); - - mset.Remove(1); - EXPECT_EQ(mset.Size(), (uptr)1); - Expect(mset, 0, 3, true, 4, 2); -} - -TEST(MutexSet, Full) { - MutexSet mset; - for (uptr i = 0; i < MutexSet::kMaxSize; i++) { - mset.Add(i, true, i + 1); - } - EXPECT_EQ(mset.Size(), MutexSet::kMaxSize); - for (uptr i = 0; i < MutexSet::kMaxSize; i++) { - Expect(mset, i, i, true, i + 1, 1); - } - - for (uptr i = 0; i < MutexSet::kMaxSize; i++) { - mset.Add(i, true, i + 1); - } - EXPECT_EQ(mset.Size(), MutexSet::kMaxSize); - for (uptr i = 0; i < MutexSet::kMaxSize; i++) { - Expect(mset, i, i, true, i + 1, 2); - } -} - -TEST(MutexSet, Overflow) { - MutexSet mset; - for (uptr i = 0; i < MutexSet::kMaxSize; i++) { - mset.Add(i, true, i + 1); - mset.Add(i, true, i + 1); - } - mset.Add(100, true, 200); - EXPECT_EQ(mset.Size(), MutexSet::kMaxSize); - for (uptr i = 0; i < MutexSet::kMaxSize; i++) { - if (i == 0) - Expect(mset, i, MutexSet::kMaxSize - 1, - true, MutexSet::kMaxSize, 2); - else if (i == MutexSet::kMaxSize - 1) - Expect(mset, i, 100, true, 200, 1); - else - Expect(mset, i, i, true, i + 1, 2); - } -} - -} // namespace __tsan diff --git a/contrib/compiler-rt/lib/tsan/tests/unit/tsan_shadow_test.cc b/contrib/compiler-rt/lib/tsan/tests/unit/tsan_shadow_test.cc deleted file mode 100644 index 17b17977bf86..000000000000 --- a/contrib/compiler-rt/lib/tsan/tests/unit/tsan_shadow_test.cc +++ /dev/null @@ -1,78 +0,0 @@ -//===-- tsan_shadow_test.cc -----------------------------------------------===// -// -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. -// -//===----------------------------------------------------------------------===// -// -// This file is a part of ThreadSanitizer (TSan), a race detector. -// -//===----------------------------------------------------------------------===// -#include "tsan_platform.h" -#include "tsan_rtl.h" -#include "gtest/gtest.h" - -namespace __tsan { - -TEST(Shadow, FastState) { - Shadow s(FastState(11, 22)); - EXPECT_EQ(s.tid(), (u64)11); - EXPECT_EQ(s.epoch(), (u64)22); - EXPECT_EQ(s.GetIgnoreBit(), false); - EXPECT_EQ(s.GetFreedAndReset(), false); - EXPECT_EQ(s.GetHistorySize(), 0); - EXPECT_EQ(s.addr0(), (u64)0); - EXPECT_EQ(s.size(), (u64)1); - EXPECT_EQ(s.IsWrite(), true); - - s.IncrementEpoch(); - EXPECT_EQ(s.epoch(), (u64)23); - s.IncrementEpoch(); - EXPECT_EQ(s.epoch(), (u64)24); - - s.SetIgnoreBit(); - EXPECT_EQ(s.GetIgnoreBit(), true); - s.ClearIgnoreBit(); - EXPECT_EQ(s.GetIgnoreBit(), false); - - for (int i = 0; i < 8; i++) { - s.SetHistorySize(i); - EXPECT_EQ(s.GetHistorySize(), i); - } - s.SetHistorySize(2); - s.ClearHistorySize(); - EXPECT_EQ(s.GetHistorySize(), 0); -} - -TEST(Shadow, Mapping) { - static int global; - int stack; - void *heap = malloc(0); - free(heap); - - CHECK(IsAppMem((uptr)&global)); - CHECK(IsAppMem((uptr)&stack)); - CHECK(IsAppMem((uptr)heap)); - - CHECK(IsShadowMem(MemToShadow((uptr)&global))); - CHECK(IsShadowMem(MemToShadow((uptr)&stack))); - CHECK(IsShadowMem(MemToShadow((uptr)heap))); -} - -TEST(Shadow, Celling) { - u64 aligned_data[4]; - char *data = (char*)aligned_data; - CHECK_EQ((uptr)data % kShadowSize, 0); - uptr s0 = MemToShadow((uptr)&data[0]); - CHECK_EQ(s0 % kShadowSize, 0); - for (unsigned i = 1; i < kShadowCell; i++) - CHECK_EQ(s0, MemToShadow((uptr)&data[i])); - for (unsigned i = kShadowCell; i < 2*kShadowCell; i++) - CHECK_EQ(s0 + kShadowSize*kShadowCnt, MemToShadow((uptr)&data[i])); - for (unsigned i = 2*kShadowCell; i < 3*kShadowCell; i++) - CHECK_EQ(s0 + 2*kShadowSize*kShadowCnt, MemToShadow((uptr)&data[i])); -} - -} // namespace __tsan diff --git a/contrib/compiler-rt/lib/tsan/tests/unit/tsan_stack_test.cc b/contrib/compiler-rt/lib/tsan/tests/unit/tsan_stack_test.cc deleted file mode 100644 index 92e035d8d000..000000000000 --- a/contrib/compiler-rt/lib/tsan/tests/unit/tsan_stack_test.cc +++ /dev/null @@ -1,95 +0,0 @@ -//===-- tsan_stack_test.cc ------------------------------------------------===// -// -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. -// -//===----------------------------------------------------------------------===// -// -// This file is a part of ThreadSanitizer (TSan), a race detector. -// -//===----------------------------------------------------------------------===// -#include "tsan_sync.h" -#include "tsan_rtl.h" -#include "gtest/gtest.h" -#include <string.h> - -namespace __tsan { - -template <typename StackTraceTy> -static void TestStackTrace(StackTraceTy *trace) { - ThreadState thr(0, 0, 0, 0, 0, 0, 0, 0, 0); - uptr stack[128]; - thr.shadow_stack = &stack[0]; - thr.shadow_stack_pos = &stack[0]; - thr.shadow_stack_end = &stack[128]; - - ObtainCurrentStack(&thr, 0, trace); - EXPECT_EQ(0U, trace->size); - - ObtainCurrentStack(&thr, 42, trace); - EXPECT_EQ(1U, trace->size); - EXPECT_EQ(42U, trace->trace[0]); - - *thr.shadow_stack_pos++ = 100; - *thr.shadow_stack_pos++ = 101; - ObtainCurrentStack(&thr, 0, trace); - EXPECT_EQ(2U, trace->size); - EXPECT_EQ(100U, trace->trace[0]); - EXPECT_EQ(101U, trace->trace[1]); - - ObtainCurrentStack(&thr, 42, trace); - EXPECT_EQ(3U, trace->size); - EXPECT_EQ(100U, trace->trace[0]); - EXPECT_EQ(101U, trace->trace[1]); - EXPECT_EQ(42U, trace->trace[2]); -} - -template<typename StackTraceTy> -static void TestTrim(StackTraceTy *trace) { - ThreadState thr(0, 0, 0, 0, 0, 0, 0, 0, 0); - const uptr kShadowStackSize = 2 * kStackTraceMax; - uptr stack[kShadowStackSize]; - thr.shadow_stack = &stack[0]; - thr.shadow_stack_pos = &stack[0]; - thr.shadow_stack_end = &stack[kShadowStackSize]; - - for (uptr i = 0; i < kShadowStackSize; ++i) - *thr.shadow_stack_pos++ = 100 + i; - - ObtainCurrentStack(&thr, 0, trace); - EXPECT_EQ(kStackTraceMax, trace->size); - for (uptr i = 0; i < kStackTraceMax; i++) { - EXPECT_EQ(100 + kStackTraceMax + i, trace->trace[i]); - } - - ObtainCurrentStack(&thr, 42, trace); - EXPECT_EQ(kStackTraceMax, trace->size); - for (uptr i = 0; i < kStackTraceMax - 1; i++) { - EXPECT_EQ(101 + kStackTraceMax + i, trace->trace[i]); - } - EXPECT_EQ(42U, trace->trace[kStackTraceMax - 1]); -} - -TEST(StackTrace, BasicVarSize) { - VarSizeStackTrace trace; - TestStackTrace(&trace); -} - -TEST(StackTrace, BasicBuffered) { - BufferedStackTrace trace; - TestStackTrace(&trace); -} - -TEST(StackTrace, TrimVarSize) { - VarSizeStackTrace trace; - TestTrim(&trace); -} - -TEST(StackTrace, TrimBuffered) { - BufferedStackTrace trace; - TestTrim(&trace); -} - -} // namespace __tsan diff --git a/contrib/compiler-rt/lib/tsan/tests/unit/tsan_sync_test.cc b/contrib/compiler-rt/lib/tsan/tests/unit/tsan_sync_test.cc deleted file mode 100644 index d3616a1a4b81..000000000000 --- a/contrib/compiler-rt/lib/tsan/tests/unit/tsan_sync_test.cc +++ /dev/null @@ -1,123 +0,0 @@ -//===-- tsan_sync_test.cc -------------------------------------------------===// -// -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. -// -//===----------------------------------------------------------------------===// -// -// This file is a part of ThreadSanitizer (TSan), a race detector. -// -//===----------------------------------------------------------------------===// -#include "tsan_sync.h" -#include "tsan_rtl.h" -#include "gtest/gtest.h" - -namespace __tsan { - -TEST(MetaMap, Basic) { - ThreadState *thr = cur_thread(); - MetaMap *m = &ctx->metamap; - u64 block[1] = {}; // fake malloc block - m->AllocBlock(thr, 0, (uptr)&block[0], 1 * sizeof(u64)); - MBlock *mb = m->GetBlock((uptr)&block[0]); - EXPECT_NE(mb, (MBlock*)0); - EXPECT_EQ(mb->siz, 1 * sizeof(u64)); - EXPECT_EQ(mb->tid, thr->tid); - uptr sz = m->FreeBlock(thr, 0, (uptr)&block[0]); - EXPECT_EQ(sz, 1 * sizeof(u64)); - mb = m->GetBlock((uptr)&block[0]); - EXPECT_EQ(mb, (MBlock*)0); -} - -TEST(MetaMap, FreeRange) { - ThreadState *thr = cur_thread(); - MetaMap *m = &ctx->metamap; - u64 block[4] = {}; // fake malloc block - m->AllocBlock(thr, 0, (uptr)&block[0], 1 * sizeof(u64)); - m->AllocBlock(thr, 0, (uptr)&block[1], 3 * sizeof(u64)); - MBlock *mb1 = m->GetBlock((uptr)&block[0]); - EXPECT_EQ(mb1->siz, 1 * sizeof(u64)); - MBlock *mb2 = m->GetBlock((uptr)&block[1]); - EXPECT_EQ(mb2->siz, 3 * sizeof(u64)); - m->FreeRange(thr, 0, (uptr)&block[0], 4 * sizeof(u64)); - mb1 = m->GetBlock((uptr)&block[0]); - EXPECT_EQ(mb1, (MBlock*)0); - mb2 = m->GetBlock((uptr)&block[1]); - EXPECT_EQ(mb2, (MBlock*)0); -} - -TEST(MetaMap, Sync) { - ThreadState *thr = cur_thread(); - MetaMap *m = &ctx->metamap; - u64 block[4] = {}; // fake malloc block - m->AllocBlock(thr, 0, (uptr)&block[0], 4 * sizeof(u64)); - SyncVar *s1 = m->GetIfExistsAndLock((uptr)&block[0]); - EXPECT_EQ(s1, (SyncVar*)0); - s1 = m->GetOrCreateAndLock(thr, 0, (uptr)&block[0], true); - EXPECT_NE(s1, (SyncVar*)0); - EXPECT_EQ(s1->addr, (uptr)&block[0]); - s1->mtx.Unlock(); - SyncVar *s2 = m->GetOrCreateAndLock(thr, 0, (uptr)&block[1], false); - EXPECT_NE(s2, (SyncVar*)0); - EXPECT_EQ(s2->addr, (uptr)&block[1]); - s2->mtx.ReadUnlock(); - m->FreeBlock(thr, 0, (uptr)&block[0]); - s1 = m->GetIfExistsAndLock((uptr)&block[0]); - EXPECT_EQ(s1, (SyncVar*)0); - s2 = m->GetIfExistsAndLock((uptr)&block[1]); - EXPECT_EQ(s2, (SyncVar*)0); - m->OnThreadIdle(thr); -} - -TEST(MetaMap, MoveMemory) { - ThreadState *thr = cur_thread(); - MetaMap *m = &ctx->metamap; - u64 block1[4] = {}; // fake malloc block - u64 block2[4] = {}; // fake malloc block - m->AllocBlock(thr, 0, (uptr)&block1[0], 3 * sizeof(u64)); - m->AllocBlock(thr, 0, (uptr)&block1[3], 1 * sizeof(u64)); - SyncVar *s1 = m->GetOrCreateAndLock(thr, 0, (uptr)&block1[0], true); - s1->mtx.Unlock(); - SyncVar *s2 = m->GetOrCreateAndLock(thr, 0, (uptr)&block1[1], true); - s2->mtx.Unlock(); - m->MoveMemory((uptr)&block1[0], (uptr)&block2[0], 4 * sizeof(u64)); - MBlock *mb1 = m->GetBlock((uptr)&block1[0]); - EXPECT_EQ(mb1, (MBlock*)0); - MBlock *mb2 = m->GetBlock((uptr)&block1[3]); - EXPECT_EQ(mb2, (MBlock*)0); - mb1 = m->GetBlock((uptr)&block2[0]); - EXPECT_NE(mb1, (MBlock*)0); - EXPECT_EQ(mb1->siz, 3 * sizeof(u64)); - mb2 = m->GetBlock((uptr)&block2[3]); - EXPECT_NE(mb2, (MBlock*)0); - EXPECT_EQ(mb2->siz, 1 * sizeof(u64)); - s1 = m->GetIfExistsAndLock((uptr)&block1[0]); - EXPECT_EQ(s1, (SyncVar*)0); - s2 = m->GetIfExistsAndLock((uptr)&block1[1]); - EXPECT_EQ(s2, (SyncVar*)0); - s1 = m->GetIfExistsAndLock((uptr)&block2[0]); - EXPECT_NE(s1, (SyncVar*)0); - EXPECT_EQ(s1->addr, (uptr)&block2[0]); - s1->mtx.Unlock(); - s2 = m->GetIfExistsAndLock((uptr)&block2[1]); - EXPECT_NE(s2, (SyncVar*)0); - EXPECT_EQ(s2->addr, (uptr)&block2[1]); - s2->mtx.Unlock(); - m->FreeRange(thr, 0, (uptr)&block2[0], 4 * sizeof(u64)); -} - -TEST(MetaMap, ResetSync) { - ThreadState *thr = cur_thread(); - MetaMap *m = &ctx->metamap; - u64 block[1] = {}; // fake malloc block - m->AllocBlock(thr, 0, (uptr)&block[0], 1 * sizeof(u64)); - SyncVar *s = m->GetOrCreateAndLock(thr, 0, (uptr)&block[0], true); - s->Reset(thr); - s->mtx.Unlock(); - uptr sz = m->FreeBlock(thr, 0, (uptr)&block[0]); - EXPECT_EQ(sz, 1 * sizeof(u64)); -} - -} // namespace __tsan diff --git a/contrib/compiler-rt/lib/tsan/tests/unit/tsan_unit_test_main.cc b/contrib/compiler-rt/lib/tsan/tests/unit/tsan_unit_test_main.cc deleted file mode 100644 index 84d94dd03748..000000000000 --- a/contrib/compiler-rt/lib/tsan/tests/unit/tsan_unit_test_main.cc +++ /dev/null @@ -1,19 +0,0 @@ -//===-- tsan_unit_test_main.cc --------------------------------------------===// -// -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. -// -//===----------------------------------------------------------------------===// -// -// This file is a part of ThreadSanitizer (TSan), a race detector. -// -//===----------------------------------------------------------------------===// -#include "gtest/gtest.h" - -int main(int argc, char **argv) { - testing::GTEST_FLAG(death_test_style) = "threadsafe"; - testing::InitGoogleTest(&argc, argv); - return RUN_ALL_TESTS(); -} diff --git a/contrib/compiler-rt/lib/tsan/tests/unit/tsan_vector_test.cc b/contrib/compiler-rt/lib/tsan/tests/unit/tsan_vector_test.cc deleted file mode 100644 index c54ac1ee6de9..000000000000 --- a/contrib/compiler-rt/lib/tsan/tests/unit/tsan_vector_test.cc +++ /dev/null @@ -1,43 +0,0 @@ -//===-- tsan_vector_test.cc -----------------------------------------------===// -// -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. -// -//===----------------------------------------------------------------------===// -// -// This file is a part of ThreadSanitizer (TSan), a race detector. -// -//===----------------------------------------------------------------------===// -#include "tsan_vector.h" -#include "tsan_rtl.h" -#include "gtest/gtest.h" - -namespace __tsan { - -TEST(Vector, Basic) { - Vector<int> v(MBlockScopedBuf); - EXPECT_EQ(v.Size(), (uptr)0); - v.PushBack(42); - EXPECT_EQ(v.Size(), (uptr)1); - EXPECT_EQ(v[0], 42); - v.PushBack(43); - EXPECT_EQ(v.Size(), (uptr)2); - EXPECT_EQ(v[0], 42); - EXPECT_EQ(v[1], 43); -} - -TEST(Vector, Stride) { - Vector<int> v(MBlockScopedBuf); - for (int i = 0; i < 1000; i++) { - v.PushBack(i); - EXPECT_EQ(v.Size(), (uptr)(i + 1)); - EXPECT_EQ(v[i], i); - } - for (int i = 0; i < 1000; i++) { - EXPECT_EQ(v[i], i); - } -} - -} // namespace __tsan diff --git a/contrib/compiler-rt/lib/ubsan/ubsan_checks.inc b/contrib/compiler-rt/lib/ubsan/ubsan_checks.inc new file mode 100644 index 000000000000..6e086414051e --- /dev/null +++ b/contrib/compiler-rt/lib/ubsan/ubsan_checks.inc @@ -0,0 +1,45 @@ +//===-- ubsan_checks.inc ----------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// List of checks handled by UBSan runtime. +// +//===----------------------------------------------------------------------===// +#ifndef UBSAN_CHECK +# error "Define UBSAN_CHECK prior to including this file!" +#endif + +// UBSAN_CHECK(Name, SummaryKind, FSanitizeFlagName) +// SummaryKind and FSanitizeFlagName should be string literals. + +UBSAN_CHECK(GenericUB, "undefined-behavior", "undefined") +UBSAN_CHECK(NullPointerUse, "null-pointer-use", "null") +UBSAN_CHECK(MisalignedPointerUse, "misaligned-pointer-use", "alignment") +UBSAN_CHECK(InsufficientObjectSize, "insufficient-object-size", "object-size") +UBSAN_CHECK(SignedIntegerOverflow, "signed-integer-overflow", + "signed-integer-overflow") +UBSAN_CHECK(UnsignedIntegerOverflow, "unsigned-integer-overflow", + "unsigned-integer-overflow") +UBSAN_CHECK(IntegerDivideByZero, "integer-divide-by-zero", + "integer-divide-by-zero") +UBSAN_CHECK(FloatDivideByZero, "float-divide-by-zero", "float-divide-by-zero") +UBSAN_CHECK(InvalidShiftBase, "invalid-shift-base", "shift-base") +UBSAN_CHECK(InvalidShiftExponent, "invalid-shift-exponent", "shift-exponent") +UBSAN_CHECK(OutOfBoundsIndex, "out-of-bounds-index", "bounds") +UBSAN_CHECK(UnreachableCall, "unreachable-call", "unreachable") +UBSAN_CHECK(MissingReturn, "missing-return", "return") +UBSAN_CHECK(NonPositiveVLAIndex, "non-positive-vla-index", "vla-bound") +UBSAN_CHECK(FloatCastOverflow, "float-cast-overflow", "float-cast-overflow") +UBSAN_CHECK(InvalidBoolLoad, "invalid-bool-load", "bool") +UBSAN_CHECK(InvalidEnumLoad, "invalid-enum-load", "enum") +UBSAN_CHECK(FunctionTypeMismatch, "function-type-mismatch", "function") +UBSAN_CHECK(InvalidNullReturn, "invalid-null-return", + "returns-nonnull-attribute") +UBSAN_CHECK(InvalidNullArgument, "invalid-null-argument", "nonnull-attribute") +UBSAN_CHECK(DynamicTypeMismatch, "dynamic-type-mismatch", "vptr") +UBSAN_CHECK(CFIBadType, "cfi-bad-type", "cfi") diff --git a/contrib/compiler-rt/lib/ubsan/ubsan_diag.cc b/contrib/compiler-rt/lib/ubsan/ubsan_diag.cc index 3d5042b51d2e..2476947dc914 100644 --- a/contrib/compiler-rt/lib/ubsan/ubsan_diag.cc +++ b/contrib/compiler-rt/lib/ubsan/ubsan_diag.cc @@ -43,10 +43,34 @@ static void MaybePrintStackTrace(uptr pc, uptr bp) { stack.Print(); } -static void MaybeReportErrorSummary(Location Loc) { +static const char *ConvertTypeToString(ErrorType Type) { + switch (Type) { +#define UBSAN_CHECK(Name, SummaryKind, FSanitizeFlagName) \ + case ErrorType::Name: \ + return SummaryKind; +#include "ubsan_checks.inc" +#undef UBSAN_CHECK + } + UNREACHABLE("unknown ErrorType!"); +} + +static const char *ConvertTypeToFlagName(ErrorType Type) { + switch (Type) { +#define UBSAN_CHECK(Name, SummaryKind, FSanitizeFlagName) \ + case ErrorType::Name: \ + return FSanitizeFlagName; +#include "ubsan_checks.inc" +#undef UBSAN_CHECK + } + UNREACHABLE("unknown ErrorType!"); +} + +static void MaybeReportErrorSummary(Location Loc, ErrorType Type) { if (!common_flags()->print_summary) return; - const char *ErrorType = "undefined-behavior"; + if (!flags()->report_error_type) + Type = ErrorType::GenericUB; + const char *ErrorKind = ConvertTypeToString(Type); if (Loc.isSourceLocation()) { SourceLocation SLoc = Loc.getSourceLocation(); if (!SLoc.isInvalid()) { @@ -55,16 +79,16 @@ static void MaybeReportErrorSummary(Location Loc) { AI.line = SLoc.getLine(); AI.column = SLoc.getColumn(); AI.function = internal_strdup(""); // Avoid printing ?? as function name. - ReportErrorSummary(ErrorType, AI); + ReportErrorSummary(ErrorKind, AI); AI.Clear(); return; } } else if (Loc.isSymbolizedStack()) { const AddressInfo &AI = Loc.getSymbolizedStack()->info; - ReportErrorSummary(ErrorType, AI); + ReportErrorSummary(ErrorKind, AI); return; } - ReportErrorSummary(ErrorType); + ReportErrorSummary(ErrorKind); } namespace { @@ -341,24 +365,30 @@ Diag::~Diag() { NumRanges, Args); } -ScopedReport::ScopedReport(ReportOptions Opts, Location SummaryLoc) - : Opts(Opts), SummaryLoc(SummaryLoc) { +ScopedReport::ScopedReport(ReportOptions Opts, Location SummaryLoc, + ErrorType Type) + : Opts(Opts), SummaryLoc(SummaryLoc), Type(Type) { InitAsStandaloneIfNecessary(); CommonSanitizerReportMutex.Lock(); } ScopedReport::~ScopedReport() { MaybePrintStackTrace(Opts.pc, Opts.bp); - MaybeReportErrorSummary(SummaryLoc); + MaybeReportErrorSummary(SummaryLoc, Type); CommonSanitizerReportMutex.Unlock(); - if (Opts.DieAfterReport || flags()->halt_on_error) + if (flags()->halt_on_error) Die(); } ALIGNED(64) static char suppression_placeholder[sizeof(SuppressionContext)]; static SuppressionContext *suppression_ctx = nullptr; static const char kVptrCheck[] = "vptr_check"; -static const char *kSuppressionTypes[] = { kVptrCheck }; +static const char *kSuppressionTypes[] = { +#define UBSAN_CHECK(Name, SummaryKind, FSanitizeFlagName) FSanitizeFlagName, +#include "ubsan_checks.inc" +#undef UBSAN_CHECK + kVptrCheck, +}; void __ubsan::InitializeSuppressions() { CHECK_EQ(nullptr, suppression_ctx); @@ -374,4 +404,28 @@ bool __ubsan::IsVptrCheckSuppressed(const char *TypeName) { return suppression_ctx->Match(TypeName, kVptrCheck, &s); } +bool __ubsan::IsPCSuppressed(ErrorType ET, uptr PC, const char *Filename) { + InitAsStandaloneIfNecessary(); + CHECK(suppression_ctx); + const char *SuppType = ConvertTypeToFlagName(ET); + // Fast path: don't symbolize PC if there is no suppressions for given UB + // type. + if (!suppression_ctx->HasSuppressionType(SuppType)) + return false; + Suppression *s = nullptr; + // Suppress by file name known to runtime. + if (Filename != nullptr && suppression_ctx->Match(Filename, SuppType, &s)) + return true; + // Suppress by module name. + if (const char *Module = Symbolizer::GetOrInit()->GetModuleNameForPc(PC)) { + if (suppression_ctx->Match(Module, SuppType, &s)) + return true; + } + // Suppress by function or source file name from debug info. + SymbolizedStackHolder Stack(Symbolizer::GetOrInit()->SymbolizePC(PC)); + const AddressInfo &AI = Stack.get()->info; + return suppression_ctx->Match(AI.function, SuppType, &s) || + suppression_ctx->Match(AI.file, SuppType, &s); +} + #endif // CAN_SANITIZE_UB diff --git a/contrib/compiler-rt/lib/ubsan/ubsan_diag.h b/contrib/compiler-rt/lib/ubsan/ubsan_diag.h index 615a646d9419..3edb67a03c1f 100644 --- a/contrib/compiler-rt/lib/ubsan/ubsan_diag.h +++ b/contrib/compiler-rt/lib/ubsan/ubsan_diag.h @@ -211,17 +211,25 @@ public: }; struct ReportOptions { - /// If DieAfterReport is specified, UBSan will terminate the program after the - /// report is printed. - bool DieAfterReport; + // If FromUnrecoverableHandler is specified, UBSan runtime handler is not + // expected to return. + bool FromUnrecoverableHandler; /// pc/bp are used to unwind the stack trace. uptr pc; uptr bp; }; -#define GET_REPORT_OPTIONS(die_after_report) \ +enum class ErrorType { +#define UBSAN_CHECK(Name, SummaryKind, FSanitizeFlagName) Name, +#include "ubsan_checks.inc" +#undef UBSAN_CHECK +}; + +bool ignoreReport(SourceLocation SLoc, ReportOptions Opts, ErrorType ET); + +#define GET_REPORT_OPTIONS(unrecoverable_handler) \ GET_CALLER_PC_BP; \ - ReportOptions Opts = {die_after_report, pc, bp} + ReportOptions Opts = {unrecoverable_handler, pc, bp} /// \brief Instantiate this class before printing diagnostics in the error /// report. This class ensures that reports from different threads and from @@ -229,14 +237,18 @@ struct ReportOptions { class ScopedReport { ReportOptions Opts; Location SummaryLoc; + ErrorType Type; public: - ScopedReport(ReportOptions Opts, Location SummaryLoc); + ScopedReport(ReportOptions Opts, Location SummaryLoc, ErrorType Type); ~ScopedReport(); }; void InitializeSuppressions(); bool IsVptrCheckSuppressed(const char *TypeName); +// Sometimes UBSan runtime can know filename from handlers arguments, even if +// debug info is missing. +bool IsPCSuppressed(ErrorType ET, uptr PC, const char *Filename); } // namespace __ubsan diff --git a/contrib/compiler-rt/lib/ubsan/ubsan_flags.cc b/contrib/compiler-rt/lib/ubsan/ubsan_flags.cc index 6e5ad4c7967c..20087b968782 100644 --- a/contrib/compiler-rt/lib/ubsan/ubsan_flags.cc +++ b/contrib/compiler-rt/lib/ubsan/ubsan_flags.cc @@ -60,6 +60,9 @@ void InitializeFlags() { // Override from environment variable. parser.ParseString(GetEnv("UBSAN_OPTIONS")); SetVerbosity(common_flags()->verbosity); + if (Verbosity()) ReportUnrecognizedFlags(); + + if (common_flags()->help) parser.PrintFlagDescriptions(); } } // namespace __ubsan diff --git a/contrib/compiler-rt/lib/ubsan/ubsan_flags.inc b/contrib/compiler-rt/lib/ubsan/ubsan_flags.inc index 9ca31d13a9b6..d171a98e1730 100644 --- a/contrib/compiler-rt/lib/ubsan/ubsan_flags.inc +++ b/contrib/compiler-rt/lib/ubsan/ubsan_flags.inc @@ -22,4 +22,5 @@ UBSAN_FLAG(bool, halt_on_error, false, UBSAN_FLAG(bool, print_stacktrace, false, "Include full stacktrace into an error report") UBSAN_FLAG(const char *, suppressions, "", "Suppressions file name.") - +UBSAN_FLAG(bool, report_error_type, false, + "Print specific error type instead of 'undefined-behavior' in summary.") diff --git a/contrib/compiler-rt/lib/ubsan/ubsan_handlers.cc b/contrib/compiler-rt/lib/ubsan/ubsan_handlers.cc index a65b2f5e3fc7..5d82e9afcd09 100644 --- a/contrib/compiler-rt/lib/ubsan/ubsan_handlers.cc +++ b/contrib/compiler-rt/lib/ubsan/ubsan_handlers.cc @@ -21,17 +21,20 @@ using namespace __sanitizer; using namespace __ubsan; -static bool ignoreReport(SourceLocation SLoc, ReportOptions Opts) { - // If source location is already acquired, we don't need to print an error - // report for the second time. However, if we're in an unrecoverable handler, - // it's possible that location was required by concurrently running thread. - // In this case, we should continue the execution to ensure that any of - // threads will grab the report mutex and print the report before - // crashing the program. - return SLoc.isDisabled() && !Opts.DieAfterReport; +namespace __ubsan { +bool ignoreReport(SourceLocation SLoc, ReportOptions Opts, ErrorType ET) { + // We are not allowed to skip error report: if we are in unrecoverable + // handler, we have to terminate the program right now, and therefore + // have to print some diagnostic. + // + // Even if source location is disabled, it doesn't mean that we have + // already report an error to the user: some concurrently running + // thread could have acquired it, but not yet printed the report. + if (Opts.FromUnrecoverableHandler) + return false; + return SLoc.isDisabled() || IsPCSuppressed(ET, Opts.pc, SLoc.getFilename()); } -namespace __ubsan { const char *TypeCheckKinds[] = { "load of", "store to", "reference binding to", "member access within", "member call on", "constructor call on", "downcast of", "downcast of", @@ -41,8 +44,18 @@ const char *TypeCheckKinds[] = { static void handleTypeMismatchImpl(TypeMismatchData *Data, ValueHandle Pointer, ReportOptions Opts) { Location Loc = Data->Loc.acquire(); - // Use the SourceLocation from Data to track deduplication, even if 'invalid' - if (ignoreReport(Loc.getSourceLocation(), Opts)) + + ErrorType ET; + if (!Pointer) + ET = ErrorType::NullPointerUse; + else if (Data->Alignment && (Pointer & (Data->Alignment - 1))) + ET = ErrorType::MisalignedPointerUse; + else + ET = ErrorType::InsufficientObjectSize; + + // Use the SourceLocation from Data to track deduplication, even if it's + // invalid. + if (ignoreReport(Loc.getSourceLocation(), Opts, ET)) return; SymbolizedStackHolder FallbackLoc; @@ -51,20 +64,28 @@ static void handleTypeMismatchImpl(TypeMismatchData *Data, ValueHandle Pointer, Loc = FallbackLoc; } - ScopedReport R(Opts, Loc); + ScopedReport R(Opts, Loc, ET); - if (!Pointer) + switch (ET) { + case ErrorType::NullPointerUse: Diag(Loc, DL_Error, "%0 null pointer of type %1") - << TypeCheckKinds[Data->TypeCheckKind] << Data->Type; - else if (Data->Alignment && (Pointer & (Data->Alignment - 1))) + << TypeCheckKinds[Data->TypeCheckKind] << Data->Type; + break; + case ErrorType::MisalignedPointerUse: Diag(Loc, DL_Error, "%0 misaligned address %1 for type %3, " "which requires %2 byte alignment") - << TypeCheckKinds[Data->TypeCheckKind] << (void*)Pointer - << Data->Alignment << Data->Type; - else + << TypeCheckKinds[Data->TypeCheckKind] << (void *)Pointer + << Data->Alignment << Data->Type; + break; + case ErrorType::InsufficientObjectSize: Diag(Loc, DL_Error, "%0 address %1 with insufficient space " "for an object of type %2") - << TypeCheckKinds[Data->TypeCheckKind] << (void*)Pointer << Data->Type; + << TypeCheckKinds[Data->TypeCheckKind] << (void *)Pointer << Data->Type; + break; + default: + UNREACHABLE("unexpected error type!"); + } + if (Pointer) Diag(Pointer, DL_Note, "pointer points here"); } @@ -87,23 +108,28 @@ static void handleIntegerOverflowImpl(OverflowData *Data, ValueHandle LHS, const char *Operator, T RHS, ReportOptions Opts) { SourceLocation Loc = Data->Loc.acquire(); - if (ignoreReport(Loc, Opts)) + bool IsSigned = Data->Type.isSignedIntegerTy(); + ErrorType ET = IsSigned ? ErrorType::SignedIntegerOverflow + : ErrorType::UnsignedIntegerOverflow; + + if (ignoreReport(Loc, Opts, ET)) return; - ScopedReport R(Opts, Loc); + ScopedReport R(Opts, Loc, ET); Diag(Loc, DL_Error, "%0 integer overflow: " "%1 %2 %3 cannot be represented in type %4") - << (Data->Type.isSignedIntegerTy() ? "signed" : "unsigned") + << (IsSigned ? "signed" : "unsigned") << Value(Data->Type, LHS) << Operator << RHS << Data->Type; } -#define UBSAN_OVERFLOW_HANDLER(handler_name, op, abort) \ +#define UBSAN_OVERFLOW_HANDLER(handler_name, op, unrecoverable) \ void __ubsan::handler_name(OverflowData *Data, ValueHandle LHS, \ ValueHandle RHS) { \ - GET_REPORT_OPTIONS(abort); \ + GET_REPORT_OPTIONS(unrecoverable); \ handleIntegerOverflowImpl(Data, LHS, op, Value(Data->Type, RHS), Opts); \ - if (abort) Die(); \ + if (unrecoverable) \ + Die(); \ } UBSAN_OVERFLOW_HANDLER(__ubsan_handle_add_overflow, "+", false) @@ -116,20 +142,23 @@ UBSAN_OVERFLOW_HANDLER(__ubsan_handle_mul_overflow_abort, "*", true) static void handleNegateOverflowImpl(OverflowData *Data, ValueHandle OldVal, ReportOptions Opts) { SourceLocation Loc = Data->Loc.acquire(); - if (ignoreReport(Loc, Opts)) + bool IsSigned = Data->Type.isSignedIntegerTy(); + ErrorType ET = IsSigned ? ErrorType::SignedIntegerOverflow + : ErrorType::UnsignedIntegerOverflow; + + if (ignoreReport(Loc, Opts, ET)) return; - ScopedReport R(Opts, Loc); + ScopedReport R(Opts, Loc, ET); - if (Data->Type.isSignedIntegerTy()) + if (IsSigned) Diag(Loc, DL_Error, "negation of %0 cannot be represented in type %1; " "cast to an unsigned type to negate this value to itself") - << Value(Data->Type, OldVal) << Data->Type; + << Value(Data->Type, OldVal) << Data->Type; else - Diag(Loc, DL_Error, - "negation of %0 cannot be represented in type %1") - << Value(Data->Type, OldVal) << Data->Type; + Diag(Loc, DL_Error, "negation of %0 cannot be represented in type %1") + << Value(Data->Type, OldVal) << Data->Type; } void __ubsan::__ubsan_handle_negate_overflow(OverflowData *Data, @@ -147,19 +176,31 @@ void __ubsan::__ubsan_handle_negate_overflow_abort(OverflowData *Data, static void handleDivremOverflowImpl(OverflowData *Data, ValueHandle LHS, ValueHandle RHS, ReportOptions Opts) { SourceLocation Loc = Data->Loc.acquire(); - if (ignoreReport(Loc, Opts)) - return; - - ScopedReport R(Opts, Loc); - Value LHSVal(Data->Type, LHS); Value RHSVal(Data->Type, RHS); + + ErrorType ET; if (RHSVal.isMinusOne()) - Diag(Loc, DL_Error, - "division of %0 by -1 cannot be represented in type %1") - << LHSVal << Data->Type; + ET = ErrorType::SignedIntegerOverflow; + else if (Data->Type.isIntegerTy()) + ET = ErrorType::IntegerDivideByZero; else + ET = ErrorType::FloatDivideByZero; + + if (ignoreReport(Loc, Opts, ET)) + return; + + ScopedReport R(Opts, Loc, ET); + + switch (ET) { + case ErrorType::SignedIntegerOverflow: + Diag(Loc, DL_Error, "division of %0 by -1 cannot be represented in type %1") + << LHSVal << Data->Type; + break; + default: Diag(Loc, DL_Error, "division by zero"); + break; + } } void __ubsan::__ubsan_handle_divrem_overflow(OverflowData *Data, @@ -179,25 +220,35 @@ static void handleShiftOutOfBoundsImpl(ShiftOutOfBoundsData *Data, ValueHandle LHS, ValueHandle RHS, ReportOptions Opts) { SourceLocation Loc = Data->Loc.acquire(); - if (ignoreReport(Loc, Opts)) - return; - - ScopedReport R(Opts, Loc); - Value LHSVal(Data->LHSType, LHS); Value RHSVal(Data->RHSType, RHS); - if (RHSVal.isNegative()) - Diag(Loc, DL_Error, "shift exponent %0 is negative") << RHSVal; - else if (RHSVal.getPositiveIntValue() >= Data->LHSType.getIntegerBitWidth()) - Diag(Loc, DL_Error, - "shift exponent %0 is too large for %1-bit type %2") - << RHSVal << Data->LHSType.getIntegerBitWidth() << Data->LHSType; - else if (LHSVal.isNegative()) - Diag(Loc, DL_Error, "left shift of negative value %0") << LHSVal; + + ErrorType ET; + if (RHSVal.isNegative() || + RHSVal.getPositiveIntValue() >= Data->LHSType.getIntegerBitWidth()) + ET = ErrorType::InvalidShiftExponent; else - Diag(Loc, DL_Error, - "left shift of %0 by %1 places cannot be represented in type %2") - << LHSVal << RHSVal << Data->LHSType; + ET = ErrorType::InvalidShiftBase; + + if (ignoreReport(Loc, Opts, ET)) + return; + + ScopedReport R(Opts, Loc, ET); + + if (ET == ErrorType::InvalidShiftExponent) { + if (RHSVal.isNegative()) + Diag(Loc, DL_Error, "shift exponent %0 is negative") << RHSVal; + else + Diag(Loc, DL_Error, "shift exponent %0 is too large for %1-bit type %2") + << RHSVal << Data->LHSType.getIntegerBitWidth() << Data->LHSType; + } else { + if (LHSVal.isNegative()) + Diag(Loc, DL_Error, "left shift of negative value %0") << LHSVal; + else + Diag(Loc, DL_Error, + "left shift of %0 by %1 places cannot be represented in type %2") + << LHSVal << RHSVal << Data->LHSType; + } } void __ubsan::__ubsan_handle_shift_out_of_bounds(ShiftOutOfBoundsData *Data, @@ -218,10 +269,12 @@ void __ubsan::__ubsan_handle_shift_out_of_bounds_abort( static void handleOutOfBoundsImpl(OutOfBoundsData *Data, ValueHandle Index, ReportOptions Opts) { SourceLocation Loc = Data->Loc.acquire(); - if (ignoreReport(Loc, Opts)) + ErrorType ET = ErrorType::OutOfBoundsIndex; + + if (ignoreReport(Loc, Opts, ET)) return; - ScopedReport R(Opts, Loc); + ScopedReport R(Opts, Loc, ET); Value IndexVal(Data->IndexType, Index); Diag(Loc, DL_Error, "index %0 out of bounds for type %1") @@ -242,7 +295,7 @@ void __ubsan::__ubsan_handle_out_of_bounds_abort(OutOfBoundsData *Data, static void handleBuiltinUnreachableImpl(UnreachableData *Data, ReportOptions Opts) { - ScopedReport R(Opts, Data->Loc); + ScopedReport R(Opts, Data->Loc, ErrorType::UnreachableCall); Diag(Data->Loc, DL_Error, "execution reached a __builtin_unreachable() call"); } @@ -253,7 +306,7 @@ void __ubsan::__ubsan_handle_builtin_unreachable(UnreachableData *Data) { } static void handleMissingReturnImpl(UnreachableData *Data, ReportOptions Opts) { - ScopedReport R(Opts, Data->Loc); + ScopedReport R(Opts, Data->Loc, ErrorType::MissingReturn); Diag(Data->Loc, DL_Error, "execution reached the end of a value-returning function " "without returning a value"); @@ -268,10 +321,12 @@ void __ubsan::__ubsan_handle_missing_return(UnreachableData *Data) { static void handleVLABoundNotPositive(VLABoundData *Data, ValueHandle Bound, ReportOptions Opts) { SourceLocation Loc = Data->Loc.acquire(); - if (ignoreReport(Loc, Opts)) + ErrorType ET = ErrorType::NonPositiveVLAIndex; + + if (ignoreReport(Loc, Opts, ET)) return; - ScopedReport R(Opts, Loc); + ScopedReport R(Opts, Loc, ET); Diag(Loc, DL_Error, "variable length array bound evaluates to " "non-positive value %0") @@ -290,26 +345,60 @@ void __ubsan::__ubsan_handle_vla_bound_not_positive_abort(VLABoundData *Data, Die(); } -static void handleFloatCastOverflow(FloatCastOverflowData *Data, - ValueHandle From, ReportOptions Opts) { - // TODO: Add deduplication once a SourceLocation is generated for this check. - SymbolizedStackHolder CallerLoc(getCallerLocation(Opts.pc)); - Location Loc = CallerLoc; - ScopedReport R(Opts, Loc); +static bool looksLikeFloatCastOverflowDataV1(void *Data) { + // First field is either a pointer to filename or a pointer to a + // TypeDescriptor. + u8 *FilenameOrTypeDescriptor; + internal_memcpy(&FilenameOrTypeDescriptor, Data, + sizeof(FilenameOrTypeDescriptor)); + + // Heuristic: For float_cast_overflow, the TypeKind will be either TK_Integer + // (0x0), TK_Float (0x1) or TK_Unknown (0xff). If both types are known, + // adding both bytes will be 0 or 1 (for BE or LE). If it were a filename, + // adding two printable characters will not yield such a value. Otherwise, + // if one of them is 0xff, this is most likely TK_Unknown type descriptor. + u16 MaybeFromTypeKind = + FilenameOrTypeDescriptor[0] + FilenameOrTypeDescriptor[1]; + return MaybeFromTypeKind < 2 || FilenameOrTypeDescriptor[0] == 0xff || + FilenameOrTypeDescriptor[1] == 0xff; +} + +static void handleFloatCastOverflow(void *DataPtr, ValueHandle From, + ReportOptions Opts) { + SymbolizedStackHolder CallerLoc; + Location Loc; + const TypeDescriptor *FromType, *ToType; + ErrorType ET = ErrorType::FloatCastOverflow; + + if (looksLikeFloatCastOverflowDataV1(DataPtr)) { + auto Data = reinterpret_cast<FloatCastOverflowData *>(DataPtr); + CallerLoc.reset(getCallerLocation(Opts.pc)); + Loc = CallerLoc; + FromType = &Data->FromType; + ToType = &Data->ToType; + } else { + auto Data = reinterpret_cast<FloatCastOverflowDataV2 *>(DataPtr); + SourceLocation SLoc = Data->Loc.acquire(); + if (ignoreReport(SLoc, Opts, ET)) + return; + Loc = SLoc; + FromType = &Data->FromType; + ToType = &Data->ToType; + } + + ScopedReport R(Opts, Loc, ET); Diag(Loc, DL_Error, "value %0 is outside the range of representable values of type %2") - << Value(Data->FromType, From) << Data->FromType << Data->ToType; + << Value(*FromType, From) << *FromType << *ToType; } -void __ubsan::__ubsan_handle_float_cast_overflow(FloatCastOverflowData *Data, - ValueHandle From) { +void __ubsan::__ubsan_handle_float_cast_overflow(void *Data, ValueHandle From) { GET_REPORT_OPTIONS(false); handleFloatCastOverflow(Data, From, Opts); } -void -__ubsan::__ubsan_handle_float_cast_overflow_abort(FloatCastOverflowData *Data, - ValueHandle From) { +void __ubsan::__ubsan_handle_float_cast_overflow_abort(void *Data, + ValueHandle From) { GET_REPORT_OPTIONS(true); handleFloatCastOverflow(Data, From, Opts); Die(); @@ -318,10 +407,16 @@ __ubsan::__ubsan_handle_float_cast_overflow_abort(FloatCastOverflowData *Data, static void handleLoadInvalidValue(InvalidValueData *Data, ValueHandle Val, ReportOptions Opts) { SourceLocation Loc = Data->Loc.acquire(); - if (ignoreReport(Loc, Opts)) + // This check could be more precise if we used different handlers for + // -fsanitize=bool and -fsanitize=enum. + bool IsBool = (0 == internal_strcmp(Data->Type.getTypeName(), "'bool'")); + ErrorType ET = + IsBool ? ErrorType::InvalidBoolLoad : ErrorType::InvalidEnumLoad; + + if (ignoreReport(Loc, Opts, ET)) return; - ScopedReport R(Opts, Loc); + ScopedReport R(Opts, Loc, ET); Diag(Loc, DL_Error, "load of value %0, which is not a valid value for type %1") @@ -344,10 +439,12 @@ static void handleFunctionTypeMismatch(FunctionTypeMismatchData *Data, ValueHandle Function, ReportOptions Opts) { SourceLocation CallLoc = Data->Loc.acquire(); - if (ignoreReport(CallLoc, Opts)) + ErrorType ET = ErrorType::FunctionTypeMismatch; + + if (ignoreReport(CallLoc, Opts, ET)) return; - ScopedReport R(Opts, CallLoc); + ScopedReport R(Opts, CallLoc, ET); SymbolizedStackHolder FLoc(getSymbolizedLocation(Function)); const char *FName = FLoc.get()->info.function; @@ -376,10 +473,12 @@ void __ubsan::__ubsan_handle_function_type_mismatch_abort( static void handleNonNullReturn(NonNullReturnData *Data, ReportOptions Opts) { SourceLocation Loc = Data->Loc.acquire(); - if (ignoreReport(Loc, Opts)) + ErrorType ET = ErrorType::InvalidNullReturn; + + if (ignoreReport(Loc, Opts, ET)) return; - ScopedReport R(Opts, Loc); + ScopedReport R(Opts, Loc, ET); Diag(Loc, DL_Error, "null pointer returned from function declared to never " "return null"); @@ -400,10 +499,12 @@ void __ubsan::__ubsan_handle_nonnull_return_abort(NonNullReturnData *Data) { static void handleNonNullArg(NonNullArgData *Data, ReportOptions Opts) { SourceLocation Loc = Data->Loc.acquire(); - if (ignoreReport(Loc, Opts)) + ErrorType ET = ErrorType::InvalidNullArgument; + + if (ignoreReport(Loc, Opts, ET)) return; - ScopedReport R(Opts, Loc); + ScopedReport R(Opts, Loc, ET); Diag(Loc, DL_Error, "null pointer passed as argument %0, which is declared to " "never be null") << Data->ArgIndex; @@ -422,4 +523,38 @@ void __ubsan::__ubsan_handle_nonnull_arg_abort(NonNullArgData *Data) { Die(); } +static void handleCFIBadIcall(CFIBadIcallData *Data, ValueHandle Function, + ReportOptions Opts) { + SourceLocation Loc = Data->Loc.acquire(); + ErrorType ET = ErrorType::CFIBadType; + + if (ignoreReport(Loc, Opts, ET)) + return; + + ScopedReport R(Opts, Loc, ET); + + Diag(Loc, DL_Error, "control flow integrity check for type %0 failed during " + "indirect function call") + << Data->Type; + + SymbolizedStackHolder FLoc(getSymbolizedLocation(Function)); + const char *FName = FLoc.get()->info.function; + if (!FName) + FName = "(unknown)"; + Diag(FLoc, DL_Note, "%0 defined here") << FName; +} + +void __ubsan::__ubsan_handle_cfi_bad_icall(CFIBadIcallData *Data, + ValueHandle Function) { + GET_REPORT_OPTIONS(false); + handleCFIBadIcall(Data, Function, Opts); +} + +void __ubsan::__ubsan_handle_cfi_bad_icall_abort(CFIBadIcallData *Data, + ValueHandle Function) { + GET_REPORT_OPTIONS(true); + handleCFIBadIcall(Data, Function, Opts); + Die(); +} + #endif // CAN_SANITIZE_UB diff --git a/contrib/compiler-rt/lib/ubsan/ubsan_handlers.h b/contrib/compiler-rt/lib/ubsan/ubsan_handlers.h index 87149f259607..6f309cf9aaa9 100644 --- a/contrib/compiler-rt/lib/ubsan/ubsan_handlers.h +++ b/contrib/compiler-rt/lib/ubsan/ubsan_handlers.h @@ -97,14 +97,22 @@ struct VLABoundData { /// \brief Handle a VLA with a non-positive bound. RECOVERABLE(vla_bound_not_positive, VLABoundData *Data, ValueHandle Bound) +// Keeping this around for binary compatibility with (sanitized) programs +// compiled with older compilers. struct FloatCastOverflowData { - // FIXME: SourceLocation Loc; const TypeDescriptor &FromType; const TypeDescriptor &ToType; }; -/// \brief Handle overflow in a conversion to or from a floating-point type. -RECOVERABLE(float_cast_overflow, FloatCastOverflowData *Data, ValueHandle From) +struct FloatCastOverflowDataV2 { + SourceLocation Loc; + const TypeDescriptor &FromType; + const TypeDescriptor &ToType; +}; + +/// Handle overflow in a conversion to or from a floating-point type. +/// void *Data is one of FloatCastOverflowData* or FloatCastOverflowDataV2* +RECOVERABLE(float_cast_overflow, void *Data, ValueHandle From) struct InvalidValueData { SourceLocation Loc; @@ -140,6 +148,14 @@ struct NonNullArgData { /// \brief Handle passing null pointer to function with nonnull attribute. RECOVERABLE(nonnull_arg, NonNullArgData *Data) +struct CFIBadIcallData { + SourceLocation Loc; + const TypeDescriptor &Type; +}; + +/// \brief Handle control flow integrity failure for indirect function calls. +RECOVERABLE(cfi_bad_icall, CFIBadIcallData *Data, ValueHandle Function) + } #endif // UBSAN_HANDLERS_H diff --git a/contrib/compiler-rt/lib/ubsan/ubsan_handlers_cxx.cc b/contrib/compiler-rt/lib/ubsan/ubsan_handlers_cxx.cc index 6984a963deb6..3e81be67163b 100644 --- a/contrib/compiler-rt/lib/ubsan/ubsan_handlers_cxx.cc +++ b/contrib/compiler-rt/lib/ubsan/ubsan_handlers_cxx.cc @@ -29,23 +29,25 @@ namespace __ubsan { extern const char *TypeCheckKinds[]; } -static void HandleDynamicTypeCacheMiss( +// Returns true if UBSan has printed an error report. +static bool HandleDynamicTypeCacheMiss( DynamicTypeCacheMissData *Data, ValueHandle Pointer, ValueHandle Hash, ReportOptions Opts) { if (checkDynamicType((void*)Pointer, Data->TypeInfo, Hash)) // Just a cache miss. The type matches after all. - return; + return false; // Check if error report should be suppressed. DynamicTypeInfo DTI = getDynamicTypeInfoFromObject((void*)Pointer); if (DTI.isValid() && IsVptrCheckSuppressed(DTI.getMostDerivedTypeName())) - return; + return false; SourceLocation Loc = Data->Loc.acquire(); - if (Loc.isDisabled()) - return; + ErrorType ET = ErrorType::DynamicTypeMismatch; + if (ignoreReport(Loc, Opts, ET)) + return false; - ScopedReport R(Opts, Loc); + ScopedReport R(Opts, Loc, ET); Diag(Loc, DL_Error, "%0 address %1 which does not point to an object of type %2") @@ -69,6 +71,7 @@ static void HandleDynamicTypeCacheMiss( << TypeName(DTI.getSubobjectTypeName()) << Range(Pointer, Pointer + sizeof(uptr), "vptr for %2 base class of %1"); + return true; } void __ubsan::__ubsan_handle_dynamic_type_cache_miss( @@ -78,14 +81,21 @@ void __ubsan::__ubsan_handle_dynamic_type_cache_miss( } void __ubsan::__ubsan_handle_dynamic_type_cache_miss_abort( DynamicTypeCacheMissData *Data, ValueHandle Pointer, ValueHandle Hash) { - GET_REPORT_OPTIONS(true); - HandleDynamicTypeCacheMiss(Data, Pointer, Hash, Opts); + // Note: -fsanitize=vptr is always recoverable. + GET_REPORT_OPTIONS(false); + if (HandleDynamicTypeCacheMiss(Data, Pointer, Hash, Opts)) + Die(); } static void HandleCFIBadType(CFIBadTypeData *Data, ValueHandle Vtable, ReportOptions Opts) { SourceLocation Loc = Data->Loc.acquire(); - ScopedReport R(Opts, Loc); + ErrorType ET = ErrorType::CFIBadType; + + if (ignoreReport(Loc, Opts, ET)) + return; + + ScopedReport R(Opts, Loc, ET); DynamicTypeInfo DTI = getDynamicTypeInfoFromVtable((void*)Vtable); static const char *TypeCheckKinds[] = { @@ -117,6 +127,7 @@ void __ubsan::__ubsan_handle_cfi_bad_type_abort(CFIBadTypeData *Data, ValueHandle Vtable) { GET_REPORT_OPTIONS(true); HandleCFIBadType(Data, Vtable, Opts); + Die(); } #endif // CAN_SANITIZE_UB |