From 7b7d2e060b447de9c2642848847370a58711ac1c Mon Sep 17 00:00:00 2001 From: Sam James Date: Thu, 19 Oct 2023 15:06:11 +0100 Subject: [PATCH] mtest: set ASAN_OPTIONS and UBSAN_OPTIONS to abort by default Do as we do for MALLOC_PERTURB and set a sensible value for both ASAN_OPTIONS and UBSAN_OPTIONS to abort on failure and give more helpful output at the same time. We do not set these options if the user has exported a value themselves to allow override. In the last week alone, I've observed two cases where people were expecting sanitizers to abort on failure and were surprised when it didn't: 1) https://github.com/git/git/commit/252d693797912ddb2684733160170f0408b73274 2) https://gitlab.freedesktop.org/pipewire/pipewire/-/commit/c47df433f7bc9936fc59b323ca5e53ea4a04f40e Correct this - which is in-line with meson's DWIM/DTRT philosophy. Signed-off-by: Sam James --- docs/markdown/Unit-tests.md | 6 ++++++ docs/markdown/snippets/sanitizers_test.md | 5 +++++ docs/yaml/functions/test.yaml | 4 ++++ mesonbuild/mtest.py | 9 +++++++++ 4 files changed, 24 insertions(+) create mode 100644 docs/markdown/snippets/sanitizers_test.md diff --git a/docs/markdown/Unit-tests.md b/docs/markdown/Unit-tests.md index 030eb19c4dc8..7ad95d2649c1 100644 --- a/docs/markdown/Unit-tests.md +++ b/docs/markdown/Unit-tests.md @@ -38,6 +38,12 @@ set to a random value between 1..255. This can help find memory leaks on configurations using glibc, including with non-GCC compilers. This feature can be disabled as discussed in [[test]]. +### ASAN_OPTIONS and UBSAN_OPTIONS + +By default, the environment variables `ASAN_OPTIONS` and `UBSAN_OPTIONS` are +set to enable aborting on detected violations and to give a backtrace. This +feature can be disabled as discussed in [[test]]. + ## Coverage If you enable coverage measurements by giving Meson the command line diff --git a/docs/markdown/snippets/sanitizers_test.md b/docs/markdown/snippets/sanitizers_test.md new file mode 100644 index 000000000000..24929114beab --- /dev/null +++ b/docs/markdown/snippets/sanitizers_test.md @@ -0,0 +1,5 @@ +## Tests now abort on errors by default under sanitizers + +Sanitizers like AddressSanitizer and UndefinedBehaviorSanitizer do not abort +by default on detected violations. Meson now exports `ASAN_OPTIONS` and `UBSAN_OPTIONS` +when unset in the environment to provide sensible abort-by-default behavior. diff --git a/docs/yaml/functions/test.yaml b/docs/yaml/functions/test.yaml index 4e791671c19e..622b7c3b0ba0 100644 --- a/docs/yaml/functions/test.yaml +++ b/docs/yaml/functions/test.yaml @@ -33,6 +33,10 @@ description: | test(..., env: nomalloc, ...) ``` + By default, the environment variables `ASAN_OPTIONS` and `UBSAN_OPTIONS` are + set to enable aborting on detected violations and to give a backtrace. To suppress + this, `ASAN_OPTIONS` and `UBSAN_OPTIONS` can be set in the environment. + In addition to running individual executables as test cases, `test()` can also be used to invoke an external test harness. In this case, it is best to use `verbose: true` *(since 0.62.0)* and, if supported diff --git a/mesonbuild/mtest.py b/mesonbuild/mtest.py index 1298cc031975..291b1e2fec15 100644 --- a/mesonbuild/mtest.py +++ b/mesonbuild/mtest.py @@ -1418,6 +1418,15 @@ def __init__(self, test: TestSerialisation, env: T.Dict[str, str], name: str, if ('MALLOC_PERTURB_' not in env or not env['MALLOC_PERTURB_']) and not options.benchmark: env['MALLOC_PERTURB_'] = str(random.randint(1, 255)) + # Sanitizers do not default to aborting on error. This is counter to + # expectations when using -Db_sanitize and has led to confusion in the wild + # in CI. Set our own values of {ASAN,UBSAN}_OPTOINS to rectify this, but + # only if the user has not defined them. + if ('ASAN_OPTIONS' not in env or not env['ASAN_OPTIONS']): + env['ASAN_OPTIONS'] = 'halt_on_error=1:abort_on_error=1:print_summary=1' + if ('UBSAN_OPTIONS' not in env or not env['UBSAN_OPTIONS']): + env['UBSAN_OPTIONS'] = 'halt_on_error=1:abort_on_error=1:print_summary=1:print_stacktrace=1' + if self.options.gdb or self.test.timeout is None or self.test.timeout <= 0: timeout = None elif self.options.timeout_multiplier is None: