aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMichael Pratt <mpratt@google.com>2026-02-27 14:57:48 -0500
committerCarlos Amedee <carlos@golang.org>2026-03-19 13:41:07 -0700
commit840d86c06077de2d08a21537eab6f7ac4fca34a8 (patch)
tree7d79670a37249d0a0df284abcdd01f3ede59ffa8
parente87b10ea2a2c6c65b80c4374af42b9c02ac9fb20 (diff)
downloadgo-840d86c06077de2d08a21537eab6f7ac4fca34a8.tar.xz
[release-branch.go1.26] runtime: fix printfloat, printcomplex buffer sizes
The buffers added in CL 716002 for printfloat64 and printcomplex128 are too small to fit the longest formatted values. For values that are too long, AppendFloat allocates, which may cause a crash for prints in places in the runtime where allocation is not allowed. Updates #77854. Fixes #77856. Change-Id: I6a6a636cc2fc5cae9fda25f10b28fd641aa1ff28 Reviewed-on: https://go-review.googlesource.com/c/go/+/749947 Reviewed-by: Russ Cox <rsc@golang.org> Auto-Submit: Michael Pratt <mpratt@google.com> LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com> Reviewed-by: Michael Knyszek <mknyszek@google.com> (cherry picked from commit cc1241f353abbac2df2baf7abe09506be27782e8) Reviewed-on: https://go-review.googlesource.com/c/go/+/750760 Reviewed-by: Mark Freeman <markfreeman@google.com> Reviewed-by: Cherry Mui <cherryyz@google.com>
-rw-r--r--src/runtime/export_test.go18
-rw-r--r--src/runtime/print.go20
-rw-r--r--src/runtime/print_test.go65
3 files changed, 99 insertions, 4 deletions
diff --git a/src/runtime/export_test.go b/src/runtime/export_test.go
index 4f6ef9a3f2..95e2cbb959 100644
--- a/src/runtime/export_test.go
+++ b/src/runtime/export_test.go
@@ -2079,3 +2079,21 @@ func DumpPrintQuoted(s string) string {
return string(buf)
}
+
+// DumpPrint returns the output of print(v).
+func DumpPrint[T any](v T) string {
+ gp := getg()
+ gp.writebuf = make([]byte, 0, 2048)
+ print(v)
+ buf := gp.writebuf
+ gp.writebuf = nil
+
+ return string(buf)
+}
+
+var (
+ Float64Bytes = float64Bytes
+ Float32Bytes = float32Bytes
+ Complex128Bytes = complex128Bytes
+ Complex64Bytes = complex64Bytes
+)
diff --git a/src/runtime/print.go b/src/runtime/print.go
index 5d1bc22809..d65e457a03 100644
--- a/src/runtime/print.go
+++ b/src/runtime/print.go
@@ -122,23 +122,35 @@ func printbool(v bool) {
}
}
+// float64 requires 1+17+1+1+1+3 = 24 bytes max (sign+digits+decimal point+e+sign+exponent digits).
+const float64Bytes = 24
+
func printfloat64(v float64) {
- var buf [20]byte
+ var buf [float64Bytes]byte
gwrite(strconv.AppendFloat(buf[:0], v, 'g', -1, 64))
}
+// float32 requires 1+9+1+1+1+2 = 15 bytes max (sign+digits+decimal point+e+sign+exponent digits).
+const float32Bytes = 15
+
func printfloat32(v float32) {
- var buf [20]byte
+ var buf [float32Bytes]byte
gwrite(strconv.AppendFloat(buf[:0], float64(v), 'g', -1, 32))
}
+// complex128 requires 24+24+1+1+1 = 51 bytes max (paren+float64+float64+i+paren).
+const complex128Bytes = 2*float64Bytes + 3
+
func printcomplex128(c complex128) {
- var buf [44]byte
+ var buf [complex128Bytes]byte
gwrite(strconv.AppendComplex(buf[:0], c, 'g', -1, 128))
}
+// complex64 requires 15+15+1+1+1 = 33 bytes max (paren+float32+float32+i+paren).
+const complex64Bytes = 2*float32Bytes + 3
+
func printcomplex64(c complex64) {
- var buf [44]byte
+ var buf [complex64Bytes]byte
gwrite(strconv.AppendComplex(buf[:0], complex128(c), 'g', -1, 64))
}
diff --git a/src/runtime/print_test.go b/src/runtime/print_test.go
new file mode 100644
index 0000000000..cfc27ed95b
--- /dev/null
+++ b/src/runtime/print_test.go
@@ -0,0 +1,65 @@
+// Copyright 2026 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package runtime_test
+
+import (
+ "math"
+ "runtime"
+ "testing"
+)
+
+func FuzzPrintFloat64(f *testing.F) {
+ f.Add(math.SmallestNonzeroFloat64)
+ f.Add(math.MaxFloat64)
+ f.Add(-1.7976931348623157e+308) // requires 24 digits
+
+ f.Fuzz(func(t *testing.T, v float64) {
+ s := runtime.DumpPrint(v)
+ if len(s) > runtime.Float64Bytes {
+ t.Errorf("print(%f) got %s (len %d) want len <= %d", v, s, len(s), runtime.Float64Bytes)
+ }
+ })
+}
+
+func FuzzPrintFloat32(f *testing.F) {
+ f.Add(float32(math.SmallestNonzeroFloat32))
+ f.Add(float32(math.MaxFloat32))
+ f.Add(float32(-1.06338233e+37)) // requires 15 digits
+
+ f.Fuzz(func(t *testing.T, v float32) {
+ s := runtime.DumpPrint(v)
+ if len(s) > runtime.Float32Bytes {
+ t.Errorf("print(%f) got %s (len %d) want len <= %d", v, s, len(s), runtime.Float32Bytes)
+ }
+ })
+}
+
+func FuzzPrintComplex128(f *testing.F) {
+ f.Add(math.SmallestNonzeroFloat64, math.SmallestNonzeroFloat64)
+ f.Add(math.MaxFloat64, math.MaxFloat64)
+ f.Add(-1.7976931348623157e+308, -1.7976931348623157e+308) // requires 51 digits
+
+ f.Fuzz(func(t *testing.T, r, i float64) {
+ v := complex(r, i)
+ s := runtime.DumpPrint(v)
+ if len(s) > runtime.Complex128Bytes {
+ t.Errorf("print(%f) got %s (len %d) want len <= %d", v, s, len(s), runtime.Complex128Bytes)
+ }
+ })
+}
+
+func FuzzPrintComplex64(f *testing.F) {
+ f.Add(float32(math.SmallestNonzeroFloat32), float32(math.SmallestNonzeroFloat32))
+ f.Add(float32(math.MaxFloat32), float32(math.MaxFloat32))
+ f.Add(float32(-1.06338233e+37), float32(-1.06338233e+37)) // requires 33 digits
+
+ f.Fuzz(func(t *testing.T, r, i float32) {
+ v := complex(r, i)
+ s := runtime.DumpPrint(v)
+ if len(s) > runtime.Complex64Bytes {
+ t.Errorf("print(%f) got %s (len %d) want len <= %d", v, s, len(s), runtime.Complex64Bytes)
+ }
+ })
+}