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.

Fixes #77854.

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>
This commit is contained in:
Michael Pratt
2026-02-27 14:57:48 -05:00
committed by Gopher Robot
parent a5f474fc06
commit cc1241f353
3 changed files with 99 additions and 4 deletions

View File

@@ -2084,6 +2084,24 @@ 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
)
func GetScanAlloc() uintptr {
c := getMCache(getg().m)
return c.scanAlloc

View File

@@ -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))
}

65
src/runtime/print_test.go Normal file
View File

@@ -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)
}
})
}