diff options
| author | Cherry Mui <cherryyz@google.com> | 2025-09-23 10:32:03 -0400 |
|---|---|---|
| committer | Cherry Mui <cherryyz@google.com> | 2025-09-23 10:32:03 -0400 |
| commit | 2d8cb80d7c4af3dbcb507783938ceb0e071f64e3 (patch) | |
| tree | 719d86801da431f6ba11a84a3b66c60b4e5c1f38 /src | |
| parent | 63a09d6d3d68acedfc9e5fd2daf6febc35aca1d6 (diff) | |
| parent | 9b2d39b75bcc8ced3eaab1c841d7d62e27867931 (diff) | |
| download | go-2d8cb80d7c4af3dbcb507783938ceb0e071f64e3.tar.xz | |
[dev.simd] all: merge master (9b2d39b) into dev.simd
Conflicts:
- src/internal/buildcfg/exp.go
Merge List:
+ 2025-09-22 9b2d39b75b cmd/compile/internal/ssa: match style and formatting
+ 2025-09-22 e23edf5e55 runtime: don't re-read metrics before check in TestReadMetricsSched
+ 2025-09-22 177cd8d763 log/slog: use a pooled json encoder
+ 2025-09-22 2353c15785 cmd/cgo/internal/test: skip TestMultipleAssign when using UCRT on Windows
+ 2025-09-22 32dfd69282 cmd/dist: disable FIPS 140-3 mode when testing maphash with purego
+ 2025-09-19 7f6ff5ec3e cmd/compile: fix doc word
+ 2025-09-19 9693b94be0 runtime: include stderr when objdump fails
+ 2025-09-19 8616981ce6 log/slog: optimize slog Level.String() to avoid fmt.Sprintf
+ 2025-09-19 b8af744360 testing: fix example for unexported identifier
+ 2025-09-19 51dc5bfe6c Revert "cmd/go: disable cgo by default if CC unset and DefaultCC doesn't exist"
+ 2025-09-19 ee7bf06cb3 time: improve ParseDuration performance for invalid input
+ 2025-09-19 f9e61a9a32 cmd/compile: duplicate nil check to two branches of write barrier
+ 2025-09-18 3cf1aaf8b9 runtime: use futexes with 64-bit time on Linux
+ 2025-09-18 0ab038af62 cmd/compile/internal/abi: use clear built-in
+ 2025-09-18 00bf24fdca bytes: use clear in test
+ 2025-09-18 f9701d21d2 crypto: use clear built-in
+ 2025-09-18 a58afe44fa net: fix testHookCanceledDial race
+ 2025-09-18 3203a5da29 net/http: avoid connCount underflow race
+ 2025-09-18 8ca209ec39 context: don't return a non-nil from Err before Done is closed
+ 2025-09-18 3032894e04 runtime: make explicit nil check in heapSetTypeSmallHeader
+ 2025-09-17 ef05b66d61 cmd/internal/obj/riscv: add support for Zicond instructions
+ 2025-09-17 78ef487a6f cmd/compile: fix the issue of shift amount exceeding the valid range
+ 2025-09-17 77aac7bb75 runtime: don't enable heap randomization if MSAN or ASAN is enabled
+ 2025-09-17 465b85eb76 runtime: fix CheckScavengedBitsCleared with randomized heap base
+ 2025-09-17 909704b85e encoding/json/v2: fix typo in comment
+ 2025-09-17 3db5979e8c testing: use reflect.TypeAssert and reflect.TypeFor
+ 2025-09-17 6a8dbbecbf path/filepath: fix EvalSymlinks to return ENOTDIR on plan9
+ 2025-09-17 bffe7ad9f1 go/parser: Add TestBothLineAndLeadComment
+ 2025-09-17 02a888e820 go/ast: document that (*ast.File).Comments is sorted by position
+ 2025-09-16 594deca981 cmd/link: simplify PE relocations mapping
+ 2025-09-16 9df1a289ac go/parser: simplify expectSemi
+ 2025-09-16 72ba117bda internal/buildcfg: enable randomizedHeapBase64 by default
+ 2025-09-16 796ea3bc2e os/user: align test file name and build tags
+ 2025-09-16 a69395eab2 runtime/_mkmalloc: add a copy of cloneNode
+ 2025-09-16 cbdad4fc3c cmd/go: check pattern for utf8 validity before call regexp.MustCompile
+ 2025-09-16 c2d85eb999 cmd/go: disable cgo by default if CC unset and DefaultCC doesn't exist
+ 2025-09-16 ac82fe68aa bytes,strings: remove reference to non-existent SplitFunc
+ 2025-09-16 0b26678db2 cmd/compile: fix mips zerorange implementation
+ 2025-09-16 e2cfc1eb3a cmd/internal/obj/riscv: improve handling of float point moves
+ 2025-09-16 281c632e6e crypto/x509/internal/macos: standardize package name
+ 2025-09-16 61dc7fe30d iter: document that calling yield after terminated range loop causes runtime panic
Change-Id: Ic06019efc855913632003f41eb10c746b3410b0a
Diffstat (limited to 'src')
82 files changed, 905 insertions, 276 deletions
diff --git a/src/bytes/bytes.go b/src/bytes/bytes.go index 9a7f4ee3c9..87183c0195 100644 --- a/src/bytes/bytes.go +++ b/src/bytes/bytes.go @@ -508,7 +508,7 @@ func Fields(s []byte) [][]byte { // It splits the slice s at each run of code points c satisfying f(c) and // returns a slice of subslices of s. If all code points in s satisfy f(c), or // len(s) == 0, an empty slice is returned. Every element of the returned slice is -// non-empty. Unlike [SplitFunc], leading and trailing runs of code points +// non-empty. Unlike [Split], leading and trailing runs of code points // satisfying f(c) are discarded. // // FieldsFunc makes no guarantees about the order in which it calls f(c) diff --git a/src/bytes/bytes_test.go b/src/bytes/bytes_test.go index 03f01582c5..f18915c879 100644 --- a/src/bytes/bytes_test.go +++ b/src/bytes/bytes_test.go @@ -891,9 +891,7 @@ func BenchmarkCountSingle(b *testing.B) { b.Fatal("bad count", j, expect) } } - for i := 0; i < len(buf); i++ { - buf[i] = 0 - } + clear(buf) }) } diff --git a/src/cmd/asm/internal/asm/testdata/riscv64.s b/src/cmd/asm/internal/asm/testdata/riscv64.s index 4f7e7acd77..07a898465f 100644 --- a/src/cmd/asm/internal/asm/testdata/riscv64.s +++ b/src/cmd/asm/internal/asm/testdata/riscv64.s @@ -195,6 +195,12 @@ start: RDTIME X5 // f32210c0 RDINSTRET X5 // f32220c0 + // 12.3: Integer Conditional Operations (Zicond) + CZEROEQZ X5, X6, X7 // b353530e + CZEROEQZ X5, X7 // b3d3530e + CZERONEZ X5, X6, X7 // b373530e + CZERONEZ X5, X7 // b3f3530e + // 13.1: Multiplication Operations MUL X5, X6, X7 // b3035302 MULH X5, X6, X7 // b3135302 @@ -1952,12 +1958,23 @@ start: MOVF 4(X5), F0 // 07a04200 MOVF F0, 4(X5) // 27a20200 MOVF F0, F1 // d3000020 + MOVF X1, F3 // d38100f0 + MOVF F3, X1 // d38001e0 + MOVF X0, F3 // d30100f0 + MOVF $(0.0), F3 // d30100f0 + + // Converted to load of symbol (AUIPC + FLW) + MOVF $(709.78271289338397), F3 // 970f000087a10f00 MOVD 4(X5), F0 // 07b04200 MOVD F0, 4(X5) // 27b20200 MOVD F0, F1 // d3000022 + MOVD F3, X1 // d38001e2 + MOVD X1, F3 // d38100f2 + MOVD X0, F3 // d30100f2 + MOVD $(0.0), F3 // d30100f2 - // Convert to load of symbol (AUIPC + FLD) + // Converted to load of symbol (AUIPC + FLD) MOVD $(709.78271289338397), F3 // 970f000087b10f00 // TLS load with local-exec (LUI + ADDIW + ADD of TP + load) diff --git a/src/cmd/cgo/internal/test/test.go b/src/cmd/cgo/internal/test/test.go index fb4a8250a2..9626407d88 100644 --- a/src/cmd/cgo/internal/test/test.go +++ b/src/cmd/cgo/internal/test/test.go @@ -1096,6 +1096,12 @@ func testErrno(t *testing.T) { } func testMultipleAssign(t *testing.T) { + if runtime.GOOS == "windows" && usesUCRT(t) { + // UCRT's strtol throws an unrecoverable crash when + // using an invalid base (that is, not 0 or 2..36). + // See go.dev/issue/62887. + t.Skip("skipping test on Windows when linking with UCRT") + } p := C.CString("234") n, m := C.strtol(p, nil, 345), C.strtol(p, nil, 10) defer C.free(unsafe.Pointer(p)) diff --git a/src/cmd/cgo/internal/test/test_unix.go b/src/cmd/cgo/internal/test/test_unix.go index 664c4850d3..9f35583125 100644 --- a/src/cmd/cgo/internal/test/test_unix.go +++ b/src/cmd/cgo/internal/test/test_unix.go @@ -6,6 +6,13 @@ package cgotest -import "syscall" +import ( + "syscall" + "testing" +) var syscall_dot_SIGCHLD = syscall.SIGCHLD + +func usesUCRT(t *testing.T) bool { + return false +} diff --git a/src/cmd/cgo/internal/test/test_windows.go b/src/cmd/cgo/internal/test/test_windows.go index 7bfb33a83c..c6f31430cf 100644 --- a/src/cmd/cgo/internal/test/test_windows.go +++ b/src/cmd/cgo/internal/test/test_windows.go @@ -4,6 +4,20 @@ package cgotest -import "syscall" +import ( + "internal/syscall/windows" + "syscall" + "testing" +) var syscall_dot_SIGCHLD syscall.Signal + +// usesUCRT reports whether the test is using the Windows UCRT (Universal C Runtime). +func usesUCRT(t *testing.T) bool { + name, err := syscall.UTF16PtrFromString("ucrtbase.dll") + if err != nil { + t.Fatal(err) + } + h, err := windows.GetModuleHandle(name) + return err == nil && h != 0 +} diff --git a/src/cmd/compile/README.md b/src/cmd/compile/README.md index 79d2336154..1089348030 100644 --- a/src/cmd/compile/README.md +++ b/src/cmd/compile/README.md @@ -57,7 +57,7 @@ terms of these, so the next step after type checking is to convert the syntax and types2 representations to ir and types. This process is referred to as "noding." -Noding using a process called Unified IR, which builds a node representation +Noding uses a process called Unified IR, which builds a node representation using a serialized version of the typechecked code from step 2. Unified IR is also involved in import/export of packages and inlining. diff --git a/src/cmd/compile/internal/abi/abiutils.go b/src/cmd/compile/internal/abi/abiutils.go index cef7885815..7acab36e8d 100644 --- a/src/cmd/compile/internal/abi/abiutils.go +++ b/src/cmd/compile/internal/abi/abiutils.go @@ -664,9 +664,7 @@ func (state *assignState) tryAllocRegs(typ *types.Type) []RegIndex { func (pa *ABIParamAssignment) ComputePadding(storage []uint64) []uint64 { nr := len(pa.Registers) padding := storage[:nr] - for i := 0; i < nr; i++ { - padding[i] = 0 - } + clear(padding) if pa.Type.Kind() != types.TSTRUCT || nr == 0 { return padding } diff --git a/src/cmd/compile/internal/mips/ggen.go b/src/cmd/compile/internal/mips/ggen.go index 023f4d958e..394f015589 100644 --- a/src/cmd/compile/internal/mips/ggen.go +++ b/src/cmd/compile/internal/mips/ggen.go @@ -5,6 +5,7 @@ package mips import ( + "cmd/compile/internal/base" "cmd/compile/internal/objw" "cmd/compile/internal/types" "cmd/internal/obj" @@ -17,7 +18,7 @@ func zerorange(pp *objw.Progs, p *obj.Prog, off, cnt int64, _ *uint32) *obj.Prog } for cnt != 0 { - p = pp.Append(p, mips.AMOVW, obj.TYPE_REG, mips.REGZERO, 0, obj.TYPE_MEM, mips.REGSP, off) + p = pp.Append(p, mips.AMOVW, obj.TYPE_REG, mips.REGZERO, 0, obj.TYPE_MEM, mips.REGSP, base.Ctxt.Arch.FixedFrameSize+off) cnt -= int64(types.PtrSize) off += int64(types.PtrSize) } diff --git a/src/cmd/compile/internal/mips64/ggen.go b/src/cmd/compile/internal/mips64/ggen.go index 0740d9abe8..740d68e335 100644 --- a/src/cmd/compile/internal/mips64/ggen.go +++ b/src/cmd/compile/internal/mips64/ggen.go @@ -17,7 +17,7 @@ func zerorange(pp *objw.Progs, p *obj.Prog, off, cnt int64, _ *uint32) *obj.Prog } for cnt != 0 { - p = pp.Append(p, mips.AMOVV, obj.TYPE_REG, mips.REGZERO, 0, obj.TYPE_MEM, mips.REGSP, off) + p = pp.Append(p, mips.AMOVV, obj.TYPE_REG, mips.REGZERO, 0, obj.TYPE_MEM, mips.REGSP, off+8) cnt -= int64(types.PtrSize) off += int64(types.PtrSize) } diff --git a/src/cmd/compile/internal/ssa/README.md b/src/cmd/compile/internal/ssa/README.md index 8184f9c002..3626f5bb7b 100644 --- a/src/cmd/compile/internal/ssa/README.md +++ b/src/cmd/compile/internal/ssa/README.md @@ -48,8 +48,7 @@ However, certain types don't come from Go and are special; below we will cover Some operators contain an auxiliary field. The aux fields are usually printed as enclosed in `[]` or `{}`, and could be the constant op argument, argument type, -etc. -for example: +etc. For example: v13 (?) = Const64 <int> [1] diff --git a/src/cmd/compile/internal/ssa/_gen/LOONG64.rules b/src/cmd/compile/internal/ssa/_gen/LOONG64.rules index d0a64364d6..0d2384143c 100644 --- a/src/cmd/compile/internal/ssa/_gen/LOONG64.rules +++ b/src/cmd/compile/internal/ssa/_gen/LOONG64.rules @@ -717,7 +717,8 @@ (SRLVconst [rc] (MOVBUreg x)) && rc >= 8 => (MOVVconst [0]) // (x + x) << c -> x << c+1 -((SLLV|SLL)const [c] (ADDV x x)) => ((SLLV|SLL)const [c+1] x) +((SLLV|SLL)const <t> [c] (ADDV x x)) && c < t.Size() * 8 - 1 => ((SLLV|SLL)const [c+1] x) +((SLLV|SLL)const <t> [c] (ADDV x x)) && c >= t.Size() * 8 - 1 => (MOVVconst [0]) // mul by constant (MULV _ (MOVVconst [0])) => (MOVVconst [0]) diff --git a/src/cmd/compile/internal/ssa/_gen/LOONG64Ops.go b/src/cmd/compile/internal/ssa/_gen/LOONG64Ops.go index 0d5e0eb76f..a3db4def56 100644 --- a/src/cmd/compile/internal/ssa/_gen/LOONG64Ops.go +++ b/src/cmd/compile/internal/ssa/_gen/LOONG64Ops.go @@ -247,7 +247,7 @@ func init() { {name: "SLL", argLength: 2, reg: gp21, asm: "SLL"}, // arg0 << arg1, shift amount is mod 32 {name: "SLLV", argLength: 2, reg: gp21, asm: "SLLV"}, // arg0 << arg1, shift amount is mod 64 {name: "SLLconst", argLength: 1, reg: gp11, asm: "SLL", aux: "Int64"}, // arg0 << auxInt, auxInt should be in the range 0 to 31. - {name: "SLLVconst", argLength: 1, reg: gp11, asm: "SLLV", aux: "Int64"}, // arg0 << auxInt + {name: "SLLVconst", argLength: 1, reg: gp11, asm: "SLLV", aux: "Int64"}, // arg0 << auxInt, auxInt should be in the range 0 to 63. {name: "SRL", argLength: 2, reg: gp21, asm: "SRL"}, // arg0 >> arg1, shift amount is mod 32 {name: "SRLV", argLength: 2, reg: gp21, asm: "SRLV"}, // arg0 >> arg1, unsigned, shift amount is mod 64 {name: "SRLconst", argLength: 1, reg: gp11, asm: "SRL", aux: "Int64"}, // arg0 >> auxInt, auxInt should be in the range 0 to 31. diff --git a/src/cmd/compile/internal/ssa/rewriteLOONG64.go b/src/cmd/compile/internal/ssa/rewriteLOONG64.go index 6a9b723c8c..3990b2833b 100644 --- a/src/cmd/compile/internal/ssa/rewriteLOONG64.go +++ b/src/cmd/compile/internal/ssa/rewriteLOONG64.go @@ -6561,15 +6561,17 @@ func rewriteValueLOONG64_OpLOONG64SLLV(v *Value) bool { } func rewriteValueLOONG64_OpLOONG64SLLVconst(v *Value) bool { v_0 := v.Args[0] - // match: (SLLVconst [c] (ADDV x x)) + // match: (SLLVconst <t> [c] (ADDV x x)) + // cond: c < t.Size() * 8 - 1 // result: (SLLVconst [c+1] x) for { + t := v.Type c := auxIntToInt64(v.AuxInt) if v_0.Op != OpLOONG64ADDV { break } x := v_0.Args[1] - if x != v_0.Args[0] { + if x != v_0.Args[0] || !(c < t.Size()*8-1) { break } v.reset(OpLOONG64SLLVconst) @@ -6577,6 +6579,23 @@ func rewriteValueLOONG64_OpLOONG64SLLVconst(v *Value) bool { v.AddArg(x) return true } + // match: (SLLVconst <t> [c] (ADDV x x)) + // cond: c >= t.Size() * 8 - 1 + // result: (MOVVconst [0]) + for { + t := v.Type + c := auxIntToInt64(v.AuxInt) + if v_0.Op != OpLOONG64ADDV { + break + } + x := v_0.Args[1] + if x != v_0.Args[0] || !(c >= t.Size()*8-1) { + break + } + v.reset(OpLOONG64MOVVconst) + v.AuxInt = int64ToAuxInt(0) + return true + } // match: (SLLVconst [c] (MOVVconst [d])) // result: (MOVVconst [d<<uint64(c)]) for { @@ -6593,15 +6612,17 @@ func rewriteValueLOONG64_OpLOONG64SLLVconst(v *Value) bool { } func rewriteValueLOONG64_OpLOONG64SLLconst(v *Value) bool { v_0 := v.Args[0] - // match: (SLLconst [c] (ADDV x x)) + // match: (SLLconst <t> [c] (ADDV x x)) + // cond: c < t.Size() * 8 - 1 // result: (SLLconst [c+1] x) for { + t := v.Type c := auxIntToInt64(v.AuxInt) if v_0.Op != OpLOONG64ADDV { break } x := v_0.Args[1] - if x != v_0.Args[0] { + if x != v_0.Args[0] || !(c < t.Size()*8-1) { break } v.reset(OpLOONG64SLLconst) @@ -6609,6 +6630,23 @@ func rewriteValueLOONG64_OpLOONG64SLLconst(v *Value) bool { v.AddArg(x) return true } + // match: (SLLconst <t> [c] (ADDV x x)) + // cond: c >= t.Size() * 8 - 1 + // result: (MOVVconst [0]) + for { + t := v.Type + c := auxIntToInt64(v.AuxInt) + if v_0.Op != OpLOONG64ADDV { + break + } + x := v_0.Args[1] + if x != v_0.Args[0] || !(c >= t.Size()*8-1) { + break + } + v.reset(OpLOONG64MOVVconst) + v.AuxInt = int64ToAuxInt(0) + return true + } return false } func rewriteValueLOONG64_OpLOONG64SRA(v *Value) bool { diff --git a/src/cmd/compile/internal/ssa/writebarrier.go b/src/cmd/compile/internal/ssa/writebarrier.go index 9ef3667d51..ec6901f13e 100644 --- a/src/cmd/compile/internal/ssa/writebarrier.go +++ b/src/cmd/compile/internal/ssa/writebarrier.go @@ -303,6 +303,15 @@ func writebarrier(f *Func) { mem := stores[0].MemoryArg() pos := stores[0].Pos + // If there is a nil check before the WB store, duplicate it to + // the two branches, where the store and the WB load occur. So + // they are more likely be removed by late nilcheck removal (which + // is block-local). + var nilcheck, nilcheckThen, nilcheckEnd *Value + if a := stores[0].Args[0]; a.Op == OpNilCheck && a.Args[1] == mem { + nilcheck = a + } + // If the source of a MoveWB is volatile (will be clobbered by a // function call), we need to copy it to a temporary location, as // marshaling the args of wbMove might clobber the value we're @@ -377,6 +386,10 @@ func writebarrier(f *Func) { // For each write barrier store, append write barrier code to bThen. memThen := mem + if nilcheck != nil { + nilcheckThen = bThen.NewValue2(nilcheck.Pos, OpNilCheck, nilcheck.Type, nilcheck.Args[0], memThen) + } + // Note: we can issue the write barrier code in any order. In particular, // it doesn't matter if they are in a different order *even if* they end // up referring to overlapping memory regions. For instance if an OpStore @@ -447,6 +460,9 @@ func writebarrier(f *Func) { // take care of the vast majority of these. We could // patch this up in the signal handler, or use XCHG to // combine the read and the write. + if ptr == nilcheck { + ptr = nilcheckThen + } oldVal := bThen.NewValue2(pos, OpLoad, types.Types[types.TUINTPTR], ptr, memThen) // Save old value to write buffer. addEntry(pos, oldVal) @@ -459,9 +475,12 @@ func writebarrier(f *Func) { // Now do the rare cases, Zeros and Moves. for _, w := range stores { pos := w.Pos + dst := w.Args[0] + if dst == nilcheck { + dst = nilcheckThen + } switch w.Op { case OpZeroWB: - dst := w.Args[0] typ := reflectdata.TypeLinksym(w.Aux.(*types.Type)) // zeroWB(&typ, dst) taddr := b.NewValue1A(pos, OpAddr, b.Func.Config.Types.Uintptr, typ, sb) @@ -469,7 +488,6 @@ func writebarrier(f *Func) { f.fe.Func().SetWBPos(pos) nWBops-- case OpMoveWB: - dst := w.Args[0] src := w.Args[1] if isVolatile(src) { for _, c := range volatiles { @@ -491,24 +509,29 @@ func writebarrier(f *Func) { // merge memory mem = bEnd.NewValue2(pos, OpPhi, types.TypeMem, mem, memThen) + if nilcheck != nil { + nilcheckEnd = bEnd.NewValue2(nilcheck.Pos, OpNilCheck, nilcheck.Type, nilcheck.Args[0], mem) + } + // Do raw stores after merge point. for _, w := range stores { pos := w.Pos + dst := w.Args[0] + if dst == nilcheck { + dst = nilcheckEnd + } switch w.Op { case OpStoreWB: - ptr := w.Args[0] val := w.Args[1] if buildcfg.Experiment.CgoCheck2 { // Issue cgo checking code. - mem = wbcall(pos, bEnd, cgoCheckPtrWrite, sp, mem, ptr, val) + mem = wbcall(pos, bEnd, cgoCheckPtrWrite, sp, mem, dst, val) } - mem = bEnd.NewValue3A(pos, OpStore, types.TypeMem, w.Aux, ptr, val, mem) + mem = bEnd.NewValue3A(pos, OpStore, types.TypeMem, w.Aux, dst, val, mem) case OpZeroWB: - dst := w.Args[0] mem = bEnd.NewValue2I(pos, OpZero, types.TypeMem, w.AuxInt, dst, mem) mem.Aux = w.Aux case OpMoveWB: - dst := w.Args[0] src := w.Args[1] if isVolatile(src) { for _, c := range volatiles { @@ -529,9 +552,8 @@ func writebarrier(f *Func) { case OpVarDef, OpVarLive: mem = bEnd.NewValue1A(pos, w.Op, types.TypeMem, w.Aux, mem) case OpStore: - ptr := w.Args[0] val := w.Args[1] - mem = bEnd.NewValue3A(pos, OpStore, types.TypeMem, w.Aux, ptr, val, mem) + mem = bEnd.NewValue3A(pos, OpStore, types.TypeMem, w.Aux, dst, val, mem) } } @@ -557,6 +579,9 @@ func writebarrier(f *Func) { f.freeValue(w) } } + if nilcheck != nil && nilcheck.Uses == 0 { + nilcheck.reset(OpInvalid) + } // put values after the store sequence into the end block bEnd.Values = append(bEnd.Values, after...) diff --git a/src/cmd/dist/test.go b/src/cmd/dist/test.go index 1200303334..b9673564ec 100644 --- a/src/cmd/dist/test.go +++ b/src/cmd/dist/test.go @@ -705,6 +705,7 @@ func (t *tester) registerTests() { timeout: 300 * time.Second, tags: []string{"purego"}, pkg: "hash/maphash", + env: []string{"GODEBUG=fips140=off"}, // FIPS 140-3 mode is incompatible with purego }) } diff --git a/src/cmd/go/internal/modget/query.go b/src/cmd/go/internal/modget/query.go index f95b503d8f..05872d52ec 100644 --- a/src/cmd/go/internal/modget/query.go +++ b/src/cmd/go/internal/modget/query.go @@ -10,6 +10,7 @@ import ( "regexp" "strings" "sync" + "unicode/utf8" "cmd/go/internal/base" "cmd/go/internal/gover" @@ -285,6 +286,11 @@ func reportError(q *query, err error) { // TODO(bcmills): Use errors.As to unpack these errors instead of parsing // strings with regular expressions. + if !utf8.ValidString(q.pattern) || !utf8.ValidString(q.version) { + base.Errorf("go: %s", errStr) + return + } + patternRE := regexp.MustCompile("(?m)(?:[ \t(\"`]|^)" + regexp.QuoteMeta(q.pattern) + "(?:[ @:;)\"`]|$)") if patternRE.MatchString(errStr) { if q.rawVersion == "" { diff --git a/src/cmd/go/testdata/script/get_panic_issue75251.txt b/src/cmd/go/testdata/script/get_panic_issue75251.txt new file mode 100644 index 0000000000..2cc3f3a9c4 --- /dev/null +++ b/src/cmd/go/testdata/script/get_panic_issue75251.txt @@ -0,0 +1,16 @@ +# Issue #75251: Don't panic if the package path or the package version +# contains invalid UTF-8 characters. + +go mod init m + +! go get golang.org/x/net/http/httpgutsv0.43.0 # contains 0xff byte +! stderr panic +stderr 'malformed module path' + +! go get golang.org/x/net/http/httpguts@v0.43.0 # contains 0xff byte +! stderr panic +stderr 'malformed module path' + +! go get golang.org/x/net/http/httpguts@v0.43.0 # contains 0xff byte +! stderr panic +stderr 'disallowed version string' diff --git a/src/cmd/internal/obj/riscv/anames.go b/src/cmd/internal/obj/riscv/anames.go index 88ac746573..a8807fc7a8 100644 --- a/src/cmd/internal/obj/riscv/anames.go +++ b/src/cmd/internal/obj/riscv/anames.go @@ -61,6 +61,8 @@ var Anames = []string{ "CSRRWI", "CSRRSI", "CSRRCI", + "CZEROEQZ", + "CZERONEZ", "MUL", "MULH", "MULHU", diff --git a/src/cmd/internal/obj/riscv/cpu.go b/src/cmd/internal/obj/riscv/cpu.go index e265e04482..305ef061e3 100644 --- a/src/cmd/internal/obj/riscv/cpu.go +++ b/src/cmd/internal/obj/riscv/cpu.go @@ -409,6 +409,10 @@ const ( ACSRRSI ACSRRCI + // 12.3: Integer Conditional Operations (Zicond) + ACZEROEQZ + ACZERONEZ + // 13.1: Multiplication Operations AMUL AMULH diff --git a/src/cmd/internal/obj/riscv/inst.go b/src/cmd/internal/obj/riscv/inst.go index a6a03dc565..a5b3acdb18 100644 --- a/src/cmd/internal/obj/riscv/inst.go +++ b/src/cmd/internal/obj/riscv/inst.go @@ -1,4 +1,4 @@ -// Code generated by ./parse.py -go rv64_a rv64_c rv64_d rv64_f rv64_i rv64_m rv64_q rv64_zba rv64_zbb rv64_zbs rv_a rv_c rv_c_d rv_d rv_f rv_i rv_m rv_q rv_s rv_system rv_v rv_zba rv_zbb rv_zbs rv_zicsr; DO NOT EDIT. +// Code generated by ./parse.py -go rv64_a rv64_c rv64_d rv64_f rv64_i rv64_m rv64_q rv64_zba rv64_zbb rv64_zbs rv_a rv_c rv_c_d rv_d rv_f rv_i rv_m rv_q rv_s rv_system rv_v rv_zba rv_zbb rv_zbs rv_zicond rv_zicsr; DO NOT EDIT. package riscv import "cmd/internal/obj" @@ -194,6 +194,10 @@ func encode(a obj.As) *inst { return &inst{0x13, 0x1, 0x0, 0x1, 1537, 0x30} case ACTZW: return &inst{0x1b, 0x1, 0x0, 0x1, 1537, 0x30} + case ACZEROEQZ: + return &inst{0x33, 0x5, 0x0, 0x0, 224, 0x7} + case ACZERONEZ: + return &inst{0x33, 0x7, 0x0, 0x0, 224, 0x7} case ADIV: return &inst{0x33, 0x4, 0x0, 0x0, 32, 0x1} case ADIVU: diff --git a/src/cmd/internal/obj/riscv/obj.go b/src/cmd/internal/obj/riscv/obj.go index 1538d03179..9d595f301c 100644 --- a/src/cmd/internal/obj/riscv/obj.go +++ b/src/cmd/internal/obj/riscv/obj.go @@ -29,6 +29,7 @@ import ( "internal/abi" "internal/buildcfg" "log" + "math" "math/bits" "strings" ) @@ -145,9 +146,29 @@ func progedit(ctxt *obj.Link, p *obj.Prog, newprog obj.ProgAlloc) { p.From.Offset = 0 } + case AMOVF: + if p.From.Type == obj.TYPE_FCONST && p.From.Name == obj.NAME_NONE && p.From.Reg == obj.REG_NONE { + f64 := p.From.Val.(float64) + f32 := float32(f64) + if math.Float32bits(f32) == 0 { + p.From.Type = obj.TYPE_REG + p.From.Reg = REG_ZERO + break + } + p.From.Type = obj.TYPE_MEM + p.From.Sym = ctxt.Float32Sym(f32) + p.From.Name = obj.NAME_EXTERN + p.From.Offset = 0 + } + case AMOVD: if p.From.Type == obj.TYPE_FCONST && p.From.Name == obj.NAME_NONE && p.From.Reg == obj.REG_NONE { f64 := p.From.Val.(float64) + if math.Float64bits(f64) == 0 { + p.From.Type = obj.TYPE_REG + p.From.Reg = REG_ZERO + break + } p.From.Type = obj.TYPE_MEM p.From.Sym = ctxt.Float64Sym(f64) p.From.Name = obj.NAME_EXTERN @@ -1927,6 +1948,10 @@ var instructions = [ALAST & obj.AMask]instructionData{ ACSRRW & obj.AMask: {enc: iIIEncoding, immForm: ACSRRWI}, ACSRRWI & obj.AMask: {enc: iIIEncoding}, + // 12.3: "Zicond" Extension for Integer Conditional Operations + ACZERONEZ & obj.AMask: {enc: rIIIEncoding, ternary: true}, + ACZEROEQZ & obj.AMask: {enc: rIIIEncoding, ternary: true}, + // 13.1: Multiplication Operations AMUL & obj.AMask: {enc: rIIIEncoding, ternary: true}, AMULH & obj.AMask: {enc: rIIIEncoding, ternary: true}, @@ -3254,16 +3279,37 @@ func instructionsForMOV(p *obj.Prog) []*instruction { case p.From.Type == obj.TYPE_REG && p.To.Type == obj.TYPE_REG: // Handle register to register moves. switch p.As { - case AMOV: // MOV Ra, Rb -> ADDI $0, Ra, Rb + case AMOV: + // MOV Ra, Rb -> ADDI $0, Ra, Rb ins.as, ins.rs1, ins.rs2, ins.imm = AADDI, uint32(p.From.Reg), obj.REG_NONE, 0 - case AMOVW: // MOVW Ra, Rb -> ADDIW $0, Ra, Rb + case AMOVW: + // MOVW Ra, Rb -> ADDIW $0, Ra, Rb ins.as, ins.rs1, ins.rs2, ins.imm = AADDIW, uint32(p.From.Reg), obj.REG_NONE, 0 - case AMOVBU: // MOVBU Ra, Rb -> ANDI $255, Ra, Rb + case AMOVBU: + // MOVBU Ra, Rb -> ANDI $255, Ra, Rb ins.as, ins.rs1, ins.rs2, ins.imm = AANDI, uint32(p.From.Reg), obj.REG_NONE, 255 - case AMOVF: // MOVF Ra, Rb -> FSGNJS Ra, Ra, Rb - ins.as, ins.rs1 = AFSGNJS, uint32(p.From.Reg) - case AMOVD: // MOVD Ra, Rb -> FSGNJD Ra, Ra, Rb - ins.as, ins.rs1 = AFSGNJD, uint32(p.From.Reg) + case AMOVF: + // MOVF Ra, Rb -> FSGNJS Ra, Ra, Rb + // or -> FMVWX Ra, Rb + // or -> FMVXW Ra, Rb + if ins.rs2 >= REG_X0 && ins.rs2 <= REG_X31 && ins.rd >= REG_F0 && ins.rd <= REG_F31 { + ins.as = AFMVWX + } else if ins.rs2 >= REG_F0 && ins.rs2 <= REG_F31 && ins.rd >= REG_X0 && ins.rd <= REG_X31 { + ins.as = AFMVXW + } else { + ins.as, ins.rs1 = AFSGNJS, uint32(p.From.Reg) + } + case AMOVD: + // MOVD Ra, Rb -> FSGNJD Ra, Ra, Rb + // or -> FMVDX Ra, Rb + // or -> FMVXD Ra, Rb + if ins.rs2 >= REG_X0 && ins.rs2 <= REG_X31 && ins.rd >= REG_F0 && ins.rd <= REG_F31 { + ins.as = AFMVDX + } else if ins.rs2 >= REG_F0 && ins.rs2 <= REG_F31 && ins.rd >= REG_X0 && ins.rd <= REG_X31 { + ins.as = AFMVXD + } else { + ins.as, ins.rs1 = AFSGNJD, uint32(p.From.Reg) + } case AMOVB, AMOVH: if buildcfg.GORISCV64 >= 22 { // Use SEXTB or SEXTH to extend. diff --git a/src/cmd/internal/pkgpattern/pkgpattern.go b/src/cmd/internal/pkgpattern/pkgpattern.go index 1496eebb3e..5bbe8a52fb 100644 --- a/src/cmd/internal/pkgpattern/pkgpattern.go +++ b/src/cmd/internal/pkgpattern/pkgpattern.go @@ -7,6 +7,7 @@ package pkgpattern import ( "regexp" "strings" + "unicode/utf8" ) // Note: most of this code was originally part of the cmd/go/internal/search @@ -71,7 +72,7 @@ func matchPatternInternal(pattern string, vendorExclude bool) func(name string) const vendorChar = "\x00" - if vendorExclude && strings.Contains(pattern, vendorChar) { + if vendorExclude && strings.Contains(pattern, vendorChar) || !utf8.ValidString(pattern) { return func(name string) bool { return false } } diff --git a/src/cmd/link/internal/loadpe/ldpe.go b/src/cmd/link/internal/loadpe/ldpe.go index b895ac4149..d3a050135c 100644 --- a/src/cmd/link/internal/loadpe/ldpe.go +++ b/src/cmd/link/internal/loadpe/ldpe.go @@ -17,6 +17,7 @@ import ( "errors" "fmt" "io" + "strconv" "strings" ) @@ -348,11 +349,11 @@ func Load(l *loader.Loader, arch *sys.Arch, localSymVersion int, input *bio.Read return nil, fmt.Errorf("relocation number %d symbol index idx=%d cannot be large then number of symbols %d", j, r.SymbolTableIndex, len(f.COFFSymbols)) } pesym := &f.COFFSymbols[r.SymbolTableIndex] - _, gosym, err := state.readpesym(pesym) + _, rSym, err := state.readpesym(pesym) if err != nil { return nil, err } - if gosym == 0 { + if rSym == 0 { name, err := pesym.FullName(f.StringTable) if err != nil { name = string(pesym.Name[:]) @@ -360,90 +361,53 @@ func Load(l *loader.Loader, arch *sys.Arch, localSymVersion int, input *bio.Read return nil, fmt.Errorf("reloc of invalid sym %s idx=%d type=%d", name, r.SymbolTableIndex, pesym.Type) } - rSym := gosym rSize := uint8(4) rOff := int32(r.VirtualAddress) - var rAdd int64 var rType objabi.RelocType switch arch.Family { default: return nil, fmt.Errorf("%s: unsupported arch %v", pn, arch.Family) - case sys.I386, sys.AMD64: + case sys.I386: switch r.Type { - default: - return nil, fmt.Errorf("%s: %v: unknown relocation type %v", pn, state.sectsyms[rsect], r.Type) - - case IMAGE_REL_I386_REL32, IMAGE_REL_AMD64_REL32, - IMAGE_REL_AMD64_ADDR32, // R_X86_64_PC32 - IMAGE_REL_AMD64_ADDR32NB: - if r.Type == IMAGE_REL_AMD64_ADDR32NB { - rType = objabi.R_PEIMAGEOFF - } else { - rType = objabi.R_PCREL - } - - rAdd = int64(int32(binary.LittleEndian.Uint32(state.sectdata[rsect][rOff:]))) - - case IMAGE_REL_I386_DIR32NB, IMAGE_REL_I386_DIR32: - if r.Type == IMAGE_REL_I386_DIR32NB { - rType = objabi.R_PEIMAGEOFF - } else { - rType = objabi.R_ADDR - } - - // load addend from image - rAdd = int64(int32(binary.LittleEndian.Uint32(state.sectdata[rsect][rOff:]))) - - case IMAGE_REL_AMD64_ADDR64: // R_X86_64_64 - rSize = 8 - + case IMAGE_REL_I386_REL32: + rType = objabi.R_PCREL + case IMAGE_REL_I386_DIR32: rType = objabi.R_ADDR - - // load addend from image - rAdd = int64(binary.LittleEndian.Uint64(state.sectdata[rsect][rOff:])) + case IMAGE_REL_I386_DIR32NB: + rType = objabi.R_PEIMAGEOFF } - - case sys.ARM: + case sys.AMD64: switch r.Type { - default: - return nil, fmt.Errorf("%s: %v: unknown ARM relocation type %v", pn, state.sectsyms[rsect], r.Type) - - case IMAGE_REL_ARM_SECREL: + case IMAGE_REL_AMD64_REL32: rType = objabi.R_PCREL - - rAdd = int64(int32(binary.LittleEndian.Uint32(state.sectdata[rsect][rOff:]))) - - case IMAGE_REL_ARM_ADDR32, IMAGE_REL_ARM_ADDR32NB: - if r.Type == IMAGE_REL_ARM_ADDR32NB { - rType = objabi.R_PEIMAGEOFF - } else { - rType = objabi.R_ADDR - } - - rAdd = int64(int32(binary.LittleEndian.Uint32(state.sectdata[rsect][rOff:]))) - - case IMAGE_REL_ARM_BRANCH24: - rType = objabi.R_CALLARM - - rAdd = int64(int32(binary.LittleEndian.Uint32(state.sectdata[rsect][rOff:]))) + case IMAGE_REL_AMD64_ADDR32: + rType = objabi.R_ADDR + case IMAGE_REL_AMD64_ADDR64: + rType = objabi.R_ADDR + rSize = 8 + case IMAGE_REL_AMD64_ADDR32NB: + rType = objabi.R_PEIMAGEOFF } - case sys.ARM64: switch r.Type { - default: - return nil, fmt.Errorf("%s: %v: unknown ARM64 relocation type %v", pn, state.sectsyms[rsect], r.Type) - - case IMAGE_REL_ARM64_ADDR32, IMAGE_REL_ARM64_ADDR32NB: - if r.Type == IMAGE_REL_ARM64_ADDR32NB { - rType = objabi.R_PEIMAGEOFF - } else { - rType = objabi.R_ADDR - } - - rAdd = int64(int32(binary.LittleEndian.Uint32(state.sectdata[rsect][rOff:]))) + case IMAGE_REL_ARM64_ADDR32: + rType = objabi.R_ADDR + case IMAGE_REL_ARM64_ADDR32NB: + rType = objabi.R_PEIMAGEOFF } } - + if rType == 0 { + return nil, fmt.Errorf("%s: %v: unknown relocation type %v", pn, state.sectsyms[rsect], r.Type) + } + var rAdd int64 + switch rSize { + default: + panic("unexpected relocation size " + strconv.Itoa(int(rSize))) + case 4: + rAdd = int64(int32(binary.LittleEndian.Uint32(state.sectdata[rsect][rOff:]))) + case 8: + rAdd = int64(binary.LittleEndian.Uint64(state.sectdata[rsect][rOff:])) + } // ld -r could generate multiple section symbols for the // same section but with different values, we have to take // that into account, or in the case of split resources, diff --git a/src/context/context.go b/src/context/context.go index 4fb537e233..232fc55d87 100644 --- a/src/context/context.go +++ b/src/context/context.go @@ -463,6 +463,8 @@ func (c *cancelCtx) Done() <-chan struct{} { func (c *cancelCtx) Err() error { // An atomic load is ~5x faster than a mutex, which can matter in tight loops. if err := c.err.Load(); err != nil { + // Ensure the done channel has been closed before returning a non-nil error. + <-c.Done() return err.(error) } return nil diff --git a/src/context/x_test.go b/src/context/x_test.go index 937cab1445..0cf19688c3 100644 --- a/src/context/x_test.go +++ b/src/context/x_test.go @@ -1177,3 +1177,23 @@ func (c *customContext) Err() error { func (c *customContext) Value(key any) any { return c.parent.Value(key) } + +// Issue #75533. +func TestContextErrDoneRace(t *testing.T) { + // 4 iterations reliably reproduced #75533. + for range 10 { + ctx, cancel := WithCancel(Background()) + donec := ctx.Done() + go cancel() + for ctx.Err() == nil { + if runtime.GOARCH == "wasm" { + runtime.Gosched() // need to explicitly yield + } + } + select { + case <-donec: + default: + t.Fatalf("ctx.Err is non-nil, but ctx.Done is not closed") + } + } +} diff --git a/src/crypto/internal/fips140/mlkem/field.go b/src/crypto/internal/fips140/mlkem/field.go index 1a42818247..577062526c 100644 --- a/src/crypto/internal/fips140/mlkem/field.go +++ b/src/crypto/internal/fips140/mlkem/field.go @@ -203,9 +203,7 @@ func sliceForAppend(in []byte, n int) (head, tail []byte) { // followed by ByteEncode₁, according to FIPS 203, Algorithm 5. func ringCompressAndEncode1(s []byte, f ringElement) []byte { s, b := sliceForAppend(s, encodingSize1) - for i := range b { - b[i] = 0 - } + clear(b) for i := range f { b[i/8] |= uint8(compress(f[i], 1) << (i % 8)) } diff --git a/src/crypto/internal/fips140/sha3/sha3.go b/src/crypto/internal/fips140/sha3/sha3.go index 7513f8ef5d..5c77fae4e7 100644 --- a/src/crypto/internal/fips140/sha3/sha3.go +++ b/src/crypto/internal/fips140/sha3/sha3.go @@ -61,9 +61,7 @@ func (d *Digest) Size() int { return d.outputLen } // Reset resets the Digest to its initial state. func (d *Digest) Reset() { // Zero the permutation's state. - for i := range d.a { - d.a[i] = 0 - } + clear(d.a[:]) d.state = spongeAbsorbing d.n = 0 } diff --git a/src/crypto/rc4/rc4.go b/src/crypto/rc4/rc4.go index 90943a0935..eebc1c04cb 100644 --- a/src/crypto/rc4/rc4.go +++ b/src/crypto/rc4/rc4.go @@ -55,9 +55,7 @@ func NewCipher(key []byte) (*Cipher, error) { // Deprecated: Reset can't guarantee that the key will be entirely removed from // the process's memory. func (c *Cipher) Reset() { - for i := range c.s { - c.s[i] = 0 - } + clear(c.s[:]) c.i, c.j = 0, 0 } diff --git a/src/crypto/tls/conn.go b/src/crypto/tls/conn.go index d4d68c0744..09dc9ea94c 100644 --- a/src/crypto/tls/conn.go +++ b/src/crypto/tls/conn.go @@ -220,9 +220,7 @@ func (hc *halfConn) changeCipherSpec() error { hc.mac = hc.nextMac hc.nextCipher = nil hc.nextMac = nil - for i := range hc.seq { - hc.seq[i] = 0 - } + clear(hc.seq[:]) return nil } @@ -231,9 +229,7 @@ func (hc *halfConn) setTrafficSecret(suite *cipherSuiteTLS13, level QUICEncrypti hc.level = level key, iv := suite.trafficKey(secret) hc.cipher = suite.aead(key, iv) - for i := range hc.seq { - hc.seq[i] = 0 - } + clear(hc.seq[:]) } // incSeq increments the sequence number. diff --git a/src/crypto/tls/handshake_server_test.go b/src/crypto/tls/handshake_server_test.go index 4df3f5a737..941f2a3373 100644 --- a/src/crypto/tls/handshake_server_test.go +++ b/src/crypto/tls/handshake_server_test.go @@ -1590,9 +1590,7 @@ var getConfigForClientTests = []struct { }, func(clientHello *ClientHelloInfo) (*Config, error) { config := testConfig.Clone() - for i := range config.SessionTicketKey { - config.SessionTicketKey[i] = 0 - } + clear(config.SessionTicketKey[:]) config.sessionTicketKeys = nil return config, nil }, diff --git a/src/crypto/x509/internal/macos/corefoundation.go b/src/crypto/x509/internal/macos/corefoundation.go index b4032a5d91..23fbe35c24 100644 --- a/src/crypto/x509/internal/macos/corefoundation.go +++ b/src/crypto/x509/internal/macos/corefoundation.go @@ -4,10 +4,10 @@ //go:build darwin -// Package macOS provides cgo-less wrappers for Core Foundation and +// Package macos provides cgo-less wrappers for Core Foundation and // Security.framework, similarly to how package syscall provides access to // libSystem.dylib. -package macOS +package macos import ( "bytes" diff --git a/src/crypto/x509/internal/macos/security.go b/src/crypto/x509/internal/macos/security.go index f9f37b1666..6af19bafe5 100644 --- a/src/crypto/x509/internal/macos/security.go +++ b/src/crypto/x509/internal/macos/security.go @@ -4,7 +4,7 @@ //go:build darwin -package macOS +package macos import ( "errors" diff --git a/src/crypto/x509/root_darwin.go b/src/crypto/x509/root_darwin.go index b5d7b6350b..3e9aa1ba09 100644 --- a/src/crypto/x509/root_darwin.go +++ b/src/crypto/x509/root_darwin.go @@ -5,51 +5,51 @@ package x509 import ( - macOS "crypto/x509/internal/macos" + "crypto/x509/internal/macos" "errors" "fmt" ) func (c *Certificate) systemVerify(opts *VerifyOptions) (chains [][]*Certificate, err error) { - certs := macOS.CFArrayCreateMutable() - defer macOS.ReleaseCFArray(certs) - leaf, err := macOS.SecCertificateCreateWithData(c.Raw) + certs := macos.CFArrayCreateMutable() + defer macos.ReleaseCFArray(certs) + leaf, err := macos.SecCertificateCreateWithData(c.Raw) if err != nil { return nil, errors.New("invalid leaf certificate") } - macOS.CFArrayAppendValue(certs, leaf) + macos.CFArrayAppendValue(certs, leaf) if opts.Intermediates != nil { for _, lc := range opts.Intermediates.lazyCerts { c, err := lc.getCert() if err != nil { return nil, err } - sc, err := macOS.SecCertificateCreateWithData(c.Raw) + sc, err := macos.SecCertificateCreateWithData(c.Raw) if err != nil { return nil, err } - macOS.CFArrayAppendValue(certs, sc) + macos.CFArrayAppendValue(certs, sc) } } - policies := macOS.CFArrayCreateMutable() - defer macOS.ReleaseCFArray(policies) - sslPolicy, err := macOS.SecPolicyCreateSSL(opts.DNSName) + policies := macos.CFArrayCreateMutable() + defer macos.ReleaseCFArray(policies) + sslPolicy, err := macos.SecPolicyCreateSSL(opts.DNSName) if err != nil { return nil, err } - macOS.CFArrayAppendValue(policies, sslPolicy) + macos.CFArrayAppendValue(policies, sslPolicy) - trustObj, err := macOS.SecTrustCreateWithCertificates(certs, policies) + trustObj, err := macos.SecTrustCreateWithCertificates(certs, policies) if err != nil { return nil, err } - defer macOS.CFRelease(trustObj) + defer macos.CFRelease(trustObj) if !opts.CurrentTime.IsZero() { - dateRef := macOS.TimeToCFDateRef(opts.CurrentTime) - defer macOS.CFRelease(dateRef) - if err := macOS.SecTrustSetVerifyDate(trustObj, dateRef); err != nil { + dateRef := macos.TimeToCFDateRef(opts.CurrentTime) + defer macos.CFRelease(dateRef) + if err := macos.SecTrustSetVerifyDate(trustObj, dateRef); err != nil { return nil, err } } @@ -59,13 +59,13 @@ func (c *Certificate) systemVerify(opts *VerifyOptions) (chains [][]*Certificate // always enforce its SCT requirements, and there are still _some_ people // using TLS or OCSP for that. - if ret, err := macOS.SecTrustEvaluateWithError(trustObj); err != nil { + if ret, err := macos.SecTrustEvaluateWithError(trustObj); err != nil { switch ret { - case macOS.ErrSecCertificateExpired: + case macos.ErrSecCertificateExpired: return nil, CertificateInvalidError{c, Expired, err.Error()} - case macOS.ErrSecHostNameMismatch: + case macos.ErrSecHostNameMismatch: return nil, HostnameError{c, opts.DNSName} - case macOS.ErrSecNotTrusted: + case macos.ErrSecNotTrusted: return nil, UnknownAuthorityError{Cert: c} default: return nil, fmt.Errorf("x509: %s", err) @@ -73,13 +73,13 @@ func (c *Certificate) systemVerify(opts *VerifyOptions) (chains [][]*Certificate } chain := [][]*Certificate{{}} - chainRef, err := macOS.SecTrustCopyCertificateChain(trustObj) + chainRef, err := macos.SecTrustCopyCertificateChain(trustObj) if err != nil { return nil, err } - defer macOS.CFRelease(chainRef) - for i := 0; i < macOS.CFArrayGetCount(chainRef); i++ { - certRef := macOS.CFArrayGetValueAtIndex(chainRef, i) + defer macos.CFRelease(chainRef) + for i := 0; i < macos.CFArrayGetCount(chainRef); i++ { + certRef := macos.CFArrayGetValueAtIndex(chainRef, i) cert, err := exportCertificate(certRef) if err != nil { return nil, err @@ -88,7 +88,7 @@ func (c *Certificate) systemVerify(opts *VerifyOptions) (chains [][]*Certificate } if len(chain[0]) == 0 { // This should _never_ happen, but to be safe - return nil, errors.New("x509: macOS certificate verification internal error") + return nil, errors.New("x509: macos certificate verification internal error") } if opts.DNSName != "" { @@ -118,8 +118,8 @@ func (c *Certificate) systemVerify(opts *VerifyOptions) (chains [][]*Certificate } // exportCertificate returns a *Certificate for a SecCertificateRef. -func exportCertificate(cert macOS.CFRef) (*Certificate, error) { - data, err := macOS.SecCertificateCopyData(cert) +func exportCertificate(cert macos.CFRef) (*Certificate, error) { + data, err := macos.SecCertificateCopyData(cert) if err != nil { return nil, err } diff --git a/src/encoding/json/v2/arshal_time.go b/src/encoding/json/v2/arshal_time.go index 70dca8a294..027503734e 100644 --- a/src/encoding/json/v2/arshal_time.go +++ b/src/encoding/json/v2/arshal_time.go @@ -465,7 +465,7 @@ func appendDurationISO8601(b []byte, d time.Duration) []byte { } // daysPerYear is the exact average number of days in a year according to -// the Gregorian calender, which has an extra day each year that is +// the Gregorian calendar, which has an extra day each year that is // a multiple of 4, unless it is evenly divisible by 100 but not by 400. // This does not take into account leap seconds, which are not deterministic. const daysPerYear = 365.2425 diff --git a/src/go/ast/ast.go b/src/go/ast/ast.go index 2a0d9e6860..9aca39e868 100644 --- a/src/go/ast/ast.go +++ b/src/go/ast/ast.go @@ -1064,7 +1064,7 @@ type File struct { Scope *Scope // package scope (this file only). Deprecated: see Object Imports []*ImportSpec // imports in this file Unresolved []*Ident // unresolved identifiers in this file. Deprecated: see Object - Comments []*CommentGroup // list of all comments in the source file + Comments []*CommentGroup // comments in the file, in lexical order GoVersion string // minimum Go version required by //go:build or // +build directives } diff --git a/src/go/parser/parser.go b/src/go/parser/parser.go index 9ee1576a99..e725371e76 100644 --- a/src/go/parser/parser.go +++ b/src/go/parser/parser.go @@ -345,30 +345,29 @@ func (p *parser) expectClosing(tok token.Token, context string) token.Pos { // expectSemi consumes a semicolon and returns the applicable line comment. func (p *parser) expectSemi() (comment *ast.CommentGroup) { - // semicolon is optional before a closing ')' or '}' - if p.tok != token.RPAREN && p.tok != token.RBRACE { - switch p.tok { - case token.COMMA: - // permit a ',' instead of a ';' but complain - p.errorExpected(p.pos, "';'") - fallthrough - case token.SEMICOLON: - if p.lit == ";" { - // explicit semicolon - p.next() - comment = p.lineComment // use following comments - } else { - // artificial semicolon - comment = p.lineComment // use preceding comments - p.next() - } - return comment - default: - p.errorExpected(p.pos, "';'") - p.advance(stmtStart) + switch p.tok { + case token.RPAREN, token.RBRACE: + return nil // semicolon is optional before a closing ')' or '}' + case token.COMMA: + // permit a ',' instead of a ';' but complain + p.errorExpected(p.pos, "';'") + fallthrough + case token.SEMICOLON: + if p.lit == ";" { + // explicit semicolon + p.next() + comment = p.lineComment // use following comments + } else { + // artificial semicolon + comment = p.lineComment // use preceding comments + p.next() } + return comment + default: + p.errorExpected(p.pos, "';'") + p.advance(stmtStart) + return nil } - return nil } func (p *parser) atComma(context string, follow token.Token) bool { diff --git a/src/go/parser/parser_test.go b/src/go/parser/parser_test.go index 2516cedc88..87b7d7bbab 100644 --- a/src/go/parser/parser_test.go +++ b/src/go/parser/parser_test.go @@ -896,3 +896,53 @@ func test() { t.Fatalf("unexpected f.Comments got:\n%v\nwant:\n%v", got.String(), want.String()) } } + +// TestBothLineAndLeadComment makes sure that we populate the +// p.lineComment field even though there is a comment after the +// line comment. +func TestBothLineAndLeadComment(t *testing.T) { + const src = `package test + +var _ int; /* line comment */ +// Doc comment +func _() {} + +var _ int; /* line comment */ +// Some comment + +func _() {} +` + + fset := token.NewFileSet() + f, _ := ParseFile(fset, "", src, ParseComments|SkipObjectResolution) + + lineComment := f.Decls[0].(*ast.GenDecl).Specs[0].(*ast.ValueSpec).Comment + docComment := f.Decls[1].(*ast.FuncDecl).Doc + + if lineComment == nil { + t.Fatal("missing line comment") + } + if docComment == nil { + t.Fatal("missing doc comment") + } + + if lineComment.List[0].Text != "/* line comment */" { + t.Errorf(`unexpected line comment got = %q; want "/* line comment */"`, lineComment.List[0].Text) + } + if docComment.List[0].Text != "// Doc comment" { + t.Errorf(`unexpected line comment got = %q; want "// Doc comment"`, docComment.List[0].Text) + } + + lineComment2 := f.Decls[2].(*ast.GenDecl).Specs[0].(*ast.ValueSpec).Comment + if lineComment2 == nil { + t.Fatal("missing line comment") + } + if lineComment.List[0].Text != "/* line comment */" { + t.Errorf(`unexpected line comment got = %q; want "/* line comment */"`, lineComment.List[0].Text) + } + + docComment2 := f.Decls[3].(*ast.FuncDecl).Doc + if docComment2 != nil { + t.Errorf("unexpected doc comment %v", docComment2) + } +} diff --git a/src/internal/buildcfg/exp.go b/src/internal/buildcfg/exp.go index 734059e6d8..b05150373b 100644 --- a/src/internal/buildcfg/exp.go +++ b/src/internal/buildcfg/exp.go @@ -79,10 +79,11 @@ func ParseGOEXPERIMENT(goos, goarch, goexp string) (*ExperimentFlags, error) { dwarf5Supported := (goos != "darwin" && goos != "ios" && goos != "aix") baseline := goexperiment.Flags{ - RegabiWrappers: regabiSupported, - RegabiArgs: regabiSupported, - SIMD: goarch == "amd64", // TODO remove this (default to false) when dev.simd is merged - Dwarf5: dwarf5Supported, + RegabiWrappers: regabiSupported, + RegabiArgs: regabiSupported, + SIMD: goarch == "amd64", // TODO remove this (default to false) when dev.simd is merged + Dwarf5: dwarf5Supported, + RandomizedHeapBase64: true, } // Start with the statically enabled set of experiments. diff --git a/src/iter/iter.go b/src/iter/iter.go index 3eaeb9e1fd..3d0caebf1e 100644 --- a/src/iter/iter.go +++ b/src/iter/iter.go @@ -28,6 +28,8 @@ or index-value pairs. Yield returns true if the iterator should continue with the next element in the sequence, false if it should stop. +Yield panics if called after it returns false. + For instance, [maps.Keys] returns an iterator that produces the sequence of keys of the map m, implemented as follows: diff --git a/src/log/slog/internal/benchmarks/benchmarks.go b/src/log/slog/internal/benchmarks/benchmarks.go index 3a28523beb..a94ecfa743 100644 --- a/src/log/slog/internal/benchmarks/benchmarks.go +++ b/src/log/slog/internal/benchmarks/benchmarks.go @@ -31,12 +31,19 @@ import ( const testMessage = "Test logging, but use a somewhat realistic message length." +type event struct { + ID string + Index int + Flag bool +} + var ( testTime = time.Date(2022, time.May, 1, 0, 0, 0, 0, time.UTC) testString = "7e3b3b2aaeff56a7108fe11e154200dd/7819479873059528190" testInt = 32768 testDuration = 23 * time.Second testError = errors.New("fail") + testEvent = event{"abcdefgh", 65536, true} ) var testAttrs = []slog.Attr{ diff --git a/src/log/slog/internal/benchmarks/benchmarks_test.go b/src/log/slog/internal/benchmarks/benchmarks_test.go index 18643b73e6..3fec261279 100644 --- a/src/log/slog/internal/benchmarks/benchmarks_test.go +++ b/src/log/slog/internal/benchmarks/benchmarks_test.go @@ -80,12 +80,12 @@ func BenchmarkAttrs(b *testing.B) { slog.Int("status", testInt), slog.Duration("duration", testDuration), slog.Time("time", testTime), - slog.Any("error", testError), + slog.Any("event", testEvent), slog.String("string", testString), slog.Int("status", testInt), slog.Duration("duration", testDuration), slog.Time("time", testTime), - slog.Any("error", testError), + slog.Any("event", testEvent), ) }, }, @@ -103,37 +103,37 @@ func BenchmarkAttrs(b *testing.B) { slog.Int("status", testInt), slog.Duration("duration", testDuration), slog.Time("time", testTime), - slog.Any("error", testError), + slog.Any("event", testEvent), slog.String("string", testString), slog.Int("status", testInt), slog.Duration("duration", testDuration), slog.Time("time", testTime), - slog.Any("error", testError), + slog.Any("event", testEvent), slog.String("string", testString), slog.Int("status", testInt), slog.Duration("duration", testDuration), slog.Time("time", testTime), - slog.Any("error", testError), + slog.Any("event", testEvent), slog.String("string", testString), slog.Int("status", testInt), slog.Duration("duration", testDuration), slog.Time("time", testTime), - slog.Any("error", testError), + slog.Any("event", testEvent), slog.String("string", testString), slog.Int("status", testInt), slog.Duration("duration", testDuration), slog.Time("time", testTime), - slog.Any("error", testError), + slog.Any("event", testEvent), slog.String("string", testString), slog.Int("status", testInt), slog.Duration("duration", testDuration), slog.Time("time", testTime), - slog.Any("error", testError), + slog.Any("event", testEvent), slog.String("string", testString), slog.Int("status", testInt), slog.Duration("duration", testDuration), slog.Time("time", testTime), - slog.Any("error", testError), + slog.Any("event", testEvent), ) }, }, diff --git a/src/log/slog/json_handler.go b/src/log/slog/json_handler.go index f139c54139..9c7045cda9 100644 --- a/src/log/slog/json_handler.go +++ b/src/log/slog/json_handler.go @@ -137,15 +137,40 @@ func appendJSONValue(s *handleState, v Value) error { return nil } -func appendJSONMarshal(buf *buffer.Buffer, v any) error { +type jsonEncoder struct { + buf *bytes.Buffer // Use a json.Encoder to avoid escaping HTML. - var bb bytes.Buffer - enc := json.NewEncoder(&bb) - enc.SetEscapeHTML(false) - if err := enc.Encode(v); err != nil { + json *json.Encoder +} + +var jsonEncoderPool = &sync.Pool{ + New: func() any { + enc := &jsonEncoder{ + buf: new(bytes.Buffer), + } + enc.json = json.NewEncoder(enc.buf) + enc.json.SetEscapeHTML(false) + return enc + }, +} + +func appendJSONMarshal(buf *buffer.Buffer, v any) error { + j := jsonEncoderPool.Get().(*jsonEncoder) + defer func() { + // To reduce peak allocation, return only smaller buffers to the pool. + const maxBufferSize = 16 << 10 + if j.buf.Cap() > maxBufferSize { + return + } + j.buf.Reset() + jsonEncoderPool.Put(j) + }() + + if err := j.json.Encode(v); err != nil { return err } - bs := bb.Bytes() + + bs := j.buf.Bytes() buf.Write(bs[:len(bs)-1]) // remove final newline return nil } diff --git a/src/log/slog/json_handler_test.go b/src/log/slog/json_handler_test.go index 65130f2426..fd6b2e39d4 100644 --- a/src/log/slog/json_handler_test.go +++ b/src/log/slog/json_handler_test.go @@ -142,6 +142,39 @@ func jsonValueString(v Value) string { return string(buf) } +func TestJSONAllocs(t *testing.T) { + ctx := t.Context() + l := New(NewJSONHandler(io.Discard, &HandlerOptions{})) + testErr := errors.New("an error occurred") + testEvent := struct { + ID int + Scope string + Enabled bool + }{ + 123456, "abcdefgh", true, + } + + t.Run("message", func(t *testing.T) { + wantAllocs(t, 0, func() { + l.LogAttrs(ctx, LevelInfo, + "hello world", + ) + }) + }) + t.Run("attrs", func(t *testing.T) { + wantAllocs(t, 1, func() { + l.LogAttrs(ctx, LevelInfo, + "hello world", + String("component", "subtest"), + Int("id", 67890), + Bool("flag", true), + Any("error", testErr), + Any("event", testEvent), + ) + }) + }) +} + func BenchmarkJSONHandler(b *testing.B) { for _, bench := range []struct { name string diff --git a/src/log/slog/level.go b/src/log/slog/level.go index 2957585e0e..9e812eb165 100644 --- a/src/log/slog/level.go +++ b/src/log/slog/level.go @@ -61,7 +61,11 @@ func (l Level) String() string { if val == 0 { return base } - return fmt.Sprintf("%s%+d", base, val) + sval := strconv.Itoa(int(val)) + if val > 0 { + sval = "+" + sval + } + return base + sval } switch { diff --git a/src/log/slog/level_test.go b/src/log/slog/level_test.go index 73be1126b2..c9a1c16c03 100644 --- a/src/log/slog/level_test.go +++ b/src/log/slog/level_test.go @@ -215,3 +215,25 @@ func TestLevelVarString(t *testing.T) { t.Errorf("got %q, want %q", got, want) } } + +func BenchmarkLevelString(b *testing.B) { + levels := []Level{ + 0, + LevelError, + LevelError + 2, + LevelError - 2, + LevelWarn, + LevelWarn - 1, + LevelInfo, + LevelInfo + 1, + LevelInfo - 3, + LevelDebug, + LevelDebug - 2, + } + b.ResetTimer() + for b.Loop() { + for _, level := range levels { + _ = level.String() + } + } +} diff --git a/src/net/fd_unix.go b/src/net/fd_unix.go index 40ecbef2e8..0d4303e2cc 100644 --- a/src/net/fd_unix.go +++ b/src/net/fd_unix.go @@ -82,6 +82,9 @@ func (fd *netFD) connect(ctx context.Context, la, ra syscall.Sockaddr) (rsa sysc defer fd.pfd.SetWriteDeadline(noDeadline) } + // Load the hook function synchronously to prevent a race + // with test code that restores the old value. + testHookCanceledDial := testHookCanceledDial stop := context.AfterFunc(ctx, func() { // Force the runtime's poller to immediately give up // waiting for writability, unblocking waitWrite diff --git a/src/net/http/transport.go b/src/net/http/transport.go index 139ad84af0..5cef9be487 100644 --- a/src/net/http/transport.go +++ b/src/net/http/transport.go @@ -1382,7 +1382,10 @@ func (w *wantConn) cancel(t *Transport) { w.done = true w.mu.Unlock() - if pc != nil { + // HTTP/2 connections (pc.alt != nil) aren't removed from the idle pool on use, + // and should not be added back here. If the pconn isn't in the idle pool, + // it's because we removed it due to an error. + if pc != nil && pc.alt == nil { t.putOrCloseIdleConn(pc) } } diff --git a/src/net/http/transport_test.go b/src/net/http/transport_test.go index 810f21f3a5..75dbd25d22 100644 --- a/src/net/http/transport_test.go +++ b/src/net/http/transport_test.go @@ -7625,3 +7625,35 @@ func TestTransportServerProtocols(t *testing.T) { }) } } + +func TestIssue61474(t *testing.T) { + run(t, testIssue61474, []testMode{http2Mode}) +} +func testIssue61474(t *testing.T, mode testMode) { + if testing.Short() { + return + } + + // This test reliably exercises the condition causing #61474, + // but requires many iterations to do so. + // Keep the test around for now, but don't run it by default. + t.Skip("test is too large") + + cst := newClientServerTest(t, mode, HandlerFunc(func(rw ResponseWriter, req *Request) { + }), func(tr *Transport) { + tr.MaxConnsPerHost = 1 + }) + var wg sync.WaitGroup + defer wg.Wait() + for range 100000 { + wg.Go(func() { + ctx, cancel := context.WithTimeout(t.Context(), 1*time.Millisecond) + defer cancel() + req, _ := NewRequestWithContext(ctx, "GET", cst.ts.URL, nil) + resp, err := cst.c.Do(req) + if err == nil { + resp.Body.Close() + } + }) + } +} diff --git a/src/os/user/cgo_unix_test.go b/src/os/user/cgo_lookup_unix_test.go index 6d16aa20b3..cd225eb8df 100644 --- a/src/os/user/cgo_unix_test.go +++ b/src/os/user/cgo_lookup_unix_test.go @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -//go:build (darwin || dragonfly || freebsd || (!android && linux) || netbsd || openbsd || solaris) && cgo && !osusergo +//go:build (cgo || darwin) && !osusergo && unix && !android package user diff --git a/src/path/filepath/symlink_plan9.go b/src/path/filepath/symlink_plan9.go index 820d150d97..b29abe1dfd 100644 --- a/src/path/filepath/symlink_plan9.go +++ b/src/path/filepath/symlink_plan9.go @@ -16,8 +16,8 @@ func evalSymlinks(path string) (string, error) { // Check validity of path _, err := os.Lstat(path) if err != nil { - // Return the same error value as on other operating systems - if strings.HasSuffix(err.Error(), "not a directory") { + // Return the same error value as on other operating systems. + if strings.Contains(err.Error(), "not a directory") { err = syscall.ENOTDIR } return "", err diff --git a/src/runtime/_mkmalloc/astutil/clone.go b/src/runtime/_mkmalloc/astutil/clone.go new file mode 100644 index 0000000000..16ea7163ca --- /dev/null +++ b/src/runtime/_mkmalloc/astutil/clone.go @@ -0,0 +1,73 @@ +// Copyright 2023 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. + +// This file is a copy of golang.org/x/tools/internal/astutil/clone.go + +package astutil + +import ( + "go/ast" + "reflect" +) + +// CloneNode returns a deep copy of a Node. +// It omits pointers to ast.{Scope,Object} variables. +func CloneNode[T ast.Node](n T) T { + return cloneNode(n).(T) +} + +func cloneNode(n ast.Node) ast.Node { + var clone func(x reflect.Value) reflect.Value + set := func(dst, src reflect.Value) { + src = clone(src) + if src.IsValid() { + dst.Set(src) + } + } + clone = func(x reflect.Value) reflect.Value { + switch x.Kind() { + case reflect.Pointer: + if x.IsNil() { + return x + } + // Skip fields of types potentially involved in cycles. + switch x.Interface().(type) { + case *ast.Object, *ast.Scope: + return reflect.Zero(x.Type()) + } + y := reflect.New(x.Type().Elem()) + set(y.Elem(), x.Elem()) + return y + + case reflect.Struct: + y := reflect.New(x.Type()).Elem() + for i := 0; i < x.Type().NumField(); i++ { + set(y.Field(i), x.Field(i)) + } + return y + + case reflect.Slice: + if x.IsNil() { + return x + } + y := reflect.MakeSlice(x.Type(), x.Len(), x.Cap()) + for i := 0; i < x.Len(); i++ { + set(y.Index(i), x.Index(i)) + } + return y + + case reflect.Interface: + y := reflect.New(x.Type()).Elem() + set(y, x.Elem()) + return y + + case reflect.Array, reflect.Chan, reflect.Func, reflect.Map, reflect.UnsafePointer: + panic(x) // unreachable in AST + + default: + return x // bool, string, number + } + } + return clone(reflect.ValueOf(n)).Interface().(ast.Node) +} diff --git a/src/runtime/defs2_linux.go b/src/runtime/defs2_linux.go index 5d6730a7ad..597073c39d 100644 --- a/src/runtime/defs2_linux.go +++ b/src/runtime/defs2_linux.go @@ -48,6 +48,7 @@ const ( EINTR = C.EINTR EAGAIN = C.EAGAIN ENOMEM = C.ENOMEM + ENOSYS = C.ENOSYS PROT_NONE = C.PROT_NONE PROT_READ = C.PROT_READ diff --git a/src/runtime/defs_linux.go b/src/runtime/defs_linux.go index 296fcb4bfd..d2b619ecab 100644 --- a/src/runtime/defs_linux.go +++ b/src/runtime/defs_linux.go @@ -37,6 +37,7 @@ const ( EINTR = C.EINTR EAGAIN = C.EAGAIN ENOMEM = C.ENOMEM + ENOSYS = C.ENOSYS PROT_NONE = C.PROT_NONE PROT_READ = C.PROT_READ diff --git a/src/runtime/defs_linux_386.go b/src/runtime/defs_linux_386.go index 5fef55610f..e902d8175c 100644 --- a/src/runtime/defs_linux_386.go +++ b/src/runtime/defs_linux_386.go @@ -9,6 +9,7 @@ const ( _EINTR = 0x4 _EAGAIN = 0xb _ENOMEM = 0xc + _ENOSYS = 0x26 _PROT_NONE = 0x0 _PROT_READ = 0x1 @@ -136,16 +137,30 @@ type fpstate struct { anon0 [48]byte } -type timespec struct { +// The timespec structs and types are defined in Linux in +// include/uapi/linux/time_types.h and include/uapi/asm-generic/posix_types.h. +type timespec32 struct { tv_sec int32 tv_nsec int32 } //go:nosplit -func (ts *timespec) setNsec(ns int64) { +func (ts *timespec32) setNsec(ns int64) { ts.tv_sec = timediv(ns, 1e9, &ts.tv_nsec) } +type timespec struct { + tv_sec int64 + tv_nsec int64 +} + +//go:nosplit +func (ts *timespec) setNsec(ns int64) { + var newNS int32 + ts.tv_sec = int64(timediv(ns, 1e9, &newNS)) + ts.tv_nsec = int64(newNS) +} + type timeval struct { tv_sec int32 tv_usec int32 @@ -223,8 +238,8 @@ type ucontext struct { } type itimerspec struct { - it_interval timespec - it_value timespec + it_interval timespec32 + it_value timespec32 } type itimerval struct { diff --git a/src/runtime/defs_linux_amd64.go b/src/runtime/defs_linux_amd64.go index dce7799b6a..9a908c9400 100644 --- a/src/runtime/defs_linux_amd64.go +++ b/src/runtime/defs_linux_amd64.go @@ -9,6 +9,7 @@ const ( _EINTR = 0x4 _EAGAIN = 0xb _ENOMEM = 0xc + _ENOSYS = 0x26 _PROT_NONE = 0x0 _PROT_READ = 0x1 diff --git a/src/runtime/defs_linux_arm.go b/src/runtime/defs_linux_arm.go index 71cf8c6d50..35c4faf964 100644 --- a/src/runtime/defs_linux_arm.go +++ b/src/runtime/defs_linux_arm.go @@ -11,6 +11,7 @@ const ( _EINTR = 0x4 _ENOMEM = 0xc _EAGAIN = 0xb + _ENOSYS = 0x26 _PROT_NONE = 0 _PROT_READ = 0x1 @@ -95,16 +96,30 @@ const ( _SOCK_DGRAM = 0x2 ) -type timespec struct { +// The timespec structs and types are defined in Linux in +// include/uapi/linux/time_types.h and include/uapi/asm-generic/posix_types.h. +type timespec32 struct { tv_sec int32 tv_nsec int32 } //go:nosplit -func (ts *timespec) setNsec(ns int64) { +func (ts *timespec32) setNsec(ns int64) { ts.tv_sec = timediv(ns, 1e9, &ts.tv_nsec) } +type timespec struct { + tv_sec int64 + tv_nsec int64 +} + +//go:nosplit +func (ts *timespec) setNsec(ns int64) { + var newNS int32 + ts.tv_sec = int64(timediv(ns, 1e9, &newNS)) + ts.tv_nsec = int64(newNS) +} + type stackt struct { ss_sp *byte ss_flags int32 @@ -155,8 +170,8 @@ func (tv *timeval) set_usec(x int32) { } type itimerspec struct { - it_interval timespec - it_value timespec + it_interval timespec32 + it_value timespec32 } type itimerval struct { diff --git a/src/runtime/defs_linux_arm64.go b/src/runtime/defs_linux_arm64.go index 606cd70494..4992e91ea6 100644 --- a/src/runtime/defs_linux_arm64.go +++ b/src/runtime/defs_linux_arm64.go @@ -9,6 +9,7 @@ const ( _EINTR = 0x4 _EAGAIN = 0xb _ENOMEM = 0xc + _ENOSYS = 0x26 _PROT_NONE = 0x0 _PROT_READ = 0x1 diff --git a/src/runtime/defs_linux_loong64.go b/src/runtime/defs_linux_loong64.go index b983725160..670d4c318d 100644 --- a/src/runtime/defs_linux_loong64.go +++ b/src/runtime/defs_linux_loong64.go @@ -10,6 +10,7 @@ const ( _EINTR = 0x4 _EAGAIN = 0xb _ENOMEM = 0xc + _ENOSYS = 0x26 _PROT_NONE = 0x0 _PROT_READ = 0x1 diff --git a/src/runtime/defs_linux_mips64x.go b/src/runtime/defs_linux_mips64x.go index 8a0af41234..7449d2cfac 100644 --- a/src/runtime/defs_linux_mips64x.go +++ b/src/runtime/defs_linux_mips64x.go @@ -12,6 +12,7 @@ const ( _EINTR = 0x4 _EAGAIN = 0xb _ENOMEM = 0xc + _ENOSYS = 0x26 _PROT_NONE = 0x0 _PROT_READ = 0x1 diff --git a/src/runtime/defs_linux_mipsx.go b/src/runtime/defs_linux_mipsx.go index 8322beab2b..cec504c885 100644 --- a/src/runtime/defs_linux_mipsx.go +++ b/src/runtime/defs_linux_mipsx.go @@ -12,6 +12,7 @@ const ( _EINTR = 0x4 _EAGAIN = 0xb _ENOMEM = 0xc + _ENOSYS = 0x26 _PROT_NONE = 0x0 _PROT_READ = 0x1 @@ -93,16 +94,30 @@ const ( _SIGEV_THREAD_ID = 0x4 ) -type timespec struct { +// The timespec structs and types are defined in Linux in +// include/uapi/linux/time_types.h and include/uapi/asm-generic/posix_types.h. +type timespec32 struct { tv_sec int32 tv_nsec int32 } //go:nosplit -func (ts *timespec) setNsec(ns int64) { +func (ts *timespec32) setNsec(ns int64) { ts.tv_sec = timediv(ns, 1e9, &ts.tv_nsec) } +type timespec struct { + tv_sec int64 + tv_nsec int64 +} + +//go:nosplit +func (ts *timespec) setNsec(ns int64) { + var newNS int32 + ts.tv_sec = int64(timediv(ns, 1e9, &newNS)) + ts.tv_nsec = int64(newNS) +} + type timeval struct { tv_sec int32 tv_usec int32 @@ -138,8 +153,8 @@ type siginfo struct { } type itimerspec struct { - it_interval timespec - it_value timespec + it_interval timespec32 + it_value timespec32 } type itimerval struct { diff --git a/src/runtime/defs_linux_ppc64.go b/src/runtime/defs_linux_ppc64.go index f87924affe..dc45f37b7c 100644 --- a/src/runtime/defs_linux_ppc64.go +++ b/src/runtime/defs_linux_ppc64.go @@ -9,6 +9,7 @@ const ( _EINTR = 0x4 _EAGAIN = 0xb _ENOMEM = 0xc + _ENOSYS = 0x26 _PROT_NONE = 0x0 _PROT_READ = 0x1 diff --git a/src/runtime/defs_linux_ppc64le.go b/src/runtime/defs_linux_ppc64le.go index f87924affe..dc45f37b7c 100644 --- a/src/runtime/defs_linux_ppc64le.go +++ b/src/runtime/defs_linux_ppc64le.go @@ -9,6 +9,7 @@ const ( _EINTR = 0x4 _EAGAIN = 0xb _ENOMEM = 0xc + _ENOSYS = 0x26 _PROT_NONE = 0x0 _PROT_READ = 0x1 diff --git a/src/runtime/defs_linux_riscv64.go b/src/runtime/defs_linux_riscv64.go index 29b1ef2a50..b73e208ac3 100644 --- a/src/runtime/defs_linux_riscv64.go +++ b/src/runtime/defs_linux_riscv64.go @@ -10,6 +10,7 @@ const ( _EINTR = 0x4 _EAGAIN = 0xb _ENOMEM = 0xc + _ENOSYS = 0x26 _PROT_NONE = 0x0 _PROT_READ = 0x1 diff --git a/src/runtime/defs_linux_s390x.go b/src/runtime/defs_linux_s390x.go index b0280213b3..c03d0f2117 100644 --- a/src/runtime/defs_linux_s390x.go +++ b/src/runtime/defs_linux_s390x.go @@ -10,6 +10,7 @@ const ( _EINTR = 0x4 _EAGAIN = 0xb _ENOMEM = 0xc + _ENOSYS = 0x26 _PROT_NONE = 0x0 _PROT_READ = 0x1 diff --git a/src/runtime/export_test.go b/src/runtime/export_test.go index fc77b535da..a7b51dd6c7 100644 --- a/src/runtime/export_test.go +++ b/src/runtime/export_test.go @@ -9,7 +9,6 @@ package runtime import ( "internal/abi" "internal/goarch" - "internal/goexperiment" "internal/goos" "internal/runtime/atomic" "internal/runtime/gc" @@ -1122,8 +1121,6 @@ func CheckScavengedBitsCleared(mismatches []BitsMismatch) (n int, ok bool) { // Lock so that we can safely access the bitmap. lock(&mheap_.lock) - heapBase := mheap_.pages.inUse.ranges[0].base.addr() - secondArenaBase := arenaBase(arenaIndex(heapBase) + 1) chunkLoop: for i := mheap_.pages.start; i < mheap_.pages.end; i++ { chunk := mheap_.pages.tryChunkOf(i) @@ -1140,14 +1137,6 @@ func CheckScavengedBitsCleared(mismatches []BitsMismatch) (n int, ok bool) { want := chunk.scavenged[j] &^ chunk.pallocBits[j] got := chunk.scavenged[j] if want != got { - // When goexperiment.RandomizedHeapBase64 is set we use a - // series of padding pages to generate randomized heap base - // address which have both the alloc and scav bits set. If - // we see this for a chunk between the address of the heap - // base, and the address of the second arena continue. - if goexperiment.RandomizedHeapBase64 && (cb >= heapBase && cb < secondArenaBase) { - continue - } ok = false if n >= len(mismatches) { break chunkLoop @@ -1165,6 +1154,37 @@ func CheckScavengedBitsCleared(mismatches []BitsMismatch) (n int, ok bool) { getg().m.mallocing-- }) + + if randomizeHeapBase && len(mismatches) > 0 { + // When goexperiment.RandomizedHeapBase64 is set we use a series of + // padding pages to generate randomized heap base address which have + // both the alloc and scav bits set. Because of this we expect exactly + // one arena will have mismatches, so check for that explicitly and + // remove the mismatches if that property holds. If we see more than one + // arena with this property, that is an indication something has + // actually gone wrong, so return the mismatches. + // + // We do this, instead of ignoring the mismatches in the chunkLoop, because + // it's not easy to determine which arena we added the padding pages to + // programmatically, without explicitly recording the base address somewhere + // in a global variable (which we'd rather not do as the address of that variable + // is likely to be somewhat predictable, potentially defeating the purpose + // of our randomization). + affectedArenas := map[arenaIdx]bool{} + for _, mismatch := range mismatches { + if mismatch.Base > 0 { + affectedArenas[arenaIndex(mismatch.Base)] = true + } + } + if len(affectedArenas) == 1 { + ok = true + // zero the mismatches + for i := range n { + mismatches[i] = BitsMismatch{} + } + } + } + return } diff --git a/src/runtime/malloc.go b/src/runtime/malloc.go index ec5f0765ba..5b5a633d9a 100644 --- a/src/runtime/malloc.go +++ b/src/runtime/malloc.go @@ -349,7 +349,7 @@ const ( // randomizeHeapBase indicates if the heap base address should be randomized. // See comment in mallocinit for how the randomization is performed. - randomizeHeapBase = goexperiment.RandomizedHeapBase64 && goarch.PtrSize == 8 && !isSbrkPlatform + randomizeHeapBase = goexperiment.RandomizedHeapBase64 && goarch.PtrSize == 8 && !isSbrkPlatform && !raceenabled && !msanenabled && !asanenabled // randHeapBasePrefixMask is used to extract the top byte of the randomized // heap base address. diff --git a/src/runtime/mbitmap.go b/src/runtime/mbitmap.go index 9872e5297f..508de9a115 100644 --- a/src/runtime/mbitmap.go +++ b/src/runtime/mbitmap.go @@ -714,6 +714,26 @@ func heapSetTypeNoHeader(x, dataSize uintptr, typ *_type, span *mspan) uintptr { } func heapSetTypeSmallHeader(x, dataSize uintptr, typ *_type, header **_type, span *mspan) uintptr { + if header == nil { + // This nil check and throw is almost pointless. Normally we would + // expect header to never be nil. However, this is called on potentially + // freshly-allocated virtual memory. As of 2025, the compiler-inserted + // nil check is not a branch but a memory read that we expect to fault + // if the pointer really is nil. + // + // However, this causes a read of the page, and operating systems may + // take it as a hint to back the accessed memory with a read-only zero + // page. However, we immediately write to this memory, which can then + // force operating systems to have to update the page table and flush + // the TLB. + // + // This nil check is thus an explicit branch instead of what the compiler + // would insert circa 2025, which is a memory read instruction. + // + // See go.dev/issue/74375 for details of a similar issue in + // spanInlineMarkBits. + throw("runtime: pointer to heap type header nil?") + } *header = typ if doubleCheckHeapSetType { doubleCheckHeapType(x, dataSize, typ, header, span) diff --git a/src/runtime/metrics_test.go b/src/runtime/metrics_test.go index 3989b79293..af042f4445 100644 --- a/src/runtime/metrics_test.go +++ b/src/runtime/metrics_test.go @@ -1760,8 +1760,6 @@ func TestReadMetricsSched(t *testing.T) { metrics.Read(s[:]) return s[notInGo].Value.Uint64() >= count }) - - metrics.Read(s[:]) logMetrics(t, s[:]) check(t, &s[notInGo], count, count+generalSlack) @@ -1782,8 +1780,6 @@ func TestReadMetricsSched(t *testing.T) { metrics.Read(s[:]) return s[waiting].Value.Uint64() >= waitingCount }) - - metrics.Read(s[:]) logMetrics(t, s[:]) check(t, &s[waiting], waitingCount, waitingCount+waitingSlack) diff --git a/src/runtime/os_linux.go b/src/runtime/os_linux.go index f9fe1b5f33..080dd96532 100644 --- a/src/runtime/os_linux.go +++ b/src/runtime/os_linux.go @@ -40,9 +40,6 @@ type mOS struct { waitsema uint32 // semaphore for parking on locks } -//go:noescape -func futex(addr unsafe.Pointer, op int32, val uint32, ts, addr2 unsafe.Pointer, val3 uint32) int32 - // Linux futex. // // futexsleep(uint32 *addr, uint32 val) @@ -79,7 +76,7 @@ func futexsleep(addr *uint32, val uint32, ns int64) { var ts timespec ts.setNsec(ns) - futex(unsafe.Pointer(addr), _FUTEX_WAIT_PRIVATE, val, unsafe.Pointer(&ts), nil, 0) + futex(unsafe.Pointer(addr), _FUTEX_WAIT_PRIVATE, val, &ts, nil, 0) } // If any procs are sleeping on addr, wake up at most cnt. diff --git a/src/runtime/os_linux_futex32.go b/src/runtime/os_linux_futex32.go new file mode 100644 index 0000000000..fdf99e5669 --- /dev/null +++ b/src/runtime/os_linux_futex32.go @@ -0,0 +1,40 @@ +// Copyright 2025 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. + +//go:build linux && (386 || arm || mips || mipsle || ppc) + +package runtime + +import ( + "internal/runtime/atomic" + "unsafe" +) + +//go:noescape +func futex_time32(addr unsafe.Pointer, op int32, val uint32, ts *timespec32, addr2 unsafe.Pointer, val3 uint32) int32 + +//go:noescape +func futex_time64(addr unsafe.Pointer, op int32, val uint32, ts *timespec, addr2 unsafe.Pointer, val3 uint32) int32 + +var is32bitOnly atomic.Bool + +//go:nosplit +func futex(addr unsafe.Pointer, op int32, val uint32, ts *timespec, addr2 unsafe.Pointer, val3 uint32) int32 { + if !is32bitOnly.Load() { + ret := futex_time64(addr, op, val, ts, addr2, val3) + // futex_time64 is only supported on Linux 5.0+ + if ret != -_ENOSYS { + return ret + } + is32bitOnly.Store(true) + } + // Downgrade ts. + var ts32 timespec32 + var pts32 *timespec32 + if ts != nil { + ts32.setNsec(ts.tv_sec*1e9 + ts.tv_nsec) + pts32 = &ts32 + } + return futex_time32(addr, op, val, pts32, addr2, val3) +} diff --git a/src/runtime/os_linux_futex64.go b/src/runtime/os_linux_futex64.go new file mode 100644 index 0000000000..487d0e0397 --- /dev/null +++ b/src/runtime/os_linux_futex64.go @@ -0,0 +1,14 @@ +// Copyright 2025 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. + +//go:build linux && !(386 || arm || mips || mipsle || ppc || s390) + +package runtime + +import ( + "unsafe" +) + +//go:noescape +func futex(addr unsafe.Pointer, op int32, val uint32, ts *timespec, addr2 unsafe.Pointer, val3 uint32) int32 diff --git a/src/runtime/sys_linux_386.s b/src/runtime/sys_linux_386.s index 8e832687e0..1c3f1ff3e6 100644 --- a/src/runtime/sys_linux_386.s +++ b/src/runtime/sys_linux_386.s @@ -48,6 +48,7 @@ #define SYS_madvise 219 #define SYS_gettid 224 #define SYS_futex 240 +#define SYS_futex_time64 422 #define SYS_sched_getaffinity 242 #define SYS_set_thread_area 243 #define SYS_exit_group 252 @@ -532,9 +533,10 @@ TEXT runtime·madvise(SB),NOSPLIT,$0 MOVL AX, ret+12(FP) RET +// Linux: kernel/futex/syscalls.c, requiring COMPAT_32BIT_TIME // int32 futex(int32 *uaddr, int32 op, int32 val, -// struct timespec *timeout, int32 *uaddr2, int32 val2); -TEXT runtime·futex(SB),NOSPLIT,$0 +// struct old_timespec32 *timeout, int32 *uaddr2, int32 val2); +TEXT runtime·futex_time32(SB),NOSPLIT,$0 MOVL $SYS_futex, AX MOVL addr+0(FP), BX MOVL op+4(FP), CX @@ -546,6 +548,21 @@ TEXT runtime·futex(SB),NOSPLIT,$0 MOVL AX, ret+24(FP) RET +// Linux: kernel/futex/syscalls.c +// int32 futex(int32 *uaddr, int32 op, int32 val, +// struct timespec *timeout, int32 *uaddr2, int32 val2); +TEXT runtime·futex_time64(SB),NOSPLIT,$0 + MOVL $SYS_futex_time64, AX + MOVL addr+0(FP), BX + MOVL op+4(FP), CX + MOVL val+8(FP), DX + MOVL ts+12(FP), SI + MOVL addr2+16(FP), DI + MOVL val3+20(FP), BP + INVOKE_SYSCALL + MOVL AX, ret+24(FP) + RET + // int32 clone(int32 flags, void *stack, M *mp, G *gp, void (*fn)(void)); TEXT runtime·clone(SB),NOSPLIT,$0 MOVL $SYS_clone, AX diff --git a/src/runtime/sys_linux_arm.s b/src/runtime/sys_linux_arm.s index 992d32ab6c..44b56ccb9f 100644 --- a/src/runtime/sys_linux_arm.s +++ b/src/runtime/sys_linux_arm.s @@ -30,6 +30,7 @@ #define SYS_sigaltstack (SYS_BASE + 186) #define SYS_mmap2 (SYS_BASE + 192) #define SYS_futex (SYS_BASE + 240) +#define SYS_futex_time64 (SYS_BASE + 422) #define SYS_exit_group (SYS_BASE + 248) #define SYS_munmap (SYS_BASE + 91) #define SYS_madvise (SYS_BASE + 220) @@ -403,9 +404,10 @@ finish: RET +// Linux: kernel/futex/syscalls.c, requiring COMPAT_32BIT_TIME // int32 futex(int32 *uaddr, int32 op, int32 val, -// struct timespec *timeout, int32 *uaddr2, int32 val2); -TEXT runtime·futex(SB),NOSPLIT,$0 +// struct old_timespec32 *timeout, int32 *uaddr2, int32 val2); +TEXT runtime·futex_time32(SB),NOSPLIT,$0 MOVW addr+0(FP), R0 MOVW op+4(FP), R1 MOVW val+8(FP), R2 @@ -417,6 +419,21 @@ TEXT runtime·futex(SB),NOSPLIT,$0 MOVW R0, ret+24(FP) RET +// Linux: kernel/futex/syscalls.c +// int32 futex(int32 *uaddr, int32 op, int32 val, +// struct timespec *timeout, int32 *uaddr2, int32 val2); +TEXT runtime·futex_time64(SB),NOSPLIT,$0 + MOVW addr+0(FP), R0 + MOVW op+4(FP), R1 + MOVW val+8(FP), R2 + MOVW ts+12(FP), R3 + MOVW addr2+16(FP), R4 + MOVW val3+20(FP), R5 + MOVW $SYS_futex_time64, R7 + SWI $0 + MOVW R0, ret+24(FP) + RET + // int32 clone(int32 flags, void *stack, M *mp, G *gp, void (*fn)(void)); TEXT runtime·clone(SB),NOSPLIT,$0 MOVW flags+0(FP), R0 diff --git a/src/runtime/sys_linux_mipsx.s b/src/runtime/sys_linux_mipsx.s index 5e6b6c1504..6f11841efc 100644 --- a/src/runtime/sys_linux_mipsx.s +++ b/src/runtime/sys_linux_mipsx.s @@ -34,6 +34,7 @@ #define SYS_mincore 4217 #define SYS_gettid 4222 #define SYS_futex 4238 +#define SYS_futex_time64 4422 #define SYS_sched_getaffinity 4240 #define SYS_exit_group 4246 #define SYS_timer_create 4257 @@ -362,8 +363,10 @@ TEXT runtime·madvise(SB),NOSPLIT,$0-16 MOVW R2, ret+12(FP) RET -// int32 futex(int32 *uaddr, int32 op, int32 val, struct timespec *timeout, int32 *uaddr2, int32 val2); -TEXT runtime·futex(SB),NOSPLIT,$20-28 +// Linux: kernel/futex/syscalls.c, requiring COMPAT_32BIT_TIME +// int32 futex(int32 *uaddr, int32 op, int32 val, +// struct old_timespec32 *timeout, int32 *uaddr2, int32 val2); +TEXT runtime·futex_time32(SB),NOSPLIT,$20-28 MOVW addr+0(FP), R4 MOVW op+4(FP), R5 MOVW val+8(FP), R6 @@ -382,6 +385,27 @@ TEXT runtime·futex(SB),NOSPLIT,$20-28 MOVW R2, ret+24(FP) RET +// Linux: kernel/futex/syscalls.c +// int32 futex(int32 *uaddr, int32 op, int32 val, +// struct timespec *timeout, int32 *uaddr2, int32 val2); +TEXT runtime·futex_time64(SB),NOSPLIT,$20-28 + MOVW addr+0(FP), R4 + MOVW op+4(FP), R5 + MOVW val+8(FP), R6 + MOVW ts+12(FP), R7 + + MOVW addr2+16(FP), R8 + MOVW val3+20(FP), R9 + + MOVW R8, 16(R29) + MOVW R9, 20(R29) + + MOVW $SYS_futex_time64, R2 + SYSCALL + BEQ R7, 2(PC) + SUBU R2, R0, R2 // caller expects negative errno + MOVW R2, ret+24(FP) + RET // int32 clone(int32 flags, void *stk, M *mp, G *gp, void (*fn)(void)); TEXT runtime·clone(SB),NOSPLIT|NOFRAME,$0-24 diff --git a/src/runtime/unsafepoint_test.go b/src/runtime/unsafepoint_test.go index 2c97adead8..79f0171854 100644 --- a/src/runtime/unsafepoint_test.go +++ b/src/runtime/unsafepoint_test.go @@ -43,7 +43,7 @@ func TestUnsafePoint(t *testing.T) { cmd := exec.Command(testenv.GoToolPath(t), "tool", "objdump", "-s", "setGlobalPointer", os.Args[0]) out, err := cmd.CombinedOutput() if err != nil { - t.Fatalf("can't objdump %v", err) + t.Fatalf("can't objdump %v:\n%s", err, out) } lines := strings.Split(string(out), "\n")[1:] diff --git a/src/strings/strings.go b/src/strings/strings.go index 3cc3e79f98..1efd00d5f0 100644 --- a/src/strings/strings.go +++ b/src/strings/strings.go @@ -433,7 +433,7 @@ func Fields(s string) []string { // FieldsFunc splits the string s at each run of Unicode code points c satisfying f(c) // and returns an array of slices of s. If all code points in s satisfy f(c) or the // string is empty, an empty slice is returned. Every element of the returned slice is -// non-empty. Unlike [SplitFunc], leading and trailing runs of code points satisfying f(c) +// non-empty. Unlike [Split], leading and trailing runs of code points satisfying f(c) // are discarded. // // FieldsFunc makes no guarantees about the order in which it calls f(c) diff --git a/src/testing/fuzz.go b/src/testing/fuzz.go index c07d6a0604..5867935b81 100644 --- a/src/testing/fuzz.go +++ b/src/testing/fuzz.go @@ -163,23 +163,23 @@ func (f *F) Add(args ...any) { // supportedTypes represents all of the supported types which can be fuzzed. var supportedTypes = map[reflect.Type]bool{ - reflect.TypeOf(([]byte)("")): true, - reflect.TypeOf((string)("")): true, - reflect.TypeOf((bool)(false)): true, - reflect.TypeOf((byte)(0)): true, - reflect.TypeOf((rune)(0)): true, - reflect.TypeOf((float32)(0)): true, - reflect.TypeOf((float64)(0)): true, - reflect.TypeOf((int)(0)): true, - reflect.TypeOf((int8)(0)): true, - reflect.TypeOf((int16)(0)): true, - reflect.TypeOf((int32)(0)): true, - reflect.TypeOf((int64)(0)): true, - reflect.TypeOf((uint)(0)): true, - reflect.TypeOf((uint8)(0)): true, - reflect.TypeOf((uint16)(0)): true, - reflect.TypeOf((uint32)(0)): true, - reflect.TypeOf((uint64)(0)): true, + reflect.TypeFor[[]byte](): true, + reflect.TypeFor[string](): true, + reflect.TypeFor[bool](): true, + reflect.TypeFor[byte](): true, + reflect.TypeFor[rune](): true, + reflect.TypeFor[float32](): true, + reflect.TypeFor[float64](): true, + reflect.TypeFor[int](): true, + reflect.TypeFor[int8](): true, + reflect.TypeFor[int16](): true, + reflect.TypeFor[int32](): true, + reflect.TypeFor[int64](): true, + reflect.TypeFor[uint](): true, + reflect.TypeFor[uint8](): true, + reflect.TypeFor[uint16](): true, + reflect.TypeFor[uint32](): true, + reflect.TypeFor[uint64](): true, } // Fuzz runs the fuzz function, ff, for fuzz testing. If ff fails for a set of @@ -224,7 +224,7 @@ func (f *F) Fuzz(ff any) { if fnType.Kind() != reflect.Func { panic("testing: F.Fuzz must receive a function") } - if fnType.NumIn() < 2 || fnType.In(0) != reflect.TypeOf((*T)(nil)) { + if fnType.NumIn() < 2 || fnType.In(0) != reflect.TypeFor[*T]() { panic("testing: fuzz target must receive at least two arguments, where the first argument is a *T") } if fnType.NumOut() != 0 { diff --git a/src/testing/quick/quick.go b/src/testing/quick/quick.go index 8ef9cf7dda..6b3089d049 100644 --- a/src/testing/quick/quick.go +++ b/src/testing/quick/quick.go @@ -64,7 +64,7 @@ func Value(t reflect.Type, rand *rand.Rand) (value reflect.Value, ok bool) { // hint is used for shrinking as a function of indirection level so // that recursive data structures will terminate. func sizedValue(t reflect.Type, rand *rand.Rand, size int) (value reflect.Value, ok bool) { - if m, ok := reflect.Zero(t).Interface().(Generator); ok { + if m, ok := reflect.TypeAssert[Generator](reflect.Zero(t)); ok { return m.Generate(rand, size), true } diff --git a/src/testing/testing.go b/src/testing/testing.go index 2f0206b0a4..3f76446549 100644 --- a/src/testing/testing.go +++ b/src/testing/testing.go @@ -30,9 +30,9 @@ // import "testing" // // func TestAbs(t *testing.T) { -// got := Abs(-1) +// got := abs(-1) // if got != 1 { -// t.Errorf("Abs(-1) = %d; want 1", got) +// t.Errorf("abs(-1) = %d; want 1", got) // } // } // diff --git a/src/time/format.go b/src/time/format.go index 87e990d48a..ad5486f4d2 100644 --- a/src/time/format.go +++ b/src/time/format.go @@ -1602,6 +1602,16 @@ func leadingFraction(s string) (x uint64, scale float64, rem string) { return x, scale, s[i:] } +// parseDurationError describes a problem parsing a duration string. +type parseDurationError struct { + message string + value string +} + +func (e *parseDurationError) Error() string { + return "time: " + e.message + " " + quote(e.value) +} + var unitMap = map[string]uint64{ "ns": uint64(Nanosecond), "us": uint64(Microsecond), @@ -1637,7 +1647,7 @@ func ParseDuration(s string) (Duration, error) { return 0, nil } if s == "" { - return 0, errors.New("time: invalid duration " + quote(orig)) + return 0, &parseDurationError{"invalid duration", orig} } for s != "" { var ( @@ -1649,13 +1659,13 @@ func ParseDuration(s string) (Duration, error) { // The next character must be [0-9.] if !(s[0] == '.' || '0' <= s[0] && s[0] <= '9') { - return 0, errors.New("time: invalid duration " + quote(orig)) + return 0, &parseDurationError{"invalid duration", orig} } // Consume [0-9]* pl := len(s) v, s, err = leadingInt(s) if err != nil { - return 0, errors.New("time: invalid duration " + quote(orig)) + return 0, &parseDurationError{"invalid duration", orig} } pre := pl != len(s) // whether we consumed anything before a period @@ -1669,7 +1679,7 @@ func ParseDuration(s string) (Duration, error) { } if !pre && !post { // no digits (e.g. ".s" or "-.s") - return 0, errors.New("time: invalid duration " + quote(orig)) + return 0, &parseDurationError{"invalid duration", orig} } // Consume unit. @@ -1681,17 +1691,17 @@ func ParseDuration(s string) (Duration, error) { } } if i == 0 { - return 0, errors.New("time: missing unit in duration " + quote(orig)) + return 0, &parseDurationError{"missing unit in duration", orig} } u := s[:i] s = s[i:] unit, ok := unitMap[u] if !ok { - return 0, errors.New("time: unknown unit " + quote(u) + " in duration " + quote(orig)) + return 0, &parseDurationError{"unknown unit " + quote(u) + " in duration", orig} } if v > 1<<63/unit { // overflow - return 0, errors.New("time: invalid duration " + quote(orig)) + return 0, &parseDurationError{"invalid duration", orig} } v *= unit if f > 0 { @@ -1700,19 +1710,19 @@ func ParseDuration(s string) (Duration, error) { v += uint64(float64(f) * (float64(unit) / scale)) if v > 1<<63 { // overflow - return 0, errors.New("time: invalid duration " + quote(orig)) + return 0, &parseDurationError{"invalid duration", orig} } } d += v if d > 1<<63 { - return 0, errors.New("time: invalid duration " + quote(orig)) + return 0, &parseDurationError{"invalid duration", orig} } } if neg { return -Duration(d), nil } if d > 1<<63-1 { - return 0, errors.New("time: invalid duration " + quote(orig)) + return 0, &parseDurationError{"invalid duration", orig} } return Duration(d), nil } diff --git a/src/time/time_test.go b/src/time/time_test.go index a2d4305c8c..a453ee043c 100644 --- a/src/time/time_test.go +++ b/src/time/time_test.go @@ -1620,6 +1620,13 @@ func BenchmarkParseDuration(b *testing.B) { } } +func BenchmarkParseDurationError(b *testing.B) { + for i := 0; i < b.N; i++ { + ParseDuration("9223372036854775810ns") // overflow + ParseDuration("9007199254.740993") // missing unit + } +} + func BenchmarkHour(b *testing.B) { t := Now() for i := 0; i < b.N; i++ { |
