From f4d39d63a5cdc1e533516ff8fa4228f25bd1b9e3 Mon Sep 17 00:00:00 2001 From: Eric Date: Thu, 29 May 2025 10:19:36 +0800 Subject: [PATCH] [Compiler_RT][GWP_ASan] Add MinSampleSize & WhiteListPath options For GWP_ASan (Patch 2) WhiteListPath: if the .so path in WhiteListPath, GWP_ASan determin to sample it without considering the sample rate. On Single 6.x frame, Enabling GWP_ASan with a specified library for sampling ensures deterministic sampling of all call stacks related to that library. Change-Id: I3b01f0df0b5b1cbd643ca02837d58f2b0a9bd6ef Signed-off-by: Eric --- compiler-rt/lib/gwp_asan/CMakeLists.txt | 1 + .../lib/gwp_asan/guarded_pool_allocator.cpp | 102 ++++++++++++++++++ .../lib/gwp_asan/guarded_pool_allocator.h | 43 ++++++-- .../sanitizer_common/sanitizer_symbolizer.h | 7 ++ .../sanitizer_symbolizer_libcdep.cpp | 20 ++++ 5 files changed, 164 insertions(+), 9 deletions(-) diff --git a/compiler-rt/lib/gwp_asan/CMakeLists.txt b/compiler-rt/lib/gwp_asan/CMakeLists.txt index bc2ab445409a..2ba6596c6492 100644 --- a/compiler-rt/lib/gwp_asan/CMakeLists.txt +++ b/compiler-rt/lib/gwp_asan/CMakeLists.txt @@ -101,6 +101,7 @@ if (COMPILER_RT_HAS_GWP_ASAN) # OHOS_LOCAL begin RTSanitizerCommon RTSanitizerCommonLibc + RTSanitizerCommonSymbolizer # OHOS_LOCAL end PARENT_TARGET gwp_asan ) diff --git a/compiler-rt/lib/gwp_asan/guarded_pool_allocator.cpp b/compiler-rt/lib/gwp_asan/guarded_pool_allocator.cpp index 47160942daca..739463efc836 100644 --- a/compiler-rt/lib/gwp_asan/guarded_pool_allocator.cpp +++ b/compiler-rt/lib/gwp_asan/guarded_pool_allocator.cpp @@ -12,6 +12,9 @@ #include "gwp_asan/utilities.h" // OHOS_LOCAL begin #include "sanitizer_common/sanitizer_common.h" +#include "sanitizer_common/sanitizer_stacktrace_printer.h" +#include "sanitizer_common/sanitizer_symbolizer.h" +#include "sanitizer_common/sanitizer_symbolizer_internal.h" // OHOS_LOCAL end #include @@ -120,6 +123,14 @@ void GuardedPoolAllocator::init(const options::Options &Opts) { if (Opts.InstallForkHandlers) installAtFork(); +// OHOS_LOCAL begin +#if defined (__OHOS__) + Symbolizer = __sanitizer::Symbolizer::GetOrInit(); + Symbolizer->RefreshModules(); + parseWhiteList(); + findmodule(); +#endif +// OHOS_LOCAL end } void GuardedPoolAllocator::disable() { @@ -424,5 +435,96 @@ void GuardedPoolAllocator::accumulatePersistInterval(size_t reservedSlotsLength) PersistInterval += (curTime - PreTime) * reservedSlotsLength; PreTime = curTime; }; + +#if defined (__OHOS__) +// This function detects a specific library and returns true immediately if found, +// and skipping all following probabilistic detection procedures. +bool GuardedPoolAllocator::checkLib() { + if (LibraryPathLength == 0 && ModuleLength == 0) + return false; + + static constexpr unsigned kMaximumStackFramesForCrashTrace = 512; + uintptr_t Trace[kMaximumStackFramesForCrashTrace]; + size_t TraceLength = Backtrace(Trace, kMaximumStackFramesForCrashTrace); + + uintptr_t pc; + for (int i = 0; i < TraceLength; ++i) { + pc = Trace[i]; + + for (int j = 0; j < ModuleLength; ++j) { + if (Modules[j]->containsAddress(pc)) + return true; + } + + if (LibraryPathLength == 0) + continue; + // Check if there are any dlopen libraries + if (Symbolizer->GetModulesFresh()) + continue; + + { + ScopedLock l(FindModMutex); + Symbolizer->RefreshModules(); + findmodule(); + } + + for (int j = 0; j < ModuleLength; ++j) { + if (Modules[j]->containsAddress(pc)) + return true; + } + } + return false; +} + +// Parse `WhiteListPath` to array. +void GuardedPoolAllocator::parseWhiteList() { + if (!WhiteListPath || __sanitizer::internal_strlen(WhiteListPath) == 0) + return; + + int Num_colons = 1; + for (const char* p = WhiteListPath; *p != '\0'; ++p) { + if (*p == ':') ++Num_colons; + } + + // Initialized `LibraryPath` and `Modules` according to the number of libraries that needed to be checked. + size_t BytesRequired = roundUpTo( + Num_colons * sizeof(*LibraryPath), State.PageSize); + LibraryPath = + reinterpret_cast(map(BytesRequired, "GWP-ASan Checked LibraryPath")); + + BytesRequired = roundUpTo( + Num_colons * sizeof(*Modules), State.PageSize); + Modules = + reinterpret_cast(map(BytesRequired, "GWP-ASan Checked Module")); + + const char *Start = WhiteListPath; + for (int i = 0; i < Num_colons; ++i) { + const char *End = Start; + while(*End != ':' && *End != '\0') ++End; + + int Len = End - Start; + BytesRequired = roundUpTo((Len + 1)*sizeof(char), State.PageSize); + LibraryPath[i] = reinterpret_cast(map(BytesRequired, "GWP-ASan Checked Library")); + __sanitizer::internal_strncpy(LibraryPath[i], Start, Len); + LibraryPath[i][Len] = '\0'; + Start = End + 1; + } + LibraryPathLength = Num_colons; +} + +void GuardedPoolAllocator::findmodule() { + int Index = 0; + while (Index < LibraryPathLength) { + if (auto *Module = Symbolizer->FindLibraryByName(LibraryPath[Index])) { + Modules[ModuleLength++] = Module; + // Upon locating the target module, the last element is updated to the current element and + // LibraryPathLength is decremented by one to ensure proper bounds. + LibraryPath[Index] = LibraryPath[--LibraryPathLength]; + } else { + Index++; + } + } +} +#endif // OHOS_LOCAL end } // namespace gwp_asan diff --git a/compiler-rt/lib/gwp_asan/guarded_pool_allocator.h b/compiler-rt/lib/gwp_asan/guarded_pool_allocator.h index 0ead83f09aa0..967c3a34d962 100644 --- a/compiler-rt/lib/gwp_asan/guarded_pool_allocator.h +++ b/compiler-rt/lib/gwp_asan/guarded_pool_allocator.h @@ -17,6 +17,11 @@ #include "gwp_asan/platform_specific/guarded_pool_allocator_posix.h" // IWYU pragma: keep #include "gwp_asan/platform_specific/guarded_pool_allocator_tls.h" +namespace __sanitizer { + class LoadedModule; + class Symbolizer; +} // namespace __sanitizer + #include #include // IWYU pragma: no_include <__stddef_max_align_t.h> @@ -90,14 +95,20 @@ public: // Return whether the allocation should be randomly chosen for sampling. GWP_ASAN_ALWAYS_INLINE bool shouldSample() { // OHOS_LOCAL begin - #if defined (__OHOS__) +#if defined (__OHOS__) Nmalloc++; if( Nmalloc % PRINT_COUNTER == 0 ) { MUSL_LOG("[gwp_asan]: AvgDuration %{public}u us, FreeSlotsLength %{public}d\n", PersistInterval / ReserveCounter, FreeSlotsLength); Nmalloc = 0; } - #endif + // If the RandomState is calculated from getRandomUnsigned32, the value + // of RandomState will never be 1, so we use RandomState == 1 to force + // GWP_ASAN sample. + if (GWP_ASAN_UNLIKELY(checkLib() || getThreadLocals()->RandomState == 1)) { + return true; + } +#endif // OHOS_LOCAL end // NextSampleCounter == 0 means we "should regenerate the counter". // == 1 means we "should sample this allocation". @@ -105,13 +116,6 @@ public: // class must be valid when zero-initialised, and we wish to sample as // infrequently as possible when this is the case, hence we underflow to // UINT32_MAX. - // OHOS_LOCAL begin - // If the RandomState is calculated from getRandomUnsigned32, the value - // of RandomState will never be 1, so we use RandomState == 1 to force - // GWP_ASAN sample. - if (GWP_ASAN_UNLIKELY(getThreadLocals()->RandomState == 1)) - return true; - // OHOS_LOCAL end if (GWP_ASAN_UNLIKELY(getThreadLocals()->NextSampleCounter == 0)) getThreadLocals()->NextSampleCounter = @@ -244,6 +248,11 @@ private: // OHOS_LOCAL begin void accumulatePersistInterval(size_t reservedSlotsLength); +#if defined (__OHOS__) + bool checkLib(); + void findmodule(); + void parseWhiteList(); +#endif // OHOS_LOCAL end gwp_asan::AllocatorState State; @@ -255,6 +264,12 @@ private: // A mutex to protect the guarded slot and metadata pool for this class. Mutex PoolMutex; +// OHOS_LOCAL begin +#if defined (__OHOS__) + // A mutex to protect the find library. + Mutex FindModMutex; +#endif +// OHOS_LOCAL end // Some unwinders can grab the libdl lock. In order to provide atfork // protection, we need to ensure that we allow an unwinding thread to release // the libdl lock before forking. @@ -287,10 +302,20 @@ private: // the sample rate. uint32_t AdjustedSampleRatePlusOne = 0; // OHOS_LOCAL begin + // The Count that GWP_ASan tracks the malloc and prints upon reaching PRINT_COUNT. size_t Nmalloc{0}; size_t PersistInterval{0}; size_t PreTime{0}; size_t ReserveCounter{0}; +#if defined (__OHOS__) + __sanitizer::Symbolizer *Symbolizer = nullptr; + // Pointer to an array of modules. + const __sanitizer::LoadedModule **Modules = nullptr; + uint8_t ModuleLength{0}; + // Pointer to an array of LibraryPath. + char **LibraryPath = nullptr; + uint8_t LibraryPathLength{0}; +#endif // OHOS_LOCAL end // Additional platform specific data structure for the guarded pool mapping. diff --git a/compiler-rt/lib/sanitizer_common/sanitizer_symbolizer.h b/compiler-rt/lib/sanitizer_common/sanitizer_symbolizer.h index bad4761e345f..b651efe92b13 100644 --- a/compiler-rt/lib/sanitizer_common/sanitizer_symbolizer.h +++ b/compiler-rt/lib/sanitizer_common/sanitizer_symbolizer.h @@ -152,6 +152,13 @@ class Symbolizer final { void RefreshModules(); const LoadedModule *FindModuleForAddress(uptr address); +// OHOS_LOCAL begin +#if defined (__OHOS__) + const LoadedModule *FindLibraryByName(const char *name); + + const bool GetModulesFresh() { return modules_fresh_; }; +#endif +// OHOS_LOCAL end void InvalidateModuleList(); private: diff --git a/compiler-rt/lib/sanitizer_common/sanitizer_symbolizer_libcdep.cpp b/compiler-rt/lib/sanitizer_common/sanitizer_symbolizer_libcdep.cpp index a6f82ced2036..c8174102b69f 100644 --- a/compiler-rt/lib/sanitizer_common/sanitizer_symbolizer_libcdep.cpp +++ b/compiler-rt/lib/sanitizer_common/sanitizer_symbolizer_libcdep.cpp @@ -198,6 +198,26 @@ static const LoadedModule *SearchForModule(const ListOfModules &modules, return nullptr; } +// OHOS_LOCAL begin +#if defined (__OHOS__) +const LoadedModule *Symbolizer::FindLibraryByName(const char *name) { + for (uptr i = 0; i < modules_.size(); i++) { + if (internal_strstr(modules_[i].full_name(), name) != nullptr) { + return &modules_[i]; + } + } + if (fallback_modules_.size()) { + for (uptr i = 0; i < fallback_modules_.size(); i++) { + if (internal_strstr(fallback_modules_[i].full_name(), name) != nullptr) { + return &fallback_modules_[i]; + } + } + } + return nullptr; +} +#endif +// OHOS_LOCAL end + const LoadedModule *Symbolizer::FindModuleForAddress(uptr address) { bool modules_were_reloaded = false; if (!modules_fresh_) { -- Gitee