From 4ef278779fcf8548218564020a1050417c923161 Mon Sep 17 00:00:00 2001 From: Michael Pratt Date: Wed, 18 Mar 2026 14:14:20 -0400 Subject: [release-branch.go1.26] runtime/race: apply LLVM zero-initialization fix Upstream TSAN had bug that could result in use of uninitialized memory on Go threads that don't have any TSAN events. For example, if the thread only ever runs the GC. This bug was fixed upstream in https://github.com/llvm/llvm-project/commit/cdfdb06c9155080ec97d6e4f4dd90b6e7cefb0ca. In https://go.dev/issue/78059 we have reports of actual Go crashes due to this bug. Update the prebuilt race sysos to incorporate this fix. The fix is applied as a single patch on top of the existing LLVM revisions to minimize risk of this CL, making it safe to backport. A later CL can update to a newer version of LLVM. Note that all of the patch files are identical. CL 756620 makes racebuild add a unique patch file for each architecture in the event that some arches need distinct patches. linux-loong64 failed race.bash when building the new syso, though they were just timeouts, perhaps from a slow builder. linux-riscv64 is not updated because its builder is too slow (https://go.dev/issue/78258). linux-ppc64le is not updated because its builder is missing curl (https://go.dev/issue/78210). openbsd-amd64 is not updated because its builder is missing unzip (https://go.dev/issue/78212). netbsd-amd64 is not updated because it does not have a LUCI builder (https://go.dev/issue/61121). For #78059. Fixes #78087. Cq-Include-Trybots: luci.golang.try:go1.26-linux-amd64-longtest-race,go1.26-darwin-arm64-race,go1.26-darwin-amd64-race,go1.26-freebsd-amd64-race,go1.26-windows-amd64-race,go1.26-linux-s390x-race,go1.26-linux-arm64-race,go1.26-linux-loong64 Change-Id: I5404cb88af9d86b56b385801f8a9ed106a6a6964 Reviewed-on: https://go-review.googlesource.com/c/go/+/757521 Reviewed-by: Carlos Amedee Reviewed-by: Cherry Mui TryBot-Bypass: Michael Pratt (cherry picked from commit 325eedb2a993aafb7f20738d73a1098036447917) Reviewed-on: https://go-review.googlesource.com/c/go/+/758161 LUCI-TryBot-Result: Go LUCI Auto-Submit: Dmitri Shuralyov --- src/runtime/race/README | 18 +++--- .../race/internal/amd64v1/race_darwin.patch | 63 +++++++++++++++++++++ src/runtime/race/internal/amd64v1/race_darwin.syso | Bin 615552 -> 615616 bytes .../race/internal/amd64v1/race_freebsd.patch | 63 +++++++++++++++++++++ .../race/internal/amd64v1/race_freebsd.syso | Bin 722120 -> 718232 bytes src/runtime/race/internal/amd64v1/race_linux.patch | 63 +++++++++++++++++++++ src/runtime/race/internal/amd64v1/race_linux.syso | Bin 571888 -> 571888 bytes .../race/internal/amd64v1/race_windows.patch | 63 +++++++++++++++++++++ .../race/internal/amd64v1/race_windows.syso | Bin 562886 -> 562902 bytes src/runtime/race/internal/amd64v3/race_linux.patch | 63 +++++++++++++++++++++ src/runtime/race/internal/amd64v3/race_linux.syso | Bin 571728 -> 571728 bytes src/runtime/race/race_darwin_arm64.patch | 63 +++++++++++++++++++++ src/runtime/race/race_darwin_arm64.syso | Bin 502032 -> 502032 bytes src/runtime/race/race_linux_arm64.patch | 63 +++++++++++++++++++++ src/runtime/race/race_linux_arm64.syso | Bin 580032 -> 579512 bytes src/runtime/race/race_linux_loong64.patch | 63 +++++++++++++++++++++ src/runtime/race/race_linux_loong64.syso | Bin 662040 -> 662040 bytes src/runtime/race/race_linux_s390x.patch | 63 +++++++++++++++++++++ src/runtime/race/race_linux_s390x.syso | Bin 582872 -> 582872 bytes 19 files changed, 576 insertions(+), 9 deletions(-) create mode 100644 src/runtime/race/internal/amd64v1/race_darwin.patch create mode 100644 src/runtime/race/internal/amd64v1/race_freebsd.patch create mode 100644 src/runtime/race/internal/amd64v1/race_linux.patch create mode 100644 src/runtime/race/internal/amd64v1/race_windows.patch create mode 100644 src/runtime/race/internal/amd64v3/race_linux.patch create mode 100644 src/runtime/race/race_darwin_arm64.patch create mode 100644 src/runtime/race/race_linux_arm64.patch create mode 100644 src/runtime/race/race_linux_loong64.patch create mode 100644 src/runtime/race/race_linux_s390x.patch diff --git a/src/runtime/race/README b/src/runtime/race/README index a65d463e10..7937c5deb3 100644 --- a/src/runtime/race/README +++ b/src/runtime/race/README @@ -4,16 +4,16 @@ the LLVM project (https://github.com/llvm/llvm-project/tree/main/compiler-rt). To update the .syso files use golang.org/x/build/cmd/racebuild. -internal/amd64v1/race_darwin.syso built with LLVM 51bfeff0e4b0757ff773da6882f4d538996c9b04 and Go e7d582b55dda36e76ce4d0ce770139ca0915b7c5. -internal/amd64v1/race_freebsd.syso built with LLVM 51bfeff0e4b0757ff773da6882f4d538996c9b04 and Go e7d582b55dda36e76ce4d0ce770139ca0915b7c5. -internal/amd64v1/race_linux.syso built with LLVM 51bfeff0e4b0757ff773da6882f4d538996c9b04 and Go e7d582b55dda36e76ce4d0ce770139ca0915b7c5. +internal/amd64v1/race_darwin.syso built with LLVM 51bfeff0e4b0757ff773da6882f4d538996c9b04 with patch internal/amd64v1/race_darwin.patch and Go a61fd428974822a8c57a2b2840fc237e6711b24d. +internal/amd64v1/race_freebsd.syso built with LLVM 51bfeff0e4b0757ff773da6882f4d538996c9b04 with patch internal/amd64v1/race_freebsd.patch and Go a61fd428974822a8c57a2b2840fc237e6711b24d. +internal/amd64v1/race_linux.syso built with LLVM 51bfeff0e4b0757ff773da6882f4d538996c9b04 with patch internal/amd64v1/race_linux.patch and Go a61fd428974822a8c57a2b2840fc237e6711b24d. internal/amd64v1/race_netbsd.syso built with LLVM 51bfeff0e4b0757ff773da6882f4d538996c9b04 and Go e7d582b55dda36e76ce4d0ce770139ca0915b7c5. internal/amd64v1/race_openbsd.syso built with LLVM fcf6ae2f070eba73074b6ec8d8281e54d29dbeeb and Go 8f2db14cd35bbd674cb2988a508306de6655e425. -internal/amd64v1/race_windows.syso built with LLVM 51bfeff0e4b0757ff773da6882f4d538996c9b04 and Go e7d582b55dda36e76ce4d0ce770139ca0915b7c5. -internal/amd64v3/race_linux.syso built with LLVM 51bfeff0e4b0757ff773da6882f4d538996c9b04 and Go e7d582b55dda36e76ce4d0ce770139ca0915b7c5. -race_darwin_arm64.syso built with LLVM 51bfeff0e4b0757ff773da6882f4d538996c9b04 and Go e7d582b55dda36e76ce4d0ce770139ca0915b7c5. -race_linux_arm64.syso built with LLVM 51bfeff0e4b0757ff773da6882f4d538996c9b04 and Go e7d582b55dda36e76ce4d0ce770139ca0915b7c5. -race_linux_loong64.syso built with LLVM 83fe85115da9dc25fa270d2ea8140113c8d49670 and Go 037112464b4439571b45536de9ebe4bc9e10ecb7. +internal/amd64v1/race_windows.syso built with LLVM 51bfeff0e4b0757ff773da6882f4d538996c9b04 with patch internal/amd64v1/race_windows.patch and Go a61fd428974822a8c57a2b2840fc237e6711b24d. +internal/amd64v3/race_linux.syso built with LLVM 51bfeff0e4b0757ff773da6882f4d538996c9b04 with patch internal/amd64v3/race_linux.patch and Go a61fd428974822a8c57a2b2840fc237e6711b24d. +race_darwin_arm64.syso built with LLVM 51bfeff0e4b0757ff773da6882f4d538996c9b04 with patch race_darwin_arm64.patch and Go a61fd428974822a8c57a2b2840fc237e6711b24d. +race_linux_arm64.syso built with LLVM 51bfeff0e4b0757ff773da6882f4d538996c9b04 with patch race_linux_arm64.patch and Go a61fd428974822a8c57a2b2840fc237e6711b24d. +race_linux_loong64.syso built with LLVM 83fe85115da9dc25fa270d2ea8140113c8d49670 with patch race_linux_loong64.patch and Go a61fd428974822a8c57a2b2840fc237e6711b24d. race_linux_ppc64le.syso built with LLVM 51bfeff0e4b0757ff773da6882f4d538996c9b04 and Go e7d582b55dda36e76ce4d0ce770139ca0915b7c5. race_linux_riscv64.syso built with LLVM c3c24be13f7928460ca1e2fe613a1146c868854e and Go a21249436b6e1fd47356361d53dc053bbc074f90. -race_linux_s390x.syso built with LLVM 51bfeff0e4b0757ff773da6882f4d538996c9b04 and Go e7d582b55dda36e76ce4d0ce770139ca0915b7c5. +race_linux_s390x.syso built with LLVM 51bfeff0e4b0757ff773da6882f4d538996c9b04 with patch race_linux_s390x.patch and Go a61fd428974822a8c57a2b2840fc237e6711b24d. diff --git a/src/runtime/race/internal/amd64v1/race_darwin.patch b/src/runtime/race/internal/amd64v1/race_darwin.patch new file mode 100644 index 0000000000..04e515981a --- /dev/null +++ b/src/runtime/race/internal/amd64v1/race_darwin.patch @@ -0,0 +1,63 @@ +From cdfdb06c9155080ec97d6e4f4dd90b6e7cefb0ca Mon Sep 17 00:00:00 2001 +From: Michael Pratt +Date: Fri, 12 Dec 2025 16:31:44 +1100 +Subject: [PATCH] [TSan] Zero-initialize Trace.local_head + +Trace.local_head is currently uninitialized when Trace is created. It is +first initialized when the first event is added to the trace, via the +first call to TraceSwitchPartImpl. + +However, ThreadContext::OnFinished uses local_head, assuming that it is +initialized. If it has not been initialized, we have undefined behavior, +likely crashing if the contents are garbage. The allocator (Alloc) +reuses previously allocations, so the contents of the uninitialized +memory are arbitrary. + +In a C/C++ TSAN binary it is likely very difficult for a thread to start +and exit without a single event inbetween. For Go programs, code running +in the Go runtime itself is not TSan-instrumented, so goroutines that +exclusively run runtime code (such as GC workers) can quite reasonably +have no TSan events. + +The addition of such a goroutine to the Go test.c is sufficient to +trigger this case, though for reliable failure (segfault) I've found it +necessary to poison the ThreadContext allocation like so: + +(Example patch redacted because patch tries to apply this as a real +patch. See full commit at +https://github.com/llvm/llvm-project/commit/cdfdb06c9155080ec97d6e4f4dd90b6e7cefb0ca). + +The fix is trivial: local_head should be zero-initialized. +--- + compiler-rt/lib/tsan/go/test.c | 4 ++++ + compiler-rt/lib/tsan/rtl/tsan_trace.h | 2 +- + 2 files changed, 5 insertions(+), 1 deletion(-) + +diff --git a/compiler-rt/lib/tsan/go/test.c b/compiler-rt/lib/tsan/go/test.c +index d328ab1b331d7..fcd396227a4ab 100644 +--- a/compiler-rt/lib/tsan/go/test.c ++++ b/compiler-rt/lib/tsan/go/test.c +@@ -91,6 +91,10 @@ int main(void) { + __tsan_go_start(thr0, &thr1, (char*)&barfoo + 1); + void *thr2 = 0; + __tsan_go_start(thr0, &thr2, (char*)&barfoo + 1); ++ // Goroutine that exits without a single event. ++ void *thr3 = 0; ++ __tsan_go_start(thr0, &thr3, (char*)&barfoo + 1); ++ __tsan_go_end(thr3); + __tsan_func_exit(thr0); + __tsan_func_enter(thr1, (char*)&foobar + 1); + __tsan_func_enter(thr1, (char*)&foobar + 1); +diff --git a/compiler-rt/lib/tsan/rtl/tsan_trace.h b/compiler-rt/lib/tsan/rtl/tsan_trace.h +index 01bb7b34f43a2..1e791ff765fec 100644 +--- a/compiler-rt/lib/tsan/rtl/tsan_trace.h ++++ b/compiler-rt/lib/tsan/rtl/tsan_trace.h +@@ -190,7 +190,7 @@ struct Trace { + Mutex mtx; + IList parts; + // First node non-queued into ctx->trace_part_recycle. +- TracePart* local_head; ++ TracePart* local_head = nullptr; + // Final position in the last part for finished threads. + Event* final_pos = nullptr; + // Number of trace parts allocated on behalf of this trace specifically. diff --git a/src/runtime/race/internal/amd64v1/race_darwin.syso b/src/runtime/race/internal/amd64v1/race_darwin.syso index d3a9c200a5..1eb7069815 100644 Binary files a/src/runtime/race/internal/amd64v1/race_darwin.syso and b/src/runtime/race/internal/amd64v1/race_darwin.syso differ diff --git a/src/runtime/race/internal/amd64v1/race_freebsd.patch b/src/runtime/race/internal/amd64v1/race_freebsd.patch new file mode 100644 index 0000000000..04e515981a --- /dev/null +++ b/src/runtime/race/internal/amd64v1/race_freebsd.patch @@ -0,0 +1,63 @@ +From cdfdb06c9155080ec97d6e4f4dd90b6e7cefb0ca Mon Sep 17 00:00:00 2001 +From: Michael Pratt +Date: Fri, 12 Dec 2025 16:31:44 +1100 +Subject: [PATCH] [TSan] Zero-initialize Trace.local_head + +Trace.local_head is currently uninitialized when Trace is created. It is +first initialized when the first event is added to the trace, via the +first call to TraceSwitchPartImpl. + +However, ThreadContext::OnFinished uses local_head, assuming that it is +initialized. If it has not been initialized, we have undefined behavior, +likely crashing if the contents are garbage. The allocator (Alloc) +reuses previously allocations, so the contents of the uninitialized +memory are arbitrary. + +In a C/C++ TSAN binary it is likely very difficult for a thread to start +and exit without a single event inbetween. For Go programs, code running +in the Go runtime itself is not TSan-instrumented, so goroutines that +exclusively run runtime code (such as GC workers) can quite reasonably +have no TSan events. + +The addition of such a goroutine to the Go test.c is sufficient to +trigger this case, though for reliable failure (segfault) I've found it +necessary to poison the ThreadContext allocation like so: + +(Example patch redacted because patch tries to apply this as a real +patch. See full commit at +https://github.com/llvm/llvm-project/commit/cdfdb06c9155080ec97d6e4f4dd90b6e7cefb0ca). + +The fix is trivial: local_head should be zero-initialized. +--- + compiler-rt/lib/tsan/go/test.c | 4 ++++ + compiler-rt/lib/tsan/rtl/tsan_trace.h | 2 +- + 2 files changed, 5 insertions(+), 1 deletion(-) + +diff --git a/compiler-rt/lib/tsan/go/test.c b/compiler-rt/lib/tsan/go/test.c +index d328ab1b331d7..fcd396227a4ab 100644 +--- a/compiler-rt/lib/tsan/go/test.c ++++ b/compiler-rt/lib/tsan/go/test.c +@@ -91,6 +91,10 @@ int main(void) { + __tsan_go_start(thr0, &thr1, (char*)&barfoo + 1); + void *thr2 = 0; + __tsan_go_start(thr0, &thr2, (char*)&barfoo + 1); ++ // Goroutine that exits without a single event. ++ void *thr3 = 0; ++ __tsan_go_start(thr0, &thr3, (char*)&barfoo + 1); ++ __tsan_go_end(thr3); + __tsan_func_exit(thr0); + __tsan_func_enter(thr1, (char*)&foobar + 1); + __tsan_func_enter(thr1, (char*)&foobar + 1); +diff --git a/compiler-rt/lib/tsan/rtl/tsan_trace.h b/compiler-rt/lib/tsan/rtl/tsan_trace.h +index 01bb7b34f43a2..1e791ff765fec 100644 +--- a/compiler-rt/lib/tsan/rtl/tsan_trace.h ++++ b/compiler-rt/lib/tsan/rtl/tsan_trace.h +@@ -190,7 +190,7 @@ struct Trace { + Mutex mtx; + IList parts; + // First node non-queued into ctx->trace_part_recycle. +- TracePart* local_head; ++ TracePart* local_head = nullptr; + // Final position in the last part for finished threads. + Event* final_pos = nullptr; + // Number of trace parts allocated on behalf of this trace specifically. diff --git a/src/runtime/race/internal/amd64v1/race_freebsd.syso b/src/runtime/race/internal/amd64v1/race_freebsd.syso index 10edcba4f3..dba6dc5ac5 100644 Binary files a/src/runtime/race/internal/amd64v1/race_freebsd.syso and b/src/runtime/race/internal/amd64v1/race_freebsd.syso differ diff --git a/src/runtime/race/internal/amd64v1/race_linux.patch b/src/runtime/race/internal/amd64v1/race_linux.patch new file mode 100644 index 0000000000..04e515981a --- /dev/null +++ b/src/runtime/race/internal/amd64v1/race_linux.patch @@ -0,0 +1,63 @@ +From cdfdb06c9155080ec97d6e4f4dd90b6e7cefb0ca Mon Sep 17 00:00:00 2001 +From: Michael Pratt +Date: Fri, 12 Dec 2025 16:31:44 +1100 +Subject: [PATCH] [TSan] Zero-initialize Trace.local_head + +Trace.local_head is currently uninitialized when Trace is created. It is +first initialized when the first event is added to the trace, via the +first call to TraceSwitchPartImpl. + +However, ThreadContext::OnFinished uses local_head, assuming that it is +initialized. If it has not been initialized, we have undefined behavior, +likely crashing if the contents are garbage. The allocator (Alloc) +reuses previously allocations, so the contents of the uninitialized +memory are arbitrary. + +In a C/C++ TSAN binary it is likely very difficult for a thread to start +and exit without a single event inbetween. For Go programs, code running +in the Go runtime itself is not TSan-instrumented, so goroutines that +exclusively run runtime code (such as GC workers) can quite reasonably +have no TSan events. + +The addition of such a goroutine to the Go test.c is sufficient to +trigger this case, though for reliable failure (segfault) I've found it +necessary to poison the ThreadContext allocation like so: + +(Example patch redacted because patch tries to apply this as a real +patch. See full commit at +https://github.com/llvm/llvm-project/commit/cdfdb06c9155080ec97d6e4f4dd90b6e7cefb0ca). + +The fix is trivial: local_head should be zero-initialized. +--- + compiler-rt/lib/tsan/go/test.c | 4 ++++ + compiler-rt/lib/tsan/rtl/tsan_trace.h | 2 +- + 2 files changed, 5 insertions(+), 1 deletion(-) + +diff --git a/compiler-rt/lib/tsan/go/test.c b/compiler-rt/lib/tsan/go/test.c +index d328ab1b331d7..fcd396227a4ab 100644 +--- a/compiler-rt/lib/tsan/go/test.c ++++ b/compiler-rt/lib/tsan/go/test.c +@@ -91,6 +91,10 @@ int main(void) { + __tsan_go_start(thr0, &thr1, (char*)&barfoo + 1); + void *thr2 = 0; + __tsan_go_start(thr0, &thr2, (char*)&barfoo + 1); ++ // Goroutine that exits without a single event. ++ void *thr3 = 0; ++ __tsan_go_start(thr0, &thr3, (char*)&barfoo + 1); ++ __tsan_go_end(thr3); + __tsan_func_exit(thr0); + __tsan_func_enter(thr1, (char*)&foobar + 1); + __tsan_func_enter(thr1, (char*)&foobar + 1); +diff --git a/compiler-rt/lib/tsan/rtl/tsan_trace.h b/compiler-rt/lib/tsan/rtl/tsan_trace.h +index 01bb7b34f43a2..1e791ff765fec 100644 +--- a/compiler-rt/lib/tsan/rtl/tsan_trace.h ++++ b/compiler-rt/lib/tsan/rtl/tsan_trace.h +@@ -190,7 +190,7 @@ struct Trace { + Mutex mtx; + IList parts; + // First node non-queued into ctx->trace_part_recycle. +- TracePart* local_head; ++ TracePart* local_head = nullptr; + // Final position in the last part for finished threads. + Event* final_pos = nullptr; + // Number of trace parts allocated on behalf of this trace specifically. diff --git a/src/runtime/race/internal/amd64v1/race_linux.syso b/src/runtime/race/internal/amd64v1/race_linux.syso index f86799087f..3fa4857cf7 100644 Binary files a/src/runtime/race/internal/amd64v1/race_linux.syso and b/src/runtime/race/internal/amd64v1/race_linux.syso differ diff --git a/src/runtime/race/internal/amd64v1/race_windows.patch b/src/runtime/race/internal/amd64v1/race_windows.patch new file mode 100644 index 0000000000..04e515981a --- /dev/null +++ b/src/runtime/race/internal/amd64v1/race_windows.patch @@ -0,0 +1,63 @@ +From cdfdb06c9155080ec97d6e4f4dd90b6e7cefb0ca Mon Sep 17 00:00:00 2001 +From: Michael Pratt +Date: Fri, 12 Dec 2025 16:31:44 +1100 +Subject: [PATCH] [TSan] Zero-initialize Trace.local_head + +Trace.local_head is currently uninitialized when Trace is created. It is +first initialized when the first event is added to the trace, via the +first call to TraceSwitchPartImpl. + +However, ThreadContext::OnFinished uses local_head, assuming that it is +initialized. If it has not been initialized, we have undefined behavior, +likely crashing if the contents are garbage. The allocator (Alloc) +reuses previously allocations, so the contents of the uninitialized +memory are arbitrary. + +In a C/C++ TSAN binary it is likely very difficult for a thread to start +and exit without a single event inbetween. For Go programs, code running +in the Go runtime itself is not TSan-instrumented, so goroutines that +exclusively run runtime code (such as GC workers) can quite reasonably +have no TSan events. + +The addition of such a goroutine to the Go test.c is sufficient to +trigger this case, though for reliable failure (segfault) I've found it +necessary to poison the ThreadContext allocation like so: + +(Example patch redacted because patch tries to apply this as a real +patch. See full commit at +https://github.com/llvm/llvm-project/commit/cdfdb06c9155080ec97d6e4f4dd90b6e7cefb0ca). + +The fix is trivial: local_head should be zero-initialized. +--- + compiler-rt/lib/tsan/go/test.c | 4 ++++ + compiler-rt/lib/tsan/rtl/tsan_trace.h | 2 +- + 2 files changed, 5 insertions(+), 1 deletion(-) + +diff --git a/compiler-rt/lib/tsan/go/test.c b/compiler-rt/lib/tsan/go/test.c +index d328ab1b331d7..fcd396227a4ab 100644 +--- a/compiler-rt/lib/tsan/go/test.c ++++ b/compiler-rt/lib/tsan/go/test.c +@@ -91,6 +91,10 @@ int main(void) { + __tsan_go_start(thr0, &thr1, (char*)&barfoo + 1); + void *thr2 = 0; + __tsan_go_start(thr0, &thr2, (char*)&barfoo + 1); ++ // Goroutine that exits without a single event. ++ void *thr3 = 0; ++ __tsan_go_start(thr0, &thr3, (char*)&barfoo + 1); ++ __tsan_go_end(thr3); + __tsan_func_exit(thr0); + __tsan_func_enter(thr1, (char*)&foobar + 1); + __tsan_func_enter(thr1, (char*)&foobar + 1); +diff --git a/compiler-rt/lib/tsan/rtl/tsan_trace.h b/compiler-rt/lib/tsan/rtl/tsan_trace.h +index 01bb7b34f43a2..1e791ff765fec 100644 +--- a/compiler-rt/lib/tsan/rtl/tsan_trace.h ++++ b/compiler-rt/lib/tsan/rtl/tsan_trace.h +@@ -190,7 +190,7 @@ struct Trace { + Mutex mtx; + IList parts; + // First node non-queued into ctx->trace_part_recycle. +- TracePart* local_head; ++ TracePart* local_head = nullptr; + // Final position in the last part for finished threads. + Event* final_pos = nullptr; + // Number of trace parts allocated on behalf of this trace specifically. diff --git a/src/runtime/race/internal/amd64v1/race_windows.syso b/src/runtime/race/internal/amd64v1/race_windows.syso index 82ac90b919..4cc08d6700 100644 Binary files a/src/runtime/race/internal/amd64v1/race_windows.syso and b/src/runtime/race/internal/amd64v1/race_windows.syso differ diff --git a/src/runtime/race/internal/amd64v3/race_linux.patch b/src/runtime/race/internal/amd64v3/race_linux.patch new file mode 100644 index 0000000000..04e515981a --- /dev/null +++ b/src/runtime/race/internal/amd64v3/race_linux.patch @@ -0,0 +1,63 @@ +From cdfdb06c9155080ec97d6e4f4dd90b6e7cefb0ca Mon Sep 17 00:00:00 2001 +From: Michael Pratt +Date: Fri, 12 Dec 2025 16:31:44 +1100 +Subject: [PATCH] [TSan] Zero-initialize Trace.local_head + +Trace.local_head is currently uninitialized when Trace is created. It is +first initialized when the first event is added to the trace, via the +first call to TraceSwitchPartImpl. + +However, ThreadContext::OnFinished uses local_head, assuming that it is +initialized. If it has not been initialized, we have undefined behavior, +likely crashing if the contents are garbage. The allocator (Alloc) +reuses previously allocations, so the contents of the uninitialized +memory are arbitrary. + +In a C/C++ TSAN binary it is likely very difficult for a thread to start +and exit without a single event inbetween. For Go programs, code running +in the Go runtime itself is not TSan-instrumented, so goroutines that +exclusively run runtime code (such as GC workers) can quite reasonably +have no TSan events. + +The addition of such a goroutine to the Go test.c is sufficient to +trigger this case, though for reliable failure (segfault) I've found it +necessary to poison the ThreadContext allocation like so: + +(Example patch redacted because patch tries to apply this as a real +patch. See full commit at +https://github.com/llvm/llvm-project/commit/cdfdb06c9155080ec97d6e4f4dd90b6e7cefb0ca). + +The fix is trivial: local_head should be zero-initialized. +--- + compiler-rt/lib/tsan/go/test.c | 4 ++++ + compiler-rt/lib/tsan/rtl/tsan_trace.h | 2 +- + 2 files changed, 5 insertions(+), 1 deletion(-) + +diff --git a/compiler-rt/lib/tsan/go/test.c b/compiler-rt/lib/tsan/go/test.c +index d328ab1b331d7..fcd396227a4ab 100644 +--- a/compiler-rt/lib/tsan/go/test.c ++++ b/compiler-rt/lib/tsan/go/test.c +@@ -91,6 +91,10 @@ int main(void) { + __tsan_go_start(thr0, &thr1, (char*)&barfoo + 1); + void *thr2 = 0; + __tsan_go_start(thr0, &thr2, (char*)&barfoo + 1); ++ // Goroutine that exits without a single event. ++ void *thr3 = 0; ++ __tsan_go_start(thr0, &thr3, (char*)&barfoo + 1); ++ __tsan_go_end(thr3); + __tsan_func_exit(thr0); + __tsan_func_enter(thr1, (char*)&foobar + 1); + __tsan_func_enter(thr1, (char*)&foobar + 1); +diff --git a/compiler-rt/lib/tsan/rtl/tsan_trace.h b/compiler-rt/lib/tsan/rtl/tsan_trace.h +index 01bb7b34f43a2..1e791ff765fec 100644 +--- a/compiler-rt/lib/tsan/rtl/tsan_trace.h ++++ b/compiler-rt/lib/tsan/rtl/tsan_trace.h +@@ -190,7 +190,7 @@ struct Trace { + Mutex mtx; + IList parts; + // First node non-queued into ctx->trace_part_recycle. +- TracePart* local_head; ++ TracePart* local_head = nullptr; + // Final position in the last part for finished threads. + Event* final_pos = nullptr; + // Number of trace parts allocated on behalf of this trace specifically. diff --git a/src/runtime/race/internal/amd64v3/race_linux.syso b/src/runtime/race/internal/amd64v3/race_linux.syso index 8ca0f4d5e7..4e6f4900e8 100644 Binary files a/src/runtime/race/internal/amd64v3/race_linux.syso and b/src/runtime/race/internal/amd64v3/race_linux.syso differ diff --git a/src/runtime/race/race_darwin_arm64.patch b/src/runtime/race/race_darwin_arm64.patch new file mode 100644 index 0000000000..04e515981a --- /dev/null +++ b/src/runtime/race/race_darwin_arm64.patch @@ -0,0 +1,63 @@ +From cdfdb06c9155080ec97d6e4f4dd90b6e7cefb0ca Mon Sep 17 00:00:00 2001 +From: Michael Pratt +Date: Fri, 12 Dec 2025 16:31:44 +1100 +Subject: [PATCH] [TSan] Zero-initialize Trace.local_head + +Trace.local_head is currently uninitialized when Trace is created. It is +first initialized when the first event is added to the trace, via the +first call to TraceSwitchPartImpl. + +However, ThreadContext::OnFinished uses local_head, assuming that it is +initialized. If it has not been initialized, we have undefined behavior, +likely crashing if the contents are garbage. The allocator (Alloc) +reuses previously allocations, so the contents of the uninitialized +memory are arbitrary. + +In a C/C++ TSAN binary it is likely very difficult for a thread to start +and exit without a single event inbetween. For Go programs, code running +in the Go runtime itself is not TSan-instrumented, so goroutines that +exclusively run runtime code (such as GC workers) can quite reasonably +have no TSan events. + +The addition of such a goroutine to the Go test.c is sufficient to +trigger this case, though for reliable failure (segfault) I've found it +necessary to poison the ThreadContext allocation like so: + +(Example patch redacted because patch tries to apply this as a real +patch. See full commit at +https://github.com/llvm/llvm-project/commit/cdfdb06c9155080ec97d6e4f4dd90b6e7cefb0ca). + +The fix is trivial: local_head should be zero-initialized. +--- + compiler-rt/lib/tsan/go/test.c | 4 ++++ + compiler-rt/lib/tsan/rtl/tsan_trace.h | 2 +- + 2 files changed, 5 insertions(+), 1 deletion(-) + +diff --git a/compiler-rt/lib/tsan/go/test.c b/compiler-rt/lib/tsan/go/test.c +index d328ab1b331d7..fcd396227a4ab 100644 +--- a/compiler-rt/lib/tsan/go/test.c ++++ b/compiler-rt/lib/tsan/go/test.c +@@ -91,6 +91,10 @@ int main(void) { + __tsan_go_start(thr0, &thr1, (char*)&barfoo + 1); + void *thr2 = 0; + __tsan_go_start(thr0, &thr2, (char*)&barfoo + 1); ++ // Goroutine that exits without a single event. ++ void *thr3 = 0; ++ __tsan_go_start(thr0, &thr3, (char*)&barfoo + 1); ++ __tsan_go_end(thr3); + __tsan_func_exit(thr0); + __tsan_func_enter(thr1, (char*)&foobar + 1); + __tsan_func_enter(thr1, (char*)&foobar + 1); +diff --git a/compiler-rt/lib/tsan/rtl/tsan_trace.h b/compiler-rt/lib/tsan/rtl/tsan_trace.h +index 01bb7b34f43a2..1e791ff765fec 100644 +--- a/compiler-rt/lib/tsan/rtl/tsan_trace.h ++++ b/compiler-rt/lib/tsan/rtl/tsan_trace.h +@@ -190,7 +190,7 @@ struct Trace { + Mutex mtx; + IList parts; + // First node non-queued into ctx->trace_part_recycle. +- TracePart* local_head; ++ TracePart* local_head = nullptr; + // Final position in the last part for finished threads. + Event* final_pos = nullptr; + // Number of trace parts allocated on behalf of this trace specifically. diff --git a/src/runtime/race/race_darwin_arm64.syso b/src/runtime/race/race_darwin_arm64.syso index 706951f471..efc0f2eaf0 100644 Binary files a/src/runtime/race/race_darwin_arm64.syso and b/src/runtime/race/race_darwin_arm64.syso differ diff --git a/src/runtime/race/race_linux_arm64.patch b/src/runtime/race/race_linux_arm64.patch new file mode 100644 index 0000000000..04e515981a --- /dev/null +++ b/src/runtime/race/race_linux_arm64.patch @@ -0,0 +1,63 @@ +From cdfdb06c9155080ec97d6e4f4dd90b6e7cefb0ca Mon Sep 17 00:00:00 2001 +From: Michael Pratt +Date: Fri, 12 Dec 2025 16:31:44 +1100 +Subject: [PATCH] [TSan] Zero-initialize Trace.local_head + +Trace.local_head is currently uninitialized when Trace is created. It is +first initialized when the first event is added to the trace, via the +first call to TraceSwitchPartImpl. + +However, ThreadContext::OnFinished uses local_head, assuming that it is +initialized. If it has not been initialized, we have undefined behavior, +likely crashing if the contents are garbage. The allocator (Alloc) +reuses previously allocations, so the contents of the uninitialized +memory are arbitrary. + +In a C/C++ TSAN binary it is likely very difficult for a thread to start +and exit without a single event inbetween. For Go programs, code running +in the Go runtime itself is not TSan-instrumented, so goroutines that +exclusively run runtime code (such as GC workers) can quite reasonably +have no TSan events. + +The addition of such a goroutine to the Go test.c is sufficient to +trigger this case, though for reliable failure (segfault) I've found it +necessary to poison the ThreadContext allocation like so: + +(Example patch redacted because patch tries to apply this as a real +patch. See full commit at +https://github.com/llvm/llvm-project/commit/cdfdb06c9155080ec97d6e4f4dd90b6e7cefb0ca). + +The fix is trivial: local_head should be zero-initialized. +--- + compiler-rt/lib/tsan/go/test.c | 4 ++++ + compiler-rt/lib/tsan/rtl/tsan_trace.h | 2 +- + 2 files changed, 5 insertions(+), 1 deletion(-) + +diff --git a/compiler-rt/lib/tsan/go/test.c b/compiler-rt/lib/tsan/go/test.c +index d328ab1b331d7..fcd396227a4ab 100644 +--- a/compiler-rt/lib/tsan/go/test.c ++++ b/compiler-rt/lib/tsan/go/test.c +@@ -91,6 +91,10 @@ int main(void) { + __tsan_go_start(thr0, &thr1, (char*)&barfoo + 1); + void *thr2 = 0; + __tsan_go_start(thr0, &thr2, (char*)&barfoo + 1); ++ // Goroutine that exits without a single event. ++ void *thr3 = 0; ++ __tsan_go_start(thr0, &thr3, (char*)&barfoo + 1); ++ __tsan_go_end(thr3); + __tsan_func_exit(thr0); + __tsan_func_enter(thr1, (char*)&foobar + 1); + __tsan_func_enter(thr1, (char*)&foobar + 1); +diff --git a/compiler-rt/lib/tsan/rtl/tsan_trace.h b/compiler-rt/lib/tsan/rtl/tsan_trace.h +index 01bb7b34f43a2..1e791ff765fec 100644 +--- a/compiler-rt/lib/tsan/rtl/tsan_trace.h ++++ b/compiler-rt/lib/tsan/rtl/tsan_trace.h +@@ -190,7 +190,7 @@ struct Trace { + Mutex mtx; + IList parts; + // First node non-queued into ctx->trace_part_recycle. +- TracePart* local_head; ++ TracePart* local_head = nullptr; + // Final position in the last part for finished threads. + Event* final_pos = nullptr; + // Number of trace parts allocated on behalf of this trace specifically. diff --git a/src/runtime/race/race_linux_arm64.syso b/src/runtime/race/race_linux_arm64.syso index 7e9fc5a5ca..6a26af1ca3 100644 Binary files a/src/runtime/race/race_linux_arm64.syso and b/src/runtime/race/race_linux_arm64.syso differ diff --git a/src/runtime/race/race_linux_loong64.patch b/src/runtime/race/race_linux_loong64.patch new file mode 100644 index 0000000000..04e515981a --- /dev/null +++ b/src/runtime/race/race_linux_loong64.patch @@ -0,0 +1,63 @@ +From cdfdb06c9155080ec97d6e4f4dd90b6e7cefb0ca Mon Sep 17 00:00:00 2001 +From: Michael Pratt +Date: Fri, 12 Dec 2025 16:31:44 +1100 +Subject: [PATCH] [TSan] Zero-initialize Trace.local_head + +Trace.local_head is currently uninitialized when Trace is created. It is +first initialized when the first event is added to the trace, via the +first call to TraceSwitchPartImpl. + +However, ThreadContext::OnFinished uses local_head, assuming that it is +initialized. If it has not been initialized, we have undefined behavior, +likely crashing if the contents are garbage. The allocator (Alloc) +reuses previously allocations, so the contents of the uninitialized +memory are arbitrary. + +In a C/C++ TSAN binary it is likely very difficult for a thread to start +and exit without a single event inbetween. For Go programs, code running +in the Go runtime itself is not TSan-instrumented, so goroutines that +exclusively run runtime code (such as GC workers) can quite reasonably +have no TSan events. + +The addition of such a goroutine to the Go test.c is sufficient to +trigger this case, though for reliable failure (segfault) I've found it +necessary to poison the ThreadContext allocation like so: + +(Example patch redacted because patch tries to apply this as a real +patch. See full commit at +https://github.com/llvm/llvm-project/commit/cdfdb06c9155080ec97d6e4f4dd90b6e7cefb0ca). + +The fix is trivial: local_head should be zero-initialized. +--- + compiler-rt/lib/tsan/go/test.c | 4 ++++ + compiler-rt/lib/tsan/rtl/tsan_trace.h | 2 +- + 2 files changed, 5 insertions(+), 1 deletion(-) + +diff --git a/compiler-rt/lib/tsan/go/test.c b/compiler-rt/lib/tsan/go/test.c +index d328ab1b331d7..fcd396227a4ab 100644 +--- a/compiler-rt/lib/tsan/go/test.c ++++ b/compiler-rt/lib/tsan/go/test.c +@@ -91,6 +91,10 @@ int main(void) { + __tsan_go_start(thr0, &thr1, (char*)&barfoo + 1); + void *thr2 = 0; + __tsan_go_start(thr0, &thr2, (char*)&barfoo + 1); ++ // Goroutine that exits without a single event. ++ void *thr3 = 0; ++ __tsan_go_start(thr0, &thr3, (char*)&barfoo + 1); ++ __tsan_go_end(thr3); + __tsan_func_exit(thr0); + __tsan_func_enter(thr1, (char*)&foobar + 1); + __tsan_func_enter(thr1, (char*)&foobar + 1); +diff --git a/compiler-rt/lib/tsan/rtl/tsan_trace.h b/compiler-rt/lib/tsan/rtl/tsan_trace.h +index 01bb7b34f43a2..1e791ff765fec 100644 +--- a/compiler-rt/lib/tsan/rtl/tsan_trace.h ++++ b/compiler-rt/lib/tsan/rtl/tsan_trace.h +@@ -190,7 +190,7 @@ struct Trace { + Mutex mtx; + IList parts; + // First node non-queued into ctx->trace_part_recycle. +- TracePart* local_head; ++ TracePart* local_head = nullptr; + // Final position in the last part for finished threads. + Event* final_pos = nullptr; + // Number of trace parts allocated on behalf of this trace specifically. diff --git a/src/runtime/race/race_linux_loong64.syso b/src/runtime/race/race_linux_loong64.syso index 0c8ecef75d..147dee1dfa 100644 Binary files a/src/runtime/race/race_linux_loong64.syso and b/src/runtime/race/race_linux_loong64.syso differ diff --git a/src/runtime/race/race_linux_s390x.patch b/src/runtime/race/race_linux_s390x.patch new file mode 100644 index 0000000000..04e515981a --- /dev/null +++ b/src/runtime/race/race_linux_s390x.patch @@ -0,0 +1,63 @@ +From cdfdb06c9155080ec97d6e4f4dd90b6e7cefb0ca Mon Sep 17 00:00:00 2001 +From: Michael Pratt +Date: Fri, 12 Dec 2025 16:31:44 +1100 +Subject: [PATCH] [TSan] Zero-initialize Trace.local_head + +Trace.local_head is currently uninitialized when Trace is created. It is +first initialized when the first event is added to the trace, via the +first call to TraceSwitchPartImpl. + +However, ThreadContext::OnFinished uses local_head, assuming that it is +initialized. If it has not been initialized, we have undefined behavior, +likely crashing if the contents are garbage. The allocator (Alloc) +reuses previously allocations, so the contents of the uninitialized +memory are arbitrary. + +In a C/C++ TSAN binary it is likely very difficult for a thread to start +and exit without a single event inbetween. For Go programs, code running +in the Go runtime itself is not TSan-instrumented, so goroutines that +exclusively run runtime code (such as GC workers) can quite reasonably +have no TSan events. + +The addition of such a goroutine to the Go test.c is sufficient to +trigger this case, though for reliable failure (segfault) I've found it +necessary to poison the ThreadContext allocation like so: + +(Example patch redacted because patch tries to apply this as a real +patch. See full commit at +https://github.com/llvm/llvm-project/commit/cdfdb06c9155080ec97d6e4f4dd90b6e7cefb0ca). + +The fix is trivial: local_head should be zero-initialized. +--- + compiler-rt/lib/tsan/go/test.c | 4 ++++ + compiler-rt/lib/tsan/rtl/tsan_trace.h | 2 +- + 2 files changed, 5 insertions(+), 1 deletion(-) + +diff --git a/compiler-rt/lib/tsan/go/test.c b/compiler-rt/lib/tsan/go/test.c +index d328ab1b331d7..fcd396227a4ab 100644 +--- a/compiler-rt/lib/tsan/go/test.c ++++ b/compiler-rt/lib/tsan/go/test.c +@@ -91,6 +91,10 @@ int main(void) { + __tsan_go_start(thr0, &thr1, (char*)&barfoo + 1); + void *thr2 = 0; + __tsan_go_start(thr0, &thr2, (char*)&barfoo + 1); ++ // Goroutine that exits without a single event. ++ void *thr3 = 0; ++ __tsan_go_start(thr0, &thr3, (char*)&barfoo + 1); ++ __tsan_go_end(thr3); + __tsan_func_exit(thr0); + __tsan_func_enter(thr1, (char*)&foobar + 1); + __tsan_func_enter(thr1, (char*)&foobar + 1); +diff --git a/compiler-rt/lib/tsan/rtl/tsan_trace.h b/compiler-rt/lib/tsan/rtl/tsan_trace.h +index 01bb7b34f43a2..1e791ff765fec 100644 +--- a/compiler-rt/lib/tsan/rtl/tsan_trace.h ++++ b/compiler-rt/lib/tsan/rtl/tsan_trace.h +@@ -190,7 +190,7 @@ struct Trace { + Mutex mtx; + IList parts; + // First node non-queued into ctx->trace_part_recycle. +- TracePart* local_head; ++ TracePart* local_head = nullptr; + // Final position in the last part for finished threads. + Event* final_pos = nullptr; + // Number of trace parts allocated on behalf of this trace specifically. diff --git a/src/runtime/race/race_linux_s390x.syso b/src/runtime/race/race_linux_s390x.syso index 35a0f39b3f..bbcfa6f34e 100644 Binary files a/src/runtime/race/race_linux_s390x.syso and b/src/runtime/race/race_linux_s390x.syso differ -- cgit v1.3