diff --git a/doc/go_spec.html b/doc/go_spec.html index ab90c420fd359c38d5ecd705f6d11e9a9ef71b01..db5fba45a53378e32aec485b3c0193e14faf73d0 100644 --- a/doc/go_spec.html +++ b/doc/go_spec.html @@ -8514,7 +8514,7 @@ var p ptr = nil <p> The functions <code>Alignof</code> and <code>Sizeof</code> take an expression <code>x</code> of any type and return the alignment or size, respectively, of a hypothetical variable <code>v</code> -as if <code>v</code> was declared via <code>var v = x</code>. +as if <code>v</code> were declared via <code>var v = x</code>. </p> <p> The function <code>Offsetof</code> takes a (possibly parenthesized) <a href="#Selectors">selector</a> diff --git a/lib/fips140/Makefile b/lib/fips140/Makefile index cd657ae72fd24809fc71239b0b9246ee48675d05..8dcb8fbebe5becf736c4c20993f024dcdbf22c58 100644 --- a/lib/fips140/Makefile +++ b/lib/fips140/Makefile @@ -27,7 +27,7 @@ default: # copy and edit the 'go run' command by hand to use a different branch. v%.zip: git fetch origin master - go run ../../src/cmd/go/internal/fips140/mkzip.go -b master v$* + go run ../../src/cmd/go/internal/fips140/mkzip.go v$* # normally mkzip refuses to overwrite an existing zip file. # make v1.2.3.rm removes the zip file and and unpacked diff --git a/lib/fips140/fips140.sum b/lib/fips140/fips140.sum index 013112d9e58d0d07242fa9b3647cbab2098467e9..66b1e23dfe619d1c1fcc866e3ec17e7cdf8f148e 100644 --- a/lib/fips140/fips140.sum +++ b/lib/fips140/fips140.sum @@ -9,3 +9,4 @@ # # go test cmd/go/internal/fips140 -update # +v1.0.0.zip b50508feaeff05d22516b21e1fd210bbf5d6a1e422eaf2cfa23fe379342713b8 diff --git a/lib/fips140/v1.0.0.zip b/lib/fips140/v1.0.0.zip new file mode 100644 index 0000000000000000000000000000000000000000..bd9d3c19d05b9075a2587806d00224c3f8859ba1 Binary files /dev/null and b/lib/fips140/v1.0.0.zip differ diff --git a/lib/time/update.bash b/lib/time/update.bash index 6b66fa54a9595a9b953a9654e92936dce0514c9e..67cb016e79392fd519d2d62e3bd17cbe0a7f4767 100755 --- a/lib/time/update.bash +++ b/lib/time/update.bash @@ -24,8 +24,8 @@ # in the CL match the update.bash in the CL. # Versions to use. -CODE=2024b -DATA=2024b +CODE=2025a +DATA=2025a set -e diff --git a/lib/time/zoneinfo.zip b/lib/time/zoneinfo.zip index b36e82c958aef016d7cdf72cdcbf570adb4418b1..6ba9ff6fd6afc9736c41c5ec2c3bfd1aa12a0f1b 100644 Binary files a/lib/time/zoneinfo.zip and b/lib/time/zoneinfo.zip differ diff --git a/src/archive/tar/writer.go b/src/archive/tar/writer.go index 059669767f2b9db811c31ebca3293218a77687c1..f966c5b4c648f5268e0a3fc528a797a8b90cfb5d 100644 --- a/src/archive/tar/writer.go +++ b/src/archive/tar/writer.go @@ -424,6 +424,9 @@ func (tw *Writer) AddFS(fsys fs.FS) error { return err } h.Name = name + if d.IsDir() { + h.Name += "/" + } if err := tw.WriteHeader(h); err != nil { return err } diff --git a/src/archive/tar/writer_test.go b/src/archive/tar/writer_test.go index 2a01915d368c5a8a3cf65f263bcb23edd70a629f..7b10bf6a700d7f0340506476bd98e3ba2289f9fa 100644 --- a/src/archive/tar/writer_test.go +++ b/src/archive/tar/writer_test.go @@ -1382,7 +1382,11 @@ func TestWriterAddFS(t *testing.T) { t.Fatal(err) } - if hdr.Name != name { + tmpName := name + if entryInfo.IsDir() { + tmpName += "/" + } + if hdr.Name != tmpName { t.Errorf("test fs has filename %v; archive header has %v", name, hdr.Name) } diff --git a/src/archive/zip/writer.go b/src/archive/zip/writer.go index cbe5ba262747f64bae152907a0678c1ea6105486..0a310054e37678cd521bbb9b09f23a3eca58fd19 100644 --- a/src/archive/zip/writer.go +++ b/src/archive/zip/writer.go @@ -520,6 +520,9 @@ func (w *Writer) AddFS(fsys fs.FS) error { return err } h.Name = name + if d.IsDir() { + h.Name += "/" + } h.Method = Deflate fw, err := w.CreateHeader(h) if err != nil { diff --git a/src/archive/zip/writer_test.go b/src/archive/zip/writer_test.go index 27a99b6b3a13b9c65bff71a1e3c951e9fdeb0930..44592ce8318826e0a912304d9ac0d2bb2b2f113d 100644 --- a/src/archive/zip/writer_test.go +++ b/src/archive/zip/writer_test.go @@ -633,7 +633,7 @@ func TestWriterAddFS(t *testing.T) { t.Fatal(err) } - // Add subfolder into fsys to match what we'll read from the tar. + // Add subfolder into fsys to match what we'll read from the zip. tests = append(tests[:2:2], WriteTest{Name: "subfolder", Mode: 0o555 | os.ModeDir}, tests[2]) // read it back @@ -642,6 +642,9 @@ func TestWriterAddFS(t *testing.T) { t.Fatal(err) } for i, wt := range tests { + if wt.Mode.IsDir() { + wt.Name += "/" + } testReadFile(t, r.File[i], &wt) } } diff --git a/src/cmd/api/api_test.go b/src/cmd/api/api_test.go index 78482333330bd8bce36d69e385b6e901ecd90a06..32da68982bce452b59f6b9aa19f1c0af77df59ff 100644 --- a/src/cmd/api/api_test.go +++ b/src/cmd/api/api_test.go @@ -57,7 +57,10 @@ func TestGolden(t *testing.T) { // TODO(gri) remove extra pkg directory eventually goldenFile := filepath.Join("testdata", "src", "pkg", fi.Name(), "golden.txt") w := NewWalker(nil, "testdata/src/pkg") - pkg, _ := w.import_(fi.Name()) + pkg, err := w.import_(fi.Name()) + if err != nil { + t.Fatalf("import %s: %v", fi.Name(), err) + } w.export(pkg) if *updateGolden { @@ -201,7 +204,13 @@ func BenchmarkAll(b *testing.B) { for _, context := range contexts { w := NewWalker(context, filepath.Join(testenv.GOROOT(b), "src")) for _, name := range w.stdPackages { - pkg, _ := w.import_(name) + pkg, err := w.import_(name) + if _, nogo := err.(*build.NoGoError); nogo { + continue + } + if err != nil { + b.Fatalf("import %s (%s-%s): %v", name, context.GOOS, context.GOARCH, err) + } w.export(pkg) } w.Features() @@ -239,8 +248,7 @@ func TestIssue21181(t *testing.T) { w := NewWalker(context, "testdata/src/issue21181") pkg, err := w.import_("p") if err != nil { - t.Fatalf("%s: (%s-%s) %s %v", err, context.GOOS, context.GOARCH, - pkg.Name(), w.imported) + t.Fatalf("import %s (%s-%s): %v", "p", context.GOOS, context.GOARCH, err) } w.export(pkg) } diff --git a/src/cmd/compile/internal/ssa/writebarrier.go b/src/cmd/compile/internal/ssa/writebarrier.go index 1caccb7c18d3c43ca24ff9814ef7b10d0660826d..71acefbf8ae52641a5376db296fea23e52b6d2ff 100644 --- a/src/cmd/compile/internal/ssa/writebarrier.go +++ b/src/cmd/compile/internal/ssa/writebarrier.go @@ -252,6 +252,7 @@ func writebarrier(f *Func) { var start, end int var nonPtrStores int values := b.Values + hasMove := false FindSeq: for i := len(values) - 1; i >= 0; i-- { w := values[i] @@ -263,6 +264,9 @@ func writebarrier(f *Func) { end = i + 1 } nonPtrStores = 0 + if w.Op == OpMoveWB { + hasMove = true + } case OpVarDef, OpVarLive: continue case OpStore: @@ -273,6 +277,17 @@ func writebarrier(f *Func) { if nonPtrStores > 2 { break FindSeq } + if hasMove { + // We need to ensure that this store happens + // before we issue a wbMove, as the wbMove might + // use the result of this store as its source. + // Even though this store is not write-barrier + // eligible, it might nevertheless be the store + // of a pointer to the stack, which is then the + // source of the move. + // See issue 71228. + break FindSeq + } default: if last == nil { continue diff --git a/src/cmd/compile/internal/typecheck/_builtin/runtime.go b/src/cmd/compile/internal/typecheck/_builtin/runtime.go index 9a83911487830a96890561b877b9b886ccc793af..cf07f31e31b83ed3da95674a437385ef428e53d9 100644 --- a/src/cmd/compile/internal/typecheck/_builtin/runtime.go +++ b/src/cmd/compile/internal/typecheck/_builtin/runtime.go @@ -152,12 +152,14 @@ func mapassign_fast32ptr(mapType *byte, hmap map[any]any, key unsafe.Pointer) (v func mapassign_fast64(mapType *byte, hmap map[any]any, key uint64) (val *any) func mapassign_fast64ptr(mapType *byte, hmap map[any]any, key unsafe.Pointer) (val *any) func mapassign_faststr(mapType *byte, hmap map[any]any, key string) (val *any) -func mapiterinit(mapType *byte, hmap map[any]any, hiter *any) +func mapiterinit(mapType *byte, hmap map[any]any, hiter *any) // old maps +func mapIterStart(mapType *byte, hmap map[any]any, hiter *any) // swiss maps func mapdelete(mapType *byte, hmap map[any]any, key *any) func mapdelete_fast32(mapType *byte, hmap map[any]any, key uint32) func mapdelete_fast64(mapType *byte, hmap map[any]any, key uint64) func mapdelete_faststr(mapType *byte, hmap map[any]any, key string) -func mapiternext(hiter *any) +func mapiternext(hiter *any) // old maps +func mapIterNext(hiter *any) // swiss maps func mapclear(mapType *byte, hmap map[any]any) // *byte is really *runtime.Type diff --git a/src/cmd/compile/internal/typecheck/builtin.go b/src/cmd/compile/internal/typecheck/builtin.go index 6860d78b2e4024b2b332343a657c5122f1b3fc57..be08d0b40329a12a601685059b900599c6e151ff 100644 --- a/src/cmd/compile/internal/typecheck/builtin.go +++ b/src/cmd/compile/internal/typecheck/builtin.go @@ -131,11 +131,13 @@ var runtimeDecls = [...]struct { {"mapassign_fast64ptr", funcTag, 96}, {"mapassign_faststr", funcTag, 89}, {"mapiterinit", funcTag, 97}, + {"mapIterStart", funcTag, 97}, {"mapdelete", funcTag, 97}, {"mapdelete_fast32", funcTag, 98}, {"mapdelete_fast64", funcTag, 99}, {"mapdelete_faststr", funcTag, 100}, {"mapiternext", funcTag, 101}, + {"mapIterNext", funcTag, 101}, {"mapclear", funcTag, 102}, {"makechan64", funcTag, 104}, {"makechan", funcTag, 105}, diff --git a/src/cmd/compile/internal/types2/call.go b/src/cmd/compile/internal/types2/call.go index ae2ab5f9849bc96cd8a2a0502aeefdf84ee046f2..897c846d8ff98de099b76b01ef1837d01fb85a53 100644 --- a/src/cmd/compile/internal/types2/call.go +++ b/src/cmd/compile/internal/types2/call.go @@ -142,6 +142,9 @@ func (check *Checker) instantiateSignature(pos syntax.Pos, expr syntax.Expr, typ }() } + // For signatures, Checker.instance will always succeed because the type argument + // count is correct at this point (see assertion above); hence the type assertion + // to *Signature will always succeed. inst := check.instance(pos, typ, targs, nil, check.context()).(*Signature) assert(inst.TypeParams().Len() == 0) // signature is not generic anymore check.recordInstance(expr, targs, inst) diff --git a/src/cmd/compile/internal/types2/initorder.go b/src/cmd/compile/internal/types2/initorder.go index ef2ad010a6d5fb80bc0bff2a6686f05a69226f6a..699bfca8bbc895a1f966af307e477142810022ae 100644 --- a/src/cmd/compile/internal/types2/initorder.go +++ b/src/cmd/compile/internal/types2/initorder.go @@ -10,6 +10,7 @@ import ( "fmt" . "internal/types/errors" "slices" + "sort" ) // initOrder computes the Info.InitOrder for package variables. @@ -139,7 +140,16 @@ func findPath(objMap map[Object]*declInfo, from, to Object, seen map[Object]bool } seen[from] = true + // sort deps for deterministic result + var deps []Object for d := range objMap[from].deps { + deps = append(deps, d) + } + sort.Slice(deps, func(i, j int) bool { + return deps[i].order() < deps[j].order() + }) + + for _, d := range deps { if d == to { return []Object{d} } diff --git a/src/cmd/compile/internal/types2/instantiate.go b/src/cmd/compile/internal/types2/instantiate.go index e51cf18de67fd675cde2212328b998c854e283cf..03c490a3866df2123eb3e935aa6c855b983e992c 100644 --- a/src/cmd/compile/internal/types2/instantiate.go +++ b/src/cmd/compile/internal/types2/instantiate.go @@ -74,7 +74,8 @@ func Instantiate(ctxt *Context, orig Type, targs []Type, validate bool) (Type, e // instance instantiates the given original (generic) function or type with the // provided type arguments and returns the resulting instance. If an identical // instance exists already in the given contexts, it returns that instance, -// otherwise it creates a new one. +// otherwise it creates a new one. If there is an error (such as wrong number +// of type arguments), the result is Typ[Invalid]. // // If expanding is non-nil, it is the Named instance type currently being // expanded. If ctxt is non-nil, it is the context associated with the current @@ -133,9 +134,13 @@ func (check *Checker) instance(pos syntax.Pos, orig genericType, targs []Type, e assert(expanding == nil) // Alias instances cannot be reached from Named types } + // verify type parameter count (see go.dev/issue/71198 for a test case) tparams := orig.TypeParams() - // TODO(gri) investigate if this is needed (type argument and parameter count seem to be correct here) - if !check.validateTArgLen(pos, orig.String(), tparams.Len(), len(targs)) { + if !check.validateTArgLen(pos, orig.obj.Name(), tparams.Len(), len(targs)) { + // TODO(gri) Consider returning a valid alias instance with invalid + // underlying (aliased) type to match behavior of *Named + // types. Then this function will never return an invalid + // result. return Typ[Invalid] } if tparams.Len() == 0 { diff --git a/src/cmd/compile/internal/types2/stmt.go b/src/cmd/compile/internal/types2/stmt.go index 2174aedf7f4f0194a9cd0091c00088cdbe39b4c0..c46ea7a091afd2df4827f5c353209c5e2ea1b878 100644 --- a/src/cmd/compile/internal/types2/stmt.go +++ b/src/cmd/compile/internal/types2/stmt.go @@ -1057,8 +1057,13 @@ func rangeKeyVal(typ Type, allowVersion func(goVersion) bool) (key, val Type, ca return bad("func must be func(yield func(...) bool): argument is not func") case cb.Params().Len() > 2: return bad("func must be func(yield func(...) bool): yield func has too many parameters") - case cb.Results().Len() != 1 || !isBoolean(cb.Results().At(0).Type()): - return bad("func must be func(yield func(...) bool): yield func does not return bool") + case cb.Results().Len() != 1 || !Identical(cb.Results().At(0).Type(), universeBool): + // see go.dev/issues/71131, go.dev/issues/71164 + if cb.Results().Len() == 1 && isBoolean(cb.Results().At(0).Type()) { + return bad("func must be func(yield func(...) bool): yield func returns user-defined boolean, not bool") + } else { + return bad("func must be func(yield func(...) bool): yield func does not return bool") + } } assert(cb.Recv() == nil) // determine key and value types, if any diff --git a/src/cmd/compile/internal/types2/testdata/local/issue71254.go b/src/cmd/compile/internal/types2/testdata/local/issue71254.go new file mode 100644 index 0000000000000000000000000000000000000000..9cca9d5bc4dc6e2a70e5bb9bdc19032a459c9337 --- /dev/null +++ b/src/cmd/compile/internal/types2/testdata/local/issue71254.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. + +package p + +const ( + B /* ERROR "initialization cycle: B refers to itself" */ = A + B + A /* ERRORx "initialization cycle for A\\s+.*A refers to B\\s+.*B refers to A" */ = A + B + + C /* ERRORx "initialization cycle for C\\s+.*C refers to D\\s+.*D refers to C" */ = E + D + D /* ERRORx "initialization cycle for D\\s+.*D refers to C\\s+.*C refers to D" */ = E + C + E = D + C +) diff --git a/src/cmd/compile/internal/types2/testdata/manual.go b/src/cmd/compile/internal/types2/testdata/manual.go index d8f312f61d58c50e54cb8e70f9f80dc0a98c05a1..825ab50f92de4907d91e17bd872c0a198e13c840 100644 --- a/src/cmd/compile/internal/types2/testdata/manual.go +++ b/src/cmd/compile/internal/types2/testdata/manual.go @@ -1,4 +1,4 @@ -// Copyright 2024 The Go Authors. All rights reserved. +// 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. diff --git a/src/cmd/compile/internal/types2/typexpr.go b/src/cmd/compile/internal/types2/typexpr.go index d955654fc9af3760b69ea084c79f2cee249ea55e..e9b5ca9aa6c329068d2917da2dbf5dd96fca3ff9 100644 --- a/src/cmd/compile/internal/types2/typexpr.go +++ b/src/cmd/compile/internal/types2/typexpr.go @@ -423,11 +423,6 @@ func setDefType(def *TypeName, typ Type) { if def != nil { switch t := def.typ.(type) { case *Alias: - // t.fromRHS should always be set, either to an invalid type - // in the beginning, or to typ in certain cyclic declarations. - if t.fromRHS != Typ[Invalid] && t.fromRHS != typ { - panic(sprintf(nil, true, "t.fromRHS = %s, typ = %s\n", t.fromRHS, typ)) - } t.fromRHS = typ case *Basic: assert(t == Typ[Invalid]) @@ -475,9 +470,14 @@ func (check *Checker) instantiatedType(x syntax.Expr, xlist []syntax.Expr, def * } // create instance - // The instance is not generic anymore as it has type arguments, but it still - // satisfies the genericType interface because it has type parameters, too. - inst := check.instance(x.Pos(), gtyp, targs, nil, check.context()).(genericType) + // The instance is not generic anymore as it has type arguments, but unless + // instantiation failed, it still satisfies the genericType interface because + // it has type parameters, too. + ityp := check.instance(x.Pos(), gtyp, targs, nil, check.context()) + inst, _ := ityp.(genericType) + if inst == nil { + return Typ[Invalid] + } // For Named types, orig.tparams may not be set up, so we need to do expansion later. check.later(func() { diff --git a/src/cmd/compile/internal/types2/universe.go b/src/cmd/compile/internal/types2/universe.go index 9c76ac23730f0fbd10d5c68777188831ee6cae11..7664a53579ab3b3a7e9a7d1e117a936b8e5658a7 100644 --- a/src/cmd/compile/internal/types2/universe.go +++ b/src/cmd/compile/internal/types2/universe.go @@ -21,6 +21,7 @@ var Unsafe *Package var ( universeIota Object + universeBool Type universeByte Type // uint8 alias, but has name "byte" universeRune Type // int32 alias, but has name "rune" universeAnyNoAlias *TypeName @@ -275,6 +276,7 @@ func init() { defPredeclaredFuncs() universeIota = Universe.Lookup("iota") + universeBool = Universe.Lookup("bool").Type() universeByte = Universe.Lookup("byte").Type() universeRune = Universe.Lookup("rune").Type() universeError = Universe.Lookup("error").Type() diff --git a/src/cmd/compile/internal/walk/range.go b/src/cmd/compile/internal/walk/range.go index 27e71425c1b015ae831d76d6898981455b3f2f46..a51b218ae57d20c07b83a710f53c01e533f46b27 100644 --- a/src/cmd/compile/internal/walk/range.go +++ b/src/cmd/compile/internal/walk/range.go @@ -244,19 +244,24 @@ func walkRange(nrange *ir.RangeStmt) ir.Node { // depends on layout of iterator struct. // See cmd/compile/internal/reflectdata/reflect.go:MapIterType var keysym, elemsym *types.Sym + var iterInit, iterNext string if buildcfg.Experiment.SwissMap { keysym = th.Field(0).Sym elemsym = th.Field(1).Sym // ditto + iterInit = "mapIterStart" + iterNext = "mapIterNext" } else { keysym = th.Field(0).Sym elemsym = th.Field(1).Sym // ditto + iterInit = "mapiterinit" + iterNext = "mapiternext" } - fn := typecheck.LookupRuntime("mapiterinit", t.Key(), t.Elem(), th) + fn := typecheck.LookupRuntime(iterInit, t.Key(), t.Elem(), th) init = append(init, mkcallstmt1(fn, reflectdata.RangeMapRType(base.Pos, nrange), ha, typecheck.NodAddr(hit))) nfor.Cond = ir.NewBinaryExpr(base.Pos, ir.ONE, ir.NewSelectorExpr(base.Pos, ir.ODOT, hit, keysym), typecheck.NodNil()) - fn = typecheck.LookupRuntime("mapiternext", th) + fn = typecheck.LookupRuntime(iterNext, th) nfor.Post = mkcallstmt1(fn, typecheck.NodAddr(hit)) key := ir.NewStarExpr(base.Pos, typecheck.ConvNop(ir.NewSelectorExpr(base.Pos, ir.ODOT, hit, keysym), types.NewPtr(t.Key()))) diff --git a/src/cmd/covdata/dump.go b/src/cmd/covdata/dump.go index cbbeae0a8020de30b016ceed9266e67cb8042d23..e141689b00e563a48556efb5d68ac00efb9b1aa5 100644 --- a/src/cmd/covdata/dump.go +++ b/src/cmd/covdata/dump.go @@ -331,7 +331,7 @@ func (d *dstate) Finish() { d.format.EmitFuncs(os.Stdout) } if d.textfmtoutf != nil { - if err := d.format.EmitTextual(d.textfmtoutf); err != nil { + if err := d.format.EmitTextual(nil, d.textfmtoutf); err != nil { fatal("writing to %s: %v", *textfmtoutflag, err) } } diff --git a/src/cmd/go.mod b/src/cmd/go.mod index b29321de7b42449a2ff640c53e868d5bdcccbcc6..9c29c3ac74d197e991578cbef9d14f033978345a 100644 --- a/src/cmd/go.mod +++ b/src/cmd/go.mod @@ -11,7 +11,7 @@ require ( golang.org/x/sys v0.28.0 golang.org/x/telemetry v0.0.0-20241204182053-c0ac0e154df3 golang.org/x/term v0.27.0 - golang.org/x/tools v0.28.0 + golang.org/x/tools v0.28.1-0.20250131145412-98746475647e ) require ( diff --git a/src/cmd/go.sum b/src/cmd/go.sum index 5c262454d5b6535ca34052e95f8c167f144e1ddf..593063a9daa35156d0863cef93c02beb8b7dfe9d 100644 --- a/src/cmd/go.sum +++ b/src/cmd/go.sum @@ -22,7 +22,7 @@ golang.org/x/term v0.27.0 h1:WP60Sv1nlK1T6SupCHbXzSaN0b9wUmsPoRS9b61A23Q= golang.org/x/term v0.27.0/go.mod h1:iMsnZpn0cago0GOrHO2+Y7u7JPn5AylBrcoWkElMTSM= golang.org/x/text v0.21.0 h1:zyQAAkrwaneQ066sspRyJaG9VNi/YJ1NfzcGB3hZ/qo= golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ= -golang.org/x/tools v0.28.0 h1:WuB6qZ4RPCQo5aP3WdKZS7i595EdWqWR8vqJTlwTVK8= -golang.org/x/tools v0.28.0/go.mod h1:dcIOrVd3mfQKTgrDVQHqCPMWy6lnhfhtX3hLXYVLfRw= +golang.org/x/tools v0.28.1-0.20250131145412-98746475647e h1:6Kzwg7JxW2HRWToKpIKqlpF8l8XMasoALX3OcAMdgL8= +golang.org/x/tools v0.28.1-0.20250131145412-98746475647e/go.mod h1:dcIOrVd3mfQKTgrDVQHqCPMWy6lnhfhtX3hLXYVLfRw= rsc.io/markdown v0.0.0-20240306144322-0bf8f97ee8ef h1:mqLYrXCXYEZOop9/Dbo6RPX11539nwiCNBb1icVPmw8= rsc.io/markdown v0.0.0-20240306144322-0bf8f97ee8ef/go.mod h1:8xcPgWmwlZONN1D9bjxtHEjrUtSEa3fakVF8iaewYKQ= diff --git a/src/cmd/go/alldocs.go b/src/cmd/go/alldocs.go index 20d76de0c742e1300e5c647a226d784d4525f34d..2220863b8edce482342bc2a568be16c82ee90cd9 100644 --- a/src/cmd/go/alldocs.go +++ b/src/cmd/go/alldocs.go @@ -2338,8 +2338,9 @@ // external go command build cache. // See 'go doc cmd/go/internal/cacheprog'. // GODEBUG -// Enable various debugging facilities. See https://go.dev/doc/godebug -// for details. +// Enable various debugging facilities for programs built with Go, +// including the go command. Cannot be set using 'go env -w'. +// See https://go.dev/doc/godebug for details. // GOENV // The location of the Go environment configuration file. // Cannot be set using 'go env -w'. @@ -2631,8 +2632,7 @@ // Content-Type: text/plain; charset=utf-8 // Date: Thu, 07 Nov 2024 18:43:09 GMT // -// Note: at least for HTTP 1.1, the contents written to stdin can be parsed -// as an HTTP response. +// Note: it is safe to use net/http.ReadResponse to parse this input. // // Before the first HTTPS fetch, the go command will invoke each GOAUTH // command in the list with no additional arguments and no input. diff --git a/src/cmd/go/internal/auth/auth.go b/src/cmd/go/internal/auth/auth.go index bd80222427ae36fec075c91ffceefcc6355d5c99..79e0d8b5e8f2f62e1e2c803e103cc43f69bea4a0 100644 --- a/src/cmd/go/internal/auth/auth.go +++ b/src/cmd/go/internal/auth/auth.go @@ -70,7 +70,8 @@ func runGoAuth(client *http.Client, res *http.Response, url string) { case "netrc": lines, err := readNetrc() if err != nil { - base.Fatalf("go: could not parse netrc (GOAUTH=%s): %v", cfg.GOAUTH, err) + cmdErrs = append(cmdErrs, fmt.Errorf("GOAUTH=%s: %v", command, err)) + continue } // Process lines in reverse so that if the same machine is listed // multiple times, we end up saving the earlier one diff --git a/src/cmd/go/internal/auth/httputils.go b/src/cmd/go/internal/auth/httputils.go new file mode 100644 index 0000000000000000000000000000000000000000..b8629546d5bf3e0239b32a7f8f08d237e0dbafbf --- /dev/null +++ b/src/cmd/go/internal/auth/httputils.go @@ -0,0 +1,173 @@ +// Copyright 2019 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. + +// Code copied from x/net/http/httpguts/httplex.go +package auth + +var isTokenTable = [256]bool{ + '!': true, + '#': true, + '$': true, + '%': true, + '&': true, + '\'': true, + '*': true, + '+': true, + '-': true, + '.': true, + '0': true, + '1': true, + '2': true, + '3': true, + '4': true, + '5': true, + '6': true, + '7': true, + '8': true, + '9': true, + 'A': true, + 'B': true, + 'C': true, + 'D': true, + 'E': true, + 'F': true, + 'G': true, + 'H': true, + 'I': true, + 'J': true, + 'K': true, + 'L': true, + 'M': true, + 'N': true, + 'O': true, + 'P': true, + 'Q': true, + 'R': true, + 'S': true, + 'T': true, + 'U': true, + 'W': true, + 'V': true, + 'X': true, + 'Y': true, + 'Z': true, + '^': true, + '_': true, + '`': true, + 'a': true, + 'b': true, + 'c': true, + 'd': true, + 'e': true, + 'f': true, + 'g': true, + 'h': true, + 'i': true, + 'j': true, + 'k': true, + 'l': true, + 'm': true, + 'n': true, + 'o': true, + 'p': true, + 'q': true, + 'r': true, + 's': true, + 't': true, + 'u': true, + 'v': true, + 'w': true, + 'x': true, + 'y': true, + 'z': true, + '|': true, + '~': true, +} + +// isLWS reports whether b is linear white space, according +// to http://www.w3.org/Protocols/rfc2616/rfc2616-sec2.html#sec2.2 +// +// LWS = [CRLF] 1*( SP | HT ) +func isLWS(b byte) bool { return b == ' ' || b == '\t' } + +// isCTL reports whether b is a control byte, according +// to http://www.w3.org/Protocols/rfc2616/rfc2616-sec2.html#sec2.2 +// +// CTL = <any US-ASCII control character +// (octets 0 - 31) and DEL (127)> +func isCTL(b byte) bool { + const del = 0x7f // a CTL + return b < ' ' || b == del +} + +// validHeaderFieldName reports whether v is a valid HTTP/1.x header name. +// HTTP/2 imposes the additional restriction that uppercase ASCII +// letters are not allowed. +// +// RFC 7230 says: +// +// header-field = field-name ":" OWS field-value OWS +// field-name = token +// token = 1*tchar +// tchar = "!" / "#" / "$" / "%" / "&" / "'" / "*" / "+" / "-" / "." / +// "^" / "_" / "`" / "|" / "~" / DIGIT / ALPHA +func validHeaderFieldName(v string) bool { + if len(v) == 0 { + return false + } + for i := 0; i < len(v); i++ { + if !isTokenTable[v[i]] { + return false + } + } + return true +} + +// validHeaderFieldValue reports whether v is a valid "field-value" according to +// http://www.w3.org/Protocols/rfc2616/rfc2616-sec4.html#sec4.2 : +// +// message-header = field-name ":" [ field-value ] +// field-value = *( field-content | LWS ) +// field-content = <the OCTETs making up the field-value +// and consisting of either *TEXT or combinations +// of token, separators, and quoted-string> +// +// http://www.w3.org/Protocols/rfc2616/rfc2616-sec2.html#sec2.2 : +// +// TEXT = <any OCTET except CTLs, +// but including LWS> +// LWS = [CRLF] 1*( SP | HT ) +// CTL = <any US-ASCII control character +// (octets 0 - 31) and DEL (127)> +// +// RFC 7230 says: +// +// field-value = *( field-content / obs-fold ) +// obj-fold = N/A to http2, and deprecated +// field-content = field-vchar [ 1*( SP / HTAB ) field-vchar ] +// field-vchar = VCHAR / obs-text +// obs-text = %x80-FF +// VCHAR = "any visible [USASCII] character" +// +// http2 further says: "Similarly, HTTP/2 allows header field values +// that are not valid. While most of the values that can be encoded +// will not alter header field parsing, carriage return (CR, ASCII +// 0xd), line feed (LF, ASCII 0xa), and the zero character (NUL, ASCII +// 0x0) might be exploited by an attacker if they are translated +// verbatim. Any request or response that contains a character not +// permitted in a header field value MUST be treated as malformed +// (Section 8.1.2.6). Valid characters are defined by the +// field-content ABNF rule in Section 3.2 of [RFC7230]." +// +// This function does not (yet?) properly handle the rejection of +// strings that begin or end with SP or HTAB. +func validHeaderFieldValue(v string) bool { + for i := 0; i < len(v); i++ { + b := v[i] + if isCTL(b) && !isLWS(b) { + return false + } + } + return true +} diff --git a/src/cmd/go/internal/auth/userauth.go b/src/cmd/go/internal/auth/userauth.go index 0e54a83e31873143bbe5dcdfa51acca0967f0901..1a60693a9cd9409c2b22c6c9e7053f99a04c53a4 100644 --- a/src/cmd/go/internal/auth/userauth.go +++ b/src/cmd/go/internal/auth/userauth.go @@ -6,14 +6,11 @@ package auth import ( - "bufio" - "bytes" "cmd/internal/quoted" "fmt" - "io" "maps" "net/http" - "net/textproto" + "net/url" "os/exec" "strings" ) @@ -42,7 +39,7 @@ func runAuthCommand(command string, url string, res *http.Response) (map[string] if err != nil { return nil, fmt.Errorf("could not run command %s: %v\n%s", command, err, cmd.Stderr) } - credentials, err := parseUserAuth(bytes.NewReader(out)) + credentials, err := parseUserAuth(string(out)) if err != nil { return nil, fmt.Errorf("cannot parse output of GOAUTH command %s: %v", command, err) } @@ -54,53 +51,47 @@ func runAuthCommand(command string, url string, res *http.Response) (map[string] // or an error if the data does not follow the expected format. // Returns an nil error and an empty map if the data is empty. // See the expected format in 'go help goauth'. -func parseUserAuth(data io.Reader) (map[string]http.Header, error) { +func parseUserAuth(data string) (map[string]http.Header, error) { credentials := make(map[string]http.Header) - reader := textproto.NewReader(bufio.NewReader(data)) - for { - // Return the processed credentials if the reader is at EOF. - if _, err := reader.R.Peek(1); err == io.EOF { - return credentials, nil + for data != "" { + var line string + var ok bool + var urls []string + // Parse URLS first. + for { + line, data, ok = strings.Cut(data, "\n") + if !ok { + return nil, fmt.Errorf("invalid format: missing empty line after URLs") + } + if line == "" { + break + } + u, err := url.ParseRequestURI(line) + if err != nil { + return nil, fmt.Errorf("could not parse URL %s: %v", line, err) + } + urls = append(urls, u.String()) } - urls, err := readURLs(reader) - if err != nil { - return nil, err - } - if len(urls) == 0 { - return nil, fmt.Errorf("invalid format: expected url prefix") - } - mimeHeader, err := reader.ReadMIMEHeader() - if err != nil { - return nil, err - } - header := http.Header(mimeHeader) - // Process the block (urls and headers). - credentialMap := mapHeadersToPrefixes(urls, header) - maps.Copy(credentials, credentialMap) - } -} - -// readURLs reads URL prefixes from the given reader until an empty line -// is encountered or an error occurs. It returns the list of URLs or an error -// if the format is invalid. -func readURLs(reader *textproto.Reader) (urls []string, err error) { - for { - line, err := reader.ReadLine() - if err != nil { - return nil, err - } - trimmedLine := strings.TrimSpace(line) - if trimmedLine != line { - return nil, fmt.Errorf("invalid format: leading or trailing white space") - } - if strings.HasPrefix(line, "https://") { - urls = append(urls, line) - } else if line == "" { - return urls, nil - } else { - return nil, fmt.Errorf("invalid format: expected url prefix or empty line") + // Parse Headers second. + header := make(http.Header) + for { + line, data, ok = strings.Cut(data, "\n") + if !ok { + return nil, fmt.Errorf("invalid format: missing empty line after headers") + } + if line == "" { + break + } + name, value, ok := strings.Cut(line, ": ") + value = strings.TrimSpace(value) + if !ok || !validHeaderFieldName(name) || !validHeaderFieldValue(value) { + return nil, fmt.Errorf("invalid format: invalid header line") + } + header.Add(name, value) } + maps.Copy(credentials, mapHeadersToPrefixes(urls, header)) } + return credentials, nil } // mapHeadersToPrefixes returns a mapping of prefix → http.Header without @@ -127,8 +118,8 @@ func buildCommand(command string) (*exec.Cmd, error) { func writeResponseToStdin(cmd *exec.Cmd, res *http.Response) error { var output strings.Builder output.WriteString(res.Proto + " " + res.Status + "\n") - if err := res.Header.Write(&output); err != nil { - return err + for k, v := range res.Header { + output.WriteString(k + ": " + strings.Join(v, ", ") + "\n") } output.WriteString("\n") cmd.Stdin = strings.NewReader(output.String()) diff --git a/src/cmd/go/internal/auth/userauth_test.go b/src/cmd/go/internal/auth/userauth_test.go index 91a5bb76ecd3bc1f72239d6f9d21559058e08c00..1b281ed3cdef45d2a9fb5c20cce2b935944d649e 100644 --- a/src/cmd/go/internal/auth/userauth_test.go +++ b/src/cmd/go/internal/auth/userauth_test.go @@ -7,7 +7,6 @@ package auth import ( "net/http" "reflect" - "strings" "testing" ) @@ -40,7 +39,7 @@ Data: Test567 "Test567", }, } - credentials, err := parseUserAuth(strings.NewReader(data)) + credentials, err := parseUserAuth(data) if err != nil { t.Errorf("parseUserAuth(%s): %v", data, err) } @@ -100,10 +99,55 @@ Authorization: Basic GVuc2VzYW1lYWxhZGRpbjpvc Authorization: Basic 1lYWxhZGRplW1lYWxhZGRpbs Data: Test567 +`, + // Continuation in URL line + `https://example.com/ + Authorization: Basic YWxhZGRpbjpvcGVuc2VzYW1l +`, + + // Continuation in header line + `https://example.com + +Authorization: Basic YWxhZGRpbjpvcGVuc2VzYW1l + Authorization: Basic jpvcGVuc2VzYW1lYWxhZGRpb +`, + + // Continuation in multiple header lines + `https://example.com + +Authorization: Basic YWxhZGRpbjpvcGVuc2VzYW1l + Authorization: Basic jpvcGVuc2VzYW1lYWxhZGRpb + Authorization: Basic dGhpc2lzYWxvbmdzdHJpbmc= +`, + + // Continuation with mixed spacing + `https://example.com + +Authorization: Basic YWxhZGRpbjpvcGVuc2VzYW1l + Authorization: Basic jpvcGVuc2VzYW1lYWxhZGRpb +`, + + // Continuation with tab character + `https://example.com + +Authorization: Basic YWxhZGRpbjpvcGVuc2VzYW1l + Authorization: Basic jpvcGVuc2VzYW1lYWxhZGRpb +`, + // Continuation at the start of a block + ` https://example.com + +Authorization: Basic YWxhZGRpbjpvcGVuc2VzYW1l +`, + + // Continuation after a blank line + `https://example.com + + +Authorization: Basic YWxhZGRpbjpvcGVuc2VzYW1l `, } for _, tc := range testCases { - if credentials, err := parseUserAuth(strings.NewReader(tc)); err == nil { + if credentials, err := parseUserAuth(tc); err == nil { t.Errorf("parseUserAuth(%s) should have failed, but got: %v", tc, credentials) } } @@ -132,7 +176,7 @@ Data: Test567 "Test567", }, } - credentials, err := parseUserAuth(strings.NewReader(data)) + credentials, err := parseUserAuth(data) if err != nil { t.Errorf("parseUserAuth(%s): %v", data, err) } @@ -146,7 +190,7 @@ func TestParseUserAuthEmptyHeader(t *testing.T) { data := "https://example.com\n\n\n" // Build the expected header header := http.Header{} - credentials, err := parseUserAuth(strings.NewReader(data)) + credentials, err := parseUserAuth(data) if err != nil { t.Errorf("parseUserAuth(%s): %v", data, err) } @@ -159,7 +203,7 @@ func TestParseUserAuthEmptyHeader(t *testing.T) { func TestParseUserAuthEmpty(t *testing.T) { data := `` // Build the expected header - credentials, err := parseUserAuth(strings.NewReader(data)) + credentials, err := parseUserAuth(data) if err != nil { t.Errorf("parseUserAuth(%s) should have succeeded", data) } diff --git a/src/cmd/go/internal/help/helpdoc.go b/src/cmd/go/internal/help/helpdoc.go index 65d0f1a45c0c17ef32b3ccd62a1f97d4ec39136d..ccc04c25d27d7e560a33960eb09c686d54d2b145 100644 --- a/src/cmd/go/internal/help/helpdoc.go +++ b/src/cmd/go/internal/help/helpdoc.go @@ -511,8 +511,9 @@ General-purpose environment variables: external go command build cache. See 'go doc cmd/go/internal/cacheprog'. GODEBUG - Enable various debugging facilities. See https://go.dev/doc/godebug - for details. + Enable various debugging facilities for programs built with Go, + including the go command. Cannot be set using 'go env -w'. + See https://go.dev/doc/godebug for details. GOENV The location of the Go environment configuration file. Cannot be set using 'go env -w'. @@ -1049,8 +1050,7 @@ command Content-Type: text/plain; charset=utf-8 Date: Thu, 07 Nov 2024 18:43:09 GMT - Note: at least for HTTP 1.1, the contents written to stdin can be parsed - as an HTTP response. + Note: it is safe to use net/http.ReadResponse to parse this input. Before the first HTTPS fetch, the go command will invoke each GOAUTH command in the list with no additional arguments and no input. diff --git a/src/cmd/go/internal/mmap/mmap_test.go b/src/cmd/go/internal/mmap/mmap_test.go new file mode 100644 index 0000000000000000000000000000000000000000..3f4b63caf19bd2a62ecc3bf59ed6992e9ba9825f --- /dev/null +++ b/src/cmd/go/internal/mmap/mmap_test.go @@ -0,0 +1,32 @@ +// 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. + +package mmap + +import ( + "bytes" + "testing" +) + +// TestMmap does a round trip to make sure the slice returned by +// mmap contains the same data as was written to the file. It's +// a test on one of the issues in #71059: on Windows we were +// returning a slice containing all the data in the mmapped pages, +// which could be longer than the file. +func TestMmap(t *testing.T) { + // Use an already existing file as our test data. Avoid creating + // a temporary file so that we don't have to close the mapping on + // Windows before deleting the file during test cleanup. + f := "testdata/small_file.txt" + + want := []byte("This file is shorter than 4096 bytes.\n") + + data, _, err := Mmap(f) + if err != nil { + t.Fatalf("calling Mmap: %v", err) + } + if !bytes.Equal(data.Data, want) { + t.Fatalf("mmapped data slice: got %q; want %q", data.Data, want) + } +} diff --git a/src/cmd/go/internal/mmap/mmap_windows.go b/src/cmd/go/internal/mmap/mmap_windows.go index d00bef71e5cfc5fbbbd5208b39baa56951e456a6..4163484b1a351026231cb378ef0c83b2b3a5133d 100644 --- a/src/cmd/go/internal/mmap/mmap_windows.go +++ b/src/cmd/go/internal/mmap/mmap_windows.go @@ -37,5 +37,11 @@ func mmapFile(f *os.File) (Data, error) { return Data{}, fmt.Errorf("VirtualQuery %s: %w", f.Name(), err) } data := unsafe.Slice((*byte)(unsafe.Pointer(addr)), int(info.RegionSize)) - return Data{f, data}, nil + if len(data) < int(size) { + // In some cases, especially on 386, we may not receive a in incomplete mapping: + // one that is shorter than the file itself. Return an error in those cases because + // incomplete mappings are not useful. + return Data{}, fmt.Errorf("mmapFile: received incomplete mapping of file") + } + return Data{f, data[:int(size)]}, nil } diff --git a/src/cmd/go/internal/mmap/testdata/small_file.txt b/src/cmd/go/internal/mmap/testdata/small_file.txt new file mode 100644 index 0000000000000000000000000000000000000000..10bb609f2ab33483fd59145634f8df156acf6dc3 --- /dev/null +++ b/src/cmd/go/internal/mmap/testdata/small_file.txt @@ -0,0 +1 @@ +This file is shorter than 4096 bytes. diff --git a/src/cmd/go/internal/modfetch/cache.go b/src/cmd/go/internal/modfetch/cache.go index 02d3849314a75a854ceac6a5c7d9fa0a0e9d48d9..9c34581a910a4ab7bfda024b4d5b649125b8e26f 100644 --- a/src/cmd/go/internal/modfetch/cache.go +++ b/src/cmd/go/internal/modfetch/cache.go @@ -113,6 +113,13 @@ func DownloadDir(ctx context.Context, m module.Version) (string, error) { return dir, err } + // Special case: ziphash is not required for the golang.org/fips140 module, + // because it is unpacked from a file in GOROOT, not downloaded. + // We've already checked that it's not a partial unpacking, so we're happy. + if m.Path == "golang.org/fips140" { + return dir, nil + } + // Check if a .ziphash file exists. It should be created before the // zip is extracted, but if it was deleted (by another program?), we need // to re-calculate it. Note that checkMod will repopulate the ziphash diff --git a/src/cmd/go/internal/modfetch/codehost/git.go b/src/cmd/go/internal/modfetch/codehost/git.go index 50a4526eb3ca64feed8864757a50e8234dee934c..dfb366788967f7c403e5021070d212ecefdea75d 100644 --- a/src/cmd/go/internal/modfetch/codehost/git.go +++ b/src/cmd/go/internal/modfetch/codehost/git.go @@ -649,7 +649,21 @@ func (r *gitRepo) statLocal(ctx context.Context, version, rev string) (*RevInfo, } } } - sort.Strings(info.Tags) + + // Git 2.47.1 does not send the tags during shallow clone anymore + // (perhaps the exact version that changed behavior is an earlier one), + // so we have to also add tags from the refs list we fetched with ls-remote. + if refs, err := r.loadRefs(ctx); err == nil { + for ref, h := range refs { + if h == hash { + if tag, found := strings.CutPrefix(ref, "refs/tags/"); found { + info.Tags = append(info.Tags, tag) + } + } + } + } + slices.Sort(info.Tags) + info.Tags = slices.Compact(info.Tags) // Used hash as info.Version above. // Use caller's suggested version if it appears in the tag list diff --git a/src/cmd/go/internal/modindex/read.go b/src/cmd/go/internal/modindex/read.go index 4c1fbd835961f65c0d4ac07fce3d4a8b2f63ee03..76216f35ba149f0815093b506516905d8fe0f9e9 100644 --- a/src/cmd/go/internal/modindex/read.go +++ b/src/cmd/go/internal/modindex/read.go @@ -33,10 +33,7 @@ import ( "cmd/internal/par" ) -// enabled is used to flag off the behavior of the module index on tip. -// It will be removed before the release. -// TODO(matloob): Remove enabled once we have more confidence on the -// module index. +// enabled is used to flag off the behavior of the module index on tip, for debugging. var enabled = godebug.New("#goindex").Value() != "0" // Module represents and encoded module index file. It is used to @@ -126,6 +123,7 @@ var ErrNotIndexed = errors.New("not in module index") var ( errDisabled = fmt.Errorf("%w: module indexing disabled", ErrNotIndexed) errNotFromModuleCache = fmt.Errorf("%w: not from module cache", ErrNotIndexed) + errFIPS140 = fmt.Errorf("%w: fips140 snapshots not indexed", ErrNotIndexed) ) // GetPackage returns the IndexPackage for the directory at the given path. @@ -143,6 +141,11 @@ func GetPackage(modroot, pkgdir string) (*IndexPackage, error) { if cfg.BuildContext.Compiler == "gccgo" && str.HasPathPrefix(modroot, cfg.GOROOTsrc) { return nil, err // gccgo has no sources for GOROOT packages. } + // The pkgdir for fips140 has been replaced in the fsys overlay, + // but the module index does not see that. Do not try to use the module index. + if strings.Contains(filepath.ToSlash(pkgdir), "internal/fips140/v") { + return nil, errFIPS140 + } return openIndexPackage(modroot, pkgdir) } diff --git a/src/cmd/go/internal/modload/modfile.go b/src/cmd/go/internal/modload/modfile.go index 94d2f5bd666cabc3bbff9188f8a6614afc3021f6..4687deae686c89b46a2cace590c3953dc00d7188 100644 --- a/src/cmd/go/internal/modload/modfile.go +++ b/src/cmd/go/internal/modload/modfile.go @@ -44,6 +44,17 @@ func ReadModFile(gomod string, fix modfile.VersionFixer) (data []byte, f *modfil f, err = modfile.Parse(gomod, data, fix) if err != nil { + f, laxErr := modfile.ParseLax(gomod, data, fix) + if laxErr == nil { + if f.Go != nil && gover.Compare(f.Go.Version, gover.Local()) > 0 { + toolchain := "" + if f.Toolchain != nil { + toolchain = f.Toolchain.Name + } + return nil, nil, &gover.TooNewError{What: base.ShortPath(gomod), GoVersion: f.Go.Version, Toolchain: toolchain} + } + } + // Errors returned by modfile.Parse begin with file:line. return nil, nil, fmt.Errorf("errors parsing %s:\n%w", base.ShortPath(gomod), shortPathErrorList(err)) } diff --git a/src/cmd/go/testdata/script/cover_coverprofile_nocoverpkg.txt b/src/cmd/go/testdata/script/cover_coverprofile_nocoverpkg.txt new file mode 100644 index 0000000000000000000000000000000000000000..85b3136bf994f6084139bc46012bf25db07b8077 --- /dev/null +++ b/src/cmd/go/testdata/script/cover_coverprofile_nocoverpkg.txt @@ -0,0 +1,50 @@ +# Testcase for #70244. In this bug we're doing a "go test -coverprofile" +# run for a pair of packages, the first one without tests and the second +# one with tests. When writing the profile for the second test, profile +# data from the first package was leaking into the output (we should +# only see lines in the output profile for the package whose test is +# being run). + +[short] skip + +# Kick off test. +go test -vet=off -count=1 -coverprofile=cov.p ./... + +# Generate a function profile. +go tool cover -func=cov.p + +# Prior to GOEXPERIMENT=coverageredesign we should see no output at all for +# pkg1 (since it has no tests). +[!GOEXPERIMENT:coverageredesign] ! stdout 'pkg1' + +# With GOEXPERIMENT=coverageredesign enabled we should see zero percent +# coverage for pkg1's DoSomething, not 100% (as in the bug). +[GOEXPERIMENT:coverageredesign] stdout 'cov/pkg1/file.go:3:\s+DoSomething\s+0.0%' + +-- go.mod -- +module cov + +-- pkg1/file.go -- +package pkg1 + +func DoSomething() bool { + return true +} +-- pkg2/file.go -- +package pkg2 + +func DoSomething() bool { + return true +} +-- pkg2/file_test.go -- +package pkg2 + +import ( + "cov/pkg1" + "testing" +) + +func TestSmth(t *testing.T) { + pkg1.DoSomething() + DoSomething() +} diff --git a/src/cmd/go/testdata/script/fipssnap.txt b/src/cmd/go/testdata/script/fipssnap.txt index 465f304c46c126cf83d151b132feb380fe4cfb29..9888bc82f11d81ed3506d46c4eb7ff9023ad165a 100644 --- a/src/cmd/go/testdata/script/fipssnap.txt +++ b/src/cmd/go/testdata/script/fipssnap.txt @@ -1,10 +1,6 @@ -## Note: Need a snapshot in lib/fips140 to run this test. -## For local testing, can run 'cd lib/fips140; make v0.0.1.test' -## and then remove the skip. -env snap=v0.0.1 +env snap=v1.0.0 env alias=inprocess -skip 'no snapshots yet' env GOFIPS140=$snap # Go+BoringCrypto conflicts with GOFIPS140. @@ -27,7 +23,8 @@ stdout crypto/internal/fips140/$snap/sha256 ! stdout crypto/internal/fips140/check # again with GOFIPS140=$alias -env GOFIPS140=$alias +# TODO: enable when we add inprocess.txt +# env GOFIPS140=$alias # default GODEBUG includes fips140=on go list -f '{{.DefaultGODEBUG}}' diff --git a/src/cmd/go/testdata/script/goauth_netrc.txt b/src/cmd/go/testdata/script/goauth_netrc.txt index 26e03f8968c5b301f8249d62900053ecaf318553..0baa09de1ecaf9cf559531111b9677996b2d8328 100644 --- a/src/cmd/go/testdata/script/goauth_netrc.txt +++ b/src/cmd/go/testdata/script/goauth_netrc.txt @@ -53,6 +53,19 @@ go get vcs-test.golang.org/auth/or401 env NETRC=$WORK/missing ! go get vcs-test.golang.org/auth/or401 stderr '^\tserver response: ACCESS DENIED, buddy$' + +[short] skip 'requires a remote vcs lookup' +[!git] skip +# An unset home directory should warn the user but not cause a failure. +env NETRC= +env HOME= +env USERPROFILE= +env home= +go get -x vcs-test.golang.org/git/emptytest.git +[!GOOS:windows] [!GOOS:plan9] stderr 'GOAUTH=netrc: \$HOME is not defined' +[GOOS:windows] stderr 'GOAUTH=netrc: \%userprofile\% is not defined' +[GOOS:plan9] stderr 'GOAUTH=netrc: \$home is not defined' + -- go.mod -- module private.example.com -- $WORK/empty -- diff --git a/src/cmd/go/testdata/script/mod_unknown_block.txt b/src/cmd/go/testdata/script/mod_unknown_block.txt new file mode 100644 index 0000000000000000000000000000000000000000..071269bb8db0ea436871af86be3da466db024ae4 --- /dev/null +++ b/src/cmd/go/testdata/script/mod_unknown_block.txt @@ -0,0 +1,11 @@ +env GOTOOLCHAIN=local +! go list . +stderr 'go: go.mod requires go >= 1.999' + + +-- go.mod -- +module example.com + +go 1.999 + +anewblock foo diff --git a/src/cmd/internal/goobj/builtinlist.go b/src/cmd/internal/goobj/builtinlist.go index c133c60427f59855c281e658d53dfaebb98f61ae..3e550d8dd9763b8f3df6e881f974ecb3148fc872 100644 --- a/src/cmd/internal/goobj/builtinlist.go +++ b/src/cmd/internal/goobj/builtinlist.go @@ -110,11 +110,13 @@ var builtins = [...]struct { {"runtime.mapassign_fast64ptr", 1}, {"runtime.mapassign_faststr", 1}, {"runtime.mapiterinit", 1}, + {"runtime.mapIterStart", 1}, {"runtime.mapdelete", 1}, {"runtime.mapdelete_fast32", 1}, {"runtime.mapdelete_fast64", 1}, {"runtime.mapdelete_faststr", 1}, {"runtime.mapiternext", 1}, + {"runtime.mapIterNext", 1}, {"runtime.mapclear", 1}, {"runtime.makechan64", 1}, {"runtime.makechan", 1}, diff --git a/src/cmd/internal/obj/wasm/wasmobj.go b/src/cmd/internal/obj/wasm/wasmobj.go index 48eee4e3ea12de20f887ca22cbb058cd5de3b37f..42e5534f3b6254cc31960c0f92c30b6be76db4b7 100644 --- a/src/cmd/internal/obj/wasm/wasmobj.go +++ b/src/cmd/internal/obj/wasm/wasmobj.go @@ -129,6 +129,7 @@ var ( morestackNoCtxt *obj.LSym sigpanic *obj.LSym wasm_pc_f_loop_export *obj.LSym + runtimeNotInitialized *obj.LSym ) const ( @@ -149,6 +150,7 @@ func instinit(ctxt *obj.Link) { morestackNoCtxt = ctxt.Lookup("runtime.morestack_noctxt") sigpanic = ctxt.LookupABI("runtime.sigpanic", obj.ABIInternal) wasm_pc_f_loop_export = ctxt.Lookup("wasm_pc_f_loop_export") + runtimeNotInitialized = ctxt.Lookup("runtime.notInitialized") } func preprocess(ctxt *obj.Link, s *obj.LSym, newprog obj.ProgAlloc) { @@ -255,7 +257,7 @@ func preprocess(ctxt *obj.Link, s *obj.LSym, newprog obj.ProgAlloc) { p = appendp(p, AEnd) } - if framesize > 0 { + if framesize > 0 && s.Func().WasmExport == nil { // genWasmExportWrapper has its own prologue generation p := s.Func().Text p = appendp(p, AGet, regAddr(REG_SP)) p = appendp(p, AI32Const, constAddr(framesize)) @@ -935,6 +937,23 @@ func genWasmExportWrapper(s *obj.LSym, appendp func(p *obj.Prog, as obj.As, args panic("wrapper functions for WASM export should not have a body") } + // Detect and error out if called before runtime initialization + // SP is 0 if not initialized + p = appendp(p, AGet, regAddr(REG_SP)) + p = appendp(p, AI32Eqz) + p = appendp(p, AIf) + p = appendp(p, ACall, obj.Addr{Type: obj.TYPE_MEM, Name: obj.NAME_EXTERN, Sym: runtimeNotInitialized}) + p = appendp(p, AEnd) + + // Now that we've checked the SP, generate the prologue + if framesize > 0 { + p = appendp(p, AGet, regAddr(REG_SP)) + p = appendp(p, AI32Const, constAddr(framesize)) + p = appendp(p, AI32Sub) + p = appendp(p, ASet, regAddr(REG_SP)) + p.Spadj = int32(framesize) + } + // Store args for i, f := range we.Params { p = appendp(p, AGet, regAddr(REG_SP)) @@ -1056,6 +1075,7 @@ var notUsePC_B = map[string]bool{ "runtime.gcWriteBarrier6": true, "runtime.gcWriteBarrier7": true, "runtime.gcWriteBarrier8": true, + "runtime.notInitialized": true, "runtime.wasmDiv": true, "runtime.wasmTruncS": true, "runtime.wasmTruncU": true, @@ -1121,7 +1141,8 @@ func assemble(ctxt *obj.Link, s *obj.LSym, newprog obj.ProgAlloc) { "runtime.gcWriteBarrier5", "runtime.gcWriteBarrier6", "runtime.gcWriteBarrier7", - "runtime.gcWriteBarrier8": + "runtime.gcWriteBarrier8", + "runtime.notInitialized": // no locals useAssemblyRegMap() default: diff --git a/src/cmd/link/internal/loader/loader.go b/src/cmd/link/internal/loader/loader.go index e7cc30ab0731c2a12774c513dbbe809acb41abfc..0c234e89758df32960660194784fcad0367fe4dd 100644 --- a/src/cmd/link/internal/loader/loader.go +++ b/src/cmd/link/internal/loader/loader.go @@ -2394,6 +2394,16 @@ func (l *Loader) checkLinkname(pkg, name string, s Sym) { if pkg == p { return // pkg is allowed } + // crypto/internal/fips140/vX.Y.Z/... is the frozen version of + // crypto/internal/fips140/... and is similarly allowed. + if strings.HasPrefix(pkg, "crypto/internal/fips140/v") { + parts := strings.Split(pkg, "/") + parts = append(parts[:3], parts[4:]...) + pkg := strings.Join(parts, "/") + if pkg == p { + return + } + } } error() } diff --git a/src/cmd/link/internal/wasm/asm.go b/src/cmd/link/internal/wasm/asm.go index 727da59da630d44a62f80a35212cecc9c3efaa19..d03102cc6be629cc1cbeb13e709bc7cc6ca5ab9a 100644 --- a/src/cmd/link/internal/wasm/asm.go +++ b/src/cmd/link/internal/wasm/asm.go @@ -87,6 +87,7 @@ var wasmFuncTypes = map[string]*wasmFuncType{ "runtime.gcWriteBarrier6": {Results: []byte{I64}}, // -> bufptr "runtime.gcWriteBarrier7": {Results: []byte{I64}}, // -> bufptr "runtime.gcWriteBarrier8": {Results: []byte{I64}}, // -> bufptr + "runtime.notInitialized": {}, // "cmpbody": {Params: []byte{I64, I64, I64, I64}, Results: []byte{I64}}, // a, alen, b, blen -> -1/0/1 "memeqbody": {Params: []byte{I64, I64, I64}, Results: []byte{I64}}, // a, b, len -> 0/1 "memcmp": {Params: []byte{I32, I32, I32}, Results: []byte{I32}}, // a, b, len -> <0/0/>0 diff --git a/src/cmd/vendor/golang.org/x/tools/go/analysis/passes/printf/printf.go b/src/cmd/vendor/golang.org/x/tools/go/analysis/passes/printf/printf.go index 171ad2013722c418c88098d99a123df60c018040..011ea8bef629256a21f87a6ead7ad394962653bb 100644 --- a/src/cmd/vendor/golang.org/x/tools/go/analysis/passes/printf/printf.go +++ b/src/cmd/vendor/golang.org/x/tools/go/analysis/passes/printf/printf.go @@ -25,6 +25,7 @@ import ( "golang.org/x/tools/go/ast/inspector" "golang.org/x/tools/go/types/typeutil" "golang.org/x/tools/internal/typeparams" + "golang.org/x/tools/internal/versions" ) func init() { @@ -108,12 +109,12 @@ func (f *isWrapper) String() string { } } -func run(pass *analysis.Pass) (interface{}, error) { +func run(pass *analysis.Pass) (any, error) { res := &Result{ funcs: make(map[*types.Func]Kind), } findPrintfLike(pass, res) - checkCall(pass) + checkCalls(pass) return res, nil } @@ -182,7 +183,7 @@ func maybePrintfWrapper(info *types.Info, decl ast.Decl) *printfWrapper { } // findPrintfLike scans the entire package to find printf-like functions. -func findPrintfLike(pass *analysis.Pass, res *Result) (interface{}, error) { +func findPrintfLike(pass *analysis.Pass, res *Result) (any, error) { // Gather potential wrappers and call graph between them. byObj := make(map[*types.Func]*printfWrapper) var wrappers []*printfWrapper @@ -409,20 +410,29 @@ func stringConstantExpr(pass *analysis.Pass, expr ast.Expr) (string, bool) { return "", false } -// checkCall triggers the print-specific checks if the call invokes a print function. -func checkCall(pass *analysis.Pass) { +// checkCalls triggers the print-specific checks for calls that invoke a print +// function. +func checkCalls(pass *analysis.Pass) { inspect := pass.ResultOf[inspect.Analyzer].(*inspector.Inspector) nodeFilter := []ast.Node{ + (*ast.File)(nil), (*ast.CallExpr)(nil), } + + var fileVersion string // for selectively suppressing checks; "" if unknown. inspect.Preorder(nodeFilter, func(n ast.Node) { - call := n.(*ast.CallExpr) - fn, kind := printfNameAndKind(pass, call) - switch kind { - case KindPrintf, KindErrorf: - checkPrintf(pass, kind, call, fn) - case KindPrint: - checkPrint(pass, call, fn) + switch n := n.(type) { + case *ast.File: + fileVersion = versions.Lang(versions.FileVersion(pass.TypesInfo, n)) + + case *ast.CallExpr: + fn, kind := printfNameAndKind(pass, n) + switch kind { + case KindPrintf, KindErrorf: + checkPrintf(pass, fileVersion, kind, n, fn) + case KindPrint: + checkPrint(pass, n, fn) + } } }) } @@ -503,7 +513,7 @@ type formatState struct { } // checkPrintf checks a call to a formatted print routine such as Printf. -func checkPrintf(pass *analysis.Pass, kind Kind, call *ast.CallExpr, fn *types.Func) { +func checkPrintf(pass *analysis.Pass, fileVersion string, kind Kind, call *ast.CallExpr, fn *types.Func) { idx := formatStringIndex(pass, call) if idx < 0 || idx >= len(call.Args) { return @@ -517,7 +527,17 @@ func checkPrintf(pass *analysis.Pass, kind Kind, call *ast.CallExpr, fn *types.F // non-constant format string and no arguments: // if msg contains "%", misformatting occurs. // Report the problem and suggest a fix: fmt.Printf("%s", msg). - if !suppressNonconstants && idx == len(call.Args)-1 { + // + // However, as described in golang/go#71485, this analysis can produce a + // significant number of diagnostics in existing code, and the bugs it + // finds are sometimes unlikely or inconsequential, and may not be worth + // fixing for some users. Gating on language version allows us to avoid + // breaking existing tests and CI scripts. + if !suppressNonconstants && + idx == len(call.Args)-1 && + fileVersion != "" && // fail open + versions.AtLeast(fileVersion, "go1.24") { + pass.Report(analysis.Diagnostic{ Pos: formatArg.Pos(), End: formatArg.End(), diff --git a/src/cmd/vendor/modules.txt b/src/cmd/vendor/modules.txt index 281989b1e2c5c2b0aff42c21614621e7cd4a8e4b..118646d75c4ccbe6ea3ac4f2771bb55cfb48daab 100644 --- a/src/cmd/vendor/modules.txt +++ b/src/cmd/vendor/modules.txt @@ -73,7 +73,7 @@ golang.org/x/text/internal/tag golang.org/x/text/language golang.org/x/text/transform golang.org/x/text/unicode/norm -# golang.org/x/tools v0.28.0 +# golang.org/x/tools v0.28.1-0.20250131145412-98746475647e ## explicit; go 1.22.0 golang.org/x/tools/cmd/bisect golang.org/x/tools/cover diff --git a/src/crypto/ecdsa/ecdsa.go b/src/crypto/ecdsa/ecdsa.go index f682e6b1c6cfa6f6c9a294f5fee5f87ed1337dfb..cb308b41e9df86015a53cb2d7c381b15ab8a2415 100644 --- a/src/crypto/ecdsa/ecdsa.go +++ b/src/crypto/ecdsa/ecdsa.go @@ -23,6 +23,7 @@ import ( "crypto/internal/boring" "crypto/internal/boring/bbig" "crypto/internal/fips140/ecdsa" + "crypto/internal/fips140hash" "crypto/internal/fips140only" "crypto/internal/randutil" "crypto/sha512" @@ -281,7 +282,11 @@ func signFIPSDeterministic[P ecdsa.Point[P]](c *ecdsa.Curve[P], hashFunc crypto. if err != nil { return nil, err } - sig, err := ecdsa.SignDeterministic(c, hashFunc.New, k, hash) + h := fips140hash.UnwrapNew(hashFunc.New) + if fips140only.Enabled && !fips140only.ApprovedHash(h()) { + return nil, errors.New("crypto/ecdsa: use of hash functions other than SHA-2 or SHA-3 is not allowed in FIPS 140-only mode") + } + sig, err := ecdsa.SignDeterministic(c, h, k, hash) if err != nil { return nil, err } diff --git a/src/crypto/hkdf/hkdf.go b/src/crypto/hkdf/hkdf.go index 7cfbe2c60de3568e1cc7545080dcdbe4843780ce..6b02522866d57fb66bb9755c976345082b12dbc5 100644 --- a/src/crypto/hkdf/hkdf.go +++ b/src/crypto/hkdf/hkdf.go @@ -12,6 +12,7 @@ package hkdf import ( "crypto/internal/fips140/hkdf" + "crypto/internal/fips140hash" "crypto/internal/fips140only" "errors" "hash" @@ -24,10 +25,11 @@ import ( // Expand invocations and different context values. Most common scenarios, // including the generation of multiple keys, should use [Key] instead. func Extract[H hash.Hash](h func() H, secret, salt []byte) ([]byte, error) { - if err := checkFIPS140Only(h, secret); err != nil { + fh := fips140hash.UnwrapNew(h) + if err := checkFIPS140Only(fh, secret); err != nil { return nil, err } - return hkdf.Extract(h, secret, salt), nil + return hkdf.Extract(fh, secret, salt), nil } // Expand derives a key from the given hash, key, and optional context info, @@ -38,35 +40,37 @@ func Extract[H hash.Hash](h func() H, secret, salt []byte) ([]byte, error) { // random or pseudorandom cryptographically strong key. See RFC 5869, Section // 3.3. Most common scenarios will want to use [Key] instead. func Expand[H hash.Hash](h func() H, pseudorandomKey []byte, info string, keyLength int) ([]byte, error) { - if err := checkFIPS140Only(h, pseudorandomKey); err != nil { + fh := fips140hash.UnwrapNew(h) + if err := checkFIPS140Only(fh, pseudorandomKey); err != nil { return nil, err } - limit := h().Size() * 255 + limit := fh().Size() * 255 if keyLength > limit { return nil, errors.New("hkdf: requested key length too large") } - return hkdf.Expand(h, pseudorandomKey, info, keyLength), nil + return hkdf.Expand(fh, pseudorandomKey, info, keyLength), nil } // Key derives a key from the given hash, secret, salt and context info, // returning a []byte of length keyLength that can be used as cryptographic key. // Salt and info can be nil. func Key[Hash hash.Hash](h func() Hash, secret, salt []byte, info string, keyLength int) ([]byte, error) { - if err := checkFIPS140Only(h, secret); err != nil { + fh := fips140hash.UnwrapNew(h) + if err := checkFIPS140Only(fh, secret); err != nil { return nil, err } - limit := h().Size() * 255 + limit := fh().Size() * 255 if keyLength > limit { return nil, errors.New("hkdf: requested key length too large") } - return hkdf.Key(h, secret, salt, info, keyLength), nil + return hkdf.Key(fh, secret, salt, info, keyLength), nil } -func checkFIPS140Only[H hash.Hash](h func() H, key []byte) error { +func checkFIPS140Only[Hash hash.Hash](h func() Hash, key []byte) error { if !fips140only.Enabled { return nil } diff --git a/src/crypto/hkdf/hkdf_test.go b/src/crypto/hkdf/hkdf_test.go index 201b440289bb2d4aef6f2bf1965d19941afaa037..57d90f88e93e758d15444f41ae78c578fbffacb8 100644 --- a/src/crypto/hkdf/hkdf_test.go +++ b/src/crypto/hkdf/hkdf_test.go @@ -404,6 +404,9 @@ func TestFIPSServiceIndicator(t *testing.T) { // Salt and info are short, which is ok, but translates to a short HMAC key. fips140.ResetServiceIndicator() _, err = Key(sha256.New, []byte("YELLOW SUBMARINE"), []byte("salt"), "info", 32) + if err != nil { + panic(err) + } if !fips140.ServiceIndicator() { t.Error("FIPS service indicator should be set") } diff --git a/src/crypto/hmac/hmac.go b/src/crypto/hmac/hmac.go index 72f5a4abea9d359fd7ee66efd7e59acc43e493b3..554c8c9b78940bef180649b8e5f18278f8c788fc 100644 --- a/src/crypto/hmac/hmac.go +++ b/src/crypto/hmac/hmac.go @@ -24,6 +24,7 @@ package hmac import ( "crypto/internal/boring" "crypto/internal/fips140/hmac" + "crypto/internal/fips140hash" "crypto/internal/fips140only" "crypto/subtle" "hash" @@ -43,6 +44,7 @@ func New(h func() hash.Hash, key []byte) hash.Hash { } // BoringCrypto did not recognize h, so fall through to standard Go code. } + h = fips140hash.UnwrapNew(h) if fips140only.Enabled { if len(key) < 112/8 { panic("crypto/hmac: use of keys shorter than 112 bits is not allowed in FIPS 140-only mode") diff --git a/src/crypto/internal/boring/ecdh.go b/src/crypto/internal/boring/ecdh.go index b90e533e7cc38c367643beb1195be8a067c18f12..ff29eb17b1134482bc5bb4911bc4b737d888b506 100644 --- a/src/crypto/internal/boring/ecdh.go +++ b/src/crypto/internal/boring/ecdh.go @@ -138,6 +138,15 @@ func pointBytesECDH(curve string, group *C.GO_EC_GROUP, pt *C.GO_EC_POINT) ([]by } func ECDH(priv *PrivateKeyECDH, pub *PublicKeyECDH) ([]byte, error) { + // Make sure priv and pub are not garbage collected while we are in a cgo + // call. + // + // The call to xCoordBytesECDH should prevent priv from being collected, but + // include this in case the code is reordered and there is a subsequent call + // cgo call after that point. + defer runtime.KeepAlive(priv) + defer runtime.KeepAlive(pub) + group := C._goboringcrypto_EC_KEY_get0_group(priv.key) if group == nil { return nil, fail("EC_KEY_get0_group") diff --git a/src/crypto/internal/fips140/aes/cbc.go b/src/crypto/internal/fips140/aes/cbc.go index f92af23a2a55619a09324bc1ad6c0e44d3269f0e..a5a079453f7f4ca7ababa9be765388d70e3b29b0 100644 --- a/src/crypto/internal/fips140/aes/cbc.go +++ b/src/crypto/internal/fips140/aes/cbc.go @@ -5,6 +5,7 @@ package aes import ( + "crypto/internal/fips140" "crypto/internal/fips140/alias" "crypto/internal/fips140/subtle" ) @@ -32,6 +33,7 @@ func (c *CBCEncrypter) CryptBlocks(dst, src []byte) { if alias.InexactOverlap(dst[:len(src)], src) { panic("crypto/cipher: invalid buffer overlap") } + fips140.RecordApproved() if len(src) == 0 { return } @@ -85,6 +87,7 @@ func (c *CBCDecrypter) CryptBlocks(dst, src []byte) { if alias.InexactOverlap(dst[:len(src)], src) { panic("crypto/cipher: invalid buffer overlap") } + fips140.RecordApproved() if len(src) == 0 { return } diff --git a/src/crypto/internal/fips140/aes/ctr.go b/src/crypto/internal/fips140/aes/ctr.go index 2b0ee44cddb66c6d17ded21377a255d9715cee34..2e55d233d3a7678e44bda345832a4936daca1a8e 100644 --- a/src/crypto/internal/fips140/aes/ctr.go +++ b/src/crypto/internal/fips140/aes/ctr.go @@ -5,6 +5,7 @@ package aes import ( + "crypto/internal/fips140" "crypto/internal/fips140/alias" "crypto/internal/fips140/subtle" "crypto/internal/fips140deps/byteorder" @@ -71,6 +72,7 @@ func (c *CTR) XORKeyStreamAt(dst, src []byte, offset uint64) { if alias.InexactOverlap(dst, src) { panic("crypto/aes: invalid buffer overlap") } + fips140.RecordApproved() ivlo, ivhi := add128(c.ivlo, c.ivhi, offset/BlockSize) diff --git a/src/crypto/internal/fips140/ecdsa/ecdsa_s390x.go b/src/crypto/internal/fips140/ecdsa/ecdsa_s390x.go index 271a35897f8c8fccb2b3b6b9b682fd9aa22b3da3..d0a49cad610c251804ba2e9aa194b302a5a3bbd4 100644 --- a/src/crypto/internal/fips140/ecdsa/ecdsa_s390x.go +++ b/src/crypto/internal/fips140/ecdsa/ecdsa_s390x.go @@ -59,6 +59,25 @@ func hashToBytes[P Point[P]](c *Curve[P], hash []byte) []byte { return e.Bytes(c.N) } +// randomScalar is a copy of [randomPoint] that doesn't call ScalarBaseMult. +func randomScalar[P Point[P]](c *Curve[P], generate func([]byte) error) (k *bigmod.Nat, err error) { + for { + b := make([]byte, c.N.Size()) + if err := generate(b); err != nil { + return nil, err + } + if excess := len(b)*8 - c.N.BitLen(); excess > 0 { + if c.curve != p521 { + panic("ecdsa: internal error: unexpectedly masking off bits") + } + b = rightShift(b, excess) + } + if k, err := bigmod.NewNat().SetBytes(b, c.N); err == nil && k.IsZero() == 0 { + return k, nil + } + } +} + func appendBlock(p []byte, blocksize int, b []byte) []byte { if len(b) > blocksize { panic("ecdsa: internal error: appendBlock input larger than block") @@ -83,7 +102,7 @@ func sign[P Point[P]](c *Curve[P], priv *PrivateKey, drbg *hmacDRBG, hash []byte return signGeneric(c, priv, drbg, hash) } for { - k, _, err := randomPoint(c, func(b []byte) error { + k, err := randomScalar(c, func(b []byte) error { drbg.Generate(b) return nil }) diff --git a/src/crypto/internal/fips140/ecdsa/hmacdrbg.go b/src/crypto/internal/fips140/ecdsa/hmacdrbg.go index 4f085e2801b79dab307f831b34ec4d19f78fc299..8f52091170249256a70ec9a78f035c1f5e4fb46f 100644 --- a/src/crypto/internal/fips140/ecdsa/hmacdrbg.go +++ b/src/crypto/internal/fips140/ecdsa/hmacdrbg.go @@ -116,6 +116,15 @@ func newDRBG[H fips140.Hash](hash func() H, entropy, nonce []byte, s personaliza return d } +// TestingOnlyNewDRBG creates an SP 800-90A Rev. 1 HMAC_DRBG with a plain +// personalization string. +// +// This should only be used for ACVP testing. hmacDRBG is not intended to be +// used directly. +func TestingOnlyNewDRBG(hash func() fips140.Hash, entropy, nonce []byte, s []byte) *hmacDRBG { + return newDRBG(hash, entropy, nonce, plainPersonalizationString(s)) +} + func pad000(h *hmac.HMAC, writtenSoFar int) { blockSize := h.BlockSize() if rem := writtenSoFar % blockSize; rem != 0 { diff --git a/src/crypto/internal/fips140/mlkem/generate1024.go b/src/crypto/internal/fips140/mlkem/generate1024.go index e002bf1414ae966b8e8506db2cfffcb760a40cb9..9e38ad00df99e0fa356b682d34ea0407531ade01 100644 --- a/src/crypto/internal/fips140/mlkem/generate1024.go +++ b/src/crypto/internal/fips140/mlkem/generate1024.go @@ -22,6 +22,7 @@ var replacements = map[string]string{ "CiphertextSize768": "CiphertextSize1024", "EncapsulationKeySize768": "EncapsulationKeySize1024", + "decapsulationKeySize768": "decapsulationKeySize1024", "encryptionKey": "encryptionKey1024", "decryptionKey": "decryptionKey1024", @@ -33,9 +34,11 @@ var replacements = map[string]string{ "kemEncaps": "kemEncaps1024", "pkeEncrypt": "pkeEncrypt1024", - "DecapsulationKey768": "DecapsulationKey1024", - "NewDecapsulationKey768": "NewDecapsulationKey1024", - "newKeyFromSeed": "newKeyFromSeed1024", + "DecapsulationKey768": "DecapsulationKey1024", + "NewDecapsulationKey768": "NewDecapsulationKey1024", + "TestingOnlyNewDecapsulationKey768": "TestingOnlyNewDecapsulationKey1024", + "newKeyFromSeed": "newKeyFromSeed1024", + "TestingOnlyExpandedBytes768": "TestingOnlyExpandedBytes1024", "kemDecaps": "kemDecaps1024", "pkeDecrypt": "pkeDecrypt1024", diff --git a/src/crypto/internal/fips140/mlkem/mlkem1024.go b/src/crypto/internal/fips140/mlkem/mlkem1024.go index c924c382933c5f19bda70a8512ce78cc80a79fb8..034bf3b5d6682d3164d4290437529ad2eae5d590 100644 --- a/src/crypto/internal/fips140/mlkem/mlkem1024.go +++ b/src/crypto/internal/fips140/mlkem/mlkem1024.go @@ -3,6 +3,7 @@ package mlkem import ( + "bytes" "crypto/internal/fips140" "crypto/internal/fips140/drbg" "crypto/internal/fips140/sha3" @@ -33,6 +34,32 @@ func (dk *DecapsulationKey1024) Bytes() []byte { return b[:] } +// TestingOnlyExpandedBytes1024 returns the decapsulation key as a byte slice +// using the full expanded NIST encoding. +// +// This should only be used for ACVP testing. For all other purposes prefer +// the Bytes method that returns the (much smaller) seed. +func TestingOnlyExpandedBytes1024(dk *DecapsulationKey1024) []byte { + b := make([]byte, 0, decapsulationKeySize1024) + + // ByteEncode₁₂(s) + for i := range dk.s { + b = polyByteEncode(b, dk.s[i]) + } + + // ByteEncode₁₂(t) || ρ + for i := range dk.t { + b = polyByteEncode(b, dk.t[i]) + } + b = append(b, dk.ρ[:]...) + + // H(ek) || z + b = append(b, dk.h[:]...) + b = append(b, dk.z[:]...) + + return b +} + // EncapsulationKey returns the public encapsulation key necessary to produce // ciphertexts. func (dk *DecapsulationKey1024) EncapsulationKey() *EncapsulationKey1024 { @@ -130,6 +157,53 @@ func newKeyFromSeed1024(dk *DecapsulationKey1024, seed []byte) (*DecapsulationKe return dk, nil } +// TestingOnlyNewDecapsulationKey1024 parses a decapsulation key from its expanded NIST format. +// +// Bytes() must not be called on the returned key, as it will not produce the +// original seed. +// +// This function should only be used for ACVP testing. Prefer NewDecapsulationKey1024 for all +// other purposes. +func TestingOnlyNewDecapsulationKey1024(b []byte) (*DecapsulationKey1024, error) { + if len(b) != decapsulationKeySize1024 { + return nil, errors.New("mlkem: invalid NIST decapsulation key length") + } + + dk := &DecapsulationKey1024{} + for i := range dk.s { + var err error + dk.s[i], err = polyByteDecode[nttElement](b[:encodingSize12]) + if err != nil { + return nil, errors.New("mlkem: invalid secret key encoding") + } + b = b[encodingSize12:] + } + + ek, err := NewEncapsulationKey1024(b[:EncapsulationKeySize1024]) + if err != nil { + return nil, err + } + dk.ρ = ek.ρ + dk.h = ek.h + dk.encryptionKey1024 = ek.encryptionKey1024 + b = b[EncapsulationKeySize1024:] + + if !bytes.Equal(dk.h[:], b[:32]) { + return nil, errors.New("mlkem: inconsistent H(ek) in encoded bytes") + } + b = b[32:] + + copy(dk.z[:], b) + + // Generate a random d value for use in Bytes(). This is a safety mechanism + // that avoids returning a broken key vs a random key if this function is + // called in contravention of the TestingOnlyNewDecapsulationKey1024 function + // comment advising against it. + drbg.Read(dk.d[:]) + + return dk, nil +} + // kemKeyGen1024 generates a decapsulation key. // // It implements ML-KEM.KeyGen_internal according to FIPS 203, Algorithm 16, and diff --git a/src/crypto/internal/fips140/mlkem/mlkem768.go b/src/crypto/internal/fips140/mlkem/mlkem768.go index 2c1cb5c33fcdd110ea13a894dca1a10e59418a62..77043830d4d962e2162011e09311f482a370210e 100644 --- a/src/crypto/internal/fips140/mlkem/mlkem768.go +++ b/src/crypto/internal/fips140/mlkem/mlkem768.go @@ -24,6 +24,7 @@ package mlkem //go:generate go run generate1024.go -input mlkem768.go -output mlkem1024.go import ( + "bytes" "crypto/internal/fips140" "crypto/internal/fips140/drbg" "crypto/internal/fips140/sha3" @@ -57,6 +58,7 @@ const ( CiphertextSize768 = k*encodingSize10 + encodingSize4 EncapsulationKeySize768 = k*encodingSize12 + 32 + decapsulationKeySize768 = k*encodingSize12 + EncapsulationKeySize768 + 32 + 32 ) // ML-KEM-1024 parameters. @@ -65,6 +67,7 @@ const ( CiphertextSize1024 = k1024*encodingSize11 + encodingSize5 EncapsulationKeySize1024 = k1024*encodingSize12 + 32 + decapsulationKeySize1024 = k1024*encodingSize12 + EncapsulationKeySize1024 + 32 + 32 ) // A DecapsulationKey768 is the secret key used to decapsulate a shared key from a @@ -90,6 +93,32 @@ func (dk *DecapsulationKey768) Bytes() []byte { return b[:] } +// TestingOnlyExpandedBytes768 returns the decapsulation key as a byte slice +// using the full expanded NIST encoding. +// +// This should only be used for ACVP testing. For all other purposes prefer +// the Bytes method that returns the (much smaller) seed. +func TestingOnlyExpandedBytes768(dk *DecapsulationKey768) []byte { + b := make([]byte, 0, decapsulationKeySize768) + + // ByteEncode₁₂(s) + for i := range dk.s { + b = polyByteEncode(b, dk.s[i]) + } + + // ByteEncode₁₂(t) || ρ + for i := range dk.t { + b = polyByteEncode(b, dk.t[i]) + } + b = append(b, dk.ρ[:]...) + + // H(ek) || z + b = append(b, dk.h[:]...) + b = append(b, dk.z[:]...) + + return b +} + // EncapsulationKey returns the public encapsulation key necessary to produce // ciphertexts. func (dk *DecapsulationKey768) EncapsulationKey() *EncapsulationKey768 { @@ -187,6 +216,53 @@ func newKeyFromSeed(dk *DecapsulationKey768, seed []byte) (*DecapsulationKey768, return dk, nil } +// TestingOnlyNewDecapsulationKey768 parses a decapsulation key from its expanded NIST format. +// +// Bytes() must not be called on the returned key, as it will not produce the +// original seed. +// +// This function should only be used for ACVP testing. Prefer NewDecapsulationKey768 for all +// other purposes. +func TestingOnlyNewDecapsulationKey768(b []byte) (*DecapsulationKey768, error) { + if len(b) != decapsulationKeySize768 { + return nil, errors.New("mlkem: invalid NIST decapsulation key length") + } + + dk := &DecapsulationKey768{} + for i := range dk.s { + var err error + dk.s[i], err = polyByteDecode[nttElement](b[:encodingSize12]) + if err != nil { + return nil, errors.New("mlkem: invalid secret key encoding") + } + b = b[encodingSize12:] + } + + ek, err := NewEncapsulationKey768(b[:EncapsulationKeySize768]) + if err != nil { + return nil, err + } + dk.ρ = ek.ρ + dk.h = ek.h + dk.encryptionKey = ek.encryptionKey + b = b[EncapsulationKeySize768:] + + if !bytes.Equal(dk.h[:], b[:32]) { + return nil, errors.New("mlkem: inconsistent H(ek) in encoded bytes") + } + b = b[32:] + + copy(dk.z[:], b) + + // Generate a random d value for use in Bytes(). This is a safety mechanism + // that avoids returning a broken key vs a random key if this function is + // called in contravention of the TestingOnlyNewDecapsulationKey768 function + // comment advising against it. + drbg.Read(dk.d[:]) + + return dk, nil +} + // kemKeyGen generates a decapsulation key. // // It implements ML-KEM.KeyGen_internal according to FIPS 203, Algorithm 16, and diff --git a/src/crypto/internal/fips140/nistec/p256_asm_ppc64le.s b/src/crypto/internal/fips140/nistec/p256_asm_ppc64le.s index 7c46b268ef75714433a288f466a3e8d3d61f6afa..7efaa6ac187ad794c9990b8d6627e4a155a904b6 100644 --- a/src/crypto/internal/fips140/nistec/p256_asm_ppc64le.s +++ b/src/crypto/internal/fips140/nistec/p256_asm_ppc64le.s @@ -126,14 +126,23 @@ GLOBL p256mul<>(SB), 8, $160 #define PH V31 #define CAR1 V6 + +#define SEL V8 +#define ZER V9 + // func p256NegCond(val *p256Point, cond int) TEXT ·p256NegCond(SB), NOSPLIT, $0-16 MOVD val+0(FP), P1ptr MOVD $16, R16 - MOVD cond+8(FP), R6 - CMP $0, R6 - BC 12, 2, LR // just return if cond == 0 + // Copy cond into SEL (cond is R1 + 8 (cond offset) + 32) + MOVD $40, R17 + LXVDSX (R1)(R17), SEL + // Zeroize ZER + VSPLTISB $0, ZER + // SEL controls whether to return the original value (Y1H/Y1L) + // or the negated value (T1H/T1L). + VCMPEQUD SEL, ZER, SEL MOVD $p256mul<>+0x00(SB), CPOOL @@ -150,6 +159,9 @@ TEXT ·p256NegCond(SB), NOSPLIT, $0-16 VSUBUQM PL, Y1L, T1L // subtract part2 giving result VSUBEUQM PH, Y1H, CAR1, T1H // subtract part1 using carry from part2 + VSEL T1H, Y1H, SEL, T1H + VSEL T1L, Y1L, SEL, T1L + XXPERMDI T1H, T1H, $2, T1H XXPERMDI T1L, T1L, $2, T1L @@ -166,6 +178,8 @@ TEXT ·p256NegCond(SB), NOSPLIT, $0-16 #undef PL #undef PH #undef CAR1 +#undef SEL +#undef ZER #define P3ptr R3 #define P1ptr R4 diff --git a/src/crypto/internal/fips140/pbkdf2/pbkdf2.go b/src/crypto/internal/fips140/pbkdf2/pbkdf2.go index 8f6d9915041d3f1ee701c21a7653378541b6089b..05923f68262ac674ed254a729b29cc9752b887b0 100644 --- a/src/crypto/internal/fips140/pbkdf2/pbkdf2.go +++ b/src/crypto/internal/fips140/pbkdf2/pbkdf2.go @@ -7,15 +7,33 @@ package pbkdf2 import ( "crypto/internal/fips140" "crypto/internal/fips140/hmac" + "errors" ) +// divRoundUp divides x+y-1 by y, rounding up if the result is not whole. +// This function casts x and y to int64 in order to avoid cases where +// x+y would overflow int on systems where int is an int32. The result +// is an int, which is safe as (x+y-1)/y should always fit, regardless +// of the integer size. +func divRoundUp(x, y int) int { + return int((int64(x) + int64(y) - 1) / int64(y)) +} + func Key[Hash fips140.Hash](h func() Hash, password string, salt []byte, iter, keyLength int) ([]byte, error) { setServiceIndicator(salt, keyLength) + if keyLength <= 0 { + return nil, errors.New("pkbdf2: keyLength must be larger than 0") + } + prf := hmac.New(h, []byte(password)) hmac.MarkAsUsedInKDF(prf) hashLen := prf.Size() - numBlocks := (keyLength + hashLen - 1) / hashLen + numBlocks := divRoundUp(keyLength, hashLen) + const maxBlocks = int64(1<<32 - 1) + if keyLength+hashLen < keyLength || int64(numBlocks) > maxBlocks { + return nil, errors.New("pbkdf2: keyLength too long") + } var buf [4]byte dk := make([]byte, 0, numBlocks*hashLen) diff --git a/src/crypto/internal/fips140/rsa/cast.go b/src/crypto/internal/fips140/rsa/cast.go index ec7b5f3aeb92751ae79ab9e770e347b42109d98b..b900b32c88864181a990cb5d35aa61395062ddfb 100644 --- a/src/crypto/internal/fips140/rsa/cast.go +++ b/src/crypto/internal/fips140/rsa/cast.go @@ -171,6 +171,7 @@ func testPrivateKey() *PrivateKey { N: N, E: 65537, }, d: d, p: p, q: q, qInv: qInv, dP: dP, dQ: dQ, + fipsApproved: true, } } diff --git a/src/crypto/internal/fips140hash/hash.go b/src/crypto/internal/fips140hash/hash.go new file mode 100644 index 0000000000000000000000000000000000000000..6d67ee8b3429a14c0481f5c80a08bae7ae8b2a78 --- /dev/null +++ b/src/crypto/internal/fips140hash/hash.go @@ -0,0 +1,34 @@ +// Copyright 2024 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 fips140hash + +import ( + fsha3 "crypto/internal/fips140/sha3" + "crypto/sha3" + "hash" + _ "unsafe" +) + +//go:linkname sha3Unwrap +func sha3Unwrap(*sha3.SHA3) *fsha3.Digest + +// Unwrap returns h, or a crypto/internal/fips140 inner implementation of h. +// +// The return value can be type asserted to one of +// [crypto/internal/fips140/sha256.Digest], +// [crypto/internal/fips140/sha512.Digest], or +// [crypto/internal/fips140/sha3.Digest] if it is a FIPS 140-3 approved hash. +func Unwrap(h hash.Hash) hash.Hash { + if sha3, ok := h.(*sha3.SHA3); ok { + return sha3Unwrap(sha3) + } + return h +} + +// UnwrapNew returns a function that calls newHash and applies [Unwrap] to the +// return value. +func UnwrapNew[Hash hash.Hash](newHash func() Hash) func() hash.Hash { + return func() hash.Hash { return Unwrap(newHash()) } +} diff --git a/src/crypto/internal/fips140test/acvp_capabilities.json b/src/crypto/internal/fips140test/acvp_capabilities.json index 6502a98db12dcb7b285b51f233299002a2c376a1..8a4a97758cce39dc99706797665dfe77d4b1f07b 100644 --- a/src/crypto/internal/fips140test/acvp_capabilities.json +++ b/src/crypto/internal/fips140test/acvp_capabilities.json @@ -23,5 +23,19 @@ {"algorithm":"HMAC-SHA3-384","keyLen":[{"increment":8,"max":524288,"min":8}],"macLen":[{"increment":8,"max":384,"min":32}],"revision":"1.0"}, {"algorithm":"HMAC-SHA3-512","keyLen":[{"increment":8,"max":524288,"min":8}],"macLen":[{"increment":8,"max":512,"min":32}],"revision":"1.0"}, - {"algorithm":"PBKDF","capabilities":[{"iterationCount":[{"min":1,"max":10000,"increment":1}],"keyLen":[{"min":112,"max":4096,"increment":8}],"passwordLen":[{"min":8,"max":64,"increment":1}],"saltLen":[{"min":128,"max":512,"increment":8}],"hmacAlg":["SHA2-224","SHA2-256","SHA2-384","SHA2-512","SHA2-512/224","SHA2-512/256","SHA3-224","SHA3-256","SHA3-384","SHA3-512"]}],"revision":"1.0"} -] \ No newline at end of file + {"algorithm":"PBKDF","capabilities":[{"iterationCount":[{"min":1,"max":10000,"increment":1}],"keyLen":[{"min":112,"max":4096,"increment":8}],"passwordLen":[{"min":8,"max":64,"increment":1}],"saltLen":[{"min":128,"max":512,"increment":8}],"hmacAlg":["SHA2-224","SHA2-256","SHA2-384","SHA2-512","SHA2-512/224","SHA2-512/256","SHA3-224","SHA3-256","SHA3-384","SHA3-512"]}],"revision":"1.0"}, + + {"algorithm":"ML-KEM","mode":"keyGen","revision":"FIPS203","parameterSets":["ML-KEM-768","ML-KEM-1024"]}, + {"algorithm":"ML-KEM","mode":"encapDecap","revision":"FIPS203","parameterSets":["ML-KEM-768","ML-KEM-1024"],"functions":["encapsulation","decapsulation"]}, + + {"algorithm":"hmacDRBG","revision":"1.0","predResistanceEnabled":[false],"reseedImplemented":false,"capabilities":[{"mode":"SHA2-224","derFuncEnabled":false,"entropyInputLen":[192],"nonceLen":[96],"persoStringLen":[192],"additionalInputLen":[0],"returnedBitsLen":224}]}, + {"algorithm":"hmacDRBG","revision":"1.0","predResistanceEnabled":[false],"reseedImplemented":false,"capabilities":[{"mode":"SHA2-256","derFuncEnabled":false,"entropyInputLen":[256],"nonceLen":[128],"persoStringLen":[256],"additionalInputLen":[0],"returnedBitsLen":256}]}, + {"algorithm":"hmacDRBG","revision":"1.0","predResistanceEnabled":[false],"reseedImplemented":false,"capabilities":[{"mode":"SHA2-384","derFuncEnabled":false,"entropyInputLen":[256],"nonceLen":[128],"persoStringLen":[256],"additionalInputLen":[0],"returnedBitsLen":384}]}, + {"algorithm":"hmacDRBG","revision":"1.0","predResistanceEnabled":[false],"reseedImplemented":false,"capabilities":[{"mode":"SHA2-512","derFuncEnabled":false,"entropyInputLen":[256],"nonceLen":[128],"persoStringLen":[256],"additionalInputLen":[0],"returnedBitsLen":512}]}, + {"algorithm":"hmacDRBG","revision":"1.0","predResistanceEnabled":[false],"reseedImplemented":false,"capabilities":[{"mode":"SHA2-512/224","derFuncEnabled":false,"entropyInputLen":[192],"nonceLen":[96],"persoStringLen":[192],"additionalInputLen":[0],"returnedBitsLen":224}]}, + {"algorithm":"hmacDRBG","revision":"1.0","predResistanceEnabled":[false],"reseedImplemented":false,"capabilities":[{"mode":"SHA2-512/256","derFuncEnabled":false,"entropyInputLen":[256],"nonceLen":[128],"persoStringLen":[256],"additionalInputLen":[0],"returnedBitsLen":256}]}, + {"algorithm":"hmacDRBG","revision":"1.0","predResistanceEnabled":[false],"reseedImplemented":false,"capabilities":[{"mode":"SHA3-224","derFuncEnabled":false,"entropyInputLen":[192],"nonceLen":[96],"persoStringLen":[192],"additionalInputLen":[0],"returnedBitsLen":224}]}, + {"algorithm":"hmacDRBG","revision":"1.0","predResistanceEnabled":[false],"reseedImplemented":false,"capabilities":[{"mode":"SHA3-256","derFuncEnabled":false,"entropyInputLen":[256],"nonceLen":[128],"persoStringLen":[256],"additionalInputLen":[0],"returnedBitsLen":256}]}, + {"algorithm":"hmacDRBG","revision":"1.0","predResistanceEnabled":[false],"reseedImplemented":false,"capabilities":[{"mode":"SHA3-384","derFuncEnabled":false,"entropyInputLen":[256],"nonceLen":[128],"persoStringLen":[256],"additionalInputLen":[0],"returnedBitsLen":384}]}, + {"algorithm":"hmacDRBG","revision":"1.0","predResistanceEnabled":[false],"reseedImplemented":false,"capabilities":[{"mode":"SHA3-512","derFuncEnabled":false,"entropyInputLen":[256],"nonceLen":[128],"persoStringLen":[256],"additionalInputLen":[0],"returnedBitsLen":512}]} +] diff --git a/src/crypto/internal/fips140test/acvp_test.config.json b/src/crypto/internal/fips140test/acvp_test.config.json index 49ab51d0d2a55235e01d7727244a4b9f8d56a81d..dc4d714f19c6207aa2c2558a72b5e2419c517815 100644 --- a/src/crypto/internal/fips140test/acvp_test.config.json +++ b/src/crypto/internal/fips140test/acvp_test.config.json @@ -23,5 +23,9 @@ {"Wrapper": "go", "In": "vectors/HMAC-SHA3-384.bz2", "Out": "expected/HMAC-SHA3-384.bz2"}, {"Wrapper": "go", "In": "vectors/HMAC-SHA3-512.bz2", "Out": "expected/HMAC-SHA3-512.bz2"}, - {"Wrapper": "go", "In": "vectors/PBKDF.bz2", "Out": "expected/PBKDF.bz2"} + {"Wrapper": "go", "In": "vectors/PBKDF.bz2", "Out": "expected/PBKDF.bz2"}, + + {"Wrapper": "go", "In": "vectors/ML-KEM.bz2", "Out": "expected/ML-KEM.bz2"}, + + {"Wrapper": "go", "In": "vectors/hmacDRBG.bz2", "Out": "expected/hmacDRBG.bz2"} ] \ No newline at end of file diff --git a/src/crypto/internal/fips140test/acvp_test.go b/src/crypto/internal/fips140test/acvp_test.go index 139655ecf6038993a1b3b99009253c64be06b3ca..8dedb9a79159b5cd0720348b6cdeb715296a3f26 100644 --- a/src/crypto/internal/fips140test/acvp_test.go +++ b/src/crypto/internal/fips140test/acvp_test.go @@ -23,7 +23,9 @@ import ( "bytes" "crypto/internal/cryptotest" "crypto/internal/fips140" + "crypto/internal/fips140/ecdsa" "crypto/internal/fips140/hmac" + "crypto/internal/fips140/mlkem" "crypto/internal/fips140/pbkdf2" "crypto/internal/fips140/sha256" "crypto/internal/fips140/sha3" @@ -75,6 +77,10 @@ var ( // https://pages.nist.gov/ACVP/draft-fussell-acvp-mac.html#section-7 // PBKDF2 algorithm capabilities: // https://pages.nist.gov/ACVP/draft-celi-acvp-pbkdf.html#section-7.3 + // ML-KEM algorithm capabilities: + // https://pages.nist.gov/ACVP/draft-celi-acvp-ml-kem.html#section-7.3 + // HMAC DRBG algorithm capabilities: + // https://pages.nist.gov/ACVP/draft-vassilev-acvp-drbg.html#section-7.2 //go:embed acvp_capabilities.json capabilitiesJson []byte @@ -118,6 +124,24 @@ var ( "HMAC-SHA3-512": cmdHmacAft(func() fips140.Hash { return sha3.New512() }), "PBKDF": cmdPbkdf(), + + "ML-KEM-768/keyGen": cmdMlKem768KeyGenAft(), + "ML-KEM-768/encap": cmdMlKem768EncapAft(), + "ML-KEM-768/decap": cmdMlKem768DecapAft(), + "ML-KEM-1024/keyGen": cmdMlKem1024KeyGenAft(), + "ML-KEM-1024/encap": cmdMlKem1024EncapAft(), + "ML-KEM-1024/decap": cmdMlKem1024DecapAft(), + + "hmacDRBG/SHA2-224": cmdHmacDrbgAft(func() fips140.Hash { return sha256.New224() }), + "hmacDRBG/SHA2-256": cmdHmacDrbgAft(func() fips140.Hash { return sha256.New() }), + "hmacDRBG/SHA2-384": cmdHmacDrbgAft(func() fips140.Hash { return sha512.New384() }), + "hmacDRBG/SHA2-512": cmdHmacDrbgAft(func() fips140.Hash { return sha512.New() }), + "hmacDRBG/SHA2-512/224": cmdHmacDrbgAft(func() fips140.Hash { return sha512.New512_224() }), + "hmacDRBG/SHA2-512/256": cmdHmacDrbgAft(func() fips140.Hash { return sha512.New512_256() }), + "hmacDRBG/SHA3-224": cmdHmacDrbgAft(func() fips140.Hash { return sha3.New224() }), + "hmacDRBG/SHA3-256": cmdHmacDrbgAft(func() fips140.Hash { return sha3.New256() }), + "hmacDRBG/SHA3-384": cmdHmacDrbgAft(func() fips140.Hash { return sha3.New384() }), + "hmacDRBG/SHA3-512": cmdHmacDrbgAft(func() fips140.Hash { return sha3.New512() }), } ) @@ -404,14 +428,171 @@ func lookupHash(name string) (func() fips140.Hash, error) { return h, nil } +func cmdMlKem768KeyGenAft() command { + return command{ + requiredArgs: 1, // Seed + handler: func(args [][]byte) ([][]byte, error) { + seed := args[0] + + dk, err := mlkem.NewDecapsulationKey768(seed) + if err != nil { + return nil, fmt.Errorf("generating ML-KEM 768 decapsulation key: %w", err) + } + + // Important: we must return the full encoding of dk, not the seed. + return [][]byte{dk.EncapsulationKey().Bytes(), mlkem.TestingOnlyExpandedBytes768(dk)}, nil + }, + } +} + +func cmdMlKem768EncapAft() command { + return command{ + requiredArgs: 2, // Public key, entropy + handler: func(args [][]byte) ([][]byte, error) { + pk := args[0] + entropy := args[1] + + ek, err := mlkem.NewEncapsulationKey768(pk) + if err != nil { + return nil, fmt.Errorf("generating ML-KEM 768 encapsulation key: %w", err) + } + + if len(entropy) != 32 { + return nil, fmt.Errorf("wrong entropy length: got %d, want 32", len(entropy)) + } + + sharedKey, ct := ek.EncapsulateInternal((*[32]byte)(entropy[:32])) + + return [][]byte{ct, sharedKey}, nil + }, + } +} + +func cmdMlKem768DecapAft() command { + return command{ + requiredArgs: 2, // Private key, ciphertext + handler: func(args [][]byte) ([][]byte, error) { + pk := args[0] + ct := args[1] + + dk, err := mlkem.TestingOnlyNewDecapsulationKey768(pk) + if err != nil { + return nil, fmt.Errorf("generating ML-KEM 768 decapsulation key: %w", err) + } + + sharedKey, err := dk.Decapsulate(ct) + if err != nil { + return nil, fmt.Errorf("decapsulating ML-KEM 768 ciphertext: %w", err) + } + + return [][]byte{sharedKey}, nil + }, + } +} + +func cmdMlKem1024KeyGenAft() command { + return command{ + requiredArgs: 1, // Seed + handler: func(args [][]byte) ([][]byte, error) { + seed := args[0] + + dk, err := mlkem.NewDecapsulationKey1024(seed) + if err != nil { + return nil, fmt.Errorf("generating ML-KEM 1024 decapsulation key: %w", err) + } + + // Important: we must return the full encoding of dk, not the seed. + return [][]byte{dk.EncapsulationKey().Bytes(), mlkem.TestingOnlyExpandedBytes1024(dk)}, nil + }, + } +} + +func cmdMlKem1024EncapAft() command { + return command{ + requiredArgs: 2, // Public key, entropy + handler: func(args [][]byte) ([][]byte, error) { + pk := args[0] + entropy := args[1] + + ek, err := mlkem.NewEncapsulationKey1024(pk) + if err != nil { + return nil, fmt.Errorf("generating ML-KEM 1024 encapsulation key: %w", err) + } + + if len(entropy) != 32 { + return nil, fmt.Errorf("wrong entropy length: got %d, want 32", len(entropy)) + } + + sharedKey, ct := ek.EncapsulateInternal((*[32]byte)(entropy[:32])) + + return [][]byte{ct, sharedKey}, nil + }, + } +} + +func cmdMlKem1024DecapAft() command { + return command{ + requiredArgs: 2, // Private key, ciphertext + handler: func(args [][]byte) ([][]byte, error) { + pk := args[0] + ct := args[1] + + dk, err := mlkem.TestingOnlyNewDecapsulationKey1024(pk) + if err != nil { + return nil, fmt.Errorf("generating ML-KEM 1024 decapsulation key: %w", err) + } + + sharedKey, err := dk.Decapsulate(ct) + if err != nil { + return nil, fmt.Errorf("decapsulating ML-KEM 1024 ciphertext: %w", err) + } + + return [][]byte{sharedKey}, nil + }, + } +} + +func cmdHmacDrbgAft(h func() fips140.Hash) command { + return command{ + requiredArgs: 6, // Output length, entropy, personalization, ad1, ad2, nonce + handler: func(args [][]byte) ([][]byte, error) { + outLen := binary.LittleEndian.Uint32(args[0]) + entropy := args[1] + personalization := args[2] + ad1 := args[3] + ad2 := args[4] + nonce := args[5] + + // Our capabilities describe no additional data support. + if len(ad1) != 0 || len(ad2) != 0 { + return nil, errors.New("additional data not supported") + } + + // Our capabilities describe no prediction resistance (requires reseed) and no reseed. + // So the test procedure is: + // * Instantiate DRBG + // * Generate but don't output + // * Generate output + // * Uninstantiate + // See Table 7 in draft-vassilev-acvp-drbg + out := make([]byte, outLen) + drbg := ecdsa.TestingOnlyNewDRBG(h, entropy, nonce, personalization) + drbg.Generate(out) + drbg.Generate(out) + + return [][]byte{out}, nil + }, + } +} + func TestACVP(t *testing.T) { testenv.SkipIfShortAndSlow(t) const ( bsslModule = "boringssl.googlesource.com/boringssl.git" - bsslVersion = "v0.0.0-20241015160643-2587c4974dbe" + bsslVersion = "v0.0.0-20250108043213-d3f61eeacbf7" goAcvpModule = "github.com/cpu/go-acvp" - goAcvpVersion = "v0.0.0-20241011151719-6e0509dcb7ce" + goAcvpVersion = "v0.0.0-20250102201911-6839fc40f9f8" ) // In crypto/tls/bogo_shim_test.go the test is skipped if run on a builder with runtime.GOOS == "windows" diff --git a/src/crypto/internal/sysrand/rand_linux_test.go b/src/crypto/internal/sysrand/rand_linux_test.go index 417523c29ddc9f890eead7fe216781537d2e6c3f..ab43904f91296f4609024f55f5636a253c6d039e 100644 --- a/src/crypto/internal/sysrand/rand_linux_test.go +++ b/src/crypto/internal/sysrand/rand_linux_test.go @@ -2,6 +2,8 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. +//go:build cgo + package sysrand_test import ( diff --git a/src/crypto/md5/md5.go b/src/crypto/md5/md5.go index 75e1fc7404724a8ce233ea496e48e6142a1a57e2..a0384e175f31bdb6215d610d8edfb954b46397a3 100644 --- a/src/crypto/md5/md5.go +++ b/src/crypto/md5/md5.go @@ -104,9 +104,6 @@ func consumeUint32(b []byte) ([]byte, uint32) { // [encoding.BinaryUnmarshaler] to marshal and unmarshal the internal // state of the hash. func New() hash.Hash { - if fips140only.Enabled { - panic("crypto/md5: use of MD5 is not allowed in FIPS 140-only mode") - } d := new(digest) d.Reset() return d @@ -117,6 +114,9 @@ func (d *digest) Size() int { return Size } func (d *digest) BlockSize() int { return BlockSize } func (d *digest) Write(p []byte) (nn int, err error) { + if fips140only.Enabled { + return 0, errors.New("crypto/md5: use of MD5 is not allowed in FIPS 140-only mode") + } // Note that we currently call block or blockGeneric // directly (guarded using haveAsm) because this allows // escape analysis to see that p and d don't escape. @@ -158,6 +158,10 @@ func (d *digest) Sum(in []byte) []byte { } func (d *digest) checkSum() [Size]byte { + if fips140only.Enabled { + panic("crypto/md5: use of MD5 is not allowed in FIPS 140-only mode") + } + // Append 0x80 to the end of the message and then append zeros // until the length is a multiple of 56 bytes. Finally append // 8 bytes representing the message length in bits. @@ -184,9 +188,6 @@ func (d *digest) checkSum() [Size]byte { // Sum returns the MD5 checksum of the data. func Sum(data []byte) [Size]byte { - if fips140only.Enabled { - panic("crypto/md5: use of MD5 is not allowed in FIPS 140-only mode") - } var d digest d.Reset() d.Write(data) diff --git a/src/crypto/mlkem/example_test.go b/src/crypto/mlkem/example_test.go new file mode 100644 index 0000000000000000000000000000000000000000..28bf3f29e711edd6264bcc60b659352f82efe25b --- /dev/null +++ b/src/crypto/mlkem/example_test.go @@ -0,0 +1,47 @@ +// Copyright 2024 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 mlkem_test + +import ( + "crypto/mlkem" + "log" +) + +func Example() { + // Alice generates a new key pair and sends the encapsulation key to Bob. + dk, err := mlkem.GenerateKey768() + if err != nil { + log.Fatal(err) + } + encapsulationKey := dk.EncapsulationKey().Bytes() + + // Bob uses the encapsulation key to encapsulate a shared secret, and sends + // back the ciphertext to Alice. + ciphertext := Bob(encapsulationKey) + + // Alice decapsulates the shared secret from the ciphertext. + sharedSecret, err := dk.Decapsulate(ciphertext) + if err != nil { + log.Fatal(err) + } + + // Alice and Bob now share a secret. + _ = sharedSecret +} + +func Bob(encapsulationKey []byte) (ciphertext []byte) { + // Bob encapsulates a shared secret using the encapsulation key. + ek, err := mlkem.NewEncapsulationKey768(encapsulationKey) + if err != nil { + log.Fatal(err) + } + sharedSecret, ciphertext := ek.Encapsulate() + + // Alice and Bob now share a secret. + _ = sharedSecret + + // Bob sends the ciphertext to Alice. + return ciphertext +} diff --git a/src/crypto/mlkem/mlkem.go b/src/crypto/mlkem/mlkem.go new file mode 100644 index 0000000000000000000000000000000000000000..69c0bc571f95a92a3c4d1ae291d7af5ce0d6688b --- /dev/null +++ b/src/crypto/mlkem/mlkem.go @@ -0,0 +1,192 @@ +// 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. + +// Package mlkem implements the quantum-resistant key encapsulation method +// ML-KEM (formerly known as Kyber), as specified in [NIST FIPS 203]. +// +// Most applications should use the ML-KEM-768 parameter set, as implemented by +// [DecapsulationKey768] and [EncapsulationKey768]. +// +// [NIST FIPS 203]: https://doi.org/10.6028/NIST.FIPS.203 +package mlkem + +import "crypto/internal/fips140/mlkem" + +const ( + // SharedKeySize is the size of a shared key produced by ML-KEM. + SharedKeySize = 32 + + // SeedSize is the size of a seed used to generate a decapsulation key. + SeedSize = 64 + + // CiphertextSize768 is the size of a ciphertext produced by ML-KEM-768. + CiphertextSize768 = 1088 + + // EncapsulationKeySize768 is the size of an ML-KEM-768 encapsulation key. + EncapsulationKeySize768 = 1184 + + // CiphertextSize1024 is the size of a ciphertext produced by ML-KEM-1024. + CiphertextSize1024 = 1568 + + // EncapsulationKeySize1024 is the size of an ML-KEM-1024 encapsulation key. + EncapsulationKeySize1024 = 1568 +) + +// DecapsulationKey768 is the secret key used to decapsulate a shared key +// from a ciphertext. It includes various precomputed values. +type DecapsulationKey768 struct { + key *mlkem.DecapsulationKey768 +} + +// GenerateKey768 generates a new decapsulation key, drawing random bytes from +// the default crypto/rand source. The decapsulation key must be kept secret. +func GenerateKey768() (*DecapsulationKey768, error) { + key, err := mlkem.GenerateKey768() + if err != nil { + return nil, err + } + + return &DecapsulationKey768{key}, nil +} + +// NewDecapsulationKey768 expands a decapsulation key from a 64-byte seed in the +// "d || z" form. The seed must be uniformly random. +func NewDecapsulationKey768(seed []byte) (*DecapsulationKey768, error) { + key, err := mlkem.NewDecapsulationKey768(seed) + if err != nil { + return nil, err + } + + return &DecapsulationKey768{key}, nil +} + +// Bytes returns the decapsulation key as a 64-byte seed in the "d || z" form. +// +// The decapsulation key must be kept secret. +func (dk *DecapsulationKey768) Bytes() []byte { + return dk.key.Bytes() +} + +// Decapsulate generates a shared key from a ciphertext and a decapsulation +// key. If the ciphertext is not valid, Decapsulate returns an error. +// +// The shared key must be kept secret. +func (dk *DecapsulationKey768) Decapsulate(ciphertext []byte) (sharedKey []byte, err error) { + return dk.key.Decapsulate(ciphertext) +} + +// EncapsulationKey returns the public encapsulation key necessary to produce +// ciphertexts. +func (dk *DecapsulationKey768) EncapsulationKey() *EncapsulationKey768 { + return &EncapsulationKey768{dk.key.EncapsulationKey()} +} + +// An EncapsulationKey768 is the public key used to produce ciphertexts to be +// decapsulated by the corresponding DecapsulationKey768. +type EncapsulationKey768 struct { + key *mlkem.EncapsulationKey768 +} + +// NewEncapsulationKey768 parses an encapsulation key from its encoded form. If +// the encapsulation key is not valid, NewEncapsulationKey768 returns an error. +func NewEncapsulationKey768(encapsulationKey []byte) (*EncapsulationKey768, error) { + key, err := mlkem.NewEncapsulationKey768(encapsulationKey) + if err != nil { + return nil, err + } + + return &EncapsulationKey768{key}, nil +} + +// Bytes returns the encapsulation key as a byte slice. +func (ek *EncapsulationKey768) Bytes() []byte { + return ek.key.Bytes() +} + +// Encapsulate generates a shared key and an associated ciphertext from an +// encapsulation key, drawing random bytes from the default crypto/rand source. +// +// The shared key must be kept secret. +func (ek *EncapsulationKey768) Encapsulate() (sharedKey, ciphertext []byte) { + return ek.key.Encapsulate() +} + +// DecapsulationKey1024 is the secret key used to decapsulate a shared key +// from a ciphertext. It includes various precomputed values. +type DecapsulationKey1024 struct { + key *mlkem.DecapsulationKey1024 +} + +// GenerateKey1024 generates a new decapsulation key, drawing random bytes from +// the default crypto/rand source. The decapsulation key must be kept secret. +func GenerateKey1024() (*DecapsulationKey1024, error) { + key, err := mlkem.GenerateKey1024() + if err != nil { + return nil, err + } + + return &DecapsulationKey1024{key}, nil +} + +// NewDecapsulationKey1024 expands a decapsulation key from a 64-byte seed in the +// "d || z" form. The seed must be uniformly random. +func NewDecapsulationKey1024(seed []byte) (*DecapsulationKey1024, error) { + key, err := mlkem.NewDecapsulationKey1024(seed) + if err != nil { + return nil, err + } + + return &DecapsulationKey1024{key}, nil +} + +// Bytes returns the decapsulation key as a 64-byte seed in the "d || z" form. +// +// The decapsulation key must be kept secret. +func (dk *DecapsulationKey1024) Bytes() []byte { + return dk.key.Bytes() +} + +// Decapsulate generates a shared key from a ciphertext and a decapsulation +// key. If the ciphertext is not valid, Decapsulate returns an error. +// +// The shared key must be kept secret. +func (dk *DecapsulationKey1024) Decapsulate(ciphertext []byte) (sharedKey []byte, err error) { + return dk.key.Decapsulate(ciphertext) +} + +// EncapsulationKey returns the public encapsulation key necessary to produce +// ciphertexts. +func (dk *DecapsulationKey1024) EncapsulationKey() *EncapsulationKey1024 { + return &EncapsulationKey1024{dk.key.EncapsulationKey()} +} + +// An EncapsulationKey1024 is the public key used to produce ciphertexts to be +// decapsulated by the corresponding DecapsulationKey1024. +type EncapsulationKey1024 struct { + key *mlkem.EncapsulationKey1024 +} + +// NewEncapsulationKey1024 parses an encapsulation key from its encoded form. If +// the encapsulation key is not valid, NewEncapsulationKey1024 returns an error. +func NewEncapsulationKey1024(encapsulationKey []byte) (*EncapsulationKey1024, error) { + key, err := mlkem.NewEncapsulationKey1024(encapsulationKey) + if err != nil { + return nil, err + } + + return &EncapsulationKey1024{key}, nil +} + +// Bytes returns the encapsulation key as a byte slice. +func (ek *EncapsulationKey1024) Bytes() []byte { + return ek.key.Bytes() +} + +// Encapsulate generates a shared key and an associated ciphertext from an +// encapsulation key, drawing random bytes from the default crypto/rand source. +// +// The shared key must be kept secret. +func (ek *EncapsulationKey1024) Encapsulate() (sharedKey, ciphertext []byte) { + return ek.key.Encapsulate() +} diff --git a/src/crypto/mlkem/mlkem1024.go b/src/crypto/mlkem/mlkem1024.go deleted file mode 100644 index 05bad1eb2aa42c248c7caa2736bc2839589a570d..0000000000000000000000000000000000000000 --- a/src/crypto/mlkem/mlkem1024.go +++ /dev/null @@ -1,96 +0,0 @@ -// 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. - -package mlkem - -import "crypto/internal/fips140/mlkem" - -const ( - // CiphertextSize1024 is the size of a ciphertext produced by the 1024-bit - // variant of ML-KEM. - CiphertextSize1024 = 1568 - - // EncapsulationKeySize1024 is the size of an encapsulation key for the - // 1024-bit variant of ML-KEM. - EncapsulationKeySize1024 = 1568 -) - -// DecapsulationKey1024 is the secret key used to decapsulate a shared key -// from a ciphertext. It includes various precomputed values. -type DecapsulationKey1024 struct { - key *mlkem.DecapsulationKey1024 -} - -// GenerateKey1024 generates a new decapsulation key, drawing random bytes from -// crypto/rand. The decapsulation key must be kept secret. -func GenerateKey1024() (*DecapsulationKey1024, error) { - key, err := mlkem.GenerateKey1024() - if err != nil { - return nil, err - } - - return &DecapsulationKey1024{key}, nil -} - -// NewDecapsulationKey1024 parses a decapsulation key from a 64-byte seed in the -// "d || z" form. The seed must be uniformly random. -func NewDecapsulationKey1024(seed []byte) (*DecapsulationKey1024, error) { - key, err := mlkem.NewDecapsulationKey1024(seed) - if err != nil { - return nil, err - } - - return &DecapsulationKey1024{key}, nil -} - -// Bytes returns the decapsulation key as a 64-byte seed in the "d || z" form. -// -// The decapsulation key must be kept secret. -func (dk *DecapsulationKey1024) Bytes() []byte { - return dk.key.Bytes() -} - -// Decapsulate generates a shared key from a ciphertext and a decapsulation -// key. If the ciphertext is not valid, Decapsulate returns an error. -// -// The shared key must be kept secret. -func (dk *DecapsulationKey1024) Decapsulate(ciphertext []byte) (sharedKey []byte, err error) { - return dk.key.Decapsulate(ciphertext) -} - -// EncapsulationKey returns the public encapsulation key necessary to produce -// ciphertexts. -func (dk *DecapsulationKey1024) EncapsulationKey() *EncapsulationKey1024 { - return &EncapsulationKey1024{dk.key.EncapsulationKey()} -} - -// An EncapsulationKey1024 is the public key used to produce ciphertexts to be -// decapsulated by the corresponding DecapsulationKey1024. -type EncapsulationKey1024 struct { - key *mlkem.EncapsulationKey1024 -} - -// NewEncapsulationKey1024 parses an encapsulation key from its encoded form. If -// the encapsulation key is not valid, NewEncapsulationKey1024 returns an error. -func NewEncapsulationKey1024(encapsulationKey []byte) (*EncapsulationKey1024, error) { - key, err := mlkem.NewEncapsulationKey1024(encapsulationKey) - if err != nil { - return nil, err - } - - return &EncapsulationKey1024{key}, nil -} - -// Bytes returns the encapsulation key as a byte slice. -func (ek *EncapsulationKey1024) Bytes() []byte { - return ek.key.Bytes() -} - -// Encapsulate generates a shared key and an associated ciphertext from an -// encapsulation key, drawing random bytes from crypto/rand. -// -// The shared key must be kept secret. -func (ek *EncapsulationKey1024) Encapsulate() (sharedKey, ciphertext []byte) { - return ek.key.Encapsulate() -} diff --git a/src/crypto/mlkem/mlkem768.go b/src/crypto/mlkem/mlkem768.go deleted file mode 100644 index c367c551e6fc024cbc241ec636311ed06d1a8d6b..0000000000000000000000000000000000000000 --- a/src/crypto/mlkem/mlkem768.go +++ /dev/null @@ -1,106 +0,0 @@ -// 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. - -// Package mlkem implements the quantum-resistant key encapsulation method -// ML-KEM (formerly known as Kyber), as specified in [NIST FIPS 203]. -// -// [NIST FIPS 203]: https://doi.org/10.6028/NIST.FIPS.203 -package mlkem - -import "crypto/internal/fips140/mlkem" - -const ( - // SharedKeySize is the size of a shared key produced by ML-KEM. - SharedKeySize = 32 - - // SeedSize is the size of a seed used to generate a decapsulation key. - SeedSize = 64 - - // CiphertextSize768 is the size of a ciphertext produced by the 768-bit - // variant of ML-KEM. - CiphertextSize768 = 1088 - - // EncapsulationKeySize768 is the size of an encapsulation key for the - // 768-bit variant of ML-KEM. - EncapsulationKeySize768 = 1184 -) - -// DecapsulationKey768 is the secret key used to decapsulate a shared key -// from a ciphertext. It includes various precomputed values. -type DecapsulationKey768 struct { - key *mlkem.DecapsulationKey768 -} - -// GenerateKey768 generates a new decapsulation key, drawing random bytes from -// crypto/rand. The decapsulation key must be kept secret. -func GenerateKey768() (*DecapsulationKey768, error) { - key, err := mlkem.GenerateKey768() - if err != nil { - return nil, err - } - - return &DecapsulationKey768{key}, nil -} - -// NewDecapsulationKey768 parses a decapsulation key from a 64-byte seed in the -// "d || z" form. The seed must be uniformly random. -func NewDecapsulationKey768(seed []byte) (*DecapsulationKey768, error) { - key, err := mlkem.NewDecapsulationKey768(seed) - if err != nil { - return nil, err - } - - return &DecapsulationKey768{key}, nil -} - -// Bytes returns the decapsulation key as a 64-byte seed in the "d || z" form. -// -// The decapsulation key must be kept secret. -func (dk *DecapsulationKey768) Bytes() []byte { - return dk.key.Bytes() -} - -// Decapsulate generates a shared key from a ciphertext and a decapsulation -// key. If the ciphertext is not valid, Decapsulate returns an error. -// -// The shared key must be kept secret. -func (dk *DecapsulationKey768) Decapsulate(ciphertext []byte) (sharedKey []byte, err error) { - return dk.key.Decapsulate(ciphertext) -} - -// EncapsulationKey returns the public encapsulation key necessary to produce -// ciphertexts. -func (dk *DecapsulationKey768) EncapsulationKey() *EncapsulationKey768 { - return &EncapsulationKey768{dk.key.EncapsulationKey()} -} - -// An EncapsulationKey768 is the public key used to produce ciphertexts to be -// decapsulated by the corresponding DecapsulationKey768. -type EncapsulationKey768 struct { - key *mlkem.EncapsulationKey768 -} - -// NewEncapsulationKey768 parses an encapsulation key from its encoded form. If -// the encapsulation key is not valid, NewEncapsulationKey768 returns an error. -func NewEncapsulationKey768(encapsulationKey []byte) (*EncapsulationKey768, error) { - key, err := mlkem.NewEncapsulationKey768(encapsulationKey) - if err != nil { - return nil, err - } - - return &EncapsulationKey768{key}, nil -} - -// Bytes returns the encapsulation key as a byte slice. -func (ek *EncapsulationKey768) Bytes() []byte { - return ek.key.Bytes() -} - -// Encapsulate generates a shared key and an associated ciphertext from an -// encapsulation key, drawing random bytes from crypto/rand. -// -// The shared key must be kept secret. -func (ek *EncapsulationKey768) Encapsulate() (sharedKey, ciphertext []byte) { - return ek.key.Encapsulate() -} diff --git a/src/crypto/pbkdf2/pbkdf2.go b/src/crypto/pbkdf2/pbkdf2.go index d40daab5e5b879658dcf742f35762407cfea4d95..dd5fc33f2120c335328291e232721642ba83d779 100644 --- a/src/crypto/pbkdf2/pbkdf2.go +++ b/src/crypto/pbkdf2/pbkdf2.go @@ -12,6 +12,7 @@ package pbkdf2 import ( "crypto/internal/fips140/pbkdf2" + "crypto/internal/fips140hash" "crypto/internal/fips140only" "errors" "hash" @@ -33,7 +34,11 @@ import ( // // Using a higher iteration count will increase the cost of an exhaustive // search but will also make derivation proportionally slower. +// +// keyLength must be a positive integer between 1 and (2^32 - 1) * h.Size(). +// Setting keyLength to a value outside of this range will result in an error. func Key[Hash hash.Hash](h func() Hash, password string, salt []byte, iter, keyLength int) ([]byte, error) { + fh := fips140hash.UnwrapNew(h) if fips140only.Enabled { if keyLength < 112/8 { return nil, errors.New("crypto/pbkdf2: use of keys shorter than 112 bits is not allowed in FIPS 140-only mode") @@ -41,9 +46,9 @@ func Key[Hash hash.Hash](h func() Hash, password string, salt []byte, iter, keyL if len(salt) < 128/8 { return nil, errors.New("crypto/pbkdf2: use of salts shorter than 128 bits is not allowed in FIPS 140-only mode") } - if !fips140only.ApprovedHash(h()) { + if !fips140only.ApprovedHash(fh()) { return nil, errors.New("crypto/pbkdf2: use of hash functions other than SHA-2 or SHA-3 is not allowed in FIPS 140-only mode") } } - return pbkdf2.Key(h, password, salt, iter, keyLength) + return pbkdf2.Key(fh, password, salt, iter, keyLength) } diff --git a/src/crypto/pbkdf2/pbkdf2_test.go b/src/crypto/pbkdf2/pbkdf2_test.go index 03980c7e54d3be59a85c9569ae358f6a32069056..eb0ed14e243c6ba22e4de47cadc139f0c0ced177 100644 --- a/src/crypto/pbkdf2/pbkdf2_test.go +++ b/src/crypto/pbkdf2/pbkdf2_test.go @@ -221,3 +221,33 @@ func TestPBKDF2ServiceIndicator(t *testing.T) { t.Error("FIPS service indicator should not be set") } } + +func TestMaxKeyLength(t *testing.T) { + // This error cannot be triggered on platforms where int is 31 bits (i.e. + // 32-bit platforms), since the max value for keyLength is 1<<31-1 and + // 1<<31-1 * hLen will always be less than 1<<32-1 * hLen. + keySize := int64(1<<63 - 1) + if int64(int(keySize)) != keySize { + t.Skip("cannot be replicated on platforms where int is 31 bits") + } + _, err := pbkdf2.Key(sha256.New, "password", []byte("salt"), 1, int(keySize)) + if err == nil { + t.Fatal("expected pbkdf2.Key to fail with extremely large keyLength") + } + keySize = int64(1<<32-1) * (sha256.Size + 1) + _, err = pbkdf2.Key(sha256.New, "password", []byte("salt"), 1, int(keySize)) + if err == nil { + t.Fatal("expected pbkdf2.Key to fail with extremely large keyLength") + } +} + +func TestZeroKeyLength(t *testing.T) { + _, err := pbkdf2.Key(sha256.New, "password", []byte("salt"), 1, 0) + if err == nil { + t.Fatal("expected pbkdf2.Key to fail with zero keyLength") + } + _, err = pbkdf2.Key(sha256.New, "password", []byte("salt"), 1, -1) + if err == nil { + t.Fatal("expected pbkdf2.Key to fail with negative keyLength") + } +} diff --git a/src/crypto/rsa/fips.go b/src/crypto/rsa/fips.go index 24dfb38cf625bda5f87f003c140175cd7763bae4..8373c125ae3096b1d8719f77830027e8721ffc72 100644 --- a/src/crypto/rsa/fips.go +++ b/src/crypto/rsa/fips.go @@ -8,6 +8,7 @@ import ( "crypto" "crypto/internal/boring" "crypto/internal/fips140/rsa" + "crypto/internal/fips140hash" "crypto/internal/fips140only" "errors" "hash" @@ -64,15 +65,6 @@ func SignPSS(rand io.Reader, priv *PrivateKey, hash crypto.Hash, digest []byte, if err := checkPublicKeySize(&priv.PublicKey); err != nil { return nil, err } - if err := checkFIPS140OnlyPrivateKey(priv); err != nil { - return nil, err - } - if fips140only.Enabled && !fips140only.ApprovedHash(hash.New()) { - return nil, errors.New("crypto/rsa: use of hash functions other than SHA-2 or SHA-3 is not allowed in FIPS 140-only mode") - } - if fips140only.Enabled && !fips140only.ApprovedRandomReader(rand) { - return nil, errors.New("crypto/rsa: only crypto/rand.Reader is allowed in FIPS 140-only mode") - } if opts != nil && opts.Hash != 0 { hash = opts.Hash @@ -87,14 +79,25 @@ func SignPSS(rand io.Reader, priv *PrivateKey, hash crypto.Hash, digest []byte, } boring.UnreachableExceptTests() + h := fips140hash.Unwrap(hash.New()) + + if err := checkFIPS140OnlyPrivateKey(priv); err != nil { + return nil, err + } + if fips140only.Enabled && !fips140only.ApprovedHash(h) { + return nil, errors.New("crypto/rsa: use of hash functions other than SHA-2 or SHA-3 is not allowed in FIPS 140-only mode") + } + if fips140only.Enabled && !fips140only.ApprovedRandomReader(rand) { + return nil, errors.New("crypto/rsa: only crypto/rand.Reader is allowed in FIPS 140-only mode") + } + k, err := fipsPrivateKey(priv) if err != nil { return nil, err } - h := hash.New() saltLength := opts.saltLength() - if fips140only.Enabled && saltLength > hash.Size() { + if fips140only.Enabled && saltLength > h.Size() { return nil, errors.New("crypto/rsa: use of PSS salt longer than the hash is not allowed in FIPS 140-only mode") } switch saltLength { @@ -104,7 +107,7 @@ func SignPSS(rand io.Reader, priv *PrivateKey, hash crypto.Hash, digest []byte, return nil, fipsError(err) } case PSSSaltLengthEqualsHash: - saltLength = hash.Size() + saltLength = h.Size() default: // If we get here saltLength is either > 0 or < -1, in the // latter case we fail out. @@ -129,12 +132,6 @@ func VerifyPSS(pub *PublicKey, hash crypto.Hash, digest []byte, sig []byte, opts if err := checkPublicKeySize(pub); err != nil { return err } - if err := checkFIPS140OnlyPublicKey(pub); err != nil { - return err - } - if fips140only.Enabled && !fips140only.ApprovedHash(hash.New()) { - return errors.New("crypto/rsa: use of hash functions other than SHA-2 or SHA-3 is not allowed in FIPS 140-only mode") - } if boring.Enabled { bkey, err := boringPublicKey(pub) @@ -147,22 +144,31 @@ func VerifyPSS(pub *PublicKey, hash crypto.Hash, digest []byte, sig []byte, opts return nil } + h := fips140hash.Unwrap(hash.New()) + + if err := checkFIPS140OnlyPublicKey(pub); err != nil { + return err + } + if fips140only.Enabled && !fips140only.ApprovedHash(h) { + return errors.New("crypto/rsa: use of hash functions other than SHA-2 or SHA-3 is not allowed in FIPS 140-only mode") + } + k, err := fipsPublicKey(pub) if err != nil { return err } saltLength := opts.saltLength() - if fips140only.Enabled && saltLength > hash.Size() { + if fips140only.Enabled && saltLength > h.Size() { return errors.New("crypto/rsa: use of PSS salt longer than the hash is not allowed in FIPS 140-only mode") } switch saltLength { case PSSSaltLengthAuto: - return fipsError(rsa.VerifyPSS(k, hash.New(), digest, sig)) + return fipsError(rsa.VerifyPSS(k, h, digest, sig)) case PSSSaltLengthEqualsHash: - return fipsError(rsa.VerifyPSSWithSaltLength(k, hash.New(), digest, sig, hash.Size())) + return fipsError(rsa.VerifyPSSWithSaltLength(k, h, digest, sig, h.Size())) default: - return fipsError(rsa.VerifyPSSWithSaltLength(k, hash.New(), digest, sig, saltLength)) + return fipsError(rsa.VerifyPSSWithSaltLength(k, h, digest, sig, saltLength)) } } @@ -188,15 +194,6 @@ func EncryptOAEP(hash hash.Hash, random io.Reader, pub *PublicKey, msg []byte, l if err := checkPublicKeySize(pub); err != nil { return nil, err } - if err := checkFIPS140OnlyPublicKey(pub); err != nil { - return nil, err - } - if fips140only.Enabled && !fips140only.ApprovedHash(hash) { - return nil, errors.New("crypto/rsa: use of hash functions other than SHA-2 or SHA-3 is not allowed in FIPS 140-only mode") - } - if fips140only.Enabled && !fips140only.ApprovedRandomReader(random) { - return nil, errors.New("crypto/rsa: only crypto/rand.Reader is allowed in FIPS 140-only mode") - } defer hash.Reset() @@ -214,6 +211,18 @@ func EncryptOAEP(hash hash.Hash, random io.Reader, pub *PublicKey, msg []byte, l } boring.UnreachableExceptTests() + hash = fips140hash.Unwrap(hash) + + if err := checkFIPS140OnlyPublicKey(pub); err != nil { + return nil, err + } + if fips140only.Enabled && !fips140only.ApprovedHash(hash) { + return nil, errors.New("crypto/rsa: use of hash functions other than SHA-2 or SHA-3 is not allowed in FIPS 140-only mode") + } + if fips140only.Enabled && !fips140only.ApprovedRandomReader(random) { + return nil, errors.New("crypto/rsa: only crypto/rand.Reader is allowed in FIPS 140-only mode") + } + k, err := fipsPublicKey(pub) if err != nil { return nil, err @@ -240,14 +249,6 @@ func decryptOAEP(hash, mgfHash hash.Hash, priv *PrivateKey, ciphertext []byte, l if err := checkPublicKeySize(&priv.PublicKey); err != nil { return nil, err } - if err := checkFIPS140OnlyPrivateKey(priv); err != nil { - return nil, err - } - if fips140only.Enabled { - if !fips140only.ApprovedHash(hash) || !fips140only.ApprovedHash(mgfHash) { - return nil, errors.New("crypto/rsa: use of hash functions other than SHA-2 or SHA-3 is not allowed in FIPS 140-only mode") - } - } if boring.Enabled { k := priv.Size() @@ -266,6 +267,18 @@ func decryptOAEP(hash, mgfHash hash.Hash, priv *PrivateKey, ciphertext []byte, l return out, nil } + hash = fips140hash.Unwrap(hash) + mgfHash = fips140hash.Unwrap(mgfHash) + + if err := checkFIPS140OnlyPrivateKey(priv); err != nil { + return nil, err + } + if fips140only.Enabled { + if !fips140only.ApprovedHash(hash) || !fips140only.ApprovedHash(mgfHash) { + return nil, errors.New("crypto/rsa: use of hash functions other than SHA-2 or SHA-3 is not allowed in FIPS 140-only mode") + } + } + k, err := fipsPrivateKey(priv) if err != nil { return nil, err @@ -298,12 +311,6 @@ func SignPKCS1v15(random io.Reader, priv *PrivateKey, hash crypto.Hash, hashed [ if err := checkPublicKeySize(&priv.PublicKey); err != nil { return nil, err } - if err := checkFIPS140OnlyPrivateKey(priv); err != nil { - return nil, err - } - if fips140only.Enabled && !fips140only.ApprovedHash(hash.New()) { - return nil, errors.New("crypto/rsa: use of hash functions other than SHA-2 or SHA-3 is not allowed in FIPS 140-only mode") - } if boring.Enabled { bkey, err := boringPrivateKey(priv) @@ -313,6 +320,13 @@ func SignPKCS1v15(random io.Reader, priv *PrivateKey, hash crypto.Hash, hashed [ return boring.SignRSAPKCS1v15(bkey, hash, hashed) } + if err := checkFIPS140OnlyPrivateKey(priv); err != nil { + return nil, err + } + if fips140only.Enabled && !fips140only.ApprovedHash(fips140hash.Unwrap(hash.New())) { + return nil, errors.New("crypto/rsa: use of hash functions other than SHA-2 or SHA-3 is not allowed in FIPS 140-only mode") + } + k, err := fipsPrivateKey(priv) if err != nil { return nil, err @@ -329,15 +343,17 @@ func SignPKCS1v15(random io.Reader, priv *PrivateKey, hash crypto.Hash, hashed [ // The inputs are not considered confidential, and may leak through timing side // channels, or if an attacker has control of part of the inputs. func VerifyPKCS1v15(pub *PublicKey, hash crypto.Hash, hashed []byte, sig []byte) error { - if err := checkPublicKeySize(pub); err != nil { - return err + var hashName string + if hash != crypto.Hash(0) { + if len(hashed) != hash.Size() { + return errors.New("crypto/rsa: input must be hashed message") + } + hashName = hash.String() } - if err := checkFIPS140OnlyPublicKey(pub); err != nil { + + if err := checkPublicKeySize(pub); err != nil { return err } - if fips140only.Enabled && !fips140only.ApprovedHash(hash.New()) { - return errors.New("crypto/rsa: use of hash functions other than SHA-2 or SHA-3 is not allowed in FIPS 140-only mode") - } if boring.Enabled { bkey, err := boringPublicKey(pub) @@ -350,17 +366,17 @@ func VerifyPKCS1v15(pub *PublicKey, hash crypto.Hash, hashed []byte, sig []byte) return nil } + if err := checkFIPS140OnlyPublicKey(pub); err != nil { + return err + } + if fips140only.Enabled && !fips140only.ApprovedHash(fips140hash.Unwrap(hash.New())) { + return errors.New("crypto/rsa: use of hash functions other than SHA-2 or SHA-3 is not allowed in FIPS 140-only mode") + } + k, err := fipsPublicKey(pub) if err != nil { return err } - var hashName string - if hash != crypto.Hash(0) { - if len(hashed) != hash.Size() { - return errors.New("crypto/rsa: input must be hashed message") - } - hashName = hash.String() - } return fipsError(rsa.VerifyPKCS1v15(k, hashName, hashed, sig)) } diff --git a/src/crypto/rsa/rsa.go b/src/crypto/rsa/rsa.go index fb23f003a6f2177ed6a1d1c0dfc1220d80425a9c..95bb4becd2ff8cc999802d37147f2e95ae500412 100644 --- a/src/crypto/rsa/rsa.go +++ b/src/crypto/rsa/rsa.go @@ -327,6 +327,21 @@ func GenerateKey(random io.Reader, bits int) (*PrivateKey, error) { } k, err := rsa.GenerateKey(random, bits) + if bits < 256 && err != nil { + // Toy-sized keys have a non-negligible chance of hitting two hard + // failure cases: p == q and d <= 2^(nlen / 2). + // + // Since these are impossible to hit for real keys, we don't want to + // make the production code path more complex and harder to think about + // to handle them. + // + // Instead, just rerun the whole process a total of 8 times, which + // brings the chance of failure for 32-bit keys down to the same as for + // 256-bit keys. + for i := 1; i < 8 && err != nil; i++ { + k, err = rsa.GenerateKey(random, bits) + } + } if err != nil { return nil, err } diff --git a/src/crypto/rsa/rsa_test.go b/src/crypto/rsa/rsa_test.go index 2535661040273a73bdad1ea1857044f45ae2616e..73b0c3749eedb25c3367370e6a0149560e023919 100644 --- a/src/crypto/rsa/rsa_test.go +++ b/src/crypto/rsa/rsa_test.go @@ -109,6 +109,23 @@ func TestImpossibleKeyGeneration(t *testing.T) { } } +func TestTinyKeyGeneration(t *testing.T) { + // Toy-sized keys can randomly hit hard failures in GenerateKey. + if testing.Short() { + t.Skip("skipping in short mode") + } + t.Setenv("GODEBUG", "rsa1024min=0") + for range 10000 { + k, err := GenerateKey(rand.Reader, 32) + if err != nil { + t.Fatalf("GenerateKey(32): %v", err) + } + if err := k.Validate(); err != nil { + t.Fatalf("Validate(32): %v", err) + } + } +} + func TestGnuTLSKey(t *testing.T) { t.Setenv("GODEBUG", "rsa1024min=0") // This is a key generated by `certtool --generate-privkey --bits 128`. diff --git a/src/crypto/sha1/sha1.go b/src/crypto/sha1/sha1.go index b799f0d2fb154886877c800214abd5b3e06ebfd8..d2ffaac0aeb674cf4065934b5763cb5ab0c63277 100644 --- a/src/crypto/sha1/sha1.go +++ b/src/crypto/sha1/sha1.go @@ -111,9 +111,6 @@ func New() hash.Hash { if boring.Enabled { return boring.NewSHA1() } - if fips140only.Enabled { - panic("crypto/sha1: use of weak SHA-1 is not allowed in FIPS 140-only mode") - } d := new(digest) d.Reset() return d @@ -124,6 +121,9 @@ func (d *digest) Size() int { return Size } func (d *digest) BlockSize() int { return BlockSize } func (d *digest) Write(p []byte) (nn int, err error) { + if fips140only.Enabled { + return 0, errors.New("crypto/sha1: use of SHA-1 is not allowed in FIPS 140-only mode") + } boring.Unreachable() nn = len(p) d.len += uint64(nn) @@ -156,6 +156,10 @@ func (d *digest) Sum(in []byte) []byte { } func (d *digest) checkSum() [Size]byte { + if fips140only.Enabled { + panic("crypto/sha1: use of SHA-1 is not allowed in FIPS 140-only mode") + } + len := d.len // Padding. Add a 1 bit and 0 bits until 56 bytes mod 64. var tmp [64 + 8]byte // padding + length buffer @@ -196,6 +200,10 @@ func (d *digest) ConstantTimeSum(in []byte) []byte { } func (d *digest) constSum() [Size]byte { + if fips140only.Enabled { + panic("crypto/sha1: use of SHA-1 is not allowed in FIPS 140-only mode") + } + var length [8]byte l := d.len << 3 for i := uint(0); i < 8; i++ { @@ -262,7 +270,7 @@ func Sum(data []byte) [Size]byte { return boring.SHA1(data) } if fips140only.Enabled { - panic("crypto/sha1: use of weak SHA-1 is not allowed in FIPS 140-only mode") + panic("crypto/sha1: use of SHA-1 is not allowed in FIPS 140-only mode") } var d digest d.Reset() diff --git a/src/crypto/sha3/sha3.go b/src/crypto/sha3/sha3.go index 0f4d7ed4370b5fc120deb06ebc3118f7fef41a3a..a6c5ae55f1ff206d2cf745e8963cbe29df96577d 100644 --- a/src/crypto/sha3/sha3.go +++ b/src/crypto/sha3/sha3.go @@ -10,6 +10,7 @@ import ( "crypto" "crypto/internal/fips140/sha3" "hash" + _ "unsafe" ) func init() { @@ -100,6 +101,11 @@ type SHA3 struct { s sha3.Digest } +//go:linkname fips140hash_sha3Unwrap crypto/internal/fips140hash.sha3Unwrap +func fips140hash_sha3Unwrap(sha3 *SHA3) *sha3.Digest { + return &sha3.s +} + // New224 creates a new SHA3-224 hash. func New224() *SHA3 { return &SHA3{*sha3.New224()} diff --git a/src/encoding/json/decode_test.go b/src/encoding/json/decode_test.go index a2b462af775e3ad5c474bcd6af3247793d86abb5..8aad11b8bfbce229987df12f40947ac18a61b4db 100644 --- a/src/encoding/json/decode_test.go +++ b/src/encoding/json/decode_test.go @@ -458,10 +458,10 @@ var unmarshalTests = []struct { // Z has a "-" tag. {CaseName: Name(""), in: `{"Y": 1, "Z": 2}`, ptr: new(T), out: T{Y: 1}}, - {CaseName: Name(""), in: `{"Y": 1, "Z": 2}`, ptr: new(T), err: fmt.Errorf("json: unknown field \"Z\""), disallowUnknownFields: true}, + {CaseName: Name(""), in: `{"Y": 1, "Z": 2}`, ptr: new(T), out: T{Y: 1}, err: fmt.Errorf("json: unknown field \"Z\""), disallowUnknownFields: true}, {CaseName: Name(""), in: `{"alpha": "abc", "alphabet": "xyz"}`, ptr: new(U), out: U{Alphabet: "abc"}}, - {CaseName: Name(""), in: `{"alpha": "abc", "alphabet": "xyz"}`, ptr: new(U), err: fmt.Errorf("json: unknown field \"alphabet\""), disallowUnknownFields: true}, + {CaseName: Name(""), in: `{"alpha": "abc", "alphabet": "xyz"}`, ptr: new(U), out: U{Alphabet: "abc"}, err: fmt.Errorf("json: unknown field \"alphabet\""), disallowUnknownFields: true}, {CaseName: Name(""), in: `{"alpha": "abc"}`, ptr: new(U), out: U{Alphabet: "abc"}}, {CaseName: Name(""), in: `{"alphabet": "xyz"}`, ptr: new(U), out: U{}}, {CaseName: Name(""), in: `{"alphabet": "xyz"}`, ptr: new(U), err: fmt.Errorf("json: unknown field \"alphabet\""), disallowUnknownFields: true}, @@ -471,7 +471,7 @@ var unmarshalTests = []struct { {CaseName: Name(""), in: `[1, 2, 3+]`, err: &SyntaxError{"invalid character '+' after array element", 9}}, {CaseName: Name(""), in: `{"X":12x}`, err: &SyntaxError{"invalid character 'x' after object key:value pair", 8}, useNumber: true}, {CaseName: Name(""), in: `[2, 3`, err: &SyntaxError{msg: "unexpected end of JSON input", Offset: 5}}, - {CaseName: Name(""), in: `{"F3": -}`, ptr: new(V), out: V{F3: Number("-")}, err: &SyntaxError{msg: "invalid character '}' in numeric literal", Offset: 9}}, + {CaseName: Name(""), in: `{"F3": -}`, ptr: new(V), err: &SyntaxError{msg: "invalid character '}' in numeric literal", Offset: 9}}, // raw value errors {CaseName: Name(""), in: "\x01 42", err: &SyntaxError{"invalid character '\\x01' looking for beginning of value", 1}}, @@ -563,6 +563,7 @@ var unmarshalTests = []struct { CaseName: Name(""), in: `{"2":4}`, ptr: new(map[u8marshal]int), + out: map[u8marshal]int{}, err: errMissingU8Prefix, }, @@ -571,36 +572,42 @@ var unmarshalTests = []struct { CaseName: Name(""), in: `{"abc":"abc"}`, ptr: new(map[int]string), + out: map[int]string{}, err: &UnmarshalTypeError{Value: "number abc", Type: reflect.TypeFor[int](), Offset: 2}, }, { CaseName: Name(""), in: `{"256":"abc"}`, ptr: new(map[uint8]string), + out: map[uint8]string{}, err: &UnmarshalTypeError{Value: "number 256", Type: reflect.TypeFor[uint8](), Offset: 2}, }, { CaseName: Name(""), in: `{"128":"abc"}`, ptr: new(map[int8]string), + out: map[int8]string{}, err: &UnmarshalTypeError{Value: "number 128", Type: reflect.TypeFor[int8](), Offset: 2}, }, { CaseName: Name(""), in: `{"-1":"abc"}`, ptr: new(map[uint8]string), + out: map[uint8]string{}, err: &UnmarshalTypeError{Value: "number -1", Type: reflect.TypeFor[uint8](), Offset: 2}, }, { CaseName: Name(""), in: `{"F":{"a":2,"3":4}}`, ptr: new(map[string]map[int]int), + out: map[string]map[int]int{"F": {3: 4}}, err: &UnmarshalTypeError{Value: "number a", Type: reflect.TypeFor[int](), Offset: 7}, }, { CaseName: Name(""), in: `{"F":{"a":2,"3":4}}`, ptr: new(map[string]map[uint]int), + out: map[string]map[uint]int{"F": {3: 4}}, err: &UnmarshalTypeError{Value: "number a", Type: reflect.TypeFor[uint](), Offset: 7}, }, @@ -682,6 +689,7 @@ var unmarshalTests = []struct { CaseName: Name(""), in: `{"X": 1,"Y":2}`, ptr: new(S5), + out: S5{S8: S8{S9{Y: 2}}}, err: fmt.Errorf("json: unknown field \"X\""), disallowUnknownFields: true, }, @@ -695,6 +703,7 @@ var unmarshalTests = []struct { CaseName: Name(""), in: `{"X": 1,"Y":2}`, ptr: new(S10), + out: S10{S13: S13{S8{S9{Y: 2}}}}, err: fmt.Errorf("json: unknown field \"X\""), disallowUnknownFields: true, }, @@ -889,6 +898,7 @@ var unmarshalTests = []struct { CaseName: Name(""), in: `{"V": {"F4": {}, "F2": "hello"}}`, ptr: new(VOuter), + out: VOuter{V: V{F4: &VOuter{}}}, err: &UnmarshalTypeError{ Value: "string", Struct: "V", @@ -902,6 +912,7 @@ var unmarshalTests = []struct { CaseName: Name(""), in: `{"Level1a": "hello"}`, ptr: new(Top), + out: Top{Embed0a: &Embed0a{}}, err: &UnmarshalTypeError{ Value: "string", Struct: "Top", @@ -947,7 +958,29 @@ var unmarshalTests = []struct { "Q": 18, "extra": true }`, - ptr: new(Top), + ptr: new(Top), + out: Top{ + Level0: 1, + Embed0: Embed0{ + Level1b: 2, + Level1c: 3, + }, + Embed0a: &Embed0a{Level1a: 5, Level1b: 6}, + Embed0b: &Embed0b{Level1a: 8, Level1b: 9, Level1c: 10, Level1d: 11, Level1e: 12}, + Loop: Loop{ + Loop1: 13, + Loop2: 14, + Loop: nil, + }, + Embed0p: Embed0p{ + Point: image.Point{ + X: 15, + Y: 16, + }, + }, + Embed0q: Embed0q{Point: Point{Z: 17}}, + embed: embed{Q: 18}, + }, err: fmt.Errorf("json: unknown field \"extra\""), disallowUnknownFields: true, }, @@ -975,7 +1008,29 @@ var unmarshalTests = []struct { "Z": 17, "Q": 18 }`, - ptr: new(Top), + ptr: new(Top), + out: Top{ + Level0: 1, + Embed0: Embed0{ + Level1b: 2, + Level1c: 3, + }, + Embed0a: &Embed0a{Level1a: 5, Level1b: 6}, + Embed0b: &Embed0b{Level1a: 8, Level1b: 9, Level1c: 10, Level1d: 11, Level1e: 12}, + Loop: Loop{ + Loop1: 13, + Loop2: 14, + Loop: nil, + }, + Embed0p: Embed0p{ + Point: image.Point{ + X: 15, + Y: 16, + }, + }, + Embed0q: Embed0q{Point: Point{Z: 17}}, + embed: embed{Q: 18}, + }, err: fmt.Errorf("json: unknown field \"extra\""), disallowUnknownFields: true, }, @@ -985,12 +1040,14 @@ var unmarshalTests = []struct { CaseName: Name(""), in: `{"data":{"test1": "bob", "test2": 123}}`, ptr: new(mapStringToStringData), + out: mapStringToStringData{map[string]string{"test1": "bob", "test2": ""}}, err: &UnmarshalTypeError{Value: "number", Type: reflect.TypeFor[string](), Offset: 37, Struct: "mapStringToStringData", Field: "data"}, }, { CaseName: Name(""), in: `{"data":{"test1": 123, "test2": "bob"}}`, ptr: new(mapStringToStringData), + out: mapStringToStringData{Data: map[string]string{"test1": "", "test2": "bob"}}, err: &UnmarshalTypeError{Value: "number", Type: reflect.TypeFor[string](), Offset: 21, Struct: "mapStringToStringData", Field: "data"}, }, @@ -1024,6 +1081,7 @@ var unmarshalTests = []struct { CaseName: Name(""), in: `{"Ts": [{"Y": 1}, {"Y": 2}, {"Y": "bad-type"}]}`, ptr: new(PP), + out: PP{Ts: []T{{Y: 1}, {Y: 2}, {Y: 0}}}, err: &UnmarshalTypeError{ Value: "string", Struct: "T", @@ -1066,8 +1124,69 @@ var unmarshalTests = []struct { CaseName: Name(""), in: `{"A":"invalid"}`, ptr: new(map[string]Number), + out: map[string]Number{}, err: fmt.Errorf("json: invalid number literal, trying to unmarshal %q into Number", `"invalid"`), }, + + { + CaseName: Name(""), + in: `5`, + ptr: new(Number), + out: Number("5"), + }, + { + CaseName: Name(""), + in: `"5"`, + ptr: new(Number), + out: Number("5"), + }, + { + CaseName: Name(""), + in: `{"N":5}`, + ptr: new(struct{ N Number }), + out: struct{ N Number }{"5"}, + }, + { + CaseName: Name(""), + in: `{"N":"5"}`, + ptr: new(struct{ N Number }), + out: struct{ N Number }{"5"}, + }, + { + CaseName: Name(""), + in: `{"N":5}`, + ptr: new(struct { + N Number `json:",string"` + }), + err: fmt.Errorf("json: invalid use of ,string struct tag, trying to unmarshal unquoted value into json.Number"), + }, + { + CaseName: Name(""), + in: `{"N":"5"}`, + ptr: new(struct { + N Number `json:",string"` + }), + out: struct { + N Number `json:",string"` + }{"5"}, + }, + + // Verify that syntactic errors are immediately fatal, + // while semantic errors are lazily reported + // (i.e., allow processing to continue). + { + CaseName: Name(""), + in: `[1,2,true,4,5}`, + ptr: new([]int), + err: &SyntaxError{msg: "invalid character '}' after array element", Offset: 14}, + }, + { + CaseName: Name(""), + in: `[1,2,true,4,5]`, + ptr: new([]int), + out: []int{1, 2, 0, 4, 5}, + err: &UnmarshalTypeError{Value: "bool", Type: reflect.TypeFor[int](), Offset: 9}, + }, } func TestMarshal(t *testing.T) { @@ -1202,7 +1321,7 @@ func TestUnmarshal(t *testing.T) { var scan scanner if err := checkValid(in, &scan); err != nil { if !equalError(err, tt.err) { - t.Fatalf("%s: checkValid error: %#v", tt.Where, err) + t.Fatalf("%s: checkValid error:\n\tgot %#v\n\twant %#v", tt.Where, err, tt.err) } } if tt.ptr == nil { @@ -1236,9 +1355,11 @@ func TestUnmarshal(t *testing.T) { dec.DisallowUnknownFields() } if err := dec.Decode(v.Interface()); !equalError(err, tt.err) { - t.Fatalf("%s: Decode error:\n\tgot: %#v\n\twant: %#v", tt.Where, err, tt.err) - } else if err != nil { - return + t.Fatalf("%s: Decode error:\n\tgot: %v\n\twant: %v\n\n\tgot: %#v\n\twant: %#v", tt.Where, err, tt.err, err, tt.err) + } else if err != nil && tt.out == nil { + // Initialize tt.out during an error where there are no mutations, + // so the output is just the zero value of the input type. + tt.out = reflect.Zero(v.Elem().Type()).Interface() } if got := v.Elem().Interface(); !reflect.DeepEqual(got, tt.out) { gotJSON, _ := Marshal(got) diff --git a/src/encoding/json/stream_test.go b/src/encoding/json/stream_test.go index 32ede8cc7e6271eaf9729e4960dc94acc0f7dcc2..46f9407c881c36e00d9738fb09359eff3e0c1771 100644 --- a/src/encoding/json/stream_test.go +++ b/src/encoding/json/stream_test.go @@ -79,9 +79,9 @@ func TestEncoder(t *testing.T) { t.Fatalf("#%d.%d Encode error: %v", i, j, err) } } - if have, want := buf.String(), nlines(streamEncoded, i); have != want { + if got, want := buf.String(), nlines(streamEncoded, i); got != want { t.Errorf("encoding %d items: mismatch:", i) - diff(t, []byte(have), []byte(want)) + diff(t, []byte(got), []byte(want)) break } } @@ -148,9 +148,9 @@ func TestEncoderIndent(t *testing.T) { for _, v := range streamTest { enc.Encode(v) } - if have, want := buf.String(), streamEncodedIndent; have != want { - t.Error("Encode mismatch:") - diff(t, []byte(have), []byte(want)) + if got, want := buf.String(), streamEncodedIndent; got != want { + t.Errorf("Encode mismatch:\ngot:\n%s\n\nwant:\n%s", got, want) + diff(t, []byte(got), []byte(want)) } } diff --git a/src/encoding/json/tags_test.go b/src/encoding/json/tags_test.go index 1d2323dcee601418263b36bfa4d3cd311319fa9a..eb43ff553095c0784761830d52161a2471cfa0f3 100644 --- a/src/encoding/json/tags_test.go +++ b/src/encoding/json/tags_test.go @@ -4,9 +4,7 @@ package json -import ( - "testing" -) +import "testing" func TestTagParsing(t *testing.T) { name, opts := parseTag("field,foobar,foo") diff --git a/src/go.mod b/src/go.mod index 7a1318dcac32baf79a98d3b07390776868297889..ccfdbd8ea22d77344a1af775c66c3cd354f9d222 100644 --- a/src/go.mod +++ b/src/go.mod @@ -4,7 +4,7 @@ go 1.24 require ( golang.org/x/crypto v0.30.0 - golang.org/x/net v0.32.1-0.20241206180132-552d8ac903a1 + golang.org/x/net v0.32.1-0.20250121202134-9a960c88dd98 ) require ( diff --git a/src/go.sum b/src/go.sum index 9e661352f16e0b3c2a884507edfe4c13af63f117..4d6a33e34a4e6344d6541962dd8befdf3eba3774 100644 --- a/src/go.sum +++ b/src/go.sum @@ -1,7 +1,7 @@ golang.org/x/crypto v0.30.0 h1:RwoQn3GkWiMkzlX562cLB7OxWvjH1L8xutO2WoJcRoY= golang.org/x/crypto v0.30.0/go.mod h1:kDsLvtWBEx7MV9tJOj9bnXsPbxwJQ6csT/x4KIN4Ssk= -golang.org/x/net v0.32.1-0.20241206180132-552d8ac903a1 h1:+Yk1FZ5E+/ewA0nOO/HRYs9E4yeqpGOShuSAdzCNNoQ= -golang.org/x/net v0.32.1-0.20241206180132-552d8ac903a1/go.mod h1:CwU0IoeOlnQQWJ6ioyFrfRuomB8GKF6KbYXZVyeXNfs= +golang.org/x/net v0.32.1-0.20250121202134-9a960c88dd98 h1:36bTiCRO7f/J3t+LumnLTJDXqxsp1x6Q7754SsRD9u4= +golang.org/x/net v0.32.1-0.20250121202134-9a960c88dd98/go.mod h1:CwU0IoeOlnQQWJ6ioyFrfRuomB8GKF6KbYXZVyeXNfs= golang.org/x/sys v0.28.0 h1:Fksou7UEQUWlKvIdsqzJmUmCX3cZuD2+P3XyyzwMhlA= golang.org/x/sys v0.28.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/text v0.21.0 h1:zyQAAkrwaneQ066sspRyJaG9VNi/YJ1NfzcGB3hZ/qo= diff --git a/src/go/build/deps_test.go b/src/go/build/deps_test.go index a62a5173b9c6bc7253b1d3baf5f59c7e793f6348..e3e01077c18b17471979a6bba7ee75e3daf33e43 100644 --- a/src/go/build/deps_test.go +++ b/src/go/build/deps_test.go @@ -510,6 +510,8 @@ var depsRules = ` < crypto/internal/fips140only < crypto < crypto/subtle + < crypto/sha3 + < crypto/internal/fips140hash < crypto/cipher < crypto/internal/boring < crypto/boring @@ -520,7 +522,6 @@ var depsRules = ` crypto/sha1, crypto/sha256, crypto/sha512, - crypto/sha3, crypto/hmac, crypto/hkdf, crypto/pbkdf2, diff --git a/src/go/importer/importer.go b/src/go/importer/importer.go index 8a8fb0ec04030bda919dc7d8178bbc7ceca5c77d..54acd7e694020244a22e4809f0c151a2dbdeaa57 100644 --- a/src/go/importer/importer.go +++ b/src/go/importer/importer.go @@ -3,6 +3,13 @@ // license that can be found in the LICENSE file. // Package importer provides access to export data importers. +// +// These functions, which are mostly deprecated, date from before the +// introduction of modules in release Go 1.11. They should no longer +// be relied on except for use in test cases using small programs that +// depend only on the standard library. For reliable module-aware +// loading of type information, use the packages.Load function from +// golang.org/x/tools/go/packages. package importer import ( @@ -79,6 +86,12 @@ func For(compiler string, lookup Lookup) types.Importer { // Default returns an Importer for the compiler that built the running binary. // If available, the result implements [types.ImporterFrom]. +// +// Default may be convenient for use in the simplest of cases, but +// most clients should instead use [ForCompiler], which accepts a +// [token.FileSet] from the caller; without it, all position +// information derived from the Importer will be incorrect and +// misleading. See also the package documentation. func Default() types.Importer { return For(runtime.Compiler, nil) } diff --git a/src/go/types/api_test.go b/src/go/types/api_test.go index b686578b38278aba48bb4dc391d963c2359bd0b6..27b4ab8ea0041306de7e81dd4cc799d4cee8d554 100644 --- a/src/go/types/api_test.go +++ b/src/go/types/api_test.go @@ -19,11 +19,16 @@ import ( "testing" . "go/types" + "runtime" ) // nopos indicates an unknown position var nopos token.Pos +func defaultImporter(fset *token.FileSet) Importer { + return importer.ForCompiler(fset, runtime.Compiler, nil) +} + func mustParse(fset *token.FileSet, src string) *ast.File { f, err := parser.ParseFile(fset, pkgName(src), src, parser.ParseComments) if err != nil { @@ -33,12 +38,13 @@ func mustParse(fset *token.FileSet, src string) *ast.File { } func typecheck(src string, conf *Config, info *Info) (*Package, error) { + // TODO(adonovan): plumb this from caller. fset := token.NewFileSet() f := mustParse(fset, src) if conf == nil { conf = &Config{ Error: func(err error) {}, // collect all errors - Importer: importer.Default(), + Importer: defaultImporter(fset), } } return conf.Check(f.Name.Name, fset, []*ast.File{f}, info) @@ -1128,7 +1134,7 @@ var ( Implicits: make(map[ast.Node]Object), } var conf Config - conf.Importer = importer.Default() + conf.Importer = defaultImporter(fset) _, err := conf.Check("p", fset, []*ast.File{f}, &info) if err != nil { t.Fatal(err) diff --git a/src/go/types/call.go b/src/go/types/call.go index 200068b17600547a7eff643458360a776d3f7153..4e8dfc0d6bdc9d09e439643502f7d3e38c6edf8c 100644 --- a/src/go/types/call.go +++ b/src/go/types/call.go @@ -143,6 +143,9 @@ func (check *Checker) instantiateSignature(pos token.Pos, expr ast.Expr, typ *Si }() } + // For signatures, Checker.instance will always succeed because the type argument + // count is correct at this point (see assertion above); hence the type assertion + // to *Signature will always succeed. inst := check.instance(pos, typ, targs, nil, check.context()).(*Signature) assert(inst.TypeParams().Len() == 0) // signature is not generic anymore check.recordInstance(expr, targs, inst) diff --git a/src/go/types/check_test.go b/src/go/types/check_test.go index a10d0147da2c6516e6ee3c4f0444f343f97c9105..823525828ae3e45ac7a831b4ead9361623b290b6 100644 --- a/src/go/types/check_test.go +++ b/src/go/types/check_test.go @@ -34,7 +34,6 @@ import ( "flag" "fmt" "go/ast" - "go/importer" "go/parser" "go/scanner" "go/token" @@ -164,7 +163,7 @@ func testFilesImpl(t *testing.T, filenames []string, srcs [][]byte, manual bool, // set up typechecker var conf Config *boolFieldAddr(&conf, "_Trace") = manual && testing.Verbose() - conf.Importer = importer.Default() + conf.Importer = defaultImporter(fset) conf.Error = func(err error) { if *haltOnError { defer panic(err) diff --git a/src/go/types/eval_test.go b/src/go/types/eval_test.go index b9afb9117f80c1fe93d9c8b90bb81ff05bd2beed..49d901f6924b69e81e4d071536904c195c5170bf 100644 --- a/src/go/types/eval_test.go +++ b/src/go/types/eval_test.go @@ -9,7 +9,6 @@ package types_test import ( "fmt" "go/ast" - "go/importer" "go/parser" "go/token" "go/types" @@ -188,7 +187,7 @@ func TestEvalPos(t *testing.T) { files = append(files, file) } - conf := Config{Importer: importer.Default()} + conf := Config{Importer: defaultImporter(fset)} pkg, err := conf.Check("p", fset, files, nil) if err != nil { t.Fatal(err) @@ -257,7 +256,7 @@ func f(a int, s string) S { t.Fatal(err) } - conf := Config{Importer: importer.Default()} + conf := Config{Importer: defaultImporter(fset)} pkg, err := conf.Check("p", fset, []*ast.File{f}, nil) if err != nil { t.Fatal(err) diff --git a/src/go/types/example_test.go b/src/go/types/example_test.go index 279771121aaaaeace78aed772de77fb0ed0ce4df..d8e5de7476c81bc7e21b659e6bc6610239509c42 100644 --- a/src/go/types/example_test.go +++ b/src/go/types/example_test.go @@ -19,7 +19,6 @@ import ( "fmt" "go/ast" "go/format" - "go/importer" "go/parser" "go/token" "go/types" @@ -57,7 +56,7 @@ func Unused() { {}; {{ var x int; _ = x }} } // make sure empty block scopes get // Type-check a package consisting of these files. // Type information for the imported "fmt" package // comes from $GOROOT/pkg/$GOOS_$GOOARCH/fmt.a. - conf := types.Config{Importer: importer.Default()} + conf := types.Config{Importer: defaultImporter(fset)} pkg, err := conf.Check("temperature", fset, files, nil) if err != nil { log.Fatal(err) @@ -126,7 +125,7 @@ type I interface { m() byte } // Type-check a package consisting of this file. // Type information for the imported packages // comes from $GOROOT/pkg/$GOOS_$GOOARCH/fmt.a. - conf := types.Config{Importer: importer.Default()} + conf := types.Config{Importer: defaultImporter(fset)} pkg, err := conf.Check("temperature", fset, []*ast.File{f}, nil) if err != nil { log.Fatal(err) diff --git a/src/go/types/initorder.go b/src/go/types/initorder.go index 7625c20667499435b96899318fa38fc4a4f4461d..adf96fe718f13d27906eff4dfd66a5a74ad4440c 100644 --- a/src/go/types/initorder.go +++ b/src/go/types/initorder.go @@ -13,6 +13,7 @@ import ( "fmt" . "internal/types/errors" "slices" + "sort" ) // initOrder computes the Info.InitOrder for package variables. @@ -142,7 +143,16 @@ func findPath(objMap map[Object]*declInfo, from, to Object, seen map[Object]bool } seen[from] = true + // sort deps for deterministic result + var deps []Object for d := range objMap[from].deps { + deps = append(deps, d) + } + sort.Slice(deps, func(i, j int) bool { + return deps[i].order() < deps[j].order() + }) + + for _, d := range deps { if d == to { return []Object{d} } diff --git a/src/go/types/instantiate.go b/src/go/types/instantiate.go index 48eef7ca76c18733d2e45feb6d66ba81bdd9a4c7..4b36312f96e75e8a2864008a55e8c4b44421a763 100644 --- a/src/go/types/instantiate.go +++ b/src/go/types/instantiate.go @@ -77,7 +77,8 @@ func Instantiate(ctxt *Context, orig Type, targs []Type, validate bool) (Type, e // instance instantiates the given original (generic) function or type with the // provided type arguments and returns the resulting instance. If an identical // instance exists already in the given contexts, it returns that instance, -// otherwise it creates a new one. +// otherwise it creates a new one. If there is an error (such as wrong number +// of type arguments), the result is Typ[Invalid]. // // If expanding is non-nil, it is the Named instance type currently being // expanded. If ctxt is non-nil, it is the context associated with the current @@ -136,9 +137,13 @@ func (check *Checker) instance(pos token.Pos, orig genericType, targs []Type, ex assert(expanding == nil) // Alias instances cannot be reached from Named types } + // verify type parameter count (see go.dev/issue/71198 for a test case) tparams := orig.TypeParams() - // TODO(gri) investigate if this is needed (type argument and parameter count seem to be correct here) - if !check.validateTArgLen(pos, orig.String(), tparams.Len(), len(targs)) { + if !check.validateTArgLen(pos, orig.obj.Name(), tparams.Len(), len(targs)) { + // TODO(gri) Consider returning a valid alias instance with invalid + // underlying (aliased) type to match behavior of *Named + // types. Then this function will never return an invalid + // result. return Typ[Invalid] } if tparams.Len() == 0 { diff --git a/src/go/types/issues_test.go b/src/go/types/issues_test.go index 3eb34cf2d0dee520350d72b2911ec9a7e9a76f21..f2c63f16f9aba6bfedc7dc1c96bbdaebc40041ed 100644 --- a/src/go/types/issues_test.go +++ b/src/go/types/issues_test.go @@ -9,7 +9,6 @@ package types_test import ( "fmt" "go/ast" - "go/importer" "go/parser" "go/token" "internal/testenv" @@ -291,7 +290,7 @@ func TestIssue25627(t *testing.T) { } { f := mustParse(fset, prefix+src) - cfg := Config{Importer: importer.Default(), Error: func(err error) {}} + cfg := Config{Importer: defaultImporter(fset), Error: func(err error) {}} info := &Info{Types: make(map[ast.Expr]TypeAndValue)} _, err := cfg.Check(f.Name.Name, fset, []*ast.File{f}, info) if err != nil { @@ -595,7 +594,11 @@ var _ T = template /* ERRORx "cannot use.*text/template.* as T value" */.Templat ) a := mustTypecheck(asrc, nil, nil) - imp := importHelper{pkg: a, fallback: importer.Default()} + imp := importHelper{ + pkg: a, + // TODO(adonovan): use same FileSet as mustTypecheck. + fallback: defaultImporter(token.NewFileSet()), + } withImporter := func(cfg *Config) { cfg.Importer = imp diff --git a/src/go/types/lookup_test.go b/src/go/types/lookup_test.go index d3ca58b9fa16f217d3f489e78c3b3aa2b0414438..e90a2ec89a32dbc57b2bc83c37c9d486c7552cc8 100644 --- a/src/go/types/lookup_test.go +++ b/src/go/types/lookup_test.go @@ -5,7 +5,6 @@ package types_test import ( - "go/importer" "go/token" "path/filepath" "runtime" @@ -28,7 +27,7 @@ func BenchmarkLookupFieldOrMethod(b *testing.B) { } conf := Config{ - Importer: importer.Default(), + Importer: defaultImporter(fset), } pkg, err := conf.Check("http", fset, files, nil) diff --git a/src/go/types/mono_test.go b/src/go/types/mono_test.go index ccab846c6dd6e343b846deaa8f6947c9faca03fe..d1f19ac5582fa1d6100861ee6c629f05f02393a1 100644 --- a/src/go/types/mono_test.go +++ b/src/go/types/mono_test.go @@ -7,7 +7,6 @@ package types_test import ( "errors" "fmt" - "go/importer" "go/types" "strings" "testing" @@ -19,7 +18,7 @@ func checkMono(t *testing.T, body string) error { var buf strings.Builder conf := types.Config{ Error: func(err error) { fmt.Fprintln(&buf, err) }, - Importer: importer.Default(), + Importer: defaultImporter(fset), // TODO(adonovan): use same FileSet as typecheck } typecheck(src, &conf, nil) if buf.Len() == 0 { diff --git a/src/go/types/resolver_test.go b/src/go/types/resolver_test.go index a83f1344de91c03f848c6890b010f49f4c04fdff..680ee69c97f4cc2828e3d0691df40f043053e683 100644 --- a/src/go/types/resolver_test.go +++ b/src/go/types/resolver_test.go @@ -7,7 +7,6 @@ package types_test import ( "fmt" "go/ast" - "go/importer" "go/token" "internal/testenv" "slices" @@ -17,6 +16,7 @@ import ( ) type resolveTestImporter struct { + fset *token.FileSet importer ImporterFrom imported map[string]bool } @@ -30,7 +30,7 @@ func (imp *resolveTestImporter) ImportFrom(path, srcDir string, mode ImportMode) panic("mode must be 0") } if imp.importer == nil { - imp.importer = importer.Default().(ImporterFrom) + imp.importer = defaultImporter(fset).(ImporterFrom) imp.imported = make(map[string]bool) } pkg, err := imp.importer.ImportFrom(path, srcDir, mode) @@ -124,7 +124,7 @@ func TestResolveIdents(t *testing.T) { } // resolve and type-check package AST - importer := new(resolveTestImporter) + importer := &resolveTestImporter{fset: fset} conf := Config{Importer: importer} uses := make(map[*ast.Ident]Object) defs := make(map[*ast.Ident]Object) diff --git a/src/go/types/self_test.go b/src/go/types/self_test.go index 27fa75652a5204a3663a2eb89311a442416a1898..b4cc6286a18f8108676b7ac2e507f54d56c0fd60 100644 --- a/src/go/types/self_test.go +++ b/src/go/types/self_test.go @@ -6,7 +6,6 @@ package types_test import ( "go/ast" - "go/importer" "go/parser" "go/token" "internal/testenv" @@ -27,7 +26,7 @@ func TestSelf(t *testing.T) { t.Fatal(err) } - conf := Config{Importer: importer.Default()} + conf := Config{Importer: defaultImporter(fset)} _, err = conf.Check("go/types", fset, files, nil) if err != nil { t.Fatal(err) @@ -82,7 +81,7 @@ func runbench(b *testing.B, path string, ignoreFuncBodies, writeInfo bool) { for i := 0; i < b.N; i++ { conf := Config{ IgnoreFuncBodies: ignoreFuncBodies, - Importer: importer.Default(), + Importer: defaultImporter(fset), } var info *Info if writeInfo { diff --git a/src/go/types/sizes_test.go b/src/go/types/sizes_test.go index 825bc1f9f52ada9fd94f02bbdf9a2ad3b80debc1..157faf87d4c57504d89843aade55d677682b0641 100644 --- a/src/go/types/sizes_test.go +++ b/src/go/types/sizes_test.go @@ -8,7 +8,7 @@ package types_test import ( "go/ast" - "go/importer" + "go/token" "go/types" "internal/testenv" "testing" @@ -87,7 +87,8 @@ const _ = unsafe.Offsetof(struct{ x int64 }{}.x) ` info := types.Info{Types: make(map[ast.Expr]types.TypeAndValue)} conf := types.Config{ - Importer: importer.Default(), + // TODO(adonovan): use same FileSet as mustTypecheck. + Importer: defaultImporter(token.NewFileSet()), Sizes: &types.StdSizes{WordSize: 8, MaxAlign: 8}, } mustTypecheck(src, &conf, &info) @@ -117,7 +118,8 @@ var s struct { for _, arch := range []string{"386", "amd64"} { t.Run(arch, func(t *testing.T) { conf := types.Config{ - Importer: importer.Default(), + // TODO(adonovan): use same FileSet as findStructTypeConfig. + Importer: defaultImporter(token.NewFileSet()), Sizes: types.SizesFor("gc", arch), } ts := findStructTypeConfig(t, src, &conf) @@ -188,7 +190,11 @@ func TestGCSizes(t *testing.T) { tc := tc t.Run(tc.name, func(t *testing.T) { t.Parallel() - conf := types.Config{Importer: importer.Default(), Sizes: types.SizesFor("gc", "amd64")} + conf := types.Config{ + // TODO(adonovan): use same FileSet as mustTypecheck. + Importer: defaultImporter(token.NewFileSet()), + Sizes: types.SizesFor("gc", "amd64"), + } mustTypecheck(tc.src, &conf, nil) }) } diff --git a/src/go/types/stmt.go b/src/go/types/stmt.go index d3223f3b92395f5c81b77500b6d4d0cb019502f8..de3d01e8dd2b43f0494da54816802caaffb55adf 100644 --- a/src/go/types/stmt.go +++ b/src/go/types/stmt.go @@ -1075,8 +1075,13 @@ func rangeKeyVal(typ Type, allowVersion func(goVersion) bool) (key, val Type, ca return bad("func must be func(yield func(...) bool): argument is not func") case cb.Params().Len() > 2: return bad("func must be func(yield func(...) bool): yield func has too many parameters") - case cb.Results().Len() != 1 || !isBoolean(cb.Results().At(0).Type()): - return bad("func must be func(yield func(...) bool): yield func does not return bool") + case cb.Results().Len() != 1 || !Identical(cb.Results().At(0).Type(), universeBool): + // see go.dev/issues/71131, go.dev/issues/71164 + if cb.Results().Len() == 1 && isBoolean(cb.Results().At(0).Type()) { + return bad("func must be func(yield func(...) bool): yield func returns user-defined boolean, not bool") + } else { + return bad("func must be func(yield func(...) bool): yield func does not return bool") + } } assert(cb.Recv() == nil) // determine key and value types, if any diff --git a/src/go/types/testdata/manual.go b/src/go/types/testdata/manual.go index d8f312f61d58c50e54cb8e70f9f80dc0a98c05a1..825ab50f92de4907d91e17bd872c0a198e13c840 100644 --- a/src/go/types/testdata/manual.go +++ b/src/go/types/testdata/manual.go @@ -1,4 +1,4 @@ -// Copyright 2024 The Go Authors. All rights reserved. +// 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. diff --git a/src/go/types/typexpr.go b/src/go/types/typexpr.go index 5bcbc2d1d3b1143e2ee1756c66a7bc1a2c9a69d1..7928ed8ef368de6363b5376faf9c633fc1808b9b 100644 --- a/src/go/types/typexpr.go +++ b/src/go/types/typexpr.go @@ -419,11 +419,6 @@ func setDefType(def *TypeName, typ Type) { if def != nil { switch t := def.typ.(type) { case *Alias: - // t.fromRHS should always be set, either to an invalid type - // in the beginning, or to typ in certain cyclic declarations. - if t.fromRHS != Typ[Invalid] && t.fromRHS != typ { - panic(sprintf(nil, nil, true, "t.fromRHS = %s, typ = %s\n", t.fromRHS, typ)) - } t.fromRHS = typ case *Basic: assert(t == Typ[Invalid]) @@ -471,9 +466,14 @@ func (check *Checker) instantiatedType(ix *indexedExpr, def *TypeName) (res Type } // create instance - // The instance is not generic anymore as it has type arguments, but it still - // satisfies the genericType interface because it has type parameters, too. - inst := check.instance(ix.Pos(), gtyp, targs, nil, check.context()).(genericType) + // The instance is not generic anymore as it has type arguments, but unless + // instantiation failed, it still satisfies the genericType interface because + // it has type parameters, too. + ityp := check.instance(ix.Pos(), gtyp, targs, nil, check.context()) + inst, _ := ityp.(genericType) + if inst == nil { + return Typ[Invalid] + } // For Named types, orig.tparams may not be set up, so we need to do expansion later. check.later(func() { diff --git a/src/go/types/universe.go b/src/go/types/universe.go index 09b882ce055551c6d3529926707780957bc76163..750a368278ab63a2667335d8d4e962857babea1f 100644 --- a/src/go/types/universe.go +++ b/src/go/types/universe.go @@ -24,6 +24,7 @@ var Unsafe *Package var ( universeIota Object + universeBool Type universeByte Type // uint8 alias, but has name "byte" universeRune Type // int32 alias, but has name "rune" universeAnyNoAlias *TypeName @@ -278,6 +279,7 @@ func init() { defPredeclaredFuncs() universeIota = Universe.Lookup("iota") + universeBool = Universe.Lookup("bool").Type() universeByte = Universe.Lookup("byte").Type() universeRune = Universe.Lookup("rune").Type() universeError = Universe.Lookup("error").Type() diff --git a/src/internal/coverage/cfile/testsupport.go b/src/internal/coverage/cfile/testsupport.go index 3594b32aee1a8df4bb740c2c05be9a21eedfd4af..adab47fd212d1e5e0983116327e115085f8de9d7 100644 --- a/src/internal/coverage/cfile/testsupport.go +++ b/src/internal/coverage/cfile/testsupport.go @@ -109,7 +109,7 @@ func ProcessCoverTestDir(dir string, cfile string, cm string, cpkg string, w io. // Emit text output. if tf != nil { - if err := ts.cf.EmitTextual(tf); err != nil { + if err := ts.cf.EmitTextual(selpkgs, tf); err != nil { return err } tfClosed = true diff --git a/src/internal/coverage/cformat/fmt_test.go b/src/internal/coverage/cformat/fmt_test.go index d296939d5cd20ca6cc31f3b0cc81f53ee8ebe652..a26de964c435383ad54ec6a9245bd652f6a65c09 100644 --- a/src/internal/coverage/cformat/fmt_test.go +++ b/src/internal/coverage/cformat/fmt_test.go @@ -47,8 +47,8 @@ func TestBasics(t *testing.T) { fm.AddUnit("lit.go", "f3", true, u, 0) } - var b1, b2, b3, b4 strings.Builder - if err := fm.EmitTextual(&b1); err != nil { + var b1, b2, b3, b4, b5 strings.Builder + if err := fm.EmitTextual(nil, &b1); err != nil { t.Fatalf("EmitTextual returned %v", err) } wantText := strings.TrimSpace(` @@ -64,6 +64,18 @@ lit.go:99.0,100.0 1 0`) t.Errorf("emit text: got:\n%s\nwant:\n%s\n", gotText, wantText) } + selected := []string{"my/pack2"} + if err := fm.EmitTextual(selected, &b5); err != nil { + t.Fatalf("EmitTextual returned %v", err) + } + wantText = strings.TrimSpace(` +mode: atomic +lit.go:99.0,100.0 1 0`) + gotText = strings.TrimSpace(b5.String()) + if wantText != gotText { + t.Errorf("emit text: got:\n%s\nwant:\n%s\n", gotText, wantText) + } + // Percent output with no aggregation. noCoverPkg := "" if err := fm.EmitPercent(&b2, nil, noCoverPkg, false, false); err != nil { diff --git a/src/internal/coverage/cformat/format.go b/src/internal/coverage/cformat/format.go index 4df0e70b81ca6e34232775f126cb8b73f04d68c2..01d3109e31fd9d1b737bd5ce0fd6bed28548ac73 100644 --- a/src/internal/coverage/cformat/format.go +++ b/src/internal/coverage/cformat/format.go @@ -24,7 +24,7 @@ package cformat // } // } // myformatter.EmitPercent(os.Stdout, nil, "", true, true) -// myformatter.EmitTextual(somefile) +// myformatter.EmitTextual(nil, somefile) // // These apis are linked into tests that are built with "-cover", and // called at the end of test execution to produce text output or @@ -38,6 +38,7 @@ import ( "io" "maps" "slices" + "sort" "strings" "text/tabwriter" ) @@ -163,20 +164,31 @@ func (p *pstate) sortUnits(units []extcu) { }) } -// EmitTextual writes the accumulated coverage data in the legacy -// cmd/cover text format to the writer 'w'. We sort the data items by +// EmitTextual writes the accumulated coverage data for 'pkgs' in the legacy +// cmd/cover text format to the writer 'w'; if pkgs is empty, text output +// is emitted for all packages recorded. We sort the data items by // importpath, source file, and line number before emitting (this sorting // is not explicitly mandated by the format, but seems like a good idea // for repeatable/deterministic dumps). -func (fm *Formatter) EmitTextual(w io.Writer) error { +func (fm *Formatter) EmitTextual(pkgs []string, w io.Writer) error { if fm.cm == coverage.CtrModeInvalid { panic("internal error, counter mode unset") } + if len(pkgs) == 0 { + pkgs = make([]string, 0, len(fm.pm)) + for importpath := range fm.pm { + pkgs = append(pkgs, importpath) + } + } if _, err := fmt.Fprintf(w, "mode: %s\n", fm.cm.String()); err != nil { return err } - for _, importpath := range slices.Sorted(maps.Keys(fm.pm)) { + sort.Strings(pkgs) + for _, importpath := range pkgs { p := fm.pm[importpath] + if p == nil { + continue + } units := make([]extcu, 0, len(p.unitTable)) for u := range p.unitTable { units = append(units, u) diff --git a/src/internal/godebug/godebug_test.go b/src/internal/godebug/godebug_test.go index 692963035605051922e24a78a5c79c44eddf0c30..fe1e67225c9928bd66f0aa2b467ca8ef77cb5679 100644 --- a/src/internal/godebug/godebug_test.go +++ b/src/internal/godebug/godebug_test.go @@ -109,6 +109,9 @@ func TestCmdBisect(t *testing.T) { var want []string src, err := os.ReadFile("godebug_test.go") + if err != nil { + t.Fatal(err) + } for i, line := range strings.Split(string(src), "\n") { if strings.Contains(line, "BISECT"+" "+"BUG") { want = append(want, fmt.Sprintf("godebug_test.go:%d", i+1)) diff --git a/src/internal/goexperiment/flags.go b/src/internal/goexperiment/flags.go index 31b3d0315b64f8e30f1e8d0ecca54852428ddb53..948ed5c802801cab0f577c98bf8e38c779b68bc6 100644 --- a/src/internal/goexperiment/flags.go +++ b/src/internal/goexperiment/flags.go @@ -51,7 +51,7 @@ package goexperiment // tags, experiments use the strings.ToLower of their field name. // // For the baseline experimental configuration, see -// objabi.experimentBaseline. +// [internal/buildcfg.ParseGOEXPERIMENT]. // // If you change this struct definition, run "go generate". type Flags struct { diff --git a/src/internal/runtime/maps/map_swiss_test.go b/src/internal/runtime/maps/map_swiss_test.go index 4e02f3e66076b614e411390bfcb7065995efb8fa..6da006413ac61d20e5760789f68caf477fe8dae4 100644 --- a/src/internal/runtime/maps/map_swiss_test.go +++ b/src/internal/runtime/maps/map_swiss_test.go @@ -50,7 +50,6 @@ func TestTableGroupCount(t *testing.T) { var testCases = []struct { n int // n is the number of map elements escape mapCase // expected values for escaping map - // TODO(go.dev/issue/54766): implement stack allocated maps }{ { n: -(1 << 30), diff --git a/src/internal/types/testdata/fixedbugs/issue71131.go b/src/internal/types/testdata/fixedbugs/issue71131.go new file mode 100644 index 0000000000000000000000000000000000000000..8e7575b02828ded3f25ebbf1f1f63a2abb90a155 --- /dev/null +++ b/src/internal/types/testdata/fixedbugs/issue71131.go @@ -0,0 +1,15 @@ +// 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. + +package p + +func _() { + type Bool bool + for range func /* ERROR "yield func returns user-defined boolean, not bool" */ (func() Bool) {} { + } + for range func /* ERROR "yield func returns user-defined boolean, not bool" */ (func(int) Bool) {} { + } + for range func /* ERROR "yield func returns user-defined boolean, not bool" */ (func(int, string) Bool) {} { + } +} diff --git a/src/internal/types/testdata/fixedbugs/issue71198.go b/src/internal/types/testdata/fixedbugs/issue71198.go new file mode 100644 index 0000000000000000000000000000000000000000..479f8e2b0c4e64e6c729df370feba36d121857be --- /dev/null +++ b/src/internal/types/testdata/fixedbugs/issue71198.go @@ -0,0 +1,16 @@ +// -gotypesalias=1 + +// 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. + +package p + +type A[_ any] = any + +// This must not panic; also the error message must match the style for non-alias types, below. +func _[_ A /* ERROR "too many type arguments for type A: have 2, want 1" */ [int, string]]() {} + +type T[_ any] any + +func _[_ T /* ERROR "too many type arguments for type T: have 2, want 1" */ [int, string]]() {} diff --git a/src/internal/types/testdata/fixedbugs/issue71284.go b/src/internal/types/testdata/fixedbugs/issue71284.go new file mode 100644 index 0000000000000000000000000000000000000000..4b73087a78a19131ba355971b93d98adf3b84380 --- /dev/null +++ b/src/internal/types/testdata/fixedbugs/issue71284.go @@ -0,0 +1,10 @@ +// 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. + +package A + +type ( + _ = A + A /* ERROR "invalid recursive type: A refers to itself" */ = A +) diff --git a/src/internal/types/testdata/spec/range.go b/src/internal/types/testdata/spec/range.go index 52d1e70382bd2e77f8fbad9ef6b6fe6f6eca597c..c0f579479f60ece6511bae00dee6d68092b23253 100644 --- a/src/internal/types/testdata/spec/range.go +++ b/src/internal/types/testdata/spec/range.go @@ -5,7 +5,7 @@ package p type MyInt int32 -type MyBool bool +type MyBool = bool // TODO(gri) remove alias declaration - see go.dev/issues/71131, go.dev/issues/71164 type MyString string type MyFunc1 func(func(int) bool) type MyFunc2 func(int) bool diff --git a/src/net/http/h2_bundle.go b/src/net/http/h2_bundle.go index 46a2b79231e565cd717a4d1ce54477c06ddb2c94..22f013f1d48407cf3ffc6478f5522dcec4783283 100644 --- a/src/net/http/h2_bundle.go +++ b/src/net/http/h2_bundle.go @@ -3509,11 +3509,19 @@ func http2canonicalHeader(v string) string { } var ( - http2VerboseLogs bool - http2logFrameWrites bool - http2logFrameReads bool - http2inTests bool - http2disableExtendedConnectProtocol bool + http2VerboseLogs bool + http2logFrameWrites bool + http2logFrameReads bool + http2inTests bool + + // Enabling extended CONNECT by causes browsers to attempt to use + // WebSockets-over-HTTP/2. This results in problems when the server's websocket + // package doesn't support extended CONNECT. + // + // Disable extended CONNECT by default for now. + // + // Issue #71128. + http2disableExtendedConnectProtocol = true ) func init() { @@ -3526,8 +3534,8 @@ func init() { http2logFrameWrites = true http2logFrameReads = true } - if strings.Contains(e, "http2xconnect=0") { - http2disableExtendedConnectProtocol = true + if strings.Contains(e, "http2xconnect=1") { + http2disableExtendedConnectProtocol = false } } @@ -9500,10 +9508,6 @@ func http2validateHeaders(hdrs Header) string { var http2errNilRequestURL = errors.New("http2: Request.URI is nil") -func http2isNormalConnect(req *Request) bool { - return req.Method == "CONNECT" && req.Header.Get(":protocol") == "" -} - // requires cc.wmu be held. func (cc *http2ClientConn) encodeHeaders(req *Request, addGzipHeader bool, trailers string, contentLength int64) ([]byte, error) { cc.hbuf.Reset() @@ -9523,8 +9527,17 @@ func (cc *http2ClientConn) encodeHeaders(req *Request, addGzipHeader bool, trail return nil, errors.New("http2: invalid Host header") } + // isNormalConnect is true if this is a non-extended CONNECT request. + isNormalConnect := false + protocol := req.Header.Get(":protocol") + if req.Method == "CONNECT" && protocol == "" { + isNormalConnect = true + } else if protocol != "" && req.Method != "CONNECT" { + return nil, errors.New("http2: invalid :protocol header in non-CONNECT request") + } + var path string - if !http2isNormalConnect(req) { + if !isNormalConnect { path = req.URL.RequestURI() if !http2validPseudoPath(path) { orig := path @@ -9561,10 +9574,13 @@ func (cc *http2ClientConn) encodeHeaders(req *Request, addGzipHeader bool, trail m = MethodGet } f(":method", m) - if !http2isNormalConnect(req) { + if !isNormalConnect { f(":path", path) f(":scheme", req.URL.Scheme) } + if protocol != "" { + f(":protocol", protocol) + } if trailers != "" { f("trailer", trailers) } @@ -9621,6 +9637,9 @@ func (cc *http2ClientConn) encodeHeaders(req *Request, addGzipHeader bool, trail } } continue + } else if k == ":protocol" { + // :protocol pseudo-header was already sent above. + continue } for _, v := range vv { diff --git a/src/os/readfrom_linux_test.go b/src/os/readfrom_linux_test.go index cc0322882b1305013ce8797f8e9f17f39fc1cc87..d33f9cf9c984f62ba2e8ec2fec94a839f2df905e 100644 --- a/src/os/readfrom_linux_test.go +++ b/src/os/readfrom_linux_test.go @@ -242,13 +242,12 @@ func testSpliceToTTY(t *testing.T, proto string, size int64) { } var ( - copyFileTests = []copyFileTestFunc{newCopyFileRangeTest, newSendfileOverCopyFileRangeTest} - copyFileHooks = []copyFileTestHook{hookCopyFileRange, hookSendFileOverCopyFileRange} + copyFileTests = []copyFileTestFunc{newCopyFileRangeTest} + copyFileHooks = []copyFileTestHook{hookCopyFileRange} ) func testCopyFiles(t *testing.T, size, limit int64) { testCopyFileRange(t, size, limit) - testSendfileOverCopyFileRange(t, size, limit) } func testCopyFileRange(t *testing.T, size int64, limit int64) { @@ -256,11 +255,6 @@ func testCopyFileRange(t *testing.T, size int64, limit int64) { testCopyFile(t, dst, src, data, hook, limit, name) } -func testSendfileOverCopyFileRange(t *testing.T, size int64, limit int64) { - dst, src, data, hook, name := newSendfileOverCopyFileRangeTest(t, size) - testCopyFile(t, dst, src, data, hook, limit, name) -} - // newCopyFileRangeTest initializes a new test for copy_file_range. // // It hooks package os' call to poll.CopyFileRange and returns the hook, @@ -276,20 +270,6 @@ func newCopyFileRangeTest(t *testing.T, size int64) (dst, src *File, data []byte return } -// newSendfileOverCopyFileRangeTest initializes a new test for sendfile over copy_file_range. -// It hooks package os' call to poll.SendFile and returns the hook, -// so it can be inspected. -func newSendfileOverCopyFileRangeTest(t *testing.T, size int64) (dst, src *File, data []byte, hook *copyFileHook, name string) { - t.Helper() - - name = "newSendfileOverCopyFileRangeTest" - - dst, src, data = newCopyFileTest(t, size) - hook, _ = hookSendFileOverCopyFileRange(t) - - return -} - // newSpliceFileTest initializes a new test for splice. // // It creates source sockets and destination file, and populates the source sockets @@ -342,34 +322,6 @@ func hookCopyFileRange(t *testing.T) (hook *copyFileHook, name string) { return } -func hookSendFileOverCopyFileRange(t *testing.T) (*copyFileHook, string) { - return hookSendFileTB(t), "hookSendFileOverCopyFileRange" -} - -func hookSendFileTB(tb testing.TB) *copyFileHook { - // Disable poll.CopyFileRange to force the fallback to poll.SendFile. - originalCopyFileRange := *PollCopyFileRangeP - *PollCopyFileRangeP = func(dst, src *poll.FD, remain int64) (written int64, handled bool, err error) { - return 0, false, nil - } - - hook := new(copyFileHook) - orig := poll.TestHookDidSendFile - tb.Cleanup(func() { - *PollCopyFileRangeP = originalCopyFileRange - poll.TestHookDidSendFile = orig - }) - poll.TestHookDidSendFile = func(dstFD *poll.FD, src int, written int64, err error, handled bool) { - hook.called = true - hook.dstfd = dstFD.Sysfd - hook.srcfd = src - hook.written = written - hook.err = err - hook.handled = handled - } - return hook -} - func hookSpliceFile(t *testing.T) *spliceFileHook { h := new(spliceFileHook) h.install() diff --git a/src/os/readfrom_sendfile_test.go b/src/os/readfrom_sendfile_test.go index dbe1603bd15a499622703c25863a76cabb83e64b..86ef71ee0293c23cc6f2517990b3c74a940f5809 100644 --- a/src/os/readfrom_sendfile_test.go +++ b/src/os/readfrom_sendfile_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 linux || solaris +//go:build solaris package os_test diff --git a/src/os/root_test.go b/src/os/root_test.go index b461ee220804f9bdc02c9046bc3a922d3758fbde..cbb985b2ceeb484484c04ea3d7f47baac553bc51 100644 --- a/src/os/root_test.go +++ b/src/os/root_test.go @@ -1077,6 +1077,10 @@ func TestRootConcurrentClose(t *testing.T) { first = false } f.Close() + if runtime.GOARCH == "wasm" { + // TODO(go.dev/issue/71134) can lead to goroutine starvation. + runtime.Gosched() + } } }() if err := <-ch; err != nil { diff --git a/src/os/zero_copy_linux.go b/src/os/zero_copy_linux.go index 27a0882560ae2a75223e74088a5c5149679f98f9..9d666a3c7911177658f9c0a7f6f23114a84abc4c 100644 --- a/src/os/zero_copy_linux.go +++ b/src/os/zero_copy_linux.go @@ -40,17 +40,16 @@ func (f *File) writeTo(w io.Writer) (written int64, handled bool, err error) { } func (f *File) readFrom(r io.Reader) (written int64, handled bool, err error) { - // Neither copy_file_range(2)/sendfile(2) nor splice(2) supports destinations opened with + // Neither copy_file_range(2) nor splice(2) supports destinations opened with // O_APPEND, so don't bother to try zero-copy with these system calls. // // Visit https://man7.org/linux/man-pages/man2/copy_file_range.2.html#ERRORS and - // https://man7.org/linux/man-pages/man2/sendfile.2.html#ERRORS and // https://man7.org/linux/man-pages/man2/splice.2.html#ERRORS for details. if f.appendMode { return 0, false, nil } - written, handled, err = f.copyFile(r) + written, handled, err = f.copyFileRange(r) if handled { return } @@ -87,7 +86,7 @@ func (f *File) spliceToFile(r io.Reader) (written int64, handled bool, err error return written, handled, wrapSyscallError("splice", err) } -func (f *File) copyFile(r io.Reader) (written int64, handled bool, err error) { +func (f *File) copyFileRange(r io.Reader) (written int64, handled bool, err error) { var ( remain int64 lr *io.LimitedReader @@ -116,44 +115,7 @@ func (f *File) copyFile(r io.Reader) (written int64, handled bool, err error) { if lr != nil { lr.N -= written } - - if handled { - return written, handled, wrapSyscallError("copy_file_range", err) - } - - // If fd_in and fd_out refer to the same file and the source and target ranges overlap, - // copy_file_range(2) just returns EINVAL error. poll.CopyFileRange will ignore that - // error and act like it didn't call copy_file_range(2). Then the caller will fall back - // to generic copy, which results in doubling the content in the file. - // By contrast, sendfile(2) allows this kind of overlapping and works like a memmove, - // in this case the file content will remain the same after copying, which is not what we want. - // Thus, we just bail out here and leave it to generic copy when it's a file copying itself. - if f.pfd.Sysfd == src.pfd.Sysfd { - return 0, false, nil - } - - sc, err := src.SyscallConn() - if err != nil { - return - } - - // We can employ sendfile(2) when copy_file_range(2) fails to handle the copy. - // sendfile(2) enabled file-to-file copying since Linux 2.6.33 and Go requires - // Linux 3.2 or later, so we're good to go. - // Check out https://man7.org/linux/man-pages/man2/sendfile.2.html#DESCRIPTION for more details. - rerr := sc.Read(func(fd uintptr) bool { - written, err, handled = poll.SendFile(&f.pfd, int(fd), remain) - return true - }) - if lr != nil { - lr.N -= written - } - - if err == nil { - err = rerr - } - - return written, handled, wrapSyscallError("sendfile", err) + return written, handled, wrapSyscallError("copy_file_range", err) } // getPollFDAndNetwork tries to get the poll.FD and network type from the given interface diff --git a/src/reflect/map_noswiss.go b/src/reflect/map_noswiss.go index eb0a52a3902f6f963e91c9ac3028f87834795fb7..19696a4f4bd858bff83e93718022adfda118cd5e 100644 --- a/src/reflect/map_noswiss.go +++ b/src/reflect/map_noswiss.go @@ -17,6 +17,14 @@ type mapType struct { abi.OldMapType } +// Pushed from runtime. + +//go:noescape +func mapiterinit(t *abi.Type, m unsafe.Pointer, it *hiter) + +//go:noescape +func mapiternext(it *hiter) + func (t *rtype) Key() Type { if t.Kind() != Map { panic("reflect: Key of non-map type " + t.String()) diff --git a/src/reflect/map_swiss.go b/src/reflect/map_swiss.go index 75dcb117dfd2926a6a7a894132bb17ad9106675e..2eac51e57dba5df282e507c4700f6be891580ae8 100644 --- a/src/reflect/map_swiss.go +++ b/src/reflect/map_swiss.go @@ -8,14 +8,16 @@ package reflect import ( "internal/abi" + "internal/race" "internal/runtime/maps" + "internal/runtime/sys" "unsafe" ) // mapType represents a map type. -type mapType struct { - abi.SwissMapType -} +// +// TODO(prattmic): Only used within this file, could be cleaned up. +type mapType = abi.SwissMapType func (t *rtype) Key() Type { if t.Kind() != Map { @@ -176,6 +178,31 @@ func (v Value) MapIndex(key Value) Value { return copyVal(typ, fl, e) } +// Equivalent to runtime.mapIterStart. +// +//go:noinline +func mapIterStart(t *abi.SwissMapType, m *maps.Map, it *maps.Iter) { + if race.Enabled && m != nil { + callerpc := sys.GetCallerPC() + race.ReadPC(unsafe.Pointer(m), callerpc, abi.FuncPCABIInternal(mapIterStart)) + } + + it.Init(t, m) + it.Next() +} + +// Equivalent to runtime.mapIterNext. +// +//go:noinline +func mapIterNext(it *maps.Iter) { + if race.Enabled { + callerpc := sys.GetCallerPC() + race.ReadPC(unsafe.Pointer(it.Map()), callerpc, abi.FuncPCABIInternal(mapIterNext)) + } + + it.Next() +} + // MapKeys returns a slice containing all the keys present in the map, // in unspecified order. // It panics if v's Kind is not [Map]. @@ -187,13 +214,17 @@ func (v Value) MapKeys() []Value { fl := v.flag.ro() | flag(keyType.Kind()) - m := v.pointer() + // Escape analysis can't see that the map doesn't escape. It sees an + // escape from maps.IterStart, via assignment into it, even though it + // doesn't escape this function. + mptr := abi.NoEscape(v.pointer()) + m := (*maps.Map)(mptr) mlen := int(0) if m != nil { - mlen = maplen(m) + mlen = maplen(mptr) } var it maps.Iter - mapiterinit(v.typ(), m, &it) + mapIterStart(tt, m, &it) a := make([]Value, mlen) var i int for i = 0; i < len(a); i++ { @@ -205,7 +236,7 @@ func (v Value) MapKeys() []Value { break } a[i] = copyVal(keyType, fl, key) - mapiternext(&it) + mapIterNext(&it) } return a[:i] } @@ -317,12 +348,14 @@ func (iter *MapIter) Next() bool { panic("MapIter.Next called on an iterator that does not have an associated map Value") } if !iter.hiter.Initialized() { - mapiterinit(iter.m.typ(), iter.m.pointer(), &iter.hiter) + t := (*mapType)(unsafe.Pointer(iter.m.typ())) + m := (*maps.Map)(iter.m.pointer()) + mapIterStart(t, m, &iter.hiter) } else { if iter.hiter.Key() == nil { panic("MapIter.Next called on exhausted iterator") } - mapiternext(&iter.hiter) + mapIterNext(&iter.hiter) } return iter.hiter.Key() != nil } diff --git a/src/reflect/value.go b/src/reflect/value.go index 4ed94addf99a57c9c99404283584e2b319d7bc74..ba5b106c18c970357d76445e0494a90cc7ec78c1 100644 --- a/src/reflect/value.go +++ b/src/reflect/value.go @@ -3603,12 +3603,6 @@ func mapdelete(t *abi.Type, m unsafe.Pointer, key unsafe.Pointer) //go:noescape func mapdelete_faststr(t *abi.Type, m unsafe.Pointer, key string) -//go:noescape -func mapiterinit(t *abi.Type, m unsafe.Pointer, it *hiter) - -//go:noescape -func mapiternext(it *hiter) - //go:noescape func maplen(m unsafe.Pointer) int diff --git a/src/runtime/asm_arm64.s b/src/runtime/asm_arm64.s index 88bfd3ce5c3fa5fb8bf8893bd079fbe3f60e330f..64a1880589390d20b9c0fb77ec9b0628fb68f4cd 100644 --- a/src/runtime/asm_arm64.s +++ b/src/runtime/asm_arm64.s @@ -8,11 +8,6 @@ #include "funcdata.h" #include "textflag.h" -#ifdef GOARM64_LSE -DATA no_lse_msg<>+0x00(SB)/64, $"This program can only run on ARM64 processors with LSE support.\n" -GLOBL no_lse_msg<>(SB), RODATA, $64 -#endif - TEXT runtime·rt0_go(SB),NOSPLIT|TOPFRAME,$0 // SP = stack; R0 = argc; R1 = argv @@ -82,21 +77,6 @@ nocgo: BL runtime·wintls(SB) #endif - // Check that CPU we use for execution supports instructions targeted during compile-time. -#ifdef GOARM64_LSE -#ifndef GOOS_openbsd - // Read the ID_AA64ISAR0_EL1 register - MRS ID_AA64ISAR0_EL1, R0 - - // Extract the LSE field (bits [23:20]) - LSR $20, R0, R0 - AND $0xf, R0, R0 - - // LSE support is indicated by a non-zero value - CBZ R0, no_lse -#endif -#endif - MOVW 8(RSP), R0 // copy argc MOVW R0, -8(RSP) MOVD 16(RSP), R0 // copy argv @@ -115,23 +95,6 @@ nocgo: // start this M BL runtime·mstart(SB) - RET - -#ifdef GOARM64_LSE -#ifndef GOOS_openbsd -no_lse: - MOVD $1, R0 // stderr - MOVD R0, 8(RSP) - MOVD $no_lse_msg<>(SB), R1 // message address - MOVD R1, 16(RSP) - MOVD $64, R2 // message length - MOVD R2, 24(RSP) - CALL runtime·write(SB) - CALL runtime·exit(SB) - CALL runtime·abort(SB) - RET -#endif -#endif // Prevent dead-code elimination of debugCallV2 and debugPinnerV1, which are // intended to be called by debuggers. diff --git a/src/runtime/asm_wasm.s b/src/runtime/asm_wasm.s index 016d2d3825ac49f8780621f21c6118daabdfb7f6..69da583a1d54e9635bd45afc4b41ea28cf441c68 100644 --- a/src/runtime/asm_wasm.s +++ b/src/runtime/asm_wasm.s @@ -614,3 +614,13 @@ TEXT runtime·pause(SB), NOSPLIT, $0-8 I32Const $1 Set PAUSE RETUNWIND + +// Called if a wasmexport function is called before runtime initialization +TEXT runtime·notInitialized(SB), NOSPLIT, $0 + MOVD $runtime·wasmStack+(m0Stack__size-16-8)(SB), SP + I32Const $0 // entry PC_B + Call runtime·notInitialized1(SB) + Drop + I32Const $0 // entry PC_B + Call runtime·abort(SB) + UNDEF diff --git a/src/runtime/linkname_swiss.go b/src/runtime/linkname_swiss.go new file mode 100644 index 0000000000000000000000000000000000000000..1be724477e5a503b688c5edd4c1703f3cdca55d3 --- /dev/null +++ b/src/runtime/linkname_swiss.go @@ -0,0 +1,211 @@ +// 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 goexperiment.swissmap + +package runtime + +import ( + "internal/abi" + "internal/runtime/maps" + "internal/runtime/sys" + "unsafe" +) + +// Legacy //go:linkname compatibility shims +// +// The functions below are unused by the toolchain, and exist only for +// compatibility with existing //go:linkname use in the ecosystem (and in +// map_noswiss.go for normal use via GOEXPERIMENT=noswissmap). + +// linknameIter is the it argument to mapiterinit and mapiternext. +// +// Callers of mapiterinit allocate their own iter structure, which has the +// layout of the pre-Go 1.24 hiter structure, shown here for posterity: +// +// type hiter struct { +// key unsafe.Pointer +// elem unsafe.Pointer +// t *maptype +// h *hmap +// buckets unsafe.Pointer +// bptr *bmap +// overflow *[]*bmap +// oldoverflow *[]*bmap +// startBucket uintptr +// offset uint8 +// wrapped bool +// B uint8 +// i uint8 +// bucket uintptr +// checkBucket uintptr +// } +// +// Our structure must maintain compatibility with the old structure. This +// means: +// +// - Our structure must be the same size or smaller than hiter. Otherwise we +// may write outside the caller's hiter allocation. +// - Our structure must have the same pointer layout as hiter, so that the GC +// tracks pointers properly. +// +// Based on analysis of the "hall of shame" users of these linknames: +// +// - The key and elem fields must be kept up to date with the current key/elem. +// Some users directly access the key and elem fields rather than calling +// reflect.mapiterkey/reflect.mapiterelem. +// - The t field must be non-nil after mapiterinit. gonum.org/v1/gonum uses +// this to verify the iterator is initialized. +// - github.com/segmentio/encoding and github.com/RomiChan/protobuf check if h +// is non-nil, but the code has no effect. Thus the value of h does not +// matter. See internal/runtime_reflect/map.go. +type linknameIter struct { + // Fields from hiter. + key unsafe.Pointer + elem unsafe.Pointer + typ *abi.SwissMapType + + // The real iterator. + it *maps.Iter +} + +// mapiterinit is a compatibility wrapper for map iterator for users of +// //go:linkname from before Go 1.24. It is not used by Go itself. New users +// should use reflect or the maps package. +// +// mapiterinit should be an internal detail, +// but widely used packages access it using linkname. +// Notable members of the hall of shame include: +// - github.com/bytedance/sonic +// - github.com/goccy/go-json +// - github.com/RomiChan/protobuf +// - github.com/segmentio/encoding +// - github.com/ugorji/go/codec +// - github.com/wI2L/jettison +// +// Do not remove or change the type signature. +// See go.dev/issue/67401. +// +//go:linkname mapiterinit +func mapiterinit(t *abi.SwissMapType, m *maps.Map, it *linknameIter) { + if raceenabled && m != nil { + callerpc := sys.GetCallerPC() + racereadpc(unsafe.Pointer(m), callerpc, abi.FuncPCABIInternal(mapiterinit)) + } + + it.typ = t + + it.it = new(maps.Iter) + it.it.Init(t, m) + it.it.Next() + + it.key = it.it.Key() + it.elem = it.it.Elem() +} + +// reflect_mapiterinit is a compatibility wrapper for map iterator for users of +// //go:linkname from before Go 1.24. It is not used by Go itself. New users +// should use reflect or the maps package. +// +// reflect_mapiterinit should be an internal detail, +// but widely used packages access it using linkname. +// Notable members of the hall of shame include: +// - github.com/modern-go/reflect2 +// - gitee.com/quant1x/gox +// - github.com/v2pro/plz +// - github.com/wI2L/jettison +// +// Do not remove or change the type signature. +// See go.dev/issue/67401. +// +//go:linkname reflect_mapiterinit reflect.mapiterinit +func reflect_mapiterinit(t *abi.SwissMapType, m *maps.Map, it *linknameIter) { + mapiterinit(t, m, it) +} + +// mapiternext is a compatibility wrapper for map iterator for users of +// //go:linkname from before Go 1.24. It is not used by Go itself. New users +// should use reflect or the maps package. +// +// mapiternext should be an internal detail, +// but widely used packages access it using linkname. +// Notable members of the hall of shame include: +// - github.com/bytedance/sonic +// - github.com/RomiChan/protobuf +// - github.com/segmentio/encoding +// - github.com/ugorji/go/codec +// - gonum.org/v1/gonum +// +// Do not remove or change the type signature. +// See go.dev/issue/67401. +// +//go:linkname mapiternext +func mapiternext(it *linknameIter) { + if raceenabled { + callerpc := sys.GetCallerPC() + racereadpc(unsafe.Pointer(it.it.Map()), callerpc, abi.FuncPCABIInternal(mapiternext)) + } + + it.it.Next() + + it.key = it.it.Key() + it.elem = it.it.Elem() +} + +// reflect_mapiternext is a compatibility wrapper for map iterator for users of +// //go:linkname from before Go 1.24. It is not used by Go itself. New users +// should use reflect or the maps package. +// +// reflect_mapiternext is for package reflect, +// but widely used packages access it using linkname. +// Notable members of the hall of shame include: +// - gitee.com/quant1x/gox +// - github.com/modern-go/reflect2 +// - github.com/goccy/go-json +// - github.com/v2pro/plz +// - github.com/wI2L/jettison +// +// Do not remove or change the type signature. +// See go.dev/issue/67401. +// +//go:linkname reflect_mapiternext reflect.mapiternext +func reflect_mapiternext(it *linknameIter) { + mapiternext(it) +} + +// reflect_mapiterkey is a compatibility wrapper for map iterator for users of +// //go:linkname from before Go 1.24. It is not used by Go itself. New users +// should use reflect or the maps package. +// +// reflect_mapiterkey should be an internal detail, +// but widely used packages access it using linkname. +// Notable members of the hall of shame include: +// - github.com/goccy/go-json +// - gonum.org/v1/gonum +// +// Do not remove or change the type signature. +// See go.dev/issue/67401. +// +//go:linkname reflect_mapiterkey reflect.mapiterkey +func reflect_mapiterkey(it *linknameIter) unsafe.Pointer { + return it.it.Key() +} + +// reflect_mapiterelem is a compatibility wrapper for map iterator for users of +// //go:linkname from before Go 1.24. It is not used by Go itself. New users +// should use reflect or the maps package. +// +// reflect_mapiterelem should be an internal detail, +// but widely used packages access it using linkname. +// Notable members of the hall of shame include: +// - github.com/goccy/go-json +// - gonum.org/v1/gonum +// +// Do not remove or change the type signature. +// See go.dev/issue/67401. +// +//go:linkname reflect_mapiterelem reflect.mapiterelem +func reflect_mapiterelem(it *linknameIter) unsafe.Pointer { + return it.it.Elem() +} diff --git a/src/runtime/map_swiss.go b/src/runtime/map_swiss.go index e6e29bcfb800bb3ef7a09684205a0ae4e0a0185e..a8fe87257acd280e864631714acf796189fb65b0 100644 --- a/src/runtime/map_swiss.go +++ b/src/runtime/map_swiss.go @@ -158,52 +158,26 @@ func mapdelete(t *abi.SwissMapType, m *maps.Map, key unsafe.Pointer) { m.Delete(t, key) } -// mapiterinit initializes the Iter struct used for ranging over maps. -// The Iter struct pointed to by 'it' is allocated on the stack -// by the compilers order pass or on the heap by reflect_mapiterinit. -// Both need to have zeroed hiter since the struct contains pointers. -// -// mapiterinit should be an internal detail, -// but widely used packages access it using linkname. -// Notable members of the hall of shame include: -// - github.com/bytedance/sonic -// - github.com/goccy/go-json -// - github.com/RomiChan/protobuf -// - github.com/segmentio/encoding -// - github.com/ugorji/go/codec -// - github.com/wI2L/jettison -// -// Do not remove or change the type signature. -// See go.dev/issue/67401. -// -//go:linkname mapiterinit -func mapiterinit(t *abi.SwissMapType, m *maps.Map, it *maps.Iter) { +// mapIterStart initializes the Iter struct used for ranging over maps and +// performs the first step of iteration. The Iter struct pointed to by 'it' is +// allocated on the stack by the compilers order pass or on the heap by +// reflect. Both need to have zeroed it since the struct contains pointers. +func mapIterStart(t *abi.SwissMapType, m *maps.Map, it *maps.Iter) { if raceenabled && m != nil { callerpc := sys.GetCallerPC() - racereadpc(unsafe.Pointer(m), callerpc, abi.FuncPCABIInternal(mapiterinit)) + racereadpc(unsafe.Pointer(m), callerpc, abi.FuncPCABIInternal(mapIterStart)) } it.Init(t, m) it.Next() } -// mapiternext should be an internal detail, -// but widely used packages access it using linkname. -// Notable members of the hall of shame include: -// - github.com/bytedance/sonic -// - github.com/RomiChan/protobuf -// - github.com/segmentio/encoding -// - github.com/ugorji/go/codec -// - gonum.org/v1/gonum -// -// Do not remove or change the type signature. -// See go.dev/issue/67401. -// -//go:linkname mapiternext -func mapiternext(it *maps.Iter) { +// mapIterNext performs the next step of iteration. Afterwards, the next +// key/elem are in it.Key()/it.Elem(). +func mapIterNext(it *maps.Iter) { if raceenabled { callerpc := sys.GetCallerPC() - racereadpc(unsafe.Pointer(it.Map()), callerpc, abi.FuncPCABIInternal(mapiternext)) + racereadpc(unsafe.Pointer(it.Map()), callerpc, abi.FuncPCABIInternal(mapIterNext)) } it.Next() @@ -306,67 +280,6 @@ func reflect_mapdelete_faststr(t *abi.SwissMapType, m *maps.Map, key string) { mapdelete_faststr(t, m, key) } -// reflect_mapiterinit is for package reflect, -// but widely used packages access it using linkname. -// Notable members of the hall of shame include: -// - github.com/modern-go/reflect2 -// - gitee.com/quant1x/gox -// - github.com/v2pro/plz -// - github.com/wI2L/jettison -// -// Do not remove or change the type signature. -// See go.dev/issue/67401. -// -//go:linkname reflect_mapiterinit reflect.mapiterinit -func reflect_mapiterinit(t *abi.SwissMapType, m *maps.Map, it *maps.Iter) { - mapiterinit(t, m, it) -} - -// reflect_mapiternext is for package reflect, -// but widely used packages access it using linkname. -// Notable members of the hall of shame include: -// - gitee.com/quant1x/gox -// - github.com/modern-go/reflect2 -// - github.com/goccy/go-json -// - github.com/v2pro/plz -// - github.com/wI2L/jettison -// -// Do not remove or change the type signature. -// See go.dev/issue/67401. -// -//go:linkname reflect_mapiternext reflect.mapiternext -func reflect_mapiternext(it *maps.Iter) { - mapiternext(it) -} - -// reflect_mapiterkey was for package reflect, -// but widely used packages access it using linkname. -// Notable members of the hall of shame include: -// - github.com/goccy/go-json -// - gonum.org/v1/gonum -// -// Do not remove or change the type signature. -// See go.dev/issue/67401. -// -//go:linkname reflect_mapiterkey reflect.mapiterkey -func reflect_mapiterkey(it *maps.Iter) unsafe.Pointer { - return it.Key() -} - -// reflect_mapiterelem was for package reflect, -// but widely used packages access it using linkname. -// Notable members of the hall of shame include: -// - github.com/goccy/go-json -// - gonum.org/v1/gonum -// -// Do not remove or change the type signature. -// See go.dev/issue/67401. -// -//go:linkname reflect_mapiterelem reflect.mapiterelem -func reflect_mapiterelem(it *maps.Iter) unsafe.Pointer { - return it.Elem() -} - // reflect_maplen is for package reflect, // but widely used packages access it using linkname. // Notable members of the hall of shame include: diff --git a/src/runtime/map_test.go b/src/runtime/map_test.go index e3c092bef90562657ac5f432416536a5c07640d8..c522c44a4ef4767139784b212f3792aae85e451f 100644 --- a/src/runtime/map_test.go +++ b/src/runtime/map_test.go @@ -674,10 +674,6 @@ func TestIgnoreBogusMapHint(t *testing.T) { var testNonEscapingMapVariable int = 8 func TestNonEscapingMap(t *testing.T) { - if goexperiment.SwissMap { - t.Skip("TODO(go.dev/issue/54766): implement stack allocated maps") - } - n := testing.AllocsPerRun(1000, func() { m := map[int]int{} m[0] = 0 diff --git a/src/runtime/mcleanup.go b/src/runtime/mcleanup.go index 22d40a5e841243206b72a8f3d5be39fe40f3fd53..972532d475c0b40209b47d615a38d30c64f1fef7 100644 --- a/src/runtime/mcleanup.go +++ b/src/runtime/mcleanup.go @@ -70,19 +70,19 @@ func AddCleanup[T, S any](ptr *T, cleanup func(S), arg S) Cleanup { // The pointer to the object must be valid. if ptr == nil { - throw("runtime.AddCleanup: ptr is nil") + panic("runtime.AddCleanup: ptr is nil") } usptr := uintptr(unsafe.Pointer(ptr)) // Check that arg is not equal to ptr. - // TODO(67535) this does not cover the case where T and *S are the same - // type and ptr and arg are equal. - if unsafe.Pointer(&arg) == unsafe.Pointer(ptr) { - throw("runtime.AddCleanup: ptr is equal to arg, cleanup will never run") + if kind := abi.TypeOf(arg).Kind(); kind == abi.Pointer || kind == abi.UnsafePointer { + if unsafe.Pointer(ptr) == *((*unsafe.Pointer)(unsafe.Pointer(&arg))) { + panic("runtime.AddCleanup: ptr is equal to arg, cleanup will never run") + } } if inUserArenaChunk(usptr) { // Arena-allocated objects are not eligible for cleanup. - throw("runtime.AddCleanup: ptr is arena-allocated") + panic("runtime.AddCleanup: ptr is arena-allocated") } if debug.sbrk != 0 { // debug.sbrk never frees memory, so no cleanup will ever run @@ -105,7 +105,7 @@ func AddCleanup[T, S any](ptr *T, cleanup func(S), arg S) Cleanup { // Cleanup is a noop. return Cleanup{} } - throw("runtime.AddCleanup: ptr not in allocated block") + panic("runtime.AddCleanup: ptr not in allocated block") } // Ensure we have a finalizer processing goroutine running. diff --git a/src/runtime/mcleanup_test.go b/src/runtime/mcleanup_test.go index 8c2d1f06477fdd28263d061aab9177cad3372349..d62356feefb049d8d61fd55ae7ae7a641b1c71ea 100644 --- a/src/runtime/mcleanup_test.go +++ b/src/runtime/mcleanup_test.go @@ -269,3 +269,30 @@ func TestCleanupStopAfterCleanupRuns(t *testing.T) { <-ch stop() } + +func TestCleanupPointerEqualsArg(t *testing.T) { + // See go.dev/issue/71316 + defer func() { + want := "runtime.AddCleanup: ptr is equal to arg, cleanup will never run" + if r := recover(); r == nil { + t.Error("want panic, test did not panic") + } else if r == want { + // do nothing + } else { + t.Errorf("wrong panic: want=%q, got=%q", want, r) + } + }() + + // allocate struct with pointer to avoid hitting tinyalloc. + // Otherwise we can't be sure when the allocation will + // be freed. + type T struct { + v int + p unsafe.Pointer + } + v := &new(T).v + *v = 97531 + runtime.AddCleanup(v, func(x *int) {}, v) + v = nil + runtime.GC() +} diff --git a/src/runtime/panic.go b/src/runtime/panic.go index dc7a7fe357e091390fd2964672a7fdf5abe648d6..3ffb3966d026c8d43220c0fb45ff92e355f485b4 100644 --- a/src/runtime/panic.go +++ b/src/runtime/panic.go @@ -1068,9 +1068,6 @@ func internal_sync_fatal(s string) { // throw should be used for runtime-internal fatal errors where Go itself, // rather than user code, may be at fault for the failure. // -// NOTE: temporarily marked "go:noinline" pending investigation/fix of -// issue #67274, so as to fix longtest builders. -// // throw should be an internal detail, // but widely used packages access it using linkname. // Notable members of the hall of shame include: diff --git a/src/runtime/pprof/pprof.go b/src/runtime/pprof/pprof.go index f6b4a5c367f05fd3c4a2490927c27709018ea03a..b7680a13fdcb2589ed7a4be0fb03b26764c28c78 100644 --- a/src/runtime/pprof/pprof.go +++ b/src/runtime/pprof/pprof.go @@ -555,7 +555,7 @@ func printStackRecord(w io.Writer, stk []uintptr, allFrames bool) { if name == "" { show = true fmt.Fprintf(w, "#\t%#x\n", frame.PC) - } else if name != "runtime.goexit" && (show || !strings.HasPrefix(name, "runtime.")) { + } else if name != "runtime.goexit" && (show || !(strings.HasPrefix(name, "runtime.") || strings.HasPrefix(name, "internal/runtime/"))) { // Hide runtime.goexit and any runtime functions at the beginning. // This is useful mainly for allocation traces. show = true diff --git a/src/runtime/pprof/protomem.go b/src/runtime/pprof/protomem.go index ab3550f43f9a390544dcf87324dec9406d7a8268..72aad82b3005343572c8be15b291720aefa904f4 100644 --- a/src/runtime/pprof/protomem.go +++ b/src/runtime/pprof/protomem.go @@ -36,7 +36,7 @@ func writeHeapProto(w io.Writer, p []profilerecord.MemProfileRecord, rate int64, // what appendLocsForStack expects. if hideRuntime { for i, addr := range stk { - if f := runtime.FuncForPC(addr); f != nil && strings.HasPrefix(f.Name(), "runtime.") { + if f := runtime.FuncForPC(addr); f != nil && (strings.HasPrefix(f.Name(), "runtime.") || strings.HasPrefix(f.Name(), "internal/runtime/")) { continue } // Found non-runtime. Show any runtime uses above it. diff --git a/src/runtime/pprof/protomem_test.go b/src/runtime/pprof/protomem_test.go index 885f4dca5b87624814bb9577782fda766f66d43c..4d08e67ddc7540123367c35f37a631ae0c5c9dd8 100644 --- a/src/runtime/pprof/protomem_test.go +++ b/src/runtime/pprof/protomem_test.go @@ -118,7 +118,7 @@ func locationToStrings(loc *profile.Location, funcs []string) []string { return funcs } -// This is a regression test for https://go.dev/issue/64528 . +// This is a regression test for https://go.dev/issue/64528. func TestGenericsHashKeyInPprofBuilder(t *testing.T) { if asan.Enabled { t.Skip("extra allocations with -asan throw off the test; see #70079") @@ -229,3 +229,61 @@ func TestGenericsInlineLocations(t *testing.T) { t.Errorf("expected a location with at least 3 functions\n%s\ngot\n%s\n", expectedLocation, actual) } } + +func growMap() { + m := make(map[int]int) + for i := range 512 { + m[i] = i + } +} + +// Runtime frames are hidden in heap profiles. +// This is a regression test for https://go.dev/issue/71174. +func TestHeapRuntimeFrames(t *testing.T) { + previousRate := runtime.MemProfileRate + runtime.MemProfileRate = 1 + defer func() { + runtime.MemProfileRate = previousRate + }() + + growMap() + + runtime.GC() + buf := bytes.NewBuffer(nil) + if err := WriteHeapProfile(buf); err != nil { + t.Fatalf("writing profile: %v", err) + } + p, err := profile.Parse(buf) + if err != nil { + t.Fatalf("profile.Parse: %v", err) + } + + actual := profileToStrings(p) + + // We must see growMap at least once. + foundGrowMap := false + for _, l := range actual { + if !strings.Contains(l, "runtime/pprof.growMap") { + continue + } + foundGrowMap = true + + // Runtime frames like mapassign and map internals should be hidden. + if strings.Contains(l, "runtime.") { + t.Errorf("Sample got %s, want no runtime frames", l) + } + if strings.Contains(l, "internal/runtime/") { + t.Errorf("Sample got %s, want no runtime frames", l) + } + if strings.Contains(l, "runtime/internal/") { + t.Errorf("Sample got %s, want no runtime frames", l) + } + if strings.Contains(l, "mapassign") { // in case mapassign moves to a package not matching above paths. + t.Errorf("Sample got %s, want no mapassign frames", l) + } + } + + if !foundGrowMap { + t.Errorf("Profile got:\n%s\nwant sample in runtime/pprof.growMap", strings.Join(actual, "\n")) + } +} diff --git a/src/runtime/sys_wasm.go b/src/runtime/sys_wasm.go index f88b992e9c39a38a4670047152f7627001e3c51f..6b40a8d3e94c96acc27cc65b43c263e285e9afd8 100644 --- a/src/runtime/sys_wasm.go +++ b/src/runtime/sys_wasm.go @@ -34,3 +34,17 @@ func gostartcall(buf *gobuf, fn, ctxt unsafe.Pointer) { buf.pc = uintptr(fn) buf.ctxt = ctxt } + +func notInitialized() // defined in assembly, call notInitialized1 + +// Called if a wasmexport function is called before runtime initialization +// +//go:nosplit +func notInitialized1() { + writeErrStr("runtime: wasmexport function called before runtime initialization\n") + if isarchive || islibrary { + writeErrStr("\tcall _initialize first\n") + } else { + writeErrStr("\tcall _start first\n") + } +} diff --git a/src/strconv/quote.go b/src/strconv/quote.go index 1f4929a952c6bf786eb551ef729f4011817f1848..99c292a8ed58848441a400031260fa44960caa28 100644 --- a/src/strconv/quote.go +++ b/src/strconv/quote.go @@ -378,7 +378,8 @@ func QuotedPrefix(s string) (string, error) { // or backquoted Go string literal, returning the string value // that s quotes. (If s is single-quoted, it would be a Go // character literal; Unquote returns the corresponding -// one-character string. For '' Unquote returns the empty string.) +// one-character string. For an empty character literal +// Unquote returns the empty string.) func Unquote(s string) (string, error) { out, rem, err := unquote(s, true) if len(rem) > 0 { diff --git a/src/syscall/js/js.go b/src/syscall/js/js.go index 74c02cdbe64ada4d2746c09e249141bae87ed536..bbf3de199b4b829d4265c3849d4f06f5a806aef6 100644 --- a/src/syscall/js/js.go +++ b/src/syscall/js/js.go @@ -212,8 +212,8 @@ func ValueOf(x any) Value { // stringVal copies string x to Javascript and returns a ref. // -// (noescape): This is safe because no references are maintained to the -// Go string x after the syscall returns. +// Using go:noescape is safe because no references are maintained to the +// Go string x after the syscall returns. // //go:wasmimport gojs syscall/js.stringVal //go:noescape @@ -302,8 +302,8 @@ func (v Value) Get(p string) Value { // valueGet returns a ref to JavaScript property p of ref v. // -// (noescape): This is safe because no references are maintained to the -// Go string p after the syscall returns. +// Using go:noescape is safe because no references are maintained to the +// Go string p after the syscall returns. // //go:wasmimport gojs syscall/js.valueGet //go:noescape @@ -323,8 +323,8 @@ func (v Value) Set(p string, x any) { // valueSet sets property p of ref v to ref x. // -// (noescape): This is safe because no references are maintained to the -// Go string p after the syscall returns. +// Using go:noescape is safe because no references are maintained to the +// Go string p after the syscall returns. // //go:wasmimport gojs syscall/js.valueSet //go:noescape @@ -342,8 +342,8 @@ func (v Value) Delete(p string) { // valueDelete deletes the JavaScript property p of ref v. // -// (noescape): This is safe because no references are maintained to the -// Go string p after the syscall returns. +// Using go:noescape is safe because no references are maintained to the +// Go string p after the syscall returns. // //go:wasmimport gojs syscall/js.valueDelete //go:noescape @@ -447,10 +447,10 @@ func (v Value) Call(m string, args ...any) Value { // valueCall does a JavaScript call to the method name m of ref v with the given arguments. // -// (noescape): This is safe because no references are maintained to the -// Go string m after the syscall returns. Additionally, the args slice -// is only used temporarily to collect the JavaScript objects for -// the JavaScript method invocation. +// Using go:noescape is safe because no references are maintained to the +// Go string m after the syscall returns. Additionally, the args slice +// is only used temporarily to collect the JavaScript objects for +// the JavaScript method invocation. // //go:wasmimport gojs syscall/js.valueCall //go:nosplit @@ -477,9 +477,9 @@ func (v Value) Invoke(args ...any) Value { // valueInvoke does a JavaScript call to value v with the given arguments. // -// (noescape): This is safe because the args slice is only used temporarily -// to collect the JavaScript objects for the JavaScript method -// invocation. +// Using go:noescape is safe because the args slice is only used temporarily +// to collect the JavaScript objects for the JavaScript method +// invocation. // //go:wasmimport gojs syscall/js.valueInvoke //go:noescape @@ -505,8 +505,8 @@ func (v Value) New(args ...any) Value { // valueNew uses JavaScript's "new" operator with value v as a constructor and the given arguments. // -// (noescape): This is safe because the args slice is only used temporarily -// to collect the JavaScript objects for the constructor execution. +// Using go:noescape is safe because the args slice is only used temporarily +// to collect the JavaScript objects for the constructor execution. // //go:wasmimport gojs syscall/js.valueNew //go:noescape @@ -614,8 +614,8 @@ func valuePrepareString(v ref) (ref, int) // valueLoadString loads string data located at ref v into byte slice b. // -// (noescape): This is safe because the byte slice is only used as a destination -// for storing the string data and references to it are not maintained. +// Using go:noescape is safe because the byte slice is only used as a destination +// for storing the string data and references to it are not maintained. // //go:wasmimport gojs syscall/js.valueLoadString //go:noescape @@ -658,8 +658,8 @@ func CopyBytesToGo(dst []byte, src Value) int { // copyBytesToGo copies bytes from src to dst. // -// (noescape): This is safe because the dst byte slice is only used as a dst -// copy buffer and no references to it are maintained. +// Using go:noescape is safe because the dst byte slice is only used as a dst +// copy buffer and no references to it are maintained. // //go:wasmimport gojs syscall/js.copyBytesToGo //go:noescape @@ -677,10 +677,10 @@ func CopyBytesToJS(dst Value, src []byte) int { return n } -// copyBytesToJs copies bytes from src to dst. +// copyBytesToJS copies bytes from src to dst. // -// (noescape): This is safe because the src byte slice is only used as a src -// copy buffer and no references to it are maintained. +// Using go:noescape is safe because the src byte slice is only used as a src +// copy buffer and no references to it are maintained. // //go:wasmimport gojs syscall/js.copyBytesToJS //go:noescape diff --git a/src/testing/fstest/testfs.go b/src/testing/fstest/testfs.go index 2917a303b20adfed093f1fcf8504a6507af80653..affdfa64291ba8a4b04083d60d11523177b87821 100644 --- a/src/testing/fstest/testfs.go +++ b/src/testing/fstest/testfs.go @@ -570,7 +570,7 @@ func (t *fsTester) checkFileRead(file, desc string, data1, data2 []byte) { } } -// checkBadPath checks that various invalid forms of file's name cannot be opened using t.fsys.Open. +// checkOpen validates file opening behavior by attempting to open and then close the given file path. func (t *fsTester) checkOpen(file string) { t.checkBadPath(file, "Open", func(file string) error { f, err := t.fsys.Open(file) diff --git a/src/testing/testing.go b/src/testing/testing.go index be6391b0ab15ce5dce07bc22b3c21c2b80581c0d..3833bfc84be49fcc113062f79ef06613125bc03a 100644 --- a/src/testing/testing.go +++ b/src/testing/testing.go @@ -120,7 +120,7 @@ // # b.N-style benchmarks // // Prior to the introduction of [B.Loop], benchmarks were written in a -// different style using [B.N]. For example: +// different style using B.N. For example: // // func BenchmarkRandInt(b *testing.B) { // for range b.N { diff --git a/src/vendor/modules.txt b/src/vendor/modules.txt index 1c8de570cc2f1fa89678c9e91c4097d07ca1c4c6..d42f50b43ccdba4eedcb8f36026b2343e5b8a866 100644 --- a/src/vendor/modules.txt +++ b/src/vendor/modules.txt @@ -6,7 +6,7 @@ golang.org/x/crypto/cryptobyte golang.org/x/crypto/cryptobyte/asn1 golang.org/x/crypto/internal/alias golang.org/x/crypto/internal/poly1305 -# golang.org/x/net v0.32.1-0.20241206180132-552d8ac903a1 +# golang.org/x/net v0.32.1-0.20250121202134-9a960c88dd98 ## explicit; go 1.18 golang.org/x/net/dns/dnsmessage golang.org/x/net/http/httpguts diff --git a/test/codegen/maps.go b/test/codegen/maps.go index d7cf6534ad907a43824a148dc9d05d30df4529ba..c4aed3354514f6fef3be62b48f869a83b1cca4d3 100644 --- a/test/codegen/maps.go +++ b/test/codegen/maps.go @@ -4,11 +4,6 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -// TODO(#54766): Temporarily disable for swissmap, which have fast variants -// disabled. This test expects fast variants. -// -//go:build !goexperiment.swissmap - package codegen // This file contains code generation tests related to the handling of @@ -79,7 +74,7 @@ func LookupStringConversionKeyedArrayLit(m map[[2]string]int, bytes []byte) int func MapClearReflexive(m map[int]int) { // amd64:`.*runtime\.mapclear` - // amd64:-`.*runtime\.mapiterinit` + // amd64:-`.*runtime\.(mapiterinit|mapIterStart)` for k := range m { delete(m, k) } @@ -88,7 +83,7 @@ func MapClearReflexive(m map[int]int) { func MapClearIndirect(m map[int]int) { s := struct{ m map[int]int }{m: m} // amd64:`.*runtime\.mapclear` - // amd64:-`.*runtime\.mapiterinit` + // amd64:-`.*runtime\.(mapiterinit|mapIterStart)` for k := range s.m { delete(s.m, k) } @@ -96,14 +91,14 @@ func MapClearIndirect(m map[int]int) { func MapClearPointer(m map[*byte]int) { // amd64:`.*runtime\.mapclear` - // amd64:-`.*runtime\.mapiterinit` + // amd64:-`.*runtime\.(mapiterinit|mapIterStart)` for k := range m { delete(m, k) } } func MapClearNotReflexive(m map[float64]int) { - // amd64:`.*runtime\.mapiterinit` + // amd64:`.*runtime\.(mapiterinit|mapIterStart)` // amd64:-`.*runtime\.mapclear` for k := range m { delete(m, k) @@ -111,7 +106,7 @@ func MapClearNotReflexive(m map[float64]int) { } func MapClearInterface(m map[interface{}]int) { - // amd64:`.*runtime\.mapiterinit` + // amd64:`.*runtime\.(mapiterinit|mapIterStart)` // amd64:-`.*runtime\.mapclear` for k := range m { delete(m, k) @@ -120,7 +115,7 @@ func MapClearInterface(m map[interface{}]int) { func MapClearSideEffect(m map[int]int) int { k := 0 - // amd64:`.*runtime\.mapiterinit` + // amd64:`.*runtime\.(mapiterinit|mapIterStart)` // amd64:-`.*runtime\.mapclear` for k = range m { delete(m, k) diff --git a/test/codegen/writebarrier.go b/test/codegen/writebarrier.go index e125973e7cf09f13a6f7850f0387a9938ad37c1f..e2b1399399b020f07a2bad9f6885c48e8f47a5cb 100644 --- a/test/codegen/writebarrier.go +++ b/test/codegen/writebarrier.go @@ -63,3 +63,28 @@ func trickyWriteNil(p *int, q **int) { *q = p } } + +type S struct { + a, b string + c *int +} + +var g1, g2 *int + +func issue71228(dst *S, ptr *int) { + // Make sure that the non-write-barrier write. + // "sp.c = ptr" happens before the large write + // barrier "*dst = *sp". We approximate testing + // that by ensuring that two global variable write + // barriers aren't combined. + _ = *dst + var s S + sp := &s + //amd64:`.*runtime[.]gcWriteBarrier1` + g1 = nil + sp.c = ptr // outside of any write barrier + //amd64:`.*runtime[.]gcWriteBarrier1` + g2 = nil + //amd64:`.*runtime[.]wbMove` + *dst = *sp +} diff --git a/test/live.go b/test/live.go index 250a77cdac46144e54dca6a440f6a835a4a332c0..c0b0fcd274a0cac048b28a1470d6073216dbdca0 100644 --- a/test/live.go +++ b/test/live.go @@ -458,14 +458,14 @@ func f28(b bool) { func f29(b bool) { if b { - for k := range m { // ERROR "live at call to mapiterinit: .autotmp_[0-9]+$" "live at call to mapiternext: .autotmp_[0-9]+$" "stack object .autotmp_[0-9]+ (runtime.hiter|internal/runtime/maps.Iter)$" + for k := range m { // ERROR "live at call to (mapiterinit|mapIterStart): .autotmp_[0-9]+$" "live at call to (mapiternext|mapIterNext): .autotmp_[0-9]+$" "stack object .autotmp_[0-9]+ (runtime.hiter|internal/runtime/maps.Iter)$" printstring(k) // ERROR "live at call to printstring: .autotmp_[0-9]+$" } } - for k := range m { // ERROR "live at call to mapiterinit: .autotmp_[0-9]+$" "live at call to mapiternext: .autotmp_[0-9]+$" + for k := range m { // ERROR "live at call to (mapiterinit|mapIterStart): .autotmp_[0-9]+$" "live at call to (mapiternext|mapIterNext): .autotmp_[0-9]+$" printstring(k) // ERROR "live at call to printstring: .autotmp_[0-9]+$" } - for k := range m { // ERROR "live at call to mapiterinit: .autotmp_[0-9]+$" "live at call to mapiternext: .autotmp_[0-9]+$" + for k := range m { // ERROR "live at call to (mapiterinit|mapIterStart): .autotmp_[0-9]+$" "live at call to (mapiternext|mapIterNext): .autotmp_[0-9]+$" printstring(k) // ERROR "live at call to printstring: .autotmp_[0-9]+$" } } diff --git a/test/live_regabi.go b/test/live_regabi.go index 090e2ec57776fe6915d5340920fbc41beac52a16..35f874ecc365f679bf0723920026eedba498c977 100644 --- a/test/live_regabi.go +++ b/test/live_regabi.go @@ -456,14 +456,14 @@ func f28(b bool) { func f29(b bool) { if b { - for k := range m { // ERROR "live at call to mapiterinit: .autotmp_[0-9]+$" "live at call to mapiternext: .autotmp_[0-9]+$" "stack object .autotmp_[0-9]+ (runtime.hiter|internal/runtime/maps.Iter)$" + for k := range m { // ERROR "live at call to (mapiterinit|mapIterStart): .autotmp_[0-9]+$" "live at call to (mapiternext|mapIterNext): .autotmp_[0-9]+$" "stack object .autotmp_[0-9]+ (runtime.hiter|internal/runtime/maps.Iter)$" printstring(k) // ERROR "live at call to printstring: .autotmp_[0-9]+$" } } - for k := range m { // ERROR "live at call to mapiterinit: .autotmp_[0-9]+$" "live at call to mapiternext: .autotmp_[0-9]+$" + for k := range m { // ERROR "live at call to (mapiterinit|mapIterStart): .autotmp_[0-9]+$" "live at call to (mapiternext|mapIterNext): .autotmp_[0-9]+$" printstring(k) // ERROR "live at call to printstring: .autotmp_[0-9]+$" } - for k := range m { // ERROR "live at call to mapiterinit: .autotmp_[0-9]+$" "live at call to mapiternext: .autotmp_[0-9]+$" + for k := range m { // ERROR "live at call to (mapiterinit|mapIterStart): .autotmp_[0-9]+$" "live at call to (mapiternext|mapIterNext): .autotmp_[0-9]+$" printstring(k) // ERROR "live at call to printstring: .autotmp_[0-9]+$" } }