Actually report ASAN memory leaks

The new asan exit handlers are called to get proper ASAN leak reports (as
calling _exit(0) skips the LSAN reporting stage and exits with success every
time).

They are no-ops when not compiled for ASAN.
This commit is contained in:
Mahmoud Al-Qudsi
2023-04-29 12:07:59 -05:00
parent c43e040c7c
commit 3651e0e9d8
5 changed files with 42 additions and 4 deletions

View File

@@ -25,6 +25,7 @@ set(fish_rust_target "fish-rust")
set(fish_autocxx_gen_dir "${CMAKE_BINARY_DIR}/fish-autocxx-gen/")
set(FISH_CRATE_FEATURES "fish-ffi-tests")
if(NOT DEFINED CARGO_FLAGS)
# Corrosion doesn't like an empty string as FLAGS. This is basically a no-op alternative.
# See https://github.com/corrosion-rs/corrosion/issues/356
@@ -32,11 +33,12 @@ if(NOT DEFINED CARGO_FLAGS)
endif()
if(DEFINED ASAN)
list(APPEND CARGO_FLAGS "-Z" "build-std")
list(APPEND FISH_CRATE_FEATURES "asan")
endif()
corrosion_import_crate(
MANIFEST_PATH "${CMAKE_SOURCE_DIR}/fish-rust/Cargo.toml"
FEATURES "fish-ffi-tests"
FEATURES "${FISH_CRATE_FEATURES}"
FLAGS "${CARGO_FLAGS}"
)

View File

@@ -44,6 +44,7 @@ default = ["fish-ffi-tests"]
fish-ffi-tests = ["inventory"]
# The following features are auto-detected by the build-script and should not be enabled manually.
asan = []
bsd = []
[patch.crates-io]

View File

@@ -75,6 +75,8 @@ mod ffi {
extern "Rust" {
#[cxx_name = "make_detached_pthread"]
fn spawn_ffi(callback: &SharedPtr<CppCallback>) -> bool;
fn asan_before_exit();
fn asan_maybe_exit(code: i32);
}
extern "Rust" {
@@ -265,10 +267,12 @@ pub fn spawn<F: FnOnce() + Send + 'static>(callback: F) -> bool {
// We don't have to port the PTHREAD_CREATE_DETACHED logic. Rust threads are detached
// automatically if the returned join handle is dropped.
let result = match std::thread::Builder::new().spawn(callback) {
let result = match std::thread::Builder::new().spawn(move || {
(callback)();
}) {
Ok(handle) => {
let id = handle.thread().id();
FLOG!(iothread, "rust thread", id, "spawned");
let thread_id = handle.thread().id();
FLOG!(iothread, "rust thread", thread_id, "spawned");
// Drop the handle to detach the thread
drop(handle);
true
@@ -299,6 +303,31 @@ fn spawn_ffi(callback: &cxx::SharedPtr<ffi::CppCallback>) -> bool {
})
}
/// Exits calling onexit handlers if running under ASAN, otherwise does nothing.
///
/// This function is always defined but is a no-op if not running under ASAN. This is to make it
/// more ergonomic to call it in general and also makes it possible to call it via ffi at all.
pub fn asan_maybe_exit(#[allow(unused)] code: i32) {
#[cfg(feature = "asan")]
{
asan_before_exit();
unsafe {
libc::exit(code);
}
}
}
/// When running under ASAN, free up some allocations that would normally have been left for the OS
/// to reclaim to avoid some false positive LSAN reports.
///
/// This function is always defined but is a no-op if not running under ASAN. This is to make it
/// more ergonomic to call it in general and also makes it possible to call it via ffi at all.
pub fn asan_before_exit() {
#[cfg(feature = "asan")]
if !is_forked_child() {
}
}
/// Data shared between the thread pool [`ThreadPool`] and worker threads [`WorkerThread`].
#[derive(Default)]
struct ThreadPoolProtected {

View File

@@ -63,6 +63,7 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
#include "proc.h"
#include "reader.h"
#include "signals.h"
#include "threads.rs.h"
#include "wcstringutil.h"
#include "wutil.h" // IWYU pragma: keep
@@ -614,6 +615,7 @@ int main(int argc, char **argv) {
if (debug_output) {
fclose(debug_output);
}
asan_maybe_exit(exit_status);
exit_without_destructors(exit_status);
return EXIT_FAILURE; // above line should always exit
}

View File

@@ -96,6 +96,7 @@
#include "signals.h"
#include "smoke.rs.h"
#include "termsize.h"
#include "threads.rs.h"
#include "tokenizer.h"
#include "topic_monitor.h"
#include "utf8.h"
@@ -6514,6 +6515,9 @@ int main(int argc, char **argv) {
say(L"Encountered %d errors in low-level tests", err_count);
if (s_test_run_count == 0) say(L"*** No Tests Were Actually Run! ***");
// If under ASAN, reclaim some resources before exiting.
asan_before_exit();
if (err_count != 0) {
return 1;
}