diff --git a/VERSION b/VERSION index 5378f3c07548b6..5da5a8e2892581 100644 --- a/VERSION +++ b/VERSION @@ -1,2 +1,2 @@ -go1.26.3 -time 2026-05-04T20:36:18Z +go1.26.4 +time 2026-05-29T15:26:39Z diff --git a/src/cmd/compile/internal/ssa/_gen/AMD64.rules b/src/cmd/compile/internal/ssa/_gen/AMD64.rules index 956077d3924011..f4e18337382c40 100644 --- a/src/cmd/compile/internal/ssa/_gen/AMD64.rules +++ b/src/cmd/compile/internal/ssa/_gen/AMD64.rules @@ -909,7 +909,8 @@ (LEA(Q|L)2 [0] {s} (ADD(Q|L) x x) x) && s == nil => (SHL(Q|L)const [2] x) // (x + x) << 2 -> x << 3 and similar -(SHL(Q|L)const [c] (ADD(Q|L) x x)) => (SHL(Q|L)const [c+1] x) +(SHLQconst [c] (ADDQ x x)) && c < 63 => (SHLQconst [c+1] x) +(SHLLconst [c] (ADDL x x)) && c < 31 => (SHLLconst [c+1] x) // reverse ordering of compare instruction (SETL (InvertFlags x)) => (SETG x) diff --git a/src/cmd/compile/internal/ssa/rewriteAMD64.go b/src/cmd/compile/internal/ssa/rewriteAMD64.go index d005b15a575264..f32d46407533ff 100644 --- a/src/cmd/compile/internal/ssa/rewriteAMD64.go +++ b/src/cmd/compile/internal/ssa/rewriteAMD64.go @@ -26242,6 +26242,7 @@ func rewriteValueAMD64_OpAMD64SHLLconst(v *Value) bool { return true } // match: (SHLLconst [c] (ADDL x x)) + // cond: c < 31 // result: (SHLLconst [c+1] x) for { c := auxIntToInt8(v.AuxInt) @@ -26249,7 +26250,7 @@ func rewriteValueAMD64_OpAMD64SHLLconst(v *Value) bool { break } x := v_0.Args[1] - if x != v_0.Args[0] { + if x != v_0.Args[0] || !(c < 31) { break } v.reset(OpAMD64SHLLconst) @@ -26513,6 +26514,7 @@ func rewriteValueAMD64_OpAMD64SHLQconst(v *Value) bool { return true } // match: (SHLQconst [c] (ADDQ x x)) + // cond: c < 63 // result: (SHLQconst [c+1] x) for { c := auxIntToInt8(v.AuxInt) @@ -26520,7 +26522,7 @@ func rewriteValueAMD64_OpAMD64SHLQconst(v *Value) bool { break } x := v_0.Args[1] - if x != v_0.Args[0] { + if x != v_0.Args[0] || !(c < 63) { break } v.reset(OpAMD64SHLQconst) diff --git a/src/cmd/go.mod b/src/cmd/go.mod index 14107c2d8ed9f3..8ffd61a0242c2d 100644 --- a/src/cmd/go.mod +++ b/src/cmd/go.mod @@ -11,7 +11,7 @@ require ( golang.org/x/sys v0.39.0 golang.org/x/telemetry v0.0.0-20251128220624-abf20d0e57ec golang.org/x/term v0.38.0 - golang.org/x/tools v0.39.1-0.20260323181443-4f499ecaa91d + golang.org/x/tools v0.39.1-0.20260527181557-0f52e3809b35 ) require ( diff --git a/src/cmd/go.sum b/src/cmd/go.sum index c4920417b21b3d..107b74bfc23781 100644 --- a/src/cmd/go.sum +++ b/src/cmd/go.sum @@ -22,7 +22,7 @@ golang.org/x/term v0.38.0 h1:PQ5pkm/rLO6HnxFR7N2lJHOZX6Kez5Y1gDSJla6jo7Q= golang.org/x/term v0.38.0/go.mod h1:bSEAKrOT1W+VSu9TSCMtoGEOUcKxOKgl3LE5QEF/xVg= golang.org/x/text v0.32.0 h1:ZD01bjUt1FQ9WJ0ClOL5vxgxOI/sVCNgX1YtKwcY0mU= golang.org/x/text v0.32.0/go.mod h1:o/rUWzghvpD5TXrTIBuJU77MTaN0ljMWE47kxGJQ7jY= -golang.org/x/tools v0.39.1-0.20260323181443-4f499ecaa91d h1:d9RYG/Z8xQk+tFy5IyhSwUrt4HhSsqHw/uhwmn1KaJg= -golang.org/x/tools v0.39.1-0.20260323181443-4f499ecaa91d/go.mod h1:JnefbkDPyD8UU2kI5fuf8ZX4/yUeh9W877ZeBONxUqQ= +golang.org/x/tools v0.39.1-0.20260527181557-0f52e3809b35 h1:NtO66pjvzVNcFzUfpQKKOKMrSs1SYT4qPtV3xpdv6BE= +golang.org/x/tools v0.39.1-0.20260527181557-0f52e3809b35/go.mod h1:JnefbkDPyD8UU2kI5fuf8ZX4/yUeh9W877ZeBONxUqQ= rsc.io/markdown v0.0.0-20240306144322-0bf8f97ee8ef h1:mqLYrXCXYEZOop9/Dbo6RPX11539nwiCNBb1icVPmw8= rsc.io/markdown v0.0.0-20240306144322-0bf8f97ee8ef/go.mod h1:8xcPgWmwlZONN1D9bjxtHEjrUtSEa3fakVF8iaewYKQ= diff --git a/src/cmd/vendor/golang.org/x/tools/go/analysis/passes/modernize/slicescontains.go b/src/cmd/vendor/golang.org/x/tools/go/analysis/passes/modernize/slicescontains.go index 3b3268526689d7..a5bed59347dd67 100644 --- a/src/cmd/vendor/golang.org/x/tools/go/analysis/passes/modernize/slicescontains.go +++ b/src/cmd/vendor/golang.org/x/tools/go/analysis/passes/modernize/slicescontains.go @@ -19,6 +19,7 @@ import ( "golang.org/x/tools/internal/astutil" "golang.org/x/tools/internal/refactor" "golang.org/x/tools/internal/typeparams" + "golang.org/x/tools/internal/typesinternal" "golang.org/x/tools/internal/typesinternal/typeindex" "golang.org/x/tools/internal/versions" ) @@ -58,12 +59,8 @@ var SlicesContainsAnalyzer = &analysis.Analyzer{ // statement is "found = false" (or vice versa), the // loop becomes "found = [!]slices.Contains(...)". // -// It may change cardinality of effects of the "needle" expression. -// (Mostly this appears to be a desirable optimization, avoiding -// redundantly repeated evaluation.) -// -// TODO(adonovan): Add a check that needle/predicate expression from -// if-statement has no effects. Now the program behavior may change. +// It rejects candidates whose needle/predicate expression from the if-statement +// has side effects to avoid changes in program behavior. func slicescontains(pass *analysis.Pass) (any, error) { // Skip the analyzer in packages where its // fixes would create an import cycle. @@ -174,6 +171,11 @@ func slicescontains(pass *analysis.Pass) (any, error) { return } + // Reject if needle/predicate expression has side effects. + if !typesinternal.NoEffects(info, arg2) { + return + } + // Reject if the body, needle or predicate references either range variable. usesRangeVar := func(n ast.Node) bool { cur, ok := curRange.FindNode(n) diff --git a/src/cmd/vendor/modules.txt b/src/cmd/vendor/modules.txt index 4e2260af522911..781b9c8fb47af3 100644 --- a/src/cmd/vendor/modules.txt +++ b/src/cmd/vendor/modules.txt @@ -73,7 +73,7 @@ golang.org/x/text/internal/tag golang.org/x/text/language golang.org/x/text/transform golang.org/x/text/unicode/norm -# golang.org/x/tools v0.39.1-0.20260323181443-4f499ecaa91d +# golang.org/x/tools v0.39.1-0.20260527181557-0f52e3809b35 ## explicit; go 1.24.0 golang.org/x/tools/cmd/bisect golang.org/x/tools/cover diff --git a/src/crypto/internal/fips140/drbg/entropy_fips140.go b/src/crypto/internal/fips140/drbg/entropy_fips140.go index ba083e8c332f70..9f936a37b89ec1 100644 --- a/src/crypto/internal/fips140/drbg/entropy_fips140.go +++ b/src/crypto/internal/fips140/drbg/entropy_fips140.go @@ -2,24 +2,18 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. +// Entropy generation in FIPS 140-3 mode uses a scratch buffer in the BSS +// section (see below), which usually doesn't cost much, except on Wasm, due to +// the way the linear memory works. FIPS 140-3 mode is not supported on Wasm, so +// we just use a build tag to exclude it. (Could also exclude other platforms +// that does not support FIPS 140-3 mode, but as the BSS variable doesn't cost +// much, don't bother.) +// //go:build !wasm -// This file contains reading from from entropy sources in FIPS-140 -// mode. It uses a scratch buffer in the BSS section (see below), -// which usually doesn't cost much, except on Wasm, due to the way -// the linear memory works. FIPS-140 mode is not supported on Wasm, -// so we just use a build tag to exclude it. (Could also exclude other -// platforms that does not support FIPS-140 mode, but as the BSS -// variable doesn't cost much, don't bother.) - package drbg -import ( - entropy "crypto/internal/entropy/v1.0.0" - "crypto/internal/sysrand" - "sync" - "sync/atomic" -) +import entropy "crypto/internal/entropy/v1.0.0" // memory is a scratch buffer that is accessed between samples by the entropy // source to expose it to memory access timings. @@ -50,48 +44,3 @@ func getEntropy() *[SeedSize]byte { } return &seed } - -// getEntropy is very slow (~500µs), so we don't want it on the hot path. -// We keep both a persistent DRBG instance and a pool of additional instances. -// Occasional uses will use drbgInstance, even if the pool was emptied since the -// last use. Frequent concurrent uses will fill the pool and use it. -var drbgInstance atomic.Pointer[Counter] -var drbgPool = sync.Pool{ - New: func() any { - return NewCounter(getEntropy()) - }, -} - -func readFromEntropy(b []byte) { - // At every read, 128 random bits from the operating system are mixed as - // additional input, to make the output as strong as non-FIPS randomness. - // This is not credited as entropy for FIPS purposes, as allowed by Section - // 8.7.2: "Note that a DRBG does not rely on additional input to provide - // entropy, even though entropy could be provided in the additional input". - additionalInput := new([SeedSize]byte) - sysrand.Read(additionalInput[:16]) - - drbg := drbgInstance.Swap(nil) - if drbg == nil { - drbg = drbgPool.Get().(*Counter) - } - defer func() { - if !drbgInstance.CompareAndSwap(nil, drbg) { - drbgPool.Put(drbg) - } - }() - - for len(b) > 0 { - size := min(len(b), maxRequestSize) - if reseedRequired := drbg.Generate(b[:size], additionalInput); reseedRequired { - // See SP 800-90A Rev. 1, Section 9.3.1, Steps 6-8, as explained in - // Section 9.3.2: if Generate reports a reseed is required, the - // additional input is passed to Reseed along with the entropy and - // then nulled before the next Generate call. - drbg.Reseed(getEntropy(), additionalInput) - additionalInput = nil - continue - } - b = b[size:] - } -} diff --git a/src/crypto/internal/fips140/drbg/entropy_wasm.go b/src/crypto/internal/fips140/drbg/entropy_wasm.go index f2e4cc73b16d31..894fda974a43f0 100644 --- a/src/crypto/internal/fips140/drbg/entropy_wasm.go +++ b/src/crypto/internal/fips140/drbg/entropy_wasm.go @@ -6,6 +6,6 @@ package drbg -func readFromEntropy(b []byte) { - panic("FIPS-140 entropy generation is not supported on Wasm") +func getEntropy() *[SeedSize]byte { + panic("FIPS 140-3 entropy generation is not supported on Wasm") } diff --git a/src/crypto/internal/fips140/drbg/rand.go b/src/crypto/internal/fips140/drbg/rand.go index d9e545f980b2fc..7eee5fa6baab19 100644 --- a/src/crypto/internal/fips140/drbg/rand.go +++ b/src/crypto/internal/fips140/drbg/rand.go @@ -12,8 +12,21 @@ import ( "crypto/internal/fips140" "crypto/internal/sysrand" "io" + "sync" + "sync/atomic" ) +// getEntropy is very slow (~500µs), so we don't want it on the hot path. +// We keep both a persistent DRBG instance and a pool of additional instances. +// Occasional uses will use drbgInstance, even if the pool was emptied since the +// last use. Frequent concurrent uses will fill the pool and use it. +var drbgInstance atomic.Pointer[Counter] +var drbgPool = sync.Pool{ + New: func() any { + return NewCounter(getEntropy()) + }, +} + // Read fills b with cryptographically secure random bytes. In FIPS mode, it // uses an SP 800-90A Rev. 1 Deterministic Random Bit Generator (DRBG). // Otherwise, it uses the operating system's random number generator. @@ -32,7 +45,37 @@ func Read(b []byte) { return } - readFromEntropy(b) + // At every read, 128 random bits from the operating system are mixed as + // additional input, to make the output as strong as non-FIPS randomness. + // This is not credited as entropy for FIPS purposes, as allowed by Section + // 8.7.2: "Note that a DRBG does not rely on additional input to provide + // entropy, even though entropy could be provided in the additional input". + additionalInput := new([SeedSize]byte) + sysrand.Read(additionalInput[:16]) + + drbg := drbgInstance.Swap(nil) + if drbg == nil { + drbg = drbgPool.Get().(*Counter) + } + defer func() { + if !drbgInstance.CompareAndSwap(nil, drbg) { + drbgPool.Put(drbg) + } + }() + + for len(b) > 0 { + size := min(len(b), maxRequestSize) + if reseedRequired := drbg.Generate(b[:size], additionalInput); reseedRequired { + // See SP 800-90A Rev. 1, Section 9.3.1, Steps 6-8, as explained in + // Section 9.3.2: if Generate reports a reseed is required, the + // additional input is passed to Reseed along with the entropy and + // then nulled before the next Generate call. + drbg.Reseed(getEntropy(), additionalInput) + additionalInput = nil + continue + } + b = b[size:] + } } var testingReader io.Reader diff --git a/src/crypto/x509/verify.go b/src/crypto/x509/verify.go index 8151a731253c67..5a6ab35b2a5b6b 100644 --- a/src/crypto/x509/verify.go +++ b/src/crypto/x509/verify.go @@ -110,7 +110,7 @@ func (h HostnameError) Error() string { c := h.Certificate maxNamesIncluded := 100 - if !c.hasSANExtension() && matchHostnames(c.Subject.CommonName, h.Host) { + if !c.hasSANExtension() && matchHostnames(c.Subject.CommonName, splitHostname(h.Host)) { return "x509: certificate relies on legacy Common Name field, use SANs instead" } @@ -867,16 +867,14 @@ func matchExactly(hostA, hostB string) bool { return toLowerCaseASCII(hostA) == toLowerCaseASCII(hostB) } -func matchHostnames(pattern, host string) bool { +func matchHostnames(pattern string, hostParts []string) bool { pattern = toLowerCaseASCII(pattern) - host = toLowerCaseASCII(strings.TrimSuffix(host, ".")) - if len(pattern) == 0 || len(host) == 0 { + if len(pattern) == 0 || len(hostParts) == 0 { return false } patternParts := strings.Split(pattern, ".") - hostParts := strings.Split(host, ".") if len(patternParts) != len(hostParts) { return false @@ -954,6 +952,7 @@ func (c *Certificate) VerifyHostname(h string) error { candidateName := toLowerCaseASCII(h) // Save allocations inside the loop. validCandidateName := validHostnameInput(candidateName) + hostParts := splitHostname(candidateName) for _, match := range c.DNSNames { // Ideally, we'd only match valid hostnames according to RFC 6125 like @@ -962,7 +961,7 @@ func (c *Certificate) VerifyHostname(h string) error { // always allow perfect matches, and only apply wildcard and trailing // dot processing to valid hostnames. if validCandidateName && validHostnamePattern(match) { - if matchHostnames(match, candidateName) { + if matchHostnames(match, hostParts) { return nil } } else { @@ -975,6 +974,10 @@ func (c *Certificate) VerifyHostname(h string) error { return HostnameError{c, h} } +func splitHostname(host string) []string { + return strings.Split(toLowerCaseASCII(strings.TrimSuffix(host, ".")), ".") +} + func checkChainForKeyUsage(chain []*Certificate, keyUsages []ExtKeyUsage) bool { usages := make([]ExtKeyUsage, len(keyUsages)) copy(usages, keyUsages) diff --git a/src/mime/encodedword.go b/src/mime/encodedword.go index c4afad043a2908..d3c0329750cc66 100644 --- a/src/mime/encodedword.go +++ b/src/mime/encodedword.go @@ -275,8 +275,8 @@ func (d *WordDecoder) DecodeHeader(header string) (string, error) { content, err := decode(encoding, text) if err != nil { betweenWords = false - buf.WriteString(header[:start+2]) - header = header[start+2:] + buf.WriteString(header[:end]) + header = header[end:] continue } diff --git a/src/mime/encodedword_test.go b/src/mime/encodedword_test.go index 2a98794380fb02..befc3cd99609d8 100644 --- a/src/mime/encodedword_test.go +++ b/src/mime/encodedword_test.go @@ -140,6 +140,10 @@ func TestDecodeHeader(t *testing.T) { {"=?ISO-8859-1?Q?a?= =?ISO-8859-1?Q?b?=", "ab"}, {"=?ISO-8859-1?Q?a?= \r\n\t =?ISO-8859-1?Q?b?=", "ab"}, {"=?ISO-8859-1?Q?a_b?=", "a b"}, + // Undecodable words + {"=?UTF-8?b?garbage?= =?UTF-8?b?QW5kcsOp?= =?UTF-8?b?garbage?=", "=?UTF-8?b?garbage?= André =?UTF-8?b?garbage?="}, + {"=?UTF-8?b?QW5kcsOp", "=?UTF-8?b?QW5kcsOp"}, + {"=?UTF-8?x?y?=?UTF-8?x?y=?", "=?UTF-8?x?y?=?UTF-8?x?y=?"}, } for _, test := range tests { diff --git a/src/net/smtp/smtp_test.go b/src/net/smtp/smtp_test.go index b272b29dc54c36..7995fb7d80d41b 100644 --- a/src/net/smtp/smtp_test.go +++ b/src/net/smtp/smtp_test.go @@ -695,7 +695,7 @@ func TestHello(t *testing.T) { err = c.Hello("customhost") case 1: err = c.StartTLS(nil) - if err.Error() == "502 Not implemented" { + if err.Error() == `502 "Not implemented"` { err = nil } case 2: @@ -953,8 +953,8 @@ func TestAuthFailed(t *testing.T) { if err == nil { t.Error("Auth: expected error; got none") - } else if err.Error() != "535 Invalid credentials\nplease see www.example.com" { - t.Errorf("Auth: got error: %v, want: %s", err, "535 Invalid credentials\nplease see www.example.com") + } else if err.Error() != `535 "Invalid credentials\nplease see www.example.com"` { + t.Errorf("Auth: got error: %v, want: %s", err, `535 "Invalid credentials\nplease see www.example.com"`) } bcmdbuf.Flush() diff --git a/src/net/textproto/reader.go b/src/net/textproto/reader.go index 6df3a630917d78..8673ddcb48c5c5 100644 --- a/src/net/textproto/reader.go +++ b/src/net/textproto/reader.go @@ -215,13 +215,13 @@ func (r *Reader) readCodeLine(expectCode int) (code int, continued bool, message func parseCodeLine(line string, expectCode int) (code int, continued bool, message string, err error) { if len(line) < 4 || line[3] != ' ' && line[3] != '-' { - err = ProtocolError("short response: " + line) + err = ProtocolError(fmt.Sprintf("short response: %q", line)) return } continued = line[3] == '-' code, err = strconv.Atoi(line[0:3]) if err != nil || code < 100 { - err = ProtocolError("invalid response code: " + line) + err = ProtocolError(fmt.Sprintf("invalid response code: %q", line)) return } message = line[4:] @@ -253,7 +253,7 @@ func parseCodeLine(line string, expectCode int) (code int, continued bool, messa func (r *Reader) ReadCodeLine(expectCode int) (code int, message string, err error) { code, continued, message, err := r.readCodeLine(expectCode) if err == nil && continued { - err = ProtocolError("unexpected multi-line response: " + message) + err = ProtocolError(fmt.Sprintf("unexpected multi-line response: %q", message)) } return } @@ -541,7 +541,7 @@ func readMIMEHeader(r *Reader, maxMemory, maxHeaders int64) (MIMEHeader, error) if err != nil { return m, err } - return m, ProtocolError("malformed MIME header initial line: " + string(line)) + return m, ProtocolError(fmt.Sprintf("malformed MIME header initial line: %q", line)) } for { @@ -553,15 +553,15 @@ func readMIMEHeader(r *Reader, maxMemory, maxHeaders int64) (MIMEHeader, error) // Key ends at first colon. k, v, ok := bytes.Cut(kv, colon) if !ok { - return m, ProtocolError("malformed MIME header line: " + string(kv)) + return m, ProtocolError(fmt.Sprintf("malformed MIME header line: %q", kv)) } key, ok := canonicalMIMEHeaderKey(k) if !ok { - return m, ProtocolError("malformed MIME header line: " + string(kv)) + return m, ProtocolError(fmt.Sprintf("malformed MIME header line: %q", kv)) } for _, c := range v { if !validHeaderValueByte(c) { - return m, ProtocolError("malformed MIME header line: " + string(kv)) + return m, ProtocolError(fmt.Sprintf("malformed MIME header line: %q", kv)) } } diff --git a/src/net/textproto/reader_test.go b/src/net/textproto/reader_test.go index d510f9b338673f..3b2d003bcf2f07 100644 --- a/src/net/textproto/reader_test.go +++ b/src/net/textproto/reader_test.go @@ -411,6 +411,8 @@ func TestReadMultiLineError(t *testing.T) { "Unexpected but legal text!\n" + "5.1.1 https://support.google.com/mail/answer/6596 h20si25154304pfd.166 - gsmtp" + wantError := `550 "5.1.1 The email account that you tried to reach does not exist. Please try\n5.1.1 double-checking the recipient's email address for typos or\n5.1.1 unnecessary spaces. Learn more at\nUnexpected but legal text!\n5.1.1 https://support.google.com/mail/answer/6596 h20si25154304pfd.166 - gsmtp"` + code, msg, err := r.ReadResponse(250) if err == nil { t.Errorf("ReadResponse: no error, want error") @@ -421,8 +423,8 @@ func TestReadMultiLineError(t *testing.T) { if msg != wantMsg { t.Errorf("ReadResponse: msg=%q, want %q", msg, wantMsg) } - if err != nil && err.Error() != "550 "+wantMsg { - t.Errorf("ReadResponse: error=%q, want %q", err.Error(), "550 "+wantMsg) + if err != nil && err.Error() != wantError { + t.Errorf("ReadResponse: error=%q, want %q", err.Error(), wantError) } } diff --git a/src/net/textproto/textproto.go b/src/net/textproto/textproto.go index 00dc8cbee52b48..8d323e1f16eef0 100644 --- a/src/net/textproto/textproto.go +++ b/src/net/textproto/textproto.go @@ -41,7 +41,7 @@ type Error struct { } func (e *Error) Error() string { - return fmt.Sprintf("%03d %s", e.Code, e.Msg) + return fmt.Sprintf("%03d %q", e.Code, e.Msg) } // A ProtocolError describes a protocol violation such diff --git a/src/runtime/race/race_linux_arm64.syso b/src/runtime/race/race_linux_arm64.syso index 6a26af1ca30820..bf7a1e84a2f468 100644 Binary files a/src/runtime/race/race_linux_arm64.syso and b/src/runtime/race/race_linux_arm64.syso differ diff --git a/test/fixedbugs/issue79182.go b/test/fixedbugs/issue79182.go new file mode 100644 index 00000000000000..9b2694a9359cc6 --- /dev/null +++ b/test/fixedbugs/issue79182.go @@ -0,0 +1,32 @@ +// run + +// Copyright 2026 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Issue 79182: SHLQconst/SHLLconst rewrite rule for (x+x)<