From 6d4ee095f591f6e535aa677ab29fce6652afe4c3 Mon Sep 17 00:00:00 2001 From: Vorapol Rinsatitnon Date: Fri, 10 Oct 2025 07:17:45 +0700 Subject: [PATCH] Update to go1.25.2 --- VERSION | 4 +- doc/go_spec.html | 2 +- doc/godebug.md | 10 + lib/fips140/fips140.sum | 2 +- lib/fips140/inprocess.txt | 2 +- .../{v1.0.0.zip => v1.0.0-c2097c7c.zip} | Bin 650281 -> 658130 bytes lib/fips140/v1.0.0.txt | 1 + src/archive/tar/common.go | 1 + src/archive/tar/reader.go | 9 +- src/archive/tar/reader_test.go | 5 + .../testdata/gnu-sparse-many-zeros.tar.bz2 | Bin 0 -> 1642 bytes src/cmd/compile/internal/dwarfgen/dwarf.go | 15 ++ src/cmd/compile/internal/ssa/tighten.go | 27 ++- src/cmd/go/internal/fips140/mkzip.go | 8 +- src/cmd/go/testdata/script/fipssnap.txt | 5 +- src/context/context.go | 2 + src/context/x_test.go | 20 ++ src/crypto/internal/cryptotest/hash.go | 2 +- src/crypto/internal/fips140/cast.go | 17 +- src/crypto/internal/fips140/ecdh/ecdh.go | 43 ++-- src/crypto/internal/fips140/ecdsa/cast.go | 4 +- src/crypto/internal/fips140/ecdsa/ecdsa.go | 10 +- src/crypto/internal/fips140/ecdsa/hmacdrbg.go | 2 +- src/crypto/internal/fips140/ed25519/cast.go | 4 +- .../internal/fips140/ed25519/ed25519.go | 15 +- src/crypto/internal/fips140/fips140.go | 9 +- .../internal/fips140/mlkem/mlkem1024.go | 9 +- src/crypto/internal/fips140/mlkem/mlkem768.go | 9 +- src/crypto/internal/fips140/rsa/keygen.go | 23 +- src/crypto/internal/fips140/rsa/rsa.go | 20 -- src/crypto/internal/fips140test/acvp_test.go | 2 +- src/crypto/internal/fips140test/cast_test.go | 92 ++++---- src/crypto/internal/fips140test/fips_test.go | 8 +- src/crypto/tls/handshake_server.go | 2 +- src/crypto/x509/name_constraints_test.go | 75 +------ src/crypto/x509/parser.go | 77 ++++--- src/crypto/x509/parser_test.go | 43 ++++ src/crypto/x509/verify.go | 6 +- src/crypto/x509/verify_test.go | 127 +++++++++++ src/debug/pe/symbol.go | 7 +- src/encoding/asn1/asn1.go | 10 +- src/encoding/asn1/asn1_test.go | 38 ++++ src/encoding/pem/pem.go | 67 +++--- src/encoding/pem/pem_test.go | 13 +- src/go/build/deps_test.go | 12 +- src/internal/buildcfg/cfg.go | 5 +- src/internal/godebugs/table.go | 1 + src/internal/poll/fd_windows.go | 40 +++- src/internal/synctest/synctest_test.go | 22 ++ src/net/http/cookie.go | 59 ++++- src/net/http/cookie_test.go | 206 ++++++++++++------ src/net/http/transport.go | 5 +- src/net/http/transport_test.go | 32 +++ src/net/mail/message.go | 6 +- src/net/textproto/reader.go | 11 +- src/net/udpsock_test.go | 5 + src/net/url/url.go | 42 +++- src/net/url/url_test.go | 39 ++++ src/os/os_windows_test.go | 28 +++ src/os/root_openat.go | 4 +- src/os/root_test.go | 33 +++ src/os/root_unix.go | 2 +- src/os/root_windows.go | 2 +- src/runtime/decoratemappings_test.go | 72 ++++++ src/runtime/export_test.go | 4 + src/runtime/metrics/doc.go | 5 + src/runtime/proc.go | 24 +- src/runtime/runtime1.go | 15 +- src/runtime/set_vma_name_linux.go | 6 +- src/runtime/set_vma_name_stub.go | 2 + src/runtime/synctest.go | 2 + test/fixedbugs/issue75569.go | 77 +++++++ 72 files changed, 1167 insertions(+), 441 deletions(-) rename lib/fips140/{v1.0.0.zip => v1.0.0-c2097c7c.zip} (78%) create mode 100644 lib/fips140/v1.0.0.txt create mode 100644 src/archive/tar/testdata/gnu-sparse-many-zeros.tar.bz2 create mode 100644 src/runtime/decoratemappings_test.go create mode 100644 test/fixedbugs/issue75569.go diff --git a/VERSION b/VERSION index cf1f2f4e..4097adc4 100644 --- a/VERSION +++ b/VERSION @@ -1,2 +1,2 @@ -go1.25.1 -time 2025-08-27T15:49:40Z +go1.25.2 +time 2025-10-02T18:00:14Z diff --git a/doc/go_spec.html b/doc/go_spec.html index 183bc7fb..2b47fb2e 100644 --- a/doc/go_spec.html +++ b/doc/go_spec.html @@ -1,6 +1,6 @@ diff --git a/doc/godebug.md b/doc/godebug.md index aaa0f9dd..c12ce531 100644 --- a/doc/godebug.md +++ b/doc/godebug.md @@ -153,6 +153,16 @@ for example, see the [runtime documentation](/pkg/runtime#hdr-Environment_Variables) and the [go command documentation](/cmd/go#hdr-Build_and_test_caching). +### Go 1.26 + +Go 1.26 added a new `httpcookiemaxnum` setting that controls the maximum number +of cookies that net/http will accept when parsing HTTP headers. If the number of +cookie in a header exceeds the number set in `httpcookiemaxnum`, cookie parsing +will fail early. The default value is `httpcookiemaxnum=3000`. Setting +`httpcookiemaxnum=0` will allow the cookie parsing to accept an indefinite +number of cookies. To avoid denial of service attacks, this setting and default +was backported to Go 1.25.2 and Go 1.24.8. + ### Go 1.25 Go 1.25 added a new `decoratemappings` setting that controls whether the Go diff --git a/lib/fips140/fips140.sum b/lib/fips140/fips140.sum index 66b1e23d..703d1dc6 100644 --- a/lib/fips140/fips140.sum +++ b/lib/fips140/fips140.sum @@ -9,4 +9,4 @@ # # go test cmd/go/internal/fips140 -update # -v1.0.0.zip b50508feaeff05d22516b21e1fd210bbf5d6a1e422eaf2cfa23fe379342713b8 +v1.0.0-c2097c7c.zip daf3614e0406f67ae6323c902db3f953a1effb199142362a039e7526dfb9368b diff --git a/lib/fips140/inprocess.txt b/lib/fips140/inprocess.txt index 0ec25f75..efd3caba 100644 --- a/lib/fips140/inprocess.txt +++ b/lib/fips140/inprocess.txt @@ -1 +1 @@ -v1.0.0 +v1.0.0-c2097c7c diff --git a/lib/fips140/v1.0.0.zip b/lib/fips140/v1.0.0-c2097c7c.zip similarity index 78% rename from lib/fips140/v1.0.0.zip rename to lib/fips140/v1.0.0-c2097c7c.zip index bd9d3c19d05b9075a2587806d00224c3f8859ba1..aabf762d0fecb29fcf3e42fbf8acefc994cf8358 100644 GIT binary patch delta 60205 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

g^bw;!&4w3UNR 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 delta 52540 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 maxSpecialFileSize { + return errSparseTooLong + } if _, err := mustReadFull(r, blk[:]); err != nil { return err } @@ -569,8 +574,8 @@ func readGNUSparseMap1x0(r io.Reader) (sparseDatas, error) { } // Parse for all member entries. - // numEntries is trusted after this since a potential attacker must have - // committed resources proportional to what this library used. + // numEntries is trusted after this since feedTokens limits the number of + // tokens based on maxSpecialFileSize. if err := feedTokens(2 * numEntries); err != nil { return nil, err } diff --git a/src/archive/tar/reader_test.go b/src/archive/tar/reader_test.go index 99340a30..fca53dae 100644 --- a/src/archive/tar/reader_test.go +++ b/src/archive/tar/reader_test.go @@ -621,6 +621,11 @@ func TestReader(t *testing.T) { }, Format: FormatPAX, }}, + }, { + // Small compressed file that uncompresses to + // a file with a very large GNU 1.0 sparse map. + file: "testdata/gnu-sparse-many-zeros.tar.bz2", + err: errSparseTooLong, }} for _, v := range vectors { diff --git a/src/archive/tar/testdata/gnu-sparse-many-zeros.tar.bz2 b/src/archive/tar/testdata/gnu-sparse-many-zeros.tar.bz2 new file mode 100644 index 0000000000000000000000000000000000000000..751d7fd4b68be1a7439413b4089dbbde33a2900a GIT binary patch literal 1642 zcmZ>Y%CIzaj8qGb%wCwXiS2H0{DC_Y9GL$z2|RUTNGrcrz`)SKVZdrPcO01{ps1pvVeGD$*OEWa_73ksV;zBwoQh0BPEH%vtSITeAlMvtlB3E`^y)8{ zw~&+g8{-?Ahs_oW3u(QcHog6FpP~f~~=z-9g zf_wTZ7^XHZn&-}Zc8R{Fft9C2PEyk*pJkiw?fk^YcK6r>1_p%&1_y>jhFQI$94?0z za?Cl(nljTjt?TS76W-mu3JeU63=9nnhZs#ILKKBvodhkrxK0|)I$ibrgoSju6wr@N z42%qnElgP^KzXhfrD74$NvWo@w9QvsFnedxz`)4Dz{J4J!Ez-rpv4zx#WM|Ul~dWe zujeZ~$i0&Z3?dE&76vu}&J`B}T70wwI?n`zc}}^OvF6@lhub?YF)*+QFmN#NCX1lVw7II6Iq$It1A~AA zg93v=gRdLVZlD!@quw8l{n5lfn)hi|^v!SyiA^y0F{gnCT=X?DusWMCWpyr3vv&I) ZCg2!yghOkJkj3svnul7NKE7EI0RUq%CTjoy literal 0 HcmV?d00001 diff --git a/src/cmd/compile/internal/dwarfgen/dwarf.go b/src/cmd/compile/internal/dwarfgen/dwarf.go index 7d75c0c5..4ded846a 100644 --- a/src/cmd/compile/internal/dwarfgen/dwarf.go +++ b/src/cmd/compile/internal/dwarfgen/dwarf.go @@ -128,14 +128,29 @@ func Info(ctxt *obj.Link, fnsym *obj.LSym, infosym *obj.LSym, curfn obj.Func) (s // already referenced by a dwarf var, attach an R_USETYPE relocation to // the function symbol to insure that the type included in DWARF // processing during linking. + // Do the same with R_USEIFACE relocations from the function symbol for the + // same reason. + // All these R_USETYPE relocations are only looked at if the function + // survives deadcode elimination in the linker. typesyms := []*obj.LSym{} for t := range fnsym.Func().Autot { typesyms = append(typesyms, t) } + for i := range fnsym.R { + if fnsym.R[i].Type == objabi.R_USEIFACE && !strings.HasPrefix(fnsym.R[i].Sym.Name, "go:itab.") { + // Types referenced through itab will be referenced from somewhere else + typesyms = append(typesyms, fnsym.R[i].Sym) + } + } slices.SortFunc(typesyms, func(a, b *obj.LSym) int { return strings.Compare(a.Name, b.Name) }) + var lastsym *obj.LSym for _, sym := range typesyms { + if sym == lastsym { + continue + } + lastsym = sym infosym.AddRel(ctxt, obj.Reloc{Type: objabi.R_USETYPE, Sym: sym}) } fnsym.Func().Autot = nil diff --git a/src/cmd/compile/internal/ssa/tighten.go b/src/cmd/compile/internal/ssa/tighten.go index eb5007b2..0a4b56d5 100644 --- a/src/cmd/compile/internal/ssa/tighten.go +++ b/src/cmd/compile/internal/ssa/tighten.go @@ -124,18 +124,21 @@ func tighten(f *Func) { // If the target location is inside a loop, // move the target location up to just before the loop head. - for _, b := range f.Blocks { - origloop := loops.b2l[b.ID] - for _, v := range b.Values { - t := target[v.ID] - if t == nil { - continue - } - targetloop := loops.b2l[t.ID] - for targetloop != nil && (origloop == nil || targetloop.depth > origloop.depth) { - t = idom[targetloop.header.ID] - target[v.ID] = t - targetloop = loops.b2l[t.ID] + if !loops.hasIrreducible { + // Loop info might not be correct for irreducible loops. See issue 75569. + for _, b := range f.Blocks { + origloop := loops.b2l[b.ID] + for _, v := range b.Values { + t := target[v.ID] + if t == nil { + continue + } + targetloop := loops.b2l[t.ID] + for targetloop != nil && (origloop == nil || targetloop.depth > origloop.depth) { + t = idom[targetloop.header.ID] + target[v.ID] = t + targetloop = loops.b2l[t.ID] + } } } } diff --git a/src/cmd/go/internal/fips140/mkzip.go b/src/cmd/go/internal/fips140/mkzip.go index 7a6ba803..a139a0f2 100644 --- a/src/cmd/go/internal/fips140/mkzip.go +++ b/src/cmd/go/internal/fips140/mkzip.go @@ -27,10 +27,10 @@ import ( "log" "os" "path/filepath" - "regexp" "strings" "golang.org/x/mod/module" + "golang.org/x/mod/semver" modzip "golang.org/x/mod/zip" ) @@ -61,7 +61,7 @@ func main() { // Must have valid version, and must not overwrite existing file. version := flag.Arg(0) - if !regexp.MustCompile(`^v\d+\.\d+\.\d+$`).MatchString(version) { + if semver.Canonical(version) != version { log.Fatalf("invalid version %q; must be vX.Y.Z", version) } if _, err := os.Stat(version + ".zip"); err == nil { @@ -117,7 +117,9 @@ func main() { if !bytes.Contains(contents, []byte(returnLine)) { log.Fatalf("did not find %q in fips140.go", returnLine) } - newLine := `return "` + version + `"` + // Use only the vX.Y.Z part of a possible vX.Y.Z-hash version. + v, _, _ := strings.Cut(version, "-") + newLine := `return "` + v + `"` contents = bytes.ReplaceAll(contents, []byte(returnLine), []byte(newLine)) wf, err := zw.Create(f.Name) if err != nil { diff --git a/src/cmd/go/testdata/script/fipssnap.txt b/src/cmd/go/testdata/script/fipssnap.txt index 9888bc82..4d96aedf 100644 --- a/src/cmd/go/testdata/script/fipssnap.txt +++ b/src/cmd/go/testdata/script/fipssnap.txt @@ -1,4 +1,4 @@ -env snap=v1.0.0 +env snap=v1.0.0-c2097c7c env alias=inprocess env GOFIPS140=$snap @@ -23,8 +23,7 @@ stdout crypto/internal/fips140/$snap/sha256 ! stdout crypto/internal/fips140/check # again with GOFIPS140=$alias -# TODO: enable when we add inprocess.txt -# env GOFIPS140=$alias +env GOFIPS140=$alias # default GODEBUG includes fips140=on go list -f '{{.DefaultGODEBUG}}' diff --git a/src/context/context.go b/src/context/context.go index 4f150f6a..24bb18ab 100644 --- a/src/context/context.go +++ b/src/context/context.go @@ -463,6 +463,8 @@ func (c *cancelCtx) Done() <-chan struct{} { func (c *cancelCtx) Err() error { // An atomic load is ~5x faster than a mutex, which can matter in tight loops. if err := c.err.Load(); err != nil { + // Ensure the done channel has been closed before returning a non-nil error. + <-c.Done() return err.(error) } return nil diff --git a/src/context/x_test.go b/src/context/x_test.go index 937cab14..0cf19688 100644 --- a/src/context/x_test.go +++ b/src/context/x_test.go @@ -1177,3 +1177,23 @@ func (c *customContext) Err() error { func (c *customContext) Value(key any) any { return c.parent.Value(key) } + +// Issue #75533. +func TestContextErrDoneRace(t *testing.T) { + // 4 iterations reliably reproduced #75533. + for range 10 { + ctx, cancel := WithCancel(Background()) + donec := ctx.Done() + go cancel() + for ctx.Err() == nil { + if runtime.GOARCH == "wasm" { + runtime.Gosched() // need to explicitly yield + } + } + select { + case <-donec: + default: + t.Fatalf("ctx.Err is non-nil, but ctx.Done is not closed") + } + } +} diff --git a/src/crypto/internal/cryptotest/hash.go b/src/crypto/internal/cryptotest/hash.go index f00e9c80..37fd96a2 100644 --- a/src/crypto/internal/cryptotest/hash.go +++ b/src/crypto/internal/cryptotest/hash.go @@ -20,7 +20,7 @@ type MakeHash func() hash.Hash // TestHash performs a set of tests on hash.Hash implementations, checking the // documented requirements of Write, Sum, Reset, Size, and BlockSize. func TestHash(t *testing.T, mh MakeHash) { - if boring.Enabled || fips140.Version() == "v1.0" { + if boring.Enabled || fips140.Version() == "v1.0.0" { testhash.TestHashWithoutClone(t, testhash.MakeHash(mh)) return } diff --git a/src/crypto/internal/fips140/cast.go b/src/crypto/internal/fips140/cast.go index 66e21d8a..3968dcad 100644 --- a/src/crypto/internal/fips140/cast.go +++ b/src/crypto/internal/fips140/cast.go @@ -56,9 +56,10 @@ func CAST(name string, f func() error) { } // PCT runs the named Pairwise Consistency Test (if operated in FIPS mode) and -// returns any errors. If an error is returned, the key must not be used. +// aborts the program (stopping the module input/output and entering the "error +// state") if the test fails. // -// PCTs are mandatory for every key pair that is generated/imported, including +// PCTs are mandatory for every generated (but not imported) key pair, including // ephemeral keys (which effectively doubles the cost of key establishment). See // Implementation Guidance 10.3.A Additional Comment 1. // @@ -66,17 +67,23 @@ func CAST(name string, f func() error) { // // If a package p calls PCT during key generation, an invocation of that // function should be added to fipstest.TestConditionals. -func PCT(name string, f func() error) error { +func PCT(name string, f func() error) { if strings.ContainsAny(name, ",#=:") { panic("fips: invalid self-test name: " + name) } if !Enabled { - return nil + return } err := f() if name == failfipscast { err = errors.New("simulated PCT failure") } - return err + if err != nil { + fatal("FIPS 140-3 self-test failed: " + name + ": " + err.Error()) + panic("unreachable") + } + if debug { + println("FIPS 140-3 PCT passed:", name) + } } diff --git a/src/crypto/internal/fips140/ecdh/ecdh.go b/src/crypto/internal/fips140/ecdh/ecdh.go index bf71c75a..967032aa 100644 --- a/src/crypto/internal/fips140/ecdh/ecdh.go +++ b/src/crypto/internal/fips140/ecdh/ecdh.go @@ -161,6 +161,27 @@ func GenerateKey[P Point[P]](c *Curve[P], rand io.Reader) (*PrivateKey, error) { if err != nil { continue } + + // A "Pairwise Consistency Test" makes no sense if we just generated the + // public key from an ephemeral private key. Moreover, there is no way to + // check it aside from redoing the exact same computation again. SP 800-56A + // Rev. 3, Section 5.6.2.1.4 acknowledges that, and doesn't require it. + // However, ISO 19790:2012, Section 7.10.3.3 has a blanket requirement for a + // PCT for all generated keys (AS10.35) and FIPS 140-3 IG 10.3.A, Additional + // Comment 1 goes out of its way to say that "the PCT shall be performed + // consistent [...], even if the underlying standard does not require a + // PCT". So we do it. And make ECDH nearly 50% slower (only) in FIPS mode. + fips140.PCT("ECDH PCT", func() error { + p1, err := c.newPoint().ScalarBaseMult(privateKey.d) + if err != nil { + return err + } + if !bytes.Equal(p1.Bytes(), privateKey.pub.q) { + return errors.New("crypto/ecdh: public key does not match private key") + } + return nil + }) + return privateKey, nil } } @@ -188,28 +209,6 @@ func NewPrivateKey[P Point[P]](c *Curve[P], key []byte) (*PrivateKey, error) { panic("crypto/ecdh: internal error: public key is the identity element") } - // A "Pairwise Consistency Test" makes no sense if we just generated the - // public key from an ephemeral private key. Moreover, there is no way to - // check it aside from redoing the exact same computation again. SP 800-56A - // Rev. 3, Section 5.6.2.1.4 acknowledges that, and doesn't require it. - // However, ISO 19790:2012, Section 7.10.3.3 has a blanket requirement for a - // PCT for all generated keys (AS10.35) and FIPS 140-3 IG 10.3.A, Additional - // Comment 1 goes out of its way to say that "the PCT shall be performed - // consistent [...], even if the underlying standard does not require a - // PCT". So we do it. And make ECDH nearly 50% slower (only) in FIPS mode. - if err := fips140.PCT("ECDH PCT", func() error { - p1, err := c.newPoint().ScalarBaseMult(key) - if err != nil { - return err - } - if !bytes.Equal(p1.Bytes(), publicKey) { - return errors.New("crypto/ecdh: public key does not match private key") - } - return nil - }); err != nil { - panic(err) - } - k := &PrivateKey{d: bytes.Clone(key), pub: PublicKey{curve: c.curve, q: publicKey}} return k, nil } diff --git a/src/crypto/internal/fips140/ecdsa/cast.go b/src/crypto/internal/fips140/ecdsa/cast.go index 219b7211..6bc9fd1f 100644 --- a/src/crypto/internal/fips140/ecdsa/cast.go +++ b/src/crypto/internal/fips140/ecdsa/cast.go @@ -51,8 +51,8 @@ func testHash() []byte { } } -func fipsPCT[P Point[P]](c *Curve[P], k *PrivateKey) error { - return fips140.PCT("ECDSA PCT", func() error { +func fipsPCT[P Point[P]](c *Curve[P], k *PrivateKey) { + fips140.PCT("ECDSA PCT", func() error { hash := testHash() drbg := newDRBG(sha512.New, k.d, bits2octets(P256(), hash), nil) sig, err := sign(c, k, drbg, hash) diff --git a/src/crypto/internal/fips140/ecdsa/ecdsa.go b/src/crypto/internal/fips140/ecdsa/ecdsa.go index 47c1b244..81179de4 100644 --- a/src/crypto/internal/fips140/ecdsa/ecdsa.go +++ b/src/crypto/internal/fips140/ecdsa/ecdsa.go @@ -167,11 +167,6 @@ func NewPrivateKey[P Point[P]](c *Curve[P], D, Q []byte) (*PrivateKey, error) { return nil, err } priv := &PrivateKey{pub: *pub, d: d.Bytes(c.N)} - if err := fipsPCT(c, priv); err != nil { - // This can happen if the application went out of its way to make an - // ecdsa.PrivateKey with a mismatching PublicKey. - return nil, err - } return priv, nil } @@ -204,10 +199,7 @@ func GenerateKey[P Point[P]](c *Curve[P], rand io.Reader) (*PrivateKey, error) { }, d: k.Bytes(c.N), } - if err := fipsPCT(c, priv); err != nil { - // This clearly can't happen, but FIPS 140-3 mandates that we check it. - panic(err) - } + fipsPCT(c, priv) return priv, nil } diff --git a/src/crypto/internal/fips140/ecdsa/hmacdrbg.go b/src/crypto/internal/fips140/ecdsa/hmacdrbg.go index fa82ce39..698c23bc 100644 --- a/src/crypto/internal/fips140/ecdsa/hmacdrbg.go +++ b/src/crypto/internal/fips140/ecdsa/hmacdrbg.go @@ -122,7 +122,7 @@ func newDRBG[H hash.Hash](hash func() H, entropy, nonce []byte, s personalizatio // // This should only be used for ACVP testing. hmacDRBG is not intended to be // used directly. -func TestingOnlyNewDRBG(hash func() hash.Hash, entropy, nonce []byte, s []byte) *hmacDRBG { +func TestingOnlyNewDRBG[H hash.Hash](hash func() H, entropy, nonce []byte, s []byte) *hmacDRBG { return newDRBG(hash, entropy, nonce, plainPersonalizationString(s)) } diff --git a/src/crypto/internal/fips140/ed25519/cast.go b/src/crypto/internal/fips140/ed25519/cast.go index a680c251..2a3426bd 100644 --- a/src/crypto/internal/fips140/ed25519/cast.go +++ b/src/crypto/internal/fips140/ed25519/cast.go @@ -12,8 +12,8 @@ import ( "sync" ) -func fipsPCT(k *PrivateKey) error { - return fips140.PCT("Ed25519 sign and verify PCT", func() error { +func fipsPCT(k *PrivateKey) { + fips140.PCT("Ed25519 sign and verify PCT", func() error { return pairwiseTest(k) }) } diff --git a/src/crypto/internal/fips140/ed25519/ed25519.go b/src/crypto/internal/fips140/ed25519/ed25519.go index bbdc5b4a..8beda341 100644 --- a/src/crypto/internal/fips140/ed25519/ed25519.go +++ b/src/crypto/internal/fips140/ed25519/ed25519.go @@ -69,10 +69,7 @@ func generateKey(priv *PrivateKey) (*PrivateKey, error) { fips140.RecordApproved() drbg.Read(priv.seed[:]) precomputePrivateKey(priv) - if err := fipsPCT(priv); err != nil { - // This clearly can't happen, but FIPS 140-3 requires that we check. - panic(err) - } + fipsPCT(priv) return priv, nil } @@ -88,10 +85,6 @@ func newPrivateKeyFromSeed(priv *PrivateKey, seed []byte) (*PrivateKey, error) { } copy(priv.seed[:], seed) precomputePrivateKey(priv) - if err := fipsPCT(priv); err != nil { - // This clearly can't happen, but FIPS 140-3 requires that we check. - panic(err) - } return priv, nil } @@ -137,12 +130,6 @@ func newPrivateKey(priv *PrivateKey, privBytes []byte) (*PrivateKey, error) { copy(priv.prefix[:], h[32:]) - if err := fipsPCT(priv); err != nil { - // This can happen if the application messed with the private key - // encoding, and the public key doesn't match the seed anymore. - return nil, err - } - return priv, nil } diff --git a/src/crypto/internal/fips140/fips140.go b/src/crypto/internal/fips140/fips140.go index 050967f4..e48706fb 100644 --- a/src/crypto/internal/fips140/fips140.go +++ b/src/crypto/internal/fips140/fips140.go @@ -7,7 +7,6 @@ package fips140 import ( "crypto/internal/fips140deps/godebug" "errors" - "hash" "runtime" ) @@ -63,16 +62,10 @@ func Name() string { return "Go Cryptographic Module" } -// Version returns the formal version (such as "v1.0") if building against a +// Version returns the formal version (such as "v1.0.0") if building against a // frozen module with GOFIPS140. Otherwise, it returns "latest". func Version() string { // This return value is replaced by mkzip.go, it must not be changed or // moved to a different file. return "latest" //mkzip:version } - -// Hash is a legacy compatibility alias for hash.Hash. -// -// It's only here because [crypto/internal/fips140/ecdsa.TestingOnlyNewDRBG] -// takes a "func() fips140.Hash" in v1.0.0, instead of being generic. -type Hash = hash.Hash diff --git a/src/crypto/internal/fips140/mlkem/mlkem1024.go b/src/crypto/internal/fips140/mlkem/mlkem1024.go index 034bf3b5..1419cf20 100644 --- a/src/crypto/internal/fips140/mlkem/mlkem1024.go +++ b/src/crypto/internal/fips140/mlkem/mlkem1024.go @@ -118,10 +118,7 @@ func generateKey1024(dk *DecapsulationKey1024) (*DecapsulationKey1024, error) { var z [32]byte drbg.Read(z[:]) kemKeyGen1024(dk, &d, &z) - if err := fips140.PCT("ML-KEM PCT", func() error { return kemPCT1024(dk) }); err != nil { - // This clearly can't happen, but FIPS 140-3 requires us to check. - panic(err) - } + fips140.PCT("ML-KEM PCT", func() error { return kemPCT1024(dk) }) fips140.RecordApproved() return dk, nil } @@ -149,10 +146,6 @@ func newKeyFromSeed1024(dk *DecapsulationKey1024, seed []byte) (*DecapsulationKe d := (*[32]byte)(seed[:32]) z := (*[32]byte)(seed[32:]) kemKeyGen1024(dk, d, z) - if err := fips140.PCT("ML-KEM PCT", func() error { return kemPCT1024(dk) }); err != nil { - // This clearly can't happen, but FIPS 140-3 requires us to check. - panic(err) - } fips140.RecordApproved() return dk, nil } diff --git a/src/crypto/internal/fips140/mlkem/mlkem768.go b/src/crypto/internal/fips140/mlkem/mlkem768.go index 77043830..298660e4 100644 --- a/src/crypto/internal/fips140/mlkem/mlkem768.go +++ b/src/crypto/internal/fips140/mlkem/mlkem768.go @@ -177,10 +177,7 @@ func generateKey(dk *DecapsulationKey768) (*DecapsulationKey768, error) { var z [32]byte drbg.Read(z[:]) kemKeyGen(dk, &d, &z) - if err := fips140.PCT("ML-KEM PCT", func() error { return kemPCT(dk) }); err != nil { - // This clearly can't happen, but FIPS 140-3 requires us to check. - panic(err) - } + fips140.PCT("ML-KEM PCT", func() error { return kemPCT(dk) }) fips140.RecordApproved() return dk, nil } @@ -208,10 +205,6 @@ func newKeyFromSeed(dk *DecapsulationKey768, seed []byte) (*DecapsulationKey768, d := (*[32]byte)(seed[:32]) z := (*[32]byte)(seed[32:]) kemKeyGen(dk, d, z) - if err := fips140.PCT("ML-KEM PCT", func() error { return kemPCT(dk) }); err != nil { - // This clearly can't happen, but FIPS 140-3 requires us to check. - panic(err) - } fips140.RecordApproved() return dk, nil } diff --git a/src/crypto/internal/fips140/rsa/keygen.go b/src/crypto/internal/fips140/rsa/keygen.go index 7c027223..00b325d2 100644 --- a/src/crypto/internal/fips140/rsa/keygen.go +++ b/src/crypto/internal/fips140/rsa/keygen.go @@ -105,7 +105,28 @@ func GenerateKey(rand io.Reader, bits int) (*PrivateKey, error) { // negligible chance of failure we can defer the check to the end of key // generation and return an error if it fails. See [checkPrivateKey]. - return newPrivateKey(N, 65537, d, P, Q) + k, err := newPrivateKey(N, 65537, d, P, Q) + if err != nil { + return nil, err + } + + if k.fipsApproved { + fips140.PCT("RSA sign and verify PCT", func() error { + hash := []byte{ + 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, + 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, + 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, + 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20, + } + sig, err := signPKCS1v15(k, "SHA-256", hash) + if err != nil { + return err + } + return verifyPKCS1v15(k.PublicKey(), "SHA-256", hash, sig) + }) + } + + return k, nil } } diff --git a/src/crypto/internal/fips140/rsa/rsa.go b/src/crypto/internal/fips140/rsa/rsa.go index 0bbf7010..76433894 100644 --- a/src/crypto/internal/fips140/rsa/rsa.go +++ b/src/crypto/internal/fips140/rsa/rsa.go @@ -310,26 +310,6 @@ func checkPrivateKey(priv *PrivateKey) error { return errors.New("crypto/rsa: d too small") } - // If the key is still in scope for FIPS mode, perform a Pairwise - // Consistency Test. - if priv.fipsApproved { - if err := fips140.PCT("RSA sign and verify PCT", func() error { - hash := []byte{ - 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, - 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, - 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, - 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20, - } - sig, err := signPKCS1v15(priv, "SHA-256", hash) - if err != nil { - return err - } - return verifyPKCS1v15(priv.PublicKey(), "SHA-256", hash, sig) - }); err != nil { - return err - } - } - return nil } diff --git a/src/crypto/internal/fips140test/acvp_test.go b/src/crypto/internal/fips140test/acvp_test.go index 5871bde8..47a42cce 100644 --- a/src/crypto/internal/fips140test/acvp_test.go +++ b/src/crypto/internal/fips140test/acvp_test.go @@ -1624,7 +1624,7 @@ func cmdHmacDrbgAft(h func() hash.Hash) command { // * Uninstantiate // See Table 7 in draft-vassilev-acvp-drbg out := make([]byte, outLen) - drbg := ecdsa.TestingOnlyNewDRBG(func() fips140.Hash { return h() }, entropy, nonce, personalization) + drbg := ecdsa.TestingOnlyNewDRBG(h, entropy, nonce, personalization) drbg.Generate(out) drbg.Generate(out) diff --git a/src/crypto/internal/fips140test/cast_test.go b/src/crypto/internal/fips140test/cast_test.go index 1818b583..14ab72d1 100644 --- a/src/crypto/internal/fips140test/cast_test.go +++ b/src/crypto/internal/fips140test/cast_test.go @@ -5,9 +5,9 @@ package fipstest import ( + "crypto" + "crypto/internal/fips140" "crypto/rand" - "crypto/x509" - "encoding/pem" "fmt" "internal/testenv" "io/fs" @@ -50,8 +50,6 @@ var allCASTs = []string{ "KAS-ECC-SSC P-256", "ML-KEM PCT", "ML-KEM PCT", - "ML-KEM PCT", - "ML-KEM PCT", "ML-KEM-768", "PBKDF2", "RSA sign and verify PCT", @@ -107,60 +105,65 @@ func TestAllCASTs(t *testing.T) { // TestConditionals causes the conditional CASTs and PCTs to be invoked. func TestConditionals(t *testing.T) { mlkem.GenerateKey768() - k, err := ecdh.GenerateKey(ecdh.P256(), rand.Reader) + kDH, err := ecdh.GenerateKey(ecdh.P256(), rand.Reader) if err != nil { - t.Fatal(err) + t.Error(err) + } else { + ecdh.ECDH(ecdh.P256(), kDH, kDH.PublicKey()) } - ecdh.ECDH(ecdh.P256(), k, k.PublicKey()) kDSA, err := ecdsa.GenerateKey(ecdsa.P256(), rand.Reader) if err != nil { - t.Fatal(err) + t.Error(err) + } else { + ecdsa.SignDeterministic(ecdsa.P256(), sha256.New, kDSA, make([]byte, 32)) } - ecdsa.SignDeterministic(ecdsa.P256(), sha256.New, kDSA, make([]byte, 32)) k25519, err := ed25519.GenerateKey() if err != nil { - t.Fatal(err) + t.Error(err) + } else { + ed25519.Sign(k25519, make([]byte, 32)) } - ed25519.Sign(k25519, make([]byte, 32)) - rsa.VerifyPKCS1v15(&rsa.PublicKey{}, "", nil, nil) - // Parse an RSA key to hit the PCT rather than generating one (which is slow). - block, _ := pem.Decode([]byte(strings.ReplaceAll( - `-----BEGIN RSA TESTING KEY----- -MIIEowIBAAKCAQEAsPnoGUOnrpiSqt4XynxA+HRP7S+BSObI6qJ7fQAVSPtRkqso -tWxQYLEYzNEx5ZSHTGypibVsJylvCfuToDTfMul8b/CZjP2Ob0LdpYrNH6l5hvFE -89FU1nZQF15oVLOpUgA7wGiHuEVawrGfey92UE68mOyUVXGweJIVDdxqdMoPvNNU -l86BU02vlBiESxOuox+dWmuVV7vfYZ79Toh/LUK43YvJh+rhv4nKuF7iHjVjBd9s -B6iDjj70HFldzOQ9r8SRI+9NirupPTkF5AKNe6kUhKJ1luB7S27ZkvB3tSTT3P59 -3VVJvnzOjaA1z6Cz+4+eRvcysqhrRgFlwI9TEwIDAQABAoIBAEEYiyDP29vCzx/+ -dS3LqnI5BjUuJhXUnc6AWX/PCgVAO+8A+gZRgvct7PtZb0sM6P9ZcLrweomlGezI -FrL0/6xQaa8bBr/ve/a8155OgcjFo6fZEw3Dz7ra5fbSiPmu4/b/kvrg+Br1l77J -aun6uUAs1f5B9wW+vbR7tzbT/mxaUeDiBzKpe15GwcvbJtdIVMa2YErtRjc1/5B2 -BGVXyvlJv0SIlcIEMsHgnAFOp1ZgQ08aDzvilLq8XVMOahAhP1O2A3X8hKdXPyrx -IVWE9bS9ptTo+eF6eNl+d7htpKGEZHUxinoQpWEBTv+iOoHsVunkEJ3vjLP3lyI/ -fY0NQ1ECgYEA3RBXAjgvIys2gfU3keImF8e/TprLge1I2vbWmV2j6rZCg5r/AS0u -pii5CvJ5/T5vfJPNgPBy8B/yRDs+6PJO1GmnlhOkG9JAIPkv0RBZvR0PMBtbp6nT -Y3yo1lwamBVBfY6rc0sLTzosZh2aGoLzrHNMQFMGaauORzBFpY5lU50CgYEAzPHl -u5DI6Xgep1vr8QvCUuEesCOgJg8Yh1UqVoY/SmQh6MYAv1I9bLGwrb3WW/7kqIoD -fj0aQV5buVZI2loMomtU9KY5SFIsPV+JuUpy7/+VE01ZQM5FdY8wiYCQiVZYju9X -Wz5LxMNoz+gT7pwlLCsC4N+R8aoBk404aF1gum8CgYAJ7VTq7Zj4TFV7Soa/T1eE -k9y8a+kdoYk3BASpCHJ29M5R2KEA7YV9wrBklHTz8VzSTFTbKHEQ5W5csAhoL5Fo -qoHzFFi3Qx7MHESQb9qHyolHEMNx6QdsHUn7rlEnaTTyrXh3ifQtD6C0yTmFXUIS -CW9wKApOrnyKJ9nI0HcuZQKBgQCMtoV6e9VGX4AEfpuHvAAnMYQFgeBiYTkBKltQ -XwozhH63uMMomUmtSG87Sz1TmrXadjAhy8gsG6I0pWaN7QgBuFnzQ/HOkwTm+qKw -AsrZt4zeXNwsH7QXHEJCFnCmqw9QzEoZTrNtHJHpNboBuVnYcoueZEJrP8OnUG3r -UjmopwKBgAqB2KYYMUqAOvYcBnEfLDmyZv9BTVNHbR2lKkMYqv5LlvDaBxVfilE0 -2riO4p6BaAdvzXjKeRrGNEKoHNBpOSfYCOM16NjL8hIZB1CaV3WbT5oY+jp7Mzd5 -7d56RZOE+ERK2uz/7JX9VSsM/LbH9pJibd4e8mikDS9ntciqOH/3 ------END RSA TESTING KEY-----`, "TESTING KEY", "PRIVATE KEY"))) - if _, err := x509.ParsePKCS1PrivateKey(block.Bytes); err != nil { - t.Fatal(err) + kRSA, err := rsa.GenerateKey(rand.Reader, 2048) + if err != nil { + t.Error(err) + } else { + rsa.SignPKCS1v15(kRSA, crypto.SHA256.String(), make([]byte, 32)) } t.Log("completed successfully") } +func TestCASTPasses(t *testing.T) { + moduleStatus(t) + testenv.MustHaveExec(t) + if err := fips140.Supported(); err != nil { + t.Skipf("test requires FIPS 140 mode: %v", err) + } + + cmd := testenv.Command(t, testenv.Executable(t), "-test.run=^TestConditionals$", "-test.v") + cmd.Env = append(cmd.Env, "GODEBUG=fips140=debug") + out, err := cmd.CombinedOutput() + t.Logf("%s", out) + if err != nil || !strings.Contains(string(out), "completed successfully") { + t.Errorf("TestConditionals did not complete successfully") + } + + for _, name := range allCASTs { + t.Run(name, func(t *testing.T) { + if !strings.Contains(string(out), fmt.Sprintf("passed: %s\n", name)) { + t.Errorf("CAST/PCT %s success was not logged", name) + } else { + t.Logf("CAST/PCT succeeded: %s", name) + } + }) + } +} + func TestCASTFailures(t *testing.T) { moduleStatus(t) testenv.MustHaveExec(t) + if err := fips140.Supported(); err != nil { + t.Skipf("test requires FIPS 140 mode: %v", err) + } for _, name := range allCASTs { t.Run(name, func(t *testing.T) { @@ -169,7 +172,6 @@ func TestCASTFailures(t *testing.T) { if !testing.Verbose() { t.Parallel() } - t.Logf("CAST/PCT succeeded: %s", name) t.Logf("Testing CAST/PCT failure...") cmd := testenv.Command(t, testenv.Executable(t), "-test.run=^TestConditionals$", "-test.v") cmd.Env = append(cmd.Env, fmt.Sprintf("GODEBUG=failfipscast=%s,fips140=on", name)) @@ -180,6 +182,8 @@ func TestCASTFailures(t *testing.T) { } if strings.Contains(string(out), "completed successfully") { t.Errorf("CAST/PCT %s failure did not stop the program", name) + } else if !strings.Contains(string(out), "self-test failed: "+name) { + t.Errorf("CAST/PCT %s failure did not log the expected message", name) } else { t.Logf("CAST/PCT %s failed as expected and caused the program to exit", name) } diff --git a/src/crypto/internal/fips140test/fips_test.go b/src/crypto/internal/fips140test/fips_test.go index 08d60933..52fc9d34 100644 --- a/src/crypto/internal/fips140test/fips_test.go +++ b/src/crypto/internal/fips140test/fips_test.go @@ -74,11 +74,9 @@ func TestVersion(t *testing.T) { continue } exp := setting.Value - if exp == "v1.0.0" { - // Unfortunately we enshrined the version of the first module as - // v1.0 before deciding to go for full versions. - exp = "v1.0" - } + // Remove the -hash suffix, if any. + // The version from fips140.Version omits it. + exp, _, _ = strings.Cut(exp, "-") if v := fips140.Version(); v != exp { t.Errorf("Version is %q, expected %q", v, exp) } diff --git a/src/crypto/tls/handshake_server.go b/src/crypto/tls/handshake_server.go index 1e0b5f06..088c66fa 100644 --- a/src/crypto/tls/handshake_server.go +++ b/src/crypto/tls/handshake_server.go @@ -357,7 +357,7 @@ func negotiateALPN(serverProtos, clientProtos []string, quic bool) (string, erro if http11fallback { return "", nil } - return "", fmt.Errorf("tls: client requested unsupported application protocols (%s)", clientProtos) + return "", fmt.Errorf("tls: client requested unsupported application protocols (%q)", clientProtos) } // supportsECDHE returns whether ECDHE key exchanges can be used with this diff --git a/src/crypto/x509/name_constraints_test.go b/src/crypto/x509/name_constraints_test.go index a5851845..831fcbc8 100644 --- a/src/crypto/x509/name_constraints_test.go +++ b/src/crypto/x509/name_constraints_test.go @@ -1456,63 +1456,7 @@ var nameConstraintsTests = []nameConstraintsTest{ expectedError: "incompatible key usage", }, - // An invalid DNS SAN should be detected only at validation time so - // that we can process CA certificates in the wild that have invalid SANs. - // See https://github.com/golang/go/issues/23995 - - // #77: an invalid DNS or mail SAN will not be detected if name constraint - // checking is not triggered. - { - roots: make([]constraintsSpec, 1), - intermediates: [][]constraintsSpec{ - { - {}, - }, - }, - leaf: leafSpec{ - sans: []string{"dns:this is invalid", "email:this @ is invalid"}, - }, - }, - - // #78: an invalid DNS SAN will be detected if any name constraint checking - // is triggered. - { - roots: []constraintsSpec{ - { - bad: []string{"uri:"}, - }, - }, - intermediates: [][]constraintsSpec{ - { - {}, - }, - }, - leaf: leafSpec{ - sans: []string{"dns:this is invalid"}, - }, - expectedError: "cannot parse dnsName", - }, - - // #79: an invalid email SAN will be detected if any name constraint - // checking is triggered. - { - roots: []constraintsSpec{ - { - bad: []string{"uri:"}, - }, - }, - intermediates: [][]constraintsSpec{ - { - {}, - }, - }, - leaf: leafSpec{ - sans: []string{"email:this @ is invalid"}, - }, - expectedError: "cannot parse rfc822Name", - }, - - // #80: if several EKUs are requested, satisfying any of them is sufficient. + // #77: if several EKUs are requested, satisfying any of them is sufficient. { roots: make([]constraintsSpec, 1), intermediates: [][]constraintsSpec{ @@ -1527,7 +1471,7 @@ var nameConstraintsTests = []nameConstraintsTest{ requestedEKUs: []ExtKeyUsage{ExtKeyUsageClientAuth, ExtKeyUsageEmailProtection}, }, - // #81: EKUs that are not asserted in VerifyOpts are not required to be + // #78: EKUs that are not asserted in VerifyOpts are not required to be // nested. { roots: make([]constraintsSpec, 1), @@ -1546,7 +1490,7 @@ var nameConstraintsTests = []nameConstraintsTest{ }, }, - // #82: a certificate without SANs and CN is accepted in a constrained chain. + // #79: a certificate without SANs and CN is accepted in a constrained chain. { roots: []constraintsSpec{ { @@ -1563,7 +1507,7 @@ var nameConstraintsTests = []nameConstraintsTest{ }, }, - // #83: a certificate without SANs and with a CN that does not parse as a + // #80: a certificate without SANs and with a CN that does not parse as a // hostname is accepted in a constrained chain. { roots: []constraintsSpec{ @@ -1582,7 +1526,7 @@ var nameConstraintsTests = []nameConstraintsTest{ }, }, - // #84: a certificate with SANs and CN is accepted in a constrained chain. + // #81: a certificate with SANs and CN is accepted in a constrained chain. { roots: []constraintsSpec{ { @@ -1600,14 +1544,7 @@ var nameConstraintsTests = []nameConstraintsTest{ }, }, - // #85: .example.com is an invalid DNS name, it should not match the - // constraint example.com. - { - roots: []constraintsSpec{{ok: []string{"dns:example.com"}}}, - leaf: leafSpec{sans: []string{"dns:.example.com"}}, - expectedError: "cannot parse dnsName \".example.com\"", - }, - // #86: URIs with IPv6 addresses with zones and ports are rejected + // #82: URIs with IPv6 addresses with zones and ports are rejected { roots: []constraintsSpec{ { diff --git a/src/crypto/x509/parser.go b/src/crypto/x509/parser.go index 4abcc1b7..9d6bfd6e 100644 --- a/src/crypto/x509/parser.go +++ b/src/crypto/x509/parser.go @@ -413,10 +413,14 @@ func parseSANExtension(der cryptobyte.String) (dnsNames, emailAddresses []string if err := isIA5String(email); err != nil { return errors.New("x509: SAN rfc822Name is malformed") } + parsed, ok := parseRFC2821Mailbox(email) + if !ok || (ok && !domainNameValid(parsed.domain, false)) { + return errors.New("x509: SAN rfc822Name is malformed") + } emailAddresses = append(emailAddresses, email) case nameTypeDNS: name := string(data) - if err := isIA5String(name); err != nil { + if err := isIA5String(name); err != nil || (err == nil && !domainNameValid(name, false)) { return errors.New("x509: SAN dNSName is malformed") } dnsNames = append(dnsNames, string(name)) @@ -426,14 +430,9 @@ func parseSANExtension(der cryptobyte.String) (dnsNames, emailAddresses []string return errors.New("x509: SAN uniformResourceIdentifier is malformed") } uri, err := url.Parse(uriStr) - if err != nil { + if err != nil || (err == nil && uri.Host != "" && !domainNameValid(uri.Host, false)) { return fmt.Errorf("x509: cannot parse URI %q: %s", uriStr, err) } - if len(uri.Host) > 0 { - if _, ok := domainToReverseLabels(uri.Host); !ok { - return fmt.Errorf("x509: cannot parse URI %q: invalid domain", uriStr) - } - } uris = append(uris, uri) case nameTypeIP: switch len(data) { @@ -598,15 +597,7 @@ func parseNameConstraintsExtension(out *Certificate, e pkix.Extension) (unhandle return nil, nil, nil, nil, errors.New("x509: invalid constraint value: " + err.Error()) } - trimmedDomain := domain - if len(trimmedDomain) > 0 && trimmedDomain[0] == '.' { - // constraints can have a leading - // period to exclude the domain - // itself, but that's not valid in a - // normal domain name. - trimmedDomain = trimmedDomain[1:] - } - if _, ok := domainToReverseLabels(trimmedDomain); !ok { + if !domainNameValid(domain, true) { return nil, nil, nil, nil, fmt.Errorf("x509: failed to parse dnsName constraint %q", domain) } dnsNames = append(dnsNames, domain) @@ -647,12 +638,7 @@ func parseNameConstraintsExtension(out *Certificate, e pkix.Extension) (unhandle return nil, nil, nil, nil, fmt.Errorf("x509: failed to parse rfc822Name constraint %q", constraint) } } else { - // Otherwise it's a domain name. - domain := constraint - if len(domain) > 0 && domain[0] == '.' { - domain = domain[1:] - } - if _, ok := domainToReverseLabels(domain); !ok { + if !domainNameValid(constraint, true) { return nil, nil, nil, nil, fmt.Errorf("x509: failed to parse rfc822Name constraint %q", constraint) } } @@ -668,15 +654,7 @@ func parseNameConstraintsExtension(out *Certificate, e pkix.Extension) (unhandle return nil, nil, nil, nil, fmt.Errorf("x509: failed to parse URI constraint %q: cannot be IP address", domain) } - trimmedDomain := domain - if len(trimmedDomain) > 0 && trimmedDomain[0] == '.' { - // constraints can have a leading - // period to exclude the domain itself, - // but that's not valid in a normal - // domain name. - trimmedDomain = trimmedDomain[1:] - } - if _, ok := domainToReverseLabels(trimmedDomain); !ok { + if !domainNameValid(domain, true) { return nil, nil, nil, nil, fmt.Errorf("x509: failed to parse URI constraint %q", domain) } uriDomains = append(uriDomains, domain) @@ -1317,3 +1295,40 @@ func ParseRevocationList(der []byte) (*RevocationList, error) { return rl, nil } + +// domainNameValid does minimal domain name validity checking. In particular it +// enforces the following properties: +// - names cannot have the trailing period +// - names can only have a leading period if constraint is true +// - names must be <= 253 characters +// - names cannot have empty labels +// - names cannot labels that are longer than 63 characters +// +// Note that this does not enforce the LDH requirements for domain names. +func domainNameValid(s string, constraint bool) bool { + if len(s) == 0 && constraint { + return true + } + if len(s) == 0 || (!constraint && s[0] == '.') || s[len(s)-1] == '.' || len(s) > 253 { + return false + } + lastDot := -1 + if constraint && s[0] == '.' { + s = s[1:] + } + + for i := 0; i <= len(s); i++ { + if i == len(s) || s[i] == '.' { + labelLen := i + if lastDot >= 0 { + labelLen -= lastDot + 1 + } + if labelLen == 0 || labelLen > 63 { + return false + } + lastDot = i + } + } + + return true +} diff --git a/src/crypto/x509/parser_test.go b/src/crypto/x509/parser_test.go index 3b9d9aed..1b553e36 100644 --- a/src/crypto/x509/parser_test.go +++ b/src/crypto/x509/parser_test.go @@ -8,6 +8,7 @@ import ( "encoding/asn1" "encoding/pem" "os" + "strings" "testing" cryptobyte_asn1 "golang.org/x/crypto/cryptobyte/asn1" @@ -251,3 +252,45 @@ d5l1tRhScKu2NBgm74nYmJxJYgvuTA38wGhRrGU= } } } + +func TestDomainNameValid(t *testing.T) { + for _, tc := range []struct { + name string + dnsName string + constraint bool + valid bool + }{ + {"empty name, name", "", false, false}, + {"empty name, constraint", "", true, true}, + {"empty label, name", "a..a", false, false}, + {"empty label, constraint", "a..a", true, false}, + {"period, name", ".", false, false}, + {"period, constraint", ".", true, false}, // TODO(roland): not entirely clear if this is a valid constraint (require at least one label?) + {"valid, name", "a.b.c", false, true}, + {"valid, constraint", "a.b.c", true, true}, + {"leading period, name", ".a.b.c", false, false}, + {"leading period, constraint", ".a.b.c", true, true}, + {"trailing period, name", "a.", false, false}, + {"trailing period, constraint", "a.", true, false}, + {"bare label, name", "a", false, true}, + {"bare label, constraint", "a", true, true}, + {"254 char label, name", strings.Repeat("a.a", 84) + "aaa", false, false}, + {"254 char label, constraint", strings.Repeat("a.a", 84) + "aaa", true, false}, + {"253 char label, name", strings.Repeat("a.a", 84) + "aa", false, false}, + {"253 char label, constraint", strings.Repeat("a.a", 84) + "aa", true, false}, + {"64 char single label, name", strings.Repeat("a", 64), false, false}, + {"64 char single label, constraint", strings.Repeat("a", 64), true, false}, + {"63 char single label, name", strings.Repeat("a", 63), false, true}, + {"63 char single label, constraint", strings.Repeat("a", 63), true, true}, + {"64 char label, name", "a." + strings.Repeat("a", 64), false, false}, + {"64 char label, constraint", "a." + strings.Repeat("a", 64), true, false}, + {"63 char label, name", "a." + strings.Repeat("a", 63), false, true}, + {"63 char label, constraint", "a." + strings.Repeat("a", 63), true, true}, + } { + t.Run(tc.name, func(t *testing.T) { + if tc.valid != domainNameValid(tc.dnsName, tc.constraint) { + t.Errorf("domainNameValid(%q, %t) = %v; want %v", tc.dnsName, tc.constraint, !tc.valid, tc.valid) + } + }) + } +} diff --git a/src/crypto/x509/verify.go b/src/crypto/x509/verify.go index 7cc0fb2e..058153fb 100644 --- a/src/crypto/x509/verify.go +++ b/src/crypto/x509/verify.go @@ -391,6 +391,7 @@ func parseRFC2821Mailbox(in string) (mailbox rfc2821Mailbox, ok bool) { // domainToReverseLabels converts a textual domain name like foo.example.com to // the list of labels in reverse order, e.g. ["com", "example", "foo"]. func domainToReverseLabels(domain string) (reverseLabels []string, ok bool) { + reverseLabels = make([]string, 0, strings.Count(domain, ".")+1) for len(domain) > 0 { if i := strings.LastIndexByte(domain, '.'); i == -1 { reverseLabels = append(reverseLabels, domain) @@ -927,7 +928,10 @@ func alreadyInChain(candidate *Certificate, chain []*Certificate) bool { if !bytes.Equal(candidate.RawSubject, cert.RawSubject) { continue } - if !candidate.PublicKey.(pubKeyEqual).Equal(cert.PublicKey) { + // We enforce the canonical encoding of SPKI (by only allowing the + // correct AI paremeter encodings in parseCertificate), so it's safe to + // directly compare the raw bytes. + if !bytes.Equal(candidate.RawSubjectPublicKeyInfo, cert.RawSubjectPublicKeyInfo) { continue } var certSAN *pkix.Extension diff --git a/src/crypto/x509/verify_test.go b/src/crypto/x509/verify_test.go index 7991f499..5595f99e 100644 --- a/src/crypto/x509/verify_test.go +++ b/src/crypto/x509/verify_test.go @@ -6,6 +6,7 @@ package x509 import ( "crypto" + "crypto/dsa" "crypto/ecdsa" "crypto/elliptic" "crypto/rand" @@ -3048,3 +3049,129 @@ func TestInvalidPolicyWithAnyKeyUsage(t *testing.T) { t.Fatalf("unexpected error, got %q, want %q", err, expectedErr) } } + +func TestCertificateChainSignedByECDSA(t *testing.T) { + caKey, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader) + if err != nil { + t.Fatal(err) + } + root := &Certificate{ + SerialNumber: big.NewInt(1), + Subject: pkix.Name{CommonName: "X"}, + NotBefore: time.Now().Add(-time.Hour), + NotAfter: time.Now().Add(365 * 24 * time.Hour), + IsCA: true, + KeyUsage: KeyUsageCertSign | KeyUsageCRLSign, + BasicConstraintsValid: true, + } + caDER, err := CreateCertificate(rand.Reader, root, root, &caKey.PublicKey, caKey) + if err != nil { + t.Fatal(err) + } + root, err = ParseCertificate(caDER) + if err != nil { + t.Fatal(err) + } + + leafKey, _ := ecdsa.GenerateKey(elliptic.P256(), rand.Reader) + leaf := &Certificate{ + SerialNumber: big.NewInt(42), + Subject: pkix.Name{CommonName: "leaf"}, + NotBefore: time.Now().Add(-10 * time.Minute), + NotAfter: time.Now().Add(24 * time.Hour), + KeyUsage: KeyUsageDigitalSignature, + ExtKeyUsage: []ExtKeyUsage{ExtKeyUsageServerAuth}, + BasicConstraintsValid: true, + } + leafDER, err := CreateCertificate(rand.Reader, leaf, root, &leafKey.PublicKey, caKey) + if err != nil { + t.Fatal(err) + } + leaf, err = ParseCertificate(leafDER) + if err != nil { + t.Fatal(err) + } + + inter, err := ParseCertificate(dsaSelfSignedCNX(t)) + if err != nil { + t.Fatal(err) + } + + inters := NewCertPool() + inters.AddCert(root) + inters.AddCert(inter) + + wantErr := "certificate signed by unknown authority" + _, err = leaf.Verify(VerifyOptions{Intermediates: inters, Roots: NewCertPool()}) + if !strings.Contains(err.Error(), wantErr) { + t.Errorf("got %v, want %q", err, wantErr) + } +} + +// dsaSelfSignedCNX produces DER-encoded +// certificate with the properties: +// +// Subject=Issuer=CN=X +// DSA SPKI +// Matching inner/outer signature OIDs +// Dummy ECDSA signature +func dsaSelfSignedCNX(t *testing.T) []byte { + t.Helper() + var params dsa.Parameters + if err := dsa.GenerateParameters(¶ms, rand.Reader, dsa.L1024N160); err != nil { + t.Fatal(err) + } + + var dsaPriv dsa.PrivateKey + dsaPriv.Parameters = params + if err := dsa.GenerateKey(&dsaPriv, rand.Reader); err != nil { + t.Fatal(err) + } + dsaPub := &dsaPriv.PublicKey + + type dsaParams struct{ P, Q, G *big.Int } + paramDER, err := asn1.Marshal(dsaParams{dsaPub.P, dsaPub.Q, dsaPub.G}) + if err != nil { + t.Fatal(err) + } + yDER, err := asn1.Marshal(dsaPub.Y) + if err != nil { + t.Fatal(err) + } + + spki := publicKeyInfo{ + Algorithm: pkix.AlgorithmIdentifier{ + Algorithm: oidPublicKeyDSA, + Parameters: asn1.RawValue{FullBytes: paramDER}, + }, + PublicKey: asn1.BitString{Bytes: yDER, BitLength: 8 * len(yDER)}, + } + + rdn := pkix.Name{CommonName: "X"}.ToRDNSequence() + b, err := asn1.Marshal(rdn) + if err != nil { + t.Fatal(err) + } + rawName := asn1.RawValue{FullBytes: b} + + algoIdent := pkix.AlgorithmIdentifier{Algorithm: oidSignatureDSAWithSHA256} + tbs := tbsCertificate{ + Version: 0, + SerialNumber: big.NewInt(1002), + SignatureAlgorithm: algoIdent, + Issuer: rawName, + Validity: validity{NotBefore: time.Now().Add(-time.Hour), NotAfter: time.Now().Add(24 * time.Hour)}, + Subject: rawName, + PublicKey: spki, + } + c := certificate{ + TBSCertificate: tbs, + SignatureAlgorithm: algoIdent, + SignatureValue: asn1.BitString{Bytes: []byte{0}, BitLength: 8}, + } + dsaDER, err := asn1.Marshal(c) + if err != nil { + t.Fatal(err) + } + return dsaDER +} diff --git a/src/debug/pe/symbol.go b/src/debug/pe/symbol.go index 6e8d9d16..80acebe9 100644 --- a/src/debug/pe/symbol.go +++ b/src/debug/pe/symbol.go @@ -98,7 +98,12 @@ func readCOFFSymbols(fh *FileHeader, r io.ReadSeeker) ([]COFFSymbol, error) { // isSymNameOffset checks symbol name if it is encoded as offset into string table. func isSymNameOffset(name [8]byte) (bool, uint32) { if name[0] == 0 && name[1] == 0 && name[2] == 0 && name[3] == 0 { - return true, binary.LittleEndian.Uint32(name[4:]) + offset := binary.LittleEndian.Uint32(name[4:]) + if offset == 0 { + // symbol has no name + return false, 0 + } + return true, offset } return false, 0 } diff --git a/src/encoding/asn1/asn1.go b/src/encoding/asn1/asn1.go index 0b64f06d..f4be515b 100644 --- a/src/encoding/asn1/asn1.go +++ b/src/encoding/asn1/asn1.go @@ -22,6 +22,7 @@ package asn1 import ( "errors" "fmt" + "internal/saferio" "math" "math/big" "reflect" @@ -666,10 +667,17 @@ func parseSequenceOf(bytes []byte, sliceType reflect.Type, elemType reflect.Type offset += t.length numElements++ } - ret = reflect.MakeSlice(sliceType, numElements, numElements) + elemSize := uint64(elemType.Size()) + safeCap := saferio.SliceCapWithSize(elemSize, uint64(numElements)) + if safeCap < 0 { + err = SyntaxError{fmt.Sprintf("%s slice too big: %d elements of %d bytes", elemType.Kind(), numElements, elemSize)} + return + } + ret = reflect.MakeSlice(sliceType, 0, safeCap) params := fieldParameters{} offset := 0 for i := 0; i < numElements; i++ { + ret = reflect.Append(ret, reflect.Zero(elemType)) offset, err = parseField(ret.Index(i), bytes, offset, params) if err != nil { return diff --git a/src/encoding/asn1/asn1_test.go b/src/encoding/asn1/asn1_test.go index 0597740b..41cc0ba5 100644 --- a/src/encoding/asn1/asn1_test.go +++ b/src/encoding/asn1/asn1_test.go @@ -7,10 +7,12 @@ package asn1 import ( "bytes" "encoding/hex" + "errors" "fmt" "math" "math/big" "reflect" + "runtime" "strings" "testing" "time" @@ -1216,3 +1218,39 @@ func TestImplicitTypeRoundtrip(t *testing.T) { t.Fatalf("Unexpected diff after roundtripping struct\na: %#v\nb: %#v", a, b) } } + +func TestParsingMemoryConsumption(t *testing.T) { + // Craft a syntatically valid, but empty, ~10 MB DER bomb. A successful + // unmarshal of this bomb should yield ~280 MB. However, the parsing should + // fail due to the empty content; and, in such cases, we want to make sure + // that we do not unnecessarily allocate memories. + derBomb := make([]byte, 10_000_000) + for i := range derBomb { + derBomb[i] = 0x30 + } + derBomb = append([]byte{0x30, 0x83, 0x98, 0x96, 0x80}, derBomb...) + + var m runtime.MemStats + runtime.GC() + runtime.ReadMemStats(&m) + memBefore := m.TotalAlloc + + var out []struct { + Id []int + Critical bool `asn1:"optional"` + Value []byte + } + _, err := Unmarshal(derBomb, &out) + if !errors.As(err, &SyntaxError{}) { + t.Fatalf("Incorrect error result: want (%v), but got (%v) instead", &SyntaxError{}, err) + } + + runtime.ReadMemStats(&m) + memDiff := m.TotalAlloc - memBefore + + // Ensure that the memory allocated does not exceed 10<<21 (~20 MB) when + // the parsing fails. + if memDiff > 10<<21 { + t.Errorf("Too much memory allocated while parsing DER: %v MiB", memDiff/1024/1024) + } +} diff --git a/src/encoding/pem/pem.go b/src/encoding/pem/pem.go index dcc7416e..21887008 100644 --- a/src/encoding/pem/pem.go +++ b/src/encoding/pem/pem.go @@ -37,7 +37,7 @@ type Block struct { // line bytes. The remainder of the byte array (also not including the new line // bytes) is also returned and this will always be smaller than the original // argument. -func getLine(data []byte) (line, rest []byte) { +func getLine(data []byte) (line, rest []byte, consumed int) { i := bytes.IndexByte(data, '\n') var j int if i < 0 { @@ -49,7 +49,7 @@ func getLine(data []byte) (line, rest []byte) { i-- } } - return bytes.TrimRight(data[0:i], " \t"), data[j:] + return bytes.TrimRight(data[0:i], " \t"), data[j:], j } // removeSpacesAndTabs returns a copy of its input with all spaces and tabs @@ -90,20 +90,32 @@ func Decode(data []byte) (p *Block, rest []byte) { // pemStart begins with a newline. However, at the very beginning of // the byte array, we'll accept the start string without it. rest = data + for { - if bytes.HasPrefix(rest, pemStart[1:]) { - rest = rest[len(pemStart)-1:] - } else if _, after, ok := bytes.Cut(rest, pemStart); ok { - rest = after - } else { + // Find the first END line, and then find the last BEGIN line before + // the end line. This lets us skip any repeated BEGIN lines that don't + // have a matching END. + endIndex := bytes.Index(rest, pemEnd) + if endIndex < 0 { return nil, data } + endTrailerIndex := endIndex + len(pemEnd) + beginIndex := bytes.LastIndex(rest[:endIndex], pemStart[1:]) + if beginIndex < 0 || beginIndex > 0 && rest[beginIndex-1] != '\n' { + return nil, data + } + rest = rest[beginIndex+len(pemStart)-1:] + endIndex -= beginIndex + len(pemStart) - 1 + endTrailerIndex -= beginIndex + len(pemStart) - 1 var typeLine []byte - typeLine, rest = getLine(rest) + var consumed int + typeLine, rest, consumed = getLine(rest) if !bytes.HasSuffix(typeLine, pemEndOfLine) { continue } + endIndex -= consumed + endTrailerIndex -= consumed typeLine = typeLine[0 : len(typeLine)-len(pemEndOfLine)] p = &Block{ @@ -117,7 +129,7 @@ func Decode(data []byte) (p *Block, rest []byte) { if len(rest) == 0 { return nil, data } - line, next := getLine(rest) + line, next, consumed := getLine(rest) key, val, ok := bytes.Cut(line, colon) if !ok { @@ -129,21 +141,13 @@ func Decode(data []byte) (p *Block, rest []byte) { val = bytes.TrimSpace(val) p.Headers[string(key)] = string(val) rest = next + endIndex -= consumed + endTrailerIndex -= consumed } - var endIndex, endTrailerIndex int - - // If there were no headers, the END line might occur - // immediately, without a leading newline. - if len(p.Headers) == 0 && bytes.HasPrefix(rest, pemEnd[1:]) { - endIndex = 0 - endTrailerIndex = len(pemEnd) - 1 - } else { - endIndex = bytes.Index(rest, pemEnd) - endTrailerIndex = endIndex + len(pemEnd) - } - - if endIndex < 0 { + // If there were headers, there must be a newline between the headers + // and the END line, so endIndex should be >= 0. + if len(p.Headers) > 0 && endIndex < 0 { continue } @@ -163,21 +167,24 @@ func Decode(data []byte) (p *Block, rest []byte) { } // The line must end with only whitespace. - if s, _ := getLine(restOfEndLine); len(s) != 0 { + if s, _, _ := getLine(restOfEndLine); len(s) != 0 { continue } - base64Data := removeSpacesAndTabs(rest[:endIndex]) - p.Bytes = make([]byte, base64.StdEncoding.DecodedLen(len(base64Data))) - n, err := base64.StdEncoding.Decode(p.Bytes, base64Data) - if err != nil { - continue + p.Bytes = []byte{} + if endIndex > 0 { + base64Data := removeSpacesAndTabs(rest[:endIndex]) + p.Bytes = make([]byte, base64.StdEncoding.DecodedLen(len(base64Data))) + n, err := base64.StdEncoding.Decode(p.Bytes, base64Data) + if err != nil { + continue + } + p.Bytes = p.Bytes[:n] } - p.Bytes = p.Bytes[:n] // the -1 is because we might have only matched pemEnd without the // leading newline if the PEM block was empty. - _, rest = getLine(rest[endIndex+len(pemEnd)-1:]) + _, rest, _ = getLine(rest[endIndex+len(pemEnd)-1:]) return p, rest } } diff --git a/src/encoding/pem/pem_test.go b/src/encoding/pem/pem_test.go index e252ffd8..2c9b3eab 100644 --- a/src/encoding/pem/pem_test.go +++ b/src/encoding/pem/pem_test.go @@ -34,7 +34,7 @@ var getLineTests = []GetLineTest{ func TestGetLine(t *testing.T) { for i, test := range getLineTests { - x, y := getLine([]byte(test.in)) + x, y, _ := getLine([]byte(test.in)) if string(x) != test.out1 || string(y) != test.out2 { t.Errorf("#%d got:%+v,%+v want:%s,%s", i, x, y, test.out1, test.out2) } @@ -46,6 +46,7 @@ func TestDecode(t *testing.T) { if !reflect.DeepEqual(result, certificate) { t.Errorf("#0 got:%#v want:%#v", result, certificate) } + result, remainder = Decode(remainder) if !reflect.DeepEqual(result, privateKey) { t.Errorf("#1 got:%#v want:%#v", result, privateKey) @@ -68,7 +69,7 @@ func TestDecode(t *testing.T) { } result, remainder = Decode(remainder) - if result == nil || result.Type != "HEADERS" || len(result.Headers) != 1 { + if result == nil || result.Type != "VALID HEADERS" || len(result.Headers) != 1 { t.Errorf("#5 expected single header block but got :%v", result) } @@ -381,15 +382,15 @@ ZWAaUoVtWIQ52aKS0p19G99hhb+IVANC4akkdHV4SP8i7MVNZhfUmg== # This shouldn't be recognised because of the missing newline after the headers. ------BEGIN HEADERS----- +-----BEGIN INVALID HEADERS----- Header: 1 ------END HEADERS----- +-----END INVALID HEADERS----- # This should be valid, however. ------BEGIN HEADERS----- +-----BEGIN VALID HEADERS----- Header: 1 ------END HEADERS-----`) +-----END VALID HEADERS-----`) var certificate = &Block{Type: "CERTIFICATE", Headers: map[string]string{}, diff --git a/src/go/build/deps_test.go b/src/go/build/deps_test.go index 6d92542e..641d1a32 100644 --- a/src/go/build/deps_test.go +++ b/src/go/build/deps_test.go @@ -235,7 +235,6 @@ var depsRules = ` internal/types/errors, mime/quotedprintable, net/internal/socktest, - net/url, runtime/trace, text/scanner, text/tabwriter; @@ -298,6 +297,12 @@ var depsRules = ` FMT < text/template/parse; + internal/bytealg, internal/itoa, math/bits, slices, strconv, unique + < net/netip; + + FMT, net/netip + < net/url; + net/url, text/template/parse < text/template < internal/lazytemplate; @@ -412,9 +417,6 @@ var depsRules = ` < golang.org/x/net/dns/dnsmessage, golang.org/x/net/lif; - internal/bytealg, internal/itoa, math/bits, slices, strconv, unique - < net/netip; - os, net/netip < internal/routebsd; @@ -557,7 +559,7 @@ var depsRules = ` # CRYPTO-MATH is crypto that exposes math/big APIs - no cgo, net; fmt now ok. - CRYPTO, FMT, math/big + CRYPTO, FMT, math/big, internal/saferio < crypto/internal/boring/bbig < crypto/internal/fips140cache < crypto/rand diff --git a/src/internal/buildcfg/cfg.go b/src/internal/buildcfg/cfg.go index 5ae4c0c7..50b122fd 100644 --- a/src/internal/buildcfg/cfg.go +++ b/src/internal/buildcfg/cfg.go @@ -85,7 +85,7 @@ func gofips140() string { } // isFIPSVersion reports whether v is a valid FIPS version, -// of the form vX.Y.Z. +// of the form vX.Y.Z or vX.Y.Z-hash. func isFIPSVersion(v string) bool { if !strings.HasPrefix(v, "v") { return false @@ -99,7 +99,8 @@ func isFIPSVersion(v string) bool { return false } v, ok = skipNum(v[len("."):]) - return ok && v == "" + hasHash := strings.HasPrefix(v, "-") && len(v) == len("-")+8 + return ok && (v == "" || hasHash) } // skipNum skips the leading text matching [0-9]+ diff --git a/src/internal/godebugs/table.go b/src/internal/godebugs/table.go index 2d008825..852305e8 100644 --- a/src/internal/godebugs/table.go +++ b/src/internal/godebugs/table.go @@ -42,6 +42,7 @@ var All = []Info{ {Name: "http2client", Package: "net/http"}, {Name: "http2debug", Package: "net/http", Opaque: true}, {Name: "http2server", Package: "net/http"}, + {Name: "httpcookiemaxnum", Package: "net/http", Changed: 24, Old: "0"}, {Name: "httplaxcontentlength", Package: "net/http", Changed: 22, Old: "1"}, {Name: "httpmuxgo121", Package: "net/http", Changed: 22, Old: "1"}, {Name: "httpservecontentkeepheaders", Package: "net/http", Changed: 23, Old: "1"}, diff --git a/src/internal/poll/fd_windows.go b/src/internal/poll/fd_windows.go index 74188c05..dd833afd 100644 --- a/src/internal/poll/fd_windows.go +++ b/src/internal/poll/fd_windows.go @@ -636,12 +636,22 @@ func (fd *FD) Pread(b []byte, off int64) (int, error) { fd.l.Lock() defer fd.l.Unlock() - curoffset, err := syscall.Seek(fd.Sysfd, 0, io.SeekCurrent) - if err != nil { - return 0, err + if fd.isBlocking { + curoffset, err := syscall.Seek(fd.Sysfd, 0, io.SeekCurrent) + if err != nil { + return 0, err + } + defer syscall.Seek(fd.Sysfd, curoffset, io.SeekStart) + defer fd.setOffset(curoffset) + } else { + // Overlapped handles don't have the file pointer updated + // when performing I/O operations, so there is no need to + // call Seek to reset the file pointer. + // Also, some overlapped file handles don't support seeking. + // See https://go.dev/issues/74951. + curoffset := fd.offset + defer fd.setOffset(curoffset) } - defer syscall.Seek(fd.Sysfd, curoffset, io.SeekStart) - defer fd.setOffset(curoffset) o := &fd.rop o.InitBuf(b) fd.setOffset(off) @@ -852,12 +862,22 @@ func (fd *FD) Pwrite(buf []byte, off int64) (int, error) { fd.l.Lock() defer fd.l.Unlock() - curoffset, err := syscall.Seek(fd.Sysfd, 0, io.SeekCurrent) - if err != nil { - return 0, err + if fd.isBlocking { + curoffset, err := syscall.Seek(fd.Sysfd, 0, io.SeekCurrent) + if err != nil { + return 0, err + } + defer syscall.Seek(fd.Sysfd, curoffset, io.SeekStart) + defer fd.setOffset(curoffset) + } else { + // Overlapped handles don't have the file pointer updated + // when performing I/O operations, so there is no need to + // call Seek to reset the file pointer. + // Also, some overlapped file handles don't support seeking. + // See https://go.dev/issues/74951. + curoffset := fd.offset + defer fd.setOffset(curoffset) } - defer syscall.Seek(fd.Sysfd, curoffset, io.SeekStart) - defer fd.setOffset(curoffset) var ntotal int for { diff --git a/src/internal/synctest/synctest_test.go b/src/internal/synctest/synctest_test.go index 307eee62..73a0a1c4 100644 --- a/src/internal/synctest/synctest_test.go +++ b/src/internal/synctest/synctest_test.go @@ -779,6 +779,28 @@ func TestWaitGroupHeapAllocated(t *testing.T) { }) } +// Issue #75134: Many racing bubble associations. +func TestWaitGroupManyBubbles(t *testing.T) { + var wg sync.WaitGroup + for range 100 { + wg.Go(func() { + synctest.Run(func() { + cancelc := make(chan struct{}) + var wg2 sync.WaitGroup + for range 100 { + wg2.Go(func() { + <-cancelc + }) + } + synctest.Wait() + close(cancelc) + wg2.Wait() + }) + }) + } + wg.Wait() +} + func TestHappensBefore(t *testing.T) { // Use two parallel goroutines accessing different vars to ensure that // we correctly account for multiple goroutines in the bubble. diff --git a/src/net/http/cookie.go b/src/net/http/cookie.go index 408fe884..d3c5c168 100644 --- a/src/net/http/cookie.go +++ b/src/net/http/cookie.go @@ -7,6 +7,7 @@ package http import ( "errors" "fmt" + "internal/godebug" "log" "net" "net/http/internal/ascii" @@ -16,6 +17,8 @@ import ( "time" ) +var httpcookiemaxnum = godebug.New("httpcookiemaxnum") + // A Cookie represents an HTTP cookie as sent in the Set-Cookie header of an // HTTP response or the Cookie header of an HTTP request. // @@ -58,16 +61,37 @@ const ( ) var ( - errBlankCookie = errors.New("http: blank cookie") - errEqualNotFoundInCookie = errors.New("http: '=' not found in cookie") - errInvalidCookieName = errors.New("http: invalid cookie name") - errInvalidCookieValue = errors.New("http: invalid cookie value") + errBlankCookie = errors.New("http: blank cookie") + errEqualNotFoundInCookie = errors.New("http: '=' not found in cookie") + errInvalidCookieName = errors.New("http: invalid cookie name") + errInvalidCookieValue = errors.New("http: invalid cookie value") + errCookieNumLimitExceeded = errors.New("http: number of cookies exceeded limit") ) +const defaultCookieMaxNum = 3000 + +func cookieNumWithinMax(cookieNum int) bool { + withinDefaultMax := cookieNum <= defaultCookieMaxNum + if httpcookiemaxnum.Value() == "" { + return withinDefaultMax + } + if customMax, err := strconv.Atoi(httpcookiemaxnum.Value()); err == nil { + withinCustomMax := customMax == 0 || cookieNum <= customMax + if withinDefaultMax != withinCustomMax { + httpcookiemaxnum.IncNonDefault() + } + return withinCustomMax + } + return withinDefaultMax +} + // ParseCookie parses a Cookie header value and returns all the cookies // which were set in it. Since the same cookie name can appear multiple times // the returned Values can contain more than one value for a given key. func ParseCookie(line string) ([]*Cookie, error) { + if !cookieNumWithinMax(strings.Count(line, ";") + 1) { + return nil, errCookieNumLimitExceeded + } parts := strings.Split(textproto.TrimString(line), ";") if len(parts) == 1 && parts[0] == "" { return nil, errBlankCookie @@ -197,11 +221,21 @@ func ParseSetCookie(line string) (*Cookie, error) { // readSetCookies parses all "Set-Cookie" values from // the header h and returns the successfully parsed Cookies. +// +// If the amount of cookies exceeds CookieNumLimit, and httpcookielimitnum +// GODEBUG option is not explicitly turned off, this function will silently +// fail and return an empty slice. func readSetCookies(h Header) []*Cookie { cookieCount := len(h["Set-Cookie"]) if cookieCount == 0 { return []*Cookie{} } + // Cookie limit was unfortunately introduced at a later point in time. + // As such, we can only fail by returning an empty slice rather than + // explicit error. + if !cookieNumWithinMax(cookieCount) { + return []*Cookie{} + } cookies := make([]*Cookie, 0, cookieCount) for _, line := range h["Set-Cookie"] { if cookie, err := ParseSetCookie(line); err == nil { @@ -329,13 +363,28 @@ func (c *Cookie) Valid() error { // readCookies parses all "Cookie" values from the header h and // returns the successfully parsed Cookies. // -// if filter isn't empty, only cookies of that name are returned. +// If filter isn't empty, only cookies of that name are returned. +// +// If the amount of cookies exceeds CookieNumLimit, and httpcookielimitnum +// GODEBUG option is not explicitly turned off, this function will silently +// fail and return an empty slice. func readCookies(h Header, filter string) []*Cookie { lines := h["Cookie"] if len(lines) == 0 { return []*Cookie{} } + // Cookie limit was unfortunately introduced at a later point in time. + // As such, we can only fail by returning an empty slice rather than + // explicit error. + cookieCount := 0 + for _, line := range lines { + cookieCount += strings.Count(line, ";") + 1 + } + if !cookieNumWithinMax(cookieCount) { + return []*Cookie{} + } + cookies := make([]*Cookie, 0, len(lines)+strings.Count(lines[0], ";")) for _, line := range lines { line = textproto.TrimString(line) diff --git a/src/net/http/cookie_test.go b/src/net/http/cookie_test.go index aac69563..d028725f 100644 --- a/src/net/http/cookie_test.go +++ b/src/net/http/cookie_test.go @@ -11,6 +11,7 @@ import ( "log" "os" "reflect" + "slices" "strings" "testing" "time" @@ -255,16 +256,17 @@ func TestAddCookie(t *testing.T) { } var readSetCookiesTests = []struct { - Header Header - Cookies []*Cookie + header Header + cookies []*Cookie + godebug string }{ { - Header{"Set-Cookie": {"Cookie-1=v$1"}}, - []*Cookie{{Name: "Cookie-1", Value: "v$1", Raw: "Cookie-1=v$1"}}, + header: Header{"Set-Cookie": {"Cookie-1=v$1"}}, + cookies: []*Cookie{{Name: "Cookie-1", Value: "v$1", Raw: "Cookie-1=v$1"}}, }, { - Header{"Set-Cookie": {"NID=99=YsDT5i3E-CXax-; expires=Wed, 23-Nov-2011 01:05:03 GMT; path=/; domain=.google.ch; HttpOnly"}}, - []*Cookie{{ + header: Header{"Set-Cookie": {"NID=99=YsDT5i3E-CXax-; expires=Wed, 23-Nov-2011 01:05:03 GMT; path=/; domain=.google.ch; HttpOnly"}}, + cookies: []*Cookie{{ Name: "NID", Value: "99=YsDT5i3E-CXax-", Path: "/", @@ -276,8 +278,8 @@ var readSetCookiesTests = []struct { }}, }, { - Header{"Set-Cookie": {".ASPXAUTH=7E3AA; expires=Wed, 07-Mar-2012 14:25:06 GMT; path=/; HttpOnly"}}, - []*Cookie{{ + header: Header{"Set-Cookie": {".ASPXAUTH=7E3AA; expires=Wed, 07-Mar-2012 14:25:06 GMT; path=/; HttpOnly"}}, + cookies: []*Cookie{{ Name: ".ASPXAUTH", Value: "7E3AA", Path: "/", @@ -288,8 +290,8 @@ var readSetCookiesTests = []struct { }}, }, { - Header{"Set-Cookie": {"ASP.NET_SessionId=foo; path=/; HttpOnly"}}, - []*Cookie{{ + header: Header{"Set-Cookie": {"ASP.NET_SessionId=foo; path=/; HttpOnly"}}, + cookies: []*Cookie{{ Name: "ASP.NET_SessionId", Value: "foo", Path: "/", @@ -298,8 +300,8 @@ var readSetCookiesTests = []struct { }}, }, { - Header{"Set-Cookie": {"samesitedefault=foo; SameSite"}}, - []*Cookie{{ + header: Header{"Set-Cookie": {"samesitedefault=foo; SameSite"}}, + cookies: []*Cookie{{ Name: "samesitedefault", Value: "foo", SameSite: SameSiteDefaultMode, @@ -307,8 +309,8 @@ var readSetCookiesTests = []struct { }}, }, { - Header{"Set-Cookie": {"samesiteinvalidisdefault=foo; SameSite=invalid"}}, - []*Cookie{{ + header: Header{"Set-Cookie": {"samesiteinvalidisdefault=foo; SameSite=invalid"}}, + cookies: []*Cookie{{ Name: "samesiteinvalidisdefault", Value: "foo", SameSite: SameSiteDefaultMode, @@ -316,8 +318,8 @@ var readSetCookiesTests = []struct { }}, }, { - Header{"Set-Cookie": {"samesitelax=foo; SameSite=Lax"}}, - []*Cookie{{ + header: Header{"Set-Cookie": {"samesitelax=foo; SameSite=Lax"}}, + cookies: []*Cookie{{ Name: "samesitelax", Value: "foo", SameSite: SameSiteLaxMode, @@ -325,8 +327,8 @@ var readSetCookiesTests = []struct { }}, }, { - Header{"Set-Cookie": {"samesitestrict=foo; SameSite=Strict"}}, - []*Cookie{{ + header: Header{"Set-Cookie": {"samesitestrict=foo; SameSite=Strict"}}, + cookies: []*Cookie{{ Name: "samesitestrict", Value: "foo", SameSite: SameSiteStrictMode, @@ -334,8 +336,8 @@ var readSetCookiesTests = []struct { }}, }, { - Header{"Set-Cookie": {"samesitenone=foo; SameSite=None"}}, - []*Cookie{{ + header: Header{"Set-Cookie": {"samesitenone=foo; SameSite=None"}}, + cookies: []*Cookie{{ Name: "samesitenone", Value: "foo", SameSite: SameSiteNoneMode, @@ -345,47 +347,66 @@ var readSetCookiesTests = []struct { // Make sure we can properly read back the Set-Cookie headers we create // for values containing spaces or commas: { - Header{"Set-Cookie": {`special-1=a z`}}, - []*Cookie{{Name: "special-1", Value: "a z", Raw: `special-1=a z`}}, + header: Header{"Set-Cookie": {`special-1=a z`}}, + cookies: []*Cookie{{Name: "special-1", Value: "a z", Raw: `special-1=a z`}}, }, { - Header{"Set-Cookie": {`special-2=" z"`}}, - []*Cookie{{Name: "special-2", Value: " z", Quoted: true, Raw: `special-2=" z"`}}, + header: Header{"Set-Cookie": {`special-2=" z"`}}, + cookies: []*Cookie{{Name: "special-2", Value: " z", Quoted: true, Raw: `special-2=" z"`}}, }, { - Header{"Set-Cookie": {`special-3="a "`}}, - []*Cookie{{Name: "special-3", Value: "a ", Quoted: true, Raw: `special-3="a "`}}, + header: Header{"Set-Cookie": {`special-3="a "`}}, + cookies: []*Cookie{{Name: "special-3", Value: "a ", Quoted: true, Raw: `special-3="a "`}}, }, { - Header{"Set-Cookie": {`special-4=" "`}}, - []*Cookie{{Name: "special-4", Value: " ", Quoted: true, Raw: `special-4=" "`}}, + header: Header{"Set-Cookie": {`special-4=" "`}}, + cookies: []*Cookie{{Name: "special-4", Value: " ", Quoted: true, Raw: `special-4=" "`}}, }, { - Header{"Set-Cookie": {`special-5=a,z`}}, - []*Cookie{{Name: "special-5", Value: "a,z", Raw: `special-5=a,z`}}, + header: Header{"Set-Cookie": {`special-5=a,z`}}, + cookies: []*Cookie{{Name: "special-5", Value: "a,z", Raw: `special-5=a,z`}}, }, { - Header{"Set-Cookie": {`special-6=",z"`}}, - []*Cookie{{Name: "special-6", Value: ",z", Quoted: true, Raw: `special-6=",z"`}}, + header: Header{"Set-Cookie": {`special-6=",z"`}}, + cookies: []*Cookie{{Name: "special-6", Value: ",z", Quoted: true, Raw: `special-6=",z"`}}, }, { - Header{"Set-Cookie": {`special-7=a,`}}, - []*Cookie{{Name: "special-7", Value: "a,", Raw: `special-7=a,`}}, + header: Header{"Set-Cookie": {`special-7=a,`}}, + cookies: []*Cookie{{Name: "special-7", Value: "a,", Raw: `special-7=a,`}}, }, { - Header{"Set-Cookie": {`special-8=","`}}, - []*Cookie{{Name: "special-8", Value: ",", Quoted: true, Raw: `special-8=","`}}, + header: Header{"Set-Cookie": {`special-8=","`}}, + cookies: []*Cookie{{Name: "special-8", Value: ",", Quoted: true, Raw: `special-8=","`}}, }, // Make sure we can properly read back the Set-Cookie headers // for names containing spaces: { - Header{"Set-Cookie": {`special-9 =","`}}, - []*Cookie{{Name: "special-9", Value: ",", Quoted: true, Raw: `special-9 =","`}}, + header: Header{"Set-Cookie": {`special-9 =","`}}, + cookies: []*Cookie{{Name: "special-9", Value: ",", Quoted: true, Raw: `special-9 =","`}}, }, // Quoted values (issue #46443) { - Header{"Set-Cookie": {`cookie="quoted"`}}, - []*Cookie{{Name: "cookie", Value: "quoted", Quoted: true, Raw: `cookie="quoted"`}}, + header: Header{"Set-Cookie": {`cookie="quoted"`}}, + cookies: []*Cookie{{Name: "cookie", Value: "quoted", Quoted: true, Raw: `cookie="quoted"`}}, + }, + { + header: Header{"Set-Cookie": slices.Repeat([]string{"a="}, defaultCookieMaxNum+1)}, + cookies: []*Cookie{}, + }, + { + header: Header{"Set-Cookie": slices.Repeat([]string{"a="}, 10)}, + cookies: []*Cookie{}, + godebug: "httpcookiemaxnum=5", + }, + { + header: Header{"Set-Cookie": strings.Split(strings.Repeat(";a=", defaultCookieMaxNum+1)[1:], ";")}, + cookies: slices.Repeat([]*Cookie{{Name: "a", Value: "", Quoted: false, Raw: "a="}}, defaultCookieMaxNum+1), + godebug: "httpcookiemaxnum=0", + }, + { + header: Header{"Set-Cookie": strings.Split(strings.Repeat(";a=", defaultCookieMaxNum+1)[1:], ";")}, + cookies: slices.Repeat([]*Cookie{{Name: "a", Value: "", Quoted: false, Raw: "a="}}, defaultCookieMaxNum+1), + godebug: fmt.Sprintf("httpcookiemaxnum=%v", defaultCookieMaxNum+1), }, // TODO(bradfitz): users have reported seeing this in the @@ -405,79 +426,103 @@ func toJSON(v any) string { func TestReadSetCookies(t *testing.T) { for i, tt := range readSetCookiesTests { + t.Setenv("GODEBUG", tt.godebug) for n := 0; n < 2; n++ { // to verify readSetCookies doesn't mutate its input - c := readSetCookies(tt.Header) - if !reflect.DeepEqual(c, tt.Cookies) { - t.Errorf("#%d readSetCookies: have\n%s\nwant\n%s\n", i, toJSON(c), toJSON(tt.Cookies)) + c := readSetCookies(tt.header) + if !reflect.DeepEqual(c, tt.cookies) { + t.Errorf("#%d readSetCookies: have\n%s\nwant\n%s\n", i, toJSON(c), toJSON(tt.cookies)) } } } } var readCookiesTests = []struct { - Header Header - Filter string - Cookies []*Cookie + header Header + filter string + cookies []*Cookie + godebug string }{ { - Header{"Cookie": {"Cookie-1=v$1", "c2=v2"}}, - "", - []*Cookie{ + header: Header{"Cookie": {"Cookie-1=v$1", "c2=v2"}}, + filter: "", + cookies: []*Cookie{ {Name: "Cookie-1", Value: "v$1"}, {Name: "c2", Value: "v2"}, }, }, { - Header{"Cookie": {"Cookie-1=v$1", "c2=v2"}}, - "c2", - []*Cookie{ + header: Header{"Cookie": {"Cookie-1=v$1", "c2=v2"}}, + filter: "c2", + cookies: []*Cookie{ {Name: "c2", Value: "v2"}, }, }, { - Header{"Cookie": {"Cookie-1=v$1; c2=v2"}}, - "", - []*Cookie{ + header: Header{"Cookie": {"Cookie-1=v$1; c2=v2"}}, + filter: "", + cookies: []*Cookie{ {Name: "Cookie-1", Value: "v$1"}, {Name: "c2", Value: "v2"}, }, }, { - Header{"Cookie": {"Cookie-1=v$1; c2=v2"}}, - "c2", - []*Cookie{ + header: Header{"Cookie": {"Cookie-1=v$1; c2=v2"}}, + filter: "c2", + cookies: []*Cookie{ {Name: "c2", Value: "v2"}, }, }, { - Header{"Cookie": {`Cookie-1="v$1"; c2="v2"`}}, - "", - []*Cookie{ + header: Header{"Cookie": {`Cookie-1="v$1"; c2="v2"`}}, + filter: "", + cookies: []*Cookie{ {Name: "Cookie-1", Value: "v$1", Quoted: true}, {Name: "c2", Value: "v2", Quoted: true}, }, }, { - Header{"Cookie": {`Cookie-1="v$1"; c2=v2;`}}, - "", - []*Cookie{ + header: Header{"Cookie": {`Cookie-1="v$1"; c2=v2;`}}, + filter: "", + cookies: []*Cookie{ {Name: "Cookie-1", Value: "v$1", Quoted: true}, {Name: "c2", Value: "v2"}, }, }, { - Header{"Cookie": {``}}, - "", - []*Cookie{}, + header: Header{"Cookie": {``}}, + filter: "", + cookies: []*Cookie{}, + }, + // GODEBUG=httpcookiemaxnum should work regardless if all cookies are sent + // via one "Cookie" field, or multiple fields. + { + header: Header{"Cookie": {strings.Repeat(";a=", defaultCookieMaxNum+1)[1:]}}, + cookies: []*Cookie{}, + }, + { + header: Header{"Cookie": slices.Repeat([]string{"a="}, 10)}, + cookies: []*Cookie{}, + godebug: "httpcookiemaxnum=5", + }, + { + header: Header{"Cookie": {strings.Repeat(";a=", defaultCookieMaxNum+1)[1:]}}, + cookies: slices.Repeat([]*Cookie{{Name: "a", Value: "", Quoted: false}}, defaultCookieMaxNum+1), + godebug: "httpcookiemaxnum=0", + }, + { + header: Header{"Cookie": slices.Repeat([]string{"a="}, defaultCookieMaxNum+1)}, + cookies: slices.Repeat([]*Cookie{{Name: "a", Value: "", Quoted: false}}, defaultCookieMaxNum+1), + godebug: fmt.Sprintf("httpcookiemaxnum=%v", defaultCookieMaxNum+1), }, } func TestReadCookies(t *testing.T) { for i, tt := range readCookiesTests { + t.Setenv("GODEBUG", tt.godebug) for n := 0; n < 2; n++ { // to verify readCookies doesn't mutate its input - c := readCookies(tt.Header, tt.Filter) - if !reflect.DeepEqual(c, tt.Cookies) { - t.Errorf("#%d readCookies:\nhave: %s\nwant: %s\n", i, toJSON(c), toJSON(tt.Cookies)) + c := readCookies(tt.header, tt.filter) + if !reflect.DeepEqual(c, tt.cookies) { + t.Errorf("#%d readCookies:\nhave: %s\nwant: %s\n", i, toJSON(c), toJSON(tt.cookies)) } } } @@ -689,6 +734,7 @@ func TestParseCookie(t *testing.T) { line string cookies []*Cookie err error + godebug string }{ { line: "Cookie-1=v$1", @@ -722,8 +768,28 @@ func TestParseCookie(t *testing.T) { line: "k1=\\", err: errInvalidCookieValue, }, + { + line: strings.Repeat(";a=", defaultCookieMaxNum+1)[1:], + err: errCookieNumLimitExceeded, + }, + { + line: strings.Repeat(";a=", 10)[1:], + err: errCookieNumLimitExceeded, + godebug: "httpcookiemaxnum=5", + }, + { + line: strings.Repeat(";a=", defaultCookieMaxNum+1)[1:], + cookies: slices.Repeat([]*Cookie{{Name: "a", Value: "", Quoted: false}}, defaultCookieMaxNum+1), + godebug: "httpcookiemaxnum=0", + }, + { + line: strings.Repeat(";a=", defaultCookieMaxNum+1)[1:], + cookies: slices.Repeat([]*Cookie{{Name: "a", Value: "", Quoted: false}}, defaultCookieMaxNum+1), + godebug: fmt.Sprintf("httpcookiemaxnum=%v", defaultCookieMaxNum+1), + }, } for i, tt := range tests { + t.Setenv("GODEBUG", tt.godebug) gotCookies, gotErr := ParseCookie(tt.line) if !errors.Is(gotErr, tt.err) { t.Errorf("#%d ParseCookie got error %v, want error %v", i, gotErr, tt.err) diff --git a/src/net/http/transport.go b/src/net/http/transport.go index 07b3a9e1..2778db33 100644 --- a/src/net/http/transport.go +++ b/src/net/http/transport.go @@ -1372,7 +1372,10 @@ func (w *wantConn) cancel(t *Transport) { w.done = true w.mu.Unlock() - if pc != nil { + // HTTP/2 connections (pc.alt != nil) aren't removed from the idle pool on use, + // and should not be added back here. If the pconn isn't in the idle pool, + // it's because we removed it due to an error. + if pc != nil && pc.alt == nil { t.putOrCloseIdleConn(pc) } } diff --git a/src/net/http/transport_test.go b/src/net/http/transport_test.go index 9762f058..28ad3eb6 100644 --- a/src/net/http/transport_test.go +++ b/src/net/http/transport_test.go @@ -7559,3 +7559,35 @@ func TestTransportServerProtocols(t *testing.T) { }) } } + +func TestIssue61474(t *testing.T) { + run(t, testIssue61474, []testMode{http2Mode}) +} +func testIssue61474(t *testing.T, mode testMode) { + if testing.Short() { + return + } + + // This test reliably exercises the condition causing #61474, + // but requires many iterations to do so. + // Keep the test around for now, but don't run it by default. + t.Skip("test is too large") + + cst := newClientServerTest(t, mode, HandlerFunc(func(rw ResponseWriter, req *Request) { + }), func(tr *Transport) { + tr.MaxConnsPerHost = 1 + }) + var wg sync.WaitGroup + defer wg.Wait() + for range 100000 { + wg.Go(func() { + ctx, cancel := context.WithTimeout(t.Context(), 1*time.Millisecond) + defer cancel() + req, _ := NewRequestWithContext(ctx, "GET", cst.ts.URL, nil) + resp, err := cst.c.Do(req) + if err == nil { + resp.Body.Close() + } + }) + } +} diff --git a/src/net/mail/message.go b/src/net/mail/message.go index 14f839a0..1502b359 100644 --- a/src/net/mail/message.go +++ b/src/net/mail/message.go @@ -724,7 +724,8 @@ func (p *addrParser) consumeDomainLiteral() (string, error) { } // Parse the dtext - var dtext string + dtext := p.s + dtextLen := 0 for { if p.empty() { return "", errors.New("mail: unclosed domain-literal") @@ -741,9 +742,10 @@ func (p *addrParser) consumeDomainLiteral() (string, error) { return "", fmt.Errorf("mail: bad character in domain-literal: %q", r) } - dtext += p.s[:size] + dtextLen += size p.s = p.s[size:] } + dtext = dtext[:dtextLen] // Skip the trailing ] if !p.consume(']') { diff --git a/src/net/textproto/reader.go b/src/net/textproto/reader.go index d3753401..94e3e0b5 100644 --- a/src/net/textproto/reader.go +++ b/src/net/textproto/reader.go @@ -284,8 +284,10 @@ func (r *Reader) ReadCodeLine(expectCode int) (code int, message string, err err // // An expectCode <= 0 disables the check of the status code. func (r *Reader) ReadResponse(expectCode int) (code int, message string, err error) { - code, continued, message, err := r.readCodeLine(expectCode) + code, continued, first, err := r.readCodeLine(expectCode) multi := continued + var messageBuilder strings.Builder + messageBuilder.WriteString(first) for continued { line, err := r.ReadLine() if err != nil { @@ -296,12 +298,15 @@ func (r *Reader) ReadResponse(expectCode int) (code int, message string, err err var moreMessage string code2, continued, moreMessage, err = parseCodeLine(line, 0) if err != nil || code2 != code { - message += "\n" + strings.TrimRight(line, "\r\n") + messageBuilder.WriteByte('\n') + messageBuilder.WriteString(strings.TrimRight(line, "\r\n")) continued = true continue } - message += "\n" + moreMessage + messageBuilder.WriteByte('\n') + messageBuilder.WriteString(moreMessage) } + message = messageBuilder.String() if err != nil && multi && message != "" { // replace one line error message with all lines (full message) err = &Error{code, message} diff --git a/src/net/udpsock_test.go b/src/net/udpsock_test.go index 7ad8a585..3c8da43b 100644 --- a/src/net/udpsock_test.go +++ b/src/net/udpsock_test.go @@ -710,6 +710,11 @@ func TestIPv6WriteMsgUDPAddrPortTargetAddrIPVersion(t *testing.T) { // WriteMsgUDPAddrPort accepts IPv4 and IPv4-mapped IPv6 destination addresses, // and rejects IPv6 destination addresses on a "udp4" connection. func TestIPv4WriteMsgUDPAddrPortTargetAddrIPVersion(t *testing.T) { + switch runtime.GOOS { + case "plan9": + t.Skipf("not supported on %s", runtime.GOOS) + } + if !testableNetwork("udp4") { t.Skipf("skipping: udp4 not available") } diff --git a/src/net/url/url.go b/src/net/url/url.go index 2a576594..40faa7cb 100644 --- a/src/net/url/url.go +++ b/src/net/url/url.go @@ -16,6 +16,7 @@ import ( "errors" "fmt" "maps" + "net/netip" "path" "slices" "strconv" @@ -626,40 +627,61 @@ func parseAuthority(authority string) (user *Userinfo, host string, err error) { // parseHost parses host as an authority without user // information. That is, as host[:port]. func parseHost(host string) (string, error) { - if strings.HasPrefix(host, "[") { + if openBracketIdx := strings.LastIndex(host, "["); openBracketIdx != -1 { // Parse an IP-Literal in RFC 3986 and RFC 6874. // E.g., "[fe80::1]", "[fe80::1%25en0]", "[fe80::1]:80". - i := strings.LastIndex(host, "]") - if i < 0 { + closeBracketIdx := strings.LastIndex(host, "]") + if closeBracketIdx < 0 { return "", errors.New("missing ']' in host") } - colonPort := host[i+1:] + + colonPort := host[closeBracketIdx+1:] if !validOptionalPort(colonPort) { return "", fmt.Errorf("invalid port %q after host", colonPort) } + unescapedColonPort, err := unescape(colonPort, encodeHost) + if err != nil { + return "", err + } + hostname := host[openBracketIdx+1 : closeBracketIdx] + var unescapedHostname string // RFC 6874 defines that %25 (%-encoded percent) introduces // the zone identifier, and the zone identifier can use basically // any %-encoding it likes. That's different from the host, which // can only %-encode non-ASCII bytes. // We do impose some restrictions on the zone, to avoid stupidity // like newlines. - zone := strings.Index(host[:i], "%25") - if zone >= 0 { - host1, err := unescape(host[:zone], encodeHost) + zoneIdx := strings.Index(hostname, "%25") + if zoneIdx >= 0 { + hostPart, err := unescape(hostname[:zoneIdx], encodeHost) if err != nil { return "", err } - host2, err := unescape(host[zone:i], encodeZone) + zonePart, err := unescape(hostname[zoneIdx:], encodeZone) if err != nil { return "", err } - host3, err := unescape(host[i:], encodeHost) + unescapedHostname = hostPart + zonePart + } else { + var err error + unescapedHostname, err = unescape(hostname, encodeHost) if err != nil { return "", err } - return host1 + host2 + host3, nil } + + // Per RFC 3986, only a host identified by a valid + // IPv6 address can be enclosed by square brackets. + // This excludes any IPv4 or IPv4-mapped addresses. + addr, err := netip.ParseAddr(unescapedHostname) + if err != nil { + return "", fmt.Errorf("invalid host: %w", err) + } + if addr.Is4() || addr.Is4In6() { + return "", errors.New("invalid IPv6 host") + } + return "[" + unescapedHostname + "]" + unescapedColonPort, nil } else if i := strings.LastIndex(host, ":"); i != -1 { colonPort := host[i:] if !validOptionalPort(colonPort) { diff --git a/src/net/url/url_test.go b/src/net/url/url_test.go index 16e08b63..32065583 100644 --- a/src/net/url/url_test.go +++ b/src/net/url/url_test.go @@ -383,6 +383,16 @@ var urltests = []URLTest{ }, "", }, + // valid IPv6 host with port and path + { + "https://[2001:db8::1]:8443/test/path", + &URL{ + Scheme: "https", + Host: "[2001:db8::1]:8443", + Path: "/test/path", + }, + "", + }, // host subcomponent; IPv6 address with zone identifier in RFC 6874 { "http://[fe80::1%25en0]/", // alphanum zone identifier @@ -707,6 +717,24 @@ var parseRequestURLTests = []struct { // RFC 6874. {"http://[fe80::1%en0]/", false}, {"http://[fe80::1%en0]:8080/", false}, + + // Tests exercising RFC 3986 compliance + {"https://[1:2:3:4:5:6:7:8]", true}, // full IPv6 address + {"https://[2001:db8::a:b:c:d]", true}, // compressed IPv6 address + {"https://[fe80::1%25eth0]", true}, // link-local address with zone ID (interface name) + {"https://[fe80::abc:def%254]", true}, // link-local address with zone ID (interface index) + {"https://[2001:db8::1]/path", true}, // compressed IPv6 address with path + {"https://[fe80::1%25eth0]/path?query=1", true}, // link-local with zone, path, and query + + {"https://[::ffff:192.0.2.1]", false}, + {"https://[:1] ", false}, + {"https://[1:2:3:4:5:6:7:8:9]", false}, + {"https://[1::1::1]", false}, + {"https://[1:2:3:]", false}, + {"https://[ffff::127.0.0.4000]", false}, + {"https://[0:0::test.com]:80", false}, + {"https://[2001:db8::test.com]", false}, + {"https://[test.com]", false}, } func TestParseRequestURI(t *testing.T) { @@ -1643,6 +1671,17 @@ func TestParseErrors(t *testing.T) { {"cache_object:foo", true}, {"cache_object:foo/bar", true}, {"cache_object/:foo/bar", false}, + + {"http://[192.168.0.1]/", true}, // IPv4 in brackets + {"http://[192.168.0.1]:8080/", true}, // IPv4 in brackets with port + {"http://[::ffff:192.168.0.1]/", true}, // IPv4-mapped IPv6 in brackets + {"http://[::ffff:192.168.0.1]:8080/", true}, // IPv4-mapped IPv6 in brackets with port + {"http://[::ffff:c0a8:1]/", true}, // IPv4-mapped IPv6 in brackets (hex) + {"http://[not-an-ip]/", true}, // invalid IP string in brackets + {"http://[fe80::1%foo]/", true}, // invalid zone format in brackets + {"http://[fe80::1", true}, // missing closing bracket + {"http://fe80::1]/", true}, // missing opening bracket + {"http://[test.com]/", true}, // domain name in brackets } for _, tt := range tests { u, err := Parse(tt.in) diff --git a/src/os/os_windows_test.go b/src/os/os_windows_test.go index d9af25d4..6f091c38 100644 --- a/src/os/os_windows_test.go +++ b/src/os/os_windows_test.go @@ -1883,6 +1883,34 @@ func TestFileOverlappedSeek(t *testing.T) { } } +func TestFileOverlappedReadAtVolume(t *testing.T) { + // Test that we can use File.ReadAt with an overlapped volume handle. + // See https://go.dev/issues/74951. + t.Parallel() + name := `\\.\` + filepath.VolumeName(t.TempDir()) + namep, err := syscall.UTF16PtrFromString(name) + if err != nil { + t.Fatal(err) + } + h, err := syscall.CreateFile(namep, + syscall.GENERIC_READ|syscall.GENERIC_WRITE, + syscall.FILE_SHARE_WRITE|syscall.FILE_SHARE_READ, + nil, syscall.OPEN_ALWAYS, syscall.FILE_FLAG_OVERLAPPED, 0) + if err != nil { + if errors.Is(err, syscall.ERROR_ACCESS_DENIED) { + t.Skip("skipping test: access denied") + } + t.Fatal(err) + } + f := os.NewFile(uintptr(h), name) + defer f.Close() + + var buf [0]byte + if _, err := f.ReadAt(buf[:], 0); err != nil { + t.Fatal(err) + } +} + func TestPipe(t *testing.T) { t.Parallel() r, w, err := os.Pipe() diff --git a/src/os/root_openat.go b/src/os/root_openat.go index cfc6d906..af953498 100644 --- a/src/os/root_openat.go +++ b/src/os/root_openat.go @@ -131,7 +131,9 @@ func rootMkdirAll(r *Root, fullname string, perm FileMode) error { if try > 0 || !IsNotExist(err) { return 0, &PathError{Op: "openat", Err: err} } - if err := mkdirat(parent, name, perm); err != nil { + // Try again on EEXIST, because the directory may have been created + // by another process or thread between the rootOpenDir and mkdirat calls. + if err := mkdirat(parent, name, perm); err != nil && err != syscall.EEXIST { return 0, &PathError{Op: "mkdirat", Err: err} } } diff --git a/src/os/root_test.go b/src/os/root_test.go index effcdeab..f9fbd115 100644 --- a/src/os/root_test.go +++ b/src/os/root_test.go @@ -1919,3 +1919,36 @@ func TestRootWriteReadFile(t *testing.T) { t.Fatalf("root.ReadFile(%q) = %q, %v; want %q, nil", name, got, err, want) } } + +func TestRootName(t *testing.T) { + dir := t.TempDir() + root, err := os.OpenRoot(dir) + if err != nil { + t.Fatal(err) + } + defer root.Close() + if got, want := root.Name(), dir; got != want { + t.Errorf("root.Name() = %q, want %q", got, want) + } + + f, err := root.Create("file") + if err != nil { + t.Fatal(err) + } + defer f.Close() + if got, want := f.Name(), filepath.Join(dir, "file"); got != want { + t.Errorf(`root.Create("file").Name() = %q, want %q`, got, want) + } + + if err := root.Mkdir("dir", 0o777); err != nil { + t.Fatal(err) + } + subroot, err := root.OpenRoot("dir") + if err != nil { + t.Fatal(err) + } + defer subroot.Close() + if got, want := subroot.Name(), filepath.Join(dir, "dir"); got != want { + t.Errorf(`root.OpenRoot("dir").Name() = %q, want %q`, got, want) + } +} diff --git a/src/os/root_unix.go b/src/os/root_unix.go index 4d6fc19a..c891e81b 100644 --- a/src/os/root_unix.go +++ b/src/os/root_unix.go @@ -75,7 +75,7 @@ func openRootInRoot(r *Root, name string) (*Root, error) { if err != nil { return nil, &PathError{Op: "openat", Path: name, Err: err} } - return newRoot(fd, name) + return newRoot(fd, joinPath(r.Name(), name)) } // rootOpenFileNolog is Root.OpenFile. diff --git a/src/os/root_windows.go b/src/os/root_windows.go index 523ee48d..2ec09cf2 100644 --- a/src/os/root_windows.go +++ b/src/os/root_windows.go @@ -123,7 +123,7 @@ func openRootInRoot(r *Root, name string) (*Root, error) { if err != nil { return nil, &PathError{Op: "openat", Path: name, Err: err} } - return newRoot(fd, name) + return newRoot(fd, joinPath(r.Name(), name)) } // rootOpenFileNolog is Root.OpenFile. diff --git a/src/runtime/decoratemappings_test.go b/src/runtime/decoratemappings_test.go new file mode 100644 index 00000000..7d1121c1 --- /dev/null +++ b/src/runtime/decoratemappings_test.go @@ -0,0 +1,72 @@ +// 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 runtime_test + +import ( + "os" + "regexp" + "runtime" + "testing" +) + +func validateMapLabels(t *testing.T, labels []string) { + // These are the specific region labels that need get added during the + // runtime phase. Hence they are the ones that need to be confirmed as + // present at the time the test reads its own region labels, which + // is sufficient to validate that the default `decoratemappings` value + // (enabled) was set early enough in the init process. + regions := map[string]bool{ + "allspans array": false, + "gc bits": false, + "heap": false, + "heap index": false, + "heap reservation": false, + "immortal metadata": false, + "page alloc": false, + "page alloc index": false, + "page summary": false, + "scavenge index": false, + } + for _, label := range labels { + if _, ok := regions[label]; !ok { + t.Logf("unexpected region label found: \"%s\"", label) + } + regions[label] = true + } + for label, found := range regions { + if !found { + t.Logf("region label missing: \"%s\"", label) + } + } +} + +func TestDecorateMappings(t *testing.T) { + if runtime.GOOS != "linux" { + t.Skip("decoratemappings is only supported on Linux") + // /proc/self/maps is also Linux-specific + } + + var labels []string + if rawMaps, err := os.ReadFile("/proc/self/maps"); err != nil { + t.Fatalf("failed to read /proc/self/maps: %v", err) + } else { + t.Logf("maps:%s\n", string(rawMaps)) + matches := regexp.MustCompile("[^[]+ \\[anon: Go: (.+)\\]\n").FindAllSubmatch(rawMaps, -1) + for _, match_pair := range matches { + // match_pair consists of the matching substring and the parenthesized group + labels = append(labels, string(match_pair[1])) + } + } + t.Logf("DebugDecorateMappings: %v", *runtime.DebugDecorateMappings) + if *runtime.DebugDecorateMappings != 0 && runtime.SetVMANameSupported() { + validateMapLabels(t, labels) + } else { + if len(labels) > 0 { + t.Errorf("unexpected mapping labels present: %v", labels) + } else { + t.Skip("mapping labels absent as expected") + } + } +} diff --git a/src/runtime/export_test.go b/src/runtime/export_test.go index 9a4611e2..6559ecec 100644 --- a/src/runtime/export_test.go +++ b/src/runtime/export_test.go @@ -1927,3 +1927,7 @@ func (t *TraceStackTable) Reset() { func TraceStack(gp *G, tab *TraceStackTable) { traceStack(0, gp, (*traceStackTable)(tab)) } + +var DebugDecorateMappings = &debug.decoratemappings + +func SetVMANameSupported() bool { return setVMANameSupported() } diff --git a/src/runtime/metrics/doc.go b/src/runtime/metrics/doc.go index a1902bc6..251ee22f 100644 --- a/src/runtime/metrics/doc.go +++ b/src/runtime/metrics/doc.go @@ -282,6 +282,11 @@ Below is the full list of supported metrics, ordered lexicographically. The number of non-default behaviors executed by the net/http package due to a non-default GODEBUG=http2server=... setting. + /godebug/non-default-behavior/httpcookiemaxnum:events + The number of non-default behaviors executed by the net/http + package due to a non-default GODEBUG=httpcookiemaxnum=... + setting. + /godebug/non-default-behavior/httplaxcontentlength:events The number of non-default behaviors executed by the net/http package due to a non-default GODEBUG=httplaxcontentlength=... diff --git a/src/runtime/proc.go b/src/runtime/proc.go index b41bbe93..d898c966 100644 --- a/src/runtime/proc.go +++ b/src/runtime/proc.go @@ -789,7 +789,9 @@ func cpuinit(env string) { // getGodebugEarly extracts the environment variable GODEBUG from the environment on // Unix-like operating systems and returns it. This function exists to extract GODEBUG // early before much of the runtime is initialized. -func getGodebugEarly() string { +// +// Returns nil, false if OS doesn't provide env vars early in the init sequence. +func getGodebugEarly() (string, bool) { const prefix = "GODEBUG=" var env string switch GOOS { @@ -807,12 +809,16 @@ func getGodebugEarly() string { s := unsafe.String(p, findnull(p)) if stringslite.HasPrefix(s, prefix) { - env = gostring(p)[len(prefix):] + env = gostringnocopy(p)[len(prefix):] break } } + break + + default: + return "", false } - return env + return env, true } // The bootstrap sequence is: @@ -859,11 +865,14 @@ func schedinit() { // The world starts stopped. worldStopped() + godebug, parsedGodebug := getGodebugEarly() + if parsedGodebug { + parseRuntimeDebugVars(godebug) + } ticks.init() // run as early as possible moduledataverify() stackinit() mallocinit() - godebug := getGodebugEarly() cpuinit(godebug) // must run before alginit randinit() // must run before alginit, mcommoninit alginit() // maps, hash, rand must not be used before this call @@ -880,7 +889,12 @@ func schedinit() { goenvs() secure() checkfds() - parsedebugvars() + if !parsedGodebug { + // Some platforms, e.g., Windows, didn't make env vars available "early", + // so try again now. + parseRuntimeDebugVars(gogetenv("GODEBUG")) + } + finishDebugVarsSetup() gcinit() // Allocate stack space that can be used when crashing due to bad stack diff --git a/src/runtime/runtime1.go b/src/runtime/runtime1.go index 424745d2..15b54678 100644 --- a/src/runtime/runtime1.go +++ b/src/runtime/runtime1.go @@ -402,7 +402,7 @@ var dbgvars = []*dbgVar{ {name: "updatemaxprocs", value: &debug.updatemaxprocs, def: 1}, } -func parsedebugvars() { +func parseRuntimeDebugVars(godebug string) { // defaults debug.cgocheck = 1 debug.invalidptr = 1 @@ -420,12 +420,6 @@ func parsedebugvars() { } debug.traceadvanceperiod = defaultTraceAdvancePeriod - godebug := gogetenv("GODEBUG") - - p := new(string) - *p = godebug - godebugEnv.Store(p) - // apply runtime defaults, if any for _, v := range dbgvars { if v.def != 0 { @@ -437,7 +431,6 @@ func parsedebugvars() { } } } - // apply compile-time GODEBUG settings parsegodebug(godebugDefault, nil) @@ -463,6 +456,12 @@ func parsedebugvars() { if debug.gccheckmark > 0 { debug.asyncpreemptoff = 1 } +} + +func finishDebugVarsSetup() { + p := new(string) + *p = gogetenv("GODEBUG") + godebugEnv.Store(p) setTraceback(gogetenv("GOTRACEBACK")) traceback_env = traceback_cache diff --git a/src/runtime/set_vma_name_linux.go b/src/runtime/set_vma_name_linux.go index 100c2bfe..792d6ad3 100644 --- a/src/runtime/set_vma_name_linux.go +++ b/src/runtime/set_vma_name_linux.go @@ -14,9 +14,13 @@ import ( var prSetVMAUnsupported atomic.Bool +func setVMANameSupported() bool { + return !prSetVMAUnsupported.Load() +} + // setVMAName calls prctl(PR_SET_VMA, PR_SET_VMA_ANON_NAME, start, len, name) func setVMAName(start unsafe.Pointer, length uintptr, name string) { - if debug.decoratemappings == 0 || prSetVMAUnsupported.Load() { + if debug.decoratemappings == 0 || !setVMANameSupported() { return } diff --git a/src/runtime/set_vma_name_stub.go b/src/runtime/set_vma_name_stub.go index 38f65fd5..6cb01ebf 100644 --- a/src/runtime/set_vma_name_stub.go +++ b/src/runtime/set_vma_name_stub.go @@ -10,3 +10,5 @@ import "unsafe" // setVMAName isn’t implemented func setVMAName(start unsafe.Pointer, len uintptr, name string) {} + +func setVMANameSupported() bool { return false } diff --git a/src/runtime/synctest.go b/src/runtime/synctest.go index 16af1209..529f69fd 100644 --- a/src/runtime/synctest.go +++ b/src/runtime/synctest.go @@ -410,7 +410,9 @@ func getOrSetBubbleSpecial(p unsafe.Pointer, bubbleid uint64, add bool) (assoc i } else if add { // p is not associated with a bubble, // and we've been asked to add an association. + lock(&mheap_.speciallock) s := (*specialBubble)(mheap_.specialBubbleAlloc.alloc()) + unlock(&mheap_.speciallock) s.bubbleid = bubbleid s.special.kind = _KindSpecialBubble s.special.offset = offset diff --git a/test/fixedbugs/issue75569.go b/test/fixedbugs/issue75569.go new file mode 100644 index 00000000..8420641d --- /dev/null +++ b/test/fixedbugs/issue75569.go @@ -0,0 +1,77 @@ +// run + +// 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 main + +func fff(a []int, b bool, p, q *int) { +outer: + n := a[0] + a = a[1:] + switch n { + case 1: + goto one + case 2: + goto two + case 3: + goto three + case 4: + goto four + } + +one: + goto inner +two: + goto outer +three: + goto inner +four: + goto innerSideEntry + +inner: + n = a[0] + a = a[1:] + switch n { + case 1: + goto outer + case 2: + goto inner + case 3: + goto innerSideEntry + default: + return + } +innerSideEntry: + n = a[0] + a = a[1:] + switch n { + case 1: + goto outer + case 2: + goto inner + case 3: + goto inner + } + ggg(p, q) + goto inner +} + +var b bool + +func ggg(p, q *int) { + n := *p + 5 // this +5 ends up in the entry block, well before the *p load + if b { + *q = 0 + } + *p = n +} + +func main() { + var x, y int + fff([]int{4, 4, 4}, false, &x, &y) + if x != 5 { + panic(x) + } +}