aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorCarlos Amedee <carlos@golang.org>2025-01-31 12:45:08 -0500
committerCarlos Amedee <carlos@golang.org>2025-01-31 12:45:08 -0500
commit4241f582fc325e65b1badc6423a83a3973bcdc08 (patch)
treee806e39a406ae625bb53386720a896a93dc3a91e
parent8a4c24f9bba00d1ac5f4a5d286445af9c5afe339 (diff)
parent37f27fbecd422da9fefb8ae1cc601bc5b4fec44b (diff)
downloadgo-4241f582fc325e65b1badc6423a83a3973bcdc08.tar.xz
[release-branch.go1.24] all: merge master (37f27fb) into release-branch.go1.24
Conflicts: - src/cmd/go/testdata/script/goauth_netrc.txt Merge List: + 2025-01-31 37f27fbecd cmd/go: enable fips test and fix caching bug + 2025-01-31 77d20838e9 cmd: update golang.org/x/tools to CL 645697, and revendor + 2025-01-30 ce7ea0a6a5 cmd/go: refine GOAUTH user parsing to be more strict + 2025-01-29 e81f715515 lib/fips140: freeze v1.0.0 FIPS 140 module zip file + 2025-01-29 4f48ad5c6b cmd/link/internal/loader: fix linknames from FIPS 140 frozen tree + 2025-01-29 1f58ad5d6d Revert "os: employ sendfile(2) for file-to-file copying on Linux when needed" + 2025-01-28 90ec9996cb crypto/pbkdf2: add keyLength limit + 2025-01-28 62cd7cb6cd crypto/hkdf: check error in TestFIPSServiceIndicator + 2025-01-28 7764c502e2 crypto/internal/sysrand: skip TestNoGetrandom without cgo + 2025-01-28 50455385b0 internal/coverage: fix bug in text-format coverage output with multiple packages + 2025-01-28 28d389ef30 internal/godebug: check error from os.ReadFile in test + 2025-01-28 8071f2a169 runtime: mapiter linkname compatibility layer + 2025-01-28 78e6f2a1c8 runtime: rename mapiterinit and mapiternext + 2025-01-28 4ebd5bf855 internal/goexperiment: update location of baseline experiment in comment + 2025-01-27 f8937cb625 archive/zip, archive/tar: writer appends slash to directory names + 2025-01-27 11e08d9d96 strconv: adjust comment so that gofmt doesn't mung it + 2025-01-27 b9872221cd crypto/internal/fips140/rsa: avoid CAST unsetting the service indicator + 2025-01-27 3f791c8dfb crypto/internal/fips140/aes: set FIPS 140 service indicator for CTR and CBC + 2025-01-27 e0aeee82f3 crypto/ecdsa: avoid needless ScalarBaseMult in s390x + 2025-01-27 f70aa3824b cmd/go: do not call base.fatal for an unset HOME for GOAUTH=netrc + 2025-01-27 475e08349d Revert "runtime: Check LSE support on ARM64 at runtime init" + 2025-01-27 e2e700f8b1 crypto/internal/boring: keep ECDH public key alive during cgo calls + 2025-01-22 608acff847 go/types: avoid importer.Default + 2025-01-22 9d21ef3bd4 runtime: fix the equality check in AddCleanup + 2025-01-22 5a46b17b5f os: force a goroutine to be scheduled on WASM + 2025-01-22 6fc23a3cff crypto/internal/fips140/nistec: make p256NegCond constant time on ppc64le + 2025-01-22 70b603f4d2 go/importer: document limitations of this API + 2025-01-21 f6d17c5400 net/http: update bundled golang.org/x/net/http2 [generated] + 2025-01-21 3aa7c5ef01 testing: fix reference to B.N in docstring + 2025-01-20 3f4164f508 runtime: delete out of date comment + 2025-01-17 40b3c0e58a internal/coverage: refactor EmitTextual in preparation for bugfix + 2025-01-17 87023bb27f go/types, types2: ensure deterministic output when reporting an init cycle + 2025-01-17 80bf7d83ed go/types, types2: remove superfluous assertion (fix build) + 2025-01-16 1a93e4a2cf lib/time: update to 2025a/2025a + 2025-01-16 0b632d26b9 cmd/internal/obj/wasm, runtime: detect wasmexport call before runtime initialization + 2025-01-16 6a4effa08b crypto/x509: avoid panic when parsing partial PKCS#1 private keys + 2025-01-16 139d6eedae cmd/go: restore netrc preferences for GOAUTH and fix domain lookup + 2025-01-16 2b2314e9f6 crypto/x509: properly check for IPv6 hosts in URIs + 2025-01-16 6783377295 net/http: persist header stripping across repeated redirects + 2025-01-14 368a9ec998 encoding/json: cleanup tests + 2025-01-14 bd80d8956f cmd/go/internal/modfetch: do not trust server to send all tags in shallow fetch + 2025-01-14 4fa61d6f9c cmd/api: report error in test instead of crashing + 2025-01-14 c5e205e928 internal/runtime/maps: re-enable some tests + 2025-01-14 befc43655b testing/fstest: fix function name and comment + 2025-01-14 c83f2ca4b3 cmd/dist: ignore packages with no Go files in BenchmarkAll + 2025-01-13 6da16013ba cmd/go: check go version when parsing go.mod fails + 2025-01-13 de9fdc7b71 syscall/js: adjust comments to that gofmt does not change them + 2025-01-13 17ed215958 go/types, types2: don't panic when instantiating generic alias with wrong number of type arguments + 2025-01-13 c53307c3fd spec: fix grammar issue + 2025-01-13 47a56b2b6d encoding/json: add cases to TestUnmarshal for fatal syntactic errors + 2025-01-13 7bb192a1c5 encoding/json: always check resulting Go value for unmarshaling + 2025-01-12 44a6f817ea cmd/compile: fix write barrier coalescing + 2025-01-10 19e923182e crypto/internal/fips140test: add hmac DRBG ACVP tests + 2025-01-10 7255b94920 crypto/internal/fips140test: add ML-KEM ACVP tests + 2025-01-09 932ec2be8d crypto/rsa: fix GenerateKey flakes for toy-sized keys + 2025-01-09 d0c9142ce3 runtime/pprof: hide map runtime frames from heap profiles + 2025-01-09 c7c4420ae4 cmd/go: clarify GODEBUG in go help environment + 2025-01-09 c6ab13fc43 cmd/go/internal/mmap: reslice to file size on Windows + 2025-01-09 f5a89dff67 crypto: fix fips140=only detection of SHA-3 + 2025-01-08 4225c6cb37 encoding/json: improve fidelity of TestUnmarshal for Numbers + 2025-01-08 c87a6f932e crypto/mlkem: merge mlkem768.go and mlkem1024.go to improve godoc + 2025-01-08 f57a3a7c04 crypto/mlkem: add example and improve docs + 2025-01-08 c9afcbade7 go/types, types2: require iterator yield to return bool (work-around) + 2025-01-08 54693a81fd crypto/md5,crypto/sha1: apply fips140=only to Write and Sum, not New + 2025-01-08 0cdf8c7a8c crypto/ecdsa: apply fips140=only to deterministic ECDSA hash + 2025-01-08 4640e92af7 crypto/rsa: apply fips140=only to opts.Hash in SignPSS Change-Id: I443d8d9433e7f504905b60652d3fcd975e5f674b
-rw-r--r--doc/go_spec.html2
-rw-r--r--lib/fips140/Makefile2
-rw-r--r--lib/fips140/fips140.sum1
-rw-r--r--lib/fips140/v1.0.0.zipbin0 -> 650281 bytes
-rwxr-xr-xlib/time/update.bash4
-rw-r--r--lib/time/zoneinfo.zipbin406172 -> 406409 bytes
-rw-r--r--src/archive/tar/writer.go3
-rw-r--r--src/archive/tar/writer_test.go6
-rw-r--r--src/archive/zip/writer.go3
-rw-r--r--src/archive/zip/writer_test.go5
-rw-r--r--src/cmd/api/api_test.go16
-rw-r--r--src/cmd/compile/internal/ssa/writebarrier.go15
-rw-r--r--src/cmd/compile/internal/typecheck/_builtin/runtime.go6
-rw-r--r--src/cmd/compile/internal/typecheck/builtin.go2
-rw-r--r--src/cmd/compile/internal/types2/call.go3
-rw-r--r--src/cmd/compile/internal/types2/initorder.go10
-rw-r--r--src/cmd/compile/internal/types2/instantiate.go11
-rw-r--r--src/cmd/compile/internal/types2/stmt.go9
-rw-r--r--src/cmd/compile/internal/types2/testdata/local/issue71254.go14
-rw-r--r--src/cmd/compile/internal/types2/testdata/manual.go2
-rw-r--r--src/cmd/compile/internal/types2/typexpr.go16
-rw-r--r--src/cmd/compile/internal/types2/universe.go2
-rw-r--r--src/cmd/compile/internal/walk/range.go9
-rw-r--r--src/cmd/covdata/dump.go2
-rw-r--r--src/cmd/go.mod2
-rw-r--r--src/cmd/go.sum4
-rw-r--r--src/cmd/go/alldocs.go8
-rw-r--r--src/cmd/go/internal/auth/auth.go3
-rw-r--r--src/cmd/go/internal/auth/httputils.go173
-rw-r--r--src/cmd/go/internal/auth/userauth.go91
-rw-r--r--src/cmd/go/internal/auth/userauth_test.go56
-rw-r--r--src/cmd/go/internal/help/helpdoc.go8
-rw-r--r--src/cmd/go/internal/mmap/mmap_test.go32
-rw-r--r--src/cmd/go/internal/mmap/mmap_windows.go8
-rw-r--r--src/cmd/go/internal/mmap/testdata/small_file.txt1
-rw-r--r--src/cmd/go/internal/modfetch/cache.go7
-rw-r--r--src/cmd/go/internal/modfetch/codehost/git.go16
-rw-r--r--src/cmd/go/internal/modindex/read.go11
-rw-r--r--src/cmd/go/internal/modload/modfile.go11
-rw-r--r--src/cmd/go/testdata/script/cover_coverprofile_nocoverpkg.txt50
-rw-r--r--src/cmd/go/testdata/script/fipssnap.txt9
-rw-r--r--src/cmd/go/testdata/script/goauth_netrc.txt13
-rw-r--r--src/cmd/go/testdata/script/mod_unknown_block.txt11
-rw-r--r--src/cmd/internal/goobj/builtinlist.go2
-rw-r--r--src/cmd/internal/obj/wasm/wasmobj.go25
-rw-r--r--src/cmd/link/internal/loader/loader.go10
-rw-r--r--src/cmd/link/internal/wasm/asm.go1
-rw-r--r--src/cmd/vendor/golang.org/x/tools/go/analysis/passes/printf/printf.go48
-rw-r--r--src/cmd/vendor/modules.txt2
-rw-r--r--src/crypto/ecdsa/ecdsa.go7
-rw-r--r--src/crypto/hkdf/hkdf.go22
-rw-r--r--src/crypto/hkdf/hkdf_test.go3
-rw-r--r--src/crypto/hmac/hmac.go2
-rw-r--r--src/crypto/internal/boring/ecdh.go9
-rw-r--r--src/crypto/internal/fips140/aes/cbc.go3
-rw-r--r--src/crypto/internal/fips140/aes/ctr.go2
-rw-r--r--src/crypto/internal/fips140/ecdsa/ecdsa_s390x.go21
-rw-r--r--src/crypto/internal/fips140/ecdsa/hmacdrbg.go9
-rw-r--r--src/crypto/internal/fips140/mlkem/generate1024.go9
-rw-r--r--src/crypto/internal/fips140/mlkem/mlkem1024.go74
-rw-r--r--src/crypto/internal/fips140/mlkem/mlkem768.go76
-rw-r--r--src/crypto/internal/fips140/nistec/p256_asm_ppc64le.s20
-rw-r--r--src/crypto/internal/fips140/pbkdf2/pbkdf2.go20
-rw-r--r--src/crypto/internal/fips140/rsa/cast.go1
-rw-r--r--src/crypto/internal/fips140hash/hash.go34
-rw-r--r--src/crypto/internal/fips140test/acvp_capabilities.json18
-rw-r--r--src/crypto/internal/fips140test/acvp_test.config.json6
-rw-r--r--src/crypto/internal/fips140test/acvp_test.go185
-rw-r--r--src/crypto/internal/sysrand/rand_linux_test.go2
-rw-r--r--src/crypto/md5/md5.go13
-rw-r--r--src/crypto/mlkem/example_test.go47
-rw-r--r--src/crypto/mlkem/mlkem.go192
-rw-r--r--src/crypto/mlkem/mlkem1024.go96
-rw-r--r--src/crypto/mlkem/mlkem768.go106
-rw-r--r--src/crypto/pbkdf2/pbkdf2.go9
-rw-r--r--src/crypto/pbkdf2/pbkdf2_test.go30
-rw-r--r--src/crypto/rsa/fips.go132
-rw-r--r--src/crypto/rsa/rsa.go15
-rw-r--r--src/crypto/rsa/rsa_test.go17
-rw-r--r--src/crypto/sha1/sha1.go16
-rw-r--r--src/crypto/sha3/sha3.go6
-rw-r--r--src/encoding/json/decode_test.go139
-rw-r--r--src/encoding/json/stream_test.go10
-rw-r--r--src/encoding/json/tags_test.go4
-rw-r--r--src/go.mod2
-rw-r--r--src/go.sum4
-rw-r--r--src/go/build/deps_test.go3
-rw-r--r--src/go/importer/importer.go13
-rw-r--r--src/go/types/api_test.go10
-rw-r--r--src/go/types/call.go3
-rw-r--r--src/go/types/check_test.go3
-rw-r--r--src/go/types/eval_test.go5
-rw-r--r--src/go/types/example_test.go5
-rw-r--r--src/go/types/initorder.go10
-rw-r--r--src/go/types/instantiate.go11
-rw-r--r--src/go/types/issues_test.go9
-rw-r--r--src/go/types/lookup_test.go3
-rw-r--r--src/go/types/mono_test.go3
-rw-r--r--src/go/types/resolver_test.go6
-rw-r--r--src/go/types/self_test.go5
-rw-r--r--src/go/types/sizes_test.go14
-rw-r--r--src/go/types/stmt.go9
-rw-r--r--src/go/types/testdata/manual.go2
-rw-r--r--src/go/types/typexpr.go16
-rw-r--r--src/go/types/universe.go2
-rw-r--r--src/internal/coverage/cfile/testsupport.go2
-rw-r--r--src/internal/coverage/cformat/fmt_test.go16
-rw-r--r--src/internal/coverage/cformat/format.go22
-rw-r--r--src/internal/godebug/godebug_test.go3
-rw-r--r--src/internal/goexperiment/flags.go2
-rw-r--r--src/internal/runtime/maps/map_swiss_test.go1
-rw-r--r--src/internal/types/testdata/fixedbugs/issue71131.go15
-rw-r--r--src/internal/types/testdata/fixedbugs/issue71198.go16
-rw-r--r--src/internal/types/testdata/fixedbugs/issue71284.go10
-rw-r--r--src/internal/types/testdata/spec/range.go2
-rw-r--r--src/net/http/h2_bundle.go45
-rw-r--r--src/os/readfrom_linux_test.go52
-rw-r--r--src/os/readfrom_sendfile_test.go2
-rw-r--r--src/os/root_test.go4
-rw-r--r--src/os/zero_copy_linux.go46
-rw-r--r--src/reflect/map_noswiss.go8
-rw-r--r--src/reflect/map_swiss.go51
-rw-r--r--src/reflect/value.go6
-rw-r--r--src/runtime/asm_arm64.s37
-rw-r--r--src/runtime/asm_wasm.s10
-rw-r--r--src/runtime/linkname_swiss.go211
-rw-r--r--src/runtime/map_swiss.go107
-rw-r--r--src/runtime/map_test.go4
-rw-r--r--src/runtime/mcleanup.go14
-rw-r--r--src/runtime/mcleanup_test.go27
-rw-r--r--src/runtime/panic.go3
-rw-r--r--src/runtime/pprof/pprof.go2
-rw-r--r--src/runtime/pprof/protomem.go2
-rw-r--r--src/runtime/pprof/protomem_test.go60
-rw-r--r--src/runtime/sys_wasm.go14
-rw-r--r--src/strconv/quote.go3
-rw-r--r--src/syscall/js/js.go48
-rw-r--r--src/testing/fstest/testfs.go2
-rw-r--r--src/testing/testing.go2
-rw-r--r--src/vendor/modules.txt2
-rw-r--r--test/codegen/maps.go17
-rw-r--r--test/codegen/writebarrier.go25
-rw-r--r--test/live.go6
-rw-r--r--test/live_regabi.go6
144 files changed, 2332 insertions, 811 deletions
diff --git a/doc/go_spec.html b/doc/go_spec.html
index ab90c420fd..db5fba45a5 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 cd657ae72f..8dcb8fbebe 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 013112d9e5..66b1e23dfe 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 0000000000..bd9d3c19d0
--- /dev/null
+++ b/lib/fips140/v1.0.0.zip
Binary files differ
diff --git a/lib/time/update.bash b/lib/time/update.bash
index 6b66fa54a9..67cb016e79 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 b36e82c958..6ba9ff6fd6 100644
--- a/lib/time/zoneinfo.zip
+++ b/lib/time/zoneinfo.zip
Binary files differ
diff --git a/src/archive/tar/writer.go b/src/archive/tar/writer.go
index 059669767f..f966c5b4c6 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 2a01915d36..7b10bf6a70 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 cbe5ba2627..0a310054e3 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 27a99b6b3a..44592ce831 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 7848233333..32da68982b 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 1caccb7c18..71acefbf8a 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 9a83911487..cf07f31e31 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 6860d78b2e..be08d0b403 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 ae2ab5f984..897c846d8f 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 ef2ad010a6..699bfca8bb 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 e51cf18de6..03c490a386 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 2174aedf7f..c46ea7a091 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 0000000000..9cca9d5bc4
--- /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 d8f312f61d..825ab50f92 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 d955654fc9..e9b5ca9aa6 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 9c76ac2373..7664a53579 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 27e71425c1..a51b218ae5 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 cbbeae0a80..e141689b00 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 b29321de7b..9c29c3ac74 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 5c262454d5..593063a9da 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 20d76de0c7..2220863b8e 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 bd80222427..79e0d8b5e8 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 0000000000..b8629546d5
--- /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 0e54a83e31..1a60693a9c 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 91a5bb76ec..1b281ed3cd 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)
}
@@ -101,9 +100,54 @@ 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 65d0f1a45c..ccc04c25d2 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 0000000000..3f4b63caf1
--- /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 d00bef71e5..4163484b1a 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 0000000000..10bb609f2a
--- /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 02d3849314..9c34581a91 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 50a4526eb3..dfb3667889 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 4c1fbd8359..76216f35ba 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 94d2f5bd66..4687deae68 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 0000000000..85b3136bf9
--- /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 465f304c46..9888bc82f1 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 26e03f8968..0baa09de1e 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 0000000000..071269bb8d
--- /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 c133c60427..3e550d8dd9 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 48eee4e3ea..42e5534f3b 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 e7cc30ab07..0c234e8975 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 727da59da6..d03102cc6b 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 171ad20137..011ea8bef6 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 281989b1e2..118646d75c 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 f682e6b1c6..cb308b41e9 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 7cfbe2c60d..6b02522866 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 201b440289..57d90f88e9 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 72f5a4abea..554c8c9b78 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 b90e533e7c..ff29eb17b1 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 f92af23a2a..a5a079453f 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 2b0ee44cdd..2e55d233d3 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 271a35897f..d0a49cad61 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 4f085e2801..8f52091170 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 e002bf1414..9e38ad00df 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 c924c38293..034bf3b5d6 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 2c1cb5c33f..77043830d4 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 7c46b268ef..7efaa6ac18 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 8f6d991504..05923f6826 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 ec7b5f3aeb..b900b32c88 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 0000000000..6d67ee8b34
--- /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 6502a98db1..8a4a97758c 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 49ab51d0d2..dc4d714f19 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 139655ecf6..8dedb9a791 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 417523c29d..ab43904f91 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 75e1fc7404..a0384e175f 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 0000000000..28bf3f29e7
--- /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 0000000000..69c0bc571f
--- /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 05bad1eb2a..0000000000
--- 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 c367c551e6..0000000000
--- 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 d40daab5e5..dd5fc33f21 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 03980c7e54..eb0ed14e24 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 24dfb38cf6..8373c125ae 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 fb23f003a6..95bb4becd2 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 2535661040..73b0c3749e 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 b799f0d2fb..d2ffaac0ae 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 0f4d7ed437..a6c5ae55f1 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 a2b462af77..8aad11b8bf 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 32ede8cc7e..46f9407c88 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 1d2323dcee..eb43ff5530 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 7a1318dcac..ccfdbd8ea2 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 9e661352f1..4d6a33e34a 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 a62a5173b9..e3e01077c1 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 8a8fb0ec04..54acd7e694 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 b686578b38..27b4ab8ea0 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 200068b176..4e8dfc0d6b 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 a10d0147da..823525828a 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 b9afb9117f..49d901f692 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 279771121a..d8e5de7476 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 7625c20667..adf96fe718 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 48eef7ca76..4b36312f96 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 3eb34cf2d0..f2c63f16f9 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 d3ca58b9fa..e90a2ec89a 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 ccab846c6d..d1f19ac558 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 a83f1344de..680ee69c97 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 27fa75652a..b4cc6286a1 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 825bc1f9f5..157faf87d4 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 d3223f3b92..de3d01e8dd 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 d8f312f61d..825ab50f92 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 5bcbc2d1d3..7928ed8ef3 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 09b882ce05..750a368278 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 3594b32aee..adab47fd21 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 d296939d5c..a26de964c4 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 4df0e70b81..01d3109e31 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 6929630356..fe1e67225c 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 31b3d0315b..948ed5c802 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 4e02f3e660..6da006413a 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 0000000000..8e7575b028
--- /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 0000000000..479f8e2b0c
--- /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 0000000000..4b73087a78
--- /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 52d1e70382..c0f579479f 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 46a2b79231..22f013f1d4 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 cc0322882b..d33f9cf9c9 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 dbe1603bd1..86ef71ee02 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 b461ee2208..cbb985b2ce 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 27a0882560..9d666a3c79 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 eb0a52a390..19696a4f4b 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 75dcb117df..2eac51e57d 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 4ed94addf9..ba5b106c18 100644
--- a/src/reflect/value.go
+++ b/src/reflect/value.go
@@ -3604,12 +3604,6 @@ func mapdelete(t *abi.Type, m unsafe.Pointer, key unsafe.Pointer)
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
func mapclear(t *abi.Type, m unsafe.Pointer)
diff --git a/src/runtime/asm_arm64.s b/src/runtime/asm_arm64.s
index 88bfd3ce5c..64a1880589 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 016d2d3825..69da583a1d 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 0000000000..1be724477e
--- /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 e6e29bcfb8..a8fe87257a 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 e3c092bef9..c522c44a4e 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 22d40a5e84..972532d475 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 8c2d1f0647..d62356feef 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 dc7a7fe357..3ffb3966d0 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 f6b4a5c367..b7680a13fd 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 ab3550f43f..72aad82b30 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 885f4dca5b..4d08e67ddc 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 f88b992e9c..6b40a8d3e9 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 1f4929a952..99c292a8ed 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 74c02cdbe6..bbf3de199b 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 2917a303b2..affdfa6429 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 be6391b0ab..3833bfc84b 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 1c8de570cc..d42f50b43c 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 d7cf6534ad..c4aed33545 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 e125973e7c..e2b1399399 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 250a77cdac..c0b0fcd274 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 090e2ec577..35f874ecc3 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]+$"
}
}