diff --git a/VERSION b/VERSION index 4d7d2d8b35d185..33dc5adf643ee0 100644 --- a/VERSION +++ b/VERSION @@ -1,2 +1,2 @@ -go1.22.4 -time 2024-05-30T19:26:07Z +go1.22.5 +time 2024-06-27T20:11:12Z diff --git a/doc/godebug.md b/doc/godebug.md index a7619c9a3da48a..fb3f32f4424907 100644 --- a/doc/godebug.md +++ b/doc/godebug.md @@ -248,6 +248,13 @@ Go 1.19 made it an error for path lookups to resolve to binaries in the current controlled by the [`execerrdot` setting](/pkg/os/exec#hdr-Executables_in_the_current_directory). There is no plan to remove this setting. +Go 1.19 started sending EDNS0 additional headers on DNS requests. +This can reportedly break the DNS server provided on some routers, +such as CenturyLink Zyxel C3000Z. +This can be changed by the [`netedns0` setting](/pkg/net#hdr-Name_Resolution). +This setting is available in Go 1.21.12, Go 1.22.5, Go 1.23, and later. +There is no plan to remove this setting. + ### Go 1.18 Go 1.18 removed support for SHA1 in most X.509 certificates, diff --git a/src/cmd/cgo/internal/swig/swig_test.go b/src/cmd/cgo/internal/swig/swig_test.go index 41563138a7b3d8..923378b2dd8fb0 100644 --- a/src/cmd/cgo/internal/swig/swig_test.go +++ b/src/cmd/cgo/internal/swig/swig_test.go @@ -44,11 +44,18 @@ func run(t *testing.T, dir string, lto bool, args ...string) { cmd := exec.Command("go", runArgs...) cmd.Dir = dir if lto { + // On the builders we're using the default /usr/bin/ld, but + // that has problems when asking for LTO in particular. Force + // use of lld, which ships with our clang installation. + extraLDFlags := "" + if strings.Contains(testenv.Builder(), "clang") { + extraLDFlags += " -fuse-ld=lld" + } const cflags = "-flto -Wno-lto-type-mismatch -Wno-unknown-warning-option" cmd.Env = append(cmd.Environ(), "CGO_CFLAGS="+cflags, "CGO_CXXFLAGS="+cflags, - "CGO_LDFLAGS="+cflags) + "CGO_LDFLAGS="+cflags+extraLDFlags) } out, err := cmd.CombinedOutput() if string(out) != "OK\n" { diff --git a/src/cmd/cgo/internal/testplugin/plugin_test.go b/src/cmd/cgo/internal/testplugin/plugin_test.go index 4900ada1820156..85dfd31123b5de 100644 --- a/src/cmd/cgo/internal/testplugin/plugin_test.go +++ b/src/cmd/cgo/internal/testplugin/plugin_test.go @@ -414,3 +414,11 @@ func TestTextSectionSplit(t *testing.T) { t.Errorf("runtime.text.1 not found, text section not split?") } } + +func TestIssue67976(t *testing.T) { + // Issue 67976: build failure with loading a dynimport variable (the runtime/pprof + // package does this on darwin) in a plugin on darwin/amd64. + // The test program uses runtime/pprof in a plugin. + globalSkip(t) + goCmd(t, "build", "-buildmode=plugin", "-o", "issue67976.so", "./issue67976/plugin.go") +} diff --git a/src/cmd/cgo/internal/testplugin/testdata/issue67976/plugin.go b/src/cmd/cgo/internal/testplugin/testdata/issue67976/plugin.go new file mode 100644 index 00000000000000..502ecc5c4750f4 --- /dev/null +++ b/src/cmd/cgo/internal/testplugin/testdata/issue67976/plugin.go @@ -0,0 +1,16 @@ +// 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 + +import ( + "io" + "runtime/pprof" +) + +func main() {} + +func Start() { + pprof.StartCPUProfile(io.Discard) +} diff --git a/src/cmd/cgo/internal/testsanitizers/libfuzzer_test.go b/src/cmd/cgo/internal/testsanitizers/libfuzzer_test.go index 3f5b1d91c786e0..85c8f7bbfbedda 100644 --- a/src/cmd/cgo/internal/testsanitizers/libfuzzer_test.go +++ b/src/cmd/cgo/internal/testsanitizers/libfuzzer_test.go @@ -13,8 +13,13 @@ import ( ) func TestLibFuzzer(t *testing.T) { + // Skip tests in short mode. + if testing.Short() { + t.Skip("libfuzzer tests can take upwards of minutes to run; skipping in short mode") + } testenv.MustHaveGoBuild(t) testenv.MustHaveCGO(t) + goos, err := goEnv("GOOS") if err != nil { t.Fatal(err) diff --git a/src/cmd/compile/internal/types/type.go b/src/cmd/compile/internal/types/type.go index 2777b4f007318b..c2b0ca3a4458c2 100644 --- a/src/cmd/compile/internal/types/type.go +++ b/src/cmd/compile/internal/types/type.go @@ -657,6 +657,9 @@ func NewPtr(elem *Type) *Type { if elem.HasShape() { t.SetHasShape(true) } + if elem.Noalg() { + t.SetNoalg(true) + } return t } diff --git a/src/cmd/compile/internal/types2/initorder.go b/src/cmd/compile/internal/types2/initorder.go index 6e041721e86ed6..841b725b17160a 100644 --- a/src/cmd/compile/internal/types2/initorder.go +++ b/src/cmd/compile/internal/types2/initorder.go @@ -310,6 +310,14 @@ func (a nodeQueue) Swap(i, j int) { func (a nodeQueue) Less(i, j int) bool { x, y := a[i], a[j] + + // Prioritize all constants before non-constants. See go.dev/issue/66575/. + _, xConst := x.obj.(*Const) + _, yConst := y.obj.(*Const) + if xConst != yConst { + return xConst + } + // nodes are prioritized by number of incoming dependencies (1st key) // and source order (2nd key) return x.ndeps < y.ndeps || x.ndeps == y.ndeps && x.obj.order() < y.obj.order() diff --git a/src/cmd/compile/internal/types2/stmt.go b/src/cmd/compile/internal/types2/stmt.go index c9713dac6fe7ff..d519657b6b153e 100644 --- a/src/cmd/compile/internal/types2/stmt.go +++ b/src/cmd/compile/internal/types2/stmt.go @@ -902,7 +902,7 @@ func (check *Checker) rangeStmt(inner stmtContext, s *syntax.ForStmt, rclause *s lhs := [2]Expr{sKey, sValue} // sKey, sValue may be nil rhs := [2]Type{key, val} // key, val may be nil - constIntRange := x.mode == constant_ && isInteger(x.typ) + rangeOverInt := isInteger(x.typ) if isDef { // short variable declaration @@ -927,19 +927,27 @@ func (check *Checker) rangeStmt(inner stmtContext, s *syntax.ForStmt, rclause *s check.errorf(lhs, InvalidSyntaxTree, "cannot declare %s", lhs) obj = NewVar(lhs.Pos(), check.pkg, "_", nil) // dummy variable } + assert(obj.typ == nil) - // initialize lhs variable - if constIntRange { - check.initVar(obj, &x, "range clause") - } else if typ := rhs[i]; typ != nil { - x.mode = value - x.expr = lhs // we don't have a better rhs expression to use here - x.typ = typ - check.initVar(obj, &x, "assignment") // error is on variable, use "assignment" not "range clause" - } else { + // initialize lhs iteration variable, if any + typ := rhs[i] + if typ == nil { obj.typ = Typ[Invalid] obj.used = true // don't complain about unused variable + continue + } + + if rangeOverInt { + assert(i == 0) // at most one iteration variable (rhs[1] == nil for rangeOverInt) + check.initVar(obj, &x, "range clause") + } else { + var y operand + y.mode = value + y.expr = lhs // we don't have a better rhs expression to use here + y.typ = typ + check.initVar(obj, &y, "assignment") // error is on variable, use "assignment" not "range clause" } + assert(obj.typ != nil) } // declare variables @@ -958,21 +966,36 @@ func (check *Checker) rangeStmt(inner stmtContext, s *syntax.ForStmt, rclause *s continue } - if constIntRange { + // assign to lhs iteration variable, if any + typ := rhs[i] + if typ == nil { + continue + } + + if rangeOverInt { + assert(i == 0) // at most one iteration variable (rhs[1] == nil for rangeOverInt) check.assignVar(lhs, nil, &x, "range clause") - } else if typ := rhs[i]; typ != nil { - x.mode = value - x.expr = lhs // we don't have a better rhs expression to use here - x.typ = typ - check.assignVar(lhs, nil, &x, "assignment") // error is on variable, use "assignment" not "range clause" + // If the assignment succeeded, if x was untyped before, it now + // has a type inferred via the assignment. It must be an integer. + // (go.dev/issues/67027) + if x.mode != invalid && !isInteger(x.typ) { + check.softErrorf(lhs, InvalidRangeExpr, "cannot use iteration variable of type %s", x.typ) + } + } else { + var y operand + y.mode = value + y.expr = lhs // we don't have a better rhs expression to use here + y.typ = typ + check.assignVar(lhs, nil, &y, "assignment") // error is on variable, use "assignment" not "range clause" } } - } else if constIntRange { + } else if rangeOverInt { // If we don't have any iteration variables, we still need to // check that a (possibly untyped) integer range expression x // is valid. // We do this by checking the assignment _ = x. This ensures - // that an untyped x can be converted to a value of type int. + // that an untyped x can be converted to a value of its default + // type (rune or int). check.assignment(&x, nil, "range clause") } diff --git a/src/cmd/go/internal/modload/modfile.go b/src/cmd/go/internal/modload/modfile.go index 899f1b3d09aca5..1d6b28db19ebd6 100644 --- a/src/cmd/go/internal/modload/modfile.go +++ b/src/cmd/go/internal/modload/modfile.go @@ -190,7 +190,7 @@ func CheckRetractions(ctx context.Context, m module.Version) (err error) { return err } summary, err := rawGoModSummary(rm) - if err != nil { + if err != nil && !errors.Is(err, gover.ErrTooNew) { return err } @@ -298,7 +298,7 @@ func CheckDeprecation(ctx context.Context, m module.Version) (deprecation string return "", err } summary, err := rawGoModSummary(latest) - if err != nil { + if err != nil && !errors.Is(err, gover.ErrTooNew) { return "", err } return summary.deprecated, nil @@ -644,6 +644,8 @@ func goModSummary(m module.Version) (*modFileSummary, error) { // its dependencies. // // rawGoModSummary cannot be used on the main module outside of workspace mode. +// The modFileSummary can still be used for retractions and deprecations +// even if a TooNewError is returned. func rawGoModSummary(m module.Version) (*modFileSummary, error) { if gover.IsToolchain(m.Path) { if m.Path == "go" && gover.Compare(m.Version, gover.GoStrictVersion) >= 0 { @@ -698,12 +700,7 @@ func rawGoModSummary(m module.Version) (*modFileSummary, error) { summary.require = append(summary.require, req.Mod) } } - if summary.goVersion != "" && gover.Compare(summary.goVersion, gover.GoStrictVersion) >= 0 { - if gover.Compare(summary.goVersion, gover.Local()) > 0 { - return nil, &gover.TooNewError{What: "module " + m.String(), GoVersion: summary.goVersion} - } - summary.require = append(summary.require, module.Version{Path: "go", Version: summary.goVersion}) - } + if len(f.Retract) > 0 { summary.retract = make([]retraction, 0, len(f.Retract)) for _, ret := range f.Retract { @@ -714,6 +711,16 @@ func rawGoModSummary(m module.Version) (*modFileSummary, error) { } } + // This block must be kept at the end of the function because the summary may + // be used for reading retractions or deprecations even if a TooNewError is + // returned. + if summary.goVersion != "" && gover.Compare(summary.goVersion, gover.GoStrictVersion) >= 0 { + summary.require = append(summary.require, module.Version{Path: "go", Version: summary.goVersion}) + if gover.Compare(summary.goVersion, gover.Local()) > 0 { + return summary, &gover.TooNewError{What: "module " + m.String(), GoVersion: summary.goVersion} + } + } + return summary, nil }) } diff --git a/src/cmd/go/testdata/mod/example.com_retract_newergoversion_v1.0.0.txt b/src/cmd/go/testdata/mod/example.com_retract_newergoversion_v1.0.0.txt new file mode 100644 index 00000000000000..21d53529842e8e --- /dev/null +++ b/src/cmd/go/testdata/mod/example.com_retract_newergoversion_v1.0.0.txt @@ -0,0 +1,10 @@ +-- .mod -- +module example.com/retract/newergoversion + +go 1.21 + +-- .info -- +{"Version":"v1.0.0"} + +-- retract.go -- +package newergoversion \ No newline at end of file diff --git a/src/cmd/go/testdata/mod/example.com_retract_newergoversion_v1.2.0.txt b/src/cmd/go/testdata/mod/example.com_retract_newergoversion_v1.2.0.txt new file mode 100644 index 00000000000000..7aa28b90e3ab9b --- /dev/null +++ b/src/cmd/go/testdata/mod/example.com_retract_newergoversion_v1.2.0.txt @@ -0,0 +1,12 @@ +-- .mod -- +module example.com/retract/newergoversion + +go 1.23 + +retract v1.2.0 + +-- .info -- +{"Version":"v1.2.0"} + +-- retract.go -- +package newergoversion diff --git a/src/cmd/go/testdata/script/list_retractions_issue66403.txt b/src/cmd/go/testdata/script/list_retractions_issue66403.txt new file mode 100644 index 00000000000000..717d129d4cd4fa --- /dev/null +++ b/src/cmd/go/testdata/script/list_retractions_issue66403.txt @@ -0,0 +1,20 @@ +# For issue #66403, go list -u -m all should not fail if a module +# with retractions has a newer version. + +env TESTGO_VERSION=go1.21 +env TESTGO_VERSION_SWITCH=switch +go list -u -m example.com/retract/newergoversion +stdout 'example.com/retract/newergoversion v1.0.0' +! stdout 'v1.2.0' + +-- go.mod -- +module example.com/m + +go 1.22 + +require example.com/retract/newergoversion v1.0.0 + +-- main.go -- +package main + +import _ "example.com/retract/newergoversion" \ No newline at end of file diff --git a/src/cmd/go/testdata/script/test_fuzz_cgo.txt b/src/cmd/go/testdata/script/test_fuzz_cgo.txt new file mode 100644 index 00000000000000..1a0487700da7b2 --- /dev/null +++ b/src/cmd/go/testdata/script/test_fuzz_cgo.txt @@ -0,0 +1,28 @@ +[!fuzz] skip +[!cgo] skip +[short] skip +env GOCACHE=$WORK/cache + +# Test that fuzzing works with cgo (issue 65169) + +go test -fuzz=. -fuzztime=1x +stdout ok +! stdout FAIL + +-- go.mod -- +module example.com/p + +go 1.20 +-- c.go -- +package p + +import "C" +-- c_test.go -- +package p + +import "testing" + +func Fuzz(f *testing.F) { + f.Add(0) + f.Fuzz(func(t *testing.T, x int) {}) +} diff --git a/src/cmd/link/internal/amd64/asm.go b/src/cmd/link/internal/amd64/asm.go index f86d22493227ac..9da0541f5263c2 100644 --- a/src/cmd/link/internal/amd64/asm.go +++ b/src/cmd/link/internal/amd64/asm.go @@ -398,6 +398,13 @@ func adddynrel(target *ld.Target, ldr *loader.Loader, syms *ld.ArchSyms, s loade // (e.g. go version). return true } + case objabi.R_GOTPCREL: + if target.IsExternal() { + // External linker will do this relocation. + return true + } + // We only need to handle external linking mode, as R_GOTPCREL can + // only occur in plugin or shared build modes. } return false diff --git a/src/cmd/link/internal/ld/data.go b/src/cmd/link/internal/ld/data.go index f4ea8407c83d05..a5a0615c8dff8d 100644 --- a/src/cmd/link/internal/ld/data.go +++ b/src/cmd/link/internal/ld/data.go @@ -1915,7 +1915,6 @@ func (state *dodataState) allocateDataSections(ctxt *Link) { sect = state.allocateNamedSectionAndAssignSyms(&Segdata, ".noptrbss", sym.SNOPTRBSS, sym.Sxxx, 06) ldr.SetSymSect(ldr.LookupOrCreateSym("runtime.noptrbss", 0), sect) ldr.SetSymSect(ldr.LookupOrCreateSym("runtime.enoptrbss", 0), sect) - ldr.SetSymSect(ldr.LookupOrCreateSym("runtime.end", 0), sect) // Code coverage counters are assigned to the .noptrbss section. // We assign them in a separate pass so that they stay aggregated @@ -1935,6 +1934,9 @@ func (state *dodataState) allocateDataSections(ctxt *Link) { ldr.SetSymSect(ldr.LookupOrCreateSym("internal/fuzz._ecounters", 0), sect) } + // Assign runtime.end to the last section of data segment. + ldr.SetSymSect(ldr.LookupOrCreateSym("runtime.end", 0), Segdata.Sections[len(Segdata.Sections)-1]) + if len(state.data[sym.STLSBSS]) > 0 { var sect *sym.Section // FIXME: not clear why it is sometimes necessary to suppress .tbss section creation. diff --git a/src/crypto/tls/handshake_client.go b/src/crypto/tls/handshake_client.go index f016e01b4b5182..08a2d47974c20e 100644 --- a/src/crypto/tls/handshake_client.go +++ b/src/crypto/tls/handshake_client.go @@ -526,7 +526,7 @@ func (hs *clientHandshakeState) pickCipherSuite() error { return errors.New("tls: server chose an unconfigured cipher suite") } - if hs.c.config.CipherSuites == nil && rsaKexCiphers[hs.suite.id] { + if hs.c.config.CipherSuites == nil && !needFIPS() && rsaKexCiphers[hs.suite.id] { tlsrsakex.IncNonDefault() } diff --git a/src/crypto/tls/handshake_server.go b/src/crypto/tls/handshake_server.go index 8129e9c6164af9..4e84aa9d8f0a1d 100644 --- a/src/crypto/tls/handshake_server.go +++ b/src/crypto/tls/handshake_server.go @@ -370,7 +370,7 @@ func (hs *serverHandshakeState) pickCipherSuite() error { } c.cipherSuite = hs.suite.id - if c.config.CipherSuites == nil && rsaKexCiphers[hs.suite.id] { + if c.config.CipherSuites == nil && !needFIPS() && rsaKexCiphers[hs.suite.id] { tlsrsakex.IncNonDefault() } diff --git a/src/go/internal/gccgoimporter/parser.go b/src/go/internal/gccgoimporter/parser.go index a7d2094e0c356c..e8ee74783b9e96 100644 --- a/src/go/internal/gccgoimporter/parser.go +++ b/src/go/internal/gccgoimporter/parser.go @@ -902,6 +902,7 @@ const ( gccgoBuiltinERROR = 19 gccgoBuiltinBYTE = 20 gccgoBuiltinRUNE = 21 + gccgoBuiltinANY = 22 ) func lookupBuiltinType(typ int) types.Type { @@ -926,6 +927,7 @@ func lookupBuiltinType(typ int) types.Type { gccgoBuiltinERROR: types.Universe.Lookup("error").Type(), gccgoBuiltinBYTE: types.Universe.Lookup("byte").Type(), gccgoBuiltinRUNE: types.Universe.Lookup("rune").Type(), + gccgoBuiltinANY: types.Universe.Lookup("any").Type(), }[typ] } diff --git a/src/go/types/initorder.go b/src/go/types/initorder.go index 9ee176fbdb5e9d..a8d8f26b2242ef 100644 --- a/src/go/types/initorder.go +++ b/src/go/types/initorder.go @@ -307,6 +307,14 @@ func (a nodeQueue) Swap(i, j int) { func (a nodeQueue) Less(i, j int) bool { x, y := a[i], a[j] + + // Prioritize all constants before non-constants. See go.dev/issue/66575/. + _, xConst := x.obj.(*Const) + _, yConst := y.obj.(*Const) + if xConst != yConst { + return xConst + } + // nodes are prioritized by number of incoming dependencies (1st key) // and source order (2nd key) return x.ndeps < y.ndeps || x.ndeps == y.ndeps && x.obj.order() < y.obj.order() diff --git a/src/go/types/stmt.go b/src/go/types/stmt.go index 80f3ac75da2cd8..bb203f130c192a 100644 --- a/src/go/types/stmt.go +++ b/src/go/types/stmt.go @@ -893,7 +893,7 @@ func (check *Checker) rangeStmt(inner stmtContext, s *ast.RangeStmt) { lhs := [2]Expr{sKey, sValue} // sKey, sValue may be nil rhs := [2]Type{key, val} // key, val may be nil - constIntRange := x.mode == constant_ && isInteger(x.typ) + rangeOverInt := isInteger(x.typ) if isDef { // short variable declaration @@ -918,19 +918,27 @@ func (check *Checker) rangeStmt(inner stmtContext, s *ast.RangeStmt) { check.errorf(lhs, InvalidSyntaxTree, "cannot declare %s", lhs) obj = NewVar(lhs.Pos(), check.pkg, "_", nil) // dummy variable } + assert(obj.typ == nil) - // initialize lhs variable - if constIntRange { - check.initVar(obj, &x, "range clause") - } else if typ := rhs[i]; typ != nil { - x.mode = value - x.expr = lhs // we don't have a better rhs expression to use here - x.typ = typ - check.initVar(obj, &x, "assignment") // error is on variable, use "assignment" not "range clause" - } else { + // initialize lhs iteration variable, if any + typ := rhs[i] + if typ == nil { obj.typ = Typ[Invalid] obj.used = true // don't complain about unused variable + continue + } + + if rangeOverInt { + assert(i == 0) // at most one iteration variable (rhs[1] == nil for rangeOverInt) + check.initVar(obj, &x, "range clause") + } else { + var y operand + y.mode = value + y.expr = lhs // we don't have a better rhs expression to use here + y.typ = typ + check.initVar(obj, &y, "assignment") // error is on variable, use "assignment" not "range clause" } + assert(obj.typ != nil) } // declare variables @@ -949,21 +957,36 @@ func (check *Checker) rangeStmt(inner stmtContext, s *ast.RangeStmt) { continue } - if constIntRange { + // assign to lhs iteration variable, if any + typ := rhs[i] + if typ == nil { + continue + } + + if rangeOverInt { + assert(i == 0) // at most one iteration variable (rhs[1] == nil for rangeOverInt) check.assignVar(lhs, nil, &x, "range clause") - } else if typ := rhs[i]; typ != nil { - x.mode = value - x.expr = lhs // we don't have a better rhs expression to use here - x.typ = typ - check.assignVar(lhs, nil, &x, "assignment") // error is on variable, use "assignment" not "range clause" + // If the assignment succeeded, if x was untyped before, it now + // has a type inferred via the assignment. It must be an integer. + // (go.dev/issues/67027) + if x.mode != invalid && !isInteger(x.typ) { + check.softErrorf(lhs, InvalidRangeExpr, "cannot use iteration variable of type %s", x.typ) + } + } else { + var y operand + y.mode = value + y.expr = lhs // we don't have a better rhs expression to use here + y.typ = typ + check.assignVar(lhs, nil, &y, "assignment") // error is on variable, use "assignment" not "range clause" } } - } else if constIntRange { + } else if rangeOverInt { // If we don't have any iteration variables, we still need to // check that a (possibly untyped) integer range expression x // is valid. // We do this by checking the assignment _ = x. This ensures - // that an untyped x can be converted to a value of type int. + // that an untyped x can be converted to a value of its default + // type (rune or int). check.assignment(&x, nil, "range clause") } @@ -993,6 +1016,7 @@ func rangeKeyVal(typ Type, allowVersion func(goVersion) bool) (key, val Type, ca return Typ[Int], universeRune, "", false, true // use 'rune' name } if isInteger(typ) { + // untyped numeric constants may be representable as integer values if allowVersion != nil && !allowVersion(go1_22) { return bad("requires go1.22 or later") } diff --git a/src/internal/godebugs/table.go b/src/internal/godebugs/table.go index a0a06729668641..11c5b7d6fdb93b 100644 --- a/src/internal/godebugs/table.go +++ b/src/internal/godebugs/table.go @@ -42,6 +42,7 @@ var All = []Info{ {Name: "multipartmaxparts", Package: "mime/multipart"}, {Name: "multipathtcp", Package: "net"}, {Name: "netdns", Package: "net", Opaque: true}, + {Name: "netedns0", Package: "net", Changed: 19, Old: "0"}, {Name: "panicnil", Package: "runtime", Changed: 21, Old: "1"}, {Name: "randautoseed", Package: "math/rand"}, {Name: "tarinsecurepath", Package: "archive/tar"}, diff --git a/src/internal/types/testdata/check/stmt0.go b/src/internal/types/testdata/check/stmt0.go index b61f1c72320f79..d7ae8f8a02bfc3 100644 --- a/src/internal/types/testdata/check/stmt0.go +++ b/src/internal/types/testdata/check/stmt0.go @@ -953,10 +953,10 @@ func issue10148() { for y /* ERROR "declared and not used" */ := range "" { _ = "" /* ERROR "mismatched types untyped string and untyped int" */ + 1 } - for range 1.5 /* ERROR "cannot range over 1.5" */ { + for range 1.5 /* ERROR "cannot range over 1.5 (untyped float constant)" */ { _ = "" /* ERROR "mismatched types untyped string and untyped int" */ + 1 } - for y := range 1.5 /* ERROR "cannot range over 1.5" */ { + for y := range 1.5 /* ERROR "cannot range over 1.5 (untyped float constant)" */ { _ = "" /* ERROR "mismatched types untyped string and untyped int" */ + 1 } } diff --git a/src/internal/types/testdata/spec/range_int.go b/src/internal/types/testdata/spec/range_int.go index 7f722e2d997c03..766736cc1556ef 100644 --- a/src/internal/types/testdata/spec/range_int.go +++ b/src/internal/types/testdata/spec/range_int.go @@ -129,3 +129,62 @@ func issue65133() { for u8 = range 256 /* ERROR "cannot use 256 (untyped int constant) as uint8 value in range clause (overflows)" */ { } } + +func issue64471() { + for i := range 'a' { + var _ *rune = &i // ensure i has type rune + } +} + +func issue66561() { + for range 10.0 /* ERROR "cannot range over 10.0 (untyped float constant 10)" */ { + } + for range 1e3 /* ERROR "cannot range over 1e3 (untyped float constant 1000)" */ { + } + for range 1 /* ERROR "cannot range over 1 + 0i (untyped complex constant (1 + 0i))" */ + 0i { + } + + for range 1.1 /* ERROR "cannot range over 1.1 (untyped float constant)" */ { + } + for range 1i /* ERROR "cannot range over 1i (untyped complex constant (0 + 1i))" */ { + } + + for i := range 10.0 /* ERROR "cannot range over 10.0 (untyped float constant 10)" */ { + _ = i + } + for i := range 1e3 /* ERROR "cannot range over 1e3 (untyped float constant 1000)" */ { + _ = i + } + for i := range 1 /* ERROR "cannot range over 1 + 0i (untyped complex constant (1 + 0i))" */ + 0i { + _ = i + } + + for i := range 1.1 /* ERROR "cannot range over 1.1 (untyped float constant)" */ { + _ = i + } + for i := range 1i /* ERROR "cannot range over 1i (untyped complex constant (0 + 1i))" */ { + _ = i + } + + var j float64 + _ = j + for j /* ERROR "cannot use iteration variable of type float64" */ = range 1 { + } + for j = range 1.1 /* ERROR "cannot range over 1.1 (untyped float constant)" */ { + } + for j = range 10.0 /* ERROR "cannot range over 10.0 (untyped float constant 10)" */ { + } + + // There shouldn't be assignment errors if there are more iteration variables than permitted. + var i int + _ = i + for i, j /* ERROR "range over 10 (untyped int constant) permits only one iteration variable" */ = range 10 { + } +} + +func issue67027() { + var i float64 + _ = i + for i /* ERROR "cannot use iteration variable of type float64" */ = range 10 { + } +} diff --git a/src/net/dnsclient_unix.go b/src/net/dnsclient_unix.go index 8b3dd5371aa306..8821641a016287 100644 --- a/src/net/dnsclient_unix.go +++ b/src/net/dnsclient_unix.go @@ -16,6 +16,7 @@ import ( "context" "errors" "internal/bytealg" + "internal/godebug" "internal/itoa" "io" "os" @@ -51,6 +52,9 @@ var ( errServerTemporarilyMisbehaving = errors.New("server misbehaving") ) +// netedns0 controls whether we send an EDNS0 additional header. +var netedns0 = godebug.New("netedns0") + func newRequest(q dnsmessage.Question, ad bool) (id uint16, udpReq, tcpReq []byte, err error) { id = uint16(randInt()) b := dnsmessage.NewBuilder(make([]byte, 2, 514), dnsmessage.Header{ID: id, RecursionDesired: true, AuthenticData: ad}) @@ -61,16 +65,20 @@ func newRequest(q dnsmessage.Question, ad bool) (id uint16, udpReq, tcpReq []byt return 0, nil, nil, err } - // Accept packets up to maxDNSPacketSize. RFC 6891. - if err := b.StartAdditionals(); err != nil { - return 0, nil, nil, err - } - var rh dnsmessage.ResourceHeader - if err := rh.SetEDNS0(maxDNSPacketSize, dnsmessage.RCodeSuccess, false); err != nil { - return 0, nil, nil, err - } - if err := b.OPTResource(rh, dnsmessage.OPTResource{}); err != nil { - return 0, nil, nil, err + if netedns0.Value() == "0" { + netedns0.IncNonDefault() + } else { + // Accept packets up to maxDNSPacketSize. RFC 6891. + if err := b.StartAdditionals(); err != nil { + return 0, nil, nil, err + } + var rh dnsmessage.ResourceHeader + if err := rh.SetEDNS0(maxDNSPacketSize, dnsmessage.RCodeSuccess, false); err != nil { + return 0, nil, nil, err + } + if err := b.OPTResource(rh, dnsmessage.OPTResource{}); err != nil { + return 0, nil, nil, err + } } tcpReq, err = b.Finish() diff --git a/src/net/dnsclient_unix_test.go b/src/net/dnsclient_unix_test.go index 0da36303cc8887..f42fbfbf7b129a 100644 --- a/src/net/dnsclient_unix_test.go +++ b/src/net/dnsclient_unix_test.go @@ -2259,19 +2259,34 @@ func testGoLookupIPCNAMEOrderHostsAliases(t *testing.T, mode hostLookupOrder, lo // This isn't a great test as it just tests the dnsmessage package // against itself. func TestDNSPacketSize(t *testing.T) { + t.Run("enabled", func(t *testing.T) { + testDNSPacketSize(t, false) + }) + t.Run("disabled", func(t *testing.T) { + testDNSPacketSize(t, true) + }) +} + +func testDNSPacketSize(t *testing.T, disable bool) { fake := fakeDNSServer{ rh: func(_, _ string, q dnsmessage.Message, _ time.Time) (dnsmessage.Message, error) { - if len(q.Additionals) == 0 { - t.Error("missing EDNS record") - } else if opt, ok := q.Additionals[0].Body.(*dnsmessage.OPTResource); !ok { - t.Errorf("additional record type %T, expected OPTResource", q.Additionals[0]) - } else if len(opt.Options) != 0 { - t.Errorf("found %d Options, expected none", len(opt.Options)) + if disable { + if len(q.Additionals) > 0 { + t.Error("unexpected additional record") + } } else { - got := int(q.Additionals[0].Header.Class) - t.Logf("EDNS packet size == %d", got) - if got != maxDNSPacketSize { - t.Errorf("EDNS packet size == %d, want %d", got, maxDNSPacketSize) + if len(q.Additionals) == 0 { + t.Error("missing EDNS record") + } else if opt, ok := q.Additionals[0].Body.(*dnsmessage.OPTResource); !ok { + t.Errorf("additional record type %T, expected OPTResource", q.Additionals[0]) + } else if len(opt.Options) != 0 { + t.Errorf("found %d Options, expected none", len(opt.Options)) + } else { + got := int(q.Additionals[0].Header.Class) + t.Logf("EDNS packet size == %d", got) + if got != maxDNSPacketSize { + t.Errorf("EDNS packet size == %d, want %d", got, maxDNSPacketSize) + } } } @@ -2304,6 +2319,10 @@ func TestDNSPacketSize(t *testing.T) { }, } + if disable { + t.Setenv("GODEBUG", "netedns0=0") + } + r := &Resolver{PreferGo: true, Dial: fake.DialContext} if _, err := r.LookupIPAddr(context.Background(), "go.dev"); err != nil { t.Errorf("lookup failed: %v", err) diff --git a/src/net/http/server.go b/src/net/http/server.go index acac78bcd0c54b..23a603a83dd713 100644 --- a/src/net/http/server.go +++ b/src/net/http/server.go @@ -1357,16 +1357,21 @@ func (cw *chunkWriter) writeHeader(p []byte) { // If the client wanted a 100-continue but we never sent it to // them (or, more strictly: we never finished reading their - // request body), don't reuse this connection because it's now - // in an unknown state: we might be sending this response at - // the same time the client is now sending its request body - // after a timeout. (Some HTTP clients send Expect: - // 100-continue but knowing that some servers don't support - // it, the clients set a timer and send the body later anyway) - // If we haven't seen EOF, we can't skip over the unread body - // because we don't know if the next bytes on the wire will be - // the body-following-the-timer or the subsequent request. - // See Issue 11549. + // request body), don't reuse this connection. + // + // This behavior was first added on the theory that we don't know + // if the next bytes on the wire are going to be the remainder of + // the request body or the subsequent request (see issue 11549), + // but that's not correct: If we keep using the connection, + // the client is required to send the request body whether we + // asked for it or not. + // + // We probably do want to skip reusing the connection in most cases, + // however. If the client is offering a large request body that we + // don't intend to use, then it's better to close the connection + // than to read the body. For now, assume that if we're sending + // headers, the handler is done reading the body and we should + // drop the connection if we haven't seen EOF. if ecr, ok := w.req.Body.(*expectContinueReader); ok && !ecr.sawEOF.Load() { w.closeAfterReply = true } diff --git a/src/net/http/transport.go b/src/net/http/transport.go index 31cf43a6b33446..cd54dd73b1de2b 100644 --- a/src/net/http/transport.go +++ b/src/net/http/transport.go @@ -2341,17 +2341,12 @@ func (pc *persistConn) readResponse(rc requestAndChan, trace *httptrace.ClientTr return } resCode := resp.StatusCode - if continueCh != nil { - if resCode == 100 { - if trace != nil && trace.Got100Continue != nil { - trace.Got100Continue() - } - continueCh <- struct{}{} - continueCh = nil - } else if resCode >= 200 { - close(continueCh) - continueCh = nil + if continueCh != nil && resCode == StatusContinue { + if trace != nil && trace.Got100Continue != nil { + trace.Got100Continue() } + continueCh <- struct{}{} + continueCh = nil } is1xx := 100 <= resCode && resCode <= 199 // treat 101 as a terminal status, see issue 26161 @@ -2374,6 +2369,25 @@ func (pc *persistConn) readResponse(rc requestAndChan, trace *httptrace.ClientTr if resp.isProtocolSwitch() { resp.Body = newReadWriteCloserBody(pc.br, pc.conn) } + if continueCh != nil { + // We send an "Expect: 100-continue" header, but the server + // responded with a terminal status and no 100 Continue. + // + // If we're going to keep using the connection, we need to send the request body. + // Tell writeLoop to skip sending the body if we're going to close the connection, + // or to send it otherwise. + // + // The case where we receive a 101 Switching Protocols response is a bit + // ambiguous, since we don't know what protocol we're switching to. + // Conceivably, it's one that doesn't need us to send the body. + // Given that we'll send the body if ExpectContinueTimeout expires, + // be consistent and always send it if we aren't closing the connection. + if resp.Close || rc.req.Close { + close(continueCh) // don't send the body; the connection will close + } else { + continueCh <- struct{}{} // send the body + } + } resp.TLS = pc.tlsState return diff --git a/src/net/http/transport_test.go b/src/net/http/transport_test.go index 3fb56246640ff4..4a1ce0b61c9f38 100644 --- a/src/net/http/transport_test.go +++ b/src/net/http/transport_test.go @@ -1185,94 +1185,142 @@ func testTransportGzip(t *testing.T, mode testMode) { } } -// If a request has Expect:100-continue header, the request blocks sending body until the first response. -// Premature consumption of the request body should not be occurred. -func TestTransportExpect100Continue(t *testing.T) { - run(t, testTransportExpect100Continue, []testMode{http1Mode}) +// A transport100Continue test exercises Transport behaviors when sending a +// request with an Expect: 100-continue header. +type transport100ContinueTest struct { + t *testing.T + + reqdone chan struct{} + resp *Response + respErr error + + conn net.Conn + reader *bufio.Reader } -func testTransportExpect100Continue(t *testing.T, mode testMode) { - ts := newClientServerTest(t, mode, HandlerFunc(func(rw ResponseWriter, req *Request) { - switch req.URL.Path { - case "/100": - // This endpoint implicitly responds 100 Continue and reads body. - if _, err := io.Copy(io.Discard, req.Body); err != nil { - t.Error("Failed to read Body", err) - } - rw.WriteHeader(StatusOK) - case "/200": - // Go 1.5 adds Connection: close header if the client expect - // continue but not entire request body is consumed. - rw.WriteHeader(StatusOK) - case "/500": - rw.WriteHeader(StatusInternalServerError) - case "/keepalive": - // This hijacked endpoint responds error without Connection:close. - _, bufrw, err := rw.(Hijacker).Hijack() - if err != nil { - log.Fatal(err) - } - bufrw.WriteString("HTTP/1.1 500 Internal Server Error\r\n") - bufrw.WriteString("Content-Length: 0\r\n\r\n") - bufrw.Flush() - case "/timeout": - // This endpoint tries to read body without 100 (Continue) response. - // After ExpectContinueTimeout, the reading will be started. - conn, bufrw, err := rw.(Hijacker).Hijack() - if err != nil { - log.Fatal(err) - } - if _, err := io.CopyN(io.Discard, bufrw, req.ContentLength); err != nil { - t.Error("Failed to read Body", err) - } - bufrw.WriteString("HTTP/1.1 200 OK\r\n\r\n") - bufrw.Flush() - conn.Close() - } - })).ts +const transport100ContinueTestBody = "request body" - tests := []struct { - path string - body []byte - sent int - status int - }{ - {path: "/100", body: []byte("hello"), sent: 5, status: 200}, // Got 100 followed by 200, entire body is sent. - {path: "/200", body: []byte("hello"), sent: 0, status: 200}, // Got 200 without 100. body isn't sent. - {path: "/500", body: []byte("hello"), sent: 0, status: 500}, // Got 500 without 100. body isn't sent. - {path: "/keepalive", body: []byte("hello"), sent: 0, status: 500}, // Although without Connection:close, body isn't sent. - {path: "/timeout", body: []byte("hello"), sent: 5, status: 200}, // Timeout exceeded and entire body is sent. +// newTransport100ContinueTest creates a Transport and sends an Expect: 100-continue +// request on it. +func newTransport100ContinueTest(t *testing.T, timeout time.Duration) *transport100ContinueTest { + ln := newLocalListener(t) + defer ln.Close() + + test := &transport100ContinueTest{ + t: t, + reqdone: make(chan struct{}), } - c := ts.Client() - for i, v := range tests { - tr := &Transport{ - ExpectContinueTimeout: 2 * time.Second, - } - defer tr.CloseIdleConnections() - c.Transport = tr - body := bytes.NewReader(v.body) - req, err := NewRequest("PUT", ts.URL+v.path, body) - if err != nil { - t.Fatal(err) - } + tr := &Transport{ + ExpectContinueTimeout: timeout, + } + go func() { + defer close(test.reqdone) + body := strings.NewReader(transport100ContinueTestBody) + req, _ := NewRequest("PUT", "http://"+ln.Addr().String(), body) req.Header.Set("Expect", "100-continue") - req.ContentLength = int64(len(v.body)) + req.ContentLength = int64(len(transport100ContinueTestBody)) + test.resp, test.respErr = tr.RoundTrip(req) + test.resp.Body.Close() + }() - resp, err := c.Do(req) - if err != nil { - t.Fatal(err) + c, err := ln.Accept() + if err != nil { + t.Fatalf("Accept: %v", err) + } + t.Cleanup(func() { + c.Close() + }) + br := bufio.NewReader(c) + _, err = ReadRequest(br) + if err != nil { + t.Fatalf("ReadRequest: %v", err) + } + test.conn = c + test.reader = br + t.Cleanup(func() { + <-test.reqdone + tr.CloseIdleConnections() + got, _ := io.ReadAll(test.reader) + if len(got) > 0 { + t.Fatalf("Transport sent unexpected bytes: %q", got) } - resp.Body.Close() + }) - sent := len(v.body) - body.Len() - if v.status != resp.StatusCode { - t.Errorf("test %d: status code should be %d but got %d. (%s)", i, v.status, resp.StatusCode, v.path) - } - if v.sent != sent { - t.Errorf("test %d: sent body should be %d but sent %d. (%s)", i, v.sent, sent, v.path) + return test +} + +// respond sends response lines from the server to the transport. +func (test *transport100ContinueTest) respond(lines ...string) { + for _, line := range lines { + if _, err := test.conn.Write([]byte(line + "\r\n")); err != nil { + test.t.Fatalf("Write: %v", err) } } + if _, err := test.conn.Write([]byte("\r\n")); err != nil { + test.t.Fatalf("Write: %v", err) + } +} + +// wantBodySent ensures the transport has sent the request body to the server. +func (test *transport100ContinueTest) wantBodySent() { + got, err := io.ReadAll(io.LimitReader(test.reader, int64(len(transport100ContinueTestBody)))) + if err != nil { + test.t.Fatalf("unexpected error reading body: %v", err) + } + if got, want := string(got), transport100ContinueTestBody; got != want { + test.t.Fatalf("unexpected body: got %q, want %q", got, want) + } +} + +// wantRequestDone ensures the Transport.RoundTrip has completed with the expected status. +func (test *transport100ContinueTest) wantRequestDone(want int) { + <-test.reqdone + if test.respErr != nil { + test.t.Fatalf("unexpected RoundTrip error: %v", test.respErr) + } + if got := test.resp.StatusCode; got != want { + test.t.Fatalf("unexpected response code: got %v, want %v", got, want) + } +} + +func TestTransportExpect100ContinueSent(t *testing.T) { + test := newTransport100ContinueTest(t, 1*time.Hour) + // Server sends a 100 Continue response, and the client sends the request body. + test.respond("HTTP/1.1 100 Continue") + test.wantBodySent() + test.respond("HTTP/1.1 200", "Content-Length: 0") + test.wantRequestDone(200) +} + +func TestTransportExpect100Continue200ResponseNoConnClose(t *testing.T) { + test := newTransport100ContinueTest(t, 1*time.Hour) + // No 100 Continue response, no Connection: close header. + test.respond("HTTP/1.1 200", "Content-Length: 0") + test.wantBodySent() + test.wantRequestDone(200) +} + +func TestTransportExpect100Continue200ResponseWithConnClose(t *testing.T) { + test := newTransport100ContinueTest(t, 1*time.Hour) + // No 100 Continue response, Connection: close header set. + test.respond("HTTP/1.1 200", "Connection: close", "Content-Length: 0") + test.wantRequestDone(200) +} + +func TestTransportExpect100Continue500ResponseNoConnClose(t *testing.T) { + test := newTransport100ContinueTest(t, 1*time.Hour) + // No 100 Continue response, no Connection: close header. + test.respond("HTTP/1.1 500", "Content-Length: 0") + test.wantBodySent() + test.wantRequestDone(500) +} + +func TestTransportExpect100Continue500ResponseTimeout(t *testing.T) { + test := newTransport100ContinueTest(t, 5*time.Millisecond) // short timeout + test.wantBodySent() // after timeout + test.respond("HTTP/1.1 200", "Content-Length: 0") + test.wantRequestDone(200) } func TestSOCKS5Proxy(t *testing.T) { diff --git a/src/net/net.go b/src/net/net.go index c434c96bf899af..2dd1b5865e5dc6 100644 --- a/src/net/net.go +++ b/src/net/net.go @@ -71,6 +71,12 @@ to print debugging information about its decisions. To force a particular resolver while also printing debugging information, join the two settings by a plus sign, as in GODEBUG=netdns=go+1. +The Go resolver will send an EDNS0 additional header with a DNS request, +to signal a willingness to accept a larger DNS packet size. +This can reportedly cause sporadic failures with the DNS server run +by some modems and routers. Setting GODEBUG=netedns0=0 will disable +sending the additional header. + On macOS, if Go code that uses the net package is built with -buildmode=c-archive, linking the resulting archive into a C program requires passing -lresolv when linking the C code. diff --git a/src/os/exec/exec.go b/src/os/exec/exec.go index c88ee7f52c7b02..a52b75f69cde0f 100644 --- a/src/os/exec/exec.go +++ b/src/os/exec/exec.go @@ -332,6 +332,10 @@ type Cmd struct { // See https://go.dev/blog/path-security // and https://go.dev/issue/43724 for more context. lookPathErr error + + // cachedLookExtensions caches the result of calling lookExtensions. + // This is only used on Windows. + cachedLookExtensions string } // A ctxResult reports the result of watching the Context associated with a @@ -430,16 +434,13 @@ func Command(name string, arg ...string) *Cmd { // We may need to add a filename extension from PATHEXT // or verify an extension that is already present. // Since the path is absolute, its extension should be unambiguous - // and independent of cmd.Dir, and we can go ahead and update cmd.Path to - // reflect it. + // and independent of cmd.Dir, and we can go ahead and cache the lookup now. // // Note that we cannot add an extension here for relative paths, because // cmd.Dir may be set after we return from this function and that may cause // the command to resolve to a different extension. lp, err := lookExtensions(name, "") - if lp != "" { - cmd.Path = lp - } + cmd.cachedLookExtensions = lp if err != nil { cmd.Err = err } @@ -641,7 +642,10 @@ func (c *Cmd) Start() error { return c.Err } lp := c.Path - if runtime.GOOS == "windows" && !filepath.IsAbs(c.Path) { + if c.cachedLookExtensions != "" { + lp = c.cachedLookExtensions + } + if runtime.GOOS == "windows" && c.cachedLookExtensions == "" { // If c.Path is relative, we had to wait until now // to resolve it in case c.Dir was changed. // (If it is absolute, we already resolved its extension in Command diff --git a/src/os/exec/exec_test.go b/src/os/exec/exec_test.go index 71a00494ad3bbe..b08e0c1ccf2110 100644 --- a/src/os/exec/exec_test.go +++ b/src/os/exec/exec_test.go @@ -1835,3 +1835,32 @@ func TestPathRace(t *testing.T) { t.Logf("running in background: %v", cmd) <-done } + +func TestAbsPathExec(t *testing.T) { + testenv.MustHaveExec(t) + testenv.MustHaveGoBuild(t) // must have GOROOT/bin/gofmt, but close enough + + // A simple exec of a full path should work. + // Go 1.22 broke this on Windows, requiring ".exe"; see #66586. + exe := filepath.Join(testenv.GOROOT(t), "bin/gofmt") + cmd := exec.Command(exe) + if cmd.Path != exe { + t.Errorf("exec.Command(%#q) set Path=%#q", exe, cmd.Path) + } + err := cmd.Run() + if err != nil { + t.Errorf("using exec.Command(%#q): %v", exe, err) + } + + cmd = &exec.Cmd{Path: exe} + err = cmd.Run() + if err != nil { + t.Errorf("using exec.Cmd{Path: %#q}: %v", cmd.Path, err) + } + + cmd = &exec.Cmd{Path: "gofmt", Dir: "/"} + err = cmd.Run() + if err == nil { + t.Errorf("using exec.Cmd{Path: %#q}: unexpected success", cmd.Path) + } +} diff --git a/src/runtime/cgo/gcc_stack_darwin.c b/src/runtime/cgo/gcc_stack_darwin.c index 0a9038eb3bcb85..28364c7420e992 100644 --- a/src/runtime/cgo/gcc_stack_darwin.c +++ b/src/runtime/cgo/gcc_stack_darwin.c @@ -15,6 +15,11 @@ x_cgo_getstackbound(uintptr bounds[2]) p = pthread_self(); addr = pthread_get_stackaddr_np(p); // high address (!) size = pthread_get_stacksize_np(p); + + // bounds points into the Go stack. TSAN can't see the synchronization + // in Go around stack reuse. + _cgo_tsan_acquire(); bounds[0] = (uintptr)addr - size; bounds[1] = (uintptr)addr; + _cgo_tsan_release(); } diff --git a/src/runtime/cgo/gcc_stack_unix.c b/src/runtime/cgo/gcc_stack_unix.c index 98a75a19679547..884281dc156643 100644 --- a/src/runtime/cgo/gcc_stack_unix.c +++ b/src/runtime/cgo/gcc_stack_unix.c @@ -36,6 +36,10 @@ x_cgo_getstackbound(uintptr bounds[2]) #endif pthread_attr_destroy(&attr); + // bounds points into the Go stack. TSAN can't see the synchronization + // in Go around stack reuse. + _cgo_tsan_acquire(); bounds[0] = (uintptr)addr; bounds[1] = (uintptr)addr + size; + _cgo_tsan_release(); } diff --git a/src/runtime/cgocall.go b/src/runtime/cgocall.go index f2dd98702d18b3..0d3cc40903a393 100644 --- a/src/runtime/cgocall.go +++ b/src/runtime/cgocall.go @@ -214,15 +214,18 @@ func cgocall(fn, arg unsafe.Pointer) int32 { //go:nosplit func callbackUpdateSystemStack(mp *m, sp uintptr, signal bool) { g0 := mp.g0 - if sp > g0.stack.lo && sp <= g0.stack.hi { - // Stack already in bounds, nothing to do. - return - } - if mp.ncgo > 0 { + inBound := sp > g0.stack.lo && sp <= g0.stack.hi + if mp.ncgo > 0 && !inBound { // ncgo > 0 indicates that this M was in Go further up the stack - // (it called C and is now receiving a callback). It is not - // safe for the C call to change the stack out from under us. + // (it called C and is now receiving a callback). + // + // !inBound indicates that we were called with SP outside the + // expected system stack bounds (C changed the stack out from + // under us between the cgocall and cgocallback?). + // + // It is not safe for the C call to change the stack out from + // under us, so throw. // Note that this case isn't possible for signal == true, as // that is always passing a new M from needm. @@ -240,12 +243,26 @@ func callbackUpdateSystemStack(mp *m, sp uintptr, signal bool) { exit(2) } + if !mp.isextra { + // We allocated the stack for standard Ms. Don't replace the + // stack bounds with estimated ones when we already initialized + // with the exact ones. + return + } + // This M does not have Go further up the stack. However, it may have // previously called into Go, initializing the stack bounds. Between // that call returning and now the stack may have changed (perhaps the // C thread is running a coroutine library). We need to update the // stack bounds for this case. // + // N.B. we need to update the stack bounds even if SP appears to + // already be in bounds. Our "bounds" may actually be estimated dummy + // bounds (below). The actual stack bounds could have shifted but still + // have partial overlap with our dummy bounds. If we failed to update + // in that case, we could find ourselves seemingly called near the + // bottom of the stack bounds, where we quickly run out of space. + // Set the stack bounds to match the current stack. If we don't // actually know how big the stack is, like we don't know how big any // scheduling stack is, but we assume there's at least 32 kB. If we diff --git a/src/runtime/metrics/doc.go b/src/runtime/metrics/doc.go index fb2f44da29c55d..85f256d65a2959 100644 --- a/src/runtime/metrics/doc.go +++ b/src/runtime/metrics/doc.go @@ -290,6 +290,10 @@ Below is the full list of supported metrics, ordered lexicographically. The number of non-default behaviors executed by the net package due to a non-default GODEBUG=multipathtcp=... setting. + /godebug/non-default-behavior/netedns0:events + The number of non-default behaviors executed by the net package + due to a non-default GODEBUG=netedns0=... setting. + /godebug/non-default-behavior/panicnil:events The number of non-default behaviors executed by the runtime package due to a non-default GODEBUG=panicnil=... setting. diff --git a/test/fixedbugs/issue65957.dir/a.go b/test/fixedbugs/issue65957.dir/a.go new file mode 100644 index 00000000000000..284ec4af9f2df5 --- /dev/null +++ b/test/fixedbugs/issue65957.dir/a.go @@ -0,0 +1,12 @@ +// 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 a + +var s any + +//go:noinline +func F() { + s = new([4]int32) +} diff --git a/test/fixedbugs/issue65957.dir/main.go b/test/fixedbugs/issue65957.dir/main.go new file mode 100644 index 00000000000000..89b8a282345131 --- /dev/null +++ b/test/fixedbugs/issue65957.dir/main.go @@ -0,0 +1,19 @@ +// 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 + +import ( + "./a" + "reflect" +) + +var s = []rune{0, 1, 2, 3} + +func main() { + m := map[any]int{} + k := reflect.New(reflect.ArrayOf(4, reflect.TypeOf(int32(0)))).Elem().Interface() + m[k] = 1 + a.F() +} diff --git a/test/fixedbugs/issue65957.go b/test/fixedbugs/issue65957.go new file mode 100644 index 00000000000000..48e4d34c932129 --- /dev/null +++ b/test/fixedbugs/issue65957.go @@ -0,0 +1,7 @@ +// rundir + +// 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 ignored diff --git a/test/fixedbugs/issue66575.go b/test/fixedbugs/issue66575.go new file mode 100644 index 00000000000000..1ad6ca0b707ebc --- /dev/null +++ b/test/fixedbugs/issue66575.go @@ -0,0 +1,31 @@ +// 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 ( + v0 = initv0() + v1 = initv1() +) + +const c = "c" + +func initv0() string { + println("initv0") + if c != "" { // have a dependency on c + return "" + } + return "" +} + +func initv1() string { + println("initv1") + return "" +} + +func main() { + // do nothing +} diff --git a/test/fixedbugs/issue66575.out b/test/fixedbugs/issue66575.out new file mode 100644 index 00000000000000..36d1f17a68f30f --- /dev/null +++ b/test/fixedbugs/issue66575.out @@ -0,0 +1,2 @@ +initv0 +initv1