From 792c53a963d0c7172028ae670e8fc00876e7787a Mon Sep 17 00:00:00 2001 From: Xavier Roche Date: Mon, 24 Aug 2026 22:35:05 +0200 Subject: [PATCH 1/2] Pin the sanitizer collector's pattern arms, and close its trailing-dir hole tools/ci-sanitizer-report.sh is the only path from a sanitizer finding to a red build, and its pattern also gates `msan`, a required check. Only the UBSan and DEADLYSIGNAL arms had a fixture; mutation testing showed the Leak alternative, the WARNING:(Memory|Thread)Sanitizer arm, and the SUMMARY:.*Sanitizer arm could all be dropped -- singly or in the two combinations that fully blind the collector to LeakSanitizer or MemorySanitizer -- with tests/349_sanitizer-stderr-capture.test staying green throughout. This predates #1404 and is not a regression; it was only newly load-bearing once msan became required. Add an isolated fixture per runtime (ASan, LeakSanitizer, MemorySanitizer, ThreadSanitizer, a SUMMARY-only line) plus two full multi-line reports that pin the LeakSanitizer- and MemorySanitizer-blind combinations. Verified by mutation: each of the seven pattern deletions now turns the test red. need_dir already refused a missing -s STDERR_DIR or LOG_PATH_DIR (#1374), but a missing trailing TEST_LOG_DIR was silently skipped, so a typo'd log path read as a clean run instead of a broken one. Close that the same way, with a test row for it. Closes #1405 Co-Authored-By: Claude Opus 5 (1M context) Signed-off-by: Xavier Roche --- tests/349_sanitizer-stderr-capture.test | 56 +++++++++++++++++++++++++ tools/ci-sanitizer-report.sh | 2 +- 2 files changed, 57 insertions(+), 1 deletion(-) diff --git a/tests/349_sanitizer-stderr-capture.test b/tests/349_sanitizer-stderr-capture.test index 07fa8a57b..ddbc878dd 100644 --- a/tests/349_sanitizer-stderr-capture.test +++ b/tests/349_sanitizer-stderr-capture.test @@ -89,8 +89,64 @@ for bad in "$tmp/absent" "$tmp/plainfile"; do rc=0 "$sh_run" "$report" -s "$cap" "$bad" >"$tmp/bad.out" 2>&1 || rc=$? assert_eq 2 "$rc" "collector took a log dir of $bad" + rc=0 + "$sh_run" "$report" "$logs" "$bad" >"$tmp/bad.out" 2>&1 || rc=$? + assert_eq 2 "$rc" "collector took a trailing TEST_LOG_DIR of $bad" done +# 4c. Each runtime's own header, isolated from every other arm -- a fixture +# carrying two arms' markers would stay caught by the one not under test. +hit() { # hit LABEL LINE... -- LABEL's log must be a match with today's pattern + label=$1 + shift + dir=$tmp/hit-$label + mkdir -p "$dir" + printf '%s\n' "$@" >"$dir/finding.log" + "$sh_run" "$report" "$dir" >"$tmp/hit-$label.out" 2>&1 && + fail "the collector missed $label: $(cat "$tmp/hit-$label.out")" + return 0 +} + +# ASan header (its own doc example), no SUMMARY line. +hit asan '==5==ERROR: AddressSanitizer: heap-buffer-overflow on address 0x60200000eff0 at pc 0x000000455935 bp 0x7ffd7f5c1af8 sp 0x7ffd7f5c1af0' \ + 'READ of size 4 at 0x60200000eff0 thread T0' + +# LeakSanitizer header (clang's doc example), no SUMMARY line. +hit leak '==23646==ERROR: LeakSanitizer: detected memory leaks' \ + '' \ + 'Direct leak of 7 byte(s) in 1 object(s) allocated from:' \ + ' #0 0x4af01b in operator new[](unsigned long) asan_new_delete.cc:66' + +# MemorySanitizer reports as WARNING, never ERROR (see the pattern comment +# above); no SUMMARY line. +hit msan '==29418==WARNING: MemorySanitizer: use-of-uninitialized-value' \ + ' #0 0x4008d6 in main umr.cc:5' + +# ThreadSanitizer: the pattern's WARNING arm claims to cover this too. +hit tsan 'WARNING: ThreadSanitizer: data race (pid=19219)' \ + ' Write of size 4 at 0x7fcf47b21bc0 by thread T1:' + +# A SUMMARY line with no ERROR/WARNING header at all, e.g. a log truncated +# to its tail. +hit summary-only 'SUMMARY: AddressSanitizer: heap-buffer-overflow example.c:5 in main' + +# 4d. A full report carries both a header and a SUMMARY line, so the +# collector only goes blind once BOTH arms are gone -- the two combinations +# #1405 names as leaving `msan`, a required check, silently green. +hit leak-full '==23646==ERROR: LeakSanitizer: detected memory leaks' \ + '' \ + 'Direct leak of 7 byte(s) in 1 object(s) allocated from:' \ + ' #0 0x4af01b in operator new[](unsigned long) asan_new_delete.cc:66' \ + ' #1 0x4da26f in main main.cpp:4' \ + '' \ + 'SUMMARY: AddressSanitizer: 7 byte(s) leaked in 1 allocation(s).' + +hit msan-full '==29418==WARNING: MemorySanitizer: use-of-uninitialized-value' \ + ' #0 0x4008d6 in main umr.cc:5' \ + ' #1 0x7f0c31eaabd4 in __libc_start_main libc-start.c:287' \ + '' \ + 'SUMMARY: MemorySanitizer: use-of-uninitialized-value umr.cc:5 in main' + # 5. What the caller redirected stderr into has to be whole the moment the shim # exits; a tee nobody waited for left it short (#1374). Go a batch at a time, # which one run can win on luck and which also checks that concurrent shims do diff --git a/tools/ci-sanitizer-report.sh b/tools/ci-sanitizer-report.sh index d6b1c1a1f..ab037a6a0 100755 --- a/tools/ci-sanitizer-report.sh +++ b/tools/ci-sanitizer-report.sh @@ -84,7 +84,7 @@ fi # Only the harness logs: an in-tree build puts the .test sources here too, and # one of them carries the pattern as literal text. for dir in "$@"; do - [ -d "$dir" ] || continue + need_dir "TEST_LOG_DIR" "$dir" for f in "$dir"/*.log; do scan "sanitizer output in a test log" "$f" done From 4b0f3f8474c3bc94540017f7264907061465ff6c Mon Sep 17 00:00:00 2001 From: Xavier Roche Date: Mon, 24 Aug 2026 23:21:52 +0200 Subject: [PATCH 2/2] Pin DEADLYSIGNAL and the ERROR-form of MSan/TSan crash reports Review of #1416 found three more pattern pieces the fixtures never exercised: DEADLYSIGNAL, and MemorySanitizer/ThreadSanitizer's ERROR-group form. compiler-rt's signal handler is shared, so a crash makes MSan or TSan report through `ERROR: (Memory|Thread)Sanitizer`, not the WARNING form the earlier fixtures pinned -- exactly the shape a real `msan` crash would take. Add isolated fixtures for all three, sourced from real crash reports (a ThreadSanitizer SEGV, a MemorySanitizer SEGV, and the bare "AddressSanitizer:DEADLYSIGNAL" marker a sanitizer writes ahead of its full report). All three now turn the test red when dropped. Also: drop a stale cross-file comment reference, expand an "e.g.", and tighten a run-on comment. Co-Authored-By: Claude Opus 5 (1M context) Signed-off-by: Xavier Roche --- tests/349_sanitizer-stderr-capture.test | 25 ++++++++++++++++++------- 1 file changed, 18 insertions(+), 7 deletions(-) diff --git a/tests/349_sanitizer-stderr-capture.test b/tests/349_sanitizer-stderr-capture.test index ddbc878dd..4eac8f7f5 100644 --- a/tests/349_sanitizer-stderr-capture.test +++ b/tests/349_sanitizer-stderr-capture.test @@ -117,8 +117,7 @@ hit leak '==23646==ERROR: LeakSanitizer: detected memory leaks' \ 'Direct leak of 7 byte(s) in 1 object(s) allocated from:' \ ' #0 0x4af01b in operator new[](unsigned long) asan_new_delete.cc:66' -# MemorySanitizer reports as WARNING, never ERROR (see the pattern comment -# above); no SUMMARY line. +# MemorySanitizer's own finding is a WARNING, not an ERROR; no SUMMARY line. hit msan '==29418==WARNING: MemorySanitizer: use-of-uninitialized-value' \ ' #0 0x4008d6 in main umr.cc:5' @@ -126,13 +125,25 @@ hit msan '==29418==WARNING: MemorySanitizer: use-of-uninitialized-value' \ hit tsan 'WARNING: ThreadSanitizer: data race (pid=19219)' \ ' Write of size 4 at 0x7fcf47b21bc0 by thread T1:' -# A SUMMARY line with no ERROR/WARNING header at all, e.g. a log truncated -# to its tail. +# A SUMMARY line with no ERROR/WARNING header at all -- for example, a log +# truncated to its tail. hit summary-only 'SUMMARY: AddressSanitizer: heap-buffer-overflow example.c:5 in main' -# 4d. A full report carries both a header and a SUMMARY line, so the -# collector only goes blind once BOTH arms are gone -- the two combinations -# #1405 names as leaving `msan`, a required check, silently green. +# compiler-rt's signal handler is shared: a crash reports through the +# ERROR-group, not the WARNING form above. +hit msan-crash '==46812==ERROR: MemorySanitizer: SEGV on unknown address 0x006000000000 (pc 0xaaaac1b12dd4 bp 0xffffcba33690 sp 0xffffcba31760 T46812)' \ + '==46812==The signal is caused by a WRITE memory access.' + +hit tsan-crash '==205049==ERROR: ThreadSanitizer: SEGV on unknown address 0x000000000000 (pc 0x000000490f8a bp 0x000000000000 sp 0x7f88234bf268 T205050)' \ + '==205049==The signal is caused by a WRITE memory access.' + +# The bare marker a sanitizer writes before its full report, in case that +# report itself crashes; no "ERROR:" prefix, so it pins DEADLYSIGNAL alone. +hit deadlysignal 'AddressSanitizer:DEADLYSIGNAL' + +# 4d. A full report carries a header AND a SUMMARY line: only losing both +# arms blinds it. These are the two combinations #1405 flags for `msan`, a +# required check. hit leak-full '==23646==ERROR: LeakSanitizer: detected memory leaks' \ '' \ 'Direct leak of 7 byte(s) in 1 object(s) allocated from:' \