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