diff options
Diffstat (limited to 'lib')
87 files changed, 1357 insertions, 919 deletions
diff --git a/lib/Makefile.mk b/lib/Makefile.mk index ed9690d467aa..7eb6489fec8c 100644 --- a/lib/Makefile.mk +++ b/lib/Makefile.mk @@ -12,11 +12,8 @@ SubDirs := # Add submodules. SubDirs += asan SubDirs += builtins -SubDirs += dfsan SubDirs += interception SubDirs += lsan -SubDirs += msan SubDirs += profile SubDirs += sanitizer_common -SubDirs += tsan SubDirs += ubsan diff --git a/lib/asan/README.txt b/lib/asan/README.txt index b9c43acd5fe4..8cc9bb17b59d 100644 --- a/lib/asan/README.txt +++ b/lib/asan/README.txt @@ -1,7 +1,6 @@ AddressSanitizer RT ================================ -This directory contains sources of the AddressSanitizer (asan) runtime library. -We are in the process of integrating AddressSanitizer with LLVM, stay tuned. +This directory contains sources of the AddressSanitizer (ASan) runtime library. Directory structure: README.txt : This file. @@ -13,14 +12,13 @@ tests/* : ASan unit tests. Also ASan runtime needs the following libraries: lib/interception/ : Machinery used to intercept function calls. -lib/sanitizer_common/ : Code shared between ASan and TSan. +lib/sanitizer_common/ : Code shared between various sanitizers. -Currently ASan runtime can be built by both make and cmake build systems. -(see compiler-rt/make and files Makefile.mk for make-based build and -files CMakeLists.txt for cmake-based build). +ASan runtime currently also embeds part of LeakSanitizer runtime for +leak detection (lib/lsan/lsan_common.{cc,h}). -ASan unit and output tests work only with cmake. You may run this -command from the root of your cmake build tree: +ASan runtime can only be built by CMake. You can run ASan tests +from the root of your CMake build tree: make check-asan diff --git a/lib/asan/asan_flags.cc b/lib/asan/asan_flags.cc index 1d82ab0e725f..efb7767d5d91 100644 --- a/lib/asan/asan_flags.cc +++ b/lib/asan/asan_flags.cc @@ -46,18 +46,15 @@ void Flags::SetDefaults() { #undef ASAN_FLAG } -void RegisterAsanFlags(FlagParser *parser, Flags *f) { +static void RegisterAsanFlags(FlagParser *parser, Flags *f) { #define ASAN_FLAG(Type, Name, DefaultValue, Description) \ RegisterFlag(parser, #Name, Description, &f->Name); #include "asan_flags.inc" #undef ASAN_FLAG } -void InitializeFlags(Flags *f) { - FlagParser parser; - RegisterAsanFlags(&parser, f); - RegisterCommonFlags(&parser); - +void InitializeFlags() { + // Set the default values and prepare for parsing ASan and common flags. SetCommonFlagsDefaults(); { CommonFlags cf; @@ -68,28 +65,44 @@ void InitializeFlags(Flags *f) { cf.intercept_tls_get_addr = true; OverrideCommonFlags(cf); } - - const int kDefaultQuarantineSizeMb = (ASAN_LOW_MEMORY) ? 1UL << 6 : 1UL << 8; + Flags *f = flags(); f->SetDefaults(); - // Override from compile definition. - const char *compile_def = MaybeUseAsanDefaultOptionsCompileDefinition(); - parser.ParseString(compile_def); + FlagParser asan_parser; + RegisterAsanFlags(&asan_parser, f); + RegisterCommonFlags(&asan_parser); + + // Set the default values and prepare for parsing LSan flags (which can also + // overwrite common flags). +#if CAN_SANITIZE_LEAKS + __lsan::Flags *lf = __lsan::flags(); + lf->SetDefaults(); + + FlagParser lsan_parser; + __lsan::RegisterLsanFlags(&lsan_parser, lf); + RegisterCommonFlags(&lsan_parser); +#endif + + // Override from ASan compile definition. + const char *asan_compile_def = MaybeUseAsanDefaultOptionsCompileDefinition(); + asan_parser.ParseString(asan_compile_def); // Override from user-specified string. - const char *default_options = MaybeCallAsanDefaultOptions(); - parser.ParseString(default_options); + const char *asan_default_options = MaybeCallAsanDefaultOptions(); + asan_parser.ParseString(asan_default_options); // Override from command line. - const char *env = GetEnv("ASAN_OPTIONS"); - if (env) parser.ParseString(env); + asan_parser.ParseString(GetEnv("ASAN_OPTIONS")); +#if CAN_SANITIZE_LEAKS + lsan_parser.ParseString(GetEnv("LSAN_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)); - parser.ParseString(buf); + asan_parser.ParseString(buf); } SetVerbosity(common_flags()->verbosity); @@ -97,7 +110,10 @@ void InitializeFlags(Flags *f) { // TODO(eugenis): dump all flags at verbosity>=2? if (Verbosity()) ReportUnrecognizedFlags(); - if (common_flags()->help) parser.PrintFlagDescriptions(); + if (common_flags()->help) { + // TODO(samsonov): print all of the flags (ASan, LSan, common). + asan_parser.PrintFlagDescriptions(); + } // Flag validation: if (!CAN_SANITIZE_LEAKS && common_flags()->detect_leaks) { @@ -127,8 +143,11 @@ void InitializeFlags(Flags *f) { } if (f->quarantine_size >= 0) f->quarantine_size_mb = f->quarantine_size >> 20; - if (f->quarantine_size_mb < 0) + if (f->quarantine_size_mb < 0) { + const int kDefaultQuarantineSizeMb = + (ASAN_LOW_MEMORY) ? 1UL << 6 : 1UL << 8; f->quarantine_size_mb = kDefaultQuarantineSizeMb; + } } } // namespace __asan diff --git a/lib/asan/asan_flags.h b/lib/asan/asan_flags.h index 7653543f6d88..4935161c30f1 100644 --- a/lib/asan/asan_flags.h +++ b/lib/asan/asan_flags.h @@ -41,8 +41,8 @@ extern Flags asan_flags_dont_use_directly; inline Flags *flags() { return &asan_flags_dont_use_directly; } -void RegisterAsanFlags(FlagParser *parser, Flags *f); -void InitializeFlags(Flags *f); + +void InitializeFlags(); } // namespace __asan diff --git a/lib/asan/asan_flags.inc b/lib/asan/asan_flags.inc index ec9d41980d9c..53a8a4039e7e 100644 --- a/lib/asan/asan_flags.inc +++ b/lib/asan/asan_flags.inc @@ -142,3 +142,4 @@ ASAN_FLAG(int, detect_odr_violation, 2, "have different sizes") 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.") diff --git a/lib/asan/asan_globals.cc b/lib/asan/asan_globals.cc index c4571953c408..06140bbb360a 100644 --- a/lib/asan/asan_globals.cc +++ b/lib/asan/asan_globals.cc @@ -182,6 +182,8 @@ static void RegisterGlobal(const Global *g) { static void UnregisterGlobal(const Global *g) { CHECK(asan_inited); + if (flags()->report_globals >= 2) + ReportGlobal(*g, "Removed"); CHECK(flags()->report_globals); CHECK(AddrIsInMem(g->beg)); CHECK(AddrIsAlignedByGranularity(g->beg)); @@ -208,6 +210,20 @@ void StopInitOrderChecking() { } } +#if SANITIZER_WINDOWS // Should only be called on Windows. +SANITIZER_INTERFACE_ATTRIBUTE +void UnregisterGlobalsInRange(void *beg, void *end) { + if (!flags()->report_globals) + return; + BlockingMutexLock lock(&mu_for_globals); + for (ListOfGlobals *l = list_of_all_globals; l; l = l->next) { + void *address = (void *)l->g->beg; + if (beg <= address && address < end) + UnregisterGlobal(l->g); + } +} +#endif + } // namespace __asan // ---------------------- Interface ---------------- {{{1 diff --git a/lib/asan/asan_interface_internal.h b/lib/asan/asan_interface_internal.h index edaf44d7893a..ea7540f6bb56 100644 --- a/lib/asan/asan_interface_internal.h +++ b/lib/asan/asan_interface_internal.h @@ -9,8 +9,11 @@ // // This file is a part of AddressSanitizer, an address sanity checker. // -// This header can be included by the instrumented program to fetch -// data (mostly allocator statistics) from ASan runtime library. +// This header declares the AddressSanitizer runtime interface functions. +// The runtime library has to define these functions so the instrumented program +// could call them. +// +// See also include/sanitizer/asan_interface.h //===----------------------------------------------------------------------===// #ifndef ASAN_INTERFACE_INTERNAL_H #define ASAN_INTERFACE_INTERNAL_H diff --git a/lib/asan/asan_internal.h b/lib/asan/asan_internal.h index a8e23ce772ff..ffd3ff82d71e 100644 --- a/lib/asan/asan_internal.h +++ b/lib/asan/asan_internal.h @@ -93,6 +93,7 @@ void AsanCheckIncompatibleRT(); void GetPcSpBp(void *context, uptr *pc, uptr *sp, uptr *bp); void AsanOnSIGSEGV(int, void *siginfo, void *context); +void DisableReexec(); void MaybeReexec(); void ReadContextStack(void *context, uptr *stack, uptr *ssize); void AsanPlatformThreadInit(); diff --git a/lib/asan/asan_linux.cc b/lib/asan/asan_linux.cc index 65605009005f..8e8bafd47af6 100644 --- a/lib/asan/asan_linux.cc +++ b/lib/asan/asan_linux.cc @@ -68,6 +68,10 @@ asan_rt_version_t __asan_rt_version; namespace __asan { +void DisableReexec() { + // No need to re-exec on Linux. +} + void MaybeReexec() { // No need to re-exec on Linux. } diff --git a/lib/asan/asan_mac.cc b/lib/asan/asan_mac.cc index 5c2caeae4934..b35368617dca 100644 --- a/lib/asan/asan_mac.cc +++ b/lib/asan/asan_mac.cc @@ -101,7 +101,15 @@ void LeakyResetEnv(const char *name, const char *name_value) { } } +static bool reexec_disabled = false; + +void DisableReexec() { + reexec_disabled = true; +} + 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. diff --git a/lib/asan/asan_rtl.cc b/lib/asan/asan_rtl.cc index 0b2a23d40379..9126e71a6437 100644 --- a/lib/asan/asan_rtl.cc +++ b/lib/asan/asan_rtl.cc @@ -314,7 +314,7 @@ static void AsanInitInternal() { // Initialize flags. This must be done early, because most of the // initialization steps look at flags(). - InitializeFlags(flags()); + InitializeFlags(); SetCanPoisonMemory(flags()->poison_heap); SetMallocContextSize(common_flags()->malloc_context_size); @@ -440,7 +440,7 @@ static void AsanInitInternal() { SanitizerInitializeUnwinder(); #if CAN_SANITIZE_LEAKS - __lsan::InitCommonLsan(false); + __lsan::InitCommonLsan(); if (common_flags()->detect_leaks && common_flags()->leak_check_at_exit) { Atexit(__lsan::DoLeakCheck); } diff --git a/lib/asan/asan_suppressions.cc b/lib/asan/asan_suppressions.cc index ef554716faa0..62198aec64e7 100644 --- a/lib/asan/asan_suppressions.cc +++ b/lib/asan/asan_suppressions.cc @@ -15,57 +15,62 @@ #include "asan_suppressions.h" #include "asan_stack.h" +#include "sanitizer_common/sanitizer_placement_new.h" #include "sanitizer_common/sanitizer_suppressions.h" #include "sanitizer_common/sanitizer_symbolizer.h" namespace __asan { -static bool suppressions_inited = false; +ALIGNED(64) static char suppression_placeholder[sizeof(SuppressionContext)]; +static SuppressionContext *suppression_ctx = nullptr; +static const char kInterceptorName[] = "interceptor_name"; +static const char kInterceptorViaFunction[] = "interceptor_via_fun"; +static const char kInterceptorViaLibrary[] = "interceptor_via_lib"; +static const char *kSuppressionTypes[] = { + kInterceptorName, kInterceptorViaFunction, kInterceptorViaLibrary}; void InitializeSuppressions() { - CHECK(!suppressions_inited); - SuppressionContext::InitIfNecessary(); - suppressions_inited = true; + CHECK_EQ(nullptr, suppression_ctx); + suppression_ctx = new (suppression_placeholder) // NOLINT + SuppressionContext(kSuppressionTypes, ARRAY_SIZE(kSuppressionTypes)); + suppression_ctx->ParseFromFile(flags()->suppressions); } bool IsInterceptorSuppressed(const char *interceptor_name) { - CHECK(suppressions_inited); - SuppressionContext *ctx = SuppressionContext::Get(); + CHECK(suppression_ctx); Suppression *s; // Match "interceptor_name" suppressions. - return ctx->Match(interceptor_name, SuppressionInterceptorName, &s); + return suppression_ctx->Match(interceptor_name, kInterceptorName, &s); } bool HaveStackTraceBasedSuppressions() { - CHECK(suppressions_inited); - SuppressionContext *ctx = SuppressionContext::Get(); - return ctx->HasSuppressionType(SuppressionInterceptorViaFunction) || - ctx->HasSuppressionType(SuppressionInterceptorViaLibrary); + CHECK(suppression_ctx); + return suppression_ctx->HasSuppressionType(kInterceptorViaFunction) || + suppression_ctx->HasSuppressionType(kInterceptorViaLibrary); } bool IsStackTraceSuppressed(const StackTrace *stack) { - CHECK(suppressions_inited); if (!HaveStackTraceBasedSuppressions()) return false; - SuppressionContext *ctx = SuppressionContext::Get(); + CHECK(suppression_ctx); Symbolizer *symbolizer = Symbolizer::GetOrInit(); Suppression *s; for (uptr i = 0; i < stack->size && stack->trace[i]; i++) { uptr addr = stack->trace[i]; - if (ctx->HasSuppressionType(SuppressionInterceptorViaLibrary)) { + if (suppression_ctx->HasSuppressionType(kInterceptorViaLibrary)) { const char *module_name; uptr module_offset; // Match "interceptor_via_lib" suppressions. if (symbolizer->GetModuleNameAndOffsetForPC(addr, &module_name, &module_offset) && - ctx->Match(module_name, SuppressionInterceptorViaLibrary, &s)) { + suppression_ctx->Match(module_name, kInterceptorViaLibrary, &s)) { return true; } } - if (ctx->HasSuppressionType(SuppressionInterceptorViaFunction)) { + if (suppression_ctx->HasSuppressionType(kInterceptorViaFunction)) { SymbolizedStack *frames = symbolizer->SymbolizePC(addr); for (SymbolizedStack *cur = frames; cur; cur = cur->next) { const char *function_name = cur->info.function; @@ -73,7 +78,8 @@ bool IsStackTraceSuppressed(const StackTrace *stack) { continue; } // Match "interceptor_via_fun" suppressions. - if (ctx->Match(function_name, SuppressionInterceptorViaFunction, &s)) { + if (suppression_ctx->Match(function_name, kInterceptorViaFunction, + &s)) { frames->ClearAll(); return true; } diff --git a/lib/asan/asan_win.cc b/lib/asan/asan_win.cc index 4f02b022fe79..693f0bcdee8e 100644 --- a/lib/asan/asan_win.cc +++ b/lib/asan/asan_win.cc @@ -60,6 +60,10 @@ void PlatformTSDDtor(void *tsd) { AsanThread::TSDDtor(tsd); } // ---------------------- Various stuff ---------------- {{{1 +void DisableReexec() { + // No need to re-exec on Windows. +} + void MaybeReexec() { // No need to re-exec on Windows. } diff --git a/lib/asan/asan_win_dll_thunk.cc b/lib/asan/asan_win_dll_thunk.cc index b38a2d16087f..5d39e33096a8 100644 --- a/lib/asan/asan_win_dll_thunk.cc +++ b/lib/asan/asan_win_dll_thunk.cc @@ -294,7 +294,43 @@ INTERFACE_FUNCTION(__asan_stack_free_8) INTERFACE_FUNCTION(__asan_stack_free_9) 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_cov) +INTERFACE_FUNCTION(__sanitizer_cov_dump) +INTERFACE_FUNCTION(__sanitizer_cov_indir_call16) +INTERFACE_FUNCTION(__sanitizer_cov_init) INTERFACE_FUNCTION(__sanitizer_cov_module_init) +INTERFACE_FUNCTION(__sanitizer_cov_trace_basic_block) +INTERFACE_FUNCTION(__sanitizer_cov_trace_func_enter) +INTERFACE_FUNCTION(__sanitizer_cov_with_check) +INTERFACE_FUNCTION(__sanitizer_free_hook) +INTERFACE_FUNCTION(__sanitizer_get_allocated_size) +INTERFACE_FUNCTION(__sanitizer_get_coverage_guards) +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_coverage) +INTERFACE_FUNCTION(__sanitizer_get_unmapped_bytes) +INTERFACE_FUNCTION(__sanitizer_malloc_hook) +INTERFACE_FUNCTION(__sanitizer_maybe_open_cov_file) +INTERFACE_FUNCTION(__sanitizer_print_stack_trace) +INTERFACE_FUNCTION(__sanitizer_ptr_cmp) +INTERFACE_FUNCTION(__sanitizer_ptr_sub) +INTERFACE_FUNCTION(__sanitizer_report_error_summary) +INTERFACE_FUNCTION(__sanitizer_reset_coverage) +INTERFACE_FUNCTION(__sanitizer_sandbox_on_notify) +INTERFACE_FUNCTION(__sanitizer_set_death_callback) +INTERFACE_FUNCTION(__sanitizer_set_report_path) +INTERFACE_FUNCTION(__sanitizer_unaligned_load16) +INTERFACE_FUNCTION(__sanitizer_unaligned_load32) +INTERFACE_FUNCTION(__sanitizer_unaligned_load64) +INTERFACE_FUNCTION(__sanitizer_unaligned_store16) +INTERFACE_FUNCTION(__sanitizer_unaligned_store32) +INTERFACE_FUNCTION(__sanitizer_unaligned_store64) +INTERFACE_FUNCTION(__sanitizer_verify_contiguous_container) // TODO(timurrrr): Add more interface functions on the as-needed basis. diff --git a/lib/asan/asan_win_dynamic_runtime_thunk.cc b/lib/asan/asan_win_dynamic_runtime_thunk.cc index 3a4de7dbf1fb..19456141c1ec 100644 --- a/lib/asan/asan_win_dynamic_runtime_thunk.cc +++ b/lib/asan/asan_win_dynamic_runtime_thunk.cc @@ -23,10 +23,11 @@ // Using #ifdef rather than relying on Makefiles etc. // simplifies the build procedure. #ifdef ASAN_DYNAMIC_RUNTIME_THUNK -extern "C" { -__declspec(dllimport) int __asan_set_seh_filter(); -__declspec(dllimport) int __asan_should_detect_stack_use_after_return(); +#include <windows.h> +#include <psapi.h> +extern "C" { +//////////////////////////////////////////////////////////////////////////////// // Define a copy of __asan_option_detect_stack_use_after_return that should be // used when linking an MD runtime with a set of object files on Windows. // @@ -37,16 +38,82 @@ __declspec(dllimport) int __asan_should_detect_stack_use_after_return(); // with a MT or MD runtime and we don't want to use ugly __imp_ names on Windows // just to work around this issue, let's clone the a variable that is // constant after initialization anyways. +__declspec(dllimport) int __asan_should_detect_stack_use_after_return(); int __asan_option_detect_stack_use_after_return = __asan_should_detect_stack_use_after_return(); +} + +//////////////////////////////////////////////////////////////////////////////// +// For some reason, the MD CRT doesn't call the C/C++ terminators as MT does. +// To work around this, for each DLL we schedule a call to +// UnregisterGlobalsInRange atexit() specifying the address range of the DLL +// image to unregister globals in that range. We don't do the same +// for the main module (.exe) as the asan_globals.cc allocator is destroyed +// by the time UnregisterGlobalsInRange is executed. +// See PR22545 for the details. +namespace __asan { +__declspec(dllimport) +void UnregisterGlobalsInRange(void *beg, void *end); +} + +namespace { +void *this_module_base, *this_module_end; + +void UnregisterGlobals() { + __asan::UnregisterGlobalsInRange(this_module_base, this_module_end); +} + +int ScheduleUnregisterGlobals() { + HMODULE this_module = 0; + // Increments the reference counter of the DLL module, so need to call + // FreeLibrary later. + if (!GetModuleHandleEx(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS, + (LPCTSTR)&UnregisterGlobals, &this_module)) + return 1; + + // Skip the main module. + if (this_module == GetModuleHandle(0)) + return 0; + + MODULEINFO mi; + bool success = + GetModuleInformation(GetCurrentProcess(), this_module, &mi, sizeof(mi)); + if (!FreeLibrary(this_module)) + return 2; + if (!success) + return 3; -// Set the ASan-specific SEH handler at the end of CRT initialization of each -// module (see asan_win.cc for the details). + this_module_base = mi.lpBaseOfDll; + this_module_end = (char*)mi.lpBaseOfDll + mi.SizeOfImage; + + return atexit(UnregisterGlobals); +} +} // namespace + +/////////////////////////////////////////////////////////////////////////////// +// ASan SEH handling. +extern "C" __declspec(dllimport) int __asan_set_seh_filter(); +static int SetSEHFilter() { return __asan_set_seh_filter(); } + +/////////////////////////////////////////////////////////////////////////////// +// We schedule some work at start-up by placing callbacks to our code to the +// list of CRT C initializers. +// +// First, declare sections we'll be using: +#pragma section(".CRT$XID", long, read) // NOLINT +#pragma section(".CRT$XIZ", long, read) // NOLINT + +// We need to call 'atexit(UnregisterGlobals);' after atexit() is initialized +// (.CRT$XIC) but before the C++ constructors (.CRT$XCA). +__declspec(allocate(".CRT$XID")) +static int (*__asan_schedule_unregister_globals)() = ScheduleUnregisterGlobals; + +// We need to set the ASan-specific SEH handler at the end of CRT initialization +// of each module (see also asan_win.cc). // // Unfortunately, putting a pointer to __asan_set_seh_filter into // __asan_intercept_seh gets optimized out, so we have to use an extra function. -static int SetSEHFilter() { return __asan_set_seh_filter(); } -#pragma section(".CRT$XIZ", long, read) // NOLINT -__declspec(allocate(".CRT$XIZ")) int (*__asan_seh_interceptor)() = SetSEHFilter; -} +extern "C" __declspec(allocate(".CRT$XIZ")) +int (*__asan_seh_interceptor)() = SetSEHFilter; + #endif // ASAN_DYNAMIC_RUNTIME_THUNK diff --git a/lib/asan/scripts/asan_device_setup b/lib/asan/scripts/asan_device_setup index c143b9fab13c..104e07b722ca 100755 --- a/lib/asan/scripts/asan_device_setup +++ b/lib/asan/scripts/asan_device_setup @@ -18,6 +18,7 @@ revert=no extra_options= device= lib= +use_su=0 function usage { echo "usage: $0 [--revert] [--device device-id] [--lib path] [--extra-options options]" @@ -26,13 +27,70 @@ function usage { 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 _ABI=$(adb_shell getprop ro.product.cpu.abi) local _ARCH= if [[ $_ABI == x86* ]]; then _ARCH=i686 @@ -74,6 +132,9 @@ while [[ $# > 0 ]]; do fi device="$1" ;; + --use-su) + use_su=1 + ;; *) usage ;; @@ -86,12 +147,25 @@ 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 +adb_wait_for_device +adb_root +adb_wait_for_device +adb_remount +adb_wait_for_device get_device_arch ARCH echo "Target architecture: $ARCH" @@ -100,22 +174,24 @@ ASAN_RT="libclang_rt.asan-$ARCH-android.so" if [[ x$revert == xyes ]]; then echo '>> Uninstalling ASan' - if ! $ADB shell readlink /system/bin/app_process | grep 'app_process' >&/dev/null; then + 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 - $ADB shell rm /system/lib/$ASAN_RT + 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/lib/$ASAN_RT - $ADB shell rm /system/bin/app_process - $ADB shell ln -s /system/bin/app_process32 /system/bin/app_process + 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 + 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 @@ -146,28 +222,28 @@ TMPDIROLD="$TMPDIRBASE/old" TMPDIR="$TMPDIRBASE/new" mkdir "$TMPDIROLD" -RELEASE=$($ADB shell getprop ro.build.version.release) +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 readlink /system/bin/app_process | grep 'app_process' >&/dev/null; then +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 + 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 + 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 + 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 +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 @@ -213,52 +289,52 @@ 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/app_process.wrap - $ADB push "$TMPDIR/asanwrapper" /system/bin/asanwrapper + 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 rm /system/bin/app_process + adb_shell ln -s /system/bin/app_process.wrap /system/bin/app_process - $ADB shell chown root.shell \ + adb_shell chown root.shell \ /system/lib/"$ASAN_RT" \ /system/bin/app_process.wrap \ /system/bin/asanwrapper - $ADB shell chmod 644 \ + adb_shell chmod 644 \ /system/lib/"$ASAN_RT" - $ADB shell chmod 755 \ + 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 + 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 + adb_shell setenforce 0 fi - $ADB shell cp /system/bin/sh /system/bin/sh-from-zygote + 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 \ + 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 + adb_shell setenforce 1 fi echo '>> Restarting shell (asynchronous)' - $ADB shell stop - $ADB shell start + adb_shell stop + adb_shell start echo '>> Please wait until the device restarts' else diff --git a/lib/asan/tests/asan_noinst_test.cc b/lib/asan/tests/asan_noinst_test.cc index c469d62a6766..6a428fbbc2b9 100644 --- a/lib/asan/tests/asan_noinst_test.cc +++ b/lib/asan/tests/asan_noinst_test.cc @@ -33,7 +33,10 @@ // Make sure __asan_init is called before any test case is run. struct AsanInitCaller { - AsanInitCaller() { __asan_init(); } + AsanInitCaller() { + __asan::DisableReexec(); + __asan_init(); + } }; static AsanInitCaller asan_init_caller; diff --git a/lib/builtins/clear_cache.c b/lib/builtins/clear_cache.c index 61b1e9bbb5c0..8dc0fb1c5907 100644 --- a/lib/builtins/clear_cache.c +++ b/lib/builtins/clear_cache.c @@ -22,10 +22,10 @@ #include <machine/sysarch.h> #endif -#if defined(__ANDROID__) && defined(__mips__) +#if defined(__mips__) #include <sys/cachectl.h> #include <sys/syscall.h> - #ifdef __LP64__ + #if defined(__ANDROID__) && defined(__LP64__) /* * clear_mips_cache - Invalidates instruction cache for Mips. */ @@ -109,10 +109,10 @@ void __clear_cache(void *start, void *end) { #else compilerrt_abort(); #endif -#elif defined(__ANDROID__) && defined(__mips__) +#elif defined(__mips__) const uintptr_t start_int = (uintptr_t) start; const uintptr_t end_int = (uintptr_t) end; - #ifdef __LP64__ + #if defined(__ANDROID__) && defined(__LP64__) // Call synci implementation for short address range. const uintptr_t address_range_limit = 256; if ((end_int - start_int) <= address_range_limit) { diff --git a/lib/dfsan/Makefile.mk b/lib/dfsan/Makefile.mk deleted file mode 100644 index 4aeaac42dea2..000000000000 --- a/lib/dfsan/Makefile.mk +++ /dev/null @@ -1,23 +0,0 @@ -#===- lib/dfsan/Makefile.mk --------------------------------*- Makefile -*--===# -# -# The LLVM Compiler Infrastructure -# -# This file is distributed under the University of Illinois Open Source -# License. See LICENSE.TXT for details. -# -#===------------------------------------------------------------------------===# - -ModuleName := dfsan -SubDirs := - -Sources := $(foreach file,$(wildcard $(Dir)/*.cc),$(notdir $(file))) -ObjNames := $(Sources:%.cc=%.o) - -Implementation := Generic - -# FIXME: use automatic dependencies? -Dependencies := $(wildcard $(Dir)/*.h) -Dependencies += $(wildcard $(Dir)/../sanitizer_common/*.h) - -# Define a convenience variable for all the dfsan functions. -DfsanFunctions := $(Sources:%.cc=%) diff --git a/lib/dfsan/dfsan.cc b/lib/dfsan/dfsan.cc index dd0ea61142b8..de5b2ce107b4 100644 --- a/lib/dfsan/dfsan.cc +++ b/lib/dfsan/dfsan.cc @@ -317,18 +317,18 @@ void Flags::SetDefaults() { #undef DFSAN_FLAG } -void RegisterDfsanFlags(FlagParser *parser, Flags *f) { +static void RegisterDfsanFlags(FlagParser *parser, Flags *f) { #define DFSAN_FLAG(Type, Name, DefaultValue, Description) \ RegisterFlag(parser, #Name, Description, &f->Name); #include "dfsan_flags.inc" #undef DFSAN_FLAG } -static void InitializeFlags(Flags &f, const char *env) { +static void InitializeFlags() { FlagParser parser; - RegisterDfsanFlags(&parser, &f); - f.SetDefaults(); - parser.ParseString(env); + RegisterDfsanFlags(&parser, &flags()); + flags().SetDefaults(); + parser.ParseString(GetEnv("DFSAN_OPTIONS")); } static void dfsan_fini() { @@ -363,8 +363,7 @@ static void dfsan_init(int argc, char **argv, char **envp) { if (!(init_addr >= kUnusedAddr && init_addr < kAppAddr)) Mprotect(kUnusedAddr, kAppAddr - kUnusedAddr); - InitializeFlags(flags(), GetEnv("DFSAN_OPTIONS")); - + InitializeFlags(); InitializeInterceptors(); // Register the fini callback to run when the program terminates successfully diff --git a/lib/lsan/Makefile.mk b/lib/lsan/Makefile.mk index 2a6b41c98e2c..5e70634e792e 100644 --- a/lib/lsan/Makefile.mk +++ b/lib/lsan/Makefile.mk @@ -20,9 +20,6 @@ Dependencies := $(wildcard $(Dir)/*.h) Dependencies += $(wildcard $(Dir)/../interception/*.h) Dependencies += $(wildcard $(Dir)/../sanitizer_common/*.h) -# Define a convenience variable for all the lsan functions. -LsanFunctions := $(Sources:%.cc=%) - # lsan functions used in another sanitizers. LsanCommonSources := $(foreach file,$(wildcard $(Dir)/lsan_common*.cc),$(notdir $(file))) LsanCommonFunctions := $(LsanCommonSources:%.cc=%) diff --git a/lib/lsan/lsan.cc b/lib/lsan/lsan.cc index 1509b2a7d342..6018f7bf6f49 100644 --- a/lib/lsan/lsan.cc +++ b/lib/lsan/lsan.cc @@ -15,6 +15,7 @@ #include "lsan.h" #include "sanitizer_common/sanitizer_flags.h" +#include "sanitizer_common/sanitizer_flag_parser.h" #include "sanitizer_common/sanitizer_stacktrace.h" #include "lsan_allocator.h" #include "lsan_common.h" @@ -34,13 +35,42 @@ bool WordIsPoisoned(uptr addr) { using namespace __lsan; // NOLINT +static void InitializeFlags() { + // Set all the default values. + SetCommonFlagsDefaults(); + { + CommonFlags cf; + cf.CopyFrom(*common_flags()); + cf.external_symbolizer_path = GetEnv("LSAN_SYMBOLIZER_PATH"); + cf.malloc_context_size = 30; + cf.detect_leaks = true; + OverrideCommonFlags(cf); + } + + Flags *f = flags(); + f->SetDefaults(); + + FlagParser parser; + RegisterLsanFlags(&parser, f); + RegisterCommonFlags(&parser); + + parser.ParseString(GetEnv("LSAN_OPTIONS")); + + SetVerbosity(common_flags()->verbosity); + + if (Verbosity()) ReportUnrecognizedFlags(); + + if (common_flags()->help) parser.PrintFlagDescriptions(); +} + extern "C" void __lsan_init() { CHECK(!lsan_init_is_running); if (lsan_inited) return; lsan_init_is_running = true; SanitizerToolName = "LeakSanitizer"; - InitCommonLsan(true); + InitializeFlags(); + InitCommonLsan(); InitializeAllocator(); InitTlsSize(); InitializeInterceptors(); diff --git a/lib/lsan/lsan_allocator.cc b/lib/lsan/lsan_allocator.cc index 96a2b0428a61..67125dbb3e45 100644 --- a/lib/lsan/lsan_allocator.cc +++ b/lib/lsan/lsan_allocator.cc @@ -25,10 +25,6 @@ extern "C" void *memset(void *ptr, int value, uptr num); namespace __lsan { -static const uptr kMaxAllowedMallocSize = 8UL << 30; -static const uptr kAllocatorSpace = 0x600000000000ULL; -static const uptr kAllocatorSize = 0x40000000000ULL; // 4T. - struct ChunkMetadata { bool allocated : 8; // Must be first. ChunkTag tag : 2; @@ -36,8 +32,22 @@ struct ChunkMetadata { u32 stack_trace_id; }; +#if defined(__mips64) +static const uptr kMaxAllowedMallocSize = 4UL << 30; +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(ChunkMetadata), SizeClassMap, kRegionSizeLog, ByteMap> + PrimaryAllocator; +#else +static const uptr kMaxAllowedMallocSize = 8UL << 30; +static const uptr kAllocatorSpace = 0x600000000000ULL; +static const uptr kAllocatorSize = 0x40000000000ULL; // 4T. typedef SizeClassAllocator64<kAllocatorSpace, kAllocatorSize, sizeof(ChunkMetadata), DefaultSizeClassMap> PrimaryAllocator; +#endif typedef SizeClassAllocatorLocalCache<PrimaryAllocator> AllocatorCache; typedef LargeMmapAllocator<> SecondaryAllocator; typedef CombinedAllocator<PrimaryAllocator, AllocatorCache, diff --git a/lib/lsan/lsan_common.cc b/lib/lsan/lsan_common.cc index 7b3cd1639761..b9e2a1104326 100644 --- a/lib/lsan/lsan_common.cc +++ b/lib/lsan/lsan_common.cc @@ -43,46 +43,13 @@ void Flags::SetDefaults() { #undef LSAN_FLAG } -static void RegisterLsanFlags(FlagParser *parser, Flags *f) { +void RegisterLsanFlags(FlagParser *parser, Flags *f) { #define LSAN_FLAG(Type, Name, DefaultValue, Description) \ RegisterFlag(parser, #Name, Description, &f->Name); #include "lsan_flags.inc" #undef LSAN_FLAG } -static void InitializeFlags(bool standalone) { - Flags *f = flags(); - FlagParser parser; - RegisterLsanFlags(&parser, f); - RegisterCommonFlags(&parser); - - f->SetDefaults(); - - // Set defaults for common flags (only in standalone mode) and parse - // them from LSAN_OPTIONS. - if (standalone) { - SetCommonFlagsDefaults(); - CommonFlags cf; - cf.CopyFrom(*common_flags()); - cf.external_symbolizer_path = GetEnv("LSAN_SYMBOLIZER_PATH"); - cf.malloc_context_size = 30; - cf.detect_leaks = true; - OverrideCommonFlags(cf); - } - - bool help_before = common_flags()->help; - - const char *options = GetEnv("LSAN_OPTIONS"); - parser.ParseString(options); - - SetVerbosity(common_flags()->verbosity); - - if (Verbosity()) ReportUnrecognizedFlags(); - - if (!help_before && common_flags()->help) - parser.PrintFlagDescriptions(); -} - #define LOG_POINTERS(...) \ do { \ if (flags()->log_pointers) Report(__VA_ARGS__); \ @@ -93,14 +60,23 @@ static void InitializeFlags(bool standalone) { if (flags()->log_threads) Report(__VA_ARGS__); \ } while (0); -static bool suppressions_inited = false; +ALIGNED(64) static char suppression_placeholder[sizeof(SuppressionContext)]; +static SuppressionContext *suppression_ctx = nullptr; +static const char kSuppressionLeak[] = "leak"; +static const char *kSuppressionTypes[] = { kSuppressionLeak }; void InitializeSuppressions() { - CHECK(!suppressions_inited); - SuppressionContext::InitIfNecessary(); + CHECK_EQ(nullptr, suppression_ctx); + suppression_ctx = new (suppression_placeholder) // NOLINT + SuppressionContext(kSuppressionTypes, ARRAY_SIZE(kSuppressionTypes)); + suppression_ctx->ParseFromFile(flags()->suppressions); if (&__lsan_default_suppressions) - SuppressionContext::Get()->Parse(__lsan_default_suppressions()); - suppressions_inited = true; + suppression_ctx->Parse(__lsan_default_suppressions()); +} + +static SuppressionContext *GetSuppressionContext() { + CHECK(suppression_ctx); + return suppression_ctx; } struct RootRegion { @@ -116,8 +92,7 @@ void InitializeRootRegions() { root_regions = new(placeholder) InternalMmapVector<RootRegion>(1); } -void InitCommonLsan(bool standalone) { - InitializeFlags(standalone); +void InitCommonLsan() { InitializeRootRegions(); if (common_flags()->detect_leaks) { // Initialization which can fail or print warnings should only be done if @@ -140,9 +115,11 @@ static inline bool CanBeAHeapPointer(uptr p) { // bound on heap addresses. const uptr kMinAddress = 4 * 4096; if (p < kMinAddress) return false; -#ifdef __x86_64__ +#if defined(__x86_64__) // Accept only canonical form user-space addresses. return ((p >> 47) == 0); +#elif defined(__mips64) + return ((p >> 40) == 0); #else return true; #endif @@ -382,7 +359,7 @@ static void CollectLeaksCb(uptr chunk, void *arg) { static void PrintMatchedSuppressions() { InternalMmapVector<Suppression *> matched(1); - SuppressionContext::Get()->GetMatched(&matched); + GetSuppressionContext()->GetMatched(&matched); if (!matched.size()) return; const char *line = "-----------------------------------------------------"; @@ -461,17 +438,17 @@ static Suppression *GetSuppressionForAddr(uptr addr) { // Suppress by module name. const char *module_name; uptr module_offset; + SuppressionContext *suppressions = GetSuppressionContext(); if (Symbolizer::GetOrInit()->GetModuleNameAndOffsetForPC(addr, &module_name, &module_offset) && - SuppressionContext::Get()->Match(module_name, SuppressionLeak, &s)) + suppressions->Match(module_name, kSuppressionLeak, &s)) return s; // Suppress by file or function name. SymbolizedStack *frames = Symbolizer::GetOrInit()->SymbolizePC(addr); for (SymbolizedStack *cur = frames; cur; cur = cur->next) { - if (SuppressionContext::Get()->Match(cur->info.function, SuppressionLeak, - &s) || - SuppressionContext::Get()->Match(cur->info.file, SuppressionLeak, &s)) { + if (suppressions->Match(cur->info.function, kSuppressionLeak, &s) || + suppressions->Match(cur->info.file, kSuppressionLeak, &s)) { break; } } diff --git a/lib/lsan/lsan_common.h b/lib/lsan/lsan_common.h index 64cbef38c7a6..2c3a12ab6bd8 100644 --- a/lib/lsan/lsan_common.h +++ b/lib/lsan/lsan_common.h @@ -21,12 +21,17 @@ #include "sanitizer_common/sanitizer_platform.h" #include "sanitizer_common/sanitizer_symbolizer.h" -#if SANITIZER_LINUX && defined(__x86_64__) && (SANITIZER_WORDSIZE == 64) +#if SANITIZER_LINUX && (defined(__x86_64__) || defined(__mips64)) \ + && (SANITIZER_WORDSIZE == 64) #define CAN_SANITIZE_LEAKS 1 #else #define CAN_SANITIZE_LEAKS 0 #endif +namespace __sanitizer { +class FlagParser; +} + namespace __lsan { // Chunk tags. @@ -50,6 +55,7 @@ struct Flags { extern Flags lsan_flags; inline Flags *flags() { return &lsan_flags; } +void RegisterLsanFlags(FlagParser *parser, Flags *f); struct Leak { u32 id; @@ -105,7 +111,7 @@ enum IgnoreObjectResult { }; // Functions called from the parent tool. -void InitCommonLsan(bool standalone); +void InitCommonLsan(); void DoLeakCheck(); bool DisabledInThisThread(); diff --git a/lib/lsan/lsan_flags.inc b/lib/lsan/lsan_flags.inc index 7f00acbf9119..b19b3452b2fc 100644 --- a/lib/lsan/lsan_flags.inc +++ b/lib/lsan/lsan_flags.inc @@ -42,3 +42,4 @@ LSAN_FLAG(bool, use_poisoned, false, "Consider pointers found in poisoned memory to be valid.") LSAN_FLAG(bool, log_pointers, false, "Debug logging") LSAN_FLAG(bool, log_threads, false, "Debug logging") +LSAN_FLAG(const char *, suppressions, "", "Suppressions file name.") diff --git a/lib/msan/Makefile.mk b/lib/msan/Makefile.mk deleted file mode 100644 index 99e3b036ea11..000000000000 --- a/lib/msan/Makefile.mk +++ /dev/null @@ -1,24 +0,0 @@ -#===- lib/msan/Makefile.mk ---------------------------------*- Makefile -*--===# -# -# The LLVM Compiler Infrastructure -# -# This file is distributed under the University of Illinois Open Source -# License. See LICENSE.TXT for details. -# -#===------------------------------------------------------------------------===# - -ModuleName := msan -SubDirs := - -Sources := $(foreach file,$(wildcard $(Dir)/*.cc),$(notdir $(file))) -ObjNames := $(Sources:%.cc=%.o) - -Implementation := Generic - -# FIXME: use automatic dependencies? -Dependencies := $(wildcard $(Dir)/*.h) -Dependencies += $(wildcard $(Dir)/../interception/*.h) -Dependencies += $(wildcard $(Dir)/../sanitizer_common/*.h) - -# Define a convenience variable for all the msan functions. -MsanFunctions := $(Sources:%.cc=%) diff --git a/lib/msan/msan.cc b/lib/msan/msan.cc index 4adcc9eb8e6a..ed6efbdd682f 100644 --- a/lib/msan/msan.cc +++ b/lib/msan/msan.cc @@ -111,7 +111,7 @@ class FlagHandlerKeepGoing : public FlagHandlerBase { public: explicit FlagHandlerKeepGoing(bool *halt_on_error) : halt_on_error_(halt_on_error) {} - bool Parse(const char *value) { + bool Parse(const char *value) final { bool tmp; FlagHandler<bool> h(&tmp); if (!h.Parse(value)) return false; @@ -120,7 +120,7 @@ class FlagHandlerKeepGoing : public FlagHandlerBase { } }; -void RegisterMsanFlags(FlagParser *parser, Flags *f) { +static void RegisterMsanFlags(FlagParser *parser, Flags *f) { #define MSAN_FLAG(Type, Name, DefaultValue, Description) \ RegisterFlag(parser, #Name, Description, &f->Name); #include "msan_flags.inc" @@ -132,7 +132,8 @@ void RegisterMsanFlags(FlagParser *parser, Flags *f) { "deprecated, use halt_on_error"); } -static void InitializeFlags(Flags *f, const char *options) { +static void InitializeFlags() { + Flags *f = flags(); FlagParser parser; RegisterMsanFlags(&parser, f); RegisterCommonFlags(&parser); @@ -156,7 +157,9 @@ static void InitializeFlags(Flags *f, const char *options) { if (__msan_default_options) parser.ParseString(__msan_default_options()); - parser.ParseString(options); + const char *msan_options = GetEnv("MSAN_OPTIONS"); + parser.ParseString(msan_options); + VPrintf(1, "MSAN_OPTIONS: %s\n", msan_options ? msan_options : "<empty>"); SetVerbosity(common_flags()->verbosity); @@ -351,8 +354,7 @@ void __msan_init() { SetDieCallback(MsanDie); InitTlsSize(); - const char *msan_options = GetEnv("MSAN_OPTIONS"); - InitializeFlags(&msan_flags, msan_options); + InitializeFlags(); __sanitizer_set_report_path(common_flags()->log_path); InitializeInterceptors(); @@ -369,8 +371,6 @@ void __msan_init() { ReExec(); } - VPrintf(1, "MSAN_OPTIONS: %s\n", msan_options ? msan_options : "<empty>"); - __msan_clear_on_return(); if (__msan_get_track_origins()) VPrintf(1, "msan_track_origins\n"); diff --git a/lib/msan/tests/msan_test.cc b/lib/msan/tests/msan_test.cc index 1c5fc5f7f1e3..317f70cbc32e 100644 --- a/lib/msan/tests/msan_test.cc +++ b/lib/msan/tests/msan_test.cc @@ -72,12 +72,21 @@ int shmdt(const void *); # 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__) @@ -97,14 +106,17 @@ int shmdt(const void *); # 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 -static const size_t kPageSize = 4096; +const size_t kPageSize = 4096; +const size_t kMaxPathLength = 4096; typedef unsigned char U1; typedef unsigned short U2; // NOLINT @@ -158,11 +170,11 @@ void ExpectPoisonedWithOrigin(const T& t, unsigned origin) { EXPECT_EQ(origin, __msan_get_origin((void*)&t)); } -#define EXPECT_NOT_POISONED(x) ExpectNotPoisoned(x) +#define EXPECT_NOT_POISONED(x) EXPECT_EQ(true, TestForNotPoisoned((x))) template<typename T> -void ExpectNotPoisoned(const T& t) { - EXPECT_EQ(-1, __msan_test_shadow((void*)&t, sizeof(t))); +bool TestForNotPoisoned(const T& t) { + return __msan_test_shadow((void*)&t, sizeof(t)) == -1; } static U8 poisoned_array[100]; @@ -2166,6 +2178,8 @@ TEST(MemorySanitizer, mmap) { } } +// 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) { @@ -2181,7 +2195,10 @@ TEST(MemorySanitizer, fcvt) { 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); @@ -2195,7 +2212,7 @@ TEST(MemorySanitizer, fcvt_long) { EXPECT_NOT_POISONED(str[0]); ASSERT_NE(0U, strlen(str)); } - +#endif TEST(MemorySanitizer, memchr) { char x[10]; @@ -2797,9 +2814,20 @@ TEST(MemorySanitizer, getrusage) { EXPECT_NOT_POISONED(usage.ru_nivcsw); } -#ifdef __GLIBC__ -extern char *program_invocation_name; -#else // __GLIBC__ +#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 @@ -2834,21 +2862,29 @@ static int dl_phdr_callback(struct dl_phdr_info *info, size_t size, void *data) // 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 int PathToLoadable(char *buf, size_t sz) { - const char *basename = "libmsan_loadable.x86_64.so"; - char *argv0 = program_invocation_name; - char *last_slash = strrchr(argv0, '/'); - assert(last_slash); - int res = - snprintf(buf, sz, "%.*s/%s", int(last_slash - argv0), argv0, basename); - assert(res >= 0); - return (size_t)res < sz ? 0 : res; +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[4096]; - int res = PathToLoadable(path, sizeof(path)); - ASSERT_EQ(0, res); + char path[kMaxPathLength]; + GetPathToLoadable(path, sizeof(path)); // Having at least one dlopen'ed library in the process makes this more // entertaining. @@ -2858,15 +2894,13 @@ TEST(MemorySanitizer, dl_iterate_phdr) { int count = 0; int result = dl_iterate_phdr(dl_phdr_callback, &count); ASSERT_GT(count, 0); - + dlclose(lib); } - TEST(MemorySanitizer, dlopen) { - char path[4096]; - int res = PathToLoadable(path, sizeof(path)); - ASSERT_EQ(0, res); + 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 @@ -2891,7 +2925,7 @@ TEST(MemorySanitizer, dlopen) { // Regression test for a crash in dlopen() interceptor. TEST(MemorySanitizer, dlopenFailed) { - const char *path = "/libmsan_loadable_does_not_exist.x86_64.so"; + const char *path = "/libmsan_loadable_does_not_exist.so"; void *lib = dlopen(path, RTLD_LAZY); ASSERT_TRUE(lib == NULL); } @@ -3271,8 +3305,10 @@ TEST(MemorySanitizer, getgrnam_r) { struct group grp; struct group *grpres; char buf[10000]; - int res = getgrnam_r("root", &grp, buf, sizeof(buf), &grpres); + 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]); @@ -3360,6 +3396,8 @@ TEST(MemorySanitizer, getgrent_r) { 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; @@ -3375,6 +3413,7 @@ TEST(MemorySanitizer, fgetgrent_r) { EXPECT_NOT_POISONED(grpres); fclose(fp); } +#endif TEST(MemorySanitizer, getgroups) { int n = getgroups(0, 0); @@ -3502,7 +3541,7 @@ TEST(MemorySanitizer, VolatileBitfield) { } TEST(MemorySanitizer, UnalignedLoad) { - char x[32]; + 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); @@ -3536,7 +3575,7 @@ TEST(MemorySanitizer, UnalignedLoad) { } TEST(MemorySanitizer, UnalignedStore16) { - char x[5]; + char x[5] __attribute__((aligned(4))); U2 y2 = 0; U4 origin = __LINE__; __msan_poison(&y2, 1); @@ -3547,11 +3586,10 @@ TEST(MemorySanitizer, UnalignedStore16) { EXPECT_POISONED_O(x[1], origin); EXPECT_NOT_POISONED(x[2]); EXPECT_POISONED_O(x[3], origin); - EXPECT_POISONED_O(x[4], origin); } TEST(MemorySanitizer, UnalignedStore32) { - char x[8]; + char x[8] __attribute__((aligned(4))); U4 y4 = 0; U4 origin = __LINE__; __msan_poison(&y4, 2); @@ -3569,7 +3607,7 @@ TEST(MemorySanitizer, UnalignedStore32) { } TEST(MemorySanitizer, UnalignedStore64) { - char x[16]; + char x[16] __attribute__((aligned(8))); U8 y8 = 0; U4 origin = __LINE__; __msan_poison(&y8, 3); @@ -3592,7 +3630,7 @@ TEST(MemorySanitizer, UnalignedStore64) { } TEST(MemorySanitizer, UnalignedStore16_precise) { - char x[8]; + char x[8] __attribute__((aligned(4))); U2 y = 0; U4 originx1 = __LINE__; U4 originx2 = __LINE__; @@ -3615,7 +3653,7 @@ TEST(MemorySanitizer, UnalignedStore16_precise) { } TEST(MemorySanitizer, UnalignedStore16_precise2) { - char x[8]; + char x[8] __attribute__((aligned(4))); U2 y = 0; U4 originx1 = __LINE__; U4 originx2 = __LINE__; @@ -3638,7 +3676,7 @@ TEST(MemorySanitizer, UnalignedStore16_precise2) { } TEST(MemorySanitizer, UnalignedStore64_precise) { - char x[12]; + char x[12] __attribute__((aligned(8))); U8 y = 0; U4 originx1 = __LINE__; U4 originx2 = __LINE__; @@ -3670,7 +3708,7 @@ TEST(MemorySanitizer, UnalignedStore64_precise) { } TEST(MemorySanitizer, UnalignedStore64_precise2) { - char x[12]; + char x[12] __attribute__((aligned(8))); U8 y = 0; U4 originx1 = __LINE__; U4 originx2 = __LINE__; diff --git a/lib/sanitizer_common/CMakeLists.txt b/lib/sanitizer_common/CMakeLists.txt index 86697e7f7c40..6eb6ca8fc900 100644 --- a/lib/sanitizer_common/CMakeLists.txt +++ b/lib/sanitizer_common/CMakeLists.txt @@ -67,6 +67,7 @@ set(SANITIZER_HEADERS sanitizer_flag_parser.h sanitizer_flags.h sanitizer_flags.inc + sanitizer_interface_internal.h sanitizer_internal_defs.h sanitizer_lfstack.h sanitizer_libc.h diff --git a/lib/sanitizer_common/sanitizer_common.cc b/lib/sanitizer_common/sanitizer_common.cc index 489081e0760b..4be3c7abf756 100644 --- a/lib/sanitizer_common/sanitizer_common.cc +++ b/lib/sanitizer_common/sanitizer_common.cc @@ -288,6 +288,48 @@ void DecreaseTotalMmap(uptr size) { atomic_fetch_sub(&g_total_mmaped, size, memory_order_relaxed); } +bool TemplateMatch(const char *templ, const char *str) { + if (str == 0 || str[0] == 0) + return false; + bool start = false; + if (templ && templ[0] == '^') { + start = true; + templ++; + } + bool asterisk = false; + while (templ && templ[0]) { + if (templ[0] == '*') { + templ++; + start = false; + asterisk = true; + continue; + } + if (templ[0] == '$') + return str[0] == 0 || asterisk; + if (str[0] == 0) + return false; + char *tpos = (char*)internal_strchr(templ, '*'); + char *tpos1 = (char*)internal_strchr(templ, '$'); + if (tpos == 0 || (tpos1 && tpos1 < tpos)) + tpos = tpos1; + if (tpos != 0) + tpos[0] = 0; + const char *str0 = str; + const char *spos = internal_strstr(str, templ); + str = spos + internal_strlen(templ); + templ = tpos; + if (tpos) + tpos[0] = tpos == tpos1 ? '$' : '*'; + if (spos == 0) + return false; + if (start && spos != str0) + return false; + start = false; + asterisk = false; + } + return true; +} + } // namespace __sanitizer using namespace __sanitizer; // NOLINT diff --git a/lib/sanitizer_common/sanitizer_common.h b/lib/sanitizer_common/sanitizer_common.h index 720cd73a43c5..ff13ef164045 100644 --- a/lib/sanitizer_common/sanitizer_common.h +++ b/lib/sanitizer_common/sanitizer_common.h @@ -7,8 +7,8 @@ // //===----------------------------------------------------------------------===// // -// This file is shared between AddressSanitizer and ThreadSanitizer -// run-time libraries. +// This file is shared between run-time libraries of sanitizers. +// // It declares common functions and classes that are used in both runtimes. // Implementation of some functions are provided in sanitizer_common, while // others must be defined by run-time library itself. @@ -17,6 +17,7 @@ #define SANITIZER_COMMON_H #include "sanitizer_flags.h" +#include "sanitizer_interface_internal.h" #include "sanitizer_internal_defs.h" #include "sanitizer_libc.h" #include "sanitizer_list.h" @@ -241,6 +242,7 @@ void SleepForMillis(int millis); u64 NanoTime(); int Atexit(void (*function)(void)); void SortArray(uptr *array, uptr size); +bool TemplateMatch(const char *templ, const char *str); // Exit void NORETURN Abort(); diff --git a/lib/sanitizer_common/sanitizer_common_libcdep.cc b/lib/sanitizer_common/sanitizer_common_libcdep.cc index e357e1cbbfc9..17ef6897ba26 100644 --- a/lib/sanitizer_common/sanitizer_common_libcdep.cc +++ b/lib/sanitizer_common/sanitizer_common_libcdep.cc @@ -121,7 +121,7 @@ void MaybeStartBackgroudThread() { // Start the background thread if one of the rss limits is given. if (!common_flags()->hard_rss_limit_mb && !common_flags()->soft_rss_limit_mb) return; - if (!real_pthread_create) return; // Can't spawn the thread anyway. + if (!&real_pthread_create) return; // Can't spawn the thread anyway. internal_start_thread(BackgroundThread, nullptr); } diff --git a/lib/sanitizer_common/sanitizer_common_syscalls.inc b/lib/sanitizer_common/sanitizer_common_syscalls.inc index a52338b62f5e..7e15d51ff35b 100644 --- a/lib/sanitizer_common/sanitizer_common_syscalls.inc +++ b/lib/sanitizer_common/sanitizer_common_syscalls.inc @@ -2297,7 +2297,8 @@ PRE_SYSCALL(ni_syscall)() {} 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)) +#if !SANITIZER_ANDROID && \ + (defined(__i386) || defined(__x86_64) || defined(__mips64)) if (data) { if (request == ptrace_setregs) { PRE_READ((void *)data, struct_user_regs_struct_sz); @@ -2316,7 +2317,8 @@ PRE_SYSCALL(ptrace)(long request, long pid, long addr, long data) { } POST_SYSCALL(ptrace)(long res, long request, long pid, long addr, long data) { -#if !SANITIZER_ANDROID && (defined(__i386) || defined (__x86_64)) +#if !SANITIZER_ANDROID && \ + (defined(__i386) || defined(__x86_64) || defined(__mips64)) if (res >= 0 && data) { // Note that this is different from the interceptor in // sanitizer_common_interceptors.inc. diff --git a/lib/sanitizer_common/sanitizer_coverage_libcdep.cc b/lib/sanitizer_common/sanitizer_coverage_libcdep.cc index e8f42f68a42f..49887b1e91a9 100644 --- a/lib/sanitizer_common/sanitizer_coverage_libcdep.cc +++ b/lib/sanitizer_common/sanitizer_coverage_libcdep.cc @@ -82,7 +82,7 @@ class CoverageData { void TraceBasicBlock(s32 *id); void InitializeGuardArray(s32 *guards); - void InitializeGuards(s32 *guards, uptr n); + void InitializeGuards(s32 *guards, uptr n, const char *module_name); void ReinitializeGuards(); uptr *data(); @@ -110,6 +110,9 @@ class CoverageData { // Vector of coverage guard arrays, protected by mu. InternalMmapVectorNoCtor<s32*> guard_array_vec; + // Vector of module (compilation unit) names. + InternalMmapVectorNoCtor<const char*> comp_unit_name_vec; + // Caller-Callee (cc) array, size and current index. static const uptr kCcArrayMaxSize = FIRST_32_SECOND_64(1 << 18, 1 << 24); uptr **cc_array; @@ -286,13 +289,15 @@ void CoverageData::Extend(uptr npcs) { atomic_store(&pc_array_size, size, memory_order_release); } -void CoverageData::InitializeGuards(s32 *guards, uptr n) { +void CoverageData::InitializeGuards(s32 *guards, uptr n, + const char *module_name) { // The array 'guards' has n+1 elements, we use the element zero // to store 'n'. CHECK_LT(n, 1 << 30); guards[0] = static_cast<s32>(n); InitializeGuardArray(guards); SpinMutexLock l(&mu); + comp_unit_name_vec.push_back(module_name); guard_array_vec.push_back(guards); } @@ -450,6 +455,14 @@ void CoverageData::DumpTrace() { internal_write(fd, out.data(), out.length()); internal_close(fd); + fd = CovOpenFile(false, "trace-compunits"); + if (fd < 0) return; + out.clear(); + for (uptr i = 0; i < comp_unit_name_vec.size(); i++) + out.append("%s\n", comp_unit_name_vec[i]); + internal_write(fd, out.data(), out.length()); + internal_close(fd); + fd = CovOpenFile(false, "trace-events"); if (fd < 0) return; uptr bytes_to_write = max_idx * sizeof(tr_event_array[0]); @@ -675,9 +688,9 @@ SANITIZER_INTERFACE_ATTRIBUTE void __sanitizer_cov_init() { coverage_data.Init(); } SANITIZER_INTERFACE_ATTRIBUTE void __sanitizer_cov_dump() { CovDump(); } -SANITIZER_INTERFACE_ATTRIBUTE void __sanitizer_cov_module_init(s32 *guards, - uptr npcs) { - coverage_data.InitializeGuards(guards, npcs); +SANITIZER_INTERFACE_ATTRIBUTE void +__sanitizer_cov_module_init(s32 *guards, uptr npcs, const char *module_name) { + coverage_data.InitializeGuards(guards, npcs, module_name); if (!common_flags()->coverage_direct) return; if (SANITIZER_ANDROID && coverage_enabled) { // dlopen/dlclose interceptors do not work on Android, so we rely on diff --git a/lib/sanitizer_common/sanitizer_flag_parser.h b/lib/sanitizer_common/sanitizer_flag_parser.h index 87afb8238c01..0ac7634cb876 100644 --- a/lib/sanitizer_common/sanitizer_flag_parser.h +++ b/lib/sanitizer_common/sanitizer_flag_parser.h @@ -31,7 +31,7 @@ class FlagHandler : public FlagHandlerBase { public: explicit FlagHandler(T *t) : t_(t) {} - bool Parse(const char *value); + bool Parse(const char *value) final; }; template <> diff --git a/lib/sanitizer_common/sanitizer_flags.cc b/lib/sanitizer_common/sanitizer_flags.cc index a2965351cf2a..e835b46a24fc 100644 --- a/lib/sanitizer_common/sanitizer_flags.cc +++ b/lib/sanitizer_common/sanitizer_flags.cc @@ -51,7 +51,7 @@ class FlagHandlerInclude : public FlagHandlerBase { public: explicit FlagHandlerInclude(FlagParser *parser) : parser_(parser) {} - bool Parse(const char *value) { + bool Parse(const char *value) final { char *data; uptr data_mapped_size; int err; diff --git a/lib/sanitizer_common/sanitizer_flags.inc b/lib/sanitizer_common/sanitizer_flags.inc index e8c8a87537d7..58f7f372228f 100644 --- a/lib/sanitizer_common/sanitizer_flags.inc +++ b/lib/sanitizer_common/sanitizer_flags.inc @@ -128,7 +128,6 @@ COMMON_FLAG(const char *, coverage_dir, ".", COMMON_FLAG(bool, full_address_space, false, "Sanitize complete address space; " "by default kernel area on 32-bit platforms will not be sanitized") -COMMON_FLAG(const char *, suppressions, "", "Suppressions file name.") COMMON_FLAG(bool, print_suppressions, true, "Print matched suppressions at exit.") COMMON_FLAG( diff --git a/lib/sanitizer_common/sanitizer_interface_internal.h b/lib/sanitizer_common/sanitizer_interface_internal.h new file mode 100644 index 000000000000..94d9f4e9524a --- /dev/null +++ b/lib/sanitizer_common/sanitizer_interface_internal.h @@ -0,0 +1,58 @@ +//===-- sanitizer_interface_internal.h --------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file is shared between run-time libraries of sanitizers. +// +// This header declares the sanitizer runtime interface functions. +// The runtime library has to define these functions so the instrumented program +// could call them. +// +// See also include/sanitizer/common_interface_defs.h +//===----------------------------------------------------------------------===// +#ifndef SANITIZER_INTERFACE_INTERNAL_H +#define SANITIZER_INTERFACE_INTERNAL_H + +#include "sanitizer_internal_defs.h" + +extern "C" { + // Tell the tools to write their reports to "path.<pid>" instead of stderr. + // The special values are "stdout" and "stderr". + SANITIZER_INTERFACE_ATTRIBUTE + void __sanitizer_set_report_path(const char *path); + + typedef struct { + int coverage_sandboxed; + __sanitizer::sptr coverage_fd; + unsigned int coverage_max_block_size; + } __sanitizer_sandbox_arguments; + + // Notify the tools that the sandbox is going to be turned on. + SANITIZER_INTERFACE_ATTRIBUTE SANITIZER_WEAK_ATTRIBUTE void + __sanitizer_sandbox_on_notify(__sanitizer_sandbox_arguments *args); + + // This function is called by the tool when it has just finished reporting + // an error. 'error_summary' is a one-line string that summarizes + // the error message. This function can be overridden by the client. + SANITIZER_INTERFACE_ATTRIBUTE SANITIZER_WEAK_ATTRIBUTE + void __sanitizer_report_error_summary(const char *error_summary); + + SANITIZER_INTERFACE_ATTRIBUTE void __sanitizer_cov_dump(); + SANITIZER_INTERFACE_ATTRIBUTE void __sanitizer_cov_init(); + SANITIZER_INTERFACE_ATTRIBUTE void __sanitizer_cov(__sanitizer::u32 *guard); + SANITIZER_INTERFACE_ATTRIBUTE + void __sanitizer_annotate_contiguous_container(const void *beg, + const void *end, + const void *old_mid, + const void *new_mid); + SANITIZER_INTERFACE_ATTRIBUTE + int __sanitizer_verify_contiguous_container(const void *beg, const void *mid, + const void *end); +} // extern "C" + +#endif // SANITIZER_INTERFACE_INTERNAL_H diff --git a/lib/sanitizer_common/sanitizer_internal_defs.h b/lib/sanitizer_common/sanitizer_internal_defs.h index 2a0b41f0bb99..a969f305cd1a 100644 --- a/lib/sanitizer_common/sanitizer_internal_defs.h +++ b/lib/sanitizer_common/sanitizer_internal_defs.h @@ -101,41 +101,6 @@ typedef u32 operator_new_size_type; #endif } // namespace __sanitizer -extern "C" { - // Tell the tools to write their reports to "path.<pid>" instead of stderr. - // The special values are "stdout" and "stderr". - SANITIZER_INTERFACE_ATTRIBUTE - void __sanitizer_set_report_path(const char *path); - - typedef struct { - int coverage_sandboxed; - __sanitizer::sptr coverage_fd; - unsigned int coverage_max_block_size; - } __sanitizer_sandbox_arguments; - - // Notify the tools that the sandbox is going to be turned on. - SANITIZER_INTERFACE_ATTRIBUTE SANITIZER_WEAK_ATTRIBUTE void - __sanitizer_sandbox_on_notify(__sanitizer_sandbox_arguments *args); - - // This function is called by the tool when it has just finished reporting - // an error. 'error_summary' is a one-line string that summarizes - // the error message. This function can be overridden by the client. - SANITIZER_INTERFACE_ATTRIBUTE SANITIZER_WEAK_ATTRIBUTE - void __sanitizer_report_error_summary(const char *error_summary); - - SANITIZER_INTERFACE_ATTRIBUTE void __sanitizer_cov_dump(); - SANITIZER_INTERFACE_ATTRIBUTE void __sanitizer_cov_init(); - SANITIZER_INTERFACE_ATTRIBUTE void __sanitizer_cov(__sanitizer::u32 *guard); - SANITIZER_INTERFACE_ATTRIBUTE - void __sanitizer_annotate_contiguous_container(const void *beg, - const void *end, - const void *old_mid, - const void *new_mid); - SANITIZER_INTERFACE_ATTRIBUTE - int __sanitizer_verify_contiguous_container(const void *beg, const void *mid, - const void *end); -} // extern "C" - using namespace __sanitizer; // NOLINT // ----------- ATTENTION ------------- diff --git a/lib/sanitizer_common/sanitizer_libignore.cc b/lib/sanitizer_common/sanitizer_libignore.cc index 8df0467b1e9b..cefb1dc97a17 100644 --- a/lib/sanitizer_common/sanitizer_libignore.cc +++ b/lib/sanitizer_common/sanitizer_libignore.cc @@ -19,24 +19,18 @@ namespace __sanitizer { LibIgnore::LibIgnore(LinkerInitialized) { } -void LibIgnore::Init(const SuppressionContext &supp) { +void LibIgnore::AddIgnoredLibrary(const char *name_templ) { BlockingMutexLock lock(&mutex_); - CHECK_EQ(count_, 0); - const uptr n = supp.SuppressionCount(); - for (uptr i = 0; i < n; i++) { - const Suppression *s = supp.SuppressionAt(i); - if (s->type != SuppressionLib) - continue; - if (count_ >= kMaxLibs) { - Report("%s: too many called_from_lib suppressions (max: %d)\n", - SanitizerToolName, kMaxLibs); - Die(); - } - Lib *lib = &libs_[count_++]; - lib->templ = internal_strdup(s->templ); - lib->name = 0; - lib->loaded = false; + if (count_ >= kMaxLibs) { + Report("%s: too many ignored libraries (max: %d)\n", SanitizerToolName, + kMaxLibs); + Die(); } + Lib *lib = &libs_[count_++]; + lib->templ = internal_strdup(name_templ); + lib->name = nullptr; + lib->real_name = nullptr; + lib->loaded = false; } void LibIgnore::OnLibraryLoaded(const char *name) { diff --git a/lib/sanitizer_common/sanitizer_libignore.h b/lib/sanitizer_common/sanitizer_libignore.h index 8e1d584d8e3c..cd56c36c1c0e 100644 --- a/lib/sanitizer_common/sanitizer_libignore.h +++ b/lib/sanitizer_common/sanitizer_libignore.h @@ -8,8 +8,8 @@ //===----------------------------------------------------------------------===// // // LibIgnore allows to ignore all interceptors called from a particular set -// of dynamic libraries. LibIgnore remembers all "called_from_lib" suppressions -// from the provided SuppressionContext; finds code ranges for the libraries; +// of dynamic libraries. LibIgnore can be initialized with several templates +// of names of libraries to be ignored. It finds code ranges for the libraries; // and checks whether the provided PC value belongs to the code ranges. // //===----------------------------------------------------------------------===// @@ -19,7 +19,6 @@ #include "sanitizer_internal_defs.h" #include "sanitizer_common.h" -#include "sanitizer_suppressions.h" #include "sanitizer_atomic.h" #include "sanitizer_mutex.h" @@ -29,8 +28,8 @@ class LibIgnore { public: explicit LibIgnore(LinkerInitialized); - // Fetches all "called_from_lib" suppressions from the SuppressionContext. - void Init(const SuppressionContext &supp); + // Must be called during initialization. + void AddIgnoredLibrary(const char *name_templ); // Must be called after a new dynamic library is loaded. void OnLibraryLoaded(const char *name); diff --git a/lib/sanitizer_common/sanitizer_linux.cc b/lib/sanitizer_common/sanitizer_linux.cc index 26138ba29d35..8029181a5173 100644 --- a/lib/sanitizer_common/sanitizer_linux.cc +++ b/lib/sanitizer_common/sanitizer_linux.cc @@ -860,6 +860,13 @@ uptr internal_clone(int (*fn)(void *), void *child_stack, int flags, void *arg, : "rsp", "memory", "r11", "rcx"); return res; } +#elif defined(__mips__) +// TODO(sagarthakur): clone function is to be rewritten in assembly. +uptr internal_clone(int (*fn)(void *), void *child_stack, int flags, void *arg, + int *parent_tidptr, void *newtls, int *child_tidptr) { + return clone(fn, child_stack, flags, arg, parent_tidptr, + newtls, child_tidptr); +} #endif // defined(__x86_64__) && SANITIZER_LINUX #if SANITIZER_ANDROID diff --git a/lib/sanitizer_common/sanitizer_linux.h b/lib/sanitizer_common/sanitizer_linux.h index 3013c25f7c38..b2e603d3a23e 100644 --- a/lib/sanitizer_common/sanitizer_linux.h +++ b/lib/sanitizer_common/sanitizer_linux.h @@ -43,7 +43,7 @@ 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__) +#if defined(__x86_64__) || defined(__mips__) 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/lib/sanitizer_common/sanitizer_linux_libcdep.cc b/lib/sanitizer_common/sanitizer_linux_libcdep.cc index df42c3604ae9..c71b6257ebc3 100644 --- a/lib/sanitizer_common/sanitizer_linux_libcdep.cc +++ b/lib/sanitizer_common/sanitizer_linux_libcdep.cc @@ -168,6 +168,20 @@ static uptr g_tls_size; # define DL_INTERNAL_FUNCTION #endif +#if defined(__mips__) +// 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; + const uptr kTlsAlign = 16; + const uptr kTlsPreTcbSize = + (ThreadDescriptorSize() + kTcbHead + kTlsAlign - 1) & ~(kTlsAlign - 1); + InitTlsSize(); + g_tls_size = (g_tls_size + kTlsPreTcbSize + kTlsAlign -1) & ~(kTlsAlign - 1); + return kTlsPreTcbSize; +} +#endif + void InitTlsSize() { #if !SANITIZER_FREEBSD && !SANITIZER_ANDROID && !SANITIZER_GO typedef void (*get_tls_func)(size_t*, size_t*) DL_INTERNAL_FUNCTION; @@ -184,7 +198,8 @@ void InitTlsSize() { #endif // !SANITIZER_FREEBSD && !SANITIZER_ANDROID } -#if (defined(__x86_64__) || defined(__i386__)) && SANITIZER_LINUX +#if (defined(__x86_64__) || defined(__i386__) || defined(__mips__)) \ + && SANITIZER_LINUX // sizeof(struct thread) from glibc. static atomic_uintptr_t kThreadDescriptorSize; @@ -192,6 +207,7 @@ uptr ThreadDescriptorSize() { uptr val = atomic_load(&kThreadDescriptorSize, memory_order_relaxed); if (val) return val; +#if defined(__x86_64__) || defined(__i386__) #ifdef _CS_GNU_LIBC_VERSION char buf[64]; uptr len = confstr(_CS_GNU_LIBC_VERSION, buf, sizeof(buf)); @@ -224,6 +240,13 @@ uptr ThreadDescriptorSize() { return val; } #endif +#elif defined(__mips__) + // TODO(sagarthakur): add more values as per different glibc versions. + val = FIRST_32_SECOND_64(1152, 1776); + if (val) + atomic_store(&kThreadDescriptorSize, val, memory_order_relaxed); + return val; +#endif return 0; } @@ -240,12 +263,24 @@ uptr ThreadSelf() { asm("mov %%gs:%c1,%0" : "=r"(descr_addr) : "i"(kThreadSelfOffset)); # elif defined(__x86_64__) asm("mov %%fs:%c1,%0" : "=r"(descr_addr) : "i"(kThreadSelfOffset)); +# elif defined(__mips__) + // MIPS uses TLS variant I. The thread pointer (in hardware register $29) + // points to the end of the TCB + 0x7000. The pthread_descr structure is + // immediately in front of the TCB. TlsPreTcbSize() includes the size of the + // TCB and the size of pthread_descr. + const uptr kTlsTcbOffset = 0x7000; + uptr thread_pointer; + asm volatile(".set push;\ + .set mips64r2;\ + rdhwr %0,$29;\ + .set pop" : "=r" (thread_pointer)); + descr_addr = thread_pointer - kTlsTcbOffset - TlsPreTcbSize(); # else # error "unsupported CPU arch" # endif return descr_addr; } -#endif // (defined(__x86_64__) || defined(__i386__)) && SANITIZER_LINUX +#endif // (x86_64 || i386 || MIPS) && SANITIZER_LINUX #if SANITIZER_FREEBSD static void **ThreadSelfSegbase() { @@ -275,6 +310,9 @@ static void GetTls(uptr *addr, uptr *size) { *size = GetTlsSize(); *addr -= *size; *addr += ThreadDescriptorSize(); +# elif defined(__mips__) + *addr = ThreadSelf(); + *size = GetTlsSize(); # else *addr = 0; *size = 0; @@ -298,6 +336,7 @@ static void GetTls(uptr *addr, uptr *size) { } #endif +#if !SANITIZER_GO uptr GetTlsSize() { #if SANITIZER_FREEBSD uptr addr, size; @@ -307,6 +346,7 @@ uptr GetTlsSize() { return g_tls_size; #endif } +#endif void GetThreadStackAndTls(bool main, uptr *stk_addr, uptr *stk_size, uptr *tls_addr, uptr *tls_size) { diff --git a/lib/sanitizer_common/sanitizer_platform_interceptors.h b/lib/sanitizer_common/sanitizer_platform_interceptors.h index 54a1cfd8582b..438ecbaa2ec8 100644 --- a/lib/sanitizer_common/sanitizer_platform_interceptors.h +++ b/lib/sanitizer_common/sanitizer_platform_interceptors.h @@ -127,7 +127,7 @@ #define SANITIZER_INTERCEPT_READDIR SI_NOT_WINDOWS #define SANITIZER_INTERCEPT_READDIR64 SI_LINUX_NOT_ANDROID #define SANITIZER_INTERCEPT_PTRACE SI_LINUX_NOT_ANDROID && \ - (defined(__i386) || defined (__x86_64)) // NOLINT + (defined(__i386) || defined (__x86_64) || defined (__mips64)) // NOLINT #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 diff --git a/lib/sanitizer_common/sanitizer_platform_limits_posix.cc b/lib/sanitizer_common/sanitizer_platform_limits_posix.cc index 4eabcd7a3d22..8824c8088f2d 100644 --- a/lib/sanitizer_common/sanitizer_platform_limits_posix.cc +++ b/lib/sanitizer_common/sanitizer_platform_limits_posix.cc @@ -97,7 +97,6 @@ # include <sys/link_elf.h> # include <netinet/ip_mroute.h> # include <netinet/in.h> -# include <netinet/ip_compat.h> # include <net/ethernet.h> # include <net/ppp_defs.h> # include <glob.h> @@ -117,6 +116,9 @@ #if SANITIZER_LINUX || SANITIZER_FREEBSD # include <utime.h> # include <sys/ptrace.h> +# if defined(__mips64) +# include <asm/ptrace.h> +# endif #endif #if !SANITIZER_ANDROID @@ -140,6 +142,9 @@ #include <sys/shm.h> #include <sys/statvfs.h> #include <sys/timex.h> +#if defined(__mips64) +# include <sys/procfs.h> +#endif #include <sys/user.h> #include <sys/ustat.h> #include <linux/cyclades.h> @@ -284,14 +289,19 @@ namespace __sanitizer { #endif #if SANITIZER_LINUX && !SANITIZER_ANDROID && \ - (defined(__i386) || defined(__x86_64)) + (defined(__i386) || defined(__x86_64) || defined(__mips64)) +#if defined(__mips64) + unsigned struct_user_regs_struct_sz = sizeof(struct pt_regs); + unsigned struct_user_fpregs_struct_sz = sizeof(elf_fpregset_t); +#else unsigned struct_user_regs_struct_sz = sizeof(struct user_regs_struct); unsigned struct_user_fpregs_struct_sz = sizeof(struct user_fpregs_struct); -#ifdef __x86_64 +#endif // __mips64 +#if (defined(__x86_64) || defined(__mips64)) unsigned struct_user_fpxregs_struct_sz = 0; #else unsigned struct_user_fpxregs_struct_sz = sizeof(struct user_fpxregs_struct); -#endif +#endif // __x86_64 || __mips64 int ptrace_peektext = PTRACE_PEEKTEXT; int ptrace_peekdata = PTRACE_PEEKDATA; diff --git a/lib/sanitizer_common/sanitizer_platform_limits_posix.h b/lib/sanitizer_common/sanitizer_platform_limits_posix.h index d375e01a20e4..bd20bea94e93 100644 --- a/lib/sanitizer_common/sanitizer_platform_limits_posix.h +++ b/lib/sanitizer_common/sanitizer_platform_limits_posix.h @@ -547,6 +547,10 @@ namespace __sanitizer { #if SANITIZER_FREEBSD typedef __sanitizer_sigset_t __sanitizer_kernel_sigset_t; +#elif defined(__mips__) + struct __sanitizer_kernel_sigset_t { + u8 sig[16]; + }; #else struct __sanitizer_kernel_sigset_t { u8 sig[8]; @@ -695,7 +699,7 @@ namespace __sanitizer { #endif #if SANITIZER_LINUX && !SANITIZER_ANDROID && \ - (defined(__i386) || defined(__x86_64)) + (defined(__i386) || defined(__x86_64) || defined(__mips64)) extern unsigned struct_user_regs_struct_sz; extern unsigned struct_user_fpregs_struct_sz; extern unsigned struct_user_fpxregs_struct_sz; diff --git a/lib/sanitizer_common/sanitizer_posix.cc b/lib/sanitizer_common/sanitizer_posix.cc index 6cb51efce2d1..5bc41c2580fb 100644 --- a/lib/sanitizer_common/sanitizer_posix.cc +++ b/lib/sanitizer_common/sanitizer_posix.cc @@ -30,6 +30,13 @@ #include <sys/personality.h> #endif +#if SANITIZER_FREEBSD +// The MAP_NORESERVE define has been removed in FreeBSD 11.x, and even before +// that, it was never implemented. So just define it to zero. +#undef MAP_NORESERVE +#define MAP_NORESERVE 0 +#endif + namespace __sanitizer { // ------------- sanitizer_common.h diff --git a/lib/sanitizer_common/sanitizer_stoptheworld_linux_libcdep.cc b/lib/sanitizer_common/sanitizer_stoptheworld_linux_libcdep.cc index d20b52483a91..ad20e39556d8 100644 --- a/lib/sanitizer_common/sanitizer_stoptheworld_linux_libcdep.cc +++ b/lib/sanitizer_common/sanitizer_stoptheworld_linux_libcdep.cc @@ -14,7 +14,7 @@ #include "sanitizer_platform.h" -#if SANITIZER_LINUX && defined(__x86_64__) +#if SANITIZER_LINUX && (defined(__x86_64__) || defined(__mips__)) #include "sanitizer_stoptheworld.h" @@ -89,36 +89,50 @@ class ThreadSuspender { bool SuspendThread(SuspendedThreadID thread_id); }; -bool ThreadSuspender::SuspendThread(SuspendedThreadID thread_id) { +bool ThreadSuspender::SuspendThread(SuspendedThreadID tid) { // Are we already attached to this thread? // Currently this check takes linear time, however the number of threads is // usually small. - if (suspended_threads_list_.Contains(thread_id)) + if (suspended_threads_list_.Contains(tid)) return false; int pterrno; - if (internal_iserror(internal_ptrace(PTRACE_ATTACH, thread_id, NULL, NULL), + if (internal_iserror(internal_ptrace(PTRACE_ATTACH, tid, NULL, NULL), &pterrno)) { // Either the thread is dead, or something prevented us from attaching. // Log this event and move on. - VReport(1, "Could not attach to thread %d (errno %d).\n", thread_id, - pterrno); + VReport(1, "Could not attach to thread %d (errno %d).\n", tid, pterrno); return false; } else { - VReport(1, "Attached to thread %d.\n", thread_id); + VReport(1, "Attached to thread %d.\n", tid); // The thread is not guaranteed to stop before ptrace returns, so we must - // wait on it. - uptr waitpid_status; - HANDLE_EINTR(waitpid_status, internal_waitpid(thread_id, NULL, __WALL)); - int wperrno; - if (internal_iserror(waitpid_status, &wperrno)) { - // Got a ECHILD error. I don't think this situation is possible, but it - // doesn't hurt to report it. - VReport(1, "Waiting on thread %d failed, detaching (errno %d).\n", - thread_id, wperrno); - internal_ptrace(PTRACE_DETACH, thread_id, NULL, NULL); - return false; + // wait on it. Note: if the thread receives a signal concurrently, + // we can get notification about the signal before notification about stop. + // In such case we need to forward the signal to the thread, otherwise + // the signal will be missed (as we do PTRACE_DETACH with arg=0) and + // any logic relying on signals will break. After forwarding we need to + // continue to wait for stopping, because the thread is not stopped yet. + // We do ignore delivery of SIGSTOP, because we want to make stop-the-world + // as invisible as possible. + for (;;) { + int status; + uptr waitpid_status; + HANDLE_EINTR(waitpid_status, internal_waitpid(tid, &status, __WALL)); + int wperrno; + if (internal_iserror(waitpid_status, &wperrno)) { + // Got a ECHILD error. I don't think this situation is possible, but it + // doesn't hurt to report it. + VReport(1, "Waiting on thread %d failed, detaching (errno %d).\n", + tid, wperrno); + internal_ptrace(PTRACE_DETACH, tid, NULL, NULL); + return false; + } + if (WIFSTOPPED(status) && WSTOPSIG(status) != SIGSTOP) { + internal_ptrace(PTRACE_CONT, tid, 0, (void*)(uptr)WSTOPSIG(status)); + continue; + } + break; } - suspended_threads_list_.Append(thread_id); + suspended_threads_list_.Append(tid); return true; } } @@ -170,10 +184,9 @@ bool ThreadSuspender::SuspendAllThreads() { // Pointer to the ThreadSuspender instance for use in signal handler. static ThreadSuspender *thread_suspender_instance = NULL; -// Signals that should not be blocked (this is used in the parent thread as well -// as the tracer thread). -static const int kUnblockedSignals[] = { SIGABRT, SIGILL, SIGFPE, SIGSEGV, - SIGBUS, SIGXCPU, SIGXFSZ }; +// Synchronous signals that should not be blocked. +static const int kSyncSignals[] = { SIGABRT, SIGILL, SIGFPE, SIGSEGV, SIGBUS, + SIGXCPU, SIGXFSZ }; // Structure for passing arguments into the tracer thread. struct TracerThreadArgument { @@ -188,7 +201,7 @@ struct TracerThreadArgument { static DieCallbackType old_die_callback; // Signal handler to wake up suspended threads when the tracer thread dies. -void TracerThreadSignalHandler(int signum, void *siginfo, void *) { +static void TracerThreadSignalHandler(int signum, void *siginfo, void *) { if (thread_suspender_instance != NULL) { if (signum == SIGABRT) thread_suspender_instance->KillAllThreads(); @@ -228,6 +241,7 @@ static int TracerThread(void* argument) { tracer_thread_argument->mutex.Lock(); tracer_thread_argument->mutex.Unlock(); + old_die_callback = GetDieCallback(); SetDieCallback(TracerThreadDieCallback); ThreadSuspender thread_suspender(internal_getppid()); @@ -242,17 +256,14 @@ static int TracerThread(void* argument) { handler_stack.ss_size = kHandlerStackSize; internal_sigaltstack(&handler_stack, NULL); - // Install our handler for fatal signals. Other signals should be blocked by - // the mask we inherited from the caller thread. - for (uptr signal_index = 0; signal_index < ARRAY_SIZE(kUnblockedSignals); - signal_index++) { - __sanitizer_sigaction new_sigaction; - internal_memset(&new_sigaction, 0, sizeof(new_sigaction)); - new_sigaction.sigaction = TracerThreadSignalHandler; - new_sigaction.sa_flags = SA_ONSTACK | SA_SIGINFO; - internal_sigfillset(&new_sigaction.sa_mask); - internal_sigaction_norestorer(kUnblockedSignals[signal_index], - &new_sigaction, NULL); + // Install our handler for synchronous signals. Other signals should be + // blocked by the mask we inherited from the parent thread. + for (uptr i = 0; i < ARRAY_SIZE(kSyncSignals); i++) { + __sanitizer_sigaction act; + internal_memset(&act, 0, sizeof(act)); + act.sigaction = TracerThreadSignalHandler; + act.sa_flags = SA_ONSTACK | SA_SIGINFO; + internal_sigaction_norestorer(kSyncSignals[i], &act, 0); } int exit_code = 0; @@ -265,9 +276,11 @@ static int TracerThread(void* argument) { thread_suspender.ResumeAllThreads(); exit_code = 0; } + // Note, this is a bad race. If TracerThreadDieCallback is already started + // in another thread and observed that thread_suspender_instance != 0, + // it can call KillAllThreads on the destroyed variable. + SetDieCallback(old_die_callback); thread_suspender_instance = NULL; - handler_stack.ss_flags = SS_DISABLE; - internal_sigaltstack(&handler_stack, NULL); return exit_code; } @@ -299,53 +312,21 @@ class ScopedStackSpaceWithGuard { // into globals. static __sanitizer_sigset_t blocked_sigset; static __sanitizer_sigset_t old_sigset; -static __sanitizer_sigaction old_sigactions - [ARRAY_SIZE(kUnblockedSignals)]; class StopTheWorldScope { public: StopTheWorldScope() { - // Block all signals that can be blocked safely, and install - // default handlers for the remaining signals. - // We cannot allow user-defined handlers to run while the ThreadSuspender - // thread is active, because they could conceivably call some libc functions - // which modify errno (which is shared between the two threads). - internal_sigfillset(&blocked_sigset); - for (uptr signal_index = 0; signal_index < ARRAY_SIZE(kUnblockedSignals); - signal_index++) { - // Remove the signal from the set of blocked signals. - internal_sigdelset(&blocked_sigset, kUnblockedSignals[signal_index]); - // Install the default handler. - __sanitizer_sigaction new_sigaction; - internal_memset(&new_sigaction, 0, sizeof(new_sigaction)); - new_sigaction.handler = SIG_DFL; - internal_sigfillset(&new_sigaction.sa_mask); - internal_sigaction_norestorer(kUnblockedSignals[signal_index], - &new_sigaction, &old_sigactions[signal_index]); - } - int sigprocmask_status = - internal_sigprocmask(SIG_BLOCK, &blocked_sigset, &old_sigset); - CHECK_EQ(sigprocmask_status, 0); // sigprocmask should never fail // Make this process dumpable. Processes that are not dumpable cannot be // attached to. process_was_dumpable_ = internal_prctl(PR_GET_DUMPABLE, 0, 0, 0, 0); if (!process_was_dumpable_) internal_prctl(PR_SET_DUMPABLE, 1, 0, 0, 0); - old_die_callback = GetDieCallback(); } ~StopTheWorldScope() { - SetDieCallback(old_die_callback); // Restore the dumpable flag. if (!process_was_dumpable_) internal_prctl(PR_SET_DUMPABLE, 0, 0, 0, 0); - // Restore the signal handlers. - for (uptr signal_index = 0; signal_index < ARRAY_SIZE(kUnblockedSignals); - signal_index++) { - internal_sigaction_norestorer(kUnblockedSignals[signal_index], - &old_sigactions[signal_index], NULL); - } - internal_sigprocmask(SIG_SETMASK, &old_sigset, &old_sigset); } private: @@ -378,11 +359,36 @@ void StopTheWorld(StopTheWorldCallback callback, void *argument) { // Block the execution of TracerThread until after we have set ptrace // permissions. tracer_thread_argument.mutex.Lock(); + // Signal handling story. + // We don't want async signals to be delivered to the tracer thread, + // so we block all async signals before creating the thread. An async signal + // handler can temporary modify errno, which is shared with this thread. + // We ought to use pthread_sigmask here, because sigprocmask has undefined + // behavior in multithreaded programs. However, on linux sigprocmask is + // equivalent to pthread_sigmask with the exception that pthread_sigmask + // does not allow to block some signals used internally in pthread + // implementation. We are fine with blocking them here, we are really not + // going to pthread_cancel the thread. + // The tracer thread should not raise any synchronous signals. But in case it + // does, we setup a special handler for sync signals that properly kills the + // parent as well. Note: we don't pass CLONE_SIGHAND to clone, so handlers + // in the tracer thread won't interfere with user program. Double note: if a + // user does something along the lines of 'kill -11 pid', that can kill the + // process even if user setup own handler for SEGV. + // Thing to watch out for: this code should not change behavior of user code + // in any observable way. In particular it should not override user signal + // handlers. + internal_sigfillset(&blocked_sigset); + for (uptr i = 0; i < ARRAY_SIZE(kSyncSignals); i++) + internal_sigdelset(&blocked_sigset, kSyncSignals[i]); + int rv = internal_sigprocmask(SIG_BLOCK, &blocked_sigset, &old_sigset); + CHECK_EQ(rv, 0); uptr tracer_pid = internal_clone( TracerThread, tracer_stack.Bottom(), CLONE_VM | CLONE_FS | CLONE_FILES | CLONE_UNTRACED, &tracer_thread_argument, 0 /* parent_tidptr */, 0 /* newtls */, 0 /* child_tidptr */); + internal_sigprocmask(SIG_SETMASK, &old_sigset, 0); int local_errno = 0; if (internal_iserror(tracer_pid, &local_errno)) { VReport(1, "Failed spawning a tracer thread (errno %d).\n", local_errno); @@ -459,4 +465,4 @@ uptr SuspendedThreadsList::RegisterCount() { } } // namespace __sanitizer -#endif // SANITIZER_LINUX && defined(__x86_64__) +#endif // SANITIZER_LINUX && (defined(__x86_64__) || defined(__mips__)) diff --git a/lib/sanitizer_common/sanitizer_suppressions.cc b/lib/sanitizer_common/sanitizer_suppressions.cc index 6b75036c7e50..2b697e955709 100644 --- a/lib/sanitizer_common/sanitizer_suppressions.cc +++ b/lib/sanitizer_common/sanitizer_suppressions.cc @@ -7,7 +7,7 @@ // //===----------------------------------------------------------------------===// // -// Suppression parsing/matching code shared between TSan and LSan. +// Suppression parsing/matching code. // //===----------------------------------------------------------------------===// @@ -21,97 +21,43 @@ namespace __sanitizer { -static const char *const kTypeStrings[SuppressionTypeCount] = { - "none", "race", "mutex", "thread", "signal", "leak", "called_from_lib", - "deadlock", "vptr_check", "interceptor_name", "interceptor_via_fun", - "interceptor_via_lib"}; - -bool TemplateMatch(char *templ, const char *str) { - if (str == 0 || str[0] == 0) - return false; - bool start = false; - if (templ && templ[0] == '^') { - start = true; - templ++; - } - bool asterisk = false; - while (templ && templ[0]) { - if (templ[0] == '*') { - templ++; - start = false; - asterisk = true; - continue; - } - if (templ[0] == '$') - return str[0] == 0 || asterisk; - if (str[0] == 0) - return false; - char *tpos = (char*)internal_strchr(templ, '*'); - char *tpos1 = (char*)internal_strchr(templ, '$'); - if (tpos == 0 || (tpos1 && tpos1 < tpos)) - tpos = tpos1; - if (tpos != 0) - tpos[0] = 0; - const char *str0 = str; - const char *spos = internal_strstr(str, templ); - str = spos + internal_strlen(templ); - templ = tpos; - if (tpos) - tpos[0] = tpos == tpos1 ? '$' : '*'; - if (spos == 0) - return false; - if (start && spos != str0) - return false; - start = false; - asterisk = false; - } - return true; -} - -ALIGNED(64) static char placeholder[sizeof(SuppressionContext)]; -static SuppressionContext *suppression_ctx = 0; - -SuppressionContext::SuppressionContext() : suppressions_(1), can_parse_(true) { - internal_memset(has_suppresson_type_, 0, sizeof(has_suppresson_type_)); -} - -SuppressionContext *SuppressionContext::Get() { - CHECK(suppression_ctx); - return suppression_ctx; +SuppressionContext::SuppressionContext(const char *suppression_types[], + int suppression_types_num) + : suppression_types_(suppression_types), + suppression_types_num_(suppression_types_num), suppressions_(1), + can_parse_(true) { + CHECK_LE(suppression_types_num_, kMaxSuppressionTypes); + internal_memset(has_suppression_type_, 0, suppression_types_num_); } -void SuppressionContext::InitIfNecessary() { - if (suppression_ctx) +void SuppressionContext::ParseFromFile(const char *filename) { + if (filename[0] == '\0') return; - suppression_ctx = new(placeholder) SuppressionContext; - if (common_flags()->suppressions[0] == '\0') - return; - char *suppressions_from_file; + char *file_contents; uptr buffer_size; - uptr contents_size = - ReadFileToBuffer(common_flags()->suppressions, &suppressions_from_file, - &buffer_size, 1 << 26 /* max_len */); + uptr contents_size = ReadFileToBuffer(filename, &file_contents, &buffer_size, + 1 << 26 /* max_len */); if (contents_size == 0) { Printf("%s: failed to read suppressions file '%s'\n", SanitizerToolName, - common_flags()->suppressions); + filename); Die(); } - suppression_ctx->Parse(suppressions_from_file); + Parse(file_contents); } -bool SuppressionContext::Match(const char *str, SuppressionType type, +bool SuppressionContext::Match(const char *str, const char *type, Suppression **s) { - if (!has_suppresson_type_[type]) - return false; can_parse_ = false; - uptr i; - for (i = 0; i < suppressions_.size(); i++) - if (type == suppressions_[i].type && - TemplateMatch(suppressions_[i].templ, str)) - break; - if (i == suppressions_.size()) return false; - *s = &suppressions_[i]; - return true; + if (!HasSuppressionType(type)) + return false; + for (uptr i = 0; i < suppressions_.size(); i++) { + Suppression &cur = suppressions_[i]; + if (0 == internal_strcmp(cur.type, type) && TemplateMatch(cur.templ, str)) { + *s = &cur; + return true; + } + } + return false; } static const char *StripPrefix(const char *str, const char *prefix) { @@ -139,26 +85,26 @@ void SuppressionContext::Parse(const char *str) { while (line != end2 && (end2[-1] == ' ' || end2[-1] == '\t')) end2--; int type; - for (type = 0; type < SuppressionTypeCount; type++) { - const char *next_char = StripPrefix(line, kTypeStrings[type]); + for (type = 0; type < suppression_types_num_; type++) { + const char *next_char = StripPrefix(line, suppression_types_[type]); if (next_char && *next_char == ':') { line = ++next_char; break; } } - if (type == SuppressionTypeCount) { + if (type == suppression_types_num_) { Printf("%s: failed to parse suppressions\n", SanitizerToolName); Die(); } Suppression s; - s.type = static_cast<SuppressionType>(type); + s.type = suppression_types_[type]; s.templ = (char*)InternalAlloc(end2 - line + 1); internal_memcpy(s.templ, line, end2 - line); s.templ[end2 - line] = 0; s.hit_count = 0; s.weight = 0; suppressions_.push_back(s); - has_suppresson_type_[s.type] = true; + has_suppression_type_[type] = true; } if (end[0] == 0) break; @@ -170,8 +116,12 @@ uptr SuppressionContext::SuppressionCount() const { return suppressions_.size(); } -bool SuppressionContext::HasSuppressionType(SuppressionType type) const { - return has_suppresson_type_[type]; +bool SuppressionContext::HasSuppressionType(const char *type) const { + for (int i = 0; i < suppression_types_num_; i++) { + if (0 == internal_strcmp(type, suppression_types_[i])) + return has_suppression_type_[i]; + } + return false; } const Suppression *SuppressionContext::SuppressionAt(uptr i) const { @@ -186,9 +136,4 @@ void SuppressionContext::GetMatched( matched->push_back(&suppressions_[i]); } -const char *SuppressionTypeString(SuppressionType t) { - CHECK(t < SuppressionTypeCount); - return kTypeStrings[t]; -} - } // namespace __sanitizer diff --git a/lib/sanitizer_common/sanitizer_suppressions.h b/lib/sanitizer_common/sanitizer_suppressions.h index 453731456169..02dbf6f9690b 100644 --- a/lib/sanitizer_common/sanitizer_suppressions.h +++ b/lib/sanitizer_common/sanitizer_suppressions.h @@ -7,7 +7,7 @@ // //===----------------------------------------------------------------------===// // -// Suppression parsing/matching code shared between TSan and LSan. +// Suppression parsing/matching code. // //===----------------------------------------------------------------------===// #ifndef SANITIZER_SUPPRESSIONS_H @@ -18,24 +18,8 @@ namespace __sanitizer { -enum SuppressionType { - SuppressionNone, - SuppressionRace, - SuppressionMutex, - SuppressionThread, - SuppressionSignal, - SuppressionLeak, - SuppressionLib, - SuppressionDeadlock, - SuppressionVptrCheck, - SuppressionInterceptorName, - SuppressionInterceptorViaFunction, - SuppressionInterceptorViaLibrary, - SuppressionTypeCount -}; - struct Suppression { - SuppressionType type; + const char *type; char *templ; unsigned hit_count; uptr weight; @@ -43,33 +27,29 @@ struct Suppression { class SuppressionContext { public: + // Create new SuppressionContext capable of parsing given suppression types. + SuppressionContext(const char *supprression_types[], + int suppression_types_num); + + void ParseFromFile(const char *filename); void Parse(const char *str); - bool Match(const char* str, SuppressionType type, Suppression **s); + + bool Match(const char *str, const char *type, Suppression **s); uptr SuppressionCount() const; - bool HasSuppressionType(SuppressionType type) const; + bool HasSuppressionType(const char *type) const; const Suppression *SuppressionAt(uptr i) const; void GetMatched(InternalMmapVector<Suppression *> *matched); - // Create a SuppressionContext singleton if it hasn't been created earlier. - // Not thread safe. Must be called early during initialization (but after - // runtime flags are parsed). - static void InitIfNecessary(); - // Returns a SuppressionContext singleton. - static SuppressionContext *Get(); - private: - SuppressionContext(); + static const int kMaxSuppressionTypes = 16; + const char **const suppression_types_; + const int suppression_types_num_; + InternalMmapVector<Suppression> suppressions_; - bool has_suppresson_type_[SuppressionTypeCount]; + bool has_suppression_type_[kMaxSuppressionTypes]; bool can_parse_; - - friend class SuppressionContextTest; }; -const char *SuppressionTypeString(SuppressionType t); - -bool TemplateMatch(char *templ, const char *str); - } // namespace __sanitizer #endif // SANITIZER_SUPPRESSIONS_H diff --git a/lib/sanitizer_common/sanitizer_symbolizer_win.cc b/lib/sanitizer_common/sanitizer_symbolizer_win.cc index 6bb7d3805604..ed96a3a895a8 100644 --- a/lib/sanitizer_common/sanitizer_symbolizer_win.cc +++ b/lib/sanitizer_common/sanitizer_symbolizer_win.cc @@ -30,25 +30,7 @@ class WinSymbolizer : public Symbolizer { SymbolizedStack *frame = SymbolizedStack::New(addr); BlockingMutexLock l(&dbghelp_mu_); - if (!initialized_) { - if (!TrySymInitialize()) { - // OK, maybe the client app has called SymInitialize already. - // That's a bit unfortunate for us as all the DbgHelp functions are - // single-threaded and we can't coordinate with the app. - // FIXME: Can we stop the other threads at this point? - // Anyways, we have to reconfigure stuff to make sure that SymInitialize - // has all the appropriate options set. - // Cross our fingers and reinitialize DbgHelp. - Report("*** WARNING: Failed to initialize DbgHelp! ***\n"); - Report("*** Most likely this means that the app is already ***\n"); - Report("*** using DbgHelp, possibly with incompatible flags. ***\n"); - Report("*** Due to technical reasons, symbolization might crash ***\n"); - Report("*** or produce wrong results. ***\n"); - SymCleanup(GetCurrentProcess()); - TrySymInitialize(); - } - initialized_ = true; - } + InitializeIfNeeded(); // See http://msdn.microsoft.com/en-us/library/ms680578(VS.85).aspx char buffer[sizeof(SYMBOL_INFO) + MAX_SYM_NAME * sizeof(CHAR)]; @@ -100,6 +82,58 @@ class WinSymbolizer : public Symbolizer { // FIXME: Implement GetModuleNameAndOffsetForPC(). private: + void InitializeIfNeeded() { + if (initialized_) + return; + if (!TrySymInitialize()) { + // OK, maybe the client app has called SymInitialize already. + // That's a bit unfortunate for us as all the DbgHelp functions are + // single-threaded and we can't coordinate with the app. + // FIXME: Can we stop the other threads at this point? + // Anyways, we have to reconfigure stuff to make sure that SymInitialize + // has all the appropriate options set. + // Cross our fingers and reinitialize DbgHelp. + Report("*** WARNING: Failed to initialize DbgHelp! ***\n"); + Report("*** Most likely this means that the app is already ***\n"); + Report("*** using DbgHelp, possibly with incompatible flags. ***\n"); + Report("*** Due to technical reasons, symbolization might crash ***\n"); + Report("*** or produce wrong results. ***\n"); + SymCleanup(GetCurrentProcess()); + TrySymInitialize(); + } + initialized_ = true; + + // When an executable is run from a location different from the one where it + // was originally built, we may not see the nearby PDB files. + // To work around this, let's append the directory of the main module + // to the symbol search path. All the failures below are not fatal. + const size_t kSymPathSize = 2048; + static wchar_t path_buffer[kSymPathSize + 1 + MAX_PATH]; + if (!SymGetSearchPathW(GetCurrentProcess(), path_buffer, kSymPathSize)) { + Report("*** WARNING: Failed to SymGetSearchPathW ***\n"); + return; + } + size_t sz = wcslen(path_buffer); + if (sz) { + CHECK_EQ(0, wcscat_s(path_buffer, L";")); + sz++; + } + DWORD res = GetModuleFileNameW(NULL, path_buffer + sz, MAX_PATH); + if (res == 0 || res == MAX_PATH) { + Report("*** WARNING: Failed to getting the EXE directory ***\n"); + return; + } + // Write the zero character in place of the last backslash to get the + // directory of the main module at the end of path_buffer. + wchar_t *last_bslash = wcsrchr(path_buffer + sz, L'\\'); + CHECK_NE(last_bslash, 0); + *last_bslash = L'\0'; + if (!SymSetSearchPathW(GetCurrentProcess(), path_buffer)) { + Report("*** WARNING: Failed to SymSetSearchPathW\n"); + return; + } + } + bool TrySymInitialize() { SymSetOptions(SYMOPT_DEFERRED_LOADS | SYMOPT_UNDNAME | SYMOPT_LOAD_LINES); return SymInitialize(GetCurrentProcess(), 0, TRUE); diff --git a/lib/sanitizer_common/sanitizer_syscall_generic.inc b/lib/sanitizer_common/sanitizer_syscall_generic.inc index 88d237f4e3ce..15cf05f06087 100644 --- a/lib/sanitizer_common/sanitizer_syscall_generic.inc +++ b/lib/sanitizer_common/sanitizer_syscall_generic.inc @@ -11,13 +11,13 @@ // //===----------------------------------------------------------------------===// -#if SANITIZER_FREEBSD +#if SANITIZER_FREEBSD || SANITIZER_MAC # define SYSCALL(name) SYS_ ## name #else # define SYSCALL(name) __NR_ ## name #endif -#if SANITIZER_FREEBSD && defined(__x86_64__) +#if (SANITIZER_FREEBSD || SANITIZER_MAC) && defined(__x86_64__) # define internal_syscall __syscall # else # define internal_syscall syscall diff --git a/lib/sanitizer_common/sanitizer_win.cc b/lib/sanitizer_common/sanitizer_win.cc index edd4bd11b369..335cecabe118 100644 --- a/lib/sanitizer_common/sanitizer_win.cc +++ b/lib/sanitizer_common/sanitizer_win.cc @@ -219,6 +219,7 @@ int CompareModulesBase(const void *pl, const void *pr) { } } // namespace +#ifndef SANITIZER_GO void DumpProcessMap() { Report("Dumping process modules:\n"); HANDLE cur_process = GetCurrentProcess(); @@ -263,8 +264,8 @@ void DumpProcessMap() { for (size_t i = 0; i < num_modules; ++i) { const ModuleInfo &mi = modules[i]; char module_name[MAX_PATH]; - bool got_module_name = GetModuleFileNameEx( - cur_process, mi.handle, module_name, sizeof(module_name)); + bool got_module_name = GetModuleFileNameA( + mi.handle, module_name, sizeof(module_name)); if (mi.end_address != 0) { Printf("\t%p-%p %s\n", mi.base_address, mi.end_address, got_module_name ? module_name : "[no name]"); @@ -276,6 +277,7 @@ void DumpProcessMap() { } UnmapOrDie(modules, num_modules * sizeof(ModuleInfo)); } +#endif void DisableCoreDumperIfNecessary() { // Do nothing. diff --git a/lib/sanitizer_common/tests/sanitizer_linux_test.cc b/lib/sanitizer_common/tests/sanitizer_linux_test.cc index 592d9c3eeaf5..11342b775cc7 100644 --- a/lib/sanitizer_common/tests/sanitizer_linux_test.cc +++ b/lib/sanitizer_common/tests/sanitizer_linux_test.cc @@ -255,6 +255,14 @@ TEST(SanitizerCommon, LibraryNameIs) { } } +#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/lib/sanitizer_common/tests/sanitizer_suppressions_test.cc b/lib/sanitizer_common/tests/sanitizer_suppressions_test.cc index 0699243283df..e8c30d07e78c 100644 --- a/lib/sanitizer_common/tests/sanitizer_suppressions_test.cc +++ b/lib/sanitizer_common/tests/sanitizer_suppressions_test.cc @@ -58,117 +58,77 @@ TEST(Suppressions, Match) { EXPECT_FALSE(MyMatch("foo$^bar", "foobar")); } -TEST(Suppressions, TypeStrings) { - CHECK(!internal_strcmp(SuppressionTypeString(SuppressionNone), "none")); - CHECK(!internal_strcmp(SuppressionTypeString(SuppressionRace), "race")); - CHECK(!internal_strcmp(SuppressionTypeString(SuppressionMutex), "mutex")); - CHECK(!internal_strcmp(SuppressionTypeString(SuppressionThread), "thread")); - CHECK(!internal_strcmp(SuppressionTypeString(SuppressionSignal), "signal")); - CHECK(!internal_strcmp(SuppressionTypeString(SuppressionLeak), "leak")); - CHECK(!internal_strcmp(SuppressionTypeString(SuppressionLib), - "called_from_lib")); - CHECK( - !internal_strcmp(SuppressionTypeString(SuppressionDeadlock), "deadlock")); - CHECK(!internal_strcmp(SuppressionTypeString(SuppressionVptrCheck), - "vptr_check")); - CHECK(!internal_strcmp(SuppressionTypeString(SuppressionInterceptorName), - "interceptor_name")); - CHECK( - !internal_strcmp(SuppressionTypeString(SuppressionInterceptorViaFunction), - "interceptor_via_fun")); - CHECK( - !internal_strcmp(SuppressionTypeString(SuppressionInterceptorViaLibrary), - "interceptor_via_lib")); - // Ensure this test is up-to-date when suppression types are added. - CHECK_EQ(12, SuppressionTypeCount); -} +static const char *kTestSuppressionTypes[] = {"race", "thread", "mutex", + "signal"}; class SuppressionContextTest : public ::testing::Test { public: - virtual void SetUp() { ctx_ = new(placeholder_) SuppressionContext; } - virtual void TearDown() { ctx_->~SuppressionContext(); } + SuppressionContextTest() + : ctx_(kTestSuppressionTypes, ARRAY_SIZE(kTestSuppressionTypes)) {} protected: - InternalMmapVector<Suppression> *Suppressions() { - return &ctx_->suppressions_; + 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); + } } - SuppressionContext *ctx_; - ALIGNED(64) char placeholder_[sizeof(SuppressionContext)]; }; TEST_F(SuppressionContextTest, Parse) { - ctx_->Parse( - "race:foo\n" - " race:bar\n" // NOLINT - "race:baz \n" // NOLINT - "# a comment\n" - "race:quz\n" - ); // NOLINT - EXPECT_EQ((unsigned)4, ctx_->SuppressionCount()); - EXPECT_EQ((*Suppressions())[3].type, SuppressionRace); - EXPECT_EQ(0, strcmp((*Suppressions())[3].templ, "quz")); - EXPECT_EQ((*Suppressions())[2].type, SuppressionRace); - EXPECT_EQ(0, strcmp((*Suppressions())[2].templ, "baz")); - EXPECT_EQ((*Suppressions())[1].type, SuppressionRace); - EXPECT_EQ(0, strcmp((*Suppressions())[1].templ, "bar")); - EXPECT_EQ((*Suppressions())[0].type, SuppressionRace); - EXPECT_EQ(0, strcmp((*Suppressions())[0].templ, "foo")); + 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( + ctx_.Parse( " # first line comment\n" // NOLINT " race:bar \n" // NOLINT "race:baz* *baz\n" "# a comment\n" "# last line comment\n" ); // NOLINT - EXPECT_EQ((unsigned)2, ctx_->SuppressionCount()); - EXPECT_EQ((*Suppressions())[1].type, SuppressionRace); - EXPECT_EQ(0, strcmp((*Suppressions())[1].templ, "baz* *baz")); - EXPECT_EQ((*Suppressions())[0].type, SuppressionRace); - EXPECT_EQ(0, strcmp((*Suppressions())[0].templ, "bar")); + CheckSuppressions(2, {"race", "race"}, {"bar", "baz* *baz"}); } TEST_F(SuppressionContextTest, Parse3) { - ctx_->Parse( + ctx_.Parse( "# last suppression w/o line-feed\n" "race:foo\n" "race:bar" ); // NOLINT - EXPECT_EQ((unsigned)2, ctx_->SuppressionCount()); - EXPECT_EQ((*Suppressions())[1].type, SuppressionRace); - EXPECT_EQ(0, strcmp((*Suppressions())[1].templ, "bar")); - EXPECT_EQ((*Suppressions())[0].type, SuppressionRace); - EXPECT_EQ(0, strcmp((*Suppressions())[0].templ, "foo")); + CheckSuppressions(2, {"race", "race"}, {"foo", "bar"}); } TEST_F(SuppressionContextTest, ParseType) { - ctx_->Parse( + ctx_.Parse( "race:foo\n" "thread:bar\n" "mutex:baz\n" "signal:quz\n" ); // NOLINT - EXPECT_EQ((unsigned)4, ctx_->SuppressionCount()); - EXPECT_EQ((*Suppressions())[3].type, SuppressionSignal); - EXPECT_EQ(0, strcmp((*Suppressions())[3].templ, "quz")); - EXPECT_EQ((*Suppressions())[2].type, SuppressionMutex); - EXPECT_EQ(0, strcmp((*Suppressions())[2].templ, "baz")); - EXPECT_EQ((*Suppressions())[1].type, SuppressionThread); - EXPECT_EQ(0, strcmp((*Suppressions())[1].templ, "bar")); - EXPECT_EQ((*Suppressions())[0].type, SuppressionRace); - EXPECT_EQ(0, strcmp((*Suppressions())[0].templ, "foo")); + CheckSuppressions(4, {"race", "thread", "mutex", "signal"}, + {"foo", "bar", "baz", "quz"}); } TEST_F(SuppressionContextTest, HasSuppressionType) { - ctx_->Parse( + ctx_.Parse( "race:foo\n" "thread:bar\n"); - EXPECT_TRUE(ctx_->HasSuppressionType(SuppressionRace)); - EXPECT_TRUE(ctx_->HasSuppressionType(SuppressionThread)); - EXPECT_FALSE(ctx_->HasSuppressionType(SuppressionMutex)); - EXPECT_FALSE(ctx_->HasSuppressionType(SuppressionSignal)); + 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/lib/tsan/CMakeLists.txt b/lib/tsan/CMakeLists.txt index baf07a034e3b..9f6d63041a68 100644 --- a/lib/tsan/CMakeLists.txt +++ b/lib/tsan/CMakeLists.txt @@ -79,35 +79,40 @@ set(TSAN_HEADERS set(TSAN_RUNTIME_LIBRARIES) add_custom_target(tsan) # TSan is currently supported on 64-bit Linux only. -if(CAN_TARGET_x86_64 AND UNIX AND NOT APPLE) - set(TSAN_ASM_SOURCES rtl/tsan_rtl_amd64.S) - # Pass ASM file directly to the C++ compiler. - set_source_files_properties(${TSAN_ASM_SOURCES} PROPERTIES - LANGUAGE C) - set(arch "x86_64") - add_compiler_rt_runtime(clang_rt.tsan-${arch} ${arch} STATIC - SOURCES ${TSAN_SOURCES} ${TSAN_ASM_SOURCES} - $<TARGET_OBJECTS:RTInterception.${arch}> - $<TARGET_OBJECTS:RTSanitizerCommon.${arch}> - $<TARGET_OBJECTS:RTSanitizerCommonLibc.${arch}> - CFLAGS ${TSAN_RTL_CFLAGS} - DEFS ${TSAN_COMMON_DEFINITIONS}) - list(APPEND TSAN_RUNTIME_LIBRARIES clang_rt.tsan-${arch}) - add_sanitizer_rt_symbols(clang_rt.tsan-${arch} rtl/tsan.syms.extra) - add_dependencies(tsan clang_rt.tsan-${arch} - clang_rt.tsan-${arch}-symbols) +if(UNIX AND NOT APPLE) + foreach(arch ${TSAN_SUPPORTED_ARCH}) + if(arch STREQUAL "x86_64") + set(TSAN_ASM_SOURCES rtl/tsan_rtl_amd64.S) + # Pass ASM file directly to the C++ compiler. + set_source_files_properties(${TSAN_ASM_SOURCES} PROPERTIES + LANGUAGE C) + # Sanity check for Go runtime. + set(BUILDGO_SCRIPT ${CMAKE_CURRENT_SOURCE_DIR}/go/buildgo.sh) + add_custom_target(GotsanRuntimeCheck + COMMAND CC=${CMAKE_C_COMPILER} IN_TMPDIR=1 SILENT=1 ${BUILDGO_SCRIPT} + DEPENDS clang_rt.tsan-${arch} ${BUILDGO_SCRIPT} + WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/go + COMMENT "Checking TSan Go runtime..." + VERBATIM) + else() + set(TSAN_ASM_SOURCES) + endif() + add_compiler_rt_runtime(clang_rt.tsan-${arch} ${arch} STATIC + SOURCES ${TSAN_SOURCES} ${TSAN_ASM_SOURCES} + $<TARGET_OBJECTS:RTInterception.${arch}> + $<TARGET_OBJECTS:RTSanitizerCommon.${arch}> + $<TARGET_OBJECTS:RTSanitizerCommonLibc.${arch}> + CFLAGS ${TSAN_RTL_CFLAGS} + DEFS ${TSAN_COMMON_DEFINITIONS}) + list(APPEND TSAN_RUNTIME_LIBRARIES clang_rt.tsan-${arch}) + add_sanitizer_rt_symbols(clang_rt.tsan-${arch} rtl/tsan.syms.extra) + add_dependencies(tsan clang_rt.tsan-${arch} + clang_rt.tsan-${arch}-symbols) + endforeach() endif() add_dependencies(compiler-rt tsan) -# Sanity check for Go runtime. -set(BUILDGO_SCRIPT ${CMAKE_CURRENT_SOURCE_DIR}/go/buildgo.sh) -add_custom_target(GotsanRuntimeCheck - COMMAND CC=${CMAKE_C_COMPILER} IN_TMPDIR=1 SILENT=1 ${BUILDGO_SCRIPT} - DEPENDS clang_rt.tsan-${arch} ${BUILDGO_SCRIPT} - WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/go - COMMENT "Checking TSan Go runtime..." - VERBATIM) # Build libcxx instrumented with TSan. if(TSAN_SUPPORTED_ARCH AND diff --git a/lib/tsan/Makefile.mk b/lib/tsan/Makefile.mk deleted file mode 100644 index 70fb610bdc61..000000000000 --- a/lib/tsan/Makefile.mk +++ /dev/null @@ -1,18 +0,0 @@ -#===- lib/tsan/Makefile.mk ---------------------------------*- Makefile -*--===# -# -# The LLVM Compiler Infrastructure -# -# This file is distributed under the University of Illinois Open Source -# License. See LICENSE.TXT for details. -# -#===------------------------------------------------------------------------===# - -ModuleName := tsan -SubDirs := rtl -Sources := -ObjNames := -Dependencies := - -Implementation := Generic - -TsanFunctions := diff --git a/lib/tsan/dd/dd_rtl.cc b/lib/tsan/dd/dd_rtl.cc index 2ba1ee324111..99b8ee597cd1 100644 --- a/lib/tsan/dd/dd_rtl.cc +++ b/lib/tsan/dd/dd_rtl.cc @@ -65,8 +65,8 @@ u32 Callback::Unwind() { return CurrentStackTrace(thr, 3); } -void InitializeFlags(Flags *f, const char *env) { - internal_memset(f, 0, sizeof(*f)); +static void InitializeFlags() { + Flags *f = flags(); // Default values. f->second_deadlock_stack = false; @@ -84,7 +84,7 @@ void InitializeFlags(Flags *f, const char *env) { FlagParser parser; RegisterFlag(&parser, "second_deadlock_stack", "", &f->second_deadlock_stack); RegisterCommonFlags(&parser); - parser.ParseString(env); + parser.ParseString(GetEnv("DSAN_OPTIONS")); SetVerbosity(common_flags()->verbosity); } @@ -93,7 +93,7 @@ void Initialize() { ctx = new(ctx_mem) Context(); InitializeInterceptors(); - InitializeFlags(flags(), GetEnv("DSAN_OPTIONS")); + InitializeFlags(); ctx->dd = DDetector::Create(flags()); } diff --git a/lib/tsan/go/build.bat b/lib/tsan/go/build.bat index 9b350a2d633e..7d393dc0e025 100644 --- a/lib/tsan/go/build.bat +++ b/lib/tsan/go/build.bat @@ -1,4 +1,4 @@ -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 > gotsan.cc +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 -DSANITIZER_DEBUG=0 -O3 -fomit-frame-pointer +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/lib/tsan/go/buildgo.sh b/lib/tsan/go/buildgo.sh index e00408cb10f0..5ac60349e081 100755 --- a/lib/tsan/go/buildgo.sh +++ b/lib/tsan/go/buildgo.sh @@ -45,7 +45,6 @@ if [ "`uname -a | grep Linux`" != "" ]; then ../../sanitizer_common/sanitizer_procmaps_common.cc ../../sanitizer_common/sanitizer_procmaps_linux.cc ../../sanitizer_common/sanitizer_linux.cc - ../../sanitizer_common/sanitizer_linux_libcdep.cc ../../sanitizer_common/sanitizer_stoptheworld_linux_libcdep.cc " elif [ "`uname -a | grep FreeBSD`" != "" ]; then @@ -60,7 +59,6 @@ elif [ "`uname -a | grep FreeBSD`" != "" ]; then ../../sanitizer_common/sanitizer_procmaps_common.cc ../../sanitizer_common/sanitizer_procmaps_freebsd.cc ../../sanitizer_common/sanitizer_linux.cc - ../../sanitizer_common/sanitizer_linux_libcdep.cc ../../sanitizer_common/sanitizer_stoptheworld_linux_libcdep.cc " elif [ "`uname -a | grep Darwin`" != "" ]; then diff --git a/lib/tsan/rtl/Makefile.mk b/lib/tsan/rtl/Makefile.mk deleted file mode 100644 index 2687123f731d..000000000000 --- a/lib/tsan/rtl/Makefile.mk +++ /dev/null @@ -1,25 +0,0 @@ -#===- lib/tsan/rtl/Makefile.mk -----------------------------*- Makefile -*--===# -# -# The LLVM Compiler Infrastructure -# -# This file is distributed under the University of Illinois Open Source -# License. See LICENSE.TXT for details. -# -#===------------------------------------------------------------------------===# - -ModuleName := tsan -SubDirs := - -Sources := $(foreach file,$(wildcard $(Dir)/*.cc),$(notdir $(file))) -AsmSources := $(foreach file,$(wildcard $(Dir)/*.S),$(notdir $(file))) -ObjNames := $(Sources:%.cc=%.o) $(AsmSources:%.S=%.o) - -Implementation := Generic - -# FIXME: use automatic dependencies? -Dependencies := $(wildcard $(Dir)/*.h) -Dependencies += $(wildcard $(Dir)/../../interception/*.h) -Dependencies += $(wildcard $(Dir)/../../sanitizer_common/*.h) - -# Define a convenience variable for all the tsan functions. -TsanFunctions += $(Sources:%.cc=%) $(AsmSources:%.S=%) diff --git a/lib/tsan/rtl/tsan_defs.h b/lib/tsan/rtl/tsan_defs.h index f19aee999332..910a483127d5 100644 --- a/lib/tsan/rtl/tsan_defs.h +++ b/lib/tsan/rtl/tsan_defs.h @@ -18,6 +18,15 @@ #include "sanitizer_common/sanitizer_libc.h" #include "tsan_stat.h" +// Setup defaults for compile definitions. +#ifndef TSAN_NO_HISTORY +# define TSAN_NO_HISTORY 0 +#endif + +#ifndef TSAN_COLLECT_STATS +# define TSAN_COLLECT_STATS 0 +#endif + namespace __tsan { #ifdef SANITIZER_GO @@ -35,7 +44,11 @@ const char *const kTsanOptionsEnv = "TSAN_OPTIONS"; const int kTidBits = 13; const unsigned kMaxTid = 1 << kTidBits; +#ifndef SANITIZER_GO const unsigned kMaxTidInClock = kMaxTid * 2; // This includes msb 'freed' bit. +#else +const unsigned kMaxTidInClock = kMaxTid; // Go does not track freed memory. +#endif const int kClkBits = 42; const unsigned kMaxTidReuse = (1 << (64 - kClkBits)) - 1; const uptr kShadowStackSize = 64 * 1024; @@ -59,18 +72,12 @@ const uptr kMetaShadowCell = 8; // Size of a single meta shadow value (u32). const uptr kMetaShadowSize = 4; -#if defined(TSAN_NO_HISTORY) && TSAN_NO_HISTORY +#if TSAN_NO_HISTORY const bool kCollectHistory = false; #else const bool kCollectHistory = true; #endif -#if defined(TSAN_COLLECT_STATS) && TSAN_COLLECT_STATS -const bool kCollectStats = true; -#else -const bool kCollectStats = false; -#endif - // 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/lib/tsan/rtl/tsan_flags.cc b/lib/tsan/rtl/tsan_flags.cc index fed3de8db2ed..1e81ef3d0003 100644 --- a/lib/tsan/rtl/tsan_flags.cc +++ b/lib/tsan/rtl/tsan_flags.cc @@ -62,7 +62,9 @@ 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 cf.print_suppressions = false; cf.stack_trace_format = " #%n %f %S %M"; OverrideCommonFlags(cf); diff --git a/lib/tsan/rtl/tsan_flags.inc b/lib/tsan/rtl/tsan_flags.inc index 2925f38482a2..e4994685fa0d 100644 --- a/lib/tsan/rtl/tsan_flags.inc +++ b/lib/tsan/rtl/tsan_flags.inc @@ -63,7 +63,7 @@ TSAN_FLAG(bool, stop_on_start, false, TSAN_FLAG(bool, running_on_valgrind, false, "Controls whether RunningOnValgrind() returns true or false.") TSAN_FLAG( - int, history_size, kGoMode ? 1 : 2, // There are a lot of goroutines in Go. + int, history_size, kGoMode ? 1 : 3, // There are a lot of goroutines in Go. "Per-thread history size, controls how many previous memory accesses " "are remembered per thread. Possible values are [0..7]. " "history_size=0 amounts to 32K memory accesses. Each next value doubles " @@ -76,3 +76,4 @@ TSAN_FLAG(int, io_sync, 1, "2 - global synchronization of all IO operations.") TSAN_FLAG(bool, die_after_fork, true, "Die after multi-threaded fork if the child creates new threads.") +TSAN_FLAG(const char *, suppressions, "", "Suppressions file name.") diff --git a/lib/tsan/rtl/tsan_interceptors.cc b/lib/tsan/rtl/tsan_interceptors.cc index 9a6401167bc1..31ff7d56aac7 100644 --- a/lib/tsan/rtl/tsan_interceptors.cc +++ b/lib/tsan/rtl/tsan_interceptors.cc @@ -39,17 +39,27 @@ using namespace __tsan; // NOLINT #define stderr __stderrp #endif +#ifdef __mips__ +const int kSigCount = 129; +#else const int kSigCount = 65; +#endif struct my_siginfo_t { // The size is determined by looking at sizeof of real siginfo_t on linux. u64 opaque[128 / sizeof(u64)]; }; +#ifdef __mips__ +struct ucontext_t { + u64 opaque[768 / sizeof(u64) + 1]; +}; +#else struct ucontext_t { // The size is determined by looking at sizeof of real ucontext_t on linux. u64 opaque[936 / sizeof(u64) + 1]; }; +#endif extern "C" int pthread_attr_init(void *attr); extern "C" int pthread_attr_destroy(void *attr); @@ -89,8 +99,13 @@ const int SIGFPE = 8; const int SIGSEGV = 11; const int SIGPIPE = 13; const int SIGTERM = 15; +#ifdef __mips__ +const int SIGBUS = 10; +const int SIGSYS = 12; +#else const int SIGBUS = 7; const int SIGSYS = 31; +#endif void *const MAP_FAILED = (void*)-1; const int PTHREAD_BARRIER_SERIAL_THREAD = -1; const int MAP_FIXED = 0x10; @@ -108,6 +123,9 @@ typedef void (*sighandler_t)(int sig); typedef void (*sigactionhandler_t)(int sig, my_siginfo_t *siginfo, void *uctx); struct sigaction_t { +#ifdef __mips__ + u32 sa_flags; +#endif union { sighandler_t sa_handler; sigactionhandler_t sa_sigaction; @@ -117,7 +135,9 @@ struct sigaction_t { __sanitizer_sigset_t sa_mask; #else __sanitizer_sigset_t sa_mask; +#ifndef __mips__ int sa_flags; +#endif void (*sa_restorer)(); #endif }; @@ -125,8 +145,13 @@ struct sigaction_t { const sighandler_t SIG_DFL = (sighandler_t)0; const sighandler_t SIG_IGN = (sighandler_t)1; const sighandler_t SIG_ERR = (sighandler_t)-1; +#ifdef __mips__ +const int SA_SIGINFO = 8; +const int SIG_SETMASK = 3; +#else const int SA_SIGINFO = 4; const int SIG_SETMASK = 2; +#endif namespace std { struct nothrow_t {}; @@ -157,7 +182,13 @@ static LibIgnore *libignore() { } void InitializeLibIgnore() { - libignore()->Init(*SuppressionContext::Get()); + const SuppressionContext &supp = *Suppressions(); + const uptr n = supp.SuppressionCount(); + for (uptr i = 0; i < n; i++) { + const Suppression *s = supp.SuppressionAt(i); + if (0 == internal_strcmp(s->type, kSuppressionLib)) + libignore()->AddIgnoredLibrary(s->templ); + } libignore()->OnLibraryLoaded(0); } @@ -1895,9 +1926,9 @@ static void CallUserSignalHandler(ThreadState *thr, bool sync, bool acquire, // signal; and it looks too fragile to intercept all ways to reraise a signal. if (flags()->report_bugs && !sync && sig != SIGTERM && errno != 99) { VarSizeStackTrace stack; - // Add 1 to pc because return address is expected, - // OutputReport() will undo this. - ObtainCurrentStack(thr, pc + 1, &stack); + // StackTrace::GetNestInstructionPc(pc) is used because return address is + // expected, OutputReport() will undo this. + ObtainCurrentStack(thr, StackTrace::GetNextInstructionPc(pc), &stack); ThreadRegistryLock l(ctx->thread_registry); ScopedReport rep(ReportTypeErrnoInSignal); if (!IsFiredSuppression(ctx, rep, stack)) { diff --git a/lib/tsan/rtl/tsan_interface_atomic.cc b/lib/tsan/rtl/tsan_interface_atomic.cc index 9b699511674a..27031991438c 100644 --- a/lib/tsan/rtl/tsan_interface_atomic.cc +++ b/lib/tsan/rtl/tsan_interface_atomic.cc @@ -33,14 +33,14 @@ typedef unsigned short a16; // NOLINT typedef unsigned int a32; typedef unsigned long long a64; // NOLINT #if !defined(SANITIZER_GO) && (defined(__SIZEOF_INT128__) \ - || (__clang_major__ * 100 + __clang_minor__ >= 302)) + || (__clang_major__ * 100 + __clang_minor__ >= 302)) && !defined(__mips64) __extension__ typedef __int128 a128; # define __TSAN_HAS_INT128 1 #else # define __TSAN_HAS_INT128 0 #endif -#ifndef SANITIZER_GO +#if !defined(SANITIZER_GO) && __TSAN_HAS_INT128 // Protects emulation of 128-bit atomic operations. static StaticSpinMutex mutex128; #endif @@ -125,7 +125,8 @@ template<typename T> T func_cas(volatile T *v, T cmp, T xch) { // Atomic ops are executed under tsan internal mutex, // here we assume that the atomic variables are not accessed // from non-instrumented code. -#if !defined(__GCC_HAVE_SYNC_COMPARE_AND_SWAP_16) && !defined(SANITIZER_GO) +#if !defined(__GCC_HAVE_SYNC_COMPARE_AND_SWAP_16) && !defined(SANITIZER_GO) \ + && __TSAN_HAS_INT128 a128 func_xchg(volatile a128 *v, a128 op) { SpinMutexLock lock(&mutex128); a128 cmp = *v; diff --git a/lib/tsan/rtl/tsan_platform.h b/lib/tsan/rtl/tsan_platform.h index 03f95694d26c..135e16027132 100644 --- a/lib/tsan/rtl/tsan_platform.h +++ b/lib/tsan/rtl/tsan_platform.h @@ -26,8 +26,9 @@ namespace __tsan { #if !defined(SANITIZER_GO) +#if defined(__x86_64__) /* -C/C++ on linux and freebsd +C/C++ on linux/x86_64 and freebsd/x86_64 0000 0000 1000 - 0100 0000 0000: main binary and/or MAP_32BIT mappings 0100 0000 0000 - 0200 0000 0000: - 0200 0000 0000 - 1000 0000 0000: shadow @@ -40,7 +41,6 @@ C/C++ on linux and freebsd 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; @@ -55,6 +55,38 @@ const uptr kHiAppMemBeg = 0x7e8000000000ull; const uptr kHiAppMemEnd = 0x800000000000ull; const uptr kAppMemMsk = 0x7c0000000000ull; const uptr kAppMemXor = 0x020000000000ull; +const uptr kVdsoBeg = 0xf000000000000000ull; +#elif defined(__mips64) +/* +C/C++ on linux/mips64 +0100 0000 00 - 0200 0000 00: main binary +0200 0000 00 - 1400 0000 00: - +1400 0000 00 - 2400 0000 00: shadow +2400 0000 00 - 3000 0000 00: - +3000 0000 00 - 4000 0000 00: metainfo (memory blocks and sync objects) +4000 0000 00 - 6000 0000 00: - +6000 0000 00 - 6200 0000 00: traces +6200 0000 00 - fe00 0000 00: - +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) { @@ -171,8 +203,8 @@ static USED uptr UserRegions[] = { 0000 1000 0000 - 00f8 0000 0000: - 00c0 0000 0000 - 00e0 0000 0000: heap 00e0 0000 0000 - 0100 0000 0000: - -0100 0000 0000 - 0380 0000 0000: shadow -0380 0000 0000 - 0560 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: - @@ -183,7 +215,7 @@ const uptr kMetaShadowEnd = 0x07d000000000ull; const uptr kTraceMemBeg = 0x056000000000ull; const uptr kTraceMemEnd = 0x076000000000ull; const uptr kShadowBeg = 0x010000000000ull; -const uptr kShadowEnd = 0x038000000000ull; +const uptr kShadowEnd = 0x050000000000ull; const uptr kAppMemBeg = 0x000000001000ull; const uptr kAppMemEnd = 0x00e000000000ull; @@ -205,21 +237,21 @@ bool IsMetaMem(uptr mem) { ALWAYS_INLINE uptr MemToShadow(uptr x) { DCHECK(IsAppMem(x)); - return ((x & ~(kShadowCell - 1)) * kShadowCnt) | kShadowBeg; + return ((x & ~(kShadowCell - 1)) * kShadowCnt) + kShadowBeg; } ALWAYS_INLINE u32 *MemToMeta(uptr x) { DCHECK(IsAppMem(x)); return (u32*)(((x & ~(kMetaShadowCell - 1)) / \ - kMetaShadowCell * kMetaShadowSize) | kMetaShadowEnd); + kMetaShadowCell * kMetaShadowSize) | kMetaShadowBeg); } ALWAYS_INLINE uptr ShadowToMem(uptr s) { CHECK(IsShadowMem(s)); // FIXME(dvyukov): this is most likely wrong as the mapping is not bijection. - return (x & ~kShadowBeg) / kShadowCnt; + return (s - kShadowBeg) / kShadowCnt; } static USED uptr UserRegions[] = { diff --git a/lib/tsan/rtl/tsan_platform_linux.cc b/lib/tsan/rtl/tsan_platform_linux.cc index 7bc28db296ec..659e8d8a8345 100644 --- a/lib/tsan/rtl/tsan_platform_linux.cc +++ b/lib/tsan/rtl/tsan_platform_linux.cc @@ -66,8 +66,6 @@ namespace __tsan { static uptr g_data_start; static uptr g_data_end; -const uptr kPageSize = 4096; - enum { MemTotal = 0, MemShadow = 1, @@ -173,7 +171,7 @@ static void MapRodata() { *p = kShadowRodata; internal_write(fd, marker.data(), marker.size()); // Map the file into memory. - uptr page = internal_mmap(0, kPageSize, PROT_READ | PROT_WRITE, + uptr page = internal_mmap(0, GetPageSizeCached(), PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, fd, 0); if (internal_iserror(page)) { internal_close(fd); @@ -216,8 +214,15 @@ void InitializeShadowMemory() { // 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. - NoHugePagesInRegion(MemToShadow(0x7f0000000000ULL), - 0x10000000000ULL * kShadowMultiplier); +#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); if (common_flags()->use_madv_dontdump) DontDumpShadowMemory(kShadowBeg, kShadowEnd - kShadowBeg); DPrintf("memory shadow: %zx-%zx (%zuGB)\n", @@ -289,9 +294,9 @@ static void CheckAndProtect() { if (IsAppMem(p)) continue; if (p >= kHeapMemEnd && - p < kHeapMemEnd + PrimaryAllocator::AdditionalSize()) + p < HeapEnd()) continue; - if (p >= 0xf000000000000000ull) // vdso + if (p >= kVdsoBeg) // vdso break; Printf("FATAL: ThreadSanitizer: unexpected memory mapping %p-%p\n", p, end); Die(); @@ -304,7 +309,7 @@ static void CheckAndProtect() { // Protect the whole range for now, so that user does not map something here. ProtectRange(kTraceMemBeg, kTraceMemEnd); ProtectRange(kTraceMemEnd, kHeapMemBeg); - ProtectRange(kHeapMemEnd + PrimaryAllocator::AdditionalSize(), kHiAppMemBeg); + ProtectRange(HeapEnd(), kHiAppMemBeg); } #endif // #ifndef SANITIZER_GO diff --git a/lib/tsan/rtl/tsan_report.cc b/lib/tsan/rtl/tsan_report.cc index c22f12a1bfa5..7e69cb4ecfbc 100644 --- a/lib/tsan/rtl/tsan_report.cc +++ b/lib/tsan/rtl/tsan_report.cc @@ -356,8 +356,9 @@ void PrintStack(const ReportStack *ent) { SymbolizedStack *frame = ent->frames; for (int i = 0; frame; frame = frame->next, i++) { const AddressInfo &info = frame->info; - Printf(" %s()\n %s:%d +0x%zx\n", info.function, info.file, info.line, - (void *)info.module_offset); + Printf(" %s()\n %s:%d +0x%zx\n", info.function, + StripPathPrefix(info.file, common_flags()->strip_path_prefix), + info.line, (void *)info.module_offset); } } diff --git a/lib/tsan/rtl/tsan_rtl.cc b/lib/tsan/rtl/tsan_rtl.cc index b3320aad8038..b76f3e05dde6 100644 --- a/lib/tsan/rtl/tsan_rtl.cc +++ b/lib/tsan/rtl/tsan_rtl.cc @@ -67,8 +67,17 @@ static char thread_registry_placeholder[sizeof(ThreadRegistry)]; static ThreadContextBase *CreateThreadContext(u32 tid) { // Map thread trace when context is created. MapThreadTrace(GetThreadTrace(tid), TraceSize() * sizeof(Event)); - MapThreadTrace(GetThreadTraceHeader(tid), sizeof(Trace)); - new(ThreadTrace(tid)) Trace(); + const uptr hdr = GetThreadTraceHeader(tid); + MapThreadTrace(hdr, sizeof(Trace)); + new((void*)hdr) Trace(); + // We are going to use only a small part of the trace with the default + // value of history_size. However, the constructor writes to the whole trace. + // Unmap the unused part. + uptr hdr_end = hdr + sizeof(Trace); + hdr_end -= sizeof(TraceHeader) * (kTraceParts - TraceParts()); + hdr_end = RoundUp(hdr_end, GetPageSizeCached()); + if (hdr_end < hdr + sizeof(Trace)) + UnmapOrDie((void*)hdr_end, hdr + sizeof(Trace) - hdr_end); void *mem = internal_alloc(MBlockThreadContex, sizeof(ThreadContext)); return new(mem) ThreadContext(tid); } @@ -117,6 +126,7 @@ ThreadState::ThreadState(Context *ctx, int tid, int unique_id, u64 epoch, { } +#ifndef SANITIZER_GO static void MemoryProfiler(Context *ctx, fd_t fd, int i) { uptr n_threads; uptr n_running_threads; @@ -127,13 +137,11 @@ static void MemoryProfiler(Context *ctx, fd_t fd, int i) { } static void BackgroundThread(void *arg) { -#ifndef SANITIZER_GO // This is a non-initialized non-user thread, nothing to see here. // We don't use ScopedIgnoreInterceptors, because we want ignores to be // enabled even when the thread function exits (e.g. during pthread thread // shutdown code). cur_thread()->ignore_interceptors++; -#endif const u64 kMs2Ns = 1000 * 1000; fd_t mprof_fd = kInvalidFd; @@ -191,7 +199,6 @@ static void BackgroundThread(void *arg) { if (mprof_fd != kInvalidFd) MemoryProfiler(ctx, mprof_fd, i); -#ifndef SANITIZER_GO // Flush symbolizer cache if requested. if (flags()->flush_symbolizer_ms > 0) { u64 last = atomic_load(&ctx->last_symbolize_time_ns, @@ -203,7 +210,6 @@ static void BackgroundThread(void *arg) { atomic_store(&ctx->last_symbolize_time_ns, 0, memory_order_relaxed); } } -#endif } } @@ -211,13 +217,14 @@ static void StartBackgroundThread() { ctx->background_thread = internal_start_thread(&BackgroundThread, 0); } -#ifndef SANITIZER_GO +#ifndef __mips__ static void StopBackgroundThread() { atomic_store(&ctx->stop_background_thread, 1, memory_order_relaxed); internal_join_thread(ctx->background_thread); ctx->background_thread = 0; } #endif +#endif void DontNeedShadowFor(uptr addr, uptr size) { uptr shadow_beg = MemToShadow(addr); @@ -282,11 +289,11 @@ static void CheckShadowMapping() { if (p < beg || p >= end) continue; const uptr s = MemToShadow(p); - VPrintf(3, " checking pointer %p -> %p\n", p, s); + const uptr m = (uptr)MemToMeta(p); + VPrintf(3, " checking pointer %p: shadow=%p meta=%p\n", p, s, m); CHECK(IsAppMem(p)); CHECK(IsShadowMem(s)); CHECK_EQ(p & ~(kShadowCell - 1), ShadowToMem(s)); - const uptr m = (uptr)MemToMeta(p); CHECK(IsMetaMem(m)); } } @@ -325,11 +332,14 @@ void Initialize(ThreadState *thr) { #ifndef SANITIZER_GO InitializeLibIgnore(); Symbolizer::GetOrInit()->AddHooks(EnterSymbolizer, ExitSymbolizer); -#endif + // On MIPS, TSan initialization is run before + // __pthread_initialize_minimal_internal() is finished, so we can not spawn + // new threads. +#ifndef __mips__ StartBackgroundThread(); -#ifndef SANITIZER_GO SetSandboxingCallback(StopBackgroundThread); #endif +#endif if (common_flags()->detect_deadlocks) ctx->dd = DDetector::Create(flags()); @@ -394,8 +404,11 @@ int Finalize(ThreadState *thr) { failed = OnFinalize(failed); +#if TSAN_COLLECT_STATS StatAggregate(ctx->stat, thr->stat); StatOutput(ctx->stat); +#endif + return failed ? flags()->exitcode : 0; } @@ -419,7 +432,7 @@ void ForkChildAfter(ThreadState *thr, uptr pc) { VPrintf(1, "ThreadSanitizer: forked new process with pid %d," " parent had %d threads\n", (int)internal_getpid(), (int)nthread); if (nthread == 1) { - internal_start_thread(&BackgroundThread, 0); + StartBackgroundThread(); } else { // We've just forked a multi-threaded process. We cannot reasonably function // after that (some mutexes may be locked before fork). So just enable @@ -826,7 +839,7 @@ static void MemoryRangeSet(ThreadState *thr, uptr pc, uptr addr, uptr size, } } else { // The region is big, reset only beginning and end. - const uptr kPageSize = 4096; + const uptr kPageSize = GetPageSizeCached(); u64 *begin = (u64*)MemToShadow(addr); u64 *end = begin + size / kShadowCell * kShadowCnt; u64 *p = begin; diff --git a/lib/tsan/rtl/tsan_rtl.h b/lib/tsan/rtl/tsan_rtl.h index 768e8307fcc8..7a60e5c55af4 100644 --- a/lib/tsan/rtl/tsan_rtl.h +++ b/lib/tsan/rtl/tsan_rtl.h @@ -54,8 +54,21 @@ namespace __tsan { #ifndef SANITIZER_GO struct MapUnmapCallback; +#ifdef __mips64 +static const uptr kAllocatorSpace = 0; +static const uptr kAllocatorSize = SANITIZER_MMAP_RANGE_SIZE; +static const uptr kAllocatorRegionSizeLog = 20; +static const uptr kAllocatorNumRegions = + kAllocatorSize >> kAllocatorRegionSizeLog; +typedef TwoLevelByteMap<(kAllocatorNumRegions >> 12), 1 << 12, + MapUnmapCallback> ByteMap; +typedef SizeClassAllocator32<kAllocatorSpace, kAllocatorSize, 0, + CompactSizeClassMap, kAllocatorRegionSizeLog, ByteMap, + MapUnmapCallback> PrimaryAllocator; +#else typedef SizeClassAllocator64<kHeapMemBeg, kHeapMemEnd - kHeapMemBeg, 0, DefaultSizeClassMap, MapUnmapCallback> PrimaryAllocator; +#endif typedef SizeClassAllocatorLocalCache<PrimaryAllocator> AllocatorCache; typedef LargeMmapAllocator<MapUnmapCallback> SecondaryAllocator; typedef CombinedAllocator<PrimaryAllocator, AllocatorCache, @@ -351,7 +364,9 @@ struct ThreadState { Vector<JmpBuf> jmp_bufs; int ignore_interceptors; #endif +#if TSAN_COLLECT_STATS u64 stat[StatCnt]; +#endif const int tid; const int unique_id; bool in_symbolizer; @@ -365,7 +380,9 @@ struct ThreadState { const uptr tls_size; ThreadContext *tctx; +#if SANITIZER_DEBUG && !SANITIZER_GO InternalDeadlockDetector internal_deadlock_detector; +#endif DDPhysicalThread *dd_pt; DDLogicalThread *dd_lt; @@ -539,15 +556,20 @@ void ObtainCurrentStack(ThreadState *thr, uptr toppc, StackTraceTy *stack) { } +#if TSAN_COLLECT_STATS void StatAggregate(u64 *dst, u64 *src); void StatOutput(u64 *stat); +#endif + void ALWAYS_INLINE StatInc(ThreadState *thr, StatType typ, u64 n = 1) { - if (kCollectStats) - thr->stat[typ] += n; +#if TSAN_COLLECT_STATS + thr->stat[typ] += n; +#endif } void ALWAYS_INLINE StatSet(ThreadState *thr, StatType typ, u64 n) { - if (kCollectStats) - thr->stat[typ] = n; +#if TSAN_COLLECT_STATS + thr->stat[typ] = n; +#endif } void MapShadow(uptr addr, uptr size); @@ -729,6 +751,16 @@ void ALWAYS_INLINE TraceAddEvent(ThreadState *thr, FastState fs, *evp = ev; } +#ifndef SANITIZER_GO +uptr ALWAYS_INLINE HeapEnd() { +#if SANITIZER_CAN_USE_ALLOCATOR64 + return kHeapMemEnd + PrimaryAllocator::AdditionalSize(); +#else + return kHeapMemEnd; +#endif +} +#endif + } // namespace __tsan #endif // TSAN_RTL_H diff --git a/lib/tsan/rtl/tsan_rtl_report.cc b/lib/tsan/rtl/tsan_rtl_report.cc index d1621454242c..dc9438e6371b 100644 --- a/lib/tsan/rtl/tsan_rtl_report.cc +++ b/lib/tsan/rtl/tsan_rtl_report.cc @@ -112,16 +112,10 @@ static ReportStack *SymbolizeStack(StackTrace trace) { for (uptr si = 0; si < trace.size; si++) { const uptr pc = trace.trace[si]; uptr pc1 = pc; -#ifndef SANITIZER_GO // We obtain the return address, but we're interested in the previous // instruction. if ((pc & kExternalPCBit) == 0) pc1 = StackTrace::GetPreviousInstructionPc(pc); -#else - // FIXME(dvyukov): Go sometimes uses address of a function as top pc. - if (si != trace.size - 1) - pc1 -= 1; -#endif SymbolizedStack *ent = SymbolizeCode(pc1); CHECK_NE(ent, 0); SymbolizedStack *last = ent; diff --git a/lib/tsan/rtl/tsan_rtl_thread.cc b/lib/tsan/rtl/tsan_rtl_thread.cc index e026217ed171..8ed1fbf2edae 100644 --- a/lib/tsan/rtl/tsan_rtl_thread.cc +++ b/lib/tsan/rtl/tsan_rtl_thread.cc @@ -145,7 +145,9 @@ void ThreadContext::OnFinished() { AllocatorThreadFinish(thr); #endif thr->~ThreadState(); +#if TSAN_COLLECT_STATS StatAggregate(ctx->stat, thr->stat); +#endif thr = 0; } @@ -239,6 +241,7 @@ void ThreadStart(ThreadState *thr, int tid, uptr os_id) { uptr stk_size = 0; uptr tls_addr = 0; uptr tls_size = 0; +#ifndef SANITIZER_GO GetThreadStackAndTls(tid == 0, &stk_addr, &stk_size, &tls_addr, &tls_size); if (tid) { @@ -259,6 +262,7 @@ void ThreadStart(ThreadState *thr, int tid, uptr os_id) { thr_end, tls_addr + tls_size - thr_end); } } +#endif ThreadRegistry *tr = ctx->thread_registry; OnStartedArgs args = { thr, stk_addr, stk_size, tls_addr, tls_size }; diff --git a/lib/tsan/rtl/tsan_stat.cc b/lib/tsan/rtl/tsan_stat.cc index 350a2ba48253..15fa43d6f8a1 100644 --- a/lib/tsan/rtl/tsan_stat.cc +++ b/lib/tsan/rtl/tsan_stat.cc @@ -15,17 +15,14 @@ namespace __tsan { +#if TSAN_COLLECT_STATS + void StatAggregate(u64 *dst, u64 *src) { - if (!kCollectStats) - return; for (int i = 0; i < StatCnt; i++) dst[i] += src[i]; } void StatOutput(u64 *stat) { - if (!kCollectStats) - return; - stat[StatShadowNonZero] = stat[StatShadowProcessed] - stat[StatShadowZero]; static const char *name[StatCnt] = {}; @@ -176,4 +173,6 @@ void StatOutput(u64 *stat) { Printf("%s: %16zu\n", name[i], (uptr)stat[i]); } +#endif + } // namespace __tsan diff --git a/lib/tsan/rtl/tsan_suppressions.cc b/lib/tsan/rtl/tsan_suppressions.cc index 299fc80fd262..5413f04af1d2 100644 --- a/lib/tsan/rtl/tsan_suppressions.cc +++ b/lib/tsan/rtl/tsan_suppressions.cc @@ -41,63 +41,74 @@ extern "C" const char *WEAK __tsan_default_suppressions() { namespace __tsan { -static bool suppressions_inited = false; +ALIGNED(64) static char suppression_placeholder[sizeof(SuppressionContext)]; +static SuppressionContext *suppression_ctx = nullptr; +static const char *kSuppressionTypes[] = { + kSuppressionRace, kSuppressionMutex, kSuppressionThread, + kSuppressionSignal, kSuppressionLib, kSuppressionDeadlock}; void InitializeSuppressions() { - CHECK(!suppressions_inited); - SuppressionContext::InitIfNecessary(); + CHECK_EQ(nullptr, suppression_ctx); + suppression_ctx = new (suppression_placeholder) // NOLINT + SuppressionContext(kSuppressionTypes, ARRAY_SIZE(kSuppressionTypes)); + suppression_ctx->ParseFromFile(flags()->suppressions); #ifndef SANITIZER_GO - SuppressionContext::Get()->Parse(__tsan_default_suppressions()); - SuppressionContext::Get()->Parse(std_suppressions); + suppression_ctx->Parse(__tsan_default_suppressions()); + suppression_ctx->Parse(std_suppressions); #endif - suppressions_inited = true; } -SuppressionType conv(ReportType typ) { +SuppressionContext *Suppressions() { + CHECK(suppression_ctx); + return suppression_ctx; +} + +static const char *conv(ReportType typ) { if (typ == ReportTypeRace) - return SuppressionRace; + return kSuppressionRace; else if (typ == ReportTypeVptrRace) - return SuppressionRace; + return kSuppressionRace; else if (typ == ReportTypeUseAfterFree) - return SuppressionRace; + return kSuppressionRace; else if (typ == ReportTypeVptrUseAfterFree) - return SuppressionRace; + return kSuppressionRace; else if (typ == ReportTypeThreadLeak) - return SuppressionThread; + return kSuppressionThread; else if (typ == ReportTypeMutexDestroyLocked) - return SuppressionMutex; + return kSuppressionMutex; else if (typ == ReportTypeMutexDoubleLock) - return SuppressionMutex; + return kSuppressionMutex; else if (typ == ReportTypeMutexBadUnlock) - return SuppressionMutex; + return kSuppressionMutex; else if (typ == ReportTypeMutexBadReadLock) - return SuppressionMutex; + return kSuppressionMutex; else if (typ == ReportTypeMutexBadReadUnlock) - return SuppressionMutex; + return kSuppressionMutex; else if (typ == ReportTypeSignalUnsafe) - return SuppressionSignal; + return kSuppressionSignal; else if (typ == ReportTypeErrnoInSignal) - return SuppressionNone; + return kSuppressionNone; else if (typ == ReportTypeDeadlock) - return SuppressionDeadlock; + return kSuppressionDeadlock; Printf("ThreadSanitizer: unknown report type %d\n", typ), Die(); } uptr IsSuppressed(ReportType typ, const ReportStack *stack, Suppression **sp) { - if (!SuppressionContext::Get()->SuppressionCount() || stack == 0 || + CHECK(suppression_ctx); + if (!suppression_ctx->SuppressionCount() || stack == 0 || !stack->suppressable) return 0; - SuppressionType stype = conv(typ); - if (stype == SuppressionNone) + const char *stype = conv(typ); + if (0 == internal_strcmp(stype, kSuppressionNone)) return 0; Suppression *s; for (const SymbolizedStack *frame = stack->frames; frame; frame = frame->next) { const AddressInfo &info = frame->info; - if (SuppressionContext::Get()->Match(info.function, stype, &s) || - SuppressionContext::Get()->Match(info.file, stype, &s) || - SuppressionContext::Get()->Match(info.module, stype, &s)) { + if (suppression_ctx->Match(info.function, stype, &s) || + suppression_ctx->Match(info.file, stype, &s) || + suppression_ctx->Match(info.module, stype, &s)) { DPrintf("ThreadSanitizer: matched suppression '%s'\n", s->templ); s->hit_count++; *sp = s; @@ -108,16 +119,17 @@ uptr IsSuppressed(ReportType typ, const ReportStack *stack, Suppression **sp) { } uptr IsSuppressed(ReportType typ, const ReportLocation *loc, Suppression **sp) { - if (!SuppressionContext::Get()->SuppressionCount() || loc == 0 || + CHECK(suppression_ctx); + if (!suppression_ctx->SuppressionCount() || loc == 0 || loc->type != ReportLocationGlobal || !loc->suppressable) return 0; - SuppressionType stype = conv(typ); - if (stype == SuppressionNone) + const char *stype = conv(typ); + if (0 == internal_strcmp(stype, kSuppressionNone)) return 0; Suppression *s; const DataInfo &global = loc->global; - if (SuppressionContext::Get()->Match(global.name, stype, &s) || - SuppressionContext::Get()->Match(global.module, stype, &s)) { + 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++; *sp = s; @@ -128,7 +140,8 @@ uptr IsSuppressed(ReportType typ, const ReportLocation *loc, Suppression **sp) { void PrintMatchedSuppressions() { InternalMmapVector<Suppression *> matched(1); - SuppressionContext::Get()->GetMatched(&matched); + CHECK(suppression_ctx); + suppression_ctx->GetMatched(&matched); if (!matched.size()) return; int hit_count = 0; @@ -137,8 +150,8 @@ void PrintMatchedSuppressions() { Printf("ThreadSanitizer: Matched %d suppressions (pid=%d):\n", hit_count, (int)internal_getpid()); for (uptr i = 0; i < matched.size(); i++) { - Printf("%d %s:%s\n", matched[i]->hit_count, - SuppressionTypeString(matched[i]->type), matched[i]->templ); + Printf("%d %s:%s\n", matched[i]->hit_count, matched[i]->type, + matched[i]->templ); } } } // namespace __tsan diff --git a/lib/tsan/rtl/tsan_suppressions.h b/lib/tsan/rtl/tsan_suppressions.h index c618b3db4c2d..e6d279c33a4e 100644 --- a/lib/tsan/rtl/tsan_suppressions.h +++ b/lib/tsan/rtl/tsan_suppressions.h @@ -18,7 +18,16 @@ namespace __tsan { +const char kSuppressionNone[] = "none"; +const char kSuppressionRace[] = "race"; +const char kSuppressionMutex[] = "mutex"; +const char kSuppressionThread[] = "thread"; +const char kSuppressionSignal[] = "signal"; +const char kSuppressionLib[] = "called_from_lib"; +const char kSuppressionDeadlock[] = "deadlock"; + void InitializeSuppressions(); +SuppressionContext *Suppressions(); void PrintMatchedSuppressions(); uptr IsSuppressed(ReportType typ, const ReportStack *stack, Suppression **sp); uptr IsSuppressed(ReportType typ, const ReportLocation *loc, Suppression **sp); diff --git a/lib/tsan/rtl/tsan_trace.h b/lib/tsan/rtl/tsan_trace.h index 1da8752f6fa7..2569c7e42a47 100644 --- a/lib/tsan/rtl/tsan_trace.h +++ b/lib/tsan/rtl/tsan_trace.h @@ -20,9 +20,9 @@ namespace __tsan { -const int kTracePartSizeBits = 14; +const int kTracePartSizeBits = 13; const int kTracePartSize = 1 << kTracePartSizeBits; -const int kTraceParts = 4 * 1024 * 1024 / kTracePartSize; +const int kTraceParts = 2 * 1024 * 1024 / kTracePartSize; const int kTraceSize = kTracePartSize * kTraceParts; // Must fit into 3 bits. @@ -54,13 +54,15 @@ struct TraceHeader { }; struct Trace { - TraceHeader headers[kTraceParts]; Mutex mtx; #ifndef SANITIZER_GO // Must be last to catch overflow as paging fault. // Go shadow stack is dynamically allocated. uptr shadow_stack[kShadowStackSize]; #endif + // Must be the last field, because we unmap the unused part in + // CreateThreadContext. + TraceHeader headers[kTraceParts]; Trace() : mtx(MutexTypeTrace, StatMtxTrace) { diff --git a/lib/tsan/tests/CMakeLists.txt b/lib/tsan/tests/CMakeLists.txt index cb7e5374ec8a..e0c3f8a1a6d8 100644 --- a/lib/tsan/tests/CMakeLists.txt +++ b/lib/tsan/tests/CMakeLists.txt @@ -34,26 +34,28 @@ endmacro() macro(add_tsan_unittest testname) # Build unit tests only for 64-bit Linux. - if(UNIX AND NOT APPLE AND CAN_TARGET_x86_64) - parse_arguments(TEST "SOURCES;HEADERS" "" ${ARGN}) - set(TEST_OBJECTS) - foreach(SOURCE ${TEST_SOURCES} ${COMPILER_RT_GTEST_SOURCE}) - tsan_compile(TEST_OBJECTS ${SOURCE} x86_64 ${TEST_HEADERS}) + if(UNIX AND NOT APPLE) + foreach(arch ${TSAN_SUPPORTED_ARCH}) + parse_arguments(TEST "SOURCES;HEADERS" "" ${ARGN}) + set(TEST_OBJECTS) + foreach(SOURCE ${TEST_SOURCES} ${COMPILER_RT_GTEST_SOURCE}) + tsan_compile(TEST_OBJECTS ${SOURCE} ${arch} ${TEST_HEADERS}) + endforeach() + get_target_flags_for_arch(${arch} TARGET_LINK_FLAGS) + set(TEST_DEPS ${TEST_OBJECTS}) + if(NOT COMPILER_RT_STANDALONE_BUILD) + list(APPEND TEST_DEPS tsan) + endif() + # FIXME: Looks like we should link TSan with just-built runtime, + # and not rely on -fsanitize=thread, as these tests are essentially + # unit tests. + add_compiler_rt_test(TsanUnitTests ${testname} + OBJECTS ${TEST_OBJECTS} + DEPS ${TEST_DEPS} + LINK_FLAGS ${TARGET_LINK_FLAGS} + -fsanitize=thread + -lstdc++ -lm) endforeach() - get_target_flags_for_arch(${arch} TARGET_LINK_FLAGS) - set(TEST_DEPS ${TEST_OBJECTS}) - if(NOT COMPILER_RT_STANDALONE_BUILD) - list(APPEND TEST_DEPS tsan) - endif() - # FIXME: Looks like we should link TSan with just-built runtime, - # and not rely on -fsanitize=thread, as these tests are essentially - # unit tests. - add_compiler_rt_test(TsanUnitTests ${testname} - OBJECTS ${TEST_OBJECTS} - DEPS ${TEST_DEPS} - LINK_FLAGS ${TARGET_LINK_FLAGS} - -fsanitize=thread - -lstdc++ -lm) endif() endmacro() diff --git a/lib/ubsan/CMakeLists.txt b/lib/ubsan/CMakeLists.txt index 541138044654..09c7a851e075 100644 --- a/lib/ubsan/CMakeLists.txt +++ b/lib/ubsan/CMakeLists.txt @@ -17,12 +17,7 @@ include_directories(..) set(UBSAN_CFLAGS ${SANITIZER_COMMON_CFLAGS}) append_no_rtti_flag(UBSAN_CFLAGS) -append_list_if(COMPILER_RT_HAS_WGLOBAL_CONSTRUCTORS_FLAG -Wglobal-constructors - UBSAN_CFLAGS) - set(UBSAN_CXXFLAGS ${SANITIZER_COMMON_CFLAGS}) -append_list_if(COMPILER_RT_HAS_WGLOBAL_CONSTRUCTORS_FLAG -Wglobal-constructors - UBSAN_CXXFLAGS) add_custom_target(ubsan) diff --git a/lib/ubsan/ubsan_diag.cc b/lib/ubsan/ubsan_diag.cc index 76ce2bd39996..4f2a2a9f3562 100644 --- a/lib/ubsan/ubsan_diag.cc +++ b/lib/ubsan/ubsan_diag.cc @@ -14,9 +14,11 @@ #include "ubsan_diag.h" #include "ubsan_init.h" #include "ubsan_flags.h" +#include "sanitizer_common/sanitizer_placement_new.h" #include "sanitizer_common/sanitizer_report_decorator.h" #include "sanitizer_common/sanitizer_stacktrace.h" #include "sanitizer_common/sanitizer_stacktrace_printer.h" +#include "sanitizer_common/sanitizer_suppressions.h" #include "sanitizer_common/sanitizer_symbolizer.h" #include <stdio.h> @@ -66,39 +68,9 @@ class Decorator : public SanitizerCommonDecorator { }; } -Location __ubsan::getCallerLocation(uptr CallerLoc) { - if (!CallerLoc) - return Location(); - - uptr Loc = StackTrace::GetPreviousInstructionPc(CallerLoc); - return getFunctionLocation(Loc, 0); -} - -Location __ubsan::getFunctionLocation(uptr Loc, const char **FName) { - if (!Loc) - return Location(); +SymbolizedStack *__ubsan::getSymbolizedLocation(uptr PC) { InitIfNecessary(); - - SymbolizedStack *Frames = Symbolizer::GetOrInit()->SymbolizePC(Loc); - const AddressInfo &Info = Frames->info; - - if (!Info.module) { - Frames->ClearAll(); - return Location(Loc); - } - - if (FName && Info.function) - *FName = internal_strdup(Info.function); - - if (!Info.file) { - ModuleLocation MLoc(internal_strdup(Info.module), Info.module_offset); - Frames->ClearAll(); - return MLoc; - } - - SourceLocation SLoc(internal_strdup(Info.file), Info.line, Info.column); - Frames->ClearAll(); - return SLoc; + return Symbolizer::GetOrInit()->SymbolizePC(PC); } Diag &Diag::operator<<(const TypeDescriptor &V) { @@ -142,15 +114,22 @@ static void renderLocation(Location Loc) { SLoc.getColumn(), common_flags()->strip_path_prefix); break; } - case Location::LK_Module: { - ModuleLocation MLoc = Loc.getModuleLocation(); - RenderModuleLocation(&LocBuffer, MLoc.getModuleName(), MLoc.getOffset(), - common_flags()->strip_path_prefix); - break; - } case Location::LK_Memory: LocBuffer.append("%p", Loc.getMemoryLocation()); break; + case Location::LK_Symbolized: { + const AddressInfo &Info = Loc.getSymbolizedStack()->info; + if (Info.file) { + RenderSourceLocation(&LocBuffer, Info.file, Info.line, Info.column, + common_flags()->strip_path_prefix); + } else if (Info.module) { + RenderModuleLocation(&LocBuffer, Info.module, Info.module_offset, + common_flags()->strip_path_prefix); + } else { + LocBuffer.append("%p", Info.address); + } + break; + } case Location::LK_Null: LocBuffer.append("<unknown>"); break; @@ -356,11 +335,24 @@ ScopedReport::~ScopedReport() { Die(); } -bool __ubsan::MatchSuppression(const char *Str, SuppressionType Type) { - Suppression *s; +ALIGNED(64) static char suppression_placeholder[sizeof(SuppressionContext)]; +static SuppressionContext *suppression_ctx = nullptr; +static const char kVptrCheck[] = "vptr_check"; +static const char *kSuppressionTypes[] = { kVptrCheck }; + +void __ubsan::InitializeSuppressions() { + CHECK_EQ(nullptr, suppression_ctx); + suppression_ctx = new (suppression_placeholder) // NOLINT + SuppressionContext(kSuppressionTypes, ARRAY_SIZE(kSuppressionTypes)); + suppression_ctx->ParseFromFile(flags()->suppressions); +} + +bool __ubsan::IsVptrCheckSuppressed(const char *TypeName) { // If .preinit_array is not used, it is possible that the UBSan runtime is not // initialized. if (!SANITIZER_CAN_USE_PREINIT_ARRAY) InitIfNecessary(); - return SuppressionContext::Get()->Match(Str, Type, &s); + CHECK(suppression_ctx); + Suppression *s; + return suppression_ctx->Match(TypeName, kVptrCheck, &s); } diff --git a/lib/ubsan/ubsan_diag.h b/lib/ubsan/ubsan_diag.h index 296ec0d3f61a..44dca90b7a53 100644 --- a/lib/ubsan/ubsan_diag.h +++ b/lib/ubsan/ubsan_diag.h @@ -15,79 +15,84 @@ #include "ubsan_value.h" #include "sanitizer_common/sanitizer_stacktrace.h" -#include "sanitizer_common/sanitizer_suppressions.h" +#include "sanitizer_common/sanitizer_symbolizer.h" namespace __ubsan { -/// \brief A location within a loaded module in the program. These are used when -/// the location can't be resolved to a SourceLocation. -class ModuleLocation { - const char *ModuleName; - uptr Offset; +class SymbolizedStackHolder { + SymbolizedStack *Stack; + + void clear() { + if (Stack) + Stack->ClearAll(); + } public: - ModuleLocation() : ModuleName(0), Offset(0) {} - ModuleLocation(const char *ModuleName, uptr Offset) - : ModuleName(ModuleName), Offset(Offset) {} - const char *getModuleName() const { return ModuleName; } - uptr getOffset() const { return Offset; } + explicit SymbolizedStackHolder(SymbolizedStack *Stack = nullptr) + : Stack(Stack) {} + ~SymbolizedStackHolder() { clear(); } + void reset(SymbolizedStack *S) { + if (Stack != S) + clear(); + Stack = S; + } + const SymbolizedStack *get() const { return Stack; } }; +SymbolizedStack *getSymbolizedLocation(uptr PC); + +inline SymbolizedStack *getCallerLocation(uptr CallerPC) { + CHECK(CallerPC); + uptr PC = StackTrace::GetPreviousInstructionPc(CallerPC); + return getSymbolizedLocation(PC); +} + /// A location of some data within the program's address space. typedef uptr MemoryLocation; /// \brief Location at which a diagnostic can be emitted. Either a -/// SourceLocation, a ModuleLocation, or a MemoryLocation. +/// SourceLocation, a MemoryLocation, or a SymbolizedStack. class Location { public: - enum LocationKind { LK_Null, LK_Source, LK_Module, LK_Memory }; + enum LocationKind { LK_Null, LK_Source, LK_Memory, LK_Symbolized }; private: LocationKind Kind; // FIXME: In C++11, wrap these in an anonymous union. SourceLocation SourceLoc; - ModuleLocation ModuleLoc; MemoryLocation MemoryLoc; + const SymbolizedStack *SymbolizedLoc; // Not owned. public: Location() : Kind(LK_Null) {} Location(SourceLocation Loc) : Kind(LK_Source), SourceLoc(Loc) {} - Location(ModuleLocation Loc) : - Kind(LK_Module), ModuleLoc(Loc) {} Location(MemoryLocation Loc) : Kind(LK_Memory), MemoryLoc(Loc) {} + // SymbolizedStackHolder must outlive Location object. + Location(const SymbolizedStackHolder &Stack) : + Kind(LK_Symbolized), SymbolizedLoc(Stack.get()) {} LocationKind getKind() const { return Kind; } bool isSourceLocation() const { return Kind == LK_Source; } - bool isModuleLocation() const { return Kind == LK_Module; } bool isMemoryLocation() const { return Kind == LK_Memory; } + bool isSymbolizedStack() const { return Kind == LK_Symbolized; } SourceLocation getSourceLocation() const { CHECK(isSourceLocation()); return SourceLoc; } - ModuleLocation getModuleLocation() const { - CHECK(isModuleLocation()); - return ModuleLoc; - } MemoryLocation getMemoryLocation() const { CHECK(isMemoryLocation()); return MemoryLoc; } + const SymbolizedStack *getSymbolizedStack() const { + CHECK(isSymbolizedStack()); + return SymbolizedLoc; + } }; -/// Try to obtain a location for the caller. This might fail, and produce either -/// an invalid location or a module location for the caller. -Location getCallerLocation(uptr CallerLoc = GET_CALLER_PC()); - -/// Try to obtain a location for the given function pointer. This might fail, -/// and produce either an invalid location or a module location for the caller. -/// If FName is non-null and the name of the function is known, set *FName to -/// the function name, otherwise *FName is unchanged. -Location getFunctionLocation(uptr Loc, const char **FName); - /// A diagnostic severity level. enum DiagLevel { DL_Error, ///< An error. @@ -230,7 +235,8 @@ public: ~ScopedReport(); }; -bool MatchSuppression(const char *Str, SuppressionType Type); +void InitializeSuppressions(); +bool IsVptrCheckSuppressed(const char *TypeName); } // namespace __ubsan diff --git a/lib/ubsan/ubsan_flags.inc b/lib/ubsan/ubsan_flags.inc index 3260e8e13df8..9ca31d13a9b6 100644 --- a/lib/ubsan/ubsan_flags.inc +++ b/lib/ubsan/ubsan_flags.inc @@ -21,4 +21,5 @@ UBSAN_FLAG(bool, halt_on_error, false, "Crash the program after printing the first error report") UBSAN_FLAG(bool, print_stacktrace, false, "Include full stacktrace into an error report") +UBSAN_FLAG(const char *, suppressions, "", "Suppressions file name.") diff --git a/lib/ubsan/ubsan_handlers.cc b/lib/ubsan/ubsan_handlers.cc index a0ecff943592..78e7508f7f93 100644 --- a/lib/ubsan/ubsan_handlers.cc +++ b/lib/ubsan/ubsan_handlers.cc @@ -37,14 +37,17 @@ const char *TypeCheckKinds[] = { } static void handleTypeMismatchImpl(TypeMismatchData *Data, ValueHandle Pointer, - Location FallbackLoc, ReportOptions Opts) { + ReportOptions Opts) { Location Loc = Data->Loc.acquire(); // Use the SourceLocation from Data to track deduplication, even if 'invalid' if (ignoreReport(Loc.getSourceLocation(), Opts)) return; - if (Data->Loc.isInvalid()) + SymbolizedStackHolder FallbackLoc; + if (Data->Loc.isInvalid()) { + FallbackLoc.reset(getCallerLocation(Opts.pc)); Loc = FallbackLoc; + } ScopedReport R(Opts, Loc); @@ -67,12 +70,12 @@ static void handleTypeMismatchImpl(TypeMismatchData *Data, ValueHandle Pointer, void __ubsan::__ubsan_handle_type_mismatch(TypeMismatchData *Data, ValueHandle Pointer) { GET_REPORT_OPTIONS(false); - handleTypeMismatchImpl(Data, Pointer, getCallerLocation(), Opts); + handleTypeMismatchImpl(Data, Pointer, Opts); } void __ubsan::__ubsan_handle_type_mismatch_abort(TypeMismatchData *Data, ValueHandle Pointer) { GET_REPORT_OPTIONS(true); - handleTypeMismatchImpl(Data, Pointer, getCallerLocation(), Opts); + handleTypeMismatchImpl(Data, Pointer, Opts); Die(); } @@ -288,7 +291,8 @@ void __ubsan::__ubsan_handle_vla_bound_not_positive_abort(VLABoundData *Data, static void handleFloatCastOverflow(FloatCastOverflowData *Data, ValueHandle From, ReportOptions Opts) { // TODO: Add deduplication once a SourceLocation is generated for this check. - Location Loc = getCallerLocation(); + SymbolizedStackHolder CallerLoc(getCallerLocation(Opts.pc)); + Location Loc = CallerLoc; ScopedReport R(Opts, Loc); Diag(Loc, DL_Error, @@ -337,16 +341,21 @@ void __ubsan::__ubsan_handle_load_invalid_value_abort(InvalidValueData *Data, static void handleFunctionTypeMismatch(FunctionTypeMismatchData *Data, ValueHandle Function, ReportOptions Opts) { - const char *FName = "(unknown)"; + SourceLocation CallLoc = Data->Loc.acquire(); + if (ignoreReport(CallLoc, Opts)) + return; - Location Loc = getFunctionLocation(Function, &FName); + ScopedReport R(Opts, CallLoc); - ScopedReport R(Opts, Loc); + SymbolizedStackHolder FLoc(getSymbolizedLocation(Function)); + const char *FName = FLoc.get()->info.function; + if (!FName) + FName = "(unknown)"; - Diag(Data->Loc, DL_Error, + Diag(CallLoc, DL_Error, "call to function %0 through pointer to incorrect function type %1") - << FName << Data->Type; - Diag(Loc, DL_Note, "%0 defined here") << FName; + << FName << Data->Type; + Diag(FLoc, DL_Note, "%0 defined here") << FName; } void diff --git a/lib/ubsan/ubsan_handlers_cxx.cc b/lib/ubsan/ubsan_handlers_cxx.cc index 5704c1e6342d..4718e6eacee2 100644 --- a/lib/ubsan/ubsan_handlers_cxx.cc +++ b/lib/ubsan/ubsan_handlers_cxx.cc @@ -36,8 +36,7 @@ static void HandleDynamicTypeCacheMiss( // Check if error report should be suppressed. DynamicTypeInfo DTI = getDynamicTypeInfo((void*)Pointer); - if (DTI.isValid() && - MatchSuppression(DTI.getMostDerivedTypeName(), SuppressionVptrCheck)) + if (DTI.isValid() && IsVptrCheckSuppressed(DTI.getMostDerivedTypeName())) return; SourceLocation Loc = Data->Loc.acquire(); diff --git a/lib/ubsan/ubsan_init.cc b/lib/ubsan/ubsan_init.cc index 48fa492486dd..219273d51921 100644 --- a/lib/ubsan/ubsan_init.cc +++ b/lib/ubsan/ubsan_init.cc @@ -11,12 +11,12 @@ // //===----------------------------------------------------------------------===// +#include "ubsan_diag.h" #include "ubsan_init.h" #include "ubsan_flags.h" #include "sanitizer_common/sanitizer_common.h" #include "sanitizer_common/sanitizer_libc.h" #include "sanitizer_common/sanitizer_mutex.h" -#include "sanitizer_common/sanitizer_suppressions.h" #include "sanitizer_common/sanitizer_symbolizer.h" using namespace __ubsan; @@ -43,7 +43,7 @@ void __ubsan::InitIfNecessary() { } // Initialize UBSan-specific flags. InitializeFlags(standalone); - SuppressionContext::InitIfNecessary(); + InitializeSuppressions(); InitializeCoverage(common_flags()->coverage, common_flags()->coverage_dir); ubsan_inited = true; } |