diff options
27 files changed, 451 insertions, 336 deletions
@@ -1,2 +1,2 @@ -go1.22.3 -time 2024-05-01T19:49:47Z +go1.22.4 +time 2024-05-30T19:26:07Z diff --git a/src/archive/zip/reader.go b/src/archive/zip/reader.go index ff6fedf..60b34b7 100644 --- a/src/archive/zip/reader.go +++ b/src/archive/zip/reader.go @@ -699,9 +699,13 @@ func findSignatureInBlock(b []byte) int { if b[i] == 'P' && b[i+1] == 'K' && b[i+2] == 0x05 && b[i+3] == 0x06 { // n is length of comment n := int(b[i+directoryEndLen-2]) | int(b[i+directoryEndLen-1])<<8 - if n+directoryEndLen+i <= len(b) { - return i + if n+directoryEndLen+i > len(b) { + // Truncated comment. + // Some parsers (such as Info-ZIP) ignore the truncated comment + // rather than treating it as a hard error. + return -1 } + return i } } return -1 diff --git a/src/archive/zip/reader_test.go b/src/archive/zip/reader_test.go index 631515c..9a77c1a 100644 --- a/src/archive/zip/reader_test.go +++ b/src/archive/zip/reader_test.go @@ -570,6 +570,14 @@ var tests = []ZipTest{ }, }, }, + // Issue 66869: Don't skip over an EOCDR with a truncated comment. + // The test file sneakily hides a second EOCDR before the first one; + // previously we would extract one file ("file") from this archive, + // while most other tools would reject the file or extract a different one ("FILE"). + { + Name: "comment-truncated.zip", + Error: ErrFormat, + }, } func TestReader(t *testing.T) { diff --git a/src/archive/zip/testdata/comment-truncated.zip b/src/archive/zip/testdata/comment-truncated.zip Binary files differnew file mode 100644 index 0000000..1bc19a8 --- /dev/null +++ b/src/archive/zip/testdata/comment-truncated.zip diff --git a/src/cmd/cgo/internal/testplugin/plugin_test.go b/src/cmd/cgo/internal/testplugin/plugin_test.go index 1e32ff8..4900ada 100644 --- a/src/cmd/cgo/internal/testplugin/plugin_test.go +++ b/src/cmd/cgo/internal/testplugin/plugin_test.go @@ -74,6 +74,7 @@ func testMain(m *testing.M) int { } defer os.RemoveAll(GOPATH) tmpDir = GOPATH + fmt.Printf("TMPDIR=%s\n", tmpDir) modRoot := filepath.Join(GOPATH, "src", "testplugin") altRoot := filepath.Join(GOPATH, "alt", "src", "testplugin") @@ -395,3 +396,21 @@ func TestIssue62430(t *testing.T) { goCmd(t, "build", "-o", "issue62430.exe", "./issue62430/main.go") run(t, "./issue62430.exe") } + +func TestTextSectionSplit(t *testing.T) { + globalSkip(t) + if runtime.GOOS != "darwin" || runtime.GOARCH != "arm64" { + t.Skipf("text section splitting is not done in %s/%s", runtime.GOOS, runtime.GOARCH) + } + + // Use -ldflags=-debugtextsize=262144 to let the linker split text section + // at a smaller size threshold, so it actually splits for the test binary. + goCmd(nil, "build", "-ldflags=-debugtextsize=262144", "-o", "host-split.exe", "./host") + run(t, "./host-split.exe") + + // Check that we did split text sections. + syms := goCmd(nil, "tool", "nm", "host-split.exe") + if !strings.Contains(syms, "runtime.text.1") { + t.Errorf("runtime.text.1 not found, text section not split?") + } +} diff --git a/src/cmd/compile/internal/compare/compare.go b/src/cmd/compile/internal/compare/compare.go index e165cd6..cb2f84e 100644 --- a/src/cmd/compile/internal/compare/compare.go +++ b/src/cmd/compile/internal/compare/compare.go @@ -148,7 +148,7 @@ func calculateCostForType(t *types.Type) int64 { return EqStructCost(t) case types.TSLICE: // Slices are not comparable. - base.Fatalf("eqStructFieldCost: unexpected slice type") + base.Fatalf("calculateCostForType: unexpected slice type") case types.TARRAY: elemCost := calculateCostForType(t.Elem()) cost = t.NumElem() * elemCost @@ -371,6 +371,11 @@ func eqmem(p, q ir.Node, field int, size int64) ir.Node { } func eqmemfunc(size int64, t *types.Type) (fn *ir.Name, needsize bool) { + if !base.Ctxt.Arch.CanMergeLoads && t.Alignment() < int64(base.Ctxt.Arch.Alignment) && t.Alignment() < t.Size() { + // We can't use larger comparisons if the value might not be aligned + // enough for the larger comparison. See issues 46283 and 67160. + size = 0 + } switch size { case 1, 2, 4, 8, 16: buf := fmt.Sprintf("memequal%d", int(size)*8) diff --git a/src/cmd/compile/internal/noder/irgen.go b/src/cmd/compile/internal/noder/irgen.go index e0b7bb9..145bcc8 100644 --- a/src/cmd/compile/internal/noder/irgen.go +++ b/src/cmd/compile/internal/noder/irgen.go @@ -34,7 +34,13 @@ func checkFiles(m posMap, noders []*noder) (*types2.Package, *types2.Info) { posBaseMap := make(map[*syntax.PosBase]*syntax.File) for i, p := range noders { files[i] = p.file - posBaseMap[p.file.Pos().Base()] = p.file + // The file.Pos() is the position of the package clause. + // If there's a //line directive before that, file.Pos().Base() + // refers to that directive, not the file itself. + // Make sure to consistently map back to file base, here and + // when we look for a file in the conf.Error handler below, + // otherwise the file may not be found (was go.dev/issue/67141). + posBaseMap[fileBase(p.file.Pos())] = p.file } // typechecking @@ -68,13 +74,12 @@ func checkFiles(m posMap, noders []*noder) (*types2.Package, *types2.Info) { terr := err.(types2.Error) msg := terr.Msg if versionErrorRx.MatchString(msg) { - posBase := terr.Pos.Base() - for !posBase.IsFileBase() { // line directive base - posBase = posBase.Pos().Base() - } + posBase := fileBase(terr.Pos) fileVersion := info.FileVersions[posBase] file := posBaseMap[posBase] - if file.GoVersion == fileVersion { + if file == nil { + // This should never happen, but be careful and don't crash. + } else if file.GoVersion == fileVersion { // If we have a version error caused by //go:build, report it. msg = fmt.Sprintf("%s (file declares //go:build %s)", msg, fileVersion) } else { @@ -149,6 +154,15 @@ func checkFiles(m posMap, noders []*noder) (*types2.Package, *types2.Info) { return pkg, info } +// fileBase returns a file's position base given a position in the file. +func fileBase(pos syntax.Pos) *syntax.PosBase { + base := pos.Base() + for !base.IsFileBase() { // line directive base + base = base.Pos().Base() + } + return base +} + // A cycleFinder detects anonymous interface cycles (go.dev/issue/56103). type cycleFinder struct { cyclic map[*types2.Interface]bool diff --git a/src/cmd/compile/internal/walk/assign.go b/src/cmd/compile/internal/walk/assign.go index fc3b858..63b6a1d 100644 --- a/src/cmd/compile/internal/walk/assign.go +++ b/src/cmd/compile/internal/walk/assign.go @@ -623,21 +623,23 @@ func isAppendOfMake(n ir.Node) bool { // panicmakeslicelen() // } // s := l1 -// n := len(s) + l2 -// // Compare n and s as uint so growslice can panic on overflow of len(s) + l2. -// // cap is a positive int and n can become negative when len(s) + l2 -// // overflows int. Interpreting n when negative as uint makes it larger -// // than cap(s). growslice will check the int n arg and panic if n is -// // negative. This prevents the overflow from being undetected. -// if uint(n) <= uint(cap(s)) { -// s = s[:n] -// } else { -// s = growslice(T, s.ptr, n, s.cap, l2, T) +// if l2 != 0 { +// n := len(s) + l2 +// // Compare n and s as uint so growslice can panic on overflow of len(s) + l2. +// // cap is a positive int and n can become negative when len(s) + l2 +// // overflows int. Interpreting n when negative as uint makes it larger +// // than cap(s). growslice will check the int n arg and panic if n is +// // negative. This prevents the overflow from being undetected. +// if uint(n) <= uint(cap(s)) { +// s = s[:n] +// } else { +// s = growslice(T, s.ptr, n, s.cap, l2, T) +// } +// // clear the new portion of the underlying array. +// hp := &s[len(s)-l2] +// hn := l2 * sizeof(T) +// memclr(hp, hn) // } -// // clear the new portion of the underlying array. -// hp := &s[len(s)-l2] -// hn := l2 * sizeof(T) -// memclr(hp, hn) // } // s // @@ -671,11 +673,18 @@ func extendSlice(n *ir.CallExpr, init *ir.Nodes) ir.Node { s := typecheck.TempAt(base.Pos, ir.CurFunc, l1.Type()) nodes = append(nodes, ir.NewAssignStmt(base.Pos, s, l1)) + // if l2 != 0 { + // Avoid work if we're not appending anything. But more importantly, + // avoid allowing hp to be a past-the-end pointer when clearing. See issue 67255. + nifnz := ir.NewIfStmt(base.Pos, ir.NewBinaryExpr(base.Pos, ir.ONE, l2, ir.NewInt(base.Pos, 0)), nil, nil) + nifnz.Likely = true + nodes = append(nodes, nifnz) + elemtype := s.Type().Elem() // n := s.len + l2 nn := typecheck.TempAt(base.Pos, ir.CurFunc, types.Types[types.TINT]) - nodes = append(nodes, ir.NewAssignStmt(base.Pos, nn, ir.NewBinaryExpr(base.Pos, ir.OADD, ir.NewUnaryExpr(base.Pos, ir.OLEN, s), l2))) + nifnz.Body = append(nifnz.Body, ir.NewAssignStmt(base.Pos, nn, ir.NewBinaryExpr(base.Pos, ir.OADD, ir.NewUnaryExpr(base.Pos, ir.OLEN, s), l2))) // if uint(n) <= uint(s.cap) nuint := typecheck.Conv(nn, types.Types[types.TUINT]) @@ -697,7 +706,7 @@ func extendSlice(n *ir.CallExpr, init *ir.Nodes) ir.Node { l2)), } - nodes = append(nodes, nif) + nifnz.Body = append(nifnz.Body, nif) // hp := &s[s.len - l2] // TODO: &s[s.len] - hn? @@ -723,7 +732,7 @@ func extendSlice(n *ir.CallExpr, init *ir.Nodes) ir.Node { // if growslice isn't called do we need to do the zeroing ourselves. nif.Body = append(nif.Body, clr...) } else { - nodes = append(nodes, clr...) + nifnz.Body = append(nifnz.Body, clr...) } typecheck.Stmts(nodes) diff --git a/src/cmd/go/internal/toolchain/select.go b/src/cmd/go/internal/toolchain/select.go index dcf3be9..14a8d3c 100644 --- a/src/cmd/go/internal/toolchain/select.go +++ b/src/cmd/go/internal/toolchain/select.go @@ -184,6 +184,13 @@ func Select() { } if gover.Compare(goVers, minVers) > 0 { gotoolchain = "go" + goVers + // Starting with Go 1.21, the first released version has a .0 patch version suffix. + // Don't try to download a language version (sans patch component), such as go1.22. + // Instead, use the first toolchain of that language version, such as 1.22.0. + // See golang.org/issue/62278. + if gover.IsLang(goVers) && gover.Compare(goVers, "1.21") >= 0 { + gotoolchain += ".0" + } gover.Startup.AutoGoVersion = goVers gover.Startup.AutoToolchain = "" // in case we are overriding it for being too old } @@ -312,6 +319,10 @@ func Exec(gotoolchain string) { dir, err := modfetch.Download(context.Background(), m) if err != nil { if errors.Is(err, fs.ErrNotExist) { + toolVers := gover.FromToolchain(gotoolchain) + if gover.IsLang(toolVers) && gover.Compare(toolVers, "1.21") >= 0 { + base.Fatalf("invalid toolchain: %s is a language version but not a toolchain version (%s.x)", gotoolchain, gotoolchain) + } base.Fatalf("download %s for %s/%s: toolchain not available", gotoolchain, runtime.GOOS, runtime.GOARCH) } base.Fatalf("download %s: %v", gotoolchain, err) diff --git a/src/cmd/go/testdata/script/build_issue48319.txt b/src/cmd/go/testdata/script/build_issue48319.txt index 4543303..148d8f0 100644 --- a/src/cmd/go/testdata/script/build_issue48319.txt +++ b/src/cmd/go/testdata/script/build_issue48319.txt @@ -4,6 +4,13 @@ [short] skip [!cgo] skip +# This test has problems when run on the LUCI darwin longtest builder, +# which uses a more contemporary Xcode version that is unfriendly to +# reproducible builds (see issue #64947 for the gory details). Note +# that individual developers running "go test cmd/go" on Darwin may +# still run into failures depending on their Xcode version. +[GOOS:darwin] [go-builder] skip + # This test is sensitive to cache invalidation, # so use a separate build cache that we can control. env GOCACHE=$WORK/gocache diff --git a/src/cmd/go/testdata/script/build_plugin_reproducible.txt b/src/cmd/go/testdata/script/build_plugin_reproducible.txt index 5369954..aa489df 100644 --- a/src/cmd/go/testdata/script/build_plugin_reproducible.txt +++ b/src/cmd/go/testdata/script/build_plugin_reproducible.txt @@ -1,6 +1,13 @@ [!buildmode:plugin] skip [short] skip +# This test has problems when run on the LUCI darwin longtest builder, +# which uses a more contemporary Xcode version that is unfriendly to +# reproducible builds (see issue #64947 for the gory details). Note +# that individual developers running "go test cmd/go" on Darwin may +# still run into failures depending on their Xcode version. +[GOOS:darwin] [go-builder] skip + go build -trimpath -buildvcs=false -buildmode=plugin -o a.so main.go go build -trimpath -buildvcs=false -buildmode=plugin -o b.so main.go cmp -q a.so b.so diff --git a/src/cmd/go/testdata/script/gotoolchain_issue66175.txt b/src/cmd/go/testdata/script/gotoolchain_issue66175.txt new file mode 100644 index 0000000..5db4dbf --- /dev/null +++ b/src/cmd/go/testdata/script/gotoolchain_issue66175.txt @@ -0,0 +1,109 @@ +env TESTGO_VERSION=go1.14 + +# Clear the path so this test doesn't fail if the system running it\ +# has a binary named go1.21 or go1.22 on its path. +[GOOS:plan9] env path= +[!GOOS:plan9] env PATH= + +# check for invalid toolchain in go.mod +go mod init m +go mod edit -go=1.14 -toolchain=go1.22 +! go version +stderr 'go: invalid toolchain: go1.22 is a language version but not a toolchain version \(go1.22.x\)' + +rm go.mod +go mod init m +go mod edit -go=1.14 -toolchain=go1.21 +! go version +stderr 'go: invalid toolchain: go1.21 is a language version but not a toolchain version \(go1.21.x\)' + +rm go.mod +go mod init m +go mod edit -go=1.14 -toolchain=go1.20 +! go version +stderr 'go: downloading go1.20 ' + + +# check for invalid GOTOOLCHAIN +env GOTOOLCHAIN=go1.14 +go version +stdout 'go1.14' + +env GOTOOLCHAIN=go1.20 +! go version +stderr 'go: downloading go1.20 ' + +env GOTOOLCHAIN=go1.21 +! go version +stderr 'go: invalid toolchain: go1.21 is a language version but not a toolchain version \(go1.21.x\)' + +env GOTOOLCHAIN=go1.22 +! go version +stderr 'go: invalid toolchain: go1.22 is a language version but not a toolchain version \(go1.22.x\)' + +env GOTOOLCHAIN=go1.20+auto +! go version +stderr 'go: downloading go1.20 ' + +env GOTOOLCHAIN=go1.21+auto +! go version +stderr 'go: invalid toolchain: go1.21 is a language version but not a toolchain version \(go1.21.x\)' + +env GOTOOLCHAIN=go1.22+auto +! go version +stderr 'go: invalid toolchain: go1.22 is a language version but not a toolchain version \(go1.22.x\)' + +env GOTOOLCHAIN=go1.21rc3 +! go version +stderr 'go: downloading go1.21rc3 ' + +env GOTOOLCHAIN=go1.22rc2 +! go version +stderr 'go: downloading go1.22rc2 ' + +env GOTOOLCHAIN=go1.66 +! go version +stderr 'go: invalid toolchain: go1.66 is a language version but not a toolchain version \(go1.66.x\)' + +env GOTOOLCHAIN=go1.18beta2 +! go version +stderr 'go: downloading go1.18beta2 ' + +# go1.X is okay for path lookups +env GOTOOLCHAIN=go1.20+path +! go version +stderr 'go: cannot find "go1.20" in PATH' + +env GOTOOLCHAIN=go1.21+path +! go version +stderr 'go: cannot find "go1.21" in PATH' + +env GOTOOLCHAIN=go1.22+path +! go version +stderr 'go: cannot find "go1.22" in PATH' + +# When a toolchain download takes place, download 1.X.0 +env GOTOOLCHAIN=auto +rm go.mod +go mod init m +go mod edit -go=1.300 -toolchain=none +! go version +stderr 'go: downloading go1.300.0 ' + +rm go.mod +go mod init m +go mod edit -go=1.21 -toolchain=none +! go version +stderr 'go: downloading go1.21.0 ' + +rm go.mod +go mod init m +go mod edit -go=1.22 -toolchain=none +! go version +stderr 'go: downloading go1.22.0 ' + +rm go.mod +go mod init m +go mod edit -go=1.15 -toolchain=none +! go version +stderr 'go: downloading go1.15 ' diff --git a/src/cmd/link/internal/ld/macho.go b/src/cmd/link/internal/ld/macho.go index fc38b0d..a36043c 100644 --- a/src/cmd/link/internal/ld/macho.go +++ b/src/cmd/link/internal/ld/macho.go @@ -901,21 +901,25 @@ func collectmachosyms(ctxt *Link) { // Add special runtime.text and runtime.etext symbols (which are local). // We've already included this symbol in Textp on darwin if ctxt.DynlinkingGo(). // See data.go:/textaddress + // NOTE: runtime.text.N symbols (if we split text sections) are not added, though, + // so we handle them here. if !*FlagS { if !ctxt.DynlinkingGo() { s := ldr.Lookup("runtime.text", 0) if ldr.SymType(s) == sym.STEXT { addsym(s) } - for n := range Segtext.Sections[1:] { - s := ldr.Lookup(fmt.Sprintf("runtime.text.%d", n+1), 0) - if s != 0 { - addsym(s) - } else { - break - } + } + for n := range Segtext.Sections[1:] { + s := ldr.Lookup(fmt.Sprintf("runtime.text.%d", n+1), 0) + if s != 0 { + addsym(s) + } else { + break } - s = ldr.Lookup("runtime.etext", 0) + } + if !ctxt.DynlinkingGo() { + s := ldr.Lookup("runtime.etext", 0) if ldr.SymType(s) == sym.STEXT { addsym(s) } diff --git a/src/crypto/x509/root_darwin_test.go b/src/crypto/x509/root_darwin_test.go deleted file mode 100644 index e6b52e9..0000000 --- a/src/crypto/x509/root_darwin_test.go +++ /dev/null @@ -1,131 +0,0 @@ -// Copyright 2013 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 x509_test - -import ( - "crypto/tls" - "crypto/x509" - "internal/testenv" - "testing" - "time" -) - -func TestPlatformVerifierLegacy(t *testing.T) { - // TODO(#52108): This can be removed once the synthetic test root is deployed on - // builders. - if !testenv.HasExternalNetwork() { - t.Skip() - } - - getChain := func(host string) []*x509.Certificate { - t.Helper() - c, err := tls.Dial("tcp", host+":443", &tls.Config{InsecureSkipVerify: true}) - if err != nil { - t.Fatalf("tls connection failed: %s", err) - } - return c.ConnectionState().PeerCertificates - } - - tests := []struct { - name string - host string - verifyName string - verifyTime time.Time - verifyEKU []x509.ExtKeyUsage - expectedErr string - skip string - }{ - { - // whatever google.com serves should, hopefully, be trusted - name: "valid chain", - host: "google.com", - }, - { - name: "expired leaf", - host: "expired.badssl.com", - expectedErr: "x509: certificate has expired or is not yet valid: “*.badssl.com” certificate is expired", - }, - { - name: "wrong host for leaf", - host: "wrong.host.badssl.com", - verifyName: "wrong.host.badssl.com", - expectedErr: "x509: certificate is valid for *.badssl.com, badssl.com, not wrong.host.badssl.com", - }, - { - name: "self-signed leaf", - host: "self-signed.badssl.com", - expectedErr: "x509: certificate signed by unknown authority", - }, - { - name: "untrusted root", - host: "untrusted-root.badssl.com", - expectedErr: "x509: certificate signed by unknown authority", - }, - { - name: "revoked leaf", - host: "revoked.badssl.com", - expectedErr: "x509: “revoked.badssl.com” certificate is revoked", - skip: "skipping; broken on recent versions of macOS. See issue 57428.", - }, - { - name: "leaf missing SCTs", - host: "no-sct.badssl.com", - expectedErr: "x509: “no-sct.badssl.com” certificate is not standards compliant", - skip: "skipping; broken on recent versions of macOS. See issue 57428.", - }, - { - name: "expired leaf (custom time)", - host: "google.com", - verifyTime: time.Time{}.Add(time.Hour), - expectedErr: "x509: certificate has expired or is not yet valid: “*.google.com” certificate is expired", - }, - { - name: "valid chain (custom time)", - host: "google.com", - verifyTime: time.Now(), - }, - { - name: "leaf doesn't have acceptable ExtKeyUsage", - host: "google.com", - expectedErr: "x509: certificate specifies an incompatible key usage", - verifyEKU: []x509.ExtKeyUsage{x509.ExtKeyUsageEmailProtection}, - }, - } - - for _, tc := range tests { - t.Run(tc.name, func(t *testing.T) { - if tc.skip != "" { - t.Skip(tc.skip) - } - - chain := getChain(tc.host) - var opts x509.VerifyOptions - if len(chain) > 1 { - opts.Intermediates = x509.NewCertPool() - for _, c := range chain[1:] { - opts.Intermediates.AddCert(c) - } - } - if tc.verifyName != "" { - opts.DNSName = tc.verifyName - } - if !tc.verifyTime.IsZero() { - opts.CurrentTime = tc.verifyTime - } - if len(tc.verifyEKU) > 0 { - opts.KeyUsages = tc.verifyEKU - } - - _, err := chain[0].Verify(opts) - if err != nil && tc.expectedErr == "" { - t.Errorf("unexpected verification error: %s", err) - } else if err != nil && err.Error() != tc.expectedErr { - t.Errorf("unexpected verification error: got %q, want %q", err.Error(), tc.expectedErr) - } else if err == nil && tc.expectedErr != "" { - t.Errorf("unexpected verification success: want %q", tc.expectedErr) - } - }) - } -} diff --git a/src/crypto/x509/root_windows_test.go b/src/crypto/x509/root_windows_test.go deleted file mode 100644 index 1372c04..0000000 --- a/src/crypto/x509/root_windows_test.go +++ /dev/null @@ -1,127 +0,0 @@ -// Copyright 2021 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 x509_test - -import ( - "crypto/tls" - "crypto/x509" - "errors" - "internal/testenv" - "net" - "strings" - "syscall" - "testing" - "time" -) - -func TestPlatformVerifierLegacy(t *testing.T) { - // TODO(#52108): This can be removed once the synthetic test root is deployed on - // builders. - if !testenv.HasExternalNetwork() { - t.Skip() - } - - getChain := func(t *testing.T, host string) []*x509.Certificate { - t.Helper() - c, err := tls.Dial("tcp", host+":443", &tls.Config{InsecureSkipVerify: true}) - if err != nil { - // From https://docs.microsoft.com/en-us/windows/win32/winsock/windows-sockets-error-codes-2, - // matching the error string observed in https://go.dev/issue/52094. - const WSATRY_AGAIN syscall.Errno = 11002 - var errDNS *net.DNSError - if strings.HasSuffix(host, ".badssl.com") && errors.As(err, &errDNS) && strings.HasSuffix(errDNS.Err, WSATRY_AGAIN.Error()) { - t.Log(err) - testenv.SkipFlaky(t, 52094) - } - - t.Fatalf("tls connection failed: %s", err) - } - return c.ConnectionState().PeerCertificates - } - - tests := []struct { - name string - host string - verifyName string - verifyTime time.Time - expectedErr string - }{ - { - // whatever google.com serves should, hopefully, be trusted - name: "valid chain", - host: "google.com", - }, - { - name: "valid chain (dns check)", - host: "google.com", - verifyName: "google.com", - }, - { - name: "valid chain (fqdn dns check)", - host: "google.com.", - verifyName: "google.com.", - }, - { - name: "expired leaf", - host: "expired.badssl.com", - expectedErr: "x509: certificate has expired or is not yet valid: ", - }, - { - name: "wrong host for leaf", - host: "wrong.host.badssl.com", - verifyName: "wrong.host.badssl.com", - expectedErr: "x509: certificate is valid for *.badssl.com, badssl.com, not wrong.host.badssl.com", - }, - { - name: "self-signed leaf", - host: "self-signed.badssl.com", - expectedErr: "x509: certificate signed by unknown authority", - }, - { - name: "untrusted root", - host: "untrusted-root.badssl.com", - expectedErr: "x509: certificate signed by unknown authority", - }, - { - name: "expired leaf (custom time)", - host: "google.com", - verifyTime: time.Time{}.Add(time.Hour), - expectedErr: "x509: certificate has expired or is not yet valid: ", - }, - { - name: "valid chain (custom time)", - host: "google.com", - verifyTime: time.Now(), - }, - } - - for _, tc := range tests { - t.Run(tc.name, func(t *testing.T) { - chain := getChain(t, tc.host) - var opts x509.VerifyOptions - if len(chain) > 1 { - opts.Intermediates = x509.NewCertPool() - for _, c := range chain[1:] { - opts.Intermediates.AddCert(c) - } - } - if tc.verifyName != "" { - opts.DNSName = tc.verifyName - } - if !tc.verifyTime.IsZero() { - opts.CurrentTime = tc.verifyTime - } - - _, err := chain[0].Verify(opts) - if err != nil && tc.expectedErr == "" { - t.Errorf("unexpected verification error: %s", err) - } else if err != nil && err.Error() != tc.expectedErr { - t.Errorf("unexpected verification error: got %q, want %q", err.Error(), tc.expectedErr) - } else if err == nil && tc.expectedErr != "" { - t.Errorf("unexpected verification success: want %q", tc.expectedErr) - } - }) - } -} diff --git a/src/net/netip/inlining_test.go b/src/net/netip/inlining_test.go index b521eee..98584b0 100644 --- a/src/net/netip/inlining_test.go +++ b/src/net/netip/inlining_test.go @@ -36,8 +36,6 @@ func TestInlining(t *testing.T) { "Addr.Is4", "Addr.Is4In6", "Addr.Is6", - "Addr.IsLoopback", - "Addr.IsMulticast", "Addr.IsInterfaceLocalMulticast", "Addr.IsValid", "Addr.IsUnspecified", diff --git a/src/net/netip/netip.go b/src/net/netip/netip.go index 7a189e8..92cb57e 100644 --- a/src/net/netip/netip.go +++ b/src/net/netip/netip.go @@ -508,6 +508,10 @@ func (ip Addr) hasZone() bool { // IsLinkLocalUnicast reports whether ip is a link-local unicast address. func (ip Addr) IsLinkLocalUnicast() bool { + if ip.Is4In6() { + ip = ip.Unmap() + } + // Dynamic Configuration of IPv4 Link-Local Addresses // https://datatracker.ietf.org/doc/html/rfc3927#section-2.1 if ip.Is4() { @@ -523,6 +527,10 @@ func (ip Addr) IsLinkLocalUnicast() bool { // IsLoopback reports whether ip is a loopback address. func (ip Addr) IsLoopback() bool { + if ip.Is4In6() { + ip = ip.Unmap() + } + // Requirements for Internet Hosts -- Communication Layers (3.2.1.3 Addressing) // https://datatracker.ietf.org/doc/html/rfc1122#section-3.2.1.3 if ip.Is4() { @@ -538,6 +546,10 @@ func (ip Addr) IsLoopback() bool { // IsMulticast reports whether ip is a multicast address. func (ip Addr) IsMulticast() bool { + if ip.Is4In6() { + ip = ip.Unmap() + } + // Host Extensions for IP Multicasting (4. HOST GROUP ADDRESSES) // https://datatracker.ietf.org/doc/html/rfc1112#section-4 if ip.Is4() { @@ -556,7 +568,7 @@ func (ip Addr) IsMulticast() bool { func (ip Addr) IsInterfaceLocalMulticast() bool { // IPv6 Addressing Architecture (2.7.1. Pre-Defined Multicast Addresses) // https://datatracker.ietf.org/doc/html/rfc4291#section-2.7.1 - if ip.Is6() { + if ip.Is6() && !ip.Is4In6() { return ip.v6u16(0)&0xff0f == 0xff01 } return false // zero value @@ -564,6 +576,10 @@ func (ip Addr) IsInterfaceLocalMulticast() bool { // IsLinkLocalMulticast reports whether ip is a link-local multicast address. func (ip Addr) IsLinkLocalMulticast() bool { + if ip.Is4In6() { + ip = ip.Unmap() + } + // IPv4 Multicast Guidelines (4. Local Network Control Block (224.0.0/24)) // https://datatracker.ietf.org/doc/html/rfc5771#section-4 if ip.Is4() { @@ -592,6 +608,10 @@ func (ip Addr) IsGlobalUnicast() bool { return false } + if ip.Is4In6() { + ip = ip.Unmap() + } + // Match package net's IsGlobalUnicast logic. Notably private IPv4 addresses // and ULA IPv6 addresses are still considered "global unicast". if ip.Is4() && (ip == IPv4Unspecified() || ip == AddrFrom4([4]byte{255, 255, 255, 255})) { @@ -609,6 +629,10 @@ func (ip Addr) IsGlobalUnicast() bool { // ip is in 10.0.0.0/8, 172.16.0.0/12, 192.168.0.0/16, or fc00::/7. This is the // same as [net.IP.IsPrivate]. func (ip Addr) IsPrivate() bool { + if ip.Is4In6() { + ip = ip.Unmap() + } + // Match the stdlib's IsPrivate logic. if ip.Is4() { // RFC 1918 allocates 10.0.0.0/8, 172.16.0.0/12, and 192.168.0.0/16 as diff --git a/src/net/netip/netip_test.go b/src/net/netip/netip_test.go index a748ac3..56a6c7d 100644 --- a/src/net/netip/netip_test.go +++ b/src/net/netip/netip_test.go @@ -591,10 +591,13 @@ func TestIPProperties(t *testing.T) { ilm6 = mustIP("ff01::1") ilmZone6 = mustIP("ff01::1%eth0") - private4a = mustIP("10.0.0.1") - private4b = mustIP("172.16.0.1") - private4c = mustIP("192.168.1.1") - private6 = mustIP("fd00::1") + private4a = mustIP("10.0.0.1") + private4b = mustIP("172.16.0.1") + private4c = mustIP("192.168.1.1") + private6 = mustIP("fd00::1") + private6mapped4a = mustIP("::ffff:10.0.0.1") + private6mapped4b = mustIP("::ffff:172.16.0.1") + private6mapped4c = mustIP("::ffff:192.168.1.1") ) tests := []struct { @@ -619,6 +622,11 @@ func TestIPProperties(t *testing.T) { globalUnicast: true, }, { + name: "unicast v6 mapped v4Addr", + ip: AddrFrom16(unicast4.As16()), + globalUnicast: true, + }, + { name: "unicast v6Addr", ip: unicast6, globalUnicast: true, @@ -640,6 +648,12 @@ func TestIPProperties(t *testing.T) { multicast: true, }, { + name: "multicast v6 mapped v4Addr", + ip: AddrFrom16(multicast4.As16()), + linkLocalMulticast: true, + multicast: true, + }, + { name: "multicast v6Addr", ip: multicast6, linkLocalMulticast: true, @@ -657,6 +671,11 @@ func TestIPProperties(t *testing.T) { linkLocalUnicast: true, }, { + name: "link-local unicast v6 mapped v4Addr", + ip: AddrFrom16(llu4.As16()), + linkLocalUnicast: true, + }, + { name: "link-local unicast v6Addr", ip: llu6, linkLocalUnicast: true, @@ -682,6 +701,11 @@ func TestIPProperties(t *testing.T) { loopback: true, }, { + name: "loopback v6 mapped v4Addr", + ip: AddrFrom16(IPv6Loopback().As16()), + loopback: true, + }, + { name: "interface-local multicast v6Addr", ip: ilm6, interfaceLocalMulticast: true, @@ -718,6 +742,24 @@ func TestIPProperties(t *testing.T) { private: true, }, { + name: "private v6 mapped v4Addr 10/8", + ip: private6mapped4a, + globalUnicast: true, + private: true, + }, + { + name: "private v6 mapped v4Addr 172.16/12", + ip: private6mapped4b, + globalUnicast: true, + private: true, + }, + { + name: "private v6 mapped v4Addr 192.168/16", + ip: private6mapped4c, + globalUnicast: true, + private: true, + }, + { name: "unspecified v4Addr", ip: IPv4Unspecified(), unspecified: true, diff --git a/src/os/file_unix.go b/src/os/file_unix.go index a527b23..649ec3e 100644 --- a/src/os/file_unix.go +++ b/src/os/file_unix.go @@ -157,7 +157,7 @@ const ( kindNonBlock // kindNoPoll means that we should not put the descriptor into // non-blocking mode, because we know it is not a pipe or FIFO. - // Used by openFdAt for directories. + // Used by openDirAt for directories. kindNoPoll ) @@ -256,7 +256,7 @@ func epipecheck(file *File, e error) { const DevNull = "/dev/null" // openFileNolog is the Unix implementation of OpenFile. -// Changes here should be reflected in openFdAt, if relevant. +// Changes here should be reflected in openDirAt, if relevant. func openFileNolog(name string, flag int, perm FileMode) (*File, error) { setSticky := false if !supportsCreateWithStickyBit && flag&O_CREATE != 0 && perm&ModeSticky != 0 { diff --git a/src/os/removeall_at.go b/src/os/removeall_at.go index 8ea5df4..774ca15 100644 --- a/src/os/removeall_at.go +++ b/src/os/removeall_at.go @@ -74,22 +74,7 @@ func removeAllFrom(parent *File, base string) error { if err != syscall.EISDIR && err != syscall.EPERM && err != syscall.EACCES { return &PathError{Op: "unlinkat", Path: base, Err: err} } - - // Is this a directory we need to recurse into? - var statInfo syscall.Stat_t - statErr := ignoringEINTR(func() error { - return unix.Fstatat(parentFd, base, &statInfo, unix.AT_SYMLINK_NOFOLLOW) - }) - if statErr != nil { - if IsNotExist(statErr) { - return nil - } - return &PathError{Op: "fstatat", Path: base, Err: statErr} - } - if statInfo.Mode&syscall.S_IFMT != syscall.S_IFDIR { - // Not a directory; return the error from the unix.Unlinkat. - return &PathError{Op: "unlinkat", Path: base, Err: err} - } + uErr := err // Remove the directory's entries. var recurseErr error @@ -98,11 +83,15 @@ func removeAllFrom(parent *File, base string) error { var respSize int // Open the directory to recurse into - file, err := openFdAt(parentFd, base) + file, err := openDirAt(parentFd, base) if err != nil { if IsNotExist(err) { return nil } + if err == syscall.ENOTDIR { + // Not a directory; return the error from the unix.Unlinkat. + return &PathError{Op: "unlinkat", Path: base, Err: uErr} + } recurseErr = &PathError{Op: "openfdat", Path: base, Err: err} break } @@ -168,16 +157,19 @@ func removeAllFrom(parent *File, base string) error { return &PathError{Op: "unlinkat", Path: base, Err: unlinkError} } -// openFdAt opens path relative to the directory in fd. -// Other than that this should act like openFileNolog. +// openDirAt opens a directory name relative to the directory referred to by +// the file descriptor dirfd. If name is anything but a directory (this +// includes a symlink to one), it should return an error. Other than that this +// should act like openFileNolog. +// // This acts like openFileNolog rather than OpenFile because // we are going to (try to) remove the file. // The contents of this file are not relevant for test caching. -func openFdAt(dirfd int, name string) (*File, error) { +func openDirAt(dirfd int, name string) (*File, error) { var r int for { var e error - r, e = unix.Openat(dirfd, name, O_RDONLY|syscall.O_CLOEXEC, 0) + r, e = unix.Openat(dirfd, name, O_RDONLY|syscall.O_CLOEXEC|syscall.O_DIRECTORY|syscall.O_NOFOLLOW, 0) if e == nil { break } diff --git a/src/runtime/cgo/gcc_stack_unix.c b/src/runtime/cgo/gcc_stack_unix.c index f3fead9..98a75a1 100644 --- a/src/runtime/cgo/gcc_stack_unix.c +++ b/src/runtime/cgo/gcc_stack_unix.c @@ -18,6 +18,9 @@ x_cgo_getstackbound(uintptr bounds[2]) void *addr; size_t size; + // Needed before pthread_getattr_np, too, since before glibc 2.32 + // it did not call pthread_attr_init in all cases (see #65625). + pthread_attr_init(&attr); #if defined(__GLIBC__) || (defined(__sun) && !defined(__illumos__)) // pthread_getattr_np is a GNU extension supported in glibc. // Solaris is not glibc but does support pthread_getattr_np @@ -25,11 +28,9 @@ x_cgo_getstackbound(uintptr bounds[2]) pthread_getattr_np(pthread_self(), &attr); // GNU extension pthread_attr_getstack(&attr, &addr, &size); // low address #elif defined(__illumos__) - pthread_attr_init(&attr); pthread_attr_get_np(pthread_self(), &attr); pthread_attr_getstack(&attr, &addr, &size); // low address #else - pthread_attr_init(&attr); pthread_attr_getstacksize(&attr, &size); addr = __builtin_frame_address(0) + 4096 - size; #endif diff --git a/src/runtime/metrics_test.go b/src/runtime/metrics_test.go index d7f4133..5866107 100644 --- a/src/runtime/metrics_test.go +++ b/src/runtime/metrics_test.go @@ -1290,3 +1290,38 @@ func (w *contentionWorker) run() { for w.fn() { } } + +func TestMetricHeapUnusedLargeObjectOverflow(t *testing.T) { + // This test makes sure /memory/classes/heap/unused:bytes + // doesn't overflow when allocating and deallocating large + // objects. It is a regression test for #67019. + done := make(chan struct{}) + var wg sync.WaitGroup + wg.Add(1) + go func() { + defer wg.Done() + for { + for i := 0; i < 10; i++ { + runtime.Escape(make([]byte, 1<<20)) + } + runtime.GC() + select { + case <-done: + return + default: + } + } + }() + s := []metrics.Sample{ + {Name: "/memory/classes/heap/unused:bytes"}, + } + for i := 0; i < 1000; i++ { + metrics.Read(s) + if s[0].Value.Uint64() > 1<<40 { + t.Errorf("overflow") + break + } + } + done <- struct{}{} + wg.Wait() +} diff --git a/src/runtime/mgcsweep.go b/src/runtime/mgcsweep.go index 3dbe9bc..35be794 100644 --- a/src/runtime/mgcsweep.go +++ b/src/runtime/mgcsweep.go @@ -770,6 +770,19 @@ func (sl *sweepLocked) sweep(preserve bool) bool { if nfreed != 0 { // Free large object span to heap. + // Count the free in the consistent, external stats. + // + // Do this before freeSpan, which might update heapStats' inHeap + // value. If it does so, then metrics that subtract object footprint + // from inHeap might overflow. See #67019. + stats := memstats.heapStats.acquire() + atomic.Xadd64(&stats.largeFreeCount, 1) + atomic.Xadd64(&stats.largeFree, int64(size)) + memstats.heapStats.release() + + // Count the free in the inconsistent, internal stats. + gcController.totalFree.Add(int64(size)) + // NOTE(rsc,dvyukov): The original implementation of efence // in CL 22060046 used sysFree instead of sysFault, so that // the operating system would eventually give the memory @@ -802,16 +815,6 @@ func (sl *sweepLocked) sweep(preserve bool) bool { // invalid pointer. See arena.go:(*mheap).allocUserArenaChunk. *(*uintptr)(unsafe.Pointer(&s.largeType)) = 0 } - - // Count the free in the consistent, external stats. - stats := memstats.heapStats.acquire() - atomic.Xadd64(&stats.largeFreeCount, 1) - atomic.Xadd64(&stats.largeFree, int64(size)) - memstats.heapStats.release() - - // Count the free in the inconsistent, internal stats. - gcController.totalFree.Add(int64(size)) - return true } diff --git a/src/syscall/zerrors_solaris_amd64.go b/src/syscall/zerrors_solaris_amd64.go index 4a1d9c3..b2c81d9 100644 --- a/src/syscall/zerrors_solaris_amd64.go +++ b/src/syscall/zerrors_solaris_amd64.go @@ -634,6 +634,7 @@ const ( O_APPEND = 0x8 O_CLOEXEC = 0x800000 O_CREAT = 0x100 + O_DIRECTORY = 0x1000000 O_DSYNC = 0x40 O_EXCL = 0x400 O_EXEC = 0x400000 diff --git a/test/fixedbugs/issue67141.go b/test/fixedbugs/issue67141.go new file mode 100644 index 0000000..0464d1f --- /dev/null +++ b/test/fixedbugs/issue67141.go @@ -0,0 +1,15 @@ +// errorcheck -lang=go1.22 + +//go:build go1.21 + +// We need a line directive before the package clause, +// but don't change file name or position so that the +// error message appears at the right place. + +//line issue67141.go:10 +package p + +func _() { + for range 10 { // ERROR "cannot range over 10" + } +} diff --git a/test/fixedbugs/issue67160.go b/test/fixedbugs/issue67160.go new file mode 100644 index 0000000..be45a61 --- /dev/null +++ b/test/fixedbugs/issue67160.go @@ -0,0 +1,32 @@ +// run + +// Copyright 2024 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Test to make sure that we don't try using larger loads for +// generated equality functions on architectures that can't do +// unaligned loads. + +package main + +// T has a big field that wants to be compared with larger loads/stores. +// T is "special" because of the unnamed field, so it needs a generated equality function. +// T is an odd number of bytes in size and has alignment 1. +type T struct { + src [8]byte + _ byte +} + +// U contains 8 copies of T, each at a different %8 alignment. +type U [8]T + +//go:noinline +func f(x, y *U) bool { + return *x == *y +} + +func main() { + var a U + _ = f(&a, &a) +} diff --git a/test/fixedbugs/issue67255.go b/test/fixedbugs/issue67255.go new file mode 100644 index 0000000..7ca7a23 --- /dev/null +++ b/test/fixedbugs/issue67255.go @@ -0,0 +1,33 @@ +// run + +// Copyright 2024 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package main + +var zero int + +var sink any + +func main() { + var objs [][]*byte + for i := 10; i < 200; i++ { + // The objects we're allocating here are pointer-ful. Some will + // max out their size class, which are the ones we want. + // We also allocate from small to large, so that the object which + // maxes out its size class is the last one allocated in that class. + // This allocation pattern leaves the next object in the class + // unallocated, which we need to reproduce the bug. + objs = append(objs, make([]*byte, i)) + } + sink = objs // force heap allocation + + // Bug will happen as soon as the write barrier turns on. + for range 10000 { + sink = make([]*byte, 1024) + for _, s := range objs { + s = append(s, make([]*byte, zero)...) + } + } +} |