From 1cfd0dc9a3416c1077acfcd44ad0084dd508116a Mon Sep 17 00:00:00 2001 From: Tobias Grieger Date: Thu, 26 Mar 2026 07:47:30 +0000 Subject: runtime: truncate trace strings before inserting into trace map traceStringTable.put inserted the full user-supplied string into the trace map, then only truncated it to MaxEventTrailerDataSize (1024 bytes) when writing to the trace buffer. If the string exceeded the traceRegionAlloc block size (~64KB), this caused a fatal "traceRegion: alloc too large" crash. Move the truncation to the top of put, before the map insertion, so that the map key, map entry, and written output are all consistent and bounded. The existing truncation in writeString is retained: the emit method also calls writeString without going through the map, so writeString still needs its own guard. TestStartRegionLongString reproduces the crash before the fix. Observed in production at CockroachDB: Stopper.RunTask passes singleflight keys (up to ~450KB) as trace region names via trace.StartRegion. See: https://github.com/cockroachdb/cockroach/pull/166669 for context on the trigger. Change-Id: I95d0b2f0bd2e806840b83a0b675ce6d2f0e2c2c5 GitHub-Last-Rev: 3c53061685d5237f9f2fc4522fce6d774776fede GitHub-Pull-Request: golang/go#78348 Reviewed-on: https://go-review.googlesource.com/c/go/+/759140 Auto-Submit: Michael Knyszek Reviewed-by: Michael Knyszek Reviewed-by: Dmitri Shuralyov LUCI-TryBot-Result: Go LUCI --- src/runtime/trace/annotation_test.go | 13 +++++++++++++ src/runtime/tracestring.go | 5 +++++ 2 files changed, 18 insertions(+) (limited to 'src/runtime') diff --git a/src/runtime/trace/annotation_test.go b/src/runtime/trace/annotation_test.go index ea10843230..63e01fab9c 100644 --- a/src/runtime/trace/annotation_test.go +++ b/src/runtime/trace/annotation_test.go @@ -8,9 +8,22 @@ import ( "context" "io" . "runtime/trace" + "strings" "testing" ) +func TestStartRegionLongString(t *testing.T) { + // Regression test: a region name longer than the trace region + // allocator's block size (~64KB) used to crash with + // "traceRegion: alloc too large" because traceStringTable.put + // inserted the full string into the trace map before truncation. + Start(io.Discard) + defer Stop() + + big := strings.Repeat("x", 70_000) + StartRegion(context.Background(), big).End() +} + func BenchmarkStartRegion(b *testing.B) { b.ReportAllocs() ctx, task := NewTask(context.Background(), "benchmark") diff --git a/src/runtime/tracestring.go b/src/runtime/tracestring.go index bd31f06a67..d85299441b 100644 --- a/src/runtime/tracestring.go +++ b/src/runtime/tracestring.go @@ -25,6 +25,11 @@ type traceStringTable struct { // put adds a string to the table, emits it, and returns a unique ID for it. func (t *traceStringTable) put(gen uintptr, s string) uint64 { + // Truncate the string now to avoid wasting space in the + // traceMap and to stay within traceRegionAlloc's block size limit. + if len(s) > tracev2.MaxEventTrailerDataSize { + s = s[:tracev2.MaxEventTrailerDataSize] + } // Put the string in the table. ss := stringStructOf(&s) id, added := t.tab.put(ss.str, uintptr(ss.len)) -- cgit v1.3