diff --git a/api/next/78543.txt b/api/next/78543.txt new file mode 100644 index 00000000000000..994f86ff72da5d --- /dev/null +++ b/api/next/78543.txt @@ -0,0 +1,2 @@ +pkg crypto/tls, const MLKEM1024 = 514 #78543 +pkg crypto/tls, const MLKEM1024 CurveID #78543 diff --git a/api/next/79367.txt b/api/next/79367.txt new file mode 100644 index 00000000000000..3213d155052b98 --- /dev/null +++ b/api/next/79367.txt @@ -0,0 +1 @@ +pkg crypto/tls, type Config struct, Rand //deprecated #79367 diff --git a/doc/godebug.md b/doc/godebug.md index f28af6a0b73756..9c5c01d3b27670 100644 --- a/doc/godebug.md +++ b/doc/godebug.md @@ -158,6 +158,16 @@ and the [go command documentation](/cmd/go#hdr-Build_and_test_caching). Go 1.27 removed the `gotypesalias` setting, as noted in the [Go 1.22](#go-122) section. +Go 1.27 removed the `tlsunsafeekm` setting, as noted in the [Go 1.22](#go-122) section. + +Go 1.27 removed the `tlsrsakex` setting, as noted in the [Go 1.22](#go-122) section. + +Go 1.27 removed the `tls3des` setting, as noted in the [Go 1.23](#go-123) section. + +Go 1.27 removed the `tls10server` setting, as noted in the [Go 1.22](#go-122) section. + +Go 1.27 removed the `x509keypairleaf` setting, as noted in the [Go 1.23](#go-123) section. + Go 1.27 added a new `htmlmetacontenturlescape` setting that controls whether html/template will escape URLs in the `url=` portion of the content attribute of HTML meta tags. The default `htmlmetacontentescape=1` will cause URLs to be diff --git a/doc/next/6-stdlib/99-minor/crypto/ecdsa/hashlen.md b/doc/next/6-stdlib/99-minor/crypto/ecdsa/hashlen.md new file mode 100644 index 00000000000000..88673f8fa3a135 --- /dev/null +++ b/doc/next/6-stdlib/99-minor/crypto/ecdsa/hashlen.md @@ -0,0 +1,2 @@ +[PrivateKey.Sign] now checks that the length of the hash is correct, if opts is +not nil. diff --git a/doc/next/6-stdlib/99-minor/crypto/tls/78543.md b/doc/next/6-stdlib/99-minor/crypto/tls/78543.md new file mode 100644 index 00000000000000..4dd30688547d2b --- /dev/null +++ b/doc/next/6-stdlib/99-minor/crypto/tls/78543.md @@ -0,0 +1,2 @@ +The [MLKEM1024] key exchange is now supported. It can be enabled by adding it to +[Config.CurvePreferences]. diff --git a/doc/next/6-stdlib/99-minor/crypto/tls/79367.md b/doc/next/6-stdlib/99-minor/crypto/tls/79367.md new file mode 100644 index 00000000000000..f5a37f8ee9b008 --- /dev/null +++ b/doc/next/6-stdlib/99-minor/crypto/tls/79367.md @@ -0,0 +1,2 @@ +[Config.Rand] is now deprecated. +For deterministic testing, use [testing/cryptotest.SetGlobalRandom]. diff --git a/doc/next/6-stdlib/99-minor/crypto/tls/tlsmlkem.md b/doc/next/6-stdlib/99-minor/crypto/tls/tlsmlkem.md new file mode 100644 index 00000000000000..834978e1ec39a4 --- /dev/null +++ b/doc/next/6-stdlib/99-minor/crypto/tls/tlsmlkem.md @@ -0,0 +1,4 @@ +Post-quantum hybrid key exchanges can now be explicitly enabled in +[Config.CurvePreferences] even if the `tlsmlkem=0` or `tlssecpmlkem=0` GODEBUG +options are used. Those options were always meant to only apply to the default +set used when [Config.CurvePreferences] is nil. diff --git a/doc/next/7-ports.md b/doc/next/7-ports.md index 3f39ced990f92f..7ce1d914ac1d28 100644 --- a/doc/next/7-ports.md +++ b/doc/next/7-ports.md @@ -6,3 +6,13 @@ As [announced](go1.26#darwin) in the Go 1.26 release notes, Go 1.27 requires macOS 13 Ventura or later; support for previous versions has been discontinued. + +### Linux {#linux} + +On ppc64, the ABI has been migrated to ELFv2. This change +has no effect for those building and running pure Go +binaries. + +On ppc64, external linking, cgo, and PIE binaries are now +supported. Using these features requires an ELFv2 compatible +runtime (libc, kernel, and all linked and loaded libraries). diff --git a/src/cmd/api/main_test.go b/src/cmd/api/main_test.go index 411ecc12722239..8e6c6f216ebd31 100644 --- a/src/cmd/api/main_test.go +++ b/src/cmd/api/main_test.go @@ -389,7 +389,7 @@ func (w *Walker) parseFile(dir, file string) (*ast.File, error) { return f, nil } - f, err := parser.ParseFile(fset, filename, nil, parser.ParseComments) + f, err := parser.ParseFile(fset, filename, nil, parser.ParseComments|parser.SkipObjectResolution) if err != nil { return nil, err } diff --git a/src/cmd/cgo/internal/testerrors/argposition_test.go b/src/cmd/cgo/internal/testerrors/argposition_test.go index 035552127b8f46..30447e7a68807e 100644 --- a/src/cmd/cgo/internal/testerrors/argposition_test.go +++ b/src/cmd/cgo/internal/testerrors/argposition_test.go @@ -96,7 +96,7 @@ func TestArgumentsPositions(t *testing.T) { t.Fatal(err) } fset := token.NewFileSet() - f, err := parser.ParseFile(fset, "", mainProcessed, parser.AllErrors) + f, err := parser.ParseFile(fset, "", mainProcessed, parser.AllErrors|parser.SkipObjectResolution) if err != nil { fmt.Println(err) return diff --git a/src/cmd/compile/internal/noder/noder.go b/src/cmd/compile/internal/noder/noder.go index 4e48126e82ead2..5d33072690f075 100644 --- a/src/cmd/compile/internal/noder/noder.go +++ b/src/cmd/compile/internal/noder/noder.go @@ -175,6 +175,10 @@ type pragmas struct { WasmExport *WasmExport } +func (p *pragmas) Nointerface() bool { + return p.Flag&ir.Nointerface != 0 +} + // WasmImport stores metadata associated with the //go:wasmimport pragma type WasmImport struct { Pos syntax.Pos diff --git a/src/cmd/compile/internal/ssa/_gen/rulegen.go b/src/cmd/compile/internal/ssa/_gen/rulegen.go index 3daf3e8605af7a..6cabb0006c980e 100644 --- a/src/cmd/compile/internal/ssa/_gen/rulegen.go +++ b/src/cmd/compile/internal/ssa/_gen/rulegen.go @@ -289,7 +289,7 @@ func genRulesSuffix(arch arch, suff string) { buf := new(bytes.Buffer) fprint(buf, genFile) fset := token.NewFileSet() - file, err := parser.ParseFile(fset, "", buf, parser.ParseComments) + file, err := parser.ParseFile(fset, "", buf, parser.ParseComments|parser.SkipObjectResolution) if err != nil { filename := fmt.Sprintf("%s_broken.go", arch.name) if err := os.WriteFile(filename, buf.Bytes(), 0644); err != nil { @@ -841,7 +841,7 @@ func exprf(format string, a ...interface{}) ast.Expr { func stmtf(format string, a ...interface{}) Statement { src := fmt.Sprintf(format, a...) fsrc := "package p\nfunc _() {\n" + src + "\n}\n" - file, err := parser.ParseFile(token.NewFileSet(), "", fsrc, 0) + file, err := parser.ParseFile(token.NewFileSet(), "", fsrc, parser.SkipObjectResolution) if err != nil { log.Fatalf("stmt parse error on %q: %v", src, err) } diff --git a/src/cmd/compile/internal/ssagen/abi.go b/src/cmd/compile/internal/ssagen/abi.go index 011c0d12f43262..724b9e94000916 100644 --- a/src/cmd/compile/internal/ssagen/abi.go +++ b/src/cmd/compile/internal/ssagen/abi.go @@ -280,6 +280,10 @@ func makeABIWrapper(f *ir.Func, wrapperABI obj.ABI) { fn.SetABIWrapper(true) fn.SetDupok(true) + // Propagate linkname attribute. + fn.LinksymABI(fn.ABI).Set(obj.AttrLinkname, f.Linksym().IsLinkname()) + fn.LinksymABI(fn.ABI).Set(obj.AttrLinknameStd, f.Linksym().IsLinknameStd()) + // ABI0-to-ABIInternal wrappers will be mainly loading params from // stack into registers (and/or storing stack locations back to // registers after the wrapped call); in most cases they won't diff --git a/src/cmd/compile/internal/test/ssa_test.go b/src/cmd/compile/internal/test/ssa_test.go index 10f32f65ad9fd9..c0a8b6c1c0817c 100644 --- a/src/cmd/compile/internal/test/ssa_test.go +++ b/src/cmd/compile/internal/test/ssa_test.go @@ -95,7 +95,7 @@ func TestCode(t *testing.T) { t.Fatalf("can't read testdata/%s: %v", f.Name(), err) } fset := token.NewFileSet() - code, err := parser.ParseFile(fset, f.Name(), text, 0) + code, err := parser.ParseFile(fset, f.Name(), text, parser.SkipObjectResolution) if err != nil { t.Fatalf("can't parse testdata/%s: %v", f.Name(), err) } diff --git a/src/cmd/compile/internal/typecheck/mkbuiltin.go b/src/cmd/compile/internal/typecheck/mkbuiltin.go index 28afac5d7ab190..c3b0848b1eef17 100644 --- a/src/cmd/compile/internal/typecheck/mkbuiltin.go +++ b/src/cmd/compile/internal/typecheck/mkbuiltin.go @@ -79,7 +79,7 @@ func params(tlist ...*types.Type) []*types.Field { func mkbuiltin(w io.Writer, name string) { fset := token.NewFileSet() - f, err := parser.ParseFile(fset, filepath.Join("_builtin", name+".go"), nil, 0) + f, err := parser.ParseFile(fset, filepath.Join("_builtin", name+".go"), nil, parser.SkipObjectResolution) if err != nil { log.Fatal(err) } diff --git a/src/cmd/compile/internal/types2/decl.go b/src/cmd/compile/internal/types2/decl.go index 9644253aebc80a..2028edff0d988d 100644 --- a/src/cmd/compile/internal/types2/decl.go +++ b/src/cmd/compile/internal/types2/decl.go @@ -684,6 +684,12 @@ func (check *Checker) funcDecl(obj *Func, decl *declInfo) { fdecl := decl.fdecl check.funcType(sig, fdecl.Recv, fdecl.TParamList, fdecl.Type) + if fdecl.Pragma != nil { + if p, ok := fdecl.Pragma.(interface{ Nointerface() bool }); ok && p.Nointerface() { + obj.nointerface = true + } + } + // Set the scope's extent to the complete "func (...) { ... }" // so that Scope.Innermost works correctly. sig.scope.pos = fdecl.Pos() diff --git a/src/cmd/compile/internal/types2/lookup.go b/src/cmd/compile/internal/types2/lookup.go index 457352fc88ba0c..107101fe1ca038 100644 --- a/src/cmd/compile/internal/types2/lookup.go +++ b/src/cmd/compile/internal/types2/lookup.go @@ -390,6 +390,7 @@ func (check *Checker) missingMethod(V, T Type, static bool, equivalent func(x, y ambigSel ptrRecv field + nointerface ) state := ok @@ -453,6 +454,11 @@ func (check *Checker) missingMethod(V, T Type, static bool, equivalent func(x, y check.objDecl(f) } + if f.nointerface { + state = nointerface + break + } + if !equivalent(f.typ, m.typ) { state = wrongSig break @@ -512,6 +518,8 @@ func (check *Checker) missingMethod(V, T Type, static bool, equivalent func(x, y *cause = check.sprintf("(method %s has pointer receiver)", m.Name()) case field: *cause = check.sprintf("(%s.%s is a field, not a method)", V, m.Name()) + case nointerface: + *cause = check.sprintf("(%s method is marked 'nointerface')", m.Name()) default: panic("unreachable") } diff --git a/src/cmd/compile/internal/types2/object.go b/src/cmd/compile/internal/types2/object.go index 87d8d70a2a4da5..a3ab50f7cad406 100644 --- a/src/cmd/compile/internal/types2/object.go +++ b/src/cmd/compile/internal/types2/object.go @@ -396,8 +396,9 @@ func (*Var) isDependency() {} // a variable may be a dependency of an initializa // An abstract method may belong to many interfaces due to embedding. type Func struct { object - hasPtrRecv_ bool // only valid for methods that don't have a type yet; use hasPtrRecv() to read origin *Func // if non-nil, the Func from which this one was instantiated + hasPtrRecv_ bool // only valid for methods that don't have a type yet; use hasPtrRecv() to read + nointerface bool } // NewFunc returns a new function with the given signature, representing @@ -412,7 +413,7 @@ func NewFunc(pos syntax.Pos, pkg *Package, name string, sig *Signature) *Func { // as this would violate object.{Type,color} invariants. // TODO(adonovan): propose to disallow NewFunc with nil *Signature. } - return &Func{object{nil, pos, pkg, name, typ, 0, nopos}, false, nil} + return &Func{object{nil, pos, pkg, name, typ, 0, nopos}, nil, false, false} } // Signature returns the signature (type) of the function or method. diff --git a/src/cmd/cover/cover.go b/src/cmd/cover/cover.go index 578a978f4e53da..bc24c52f138a9b 100644 --- a/src/cmd/cover/cover.go +++ b/src/cmd/cover/cover.go @@ -773,7 +773,7 @@ func (p *Package) annotateFile(name string, fd io.Writer) { if err != nil { log.Fatalf("cover: %s: %s", name, err) } - parsedFile, err := parser.ParseFile(fset, name, content, parser.ParseComments) + parsedFile, err := parser.ParseFile(fset, name, content, parser.ParseComments|parser.SkipObjectResolution) if err != nil { log.Fatalf("cover: %s: %s", name, err) } diff --git a/src/cmd/cover/cover_test.go b/src/cmd/cover/cover_test.go index 504c263c9c6415..b86ebd0d149a0b 100644 --- a/src/cmd/cover/cover_test.go +++ b/src/cmd/cover/cover_test.go @@ -259,7 +259,7 @@ func TestDirectives(t *testing.T) { // come before the beginning of the named declaration and after the end // of the previous declaration. fset := token.NewFileSet() - astFile, err := parser.ParseFile(fset, testDirectives, output, 0) + astFile, err := parser.ParseFile(fset, testDirectives, output, parser.SkipObjectResolution) if err != nil { t.Fatal(err) } diff --git a/src/cmd/cover/func.go b/src/cmd/cover/func.go index dffd3c1a0553ac..82e9d0c7ac9af1 100644 --- a/src/cmd/cover/func.go +++ b/src/cmd/cover/func.go @@ -93,7 +93,7 @@ func funcOutput(profile, outputFile string) error { // findFuncs parses the file and returns a slice of FuncExtent descriptors. func findFuncs(name string) ([]*FuncExtent, error) { fset := token.NewFileSet() - parsedFile, err := parser.ParseFile(fset, name, nil, 0) + parsedFile, err := parser.ParseFile(fset, name, nil, parser.SkipObjectResolution) if err != nil { return nil, err } diff --git a/src/cmd/dist/testjson.go b/src/cmd/dist/testjson.go index c190c665ebf779..8ae6e8905d578c 100644 --- a/src/cmd/dist/testjson.go +++ b/src/cmd/dist/testjson.go @@ -80,7 +80,8 @@ func (f *testJSONFilter) process(line []byte) { // struct, or other additions outside of it. If humans are ever looking // at the output, it's really nice to keep field order because it // preserves a lot of regularity in the output. - dec := json.NewDecoder(bytes.NewBuffer(line)) + lineBuf := bytes.NewBuffer(line) + dec := json.NewDecoder(lineBuf) dec.UseNumber() val, err := decodeJSONValue(dec) if err == nil && val.atom == json.Delim('{') { @@ -105,6 +106,7 @@ func (f *testJSONFilter) process(line []byte) { // Copy any trailing text. We expect at most a "\n" here, but // there could be other text and we want to feed that through. io.Copy(f.w, dec.Buffered()) + io.Copy(f.w, lineBuf) return } } diff --git a/src/cmd/go/internal/generate/generate.go b/src/cmd/go/internal/generate/generate.go index bb66ce4bbdd067..c4ab16cf929dde 100644 --- a/src/cmd/go/internal/generate/generate.go +++ b/src/cmd/go/internal/generate/generate.go @@ -244,7 +244,7 @@ func generate(absFile string) bool { } // Parse package clause - filePkg, err := parser.ParseFile(token.NewFileSet(), "", src, parser.PackageClauseOnly) + filePkg, err := parser.ParseFile(token.NewFileSet(), "", src, parser.PackageClauseOnly|parser.SkipObjectResolution) if err != nil { // Invalid package clause - ignore file. return true diff --git a/src/cmd/go/internal/modindex/build_read.go b/src/cmd/go/internal/modindex/build_read.go index f2e48f36cdff54..86b908a9feae3d 100644 --- a/src/cmd/go/internal/modindex/build_read.go +++ b/src/cmd/go/internal/modindex/build_read.go @@ -304,7 +304,7 @@ func readGoInfo(f io.Reader, info *fileInfo) error { } // Parse file header & record imports. - info.parsed, info.parseErr = parser.ParseFile(info.fset, info.name, info.header, parser.ImportsOnly|parser.ParseComments) + info.parsed, info.parseErr = parser.ParseFile(info.fset, info.name, info.header, parser.ImportsOnly|parser.ParseComments|parser.SkipObjectResolution) if info.parseErr != nil { return nil } diff --git a/src/cmd/gofmt/gofmt.go b/src/cmd/gofmt/gofmt.go index ad6ad636524479..8f8540b41f83a4 100644 --- a/src/cmd/gofmt/gofmt.go +++ b/src/cmd/gofmt/gofmt.go @@ -79,7 +79,7 @@ func usage() { } func initParserMode() { - parserMode = parser.ParseComments + parserMode = parser.ParseComments | parser.SkipObjectResolution if *allErrors { parserMode |= parser.AllErrors } diff --git a/src/cmd/internal/dwarf/putvarabbrevgen_test.go b/src/cmd/internal/dwarf/putvarabbrevgen_test.go index 24500a33889d62..ffe400a30a8504 100644 --- a/src/cmd/internal/dwarf/putvarabbrevgen_test.go +++ b/src/cmd/internal/dwarf/putvarabbrevgen_test.go @@ -80,7 +80,7 @@ func TestPutVarAbbrevGenerator(t *testing.T) { func pvagenerate(t *testing.T) string { var fset token.FileSet - f, err := parser.ParseFile(&fset, "./dwarf.go", nil, parser.ParseComments) + f, err := parser.ParseFile(&fset, "./dwarf.go", nil, parser.ParseComments|parser.SkipObjectResolution) if err != nil { t.Fatal(err) } diff --git a/src/cmd/internal/goobj/mkbuiltin.go b/src/cmd/internal/goobj/mkbuiltin.go index 8b73bb115bec88..7e1c425c9d18bd 100644 --- a/src/cmd/internal/goobj/mkbuiltin.go +++ b/src/cmd/internal/goobj/mkbuiltin.go @@ -53,7 +53,7 @@ func mkbuiltin(w io.Writer) { const pkg = "runtime" fset := token.NewFileSet() path := filepath.Join("..", "..", "compile", "internal", "typecheck", "_builtin", "runtime.go") - f, err := parser.ParseFile(fset, path, nil, 0) + f, err := parser.ParseFile(fset, path, nil, parser.SkipObjectResolution) if err != nil { log.Fatal(err) } diff --git a/src/cmd/internal/obj/objfile.go b/src/cmd/internal/obj/objfile.go index fc928f3d7016ea..6bd5bca04462b4 100644 --- a/src/cmd/internal/obj/objfile.go +++ b/src/cmd/internal/obj/objfile.go @@ -371,9 +371,7 @@ func (w *writer) Sym(s *LSym) { if s.IsPkgInit() { flag2 |= goobj.SymFlagPkgInit } - if s.IsLinkname() || (w.ctxt.IsAsm && name != "") || name == "main.main" { - // Assembly reference is treated the same as linkname, - // but not for unnamed (aux) symbols. + if s.IsLinkname() || name == "main.main" { // The runtime linknames main.main. flag2 |= goobj.SymFlagLinkname } diff --git a/src/cmd/internal/obj/x86/asm6.go b/src/cmd/internal/obj/x86/asm6.go index 5aff15c5b3995b..89b64f4eedd8ea 100644 --- a/src/cmd/internal/obj/x86/asm6.go +++ b/src/cmd/internal/obj/x86/asm6.go @@ -2265,7 +2265,16 @@ func instinit(ctxt *obj.Link) { switch ctxt.Headtype { case objabi.Hplan9: + // _privates is a special symbol on Plan 9 that + // points to per–process private data (like TLS area). + // See https://9p.io/magic/man2html/2/exec . + // The assembler inserts a reference to this symbol + // for accessing the G. Mark it as linkname so it is + // allowed to access from anywhere. (Would be nice to + // mark it external, but we don't have a mechanism for + // that.) plan9privates = ctxt.Lookup("_privates") + plan9privates.Set(obj.AttrLinkname, true) } for i := range avxOptab { diff --git a/src/cmd/link/internal/loader/loader.go b/src/cmd/link/internal/loader/loader.go index cd8100baf21d71..ef48467a04b608 100644 --- a/src/cmd/link/internal/loader/loader.go +++ b/src/cmd/link/internal/loader/loader.go @@ -15,6 +15,7 @@ import ( "debug/elf" "fmt" "internal/abi" + "internal/buildcfg" "io" "iter" "log" @@ -2368,7 +2369,7 @@ func loadObjRefs(l *Loader, r *oReader, arch *sys.Arch) { v := abiToVer(osym.ABI(), r.version) gi := l.LookupOrCreateSym(name, v) r.syms[ndef+i] = gi - if osym.IsLinkname() || osym.IsLinknameStd() { + if osym.IsLinkname() || osym.IsLinknameStd() || r.FromAssembly() { // Check if a linkname reference is allowed. // Only check references (pull), not definitions (push), // so push is always allowed. @@ -2426,8 +2427,6 @@ func abiToVer(abi uint16, localSymVersion int) int { // If a name is in this map, it is allowed only in listed packages, // even if it has a linknamed definition. var blockedLinknames = map[string][]string{ - // coroutines - "runtime.newcoro": {"iter"}, // fips info "go:fipsinfo": {"crypto/internal/fips140/check"}, // New internal linknames in Go 1.24 @@ -2553,6 +2552,27 @@ func (l *Loader) checkLinkname(refpkg *oReader, name string, s Sym) { return } osym := r.Sym(li) + if r.FromAssembly() && !osym.IsLinknameStd() && !osym.IsLinkname() { + if strings.HasPrefix(name, pkg) { + // Allow if by name it is pushed to pkg, e.g. in package a, + // an assembly function is defined as b.F, then it is allowed + // to be used in package b. + return + } + // For an assembly symbol, check if there is a linkname applied + // to its ABI wrapper. + if !buildcfg.Experiment.RegabiWrappers { + // If ABI wrapper is not enabled (i.e. non-regabi platform), + // permit for now, as there is no good way to check. + return + } + otherABI := 1 - abiToVer(osym.ABI(), r.version) // for now, we only have ABI 0 and 1 + w := l.Lookup(name, otherABI) // TODO: use an aux symbol instead of name lookup? + if w != 0 { + r, li = l.toLocal(w) + osym = r.Sym(li) + } + } if osym.IsLinknameStd() { // It is pushed with linknamestd. Allow only pulls from the // standard library. @@ -2560,12 +2580,8 @@ func (l *Loader) checkLinkname(refpkg *oReader, name string, s Sym) { return } } - if osym.IsLinkname() || osym.ABIWrapper() { + if osym.IsLinkname() { // Allow if the def has a linkname (push). - // ABI wrapper usually wraps an assembly symbol, a linknamed symbol, - // or an external symbol, or provide access of a Go symbol to assembly. - // For now, allow ABI wrappers. - // TODO: check the wrapped symbol? return } error() diff --git a/src/cmd/link/link_test.go b/src/cmd/link/link_test.go index 807cf15f8c6f20..e7c406c4ad1eb8 100644 --- a/src/cmd/link/link_test.go +++ b/src/cmd/link/link_test.go @@ -12,6 +12,7 @@ import ( "debug/pe" "errors" "internal/abi" + "internal/buildcfg" "internal/platform" "internal/testenv" "internal/xcoff" @@ -1715,6 +1716,10 @@ func TestCheckLinkname(t *testing.T) { {"coro2.go", false}, // pull linkname of a builtin symbol is not ok {"builtin.go", false}, + // using a linkname to reference a runtime assembly + // function is not ok (except on non-regabi platforms) + {"systemstack.go", !buildcfg.Experiment.RegabiWrappers}, + // misc {"addmoduledata.go", false}, {"freegc.go", false}, // legacy bad linkname is ok, for now diff --git a/src/cmd/link/testdata/linkname/badlinkname.go b/src/cmd/link/testdata/linkname/badlinkname.go index fb9f9c6b7d164e..29e645126d0ba5 100644 --- a/src/cmd/link/testdata/linkname/badlinkname.go +++ b/src/cmd/link/testdata/linkname/badlinkname.go @@ -3,7 +3,8 @@ // license that can be found in the LICENSE file. // Existing pull linknames in the wild are allowed _for now_, -// for legacy reason. Test a function and a method. +// for legacy reason. Test a function, a method, and an +// assembly symbol. // NOTE: this may not be allowed in the future. Don't do this! package main @@ -19,6 +20,12 @@ func noescape(unsafe.Pointer) unsafe.Pointer //go:linkname rtype_String reflect.(*rtype).String func rtype_String(unsafe.Pointer) string +//go:linkname memmove runtime.memmove +func memmove(to, from unsafe.Pointer, n uintptr) + +var n uintptr // use a global to prevent compiler optimize out memmove call + func main() { println(rtype_String(noescape(nil))) + memmove(nil, nil, n) } diff --git a/src/cmd/link/testdata/linkname/systemstack.go b/src/cmd/link/testdata/linkname/systemstack.go new file mode 100644 index 00000000000000..7fc575d6eb7679 --- /dev/null +++ b/src/cmd/link/testdata/linkname/systemstack.go @@ -0,0 +1,19 @@ +// 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. + +// Linkname systemstack is not allowed, even if it is +// defined in assembly. + +package main + +import _ "unsafe" + +func f() {} + +func main() { + systemstack(f) +} + +//go:linkname systemstack runtime.systemstack +func systemstack(func()) diff --git a/src/crypto/ecdsa/ecdsa.go b/src/crypto/ecdsa/ecdsa.go index b2319ba0ecf9bd..40a89017570171 100644 --- a/src/crypto/ecdsa/ecdsa.go +++ b/src/crypto/ecdsa/ecdsa.go @@ -324,6 +324,11 @@ func (priv *PrivateKey) Sign(random io.Reader, digest []byte, opts crypto.Signer if random == nil { return signRFC6979(priv, digest, opts) } + if opts != nil { + if hashSize := opts.HashFunc().Size(); hashSize != len(digest) { + return nil, errors.New("ecdsa: hash length does not match hash function") + } + } random = rand.CustomReader(random) return SignASN1(random, priv, digest) } @@ -380,6 +385,10 @@ func generateFIPS[P ecdsa.Point[P]](curve elliptic.Curve, c *ecdsa.Curve[P], ran // is set. This setting will be removed in a future Go release. Instead, use // [testing/cryptotest.SetGlobalRandom]. func SignASN1(r io.Reader, priv *PrivateKey, hash []byte) ([]byte, error) { + if len(hash) == 0 { + return nil, errors.New("ecdsa: hash cannot be empty") + } + if boring.Enabled && rand.IsDefaultReader(r) { b, err := boringPrivateKey(priv) if err != nil { @@ -497,6 +506,10 @@ func addASN1IntBytes(b *cryptobyte.Builder, bytes []byte) { // The inputs are not considered confidential, and may leak through timing side // channels, or if an attacker has control of part of the inputs. func VerifyASN1(pub *PublicKey, hash, sig []byte) bool { + if len(hash) == 0 { + return false + } + if boring.Enabled { key, err := boringPublicKey(pub) if err != nil { diff --git a/src/crypto/internal/fips140/ecdsa/ecdsa.go b/src/crypto/internal/fips140/ecdsa/ecdsa.go index 1c00f55810a772..5759c34c6a12bb 100644 --- a/src/crypto/internal/fips140/ecdsa/ecdsa.go +++ b/src/crypto/internal/fips140/ecdsa/ecdsa.go @@ -274,7 +274,7 @@ type Signature struct { R, S []byte } -// Sign signs a hash (which shall be the result of hashing a larger message with +// Sign signs a hash (which should be the result of hashing a larger message with // the hash function H) using the private key, priv. If the hash is longer than // the bit-length of the private key's curve order, the hash will be truncated // to that length. @@ -282,6 +282,9 @@ func Sign[P Point[P], H hash.Hash](c *Curve[P], h func() H, priv *PrivateKey, ra if priv.pub.curve != c.curve { return nil, errors.New("ecdsa: private key does not match curve") } + if len(hash) == 0 { + return nil, errors.New("ecdsa: hash cannot be empty") + } fips140.RecordApproved() fipsSelfTest() @@ -315,6 +318,9 @@ func SignDeterministic[P Point[P], H hash.Hash](c *Curve[P], h func() H, priv *P if priv.pub.curve != c.curve { return nil, errors.New("ecdsa: private key does not match curve") } + if len(hash) == 0 { + return nil, errors.New("ecdsa: hash cannot be empty") + } fips140.RecordApproved() fipsSelfTestDeterministic() drbg := newDRBG(h, priv.d, bits2octets(c, hash), nil) // RFC 6979, Section 3.3 @@ -447,6 +453,9 @@ func Verify[P Point[P]](c *Curve[P], pub *PublicKey, hash []byte, sig *Signature if pub.curve != c.curve { return errors.New("ecdsa: public key does not match curve") } + if len(hash) == 0 { + return errors.New("ecdsa: hash cannot be empty") + } fips140.RecordApproved() fipsSelfTest() return verify(c, pub, hash, sig) diff --git a/src/crypto/internal/fips140/rsa/largeexponent.go b/src/crypto/internal/fips140/rsa/largeexponent.go index e09052509518b5..ba9a3365ea7eb9 100644 --- a/src/crypto/internal/fips140/rsa/largeexponent.go +++ b/src/crypto/internal/fips140/rsa/largeexponent.go @@ -6,10 +6,14 @@ package rsa import ( "bytes" + "crypto/internal/constanttime" "crypto/internal/fips140" "crypto/internal/fips140/bigmod" + "crypto/internal/fips140/drbg" + "crypto/internal/fips140/subtle" "errors" "hash" + "io" ) // TestingOnlyLargeExponentPublicKey is a variant of [PublicKey] that supports @@ -113,3 +117,249 @@ func TestingOnlyLargeExponentVerifyPSS(pub *TestingOnlyLargeExponentPublicKey, h } return emsaPSSVerify(digest, em, emBits, pssSaltLengthAutodetect, hash) } + +// TestingOnlyLargeExponentPrivateKey is a variant of [PrivateKey] that supports +// large public exponents. It is only meant for supporting the full ACVP test +// suite. This type must not be used in production code. +type TestingOnlyLargeExponentPrivateKey struct { + n *bigmod.Modulus + e []byte // big-endian public exponent + d *bigmod.Nat + p, q *bigmod.Modulus + dP []byte + dQ []byte + qInv *bigmod.Nat +} + +func (priv *TestingOnlyLargeExponentPrivateKey) Size() int { + return (priv.n.BitLen() + 7) / 8 +} + +// TestingOnlyNewLargeExponentPrivateKeyWithPrecomputation creates a new RSA private key +// with a large public exponent from the given parameters. It is only meant for ACVP testing. +func TestingOnlyNewLargeExponentPrivateKeyWithPrecomputation(N []byte, e []byte, d, P, Q, dP, dQ, qInv []byte) (*TestingOnlyLargeExponentPrivateKey, error) { + n, err := bigmod.NewModulus(N) + if err != nil { + return nil, err + } + p, err := bigmod.NewModulus(P) + if err != nil { + return nil, err + } + q, err := bigmod.NewModulus(Q) + if err != nil { + return nil, err + } + dN, err := bigmod.NewNat().SetBytes(d, n) + if err != nil { + return nil, err + } + qInvNat, err := bigmod.NewNat().SetBytes(qInv, p) + if err != nil { + return nil, err + } + + priv := &TestingOnlyLargeExponentPrivateKey{ + n: n, e: e, d: dN, p: p, q: q, + dP: dP, dQ: dQ, qInv: qInvNat, + } + if err := checkLargeExponentPrivateKey(priv); err != nil { + return nil, err + } + return priv, nil +} + +func checkLargeExponentPrivateKey(priv *TestingOnlyLargeExponentPrivateKey) error { + // Check public key portion. + pub := &TestingOnlyLargeExponentPublicKey{N: priv.n, E: priv.e} + if err := checkLargeExponentPublicKey(pub); err != nil { + return err + } + + N := priv.n + p := priv.p + q := priv.q + + // FIPS 186-5, Section 5.1 requires "that p and q be of the same bit length." + if p.BitLen() != q.BitLen() { + // We don't enforce this for testing, just note it. + } + + // Check that pq ≡ 1 mod N (and that p < N and q < N). + pN := bigmod.NewNat().ExpandFor(N) + if _, err := pN.SetBytes(p.Nat().Bytes(p), N); err != nil { + return errors.New("crypto/rsa: invalid prime") + } + qN := bigmod.NewNat().ExpandFor(N) + if _, err := qN.SetBytes(q.Nat().Bytes(q), N); err != nil { + return errors.New("crypto/rsa: invalid prime") + } + if pN.Mul(qN, N).IsZero() != 1 { + return errors.New("crypto/rsa: p * q != n") + } + + // Check that de ≡ 1 mod p-1, and de ≡ 1 mod q-1. + // Uses byte-slice exponent for large exponents. + pMinus1, err := bigmod.NewModulus(p.Nat().SubOne(p).Bytes(p)) + if err != nil { + return errors.New("crypto/rsa: invalid prime") + } + dP, err := bigmod.NewNat().SetBytes(priv.dP, pMinus1) + if err != nil { + return errors.New("crypto/rsa: invalid CRT exponent") + } + de := bigmod.NewNat() + if _, err := de.SetBytes(priv.e, pMinus1); err != nil { + // Exponent might be larger than p-1, reduce it. + eNat, _ := bigmod.NewNat().SetBytes(priv.e, priv.n) + de.Mod(eNat, pMinus1) + } + de.Mul(dP, pMinus1) + if de.IsOne() != 1 { + return errors.New("crypto/rsa: invalid CRT exponent") + } + + qMinus1, err := bigmod.NewModulus(q.Nat().SubOne(q).Bytes(q)) + if err != nil { + return errors.New("crypto/rsa: invalid prime") + } + dQ, err := bigmod.NewNat().SetBytes(priv.dQ, qMinus1) + if err != nil { + return errors.New("crypto/rsa: invalid CRT exponent") + } + if _, err := de.SetBytes(priv.e, qMinus1); err != nil { + // Exponent might be larger than q-1, reduce it. + eNat, _ := bigmod.NewNat().SetBytes(priv.e, priv.n) + de.Mod(eNat, qMinus1) + } + de.Mul(dQ, qMinus1) + if de.IsOne() != 1 { + return errors.New("crypto/rsa: invalid CRT exponent") + } + + // Check that qInv * q ≡ 1 mod p. + qP, err := bigmod.NewNat().SetOverflowingBytes(q.Nat().Bytes(q), p) + if err != nil { + // q >= 2^⌈log2(p)⌉ + qP = bigmod.NewNat().Mod(q.Nat(), p) + } + if qP.Mul(priv.qInv, p).IsOne() != 1 { + return errors.New("crypto/rsa: invalid CRT coefficient") + } + + return nil +} + +func decryptLargeExponent(priv *TestingOnlyLargeExponentPrivateKey, ciphertext []byte) ([]byte, error) { + N := priv.n + c, err := bigmod.NewNat().SetBytes(ciphertext, N) + if err != nil { + return nil, ErrDecryption + } + + // CRT-based decryption (same as regular decrypt, doesn't use E). + P, Q := priv.p, priv.q + t0 := bigmod.NewNat() + // m = c ^ Dp mod p + m := bigmod.NewNat().Exp(t0.Mod(c, P), priv.dP, P) + // m2 = c ^ Dq mod q + m2 := bigmod.NewNat().Exp(t0.Mod(c, Q), priv.dQ, Q) + // m = m - m2 mod p + m.Sub(t0.Mod(m2, P), P) + // m = m * Qinv mod p + m.Mul(priv.qInv, P) + // m = m * q mod N + m.ExpandFor(N).Mul(t0.Mod(Q.Nat(), N), N) + // m = m + m2 mod N + m.Add(m2.ExpandFor(N), N) + + return m.Bytes(N), nil +} + +// TestingOnlyLargeExponentDecryptOAEP decrypts ciphertext using RSAES-OAEP with +// a private key that has a large public exponent. It is only meant for ACVP testing. +func TestingOnlyLargeExponentDecryptOAEP(hash, mgfHash hash.Hash, priv *TestingOnlyLargeExponentPrivateKey, ciphertext []byte, label []byte) ([]byte, error) { + fipsSelfTest() + fips140.RecordApproved() + checkApprovedHash(hash) + + k := priv.Size() + if len(ciphertext) > k || k < hash.Size()*2+2 { + return nil, ErrDecryption + } + + em, err := decryptLargeExponent(priv, ciphertext) + if err != nil { + return nil, err + } + + hash.Reset() + hash.Write(label) + lHash := hash.Sum(nil) + + firstByteIsZero := constanttime.ByteEq(em[0], 0) + + seed := em[1 : hash.Size()+1] + db := em[hash.Size()+1:] + + mgf1XOR(seed, mgfHash, db) + mgf1XOR(db, mgfHash, seed) + + lHash2 := db[0:hash.Size()] + + lHash2Good := subtle.ConstantTimeCompare(lHash, lHash2) + + var lookingForIndex, index, invalid int + lookingForIndex = 1 + rest := db[hash.Size():] + + for i := 0; i < len(rest); i++ { + equals0 := constanttime.ByteEq(rest[i], 0) + equals1 := constanttime.ByteEq(rest[i], 1) + index = constanttime.Select(lookingForIndex&equals1, i, index) + lookingForIndex = constanttime.Select(equals1, 0, lookingForIndex) + invalid = constanttime.Select(lookingForIndex&^equals0, 1, invalid) + } + + if firstByteIsZero&lHash2Good&^invalid&^lookingForIndex != 1 { + return nil, ErrDecryption + } + + return rest[index+1:], nil +} + +// TestingOnlyLargeExponentEncryptOAEP encrypts the given message with RSAES-OAEP +// using a public key with a large exponent. It is only meant for ACVP testing. +func TestingOnlyLargeExponentEncryptOAEP(hash, mgfHash hash.Hash, random io.Reader, pub *TestingOnlyLargeExponentPublicKey, msg []byte, label []byte) ([]byte, error) { + fipsSelfTest() + fips140.RecordApproved() + checkApprovedHash(hash) + if err := checkLargeExponentPublicKey(pub); err != nil { + return nil, err + } + k := pub.Size() + if len(msg) > k-2*hash.Size()-2 { + return nil, ErrMessageTooLong + } + + hash.Reset() + hash.Write(label) + lHash := hash.Sum(nil) + + em := make([]byte, k) + seed := em[1 : 1+hash.Size()] + db := em[1+hash.Size():] + + copy(db[0:hash.Size()], lHash) + db[len(db)-len(msg)-1] = 1 + copy(db[len(db)-len(msg):], msg) + + if err := drbg.ReadWithReader(random, seed); err != nil { + return nil, err + } + + mgf1XOR(db, mgfHash, seed) + mgf1XOR(seed, mgfHash, db) + + return encryptLargeExponent(pub, em) +} diff --git a/src/crypto/tls/bogo_config.json b/src/crypto/tls/bogo_config.json index d57b72aaf8fc06..0d2c1a709b5a85 100644 --- a/src/crypto/tls/bogo_config.json +++ b/src/crypto/tls/bogo_config.json @@ -34,7 +34,12 @@ "TLS-ECH-Server-EarlyDataRejected": "Go does not support early (0-RTT) data", "MLKEMKeyShareIncludedSecond": "BoGo wants us to order the key shares based on its preference, but we don't support that", + "MLKEMKeyShareIncludedSecond-*": "BoGo wants us to order the key shares based on its preference, but we don't support that", "MLKEMKeyShareIncludedThird": "BoGo wants us to order the key shares based on its preference, but we don't support that", + "MLKEMKeyShareIncludedThird-*": "BoGo wants us to order the key shares based on its preference, but we don't support that", + "TwoMLKEMs": "BoGo wants us to order the key shares based on its preference, but we don't support that", + "NotJustMLKEMKeyShare-MLKEM1024": "BoringSSL sends an ECC key share for fallback when the main key share is MLKEM1024, we currently don't", + "PostQuantumNotEnabledByDefaultInClients": "We do enable it by default!", "*-Kyber-TLS13": "We don't support Kyber, only ML-KEM (BoGo bug ignoring AllCurves?)", @@ -42,7 +47,7 @@ "*-Verify-RSA_PKCS1_SHA256_LEGACY-TLS12": "Likewise, we don't know how to handle it in TLS 1.2, so we send the wrong alert", "*-VerifyDefault-*": "Our signature algorithms are not configurable, so there is no difference between default and supported", "Ed25519DefaultDisable-*": "We support Ed25519 by default", - "NoCommonSignatureAlgorithms-TLS12-Fallback": "We don't support the legacy RSA exchange (without tlsrsakex=1)", + "NoCommonSignatureAlgorithms-TLS12-Fallback": "We don't support the legacy RSA exchange", "*_SHA1-TLS12": "We don't support SHA-1 in TLS 1.2 (without tlssha1=1)", "Agree-Digest-SHA1": "We don't support SHA-1 in TLS 1.2 (without tlssha1=1)", @@ -76,21 +81,6 @@ "PAKE-Extension-*": "We don't support PAKE", "*TicketFlags": "We don't support draft-ietf-tls-tlsflags", - "BothMLKEMAndKyber-MLKEM1024": "We don't support ML-KEM 1024 KEX", - "CurveTest-Client-MLKEM1024-TLS13": "We don't support ML-KEM 1024 KEX", - "CurveTest-Invalid-MLKEMEncapKeyNotReduced-Server-MLKEM1024-TLS13": "We don't support ML-KEM 1024 KEX", - "CurveTest-Invalid-PadKeyShare-Client-MLKEM1024-TLS13": "We don't support ML-KEM 1024 KEX", - "CurveTest-Invalid-PadKeyShare-Server-MLKEM1024-TLS13": "We don't support ML-KEM 1024 KEX", - "CurveTest-Invalid-TruncateKeyShare-Client-MLKEM1024-TLS13": "We don't support ML-KEM 1024 KEX", - "CurveTest-Invalid-TruncateKeyShare-Server-MLKEM1024-TLS13": "We don't support ML-KEM 1024 KEX", - "CurveTest-Server-MLKEM1024-TLS13": "We don't support ML-KEM 1024 KEX", - "JustConfiguringMLKEMWorks-MLKEM1024": "We don't support ML-KEM 1024 KEX", - "NotJustMLKEMKeyShare-MLKEM1024": "We don't support ML-KEM 1024 KEX", - "TwoMLKEMs": "We don't support ML-KEM 1024 KEX", - "MLKEMKeyShareIncludedSecond-MLKEM1024": "We don't support ML-KEM 1024 KEX", - "MLKEMKeyShareIncludedSecond-X25519MLKEM768": "We don't support ML-KEM 1024 KEX", - "MLKEMKeyShareIncludedThird-MLKEM1024": "We don't support ML-KEM 1024 KEX", - "MLKEMKeyShareIncludedThird-X25519MLKEM768": "We don't return key shares in client preference order", "ECDSAKeyUsage-*": "We don't enforce ECDSA KU", diff --git a/src/crypto/tls/bogo_shim_test.go b/src/crypto/tls/bogo_shim_test.go index 5e6110a8daf9bf..a0e7c57b31c443 100644 --- a/src/crypto/tls/bogo_shim_test.go +++ b/src/crypto/tls/bogo_shim_test.go @@ -625,6 +625,8 @@ func TestBogoSuite(t *testing.T) { assertResults := map[string]string{ "CurveTest-Client-X25519MLKEM768-TLS13": "PASS", "CurveTest-Server-X25519MLKEM768-TLS13": "PASS", + "CurveTest-Client-MLKEM1024-TLS13": "PASS", + "CurveTest-Server-MLKEM1024-TLS13": "PASS", // Various signature algorithm tests checking that we enforce our // preferences on the peer. diff --git a/src/crypto/tls/cipher_suites.go b/src/crypto/tls/cipher_suites.go index 7597aabe780a89..1952be64fa952c 100644 --- a/src/crypto/tls/cipher_suites.go +++ b/src/crypto/tls/cipher_suites.go @@ -346,25 +346,16 @@ var disabledCipherSuites = map[uint16]bool{ TLS_ECDHE_ECDSA_WITH_RC4_128_SHA: true, TLS_ECDHE_RSA_WITH_RC4_128_SHA: true, TLS_RSA_WITH_RC4_128_SHA: true, -} -// rsaKexCiphers contains the ciphers which use RSA based key exchange, -// which we also disable by default unless a GODEBUG is set. -var rsaKexCiphers = map[uint16]bool{ - TLS_RSA_WITH_RC4_128_SHA: true, + // RSA key exchange TLS_RSA_WITH_3DES_EDE_CBC_SHA: true, TLS_RSA_WITH_AES_128_CBC_SHA: true, TLS_RSA_WITH_AES_256_CBC_SHA: true, - TLS_RSA_WITH_AES_128_CBC_SHA256: true, TLS_RSA_WITH_AES_128_GCM_SHA256: true, TLS_RSA_WITH_AES_256_GCM_SHA384: true, -} -// tdesCiphers contains 3DES ciphers, -// which we also disable by default unless a GODEBUG is set. -var tdesCiphers = map[uint16]bool{ + // 3DES TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA: true, - TLS_RSA_WITH_3DES_EDE_CBC_SHA: true, } var ( diff --git a/src/crypto/tls/common.go b/src/crypto/tls/common.go index a4343c34f394cd..b2cb869650ca97 100644 --- a/src/crypto/tls/common.go +++ b/src/crypto/tls/common.go @@ -155,11 +155,12 @@ const ( X25519MLKEM768 CurveID = 4588 SecP256r1MLKEM768 CurveID = 4587 SecP384r1MLKEM1024 CurveID = 4589 + MLKEM1024 CurveID = 514 ) func isTLS13OnlyKeyExchange(curve CurveID) bool { switch curve { - case X25519MLKEM768, SecP256r1MLKEM768, SecP384r1MLKEM1024: + case X25519MLKEM768, SecP256r1MLKEM768, SecP384r1MLKEM1024, MLKEM1024: return true default: return false @@ -168,7 +169,7 @@ func isTLS13OnlyKeyExchange(curve CurveID) bool { func isPQKeyExchange(curve CurveID) bool { switch curve { - case X25519MLKEM768, SecP256r1MLKEM768, SecP384r1MLKEM1024: + case X25519MLKEM768, SecP256r1MLKEM768, SecP384r1MLKEM1024, MLKEM1024: return true default: return false @@ -337,11 +338,6 @@ type ConnectionState struct { // the seed. If the connection was set to allow renegotiation via // Config.Renegotiation, or if the connections supports neither TLS 1.3 nor // Extended Master Secret, this function will return an error. -// -// Exporting key material without Extended Master Secret or TLS 1.3 was disabled -// in Go 1.22 due to security issues (see the Security Considerations sections -// of RFC 5705 and RFC 7627), but can be re-enabled with the GODEBUG setting -// tlsunsafeekm=1. func (cs *ConnectionState) ExportKeyingMaterial(label string, context []byte, length int) ([]byte, error) { return cs.ekm(label, context, length) } @@ -499,6 +495,9 @@ type ClientHelloInfo struct { // for use with SupportsCertificate. config *Config + // isQUIC indicates whether the connection is a QUIC connection. + isQUIC bool + // ctx is the context of the handshake that is in progress. ctx context.Context } @@ -572,10 +571,13 @@ const ( // modified. A Config may be reused; the tls package will also not // modify it. type Config struct { - // Rand provides the source of entropy for nonces and RSA blinding. + // Rand provides the source of entropy for the connection. // If Rand is nil, TLS uses the cryptographic random reader in package - // crypto/rand. - // The Reader must be safe for use by multiple goroutines. + // crypto/rand. The Reader must be safe for use by multiple goroutines. + // + // Deprecated: this should be left nil in production. Not all TLS + // configurations are guaranteed to use Rand. Test code can use + // [testing/cryptotest.SetGlobalRandom] instead. Rand io.Reader // Time returns the current time as the number of seconds since the epoch. @@ -724,11 +726,7 @@ type Config struct { // the list is ignored. Note that TLS 1.3 ciphersuites are not configurable. // // If CipherSuites is nil, a safe default list is used. The default cipher - // suites might change over time. In Go 1.22 RSA key exchange based cipher - // suites were removed from the default list, but can be re-added with the - // GODEBUG setting tlsrsakex=1. In Go 1.23 3DES cipher suites were removed - // from the default list, but can be re-added with the GODEBUG setting - // tls3des=1. + // suites might change over time. CipherSuites []uint16 // PreferServerCipherSuites is a legacy field and has no effect. @@ -793,9 +791,6 @@ type Config struct { // // By default, TLS 1.2 is currently used as the minimum. TLS 1.0 is the // minimum supported by this package. - // - // The server-side default can be reverted to TLS 1.0 by including the value - // "tls10server=1" in the GODEBUG environment variable. MinVersion uint16 // MaxVersion contains the maximum TLS version that is acceptable. @@ -1076,7 +1071,6 @@ func (c *Config) initLegacySessionTicketKeyRLocked() { } else if !bytes.HasPrefix(c.SessionTicketKey[:], deprecatedSessionTicketKey) && len(c.sessionTicketKeys) == 0 { c.sessionTicketKeys = []ticketKey{c.ticketKeyFromBytes(c.SessionTicketKey)} } - } // ticketKeys returns the ticketKeys for this connection. @@ -1224,20 +1218,16 @@ var supportedVersions = []uint16{ const roleClient = true const roleServer = false -var tls10server = godebug.New("tls10server") - // supportedVersions returns the list of supported TLS versions, sorted from // highest to lowest (and hence also in preference order). -func (c *Config) supportedVersions(isClient bool) []uint16 { +func (c *Config) supportedVersions(isClient, isQUIC bool) []uint16 { versions := make([]uint16, 0, len(supportedVersions)) for _, v := range supportedVersions { if fips140tls.Required() && !slices.Contains(allowedSupportedVersionsFIPS, v) { continue } if (c == nil || c.MinVersion == 0) && v < VersionTLS12 { - if isClient || tls10server.Value() != "1" { - continue - } + continue } if isClient && c.EncryptedClientHelloConfigList != nil && v < VersionTLS13 { continue @@ -1248,13 +1238,16 @@ func (c *Config) supportedVersions(isClient bool) []uint16 { if c != nil && c.MaxVersion != 0 && v > c.MaxVersion { continue } + if isQUIC && v < VersionTLS13 { + continue + } versions = append(versions, v) } return versions } -func (c *Config) maxSupportedVersion(isClient bool) uint16 { - supportedVersions := c.supportedVersions(isClient) +func (c *Config) maxSupportedVersion(isClient, isQUIC bool) uint16 { + supportedVersions := c.supportedVersions(isClient, isQUIC) if len(supportedVersions) == 0 { return 0 } @@ -1276,31 +1269,38 @@ func supportedVersionsFromMax(maxVersion uint16) []uint16 { } func (c *Config) curvePreferences(version uint16) []CurveID { - curvePreferences := defaultCurvePreferences() - if fips140tls.Required() { - curvePreferences = slices.DeleteFunc(curvePreferences, func(x CurveID) bool { - return !slices.Contains(allowedCurvePreferencesFIPS, x) - }) - } + return slices.DeleteFunc(curvePreferenceOrder(), func(x CurveID) bool { + return !c.supportsCurve(version, x) + }) +} + +func (c *Config) supportsCurve(version uint16, x CurveID) bool { if c != nil && len(c.CurvePreferences) != 0 { - curvePreferences = slices.DeleteFunc(curvePreferences, func(x CurveID) bool { - return !slices.Contains(c.CurvePreferences, x) - }) + if !slices.Contains(c.CurvePreferences, x) { + return false + } + // Ignore unimplemented entries in c.CurvePreferences. + if !slices.Contains(curvePreferenceOrder(), x) { + return false + } + } else { + if !defaultCurveEnabled(x) { + return false + } } - if version < VersionTLS13 { - curvePreferences = slices.DeleteFunc(curvePreferences, isTLS13OnlyKeyExchange) + if fips140tls.Required() && !slices.Contains(allowedCurvePreferencesFIPS, x) { + return false } - return curvePreferences -} - -func (c *Config) supportsCurve(version uint16, curve CurveID) bool { - return slices.Contains(c.curvePreferences(version), curve) + if version < VersionTLS13 && isTLS13OnlyKeyExchange(x) { + return false + } + return true } // mutualVersion returns the protocol version to use given the advertised // versions of the peer. The highest supported version is preferred. -func (c *Config) mutualVersion(isClient bool, peerVersions []uint16) (uint16, bool) { - supportedVersions := c.supportedVersions(isClient) +func (c *Config) mutualVersion(isClient, isQUIC bool, peerVersions []uint16) (uint16, bool) { + supportedVersions := c.supportedVersions(isClient, isQUIC) for _, v := range supportedVersions { if slices.Contains(peerVersions, v) { return v, true @@ -1386,7 +1386,7 @@ func (chi *ClientHelloInfo) SupportsCertificate(c *Certificate) error { if config == nil { config = &Config{} } - vers, ok := config.mutualVersion(roleServer, chi.SupportedVersions) + vers, ok := config.mutualVersion(roleServer, chi.isQUIC, chi.SupportedVersions) if !ok { return errors.New("no mutually supported protocol versions") } diff --git a/src/crypto/tls/common_string.go b/src/crypto/tls/common_string.go index 04c7283dd97576..6bbaf1597f59d0 100644 --- a/src/crypto/tls/common_string.go +++ b/src/crypto/tls/common_string.go @@ -82,17 +82,19 @@ func _() { _ = x[X25519MLKEM768-4588] _ = x[SecP256r1MLKEM768-4587] _ = x[SecP384r1MLKEM1024-4589] + _ = x[MLKEM1024-514] } const ( _CurveID_name_0 = "CurveP256CurveP384CurveP521" _CurveID_name_1 = "X25519" - _CurveID_name_2 = "SecP256r1MLKEM768X25519MLKEM768SecP384r1MLKEM1024" + _CurveID_name_2 = "MLKEM1024" + _CurveID_name_3 = "SecP256r1MLKEM768X25519MLKEM768SecP384r1MLKEM1024" ) var ( _CurveID_index_0 = [...]uint8{0, 9, 18, 27} - _CurveID_index_2 = [...]uint8{0, 17, 31, 49} + _CurveID_index_3 = [...]uint8{0, 17, 31, 49} ) func (i CurveID) String() string { @@ -102,9 +104,11 @@ func (i CurveID) String() string { return _CurveID_name_0[_CurveID_index_0[i]:_CurveID_index_0[i+1]] case i == 29: return _CurveID_name_1 + case i == 514: + return _CurveID_name_2 case 4587 <= i && i <= 4589: i -= 4587 - return _CurveID_name_2[_CurveID_index_2[i]:_CurveID_index_2[i+1]] + return _CurveID_name_3[_CurveID_index_3[i]:_CurveID_index_3[i+1]] default: return "CurveID(" + strconv.FormatInt(int64(i), 10) + ")" } diff --git a/src/crypto/tls/conn.go b/src/crypto/tls/conn.go index 7ce69e914a60b7..b4508d6a812303 100644 --- a/src/crypto/tls/conn.go +++ b/src/crypto/tls/conn.go @@ -15,7 +15,6 @@ import ( "errors" "fmt" "hash" - "internal/godebug" "io" "net" "sync" @@ -1610,8 +1609,6 @@ func (c *Conn) ConnectionState() ConnectionState { return c.connectionStateLocked() } -var tlsunsafeekm = godebug.New("tlsunsafeekm") - func (c *Conn) connectionStateLocked() ConnectionState { var state ConnectionState state.HandshakeComplete = c.isHandshakeComplete.Load() @@ -1638,13 +1635,7 @@ func (c *Conn) connectionStateLocked() ConnectionState { if c.config.Renegotiation != RenegotiateNever { state.ekm = noEKMBecauseRenegotiation } else if c.vers != VersionTLS13 && !c.extMasterSecret { - state.ekm = func(label string, context []byte, length int) ([]byte, error) { - if tlsunsafeekm.Value() == "1" { - tlsunsafeekm.IncNonDefault() - return c.ekm(label, context, length) - } - return noEKMBecauseNoEMS(label, context, length) - } + state.ekm = noEKMBecauseNoEMS } else { state.ekm = c.ekm } diff --git a/src/crypto/tls/conn_test.go b/src/crypto/tls/conn_test.go index d272abf3bb601e..9fa09d97deceda 100644 --- a/src/crypto/tls/conn_test.go +++ b/src/crypto/tls/conn_test.go @@ -124,6 +124,37 @@ func TestCertificateSelection(t *testing.T) { } } +// TestBrokenCertificateSkipped checks that a Certificate in Config.Certificates +// whose leaf doesn't parse as X.509 doesn't prevent the next, valid certificate +// from being selected. It exercises both the legacy BuildNameToCertificate path +// and the SupportsCertificate-based selection. +func TestBrokenCertificateSkipped(t *testing.T) { + brokenCert := Certificate{Certificate: [][]byte{[]byte("not a valid X.509 certificate")}} + for _, test := range []struct { + name string + buildIndex bool + }{ + {name: "BuildNameToCertificate", buildIndex: true}, + {name: "SupportsCertificate", buildIndex: false}, + } { + t.Run(test.name, func(t *testing.T) { + serverConfig := testConfigServer.Clone() + serverConfig.Certificates = []Certificate{brokenCert, testECDSAP256Cert} + if test.buildIndex { + serverConfig.BuildNameToCertificate() + } + clientConfig := testConfigClient.Clone() + _, cs, err := testHandshake(t, clientConfig, serverConfig) + if err != nil { + t.Fatalf("handshake failed: %v", err) + } + if !cs.PeerCertificates[0].Equal(testECDSAP256Cert.Leaf) { + t.Fatalf("handshake succeeded but wrong certificate was used") + } + }) + } +} + // Run with multiple crypto configs to test the logic for computing TLS record overheads. func runDynamicRecordSizingTest(t *testing.T, serverConfig *Config) { clientConn, serverConn := localPipe(t) diff --git a/src/crypto/tls/defaults.go b/src/crypto/tls/defaults.go index 8bacaed29f60d2..c89036047ad5a3 100644 --- a/src/crypto/tls/defaults.go +++ b/src/crypto/tls/defaults.go @@ -13,24 +13,32 @@ import ( // Defaults are collected in this file to allow distributions to more easily patch // them to apply local policies. +// tlsmlkem=0 restores the pre-Go 1.24 default key exchanges. var tlsmlkem = godebug.New("tlsmlkem") + +// tlssecpmlkem=0 restores the pre-Go 1.26 default key exchanges. var tlssecpmlkem = godebug.New("tlssecpmlkem") -// defaultCurvePreferences is the default set of supported key exchanges, as -// well as the preference order. -func defaultCurvePreferences() []CurveID { - switch { - // tlsmlkem=0 restores the pre-Go 1.24 default. - case tlsmlkem.Value() == "0": - return []CurveID{X25519, CurveP256, CurveP384, CurveP521} - // tlssecpmlkem=0 restores the pre-Go 1.26 default. - case tlssecpmlkem.Value() == "0": - return []CurveID{X25519MLKEM768, X25519, CurveP256, CurveP384, CurveP521} +// defaultCurveEnabled returns whether the key exchange c is enabled by default. +func defaultCurveEnabled(c CurveID) bool { + switch c { + case X25519, CurveP256, CurveP384, CurveP521: + return true + case X25519MLKEM768: + return tlsmlkem.Value() != "0" + case SecP256r1MLKEM768, SecP384r1MLKEM1024: + return tlsmlkem.Value() != "0" && tlssecpmlkem.Value() != "0" default: - return []CurveID{ - X25519MLKEM768, SecP256r1MLKEM768, SecP384r1MLKEM1024, - X25519, CurveP256, CurveP384, CurveP521, - } + return false + } +} + +// curvePreferenceOrder is the fixed preference order of key exchanges. It must +// include every supported key exchange. +func curvePreferenceOrder() []CurveID { + return []CurveID{ + X25519MLKEM768, SecP256r1MLKEM768, SecP384r1MLKEM1024, MLKEM1024, + X25519, CurveP256, CurveP384, CurveP521, } } @@ -58,9 +66,6 @@ func defaultSupportedSignatureAlgorithms() []SignatureScheme { } } -var tlsrsakex = godebug.New("tlsrsakex") -var tls3des = godebug.New("tls3des") - func supportedCipherSuites(aesGCMPreferred bool) []uint16 { if aesGCMPreferred { return slices.Clone(cipherSuitesPreferenceOrder) @@ -72,9 +77,7 @@ func supportedCipherSuites(aesGCMPreferred bool) []uint16 { func defaultCipherSuites(aesGCMPreferred bool) []uint16 { cipherSuites := supportedCipherSuites(aesGCMPreferred) return slices.DeleteFunc(cipherSuites, func(c uint16) bool { - return disabledCipherSuites[c] || - tlsrsakex.Value() != "1" && rsaKexCiphers[c] || - tls3des.Value() != "1" && tdesCiphers[c] + return disabledCipherSuites[c] }) } diff --git a/src/crypto/tls/defaults_fips140.go b/src/crypto/tls/defaults_fips140.go index e1c935df2f26dd..1c713898ed1832 100644 --- a/src/crypto/tls/defaults_fips140.go +++ b/src/crypto/tls/defaults_fips140.go @@ -36,6 +36,7 @@ var ( X25519MLKEM768, SecP256r1MLKEM768, SecP384r1MLKEM1024, + MLKEM1024, CurveP256, CurveP384, CurveP521, diff --git a/src/crypto/tls/example_test.go b/src/crypto/tls/example_test.go index 95e4953fb2e0c3..aa9419a5955a53 100644 --- a/src/crypto/tls/example_test.go +++ b/src/crypto/tls/example_test.go @@ -14,14 +14,6 @@ import ( "time" ) -// zeroSource is an io.Reader that returns an unlimited number of zero bytes. -type zeroSource struct{} - -func (zeroSource) Read(b []byte) (n int, err error) { - clear(b) - return len(b), nil -} - func ExampleDial() { // Connecting with a custom root-certificate set. @@ -70,19 +62,17 @@ TBj0/VLZjmmx6BEP3ojY+x1J96relc8geMJgEtslQIxq/H5COEBkEveegeGTLg== func ExampleConfig_keyLogWriter() { // Debugging TLS applications by decrypting a network traffic capture. - // WARNING: Use of KeyLogWriter compromises security and should only be // used for debugging. - // Dummy test HTTP server for the example with insecure random so output is - // reproducible. - server := httptest.NewUnstartedServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {})) - server.TLS = &tls.Config{ - Rand: zeroSource{}, // for example only; don't do this. - } + server := httptest.NewUnstartedServer(http.HandlerFunc( + func(w http.ResponseWriter, r *http.Request) {})) server.StartTLS() defer server.Close() + pool := x509.NewCertPool() + pool.AddCert(server.Certificate()) + // Typically the log would go to an open file: // w, err := os.OpenFile("tls-secrets.txt", os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0600) w := os.Stdout @@ -91,9 +81,7 @@ func ExampleConfig_keyLogWriter() { Transport: &http.Transport{ TLSClientConfig: &tls.Config{ KeyLogWriter: w, - - Rand: zeroSource{}, // for reproducible output; don't do this. - InsecureSkipVerify: true, // test server certificate is not trusted. + RootCAs: pool, }, }, } diff --git a/src/crypto/tls/fips140_test.go b/src/crypto/tls/fips140_test.go index 1f0702b6574642..c3f6ec917b5632 100644 --- a/src/crypto/tls/fips140_test.go +++ b/src/crypto/tls/fips140_test.go @@ -151,7 +151,7 @@ func isFIPSCurve(id CurveID) bool { switch id { case CurveP256, CurveP384, CurveP521: return true - case X25519MLKEM768, SecP256r1MLKEM768, SecP384r1MLKEM1024: + case X25519MLKEM768, SecP256r1MLKEM768, SecP384r1MLKEM1024, MLKEM1024: // Only for the native module. return !boring.Enabled case X25519: @@ -233,7 +233,7 @@ func TestFIPSServerCipherSuites(t *testing.T) { } func TestFIPSServerCurves(t *testing.T) { - for _, curveid := range defaultCurvePreferences() { + for _, curveid := range curvePreferenceOrder() { t.Run(fmt.Sprintf("curve=%v", curveid), func(t *testing.T) { testConfig := testConfigFIPS140.Clone() testConfig.CurvePreferences = []CurveID{curveid} @@ -342,7 +342,7 @@ func testFIPSClientHello(t *testing.T) { clientConfig.MinVersion = VersionSSL30 clientConfig.MaxVersion = VersionTLS13 clientConfig.CipherSuites = allCipherSuitesIncludingTLS13() - clientConfig.CurvePreferences = defaultCurvePreferences() + clientConfig.CurvePreferences = curvePreferenceOrder() go Client(c, clientConfig).Handshake() srv := Server(s, testConfigFIPS140) diff --git a/src/crypto/tls/handshake_client.go b/src/crypto/tls/handshake_client.go index 9c07a3e2833484..52bf6087b62955 100644 --- a/src/crypto/tls/handshake_client.go +++ b/src/crypto/tls/handshake_client.go @@ -59,7 +59,7 @@ func (c *Conn) makeClientHello() (*clientHelloMsg, *keySharePrivateKeys, *echCli return nil, nil, nil, errors.New("tls: NextProtos values too large") } - supportedVersions := config.supportedVersions(roleClient) + supportedVersions := config.supportedVersions(roleClient, c.quic != nil) if len(supportedVersions) == 0 { return nil, nil, nil, errors.New("tls: no supported versions satisfy MinVersion and MaxVersion") } @@ -140,7 +140,7 @@ func (c *Conn) makeClientHello() (*clientHelloMsg, *keySharePrivateKeys, *echCli } if len(hello.supportedCurves) == 0 { - return nil, nil, nil, errors.New("tls: no supported elliptic curves for ECDHE") + return nil, nil, nil, errors.New("tls: no supported key exchange methods (CurveIDs)") } // Since the order is fixed, the first one is always the one to send a // key share for. All the PQ hybrids sort first, and produce a fallback @@ -148,7 +148,7 @@ func (c *Conn) makeClientHello() (*clientHelloMsg, *keySharePrivateKeys, *echCli curveID := hello.supportedCurves[0] ke, err := keyExchangeForCurveID(curveID) if err != nil { - return nil, nil, nil, errors.New("tls: CurvePreferences includes unsupported curve") + return nil, nil, nil, errors.New("tls: internal error: supportsCurve accepted unimplemented curve") } keyShareKeys, hello.keyShares, err = ke.keyShares(config.rand()) if err != nil { @@ -316,7 +316,7 @@ func (c *Conn) clientHandshake(ctx context.Context) (err error) { // If we are negotiating a protocol version that's lower than what we // support, check for the server downgrade canaries. // See RFC 8446, Section 4.1.3. - maxVers := c.config.maxSupportedVersion(roleClient) + maxVers := c.config.maxSupportedVersion(roleClient, c.quic != nil) tls12Downgrade := string(serverHello.random[24:]) == downgradeCanaryTLS12 tls11Downgrade := string(serverHello.random[24:]) == downgradeCanaryTLS11 if maxVers == VersionTLS13 && c.vers <= VersionTLS12 && (tls12Downgrade || tls11Downgrade) || @@ -508,7 +508,7 @@ func (c *Conn) pickTLSVersion(serverHello *serverHelloMsg) error { peerVersion = serverHello.supportedVersion } - vers, ok := c.config.mutualVersion(roleClient, []uint16{peerVersion}) + vers, ok := c.config.mutualVersion(roleClient, c.quic != nil, []uint16{peerVersion}) if !ok { c.sendAlert(alertProtocolVersion) return fmt.Errorf("tls: server selected unsupported protocol version %x", peerVersion) @@ -627,15 +627,6 @@ func (hs *clientHandshakeState) pickCipherSuite() error { return errors.New("tls: server chose an unconfigured cipher suite") } - if hs.c.config.CipherSuites == nil && !fips140tls.Required() && rsaKexCiphers[hs.suite.id] { - tlsrsakex.Value() // ensure godebug is initialized - tlsrsakex.IncNonDefault() - } - if hs.c.config.CipherSuites == nil && !fips140tls.Required() && tdesCiphers[hs.suite.id] { - tls3des.Value() // ensure godebug is initialized - tls3des.IncNonDefault() - } - hs.c.cipherSuite = hs.suite.id return nil } diff --git a/src/crypto/tls/handshake_client_test.go b/src/crypto/tls/handshake_client_test.go index 7cf40ee5bcdd14..64a668eae9b512 100644 --- a/src/crypto/tls/handshake_client_test.go +++ b/src/crypto/tls/handshake_client_test.go @@ -1211,12 +1211,10 @@ func TestKeyLogTLS12(t *testing.T) { clientConfig := testConfigClient.Clone() clientConfig.KeyLogWriter = &clientBuf clientConfig.MaxVersion = VersionTLS12 - clientConfig.Rand = zeroSource{} serverConfig := testConfigServer.Clone() serverConfig.KeyLogWriter = &serverBuf serverConfig.MaxVersion = VersionTLS12 - serverConfig.Rand = zeroSource{} c, s := localPipe(t) done := make(chan bool) @@ -1242,17 +1240,23 @@ func TestKeyLogTLS12(t *testing.T) { if len(loggedLine) == 0 { t.Fatalf("%s: no keylog line was produced", side) } - const expectedLen = 13 /* "CLIENT_RANDOM" */ + - 1 /* space */ + - 32*2 /* hex client nonce */ + - 1 /* space */ + - 48*2 /* hex master secret */ + - 1 /* new line */ - if len(loggedLine) != expectedLen { - t.Fatalf("%s: keylog line has incorrect length (want %d, got %d): %q", side, expectedLen, len(loggedLine), loggedLine) + rest, ok := strings.CutSuffix(loggedLine, "\n") + if !ok { + t.Fatalf("%s: keylog line is missing trailing newline: %q", side, loggedLine) + } + label, rest, ok := strings.Cut(rest, " ") + if !ok || label != "CLIENT_RANDOM" { + t.Fatalf("%s: keylog line has incorrect label: %q", side, loggedLine) + } + clientRandom, masterSecret, ok := strings.Cut(rest, " ") + if !ok { + t.Fatalf("%s: keylog line is missing master secret: %q", side, loggedLine) + } + if b, err := hex.DecodeString(clientRandom); err != nil || len(b) != 32 { + t.Fatalf("%s: keylog line has invalid client random: %q", side, loggedLine) } - if !strings.HasPrefix(loggedLine, "CLIENT_RANDOM "+strings.Repeat("0", 64)+" ") { - t.Fatalf("%s: keylog line has incorrect structure or nonce: %q", side, loggedLine) + if b, err := hex.DecodeString(masterSecret); err != nil || len(b) != 48 { + t.Fatalf("%s: keylog line has invalid master secret: %q", side, loggedLine) } } @@ -1265,11 +1269,9 @@ func TestKeyLogTLS13(t *testing.T) { clientConfig := testConfigClient.Clone() clientConfig.KeyLogWriter = &clientBuf - clientConfig.Rand = zeroSource{} serverConfig := testConfigServer.Clone() serverConfig.KeyLogWriter = &serverBuf - serverConfig.Rand = zeroSource{} c, s := localPipe(t) done := make(chan bool) diff --git a/src/crypto/tls/handshake_client_tls13.go b/src/crypto/tls/handshake_client_tls13.go index 4ddd05284c15b0..d367020c2066bf 100644 --- a/src/crypto/tls/handshake_client_tls13.go +++ b/src/crypto/tls/handshake_client_tls13.go @@ -54,7 +54,8 @@ func (hs *clientHandshakeStateTLS13) handshake() error { } // Consistency check on the presence of a keyShare and its parameters. - if hs.keyShareKeys == nil || hs.keyShareKeys.ecdhe == nil || len(hs.hello.keyShares) == 0 { + if hs.keyShareKeys == nil || (hs.keyShareKeys.ecdhe == nil && hs.keyShareKeys.mlkem == nil) || + len(hs.hello.keyShares) == 0 { return c.sendAlert(alertInternalError) } @@ -322,7 +323,7 @@ func (hs *clientHandshakeStateTLS13) processHelloRetryRequest() error { ke, err := keyExchangeForCurveID(curveID) if err != nil { c.sendAlert(alertInternalError) - return errors.New("tls: CurvePreferences includes unsupported curve") + return errors.New("tls: internal error: supportsCurve accepted unimplemented curve") } hs.keyShareKeys, hello.keyShares, err = ke.keyShares(c.config.rand()) if err != nil { diff --git a/src/crypto/tls/handshake_server.go b/src/crypto/tls/handshake_server.go index 92f3e979bf8b84..cde70463ef0fa4 100644 --- a/src/crypto/tls/handshake_server.go +++ b/src/crypto/tls/handshake_server.go @@ -188,7 +188,7 @@ func (c *Conn) readClientHello(ctx context.Context) (*clientHelloMsg, *echServer } else if len(clientVersions) == 0 { clientVersions = supportedVersionsFromMax(clientHello.vers) } - c.vers, ok = c.config.mutualVersion(roleServer, clientVersions) + c.vers, ok = c.config.mutualVersion(roleServer, c.quic != nil, clientVersions) if !ok { c.sendAlert(alertProtocolVersion) return nil, nil, fmt.Errorf("tls: client offered only unsupported versions: %x", clientVersions) @@ -209,11 +209,6 @@ func (c *Conn) readClientHello(ctx context.Context) (*clientHelloMsg, *echServer return nil, nil, errors.New("tls: Encrypted Client Hello cannot be used pre-TLS 1.3") } - if c.config.MinVersion == 0 && c.vers < VersionTLS12 { - tls10server.Value() // ensure godebug is initialized - tls10server.IncNonDefault() - } - return clientHello, ech, nil } @@ -240,7 +235,7 @@ func (hs *serverHandshakeState) processClientHello() error { hs.hello.random = make([]byte, 32) serverRandom := hs.hello.random // Downgrade protection canaries. See RFC 8446, Section 4.1.3. - maxVers := c.config.maxSupportedVersion(roleServer) + maxVers := c.config.maxSupportedVersion(roleServer, c.quic != nil) if maxVers >= VersionTLS12 && c.vers < maxVers || testingOnlyForceDowngradeCanary { if c.vers == VersionTLS12 { copy(serverRandom[24:], downgradeCanaryTLS12) @@ -412,19 +407,10 @@ func (hs *serverHandshakeState) pickCipherSuite() error { } c.cipherSuite = hs.suite.id - if c.config.CipherSuites == nil && !fips140tls.Required() && rsaKexCiphers[hs.suite.id] { - tlsrsakex.Value() // ensure godebug is initialized - tlsrsakex.IncNonDefault() - } - if c.config.CipherSuites == nil && !fips140tls.Required() && tdesCiphers[hs.suite.id] { - tls3des.Value() // ensure godebug is initialized - tls3des.IncNonDefault() - } - for _, id := range hs.clientHello.cipherSuites { if id == TLS_FALLBACK_SCSV { // The client is doing a fallback connection. See RFC 7507. - if hs.clientHello.vers < c.config.maxSupportedVersion(roleServer) { + if hs.clientHello.vers < c.config.maxSupportedVersion(roleServer, c.quic != nil) { c.sendAlert(alertInappropriateFallback) return errors.New("tls: client using inappropriate protocol fallback") } @@ -1051,6 +1037,7 @@ func clientHelloInfo(ctx context.Context, c *Conn, clientHello *clientHelloMsg) Conn: conn, HelloRetryRequest: c.didHRR, config: c.config, + isQUIC: c.quic != nil, ctx: ctx, } } diff --git a/src/crypto/tls/handshake_server_tls13.go b/src/crypto/tls/handshake_server_tls13.go index 48e0cf50a02542..3175d74587d83a 100644 --- a/src/crypto/tls/handshake_server_tls13.go +++ b/src/crypto/tls/handshake_server_tls13.go @@ -132,7 +132,7 @@ func (hs *serverHandshakeStateTLS13) processClientHello() error { if id == TLS_FALLBACK_SCSV { // Use c.vers instead of max(supported_versions) because an attacker // could defeat this by adding an arbitrary high version otherwise. - if c.vers < c.config.maxSupportedVersion(roleServer) { + if c.vers < c.config.maxSupportedVersion(roleServer, c.quic != nil) { c.sendAlert(alertInappropriateFallback) return errors.New("tls: client using inappropriate protocol fallback") } @@ -249,7 +249,7 @@ func (hs *serverHandshakeStateTLS13) processClientHello() error { ke, err := keyExchangeForCurveID(selectedGroup) if err != nil { c.sendAlert(alertInternalError) - return errors.New("tls: CurvePreferences includes unsupported curve") + return errors.New("tls: internal error: supportsCurve accepted unimplemented curve") } hs.sharedKey, hs.hello.serverShare, err = ke.serverSharedSecret(c.config.rand(), clientKeyShare.data) if err != nil { diff --git a/src/crypto/tls/handshake_test.go b/src/crypto/tls/handshake_test.go index 9b72b1f9c7c319..3205dd97d68f40 100644 --- a/src/crypto/tls/handshake_test.go +++ b/src/crypto/tls/handshake_test.go @@ -394,14 +394,6 @@ Dialing: panic("unreachable") } -// zeroSource is an io.Reader that returns an unlimited number of zero bytes. -type zeroSource struct{} - -func (zeroSource) Read(b []byte) (n int, err error) { - clear(b) - return len(b), nil -} - func TestMain(m *testing.M) { flag.Usage = func() { fmt.Fprintf(flag.CommandLine.Output(), "Usage of %s:\n", os.Args) diff --git a/src/crypto/tls/key_agreement.go b/src/crypto/tls/key_agreement.go index 126a29ffa6807a..eef729d53d753d 100644 --- a/src/crypto/tls/key_agreement.go +++ b/src/crypto/tls/key_agreement.go @@ -167,7 +167,7 @@ func (ka *ecdheKeyAgreement) generateServerKeyExchange(config *Config, cert *Cer return nil, errors.New("tls: no supported elliptic curves offered") } if _, ok := curveForCurveID(ka.curveID); !ok { - return nil, errors.New("tls: CurvePreferences includes unsupported curve") + return nil, errors.New("tls: internal error: supportsCurve accepted unimplemented curve") } key, err := generateECDHEKey(config.rand(), ka.curveID) diff --git a/src/crypto/tls/key_schedule.go b/src/crypto/tls/key_schedule.go index 97f4993a179939..652a6489b899d6 100644 --- a/src/crypto/tls/key_schedule.go +++ b/src/crypto/tls/key_schedule.go @@ -74,16 +74,16 @@ type keyExchange interface { } func keyExchangeForCurveID(id CurveID) (keyExchange, error) { - newMLKEMPrivateKey768 := func(b []byte) (crypto.Decapsulator, error) { - return mlkem.NewDecapsulationKey768(b) + mlkemGenerateKey768 := func() (crypto.Decapsulator, error) { + return mlkem.GenerateKey768() } - newMLKEMPrivateKey1024 := func(b []byte) (crypto.Decapsulator, error) { - return mlkem.NewDecapsulationKey1024(b) + mlkemGenerateKey1024 := func() (crypto.Decapsulator, error) { + return mlkem.GenerateKey1024() } - newMLKEMPublicKey768 := func(b []byte) (crypto.Encapsulator, error) { + mlkemNewPublicKey768 := func(b []byte) (crypto.Encapsulator, error) { return mlkem.NewEncapsulationKey768(b) } - newMLKEMPublicKey1024 := func(b []byte) (crypto.Encapsulator, error) { + mlkemNewPublicKey1024 := func(b []byte) (crypto.Encapsulator, error) { return mlkem.NewEncapsulationKey1024(b) } switch id { @@ -98,20 +98,49 @@ func keyExchangeForCurveID(id CurveID) (keyExchange, error) { case X25519MLKEM768: return &hybridKeyExchange{id, ecdhKeyExchange{X25519, ecdh.X25519()}, 32, mlkem.EncapsulationKeySize768, mlkem.CiphertextSize768, - newMLKEMPrivateKey768, newMLKEMPublicKey768}, nil + mlkemGenerateKey768, mlkemNewPublicKey768}, nil case SecP256r1MLKEM768: return &hybridKeyExchange{id, ecdhKeyExchange{CurveP256, ecdh.P256()}, 65, mlkem.EncapsulationKeySize768, mlkem.CiphertextSize768, - newMLKEMPrivateKey768, newMLKEMPublicKey768}, nil + mlkemGenerateKey768, mlkemNewPublicKey768}, nil case SecP384r1MLKEM1024: return &hybridKeyExchange{id, ecdhKeyExchange{CurveP384, ecdh.P384()}, 97, mlkem.EncapsulationKeySize1024, mlkem.CiphertextSize1024, - newMLKEMPrivateKey1024, newMLKEMPublicKey1024}, nil + mlkemGenerateKey1024, mlkemNewPublicKey1024}, nil + case MLKEM1024: + return &mlkem1024KeyExchange{}, nil default: return nil, errors.New("tls: unsupported key exchange") } } +type mlkem1024KeyExchange struct{} + +func (ke *mlkem1024KeyExchange) keyShares(_ io.Reader) (*keySharePrivateKeys, []keyShare, error) { + priv, err := mlkem.GenerateKey1024() + if err != nil { + return nil, nil, err + } + return &keySharePrivateKeys{mlkem: priv}, []keyShare{{MLKEM1024, priv.EncapsulationKey().Bytes()}}, nil +} + +func (ke *mlkem1024KeyExchange) serverSharedSecret(_ io.Reader, clientKeyShare []byte) ([]byte, keyShare, error) { + peerKey, err := mlkem.NewEncapsulationKey1024(clientKeyShare) + if err != nil { + return nil, keyShare{}, err + } + sharedKey, keyShareData := peerKey.Encapsulate() + return sharedKey, keyShare{MLKEM1024, keyShareData}, nil +} + +func (ke *mlkem1024KeyExchange) clientSharedSecret(priv *keySharePrivateKeys, serverKeyShare []byte) ([]byte, error) { + sharedKey, err := priv.mlkem.Decapsulate(serverKeyShare) + if err != nil { + return nil, err + } + return sharedKey, nil +} + type ecdhKeyExchange struct { id CurveID curve ecdh.Curve @@ -161,8 +190,8 @@ type hybridKeyExchange struct { mlkemPublicKeySize int mlkemCiphertextSize int - newMLKEMPrivateKey func([]byte) (crypto.Decapsulator, error) - newMLKEMPublicKey func([]byte) (crypto.Encapsulator, error) + mlkemGenerateKey func() (crypto.Decapsulator, error) + mlkemNewPublicKey func([]byte) (crypto.Encapsulator, error) } func (ke *hybridKeyExchange) keyShares(rand io.Reader) (*keySharePrivateKeys, []keyShare, error) { @@ -177,11 +206,7 @@ func (ke *hybridKeyExchange) keyShares(rand io.Reader) (*keySharePrivateKeys, [] if err != nil { return nil, nil, err } - seed := make([]byte, mlkem.SeedSize) - if _, err := io.ReadFull(rand, seed); err != nil { - return nil, nil, err - } - priv.mlkem, err = ke.newMLKEMPrivateKey(seed) + priv.mlkem, err = ke.mlkemGenerateKey() if err != nil { return nil, nil, err } @@ -220,7 +245,7 @@ func (ke *hybridKeyExchange) serverSharedSecret(rand io.Reader, clientKeyShare [ if err != nil { return nil, keyShare{}, err } - mlkemPeerKey, err := ke.newMLKEMPublicKey(mlkemShareData) + mlkemPeerKey, err := ke.mlkemNewPublicKey(mlkemShareData) if err != nil { return nil, keyShare{}, err } diff --git a/src/crypto/tls/prf.go b/src/crypto/tls/prf.go index 19f80ac2c91c20..bc59300f41c762 100644 --- a/src/crypto/tls/prf.go +++ b/src/crypto/tls/prf.go @@ -248,7 +248,7 @@ func noEKMBecauseRenegotiation(label string, context []byte, length int) ([]byte // Master Secret is not negotiated and thus we wish to fail all key-material // export requests. func noEKMBecauseNoEMS(label string, context []byte, length int) ([]byte, error) { - return nil, errors.New("crypto/tls: ExportKeyingMaterial is unavailable when neither TLS 1.3 nor Extended Master Secret are negotiated; override with GODEBUG=tlsunsafeekm=1") + return nil, errors.New("crypto/tls: ExportKeyingMaterial is unavailable when neither TLS 1.3 nor Extended Master Secret are negotiated") } // ekmFromMasterSecret generates exported keying material as defined in RFC 5705. diff --git a/src/crypto/tls/quic.go b/src/crypto/tls/quic.go index b2e78887a40346..b872d4fe0174a0 100644 --- a/src/crypto/tls/quic.go +++ b/src/crypto/tls/quic.go @@ -185,16 +185,12 @@ type quicState struct { // QUICClient returns a new TLS client side connection using QUICTransport as the // underlying transport. The config cannot be nil. -// -// The config's MinVersion must be at least TLS 1.3. func QUICClient(config *QUICConfig) *QUICConn { return newQUICConn(Client(nil, config.TLSConfig), config) } // QUICServer returns a new TLS server side connection using QUICTransport as the // underlying transport. The config cannot be nil. -// -// The config's MinVersion must be at least TLS 1.3. func QUICServer(config *QUICConfig) *QUICConn { return newQUICConn(Server(nil, config.TLSConfig), config) } @@ -221,9 +217,6 @@ func (q *QUICConn) Start(ctx context.Context) error { return quicError(errors.New("tls: Start called more than once")) } q.conn.quic.started = true - if q.conn.config.MinVersion < VersionTLS13 { - return quicError(errors.New("tls: Config MinVersion must be at least TLS 1.3")) - } go q.conn.HandshakeContext(ctx) if _, ok := <-q.conn.quic.blockedc; !ok { return q.conn.handshakeErr diff --git a/src/crypto/tls/quic_test.go b/src/crypto/tls/quic_test.go index 57d7bf2d9d276f..29dc05448fb1c5 100644 --- a/src/crypto/tls/quic_test.go +++ b/src/crypto/tls/quic_test.go @@ -215,6 +215,59 @@ func TestQUICConnection(t *testing.T) { } } +func TestQUICVersions(t *testing.T) { + for _, tc := range []struct { + name string + clientMin uint16 + clientMax uint16 + serverMin uint16 + serverMax uint16 + wantErr bool + }{ + { + name: "defaults", + }, + { + name: "MinVersion TLS 1.2", + clientMin: VersionTLS12, + serverMin: VersionTLS12, + }, + { + name: "MinVersion TLS 1.3", + clientMin: VersionTLS13, + serverMin: VersionTLS13, + }, + { + name: "client MaxVersion TLS 1.2", + clientMax: VersionTLS12, + wantErr: true, + }, + { + name: "server MaxVersion TLS 1.2", + serverMax: VersionTLS12, + wantErr: true, + }, + } { + t.Run(tc.name, func(t *testing.T) { + client := testConfigClient.Clone() + client.MinVersion = tc.clientMin + client.MaxVersion = tc.clientMax + server := testConfigServer.Clone() + server.MinVersion = tc.serverMin + server.MaxVersion = tc.serverMax + + cli := newTestQUICClient(t, &QUICConfig{TLSConfig: client}) + cli.conn.SetTransportParameters(nil) + srv := newTestQUICServer(t, &QUICConfig{TLSConfig: server}) + srv.conn.SetTransportParameters(nil) + err := runTestQUICConnection(context.Background(), cli, srv, nil) + if tc.wantErr == (err == nil) { + t.Errorf("got err=%v, wantErr=%v", err, tc.wantErr) + } + }) + } +} + func TestQUICSessionResumption(t *testing.T) { clientConfig := &QUICConfig{TLSConfig: testConfigClient.Clone()} clientConfig.TLSConfig.MinVersion = VersionTLS13 diff --git a/src/crypto/tls/tls.go b/src/crypto/tls/tls.go index 680d80f38952da..5a710a3b88dbfa 100644 --- a/src/crypto/tls/tls.go +++ b/src/crypto/tls/tls.go @@ -34,7 +34,6 @@ import ( "encoding/pem" "errors" "fmt" - "internal/godebug" "net" "os" "strings" @@ -240,10 +239,6 @@ func (d *Dialer) DialContext(ctx context.Context, network, addr string) (net.Con // files. The files must contain PEM encoded data. The certificate file may // contain intermediate certificates following the leaf certificate to form a // certificate chain. On successful return, Certificate.Leaf will be populated. -// -// Before Go 1.23 Certificate.Leaf was left nil, and the parsed certificate was -// discarded. This behavior can be re-enabled by setting "x509keypairleaf=0" -// in the GODEBUG environment variable. func LoadX509KeyPair(certFile, keyFile string) (Certificate, error) { certPEMBlock, err := os.ReadFile(certFile) if err != nil { @@ -256,14 +251,8 @@ func LoadX509KeyPair(certFile, keyFile string) (Certificate, error) { return X509KeyPair(certPEMBlock, keyPEMBlock) } -var x509keypairleaf = godebug.New("x509keypairleaf") - // X509KeyPair parses a public/private key pair from a pair of // PEM encoded data. On successful return, Certificate.Leaf will be populated. -// -// Before Go 1.23 Certificate.Leaf was left nil, and the parsed certificate was -// discarded. This behavior can be re-enabled by setting "x509keypairleaf=0" -// in the GODEBUG environment variable. func X509KeyPair(certPEMBlock, keyPEMBlock []byte) (Certificate, error) { fail := func(err error) (Certificate, error) { return Certificate{}, err } @@ -317,12 +306,7 @@ func X509KeyPair(certPEMBlock, keyPEMBlock []byte) (Certificate, error) { if err != nil { return fail(err) } - - if x509keypairleaf.Value() != "0" { - cert.Leaf = x509Cert - } else { - x509keypairleaf.IncNonDefault() - } + cert.Leaf = x509Cert cert.PrivateKey, err = parsePrivateKey(keyDERBlock.Bytes) if err != nil { @@ -373,20 +357,21 @@ func X509KeyPair(certPEMBlock, keyPEMBlock []byte) (Certificate, error) { // PKCS #1 private keys by default, while OpenSSL 1.0.0 generates PKCS #8 keys. // OpenSSL ecparam generates SEC1 EC private keys for ECDSA. We try all three. func parsePrivateKey(der []byte) (crypto.PrivateKey, error) { - if key, err := x509.ParsePKCS1PrivateKey(der); err == nil { - return key, nil + key, err := x509.ParsePKCS8PrivateKey(der) + pkcs8Err := err // Return the PKCS#8 error if all parsing attempts fail. + if err != nil { + key, err = x509.ParsePKCS1PrivateKey(der) } - if key, err := x509.ParsePKCS8PrivateKey(der); err == nil { - switch key := key.(type) { - case *rsa.PrivateKey, *ecdsa.PrivateKey, ed25519.PrivateKey, *mldsa.PrivateKey: - return key, nil - default: - return nil, errors.New("tls: found unknown private key type in PKCS#8 wrapping") - } + if err != nil { + key, err = x509.ParseECPrivateKey(der) + } + if err != nil { + return nil, fmt.Errorf("tls: failed to parse private key: %w", pkcs8Err) } - if key, err := x509.ParseECPrivateKey(der); err == nil { + switch key := key.(type) { + case *rsa.PrivateKey, *ecdsa.PrivateKey, ed25519.PrivateKey, *mldsa.PrivateKey: return key, nil + default: + return nil, errors.New("tls: found unknown private key type in PKCS#8 wrapping") } - - return nil, errors.New("tls: failed to parse private key") } diff --git a/src/crypto/tls/tls_test.go b/src/crypto/tls/tls_test.go index 01e398db837726..3db1365a971349 100644 --- a/src/crypto/tls/tls_test.go +++ b/src/crypto/tls/tls_test.go @@ -1715,16 +1715,14 @@ func TestCipherSuites(t *testing.T) { } // Check that disabled suites are marked insecure. - for _, badSuites := range []map[uint16]bool{disabledCipherSuites, rsaKexCiphers} { - for id := range badSuites { - c := CipherSuiteByID(id) - if c == nil { - t.Errorf("%#04x: no CipherSuite entry", id) - continue - } - if !c.Insecure { - t.Errorf("%#04x: disabled by default but not marked insecure", id) - } + for id := range disabledCipherSuites { + c := CipherSuiteByID(id) + if c == nil { + t.Errorf("%#04x: no CipherSuite entry", id) + continue + } + if !c.Insecure { + t.Errorf("%#04x: disabled by default but not marked insecure", id) } } @@ -2108,6 +2106,18 @@ func TestHandshakeMLKEM(t *testing.T) { expectClient: []CurveID{SecP256r1MLKEM768, CurveP256}, expectSelected: CurveP256, }, + { + name: "CurveP384HRR", + clientConfig: func(config *Config) { + config.CurvePreferences = []CurveID{SecP256r1MLKEM768, CurveP384} + }, + serverConfig: func(config *Config) { + config.CurvePreferences = []CurveID{CurveP384} + }, + expectClient: []CurveID{SecP256r1MLKEM768, CurveP384}, + expectSelected: CurveP384, + expectHRR: true, + }, { name: "ClientMLKEMOnly", clientConfig: func(config *Config) { @@ -2156,6 +2166,67 @@ func TestHandshakeMLKEM(t *testing.T) { expectClient: []CurveID{X25519MLKEM768, X25519, CurveP256, CurveP384, CurveP521}, expectSelected: X25519MLKEM768, }, + { + name: "CurvePreferences override GODEBUG", + preparation: func(t *testing.T) { + testenv.SetGODEBUG(t, "tlsmlkem=0") + testenv.SetGODEBUG(t, "tlssecpmlkem=0") + }, + clientConfig: func(config *Config) { + config.CurvePreferences = []CurveID{CurveP256, SecP256r1MLKEM768, MLKEM1024} + }, + serverConfig: func(config *Config) { + config.CurvePreferences = []CurveID{CurveP256, SecP256r1MLKEM768, MLKEM1024} + }, + expectClient: []CurveID{SecP256r1MLKEM768, MLKEM1024, CurveP256}, + expectSelected: SecP256r1MLKEM768, + }, + { + name: "ClientMLKEM1024Only", + clientConfig: func(config *Config) { + config.CurvePreferences = []CurveID{MLKEM1024} + }, + serverConfig: func(config *Config) { + config.CurvePreferences = append(defaultWithPQ, MLKEM1024) + }, + expectClient: []CurveID{MLKEM1024}, + expectSelected: MLKEM1024, + }, + { + name: "ServerMLKEM1024Only", + clientConfig: func(config *Config) { + config.CurvePreferences = append(defaultWithPQ, MLKEM1024) + }, + serverConfig: func(config *Config) { + config.CurvePreferences = []CurveID{MLKEM1024} + }, + expectClient: []CurveID{X25519MLKEM768, SecP256r1MLKEM768, SecP384r1MLKEM1024, + MLKEM1024, X25519, CurveP256, CurveP384, CurveP521}, + expectSelected: MLKEM1024, + expectHRR: true, + }, + { + name: "MLKEM1024NotPreferredOverHybrid", + clientConfig: func(config *Config) { + config.CurvePreferences = []CurveID{MLKEM1024, X25519MLKEM768} + }, + serverConfig: func(config *Config) { + config.CurvePreferences = []CurveID{MLKEM1024, X25519MLKEM768} + }, + expectClient: []CurveID{X25519MLKEM768, MLKEM1024}, + expectSelected: X25519MLKEM768, + }, + { + name: "MLKEM1024PreferredOverECC", + clientConfig: func(config *Config) { + config.CurvePreferences = []CurveID{X25519, MLKEM1024} + }, + serverConfig: func(config *Config) { + config.CurvePreferences = []CurveID{X25519, MLKEM1024} + }, + expectClient: []CurveID{MLKEM1024, X25519}, + expectSelected: MLKEM1024, + }, } baseServerConfig := testConfigServer.Clone() @@ -2450,21 +2521,6 @@ func TestHandshakeMLDSA(t *testing.T) { } } -// bitFlippingSigner wraps a crypto.Signer and flips the last bit of every -// signature it produces, used to test that peers reject invalid signatures. -type bitFlippingSigner struct{ crypto.Signer } - -func (s bitFlippingSigner) Sign(rand io.Reader, msg []byte, opts crypto.SignerOpts) ([]byte, error) { - sig, err := s.Signer.Sign(rand, msg, opts) - if err != nil { - return nil, err - } - if len(sig) > 0 { - sig[len(sig)-1] ^= 1 - } - return sig, nil -} - func TestX509KeyPairPopulateCertificate(t *testing.T) { key, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader) if err != nil { @@ -2485,35 +2541,13 @@ func TestX509KeyPairPopulateCertificate(t *testing.T) { } certPEM := pem.EncodeToMemory(&pem.Block{Type: "CERTIFICATE", Bytes: certDER}) - t.Run("x509keypairleaf=0", func(t *testing.T) { - testenv.SetGODEBUG(t, "x509keypairleaf=0") - cert, err := X509KeyPair(certPEM, keyPEM) - if err != nil { - t.Fatal(err) - } - if cert.Leaf != nil { - t.Fatal("Leaf should not be populated") - } - }) - t.Run("x509keypairleaf=1", func(t *testing.T) { - testenv.SetGODEBUG(t, "x509keypairleaf=1") - cert, err := X509KeyPair(certPEM, keyPEM) - if err != nil { - t.Fatal(err) - } - if cert.Leaf == nil { - t.Fatal("Leaf should be populated") - } - }) - t.Run("GODEBUG unset", func(t *testing.T) { - cert, err := X509KeyPair(certPEM, keyPEM) - if err != nil { - t.Fatal(err) - } - if cert.Leaf == nil { - t.Fatal("Leaf should be populated") - } - }) + cert, err := X509KeyPair(certPEM, keyPEM) + if err != nil { + t.Fatal(err) + } + if cert.Leaf == nil { + t.Fatal("Leaf should be populated") + } } func TestEarlyLargeCertMsg(t *testing.T) { @@ -2785,3 +2819,116 @@ func (s messageOnlySigner) SignMessage(rand io.Reader, msg []byte, opts crypto.S digest := h.Sum(nil) return s.Signer.Sign(rand, digest, opts) } + +// bitFlippingSigner wraps a crypto.Signer and flips a bit in the signature, +// producing an invalid signature. +type bitFlippingSigner struct{ crypto.Signer } + +func (s bitFlippingSigner) Public() crypto.PublicKey { + return s.Signer.Public() +} + +func (s bitFlippingSigner) Sign(rand io.Reader, digest []byte, opts crypto.SignerOpts) ([]byte, error) { + sig, err := s.Signer.Sign(rand, digest, opts) + if err != nil { + return nil, err + } + sig[0] ^= 1 + return sig, nil +} + +// TestInvalidHandshakeSignature tests that invalid handshake signatures are +// rejected for all TLS versions, for both server and client certificates, +// even when InsecureSkipVerify or RequireAnyClientCert are used (which disable +// certificate chain verification, but not signature verification). +func TestInvalidHandshakeSignature(t *testing.T) { + t.Run("TLSv10", func(t *testing.T) { + skipFIPS(t) + testInvalidHandshakeSignature(t, VersionTLS10) + }) + t.Run("TLSv12", func(t *testing.T) { testInvalidHandshakeSignature(t, VersionTLS12) }) + t.Run("TLSv13", func(t *testing.T) { testInvalidHandshakeSignature(t, VersionTLS13) }) +} + +func testInvalidHandshakeSignature(t *testing.T, version uint16) { + serverConfig := testConfigServer.Clone() + serverConfig.MaxVersion = version + serverConfig.MinVersion = version + serverConfig.SessionTicketsDisabled = true + clientConfig := testConfigClient.Clone() + clientConfig.MaxVersion = version + clientConfig.MinVersion = version + + // Test that the server rejects invalid client certificate signatures, + // even when RequireAnyClientCert is used. + t.Run("ClientSignature", func(t *testing.T) { + serverConfig := serverConfig.Clone() + serverConfig.ClientAuth = RequireAnyClientCert + clientConfig := clientConfig.Clone() + clientConfig.Certificates = []Certificate{{ + Certificate: testClientECDSAP256Cert.Certificate, + PrivateKey: bitFlippingSigner{testClientECDSAP256Cert.PrivateKey.(crypto.Signer)}, + }} + + clientErr, serverErr := testInvalidSignatureHandshake(t, clientConfig, serverConfig) + if serverErr == nil { + t.Fatalf("expected server to reject invalid client signature; client err = %v", clientErr) + } + if !strings.Contains(serverErr.Error(), "invalid signature") { + t.Errorf("expected 'invalid signature' error, got: %v", serverErr) + } + }) + + // Test that the client rejects invalid server certificate signatures. + t.Run("ServerSignature", func(t *testing.T) { + serverConfig := serverConfig.Clone() + serverConfig.Certificates = []Certificate{{ + Certificate: testRSA2048Cert.Certificate, + PrivateKey: bitFlippingSigner{testRSA2048Cert.PrivateKey.(crypto.Signer)}, + }} + + clientErr, serverErr := testInvalidSignatureHandshake(t, clientConfig, serverConfig) + if clientErr == nil { + t.Fatalf("expected client to reject invalid server signature; server err = %v", serverErr) + } + if !strings.Contains(clientErr.Error(), "invalid signature") { + t.Errorf("expected 'invalid signature' error, got: %v", clientErr) + } + }) + + // Test that InsecureSkipVerify doesn't disable server signature verification. + t.Run("ServerSignature/InsecureSkipVerify", func(t *testing.T) { + clientConfig := clientConfig.Clone() + clientConfig.InsecureSkipVerify = true + serverConfig := serverConfig.Clone() + serverConfig.Certificates = []Certificate{{ + Certificate: testRSA2048Cert.Certificate, + PrivateKey: bitFlippingSigner{testRSA2048Cert.PrivateKey.(crypto.Signer)}, + }} + + clientErr, serverErr := testInvalidSignatureHandshake(t, clientConfig, serverConfig) + if clientErr == nil { + t.Fatalf("expected client to reject invalid server signature despite InsecureSkipVerify; server err = %v", serverErr) + } + if !strings.Contains(clientErr.Error(), "invalid signature") { + t.Errorf("expected 'invalid signature' error, got: %v", clientErr) + } + }) +} + +// testInvalidSignatureHandshake performs a TLS handshake and returns the +// errors from both client and server. Unlike testHandshake, it doesn't try +// to exchange data after the handshake. +func testInvalidSignatureHandshake(t *testing.T, clientConfig, serverConfig *Config) (clientErr, serverErr error) { + c, s := localPipe(t) + done := make(chan struct{}) + go func() { + defer close(done) + clientErr = Client(c, clientConfig).Handshake() + c.Close() + }() + serverErr = Server(s, serverConfig).Handshake() + s.Close() + <-done + return +} diff --git a/src/encoding/json/v2/arshal_default.go b/src/encoding/json/v2/arshal_default.go index ee001d5d75f13a..2c6b0c64cc9601 100644 --- a/src/encoding/json/v2/arshal_default.go +++ b/src/encoding/json/v2/arshal_default.go @@ -1085,7 +1085,7 @@ func makeStructArshaler(t reflect.Type) *arshaler { // This validation is effectively a makeStructFields error that // occurs before any marshalling begins, but since it depends // on the marshal options it can't be part of the sync.Once. - if !mo.Flags.Get(jsonflags.ReportErrorsWithLegacySemantics) { + if fields.hasString && !mo.Flags.Get(jsonflags.ReportErrorsWithLegacySemantics) { for i := range fields.flattened { f := &fields.flattened[i] if f.string { @@ -1290,7 +1290,7 @@ func makeStructArshaler(t reflect.Type) *arshaler { // This validation is effectively a makeStructFields error that // occurs before any marshalling begins, but since it depends // on the marshal options it can't be part of the sync.Once. - if !uo.Flags.Get(jsonflags.ReportErrorsWithLegacySemantics) { + if fields.hasString && !uo.Flags.Get(jsonflags.ReportErrorsWithLegacySemantics) { for i := range fields.flattened { f := &fields.flattened[i] if f.string { diff --git a/src/encoding/json/v2/fields.go b/src/encoding/json/v2/fields.go index 0fcf4f849835a1..d91e5bf26e3762 100644 --- a/src/encoding/json/v2/fields.go +++ b/src/encoding/json/v2/fields.go @@ -34,6 +34,7 @@ type structFields struct { byActualName map[string]*structField byFoldedName map[string][]*structField inlinedFallback *structField + hasString bool // one or more fields set the string option } // reindex recomputes index to avoid bounds check during runtime. @@ -81,6 +82,9 @@ func makeStructFields(root reflect.Type) (fs structFields, serr *SemanticError) return cmp.Or(serr, &SemanticError{GoType: t, Err: fmt.Errorf(f, a...)}) } + // Whether any field sets the string option. + var hasString bool + // Setup a queue for a breadth-first search. var queueIndex int type queueEntry struct { @@ -248,6 +252,9 @@ func makeStructFields(root reflect.Type) (fs structFields, serr *SemanticError) f.id = len(allFields) f.fncs = lookupArshaler(sf.Type) allFields = append(allFields, f) + if f.string { + hasString = true + } } } @@ -313,6 +320,7 @@ func makeStructFields(root reflect.Type) (fs structFields, serr *SemanticError) flattened: flattened, byActualName: make(map[string]*structField, len(flattened)), byFoldedName: make(map[string][]*structField, len(flattened)), + hasString: hasString, } for i, f := range fs.flattened { foldedName := string(foldName([]byte(f.name))) diff --git a/src/go/ast/ast.go b/src/go/ast/ast.go index cbd4edd595b05d..c916abded148cd 100644 --- a/src/go/ast/ast.go +++ b/src/go/ast/ast.go @@ -1113,7 +1113,7 @@ func (p *Package) End() token.Pos { return token.NoPos } // The syntax tree must have been parsed with the [go/parser.ParseComments] flag. // Example: // -// f, err := parser.ParseFile(fset, filename, src, parser.ParseComments|parser.PackageClauseOnly) +// f, err := parser.ParseFile(fset, filename, src, parser.ParseComments|parser.PackageClauseOnly|parser.SkipObjectResolution) // if err != nil { ... } // gen := ast.IsGenerated(f) func IsGenerated(file *File) bool { diff --git a/src/go/ast/commentmap_test.go b/src/go/ast/commentmap_test.go index 0d5e8de0137669..332d099de4433d 100644 --- a/src/go/ast/commentmap_test.go +++ b/src/go/ast/commentmap_test.go @@ -103,7 +103,7 @@ func ctext(list []*CommentGroup) string { func TestCommentMap(t *testing.T) { fset := token.NewFileSet() - f, err := parser.ParseFile(fset, "", src, parser.ParseComments) + f, err := parser.ParseFile(fset, "", src, parser.ParseComments|parser.SkipObjectResolution) if err != nil { t.Fatal(err) } @@ -141,7 +141,7 @@ func TestCommentMap(t *testing.T) { func TestFilter(t *testing.T) { fset := token.NewFileSet() - f, err := parser.ParseFile(fset, "", src, parser.ParseComments) + f, err := parser.ParseFile(fset, "", src, parser.ParseComments|parser.SkipObjectResolution) if err != nil { t.Fatal(err) } diff --git a/src/go/ast/example_test.go b/src/go/ast/example_test.go index 36daa7e7e1e646..6f54028b633224 100644 --- a/src/go/ast/example_test.go +++ b/src/go/ast/example_test.go @@ -24,7 +24,7 @@ var X = f(3.14)*2 + c // Create the AST by parsing src. fset := token.NewFileSet() // positions are relative to fset - f, err := parser.ParseFile(fset, "src.go", src, 0) + f, err := parser.ParseFile(fset, "src.go", src, parser.SkipObjectResolution) if err != nil { panic(err) } @@ -66,8 +66,8 @@ func main() { ` // Create the AST by parsing src. - fset := token.NewFileSet() // positions are relative to fset - f, err := parser.ParseFile(fset, "", src, 0) + fset := token.NewFileSet() // positions are relative to fset + f, err := parser.ParseFile(fset, "", src, 0) // requires object resolution if err != nil { panic(err) } @@ -151,7 +151,7 @@ func f(x, y int) { ` fset := token.NewFileSet() - f, err := parser.ParseFile(fset, "", src, 0) + f, err := parser.ParseFile(fset, "", src, parser.SkipObjectResolution) if err != nil { panic(err) } @@ -200,7 +200,7 @@ func main() { // Create the AST by parsing src. fset := token.NewFileSet() // positions are relative to fset - f, err := parser.ParseFile(fset, "src.go", src, parser.ParseComments) + f, err := parser.ParseFile(fset, "src.go", src, parser.ParseComments|parser.SkipObjectResolution) if err != nil { panic(err) } diff --git a/src/go/ast/filter_test.go b/src/go/ast/filter_test.go index d5cb0c2e3f1797..c33dc56773a336 100644 --- a/src/go/ast/filter_test.go +++ b/src/go/ast/filter_test.go @@ -57,7 +57,7 @@ func (x *t2) f2() {} func TestFilterDuplicates(t *testing.T) { // parse input fset := token.NewFileSet() - file, err := parser.ParseFile(fset, "", input, 0) + file, err := parser.ParseFile(fset, "", input, 0) // requires object resolution if err != nil { t.Fatal(err) } diff --git a/src/go/ast/issues_test.go b/src/go/ast/issues_test.go index f5e26af462f55a..2db6f8afcc78dc 100644 --- a/src/go/ast/issues_test.go +++ b/src/go/ast/issues_test.go @@ -19,7 +19,7 @@ func TestIssue33649(t *testing.T) { `package p; func _() { _ = 0 }`, } { fset := token.NewFileSet() - f, _ := parser.ParseFile(fset, "", src, parser.AllErrors) + f, _ := parser.ParseFile(fset, "", src, parser.AllErrors|parser.SkipObjectResolution) if f == nil { panic("invalid test setup: parser didn't return an AST") } @@ -125,7 +125,7 @@ package p `, true}, } { fset := token.NewFileSet() - f, err := parser.ParseFile(fset, "", test.src, parser.PackageClauseOnly|parser.ParseComments) + f, err := parser.ParseFile(fset, "", test.src, parser.PackageClauseOnly|parser.ParseComments|parser.SkipObjectResolution) if f == nil { t.Fatalf("parse %d failed to return AST: %v", i, err) } diff --git a/src/go/ast/walk_test.go b/src/go/ast/walk_test.go index 172b2e3f5dd269..60a1b5f3fa5de9 100644 --- a/src/go/ast/walk_test.go +++ b/src/go/ast/walk_test.go @@ -46,7 +46,7 @@ func g() { } ` fset := token.NewFileSet() - f, _ := parser.ParseFile(fset, "a.go", src, 0) + f, _ := parser.ParseFile(fset, "a.go", src, parser.SkipObjectResolution) str := func(n ast.Node) string { return strings.TrimPrefix(reflect.TypeOf(n).String(), "*ast.") diff --git a/src/go/build/read.go b/src/go/build/read.go index 3185cf09bec72e..737f409d4eb427 100644 --- a/src/go/build/read.go +++ b/src/go/build/read.go @@ -311,7 +311,7 @@ func readGoInfo(f io.Reader, info *fileInfo) error { } // Parse file header & record imports. - info.parsed, info.parseErr = parser.ParseFile(info.fset, info.name, info.header, parser.ImportsOnly|parser.ParseComments) + info.parsed, info.parseErr = parser.ParseFile(info.fset, info.name, info.header, parser.ImportsOnly|parser.ParseComments|parser.SkipObjectResolution) if info.parseErr != nil { return nil } diff --git a/src/go/doc/doc_test.go b/src/go/doc/doc_test.go index b79087e5387d3d..144a411c8180cb 100644 --- a/src/go/doc/doc_test.go +++ b/src/go/doc/doc_test.go @@ -154,7 +154,7 @@ func Test(t *testing.T) { func TestFuncs(t *testing.T) { fset := token.NewFileSet() - file, err := parser.ParseFile(fset, "funcs.go", strings.NewReader(funcsTestFile), parser.ParseComments) + file, err := parser.ParseFile(fset, "funcs.go", strings.NewReader(funcsTestFile), parser.ParseComments|parser.SkipObjectResolution) if err != nil { t.Fatal(err) } diff --git a/src/go/doc/example_internal_test.go b/src/go/doc/example_internal_test.go index 08ddfafdd441dd..90644e34b800f0 100644 --- a/src/go/doc/example_internal_test.go +++ b/src/go/doc/example_internal_test.go @@ -100,7 +100,7 @@ import ( } { t.Run(test.name, func(t *testing.T) { fset := token.NewFileSet() - file, err := parser.ParseFile(fset, "test.go", strings.NewReader(test.in), parser.ParseComments) + file, err := parser.ParseFile(fset, "test.go", strings.NewReader(test.in), parser.ParseComments|parser.SkipObjectResolution) if err != nil { t.Fatal(err) } diff --git a/src/go/doc/example_test.go b/src/go/doc/example_test.go index db2b2d34cd3060..100e359212a813 100644 --- a/src/go/doc/example_test.go +++ b/src/go/doc/example_test.go @@ -29,7 +29,7 @@ func TestExamples(t *testing.T) { for _, filename := range filenames { t.Run(strings.TrimSuffix(filepath.Base(filename), ".go"), func(t *testing.T) { fset := token.NewFileSet() - astFile, err := parser.ParseFile(fset, filename, nil, parser.ParseComments) + astFile, err := parser.ParseFile(fset, filename, nil, parser.ParseComments) // requires object resolution if err != nil { t.Fatal(err) } diff --git a/src/go/format/format_test.go b/src/go/format/format_test.go index 6cc0278b79b696..0980774f18577c 100644 --- a/src/go/format/format_test.go +++ b/src/go/format/format_test.go @@ -44,7 +44,7 @@ func TestNode(t *testing.T) { } fset := token.NewFileSet() - file, err := parser.ParseFile(fset, testfile, src, parser.ParseComments) + file, err := parser.ParseFile(fset, testfile, src, parser.ParseComments|parser.SkipObjectResolution) if err != nil { t.Fatal(err) } @@ -67,7 +67,7 @@ func TestNodeNoModify(t *testing.T) { ) fset := token.NewFileSet() - file, err := parser.ParseFile(fset, "", src, parser.ParseComments) + file, err := parser.ParseFile(fset, "", src, parser.ParseComments|parser.SkipObjectResolution) if err != nil { t.Fatal(err) } diff --git a/src/go/internal/gcimporter/gcimporter_test.go b/src/go/internal/gcimporter/gcimporter_test.go index c4861c706751f5..17f0893e1aaf14 100644 --- a/src/go/internal/gcimporter/gcimporter_test.go +++ b/src/go/internal/gcimporter/gcimporter_test.go @@ -231,7 +231,7 @@ func sanitizeObjectString(s string) string { func checkFile(t *testing.T, filename string, src []byte) *types.Package { fset := token.NewFileSet() - f, err := parser.ParseFile(fset, filename, src, 0) + f, err := parser.ParseFile(fset, filename, src, parser.SkipObjectResolution) if err != nil { t.Fatal(err) } @@ -792,7 +792,7 @@ func TestIssue69912(t *testing.T) { } check := func(pkgname, src string, imports importMap) (*types.Package, error) { - f, err := parser.ParseFile(fset, "a.go", src, 0) + f, err := parser.ParseFile(fset, "a.go", src, parser.SkipObjectResolution) if err != nil { return nil, err } diff --git a/src/go/internal/gcimporter/genmeth_test.go b/src/go/internal/gcimporter/genmeth_test.go index 63b8858af3c0d2..2bbb7bde26391c 100644 --- a/src/go/internal/gcimporter/genmeth_test.go +++ b/src/go/internal/gcimporter/genmeth_test.go @@ -45,7 +45,7 @@ func TestGenMeth(t *testing.T) { } check := func(pkgname, src string, imports importMap) (*types.Package, error) { - f, err := parser.ParseFile(fset, "genmeth.go", src, 0) + f, err := parser.ParseFile(fset, "genmeth.go", src, parser.SkipObjectResolution) if err != nil { return nil, err } diff --git a/src/go/parser/example_test.go b/src/go/parser/example_test.go index c2f7f293bc1467..b4e2a1b34df66a 100644 --- a/src/go/parser/example_test.go +++ b/src/go/parser/example_test.go @@ -25,7 +25,7 @@ func bar() { }` // Parse src but stop after processing the imports. - f, err := parser.ParseFile(fset, "", src, parser.ImportsOnly) + f, err := parser.ParseFile(fset, "", src, parser.ImportsOnly|parser.SkipObjectResolution) if err != nil { fmt.Println(err) return diff --git a/src/go/printer/example_test.go b/src/go/printer/example_test.go index f7d72d136d7aea..947f2c8788981e 100644 --- a/src/go/printer/example_test.go +++ b/src/go/printer/example_test.go @@ -16,7 +16,7 @@ import ( func parseFunc(filename, functionname string) (fun *ast.FuncDecl, fset *token.FileSet) { fset = token.NewFileSet() - if file, err := parser.ParseFile(fset, filename, nil, 0); err == nil { + if file, err := parser.ParseFile(fset, filename, nil, parser.SkipObjectResolution); err == nil { for _, d := range file.Decls { if f, ok := d.(*ast.FuncDecl); ok && f.Name.Name == functionname { fun = f diff --git a/src/go/printer/nodes.go b/src/go/printer/nodes.go index 5f697e88bc74a3..f481a196c1daa4 100644 --- a/src/go/printer/nodes.go +++ b/src/go/printer/nodes.go @@ -1312,8 +1312,7 @@ func (p *printer) controlClause(isForStmt bool, init ast.Stmt, expr ast.Expr, po // isCompositeLitLike reports whether x is a composite literal or an expression // whose core is a composite literal (e.g. &T{...}), ignoring parentheses. func isCompositeLitLike(x ast.Expr) bool { - x = stripParensAlways(x) - switch x := x.(type) { + switch x := stripParensAlways(x).(type) { case *ast.CompositeLit: return true case *ast.UnaryExpr: @@ -1326,10 +1325,13 @@ func isCompositeLitLike(x ast.Expr) bool { // indentList reports whether an expression list would look better if it // were indented wholesale (starting with the very first element, rather // than starting at the first line break). +// Currently this function is only used to improve formatting of return +// statements. func (p *printer) indentList(list []ast.Expr) bool { // Heuristic: indentList reports whether there are more than one multi- - // line element in the list, or if there is any element that is not - // starting on the same line as the previous one ends. + // line element (such as a complex expression, but excluding composite + // literals) in the list, or if there is any element that is not starting + // on the same line as the previous one ends. if len(list) >= 2 { var b = p.lineFor(list[0].Pos()) var e = p.lineFor(list[len(list)-1].End()) @@ -1345,11 +1347,11 @@ func (p *printer) indentList(list []ast.Expr) bool { // line as the previous one ended return true } - if xb < xe { - // x is a multi-line element. - if !isCompositeLitLike(x) { - n++ - } + if xb < xe && !isCompositeLitLike(x) { + // x is a multi-line element but not a composite literal + // (composite literals have their own field indentation + // already, see go.dev/issue/7195) + n++ } line = xe } diff --git a/src/go/printer/performance_test.go b/src/go/printer/performance_test.go index c58f6d470610a8..19a2cb7af98fcb 100644 --- a/src/go/printer/performance_test.go +++ b/src/go/printer/performance_test.go @@ -41,7 +41,7 @@ func initialize() { log.Fatalf("%s", err) } - file, err := parser.ParseFile(fset, filename, src, parser.ParseComments) + file, err := parser.ParseFile(fset, filename, src, parser.ParseComments|parser.SkipObjectResolution) if err != nil { log.Fatalf("%s", err) } diff --git a/src/go/printer/printer_test.go b/src/go/printer/printer_test.go index fd79726b8e11d6..806d796c0cb688 100644 --- a/src/go/printer/printer_test.go +++ b/src/go/printer/printer_test.go @@ -44,7 +44,7 @@ const ( // if any. func format(src []byte, mode checkMode) ([]byte, error) { // parse src - f, err := parser.ParseFile(fset, "", src, parser.ParseComments) + f, err := parser.ParseFile(fset, "", src, parser.ParseComments|parser.SkipObjectResolution) if err != nil { return nil, fmt.Errorf("parse: %s\n%s", err, src) } @@ -72,7 +72,7 @@ func format(src []byte, mode checkMode) ([]byte, error) { // make sure formatted output is syntactically correct res := buf.Bytes() - if _, err := parser.ParseFile(fset, "", res, parser.ParseComments); err != nil { + if _, err := parser.ParseFile(fset, "", res, parser.ParseComments|parser.SkipObjectResolution); err != nil { return nil, fmt.Errorf("re-parse: %s\n%s", err, buf.Bytes()) } @@ -220,7 +220,7 @@ func TestLineComments(t *testing.T) { ` fset := token.NewFileSet() - f, err := parser.ParseFile(fset, "", src, parser.ParseComments) + f, err := parser.ParseFile(fset, "", src, parser.ParseComments|parser.SkipObjectResolution) if err != nil { panic(err) // error in test } @@ -261,7 +261,7 @@ func init() { func TestBadNodes(t *testing.T) { const src = "package p\n(" const res = "package p\nBadDecl\n" - f, err := parser.ParseFile(fset, "", src, parser.ParseComments) + f, err := parser.ParseFile(fset, "", src, parser.ParseComments|parser.SkipObjectResolution) if err == nil { t.Error("expected illegal program") // error in test } @@ -284,7 +284,7 @@ func testComment(t *testing.T, f *ast.File, srclen int, comment *ast.Comment) { if err := Fprint(&buf, fset, f); err != nil { t.Error(err) } - if _, err := parser.ParseFile(fset, "", buf.Bytes(), 0); err != nil { + if _, err := parser.ParseFile(fset, "", buf.Bytes(), parser.SkipObjectResolution); err != nil { t.Fatalf("incorrect program for pos = %d:\n%s", comment.Slash, buf.String()) } // Position information is just an offset. @@ -315,7 +315,7 @@ func fibo(n int) { } ` - f, err := parser.ParseFile(fset, "", src, parser.ParseComments) + f, err := parser.ParseFile(fset, "", src, parser.ParseComments|parser.SkipObjectResolution) if err != nil { t.Error(err) // error in test } @@ -378,7 +378,7 @@ func (t *t) foo(a, b, c int) int { ` // parse original - f1, err := parser.ParseFile(fset, "src", src, parser.ParseComments) + f1, err := parser.ParseFile(fset, "src", src, parser.ParseComments|parser.SkipObjectResolution) if err != nil { t.Fatal(err) } @@ -392,7 +392,7 @@ func (t *t) foo(a, b, c int) int { // parse pretty printed original // (//line directives must be interpreted even w/o parser.ParseComments set) - f2, err := parser.ParseFile(fset, "", buf.Bytes(), 0) + f2, err := parser.ParseFile(fset, "", buf.Bytes(), parser.SkipObjectResolution) if err != nil { t.Fatalf("%s\n%s", err, buf.Bytes()) } @@ -460,7 +460,7 @@ func g() { ` // parse original - f1, err := parser.ParseFile(fset, "src.go", orig, 0) + f1, err := parser.ParseFile(fset, "src.go", orig, parser.SkipObjectResolution) if err != nil { t.Fatal(err) } @@ -502,7 +502,7 @@ type T struct { } ` - f, err := parser.ParseFile(fset, "src.go", orig, parser.ParseComments) + f, err := parser.ParseFile(fset, "src.go", orig, parser.ParseComments|parser.SkipObjectResolution) if err != nil { t.Fatal(err) } @@ -527,7 +527,7 @@ var decls = []string{ func TestDeclLists(t *testing.T) { for _, src := range decls { - file, err := parser.ParseFile(fset, "", "package p;"+src, parser.ParseComments) + file, err := parser.ParseFile(fset, "", "package p;"+src, parser.ParseComments|parser.SkipObjectResolution) if err != nil { panic(err) // error in test } @@ -553,7 +553,7 @@ var stmts = []string{ func TestStmtLists(t *testing.T) { for _, src := range stmts { - file, err := parser.ParseFile(fset, "", "package p; func _() {"+src+"}", parser.ParseComments) + file, err := parser.ParseFile(fset, "", "package p; func _() {"+src+"}", parser.ParseComments|parser.SkipObjectResolution) if err != nil { panic(err) // error in test } @@ -582,7 +582,7 @@ func TestBaseIndent(t *testing.T) { panic(err) // error in test } - file, err := parser.ParseFile(fset, filename, src, 0) + file, err := parser.ParseFile(fset, filename, src, parser.SkipObjectResolution) if err != nil { panic(err) // error in test } @@ -690,7 +690,7 @@ func TestWriteErrors(t *testing.T) { if err != nil { panic(err) // error in test } - file, err := parser.ParseFile(fset, filename, src, 0) + file, err := parser.ParseFile(fset, filename, src, parser.SkipObjectResolution) if err != nil { panic(err) // error in test } @@ -743,7 +743,7 @@ type bar int // comment2 ) fset := token.NewFileSet() - f, err := parser.ParseFile(fset, "input.go", input, parser.ParseComments) + f, err := parser.ParseFile(fset, "input.go", input, parser.ParseComments|parser.SkipObjectResolution) if err != nil { t.Fatal(err) } @@ -774,7 +774,7 @@ type bar int // comment2 func TestIssue11151(t *testing.T) { const src = "package p\t/*\r/1\r*\r/2*\r\r\r\r/3*\r\r+\r\r/4*/\n" fset := token.NewFileSet() - f, err := parser.ParseFile(fset, "", src, parser.ParseComments) + f, err := parser.ParseFile(fset, "", src, parser.ParseComments|parser.SkipObjectResolution) if err != nil { t.Fatal(err) } @@ -788,7 +788,7 @@ func TestIssue11151(t *testing.T) { } // the resulting program must be valid - _, err = parser.ParseFile(fset, "", got, 0) + _, err = parser.ParseFile(fset, "", got, parser.SkipObjectResolution) if err != nil { t.Errorf("%v\norig: %q\ngot : %q", err, src, got) } @@ -800,7 +800,7 @@ func TestParenthesizedDecl(t *testing.T) { // a package with multiple specs in a single declaration const src = "package p; var ( a float64; b int )" fset := token.NewFileSet() - f, err := parser.ParseFile(fset, "", src, 0) + f, err := parser.ParseFile(fset, "", src, parser.SkipObjectResolution) if err != nil { t.Fatal(err) } @@ -840,7 +840,7 @@ func f() { } }` fset := token.NewFileSet() - file, err := parser.ParseFile(fset, "", src, 0) + file, err := parser.ParseFile(fset, "", src, parser.SkipObjectResolution) if err != nil { panic(err) } @@ -907,40 +907,34 @@ func TestEmptyDecl(t *testing.T) { // issue 63566 // when printing a return statement with multiple multi-line composite literals. func TestIssue7195(t *testing.T) { const src = `package p - type T struct{ x int } - -func f() (*T, *T) { - return &T{ +func _() (T, *T) { + return T{ x: 1, }, &T{ x: 2, } } ` - fset := token.NewFileSet() - file, err := parser.ParseFile(fset, "", src, 0) - if err != nil { - t.Fatal(err) - } - var buf bytes.Buffer - if err := Fprint(&buf, fset, file); err != nil { - t.Fatal(err) - } const want = `package p type T struct{ x int } -func f() (*T, *T) { - return &T{ +func _() (T, *T) { + return T{ x: 1, }, &T{ x: 2, } } ` - if got := buf.String(); got != want { + + got, err := format([]byte(src), 0) + if err != nil { + t.Fatal(err) + } + if got := string(got); got != want { t.Fatalf("got:\n%s\nwant:\n%s\n", got, want) } } diff --git a/src/go/token/example_test.go b/src/go/token/example_test.go index 7e5017e13c8ba1..80c2cdee63b19a 100644 --- a/src/go/token/example_test.go +++ b/src/go/token/example_test.go @@ -35,7 +35,7 @@ func ok(pos p) bool { } ` - f, err := parser.ParseFile(fset, "main.go", src, 0) + f, err := parser.ParseFile(fset, "main.go", src, parser.SkipObjectResolution) if err != nil { fmt.Println(err) return diff --git a/src/go/types/alias_test.go b/src/go/types/alias_test.go index 007f0c29998702..e0c52fbe78372d 100644 --- a/src/go/types/alias_test.go +++ b/src/go/types/alias_test.go @@ -59,7 +59,7 @@ func TestPartialTypeCheckUndeclaredAliasPanic(t *testing.T) { type A = B // undeclared` fset := token.NewFileSet() - file, err := parser.ParseFile(fset, "p.go", src, parser.ParseComments) + file, err := parser.ParseFile(fset, "p.go", src, parser.ParseComments|parser.SkipObjectResolution) if err != nil { t.Fatalf("could not parse: %v", err) } diff --git a/src/go/types/api_test.go b/src/go/types/api_test.go index bb9e2f47be0870..e8e4f502313b5b 100644 --- a/src/go/types/api_test.go +++ b/src/go/types/api_test.go @@ -31,7 +31,7 @@ func defaultImporter(fset *token.FileSet) Importer { } func mustParse(fset *token.FileSet, src string) *ast.File { - f, err := parser.ParseFile(fset, pkgName(src), src, parser.ParseComments) + f, err := parser.ParseFile(fset, pkgName(src), src, parser.ParseComments|parser.SkipObjectResolution) if err != nil { panic(err) // so we don't need to pass *testing.T } @@ -3055,7 +3055,7 @@ type C = int // to compute which file it is "in" based on syntax position. func TestVersionIssue69477(t *testing.T) { fset := token.NewFileSet() - f, _ := parser.ParseFile(fset, "a.go", "package p; const k = 123", 0) + f, _ := parser.ParseFile(fset, "a.go", "package p; const k = 123", parser.SkipObjectResolution) // Set an invalid Pos on the BasicLit. ast.Inspect(f, func(n ast.Node) bool { @@ -3085,10 +3085,10 @@ func TestVersionIssue69477(t *testing.T) { // The Checker now holds the effective version in a state variable. func TestVersionWithoutPos(t *testing.T) { fset := token.NewFileSet() - f, _ := parser.ParseFile(fset, "a.go", "//go:build go1.22\n\npackage p; var _ int", 0) + f, _ := parser.ParseFile(fset, "a.go", "//go:build go1.22\n\npackage p; var _ int", parser.SkipObjectResolution) // Splice in a decl from another file. Its pos will be wrong. - f2, _ := parser.ParseFile(fset, "a.go", "package q; func _(s func(func() bool)) { for range s {} }", 0) + f2, _ := parser.ParseFile(fset, "a.go", "package q; func _(s func(func() bool)) { for range s {} }", parser.SkipObjectResolution) f.Decls[0] = f2.Decls[0] // Type check. The checker will consult the effective @@ -3122,7 +3122,7 @@ func (recv T) f(param int) (result int) { } return local2 } -`, 0) +`, parser.SkipObjectResolution) pkg := NewPackage("p", "p") info := &Info{Defs: make(map[*ast.Ident]Object)} @@ -3160,7 +3160,7 @@ type B []byte var _ = f[B] ` fset := token.NewFileSet() - f, _ := parser.ParseFile(fset, "p.go", src, 0) + f, _ := parser.ParseFile(fset, "p.go", src, parser.SkipObjectResolution) pkg := NewPackage("p", "p") info := &Info{Types: make(map[ast.Expr]TypeAndValue)} diff --git a/src/go/types/check_test.go b/src/go/types/check_test.go index 142d221502d527..2fb6b9de4458ec 100644 --- a/src/go/types/check_test.go +++ b/src/go/types/check_test.go @@ -139,7 +139,7 @@ func testFiles(t *testing.T, filenames []string, srcs [][]byte, manual bool, opt } // parse files - files, errlist := parseFiles(t, filenames, srcs, parser.AllErrors) + files, errlist := parseFiles(t, filenames, srcs, parser.AllErrors|parser.SkipObjectResolution) pkgName := "" if len(files) > 0 { pkgName = files[0].Name.Name diff --git a/src/go/types/decl.go b/src/go/types/decl.go index 34694262cfda4a..fc32f1d6d917df 100644 --- a/src/go/types/decl.go +++ b/src/go/types/decl.go @@ -771,6 +771,9 @@ func (check *Checker) funcDecl(obj *Func, decl *declInfo) { fdecl := decl.fdecl check.funcType(sig, fdecl.Recv, fdecl.Type) + // types2 handles go:nointerface pragma here by setting obj.nointerface. + // go/types currently doesn't handle pragmas. + // Set the scope's extent to the complete "func (...) { ... }" // so that Scope.Innermost works correctly. sig.scope.pos = fdecl.Pos() diff --git a/src/go/types/eval_test.go b/src/go/types/eval_test.go index fd62b7ab88bdf5..2e0624e65ee2b9 100644 --- a/src/go/types/eval_test.go +++ b/src/go/types/eval_test.go @@ -168,7 +168,7 @@ func TestEvalPos(t *testing.T) { fset := token.NewFileSet() var files []*ast.File for i, src := range sources { - file, err := parser.ParseFile(fset, "p", src, parser.ParseComments) + file, err := parser.ParseFile(fset, "p", src, parser.ParseComments|parser.SkipObjectResolution) if err != nil { t.Fatalf("could not parse file %d: %s", i, err) } @@ -241,7 +241,7 @@ func f(a int, s string) S { }` fset := token.NewFileSet() - f, err := parser.ParseFile(fset, "p", src, parser.ParseComments) + f, err := parser.ParseFile(fset, "p", src, parser.ParseComments|parser.SkipObjectResolution) if err != nil { t.Fatal(err) } diff --git a/src/go/types/example_test.go b/src/go/types/example_test.go index d8e5de7476c81b..cec01457355148 100644 --- a/src/go/types/example_test.go +++ b/src/go/types/example_test.go @@ -117,7 +117,7 @@ type S struct { I; m int } type I interface { m() byte } ` fset := token.NewFileSet() - f, err := parser.ParseFile(fset, "celsius.go", input, 0) + f, err := parser.ParseFile(fset, "celsius.go", input, parser.SkipObjectResolution) if err != nil { log.Fatal(err) } diff --git a/src/go/types/generate_test.go b/src/go/types/generate_test.go index 4b49d6d4a1c190..7e090c909bf559 100644 --- a/src/go/types/generate_test.go +++ b/src/go/types/generate_test.go @@ -54,7 +54,7 @@ func TestGenerate(t *testing.T) { func generate(t *testing.T, filename string, write bool) { // parse src (cmd/compile/internal/types2) srcFilename := filepath.FromSlash(runtime.GOROOT() + srcDir + filename) - file, err := parser.ParseFile(fset, srcFilename, nil, parser.ParseComments) + file, err := parser.ParseFile(fset, srcFilename, nil, parser.ParseComments|parser.SkipObjectResolution) if err != nil { t.Fatal(err) } diff --git a/src/go/types/gotype.go b/src/go/types/gotype.go index ee9c12c447a414..7228916c0c4539 100644 --- a/src/go/types/gotype.go +++ b/src/go/types/gotype.go @@ -120,6 +120,7 @@ var ( ) func initParserMode() { + parserMode = parser.SkipObjectResolution if *allErrors { parserMode |= parser.AllErrors } diff --git a/src/go/types/hash_test.go b/src/go/types/hash_test.go index 98b1ae23df59b5..4b040d26472146 100644 --- a/src/go/types/hash_test.go +++ b/src/go/types/hash_test.go @@ -95,7 +95,7 @@ type Tagged2 struct { F int "tag2" } ` fset := token.NewFileSet() - file, err := parser.ParseFile(fset, "p.go", src, 0) + file, err := parser.ParseFile(fset, "p.go", src, parser.SkipObjectResolution) if err != nil { t.Fatal(err) } diff --git a/src/go/types/issues_test.go b/src/go/types/issues_test.go index 676bcdb159540b..2403d403262bf4 100644 --- a/src/go/types/issues_test.go +++ b/src/go/types/issues_test.go @@ -950,7 +950,7 @@ func _() { } ` fset := token.NewFileSet() - f, err := parser.ParseFile(fset, pkgName(src), src, 0) + f, err := parser.ParseFile(fset, pkgName(src), src, parser.SkipObjectResolution) if err == nil { t.Fatal("expected syntax error") } diff --git a/src/go/types/lookup.go b/src/go/types/lookup.go index bacfeac56d7df2..0d056e7c70e9b3 100644 --- a/src/go/types/lookup.go +++ b/src/go/types/lookup.go @@ -393,6 +393,7 @@ func (check *Checker) missingMethod(V, T Type, static bool, equivalent func(x, y ambigSel ptrRecv field + nointerface ) state := ok @@ -456,6 +457,11 @@ func (check *Checker) missingMethod(V, T Type, static bool, equivalent func(x, y check.objDecl(f) } + if f.nointerface { + state = nointerface + break + } + if !equivalent(f.typ, m.typ) { state = wrongSig break @@ -515,6 +521,8 @@ func (check *Checker) missingMethod(V, T Type, static bool, equivalent func(x, y *cause = check.sprintf("(method %s has pointer receiver)", m.Name()) case field: *cause = check.sprintf("(%s.%s is a field, not a method)", V, m.Name()) + case nointerface: + *cause = check.sprintf("(%s method is marked 'nointerface')", m.Name()) default: panic("unreachable") } diff --git a/src/go/types/methodset_test.go b/src/go/types/methodset_test.go index 37529fea4a985d..d0ec1f1d881a6e 100644 --- a/src/go/types/methodset_test.go +++ b/src/go/types/methodset_test.go @@ -144,7 +144,7 @@ type Instance = *Tree[int] ` fset := token.NewFileSet() - f, err := parser.ParseFile(fset, "foo.go", src, 0) + f, err := parser.ParseFile(fset, "foo.go", src, parser.SkipObjectResolution) if err != nil { panic(err) } @@ -165,7 +165,7 @@ func (T) m() {} // expected error: invalid receiver type ` fset := token.NewFileSet() - f, err := parser.ParseFile(fset, "p.go", src, 0) + f, err := parser.ParseFile(fset, "p.go", src, parser.SkipObjectResolution) if err != nil { t.Fatal(err) } diff --git a/src/go/types/object.go b/src/go/types/object.go index 46cc830bce3473..0e104c34e35450 100644 --- a/src/go/types/object.go +++ b/src/go/types/object.go @@ -399,8 +399,9 @@ func (*Var) isDependency() {} // a variable may be a dependency of an initializa // An abstract method may belong to many interfaces due to embedding. type Func struct { object - hasPtrRecv_ bool // only valid for methods that don't have a type yet; use hasPtrRecv() to read origin *Func // if non-nil, the Func from which this one was instantiated + hasPtrRecv_ bool // only valid for methods that don't have a type yet; use hasPtrRecv() to read + nointerface bool } // NewFunc returns a new function with the given signature, representing @@ -415,7 +416,7 @@ func NewFunc(pos token.Pos, pkg *Package, name string, sig *Signature) *Func { // as this would violate object.{Type,color} invariants. // TODO(adonovan): propose to disallow NewFunc with nil *Signature. } - return &Func{object{nil, pos, pkg, name, typ, 0, nopos}, false, nil} + return &Func{object{nil, pos, pkg, name, typ, 0, nopos}, nil, false, false} } // Signature returns the signature (type) of the function or method. diff --git a/src/go/types/self_test.go b/src/go/types/self_test.go index b4cc6286a18f81..130c2c7b65e4e7 100644 --- a/src/go/types/self_test.go +++ b/src/go/types/self_test.go @@ -110,7 +110,7 @@ func pkgFiles(fset *token.FileSet, path string) ([]*ast.File, error) { var files []*ast.File for _, filename := range filenames { - file, err := parser.ParseFile(fset, filename, nil, 0) + file, err := parser.ParseFile(fset, filename, nil, parser.SkipObjectResolution) if err != nil { return nil, err } diff --git a/src/go/types/stdlib_test.go b/src/go/types/stdlib_test.go index 74b456d66e8e7c..cf25db2bfe47b3 100644 --- a/src/go/types/stdlib_test.go +++ b/src/go/types/stdlib_test.go @@ -263,7 +263,7 @@ func testTestDir(t *testing.T, path string, ignore ...string) { } // parse and type-check file - file, err := parser.ParseFile(fset, filename, nil, 0) + file, err := parser.ParseFile(fset, filename, nil, parser.SkipObjectResolution) if err == nil { conf := Config{ GoVersion: goVersion, @@ -377,7 +377,7 @@ func typecheckFiles(path string, filenames []string, importer Importer) (*Packag // Parse package files. var files []*ast.File for _, filename := range filenames { - file, err := parser.ParseFile(fset, filename, nil, parser.AllErrors) + file, err := parser.ParseFile(fset, filename, nil, parser.AllErrors|parser.SkipObjectResolution) if err != nil { return nil, err } diff --git a/src/go/types/typeset_test.go b/src/go/types/typeset_test.go index a4feb7469b0637..d2ff6c6cbd207f 100644 --- a/src/go/types/typeset_test.go +++ b/src/go/types/typeset_test.go @@ -48,7 +48,7 @@ func TestTypeSetString(t *testing.T) { // parse src := "package p; type T interface" + body fset := token.NewFileSet() - file, err := parser.ParseFile(fset, "p.go", src, parser.AllErrors) + file, err := parser.ParseFile(fset, "p.go", src, parser.AllErrors|parser.SkipObjectResolution) if file == nil { t.Fatalf("%s: %v (invalid test case)", body, err) } diff --git a/src/internal/buildcfg/exp.go b/src/internal/buildcfg/exp.go index f45dc62f2a4882..8eeef17c1f470e 100644 --- a/src/internal/buildcfg/exp.go +++ b/src/internal/buildcfg/exp.go @@ -79,13 +79,14 @@ func ParseGOEXPERIMENT(goos, goarch, goexp string) (*ExperimentFlags, error) { dwarf5Supported := (goos != "darwin" && goos != "ios" && goos != "aix") baseline := goexperiment.Flags{ - RegabiWrappers: regabiSupported, - RegabiArgs: regabiSupported, - Dwarf5: dwarf5Supported, - RandomizedHeapBase64: true, - GreenTeaGC: true, - JSONv2: true, - GenericMethods: true, + RegabiWrappers: regabiSupported, + RegabiArgs: regabiSupported, + Dwarf5: dwarf5Supported, + RandomizedHeapBase64: true, + GreenTeaGC: true, + JSONv2: true, + GenericMethods: true, + SizeSpecializedMalloc: true, } flags := &ExperimentFlags{ Flags: baseline, diff --git a/src/internal/godebugs/table.go b/src/internal/godebugs/table.go index 56a139006c28a9..10b3919d51e10d 100644 --- a/src/internal/godebugs/table.go +++ b/src/internal/godebugs/table.go @@ -60,14 +60,10 @@ var All = []Info{ {Name: "randseednop", Package: "math/rand", Changed: 24, Old: "0"}, {Name: "rsa1024min", Package: "crypto/rsa", Changed: 24, Old: "0"}, {Name: "tarinsecurepath", Package: "archive/tar"}, - {Name: "tls10server", Package: "crypto/tls", Changed: 22, Old: "1"}, - {Name: "tls3des", Package: "crypto/tls", Changed: 23, Old: "1"}, {Name: "tlsmaxrsasize", Package: "crypto/tls"}, {Name: "tlsmlkem", Package: "crypto/tls", Changed: 24, Old: "0", Opaque: true}, - {Name: "tlsrsakex", Package: "crypto/tls", Changed: 22, Old: "1"}, {Name: "tlssecpmlkem", Package: "crypto/tls", Changed: 26, Old: "0", Opaque: true}, {Name: "tlssha1", Package: "crypto/tls", Changed: 25, Old: "1"}, - {Name: "tlsunsafeekm", Package: "crypto/tls", Changed: 22, Old: "1"}, // Mark tracebacklabels as Opaque so we don't generate a metric that we can't increment. // IncNonDefault uses a sync.Once, which involves sync.Mutex, and is not safe from a signal handler. // (Tracebacks are generated in signal-handlers.) @@ -77,7 +73,6 @@ var All = []Info{ {Name: "urlstrictcolons", Package: "net/url", Changed: 26, Old: "0"}, {Name: "winreadlinkvolume", Package: "os", Changed: 23, Old: "0"}, {Name: "winsymlink", Package: "os", Changed: 23, Old: "0"}, - {Name: "x509keypairleaf", Package: "crypto/tls", Changed: 23, Old: "0"}, {Name: "x509negativeserial", Package: "crypto/x509", Changed: 23, Old: "1"}, {Name: "x509rsacrt", Package: "crypto/x509", Changed: 24, Old: "0"}, {Name: "x509sha256skid", Package: "crypto/x509", Changed: 25, Old: "0"}, @@ -99,6 +94,11 @@ type RemovedInfo struct { var Removed = []RemovedInfo{ {Name: "x509sha1", Removed: 24}, {Name: "gotypesalias", Removed: 27}, + {Name: "tlsunsafeekm", Removed: 27}, // Old: "1" + {Name: "tlsrsakex", Removed: 27}, // Old: "1" + {Name: "tls3des", Removed: 27}, // Old: "1" + {Name: "tls10server", Removed: 27}, // Old: "1" + {Name: "x509keypairleaf", Removed: 27}, // Old: "0" } // Lookup returns the Info with the given name. diff --git a/src/internal/types/errors/codes_test.go b/src/internal/types/errors/codes_test.go index 2490ade5c36975..dd9e7f695bec4a 100644 --- a/src/internal/types/errors/codes_test.go +++ b/src/internal/types/errors/codes_test.go @@ -47,7 +47,7 @@ func TestErrorCodeExamples(t *testing.T) { func walkCodes(t *testing.T, f func(string, int, *ast.ValueSpec)) { t.Helper() fset := token.NewFileSet() - file, err := parser.ParseFile(fset, "codes.go", nil, parser.ParseComments) + file, err := parser.ParseFile(fset, "codes.go", nil, parser.ParseComments|parser.SkipObjectResolution) if err != nil { t.Fatal(err) } @@ -95,7 +95,7 @@ func checkExample(t *testing.T, example string) error { if !strings.HasPrefix(example, "package") { example = "package p\n\n" + example } - file, err := parser.ParseFile(fset, "example.go", example, 0) + file, err := parser.ParseFile(fset, "example.go", example, parser.SkipObjectResolution) if err != nil { t.Fatal(err) } diff --git a/src/internal/types/errors/generrordocs.go b/src/internal/types/errors/generrordocs.go index 613c77426928de..4df53e182796fd 100644 --- a/src/internal/types/errors/generrordocs.go +++ b/src/internal/types/errors/generrordocs.go @@ -67,7 +67,7 @@ func main() { func walkCodes(f func(string, *ast.ValueSpec)) { fset := token.NewFileSet() - file, err := parser.ParseFile(fset, "codes.go", nil, parser.ParseComments) + file, err := parser.ParseFile(fset, "codes.go", nil, parser.ParseComments|parser.SkipObjectResolution) if err != nil { log.Fatalf("ParseFile failed: %s", err) } diff --git a/src/iter/iter.go b/src/iter/iter.go index 57d19d047bcf2c..171ae4f30fb642 100644 --- a/src/iter/iter.go +++ b/src/iter/iter.go @@ -230,7 +230,7 @@ type Seq2[K, V any] func(yield func(K, V) bool) type coro struct{} -//go:linkname newcoro runtime.newcoro +//go:linknamestd newcoro runtime.newcoro func newcoro(func(*coro)) *coro //go:linknamestd coroswitch runtime.coroswitch diff --git a/src/math/big/nat.go b/src/math/big/nat.go index 43e36d309389f5..39981c28e07132 100644 --- a/src/math/big/nat.go +++ b/src/math/big/nat.go @@ -265,33 +265,27 @@ func (z nat) mulRange(stk *stack, a, b uint64) nat { return z.mul(stk, nat(nil).mulRange(stk, a, m), nat(nil).mulRange(stk, m+1, b)) } -// A stack provides temporary storage for complex calculations +// A stackInner provides temporary storage for complex calculations // such as multiplication and division. -// The stack is a simple slice of words, extended as needed -// to hold all the temporary storage for a calculation. -// In general, if a function takes a *stack, it expects a non-nil *stack. -// However, certain functions may allow passing a nil *stack instead, -// so that they can handle trivial stack-free cases without forcing the -// caller to obtain and free a stack that will be unused. These functions -// document that they accept a nil *stack in their doc comments. -type stack struct { +// It should only be used by [stack], below. +type stackInner struct { w []Word } -var stackPool sync.Pool +var stackPool sync.Pool // pool of *stackInner // getStack returns a temporary stack. // The caller must call [stack.free] to give up use of the stack when finished. -func getStack() *stack { - s, _ := stackPool.Get().(*stack) +func getStackInner() *stackInner { + s, _ := stackPool.Get().(*stackInner) if s == nil { - s = new(stack) + s = new(stackInner) } return s } // free returns the stack for use by another calculation. -func (s *stack) free() { +func (s *stackInner) free() { s.w = s.w[:0] stackPool.Put(s) } @@ -299,7 +293,7 @@ func (s *stack) free() { // save returns the current stack pointer. // A future call to restore with the same value // frees any temporaries allocated on the stack after the call to save. -func (s *stack) save() int { +func (s *stackInner) save() int { return len(s.w) } @@ -310,12 +304,12 @@ func (s *stack) save() int { // // which makes sure to pop any temporaries allocated in the current function // from the stack before returning. -func (s *stack) restore(n int) { +func (s *stackInner) restore(n int) { s.w = s.w[:n] } // nat returns a nat of n words, allocated on the stack. -func (s *stack) nat(n int) nat { +func (s *stackInner) nat(n int) nat { nr := (n + 3) &^ 3 // round up to multiple of 4 off := len(s.w) s.w = slices.Grow(s.w, nr) @@ -327,6 +321,63 @@ func (s *stack) nat(n int) nat { return x } +// A stack provides temporary storage for complex calculations +// such as multiplication and division. +// In general, if a function takes a *stack, it expects a non-nil *stack. +// However, certain functions may allow passing a nil *stack instead, +// so that they can handle trivial stack-free cases without forcing the +// caller to obtain and free a stack that will be unused. These functions +// document that they accept a nil *stack in their doc comments. +type stack struct { + si *stackInner +} + +func getStack() *stack { + return &stack{} +} +func (s *stack) free() { + si := s.si + if si != nil { + si.free() + } +} +func (s *stack) save() int { + si := s.si + if si == nil { + return 0 + } + return si.save() +} +func (s *stack) restore(n int) { + si := s.si + if si == nil { + return + } + si.restore(n) +} +func (s *stack) nat(n int) nat { + si := s.si + if si == nil { + if n <= 4 { + // For small allocations, just ask the allocator. + // It isn't worth pooling these allocations. + // See issue 73999. + r := slices.Grow(nat(nil), n) + r = r[:n] + if n > 0 { + r[0] = 0xabcdef + } + return r + } + si, _ = stackPool.Get().(*stackInner) + if si == nil { + si = new(stackInner) + } + s.si = si + } + return si.nat(n) +} + // bitLen returns the length of x in bits. // Unlike most methods, it works even if x is not normalized. func (x nat) bitLen() int { diff --git a/src/net/mail/message.go b/src/net/mail/message.go index 80140a467771b9..7c18c28d31485d 100644 --- a/src/net/mail/message.go +++ b/src/net/mail/message.go @@ -81,7 +81,7 @@ func readHeader(r *textproto.Reader) (map[string][]string, error) { if err != nil { return m, err } - return m, errors.New("malformed initial line: " + line) + return m, fmt.Errorf("malformed initial line: %q", line) } for { @@ -93,7 +93,7 @@ func readHeader(r *textproto.Reader) (map[string][]string, error) { // Key ends at first colon. k, v, ok := strings.Cut(kv, ":") if !ok { - return m, errors.New("malformed header line: " + kv) + return m, fmt.Errorf("malformed header line: %q", kv) } key := textproto.CanonicalMIMEHeaderKey(k) diff --git a/src/runtime/_mkmalloc/mkmalloc.go b/src/runtime/_mkmalloc/mkmalloc.go index 0a008388049bb0..727e17aa506ace 100644 --- a/src/runtime/_mkmalloc/mkmalloc.go +++ b/src/runtime/_mkmalloc/mkmalloc.go @@ -298,7 +298,7 @@ func inline(config generatorConfig) []byte { // Read the template file in. fset := token.NewFileSet() - f, err := parser.ParseFile(fset, config.file, nil, 0) + f, err := parser.ParseFile(fset, config.file, nil, parser.SkipObjectResolution) if err != nil { log.Fatalf("parsing %s: %v", config.file, err) } @@ -464,6 +464,13 @@ func foldIfCondition(node ast.Node, from, to string) ast.Node { cursor.InsertBefore(stmt) } } + if n.Else != nil { + if block, ok := n.Else.(*ast.BlockStmt); ok { + for i := len(block.List) - 1; i >= 0; i-- { + cursor.InsertAfter(block.List[i]) + } + } + } cursor.Delete() } } @@ -889,6 +896,11 @@ func benchmarkConfig(classes []class, sizeToSizeClass []uint8) generatorConfig { {foldCondition, "noscan_", str(false)}, }, }) + config.specs = append(config.specs, spec{ + templateFunc: "benchmarkScanSliceStub", + name: fmt.Sprintf("benchmarkMallocgcScanSlice%d", elemsize), + ops: []op{{subBasicLit, "size_", str(elemsize)}}, + }) } for size := 1; size < tinySize; size++ { @@ -920,6 +932,14 @@ func generateTopBenchmark(classes []class, sizeToSizeClass []uint8) string { for sc := uint8(1); sc <= scMax; sc++ { elemsize := classes[sc].size bench += fmt.Sprintf(`b.Run("size=%d", benchmarkMallocgcScan%d)`, elemsize, elemsize) + "\n" + + } + bench += `}) + b.Run("scan=scanslice", func(b *testing.B) { +` + for sc := uint8(1); sc <= scMax; sc++ { + elemsize := classes[sc].size + bench += fmt.Sprintf(`b.Run("size=%d", benchmarkMallocgcScanSlice%d)`, elemsize, elemsize) + "\n" } bench += `}) }` diff --git a/src/runtime/asm_amd64.s b/src/runtime/asm_amd64.s index 96b80de1f1dd52..559fe25adce6e8 100644 --- a/src/runtime/asm_amd64.s +++ b/src/runtime/asm_amd64.s @@ -942,6 +942,11 @@ TEXT ·asmcgocall(SB),NOSPLIT,$0-20 JEQ nosecret CALL ·secretEraseRegisters(SB) + get_tls(CX) + MOVQ g(CX), DI + MOVQ g_m(DI), R8 + MOVQ m_g0(R8), SI + nosecret: #endif MOVQ fn+0(FP), AX diff --git a/src/runtime/coro.go b/src/runtime/coro.go index 72c58b79647275..901e9391bfe640 100644 --- a/src/runtime/coro.go +++ b/src/runtime/coro.go @@ -34,7 +34,7 @@ type coro struct { lockedInt uint32 // mp's internal lockOSThread counter at coro creation time. } -//go:linkname newcoro +//go:linknamestd newcoro // newcoro creates a new coro containing a // goroutine blocked waiting to run f diff --git a/src/runtime/crash_cgo_test.go b/src/runtime/crash_cgo_test.go index fb95d368c23ee3..d4b7421ac09a65 100644 --- a/src/runtime/crash_cgo_test.go +++ b/src/runtime/crash_cgo_test.go @@ -15,6 +15,7 @@ import ( "internal/testenv" "os" "os/exec" + "path/filepath" "runtime" "strconv" "strings" @@ -118,6 +119,27 @@ func TestCgoCallbackX15(t *testing.T) { } } +func TestSecretCgo(t *testing.T) { + t.Parallel() + testenv.MustHaveGoBuild(t) + testenv.MustHaveCGO(t) + + exe := filepath.Join(t.TempDir(), "secretcgo.exe") + cmd := exec.Command(testenv.GoToolPath(t), "build", "-o", exe) + cmd.Dir = "testdata/testprogcgo" + cmd = testenv.CleanCmdEnv(cmd) + cmd.Env = append(cmd.Env, "GOEXPERIMENT=runtimesecret") + out, err := cmd.CombinedOutput() + if err != nil { + t.Fatalf("building testprogcgo with runtimesecret: %v\n%s", err, out) + } + + got := runBuiltTestProg(t, exe, "SecretCgo") + if want := "OK\n"; got != want { + t.Fatalf("expected %q, got:\n%s", want, got) + } +} + func TestCgoExternalThreadPanic(t *testing.T) { t.Parallel() if runtime.GOOS == "plan9" { diff --git a/src/runtime/malloc_bench_generated_test.go b/src/runtime/malloc_bench_generated_test.go index 34755f25feef53..f0a7473b6d6003 100644 --- a/src/runtime/malloc_bench_generated_test.go +++ b/src/runtime/malloc_bench_generated_test.go @@ -49,6 +49,13 @@ func benchmarkMallocgcScan8(b *testing.B) { }) } +func benchmarkMallocgcScanSlice8(b *testing.B) { + const size = 8 + for b.Loop() { + runtime.Escape(make([]*uint64, size/8)) + } +} + func benchmarkMallocgcNoscan16(b *testing.B) { const size = 16 b.Run("kind=new", func(b *testing.B) { @@ -89,6 +96,13 @@ func benchmarkMallocgcScan16(b *testing.B) { }) } +func benchmarkMallocgcScanSlice16(b *testing.B) { + const size = 16 + for b.Loop() { + runtime.Escape(make([]*uint64, size/8)) + } +} + func benchmarkMallocgcNoscan24(b *testing.B) { const size = 24 b.Run("kind=new", func(b *testing.B) { @@ -129,6 +143,13 @@ func benchmarkMallocgcScan24(b *testing.B) { }) } +func benchmarkMallocgcScanSlice24(b *testing.B) { + const size = 24 + for b.Loop() { + runtime.Escape(make([]*uint64, size/8)) + } +} + func benchmarkMallocgcNoscan32(b *testing.B) { const size = 32 b.Run("kind=new", func(b *testing.B) { @@ -169,6 +190,13 @@ func benchmarkMallocgcScan32(b *testing.B) { }) } +func benchmarkMallocgcScanSlice32(b *testing.B) { + const size = 32 + for b.Loop() { + runtime.Escape(make([]*uint64, size/8)) + } +} + func benchmarkMallocgcNoscan48(b *testing.B) { const size = 48 b.Run("kind=new", func(b *testing.B) { @@ -209,6 +237,13 @@ func benchmarkMallocgcScan48(b *testing.B) { }) } +func benchmarkMallocgcScanSlice48(b *testing.B) { + const size = 48 + for b.Loop() { + runtime.Escape(make([]*uint64, size/8)) + } +} + func benchmarkMallocgcNoscan64(b *testing.B) { const size = 64 b.Run("kind=new", func(b *testing.B) { @@ -249,6 +284,13 @@ func benchmarkMallocgcScan64(b *testing.B) { }) } +func benchmarkMallocgcScanSlice64(b *testing.B) { + const size = 64 + for b.Loop() { + runtime.Escape(make([]*uint64, size/8)) + } +} + func benchmarkMallocgcNoscan80(b *testing.B) { const size = 80 b.Run("kind=new", func(b *testing.B) { @@ -289,6 +331,13 @@ func benchmarkMallocgcScan80(b *testing.B) { }) } +func benchmarkMallocgcScanSlice80(b *testing.B) { + const size = 80 + for b.Loop() { + runtime.Escape(make([]*uint64, size/8)) + } +} + func benchmarkMallocgcNoscan96(b *testing.B) { const size = 96 b.Run("kind=new", func(b *testing.B) { @@ -329,6 +378,13 @@ func benchmarkMallocgcScan96(b *testing.B) { }) } +func benchmarkMallocgcScanSlice96(b *testing.B) { + const size = 96 + for b.Loop() { + runtime.Escape(make([]*uint64, size/8)) + } +} + func benchmarkMallocgcNoscan112(b *testing.B) { const size = 112 b.Run("kind=new", func(b *testing.B) { @@ -369,6 +425,13 @@ func benchmarkMallocgcScan112(b *testing.B) { }) } +func benchmarkMallocgcScanSlice112(b *testing.B) { + const size = 112 + for b.Loop() { + runtime.Escape(make([]*uint64, size/8)) + } +} + func benchmarkMallocgcNoscan128(b *testing.B) { const size = 128 b.Run("kind=new", func(b *testing.B) { @@ -409,6 +472,13 @@ func benchmarkMallocgcScan128(b *testing.B) { }) } +func benchmarkMallocgcScanSlice128(b *testing.B) { + const size = 128 + for b.Loop() { + runtime.Escape(make([]*uint64, size/8)) + } +} + func benchmarkMallocgcNoscan144(b *testing.B) { const size = 144 b.Run("kind=new", func(b *testing.B) { @@ -449,6 +519,13 @@ func benchmarkMallocgcScan144(b *testing.B) { }) } +func benchmarkMallocgcScanSlice144(b *testing.B) { + const size = 144 + for b.Loop() { + runtime.Escape(make([]*uint64, size/8)) + } +} + func benchmarkMallocgcNoscan160(b *testing.B) { const size = 160 b.Run("kind=new", func(b *testing.B) { @@ -489,6 +566,13 @@ func benchmarkMallocgcScan160(b *testing.B) { }) } +func benchmarkMallocgcScanSlice160(b *testing.B) { + const size = 160 + for b.Loop() { + runtime.Escape(make([]*uint64, size/8)) + } +} + func benchmarkMallocgcNoscan176(b *testing.B) { const size = 176 b.Run("kind=new", func(b *testing.B) { @@ -529,6 +613,13 @@ func benchmarkMallocgcScan176(b *testing.B) { }) } +func benchmarkMallocgcScanSlice176(b *testing.B) { + const size = 176 + for b.Loop() { + runtime.Escape(make([]*uint64, size/8)) + } +} + func benchmarkMallocgcNoscan192(b *testing.B) { const size = 192 b.Run("kind=new", func(b *testing.B) { @@ -569,6 +660,13 @@ func benchmarkMallocgcScan192(b *testing.B) { }) } +func benchmarkMallocgcScanSlice192(b *testing.B) { + const size = 192 + for b.Loop() { + runtime.Escape(make([]*uint64, size/8)) + } +} + func benchmarkMallocgcNoscan208(b *testing.B) { const size = 208 b.Run("kind=new", func(b *testing.B) { @@ -609,6 +707,13 @@ func benchmarkMallocgcScan208(b *testing.B) { }) } +func benchmarkMallocgcScanSlice208(b *testing.B) { + const size = 208 + for b.Loop() { + runtime.Escape(make([]*uint64, size/8)) + } +} + func benchmarkMallocgcNoscan224(b *testing.B) { const size = 224 b.Run("kind=new", func(b *testing.B) { @@ -649,6 +754,13 @@ func benchmarkMallocgcScan224(b *testing.B) { }) } +func benchmarkMallocgcScanSlice224(b *testing.B) { + const size = 224 + for b.Loop() { + runtime.Escape(make([]*uint64, size/8)) + } +} + func benchmarkMallocgcNoscan240(b *testing.B) { const size = 240 b.Run("kind=new", func(b *testing.B) { @@ -689,6 +801,13 @@ func benchmarkMallocgcScan240(b *testing.B) { }) } +func benchmarkMallocgcScanSlice240(b *testing.B) { + const size = 240 + for b.Loop() { + runtime.Escape(make([]*uint64, size/8)) + } +} + func benchmarkMallocgcNoscan256(b *testing.B) { const size = 256 b.Run("kind=new", func(b *testing.B) { @@ -729,6 +848,13 @@ func benchmarkMallocgcScan256(b *testing.B) { }) } +func benchmarkMallocgcScanSlice256(b *testing.B) { + const size = 256 + for b.Loop() { + runtime.Escape(make([]*uint64, size/8)) + } +} + func benchmarkMallocgcNoscan288(b *testing.B) { const size = 288 b.Run("kind=new", func(b *testing.B) { @@ -769,6 +895,13 @@ func benchmarkMallocgcScan288(b *testing.B) { }) } +func benchmarkMallocgcScanSlice288(b *testing.B) { + const size = 288 + for b.Loop() { + runtime.Escape(make([]*uint64, size/8)) + } +} + func benchmarkMallocgcNoscan320(b *testing.B) { const size = 320 b.Run("kind=new", func(b *testing.B) { @@ -809,6 +942,13 @@ func benchmarkMallocgcScan320(b *testing.B) { }) } +func benchmarkMallocgcScanSlice320(b *testing.B) { + const size = 320 + for b.Loop() { + runtime.Escape(make([]*uint64, size/8)) + } +} + func benchmarkMallocgcNoscan352(b *testing.B) { const size = 352 b.Run("kind=new", func(b *testing.B) { @@ -849,6 +989,13 @@ func benchmarkMallocgcScan352(b *testing.B) { }) } +func benchmarkMallocgcScanSlice352(b *testing.B) { + const size = 352 + for b.Loop() { + runtime.Escape(make([]*uint64, size/8)) + } +} + func benchmarkMallocgcNoscan384(b *testing.B) { const size = 384 b.Run("kind=new", func(b *testing.B) { @@ -889,6 +1036,13 @@ func benchmarkMallocgcScan384(b *testing.B) { }) } +func benchmarkMallocgcScanSlice384(b *testing.B) { + const size = 384 + for b.Loop() { + runtime.Escape(make([]*uint64, size/8)) + } +} + func benchmarkMallocgcNoscan416(b *testing.B) { const size = 416 b.Run("kind=new", func(b *testing.B) { @@ -929,6 +1083,13 @@ func benchmarkMallocgcScan416(b *testing.B) { }) } +func benchmarkMallocgcScanSlice416(b *testing.B) { + const size = 416 + for b.Loop() { + runtime.Escape(make([]*uint64, size/8)) + } +} + func benchmarkMallocgcNoscan448(b *testing.B) { const size = 448 b.Run("kind=new", func(b *testing.B) { @@ -969,6 +1130,13 @@ func benchmarkMallocgcScan448(b *testing.B) { }) } +func benchmarkMallocgcScanSlice448(b *testing.B) { + const size = 448 + for b.Loop() { + runtime.Escape(make([]*uint64, size/8)) + } +} + func benchmarkMallocgcNoscan480(b *testing.B) { const size = 480 b.Run("kind=new", func(b *testing.B) { @@ -1009,6 +1177,13 @@ func benchmarkMallocgcScan480(b *testing.B) { }) } +func benchmarkMallocgcScanSlice480(b *testing.B) { + const size = 480 + for b.Loop() { + runtime.Escape(make([]*uint64, size/8)) + } +} + func benchmarkMallocgcNoscan512(b *testing.B) { const size = 512 b.Run("kind=new", func(b *testing.B) { @@ -1049,6 +1224,13 @@ func benchmarkMallocgcScan512(b *testing.B) { }) } +func benchmarkMallocgcScanSlice512(b *testing.B) { + const size = 512 + for b.Loop() { + runtime.Escape(make([]*uint64, size/8)) + } +} + func benchmarkMallocgcTiny1(b *testing.B) { const size = 1 type s struct { @@ -1390,4 +1572,32 @@ func BenchmarkMallocgc(b *testing.B) { b.Run("size=480", benchmarkMallocgcScan480) b.Run("size=512", benchmarkMallocgcScan512) }) + b.Run("scan=scanslice", func(b *testing.B) { + b.Run("size=8", benchmarkMallocgcScanSlice8) + b.Run("size=16", benchmarkMallocgcScanSlice16) + b.Run("size=24", benchmarkMallocgcScanSlice24) + b.Run("size=32", benchmarkMallocgcScanSlice32) + b.Run("size=48", benchmarkMallocgcScanSlice48) + b.Run("size=64", benchmarkMallocgcScanSlice64) + b.Run("size=80", benchmarkMallocgcScanSlice80) + b.Run("size=96", benchmarkMallocgcScanSlice96) + b.Run("size=112", benchmarkMallocgcScanSlice112) + b.Run("size=128", benchmarkMallocgcScanSlice128) + b.Run("size=144", benchmarkMallocgcScanSlice144) + b.Run("size=160", benchmarkMallocgcScanSlice160) + b.Run("size=176", benchmarkMallocgcScanSlice176) + b.Run("size=192", benchmarkMallocgcScanSlice192) + b.Run("size=208", benchmarkMallocgcScanSlice208) + b.Run("size=224", benchmarkMallocgcScanSlice224) + b.Run("size=240", benchmarkMallocgcScanSlice240) + b.Run("size=256", benchmarkMallocgcScanSlice256) + b.Run("size=288", benchmarkMallocgcScanSlice288) + b.Run("size=320", benchmarkMallocgcScanSlice320) + b.Run("size=352", benchmarkMallocgcScanSlice352) + b.Run("size=384", benchmarkMallocgcScanSlice384) + b.Run("size=416", benchmarkMallocgcScanSlice416) + b.Run("size=448", benchmarkMallocgcScanSlice448) + b.Run("size=480", benchmarkMallocgcScanSlice480) + b.Run("size=512", benchmarkMallocgcScanSlice512) + }) } diff --git a/src/runtime/malloc_generated.go b/src/runtime/malloc_generated.go index 66420c0f9f8eeb..e2efa7168a9b5b 100644 --- a/src/runtime/malloc_generated.go +++ b/src/runtime/malloc_generated.go @@ -135,13 +135,7 @@ func mallocgcSmallScanNoHeaderSC1(size uintptr, typ *_type, needzero bool) unsaf publicationBarrier() - if writeBarrier.enabled { - - gcmarknewobject(span, uintptr(x)) - } else { - - span.freeIndexForScan = span.freeindex - } + span.freeIndexForScan = span.freeindex c.nextSample -= int64(elemsize) if c.nextSample < 0 || MemProfileRate != c.memProfRate { @@ -284,13 +278,7 @@ func mallocgcSmallScanNoHeaderSC2(size uintptr, typ *_type, needzero bool) unsaf publicationBarrier() - if writeBarrier.enabled { - - gcmarknewobject(span, uintptr(x)) - } else { - - span.freeIndexForScan = span.freeindex - } + span.freeIndexForScan = span.freeindex c.nextSample -= int64(elemsize) if c.nextSample < 0 || MemProfileRate != c.memProfRate { @@ -433,13 +421,7 @@ func mallocgcSmallScanNoHeaderSC3(size uintptr, typ *_type, needzero bool) unsaf publicationBarrier() - if writeBarrier.enabled { - - gcmarknewobject(span, uintptr(x)) - } else { - - span.freeIndexForScan = span.freeindex - } + span.freeIndexForScan = span.freeindex c.nextSample -= int64(elemsize) if c.nextSample < 0 || MemProfileRate != c.memProfRate { @@ -582,13 +564,7 @@ func mallocgcSmallScanNoHeaderSC4(size uintptr, typ *_type, needzero bool) unsaf publicationBarrier() - if writeBarrier.enabled { - - gcmarknewobject(span, uintptr(x)) - } else { - - span.freeIndexForScan = span.freeindex - } + span.freeIndexForScan = span.freeindex c.nextSample -= int64(elemsize) if c.nextSample < 0 || MemProfileRate != c.memProfRate { @@ -731,13 +707,7 @@ func mallocgcSmallScanNoHeaderSC5(size uintptr, typ *_type, needzero bool) unsaf publicationBarrier() - if writeBarrier.enabled { - - gcmarknewobject(span, uintptr(x)) - } else { - - span.freeIndexForScan = span.freeindex - } + span.freeIndexForScan = span.freeindex c.nextSample -= int64(elemsize) if c.nextSample < 0 || MemProfileRate != c.memProfRate { @@ -880,13 +850,7 @@ func mallocgcSmallScanNoHeaderSC6(size uintptr, typ *_type, needzero bool) unsaf publicationBarrier() - if writeBarrier.enabled { - - gcmarknewobject(span, uintptr(x)) - } else { - - span.freeIndexForScan = span.freeindex - } + span.freeIndexForScan = span.freeindex c.nextSample -= int64(elemsize) if c.nextSample < 0 || MemProfileRate != c.memProfRate { @@ -1029,13 +993,7 @@ func mallocgcSmallScanNoHeaderSC7(size uintptr, typ *_type, needzero bool) unsaf publicationBarrier() - if writeBarrier.enabled { - - gcmarknewobject(span, uintptr(x)) - } else { - - span.freeIndexForScan = span.freeindex - } + span.freeIndexForScan = span.freeindex c.nextSample -= int64(elemsize) if c.nextSample < 0 || MemProfileRate != c.memProfRate { @@ -1178,13 +1136,7 @@ func mallocgcSmallScanNoHeaderSC8(size uintptr, typ *_type, needzero bool) unsaf publicationBarrier() - if writeBarrier.enabled { - - gcmarknewobject(span, uintptr(x)) - } else { - - span.freeIndexForScan = span.freeindex - } + span.freeIndexForScan = span.freeindex c.nextSample -= int64(elemsize) if c.nextSample < 0 || MemProfileRate != c.memProfRate { @@ -1327,13 +1279,7 @@ func mallocgcSmallScanNoHeaderSC9(size uintptr, typ *_type, needzero bool) unsaf publicationBarrier() - if writeBarrier.enabled { - - gcmarknewobject(span, uintptr(x)) - } else { - - span.freeIndexForScan = span.freeindex - } + span.freeIndexForScan = span.freeindex c.nextSample -= int64(elemsize) if c.nextSample < 0 || MemProfileRate != c.memProfRate { @@ -1476,13 +1422,7 @@ func mallocgcSmallScanNoHeaderSC10(size uintptr, typ *_type, needzero bool) unsa publicationBarrier() - if writeBarrier.enabled { - - gcmarknewobject(span, uintptr(x)) - } else { - - span.freeIndexForScan = span.freeindex - } + span.freeIndexForScan = span.freeindex c.nextSample -= int64(elemsize) if c.nextSample < 0 || MemProfileRate != c.memProfRate { @@ -1589,13 +1529,7 @@ func mallocgcTinySC2(size uintptr, typ *_type, needzero bool) unsafe.Pointer { publicationBarrier() - if writeBarrier.enabled { - - gcmarknewobject(span, uintptr(x)) - } else { - - span.freeIndexForScan = span.freeindex - } + span.freeIndexForScan = span.freeindex c.nextSample -= int64(elemsize) if c.nextSample < 0 || MemProfileRate != c.memProfRate { @@ -1683,13 +1617,7 @@ func mallocgcSmallNoScanSC2(size uintptr, typ *_type, needzero bool) unsafe.Poin publicationBarrier() - if writeBarrier.enabled { - - gcmarknewobject(span, uintptr(x)) - } else { - - span.freeIndexForScan = span.freeindex - } + span.freeIndexForScan = span.freeindex c.nextSample -= int64(elemsize) if c.nextSample < 0 || MemProfileRate != c.memProfRate { @@ -1777,13 +1705,7 @@ func mallocgcSmallNoScanSC3(size uintptr, typ *_type, needzero bool) unsafe.Poin publicationBarrier() - if writeBarrier.enabled { - - gcmarknewobject(span, uintptr(x)) - } else { - - span.freeIndexForScan = span.freeindex - } + span.freeIndexForScan = span.freeindex c.nextSample -= int64(elemsize) if c.nextSample < 0 || MemProfileRate != c.memProfRate { @@ -1871,13 +1793,7 @@ func mallocgcSmallNoScanSC4(size uintptr, typ *_type, needzero bool) unsafe.Poin publicationBarrier() - if writeBarrier.enabled { - - gcmarknewobject(span, uintptr(x)) - } else { - - span.freeIndexForScan = span.freeindex - } + span.freeIndexForScan = span.freeindex c.nextSample -= int64(elemsize) if c.nextSample < 0 || MemProfileRate != c.memProfRate { @@ -1965,13 +1881,7 @@ func mallocgcSmallNoScanSC5(size uintptr, typ *_type, needzero bool) unsafe.Poin publicationBarrier() - if writeBarrier.enabled { - - gcmarknewobject(span, uintptr(x)) - } else { - - span.freeIndexForScan = span.freeindex - } + span.freeIndexForScan = span.freeindex c.nextSample -= int64(elemsize) if c.nextSample < 0 || MemProfileRate != c.memProfRate { @@ -2059,13 +1969,7 @@ func mallocgcSmallNoScanSC6(size uintptr, typ *_type, needzero bool) unsafe.Poin publicationBarrier() - if writeBarrier.enabled { - - gcmarknewobject(span, uintptr(x)) - } else { - - span.freeIndexForScan = span.freeindex - } + span.freeIndexForScan = span.freeindex c.nextSample -= int64(elemsize) if c.nextSample < 0 || MemProfileRate != c.memProfRate { @@ -2153,13 +2057,7 @@ func mallocgcSmallNoScanSC7(size uintptr, typ *_type, needzero bool) unsafe.Poin publicationBarrier() - if writeBarrier.enabled { - - gcmarknewobject(span, uintptr(x)) - } else { - - span.freeIndexForScan = span.freeindex - } + span.freeIndexForScan = span.freeindex c.nextSample -= int64(elemsize) if c.nextSample < 0 || MemProfileRate != c.memProfRate { @@ -2247,13 +2145,7 @@ func mallocgcSmallNoScanSC8(size uintptr, typ *_type, needzero bool) unsafe.Poin publicationBarrier() - if writeBarrier.enabled { - - gcmarknewobject(span, uintptr(x)) - } else { - - span.freeIndexForScan = span.freeindex - } + span.freeIndexForScan = span.freeindex c.nextSample -= int64(elemsize) if c.nextSample < 0 || MemProfileRate != c.memProfRate { @@ -2341,13 +2233,7 @@ func mallocgcSmallNoScanSC9(size uintptr, typ *_type, needzero bool) unsafe.Poin publicationBarrier() - if writeBarrier.enabled { - - gcmarknewobject(span, uintptr(x)) - } else { - - span.freeIndexForScan = span.freeindex - } + span.freeIndexForScan = span.freeindex c.nextSample -= int64(elemsize) if c.nextSample < 0 || MemProfileRate != c.memProfRate { @@ -2435,13 +2321,7 @@ func mallocgcSmallNoScanSC10(size uintptr, typ *_type, needzero bool) unsafe.Poi publicationBarrier() - if writeBarrier.enabled { - - gcmarknewobject(span, uintptr(x)) - } else { - - span.freeIndexForScan = span.freeindex - } + span.freeIndexForScan = span.freeindex c.nextSample -= int64(elemsize) if c.nextSample < 0 || MemProfileRate != c.memProfRate { diff --git a/src/runtime/malloc_stubs.go b/src/runtime/malloc_stubs.go index c19846b47a99ce..8320da3e4c7f49 100644 --- a/src/runtime/malloc_stubs.go +++ b/src/runtime/malloc_stubs.go @@ -256,7 +256,7 @@ func smallStub(size uintptr, typ *_type, needzero bool) unsafe.Pointer { // but see uninitialized memory or stale heap bits. publicationBarrier() - if writeBarrier.enabled { + if isSlowPath_ && writeBarrier.enabled { // Allocate black during GC. // All slots hold nil so no scanning is needed. // This may be racing with GC so do it atomically if there can be @@ -419,7 +419,7 @@ func tinyStub(size uintptr, typ *_type, needzero bool) unsafe.Pointer { // but see uninitialized memory or stale heap bits. publicationBarrier() - if writeBarrier.enabled { + if isSlowPath_ && writeBarrier.enabled { // Allocate black during GC. // All slots hold nil so no scanning is needed. // This may be racing with GC so do it atomically if there can be diff --git a/src/runtime/malloc_stubs_test.go b/src/runtime/malloc_stubs_test.go index 10e34ade9f24da..7b536977de5386 100644 --- a/src/runtime/malloc_stubs_test.go +++ b/src/runtime/malloc_stubs_test.go @@ -57,3 +57,10 @@ func benchmarkStub(b *testing.B) { } }) } + +func benchmarkScanSliceStub(b *testing.B) { + const size = size_ + for b.Loop() { + runtime.Escape(make([]*uint64, size/8)) + } +} diff --git a/src/runtime/metrics/description_test.go b/src/runtime/metrics/description_test.go index 71c7b00b02ed28..a9dae4d761a32f 100644 --- a/src/runtime/metrics/description_test.go +++ b/src/runtime/metrics/description_test.go @@ -94,7 +94,7 @@ func TestDocs(t *testing.T) { t.Fatal(err) } fset := token.NewFileSet() - f, err := parser.ParseFile(fset, "doc.go", src, parser.ParseComments) + f, err := parser.ParseFile(fset, "doc.go", src, parser.ParseComments|parser.SkipObjectResolution) if err != nil { t.Fatal(err) } diff --git a/src/runtime/metrics/doc.go b/src/runtime/metrics/doc.go index edd955928e332e..22648f4008c73f 100644 --- a/src/runtime/metrics/doc.go +++ b/src/runtime/metrics/doc.go @@ -377,30 +377,14 @@ Below is the full list of supported metrics, ordered lexicographically. package due to a non-default GODEBUG=tarinsecurepath=... setting. - /godebug/non-default-behavior/tls10server:events - The number of non-default behaviors executed by the crypto/tls - package due to a non-default GODEBUG=tls10server=... setting. - - /godebug/non-default-behavior/tls3des:events - The number of non-default behaviors executed by the crypto/tls - package due to a non-default GODEBUG=tls3des=... setting. - /godebug/non-default-behavior/tlsmaxrsasize:events The number of non-default behaviors executed by the crypto/tls package due to a non-default GODEBUG=tlsmaxrsasize=... setting. - /godebug/non-default-behavior/tlsrsakex:events - The number of non-default behaviors executed by the crypto/tls - package due to a non-default GODEBUG=tlsrsakex=... setting. - /godebug/non-default-behavior/tlssha1:events The number of non-default behaviors executed by the crypto/tls package due to a non-default GODEBUG=tlssha1=... setting. - /godebug/non-default-behavior/tlsunsafeekm:events - The number of non-default behaviors executed by the crypto/tls - package due to a non-default GODEBUG=tlsunsafeekm=... setting. - /godebug/non-default-behavior/updatemaxprocs:events The number of non-default behaviors executed by the runtime package due to a non-default GODEBUG=updatemaxprocs=... setting. @@ -423,11 +407,6 @@ Below is the full list of supported metrics, ordered lexicographically. The number of non-default behaviors executed by the os package due to a non-default GODEBUG=winsymlink=... setting. - /godebug/non-default-behavior/x509keypairleaf:events - The number of non-default behaviors executed by the crypto/tls - package due to a non-default GODEBUG=x509keypairleaf=... - setting. - /godebug/non-default-behavior/x509negativeserial:events The number of non-default behaviors executed by the crypto/x509 package due to a non-default GODEBUG=x509negativeserial=... diff --git a/src/runtime/testdata/testprogcgo/secretcgo.go b/src/runtime/testdata/testprogcgo/secretcgo.go new file mode 100644 index 00000000000000..a4fec846294b0a --- /dev/null +++ b/src/runtime/testdata/testprogcgo/secretcgo.go @@ -0,0 +1,31 @@ +// 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. + +//go:build goexperiment.runtimesecret + +package main + +/* +static int cAdd(int a, int b) { return a + b; } +*/ +import "C" + +import ( + "fmt" + "runtime/secret" +) + +func init() { + register("SecretCgo", SecretCgo) +} + +func SecretCgo() { + secret.Do(func() { + r := C.cAdd(1, 2) + if r != 3 { + panic(fmt.Sprintf("got %d, want 3", r)) + } + }) + fmt.Println("OK") +} diff --git a/test/fixedbugs/issue74626.go b/test/fixedbugs/issue74626.go new file mode 100644 index 00000000000000..b41fbe4ec4e454 --- /dev/null +++ b/test/fixedbugs/issue74626.go @@ -0,0 +1,25 @@ +// errorcheck -goexperiment fieldtrack + +// 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. + +package main + +type Fooer interface { + Foo() string +} + +type FooImpl struct{} + +//go:nointerface +func (FooImpl) Foo() string { return "foo" } + +func toInterface[T Fooer](fooer T) Fooer { + return fooer +} + +func main() { + var iface Fooer = toInterface(FooImpl{}) // ERROR "does not satisfy Fooer" + iface.Foo() +}