diff options
author | Kito Cheng <kito.cheng@sifive.com> | 2024-11-01 15:33:03 +0800 |
---|---|---|
committer | Kito Cheng <kito.cheng@sifive.com> | 2024-11-12 21:56:06 +0800 |
commit | fa321004f3f6288d3ee2eefa6b02177131882dca (patch) | |
tree | 6e111379127d142eb20c7630a44254124adef5bb | |
parent | b8ecd96aea9a97a60b143fc70efa6d03d0f188a2 (diff) | |
download | gcc-fa321004f3f6288d3ee2eefa6b02177131882dca.zip gcc-fa321004f3f6288d3ee2eefa6b02177131882dca.tar.gz gcc-fa321004f3f6288d3ee2eefa6b02177131882dca.tar.bz2 |
libsanitizer: merge from upstream (61a6439f35b6de28)
238 files changed, 7276 insertions, 4764 deletions
diff --git a/libsanitizer/MERGE b/libsanitizer/MERGE index 2ee0843..a223fee 100644 --- a/libsanitizer/MERGE +++ b/libsanitizer/MERGE @@ -1,4 +1,4 @@ -c425db2eb558c26377edc04e062c0c1f999b2770 +61a6439f35b6de28ff4aff4450d6fca970292fd5 The first line of this file holds the git revision number of the last merge done from the master library sources. diff --git a/libsanitizer/asan/Makefile.am b/libsanitizer/asan/Makefile.am index 6af250e..5c77b3b 100644 --- a/libsanitizer/asan/Makefile.am +++ b/libsanitizer/asan/Makefile.am @@ -46,7 +46,6 @@ asan_files = \ asan_suppressions.cpp \ asan_thread.cpp \ asan_win.cpp \ - asan_win_dll_thunk.cpp \ asan_win_dynamic_runtime_thunk.cpp \ asan_interceptors_vfork.S diff --git a/libsanitizer/asan/Makefile.in b/libsanitizer/asan/Makefile.in index ac72e13..ef4b65d 100644 --- a/libsanitizer/asan/Makefile.in +++ b/libsanitizer/asan/Makefile.in @@ -160,8 +160,7 @@ am__objects_1 = asan_activation.lo asan_allocator.lo asan_debugging.lo \ asan_posix.lo asan_premap_shadow.lo asan_report.lo asan_rtl.lo \ asan_shadow_setup.lo asan_stack.lo asan_stats.lo \ asan_suppressions.lo asan_thread.lo asan_win.lo \ - asan_win_dll_thunk.lo asan_win_dynamic_runtime_thunk.lo \ - asan_interceptors_vfork.lo + asan_win_dynamic_runtime_thunk.lo asan_interceptors_vfork.lo am_libasan_la_OBJECTS = $(am__objects_1) libasan_la_OBJECTS = $(am_libasan_la_OBJECTS) AM_V_lt = $(am__v_lt_@AM_V@) @@ -457,7 +456,6 @@ asan_files = \ asan_suppressions.cpp \ asan_thread.cpp \ asan_win.cpp \ - asan_win_dll_thunk.cpp \ asan_win_dynamic_runtime_thunk.cpp \ asan_interceptors_vfork.S @@ -619,7 +617,6 @@ distclean-compile: @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/asan_suppressions.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/asan_thread.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/asan_win.Plo@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/asan_win_dll_thunk.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/asan_win_dynamic_runtime_thunk.Plo@am__quote@ .S.o: diff --git a/libsanitizer/asan/asan_allocator.cpp b/libsanitizer/asan/asan_allocator.cpp index 22dcf61..9e66f77 100644 --- a/libsanitizer/asan/asan_allocator.cpp +++ b/libsanitizer/asan/asan_allocator.cpp @@ -717,7 +717,15 @@ struct Allocator { return; } - RunFreeHooks(ptr); + if (RunFreeHooks(ptr)) { + // Someone used __sanitizer_ignore_free_hook() and decided that they + // didn't want the memory to __sanitizer_ignore_free_hook freed right now. + // When they call free() on this pointer again at a later time, we should + // ignore the alloc-type mismatch and allow them to deallocate the pointer + // through free(), rather than the initial alloc type. + m->alloc_type = FROM_MALLOC; + return; + } // Must mark the chunk as quarantined before any changes to its metadata. // Do not quarantine given chunk if we failed to set CHUNK_QUARANTINE flag. diff --git a/libsanitizer/asan/asan_allocator.h b/libsanitizer/asan/asan_allocator.h index c3c4fae..db8dc3b 100644 --- a/libsanitizer/asan/asan_allocator.h +++ b/libsanitizer/asan/asan_allocator.h @@ -182,42 +182,44 @@ static_assert(SizeClassMap::kNumClassesRounded <= 32, "allocator size and SizeClassMap tunings that allows us to " "reliably run all bringup tests in a sanitized environment."); -# else +# else // SANITIZER_RISCV64 // These are the default allocator tunings for non-RISCV environments where the // VMA is usually 48 bits and we have lots of space. const uptr kAllocatorSize = 0x40000000000ULL; // 4T. typedef DefaultSizeClassMap SizeClassMap; -# endif -# elif defined(__powerpc64__) +# endif // SANITIZER_RISCV64 +# else // SANITIZER_FUCHSIA + +# if SANITIZER_APPLE +const uptr kAllocatorSpace = 0x600000000000ULL; +# else // SANITIZER_APPLE const uptr kAllocatorSpace = ~(uptr)0; +# endif // SANITIZER_APPLE + +# if defined(__powerpc64__) const uptr kAllocatorSize = 0x20000000000ULL; // 2T. typedef DefaultSizeClassMap SizeClassMap; -# elif defined(__aarch64__) && SANITIZER_ANDROID +# elif defined(__aarch64__) && SANITIZER_ANDROID // Android needs to support 39, 42 and 48 bit VMA. -const uptr kAllocatorSpace = ~(uptr)0; const uptr kAllocatorSize = 0x2000000000ULL; // 128G. typedef VeryCompactSizeClassMap SizeClassMap; -# elif SANITIZER_RISCV64 -const uptr kAllocatorSpace = ~(uptr)0; +# elif SANITIZER_RISCV64 const uptr kAllocatorSize = 0x2000000000ULL; // 128G. typedef VeryDenseSizeClassMap SizeClassMap; -# elif defined(__sparc__) -const uptr kAllocatorSpace = ~(uptr)0; +# elif defined(__sparc__) const uptr kAllocatorSize = 0x20000000000ULL; // 2T. typedef DefaultSizeClassMap SizeClassMap; -# elif SANITIZER_WINDOWS -const uptr kAllocatorSpace = ~(uptr)0; +# elif SANITIZER_WINDOWS const uptr kAllocatorSize = 0x8000000000ULL; // 500G typedef DefaultSizeClassMap SizeClassMap; -# elif SANITIZER_APPLE -const uptr kAllocatorSpace = 0x600000000000ULL; +# elif SANITIZER_APPLE const uptr kAllocatorSize = 0x40000000000ULL; // 4T. typedef DefaultSizeClassMap SizeClassMap; -# else -const uptr kAllocatorSpace = 0x500000000000ULL; +# else const uptr kAllocatorSize = 0x40000000000ULL; // 4T. typedef DefaultSizeClassMap SizeClassMap; -# endif +# endif // defined(__powerpc64__) etc. +# endif // SANITIZER_FUCHSIA template <typename AddressSpaceViewTy> struct AP64 { // Allocator64 parameters. Deliberately using a short name. static const uptr kSpaceBeg = kAllocatorSpace; @@ -232,7 +234,7 @@ struct AP64 { // Allocator64 parameters. Deliberately using a short name. template <typename AddressSpaceView> using PrimaryAllocatorASVT = SizeClassAllocator64<AP64<AddressSpaceView>>; using PrimaryAllocator = PrimaryAllocatorASVT<LocalAddressSpaceView>; -#else // Fallback to SizeClassAllocator32. +#else // SANITIZER_CAN_USE_ALLOCATOR64. Fallback to SizeClassAllocator32. typedef CompactSizeClassMap SizeClassMap; template <typename AddressSpaceViewTy> struct AP32 { diff --git a/libsanitizer/asan/asan_descriptions.cpp b/libsanitizer/asan/asan_descriptions.cpp index ef6f3e0..caec793 100644 --- a/libsanitizer/asan/asan_descriptions.cpp +++ b/libsanitizer/asan/asan_descriptions.cpp @@ -20,24 +20,20 @@ namespace __asan { AsanThreadIdAndName::AsanThreadIdAndName(AsanThreadContext *t) { - Init(t->tid, t->name); -} - -AsanThreadIdAndName::AsanThreadIdAndName(u32 tid) { - if (tid == kInvalidTid) { - Init(tid, ""); - } else { - asanThreadRegistry().CheckLocked(); - AsanThreadContext *t = GetThreadContextByTidLocked(tid); - Init(tid, t->name); + if (!t) { + internal_snprintf(name, sizeof(name), "T-1"); + return; } + int len = internal_snprintf(name, sizeof(name), "T%llu", t->unique_id); + CHECK(((unsigned int)len) < sizeof(name)); + if (internal_strlen(t->name)) + internal_snprintf(&name[len], sizeof(name) - len, " (%s)", t->name); } -void AsanThreadIdAndName::Init(u32 tid, const char *tname) { - int len = internal_snprintf(name, sizeof(name), "T%d", tid); - CHECK(((unsigned int)len) < sizeof(name)); - if (tname[0] != '\0') - internal_snprintf(&name[len], sizeof(name) - len, " (%s)", tname); +AsanThreadIdAndName::AsanThreadIdAndName(u32 tid) + : AsanThreadIdAndName( + tid == kInvalidTid ? nullptr : GetThreadContextByTidLocked(tid)) { + asanThreadRegistry().CheckLocked(); } void DescribeThread(AsanThreadContext *context) { @@ -48,9 +44,20 @@ void DescribeThread(AsanThreadContext *context) { return; } context->announced = true; + + AsanThreadContext *parent_context = + context->parent_tid == kInvalidTid + ? nullptr + : GetThreadContextByTidLocked(context->parent_tid); + + // `context->parent_tid` may point to reused slot. Check `unique_id` which + // is always smaller for the parent, always greater for a new user. + if (context->unique_id <= parent_context->unique_id) + parent_context = nullptr; + InternalScopedString str; str.AppendF("Thread %s", AsanThreadIdAndName(context).c_str()); - if (context->parent_tid == kInvalidTid) { + if (!parent_context) { str.Append(" created by unknown thread\n"); Printf("%s", str.data()); return; @@ -60,11 +67,8 @@ void DescribeThread(AsanThreadContext *context) { Printf("%s", str.data()); StackDepotGet(context->stack_id).Print(); // Recursively described parent thread if needed. - if (flags()->print_full_thread_history) { - AsanThreadContext *parent_context = - GetThreadContextByTidLocked(context->parent_tid); + if (flags()->print_full_thread_history) DescribeThread(parent_context); - } } // Shadow descriptions @@ -245,11 +249,11 @@ static void PrintAccessAndVarIntersection(const StackVarDescr &var, uptr addr, InternalScopedString str; str.AppendF(" [%zd, %zd)", var.beg, var_end); // Render variable name. - str.AppendF(" '"); + str.Append(" '"); for (uptr i = 0; i < var.name_len; ++i) { str.AppendF("%c", var.name_pos[i]); } - str.AppendF("'"); + str.Append("'"); if (var.line > 0) { str.AppendF(" (line %zd)", var.line); } @@ -260,7 +264,7 @@ static void PrintAccessAndVarIntersection(const StackVarDescr &var, uptr addr, str.AppendF("%s <== Memory access at offset %zd %s this variable%s\n", d.Location(), addr, pos_descr, d.Default()); } else { - str.AppendF("\n"); + str.Append("\n"); } Printf("%s", str.data()); } @@ -292,7 +296,7 @@ static void DescribeAddressRelativeToGlobal(uptr addr, uptr access_size, str.AppendF(" global variable '%s' defined in '", MaybeDemangleGlobalName(g.name)); PrintGlobalLocation(&str, g, /*print_module_name=*/false); - str.AppendF("' (0x%zx) of size %zu\n", g.beg, g.size); + str.AppendF("' (%p) of size %zu\n", (void *)g.beg, g.size); str.Append(d.Default()); PrintGlobalNameIfASCII(&str, g); Printf("%s", str.data()); diff --git a/libsanitizer/asan/asan_descriptions.h b/libsanitizer/asan/asan_descriptions.h index 650e2eb..a614f47 100644 --- a/libsanitizer/asan/asan_descriptions.h +++ b/libsanitizer/asan/asan_descriptions.h @@ -35,8 +35,6 @@ class AsanThreadIdAndName { const char *c_str() const { return &name[0]; } private: - void Init(u32 tid, const char *tname); - char name[128]; }; diff --git a/libsanitizer/asan/asan_errors.cpp b/libsanitizer/asan/asan_errors.cpp index 3f2d13e..4f112cc 100644 --- a/libsanitizer/asan/asan_errors.cpp +++ b/libsanitizer/asan/asan_errors.cpp @@ -327,9 +327,6 @@ void ErrorBadParamsToAnnotateContiguousContainer::Print() { " old_mid : %p\n" " new_mid : %p\n", (void *)beg, (void *)end, (void *)old_mid, (void *)new_mid); - uptr granularity = ASAN_SHADOW_GRANULARITY; - if (!IsAligned(beg, granularity)) - Report("ERROR: beg is not aligned by %zu\n", granularity); stack->Print(); ReportErrorSummary(scariness.GetDescription(), stack); } @@ -347,9 +344,20 @@ void ErrorBadParamsToAnnotateDoubleEndedContiguousContainer::Print() { (void *)storage_beg, (void *)storage_end, (void *)old_container_beg, (void *)old_container_end, (void *)new_container_beg, (void *)new_container_end); - uptr granularity = ASAN_SHADOW_GRANULARITY; - if (!IsAligned(storage_beg, granularity)) - Report("ERROR: storage_beg is not aligned by %zu\n", granularity); + stack->Print(); + ReportErrorSummary(scariness.GetDescription(), stack); +} + +void ErrorBadParamsToCopyContiguousContainerAnnotations::Print() { + Report( + "ERROR: AddressSanitizer: bad parameters to " + "__sanitizer_copy_contiguous_container_annotations:\n" + " src_storage_beg : %p\n" + " src_storage_end : %p\n" + " dst_storage_beg : %p\n" + " new_storage_end : %p\n", + (void *)old_storage_beg, (void *)old_storage_end, (void *)new_storage_beg, + (void *)new_storage_end); stack->Print(); ReportErrorSummary(scariness.GetDescription(), stack); } diff --git a/libsanitizer/asan/asan_errors.h b/libsanitizer/asan/asan_errors.h index 634f6da..b3af655 100644 --- a/libsanitizer/asan/asan_errors.h +++ b/libsanitizer/asan/asan_errors.h @@ -353,6 +353,24 @@ struct ErrorBadParamsToAnnotateDoubleEndedContiguousContainer : ErrorBase { void Print(); }; +struct ErrorBadParamsToCopyContiguousContainerAnnotations : ErrorBase { + const BufferedStackTrace *stack; + uptr old_storage_beg, old_storage_end, new_storage_beg, new_storage_end; + + ErrorBadParamsToCopyContiguousContainerAnnotations() = default; // (*) + ErrorBadParamsToCopyContiguousContainerAnnotations( + u32 tid, BufferedStackTrace *stack_, uptr old_storage_beg_, + uptr old_storage_end_, uptr new_storage_beg_, uptr new_storage_end_) + : ErrorBase(tid, 10, + "bad-__sanitizer_annotate_double_ended_contiguous_container"), + stack(stack_), + old_storage_beg(old_storage_beg_), + old_storage_end(old_storage_end_), + new_storage_beg(new_storage_beg_), + new_storage_end(new_storage_end_) {} + void Print(); +}; + struct ErrorODRViolation : ErrorBase { __asan_global global1, global2; u32 stack_id1, stack_id2; @@ -421,6 +439,7 @@ struct ErrorGeneric : ErrorBase { macro(StringFunctionSizeOverflow) \ macro(BadParamsToAnnotateContiguousContainer) \ macro(BadParamsToAnnotateDoubleEndedContiguousContainer) \ + macro(BadParamsToCopyContiguousContainerAnnotations) \ macro(ODRViolation) \ macro(InvalidPointerPair) \ macro(Generic) diff --git a/libsanitizer/asan/asan_flags.cpp b/libsanitizer/asan/asan_flags.cpp index 2398984..56deb1b 100644 --- a/libsanitizer/asan/asan_flags.cpp +++ b/libsanitizer/asan/asan_flags.cpp @@ -11,14 +11,16 @@ // ASan flag parsing logic. //===----------------------------------------------------------------------===// -#include "asan_activation.h" #include "asan_flags.h" + +#include "asan_activation.h" #include "asan_interface_internal.h" #include "asan_stack.h" #include "lsan/lsan_common.h" #include "sanitizer_common/sanitizer_common.h" -#include "sanitizer_common/sanitizer_flags.h" #include "sanitizer_common/sanitizer_flag_parser.h" +#include "sanitizer_common/sanitizer_flags.h" +#include "sanitizer_common/sanitizer_win_interception.h" #include "ubsan/ubsan_flags.h" #include "ubsan/ubsan_platform.h" @@ -47,7 +49,21 @@ static void RegisterAsanFlags(FlagParser *parser, Flags *f) { #undef ASAN_FLAG } -void InitializeFlags() { +static void DisplayHelpMessages(FlagParser *parser) { + // TODO(eugenis): dump all flags at verbosity>=2? + if (Verbosity()) { + ReportUnrecognizedFlags(); + } + + if (common_flags()->help) { + parser->PrintFlagDescriptions(); + } +} + +static void InitializeDefaultFlags() { + Flags *f = flags(); + FlagParser asan_parser; + // Set the default values and prepare for parsing ASan and common flags. SetCommonFlagsDefaults(); { @@ -60,10 +76,8 @@ void InitializeFlags() { cf.exitcode = 1; OverrideCommonFlags(cf); } - Flags *f = flags(); f->SetDefaults(); - FlagParser asan_parser; RegisterAsanFlags(&asan_parser, f); RegisterCommonFlags(&asan_parser); @@ -126,13 +140,12 @@ void InitializeFlags() { InitializeCommonFlags(); - // TODO(eugenis): dump all flags at verbosity>=2? - if (Verbosity()) ReportUnrecognizedFlags(); + // TODO(samsonov): print all of the flags (ASan, LSan, common). + DisplayHelpMessages(&asan_parser); +} - if (common_flags()->help) { - // TODO(samsonov): print all of the flags (ASan, LSan, common). - asan_parser.PrintFlagDescriptions(); - } +static void ProcessFlags() { + Flags *f = flags(); // Flag validation: if (!CAN_SANITIZE_LEAKS && common_flags()->detect_leaks) { @@ -199,6 +212,67 @@ void InitializeFlags() { } } +void InitializeFlags() { + InitializeDefaultFlags(); + ProcessFlags(); + +#if SANITIZER_WINDOWS + // On Windows, weak symbols are emulated by having the user program + // register which weak functions are defined. + // The ASAN DLL will initialize flags prior to user module initialization, + // so __asan_default_options will not point to the user definition yet. + // We still want to ensure we capture when options are passed via + // __asan_default_options, so we add a callback to be run + // when it is registered with the runtime. + + // There is theoretically time between the initial ProcessFlags and + // registering the weak callback where a weak function could be added and we + // would miss it, but in practice, InitializeFlags will always happen under + // the loader lock (if built as a DLL) and so will any calls to + // __sanitizer_register_weak_function. + AddRegisterWeakFunctionCallback( + reinterpret_cast<uptr>(__asan_default_options), []() { + FlagParser asan_parser; + + RegisterAsanFlags(&asan_parser, flags()); + RegisterCommonFlags(&asan_parser); + asan_parser.ParseString(__asan_default_options()); + + DisplayHelpMessages(&asan_parser); + ProcessFlags(); + }); + +# if CAN_SANITIZE_UB + AddRegisterWeakFunctionCallback( + reinterpret_cast<uptr>(__ubsan_default_options), []() { + FlagParser ubsan_parser; + + __ubsan::RegisterUbsanFlags(&ubsan_parser, __ubsan::flags()); + RegisterCommonFlags(&ubsan_parser); + ubsan_parser.ParseString(__ubsan_default_options()); + + // To match normal behavior, do not print UBSan help. + ProcessFlags(); + }); +# endif + +# if CAN_SANITIZE_LEAKS + AddRegisterWeakFunctionCallback( + reinterpret_cast<uptr>(__lsan_default_options), []() { + FlagParser lsan_parser; + + __lsan::RegisterLsanFlags(&lsan_parser, __lsan::flags()); + RegisterCommonFlags(&lsan_parser); + lsan_parser.ParseString(__lsan_default_options()); + + // To match normal behavior, do not print LSan help. + ProcessFlags(); + }); +# endif + +#endif +} + } // namespace __asan SANITIZER_INTERFACE_WEAK_DEF(const char*, __asan_default_options, void) { diff --git a/libsanitizer/asan/asan_fuchsia.cpp b/libsanitizer/asan/asan_fuchsia.cpp index 2b15504..96c41e9 100644 --- a/libsanitizer/asan/asan_fuchsia.cpp +++ b/libsanitizer/asan/asan_fuchsia.cpp @@ -57,8 +57,6 @@ void AsanCheckDynamicRTPrereqs() {} void AsanCheckIncompatibleRT() {} void InitializeAsanInterceptors() {} -void *AsanDoesNotSupportStaticLinkage() { return nullptr; } - void InitializePlatformExceptionHandlers() {} void AsanOnDeadlySignal(int signo, void *siginfo, void *context) { UNIMPLEMENTED(); @@ -123,8 +121,7 @@ static AsanThread *CreateAsanThread(StackTrace *stack, u32 parent_tid, // In lieu of AsanThread::Create. AsanThread *thread = (AsanThread *)MmapOrDie(AsanThreadMmapSize(), __func__); - AsanThreadContext::CreateThreadContextArgs args = {thread, stack}; - u32 tid = asanThreadRegistry().CreateThread(0, detached, parent_tid, &args); + u32 tid = asanThreadRegistry().CreateThread(0, detached, parent_tid, thread); asanThreadRegistry().SetThreadName(tid, name); return thread; @@ -240,6 +237,8 @@ void FlushUnneededASanShadowMemory(uptr p, uptr size) { // So this doesn't install any atexit hook like on other platforms. void InstallAtExitCheckLeaks() {} +void InstallAtForkHandler() {} + } // namespace __asan namespace __lsan { diff --git a/libsanitizer/asan/asan_globals.cpp b/libsanitizer/asan/asan_globals.cpp index 1b41992..c83b782 100644 --- a/libsanitizer/asan/asan_globals.cpp +++ b/libsanitizer/asan/asan_globals.cpp @@ -21,31 +21,32 @@ #include "asan_suppressions.h" #include "asan_thread.h" #include "sanitizer_common/sanitizer_common.h" +#include "sanitizer_common/sanitizer_dense_map.h" +#include "sanitizer_common/sanitizer_list.h" #include "sanitizer_common/sanitizer_mutex.h" #include "sanitizer_common/sanitizer_placement_new.h" #include "sanitizer_common/sanitizer_stackdepot.h" #include "sanitizer_common/sanitizer_symbolizer.h" +#include "sanitizer_common/sanitizer_thread_safety.h" namespace __asan { typedef __asan_global Global; -struct ListOfGlobals { - const Global *g; - ListOfGlobals *next; +struct GlobalListNode { + const Global *g = nullptr; + GlobalListNode *next = nullptr; }; +typedef IntrusiveList<GlobalListNode> ListOfGlobals; static Mutex mu_for_globals; -static ListOfGlobals *list_of_all_globals; +static ListOfGlobals list_of_all_globals SANITIZER_GUARDED_BY(mu_for_globals); -static const int kDynamicInitGlobalsInitialCapacity = 512; struct DynInitGlobal { - Global g; - bool initialized; + Global g = {}; + bool initialized = false; + DynInitGlobal *next = nullptr; }; -typedef InternalMmapVector<DynInitGlobal> VectorOfGlobals; -// Lazy-initialized and never deleted. -static VectorOfGlobals *dynamic_init_globals; // We want to remember where a certain range of globals was registered. struct GlobalRegistrationSite { @@ -55,6 +56,39 @@ struct GlobalRegistrationSite { typedef InternalMmapVector<GlobalRegistrationSite> GlobalRegistrationSiteVector; static GlobalRegistrationSiteVector *global_registration_site_vector; +static ListOfGlobals &GlobalsByIndicator(uptr odr_indicator) + SANITIZER_REQUIRES(mu_for_globals) { + using MapOfGlobals = DenseMap<uptr, ListOfGlobals>; + + static MapOfGlobals *globals_by_indicator = nullptr; + if (!globals_by_indicator) { + alignas( + alignof(MapOfGlobals)) static char placeholder[sizeof(MapOfGlobals)]; + globals_by_indicator = new (placeholder) MapOfGlobals(); + } + + return (*globals_by_indicator)[odr_indicator]; +} + +static const char *current_dynamic_init_module_name + SANITIZER_GUARDED_BY(mu_for_globals) = nullptr; + +using DynInitGlobalsByModule = + DenseMap<const char *, IntrusiveList<DynInitGlobal>>; + +// TODO: Add a NoDestroy helper, this patter is very common in sanitizers. +static DynInitGlobalsByModule &DynInitGlobals() + SANITIZER_REQUIRES(mu_for_globals) { + static DynInitGlobalsByModule *globals_by_module = nullptr; + if (!globals_by_module) { + alignas(alignof(DynInitGlobalsByModule)) static char + placeholder[sizeof(DynInitGlobalsByModule)]; + globals_by_module = new (placeholder) DynInitGlobalsByModule(); + } + + return *globals_by_module; +} + ALWAYS_INLINE void PoisonShadowForGlobal(const Global *g, u8 value) { FastPoisonShadow(g->beg, g->size_with_redzone, value); } @@ -73,6 +107,35 @@ ALWAYS_INLINE void PoisonRedZones(const Global &g) { const uptr kMinimalDistanceFromAnotherGlobal = 64; +static void AddGlobalToList(ListOfGlobals &list, const Global *g) { + list.push_front(new (GetGlobalLowLevelAllocator()) GlobalListNode{g}); +} + +static void UnpoisonDynamicGlobals(IntrusiveList<DynInitGlobal> &dyn_globals, + bool mark_initialized) { + for (auto &dyn_g : dyn_globals) { + const Global *g = &dyn_g.g; + if (dyn_g.initialized) + continue; + // Unpoison the whole global. + PoisonShadowForGlobal(g, 0); + // Poison redzones back. + PoisonRedZones(*g); + if (mark_initialized) + dyn_g.initialized = true; + } +} + +static void PoisonDynamicGlobals( + const IntrusiveList<DynInitGlobal> &dyn_globals) { + for (auto &dyn_g : dyn_globals) { + const Global *g = &dyn_g.g; + if (dyn_g.initialized) + continue; + PoisonShadowForGlobal(g, kAsanInitializationOrderMagic); + } +} + static bool IsAddressNearGlobal(uptr addr, const __asan_global &g) { if (addr <= g.beg - kMinimalDistanceFromAnotherGlobal) return false; if (addr >= g.beg + g.size_with_redzone) return false; @@ -114,8 +177,8 @@ int GetGlobalsForAddress(uptr addr, Global *globals, u32 *reg_sites, if (!flags()->report_globals) return 0; Lock lock(&mu_for_globals); int res = 0; - for (ListOfGlobals *l = list_of_all_globals; l; l = l->next) { - const Global &g = *l->g; + for (const auto &l : list_of_all_globals) { + const Global &g = *l.g; if (flags()->report_globals >= 2) ReportGlobal(g, "Search"); if (IsAddressNearGlobal(addr, g)) { @@ -138,23 +201,47 @@ enum GlobalSymbolState { // Check ODR violation for given global G via special ODR indicator. We use // this method in case compiler instruments global variables through their // local aliases. -static void CheckODRViolationViaIndicator(const Global *g) { +static void CheckODRViolationViaIndicator(const Global *g) + SANITIZER_REQUIRES(mu_for_globals) { // Instrumentation requests to skip ODR check. if (g->odr_indicator == UINTPTR_MAX) return; + + ListOfGlobals &relevant_globals = GlobalsByIndicator(g->odr_indicator); + u8 *odr_indicator = reinterpret_cast<u8 *>(g->odr_indicator); - if (*odr_indicator == UNREGISTERED) { + if (*odr_indicator == REGISTERED) { + // If *odr_indicator is REGISTERED, some module have already registered + // externally visible symbol with the same name. This is an ODR violation. + for (const auto &l : relevant_globals) { + if ((flags()->detect_odr_violation >= 2 || g->size != l.g->size) && + !IsODRViolationSuppressed(g->name)) + ReportODRViolation(g, FindRegistrationSite(g), l.g, + FindRegistrationSite(l.g)); + } + } else { // UNREGISTERED *odr_indicator = REGISTERED; - return; } - // If *odr_indicator is DEFINED, some module have already registered - // externally visible symbol with the same name. This is an ODR violation. - for (ListOfGlobals *l = list_of_all_globals; l; l = l->next) { - if (g->odr_indicator == l->g->odr_indicator && - (flags()->detect_odr_violation >= 2 || g->size != l->g->size) && - !IsODRViolationSuppressed(g->name)) - ReportODRViolation(g, FindRegistrationSite(g), - l->g, FindRegistrationSite(l->g)); + + AddGlobalToList(relevant_globals, g); +} + +// Check ODR violation for given global G by checking if it's already poisoned. +// We use this method in case compiler doesn't use private aliases for global +// variables. +static void CheckODRViolationViaPoisoning(const Global *g) + SANITIZER_REQUIRES(mu_for_globals) { + if (__asan_region_is_poisoned(g->beg, g->size_with_redzone)) { + // This check may not be enough: if the first global is much larger + // the entire redzone of the second global may be within the first global. + for (const auto &l : list_of_all_globals) { + if (g->beg == l.g->beg && + (flags()->detect_odr_violation >= 2 || g->size != l.g->size) && + !IsODRViolationSuppressed(g->name)) { + ReportODRViolation(g, FindRegistrationSite(g), l.g, + FindRegistrationSite(l.g)); + } + } } } @@ -181,7 +268,7 @@ static inline bool UseODRIndicator(const Global *g) { // Register a global variable. // This function may be called more than once for every global // so we store the globals in a map. -static void RegisterGlobal(const Global *g) { +static void RegisterGlobal(const Global *g) SANITIZER_REQUIRES(mu_for_globals) { CHECK(AsanInited()); if (flags()->report_globals >= 2) ReportGlobal(*g, "Added"); @@ -203,24 +290,22 @@ static void RegisterGlobal(const Global *g) { // where two globals with the same name are defined in different modules. if (UseODRIndicator(g)) CheckODRViolationViaIndicator(g); + else + CheckODRViolationViaPoisoning(g); } if (CanPoisonMemory()) PoisonRedZones(*g); - ListOfGlobals *l = new (GetGlobalLowLevelAllocator()) ListOfGlobals; - l->g = g; - l->next = list_of_all_globals; - list_of_all_globals = l; + + AddGlobalToList(list_of_all_globals, g); + if (g->has_dynamic_init) { - if (!dynamic_init_globals) { - dynamic_init_globals = new (GetGlobalLowLevelAllocator()) VectorOfGlobals; - dynamic_init_globals->reserve(kDynamicInitGlobalsInitialCapacity); - } - DynInitGlobal dyn_global = { *g, false }; - dynamic_init_globals->push_back(dyn_global); + DynInitGlobals()[g->module_name].push_back( + new (GetGlobalLowLevelAllocator()) DynInitGlobal{*g, false}); } } -static void UnregisterGlobal(const Global *g) { +static void UnregisterGlobal(const Global *g) + SANITIZER_REQUIRES(mu_for_globals) { CHECK(AsanInited()); if (flags()->report_globals >= 2) ReportGlobal(*g, "Removed"); @@ -242,18 +327,14 @@ static void UnregisterGlobal(const Global *g) { } void StopInitOrderChecking() { - Lock lock(&mu_for_globals); - if (!flags()->check_initialization_order || !dynamic_init_globals) + if (!flags()->check_initialization_order) return; + Lock lock(&mu_for_globals); flags()->check_initialization_order = false; - for (uptr i = 0, n = dynamic_init_globals->size(); i < n; ++i) { - DynInitGlobal &dyn_g = (*dynamic_init_globals)[i]; - const Global *g = &dyn_g.g; - // Unpoison the whole global. - PoisonShadowForGlobal(g, 0); - // Poison redzones back. - PoisonRedZones(*g); - } + DynInitGlobals().forEach([&](auto &kv) { + UnpoisonDynamicGlobals(kv.second, /*mark_initialized=*/false); + return true; + }); } static bool IsASCII(unsigned char c) { return /*0x00 <= c &&*/ c <= 0x7F; } @@ -325,8 +406,8 @@ void __asan_unregister_image_globals(uptr *flag) { } void __asan_register_elf_globals(uptr *flag, void *start, void *stop) { - if (*flag) return; - if (!start) return; + if (*flag || start == stop) + return; CHECK_EQ(0, ((uptr)stop - (uptr)start) % sizeof(__asan_global)); __asan_global *globals_start = (__asan_global*)start; __asan_global *globals_stop = (__asan_global*)stop; @@ -335,8 +416,8 @@ void __asan_register_elf_globals(uptr *flag, void *start, void *stop) { } void __asan_unregister_elf_globals(uptr *flag, void *start, void *stop) { - if (!*flag) return; - if (!start) return; + if (!*flag || start == stop) + return; CHECK_EQ(0, ((uptr)stop - (uptr)start) % sizeof(__asan_global)); __asan_global *globals_start = (__asan_global*)start; __asan_global *globals_stop = (__asan_global*)stop; @@ -408,47 +489,94 @@ void __asan_unregister_globals(__asan_global *globals, uptr n) { // poisons all global variables not defined in this TU, so that a dynamic // initializer can only touch global variables in the same TU. void __asan_before_dynamic_init(const char *module_name) { - if (!flags()->check_initialization_order || - !CanPoisonMemory() || - !dynamic_init_globals) + if (!flags()->check_initialization_order || !CanPoisonMemory()) return; bool strict_init_order = flags()->strict_init_order; CHECK(module_name); CHECK(AsanInited()); Lock lock(&mu_for_globals); + if (current_dynamic_init_module_name == module_name) + return; if (flags()->report_globals >= 3) Printf("DynInitPoison module: %s\n", module_name); - for (uptr i = 0, n = dynamic_init_globals->size(); i < n; ++i) { - DynInitGlobal &dyn_g = (*dynamic_init_globals)[i]; - const Global *g = &dyn_g.g; - if (dyn_g.initialized) - continue; - if (g->module_name != module_name) - PoisonShadowForGlobal(g, kAsanInitializationOrderMagic); - else if (!strict_init_order) - dyn_g.initialized = true; + + if (current_dynamic_init_module_name == nullptr) { + // First call, poison all globals from other modules. + DynInitGlobals().forEach([&](auto &kv) { + if (kv.first != module_name) { + PoisonDynamicGlobals(kv.second); + } else { + UnpoisonDynamicGlobals(kv.second, + /*mark_initialized=*/!strict_init_order); + } + return true; + }); + } else { + // Module changed. + PoisonDynamicGlobals(DynInitGlobals()[current_dynamic_init_module_name]); + UnpoisonDynamicGlobals(DynInitGlobals()[module_name], + /*mark_initialized=*/!strict_init_order); } + current_dynamic_init_module_name = module_name; } +// Maybe SANITIZER_CAN_USE_PREINIT_ARRAY is to conservative for `.init_array`, +// however we should not make mistake here. If `UnpoisonBeforeMain` was not +// executed at all we will have false reports on globals. +#if SANITIZER_CAN_USE_PREINIT_ARRAY +// This optimization aims to reduce the overhead of `__asan_after_dynamic_init` +// calls by leveraging incremental unpoisoning/poisoning in +// `__asan_before_dynamic_init`. We expect most `__asan_after_dynamic_init +// calls` to be no-ops. However, to ensure all globals are unpoisoned before the +// `main`, we force `UnpoisonBeforeMain` to fully execute +// `__asan_after_dynamic_init`. + +// With lld, `UnpoisonBeforeMain` runs after standard `.init_array`, making it +// the final `__asan_after_dynamic_init` call for the static runtime. In +// contrast, GNU ld executes it earlier, causing subsequent +// `__asan_after_dynamic_init` calls to perform full unpoisoning, losing the +// optimization. +bool allow_after_dynamic_init SANITIZER_GUARDED_BY(mu_for_globals) = false; + +static void UnpoisonBeforeMain(void) { + { + Lock lock(&mu_for_globals); + if (allow_after_dynamic_init) + return; + allow_after_dynamic_init = true; + } + if (flags()->report_globals >= 3) + Printf("UnpoisonBeforeMain\n"); + __asan_after_dynamic_init(); +} + +__attribute__((section(".init_array.65537"), used)) static void ( + *asan_after_init_array)(void) = UnpoisonBeforeMain; +#else +// Incremental poisoning is disabled, unpoison globals immediately. +static constexpr bool allow_after_dynamic_init = true; +#endif // SANITIZER_CAN_USE_PREINIT_ARRAY + // This method runs immediately after dynamic initialization in each TU, when // all dynamically initialized globals except for those defined in the current // TU are poisoned. It simply unpoisons all dynamically initialized globals. void __asan_after_dynamic_init() { - if (!flags()->check_initialization_order || - !CanPoisonMemory() || - !dynamic_init_globals) + if (!flags()->check_initialization_order || !CanPoisonMemory()) return; CHECK(AsanInited()); Lock lock(&mu_for_globals); - // FIXME: Optionally report that we're unpoisoning globals from a module. - for (uptr i = 0, n = dynamic_init_globals->size(); i < n; ++i) { - DynInitGlobal &dyn_g = (*dynamic_init_globals)[i]; - const Global *g = &dyn_g.g; - if (!dyn_g.initialized) { - // Unpoison the whole global. - PoisonShadowForGlobal(g, 0); - // Poison redzones back. - PoisonRedZones(*g); - } - } + if (!allow_after_dynamic_init) + return; + if (!current_dynamic_init_module_name) + return; + + if (flags()->report_globals >= 3) + Printf("DynInitUnpoison\n"); + + DynInitGlobals().forEach([&](auto &kv) { + UnpoisonDynamicGlobals(kv.second, /*mark_initialized=*/false); + return true; + }); + + current_dynamic_init_module_name = nullptr; } diff --git a/libsanitizer/asan/asan_globals_win.cpp b/libsanitizer/asan/asan_globals_win.cpp index 19af88a..2b59595 100644 --- a/libsanitizer/asan/asan_globals_win.cpp +++ b/libsanitizer/asan/asan_globals_win.cpp @@ -17,10 +17,10 @@ namespace __asan { #pragma section(".ASAN$GA", read, write) #pragma section(".ASAN$GZ", read, write) -extern "C" __declspec(allocate(".ASAN$GA")) - ALIGNED(sizeof(__asan_global)) __asan_global __asan_globals_start = {}; -extern "C" __declspec(allocate(".ASAN$GZ")) - ALIGNED(sizeof(__asan_global)) __asan_global __asan_globals_end = {}; +extern "C" alignas(sizeof(__asan_global)) + __declspec(allocate(".ASAN$GA")) __asan_global __asan_globals_start = {}; +extern "C" alignas(sizeof(__asan_global)) + __declspec(allocate(".ASAN$GZ")) __asan_global __asan_globals_end = {}; #pragma comment(linker, "/merge:.ASAN=.data") static void call_on_globals(void (*hook)(__asan_global *, uptr)) { @@ -28,7 +28,9 @@ static void call_on_globals(void (*hook)(__asan_global *, uptr)) { __asan_global *end = &__asan_globals_end; uptr bytediff = (uptr)end - (uptr)start; if (bytediff % sizeof(__asan_global) != 0) { -#if defined(SANITIZER_DLL_THUNK) || defined(SANITIZER_DYNAMIC_RUNTIME_THUNK) +# if defined(SANITIZER_DLL_THUNK) || \ + defined(SANITIZER_DYNAMIC_RUNTIME_THUNK) || \ + defined(SANITIZER_STATIC_RUNTIME_THUNK) __debugbreak(); #else CHECK("corrupt asan global array"); diff --git a/libsanitizer/asan/asan_interceptors.cpp b/libsanitizer/asan/asan_interceptors.cpp index 635546b..c13bcf2 100644 --- a/libsanitizer/asan/asan_interceptors.cpp +++ b/libsanitizer/asan/asan_interceptors.cpp @@ -96,14 +96,16 @@ DECLARE_REAL_AND_INTERCEPTOR(void, free, void *) ASAN_WRITE_RANGE(ctx, ptr, size) #define COMMON_INTERCEPTOR_READ_RANGE(ctx, ptr, size) \ ASAN_READ_RANGE(ctx, ptr, size) -# define COMMON_INTERCEPTOR_ENTER(ctx, func, ...) \ - ASAN_INTERCEPTOR_ENTER(ctx, func); \ - do { \ - if (AsanInitIsRunning()) \ - return REAL(func)(__VA_ARGS__); \ - if (SANITIZER_APPLE && UNLIKELY(!AsanInited())) \ - return REAL(func)(__VA_ARGS__); \ - ENSURE_ASAN_INITED(); \ +# define COMMON_INTERCEPTOR_ENTER(ctx, func, ...) \ + ASAN_INTERCEPTOR_ENTER(ctx, func); \ + do { \ + if constexpr (SANITIZER_APPLE) { \ + if (UNLIKELY(!AsanInited())) \ + return REAL(func)(__VA_ARGS__); \ + } else { \ + if (!TryAsanInitFromRtl()) \ + return REAL(func)(__VA_ARGS__); \ + } \ } while (false) #define COMMON_INTERCEPTOR_DIR_ACQUIRE(ctx, path) \ do { \ @@ -194,10 +196,13 @@ static int munmap_interceptor(Munmap real_munmap, void *addr, SIZE_T length) { __lsan::ScopedInterceptorDisabler disabler #endif -#define SIGNAL_INTERCEPTOR_ENTER() ENSURE_ASAN_INITED() +# define SIGNAL_INTERCEPTOR_ENTER() \ + do { \ + AsanInitFromRtl(); \ + } while (false) -#include "sanitizer_common/sanitizer_common_interceptors.inc" -#include "sanitizer_common/sanitizer_signal_interceptors.inc" +# include "sanitizer_common/sanitizer_common_interceptors.inc" +# include "sanitizer_common/sanitizer_signal_interceptors.inc" // Syscall interceptors don't have contexts, we don't support suppressions // for them. @@ -328,7 +333,7 @@ INTERCEPTOR(int, pthread_timedjoin_np, void *thread, void **ret, } # endif -DEFINE_REAL_PTHREAD_FUNCTIONS +DEFINE_INTERNAL_PTHREAD_FUNCTIONS #endif // ASAN_INTERCEPT_PTHREAD_CREATE #if ASAN_INTERCEPT_SWAPCONTEXT @@ -506,7 +511,7 @@ DEFINE_REAL(char*, index, const char *string, int c) INTERCEPTOR(char *, strcat, char *to, const char *from) { void *ctx; ASAN_INTERCEPTOR_ENTER(ctx, strcat); - ENSURE_ASAN_INITED(); + AsanInitFromRtl(); if (flags()->replace_str) { uptr from_length = internal_strlen(from); ASAN_READ_RANGE(ctx, from, from_length + 1); @@ -527,7 +532,7 @@ DEFINE_REAL(char*, index, const char *string, int c) INTERCEPTOR(char*, strncat, char *to, const char *from, uptr size) { void *ctx; ASAN_INTERCEPTOR_ENTER(ctx, strncat); - ENSURE_ASAN_INITED(); + AsanInitFromRtl(); if (flags()->replace_str) { uptr from_length = MaybeRealStrnlen(from, size); uptr copy_length = Min(size, from_length + 1); @@ -546,16 +551,16 @@ INTERCEPTOR(char*, strncat, char *to, const char *from, uptr size) { INTERCEPTOR(char *, strcpy, char *to, const char *from) { void *ctx; ASAN_INTERCEPTOR_ENTER(ctx, strcpy); -#if SANITIZER_APPLE - if (UNLIKELY(!AsanInited())) - return REAL(strcpy)(to, from); -#endif - // strcpy is called from malloc_default_purgeable_zone() - // in __asan::ReplaceSystemAlloc() on Mac. - if (AsanInitIsRunning()) { - return REAL(strcpy)(to, from); + if constexpr (SANITIZER_APPLE) { + // strcpy is called from malloc_default_purgeable_zone() + // in __asan::ReplaceSystemAlloc() on Mac. + if (UNLIKELY(!AsanInited())) + return REAL(strcpy)(to, from); + } else { + if (!TryAsanInitFromRtl()) + return REAL(strcpy)(to, from); } - ENSURE_ASAN_INITED(); + if (flags()->replace_str) { uptr from_size = internal_strlen(from) + 1; CHECK_RANGES_OVERLAP("strcpy", to, from_size, from, from_size); @@ -565,12 +570,22 @@ INTERCEPTOR(char *, strcpy, char *to, const char *from) { return REAL(strcpy)(to, from); } +// Windows doesn't always define the strdup identifier, +// and when it does it's a macro defined to either _strdup +// or _strdup_dbg, _strdup_dbg ends up calling _strdup, so +// we want to intercept that. push/pop_macro are used to avoid problems +// if this file ends up including <string.h> in the future. +# if SANITIZER_WINDOWS +# pragma push_macro("strdup") +# undef strdup +# define strdup _strdup +# endif + INTERCEPTOR(char*, strdup, const char *s) { void *ctx; ASAN_INTERCEPTOR_ENTER(ctx, strdup); - if (UNLIKELY(!AsanInited())) + if (UNLIKELY(!TryAsanInitFromRtl())) return internal_strdup(s); - ENSURE_ASAN_INITED(); uptr length = internal_strlen(s); if (flags()->replace_str) { ASAN_READ_RANGE(ctx, s, length + 1); @@ -583,13 +598,12 @@ INTERCEPTOR(char*, strdup, const char *s) { return reinterpret_cast<char*>(new_mem); } -#if ASAN_INTERCEPT___STRDUP +# if ASAN_INTERCEPT___STRDUP INTERCEPTOR(char*, __strdup, const char *s) { void *ctx; ASAN_INTERCEPTOR_ENTER(ctx, strdup); - if (UNLIKELY(!AsanInited())) + if (UNLIKELY(!TryAsanInitFromRtl())) return internal_strdup(s); - ENSURE_ASAN_INITED(); uptr length = internal_strlen(s); if (flags()->replace_str) { ASAN_READ_RANGE(ctx, s, length + 1); @@ -606,7 +620,7 @@ INTERCEPTOR(char*, __strdup, const char *s) { INTERCEPTOR(char*, strncpy, char *to, const char *from, uptr size) { void *ctx; ASAN_INTERCEPTOR_ENTER(ctx, strncpy); - ENSURE_ASAN_INITED(); + AsanInitFromRtl(); if (flags()->replace_str) { uptr from_size = Min(size, MaybeRealStrnlen(from, size) + 1); CHECK_RANGES_OVERLAP("strncpy", to, from_size, from, from_size); @@ -632,13 +646,38 @@ static ALWAYS_INLINE auto StrtolImpl(void *ctx, Fn real, const char *nptr, INTERCEPTOR(ret_type, func, const char *nptr, char **endptr, int base) { \ void *ctx; \ ASAN_INTERCEPTOR_ENTER(ctx, func); \ - ENSURE_ASAN_INITED(); \ + AsanInitFromRtl(); \ return StrtolImpl(ctx, REAL(func), nptr, endptr, base); \ } -INTERCEPTOR_STRTO_BASE(long, strtol) INTERCEPTOR_STRTO_BASE(long long, strtoll) +# if SANITIZER_WINDOWS +INTERCEPTOR(long, strtol, const char *nptr, char **endptr, int base) { + // REAL(strtol) may be ntdll!strtol, which doesn't set errno. Instead, + // call REAL(strtoll) and do the range check ourselves. + COMPILER_CHECK(sizeof(long) == sizeof(u32)); + + void *ctx; + ASAN_INTERCEPTOR_ENTER(ctx, strtol); + AsanInitFromRtl(); + + long long result = StrtolImpl(ctx, REAL(strtoll), nptr, endptr, base); + + if (result > INT32_MAX) { + errno = errno_ERANGE; + return INT32_MAX; + } + if (result < INT32_MIN) { + errno = errno_ERANGE; + return INT32_MIN; + } + return (long)result; +} +# else +INTERCEPTOR_STRTO_BASE(long, strtol) +# endif + # if SANITIZER_GLIBC INTERCEPTOR_STRTO_BASE(long, __isoc23_strtol) INTERCEPTOR_STRTO_BASE(long long, __isoc23_strtoll) @@ -647,11 +686,9 @@ INTERCEPTOR_STRTO_BASE(long long, __isoc23_strtoll) INTERCEPTOR(int, atoi, const char *nptr) { void *ctx; ASAN_INTERCEPTOR_ENTER(ctx, atoi); -#if SANITIZER_APPLE - if (UNLIKELY(!AsanInited())) + if (SANITIZER_APPLE && UNLIKELY(!AsanInited())) return REAL(atoi)(nptr); -# endif - ENSURE_ASAN_INITED(); + AsanInitFromRtl(); if (!flags()->replace_str) { return REAL(atoi)(nptr); } @@ -669,11 +706,9 @@ INTERCEPTOR(int, atoi, const char *nptr) { INTERCEPTOR(long, atol, const char *nptr) { void *ctx; ASAN_INTERCEPTOR_ENTER(ctx, atol); -#if SANITIZER_APPLE - if (UNLIKELY(!AsanInited())) + if (SANITIZER_APPLE && UNLIKELY(!AsanInited())) return REAL(atol)(nptr); -# endif - ENSURE_ASAN_INITED(); + AsanInitFromRtl(); if (!flags()->replace_str) { return REAL(atol)(nptr); } @@ -687,7 +722,7 @@ INTERCEPTOR(long, atol, const char *nptr) { INTERCEPTOR(long long, atoll, const char *nptr) { void *ctx; ASAN_INTERCEPTOR_ENTER(ctx, atoll); - ENSURE_ASAN_INITED(); + AsanInitFromRtl(); if (!flags()->replace_str) { return REAL(atoll)(nptr); } @@ -708,12 +743,10 @@ static void AtCxaAtexit(void *unused) { #if ASAN_INTERCEPT___CXA_ATEXIT INTERCEPTOR(int, __cxa_atexit, void (*func)(void *), void *arg, void *dso_handle) { -#if SANITIZER_APPLE - if (UNLIKELY(!AsanInited())) + if (SANITIZER_APPLE && UNLIKELY(!AsanInited())) return REAL(__cxa_atexit)(func, arg, dso_handle); -# endif - ENSURE_ASAN_INITED(); -#if CAN_SANITIZE_LEAKS + AsanInitFromRtl(); +# if CAN_SANITIZE_LEAKS __lsan::ScopedInterceptorDisabler disabler; #endif int res = REAL(__cxa_atexit)(func, arg, dso_handle); @@ -724,8 +757,8 @@ INTERCEPTOR(int, __cxa_atexit, void (*func)(void *), void *arg, #if ASAN_INTERCEPT_ATEXIT INTERCEPTOR(int, atexit, void (*func)()) { - ENSURE_ASAN_INITED(); -#if CAN_SANITIZE_LEAKS + AsanInitFromRtl(); +# if CAN_SANITIZE_LEAKS __lsan::ScopedInterceptorDisabler disabler; #endif // Avoid calling real atexit as it is unreachable on at least on Linux. @@ -739,7 +772,7 @@ INTERCEPTOR(int, atexit, void (*func)()) { extern "C" { extern int _pthread_atfork(void (*prepare)(), void (*parent)(), void (*child)()); -}; +} INTERCEPTOR(int, pthread_atfork, void (*prepare)(), void (*parent)(), void (*child)()) { @@ -753,8 +786,8 @@ INTERCEPTOR(int, pthread_atfork, void (*prepare)(), void (*parent)(), #endif #if ASAN_INTERCEPT_VFORK -DEFINE_REAL(int, vfork) -DECLARE_EXTERN_INTERCEPTOR_AND_WRAPPER(int, vfork) +DEFINE_REAL(int, vfork,) +DECLARE_EXTERN_INTERCEPTOR_AND_WRAPPER(int, vfork,) #endif // ---------------------- InitializeAsanInterceptors ---------------- {{{1 @@ -773,7 +806,7 @@ void InitializeAsanInterceptors() { ASAN_INTERCEPT_FUNC(strncat); ASAN_INTERCEPT_FUNC(strncpy); ASAN_INTERCEPT_FUNC(strdup); -#if ASAN_INTERCEPT___STRDUP +# if ASAN_INTERCEPT___STRDUP ASAN_INTERCEPT_FUNC(__strdup); #endif #if ASAN_INTERCEPT_INDEX && ASAN_USE_ALIAS_ATTRIBUTE_FOR_INDEX @@ -869,6 +902,10 @@ void InitializeAsanInterceptors() { VReport(1, "AddressSanitizer: libc interceptors initialized\n"); } +# if SANITIZER_WINDOWS +# pragma pop_macro("strdup") +# endif + } // namespace __asan #endif // !SANITIZER_FUCHSIA diff --git a/libsanitizer/asan/asan_interceptors.h b/libsanitizer/asan/asan_interceptors.h index 8e9ed5d..826b45f 100644 --- a/libsanitizer/asan/asan_interceptors.h +++ b/libsanitizer/asan/asan_interceptors.h @@ -24,14 +24,6 @@ namespace __asan { void InitializeAsanInterceptors(); void InitializePlatformInterceptors(); -#define ENSURE_ASAN_INITED() \ - do { \ - CHECK(!AsanInitIsRunning()); \ - if (UNLIKELY(!AsanInited())) { \ - AsanInitFromRtl(); \ - } \ - } while (0) - } // namespace __asan // There is no general interception at all on Fuchsia. @@ -79,12 +71,7 @@ void InitializePlatformInterceptors(); #if ASAN_HAS_EXCEPTIONS && !SANITIZER_SOLARIS && !SANITIZER_NETBSD && \ (!SANITIZER_WINDOWS || (defined(__MINGW32__) && defined(__i386__))) # define ASAN_INTERCEPT___CXA_THROW 1 -# if ! defined(ASAN_HAS_CXA_RETHROW_PRIMARY_EXCEPTION) \ - || ASAN_HAS_CXA_RETHROW_PRIMARY_EXCEPTION -# define ASAN_INTERCEPT___CXA_RETHROW_PRIMARY_EXCEPTION 1 -# else -# define ASAN_INTERCEPT___CXA_RETHROW_PRIMARY_EXCEPTION 0 -# endif +# define ASAN_INTERCEPT___CXA_RETHROW_PRIMARY_EXCEPTION 1 # if defined(_GLIBCXX_SJLJ_EXCEPTIONS) || (SANITIZER_IOS && defined(__arm__)) # define ASAN_INTERCEPT__UNWIND_SJLJ_RAISEEXCEPTION 1 # else diff --git a/libsanitizer/asan/asan_internal.h b/libsanitizer/asan/asan_internal.h index e2b1e98..06dfc4b 100644 --- a/libsanitizer/asan/asan_internal.h +++ b/libsanitizer/asan/asan_internal.h @@ -60,6 +60,7 @@ class AsanThread; using __sanitizer::StackTrace; void AsanInitFromRtl(); +bool TryAsanInitFromRtl(); // asan_win.cpp void InitializePlatformExceptionHandlers(); @@ -79,7 +80,6 @@ void ReplaceSystemMalloc(); // asan_linux.cpp / asan_mac.cpp / asan_win.cpp uptr FindDynamicShadowStart(); -void *AsanDoesNotSupportStaticLinkage(); void AsanCheckDynamicRTPrereqs(); void AsanCheckIncompatibleRT(); @@ -125,13 +125,13 @@ void *AsanDlSymNext(const char *sym); bool HandleDlopenInit(); void InstallAtExitCheckLeaks(); +void InstallAtForkHandler(); #define ASAN_ON_ERROR() \ if (&__asan_on_error) \ __asan_on_error() bool AsanInited(); -bool AsanInitIsRunning(); // Used to avoid infinite recursion in __asan_init(). extern bool replace_intrin_cached; extern void (*death_callback)(void); // These magic values are written to shadow for better error diff --git a/libsanitizer/asan/asan_linux.cpp b/libsanitizer/asan/asan_linux.cpp index e19b447..4cabca3 100644 --- a/libsanitizer/asan/asan_linux.cpp +++ b/libsanitizer/asan/asan_linux.cpp @@ -33,7 +33,6 @@ # include "asan_premap_shadow.h" # include "asan_thread.h" # include "sanitizer_common/sanitizer_flags.h" -# include "sanitizer_common/sanitizer_freebsd.h" # include "sanitizer_common/sanitizer_hash.h" # include "sanitizer_common/sanitizer_libc.h" # include "sanitizer_common/sanitizer_procmaps.h" @@ -48,22 +47,12 @@ # if SANITIZER_ANDROID || SANITIZER_FREEBSD || SANITIZER_SOLARIS # include <ucontext.h> -extern "C" void *_DYNAMIC; # elif SANITIZER_NETBSD # include <link_elf.h> # include <ucontext.h> -extern Elf_Dyn _DYNAMIC; # else # include <link.h> # include <sys/ucontext.h> -extern ElfW(Dyn) _DYNAMIC[]; -# endif - -// x86-64 FreeBSD 9.2 and older define 'ucontext_t' incorrectly in -// 32-bit mode. -# if SANITIZER_FREEBSD && (SANITIZER_WORDSIZE == 32) && \ - __FreeBSD_version <= 902001 // v9.2 -# define ucontext_t xucontext_t # endif typedef enum { @@ -84,11 +73,6 @@ void InitializePlatformInterceptors() {} void InitializePlatformExceptionHandlers() {} bool IsSystemHeapAddress(uptr addr) { return false; } -void *AsanDoesNotSupportStaticLinkage() { - // This will fail to link with -static. - return &_DYNAMIC; -} - # if ASAN_PREMAP_SHADOW uptr FindPremappedShadowStart(uptr shadow_size_bytes) { uptr granularity = GetMmapGranularity(); @@ -109,7 +93,8 @@ uptr FindDynamicShadowStart() { # endif return MapDynamicShadow(shadow_size_bytes, ASAN_SHADOW_SCALE, - /*min_shadow_base_alignment*/ 0, kHighMemEnd); + /*min_shadow_base_alignment*/ 0, kHighMemEnd, + GetMmapGranularity()); } void AsanApplyToGlobals(globals_op_fptr op, const void *needle) { @@ -148,6 +133,11 @@ static int FindFirstDSOCallback(struct dl_phdr_info *info, size_t size, internal_strncmp(info->dlpi_name, "linux-", sizeof("linux-") - 1) == 0) return 0; # endif +# if SANITIZER_FREEBSD + // Ignore vDSO. + if (internal_strcmp(info->dlpi_name, "[vdso]") == 0) + return 0; +# endif *name = info->dlpi_name; return 1; @@ -197,10 +187,7 @@ void AsanCheckIncompatibleRT() { MemoryMappedSegment segment(filename, sizeof(filename)); while (proc_maps.Next(&segment)) { if (IsDynamicRTName(segment.filename)) { - Report( - "Your application is linked against " - "incompatible ASan runtimes.\n"); - Die(); + ReportIncompatibleRT(); } } __asan_rt_version = ASAN_RT_VERSION_STATIC; diff --git a/libsanitizer/asan/asan_lock.h b/libsanitizer/asan/asan_lock.h deleted file mode 100644 index e69de29..0000000 --- a/libsanitizer/asan/asan_lock.h +++ /dev/null diff --git a/libsanitizer/asan/asan_mac.cpp b/libsanitizer/asan/asan_mac.cpp index 6252fa2..bfc3492 100644 --- a/libsanitizer/asan/asan_mac.cpp +++ b/libsanitizer/asan/asan_mac.cpp @@ -49,14 +49,10 @@ void InitializePlatformInterceptors() {} void InitializePlatformExceptionHandlers() {} bool IsSystemHeapAddress (uptr addr) { return false; } -// No-op. Mac does not support static linkage anyway. -void *AsanDoesNotSupportStaticLinkage() { - return 0; -} - uptr FindDynamicShadowStart() { return MapDynamicShadow(MemToShadowSize(kHighMemEnd), ASAN_SHADOW_SCALE, - /*min_shadow_base_alignment*/ 0, kHighMemEnd); + /*min_shadow_base_alignment*/ 0, kHighMemEnd, + GetMmapGranularity()); } // No-op. Mac does not support static linkage anyway. @@ -139,11 +135,11 @@ typedef void (*dispatch_mach_handler_function_t)(void *context, dispatch_mach_reason reason, dispatch_mach_msg_t message, mach_error_t error); -#if !defined(MISSING_BLOCKS_SUPPORT) +# if !defined(MISSING_BLOCKS_SUPPORT) typedef void (^dispatch_mach_handler_t)(dispatch_mach_reason reason, dispatch_mach_msg_t message, mach_error_t error); -#endif +# endif // A wrapper for the ObjC blocks used to support libdispatch. typedef struct { diff --git a/libsanitizer/asan/asan_malloc_linux.cpp b/libsanitizer/asan/asan_malloc_linux.cpp index 0ba74c5..3d6b03f 100644 --- a/libsanitizer/asan/asan_malloc_linux.cpp +++ b/libsanitizer/asan/asan_malloc_linux.cpp @@ -25,13 +25,12 @@ # include "sanitizer_common/sanitizer_allocator_checks.h" # include "sanitizer_common/sanitizer_allocator_dlsym.h" # include "sanitizer_common/sanitizer_errno.h" -# include "sanitizer_common/sanitizer_tls_get_addr.h" // ---------------------- Replacement functions ---------------- {{{1 using namespace __asan; struct DlsymAlloc : public DlSymAllocator<DlsymAlloc> { - static bool UseImpl() { return AsanInitIsRunning(); } + static bool UseImpl() { return !TryAsanInitFromRtl(); } static void OnAllocate(const void *ptr, uptr size) { # if CAN_SANITIZE_LEAKS // Suppress leaks from dlerror(). Previously dlsym hack on global array was @@ -65,7 +64,6 @@ INTERCEPTOR(void, cfree, void *ptr) { INTERCEPTOR(void*, malloc, uptr size) { if (DlsymAlloc::Use()) return DlsymAlloc::Allocate(size); - ENSURE_ASAN_INITED(); GET_STACK_TRACE_MALLOC; return asan_malloc(size, &stack); } @@ -73,7 +71,6 @@ INTERCEPTOR(void*, malloc, uptr size) { INTERCEPTOR(void*, calloc, uptr nmemb, uptr size) { if (DlsymAlloc::Use()) return DlsymAlloc::Callocate(nmemb, size); - ENSURE_ASAN_INITED(); GET_STACK_TRACE_MALLOC; return asan_calloc(nmemb, size, &stack); } @@ -81,14 +78,13 @@ INTERCEPTOR(void*, calloc, uptr nmemb, uptr size) { INTERCEPTOR(void*, realloc, void *ptr, uptr size) { if (DlsymAlloc::Use() || DlsymAlloc::PointerIsMine(ptr)) return DlsymAlloc::Realloc(ptr, size); - ENSURE_ASAN_INITED(); GET_STACK_TRACE_MALLOC; return asan_realloc(ptr, size, &stack); } #if SANITIZER_INTERCEPT_REALLOCARRAY INTERCEPTOR(void*, reallocarray, void *ptr, uptr nmemb, uptr size) { - ENSURE_ASAN_INITED(); + AsanInitFromRtl(); GET_STACK_TRACE_MALLOC; return asan_reallocarray(ptr, nmemb, size, &stack); } @@ -102,9 +98,7 @@ INTERCEPTOR(void*, memalign, uptr boundary, uptr size) { INTERCEPTOR(void*, __libc_memalign, uptr boundary, uptr size) { GET_STACK_TRACE_MALLOC; - void *res = asan_memalign(boundary, size, &stack, FROM_MALLOC); - DTLS_on_libc_memalign(res, size); - return res; + return asan_memalign(boundary, size, &stack, FROM_MALLOC); } #endif // SANITIZER_INTERCEPT_MEMALIGN @@ -188,11 +182,11 @@ struct MallocDebugL { void* (*valloc)(uptr size); }; -ALIGNED(32) const MallocDebugK asan_malloc_dispatch_k = { +alignas(32) const MallocDebugK asan_malloc_dispatch_k = { WRAP(malloc), WRAP(free), WRAP(calloc), WRAP(realloc), WRAP(memalign), WRAP(malloc_usable_size)}; -ALIGNED(32) const MallocDebugL asan_malloc_dispatch_l = { +alignas(32) const MallocDebugL asan_malloc_dispatch_l = { WRAP(calloc), WRAP(free), WRAP(mallinfo), WRAP(malloc), WRAP(malloc_usable_size), WRAP(memalign), WRAP(posix_memalign), WRAP(pvalloc), WRAP(realloc), diff --git a/libsanitizer/asan/asan_malloc_mac.cpp b/libsanitizer/asan/asan_malloc_mac.cpp index d2380ee..f25d7e1 100644 --- a/libsanitizer/asan/asan_malloc_mac.cpp +++ b/libsanitizer/asan/asan_malloc_mac.cpp @@ -22,7 +22,10 @@ using namespace __asan; #define COMMON_MALLOC_ZONE_NAME "asan" -#define COMMON_MALLOC_ENTER() ENSURE_ASAN_INITED() +# define COMMON_MALLOC_ENTER() \ + do { \ + AsanInitFromRtl(); \ + } while (false) # define COMMON_MALLOC_SANITIZER_INITIALIZED AsanInited() # define COMMON_MALLOC_FORCE_LOCK() asan_mz_force_lock() # define COMMON_MALLOC_FORCE_UNLOCK() asan_mz_force_unlock() diff --git a/libsanitizer/asan/asan_malloc_win.cpp b/libsanitizer/asan/asan_malloc_win.cpp index 7e1d04c..3278f07 100644 --- a/libsanitizer/asan/asan_malloc_win.cpp +++ b/libsanitizer/asan/asan_malloc_win.cpp @@ -58,97 +58,69 @@ using namespace __asan; // MD: Memory allocation functions are defined in the CRT .dll, // so we have to intercept them before they are called for the first time. -#if ASAN_DYNAMIC -# define ALLOCATION_FUNCTION_ATTRIBUTE -#else -# define ALLOCATION_FUNCTION_ATTRIBUTE SANITIZER_INTERFACE_ATTRIBUTE -#endif - extern "C" { -ALLOCATION_FUNCTION_ATTRIBUTE -size_t _msize(void *ptr) { +__declspec(noinline) size_t _msize(void *ptr) { GET_CURRENT_PC_BP_SP; (void)sp; return asan_malloc_usable_size(ptr, pc, bp); } -ALLOCATION_FUNCTION_ATTRIBUTE -size_t _msize_base(void *ptr) { - return _msize(ptr); -} +__declspec(noinline) size_t _msize_base(void *ptr) { return _msize(ptr); } -ALLOCATION_FUNCTION_ATTRIBUTE -void free(void *ptr) { +__declspec(noinline) void free(void *ptr) { GET_STACK_TRACE_FREE; return asan_free(ptr, &stack, FROM_MALLOC); } -ALLOCATION_FUNCTION_ATTRIBUTE -void _free_dbg(void *ptr, int) { - free(ptr); -} +__declspec(noinline) void _free_dbg(void *ptr, int) { free(ptr); } -ALLOCATION_FUNCTION_ATTRIBUTE -void _free_base(void *ptr) { - free(ptr); -} +__declspec(noinline) void _free_base(void *ptr) { free(ptr); } -ALLOCATION_FUNCTION_ATTRIBUTE -void *malloc(size_t size) { +__declspec(noinline) void *malloc(size_t size) { GET_STACK_TRACE_MALLOC; return asan_malloc(size, &stack); } -ALLOCATION_FUNCTION_ATTRIBUTE -void *_malloc_base(size_t size) { - return malloc(size); -} +__declspec(noinline) void *_malloc_base(size_t size) { return malloc(size); } -ALLOCATION_FUNCTION_ATTRIBUTE -void *_malloc_dbg(size_t size, int, const char *, int) { +__declspec(noinline) void *_malloc_dbg(size_t size, int, const char *, int) { return malloc(size); } -ALLOCATION_FUNCTION_ATTRIBUTE -void *calloc(size_t nmemb, size_t size) { +__declspec(noinline) void *calloc(size_t nmemb, size_t size) { GET_STACK_TRACE_MALLOC; return asan_calloc(nmemb, size, &stack); } -ALLOCATION_FUNCTION_ATTRIBUTE -void *_calloc_base(size_t nmemb, size_t size) { +__declspec(noinline) void *_calloc_base(size_t nmemb, size_t size) { return calloc(nmemb, size); } -ALLOCATION_FUNCTION_ATTRIBUTE -void *_calloc_dbg(size_t nmemb, size_t size, int, const char *, int) { +__declspec(noinline) void *_calloc_dbg(size_t nmemb, size_t size, int, + const char *, int) { return calloc(nmemb, size); } -ALLOCATION_FUNCTION_ATTRIBUTE -void *_calloc_impl(size_t nmemb, size_t size, int *errno_tmp) { +__declspec(noinline) void *_calloc_impl(size_t nmemb, size_t size, + int *errno_tmp) { return calloc(nmemb, size); } -ALLOCATION_FUNCTION_ATTRIBUTE -void *realloc(void *ptr, size_t size) { +__declspec(noinline) void *realloc(void *ptr, size_t size) { GET_STACK_TRACE_MALLOC; return asan_realloc(ptr, size, &stack); } -ALLOCATION_FUNCTION_ATTRIBUTE -void *_realloc_dbg(void *ptr, size_t size, int) { +__declspec(noinline) void *_realloc_dbg(void *ptr, size_t size, int) { UNREACHABLE("_realloc_dbg should not exist!"); return 0; } -ALLOCATION_FUNCTION_ATTRIBUTE -void *_realloc_base(void *ptr, size_t size) { +__declspec(noinline) void *_realloc_base(void *ptr, size_t size) { return realloc(ptr, size); } -ALLOCATION_FUNCTION_ATTRIBUTE -void *_recalloc(void *p, size_t n, size_t elem_size) { +__declspec(noinline) void *_recalloc(void *p, size_t n, size_t elem_size) { if (!p) return calloc(n, elem_size); const size_t size = n * elem_size; @@ -166,23 +138,41 @@ void *_recalloc(void *p, size_t n, size_t elem_size) { return new_alloc; } -ALLOCATION_FUNCTION_ATTRIBUTE -void *_recalloc_base(void *p, size_t n, size_t elem_size) { +__declspec(noinline) void *_recalloc_base(void *p, size_t n, size_t elem_size) { return _recalloc(p, n, elem_size); } -ALLOCATION_FUNCTION_ATTRIBUTE -void *_expand(void *memblock, size_t size) { +__declspec(noinline) void *_expand(void *memblock, size_t size) { // _expand is used in realloc-like functions to resize the buffer if possible. // We don't want memory to stand still while resizing buffers, so return 0. return 0; } -ALLOCATION_FUNCTION_ATTRIBUTE -void *_expand_dbg(void *memblock, size_t size) { +__declspec(noinline) void *_expand_dbg(void *memblock, size_t size) { return _expand(memblock, size); } +__declspec(dllexport) size_t __cdecl __asan_msize(void *ptr) { + return _msize(ptr); +} +__declspec(dllexport) void __cdecl __asan_free(void *const ptr) { free(ptr); } +__declspec(dllexport) void *__cdecl __asan_malloc(const size_t size) { + return malloc(size); +} +__declspec(dllexport) void *__cdecl __asan_calloc(const size_t nmemb, + const size_t size) { + return calloc(nmemb, size); +} +__declspec(dllexport) void *__cdecl __asan_realloc(void *const ptr, + const size_t size) { + return realloc(ptr, size); +} +__declspec(dllexport) void *__cdecl __asan_recalloc(void *const ptr, + const size_t nmemb, + const size_t size) { + return _recalloc(ptr, nmemb, size); +} + // TODO(timurrrr): Might want to add support for _aligned_* allocation // functions to detect a bit more bugs. Those functions seem to wrap malloc(). @@ -487,7 +477,6 @@ static void TryToOverrideFunction(const char *fname, uptr new_func) { } void ReplaceSystemMalloc() { -#if defined(ASAN_DYNAMIC) TryToOverrideFunction("free", (uptr)free); TryToOverrideFunction("_free_base", (uptr)free); TryToOverrideFunction("malloc", (uptr)malloc); @@ -543,8 +532,6 @@ void ReplaceSystemMalloc() { // allocation API will be directed to ASan's heap. We don't currently // intercept all calls to HeapAlloc. If we did, we would have to check on // HeapFree whether the pointer came from ASan of from the system. - -#endif // defined(ASAN_DYNAMIC) } } // namespace __asan diff --git a/libsanitizer/asan/asan_malloc_win_thunk.cpp b/libsanitizer/asan/asan_malloc_win_thunk.cpp new file mode 100644 index 0000000..9cc0091 --- /dev/null +++ b/libsanitizer/asan/asan_malloc_win_thunk.cpp @@ -0,0 +1,229 @@ +//===-- asan_malloc_win_thunk.cpp +//-----------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This file is a part of AddressSanitizer, an address sanity checker. +// +// Windows-specific malloc interception. +// This is included statically for projects statically linking +// with the C Runtime (/MT, /MTd) in order to provide ASAN-aware +// versions of the C allocation functions. +//===----------------------------------------------------------------------===// + +#ifdef SANITIZER_STATIC_RUNTIME_THUNK +# include "..\sanitizer_common\sanitizer_allocator_interface.h" +// #include "asan_win_thunk_common.h" + +// Preserve stack traces with noinline. +# define STATIC_MALLOC_INTERFACE __declspec(noinline) + +extern "C" { +__declspec(dllimport) size_t __cdecl __asan_msize(void *ptr); +__declspec(dllimport) void __cdecl __asan_free(void *const ptr); +__declspec(dllimport) void *__cdecl __asan_malloc(const size_t size); +__declspec(dllimport) void *__cdecl __asan_calloc(const size_t nmemb, + const size_t size); +__declspec(dllimport) void *__cdecl __asan_realloc(void *const ptr, + const size_t size); +__declspec(dllimport) void *__cdecl __asan_recalloc(void *const ptr, + const size_t nmemb, + const size_t size); + +// Avoid tailcall optimization to preserve stack frames. +# pragma optimize("", off) + +// _msize +STATIC_MALLOC_INTERFACE size_t _msize(void *ptr) { return __asan_msize(ptr); } + +STATIC_MALLOC_INTERFACE size_t _msize_base(void *ptr) { + return __asan_msize(ptr); +} + +STATIC_MALLOC_INTERFACE size_t _msize_dbg(void *ptr) { + return __asan_msize(ptr); +} + +// free +STATIC_MALLOC_INTERFACE void free(void *const ptr) { return __asan_free(ptr); } + +STATIC_MALLOC_INTERFACE void _free_base(void *const ptr) { + return __asan_free(ptr); +} + +STATIC_MALLOC_INTERFACE void _free_dbg(void *const ptr) { + return __asan_free(ptr); +} + +// malloc +STATIC_MALLOC_INTERFACE void *malloc(const size_t size) { + return __asan_malloc(size); +} + +STATIC_MALLOC_INTERFACE void *_malloc_base(const size_t size) { + return __asan_malloc(size); +} + +STATIC_MALLOC_INTERFACE void *_malloc_dbg(const size_t size) { + return __asan_malloc(size); +} + +// calloc +STATIC_MALLOC_INTERFACE void *calloc(const size_t nmemb, const size_t size) { + return __asan_calloc(nmemb, size); +} + +STATIC_MALLOC_INTERFACE void *_calloc_base(const size_t nmemb, + const size_t size) { + return __asan_calloc(nmemb, size); +} + +STATIC_MALLOC_INTERFACE void *_calloc_impl(const size_t nmemb, + const size_t size, + int *const errno_tmp) { + // Provided by legacy msvcrt. + (void)errno_tmp; + + return __asan_calloc(nmemb, size); +} + +STATIC_MALLOC_INTERFACE void *_calloc_dbg(const size_t nmemb, const size_t size, + int, const char *, int) { + return __asan_calloc(nmemb, size); +} + +// realloc +STATIC_MALLOC_INTERFACE void *realloc(void *const ptr, const size_t size) { + return __asan_realloc(ptr, size); +} + +STATIC_MALLOC_INTERFACE void *_realloc_base(void *const ptr, + const size_t size) { + return __asan_realloc(ptr, size); +} + +STATIC_MALLOC_INTERFACE void *_realloc_dbg(void *const ptr, const size_t size, + int, const char *, int) { + return __asan_realloc(ptr, size); +} + +// recalloc +STATIC_MALLOC_INTERFACE void *_recalloc(void *const ptr, const size_t nmemb, + const size_t size) { + return __asan_recalloc(ptr, nmemb, size); +} + +STATIC_MALLOC_INTERFACE void *_recalloc_base(void *const ptr, + const size_t nmemb, + const size_t size) { + return __asan_recalloc(ptr, nmemb, size); +} + +STATIC_MALLOC_INTERFACE void *_recalloc_dbg(void *const ptr, const size_t nmemb, + const size_t size, int, + const char *, int) { + return __asan_recalloc(ptr, nmemb, size); +} + +// expand +STATIC_MALLOC_INTERFACE void *_expand(void *, size_t) { + // _expand is used in realloc-like functions to resize the buffer if possible. + // We don't want memory to stand still while resizing buffers, so return 0. + return nullptr; +} + +STATIC_MALLOC_INTERFACE void *_expand_dbg(void *, size_t, int, const char *, + int) { + return nullptr; +} + +// We need to provide symbols for all the debug CRT functions if we decide to +// provide any. Most of these functions make no sense under ASan and so we +// make them no-ops. +long _CrtSetBreakAlloc(long const) { return ~0; } + +void _CrtSetDbgBlockType(void *const, int const) { return; } + +typedef int(__cdecl *CRT_ALLOC_HOOK)(int, void *, size_t, int, long, + const unsigned char *, int); + +CRT_ALLOC_HOOK _CrtGetAllocHook() { return nullptr; } + +CRT_ALLOC_HOOK _CrtSetAllocHook(CRT_ALLOC_HOOK const hook) { return hook; } + +int _CrtCheckMemory() { return 1; } + +int _CrtSetDbgFlag(int const new_bits) { return new_bits; } + +typedef void (*CrtDoForAllClientObjectsCallback)(void *, void *); + +void _CrtDoForAllClientObjects(CrtDoForAllClientObjectsCallback const, + void *const) { + return; +} + +int _CrtIsValidPointer(void const *const p, unsigned int const, int const) { + return p != nullptr; +} + +int _CrtIsValidHeapPointer(void const *const block) { + if (!block) { + return 0; + } + + return __sanitizer_get_ownership(block); +} + +int _CrtIsMemoryBlock(void const *const, unsigned const, long *const, + char **const, int *const) { + return 0; +} + +int _CrtReportBlockType(void const *const) { return -1; } + +typedef void(__cdecl *CRT_DUMP_CLIENT)(void *, size_t); + +CRT_DUMP_CLIENT _CrtGetDumpClient() { return nullptr; } + +CRT_DUMP_CLIENT _CrtSetDumpClient(CRT_DUMP_CLIENT new_client) { + return new_client; +} + +void _CrtMemCheckpoint(void *const) { return; } + +int _CrtMemDifference(void *const, void const *const, void const *const) { + return 0; +} + +void _CrtMemDumpAllObjectsSince(void const *const) { return; } + +int _CrtDumpMemoryLeaks() { return 0; } + +void _CrtMemDumpStatistics(void const *const) { return; } + +int _crtDbgFlag{0}; +long _crtBreakAlloc{-1}; +CRT_DUMP_CLIENT _pfnDumpClient{nullptr}; + +int *__p__crtDbgFlag() { return &_crtDbgFlag; } + +long *__p__crtBreakAlloc() { return &_crtBreakAlloc; } + +// TODO: These were added upstream but conflict with definitions in ucrtbased. +// int _CrtDbgReport(int, const char *, int, const char *, const char *, ...) { +// ShowStatsAndAbort(); +// } +// +// int _CrtDbgReportW(int reportType, const wchar_t *, int, const wchar_t *, +// const wchar_t *, ...) { +// ShowStatsAndAbort(); +// } +// +// int _CrtSetReportMode(int, int) { return 0; } + +} // extern "C" +#endif // SANITIZER_STATIC_RUNTIME_THUNK diff --git a/libsanitizer/asan/asan_mapping.h b/libsanitizer/asan/asan_mapping.h index 47ccf84..91fe60d 100644 --- a/libsanitizer/asan/asan_mapping.h +++ b/libsanitizer/asan/asan_mapping.h @@ -72,7 +72,10 @@ // || `[0x2000000000, 0x23ffffffff]` || LowShadow || // || `[0x0000000000, 0x1fffffffff]` || LowMem || // -// Default Linux/RISCV64 Sv39 mapping: +// Default Linux/RISCV64 Sv39 mapping with SHADOW_OFFSET == 0xd55550000; +// (the exact location of SHADOW_OFFSET may vary depending the dynamic probing +// by FindDynamicShadowStart). +// // || `[0x1555550000, 0x3fffffffff]` || HighMem || // || `[0x0fffffa000, 0x1555555fff]` || HighShadow || // || `[0x0effffa000, 0x0fffff9fff]` || ShadowGap || @@ -186,11 +189,11 @@ # elif SANITIZER_FREEBSD && defined(__aarch64__) # define ASAN_SHADOW_OFFSET_CONST 0x0000800000000000 # elif SANITIZER_RISCV64 -# define ASAN_SHADOW_OFFSET_CONST 0x0000000d55550000 +# define ASAN_SHADOW_OFFSET_DYNAMIC # elif defined(__aarch64__) # define ASAN_SHADOW_OFFSET_CONST 0x0000001000000000 # elif defined(__powerpc64__) -# define ASAN_SHADOW_OFFSET_CONST 0x0000020000000000 +# define ASAN_SHADOW_OFFSET_CONST 0x0000100000000000 # elif defined(__s390x__) # define ASAN_SHADOW_OFFSET_CONST 0x0010000000000000 # elif SANITIZER_FREEBSD diff --git a/libsanitizer/asan/asan_new_delete.cpp b/libsanitizer/asan/asan_new_delete.cpp index 1728012..b5b1ced 100644 --- a/libsanitizer/asan/asan_new_delete.cpp +++ b/libsanitizer/asan/asan_new_delete.cpp @@ -48,15 +48,6 @@ COMMENT_EXPORT("??_V@YAXPAX@Z") // operator delete[] using namespace __asan; -// FreeBSD prior v9.2 have wrong definition of 'size_t'. -// http://svnweb.freebsd.org/base?view=revision&revision=232261 -#if SANITIZER_FREEBSD && SANITIZER_WORDSIZE == 32 -#include <sys/param.h> -#if __FreeBSD_version <= 902001 // v9.2 -#define size_t unsigned -#endif // __FreeBSD_version -#endif // SANITIZER_FREEBSD && SANITIZER_WORDSIZE == 32 - // This code has issues on OSX. // See https://github.com/google/sanitizers/issues/131. diff --git a/libsanitizer/asan/asan_poisoning.cpp b/libsanitizer/asan/asan_poisoning.cpp index 746ad61..7626706 100644 --- a/libsanitizer/asan/asan_poisoning.cpp +++ b/libsanitizer/asan/asan_poisoning.cpp @@ -16,6 +16,7 @@ #include "asan_report.h" #include "asan_stack.h" #include "sanitizer_common/sanitizer_atomic.h" +#include "sanitizer_common/sanitizer_common.h" #include "sanitizer_common/sanitizer_flags.h" #include "sanitizer_common/sanitizer_interface_internal.h" #include "sanitizer_common/sanitizer_libc.h" @@ -410,7 +411,7 @@ void __sanitizer_annotate_contiguous_container(const void *beg_p, const void *new_mid_p) { if (!flags()->detect_container_overflow) return; - VPrintf(2, "contiguous_container: %p %p %p %p\n", beg_p, end_p, old_mid_p, + VPrintf(3, "contiguous_container: %p %p %p %p\n", beg_p, end_p, old_mid_p, new_mid_p); uptr storage_beg = reinterpret_cast<uptr>(beg_p); uptr storage_end = reinterpret_cast<uptr>(end_p); @@ -479,7 +480,7 @@ void __sanitizer_annotate_double_ended_contiguous_container( if (!flags()->detect_container_overflow) return; - VPrintf(2, "contiguous_container: %p %p %p %p %p %p\n", storage_beg_p, + VPrintf(3, "contiguous_container: %p %p %p %p %p %p\n", storage_beg_p, storage_end_p, old_container_beg_p, old_container_end_p, new_container_beg_p, new_container_end_p); @@ -576,6 +577,185 @@ void __sanitizer_annotate_double_ended_contiguous_container( } } +// Marks the specified number of bytes in a granule as accessible or +// poisones the whole granule with kAsanContiguousContainerOOBMagic value. +static void SetContainerGranule(uptr ptr, u8 n) { + constexpr uptr granularity = ASAN_SHADOW_GRANULARITY; + u8 s = (n == granularity) ? 0 : (n ? n : kAsanContiguousContainerOOBMagic); + *(u8 *)MemToShadow(ptr) = s; +} + +// Performs a byte-by-byte copy of ASan annotations (shadow memory values). +// Result may be different due to ASan limitations, but result cannot lead +// to false positives (more memory than requested may get unpoisoned). +static void SlowCopyContainerAnnotations(uptr src_beg, uptr src_end, + uptr dst_beg, uptr dst_end) { + constexpr uptr granularity = ASAN_SHADOW_GRANULARITY; + uptr dst_end_down = RoundDownTo(dst_end, granularity); + uptr src_ptr = src_beg; + uptr dst_ptr = dst_beg; + + while (dst_ptr < dst_end) { + uptr granule_beg = RoundDownTo(dst_ptr, granularity); + uptr granule_end = granule_beg + granularity; + uptr unpoisoned_bytes = 0; + + uptr end = Min(granule_end, dst_end); + for (; dst_ptr != end; ++dst_ptr, ++src_ptr) + if (!AddressIsPoisoned(src_ptr)) + unpoisoned_bytes = dst_ptr - granule_beg + 1; + + if (dst_ptr == dst_end && dst_end != dst_end_down && + !AddressIsPoisoned(dst_end)) + continue; + + if (unpoisoned_bytes != 0 || granule_beg >= dst_beg) + SetContainerGranule(granule_beg, unpoisoned_bytes); + else if (!AddressIsPoisoned(dst_beg)) + SetContainerGranule(granule_beg, dst_beg - granule_beg); + } +} + +// Performs a byte-by-byte copy of ASan annotations (shadow memory values), +// going through bytes in reversed order, but not reversing annotations. +// Result may be different due to ASan limitations, but result cannot lead +// to false positives (more memory than requested may get unpoisoned). +static void SlowReversedCopyContainerAnnotations(uptr src_beg, uptr src_end, + uptr dst_beg, uptr dst_end) { + constexpr uptr granularity = ASAN_SHADOW_GRANULARITY; + uptr dst_end_down = RoundDownTo(dst_end, granularity); + uptr src_ptr = src_end; + uptr dst_ptr = dst_end; + + while (dst_ptr > dst_beg) { + uptr granule_beg = RoundDownTo(dst_ptr - 1, granularity); + uptr unpoisoned_bytes = 0; + + uptr end = Max(granule_beg, dst_beg); + for (; dst_ptr != end; --dst_ptr, --src_ptr) + if (unpoisoned_bytes == 0 && !AddressIsPoisoned(src_ptr - 1)) + unpoisoned_bytes = dst_ptr - granule_beg; + + if (dst_ptr >= dst_end_down && !AddressIsPoisoned(dst_end)) + continue; + + if (granule_beg == dst_ptr || unpoisoned_bytes != 0) + SetContainerGranule(granule_beg, unpoisoned_bytes); + else if (!AddressIsPoisoned(dst_beg)) + SetContainerGranule(granule_beg, dst_beg - granule_beg); + } +} + +// A helper function for __sanitizer_copy_contiguous_container_annotations, +// has assumption about begin and end of the container. +// Should not be used stand alone. +static void CopyContainerFirstGranuleAnnotation(uptr src_beg, uptr dst_beg) { + constexpr uptr granularity = ASAN_SHADOW_GRANULARITY; + // First granule + uptr src_beg_down = RoundDownTo(src_beg, granularity); + uptr dst_beg_down = RoundDownTo(dst_beg, granularity); + if (dst_beg_down == dst_beg) + return; + if (!AddressIsPoisoned(src_beg)) + *(u8 *)MemToShadow(dst_beg_down) = *(u8 *)MemToShadow(src_beg_down); + else if (!AddressIsPoisoned(dst_beg)) + SetContainerGranule(dst_beg_down, dst_beg - dst_beg_down); +} + +// A helper function for __sanitizer_copy_contiguous_container_annotations, +// has assumption about begin and end of the container. +// Should not be used stand alone. +static void CopyContainerLastGranuleAnnotation(uptr src_end, uptr dst_end) { + constexpr uptr granularity = ASAN_SHADOW_GRANULARITY; + // Last granule + uptr src_end_down = RoundDownTo(src_end, granularity); + uptr dst_end_down = RoundDownTo(dst_end, granularity); + if (dst_end_down == dst_end || !AddressIsPoisoned(dst_end)) + return; + if (AddressIsPoisoned(src_end)) + *(u8 *)MemToShadow(dst_end_down) = *(u8 *)MemToShadow(src_end_down); + else + SetContainerGranule(dst_end_down, src_end - src_end_down); +} + +// This function copies ASan memory annotations (poisoned/unpoisoned states) +// from one buffer to another. +// It's main purpose is to help with relocating trivially relocatable objects, +// which memory may be poisoned, without calling copy constructor. +// However, it does not move memory content itself, only annotations. +// If the buffers aren't aligned (the distance between buffers isn't +// granule-aligned) +// // src_beg % granularity != dst_beg % granularity +// the function handles this by going byte by byte, slowing down performance. +// The old buffer annotations are not removed. If necessary, +// user can unpoison old buffer with __asan_unpoison_memory_region. +void __sanitizer_copy_contiguous_container_annotations(const void *src_beg_p, + const void *src_end_p, + const void *dst_beg_p, + const void *dst_end_p) { + if (!flags()->detect_container_overflow) + return; + + VPrintf(3, "contiguous_container_src: %p %p\n", src_beg_p, src_end_p); + VPrintf(3, "contiguous_container_dst: %p %p\n", dst_beg_p, dst_end_p); + + uptr src_beg = reinterpret_cast<uptr>(src_beg_p); + uptr src_end = reinterpret_cast<uptr>(src_end_p); + uptr dst_beg = reinterpret_cast<uptr>(dst_beg_p); + uptr dst_end = reinterpret_cast<uptr>(dst_end_p); + + constexpr uptr granularity = ASAN_SHADOW_GRANULARITY; + + if (src_beg > src_end || (dst_end - dst_beg) != (src_end - src_beg)) { + GET_STACK_TRACE_FATAL_HERE; + ReportBadParamsToCopyContiguousContainerAnnotations( + src_beg, src_end, dst_beg, dst_end, &stack); + } + + if (src_beg == src_end || src_beg == dst_beg) + return; + // Due to support for overlapping buffers, we may have to copy elements + // in reversed order, when destination buffer starts in the middle of + // the source buffer (or shares first granule with it). + // + // When buffers are not granule-aligned (or distance between them, + // to be specific), annotatios have to be copied byte by byte. + // + // The only remaining edge cases involve edge granules, + // when the container starts or ends within a granule. + uptr src_beg_up = RoundUpTo(src_beg, granularity); + uptr src_end_up = RoundUpTo(src_end, granularity); + bool copy_in_reversed_order = src_beg < dst_beg && dst_beg <= src_end_up; + if (src_beg % granularity != dst_beg % granularity || + RoundDownTo(dst_end - 1, granularity) <= dst_beg) { + if (copy_in_reversed_order) + SlowReversedCopyContainerAnnotations(src_beg, src_end, dst_beg, dst_end); + else + SlowCopyContainerAnnotations(src_beg, src_end, dst_beg, dst_end); + return; + } + + // As buffers are granule-aligned, we can just copy annotations of granules + // from the middle. + uptr dst_beg_up = RoundUpTo(dst_beg, granularity); + uptr dst_end_down = RoundDownTo(dst_end, granularity); + if (copy_in_reversed_order) + CopyContainerLastGranuleAnnotation(src_end, dst_end); + else + CopyContainerFirstGranuleAnnotation(src_beg, dst_beg); + + if (dst_beg_up < dst_end_down) { + internal_memmove((u8 *)MemToShadow(dst_beg_up), + (u8 *)MemToShadow(src_beg_up), + (dst_end_down - dst_beg_up) / granularity); + } + + if (copy_in_reversed_order) + CopyContainerFirstGranuleAnnotation(src_beg, dst_beg); + else + CopyContainerLastGranuleAnnotation(src_end, dst_end); +} + static const void *FindBadAddress(uptr begin, uptr end, bool poisoned) { CHECK_LE(begin, end); constexpr uptr kMaxRangeToCheck = 32; diff --git a/libsanitizer/asan/asan_posix.cpp b/libsanitizer/asan/asan_posix.cpp index e1f6664..3968569 100644 --- a/libsanitizer/asan/asan_posix.cpp +++ b/libsanitizer/asan/asan_posix.cpp @@ -59,10 +59,10 @@ bool PlatformUnpoisonStacks() { // Since we're on the signal alternate stack, we cannot find the DEFAULT // stack bottom using a local variable. - uptr default_bottom, tls_addr, tls_size, stack_size; - GetThreadStackAndTls(/*main=*/false, &default_bottom, &stack_size, &tls_addr, - &tls_size); - UnpoisonStack(default_bottom, default_bottom + stack_size, "default"); + uptr stack_begin, stack_end, tls_begin, tls_end; + GetThreadStackAndTls(/*main=*/false, &stack_begin, &stack_end, &tls_begin, + &tls_end); + UnpoisonStack(stack_begin, stack_end, "default"); return true; } @@ -146,7 +146,44 @@ void PlatformTSDDtor(void *tsd) { # endif AsanThread::TSDDtor(tsd); } -#endif +# endif + +static void BeforeFork() { + VReport(2, "BeforeFork tid: %llu\n", GetTid()); + if (CAN_SANITIZE_LEAKS) { + __lsan::LockGlobal(); + } + // `_lsan` functions defined regardless of `CAN_SANITIZE_LEAKS` and lock the + // stuff we need. + __lsan::LockThreads(); + __lsan::LockAllocator(); + StackDepotLockBeforeFork(); +} + +static void AfterFork(bool fork_child) { + StackDepotUnlockAfterFork(fork_child); + // `_lsan` functions defined regardless of `CAN_SANITIZE_LEAKS` and unlock + // the stuff we need. + __lsan::UnlockAllocator(); + __lsan::UnlockThreads(); + if (CAN_SANITIZE_LEAKS) { + __lsan::UnlockGlobal(); + } + VReport(2, "AfterFork tid: %llu\n", GetTid()); +} + +void InstallAtForkHandler() { +# if SANITIZER_SOLARIS || SANITIZER_NETBSD || SANITIZER_APPLE || \ + (SANITIZER_LINUX && SANITIZER_SPARC) + // While other Linux targets use clone in internal_fork which doesn't + // trigger pthread_atfork handlers, Linux/sparc64 uses __fork, causing a + // hang. + return; // FIXME: Implement FutexWait. +# endif + pthread_atfork( + &BeforeFork, []() { AfterFork(/* fork_child= */ false); }, + []() { AfterFork(/* fork_child= */ true); }); +} void InstallAtExitCheckLeaks() { if (CAN_SANITIZE_LEAKS) { diff --git a/libsanitizer/asan/asan_preinit.cpp b/libsanitizer/asan/asan_preinit.cpp index b07556e..2316938 100644 --- a/libsanitizer/asan/asan_preinit.cpp +++ b/libsanitizer/asan/asan_preinit.cpp @@ -15,10 +15,8 @@ using namespace __asan; #if SANITIZER_CAN_USE_PREINIT_ARRAY - // The symbol is called __local_asan_preinit, because it's not intended to be - // exported. - // This code linked into the main executable when -fsanitize=address is in - // the link flags. It can only use exported interface functions. - __attribute__((section(".preinit_array"), used)) - void (*__local_asan_preinit)(void) = __asan_init; +// This section is linked into the main executable when -fsanitize=address is +// specified to perform initialization at a very early stage. +__attribute__((section(".preinit_array"), used)) static auto preinit = + __asan_init; #endif diff --git a/libsanitizer/asan/asan_premap_shadow.cpp b/libsanitizer/asan/asan_premap_shadow.cpp index bed2f62..6e08b8f 100644 --- a/libsanitizer/asan/asan_premap_shadow.cpp +++ b/libsanitizer/asan/asan_premap_shadow.cpp @@ -33,7 +33,8 @@ uptr PremapShadowSize() { // PremapShadowSize() bytes on the right of it are mapped r/o. uptr PremapShadow() { return MapDynamicShadow(PremapShadowSize(), /*mmap_alignment_scale*/ 3, - /*min_shadow_base_alignment*/ 0, kHighMemEnd); + /*min_shadow_base_alignment*/ 0, kHighMemEnd, + GetMmapGranularity()); } bool PremapShadowFailed() { diff --git a/libsanitizer/asan/asan_report.cpp b/libsanitizer/asan/asan_report.cpp index 7603e81..45aa607 100644 --- a/libsanitizer/asan/asan_report.cpp +++ b/libsanitizer/asan/asan_report.cpp @@ -24,6 +24,7 @@ #include "sanitizer_common/sanitizer_common.h" #include "sanitizer_common/sanitizer_flags.h" #include "sanitizer_common/sanitizer_interface_internal.h" +#include "sanitizer_common/sanitizer_placement_new.h" #include "sanitizer_common/sanitizer_report_decorator.h" #include "sanitizer_common/sanitizer_stackdepot.h" #include "sanitizer_common/sanitizer_symbolizer.h" @@ -32,8 +33,11 @@ namespace __asan { // -------------------- User-specified callbacks ----------------- {{{1 static void (*error_report_callback)(const char*); -static char *error_message_buffer = nullptr; -static uptr error_message_buffer_pos = 0; +using ErrorMessageBuffer = InternalMmapVectorNoCtor<char, true>; +alignas( + alignof(ErrorMessageBuffer)) static char error_message_buffer_placeholder + [sizeof(ErrorMessageBuffer)]; +static ErrorMessageBuffer *error_message_buffer = nullptr; static Mutex error_message_buf_mutex; static const unsigned kAsanBuggyPcPoolSize = 25; static __sanitizer::atomic_uintptr_t AsanBuggyPcPool[kAsanBuggyPcPoolSize]; @@ -42,17 +46,14 @@ void AppendToErrorMessageBuffer(const char *buffer) { Lock l(&error_message_buf_mutex); if (!error_message_buffer) { error_message_buffer = - (char*)MmapOrDieQuietly(kErrorMessageBufferSize, __func__); - error_message_buffer_pos = 0; + new (error_message_buffer_placeholder) ErrorMessageBuffer(); + error_message_buffer->Initialize(kErrorMessageBufferSize); } - uptr length = internal_strlen(buffer); - RAW_CHECK(kErrorMessageBufferSize >= error_message_buffer_pos); - uptr remaining = kErrorMessageBufferSize - error_message_buffer_pos; - internal_strncpy(error_message_buffer + error_message_buffer_pos, - buffer, remaining); - error_message_buffer[kErrorMessageBufferSize - 1] = '\0'; - // FIXME: reallocate the buffer instead of truncating the message. - error_message_buffer_pos += Min(remaining, length); + uptr error_message_buffer_len = error_message_buffer->size(); + uptr buffer_len = internal_strlen(buffer); + error_message_buffer->resize(error_message_buffer_len + buffer_len); + internal_memcpy(error_message_buffer->data() + error_message_buffer_len, + buffer, buffer_len); } // ---------------------- Helper functions ----------------------- {{{1 @@ -158,14 +159,14 @@ class ScopedInErrorReport { // Copy the message buffer so that we could start logging without holding a // lock that gets acquired during printing. - InternalMmapVector<char> buffer_copy(kErrorMessageBufferSize); + InternalScopedString buffer_copy; { Lock l(&error_message_buf_mutex); - internal_memcpy(buffer_copy.data(), - error_message_buffer, kErrorMessageBufferSize); + error_message_buffer->push_back('\0'); + buffer_copy.Append(error_message_buffer->data()); // Clear error_message_buffer so that if we find other errors // we don't re-log this error. - error_message_buffer_pos = 0; + error_message_buffer->clear(); } LogFullErrorReport(buffer_copy.data()); @@ -366,6 +367,16 @@ void ReportBadParamsToAnnotateDoubleEndedContiguousContainer( in_report.ReportError(error); } +void ReportBadParamsToCopyContiguousContainerAnnotations( + uptr old_storage_beg, uptr old_storage_end, uptr new_storage_beg, + uptr new_storage_end, BufferedStackTrace *stack) { + ScopedInErrorReport in_report; + ErrorBadParamsToCopyContiguousContainerAnnotations error( + GetCurrentTidOrInvalid(), stack, old_storage_beg, old_storage_end, + new_storage_beg, new_storage_end); + in_report.ReportError(error); +} + void ReportODRViolation(const __asan_global *g1, u32 stack_id1, const __asan_global *g2, u32 stack_id2) { ScopedInErrorReport in_report; diff --git a/libsanitizer/asan/asan_report.h b/libsanitizer/asan/asan_report.h index 3540b3b..3143d83 100644 --- a/libsanitizer/asan/asan_report.h +++ b/libsanitizer/asan/asan_report.h @@ -88,6 +88,9 @@ void ReportBadParamsToAnnotateDoubleEndedContiguousContainer( uptr storage_beg, uptr storage_end, uptr old_container_beg, uptr old_container_end, uptr new_container_beg, uptr new_container_end, BufferedStackTrace *stack); +void ReportBadParamsToCopyContiguousContainerAnnotations( + uptr old_storage_beg, uptr old_storage_end, uptr new_storage_beg, + uptr new_storage_end, BufferedStackTrace *stack); void ReportODRViolation(const __asan_global *g1, u32 stack_id1, const __asan_global *g2, u32 stack_id2); diff --git a/libsanitizer/asan/asan_rtl.cpp b/libsanitizer/asan/asan_rtl.cpp index d1e7856..19c6c21 100644 --- a/libsanitizer/asan/asan_rtl.cpp +++ b/libsanitizer/asan/asan_rtl.cpp @@ -71,16 +71,16 @@ static void CheckUnwind() { } // -------------------------- Globals --------------------- {{{1 -static int asan_inited = 0; -static int asan_init_is_running = 0; +static StaticSpinMutex asan_inited_mutex; +static atomic_uint8_t asan_inited = {0}; -void SetAsanInited(u32 val) { asan_inited = val; } - -void SetAsanInitIsRunning(u32 val) { asan_init_is_running = val; } - -bool AsanInited() { return asan_inited == 1; } +static void SetAsanInited() { + atomic_store(&asan_inited, 1, memory_order_release); +} -bool AsanInitIsRunning() { return asan_init_is_running == 1; } +bool AsanInited() { + return atomic_load(&asan_inited, memory_order_acquire) == 1; +} bool replace_intrin_cached; @@ -382,7 +382,7 @@ void PrintAddressSpaceLayout() { Printf("SHADOW_SCALE: %d\n", (int)ASAN_SHADOW_SCALE); Printf("SHADOW_GRANULARITY: %d\n", (int)ASAN_SHADOW_GRANULARITY); - Printf("SHADOW_OFFSET: 0x%zx\n", (uptr)ASAN_SHADOW_OFFSET); + Printf("SHADOW_OFFSET: %p\n", (void *)ASAN_SHADOW_OFFSET); CHECK(ASAN_SHADOW_SCALE >= 3 && ASAN_SHADOW_SCALE <= 7); if (kMidMemBeg) CHECK(kMidShadowBeg > kLowShadowEnd && @@ -390,12 +390,10 @@ void PrintAddressSpaceLayout() { kHighShadowBeg > kMidMemEnd); } -static void AsanInitInternal() { +static bool AsanInitInternal() { if (LIKELY(AsanInited())) - return; + return true; SanitizerToolName = "AddressSanitizer"; - CHECK(!AsanInitIsRunning() && "ASan init calls itself!"); - SetAsanInitIsRunning(1); CacheBinaryName(); @@ -408,11 +406,12 @@ static void AsanInitInternal() { // Stop performing init at this point if we are being loaded via // dlopen() and the platform supports it. if (SANITIZER_SUPPORTS_INIT_FOR_DLOPEN && UNLIKELY(HandleDlopenInit())) { - SetAsanInitIsRunning(0); VReport(1, "AddressSanitizer init is being performed for dlopen().\n"); - return; + return false; } + // Make sure we are not statically linked. + __interception::DoesNotSupportStaticLinking(); AsanCheckIncompatibleRT(); AsanCheckDynamicRTPrereqs(); AvoidCVE_2016_2143(); @@ -424,9 +423,6 @@ static void AsanInitInternal() { InitializeHighMemEnd(); - // Make sure we are not statically linked. - AsanDoesNotSupportStaticLinkage(); - // Install tool-specific callbacks in sanitizer_common. AddDieCallback(AsanDie); SetCheckUnwindCallback(CheckUnwind); @@ -470,8 +466,7 @@ static void AsanInitInternal() { // On Linux AsanThread::ThreadStart() calls malloc() that's why asan_inited // should be set to 1 prior to initializing the threads. replace_intrin_cached = flags()->replace_intrin; - SetAsanInited(1); - SetAsanInitIsRunning(0); + SetAsanInited(); if (flags()->atexit) Atexit(asan_atexit); @@ -483,9 +478,6 @@ static void AsanInitInternal() { if (flags()->start_deactivated) AsanDeactivate(); - // interceptors - InitTlsSize(); - // Create main thread. AsanThread *main_thread = CreateMainThread(); CHECK_EQ(0, main_thread->tid()); @@ -497,6 +489,8 @@ static void AsanInitInternal() { InstallAtExitCheckLeaks(); } + InstallAtForkHandler(); + #if CAN_SANITIZE_UB __ubsan::InitAsPlugin(); #endif @@ -515,14 +509,29 @@ static void AsanInitInternal() { VReport(1, "AddressSanitizer Init done\n"); WaitForDebugger(flags()->sleep_after_init, "after init"); + + return true; } // Initialize as requested from some part of ASan runtime library (interceptors, // allocator, etc). void AsanInitFromRtl() { + if (LIKELY(AsanInited())) + return; + SpinMutexLock lock(&asan_inited_mutex); AsanInitInternal(); } +bool TryAsanInitFromRtl() { + if (LIKELY(AsanInited())) + return true; + if (!asan_inited_mutex.TryLock()) + return false; + bool result = AsanInitInternal(); + asan_inited_mutex.Unlock(); + return result; +} + #if ASAN_DYNAMIC // Initialize runtime in case it's LD_PRELOAD-ed into unsanitized executable // (and thus normal initializers from .preinit_array or modules haven't run). @@ -568,10 +577,8 @@ static void UnpoisonDefaultStack() { } else { CHECK(!SANITIZER_FUCHSIA); // If we haven't seen this thread, try asking the OS for stack bounds. - uptr tls_addr, tls_size, stack_size; - GetThreadStackAndTls(/*main=*/false, &bottom, &stack_size, &tls_addr, - &tls_size); - top = bottom + stack_size; + uptr tls_begin, tls_end; + GetThreadStackAndTls(/*main=*/false, &bottom, &top, &tls_begin, &tls_end); } UnpoisonStack(bottom, top, "default"); @@ -593,7 +600,7 @@ static void UnpoisonFakeStack() { using namespace __asan; void NOINLINE __asan_handle_no_return() { - if (AsanInitIsRunning()) + if (UNLIKELY(!AsanInited())) return; if (!PlatformUnpoisonStacks()) @@ -623,7 +630,7 @@ void NOINLINE __asan_set_death_callback(void (*callback)(void)) { // We use this call as a trigger to wake up ASan from deactivated state. void __asan_init() { AsanActivate(); - AsanInitInternal(); + AsanInitFromRtl(); } void __asan_version_mismatch_check() { diff --git a/libsanitizer/asan/asan_rtl_x86_64.S b/libsanitizer/asan/asan_rtl_x86_64.S index 0b73630..9c52898 100644 --- a/libsanitizer/asan/asan_rtl_x86_64.S +++ b/libsanitizer/asan/asan_rtl_x86_64.S @@ -27,7 +27,12 @@ FNAME(reg, op, s, i): ;\ #define ASAN_MEMORY_ACCESS_INITIAL_CHECK_ADD(reg, op, s) \ mov %##reg,%r10 ;\ shr $0x3,%r10 ;\ + .if ASAN_SHADOW_OFFSET_CONST < 0x80000000 ;\ movsbl ASAN_SHADOW_OFFSET_CONST(%r10),%r10d ;\ + .else ;\ + movabsq $ASAN_SHADOW_OFFSET_CONST,%r11 ;\ + movsbl (%r10,%r11),%r10d ;\ + .endif ;\ test %r10d,%r10d ;\ jne CLABEL(reg, op, s, add) ;\ RLABEL(reg, op, s, add): ;\ @@ -84,7 +89,12 @@ ENDF #define ASAN_MEMORY_ACCESS_CHECK_ADD(reg, op, s, c) \ mov %##reg,%r10 ;\ shr $0x3,%r10 ;\ + .if ASAN_SHADOW_OFFSET_CONST < 0x80000000 ;\ ##c $0x0,ASAN_SHADOW_OFFSET_CONST(%r10) ;\ + .else ;\ + movabsq $ASAN_SHADOW_OFFSET_CONST,%r11 ;\ + ##c $0x0,(%r10,%r11) ;\ + .endif ;\ jne FLABEL(reg, op, s, add) ;\ retq ;\ diff --git a/libsanitizer/asan/asan_suppressions.cpp b/libsanitizer/asan/asan_suppressions.cpp index 8cb2c3e..94289d1 100644 --- a/libsanitizer/asan/asan_suppressions.cpp +++ b/libsanitizer/asan/asan_suppressions.cpp @@ -20,7 +20,7 @@ namespace __asan { -ALIGNED(64) static char suppression_placeholder[sizeof(SuppressionContext)]; +alignas(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"; @@ -39,8 +39,7 @@ void InitializeSuppressions() { suppression_ctx = new (suppression_placeholder) SuppressionContext(kSuppressionTypes, ARRAY_SIZE(kSuppressionTypes)); suppression_ctx->ParseFromFile(flags()->suppressions); - if (&__asan_default_suppressions) - suppression_ctx->Parse(__asan_default_suppressions()); + suppression_ctx->Parse(__asan_default_suppressions()); } bool IsInterceptorSuppressed(const char *interceptor_name) { @@ -81,9 +80,10 @@ bool IsStackTraceSuppressed(const StackTrace *stack) { } if (suppression_ctx->HasSuppressionType(kInterceptorViaFunction)) { - SymbolizedStack *frames = symbolizer->SymbolizePC(addr); + SymbolizedStackHolder symbolized_stack(symbolizer->SymbolizePC(addr)); + const SymbolizedStack *frames = symbolized_stack.get(); CHECK(frames); - for (SymbolizedStack *cur = frames; cur; cur = cur->next) { + for (const SymbolizedStack *cur = frames; cur; cur = cur->next) { const char *function_name = cur->info.function; if (!function_name) { continue; @@ -91,11 +91,9 @@ bool IsStackTraceSuppressed(const StackTrace *stack) { // Match "interceptor_via_fun" suppressions. if (suppression_ctx->Match(function_name, kInterceptorViaFunction, &s)) { - frames->ClearAll(); return true; } } - frames->ClearAll(); } } return false; diff --git a/libsanitizer/asan/asan_thread.cpp b/libsanitizer/asan/asan_thread.cpp index 8798968..37fb6f2 100644 --- a/libsanitizer/asan/asan_thread.cpp +++ b/libsanitizer/asan/asan_thread.cpp @@ -21,6 +21,7 @@ #include "sanitizer_common/sanitizer_common.h" #include "sanitizer_common/sanitizer_placement_new.h" #include "sanitizer_common/sanitizer_stackdepot.h" +#include "sanitizer_common/sanitizer_thread_history.h" #include "sanitizer_common/sanitizer_tls_get_addr.h" namespace __asan { @@ -28,10 +29,7 @@ namespace __asan { // AsanThreadContext implementation. void AsanThreadContext::OnCreated(void *arg) { - CreateThreadContextArgs *args = static_cast<CreateThreadContextArgs *>(arg); - if (args->stack) - stack_id = StackDepotPut(*args->stack); - thread = args->thread; + thread = static_cast<AsanThread *>(arg); thread->set_context(this); } @@ -44,10 +42,15 @@ static ThreadRegistry *asan_thread_registry; static ThreadArgRetval *thread_data; static Mutex mu_for_thread_context; +// TODO(leonardchan@): It should be possible to make LowLevelAllocator +// threadsafe and consolidate this one into the GlobalLoweLevelAllocator. +// We should be able to do something similar to what's in +// sanitizer_stack_store.cpp. +static LowLevelAllocator allocator_for_thread_context; static ThreadContextBase *GetAsanThreadContext(u32 tid) { Lock lock(&mu_for_thread_context); - return new (GetGlobalLowLevelAllocator()) AsanThreadContext(tid); + return new (allocator_for_thread_context) AsanThreadContext(tid); } static void InitThreads() { @@ -62,10 +65,10 @@ static void InitThreads() { // thread before all TSD destructors will be called for it. // MIPS requires aligned address - static ALIGNED(alignof( - ThreadRegistry)) char thread_registry_placeholder[sizeof(ThreadRegistry)]; - static ALIGNED(alignof( - ThreadArgRetval)) char thread_data_placeholder[sizeof(ThreadArgRetval)]; + alignas(alignof(ThreadRegistry)) static char + thread_registry_placeholder[sizeof(ThreadRegistry)]; + alignas(alignof(ThreadArgRetval)) static char + thread_data_placeholder[sizeof(ThreadArgRetval)]; asan_thread_registry = new (thread_registry_placeholder) ThreadRegistry(GetAsanThreadContext); @@ -101,8 +104,8 @@ AsanThread *AsanThread::Create(const void *start_data, uptr data_size, CHECK_LE(data_size, availible_size); internal_memcpy(thread->start_data_, start_data, data_size); } - AsanThreadContext::CreateThreadContextArgs args = {thread, stack}; - asanThreadRegistry().CreateThread(0, detached, parent_tid, &args); + asanThreadRegistry().CreateThread(0, detached, parent_tid, + stack ? StackDepotPut(*stack) : 0, thread); return thread; } @@ -301,13 +304,10 @@ AsanThread *CreateMainThread() { // OS-specific implementations that need more information passed through. void AsanThread::SetThreadStackAndTls(const InitOptions *options) { DCHECK_EQ(options, nullptr); - uptr tls_size = 0; - uptr stack_size = 0; - GetThreadStackAndTls(tid() == kMainTid, &stack_bottom_, &stack_size, - &tls_begin_, &tls_size); - stack_top_ = RoundDownTo(stack_bottom_ + stack_size, ASAN_SHADOW_GRANULARITY); + GetThreadStackAndTls(tid() == kMainTid, &stack_bottom_, &stack_top_, + &tls_begin_, &tls_end_); + stack_top_ = RoundDownTo(stack_top_, ASAN_SHADOW_GRANULARITY); stack_bottom_ = RoundDownTo(stack_bottom_, ASAN_SHADOW_GRANULARITY); - tls_end_ = tls_begin_ + tls_size; dtls_ = DTLS_Get(); if (stack_top_ != stack_bottom_) { @@ -556,6 +556,12 @@ void GetRunningThreadsLocked(InternalMmapVector<tid_t> *threads) { threads); } +void PrintThreads() { + InternalScopedString out; + PrintThreadHistory(__asan::asanThreadRegistry(), out); + Report("%s\n", out.data()); +} + } // namespace __lsan // ---------------------- Interface ---------------- {{{1 diff --git a/libsanitizer/asan/asan_thread.h b/libsanitizer/asan/asan_thread.h index 62f1b53..ad9e03d 100644 --- a/libsanitizer/asan/asan_thread.h +++ b/libsanitizer/asan/asan_thread.h @@ -36,21 +36,16 @@ class AsanThread; class AsanThreadContext final : public ThreadContextBase { public: explicit AsanThreadContext(int tid) - : ThreadContextBase(tid), announced(false), - destructor_iterations(GetPthreadDestructorIterations()), stack_id(0), + : ThreadContextBase(tid), + announced(false), + destructor_iterations(GetPthreadDestructorIterations()), thread(nullptr) {} bool announced; u8 destructor_iterations; - u32 stack_id; AsanThread *thread; void OnCreated(void *arg) override; void OnFinished() override; - - struct CreateThreadContextArgs { - AsanThread *thread; - StackTrace *stack; - }; }; // AsanThreadContext objects are never freed, so we need many of them. diff --git a/libsanitizer/asan/asan_win.cpp b/libsanitizer/asan/asan_win.cpp index d5a30f4..09a13b1 100644 --- a/libsanitizer/asan/asan_win.cpp +++ b/libsanitizer/asan/asan_win.cpp @@ -203,6 +203,8 @@ void InitializePlatformInterceptors() { void InstallAtExitCheckLeaks() {} +void InstallAtForkHandler() {} + void AsanApplyToGlobals(globals_op_fptr op, const void *needle) { UNIMPLEMENTED(); } @@ -264,16 +266,10 @@ void PlatformTSDDtor(void *tsd) { AsanThread::TSDDtor(tsd); } // }}} // ---------------------- Various stuff ---------------- {{{ -void *AsanDoesNotSupportStaticLinkage() { -#if defined(_DEBUG) -#error Please build the runtime with a non-debug CRT: /MD or /MT -#endif - return 0; -} - uptr FindDynamicShadowStart() { return MapDynamicShadow(MemToShadowSize(kHighMemEnd), ASAN_SHADOW_SCALE, - /*min_shadow_base_alignment*/ 0, kHighMemEnd); + /*min_shadow_base_alignment*/ 0, kHighMemEnd, + GetMmapGranularity()); } void AsanCheckDynamicRTPrereqs() {} diff --git a/libsanitizer/asan/asan_win_common_runtime_thunk.cpp b/libsanitizer/asan/asan_win_common_runtime_thunk.cpp new file mode 100644 index 0000000..056e493 --- /dev/null +++ b/libsanitizer/asan/asan_win_common_runtime_thunk.cpp @@ -0,0 +1,112 @@ +//===-- asan_win_common_runtime_thunk.cpp --------------------------- -----===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This file is a part of AddressSanitizer, an address sanity checker. +// +// This file defines things that need to be present in the application modules +// to interact with the ASan DLL runtime correctly and can't be implemented +// using the default "import library" generated when linking the DLL. +// +// This includes: +// - Cloning shadow memory dynamic address from ASAN DLL +// - Creating weak aliases to default implementation imported from asan dll +// - Forwarding the detect_stack_use_after_return runtime option +// - installing a custom SEH handler +// +//===----------------------------------------------------------------------===// + +#if defined(SANITIZER_DYNAMIC_RUNTIME_THUNK) || \ + defined(SANITIZER_STATIC_RUNTIME_THUNK) +# define SANITIZER_IMPORT_INTERFACE 1 +# define WIN32_LEAN_AND_MEAN +# include "asan_win_common_runtime_thunk.h" + +# include <windows.h> + +# include "sanitizer_common/sanitizer_win_defs.h" +# include "sanitizer_common/sanitizer_win_thunk_interception.h" + +// Define weak alias for all weak functions imported from asan dll. +# define INTERFACE_FUNCTION(Name) +# define INTERFACE_WEAK_FUNCTION(Name) REGISTER_WEAK_FUNCTION(Name) +# include "asan_interface.inc" + +//////////////////////////////////////////////////////////////////////////////// +// 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. +// +// The ASan MD runtime dllexports '__asan_option_detect_stack_use_after_return', +// so normally we would just dllimport it. Unfortunately, the dllimport +// attribute adds __imp_ prefix to the symbol name of a variable. +// Since in general we don't know if a given TU is going to be used +// 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 variable that is constant +// after initialization anyways. + +extern "C" { +__declspec(dllimport) int __asan_should_detect_stack_use_after_return(); +int __asan_option_detect_stack_use_after_return; + +__declspec(dllimport) void *__asan_get_shadow_memory_dynamic_address(); +void *__asan_shadow_memory_dynamic_address; + +static void __asan_initialize_cloned_variables() { + __asan_option_detect_stack_use_after_return = + __asan_should_detect_stack_use_after_return(); + __asan_shadow_memory_dynamic_address = + __asan_get_shadow_memory_dynamic_address(); +} +} + +static int asan_thunk_init() { + __asan_initialize_cloned_variables(); + +# ifdef SANITIZER_STATIC_RUNTIME_THUNK + __asan_initialize_static_thunk(); +# endif + + return 0; +} + +static void WINAPI asan_thread_init(void *mod, unsigned long reason, + void *reserved) { + if (reason == DLL_PROCESS_ATTACH) { + asan_thunk_init(); + } +} + +// Our cloned variables must be initialized before C/C++ constructors. If TLS +// is used, our .CRT$XLAB initializer will run first. If not, our .CRT$XIB +// initializer is needed as a backup. +extern "C" __declspec(allocate(".CRT$XIB")) int (*__asan_thunk_init)() = + asan_thunk_init; +WIN_FORCE_LINK(__asan_thunk_init) + +extern "C" __declspec(allocate(".CRT$XLAB")) void(WINAPI *__asan_tls_init)( + void *, unsigned long, void *) = asan_thread_init; +WIN_FORCE_LINK(__asan_tls_init) + +//////////////////////////////////////////////////////////////////////////////// +// ASan SEH handling. +// We need to set the ASan-specific SEH handler at the end of CRT initialization +// of each module (see also asan_win.cpp). +extern "C" { +__declspec(dllimport) int __asan_set_seh_filter(); +static int SetSEHFilter() { return __asan_set_seh_filter(); } + +// Unfortunately, putting a pointer to __asan_set_seh_filter into +// __asan_intercept_seh gets optimized out, so we have to use an extra function. +extern "C" __declspec(allocate(".CRT$XCAB")) int (*__asan_seh_interceptor)() = + SetSEHFilter; +WIN_FORCE_LINK(__asan_seh_interceptor) +} + +WIN_FORCE_LINK(__asan_dso_reg_hook) + +#endif // defined(SANITIZER_DYNAMIC_RUNTIME_THUNK) || + // defined(SANITIZER_STATIC_RUNTIME_THUNK) diff --git a/libsanitizer/asan/asan_win_common_runtime_thunk.h b/libsanitizer/asan/asan_win_common_runtime_thunk.h new file mode 100644 index 0000000..159cc15 --- /dev/null +++ b/libsanitizer/asan/asan_win_common_runtime_thunk.h @@ -0,0 +1,38 @@ +//===-- asan_win_common_runtime_thunk.h -------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This file is a part of AddressSanitizer, an address sanity checker. +// +// This file defines things that need to be present in the application modules +// to interact with the ASan DLL runtime correctly and can't be implemented +// using the default "import library" generated when linking the DLL. +// +//===----------------------------------------------------------------------===// + +#if defined(SANITIZER_STATIC_RUNTIME_THUNK) || \ + defined(SANITIZER_DYNAMIC_RUNTIME_THUNK) +# include "sanitizer_common/sanitizer_win_defs.h" + +# pragma section(".CRT$XIB", long, \ + read) // C initializer (during C init before dyninit) +# pragma section(".CRT$XID", long, \ + read) // First C initializer after CRT initializers +# pragma section(".CRT$XCAB", long, \ + read) // First C++ initializer after startup initializers + +# pragma section(".CRT$XTW", long, read) // First ASAN globals terminator +# pragma section(".CRT$XTY", long, read) // Last ASAN globals terminator + +# pragma section(".CRT$XLAB", long, read) // First TLS initializer + +# ifdef SANITIZER_STATIC_RUNTIME_THUNK +extern "C" void __asan_initialize_static_thunk(); +# endif + +#endif // defined(SANITIZER_STATIC_RUNTIME_THUNK) || + // defined(SANITIZER_DYNAMIC_RUNTIME_THUNK)
\ No newline at end of file diff --git a/libsanitizer/asan/asan_win_dll_thunk.cpp b/libsanitizer/asan/asan_win_dll_thunk.cpp deleted file mode 100644 index 0fa636b..0000000 --- a/libsanitizer/asan/asan_win_dll_thunk.cpp +++ /dev/null @@ -1,165 +0,0 @@ -//===-- asan_win_dll_thunk.cpp --------------------------------------------===// -// -// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. -// See https://llvm.org/LICENSE.txt for license information. -// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception -// -//===----------------------------------------------------------------------===// -// -// This file is a part of AddressSanitizer, an address sanity checker. -// -// This file defines a family of thunks that should be statically linked into -// the DLLs that have ASan instrumentation in order to delegate the calls to the -// shared runtime that lives in the main binary. -// See https://github.com/google/sanitizers/issues/209 for the details. -//===----------------------------------------------------------------------===// - -#ifdef SANITIZER_DLL_THUNK -#include "asan_init_version.h" -#include "interception/interception.h" -#include "sanitizer_common/sanitizer_win_defs.h" -#include "sanitizer_common/sanitizer_win_dll_thunk.h" -#include "sanitizer_common/sanitizer_platform_interceptors.h" - -// ASan own interface functions. -#define INTERFACE_FUNCTION(Name) INTERCEPT_SANITIZER_FUNCTION(Name) -#define INTERFACE_WEAK_FUNCTION(Name) INTERCEPT_SANITIZER_WEAK_FUNCTION(Name) -#include "asan_interface.inc" - -// Memory allocation functions. -INTERCEPT_WRAP_V_W(free) -INTERCEPT_WRAP_V_W(_free_base) -INTERCEPT_WRAP_V_WW(_free_dbg) - -INTERCEPT_WRAP_W_W(malloc) -INTERCEPT_WRAP_W_W(_malloc_base) -INTERCEPT_WRAP_W_WWWW(_malloc_dbg) - -INTERCEPT_WRAP_W_WW(calloc) -INTERCEPT_WRAP_W_WW(_calloc_base) -INTERCEPT_WRAP_W_WWWWW(_calloc_dbg) -INTERCEPT_WRAP_W_WWW(_calloc_impl) - -INTERCEPT_WRAP_W_WW(realloc) -INTERCEPT_WRAP_W_WW(_realloc_base) -INTERCEPT_WRAP_W_WWW(_realloc_dbg) -INTERCEPT_WRAP_W_WWW(_recalloc) -INTERCEPT_WRAP_W_WWW(_recalloc_base) - -INTERCEPT_WRAP_W_W(_msize) -INTERCEPT_WRAP_W_W(_msize_base) -INTERCEPT_WRAP_W_W(_expand) -INTERCEPT_WRAP_W_W(_expand_dbg) - -// TODO(timurrrr): Might want to add support for _aligned_* allocation -// functions to detect a bit more bugs. Those functions seem to wrap malloc(). - -// TODO(timurrrr): Do we need to add _Crt* stuff here? (see asan_malloc_win.cpp) - -# if defined(_MSC_VER) && !defined(__clang__) -// Disable warnings such as: 'void memchr(void)': incorrect number of arguments -// for intrinsic function, expected '3' arguments. -# pragma warning(push) -# pragma warning(disable : 4392) -# endif - -INTERCEPT_LIBRARY_FUNCTION(atoi); -INTERCEPT_LIBRARY_FUNCTION(atol); -INTERCEPT_LIBRARY_FUNCTION(atoll); -INTERCEPT_LIBRARY_FUNCTION(frexp); -INTERCEPT_LIBRARY_FUNCTION(longjmp); -#if SANITIZER_INTERCEPT_MEMCHR -INTERCEPT_LIBRARY_FUNCTION(memchr); -#endif -INTERCEPT_LIBRARY_FUNCTION(memcmp); -INTERCEPT_LIBRARY_FUNCTION(memcpy); -INTERCEPT_LIBRARY_FUNCTION(memmove); -INTERCEPT_LIBRARY_FUNCTION(memset); -INTERCEPT_LIBRARY_FUNCTION(strcat); -INTERCEPT_LIBRARY_FUNCTION(strchr); -INTERCEPT_LIBRARY_FUNCTION(strcmp); -INTERCEPT_LIBRARY_FUNCTION(strcpy); -INTERCEPT_LIBRARY_FUNCTION(strcspn); -INTERCEPT_LIBRARY_FUNCTION(strdup); -INTERCEPT_LIBRARY_FUNCTION(strlen); -INTERCEPT_LIBRARY_FUNCTION(strncat); -INTERCEPT_LIBRARY_FUNCTION(strncmp); -INTERCEPT_LIBRARY_FUNCTION(strncpy); -INTERCEPT_LIBRARY_FUNCTION(strnlen); -INTERCEPT_LIBRARY_FUNCTION(strpbrk); -INTERCEPT_LIBRARY_FUNCTION(strrchr); -INTERCEPT_LIBRARY_FUNCTION(strspn); -INTERCEPT_LIBRARY_FUNCTION(strstr); -INTERCEPT_LIBRARY_FUNCTION(strtok); -INTERCEPT_LIBRARY_FUNCTION(strtol); -INTERCEPT_LIBRARY_FUNCTION(strtoll); -INTERCEPT_LIBRARY_FUNCTION(wcslen); -INTERCEPT_LIBRARY_FUNCTION(wcsnlen); - -# if defined(_MSC_VER) && !defined(__clang__) -# pragma warning(pop) -# endif - -#ifdef _WIN64 -INTERCEPT_LIBRARY_FUNCTION(__C_specific_handler); -#else -INTERCEPT_LIBRARY_FUNCTION(_except_handler3); -// _except_handler4 checks -GS cookie which is different for each module, so we -// can't use INTERCEPT_LIBRARY_FUNCTION(_except_handler4). -INTERCEPTOR(int, _except_handler4, void *a, void *b, void *c, void *d) { - __asan_handle_no_return(); - return REAL(_except_handler4)(a, b, c, d); -} -#endif - -// Windows specific functions not included in asan_interface.inc. -INTERCEPT_WRAP_W_V(__asan_should_detect_stack_use_after_return) -INTERCEPT_WRAP_W_V(__asan_get_shadow_memory_dynamic_address) -INTERCEPT_WRAP_W_W(__asan_unhandled_exception_filter) - -using namespace __sanitizer; - -extern "C" { -int __asan_option_detect_stack_use_after_return; -uptr __asan_shadow_memory_dynamic_address; -} // extern "C" - -static int asan_dll_thunk_init() { - typedef void (*fntype)(); - static fntype fn = 0; - // asan_dll_thunk_init is expected to be called by only one thread. - if (fn) return 0; - - // Ensure all interception was executed. - __dll_thunk_init(); - - fn = (fntype) dllThunkGetRealAddrOrDie("__asan_init"); - fn(); - __asan_option_detect_stack_use_after_return = - (__asan_should_detect_stack_use_after_return() != 0); - __asan_shadow_memory_dynamic_address = - (uptr)__asan_get_shadow_memory_dynamic_address(); - -#ifndef _WIN64 - INTERCEPT_FUNCTION(_except_handler4); -#endif - // In DLLs, the callbacks are expected to return 0, - // otherwise CRT initialization fails. - return 0; -} - -#pragma section(".CRT$XIB", long, read) -__declspec(allocate(".CRT$XIB")) int (*__asan_preinit)() = asan_dll_thunk_init; - -static void WINAPI asan_thread_init(void *mod, unsigned long reason, - void *reserved) { - if (reason == /*DLL_PROCESS_ATTACH=*/1) asan_dll_thunk_init(); -} - -#pragma section(".CRT$XLAB", long, read) -__declspec(allocate(".CRT$XLAB")) void (WINAPI *__asan_tls_init)(void *, - unsigned long, void *) = asan_thread_init; - -WIN_FORCE_LINK(__asan_dso_reg_hook) - -#endif // SANITIZER_DLL_THUNK diff --git a/libsanitizer/asan/asan_win_dynamic_runtime_thunk.cpp b/libsanitizer/asan/asan_win_dynamic_runtime_thunk.cpp index f0b5ec9..421fe65 100644 --- a/libsanitizer/asan/asan_win_dynamic_runtime_thunk.cpp +++ b/libsanitizer/asan/asan_win_dynamic_runtime_thunk.cpp @@ -8,76 +8,17 @@ // // This file is a part of AddressSanitizer, an address sanity checker. // -// This file defines things that need to be present in the application modules -// to interact with the ASan DLL runtime correctly and can't be implemented -// using the default "import library" generated when linking the DLL RTL. -// -// This includes: -// - creating weak aliases to default implementation imported from asan dll. -// - forwarding the detect_stack_use_after_return runtime option -// - working around deficiencies of the MD runtime -// - installing a custom SEH handler +// This file defines things that need to be present for application modules +// that are dynamic linked with the C Runtime. // //===----------------------------------------------------------------------===// #ifdef SANITIZER_DYNAMIC_RUNTIME_THUNK -#define SANITIZER_IMPORT_INTERFACE 1 -#include "sanitizer_common/sanitizer_win_defs.h" -#define WIN32_LEAN_AND_MEAN -#include <windows.h> - -// Define weak alias for all weak functions imported from asan dll. -#define INTERFACE_FUNCTION(Name) -#define INTERFACE_WEAK_FUNCTION(Name) WIN_WEAK_IMPORT_DEF(Name) -#include "asan_interface.inc" - -// First, declare CRT sections we'll be using in this file -#pragma section(".CRT$XIB", long, read) -#pragma section(".CRT$XID", long, read) -#pragma section(".CRT$XCAB", long, read) -#pragma section(".CRT$XTW", long, read) -#pragma section(".CRT$XTY", long, read) -#pragma section(".CRT$XLAB", long, read) - -//////////////////////////////////////////////////////////////////////////////// -// 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. -// -// The ASan MD runtime dllexports '__asan_option_detect_stack_use_after_return', -// so normally we would just dllimport it. Unfortunately, the dllimport -// attribute adds __imp_ prefix to the symbol name of a variable. -// Since in general we don't know if a given TU is going to be used -// 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 variable that is constant -// after initialization anyways. -extern "C" { -__declspec(dllimport) int __asan_should_detect_stack_use_after_return(); -int __asan_option_detect_stack_use_after_return; - -__declspec(dllimport) void* __asan_get_shadow_memory_dynamic_address(); -void* __asan_shadow_memory_dynamic_address; -} - -static int InitializeClonedVariables() { - __asan_option_detect_stack_use_after_return = - __asan_should_detect_stack_use_after_return(); - __asan_shadow_memory_dynamic_address = - __asan_get_shadow_memory_dynamic_address(); - return 0; -} - -static void NTAPI asan_thread_init(void *mod, unsigned long reason, - void *reserved) { - if (reason == DLL_PROCESS_ATTACH) InitializeClonedVariables(); -} +# define WIN32_LEAN_AND_MEAN +# include <windows.h> -// Our cloned variables must be initialized before C/C++ constructors. If TLS -// is used, our .CRT$XLAB initializer will run first. If not, our .CRT$XIB -// initializer is needed as a backup. -__declspec(allocate(".CRT$XIB")) int (*__asan_initialize_cloned_variables)() = - InitializeClonedVariables; -__declspec(allocate(".CRT$XLAB")) void (NTAPI *__asan_tls_init)(void *, - unsigned long, void *) = asan_thread_init; +# include "asan_win_common_runtime_thunk.h" +# include "sanitizer_common/sanitizer_win_defs.h" //////////////////////////////////////////////////////////////////////////////// // For some reason, the MD CRT doesn't call the C/C++ terminators during on DLL @@ -88,43 +29,26 @@ __declspec(allocate(".CRT$XLAB")) void (NTAPI *__asan_tls_init)(void *, // using atexit() that calls a small subset of C terminators // where LLVM global_dtors is placed. Fingers crossed, no other C terminators // are there. -extern "C" int __cdecl atexit(void (__cdecl *f)(void)); +extern "C" int __cdecl atexit(void(__cdecl *f)(void)); extern "C" void __cdecl _initterm(void *a, void *b); namespace { -__declspec(allocate(".CRT$XTW")) void* before_global_dtors = 0; -__declspec(allocate(".CRT$XTY")) void* after_global_dtors = 0; +__declspec(allocate(".CRT$XTW")) void *before_global_dtors = 0; +__declspec(allocate(".CRT$XTY")) void *after_global_dtors = 0; void UnregisterGlobals() { _initterm(&before_global_dtors, &after_global_dtors); } -int ScheduleUnregisterGlobals() { - return atexit(UnregisterGlobals); -} +int ScheduleUnregisterGlobals() { return atexit(UnregisterGlobals); } } // namespace // We need to call 'atexit(UnregisterGlobals);' as early as possible, but after // atexit() is initialized (.CRT$XIC). As this is executed before C++ // initializers (think ctors for globals), UnregisterGlobals gets executed after // dtors for C++ globals. -__declspec(allocate(".CRT$XID")) -int (*__asan_schedule_unregister_globals)() = ScheduleUnregisterGlobals; - -//////////////////////////////////////////////////////////////////////////////// -// ASan SEH handling. -// We need to set the ASan-specific SEH handler at the end of CRT initialization -// of each module (see also asan_win.cpp). -extern "C" { -__declspec(dllimport) int __asan_set_seh_filter(); -static int SetSEHFilter() { return __asan_set_seh_filter(); } - -// Unfortunately, putting a pointer to __asan_set_seh_filter into -// __asan_intercept_seh gets optimized out, so we have to use an extra function. -__declspec(allocate(".CRT$XCAB")) int (*__asan_seh_interceptor)() = - SetSEHFilter; -} - -WIN_FORCE_LINK(__asan_dso_reg_hook) +extern "C" __declspec(allocate(".CRT$XID")) int ( + *__asan_schedule_unregister_globals)() = ScheduleUnregisterGlobals; +WIN_FORCE_LINK(__asan_schedule_unregister_globals) -#endif // SANITIZER_DYNAMIC_RUNTIME_THUNK +#endif // SANITIZER_DYNAMIC_RUNTIME_THUNK diff --git a/libsanitizer/asan/asan_win_static_runtime_thunk.cpp b/libsanitizer/asan/asan_win_static_runtime_thunk.cpp new file mode 100644 index 0000000..4a69b66 --- /dev/null +++ b/libsanitizer/asan/asan_win_static_runtime_thunk.cpp @@ -0,0 +1,113 @@ +//===-- asan_win_static_runtime_thunk.cpp ---------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This file is a part of AddressSanitizer, an address sanity checker. +// +// This file defines a family of thunks that should be statically linked into +// modules that are statically linked with the C Runtime in order to delegate +// the calls to the ASAN runtime DLL. +// See https://github.com/google/sanitizers/issues/209 for the details. +//===----------------------------------------------------------------------===// + +#ifdef SANITIZER_STATIC_RUNTIME_THUNK +# include "asan_init_version.h" +# include "asan_interface_internal.h" +# include "asan_win_common_runtime_thunk.h" +# include "sanitizer_common/sanitizer_platform_interceptors.h" +# include "sanitizer_common/sanitizer_win_defs.h" +# include "sanitizer_common/sanitizer_win_thunk_interception.h" + +# if defined(_MSC_VER) && !defined(__clang__) +// Disable warnings such as: 'void memchr(void)': incorrect number of arguments +// for intrinsic function, expected '3' arguments. +# pragma warning(push) +# pragma warning(disable : 4392) +# endif + +# define INTERCEPT_LIBRARY_FUNCTION_ASAN(X) \ + INTERCEPT_LIBRARY_FUNCTION(X, "__asan_wrap_" #X) + +INTERCEPT_LIBRARY_FUNCTION_ASAN(atoi); +INTERCEPT_LIBRARY_FUNCTION_ASAN(atol); +INTERCEPT_LIBRARY_FUNCTION_ASAN(atoll); +INTERCEPT_LIBRARY_FUNCTION_ASAN(frexp); +INTERCEPT_LIBRARY_FUNCTION_ASAN(longjmp); +# if SANITIZER_INTERCEPT_MEMCHR +INTERCEPT_LIBRARY_FUNCTION_ASAN(memchr); +# endif +INTERCEPT_LIBRARY_FUNCTION_ASAN(memcmp); +INTERCEPT_LIBRARY_FUNCTION_ASAN(memcpy); +# ifndef _WIN64 +// memmove and memcpy share an implementation on amd64 +INTERCEPT_LIBRARY_FUNCTION_ASAN(memmove); +# endif +INTERCEPT_LIBRARY_FUNCTION_ASAN(memset); +INTERCEPT_LIBRARY_FUNCTION_ASAN(strcat); +INTERCEPT_LIBRARY_FUNCTION_ASAN(strchr); +INTERCEPT_LIBRARY_FUNCTION_ASAN(strcmp); +INTERCEPT_LIBRARY_FUNCTION_ASAN(strcpy); +INTERCEPT_LIBRARY_FUNCTION_ASAN(strcspn); +INTERCEPT_LIBRARY_FUNCTION_ASAN(_strdup); +INTERCEPT_LIBRARY_FUNCTION_ASAN(strlen); +INTERCEPT_LIBRARY_FUNCTION_ASAN(strncat); +INTERCEPT_LIBRARY_FUNCTION_ASAN(strncmp); +INTERCEPT_LIBRARY_FUNCTION_ASAN(strncpy); +INTERCEPT_LIBRARY_FUNCTION_ASAN(strnlen); +INTERCEPT_LIBRARY_FUNCTION_ASAN(strpbrk); +// INTERCEPT_LIBRARY_FUNCTION_ASAN(strrchr); +INTERCEPT_LIBRARY_FUNCTION_ASAN(strspn); +INTERCEPT_LIBRARY_FUNCTION_ASAN(strstr); +INTERCEPT_LIBRARY_FUNCTION_ASAN(strtok); +INTERCEPT_LIBRARY_FUNCTION_ASAN(wcslen); +INTERCEPT_LIBRARY_FUNCTION_ASAN(wcsnlen); + +// Note: Don't intercept strtol(l). They are supposed to set errno for out-of- +// range values, but since the ASan runtime is linked against the dynamic CRT, +// its errno is different from the one in the current module. + +# if defined(_MSC_VER) && !defined(__clang__) +# pragma warning(pop) +# endif + +# ifdef _WIN64 +INTERCEPT_LIBRARY_FUNCTION_ASAN(__C_specific_handler); +# else +extern "C" void abort(); +INTERCEPT_LIBRARY_FUNCTION_ASAN(_except_handler3); +// _except_handler4 checks -GS cookie which is different for each module, so we +// can't use INTERCEPT_LIBRARY_FUNCTION_ASAN(_except_handler4), need to apply +// manually +extern "C" int _except_handler4(void *, void *, void *, void *); +static int (*real_except_handler4)(void *, void *, void *, + void *) = &_except_handler4; +static int intercept_except_handler4(void *a, void *b, void *c, void *d) { + __asan_handle_no_return(); + return real_except_handler4(a, b, c, d); +} +# endif + +// Windows specific functions not included in asan_interface.inc. +// INTERCEPT_WRAP_W_V(__asan_should_detect_stack_use_after_return) +// INTERCEPT_WRAP_W_V(__asan_get_shadow_memory_dynamic_address) +// INTERCEPT_WRAP_W_W(__asan_unhandled_exception_filter) + +extern "C" void __asan_initialize_static_thunk() { +# ifndef _WIN64 + if (real_except_handler4 == &_except_handler4) { + // Single threaded, no need for synchronization. + if (!__sanitizer_override_function_by_addr( + reinterpret_cast<__sanitizer::uptr>(&intercept_except_handler4), + reinterpret_cast<__sanitizer::uptr>(&_except_handler4), + reinterpret_cast<__sanitizer::uptr*>(&real_except_handler4))) { + abort(); + } + } +# endif +} + +#endif // SANITIZER_DLL_THUNK diff --git a/libsanitizer/asan/asan_win_weak_interception.cpp b/libsanitizer/asan/asan_win_weak_interception.cpp deleted file mode 100644 index 62534e1..0000000 --- a/libsanitizer/asan/asan_win_weak_interception.cpp +++ /dev/null @@ -1,22 +0,0 @@ -//===-- asan_win_weak_interception.cpp ------------------------------------===// -// -// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. -// See https://llvm.org/LICENSE.txt for license information. -// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception -// -//===----------------------------------------------------------------------===// -// This module should be included in Address Sanitizer when it is implemented as -// a shared library on Windows (dll), in order to delegate the calls of weak -// functions to the implementation in the main executable when a strong -// definition is provided. -//===----------------------------------------------------------------------===// -#ifdef SANITIZER_DYNAMIC -#include "sanitizer_common/sanitizer_win_weak_interception.h" -#include "asan_interface_internal.h" -// Check if strong definitions for weak functions are present in the main -// executable. If that is the case, override dll functions to point to strong -// implementations. -#define INTERFACE_FUNCTION(Name) -#define INTERFACE_WEAK_FUNCTION(Name) INTERCEPT_SANITIZER_WEAK_FUNCTION(Name) -#include "asan_interface.inc" -#endif // SANITIZER_DYNAMIC diff --git a/libsanitizer/builtins/assembly.h b/libsanitizer/builtins/assembly.h index 169d496..8c42fc7 100644 --- a/libsanitizer/builtins/assembly.h +++ b/libsanitizer/builtins/assembly.h @@ -260,9 +260,10 @@ .globl name SEPARATOR \ SYMBOL_IS_FUNC(name) SEPARATOR \ DECLARE_SYMBOL_VISIBILITY_UNMANGLED(name) SEPARATOR \ - CFI_START SEPARATOR \ DECLARE_FUNC_ENCODING \ - name: SEPARATOR BTI_C + name: \ + SEPARATOR CFI_START \ + SEPARATOR BTI_C #define DEFINE_COMPILERRT_FUNCTION_ALIAS(name, target) \ .globl SYMBOL_NAME(name) SEPARATOR \ diff --git a/libsanitizer/hwasan/hwasan.cpp b/libsanitizer/hwasan/hwasan.cpp index 2f6cb10..24384d8 100644 --- a/libsanitizer/hwasan/hwasan.cpp +++ b/libsanitizer/hwasan/hwasan.cpp @@ -357,8 +357,6 @@ __attribute__((constructor(0))) void __hwasan_init() { hwasan_init_is_running = 1; SanitizerToolName = "HWAddressSanitizer"; - InitTlsSize(); - CacheBinaryName(); InitializeFlags(); @@ -367,6 +365,8 @@ __attribute__((constructor(0))) void __hwasan_init() { __sanitizer_set_report_path(common_flags()->log_path); + InitializePlatformEarly(); + AndroidTestTlsSlot(); DisableCoreDumperIfNecessary(); @@ -678,6 +678,8 @@ uptr __hwasan_tag_pointer(uptr p, u8 tag) { return AddTagToPointer(p, tag); } +u8 __hwasan_get_tag_from_pointer(uptr p) { return GetTagFromPointer(p); } + void __hwasan_handle_longjmp(const void *sp_dst) { uptr dst = (uptr)sp_dst; // HWASan does not support tagged SP. @@ -690,7 +692,7 @@ void __hwasan_handle_longjmp(const void *sp_dst) { "WARNING: HWASan is ignoring requested __hwasan_handle_longjmp: " "stack top: %p; target %p; distance: %p (%zd)\n" "False positive error reports may follow\n", - (void *)sp, (void *)dst, dst - sp); + (void *)sp, (void *)dst, dst - sp, dst - sp); return; } TagMemory(sp, dst - sp, 0); diff --git a/libsanitizer/hwasan/hwasan.h b/libsanitizer/hwasan/hwasan.h index 37ef482..1ae463f 100644 --- a/libsanitizer/hwasan/hwasan.h +++ b/libsanitizer/hwasan/hwasan.h @@ -104,9 +104,9 @@ static inline void *UntagPtr(const void *tagged_ptr) { } static inline uptr AddTagToPointer(uptr p, tag_t tag) { - return InTaggableRegion(p) - ? ((p & ~kAddressTagMask) | ((uptr)tag << kAddressTagShift)) - : p; + return InTaggableRegion(p) ? ((p & ~kAddressTagMask) | + ((uptr)(tag & kTagMask) << kAddressTagShift)) + : p; } namespace __hwasan { @@ -139,14 +139,14 @@ void hwasan_free(void *ptr, StackTrace *stack); void InstallAtExitHandler(); #define GET_MALLOC_STACK_TRACE \ - BufferedStackTrace stack; \ + UNINITIALIZED BufferedStackTrace stack; \ if (hwasan_inited) \ stack.Unwind(StackTrace::GetCurrentPc(), GET_CURRENT_FRAME(), \ nullptr, common_flags()->fast_unwind_on_malloc, \ common_flags()->malloc_context_size) #define GET_FATAL_STACK_TRACE_PC_BP(pc, bp) \ - BufferedStackTrace stack; \ + UNINITIALIZED BufferedStackTrace stack; \ if (hwasan_inited) \ stack.Unwind(pc, bp, nullptr, common_flags()->fast_unwind_on_fatal) diff --git a/libsanitizer/hwasan/hwasan_allocation_functions.cpp b/libsanitizer/hwasan/hwasan_allocation_functions.cpp index 75d91ed..25ca0a3 100644 --- a/libsanitizer/hwasan/hwasan_allocation_functions.cpp +++ b/libsanitizer/hwasan/hwasan_allocation_functions.cpp @@ -17,7 +17,6 @@ #include "sanitizer_common/sanitizer_allocator_dlsym.h" #include "sanitizer_common/sanitizer_allocator_interface.h" #include "sanitizer_common/sanitizer_mallinfo.h" -#include "sanitizer_common/sanitizer_tls_get_addr.h" using namespace __hwasan; @@ -62,10 +61,7 @@ void *__sanitizer_aligned_alloc(uptr alignment, uptr size) { SANITIZER_INTERFACE_ATTRIBUTE void *__sanitizer___libc_memalign(uptr alignment, uptr size) { GET_MALLOC_STACK_TRACE; - void *ptr = hwasan_memalign(alignment, size, &stack); - if (ptr) - DTLS_on_libc_memalign(ptr, size); - return ptr; + return hwasan_memalign(alignment, size, &stack); } SANITIZER_INTERFACE_ATTRIBUTE @@ -184,7 +180,7 @@ INTERCEPTOR_ALIAS(void *, malloc, SIZE_T size); INTERCEPTOR_ALIAS(void *, memalign, SIZE_T alignment, SIZE_T size); INTERCEPTOR_ALIAS(void *, pvalloc, SIZE_T size); INTERCEPTOR_ALIAS(void, cfree, void *ptr); -INTERCEPTOR_ALIAS(__sanitizer_struct_mallinfo, mallinfo); +INTERCEPTOR_ALIAS(__sanitizer_struct_mallinfo, mallinfo,); INTERCEPTOR_ALIAS(int, mallopt, int cmd, int value); INTERCEPTOR_ALIAS(void, malloc_stats, void); # endif diff --git a/libsanitizer/hwasan/hwasan_allocator.cpp b/libsanitizer/hwasan/hwasan_allocator.cpp index d21ba02..75dbb33 100644 --- a/libsanitizer/hwasan/hwasan_allocator.cpp +++ b/libsanitizer/hwasan/hwasan_allocator.cpp @@ -44,7 +44,7 @@ enum { // Initialized in HwasanAllocatorInit, an never changed. -static ALIGNED(16) u8 tail_magic[kShadowAlignment - 1]; +alignas(16) static u8 tail_magic[kShadowAlignment - 1]; static uptr max_malloc_size; bool HwasanChunkView::IsAllocated() const { @@ -289,6 +289,9 @@ static void HwasanDeallocate(StackTrace *stack, void *tagged_ptr) { CHECK(tagged_ptr); void *untagged_ptr = UntagPtr(tagged_ptr); + if (RunFreeHooks(tagged_ptr)) + return; + if (CheckInvalidFree(stack, untagged_ptr, tagged_ptr)) return; @@ -302,8 +305,6 @@ static void HwasanDeallocate(StackTrace *stack, void *tagged_ptr) { return; } - RunFreeHooks(tagged_ptr); - uptr orig_size = meta->GetRequestedSize(); u32 free_context_id = StackDepotPut(*stack); u32 alloc_context_id = meta->GetAllocStackId(); diff --git a/libsanitizer/hwasan/hwasan_checks.h b/libsanitizer/hwasan/hwasan_checks.h index 0911af3..735d21a 100644 --- a/libsanitizer/hwasan/hwasan_checks.h +++ b/libsanitizer/hwasan/hwasan_checks.h @@ -140,7 +140,6 @@ __attribute__((always_inline, nodebug)) static inline uptr ShortTagSize( __attribute__((always_inline, nodebug)) static inline bool PossiblyShortTagMatches(tag_t mem_tag, uptr ptr, uptr sz) { - DCHECK(IsAligned(ptr, kShadowAlignment)); tag_t ptr_tag = GetTagFromPointer(ptr); if (ptr_tag == mem_tag) return true; diff --git a/libsanitizer/hwasan/hwasan_dynamic_shadow.cpp b/libsanitizer/hwasan/hwasan_dynamic_shadow.cpp index 7642ba6..48bc3b6 100644 --- a/libsanitizer/hwasan/hwasan_dynamic_shadow.cpp +++ b/libsanitizer/hwasan/hwasan_dynamic_shadow.cpp @@ -36,15 +36,20 @@ decltype(__hwasan_shadow)* __hwasan_premap_shadow(); namespace __hwasan { +// We cannot call anything in libc here (see comment above), so we need to +// assume the biggest allowed page size. +// Android max page size is defined as 16k here: +// https://android.googlesource.com/platform/bionic/+/main/libc/platform/bionic/page.h#41 +static constexpr uptr kMaxGranularity = 16384; + // Conservative upper limit. static uptr PremapShadowSize() { - return RoundUpTo(GetMaxVirtualAddress() >> kShadowScale, - GetMmapGranularity()); + return RoundUpTo(GetMaxVirtualAddress() >> kShadowScale, kMaxGranularity); } static uptr PremapShadow() { return MapDynamicShadow(PremapShadowSize(), kShadowScale, - kShadowBaseAlignment, kHighMemEnd); + kShadowBaseAlignment, kHighMemEnd, kMaxGranularity); } static bool IsPremapShadowAvailable() { @@ -56,7 +61,7 @@ static bool IsPremapShadowAvailable() { } static uptr FindPremappedShadowStart(uptr shadow_size_bytes) { - const uptr granularity = GetMmapGranularity(); + const uptr granularity = kMaxGranularity; const uptr shadow_start = reinterpret_cast<uptr>(&__hwasan_shadow); const uptr premap_shadow_size = PremapShadowSize(); const uptr shadow_size = RoundUpTo(shadow_size_bytes, granularity); @@ -109,7 +114,7 @@ uptr FindDynamicShadowStart(uptr shadow_size_bytes) { if (IsPremapShadowAvailable()) return FindPremappedShadowStart(shadow_size_bytes); return MapDynamicShadow(shadow_size_bytes, kShadowScale, kShadowBaseAlignment, - kHighMemEnd); + kHighMemEnd, kMaxGranularity); } } // namespace __hwasan @@ -135,7 +140,7 @@ uptr FindDynamicShadowStart(uptr shadow_size_bytes) { RingBufferSize()); # endif return MapDynamicShadow(shadow_size_bytes, kShadowScale, kShadowBaseAlignment, - kHighMemEnd); + kHighMemEnd, GetMmapGranularity()); } } // namespace __hwasan diff --git a/libsanitizer/hwasan/hwasan_flags.inc b/libsanitizer/hwasan/hwasan_flags.inc index 978fa46..058a045 100644 --- a/libsanitizer/hwasan/hwasan_flags.inc +++ b/libsanitizer/hwasan/hwasan_flags.inc @@ -84,3 +84,10 @@ HWASAN_FLAG(bool, malloc_bisect_dump, false, // are untagged before the call. HWASAN_FLAG(bool, fail_without_syscall_abi, true, "Exit if fail to request relaxed syscall ABI.") + +HWASAN_FLAG( + uptr, fixed_shadow_base, -1, + "If not -1, HWASan will attempt to allocate the shadow at this address, " + "instead of choosing one dynamically." + "Tip: this can be combined with the compiler option, " + "-hwasan-mapping-offset, to optimize the instrumentation.") diff --git a/libsanitizer/hwasan/hwasan_interceptors.cpp b/libsanitizer/hwasan/hwasan_interceptors.cpp index 96df4dd..c10b5c1 100644 --- a/libsanitizer/hwasan/hwasan_interceptors.cpp +++ b/libsanitizer/hwasan/hwasan_interceptors.cpp @@ -334,10 +334,10 @@ INTERCEPTOR(int, pthread_timedjoin_np, void *thread, void **ret, } # endif -DEFINE_REAL_PTHREAD_FUNCTIONS +DEFINE_INTERNAL_PTHREAD_FUNCTIONS -DEFINE_REAL(int, vfork) -DECLARE_EXTERN_INTERCEPTOR_AND_WRAPPER(int, vfork) +DEFINE_REAL(int, vfork,) +DECLARE_EXTERN_INTERCEPTOR_AND_WRAPPER(int, vfork,) // Get and/or change the set of blocked signals. extern "C" int sigprocmask(int __how, const __hw_sigset_t *__restrict __set, @@ -520,6 +520,7 @@ void InitializeInterceptors() { CHECK_EQ(inited, 0); # if HWASAN_WITH_INTERCEPTORS + __interception::DoesNotSupportStaticLinking(); InitializeCommonInterceptors(); (void)(read_iovec); diff --git a/libsanitizer/hwasan/hwasan_interface_internal.h b/libsanitizer/hwasan/hwasan_interface_internal.h index e7804cc..8f2f77d 100644 --- a/libsanitizer/hwasan/hwasan_interface_internal.h +++ b/libsanitizer/hwasan/hwasan_interface_internal.h @@ -161,6 +161,9 @@ SANITIZER_INTERFACE_ATTRIBUTE uptr __hwasan_tag_pointer(uptr p, u8 tag); SANITIZER_INTERFACE_ATTRIBUTE +u8 __hwasan_get_tag_from_pointer(uptr p); + +SANITIZER_INTERFACE_ATTRIBUTE void __hwasan_tag_mismatch(uptr addr, u8 ts); SANITIZER_INTERFACE_ATTRIBUTE diff --git a/libsanitizer/hwasan/hwasan_linux.cpp b/libsanitizer/hwasan/hwasan_linux.cpp index 6f5e943..68651d3 100644 --- a/libsanitizer/hwasan/hwasan_linux.cpp +++ b/libsanitizer/hwasan/hwasan_linux.cpp @@ -106,8 +106,22 @@ static uptr GetHighMemEnd() { } static void InitializeShadowBaseAddress(uptr shadow_size_bytes) { - __hwasan_shadow_memory_dynamic_address = - FindDynamicShadowStart(shadow_size_bytes); + // FIXME: Android should init flags before shadow. + if (!SANITIZER_ANDROID && flags()->fixed_shadow_base != (uptr)-1) { + __hwasan_shadow_memory_dynamic_address = flags()->fixed_shadow_base; + uptr beg = __hwasan_shadow_memory_dynamic_address; + uptr end = beg + shadow_size_bytes; + if (!MemoryRangeIsAvailable(beg, end)) { + Report( + "FATAL: HWAddressSanitizer: Shadow range %p-%p is not available.\n", + (void *)beg, (void *)end); + DumpProcessMap(); + CHECK(MemoryRangeIsAvailable(beg, end)); + } + } else { + __hwasan_shadow_memory_dynamic_address = + FindDynamicShadowStart(shadow_size_bytes); + } } static void MaybeDieIfNoTaggingAbi(const char *message) { @@ -246,9 +260,6 @@ bool InitShadow() { CHECK_GT(kLowShadowEnd, kLowShadowStart); CHECK_GT(kLowShadowStart, kLowMemEnd); - if (Verbosity()) - PrintAddressSpaceLayout(); - // Reserve shadow memory. ReserveShadowMemoryRange(kLowShadowStart, kLowShadowEnd, "low shadow"); ReserveShadowMemoryRange(kHighShadowStart, kHighShadowEnd, "high shadow"); @@ -262,6 +273,9 @@ bool InitShadow() { if (kHighShadowEnd + 1 < kHighMemStart) ProtectGap(kHighShadowEnd + 1, kHighMemStart - kHighShadowEnd - 1); + if (Verbosity()) + PrintAddressSpaceLayout(); + return true; } @@ -294,25 +308,6 @@ void InstallAtExitHandler() { atexit(HwasanAtExit); } // ---------------------- TSD ---------------- {{{1 -extern "C" void __hwasan_thread_enter() { - hwasanThreadList().CreateCurrentThread()->EnsureRandomStateInited(); -} - -extern "C" void __hwasan_thread_exit() { - Thread *t = GetCurrentThread(); - // Make sure that signal handler can not see a stale current thread pointer. - atomic_signal_fence(memory_order_seq_cst); - if (t) { - // Block async signals on the thread as the handler can be instrumented. - // After this point instrumented code can't access essential data from TLS - // and will crash. - // Bionic already calls __hwasan_thread_exit with blocked signals. - if (SANITIZER_GLIBC) - BlockSignals(); - hwasanThreadList().ReleaseThread(t); - } -} - # if HWASAN_WITH_INTERCEPTORS static pthread_key_t tsd_key; static bool tsd_key_inited = false; @@ -504,12 +499,8 @@ void HwasanOnDeadlySignal(int signo, void *info, void *context) { } void Thread::InitStackAndTls(const InitState *) { - uptr tls_size; - uptr stack_size; - GetThreadStackAndTls(IsMainThread(), &stack_bottom_, &stack_size, &tls_begin_, - &tls_size); - stack_top_ = stack_bottom_ + stack_size; - tls_end_ = tls_begin_ + tls_size; + GetThreadStackAndTls(IsMainThread(), &stack_bottom_, &stack_top_, &tls_begin_, + &tls_end_); } uptr TagMemoryAligned(uptr p, uptr size, tag_t tag) { @@ -536,16 +527,34 @@ uptr TagMemoryAligned(uptr p, uptr size, tag_t tag) { return AddTagToPointer(p, tag); } +static void BeforeFork() { + VReport(2, "BeforeFork tid: %llu\n", GetTid()); + if (CAN_SANITIZE_LEAKS) { + __lsan::LockGlobal(); + } + // `_lsan` functions defined regardless of `CAN_SANITIZE_LEAKS` and lock the + // stuff we need. + __lsan::LockThreads(); + __lsan::LockAllocator(); + StackDepotLockBeforeFork(); +} + +static void AfterFork(bool fork_child) { + StackDepotUnlockAfterFork(fork_child); + // `_lsan` functions defined regardless of `CAN_SANITIZE_LEAKS` and unlock + // the stuff we need. + __lsan::UnlockAllocator(); + __lsan::UnlockThreads(); + if (CAN_SANITIZE_LEAKS) { + __lsan::UnlockGlobal(); + } + VReport(2, "AfterFork tid: %llu\n", GetTid()); +} + void HwasanInstallAtForkHandler() { - auto before = []() { - HwasanAllocatorLock(); - StackDepotLockAll(); - }; - auto after = []() { - StackDepotUnlockAll(); - HwasanAllocatorUnlock(); - }; - pthread_atfork(before, after, after); + pthread_atfork( + &BeforeFork, []() { AfterFork(/* fork_child= */ false); }, + []() { AfterFork(/* fork_child= */ true); }); } void InstallAtExitCheckLeaks() { @@ -561,4 +570,25 @@ void InstallAtExitCheckLeaks() { } // namespace __hwasan +using namespace __hwasan; + +extern "C" void __hwasan_thread_enter() { + hwasanThreadList().CreateCurrentThread()->EnsureRandomStateInited(); +} + +extern "C" void __hwasan_thread_exit() { + Thread *t = GetCurrentThread(); + // Make sure that signal handler can not see a stale current thread pointer. + atomic_signal_fence(memory_order_seq_cst); + if (t) { + // Block async signals on the thread as the handler can be instrumented. + // After this point instrumented code can't access essential data from TLS + // and will crash. + // Bionic already calls __hwasan_thread_exit with blocked signals. + if (SANITIZER_GLIBC) + BlockSignals(); + hwasanThreadList().ReleaseThread(t); + } +} + #endif // SANITIZER_FREEBSD || SANITIZER_LINUX || SANITIZER_NETBSD diff --git a/libsanitizer/hwasan/hwasan_preinit.cpp b/libsanitizer/hwasan/hwasan_preinit.cpp index 8c9c95f..8da47b5 100644 --- a/libsanitizer/hwasan/hwasan_preinit.cpp +++ b/libsanitizer/hwasan/hwasan_preinit.cpp @@ -14,10 +14,8 @@ #include "sanitizer_common/sanitizer_internal_defs.h" #if SANITIZER_CAN_USE_PREINIT_ARRAY -// The symbol is called __local_hwasan_preinit, because it's not intended to -// be exported. -// This code linked into the main executable when -fsanitize=hwaddress is in -// the link flags. It can only use exported interface functions. -__attribute__((section(".preinit_array"), used)) static void ( - *__local_hwasan_preinit)(void) = __hwasan_init; +// This section is linked into the main executable when -fsanitize=hwaddress is +// specified to perform initialization at a very early stage. +__attribute__((section(".preinit_array"), used)) static auto preinit = + __hwasan_init; #endif diff --git a/libsanitizer/hwasan/hwasan_report.cpp b/libsanitizer/hwasan/hwasan_report.cpp index 5e8aa31..bc66e6e8 100644 --- a/libsanitizer/hwasan/hwasan_report.cpp +++ b/libsanitizer/hwasan/hwasan_report.cpp @@ -27,6 +27,7 @@ #include "sanitizer_common/sanitizer_flags.h" #include "sanitizer_common/sanitizer_internal_defs.h" #include "sanitizer_common/sanitizer_mutex.h" +#include "sanitizer_common/sanitizer_placement_new.h" #include "sanitizer_common/sanitizer_report_decorator.h" #include "sanitizer_common/sanitizer_stackdepot.h" #include "sanitizer_common/sanitizer_stacktrace_printer.h" @@ -40,7 +41,7 @@ class ScopedReport { public: explicit ScopedReport(bool fatal) : fatal(fatal) { Lock lock(&error_message_lock_); - error_message_ptr_ = fatal ? &error_message_ : nullptr; + error_message_ptr_ = &error_message_; ++hwasan_report_count; } @@ -205,6 +206,7 @@ static void PrintStackAllocations(const StackAllocationsRingBuffer *sa, tag_t addr_tag, uptr untagged_addr) { uptr frames = Min((uptr)flags()->stack_history_size, sa->size()); bool found_local = false; + InternalScopedString location; for (uptr i = 0; i < frames; i++) { const uptr *record_addr = &(*sa)[i]; uptr record = *record_addr; @@ -212,35 +214,104 @@ static void PrintStackAllocations(const StackAllocationsRingBuffer *sa, break; tag_t base_tag = reinterpret_cast<uptr>(record_addr) >> kRecordAddrBaseTagShift; - uptr fp = (record >> kRecordFPShift) << kRecordFPLShift; + const uptr fp = (record >> kRecordFPShift) << kRecordFPLShift; + CHECK_LT(fp, kRecordFPModulus); uptr pc_mask = (1ULL << kRecordFPShift) - 1; uptr pc = record & pc_mask; FrameInfo frame; - if (Symbolizer::GetOrInit()->SymbolizeFrame(pc, &frame)) { - for (LocalInfo &local : frame.locals) { - if (!local.has_frame_offset || !local.has_size || !local.has_tag_offset) - continue; - tag_t obj_tag = base_tag ^ local.tag_offset; - if (obj_tag != addr_tag) - continue; - // Calculate the offset from the object address to the faulting - // address. Because we only store bits 4-19 of FP (bits 0-3 are - // guaranteed to be zero), the calculation is performed mod 2^20 and may - // harmlessly underflow if the address mod 2^20 is below the object - // address. - uptr obj_offset = - (untagged_addr - fp - local.frame_offset) & (kRecordFPModulus - 1); - if (obj_offset >= local.size) - continue; - if (!found_local) { - Printf("Potentially referenced stack objects:\n"); - found_local = true; + if (!Symbolizer::GetOrInit()->SymbolizeFrame(pc, &frame)) + continue; + for (LocalInfo &local : frame.locals) { + if (!local.has_frame_offset || !local.has_size || !local.has_tag_offset) + continue; + if (!(local.name && internal_strlen(local.name)) && + !(local.function_name && internal_strlen(local.function_name)) && + !(local.decl_file && internal_strlen(local.decl_file))) + continue; + tag_t obj_tag = base_tag ^ local.tag_offset; + if (obj_tag != addr_tag) + continue; + + // We only store bits 4-19 of FP (bits 0-3 are guaranteed to be zero). + // So we know only `FP % kRecordFPModulus`, and we can only calculate + // `local_beg % kRecordFPModulus`. + // Out of all possible `local_beg` we will only consider 2 candidates + // nearest to the `untagged_addr`. + uptr local_beg_mod = (fp + local.frame_offset) % kRecordFPModulus; + // Pick `local_beg` in the same 1 MiB block as `untagged_addr`. + uptr local_beg = + RoundDownTo(untagged_addr, kRecordFPModulus) + local_beg_mod; + // Pick the largest `local_beg <= untagged_addr`. It's either the current + // one or the one before. + if (local_beg > untagged_addr) + local_beg -= kRecordFPModulus; + + uptr offset = -1ull; + const char *whence; + const char *cause = nullptr; + uptr best_beg; + + // Try two 1 MiB blocks options and pick nearest one. + for (uptr i = 0; i < 2; ++i, local_beg += kRecordFPModulus) { + uptr local_end = local_beg + local.size; + if (local_beg > local_end) + continue; // This is a wraparound. + if (local_beg <= untagged_addr && untagged_addr < local_end) { + offset = untagged_addr - local_beg; + whence = "inside"; + cause = "use-after-scope"; + best_beg = local_beg; + break; // This is as close at it can be. + } + + if (untagged_addr >= local_end) { + uptr new_offset = untagged_addr - local_end; + if (new_offset < offset) { + offset = new_offset; + whence = "after"; + cause = "stack-buffer-overflow"; + best_beg = local_beg; + } + } else { + uptr new_offset = local_beg - untagged_addr; + if (new_offset < offset) { + offset = new_offset; + whence = "before"; + cause = "stack-buffer-overflow"; + best_beg = local_beg; + } } - Printf(" %s in %s %s:%d\n", local.name, local.function_name, - local.decl_file, local.decl_line); } - frame.Clear(); + + // To fail the `untagged_addr` must be near nullptr, which is impossible + // with Linux user space memory layout. + if (!cause) + continue; + + if (!found_local) { + Printf("\nPotentially referenced stack objects:\n"); + found_local = true; + } + + Decorator d; + Printf("%s", d.Error()); + Printf("Cause: %s\n", cause); + Printf("%s", d.Default()); + Printf("%s", d.Location()); + StackTracePrinter::GetOrInit()->RenderSourceLocation( + &location, local.decl_file, local.decl_line, /* column= */ 0, + common_flags()->symbolize_vs_style, + common_flags()->strip_path_prefix); + Printf( + "%p is located %zd bytes %s a %zd-byte local variable %s " + "[%p,%p) " + "in %s %s\n", + untagged_addr, offset, whence, local.size, local.name, best_beg, + best_beg + local.size, local.function_name, location.data()); + location.clear(); + Printf("%s\n", d.Default()); } + frame.Clear(); } if (found_local) @@ -257,14 +328,16 @@ static void PrintStackAllocations(const StackAllocationsRingBuffer *sa, break; uptr pc_mask = (1ULL << 48) - 1; uptr pc = record & pc_mask; - frame_desc.AppendF(" record_addr:0x%zx record:0x%zx", - reinterpret_cast<uptr>(record_addr), record); - if (SymbolizedStack *frame = Symbolizer::GetOrInit()->SymbolizePC(pc)) { + frame_desc.AppendF(" record_addr:%p record:0x%zx", + reinterpret_cast<const void *>(record_addr), record); + SymbolizedStackHolder symbolized_stack( + Symbolizer::GetOrInit()->SymbolizePC(pc)); + const SymbolizedStack *frame = symbolized_stack.get(); + if (frame) { StackTracePrinter::GetOrInit()->RenderFrame( &frame_desc, " %F %L", 0, frame->info.address, &frame->info, common_flags()->symbolize_vs_style, common_flags()->strip_path_prefix); - frame->ClearAll(); } Printf("%s\n", frame_desc.data()); frame_desc.clear(); @@ -353,7 +426,7 @@ static void PrintTagInfoAroundAddr(uptr addr, uptr num_rows, print_tag(s, row + i); s.Append(row + i == addr ? "]" : " "); } - s.AppendF("\n"); + s.Append("\n"); } } @@ -363,7 +436,7 @@ static void PrintTagsAroundAddr(uptr addr, GetTag get_tag, InternalScopedString s; addr = MemToShadow(addr); s.AppendF( - "Memory tags around the buggy address (one tag corresponds to %zd " + "\nMemory tags around the buggy address (one tag corresponds to %zd " "bytes):\n", kShadowAlignment); PrintTagInfoAroundAddr(addr, kShadowLines, s, @@ -383,10 +456,10 @@ static void PrintTagsAroundAddr(uptr addr, GetTag get_tag, tag_t short_tag = get_short_tag(tag_addr); s.AppendF("%02x", short_tag); } else { - s.AppendF(".."); + s.Append(".."); } }); - s.AppendF( + s.Append( "See " "https://clang.llvm.org/docs/" "HardwareAssistedAddressSanitizerDesign.html#short-granules for a " @@ -745,8 +818,6 @@ void BaseReport::PrintAddressDescription() const { // Check stack first. If the address is on the stack of a live thread, we // know it cannot be a heap / global overflow. for (const auto &sa : allocations.stack) { - // TODO(fmayer): figure out how to distinguish use-after-return and - // stack-buffer-overflow. Printf("%s", d.Error()); Printf("\nCause: stack tag-mismatch\n"); Printf("%s", d.Location()); @@ -803,8 +874,10 @@ void BaseReport::PrintAddressDescription() const { } // Print the remaining threads, as an extra information, 1 line per thread. - if (flags()->print_live_threads_info) + if (flags()->print_live_threads_info) { + Printf("\n"); hwasanThreadList().VisitAllLiveThreads([&](Thread *t) { t->Announce(); }); + } if (!num_descriptions_printed) // We exhausted our possibilities. Bail out. @@ -912,16 +985,16 @@ TailOverwrittenReport::~TailOverwrittenReport() { InternalScopedString s; u8 *tail = tail_copy; - s.AppendF("Tail contains: "); - for (uptr i = 0; i < kShadowAlignment - tail_size; i++) s.AppendF(".. "); + s.Append("Tail contains: "); + for (uptr i = 0; i < kShadowAlignment - tail_size; i++) s.Append(".. "); for (uptr i = 0; i < tail_size; i++) s.AppendF("%02x ", tail[i]); - s.AppendF("\n"); - s.AppendF("Expected: "); - for (uptr i = 0; i < kShadowAlignment - tail_size; i++) s.AppendF(".. "); + s.Append("\n"); + s.Append("Expected: "); + for (uptr i = 0; i < kShadowAlignment - tail_size; i++) s.Append(".. "); for (uptr i = 0; i < tail_size; i++) s.AppendF("%02x ", actual_expected[i]); - s.AppendF("\n"); - s.AppendF(" "); - for (uptr i = 0; i < kShadowAlignment - tail_size; i++) s.AppendF(" "); + s.Append("\n"); + s.Append(" "); + for (uptr i = 0; i < kShadowAlignment - tail_size; i++) s.Append(" "); for (uptr i = 0; i < tail_size; i++) s.AppendF("%s ", actual_expected[i] != tail[i] ? "^^" : " "); @@ -1020,7 +1093,7 @@ void ReportTagMismatch(StackTrace *stack, uptr tagged_addr, uptr access_size, // See the frame breakdown defined in __hwasan_tag_mismatch (from // hwasan_tag_mismatch_{aarch64,riscv64}.S). void ReportRegisters(const uptr *frame, uptr pc) { - Printf("Registers where the failure occurred (pc %p):\n", pc); + Printf("\nRegisters where the failure occurred (pc %p):\n", pc); // We explicitly print a single line (4 registers/line) each iteration to // reduce the amount of logcat error messages printed. Each Printf() will diff --git a/libsanitizer/hwasan/hwasan_thread.cpp b/libsanitizer/hwasan/hwasan_thread.cpp index ce36547..8b32e4e 100644 --- a/libsanitizer/hwasan/hwasan_thread.cpp +++ b/libsanitizer/hwasan/hwasan_thread.cpp @@ -68,6 +68,7 @@ void Thread::Init(uptr stack_buffer_start, uptr stack_buffer_size, } Print("Creating : "); } + ClearShadowForThreadStackAndTLS(); } void Thread::InitStackRingBuffer(uptr stack_buffer_start, @@ -217,6 +218,11 @@ void GetAdditionalThreadContextPtrsLocked(InternalMmapVector<uptr> *ptrs) { __hwasan::hwasanThreadArgRetval().GetAllPtrsLocked(ptrs); } -void GetRunningThreadsLocked(InternalMmapVector<tid_t> *threads) {} +void GetRunningThreadsLocked(InternalMmapVector<tid_t> *threads) { + // TODO: implement. +} +void PrintThreads() { + // TODO: implement. +} } // namespace __lsan diff --git a/libsanitizer/hwasan/hwasan_thread_list.cpp b/libsanitizer/hwasan/hwasan_thread_list.cpp index 7df4dd3..794cfb7 100644 --- a/libsanitizer/hwasan/hwasan_thread_list.cpp +++ b/libsanitizer/hwasan/hwasan_thread_list.cpp @@ -1,5 +1,6 @@ #include "hwasan_thread_list.h" +#include "sanitizer_common/sanitizer_placement_new.h" #include "sanitizer_common/sanitizer_thread_arg_retval.h" namespace __hwasan { @@ -13,15 +14,15 @@ ThreadArgRetval &hwasanThreadArgRetval() { return *thread_data; } void InitThreadList(uptr storage, uptr size) { CHECK_EQ(hwasan_thread_list, nullptr); - static ALIGNED(alignof( - HwasanThreadList)) char thread_list_placeholder[sizeof(HwasanThreadList)]; + alignas(alignof(HwasanThreadList)) static char + thread_list_placeholder[sizeof(HwasanThreadList)]; hwasan_thread_list = new (thread_list_placeholder) HwasanThreadList(storage, size); CHECK_EQ(thread_data, nullptr); - static ALIGNED(alignof( - ThreadArgRetval)) char thread_data_placeholder[sizeof(ThreadArgRetval)]; + alignas(alignof(ThreadArgRetval)) static char + thread_data_placeholder[sizeof(ThreadArgRetval)]; thread_data = new (thread_data_placeholder) ThreadArgRetval(); } diff --git a/libsanitizer/hwasan/hwasan_thread_list.h b/libsanitizer/hwasan/hwasan_thread_list.h index 82f6c70..369a1c3 100644 --- a/libsanitizer/hwasan/hwasan_thread_list.h +++ b/libsanitizer/hwasan/hwasan_thread_list.h @@ -18,7 +18,7 @@ // * Start of the shadow memory region is aligned to 2**kShadowBaseAlignment. // * All stack ring buffers are located within (2**kShadowBaseAlignment) // sized region below and adjacent to the shadow region. -// * Each ring buffer has a size of (2**N)*4096 where N is in [0, 8), and is +// * Each ring buffer has a size of (2**N)*4096 where N is in [0, 7), and is // aligned to twice its size. The value of N can be different for each buffer. // // These constrains guarantee that, given an address A of any element of the @@ -47,7 +47,6 @@ #include "hwasan_allocator.h" #include "hwasan_flags.h" #include "hwasan_thread.h" -#include "sanitizer_common/sanitizer_placement_new.h" #include "sanitizer_common/sanitizer_thread_arg_retval.h" namespace __hwasan { @@ -56,7 +55,10 @@ static uptr RingBufferSize() { uptr desired_bytes = flags()->stack_history_size * sizeof(uptr); // FIXME: increase the limit to 8 once this bug is fixed: // https://bugs.llvm.org/show_bug.cgi?id=39030 - for (int shift = 1; shift < 7; ++shift) { + // Note that we *cannot* do that on Android, as the runtime will indefinitely + // have to support code that is compiled with ashr, which only works with + // shifts up to 6. + for (int shift = 0; shift < 7; ++shift) { uptr size = 4096 * (1ULL << shift); if (size >= desired_bytes) return size; diff --git a/libsanitizer/include/sanitizer/allocator_interface.h b/libsanitizer/include/sanitizer/allocator_interface.h index a792e9f..1696a92 100644 --- a/libsanitizer/include/sanitizer/allocator_interface.h +++ b/libsanitizer/include/sanitizer/allocator_interface.h @@ -62,13 +62,20 @@ size_t SANITIZER_CDECL __sanitizer_get_free_bytes(void); size_t SANITIZER_CDECL __sanitizer_get_unmapped_bytes(void); /* Malloc hooks that may be optionally provided by user. - __sanitizer_malloc_hook(ptr, size) is called immediately after - allocation of "size" bytes, which returned "ptr". - __sanitizer_free_hook(ptr) is called immediately before - deallocation of "ptr". */ + - __sanitizer_malloc_hook(ptr, size) is called immediately after allocation + of "size" bytes, which returned "ptr". + - __sanitizer_free_hook(ptr) is called immediately before deallocation of + "ptr". + - __sanitizer_ignore_free_hook(ptr) is called immediately before deallocation + of "ptr", and if it returns a non-zero value, the deallocation of "ptr" + will not take place. This allows software to make free a no-op until it + calls free() again in the same pointer at a later time. Hint: read this as + "ignore the free" rather than "ignore the hook". +*/ void SANITIZER_CDECL __sanitizer_malloc_hook(const volatile void *ptr, size_t size); void SANITIZER_CDECL __sanitizer_free_hook(const volatile void *ptr); +int SANITIZER_CDECL __sanitizer_ignore_free_hook(const volatile void *ptr); /* Installs a pair of hooks for malloc/free. Several (currently, 5) hook pairs may be installed, they are executed diff --git a/libsanitizer/include/sanitizer/common_interface_defs.h b/libsanitizer/include/sanitizer/common_interface_defs.h index 56d9e00..57313f9 100644 --- a/libsanitizer/include/sanitizer/common_interface_defs.h +++ b/libsanitizer/include/sanitizer/common_interface_defs.h @@ -193,6 +193,43 @@ void SANITIZER_CDECL __sanitizer_annotate_double_ended_contiguous_container( const void *old_container_beg, const void *old_container_end, const void *new_container_beg, const void *new_container_end); +/// Copies memory annotations from a source storage region to a destination +/// storage region. After the operation, the destination region has the same +/// memory annotations as the source region, as long as sanitizer limitations +/// allow it (more bytes may be unpoisoned than in the source region, resulting +/// in more false negatives, but never false positives). If the source and +/// destination regions overlap, only the minimal required changes are made to +/// preserve the correct annotations. Old storage bytes that are not in the new +/// storage should have the same annotations, as long as sanitizer limitations +/// allow it. +/// +/// This function is primarily designed to be used when moving trivially +/// relocatable objects that may have poisoned memory, making direct copying +/// problematic under sanitizer. However, this function does not move memory +/// content itself, only annotations. +/// +/// A contiguous container is a container that keeps all of its elements in a +/// contiguous region of memory. The container owns the region of memory +/// <c>[src_begin, src_end)</c> and <c>[dst_begin, dst_end)</c>. The memory +/// within these regions may be alternately poisoned and non-poisoned, with +/// possibly smaller poisoned and unpoisoned regions. +/// +/// If this function fully poisons a granule, it is marked as "container +/// overflow". +/// +/// Argument requirements: The destination container must have the same size as +/// the source container, which is inferred from the beginning and end of the +/// source region. Addresses may be granule-unaligned, but this may affect +/// performance. +/// +/// \param src_begin Begin of the source container region. +/// \param src_end End of the source container region. +/// \param dst_begin Begin of the destination container region. +/// \param dst_end End of the destination container region. +void SANITIZER_CDECL __sanitizer_copy_contiguous_container_annotations( + const void *src_begin, const void *src_end, const void *dst_begin, + const void *dst_end); + /// Returns true if the contiguous container <c>[beg, end)</c> is properly /// poisoned. /// @@ -293,7 +330,7 @@ void SANITIZER_CDECL __sanitizer_symbolize_global(void *data_ptr, #define __sanitizer_return_address() \ __builtin_extract_return_addr(__builtin_return_address(0)) #else -void *SANITIZER_CDECL _ReturnAddress(void); +void *_ReturnAddress(void); #pragma intrinsic(_ReturnAddress) #define __sanitizer_return_address() _ReturnAddress() #endif diff --git a/libsanitizer/include/sanitizer/hwasan_interface.h b/libsanitizer/include/sanitizer/hwasan_interface.h index abe310c..407f488 100644 --- a/libsanitizer/include/sanitizer/hwasan_interface.h +++ b/libsanitizer/include/sanitizer/hwasan_interface.h @@ -44,6 +44,10 @@ void SANITIZER_CDECL __hwasan_tag_memory(const volatile void *p, void *SANITIZER_CDECL __hwasan_tag_pointer(const volatile void *p, unsigned char tag); +/// Get tag from the pointer. +unsigned char SANITIZER_CDECL +__hwasan_get_tag_from_pointer(const volatile void *p); + // Set memory tag from the current SP address to the given address to zero. // This is meant to annotate longjmp and other non-local jumps. // This function needs to know the (almost) exact destination frame address; diff --git a/libsanitizer/include/sanitizer/linux_syscall_hooks.h b/libsanitizer/include/sanitizer/linux_syscall_hooks.h index 3f3f1e7..5f26245 100644 --- a/libsanitizer/include/sanitizer/linux_syscall_hooks.h +++ b/libsanitizer/include/sanitizer/linux_syscall_hooks.h @@ -1856,6 +1856,15 @@ __sanitizer_syscall_pre_impl_sigaltstack((long)ss, (long)oss) #define __sanitizer_syscall_post_sigaltstack(res, ss, oss) \ __sanitizer_syscall_post_impl_sigaltstack(res, (long)ss, (long)oss) +#define __sanitizer_syscall_pre_futex(uaddr, futex_op, val, timeout, uaddr2, \ + val3) \ + __sanitizer_syscall_pre_impl_futex((long)uaddr, (long)futex_op, (long)val, \ + (long)timeout, (long)uaddr2, (long)val3) +#define __sanitizer_syscall_post_futex(res, uaddr, futex_op, val, timeout, \ + uaddr2, val3) \ + __sanitizer_syscall_post_impl_futex(res, (long)uaddr, (long)futex_op, \ + (long)val, (long)timeout, (long)uaddr2, \ + (long)val3) // And now a few syscalls we don't handle yet. #define __sanitizer_syscall_pre_afs_syscall(...) @@ -1875,7 +1884,6 @@ #define __sanitizer_syscall_pre_fchown32(...) #define __sanitizer_syscall_pre_ftime(...) #define __sanitizer_syscall_pre_ftruncate64(...) -#define __sanitizer_syscall_pre_futex(...) #define __sanitizer_syscall_pre_getegid32(...) #define __sanitizer_syscall_pre_geteuid32(...) #define __sanitizer_syscall_pre_getgid32(...) @@ -1954,7 +1962,6 @@ #define __sanitizer_syscall_post_fchown32(res, ...) #define __sanitizer_syscall_post_ftime(res, ...) #define __sanitizer_syscall_post_ftruncate64(res, ...) -#define __sanitizer_syscall_post_futex(res, ...) #define __sanitizer_syscall_post_getegid32(res, ...) #define __sanitizer_syscall_post_geteuid32(res, ...) #define __sanitizer_syscall_post_getgid32(res, ...) @@ -3093,6 +3100,11 @@ void __sanitizer_syscall_post_impl_rt_sigaction(long res, long signum, long act, long oldact, long sz); void __sanitizer_syscall_pre_impl_sigaltstack(long ss, long oss); void __sanitizer_syscall_post_impl_sigaltstack(long res, long ss, long oss); +void __sanitizer_syscall_pre_impl_futex(long uaddr, long futex_op, long val, + long timeout, long uaddr2, long val3); +void __sanitizer_syscall_post_impl_futex(long res, long uaddr, long futex_op, + long val, long timeout, long uaddr2, + long val3); #ifdef __cplusplus } // extern "C" #endif diff --git a/libsanitizer/include/sanitizer/memprof_interface.h b/libsanitizer/include/sanitizer/memprof_interface.h index fe0a2fc..4660a78 100644 --- a/libsanitizer/include/sanitizer/memprof_interface.h +++ b/libsanitizer/include/sanitizer/memprof_interface.h @@ -59,6 +59,12 @@ const char *SANITIZER_CDECL __memprof_default_options(void); /// \returns 0 on success. int SANITIZER_CDECL __memprof_profile_dump(void); +/// Closes the existing file descriptor, if it is valid and not stdout or +/// stderr, and resets the internal state such that the profile filename is +/// reopened on the next profile dump attempt. This can be used to enable +/// multiple rounds of profiling on the same binary. +void SANITIZER_CDECL __memprof_profile_reset(void); + #ifdef __cplusplus } // extern "C" #endif diff --git a/libsanitizer/include/sanitizer/nsan_interface.h b/libsanitizer/include/sanitizer/nsan_interface.h new file mode 100644 index 0000000..057ca04 --- /dev/null +++ b/libsanitizer/include/sanitizer/nsan_interface.h @@ -0,0 +1,75 @@ +//===-- sanitizer/nsan_interface.h ------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// Public interface for nsan. +// +//===----------------------------------------------------------------------===// +#ifndef SANITIZER_NSAN_INTERFACE_H +#define SANITIZER_NSAN_INTERFACE_H + +#include <sanitizer/common_interface_defs.h> + +#ifdef __cplusplus +extern "C" { +#endif + +/// User-provided default option settings. +/// +/// You can provide your own implementation of this function to return a string +/// containing NSan runtime options (for example, +/// <c>verbosity=1:halt_on_error=0</c>). +/// +/// \returns Default options string. +const char *__nsan_default_options(void); + +// Dumps nsan shadow data for a block of `size_bytes` bytes of application +// memory at location `addr`. +// +// Each line contains application address, shadow types, then values. +// Unknown types are shown as `__`, while known values are shown as +// `f`, `d`, `l` for float, double, and long double respectively. Position is +// shown as a single hex digit. The shadow value itself appears on the line that +// contains the first byte of the value. +// FIXME: Show both shadow and application value. +// +// Example: `__nsan_dump_shadow_mem(addr, 32, 8, 0)` might print: +// +// 0x0add7359: __ f0 f1 f2 f3 __ __ __ (42.000) +// 0x0add7361: __ d1 d2 d3 d4 d5 d6 d7 +// 0x0add7369: d8 f0 f1 f2 f3 __ __ f2 (-1.000) (12.5) +// 0x0add7371: f3 __ __ __ __ __ __ __ +// +// This means that there is: +// - a shadow double for the float at address 0x0add7360, with value 42; +// - a shadow float128 for the double at address 0x0add7362, with value -1; +// - a shadow double for the float at address 0x0add736a, with value 12.5; +// There was also a shadow double for the float at address 0x0add736e, but bytes +// f0 and f1 were overwritten by one or several stores, so that the shadow value +// is no longer valid. +// The argument `reserved` can be any value. Its true value is provided by the +// instrumentation. +void __nsan_dump_shadow_mem(const char *addr, size_t size_bytes, + size_t bytes_per_line, size_t reserved); + +// Explicitly dumps a value. +// FIXME: vector versions ? +void __nsan_dump_float(float value); +void __nsan_dump_double(double value); +void __nsan_dump_longdouble(long double value); + +// Explicitly checks a value. +// FIXME: vector versions ? +void __nsan_check_float(float value); +void __nsan_check_double(double value); +void __nsan_check_longdouble(long double value); + +#ifdef __cplusplus +} // extern "C" +#endif + +#endif // SANITIZER_NSAN_INTERFACE_H diff --git a/libsanitizer/include/sanitizer/rtsan_interface.h b/libsanitizer/include/sanitizer/rtsan_interface.h new file mode 100644 index 0000000..5d7ce53 --- /dev/null +++ b/libsanitizer/include/sanitizer/rtsan_interface.h @@ -0,0 +1,75 @@ +//===-- sanitizer/rtsan_interface.h -----------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This file is a part of RealtimeSanitizer. +// +// Public interface header. +//===----------------------------------------------------------------------===// + +#ifndef SANITIZER_RTSAN_INTERFACE_H +#define SANITIZER_RTSAN_INTERFACE_H + +#include <sanitizer/common_interface_defs.h> + +#ifdef __cplusplus +extern "C" { +#endif // __cplusplus + +// Disable all RTSan error reporting. +// Must be paired with a call to `__rtsan_enable` +void SANITIZER_CDECL __rtsan_disable(void); + +// Re-enable all RTSan error reporting. +// Must follow a call to `__rtsan_disable`. +void SANITIZER_CDECL __rtsan_enable(void); + +#ifdef __cplusplus +} // extern "C" + +namespace __rtsan { +#if defined(__has_feature) && __has_feature(realtime_sanitizer) + +class ScopedDisabler { +public: + ScopedDisabler() { __rtsan_disable(); } + ~ScopedDisabler() { __rtsan_enable(); } + +#if __cplusplus >= 201103L + ScopedDisabler(const ScopedDisabler &) = delete; + ScopedDisabler &operator=(const ScopedDisabler &) = delete; + ScopedDisabler(ScopedDisabler &&) = delete; + ScopedDisabler &operator=(ScopedDisabler &&) = delete; +#else +private: + ScopedDisabler(const ScopedDisabler &); + ScopedDisabler &operator=(const ScopedDisabler &); +#endif // __cplusplus >= 201103L +}; + +#else + +class ScopedDisabler { +public: + ScopedDisabler() {} +#if __cplusplus >= 201103L + ScopedDisabler(const ScopedDisabler &) = delete; + ScopedDisabler &operator=(const ScopedDisabler &) = delete; + ScopedDisabler(ScopedDisabler &&) = delete; + ScopedDisabler &operator=(ScopedDisabler &&) = delete; +#else +private: + ScopedDisabler(const ScopedDisabler &); + ScopedDisabler &operator=(const ScopedDisabler &); +#endif // __cplusplus >= 201103L +}; + +#endif // defined(__has_feature) && __has_feature(realtime_sanitizer) +} // namespace __rtsan +#endif // __cplusplus + +#endif // SANITIZER_RTSAN_INTERFACE_H diff --git a/libsanitizer/include/sanitizer/ubsan_interface.h b/libsanitizer/include/sanitizer/ubsan_interface.h index 435eb1a..30a7fd8 100644 --- a/libsanitizer/include/sanitizer/ubsan_interface.h +++ b/libsanitizer/include/sanitizer/ubsan_interface.h @@ -13,6 +13,8 @@ #ifndef SANITIZER_UBSAN_INTERFACE_H #define SANITIZER_UBSAN_INTERFACE_H +#include <sanitizer/common_interface_defs.h> + #ifdef __cplusplus extern "C" { #endif diff --git a/libsanitizer/interception/interception.h b/libsanitizer/interception/interception.h index 73135b3..0580d97 100644 --- a/libsanitizer/interception/interception.h +++ b/libsanitizer/interception/interception.h @@ -25,8 +25,19 @@ // These typedefs should be used only in the interceptor definitions to replace // the standard system types (e.g. SSIZE_T instead of ssize_t) -typedef __sanitizer::uptr SIZE_T; -typedef __sanitizer::sptr SSIZE_T; +// On Windows the system headers (basetsd.h) provide a conflicting definition +// of SIZE_T/SSIZE_T that do not match the real size_t/ssize_t for 32-bit +// systems (using long instead of the expected int). Work around the typedef +// redefinition by #defining SIZE_T instead of using a typedef. +// TODO: We should be using __sanitizer::usize (and a new ssize) instead of +// these new macros as long as we ensure they match the real system definitions. +#if SANITIZER_WINDOWS +// Ensure that (S)SIZE_T were already defined as we are about to override them. +# include <basetsd.h> +#endif + +#define SIZE_T __sanitizer::usize +#define SSIZE_T __sanitizer::sptr typedef __sanitizer::sptr PTRDIFF_T; typedef __sanitizer::s64 INTMAX_T; typedef __sanitizer::u64 UINTMAX_T; @@ -338,16 +349,20 @@ const interpose_substitution substitution_##func_name[] \ #endif // ISO C++ forbids casting between pointer-to-function and pointer-to-object, -// so we use casting via an integral type __interception::uptr, -// assuming that system is POSIX-compliant. Using other hacks seem -// challenging, as we don't even pass function type to -// INTERCEPT_FUNCTION macro, only its name. +// so we use casts via uintptr_t (the local __sanitizer::uptr equivalent). namespace __interception { -#if defined(_WIN64) -typedef unsigned long long uptr; + +#if defined(__ELF__) && !SANITIZER_FUCHSIA +// The use of interceptors makes many sanitizers unusable for static linking. +// Define a function, if called, will cause a linker error (undefined _DYNAMIC). +// However, -static-pie (which is not common) cannot be detected at link time. +extern uptr kDynamic[] asm("_DYNAMIC"); +inline void DoesNotSupportStaticLinking() { + [[maybe_unused]] volatile auto x = &kDynamic; +} #else -typedef unsigned long uptr; -#endif // _WIN64 +inline void DoesNotSupportStaticLinking() {} +#endif } // namespace __interception #define INCLUDED_FROM_INTERCEPTION_LIB diff --git a/libsanitizer/interception/interception_linux.h b/libsanitizer/interception/interception_linux.h index 433a3d9..2e01ff4 100644 --- a/libsanitizer/interception/interception_linux.h +++ b/libsanitizer/interception/interception_linux.h @@ -28,12 +28,14 @@ bool InterceptFunction(const char *name, const char *ver, uptr *ptr_to_real, uptr func, uptr trampoline); } // namespace __interception -#define INTERCEPT_FUNCTION_LINUX_OR_FREEBSD(func) \ - ::__interception::InterceptFunction( \ - #func, \ - (::__interception::uptr *)&REAL(func), \ - (::__interception::uptr)&(func), \ - (::__interception::uptr)&TRAMPOLINE(func)) +// Cast func to type of REAL(func) before casting to uptr in case it is an +// overloaded function, which is the case for some glibc functions when +// _FORTIFY_SOURCE is used. This disambiguates which overload to use. +#define INTERCEPT_FUNCTION_LINUX_OR_FREEBSD(func) \ + ::__interception::InterceptFunction( \ + #func, (::__interception::uptr *)&REAL(func), \ + (::__interception::uptr)(decltype(REAL(func)))&(func), \ + (::__interception::uptr) &TRAMPOLINE(func)) // dlvsym is a GNU extension supported by some other platforms. #if SANITIZER_GLIBC || SANITIZER_FREEBSD || SANITIZER_NETBSD @@ -41,7 +43,7 @@ bool InterceptFunction(const char *name, const char *ver, uptr *ptr_to_real, ::__interception::InterceptFunction( \ #func, symver, \ (::__interception::uptr *)&REAL(func), \ - (::__interception::uptr)&(func), \ + (::__interception::uptr)(decltype(REAL(func)))&(func), \ (::__interception::uptr)&TRAMPOLINE(func)) #else #define INTERCEPT_FUNCTION_VER_LINUX_OR_FREEBSD(func, symver) \ diff --git a/libsanitizer/interception/interception_type_test.cpp b/libsanitizer/interception/interception_type_test.cpp index 7c3de82..41041ce 100644 --- a/libsanitizer/interception/interception_type_test.cpp +++ b/libsanitizer/interception/interception_type_test.cpp @@ -12,28 +12,35 @@ //===----------------------------------------------------------------------===// #include "interception.h" +#include "sanitizer_common/sanitizer_type_traits.h" -#if SANITIZER_LINUX || SANITIZER_APPLE - -#include <sys/types.h> +#if __has_include(<sys/types.h>) +# include <sys/types.h> +#endif #include <stddef.h> #include <stdint.h> -COMPILER_CHECK(sizeof(::SIZE_T) == sizeof(size_t)); -COMPILER_CHECK(sizeof(::SSIZE_T) == sizeof(ssize_t)); -COMPILER_CHECK(sizeof(::PTRDIFF_T) == sizeof(ptrdiff_t)); +COMPILER_CHECK((__sanitizer::is_same<__sanitizer::uptr, ::uintptr_t>::value)); +COMPILER_CHECK((__sanitizer::is_same<__sanitizer::sptr, ::intptr_t>::value)); +COMPILER_CHECK((__sanitizer::is_same<__sanitizer::usize, ::size_t>::value)); +COMPILER_CHECK((__sanitizer::is_same<::PTRDIFF_T, ::ptrdiff_t>::value)); +COMPILER_CHECK((__sanitizer::is_same<::SIZE_T, ::size_t>::value)); +#if !SANITIZER_WINDOWS +// No ssize_t on Windows. +COMPILER_CHECK((__sanitizer::is_same<::SSIZE_T, ::ssize_t>::value)); +#endif +// TODO: These are not actually the same type on Linux (long vs long long) COMPILER_CHECK(sizeof(::INTMAX_T) == sizeof(intmax_t)); +COMPILER_CHECK(sizeof(::UINTMAX_T) == sizeof(uintmax_t)); -# if SANITIZER_GLIBC || SANITIZER_ANDROID +#if SANITIZER_GLIBC || SANITIZER_ANDROID COMPILER_CHECK(sizeof(::OFF64_T) == sizeof(off64_t)); -# endif +#endif // The following are the cases when pread (and friends) is used instead of // pread64. In those cases we need OFF_T to match off_t. We don't care about the // rest (they depend on _FILE_OFFSET_BITS setting when building an application). -# if SANITIZER_ANDROID || !defined _FILE_OFFSET_BITS || \ - _FILE_OFFSET_BITS != 64 +#if !SANITIZER_WINDOWS && (SANITIZER_ANDROID || !defined _FILE_OFFSET_BITS || \ + _FILE_OFFSET_BITS != 64) COMPILER_CHECK(sizeof(::OFF_T) == sizeof(off_t)); -# endif - #endif diff --git a/libsanitizer/interception/interception_win.cpp b/libsanitizer/interception/interception_win.cpp index 1b681ad..077a536 100644 --- a/libsanitizer/interception/interception_win.cpp +++ b/libsanitizer/interception/interception_win.cpp @@ -27,7 +27,7 @@ // // 1) Detour // -// The Detour hooking technique is assuming the presence of an header with +// The Detour hooking technique is assuming the presence of a header with // padding and an overridable 2-bytes nop instruction (mov edi, edi). The // nop instruction can safely be replaced by a 2-bytes jump without any need // to save the instruction. A jump to the target is encoded in the function @@ -47,7 +47,7 @@ // // func: jmp <label> --> func: jmp <hook> // -// On an 64-bit architecture, a trampoline is inserted. +// On a 64-bit architecture, a trampoline is inserted. // // func: jmp <label> --> func: jmp <tramp> // [...] @@ -60,7 +60,7 @@ // // 3) HotPatch // -// The HotPatch hooking is assuming the presence of an header with padding +// The HotPatch hooking is assuming the presence of a header with padding // and a first instruction with at least 2-bytes. // // The reason to enforce the 2-bytes limitation is to provide the minimal @@ -80,7 +80,7 @@ // real: <instr> // jmp <body> // -// On an 64-bit architecture: +// On a 64-bit architecture: // // head: 6 x nop head: jmp QWORD [addr1] // func: <instr> --> func: jmp short <head> @@ -110,7 +110,7 @@ // <instr> // jmp <body> // -// On an 64-bit architecture: +// On a 64-bit architecture: // // func: <instr> --> func: jmp QWORD [addr1] // <instr> @@ -130,6 +130,7 @@ #include "sanitizer_common/sanitizer_platform.h" #define WIN32_LEAN_AND_MEAN #include <windows.h> +#include <psapi.h> namespace __interception { @@ -339,7 +340,7 @@ struct TrampolineMemoryRegion { uptr max_size; }; -UNUSED static const uptr kTrampolineScanLimitRange = 1 << 31; // 2 gig +UNUSED static const uptr kTrampolineScanLimitRange = 1ull << 31; // 2 gig static const int kMaxTrampolineRegion = 1024; static TrampolineMemoryRegion TrampolineRegions[kMaxTrampolineRegion]; @@ -385,7 +386,30 @@ void TestOnlyReleaseTrampolineRegions() { } } -static uptr AllocateMemoryForTrampoline(uptr image_address, size_t size) { +static uptr AllocateMemoryForTrampoline(uptr func_address, size_t size) { + uptr image_address = func_address; + +#if SANITIZER_WINDOWS64 + // Allocate memory after the module (DLL or EXE file), but within 2GB + // of the start of the module so that any address within the module can be + // referenced with PC-relative operands. + // This allows us to not just jump to the trampoline with a PC-relative + // offset, but to relocate any instructions that we copy to the trampoline + // which have references to the original module. If we can't find the base + // address of the module (e.g. if func_address is in mmap'ed memory), just + // use func_address as is. + HMODULE module; + if (::GetModuleHandleExW(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS | + GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT, + (LPCWSTR)func_address, &module)) { + MODULEINFO module_info; + if (::GetModuleInformation(::GetCurrentProcess(), module, + &module_info, sizeof(module_info))) { + image_address = (uptr)module_info.lpBaseOfDll; + } + } +#endif + // Find a region within 2G with enough space to allocate |size| bytes. TrampolineMemoryRegion *region = nullptr; for (size_t bucket = 0; bucket < kMaxTrampolineRegion; ++bucket) { @@ -431,7 +455,8 @@ static uptr AllocateMemoryForTrampoline(uptr image_address, size_t size) { // The following prologues cannot be patched because of the short jump // jumping to the patching region. -#if SANITIZER_WINDOWS64 +// Short jump patterns below are only for x86_64. +# if SANITIZER_WINDOWS_x64 // ntdll!wcslen in Win11 // 488bc1 mov rax,rcx // 0fb710 movzx edx,word ptr [rax] @@ -462,7 +487,7 @@ static size_t GetInstructionSize(uptr address, size_t* rel_offset = nullptr) { return 4; #endif -#if SANITIZER_WINDOWS64 +# if SANITIZER_WINDOWS_x64 if (memcmp((u8*)address, kPrologueWithShortJump1, sizeof(kPrologueWithShortJump1)) == 0 || memcmp((u8*)address, kPrologueWithShortJump2, @@ -478,6 +503,8 @@ static size_t GetInstructionSize(uptr address, size_t* rel_offset = nullptr) { switch (*(u8*)address) { case 0x90: // 90 : nop + case 0xC3: // C3 : ret (for small/empty function interception + case 0xCC: // CC : int 3 i.e. registering weak functions) return 1; case 0x50: // push eax / rax @@ -494,6 +521,11 @@ static size_t GetInstructionSize(uptr address, size_t* rel_offset = nullptr) { case 0x6A: // 6A XX = push XX return 2; + // This instruction can be encoded with a 16-bit immediate but that is + // incredibly unlikely. + case 0x68: // 68 XX XX XX XX : push imm32 + return 5; + case 0xb8: // b8 XX XX XX XX : mov eax, XX XX XX XX case 0xB9: // b9 XX XX XX XX : mov ecx, XX XX XX XX return 5; @@ -501,7 +533,6 @@ static size_t GetInstructionSize(uptr address, size_t* rel_offset = nullptr) { // Cannot overwrite control-instruction. Return 0 to indicate failure. case 0xE9: // E9 XX XX XX XX : jmp <label> case 0xE8: // E8 XX XX XX XX : call <func> - case 0xC3: // C3 : ret case 0xEB: // EB XX : jmp XX (short jump) case 0x70: // 7Y YY : jy XX (short conditional jump) case 0x71: @@ -532,6 +563,9 @@ static size_t GetInstructionSize(uptr address, size_t* rel_offset = nullptr) { case 0xC033: // 33 C0 : xor eax, eax case 0xC933: // 33 C9 : xor ecx, ecx case 0xD233: // 33 D2 : xor edx, edx + case 0xDB84: // 84 DB : test bl,bl + case 0xC984: // 84 C9 : test cl,cl + case 0xD284: // 84 D2 : test dl,dl return 2; // Cannot overwrite control-instruction. Return 0 to indicate failure. @@ -540,15 +574,38 @@ static size_t GetInstructionSize(uptr address, size_t* rel_offset = nullptr) { } switch (0x00FFFFFF & *(u32*)address) { + case 0xF8E483: // 83 E4 F8 : and esp, 0xFFFFFFF8 + case 0x64EC83: // 83 EC 64 : sub esp, 64h + return 3; case 0x24A48D: // 8D A4 24 XX XX XX XX : lea esp, [esp + XX XX XX XX] return 7; } -#if SANITIZER_WINDOWS64 + switch (0x000000FF & *(u32 *)address) { + case 0xc2: // C2 XX XX : ret XX (needed for registering weak functions) + return 3; + } + +# if SANITIZER_WINDOWS_x64 switch (*(u8*)address) { case 0xA1: // A1 XX XX XX XX XX XX XX XX : // movabs eax, dword ptr ds:[XXXXXXXX] return 9; + case 0xF2: + switch (*(u32 *)(address + 1)) { + case 0x2444110f: // f2 0f 11 44 24 XX movsd QWORD PTR + // [rsp + XX], xmm0 + case 0x244c110f: // f2 0f 11 4c 24 XX movsd QWORD PTR + // [rsp + XX], xmm1 + case 0x2454110f: // f2 0f 11 54 24 XX movsd QWORD PTR + // [rsp + XX], xmm2 + case 0x245c110f: // f2 0f 11 5c 24 XX movsd QWORD PTR + // [rsp + XX], xmm3 + case 0x2464110f: // f2 0f 11 64 24 XX movsd QWORD PTR + // [rsp + XX], xmm4 + return 6; + } + break; case 0x83: const u8 next_byte = *(u8*)(address + 1); @@ -577,51 +634,126 @@ static size_t GetInstructionSize(uptr address, size_t* rel_offset = nullptr) { case 0x018a: // mov al, byte ptr [rcx] return 2; + case 0x058A: // 8A 05 XX XX XX XX : mov al, byte ptr [XX XX XX XX] + case 0x7E80: // 80 7E YY XX cmp BYTE PTR [rsi+YY], XX + case 0x7D80: // 80 7D YY XX cmp BYTE PTR [rbp+YY], XX + case 0x7A80: // 80 7A YY XX cmp BYTE PTR [rdx+YY], XX + case 0x7880: // 80 78 YY XX cmp BYTE PTR [rax+YY], XX + case 0x7B80: // 80 7B YY XX cmp BYTE PTR [rbx+YY], XX + case 0x7980: // 80 79 YY XX cmp BYTE ptr [rcx+YY], XX + return 4; + case 0x058B: // 8B 05 XX XX XX XX : mov eax, dword ptr [XX XX XX XX] if (rel_offset) *rel_offset = 2; return 6; + + case 0x7E81: // 81 7E YY XX XX XX XX cmp DWORD PTR [rsi+YY], XX XX XX XX + case 0x7D81: // 81 7D YY XX XX XX XX cmp DWORD PTR [rbp+YY], XX XX XX XX + case 0x7A81: // 81 7A YY XX XX XX XX cmp DWORD PTR [rdx+YY], XX XX XX XX + case 0x7881: // 81 78 YY XX XX XX XX cmp DWORD PTR [rax+YY], XX XX XX XX + case 0x7B81: // 81 7B YY XX XX XX XX cmp DWORD PTR [rbx+YY], XX XX XX XX + case 0x7981: // 81 79 YY XX XX XX XX cmp dword ptr [rcx+YY], XX XX XX XX + return 7; } switch (0x00FFFFFF & *(u32*)address) { - case 0xe58948: // 48 8b c4 : mov rbp, rsp - case 0xc18b48: // 48 8b c1 : mov rax, rcx - case 0xc48b48: // 48 8b c4 : mov rax, rsp - case 0xd9f748: // 48 f7 d9 : neg rcx - case 0xd12b48: // 48 2b d1 : sub rdx, rcx case 0x07c1f6: // f6 c1 07 : test cl, 0x7 - case 0xc98548: // 48 85 C9 : test rcx, rcx - case 0xd28548: // 48 85 d2 : test rdx, rdx + case 0x10b70f: // 0f b7 10 : movzx edx, WORD PTR [rax] + case 0xc00b4d: // 4d 0b c0 : or r8, r8 + case 0xc03345: // 45 33 c0 : xor r8d, r8d + case 0xc08548: // 48 85 c0 : test rax, rax case 0xc0854d: // 4d 85 c0 : test r8, r8 + case 0xc08b41: // 41 8b c0 : mov eax, r8d + case 0xc0ff48: // 48 ff c0 : inc rax + case 0xc0ff49: // 49 ff c0 : inc r8 + case 0xc18b41: // 41 8b c1 : mov eax, r9d + case 0xc18b48: // 48 8b c1 : mov rax, rcx + case 0xc18b4c: // 4c 8b c1 : mov r8, rcx + case 0xc1ff48: // 48 ff c1 : inc rcx + case 0xc1ff49: // 49 ff c1 : inc r9 + case 0xc28b41: // 41 8b c2 : mov eax, r10d case 0xc2b60f: // 0f b6 c2 : movzx eax, dl - case 0xc03345: // 45 33 c0 : xor r8d, r8d + case 0xc2ff48: // 48 ff c2 : inc rdx + case 0xc2ff49: // 49 ff c2 : inc r10 + case 0xc38b41: // 41 8b c3 : mov eax, r11d + case 0xc3ff48: // 48 ff c3 : inc rbx + case 0xc3ff49: // 49 ff c3 : inc r11 + case 0xc48b41: // 41 8b c4 : mov eax, r12d + case 0xc48b48: // 48 8b c4 : mov rax, rsp + case 0xc4ff49: // 49 ff c4 : inc r12 + case 0xc5ff49: // 49 ff c5 : inc r13 + case 0xc6ff48: // 48 ff c6 : inc rsi + case 0xc6ff49: // 49 ff c6 : inc r14 + case 0xc7ff48: // 48 ff c7 : inc rdi + case 0xc7ff49: // 49 ff c7 : inc r15 case 0xc93345: // 45 33 c9 : xor r9d, r9d - case 0xdb3345: // 45 33 DB : xor r11d, r11d - case 0xd98b4c: // 4c 8b d9 : mov r11, rcx - case 0xd28b4c: // 4c 8b d2 : mov r10, rdx - case 0xc98b4c: // 4C 8B C9 : mov r9, rcx - case 0xc18b4c: // 4C 8B C1 : mov r8, rcx - case 0xd2b60f: // 0f b6 d2 : movzx edx, dl + case 0xc98548: // 48 85 c9 : test rcx, rcx + case 0xc9854d: // 4d 85 c9 : test r9, r9 + case 0xc98b4c: // 4c 8b c9 : mov r9, rcx case 0xca2b48: // 48 2b ca : sub rcx, rdx - case 0x10b70f: // 0f b7 10 : movzx edx, WORD PTR [rax] - case 0xc00b4d: // 3d 0b c0 : or r8, r8 - case 0xc08b41: // 41 8b c0 : mov eax, r8d + case 0xca3b48: // 48 3b ca : cmp rcx, rdx + case 0xd12b48: // 48 2b d1 : sub rdx, rcx case 0xd18b48: // 48 8b d1 : mov rdx, rcx - case 0xdc8b4c: // 4c 8b dc : mov r11, rsp case 0xd18b4c: // 4c 8b d1 : mov r10, rcx - case 0xE0E483: // 83 E4 E0 : and esp, 0xFFFFFFE0 + case 0xd28548: // 48 85 d2 : test rdx, rdx + case 0xd2854d: // 4d 85 d2 : test r10, r10 + case 0xd28b4c: // 4c 8b d2 : mov r10, rdx + case 0xd2b60f: // 0f b6 d2 : movzx edx, dl + case 0xd98b4c: // 4c 8b d9 : mov r11, rcx + case 0xd9f748: // 48 f7 d9 : neg rcx + case 0xdb3345: // 45 33 db : xor r11d, r11d + case 0xdb8548: // 48 85 db : test rbx, rbx + case 0xdb854d: // 4d 85 db : test r11, r11 + case 0xdc8b4c: // 4c 8b dc : mov r11, rsp + case 0xe0e483: // 83 e4 e0 : and esp, 0xFFFFFFE0 + case 0xe48548: // 48 85 e4 : test rsp, rsp + case 0xe4854d: // 4d 85 e4 : test r12, r12 + case 0xe58948: // 48 89 e5 : mov rbp, rsp + case 0xed8548: // 48 85 ed : test rbp, rbp + case 0xed854d: // 4d 85 ed : test r13, r13 + case 0xf6854d: // 4d 85 f6 : test r14, r14 + case 0xff854d: // 4d 85 ff : test r15, r15 return 3; + case 0x245489: // 89 54 24 XX : mov DWORD PTR[rsp + XX], edx + case 0x428d44: // 44 8d 42 XX : lea r8d , [rdx + XX] + case 0x588948: // 48 89 58 XX : mov QWORD PTR[rax + XX], rbx case 0xec8348: // 48 83 ec XX : sub rsp, XX case 0xf88349: // 49 83 f8 XX : cmp r8, XX - case 0x588948: // 48 89 58 XX : mov QWORD PTR[rax + XX], rbx return 4; + case 0x246483: // 83 64 24 XX YY : and DWORD PTR [rsp+XX], YY + return 5; + + case 0x788166: // 66 81 78 XX YY YY cmp WORD PTR [rax+XX], YY YY + case 0x798166: // 66 81 79 XX YY YY cmp WORD PTR [rcx+XX], YY YY + case 0x7a8166: // 66 81 7a XX YY YY cmp WORD PTR [rdx+XX], YY YY + case 0x7b8166: // 66 81 7b XX YY YY cmp WORD PTR [rbx+XX], YY YY + case 0x7e8166: // 66 81 7e XX YY YY cmp WORD PTR [rsi+XX], YY YY + case 0x7f8166: // 66 81 7f XX YY YY cmp WORD PTR [rdi+XX], YY YY + return 6; + case 0xec8148: // 48 81 EC XX XX XX XX : sub rsp, XXXXXXXX return 7; + // clang-format off + case 0x788141: // 41 81 78 XX YY YY YY YY : cmp DWORD PTR [r8+YY], XX XX XX XX + case 0x798141: // 41 81 79 XX YY YY YY YY : cmp DWORD PTR [r9+YY], XX XX XX XX + case 0x7a8141: // 41 81 7a XX YY YY YY YY : cmp DWORD PTR [r10+YY], XX XX XX XX + case 0x7b8141: // 41 81 7b XX YY YY YY YY : cmp DWORD PTR [r11+YY], XX XX XX XX + case 0x7c8141: // 41 81 7c XX YY YY YY YY : cmp DWORD PTR [r12+YY], XX XX XX XX + case 0x7d8141: // 41 81 7d XX YY YY YY YY : cmp DWORD PTR [r13+YY], XX XX XX XX + case 0x7e8141: // 41 81 7e XX YY YY YY YY : cmp DWORD PTR [r14+YY], XX XX XX XX + case 0x7f8141: // 41 81 7f YY XX XX XX XX : cmp DWORD PTR [r15+YY], XX XX XX XX + case 0x247c81: // 81 7c 24 YY XX XX XX XX : cmp DWORD PTR [rsp+YY], XX XX XX XX + return 8; + // clang-format on + case 0x058b48: // 48 8b 05 XX XX XX XX : // mov rax, QWORD PTR [rip + XXXXXXXX] + case 0x058d48: // 48 8d 05 XX XX XX XX : + // lea rax, QWORD PTR [rip + XXXXXXXX] case 0x25ff48: // 48 ff 25 XX XX XX XX : // rex.W jmp QWORD PTR [rip + XXXXXXXX] case 0x158D4C: // 4c 8d 15 XX XX XX XX : lea r10, [rip + XX] @@ -636,6 +768,8 @@ static size_t GetInstructionSize(uptr address, size_t* rel_offset = nullptr) { } switch (*(u32*)(address)) { + case 0x1ab60f44: // 44 0f b6 1a : movzx r11d, BYTE PTR [rdx] + return 4; case 0x24448b48: // 48 8b 44 24 XX : mov rax, QWORD ptr [rsp + XX] case 0x246c8948: // 48 89 6C 24 XX : mov QWORD ptr [rsp + XX], rbp case 0x245c8948: // 48 89 5c 24 XX : mov QWORD PTR [rsp + XX], rbx @@ -645,8 +779,11 @@ static size_t GetInstructionSize(uptr address, size_t* rel_offset = nullptr) { case 0x24548948: // 48 89 54 24 XX : mov QWORD PTR [rsp + XX], rdx case 0x244c894c: // 4c 89 4c 24 XX : mov QWORD PTR [rsp + XX], r9 case 0x2444894c: // 4c 89 44 24 XX : mov QWORD PTR [rsp + XX], r8 + case 0x244c8944: // 44 89 4c 24 XX mov DWORD PTR [rsp + XX], r9d + case 0x24448944: // 44 89 44 24 XX mov DWORD PTR [rsp + XX], r8d + case 0x246c8d48: // 48 8d 6c 24 XX : lea rbp, [rsp + XX] return 5; - case 0x24648348: // 48 83 64 24 XX : and QWORD PTR [rsp + XX], YY + case 0x24648348: // 48 83 64 24 XX YY : and QWORD PTR [rsp + XX], YY return 6; } @@ -660,6 +797,7 @@ static size_t GetInstructionSize(uptr address, size_t* rel_offset = nullptr) { case 0x458B: // 8B 45 XX : mov eax, dword ptr [ebp + XX] case 0x5D8B: // 8B 5D XX : mov ebx, dword ptr [ebp + XX] case 0x7D8B: // 8B 7D XX : mov edi, dword ptr [ebp + XX] + case 0x758B: // 8B 75 XX : mov esi, dword ptr [ebp + XX] case 0xEC83: // 83 EC XX : sub esp, XX case 0x75FF: // FF 75 XX : push dword ptr [ebp + XX] return 3; @@ -943,19 +1081,26 @@ bool OverrideFunction( static void **InterestingDLLsAvailable() { static const char *InterestingDLLs[] = { - "kernel32.dll", - "msvcr100.dll", // VS2010 - "msvcr110.dll", // VS2012 - "msvcr120.dll", // VS2013 - "vcruntime140.dll", // VS2015 - "ucrtbase.dll", // Universal CRT -#if (defined(__MINGW32__) && defined(__i386__)) - "libc++.dll", // libc++ - "libunwind.dll", // libunwind -#endif - // NTDLL should go last as it exports some functions that we should - // override in the CRT [presumably only used internally]. - "ntdll.dll", NULL}; + "kernel32.dll", + "msvcr100d.dll", // VS2010 + "msvcr110d.dll", // VS2012 + "msvcr120d.dll", // VS2013 + "vcruntime140d.dll", // VS2015 + "ucrtbased.dll", // Universal CRT + "msvcr100.dll", // VS2010 + "msvcr110.dll", // VS2012 + "msvcr120.dll", // VS2013 + "vcruntime140.dll", // VS2015 + "ucrtbase.dll", // Universal CRT +# if (defined(__MINGW32__) && defined(__i386__)) + "libc++.dll", // libc++ + "libunwind.dll", // libunwind +# endif + // NTDLL should go last as it exports some functions that we should + // override in the CRT [presumably only used internally]. + "ntdll.dll", + NULL + }; static void *result[ARRAY_SIZE(InterestingDLLs)] = { 0 }; if (!result[0]) { for (size_t i = 0, j = 0; InterestingDLLs[i]; ++i) { @@ -1126,4 +1271,4 @@ bool OverrideImportedFunction(const char *module_to_patch, } // namespace __interception -#endif // SANITIZER_APPLE +#endif // SANITIZER_WINDOWS diff --git a/libsanitizer/lsan/lsan.cpp b/libsanitizer/lsan/lsan.cpp index 6b22360..798294b 100644 --- a/libsanitizer/lsan/lsan.cpp +++ b/libsanitizer/lsan/lsan.cpp @@ -92,15 +92,16 @@ extern "C" void __lsan_init() { CacheBinaryName(); AvoidCVE_2016_2143(); InitializeFlags(); + InitializePlatformEarly(); InitCommonLsan(); InitializeAllocator(); ReplaceSystemMalloc(); - InitTlsSize(); InitializeInterceptors(); InitializeThreads(); InstallDeadlySignalHandlers(LsanOnDeadlySignal); InitializeMainThread(); InstallAtExitCheckLeaks(); + InstallAtForkHandler(); InitializeCoverage(common_flags()->coverage, common_flags()->coverage_dir); diff --git a/libsanitizer/lsan/lsan.h b/libsanitizer/lsan/lsan.h index 757edec..0074ad530 100644 --- a/libsanitizer/lsan/lsan.h +++ b/libsanitizer/lsan/lsan.h @@ -40,6 +40,7 @@ void InitializeInterceptors(); void ReplaceSystemMalloc(); void LsanOnDeadlySignal(int signo, void *siginfo, void *context); void InstallAtExitCheckLeaks(); +void InstallAtForkHandler(); #define ENSURE_LSAN_INITED \ do { \ diff --git a/libsanitizer/lsan/lsan_allocator.cpp b/libsanitizer/lsan/lsan_allocator.cpp index 12d579a..493bf5f 100644 --- a/libsanitizer/lsan/lsan_allocator.cpp +++ b/libsanitizer/lsan/lsan_allocator.cpp @@ -31,7 +31,7 @@ static const uptr kMaxAllowedMallocSize = 1ULL << 30; #elif defined(__mips64) || defined(__aarch64__) static const uptr kMaxAllowedMallocSize = 4ULL << 30; #else -static const uptr kMaxAllowedMallocSize = 8ULL << 30; +static const uptr kMaxAllowedMallocSize = 1ULL << 40; #endif static Allocator allocator; diff --git a/libsanitizer/lsan/lsan_common.cpp b/libsanitizer/lsan/lsan_common.cpp index 8b1af5b..5c44c00 100644 --- a/libsanitizer/lsan/lsan_common.cpp +++ b/libsanitizer/lsan/lsan_common.cpp @@ -42,6 +42,9 @@ namespace __lsan { // also to protect the global list of root regions. static Mutex global_mutex; +void LockGlobal() SANITIZER_ACQUIRE(global_mutex) { global_mutex.Lock(); } +void UnlockGlobal() SANITIZER_RELEASE(global_mutex) { global_mutex.Unlock(); } + Flags lsan_flags; void DisableCounterUnderflow() { @@ -105,7 +108,7 @@ class LeakSuppressionContext { void PrintMatchedSuppressions(); }; -ALIGNED(64) static char suppression_placeholder[sizeof(LeakSuppressionContext)]; +alignas(64) static char suppression_placeholder[sizeof(LeakSuppressionContext)]; static LeakSuppressionContext *suppression_ctx = nullptr; static const char kSuppressionLeak[] = "leak"; static const char *kSuppressionTypes[] = {kSuppressionLeak}; @@ -152,14 +155,15 @@ Suppression *LeakSuppressionContext::GetSuppressionForAddr(uptr addr) { return s; // Suppress by file or function name. - SymbolizedStack *frames = Symbolizer::GetOrInit()->SymbolizePC(addr); - for (SymbolizedStack *cur = frames; cur; cur = cur->next) { + SymbolizedStackHolder symbolized_stack( + Symbolizer::GetOrInit()->SymbolizePC(addr)); + const SymbolizedStack *frames = symbolized_stack.get(); + for (const SymbolizedStack *cur = frames; cur; cur = cur->next) { if (context.Match(cur->info.function, kSuppressionLeak, &s) || context.Match(cur->info.file, kSuppressionLeak, &s)) { break; } } - frames->ClearAll(); return s; } @@ -284,23 +288,54 @@ static inline bool MaybeUserPointer(uptr p) { # endif } +namespace { +struct DirectMemoryAccessor { + void Init(uptr begin, uptr end) {}; + void *LoadPtr(uptr p) const { return *reinterpret_cast<void **>(p); } +}; + +struct CopyMemoryAccessor { + void Init(uptr begin, uptr end) { + this->begin = begin; + buffer.clear(); + buffer.resize(end - begin); + MemCpyAccessible(buffer.data(), reinterpret_cast<void *>(begin), + buffer.size()); + }; + + void *LoadPtr(uptr p) const { + uptr offset = p - begin; + CHECK_LE(offset + sizeof(void *), reinterpret_cast<uptr>(buffer.size())); + return *reinterpret_cast<void **>(offset + + reinterpret_cast<uptr>(buffer.data())); + } + + private: + uptr begin; + InternalMmapVector<char> buffer; +}; +} // namespace + // Scans the memory range, looking for byte patterns that point into allocator // chunks. Marks those chunks with |tag| and adds them to |frontier|. // There are two usage modes for this function: finding reachable chunks // (|tag| = kReachable) and finding indirectly leaked chunks // (|tag| = kIndirectlyLeaked). In the second case, there's no flood fill, // so |frontier| = 0. -void ScanRangeForPointers(uptr begin, uptr end, Frontier *frontier, - const char *region_type, ChunkTag tag) { +template <class Accessor> +void ScanForPointers(uptr begin, uptr end, Frontier *frontier, + const char *region_type, ChunkTag tag, + Accessor &accessor) { CHECK(tag == kReachable || tag == kIndirectlyLeaked); const uptr alignment = flags()->pointer_alignment(); LOG_POINTERS("Scanning %s range %p-%p.\n", region_type, (void *)begin, (void *)end); + accessor.Init(begin, end); uptr pp = begin; if (pp % alignment) pp = pp + alignment - pp % alignment; for (; pp + sizeof(void *) <= end; pp += alignment) { - void *p = *reinterpret_cast<void **>(pp); + void *p = accessor.LoadPtr(pp); # if SANITIZER_APPLE p = TransformPointer(p); # endif @@ -335,6 +370,12 @@ void ScanRangeForPointers(uptr begin, uptr end, Frontier *frontier, } } +void ScanRangeForPointers(uptr begin, uptr end, Frontier *frontier, + const char *region_type, ChunkTag tag) { + DirectMemoryAccessor accessor; + ScanForPointers(begin, end, frontier, region_type, tag, accessor); +} + // Scans a global range for pointers void ScanGlobalRange(uptr begin, uptr end, Frontier *frontier) { uptr allocator_begin = 0, allocator_end = 0; @@ -352,14 +393,21 @@ void ScanGlobalRange(uptr begin, uptr end, Frontier *frontier) { } } -void ScanExtraStackRanges(const InternalMmapVector<Range> &ranges, - Frontier *frontier) { +template <class Accessor> +void ScanRanges(const InternalMmapVector<Range> &ranges, Frontier *frontier, + const char *region_type, Accessor &accessor) { for (uptr i = 0; i < ranges.size(); i++) { - ScanRangeForPointers(ranges[i].begin, ranges[i].end, frontier, "FAKE STACK", - kReachable); + ScanForPointers(ranges[i].begin, ranges[i].end, frontier, region_type, + kReachable, accessor); } } +void ScanExtraStackRanges(const InternalMmapVector<Range> &ranges, + Frontier *frontier) { + DirectMemoryAccessor accessor; + ScanRanges(ranges, frontier, "FAKE STACK", accessor); +} + # if SANITIZER_FUCHSIA // Fuchsia handles all threads together with its own callback. @@ -395,26 +443,129 @@ static void ProcessThreadRegistry(Frontier *frontier) { } // Scans thread data (stacks and TLS) for heap pointers. +template <class Accessor> +static void ProcessThread(tid_t os_id, uptr sp, + const InternalMmapVector<uptr> ®isters, + InternalMmapVector<Range> &extra_ranges, + Frontier *frontier, Accessor &accessor) { + // `extra_ranges` is outside of the function and the loop to reused mapped + // memory. + CHECK(extra_ranges.empty()); + LOG_THREADS("Processing thread %llu.\n", os_id); + uptr stack_begin, stack_end, tls_begin, tls_end, cache_begin, cache_end; + DTLS *dtls; + bool thread_found = + GetThreadRangesLocked(os_id, &stack_begin, &stack_end, &tls_begin, + &tls_end, &cache_begin, &cache_end, &dtls); + if (!thread_found) { + // If a thread can't be found in the thread registry, it's probably in the + // process of destruction. Log this event and move on. + LOG_THREADS("Thread %llu not found in registry.\n", os_id); + return; + } + + if (!sp) + sp = stack_begin; + + if (flags()->use_registers) { + uptr registers_begin = reinterpret_cast<uptr>(registers.data()); + uptr registers_end = + reinterpret_cast<uptr>(registers.data() + registers.size()); + ScanForPointers(registers_begin, registers_end, frontier, "REGISTERS", + kReachable, accessor); + } + + if (flags()->use_stacks) { + LOG_THREADS("Stack at %p-%p (SP = %p).\n", (void *)stack_begin, + (void *)stack_end, (void *)sp); + if (sp < stack_begin || sp >= stack_end) { + // SP is outside the recorded stack range (e.g. the thread is running a + // signal handler on alternate stack, or swapcontext was used). + // Again, consider the entire stack range to be reachable. + LOG_THREADS("WARNING: stack pointer not in stack range.\n"); + uptr page_size = GetPageSizeCached(); + int skipped = 0; + while (stack_begin < stack_end && + !IsAccessibleMemoryRange(stack_begin, 1)) { + skipped++; + stack_begin += page_size; + } + LOG_THREADS("Skipped %d guard page(s) to obtain stack %p-%p.\n", skipped, + (void *)stack_begin, (void *)stack_end); + } else { + // Shrink the stack range to ignore out-of-scope values. + stack_begin = sp; + } + ScanForPointers(stack_begin, stack_end, frontier, "STACK", kReachable, + accessor); + GetThreadExtraStackRangesLocked(os_id, &extra_ranges); + ScanRanges(extra_ranges, frontier, "FAKE STACK", accessor); + } + + if (flags()->use_tls) { + if (tls_begin) { + LOG_THREADS("TLS at %p-%p.\n", (void *)tls_begin, (void *)tls_end); + // If the tls and cache ranges don't overlap, scan full tls range, + // otherwise, only scan the non-overlapping portions + if (cache_begin == cache_end || tls_end < cache_begin || + tls_begin > cache_end) { + ScanForPointers(tls_begin, tls_end, frontier, "TLS", kReachable, + accessor); + } else { + if (tls_begin < cache_begin) + ScanForPointers(tls_begin, cache_begin, frontier, "TLS", kReachable, + accessor); + if (tls_end > cache_end) + ScanForPointers(cache_end, tls_end, frontier, "TLS", kReachable, + accessor); + } + } +# if SANITIZER_ANDROID + extra_ranges.clear(); + auto *cb = +[](void *dtls_begin, void *dtls_end, uptr /*dso_idd*/, + void *arg) -> void { + reinterpret_cast<InternalMmapVector<Range> *>(arg)->push_back( + {reinterpret_cast<uptr>(dtls_begin), + reinterpret_cast<uptr>(dtls_end)}); + }; + ScanRanges(extra_ranges, frontier, "DTLS", accessor); + // FIXME: There might be a race-condition here (and in Bionic) if the + // thread is suspended in the middle of updating its DTLS. IOWs, we + // could scan already freed memory. (probably fine for now) + __libc_iterate_dynamic_tls(os_id, cb, frontier); +# else + if (dtls && !DTLSInDestruction(dtls)) { + ForEachDVT(dtls, [&](const DTLS::DTV &dtv, int id) { + uptr dtls_beg = dtv.beg; + uptr dtls_end = dtls_beg + dtv.size; + if (dtls_beg < dtls_end) { + LOG_THREADS("DTLS %d at %p-%p.\n", id, (void *)dtls_beg, + (void *)dtls_end); + ScanForPointers(dtls_beg, dtls_end, frontier, "DTLS", kReachable, + accessor); + } + }); + } else { + // We are handling a thread with DTLS under destruction. Log about + // this and continue. + LOG_THREADS("Thread %llu has DTLS under destruction.\n", os_id); + } +# endif + } +} + static void ProcessThreads(SuspendedThreadsList const &suspended_threads, Frontier *frontier, tid_t caller_tid, uptr caller_sp) { + InternalMmapVector<tid_t> done_threads; InternalMmapVector<uptr> registers; InternalMmapVector<Range> extra_ranges; for (uptr i = 0; i < suspended_threads.ThreadCount(); i++) { - tid_t os_id = static_cast<tid_t>(suspended_threads.GetThreadID(i)); - LOG_THREADS("Processing thread %llu.\n", os_id); - uptr stack_begin, stack_end, tls_begin, tls_end, cache_begin, cache_end; - DTLS *dtls; - bool thread_found = - GetThreadRangesLocked(os_id, &stack_begin, &stack_end, &tls_begin, - &tls_end, &cache_begin, &cache_end, &dtls); - if (!thread_found) { - // If a thread can't be found in the thread registry, it's probably in the - // process of destruction. Log this event and move on. - LOG_THREADS("Thread %llu not found in registry.\n", os_id); - continue; - } - uptr sp; + registers.clear(); + extra_ranges.clear(); + + const tid_t os_id = suspended_threads.GetThreadID(i); + uptr sp = 0; PtraceRegistersStatus have_registers = suspended_threads.GetRegistersAndSP(i, ®isters, &sp); if (have_registers != REGISTERS_AVAILABLE) { @@ -423,96 +574,32 @@ static void ProcessThreads(SuspendedThreadsList const &suspended_threads, // GetRegistersAndSP failed with ESRCH. if (have_registers == REGISTERS_UNAVAILABLE_FATAL) continue; - sp = stack_begin; + sp = 0; } - if (suspended_threads.GetThreadID(i) == caller_tid) { + + if (os_id == caller_tid) sp = caller_sp; - } - if (flags()->use_registers && have_registers) { - uptr registers_begin = reinterpret_cast<uptr>(registers.data()); - uptr registers_end = - reinterpret_cast<uptr>(registers.data() + registers.size()); - ScanRangeForPointers(registers_begin, registers_end, frontier, - "REGISTERS", kReachable); - } + DirectMemoryAccessor accessor; + ProcessThread(os_id, sp, registers, extra_ranges, frontier, accessor); + if (flags()->use_detached) + done_threads.push_back(os_id); + } - if (flags()->use_stacks) { - LOG_THREADS("Stack at %p-%p (SP = %p).\n", (void *)stack_begin, - (void *)stack_end, (void *)sp); - if (sp < stack_begin || sp >= stack_end) { - // SP is outside the recorded stack range (e.g. the thread is running a - // signal handler on alternate stack, or swapcontext was used). - // Again, consider the entire stack range to be reachable. - LOG_THREADS("WARNING: stack pointer not in stack range.\n"); - uptr page_size = GetPageSizeCached(); - int skipped = 0; - while (stack_begin < stack_end && - !IsAccessibleMemoryRange(stack_begin, 1)) { - skipped++; - stack_begin += page_size; - } - LOG_THREADS("Skipped %d guard page(s) to obtain stack %p-%p.\n", - skipped, (void *)stack_begin, (void *)stack_end); - } else { - // Shrink the stack range to ignore out-of-scope values. - stack_begin = sp; - } - ScanRangeForPointers(stack_begin, stack_end, frontier, "STACK", - kReachable); + if (flags()->use_detached) { + CopyMemoryAccessor accessor; + InternalMmapVector<tid_t> known_threads; + GetRunningThreadsLocked(&known_threads); + Sort(done_threads.data(), done_threads.size()); + for (tid_t os_id : known_threads) { + registers.clear(); extra_ranges.clear(); - GetThreadExtraStackRangesLocked(os_id, &extra_ranges); - ScanExtraStackRanges(extra_ranges, frontier); - } - if (flags()->use_tls) { - if (tls_begin) { - LOG_THREADS("TLS at %p-%p.\n", (void *)tls_begin, (void *)tls_end); - // If the tls and cache ranges don't overlap, scan full tls range, - // otherwise, only scan the non-overlapping portions - if (cache_begin == cache_end || tls_end < cache_begin || - tls_begin > cache_end) { - ScanRangeForPointers(tls_begin, tls_end, frontier, "TLS", kReachable); - } else { - if (tls_begin < cache_begin) - ScanRangeForPointers(tls_begin, cache_begin, frontier, "TLS", - kReachable); - if (tls_end > cache_end) - ScanRangeForPointers(cache_end, tls_end, frontier, "TLS", - kReachable); - } - } -# if SANITIZER_ANDROID - auto *cb = +[](void *dtls_begin, void *dtls_end, uptr /*dso_idd*/, - void *arg) -> void { - ScanRangeForPointers(reinterpret_cast<uptr>(dtls_begin), - reinterpret_cast<uptr>(dtls_end), - reinterpret_cast<Frontier *>(arg), "DTLS", - kReachable); - }; - - // FIXME: There might be a race-condition here (and in Bionic) if the - // thread is suspended in the middle of updating its DTLS. IOWs, we - // could scan already freed memory. (probably fine for now) - __libc_iterate_dynamic_tls(os_id, cb, frontier); -# else - if (dtls && !DTLSInDestruction(dtls)) { - ForEachDVT(dtls, [&](const DTLS::DTV &dtv, int id) { - uptr dtls_beg = dtv.beg; - uptr dtls_end = dtls_beg + dtv.size; - if (dtls_beg < dtls_end) { - LOG_THREADS("DTLS %d at %p-%p.\n", id, (void *)dtls_beg, - (void *)dtls_end); - ScanRangeForPointers(dtls_beg, dtls_end, frontier, "DTLS", - kReachable); - } - }); - } else { - // We are handling a thread with DTLS under destruction. Log about - // this and continue. - LOG_THREADS("Thread %llu has DTLS under destruction.\n", os_id); + uptr i = InternalLowerBound(done_threads, os_id); + if (i >= done_threads.size() || done_threads[i] != os_id) { + uptr sp = (os_id == caller_tid) ? caller_sp : 0; + ProcessThread(os_id, sp, registers, extra_ranges, frontier, accessor); } -# endif } } @@ -694,11 +781,13 @@ void LeakSuppressionContext::PrintMatchedSuppressions() { // Fuchsia provides a libc interface that guarantees all threads are // covered, and SuspendedThreadList is never really used. -static void ReportUnsuspendedThreads(const SuspendedThreadsList &) {} +static bool ReportUnsuspendedThreads(const SuspendedThreadsList &) { + return true; +} # else // !SANITIZER_FUCHSIA -static void ReportUnsuspendedThreads( +static bool ReportUnsuspendedThreads( const SuspendedThreadsList &suspended_threads) { InternalMmapVector<tid_t> threads(suspended_threads.ThreadCount()); for (uptr i = 0; i < suspended_threads.ThreadCount(); ++i) @@ -706,16 +795,20 @@ static void ReportUnsuspendedThreads( Sort(threads.data(), threads.size()); - InternalMmapVector<tid_t> unsuspended; - GetRunningThreadsLocked(&unsuspended); + InternalMmapVector<tid_t> known_threads; + GetRunningThreadsLocked(&known_threads); - for (auto os_id : unsuspended) { + bool succeded = true; + for (auto os_id : known_threads) { uptr i = InternalLowerBound(threads, os_id); - if (i >= threads.size() || threads[i] != os_id) + if (i >= threads.size() || threads[i] != os_id) { + succeded = false; Report( "Running thread %zu was not suspended. False leaks are possible.\n", os_id); + } } + return succeded; } # endif // !SANITIZER_FUCHSIA @@ -725,7 +818,18 @@ static void CheckForLeaksCallback(const SuspendedThreadsList &suspended_threads, CheckForLeaksParam *param = reinterpret_cast<CheckForLeaksParam *>(arg); CHECK(param); CHECK(!param->success); - ReportUnsuspendedThreads(suspended_threads); + if (!ReportUnsuspendedThreads(suspended_threads)) { + switch (flags()->thread_suspend_fail) { + case 0: + param->success = true; + return; + case 1: + break; + case 2: + // Will crash on return. + return; + } + } ClassifyAllChunks(suspended_threads, ¶m->frontier, param->caller_tid, param->caller_sp); ForEachChunk(CollectLeaksCb, ¶m->leaks); @@ -750,19 +854,20 @@ static bool PrintResults(LeakReport &report) { } if (common_flags()->print_suppressions) GetSuppressionContext()->PrintMatchedSuppressions(); - if (unsuppressed_count > 0) { + if (unsuppressed_count) report.PrintSummary(); - return true; - } - return false; + if ((unsuppressed_count && common_flags()->verbosity >= 2) || + flags()->log_threads) + PrintThreads(); + return unsuppressed_count; } -static bool CheckForLeaks() { +static bool CheckForLeaksOnce() { if (&__lsan_is_turned_off && __lsan_is_turned_off()) { - VReport(1, "LeakSanitizer is disabled"); + VReport(1, "LeakSanitizer is disabled\n"); return false; } - VReport(1, "LeakSanitizer: checking for leaks"); + VReport(1, "LeakSanitizer: checking for leaks\n"); // Inside LockStuffAndStopTheWorld we can't run symbolizer, so we can't match // suppressions. However if a stack id was previously suppressed, it should be // suppressed in future checks as well. @@ -809,6 +914,12 @@ static bool CheckForLeaks() { } } +static bool CheckForLeaks() { + int leaking_tries = 0; + for (int i = 0; i < flags()->tries; ++i) leaking_tries += CheckForLeaksOnce(); + return leaking_tries == flags()->tries; +} + static bool has_reported_leaks = false; bool HasReportedLeaks() { return has_reported_leaks; } diff --git a/libsanitizer/lsan/lsan_common.h b/libsanitizer/lsan/lsan_common.h index d3e7683..f990c78 100644 --- a/libsanitizer/lsan/lsan_common.h +++ b/libsanitizer/lsan/lsan_common.h @@ -111,6 +111,7 @@ void GetThreadExtraStackRangesLocked(tid_t os_id, InternalMmapVector<Range> *ranges); void GetAdditionalThreadContextPtrsLocked(InternalMmapVector<uptr> *ptrs); void GetRunningThreadsLocked(InternalMmapVector<tid_t> *threads); +void PrintThreads(); //// -------------------------------------------------------------------------- //// Allocator prototypes. @@ -120,6 +121,10 @@ void GetRunningThreadsLocked(InternalMmapVector<tid_t> *threads); void LockAllocator(); void UnlockAllocator(); +// Lock/unlock global mutext. +void LockGlobal(); +void UnlockGlobal(); + // Returns the address range occupied by the global allocator object. void GetAllocatorGlobalRange(uptr *begin, uptr *end); // If p points into a chunk that has been allocated to the user, returns its diff --git a/libsanitizer/lsan/lsan_common_linux.cpp b/libsanitizer/lsan/lsan_common_linux.cpp index 692ad35..7a0b2f0 100644 --- a/libsanitizer/lsan/lsan_common_linux.cpp +++ b/libsanitizer/lsan/lsan_common_linux.cpp @@ -28,7 +28,7 @@ namespace __lsan { static const char kLinkerName[] = "ld"; -static char linker_placeholder[sizeof(LoadedModule)] ALIGNED(64); +alignas(64) static char linker_placeholder[sizeof(LoadedModule)]; static LoadedModule *linker = nullptr; static bool IsLinker(const LoadedModule& module) { diff --git a/libsanitizer/lsan/lsan_flags.inc b/libsanitizer/lsan/lsan_flags.inc index 9350f4b..e0b4aa4 100644 --- a/libsanitizer/lsan/lsan_flags.inc +++ b/libsanitizer/lsan/lsan_flags.inc @@ -41,6 +41,13 @@ LSAN_FLAG(bool, use_ld_allocations, true, LSAN_FLAG(bool, use_unaligned, false, "Consider unaligned pointers valid.") LSAN_FLAG(bool, use_poisoned, false, "Consider pointers found in poisoned memory to be valid.") +LSAN_FLAG(bool, use_detached, false, + "Scan threads even if attaching to them failed.") LSAN_FLAG(bool, log_pointers, false, "Debug logging") LSAN_FLAG(bool, log_threads, false, "Debug logging") +LSAN_FLAG(int, tries, 1, "Debug option to repeat leak checking multiple times") LSAN_FLAG(const char *, suppressions, "", "Suppressions file name.") +LSAN_FLAG(int, thread_suspend_fail, 1, + "Behaviour if thread suspendion all thread (0 - " + "abandon leak checking, 1 - continue with leak checking (reported " + "leaks can be false), 2 - crash (for debugging LSAN)).")
\ No newline at end of file diff --git a/libsanitizer/lsan/lsan_fuchsia.cpp b/libsanitizer/lsan/lsan_fuchsia.cpp index 4edac97..ba59bc9 100644 --- a/libsanitizer/lsan/lsan_fuchsia.cpp +++ b/libsanitizer/lsan/lsan_fuchsia.cpp @@ -80,6 +80,7 @@ void GetAllThreadAllocatorCachesLocked(InternalMmapVector<uptr> *caches) { // On Fuchsia, leak detection is done by a special hook after atexit hooks. // So this doesn't install any atexit hook like on other platforms. void InstallAtExitCheckLeaks() {} +void InstallAtForkHandler() {} // ASan defines this to check its `halt_on_error` flag. bool UseExitcodeOnLeak() { return true; } diff --git a/libsanitizer/lsan/lsan_interceptors.cpp b/libsanitizer/lsan/lsan_interceptors.cpp index 885f7ad..a8252cd 100644 --- a/libsanitizer/lsan/lsan_interceptors.cpp +++ b/libsanitizer/lsan/lsan_interceptors.cpp @@ -26,7 +26,6 @@ #if SANITIZER_POSIX #include "sanitizer_common/sanitizer_posix.h" #endif -#include "sanitizer_common/sanitizer_tls_get_addr.h" #include "lsan.h" #include "lsan_allocator.h" #include "lsan_common.h" @@ -77,6 +76,8 @@ INTERCEPTOR(void*, malloc, uptr size) { } INTERCEPTOR(void, free, void *p) { + if (UNLIKELY(!p)) + return; if (DlsymAlloc::PointerIsMine(p)) return DlsymAlloc::Free(p); ENSURE_LSAN_INITED; @@ -133,9 +134,7 @@ INTERCEPTOR(void*, memalign, uptr alignment, uptr size) { INTERCEPTOR(void *, __libc_memalign, uptr alignment, uptr size) { ENSURE_LSAN_INITED; GET_STACK_TRACE_MALLOC; - void *res = lsan_memalign(alignment, size, stack); - DTLS_on_libc_memalign(res, size); - return res; + return lsan_memalign(alignment, size, stack); } #define LSAN_MAYBE_INTERCEPT___LIBC_MEMALIGN INTERCEPT_FUNCTION(__libc_memalign) #else @@ -389,7 +388,7 @@ INTERCEPTOR(int, atexit, void (*f)()) { extern "C" { extern int _pthread_atfork(void (*prepare)(), void (*parent)(), void (*child)()); -}; +} INTERCEPTOR(int, pthread_atfork, void (*prepare)(), void (*parent)(), void (*child)()) { @@ -525,7 +524,7 @@ INTERCEPTOR(int, pthread_timedjoin_np, void *thread, void **ret, # define LSAN_MAYBE_INTERCEPT_TIMEDJOIN # endif // SANITIZER_INTERCEPT_TIMEDJOIN -DEFINE_REAL_PTHREAD_FUNCTIONS +DEFINE_INTERNAL_PTHREAD_FUNCTIONS INTERCEPTOR(void, _exit, int status) { if (status == 0 && HasReportedLeaks()) status = common_flags()->exitcode; @@ -543,6 +542,7 @@ namespace __lsan { void InitializeInterceptors() { // Fuchsia doesn't use interceptors that require any setup. #if !SANITIZER_FUCHSIA + __interception::DoesNotSupportStaticLinking(); InitializeSignalInterceptors(); INTERCEPT_FUNCTION(malloc); diff --git a/libsanitizer/lsan/lsan_posix.cpp b/libsanitizer/lsan/lsan_posix.cpp index d99e1cc..593000b 100644 --- a/libsanitizer/lsan/lsan_posix.cpp +++ b/libsanitizer/lsan/lsan_posix.cpp @@ -14,11 +14,13 @@ #include "sanitizer_common/sanitizer_platform.h" #if SANITIZER_POSIX -#include "lsan.h" -#include "lsan_allocator.h" -#include "lsan_thread.h" -#include "sanitizer_common/sanitizer_stacktrace.h" -#include "sanitizer_common/sanitizer_tls_get_addr.h" +# include <pthread.h> + +# include "lsan.h" +# include "lsan_allocator.h" +# include "lsan_thread.h" +# include "sanitizer_common/sanitizer_stacktrace.h" +# include "sanitizer_common/sanitizer_tls_get_addr.h" namespace __lsan { @@ -48,12 +50,8 @@ void ThreadContext::OnStarted(void *arg) { void ThreadStart(u32 tid, tid_t os_id, ThreadType thread_type) { OnStartedArgs args; - uptr stack_size = 0; - uptr tls_size = 0; - GetThreadStackAndTls(tid == kMainTid, &args.stack_begin, &stack_size, - &args.tls_begin, &tls_size); - args.stack_end = args.stack_begin + stack_size; - args.tls_end = args.tls_begin + tls_size; + GetThreadStackAndTls(tid == kMainTid, &args.stack_begin, &args.stack_end, + &args.tls_begin, &args.tls_end); GetAllocatorCacheRange(&args.cache_begin, &args.cache_end); args.dtls = DTLS_Get(); ThreadContextLsanBase::ThreadStart(tid, os_id, thread_type, &args); @@ -98,6 +96,31 @@ void InstallAtExitCheckLeaks() { Atexit(DoLeakCheck); } +static void BeforeFork() { + VReport(2, "BeforeFork tid: %llu\n", GetTid()); + LockGlobal(); + LockThreads(); + LockAllocator(); + StackDepotLockBeforeFork(); +} + +static void AfterFork(bool fork_child) { + StackDepotUnlockAfterFork(fork_child); + UnlockAllocator(); + UnlockThreads(); + UnlockGlobal(); + VReport(2, "AfterFork tid: %llu\n", GetTid()); +} + +void InstallAtForkHandler() { +# if SANITIZER_SOLARIS || SANITIZER_NETBSD || SANITIZER_APPLE + return; // FIXME: Implement FutexWait. +# endif + pthread_atfork( + &BeforeFork, []() { AfterFork(/* fork_child= */ false); }, + []() { AfterFork(/* fork_child= */ true); }); +} + } // namespace __lsan #endif // SANITIZER_POSIX diff --git a/libsanitizer/lsan/lsan_preinit.cpp b/libsanitizer/lsan/lsan_preinit.cpp index cd94e1e..30e90f7 100644 --- a/libsanitizer/lsan/lsan_preinit.cpp +++ b/libsanitizer/lsan/lsan_preinit.cpp @@ -14,8 +14,8 @@ #include "lsan.h" #if SANITIZER_CAN_USE_PREINIT_ARRAY - // We force __lsan_init to be called before anyone else by placing it into - // .preinit_array section. - __attribute__((section(".preinit_array"), used)) - void (*__local_lsan_preinit)(void) = __lsan_init; +// This section is linked into the main executable when -fsanitize=leak is +// specified to perform initialization at a very early stage. +__attribute__((section(".preinit_array"), used)) static auto preinit = + __lsan_init; #endif diff --git a/libsanitizer/lsan/lsan_thread.cpp b/libsanitizer/lsan/lsan_thread.cpp index 8aa3111..b66ea61 100644 --- a/libsanitizer/lsan/lsan_thread.cpp +++ b/libsanitizer/lsan/lsan_thread.cpp @@ -18,6 +18,7 @@ #include "lsan_common.h" #include "sanitizer_common/sanitizer_common.h" #include "sanitizer_common/sanitizer_placement_new.h" +#include "sanitizer_common/sanitizer_thread_history.h" #include "sanitizer_common/sanitizer_thread_registry.h" #include "sanitizer_common/sanitizer_tls_get_addr.h" @@ -35,12 +36,12 @@ static ThreadContextBase *CreateThreadContext(u32 tid) { } void InitializeThreads() { - static ALIGNED(alignof( - ThreadRegistry)) char thread_registry_placeholder[sizeof(ThreadRegistry)]; + alignas(alignof(ThreadRegistry)) static char + thread_registry_placeholder[sizeof(ThreadRegistry)]; thread_registry = new (thread_registry_placeholder) ThreadRegistry(CreateThreadContext); - static ALIGNED(alignof(ThreadArgRetval)) char + alignas(alignof(ThreadArgRetval)) static char thread_arg_retval_placeholder[sizeof(ThreadArgRetval)]; thread_arg_retval = new (thread_arg_retval_placeholder) ThreadArgRetval(); } @@ -109,6 +110,12 @@ void GetRunningThreadsLocked(InternalMmapVector<tid_t> *threads) { threads); } +void PrintThreads() { + InternalScopedString out; + PrintThreadHistory(*thread_registry, out); + Report("%s\n", out.data()); +} + void GetAdditionalThreadContextPtrsLocked(InternalMmapVector<uptr> *ptrs) { GetThreadArgRetval().GetAllPtrsLocked(ptrs); } diff --git a/libsanitizer/sanitizer_common/Makefile.am b/libsanitizer/sanitizer_common/Makefile.am index 9ed6ef8..d3445d8 100644 --- a/libsanitizer/sanitizer_common/Makefile.am +++ b/libsanitizer/sanitizer_common/Makefile.am @@ -47,7 +47,6 @@ sanitizer_common_files = \ sanitizer_netbsd.cpp \ sanitizer_platform_limits_freebsd.cpp \ sanitizer_platform_limits_linux.cpp \ - sanitizer_platform_limits_openbsd.cpp \ sanitizer_platform_limits_posix.cpp \ sanitizer_platform_limits_solaris.cpp \ sanitizer_posix.cpp \ @@ -74,15 +73,18 @@ sanitizer_common_files = \ sanitizer_symbolizer.cpp \ sanitizer_symbolizer_libbacktrace.cpp \ sanitizer_symbolizer_libcdep.cpp \ + sanitizer_symbolizer_markup.cpp \ sanitizer_symbolizer_posix_libcdep.cpp \ sanitizer_symbolizer_win.cpp \ sanitizer_termination.cpp \ sanitizer_thread_arg_retval.cpp \ + sanitizer_thread_history.cpp \ sanitizer_thread_registry.cpp \ sanitizer_tls_get_addr.cpp \ sanitizer_unwind_linux_libcdep.cpp \ sanitizer_unwind_win.cpp \ - sanitizer_win.cpp + sanitizer_win.cpp \ + sanitizer_win_interception.cpp libsanitizer_common_la_SOURCES = $(sanitizer_common_files) diff --git a/libsanitizer/sanitizer_common/Makefile.in b/libsanitizer/sanitizer_common/Makefile.in index 31fff82..df7a254 100644 --- a/libsanitizer/sanitizer_common/Makefile.in +++ b/libsanitizer/sanitizer_common/Makefile.in @@ -133,7 +133,6 @@ am__objects_1 = sancov_flags.lo sanitizer_allocator.lo \ sanitizer_mac.lo sanitizer_mac_libcdep.lo sanitizer_mutex.lo \ sanitizer_netbsd.lo sanitizer_platform_limits_freebsd.lo \ sanitizer_platform_limits_linux.lo \ - sanitizer_platform_limits_openbsd.lo \ sanitizer_platform_limits_posix.lo \ sanitizer_platform_limits_solaris.lo sanitizer_posix.lo \ sanitizer_posix_libcdep.lo sanitizer_printf.lo \ @@ -148,12 +147,13 @@ am__objects_1 = sancov_flags.lo sanitizer_allocator.lo \ sanitizer_stoptheworld_linux_libcdep.lo \ sanitizer_stoptheworld_mac.lo sanitizer_suppressions.lo \ sanitizer_symbolizer.lo sanitizer_symbolizer_libbacktrace.lo \ - sanitizer_symbolizer_libcdep.lo \ + sanitizer_symbolizer_libcdep.lo sanitizer_symbolizer_markup.lo \ sanitizer_symbolizer_posix_libcdep.lo \ sanitizer_symbolizer_win.lo sanitizer_termination.lo \ - sanitizer_thread_arg_retval.lo sanitizer_thread_registry.lo \ - sanitizer_tls_get_addr.lo sanitizer_unwind_linux_libcdep.lo \ - sanitizer_unwind_win.lo sanitizer_win.lo + sanitizer_thread_arg_retval.lo sanitizer_thread_history.lo \ + sanitizer_thread_registry.lo sanitizer_tls_get_addr.lo \ + sanitizer_unwind_linux_libcdep.lo sanitizer_unwind_win.lo \ + sanitizer_win.lo sanitizer_win_interception.lo am_libsanitizer_common_la_OBJECTS = $(am__objects_1) libsanitizer_common_la_OBJECTS = $(am_libsanitizer_common_la_OBJECTS) AM_V_lt = $(am__v_lt_@AM_V@) @@ -409,7 +409,6 @@ sanitizer_common_files = \ sanitizer_netbsd.cpp \ sanitizer_platform_limits_freebsd.cpp \ sanitizer_platform_limits_linux.cpp \ - sanitizer_platform_limits_openbsd.cpp \ sanitizer_platform_limits_posix.cpp \ sanitizer_platform_limits_solaris.cpp \ sanitizer_posix.cpp \ @@ -436,15 +435,18 @@ sanitizer_common_files = \ sanitizer_symbolizer.cpp \ sanitizer_symbolizer_libbacktrace.cpp \ sanitizer_symbolizer_libcdep.cpp \ + sanitizer_symbolizer_markup.cpp \ sanitizer_symbolizer_posix_libcdep.cpp \ sanitizer_symbolizer_win.cpp \ sanitizer_termination.cpp \ sanitizer_thread_arg_retval.cpp \ + sanitizer_thread_history.cpp \ sanitizer_thread_registry.cpp \ sanitizer_tls_get_addr.cpp \ sanitizer_unwind_linux_libcdep.cpp \ sanitizer_unwind_win.cpp \ - sanitizer_win.cpp + sanitizer_win.cpp \ + sanitizer_win_interception.cpp libsanitizer_common_la_SOURCES = $(sanitizer_common_files) libsanitizer_common_la_LIBADD = $(SANITIZER_COMMON_TARGET_DEPENDENT_OBJECTS) @@ -568,7 +570,6 @@ distclean-compile: @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sanitizer_netbsd.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sanitizer_platform_limits_freebsd.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sanitizer_platform_limits_linux.Plo@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sanitizer_platform_limits_openbsd.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sanitizer_platform_limits_posix.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sanitizer_platform_limits_solaris.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sanitizer_posix.Plo@am__quote@ @@ -594,16 +595,19 @@ distclean-compile: @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sanitizer_symbolizer_libbacktrace.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sanitizer_symbolizer_libcdep.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sanitizer_symbolizer_mac.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sanitizer_symbolizer_markup.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sanitizer_symbolizer_posix_libcdep.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sanitizer_symbolizer_report.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sanitizer_symbolizer_win.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sanitizer_termination.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sanitizer_thread_arg_retval.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sanitizer_thread_history.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sanitizer_thread_registry.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sanitizer_tls_get_addr.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sanitizer_unwind_linux_libcdep.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sanitizer_unwind_win.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sanitizer_win.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sanitizer_win_interception.Plo@am__quote@ .cpp.o: @am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXXCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< diff --git a/libsanitizer/sanitizer_common/sancov_flags.cpp b/libsanitizer/sanitizer_common/sancov_flags.cpp index ed46e88..43b4bcc 100644 --- a/libsanitizer/sanitizer_common/sancov_flags.cpp +++ b/libsanitizer/sanitizer_common/sancov_flags.cpp @@ -37,10 +37,6 @@ static void RegisterSancovFlags(FlagParser *parser, SancovFlags *f) { #undef SANCOV_FLAG } -static const char *MaybeCallSancovDefaultOptions() { - return (&__sancov_default_options) ? __sancov_default_options() : ""; -} - void InitializeSancovFlags() { SancovFlags *f = sancov_flags(); f->SetDefaults(); @@ -48,7 +44,7 @@ void InitializeSancovFlags() { FlagParser parser; RegisterSancovFlags(&parser, f); - parser.ParseString(MaybeCallSancovDefaultOptions()); + parser.ParseString(__sancov_default_options()); parser.ParseStringFromEnv("SANCOV_OPTIONS"); ReportUnrecognizedFlags(); diff --git a/libsanitizer/sanitizer_common/sanitizer_allocator.cpp b/libsanitizer/sanitizer_common/sanitizer_allocator.cpp index 0513ae3..9d89937 100644 --- a/libsanitizer/sanitizer_common/sanitizer_allocator.cpp +++ b/libsanitizer/sanitizer_common/sanitizer_allocator.cpp @@ -25,7 +25,7 @@ namespace __sanitizer { const char *PrimaryAllocatorName = "SizeClassAllocator"; const char *SecondaryAllocatorName = "LargeMmapAllocator"; -static ALIGNED(64) char internal_alloc_placeholder[sizeof(InternalAllocator)]; +alignas(64) static char internal_alloc_placeholder[sizeof(InternalAllocator)]; static atomic_uint8_t internal_allocator_initialized; static StaticSpinMutex internal_alloc_init_mu; @@ -59,7 +59,7 @@ static void *RawInternalAlloc(uptr size, InternalAllocatorCache *cache, static void *RawInternalRealloc(void *ptr, uptr size, InternalAllocatorCache *cache) { - uptr alignment = 8; + constexpr usize alignment = Max<usize>(8, sizeof(void *)); if (cache == 0) { SpinMutexLock l(&internal_allocator_cache_mu); return internal_allocator()->Reallocate(&internal_allocator_cache, ptr, @@ -137,7 +137,8 @@ void InternalAllocatorUnlock() SANITIZER_NO_THREAD_SAFETY_ANALYSIS { } // LowLevelAllocator -constexpr uptr kLowLevelAllocatorDefaultAlignment = 8; +constexpr usize kLowLevelAllocatorDefaultAlignment = + Max<usize>(8, sizeof(void *)); constexpr uptr kMinNumPagesRounded = 16; constexpr uptr kMinRoundedSize = 65536; static uptr low_level_alloc_min_alignment = kLowLevelAllocatorDefaultAlignment; diff --git a/libsanitizer/sanitizer_common/sanitizer_allocator_dlsym.h b/libsanitizer/sanitizer_common/sanitizer_allocator_dlsym.h index 92b1373..b360478 100644 --- a/libsanitizer/sanitizer_common/sanitizer_allocator_dlsym.h +++ b/libsanitizer/sanitizer_common/sanitizer_allocator_dlsym.h @@ -15,6 +15,8 @@ #define SANITIZER_ALLOCATOR_DLSYM_H #include "sanitizer_allocator_internal.h" +#include "sanitizer_common/sanitizer_allocator_checks.h" +#include "sanitizer_common/sanitizer_internal_defs.h" namespace __sanitizer { @@ -31,15 +33,15 @@ struct DlSymAllocator { UNLIKELY(internal_allocator()->FromPrimary(ptr)); } - static void *Allocate(uptr size_in_bytes) { - void *ptr = InternalAlloc(size_in_bytes, nullptr, kWordSize); + static void *Allocate(uptr size_in_bytes, uptr align = kWordSize) { + void *ptr = InternalAlloc(size_in_bytes, nullptr, align); CHECK(internal_allocator()->FromPrimary(ptr)); Details::OnAllocate(ptr, internal_allocator()->GetActuallyAllocatedSize(ptr)); return ptr; } - static void *Callocate(SIZE_T nmemb, SIZE_T size) { + static void *Callocate(usize nmemb, usize size) { void *ptr = InternalCalloc(nmemb, size); CHECK(internal_allocator()->FromPrimary(ptr)); Details::OnAllocate(ptr, @@ -70,6 +72,11 @@ struct DlSymAllocator { return new_ptr; } + static void *ReallocArray(void *ptr, uptr count, uptr size) { + CHECK(!CheckForCallocOverflow(count, size)); + return Realloc(ptr, count * size); + } + static void OnAllocate(const void *ptr, uptr size) {} static void OnFree(const void *ptr, uptr size) {} }; diff --git a/libsanitizer/sanitizer_common/sanitizer_allocator_interface.h b/libsanitizer/sanitizer_common/sanitizer_allocator_interface.h index de2b271..d1a33a4 100644 --- a/libsanitizer/sanitizer_common/sanitizer_allocator_interface.h +++ b/libsanitizer/sanitizer_common/sanitizer_allocator_interface.h @@ -40,6 +40,8 @@ SANITIZER_INTERFACE_ATTRIBUTE SANITIZER_WEAK_ATTRIBUTE void __sanitizer_malloc_hook(void *ptr, uptr size); SANITIZER_INTERFACE_ATTRIBUTE SANITIZER_WEAK_ATTRIBUTE void __sanitizer_free_hook(void *ptr); +SANITIZER_INTERFACE_ATTRIBUTE SANITIZER_WEAK_ATTRIBUTE int +__sanitizer_ignore_free_hook(void *ptr); SANITIZER_INTERFACE_ATTRIBUTE SANITIZER_WEAK_ATTRIBUTE void __sanitizer_purge_allocator(); diff --git a/libsanitizer/sanitizer_common/sanitizer_allocator_primary32.h b/libsanitizer/sanitizer_common/sanitizer_allocator_primary32.h index 52fe3fe..602b197 100644 --- a/libsanitizer/sanitizer_common/sanitizer_allocator_primary32.h +++ b/libsanitizer/sanitizer_common/sanitizer_allocator_primary32.h @@ -278,7 +278,7 @@ class SizeClassAllocator32 { static const uptr kRegionSize = 1 << kRegionSizeLog; static const uptr kNumPossibleRegions = kSpaceSize / kRegionSize; - struct ALIGNED(SANITIZER_CACHE_LINE_SIZE) SizeClassInfo { + struct alignas(SANITIZER_CACHE_LINE_SIZE) SizeClassInfo { StaticSpinMutex mutex; IntrusiveList<TransferBatch> free_list; u32 rand_state; diff --git a/libsanitizer/sanitizer_common/sanitizer_allocator_primary64.h b/libsanitizer/sanitizer_common/sanitizer_allocator_primary64.h index d77bc05..0b0bdb0 100644 --- a/libsanitizer/sanitizer_common/sanitizer_allocator_primary64.h +++ b/libsanitizer/sanitizer_common/sanitizer_allocator_primary64.h @@ -316,13 +316,13 @@ class SizeClassAllocator64 { Printf( "%s %02zd (%6zd): mapped: %6zdK allocs: %7zd frees: %7zd inuse: %6zd " "num_freed_chunks %7zd avail: %6zd rss: %6zdK releases: %6zd " - "last released: %6lldK region: 0x%zx\n", + "last released: %6lldK region: %p\n", region->exhausted ? "F" : " ", class_id, ClassIdToSize(class_id), region->mapped_user >> 10, region->stats.n_allocated, region->stats.n_freed, in_use, region->num_freed_chunks, avail_chunks, rss >> 10, region->rtoi.num_releases, region->rtoi.last_released_bytes >> 10, - SpaceBeg() + kRegionSize * class_id); + (void *)(SpaceBeg() + kRegionSize * class_id)); } void PrintStats() { @@ -639,13 +639,14 @@ class SizeClassAllocator64 { static_assert(kRegionSize >= SizeClassMap::kMaxSize, "Region size exceed largest size"); // kRegionSize must be <= 2^36, see CompactPtrT. - COMPILER_CHECK((kRegionSize) <= (1ULL << (SANITIZER_WORDSIZE / 2 + 4))); + COMPILER_CHECK((kRegionSize) <= + (1ULL << (sizeof(CompactPtrT) * 8 + kCompactPtrScale))); // Call mmap for user memory with at least this size. - static const uptr kUserMapSize = 1 << 16; + static const uptr kUserMapSize = 1 << 18; // Call mmap for metadata memory with at least this size. static const uptr kMetaMapSize = 1 << 16; // Call mmap for free array memory with at least this size. - static const uptr kFreeArrayMapSize = 1 << 16; + static const uptr kFreeArrayMapSize = 1 << 18; atomic_sint32_t release_to_os_interval_ms_; @@ -666,7 +667,7 @@ class SizeClassAllocator64 { u64 last_released_bytes; }; - struct ALIGNED(SANITIZER_CACHE_LINE_SIZE) RegionInfo { + struct alignas(SANITIZER_CACHE_LINE_SIZE) RegionInfo { Mutex mutex; uptr num_freed_chunks; // Number of elements in the freearray. uptr mapped_free_array; // Bytes mapped for freearray. diff --git a/libsanitizer/sanitizer_common/sanitizer_atomic.h b/libsanitizer/sanitizer_common/sanitizer_atomic.h index 46f0695..257c457 100644 --- a/libsanitizer/sanitizer_common/sanitizer_atomic.h +++ b/libsanitizer/sanitizer_common/sanitizer_atomic.h @@ -18,12 +18,24 @@ namespace __sanitizer { enum memory_order { +// If the __atomic atomic builtins are supported (Clang/GCC), use the +// compiler provided macro values so that we can map the atomic operations +// to __atomic_* directly. +#ifdef __ATOMIC_SEQ_CST + memory_order_relaxed = __ATOMIC_RELAXED, + memory_order_consume = __ATOMIC_CONSUME, + memory_order_acquire = __ATOMIC_ACQUIRE, + memory_order_release = __ATOMIC_RELEASE, + memory_order_acq_rel = __ATOMIC_ACQ_REL, + memory_order_seq_cst = __ATOMIC_SEQ_CST +#else memory_order_relaxed = 1 << 0, memory_order_consume = 1 << 1, memory_order_acquire = 1 << 2, memory_order_release = 1 << 3, memory_order_acq_rel = 1 << 4, memory_order_seq_cst = 1 << 5 +#endif }; struct atomic_uint8_t { @@ -49,7 +61,7 @@ struct atomic_uint32_t { struct atomic_uint64_t { typedef u64 Type; // On 32-bit platforms u64 is not necessary aligned on 8 bytes. - volatile ALIGNED(8) Type val_dont_use; + alignas(8) volatile Type val_dont_use; }; struct atomic_uintptr_t { diff --git a/libsanitizer/sanitizer_common/sanitizer_atomic_clang.h b/libsanitizer/sanitizer_common/sanitizer_atomic_clang.h index 4318d64..1414092 100644 --- a/libsanitizer/sanitizer_common/sanitizer_atomic_clang.h +++ b/libsanitizer/sanitizer_common/sanitizer_atomic_clang.h @@ -14,60 +14,63 @@ #ifndef SANITIZER_ATOMIC_CLANG_H #define SANITIZER_ATOMIC_CLANG_H -#if defined(__i386__) || defined(__x86_64__) -# include "sanitizer_atomic_clang_x86.h" -#else -# include "sanitizer_atomic_clang_other.h" -#endif - namespace __sanitizer { -// We would like to just use compiler builtin atomic operations -// for loads and stores, but they are mostly broken in clang: -// - they lead to vastly inefficient code generation -// (http://llvm.org/bugs/show_bug.cgi?id=17281) -// - 64-bit atomic operations are not implemented on x86_32 -// (http://llvm.org/bugs/show_bug.cgi?id=15034) -// - they are not implemented on ARM -// error: undefined reference to '__atomic_load_4' +// We use the compiler builtin atomic operations for loads and stores, which +// generates correct code for all architectures, but may require libatomic +// on platforms where e.g. 64-bit atomics are not supported natively. // See http://www.cl.cam.ac.uk/~pes20/cpp/cpp0xmappings.html // for mappings of the memory model to different processors. -inline void atomic_signal_fence(memory_order) { +inline void atomic_signal_fence(memory_order mo) { __atomic_signal_fence(mo); } + +inline void atomic_thread_fence(memory_order mo) { __atomic_thread_fence(mo); } + +inline void proc_yield(int cnt) { + __asm__ __volatile__("" ::: "memory"); +#if defined(__i386__) || defined(__x86_64__) + for (int i = 0; i < cnt; i++) __asm__ __volatile__("pause"); __asm__ __volatile__("" ::: "memory"); +#endif } -inline void atomic_thread_fence(memory_order) { - __sync_synchronize(); +template <typename T> +inline typename T::Type atomic_load(const volatile T *a, memory_order mo) { + DCHECK(mo == memory_order_relaxed || mo == memory_order_consume || + mo == memory_order_acquire || mo == memory_order_seq_cst); + DCHECK(!((uptr)a % sizeof(*a))); + return __atomic_load_n(&a->val_dont_use, mo); } -template<typename T> -inline typename T::Type atomic_fetch_add(volatile T *a, - typename T::Type v, memory_order mo) { - (void)mo; +template <typename T> +inline void atomic_store(volatile T *a, typename T::Type v, memory_order mo) { + DCHECK(mo == memory_order_relaxed || mo == memory_order_release || + mo == memory_order_seq_cst); DCHECK(!((uptr)a % sizeof(*a))); - return __sync_fetch_and_add(&a->val_dont_use, v); + __atomic_store_n(&a->val_dont_use, v, mo); } -template<typename T> -inline typename T::Type atomic_fetch_sub(volatile T *a, - typename T::Type v, memory_order mo) { +template <typename T> +inline typename T::Type atomic_fetch_add(volatile T *a, typename T::Type v, + memory_order mo) { + DCHECK(!((uptr)a % sizeof(*a))); + return __atomic_fetch_add(&a->val_dont_use, v, mo); +} + +template <typename T> +inline typename T::Type atomic_fetch_sub(volatile T *a, typename T::Type v, + memory_order mo) { (void)mo; DCHECK(!((uptr)a % sizeof(*a))); - return __sync_fetch_and_add(&a->val_dont_use, -v); + return __atomic_fetch_sub(&a->val_dont_use, v, mo); } -template<typename T> -inline typename T::Type atomic_exchange(volatile T *a, - typename T::Type v, memory_order mo) { +template <typename T> +inline typename T::Type atomic_exchange(volatile T *a, typename T::Type v, + memory_order mo) { DCHECK(!((uptr)a % sizeof(*a))); - if (mo & (memory_order_release | memory_order_acq_rel | memory_order_seq_cst)) - __sync_synchronize(); - v = __sync_lock_test_and_set(&a->val_dont_use, v); - if (mo == memory_order_seq_cst) - __sync_synchronize(); - return v; + return __atomic_exchange_n(&a->val_dont_use, v, mo); } template <typename T> @@ -82,9 +85,8 @@ inline bool atomic_compare_exchange_strong(volatile T *a, typename T::Type *cmp, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST); } -template<typename T> -inline bool atomic_compare_exchange_weak(volatile T *a, - typename T::Type *cmp, +template <typename T> +inline bool atomic_compare_exchange_weak(volatile T *a, typename T::Type *cmp, typename T::Type xchg, memory_order mo) { return atomic_compare_exchange_strong(a, cmp, xchg, mo); @@ -92,13 +94,6 @@ inline bool atomic_compare_exchange_weak(volatile T *a, } // namespace __sanitizer -// This include provides explicit template instantiations for atomic_uint64_t -// on MIPS32, which does not directly support 8 byte atomics. It has to -// proceed the template definitions above. -#if defined(_MIPS_SIM) && defined(_ABIO32) && _MIPS_SIM == _ABIO32 -# include "sanitizer_atomic_clang_mips.h" -#endif - #undef ATOMIC_ORDER #endif // SANITIZER_ATOMIC_CLANG_H diff --git a/libsanitizer/sanitizer_common/sanitizer_atomic_clang_mips.h b/libsanitizer/sanitizer_common/sanitizer_atomic_clang_mips.h deleted file mode 100644 index f3d3052..0000000 --- a/libsanitizer/sanitizer_common/sanitizer_atomic_clang_mips.h +++ /dev/null @@ -1,117 +0,0 @@ -//===-- sanitizer_atomic_clang_mips.h ---------------------------*- C++ -*-===// -// -// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. -// See https://llvm.org/LICENSE.txt for license information. -// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception -// -//===----------------------------------------------------------------------===// -// -// This file is a part of ThreadSanitizer/AddressSanitizer runtime. -// Not intended for direct inclusion. Include sanitizer_atomic.h. -// -//===----------------------------------------------------------------------===// - -#ifndef SANITIZER_ATOMIC_CLANG_MIPS_H -#define SANITIZER_ATOMIC_CLANG_MIPS_H - -namespace __sanitizer { - -// MIPS32 does not support atomics > 4 bytes. To address this lack of -// functionality, the sanitizer library provides helper methods which use an -// internal spin lock mechanism to emulate atomic operations when the size is -// 8 bytes. -static void __spin_lock(volatile int *lock) { - while (__sync_lock_test_and_set(lock, 1)) - while (*lock) { - } -} - -static void __spin_unlock(volatile int *lock) { __sync_lock_release(lock); } - -// Make sure the lock is on its own cache line to prevent false sharing. -// Put it inside a struct that is aligned and padded to the typical MIPS -// cacheline which is 32 bytes. -static struct { - int lock; - char pad[32 - sizeof(int)]; -} __attribute__((aligned(32))) lock = {0, {0}}; - -template <> -inline atomic_uint64_t::Type atomic_fetch_add(volatile atomic_uint64_t *ptr, - atomic_uint64_t::Type val, - memory_order mo) { - DCHECK(mo & - (memory_order_relaxed | memory_order_release | memory_order_seq_cst)); - DCHECK(!((uptr)ptr % sizeof(*ptr))); - - atomic_uint64_t::Type ret; - - __spin_lock(&lock.lock); - ret = *(const_cast<atomic_uint64_t::Type volatile *>(&ptr->val_dont_use)); - ptr->val_dont_use = ret + val; - __spin_unlock(&lock.lock); - - return ret; -} - -template <> -inline atomic_uint64_t::Type atomic_fetch_sub(volatile atomic_uint64_t *ptr, - atomic_uint64_t::Type val, - memory_order mo) { - return atomic_fetch_add(ptr, -val, mo); -} - -template <> -inline bool atomic_compare_exchange_strong(volatile atomic_uint64_t *ptr, - atomic_uint64_t::Type *cmp, - atomic_uint64_t::Type xchg, - memory_order mo) { - DCHECK(mo & - (memory_order_relaxed | memory_order_release | memory_order_seq_cst)); - DCHECK(!((uptr)ptr % sizeof(*ptr))); - - typedef atomic_uint64_t::Type Type; - Type cmpv = *cmp; - Type prev; - bool ret = false; - - __spin_lock(&lock.lock); - prev = *(const_cast<Type volatile *>(&ptr->val_dont_use)); - if (prev == cmpv) { - ret = true; - ptr->val_dont_use = xchg; - } - __spin_unlock(&lock.lock); - - return ret; -} - -template <> -inline atomic_uint64_t::Type atomic_load(const volatile atomic_uint64_t *ptr, - memory_order mo) { - DCHECK(mo & - (memory_order_relaxed | memory_order_release | memory_order_seq_cst)); - DCHECK(!((uptr)ptr % sizeof(*ptr))); - - atomic_uint64_t::Type zero = 0; - volatile atomic_uint64_t *Newptr = - const_cast<volatile atomic_uint64_t *>(ptr); - return atomic_fetch_add(Newptr, zero, mo); -} - -template <> -inline void atomic_store(volatile atomic_uint64_t *ptr, atomic_uint64_t::Type v, - memory_order mo) { - DCHECK(mo & - (memory_order_relaxed | memory_order_release | memory_order_seq_cst)); - DCHECK(!((uptr)ptr % sizeof(*ptr))); - - __spin_lock(&lock.lock); - ptr->val_dont_use = v; - __spin_unlock(&lock.lock); -} - -} // namespace __sanitizer - -#endif // SANITIZER_ATOMIC_CLANG_MIPS_H - diff --git a/libsanitizer/sanitizer_common/sanitizer_atomic_clang_other.h b/libsanitizer/sanitizer_common/sanitizer_atomic_clang_other.h deleted file mode 100644 index 4a39889..0000000 --- a/libsanitizer/sanitizer_common/sanitizer_atomic_clang_other.h +++ /dev/null @@ -1,85 +0,0 @@ -//===-- sanitizer_atomic_clang_other.h --------------------------*- C++ -*-===// -// -// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. -// See https://llvm.org/LICENSE.txt for license information. -// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception -// -//===----------------------------------------------------------------------===// -// -// This file is a part of ThreadSanitizer/AddressSanitizer runtime. -// Not intended for direct inclusion. Include sanitizer_atomic.h. -// -//===----------------------------------------------------------------------===// - -#ifndef SANITIZER_ATOMIC_CLANG_OTHER_H -#define SANITIZER_ATOMIC_CLANG_OTHER_H - -namespace __sanitizer { - - -inline void proc_yield(int cnt) { - __asm__ __volatile__("" ::: "memory"); -} - -template<typename T> -inline typename T::Type atomic_load( - const volatile T *a, memory_order mo) { - DCHECK(mo & (memory_order_relaxed | memory_order_consume - | memory_order_acquire | memory_order_seq_cst)); - DCHECK(!((uptr)a % sizeof(*a))); - typename T::Type v; - - if (sizeof(*a) < 8 || sizeof(void*) == 8) { - // Assume that aligned loads are atomic. - if (mo == memory_order_relaxed) { - v = a->val_dont_use; - } else if (mo == memory_order_consume) { - // Assume that processor respects data dependencies - // (and that compiler won't break them). - __asm__ __volatile__("" ::: "memory"); - v = a->val_dont_use; - __asm__ __volatile__("" ::: "memory"); - } else if (mo == memory_order_acquire) { - __asm__ __volatile__("" ::: "memory"); - v = a->val_dont_use; - __sync_synchronize(); - } else { // seq_cst - // E.g. on POWER we need a hw fence even before the store. - __sync_synchronize(); - v = a->val_dont_use; - __sync_synchronize(); - } - } else { - __atomic_load(const_cast<typename T::Type volatile *>(&a->val_dont_use), &v, - __ATOMIC_SEQ_CST); - } - return v; -} - -template<typename T> -inline void atomic_store(volatile T *a, typename T::Type v, memory_order mo) { - DCHECK(mo & (memory_order_relaxed | memory_order_release - | memory_order_seq_cst)); - DCHECK(!((uptr)a % sizeof(*a))); - - if (sizeof(*a) < 8 || sizeof(void*) == 8) { - // Assume that aligned loads are atomic. - if (mo == memory_order_relaxed) { - a->val_dont_use = v; - } else if (mo == memory_order_release) { - __sync_synchronize(); - a->val_dont_use = v; - __asm__ __volatile__("" ::: "memory"); - } else { // seq_cst - __sync_synchronize(); - a->val_dont_use = v; - __sync_synchronize(); - } - } else { - __atomic_store(&a->val_dont_use, &v, __ATOMIC_SEQ_CST); - } -} - -} // namespace __sanitizer - -#endif // #ifndef SANITIZER_ATOMIC_CLANG_OTHER_H diff --git a/libsanitizer/sanitizer_common/sanitizer_atomic_clang_x86.h b/libsanitizer/sanitizer_common/sanitizer_atomic_clang_x86.h deleted file mode 100644 index 51597b4..0000000 --- a/libsanitizer/sanitizer_common/sanitizer_atomic_clang_x86.h +++ /dev/null @@ -1,113 +0,0 @@ -//===-- sanitizer_atomic_clang_x86.h ----------------------------*- C++ -*-===// -// -// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. -// See https://llvm.org/LICENSE.txt for license information. -// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception -// -//===----------------------------------------------------------------------===// -// -// This file is a part of ThreadSanitizer/AddressSanitizer runtime. -// Not intended for direct inclusion. Include sanitizer_atomic.h. -// -//===----------------------------------------------------------------------===// - -#ifndef SANITIZER_ATOMIC_CLANG_X86_H -#define SANITIZER_ATOMIC_CLANG_X86_H - -namespace __sanitizer { - -inline void proc_yield(int cnt) { - __asm__ __volatile__("" ::: "memory"); - for (int i = 0; i < cnt; i++) - __asm__ __volatile__("pause"); - __asm__ __volatile__("" ::: "memory"); -} - -template<typename T> -inline typename T::Type atomic_load( - const volatile T *a, memory_order mo) { - DCHECK(mo & (memory_order_relaxed | memory_order_consume - | memory_order_acquire | memory_order_seq_cst)); - DCHECK(!((uptr)a % sizeof(*a))); - typename T::Type v; - - if (sizeof(*a) < 8 || sizeof(void*) == 8) { - // Assume that aligned loads are atomic. - if (mo == memory_order_relaxed) { - v = a->val_dont_use; - } else if (mo == memory_order_consume) { - // Assume that processor respects data dependencies - // (and that compiler won't break them). - __asm__ __volatile__("" ::: "memory"); - v = a->val_dont_use; - __asm__ __volatile__("" ::: "memory"); - } else if (mo == memory_order_acquire) { - __asm__ __volatile__("" ::: "memory"); - v = a->val_dont_use; - // On x86 loads are implicitly acquire. - __asm__ __volatile__("" ::: "memory"); - } else { // seq_cst - // On x86 plain MOV is enough for seq_cst store. - __asm__ __volatile__("" ::: "memory"); - v = a->val_dont_use; - __asm__ __volatile__("" ::: "memory"); - } - } else { - // 64-bit load on 32-bit platform. - __asm__ __volatile__( - "movq %1, %%mm0;" // Use mmx reg for 64-bit atomic moves - "movq %%mm0, %0;" // (ptr could be read-only) - "emms;" // Empty mmx state/Reset FP regs - : "=m" (v) - : "m" (a->val_dont_use) - : // mark the mmx registers as clobbered -#ifdef __MMX__ - "mm0", "mm1", "mm2", "mm3", "mm4", "mm5", "mm6", "mm7", -#endif // #ifdef __MMX__ - "memory"); - } - return v; -} - -template<typename T> -inline void atomic_store(volatile T *a, typename T::Type v, memory_order mo) { - DCHECK(mo & (memory_order_relaxed | memory_order_release - | memory_order_seq_cst)); - DCHECK(!((uptr)a % sizeof(*a))); - - if (sizeof(*a) < 8 || sizeof(void*) == 8) { - // Assume that aligned loads are atomic. - if (mo == memory_order_relaxed) { - a->val_dont_use = v; - } else if (mo == memory_order_release) { - // On x86 stores are implicitly release. - __asm__ __volatile__("" ::: "memory"); - a->val_dont_use = v; - __asm__ __volatile__("" ::: "memory"); - } else { // seq_cst - // On x86 stores are implicitly release. - __asm__ __volatile__("" ::: "memory"); - a->val_dont_use = v; - __sync_synchronize(); - } - } else { - // 64-bit store on 32-bit platform. - __asm__ __volatile__( - "movq %1, %%mm0;" // Use mmx reg for 64-bit atomic moves - "movq %%mm0, %0;" - "emms;" // Empty mmx state/Reset FP regs - : "=m" (a->val_dont_use) - : "m" (v) - : // mark the mmx registers as clobbered -#ifdef __MMX__ - "mm0", "mm1", "mm2", "mm3", "mm4", "mm5", "mm6", "mm7", -#endif // #ifdef __MMX__ - "memory"); - if (mo == memory_order_seq_cst) - __sync_synchronize(); - } -} - -} // namespace __sanitizer - -#endif // #ifndef SANITIZER_ATOMIC_CLANG_X86_H diff --git a/libsanitizer/sanitizer_common/sanitizer_atomic_msvc.h b/libsanitizer/sanitizer_common/sanitizer_atomic_msvc.h index 31317ad..d80bfdb 100644 --- a/libsanitizer/sanitizer_common/sanitizer_atomic_msvc.h +++ b/libsanitizer/sanitizer_common/sanitizer_atomic_msvc.h @@ -70,8 +70,8 @@ inline void proc_yield(int cnt) { template<typename T> inline typename T::Type atomic_load( const volatile T *a, memory_order mo) { - DCHECK(mo & (memory_order_relaxed | memory_order_consume - | memory_order_acquire | memory_order_seq_cst)); + DCHECK(mo == memory_order_relaxed || mo == memory_order_consume || + mo == memory_order_acquire || mo == memory_order_seq_cst); DCHECK(!((uptr)a % sizeof(*a))); typename T::Type v; // FIXME(dvyukov): 64-bit load is not atomic on 32-bits. @@ -87,8 +87,8 @@ inline typename T::Type atomic_load( template<typename T> inline void atomic_store(volatile T *a, typename T::Type v, memory_order mo) { - DCHECK(mo & (memory_order_relaxed | memory_order_release - | memory_order_seq_cst)); + DCHECK(mo == memory_order_relaxed || mo == memory_order_release || + mo == memory_order_seq_cst); DCHECK(!((uptr)a % sizeof(*a))); // FIXME(dvyukov): 64-bit store is not atomic on 32-bits. if (mo == memory_order_relaxed) { diff --git a/libsanitizer/sanitizer_common/sanitizer_bitvector.h b/libsanitizer/sanitizer_common/sanitizer_bitvector.h index 07a59ab..eef1e7e 100644 --- a/libsanitizer/sanitizer_common/sanitizer_bitvector.h +++ b/libsanitizer/sanitizer_common/sanitizer_bitvector.h @@ -321,23 +321,23 @@ class TwoLevelBitVector { }; private: - void check(uptr idx) const { CHECK_LE(idx, size()); } + void check(uptr idx) const { CHECK_LT(idx, size()); } uptr idx0(uptr idx) const { uptr res = idx / (BV::kSize * BV::kSize); - CHECK_LE(res, kLevel1Size); + CHECK_LT(res, kLevel1Size); return res; } uptr idx1(uptr idx) const { uptr res = (idx / BV::kSize) % BV::kSize; - CHECK_LE(res, BV::kSize); + CHECK_LT(res, BV::kSize); return res; } uptr idx2(uptr idx) const { uptr res = idx % BV::kSize; - CHECK_LE(res, BV::kSize); + CHECK_LT(res, BV::kSize); return res; } diff --git a/libsanitizer/sanitizer_common/sanitizer_chained_origin_depot.cpp b/libsanitizer/sanitizer_common/sanitizer_chained_origin_depot.cpp index e0e2bd0..df2b2eb 100644 --- a/libsanitizer/sanitizer_common/sanitizer_chained_origin_depot.cpp +++ b/libsanitizer/sanitizer_common/sanitizer_chained_origin_depot.cpp @@ -139,9 +139,11 @@ u32 ChainedOriginDepot::Get(u32 id, u32 *other) { return desc.here_id; } -void ChainedOriginDepot::LockAll() { depot.LockAll(); } +void ChainedOriginDepot::LockBeforeFork() { depot.LockBeforeFork(); } -void ChainedOriginDepot::UnlockAll() { depot.UnlockAll(); } +void ChainedOriginDepot::UnlockAfterFork(bool fork_child) { + depot.UnlockAfterFork(fork_child); +} void ChainedOriginDepot::TestOnlyUnmap() { depot.TestOnlyUnmap(); } diff --git a/libsanitizer/sanitizer_common/sanitizer_chained_origin_depot.h b/libsanitizer/sanitizer_common/sanitizer_chained_origin_depot.h index f9f192b..f3da281 100644 --- a/libsanitizer/sanitizer_common/sanitizer_chained_origin_depot.h +++ b/libsanitizer/sanitizer_common/sanitizer_chained_origin_depot.h @@ -32,8 +32,8 @@ class ChainedOriginDepot { // Retrieves the stored StackDepot ID for the given origin ID. u32 Get(u32 id, u32 *other); - void LockAll(); - void UnlockAll(); + void LockBeforeFork(); + void UnlockAfterFork(bool fork_child); void TestOnlyUnmap(); private: diff --git a/libsanitizer/sanitizer_common/sanitizer_common.cpp b/libsanitizer/sanitizer_common/sanitizer_common.cpp index 5efdd86..6cd69a5 100644 --- a/libsanitizer/sanitizer_common/sanitizer_common.cpp +++ b/libsanitizer/sanitizer_common/sanitizer_common.cpp @@ -347,7 +347,13 @@ void RunMallocHooks(void *ptr, uptr size) { } } -void RunFreeHooks(void *ptr) { +// Returns '1' if the call to free() should be ignored (based on +// __sanitizer_ignore_free_hook), or '0' otherwise. +int RunFreeHooks(void *ptr) { + if (__sanitizer_ignore_free_hook(ptr)) { + return 1; + } + __sanitizer_free_hook(ptr); for (int i = 0; i < kMaxMallocFreeHooks; i++) { auto hook = MFHooks[i].free_hook; @@ -355,6 +361,8 @@ void RunFreeHooks(void *ptr) { break; hook(ptr); } + + return 0; } static int InstallMallocFreeHooks(void (*malloc_hook)(const void *, uptr), @@ -419,4 +427,9 @@ SANITIZER_INTERFACE_WEAK_DEF(void, __sanitizer_free_hook, void *ptr) { (void)ptr; } +SANITIZER_INTERFACE_WEAK_DEF(int, __sanitizer_ignore_free_hook, void *ptr) { + (void)ptr; + return 0; +} + } // extern "C" diff --git a/libsanitizer/sanitizer_common/sanitizer_common.h b/libsanitizer/sanitizer_common/sanitizer_common.h index 6b327a4..0b5e68c 100644 --- a/libsanitizer/sanitizer_common/sanitizer_common.h +++ b/libsanitizer/sanitizer_common/sanitizer_common.h @@ -32,6 +32,7 @@ struct AddressInfo; struct BufferedStackTrace; struct SignalContext; struct StackTrace; +struct SymbolizedStack; // Constants. const uptr kWordSize = SANITIZER_WORDSIZE / 8; @@ -59,14 +60,10 @@ inline int Verbosity() { return atomic_load(¤t_verbosity, memory_order_relaxed); } -#if SANITIZER_ANDROID -inline uptr GetPageSize() { -// Android post-M sysconf(_SC_PAGESIZE) crashes if called from .preinit_array. - return 4096; -} -inline uptr GetPageSizeCached() { - return 4096; -} +#if SANITIZER_ANDROID && !defined(__aarch64__) +// 32-bit Android only has 4k pages. +inline uptr GetPageSize() { return 4096; } +inline uptr GetPageSizeCached() { return 4096; } #else uptr GetPageSize(); extern uptr PageSizeCached; @@ -76,6 +73,7 @@ inline uptr GetPageSizeCached() { return PageSizeCached; } #endif + uptr GetMmapGranularity(); uptr GetMaxVirtualAddress(); uptr GetMaxUserVirtualAddress(); @@ -85,15 +83,16 @@ int TgKill(pid_t pid, tid_t tid, int sig); uptr GetThreadSelf(); void GetThreadStackTopAndBottom(bool at_initialization, uptr *stack_top, uptr *stack_bottom); -void GetThreadStackAndTls(bool main, uptr *stk_addr, uptr *stk_size, - uptr *tls_addr, uptr *tls_size); +void GetThreadStackAndTls(bool main, uptr *stk_begin, uptr *stk_end, + uptr *tls_begin, uptr *tls_end); // Memory management void *MmapOrDie(uptr size, const char *mem_type, bool raw_report = false); + inline void *MmapOrDieQuietly(uptr size, const char *mem_type) { return MmapOrDie(size, mem_type, /*raw_report*/ true); } -void UnmapOrDie(void *addr, uptr size); +void UnmapOrDie(void *addr, uptr size, bool raw_report = false); // Behaves just like MmapOrDie, but tolerates out of memory condition, in that // case returns nullptr. void *MmapOrDieOnFatalError(uptr size, const char *mem_type); @@ -138,7 +137,8 @@ void UnmapFromTo(uptr from, uptr to); // shadow_size_bytes bytes on the right, which on linux is mapped no access. // The high_mem_end may be updated if the original shadow size doesn't fit. uptr MapDynamicShadow(uptr shadow_size_bytes, uptr shadow_scale, - uptr min_shadow_base_alignment, uptr &high_mem_end); + uptr min_shadow_base_alignment, uptr &high_mem_end, + uptr granularity); // Let S = max(shadow_size, num_aliases * alias_size, ring_buffer_size). // Reserves 2*S bytes of address space to the right of the returned address and @@ -177,7 +177,7 @@ bool DontDumpShadowMemory(uptr addr, uptr length); // Check if the built VMA size matches the runtime one. void CheckVMASize(); void RunMallocHooks(void *ptr, uptr size); -void RunFreeHooks(void *ptr); +int RunFreeHooks(void *ptr); class ReservedAddressRange { public: @@ -239,13 +239,15 @@ void RemoveANSIEscapeSequencesFromString(char *buffer); void Printf(const char *format, ...) FORMAT(1, 2); void Report(const char *format, ...) FORMAT(1, 2); void SetPrintfAndReportCallback(void (*callback)(const char *)); -#define VReport(level, ...) \ - do { \ - if ((uptr)Verbosity() >= (level)) Report(__VA_ARGS__); \ +#define VReport(level, ...) \ + do { \ + if (UNLIKELY((uptr)Verbosity() >= (level))) \ + Report(__VA_ARGS__); \ } while (0) -#define VPrintf(level, ...) \ - do { \ - if ((uptr)Verbosity() >= (level)) Printf(__VA_ARGS__); \ +#define VPrintf(level, ...) \ + do { \ + if (UNLIKELY((uptr)Verbosity() >= (level))) \ + Printf(__VA_ARGS__); \ } while (0) // Lock sanitizer error reporting and protects against nested errors. @@ -266,7 +268,15 @@ class ScopedErrorReportLock { extern uptr stoptheworld_tracer_pid; extern uptr stoptheworld_tracer_ppid; +// Returns true if the entire range can be read. bool IsAccessibleMemoryRange(uptr beg, uptr size); +// Attempts to copy `n` bytes from memory range starting at `src` to `dest`. +// Returns true if the entire range can be read. Returns `false` if any part of +// the source range cannot be read, in which case the contents of `dest` are +// undefined. +bool TryMemCpy(void *dest, const void *src, uptr n); +// Copies accessible memory, and zero fill inaccessible. +void MemCpyAccessible(void *dest, const void *src, uptr n); // Error report formatting. const char *StripPathPrefix(const char *filepath, @@ -393,6 +403,8 @@ void ReportErrorSummary(const char *error_type, const AddressInfo &info, // Same as above, but obtains AddressInfo by symbolizing top stack trace frame. void ReportErrorSummary(const char *error_type, const StackTrace *trace, const char *alt_tool_name = nullptr); +// Skips frames which we consider internal and not usefull to the users. +const SymbolizedStack *SkipInternalFrames(const SymbolizedStack *frames); void ReportMmapWriteExec(int prot, int mflags); @@ -507,7 +519,7 @@ inline int ToLower(int c) { // A low-level vector based on mmap. May incur a significant memory overhead for // small vectors. // WARNING: The current implementation supports only POD types. -template<typename T> +template <typename T, bool raw_report = false> class InternalMmapVectorNoCtor { public: using value_type = T; @@ -517,7 +529,7 @@ class InternalMmapVectorNoCtor { data_ = 0; reserve(initial_capacity); } - void Destroy() { UnmapOrDie(data_, capacity_bytes_); } + void Destroy() { UnmapOrDie(data_, capacity_bytes_, raw_report); } T &operator[](uptr i) { CHECK_LT(i, size_); return data_[i]; @@ -593,9 +605,10 @@ class InternalMmapVectorNoCtor { CHECK_LE(size_, new_capacity); uptr new_capacity_bytes = RoundUpTo(new_capacity * sizeof(T), GetPageSizeCached()); - T *new_data = (T *)MmapOrDie(new_capacity_bytes, "InternalMmapVector"); + T *new_data = + (T *)MmapOrDie(new_capacity_bytes, "InternalMmapVector", raw_report); internal_memcpy(new_data, data_, size_ * sizeof(T)); - UnmapOrDie(data_, capacity_bytes_); + UnmapOrDie(data_, capacity_bytes_, raw_report); data_ = new_data; capacity_bytes_ = new_capacity_bytes; } @@ -1094,7 +1107,7 @@ inline u32 GetNumberOfCPUsCached() { } // namespace __sanitizer -inline void *operator new(__sanitizer::operator_new_size_type size, +inline void *operator new(__sanitizer::usize size, __sanitizer::LowLevelAllocator &alloc) { return alloc.Allocate(size); } diff --git a/libsanitizer/sanitizer_common/sanitizer_common_interceptors.inc b/libsanitizer/sanitizer_common/sanitizer_common_interceptors.inc index 607ecae..b8627f8 100644 --- a/libsanitizer/sanitizer_common/sanitizer_common_interceptors.inc +++ b/libsanitizer/sanitizer_common/sanitizer_common_interceptors.inc @@ -41,6 +41,7 @@ #include "sanitizer_errno.h" #include "sanitizer_placement_new.h" #include "sanitizer_platform_interceptors.h" +#include "sanitizer_platform_limits_posix.h" #include "sanitizer_symbolizer.h" #include "sanitizer_tls_get_addr.h" @@ -127,6 +128,39 @@ extern const short *_toupper_tab_; extern const short *_tolower_tab_; #endif +#if SANITIZER_LINUX && SANITIZER_SPARC32 +// On 32-bit Linux/sparc64, double and long double are identical and glibc +// uses a __nldbl_ (no long double) prefix for various stdio functions. +# define __isoc23_fscanf __nldbl___isoc23_fscanf +# define __isoc23_scanf __nldbl___isoc23_scanf +# define __isoc23_sscanf __nldbl___isoc23_sscanf +# define __isoc23_vfscanf __nldbl___isoc23_vfscanf +# define __isoc23_vscanf __nldbl___isoc23_vscanf +# define __isoc23_vsscanf __nldbl___isoc23_vsscanf +# define __isoc99_fscanf __nldbl___isoc99_fscanf +# define __isoc99_scanf __nldbl___isoc99_scanf +# define __isoc99_sscanf __nldbl___isoc99_sscanf +# define __isoc99_vfscanf __nldbl___isoc99_vfscanf +# define __isoc99_vscanf __nldbl___isoc99_vscanf +# define __isoc99_vsscanf __nldbl___isoc99_vsscanf +# define asprintf __nldbl_asprintf +# define fprintf __nldbl_fprintf +# define fscanf __nldbl_fscanf +# define printf __nldbl_printf +# define scanf __nldbl_scanf +# define snprintf __nldbl_snprintf +# define sprintf __nldbl_sprintf +# define sscanf __nldbl_sscanf +# define vasprintf __nldbl_vasprintf +# define vfprintf __nldbl_vfprintf +# define vfscanf __nldbl_vfscanf +# define vprintf __nldbl_vprintf +# define vscanf __nldbl_vscanf +# define vsnprintf __nldbl_vsnprintf +# define vsprintf __nldbl_vsprintf +# define vsscanf __nldbl_vsscanf +#endif + #if SANITIZER_MUSL && \ (defined(__i386__) || defined(__arm__) || SANITIZER_MIPS32 || SANITIZER_PPC32) // musl 1.2.0 on existing 32-bit architectures uses new symbol names for the @@ -974,7 +1008,7 @@ INTERCEPTOR(SSIZE_T, read, int fd, void *ptr, SIZE_T count) { // FIXME: under ASan the call below may write to freed memory and corrupt // its metadata. See // https://github.com/google/sanitizers/issues/321. - SSIZE_T res = REAL(read)(fd, ptr, count); + SSIZE_T res = COMMON_INTERCEPTOR_BLOCK_REAL(read)(fd, ptr, count); if (res > 0) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, ptr, res); if (res >= 0 && fd >= 0) COMMON_INTERCEPTOR_FD_ACQUIRE(ctx, fd); return res; @@ -1009,7 +1043,7 @@ INTERCEPTOR(SSIZE_T, pread, int fd, void *ptr, SIZE_T count, OFF_T offset) { // FIXME: under ASan the call below may write to freed memory and corrupt // its metadata. See // https://github.com/google/sanitizers/issues/321. - SSIZE_T res = REAL(pread)(fd, ptr, count, offset); + SSIZE_T res = COMMON_INTERCEPTOR_BLOCK_REAL(pread)(fd, ptr, count, offset); if (res > 0) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, ptr, res); if (res >= 0 && fd >= 0) COMMON_INTERCEPTOR_FD_ACQUIRE(ctx, fd); return res; @@ -1027,7 +1061,7 @@ INTERCEPTOR(SSIZE_T, pread64, int fd, void *ptr, SIZE_T count, OFF64_T offset) { // FIXME: under ASan the call below may write to freed memory and corrupt // its metadata. See // https://github.com/google/sanitizers/issues/321. - SSIZE_T res = REAL(pread64)(fd, ptr, count, offset); + SSIZE_T res = COMMON_INTERCEPTOR_BLOCK_REAL(pread64)(fd, ptr, count, offset); if (res > 0) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, ptr, res); if (res >= 0 && fd >= 0) COMMON_INTERCEPTOR_FD_ACQUIRE(ctx, fd); return res; @@ -1043,7 +1077,7 @@ INTERCEPTOR_WITH_SUFFIX(SSIZE_T, readv, int fd, __sanitizer_iovec *iov, void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, readv, fd, iov, iovcnt); COMMON_INTERCEPTOR_FD_ACCESS(ctx, fd); - SSIZE_T res = REAL(readv)(fd, iov, iovcnt); + SSIZE_T res = COMMON_INTERCEPTOR_BLOCK_REAL(readv)(fd, iov, iovcnt); if (res > 0) write_iovec(ctx, iov, iovcnt, res); if (res >= 0 && fd >= 0) COMMON_INTERCEPTOR_FD_ACQUIRE(ctx, fd); return res; @@ -1059,7 +1093,7 @@ INTERCEPTOR(SSIZE_T, preadv, int fd, __sanitizer_iovec *iov, int iovcnt, void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, preadv, fd, iov, iovcnt, offset); COMMON_INTERCEPTOR_FD_ACCESS(ctx, fd); - SSIZE_T res = REAL(preadv)(fd, iov, iovcnt, offset); + SSIZE_T res = COMMON_INTERCEPTOR_BLOCK_REAL(preadv)(fd, iov, iovcnt, offset); if (res > 0) write_iovec(ctx, iov, iovcnt, res); if (res >= 0 && fd >= 0) COMMON_INTERCEPTOR_FD_ACQUIRE(ctx, fd); return res; @@ -1075,7 +1109,8 @@ INTERCEPTOR(SSIZE_T, preadv64, int fd, __sanitizer_iovec *iov, int iovcnt, void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, preadv64, fd, iov, iovcnt, offset); COMMON_INTERCEPTOR_FD_ACCESS(ctx, fd); - SSIZE_T res = REAL(preadv64)(fd, iov, iovcnt, offset); + SSIZE_T res = + COMMON_INTERCEPTOR_BLOCK_REAL(preadv64)(fd, iov, iovcnt, offset); if (res > 0) write_iovec(ctx, iov, iovcnt, res); if (res >= 0 && fd >= 0) COMMON_INTERCEPTOR_FD_ACQUIRE(ctx, fd); return res; @@ -1091,8 +1126,9 @@ INTERCEPTOR(SSIZE_T, write, int fd, void *ptr, SIZE_T count) { COMMON_INTERCEPTOR_ENTER(ctx, write, fd, ptr, count); COMMON_INTERCEPTOR_FD_ACCESS(ctx, fd); if (fd >= 0) COMMON_INTERCEPTOR_FD_RELEASE(ctx, fd); - SSIZE_T res = REAL(write)(fd, ptr, count); - // FIXME: this check should be _before_ the call to REAL(write), not after + SSIZE_T res = COMMON_INTERCEPTOR_BLOCK_REAL(write)(fd, ptr, count); + // FIXME: this check should be _before_ the call to + // COMMON_INTERCEPTOR_BLOCK_REAL(write), not after if (res > 0) COMMON_INTERCEPTOR_READ_RANGE(ctx, ptr, res); return res; } @@ -1121,7 +1157,7 @@ INTERCEPTOR(SSIZE_T, pwrite, int fd, void *ptr, SIZE_T count, OFF_T offset) { COMMON_INTERCEPTOR_ENTER(ctx, pwrite, fd, ptr, count, offset); COMMON_INTERCEPTOR_FD_ACCESS(ctx, fd); if (fd >= 0) COMMON_INTERCEPTOR_FD_RELEASE(ctx, fd); - SSIZE_T res = REAL(pwrite)(fd, ptr, count, offset); + SSIZE_T res = COMMON_INTERCEPTOR_BLOCK_REAL(pwrite)(fd, ptr, count, offset); if (res > 0) COMMON_INTERCEPTOR_READ_RANGE(ctx, ptr, res); return res; } @@ -1137,7 +1173,7 @@ INTERCEPTOR(SSIZE_T, pwrite64, int fd, void *ptr, OFF64_T count, COMMON_INTERCEPTOR_ENTER(ctx, pwrite64, fd, ptr, count, offset); COMMON_INTERCEPTOR_FD_ACCESS(ctx, fd); if (fd >= 0) COMMON_INTERCEPTOR_FD_RELEASE(ctx, fd); - SSIZE_T res = REAL(pwrite64)(fd, ptr, count, offset); + SSIZE_T res = COMMON_INTERCEPTOR_BLOCK_REAL(pwrite64)(fd, ptr, count, offset); if (res > 0) COMMON_INTERCEPTOR_READ_RANGE(ctx, ptr, res); return res; } @@ -1153,7 +1189,7 @@ INTERCEPTOR_WITH_SUFFIX(SSIZE_T, writev, int fd, __sanitizer_iovec *iov, COMMON_INTERCEPTOR_ENTER(ctx, writev, fd, iov, iovcnt); COMMON_INTERCEPTOR_FD_ACCESS(ctx, fd); if (fd >= 0) COMMON_INTERCEPTOR_FD_RELEASE(ctx, fd); - SSIZE_T res = REAL(writev)(fd, iov, iovcnt); + SSIZE_T res = COMMON_INTERCEPTOR_BLOCK_REAL(writev)(fd, iov, iovcnt); if (res > 0) read_iovec(ctx, iov, iovcnt, res); return res; } @@ -1169,7 +1205,7 @@ INTERCEPTOR(SSIZE_T, pwritev, int fd, __sanitizer_iovec *iov, int iovcnt, COMMON_INTERCEPTOR_ENTER(ctx, pwritev, fd, iov, iovcnt, offset); COMMON_INTERCEPTOR_FD_ACCESS(ctx, fd); if (fd >= 0) COMMON_INTERCEPTOR_FD_RELEASE(ctx, fd); - SSIZE_T res = REAL(pwritev)(fd, iov, iovcnt, offset); + SSIZE_T res = COMMON_INTERCEPTOR_BLOCK_REAL(pwritev)(fd, iov, iovcnt, offset); if (res > 0) read_iovec(ctx, iov, iovcnt, res); return res; } @@ -1185,7 +1221,8 @@ INTERCEPTOR(SSIZE_T, pwritev64, int fd, __sanitizer_iovec *iov, int iovcnt, COMMON_INTERCEPTOR_ENTER(ctx, pwritev64, fd, iov, iovcnt, offset); COMMON_INTERCEPTOR_FD_ACCESS(ctx, fd); if (fd >= 0) COMMON_INTERCEPTOR_FD_RELEASE(ctx, fd); - SSIZE_T res = REAL(pwritev64)(fd, iov, iovcnt, offset); + SSIZE_T res = + COMMON_INTERCEPTOR_BLOCK_REAL(pwritev64)(fd, iov, iovcnt, offset); if (res > 0) read_iovec(ctx, iov, iovcnt, res); return res; } @@ -1248,9 +1285,16 @@ INTERCEPTOR(int, prctl, int option, unsigned long arg2, unsigned long arg3, void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, prctl, option, arg2, arg3, arg4, arg5); static const int PR_SET_NAME = 15; + static const int PR_GET_NAME = 16; static const int PR_SET_VMA = 0x53564d41; static const int PR_SCHED_CORE = 62; static const int PR_SCHED_CORE_GET = 0; + static const int PR_GET_PDEATHSIG = 2; + +# if !SANITIZER_ANDROID + static const int PR_SET_SECCOMP = 22; + static const int SECCOMP_MODE_FILTER = 2; +# endif if (option == PR_SET_VMA && arg2 == 0UL) { char *name = (char *)arg5; COMMON_INTERCEPTOR_READ_RANGE(ctx, name, internal_strlen(name) + 1); @@ -1261,8 +1305,19 @@ INTERCEPTOR(int, prctl, int option, unsigned long arg2, unsigned long arg3, internal_strncpy(buff, (char *)arg2, 15); buff[15] = 0; COMMON_INTERCEPTOR_SET_THREAD_NAME(ctx, buff); - } else if (res != -1 && option == PR_SCHED_CORE && arg2 == PR_SCHED_CORE_GET) { - COMMON_INTERCEPTOR_WRITE_RANGE(ctx, (u64*)(arg5), sizeof(u64)); + } else if (res == 0 && option == PR_GET_NAME) { + char *name = (char *)arg2; + COMMON_INTERCEPTOR_WRITE_RANGE(ctx, name, internal_strlen(name) + 1); + } else if (res != -1 && option == PR_SCHED_CORE && + arg2 == PR_SCHED_CORE_GET) { + COMMON_INTERCEPTOR_WRITE_RANGE(ctx, (u64 *)(arg5), sizeof(u64)); + } else if (res != -1 && option == PR_GET_PDEATHSIG) { + COMMON_INTERCEPTOR_WRITE_RANGE(ctx, (u64 *)(arg2), sizeof(int)); +# if !SANITIZER_ANDROID + } else if (res != -1 && option == PR_SET_SECCOMP && + arg2 == SECCOMP_MODE_FILTER) { + COMMON_INTERCEPTOR_WRITE_RANGE(ctx, (u64 *)(arg3), struct_sock_fprog_sz); +# endif } return res; } @@ -2549,7 +2604,7 @@ INTERCEPTOR_WITH_SUFFIX(int, wait, int *status) { // FIXME: under ASan the call below may write to freed memory and corrupt // its metadata. See // https://github.com/google/sanitizers/issues/321. - int res = REAL(wait)(status); + int res = COMMON_INTERCEPTOR_BLOCK_REAL(wait)(status); if (res != -1 && status) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, status, sizeof(*status)); return res; @@ -2567,7 +2622,7 @@ INTERCEPTOR_WITH_SUFFIX(int, waitid, int idtype, int id, void *infop, // FIXME: under ASan the call below may write to freed memory and corrupt // its metadata. See // https://github.com/google/sanitizers/issues/321. - int res = REAL(waitid)(idtype, id, infop, options); + int res = COMMON_INTERCEPTOR_BLOCK_REAL(waitid)(idtype, id, infop, options); if (res != -1 && infop) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, infop, siginfo_t_sz); return res; @@ -2578,7 +2633,7 @@ INTERCEPTOR_WITH_SUFFIX(int, waitpid, int pid, int *status, int options) { // FIXME: under ASan the call below may write to freed memory and corrupt // its metadata. See // https://github.com/google/sanitizers/issues/321. - int res = REAL(waitpid)(pid, status, options); + int res = COMMON_INTERCEPTOR_BLOCK_REAL(waitpid)(pid, status, options); if (res != -1 && status) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, status, sizeof(*status)); return res; @@ -2589,7 +2644,7 @@ INTERCEPTOR(int, wait3, int *status, int options, void *rusage) { // FIXME: under ASan the call below may write to freed memory and corrupt // its metadata. See // https://github.com/google/sanitizers/issues/321. - int res = REAL(wait3)(status, options, rusage); + int res = COMMON_INTERCEPTOR_BLOCK_REAL(wait3)(status, options, rusage); if (res != -1) { if (status) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, status, sizeof(*status)); if (rusage) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, rusage, struct_rusage_sz); @@ -2603,7 +2658,8 @@ INTERCEPTOR(int, __wait4, int pid, int *status, int options, void *rusage) { // FIXME: under ASan the call below may write to freed memory and corrupt // its metadata. See // https://github.com/google/sanitizers/issues/321. - int res = REAL(__wait4)(pid, status, options, rusage); + int res = + COMMON_INTERCEPTOR_BLOCK_REAL(__wait4)(pid, status, options, rusage); if (res != -1) { if (status) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, status, sizeof(*status)); if (rusage) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, rusage, struct_rusage_sz); @@ -2618,7 +2674,7 @@ INTERCEPTOR(int, wait4, int pid, int *status, int options, void *rusage) { // FIXME: under ASan the call below may write to freed memory and corrupt // its metadata. See // https://github.com/google/sanitizers/issues/321. - int res = REAL(wait4)(pid, status, options, rusage); + int res = COMMON_INTERCEPTOR_BLOCK_REAL(wait4)(pid, status, options, rusage); if (res != -1) { if (status) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, status, sizeof(*status)); if (rusage) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, rusage, struct_rusage_sz); @@ -2996,7 +3052,7 @@ INTERCEPTOR(int, accept, int fd, void *addr, unsigned *addrlen) { COMMON_INTERCEPTOR_READ_RANGE(ctx, addrlen, sizeof(*addrlen)); addrlen0 = *addrlen; } - int fd2 = REAL(accept)(fd, addr, addrlen); + int fd2 = COMMON_INTERCEPTOR_BLOCK_REAL(accept)(fd, addr, addrlen); if (fd2 >= 0) { if (fd >= 0) COMMON_INTERCEPTOR_FD_SOCKET_ACCEPT(ctx, fd, fd2); if (addr && addrlen) @@ -3021,7 +3077,7 @@ INTERCEPTOR(int, accept4, int fd, void *addr, unsigned *addrlen, int f) { // FIXME: under ASan the call below may write to freed memory and corrupt // its metadata. See // https://github.com/google/sanitizers/issues/321. - int fd2 = REAL(accept4)(fd, addr, addrlen, f); + int fd2 = COMMON_INTERCEPTOR_BLOCK_REAL(accept4)(fd, addr, addrlen, f); if (fd2 >= 0) { if (fd >= 0) COMMON_INTERCEPTOR_FD_SOCKET_ACCEPT(ctx, fd, fd2); if (addr && addrlen) @@ -3045,7 +3101,7 @@ INTERCEPTOR(int, paccept, int fd, void *addr, unsigned *addrlen, addrlen0 = *addrlen; } if (set) COMMON_INTERCEPTOR_READ_RANGE(ctx, set, sizeof(*set)); - int fd2 = REAL(paccept)(fd, addr, addrlen, set, f); + int fd2 = COMMON_INTERCEPTOR_BLOCK_REAL(paccept)(fd, addr, addrlen, set, f); if (fd2 >= 0) { if (fd >= 0) COMMON_INTERCEPTOR_FD_SOCKET_ACCEPT(ctx, fd, fd2); if (addr && addrlen) @@ -3126,7 +3182,7 @@ INTERCEPTOR(SSIZE_T, recvmsg, int fd, struct __sanitizer_msghdr *msg, // FIXME: under ASan the call below may write to freed memory and corrupt // its metadata. See // https://github.com/google/sanitizers/issues/321. - SSIZE_T res = REAL(recvmsg)(fd, msg, flags); + SSIZE_T res = COMMON_INTERCEPTOR_BLOCK_REAL(recvmsg)(fd, msg, flags); if (res >= 0) { if (fd >= 0) COMMON_INTERCEPTOR_FD_ACQUIRE(ctx, fd); if (msg) { @@ -3147,7 +3203,8 @@ INTERCEPTOR(int, recvmmsg, int fd, struct __sanitizer_mmsghdr *msgvec, void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, recvmmsg, fd, msgvec, vlen, flags, timeout); if (timeout) COMMON_INTERCEPTOR_READ_RANGE(ctx, timeout, struct_timespec_sz); - int res = REAL(recvmmsg)(fd, msgvec, vlen, flags, timeout); + int res = + COMMON_INTERCEPTOR_BLOCK_REAL(recvmmsg)(fd, msgvec, vlen, flags, timeout); if (res >= 0) { if (fd >= 0) COMMON_INTERCEPTOR_FD_ACQUIRE(ctx, fd); for (int i = 0; i < res; ++i) { @@ -3225,7 +3282,7 @@ INTERCEPTOR(SSIZE_T, sendmsg, int fd, struct __sanitizer_msghdr *msg, COMMON_INTERCEPTOR_FD_ACCESS(ctx, fd); COMMON_INTERCEPTOR_FD_RELEASE(ctx, fd); } - SSIZE_T res = REAL(sendmsg)(fd, msg, flags); + SSIZE_T res = COMMON_INTERCEPTOR_BLOCK_REAL(sendmsg)(fd, msg, flags); if (common_flags()->intercept_send && res >= 0 && msg) read_msghdr(ctx, msg, res); return res; @@ -3244,7 +3301,7 @@ INTERCEPTOR(int, sendmmsg, int fd, struct __sanitizer_mmsghdr *msgvec, COMMON_INTERCEPTOR_FD_ACCESS(ctx, fd); COMMON_INTERCEPTOR_FD_RELEASE(ctx, fd); } - int res = REAL(sendmmsg)(fd, msgvec, vlen, flags); + int res = COMMON_INTERCEPTOR_BLOCK_REAL(sendmmsg)(fd, msgvec, vlen, flags); if (res >= 0 && msgvec) { for (int i = 0; i < res; ++i) { COMMON_INTERCEPTOR_WRITE_RANGE(ctx, &msgvec[i].msg_len, @@ -3267,7 +3324,7 @@ INTERCEPTOR(int, msgsnd, int msqid, const void *msgp, SIZE_T msgsz, COMMON_INTERCEPTOR_ENTER(ctx, msgsnd, msqid, msgp, msgsz, msgflg); if (msgp) COMMON_INTERCEPTOR_READ_RANGE(ctx, msgp, sizeof(long) + msgsz); - int res = REAL(msgsnd)(msqid, msgp, msgsz, msgflg); + int res = COMMON_INTERCEPTOR_BLOCK_REAL(msgsnd)(msqid, msgp, msgsz, msgflg); return res; } @@ -3275,7 +3332,8 @@ INTERCEPTOR(SSIZE_T, msgrcv, int msqid, void *msgp, SIZE_T msgsz, long msgtyp, int msgflg) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, msgrcv, msqid, msgp, msgsz, msgtyp, msgflg); - SSIZE_T len = REAL(msgrcv)(msqid, msgp, msgsz, msgtyp, msgflg); + SSIZE_T len = + COMMON_INTERCEPTOR_BLOCK_REAL(msgrcv)(msqid, msgp, msgsz, msgtyp, msgflg); if (len != -1) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, msgp, sizeof(long) + len); return len; @@ -3416,23 +3474,27 @@ INTERCEPTOR(uptr, ptrace, int request, int pid, void *addr, void *data) { COMMON_INTERCEPTOR_ENTER(ctx, ptrace, request, pid, addr, data); __sanitizer_iovec local_iovec; - if (data) { + void *data_arg = ptrace_data_arg(request, addr, data); + if (data_arg) { if (request == ptrace_setregs) { - COMMON_INTERCEPTOR_READ_RANGE(ctx, data, struct_user_regs_struct_sz); + COMMON_INTERCEPTOR_READ_RANGE(ctx, data_arg, struct_user_regs_struct_sz); } else if (request == ptrace_setfpregs) { - COMMON_INTERCEPTOR_READ_RANGE(ctx, data, struct_user_fpregs_struct_sz); + COMMON_INTERCEPTOR_READ_RANGE(ctx, data_arg, + struct_user_fpregs_struct_sz); } else if (request == ptrace_setfpxregs) { - COMMON_INTERCEPTOR_READ_RANGE(ctx, data, struct_user_fpxregs_struct_sz); + COMMON_INTERCEPTOR_READ_RANGE(ctx, data_arg, + struct_user_fpxregs_struct_sz); } else if (request == ptrace_setvfpregs) { - COMMON_INTERCEPTOR_READ_RANGE(ctx, data, struct_user_vfpregs_struct_sz); + COMMON_INTERCEPTOR_READ_RANGE(ctx, data_arg, + struct_user_vfpregs_struct_sz); } else if (request == ptrace_setsiginfo) { - COMMON_INTERCEPTOR_READ_RANGE(ctx, data, siginfo_t_sz); + COMMON_INTERCEPTOR_READ_RANGE(ctx, data_arg, siginfo_t_sz); - // Some kernel might zero the iovec::iov_base in case of invalid - // write access. In this case copy the invalid address for further - // inspection. + // Some kernel might zero the iovec::iov_base in case of invalid + // write access. In this case copy the invalid address for further + // inspection. } else if (request == ptrace_setregset || request == ptrace_getregset) { - __sanitizer_iovec *iovec = (__sanitizer_iovec*)data; + __sanitizer_iovec *iovec = (__sanitizer_iovec *)data_arg; COMMON_INTERCEPTOR_READ_RANGE(ctx, iovec, sizeof(*iovec)); local_iovec = *iovec; if (request == ptrace_setregset) @@ -3445,23 +3507,26 @@ INTERCEPTOR(uptr, ptrace, int request, int pid, void *addr, void *data) { // https://github.com/google/sanitizers/issues/321. uptr res = REAL(ptrace)(request, pid, addr, data); - if (!res && data) { + if (!res && data_arg) { // Note that PEEK* requests assign different meaning to the return value. // This function does not handle them (nor does it need to). if (request == ptrace_getregs) { - COMMON_INTERCEPTOR_WRITE_RANGE(ctx, data, struct_user_regs_struct_sz); + COMMON_INTERCEPTOR_WRITE_RANGE(ctx, data_arg, struct_user_regs_struct_sz); } else if (request == ptrace_getfpregs) { - COMMON_INTERCEPTOR_WRITE_RANGE(ctx, data, struct_user_fpregs_struct_sz); + COMMON_INTERCEPTOR_WRITE_RANGE(ctx, data_arg, + struct_user_fpregs_struct_sz); } else if (request == ptrace_getfpxregs) { - COMMON_INTERCEPTOR_WRITE_RANGE(ctx, data, struct_user_fpxregs_struct_sz); + COMMON_INTERCEPTOR_WRITE_RANGE(ctx, data_arg, + struct_user_fpxregs_struct_sz); } else if (request == ptrace_getvfpregs) { - COMMON_INTERCEPTOR_WRITE_RANGE(ctx, data, struct_user_vfpregs_struct_sz); + COMMON_INTERCEPTOR_WRITE_RANGE(ctx, data_arg, + struct_user_vfpregs_struct_sz); } else if (request == ptrace_getsiginfo) { - COMMON_INTERCEPTOR_WRITE_RANGE(ctx, data, siginfo_t_sz); + COMMON_INTERCEPTOR_WRITE_RANGE(ctx, data_arg, siginfo_t_sz); } else if (request == ptrace_geteventmsg) { - COMMON_INTERCEPTOR_WRITE_RANGE(ctx, data, sizeof(unsigned long)); + COMMON_INTERCEPTOR_WRITE_RANGE(ctx, data_arg, sizeof(unsigned long)); } else if (request == ptrace_getregset) { - __sanitizer_iovec *iovec = (__sanitizer_iovec*)data; + __sanitizer_iovec *iovec = (__sanitizer_iovec *)data_arg; COMMON_INTERCEPTOR_WRITE_RANGE(ctx, iovec, sizeof(*iovec)); COMMON_INTERCEPTOR_WRITE_RANGE(ctx, local_iovec.iov_base, local_iovec.iov_len); @@ -6119,7 +6184,7 @@ INTERCEPTOR(int, flopen, const char *path, int flags, ...) { if (path) { COMMON_INTERCEPTOR_READ_RANGE(ctx, path, internal_strlen(path) + 1); } - return REAL(flopen)(path, flags, mode); + return COMMON_INTERCEPTOR_BLOCK_REAL(flopen)(path, flags, mode); } INTERCEPTOR(int, flopenat, int dirfd, const char *path, int flags, ...) { @@ -6132,7 +6197,7 @@ INTERCEPTOR(int, flopenat, int dirfd, const char *path, int flags, ...) { if (path) { COMMON_INTERCEPTOR_READ_RANGE(ctx, path, internal_strlen(path) + 1); } - return REAL(flopenat)(dirfd, path, flags, mode); + return COMMON_INTERCEPTOR_BLOCK_REAL(flopenat)(dirfd, path, flags, mode); } #define INIT_FLOPEN \ @@ -6325,9 +6390,9 @@ INTERCEPTOR(void*, dlopen, const char *filename, int flag) { const char *SelfFName = DladdrSelfFName(); VPrintf(1, "dlopen interceptor: DladdrSelfFName: %p %s\n", - (void *)SelfFName, SelfFName); + (const void *)SelfFName, SelfFName); - if (internal_strcmp(SelfFName, filename) == 0) { + if (SelfFName && internal_strcmp(SelfFName, filename) == 0) { // It's possible they copied the string from dladdr, so // we do a string comparison rather than pointer comparison. VPrintf(1, "dlopen interceptor: replacing %s because it matches %s\n", @@ -6717,7 +6782,7 @@ INTERCEPTOR(SSIZE_T, recv, int fd, void *buf, SIZE_T len, int flags) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, recv, fd, buf, len, flags); COMMON_INTERCEPTOR_FD_ACCESS(ctx, fd); - SSIZE_T res = REAL(recv)(fd, buf, len, flags); + SSIZE_T res = COMMON_INTERCEPTOR_BLOCK_REAL(recv)(fd, buf, len, flags); if (res > 0) { COMMON_INTERCEPTOR_WRITE_RANGE(ctx, buf, Min((SIZE_T)res, len)); } @@ -6734,7 +6799,8 @@ INTERCEPTOR(SSIZE_T, recvfrom, int fd, void *buf, SIZE_T len, int flags, SIZE_T srcaddr_sz; if (srcaddr) srcaddr_sz = *addrlen; (void)srcaddr_sz; // prevent "set but not used" warning - SSIZE_T res = REAL(recvfrom)(fd, buf, len, flags, srcaddr, addrlen); + SSIZE_T res = COMMON_INTERCEPTOR_BLOCK_REAL(recvfrom)(fd, buf, len, flags, + srcaddr, addrlen); if (res > 0) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, buf, Min((SIZE_T)res, len)); if (res >= 0 && srcaddr) @@ -6757,7 +6823,7 @@ INTERCEPTOR(SSIZE_T, send, int fd, void *buf, SIZE_T len, int flags) { COMMON_INTERCEPTOR_FD_ACCESS(ctx, fd); COMMON_INTERCEPTOR_FD_RELEASE(ctx, fd); } - SSIZE_T res = REAL(send)(fd, buf, len, flags); + SSIZE_T res = COMMON_INTERCEPTOR_BLOCK_REAL(send)(fd, buf, len, flags); if (common_flags()->intercept_send && res > 0) COMMON_INTERCEPTOR_READ_RANGE(ctx, buf, Min((SIZE_T)res, len)); return res; @@ -6772,7 +6838,8 @@ INTERCEPTOR(SSIZE_T, sendto, int fd, void *buf, SIZE_T len, int flags, COMMON_INTERCEPTOR_FD_RELEASE(ctx, fd); } // Can't check dstaddr as it may have uninitialized padding at the end. - SSIZE_T res = REAL(sendto)(fd, buf, len, flags, dstaddr, addrlen); + SSIZE_T res = COMMON_INTERCEPTOR_BLOCK_REAL(sendto)(fd, buf, len, flags, + dstaddr, addrlen); if (common_flags()->intercept_send && res > 0) COMMON_INTERCEPTOR_READ_RANGE(ctx, buf, Min((SIZE_T)res, len)); return res; @@ -6785,25 +6852,25 @@ INTERCEPTOR(SSIZE_T, sendto, int fd, void *buf, SIZE_T len, int flags, #endif #if SANITIZER_INTERCEPT_EVENTFD_READ_WRITE -INTERCEPTOR(int, eventfd_read, int fd, u64 *value) { +INTERCEPTOR(int, eventfd_read, int fd, __sanitizer_eventfd_t *value) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, eventfd_read, fd, value); COMMON_INTERCEPTOR_FD_ACCESS(ctx, fd); - int res = REAL(eventfd_read)(fd, value); + int res = COMMON_INTERCEPTOR_BLOCK_REAL(eventfd_read)(fd, value); if (res == 0) { COMMON_INTERCEPTOR_WRITE_RANGE(ctx, value, sizeof(*value)); if (fd >= 0) COMMON_INTERCEPTOR_FD_ACQUIRE(ctx, fd); } return res; } -INTERCEPTOR(int, eventfd_write, int fd, u64 value) { +INTERCEPTOR(int, eventfd_write, int fd, __sanitizer_eventfd_t value) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, eventfd_write, fd, value); if (fd >= 0) { COMMON_INTERCEPTOR_FD_ACCESS(ctx, fd); COMMON_INTERCEPTOR_FD_RELEASE(ctx, fd); } - int res = REAL(eventfd_write)(fd, value); + int res = COMMON_INTERCEPTOR_BLOCK_REAL(eventfd_write)(fd, value); return res; } #define INIT_EVENTFD_READ_WRITE \ @@ -7426,7 +7493,8 @@ INTERCEPTOR(int, open_by_handle_at, int mount_fd, struct file_handle* handle, COMMON_INTERCEPTOR_READ_RANGE( ctx, &sanitizer_handle->f_handle, sanitizer_handle->handle_bytes); - return REAL(open_by_handle_at)(mount_fd, handle, flags); + return COMMON_INTERCEPTOR_BLOCK_REAL(open_by_handle_at)(mount_fd, handle, + flags); } #define INIT_OPEN_BY_HANDLE_AT COMMON_INTERCEPT_FUNCTION(open_by_handle_at) @@ -7641,9 +7709,9 @@ static void write_protoent(void *ctx, struct __sanitizer_protoent *p) { COMMON_INTERCEPTOR_WRITE_RANGE(ctx, p->p_aliases, pp_size * sizeof(char *)); } -INTERCEPTOR(struct __sanitizer_protoent *, getprotoent) { +INTERCEPTOR(struct __sanitizer_protoent *, getprotoent,) { void *ctx; - COMMON_INTERCEPTOR_ENTER(ctx, getprotoent); + COMMON_INTERCEPTOR_ENTER(ctx, getprotoent,); struct __sanitizer_protoent *p = REAL(getprotoent)(); if (p) write_protoent(ctx, p); @@ -7730,9 +7798,9 @@ INTERCEPTOR(int, getprotobynumber_r, int num, #endif #if SANITIZER_INTERCEPT_NETENT -INTERCEPTOR(struct __sanitizer_netent *, getnetent) { +INTERCEPTOR(struct __sanitizer_netent *, getnetent,) { void *ctx; - COMMON_INTERCEPTOR_ENTER(ctx, getnetent); + COMMON_INTERCEPTOR_ENTER(ctx, getnetent,); struct __sanitizer_netent *n = REAL(getnetent)(); if (n) { COMMON_INTERCEPTOR_WRITE_RANGE(ctx, n, sizeof(*n)); @@ -8809,83 +8877,6 @@ INTERCEPTOR(char *, RMD160Data, u8 *data, SIZE_T len, char *buf) { #define INIT_RMD160 #endif -#if SANITIZER_INTERCEPT_MD5 -INTERCEPTOR(void, MD5Init, void *context) { - void *ctx; - COMMON_INTERCEPTOR_ENTER(ctx, MD5Init, context); - REAL(MD5Init)(context); - if (context) - COMMON_INTERCEPTOR_WRITE_RANGE(ctx, context, MD5_CTX_sz); -} - -INTERCEPTOR(void, MD5Update, void *context, const unsigned char *data, - unsigned int len) { - void *ctx; - COMMON_INTERCEPTOR_ENTER(ctx, MD5Update, context, data, len); - if (data && len > 0) - COMMON_INTERCEPTOR_READ_RANGE(ctx, data, len); - if (context) - COMMON_INTERCEPTOR_READ_RANGE(ctx, context, MD5_CTX_sz); - REAL(MD5Update)(context, data, len); - if (context) - COMMON_INTERCEPTOR_WRITE_RANGE(ctx, context, MD5_CTX_sz); -} - -INTERCEPTOR(void, MD5Final, unsigned char digest[16], void *context) { - void *ctx; - COMMON_INTERCEPTOR_ENTER(ctx, MD5Final, digest, context); - if (context) - COMMON_INTERCEPTOR_READ_RANGE(ctx, context, MD5_CTX_sz); - REAL(MD5Final)(digest, context); - if (digest) - COMMON_INTERCEPTOR_WRITE_RANGE(ctx, digest, sizeof(unsigned char) * 16); -} - -INTERCEPTOR(char *, MD5End, void *context, char *buf) { - void *ctx; - COMMON_INTERCEPTOR_ENTER(ctx, MD5End, context, buf); - if (context) - COMMON_INTERCEPTOR_READ_RANGE(ctx, context, MD5_CTX_sz); - char *ret = REAL(MD5End)(context, buf); - if (ret) - COMMON_INTERCEPTOR_WRITE_RANGE(ctx, ret, MD5_return_length); - return ret; -} - -INTERCEPTOR(char *, MD5File, const char *filename, char *buf) { - void *ctx; - COMMON_INTERCEPTOR_ENTER(ctx, MD5File, filename, buf); - if (filename) - COMMON_INTERCEPTOR_READ_RANGE(ctx, filename, internal_strlen(filename) + 1); - char *ret = REAL(MD5File)(filename, buf); - if (ret) - COMMON_INTERCEPTOR_WRITE_RANGE(ctx, ret, MD5_return_length); - return ret; -} - -INTERCEPTOR(char *, MD5Data, const unsigned char *data, unsigned int len, - char *buf) { - void *ctx; - COMMON_INTERCEPTOR_ENTER(ctx, MD5Data, data, len, buf); - if (data && len > 0) - COMMON_INTERCEPTOR_READ_RANGE(ctx, data, len); - char *ret = REAL(MD5Data)(data, len, buf); - if (ret) - COMMON_INTERCEPTOR_WRITE_RANGE(ctx, ret, MD5_return_length); - return ret; -} - -#define INIT_MD5 \ - COMMON_INTERCEPT_FUNCTION(MD5Init); \ - COMMON_INTERCEPT_FUNCTION(MD5Update); \ - COMMON_INTERCEPT_FUNCTION(MD5Final); \ - COMMON_INTERCEPT_FUNCTION(MD5End); \ - COMMON_INTERCEPT_FUNCTION(MD5File); \ - COMMON_INTERCEPT_FUNCTION(MD5Data) -#else -#define INIT_MD5 -#endif - #if SANITIZER_INTERCEPT_FSEEK INTERCEPTOR(int, fseek, __sanitizer_FILE *stream, long int offset, int whence) { void *ctx; @@ -9016,107 +9007,6 @@ INTERCEPTOR(char *, MD2Data, const unsigned char *data, unsigned int len, #define INIT_MD2 #endif -#if SANITIZER_INTERCEPT_SHA2 -#define SHA2_INTERCEPTORS(LEN, SHA2_STATE_T) \ - INTERCEPTOR(void, SHA##LEN##_Init, void *context) { \ - void *ctx; \ - COMMON_INTERCEPTOR_ENTER(ctx, SHA##LEN##_Init, context); \ - REAL(SHA##LEN##_Init)(context); \ - if (context) \ - COMMON_INTERCEPTOR_WRITE_RANGE(ctx, context, SHA##LEN##_CTX_sz); \ - } \ - INTERCEPTOR(void, SHA##LEN##_Update, void *context, \ - const u8 *data, SIZE_T len) { \ - void *ctx; \ - COMMON_INTERCEPTOR_ENTER(ctx, SHA##LEN##_Update, context, data, len); \ - if (data && len > 0) \ - COMMON_INTERCEPTOR_READ_RANGE(ctx, data, len); \ - if (context) \ - COMMON_INTERCEPTOR_READ_RANGE(ctx, context, SHA##LEN##_CTX_sz); \ - REAL(SHA##LEN##_Update)(context, data, len); \ - if (context) \ - COMMON_INTERCEPTOR_WRITE_RANGE(ctx, context, SHA##LEN##_CTX_sz); \ - } \ - INTERCEPTOR(void, SHA##LEN##_Final, u8 digest[LEN/8], \ - void *context) { \ - void *ctx; \ - CHECK_EQ(SHA##LEN##_digest_length, LEN/8); \ - COMMON_INTERCEPTOR_ENTER(ctx, SHA##LEN##_Final, digest, context); \ - if (context) \ - COMMON_INTERCEPTOR_READ_RANGE(ctx, context, SHA##LEN##_CTX_sz); \ - REAL(SHA##LEN##_Final)(digest, context); \ - if (digest) \ - COMMON_INTERCEPTOR_WRITE_RANGE(ctx, digest, \ - sizeof(digest[0]) * \ - SHA##LEN##_digest_length); \ - } \ - INTERCEPTOR(char *, SHA##LEN##_End, void *context, char *buf) { \ - void *ctx; \ - COMMON_INTERCEPTOR_ENTER(ctx, SHA##LEN##_End, context, buf); \ - if (context) \ - COMMON_INTERCEPTOR_READ_RANGE(ctx, context, SHA##LEN##_CTX_sz); \ - char *ret = REAL(SHA##LEN##_End)(context, buf); \ - if (ret) \ - COMMON_INTERCEPTOR_WRITE_RANGE(ctx, ret, SHA##LEN##_return_length); \ - return ret; \ - } \ - INTERCEPTOR(char *, SHA##LEN##_File, const char *filename, char *buf) { \ - void *ctx; \ - COMMON_INTERCEPTOR_ENTER(ctx, SHA##LEN##_File, filename, buf); \ - if (filename) \ - COMMON_INTERCEPTOR_READ_RANGE(ctx, filename, internal_strlen(filename) + 1);\ - char *ret = REAL(SHA##LEN##_File)(filename, buf); \ - if (ret) \ - COMMON_INTERCEPTOR_WRITE_RANGE(ctx, ret, SHA##LEN##_return_length); \ - return ret; \ - } \ - INTERCEPTOR(char *, SHA##LEN##_FileChunk, const char *filename, char *buf, \ - OFF_T offset, OFF_T length) { \ - void *ctx; \ - COMMON_INTERCEPTOR_ENTER(ctx, SHA##LEN##_FileChunk, filename, buf, offset, \ - length); \ - if (filename) \ - COMMON_INTERCEPTOR_READ_RANGE(ctx, filename, internal_strlen(filename) + 1);\ - char *ret = REAL(SHA##LEN##_FileChunk)(filename, buf, offset, length); \ - if (ret) \ - COMMON_INTERCEPTOR_WRITE_RANGE(ctx, ret, SHA##LEN##_return_length); \ - return ret; \ - } \ - INTERCEPTOR(char *, SHA##LEN##_Data, u8 *data, SIZE_T len, char *buf) { \ - void *ctx; \ - COMMON_INTERCEPTOR_ENTER(ctx, SHA##LEN##_Data, data, len, buf); \ - if (data && len > 0) \ - COMMON_INTERCEPTOR_READ_RANGE(ctx, data, len); \ - char *ret = REAL(SHA##LEN##_Data)(data, len, buf); \ - if (ret) \ - COMMON_INTERCEPTOR_WRITE_RANGE(ctx, ret, SHA##LEN##_return_length); \ - return ret; \ - } - -SHA2_INTERCEPTORS(224, u32) -SHA2_INTERCEPTORS(256, u32) -SHA2_INTERCEPTORS(384, u64) -SHA2_INTERCEPTORS(512, u64) - -#define INIT_SHA2_INTECEPTORS(LEN) \ - COMMON_INTERCEPT_FUNCTION(SHA##LEN##_Init); \ - COMMON_INTERCEPT_FUNCTION(SHA##LEN##_Update); \ - COMMON_INTERCEPT_FUNCTION(SHA##LEN##_Final); \ - COMMON_INTERCEPT_FUNCTION(SHA##LEN##_End); \ - COMMON_INTERCEPT_FUNCTION(SHA##LEN##_File); \ - COMMON_INTERCEPT_FUNCTION(SHA##LEN##_FileChunk); \ - COMMON_INTERCEPT_FUNCTION(SHA##LEN##_Data) - -#define INIT_SHA2 \ - INIT_SHA2_INTECEPTORS(224); \ - INIT_SHA2_INTECEPTORS(256); \ - INIT_SHA2_INTECEPTORS(384); \ - INIT_SHA2_INTECEPTORS(512) -#undef SHA2_INTERCEPTORS -#else -#define INIT_SHA2 -#endif - #if SANITIZER_INTERCEPT_VIS INTERCEPTOR(char *, vis, char *dst, int c, int flag, int nextc) { void *ctx; @@ -9894,9 +9784,9 @@ INTERCEPTOR(char *, fdevname_r, int fd, char *buf, SIZE_T len) { #endif #if SANITIZER_INTERCEPT_GETUSERSHELL -INTERCEPTOR(char *, getusershell) { +INTERCEPTOR(char *, getusershell,) { void *ctx; - COMMON_INTERCEPTOR_ENTER(ctx, getusershell); + COMMON_INTERCEPTOR_ENTER(ctx, getusershell,); char *res = REAL(getusershell)(); if (res) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, res, internal_strlen(res) + 1); @@ -9965,7 +9855,13 @@ INTERCEPTOR(void, sl_free, void *sl, int freeall) { INTERCEPTOR(SSIZE_T, getrandom, void *buf, SIZE_T buflen, unsigned int flags) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, getrandom, buf, buflen, flags); - SSIZE_T n = REAL(getrandom)(buf, buflen, flags); + // If GRND_NONBLOCK is set in the flags, it is non blocking. + static const int grnd_nonblock = 1; + SSIZE_T n; + if ((flags & grnd_nonblock)) + n = REAL(getrandom)(buf, buflen, flags); + else + n = COMMON_INTERCEPTOR_BLOCK_REAL(getrandom)(buf, buflen, flags); if (n > 0) { COMMON_INTERCEPTOR_WRITE_RANGE(ctx, buf, n); } @@ -10212,20 +10108,6 @@ INTERCEPTOR(int, __xuname, int size, void *utsname) { #define INIT___XUNAME #endif -#if SANITIZER_INTERCEPT_HEXDUMP -INTERCEPTOR(void, hexdump, const void *ptr, int length, const char *header, int flags) { - void *ctx; - COMMON_INTERCEPTOR_ENTER(ctx, hexdump, ptr, length, header, flags); - COMMON_INTERCEPTOR_READ_RANGE(ctx, ptr, length); - COMMON_INTERCEPTOR_READ_RANGE(ctx, header, internal_strlen(header) + 1); - REAL(hexdump)(ptr, length, header, flags); -} - -#define INIT_HEXDUMP COMMON_INTERCEPT_FUNCTION(hexdump); -#else -#define INIT_HEXDUMP -#endif - #if SANITIZER_INTERCEPT_ARGP_PARSE INTERCEPTOR(int, argp_parse, const struct argp *argp, int argc, char **argv, unsigned flags, int *arg_index, void *input) { @@ -10258,6 +10140,55 @@ INTERCEPTOR(int, cpuset_getaffinity, int level, int which, __int64_t id, SIZE_T #define INIT_CPUSET_GETAFFINITY #endif +#if SANITIZER_INTERCEPT_PREADV2 +INTERCEPTOR(SSIZE_T, preadv2, int fd, __sanitizer_iovec *iov, int iovcnt, + OFF_T offset, int flags) { + void *ctx; + COMMON_INTERCEPTOR_ENTER(ctx, preadv2, fd, iov, iovcnt, offset, flags); + COMMON_INTERCEPTOR_FD_ACCESS(ctx, fd); + SSIZE_T res = REAL(preadv2)(fd, iov, iovcnt, offset, flags); + if (res > 0) write_iovec(ctx, iov, iovcnt, res); + if (res >= 0 && fd >= 0) COMMON_INTERCEPTOR_FD_ACQUIRE(ctx, fd); + return res; +} +#define INIT_PREADV2 COMMON_INTERCEPT_FUNCTION(preadv2) +#else +#define INIT_PREADV2 +#endif + +#if SANITIZER_INTERCEPT_PWRITEV2 +INTERCEPTOR(SSIZE_T, pwritev2, int fd, __sanitizer_iovec *iov, int iovcnt, + OFF_T offset, int flags) { + void *ctx; + COMMON_INTERCEPTOR_ENTER(ctx, pwritev2, fd, iov, iovcnt, offset, flags); + COMMON_INTERCEPTOR_FD_ACCESS(ctx, fd); + if (fd >= 0) COMMON_INTERCEPTOR_FD_RELEASE(ctx, fd); + SSIZE_T res = REAL(pwritev2)(fd, iov, iovcnt, offset, flags); + if (res > 0) read_iovec(ctx, iov, iovcnt, res); + return res; +} +#define INIT_PWRITEV2 COMMON_INTERCEPT_FUNCTION(pwritev2) +#else +#define INIT_PWRITEV2 +#endif + +#if SANITIZER_INTERCEPT_FREADLINK +INTERCEPTOR(SSIZE_T, freadlink, int fd, char *buf, SIZE_T bufsiz) { + void *ctx; + COMMON_INTERCEPTOR_ENTER(ctx, freadlink, fd, buf, bufsiz); + COMMON_INTERCEPTOR_FD_ACCESS(ctx, fd); + SSIZE_T res = REAL(freadlink)(fd, buf, bufsiz); + if (res > 0) + COMMON_INTERCEPTOR_WRITE_RANGE(ctx, buf, res); + if (res >= 0 && fd > 0) + COMMON_INTERCEPTOR_FD_ACQUIRE(ctx, fd); + return res; +} +# define INIT_FREADLINK COMMON_INTERCEPT_FUNCTION(freadlink) +#else +# define INIT_FREADLINK +#endif + #include "sanitizer_common_interceptors_netbsd_compat.inc" namespace __sanitizer { @@ -10550,10 +10481,8 @@ static void InitializeCommonInterceptors() { INIT_SHA1; INIT_MD4; INIT_RMD160; - INIT_MD5; INIT_FSEEK; INIT_MD2; - INIT_SHA2; INIT_VIS; INIT_CDB; INIT_GETFSENT; @@ -10575,9 +10504,11 @@ static void InitializeCommonInterceptors() { INIT_PROCCTL INIT_UNAME; INIT___XUNAME; - INIT_HEXDUMP; INIT_ARGP_PARSE; INIT_CPUSET_GETAFFINITY; + INIT_PREADV2; + INIT_PWRITEV2; + INIT_FREADLINK; INIT___PRINTF_CHK; } diff --git a/libsanitizer/sanitizer_common/sanitizer_common_interface.inc b/libsanitizer/sanitizer_common/sanitizer_common_interface.inc index 557207f..4ea75cd 100644 --- a/libsanitizer/sanitizer_common/sanitizer_common_interface.inc +++ b/libsanitizer/sanitizer_common/sanitizer_common_interface.inc @@ -10,6 +10,7 @@ INTERFACE_FUNCTION(__sanitizer_acquire_crash_state) INTERFACE_FUNCTION(__sanitizer_annotate_contiguous_container) INTERFACE_FUNCTION(__sanitizer_annotate_double_ended_contiguous_container) +INTERFACE_FUNCTION(__sanitizer_copy_contiguous_container_annotations) INTERFACE_FUNCTION(__sanitizer_contiguous_container_find_bad_address) INTERFACE_FUNCTION( __sanitizer_double_ended_contiguous_container_find_bad_address) @@ -22,6 +23,7 @@ INTERFACE_FUNCTION(__sanitizer_verify_double_ended_contiguous_container) INTERFACE_WEAK_FUNCTION(__sanitizer_on_print) INTERFACE_WEAK_FUNCTION(__sanitizer_report_error_summary) INTERFACE_WEAK_FUNCTION(__sanitizer_sandbox_on_notify) +INTERFACE_WEAK_FUNCTION(__sanitizer_get_dtls_size) // Sanitizer weak hooks INTERFACE_WEAK_FUNCTION(__sanitizer_weak_hook_memcmp) INTERFACE_WEAK_FUNCTION(__sanitizer_weak_hook_strcmp) @@ -46,7 +48,14 @@ INTERFACE_FUNCTION(__sanitizer_purge_allocator) INTERFACE_FUNCTION(__sanitizer_print_memory_profile) INTERFACE_WEAK_FUNCTION(__sanitizer_free_hook) INTERFACE_WEAK_FUNCTION(__sanitizer_malloc_hook) +INTERFACE_WEAK_FUNCTION(__sanitizer_ignore_free_hook) // Memintrinsic functions. INTERFACE_FUNCTION(__sanitizer_internal_memcpy) INTERFACE_FUNCTION(__sanitizer_internal_memmove) INTERFACE_FUNCTION(__sanitizer_internal_memset) + +#if SANITIZER_WINDOWS +INTERFACE_FUNCTION(__sanitizer_override_function) +INTERFACE_FUNCTION(__sanitizer_override_function_by_addr) +INTERFACE_FUNCTION(__sanitizer_register_weak_function) +#endif diff --git a/libsanitizer/sanitizer_common/sanitizer_common_libcdep.cpp b/libsanitizer/sanitizer_common/sanitizer_common_libcdep.cpp index 7b74bb1..f275e81 100644 --- a/libsanitizer/sanitizer_common/sanitizer_common_libcdep.cpp +++ b/libsanitizer/sanitizer_common/sanitizer_common_libcdep.cpp @@ -87,8 +87,8 @@ void MaybeStartBackgroudThread() { if (!common_flags()->hard_rss_limit_mb && !common_flags()->soft_rss_limit_mb && !common_flags()->heap_profile) return; - if (!&real_pthread_create) { - VPrintf(1, "%s: real_pthread_create undefined\n", SanitizerToolName); + if (!&internal_pthread_create) { + VPrintf(1, "%s: internal_pthread_create undefined\n", SanitizerToolName); return; // Can't spawn the thread anyway. } @@ -169,9 +169,9 @@ void ReserveShadowMemoryRange(uptr beg, uptr end, const char *name, : !MmapFixedNoReserve(beg, size, name)) { Report( "ReserveShadowMemoryRange failed while trying to map 0x%zx bytes. " - "Perhaps you're using ulimit -v\n", + "Perhaps you're using ulimit -v or ulimit -d\n", size); - Abort(); + Die(); } if (madvise_shadow && common_flags()->use_madv_dontdump) DontDumpShadowMemory(beg, size); @@ -219,6 +219,32 @@ static void StopStackDepotBackgroundThread() { static void StopStackDepotBackgroundThread() {} #endif +void MemCpyAccessible(void *dest, const void *src, uptr n) { + if (TryMemCpy(dest, src, n)) + return; + + const uptr page_size = GetPageSize(); + uptr b = reinterpret_cast<uptr>(src); + uptr b_up = RoundUpTo(b, page_size); + + uptr e = reinterpret_cast<uptr>(src) + n; + uptr e_down = RoundDownTo(e, page_size); + + auto copy_or_zero = [dest, src](uptr beg, uptr end) { + const uptr udest = reinterpret_cast<uptr>(dest); + const uptr usrc = reinterpret_cast<uptr>(src); + void *d = reinterpret_cast<void *>(udest + (beg - usrc)); + const uptr size = end - beg; + if (!TryMemCpy(d, reinterpret_cast<void *>(beg), size)) + internal_memset(d, 0, size); + }; + + copy_or_zero(b, b_up); + for (uptr p = b_up; p < e_down; p += page_size) + copy_or_zero(p, p + page_size); + copy_or_zero(e_down, e); +} + } // namespace __sanitizer SANITIZER_INTERFACE_WEAK_DEF(void, __sanitizer_sandbox_on_notify, diff --git a/libsanitizer/sanitizer_common/sanitizer_common_nolibc.cpp b/libsanitizer/sanitizer_common/sanitizer_common_nolibc.cpp index 67e77a8..e49285f 100644 --- a/libsanitizer/sanitizer_common/sanitizer_common_nolibc.cpp +++ b/libsanitizer/sanitizer_common/sanitizer_common_nolibc.cpp @@ -20,13 +20,14 @@ namespace __sanitizer { // The Windows implementations of these functions use the win32 API directly, // bypassing libc. #if !SANITIZER_WINDOWS -#if SANITIZER_LINUX +# if SANITIZER_LINUX void LogMessageOnPrintf(const char *str) {} -#endif +void InitTlsSize() {} +# endif void WriteToSyslog(const char *buffer) {} void Abort() { internal__exit(1); } bool CreateDir(const char *pathname) { return false; } -#endif // !SANITIZER_WINDOWS +#endif // !SANITIZER_WINDOWS #if !SANITIZER_WINDOWS && !SANITIZER_APPLE void ListOfModules::init() {} diff --git a/libsanitizer/sanitizer_common/sanitizer_common_syscalls.inc b/libsanitizer/sanitizer_common/sanitizer_common_syscalls.inc index c10943b..29fe472 100644 --- a/libsanitizer/sanitizer_common/sanitizer_common_syscalls.inc +++ b/libsanitizer/sanitizer_common/sanitizer_common_syscalls.inc @@ -38,12 +38,17 @@ // Called before fork syscall. // COMMON_SYSCALL_POST_FORK(long res) // Called after fork syscall. +// COMMON_SYSCALL_BLOCKING_START() +// Called before blocking syscall. +// COMMON_SYSCALL_BLOCKING_END() +// Called after blocking syscall. //===----------------------------------------------------------------------===// #include "sanitizer_platform.h" #if SANITIZER_LINUX # include "sanitizer_libc.h" +# include "sanitizer_platform_limits_posix.h" # define PRE_SYSCALL(name) \ SANITIZER_INTERFACE_ATTRIBUTE void __sanitizer_syscall_pre_impl_##name @@ -85,6 +90,16 @@ {} # endif +# ifndef COMMON_SYSCALL_BLOCKING_START +# define COMMON_SYSCALL_BLOCKING_START() \ + {} +# endif + +# ifndef COMMON_SYSCALL_BLOCKING_END +# define COMMON_SYSCALL_BLOCKING_END() \ + {} +# endif + // FIXME: do some kind of PRE_READ for all syscall arguments (int(s) and such). extern "C" { @@ -2516,18 +2531,19 @@ PRE_SYSCALL(ptrace)(long request, long pid, long addr, long data) { # if !SANITIZER_ANDROID && \ (defined(__i386) || defined(__x86_64) || defined(__mips64) || \ defined(__powerpc64__) || defined(__aarch64__) || defined(__s390__) || \ - defined(__loongarch__) || SANITIZER_RISCV64) - if (data) { + defined(__loongarch__) || SANITIZER_RISCV64 || defined(__sparc__)) + long data_arg = ptrace_data_arg(request, addr, data); + if (data_arg) { if (request == ptrace_setregs) { - PRE_READ((void *)data, struct_user_regs_struct_sz); + PRE_READ((void *)data_arg, struct_user_regs_struct_sz); } else if (request == ptrace_setfpregs) { - PRE_READ((void *)data, struct_user_fpregs_struct_sz); + PRE_READ((void *)data_arg, struct_user_fpregs_struct_sz); } else if (request == ptrace_setfpxregs) { - PRE_READ((void *)data, struct_user_fpxregs_struct_sz); + PRE_READ((void *)data_arg, struct_user_fpxregs_struct_sz); } else if (request == ptrace_setsiginfo) { - PRE_READ((void *)data, siginfo_t_sz); + PRE_READ((void *)data_arg, siginfo_t_sz); } else if (request == ptrace_setregset) { - __sanitizer_iovec *iov = (__sanitizer_iovec *)data; + __sanitizer_iovec *iov = (__sanitizer_iovec *)data_arg; PRE_READ(iov->iov_base, iov->iov_len); } } @@ -2538,25 +2554,26 @@ POST_SYSCALL(ptrace)(long res, long request, long pid, long addr, long data) { # if !SANITIZER_ANDROID && \ (defined(__i386) || defined(__x86_64) || defined(__mips64) || \ defined(__powerpc64__) || defined(__aarch64__) || defined(__s390__) || \ - defined(__loongarch__) || SANITIZER_RISCV64) - if (res >= 0 && data) { + defined(__loongarch__) || SANITIZER_RISCV64 || defined(__sparc__)) + long data_arg = ptrace_data_arg(request, addr, data); + if (res >= 0 && data_arg) { // Note that this is different from the interceptor in // sanitizer_common_interceptors.inc. // PEEK* requests return resulting values through data pointer. if (request == ptrace_getregs) { - POST_WRITE((void *)data, struct_user_regs_struct_sz); + POST_WRITE((void *)data_arg, struct_user_regs_struct_sz); } else if (request == ptrace_getfpregs) { - POST_WRITE((void *)data, struct_user_fpregs_struct_sz); + POST_WRITE((void *)data_arg, struct_user_fpregs_struct_sz); } else if (request == ptrace_getfpxregs) { - POST_WRITE((void *)data, struct_user_fpxregs_struct_sz); + POST_WRITE((void *)data_arg, struct_user_fpxregs_struct_sz); } else if (request == ptrace_getsiginfo) { - POST_WRITE((void *)data, siginfo_t_sz); + POST_WRITE((void *)data_arg, siginfo_t_sz); } else if (request == ptrace_getregset) { - __sanitizer_iovec *iov = (__sanitizer_iovec *)data; + __sanitizer_iovec *iov = (__sanitizer_iovec *)data_arg; POST_WRITE(iov->iov_base, iov->iov_len); } else if (request == ptrace_peekdata || request == ptrace_peektext || request == ptrace_peekuser) { - POST_WRITE((void *)data, sizeof(void *)); + POST_WRITE((void *)data_arg, sizeof(void *)); } } # endif @@ -2808,6 +2825,15 @@ PRE_SYSCALL(fchownat) POST_SYSCALL(fchownat) (long res, long dfd, const void *filename, long user, long group, long flag) {} +PRE_SYSCALL(fchmodat2)(long dfd, const void *filename, long mode, long flag) { + if (filename) + PRE_READ(filename, + __sanitizer::internal_strlen((const char *)filename) + 1); +} + +POST_SYSCALL(fchmodat2) +(long res, long dfd, const void *filename, long mode, long flag) {} + PRE_SYSCALL(openat)(long dfd, const void *filename, long flags, long mode) { if (filename) PRE_READ(filename, @@ -3167,6 +3193,18 @@ POST_SYSCALL(sigaltstack)(long res, void *ss, void *oss) { } } } + +PRE_SYSCALL(futex) +(void *uaddr, long futex_op, long val, void *timeout, void *uaddr2, long val3) { + COMMON_SYSCALL_BLOCKING_START(); +} + +POST_SYSCALL(futex) +(long res, void *uaddr, long futex_op, long val, void *timeout, void *uaddr2, + long val3) { + COMMON_SYSCALL_BLOCKING_END(); +} + } // extern "C" # undef PRE_SYSCALL diff --git a/libsanitizer/sanitizer_common/sanitizer_coverage_fuchsia.cpp b/libsanitizer/sanitizer_common/sanitizer_coverage_fuchsia.cpp index 35c3253..73668a5 100644 --- a/libsanitizer/sanitizer_common/sanitizer_coverage_fuchsia.cpp +++ b/libsanitizer/sanitizer_common/sanitizer_coverage_fuchsia.cpp @@ -35,7 +35,7 @@ #include "sanitizer_common.h" #include "sanitizer_interface_internal.h" #include "sanitizer_internal_defs.h" -#include "sanitizer_symbolizer_fuchsia.h" +# include "sanitizer_symbolizer_markup_constants.h" using namespace __sanitizer; diff --git a/libsanitizer/sanitizer_common/sanitizer_coverage_libcdep_new.cpp b/libsanitizer/sanitizer_common/sanitizer_coverage_libcdep_new.cpp index ce43269..506659a 100644 --- a/libsanitizer/sanitizer_common/sanitizer_coverage_libcdep_new.cpp +++ b/libsanitizer/sanitizer_common/sanitizer_coverage_libcdep_new.cpp @@ -75,7 +75,8 @@ static void SanitizerDumpCoverage(const uptr* unsorted_pcs, uptr len) { if (!pc) continue; if (!GetModuleAndOffsetForPc(pc, nullptr, 0, &pcs[i])) { - Printf("ERROR: unknown pc 0x%zx (may happen if dlclose is used)\n", pc); + Printf("ERROR: unknown pc %p (may happen if dlclose is used)\n", + (void*)pc); continue; } uptr module_base = pc - pcs[i]; diff --git a/libsanitizer/sanitizer_common/sanitizer_coverage_win_dll_thunk.cpp b/libsanitizer/sanitizer_common/sanitizer_coverage_win_dll_thunk.cpp deleted file mode 100644 index d0bf8a4..0000000 --- a/libsanitizer/sanitizer_common/sanitizer_coverage_win_dll_thunk.cpp +++ /dev/null @@ -1,20 +0,0 @@ -//===-- sanitizer_coverage_win_dll_thunk.cpp ------------------------------===// -// -// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. -// See https://llvm.org/LICENSE.txt for license information. -// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception -// -//===----------------------------------------------------------------------===// -// -// This file defines a family of thunks that should be statically linked into -// the DLLs that have instrumentation in order to delegate the calls to the -// shared runtime that lives in the main binary. -// See https://github.com/google/sanitizers/issues/209 for the details. -//===----------------------------------------------------------------------===// -#ifdef SANITIZER_DLL_THUNK -#include "sanitizer_win_dll_thunk.h" -// Sanitizer Coverage interface functions. -#define INTERFACE_FUNCTION(Name) INTERCEPT_SANITIZER_FUNCTION(Name) -#define INTERFACE_WEAK_FUNCTION(Name) INTERCEPT_SANITIZER_WEAK_FUNCTION(Name) -#include "sanitizer_coverage_interface.inc" -#endif // SANITIZER_DLL_THUNK diff --git a/libsanitizer/sanitizer_common/sanitizer_coverage_win_dynamic_runtime_thunk.cpp b/libsanitizer/sanitizer_common/sanitizer_coverage_win_runtime_thunk.cpp index 0bdf0c5..2819446 100644 --- a/libsanitizer/sanitizer_common/sanitizer_coverage_win_dynamic_runtime_thunk.cpp +++ b/libsanitizer/sanitizer_common/sanitizer_coverage_win_runtime_thunk.cpp @@ -1,4 +1,4 @@ -//===-- sanitizer_coverage_win_dynamic_runtime_thunk.cpp ------------------===// +//===-- sanitizer_coverage_win_runtime_thunk.cpp --------------------------===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. @@ -10,17 +10,20 @@ // to interact with Sanitizer Coverage, when it is included in a dll. // //===----------------------------------------------------------------------===// -#ifdef SANITIZER_DYNAMIC_RUNTIME_THUNK -#define SANITIZER_IMPORT_INTERFACE 1 -#include "sanitizer_win_defs.h" +#if defined(SANITIZER_DYNAMIC_RUNTIME_THUNK) || \ + defined(SANITIZER_STATIC_RUNTIME_THUNK) +# define SANITIZER_IMPORT_INTERFACE 1 +# include "sanitizer_win_defs.h" +# include "sanitizer_win_thunk_interception.h" // Define weak alias for all weak functions imported from sanitizer coverage. -#define INTERFACE_FUNCTION(Name) -#define INTERFACE_WEAK_FUNCTION(Name) WIN_WEAK_IMPORT_DEF(Name) -#include "sanitizer_coverage_interface.inc" -#endif // SANITIZER_DYNAMIC_RUNTIME_THUNK +# define INTERFACE_FUNCTION(Name) +# define INTERFACE_WEAK_FUNCTION(Name) REGISTER_WEAK_FUNCTION(Name) +# include "sanitizer_coverage_interface.inc" +#endif // defined(SANITIZER_DYNAMIC_RUNTIME_THUNK) || + // defined(SANITIZER_STATIC_RUNTIME_THUNK) namespace __sanitizer { // Add one, otherwise unused, external symbol to this object file so that the // Visual C++ linker includes it and reads the .drective section. void ForceWholeArchiveIncludeForSanCov() {} -} +} // namespace __sanitizer diff --git a/libsanitizer/sanitizer_common/sanitizer_coverage_win_weak_interception.cpp b/libsanitizer/sanitizer_common/sanitizer_coverage_win_weak_interception.cpp deleted file mode 100644 index 5526398..0000000 --- a/libsanitizer/sanitizer_common/sanitizer_coverage_win_weak_interception.cpp +++ /dev/null @@ -1,23 +0,0 @@ -//===-- sanitizer_coverage_win_weak_interception.cpp ----------------------===// -// -// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. -// See https://llvm.org/LICENSE.txt for license information. -// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception -// -//===----------------------------------------------------------------------===// -// This module should be included in Sanitizer Coverage when it implemented as a -// shared library on Windows (dll), in order to delegate the calls of weak -// functions to the implementation in the main executable when a strong -// definition is provided. -//===----------------------------------------------------------------------===// -#ifdef SANITIZER_DYNAMIC -#include "sanitizer_win_weak_interception.h" -#include "sanitizer_interface_internal.h" -#include "sancov_flags.h" -// Check if strong definitions for weak functions are present in the main -// executable. If that is the case, override dll functions to point to strong -// implementations. -#define INTERFACE_FUNCTION(Name) -#define INTERFACE_WEAK_FUNCTION(Name) INTERCEPT_SANITIZER_WEAK_FUNCTION(Name) -#include "sanitizer_coverage_interface.inc" -#endif // SANITIZER_DYNAMIC diff --git a/libsanitizer/sanitizer_common/sanitizer_dense_map.h b/libsanitizer/sanitizer_common/sanitizer_dense_map.h index 046d77d..c637886 100644 --- a/libsanitizer/sanitizer_common/sanitizer_dense_map.h +++ b/libsanitizer/sanitizer_common/sanitizer_dense_map.h @@ -69,24 +69,14 @@ class DenseMapBase { setNumTombstones(0); } + /// Return true if the specified key is in the map, false otherwise. + bool contains(const KeyT &Key) const { return doFind(Key) != nullptr; } + /// Return 1 if the specified key is in the map, 0 otherwise. - size_type count(const KeyT &Key) const { - const BucketT *TheBucket; - return LookupBucketFor(Key, TheBucket) ? 1 : 0; - } + size_type count(const KeyT &Key) const { return contains(Key) ? 1 : 0; } - value_type *find(const KeyT &Key) { - BucketT *TheBucket; - if (LookupBucketFor(Key, TheBucket)) - return TheBucket; - return nullptr; - } - const value_type *find(const KeyT &Key) const { - const BucketT *TheBucket; - if (LookupBucketFor(Key, TheBucket)) - return TheBucket; - return nullptr; - } + value_type *find(const KeyT &Key) { return doFind(Key); } + const value_type *find(const KeyT &Key) const { return doFind(Key); } /// Alternate version of find() which allows a different, and possibly /// less expensive, key type. @@ -95,25 +85,18 @@ class DenseMapBase { /// type used. template <class LookupKeyT> value_type *find_as(const LookupKeyT &Key) { - BucketT *TheBucket; - if (LookupBucketFor(Key, TheBucket)) - return TheBucket; - return nullptr; + return doFind(Key); } template <class LookupKeyT> const value_type *find_as(const LookupKeyT &Key) const { - const BucketT *TheBucket; - if (LookupBucketFor(Key, TheBucket)) - return TheBucket; - return nullptr; + return doFind(Key); } /// lookup - Return the entry for the specified key, or a default /// constructed value if no such entry exists. ValueT lookup(const KeyT &Key) const { - const BucketT *TheBucket; - if (LookupBucketFor(Key, TheBucket)) - return TheBucket->getSecond(); + if (const BucketT *Bucket = doFind(Key)) + return Bucket->getSecond(); return ValueT(); } @@ -184,8 +167,8 @@ class DenseMapBase { } bool erase(const KeyT &Val) { - BucketT *TheBucket; - if (!LookupBucketFor(Val, TheBucket)) + BucketT *TheBucket = doFind(Val); + if (!TheBucket) return false; // not in map. TheBucket->getSecond().~ValueT(); @@ -449,6 +432,35 @@ class DenseMapBase { return TheBucket; } + template <typename LookupKeyT> + BucketT *doFind(const LookupKeyT &Val) { + BucketT *BucketsPtr = getBuckets(); + const unsigned NumBuckets = getNumBuckets(); + if (NumBuckets == 0) + return nullptr; + + const KeyT EmptyKey = getEmptyKey(); + unsigned BucketNo = getHashValue(Val) & (NumBuckets - 1); + unsigned ProbeAmt = 1; + while (true) { + BucketT *Bucket = BucketsPtr + BucketNo; + if (LIKELY(KeyInfoT::isEqual(Val, Bucket->getFirst()))) + return Bucket; + if (LIKELY(KeyInfoT::isEqual(Bucket->getFirst(), EmptyKey))) + return nullptr; + + // Otherwise, it's a hash collision or a tombstone, continue quadratic + // probing. + BucketNo += ProbeAmt++; + BucketNo &= NumBuckets - 1; + } + } + + template <typename LookupKeyT> + const BucketT *doFind(const LookupKeyT &Val) const { + return const_cast<DenseMapBase *>(this)->doFind(Val); + } + /// LookupBucketFor - Lookup the appropriate bucket for Val, returning it in /// FoundBucket. If the bucket contains the key and a value, this returns /// true, otherwise it returns a bucket with an empty marker or tombstone and diff --git a/libsanitizer/sanitizer_common/sanitizer_errno.cpp b/libsanitizer/sanitizer_common/sanitizer_errno.cpp index cbadf4d..a7cdf32 100644 --- a/libsanitizer/sanitizer_common/sanitizer_errno.cpp +++ b/libsanitizer/sanitizer_common/sanitizer_errno.cpp @@ -23,6 +23,7 @@ namespace __sanitizer { COMPILER_CHECK(errno_ENOMEM == ENOMEM); COMPILER_CHECK(errno_EBUSY == EBUSY); COMPILER_CHECK(errno_EINVAL == EINVAL); +COMPILER_CHECK(errno_ERANGE == ERANGE); // EOWNERDEAD is not present in some older platforms. #if defined(EOWNERDEAD) diff --git a/libsanitizer/sanitizer_common/sanitizer_errno_codes.h b/libsanitizer/sanitizer_common/sanitizer_errno_codes.h index 3917b28..9e6e71e 100644 --- a/libsanitizer/sanitizer_common/sanitizer_errno_codes.h +++ b/libsanitizer/sanitizer_common/sanitizer_errno_codes.h @@ -24,6 +24,7 @@ namespace __sanitizer { #define errno_ENOMEM 12 #define errno_EBUSY 16 #define errno_EINVAL 22 +#define errno_ERANGE 34 #define errno_ENAMETOOLONG 36 #define errno_ENOSYS 38 diff --git a/libsanitizer/sanitizer_common/sanitizer_file.cpp b/libsanitizer/sanitizer_common/sanitizer_file.cpp index 7ef499c..96af270 100644 --- a/libsanitizer/sanitizer_common/sanitizer_file.cpp +++ b/libsanitizer/sanitizer_common/sanitizer_file.cpp @@ -69,7 +69,7 @@ void ReportFile::ReopenIfNecessary() { WriteToFile(kStderrFd, ErrorMsgPrefix, internal_strlen(ErrorMsgPrefix)); WriteToFile(kStderrFd, full_path, internal_strlen(full_path)); char errmsg[100]; - internal_snprintf(errmsg, sizeof(errmsg), " (reason: %d)", err); + internal_snprintf(errmsg, sizeof(errmsg), " (reason: %d)\n", err); WriteToFile(kStderrFd, errmsg, internal_strlen(errmsg)); Die(); } @@ -88,6 +88,8 @@ static void RecursiveCreateParentDirs(char *path) { const char *ErrorMsgPrefix = "ERROR: Can't create directory: "; WriteToFile(kStderrFd, ErrorMsgPrefix, internal_strlen(ErrorMsgPrefix)); WriteToFile(kStderrFd, path, internal_strlen(path)); + const char *ErrorMsgSuffix = "\n"; + WriteToFile(kStderrFd, ErrorMsgSuffix, internal_strlen(ErrorMsgSuffix)); Die(); } path[i] = save; diff --git a/libsanitizer/sanitizer_common/sanitizer_flags.inc b/libsanitizer/sanitizer_common/sanitizer_flags.inc index 949bdbd..c1e3530 100644 --- a/libsanitizer/sanitizer_common/sanitizer_flags.inc +++ b/libsanitizer/sanitizer_common/sanitizer_flags.inc @@ -275,3 +275,10 @@ COMMON_FLAG(bool, test_only_emulate_no_memorymap, false, // program. COMMON_FLAG(bool, test_only_replace_dlopen_main_program, false, "TEST ONLY replace dlopen(<main program>,...) with dlopen(NULL)") + +COMMON_FLAG(bool, enable_symbolizer_markup, SANITIZER_FUCHSIA, + "Use sanitizer symbolizer markup, available on Linux " + "and always set true for Fuchsia.") + +COMMON_FLAG(bool, detect_invalid_join, true, + "If set, check invalid joins of threads.") diff --git a/libsanitizer/sanitizer_common/sanitizer_flat_map.h b/libsanitizer/sanitizer_common/sanitizer_flat_map.h index 8bb8304..d246781 100644 --- a/libsanitizer/sanitizer_common/sanitizer_flat_map.h +++ b/libsanitizer/sanitizer_common/sanitizer_flat_map.h @@ -109,6 +109,10 @@ class TwoLevelMap { return *AddressSpaceView::LoadWritable(&map2[idx % kSize2]); } + void Lock() SANITIZER_NO_THREAD_SAFETY_ANALYSIS { mu_.Lock(); } + + void Unlock() SANITIZER_NO_THREAD_SAFETY_ANALYSIS { mu_.Unlock(); } + private: constexpr uptr MmapSize() const { return RoundUpTo(kSize2 * sizeof(T), GetPageSizeCached()); diff --git a/libsanitizer/sanitizer_common/sanitizer_freebsd.h b/libsanitizer/sanitizer_common/sanitizer_freebsd.h deleted file mode 100644 index 82b227e..0000000 --- a/libsanitizer/sanitizer_common/sanitizer_freebsd.h +++ /dev/null @@ -1,137 +0,0 @@ -//===-- sanitizer_freebsd.h -------------------------------------*- C++ -*-===// -// -// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. -// See https://llvm.org/LICENSE.txt for license information. -// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception -// -//===----------------------------------------------------------------------===// -// -// This file is a part of Sanitizer runtime. It contains FreeBSD-specific -// definitions. -// -//===----------------------------------------------------------------------===// - -#ifndef SANITIZER_FREEBSD_H -#define SANITIZER_FREEBSD_H - -#include "sanitizer_internal_defs.h" - -// x86-64 FreeBSD 9.2 and older define 'ucontext_t' incorrectly in -// 32-bit mode. -#if SANITIZER_FREEBSD && (SANITIZER_WORDSIZE == 32) -#include <osreldate.h> -#if __FreeBSD_version <= 902001 // v9.2 -#include <link.h> -#include <sys/param.h> -#include <ucontext.h> - -namespace __sanitizer { - -typedef unsigned long long __xuint64_t; - -typedef __int32_t __xregister_t; - -typedef struct __xmcontext { - __xregister_t mc_onstack; - __xregister_t mc_gs; - __xregister_t mc_fs; - __xregister_t mc_es; - __xregister_t mc_ds; - __xregister_t mc_edi; - __xregister_t mc_esi; - __xregister_t mc_ebp; - __xregister_t mc_isp; - __xregister_t mc_ebx; - __xregister_t mc_edx; - __xregister_t mc_ecx; - __xregister_t mc_eax; - __xregister_t mc_trapno; - __xregister_t mc_err; - __xregister_t mc_eip; - __xregister_t mc_cs; - __xregister_t mc_eflags; - __xregister_t mc_esp; - __xregister_t mc_ss; - - int mc_len; - int mc_fpformat; - int mc_ownedfp; - __xregister_t mc_flags; - - int mc_fpstate[128] __aligned(16); - __xregister_t mc_fsbase; - __xregister_t mc_gsbase; - __xregister_t mc_xfpustate; - __xregister_t mc_xfpustate_len; - - int mc_spare2[4]; -} xmcontext_t; - -typedef struct __xucontext { - sigset_t uc_sigmask; - xmcontext_t uc_mcontext; - - struct __ucontext *uc_link; - stack_t uc_stack; - int uc_flags; - int __spare__[4]; -} xucontext_t; - -struct xkinfo_vmentry { - int kve_structsize; - int kve_type; - __xuint64_t kve_start; - __xuint64_t kve_end; - __xuint64_t kve_offset; - __xuint64_t kve_vn_fileid; - __uint32_t kve_vn_fsid; - int kve_flags; - int kve_resident; - int kve_private_resident; - int kve_protection; - int kve_ref_count; - int kve_shadow_count; - int kve_vn_type; - __xuint64_t kve_vn_size; - __uint32_t kve_vn_rdev; - __uint16_t kve_vn_mode; - __uint16_t kve_status; - int _kve_ispare[12]; - char kve_path[PATH_MAX]; -}; - -typedef struct { - __uint32_t p_type; - __uint32_t p_offset; - __uint32_t p_vaddr; - __uint32_t p_paddr; - __uint32_t p_filesz; - __uint32_t p_memsz; - __uint32_t p_flags; - __uint32_t p_align; -} XElf32_Phdr; - -struct xdl_phdr_info { - Elf_Addr dlpi_addr; - const char *dlpi_name; - const XElf32_Phdr *dlpi_phdr; - Elf_Half dlpi_phnum; - unsigned long long int dlpi_adds; - unsigned long long int dlpi_subs; - size_t dlpi_tls_modid; - void *dlpi_tls_data; -}; - -typedef int (*__xdl_iterate_hdr_callback)(struct xdl_phdr_info *, size_t, - void *); -typedef int xdl_iterate_phdr_t(__xdl_iterate_hdr_callback, void *); - -#define xdl_iterate_phdr(callback, param) \ - (((xdl_iterate_phdr_t *)dl_iterate_phdr)((callback), (param))) - -} // namespace __sanitizer - -#endif // __FreeBSD_version <= 902001 -#endif // SANITIZER_FREEBSD && (SANITIZER_WORDSIZE == 32) - -#endif // SANITIZER_FREEBSD_H diff --git a/libsanitizer/sanitizer_common/sanitizer_fuchsia.cpp b/libsanitizer/sanitizer_common/sanitizer_fuchsia.cpp index 0245164..acbf3eb 100644 --- a/libsanitizer/sanitizer_common/sanitizer_fuchsia.cpp +++ b/libsanitizer/sanitizer_common/sanitizer_fuchsia.cpp @@ -94,7 +94,6 @@ void DisableCoreDumperIfNecessary() {} void InstallDeadlySignalHandlers(SignalHandlerType handler) {} void SetAlternateSignalStack() {} void UnsetAlternateSignalStack() {} -void InitTlsSize() {} bool SignalContext::IsStackOverflow() const { return false; } void SignalContext::DumpAllRegisters(void *context) { UNIMPLEMENTED(); } @@ -129,6 +128,60 @@ uptr GetMaxVirtualAddress() { return GetMaxUserVirtualAddress(); } bool ErrorIsOOM(error_t err) { return err == ZX_ERR_NO_MEMORY; } +// For any sanitizer internal that needs to map something which can be unmapped +// later, first attempt to map to a pre-allocated VMAR. This helps reduce +// fragmentation from many small anonymous mmap calls. A good value for this +// VMAR size would be the total size of your typical sanitizer internal objects +// allocated in an "average" process lifetime. Examples of this include: +// FakeStack, LowLevelAllocator mappings, TwoLevelMap, InternalMmapVector, +// StackStore, CreateAsanThread, etc. +// +// This is roughly equal to the total sum of sanitizer internal mappings for a +// large test case. +constexpr size_t kSanitizerHeapVmarSize = 13ULL << 20; +static zx_handle_t gSanitizerHeapVmar = ZX_HANDLE_INVALID; + +static zx_status_t GetSanitizerHeapVmar(zx_handle_t *vmar) { + zx_status_t status = ZX_OK; + if (gSanitizerHeapVmar == ZX_HANDLE_INVALID) { + CHECK_EQ(kSanitizerHeapVmarSize % GetPageSizeCached(), 0); + uintptr_t base; + status = _zx_vmar_allocate( + _zx_vmar_root_self(), + ZX_VM_CAN_MAP_READ | ZX_VM_CAN_MAP_WRITE | ZX_VM_CAN_MAP_SPECIFIC, 0, + kSanitizerHeapVmarSize, &gSanitizerHeapVmar, &base); + } + *vmar = gSanitizerHeapVmar; + if (status == ZX_OK) + CHECK_NE(gSanitizerHeapVmar, ZX_HANDLE_INVALID); + return status; +} + +static zx_status_t TryVmoMapSanitizerVmar(zx_vm_option_t options, + size_t vmar_offset, zx_handle_t vmo, + size_t size, uintptr_t *addr, + zx_handle_t *vmar_used = nullptr) { + zx_handle_t vmar; + zx_status_t status = GetSanitizerHeapVmar(&vmar); + if (status != ZX_OK) + return status; + + status = _zx_vmar_map(gSanitizerHeapVmar, options, vmar_offset, vmo, + /*vmo_offset=*/0, size, addr); + if (vmar_used) + *vmar_used = gSanitizerHeapVmar; + if (status == ZX_ERR_NO_RESOURCES || status == ZX_ERR_INVALID_ARGS) { + // This means there's no space in the heap VMAR, so fallback to the root + // VMAR. + status = _zx_vmar_map(_zx_vmar_root_self(), options, vmar_offset, vmo, + /*vmo_offset=*/0, size, addr); + if (vmar_used) + *vmar_used = _zx_vmar_root_self(); + } + + return status; +} + static void *DoAnonymousMmapOrDie(uptr size, const char *mem_type, bool raw_report, bool die_for_nomem) { size = RoundUpTo(size, GetPageSize()); @@ -144,11 +197,9 @@ static void *DoAnonymousMmapOrDie(uptr size, const char *mem_type, _zx_object_set_property(vmo, ZX_PROP_NAME, mem_type, internal_strlen(mem_type)); - // TODO(mcgrathr): Maybe allocate a VMAR for all sanitizer heap and use that? uintptr_t addr; - status = - _zx_vmar_map(_zx_vmar_root_self(), ZX_VM_PERM_READ | ZX_VM_PERM_WRITE, 0, - vmo, 0, size, &addr); + status = TryVmoMapSanitizerVmar(ZX_VM_PERM_READ | ZX_VM_PERM_WRITE, + /*vmar_offset=*/0, vmo, size, &addr); _zx_handle_close(vmo); if (status != ZX_OK) { @@ -236,18 +287,22 @@ uptr ReservedAddressRange::MapOrDie(uptr fixed_addr, uptr map_size, name ? name : name_, true); } -void UnmapOrDieVmar(void *addr, uptr size, zx_handle_t target_vmar) { +void UnmapOrDieVmar(void *addr, uptr size, zx_handle_t target_vmar, + bool raw_report) { if (!addr || !size) return; size = RoundUpTo(size, GetPageSize()); zx_status_t status = _zx_vmar_unmap(target_vmar, reinterpret_cast<uintptr_t>(addr), size); - if (status != ZX_OK) { - Report("ERROR: %s failed to deallocate 0x%zx (%zd) bytes at address %p\n", - SanitizerToolName, size, size, addr); - CHECK("unable to unmap" && 0); + if (status == ZX_ERR_INVALID_ARGS && target_vmar == gSanitizerHeapVmar) { + // If there wasn't any space in the heap vmar, the fallback was the root + // vmar. + status = _zx_vmar_unmap(_zx_vmar_root_self(), + reinterpret_cast<uintptr_t>(addr), size); } + if (status != ZX_OK) + ReportMunmapFailureAndDie(addr, size, status, raw_report); DecreaseTotalMmap(size); } @@ -269,7 +324,8 @@ void ReservedAddressRange::Unmap(uptr addr, uptr size) { } // Partial unmapping does not affect the fact that the initial range is still // reserved, and the resulting unmapped memory can't be reused. - UnmapOrDieVmar(reinterpret_cast<void *>(addr), size, vmar); + UnmapOrDieVmar(reinterpret_cast<void *>(addr), size, vmar, + /*raw_report=*/false); } // This should never be called. @@ -308,17 +364,16 @@ void *MmapAlignedOrDieOnFatalError(uptr size, uptr alignment, _zx_object_set_property(vmo, ZX_PROP_NAME, mem_type, internal_strlen(mem_type)); - // TODO(mcgrathr): Maybe allocate a VMAR for all sanitizer heap and use that? - // Map a larger size to get a chunk of address space big enough that // it surely contains an aligned region of the requested size. Then // overwrite the aligned middle portion with a mapping from the // beginning of the VMO, and unmap the excess before and after. size_t map_size = size + alignment; uintptr_t addr; - status = - _zx_vmar_map(_zx_vmar_root_self(), ZX_VM_PERM_READ | ZX_VM_PERM_WRITE, 0, - vmo, 0, map_size, &addr); + zx_handle_t vmar_used; + status = TryVmoMapSanitizerVmar(ZX_VM_PERM_READ | ZX_VM_PERM_WRITE, + /*vmar_offset=*/0, vmo, map_size, &addr, + &vmar_used); if (status == ZX_OK) { uintptr_t map_addr = addr; uintptr_t map_end = map_addr + map_size; @@ -326,12 +381,12 @@ void *MmapAlignedOrDieOnFatalError(uptr size, uptr alignment, uintptr_t end = addr + size; if (addr != map_addr) { zx_info_vmar_t info; - status = _zx_object_get_info(_zx_vmar_root_self(), ZX_INFO_VMAR, &info, - sizeof(info), NULL, NULL); + status = _zx_object_get_info(vmar_used, ZX_INFO_VMAR, &info, sizeof(info), + NULL, NULL); if (status == ZX_OK) { uintptr_t new_addr; status = _zx_vmar_map( - _zx_vmar_root_self(), + vmar_used, ZX_VM_PERM_READ | ZX_VM_PERM_WRITE | ZX_VM_SPECIFIC_OVERWRITE, addr - info.base, vmo, 0, size, &new_addr); if (status == ZX_OK) @@ -339,9 +394,9 @@ void *MmapAlignedOrDieOnFatalError(uptr size, uptr alignment, } } if (status == ZX_OK && addr != map_addr) - status = _zx_vmar_unmap(_zx_vmar_root_self(), map_addr, addr - map_addr); + status = _zx_vmar_unmap(vmar_used, map_addr, addr - map_addr); if (status == ZX_OK && end != map_end) - status = _zx_vmar_unmap(_zx_vmar_root_self(), end, map_end - end); + status = _zx_vmar_unmap(vmar_used, end, map_end - end); } _zx_handle_close(vmo); @@ -356,8 +411,8 @@ void *MmapAlignedOrDieOnFatalError(uptr size, uptr alignment, return reinterpret_cast<void *>(addr); } -void UnmapOrDie(void *addr, uptr size) { - UnmapOrDieVmar(addr, size, _zx_vmar_root_self()); +void UnmapOrDie(void *addr, uptr size, bool raw_report) { + UnmapOrDieVmar(addr, size, gSanitizerHeapVmar, raw_report); } void ReleaseMemoryPagesToOS(uptr beg, uptr end) { @@ -389,6 +444,11 @@ bool IsAccessibleMemoryRange(uptr beg, uptr size) { return status == ZX_OK; } +bool TryMemCpy(void *dest, const void *src, uptr n) { + // TODO: implement. + return false; +} + // FIXME implement on this platform. void GetMemoryProfile(fill_profile_f cb, uptr *stats) {} @@ -463,7 +523,6 @@ uptr ReadLongProcessName(/*out*/ char *buf, uptr buf_len) { uptr MainThreadStackBase, MainThreadStackSize; bool GetRandom(void *buffer, uptr length, bool blocking) { - CHECK_LE(length, ZX_CPRNG_DRAW_MAX_LEN); _zx_cprng_draw(buffer, length); return true; } diff --git a/libsanitizer/sanitizer_common/sanitizer_hash.h b/libsanitizer/sanitizer_common/sanitizer_hash.h index f7cf9f2..648fcfa 100644 --- a/libsanitizer/sanitizer_common/sanitizer_hash.h +++ b/libsanitizer/sanitizer_common/sanitizer_hash.h @@ -62,6 +62,6 @@ class MurMur2Hash64Builder { return x; } }; -} //namespace __sanitizer +} // namespace __sanitizer #endif // SANITIZER_HASH_H diff --git a/libsanitizer/sanitizer_common/sanitizer_interface_internal.h b/libsanitizer/sanitizer_common/sanitizer_interface_internal.h index cd0d45e..387a4d8 100644 --- a/libsanitizer/sanitizer_common/sanitizer_interface_internal.h +++ b/libsanitizer/sanitizer_common/sanitizer_interface_internal.h @@ -49,6 +49,11 @@ __sanitizer_sandbox_on_notify(__sanitizer_sandbox_arguments *args); SANITIZER_INTERFACE_ATTRIBUTE SANITIZER_WEAK_ATTRIBUTE void __sanitizer_report_error_summary(const char *error_summary); +// Returns size of dynamically allocated block. This function can be overridden +// by the client. +SANITIZER_INTERFACE_ATTRIBUTE SANITIZER_WEAK_ATTRIBUTE __sanitizer::uptr +__sanitizer_get_dtls_size(const void *tls_begin); + SANITIZER_INTERFACE_ATTRIBUTE void __sanitizer_cov_dump(); SANITIZER_INTERFACE_ATTRIBUTE void __sanitizer_dump_coverage( const __sanitizer::uptr *pcs, const __sanitizer::uptr len); @@ -71,6 +76,11 @@ void __sanitizer_annotate_double_ended_contiguous_container( const void *old_container_beg, const void *old_container_end, const void *new_container_beg, const void *new_container_end); SANITIZER_INTERFACE_ATTRIBUTE +void __sanitizer_copy_contiguous_container_annotations(const void *src_begin, + const void *src_end, + const void *dst_begin, + const void *dst_end); +SANITIZER_INTERFACE_ATTRIBUTE int __sanitizer_verify_contiguous_container(const void *beg, const void *mid, const void *end); SANITIZER_INTERFACE_ATTRIBUTE diff --git a/libsanitizer/sanitizer_common/sanitizer_internal_defs.h b/libsanitizer/sanitizer_common/sanitizer_internal_defs.h index 3809669..9208b12 100644 --- a/libsanitizer/sanitizer_common/sanitizer_internal_defs.h +++ b/libsanitizer/sanitizer_common/sanitizer_internal_defs.h @@ -35,13 +35,20 @@ # define SANITIZER_INTERFACE_ATTRIBUTE __declspec(dllexport) #endif # define SANITIZER_WEAK_ATTRIBUTE +# define SANITIZER_WEAK_IMPORT #elif SANITIZER_GO # define SANITIZER_INTERFACE_ATTRIBUTE # define SANITIZER_WEAK_ATTRIBUTE +# define SANITIZER_WEAK_IMPORT #else # define SANITIZER_INTERFACE_ATTRIBUTE __attribute__((visibility("default"))) # define SANITIZER_WEAK_ATTRIBUTE __attribute__((weak)) -#endif +# if SANITIZER_APPLE +# define SANITIZER_WEAK_IMPORT extern "C" __attribute((weak_import)) +# else +# define SANITIZER_WEAK_IMPORT extern "C" SANITIZER_WEAK_ATTRIBUTE +# endif // SANITIZER_APPLE +#endif // SANITIZER_WINDOWS //--------------------------- WEAK FUNCTIONS ---------------------------------// // When working with weak functions, to simplify the code and make it more @@ -131,19 +138,25 @@ // in a portable way by the language itself. namespace __sanitizer { -#if defined(_WIN64) +#if defined(__UINTPTR_TYPE__) +# if defined(__arm__) && defined(__linux__) +// Linux Arm headers redefine __UINTPTR_TYPE__ and disagree with clang/gcc. +typedef unsigned int uptr; +typedef int sptr; +# else +typedef __UINTPTR_TYPE__ uptr; +typedef __INTPTR_TYPE__ sptr; +# endif +#elif defined(_WIN64) // 64-bit Windows uses LLP64 data model. typedef unsigned long long uptr; typedef signed long long sptr; -#else -# if (SANITIZER_WORDSIZE == 64) || SANITIZER_APPLE || SANITIZER_WINDOWS -typedef unsigned long uptr; -typedef signed long sptr; -# else +#elif defined(_WIN32) typedef unsigned int uptr; typedef signed int sptr; -# endif -#endif // defined(_WIN64) +#else +# error Unsupported compiler, missing __UINTPTR_TYPE__ +#endif // defined(__UINTPTR_TYPE__) #if defined(__x86_64__) // Since x32 uses ILP32 data model in 64-bit hardware mode, we must use // 64-bit pointer to unwind stack frame. @@ -184,15 +197,10 @@ typedef uptr OFF_T; #endif typedef u64 OFF64_T; -#if (SANITIZER_WORDSIZE == 64) || SANITIZER_APPLE -typedef uptr operator_new_size_type; +#ifdef __SIZE_TYPE__ +typedef __SIZE_TYPE__ usize; #else -# if defined(__s390__) && !defined(__s390x__) -// Special case: 31-bit s390 has unsigned long as size_t. -typedef unsigned long operator_new_size_type; -# else -typedef u32 operator_new_size_type; -# endif +typedef uptr usize; #endif typedef u64 tid_t; @@ -455,6 +463,9 @@ using namespace __sanitizer; namespace __msan { using namespace __sanitizer; } +namespace __nsan { +using namespace __sanitizer; +} namespace __hwasan { using namespace __sanitizer; } diff --git a/libsanitizer/sanitizer_common/sanitizer_libignore.cpp b/libsanitizer/sanitizer_common/sanitizer_libignore.cpp index b7fc944..271c92e 100644 --- a/libsanitizer/sanitizer_common/sanitizer_libignore.cpp +++ b/libsanitizer/sanitizer_common/sanitizer_libignore.cpp @@ -32,7 +32,7 @@ void LibIgnore::AddIgnoredLibrary(const char *name_templ) { lib->templ = internal_strdup(name_templ); lib->name = nullptr; lib->real_name = nullptr; - lib->loaded = false; + lib->range_id = kInvalidCodeRangeId; } void LibIgnore::OnLibraryLoaded(const char *name) { @@ -43,7 +43,7 @@ void LibIgnore::OnLibraryLoaded(const char *name) { buf[0]) { for (uptr i = 0; i < count_; i++) { Lib *lib = &libs_[i]; - if (!lib->loaded && (!lib->real_name) && + if (!lib->loaded() && (!lib->real_name) && TemplateMatch(lib->templ, name)) lib->real_name = internal_strdup(buf.data()); } @@ -70,28 +70,31 @@ void LibIgnore::OnLibraryLoaded(const char *name) { Die(); } loaded = true; - if (lib->loaded) + if (lib->loaded()) continue; VReport(1, "Matched called_from_lib suppression '%s' against library" " '%s'\n", lib->templ, mod.full_name()); - lib->loaded = true; lib->name = internal_strdup(mod.full_name()); const uptr idx = atomic_load(&ignored_ranges_count_, memory_order_relaxed); CHECK_LT(idx, ARRAY_SIZE(ignored_code_ranges_)); - ignored_code_ranges_[idx].begin = range.beg; - ignored_code_ranges_[idx].end = range.end; + ignored_code_ranges_[idx].OnLoad(range.beg, range.end); + // Record the index of the ignored range. + lib->range_id = idx; atomic_store(&ignored_ranges_count_, idx + 1, memory_order_release); break; } } - if (lib->loaded && !loaded) { - Report("%s: library '%s' that was matched against called_from_lib" - " suppression '%s' is unloaded\n", - SanitizerToolName, lib->name, lib->templ); - Die(); + if (lib->loaded() && !loaded) { + VReport(1, + "%s: library '%s' that was matched against called_from_lib" + " suppression '%s' is unloaded\n", + SanitizerToolName, lib->name, lib->templ); + // The library is unloaded so mark the ignored code range as unloaded. + ignored_code_ranges_[lib->range_id].OnUnload(); + lib->range_id = kInvalidCodeRangeId; } } @@ -105,13 +108,12 @@ void LibIgnore::OnLibraryLoaded(const char *name) { continue; if (IsPcInstrumented(range.beg) && IsPcInstrumented(range.end - 1)) continue; - VReport(1, "Adding instrumented range 0x%zx-0x%zx from library '%s'\n", - range.beg, range.end, mod.full_name()); + VReport(1, "Adding instrumented range %p-%p from library '%s'\n", + (void *)range.beg, (void *)range.end, mod.full_name()); const uptr idx = atomic_load(&instrumented_ranges_count_, memory_order_relaxed); CHECK_LT(idx, ARRAY_SIZE(instrumented_code_ranges_)); - instrumented_code_ranges_[idx].begin = range.beg; - instrumented_code_ranges_[idx].end = range.end; + instrumented_code_ranges_[idx].OnLoad(range.beg, range.end); atomic_store(&instrumented_ranges_count_, idx + 1, memory_order_release); } diff --git a/libsanitizer/sanitizer_common/sanitizer_libignore.h b/libsanitizer/sanitizer_common/sanitizer_libignore.h index 18e4d83..0e26ff4 100644 --- a/libsanitizer/sanitizer_common/sanitizer_libignore.h +++ b/libsanitizer/sanitizer_common/sanitizer_libignore.h @@ -49,25 +49,36 @@ class LibIgnore { bool IsPcInstrumented(uptr pc) const; private: + static const uptr kMaxIgnoredRanges = 128; + static const uptr kMaxInstrumentedRanges = 1024; + static const uptr kMaxLibs = 1024; + static const uptr kInvalidCodeRangeId = -1; + struct Lib { char *templ; char *name; char *real_name; // target of symlink - bool loaded; + uptr range_id; + bool loaded() const { return range_id != kInvalidCodeRangeId; }; }; struct LibCodeRange { - uptr begin; - uptr end; - }; + bool IsInRange(uptr pc) const { + return (pc >= begin && pc < atomic_load(&end, memory_order_acquire)); + } - inline bool IsInRange(uptr pc, const LibCodeRange &range) const { - return (pc >= range.begin && pc < range.end); - } + void OnLoad(uptr b, uptr e) { + begin = b; + atomic_store(&end, e, memory_order_release); + } - static const uptr kMaxIgnoredRanges = 128; - static const uptr kMaxInstrumentedRanges = 1024; - static const uptr kMaxLibs = 1024; + void OnUnload() { atomic_store(&end, 0, memory_order_release); } + + private: + uptr begin; + // A value of 0 means the associated module was unloaded. + atomic_uintptr_t end; + }; // Hot part: atomic_uintptr_t ignored_ranges_count_; @@ -90,7 +101,7 @@ class LibIgnore { inline bool LibIgnore::IsIgnored(uptr pc, bool *pc_in_ignored_lib) const { const uptr n = atomic_load(&ignored_ranges_count_, memory_order_acquire); for (uptr i = 0; i < n; i++) { - if (IsInRange(pc, ignored_code_ranges_[i])) { + if (ignored_code_ranges_[i].IsInRange(pc)) { *pc_in_ignored_lib = true; return true; } @@ -104,7 +115,7 @@ inline bool LibIgnore::IsIgnored(uptr pc, bool *pc_in_ignored_lib) const { inline bool LibIgnore::IsPcInstrumented(uptr pc) const { const uptr n = atomic_load(&instrumented_ranges_count_, memory_order_acquire); for (uptr i = 0; i < n; i++) { - if (IsInRange(pc, instrumented_code_ranges_[i])) + if (instrumented_code_ranges_[i].IsInRange(pc)) return true; } return false; diff --git a/libsanitizer/sanitizer_common/sanitizer_linux.cpp b/libsanitizer/sanitizer_common/sanitizer_linux.cpp index d2b3b63..8b1850f 100644 --- a/libsanitizer/sanitizer_common/sanitizer_linux.cpp +++ b/libsanitizer/sanitizer_common/sanitizer_linux.cpp @@ -16,101 +16,107 @@ #if SANITIZER_FREEBSD || SANITIZER_LINUX || SANITIZER_NETBSD || \ SANITIZER_SOLARIS -#include "sanitizer_common.h" -#include "sanitizer_flags.h" -#include "sanitizer_getauxval.h" -#include "sanitizer_internal_defs.h" -#include "sanitizer_libc.h" -#include "sanitizer_linux.h" -#include "sanitizer_mutex.h" -#include "sanitizer_placement_new.h" -#include "sanitizer_procmaps.h" - -#if SANITIZER_LINUX && !SANITIZER_GO -#include <asm/param.h> -#endif +# include "sanitizer_common.h" +# include "sanitizer_flags.h" +# include "sanitizer_getauxval.h" +# include "sanitizer_internal_defs.h" +# include "sanitizer_libc.h" +# include "sanitizer_linux.h" +# include "sanitizer_mutex.h" +# include "sanitizer_placement_new.h" +# include "sanitizer_procmaps.h" + +# if SANITIZER_LINUX && !SANITIZER_GO +# include <asm/param.h> +# endif // For mips64, syscall(__NR_stat) fills the buffer in the 'struct kernel_stat' // format. Struct kernel_stat is defined as 'struct stat' in asm/stat.h. To // access stat from asm/stat.h, without conflicting with definition in -// sys/stat.h, we use this trick. -#if SANITIZER_MIPS64 -#include <asm/unistd.h> -#include <sys/types.h> -#define stat kernel_stat -#if SANITIZER_GO -#undef st_atime -#undef st_mtime -#undef st_ctime -#define st_atime st_atim -#define st_mtime st_mtim -#define st_ctime st_ctim -#endif -#include <asm/stat.h> -#undef stat -#endif +// sys/stat.h, we use this trick. sparc64 is similar, using +// syscall(__NR_stat64) and struct kernel_stat64. +# if SANITIZER_LINUX && (SANITIZER_MIPS64 || SANITIZER_SPARC64) +# include <asm/unistd.h> +# include <sys/types.h> +# define stat kernel_stat +# if SANITIZER_SPARC64 +# define stat64 kernel_stat64 +# endif +# if SANITIZER_GO +# undef st_atime +# undef st_mtime +# undef st_ctime +# define st_atime st_atim +# define st_mtime st_mtim +# define st_ctime st_ctim +# endif +# include <asm/stat.h> +# undef stat +# undef stat64 +# endif -#include <dlfcn.h> -#include <errno.h> -#include <fcntl.h> -#include <link.h> -#include <pthread.h> -#include <sched.h> -#include <signal.h> -#include <sys/mman.h> -#include <sys/param.h> -#if !SANITIZER_SOLARIS -#include <sys/ptrace.h> -#endif -#include <sys/resource.h> -#include <sys/stat.h> -#include <sys/syscall.h> -#include <sys/time.h> -#include <sys/types.h> -#include <ucontext.h> -#include <unistd.h> - -#if SANITIZER_LINUX -#include <sys/utsname.h> -#endif +# include <dlfcn.h> +# include <errno.h> +# include <fcntl.h> +# include <link.h> +# include <pthread.h> +# include <sched.h> +# include <signal.h> +# include <sys/mman.h> +# if !SANITIZER_SOLARIS +# include <sys/ptrace.h> +# endif +# include <sys/resource.h> +# include <sys/stat.h> +# include <sys/syscall.h> +# include <sys/time.h> +# include <sys/types.h> +# include <ucontext.h> +# include <unistd.h> -#if SANITIZER_LINUX && !SANITIZER_ANDROID -#include <sys/personality.h> -#endif +# if SANITIZER_LINUX +# include <sys/utsname.h> +# endif -#if SANITIZER_LINUX && defined(__loongarch__) -# include <sys/sysmacros.h> -#endif +# if SANITIZER_LINUX && !SANITIZER_ANDROID +# include <sys/personality.h> +# endif + +# if SANITIZER_LINUX && defined(__loongarch__) +# include <sys/sysmacros.h> +# endif -#if SANITIZER_FREEBSD -#include <sys/exec.h> -#include <sys/procctl.h> -#include <sys/sysctl.h> -#include <machine/atomic.h> +# if SANITIZER_FREEBSD +# include <machine/atomic.h> +# include <sys/exec.h> +# include <sys/procctl.h> +# include <sys/sysctl.h> extern "C" { // <sys/umtx.h> must be included after <errno.h> and <sys/types.h> on // FreeBSD 9.2 and 10.0. -#include <sys/umtx.h> +# include <sys/umtx.h> } -#include <sys/thr.h> -#endif // SANITIZER_FREEBSD +# include <sys/thr.h> +# endif // SANITIZER_FREEBSD -#if SANITIZER_NETBSD -#include <limits.h> // For NAME_MAX -#include <sys/sysctl.h> -#include <sys/exec.h> +# if SANITIZER_NETBSD +# include <limits.h> // For NAME_MAX +# include <sys/exec.h> +# include <sys/sysctl.h> extern struct ps_strings *__ps_strings; -#endif // SANITIZER_NETBSD - -#if SANITIZER_SOLARIS -#include <stdlib.h> -#include <thread.h> -#define environ _environ -#endif +# endif // SANITIZER_NETBSD + +# if SANITIZER_SOLARIS +# include <stddef.h> +# include <stdlib.h> +# include <sys/frame.h> +# include <thread.h> +# define environ _environ +# endif extern char **environ; -#if SANITIZER_LINUX +# if SANITIZER_LINUX // <linux/time.h> struct kernel_timeval { long tv_sec; @@ -123,36 +129,34 @@ const int FUTEX_WAKE = 1; const int FUTEX_PRIVATE_FLAG = 128; const int FUTEX_WAIT_PRIVATE = FUTEX_WAIT | FUTEX_PRIVATE_FLAG; const int FUTEX_WAKE_PRIVATE = FUTEX_WAKE | FUTEX_PRIVATE_FLAG; -#endif // SANITIZER_LINUX +# endif // SANITIZER_LINUX // Are we using 32-bit or 64-bit Linux syscalls? // x32 (which defines __x86_64__) has SANITIZER_WORDSIZE == 32 // but it still needs to use 64-bit syscalls. -#if SANITIZER_LINUX && (defined(__x86_64__) || defined(__powerpc64__) || \ - SANITIZER_WORDSIZE == 64 || \ - (defined(__mips__) && _MIPS_SIM == _ABIN32)) -# define SANITIZER_LINUX_USES_64BIT_SYSCALLS 1 -#else -# define SANITIZER_LINUX_USES_64BIT_SYSCALLS 0 -#endif +# if SANITIZER_LINUX && (defined(__x86_64__) || defined(__powerpc64__) || \ + SANITIZER_WORDSIZE == 64 || \ + (defined(__mips__) && _MIPS_SIM == _ABIN32)) +# define SANITIZER_LINUX_USES_64BIT_SYSCALLS 1 +# else +# define SANITIZER_LINUX_USES_64BIT_SYSCALLS 0 +# endif -// Note : FreeBSD had implemented both -// Linux apis, available from -// future 12.x version most likely -#if SANITIZER_LINUX && defined(__NR_getrandom) -# if !defined(GRND_NONBLOCK) -# define GRND_NONBLOCK 1 -# endif -# define SANITIZER_USE_GETRANDOM 1 -#else -# define SANITIZER_USE_GETRANDOM 0 -#endif // SANITIZER_LINUX && defined(__NR_getrandom) - -#if SANITIZER_FREEBSD && __FreeBSD_version >= 1200000 -# define SANITIZER_USE_GETENTROPY 1 -#else -# define SANITIZER_USE_GETENTROPY 0 -#endif +// Note : FreeBSD implemented both Linux and OpenBSD apis. +# if SANITIZER_LINUX && defined(__NR_getrandom) +# if !defined(GRND_NONBLOCK) +# define GRND_NONBLOCK 1 +# endif +# define SANITIZER_USE_GETRANDOM 1 +# else +# define SANITIZER_USE_GETRANDOM 0 +# endif // SANITIZER_LINUX && defined(__NR_getrandom) + +# if SANITIZER_FREEBSD +# define SANITIZER_USE_GETENTROPY 1 +extern "C" void *__sys_mmap(void *addr, size_t len, int prot, int flags, int fd, + off_t offset); +# endif namespace __sanitizer { @@ -160,6 +164,7 @@ void SetSigProcMask(__sanitizer_sigset_t *set, __sanitizer_sigset_t *oldset) { CHECK_EQ(0, internal_sigprocmask(SIG_SETMASK, set, oldset)); } +// Block asynchronous signals void BlockSignals(__sanitizer_sigset_t *oldset) { __sanitizer_sigset_t set; internal_sigfillset(&set); @@ -174,7 +179,17 @@ void BlockSignals(__sanitizer_sigset_t *oldset) { // If this signal is blocked, such calls cannot be handled and the process may // hang. internal_sigdelset(&set, 31); + + // Don't block synchronous signals + internal_sigdelset(&set, SIGSEGV); + internal_sigdelset(&set, SIGBUS); + internal_sigdelset(&set, SIGILL); + internal_sigdelset(&set, SIGTRAP); + internal_sigdelset(&set, SIGABRT); + internal_sigdelset(&set, SIGFPE); + internal_sigdelset(&set, SIGPIPE); # endif + SetSigProcMask(&set, oldset); } @@ -203,33 +218,35 @@ ScopedBlockSignals::~ScopedBlockSignals() { SetSigProcMask(&saved_, nullptr); } # endif // --------------- sanitizer_libc.h -#if !SANITIZER_SOLARIS && !SANITIZER_NETBSD -#if !SANITIZER_S390 +# if !SANITIZER_SOLARIS && !SANITIZER_NETBSD +# if !SANITIZER_S390 uptr internal_mmap(void *addr, uptr length, int prot, int flags, int fd, u64 offset) { -#if SANITIZER_FREEBSD || SANITIZER_LINUX_USES_64BIT_SYSCALLS +# if SANITIZER_FREEBSD + return (uptr)__sys_mmap(addr, length, prot, flags, fd, offset); +# elif SANITIZER_LINUX_USES_64BIT_SYSCALLS return internal_syscall(SYSCALL(mmap), (uptr)addr, length, prot, flags, fd, offset); -#else +# else // mmap2 specifies file offset in 4096-byte units. CHECK(IsAligned(offset, 4096)); return internal_syscall(SYSCALL(mmap2), addr, length, prot, flags, fd, - offset / 4096); -#endif + (OFF_T)(offset / 4096)); +# endif } -#endif // !SANITIZER_S390 +# endif // !SANITIZER_S390 uptr internal_munmap(void *addr, uptr length) { return internal_syscall(SYSCALL(munmap), (uptr)addr, length); } -#if SANITIZER_LINUX +# if SANITIZER_LINUX uptr internal_mremap(void *old_address, uptr old_size, uptr new_size, int flags, void *new_address) { return internal_syscall(SYSCALL(mremap), (uptr)old_address, old_size, new_size, flags, (uptr)new_address); } -#endif +# endif int internal_mprotect(void *addr, uptr length, int prot) { return internal_syscall(SYSCALL(mprotect), (uptr)addr, length, prot); @@ -239,25 +256,28 @@ int internal_madvise(uptr addr, uptr length, int advice) { return internal_syscall(SYSCALL(madvise), addr, length, advice); } -uptr internal_close(fd_t fd) { - return internal_syscall(SYSCALL(close), fd); +# if SANITIZER_FREEBSD +uptr internal_close_range(fd_t lowfd, fd_t highfd, int flags) { + return internal_syscall(SYSCALL(close_range), lowfd, highfd, flags); } +# endif +uptr internal_close(fd_t fd) { return internal_syscall(SYSCALL(close), fd); } uptr internal_open(const char *filename, int flags) { # if SANITIZER_LINUX return internal_syscall(SYSCALL(openat), AT_FDCWD, (uptr)filename, flags); -#else +# else return internal_syscall(SYSCALL(open), (uptr)filename, flags); -#endif +# endif } uptr internal_open(const char *filename, int flags, u32 mode) { # if SANITIZER_LINUX return internal_syscall(SYSCALL(openat), AT_FDCWD, (uptr)filename, flags, mode); -#else +# else return internal_syscall(SYSCALL(open), (uptr)filename, flags, mode); -#endif +# endif } uptr internal_read(fd_t fd, void *buf, uptr count) { @@ -276,12 +296,12 @@ uptr internal_write(fd_t fd, const void *buf, uptr count) { uptr internal_ftruncate(fd_t fd, uptr size) { sptr res; - HANDLE_EINTR(res, (sptr)internal_syscall(SYSCALL(ftruncate), fd, - (OFF_T)size)); + HANDLE_EINTR(res, + (sptr)internal_syscall(SYSCALL(ftruncate), fd, (OFF_T)size)); return res; } -#if (!SANITIZER_LINUX_USES_64BIT_SYSCALLS || SANITIZER_SPARC) && SANITIZER_LINUX +# if !SANITIZER_LINUX_USES_64BIT_SYSCALLS && SANITIZER_LINUX static void stat64_to_stat(struct stat64 *in, struct stat *out) { internal_memset(out, 0, sizeof(*out)); out->st_dev = in->st_dev; @@ -298,9 +318,9 @@ static void stat64_to_stat(struct stat64 *in, struct stat *out) { out->st_mtime = in->st_mtime; out->st_ctime = in->st_ctime; } -#endif +# endif -#if SANITIZER_LINUX && defined(__loongarch__) +# if SANITIZER_LINUX && defined(__loongarch__) static void statx_to_stat(struct statx *in, struct stat *out) { internal_memset(out, 0, sizeof(*out)); out->st_dev = makedev(in->stx_dev_major, in->stx_dev_minor); @@ -320,27 +340,32 @@ static void statx_to_stat(struct statx *in, struct stat *out) { out->st_ctime = in->stx_ctime.tv_sec; out->st_ctim.tv_nsec = in->stx_ctime.tv_nsec; } -#endif +# endif -#if SANITIZER_MIPS64 +# if SANITIZER_MIPS64 || SANITIZER_SPARC64 +# if SANITIZER_MIPS64 +typedef struct kernel_stat kstat_t; +# else +typedef struct kernel_stat64 kstat_t; +# endif // Undefine compatibility macros from <sys/stat.h> // so that they would not clash with the kernel_stat // st_[a|m|c]time fields -#if !SANITIZER_GO -#undef st_atime -#undef st_mtime -#undef st_ctime -#endif -#if defined(SANITIZER_ANDROID) +# if !SANITIZER_GO +# undef st_atime +# undef st_mtime +# undef st_ctime +# endif +# if defined(SANITIZER_ANDROID) // Bionic sys/stat.h defines additional macros // for compatibility with the old NDKs and // they clash with the kernel_stat structure // st_[a|m|c]time_nsec fields. -#undef st_atime_nsec -#undef st_mtime_nsec -#undef st_ctime_nsec -#endif -static void kernel_stat_to_stat(struct kernel_stat *in, struct stat *out) { +# undef st_atime_nsec +# undef st_mtime_nsec +# undef st_ctime_nsec +# endif +static void kernel_stat_to_stat(kstat_t *in, struct stat *out) { internal_memset(out, 0, sizeof(*out)); out->st_dev = in->st_dev; out->st_ino = in->st_ino; @@ -352,96 +377,113 @@ static void kernel_stat_to_stat(struct kernel_stat *in, struct stat *out) { out->st_size = in->st_size; out->st_blksize = in->st_blksize; out->st_blocks = in->st_blocks; -#if defined(__USE_MISC) || \ - defined(__USE_XOPEN2K8) || \ - defined(SANITIZER_ANDROID) +# if defined(__USE_MISC) || defined(__USE_XOPEN2K8) || \ + defined(SANITIZER_ANDROID) out->st_atim.tv_sec = in->st_atime; out->st_atim.tv_nsec = in->st_atime_nsec; out->st_mtim.tv_sec = in->st_mtime; out->st_mtim.tv_nsec = in->st_mtime_nsec; out->st_ctim.tv_sec = in->st_ctime; out->st_ctim.tv_nsec = in->st_ctime_nsec; -#else +# else out->st_atime = in->st_atime; out->st_atimensec = in->st_atime_nsec; out->st_mtime = in->st_mtime; out->st_mtimensec = in->st_mtime_nsec; out->st_ctime = in->st_ctime; out->st_atimensec = in->st_ctime_nsec; -#endif +# endif } -#endif +# endif uptr internal_stat(const char *path, void *buf) { -# if SANITIZER_FREEBSD +# if SANITIZER_FREEBSD return internal_syscall(SYSCALL(fstatat), AT_FDCWD, (uptr)path, (uptr)buf, 0); -# elif SANITIZER_LINUX -# if defined(__loongarch__) +# elif SANITIZER_LINUX +# if defined(__loongarch__) struct statx bufx; int res = internal_syscall(SYSCALL(statx), AT_FDCWD, (uptr)path, AT_NO_AUTOMOUNT, STATX_BASIC_STATS, (uptr)&bufx); statx_to_stat(&bufx, (struct stat *)buf); return res; -# elif (SANITIZER_WORDSIZE == 64 || SANITIZER_X32 || \ - (defined(__mips__) && _MIPS_SIM == _ABIN32)) && \ - !SANITIZER_SPARC +# elif (SANITIZER_WORDSIZE == 64 || SANITIZER_X32 || \ + (defined(__mips__) && _MIPS_SIM == _ABIN32)) && \ + !SANITIZER_SPARC return internal_syscall(SYSCALL(newfstatat), AT_FDCWD, (uptr)path, (uptr)buf, 0); -# else +# elif SANITIZER_SPARC64 + kstat_t buf64; + int res = internal_syscall(SYSCALL(fstatat64), AT_FDCWD, (uptr)path, + (uptr)&buf64, 0); + kernel_stat_to_stat(&buf64, (struct stat *)buf); + return res; +# else struct stat64 buf64; int res = internal_syscall(SYSCALL(fstatat64), AT_FDCWD, (uptr)path, (uptr)&buf64, 0); stat64_to_stat(&buf64, (struct stat *)buf); return res; -# endif -# else +# endif +# else struct stat64 buf64; int res = internal_syscall(SYSCALL(stat64), path, &buf64); stat64_to_stat(&buf64, (struct stat *)buf); return res; -# endif +# endif } uptr internal_lstat(const char *path, void *buf) { -# if SANITIZER_FREEBSD +# if SANITIZER_FREEBSD return internal_syscall(SYSCALL(fstatat), AT_FDCWD, (uptr)path, (uptr)buf, AT_SYMLINK_NOFOLLOW); -# elif SANITIZER_LINUX -# if defined(__loongarch__) +# elif SANITIZER_LINUX +# if defined(__loongarch__) struct statx bufx; int res = internal_syscall(SYSCALL(statx), AT_FDCWD, (uptr)path, AT_SYMLINK_NOFOLLOW | AT_NO_AUTOMOUNT, STATX_BASIC_STATS, (uptr)&bufx); statx_to_stat(&bufx, (struct stat *)buf); return res; -# elif (defined(_LP64) || SANITIZER_X32 || \ - (defined(__mips__) && _MIPS_SIM == _ABIN32)) && \ - !SANITIZER_SPARC +# elif (defined(_LP64) || SANITIZER_X32 || \ + (defined(__mips__) && _MIPS_SIM == _ABIN32)) && \ + !SANITIZER_SPARC return internal_syscall(SYSCALL(newfstatat), AT_FDCWD, (uptr)path, (uptr)buf, AT_SYMLINK_NOFOLLOW); -# else +# elif SANITIZER_SPARC64 + kstat_t buf64; + int res = internal_syscall(SYSCALL(fstatat64), AT_FDCWD, (uptr)path, + (uptr)&buf64, AT_SYMLINK_NOFOLLOW); + kernel_stat_to_stat(&buf64, (struct stat *)buf); + return res; +# else struct stat64 buf64; int res = internal_syscall(SYSCALL(fstatat64), AT_FDCWD, (uptr)path, (uptr)&buf64, AT_SYMLINK_NOFOLLOW); stat64_to_stat(&buf64, (struct stat *)buf); return res; -# endif -# else +# endif +# else struct stat64 buf64; int res = internal_syscall(SYSCALL(lstat64), path, &buf64); stat64_to_stat(&buf64, (struct stat *)buf); return res; -# endif +# endif } uptr internal_fstat(fd_t fd, void *buf) { -#if SANITIZER_FREEBSD || SANITIZER_LINUX_USES_64BIT_SYSCALLS -#if SANITIZER_MIPS64 +# if SANITIZER_FREEBSD || SANITIZER_LINUX_USES_64BIT_SYSCALLS +# if SANITIZER_MIPS64 // For mips64, fstat syscall fills buffer in the format of kernel_stat - struct kernel_stat kbuf; + kstat_t kbuf; int res = internal_syscall(SYSCALL(fstat), fd, &kbuf); kernel_stat_to_stat(&kbuf, (struct stat *)buf); return res; +# elif SANITIZER_LINUX && SANITIZER_SPARC64 + // For sparc64, fstat64 syscall fills buffer in the format of kernel_stat64 + kstat_t kbuf; + int res = internal_syscall(SYSCALL(fstat64), fd, &kbuf); + kernel_stat_to_stat(&kbuf, (struct stat *)buf); + return res; # elif SANITIZER_LINUX && defined(__loongarch__) struct statx bufx; int res = internal_syscall(SYSCALL(statx), fd, "", AT_EMPTY_PATH, @@ -451,12 +493,12 @@ uptr internal_fstat(fd_t fd, void *buf) { # else return internal_syscall(SYSCALL(fstat), fd, (uptr)buf); # endif -#else +# else struct stat64 buf64; int res = internal_syscall(SYSCALL(fstat64), fd, &buf64); stat64_to_stat(&buf64, (struct stat *)buf); return res; -#endif +# endif } uptr internal_filesize(fd_t fd) { @@ -466,50 +508,46 @@ uptr internal_filesize(fd_t fd) { return (uptr)st.st_size; } -uptr internal_dup(int oldfd) { - return internal_syscall(SYSCALL(dup), oldfd); -} +uptr internal_dup(int oldfd) { return internal_syscall(SYSCALL(dup), oldfd); } uptr internal_dup2(int oldfd, int newfd) { # if SANITIZER_LINUX return internal_syscall(SYSCALL(dup3), oldfd, newfd, 0); -#else +# else return internal_syscall(SYSCALL(dup2), oldfd, newfd); -#endif +# endif } uptr internal_readlink(const char *path, char *buf, uptr bufsize) { # if SANITIZER_LINUX return internal_syscall(SYSCALL(readlinkat), AT_FDCWD, (uptr)path, (uptr)buf, bufsize); -#else +# else return internal_syscall(SYSCALL(readlink), (uptr)path, (uptr)buf, bufsize); -#endif +# endif } uptr internal_unlink(const char *path) { # if SANITIZER_LINUX return internal_syscall(SYSCALL(unlinkat), AT_FDCWD, (uptr)path, 0); -#else +# else return internal_syscall(SYSCALL(unlink), (uptr)path); -#endif +# endif } uptr internal_rename(const char *oldpath, const char *newpath) { -# if (defined(__riscv) || defined(__loongarch__)) && defined(__linux__) +# if (defined(__riscv) || defined(__loongarch__)) && defined(__linux__) return internal_syscall(SYSCALL(renameat2), AT_FDCWD, (uptr)oldpath, AT_FDCWD, (uptr)newpath, 0); -# elif SANITIZER_LINUX +# elif SANITIZER_LINUX return internal_syscall(SYSCALL(renameat), AT_FDCWD, (uptr)oldpath, AT_FDCWD, (uptr)newpath); -# else +# else return internal_syscall(SYSCALL(rename), (uptr)oldpath, (uptr)newpath); -# endif +# endif } -uptr internal_sched_yield() { - return internal_syscall(SYSCALL(sched_yield)); -} +uptr internal_sched_yield() { return internal_syscall(SYSCALL(sched_yield)); } void internal_usleep(u64 useconds) { struct timespec ts; @@ -523,18 +561,18 @@ uptr internal_execve(const char *filename, char *const argv[], return internal_syscall(SYSCALL(execve), (uptr)filename, (uptr)argv, (uptr)envp); } -#endif // !SANITIZER_SOLARIS && !SANITIZER_NETBSD +# endif // !SANITIZER_SOLARIS && !SANITIZER_NETBSD -#if !SANITIZER_NETBSD +# if !SANITIZER_NETBSD void internal__exit(int exitcode) { -#if SANITIZER_FREEBSD || SANITIZER_SOLARIS +# if SANITIZER_FREEBSD || SANITIZER_SOLARIS internal_syscall(SYSCALL(exit), exitcode); -#else +# else internal_syscall(SYSCALL(exit_group), exitcode); -#endif +# endif Die(); // Unreachable. } -#endif // !SANITIZER_NETBSD +# endif // !SANITIZER_NETBSD // ----------------- sanitizer_common.h bool FileExists(const char *filename) { @@ -556,30 +594,32 @@ bool DirExists(const char *path) { # if !SANITIZER_NETBSD tid_t GetTid() { -#if SANITIZER_FREEBSD +# if SANITIZER_FREEBSD long Tid; thr_self(&Tid); return Tid; -#elif SANITIZER_SOLARIS +# elif SANITIZER_SOLARIS return thr_self(); -#else +# else return internal_syscall(SYSCALL(gettid)); -#endif +# endif } int TgKill(pid_t pid, tid_t tid, int sig) { -#if SANITIZER_LINUX +# if SANITIZER_LINUX return internal_syscall(SYSCALL(tgkill), pid, tid, sig); -#elif SANITIZER_FREEBSD +# elif SANITIZER_FREEBSD return internal_syscall(SYSCALL(thr_kill2), pid, tid, sig); -#elif SANITIZER_SOLARIS +# elif SANITIZER_SOLARIS (void)pid; - return thr_kill(tid, sig); -#endif + errno = thr_kill(tid, sig); + // TgKill is expected to return -1 on error, not an errno. + return errno != 0 ? -1 : 0; +# endif } -#endif +# endif -#if SANITIZER_GLIBC +# if SANITIZER_GLIBC u64 NanoTime() { kernel_timeval tv; internal_memset(&tv, 0, sizeof(tv)); @@ -590,19 +630,19 @@ u64 NanoTime() { uptr internal_clock_gettime(__sanitizer_clockid_t clk_id, void *tp) { return internal_syscall(SYSCALL(clock_gettime), clk_id, tp); } -#elif !SANITIZER_SOLARIS && !SANITIZER_NETBSD +# elif !SANITIZER_SOLARIS && !SANITIZER_NETBSD u64 NanoTime() { struct timespec ts; clock_gettime(CLOCK_REALTIME, &ts); return (u64)ts.tv_sec * 1000 * 1000 * 1000 + ts.tv_nsec; } -#endif +# endif // Like getenv, but reads env directly from /proc (on Linux) or parses the // 'environ' array (on some others) and does not use libc. This function // should be called first inside __asan_init. const char *GetEnv(const char *name) { -#if SANITIZER_FREEBSD || SANITIZER_NETBSD || SANITIZER_SOLARIS +# if SANITIZER_FREEBSD || SANITIZER_NETBSD || SANITIZER_SOLARIS if (::environ != 0) { uptr NameLen = internal_strlen(name); for (char **Env = ::environ; *Env != 0; Env++) { @@ -611,7 +651,7 @@ const char *GetEnv(const char *name) { } } return 0; // Not found. -#elif SANITIZER_LINUX +# elif SANITIZER_LINUX static char *environ; static uptr len; static bool inited; @@ -621,13 +661,13 @@ const char *GetEnv(const char *name) { if (!ReadFileToBuffer("/proc/self/environ", &environ, &environ_size, &len)) environ = nullptr; } - if (!environ || len == 0) return nullptr; + if (!environ || len == 0) + return nullptr; uptr namelen = internal_strlen(name); const char *p = environ; while (*p != '\0') { // will happen at the \0\0 that terminates the buffer // proc file has the format NAME=value\0NAME=value\0NAME=value\0... - const char* endp = - (char*)internal_memchr(p, '\0', len - (p - environ)); + const char *endp = (char *)internal_memchr(p, '\0', len - (p - environ)); if (!endp) // this entry isn't NUL terminated return nullptr; else if (!internal_memcmp(p, name, namelen) && p[namelen] == '=') // Match. @@ -635,18 +675,18 @@ const char *GetEnv(const char *name) { p = endp + 1; } return nullptr; // Not found. -#else -#error "Unsupported platform" -#endif +# else +# error "Unsupported platform" +# endif } -#if !SANITIZER_FREEBSD && !SANITIZER_NETBSD && !SANITIZER_GO +# if !SANITIZER_FREEBSD && !SANITIZER_NETBSD && !SANITIZER_GO extern "C" { SANITIZER_WEAK_ATTRIBUTE extern void *__libc_stack_end; } -#endif +# endif -#if !SANITIZER_FREEBSD && !SANITIZER_NETBSD +# if !SANITIZER_FREEBSD && !SANITIZER_NETBSD static void ReadNullSepFileToArray(const char *path, char ***arr, int arr_size) { char *buff; @@ -659,20 +699,21 @@ static void ReadNullSepFileToArray(const char *path, char ***arr, } (*arr)[0] = buff; int count, i; - for (count = 1, i = 1; ; i++) { + for (count = 1, i = 1;; i++) { if (buff[i] == 0) { - if (buff[i+1] == 0) break; - (*arr)[count] = &buff[i+1]; + if (buff[i + 1] == 0) + break; + (*arr)[count] = &buff[i + 1]; CHECK_LE(count, arr_size - 1); // FIXME: make this more flexible. count++; } } (*arr)[count] = nullptr; } -#endif +# endif static void GetArgsAndEnv(char ***argv, char ***envp) { -#if SANITIZER_FREEBSD +# if SANITIZER_FREEBSD // On FreeBSD, retrieving the argument and environment arrays is done via the // kern.ps_strings sysctl, which returns a pointer to a structure containing // this information. See also <sys/exec.h>. @@ -684,30 +725,35 @@ static void GetArgsAndEnv(char ***argv, char ***envp) { } *argv = pss->ps_argvstr; *envp = pss->ps_envstr; -#elif SANITIZER_NETBSD +# elif SANITIZER_NETBSD *argv = __ps_strings->ps_argvstr; *envp = __ps_strings->ps_envstr; -#else // SANITIZER_FREEBSD -#if !SANITIZER_GO +# else // SANITIZER_FREEBSD +# if !SANITIZER_GO if (&__libc_stack_end) { - uptr* stack_end = (uptr*)__libc_stack_end; + uptr *stack_end = (uptr *)__libc_stack_end; + // Linux/sparc64 needs an adjustment, cf. glibc + // sysdeps/sparc/sparc{32,64}/dl-machine.h (DL_STACK_END). +# if SANITIZER_LINUX && defined(__sparc__) + stack_end = &stack_end[16]; +# endif // Normally argc can be obtained from *stack_end, however, on ARM glibc's // _start clobbers it: // https://sourceware.org/git/?p=glibc.git;a=blob;f=sysdeps/arm/start.S;hb=refs/heads/release/2.31/master#l75 // Do not special-case ARM and infer argc from argv everywhere. int argc = 0; while (stack_end[argc + 1]) argc++; - *argv = (char**)(stack_end + 1); - *envp = (char**)(stack_end + argc + 2); + *argv = (char **)(stack_end + 1); + *envp = (char **)(stack_end + argc + 2); } else { -#endif // !SANITIZER_GO +# endif // !SANITIZER_GO static const int kMaxArgv = 2000, kMaxEnvp = 2000; ReadNullSepFileToArray("/proc/self/cmdline", argv, kMaxArgv); ReadNullSepFileToArray("/proc/self/environ", envp, kMaxEnvp); -#if !SANITIZER_GO +# if !SANITIZER_GO } -#endif // !SANITIZER_GO -#endif // SANITIZER_FREEBSD +# endif // !SANITIZER_GO +# endif // SANITIZER_FREEBSD } char **GetArgv() { @@ -722,12 +768,12 @@ char **GetEnviron() { return envp; } -#if !SANITIZER_SOLARIS +# if !SANITIZER_SOLARIS void FutexWait(atomic_uint32_t *p, u32 cmp) { # if SANITIZER_FREEBSD _umtx_op(p, UMTX_OP_WAIT_UINT, cmp, 0, 0); # elif SANITIZER_NETBSD - sched_yield(); /* No userspace futex-like synchronization */ + sched_yield(); /* No userspace futex-like synchronization */ # else internal_syscall(SYSCALL(futex), (uptr)p, FUTEX_WAIT_PRIVATE, cmp, 0, 0, 0); # endif @@ -737,7 +783,7 @@ void FutexWake(atomic_uint32_t *p, u32 count) { # if SANITIZER_FREEBSD _umtx_op(p, UMTX_OP_WAKE, count, 0, 0); # elif SANITIZER_NETBSD - /* No userspace futex-like synchronization */ + /* No userspace futex-like synchronization */ # else internal_syscall(SYSCALL(futex), (uptr)p, FUTEX_WAKE_PRIVATE, count, 0, 0, 0); # endif @@ -749,26 +795,26 @@ void FutexWake(atomic_uint32_t *p, u32 count) { // The actual size of this structure is specified by d_reclen. // Note that getdents64 uses a different structure format. We only provide the // 32-bit syscall here. -#if SANITIZER_NETBSD +# if SANITIZER_NETBSD // Not used -#else +# else struct linux_dirent { # if SANITIZER_X32 || SANITIZER_LINUX u64 d_ino; u64 d_off; # else - unsigned long d_ino; - unsigned long d_off; + unsigned long d_ino; + unsigned long d_off; # endif - unsigned short d_reclen; + unsigned short d_reclen; # if SANITIZER_LINUX - unsigned char d_type; + unsigned char d_type; # endif - char d_name[256]; + char d_name[256]; }; -#endif +# endif -#if !SANITIZER_SOLARIS && !SANITIZER_NETBSD +# if !SANITIZER_SOLARIS && !SANITIZER_NETBSD // Syscall wrappers. uptr internal_ptrace(int request, int pid, void *addr, void *data) { return internal_syscall(SYSCALL(ptrace), request, pid, (uptr)addr, @@ -780,24 +826,20 @@ uptr internal_waitpid(int pid, int *status, int options) { 0 /* rusage */); } -uptr internal_getpid() { - return internal_syscall(SYSCALL(getpid)); -} +uptr internal_getpid() { return internal_syscall(SYSCALL(getpid)); } -uptr internal_getppid() { - return internal_syscall(SYSCALL(getppid)); -} +uptr internal_getppid() { return internal_syscall(SYSCALL(getppid)); } int internal_dlinfo(void *handle, int request, void *p) { -#if SANITIZER_FREEBSD +# if SANITIZER_FREEBSD return dlinfo(handle, request, p); -#else +# else UNIMPLEMENTED(); -#endif +# endif } uptr internal_getdents(fd_t fd, struct linux_dirent *dirp, unsigned int count) { -#if SANITIZER_FREEBSD +# if SANITIZER_FREEBSD return internal_syscall(SYSCALL(getdirentries), fd, (uptr)dirp, count, NULL); # elif SANITIZER_LINUX return internal_syscall(SYSCALL(getdents64), fd, (uptr)dirp, count); @@ -810,7 +852,7 @@ uptr internal_lseek(fd_t fd, OFF_T offset, int whence) { return internal_syscall(SYSCALL(lseek), fd, offset, whence); } -#if SANITIZER_LINUX +# if SANITIZER_LINUX uptr internal_prctl(int option, uptr arg2, uptr arg3, uptr arg4, uptr arg5) { return internal_syscall(SYSCALL(prctl), option, arg2, arg3, arg4, arg5); } @@ -827,10 +869,16 @@ uptr internal_sigaltstack(const void *ss, void *oss) { return internal_syscall(SYSCALL(sigaltstack), (uptr)ss, (uptr)oss); } +extern "C" pid_t __fork(void); + int internal_fork() { # if SANITIZER_LINUX # if SANITIZER_S390 return internal_syscall(SYSCALL(clone), 0, SIGCHLD); +# elif SANITIZER_SPARC + // The clone syscall interface on SPARC differs massively from the rest, + // so fall back to __fork. + return __fork(); # else return internal_syscall(SYSCALL(clone), SIGCHLD, 0); # endif @@ -839,7 +887,7 @@ int internal_fork() { # endif } -#if SANITIZER_FREEBSD +# if SANITIZER_FREEBSD int internal_sysctl(const int *name, unsigned int namelen, void *oldp, uptr *oldlenp, const void *newp, uptr newlen) { return internal_syscall(SYSCALL(__sysctl), name, namelen, oldp, @@ -854,11 +902,11 @@ int internal_sysctlbyname(const char *sname, void *oldp, uptr *oldlenp, // followed by sysctl(). To avoid calling the intercepted version and // asserting if this happens during startup, call the real sysctlnametomib() // followed by internal_sysctl() if the syscall is not available. -#ifdef SYS___sysctlbyname +# ifdef SYS___sysctlbyname return internal_syscall(SYSCALL(__sysctlbyname), sname, internal_strlen(sname), oldp, (size_t *)oldlenp, newp, (size_t)newlen); -#else +# else static decltype(sysctlnametomib) *real_sysctlnametomib = nullptr; if (!real_sysctlnametomib) real_sysctlnametomib = @@ -870,12 +918,12 @@ int internal_sysctlbyname(const char *sname, void *oldp, uptr *oldlenp, if (real_sysctlnametomib(sname, oid, &len) == -1) return (-1); return internal_sysctl(oid, len, oldp, oldlenp, newp, newlen); -#endif +# endif } -#endif +# endif -#if SANITIZER_LINUX -#define SA_RESTORER 0x04000000 +# if SANITIZER_LINUX +# define SA_RESTORER 0x04000000 // Doesn't set sa_restorer if the caller did not set it, so use with caution //(see below). int internal_sigaction_norestorer(int signum, const void *act, void *oldact) { @@ -899,15 +947,15 @@ int internal_sigaction_norestorer(int signum, const void *act, void *oldact) { // rt_sigaction, so we need to do the same (we'll need to reimplement the // restorers; for x86_64 the restorer address can be obtained from // oldact->sa_restorer upon a call to sigaction(xxx, NULL, oldact). -#if !SANITIZER_ANDROID || !SANITIZER_MIPS32 +# if !SANITIZER_ANDROID || !SANITIZER_MIPS32 k_act.sa_restorer = u_act->sa_restorer; -#endif +# endif } uptr result = internal_syscall(SYSCALL(rt_sigaction), (uptr)signum, - (uptr)(u_act ? &k_act : nullptr), - (uptr)(u_oldact ? &k_oldact : nullptr), - (uptr)sizeof(__sanitizer_kernel_sigset_t)); + (uptr)(u_act ? &k_act : nullptr), + (uptr)(u_oldact ? &k_oldact : nullptr), + (uptr)sizeof(__sanitizer_kernel_sigset_t)); if ((result == 0) && u_oldact) { u_oldact->handler = k_oldact.handler; @@ -915,24 +963,24 @@ int internal_sigaction_norestorer(int signum, const void *act, void *oldact) { internal_memcpy(&u_oldact->sa_mask, &k_oldact.sa_mask, sizeof(__sanitizer_kernel_sigset_t)); u_oldact->sa_flags = k_oldact.sa_flags; -#if !SANITIZER_ANDROID || !SANITIZER_MIPS32 +# if !SANITIZER_ANDROID || !SANITIZER_MIPS32 u_oldact->sa_restorer = k_oldact.sa_restorer; -#endif +# endif } return result; } -#endif // SANITIZER_LINUX +# endif // SANITIZER_LINUX uptr internal_sigprocmask(int how, __sanitizer_sigset_t *set, __sanitizer_sigset_t *oldset) { -#if SANITIZER_FREEBSD +# if SANITIZER_FREEBSD return internal_syscall(SYSCALL(sigprocmask), how, set, oldset); -#else +# else __sanitizer_kernel_sigset_t *k_set = (__sanitizer_kernel_sigset_t *)set; __sanitizer_kernel_sigset_t *k_oldset = (__sanitizer_kernel_sigset_t *)oldset; return internal_syscall(SYSCALL(rt_sigprocmask), (uptr)how, (uptr)k_set, (uptr)k_oldset, sizeof(__sanitizer_kernel_sigset_t)); -#endif +# endif } void internal_sigfillset(__sanitizer_sigset_t *set) { @@ -943,7 +991,7 @@ void internal_sigemptyset(__sanitizer_sigset_t *set) { internal_memset(set, 0, sizeof(*set)); } -#if SANITIZER_LINUX +# if SANITIZER_LINUX void internal_sigdelset(__sanitizer_sigset_t *set, int signum) { signum -= 1; CHECK_GE(signum, 0); @@ -963,7 +1011,7 @@ bool internal_sigismember(__sanitizer_sigset_t *set, int signum) { const uptr bit = signum % (sizeof(k_set->sig[0]) * 8); return k_set->sig[idx] & ((uptr)1 << bit); } -#elif SANITIZER_FREEBSD +# elif SANITIZER_FREEBSD uptr internal_procctl(int type, int id, int cmd, void *data) { return internal_syscall(SYSCALL(procctl), type, id, cmd, data); } @@ -977,39 +1025,34 @@ bool internal_sigismember(__sanitizer_sigset_t *set, int signum) { sigset_t *rset = reinterpret_cast<sigset_t *>(set); return sigismember(rset, signum); } -#endif -#endif // !SANITIZER_SOLARIS +# endif +# endif // !SANITIZER_SOLARIS -#if !SANITIZER_NETBSD +# if !SANITIZER_NETBSD // ThreadLister implementation. -ThreadLister::ThreadLister(pid_t pid) : pid_(pid), buffer_(4096) { - char task_directory_path[80]; - internal_snprintf(task_directory_path, sizeof(task_directory_path), - "/proc/%d/task/", pid); - descriptor_ = internal_open(task_directory_path, O_RDONLY | O_DIRECTORY); - if (internal_iserror(descriptor_)) { - Report("Can't open /proc/%d/task for reading.\n", pid); - } +ThreadLister::ThreadLister(pid_t pid) : buffer_(4096) { + task_path_.AppendF("/proc/%d/task", pid); } ThreadLister::Result ThreadLister::ListThreads( InternalMmapVector<tid_t> *threads) { - if (internal_iserror(descriptor_)) + int descriptor = internal_open(task_path_.data(), O_RDONLY | O_DIRECTORY); + if (internal_iserror(descriptor)) { + Report("Can't open %s for reading.\n", task_path_.data()); return Error; - internal_lseek(descriptor_, 0, SEEK_SET); + } + auto cleanup = at_scope_exit([&] { internal_close(descriptor); }); threads->clear(); Result result = Ok; for (bool first_read = true;; first_read = false) { - // Resize to max capacity if it was downsized by IsAlive. - buffer_.resize(buffer_.capacity()); CHECK_GE(buffer_.size(), 4096); uptr read = internal_getdents( - descriptor_, (struct linux_dirent *)buffer_.data(), buffer_.size()); + descriptor, (struct linux_dirent *)buffer_.data(), buffer_.size()); if (!read) return result; if (internal_iserror(read)) { - Report("Can't read directory entries from /proc/%d/task.\n", pid_); + Report("Can't read directory entries from %s.\n", task_path_.data()); return Error; } @@ -1047,45 +1090,53 @@ ThreadLister::Result ThreadLister::ListThreads( } } -bool ThreadLister::IsAlive(int tid) { +const char *ThreadLister::LoadStatus(tid_t tid) { + status_path_.clear(); + status_path_.AppendF("%s/%llu/status", task_path_.data(), tid); + auto cleanup = at_scope_exit([&] { + // Resize back to capacity if it is downsized by `ReadFileToVector`. + buffer_.resize(buffer_.capacity()); + }); + if (!ReadFileToVector(status_path_.data(), &buffer_) || buffer_.empty()) + return nullptr; + buffer_.push_back('\0'); + return buffer_.data(); +} + +bool ThreadLister::IsAlive(tid_t tid) { // /proc/%d/task/%d/status uses same call to detect alive threads as // proc_task_readdir. See task_state implementation in Linux. - char path[80]; - internal_snprintf(path, sizeof(path), "/proc/%d/task/%d/status", pid_, tid); - if (!ReadFileToVector(path, &buffer_) || buffer_.empty()) - return false; - buffer_.push_back(0); static const char kPrefix[] = "\nPPid:"; - const char *field = internal_strstr(buffer_.data(), kPrefix); + const char *status = LoadStatus(tid); + if (!status) + return false; + const char *field = internal_strstr(status, kPrefix); if (!field) return false; field += internal_strlen(kPrefix); return (int)internal_atoll(field) != 0; } -ThreadLister::~ThreadLister() { - if (!internal_iserror(descriptor_)) - internal_close(descriptor_); -} -#endif +# endif -#if SANITIZER_WORDSIZE == 32 +# if SANITIZER_WORDSIZE == 32 // Take care of unusable kernel area in top gigabyte. static uptr GetKernelAreaSize() { -#if SANITIZER_LINUX && !SANITIZER_X32 +# if SANITIZER_LINUX && !SANITIZER_X32 const uptr gbyte = 1UL << 30; // Firstly check if there are writable segments // mapped to top gigabyte (e.g. stack). - MemoryMappingLayout proc_maps(/*cache_enabled*/true); + MemoryMappingLayout proc_maps(/*cache_enabled*/ true); if (proc_maps.Error()) return 0; MemoryMappedSegment segment; while (proc_maps.Next(&segment)) { - if ((segment.end >= 3 * gbyte) && segment.IsWritable()) return 0; + if ((segment.end >= 3 * gbyte) && segment.IsWritable()) + return 0; } -#if !SANITIZER_ANDROID +# if !SANITIZER_ANDROID // Even if nothing is mapped, top Gb may still be accessible // if we are running on 64-bit kernel. // Uname may report misleading results if personality type @@ -1095,21 +1146,22 @@ static uptr GetKernelAreaSize() { if (!(pers & PER_MASK) && internal_uname(&uname_info) == 0 && internal_strstr(uname_info.machine, "64")) return 0; -#endif // SANITIZER_ANDROID +# endif // SANITIZER_ANDROID // Top gigabyte is reserved for kernel. return gbyte; -#else +# else return 0; -#endif // SANITIZER_LINUX && !SANITIZER_X32 +# endif // SANITIZER_LINUX && !SANITIZER_X32 } -#endif // SANITIZER_WORDSIZE == 32 +# endif // SANITIZER_WORDSIZE == 32 uptr GetMaxVirtualAddress() { -#if SANITIZER_NETBSD && defined(__x86_64__) +# if SANITIZER_NETBSD && defined(__x86_64__) return 0x7f7ffffff000ULL; // (0x00007f8000000000 - PAGE_SIZE) -#elif SANITIZER_WORDSIZE == 64 -# if defined(__powerpc64__) || defined(__aarch64__) || defined(__loongarch__) +# elif SANITIZER_WORDSIZE == 64 +# if defined(__powerpc64__) || defined(__aarch64__) || \ + defined(__loongarch__) || SANITIZER_RISCV64 // On PowerPC64 we have two different address space layouts: 44- and 46-bit. // We somehow need to figure out which one we are using now and choose // one of 0x00000fffffffffffUL and 0x00003fffffffffffUL. @@ -1118,97 +1170,97 @@ uptr GetMaxVirtualAddress() { // This should (does) work for both PowerPC64 Endian modes. // Similarly, aarch64 has multiple address space layouts: 39, 42 and 47-bit. // loongarch64 also has multiple address space layouts: default is 47-bit. + // RISC-V 64 also has multiple address space layouts: 39, 48 and 57-bit. return (1ULL << (MostSignificantSetBitIndex(GET_CURRENT_FRAME()) + 1)) - 1; -#elif SANITIZER_RISCV64 - return (1ULL << 38) - 1; -# elif SANITIZER_MIPS64 +# elif SANITIZER_MIPS64 return (1ULL << 40) - 1; // 0x000000ffffffffffUL; -# elif defined(__s390x__) +# elif defined(__s390x__) return (1ULL << 53) - 1; // 0x001fffffffffffffUL; -#elif defined(__sparc__) +# elif defined(__sparc__) return ~(uptr)0; -# else +# else return (1ULL << 47) - 1; // 0x00007fffffffffffUL; -# endif -#else // SANITIZER_WORDSIZE == 32 -# if defined(__s390__) +# endif +# else // SANITIZER_WORDSIZE == 32 +# if defined(__s390__) return (1ULL << 31) - 1; // 0x7fffffff; -# else +# else return (1ULL << 32) - 1; // 0xffffffff; -# endif -#endif // SANITIZER_WORDSIZE +# endif +# endif // SANITIZER_WORDSIZE } uptr GetMaxUserVirtualAddress() { uptr addr = GetMaxVirtualAddress(); -#if SANITIZER_WORDSIZE == 32 && !defined(__s390__) +# if SANITIZER_WORDSIZE == 32 && !defined(__s390__) if (!common_flags()->full_address_space) addr -= GetKernelAreaSize(); CHECK_LT(reinterpret_cast<uptr>(&addr), addr); -#endif +# endif return addr; } -#if !SANITIZER_ANDROID +# if !SANITIZER_ANDROID || defined(__aarch64__) uptr GetPageSize() { -#if SANITIZER_LINUX && (defined(__x86_64__) || defined(__i386__)) && \ - defined(EXEC_PAGESIZE) +# if SANITIZER_LINUX && (defined(__x86_64__) || defined(__i386__)) && \ + defined(EXEC_PAGESIZE) return EXEC_PAGESIZE; -#elif SANITIZER_FREEBSD || SANITIZER_NETBSD -// Use sysctl as sysconf can trigger interceptors internally. +# elif SANITIZER_FREEBSD || SANITIZER_NETBSD + // Use sysctl as sysconf can trigger interceptors internally. int pz = 0; uptr pzl = sizeof(pz); int mib[2] = {CTL_HW, HW_PAGESIZE}; int rv = internal_sysctl(mib, 2, &pz, &pzl, nullptr, 0); CHECK_EQ(rv, 0); return (uptr)pz; -#elif SANITIZER_USE_GETAUXVAL +# elif SANITIZER_USE_GETAUXVAL return getauxval(AT_PAGESZ); -#else +# else return sysconf(_SC_PAGESIZE); // EXEC_PAGESIZE may not be trustworthy. -#endif +# endif } -#endif // !SANITIZER_ANDROID +# endif -uptr ReadBinaryName(/*out*/char *buf, uptr buf_len) { -#if SANITIZER_SOLARIS +uptr ReadBinaryName(/*out*/ char *buf, uptr buf_len) { +# if SANITIZER_SOLARIS const char *default_module_name = getexecname(); CHECK_NE(default_module_name, NULL); return internal_snprintf(buf, buf_len, "%s", default_module_name); -#else -#if SANITIZER_FREEBSD || SANITIZER_NETBSD -#if SANITIZER_FREEBSD +# else +# if SANITIZER_FREEBSD || SANITIZER_NETBSD +# if SANITIZER_FREEBSD const int Mib[4] = {CTL_KERN, KERN_PROC, KERN_PROC_PATHNAME, -1}; -#else +# else const int Mib[4] = {CTL_KERN, KERN_PROC_ARGS, -1, KERN_PROC_PATHNAME}; -#endif +# endif const char *default_module_name = "kern.proc.pathname"; uptr Size = buf_len; bool IsErr = (internal_sysctl(Mib, ARRAY_SIZE(Mib), buf, &Size, NULL, 0) != 0); int readlink_error = IsErr ? errno : 0; uptr module_name_len = Size; -#else +# else const char *default_module_name = "/proc/self/exe"; - uptr module_name_len = internal_readlink( - default_module_name, buf, buf_len); + uptr module_name_len = internal_readlink(default_module_name, buf, buf_len); int readlink_error; bool IsErr = internal_iserror(module_name_len, &readlink_error); -#endif // SANITIZER_SOLARIS +# endif if (IsErr) { // We can't read binary name for some reason, assume it's unknown. - Report("WARNING: reading executable name failed with errno %d, " - "some stack frames may not be symbolized\n", readlink_error); - module_name_len = internal_snprintf(buf, buf_len, "%s", - default_module_name); + Report( + "WARNING: reading executable name failed with errno %d, " + "some stack frames may not be symbolized\n", + readlink_error); + module_name_len = + internal_snprintf(buf, buf_len, "%s", default_module_name); CHECK_LT(module_name_len, buf_len); } return module_name_len; -#endif +# endif } uptr ReadLongProcessName(/*out*/ char *buf, uptr buf_len) { -#if SANITIZER_LINUX +# if SANITIZER_LINUX char *tmpbuf; uptr tmpsize; uptr tmplen; @@ -1218,7 +1270,7 @@ uptr ReadLongProcessName(/*out*/ char *buf, uptr buf_len) { UnmapOrDie(tmpbuf, tmpsize); return internal_strlen(buf); } -#endif +# endif return ReadBinaryName(buf, buf_len); } @@ -1228,20 +1280,22 @@ bool LibraryNameIs(const char *full_name, const char *base_name) { // Strip path. while (*name != '\0') name++; while (name > full_name && *name != '/') name--; - if (*name == '/') name++; + if (*name == '/') + name++; uptr base_name_length = internal_strlen(base_name); - if (internal_strncmp(name, base_name, base_name_length)) return false; + if (internal_strncmp(name, base_name, base_name_length)) + return false; return (name[base_name_length] == '-' || name[base_name_length] == '.'); } -#if !SANITIZER_ANDROID +# if !SANITIZER_ANDROID // Call cb for each region mapped by map. void ForEachMappedRegion(link_map *map, void (*cb)(const void *, uptr)) { CHECK_NE(map, nullptr); -#if !SANITIZER_FREEBSD +# if !SANITIZER_FREEBSD typedef ElfW(Phdr) Elf_Phdr; typedef ElfW(Ehdr) Elf_Ehdr; -#endif // !SANITIZER_FREEBSD +# endif // !SANITIZER_FREEBSD char *base = (char *)map->l_addr; Elf_Ehdr *ehdr = (Elf_Ehdr *)base; char *phdrs = base + ehdr->e_phoff; @@ -1273,10 +1327,10 @@ void ForEachMappedRegion(link_map *map, void (*cb)(const void *, uptr)) { } } } -#endif +# endif -#if SANITIZER_LINUX -#if defined(__x86_64__) +# if SANITIZER_LINUX +# if defined(__x86_64__) // We cannot use glibc's clone wrapper, because it messes with the child // task's TLS. It writes the PID and TID of the child task to its thread // descriptor, but in our case the child task shares the thread descriptor with @@ -1295,50 +1349,46 @@ uptr internal_clone(int (*fn)(void *), void *child_stack, int flags, void *arg, register void *r8 __asm__("r8") = newtls; register int *r10 __asm__("r10") = child_tidptr; __asm__ __volatile__( - /* %rax = syscall(%rax = SYSCALL(clone), - * %rdi = flags, - * %rsi = child_stack, - * %rdx = parent_tidptr, - * %r8 = new_tls, - * %r10 = child_tidptr) - */ - "syscall\n" - - /* if (%rax != 0) - * return; - */ - "testq %%rax,%%rax\n" - "jnz 1f\n" - - /* In the child. Terminate unwind chain. */ - // XXX: We should also terminate the CFI unwind chain - // here. Unfortunately clang 3.2 doesn't support the - // necessary CFI directives, so we skip that part. - "xorq %%rbp,%%rbp\n" - - /* Call "fn(arg)". */ - "popq %%rax\n" - "popq %%rdi\n" - "call *%%rax\n" - - /* Call _exit(%rax). */ - "movq %%rax,%%rdi\n" - "movq %2,%%rax\n" - "syscall\n" - - /* Return to parent. */ - "1:\n" - : "=a" (res) - : "a"(SYSCALL(clone)), "i"(SYSCALL(exit)), - "S"(child_stack), - "D"(flags), - "d"(parent_tidptr), - "r"(r8), - "r"(r10) - : "memory", "r11", "rcx"); + /* %rax = syscall(%rax = SYSCALL(clone), + * %rdi = flags, + * %rsi = child_stack, + * %rdx = parent_tidptr, + * %r8 = new_tls, + * %r10 = child_tidptr) + */ + "syscall\n" + + /* if (%rax != 0) + * return; + */ + "testq %%rax,%%rax\n" + "jnz 1f\n" + + /* In the child. Terminate unwind chain. */ + // XXX: We should also terminate the CFI unwind chain + // here. Unfortunately clang 3.2 doesn't support the + // necessary CFI directives, so we skip that part. + "xorq %%rbp,%%rbp\n" + + /* Call "fn(arg)". */ + "popq %%rax\n" + "popq %%rdi\n" + "call *%%rax\n" + + /* Call _exit(%rax). */ + "movq %%rax,%%rdi\n" + "movq %2,%%rax\n" + "syscall\n" + + /* Return to parent. */ + "1:\n" + : "=a"(res) + : "a"(SYSCALL(clone)), "i"(SYSCALL(exit)), "S"(child_stack), "D"(flags), + "d"(parent_tidptr), "r"(r8), "r"(r10) + : "memory", "r11", "rcx"); return res; } -#elif defined(__mips__) +# elif defined(__mips__) uptr internal_clone(int (*fn)(void *), void *child_stack, int flags, void *arg, int *parent_tidptr, void *newtls, int *child_tidptr) { long long res; @@ -1353,68 +1403,63 @@ uptr internal_clone(int (*fn)(void *), void *child_stack, int flags, void *arg, // We don't have proper CFI directives here because it requires alot of code // for very marginal benefits. __asm__ __volatile__( - /* $v0 = syscall($v0 = __NR_clone, - * $a0 = flags, - * $a1 = child_stack, - * $a2 = parent_tidptr, - * $a3 = new_tls, - * $a4 = child_tidptr) - */ - ".cprestore 16;\n" - "move $4,%1;\n" - "move $5,%2;\n" - "move $6,%3;\n" - "move $7,%4;\n" - /* Store the fifth argument on stack - * if we are using 32-bit abi. - */ -#if SANITIZER_WORDSIZE == 32 - "lw %5,16($29);\n" -#else - "move $8,%5;\n" -#endif - "li $2,%6;\n" - "syscall;\n" - - /* if ($v0 != 0) - * return; - */ - "bnez $2,1f;\n" - - /* Call "fn(arg)". */ -#if SANITIZER_WORDSIZE == 32 -#ifdef __BIG_ENDIAN__ - "lw $25,4($29);\n" - "lw $4,12($29);\n" -#else - "lw $25,0($29);\n" - "lw $4,8($29);\n" -#endif -#else - "ld $25,0($29);\n" - "ld $4,8($29);\n" -#endif - "jal $25;\n" - - /* Call _exit($v0). */ - "move $4,$2;\n" - "li $2,%7;\n" - "syscall;\n" - - /* Return to parent. */ - "1:\n" - : "=r" (res) - : "r"(flags), - "r"(child_stack), - "r"(parent_tidptr), - "r"(a3), - "r"(a4), - "i"(__NR_clone), - "i"(__NR_exit) - : "memory", "$29" ); + /* $v0 = syscall($v0 = __NR_clone, + * $a0 = flags, + * $a1 = child_stack, + * $a2 = parent_tidptr, + * $a3 = new_tls, + * $a4 = child_tidptr) + */ + ".cprestore 16;\n" + "move $4,%1;\n" + "move $5,%2;\n" + "move $6,%3;\n" + "move $7,%4;\n" + /* Store the fifth argument on stack + * if we are using 32-bit abi. + */ +# if SANITIZER_WORDSIZE == 32 + "lw %5,16($29);\n" +# else + "move $8,%5;\n" +# endif + "li $2,%6;\n" + "syscall;\n" + + /* if ($v0 != 0) + * return; + */ + "bnez $2,1f;\n" + + /* Call "fn(arg)". */ +# if SANITIZER_WORDSIZE == 32 +# ifdef __BIG_ENDIAN__ + "lw $25,4($29);\n" + "lw $4,12($29);\n" +# else + "lw $25,0($29);\n" + "lw $4,8($29);\n" +# endif +# else + "ld $25,0($29);\n" + "ld $4,8($29);\n" +# endif + "jal $25;\n" + + /* Call _exit($v0). */ + "move $4,$2;\n" + "li $2,%7;\n" + "syscall;\n" + + /* Return to parent. */ + "1:\n" + : "=r"(res) + : "r"(flags), "r"(child_stack), "r"(parent_tidptr), "r"(a3), "r"(a4), + "i"(__NR_clone), "i"(__NR_exit) + : "memory", "$29"); return res; } -#elif SANITIZER_RISCV64 +# elif SANITIZER_RISCV64 uptr internal_clone(int (*fn)(void *), void *child_stack, int flags, void *arg, int *parent_tidptr, void *newtls, int *child_tidptr) { if (!fn || !child_stack) @@ -1455,7 +1500,7 @@ uptr internal_clone(int (*fn)(void *), void *child_stack, int flags, void *arg, : "memory"); return res; } -#elif defined(__aarch64__) +# elif defined(__aarch64__) uptr internal_clone(int (*fn)(void *), void *child_stack, int flags, void *arg, int *parent_tidptr, void *newtls, int *child_tidptr) { register long long res __asm__("x0"); @@ -1466,47 +1511,45 @@ uptr internal_clone(int (*fn)(void *), void *child_stack, int flags, void *arg, ((unsigned long long *)child_stack)[0] = (uptr)fn; ((unsigned long long *)child_stack)[1] = (uptr)arg; - register int (*__fn)(void *) __asm__("x0") = fn; + register int (*__fn)(void *) __asm__("x0") = fn; register void *__stack __asm__("x1") = child_stack; - register int __flags __asm__("x2") = flags; - register void *__arg __asm__("x3") = arg; - register int *__ptid __asm__("x4") = parent_tidptr; - register void *__tls __asm__("x5") = newtls; - register int *__ctid __asm__("x6") = child_tidptr; + register int __flags __asm__("x2") = flags; + register void *__arg __asm__("x3") = arg; + register int *__ptid __asm__("x4") = parent_tidptr; + register void *__tls __asm__("x5") = newtls; + register int *__ctid __asm__("x6") = child_tidptr; __asm__ __volatile__( - "mov x0,x2\n" /* flags */ - "mov x2,x4\n" /* ptid */ - "mov x3,x5\n" /* tls */ - "mov x4,x6\n" /* ctid */ - "mov x8,%9\n" /* clone */ - - "svc 0x0\n" - - /* if (%r0 != 0) - * return %r0; - */ - "cmp x0, #0\n" - "bne 1f\n" - - /* In the child, now. Call "fn(arg)". */ - "ldp x1, x0, [sp], #16\n" - "blr x1\n" - - /* Call _exit(%r0). */ - "mov x8, %10\n" - "svc 0x0\n" - "1:\n" - - : "=r" (res) - : "i"(-EINVAL), - "r"(__fn), "r"(__stack), "r"(__flags), "r"(__arg), - "r"(__ptid), "r"(__tls), "r"(__ctid), - "i"(__NR_clone), "i"(__NR_exit) - : "x30", "memory"); + "mov x0,x2\n" /* flags */ + "mov x2,x4\n" /* ptid */ + "mov x3,x5\n" /* tls */ + "mov x4,x6\n" /* ctid */ + "mov x8,%9\n" /* clone */ + + "svc 0x0\n" + + /* if (%r0 != 0) + * return %r0; + */ + "cmp x0, #0\n" + "bne 1f\n" + + /* In the child, now. Call "fn(arg)". */ + "ldp x1, x0, [sp], #16\n" + "blr x1\n" + + /* Call _exit(%r0). */ + "mov x8, %10\n" + "svc 0x0\n" + "1:\n" + + : "=r"(res) + : "i"(-EINVAL), "r"(__fn), "r"(__stack), "r"(__flags), "r"(__arg), + "r"(__ptid), "r"(__tls), "r"(__ctid), "i"(__NR_clone), "i"(__NR_exit) + : "x30", "memory"); return res; } -#elif SANITIZER_LOONGARCH64 +# elif SANITIZER_LOONGARCH64 uptr internal_clone(int (*fn)(void *), void *child_stack, int flags, void *arg, int *parent_tidptr, void *newtls, int *child_tidptr) { if (!fn || !child_stack) @@ -1544,119 +1587,110 @@ uptr internal_clone(int (*fn)(void *), void *child_stack, int flags, void *arg, : "=r"(res) : "0"(__flags), "r"(__stack), "r"(__ptid), "r"(__ctid), "r"(__tls), "r"(__fn), "r"(__arg), "r"(nr_clone), "i"(__NR_exit) - : "memory", "$t0", "$t1", "$t2", "$t3", "$t4", "$t5", "$t6", "$t7", "$t8"); + : "memory", "$t0", "$t1", "$t2", "$t3", "$t4", "$t5", "$t6", "$t7", + "$t8"); return res; } -#elif defined(__powerpc64__) +# elif defined(__powerpc64__) uptr internal_clone(int (*fn)(void *), void *child_stack, int flags, void *arg, - int *parent_tidptr, void *newtls, int *child_tidptr) { + int *parent_tidptr, void *newtls, int *child_tidptr) { long long res; // Stack frame structure. -#if SANITIZER_PPC64V1 -// Back chain == 0 (SP + 112) -// Frame (112 bytes): -// Parameter save area (SP + 48), 8 doublewords -// TOC save area (SP + 40) -// Link editor doubleword (SP + 32) -// Compiler doubleword (SP + 24) -// LR save area (SP + 16) -// CR save area (SP + 8) -// Back chain (SP + 0) -# define FRAME_SIZE 112 -# define FRAME_TOC_SAVE_OFFSET 40 -#elif SANITIZER_PPC64V2 -// Back chain == 0 (SP + 32) -// Frame (32 bytes): -// TOC save area (SP + 24) -// LR save area (SP + 16) -// CR save area (SP + 8) -// Back chain (SP + 0) -# define FRAME_SIZE 32 -# define FRAME_TOC_SAVE_OFFSET 24 -#else -# error "Unsupported PPC64 ABI" -#endif +# if SANITIZER_PPC64V1 + // Back chain == 0 (SP + 112) + // Frame (112 bytes): + // Parameter save area (SP + 48), 8 doublewords + // TOC save area (SP + 40) + // Link editor doubleword (SP + 32) + // Compiler doubleword (SP + 24) + // LR save area (SP + 16) + // CR save area (SP + 8) + // Back chain (SP + 0) +# define FRAME_SIZE 112 +# define FRAME_TOC_SAVE_OFFSET 40 +# elif SANITIZER_PPC64V2 + // Back chain == 0 (SP + 32) + // Frame (32 bytes): + // TOC save area (SP + 24) + // LR save area (SP + 16) + // CR save area (SP + 8) + // Back chain (SP + 0) +# define FRAME_SIZE 32 +# define FRAME_TOC_SAVE_OFFSET 24 +# else +# error "Unsupported PPC64 ABI" +# endif if (!fn || !child_stack) return -EINVAL; CHECK_EQ(0, (uptr)child_stack % 16); register int (*__fn)(void *) __asm__("r3") = fn; - register void *__cstack __asm__("r4") = child_stack; - register int __flags __asm__("r5") = flags; - register void *__arg __asm__("r6") = arg; - register int *__ptidptr __asm__("r7") = parent_tidptr; - register void *__newtls __asm__("r8") = newtls; - register int *__ctidptr __asm__("r9") = child_tidptr; - - __asm__ __volatile__( - /* fn and arg are saved across the syscall */ - "mr 28, %5\n\t" - "mr 27, %8\n\t" - - /* syscall - r0 == __NR_clone - r3 == flags - r4 == child_stack - r5 == parent_tidptr - r6 == newtls - r7 == child_tidptr */ - "mr 3, %7\n\t" - "mr 5, %9\n\t" - "mr 6, %10\n\t" - "mr 7, %11\n\t" - "li 0, %3\n\t" - "sc\n\t" - - /* Test if syscall was successful */ - "cmpdi cr1, 3, 0\n\t" - "crandc cr1*4+eq, cr1*4+eq, cr0*4+so\n\t" - "bne- cr1, 1f\n\t" - - /* Set up stack frame */ - "li 29, 0\n\t" - "stdu 29, -8(1)\n\t" - "stdu 1, -%12(1)\n\t" - /* Do the function call */ - "std 2, %13(1)\n\t" -#if SANITIZER_PPC64V1 - "ld 0, 0(28)\n\t" - "ld 2, 8(28)\n\t" - "mtctr 0\n\t" -#elif SANITIZER_PPC64V2 - "mr 12, 28\n\t" - "mtctr 12\n\t" -#else -# error "Unsupported PPC64 ABI" -#endif - "mr 3, 27\n\t" - "bctrl\n\t" - "ld 2, %13(1)\n\t" - - /* Call _exit(r3) */ - "li 0, %4\n\t" - "sc\n\t" - - /* Return to parent */ - "1:\n\t" - "mr %0, 3\n\t" - : "=r" (res) - : "0" (-1), - "i" (EINVAL), - "i" (__NR_clone), - "i" (__NR_exit), - "r" (__fn), - "r" (__cstack), - "r" (__flags), - "r" (__arg), - "r" (__ptidptr), - "r" (__newtls), - "r" (__ctidptr), - "i" (FRAME_SIZE), - "i" (FRAME_TOC_SAVE_OFFSET) - : "cr0", "cr1", "memory", "ctr", "r0", "r27", "r28", "r29"); + register void *__cstack __asm__("r4") = child_stack; + register int __flags __asm__("r5") = flags; + register void *__arg __asm__("r6") = arg; + register int *__ptidptr __asm__("r7") = parent_tidptr; + register void *__newtls __asm__("r8") = newtls; + register int *__ctidptr __asm__("r9") = child_tidptr; + + __asm__ __volatile__( + /* fn and arg are saved across the syscall */ + "mr 28, %5\n\t" + "mr 27, %8\n\t" + + /* syscall + r0 == __NR_clone + r3 == flags + r4 == child_stack + r5 == parent_tidptr + r6 == newtls + r7 == child_tidptr */ + "mr 3, %7\n\t" + "mr 5, %9\n\t" + "mr 6, %10\n\t" + "mr 7, %11\n\t" + "li 0, %3\n\t" + "sc\n\t" + + /* Test if syscall was successful */ + "cmpdi cr1, 3, 0\n\t" + "crandc cr1*4+eq, cr1*4+eq, cr0*4+so\n\t" + "bne- cr1, 1f\n\t" + + /* Set up stack frame */ + "li 29, 0\n\t" + "stdu 29, -8(1)\n\t" + "stdu 1, -%12(1)\n\t" + /* Do the function call */ + "std 2, %13(1)\n\t" +# if SANITIZER_PPC64V1 + "ld 0, 0(28)\n\t" + "ld 2, 8(28)\n\t" + "mtctr 0\n\t" +# elif SANITIZER_PPC64V2 + "mr 12, 28\n\t" + "mtctr 12\n\t" +# else +# error "Unsupported PPC64 ABI" +# endif + "mr 3, 27\n\t" + "bctrl\n\t" + "ld 2, %13(1)\n\t" + + /* Call _exit(r3) */ + "li 0, %4\n\t" + "sc\n\t" + + /* Return to parent */ + "1:\n\t" + "mr %0, 3\n\t" + : "=r"(res) + : "0"(-1), "i"(EINVAL), "i"(__NR_clone), "i"(__NR_exit), "r"(__fn), + "r"(__cstack), "r"(__flags), "r"(__arg), "r"(__ptidptr), "r"(__newtls), + "r"(__ctidptr), "i"(FRAME_SIZE), "i"(FRAME_TOC_SAVE_OFFSET) + : "cr0", "cr1", "memory", "ctr", "r0", "r27", "r28", "r29"); return res; } -#elif defined(__i386__) +# elif defined(__i386__) uptr internal_clone(int (*fn)(void *), void *child_stack, int flags, void *arg, int *parent_tidptr, void *newtls, int *child_tidptr) { int res; @@ -1669,59 +1703,56 @@ uptr internal_clone(int (*fn)(void *), void *child_stack, int flags, void *arg, ((unsigned int *)child_stack)[2] = (uptr)fn; ((unsigned int *)child_stack)[3] = (uptr)arg; __asm__ __volatile__( - /* %eax = syscall(%eax = SYSCALL(clone), - * %ebx = flags, - * %ecx = child_stack, - * %edx = parent_tidptr, - * %esi = new_tls, - * %edi = child_tidptr) - */ - - /* Obtain flags */ - "movl (%%ecx), %%ebx\n" - /* Do the system call */ - "pushl %%ebx\n" - "pushl %%esi\n" - "pushl %%edi\n" - /* Remember the flag value. */ - "movl %%ebx, (%%ecx)\n" - "int $0x80\n" - "popl %%edi\n" - "popl %%esi\n" - "popl %%ebx\n" - - /* if (%eax != 0) - * return; - */ - - "test %%eax,%%eax\n" - "jnz 1f\n" - - /* terminate the stack frame */ - "xorl %%ebp,%%ebp\n" - /* Call FN. */ - "call *%%ebx\n" -#ifdef PIC - "call here\n" - "here:\n" - "popl %%ebx\n" - "addl $_GLOBAL_OFFSET_TABLE_+[.-here], %%ebx\n" -#endif - /* Call exit */ - "movl %%eax, %%ebx\n" - "movl %2, %%eax\n" - "int $0x80\n" - "1:\n" - : "=a" (res) - : "a"(SYSCALL(clone)), "i"(SYSCALL(exit)), - "c"(child_stack), - "d"(parent_tidptr), - "S"(newtls), - "D"(child_tidptr) - : "memory"); + /* %eax = syscall(%eax = SYSCALL(clone), + * %ebx = flags, + * %ecx = child_stack, + * %edx = parent_tidptr, + * %esi = new_tls, + * %edi = child_tidptr) + */ + + /* Obtain flags */ + "movl (%%ecx), %%ebx\n" + /* Do the system call */ + "pushl %%ebx\n" + "pushl %%esi\n" + "pushl %%edi\n" + /* Remember the flag value. */ + "movl %%ebx, (%%ecx)\n" + "int $0x80\n" + "popl %%edi\n" + "popl %%esi\n" + "popl %%ebx\n" + + /* if (%eax != 0) + * return; + */ + + "test %%eax,%%eax\n" + "jnz 1f\n" + + /* terminate the stack frame */ + "xorl %%ebp,%%ebp\n" + /* Call FN. */ + "call *%%ebx\n" +# ifdef PIC + "call here\n" + "here:\n" + "popl %%ebx\n" + "addl $_GLOBAL_OFFSET_TABLE_+[.-here], %%ebx\n" +# endif + /* Call exit */ + "movl %%eax, %%ebx\n" + "movl %2, %%eax\n" + "int $0x80\n" + "1:\n" + : "=a"(res) + : "a"(SYSCALL(clone)), "i"(SYSCALL(exit)), "c"(child_stack), + "d"(parent_tidptr), "S"(newtls), "D"(child_tidptr) + : "memory"); return res; } -#elif defined(__arm__) +# elif defined(__arm__) uptr internal_clone(int (*fn)(void *), void *child_stack, int flags, void *arg, int *parent_tidptr, void *newtls, int *child_tidptr) { unsigned int res; @@ -1737,70 +1768,68 @@ uptr internal_clone(int (*fn)(void *), void *child_stack, int flags, void *arg, register int *r4 __asm__("r4") = child_tidptr; register int r7 __asm__("r7") = __NR_clone; -#if __ARM_ARCH > 4 || defined (__ARM_ARCH_4T__) -# define ARCH_HAS_BX -#endif -#if __ARM_ARCH > 4 -# define ARCH_HAS_BLX -#endif +# if __ARM_ARCH > 4 || defined(__ARM_ARCH_4T__) +# define ARCH_HAS_BX +# endif +# if __ARM_ARCH > 4 +# define ARCH_HAS_BLX +# endif -#ifdef ARCH_HAS_BX -# ifdef ARCH_HAS_BLX -# define BLX(R) "blx " #R "\n" -# else -# define BLX(R) "mov lr, pc; bx " #R "\n" -# endif -#else -# define BLX(R) "mov lr, pc; mov pc," #R "\n" -#endif +# ifdef ARCH_HAS_BX +# ifdef ARCH_HAS_BLX +# define BLX(R) "blx " #R "\n" +# else +# define BLX(R) "mov lr, pc; bx " #R "\n" +# endif +# else +# define BLX(R) "mov lr, pc; mov pc," #R "\n" +# endif __asm__ __volatile__( - /* %r0 = syscall(%r7 = SYSCALL(clone), - * %r0 = flags, - * %r1 = child_stack, - * %r2 = parent_tidptr, - * %r3 = new_tls, - * %r4 = child_tidptr) - */ - - /* Do the system call */ - "swi 0x0\n" - - /* if (%r0 != 0) - * return %r0; - */ - "cmp r0, #0\n" - "bne 1f\n" - - /* In the child, now. Call "fn(arg)". */ - "ldr r0, [sp, #4]\n" - "ldr ip, [sp], #8\n" - BLX(ip) - /* Call _exit(%r0). */ - "mov r7, %7\n" - "swi 0x0\n" - "1:\n" - "mov %0, r0\n" - : "=r"(res) - : "r"(r0), "r"(r1), "r"(r2), "r"(r3), "r"(r4), "r"(r7), - "i"(__NR_exit) - : "memory"); + /* %r0 = syscall(%r7 = SYSCALL(clone), + * %r0 = flags, + * %r1 = child_stack, + * %r2 = parent_tidptr, + * %r3 = new_tls, + * %r4 = child_tidptr) + */ + + /* Do the system call */ + "swi 0x0\n" + + /* if (%r0 != 0) + * return %r0; + */ + "cmp r0, #0\n" + "bne 1f\n" + + /* In the child, now. Call "fn(arg)". */ + "ldr r0, [sp, #4]\n" + "ldr ip, [sp], #8\n" BLX(ip) + /* Call _exit(%r0). */ + "mov r7, %7\n" + "swi 0x0\n" + "1:\n" + "mov %0, r0\n" + : "=r"(res) + : "r"(r0), "r"(r1), "r"(r2), "r"(r3), "r"(r4), "r"(r7), "i"(__NR_exit) + : "memory"); return res; } -#endif -#endif // SANITIZER_LINUX +# endif +# endif // SANITIZER_LINUX -#if SANITIZER_LINUX +# if SANITIZER_LINUX int internal_uname(struct utsname *buf) { return internal_syscall(SYSCALL(uname), buf); } -#endif +# endif -#if SANITIZER_ANDROID -#if __ANDROID_API__ < 21 +# if SANITIZER_ANDROID +# if __ANDROID_API__ < 21 extern "C" __attribute__((weak)) int dl_iterate_phdr( int (*)(struct dl_phdr_info *, size_t, void *), void *); -#endif +# endif static int dl_iterate_phdr_test_cb(struct dl_phdr_info *info, size_t size, void *data) { @@ -1817,40 +1846,41 @@ static int dl_iterate_phdr_test_cb(struct dl_phdr_info *info, size_t size, static atomic_uint32_t android_api_level; static AndroidApiLevel AndroidDetectApiLevelStatic() { -#if __ANDROID_API__ <= 19 +# if __ANDROID_API__ <= 19 return ANDROID_KITKAT; -#elif __ANDROID_API__ <= 22 +# elif __ANDROID_API__ <= 22 return ANDROID_LOLLIPOP_MR1; -#else +# else return ANDROID_POST_LOLLIPOP; -#endif +# endif } static AndroidApiLevel AndroidDetectApiLevel() { if (!&dl_iterate_phdr) - return ANDROID_KITKAT; // K or lower + return ANDROID_KITKAT; // K or lower bool base_name_seen = false; dl_iterate_phdr(dl_iterate_phdr_test_cb, &base_name_seen); if (base_name_seen) - return ANDROID_LOLLIPOP_MR1; // L MR1 + return ANDROID_LOLLIPOP_MR1; // L MR1 return ANDROID_POST_LOLLIPOP; // post-L // Plain L (API level 21) is completely broken wrt ASan and not very // interesting to detect. } -extern "C" __attribute__((weak)) void* _DYNAMIC; +extern "C" __attribute__((weak)) void *_DYNAMIC; AndroidApiLevel AndroidGetApiLevel() { AndroidApiLevel level = (AndroidApiLevel)atomic_load(&android_api_level, memory_order_relaxed); - if (level) return level; + if (level) + return level; level = &_DYNAMIC == nullptr ? AndroidDetectApiLevelStatic() : AndroidDetectApiLevel(); atomic_store(&android_api_level, level, memory_order_relaxed); return level; } -#endif +# endif static HandleSignalMode GetHandleSignalModeImpl(int signum) { switch (signum) { @@ -1877,28 +1907,28 @@ HandleSignalMode GetHandleSignalMode(int signum) { return result; } -#if !SANITIZER_GO +# if !SANITIZER_GO void *internal_start_thread(void *(*func)(void *arg), void *arg) { - if (&real_pthread_create == 0) + if (&internal_pthread_create == 0) return nullptr; // Start the thread with signals blocked, otherwise it can steal user signals. ScopedBlockSignals block(nullptr); void *th; - real_pthread_create(&th, nullptr, func, arg); + internal_pthread_create(&th, nullptr, func, arg); return th; } void internal_join_thread(void *th) { - if (&real_pthread_join) - real_pthread_join(th, nullptr); + if (&internal_pthread_join) + internal_pthread_join(th, nullptr); } -#else +# else void *internal_start_thread(void *(*func)(void *), void *arg) { return 0; } void internal_join_thread(void *th) {} -#endif +# endif -#if SANITIZER_LINUX && defined(__aarch64__) +# if SANITIZER_LINUX && defined(__aarch64__) // Android headers in the older NDK releases miss this definition. struct __sanitizer_esr_context { struct _aarch64_ctx head; @@ -1910,7 +1940,8 @@ static bool Aarch64GetESR(ucontext_t *ucontext, u64 *esr) { u8 *aux = reinterpret_cast<u8 *>(ucontext->uc_mcontext.__reserved); while (true) { _aarch64_ctx *ctx = (_aarch64_ctx *)aux; - if (ctx->size == 0) break; + if (ctx->size == 0) + break; if (ctx->magic == kEsrMagic) { *esr = ((__sanitizer_esr_context *)ctx)->esr; return true; @@ -1919,31 +1950,29 @@ static bool Aarch64GetESR(ucontext_t *ucontext, u64 *esr) { } return false; } -#elif SANITIZER_FREEBSD && defined(__aarch64__) +# elif SANITIZER_FREEBSD && defined(__aarch64__) // FreeBSD doesn't provide ESR in the ucontext. -static bool Aarch64GetESR(ucontext_t *ucontext, u64 *esr) { - return false; -} -#endif +static bool Aarch64GetESR(ucontext_t *ucontext, u64 *esr) { return false; } +# endif using Context = ucontext_t; SignalContext::WriteFlag SignalContext::GetWriteFlag() const { Context *ucontext = (Context *)context; -#if defined(__x86_64__) || defined(__i386__) +# if defined(__x86_64__) || defined(__i386__) static const uptr PF_WRITE = 1U << 1; -#if SANITIZER_FREEBSD +# if SANITIZER_FREEBSD uptr err = ucontext->uc_mcontext.mc_err; -#elif SANITIZER_NETBSD +# elif SANITIZER_NETBSD uptr err = ucontext->uc_mcontext.__gregs[_REG_ERR]; -#elif SANITIZER_SOLARIS && defined(__i386__) +# elif SANITIZER_SOLARIS && defined(__i386__) const int Err = 13; uptr err = ucontext->uc_mcontext.gregs[Err]; -#else +# else uptr err = ucontext->uc_mcontext.gregs[REG_ERR]; -#endif // SANITIZER_FREEBSD +# endif // SANITIZER_FREEBSD return err & PF_WRITE ? Write : Read; -#elif defined(__mips__) +# elif defined(__mips__) uint32_t *exception_source; uint32_t faulty_instruction; uint32_t op_code; @@ -1959,12 +1988,12 @@ SignalContext::WriteFlag SignalContext::GetWriteFlag() const { case 0x29: // sh case 0x2b: // sw case 0x3f: // sd -#if __mips_isa_rev < 6 +# if __mips_isa_rev < 6 case 0x2c: // sdl case 0x2d: // sdr case 0x2a: // swl case 0x2e: // swr -#endif +# endif return SignalContext::Write; case 0x20: // lb @@ -1974,14 +2003,14 @@ SignalContext::WriteFlag SignalContext::GetWriteFlag() const { case 0x23: // lw case 0x27: // lwu case 0x37: // ld -#if __mips_isa_rev < 6 +# if __mips_isa_rev < 6 case 0x1a: // ldl case 0x1b: // ldr case 0x22: // lwl case 0x26: // lwr -#endif +# endif return SignalContext::Read; -#if __mips_isa_rev == 6 +# if __mips_isa_rev == 6 case 0x3b: // pcrel op_code = (faulty_instruction >> 19) & 0x3; switch (op_code) { @@ -1989,50 +2018,63 @@ SignalContext::WriteFlag SignalContext::GetWriteFlag() const { case 0x2: // lwupc return SignalContext::Read; } -#endif +# endif } return SignalContext::Unknown; -#elif defined(__arm__) +# elif defined(__arm__) static const uptr FSR_WRITE = 1U << 11; uptr fsr = ucontext->uc_mcontext.error_code; return fsr & FSR_WRITE ? Write : Read; -#elif defined(__aarch64__) +# elif defined(__aarch64__) static const u64 ESR_ELx_WNR = 1U << 6; u64 esr; - if (!Aarch64GetESR(ucontext, &esr)) return Unknown; + if (!Aarch64GetESR(ucontext, &esr)) + return Unknown; return esr & ESR_ELx_WNR ? Write : Read; -#elif defined(__loongarch__) +# elif defined(__loongarch__) + // In the musl environment, the Linux kernel uapi sigcontext.h is not + // included in signal.h. To avoid missing the SC_ADDRERR_{RD,WR} macros, + // copy them here. The LoongArch Linux kernel uapi is already stable, + // so there's no need to worry about the value changing. +# ifndef SC_ADDRERR_RD + // Address error was due to memory load +# define SC_ADDRERR_RD (1 << 30) +# endif +# ifndef SC_ADDRERR_WR + // Address error was due to memory store +# define SC_ADDRERR_WR (1 << 31) +# endif u32 flags = ucontext->uc_mcontext.__flags; if (flags & SC_ADDRERR_RD) return SignalContext::Read; if (flags & SC_ADDRERR_WR) return SignalContext::Write; return SignalContext::Unknown; -#elif defined(__sparc__) +# elif defined(__sparc__) // Decode the instruction to determine the access type. // From OpenSolaris $SRC/uts/sun4/os/trap.c (get_accesstype). -#if SANITIZER_SOLARIS +# if SANITIZER_SOLARIS uptr pc = ucontext->uc_mcontext.gregs[REG_PC]; -#else +# else // Historical BSDism here. struct sigcontext *scontext = (struct sigcontext *)context; -#if defined(__arch64__) +# if defined(__arch64__) uptr pc = scontext->sigc_regs.tpc; -#else +# else uptr pc = scontext->si_regs.pc; -#endif -#endif +# endif +# endif u32 instr = *(u32 *)pc; - return (instr >> 21) & 1 ? Write: Read; -#elif defined(__riscv) -#if SANITIZER_FREEBSD + return (instr >> 21) & 1 ? Write : Read; +# elif defined(__riscv) +# if SANITIZER_FREEBSD unsigned long pc = ucontext->uc_mcontext.mc_gpregs.gp_sepc; -#else +# else unsigned long pc = ucontext->uc_mcontext.__gregs[REG_PC]; -#endif +# endif unsigned faulty_instruction = *(uint16_t *)pc; -#if defined(__riscv_compressed) +# if defined(__riscv_compressed) if ((faulty_instruction & 0x3) != 0x3) { // it's a compressed instruction // set op_bits to the instruction bits [1, 0, 15, 14, 13] unsigned op_bits = @@ -2040,38 +2082,38 @@ SignalContext::WriteFlag SignalContext::GetWriteFlag() const { unsigned rd = faulty_instruction & 0xF80; // bits 7-11, inclusive switch (op_bits) { case 0b10'010: // c.lwsp (rd != x0) -#if __riscv_xlen == 64 +# if __riscv_xlen == 64 case 0b10'011: // c.ldsp (rd != x0) -#endif +# endif return rd ? SignalContext::Read : SignalContext::Unknown; case 0b00'010: // c.lw -#if __riscv_flen >= 32 && __riscv_xlen == 32 +# if __riscv_flen >= 32 && __riscv_xlen == 32 case 0b10'011: // c.flwsp -#endif -#if __riscv_flen >= 32 || __riscv_xlen == 64 +# endif +# if __riscv_flen >= 32 || __riscv_xlen == 64 case 0b00'011: // c.flw / c.ld -#endif -#if __riscv_flen == 64 +# endif +# if __riscv_flen == 64 case 0b00'001: // c.fld case 0b10'001: // c.fldsp -#endif +# endif return SignalContext::Read; case 0b00'110: // c.sw case 0b10'110: // c.swsp -#if __riscv_flen >= 32 || __riscv_xlen == 64 +# if __riscv_flen >= 32 || __riscv_xlen == 64 case 0b00'111: // c.fsw / c.sd case 0b10'111: // c.fswsp / c.sdsp -#endif -#if __riscv_flen == 64 +# endif +# if __riscv_flen == 64 case 0b00'101: // c.fsd case 0b10'101: // c.fsdsp -#endif +# endif return SignalContext::Write; default: return SignalContext::Unknown; } } -#endif +# endif unsigned opcode = faulty_instruction & 0x7f; // lower 7 bits unsigned funct3 = (faulty_instruction >> 12) & 0x7; // bits 12-14, inclusive @@ -2081,9 +2123,9 @@ SignalContext::WriteFlag SignalContext::GetWriteFlag() const { case 0b000: // lb case 0b001: // lh case 0b010: // lw -#if __riscv_xlen == 64 +# if __riscv_xlen == 64 case 0b011: // ld -#endif +# endif case 0b100: // lbu case 0b101: // lhu return SignalContext::Read; @@ -2095,20 +2137,20 @@ SignalContext::WriteFlag SignalContext::GetWriteFlag() const { case 0b000: // sb case 0b001: // sh case 0b010: // sw -#if __riscv_xlen == 64 +# if __riscv_xlen == 64 case 0b011: // sd -#endif +# endif return SignalContext::Write; default: return SignalContext::Unknown; } -#if __riscv_flen >= 32 +# if __riscv_flen >= 32 case 0b0000111: // floating-point loads switch (funct3) { case 0b010: // flw -#if __riscv_flen == 64 +# if __riscv_flen == 64 case 0b011: // fld -#endif +# endif return SignalContext::Read; default: return SignalContext::Unknown; @@ -2116,21 +2158,21 @@ SignalContext::WriteFlag SignalContext::GetWriteFlag() const { case 0b0100111: // floating-point stores switch (funct3) { case 0b010: // fsw -#if __riscv_flen == 64 +# if __riscv_flen == 64 case 0b011: // fsd -#endif +# endif return SignalContext::Write; default: return SignalContext::Unknown; } -#endif +# endif default: return SignalContext::Unknown; } -#else +# else (void)ucontext; return Unknown; // FIXME: Implement. -#endif +# endif } bool SignalContext::IsTrueFaultingAddress() const { @@ -2139,129 +2181,497 @@ bool SignalContext::IsTrueFaultingAddress() const { return si->si_signo == SIGSEGV && si->si_code != 128; } +UNUSED +static const char *RegNumToRegName(int reg) { + switch (reg) { +# if SANITIZER_LINUX && SANITIZER_GLIBC || SANITIZER_NETBSD +# if defined(__x86_64__) +# if SANITIZER_NETBSD +# define REG_RAX _REG_RAX +# define REG_RBX _REG_RBX +# define REG_RCX _REG_RCX +# define REG_RDX _REG_RDX +# define REG_RDI _REG_RDI +# define REG_RSI _REG_RSI +# define REG_RBP _REG_RBP +# define REG_RSP _REG_RSP +# define REG_R8 _REG_R8 +# define REG_R9 _REG_R9 +# define REG_R10 _REG_R10 +# define REG_R11 _REG_R11 +# define REG_R12 _REG_R12 +# define REG_R13 _REG_R13 +# define REG_R14 _REG_R14 +# define REG_R15 _REG_R15 +# endif + case REG_RAX: + return "rax"; + case REG_RBX: + return "rbx"; + case REG_RCX: + return "rcx"; + case REG_RDX: + return "rdx"; + case REG_RDI: + return "rdi"; + case REG_RSI: + return "rsi"; + case REG_RBP: + return "rbp"; + case REG_RSP: + return "rsp"; + case REG_R8: + return "r8"; + case REG_R9: + return "r9"; + case REG_R10: + return "r10"; + case REG_R11: + return "r11"; + case REG_R12: + return "r12"; + case REG_R13: + return "r13"; + case REG_R14: + return "r14"; + case REG_R15: + return "r15"; +# elif defined(__i386__) +# if SANITIZER_NETBSD +# define REG_EAX _REG_EAX +# define REG_EBX _REG_EBX +# define REG_ECX _REG_ECX +# define REG_EDX _REG_EDX +# define REG_EDI _REG_EDI +# define REG_ESI _REG_ESI +# define REG_EBP _REG_EBP +# define REG_ESP _REG_ESP +# endif + case REG_EAX: + return "eax"; + case REG_EBX: + return "ebx"; + case REG_ECX: + return "ecx"; + case REG_EDX: + return "edx"; + case REG_EDI: + return "edi"; + case REG_ESI: + return "esi"; + case REG_EBP: + return "ebp"; + case REG_ESP: + return "esp"; +# elif defined(__arm__) +# ifdef MAKE_CASE +# undef MAKE_CASE +# endif +# define REG_STR(reg) #reg +# define MAKE_CASE(N) \ + case REG_R##N: \ + return REG_STR(r##N) + MAKE_CASE(0); + MAKE_CASE(1); + MAKE_CASE(2); + MAKE_CASE(3); + MAKE_CASE(4); + MAKE_CASE(5); + MAKE_CASE(6); + MAKE_CASE(7); + MAKE_CASE(8); + MAKE_CASE(9); + MAKE_CASE(10); + MAKE_CASE(11); + MAKE_CASE(12); + case REG_R13: + return "sp"; + case REG_R14: + return "lr"; + case REG_R15: + return "pc"; +# elif defined(__aarch64__) +# define REG_STR(reg) #reg +# define MAKE_CASE(N) \ + case N: \ + return REG_STR(x##N) + MAKE_CASE(0); + MAKE_CASE(1); + MAKE_CASE(2); + MAKE_CASE(3); + MAKE_CASE(4); + MAKE_CASE(5); + MAKE_CASE(6); + MAKE_CASE(7); + MAKE_CASE(8); + MAKE_CASE(9); + MAKE_CASE(10); + MAKE_CASE(11); + MAKE_CASE(12); + MAKE_CASE(13); + MAKE_CASE(14); + MAKE_CASE(15); + MAKE_CASE(16); + MAKE_CASE(17); + MAKE_CASE(18); + MAKE_CASE(19); + MAKE_CASE(20); + MAKE_CASE(21); + MAKE_CASE(22); + MAKE_CASE(23); + MAKE_CASE(24); + MAKE_CASE(25); + MAKE_CASE(26); + MAKE_CASE(27); + MAKE_CASE(28); + case 29: + return "fp"; + case 30: + return "lr"; + case 31: + return "sp"; +# endif +# endif // SANITIZER_LINUX && SANITIZER_GLIBC + default: + return NULL; + } + return NULL; +} + +# if ((SANITIZER_LINUX && SANITIZER_GLIBC) || SANITIZER_NETBSD) && \ + (defined(__arm__) || defined(__aarch64__)) +static uptr GetArmRegister(ucontext_t *ctx, int RegNum) { + switch (RegNum) { +# if defined(__arm__) && !SANITIZER_NETBSD +# ifdef MAKE_CASE +# undef MAKE_CASE +# endif +# define MAKE_CASE(N) \ + case REG_R##N: \ + return ctx->uc_mcontext.arm_r##N + MAKE_CASE(0); + MAKE_CASE(1); + MAKE_CASE(2); + MAKE_CASE(3); + MAKE_CASE(4); + MAKE_CASE(5); + MAKE_CASE(6); + MAKE_CASE(7); + MAKE_CASE(8); + MAKE_CASE(9); + MAKE_CASE(10); + case REG_R11: + return ctx->uc_mcontext.arm_fp; + case REG_R12: + return ctx->uc_mcontext.arm_ip; + case REG_R13: + return ctx->uc_mcontext.arm_sp; + case REG_R14: + return ctx->uc_mcontext.arm_lr; + case REG_R15: + return ctx->uc_mcontext.arm_pc; +# elif defined(__aarch64__) +# if SANITIZER_LINUX + case 0 ... 30: + return ctx->uc_mcontext.regs[RegNum]; + case 31: + return ctx->uc_mcontext.sp; +# elif SANITIZER_NETBSD + case 0 ... 31: + return ctx->uc_mcontext.__gregs[RegNum]; +# endif +# endif + default: + return 0; + } + return 0; +} +# endif // SANITIZER_LINUX && SANITIZER_GLIBC && (defined(__arm__) || + // defined(__aarch64__)) + +UNUSED +static void DumpSingleReg(ucontext_t *ctx, int RegNum) { + const char *RegName = RegNumToRegName(RegNum); +# if SANITIZER_LINUX && SANITIZER_GLIBC || SANITIZER_NETBSD +# if defined(__x86_64__) + Printf("%s%s = 0x%016llx ", internal_strlen(RegName) == 2 ? " " : "", + RegName, +# if SANITIZER_LINUX + ctx->uc_mcontext.gregs[RegNum] +# elif SANITIZER_NETBSD + ctx->uc_mcontext.__gregs[RegNum] +# endif + ); +# elif defined(__i386__) + Printf("%s = 0x%08x ", RegName, +# if SANITIZER_LINUX + ctx->uc_mcontext.gregs[RegNum] +# elif SANITIZER_NETBSD + ctx->uc_mcontext.__gregs[RegNum] +# endif + ); +# elif defined(__arm__) + Printf("%s%s = 0x%08zx ", internal_strlen(RegName) == 2 ? " " : "", RegName, + GetArmRegister(ctx, RegNum)); +# elif defined(__aarch64__) + Printf("%s%s = 0x%016zx ", internal_strlen(RegName) == 2 ? " " : "", RegName, + GetArmRegister(ctx, RegNum)); +# else + (void)RegName; +# endif +# else + (void)RegName; +# endif +} + void SignalContext::DumpAllRegisters(void *context) { - // FIXME: Implement this. + ucontext_t *ucontext = (ucontext_t *)context; +# if SANITIZER_LINUX && SANITIZER_GLIBC || SANITIZER_NETBSD +# if defined(__x86_64__) + Report("Register values:\n"); + DumpSingleReg(ucontext, REG_RAX); + DumpSingleReg(ucontext, REG_RBX); + DumpSingleReg(ucontext, REG_RCX); + DumpSingleReg(ucontext, REG_RDX); + Printf("\n"); + DumpSingleReg(ucontext, REG_RDI); + DumpSingleReg(ucontext, REG_RSI); + DumpSingleReg(ucontext, REG_RBP); + DumpSingleReg(ucontext, REG_RSP); + Printf("\n"); + DumpSingleReg(ucontext, REG_R8); + DumpSingleReg(ucontext, REG_R9); + DumpSingleReg(ucontext, REG_R10); + DumpSingleReg(ucontext, REG_R11); + Printf("\n"); + DumpSingleReg(ucontext, REG_R12); + DumpSingleReg(ucontext, REG_R13); + DumpSingleReg(ucontext, REG_R14); + DumpSingleReg(ucontext, REG_R15); + Printf("\n"); +# elif defined(__i386__) + // Duplication of this report print is caused by partial support + // of register values dumping. In case of unsupported yet architecture let's + // avoid printing 'Register values:' without actual values in the following + // output. + Report("Register values:\n"); + DumpSingleReg(ucontext, REG_EAX); + DumpSingleReg(ucontext, REG_EBX); + DumpSingleReg(ucontext, REG_ECX); + DumpSingleReg(ucontext, REG_EDX); + Printf("\n"); + DumpSingleReg(ucontext, REG_EDI); + DumpSingleReg(ucontext, REG_ESI); + DumpSingleReg(ucontext, REG_EBP); + DumpSingleReg(ucontext, REG_ESP); + Printf("\n"); +# elif defined(__arm__) && !SANITIZER_NETBSD + Report("Register values:\n"); + DumpSingleReg(ucontext, REG_R0); + DumpSingleReg(ucontext, REG_R1); + DumpSingleReg(ucontext, REG_R2); + DumpSingleReg(ucontext, REG_R3); + Printf("\n"); + DumpSingleReg(ucontext, REG_R4); + DumpSingleReg(ucontext, REG_R5); + DumpSingleReg(ucontext, REG_R6); + DumpSingleReg(ucontext, REG_R7); + Printf("\n"); + DumpSingleReg(ucontext, REG_R8); + DumpSingleReg(ucontext, REG_R9); + DumpSingleReg(ucontext, REG_R10); + DumpSingleReg(ucontext, REG_R11); + Printf("\n"); + DumpSingleReg(ucontext, REG_R12); + DumpSingleReg(ucontext, REG_R13); + DumpSingleReg(ucontext, REG_R14); + DumpSingleReg(ucontext, REG_R15); + Printf("\n"); +# elif defined(__aarch64__) + Report("Register values:\n"); + for (int i = 0; i <= 31; ++i) { + DumpSingleReg(ucontext, i); + if (i % 4 == 3) + Printf("\n"); + } +# else + (void)ucontext; +# endif +# elif SANITIZER_FREEBSD +# if defined(__x86_64__) + Report("Register values:\n"); + Printf("rax = 0x%016lx ", ucontext->uc_mcontext.mc_rax); + Printf("rbx = 0x%016lx ", ucontext->uc_mcontext.mc_rbx); + Printf("rcx = 0x%016lx ", ucontext->uc_mcontext.mc_rcx); + Printf("rdx = 0x%016lx ", ucontext->uc_mcontext.mc_rdx); + Printf("\n"); + Printf("rdi = 0x%016lx ", ucontext->uc_mcontext.mc_rdi); + Printf("rsi = 0x%016lx ", ucontext->uc_mcontext.mc_rsi); + Printf("rbp = 0x%016lx ", ucontext->uc_mcontext.mc_rbp); + Printf("rsp = 0x%016lx ", ucontext->uc_mcontext.mc_rsp); + Printf("\n"); + Printf(" r8 = 0x%016lx ", ucontext->uc_mcontext.mc_r8); + Printf(" r9 = 0x%016lx ", ucontext->uc_mcontext.mc_r9); + Printf("r10 = 0x%016lx ", ucontext->uc_mcontext.mc_r10); + Printf("r11 = 0x%016lx ", ucontext->uc_mcontext.mc_r11); + Printf("\n"); + Printf("r12 = 0x%016lx ", ucontext->uc_mcontext.mc_r12); + Printf("r13 = 0x%016lx ", ucontext->uc_mcontext.mc_r13); + Printf("r14 = 0x%016lx ", ucontext->uc_mcontext.mc_r14); + Printf("r15 = 0x%016lx ", ucontext->uc_mcontext.mc_r15); + Printf("\n"); +# elif defined(__i386__) + Report("Register values:\n"); + Printf("eax = 0x%08x ", ucontext->uc_mcontext.mc_eax); + Printf("ebx = 0x%08x ", ucontext->uc_mcontext.mc_ebx); + Printf("ecx = 0x%08x ", ucontext->uc_mcontext.mc_ecx); + Printf("edx = 0x%08x ", ucontext->uc_mcontext.mc_edx); + Printf("\n"); + Printf("edi = 0x%08x ", ucontext->uc_mcontext.mc_edi); + Printf("esi = 0x%08x ", ucontext->uc_mcontext.mc_esi); + Printf("ebp = 0x%08x ", ucontext->uc_mcontext.mc_ebp); + Printf("esp = 0x%08x ", ucontext->uc_mcontext.mc_esp); + Printf("\n"); +# else + (void)ucontext; +# endif +# else + (void)ucontext; +# endif + // FIXME: Implement this for other OSes and architectures. } static void GetPcSpBp(void *context, uptr *pc, uptr *sp, uptr *bp) { -#if SANITIZER_NETBSD +# if SANITIZER_NETBSD // This covers all NetBSD architectures ucontext_t *ucontext = (ucontext_t *)context; *pc = _UC_MACHINE_PC(ucontext); *bp = _UC_MACHINE_FP(ucontext); *sp = _UC_MACHINE_SP(ucontext); -#elif defined(__arm__) - ucontext_t *ucontext = (ucontext_t*)context; +# elif defined(__arm__) + ucontext_t *ucontext = (ucontext_t *)context; *pc = ucontext->uc_mcontext.arm_pc; *bp = ucontext->uc_mcontext.arm_fp; *sp = ucontext->uc_mcontext.arm_sp; -#elif defined(__aarch64__) -# if SANITIZER_FREEBSD - ucontext_t *ucontext = (ucontext_t*)context; +# elif defined(__aarch64__) +# if SANITIZER_FREEBSD + ucontext_t *ucontext = (ucontext_t *)context; *pc = ucontext->uc_mcontext.mc_gpregs.gp_elr; *bp = ucontext->uc_mcontext.mc_gpregs.gp_x[29]; *sp = ucontext->uc_mcontext.mc_gpregs.gp_sp; -# else - ucontext_t *ucontext = (ucontext_t*)context; +# else + ucontext_t *ucontext = (ucontext_t *)context; *pc = ucontext->uc_mcontext.pc; *bp = ucontext->uc_mcontext.regs[29]; *sp = ucontext->uc_mcontext.sp; -# endif -#elif defined(__hppa__) - ucontext_t *ucontext = (ucontext_t*)context; +# endif +# elif defined(__hppa__) + ucontext_t *ucontext = (ucontext_t *)context; *pc = ucontext->uc_mcontext.sc_iaoq[0]; /* GCC uses %r3 whenever a frame pointer is needed. */ *bp = ucontext->uc_mcontext.sc_gr[3]; *sp = ucontext->uc_mcontext.sc_gr[30]; -#elif defined(__x86_64__) -# if SANITIZER_FREEBSD - ucontext_t *ucontext = (ucontext_t*)context; +# elif defined(__x86_64__) +# if SANITIZER_FREEBSD + ucontext_t *ucontext = (ucontext_t *)context; *pc = ucontext->uc_mcontext.mc_rip; *bp = ucontext->uc_mcontext.mc_rbp; *sp = ucontext->uc_mcontext.mc_rsp; -# else - ucontext_t *ucontext = (ucontext_t*)context; +# else + ucontext_t *ucontext = (ucontext_t *)context; *pc = ucontext->uc_mcontext.gregs[REG_RIP]; *bp = ucontext->uc_mcontext.gregs[REG_RBP]; *sp = ucontext->uc_mcontext.gregs[REG_RSP]; -# endif -#elif defined(__i386__) -# if SANITIZER_FREEBSD - ucontext_t *ucontext = (ucontext_t*)context; +# endif +# elif defined(__i386__) +# if SANITIZER_FREEBSD + ucontext_t *ucontext = (ucontext_t *)context; *pc = ucontext->uc_mcontext.mc_eip; *bp = ucontext->uc_mcontext.mc_ebp; *sp = ucontext->uc_mcontext.mc_esp; -# else - ucontext_t *ucontext = (ucontext_t*)context; -# if SANITIZER_SOLARIS +# else + ucontext_t *ucontext = (ucontext_t *)context; +# if SANITIZER_SOLARIS /* Use the numeric values: the symbolic ones are undefined by llvm include/llvm/Support/Solaris.h. */ -# ifndef REG_EIP -# define REG_EIP 14 // REG_PC -# endif -# ifndef REG_EBP -# define REG_EBP 6 // REG_FP -# endif -# ifndef REG_UESP -# define REG_UESP 17 // REG_SP -# endif -# endif +# ifndef REG_EIP +# define REG_EIP 14 // REG_PC +# endif +# ifndef REG_EBP +# define REG_EBP 6 // REG_FP +# endif +# ifndef REG_UESP +# define REG_UESP 17 // REG_SP +# endif +# endif *pc = ucontext->uc_mcontext.gregs[REG_EIP]; *bp = ucontext->uc_mcontext.gregs[REG_EBP]; *sp = ucontext->uc_mcontext.gregs[REG_UESP]; -# endif -#elif defined(__powerpc__) || defined(__powerpc64__) +# endif +# elif defined(__powerpc__) || defined(__powerpc64__) # if SANITIZER_FREEBSD ucontext_t *ucontext = (ucontext_t *)context; *pc = ucontext->uc_mcontext.mc_srr0; *sp = ucontext->uc_mcontext.mc_frame[1]; *bp = ucontext->uc_mcontext.mc_frame[31]; # else - ucontext_t *ucontext = (ucontext_t*)context; + ucontext_t *ucontext = (ucontext_t *)context; *pc = ucontext->uc_mcontext.regs->nip; *sp = ucontext->uc_mcontext.regs->gpr[PT_R1]; // The powerpc{,64}-linux ABIs do not specify r31 as the frame // pointer, but GCC always uses r31 when we need a frame pointer. *bp = ucontext->uc_mcontext.regs->gpr[PT_R31]; # endif -#elif defined(__sparc__) -#if defined(__arch64__) || defined(__sparcv9) -#define STACK_BIAS 2047 -#else -#define STACK_BIAS 0 -# endif -# if SANITIZER_SOLARIS +# elif defined(__sparc__) +# if defined(__arch64__) || defined(__sparcv9) +# define STACK_BIAS 2047 +# else +# define STACK_BIAS 0 +# endif +# if SANITIZER_SOLARIS ucontext_t *ucontext = (ucontext_t *)context; *pc = ucontext->uc_mcontext.gregs[REG_PC]; - *sp = ucontext->uc_mcontext.gregs[REG_O6] + STACK_BIAS; -#else + *sp = ucontext->uc_mcontext.gregs[REG_SP] + STACK_BIAS; + // Avoid SEGV when dereferencing sp on stack overflow with non-faulting load. + // This requires a SPARC V9 CPU. Cannot use #ASI_PNF here: only supported + // since clang-19. +# if defined(__sparcv9) + asm("ldxa [%[fp]] 0x82, %[bp]" +# else + asm("lduwa [%[fp]] 0x82, %[bp]" +# endif + : [bp] "=r"(*bp) + : [fp] "r"(&((struct frame *)*sp)->fr_savfp)); + if (*bp) + *bp += STACK_BIAS; +# else // Historical BSDism here. struct sigcontext *scontext = (struct sigcontext *)context; -#if defined(__arch64__) +# if defined(__arch64__) *pc = scontext->sigc_regs.tpc; *sp = scontext->sigc_regs.u_regs[14] + STACK_BIAS; -#else +# else *pc = scontext->si_regs.pc; *sp = scontext->si_regs.u_regs[14]; -#endif -# endif +# endif *bp = (uptr)((uhwptr *)*sp)[14] + STACK_BIAS; -#elif defined(__mips__) - ucontext_t *ucontext = (ucontext_t*)context; +# endif +# elif defined(__mips__) + ucontext_t *ucontext = (ucontext_t *)context; *pc = ucontext->uc_mcontext.pc; *bp = ucontext->uc_mcontext.gregs[30]; *sp = ucontext->uc_mcontext.gregs[29]; -#elif defined(__s390__) - ucontext_t *ucontext = (ucontext_t*)context; -# if defined(__s390x__) +# elif defined(__s390__) + ucontext_t *ucontext = (ucontext_t *)context; +# if defined(__s390x__) *pc = ucontext->uc_mcontext.psw.addr; -# else +# else *pc = ucontext->uc_mcontext.psw.addr & 0x7fffffff; -# endif +# endif *bp = ucontext->uc_mcontext.gregs[11]; *sp = ucontext->uc_mcontext.gregs[15]; -#elif defined(__riscv) - ucontext_t *ucontext = (ucontext_t*)context; +# elif defined(__riscv) + ucontext_t *ucontext = (ucontext_t *)context; # if SANITIZER_FREEBSD *pc = ucontext->uc_mcontext.mc_gpregs.gp_sepc; *bp = ucontext->uc_mcontext.mc_gpregs.gp_s[0]; @@ -2288,12 +2698,10 @@ static void GetPcSpBp(void *context, uptr *pc, uptr *sp, uptr *bp) { void SignalContext::InitPcSpBp() { GetPcSpBp(context, &pc, &sp, &bp); } -void InitializePlatformEarly() { - // Do nothing. -} +void InitializePlatformEarly() { InitTlsSize(); } void CheckASLR() { -#if SANITIZER_NETBSD +# if SANITIZER_NETBSD int mib[3]; int paxflags; uptr len = sizeof(paxflags); @@ -2308,12 +2716,13 @@ void CheckASLR() { } if (UNLIKELY(paxflags & CTL_PROC_PAXFLAGS_ASLR)) { - Printf("This sanitizer is not compatible with enabled ASLR.\n" - "To disable ASLR, please run \"paxctl +a %s\" and try again.\n", - GetArgv()[0]); + Printf( + "This sanitizer is not compatible with enabled ASLR.\n" + "To disable ASLR, please run \"paxctl +a %s\" and try again.\n", + GetArgv()[0]); Die(); } -#elif SANITIZER_FREEBSD +# elif SANITIZER_FREEBSD int aslr_status; int r = internal_procctl(P_PID, 0, PROC_ASLR_STATUS, &aslr_status); if (UNLIKELY(r == -1)) { @@ -2323,9 +2732,13 @@ void CheckASLR() { return; } if ((aslr_status & PROC_ASLR_ACTIVE) != 0) { - Printf("This sanitizer is not compatible with enabled ASLR " - "and binaries compiled with PIE\n"); - Die(); + VReport(1, + "This sanitizer is not compatible with enabled ASLR " + "and binaries compiled with PIE\n" + "ASLR will be disabled and the program re-executed.\n"); + int aslr_ctl = PROC_ASLR_FORCE_DISABLE; + CHECK_NE(internal_procctl(P_PID, 0, PROC_ASLR_CTL, &aslr_ctl), -1); + ReExec(); } # elif SANITIZER_PPC64V2 // Disable ASLR for Linux PPC64LE. @@ -2345,7 +2758,7 @@ void CheckASLR() { } void CheckMPROTECT() { -#if SANITIZER_NETBSD +# if SANITIZER_NETBSD int mib[3]; int paxflags; uptr len = sizeof(paxflags); @@ -2363,13 +2776,13 @@ void CheckMPROTECT() { Printf("This sanitizer is not compatible with enabled MPROTECT\n"); Die(); } -#else +# else // Do nothing -#endif +# endif } void CheckNoDeepBind(const char *filename, int flag) { -#ifdef RTLD_DEEPBIND +# ifdef RTLD_DEEPBIND if (flag & RTLD_DEEPBIND) { Report( "You are trying to dlopen a %s shared library with RTLD_DEEPBIND flag" @@ -2380,7 +2793,7 @@ void CheckNoDeepBind(const char *filename, int flag) { filename, filename); Die(); } -#endif +# endif } uptr FindAvailableMemoryRange(uptr size, uptr alignment, uptr left_padding, @@ -2393,16 +2806,16 @@ uptr FindAvailableMemoryRange(uptr size, uptr alignment, uptr left_padding, bool GetRandom(void *buffer, uptr length, bool blocking) { if (!buffer || !length || length > 256) return false; -#if SANITIZER_USE_GETENTROPY +# if SANITIZER_USE_GETENTROPY uptr rnd = getentropy(buffer, length); int rverrno = 0; if (internal_iserror(rnd, &rverrno) && rverrno == EFAULT) return false; else if (rnd == 0) return true; -#endif // SANITIZER_USE_GETENTROPY +# endif // SANITIZER_USE_GETENTROPY -#if SANITIZER_USE_GETRANDOM +# if SANITIZER_USE_GETRANDOM static atomic_uint8_t skip_getrandom_syscall; if (!atomic_load_relaxed(&skip_getrandom_syscall)) { // Up to 256 bytes, getrandom will not be interrupted. @@ -2414,7 +2827,7 @@ bool GetRandom(void *buffer, uptr length, bool blocking) { else if (res == length) return true; } -#endif // SANITIZER_USE_GETRANDOM +# endif // SANITIZER_USE_GETRANDOM // Up to 256 bytes, a read off /dev/urandom will not be interrupted. // blocking is moot here, O_NONBLOCK has no effect when opening /dev/urandom. uptr fd = internal_open("/dev/urandom", O_RDONLY); @@ -2427,6 +2840,6 @@ bool GetRandom(void *buffer, uptr length, bool blocking) { return true; } -} // namespace __sanitizer +} // namespace __sanitizer #endif diff --git a/libsanitizer/sanitizer_common/sanitizer_linux.h b/libsanitizer/sanitizer_common/sanitizer_linux.h index 7454369..8b7874b 100644 --- a/libsanitizer/sanitizer_common/sanitizer_linux.h +++ b/libsanitizer/sanitizer_common/sanitizer_linux.h @@ -13,15 +13,15 @@ #define SANITIZER_LINUX_H #include "sanitizer_platform.h" -#if SANITIZER_FREEBSD || SANITIZER_LINUX || SANITIZER_NETBSD || \ +#if SANITIZER_FREEBSD || SANITIZER_LINUX || SANITIZER_NETBSD || \ SANITIZER_SOLARIS -#include "sanitizer_common.h" -#include "sanitizer_internal_defs.h" -#include "sanitizer_platform_limits_freebsd.h" -#include "sanitizer_platform_limits_netbsd.h" -#include "sanitizer_platform_limits_posix.h" -#include "sanitizer_platform_limits_solaris.h" -#include "sanitizer_posix.h" +# include "sanitizer_common.h" +# include "sanitizer_internal_defs.h" +# include "sanitizer_platform_limits_freebsd.h" +# include "sanitizer_platform_limits_netbsd.h" +# include "sanitizer_platform_limits_posix.h" +# include "sanitizer_platform_limits_solaris.h" +# include "sanitizer_posix.h" struct link_map; // Opaque type returned by dlopen(). struct utsname; @@ -46,9 +46,9 @@ void ReadProcMaps(ProcSelfMapsBuff *proc_maps); // Syscall wrappers. uptr internal_getdents(fd_t fd, struct linux_dirent *dirp, unsigned int count); -uptr internal_sigaltstack(const void* ss, void* oss); +uptr internal_sigaltstack(const void *ss, void *oss); uptr internal_sigprocmask(int how, __sanitizer_sigset_t *set, - __sanitizer_sigset_t *oldset); + __sanitizer_sigset_t *oldset); void SetSigProcMask(__sanitizer_sigset_t *set, __sanitizer_sigset_t *oldset); void BlockSignals(__sanitizer_sigset_t *oldset = nullptr); @@ -65,10 +65,10 @@ struct ScopedBlockSignals { # if SANITIZER_GLIBC uptr internal_clock_gettime(__sanitizer_clockid_t clk_id, void *tp); -#endif +# endif // Linux-only syscalls. -#if SANITIZER_LINUX +# if SANITIZER_LINUX uptr internal_prctl(int option, uptr arg2, uptr arg3, uptr arg4, uptr arg5); # if defined(__x86_64__) uptr internal_arch_prctl(int option, uptr arg2); @@ -83,33 +83,33 @@ void internal_sigdelset(__sanitizer_sigset_t *set, int signum); defined(__arm__) || SANITIZER_RISCV64 || SANITIZER_LOONGARCH64 uptr internal_clone(int (*fn)(void *), void *child_stack, int flags, void *arg, int *parent_tidptr, void *newtls, int *child_tidptr); -#endif +# endif int internal_uname(struct utsname *buf); -#elif SANITIZER_FREEBSD +# elif SANITIZER_FREEBSD uptr internal_procctl(int type, int id, int cmd, void *data); void internal_sigdelset(__sanitizer_sigset_t *set, int signum); -#elif SANITIZER_NETBSD +# elif SANITIZER_NETBSD void internal_sigdelset(__sanitizer_sigset_t *set, int signum); uptr internal_clone(int (*fn)(void *), void *child_stack, int flags, void *arg); -#endif // SANITIZER_LINUX +# endif // SANITIZER_LINUX // This class reads thread IDs from /proc/<pid>/task using only syscalls. class ThreadLister { public: explicit ThreadLister(pid_t pid); - ~ThreadLister(); enum Result { Error, Incomplete, Ok, }; Result ListThreads(InternalMmapVector<tid_t> *threads); + const char *LoadStatus(tid_t tid); private: - bool IsAlive(int tid); + bool IsAlive(tid_t tid); - pid_t pid_; - int descriptor_ = -1; + InternalScopedString task_path_; + InternalScopedString status_path_; InternalMmapVector<char> buffer_; }; @@ -135,36 +135,60 @@ inline void ReleaseMemoryPagesToOSAndZeroFill(uptr beg, uptr end) { ReleaseMemoryPagesToOS(beg, end); } -#if SANITIZER_ANDROID - -#if defined(__aarch64__) -# define __get_tls() \ - ({ void** __v; __asm__("mrs %0, tpidr_el0" : "=r"(__v)); __v; }) -#elif defined(__arm__) -# define __get_tls() \ - ({ void** __v; __asm__("mrc p15, 0, %0, c13, c0, 3" : "=r"(__v)); __v; }) -#elif defined(__mips__) +# if SANITIZER_ANDROID + +# if defined(__aarch64__) +# define __get_tls() \ + ({ \ + void **__v; \ + __asm__("mrs %0, tpidr_el0" : "=r"(__v)); \ + __v; \ + }) +# elif defined(__arm__) +# define __get_tls() \ + ({ \ + void **__v; \ + __asm__("mrc p15, 0, %0, c13, c0, 3" : "=r"(__v)); \ + __v; \ + }) +# elif defined(__mips__) // On mips32r1, this goes via a kernel illegal instruction trap that's // optimized for v1. -# define __get_tls() \ - ({ register void** __v asm("v1"); \ - __asm__(".set push\n" \ - ".set mips32r2\n" \ - "rdhwr %0,$29\n" \ - ".set pop\n" : "=r"(__v)); \ - __v; }) -#elif defined (__riscv) -# define __get_tls() \ - ({ void** __v; __asm__("mv %0, tp" : "=r"(__v)); __v; }) -#elif defined(__i386__) -# define __get_tls() \ - ({ void** __v; __asm__("movl %%gs:0, %0" : "=r"(__v)); __v; }) -#elif defined(__x86_64__) -# define __get_tls() \ - ({ void** __v; __asm__("mov %%fs:0, %0" : "=r"(__v)); __v; }) -#else -#error "Unsupported architecture." -#endif +# define __get_tls() \ + ({ \ + register void **__v asm("v1"); \ + __asm__( \ + ".set push\n" \ + ".set mips32r2\n" \ + "rdhwr %0,$29\n" \ + ".set pop\n" \ + : "=r"(__v)); \ + __v; \ + }) +# elif defined(__riscv) +# define __get_tls() \ + ({ \ + void **__v; \ + __asm__("mv %0, tp" : "=r"(__v)); \ + __v; \ + }) +# elif defined(__i386__) +# define __get_tls() \ + ({ \ + void **__v; \ + __asm__("movl %%gs:0, %0" : "=r"(__v)); \ + __v; \ + }) +# elif defined(__x86_64__) +# define __get_tls() \ + ({ \ + void **__v; \ + __asm__("mov %%fs:0, %0" : "=r"(__v)); \ + __v; \ + }) +# else +# error "Unsupported architecture." +# endif // The Android Bionic team has allocated a TLS slot for sanitizers starting // with Q, given that Android currently doesn't support ELF TLS. It is used to @@ -175,7 +199,7 @@ ALWAYS_INLINE uptr *get_android_tls_ptr() { return reinterpret_cast<uptr *>(&__get_tls()[TLS_SLOT_SANITIZER]); } -#endif // SANITIZER_ANDROID +# endif // SANITIZER_ANDROID } // namespace __sanitizer diff --git a/libsanitizer/sanitizer_common/sanitizer_linux_libcdep.cpp b/libsanitizer/sanitizer_common/sanitizer_linux_libcdep.cpp index 62b5f09..525bc10 100644 --- a/libsanitizer/sanitizer_common/sanitizer_linux_libcdep.cpp +++ b/libsanitizer/sanitizer_common/sanitizer_linux_libcdep.cpp @@ -16,89 +16,105 @@ #if SANITIZER_FREEBSD || SANITIZER_LINUX || SANITIZER_NETBSD || \ SANITIZER_SOLARIS -#include "sanitizer_allocator_internal.h" -#include "sanitizer_atomic.h" -#include "sanitizer_common.h" -#include "sanitizer_file.h" -#include "sanitizer_flags.h" -#include "sanitizer_freebsd.h" -#include "sanitizer_getauxval.h" -#include "sanitizer_glibc_version.h" -#include "sanitizer_linux.h" -#include "sanitizer_placement_new.h" -#include "sanitizer_procmaps.h" -#include "sanitizer_solaris.h" - -#if SANITIZER_NETBSD -#define _RTLD_SOURCE // for __lwp_gettcb_fast() / __lwp_getprivate_fast() -#endif +# include "sanitizer_allocator_internal.h" +# include "sanitizer_atomic.h" +# include "sanitizer_common.h" +# include "sanitizer_file.h" +# include "sanitizer_flags.h" +# include "sanitizer_getauxval.h" +# include "sanitizer_glibc_version.h" +# include "sanitizer_linux.h" +# include "sanitizer_placement_new.h" +# include "sanitizer_procmaps.h" +# include "sanitizer_solaris.h" + +# if SANITIZER_NETBSD +# define _RTLD_SOURCE // for __lwp_gettcb_fast() / __lwp_getprivate_fast() +# endif -#include <dlfcn.h> // for dlsym() -#include <link.h> -#include <pthread.h> -#include <signal.h> -#include <sys/mman.h> -#include <sys/resource.h> -#include <syslog.h> +# include <dlfcn.h> // for dlsym() +# include <link.h> +# include <pthread.h> +# include <signal.h> +# include <sys/mman.h> +# include <sys/resource.h> +# include <syslog.h> -#if !defined(ElfW) -#define ElfW(type) Elf_##type -#endif +# if SANITIZER_GLIBC +# include <gnu/libc-version.h> +# endif + +# if !defined(ElfW) +# define ElfW(type) Elf_##type +# endif -#if SANITIZER_FREEBSD -#include <pthread_np.h> -#include <osreldate.h> -#include <sys/sysctl.h> -#define pthread_getattr_np pthread_attr_get_np +# if SANITIZER_FREEBSD +# include <pthread_np.h> +# include <sys/auxv.h> +# include <sys/sysctl.h> +# define pthread_getattr_np pthread_attr_get_np // 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 +# undef MAP_NORESERVE +# define MAP_NORESERVE 0 +extern const Elf_Auxinfo *__elf_aux_vector; +extern "C" int __sys_sigaction(int signum, const struct sigaction *act, + struct sigaction *oldact); +# endif -#if SANITIZER_NETBSD -#include <sys/sysctl.h> -#include <sys/tls.h> -#include <lwp.h> -#endif +# if SANITIZER_NETBSD +# include <lwp.h> +# include <sys/sysctl.h> +# include <sys/tls.h> +# endif -#if SANITIZER_SOLARIS -#include <stddef.h> -#include <stdlib.h> -#include <thread.h> -#endif +# if SANITIZER_SOLARIS +# include <stddef.h> +# include <stdlib.h> +# include <thread.h> +# endif -#if SANITIZER_ANDROID -#include <android/api-level.h> -#if !defined(CPU_COUNT) && !defined(__aarch64__) -#include <dirent.h> -#include <fcntl.h> +# if SANITIZER_ANDROID +# include <android/api-level.h> +# if !defined(CPU_COUNT) && !defined(__aarch64__) +# include <dirent.h> +# include <fcntl.h> struct __sanitizer::linux_dirent { - long d_ino; - off_t d_off; + long d_ino; + off_t d_off; unsigned short d_reclen; - char d_name[]; + char d_name[]; }; -#endif -#endif +# endif +# endif -#if !SANITIZER_ANDROID -#include <elf.h> -#include <unistd.h> -#endif +# if !SANITIZER_ANDROID +# include <elf.h> +# include <unistd.h> +# endif namespace __sanitizer { -SANITIZER_WEAK_ATTRIBUTE int -real_sigaction(int signum, const void *act, void *oldact); +SANITIZER_WEAK_ATTRIBUTE int real_sigaction(int signum, const void *act, + void *oldact); int internal_sigaction(int signum, const void *act, void *oldact) { -#if !SANITIZER_GO +# if SANITIZER_FREEBSD + // On FreeBSD, call the sigaction syscall directly (part of libsys in FreeBSD + // 15) since the libc version goes via a global interposing table. Due to + // library initialization order the table can be relocated after the call to + // InitializeDeadlySignals() which then crashes when dereferencing the + // uninitialized pointer in libc. + return __sys_sigaction(signum, (const struct sigaction *)act, + (struct sigaction *)oldact); +# else +# if !SANITIZER_GO if (&real_sigaction) return real_sigaction(signum, act, oldact); -#endif +# endif return sigaction(signum, (const struct sigaction *)act, (struct sigaction *)oldact); +# endif } void GetThreadStackTopAndBottom(bool at_initialization, uptr *stack_top, @@ -111,7 +127,7 @@ void GetThreadStackTopAndBottom(bool at_initialization, uptr *stack_top, CHECK_EQ(getrlimit(RLIMIT_STACK, &rl), 0); // Find the mapping that contains a stack variable. - MemoryMappingLayout proc_maps(/*cache_enabled*/true); + MemoryMappingLayout proc_maps(/*cache_enabled*/ true); if (proc_maps.Error()) { *stack_top = *stack_bottom = 0; return; @@ -119,7 +135,8 @@ void GetThreadStackTopAndBottom(bool at_initialization, uptr *stack_top, MemoryMappedSegment segment; uptr prev_end = 0; while (proc_maps.Next(&segment)) { - if ((uptr)&rl < segment.end) break; + if ((uptr)&rl < segment.end) + break; prev_end = segment.end; } CHECK((uptr)&rl >= segment.start && (uptr)&rl < segment.end); @@ -127,7 +144,8 @@ void GetThreadStackTopAndBottom(bool at_initialization, uptr *stack_top, // Get stacksize from rlimit, but clip it so that it does not overlap // with other mappings. uptr stacksize = rl.rlim_cur; - if (stacksize > segment.end - prev_end) stacksize = segment.end - prev_end; + if (stacksize > segment.end - prev_end) + stacksize = segment.end - prev_end; // When running with unlimited stack size, we still want to set some limit. // The unlimited stack size is caused by 'ulimit -s unlimited'. // Also, for some reason, GNU make spawns subprocesses with unlimited stack. @@ -135,60 +153,52 @@ void GetThreadStackTopAndBottom(bool at_initialization, uptr *stack_top, stacksize = kMaxThreadStackSize; *stack_top = segment.end; *stack_bottom = segment.end - stacksize; + + uptr maxAddr = GetMaxUserVirtualAddress(); + // Edge case: the stack mapping on some systems may be off-by-one e.g., + // fffffffdf000-1000000000000 rw-p 00000000 00:00 0 [stack] + // instead of: + // fffffffdf000- ffffffffffff + // The out-of-range stack_top can result in an invalid shadow address + // calculation, since those usually assume the parameters are in range. + if (*stack_top == maxAddr + 1) + *stack_top = maxAddr; + else + CHECK_LE(*stack_top, maxAddr); + return; } uptr stacksize = 0; void *stackaddr = nullptr; -#if SANITIZER_SOLARIS +# if SANITIZER_SOLARIS stack_t ss; CHECK_EQ(thr_stksegment(&ss), 0); stacksize = ss.ss_size; stackaddr = (char *)ss.ss_sp - stacksize; -#else // !SANITIZER_SOLARIS +# else // !SANITIZER_SOLARIS pthread_attr_t attr; pthread_attr_init(&attr); CHECK_EQ(pthread_getattr_np(pthread_self(), &attr), 0); internal_pthread_attr_getstack(&attr, &stackaddr, &stacksize); pthread_attr_destroy(&attr); -#endif // SANITIZER_SOLARIS +# endif // SANITIZER_SOLARIS *stack_top = (uptr)stackaddr + stacksize; *stack_bottom = (uptr)stackaddr; } -#if !SANITIZER_GO +# if !SANITIZER_GO bool SetEnv(const char *name, const char *value) { void *f = dlsym(RTLD_NEXT, "setenv"); if (!f) return false; - typedef int(*setenv_ft)(const char *name, const char *value, int overwrite); + typedef int (*setenv_ft)(const char *name, const char *value, int overwrite); setenv_ft setenv_f; CHECK_EQ(sizeof(setenv_f), sizeof(f)); internal_memcpy(&setenv_f, &f, sizeof(f)); return setenv_f(name, value, 1) == 0; } -#endif - -__attribute__((unused)) static bool GetLibcVersion(int *major, int *minor, - int *patch) { -#ifdef _CS_GNU_LIBC_VERSION - char buf[64]; - uptr len = confstr(_CS_GNU_LIBC_VERSION, buf, sizeof(buf)); - if (len >= sizeof(buf)) - return false; - buf[len] = 0; - static const char kGLibC[] = "glibc "; - if (internal_strncmp(buf, kGLibC, sizeof(kGLibC) - 1) != 0) - return false; - const char *p = buf + sizeof(kGLibC) - 1; - *major = internal_simple_strtoll(p, &p, 10); - *minor = (*p == '.') ? internal_simple_strtoll(p + 1, &p, 10) : 0; - *patch = (*p == '.') ? internal_simple_strtoll(p + 1, &p, 10) : 0; - return true; -#else - return false; -#endif -} +# endif // True if we can use dlpi_tls_data. glibc before 2.25 may leave NULL (BZ // #19826) so dlpi_tls_data cannot be used. @@ -198,136 +208,196 @@ __attribute__((unused)) static bool GetLibcVersion(int *major, int *minor, // https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=254774 __attribute__((unused)) static int g_use_dlpi_tls_data; -#if SANITIZER_GLIBC && !SANITIZER_GO -__attribute__((unused)) static size_t g_tls_size; -void InitTlsSize() { - int major, minor, patch; - g_use_dlpi_tls_data = - GetLibcVersion(&major, &minor, &patch) && major == 2 && minor >= 25; - -#if defined(__aarch64__) || defined(__x86_64__) || defined(__powerpc64__) || \ - defined(__loongarch__) - void *get_tls_static_info = dlsym(RTLD_NEXT, "_dl_get_tls_static_info"); - size_t tls_align; - ((void (*)(size_t *, size_t *))get_tls_static_info)(&g_tls_size, &tls_align); -#endif +# if SANITIZER_GLIBC && !SANITIZER_GO +static void GetGLibcVersion(int *major, int *minor, int *patch) { + const char *p = gnu_get_libc_version(); + *major = internal_simple_strtoll(p, &p, 10); + // Caller does not expect anything else. + CHECK_EQ(*major, 2); + *minor = (*p == '.') ? internal_simple_strtoll(p + 1, &p, 10) : 0; + *patch = (*p == '.') ? internal_simple_strtoll(p + 1, &p, 10) : 0; } -#else -void InitTlsSize() { } -#endif // SANITIZER_GLIBC && !SANITIZER_GO - -// On glibc x86_64, ThreadDescriptorSize() needs to be precise due to the usage -// of g_tls_size. On other targets, ThreadDescriptorSize() is only used by lsan -// to get the pointer to thread-specific data keys in the thread control block. -#if (SANITIZER_FREEBSD || SANITIZER_LINUX || SANITIZER_SOLARIS) && \ - !SANITIZER_ANDROID && !SANITIZER_GO -// sizeof(struct pthread) from glibc. -static atomic_uintptr_t thread_descriptor_size; static uptr ThreadDescriptorSizeFallback() { - uptr val = 0; -#if defined(__x86_64__) || defined(__i386__) || defined(__arm__) +# if defined(__x86_64__) || defined(__i386__) || defined(__arm__) || \ + SANITIZER_RISCV64 int major; int minor; int patch; - if (GetLibcVersion(&major, &minor, &patch) && major == 2) { - /* sizeof(struct pthread) values from various glibc versions. */ - if (SANITIZER_X32) - val = 1728; // Assume only one particular version for x32. - // For ARM sizeof(struct pthread) changed in Glibc 2.23. - else if (SANITIZER_ARM) - val = minor <= 22 ? 1120 : 1216; - else if (minor <= 3) - val = FIRST_32_SECOND_64(1104, 1696); - else if (minor == 4) - val = FIRST_32_SECOND_64(1120, 1728); - else if (minor == 5) - val = FIRST_32_SECOND_64(1136, 1728); - else if (minor <= 9) - val = FIRST_32_SECOND_64(1136, 1712); - else if (minor == 10) - val = FIRST_32_SECOND_64(1168, 1776); - else if (minor == 11 || (minor == 12 && patch == 1)) - val = FIRST_32_SECOND_64(1168, 2288); - else if (minor <= 14) - val = FIRST_32_SECOND_64(1168, 2304); - else if (minor < 32) // Unknown version - val = FIRST_32_SECOND_64(1216, 2304); - else // minor == 32 - val = FIRST_32_SECOND_64(1344, 2496); - } -#elif defined(__s390__) || defined(__sparc__) + GetGLibcVersion(&major, &minor, &patch); +# endif + +# if defined(__x86_64__) || defined(__i386__) || defined(__arm__) + /* sizeof(struct pthread) values from various glibc versions. */ + if (SANITIZER_X32) + return 1728; // Assume only one particular version for x32. + // For ARM sizeof(struct pthread) changed in Glibc 2.23. + if (SANITIZER_ARM) + return minor <= 22 ? 1120 : 1216; + if (minor <= 3) + return FIRST_32_SECOND_64(1104, 1696); + if (minor == 4) + return FIRST_32_SECOND_64(1120, 1728); + if (minor == 5) + return FIRST_32_SECOND_64(1136, 1728); + if (minor <= 9) + return FIRST_32_SECOND_64(1136, 1712); + if (minor == 10) + return FIRST_32_SECOND_64(1168, 1776); + if (minor == 11 || (minor == 12 && patch == 1)) + return FIRST_32_SECOND_64(1168, 2288); + if (minor <= 14) + return FIRST_32_SECOND_64(1168, 2304); + if (minor < 32) // Unknown version + return FIRST_32_SECOND_64(1216, 2304); + // minor == 32 + return FIRST_32_SECOND_64(1344, 2496); +# endif + +# if SANITIZER_RISCV64 + // TODO: consider adding an optional runtime check for an unknown (untested) + // glibc version + if (minor <= 28) // WARNING: the highest tested version is 2.29 + return 1772; // no guarantees for this one + if (minor <= 31) + return 1772; // tested against glibc 2.29, 2.31 + return 1936; // tested against glibc 2.32 +# endif + +# if defined(__s390__) || defined(__sparc__) // The size of a prefix of TCB including pthread::{specific_1stblock,specific} // suffices. Just return offsetof(struct pthread, specific_used), which hasn't // changed since 2007-05. Technically this applies to i386/x86_64 as well but // we call _dl_get_tls_static_info and need the precise size of struct // pthread. return FIRST_32_SECOND_64(524, 1552); -#elif defined(__mips__) +# endif + +# if defined(__mips__) // TODO(sagarthakur): add more values as per different glibc versions. - val = FIRST_32_SECOND_64(1152, 1776); -#elif SANITIZER_LOONGARCH64 - val = 1856; // from glibc 2.36 -#elif SANITIZER_RISCV64 - int major; - int minor; - int patch; - if (GetLibcVersion(&major, &minor, &patch) && major == 2) { - // TODO: consider adding an optional runtime check for an unknown (untested) - // glibc version - if (minor <= 28) // WARNING: the highest tested version is 2.29 - val = 1772; // no guarantees for this one - else if (minor <= 31) - val = 1772; // tested against glibc 2.29, 2.31 - else - val = 1936; // tested against glibc 2.32 - } + return FIRST_32_SECOND_64(1152, 1776); +# endif -#elif defined(__aarch64__) +# if SANITIZER_LOONGARCH64 + return 1856; // from glibc 2.36 +# endif + +# if defined(__aarch64__) // The sizeof (struct pthread) is the same from GLIBC 2.17 to 2.22. - val = 1776; -#elif defined(__powerpc64__) - val = 1776; // from glibc.ppc64le 2.20-8.fc21 -#endif - return val; + return 1776; +# endif + +# if defined(__powerpc64__) + return 1776; // from glibc.ppc64le 2.20-8.fc21 +# endif +} +# endif // SANITIZER_GLIBC && !SANITIZER_GO + +# if SANITIZER_FREEBSD && !SANITIZER_GO +// FIXME: Implementation is very GLIBC specific, but it's used by FreeBSD. +static uptr ThreadDescriptorSizeFallback() { +# if defined(__s390__) || defined(__sparc__) + // The size of a prefix of TCB including pthread::{specific_1stblock,specific} + // suffices. Just return offsetof(struct pthread, specific_used), which hasn't + // changed since 2007-05. Technically this applies to i386/x86_64 as well but + // we call _dl_get_tls_static_info and need the precise size of struct + // pthread. + return FIRST_32_SECOND_64(524, 1552); +# endif + +# if defined(__mips__) + // TODO(sagarthakur): add more values as per different glibc versions. + return FIRST_32_SECOND_64(1152, 1776); +# endif + +# if SANITIZER_LOONGARCH64 + return 1856; // from glibc 2.36 +# endif + +# if defined(__aarch64__) + // The sizeof (struct pthread) is the same from GLIBC 2.17 to 2.22. + return 1776; +# endif + +# if defined(__powerpc64__) + return 1776; // from glibc.ppc64le 2.20-8.fc21 +# endif + + return 0; } +# endif // SANITIZER_FREEBSD && !SANITIZER_GO + +# if (SANITIZER_FREEBSD || SANITIZER_GLIBC) && !SANITIZER_GO +// On glibc x86_64, ThreadDescriptorSize() needs to be precise due to the usage +// of g_tls_size. On other targets, ThreadDescriptorSize() is only used by lsan +// to get the pointer to thread-specific data keys in the thread control block. +// sizeof(struct pthread) from glibc. +static uptr thread_descriptor_size; -uptr ThreadDescriptorSize() { - uptr val = atomic_load_relaxed(&thread_descriptor_size); - if (val) - return val; - // _thread_db_sizeof_pthread is a GLIBC_PRIVATE symbol that is exported in - // glibc 2.34 and later. - if (unsigned *psizeof = static_cast<unsigned *>( - dlsym(RTLD_DEFAULT, "_thread_db_sizeof_pthread"))) - val = *psizeof; - if (!val) - val = ThreadDescriptorSizeFallback(); - atomic_store_relaxed(&thread_descriptor_size, val); - return val; +uptr ThreadDescriptorSize() { return thread_descriptor_size; } + +# if SANITIZER_GLIBC +__attribute__((unused)) static size_t g_tls_size; +# endif + +void InitTlsSize() { +# if SANITIZER_GLIBC + int major, minor, patch; + GetGLibcVersion(&major, &minor, &patch); + g_use_dlpi_tls_data = major == 2 && minor >= 25; + + if (major == 2 && minor >= 34) { + // _thread_db_sizeof_pthread is a GLIBC_PRIVATE symbol that is exported in + // glibc 2.34 and later. + if (unsigned *psizeof = static_cast<unsigned *>( + dlsym(RTLD_DEFAULT, "_thread_db_sizeof_pthread"))) { + thread_descriptor_size = *psizeof; + } + } + +# if defined(__aarch64__) || defined(__x86_64__) || \ + defined(__powerpc64__) || defined(__loongarch__) + auto *get_tls_static_info = (void (*)(size_t *, size_t *))dlsym( + RTLD_DEFAULT, "_dl_get_tls_static_info"); + size_t tls_align; + // Can be null if static link. + if (get_tls_static_info) + get_tls_static_info(&g_tls_size, &tls_align); +# endif + +# endif // SANITIZER_GLIBC + + if (!thread_descriptor_size) + thread_descriptor_size = ThreadDescriptorSizeFallback(); } -#if defined(__mips__) || defined(__powerpc64__) || SANITIZER_RISCV64 || \ - SANITIZER_LOONGARCH64 +# if defined(__mips__) || defined(__powerpc64__) || SANITIZER_RISCV64 || \ + SANITIZER_LOONGARCH64 // TlsPreTcbSize includes size of struct pthread_descr and size of tcb // head structure. It lies before the static tls blocks. static uptr TlsPreTcbSize() { -#if defined(__mips__) - const uptr kTcbHead = 16; // sizeof (tcbhead_t) -#elif defined(__powerpc64__) - const uptr kTcbHead = 88; // sizeof (tcbhead_t) -#elif SANITIZER_RISCV64 +# if defined(__mips__) const uptr kTcbHead = 16; // sizeof (tcbhead_t) -#elif SANITIZER_LOONGARCH64 +# elif defined(__powerpc64__) + const uptr kTcbHead = 88; // sizeof (tcbhead_t) +# elif SANITIZER_RISCV64 const uptr kTcbHead = 16; // sizeof (tcbhead_t) -#endif +# elif SANITIZER_LOONGARCH64 + const uptr kTcbHead = 16; // sizeof (tcbhead_t) +# endif const uptr kTlsAlign = 16; const uptr kTlsPreTcbSize = RoundUpTo(ThreadDescriptorSize() + kTcbHead, kTlsAlign); return kTlsPreTcbSize; } -#endif - +# endif +# else // (SANITIZER_FREEBSD || SANITIZER_GLIBC) && !SANITIZER_GO +void InitTlsSize() {} +uptr ThreadDescriptorSize() { return 0; } +# endif // (SANITIZER_FREEBSD || SANITIZER_GLIBC) && !SANITIZER_GO + +# if (SANITIZER_FREEBSD || SANITIZER_LINUX || SANITIZER_SOLARIS) && \ + !SANITIZER_ANDROID && !SANITIZER_GO namespace { struct TlsBlock { uptr begin, end, align; @@ -336,7 +406,7 @@ struct TlsBlock { }; } // namespace -#ifdef __s390__ +# ifdef __s390__ extern "C" uptr __tls_get_offset(void *arg); static uptr TlsGetOffset(uptr ti_module, uptr ti_offset) { @@ -354,16 +424,16 @@ static uptr TlsGetOffset(uptr ti_module, uptr ti_offset) { : "memory", "cc", "0", "1", "3", "4", "5", "14"); return r2; } -#else +# else extern "C" void *__tls_get_addr(size_t *); -#endif +# endif static size_t main_tls_modid; static int CollectStaticTlsBlocks(struct dl_phdr_info *info, size_t size, void *data) { size_t tls_modid; -#if SANITIZER_SOLARIS +# if SANITIZER_SOLARIS // dlpi_tls_modid is only available since Solaris 11.4 SRU 10. Use // dlinfo(RTLD_DI_LINKMAP) instead which works on all of Solaris 11.3, // 11.4, and Illumos. The tlsmodid of the executable was changed to 1 in @@ -376,27 +446,26 @@ static int CollectStaticTlsBlocks(struct dl_phdr_info *info, size_t size, Rt_map *map; dlinfo(RTLD_SELF, RTLD_DI_LINKMAP, &map); tls_modid = map->rt_tlsmodid; -#else +# else main_tls_modid = 1; tls_modid = info->dlpi_tls_modid; -#endif +# endif if (tls_modid < main_tls_modid) return 0; uptr begin; -#if !SANITIZER_SOLARIS +# if !SANITIZER_SOLARIS begin = (uptr)info->dlpi_tls_data; -#endif +# endif if (!g_use_dlpi_tls_data) { // Call __tls_get_addr as a fallback. This forces TLS allocation on glibc // and FreeBSD. -#ifdef __s390__ - begin = (uptr)__builtin_thread_pointer() + - TlsGetOffset(tls_modid, 0); -#else +# ifdef __s390__ + begin = (uptr)__builtin_thread_pointer() + TlsGetOffset(tls_modid, 0); +# else size_t mod_and_off[2] = {tls_modid, 0}; begin = (uptr)__tls_get_addr(mod_and_off); -#endif +# endif } for (unsigned i = 0; i != info->dlpi_phnum; ++i) if (info->dlpi_phdr[i].p_type == PT_TLS) { @@ -439,23 +508,21 @@ __attribute__((unused)) static void GetStaticTlsBoundary(uptr *addr, uptr *size, *addr = ranges[l].begin; *size = ranges[r - 1].end - ranges[l].begin; } -#endif // (x86_64 || i386 || mips || ...) && (SANITIZER_FREEBSD || - // SANITIZER_LINUX) && !SANITIZER_ANDROID && !SANITIZER_GO +# endif // (x86_64 || i386 || mips || ...) && (SANITIZER_FREEBSD || + // SANITIZER_LINUX) && !SANITIZER_ANDROID && !SANITIZER_GO -#if SANITIZER_NETBSD -static struct tls_tcb * ThreadSelfTlsTcb() { +# if SANITIZER_NETBSD +static struct tls_tcb *ThreadSelfTlsTcb() { struct tls_tcb *tcb = nullptr; -#ifdef __HAVE___LWP_GETTCB_FAST +# ifdef __HAVE___LWP_GETTCB_FAST tcb = (struct tls_tcb *)__lwp_gettcb_fast(); -#elif defined(__HAVE___LWP_GETPRIVATE_FAST) +# elif defined(__HAVE___LWP_GETPRIVATE_FAST) tcb = (struct tls_tcb *)__lwp_getprivate_fast(); -#endif +# endif return tcb; } -uptr ThreadSelf() { - return (uptr)ThreadSelfTlsTcb()->tcb_pthread; -} +uptr ThreadSelf() { return (uptr)ThreadSelfTlsTcb()->tcb_pthread; } int GetSizeFromHdr(struct dl_phdr_info *info, size_t size, void *data) { const Elf_Phdr *hdr = info->dlpi_phdr; @@ -463,23 +530,23 @@ int GetSizeFromHdr(struct dl_phdr_info *info, size_t size, void *data) { for (; hdr != last_hdr; ++hdr) { if (hdr->p_type == PT_TLS && info->dlpi_tls_modid == 1) { - *(uptr*)data = hdr->p_memsz; + *(uptr *)data = hdr->p_memsz; break; } } return 0; } -#endif // SANITIZER_NETBSD +# endif // SANITIZER_NETBSD -#if SANITIZER_ANDROID +# if SANITIZER_ANDROID // Bionic provides this API since S. extern "C" SANITIZER_WEAK_ATTRIBUTE void __libc_get_static_tls_bounds(void **, void **); -#endif +# endif -#if !SANITIZER_GO +# if !SANITIZER_GO static void GetTls(uptr *addr, uptr *size) { -#if SANITIZER_ANDROID +# if SANITIZER_ANDROID if (&__libc_get_static_tls_bounds) { void *start_addr; void *end_addr; @@ -491,48 +558,48 @@ static void GetTls(uptr *addr, uptr *size) { *addr = 0; *size = 0; } -#elif SANITIZER_GLIBC && defined(__x86_64__) +# elif SANITIZER_GLIBC && defined(__x86_64__) // For aarch64 and x86-64, use an O(1) approach which requires relatively // precise ThreadDescriptorSize. g_tls_size was initialized in InitTlsSize. -# if SANITIZER_X32 +# if SANITIZER_X32 asm("mov %%fs:8,%0" : "=r"(*addr)); -# else +# else asm("mov %%fs:16,%0" : "=r"(*addr)); -# endif +# endif *size = g_tls_size; *addr -= *size; *addr += ThreadDescriptorSize(); -#elif SANITIZER_GLIBC && defined(__aarch64__) +# elif SANITIZER_GLIBC && defined(__aarch64__) *addr = reinterpret_cast<uptr>(__builtin_thread_pointer()) - ThreadDescriptorSize(); *size = g_tls_size + ThreadDescriptorSize(); -#elif SANITIZER_GLIBC && defined(__loongarch__) -# ifdef __clang__ +# elif SANITIZER_GLIBC && defined(__loongarch__) +# ifdef __clang__ *addr = reinterpret_cast<uptr>(__builtin_thread_pointer()) - ThreadDescriptorSize(); -# else +# else asm("or %0,$tp,$zero" : "=r"(*addr)); *addr -= ThreadDescriptorSize(); -# endif +# endif *size = g_tls_size + ThreadDescriptorSize(); -#elif SANITIZER_GLIBC && defined(__powerpc64__) +# elif SANITIZER_GLIBC && defined(__powerpc64__) // Workaround for glibc<2.25(?). 2.27 is known to not need this. uptr tp; asm("addi %0,13,-0x7000" : "=r"(tp)); const uptr pre_tcb_size = TlsPreTcbSize(); *addr = tp - pre_tcb_size; *size = g_tls_size + pre_tcb_size; -#elif SANITIZER_FREEBSD || SANITIZER_LINUX || SANITIZER_SOLARIS +# elif SANITIZER_FREEBSD || SANITIZER_LINUX || SANITIZER_SOLARIS uptr align; GetStaticTlsBoundary(addr, size, &align); -#if defined(__x86_64__) || defined(__i386__) || defined(__s390__) || \ - defined(__sparc__) +# if defined(__x86_64__) || defined(__i386__) || defined(__s390__) || \ + defined(__sparc__) if (SANITIZER_GLIBC) { -#if defined(__x86_64__) || defined(__i386__) +# if defined(__x86_64__) || defined(__i386__) align = Max<uptr>(align, 64); -#else +# else align = Max<uptr>(align, 16); -#endif +# endif } const uptr tp = RoundUpTo(*addr + *size, align); @@ -551,26 +618,26 @@ static void GetTls(uptr *addr, uptr *size) { // because the number of bytes after pthread::specific is larger. *addr = tp - RoundUpTo(*size, align); *size = tp - *addr + ThreadDescriptorSize(); -#else +# else if (SANITIZER_GLIBC) *size += 1664; else if (SANITIZER_FREEBSD) *size += 128; // RTLD_STATIC_TLS_EXTRA -#if defined(__mips__) || defined(__powerpc64__) || SANITIZER_RISCV64 +# if defined(__mips__) || defined(__powerpc64__) || SANITIZER_RISCV64 const uptr pre_tcb_size = TlsPreTcbSize(); *addr -= pre_tcb_size; *size += pre_tcb_size; -#else +# else // arm and aarch64 reserve two words at TP, so this underestimates the range. // However, this is sufficient for the purpose of finding the pointers to // thread-specific data keys. const uptr tcb_size = ThreadDescriptorSize(); *addr -= tcb_size; *size += tcb_size; -#endif -#endif -#elif SANITIZER_NETBSD - struct tls_tcb * const tcb = ThreadSelfTlsTcb(); +# endif +# endif +# elif SANITIZER_NETBSD + struct tls_tcb *const tcb = ThreadSelfTlsTcb(); *addr = 0; *size = 0; if (tcb != 0) { @@ -583,56 +650,59 @@ static void GetTls(uptr *addr, uptr *size) { *addr = (uptr)tcb->tcb_dtv[1]; } } -#else -#error "Unknown OS" -#endif +# else +# error "Unknown OS" +# endif } -#endif +# endif -#if !SANITIZER_GO +# if !SANITIZER_GO uptr GetTlsSize() { -#if SANITIZER_FREEBSD || SANITIZER_LINUX || SANITIZER_NETBSD || \ - SANITIZER_SOLARIS +# if SANITIZER_FREEBSD || SANITIZER_LINUX || SANITIZER_NETBSD || \ + SANITIZER_SOLARIS uptr addr, size; GetTls(&addr, &size); return size; -#else +# else return 0; -#endif +# endif } -#endif +# endif -void GetThreadStackAndTls(bool main, uptr *stk_addr, uptr *stk_size, - uptr *tls_addr, uptr *tls_size) { -#if SANITIZER_GO +void GetThreadStackAndTls(bool main, uptr *stk_begin, uptr *stk_end, + uptr *tls_begin, uptr *tls_end) { +# if SANITIZER_GO // Stub implementation for Go. - *stk_addr = *stk_size = *tls_addr = *tls_size = 0; -#else - GetTls(tls_addr, tls_size); + *stk_begin = 0; + *stk_end = 0; + *tls_begin = 0; + *tls_end = 0; +# else + uptr tls_addr = 0; + uptr tls_size = 0; + GetTls(&tls_addr, &tls_size); + *tls_begin = tls_addr; + *tls_end = tls_addr + tls_size; uptr stack_top, stack_bottom; GetThreadStackTopAndBottom(main, &stack_top, &stack_bottom); - *stk_addr = stack_bottom; - *stk_size = stack_top - stack_bottom; + *stk_begin = stack_bottom; + *stk_end = stack_top; if (!main) { // If stack and tls intersect, make them non-intersecting. - if (*tls_addr > *stk_addr && *tls_addr < *stk_addr + *stk_size) { - if (*stk_addr + *stk_size < *tls_addr + *tls_size) - *tls_size = *stk_addr + *stk_size - *tls_addr; - *stk_size = *tls_addr - *stk_addr; + if (*tls_begin > *stk_begin && *tls_begin < *stk_end) { + if (*stk_end < *tls_end) + *tls_end = *stk_end; + *stk_end = *tls_begin; } } -#endif +# endif } -#if !SANITIZER_FREEBSD +# if !SANITIZER_FREEBSD typedef ElfW(Phdr) Elf_Phdr; -#elif SANITIZER_WORDSIZE == 32 && __FreeBSD_version <= 902001 // v9.2 -#define Elf_Phdr XElf32_Phdr -#define dl_phdr_info xdl_phdr_info -#define dl_iterate_phdr(c, b) xdl_iterate_phdr((c), (b)) -#endif // !SANITIZER_FREEBSD +# endif struct DlIteratePhdrData { InternalMmapVectorNoCtor<LoadedModule> *modules; @@ -652,8 +722,7 @@ static int AddModuleSegments(const char *module_name, dl_phdr_info *info, uptr cur_end = cur_beg + phdr->p_memsz; bool executable = phdr->p_flags & PF_X; bool writable = phdr->p_flags & PF_W; - cur_module.addAddressRange(cur_beg, cur_end, executable, - writable); + cur_module.addAddressRange(cur_beg, cur_end, executable, writable); } else if (phdr->p_type == PT_NOTE) { # ifdef NT_GNU_BUILD_ID uptr off = 0; @@ -704,24 +773,24 @@ static int dl_iterate_phdr_cb(dl_phdr_info *info, size_t size, void *arg) { return 0; } -#if SANITIZER_ANDROID && __ANDROID_API__ < 21 +# if SANITIZER_ANDROID && __ANDROID_API__ < 21 extern "C" __attribute__((weak)) int dl_iterate_phdr( int (*)(struct dl_phdr_info *, size_t, void *), void *); -#endif +# endif static bool requiresProcmaps() { -#if SANITIZER_ANDROID && __ANDROID_API__ <= 22 +# if SANITIZER_ANDROID && __ANDROID_API__ <= 22 // Fall back to /proc/maps if dl_iterate_phdr is unavailable or broken. // The runtime check allows the same library to work with // both K and L (and future) Android releases. return AndroidGetApiLevel() <= ANDROID_LOLLIPOP_MR1; -#else +# else return false; -#endif +# endif } static void procmapsInit(InternalMmapVectorNoCtor<LoadedModule> *modules) { - MemoryMappingLayout memory_mapping(/*cache_enabled*/true); + MemoryMappingLayout memory_mapping(/*cache_enabled*/ true); memory_mapping.DumpListOfModules(modules); } @@ -773,22 +842,19 @@ uptr GetRSS() { // We need the second number which is RSS in pages. char *pos = buf; // Skip the first number. - while (*pos >= '0' && *pos <= '9') - pos++; + while (*pos >= '0' && *pos <= '9') pos++; // Skip whitespaces. - while (!(*pos >= '0' && *pos <= '9') && *pos != 0) - pos++; + while (!(*pos >= '0' && *pos <= '9') && *pos != 0) pos++; // Read the number. uptr rss = 0; - while (*pos >= '0' && *pos <= '9') - rss = rss * 10 + *pos++ - '0'; + while (*pos >= '0' && *pos <= '9') rss = rss * 10 + *pos++ - '0'; return rss * GetPageSizeCached(); } // sysconf(_SC_NPROCESSORS_{CONF,ONLN}) cannot be used on most platforms as // they allocate memory. u32 GetNumberOfCPUs() { -#if SANITIZER_FREEBSD || SANITIZER_NETBSD +# if SANITIZER_FREEBSD || SANITIZER_NETBSD u32 ncpu; int req[2]; uptr len = sizeof(ncpu); @@ -796,7 +862,7 @@ u32 GetNumberOfCPUs() { req[1] = HW_NCPU; CHECK_EQ(internal_sysctl(req, 2, &ncpu, &len, NULL, 0), 0); return ncpu; -#elif SANITIZER_ANDROID && !defined(CPU_COUNT) && !defined(__aarch64__) +# elif SANITIZER_ANDROID && !defined(CPU_COUNT) && !defined(__aarch64__) // Fall back to /sys/devices/system/cpu on Android when cpu_set_t doesn't // exist in sched.h. That is the case for toolchains generated with older // NDKs. @@ -824,30 +890,26 @@ u32 GetNumberOfCPUs() { break; if (entry->d_ino != 0 && *d_type == DT_DIR) { if (entry->d_name[0] == 'c' && entry->d_name[1] == 'p' && - entry->d_name[2] == 'u' && - entry->d_name[3] >= '0' && entry->d_name[3] <= '9') + entry->d_name[2] == 'u' && entry->d_name[3] >= '0' && + entry->d_name[3] <= '9') n_cpus++; } entry = (struct linux_dirent *)(((u8 *)entry) + entry->d_reclen); } internal_close(fd); return n_cpus; -#elif SANITIZER_SOLARIS +# elif SANITIZER_SOLARIS return sysconf(_SC_NPROCESSORS_ONLN); -#else -#if defined(CPU_COUNT) +# else cpu_set_t CPUs; CHECK_EQ(sched_getaffinity(0, sizeof(cpu_set_t), &CPUs), 0); return CPU_COUNT(&CPUs); -#else - return 1; -#endif -#endif +# endif } -#if SANITIZER_LINUX +# if SANITIZER_LINUX -#if SANITIZER_ANDROID +# if SANITIZER_ANDROID static atomic_uint8_t android_log_initialized; void AndroidLogInit() { @@ -859,13 +921,15 @@ static bool ShouldLogAfterPrintf() { return atomic_load(&android_log_initialized, memory_order_acquire); } -extern "C" SANITIZER_WEAK_ATTRIBUTE -int async_safe_write_log(int pri, const char* tag, const char* msg); -extern "C" SANITIZER_WEAK_ATTRIBUTE -int __android_log_write(int prio, const char* tag, const char* msg); +extern "C" SANITIZER_WEAK_ATTRIBUTE int async_safe_write_log(int pri, + const char *tag, + const char *msg); +extern "C" SANITIZER_WEAK_ATTRIBUTE int __android_log_write(int prio, + const char *tag, + const char *msg); // ANDROID_LOG_INFO is 4, but can't be resolved at runtime. -#define SANITIZER_ANDROID_LOG_INFO 4 +# define SANITIZER_ANDROID_LOG_INFO 4 // async_safe_write_log is a new public version of __libc_write_log that is // used behind syslog. It is preferable to syslog as it will not do any dynamic @@ -884,14 +948,14 @@ void WriteOneLineToSyslog(const char *s) { } } -extern "C" SANITIZER_WEAK_ATTRIBUTE -void android_set_abort_message(const char *); +extern "C" SANITIZER_WEAK_ATTRIBUTE void android_set_abort_message( + const char *); void SetAbortMessage(const char *str) { if (&android_set_abort_message) android_set_abort_message(str); } -#else +# else void AndroidLogInit() {} static bool ShouldLogAfterPrintf() { return true; } @@ -899,16 +963,16 @@ static bool ShouldLogAfterPrintf() { return true; } void WriteOneLineToSyslog(const char *s) { syslog(LOG_INFO, "%s", s); } void SetAbortMessage(const char *str) {} -#endif // SANITIZER_ANDROID +# endif // SANITIZER_ANDROID void LogMessageOnPrintf(const char *str) { if (common_flags()->log_to_syslog && ShouldLogAfterPrintf()) WriteToSyslog(str); } -#endif // SANITIZER_LINUX +# endif // SANITIZER_LINUX -#if SANITIZER_GLIBC && !SANITIZER_GO +# if SANITIZER_GLIBC && !SANITIZER_GO // glibc crashes when using clock_gettime from a preinit_array function as the // vDSO function pointers haven't been initialized yet. __progname is // initialized after the vDSO function pointers, so if it exists, is not null @@ -919,8 +983,8 @@ inline bool CanUseVDSO() { return &__progname && __progname && *__progname; } // MonotonicNanoTime is a timing function that can leverage the vDSO by calling // clock_gettime. real_clock_gettime only exists if clock_gettime is // intercepted, so define it weakly and use it if available. -extern "C" SANITIZER_WEAK_ATTRIBUTE -int real_clock_gettime(u32 clk_id, void *tp); +extern "C" SANITIZER_WEAK_ATTRIBUTE int real_clock_gettime(u32 clk_id, + void *tp); u64 MonotonicNanoTime() { timespec ts; if (CanUseVDSO()) { @@ -933,19 +997,26 @@ u64 MonotonicNanoTime() { } return (u64)ts.tv_sec * (1000ULL * 1000 * 1000) + ts.tv_nsec; } -#else +# else // Non-glibc & Go always use the regular function. u64 MonotonicNanoTime() { timespec ts; clock_gettime(CLOCK_MONOTONIC, &ts); return (u64)ts.tv_sec * (1000ULL * 1000 * 1000) + ts.tv_nsec; } -#endif // SANITIZER_GLIBC && !SANITIZER_GO +# endif // SANITIZER_GLIBC && !SANITIZER_GO void ReExec() { const char *pathname = "/proc/self/exe"; -#if SANITIZER_NETBSD +# if SANITIZER_FREEBSD + for (const auto *aux = __elf_aux_vector; aux->a_type != AT_NULL; aux++) { + if (aux->a_type == AT_EXECPATH) { + pathname = static_cast<const char *>(aux->a_un.a_ptr); + break; + } + } +# elif SANITIZER_NETBSD static const int name[] = { CTL_KERN, KERN_PROC_ARGS, @@ -958,14 +1029,14 @@ void ReExec() { len = sizeof(path); if (internal_sysctl(name, ARRAY_SIZE(name), path, &len, NULL, 0) != -1) pathname = path; -#elif SANITIZER_SOLARIS +# elif SANITIZER_SOLARIS pathname = getexecname(); CHECK_NE(pathname, NULL); -#elif SANITIZER_USE_GETAUXVAL +# elif SANITIZER_USE_GETAUXVAL // Calling execve with /proc/self/exe sets that as $EXEC_ORIGIN. Binaries that // rely on that will fail to load shared libraries. Query AT_EXECFN instead. pathname = reinterpret_cast<const char *>(getauxval(AT_EXECFN)); -#endif +# endif uptr rv = internal_execve(pathname, GetArgv(), GetEnviron()); int rverrno; @@ -987,9 +1058,8 @@ void UnmapFromTo(uptr from, uptr to) { } uptr MapDynamicShadow(uptr shadow_size_bytes, uptr shadow_scale, - uptr min_shadow_base_alignment, - UNUSED uptr &high_mem_end) { - const uptr granularity = GetMmapGranularity(); + uptr min_shadow_base_alignment, UNUSED uptr &high_mem_end, + uptr granularity) { const uptr alignment = Max<uptr>(granularity << shadow_scale, 1ULL << min_shadow_base_alignment); const uptr left_padding = @@ -1017,14 +1087,14 @@ static uptr MmapSharedNoReserve(uptr addr, uptr size) { static uptr MremapCreateAlias(uptr base_addr, uptr alias_addr, uptr alias_size) { -#if SANITIZER_LINUX +# if SANITIZER_LINUX return internal_mremap(reinterpret_cast<void *>(base_addr), 0, alias_size, MREMAP_MAYMOVE | MREMAP_FIXED, reinterpret_cast<void *>(alias_addr)); -#else +# else CHECK(false && "mremap is not supported outside of Linux"); return 0; -#endif +# endif } static void CreateAliases(uptr start_addr, uptr alias_size, uptr num_aliases) { @@ -1069,12 +1139,12 @@ uptr MapDynamicShadowAndAliases(uptr shadow_size, uptr alias_size, } void InitializePlatformCommonFlags(CommonFlags *cf) { -#if SANITIZER_ANDROID +# if SANITIZER_ANDROID if (&__libc_get_static_tls_bounds == nullptr) cf->detect_leaks = false; -#endif +# endif } -} // namespace __sanitizer +} // namespace __sanitizer #endif diff --git a/libsanitizer/sanitizer_common/sanitizer_linux_s390.cpp b/libsanitizer/sanitizer_common/sanitizer_linux_s390.cpp index 74db831..8523b54 100644 --- a/libsanitizer/sanitizer_common/sanitizer_linux_s390.cpp +++ b/libsanitizer/sanitizer_common/sanitizer_linux_s390.cpp @@ -15,14 +15,14 @@ #if SANITIZER_LINUX && SANITIZER_S390 -#include <dlfcn.h> -#include <errno.h> -#include <sys/syscall.h> -#include <sys/utsname.h> -#include <unistd.h> +# include <dlfcn.h> +# include <errno.h> +# include <sys/syscall.h> +# include <sys/utsname.h> +# include <unistd.h> -#include "sanitizer_libc.h" -#include "sanitizer_linux.h" +# include "sanitizer_libc.h" +# include "sanitizer_linux.h" namespace __sanitizer { @@ -37,22 +37,19 @@ uptr internal_mmap(void *addr, uptr length, int prot, int flags, int fd, unsigned long fd; unsigned long offset; } params = { - (unsigned long)addr, - (unsigned long)length, - (unsigned long)prot, - (unsigned long)flags, - (unsigned long)fd, -# ifdef __s390x__ - (unsigned long)offset, -# else + (unsigned long)addr, (unsigned long)length, (unsigned long)prot, + (unsigned long)flags, (unsigned long)fd, +# ifdef __s390x__ + (unsigned long)offset, +# else (unsigned long)(offset / 4096), -# endif +# endif }; -# ifdef __s390x__ +# ifdef __s390x__ return syscall(__NR_mmap, ¶ms); -# else +# else return syscall(__NR_mmap2, ¶ms); -# endif +# endif } uptr internal_clone(int (*fn)(void *), void *child_stack, int flags, void *arg, @@ -63,58 +60,54 @@ uptr internal_clone(int (*fn)(void *), void *child_stack, int flags, void *arg, } CHECK_EQ(0, (uptr)child_stack % 16); // Minimum frame size. -#ifdef __s390x__ +# ifdef __s390x__ child_stack = (char *)child_stack - 160; -#else +# else child_stack = (char *)child_stack - 96; -#endif +# endif // Terminate unwind chain. ((unsigned long *)child_stack)[0] = 0; // And pass parameters. ((unsigned long *)child_stack)[1] = (uptr)fn; ((unsigned long *)child_stack)[2] = (uptr)arg; register uptr res __asm__("r2"); - register void *__cstack __asm__("r2") = child_stack; - register long __flags __asm__("r3") = flags; - register int * __ptidptr __asm__("r4") = parent_tidptr; - register int * __ctidptr __asm__("r5") = child_tidptr; - register void * __newtls __asm__("r6") = newtls; + register void *__cstack __asm__("r2") = child_stack; + register long __flags __asm__("r3") = flags; + register int *__ptidptr __asm__("r4") = parent_tidptr; + register int *__ctidptr __asm__("r5") = child_tidptr; + register void *__newtls __asm__("r6") = newtls; __asm__ __volatile__( - /* Clone. */ - "svc %1\n" - - /* if (%r2 != 0) - * return; - */ -#ifdef __s390x__ - "cghi %%r2, 0\n" -#else - "chi %%r2, 0\n" -#endif - "jne 1f\n" - - /* Call "fn(arg)". */ -#ifdef __s390x__ - "lmg %%r1, %%r2, 8(%%r15)\n" -#else - "lm %%r1, %%r2, 4(%%r15)\n" -#endif - "basr %%r14, %%r1\n" - - /* Call _exit(%r2). */ - "svc %2\n" - - /* Return to parent. */ - "1:\n" - : "=r" (res) - : "i"(__NR_clone), "i"(__NR_exit), - "r"(__cstack), - "r"(__flags), - "r"(__ptidptr), - "r"(__ctidptr), - "r"(__newtls) - : "memory", "cc"); + /* Clone. */ + "svc %1\n" + + /* if (%r2 != 0) + * return; + */ +# ifdef __s390x__ + "cghi %%r2, 0\n" +# else + "chi %%r2, 0\n" +# endif + "jne 1f\n" + + /* Call "fn(arg)". */ +# ifdef __s390x__ + "lmg %%r1, %%r2, 8(%%r15)\n" +# else + "lm %%r1, %%r2, 4(%%r15)\n" +# endif + "basr %%r14, %%r1\n" + + /* Call _exit(%r2). */ + "svc %2\n" + + /* Return to parent. */ + "1:\n" + : "=r"(res) + : "i"(__NR_clone), "i"(__NR_exit), "r"(__cstack), "r"(__flags), + "r"(__ptidptr), "r"(__ctidptr), "r"(__newtls) + : "memory", "cc"); if (res >= (uptr)-4095) { errno = -res; return -1; @@ -122,7 +115,7 @@ uptr internal_clone(int (*fn)(void *), void *child_stack, int flags, void *arg, return res; } -#if SANITIZER_S390_64 +# if SANITIZER_S390_64 static bool FixedCVE_2016_2143() { // Try to determine if the running kernel has a fix for CVE-2016-2143, // return false if in doubt (better safe than sorry). Distros may want to @@ -137,20 +130,20 @@ static bool FixedCVE_2016_2143() { // At least first 2 should be matched. if (ptr[0] != '.') return false; - minor = internal_simple_strtoll(ptr+1, &ptr, 10); + minor = internal_simple_strtoll(ptr + 1, &ptr, 10); // Third is optional. if (ptr[0] == '.') - patch = internal_simple_strtoll(ptr+1, &ptr, 10); + patch = internal_simple_strtoll(ptr + 1, &ptr, 10); if (major < 3) { if (major == 2 && minor == 6 && patch == 32 && ptr[0] == '-' && internal_strstr(ptr, ".el6")) { // Check RHEL6 - int r1 = internal_simple_strtoll(ptr+1, &ptr, 10); - if (r1 >= 657) // 2.6.32-657.el6 or later + int r1 = internal_simple_strtoll(ptr + 1, &ptr, 10); + if (r1 >= 657) // 2.6.32-657.el6 or later return true; if (r1 == 642 && ptr[0] == '.') { - int r2 = internal_simple_strtoll(ptr+1, &ptr, 10); - if (r2 >= 9) // 2.6.32-642.9.1.el6 or later + int r2 = internal_simple_strtoll(ptr + 1, &ptr, 10); + if (r2 >= 9) // 2.6.32-642.9.1.el6 or later return true; } } @@ -166,12 +159,12 @@ static bool FixedCVE_2016_2143() { if (minor == 10 && patch == 0 && ptr[0] == '-' && internal_strstr(ptr, ".el7")) { // Check RHEL7 - int r1 = internal_simple_strtoll(ptr+1, &ptr, 10); - if (r1 >= 426) // 3.10.0-426.el7 or later + int r1 = internal_simple_strtoll(ptr + 1, &ptr, 10); + if (r1 >= 426) // 3.10.0-426.el7 or later return true; if (r1 == 327 && ptr[0] == '.') { - int r2 = internal_simple_strtoll(ptr+1, &ptr, 10); - if (r2 >= 27) // 3.10.0-327.27.1.el7 or later + int r2 = internal_simple_strtoll(ptr + 1, &ptr, 10); + if (r2 >= 27) // 3.10.0-327.27.1.el7 or later return true; } } @@ -187,8 +180,8 @@ static bool FixedCVE_2016_2143() { if (minor == 4 && patch == 0 && ptr[0] == '-' && internal_strstr(buf.version, "Ubuntu")) { // Check Ubuntu 16.04 - int r1 = internal_simple_strtoll(ptr+1, &ptr, 10); - if (r1 >= 13) // 4.4.0-13 or later + int r1 = internal_simple_strtoll(ptr + 1, &ptr, 10); + if (r1 >= 13) // 4.4.0-13 or later return true; } // Otherwise, OK if 4.5+. @@ -211,18 +204,19 @@ void AvoidCVE_2016_2143() { if (GetEnv("SANITIZER_IGNORE_CVE_2016_2143")) return; Report( - "ERROR: Your kernel seems to be vulnerable to CVE-2016-2143. Using ASan,\n" - "MSan, TSan, DFSan or LSan with such kernel can and will crash your\n" - "machine, or worse.\n" - "\n" - "If you are certain your kernel is not vulnerable (you have compiled it\n" - "yourself, or are using an unrecognized distribution kernel), you can\n" - "override this safety check by exporting SANITIZER_IGNORE_CVE_2016_2143\n" - "with any value.\n"); + "ERROR: Your kernel seems to be vulnerable to CVE-2016-2143. Using " + "ASan,\n" + "MSan, TSan, DFSan or LSan with such kernel can and will crash your\n" + "machine, or worse.\n" + "\n" + "If you are certain your kernel is not vulnerable (you have compiled it\n" + "yourself, or are using an unrecognized distribution kernel), you can\n" + "override this safety check by exporting SANITIZER_IGNORE_CVE_2016_2143\n" + "with any value.\n"); Die(); } -#endif +# endif -} // namespace __sanitizer +} // namespace __sanitizer -#endif // SANITIZER_LINUX && SANITIZER_S390 +#endif // SANITIZER_LINUX && SANITIZER_S390 diff --git a/libsanitizer/sanitizer_common/sanitizer_mac.cpp b/libsanitizer/sanitizer_common/sanitizer_mac.cpp index e1f83e4..26d2e8d 100644 --- a/libsanitizer/sanitizer_common/sanitizer_mac.cpp +++ b/libsanitizer/sanitizer_common/sanitizer_mac.cpp @@ -38,7 +38,7 @@ extern char **environ; # endif -# if defined(__has_include) && __has_include(<os/trace.h>) && defined(__BLOCKS__) +# if defined(__has_include) && __has_include(<os/trace.h>) # define SANITIZER_OS_TRACE 1 # include <os/trace.h> # else @@ -71,15 +71,7 @@ extern char ***_NSGetArgv(void); # include <mach/mach_time.h> # include <mach/vm_statistics.h> # include <malloc/malloc.h> -# if defined(__has_builtin) && __has_builtin(__builtin_os_log_format) -# include <os/log.h> -# else - /* Without support for __builtin_os_log_format, fall back to the older - method. */ -# define OS_LOG_DEFAULT 0 -# define os_log_error(A,B,C) \ - asl_log(nullptr, nullptr, ASL_LEVEL_ERR, "%s", (C)); -# endif +# include <os/log.h> # include <pthread.h> # include <pthread/introspection.h> # include <sched.h> @@ -553,9 +545,6 @@ uptr GetTlsSize() { return 0; } -void InitTlsSize() { -} - uptr TlsBaseAddr() { uptr segbase = 0; #if defined(__x86_64__) @@ -580,21 +569,18 @@ uptr TlsSize() { #endif } -void GetThreadStackAndTls(bool main, uptr *stk_addr, uptr *stk_size, - uptr *tls_addr, uptr *tls_size) { -#if !SANITIZER_GO - uptr stack_top, stack_bottom; - GetThreadStackTopAndBottom(main, &stack_top, &stack_bottom); - *stk_addr = stack_bottom; - *stk_size = stack_top - stack_bottom; - *tls_addr = TlsBaseAddr(); - *tls_size = TlsSize(); -#else - *stk_addr = 0; - *stk_size = 0; - *tls_addr = 0; - *tls_size = 0; -#endif +void GetThreadStackAndTls(bool main, uptr *stk_begin, uptr *stk_end, + uptr *tls_begin, uptr *tls_end) { +# if !SANITIZER_GO + GetThreadStackTopAndBottom(main, stk_end, stk_begin); + *tls_begin = TlsBaseAddr(); + *tls_end = *tls_begin + TlsSize(); +# else + *stk_begin = 0; + *stk_end = 0; + *tls_begin = 0; + *tls_end = 0; +# endif } void ListOfModules::init() { @@ -796,7 +782,11 @@ void WriteOneLineToSyslog(const char *s) { if (GetMacosAlignedVersion() >= MacosVersion(10, 12)) { os_log_error(OS_LOG_DEFAULT, "%{public}s", s); } else { +#pragma clang diagnostic push +// as_log is deprecated. +#pragma clang diagnostic ignored "-Wdeprecated-declarations" asl_log(nullptr, nullptr, ASL_LEVEL_ERR, "%s", s); +#pragma clang diagnostic pop } #endif } @@ -851,6 +841,9 @@ void LogFullErrorReport(const char *buffer) { #if !SANITIZER_GO // Log with os_trace. This will make it into the crash log. #if SANITIZER_OS_TRACE +#pragma clang diagnostic push +// os_trace is deprecated. +#pragma clang diagnostic ignored "-Wdeprecated-declarations" if (GetMacosAlignedVersion() >= MacosVersion(10, 10)) { // os_trace requires the message (format parameter) to be a string literal. if (internal_strncmp(SanitizerToolName, "AddressSanitizer", @@ -868,6 +861,7 @@ void LogFullErrorReport(const char *buffer) { if (common_flags()->log_to_syslog) os_trace("Consult syslog for more information."); } +#pragma clang diagnostic pop #endif // Log to syslog. @@ -1196,8 +1190,8 @@ uptr GetMaxVirtualAddress() { } uptr MapDynamicShadow(uptr shadow_size_bytes, uptr shadow_scale, - uptr min_shadow_base_alignment, uptr &high_mem_end) { - const uptr granularity = GetMmapGranularity(); + uptr min_shadow_base_alignment, uptr &high_mem_end, + uptr granularity) { const uptr alignment = Max<uptr>(granularity << shadow_scale, 1ULL << min_shadow_base_alignment); const uptr left_padding = @@ -1380,8 +1374,8 @@ void DumpProcessMap() { for (uptr i = 0; i < modules.size(); ++i) { char uuid_str[128]; FormatUUID(uuid_str, sizeof(uuid_str), modules[i].uuid()); - Printf("0x%zx-0x%zx %s (%s) %s\n", modules[i].base_address(), - modules[i].max_address(), modules[i].full_name(), + Printf("%p-%p %s (%s) %s\n", (void *)modules[i].base_address(), + (void *)modules[i].max_address(), modules[i].full_name(), ModuleArchToString(modules[i].arch()), uuid_str); } Printf("End of module map.\n"); diff --git a/libsanitizer/sanitizer_common/sanitizer_mac.h b/libsanitizer/sanitizer_common/sanitizer_mac.h index 1cf2e29..f0a97d0 100644 --- a/libsanitizer/sanitizer_common/sanitizer_mac.h +++ b/libsanitizer/sanitizer_common/sanitizer_mac.h @@ -14,26 +14,6 @@ #include "sanitizer_common.h" #include "sanitizer_platform.h" - -/* TARGET_OS_OSX is not present in SDKs before Darwin16 (macOS 10.12) use - TARGET_OS_MAC (we have no support for iOS in any form for these versions, - so there's no ambiguity). */ -#if !defined(TARGET_OS_OSX) && TARGET_OS_MAC -# define TARGET_OS_OSX 1 -#endif - -/* Other TARGET_OS_xxx are not present on earlier versions, define them to - 0 (we have no support for them; they are not valid targets anyway). */ -#ifndef TARGET_OS_IOS -#define TARGET_OS_IOS 0 -#endif -#ifndef TARGET_OS_TV -#define TARGET_OS_TV 0 -#endif -#ifndef TARGET_OS_WATCH -#define TARGET_OS_WATCH 0 -#endif - #if SANITIZER_APPLE #include "sanitizer_posix.h" diff --git a/libsanitizer/sanitizer_common/sanitizer_mallinfo.h b/libsanitizer/sanitizer_common/sanitizer_mallinfo.h index 4e58c02..1c07e68 100644 --- a/libsanitizer/sanitizer_common/sanitizer_mallinfo.h +++ b/libsanitizer/sanitizer_common/sanitizer_mallinfo.h @@ -31,6 +31,10 @@ struct __sanitizer_struct_mallinfo { int v[10]; }; +struct __sanitizer_struct_mallinfo2 { + uptr v[10]; +}; + #endif } // namespace __sanitizer diff --git a/libsanitizer/sanitizer_common/sanitizer_mutex.cpp b/libsanitizer/sanitizer_common/sanitizer_mutex.cpp index 40fe566..97085e8 100644 --- a/libsanitizer/sanitizer_common/sanitizer_mutex.cpp +++ b/libsanitizer/sanitizer_common/sanitizer_mutex.cpp @@ -212,8 +212,10 @@ struct InternalDeadlockDetector { return initialized > 0; } }; - -static THREADLOCAL InternalDeadlockDetector deadlock_detector; +// This variable is used by the __tls_get_addr interceptor, so cannot use the +// global-dynamic TLS model, as that would result in crashes. +__attribute__((tls_model("initial-exec"))) static THREADLOCAL + InternalDeadlockDetector deadlock_detector; void CheckedMutex::LockImpl(uptr pc) { deadlock_detector.Lock(type_, pc); } diff --git a/libsanitizer/sanitizer_common/sanitizer_placement_new.h b/libsanitizer/sanitizer_common/sanitizer_placement_new.h index 1ceb8b9..c9b917b 100644 --- a/libsanitizer/sanitizer_common/sanitizer_placement_new.h +++ b/libsanitizer/sanitizer_common/sanitizer_placement_new.h @@ -17,8 +17,6 @@ #include "sanitizer_internal_defs.h" -inline void *operator new(__sanitizer::operator_new_size_type sz, void *p) { - return p; -} +inline void *operator new(__sanitizer::usize sz, void *p) { return p; } #endif // SANITIZER_PLACEMENT_NEW_H diff --git a/libsanitizer/sanitizer_common/sanitizer_platform.h b/libsanitizer/sanitizer_common/sanitizer_platform.h index 3e1b078..5796640 100644 --- a/libsanitizer/sanitizer_common/sanitizer_platform.h +++ b/libsanitizer/sanitizer_common/sanitizer_platform.h @@ -260,6 +260,17 @@ # define SANITIZER_ARM64 0 #endif +#if SANITIZER_WINDOWS64 && SANITIZER_ARM64 +# define SANITIZER_WINDOWS_ARM64 1 +# define SANITIZER_WINDOWS_x64 0 +#elif SANITIZER_WINDOWS64 && !SANITIZER_ARM64 +# define SANITIZER_WINDOWS_ARM64 0 +# define SANITIZER_WINDOWS_x64 1 +#else +# define SANITIZER_WINDOWS_ARM64 0 +# define SANITIZER_WINDOWS_x64 0 +#endif + #if SANITIZER_SOLARIS && SANITIZER_WORDSIZE == 32 # define SANITIZER_SOLARIS32 1 #else @@ -284,8 +295,8 @@ // For such platforms build this code with -DSANITIZER_CAN_USE_ALLOCATOR64=0 or // change the definition of SANITIZER_CAN_USE_ALLOCATOR64 here. #ifndef SANITIZER_CAN_USE_ALLOCATOR64 -# if (SANITIZER_RISCV64 && !SANITIZER_FUCHSIA) || SANITIZER_IOS || \ - SANITIZER_DRIVERKIT +# if (SANITIZER_RISCV64 && !SANITIZER_FUCHSIA && !SANITIZER_LINUX) || \ + SANITIZER_IOS || SANITIZER_DRIVERKIT # define SANITIZER_CAN_USE_ALLOCATOR64 0 # elif defined(__mips64) || defined(__hexagon__) # define SANITIZER_CAN_USE_ALLOCATOR64 0 @@ -311,7 +322,7 @@ # if SANITIZER_FUCHSIA # define SANITIZER_MMAP_RANGE_SIZE (1ULL << 38) # else -# define SANITIZER_MMAP_RANGE_SIZE FIRST_32_SECOND_64(1ULL << 32, 1ULL << 47) +# define SANITIZER_MMAP_RANGE_SIZE FIRST_32_SECOND_64(1ULL << 32, 1ULL << 56) # endif #elif defined(__aarch64__) # if SANITIZER_APPLE diff --git a/libsanitizer/sanitizer_common/sanitizer_platform_interceptors.h b/libsanitizer/sanitizer_common/sanitizer_platform_interceptors.h index 8c7c00d..3fd6b59 100644 --- a/libsanitizer/sanitizer_common/sanitizer_platform_interceptors.h +++ b/libsanitizer/sanitizer_common/sanitizer_platform_interceptors.h @@ -84,6 +84,25 @@ #define SI_NOT_MAC 1 #endif +#if SANITIZER_APPLE +# include <Availability.h> + +// aligned_alloc was introduced in OSX 10.15 +// Linking will fail when using an older SDK +# if defined(__MAC_10_15) +// macOS 10.15 is greater than our minimal deployment target. To ensure we +// generate a weak reference so the dylib continues to work on older +// systems, we need to forward declare the intercepted function as "weak +// imports". +SANITIZER_WEAK_IMPORT void *aligned_alloc(__sanitizer::usize __alignment, + __sanitizer::usize __size); +# define SI_MAC_SDK_10_15_AVAILABLE 1 +# else +# define SI_MAC_SDK_10_15_AVAILABLE 0 +# endif // defined(__MAC_10_15) + +#endif // SANITIZER_APPLE + #if SANITIZER_IOS #define SI_IOS 1 #else @@ -183,6 +202,11 @@ #define SANITIZER_INTERCEPT_FPUTS SI_POSIX #define SANITIZER_INTERCEPT_PUTS SI_POSIX +#define SANITIZER_INTERCEPT_CREAT64 (SI_GLIBC || SI_SOLARIS32) +#define SANITIZER_INTERCEPT_FCNTL64 (SI_GLIBC || SI_SOLARIS32) +#define SANITIZER_INTERCEPT_OPEN64 (SI_GLIBC || SI_SOLARIS32) +#define SANITIZER_INTERCEPT_OPENAT64 (SI_GLIBC || SI_SOLARIS32) + #define SANITIZER_INTERCEPT_PREAD64 (SI_GLIBC || SI_SOLARIS32) #define SANITIZER_INTERCEPT_PWRITE64 (SI_GLIBC || SI_SOLARIS32) @@ -191,7 +215,8 @@ #define SANITIZER_INTERCEPT_PREADV \ (SI_FREEBSD || SI_NETBSD || SI_LINUX_NOT_ANDROID) -#define SANITIZER_INTERCEPT_PWRITEV SI_LINUX_NOT_ANDROID +#define SANITIZER_INTERCEPT_PWRITEV \ + (SI_FREEBSD || SI_NETBSD || SI_LINUX_NOT_ANDROID) #define SANITIZER_INTERCEPT_PREADV64 SI_GLIBC #define SANITIZER_INTERCEPT_PWRITEV64 SI_GLIBC @@ -273,8 +298,9 @@ #if SI_LINUX_NOT_ANDROID && \ (defined(__i386) || defined(__x86_64) || defined(__mips64) || \ defined(__powerpc64__) || defined(__aarch64__) || defined(__arm__) || \ - defined(__s390__) || defined(__loongarch__) || SANITIZER_RISCV64) -#define SANITIZER_INTERCEPT_PTRACE 1 + defined(__s390__) || defined(__loongarch__) || SANITIZER_RISCV64 || \ + defined(__sparc__)) +# define SANITIZER_INTERCEPT_PTRACE 1 #else #define SANITIZER_INTERCEPT_PTRACE 0 #endif @@ -301,7 +327,8 @@ #define SANITIZER_INTERCEPT_CANONICALIZE_FILE_NAME (SI_GLIBC || SI_SOLARIS) #define SANITIZER_INTERCEPT_CONFSTR \ (SI_FREEBSD || SI_NETBSD || SI_MAC || SI_LINUX_NOT_ANDROID || SI_SOLARIS) -#define SANITIZER_INTERCEPT_SCHED_GETAFFINITY SI_LINUX_NOT_ANDROID +#define SANITIZER_INTERCEPT_SCHED_GETAFFINITY \ + (SI_LINUX_NOT_ANDROID || SI_FREEBSD) #define SANITIZER_INTERCEPT_SCHED_GETPARAM SI_LINUX_NOT_ANDROID || SI_SOLARIS #define SANITIZER_INTERCEPT_STRERROR SI_POSIX #define SANITIZER_INTERCEPT_STRERROR_R SI_POSIX @@ -462,7 +489,7 @@ (SI_LINUX || SI_MAC || SI_WINDOWS || SI_FREEBSD || SI_NETBSD || SI_SOLARIS) #define SANITIZER_INTERCEPT_RECV_RECVFROM SI_POSIX #define SANITIZER_INTERCEPT_SEND_SENDTO SI_POSIX -#define SANITIZER_INTERCEPT_EVENTFD_READ_WRITE SI_LINUX +#define SANITIZER_INTERCEPT_EVENTFD_READ_WRITE (SI_LINUX || SI_FREEBSD) #define SI_STAT_LINUX (SI_LINUX && __GLIBC_PREREQ(2, 33)) #define SANITIZER_INTERCEPT_STAT \ @@ -492,7 +519,8 @@ #define SANITIZER_INTERCEPT_PVALLOC (SI_GLIBC || SI_ANDROID) #define SANITIZER_INTERCEPT_CFREE (SI_GLIBC && !SANITIZER_RISCV64) #define SANITIZER_INTERCEPT_REALLOCARRAY SI_POSIX -#define SANITIZER_INTERCEPT_ALIGNED_ALLOC (!SI_MAC) +#define SANITIZER_INTERCEPT_ALIGNED_ALLOC \ + (!SI_MAC || SI_MAC_SDK_10_15_AVAILABLE) #define SANITIZER_INTERCEPT_MALLOC_USABLE_SIZE (!SI_MAC && !SI_NETBSD) #define SANITIZER_INTERCEPT_MCHECK_MPROBE SI_LINUX_NOT_ANDROID #define SANITIZER_INTERCEPT_WCSLEN 1 @@ -557,10 +585,8 @@ #define SANITIZER_INTERCEPT_SHA1 SI_NETBSD #define SANITIZER_INTERCEPT_MD4 SI_NETBSD #define SANITIZER_INTERCEPT_RMD160 SI_NETBSD -#define SANITIZER_INTERCEPT_MD5 (SI_NETBSD || SI_FREEBSD) #define SANITIZER_INTERCEPT_FSEEK (SI_NETBSD || SI_FREEBSD) #define SANITIZER_INTERCEPT_MD2 SI_NETBSD -#define SANITIZER_INTERCEPT_SHA2 (SI_NETBSD || SI_FREEBSD) #define SANITIZER_INTERCEPT_CDB SI_NETBSD #define SANITIZER_INTERCEPT_VIS (SI_NETBSD || SI_FREEBSD) #define SANITIZER_INTERCEPT_POPEN SI_POSIX @@ -594,10 +620,18 @@ #define SANITIZER_INTERCEPT___XUNAME SI_FREEBSD #define SANITIZER_INTERCEPT_FLOPEN SI_FREEBSD #define SANITIZER_INTERCEPT_PROCCTL SI_FREEBSD -#define SANITIZER_INTERCEPT_HEXDUMP SI_FREEBSD #define SANITIZER_INTERCEPT_ARGP_PARSE SI_GLIBC #define SANITIZER_INTERCEPT_CPUSET_GETAFFINITY SI_FREEBSD - +// FIXME: also available from musl 1.2.5 +#define SANITIZER_INTERCEPT_PREADV2 (SI_LINUX && __GLIBC_PREREQ(2, 26)) +#define SANITIZER_INTERCEPT_PWRITEV2 (SI_LINUX && __GLIBC_PREREQ(2, 26)) +#if defined(__MAC_OS_X_VERSION_MIN_REQUIRED) && \ + __MAC_OS_X_VERSION_MIN_REQUIRED >= 130000 +# define SI_MAC_OS_DEPLOYMENT_MIN_13_00 1 +#else +# define SI_MAC_OS_DEPLOYMENT_MIN_13_00 0 +#endif +#define SANITIZER_INTERCEPT_FREADLINK (SI_MAC && SI_MAC_OS_DEPLOYMENT_MIN_13_00) // This macro gives a way for downstream users to override the above // interceptor macros irrespective of the platform they are on. They have // to do two things: diff --git a/libsanitizer/sanitizer_common/sanitizer_platform_limits_freebsd.cpp b/libsanitizer/sanitizer_common/sanitizer_platform_limits_freebsd.cpp index 38f968d..4940062 100644 --- a/libsanitizer/sanitizer_common/sanitizer_platform_limits_freebsd.cpp +++ b/libsanitizer/sanitizer_common/sanitizer_platform_limits_freebsd.cpp @@ -475,6 +475,8 @@ CHECK_TYPE_SIZE(nfds_t); CHECK_TYPE_SIZE(sigset_t); COMPILER_CHECK(sizeof(__sanitizer_sigaction) == sizeof(struct sigaction)); +COMPILER_CHECK(sizeof(__sanitizer_siginfo) == sizeof(siginfo_t)); +CHECK_SIZE_AND_OFFSET(siginfo_t, si_value); // Can't write checks for sa_handler and sa_sigaction due to them being // preprocessor macros. CHECK_STRUCT_SIZE_AND_OFFSET(sigaction, sa_mask); diff --git a/libsanitizer/sanitizer_common/sanitizer_platform_limits_freebsd.h b/libsanitizer/sanitizer_common/sanitizer_platform_limits_freebsd.h index b119f05..8ce73f2 100644 --- a/libsanitizer/sanitizer_common/sanitizer_platform_limits_freebsd.h +++ b/libsanitizer/sanitizer_common/sanitizer_platform_limits_freebsd.h @@ -301,11 +301,29 @@ struct __sanitizer_sigset_t { typedef __sanitizer_sigset_t __sanitizer_kernel_sigset_t; +union __sanitizer_sigval { + int sival_int; + void *sival_ptr; +}; + struct __sanitizer_siginfo { - // The size is determined by looking at sizeof of real siginfo_t on linux. - u64 opaque[128 / sizeof(u64)]; + int si_signo; + int si_errno; + int si_code; + pid_t si_pid; + u32 si_uid; + int si_status; + void *si_addr; + union __sanitizer_sigval si_value; +# if SANITIZER_WORDSIZE == 64 + char data[40]; +# else + char data[32]; +# endif }; +typedef __sanitizer_siginfo __sanitizer_siginfo_t; + using __sanitizer_sighandler_ptr = void (*)(int sig); using __sanitizer_sigactionhandler_ptr = void (*)(int sig, __sanitizer_siginfo *siginfo, @@ -726,6 +744,8 @@ struct __sanitizer_cpuset { typedef struct __sanitizer_cpuset __sanitizer_cpuset_t; extern unsigned struct_cpuset_sz; + +typedef unsigned long long __sanitizer_eventfd_t; } // namespace __sanitizer # define CHECK_TYPE_SIZE(TYPE) \ diff --git a/libsanitizer/sanitizer_common/sanitizer_platform_limits_linux.cpp b/libsanitizer/sanitizer_common/sanitizer_platform_limits_linux.cpp index c278c87..bf0f355 100644 --- a/libsanitizer/sanitizer_common/sanitizer_platform_limits_linux.cpp +++ b/libsanitizer/sanitizer_common/sanitizer_platform_limits_linux.cpp @@ -26,10 +26,7 @@ // With old kernels (and even new kernels on powerpc) asm/stat.h uses types that // are not defined anywhere in userspace headers. Fake them. This seems to work -// fine with newer headers, too. Beware that with <sys/stat.h>, struct stat -// takes the form of struct stat64 on 32-bit platforms if _FILE_OFFSET_BITS=64. -// Also, for some platforms (e.g. mips) there are additional members in the -// <sys/stat.h> struct stat:s. +// fine with newer headers, too. #include <linux/posix_types.h> # if defined(__x86_64__) || defined(__mips__) || defined(__hexagon__) # include <sys/stat.h> diff --git a/libsanitizer/sanitizer_common/sanitizer_platform_limits_openbsd.cpp b/libsanitizer/sanitizer_common/sanitizer_platform_limits_openbsd.cpp deleted file mode 100644 index e69de29..0000000 --- a/libsanitizer/sanitizer_common/sanitizer_platform_limits_openbsd.cpp +++ /dev/null diff --git a/libsanitizer/sanitizer_common/sanitizer_platform_limits_openbsd.h b/libsanitizer/sanitizer_common/sanitizer_platform_limits_openbsd.h deleted file mode 100644 index e69de29..0000000 --- a/libsanitizer/sanitizer_common/sanitizer_platform_limits_openbsd.h +++ /dev/null diff --git a/libsanitizer/sanitizer_common/sanitizer_platform_limits_posix.cpp b/libsanitizer/sanitizer_common/sanitizer_platform_limits_posix.cpp index 6d61d27..c87d5ef 100644 --- a/libsanitizer/sanitizer_common/sanitizer_platform_limits_posix.cpp +++ b/libsanitizer/sanitizer_common/sanitizer_platform_limits_posix.cpp @@ -94,8 +94,9 @@ #if SANITIZER_LINUX # include <utime.h> # include <sys/ptrace.h> -# if defined(__mips64) || defined(__aarch64__) || defined(__arm__) || \ - defined(__hexagon__) || defined(__loongarch__) ||SANITIZER_RISCV64 +# if defined(__mips64) || defined(__aarch64__) || defined(__arm__) || \ + defined(__hexagon__) || defined(__loongarch__) || SANITIZER_RISCV64 || \ + defined(__sparc__) # include <asm/ptrace.h> # ifdef __arm__ typedef struct user_fpregs elf_fpregset_t; @@ -117,15 +118,16 @@ typedef struct user_fpregs elf_fpregset_t; #if SANITIZER_LINUX #if SANITIZER_GLIBC #include <fstab.h> -#include <net/if_ppp.h> -#include <netax25/ax25.h> -#include <netipx/ipx.h> -#include <netrom/netrom.h> -#include <obstack.h> -#if HAVE_RPC_XDR_H -# include <rpc/xdr.h> -#endif -#include <scsi/scsi.h> +# include <linux/filter.h> +# include <net/if_ppp.h> +# include <netax25/ax25.h> +# include <netipx/ipx.h> +# include <netrom/netrom.h> +# include <obstack.h> +# if HAVE_RPC_XDR_H +# include <rpc/xdr.h> +# endif +# include <scsi/scsi.h> #else #include <linux/if_ppp.h> #include <linux/kd.h> @@ -358,11 +360,12 @@ unsigned struct_ElfW_Phdr_sz = sizeof(Elf_Phdr); const int wordexp_wrde_dooffs = WRDE_DOOFFS; # endif // !SANITIZER_ANDROID -#if SANITIZER_LINUX && !SANITIZER_ANDROID && \ - (defined(__i386) || defined(__x86_64) || defined(__mips64) || \ - defined(__powerpc64__) || defined(__aarch64__) || defined(__arm__) || \ - defined(__s390__) || defined(__loongarch__)|| SANITIZER_RISCV64) -#if defined(__mips64) || defined(__powerpc64__) || defined(__arm__) +# if SANITIZER_LINUX && !SANITIZER_ANDROID && \ + (defined(__i386) || defined(__x86_64) || defined(__mips64) || \ + defined(__powerpc64__) || defined(__aarch64__) || defined(__arm__) || \ + defined(__s390__) || defined(__loongarch__) || SANITIZER_RISCV64 || \ + defined(__sparc__)) +# if defined(__mips64) || defined(__powerpc64__) || defined(__arm__) unsigned struct_user_regs_struct_sz = sizeof(struct pt_regs); unsigned struct_user_fpregs_struct_sz = sizeof(elf_fpregset_t); #elif SANITIZER_RISCV64 @@ -377,19 +380,22 @@ unsigned struct_ElfW_Phdr_sz = sizeof(Elf_Phdr); #elif defined(__s390__) unsigned struct_user_regs_struct_sz = sizeof(struct _user_regs_struct); unsigned struct_user_fpregs_struct_sz = sizeof(struct _user_fpregs_struct); -#else +# elif defined(__sparc__) + unsigned struct_user_regs_struct_sz = sizeof(struct sunos_regs); + unsigned struct_user_fpregs_struct_sz = sizeof(struct sunos_fp); +# else unsigned struct_user_regs_struct_sz = sizeof(struct user_regs_struct); unsigned struct_user_fpregs_struct_sz = sizeof(struct user_fpregs_struct); -#endif // __mips64 || __powerpc64__ || __aarch64__ || __loongarch__ -#if defined(__x86_64) || defined(__mips64) || defined(__powerpc64__) || \ - defined(__aarch64__) || defined(__arm__) || defined(__s390__) || \ - defined(__loongarch__) || SANITIZER_RISCV64 +# endif // __mips64 || __powerpc64__ || __aarch64__ || __loongarch__ +# if defined(__x86_64) || defined(__mips64) || defined(__powerpc64__) || \ + defined(__aarch64__) || defined(__arm__) || defined(__s390__) || \ + defined(__loongarch__) || SANITIZER_RISCV64 || defined(__sparc__) unsigned struct_user_fpxregs_struct_sz = 0; #else unsigned struct_user_fpxregs_struct_sz = sizeof(struct user_fpxregs_struct); #endif // __x86_64 || __mips64 || __powerpc64__ || __aarch64__ || __arm__ -// || __s390__ || __loongarch__ -#ifdef __arm__ + // || __s390__ || __loongarch__ || SANITIZER_RISCV64 || __sparc__ +# ifdef __arm__ unsigned struct_user_vfpregs_struct_sz = ARM_VFPREGS_SIZE; #else unsigned struct_user_vfpregs_struct_sz = 0; @@ -531,9 +537,10 @@ unsigned struct_ElfW_Phdr_sz = sizeof(Elf_Phdr); unsigned struct_audio_buf_info_sz = sizeof(struct audio_buf_info); unsigned struct_ppp_stats_sz = sizeof(struct ppp_stats); -#endif // SANITIZER_GLIBC + unsigned struct_sock_fprog_sz = sizeof(struct sock_fprog); +# endif // SANITIZER_GLIBC -#if !SANITIZER_ANDROID && !SANITIZER_APPLE +# if !SANITIZER_ANDROID && !SANITIZER_APPLE unsigned struct_sioc_sg_req_sz = sizeof(struct sioc_sg_req); unsigned struct_sioc_vif_req_sz = sizeof(struct sioc_vif_req); #endif diff --git a/libsanitizer/sanitizer_common/sanitizer_platform_limits_posix.h b/libsanitizer/sanitizer_common/sanitizer_platform_limits_posix.h index 58244c9..e8c81aa 100644 --- a/libsanitizer/sanitizer_common/sanitizer_platform_limits_posix.h +++ b/libsanitizer/sanitizer_common/sanitizer_platform_limits_posix.h @@ -523,6 +523,7 @@ typedef long __sanitizer_clock_t; #if SANITIZER_LINUX typedef int __sanitizer_clockid_t; +typedef unsigned long long __sanitizer_eventfd_t; #endif #if SANITIZER_LINUX @@ -854,10 +855,11 @@ typedef void __sanitizer_FILE; # define SANITIZER_HAS_STRUCT_FILE 0 #endif -#if SANITIZER_LINUX && !SANITIZER_ANDROID && \ - (defined(__i386) || defined(__x86_64) || defined(__mips64) || \ - defined(__powerpc64__) || defined(__aarch64__) || defined(__arm__) || \ - defined(__s390__) || defined(__loongarch__) || SANITIZER_RISCV64) +# if SANITIZER_LINUX && !SANITIZER_ANDROID && \ + (defined(__i386) || defined(__x86_64) || defined(__mips64) || \ + defined(__powerpc64__) || defined(__aarch64__) || defined(__arm__) || \ + defined(__s390__) || defined(__loongarch__) || SANITIZER_RISCV64 || \ + defined(__sparc__)) extern unsigned struct_user_regs_struct_sz; extern unsigned struct_user_fpregs_struct_sz; extern unsigned struct_user_fpxregs_struct_sz; @@ -879,9 +881,24 @@ extern int ptrace_setsiginfo; extern int ptrace_getregset; extern int ptrace_setregset; extern int ptrace_geteventmsg; -#endif -#if SANITIZER_LINUX && !SANITIZER_ANDROID +// Helper for the ptrace interceptor. +template <class T> +inline T ptrace_data_arg(int request, T addr, T data) { +# if SANITIZER_LINUX && SANITIZER_SPARC + // As described in ptrace(2), the meanings of addr and data are reversed + // for the PTRACE_GETREGS, PTRACE_GETFPREGS, PTRACE_GETREGS, and + // PTRACE_GETFPREGS requests on Linux/sparc64. + if (request == ptrace_getregs || request == ptrace_getfpregs || + request == ptrace_setregs || request == ptrace_setfpregs) + return addr; + else +# endif + return data; +} +# endif + +# if SANITIZER_LINUX && !SANITIZER_ANDROID extern unsigned struct_shminfo_sz; extern unsigned struct_shm_info_sz; extern int shmctl_ipc_stat; @@ -1049,7 +1066,8 @@ extern unsigned struct_serial_struct_sz; extern unsigned struct_sockaddr_ax25_sz; extern unsigned struct_unimapdesc_sz; extern unsigned struct_unimapinit_sz; -#endif // SANITIZER_LINUX && !SANITIZER_ANDROID +extern unsigned struct_sock_fprog_sz; +# endif // SANITIZER_LINUX && !SANITIZER_ANDROID extern const unsigned long __sanitizer_bufsiz; diff --git a/libsanitizer/sanitizer_common/sanitizer_posix.cpp b/libsanitizer/sanitizer_common/sanitizer_posix.cpp index 8d2c5b2..69af646 100644 --- a/libsanitizer/sanitizer_common/sanitizer_posix.cpp +++ b/libsanitizer/sanitizer_common/sanitizer_posix.cpp @@ -54,12 +54,12 @@ void *MmapOrDie(uptr size, const char *mem_type, bool raw_report) { return (void *)res; } -void UnmapOrDie(void *addr, uptr size) { +void UnmapOrDie(void *addr, uptr size, bool raw_report) { if (!addr || !size) return; uptr res = internal_munmap(addr, size); int reserrno; if (UNLIKELY(internal_iserror(res, &reserrno))) - ReportMunmapFailureAndDie(addr, size, reserrno); + ReportMunmapFailureAndDie(addr, size, reserrno, raw_report); DecreaseTotalMmap(size); } @@ -85,8 +85,8 @@ void *MmapAlignedOrDieOnFatalError(uptr size, uptr alignment, CHECK(IsPowerOfTwo(size)); CHECK(IsPowerOfTwo(alignment)); uptr map_size = size + alignment; - // mmap maps entire pages and rounds up map_size needs to be a an integral - // number of pages. + // mmap maps entire pages and rounds up map_size needs to be a an integral + // number of pages. // We need to be aware of this size for calculating end and for unmapping // fragments before and after the alignment region. map_size = RoundUpTo(map_size, GetPageSizeCached()); @@ -130,8 +130,8 @@ static void *MmapFixedImpl(uptr fixed_addr, uptr size, bool tolerate_enomem, if (tolerate_enomem && reserrno == ENOMEM) return nullptr; char mem_type[40]; - internal_snprintf(mem_type, sizeof(mem_type), "memory at address 0x%zx", - fixed_addr); + internal_snprintf(mem_type, sizeof(mem_type), "memory at address %p", + (void *)fixed_addr); ReportMmapFailureAndDie(size, mem_type, "allocate", reserrno); } IncreaseTotalMmap(size); @@ -353,7 +353,15 @@ bool ShouldMockFailureToOpen(const char *path) { internal_strncmp(path, "/proc/", 6) == 0; } -#if SANITIZER_LINUX && !SANITIZER_ANDROID && !SANITIZER_GO +bool OpenReadsVaArgs(int oflag) { +# ifdef O_TMPFILE + return (oflag & (O_CREAT | O_TMPFILE)) != 0; +# else + return (oflag & O_CREAT) != 0; +# endif +} + +# if SANITIZER_LINUX && !SANITIZER_ANDROID && !SANITIZER_GO int GetNamedMappingFd(const char *name, uptr size, int *flags) { if (!common_flags()->decorate_proc_maps || !name) return -1; diff --git a/libsanitizer/sanitizer_common/sanitizer_posix.h b/libsanitizer/sanitizer_common/sanitizer_posix.h index c5811df..b5491c5 100644 --- a/libsanitizer/sanitizer_common/sanitizer_posix.h +++ b/libsanitizer/sanitizer_common/sanitizer_posix.h @@ -28,6 +28,9 @@ namespace __sanitizer { // Don't use directly, use __sanitizer::OpenFile() instead. uptr internal_open(const char *filename, int flags); uptr internal_open(const char *filename, int flags, u32 mode); +# if SANITIZER_FREEBSD +uptr internal_close_range(fd_t lowfd, fd_t highfd, int flags); +# endif uptr internal_close(fd_t fd); uptr internal_read(fd_t fd, void *buf, uptr count); @@ -74,21 +77,21 @@ int internal_sysctlbyname(const char *sname, void *oldp, uptr *oldlenp, // These functions call appropriate pthread_ functions directly, bypassing // the interceptor. They are weak and may not be present in some tools. SANITIZER_WEAK_ATTRIBUTE -int real_pthread_create(void *th, void *attr, void *(*callback)(void *), - void *param); +int internal_pthread_create(void *th, void *attr, void *(*callback)(void *), + void *param); SANITIZER_WEAK_ATTRIBUTE -int real_pthread_join(void *th, void **ret); - -#define DEFINE_REAL_PTHREAD_FUNCTIONS \ - namespace __sanitizer { \ - int real_pthread_create(void *th, void *attr, void *(*callback)(void *), \ - void *param) { \ - return REAL(pthread_create)(th, attr, callback, param); \ - } \ - int real_pthread_join(void *th, void **ret) { \ - return REAL(pthread_join(th, ret)); \ - } \ - } // namespace __sanitizer +int internal_pthread_join(void *th, void **ret); + +# define DEFINE_INTERNAL_PTHREAD_FUNCTIONS \ + namespace __sanitizer { \ + int internal_pthread_create(void *th, void *attr, \ + void *(*callback)(void *), void *param) { \ + return REAL(pthread_create)(th, attr, callback, param); \ + } \ + int internal_pthread_join(void *th, void **ret) { \ + return REAL(pthread_join)(th, ret); \ + } \ + } // namespace __sanitizer int internal_pthread_attr_getstack(void *attr, void **addr, uptr *size); @@ -108,6 +111,7 @@ bool IsStateDetached(int state); fd_t ReserveStandardFds(fd_t fd); bool ShouldMockFailureToOpen(const char *path); +bool OpenReadsVaArgs(int oflag); // Create a non-file mapping with a given /proc/self/maps name. uptr MmapNamed(void *addr, uptr length, int prot, int flags, const char *name); diff --git a/libsanitizer/sanitizer_common/sanitizer_posix_libcdep.cpp b/libsanitizer/sanitizer_common/sanitizer_posix_libcdep.cpp index e88e654..b1eb200 100644 --- a/libsanitizer/sanitizer_common/sanitizer_posix_libcdep.cpp +++ b/libsanitizer/sanitizer_common/sanitizer_posix_libcdep.cpp @@ -91,12 +91,12 @@ static rlim_t getlim(int res) { static void setlim(int res, rlim_t lim) { struct rlimit rlim; - if (getrlimit(res, const_cast<struct rlimit *>(&rlim))) { + if (getrlimit(res, &rlim)) { Report("ERROR: %s getrlimit() failed %d\n", SanitizerToolName, errno); Die(); } rlim.rlim_cur = lim; - if (setrlimit(res, const_cast<struct rlimit *>(&rlim))) { + if (setrlimit(res, &rlim)) { Report("ERROR: %s setrlimit() failed %d\n", SanitizerToolName, errno); Die(); } @@ -104,7 +104,27 @@ static void setlim(int res, rlim_t lim) { void DisableCoreDumperIfNecessary() { if (common_flags()->disable_coredump) { - setlim(RLIMIT_CORE, 0); + rlimit rlim; + CHECK_EQ(0, getrlimit(RLIMIT_CORE, &rlim)); + // On Linux, if the kernel.core_pattern sysctl starts with a '|' (i.e. it + // is being piped to a coredump handler such as systemd-coredumpd), the + // kernel ignores RLIMIT_CORE (since we aren't creating a file in the file + // system) except for the magic value of 1, which disables coredumps when + // piping. 1 byte is too small for any kind of valid core dump, so it + // also disables coredumps if kernel.core_pattern creates files directly. + // While most piped coredump handlers do respect the crashing processes' + // RLIMIT_CORE, this is notable not the case for Debian's systemd-coredump + // due to a local patch that changes sysctl.d/50-coredump.conf to ignore + // the specified limit and instead use RLIM_INFINITY. + // + // The alternative to using RLIMIT_CORE=1 would be to use prctl() with the + // PR_SET_DUMPABLE flag, however that also prevents ptrace(), so makes it + // impossible to attach a debugger. + // + // Note: we use rlim_max in the Min() call here since that is the upper + // limit for what can be set without getting an EINVAL error. + rlim.rlim_cur = Min<rlim_t>(SANITIZER_LINUX ? 1 : 0, rlim.rlim_max); + CHECK_EQ(0, setrlimit(RLIMIT_CORE, &rlim)); } } @@ -268,26 +288,86 @@ bool SignalContext::IsStackOverflow() const { #endif // SANITIZER_GO +static void SetNonBlock(int fd) { + int res = fcntl(fd, F_GETFL, 0); + CHECK(!internal_iserror(res, nullptr)); + + res |= O_NONBLOCK; + res = fcntl(fd, F_SETFL, res); + CHECK(!internal_iserror(res, nullptr)); +} + bool IsAccessibleMemoryRange(uptr beg, uptr size) { - uptr page_size = GetPageSizeCached(); - // Checking too large memory ranges is slow. - CHECK_LT(size, page_size * 10); - int sock_pair[2]; - if (pipe(sock_pair)) - return false; - uptr bytes_written = - internal_write(sock_pair[1], reinterpret_cast<void *>(beg), size); - int write_errno; - bool result; - if (internal_iserror(bytes_written, &write_errno)) { - CHECK_EQ(EFAULT, write_errno); - result = false; - } else { - result = (bytes_written == size); + while (size) { + // `read` from `fds[0]` into a dummy buffer to free up the pipe buffer for + // more `write` is slower than just recreating a pipe. + int fds[2]; + CHECK_EQ(0, pipe(fds)); + + auto cleanup = at_scope_exit([&]() { + internal_close(fds[0]); + internal_close(fds[1]); + }); + + SetNonBlock(fds[1]); + + int write_errno; + uptr w = internal_write(fds[1], reinterpret_cast<char *>(beg), size); + if (internal_iserror(w, &write_errno)) { + if (write_errno == EINTR) + continue; + CHECK_EQ(EFAULT, write_errno); + return false; + } + size -= w; + beg += w; + } + + return true; +} + +bool TryMemCpy(void *dest, const void *src, uptr n) { + if (!n) + return true; + int fds[2]; + CHECK_EQ(0, pipe(fds)); + + auto cleanup = at_scope_exit([&]() { + internal_close(fds[0]); + internal_close(fds[1]); + }); + + SetNonBlock(fds[0]); + SetNonBlock(fds[1]); + + char *d = static_cast<char *>(dest); + const char *s = static_cast<const char *>(src); + + while (n) { + int e; + uptr w = internal_write(fds[1], s, n); + if (internal_iserror(w, &e)) { + if (e == EINTR) + continue; + CHECK_EQ(EFAULT, e); + return false; + } + s += w; + n -= w; + + while (w) { + uptr r = internal_read(fds[0], d, w); + if (internal_iserror(r, &e)) { + CHECK_EQ(EINTR, e); + continue; + } + + d += r; + w -= r; + } } - internal_close(sock_pair[0]); - internal_close(sock_pair[1]); - return result; + + return true; } void PlatformPrepareForSandboxing(void *args) { @@ -307,9 +387,10 @@ static bool MmapFixed(uptr fixed_addr, uptr size, int additional_flags, MAP_PRIVATE | MAP_FIXED | additional_flags | MAP_ANON, name); int reserrno; if (internal_iserror(p, &reserrno)) { - Report("ERROR: %s failed to " - "allocate 0x%zx (%zd) bytes at address %zx (errno: %d)\n", - SanitizerToolName, size, size, fixed_addr, reserrno); + Report( + "ERROR: %s failed to " + "allocate 0x%zx (%zd) bytes at address %p (errno: %d)\n", + SanitizerToolName, size, size, (void *)fixed_addr, reserrno); return false; } IncreaseTotalMmap(size); @@ -462,7 +543,11 @@ pid_t StartSubprocess(const char *program, const char *const argv[], internal_close(stderr_fd); } +# if SANITIZER_FREEBSD + internal_close_range(3, ~static_cast<fd_t>(0), 0); +# else for (int fd = sysconf(_SC_OPEN_MAX); fd > 2; fd--) internal_close(fd); +# endif internal_execve(program, const_cast<char **>(&argv[0]), const_cast<char *const *>(envp)); diff --git a/libsanitizer/sanitizer_common/sanitizer_printf.cpp b/libsanitizer/sanitizer_common/sanitizer_printf.cpp index 62c1cf4..2451172 100644 --- a/libsanitizer/sanitizer_common/sanitizer_printf.cpp +++ b/libsanitizer/sanitizer_common/sanitizer_printf.cpp @@ -54,7 +54,7 @@ static int AppendNumber(char **buff, const char *buff_end, u64 absolute_value, uptr num_buffer[kMaxLen]; int pos = 0; do { - RAW_CHECK_MSG((uptr)pos < kMaxLen, "AppendNumber buffer overflow"); + RAW_CHECK_MSG((uptr)pos < kMaxLen, "AppendNumber buffer overflow",); num_buffer[pos++] = absolute_value % base; absolute_value /= base; } while (absolute_value > 0); diff --git a/libsanitizer/sanitizer_common/sanitizer_procmaps_bsd.cpp b/libsanitizer/sanitizer_common/sanitizer_procmaps_bsd.cpp index 36a82c4..ee3fd5b 100644 --- a/libsanitizer/sanitizer_common/sanitizer_procmaps_bsd.cpp +++ b/libsanitizer/sanitizer_common/sanitizer_procmaps_bsd.cpp @@ -13,9 +13,6 @@ #include "sanitizer_platform.h" #if SANITIZER_FREEBSD || SANITIZER_NETBSD #include "sanitizer_common.h" -#if SANITIZER_FREEBSD -#include "sanitizer_freebsd.h" -#endif #include "sanitizer_procmaps.h" // clang-format off @@ -29,29 +26,35 @@ #include <limits.h> -// Fix 'kinfo_vmentry' definition on FreeBSD prior v9.2 in 32-bit mode. -#if SANITIZER_FREEBSD && (SANITIZER_WORDSIZE == 32) -#include <osreldate.h> -#if __FreeBSD_version <= 902001 // v9.2 -#define kinfo_vmentry xkinfo_vmentry -#endif -#endif - namespace __sanitizer { #if SANITIZER_FREEBSD void GetMemoryProfile(fill_profile_f cb, uptr *stats) { - const int Mib[] = { - CTL_KERN, - KERN_PROC, - KERN_PROC_PID, - getpid() - }; - - struct kinfo_proc InfoProc; - uptr Len = sizeof(InfoProc); - CHECK_EQ(internal_sysctl(Mib, ARRAY_SIZE(Mib), nullptr, (uptr *)&InfoProc, &Len, 0), 0); - cb(0, InfoProc.ki_rssize * GetPageSizeCached(), false, stats); + const int Mib[] = {CTL_KERN, KERN_PROC, KERN_PROC_PID, getpid()}; + + struct kinfo_proc *InfoProc; + uptr Len = sizeof(*InfoProc); + uptr Size = Len; + InfoProc = (struct kinfo_proc *)MmapOrDie(Size, "GetMemoryProfile()"); + CHECK_EQ( + internal_sysctl(Mib, ARRAY_SIZE(Mib), nullptr, (uptr *)InfoProc, &Len, 0), + 0); + cb(0, InfoProc->ki_rssize * GetPageSizeCached(), false, stats); + UnmapOrDie(InfoProc, Size, true); +} +#elif SANITIZER_NETBSD +void GetMemoryProfile(fill_profile_f cb, uptr *stats) { + struct kinfo_proc2 *InfoProc; + uptr Len = sizeof(*InfoProc); + uptr Size = Len; + const int Mib[] = {CTL_KERN, KERN_PROC2, KERN_PROC_PID, + getpid(), (int)Size, 1}; + InfoProc = (struct kinfo_proc2 *)MmapOrDie(Size, "GetMemoryProfile()"); + CHECK_EQ( + internal_sysctl(Mib, ARRAY_SIZE(Mib), nullptr, (uptr *)InfoProc, &Len, 0), + 0); + cb(0, InfoProc->p_vm_rssize * GetPageSizeCached(), false, stats); + UnmapOrDie(InfoProc, Size, true); } #endif diff --git a/libsanitizer/sanitizer_common/sanitizer_procmaps_common.cpp b/libsanitizer/sanitizer_common/sanitizer_procmaps_common.cpp index a7805ad..7214a2b 100644 --- a/libsanitizer/sanitizer_common/sanitizer_procmaps_common.cpp +++ b/libsanitizer/sanitizer_common/sanitizer_procmaps_common.cpp @@ -145,7 +145,7 @@ void MemoryMappingLayout::DumpListOfModules( } } -#if SANITIZER_LINUX || SANITIZER_ANDROID || SANITIZER_SOLARIS || SANITIZER_NETBSD +#if SANITIZER_LINUX || SANITIZER_ANDROID || SANITIZER_SOLARIS void GetMemoryProfile(fill_profile_f cb, uptr *stats) { char *smaps = nullptr; uptr smaps_cap = 0; diff --git a/libsanitizer/sanitizer_common/sanitizer_procmaps_mac.cpp b/libsanitizer/sanitizer_common/sanitizer_procmaps_mac.cpp index f2f3846..5ff8d18 100644 --- a/libsanitizer/sanitizer_common/sanitizer_procmaps_mac.cpp +++ b/libsanitizer/sanitizer_common/sanitizer_procmaps_mac.cpp @@ -146,13 +146,8 @@ static bool IsDyldHdr(const mach_header *hdr) { // until we hit a Mach header matching dyld instead. These recurse // calls are expensive, but the first memory map generation occurs // early in the process, when dyld is one of the only images loaded, -// so it will be hit after only a few iterations. These assumptions don't -// hold on macOS 13+ anymore (dyld itself has moved into the shared cache). - -// FIXME: Unfortunately, the upstream revised version to deal with macOS 13+ -// is incompatible with GCC and also uses APIs not available on earlier -// systems which we support; backed out for now. - +// so it will be hit after only a few iterations. These assumptions don't hold +// on macOS 13+ anymore (dyld itself has moved into the shared cache). static mach_header *GetDyldImageHeaderViaVMRegion() { vm_address_t address = 0; @@ -176,17 +171,64 @@ static mach_header *GetDyldImageHeaderViaVMRegion() { } } +extern "C" { +struct dyld_shared_cache_dylib_text_info { + uint64_t version; // current version 2 + // following fields all exist in version 1 + uint64_t loadAddressUnslid; + uint64_t textSegmentSize; + uuid_t dylibUuid; + const char *path; // pointer invalid at end of iterations + // following fields all exist in version 2 + uint64_t textSegmentOffset; // offset from start of cache +}; +typedef struct dyld_shared_cache_dylib_text_info + dyld_shared_cache_dylib_text_info; + +extern bool _dyld_get_shared_cache_uuid(uuid_t uuid); +extern const void *_dyld_get_shared_cache_range(size_t *length); +extern int dyld_shared_cache_iterate_text( + const uuid_t cacheUuid, + void (^callback)(const dyld_shared_cache_dylib_text_info *info)); +} // extern "C" + +static mach_header *GetDyldImageHeaderViaSharedCache() { + uuid_t uuid; + bool hasCache = _dyld_get_shared_cache_uuid(uuid); + if (!hasCache) + return nullptr; + + size_t cacheLength; + __block uptr cacheStart = (uptr)_dyld_get_shared_cache_range(&cacheLength); + CHECK(cacheStart && cacheLength); + + __block mach_header *dyldHdr = nullptr; + int res = dyld_shared_cache_iterate_text( + uuid, ^(const dyld_shared_cache_dylib_text_info *info) { + CHECK_GE(info->version, 2); + mach_header *hdr = + (mach_header *)(cacheStart + info->textSegmentOffset); + if (IsDyldHdr(hdr)) + dyldHdr = hdr; + }); + CHECK_EQ(res, 0); + + return dyldHdr; +} + const mach_header *get_dyld_hdr() { if (!dyld_hdr) { // On macOS 13+, dyld itself has moved into the shared cache. Looking it up // via vm_region_recurse_64() causes spins/hangs/crashes. - // FIXME: find a way to do this compatible with GCC. if (GetMacosAlignedVersion() >= MacosVersion(13, 0)) { + dyld_hdr = GetDyldImageHeaderViaSharedCache(); + if (!dyld_hdr) { VReport(1, - "looking up the dyld image header in the shared cache on " - "macOS 13+ is not yet supported. Falling back to " + "Failed to lookup the dyld image header in the shared cache on " + "macOS 13+ (or no shared cache in use). Falling back to " "lookup via vm_region_recurse_64().\n"); dyld_hdr = GetDyldImageHeaderViaVMRegion(); + } } else { dyld_hdr = GetDyldImageHeaderViaVMRegion(); } @@ -391,7 +433,9 @@ void MemoryMappingLayout::DumpListOfModules( MemoryMappedSegmentData data; segment.data_ = &data; while (Next(&segment)) { - if (segment.filename[0] == '\0') continue; + // skip the __PAGEZERO segment, its vmsize is 0 + if (segment.filename[0] == '\0' || (segment.start == segment.end)) + continue; LoadedModule *cur_module = nullptr; if (!modules->empty() && 0 == internal_strcmp(segment.filename, modules->back().full_name())) { diff --git a/libsanitizer/sanitizer_common/sanitizer_procmaps_solaris.cpp b/libsanitizer/sanitizer_common/sanitizer_procmaps_solaris.cpp index eeb49e2..80b8158 100644 --- a/libsanitizer/sanitizer_common/sanitizer_procmaps_solaris.cpp +++ b/libsanitizer/sanitizer_common/sanitizer_procmaps_solaris.cpp @@ -11,6 +11,10 @@ // Before Solaris 11.4, <procfs.h> doesn't work in a largefile environment. #undef _FILE_OFFSET_BITS + +// Avoid conflict between `_TIME_BITS` defined vs. `_FILE_OFFSET_BITS` +// undefined in some Linux configurations. +#undef _TIME_BITS #include "sanitizer_platform.h" #if SANITIZER_SOLARIS # include <fcntl.h> diff --git a/libsanitizer/sanitizer_common/sanitizer_ptrauth.h b/libsanitizer/sanitizer_common/sanitizer_ptrauth.h index 5200354..265a992 100644 --- a/libsanitizer/sanitizer_common/sanitizer_ptrauth.h +++ b/libsanitizer/sanitizer_common/sanitizer_ptrauth.h @@ -9,31 +9,33 @@ #ifndef SANITIZER_PTRAUTH_H #define SANITIZER_PTRAUTH_H -#if __has_feature(ptrauth_calls) -#include <ptrauth.h> +#if __has_feature(ptrauth_intrinsics) +# include <ptrauth.h> #elif defined(__ARM_FEATURE_PAC_DEFAULT) && !defined(__APPLE__) -inline unsigned long ptrauth_strip(void* __value, unsigned int __key) { - // On the stack the link register is protected with Pointer - // Authentication Code when compiled with -mbranch-protection. - // Let's stripping the PAC unconditionally because xpaclri is in - // the NOP space so will do nothing when it is not enabled or not available. - unsigned long ret; - asm volatile( - "mov x30, %1\n\t" - "hint #7\n\t" // xpaclri - "mov %0, x30\n\t" - : "=r"(ret) - : "r"(__value) - : "x30"); - return ret; -} -#define ptrauth_auth_data(__value, __old_key, __old_data) __value -#define ptrauth_string_discriminator(__string) ((int)0) +// On the stack the link register is protected with Pointer +// Authentication Code when compiled with -mbranch-protection. +// Let's stripping the PAC unconditionally because xpaclri is in +// the NOP space so will do nothing when it is not enabled or not available. +# define ptrauth_strip(__value, __key) \ + ({ \ + __typeof(__value) ret; \ + asm volatile( \ + "mov x30, %1\n\t" \ + "hint #7\n\t" \ + "mov %0, x30\n\t" \ + "mov x30, xzr\n\t" \ + : "=r"(ret) \ + : "r"(__value) \ + : "x30"); \ + ret; \ + }) +# define ptrauth_auth_data(__value, __old_key, __old_data) __value +# define ptrauth_string_discriminator(__string) ((int)0) #else // Copied from <ptrauth.h> -#define ptrauth_strip(__value, __key) __value -#define ptrauth_auth_data(__value, __old_key, __old_data) __value -#define ptrauth_string_discriminator(__string) ((int)0) +# define ptrauth_strip(__value, __key) __value +# define ptrauth_auth_data(__value, __old_key, __old_data) __value +# define ptrauth_string_discriminator(__string) ((int)0) #endif #define STRIP_PAC_PC(pc) ((uptr)ptrauth_strip(pc, 0)) diff --git a/libsanitizer/sanitizer_common/sanitizer_redefine_builtins.h b/libsanitizer/sanitizer_common/sanitizer_redefine_builtins.h index eba8e0f..41e0613 100644 --- a/libsanitizer/sanitizer_common/sanitizer_redefine_builtins.h +++ b/libsanitizer/sanitizer_common/sanitizer_redefine_builtins.h @@ -15,11 +15,13 @@ # define SANITIZER_REDEFINE_BUILTINS_H // The asm hack only works with GCC and Clang. -# if !defined(_WIN32) && defined(HAVE_AS_SYM_ASSIGN) +# if !defined(_WIN32) -asm("memcpy = __sanitizer_internal_memcpy"); -asm("memmove = __sanitizer_internal_memmove"); -asm("memset = __sanitizer_internal_memset"); +asm(R"( + .set memcpy, __sanitizer_internal_memcpy + .set memmove, __sanitizer_internal_memmove + .set memset, __sanitizer_internal_memset + )"); # if defined(__cplusplus) && \ !defined(SANITIZER_COMMON_REDEFINE_BUILTINS_IN_STD) @@ -50,7 +52,7 @@ using vector = Define_SANITIZER_COMMON_NO_REDEFINE_BUILTINS_in_cpp_file; } // namespace std # endif // __cpluplus -# endif // !_WIN32 && HAVE_AS_SYM_ASSIGN +# endif // !_WIN32 # endif // SANITIZER_REDEFINE_BUILTINS_H #endif // SANITIZER_COMMON_NO_REDEFINE_BUILTINS diff --git a/libsanitizer/sanitizer_common/sanitizer_stack_store.cpp b/libsanitizer/sanitizer_common/sanitizer_stack_store.cpp index 1484709..c11df0d 100644 --- a/libsanitizer/sanitizer_common/sanitizer_stack_store.cpp +++ b/libsanitizer/sanitizer_common/sanitizer_stack_store.cpp @@ -44,6 +44,9 @@ StackStore::Id StackStore::Store(const StackTrace &trace, uptr *pack) { uptr idx = 0; *pack = 0; uptr *stack_trace = Alloc(h.size + 1, &idx, pack); + // No more space. + if (stack_trace == nullptr) + return 0; *stack_trace = h.ToUptr(); internal_memcpy(stack_trace + 1, trace.trace, h.size * sizeof(uptr)); *pack += blocks_[GetBlockIdx(idx)].Stored(h.size + 1); @@ -76,8 +79,10 @@ uptr *StackStore::Alloc(uptr count, uptr *idx, uptr *pack) { uptr block_idx = GetBlockIdx(start); uptr last_idx = GetBlockIdx(start + count - 1); if (LIKELY(block_idx == last_idx)) { - // Fits into the a single block. - CHECK_LT(block_idx, ARRAY_SIZE(blocks_)); + // Fits into a single block. + // No more available blocks. Indicate inability to allocate more memory. + if (block_idx >= ARRAY_SIZE(blocks_)) + return nullptr; *idx = start; return blocks_[block_idx].GetOrCreate(this) + GetInBlockIdx(start); } diff --git a/libsanitizer/sanitizer_common/sanitizer_stackdepot.cpp b/libsanitizer/sanitizer_common/sanitizer_stackdepot.cpp index a746d46..3776e8c 100644 --- a/libsanitizer/sanitizer_common/sanitizer_stackdepot.cpp +++ b/libsanitizer/sanitizer_common/sanitizer_stackdepot.cpp @@ -215,16 +215,16 @@ StackTrace StackDepotGet(u32 id) { return theDepot.Get(id); } -void StackDepotLockAll() { - theDepot.LockAll(); +void StackDepotLockBeforeFork() { + theDepot.LockBeforeFork(); compress_thread.LockAndStop(); stackStore.LockAll(); } -void StackDepotUnlockAll() { +void StackDepotUnlockAfterFork(bool fork_child) { stackStore.UnlockAll(); compress_thread.Unlock(); - theDepot.UnlockAll(); + theDepot.UnlockAfterFork(fork_child); } void StackDepotPrintAll() { diff --git a/libsanitizer/sanitizer_common/sanitizer_stackdepot.h b/libsanitizer/sanitizer_common/sanitizer_stackdepot.h index cca6fd5..82cf757 100644 --- a/libsanitizer/sanitizer_common/sanitizer_stackdepot.h +++ b/libsanitizer/sanitizer_common/sanitizer_stackdepot.h @@ -39,8 +39,8 @@ StackDepotHandle StackDepotPut_WithHandle(StackTrace stack); // Retrieves a stored stack trace by the id. StackTrace StackDepotGet(u32 id); -void StackDepotLockAll(); -void StackDepotUnlockAll(); +void StackDepotLockBeforeFork(); +void StackDepotUnlockAfterFork(bool fork_child); void StackDepotPrintAll(); void StackDepotStopBackgroundThread(); diff --git a/libsanitizer/sanitizer_common/sanitizer_stackdepotbase.h b/libsanitizer/sanitizer_common/sanitizer_stackdepotbase.h index 96d1ddc..279bc5d 100644 --- a/libsanitizer/sanitizer_common/sanitizer_stackdepotbase.h +++ b/libsanitizer/sanitizer_common/sanitizer_stackdepotbase.h @@ -52,8 +52,8 @@ class StackDepotBase { }; } - void LockAll(); - void UnlockAll(); + void LockBeforeFork(); + void UnlockAfterFork(bool fork_child); void PrintAll(); void TestOnlyUnmap() { @@ -160,18 +160,33 @@ StackDepotBase<Node, kReservedBits, kTabSizeLog>::Get(u32 id) { } template <class Node, int kReservedBits, int kTabSizeLog> -void StackDepotBase<Node, kReservedBits, kTabSizeLog>::LockAll() { - for (int i = 0; i < kTabSize; ++i) { - lock(&tab[i]); - } +void StackDepotBase<Node, kReservedBits, kTabSizeLog>::LockBeforeFork() { + // Do not lock hash table. It's very expensive, but it's not rely needed. The + // parent process will neither lock nor unlock. Child process risks to be + // deadlocked on already locked buckets. To avoid deadlock we will unlock + // every locked buckets in `UnlockAfterFork`. This may affect consistency of + // the hash table, but the only issue is a few items inserted by parent + // process will be not found by child, and the child may insert them again, + // wasting some space in `stackStore`. + + // We still need to lock nodes. + nodes.Lock(); } template <class Node, int kReservedBits, int kTabSizeLog> -void StackDepotBase<Node, kReservedBits, kTabSizeLog>::UnlockAll() { +void StackDepotBase<Node, kReservedBits, kTabSizeLog>::UnlockAfterFork( + bool fork_child) { + nodes.Unlock(); + + // Only unlock in child process to avoid deadlock. See `LockBeforeFork`. + if (!fork_child) + return; + for (int i = 0; i < kTabSize; ++i) { atomic_uint32_t *p = &tab[i]; uptr s = atomic_load(p, memory_order_relaxed); - unlock(p, s & kUnlockMask); + if (s & kLockMask) + unlock(p, s & kUnlockMask); } } diff --git a/libsanitizer/sanitizer_common/sanitizer_stacktrace.cpp b/libsanitizer/sanitizer_common/sanitizer_stacktrace.cpp index 661495e..d24fae9 100644 --- a/libsanitizer/sanitizer_common/sanitizer_stacktrace.cpp +++ b/libsanitizer/sanitizer_common/sanitizer_stacktrace.cpp @@ -87,8 +87,8 @@ static inline uhwptr *GetCanonicFrame(uptr bp, // Nope, this does not look right either. This means the frame after next does // not have a valid frame pointer, but we can still extract the caller PC. // Unfortunately, there is no way to decide between GCC and LLVM frame - // layouts. Assume GCC. - return bp_prev - 1; + // layouts. Assume LLVM. + return bp_prev; #else return (uhwptr*)bp; #endif @@ -111,21 +111,14 @@ void BufferedStackTrace::UnwindFast(uptr pc, uptr bp, uptr stack_top, IsAligned((uptr)frame, sizeof(*frame)) && size < max_depth) { #ifdef __powerpc__ - // PowerPC ABIs specify that the return address is saved on the - // *caller's* stack frame. Thus we must dereference the back chain - // to find the caller frame before extracting it. + // PowerPC ABIs specify that the return address is saved at offset + // 16 of the *caller's* stack frame. Thus we must dereference the + // back chain to find the caller frame before extracting it. uhwptr *caller_frame = (uhwptr*)frame[0]; if (!IsValidFrame((uptr)caller_frame, stack_top, bottom) || !IsAligned((uptr)caller_frame, sizeof(uhwptr))) break; - // For most ABIs the offset where the return address is saved is two - // register sizes. The exception is the SVR4 ABI, which uses an - // offset of only one register size. -#ifdef _CALL_SYSV - uhwptr pc1 = caller_frame[1]; -#else uhwptr pc1 = caller_frame[2]; -#endif #elif defined(__s390__) uhwptr pc1 = frame[14]; #elif defined(__loongarch__) || defined(__riscv) diff --git a/libsanitizer/sanitizer_common/sanitizer_stacktrace_libcdep.cpp b/libsanitizer/sanitizer_common/sanitizer_stacktrace_libcdep.cpp index 9a4c80f..1a0e00d 100644 --- a/libsanitizer/sanitizer_common/sanitizer_stacktrace_libcdep.cpp +++ b/libsanitizer/sanitizer_common/sanitizer_stacktrace_libcdep.cpp @@ -33,13 +33,14 @@ class StackTraceTextPrinter { stack_trace_fmt)) {} bool ProcessAddressFrames(uptr pc) { - SymbolizedStack *frames = symbolize_ - ? Symbolizer::GetOrInit()->SymbolizePC(pc) - : SymbolizedStack::New(pc); + SymbolizedStackHolder symbolized_stack( + symbolize_ ? Symbolizer::GetOrInit()->SymbolizePC(pc) + : SymbolizedStack::New(pc)); + const SymbolizedStack *frames = symbolized_stack.get(); if (!frames) return false; - for (SymbolizedStack *cur = frames; cur; cur = cur->next) { + for (const SymbolizedStack *cur = frames; cur; cur = cur->next) { uptr prev_len = output_->length(); StackTracePrinter::GetOrInit()->RenderFrame( output_, stack_trace_fmt_, frame_num_++, cur->info.address, @@ -51,19 +52,18 @@ class StackTraceTextPrinter { ExtendDedupToken(cur); } - frames->ClearAll(); return true; } private: // Extend the dedup token by appending a new frame. - void ExtendDedupToken(SymbolizedStack *stack) { + void ExtendDedupToken(const SymbolizedStack *stack) { if (!dedup_token_) return; if (dedup_frames_-- > 0) { if (dedup_token_->length()) - dedup_token_->AppendF("--"); + dedup_token_->Append("--"); if (stack->info.function) dedup_token_->Append(stack->info.function); } @@ -99,7 +99,7 @@ void StackTrace::PrintTo(InternalScopedString *output) const { output, &dedup_token); if (trace == nullptr || size == 0) { - output->AppendF(" <empty stack>\n\n"); + output->Append(" <empty stack>\n\n"); return; } @@ -111,7 +111,7 @@ void StackTrace::PrintTo(InternalScopedString *output) const { } // Always add a trailing empty line after stack trace. - output->AppendF("\n"); + output->Append("\n"); // Append deduplication token, if non-empty. if (dedup_token.length()) @@ -198,7 +198,7 @@ void __sanitizer_symbolize_pc(uptr pc, const char *fmt, char *out_buf, StackTraceTextPrinter printer(fmt, '\0', &output, nullptr); if (!printer.ProcessAddressFrames(pc)) { output.clear(); - output.AppendF("<can't symbolize>"); + output.Append("<can't symbolize>"); } CopyStringToBuffer(output, out_buf, out_buf_size); } diff --git a/libsanitizer/sanitizer_common/sanitizer_stacktrace_printer.cpp b/libsanitizer/sanitizer_common/sanitizer_stacktrace_printer.cpp index 8db3210..5631d13 100644 --- a/libsanitizer/sanitizer_common/sanitizer_stacktrace_printer.cpp +++ b/libsanitizer/sanitizer_common/sanitizer_stacktrace_printer.cpp @@ -12,9 +12,11 @@ #include "sanitizer_stacktrace_printer.h" +#include "sanitizer_common.h" #include "sanitizer_file.h" #include "sanitizer_flags.h" #include "sanitizer_fuchsia.h" +#include "sanitizer_symbolizer_markup.h" namespace __sanitizer { @@ -25,15 +27,13 @@ StackTracePrinter *StackTracePrinter::GetOrInit() { if (stacktrace_printer) return stacktrace_printer; - stacktrace_printer = - new (GetGlobalLowLevelAllocator()) FormattedStackTracePrinter(); + stacktrace_printer = StackTracePrinter::NewStackTracePrinter(); CHECK(stacktrace_printer); return stacktrace_printer; } -const char *FormattedStackTracePrinter::StripFunctionName( - const char *function) { +const char *StackTracePrinter::StripFunctionName(const char *function) { if (!common_flags()->demangle) return function; if (!function) @@ -62,6 +62,13 @@ const char *FormattedStackTracePrinter::StripFunctionName( // sanitizer_symbolizer_markup.cpp implements these differently. #if !SANITIZER_SYMBOLIZER_MARKUP +StackTracePrinter *StackTracePrinter::NewStackTracePrinter() { + if (common_flags()->enable_symbolizer_markup) + return new (GetGlobalLowLevelAllocator()) MarkupStackTracePrinter(); + + return new (GetGlobalLowLevelAllocator()) FormattedStackTracePrinter(); +} + static const char *DemangleFunctionName(const char *function) { if (!common_flags()->demangle) return function; @@ -145,12 +152,12 @@ static void MaybeBuildIdToBuffer(const AddressInfo &info, bool PrefixSpace, InternalScopedString *buffer) { if (info.uuid_size) { if (PrefixSpace) - buffer->AppendF(" "); - buffer->AppendF("(BuildId: "); + buffer->Append(" "); + buffer->Append("(BuildId: "); for (uptr i = 0; i < info.uuid_size; ++i) { buffer->AppendF("%02x", info.uuid[i]); } - buffer->AppendF(")"); + buffer->Append(")"); } } @@ -185,7 +192,7 @@ void FormattedStackTracePrinter::RenderFrame(InternalScopedString *buffer, buffer->AppendF("%u", frame_no); break; case 'p': - buffer->AppendF("0x%zx", address); + buffer->AppendF("%p", (void *)address); break; case 'm': buffer->AppendF("%s", StripPathPrefix(info->module, strip_path_prefix)); @@ -242,7 +249,7 @@ void FormattedStackTracePrinter::RenderFrame(InternalScopedString *buffer, MaybeBuildIdToBuffer(*info, /*PrefixSpace=*/true, buffer); #endif } else { - buffer->AppendF("(<unknown module>)"); + buffer->Append("(<unknown module>)"); } break; case 'M': @@ -262,7 +269,7 @@ void FormattedStackTracePrinter::RenderFrame(InternalScopedString *buffer, break; default: Report("Unsupported specifier in stack frame format: %c (%p)!\n", *p, - (void *)p); + (const void *)p); Die(); } } @@ -316,7 +323,7 @@ void FormattedStackTracePrinter::RenderData(InternalScopedString *buffer, break; default: Report("Unsupported specifier in stack frame format: %c (%p)!\n", *p, - (void *)p); + (const void *)p); Die(); } } @@ -324,14 +331,15 @@ void FormattedStackTracePrinter::RenderData(InternalScopedString *buffer, #endif // !SANITIZER_SYMBOLIZER_MARKUP -void FormattedStackTracePrinter::RenderSourceLocation( - InternalScopedString *buffer, const char *file, int line, int column, - bool vs_style, const char *strip_path_prefix) { +void StackTracePrinter::RenderSourceLocation(InternalScopedString *buffer, + const char *file, int line, + int column, bool vs_style, + const char *strip_path_prefix) { if (vs_style && line > 0) { buffer->AppendF("%s(%d", StripPathPrefix(file, strip_path_prefix), line); if (column > 0) buffer->AppendF(",%d", column); - buffer->AppendF(")"); + buffer->Append(")"); return; } @@ -343,9 +351,10 @@ void FormattedStackTracePrinter::RenderSourceLocation( } } -void FormattedStackTracePrinter::RenderModuleLocation( - InternalScopedString *buffer, const char *module, uptr offset, - ModuleArch arch, const char *strip_path_prefix) { +void StackTracePrinter::RenderModuleLocation(InternalScopedString *buffer, + const char *module, uptr offset, + ModuleArch arch, + const char *strip_path_prefix) { buffer->AppendF("(%s", StripPathPrefix(module, strip_path_prefix)); if (arch != kModuleArchUnknown) { buffer->AppendF(":%s", ModuleArchToString(arch)); diff --git a/libsanitizer/sanitizer_common/sanitizer_stacktrace_printer.h b/libsanitizer/sanitizer_common/sanitizer_stacktrace_printer.h index 3e02333..e39cb89 100644 --- a/libsanitizer/sanitizer_common/sanitizer_stacktrace_printer.h +++ b/libsanitizer/sanitizer_common/sanitizer_stacktrace_printer.h @@ -25,27 +25,38 @@ class StackTracePrinter { public: static StackTracePrinter *GetOrInit(); - virtual const char *StripFunctionName(const char *function) = 0; + // Strip interceptor prefixes from function name. + const char *StripFunctionName(const char *function); virtual void RenderFrame(InternalScopedString *buffer, const char *format, int frame_no, uptr address, const AddressInfo *info, - bool vs_style, - const char *strip_path_prefix = "") = 0; + bool vs_style, const char *strip_path_prefix = "") { + // Should be pure virtual, but we can't depend on __cxa_pure_virtual. + UNIMPLEMENTED(); + } - virtual bool RenderNeedsSymbolization(const char *format) = 0; + virtual bool RenderNeedsSymbolization(const char *format) { + // Should be pure virtual, but we can't depend on __cxa_pure_virtual. + UNIMPLEMENTED(); + } - virtual void RenderSourceLocation(InternalScopedString *buffer, - const char *file, int line, int column, - bool vs_style, - const char *strip_path_prefix) = 0; + void RenderSourceLocation(InternalScopedString *buffer, const char *file, + int line, int column, bool vs_style, + const char *strip_path_prefix); - virtual void RenderModuleLocation(InternalScopedString *buffer, - const char *module, uptr offset, - ModuleArch arch, - const char *strip_path_prefix) = 0; + void RenderModuleLocation(InternalScopedString *buffer, const char *module, + uptr offset, ModuleArch arch, + const char *strip_path_prefix); virtual void RenderData(InternalScopedString *buffer, const char *format, const DataInfo *DI, - const char *strip_path_prefix = "") = 0; + const char *strip_path_prefix = "") { + // Should be pure virtual, but we can't depend on __cxa_pure_virtual. + UNIMPLEMENTED(); + } + + private: + // To be called from StackTracePrinter::GetOrInit + static StackTracePrinter *NewStackTracePrinter(); protected: ~StackTracePrinter() {} @@ -53,9 +64,6 @@ class StackTracePrinter { class FormattedStackTracePrinter : public StackTracePrinter { public: - // Strip interceptor prefixes from function name. - const char *StripFunctionName(const char *function) override; - // Render the contents of "info" structure, which represents the contents of // stack frame "frame_no" and appends it to the "buffer". "format" is a // string with placeholders, which is copied to the output with @@ -90,14 +98,6 @@ class FormattedStackTracePrinter : public StackTracePrinter { bool RenderNeedsSymbolization(const char *format) override; - void RenderSourceLocation(InternalScopedString *buffer, const char *file, - int line, int column, bool vs_style, - const char *strip_path_prefix) override; - - void RenderModuleLocation(InternalScopedString *buffer, const char *module, - uptr offset, ModuleArch arch, - const char *strip_path_prefix) override; - // Same as RenderFrame, but for data section (global variables). // Accepts %s, %l from above. // Also accepts: diff --git a/libsanitizer/sanitizer_common/sanitizer_stacktrace_sparc.cpp b/libsanitizer/sanitizer_common/sanitizer_stacktrace_sparc.cpp index a200079..74f4352 100644 --- a/libsanitizer/sanitizer_common/sanitizer_stacktrace_sparc.cpp +++ b/libsanitizer/sanitizer_common/sanitizer_stacktrace_sparc.cpp @@ -58,17 +58,16 @@ void BufferedStackTrace::UnwindFast(uptr pc, uptr bp, uptr stack_top, // Avoid infinite loop when frame == frame[0] by using frame > prev_frame. while (IsValidFrame(bp, stack_top, bottom) && IsAligned(bp, sizeof(uhwptr)) && size < max_depth) { - uhwptr pc1 = ((uhwptr *)bp)[15]; + // %o7 contains the address of the call instruction and not the + // return address, so we need to compensate. + uhwptr pc1 = GetNextInstructionPc(((uhwptr *)bp)[15]); // Let's assume that any pointer in the 0th page is invalid and // stop unwinding here. If we're adding support for a platform // where this isn't true, we need to reconsider this check. if (pc1 < kPageSize) break; - if (pc1 != pc) { - // %o7 contains the address of the call instruction and not the - // return address, so we need to compensate. - trace_buffer[size++] = GetNextInstructionPc((uptr)pc1); - } + if (pc1 != pc) + trace_buffer[size++] = pc1; bottom = bp; bp = (uptr)((uhwptr *)bp)[14] + STACK_BIAS; } diff --git a/libsanitizer/sanitizer_common/sanitizer_stoptheworld_linux_libcdep.cpp b/libsanitizer/sanitizer_common/sanitizer_stoptheworld_linux_libcdep.cpp index 25c4af7..945da99 100644 --- a/libsanitizer/sanitizer_common/sanitizer_stoptheworld_linux_libcdep.cpp +++ b/libsanitizer/sanitizer_common/sanitizer_stoptheworld_linux_libcdep.cpp @@ -137,10 +137,6 @@ class ThreadSuspender { }; bool ThreadSuspender::SuspendThread(tid_t 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_.ContainsTid(tid)) return false; int pterrno; if (internal_iserror(internal_ptrace(PTRACE_ATTACH, tid, nullptr, nullptr), &pterrno)) { @@ -217,17 +213,28 @@ bool ThreadSuspender::SuspendAllThreads() { switch (thread_lister.ListThreads(&threads)) { case ThreadLister::Error: ResumeAllThreads(); + VReport(1, "Failed to list threads\n"); return false; case ThreadLister::Incomplete: + VReport(1, "Incomplete list\n"); retry = true; break; case ThreadLister::Ok: break; } for (tid_t tid : threads) { + // 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_.ContainsTid(tid)) + continue; if (SuspendThread(tid)) retry = true; + else + VReport(2, "%llu/status: %s\n", tid, thread_lister.LoadStatus(tid)); } + if (retry) + VReport(1, "SuspendAllThreads retry: %d\n", i); } return suspended_threads_list_.ThreadCount(); } @@ -257,8 +264,8 @@ static void TracerThreadDieCallback() { static void TracerThreadSignalHandler(int signum, __sanitizer_siginfo *siginfo, void *uctx) { SignalContext ctx(siginfo, uctx); - Printf("Tracer caught signal %d: addr=0x%zx pc=0x%zx sp=0x%zx\n", signum, - ctx.addr, ctx.pc, ctx.sp); + Printf("Tracer caught signal %d: addr=%p pc=%p sp=%p\n", signum, + (void *)ctx.addr, (void *)ctx.pc, (void *)ctx.sp); ThreadSuspender *inst = thread_suspender_instance; if (inst) { if (signum == SIGABRT) diff --git a/libsanitizer/sanitizer_common/sanitizer_stoptheworld_netbsd_libcdep.cpp b/libsanitizer/sanitizer_common/sanitizer_stoptheworld_netbsd_libcdep.cpp index 701db72..58a0cfd 100644 --- a/libsanitizer/sanitizer_common/sanitizer_stoptheworld_netbsd_libcdep.cpp +++ b/libsanitizer/sanitizer_common/sanitizer_stoptheworld_netbsd_libcdep.cpp @@ -158,8 +158,8 @@ static void TracerThreadDieCallback() { static void TracerThreadSignalHandler(int signum, __sanitizer_siginfo *siginfo, void *uctx) { SignalContext ctx(siginfo, uctx); - Printf("Tracer caught signal %d: addr=0x%zx pc=0x%zx sp=0x%zx\n", signum, - ctx.addr, ctx.pc, ctx.sp); + Printf("Tracer caught signal %d: addr=%p pc=%p sp=%p\n", signum, + (void *)ctx.addr, (void *)ctx.pc, (void *)ctx.sp); ThreadSuspender *inst = thread_suspender_instance; if (inst) { if (signum == SIGABRT) diff --git a/libsanitizer/sanitizer_common/sanitizer_suppressions.cpp b/libsanitizer/sanitizer_common/sanitizer_suppressions.cpp index f381852..62ebbb3 100644 --- a/libsanitizer/sanitizer_common/sanitizer_suppressions.cpp +++ b/libsanitizer/sanitizer_common/sanitizer_suppressions.cpp @@ -86,7 +86,7 @@ void SuppressionContext::ParseFromFile(const char *filename) { } Parse(file_contents); - UnmapOrDie(file_contents, contents_size); + UnmapOrDie(file_contents, buffer_size); } bool SuppressionContext::Match(const char *str, const char *type, @@ -138,7 +138,10 @@ void SuppressionContext::Parse(const char *str) { } } if (type == suppression_types_num_) { - Printf("%s: failed to parse suppressions\n", SanitizerToolName); + Printf("%s: failed to parse suppressions.\n", SanitizerToolName); + Printf("Supported suppression types are:\n"); + for (type = 0; type < suppression_types_num_; type++) + Printf("- %s\n", suppression_types_[type]); Die(); } Suppression s; diff --git a/libsanitizer/sanitizer_common/sanitizer_symbolizer.h b/libsanitizer/sanitizer_common/sanitizer_symbolizer.h index 7fb7928..bd89dc4 100644 --- a/libsanitizer/sanitizer_common/sanitizer_symbolizer.h +++ b/libsanitizer/sanitizer_common/sanitizer_symbolizer.h @@ -64,6 +64,26 @@ struct SymbolizedStack { SymbolizedStack(); }; +class SymbolizedStackHolder { + SymbolizedStack *Stack; + + void clear() { + if (Stack) + Stack->ClearAll(); + } + + public: + explicit SymbolizedStackHolder(SymbolizedStack *Stack = nullptr) + : Stack(Stack) {} + ~SymbolizedStackHolder() { clear(); } + void reset(SymbolizedStack *S = nullptr) { + if (Stack != S) + clear(); + Stack = S; + } + const SymbolizedStack *get() const { return Stack; } +}; + // For now, DataInfo is used to describe global variable. struct DataInfo { // Owns all the string members. Storage for them is @@ -154,6 +174,8 @@ class Symbolizer final { void InvalidateModuleList(); + const ListOfModules &GetRefreshedListOfModules(); + private: // GetModuleNameAndOffsetForPC has to return a string to the caller. // Since the corresponding module might get unloaded later, we should create @@ -163,17 +185,17 @@ class Symbolizer final { class ModuleNameOwner { public: explicit ModuleNameOwner(Mutex *synchronized_by) - : last_match_(nullptr), mu_(synchronized_by) { + : mu_(synchronized_by), last_match_(nullptr) { storage_.reserve(kInitialCapacity); } const char *GetOwnedCopy(const char *str); private: static const uptr kInitialCapacity = 1000; - InternalMmapVector<const char*> storage_; - const char *last_match_; Mutex *mu_; + const char *last_match_ SANITIZER_GUARDED_BY(mu_); + InternalMmapVector<const char *> storage_ SANITIZER_GUARDED_BY(*mu_); } module_names_; /// Platform-specific function for creating a Symbolizer object. @@ -198,7 +220,7 @@ class Symbolizer final { // always synchronized. Mutex mu_; - IntrusiveList<SymbolizerTool> tools_; + IntrusiveList<SymbolizerTool> tools_ SANITIZER_GUARDED_BY(mu_); explicit Symbolizer(IntrusiveList<SymbolizerTool> tools); diff --git a/libsanitizer/sanitizer_common/sanitizer_symbolizer_libcdep.cpp b/libsanitizer/sanitizer_common/sanitizer_symbolizer_libcdep.cpp index 8114102..7445802 100644 --- a/libsanitizer/sanitizer_common/sanitizer_symbolizer_libcdep.cpp +++ b/libsanitizer/sanitizer_common/sanitizer_symbolizer_libcdep.cpp @@ -191,6 +191,13 @@ void Symbolizer::RefreshModules() { modules_fresh_ = true; } +const ListOfModules &Symbolizer::GetRefreshedListOfModules() { + if (!modules_fresh_) + RefreshModules(); + + return modules_; +} + static const LoadedModule *SearchForModule(const ListOfModules &modules, uptr address) { for (uptr i = 0; i < modules.size(); i++) { diff --git a/libsanitizer/sanitizer_common/sanitizer_symbolizer_mac.cpp b/libsanitizer/sanitizer_common/sanitizer_symbolizer_mac.cpp index f1cc0b5..88536fc 100644 --- a/libsanitizer/sanitizer_common/sanitizer_symbolizer_mac.cpp +++ b/libsanitizer/sanitizer_common/sanitizer_symbolizer_mac.cpp @@ -30,7 +30,7 @@ namespace __sanitizer { bool DlAddrSymbolizer::SymbolizePC(uptr addr, SymbolizedStack *stack) { Dl_info info; int result = dladdr((const void *)addr, &info); - if (!result) return false; + if (!result || !info.dli_sname) return false; // Compute offset if possible. `dladdr()` doesn't always ensure that `addr >= // sym_addr` so only compute the offset when this holds. Failure to find the @@ -51,7 +51,7 @@ bool DlAddrSymbolizer::SymbolizePC(uptr addr, SymbolizedStack *stack) { bool DlAddrSymbolizer::SymbolizeData(uptr addr, DataInfo *datainfo) { Dl_info info; int result = dladdr((const void *)addr, &info); - if (!result) return false; + if (!result || !info.dli_sname) return false; const char *demangled = DemangleSwiftAndCXX(info.dli_sname); if (!demangled) demangled = info.dli_sname; diff --git a/libsanitizer/sanitizer_common/sanitizer_symbolizer_markup.cpp b/libsanitizer/sanitizer_common/sanitizer_symbolizer_markup.cpp index d1b0f46..31d91ef 100644 --- a/libsanitizer/sanitizer_common/sanitizer_symbolizer_markup.cpp +++ b/libsanitizer/sanitizer_common/sanitizer_symbolizer_markup.cpp @@ -8,151 +8,155 @@ // // This file is shared between various sanitizers' runtime libraries. // -// Implementation of offline markup symbolizer. +// This generic support for offline symbolizing is based on the +// Fuchsia port. We don't do any actual symbolization per se. +// Instead, we emit text containing raw addresses and raw linkage +// symbol names, embedded in Fuchsia's symbolization markup format. +// See the spec at: +// https://llvm.org/docs/SymbolizerMarkupFormat.html //===----------------------------------------------------------------------===// -#include "sanitizer_platform.h" -#if SANITIZER_SYMBOLIZER_MARKUP - -#if SANITIZER_FUCHSIA -#include "sanitizer_symbolizer_fuchsia.h" -# endif +#include "sanitizer_symbolizer_markup.h" -# include <limits.h> -# include <unwind.h> - -# include "sanitizer_stacktrace.h" -# include "sanitizer_stacktrace_printer.h" -# include "sanitizer_symbolizer.h" +#include "sanitizer_common.h" +#include "sanitizer_symbolizer.h" +#include "sanitizer_symbolizer_markup_constants.h" namespace __sanitizer { -// This generic support for offline symbolizing is based on the -// Fuchsia port. We don't do any actual symbolization per se. -// Instead, we emit text containing raw addresses and raw linkage -// symbol names, embedded in Fuchsia's symbolization markup format. -// Fuchsia's logging infrastructure emits enough information about -// process memory layout that a post-processing filter can do the -// symbolization and pretty-print the markup. See the spec at: -// https://fuchsia.googlesource.com/zircon/+/master/docs/symbolizer_markup.md - -// This is used by UBSan for type names, and by ASan for global variable names. -// It's expected to return a static buffer that will be reused on each call. -const char *Symbolizer::Demangle(const char *name) { - static char buffer[kFormatDemangleMax]; - internal_snprintf(buffer, sizeof(buffer), kFormatDemangle, name); - return buffer; +void MarkupStackTracePrinter::RenderData(InternalScopedString *buffer, + const char *format, const DataInfo *DI, + const char *strip_path_prefix) { + RenderContext(buffer); + buffer->AppendF(kFormatData, reinterpret_cast<void *>(DI->start)); } -// This is used mostly for suppression matching. Making it work -// would enable "interceptor_via_lib" suppressions. It's also used -// once in UBSan to say "in module ..." in a message that also -// includes an address in the module, so post-processing can already -// pretty-print that so as to indicate the module. -bool Symbolizer::GetModuleNameAndOffsetForPC(uptr pc, const char **module_name, - uptr *module_address) { +bool MarkupStackTracePrinter::RenderNeedsSymbolization(const char *format) { return false; } -// This is mainly used by hwasan for online symbolization. This isn't needed -// since hwasan can always just dump stack frames for offline symbolization. -bool Symbolizer::SymbolizeFrame(uptr addr, FrameInfo *info) { return false; } - -// This is used in some places for suppression checking, which we -// don't really support for Fuchsia. It's also used in UBSan to -// identify a PC location to a function name, so we always fill in -// the function member with a string containing markup around the PC -// value. -// TODO(mcgrathr): Under SANITIZER_GO, it's currently used by TSan -// to render stack frames, but that should be changed to use -// RenderStackFrame. -SymbolizedStack *Symbolizer::SymbolizePC(uptr addr) { - SymbolizedStack *s = SymbolizedStack::New(addr); +// We don't support the stack_trace_format flag at all. +void MarkupStackTracePrinter::RenderFrame(InternalScopedString *buffer, + const char *format, int frame_no, + uptr address, const AddressInfo *info, + bool vs_style, + const char *strip_path_prefix) { + CHECK(!RenderNeedsSymbolization(format)); + RenderContext(buffer); + buffer->AppendF(kFormatFrame, frame_no, reinterpret_cast<void *>(address)); +} + +bool MarkupSymbolizerTool::SymbolizePC(uptr addr, SymbolizedStack *stack) { char buffer[kFormatFunctionMax]; - internal_snprintf(buffer, sizeof(buffer), kFormatFunction, addr); - s->info.function = internal_strdup(buffer); - return s; + internal_snprintf(buffer, sizeof(buffer), kFormatFunction, + reinterpret_cast<void *>(addr)); + stack->info.function = internal_strdup(buffer); + return true; } -// Always claim we succeeded, so that RenderDataInfo will be called. -bool Symbolizer::SymbolizeData(uptr addr, DataInfo *info) { +bool MarkupSymbolizerTool::SymbolizeData(uptr addr, DataInfo *info) { info->Clear(); info->start = addr; return true; } -// We ignore the format argument to __sanitizer_symbolize_global. -void FormattedStackTracePrinter::RenderData(InternalScopedString *buffer, - const char *format, - const DataInfo *DI, - const char *strip_path_prefix) { - buffer->AppendF(kFormatData, DI->start); +const char *MarkupSymbolizerTool::Demangle(const char *name) { + static char buffer[kFormatDemangleMax]; + internal_snprintf(buffer, sizeof(buffer), kFormatDemangle, name); + return buffer; } -bool FormattedStackTracePrinter::RenderNeedsSymbolization(const char *format) { - return false; +// Fuchsia's implementation of symbolizer markup doesn't need to emit contextual +// elements at this point. +// Fuchsia's logging infrastructure emits enough information about +// process memory layout that a post-processing filter can do the +// symbolization and pretty-print the markup. +#if !SANITIZER_FUCHSIA + +static bool ModulesEq(const LoadedModule &module, + const RenderedModule &renderedModule) { + return module.base_address() == renderedModule.base_address && + internal_memcmp(module.uuid(), renderedModule.uuid, + module.uuid_size()) == 0 && + internal_strcmp(module.full_name(), renderedModule.full_name) == 0; } -// We don't support the stack_trace_format flag at all. -void FormattedStackTracePrinter::RenderFrame(InternalScopedString *buffer, - const char *format, int frame_no, - uptr address, - const AddressInfo *info, - bool vs_style, - const char *strip_path_prefix) { - CHECK(!RenderNeedsSymbolization(format)); - buffer->AppendF(kFormatFrame, frame_no, address); -} +static bool ModuleHasBeenRendered( + const LoadedModule &module, + const InternalMmapVectorNoCtor<RenderedModule> &renderedModules) { + for (const auto &renderedModule : renderedModules) + if (ModulesEq(module, renderedModule)) + return true; -Symbolizer *Symbolizer::PlatformInit() { - return new (symbolizer_allocator_) Symbolizer({}); + return false; } -void Symbolizer::LateInitialize() { Symbolizer::GetOrInit(); } - -void StartReportDeadlySignal() {} -void ReportDeadlySignal(const SignalContext &sig, u32 tid, - UnwindSignalStackCallbackType unwind, - const void *unwind_context) {} - -#if SANITIZER_CAN_SLOW_UNWIND -struct UnwindTraceArg { - BufferedStackTrace *stack; - u32 max_depth; -}; - -_Unwind_Reason_Code Unwind_Trace(struct _Unwind_Context *ctx, void *param) { - UnwindTraceArg *arg = static_cast<UnwindTraceArg *>(param); - CHECK_LT(arg->stack->size, arg->max_depth); - uptr pc = _Unwind_GetIP(ctx); - if (pc < PAGE_SIZE) return _URC_NORMAL_STOP; - arg->stack->trace_buffer[arg->stack->size++] = pc; - return (arg->stack->size == arg->max_depth ? _URC_NORMAL_STOP - : _URC_NO_REASON); +static void RenderModule(InternalScopedString *buffer, + const LoadedModule &module, uptr moduleId) { + InternalScopedString buildIdBuffer; + for (uptr i = 0; i < module.uuid_size(); i++) + buildIdBuffer.AppendF("%02x", module.uuid()[i]); + + buffer->AppendF(kFormatModule, moduleId, module.full_name(), + buildIdBuffer.data()); + buffer->Append("\n"); } -void BufferedStackTrace::UnwindSlow(uptr pc, u32 max_depth) { - CHECK_GE(max_depth, 2); - size = 0; - UnwindTraceArg arg = {this, Min(max_depth + 1, kStackTraceMax)}; - _Unwind_Backtrace(Unwind_Trace, &arg); - CHECK_GT(size, 0); - // We need to pop a few frames so that pc is on top. - uptr to_pop = LocatePcInTrace(pc); - // trace_buffer[0] belongs to the current function so we always pop it, - // unless there is only 1 frame in the stack trace (1 frame is always better - // than 0!). - PopStackFrames(Min(to_pop, static_cast<uptr>(1))); - trace_buffer[0] = pc; +static void RenderMmaps(InternalScopedString *buffer, + const LoadedModule &module, uptr moduleId) { + InternalScopedString accessBuffer; + + // All module mmaps are readable at least + for (const auto &range : module.ranges()) { + accessBuffer.Append("r"); + if (range.writable) + accessBuffer.Append("w"); + if (range.executable) + accessBuffer.Append("x"); + + //{{{mmap:%starting_addr:%size_in_hex:load:%moduleId:r%(w|x):%relative_addr}}} + + // module.base_address == dlpi_addr + // range.beg == dlpi_addr + p_vaddr + // relative address == p_vaddr == range.beg - module.base_address + buffer->AppendF(kFormatMmap, reinterpret_cast<void *>(range.beg), + range.end - range.beg, static_cast<int>(moduleId), + accessBuffer.data(), range.beg - module.base_address()); + + buffer->Append("\n"); + accessBuffer.clear(); + } } -void BufferedStackTrace::UnwindSlow(uptr pc, void *context, u32 max_depth) { - CHECK(context); - CHECK_GE(max_depth, 2); - UNREACHABLE("signal context doesn't exist"); +void MarkupStackTracePrinter::RenderContext(InternalScopedString *buffer) { + if (renderedModules_.size() == 0) + buffer->Append("{{{reset}}}\n"); + + const auto &modules = Symbolizer::GetOrInit()->GetRefreshedListOfModules(); + + for (const auto &module : modules) { + if (ModuleHasBeenRendered(module, renderedModules_)) + continue; + + // symbolizer markup id, used to refer to this modules from other contextual + // elements + uptr moduleId = renderedModules_.size(); + + RenderModule(buffer, module, moduleId); + RenderMmaps(buffer, module, moduleId); + + renderedModules_.push_back({ + internal_strdup(module.full_name()), + module.base_address(), + {}, + }); + + // kModuleUUIDSize is the size of curModule.uuid + CHECK_GE(kModuleUUIDSize, module.uuid_size()); + internal_memcpy(renderedModules_.back().uuid, module.uuid(), + module.uuid_size()); + } } -#endif // SANITIZER_CAN_SLOW_UNWIND +#endif // !SANITIZER_FUCHSIA } // namespace __sanitizer - -#endif // SANITIZER_SYMBOLIZER_MARKUP diff --git a/libsanitizer/sanitizer_common/sanitizer_symbolizer_markup.h b/libsanitizer/sanitizer_common/sanitizer_symbolizer_markup.h new file mode 100644 index 0000000..bc2ab24 --- /dev/null +++ b/libsanitizer/sanitizer_common/sanitizer_symbolizer_markup.h @@ -0,0 +1,79 @@ +//===-- sanitizer_symbolizer_markup.h -----------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This file is shared between various sanitizers' runtime libraries. +// +// Header for the offline markup symbolizer. +//===----------------------------------------------------------------------===// +#ifndef SANITIZER_SYMBOLIZER_MARKUP_H +#define SANITIZER_SYMBOLIZER_MARKUP_H + +#include "sanitizer_common.h" +#include "sanitizer_stacktrace_printer.h" +#include "sanitizer_symbolizer.h" +#include "sanitizer_symbolizer_internal.h" + +namespace __sanitizer { + +// Simplier view of a LoadedModule. It only holds information necessary to +// identify unique modules. +struct RenderedModule { + char *full_name; + uptr base_address; + u8 uuid[kModuleUUIDSize]; // BuildId +}; + +class MarkupStackTracePrinter : public StackTracePrinter { + public: + // We don't support the stack_trace_format flag at all. + void RenderFrame(InternalScopedString *buffer, const char *format, + int frame_no, uptr address, const AddressInfo *info, + bool vs_style, const char *strip_path_prefix = "") override; + + bool RenderNeedsSymbolization(const char *format) override; + + // We ignore the format argument to __sanitizer_symbolize_global. + void RenderData(InternalScopedString *buffer, const char *format, + const DataInfo *DI, + const char *strip_path_prefix = "") override; + + private: + // Keeps track of the modules that have been rendered to avoid re-rendering + // them + InternalMmapVector<RenderedModule> renderedModules_; + void RenderContext(InternalScopedString *buffer); + + protected: + ~MarkupStackTracePrinter() {} +}; + +class MarkupSymbolizerTool final : public SymbolizerTool { + public: + // This is used in some places for suppression checking, which we + // don't really support for Fuchsia. It's also used in UBSan to + // identify a PC location to a function name, so we always fill in + // the function member with a string containing markup around the PC + // value. + // TODO(mcgrathr): Under SANITIZER_GO, it's currently used by TSan + // to render stack frames, but that should be changed to use + // RenderStackFrame. + bool SymbolizePC(uptr addr, SymbolizedStack *stack) override; + + // Always claim we succeeded, so that RenderDataInfo will be called. + bool SymbolizeData(uptr addr, DataInfo *info) override; + + // May return NULL if demangling failed. + // This is used by UBSan for type names, and by ASan for global variable + // names. It's expected to return a static buffer that will be reused on each + // call. + const char *Demangle(const char *name) override; +}; + +} // namespace __sanitizer + +#endif // SANITIZER_SYMBOLIZER_MARKUP_H diff --git a/libsanitizer/sanitizer_common/sanitizer_symbolizer_fuchsia.h b/libsanitizer/sanitizer_common/sanitizer_symbolizer_markup_constants.h index c4061e3..a43661e 100644 --- a/libsanitizer/sanitizer_common/sanitizer_symbolizer_fuchsia.h +++ b/libsanitizer/sanitizer_common/sanitizer_symbolizer_markup_constants.h @@ -1,4 +1,5 @@ -//===-- sanitizer_symbolizer_fuchsia.h -----------------------------------===// +//===-- sanitizer_symbolizer_markup_constants.h +//-----------------------------------===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. @@ -8,10 +9,10 @@ // // This file is shared between various sanitizers' runtime libraries. // -// Define Fuchsia's string formats and limits for the markup symbolizer. +// Define string formats and limits for the markup symbolizer. //===----------------------------------------------------------------------===// -#ifndef SANITIZER_SYMBOLIZER_FUCHSIA_H -#define SANITIZER_SYMBOLIZER_FUCHSIA_H +#ifndef SANITIZER_SYMBOLIZER_MARKUP_CONSTANTS_H +#define SANITIZER_SYMBOLIZER_MARKUP_CONSTANTS_H #include "sanitizer_internal_defs.h" @@ -32,11 +33,17 @@ constexpr uptr kFormatFunctionMax = 64; // More than big enough for 64-bit hex. constexpr const char *kFormatData = "{{{data:%p}}}"; // One frame in a backtrace (printed on a line by itself). -constexpr const char *kFormatFrame = "{{{bt:%u:%p}}}"; +constexpr const char *kFormatFrame = "{{{bt:%d:%p}}}"; + +// Module contextual element. +constexpr const char *kFormatModule = "{{{module:%zu:%s:elf:%s}}}"; + +// mmap for a module segment. +constexpr const char *kFormatMmap = "{{{mmap:%p:0x%zx:load:%d:%s:0x%zx}}}"; // Dump trigger element. #define FORMAT_DUMPFILE "{{{dumpfile:%s:%s}}}" } // namespace __sanitizer -#endif // SANITIZER_SYMBOLIZER_FUCHSIA_H +#endif // SANITIZER_SYMBOLIZER_MARKUP_CONSTANTS_H diff --git a/libsanitizer/sanitizer_common/sanitizer_symbolizer_markup_fuchsia.cpp b/libsanitizer/sanitizer_common/sanitizer_symbolizer_markup_fuchsia.cpp new file mode 100644 index 0000000..08b06c2 --- /dev/null +++ b/libsanitizer/sanitizer_common/sanitizer_symbolizer_markup_fuchsia.cpp @@ -0,0 +1,85 @@ +//===-- sanitizer_symbolizer_markup_fuchsia.cpp ---------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This file is shared between various sanitizers' runtime libraries. +// +// Fuchsia specific implementation of offline markup symbolizer. +//===----------------------------------------------------------------------===// +#include "sanitizer_platform.h" + +#if SANITIZER_SYMBOLIZER_MARKUP + +# include "sanitizer_common.h" +# include "sanitizer_stacktrace_printer.h" +# include "sanitizer_symbolizer.h" +# include "sanitizer_symbolizer_markup.h" +# include "sanitizer_symbolizer_markup_constants.h" + +namespace __sanitizer { + +// This is used by UBSan for type names, and by ASan for global variable names. +// It's expected to return a static buffer that will be reused on each call. +const char *Symbolizer::Demangle(const char *name) { + static char buffer[kFormatDemangleMax]; + internal_snprintf(buffer, sizeof(buffer), kFormatDemangle, name); + return buffer; +} + +// This is used mostly for suppression matching. Making it work +// would enable "interceptor_via_lib" suppressions. It's also used +// once in UBSan to say "in module ..." in a message that also +// includes an address in the module, so post-processing can already +// pretty-print that so as to indicate the module. +bool Symbolizer::GetModuleNameAndOffsetForPC(uptr pc, const char **module_name, + uptr *module_address) { + return false; +} + +// This is mainly used by hwasan for online symbolization. This isn't needed +// since hwasan can always just dump stack frames for offline symbolization. +bool Symbolizer::SymbolizeFrame(uptr addr, FrameInfo *info) { return false; } + +// This is used in some places for suppression checking, which we +// don't really support for Fuchsia. It's also used in UBSan to +// identify a PC location to a function name, so we always fill in +// the function member with a string containing markup around the PC +// value. +// TODO(mcgrathr): Under SANITIZER_GO, it's currently used by TSan +// to render stack frames, but that should be changed to use +// RenderStackFrame. +SymbolizedStack *Symbolizer::SymbolizePC(uptr addr) { + SymbolizedStack *s = SymbolizedStack::New(addr); + char buffer[kFormatFunctionMax]; + internal_snprintf(buffer, sizeof(buffer), kFormatFunction, addr); + s->info.function = internal_strdup(buffer); + return s; +} + +// Always claim we succeeded, so that RenderDataInfo will be called. +bool Symbolizer::SymbolizeData(uptr addr, DataInfo *info) { + info->Clear(); + info->start = addr; + return true; +} + +// Fuchsia only uses MarkupStackTracePrinter +StackTracePrinter *StackTracePrinter::NewStackTracePrinter() { + return new (GetGlobalLowLevelAllocator()) MarkupStackTracePrinter(); +} + +void MarkupStackTracePrinter::RenderContext(InternalScopedString *) {} + +Symbolizer *Symbolizer::PlatformInit() { + return new (symbolizer_allocator_) Symbolizer({}); +} + +void Symbolizer::LateInitialize() { Symbolizer::GetOrInit(); } + +} // namespace __sanitizer + +#endif // SANITIZER_SYMBOLIZER_MARKUP diff --git a/libsanitizer/sanitizer_common/sanitizer_symbolizer_posix_libcdep.cpp b/libsanitizer/sanitizer_common/sanitizer_symbolizer_posix_libcdep.cpp index d92349c..0ddc248 100644 --- a/libsanitizer/sanitizer_common/sanitizer_symbolizer_posix_libcdep.cpp +++ b/libsanitizer/sanitizer_common/sanitizer_symbolizer_posix_libcdep.cpp @@ -12,6 +12,7 @@ //===----------------------------------------------------------------------===// #include "sanitizer_platform.h" +#include "sanitizer_symbolizer_markup.h" #if SANITIZER_POSIX # include <dlfcn.h> // for dlsym() # include <errno.h> @@ -340,15 +341,14 @@ __sanitizer_symbolize_set_inline_frames(bool InlineFrames); class InternalSymbolizer final : public SymbolizerTool { public: static InternalSymbolizer *get(LowLevelAllocator *alloc) { - if (&__sanitizer_symbolize_set_demangle) - CHECK(__sanitizer_symbolize_set_demangle(common_flags()->demangle)); - if (&__sanitizer_symbolize_set_inline_frames) - CHECK(__sanitizer_symbolize_set_inline_frames( - common_flags()->symbolize_inline_frames)); - // These are essential, we don't have InternalSymbolizer without them. - if (&__sanitizer_symbolize_code && &__sanitizer_symbolize_data) - return new (*alloc) InternalSymbolizer(); - return 0; + // These one is the most used one, so we will use it to detect a presence of + // internal symbolizer. + if (&__sanitizer_symbolize_code == nullptr) + return nullptr; + CHECK(__sanitizer_symbolize_set_demangle(common_flags()->demangle)); + CHECK(__sanitizer_symbolize_set_inline_frames( + common_flags()->symbolize_inline_frames)); + return new (*alloc) InternalSymbolizer(); } bool SymbolizePC(uptr addr, SymbolizedStack *stack) override { @@ -370,8 +370,6 @@ class InternalSymbolizer final : public SymbolizerTool { } bool SymbolizeFrame(uptr addr, FrameInfo *info) override { - if (&__sanitizer_symbolize_frame == nullptr) - return false; bool result = __sanitizer_symbolize_frame(info->module, info->module_offset, buffer_, sizeof(buffer_)); if (result) @@ -379,14 +377,10 @@ class InternalSymbolizer final : public SymbolizerTool { return result; } - void Flush() override { - if (&__sanitizer_symbolize_flush) - __sanitizer_symbolize_flush(); - } + void Flush() override { __sanitizer_symbolize_flush(); } const char *Demangle(const char *name) override { - if (&__sanitizer_symbolize_demangle && - __sanitizer_symbolize_demangle(name, buffer_, sizeof(buffer_))) { + if (__sanitizer_symbolize_demangle(name, buffer_, sizeof(buffer_))) { char *res_buff = nullptr; ExtractToken(buffer_, "", &res_buff); return res_buff; @@ -475,6 +469,12 @@ static void ChooseSymbolizerTools(IntrusiveList<SymbolizerTool> *list, VReport(2, "Symbolizer is disabled.\n"); return; } + if (common_flags()->enable_symbolizer_markup) { + VReport(2, "Using symbolizer markup"); + SymbolizerTool *tool = new (*allocator) MarkupSymbolizerTool(); + CHECK(tool); + list->push_back(tool); + } if (IsAllocatorOutOfMemory()) { VReport(2, "Cannot use internal symbolizer: out of memory\n"); } else if (SymbolizerTool *tool = InternalSymbolizer::get(allocator)) { diff --git a/libsanitizer/sanitizer_common/sanitizer_symbolizer_report.cpp b/libsanitizer/sanitizer_common/sanitizer_symbolizer_report.cpp index 3e4417a..351e00d 100644 --- a/libsanitizer/sanitizer_common/sanitizer_symbolizer_report.cpp +++ b/libsanitizer/sanitizer_common/sanitizer_symbolizer_report.cpp @@ -28,6 +28,33 @@ namespace __sanitizer { #if !SANITIZER_GO + +static bool FrameIsInternal(const SymbolizedStack *frame) { + if (!frame) + return true; + const char *file = frame->info.file; + const char *module = frame->info.module; + // On Gentoo, the path is g++-*, so there's *not* a missing /. + if (file && (internal_strstr(file, "/compiler-rt/lib/") || + internal_strstr(file, "/include/c++/") || + internal_strstr(file, "/include/g++"))) + return true; + if (file && internal_strstr(file, "\\compiler-rt\\lib\\")) + return true; + if (module && (internal_strstr(module, "libclang_rt."))) + return true; + if (module && (internal_strstr(module, "clang_rt."))) + return true; + return false; +} + +const SymbolizedStack *SkipInternalFrames(const SymbolizedStack *frames) { + for (const SymbolizedStack *f = frames; f; f = f->next) + if (!FrameIsInternal(f)) + return f; + return nullptr; +} + void ReportErrorSummary(const char *error_type, const AddressInfo &info, const char *alt_tool_name) { if (!common_flags()->print_summary) return; @@ -75,16 +102,33 @@ void ReportErrorSummary(const char *error_type, const StackTrace *stack, #if !SANITIZER_GO if (!common_flags()->print_summary) return; - if (stack->size == 0) { - ReportErrorSummary(error_type); - return; + + // Find first non-internal stack frame. + for (uptr i = 0; i < stack->size; ++i) { + uptr pc = StackTrace::GetPreviousInstructionPc(stack->trace[i]); + SymbolizedStackHolder symbolized_stack( + Symbolizer::GetOrInit()->SymbolizePC(pc)); + if (const SymbolizedStack *frame = symbolized_stack.get()) { + if (const SymbolizedStack *summary_frame = SkipInternalFrames(frame)) { + ReportErrorSummary(error_type, summary_frame->info, alt_tool_name); + return; + } + } } - // Currently, we include the first stack frame into the report summary. - // Maybe sometimes we need to choose another frame (e.g. skip memcpy/etc). - uptr pc = StackTrace::GetPreviousInstructionPc(stack->trace[0]); - SymbolizedStack *frame = Symbolizer::GetOrInit()->SymbolizePC(pc); - ReportErrorSummary(error_type, frame->info, alt_tool_name); - frame->ClearAll(); + + // Fallback to the top one. + if (stack->size) { + uptr pc = StackTrace::GetPreviousInstructionPc(stack->trace[0]); + SymbolizedStackHolder symbolized_stack( + Symbolizer::GetOrInit()->SymbolizePC(pc)); + if (const SymbolizedStack *frame = symbolized_stack.get()) { + ReportErrorSummary(error_type, frame->info, alt_tool_name); + return; + } + } + + // Fallback to a summary without location. + ReportErrorSummary(error_type); #endif } @@ -183,12 +227,15 @@ static void ReportStackOverflowImpl(const SignalContext &sig, u32 tid, SanitizerToolName, kDescription, (void *)sig.addr, (void *)sig.pc, (void *)sig.bp, (void *)sig.sp, tid); Printf("%s", d.Default()); - InternalMmapVector<BufferedStackTrace> stack_buffer(1); - BufferedStackTrace *stack = stack_buffer.data(); - stack->Reset(); - unwind(sig, unwind_context, stack); - stack->Print(); - ReportErrorSummary(kDescription, stack); + // Avoid SEGVs in the unwinder when bp couldn't be determined. + if (sig.bp) { + InternalMmapVector<BufferedStackTrace> stack_buffer(1); + BufferedStackTrace *stack = stack_buffer.data(); + stack->Reset(); + unwind(sig, unwind_context, stack); + stack->Print(); + ReportErrorSummary(kDescription, stack); + } } static void ReportDeadlySignalImpl(const SignalContext &sig, u32 tid, diff --git a/libsanitizer/sanitizer_common/sanitizer_symbolizer_report_fuchsia.cpp b/libsanitizer/sanitizer_common/sanitizer_symbolizer_report_fuchsia.cpp new file mode 100644 index 0000000..fb49cfb --- /dev/null +++ b/libsanitizer/sanitizer_common/sanitizer_symbolizer_report_fuchsia.cpp @@ -0,0 +1,33 @@ +//===-- sanitizer_symbolizer_report_fuchsia.cpp +//-----------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// Implementation of the report functions for fuchsia. +// +//===----------------------------------------------------------------------===// + +#include "sanitizer_platform.h" + +#if SANITIZER_SYMBOLIZER_MARKUP + +# include "sanitizer_common.h" + +namespace __sanitizer { +void StartReportDeadlySignal() {} + +void ReportDeadlySignal(const SignalContext &sig, u32 tid, + UnwindSignalStackCallbackType unwind, + const void *unwind_context) {} + +void HandleDeadlySignal(void *siginfo, void *context, u32 tid, + UnwindSignalStackCallbackType unwind, + const void *unwind_context) {} + +} // namespace __sanitizer + +#endif // SANITIZER_SYMBOLIZER_MARKUP diff --git a/libsanitizer/sanitizer_common/sanitizer_symbolizer_win.cpp b/libsanitizer/sanitizer_common/sanitizer_symbolizer_win.cpp index aae3e76..1ff8b8f 100644 --- a/libsanitizer/sanitizer_common/sanitizer_symbolizer_win.cpp +++ b/libsanitizer/sanitizer_common/sanitizer_symbolizer_win.cpp @@ -65,12 +65,13 @@ void InitializeDbgHelpIfNeeded() { HMODULE dbghelp = LoadLibraryA("dbghelp.dll"); CHECK(dbghelp && "failed to load dbghelp.dll"); -#define DBGHELP_IMPORT(name) \ - do { \ - name = \ - reinterpret_cast<decltype(::name) *>(GetProcAddress(dbghelp, #name)); \ - CHECK(name != nullptr); \ - } while (0) +# define DBGHELP_IMPORT(name) \ + do { \ + name = reinterpret_cast<decltype(::name) *>( \ + (void *)GetProcAddress(dbghelp, #name)); \ + CHECK(name != nullptr); \ + } while (0) + DBGHELP_IMPORT(StackWalk64); DBGHELP_IMPORT(SymCleanup); DBGHELP_IMPORT(SymFromAddr); diff --git a/libsanitizer/sanitizer_common/sanitizer_thread_arg_retval.cpp b/libsanitizer/sanitizer_common/sanitizer_thread_arg_retval.cpp index bddb285..754fd7b 100644 --- a/libsanitizer/sanitizer_common/sanitizer_thread_arg_retval.cpp +++ b/libsanitizer/sanitizer_common/sanitizer_thread_arg_retval.cpp @@ -23,6 +23,9 @@ void ThreadArgRetval::CreateLocked(uptr thread, bool detached, Data& t = data_[thread]; t = {}; t.gen = gen_++; + static_assert(sizeof(gen_) == sizeof(u32) && kInvalidGen == UINT32_MAX); + if (gen_ == kInvalidGen) + gen_ = 0; t.detached = detached; t.args = args; } @@ -53,16 +56,28 @@ void ThreadArgRetval::Finish(uptr thread, void* retval) { u32 ThreadArgRetval::BeforeJoin(uptr thread) const { __sanitizer::Lock lock(&mtx_); auto t = data_.find(thread); - CHECK(t); - CHECK(!t->second.detached); - return t->second.gen; + if (t && !t->second.detached) { + return t->second.gen; + } + if (!common_flags()->detect_invalid_join) + return kInvalidGen; + const char* reason = "unknown"; + if (!t) { + reason = "already joined"; + } else if (t->second.detached) { + reason = "detached"; + } + Report("ERROR: %s: Joining %s thread, aborting.\n", SanitizerToolName, + reason); + Die(); } void ThreadArgRetval::AfterJoin(uptr thread, u32 gen) { __sanitizer::Lock lock(&mtx_); auto t = data_.find(thread); if (!t || gen != t->second.gen) { - // Thread was reused and erased by any other event. + // Thread was reused and erased by any other event, or we had an invalid + // join. return; } CHECK(!t->second.detached); diff --git a/libsanitizer/sanitizer_common/sanitizer_thread_arg_retval.h b/libsanitizer/sanitizer_common/sanitizer_thread_arg_retval.h index c77021b..0e6d351 100644 --- a/libsanitizer/sanitizer_common/sanitizer_thread_arg_retval.h +++ b/libsanitizer/sanitizer_common/sanitizer_thread_arg_retval.h @@ -93,6 +93,7 @@ class SANITIZER_MUTEX ThreadArgRetval { // will keep pointers alive forever, missing leaks caused by cancelation. private: + static const u32 kInvalidGen = UINT32_MAX; struct Data { Args args; u32 gen; // Avoid collision if thread id re-used. diff --git a/libsanitizer/sanitizer_common/sanitizer_thread_history.cpp b/libsanitizer/sanitizer_common/sanitizer_thread_history.cpp new file mode 100644 index 0000000..0f5bec3 --- /dev/null +++ b/libsanitizer/sanitizer_common/sanitizer_thread_history.cpp @@ -0,0 +1,72 @@ +//===-- sanitizer_thread_history.cpp --------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "sanitizer_thread_history.h" + +#include "sanitizer_stackdepot.h" +namespace __sanitizer { + +void PrintThreadHistory(ThreadRegistry ®istry, InternalScopedString &out) { + ThreadRegistryLock l(®istry); + // Stack traces are largest part of printout and they often the same for + // multiple threads, so we will deduplicate them. + InternalMmapVector<const ThreadContextBase *> stacks; + + registry.RunCallbackForEachThreadLocked( + [](ThreadContextBase *context, void *arg) { + static_cast<decltype(&stacks)>(arg)->push_back(context); + }, + &stacks); + + Sort(stacks.data(), stacks.size(), + [](const ThreadContextBase *a, const ThreadContextBase *b) { + if (a->stack_id < b->stack_id) + return true; + if (a->stack_id > b->stack_id) + return false; + return a->unique_id < b->unique_id; + }); + + auto describe_thread = [&](const ThreadContextBase *context) { + if (!context) { + out.Append("T-1"); + return; + } + out.AppendF("T%llu/%llu", context->unique_id, context->os_id); + if (internal_strlen(context->name)) + out.AppendF(" (%s)", context->name); + }; + + auto get_parent = + [&](const ThreadContextBase *context) -> const ThreadContextBase * { + if (!context) + return nullptr; + ThreadContextBase *parent = registry.GetThreadLocked(context->parent_tid); + if (!parent) + return nullptr; + if (parent->unique_id >= context->unique_id) + return nullptr; + return parent; + }; + + const ThreadContextBase *prev = nullptr; + for (const ThreadContextBase *context : stacks) { + if (prev && prev->stack_id != context->stack_id) + StackDepotGet(prev->stack_id).PrintTo(&out); + prev = context; + out.Append("Thread "); + describe_thread(context); + out.Append(" was created by "); + describe_thread(get_parent(context)); + out.Append("\n"); + } + if (prev) + StackDepotGet(prev->stack_id).PrintTo(&out); +} + +} // namespace __sanitizer diff --git a/libsanitizer/sanitizer_common/sanitizer_thread_history.h b/libsanitizer/sanitizer_common/sanitizer_thread_history.h new file mode 100644 index 0000000..2995f60 --- /dev/null +++ b/libsanitizer/sanitizer_common/sanitizer_thread_history.h @@ -0,0 +1,24 @@ +//===-- sanitizer_thread_history.h ------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// Utility to print thread histroy from ThreadRegistry. +// +//===----------------------------------------------------------------------===// + +#ifndef SANITIZER_THREAD_HISTORY_H +#define SANITIZER_THREAD_HISTORY_H + +#include "sanitizer_thread_registry.h" + +namespace __sanitizer { + +void PrintThreadHistory(ThreadRegistry& registry, InternalScopedString& out); + +} // namespace __sanitizer + +#endif // SANITIZER_THREAD_HISTORY_H diff --git a/libsanitizer/sanitizer_common/sanitizer_thread_registry.cpp b/libsanitizer/sanitizer_common/sanitizer_thread_registry.cpp index 741e073..cdc24f4 100644 --- a/libsanitizer/sanitizer_common/sanitizer_thread_registry.cpp +++ b/libsanitizer/sanitizer_common/sanitizer_thread_registry.cpp @@ -18,9 +18,17 @@ namespace __sanitizer { ThreadContextBase::ThreadContextBase(u32 tid) - : tid(tid), unique_id(0), reuse_count(), os_id(0), user_id(0), - status(ThreadStatusInvalid), detached(false), - thread_type(ThreadType::Regular), parent_tid(0), next(0) { + : tid(tid), + unique_id(0), + reuse_count(), + os_id(0), + user_id(0), + status(ThreadStatusInvalid), + detached(false), + thread_type(ThreadType::Regular), + parent_tid(0), + stack_id(0), + next(0) { name[0] = '\0'; atomic_store(&thread_destroyed, 0, memory_order_release); } @@ -39,8 +47,7 @@ void ThreadContextBase::SetName(const char *new_name) { } void ThreadContextBase::SetDead() { - CHECK(status == ThreadStatusRunning || - status == ThreadStatusFinished); + CHECK(status == ThreadStatusRunning || status == ThreadStatusFinished); status = ThreadStatusDead; user_id = 0; OnDead(); @@ -68,7 +75,8 @@ void ThreadContextBase::SetFinished() { // for a thread that never actually started. In that case the thread // should go to ThreadStatusFinished regardless of whether it was created // as detached. - if (!detached || status == ThreadStatusCreated) status = ThreadStatusFinished; + if (!detached || status == ThreadStatusCreated) + status = ThreadStatusFinished; OnFinished(); } @@ -81,14 +89,17 @@ void ThreadContextBase::SetStarted(tid_t _os_id, ThreadType _thread_type, } void ThreadContextBase::SetCreated(uptr _user_id, u64 _unique_id, - bool _detached, u32 _parent_tid, void *arg) { + bool _detached, u32 _parent_tid, + u32 _stack_tid, void *arg) { status = ThreadStatusCreated; user_id = _user_id; unique_id = _unique_id; detached = _detached; // Parent tid makes no sense for the main thread. - if (tid != kMainTid) + if (tid != kMainTid) { parent_tid = _parent_tid; + stack_id = _stack_tid; + } OnCreated(arg); } @@ -124,8 +135,10 @@ void ThreadRegistry::GetNumberOfThreads(uptr *total, uptr *running, ThreadRegistryLock l(this); if (total) *total = threads_.size(); - if (running) *running = running_threads_; - if (alive) *alive = alive_threads_; + if (running) + *running = running_threads_; + if (alive) + *alive = alive_threads_; } uptr ThreadRegistry::GetMaxAliveThreads() { @@ -134,7 +147,7 @@ uptr ThreadRegistry::GetMaxAliveThreads() { } u32 ThreadRegistry::CreateThread(uptr user_id, bool detached, u32 parent_tid, - void *arg) { + u32 stack_tid, void *arg) { ThreadRegistryLock l(this); u32 tid = kInvalidTid; ThreadContextBase *tctx = QuarantinePop(); @@ -150,8 +163,10 @@ u32 ThreadRegistry::CreateThread(uptr user_id, bool detached, u32 parent_tid, Report("%s: Thread limit (%u threads) exceeded. Dying.\n", SanitizerToolName, max_threads_); #else - Printf("race: limit on %u simultaneously alive goroutines is exceeded," - " dying\n", max_threads_); + Printf( + "race: limit on %u simultaneously alive goroutines is exceeded," + " dying\n", + max_threads_); #endif Die(); } @@ -170,8 +185,8 @@ u32 ThreadRegistry::CreateThread(uptr user_id, bool detached, u32 parent_tid, // positives later (e.g. if we join a wrong thread). CHECK(live_.try_emplace(user_id, tid).second); } - tctx->SetCreated(user_id, total_threads_++, detached, - parent_tid, arg); + tctx->SetCreated(user_id, total_threads_++, detached, parent_tid, stack_tid, + arg); return tid; } @@ -196,8 +211,8 @@ u32 ThreadRegistry::FindThread(FindThreadCallback cb, void *arg) { return kInvalidTid; } -ThreadContextBase * -ThreadRegistry::FindThreadContextLocked(FindThreadCallback cb, void *arg) { +ThreadContextBase *ThreadRegistry::FindThreadContextLocked( + FindThreadCallback cb, void *arg) { CheckLocked(); for (u32 tid = 0; tid < threads_.size(); tid++) { ThreadContextBase *tctx = threads_[tid]; @@ -210,7 +225,7 @@ ThreadRegistry::FindThreadContextLocked(FindThreadCallback cb, void *arg) { static bool FindThreadContextByOsIdCallback(ThreadContextBase *tctx, void *arg) { return (tctx->os_id == (uptr)arg && tctx->status != ThreadStatusInvalid && - tctx->status != ThreadStatusDead); + tctx->status != ThreadStatusDead); } ThreadContextBase *ThreadRegistry::FindThreadContextByOsIDLocked(tid_t os_id) { diff --git a/libsanitizer/sanitizer_common/sanitizer_thread_registry.h b/libsanitizer/sanitizer_common/sanitizer_thread_registry.h index 2c7e5c2..e06abb3 100644 --- a/libsanitizer/sanitizer_common/sanitizer_thread_registry.h +++ b/libsanitizer/sanitizer_common/sanitizer_thread_registry.h @@ -52,6 +52,7 @@ class ThreadContextBase { ThreadType thread_type; u32 parent_tid; + u32 stack_id; ThreadContextBase *next; // For storing thread contexts in a list. atomic_uint32_t thread_destroyed; // To address race of Joined vs Finished @@ -63,7 +64,7 @@ class ThreadContextBase { void SetFinished(); void SetStarted(tid_t _os_id, ThreadType _thread_type, void *arg); void SetCreated(uptr _user_id, u64 _unique_id, bool _detached, - u32 _parent_tid, void *arg); + u32 _parent_tid, u32 _stack_tid, void *arg); void Reset(); void SetDestroyed(); @@ -101,12 +102,16 @@ class SANITIZER_MUTEX ThreadRegistry { // Should be guarded by ThreadRegistryLock. ThreadContextBase *GetThreadLocked(u32 tid) { - return threads_.empty() ? nullptr : threads_[tid]; + return tid < threads_.size() ? threads_[tid] : nullptr; } u32 NumThreadsLocked() const { return threads_.size(); } - u32 CreateThread(uptr user_id, bool detached, u32 parent_tid, void *arg); + u32 CreateThread(uptr user_id, bool detached, u32 parent_tid, u32 stack_tid, + void *arg); + u32 CreateThread(uptr user_id, bool detached, u32 parent_tid, void *arg) { + return CreateThread(user_id, detached, parent_tid, 0, arg); + } typedef void (*ThreadCallback)(ThreadContextBase *tctx, void *arg); // Invokes callback with a specified arg for each thread context. diff --git a/libsanitizer/sanitizer_common/sanitizer_tls_get_addr.cpp b/libsanitizer/sanitizer_common/sanitizer_tls_get_addr.cpp index 252979f..e08bcb4 100644 --- a/libsanitizer/sanitizer_common/sanitizer_tls_get_addr.cpp +++ b/libsanitizer/sanitizer_common/sanitizer_tls_get_addr.cpp @@ -14,6 +14,8 @@ #include "sanitizer_allocator_interface.h" #include "sanitizer_atomic.h" +#include "sanitizer_common/sanitizer_common.h" +#include "sanitizer_common/sanitizer_internal_defs.h" #include "sanitizer_flags.h" #include "sanitizer_platform_interceptors.h" @@ -66,7 +68,7 @@ static DTLS::DTVBlock *DTLS_NextBlock(atomic_uintptr_t *cur) { } static DTLS::DTV *DTLS_Find(uptr id) { - VReport(2, "__tls_get_addr: DTLS_Find %p %zd\n", (void *)&dtls, id); + VReport(3, "__tls_get_addr: DTLS_Find %p %zd\n", (void *)&dtls, id); static constexpr uptr kPerBlock = ARRAY_SIZE(DTLS::DTVBlock::dtvs); DTLS::DTVBlock *cur = DTLS_NextBlock(&dtls.dtv_block); if (!cur) @@ -110,6 +112,21 @@ SANITIZER_WEAK_ATTRIBUTE const void *__sanitizer_get_allocated_begin(const void *p); } +SANITIZER_INTERFACE_WEAK_DEF(uptr, __sanitizer_get_dtls_size, + const void *tls_begin) { + const void *start = __sanitizer_get_allocated_begin(tls_begin); + if (!start) + return 0; + CHECK_LE(start, tls_begin); + uptr tls_size = __sanitizer_get_allocated_size(start); + VReport(2, "__tls_get_addr: glibc DTLS suspected; tls={%p,0x%zx}\n", + tls_begin, tls_size); + uptr offset = + (reinterpret_cast<uptr>(tls_begin) - reinterpret_cast<uptr>(start)); + CHECK_LE(offset, tls_size); + return tls_size - offset; +} + DTLS::DTV *DTLS_on_tls_get_addr(void *arg_void, void *res, uptr static_tls_begin, uptr static_tls_end) { if (!common_flags()->intercept_tls_get_addr) return 0; @@ -117,44 +134,42 @@ DTLS::DTV *DTLS_on_tls_get_addr(void *arg_void, void *res, uptr dso_id = arg->dso_id; DTLS::DTV *dtv = DTLS_Find(dso_id); if (!dtv || dtv->beg) - return 0; - uptr tls_size = 0; + return nullptr; + CHECK_LE(static_tls_begin, static_tls_end); uptr tls_beg = reinterpret_cast<uptr>(res) - arg->offset - kDtvOffset; VReport(2, - "__tls_get_addr: %p {0x%zx,0x%zx} => %p; tls_beg: 0x%zx; sp: %p " + "__tls_get_addr: %p {0x%zx,0x%zx} => %p; tls_beg: %p; sp: %p " "num_live_dtls %zd\n", - (void *)arg, arg->dso_id, arg->offset, res, tls_beg, (void *)&tls_beg, + (void *)arg, arg->dso_id, arg->offset, res, (void *)tls_beg, + (void *)&tls_beg, atomic_load(&number_of_live_dtls, memory_order_relaxed)); - if (dtls.last_memalign_ptr == tls_beg) { - tls_size = dtls.last_memalign_size; - VReport(2, "__tls_get_addr: glibc <=2.24 suspected; tls={0x%zx,0x%zx}\n", - tls_beg, tls_size); - } else if (tls_beg >= static_tls_begin && tls_beg < static_tls_end) { + if (tls_beg >= static_tls_begin && tls_beg < static_tls_end) { // This is the static TLS block which was initialized / unpoisoned at thread // creation. - VReport(2, "__tls_get_addr: static tls: 0x%zx\n", tls_beg); - tls_size = 0; - } else if (const void *start = - __sanitizer_get_allocated_begin((void *)tls_beg)) { - tls_beg = (uptr)start; - tls_size = __sanitizer_get_allocated_size(start); - VReport(2, "__tls_get_addr: glibc >=2.25 suspected; tls={0x%zx,0x%zx}\n", - tls_beg, tls_size); - } else { - VReport(2, "__tls_get_addr: Can't guess glibc version\n"); - // This may happen inside the DTOR of main thread, so just ignore it. - tls_size = 0; + VReport(2, "__tls_get_addr: static tls: %p\n", (void *)tls_beg); + dtv->beg = tls_beg; + dtv->size = 0; + return nullptr; } + if (uptr tls_size = + __sanitizer_get_dtls_size(reinterpret_cast<void *>(tls_beg))) { + dtv->beg = tls_beg; + dtv->size = tls_size; + return dtv; + } + VReport(2, "__tls_get_addr: Can't guess glibc version\n"); + // This may happen inside the DTOR a thread, or async signal handlers before + // thread initialization, so just ignore it. + // + // If the unknown block is dynamic TLS, unlikely we will be able to recognize + // it in future, mark it as done with '{tls_beg, 0}'. + // + // If the block is static TLS, possible reason of failed detection is nullptr + // in `static_tls_begin`. Regardless of reasons, the future handling of static + // TLS is still '{tls_beg, 0}'. dtv->beg = tls_beg; - dtv->size = tls_size; - return dtv; -} - -void DTLS_on_libc_memalign(void *ptr, uptr size) { - if (!common_flags()->intercept_tls_get_addr) return; - VReport(2, "DTLS_on_libc_memalign: %p 0x%zx\n", ptr, size); - dtls.last_memalign_ptr = reinterpret_cast<uptr>(ptr); - dtls.last_memalign_size = size; + dtv->size = 0; + return nullptr; } DTLS *DTLS_Get() { return &dtls; } @@ -165,7 +180,9 @@ bool DTLSInDestruction(DTLS *dtls) { } #else -void DTLS_on_libc_memalign(void *ptr, uptr size) {} +SANITIZER_INTERFACE_WEAK_DEF(uptr, __sanitizer_get_dtls_size, const void *) { + return 0; +} DTLS::DTV *DTLS_on_tls_get_addr(void *arg, void *res, unsigned long, unsigned long) { return 0; } DTLS *DTLS_Get() { return 0; } diff --git a/libsanitizer/sanitizer_common/sanitizer_tls_get_addr.h b/libsanitizer/sanitizer_common/sanitizer_tls_get_addr.h index 0ddab61..2ef7672 100644 --- a/libsanitizer/sanitizer_common/sanitizer_tls_get_addr.h +++ b/libsanitizer/sanitizer_common/sanitizer_tls_get_addr.h @@ -55,10 +55,6 @@ struct DTLS { static_assert(sizeof(DTVBlock) <= 4096UL, "Unexpected block size"); atomic_uintptr_t dtv_block; - - // Auxiliary fields, don't access them outside sanitizer_tls_get_addr.cpp - uptr last_memalign_size; - uptr last_memalign_ptr; }; template <typename Fn> diff --git a/libsanitizer/sanitizer_common/sanitizer_unwind_fuchsia.cpp b/libsanitizer/sanitizer_common/sanitizer_unwind_fuchsia.cpp new file mode 100644 index 0000000..f3eb859 --- /dev/null +++ b/libsanitizer/sanitizer_common/sanitizer_unwind_fuchsia.cpp @@ -0,0 +1,66 @@ +//===------------------ sanitizer_unwind_fuchsia.cpp +//---------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +/// Sanitizer unwind Fuchsia specific functions. +// +//===----------------------------------------------------------------------===// + +#include "sanitizer_platform.h" +#if SANITIZER_FUCHSIA + +# include <limits.h> +# include <unwind.h> + +# include "sanitizer_common.h" +# include "sanitizer_stacktrace.h" + +namespace __sanitizer { + +# if SANITIZER_CAN_SLOW_UNWIND +struct UnwindTraceArg { + BufferedStackTrace *stack; + u32 max_depth; +}; + +_Unwind_Reason_Code Unwind_Trace(struct _Unwind_Context *ctx, void *param) { + UnwindTraceArg *arg = static_cast<UnwindTraceArg *>(param); + CHECK_LT(arg->stack->size, arg->max_depth); + uptr pc = _Unwind_GetIP(ctx); + if (pc < GetPageSizeCached()) + return _URC_NORMAL_STOP; + arg->stack->trace_buffer[arg->stack->size++] = pc; + return (arg->stack->size == arg->max_depth ? _URC_NORMAL_STOP + : _URC_NO_REASON); +} + +void BufferedStackTrace::UnwindSlow(uptr pc, u32 max_depth) { + CHECK_GE(max_depth, 2); + size = 0; + UnwindTraceArg arg = {this, Min(max_depth + 1, kStackTraceMax)}; + _Unwind_Backtrace(Unwind_Trace, &arg); + CHECK_GT(size, 0); + // We need to pop a few frames so that pc is on top. + uptr to_pop = LocatePcInTrace(pc); + // trace_buffer[0] belongs to the current function so we always pop it, + // unless there is only 1 frame in the stack trace (1 frame is always better + // than 0!). + PopStackFrames(Min(to_pop, static_cast<uptr>(1))); + trace_buffer[0] = pc; +} + +void BufferedStackTrace::UnwindSlow(uptr pc, void *context, u32 max_depth) { + CHECK(context); + CHECK_GE(max_depth, 2); + UNREACHABLE("signal context doesn't exist"); +} +# endif // SANITIZER_CAN_SLOW_UNWIND + +} // namespace __sanitizer + +#endif // SANITIZER_FUCHSIA diff --git a/libsanitizer/sanitizer_common/sanitizer_unwind_win.cpp b/libsanitizer/sanitizer_common/sanitizer_unwind_win.cpp index afcd01d..6fc1839 100644 --- a/libsanitizer/sanitizer_common/sanitizer_unwind_win.cpp +++ b/libsanitizer/sanitizer_common/sanitizer_unwind_win.cpp @@ -70,10 +70,17 @@ void BufferedStackTrace::UnwindSlow(uptr pc, void *context, u32 max_depth) { stack_frame.AddrStack.Offset = ctx.Rsp; # endif # else +# if SANITIZER_ARM + int machine_type = IMAGE_FILE_MACHINE_ARM; + stack_frame.AddrPC.Offset = ctx.Pc; + stack_frame.AddrFrame.Offset = ctx.R11; + stack_frame.AddrStack.Offset = ctx.Sp; +# else int machine_type = IMAGE_FILE_MACHINE_I386; stack_frame.AddrPC.Offset = ctx.Eip; stack_frame.AddrFrame.Offset = ctx.Ebp; stack_frame.AddrStack.Offset = ctx.Esp; +# endif # endif stack_frame.AddrPC.Mode = AddrModeFlat; stack_frame.AddrFrame.Mode = AddrModeFlat; diff --git a/libsanitizer/sanitizer_common/sanitizer_win.cpp b/libsanitizer/sanitizer_common/sanitizer_win.cpp index 06e4965..ea513d5 100644 --- a/libsanitizer/sanitizer_common/sanitizer_win.cpp +++ b/libsanitizer/sanitizer_common/sanitizer_win.cpp @@ -144,7 +144,7 @@ void *MmapOrDie(uptr size, const char *mem_type, bool raw_report) { return rv; } -void UnmapOrDie(void *addr, uptr size) { +void UnmapOrDie(void *addr, uptr size, bool raw_report) { if (!size || !addr) return; @@ -156,10 +156,7 @@ void UnmapOrDie(void *addr, uptr size) { // fails try MEM_DECOMMIT. if (VirtualFree(addr, 0, MEM_RELEASE) == 0) { if (VirtualFree(addr, size, MEM_DECOMMIT) == 0) { - Report("ERROR: %s failed to " - "deallocate 0x%zx (%zd) bytes at address %p (error code: %d)\n", - SanitizerToolName, size, size, addr, GetLastError()); - CHECK("unable to unmap" && 0); + ReportMunmapFailureAndDie(addr, size, GetLastError(), raw_report); } } } @@ -279,8 +276,8 @@ void *MmapFixedOrDie(uptr fixed_addr, uptr size, const char *name) { MEM_COMMIT, PAGE_READWRITE); if (p == 0) { char mem_type[30]; - internal_snprintf(mem_type, sizeof(mem_type), "memory at address 0x%zx", - fixed_addr); + internal_snprintf(mem_type, sizeof(mem_type), "memory at address %p", + (void *)fixed_addr); ReportMmapFailureAndDie(size, mem_type, "allocate", GetLastError()); } return p; @@ -311,8 +308,8 @@ void *MmapFixedOrDieOnFatalError(uptr fixed_addr, uptr size, const char *name) { MEM_COMMIT, PAGE_READWRITE); if (p == 0) { char mem_type[30]; - internal_snprintf(mem_type, sizeof(mem_type), "memory at address 0x%zx", - fixed_addr); + internal_snprintf(mem_type, sizeof(mem_type), "memory at address %p", + (void *)fixed_addr); return ReturnNullptrOnOOMOrDie(size, mem_type, "allocate"); } return p; @@ -387,9 +384,8 @@ bool DontDumpShadowMemory(uptr addr, uptr length) { } uptr MapDynamicShadow(uptr shadow_size_bytes, uptr shadow_scale, - uptr min_shadow_base_alignment, - UNUSED uptr &high_mem_end) { - const uptr granularity = GetMmapGranularity(); + uptr min_shadow_base_alignment, UNUSED uptr &high_mem_end, + uptr granularity) { const uptr alignment = Max<uptr>(granularity << shadow_scale, 1ULL << min_shadow_base_alignment); const uptr left_padding = @@ -877,24 +873,18 @@ uptr GetTlsSize() { return 0; } -void InitTlsSize() { -} - -void GetThreadStackAndTls(bool main, uptr *stk_addr, uptr *stk_size, - uptr *tls_addr, uptr *tls_size) { -#if SANITIZER_GO - *stk_addr = 0; - *stk_size = 0; - *tls_addr = 0; - *tls_size = 0; -#else - uptr stack_top, stack_bottom; - GetThreadStackTopAndBottom(main, &stack_top, &stack_bottom); - *stk_addr = stack_bottom; - *stk_size = stack_top - stack_bottom; - *tls_addr = 0; - *tls_size = 0; -#endif +void GetThreadStackAndTls(bool main, uptr *stk_begin, uptr *stk_end, + uptr *tls_begin, uptr *tls_end) { +# if SANITIZER_GO + *stk_begin = 0; + *stk_end = 0; + *tls_begin = 0; + *tls_end = 0; +# else + GetThreadStackTopAndBottom(main, stk_end, stk_begin); + *tls_begin = 0; + *tls_end = 0; +# endif } void ReportFile::Write(const char *buffer, uptr length) { @@ -978,6 +968,11 @@ bool IsAccessibleMemoryRange(uptr beg, uptr size) { return true; } +bool TryMemCpy(void *dest, const void *src, uptr n) { + // TODO: implement. + return false; +} + bool SignalContext::IsStackOverflow() const { return (DWORD)GetType() == EXCEPTION_STACK_OVERFLOW; } @@ -996,8 +991,13 @@ void SignalContext::InitPcSpBp() { sp = (uptr)context_record->Rsp; # endif # else +# if SANITIZER_ARM + bp = (uptr)context_record->R11; + sp = (uptr)context_record->Sp; +# else bp = (uptr)context_record->Ebp; sp = (uptr)context_record->Esp; +# endif # endif } @@ -1038,7 +1038,52 @@ SignalContext::WriteFlag SignalContext::GetWriteFlag() const { } void SignalContext::DumpAllRegisters(void *context) { - // FIXME: Implement this. + CONTEXT *ctx = (CONTEXT *)context; +# if defined(_M_X64) + Report("Register values:\n"); + Printf("rax = %llx ", ctx->Rax); + Printf("rbx = %llx ", ctx->Rbx); + Printf("rcx = %llx ", ctx->Rcx); + Printf("rdx = %llx ", ctx->Rdx); + Printf("\n"); + Printf("rdi = %llx ", ctx->Rdi); + Printf("rsi = %llx ", ctx->Rsi); + Printf("rbp = %llx ", ctx->Rbp); + Printf("rsp = %llx ", ctx->Rsp); + Printf("\n"); + Printf("r8 = %llx ", ctx->R8); + Printf("r9 = %llx ", ctx->R9); + Printf("r10 = %llx ", ctx->R10); + Printf("r11 = %llx ", ctx->R11); + Printf("\n"); + Printf("r12 = %llx ", ctx->R12); + Printf("r13 = %llx ", ctx->R13); + Printf("r14 = %llx ", ctx->R14); + Printf("r15 = %llx ", ctx->R15); + Printf("\n"); +# elif defined(_M_IX86) + Report("Register values:\n"); + Printf("eax = %lx ", ctx->Eax); + Printf("ebx = %lx ", ctx->Ebx); + Printf("ecx = %lx ", ctx->Ecx); + Printf("edx = %lx ", ctx->Edx); + Printf("\n"); + Printf("edi = %lx ", ctx->Edi); + Printf("esi = %lx ", ctx->Esi); + Printf("ebp = %lx ", ctx->Ebp); + Printf("esp = %lx ", ctx->Esp); + Printf("\n"); +# elif defined(_M_ARM64) + Report("Register values:\n"); + for (int i = 0; i <= 30; i++) { + Printf("x%d%s = %llx", i < 10 ? " " : "", ctx->X[i]); + if (i % 4 == 3) + Printf("\n"); + } +# else + // TODO + (void)ctx; +# endif } int SignalContext::GetType() const { diff --git a/libsanitizer/sanitizer_common/sanitizer_win_dll_thunk.cpp b/libsanitizer/sanitizer_common/sanitizer_win_dll_thunk.cpp deleted file mode 100644 index 1562c16..0000000 --- a/libsanitizer/sanitizer_common/sanitizer_win_dll_thunk.cpp +++ /dev/null @@ -1,101 +0,0 @@ -//===-- sanitizer_win_dll_thunk.cpp ---------------------------------------===// -// -// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. -// See https://llvm.org/LICENSE.txt for license information. -// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception -// -//===----------------------------------------------------------------------===// -// This file defines a family of thunks that should be statically linked into -// the DLLs that have instrumentation in order to delegate the calls to the -// shared runtime that lives in the main binary. -// See https://github.com/google/sanitizers/issues/209 for the details. -//===----------------------------------------------------------------------===// - -#ifdef SANITIZER_DLL_THUNK -#include "sanitizer_win_defs.h" -#include "sanitizer_win_dll_thunk.h" -#include "interception/interception.h" - -extern "C" { -void *WINAPI GetModuleHandleA(const char *module_name); -void abort(); -} - -namespace __sanitizer { -uptr dllThunkGetRealAddrOrDie(const char *name) { - uptr ret = - __interception::InternalGetProcAddress((void *)GetModuleHandleA(0), name); - if (!ret) - abort(); - return ret; -} - -int dllThunkIntercept(const char* main_function, uptr dll_function) { - uptr wrapper = dllThunkGetRealAddrOrDie(main_function); - if (!__interception::OverrideFunction(dll_function, wrapper, 0)) - abort(); - return 0; -} - -int dllThunkInterceptWhenPossible(const char* main_function, - const char* default_function, uptr dll_function) { - uptr wrapper = __interception::InternalGetProcAddress( - (void *)GetModuleHandleA(0), main_function); - if (!wrapper) - wrapper = dllThunkGetRealAddrOrDie(default_function); - if (!__interception::OverrideFunction(dll_function, wrapper, 0)) - abort(); - return 0; -} -} // namespace __sanitizer - -// Include Sanitizer Common interface. -#define INTERFACE_FUNCTION(Name) INTERCEPT_SANITIZER_FUNCTION(Name) -#define INTERFACE_WEAK_FUNCTION(Name) INTERCEPT_SANITIZER_WEAK_FUNCTION(Name) -#include "sanitizer_common_interface.inc" - -#pragma section(".DLLTH$A", read) -#pragma section(".DLLTH$Z", read) - -typedef void (*DllThunkCB)(); -extern "C" { -__declspec(allocate(".DLLTH$A")) DllThunkCB __start_dll_thunk; -__declspec(allocate(".DLLTH$Z")) DllThunkCB __stop_dll_thunk; -} - -// Disable compiler warnings that show up if we declare our own version -// of a compiler intrinsic (e.g. strlen). -#pragma warning(disable: 4391) -#pragma warning(disable: 4392) - -extern "C" int __dll_thunk_init() { - static bool flag = false; - // __dll_thunk_init is expected to be called by only one thread. - if (flag) return 0; - flag = true; - - for (DllThunkCB *it = &__start_dll_thunk; it < &__stop_dll_thunk; ++it) - if (*it) - (*it)(); - - // In DLLs, the callbacks are expected to return 0, - // otherwise CRT initialization fails. - return 0; -} - -// We want to call dll_thunk_init before C/C++ initializers / constructors are -// executed, otherwise functions like memset might be invoked. -#pragma section(".CRT$XIB", long, read) -__declspec(allocate(".CRT$XIB")) int (*__dll_thunk_preinit)() = - __dll_thunk_init; - -static void WINAPI dll_thunk_thread_init(void *mod, unsigned long reason, - void *reserved) { - if (reason == /*DLL_PROCESS_ATTACH=*/1) __dll_thunk_init(); -} - -#pragma section(".CRT$XLAB", long, read) -__declspec(allocate(".CRT$XLAB")) void (WINAPI *__dll_thunk_tls_init)(void *, - unsigned long, void *) = dll_thunk_thread_init; - -#endif // SANITIZER_DLL_THUNK diff --git a/libsanitizer/sanitizer_common/sanitizer_win_dll_thunk.h b/libsanitizer/sanitizer_common/sanitizer_win_dll_thunk.h deleted file mode 100644 index 639d91a..0000000 --- a/libsanitizer/sanitizer_common/sanitizer_win_dll_thunk.h +++ /dev/null @@ -1,181 +0,0 @@ -//===-- sanitizer_win_dll_thunk.h -----------------------------------------===// -// -// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. -// See https://llvm.org/LICENSE.txt for license information. -// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception -// -//===----------------------------------------------------------------------===// -// This header provide helper macros to delegate calls to the shared runtime -// that lives in the main executable. It should be included to dll_thunks that -// will be linked to the dlls, when the sanitizer is a static library included -// in the main executable. -//===----------------------------------------------------------------------===// -#ifndef SANITIZER_WIN_DLL_THUNK_H -#define SANITIZER_WIN_DLL_THUNK_H -#include "sanitizer_internal_defs.h" - -namespace __sanitizer { -uptr dllThunkGetRealAddrOrDie(const char *name); - -int dllThunkIntercept(const char* main_function, uptr dll_function); - -int dllThunkInterceptWhenPossible(const char* main_function, - const char* default_function, uptr dll_function); -} - -extern "C" int __dll_thunk_init(); - -// ----------------- Function interception helper macros -------------------- // -// Override dll_function with main_function from main executable. -#define INTERCEPT_OR_DIE(main_function, dll_function) \ - static int intercept_##dll_function() { \ - return __sanitizer::dllThunkIntercept(main_function, (__sanitizer::uptr) \ - dll_function); \ - } \ - __pragma(section(".DLLTH$M", long, read)) \ - __declspec(allocate(".DLLTH$M")) int (*__dll_thunk_##dll_function)() = \ - intercept_##dll_function; - -// Try to override dll_function with main_function from main executable. -// If main_function is not present, override dll_function with default_function. -#define INTERCEPT_WHEN_POSSIBLE(main_function, default_function, dll_function) \ - static int intercept_##dll_function() { \ - return __sanitizer::dllThunkInterceptWhenPossible(main_function, \ - default_function, (__sanitizer::uptr)dll_function); \ - } \ - __pragma(section(".DLLTH$M", long, read)) \ - __declspec(allocate(".DLLTH$M")) int (*__dll_thunk_##dll_function)() = \ - intercept_##dll_function; - -// -------------------- Function interception macros ------------------------ // -// Special case of hooks -- ASan own interface functions. Those are only called -// after __asan_init, thus an empty implementation is sufficient. -#define INTERCEPT_SANITIZER_FUNCTION(name) \ - extern "C" __declspec(noinline) void name() { \ - volatile int prevent_icf = (__LINE__ << 8) ^ __COUNTER__; \ - static const char function_name[] = #name; \ - for (const char* ptr = &function_name[0]; *ptr; ++ptr) \ - prevent_icf ^= *ptr; \ - (void)prevent_icf; \ - __debugbreak(); \ - } \ - INTERCEPT_OR_DIE(#name, name) - -// Special case of hooks -- Weak functions, could be redefined in the main -// executable, but that is not necessary, so we shouldn't die if we can not find -// a reference. Instead, when the function is not present in the main executable -// we consider the default impl provided by asan library. -#define INTERCEPT_SANITIZER_WEAK_FUNCTION(name) \ - extern "C" __declspec(noinline) void name() { \ - volatile int prevent_icf = (__LINE__ << 8) ^ __COUNTER__; \ - static const char function_name[] = #name; \ - for (const char* ptr = &function_name[0]; *ptr; ++ptr) \ - prevent_icf ^= *ptr; \ - (void)prevent_icf; \ - __debugbreak(); \ - } \ - INTERCEPT_WHEN_POSSIBLE(#name, STRINGIFY(WEAK_EXPORT_NAME(name)), name) - -// We can't define our own version of strlen etc. because that would lead to -// link-time or even type mismatch errors. Instead, we can declare a function -// just to be able to get its address. Me may miss the first few calls to the -// functions since it can be called before __dll_thunk_init, but that would lead -// to false negatives in the startup code before user's global initializers, -// which isn't a big deal. -#define INTERCEPT_LIBRARY_FUNCTION(name) \ - extern "C" void name(); \ - INTERCEPT_OR_DIE(STRINGIFY(WRAP(name)), name) - -// Use these macros for functions that could be called before __dll_thunk_init() -// is executed and don't lead to errors if defined (free, malloc, etc). -#define INTERCEPT_WRAP_V_V(name) \ - extern "C" void name() { \ - typedef decltype(name) *fntype; \ - static fntype fn = (fntype)__sanitizer::dllThunkGetRealAddrOrDie(#name); \ - fn(); \ - } \ - INTERCEPT_OR_DIE(#name, name); - -#define INTERCEPT_WRAP_V_W(name) \ - extern "C" void name(void *arg) { \ - typedef decltype(name) *fntype; \ - static fntype fn = (fntype)__sanitizer::dllThunkGetRealAddrOrDie(#name); \ - fn(arg); \ - } \ - INTERCEPT_OR_DIE(#name, name); - -#define INTERCEPT_WRAP_V_WW(name) \ - extern "C" void name(void *arg1, void *arg2) { \ - typedef decltype(name) *fntype; \ - static fntype fn = (fntype)__sanitizer::dllThunkGetRealAddrOrDie(#name); \ - fn(arg1, arg2); \ - } \ - INTERCEPT_OR_DIE(#name, name); - -#define INTERCEPT_WRAP_V_WWW(name) \ - extern "C" void name(void *arg1, void *arg2, void *arg3) { \ - typedef decltype(name) *fntype; \ - static fntype fn = (fntype)__sanitizer::dllThunkGetRealAddrOrDie(#name); \ - fn(arg1, arg2, arg3); \ - } \ - INTERCEPT_OR_DIE(#name, name); - -#define INTERCEPT_WRAP_W_V(name) \ - extern "C" void *name() { \ - typedef decltype(name) *fntype; \ - static fntype fn = (fntype)__sanitizer::dllThunkGetRealAddrOrDie(#name); \ - return fn(); \ - } \ - INTERCEPT_OR_DIE(#name, name); - -#define INTERCEPT_WRAP_W_W(name) \ - extern "C" void *name(void *arg) { \ - typedef decltype(name) *fntype; \ - static fntype fn = (fntype)__sanitizer::dllThunkGetRealAddrOrDie(#name); \ - return fn(arg); \ - } \ - INTERCEPT_OR_DIE(#name, name); - -#define INTERCEPT_WRAP_W_WW(name) \ - extern "C" void *name(void *arg1, void *arg2) { \ - typedef decltype(name) *fntype; \ - static fntype fn = (fntype)__sanitizer::dllThunkGetRealAddrOrDie(#name); \ - return fn(arg1, arg2); \ - } \ - INTERCEPT_OR_DIE(#name, name); - -#define INTERCEPT_WRAP_W_WWW(name) \ - extern "C" void *name(void *arg1, void *arg2, void *arg3) { \ - typedef decltype(name) *fntype; \ - static fntype fn = (fntype)__sanitizer::dllThunkGetRealAddrOrDie(#name); \ - return fn(arg1, arg2, arg3); \ - } \ - INTERCEPT_OR_DIE(#name, name); - -#define INTERCEPT_WRAP_W_WWWW(name) \ - extern "C" void *name(void *arg1, void *arg2, void *arg3, void *arg4) { \ - typedef decltype(name) *fntype; \ - static fntype fn = (fntype)__sanitizer::dllThunkGetRealAddrOrDie(#name); \ - return fn(arg1, arg2, arg3, arg4); \ - } \ - INTERCEPT_OR_DIE(#name, name); - -#define INTERCEPT_WRAP_W_WWWWW(name) \ - extern "C" void *name(void *arg1, void *arg2, void *arg3, void *arg4, \ - void *arg5) { \ - typedef decltype(name) *fntype; \ - static fntype fn = (fntype)__sanitizer::dllThunkGetRealAddrOrDie(#name); \ - return fn(arg1, arg2, arg3, arg4, arg5); \ - } \ - INTERCEPT_OR_DIE(#name, name); - -#define INTERCEPT_WRAP_W_WWWWWW(name) \ - extern "C" void *name(void *arg1, void *arg2, void *arg3, void *arg4, \ - void *arg5, void *arg6) { \ - typedef decltype(name) *fntype; \ - static fntype fn = (fntype)__sanitizer::dllThunkGetRealAddrOrDie(#name); \ - return fn(arg1, arg2, arg3, arg4, arg5, arg6); \ - } \ - INTERCEPT_OR_DIE(#name, name); - -#endif // SANITIZER_WIN_DLL_THUNK_H diff --git a/libsanitizer/sanitizer_common/sanitizer_win_dynamic_runtime_thunk.cpp b/libsanitizer/sanitizer_common/sanitizer_win_dynamic_runtime_thunk.cpp deleted file mode 100644 index 87c032c..0000000 --- a/libsanitizer/sanitizer_common/sanitizer_win_dynamic_runtime_thunk.cpp +++ /dev/null @@ -1,26 +0,0 @@ -//===-- santizer_win_dynamic_runtime_thunk.cpp ----------------------------===// -// -// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. -// See https://llvm.org/LICENSE.txt for license information. -// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception -// -//===----------------------------------------------------------------------===// -// -// This file defines things that need to be present in the application modules -// to interact with Sanitizer Common, when it is included in a dll. -// -//===----------------------------------------------------------------------===// -#ifdef SANITIZER_DYNAMIC_RUNTIME_THUNK -#define SANITIZER_IMPORT_INTERFACE 1 -#include "sanitizer_win_defs.h" -// Define weak alias for all weak functions imported from sanitizer common. -#define INTERFACE_FUNCTION(Name) -#define INTERFACE_WEAK_FUNCTION(Name) WIN_WEAK_IMPORT_DEF(Name) -#include "sanitizer_common_interface.inc" -#endif // SANITIZER_DYNAMIC_RUNTIME_THUNK - -namespace __sanitizer { -// Add one, otherwise unused, external symbol to this object file so that the -// Visual C++ linker includes it and reads the .drective section. -void ForceWholeArchiveIncludeForSanitizerCommon() {} -} diff --git a/libsanitizer/sanitizer_common/sanitizer_win_immortalize.h b/libsanitizer/sanitizer_common/sanitizer_win_immortalize.h new file mode 100644 index 0000000..808cd2f --- /dev/null +++ b/libsanitizer/sanitizer_common/sanitizer_win_immortalize.h @@ -0,0 +1,71 @@ +//===-- sanitizer_win_immortalize.h ---------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This file is shared between AddressSanitizer, and interception. +// +// Windows-specific thread-safe and pre-CRT global initialization safe +// infrastructure to create an object whose destructor is never called. +//===----------------------------------------------------------------------===// +#if SANITIZER_WINDOWS +# pragma once +// Requires including sanitizer_placement_new.h (which is not allowed to be +// included in headers). + +# include "sanitizer_win_defs.h" +// These types are required to satisfy XFG which requires that the names of the +// types for indirect calls to be correct as well as the name of the original +// type for any typedefs. + +// TODO: There must be a better way to do this +# ifndef _WINDOWS_ +typedef void* PVOID; +typedef int BOOL; +typedef union _RTL_RUN_ONCE { + PVOID ptr; +} INIT_ONCE, *PINIT_ONCE; + +extern "C" { +__declspec(dllimport) int WINAPI InitOnceExecuteOnce( + PINIT_ONCE, BOOL(WINAPI*)(PINIT_ONCE, PVOID, PVOID*), void*, void*); +} +# endif + +namespace __sanitizer { +template <class Ty> +BOOL WINAPI immortalize_impl(PINIT_ONCE, PVOID storage_ptr, PVOID*) noexcept { + // Ty must provide a placement new operator + new (storage_ptr) Ty(); + return 1; +} + +template <class Ty, typename Arg> +BOOL WINAPI immortalize_impl(PINIT_ONCE, PVOID storage_ptr, + PVOID* param) noexcept { + // Ty must provide a placement new operator + new (storage_ptr) Ty(*((Arg*)param)); + return 1; +} + +template <class Ty> +Ty& immortalize() { // return a reference to an object that will live forever + static INIT_ONCE flag; + alignas(Ty) static unsigned char storage[sizeof(Ty)]; + InitOnceExecuteOnce(&flag, immortalize_impl<Ty>, &storage, nullptr); + return reinterpret_cast<Ty&>(storage); +} + +template <class Ty, typename Arg> +Ty& immortalize( + Arg arg) { // return a reference to an object that will live forever + static INIT_ONCE flag; + alignas(Ty) static unsigned char storage[sizeof(Ty)]; + InitOnceExecuteOnce(&flag, immortalize_impl<Ty, Arg>, &storage, &arg); + return reinterpret_cast<Ty&>(storage); +} +} // namespace __sanitizer +#endif // SANITIZER_WINDOWS diff --git a/libsanitizer/sanitizer_common/sanitizer_win_interception.cpp b/libsanitizer/sanitizer_common/sanitizer_win_interception.cpp new file mode 100644 index 0000000..c93a411 --- /dev/null +++ b/libsanitizer/sanitizer_common/sanitizer_win_interception.cpp @@ -0,0 +1,156 @@ +//===-- sanitizer_win_interception.cpp -------------------- --*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// Windows-specific export surface to provide interception for parts of the +// runtime that are always statically linked, both for overriding user-defined +// functions as well as registering weak functions that the ASAN runtime should +// use over defaults. +// +//===----------------------------------------------------------------------===// + +#include "sanitizer_platform.h" +#if SANITIZER_WINDOWS +# include <stddef.h> + +# include "interception/interception.h" +# include "sanitizer_addrhashmap.h" +# include "sanitizer_common.h" +# include "sanitizer_internal_defs.h" +# include "sanitizer_placement_new.h" +# include "sanitizer_win_immortalize.h" +# include "sanitizer_win_interception.h" + +using namespace __sanitizer; + +extern "C" void *__ImageBase; + +namespace __sanitizer { + +static uptr GetSanitizerDllExport(const char *export_name) { + const uptr function_address = + __interception::InternalGetProcAddress(&__ImageBase, export_name); + if (function_address == 0) { + Report("ERROR: Failed to find sanitizer DLL export '%s'\n", export_name); + CHECK("Failed to find sanitizer DLL export" && 0); + } + return function_address; +} + +struct WeakCallbackList { + explicit constexpr WeakCallbackList(RegisterWeakFunctionCallback cb) + : callback(cb), next(nullptr) {} + + static void *operator new(size_t size) { return InternalAlloc(size); } + + static void operator delete(void *p) { InternalFree(p); } + + RegisterWeakFunctionCallback callback; + WeakCallbackList *next; +}; +using WeakCallbackMap = AddrHashMap<WeakCallbackList *, 11>; + +static WeakCallbackMap *GetWeakCallbackMap() { + return &immortalize<WeakCallbackMap>(); +} + +void AddRegisterWeakFunctionCallback(uptr export_address, + RegisterWeakFunctionCallback cb) { + WeakCallbackMap::Handle h_find_or_create(GetWeakCallbackMap(), export_address, + false, true); + CHECK(h_find_or_create.exists()); + if (h_find_or_create.created()) { + *h_find_or_create = new WeakCallbackList(cb); + } else { + (*h_find_or_create)->next = new WeakCallbackList(cb); + } +} + +static void RunWeakFunctionCallbacks(uptr export_address) { + WeakCallbackMap::Handle h_find(GetWeakCallbackMap(), export_address, false, + false); + if (!h_find.exists()) { + return; + } + + WeakCallbackList *list = *h_find; + do { + list->callback(); + } while ((list = list->next)); +} + +} // namespace __sanitizer + +extern "C" __declspec(dllexport) bool __cdecl __sanitizer_override_function( + const char *export_name, const uptr user_function, + uptr *const old_user_function) { + CHECK(export_name); + CHECK(user_function); + + const uptr sanitizer_function = GetSanitizerDllExport(export_name); + + const bool function_overridden = __interception::OverrideFunction( + user_function, sanitizer_function, old_user_function); + if (!function_overridden) { + Report( + "ERROR: Failed to override local function at '%p' with sanitizer " + "function '%s'\n", + user_function, export_name); + CHECK("Failed to replace local function with sanitizer version." && 0); + } + + return function_overridden; +} + +extern "C" + __declspec(dllexport) bool __cdecl __sanitizer_override_function_by_addr( + const uptr source_function, const uptr target_function, + uptr *const old_target_function) { + CHECK(source_function); + CHECK(target_function); + + const bool function_overridden = __interception::OverrideFunction( + target_function, source_function, old_target_function); + if (!function_overridden) { + Report( + "ERROR: Failed to override function at '%p' with function at " + "'%p'\n", + target_function, source_function); + CHECK("Failed to apply function override." && 0); + } + + return function_overridden; +} + +extern "C" + __declspec(dllexport) bool __cdecl __sanitizer_register_weak_function( + const char *export_name, const uptr user_function, + uptr *const old_user_function) { + CHECK(export_name); + CHECK(user_function); + + const uptr sanitizer_function = GetSanitizerDllExport(export_name); + + const bool function_overridden = __interception::OverrideFunction( + sanitizer_function, user_function, old_user_function); + if (!function_overridden) { + Report( + "ERROR: Failed to register local function at '%p' to be used in " + "place of sanitizer function '%s'\n.", + user_function, export_name); + CHECK("Failed to register weak function." && 0); + } + + // Note that thread-safety of RunWeakFunctionCallbacks in InitializeFlags + // depends on __sanitizer_register_weak_functions being called during the + // loader lock. + RunWeakFunctionCallbacks(sanitizer_function); + + return function_overridden; +} + +#endif // SANITIZER_WINDOWS diff --git a/libsanitizer/sanitizer_common/sanitizer_win_interception.h b/libsanitizer/sanitizer_common/sanitizer_win_interception.h new file mode 100644 index 0000000..04b7e91 --- /dev/null +++ b/libsanitizer/sanitizer_common/sanitizer_win_interception.h @@ -0,0 +1,32 @@ +//===-- sanitizer_win_interception.h ---------------------- --*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// Windows-specific export surface to provide interception for parts of the +// runtime that are always statically linked, both for overriding user-defined +// functions as well as registering weak functions that the ASAN runtime should +// use over defaults. +// +//===----------------------------------------------------------------------===// + +#ifndef SANITIZER_WIN_INTERCEPTION_H +#define SANITIZER_WIN_INTERCEPTION_H + +#include "sanitizer_platform.h" +#if SANITIZER_WINDOWS + +# include "sanitizer_common.h" +# include "sanitizer_internal_defs.h" + +namespace __sanitizer { +using RegisterWeakFunctionCallback = void (*)(); +void AddRegisterWeakFunctionCallback(uptr export_address, + RegisterWeakFunctionCallback cb); +} // namespace __sanitizer + +#endif // SANITIZER_WINDOWS +#endif // SANITIZER_WIN_INTERCEPTION_H
\ No newline at end of file diff --git a/libsanitizer/sanitizer_common/sanitizer_win_thunk_interception.cpp b/libsanitizer/sanitizer_common/sanitizer_win_thunk_interception.cpp new file mode 100644 index 0000000..16ba155 --- /dev/null +++ b/libsanitizer/sanitizer_common/sanitizer_win_thunk_interception.cpp @@ -0,0 +1,110 @@ +//===-- sanitizer_win_thunk_interception.cpp ----------------------- -----===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This file defines things that need to be present in the application modules +// to interact with sanitizer DLL correctly and cannot be implemented using the +// default "import library" generated when linking the DLL. +// +// This includes the common infrastructure required to intercept local functions +// that must be replaced with sanitizer-aware versions, as well as the +// registration of weak functions with the sanitizer DLL. With this in-place, +// other sanitizer components can simply write to the .INTR and .WEAK sections. +// +//===----------------------------------------------------------------------===// + +#if defined(SANITIZER_STATIC_RUNTIME_THUNK) || \ + defined(SANITIZER_DYNAMIC_RUNTIME_THUNK) +# include "sanitizer_win_thunk_interception.h" + +extern "C" void abort(); + +namespace __sanitizer { + +int override_function(const char *export_name, const uptr user_function) { + if (!__sanitizer_override_function(export_name, user_function)) { + abort(); + } + + return 0; +} + +int register_weak(const char *export_name, const uptr user_function) { + if (!__sanitizer_register_weak_function(export_name, user_function)) { + abort(); + } + + return 0; +} + +void initialize_thunks(const sanitizer_thunk *first, + const sanitizer_thunk *last) { + for (const sanitizer_thunk *it = first; it < last; ++it) { + if (*it) { + (*it)(); + } + } +} +} // namespace __sanitizer + +# define INTERFACE_FUNCTION(Name) +# define INTERFACE_WEAK_FUNCTION(Name) REGISTER_WEAK_FUNCTION(Name) +# include "sanitizer_common_interface.inc" + +# pragma section(".INTR$A", read) // intercept begin +# pragma section(".INTR$Z", read) // intercept end +# pragma section(".WEAK$A", read) // weak begin +# pragma section(".WEAK$Z", read) // weak end + +extern "C" { +__declspec(allocate( + ".INTR$A")) sanitizer_thunk __sanitizer_intercept_thunk_begin; +__declspec(allocate(".INTR$Z")) sanitizer_thunk __sanitizer_intercept_thunk_end; + +__declspec(allocate( + ".WEAK$A")) sanitizer_thunk __sanitizer_register_weak_thunk_begin; +__declspec(allocate( + ".WEAK$Z")) sanitizer_thunk __sanitizer_register_weak_thunk_end; +} + +extern "C" int __sanitizer_thunk_init() { + // __sanitizer_static_thunk_init is expected to be called by only one thread. + static bool flag = false; + if (flag) { + return 0; + } + flag = true; + + __sanitizer::initialize_thunks(&__sanitizer_intercept_thunk_begin, + &__sanitizer_intercept_thunk_end); + __sanitizer::initialize_thunks(&__sanitizer_register_weak_thunk_begin, + &__sanitizer_register_weak_thunk_end); + + // In DLLs, the callbacks are expected to return 0, + // otherwise CRT initialization fails. + return 0; +} + +// We want to call dll_thunk_init before C/C++ initializers / constructors are +// executed, otherwise functions like memset might be invoked. +# pragma section(".CRT$XIB", long, read) +__declspec(allocate(".CRT$XIB")) int (*__sanitizer_thunk_init_ptr)() = + __sanitizer_thunk_init; + +static void WINAPI sanitizer_thunk_thread_init(void *mod, unsigned long reason, + void *reserved) { + if (reason == /*DLL_PROCESS_ATTACH=*/1) + __sanitizer_thunk_init(); +} + +# pragma section(".CRT$XLAB", long, read) +__declspec(allocate(".CRT$XLAB")) void( + WINAPI *__sanitizer_thunk_thread_init_ptr)(void *, unsigned long, void *) = + sanitizer_thunk_thread_init; + +#endif // defined(SANITIZER_STATIC_RUNTIME_THUNK) || + // defined(SANITIZER_DYNAMIC_RUNTIME_THUNK)
\ No newline at end of file diff --git a/libsanitizer/sanitizer_common/sanitizer_win_thunk_interception.h b/libsanitizer/sanitizer_common/sanitizer_win_thunk_interception.h new file mode 100644 index 0000000..278450d --- /dev/null +++ b/libsanitizer/sanitizer_common/sanitizer_win_thunk_interception.h @@ -0,0 +1,88 @@ +//===-- sanitizer_win_thunk_interception.h ------------------------- -----===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// This header provide helper macros and functions to delegate calls to the +// shared runtime that lives in the sanitizer DLL. +//===----------------------------------------------------------------------===// + +#ifndef SANITIZER_WIN_THUNK_INTERCEPTION_H +#define SANITIZER_WIN_THUNK_INTERCEPTION_H +#include <stdint.h> + +#include "sanitizer_internal_defs.h" + +extern "C" { +__declspec(dllimport) bool __cdecl __sanitizer_override_function( + const char *export_name, __sanitizer::uptr user_function, + __sanitizer::uptr *old_function = nullptr); +__declspec(dllimport) bool __cdecl __sanitizer_override_function_by_addr( + __sanitizer::uptr source_function, __sanitizer::uptr target_function, + __sanitizer::uptr *old_target_function = nullptr); +__declspec(dllimport) bool __cdecl __sanitizer_register_weak_function( + const char *export_name, __sanitizer::uptr user_function, + __sanitizer::uptr *old_function = nullptr); +} + +using sanitizer_thunk = int (*)(); + +namespace __sanitizer { +int override_function(const char *export_name, uptr user_function); +int register_weak(const char *export_name, uptr user_function); +void initialize_thunks(const sanitizer_thunk *begin, + const sanitizer_thunk *end); +} // namespace __sanitizer + +// -------------------- Function interception macros ------------------------ // +// We can't define our own version of strlen etc. because that would lead to +// link-time or even type mismatch errors. Instead, we can declare a function +// just to be able to get its address. Me may miss the first few calls to the +// functions since it can be called before __dll_thunk_init, but that would lead +// to false negatives in the startup code before user's global initializers, +// which isn't a big deal. +// Use .INTR segment to register function pointers that are iterated over during +// startup that will replace local_function with sanitizer_export. + +#define INTERCEPT_LIBRARY_FUNCTION(local_function, sanitizer_export) \ + extern "C" void local_function(); \ + static int intercept_##local_function() { \ + return __sanitizer::override_function( \ + sanitizer_export, \ + reinterpret_cast<__sanitizer::uptr>(local_function)); \ + } \ + __pragma(section(".INTR$M", long, read)) __declspec(allocate( \ + ".INTR$M")) int (*__sanitizer_static_thunk_##local_function)() = \ + intercept_##local_function; + +// ------------------ Weak symbol registration macros ---------------------- // +// Use .WEAK segment to register function pointers that are iterated over during +// startup that will replace sanitizer_export with local_function +#ifdef __clang__ +# define REGISTER_WEAK_OPTNONE __attribute__((optnone)) +# define REGISTER_WEAK_FUNCTION_ADDRESS(fn) __builtin_function_start(fn) +#else +# define REGISTER_WEAK_OPTNONE +# define REGISTER_WEAK_FUNCTION_ADDRESS(fn) &fn +#endif + +#define REGISTER_WEAK_FUNCTION(local_function) \ + extern "C" void local_function(); \ + extern "C" void WEAK_EXPORT_NAME(local_function)(); \ + WIN_WEAK_IMPORT_DEF(local_function) \ + REGISTER_WEAK_OPTNONE static int register_weak_##local_function() { \ + if ((uintptr_t)REGISTER_WEAK_FUNCTION_ADDRESS(local_function) != \ + (uintptr_t)REGISTER_WEAK_FUNCTION_ADDRESS( \ + WEAK_EXPORT_NAME(local_function))) { \ + return __sanitizer::register_weak( \ + SANITIZER_STRINGIFY(WEAK_EXPORT_NAME(local_function)), \ + reinterpret_cast<__sanitizer::uptr>(local_function)); \ + } \ + return 0; \ + } \ + __pragma(section(".WEAK$M", long, read)) __declspec(allocate( \ + ".WEAK$M")) int (*__sanitizer_register_weak_##local_function)() = \ + register_weak_##local_function; +#endif // SANITIZER_WIN_STATIC_RUNTIME_THUNK_H diff --git a/libsanitizer/sanitizer_common/sanitizer_win_weak_interception.cpp b/libsanitizer/sanitizer_common/sanitizer_win_weak_interception.cpp deleted file mode 100644 index b14bbf7..0000000 --- a/libsanitizer/sanitizer_common/sanitizer_win_weak_interception.cpp +++ /dev/null @@ -1,94 +0,0 @@ -//===-- sanitizer_win_weak_interception.cpp -------------------------------===// -// -// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. -// See https://llvm.org/LICENSE.txt for license information. -// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception -// -//===----------------------------------------------------------------------===// -// This module should be included in the sanitizer when it is implemented as a -// shared library on Windows (dll), in order to delegate the calls of weak -// functions to the implementation in the main executable when a strong -// definition is provided. -//===----------------------------------------------------------------------===// - -#include "sanitizer_platform.h" -#if SANITIZER_WINDOWS && SANITIZER_DYNAMIC -#include "sanitizer_win_weak_interception.h" -#include "sanitizer_allocator_interface.h" -#include "sanitizer_interface_internal.h" -#include "sanitizer_win_defs.h" -#include "interception/interception.h" - -extern "C" { -void *WINAPI GetModuleHandleA(const char *module_name); -void abort(); -} - -namespace __sanitizer { -// Try to get a pointer to real_function in the main module and override -// dll_function with that pointer. If the function isn't found, nothing changes. -int interceptWhenPossible(uptr dll_function, const char *real_function) { - uptr real = __interception::InternalGetProcAddress( - (void *)GetModuleHandleA(0), real_function); - if (real && !__interception::OverrideFunction((uptr)dll_function, real, 0)) - abort(); - return 0; -} -} // namespace __sanitizer - -// Declare weak hooks. -extern "C" { -void __sanitizer_on_print(const char *str); -void __sanitizer_weak_hook_memcmp(uptr called_pc, const void *s1, - const void *s2, uptr n, int result); -void __sanitizer_weak_hook_strcmp(uptr called_pc, const char *s1, - const char *s2, int result); -void __sanitizer_weak_hook_strncmp(uptr called_pc, const char *s1, - const char *s2, uptr n, int result); -void __sanitizer_weak_hook_strstr(uptr called_pc, const char *s1, - const char *s2, char *result); -} - -// Include Sanitizer Common interface. -#define INTERFACE_FUNCTION(Name) -#define INTERFACE_WEAK_FUNCTION(Name) INTERCEPT_SANITIZER_WEAK_FUNCTION(Name) -#include "sanitizer_common_interface.inc" - -#pragma section(".WEAK$A", read) -#pragma section(".WEAK$Z", read) - -typedef void (*InterceptCB)(); -extern "C" { -__declspec(allocate(".WEAK$A")) InterceptCB __start_weak_list; -__declspec(allocate(".WEAK$Z")) InterceptCB __stop_weak_list; -} - -static int weak_intercept_init() { - static bool flag = false; - // weak_interception_init is expected to be called by only one thread. - if (flag) return 0; - flag = true; - - for (InterceptCB *it = &__start_weak_list; it < &__stop_weak_list; ++it) - if (*it) - (*it)(); - - // In DLLs, the callbacks are expected to return 0, - // otherwise CRT initialization fails. - return 0; -} - -#pragma section(".CRT$XIB", long, read) -__declspec(allocate(".CRT$XIB")) int (*__weak_intercept_preinit)() = - weak_intercept_init; - -static void WINAPI weak_intercept_thread_init(void *mod, unsigned long reason, - void *reserved) { - if (reason == /*DLL_PROCESS_ATTACH=*/1) weak_intercept_init(); -} - -#pragma section(".CRT$XLAB", long, read) -__declspec(allocate(".CRT$XLAB")) void(WINAPI *__weak_intercept_tls_init)( - void *, unsigned long, void *) = weak_intercept_thread_init; - -#endif // SANITIZER_WINDOWS && SANITIZER_DYNAMIC diff --git a/libsanitizer/sanitizer_common/sanitizer_win_weak_interception.h b/libsanitizer/sanitizer_common/sanitizer_win_weak_interception.h deleted file mode 100644 index 5e4d8b8..0000000 --- a/libsanitizer/sanitizer_common/sanitizer_win_weak_interception.h +++ /dev/null @@ -1,32 +0,0 @@ -//===-- sanitizer_win_weak_interception.h ---------------------------------===// -// -// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. -// See https://llvm.org/LICENSE.txt for license information. -// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception -// -//===----------------------------------------------------------------------===// -// This header provide helper macros to delegate calls of weak functions to the -// implementation in the main executable when a strong definition is present. -//===----------------------------------------------------------------------===// -#ifndef SANITIZER_WIN_WEAK_INTERCEPTION_H -#define SANITIZER_WIN_WEAK_INTERCEPTION_H -#include "sanitizer_internal_defs.h" - -namespace __sanitizer { -int interceptWhenPossible(uptr dll_function, const char *real_function); -} - -// ----------------- Function interception helper macros -------------------- // -// Weak functions, could be redefined in the main executable, but that is not -// necessary, so we shouldn't die if we can not find a reference. -#define INTERCEPT_WEAK(Name) interceptWhenPossible((uptr) Name, #Name); - -#define INTERCEPT_SANITIZER_WEAK_FUNCTION(Name) \ - static int intercept_##Name() { \ - return __sanitizer::interceptWhenPossible((__sanitizer::uptr) Name, #Name);\ - } \ - __pragma(section(".WEAK$M", long, read)) \ - __declspec(allocate(".WEAK$M")) int (*__weak_intercept_##Name)() = \ - intercept_##Name; - -#endif // SANITIZER_WIN_WEAK_INTERCEPTION_H diff --git a/libsanitizer/tsan/tsan_defs.h b/libsanitizer/tsan/tsan_defs.h index 1ffa3d6..270d441 100644 --- a/libsanitizer/tsan/tsan_defs.h +++ b/libsanitizer/tsan/tsan_defs.h @@ -30,7 +30,7 @@ # define __MM_MALLOC_H # include <emmintrin.h> # include <smmintrin.h> -# define VECTOR_ALIGNED ALIGNED(16) +# define VECTOR_ALIGNED alignas(16) typedef __m128i m128; #else # define VECTOR_ALIGNED diff --git a/libsanitizer/tsan/tsan_dispatch_defs.h b/libsanitizer/tsan/tsan_dispatch_defs.h index 54c0b0b..8d38beb 100644 --- a/libsanitizer/tsan/tsan_dispatch_defs.h +++ b/libsanitizer/tsan/tsan_dispatch_defs.h @@ -56,13 +56,6 @@ extern const dispatch_block_t _dispatch_data_destructor_munmap; # define DISPATCH_NOESCAPE #endif -#if SANITIZER_APPLE -# define SANITIZER_WEAK_IMPORT extern "C" __attribute((weak_import)) -#else -# define SANITIZER_WEAK_IMPORT extern "C" __attribute((weak)) -#endif - - // Data types used in dispatch APIs typedef unsigned long size_t; typedef unsigned long uintptr_t; diff --git a/libsanitizer/tsan/tsan_interceptors_mac.cpp b/libsanitizer/tsan/tsan_interceptors_mac.cpp index e4f9e29..9db0eeb 100644 --- a/libsanitizer/tsan/tsan_interceptors_mac.cpp +++ b/libsanitizer/tsan/tsan_interceptors_mac.cpp @@ -94,6 +94,10 @@ static constexpr morder kMacFailureOrder = mo_relaxed; m_orig(int32_t, uint32_t, a32, f##32##OrigBarrier, \ __tsan_atomic32_##tsan_atomic_f, kMacOrderBarrier) + +#pragma clang diagnostic push +// OSAtomic* functions are deprecated. +#pragma clang diagnostic ignored "-Wdeprecated-declarations" OSATOMIC_INTERCEPTORS_ARITHMETIC(OSAtomicAdd, fetch_add, OSATOMIC_INTERCEPTOR_PLUS_X) OSATOMIC_INTERCEPTORS_ARITHMETIC(OSAtomicIncrement, fetch_add, @@ -123,6 +127,9 @@ OSATOMIC_INTERCEPTORS_BITWISE(OSAtomicXor, fetch_xor, kMacOrderBarrier, kMacFailureOrder); \ } +#pragma clang diagnostic push +// OSAtomicCompareAndSwap* functions are deprecated. +#pragma clang diagnostic ignored "-Wdeprecated-declarations" OSATOMIC_INTERCEPTORS_CAS(OSAtomicCompareAndSwapInt, __tsan_atomic32, a32, int) OSATOMIC_INTERCEPTORS_CAS(OSAtomicCompareAndSwapLong, __tsan_atomic64, a64, long_t) @@ -132,6 +139,7 @@ OSATOMIC_INTERCEPTORS_CAS(OSAtomicCompareAndSwap32, __tsan_atomic32, a32, int32_t) OSATOMIC_INTERCEPTORS_CAS(OSAtomicCompareAndSwap64, __tsan_atomic64, a64, int64_t) +#pragma clang diagnostic pop #define OSATOMIC_INTERCEPTOR_BITOP(f, op, clear, mo) \ TSAN_INTERCEPTOR(bool, f, uint32_t n, volatile void *ptr) { \ diff --git a/libsanitizer/tsan/tsan_interceptors_posix.cpp b/libsanitizer/tsan/tsan_interceptors_posix.cpp index 80f86ca..423d97e 100644 --- a/libsanitizer/tsan/tsan_interceptors_posix.cpp +++ b/libsanitizer/tsan/tsan_interceptors_posix.cpp @@ -12,13 +12,15 @@ // sanitizer_common/sanitizer_common_interceptors.inc //===----------------------------------------------------------------------===// +#include "sanitizer_common/sanitizer_allocator_dlsym.h" #include "sanitizer_common/sanitizer_atomic.h" #include "sanitizer_common/sanitizer_errno.h" +#include "sanitizer_common/sanitizer_glibc_version.h" +#include "sanitizer_common/sanitizer_internal_defs.h" #include "sanitizer_common/sanitizer_libc.h" #include "sanitizer_common/sanitizer_linux.h" #include "sanitizer_common/sanitizer_platform_limits_netbsd.h" #include "sanitizer_common/sanitizer_platform_limits_posix.h" -#include "sanitizer_common/sanitizer_placement_new.h" #include "sanitizer_common/sanitizer_posix.h" #include "sanitizer_common/sanitizer_stacktrace.h" #include "sanitizer_common/sanitizer_tls_get_addr.h" @@ -207,7 +209,7 @@ struct AtExitCtx { struct InterceptorContext { // The object is 64-byte aligned, because we want hot data to be located // in a single cache line if possible (it's accessed in every interceptor). - ALIGNED(64) LibIgnore libignore; + alignas(64) LibIgnore libignore; __sanitizer_sigaction sigactions[kSigCount]; #if !SANITIZER_APPLE && !SANITIZER_NETBSD unsigned finalize_key; @@ -219,7 +221,7 @@ struct InterceptorContext { InterceptorContext() : libignore(LINKER_INITIALIZED), atexit_mu(MutexTypeAtExit), AtExitStack() {} }; -static ALIGNED(64) char interceptor_placeholder[sizeof(InterceptorContext)]; +alignas(64) static char interceptor_placeholder[sizeof(InterceptorContext)]; InterceptorContext *interceptor_ctx() { return reinterpret_cast<InterceptorContext*>(&interceptor_placeholder[0]); } @@ -251,6 +253,13 @@ SANITIZER_WEAK_CXX_DEFAULT_IMPL void OnPotentiallyBlockingRegionBegin() {} SANITIZER_WEAK_CXX_DEFAULT_IMPL void OnPotentiallyBlockingRegionEnd() {} #endif +// FIXME: Use for `in_symbolizer()` as well. As-is we can't use +// `DlSymAllocator`, because it uses the primary allocator only. Symbolizer +// requires support of the secondary allocator for larger blocks. +struct DlsymAlloc : public DlSymAllocator<DlsymAlloc> { + static bool UseImpl() { return (ctx && !ctx->initialized); } +}; + } // namespace __tsan static ThreadSignalContext *SigCtx(ThreadState *thr) { @@ -660,6 +669,8 @@ TSAN_INTERCEPTOR(void, _longjmp, uptr *env, int val) { TSAN_INTERCEPTOR(void*, malloc, uptr size) { if (in_symbolizer()) return InternalAlloc(size); + if (DlsymAlloc::Use()) + return DlsymAlloc::Allocate(size); void *p = 0; { SCOPED_INTERCEPTOR_RAW(malloc, size); @@ -677,12 +688,14 @@ TSAN_INTERCEPTOR(void*, __libc_memalign, uptr align, uptr sz) { return user_memalign(thr, pc, align, sz); } -TSAN_INTERCEPTOR(void*, calloc, uptr size, uptr n) { +TSAN_INTERCEPTOR(void *, calloc, uptr n, uptr size) { if (in_symbolizer()) - return InternalCalloc(size, n); + return InternalCalloc(n, size); + if (DlsymAlloc::Use()) + return DlsymAlloc::Callocate(n, size); void *p = 0; { - SCOPED_INTERCEPTOR_RAW(calloc, size, n); + SCOPED_INTERCEPTOR_RAW(calloc, n, size); p = user_calloc(thr, pc, size, n); } invoke_malloc_hook(p, n * size); @@ -692,6 +705,8 @@ TSAN_INTERCEPTOR(void*, calloc, uptr size, uptr n) { TSAN_INTERCEPTOR(void*, realloc, void *p, uptr size) { if (in_symbolizer()) return InternalRealloc(p, size); + if (DlsymAlloc::Use() || DlsymAlloc::PointerIsMine(p)) + return DlsymAlloc::Realloc(p, size); if (p) invoke_free_hook(p); { @@ -702,13 +717,13 @@ TSAN_INTERCEPTOR(void*, realloc, void *p, uptr size) { return p; } -TSAN_INTERCEPTOR(void*, reallocarray, void *p, uptr size, uptr n) { +TSAN_INTERCEPTOR(void *, reallocarray, void *p, uptr n, uptr size) { if (in_symbolizer()) - return InternalReallocArray(p, size, n); + return InternalReallocArray(p, n, size); if (p) invoke_free_hook(p); { - SCOPED_INTERCEPTOR_RAW(reallocarray, p, size, n); + SCOPED_INTERCEPTOR_RAW(reallocarray, p, n, size); p = user_reallocarray(thr, pc, p, size, n); } invoke_malloc_hook(p, size); @@ -716,20 +731,24 @@ TSAN_INTERCEPTOR(void*, reallocarray, void *p, uptr size, uptr n) { } TSAN_INTERCEPTOR(void, free, void *p) { - if (p == 0) + if (UNLIKELY(!p)) return; if (in_symbolizer()) return InternalFree(p); + if (DlsymAlloc::PointerIsMine(p)) + return DlsymAlloc::Free(p); invoke_free_hook(p); SCOPED_INTERCEPTOR_RAW(free, p); user_free(thr, pc, p); } TSAN_INTERCEPTOR(void, cfree, void *p) { - if (p == 0) + if (UNLIKELY(!p)) return; if (in_symbolizer()) return InternalFree(p); + if (DlsymAlloc::PointerIsMine(p)) + return DlsymAlloc::Free(p); invoke_free_hook(p); SCOPED_INTERCEPTOR_RAW(cfree, p); user_free(thr, pc, p); @@ -1087,7 +1106,18 @@ TSAN_INTERCEPTOR(int, pthread_join, void *th, void **ret) { return res; } -DEFINE_REAL_PTHREAD_FUNCTIONS +// DEFINE_INTERNAL_PTHREAD_FUNCTIONS +namespace __sanitizer { +int internal_pthread_create(void *th, void *attr, void *(*callback)(void *), + void *param) { + ScopedIgnoreInterceptors ignore; + return REAL(pthread_create)(th, attr, callback, param); +} +int internal_pthread_join(void *th, void **ret) { + ScopedIgnoreInterceptors ignore; + return REAL(pthread_join)(th, ret); +} +} // namespace __sanitizer TSAN_INTERCEPTOR(int, pthread_detach, void *th) { SCOPED_INTERCEPTOR_RAW(pthread_detach, th); @@ -1340,7 +1370,7 @@ TSAN_INTERCEPTOR(int, pthread_mutex_destroy, void *m) { TSAN_INTERCEPTOR(int, pthread_mutex_lock, void *m) { SCOPED_TSAN_INTERCEPTOR(pthread_mutex_lock, m); MutexPreLock(thr, pc, (uptr)m); - int res = REAL(pthread_mutex_lock)(m); + int res = BLOCK_REAL(pthread_mutex_lock)(m); if (res == errno_EOWNERDEAD) MutexRepair(thr, pc, (uptr)m); if (res == 0 || res == errno_EOWNERDEAD) @@ -1380,6 +1410,22 @@ TSAN_INTERCEPTOR(int, pthread_mutex_unlock, void *m) { return res; } +#if SANITIZER_LINUX +TSAN_INTERCEPTOR(int, pthread_mutex_clocklock, void *m, + __sanitizer_clockid_t clock, void *abstime) { + SCOPED_TSAN_INTERCEPTOR(pthread_mutex_clocklock, m, clock, abstime); + MutexPreLock(thr, pc, (uptr)m); + int res = BLOCK_REAL(pthread_mutex_clocklock)(m, clock, abstime); + if (res == errno_EOWNERDEAD) + MutexRepair(thr, pc, (uptr)m); + if (res == 0 || res == errno_EOWNERDEAD) + MutexPostLock(thr, pc, (uptr)m); + if (res == errno_EINVAL) + MutexInvalidAccess(thr, pc, (uptr)m); + return res; +} +#endif + #if SANITIZER_GLIBC # if !__GLIBC_PREREQ(2, 34) // glibc 2.34 applies a non-default version for the two functions. They are no @@ -1387,7 +1433,7 @@ TSAN_INTERCEPTOR(int, pthread_mutex_unlock, void *m) { TSAN_INTERCEPTOR(int, __pthread_mutex_lock, void *m) { SCOPED_TSAN_INTERCEPTOR(__pthread_mutex_lock, m); MutexPreLock(thr, pc, (uptr)m); - int res = REAL(__pthread_mutex_lock)(m); + int res = BLOCK_REAL(__pthread_mutex_lock)(m); if (res == errno_EOWNERDEAD) MutexRepair(thr, pc, (uptr)m); if (res == 0 || res == errno_EOWNERDEAD) @@ -1430,7 +1476,7 @@ TSAN_INTERCEPTOR(int, pthread_spin_destroy, void *m) { TSAN_INTERCEPTOR(int, pthread_spin_lock, void *m) { SCOPED_TSAN_INTERCEPTOR(pthread_spin_lock, m); MutexPreLock(thr, pc, (uptr)m); - int res = REAL(pthread_spin_lock)(m); + int res = BLOCK_REAL(pthread_spin_lock)(m); if (res == 0) { MutexPostLock(thr, pc, (uptr)m); } @@ -1505,7 +1551,7 @@ TSAN_INTERCEPTOR(int, pthread_rwlock_timedrdlock, void *m, void *abstime) { TSAN_INTERCEPTOR(int, pthread_rwlock_wrlock, void *m) { SCOPED_TSAN_INTERCEPTOR(pthread_rwlock_wrlock, m); MutexPreLock(thr, pc, (uptr)m); - int res = REAL(pthread_rwlock_wrlock)(m); + int res = BLOCK_REAL(pthread_rwlock_wrlock)(m); if (res == 0) { MutexPostLock(thr, pc, (uptr)m); } @@ -1597,57 +1643,60 @@ TSAN_INTERCEPTOR(int, __fxstat, int version, int fd, void *buf) { FdAccess(thr, pc, fd); return REAL(__fxstat)(version, fd, buf); } -#define TSAN_MAYBE_INTERCEPT___FXSTAT TSAN_INTERCEPT(__fxstat) + +TSAN_INTERCEPTOR(int, __fxstat64, int version, int fd, void *buf) { + SCOPED_TSAN_INTERCEPTOR(__fxstat64, version, fd, buf); + if (fd > 0) + FdAccess(thr, pc, fd); + return REAL(__fxstat64)(version, fd, buf); +} +#define TSAN_MAYBE_INTERCEPT___FXSTAT TSAN_INTERCEPT(__fxstat); TSAN_INTERCEPT(__fxstat64) #else #define TSAN_MAYBE_INTERCEPT___FXSTAT #endif +#if !SANITIZER_GLIBC || __GLIBC_PREREQ(2, 33) TSAN_INTERCEPTOR(int, fstat, int fd, void *buf) { -#if SANITIZER_GLIBC - SCOPED_TSAN_INTERCEPTOR(__fxstat, 0, fd, buf); - if (fd > 0) - FdAccess(thr, pc, fd); - return REAL(__fxstat)(0, fd, buf); -#else SCOPED_TSAN_INTERCEPTOR(fstat, fd, buf); if (fd > 0) FdAccess(thr, pc, fd); return REAL(fstat)(fd, buf); -#endif } - -#if SANITIZER_GLIBC -TSAN_INTERCEPTOR(int, __fxstat64, int version, int fd, void *buf) { - SCOPED_TSAN_INTERCEPTOR(__fxstat64, version, fd, buf); - if (fd > 0) - FdAccess(thr, pc, fd); - return REAL(__fxstat64)(version, fd, buf); -} -#define TSAN_MAYBE_INTERCEPT___FXSTAT64 TSAN_INTERCEPT(__fxstat64) +# define TSAN_MAYBE_INTERCEPT_FSTAT TSAN_INTERCEPT(fstat) #else -#define TSAN_MAYBE_INTERCEPT___FXSTAT64 +# define TSAN_MAYBE_INTERCEPT_FSTAT #endif -#if SANITIZER_GLIBC +#if __GLIBC_PREREQ(2, 33) TSAN_INTERCEPTOR(int, fstat64, int fd, void *buf) { - SCOPED_TSAN_INTERCEPTOR(__fxstat64, 0, fd, buf); + SCOPED_TSAN_INTERCEPTOR(fstat64, fd, buf); if (fd > 0) FdAccess(thr, pc, fd); - return REAL(__fxstat64)(0, fd, buf); + return REAL(fstat64)(fd, buf); } -#define TSAN_MAYBE_INTERCEPT_FSTAT64 TSAN_INTERCEPT(fstat64) +# define TSAN_MAYBE_INTERCEPT_FSTAT64 TSAN_INTERCEPT(fstat64) #else -#define TSAN_MAYBE_INTERCEPT_FSTAT64 +# define TSAN_MAYBE_INTERCEPT_FSTAT64 #endif TSAN_INTERCEPTOR(int, open, const char *name, int oflag, ...) { - va_list ap; - va_start(ap, oflag); - mode_t mode = va_arg(ap, int); - va_end(ap); + mode_t mode = 0; + if (OpenReadsVaArgs(oflag)) { + va_list ap; + va_start(ap, oflag); + mode = va_arg(ap, int); + va_end(ap); + } + SCOPED_TSAN_INTERCEPTOR(open, name, oflag, mode); READ_STRING(thr, pc, name, 0); - int fd = REAL(open)(name, oflag, mode); + + int fd; + if (OpenReadsVaArgs(oflag)) + fd = REAL(open)(name, oflag, mode); + else + fd = REAL(open)(name, oflag); + if (fd >= 0) FdFileCreate(thr, pc, fd); return fd; @@ -2657,6 +2706,25 @@ static USED void syscall_fd_release(uptr pc, int fd) { FdRelease(thr, pc, fd); } +static USED void sycall_blocking_start() { + DPrintf("sycall_blocking_start()\n"); + ThreadState *thr = cur_thread(); + EnterBlockingFunc(thr); + // When we are in a "blocking call", we process signals asynchronously + // (right when they arrive). In this context we do not expect to be + // executing any user/runtime code. The known interceptor sequence when + // this is not true is: pthread_join -> munmap(stack). It's fine + // to ignore munmap in this case -- we handle stack shadow separately. + thr->ignore_interceptors++; +} + +static USED void sycall_blocking_end() { + DPrintf("sycall_blocking_end()\n"); + ThreadState *thr = cur_thread(); + thr->ignore_interceptors--; + atomic_store(&thr->in_blocking_func, 0, memory_order_relaxed); +} + static void syscall_pre_fork(uptr pc) { ForkBefore(cur_thread(), pc); } static void syscall_post_fork(uptr pc, int pid) { @@ -2711,6 +2779,9 @@ static void syscall_post_fork(uptr pc, int pid) { #define COMMON_SYSCALL_POST_FORK(res) \ syscall_post_fork(GET_CALLER_PC(), res) +#define COMMON_SYSCALL_BLOCKING_START() sycall_blocking_start() +#define COMMON_SYSCALL_BLOCKING_END() sycall_blocking_end() + #include "sanitizer_common/sanitizer_common_syscalls.inc" #include "sanitizer_common/sanitizer_syscalls_netbsd.inc" @@ -2845,8 +2916,21 @@ void InitializeInterceptors() { REAL(memcpy) = internal_memcpy; #endif + __interception::DoesNotSupportStaticLinking(); + new(interceptor_ctx()) InterceptorContext(); + // Interpose __tls_get_addr before the common interposers. This is needed + // because dlsym() may call malloc on failure which could result in other + // interposed functions being called that could eventually make use of TLS. +#ifdef NEED_TLS_GET_ADDR +# if !SANITIZER_S390 + TSAN_INTERCEPT(__tls_get_addr); +# else + TSAN_INTERCEPT(__tls_get_addr_internal); + TSAN_INTERCEPT(__tls_get_offset); +# endif +#endif InitializeCommonInterceptors(); InitializeSignalInterceptors(); InitializeLibdispatchInterceptors(); @@ -2902,6 +2986,9 @@ void InitializeInterceptors() { TSAN_INTERCEPT(pthread_mutex_trylock); TSAN_INTERCEPT(pthread_mutex_timedlock); TSAN_INTERCEPT(pthread_mutex_unlock); +#if SANITIZER_LINUX + TSAN_INTERCEPT(pthread_mutex_clocklock); +#endif #if SANITIZER_GLIBC # if !__GLIBC_PREREQ(2, 34) TSAN_INTERCEPT(__pthread_mutex_lock); @@ -2931,10 +3018,9 @@ void InitializeInterceptors() { TSAN_INTERCEPT(pthread_once); - TSAN_INTERCEPT(fstat); TSAN_MAYBE_INTERCEPT___FXSTAT; + TSAN_MAYBE_INTERCEPT_FSTAT; TSAN_MAYBE_INTERCEPT_FSTAT64; - TSAN_MAYBE_INTERCEPT___FXSTAT64; TSAN_INTERCEPT(open); TSAN_MAYBE_INTERCEPT_OPEN64; TSAN_INTERCEPT(creat); @@ -2991,15 +3077,6 @@ void InitializeInterceptors() { TSAN_INTERCEPT(__cxa_atexit); TSAN_INTERCEPT(_exit); -#ifdef NEED_TLS_GET_ADDR -#if !SANITIZER_S390 - TSAN_INTERCEPT(__tls_get_addr); -#else - TSAN_INTERCEPT(__tls_get_addr_internal); - TSAN_INTERCEPT(__tls_get_offset); -#endif -#endif - TSAN_MAYBE_INTERCEPT__LWP_EXIT; TSAN_MAYBE_INTERCEPT_THR_EXIT; diff --git a/libsanitizer/tsan/tsan_interface_ann.cpp b/libsanitizer/tsan/tsan_interface_ann.cpp index 5154662..befd6a3 100644 --- a/libsanitizer/tsan/tsan_interface_ann.cpp +++ b/libsanitizer/tsan/tsan_interface_ann.cpp @@ -76,7 +76,7 @@ struct DynamicAnnContext { }; static DynamicAnnContext *dyn_ann_ctx; -static char dyn_ann_ctx_placeholder[sizeof(DynamicAnnContext)] ALIGNED(64); +alignas(64) static char dyn_ann_ctx_placeholder[sizeof(DynamicAnnContext)]; static void AddExpectRace(ExpectRace *list, char *f, int l, uptr addr, uptr size, char *desc) { diff --git a/libsanitizer/tsan/tsan_mman.cpp b/libsanitizer/tsan/tsan_mman.cpp index 6f118e0..0ea83fb 100644 --- a/libsanitizer/tsan/tsan_mman.cpp +++ b/libsanitizer/tsan/tsan_mman.cpp @@ -9,17 +9,19 @@ // This file is a part of ThreadSanitizer (TSan), a race detector. // //===----------------------------------------------------------------------===// +#include "tsan_mman.h" + #include "sanitizer_common/sanitizer_allocator_checks.h" #include "sanitizer_common/sanitizer_allocator_interface.h" #include "sanitizer_common/sanitizer_allocator_report.h" #include "sanitizer_common/sanitizer_common.h" #include "sanitizer_common/sanitizer_errno.h" #include "sanitizer_common/sanitizer_placement_new.h" +#include "sanitizer_common/sanitizer_stackdepot.h" +#include "tsan_flags.h" #include "tsan_interface.h" -#include "tsan_mman.h" -#include "tsan_rtl.h" #include "tsan_report.h" -#include "tsan_flags.h" +#include "tsan_rtl.h" namespace __tsan { @@ -52,7 +54,7 @@ struct MapUnmapCallback { } }; -static char allocator_placeholder[sizeof(Allocator)] ALIGNED(64); +alignas(64) static char allocator_placeholder[sizeof(Allocator)]; Allocator *allocator() { return reinterpret_cast<Allocator*>(&allocator_placeholder); } @@ -73,7 +75,7 @@ struct GlobalProc { internal_alloc_mtx(MutexTypeInternalAlloc) {} }; -static char global_proc_placeholder[sizeof(GlobalProc)] ALIGNED(64); +alignas(64) static char global_proc_placeholder[sizeof(GlobalProc)]; GlobalProc *global_proc() { return reinterpret_cast<GlobalProc*>(&global_proc_placeholder); } @@ -115,12 +117,21 @@ ScopedGlobalProcessor::~ScopedGlobalProcessor() { gp->mtx.Unlock(); } -void AllocatorLock() SANITIZER_NO_THREAD_SAFETY_ANALYSIS { +void AllocatorLockBeforeFork() SANITIZER_NO_THREAD_SAFETY_ANALYSIS { global_proc()->internal_alloc_mtx.Lock(); InternalAllocatorLock(); -} - -void AllocatorUnlock() SANITIZER_NO_THREAD_SAFETY_ANALYSIS { +#if !SANITIZER_APPLE + // OS X allocates from hooks, see 6a3958247a. + allocator()->ForceLock(); + StackDepotLockBeforeFork(); +#endif +} + +void AllocatorUnlockAfterFork(bool child) SANITIZER_NO_THREAD_SAFETY_ANALYSIS { +#if !SANITIZER_APPLE + StackDepotUnlockAfterFork(child); + allocator()->ForceUnlock(); +#endif InternalAllocatorUnlock(); global_proc()->internal_alloc_mtx.Unlock(); } @@ -241,7 +252,7 @@ void *user_reallocarray(ThreadState *thr, uptr pc, void *p, uptr size, uptr n) { if (AllocatorMayReturnNull()) return SetErrnoOnNull(nullptr); GET_STACK_TRACE_FATAL(thr, pc); - ReportReallocArrayOverflow(size, n, &stack); + ReportReallocArrayOverflow(n, size, &stack); } return user_realloc(thr, pc, p, size * n); } diff --git a/libsanitizer/tsan/tsan_mman.h b/libsanitizer/tsan/tsan_mman.h index 2095f28..f01bbc4 100644 --- a/libsanitizer/tsan/tsan_mman.h +++ b/libsanitizer/tsan/tsan_mman.h @@ -24,8 +24,8 @@ void ReplaceSystemMalloc(); void AllocatorProcStart(Processor *proc); void AllocatorProcFinish(Processor *proc); void AllocatorPrintStats(); -void AllocatorLock(); -void AllocatorUnlock(); +void AllocatorLockBeforeFork(); +void AllocatorUnlockAfterFork(bool child); void GlobalProcessorLock(); void GlobalProcessorUnlock(); diff --git a/libsanitizer/tsan/tsan_platform.h b/libsanitizer/tsan/tsan_platform.h index d9ed397..377f8ae 100644 --- a/libsanitizer/tsan/tsan_platform.h +++ b/libsanitizer/tsan/tsan_platform.h @@ -413,18 +413,18 @@ struct MappingRiscv64_39 { /* C/C++ on linux/riscv64 (48-bit VMA) -0000 0000 1000 - 0500 0000 0000: main binary ( 5 TB) +0000 0000 1000 - 0400 0000 0000: main binary ( 4 TB) 0500 0000 0000 - 2000 0000 0000: - 2000 0000 0000 - 4000 0000 0000: shadow memory (32 TB) 4000 0000 0000 - 4800 0000 0000: metainfo ( 8 TB) 4800 0000 0000 - 5555 5555 5000: - 5555 5555 5000 - 5a00 0000 0000: main binary (PIE) (~5 TB) 5a00 0000 0000 - 7a00 0000 0000: - -7a00 0000 0000 - 7fff ffff ffff: libraries and main thread stack ( 5 TB) +7a00 0000 0000 - 7fff ffff ffff: libraries and main thread stack ( 6 TB) */ struct MappingRiscv64_48 { static const uptr kLoAppMemBeg = 0x000000001000ull; - static const uptr kLoAppMemEnd = 0x050000000000ull; + static const uptr kLoAppMemEnd = 0x040000000000ull; static const uptr kShadowBeg = 0x200000000000ull; static const uptr kShadowEnd = 0x400000000000ull; static const uptr kMetaShadowBeg = 0x400000000000ull; @@ -622,6 +622,35 @@ struct MappingGoAarch64 { static const uptr kShadowAdd = 0x200000000000ull; }; +/* Go on linux/loongarch64 (47-bit VMA) +0000 0000 1000 - 0000 1000 0000: executable +0000 1000 0000 - 00c0 0000 0000: - +00c0 0000 0000 - 00e0 0000 0000: heap +00e0 0000 0000 - 2000 0000 0000: - +2000 0000 0000 - 2800 0000 0000: shadow +2800 0000 0000 - 3000 0000 0000: - +3000 0000 0000 - 3200 0000 0000: metainfo (memory blocks and sync objects) +3200 0000 0000 - 8000 0000 0000: - +*/ +struct MappingGoLoongArch64_47 { + static const uptr kMetaShadowBeg = 0x300000000000ull; + static const uptr kMetaShadowEnd = 0x320000000000ull; + static const uptr kShadowBeg = 0x200000000000ull; + static const uptr kShadowEnd = 0x280000000000ull; + static const uptr kLoAppMemBeg = 0x000000001000ull; + static const uptr kLoAppMemEnd = 0x00e000000000ull; + static const uptr kMidAppMemBeg = 0; + static const uptr kMidAppMemEnd = 0; + static const uptr kHiAppMemBeg = 0; + static const uptr kHiAppMemEnd = 0; + static const uptr kHeapMemBeg = 0; + static const uptr kHeapMemEnd = 0; + static const uptr kVdsoBeg = 0; + static const uptr kShadowMsk = 0; + static const uptr kShadowXor = 0; + static const uptr kShadowAdd = 0x200000000000ull; +}; + /* Go on linux/mips64 (47-bit VMA) 0000 0000 1000 - 0000 1000 0000: executable @@ -697,6 +726,8 @@ ALWAYS_INLINE auto SelectMapping(Arg arg) { return Func::template Apply<MappingGoS390x>(arg); # elif defined(__aarch64__) return Func::template Apply<MappingGoAarch64>(arg); +# elif defined(__loongarch_lp64) + return Func::template Apply<MappingGoLoongArch64_47>(arg); # elif SANITIZER_WINDOWS return Func::template Apply<MappingGoWindows>(arg); # else @@ -765,6 +796,7 @@ void ForEachMapping() { Func::template Apply<MappingGoPPC64_46>(); Func::template Apply<MappingGoPPC64_47>(); Func::template Apply<MappingGoAarch64>(); + Func::template Apply<MappingGoLoongArch64_47>(); Func::template Apply<MappingGoMips64_47>(); Func::template Apply<MappingGoS390x>(); } @@ -967,7 +999,7 @@ struct RestoreAddrImpl { Mapping::kMidAppMemEnd, Mapping::kHiAppMemBeg, Mapping::kHiAppMemEnd, Mapping::kHeapMemBeg, Mapping::kHeapMemEnd, }; - const uptr indicator = 0x0f0000000000ull; + const uptr indicator = 0x0e0000000000ull; const uptr ind_lsb = 1ull << LeastSignificantSetBitIndex(indicator); for (uptr i = 0; i < ARRAY_SIZE(ranges); i += 2) { uptr beg = ranges[i]; @@ -992,7 +1024,7 @@ inline uptr RestoreAddr(uptr addr) { void InitializePlatform(); void InitializePlatformEarly(); -void CheckAndProtect(); +bool CheckAndProtect(bool protect, bool ignore_heap, bool print_warnings); void InitializeShadowMemoryPlatform(); void WriteMemoryProfile(char *buf, uptr buf_size, u64 uptime_ns); int ExtractResolvFDs(void *state, int *fds, int nfd); diff --git a/libsanitizer/tsan/tsan_platform_linux.cpp b/libsanitizer/tsan/tsan_platform_linux.cpp index 369509e..3e08a1b 100644 --- a/libsanitizer/tsan/tsan_platform_linux.cpp +++ b/libsanitizer/tsan/tsan_platform_linux.cpp @@ -214,6 +214,97 @@ void InitializeShadowMemoryPlatform() { #endif // #if !SANITIZER_GO +# if !SANITIZER_GO +static void ReExecIfNeeded(bool ignore_heap) { + // Go maps shadow memory lazily and works fine with limited address space. + // Unlimited stack is not a problem as well, because the executable + // is not compiled with -pie. + bool reexec = false; + // TSan doesn't play well with unlimited stack size (as stack + // overlaps with shadow memory). If we detect unlimited stack size, + // we re-exec the program with limited stack size as a best effort. + if (StackSizeIsUnlimited()) { + const uptr kMaxStackSize = 32 * 1024 * 1024; + VReport(1, + "Program is run with unlimited stack size, which wouldn't " + "work with ThreadSanitizer.\n" + "Re-execing with stack size limited to %zd bytes.\n", + kMaxStackSize); + SetStackSizeLimitInBytes(kMaxStackSize); + reexec = true; + } + + if (!AddressSpaceIsUnlimited()) { + Report( + "WARNING: Program is run with limited virtual address space," + " which wouldn't work with ThreadSanitizer.\n"); + Report("Re-execing with unlimited virtual address space.\n"); + SetAddressSpaceUnlimited(); + reexec = true; + } + +# if SANITIZER_LINUX +# if SANITIZER_ANDROID && (defined(__aarch64__) || defined(__x86_64__)) + // ASLR personality check. + int old_personality = personality(0xffffffff); + bool aslr_on = + (old_personality != -1) && ((old_personality & ADDR_NO_RANDOMIZE) == 0); + + // After patch "arm64: mm: support ARCH_MMAP_RND_BITS." is introduced in + // linux kernel, the random gap between stack and mapped area is increased + // from 128M to 36G on 39-bit aarch64. As it is almost impossible to cover + // this big range, we should disable randomized virtual space on aarch64. + if (aslr_on) { + VReport(1, + "WARNING: Program is run with randomized virtual address " + "space, which wouldn't work with ThreadSanitizer on Android.\n" + "Re-execing with fixed virtual address space.\n"); + CHECK_NE(personality(old_personality | ADDR_NO_RANDOMIZE), -1); + reexec = true; + } +# endif + + if (reexec) { + // Don't check the address space since we're going to re-exec anyway. + } else if (!CheckAndProtect(false, ignore_heap, false)) { + // ASLR personality check. + // N.B. 'personality' is sometimes forbidden by sandboxes, so we only call + // this as a last resort (when the memory mapping is incompatible and TSan + // would fail anyway). + int old_personality = personality(0xffffffff); + bool aslr_on = + (old_personality != -1) && ((old_personality & ADDR_NO_RANDOMIZE) == 0); + + if (aslr_on) { + // Disable ASLR if the memory layout was incompatible. + // Alternatively, we could just keep re-execing until we get lucky + // with a compatible randomized layout, but the risk is that if it's + // not an ASLR-related issue, we will be stuck in an infinite loop of + // re-execing (unless we change ReExec to pass a parameter of the + // number of retries allowed.) + VReport(1, + "WARNING: ThreadSanitizer: memory layout is incompatible, " + "possibly due to high-entropy ASLR.\n" + "Re-execing with fixed virtual address space.\n" + "N.B. reducing ASLR entropy is preferable.\n"); + CHECK_NE(personality(old_personality | ADDR_NO_RANDOMIZE), -1); + reexec = true; + } else { + Printf( + "FATAL: ThreadSanitizer: memory layout is incompatible, " + "even though ASLR is disabled.\n" + "Please file a bug.\n"); + DumpProcessMap(); + Die(); + } + } +# endif // SANITIZER_LINUX + + if (reexec) + ReExec(); +} +# endif + void InitializePlatformEarly() { vmaSize = (MostSignificantSetBitIndex(GET_CURRENT_FRAME()) + 1); @@ -238,7 +329,13 @@ void InitializePlatformEarly() { Printf("FATAL: Found %zd - Supported 47\n", vmaSize); Die(); } -# endif +# else + if (vmaSize != 47) { + Printf("FATAL: ThreadSanitizer: unsupported VMA range\n"); + Printf("FATAL: Found %zd - Supported 47\n", vmaSize); + Die(); + } +# endif #elif defined(__powerpc64__) # if !SANITIZER_GO if (vmaSize != 44 && vmaSize != 46 && vmaSize != 47) { @@ -278,6 +375,11 @@ void InitializePlatformEarly() { } # endif # endif + +# if !SANITIZER_GO + // Heap has not been allocated yet + ReExecIfNeeded(false); +# endif } void InitializePlatform() { @@ -288,53 +390,34 @@ void InitializePlatform() { // is not compiled with -pie. #if !SANITIZER_GO { - bool reexec = false; - // TSan doesn't play well with unlimited stack size (as stack - // overlaps with shadow memory). If we detect unlimited stack size, - // we re-exec the program with limited stack size as a best effort. - if (StackSizeIsUnlimited()) { - const uptr kMaxStackSize = 32 * 1024 * 1024; - VReport(1, "Program is run with unlimited stack size, which wouldn't " - "work with ThreadSanitizer.\n" - "Re-execing with stack size limited to %zd bytes.\n", - kMaxStackSize); - SetStackSizeLimitInBytes(kMaxStackSize); - reexec = true; - } - - if (!AddressSpaceIsUnlimited()) { - Report("WARNING: Program is run with limited virtual address space," - " which wouldn't work with ThreadSanitizer.\n"); - Report("Re-execing with unlimited virtual address space.\n"); - SetAddressSpaceUnlimited(); - reexec = true; - } -#if SANITIZER_ANDROID && (defined(__aarch64__) || defined(__x86_64__)) - // After patch "arm64: mm: support ARCH_MMAP_RND_BITS." is introduced in - // linux kernel, the random gap between stack and mapped area is increased - // from 128M to 36G on 39-bit aarch64. As it is almost impossible to cover - // this big range, we should disable randomized virtual space on aarch64. - // ASLR personality check. - int old_personality = personality(0xffffffff); - if (old_personality != -1 && (old_personality & ADDR_NO_RANDOMIZE) == 0) { - VReport(1, "WARNING: Program is run with randomized virtual address " - "space, which wouldn't work with ThreadSanitizer.\n" - "Re-execing with fixed virtual address space.\n"); - CHECK_NE(personality(old_personality | ADDR_NO_RANDOMIZE), -1); - reexec = true; - } - -#endif -#if SANITIZER_LINUX && (defined(__aarch64__) || defined(__loongarch_lp64)) +# if SANITIZER_LINUX && (defined(__aarch64__) || defined(__loongarch_lp64)) // Initialize the xor key used in {sig}{set,long}jump. InitializeLongjmpXorKey(); -#endif - if (reexec) - ReExec(); +# endif + } + + // We called ReExecIfNeeded() in InitializePlatformEarly(), but there are + // intervening allocations that result in an edge case: + // 1) InitializePlatformEarly(): memory layout is compatible + // 2) Intervening allocations happen + // 3) InitializePlatform(): memory layout is incompatible and fails + // CheckAndProtect() +# if !SANITIZER_GO + // Heap has already been allocated + ReExecIfNeeded(true); +# endif + + // Earlier initialization steps already re-exec'ed until we got a compatible + // memory layout, so we don't expect any more issues here. + if (!CheckAndProtect(true, true, true)) { + Printf( + "FATAL: ThreadSanitizer: unexpectedly found incompatible memory " + "layout.\n"); + Printf("FATAL: Please file a bug.\n"); + DumpProcessMap(); + Die(); } - CheckAndProtect(); - InitTlsSize(); #endif // !SANITIZER_GO } diff --git a/libsanitizer/tsan/tsan_platform_mac.cpp b/libsanitizer/tsan/tsan_platform_mac.cpp index 1aac0fb..eb344df 100644 --- a/libsanitizer/tsan/tsan_platform_mac.cpp +++ b/libsanitizer/tsan/tsan_platform_mac.cpp @@ -46,8 +46,8 @@ namespace __tsan { #if !SANITIZER_GO -static char main_thread_state[sizeof(ThreadState)] ALIGNED( - SANITIZER_CACHE_LINE_SIZE); +alignas(SANITIZER_CACHE_LINE_SIZE) static char main_thread_state[sizeof( + ThreadState)]; static ThreadState *dead_thread_state; static pthread_key_t thread_state_key; @@ -239,7 +239,10 @@ static uptr longjmp_xor_key = 0; void InitializePlatform() { DisableCoreDumperIfNecessary(); #if !SANITIZER_GO - CheckAndProtect(); + if (!CheckAndProtect(true, true, true)) { + Printf("FATAL: ThreadSanitizer: found incompatible memory layout.\n"); + Die(); + } InitializeThreadStateStorage(); diff --git a/libsanitizer/tsan/tsan_platform_posix.cpp b/libsanitizer/tsan/tsan_platform_posix.cpp index e7dcd66..7d5a992 100644 --- a/libsanitizer/tsan/tsan_platform_posix.cpp +++ b/libsanitizer/tsan/tsan_platform_posix.cpp @@ -94,22 +94,51 @@ static void ProtectRange(uptr beg, uptr end) { } } -void CheckAndProtect() { +// CheckAndProtect will check if the memory layout is compatible with TSan. +// Optionally (if 'protect' is true), it will set the memory regions between +// app memory to be inaccessible. +// 'ignore_heap' means it will not consider heap memory allocations to be a +// conflict. Set this based on whether we are calling CheckAndProtect before +// or after the allocator has initialized the heap. +bool CheckAndProtect(bool protect, bool ignore_heap, bool print_warnings) { // Ensure that the binary is indeed compiled with -pie. MemoryMappingLayout proc_maps(true); MemoryMappedSegment segment; while (proc_maps.Next(&segment)) { - if (IsAppMem(segment.start)) continue; + if (segment.start >= HeapMemBeg() && segment.end <= HeapEnd()) { + if (ignore_heap) { + continue; + } else { + return false; + } + } + + // Note: IsAppMem includes if it is heap memory, hence we must + // put this check after the heap bounds check. + if (IsAppMem(segment.start) && IsAppMem(segment.end - 1)) + continue; + + // Guard page after the heap end if (segment.start >= HeapMemEnd() && segment.start < HeapEnd()) continue; + if (segment.protection == 0) // Zero page or mprotected. continue; + if (segment.start >= VdsoBeg()) // vdso break; - Printf("FATAL: ThreadSanitizer: unexpected memory mapping 0x%zx-0x%zx\n", - segment.start, segment.end); - Die(); + + // Debug output can break tests. Suppress this message in most cases. + if (print_warnings) + Printf( + "WARNING: ThreadSanitizer: unexpected memory mapping 0x%zx-0x%zx\n", + segment.start, segment.end); + + return false; } + if (!protect) + return true; + # if SANITIZER_IOS && !SANITIZER_IOSSIM ProtectRange(HeapMemEnd(), ShadowBeg()); ProtectRange(ShadowEnd(), MetaShadowBeg()); @@ -135,8 +164,10 @@ void CheckAndProtect() { // Older s390x kernels may not support 5-level page tables. TryProtectRange(user_addr_max_l4, user_addr_max_l5); #endif + + return true; } -#endif +# endif } // namespace __tsan diff --git a/libsanitizer/tsan/tsan_preinit.cpp b/libsanitizer/tsan/tsan_preinit.cpp index 205bdbf..69700f2 100644 --- a/libsanitizer/tsan/tsan_preinit.cpp +++ b/libsanitizer/tsan/tsan_preinit.cpp @@ -16,11 +16,9 @@ #if SANITIZER_CAN_USE_PREINIT_ARRAY -// The symbol is called __local_tsan_preinit, because it's not intended to be -// exported. -// This code linked into the main executable when -fsanitize=thread is in -// the link flags. It can only use exported interface functions. -__attribute__((section(".preinit_array"), used)) -void (*__local_tsan_preinit)(void) = __tsan_init; +// This section is linked into the main executable when -fsanitize=thread is +// specified to perform initialization at a very early stage. +__attribute__((section(".preinit_array"), used)) static auto preinit = + __tsan_init; #endif diff --git a/libsanitizer/tsan/tsan_report.cpp b/libsanitizer/tsan/tsan_report.cpp index 35cb671..22ba428 100644 --- a/libsanitizer/tsan/tsan_report.cpp +++ b/libsanitizer/tsan/tsan_report.cpp @@ -273,26 +273,10 @@ static ReportStack *ChooseSummaryStack(const ReportDesc *rep) { return 0; } -static bool FrameIsInternal(const SymbolizedStack *frame) { - if (frame == 0) - return false; - const char *file = frame->info.file; - const char *module = frame->info.module; - if (file != 0 && - (internal_strstr(file, "tsan_interceptors_posix.cpp") || - internal_strstr(file, "tsan_interceptors_memintrinsics.cpp") || - internal_strstr(file, "sanitizer_common_interceptors.inc") || - internal_strstr(file, "tsan_interface_"))) - return true; - if (module != 0 && (internal_strstr(module, "libclang_rt.tsan_"))) - return true; - return false; -} - -static SymbolizedStack *SkipTsanInternalFrames(SymbolizedStack *frames) { - while (FrameIsInternal(frames) && frames->next) - frames = frames->next; - return frames; +static const SymbolizedStack *SkipTsanInternalFrames(SymbolizedStack *frames) { + if (const SymbolizedStack *f = SkipInternalFrames(frames)) + return f; + return frames; // Fallback to the top frame. } void PrintReport(const ReportDesc *rep) { @@ -366,7 +350,7 @@ void PrintReport(const ReportDesc *rep) { Printf(" And %d more similar thread leaks.\n\n", rep->count - 1); if (ReportStack *stack = ChooseSummaryStack(rep)) { - if (SymbolizedStack *frame = SkipTsanInternalFrames(stack->frames)) + if (const SymbolizedStack *frame = SkipTsanInternalFrames(stack->frames)) ReportErrorSummary(rep_typ_str, frame->info); } diff --git a/libsanitizer/tsan/tsan_rtl.cpp b/libsanitizer/tsan/tsan_rtl.cpp index fd9441d..5a2d39c 100644 --- a/libsanitizer/tsan/tsan_rtl.cpp +++ b/libsanitizer/tsan/tsan_rtl.cpp @@ -35,8 +35,10 @@ extern "C" void __tsan_resume() { __tsan_resumed = 1; } +#if SANITIZER_APPLE SANITIZER_WEAK_DEFAULT_IMPL void __tsan_test_only_on_fork() {} +#endif namespace __tsan { @@ -46,11 +48,10 @@ int (*on_finalize)(int); #endif #if !SANITIZER_GO && !SANITIZER_APPLE -__attribute__((tls_model("initial-exec"))) -THREADLOCAL char cur_thread_placeholder[sizeof(ThreadState)] ALIGNED( - SANITIZER_CACHE_LINE_SIZE); +alignas(SANITIZER_CACHE_LINE_SIZE) THREADLOCAL __attribute__((tls_model( + "initial-exec"))) char cur_thread_placeholder[sizeof(ThreadState)]; #endif -static char ctx_placeholder[sizeof(Context)] ALIGNED(SANITIZER_CACHE_LINE_SIZE); +alignas(SANITIZER_CACHE_LINE_SIZE) static char ctx_placeholder[sizeof(Context)]; Context *ctx; // Can be overriden by a front-end. @@ -805,6 +806,7 @@ int Finalize(ThreadState *thr) { #if !SANITIZER_GO void ForkBefore(ThreadState* thr, uptr pc) SANITIZER_NO_THREAD_SAFETY_ANALYSIS { + VReport(2, "BeforeFork tid: %llu\n", GetTid()); GlobalProcessorLock(); // Detaching from the slot makes OnUserFree skip writing to the shadow. // The slot will be locked so any attempts to use it will deadlock anyway. @@ -813,7 +815,7 @@ void ForkBefore(ThreadState* thr, uptr pc) SANITIZER_NO_THREAD_SAFETY_ANALYSIS { ctx->thread_registry.Lock(); ctx->slot_mtx.Lock(); ScopedErrorReportLock::Lock(); - AllocatorLock(); + AllocatorLockBeforeFork(); // Suppress all reports in the pthread_atfork callbacks. // Reports will deadlock on the report_mtx. // We could ignore sync operations as well, @@ -828,14 +830,17 @@ void ForkBefore(ThreadState* thr, uptr pc) SANITIZER_NO_THREAD_SAFETY_ANALYSIS { // Disables memory write in OnUserAlloc/Free. thr->ignore_reads_and_writes++; +# if SANITIZER_APPLE __tsan_test_only_on_fork(); +# endif } -static void ForkAfter(ThreadState* thr) SANITIZER_NO_THREAD_SAFETY_ANALYSIS { +static void ForkAfter(ThreadState* thr, + bool child) SANITIZER_NO_THREAD_SAFETY_ANALYSIS { thr->suppress_reports--; // Enabled in ForkBefore. thr->ignore_interceptors--; thr->ignore_reads_and_writes--; - AllocatorUnlock(); + AllocatorUnlockAfterFork(child); ScopedErrorReportLock::Unlock(); ctx->slot_mtx.Unlock(); ctx->thread_registry.Unlock(); @@ -843,12 +848,13 @@ static void ForkAfter(ThreadState* thr) SANITIZER_NO_THREAD_SAFETY_ANALYSIS { SlotAttachAndLock(thr); SlotUnlock(thr); GlobalProcessorUnlock(); + VReport(2, "AfterFork tid: %llu\n", GetTid()); } -void ForkParentAfter(ThreadState* thr, uptr pc) { ForkAfter(thr); } +void ForkParentAfter(ThreadState* thr, uptr pc) { ForkAfter(thr, false); } void ForkChildAfter(ThreadState* thr, uptr pc, bool start_thread) { - ForkAfter(thr); + ForkAfter(thr, true); u32 nthread = ctx->thread_registry.OnFork(thr->tid); VPrintf(1, "ThreadSanitizer: forked new process with pid %d," diff --git a/libsanitizer/tsan/tsan_rtl.h b/libsanitizer/tsan/tsan_rtl.h index de4ea0b..f48be8e 100644 --- a/libsanitizer/tsan/tsan_rtl.h +++ b/libsanitizer/tsan/tsan_rtl.h @@ -136,7 +136,7 @@ struct TidEpoch { Epoch epoch; }; -struct TidSlot { +struct alignas(SANITIZER_CACHE_LINE_SIZE) TidSlot { Mutex mtx; Sid sid; atomic_uint32_t raw_epoch; @@ -153,10 +153,10 @@ struct TidSlot { } TidSlot(); -} ALIGNED(SANITIZER_CACHE_LINE_SIZE); +}; // This struct is stored in TLS. -struct ThreadState { +struct alignas(SANITIZER_CACHE_LINE_SIZE) ThreadState { FastState fast_state; int ignore_sync; #if !SANITIZER_GO @@ -234,7 +234,7 @@ struct ThreadState { const ReportDesc *current_report; explicit ThreadState(Tid tid); -} ALIGNED(SANITIZER_CACHE_LINE_SIZE); +}; #if !SANITIZER_GO #if SANITIZER_APPLE || SANITIZER_ANDROID diff --git a/libsanitizer/tsan/tsan_rtl_aarch64.S b/libsanitizer/tsan/tsan_rtl_aarch64.S index c616265..7d920be 100644 --- a/libsanitizer/tsan/tsan_rtl_aarch64.S +++ b/libsanitizer/tsan/tsan_rtl_aarch64.S @@ -2,6 +2,7 @@ #if defined(__aarch64__) #include "sanitizer_common/sanitizer_asm.h" +#include "builtins/assembly.h" #if !defined(__APPLE__) .section .text @@ -16,6 +17,7 @@ ASM_HIDDEN(__tsan_setjmp) ASM_TYPE_FUNCTION(ASM_SYMBOL_INTERCEPTOR(setjmp)) ASM_SYMBOL_INTERCEPTOR(setjmp): CFI_STARTPROC + BTI_C // Save frame/link register stp x29, x30, [sp, -32]! @@ -66,6 +68,7 @@ ASM_SIZE(ASM_SYMBOL_INTERCEPTOR(setjmp)) ASM_TYPE_FUNCTION(ASM_SYMBOL_INTERCEPTOR(_setjmp)) ASM_SYMBOL_INTERCEPTOR(_setjmp): CFI_STARTPROC + BTI_C // Save frame/link register stp x29, x30, [sp, -32]! @@ -116,6 +119,7 @@ ASM_SIZE(ASM_SYMBOL_INTERCEPTOR(_setjmp)) ASM_TYPE_FUNCTION(ASM_SYMBOL_INTERCEPTOR(sigsetjmp)) ASM_SYMBOL_INTERCEPTOR(sigsetjmp): CFI_STARTPROC + BTI_C // Save frame/link register stp x29, x30, [sp, -32]! @@ -168,6 +172,7 @@ ASM_SIZE(ASM_SYMBOL_INTERCEPTOR(sigsetjmp)) ASM_TYPE_FUNCTION(ASM_SYMBOL_INTERCEPTOR(__sigsetjmp)) ASM_SYMBOL_INTERCEPTOR(__sigsetjmp): CFI_STARTPROC + BTI_C // Save frame/link register stp x29, x30, [sp, -32]! @@ -217,4 +222,6 @@ ASM_SIZE(ASM_SYMBOL_INTERCEPTOR(__sigsetjmp)) NO_EXEC_STACK_DIRECTIVE +GNU_PROPERTY_BTI_PAC + #endif diff --git a/libsanitizer/tsan/tsan_rtl_access.cpp b/libsanitizer/tsan/tsan_rtl_access.cpp index 8b20984..cf07686 100644 --- a/libsanitizer/tsan/tsan_rtl_access.cpp +++ b/libsanitizer/tsan/tsan_rtl_access.cpp @@ -672,22 +672,28 @@ void MemoryAccessRangeT(ThreadState* thr, uptr pc, uptr addr, uptr size) { #if SANITIZER_DEBUG if (!IsAppMem(addr)) { - Printf("Access to non app mem %zx\n", addr); + Printf("Access to non app mem start: %p\n", (void*)addr); DCHECK(IsAppMem(addr)); } if (!IsAppMem(addr + size - 1)) { - Printf("Access to non app mem %zx\n", addr + size - 1); + Printf("Access to non app mem end: %p\n", (void*)(addr + size - 1)); DCHECK(IsAppMem(addr + size - 1)); } if (!IsShadowMem(shadow_mem)) { - Printf("Bad shadow addr %p (%zx)\n", static_cast<void*>(shadow_mem), addr); + Printf("Bad shadow start addr: %p (%p)\n", shadow_mem, (void*)addr); DCHECK(IsShadowMem(shadow_mem)); } - if (!IsShadowMem(shadow_mem + size * kShadowCnt - 1)) { - Printf("Bad shadow addr %p (%zx)\n", - static_cast<void*>(shadow_mem + size * kShadowCnt - 1), - addr + size - 1); - DCHECK(IsShadowMem(shadow_mem + size * kShadowCnt - 1)); + + RawShadow* shadow_mem_end = reinterpret_cast<RawShadow*>( + reinterpret_cast<uptr>(shadow_mem) + size * kShadowMultiplier - 1); + if (!IsShadowMem(shadow_mem_end)) { + Printf("Bad shadow end addr: %p (%p)\n", shadow_mem_end, + (void*)(addr + size - 1)); + Printf( + "Shadow start addr (ok): %p (%p); size: 0x%zx; kShadowMultiplier: " + "%zx\n", + shadow_mem, (void*)addr, size, kShadowMultiplier); + DCHECK(IsShadowMem(shadow_mem_end)); } #endif diff --git a/libsanitizer/tsan/tsan_rtl_mutex.cpp b/libsanitizer/tsan/tsan_rtl_mutex.cpp index 2e97885..2a8aa19 100644 --- a/libsanitizer/tsan/tsan_rtl_mutex.cpp +++ b/libsanitizer/tsan/tsan_rtl_mutex.cpp @@ -446,9 +446,9 @@ void Acquire(ThreadState *thr, uptr pc, uptr addr) { if (!s) return; SlotLocker locker(thr); + ReadLock lock(&s->mtx); if (!s->clock) return; - ReadLock lock(&s->mtx); thr->clock.Acquire(s->clock); } diff --git a/libsanitizer/tsan/tsan_rtl_ppc64.S b/libsanitizer/tsan/tsan_rtl_ppc64.S index 9e533a7..8285e21 100644 --- a/libsanitizer/tsan/tsan_rtl_ppc64.S +++ b/libsanitizer/tsan/tsan_rtl_ppc64.S @@ -1,6 +1,5 @@ #include "tsan_ppc_regs.h" - .machine altivec .section .text .hidden __tsan_setjmp .globl _setjmp diff --git a/libsanitizer/tsan/tsan_rtl_thread.cpp b/libsanitizer/tsan/tsan_rtl_thread.cpp index 77488f8..8d29e25 100644 --- a/libsanitizer/tsan/tsan_rtl_thread.cpp +++ b/libsanitizer/tsan/tsan_rtl_thread.cpp @@ -160,15 +160,21 @@ void ThreadStart(ThreadState *thr, Tid tid, tid_t os_id, } Free(thr->tctx->sync); +#if !SANITIZER_GO + thr->is_inited = true; +#endif + uptr stk_addr = 0; - uptr stk_size = 0; + uptr stk_end = 0; uptr tls_addr = 0; - uptr tls_size = 0; + uptr tls_end = 0; #if !SANITIZER_GO if (thread_type != ThreadType::Fiber) - GetThreadStackAndTls(tid == kMainTid, &stk_addr, &stk_size, &tls_addr, - &tls_size); + GetThreadStackAndTls(tid == kMainTid, &stk_addr, &stk_end, &tls_addr, + &tls_end); #endif + uptr stk_size = stk_end - stk_addr; + uptr tls_size = tls_end - tls_addr; thr->stk_addr = stk_addr; thr->stk_size = stk_size; thr->tls_addr = tls_addr; @@ -200,15 +206,11 @@ void ThreadStart(ThreadState *thr, Tid tid, tid_t os_id, } void ThreadContext::OnStarted(void *arg) { - thr = static_cast<ThreadState *>(arg); DPrintf("#%d: ThreadStart\n", tid); - new (thr) ThreadState(tid); + thr = new (arg) ThreadState(tid); if (common_flags()->detect_deadlocks) thr->dd_lt = ctx->dd->CreateLogicalThread(tid); thr->tctx = this; -#if !SANITIZER_GO - thr->is_inited = true; -#endif } void ThreadFinish(ThreadState *thr) { diff --git a/libsanitizer/tsan/tsan_suppressions.cpp b/libsanitizer/tsan/tsan_suppressions.cpp index 7064212..0559df0 100644 --- a/libsanitizer/tsan/tsan_suppressions.cpp +++ b/libsanitizer/tsan/tsan_suppressions.cpp @@ -42,7 +42,7 @@ const char *__tsan_default_suppressions() { namespace __tsan { -ALIGNED(64) static char suppression_placeholder[sizeof(SuppressionContext)]; +alignas(64) static char suppression_placeholder[sizeof(SuppressionContext)]; static SuppressionContext *suppression_ctx = nullptr; static const char *kSuppressionTypes[] = { kSuppressionRace, kSuppressionRaceTop, kSuppressionMutex, diff --git a/libsanitizer/tsan/tsan_vector_clock.h b/libsanitizer/tsan/tsan_vector_clock.h index 63b2063..51d9811 100644 --- a/libsanitizer/tsan/tsan_vector_clock.h +++ b/libsanitizer/tsan/tsan_vector_clock.h @@ -34,7 +34,7 @@ class VectorClock { VectorClock& operator=(const VectorClock& other); private: - Epoch clk_[kThreadSlotCount] VECTOR_ALIGNED; + VECTOR_ALIGNED Epoch clk_[kThreadSlotCount]; }; ALWAYS_INLINE Epoch VectorClock::Get(Sid sid) const { diff --git a/libsanitizer/ubsan/ubsan_diag.cpp b/libsanitizer/ubsan/ubsan_diag.cpp index aac2704..2146ed3 100644 --- a/libsanitizer/ubsan/ubsan_diag.cpp +++ b/libsanitizer/ubsan/ubsan_diag.cpp @@ -47,7 +47,7 @@ static void MaybePrintStackTrace(uptr pc, uptr bp) { if (!flags()->print_stacktrace) return; - BufferedStackTrace stack; + UNINITIALIZED BufferedStackTrace stack; ubsan_GetStackTrace(&stack, kStackTraceMax, pc, bp, nullptr, common_flags()->fast_unwind_on_fatal); stack.Print(); @@ -88,7 +88,7 @@ static void MaybeReportErrorSummary(Location Loc, ErrorType Type) { AI.file = internal_strdup(SLoc.getFilename()); AI.line = SLoc.getLine(); AI.column = SLoc.getColumn(); - AI.function = internal_strdup(""); // Avoid printing ?? as function name. + AI.function = nullptr; ReportErrorSummary(ErrorKind, AI, GetSanititizerToolName()); AI.Clear(); return; @@ -402,7 +402,7 @@ ScopedReport::~ScopedReport() { Die(); } -ALIGNED(64) static char suppression_placeholder[sizeof(SuppressionContext)]; +alignas(64) static char suppression_placeholder[sizeof(SuppressionContext)]; static SuppressionContext *suppression_ctx = nullptr; static const char kVptrCheck[] = "vptr_check"; static const char *kSuppressionTypes[] = { diff --git a/libsanitizer/ubsan/ubsan_diag.h b/libsanitizer/ubsan/ubsan_diag.h index b444e97..c836647 100644 --- a/libsanitizer/ubsan/ubsan_diag.h +++ b/libsanitizer/ubsan/ubsan_diag.h @@ -18,26 +18,6 @@ namespace __ubsan { -class SymbolizedStackHolder { - SymbolizedStack *Stack; - - void clear() { - if (Stack) - Stack->ClearAll(); - } - -public: - 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) { diff --git a/libsanitizer/ubsan/ubsan_diag_standalone.cpp b/libsanitizer/ubsan/ubsan_diag_standalone.cpp index 5526ae0..03dfde8 100644 --- a/libsanitizer/ubsan/ubsan_diag_standalone.cpp +++ b/libsanitizer/ubsan/ubsan_diag_standalone.cpp @@ -29,7 +29,7 @@ extern "C" { SANITIZER_INTERFACE_ATTRIBUTE void __sanitizer_print_stack_trace() { GET_CURRENT_PC_BP; - BufferedStackTrace stack; + UNINITIALIZED BufferedStackTrace stack; stack.Unwind(pc, bp, nullptr, common_flags()->fast_unwind_on_fatal); stack.Print(); } diff --git a/libsanitizer/ubsan/ubsan_flags.cpp b/libsanitizer/ubsan/ubsan_flags.cpp index 9a66bd3..25cefd4 100644 --- a/libsanitizer/ubsan/ubsan_flags.cpp +++ b/libsanitizer/ubsan/ubsan_flags.cpp @@ -50,7 +50,6 @@ void InitializeFlags() { { CommonFlags cf; cf.CopyFrom(*common_flags()); - cf.print_summary = false; cf.external_symbolizer_path = GetFlag("UBSAN_SYMBOLIZER_PATH"); OverrideCommonFlags(cf); } diff --git a/libsanitizer/ubsan/ubsan_handlers.cpp b/libsanitizer/ubsan/ubsan_handlers.cpp index 85ec961..a419cf0 100644 --- a/libsanitizer/ubsan/ubsan_handlers.cpp +++ b/libsanitizer/ubsan/ubsan_handlers.cpp @@ -555,13 +555,11 @@ static void handleImplicitConversion(ImplicitConversionData *Data, ReportOptions Opts, ValueHandle Src, ValueHandle Dst) { SourceLocation Loc = Data->Loc.acquire(); - ErrorType ET = ErrorType::GenericUB; - const TypeDescriptor &SrcTy = Data->FromType; const TypeDescriptor &DstTy = Data->ToType; - bool SrcSigned = SrcTy.isSignedIntegerTy(); bool DstSigned = DstTy.isSignedIntegerTy(); + ErrorType ET = ErrorType::GenericUB; switch (Data->Kind) { case ICCK_IntegerTruncation: { // Legacy, no longer used. @@ -594,14 +592,23 @@ static void handleImplicitConversion(ImplicitConversionData *Data, ScopedReport R(Opts, Loc, ET); + // In the case we have a bitfield, we want to explicitly say so in the + // error message. // FIXME: is it possible to dump the values as hex with fixed width? - - Diag(Loc, DL_Error, ET, - "implicit conversion from type %0 of value %1 (%2-bit, %3signed) to " - "type %4 changed the value to %5 (%6-bit, %7signed)") - << SrcTy << Value(SrcTy, Src) << SrcTy.getIntegerBitWidth() - << (SrcSigned ? "" : "un") << DstTy << Value(DstTy, Dst) - << DstTy.getIntegerBitWidth() << (DstSigned ? "" : "un"); + if (Data->BitfieldBits) + Diag(Loc, DL_Error, ET, + "implicit conversion from type %0 of value %1 (%2-bit, %3signed) to " + "type %4 changed the value to %5 (%6-bit bitfield, %7signed)") + << SrcTy << Value(SrcTy, Src) << SrcTy.getIntegerBitWidth() + << (SrcSigned ? "" : "un") << DstTy << Value(DstTy, Dst) + << Data->BitfieldBits << (DstSigned ? "" : "un"); + else + Diag(Loc, DL_Error, ET, + "implicit conversion from type %0 of value %1 (%2-bit, %3signed) to " + "type %4 changed the value to %5 (%6-bit, %7signed)") + << SrcTy << Value(SrcTy, Src) << SrcTy.getIntegerBitWidth() + << (SrcSigned ? "" : "un") << DstTy << Value(DstTy, Dst) + << DstTy.getIntegerBitWidth() << (DstSigned ? "" : "un"); } void __ubsan::__ubsan_handle_implicit_conversion(ImplicitConversionData *Data, @@ -626,13 +633,16 @@ static void handleInvalidBuiltin(InvalidBuiltinData *Data, ReportOptions Opts) { ScopedReport R(Opts, Loc, ET); - Diag(Loc, DL_Error, ET, - "passing zero to %0, which is not a valid argument") - << ((Data->Kind == BCK_CTZPassedZero) ? "ctz()" : "clz()"); + if (Data->Kind == BCK_AssumePassedFalse) + Diag(Loc, DL_Error, ET, "assumption is violated during execution"); + else + Diag(Loc, DL_Error, ET, + "passing zero to __builtin_%0(), which is not a valid argument") + << ((Data->Kind == BCK_CTZPassedZero) ? "ctz" : "clz"); } void __ubsan::__ubsan_handle_invalid_builtin(InvalidBuiltinData *Data) { - GET_REPORT_OPTIONS(true); + GET_REPORT_OPTIONS(false); handleInvalidBuiltin(Data, Opts); } void __ubsan::__ubsan_handle_invalid_builtin_abort(InvalidBuiltinData *Data) { @@ -894,21 +904,6 @@ void __ubsan_handle_cfi_bad_type(CFICheckFailData *Data, ValueHandle Vtable, } // namespace __ubsan -void __ubsan::__ubsan_handle_cfi_bad_icall(CFIBadIcallData *CallData, - ValueHandle Function) { - GET_REPORT_OPTIONS(false); - CFICheckFailData Data = {CFITCK_ICall, CallData->Loc, CallData->Type}; - handleCFIBadIcall(&Data, Function, Opts); -} - -void __ubsan::__ubsan_handle_cfi_bad_icall_abort(CFIBadIcallData *CallData, - ValueHandle Function) { - GET_REPORT_OPTIONS(true); - CFICheckFailData Data = {CFITCK_ICall, CallData->Loc, CallData->Type}; - handleCFIBadIcall(&Data, Function, Opts); - Die(); -} - void __ubsan::__ubsan_handle_cfi_check_fail(CFICheckFailData *Data, ValueHandle Value, uptr ValidVtable) { diff --git a/libsanitizer/ubsan/ubsan_handlers.h b/libsanitizer/ubsan/ubsan_handlers.h index f717bc7..4ffa143 100644 --- a/libsanitizer/ubsan/ubsan_handlers.h +++ b/libsanitizer/ubsan/ubsan_handlers.h @@ -147,6 +147,7 @@ struct ImplicitConversionData { const TypeDescriptor &FromType; const TypeDescriptor &ToType; /* ImplicitConversionCheckKind */ unsigned char Kind; + unsigned int BitfieldBits; }; /// \brief Implict conversion that changed the value. @@ -158,6 +159,7 @@ RECOVERABLE(implicit_conversion, ImplicitConversionData *Data, ValueHandle Src, enum BuiltinCheckKind : unsigned char { BCK_CTZPassedZero, BCK_CLZPassedZero, + BCK_AssumePassedFalse, }; struct InvalidBuiltinData { @@ -215,20 +217,12 @@ enum CFITypeCheckKind : unsigned char { CFITCK_VMFCall, }; -struct CFIBadIcallData { - SourceLocation Loc; - const TypeDescriptor &Type; -}; - struct CFICheckFailData { CFITypeCheckKind CheckKind; SourceLocation Loc; const TypeDescriptor &Type; }; -/// \brief Handle control flow integrity failure for indirect function calls. -RECOVERABLE(cfi_bad_icall, CFIBadIcallData *Data, ValueHandle Function) - /// \brief Handle control flow integrity failures. RECOVERABLE(cfi_check_fail, CFICheckFailData *Data, ValueHandle Function, uptr VtableIsValid) diff --git a/libsanitizer/ubsan/ubsan_handlers_cxx.cpp b/libsanitizer/ubsan/ubsan_handlers_cxx.cpp index 0317a3d..206a0bb 100644 --- a/libsanitizer/ubsan/ubsan_handlers_cxx.cpp +++ b/libsanitizer/ubsan/ubsan_handlers_cxx.cpp @@ -156,50 +156,6 @@ void __ubsan_handle_cfi_bad_type(CFICheckFailData *Data, ValueHandle Vtable, Diag(Loc, DL_Note, ET, "check failed in %0, vtable located in %1") << SrcModule << DstModule; } - -static bool handleFunctionTypeMismatch(FunctionTypeMismatchData *Data, - ValueHandle Function, - ValueHandle calleeRTTI, - ValueHandle fnRTTI, ReportOptions Opts) { - if (checkTypeInfoEquality(reinterpret_cast<void *>(calleeRTTI), - reinterpret_cast<void *>(fnRTTI))) - return false; - - SourceLocation CallLoc = Data->Loc.acquire(); - ErrorType ET = ErrorType::FunctionTypeMismatch; - - if (ignoreReport(CallLoc, Opts, ET)) - return true; - - ScopedReport R(Opts, CallLoc, ET); - - SymbolizedStackHolder FLoc(getSymbolizedLocation(Function)); - const char *FName = FLoc.get()->info.function; - if (!FName) - FName = "(unknown)"; - - Diag(CallLoc, DL_Error, ET, - "call to function %0 through pointer to incorrect function type %1") - << FName << Data->Type; - Diag(FLoc, DL_Note, ET, "%0 defined here") << FName; - return true; -} - -void __ubsan_handle_function_type_mismatch_v1(FunctionTypeMismatchData *Data, - ValueHandle Function, - ValueHandle calleeRTTI, - ValueHandle fnRTTI) { - GET_REPORT_OPTIONS(false); - handleFunctionTypeMismatch(Data, Function, calleeRTTI, fnRTTI, Opts); -} - -void __ubsan_handle_function_type_mismatch_v1_abort( - FunctionTypeMismatchData *Data, ValueHandle Function, - ValueHandle calleeRTTI, ValueHandle fnRTTI) { - GET_REPORT_OPTIONS(true); - if (handleFunctionTypeMismatch(Data, Function, calleeRTTI, fnRTTI, Opts)) - Die(); -} } // namespace __ubsan #endif // CAN_SANITIZE_UB diff --git a/libsanitizer/ubsan/ubsan_handlers_cxx.h b/libsanitizer/ubsan/ubsan_handlers_cxx.h index f6f24e8..71695cb 100644 --- a/libsanitizer/ubsan/ubsan_handlers_cxx.h +++ b/libsanitizer/ubsan/ubsan_handlers_cxx.h @@ -33,19 +33,6 @@ void __ubsan_handle_dynamic_type_cache_miss( extern "C" SANITIZER_INTERFACE_ATTRIBUTE void __ubsan_handle_dynamic_type_cache_miss_abort( DynamicTypeCacheMissData *Data, ValueHandle Pointer, ValueHandle Hash); - -struct FunctionTypeMismatchData; - -extern "C" SANITIZER_INTERFACE_ATTRIBUTE void -__ubsan_handle_function_type_mismatch_v1(FunctionTypeMismatchData *Data, - ValueHandle Val, - ValueHandle calleeRTTI, - ValueHandle fnRTTI); -extern "C" SANITIZER_INTERFACE_ATTRIBUTE void -__ubsan_handle_function_type_mismatch_v1_abort(FunctionTypeMismatchData *Data, - ValueHandle Val, - ValueHandle calleeRTTI, - ValueHandle fnRTTI); } #endif // UBSAN_HANDLERS_CXX_H diff --git a/libsanitizer/ubsan/ubsan_init.cpp b/libsanitizer/ubsan/ubsan_init.cpp index 5802d588..aea7ca0 100644 --- a/libsanitizer/ubsan/ubsan_init.cpp +++ b/libsanitizer/ubsan/ubsan_init.cpp @@ -43,8 +43,8 @@ static void CommonStandaloneInit() { SanitizerToolName = GetSanititizerToolName(); CacheBinaryName(); InitializeFlags(); - __sanitizer::InitializePlatformEarly(); __sanitizer_set_report_path(common_flags()->log_path); + __sanitizer::InitializePlatformEarly(); AndroidLogInit(); InitializeCoverage(common_flags()->coverage, common_flags()->coverage_dir); CommonInit(); diff --git a/libsanitizer/ubsan/ubsan_init_standalone_preinit.cpp b/libsanitizer/ubsan/ubsan_init_standalone_preinit.cpp index fabbf91..8a2a631 100644 --- a/libsanitizer/ubsan/ubsan_init_standalone_preinit.cpp +++ b/libsanitizer/ubsan/ubsan_init_standalone_preinit.cpp @@ -30,6 +30,6 @@ static void PreInitAsStandalone() { } // namespace __ubsan -__attribute__((section(".preinit_array"), used)) void (*__local_ubsan_preinit)( - void) = __ubsan::PreInitAsStandalone; +__attribute__((section(".preinit_array"), used)) static auto preinit = + __ubsan::PreInitAsStandalone; #endif // SANITIZER_CAN_USE_PREINIT_ARRAY diff --git a/libsanitizer/ubsan/ubsan_interface.inc b/libsanitizer/ubsan/ubsan_interface.inc index f95f71a..cb27feb 100644 --- a/libsanitizer/ubsan/ubsan_interface.inc +++ b/libsanitizer/ubsan/ubsan_interface.inc @@ -21,8 +21,6 @@ INTERFACE_FUNCTION(__ubsan_handle_dynamic_type_cache_miss) INTERFACE_FUNCTION(__ubsan_handle_dynamic_type_cache_miss_abort) INTERFACE_FUNCTION(__ubsan_handle_float_cast_overflow) INTERFACE_FUNCTION(__ubsan_handle_float_cast_overflow_abort) -INTERFACE_FUNCTION(__ubsan_handle_function_type_mismatch_v1) -INTERFACE_FUNCTION(__ubsan_handle_function_type_mismatch_v1_abort) INTERFACE_FUNCTION(__ubsan_handle_function_type_mismatch) INTERFACE_FUNCTION(__ubsan_handle_function_type_mismatch_abort) INTERFACE_FUNCTION(__ubsan_handle_implicit_conversion) diff --git a/libsanitizer/ubsan/ubsan_platform.h b/libsanitizer/ubsan/ubsan_platform.h index ad3e883..d2cc2e1 100644 --- a/libsanitizer/ubsan/ubsan_platform.h +++ b/libsanitizer/ubsan/ubsan_platform.h @@ -12,7 +12,6 @@ #ifndef UBSAN_PLATFORM_H #define UBSAN_PLATFORM_H -#ifndef CAN_SANITIZE_UB // Other platforms should be easy to add, and probably work as-is. #if defined(__linux__) || defined(__FreeBSD__) || defined(__APPLE__) || \ defined(__NetBSD__) || defined(__DragonFly__) || \ @@ -22,6 +21,5 @@ #else # define CAN_SANITIZE_UB 0 #endif -#endif //CAN_SANITIZE_UB #endif diff --git a/libsanitizer/ubsan/ubsan_signals_standalone.cpp b/libsanitizer/ubsan/ubsan_signals_standalone.cpp index 354f847..68edd3a 100644 --- a/libsanitizer/ubsan/ubsan_signals_standalone.cpp +++ b/libsanitizer/ubsan/ubsan_signals_standalone.cpp @@ -66,6 +66,11 @@ void InitializeDeadlySignals() { return; is_initialized = true; InitializeSignalInterceptors(); +#if SANITIZER_INTERCEPT_SIGNAL_AND_SIGACTION + // REAL(sigaction_symname) is nullptr in a static link. Bail out. + if (!REAL(sigaction_symname)) + return; +#endif InstallDeadlySignalHandlers(&UBsanOnDeadlySignal); } diff --git a/libsanitizer/ubsan/ubsan_type_hash_itanium.cpp b/libsanitizer/ubsan/ubsan_type_hash_itanium.cpp index 468a8fc..1578857 100644 --- a/libsanitizer/ubsan/ubsan_type_hash_itanium.cpp +++ b/libsanitizer/ubsan/ubsan_type_hash_itanium.cpp @@ -207,7 +207,7 @@ struct VtablePrefix { std::type_info *TypeInfo; }; VtablePrefix *getVtablePrefix(void *Vtable) { - Vtable = ptrauth_auth_data(Vtable, ptrauth_key_cxx_vtable_pointer, 0); + Vtable = ptrauth_strip(Vtable, ptrauth_key_cxx_vtable_pointer); VtablePrefix *Vptr = reinterpret_cast<VtablePrefix*>(Vtable); VtablePrefix *Prefix = Vptr - 1; if (!IsAccessibleMemoryRange((uptr)Prefix, sizeof(VtablePrefix))) diff --git a/libsanitizer/ubsan/ubsan_value.cpp b/libsanitizer/ubsan/ubsan_value.cpp index dc61e5b..6e88eba 100644 --- a/libsanitizer/ubsan/ubsan_value.cpp +++ b/libsanitizer/ubsan/ubsan_value.cpp @@ -67,18 +67,21 @@ const char *__ubsan::getObjCClassName(ValueHandle Pointer) { SIntMax Value::getSIntValue() const { CHECK(getType().isSignedIntegerTy()); + // Val was zero-extended to ValueHandle. Sign-extend from original width + // to SIntMax. + const unsigned ExtraBits = + sizeof(SIntMax) * 8 - getType().getIntegerBitCount(); if (isInlineInt()) { - // Val was zero-extended to ValueHandle. Sign-extend from original width - // to SIntMax. - const unsigned ExtraBits = - sizeof(SIntMax) * 8 - getType().getIntegerBitWidth(); return SIntMax(UIntMax(Val) << ExtraBits) >> ExtraBits; } - if (getType().getIntegerBitWidth() == 64) - return *reinterpret_cast<s64*>(Val); + if (getType().getIntegerBitWidth() == 64) { + return SIntMax(UIntMax(*reinterpret_cast<s64 *>(Val)) << ExtraBits) >> + ExtraBits; + } #if HAVE_INT128_T if (getType().getIntegerBitWidth() == 128) - return *reinterpret_cast<s128*>(Val); + return SIntMax(UIntMax(*reinterpret_cast<s128 *>(Val)) << ExtraBits) >> + ExtraBits; #else if (getType().getIntegerBitWidth() == 128) UNREACHABLE("libclang_rt.ubsan was built without __int128 support"); diff --git a/libsanitizer/ubsan/ubsan_value.h b/libsanitizer/ubsan/ubsan_value.h index e095727..430c9ea 100644 --- a/libsanitizer/ubsan/ubsan_value.h +++ b/libsanitizer/ubsan/ubsan_value.h @@ -103,6 +103,13 @@ public: /// representation is that of bitcasting the floating-point value to an /// integer type. TK_Float = 0x0001, + /// An _BitInt(N) type. Lowest bit is 1 for a signed value, 0 for an + /// unsigned value. Remaining bits are log_2(bit_width). The value + /// representation is the integer itself if it fits into a ValueHandle, and + /// a pointer to the integer otherwise. TypeName contains the true width + /// of the type for the signed _BitInt(N) type stored after zero bit after + /// TypeName as 32-bit unsigned integer. + TK_BitInt = 0x0002, /// Any other type. The value representation is unspecified. TK_Unknown = 0xffff }; @@ -113,10 +120,15 @@ public: return static_cast<Kind>(TypeKind); } - bool isIntegerTy() const { return getKind() == TK_Integer; } + bool isIntegerTy() const { + return getKind() == TK_Integer || getKind() == TK_BitInt; + } + bool isBitIntTy() const { return getKind() == TK_BitInt; } + bool isSignedIntegerTy() const { return isIntegerTy() && (TypeInfo & 1); } + bool isSignedBitIntTy() const { return isBitIntTy() && (TypeInfo & 1); } bool isUnsignedIntegerTy() const { return isIntegerTy() && !(TypeInfo & 1); } @@ -125,6 +137,25 @@ public: return 1 << (TypeInfo >> 1); } + const char *getBitIntBitCountPointer() const { + DCHECK(isBitIntTy()); + DCHECK(isSignedBitIntTy()); + // Scan Name for zero and return the next address + const char *p = getTypeName(); + while (*p != '\0') + ++p; + // Return the next address + return p + 1; + } + + unsigned getIntegerBitCount() const { + DCHECK(isIntegerTy()); + if (isSignedBitIntTy()) + return *reinterpret_cast<const u32 *>(getBitIntBitCountPointer()); + else + return getIntegerBitWidth(); + } + bool isFloatTy() const { return getKind() == TK_Float; } unsigned getFloatBitWidth() const { CHECK(isFloatTy()); diff --git a/libsanitizer/ubsan/ubsan_win_dll_thunk.cpp b/libsanitizer/ubsan/ubsan_win_dll_thunk.cpp deleted file mode 100644 index 5ac7fc3..0000000 --- a/libsanitizer/ubsan/ubsan_win_dll_thunk.cpp +++ /dev/null @@ -1,20 +0,0 @@ -//===-- ubsan_win_dll_thunk.cpp -------------------------------------------===// -// -// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. -// See https://llvm.org/LICENSE.txt for license information. -// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception -// -//===----------------------------------------------------------------------===// -// -// This file defines a family of thunks that should be statically linked into -// the DLLs that have instrumentation in order to delegate the calls to the -// shared runtime that lives in the main binary. -// See https://github.com/google/sanitizers/issues/209 for the details. -//===----------------------------------------------------------------------===// -#ifdef SANITIZER_DLL_THUNK -#include "sanitizer_common/sanitizer_win_dll_thunk.h" -// Ubsan interface functions. -#define INTERFACE_FUNCTION(Name) INTERCEPT_SANITIZER_FUNCTION(Name) -#define INTERFACE_WEAK_FUNCTION(Name) INTERCEPT_SANITIZER_WEAK_FUNCTION(Name) -#include "ubsan_interface.inc" -#endif // SANITIZER_DLL_THUNK diff --git a/libsanitizer/ubsan/ubsan_win_dynamic_runtime_thunk.cpp b/libsanitizer/ubsan/ubsan_win_runtime_thunk.cpp index 00722b4..5ca7d6f 100644 --- a/libsanitizer/ubsan/ubsan_win_dynamic_runtime_thunk.cpp +++ b/libsanitizer/ubsan/ubsan_win_runtime_thunk.cpp @@ -1,4 +1,4 @@ -//===-- ubsan_win_dynamic_runtime_thunk.cpp -------------------------------===// +//===-- ubsan_win_runtime_thunk.cpp ----------------------------- --===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. @@ -10,11 +10,14 @@ // to interact with Ubsan, when it is included in a dll. // //===----------------------------------------------------------------------===// -#ifdef SANITIZER_DYNAMIC_RUNTIME_THUNK +#if defined(SANITIZER_DYNAMIC_RUNTIME_THUNK) || \ + defined(SANITIZER_STATIC_RUNTIME_THUNK) #define SANITIZER_IMPORT_INTERFACE 1 #include "sanitizer_common/sanitizer_win_defs.h" +#include "sanitizer_common/sanitizer_win_thunk_interception.h" // Define weak alias for all weak functions imported from ubsan. #define INTERFACE_FUNCTION(Name) -#define INTERFACE_WEAK_FUNCTION(Name) WIN_WEAK_IMPORT_DEF(Name) +#define INTERFACE_WEAK_FUNCTION(Name) REGISTER_WEAK_FUNCTION(Name) #include "ubsan_interface.inc" -#endif // SANITIZER_DYNAMIC_RUNTIME_THUNK +#endif // defined(SANITIZER_DYNAMIC_RUNTIME_THUNK) || + // defined(SANITIZER_STATIC_RUNTIME_THUNK) diff --git a/libsanitizer/ubsan/ubsan_win_weak_interception.cpp b/libsanitizer/ubsan/ubsan_win_weak_interception.cpp deleted file mode 100644 index 01db0c0..0000000 --- a/libsanitizer/ubsan/ubsan_win_weak_interception.cpp +++ /dev/null @@ -1,23 +0,0 @@ -//===-- ubsan_win_weak_interception.cpp -----------------------------------===// -// -// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. -// See https://llvm.org/LICENSE.txt for license information. -// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception -// -//===----------------------------------------------------------------------===// -// This module should be included in Ubsan when it is implemented as a shared -// library on Windows (dll), in order to delegate the calls of weak functions to -// the implementation in the main executable when a strong definition is -// provided. -//===----------------------------------------------------------------------===// -#ifdef SANITIZER_DYNAMIC -#include "sanitizer_common/sanitizer_win_weak_interception.h" -#include "ubsan_flags.h" -#include "ubsan_monitor.h" -// Check if strong definitions for weak functions are present in the main -// executable. If that is the case, override dll functions to point to strong -// implementations. -#define INTERFACE_FUNCTION(Name) -#define INTERFACE_WEAK_FUNCTION(Name) INTERCEPT_SANITIZER_WEAK_FUNCTION(Name) -#include "ubsan_interface.inc" -#endif // SANITIZER_DYNAMIC |