Index: lib/asan/asan_interceptors.cc =================================================================== --- lib/asan/asan_interceptors.cc +++ lib/asan/asan_interceptors.cc @@ -178,6 +178,10 @@ } int OnExit() { + if (CAN_SANITIZE_LEAKS && common_flags()->detect_leaks && + __lsan::GetHasReportedLeaks()) { + return common_flags()->exitcode; + } // FIXME: ask frontend whether we need to return failure. return 0; } Index: lib/lsan/lsan_common.h =================================================================== --- lib/lsan/lsan_common.h +++ lib/lsan/lsan_common.h @@ -226,6 +226,11 @@ // Return the linker module, if valid for the platform. LoadedModule *GetLinker(); +// Mark whether leaks have been detected and handle exits accordingly +void SetHasReportedLeaks(); +bool GetHasReportedLeaks(); +void HandleErrors(); + // Wrapper for chunk metadata operations. class LsanMetadata { public: Index: lib/lsan/lsan_common.cc =================================================================== --- lib/lsan/lsan_common.cc +++ lib/lsan/lsan_common.cc @@ -576,17 +576,18 @@ return false; } +static bool has_errors = false; +void SetHasReportedLeaks() { has_errors = true; } +bool GetHasReportedLeaks() { return has_errors; } + void DoLeakCheck() { BlockingMutexLock l(&global_mutex); static bool already_done; if (already_done) return; already_done = true; bool have_leaks = CheckForLeaks(); - if (!have_leaks) { - return; - } - if (common_flags()->exitcode) { - Die(); + if (have_leaks) { + HandleErrors(); } } Index: lib/lsan/lsan_common_linux.cc =================================================================== --- lib/lsan/lsan_common_linux.cc +++ lib/lsan/lsan_common_linux.cc @@ -100,6 +100,13 @@ void *argument; }; +// While calling Die() here is undefined behavior and can potentially +// cause race conditions, it isn't possible to intercept exit on linux, +// so we have no choice but to call Die() from the atexit handler. +void HandleErrors() { + if (common_flags()->exitcode) Die(); +} + static int DoStopTheWorldCallback(struct dl_phdr_info *info, size_t size, void *data) { DoStopTheWorldParam *param = reinterpret_cast(data); Index: lib/lsan/lsan_common_mac.cc =================================================================== --- lib/lsan/lsan_common_mac.cc +++ lib/lsan/lsan_common_mac.cc @@ -171,6 +171,11 @@ } } +// On darwin, we can intercept _exit gracefully, and return a failing exit code +// if required at that point. Calling Die() here is undefined behavior and +// causes rare race conditions. +void HandleErrors() { SetHasReportedLeaks(); } + void DoStopTheWorld(StopTheWorldCallback callback, void *argument) { StopTheWorld(callback, argument); } Index: lib/lsan/lsan_interceptors.cc =================================================================== --- lib/lsan/lsan_interceptors.cc +++ lib/lsan/lsan_interceptors.cc @@ -352,6 +352,11 @@ return res; } +INTERCEPTOR(void, _exit, int status) { + if (status == 0 && GetHasReportedLeaks()) status = common_flags()->exitcode; + REAL(_exit)(status); +} + namespace __lsan { void InitializeInterceptors() { @@ -371,6 +376,7 @@ LSAN_MAYBE_INTERCEPT_MALLOPT; INTERCEPT_FUNCTION(pthread_create); INTERCEPT_FUNCTION(pthread_join); + INTERCEPT_FUNCTION(_exit); if (pthread_key_create(&g_thread_finalize_key, &thread_finalize)) { Report("LeakSanitizer: failed to create thread key.\n");