...
Text file
src/runtime/race/race_linux_arm64.patch
1From cdfdb06c9155080ec97d6e4f4dd90b6e7cefb0ca Mon Sep 17 00:00:00 2001
2From: Michael Pratt <michael@prattmic.com>
3Date: Fri, 12 Dec 2025 16:31:44 +1100
4Subject: [PATCH] [TSan] Zero-initialize Trace.local_head
5
6Trace.local_head is currently uninitialized when Trace is created. It is
7first initialized when the first event is added to the trace, via the
8first call to TraceSwitchPartImpl.
9
10However, ThreadContext::OnFinished uses local_head, assuming that it is
11initialized. If it has not been initialized, we have undefined behavior,
12likely crashing if the contents are garbage. The allocator (Alloc)
13reuses previously allocations, so the contents of the uninitialized
14memory are arbitrary.
15
16In a C/C++ TSAN binary it is likely very difficult for a thread to start
17and exit without a single event inbetween. For Go programs, code running
18in the Go runtime itself is not TSan-instrumented, so goroutines that
19exclusively run runtime code (such as GC workers) can quite reasonably
20have no TSan events.
21
22The addition of such a goroutine to the Go test.c is sufficient to
23trigger this case, though for reliable failure (segfault) I've found it
24necessary to poison the ThreadContext allocation like so:
25
26(Example patch redacted because patch tries to apply this as a real
27patch. See full commit at
28https://github.com/llvm/llvm-project/commit/cdfdb06c9155080ec97d6e4f4dd90b6e7cefb0ca).
29
30The fix is trivial: local_head should be zero-initialized.
31---
32 compiler-rt/lib/tsan/go/test.c | 4 ++++
33 compiler-rt/lib/tsan/rtl/tsan_trace.h | 2 +-
34 2 files changed, 5 insertions(+), 1 deletion(-)
35
36diff --git a/compiler-rt/lib/tsan/go/test.c b/compiler-rt/lib/tsan/go/test.c
37index d328ab1b331d7..fcd396227a4ab 100644
38--- a/compiler-rt/lib/tsan/go/test.c
39+++ b/compiler-rt/lib/tsan/go/test.c
40@@ -91,6 +91,10 @@ int main(void) {
41 __tsan_go_start(thr0, &thr1, (char*)&barfoo + 1);
42 void *thr2 = 0;
43 __tsan_go_start(thr0, &thr2, (char*)&barfoo + 1);
44+ // Goroutine that exits without a single event.
45+ void *thr3 = 0;
46+ __tsan_go_start(thr0, &thr3, (char*)&barfoo + 1);
47+ __tsan_go_end(thr3);
48 __tsan_func_exit(thr0);
49 __tsan_func_enter(thr1, (char*)&foobar + 1);
50 __tsan_func_enter(thr1, (char*)&foobar + 1);
51diff --git a/compiler-rt/lib/tsan/rtl/tsan_trace.h b/compiler-rt/lib/tsan/rtl/tsan_trace.h
52index 01bb7b34f43a2..1e791ff765fec 100644
53--- a/compiler-rt/lib/tsan/rtl/tsan_trace.h
54+++ b/compiler-rt/lib/tsan/rtl/tsan_trace.h
55@@ -190,7 +190,7 @@ struct Trace {
56 Mutex mtx;
57 IList<TraceHeader, &TraceHeader::trace_parts, TracePart> parts;
58 // First node non-queued into ctx->trace_part_recycle.
59- TracePart* local_head;
60+ TracePart* local_head = nullptr;
61 // Final position in the last part for finished threads.
62 Event* final_pos = nullptr;
63 // Number of trace parts allocated on behalf of this trace specifically.
View as plain text