diff --git a/compiler-rt/lib/hwasan/CMakeLists.txt b/compiler-rt/lib/hwasan/CMakeLists.txt --- a/compiler-rt/lib/hwasan/CMakeLists.txt +++ b/compiler-rt/lib/hwasan/CMakeLists.txt @@ -164,6 +164,7 @@ RTSanitizerCommonLibc RTSanitizerCommonCoverage RTSanitizerCommonSymbolizer + RTLSanCommon RTUbsan CFLAGS ${hwasan_rtl_flags} PARENT_TARGET hwasan) @@ -200,6 +201,7 @@ RTSanitizerCommonLibc RTSanitizerCommonCoverage RTSanitizerCommonSymbolizer + RTLSanCommon RTUbsan RTUbsan_cxx # The only purpose of RTHWAsan_dynamic_version_script_dummy is to diff --git a/compiler-rt/lib/hwasan/hwasan.h b/compiler-rt/lib/hwasan/hwasan.h --- a/compiler-rt/lib/hwasan/hwasan.h +++ b/compiler-rt/lib/hwasan/hwasan.h @@ -144,6 +144,8 @@ void HwasanInstallAtForkHandler(); +void InstallAtExitCheckLeaks(); + void UpdateMemoryUsage(); void AppendToErrorMessageBuffer(const char *buffer); diff --git a/compiler-rt/lib/hwasan/hwasan.cpp b/compiler-rt/lib/hwasan/hwasan.cpp --- a/compiler-rt/lib/hwasan/hwasan.cpp +++ b/compiler-rt/lib/hwasan/hwasan.cpp @@ -86,6 +86,8 @@ cf.clear_shadow_mmap_threshold = 4096 * (SANITIZER_ANDROID ? 2 : 8); // Sigtrap is used in error reporting. cf.handle_sigtrap = kHandleSignalExclusive; + // FIXME: enable once all false positives have been fixed. + cf.detect_leaks = false; #if SANITIZER_ANDROID // Let platform handle other signals. It is better at reporting them then we @@ -106,6 +108,15 @@ RegisterHwasanFlags(&parser, f); RegisterCommonFlags(&parser); +#if CAN_SANITIZE_LEAKS + __lsan::Flags *lf = __lsan::flags(); + lf->SetDefaults(); + + FlagParser lsan_parser; + __lsan::RegisterLsanFlags(&lsan_parser, lf); + RegisterCommonFlags(&lsan_parser); +#endif + #if HWASAN_CONTAINS_UBSAN __ubsan::Flags *uf = __ubsan::flags(); uf->SetDefaults(); @@ -124,6 +135,9 @@ #endif parser.ParseStringFromEnv("HWASAN_OPTIONS"); +#if CAN_SANITIZE_LEAKS + lsan_parser.ParseStringFromEnv("LSAN_OPTIONS"); +#endif #if HWASAN_CONTAINS_UBSAN ubsan_parser.ParseStringFromEnv("UBSAN_OPTIONS"); #endif @@ -133,6 +147,12 @@ if (Verbosity()) ReportUnrecognizedFlags(); if (common_flags()->help) parser.PrintFlagDescriptions(); + // Flag validation: + if (!CAN_SANITIZE_LEAKS && common_flags()->detect_leaks) { + Report("%s: detect_leaks is not supported on this platform.\n", + SanitizerToolName); + Die(); + } } static void CheckUnwind() { @@ -368,10 +388,22 @@ HwasanAllocatorInit(); HwasanInstallAtForkHandler(); + if (CAN_SANITIZE_LEAKS) { + __lsan::InitCommonLsan(); + InstallAtExitCheckLeaks(); + } + #if HWASAN_CONTAINS_UBSAN __ubsan::InitAsPlugin(); #endif + if (CAN_SANITIZE_LEAKS) { + __lsan::ScopedInterceptorDisabler disabler; + Symbolizer::LateInitialize(); + } else { + Symbolizer::LateInitialize(); + } + VPrintf(1, "HWAddressSanitizer init done\n"); hwasan_init_is_running = 0; diff --git a/compiler-rt/lib/hwasan/hwasan_allocator.h b/compiler-rt/lib/hwasan/hwasan_allocator.h --- a/compiler-rt/lib/hwasan/hwasan_allocator.h +++ b/compiler-rt/lib/hwasan/hwasan_allocator.h @@ -96,6 +96,8 @@ u32 GetAllocStackId() const; bool FromSmallHeap() const; bool AddrIsInside(uptr addr) const; + inline void SetLsanTag(__lsan::ChunkTag tag); + inline __lsan::ChunkTag GetLsanTag() const; private: friend class __lsan::LsanMetadata; @@ -104,6 +106,7 @@ }; HwasanChunkView FindHeapChunkByAddress(uptr address); +HwasanChunkView FindHeapChunkByAddressFastLocked(uptr address); // Information about one (de)allocation that happened in the past. // These are recorded in a thread-local ring buffer. diff --git a/compiler-rt/lib/hwasan/hwasan_allocator.cpp b/compiler-rt/lib/hwasan/hwasan_allocator.cpp --- a/compiler-rt/lib/hwasan/hwasan_allocator.cpp +++ b/compiler-rt/lib/hwasan/hwasan_allocator.cpp @@ -75,6 +75,15 @@ return (addr >= Beg()) && (addr < Beg() + UsedSize()); } +inline void HwasanChunkView::SetLsanTag(__lsan::ChunkTag tag) { + CHECK(metadata_); + metadata_->SetLsanTag(tag); +} +inline __lsan::ChunkTag HwasanChunkView::GetLsanTag() const { + CHECK(metadata_); + return metadata_->GetLsanTag(); +} + inline void Metadata::SetAllocated(u32 stack, u64 size) { Thread *t = GetCurrentThread(); u64 context = t ? t->unique_id() : kMainTid; @@ -106,8 +115,6 @@ return atomic_load(&alloc_context_id, memory_order_relaxed); } -static const uptr kChunkHeaderSize = sizeof(HwasanChunkView); - void GetAllocatorStats(AllocatorStatCounters s) { allocator.GetStats(s); } @@ -236,6 +243,10 @@ Metadata *meta = reinterpret_cast(allocator.GetMetaData(allocated)); +#if CAN_SANITIZE_LEAKS + meta->SetLsanTag(__lsan::DisabledInThisThread() ? __lsan::kIgnored + : __lsan::kDirectlyLeaked); +#endif meta->SetAllocated(StackDepotPut(*stack), orig_size); RunMallocHooks(user_ptr, size); return user_ptr; @@ -386,6 +397,16 @@ return HwasanChunkView(reinterpret_cast(block), metadata); } +HwasanChunkView FindHeapChunkByAddressFastLocked(uptr address) { + void *block = + allocator.GetBlockBeginFastLocked(reinterpret_cast(address)); + if (!block) + return HwasanChunkView(); + Metadata *metadata = + reinterpret_cast(allocator.GetMetaData(block)); + return HwasanChunkView(reinterpret_cast(block), metadata); +} + static uptr AllocationSize(const void *tagged_ptr) { const void *untagged_ptr = UntagPtr(tagged_ptr); if (!untagged_ptr) return 0; @@ -501,8 +522,9 @@ uptr PointsIntoChunk(void *p) { uptr addr = reinterpret_cast(p); - __hwasan::HwasanChunkView view = __hwasan::FindHeapChunkByAddress(addr); - if (!view.IsAllocated()) + __hwasan::HwasanChunkView view = + __hwasan::FindHeapChunkByAddressFastLocked(addr); + if (!view.IsAllocated()) return 0; uptr chunk = view.Beg(); if (view.AddrIsInside(addr)) @@ -513,12 +535,11 @@ } uptr GetUserBegin(uptr chunk) { - return __hwasan::FindHeapChunkByAddress(chunk).Beg(); + return __hwasan::FindHeapChunkByAddressFastLocked(chunk).Beg(); } LsanMetadata::LsanMetadata(uptr chunk) { - metadata_ = chunk ? reinterpret_cast<__hwasan::Metadata *>( - chunk - __hwasan::kChunkHeaderSize) + metadata_ = chunk ? __hwasan::allocator.GetMetaData((void *)chunk) : nullptr; } @@ -553,6 +574,18 @@ __hwasan::allocator.ForEachChunk(callback, arg); } +IgnoreObjectResult IgnoreObjectLocked(const void *p) { + uptr addr = reinterpret_cast(p); + __hwasan::HwasanChunkView view = __hwasan::FindHeapChunkByAddressFastLocked(addr); + if (!view.IsAllocated() || !view.AddrIsInside(addr)) { + return kIgnoreObjectInvalid; + } + if (view.GetLsanTag() == kIgnored) + return kIgnoreObjectAlreadyIgnored; + view.SetLsanTag(kIgnored); + return kIgnoreObjectSuccess; +} + } // namespace __lsan using namespace __hwasan; diff --git a/compiler-rt/lib/hwasan/hwasan_fuchsia.cpp b/compiler-rt/lib/hwasan/hwasan_fuchsia.cpp --- a/compiler-rt/lib/hwasan/hwasan_fuchsia.cpp +++ b/compiler-rt/lib/hwasan/hwasan_fuchsia.cpp @@ -185,6 +185,8 @@ void HwasanInstallAtForkHandler() {} +void InstallAtExitCheckLeaks() {} + void InitializeOsSupport() { #ifdef __aarch64__ uint32_t features = 0; diff --git a/compiler-rt/lib/hwasan/hwasan_interceptors.cpp b/compiler-rt/lib/hwasan/hwasan_interceptors.cpp --- a/compiler-rt/lib/hwasan/hwasan_interceptors.cpp +++ b/compiler-rt/lib/hwasan/hwasan_interceptors.cpp @@ -220,6 +220,10 @@ namespace __hwasan { int OnExit() { + if (CAN_SANITIZE_LEAKS && common_flags()->detect_leaks && + __lsan::HasReportedLeaks()) { + return common_flags()->exitcode; + } // FIXME: ask frontend whether we need to return failure. return 0; } diff --git a/compiler-rt/lib/hwasan/hwasan_linux.cpp b/compiler-rt/lib/hwasan/hwasan_linux.cpp --- a/compiler-rt/lib/hwasan/hwasan_linux.cpp +++ b/compiler-rt/lib/hwasan/hwasan_linux.cpp @@ -541,6 +541,17 @@ pthread_atfork(before, after, after); } +void InstallAtExitCheckLeaks() { + if (CAN_SANITIZE_LEAKS) { + if (common_flags()->detect_leaks && common_flags()->leak_check_at_exit) { + if (flags()->halt_on_error) + Atexit(__lsan::DoLeakCheck); + else + Atexit(__lsan::DoRecoverableLeakCheckVoid); + } + } +} + } // namespace __hwasan #endif // SANITIZER_FREEBSD || SANITIZER_LINUX || SANITIZER_NETBSD diff --git a/compiler-rt/lib/hwasan/hwasan_poisoning.cpp b/compiler-rt/lib/hwasan/hwasan_poisoning.cpp --- a/compiler-rt/lib/hwasan/hwasan_poisoning.cpp +++ b/compiler-rt/lib/hwasan/hwasan_poisoning.cpp @@ -26,3 +26,8 @@ } } // namespace __hwasan + +// --- Implementation of LSan-specific functions --- {{{1 +namespace __lsan { +bool WordIsPoisoned(uptr addr) { return false; } +} // namespace __lsan diff --git a/compiler-rt/lib/hwasan/hwasan_thread.h b/compiler-rt/lib/hwasan/hwasan_thread.h --- a/compiler-rt/lib/hwasan/hwasan_thread.h +++ b/compiler-rt/lib/hwasan/hwasan_thread.h @@ -69,6 +69,9 @@ Print("Thread: "); } + tid_t os_id() const { return os_id_; } + void set_os_id(tid_t os_id) { os_id_ = os_id; } + uptr &vfork_spill() { return vfork_spill_; } private: @@ -93,6 +96,8 @@ u32 unique_id_; // counting from zero. + tid_t os_id_; + u32 tagging_disabled_; // if non-zero, malloc uses zero tag in this thread. bool announced_; diff --git a/compiler-rt/lib/hwasan/hwasan_thread.cpp b/compiler-rt/lib/hwasan/hwasan_thread.cpp --- a/compiler-rt/lib/hwasan/hwasan_thread.cpp +++ b/compiler-rt/lib/hwasan/hwasan_thread.cpp @@ -5,9 +5,11 @@ #include "hwasan_interface_internal.h" #include "hwasan_mapping.h" #include "hwasan_poisoning.h" +#include "hwasan_thread_list.h" #include "sanitizer_common/sanitizer_atomic.h" #include "sanitizer_common/sanitizer_file.h" #include "sanitizer_common/sanitizer_placement_new.h" +#include "sanitizer_common/sanitizer_thread_registry.h" #include "sanitizer_common/sanitizer_tls_get_addr.h" namespace __hwasan { @@ -149,3 +151,54 @@ } } // namespace __hwasan + +// --- Implementation of LSan-specific functions --- {{{1 +namespace __lsan { + +static __hwasan::HwasanThreadList *GetHwasanThreadListLocked() { + auto &tl = __hwasan::hwasanThreadList(); + tl.CheckLocked(); + return &tl; +} + +static __hwasan::Thread *GetThreadByOsIDLocked(tid_t os_id) { + return GetHwasanThreadListLocked()->FindThread( + [os_id](__hwasan::Thread *t) { return t->os_id() == os_id; }); +} + +void LockThreadRegistry() { __hwasan::hwasanThreadList().Lock(); } + +void UnlockThreadRegistry() { __hwasan::hwasanThreadList().Unlock(); } + +void EnsureMainThreadIDIsCorrect() { + auto *t = __hwasan::GetCurrentThread(); + if (t && (t->IsMainThread())) + t->set_os_id(GetTid()); +} + +bool GetThreadRangesLocked(tid_t os_id, uptr *stack_begin, uptr *stack_end, + uptr *tls_begin, uptr *tls_end, uptr *cache_begin, + uptr *cache_end, DTLS **dtls) { + auto *t = GetThreadByOsIDLocked(os_id); + if (!t) + return false; + *stack_begin = t->stack_bottom(); + *stack_end = t->stack_top(); + *tls_begin = t->tls_begin(); + *tls_end = t->tls_end(); + // HWASan doesn't keep allocator caches in TLS, so these are unused. + *cache_begin = 0; + *cache_end = 0; + *dtls = t->dtls(); + return true; +} + +void GetAllThreadAllocatorCachesLocked(InternalMmapVector *caches) {} + +void ForEachExtraStackRange(tid_t os_id, RangeIteratorCallback callback, + void *arg) {} + +void GetAdditionalThreadContextPtrsLocked(InternalMmapVector *ptrs) {} +void ReportUnsuspendedThreadsLocked(InternalMmapVector *threads) {} + +} // namespace __lsan diff --git a/compiler-rt/lib/hwasan/hwasan_thread_list.h b/compiler-rt/lib/hwasan/hwasan_thread_list.h --- a/compiler-rt/lib/hwasan/hwasan_thread_list.h +++ b/compiler-rt/lib/hwasan/hwasan_thread_list.h @@ -71,7 +71,7 @@ uptr total_stack_size; }; -class HwasanThreadList { +class SANITIZER_MUTEX HwasanThreadList { public: HwasanThreadList(uptr storage, uptr size) : free_space_(storage), free_space_end_(storage + size) { @@ -154,6 +154,15 @@ for (Thread *t : live_list_) cb(t); } + template + Thread *FindThread(CB cb) { + SpinMutexLock l(&live_list_mutex_); + for (Thread *t : live_list_) + if (cb(t)) + return t; + return nullptr; + } + void AddThreadStats(Thread *t) { SpinMutexLock l(&stats_mutex_); stats_.n_live_threads++; @@ -173,6 +182,10 @@ uptr GetRingBufferSize() const { return ring_buffer_size_; } + void Lock() SANITIZER_ACQUIRE() { mtx_.Lock(); } + void CheckLocked() const SANITIZER_CHECK_LOCKED() { mtx_.CheckLocked(); } + void Unlock() SANITIZER_RELEASE() { mtx_.Unlock(); } + private: Thread *AllocThread() { SpinMutexLock l(&free_space_mutex_); @@ -197,6 +210,8 @@ ThreadStats stats_; SpinMutex stats_mutex_; + + Mutex mtx_; }; void InitThreadList(uptr storage, uptr size);