mirror of https://github.com/golang/go.git
Merge branch 'golang:master' into master
This commit is contained in:
commit
77171822fc
|
|
@ -0,0 +1,7 @@
|
||||||
|
pkg go/ast, const FilterFuncDuplicates //deprecated #73088
|
||||||
|
pkg go/ast, const FilterImportDuplicates //deprecated #73088
|
||||||
|
pkg go/ast, const FilterUnassociatedComments //deprecated #73088
|
||||||
|
pkg go/ast, func FilterPackage //deprecated #73088
|
||||||
|
pkg go/ast, func MergePackageFiles //deprecated #73088
|
||||||
|
pkg go/ast, func PackageExports //deprecated #73088
|
||||||
|
pkg go/ast, type MergeMode //deprecated #73088
|
||||||
|
|
@ -0,0 +1,4 @@
|
||||||
|
The [ast.FilterPackage], [ast.PackageExports], and
|
||||||
|
[ast.MergePackageFiles] functions, and the [MergeMode] type and its
|
||||||
|
constants, are all deprecated, as they are for use only with the
|
||||||
|
long-deprecated [ast.Object] and [ast.Package] machinery.
|
||||||
|
|
@ -1,6 +1,10 @@
|
||||||
<!--
|
<!--
|
||||||
Output from relnote todo that was generated and reviewed on 2025-05-23, plus summary info from bug/CL: -->
|
Output from relnote todo that was generated and reviewed on 2025-05-23, plus summary info from bug/CL: -->
|
||||||
|
|
||||||
|
### TODO
|
||||||
|
|
||||||
|
**Please turn these into proper release notes**
|
||||||
|
|
||||||
<!-- TODO: CL 420114 has a RELNOTE comment without a suggested text (from RELNOTE comment in https://go.dev/cl/420114) -->
|
<!-- TODO: CL 420114 has a RELNOTE comment without a suggested text (from RELNOTE comment in https://go.dev/cl/420114) -->
|
||||||
all: implement plugin build mode for riscv64
|
all: implement plugin build mode for riscv64
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -853,6 +853,10 @@ func IsAddressable(n Node) bool {
|
||||||
//
|
//
|
||||||
// calling StaticValue on the "int(y)" expression returns the outer
|
// calling StaticValue on the "int(y)" expression returns the outer
|
||||||
// "g()" expression.
|
// "g()" expression.
|
||||||
|
//
|
||||||
|
// NOTE: StaticValue can return a result with a different type than
|
||||||
|
// n's type because it can traverse through OCONVNOP operations.
|
||||||
|
// TODO: consider reapplying OCONVNOP operations to the result. See https://go.dev/cl/676517.
|
||||||
func StaticValue(n Node) Node {
|
func StaticValue(n Node) Node {
|
||||||
for {
|
for {
|
||||||
switch n1 := n.(type) {
|
switch n1 := n.(type) {
|
||||||
|
|
|
||||||
|
|
@ -249,14 +249,14 @@ func (o *orderState) addrTemp(n ir.Node) ir.Node {
|
||||||
if (v.Op() == ir.OSTRUCTLIT || v.Op() == ir.OARRAYLIT) && !base.Ctxt.IsFIPS() {
|
if (v.Op() == ir.OSTRUCTLIT || v.Op() == ir.OARRAYLIT) && !base.Ctxt.IsFIPS() {
|
||||||
if ir.IsZero(v) && 0 < v.Type().Size() && v.Type().Size() <= abi.ZeroValSize {
|
if ir.IsZero(v) && 0 < v.Type().Size() && v.Type().Size() <= abi.ZeroValSize {
|
||||||
// This zero value can be represented by the read-only zeroVal.
|
// This zero value can be represented by the read-only zeroVal.
|
||||||
zeroVal := ir.NewLinksymExpr(v.Pos(), ir.Syms.ZeroVal, v.Type())
|
zeroVal := ir.NewLinksymExpr(v.Pos(), ir.Syms.ZeroVal, n.Type())
|
||||||
vstat := typecheck.Expr(zeroVal).(*ir.LinksymOffsetExpr)
|
vstat := typecheck.Expr(zeroVal).(*ir.LinksymOffsetExpr)
|
||||||
return vstat
|
return vstat
|
||||||
}
|
}
|
||||||
if isStaticCompositeLiteral(v) {
|
if isStaticCompositeLiteral(v) {
|
||||||
// v can be directly represented in the read-only data section.
|
// v can be directly represented in the read-only data section.
|
||||||
lit := v.(*ir.CompLitExpr)
|
lit := v.(*ir.CompLitExpr)
|
||||||
vstat := readonlystaticname(lit.Type())
|
vstat := readonlystaticname(n.Type())
|
||||||
fixedlit(inInitFunction, initKindStatic, lit, vstat, nil) // nil init
|
fixedlit(inInitFunction, initKindStatic, lit, vstat, nil) // nil init
|
||||||
vstat = typecheck.Expr(vstat).(*ir.Name)
|
vstat = typecheck.Expr(vstat).(*ir.Name)
|
||||||
return vstat
|
return vstat
|
||||||
|
|
|
||||||
|
|
@ -337,6 +337,7 @@ func runGet(ctx context.Context, cmd *base.Command, args []string) {
|
||||||
r.performLocalQueries(ctx)
|
r.performLocalQueries(ctx)
|
||||||
r.performPathQueries(ctx)
|
r.performPathQueries(ctx)
|
||||||
r.performToolQueries(ctx)
|
r.performToolQueries(ctx)
|
||||||
|
r.performWorkQueries(ctx)
|
||||||
|
|
||||||
for {
|
for {
|
||||||
r.performWildcardQueries(ctx)
|
r.performWildcardQueries(ctx)
|
||||||
|
|
@ -513,6 +514,7 @@ type resolver struct {
|
||||||
pathQueries []*query // package path literal queries in original order
|
pathQueries []*query // package path literal queries in original order
|
||||||
wildcardQueries []*query // path wildcard queries in original order
|
wildcardQueries []*query // path wildcard queries in original order
|
||||||
patternAllQueries []*query // queries with the pattern "all"
|
patternAllQueries []*query // queries with the pattern "all"
|
||||||
|
workQueries []*query // queries with the pattern "work"
|
||||||
toolQueries []*query // queries with the pattern "tool"
|
toolQueries []*query // queries with the pattern "tool"
|
||||||
|
|
||||||
// Indexed "none" queries. These are also included in the slices above;
|
// Indexed "none" queries. These are also included in the slices above;
|
||||||
|
|
@ -578,6 +580,8 @@ func newResolver(ctx context.Context, queries []*query) *resolver {
|
||||||
for _, q := range queries {
|
for _, q := range queries {
|
||||||
if q.pattern == "all" {
|
if q.pattern == "all" {
|
||||||
r.patternAllQueries = append(r.patternAllQueries, q)
|
r.patternAllQueries = append(r.patternAllQueries, q)
|
||||||
|
} else if q.pattern == "work" {
|
||||||
|
r.workQueries = append(r.workQueries, q)
|
||||||
} else if q.pattern == "tool" {
|
} else if q.pattern == "tool" {
|
||||||
r.toolQueries = append(r.toolQueries, q)
|
r.toolQueries = append(r.toolQueries, q)
|
||||||
} else if q.patternIsLocal {
|
} else if q.patternIsLocal {
|
||||||
|
|
@ -1070,6 +1074,37 @@ func (r *resolver) performToolQueries(ctx context.Context) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// performWorkQueries populates the candidates for each query whose pattern is "work".
|
||||||
|
// The candidate module to resolve the work pattern is exactly the single main module.
|
||||||
|
func (r *resolver) performWorkQueries(ctx context.Context) {
|
||||||
|
for _, q := range r.workQueries {
|
||||||
|
q.pathOnce(q.pattern, func() pathSet {
|
||||||
|
// TODO(matloob): Maybe export MainModules.mustGetSingleMainModule and call that.
|
||||||
|
// There are a few other places outside the modload package where we expect
|
||||||
|
// a single main module.
|
||||||
|
if len(modload.MainModules.Versions()) != 1 {
|
||||||
|
panic("internal error: number of main modules is not exactly one in resolution phase of go get")
|
||||||
|
}
|
||||||
|
mainModule := modload.MainModules.Versions()[0]
|
||||||
|
|
||||||
|
// We know what the result is going to be, assuming the main module is not
|
||||||
|
// empty, (it's the main module itself) but first check to see that there
|
||||||
|
// are packages in the main module, so that if there aren't any, we can
|
||||||
|
// return the expected warning that the pattern matched no packages.
|
||||||
|
match := modload.MatchInModule(ctx, q.pattern, mainModule, imports.AnyTags())
|
||||||
|
if len(match.Errs) > 0 {
|
||||||
|
return pathSet{err: match.Errs[0]}
|
||||||
|
}
|
||||||
|
if len(match.Pkgs) == 0 {
|
||||||
|
search.WarnUnmatched([]*search.Match{match})
|
||||||
|
return pathSet{} // There are no packages in the main module, so the main module isn't needed to resolve them.
|
||||||
|
}
|
||||||
|
|
||||||
|
return pathSet{pkgMods: []module.Version{mainModule}}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// performPatternAllQueries populates the candidates for each query whose
|
// performPatternAllQueries populates the candidates for each query whose
|
||||||
// pattern is "all".
|
// pattern is "all".
|
||||||
//
|
//
|
||||||
|
|
|
||||||
|
|
@ -29,6 +29,10 @@ stderr '^go: example\.net/emptysubdir/subdir/\.\.\.: module example\.net/emptysu
|
||||||
! go get builtin/... # in GOROOT/src, but contains no packages
|
! go get builtin/... # in GOROOT/src, but contains no packages
|
||||||
stderr '^go: builtin/...: malformed module path "builtin": missing dot in first path element$'
|
stderr '^go: builtin/...: malformed module path "builtin": missing dot in first path element$'
|
||||||
|
|
||||||
|
cd ../subdirmod
|
||||||
|
go get work
|
||||||
|
stderr -count=1 'matched no packages'
|
||||||
|
|
||||||
-- go.mod --
|
-- go.mod --
|
||||||
module example.net/emptysubdir
|
module example.net/emptysubdir
|
||||||
|
|
||||||
|
|
@ -38,3 +42,7 @@ go 1.16
|
||||||
package emptysubdir
|
package emptysubdir
|
||||||
-- subdir/README.txt --
|
-- subdir/README.txt --
|
||||||
This module intentionally does not contain any p
|
This module intentionally does not contain any p
|
||||||
|
-- subdirmod/go.mod --
|
||||||
|
module example.net/emptysubdir/subdirmod
|
||||||
|
|
||||||
|
go 1.16
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,46 @@
|
||||||
|
# Test go get with the work pattern.
|
||||||
|
|
||||||
|
# go get work gets dependencies to satisfy missing imports in the
|
||||||
|
# main modules' package graph. Before the 'work' pattern existed, users
|
||||||
|
# would have to run './...' in the root of the work (main) module.
|
||||||
|
cp go.mod go.mod.orig
|
||||||
|
go get work
|
||||||
|
cmp go.mod go.mod.want
|
||||||
|
|
||||||
|
# 'go get work' and 'go get all' behave very differently. Because
|
||||||
|
# 'all' evaluates to work packages but also to their dependencies,
|
||||||
|
# 'go get all' will run the 'get' logic on all the dependency module
|
||||||
|
# packages, bumping all their modules to the latest versions.
|
||||||
|
cp go.mod.orig go.mod
|
||||||
|
go get all
|
||||||
|
cmp go.mod go.mod.all.want
|
||||||
|
-- go.mod --
|
||||||
|
module example.com/a
|
||||||
|
|
||||||
|
go 1.25
|
||||||
|
-- go.mod.want --
|
||||||
|
module example.com/a
|
||||||
|
|
||||||
|
go 1.25
|
||||||
|
|
||||||
|
require rsc.io/quote v1.5.2
|
||||||
|
|
||||||
|
require (
|
||||||
|
golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c // indirect
|
||||||
|
rsc.io/sampler v1.3.0 // indirect
|
||||||
|
)
|
||||||
|
-- go.mod.all.want --
|
||||||
|
module example.com/a
|
||||||
|
|
||||||
|
go 1.25
|
||||||
|
|
||||||
|
require rsc.io/quote v1.5.2
|
||||||
|
|
||||||
|
require (
|
||||||
|
golang.org/x/text v0.3.0 // indirect
|
||||||
|
rsc.io/sampler v1.99.99 // indirect
|
||||||
|
)
|
||||||
|
-- a.go --
|
||||||
|
package a
|
||||||
|
|
||||||
|
import _ "rsc.io/quote"
|
||||||
|
|
@ -20,6 +20,13 @@ go get ./...
|
||||||
cmp go.mod go.mod.want
|
cmp go.mod go.mod.want
|
||||||
cmp go.sum go.sum.want
|
cmp go.sum go.sum.want
|
||||||
|
|
||||||
|
# Test go get with an incomplete module using a "work" query.
|
||||||
|
cp go.mod.orig go.mod
|
||||||
|
rm go.sum
|
||||||
|
go get work
|
||||||
|
cmp go.mod go.mod.want
|
||||||
|
cmp go.sum go.sum.want
|
||||||
|
|
||||||
# Test go get with an incomplete module using a path query that can be resolved.
|
# Test go get with an incomplete module using a path query that can be resolved.
|
||||||
cp go.mod.orig go.mod
|
cp go.mod.orig go.mod
|
||||||
rm go.sum
|
rm go.sum
|
||||||
|
|
@ -149,20 +149,18 @@ func legacyTypeAndHashFromPublicKey(pub crypto.PublicKey) (sigType uint8, hash c
|
||||||
var rsaSignatureSchemes = []struct {
|
var rsaSignatureSchemes = []struct {
|
||||||
scheme SignatureScheme
|
scheme SignatureScheme
|
||||||
minModulusBytes int
|
minModulusBytes int
|
||||||
maxVersion uint16
|
|
||||||
}{
|
}{
|
||||||
// RSA-PSS is used with PSSSaltLengthEqualsHash, and requires
|
// RSA-PSS is used with PSSSaltLengthEqualsHash, and requires
|
||||||
// emLen >= hLen + sLen + 2
|
// emLen >= hLen + sLen + 2
|
||||||
{PSSWithSHA256, crypto.SHA256.Size()*2 + 2, VersionTLS13},
|
{PSSWithSHA256, crypto.SHA256.Size()*2 + 2},
|
||||||
{PSSWithSHA384, crypto.SHA384.Size()*2 + 2, VersionTLS13},
|
{PSSWithSHA384, crypto.SHA384.Size()*2 + 2},
|
||||||
{PSSWithSHA512, crypto.SHA512.Size()*2 + 2, VersionTLS13},
|
{PSSWithSHA512, crypto.SHA512.Size()*2 + 2},
|
||||||
// PKCS #1 v1.5 uses prefixes from hashPrefixes in crypto/rsa, and requires
|
// PKCS #1 v1.5 uses prefixes from hashPrefixes in crypto/rsa, and requires
|
||||||
// emLen >= len(prefix) + hLen + 11
|
// emLen >= len(prefix) + hLen + 11
|
||||||
// TLS 1.3 dropped support for PKCS #1 v1.5 in favor of RSA-PSS.
|
{PKCS1WithSHA256, 19 + crypto.SHA256.Size() + 11},
|
||||||
{PKCS1WithSHA256, 19 + crypto.SHA256.Size() + 11, VersionTLS12},
|
{PKCS1WithSHA384, 19 + crypto.SHA384.Size() + 11},
|
||||||
{PKCS1WithSHA384, 19 + crypto.SHA384.Size() + 11, VersionTLS12},
|
{PKCS1WithSHA512, 19 + crypto.SHA512.Size() + 11},
|
||||||
{PKCS1WithSHA512, 19 + crypto.SHA512.Size() + 11, VersionTLS12},
|
{PKCS1WithSHA1, 15 + crypto.SHA1.Size() + 11},
|
||||||
{PKCS1WithSHA1, 15 + crypto.SHA1.Size() + 11, VersionTLS12},
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// signatureSchemesForCertificate returns the list of supported SignatureSchemes
|
// signatureSchemesForCertificate returns the list of supported SignatureSchemes
|
||||||
|
|
@ -202,7 +200,7 @@ func signatureSchemesForCertificate(version uint16, cert *Certificate) []Signatu
|
||||||
size := pub.Size()
|
size := pub.Size()
|
||||||
sigAlgs = make([]SignatureScheme, 0, len(rsaSignatureSchemes))
|
sigAlgs = make([]SignatureScheme, 0, len(rsaSignatureSchemes))
|
||||||
for _, candidate := range rsaSignatureSchemes {
|
for _, candidate := range rsaSignatureSchemes {
|
||||||
if size >= candidate.minModulusBytes && version <= candidate.maxVersion {
|
if size >= candidate.minModulusBytes {
|
||||||
sigAlgs = append(sigAlgs, candidate.scheme)
|
sigAlgs = append(sigAlgs, candidate.scheme)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -219,10 +217,9 @@ func signatureSchemesForCertificate(version uint16, cert *Certificate) []Signatu
|
||||||
}
|
}
|
||||||
|
|
||||||
// Filter out any unsupported signature algorithms, for example due to
|
// Filter out any unsupported signature algorithms, for example due to
|
||||||
// FIPS 140-3 policy, tlssha1=0, or any downstream changes to defaults.go.
|
// FIPS 140-3 policy, tlssha1=0, or protocol version.
|
||||||
supportedAlgs := supportedSignatureAlgorithms(version)
|
|
||||||
sigAlgs = slices.DeleteFunc(sigAlgs, func(sigAlg SignatureScheme) bool {
|
sigAlgs = slices.DeleteFunc(sigAlgs, func(sigAlg SignatureScheme) bool {
|
||||||
return !isSupportedSignatureAlgorithm(sigAlg, supportedAlgs)
|
return isDisabledSignatureAlgorithm(version, sigAlg, false)
|
||||||
})
|
})
|
||||||
|
|
||||||
return sigAlgs
|
return sigAlgs
|
||||||
|
|
|
||||||
|
|
@ -20,8 +20,6 @@
|
||||||
"TLS-ECH-Client-Reject-NoChannelID-TLS13": "We don't support sending channel ID",
|
"TLS-ECH-Client-Reject-NoChannelID-TLS13": "We don't support sending channel ID",
|
||||||
"TLS-ECH-Client-Reject-NoChannelID-TLS12": "We don't support sending channel ID",
|
"TLS-ECH-Client-Reject-NoChannelID-TLS12": "We don't support sending channel ID",
|
||||||
|
|
||||||
"ServerAuth-SHA1-Fallback*": "We don't ever support SHA-1 in TLS 1.2, so we fail if there are no signature_algorithms",
|
|
||||||
|
|
||||||
"TLS-ECH-Client-GREASE-IgnoreHRRExtension": "We don't support ECH GREASE because we don't fallback to plaintext",
|
"TLS-ECH-Client-GREASE-IgnoreHRRExtension": "We don't support ECH GREASE because we don't fallback to plaintext",
|
||||||
"TLS-ECH-Client-NoSupportedConfigs-GREASE": "We don't support ECH GREASE because we don't fallback to plaintext",
|
"TLS-ECH-Client-NoSupportedConfigs-GREASE": "We don't support ECH GREASE because we don't fallback to plaintext",
|
||||||
"TLS-ECH-Client-GREASEExtensions": "We don't support ECH GREASE because we don't fallback to plaintext",
|
"TLS-ECH-Client-GREASEExtensions": "We don't support ECH GREASE because we don't fallback to plaintext",
|
||||||
|
|
@ -40,7 +38,19 @@
|
||||||
"PostQuantumNotEnabledByDefaultInClients": "We do enable it by default!",
|
"PostQuantumNotEnabledByDefaultInClients": "We do enable it by default!",
|
||||||
"*-Kyber-TLS13": "We don't support Kyber, only ML-KEM (BoGo bug ignoring AllCurves?)",
|
"*-Kyber-TLS13": "We don't support Kyber, only ML-KEM (BoGo bug ignoring AllCurves?)",
|
||||||
|
|
||||||
"*-SignDefault-*": "TODO, partially it encodes BoringSSL defaults, partially we might be missing some implicit behavior of a missing flag",
|
"*-RSA_PKCS1_SHA256_LEGACY-TLS13": "We don't support the legacy PKCS#1 v1.5 codepoint for TLS 1.3",
|
||||||
|
"*-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)",
|
||||||
|
|
||||||
|
"*_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)",
|
||||||
|
"ServerAuth-SHA1-Fallback*": "We don't support SHA-1 in TLS 1.2 (without tlssha1=1), so we fail if there are no signature_algorithms",
|
||||||
|
|
||||||
|
"Agree-Digest-SHA256": "We select signature algorithms in peer preference order. We should consider changing this.",
|
||||||
|
"ECDSACurveMismatch-Verify-TLS13": "We don't enforce the curve when verifying. This is a bug. We need to fix this.",
|
||||||
|
"*-Verify-ECDSA_P224_SHA256-TLS13": "Side effect of the bug above. BoGo sends a P-256 sigAlg with a P-224 key, and we allow it.",
|
||||||
|
|
||||||
"V2ClientHello-*": "We don't support SSLv2",
|
"V2ClientHello-*": "We don't support SSLv2",
|
||||||
"SendV2ClientHello*": "We don't support SSLv2",
|
"SendV2ClientHello*": "We don't support SSLv2",
|
||||||
|
|
@ -62,8 +72,10 @@
|
||||||
"CurveID-Resume*": "unexposed curveID is not stored in the ticket yet",
|
"CurveID-Resume*": "unexposed curveID is not stored in the ticket yet",
|
||||||
"BadRSAClientKeyExchange-4": "crypto/tls doesn't check the version number in the premaster secret - see processClientKeyExchange comment",
|
"BadRSAClientKeyExchange-4": "crypto/tls doesn't check the version number in the premaster secret - see processClientKeyExchange comment",
|
||||||
"BadRSAClientKeyExchange-5": "crypto/tls doesn't check the version number in the premaster secret - see processClientKeyExchange comment",
|
"BadRSAClientKeyExchange-5": "crypto/tls doesn't check the version number in the premaster secret - see processClientKeyExchange comment",
|
||||||
"CheckLeafCurve": "TODO: first pass, this should be fixed",
|
|
||||||
"SupportTicketsWithSessionID": "We don't support session ID resumption",
|
"SupportTicketsWithSessionID": "We don't support session ID resumption",
|
||||||
|
"ResumeTLS12SessionID-TLS13": "We don't support session ID resumption",
|
||||||
|
|
||||||
|
"CheckLeafCurve": "TODO: first pass, this should be fixed",
|
||||||
"KeyUpdate-RequestACK": "TODO: first pass, this should be fixed",
|
"KeyUpdate-RequestACK": "TODO: first pass, this should be fixed",
|
||||||
"SupportedVersionSelection-TLS12": "TODO: first pass, this should be fixed",
|
"SupportedVersionSelection-TLS12": "TODO: first pass, this should be fixed",
|
||||||
"UnsolicitedServerNameAck-TLS-TLS1": "TODO: first pass, this should be fixed",
|
"UnsolicitedServerNameAck-TLS-TLS1": "TODO: first pass, this should be fixed",
|
||||||
|
|
@ -88,19 +100,6 @@
|
||||||
"Resume-Server-OmitPSKsOnSecondClientHello": "TODO: first pass, this should be fixed",
|
"Resume-Server-OmitPSKsOnSecondClientHello": "TODO: first pass, this should be fixed",
|
||||||
"Renegotiate-Server-Forbidden": "TODO: first pass, this should be fixed",
|
"Renegotiate-Server-Forbidden": "TODO: first pass, this should be fixed",
|
||||||
"Renegotiate-Client-Forbidden-1": "TODO: first pass, this should be fixed",
|
"Renegotiate-Client-Forbidden-1": "TODO: first pass, this should be fixed",
|
||||||
"Client-Sign-RSA_PKCS1_SHA1-TLS13": "TODO: first pass, this should be fixed",
|
|
||||||
"Client-Sign-RSA_PKCS1_SHA256-TLS13": "TODO: first pass, this should be fixed",
|
|
||||||
"Client-Sign-RSA_PKCS1_SHA384-TLS13": "TODO: first pass, this should be fixed",
|
|
||||||
"Client-Sign-RSA_PKCS1_SHA512-TLS13": "TODO: first pass, this should be fixed",
|
|
||||||
"Client-Sign-ECDSA_SHA1-TLS13": "TODO: first pass, this should be fixed",
|
|
||||||
"Client-Sign-ECDSA_P224_SHA256-TLS13": "TODO: first pass, this should be fixed",
|
|
||||||
"ClientAuth-NoFallback-TLS13": "TODO: first pass, this should be fixed",
|
|
||||||
"ClientAuth-NoFallback-ECDSA": "TODO: first pass, this should be fixed",
|
|
||||||
"ClientAuth-NoFallback-RSA": "TODO: first pass, this should be fixed",
|
|
||||||
"ECDSACurveMismatch-Verify-TLS13": "TODO: first pass, this should be fixed",
|
|
||||||
"Ed25519DefaultDisable-NoAdvertise": "TODO: first pass, this should be fixed",
|
|
||||||
"Ed25519DefaultDisable-NoAccept": "TODO: first pass, this should be fixed",
|
|
||||||
"NoCommonSignatureAlgorithms-TLS12-Fallback": "TODO: first pass, this should be fixed",
|
|
||||||
"UnknownExtension-Client": "TODO: first pass, this should be fixed",
|
"UnknownExtension-Client": "TODO: first pass, this should be fixed",
|
||||||
"UnknownUnencryptedExtension-Client-TLS13": "TODO: first pass, this should be fixed",
|
"UnknownUnencryptedExtension-Client-TLS13": "TODO: first pass, this should be fixed",
|
||||||
"UnofferedExtension-Client-TLS13": "TODO: first pass, this should be fixed",
|
"UnofferedExtension-Client-TLS13": "TODO: first pass, this should be fixed",
|
||||||
|
|
@ -153,7 +152,6 @@
|
||||||
"TrailingMessageData-TLS13-ClientCertificate-TLS": "TODO: first pass, this should be fixed",
|
"TrailingMessageData-TLS13-ClientCertificate-TLS": "TODO: first pass, this should be fixed",
|
||||||
"TrailingMessageData-TLS13-ClientCertificateVerify-TLS": "TODO: first pass, this should be fixed",
|
"TrailingMessageData-TLS13-ClientCertificateVerify-TLS": "TODO: first pass, this should be fixed",
|
||||||
"TrailingMessageData-TLS13-ServerCertificate-TLS": "TODO: first pass, this should be fixed",
|
"TrailingMessageData-TLS13-ServerCertificate-TLS": "TODO: first pass, this should be fixed",
|
||||||
"ResumeTLS12SessionID-TLS13": "We don't support session ID resumption",
|
|
||||||
"SkipEarlyData-TLS13": "TODO: first pass, this should be fixed",
|
"SkipEarlyData-TLS13": "TODO: first pass, this should be fixed",
|
||||||
"DuplicateKeyShares-TLS13": "TODO: first pass, this should be fixed",
|
"DuplicateKeyShares-TLS13": "TODO: first pass, this should be fixed",
|
||||||
"Server-TooLongSessionID-TLS13": "TODO: first pass, this should be fixed",
|
"Server-TooLongSessionID-TLS13": "TODO: first pass, this should be fixed",
|
||||||
|
|
|
||||||
|
|
@ -37,6 +37,8 @@ var (
|
||||||
|
|
||||||
keyfile = flag.String("key-file", "", "")
|
keyfile = flag.String("key-file", "", "")
|
||||||
certfile = flag.String("cert-file", "", "")
|
certfile = flag.String("cert-file", "", "")
|
||||||
|
ocspResponse = flagBase64("ocsp-response", "")
|
||||||
|
signingPrefs = flagIntSlice("signing-prefs", "")
|
||||||
|
|
||||||
trustCert = flag.String("trust-cert", "", "")
|
trustCert = flag.String("trust-cert", "", "")
|
||||||
|
|
||||||
|
|
@ -55,13 +57,17 @@ var (
|
||||||
|
|
||||||
resumeCount = flag.Int("resume-count", 0, "")
|
resumeCount = flag.Int("resume-count", 0, "")
|
||||||
|
|
||||||
curves = flagStringSlice("curves", "")
|
curves = flagIntSlice("curves", "")
|
||||||
expectedCurve = flag.String("expect-curve-id", "", "")
|
expectedCurve = flag.String("expect-curve-id", "", "")
|
||||||
|
|
||||||
|
verifyPrefs = flagIntSlice("verify-prefs", "")
|
||||||
|
expectedSigAlg = flag.String("expect-peer-signature-algorithm", "", "")
|
||||||
|
expectedPeerSigAlg = flagIntSlice("expect-peer-verify-pref", "")
|
||||||
|
|
||||||
shimID = flag.Uint64("shim-id", 0, "")
|
shimID = flag.Uint64("shim-id", 0, "")
|
||||||
_ = flag.Bool("ipv6", false, "")
|
_ = flag.Bool("ipv6", false, "")
|
||||||
|
|
||||||
echConfigListB64 = flag.String("ech-config-list", "", "")
|
echConfigList = flagBase64("ech-config-list", "")
|
||||||
expectECHAccepted = flag.Bool("expect-ech-accept", false, "")
|
expectECHAccepted = flag.Bool("expect-ech-accept", false, "")
|
||||||
expectHRR = flag.Bool("expect-hrr", false, "")
|
expectHRR = flag.Bool("expect-hrr", false, "")
|
||||||
expectNoHRR = flag.Bool("expect-no-hrr", false, "")
|
expectNoHRR = flag.Bool("expect-no-hrr", false, "")
|
||||||
|
|
@ -71,7 +77,7 @@ var (
|
||||||
_ = flag.Bool("expect-no-ech-name-override", false, "")
|
_ = flag.Bool("expect-no-ech-name-override", false, "")
|
||||||
_ = flag.String("expect-ech-name-override", "", "")
|
_ = flag.String("expect-ech-name-override", "", "")
|
||||||
_ = flag.Bool("reverify-on-resume", false, "")
|
_ = flag.Bool("reverify-on-resume", false, "")
|
||||||
onResumeECHConfigListB64 = flag.String("on-resume-ech-config-list", "", "")
|
onResumeECHConfigList = flagBase64("on-resume-ech-config-list", "")
|
||||||
_ = flag.Bool("on-resume-expect-reject-early-data", false, "")
|
_ = flag.Bool("on-resume-expect-reject-early-data", false, "")
|
||||||
onResumeExpectECHAccepted = flag.Bool("on-resume-expect-ech-accept", false, "")
|
onResumeExpectECHAccepted = flag.Bool("on-resume-expect-ech-accept", false, "")
|
||||||
_ = flag.Bool("on-resume-expect-no-ech-name-override", false, "")
|
_ = flag.Bool("on-resume-expect-no-ech-name-override", false, "")
|
||||||
|
|
@ -105,7 +111,7 @@ var (
|
||||||
type stringSlice []string
|
type stringSlice []string
|
||||||
|
|
||||||
func flagStringSlice(name, usage string) *stringSlice {
|
func flagStringSlice(name, usage string) *stringSlice {
|
||||||
f := &stringSlice{}
|
f := new(stringSlice)
|
||||||
flag.Var(f, name, usage)
|
flag.Var(f, name, usage)
|
||||||
return f
|
return f
|
||||||
}
|
}
|
||||||
|
|
@ -119,12 +125,59 @@ func (saf *stringSlice) Set(s string) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type intSlice []int64
|
||||||
|
|
||||||
|
func flagIntSlice(name, usage string) *intSlice {
|
||||||
|
f := new(intSlice)
|
||||||
|
flag.Var(f, name, usage)
|
||||||
|
return f
|
||||||
|
}
|
||||||
|
|
||||||
|
func (sf *intSlice) String() string {
|
||||||
|
return strings.Join(strings.Split(fmt.Sprint(*sf), " "), ",")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (sf *intSlice) Set(s string) error {
|
||||||
|
i, err := strconv.ParseInt(s, 10, 64)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
*sf = append(*sf, i)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type base64Flag []byte
|
||||||
|
|
||||||
|
func flagBase64(name, usage string) *base64Flag {
|
||||||
|
f := new(base64Flag)
|
||||||
|
flag.Var(f, name, usage)
|
||||||
|
return f
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *base64Flag) String() string {
|
||||||
|
return base64.StdEncoding.EncodeToString(*f)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *base64Flag) Set(s string) error {
|
||||||
|
if *f != nil {
|
||||||
|
return fmt.Errorf("multiple base64 values not supported")
|
||||||
|
}
|
||||||
|
b, err := base64.StdEncoding.DecodeString(s)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
*f = b
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
func bogoShim() {
|
func bogoShim() {
|
||||||
if *isHandshakerSupported {
|
if *isHandshakerSupported {
|
||||||
fmt.Println("No")
|
fmt.Println("No")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fmt.Printf("BoGo shim flags: %q", os.Args[1:])
|
||||||
|
|
||||||
// Test with both the default and insecure cipher suites.
|
// Test with both the default and insecure cipher suites.
|
||||||
var ciphersuites []uint16
|
var ciphersuites []uint16
|
||||||
for _, s := range append(CipherSuites(), InsecureCipherSuites()...) {
|
for _, s := range append(CipherSuites(), InsecureCipherSuites()...) {
|
||||||
|
|
@ -218,7 +271,39 @@ func bogoShim() {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatalf("load key-file err: %s", err)
|
log.Fatalf("load key-file err: %s", err)
|
||||||
}
|
}
|
||||||
cfg.Certificates = []Certificate{pair}
|
for _, id := range *signingPrefs {
|
||||||
|
pair.SupportedSignatureAlgorithms = append(pair.SupportedSignatureAlgorithms, SignatureScheme(id))
|
||||||
|
}
|
||||||
|
pair.OCSPStaple = *ocspResponse
|
||||||
|
// Use Get[Client]Certificate to force the use of the certificate, which
|
||||||
|
// more closely matches the BoGo expectations (e.g. handshake failure if
|
||||||
|
// no client certificates are compatible).
|
||||||
|
cfg.GetCertificate = func(chi *ClientHelloInfo) (*Certificate, error) {
|
||||||
|
if *expectedPeerSigAlg != nil {
|
||||||
|
if len(chi.SignatureSchemes) != len(*expectedPeerSigAlg) {
|
||||||
|
return nil, fmt.Errorf("unexpected signature algorithms: got %s, want %v", chi.SignatureSchemes, *expectedPeerSigAlg)
|
||||||
|
}
|
||||||
|
for i := range *expectedPeerSigAlg {
|
||||||
|
if chi.SignatureSchemes[i] != SignatureScheme((*expectedPeerSigAlg)[i]) {
|
||||||
|
return nil, fmt.Errorf("unexpected signature algorithms: got %s, want %v", chi.SignatureSchemes, *expectedPeerSigAlg)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return &pair, nil
|
||||||
|
}
|
||||||
|
cfg.GetClientCertificate = func(cri *CertificateRequestInfo) (*Certificate, error) {
|
||||||
|
if *expectedPeerSigAlg != nil {
|
||||||
|
if len(cri.SignatureSchemes) != len(*expectedPeerSigAlg) {
|
||||||
|
return nil, fmt.Errorf("unexpected signature algorithms: got %s, want %v", cri.SignatureSchemes, *expectedPeerSigAlg)
|
||||||
|
}
|
||||||
|
for i := range *expectedPeerSigAlg {
|
||||||
|
if cri.SignatureSchemes[i] != SignatureScheme((*expectedPeerSigAlg)[i]) {
|
||||||
|
return nil, fmt.Errorf("unexpected signature algorithms: got %s, want %v", cri.SignatureSchemes, *expectedPeerSigAlg)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return &pair, nil
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if *trustCert != "" {
|
if *trustCert != "" {
|
||||||
pool := x509.NewCertPool()
|
pool := x509.NewCertPool()
|
||||||
|
|
@ -242,26 +327,24 @@ func bogoShim() {
|
||||||
cfg.ClientAuth = VerifyClientCertIfGiven
|
cfg.ClientAuth = VerifyClientCertIfGiven
|
||||||
}
|
}
|
||||||
|
|
||||||
if *echConfigListB64 != "" {
|
if *echConfigList != nil {
|
||||||
echConfigList, err := base64.StdEncoding.DecodeString(*echConfigListB64)
|
cfg.EncryptedClientHelloConfigList = *echConfigList
|
||||||
if err != nil {
|
|
||||||
log.Fatalf("parse ech-config-list err: %s", err)
|
|
||||||
}
|
|
||||||
cfg.EncryptedClientHelloConfigList = echConfigList
|
|
||||||
cfg.MinVersion = VersionTLS13
|
cfg.MinVersion = VersionTLS13
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(*curves) != 0 {
|
if *curves != nil {
|
||||||
for _, curveStr := range *curves {
|
for _, id := range *curves {
|
||||||
id, err := strconv.Atoi(curveStr)
|
|
||||||
if err != nil {
|
|
||||||
log.Fatalf("failed to parse curve id %q: %s", curveStr, err)
|
|
||||||
}
|
|
||||||
cfg.CurvePreferences = append(cfg.CurvePreferences, CurveID(id))
|
cfg.CurvePreferences = append(cfg.CurvePreferences, CurveID(id))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(*echServerConfig) != 0 {
|
if *verifyPrefs != nil {
|
||||||
|
for _, id := range *verifyPrefs {
|
||||||
|
testingOnlySupportedSignatureAlgorithms = append(testingOnlySupportedSignatureAlgorithms, SignatureScheme(id))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if *echServerConfig != nil {
|
||||||
if len(*echServerConfig) != len(*echServerKey) || len(*echServerConfig) != len(*echServerRetryConfig) {
|
if len(*echServerConfig) != len(*echServerKey) || len(*echServerConfig) != len(*echServerRetryConfig) {
|
||||||
log.Fatal("-ech-server-config, -ech-server-key, and -ech-is-retry-config mismatch")
|
log.Fatal("-ech-server-config, -ech-server-key, and -ech-is-retry-config mismatch")
|
||||||
}
|
}
|
||||||
|
|
@ -285,12 +368,8 @@ func bogoShim() {
|
||||||
}
|
}
|
||||||
|
|
||||||
for i := 0; i < *resumeCount+1; i++ {
|
for i := 0; i < *resumeCount+1; i++ {
|
||||||
if i > 0 && (*onResumeECHConfigListB64 != "") {
|
if i > 0 && *onResumeECHConfigList != nil {
|
||||||
echConfigList, err := base64.StdEncoding.DecodeString(*onResumeECHConfigListB64)
|
cfg.EncryptedClientHelloConfigList = *onResumeECHConfigList
|
||||||
if err != nil {
|
|
||||||
log.Fatalf("parse ech-config-list err: %s", err)
|
|
||||||
}
|
|
||||||
cfg.EncryptedClientHelloConfigList = echConfigList
|
|
||||||
}
|
}
|
||||||
|
|
||||||
conn, err := net.Dial("tcp", net.JoinHostPort("localhost", *port))
|
conn, err := net.Dial("tcp", net.JoinHostPort("localhost", *port))
|
||||||
|
|
@ -343,7 +422,7 @@ func bogoShim() {
|
||||||
if err != io.EOF {
|
if err != io.EOF {
|
||||||
retryErr, ok := err.(*ECHRejectionError)
|
retryErr, ok := err.(*ECHRejectionError)
|
||||||
if !ok {
|
if !ok {
|
||||||
log.Fatalf("unexpected error type returned: %v", err)
|
log.Fatal(err)
|
||||||
}
|
}
|
||||||
if *expectNoECHRetryConfigs && len(retryErr.RetryConfigList) > 0 {
|
if *expectNoECHRetryConfigs && len(retryErr.RetryConfigList) > 0 {
|
||||||
log.Fatalf("expected no ECH retry configs, got some")
|
log.Fatalf("expected no ECH retry configs, got some")
|
||||||
|
|
@ -408,10 +487,21 @@ func bogoShim() {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatalf("failed to parse -expect-curve-id: %s", err)
|
log.Fatalf("failed to parse -expect-curve-id: %s", err)
|
||||||
}
|
}
|
||||||
if tlsConn.curveID != CurveID(expectedCurveID) {
|
if cs.CurveID != CurveID(expectedCurveID) {
|
||||||
log.Fatalf("unexpected curve id: want %d, got %d", expectedCurveID, tlsConn.curveID)
|
log.Fatalf("unexpected curve id: want %d, got %d", expectedCurveID, tlsConn.curveID)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO: implement testingOnlyPeerSignatureAlgorithm on resumption.
|
||||||
|
if *expectedSigAlg != "" && !cs.DidResume {
|
||||||
|
expectedSigAlgID, err := strconv.Atoi(*expectedSigAlg)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalf("failed to parse -expect-peer-signature-algorithm: %s", err)
|
||||||
|
}
|
||||||
|
if cs.testingOnlyPeerSignatureAlgorithm != SignatureScheme(expectedSigAlgID) {
|
||||||
|
log.Fatalf("unexpected peer signature algorithm: want %s, got %s", SignatureScheme(expectedSigAlgID), cs.testingOnlyPeerSignatureAlgorithm)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -491,20 +581,36 @@ func TestBogoSuite(t *testing.T) {
|
||||||
assertResults := map[string]string{
|
assertResults := map[string]string{
|
||||||
"CurveTest-Client-MLKEM-TLS13": "PASS",
|
"CurveTest-Client-MLKEM-TLS13": "PASS",
|
||||||
"CurveTest-Server-MLKEM-TLS13": "PASS",
|
"CurveTest-Server-MLKEM-TLS13": "PASS",
|
||||||
|
|
||||||
|
// Various signature algorithm tests checking that we enforce our
|
||||||
|
// preferences on the peer.
|
||||||
|
"ClientAuth-Enforced": "PASS",
|
||||||
|
"ServerAuth-Enforced": "PASS",
|
||||||
|
"ClientAuth-Enforced-TLS13": "PASS",
|
||||||
|
"ServerAuth-Enforced-TLS13": "PASS",
|
||||||
|
"VerifyPreferences-Advertised": "PASS",
|
||||||
|
"VerifyPreferences-Enforced": "PASS",
|
||||||
|
"Client-TLS12-NoSign-RSA_PKCS1_MD5_SHA1": "PASS",
|
||||||
|
"Server-TLS12-NoSign-RSA_PKCS1_MD5_SHA1": "PASS",
|
||||||
|
"Client-TLS13-NoSign-RSA_PKCS1_MD5_SHA1": "PASS",
|
||||||
|
"Server-TLS13-NoSign-RSA_PKCS1_MD5_SHA1": "PASS",
|
||||||
}
|
}
|
||||||
|
|
||||||
for name, result := range results.Tests {
|
for name, result := range results.Tests {
|
||||||
// This is not really the intended way to do this... but... it works?
|
// This is not really the intended way to do this... but... it works?
|
||||||
t.Run(name, func(t *testing.T) {
|
t.Run(name, func(t *testing.T) {
|
||||||
if result.Actual == "FAIL" && result.IsUnexpected {
|
if result.Actual == "FAIL" && result.IsUnexpected {
|
||||||
t.Fatal(result.Error)
|
t.Fail()
|
||||||
}
|
}
|
||||||
if expectedResult, ok := assertResults[name]; ok && expectedResult != result.Actual {
|
if result.Error != "" {
|
||||||
t.Fatalf("unexpected result: got %s, want %s", result.Actual, assertResults[name])
|
t.Log(result.Error)
|
||||||
|
}
|
||||||
|
if exp, ok := assertResults[name]; ok && exp != result.Actual {
|
||||||
|
t.Errorf("unexpected result: got %s, want %s", result.Actual, exp)
|
||||||
}
|
}
|
||||||
delete(assertResults, name)
|
delete(assertResults, name)
|
||||||
if result.Actual == "SKIP" {
|
if result.Actual == "SKIP" {
|
||||||
t.Skip()
|
t.SkipNow()
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -309,6 +309,10 @@ type ConnectionState struct {
|
||||||
|
|
||||||
// testingOnlyDidHRR is true if a HelloRetryRequest was sent/received.
|
// testingOnlyDidHRR is true if a HelloRetryRequest was sent/received.
|
||||||
testingOnlyDidHRR bool
|
testingOnlyDidHRR bool
|
||||||
|
|
||||||
|
// testingOnlyPeerSignatureAlgorithm is the signature algorithm used by the
|
||||||
|
// peer to sign the handshake. It is not set for resumed connections.
|
||||||
|
testingOnlyPeerSignatureAlgorithm SignatureScheme
|
||||||
}
|
}
|
||||||
|
|
||||||
// ExportKeyingMaterial returns length bytes of exported key material in a new
|
// ExportKeyingMaterial returns length bytes of exported key material in a new
|
||||||
|
|
@ -1684,35 +1688,62 @@ func unexpectedMessageError(wanted, got any) error {
|
||||||
return fmt.Errorf("tls: received unexpected handshake message of type %T when waiting for %T", got, wanted)
|
return fmt.Errorf("tls: received unexpected handshake message of type %T when waiting for %T", got, wanted)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var testingOnlySupportedSignatureAlgorithms []SignatureScheme
|
||||||
|
|
||||||
// supportedSignatureAlgorithms returns the supported signature algorithms for
|
// supportedSignatureAlgorithms returns the supported signature algorithms for
|
||||||
// the given minimum TLS version, to advertise in ClientHello and
|
// the given minimum TLS version, to advertise in ClientHello and
|
||||||
// CertificateRequest messages.
|
// CertificateRequest messages.
|
||||||
func supportedSignatureAlgorithms(minVers uint16) []SignatureScheme {
|
func supportedSignatureAlgorithms(minVers uint16) []SignatureScheme {
|
||||||
sigAlgs := defaultSupportedSignatureAlgorithms()
|
sigAlgs := defaultSupportedSignatureAlgorithms()
|
||||||
if fips140tls.Required() {
|
if testingOnlySupportedSignatureAlgorithms != nil {
|
||||||
sigAlgs = slices.DeleteFunc(sigAlgs, func(s SignatureScheme) bool {
|
sigAlgs = slices.Clone(testingOnlySupportedSignatureAlgorithms)
|
||||||
return !slices.Contains(allowedSignatureAlgorithmsFIPS, s)
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
if minVers > VersionTLS12 {
|
return slices.DeleteFunc(sigAlgs, func(s SignatureScheme) bool {
|
||||||
sigAlgs = slices.DeleteFunc(sigAlgs, func(s SignatureScheme) bool {
|
return isDisabledSignatureAlgorithm(minVers, s, false)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
var tlssha1 = godebug.New("tlssha1")
|
||||||
|
|
||||||
|
func isDisabledSignatureAlgorithm(version uint16, s SignatureScheme, isCert bool) bool {
|
||||||
|
if fips140tls.Required() && !slices.Contains(allowedSignatureAlgorithmsFIPS, s) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
// For the _cert extension we include all algorithms, including SHA-1 and
|
||||||
|
// PKCS#1 v1.5, because it's more likely that something on our side will be
|
||||||
|
// willing to accept a *-with-SHA1 certificate (e.g. with a custom
|
||||||
|
// VerifyConnection or by a direct match with the CertPool), than that the
|
||||||
|
// peer would have a better certificate but is just choosing not to send it.
|
||||||
|
// crypto/x509 will refuse to verify important SHA-1 signatures anyway.
|
||||||
|
if isCert {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// TLS 1.3 removed support for PKCS#1 v1.5 and SHA-1 signatures,
|
||||||
|
// and Go 1.25 removed support for SHA-1 signatures in TLS 1.2.
|
||||||
|
if version > VersionTLS12 {
|
||||||
sigType, sigHash, _ := typeAndHashFromSignatureScheme(s)
|
sigType, sigHash, _ := typeAndHashFromSignatureScheme(s)
|
||||||
return sigType == signaturePKCS1v15 || sigHash == crypto.SHA1
|
if sigType == signaturePKCS1v15 || sigHash == crypto.SHA1 {
|
||||||
})
|
return true
|
||||||
}
|
}
|
||||||
return sigAlgs
|
} else if tlssha1.Value() != "1" {
|
||||||
|
_, sigHash, _ := typeAndHashFromSignatureScheme(s)
|
||||||
|
if sigHash == crypto.SHA1 {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
// supportedSignatureAlgorithmsCert returns the supported algorithms for
|
// supportedSignatureAlgorithmsCert returns the supported algorithms for
|
||||||
// signatures in certificates.
|
// signatures in certificates.
|
||||||
func supportedSignatureAlgorithmsCert() []SignatureScheme {
|
func supportedSignatureAlgorithmsCert() []SignatureScheme {
|
||||||
sigAlgs := defaultSupportedSignatureAlgorithmsCert()
|
sigAlgs := defaultSupportedSignatureAlgorithms()
|
||||||
if fips140tls.Required() {
|
return slices.DeleteFunc(sigAlgs, func(s SignatureScheme) bool {
|
||||||
sigAlgs = slices.DeleteFunc(sigAlgs, func(s SignatureScheme) bool {
|
return isDisabledSignatureAlgorithm(0, s, true)
|
||||||
return !slices.Contains(allowedSignatureAlgorithmsFIPS, s)
|
|
||||||
})
|
})
|
||||||
}
|
|
||||||
return sigAlgs
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func isSupportedSignatureAlgorithm(sigAlg SignatureScheme, supportedSignatureAlgorithms []SignatureScheme) bool {
|
func isSupportedSignatureAlgorithm(sigAlg SignatureScheme, supportedSignatureAlgorithms []SignatureScheme) bool {
|
||||||
|
|
|
||||||
|
|
@ -51,6 +51,7 @@ type Conn struct {
|
||||||
didHRR bool // whether a HelloRetryRequest was sent/received
|
didHRR bool // whether a HelloRetryRequest was sent/received
|
||||||
cipherSuite uint16
|
cipherSuite uint16
|
||||||
curveID CurveID
|
curveID CurveID
|
||||||
|
peerSigAlg SignatureScheme
|
||||||
ocspResponse []byte // stapled OCSP response
|
ocspResponse []byte // stapled OCSP response
|
||||||
scts [][]byte // signed certificate timestamps from server
|
scts [][]byte // signed certificate timestamps from server
|
||||||
peerCertificates []*x509.Certificate
|
peerCertificates []*x509.Certificate
|
||||||
|
|
@ -1630,6 +1631,7 @@ func (c *Conn) connectionStateLocked() ConnectionState {
|
||||||
state.NegotiatedProtocol = c.clientProtocol
|
state.NegotiatedProtocol = c.clientProtocol
|
||||||
state.DidResume = c.didResume
|
state.DidResume = c.didResume
|
||||||
state.testingOnlyDidHRR = c.didHRR
|
state.testingOnlyDidHRR = c.didHRR
|
||||||
|
state.testingOnlyPeerSignatureAlgorithm = c.peerSigAlg
|
||||||
state.CurveID = c.curveID
|
state.CurveID = c.curveID
|
||||||
state.NegotiatedProtocolIsMutual = true
|
state.NegotiatedProtocolIsMutual = true
|
||||||
state.ServerName = c.serverName
|
state.ServerName = c.serverName
|
||||||
|
|
|
||||||
|
|
@ -24,53 +24,11 @@ func defaultCurvePreferences() []CurveID {
|
||||||
return []CurveID{X25519MLKEM768, X25519, CurveP256, CurveP384, CurveP521}
|
return []CurveID{X25519MLKEM768, X25519, CurveP256, CurveP384, CurveP521}
|
||||||
}
|
}
|
||||||
|
|
||||||
var tlssha1 = godebug.New("tlssha1")
|
|
||||||
|
|
||||||
// defaultSupportedSignatureAlgorithms returns the signature and hash algorithms that
|
// defaultSupportedSignatureAlgorithms returns the signature and hash algorithms that
|
||||||
// the code advertises and supports in a TLS 1.2+ ClientHello and in a TLS 1.2+
|
// the code advertises and supports in a TLS 1.2+ ClientHello and in a TLS 1.2+
|
||||||
// CertificateRequest. The two fields are merged to match with TLS 1.3.
|
// CertificateRequest. The two fields are merged to match with TLS 1.3.
|
||||||
// Note that in TLS 1.2, the ECDSA algorithms are not constrained to P-256, etc.
|
// Note that in TLS 1.2, the ECDSA algorithms are not constrained to P-256, etc.
|
||||||
func defaultSupportedSignatureAlgorithms() []SignatureScheme {
|
func defaultSupportedSignatureAlgorithms() []SignatureScheme {
|
||||||
if tlssha1.Value() == "1" {
|
|
||||||
return []SignatureScheme{
|
|
||||||
PSSWithSHA256,
|
|
||||||
ECDSAWithP256AndSHA256,
|
|
||||||
Ed25519,
|
|
||||||
PSSWithSHA384,
|
|
||||||
PSSWithSHA512,
|
|
||||||
PKCS1WithSHA256,
|
|
||||||
PKCS1WithSHA384,
|
|
||||||
PKCS1WithSHA512,
|
|
||||||
ECDSAWithP384AndSHA384,
|
|
||||||
ECDSAWithP521AndSHA512,
|
|
||||||
PKCS1WithSHA1,
|
|
||||||
ECDSAWithSHA1,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return []SignatureScheme{
|
|
||||||
PSSWithSHA256,
|
|
||||||
ECDSAWithP256AndSHA256,
|
|
||||||
Ed25519,
|
|
||||||
PSSWithSHA384,
|
|
||||||
PSSWithSHA512,
|
|
||||||
PKCS1WithSHA256,
|
|
||||||
PKCS1WithSHA384,
|
|
||||||
PKCS1WithSHA512,
|
|
||||||
ECDSAWithP384AndSHA384,
|
|
||||||
ECDSAWithP521AndSHA512,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// defaultSupportedSignatureAlgorithmsCert returns the signature algorithms that
|
|
||||||
// the code advertises as supported for signatures in certificates.
|
|
||||||
//
|
|
||||||
// We include all algorithms, including SHA-1 and PKCS#1 v1.5, because it's more
|
|
||||||
// likely that something on our side will be willing to accept a *-with-SHA1
|
|
||||||
// certificate (e.g. with a custom VerifyConnection or by a direct match with
|
|
||||||
// the CertPool), than that the peer would have a better certificate but is just
|
|
||||||
// choosing not to send it. crypto/x509 will refuse to verify important SHA-1
|
|
||||||
// signatures anyway.
|
|
||||||
func defaultSupportedSignatureAlgorithmsCert() []SignatureScheme {
|
|
||||||
return []SignatureScheme{
|
return []SignatureScheme{
|
||||||
PSSWithSHA256,
|
PSSWithSHA256,
|
||||||
ECDSAWithP256AndSHA256,
|
ECDSAWithP256AndSHA256,
|
||||||
|
|
|
||||||
|
|
@ -18,6 +18,7 @@ import (
|
||||||
"internal/testenv"
|
"internal/testenv"
|
||||||
"math/big"
|
"math/big"
|
||||||
"net"
|
"net"
|
||||||
|
"os"
|
||||||
"runtime"
|
"runtime"
|
||||||
"strings"
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
@ -262,15 +263,19 @@ func fipsHandshake(t *testing.T, clientConfig, serverConfig *Config) (clientErr,
|
||||||
|
|
||||||
func TestFIPSServerSignatureAndHash(t *testing.T) {
|
func TestFIPSServerSignatureAndHash(t *testing.T) {
|
||||||
defer func() {
|
defer func() {
|
||||||
testingOnlyForceClientHelloSignatureAlgorithms = nil
|
testingOnlySupportedSignatureAlgorithms = nil
|
||||||
}()
|
}()
|
||||||
|
defer func(godebug string) {
|
||||||
|
os.Setenv("GODEBUG", godebug)
|
||||||
|
}(os.Getenv("GODEBUG"))
|
||||||
|
os.Setenv("GODEBUG", "tlssha1=1")
|
||||||
|
|
||||||
for _, sigHash := range defaultSupportedSignatureAlgorithms() {
|
for _, sigHash := range defaultSupportedSignatureAlgorithms() {
|
||||||
t.Run(fmt.Sprintf("%v", sigHash), func(t *testing.T) {
|
t.Run(fmt.Sprintf("%v", sigHash), func(t *testing.T) {
|
||||||
serverConfig := testConfig.Clone()
|
serverConfig := testConfig.Clone()
|
||||||
serverConfig.Certificates = make([]Certificate, 1)
|
serverConfig.Certificates = make([]Certificate, 1)
|
||||||
|
|
||||||
testingOnlyForceClientHelloSignatureAlgorithms = []SignatureScheme{sigHash}
|
testingOnlySupportedSignatureAlgorithms = []SignatureScheme{sigHash}
|
||||||
|
|
||||||
sigType, _, _ := typeAndHashFromSignatureScheme(sigHash)
|
sigType, _, _ := typeAndHashFromSignatureScheme(sigHash)
|
||||||
switch sigType {
|
switch sigType {
|
||||||
|
|
|
||||||
|
|
@ -20,7 +20,6 @@ import (
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"hash"
|
"hash"
|
||||||
"internal/byteorder"
|
|
||||||
"internal/godebug"
|
"internal/godebug"
|
||||||
"io"
|
"io"
|
||||||
"net"
|
"net"
|
||||||
|
|
@ -42,8 +41,6 @@ type clientHandshakeState struct {
|
||||||
ticket []byte // a fresh ticket received during this handshake
|
ticket []byte // a fresh ticket received during this handshake
|
||||||
}
|
}
|
||||||
|
|
||||||
var testingOnlyForceClientHelloSignatureAlgorithms []SignatureScheme
|
|
||||||
|
|
||||||
func (c *Conn) makeClientHello() (*clientHelloMsg, *keySharePrivateKeys, *echClientContext, error) {
|
func (c *Conn) makeClientHello() (*clientHelloMsg, *keySharePrivateKeys, *echClientContext, error) {
|
||||||
config := c.config
|
config := c.config
|
||||||
if len(config.ServerName) == 0 && !config.InsecureSkipVerify {
|
if len(config.ServerName) == 0 && !config.InsecureSkipVerify {
|
||||||
|
|
@ -126,9 +123,6 @@ func (c *Conn) makeClientHello() (*clientHelloMsg, *keySharePrivateKeys, *echCli
|
||||||
hello.supportedSignatureAlgorithms = supportedSignatureAlgorithms(minVersion)
|
hello.supportedSignatureAlgorithms = supportedSignatureAlgorithms(minVersion)
|
||||||
hello.supportedSignatureAlgorithmsCert = supportedSignatureAlgorithmsCert()
|
hello.supportedSignatureAlgorithmsCert = supportedSignatureAlgorithmsCert()
|
||||||
}
|
}
|
||||||
if testingOnlyForceClientHelloSignatureAlgorithms != nil {
|
|
||||||
hello.supportedSignatureAlgorithms = testingOnlyForceClientHelloSignatureAlgorithms
|
|
||||||
}
|
|
||||||
|
|
||||||
var keyShareKeys *keySharePrivateKeys
|
var keyShareKeys *keySharePrivateKeys
|
||||||
if maxVersion >= VersionTLS13 {
|
if maxVersion >= VersionTLS13 {
|
||||||
|
|
@ -732,8 +726,9 @@ func (hs *clientHandshakeState) doFullHandshake() error {
|
||||||
c.sendAlert(alertIllegalParameter)
|
c.sendAlert(alertIllegalParameter)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if len(skx.key) >= 3 && skx.key[0] == 3 /* named curve */ {
|
if keyAgreement, ok := keyAgreement.(*ecdheKeyAgreement); ok {
|
||||||
c.curveID = CurveID(byteorder.BEUint16(skx.key[1:]))
|
c.curveID = keyAgreement.curveID
|
||||||
|
c.peerSigAlg = keyAgreement.signatureAlgorithm
|
||||||
}
|
}
|
||||||
|
|
||||||
msg, err = c.readHandshake(&hs.finishedHash)
|
msg, err = c.readHandshake(&hs.finishedHash)
|
||||||
|
|
@ -819,7 +814,7 @@ func (hs *clientHandshakeState) doFullHandshake() error {
|
||||||
if c.vers >= VersionTLS12 {
|
if c.vers >= VersionTLS12 {
|
||||||
signatureAlgorithm, err := selectSignatureScheme(c.vers, chainToSend, certReq.supportedSignatureAlgorithms)
|
signatureAlgorithm, err := selectSignatureScheme(c.vers, chainToSend, certReq.supportedSignatureAlgorithms)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
c.sendAlert(alertIllegalParameter)
|
c.sendAlert(alertHandshakeFailure)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
sigType, sigHash, err = typeAndHashFromSignatureScheme(signatureAlgorithm)
|
sigType, sigHash, err = typeAndHashFromSignatureScheme(signatureAlgorithm)
|
||||||
|
|
|
||||||
|
|
@ -694,6 +694,7 @@ func (hs *clientHandshakeStateTLS13) readServerCertificate() error {
|
||||||
c.sendAlert(alertDecryptError)
|
c.sendAlert(alertDecryptError)
|
||||||
return errors.New("tls: invalid signature by the server certificate: " + err.Error())
|
return errors.New("tls: invalid signature by the server certificate: " + err.Error())
|
||||||
}
|
}
|
||||||
|
c.peerSigAlg = certVerify.signatureAlgorithm
|
||||||
|
|
||||||
if err := transcriptMsg(certVerify, hs.transcript); err != nil {
|
if err := transcriptMsg(certVerify, hs.transcript); err != nil {
|
||||||
return err
|
return err
|
||||||
|
|
|
||||||
|
|
@ -16,7 +16,6 @@ import (
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"hash"
|
"hash"
|
||||||
"internal/byteorder"
|
|
||||||
"io"
|
"io"
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
@ -632,8 +631,9 @@ func (hs *serverHandshakeState) doFullHandshake() error {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if skx != nil {
|
if skx != nil {
|
||||||
if len(skx.key) >= 3 && skx.key[0] == 3 /* named curve */ {
|
if keyAgreement, ok := keyAgreement.(*ecdheKeyAgreement); ok {
|
||||||
c.curveID = CurveID(byteorder.BEUint16(skx.key[1:]))
|
c.curveID = keyAgreement.curveID
|
||||||
|
c.peerSigAlg = keyAgreement.signatureAlgorithm
|
||||||
}
|
}
|
||||||
if _, err := hs.c.writeHandshakeRecord(skx, &hs.finishedHash); err != nil {
|
if _, err := hs.c.writeHandshakeRecord(skx, &hs.finishedHash); err != nil {
|
||||||
return err
|
return err
|
||||||
|
|
@ -789,6 +789,7 @@ func (hs *serverHandshakeState) doFullHandshake() error {
|
||||||
c.sendAlert(alertDecryptError)
|
c.sendAlert(alertDecryptError)
|
||||||
return errors.New("tls: invalid signature by the client certificate: " + err.Error())
|
return errors.New("tls: invalid signature by the client certificate: " + err.Error())
|
||||||
}
|
}
|
||||||
|
c.peerSigAlg = certVerify.signatureAlgorithm
|
||||||
|
|
||||||
if err := transcriptMsg(certVerify, &hs.finishedHash); err != nil {
|
if err := transcriptMsg(certVerify, &hs.finishedHash); err != nil {
|
||||||
return err
|
return err
|
||||||
|
|
|
||||||
|
|
@ -1115,6 +1115,7 @@ func (hs *serverHandshakeStateTLS13) readClientCertificate() error {
|
||||||
c.sendAlert(alertDecryptError)
|
c.sendAlert(alertDecryptError)
|
||||||
return errors.New("tls: invalid signature by the client certificate: " + err.Error())
|
return errors.New("tls: invalid signature by the client certificate: " + err.Error())
|
||||||
}
|
}
|
||||||
|
c.peerSigAlg = certVerify.signatureAlgorithm
|
||||||
|
|
||||||
if err := transcriptMsg(certVerify, hs.transcript); err != nil {
|
if err := transcriptMsg(certVerify, hs.transcript); err != nil {
|
||||||
return err
|
return err
|
||||||
|
|
|
||||||
|
|
@ -165,25 +165,29 @@ type ecdheKeyAgreement struct {
|
||||||
// and returned in generateClientKeyExchange.
|
// and returned in generateClientKeyExchange.
|
||||||
ckx *clientKeyExchangeMsg
|
ckx *clientKeyExchangeMsg
|
||||||
preMasterSecret []byte
|
preMasterSecret []byte
|
||||||
|
|
||||||
|
// curveID and signatureAlgorithm are set by processServerKeyExchange and
|
||||||
|
// generateServerKeyExchange.
|
||||||
|
curveID CurveID
|
||||||
|
signatureAlgorithm SignatureScheme
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ka *ecdheKeyAgreement) generateServerKeyExchange(config *Config, cert *Certificate, clientHello *clientHelloMsg, hello *serverHelloMsg) (*serverKeyExchangeMsg, error) {
|
func (ka *ecdheKeyAgreement) generateServerKeyExchange(config *Config, cert *Certificate, clientHello *clientHelloMsg, hello *serverHelloMsg) (*serverKeyExchangeMsg, error) {
|
||||||
var curveID CurveID
|
|
||||||
for _, c := range clientHello.supportedCurves {
|
for _, c := range clientHello.supportedCurves {
|
||||||
if config.supportsCurve(ka.version, c) {
|
if config.supportsCurve(ka.version, c) {
|
||||||
curveID = c
|
ka.curveID = c
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if curveID == 0 {
|
if ka.curveID == 0 {
|
||||||
return nil, errors.New("tls: no supported elliptic curves offered")
|
return nil, errors.New("tls: no supported elliptic curves offered")
|
||||||
}
|
}
|
||||||
if _, ok := curveForCurveID(curveID); !ok {
|
if _, ok := curveForCurveID(ka.curveID); !ok {
|
||||||
return nil, errors.New("tls: CurvePreferences includes unsupported curve")
|
return nil, errors.New("tls: CurvePreferences includes unsupported curve")
|
||||||
}
|
}
|
||||||
|
|
||||||
key, err := generateECDHEKey(config.rand(), curveID)
|
key, err := generateECDHEKey(config.rand(), ka.curveID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
@ -193,8 +197,8 @@ func (ka *ecdheKeyAgreement) generateServerKeyExchange(config *Config, cert *Cer
|
||||||
ecdhePublic := key.PublicKey().Bytes()
|
ecdhePublic := key.PublicKey().Bytes()
|
||||||
serverECDHEParams := make([]byte, 1+2+1+len(ecdhePublic))
|
serverECDHEParams := make([]byte, 1+2+1+len(ecdhePublic))
|
||||||
serverECDHEParams[0] = 3 // named curve
|
serverECDHEParams[0] = 3 // named curve
|
||||||
serverECDHEParams[1] = byte(curveID >> 8)
|
serverECDHEParams[1] = byte(ka.curveID >> 8)
|
||||||
serverECDHEParams[2] = byte(curveID)
|
serverECDHEParams[2] = byte(ka.curveID)
|
||||||
serverECDHEParams[3] = byte(len(ecdhePublic))
|
serverECDHEParams[3] = byte(len(ecdhePublic))
|
||||||
copy(serverECDHEParams[4:], ecdhePublic)
|
copy(serverECDHEParams[4:], ecdhePublic)
|
||||||
|
|
||||||
|
|
@ -203,15 +207,14 @@ func (ka *ecdheKeyAgreement) generateServerKeyExchange(config *Config, cert *Cer
|
||||||
return nil, fmt.Errorf("tls: certificate private key of type %T does not implement crypto.Signer", cert.PrivateKey)
|
return nil, fmt.Errorf("tls: certificate private key of type %T does not implement crypto.Signer", cert.PrivateKey)
|
||||||
}
|
}
|
||||||
|
|
||||||
var signatureAlgorithm SignatureScheme
|
|
||||||
var sigType uint8
|
var sigType uint8
|
||||||
var sigHash crypto.Hash
|
var sigHash crypto.Hash
|
||||||
if ka.version >= VersionTLS12 {
|
if ka.version >= VersionTLS12 {
|
||||||
signatureAlgorithm, err = selectSignatureScheme(ka.version, cert, clientHello.supportedSignatureAlgorithms)
|
ka.signatureAlgorithm, err = selectSignatureScheme(ka.version, cert, clientHello.supportedSignatureAlgorithms)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
sigType, sigHash, err = typeAndHashFromSignatureScheme(signatureAlgorithm)
|
sigType, sigHash, err = typeAndHashFromSignatureScheme(ka.signatureAlgorithm)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
@ -249,8 +252,8 @@ func (ka *ecdheKeyAgreement) generateServerKeyExchange(config *Config, cert *Cer
|
||||||
copy(skx.key, serverECDHEParams)
|
copy(skx.key, serverECDHEParams)
|
||||||
k := skx.key[len(serverECDHEParams):]
|
k := skx.key[len(serverECDHEParams):]
|
||||||
if ka.version >= VersionTLS12 {
|
if ka.version >= VersionTLS12 {
|
||||||
k[0] = byte(signatureAlgorithm >> 8)
|
k[0] = byte(ka.signatureAlgorithm >> 8)
|
||||||
k[1] = byte(signatureAlgorithm)
|
k[1] = byte(ka.signatureAlgorithm)
|
||||||
k = k[2:]
|
k = k[2:]
|
||||||
}
|
}
|
||||||
k[0] = byte(len(sig) >> 8)
|
k[0] = byte(len(sig) >> 8)
|
||||||
|
|
@ -284,7 +287,7 @@ func (ka *ecdheKeyAgreement) processServerKeyExchange(config *Config, clientHell
|
||||||
if skx.key[0] != 3 { // named curve
|
if skx.key[0] != 3 { // named curve
|
||||||
return errors.New("tls: server selected unsupported curve")
|
return errors.New("tls: server selected unsupported curve")
|
||||||
}
|
}
|
||||||
curveID := CurveID(skx.key[1])<<8 | CurveID(skx.key[2])
|
ka.curveID = CurveID(skx.key[1])<<8 | CurveID(skx.key[2])
|
||||||
|
|
||||||
publicLen := int(skx.key[3])
|
publicLen := int(skx.key[3])
|
||||||
if publicLen+4 > len(skx.key) {
|
if publicLen+4 > len(skx.key) {
|
||||||
|
|
@ -298,15 +301,15 @@ func (ka *ecdheKeyAgreement) processServerKeyExchange(config *Config, clientHell
|
||||||
return errServerKeyExchange
|
return errServerKeyExchange
|
||||||
}
|
}
|
||||||
|
|
||||||
if !slices.Contains(clientHello.supportedCurves, curveID) {
|
if !slices.Contains(clientHello.supportedCurves, ka.curveID) {
|
||||||
return errors.New("tls: server selected unoffered curve")
|
return errors.New("tls: server selected unoffered curve")
|
||||||
}
|
}
|
||||||
|
|
||||||
if _, ok := curveForCurveID(curveID); !ok {
|
if _, ok := curveForCurveID(ka.curveID); !ok {
|
||||||
return errors.New("tls: server selected unsupported curve")
|
return errors.New("tls: server selected unsupported curve")
|
||||||
}
|
}
|
||||||
|
|
||||||
key, err := generateECDHEKey(config.rand(), curveID)
|
key, err := generateECDHEKey(config.rand(), ka.curveID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
@ -330,16 +333,16 @@ func (ka *ecdheKeyAgreement) processServerKeyExchange(config *Config, clientHell
|
||||||
var sigType uint8
|
var sigType uint8
|
||||||
var sigHash crypto.Hash
|
var sigHash crypto.Hash
|
||||||
if ka.version >= VersionTLS12 {
|
if ka.version >= VersionTLS12 {
|
||||||
signatureAlgorithm := SignatureScheme(sig[0])<<8 | SignatureScheme(sig[1])
|
ka.signatureAlgorithm = SignatureScheme(sig[0])<<8 | SignatureScheme(sig[1])
|
||||||
sig = sig[2:]
|
sig = sig[2:]
|
||||||
if len(sig) < 2 {
|
if len(sig) < 2 {
|
||||||
return errServerKeyExchange
|
return errServerKeyExchange
|
||||||
}
|
}
|
||||||
|
|
||||||
if !isSupportedSignatureAlgorithm(signatureAlgorithm, clientHello.supportedSignatureAlgorithms) {
|
if !isSupportedSignatureAlgorithm(ka.signatureAlgorithm, clientHello.supportedSignatureAlgorithms) {
|
||||||
return errors.New("tls: certificate used with invalid signature algorithm")
|
return errors.New("tls: certificate used with invalid signature algorithm")
|
||||||
}
|
}
|
||||||
sigType, sigHash, err = typeAndHashFromSignatureScheme(signatureAlgorithm)
|
sigType, sigHash, err = typeAndHashFromSignatureScheme(ka.signatureAlgorithm)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -66,11 +66,13 @@ func ExampleJoin() {
|
||||||
if errors.Is(err, err2) {
|
if errors.Is(err, err2) {
|
||||||
fmt.Println("err is err2")
|
fmt.Println("err is err2")
|
||||||
}
|
}
|
||||||
|
fmt.Println(err.(interface{ Unwrap() []error }).Unwrap())
|
||||||
// Output:
|
// Output:
|
||||||
// err1
|
// err1
|
||||||
// err2
|
// err2
|
||||||
// err is err1
|
// err is err1
|
||||||
// err is err2
|
// err is err2
|
||||||
|
// [err1 err2]
|
||||||
}
|
}
|
||||||
|
|
||||||
func ExampleIs() {
|
func ExampleIs() {
|
||||||
|
|
|
||||||
|
|
@ -34,6 +34,9 @@ func FileExports(src *File) bool {
|
||||||
//
|
//
|
||||||
// PackageExports reports whether there are exported declarations;
|
// PackageExports reports whether there are exported declarations;
|
||||||
// it returns false otherwise.
|
// it returns false otherwise.
|
||||||
|
//
|
||||||
|
// Deprecated: use the type checker [go/types] instead of [Package];
|
||||||
|
// see [Object]. Alternatively, use [FileExports].
|
||||||
func PackageExports(pkg *Package) bool {
|
func PackageExports(pkg *Package) bool {
|
||||||
return filterPackage(pkg, exportFilter, true)
|
return filterPackage(pkg, exportFilter, true)
|
||||||
}
|
}
|
||||||
|
|
@ -276,6 +279,9 @@ func filterFile(src *File, f Filter, export bool) bool {
|
||||||
//
|
//
|
||||||
// FilterPackage reports whether there are any top-level declarations
|
// FilterPackage reports whether there are any top-level declarations
|
||||||
// left after filtering.
|
// left after filtering.
|
||||||
|
//
|
||||||
|
// Deprecated: use the type checker [go/types] instead of [Package];
|
||||||
|
// see [Object]. Alternatively, use [FilterFile].
|
||||||
func FilterPackage(pkg *Package, f Filter) bool {
|
func FilterPackage(pkg *Package, f Filter) bool {
|
||||||
return filterPackage(pkg, f, false)
|
return filterPackage(pkg, f, false)
|
||||||
}
|
}
|
||||||
|
|
@ -294,8 +300,13 @@ func filterPackage(pkg *Package, f Filter, export bool) bool {
|
||||||
// Merging of package files
|
// Merging of package files
|
||||||
|
|
||||||
// The MergeMode flags control the behavior of [MergePackageFiles].
|
// The MergeMode flags control the behavior of [MergePackageFiles].
|
||||||
|
//
|
||||||
|
// Deprecated: use the type checker [go/types] instead of [Package];
|
||||||
|
// see [Object].
|
||||||
type MergeMode uint
|
type MergeMode uint
|
||||||
|
|
||||||
|
// Deprecated: use the type checker [go/types] instead of [Package];
|
||||||
|
// see [Object].
|
||||||
const (
|
const (
|
||||||
// If set, duplicate function declarations are excluded.
|
// If set, duplicate function declarations are excluded.
|
||||||
FilterFuncDuplicates MergeMode = 1 << iota
|
FilterFuncDuplicates MergeMode = 1 << iota
|
||||||
|
|
@ -332,6 +343,9 @@ var separator = &Comment{token.NoPos, "//"}
|
||||||
|
|
||||||
// MergePackageFiles creates a file AST by merging the ASTs of the
|
// MergePackageFiles creates a file AST by merging the ASTs of the
|
||||||
// files belonging to a package. The mode flags control merging behavior.
|
// files belonging to a package. The mode flags control merging behavior.
|
||||||
|
//
|
||||||
|
// Deprecated: this function is poorly specified and has unfixable
|
||||||
|
// bugs; also [Package] is deprecated.
|
||||||
func MergePackageFiles(pkg *Package, mode MergeMode) *File {
|
func MergePackageFiles(pkg *Package, mode MergeMode) *File {
|
||||||
// Count the number of package docs, comments and declarations across
|
// Count the number of package docs, comments and declarations across
|
||||||
// all package files. Also, compute sorted list of filenames, so that
|
// all package files. Also, compute sorted list of filenames, so that
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,9 @@
|
||||||
|
// Copyright 2025 The Go Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
package token
|
||||||
|
|
||||||
|
// exports for tests
|
||||||
|
|
||||||
|
func SearchInts(a []int, x int) int { return searchInts(a, x) }
|
||||||
|
|
@ -2,9 +2,14 @@
|
||||||
// Use of this source code is governed by a BSD-style
|
// Use of this source code is governed by a BSD-style
|
||||||
// license that can be found in the LICENSE file.
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
package token
|
package token_test
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"go/build"
|
||||||
|
"go/token"
|
||||||
|
"math/rand/v2"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
"testing"
|
"testing"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -14,11 +19,103 @@ func BenchmarkSearchInts(b *testing.B) {
|
||||||
data[i] = i
|
data[i] = i
|
||||||
}
|
}
|
||||||
const x = 8
|
const x = 8
|
||||||
if r := searchInts(data, x); r != x {
|
if r := token.SearchInts(data, x); r != x {
|
||||||
b.Errorf("got index = %d; want %d", r, x)
|
b.Errorf("got index = %d; want %d", r, x)
|
||||||
}
|
}
|
||||||
b.ResetTimer()
|
b.ResetTimer()
|
||||||
for i := 0; i < b.N; i++ {
|
for i := 0; i < b.N; i++ {
|
||||||
searchInts(data, x)
|
token.SearchInts(data, x)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func BenchmarkFileSet_Position(b *testing.B) {
|
||||||
|
rng := rand.New(rand.NewPCG(rand.Uint64(), rand.Uint64()))
|
||||||
|
|
||||||
|
// Create a FileSet based on the files of net/http,
|
||||||
|
// a single large package.
|
||||||
|
netHTTPFset := token.NewFileSet()
|
||||||
|
pkg, err := build.Default.Import("net/http", "", 0)
|
||||||
|
if err != nil {
|
||||||
|
b.Fatal(err)
|
||||||
|
}
|
||||||
|
for _, filename := range pkg.GoFiles {
|
||||||
|
filename = filepath.Join(pkg.Dir, filename)
|
||||||
|
fi, err := os.Stat(filename)
|
||||||
|
if err != nil {
|
||||||
|
b.Fatal(err)
|
||||||
|
}
|
||||||
|
netHTTPFset.AddFile(filename, -1, int(fi.Size()))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Measure randomly distributed Pos values across net/http.
|
||||||
|
b.Run("random", func(b *testing.B) {
|
||||||
|
base := netHTTPFset.Base()
|
||||||
|
for b.Loop() {
|
||||||
|
pos := token.Pos(rng.IntN(base))
|
||||||
|
_ = netHTTPFset.Position(pos)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
// Measure random lookups within the same file of net/http.
|
||||||
|
// (It's faster because of the "last file" cache.)
|
||||||
|
b.Run("file", func(b *testing.B) {
|
||||||
|
var file *token.File
|
||||||
|
for file = range netHTTPFset.Iterate {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
base, size := file.Base(), file.Size()
|
||||||
|
for b.Loop() {
|
||||||
|
_ = netHTTPFset.Position(token.Pos(base + rng.IntN(size)))
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
// Measure random lookups on a FileSet with a great many files.
|
||||||
|
b.Run("manyfiles", func(b *testing.B) {
|
||||||
|
fset := token.NewFileSet()
|
||||||
|
for range 25000 {
|
||||||
|
fset.AddFile("", -1, 10000)
|
||||||
|
}
|
||||||
|
base := fset.Base()
|
||||||
|
for b.Loop() {
|
||||||
|
pos := token.Pos(rng.IntN(base))
|
||||||
|
_ = fset.Position(pos)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func BenchmarkFileSet_AddExistingFiles(b *testing.B) {
|
||||||
|
// Create the "universe" of files.
|
||||||
|
fset := token.NewFileSet()
|
||||||
|
var files []*token.File
|
||||||
|
for range 25000 {
|
||||||
|
files = append(files, fset.AddFile("", -1, 10000))
|
||||||
|
}
|
||||||
|
rand.Shuffle(len(files), func(i, j int) {
|
||||||
|
files[i], files[j] = files[j], files[i]
|
||||||
|
})
|
||||||
|
|
||||||
|
// choose returns n random files.
|
||||||
|
choose := func(n int) []*token.File {
|
||||||
|
res := make([]*token.File, n)
|
||||||
|
for i := range res {
|
||||||
|
res[i] = files[rand.IntN(n)]
|
||||||
|
}
|
||||||
|
return files[:n]
|
||||||
|
}
|
||||||
|
|
||||||
|
// Measure the cost of creating a FileSet with a large number
|
||||||
|
// of files added in small handfuls, with some overlap.
|
||||||
|
// This case is critical to gopls.
|
||||||
|
b.Run("sequence", func(b *testing.B) {
|
||||||
|
for b.Loop() {
|
||||||
|
b.StopTimer()
|
||||||
|
fset2 := token.NewFileSet()
|
||||||
|
fset2.AddExistingFiles(files[:10000]...)
|
||||||
|
b.StartTimer()
|
||||||
|
|
||||||
|
for range 1000 {
|
||||||
|
fset2.AddExistingFiles(choose(10)...) // about one package
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -9,11 +9,13 @@ import (
|
||||||
"internal/synctest"
|
"internal/synctest"
|
||||||
"iter"
|
"iter"
|
||||||
"reflect"
|
"reflect"
|
||||||
|
"runtime"
|
||||||
"slices"
|
"slices"
|
||||||
"strconv"
|
"strconv"
|
||||||
"sync"
|
"sync"
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
"weak"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestNow(t *testing.T) {
|
func TestNow(t *testing.T) {
|
||||||
|
|
@ -625,6 +627,17 @@ func TestHappensBefore(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// https://go.dev/issue/73817
|
||||||
|
func TestWeak(t *testing.T) {
|
||||||
|
synctest.Run(func() {
|
||||||
|
for range 100 {
|
||||||
|
runtime.GC()
|
||||||
|
b := make([]byte, 1024)
|
||||||
|
weak.Make(&b)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
func wantPanic(t *testing.T, want string) {
|
func wantPanic(t *testing.T, want string) {
|
||||||
if e := recover(); e != nil {
|
if e := recover(); e != nil {
|
||||||
if got := fmt.Sprint(e); got != want {
|
if got := fmt.Sprint(e); got != want {
|
||||||
|
|
|
||||||
|
|
@ -9,8 +9,89 @@ import (
|
||||||
"io/fs"
|
"io/fs"
|
||||||
"log"
|
"log"
|
||||||
"os"
|
"os"
|
||||||
|
"testing/fstest"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
func ExampleGlob() {
|
||||||
|
fsys := fstest.MapFS{
|
||||||
|
"file.txt": {},
|
||||||
|
"file.go": {},
|
||||||
|
"dir/file.txt": {},
|
||||||
|
"dir/file.go": {},
|
||||||
|
"dir/subdir/x.go": {},
|
||||||
|
}
|
||||||
|
|
||||||
|
patterns := []string{
|
||||||
|
"*.txt",
|
||||||
|
"*.go",
|
||||||
|
"dir/*.go",
|
||||||
|
"dir/*/x.go",
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, pattern := range patterns {
|
||||||
|
matches, err := fs.Glob(fsys, pattern)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
fmt.Printf("%q matches: %v\n", pattern, matches)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Output:
|
||||||
|
// "*.txt" matches: [file.txt]
|
||||||
|
// "*.go" matches: [file.go]
|
||||||
|
// "dir/*.go" matches: [dir/file.go]
|
||||||
|
// "dir/*/x.go" matches: [dir/subdir/x.go]
|
||||||
|
}
|
||||||
|
|
||||||
|
func ExampleReadFile() {
|
||||||
|
fsys := fstest.MapFS{
|
||||||
|
"hello.txt": {
|
||||||
|
Data: []byte("Hello, World!\n"),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
data, err := fs.ReadFile(fsys, "hello.txt")
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Print(string(data))
|
||||||
|
|
||||||
|
// Output:
|
||||||
|
// Hello, World!
|
||||||
|
}
|
||||||
|
|
||||||
|
func ExampleValidPath() {
|
||||||
|
paths := []string{
|
||||||
|
".",
|
||||||
|
"x",
|
||||||
|
"x/y/z",
|
||||||
|
"",
|
||||||
|
"..",
|
||||||
|
"/x",
|
||||||
|
"x/",
|
||||||
|
"x//y",
|
||||||
|
"x/./y",
|
||||||
|
"x/../y",
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, path := range paths {
|
||||||
|
fmt.Printf("ValidPath(%q) = %t\n", path, fs.ValidPath(path))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Output:
|
||||||
|
// ValidPath(".") = true
|
||||||
|
// ValidPath("x") = true
|
||||||
|
// ValidPath("x/y/z") = true
|
||||||
|
// ValidPath("") = false
|
||||||
|
// ValidPath("..") = false
|
||||||
|
// ValidPath("/x") = false
|
||||||
|
// ValidPath("x/") = false
|
||||||
|
// ValidPath("x//y") = false
|
||||||
|
// ValidPath("x/./y") = false
|
||||||
|
// ValidPath("x/../y") = false
|
||||||
|
}
|
||||||
|
|
||||||
func ExampleWalkDir() {
|
func ExampleWalkDir() {
|
||||||
root := "/usr/local/go/bin"
|
root := "/usr/local/go/bin"
|
||||||
fileSystem := os.DirFS(root)
|
fileSystem := os.DirFS(root)
|
||||||
|
|
|
||||||
|
|
@ -315,7 +315,7 @@ func TestCallDepthConnection(t *testing.T) {
|
||||||
got := string(firstLine)
|
got := string(firstLine)
|
||||||
|
|
||||||
want := fmt.Sprintf(
|
want := fmt.Sprintf(
|
||||||
`source=:0 msg="logger_test.go:%d: %s"`,
|
`msg="logger_test.go:%d: %s"`,
|
||||||
line+i, tt.name,
|
line+i, tt.name,
|
||||||
)
|
)
|
||||||
if got != want {
|
if got != want {
|
||||||
|
|
|
||||||
|
|
@ -2309,9 +2309,9 @@ func TestOpenFileCreateExclDanglingSymlink(t *testing.T) {
|
||||||
var f *File
|
var f *File
|
||||||
var err error
|
var err error
|
||||||
if r == nil {
|
if r == nil {
|
||||||
f, err = OpenFile(link, O_WRONLY|O_CREATE|O_EXCL, 0o666)
|
f, err = OpenFile(link, O_WRONLY|O_CREATE|O_EXCL, 0o444)
|
||||||
} else {
|
} else {
|
||||||
f, err = r.OpenFile(link, O_WRONLY|O_CREATE|O_EXCL, 0o666)
|
f, err = r.OpenFile(link, O_WRONLY|O_CREATE|O_EXCL, 0o444)
|
||||||
}
|
}
|
||||||
if err == nil {
|
if err == nil {
|
||||||
f.Close()
|
f.Close()
|
||||||
|
|
|
||||||
|
|
@ -204,7 +204,7 @@ var lockPartialOrder [][]lockRank = [][]lockRank{
|
||||||
lockRankRoot: {},
|
lockRankRoot: {},
|
||||||
lockRankItab: {},
|
lockRankItab: {},
|
||||||
lockRankReflectOffs: {lockRankItab},
|
lockRankReflectOffs: {lockRankItab},
|
||||||
lockRankSynctest: {lockRankSysmon, lockRankScavenge, lockRankSweep, lockRankTestR, lockRankTimerSend, lockRankPollDesc, lockRankWakeableSleep, lockRankHchan, lockRankNotifyList, lockRankTimers, lockRankTimer, lockRankRoot, lockRankItab, lockRankReflectOffs},
|
lockRankSynctest: {lockRankSysmon, lockRankScavenge, lockRankSweepWaiters, lockRankStrongFromWeakQueue, lockRankSweep, lockRankTestR, lockRankTimerSend, lockRankPollDesc, lockRankWakeableSleep, lockRankHchan, lockRankNotifyList, lockRankTimers, lockRankTimer, lockRankRoot, lockRankItab, lockRankReflectOffs},
|
||||||
lockRankUserArenaState: {},
|
lockRankUserArenaState: {},
|
||||||
lockRankTraceBuf: {lockRankSysmon, lockRankScavenge},
|
lockRankTraceBuf: {lockRankSysmon, lockRankScavenge},
|
||||||
lockRankTraceStrings: {lockRankSysmon, lockRankScavenge, lockRankTraceBuf},
|
lockRankTraceStrings: {lockRankSysmon, lockRankScavenge, lockRankTraceBuf},
|
||||||
|
|
|
||||||
|
|
@ -457,6 +457,13 @@ func (q *cleanupQueue) flush() {
|
||||||
// new cleanup goroutines.
|
// new cleanup goroutines.
|
||||||
var cb *cleanupBlock
|
var cb *cleanupBlock
|
||||||
for _, pp := range allp {
|
for _, pp := range allp {
|
||||||
|
if pp == nil {
|
||||||
|
// This function is reachable via mallocgc in the
|
||||||
|
// middle of procresize, when allp has been resized,
|
||||||
|
// but the new Ps not allocated yet.
|
||||||
|
missing++
|
||||||
|
continue
|
||||||
|
}
|
||||||
b := pp.cleanups
|
b := pp.cleanups
|
||||||
if b == nil {
|
if b == nil {
|
||||||
missing++
|
missing++
|
||||||
|
|
|
||||||
|
|
@ -99,7 +99,15 @@ NONE
|
||||||
< reflectOffs;
|
< reflectOffs;
|
||||||
|
|
||||||
# Synctest
|
# Synctest
|
||||||
hchan, root, timers, timer, notifyList, reflectOffs < synctest;
|
hchan,
|
||||||
|
notifyList,
|
||||||
|
reflectOffs,
|
||||||
|
root,
|
||||||
|
strongFromWeakQueue,
|
||||||
|
sweepWaiters,
|
||||||
|
timer,
|
||||||
|
timers
|
||||||
|
< synctest;
|
||||||
|
|
||||||
# User arena state
|
# User arena state
|
||||||
NONE < userArenaState;
|
NONE < userArenaState;
|
||||||
|
|
|
||||||
|
|
@ -8,6 +8,7 @@ import (
|
||||||
"internal/synctest"
|
"internal/synctest"
|
||||||
"runtime"
|
"runtime"
|
||||||
"runtime/metrics"
|
"runtime/metrics"
|
||||||
|
"sync/atomic"
|
||||||
)
|
)
|
||||||
|
|
||||||
// This program ensures system goroutines (GC workers, finalizer goroutine)
|
// This program ensures system goroutines (GC workers, finalizer goroutine)
|
||||||
|
|
@ -27,11 +28,24 @@ func numGCCycles() uint64 {
|
||||||
}
|
}
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
|
// Channels created by a finalizer and cleanup func registered within the bubble.
|
||||||
|
var (
|
||||||
|
finalizerCh atomic.Pointer[chan struct{}]
|
||||||
|
cleanupCh atomic.Pointer[chan struct{}]
|
||||||
|
)
|
||||||
synctest.Run(func() {
|
synctest.Run(func() {
|
||||||
// Start the finalizer goroutine.
|
// Start the finalizer and cleanup goroutines.
|
||||||
|
{
|
||||||
p := new(int)
|
p := new(int)
|
||||||
runtime.SetFinalizer(p, func(*int) {})
|
runtime.SetFinalizer(p, func(*int) {
|
||||||
|
ch := make(chan struct{})
|
||||||
|
finalizerCh.Store(&ch)
|
||||||
|
})
|
||||||
|
runtime.AddCleanup(p, func(struct{}) {
|
||||||
|
ch := make(chan struct{})
|
||||||
|
cleanupCh.Store(&ch)
|
||||||
|
}, struct{}{})
|
||||||
|
}
|
||||||
startingCycles := numGCCycles()
|
startingCycles := numGCCycles()
|
||||||
ch1 := make(chan *int)
|
ch1 := make(chan *int)
|
||||||
ch2 := make(chan *int)
|
ch2 := make(chan *int)
|
||||||
|
|
@ -55,13 +69,18 @@ func main() {
|
||||||
|
|
||||||
// If we've improperly put a GC goroutine into the synctest group,
|
// If we've improperly put a GC goroutine into the synctest group,
|
||||||
// this Wait is going to hang.
|
// this Wait is going to hang.
|
||||||
synctest.Wait()
|
//synctest.Wait()
|
||||||
|
|
||||||
// End the test after a couple of GC cycles have passed.
|
// End the test after a couple of GC cycles have passed.
|
||||||
if numGCCycles()-startingCycles > 1 {
|
if numGCCycles()-startingCycles > 1 && finalizerCh.Load() != nil && cleanupCh.Load() != nil {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
// Close the channels created by the finalizer and cleanup func.
|
||||||
|
// If the funcs improperly ran inside the bubble, these channels are bubbled
|
||||||
|
// and trying to close them will panic.
|
||||||
|
close(*finalizerCh.Load())
|
||||||
|
close(*cleanupCh.Load())
|
||||||
println("success")
|
println("success")
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -155,6 +155,14 @@ func (t *traceMultiplexer) startLocked() error {
|
||||||
t.subscribersMu.Unlock()
|
t.subscribersMu.Unlock()
|
||||||
|
|
||||||
go func() {
|
go func() {
|
||||||
|
header := runtime_readTrace()
|
||||||
|
if traceStartWriter != nil {
|
||||||
|
traceStartWriter.Write(header)
|
||||||
|
}
|
||||||
|
if flightRecorder != nil {
|
||||||
|
flightRecorder.Write(header)
|
||||||
|
}
|
||||||
|
|
||||||
for {
|
for {
|
||||||
data := runtime_readTrace()
|
data := runtime_readTrace()
|
||||||
if data == nil {
|
if data == nil {
|
||||||
|
|
@ -167,9 +175,18 @@ func (t *traceMultiplexer) startLocked() error {
|
||||||
|
|
||||||
// Pick up any changes.
|
// Pick up any changes.
|
||||||
t.subscribersMu.Lock()
|
t.subscribersMu.Lock()
|
||||||
|
frIsNew := flightRecorder != t.flightRecorder && t.flightRecorder != nil
|
||||||
|
trIsNew := traceStartWriter != t.traceStartWriter && t.traceStartWriter != nil
|
||||||
flightRecorder = t.flightRecorder
|
flightRecorder = t.flightRecorder
|
||||||
traceStartWriter = t.traceStartWriter
|
traceStartWriter = t.traceStartWriter
|
||||||
t.subscribersMu.Unlock()
|
t.subscribersMu.Unlock()
|
||||||
|
|
||||||
|
if trIsNew {
|
||||||
|
traceStartWriter.Write(header)
|
||||||
|
}
|
||||||
|
if frIsNew {
|
||||||
|
flightRecorder.Write(header)
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
if traceStartWriter != nil {
|
if traceStartWriter != nil {
|
||||||
traceStartWriter.Write(data)
|
traceStartWriter.Write(data)
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,153 @@
|
||||||
|
// Copyright 2025 The Go Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
package trace_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
inttrace "internal/trace"
|
||||||
|
"internal/trace/testtrace"
|
||||||
|
"io"
|
||||||
|
"runtime"
|
||||||
|
"runtime/trace"
|
||||||
|
"slices"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestSubscribers(t *testing.T) {
|
||||||
|
validate := func(t *testing.T, source string, tr io.Reader) {
|
||||||
|
// Prepare to read the trace snapshot.
|
||||||
|
r, err := inttrace.NewReader(tr)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("unexpected error creating trace reader for %s: %v", source, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
v := testtrace.NewValidator()
|
||||||
|
// These platforms can't guarantee a monotonically increasing clock reading in a short trace.
|
||||||
|
if runtime.GOOS == "windows" || runtime.GOARCH == "wasm" {
|
||||||
|
v.SkipClockSnapshotChecks()
|
||||||
|
}
|
||||||
|
// Make sure there are Sync events: at the start and end.
|
||||||
|
var syncs []int
|
||||||
|
evs := 0
|
||||||
|
for {
|
||||||
|
ev, err := r.ReadEvent()
|
||||||
|
if err == io.EOF {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("unexpected error reading trace for %s: %v", source, err)
|
||||||
|
}
|
||||||
|
if err := v.Event(ev); err != nil {
|
||||||
|
t.Fatalf("event validation failed: %s", err)
|
||||||
|
}
|
||||||
|
if ev.Kind() == inttrace.EventSync {
|
||||||
|
syncs = append(syncs, evs)
|
||||||
|
}
|
||||||
|
evs++
|
||||||
|
}
|
||||||
|
ends := []int{syncs[0], syncs[len(syncs)-1]}
|
||||||
|
if wantEnds := []int{0, evs - 1}; !slices.Equal(wantEnds, ends) {
|
||||||
|
t.Errorf("expected a sync event at each end of the trace, found sync events at %d instead of %d for %s",
|
||||||
|
ends, wantEnds, source)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
validateTraces := func(t *testing.T, tReader, frReader io.Reader) {
|
||||||
|
validate(t, "tracer", tReader)
|
||||||
|
validate(t, "flightRecorder", frReader)
|
||||||
|
}
|
||||||
|
startFlightRecorder := func(t *testing.T) *trace.FlightRecorder {
|
||||||
|
fr := trace.NewFlightRecorder(trace.FlightRecorderConfig{})
|
||||||
|
if err := fr.Start(); err != nil {
|
||||||
|
t.Fatalf("unexpected error creating flight recorder: %v", err)
|
||||||
|
}
|
||||||
|
return fr
|
||||||
|
}
|
||||||
|
startTrace := func(t *testing.T, w io.Writer) {
|
||||||
|
if err := trace.Start(w); err != nil {
|
||||||
|
t.Fatalf("unexpected error starting flight recorder: %v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
stopFlightRecorder := func(t *testing.T, fr *trace.FlightRecorder, w io.Writer) {
|
||||||
|
if _, err := fr.WriteTo(w); err != nil {
|
||||||
|
t.Fatalf("unexpected error writing trace from flight recorder: %v", err)
|
||||||
|
}
|
||||||
|
fr.Stop()
|
||||||
|
}
|
||||||
|
stopTrace := func() {
|
||||||
|
trace.Stop()
|
||||||
|
}
|
||||||
|
t.Run("start(flight)_start(trace)_stop(trace)_stop(flight)", func(t *testing.T) {
|
||||||
|
if trace.IsEnabled() {
|
||||||
|
t.Skip("skipping because trace is already enabled")
|
||||||
|
}
|
||||||
|
frBuf := new(bytes.Buffer)
|
||||||
|
tBuf := new(bytes.Buffer)
|
||||||
|
fr := startFlightRecorder(t)
|
||||||
|
defer fr.Stop()
|
||||||
|
startTrace(t, tBuf)
|
||||||
|
defer trace.Stop()
|
||||||
|
stopTrace()
|
||||||
|
stopFlightRecorder(t, fr, frBuf)
|
||||||
|
validateTraces(t, tBuf, frBuf)
|
||||||
|
})
|
||||||
|
t.Run("start(trace)_start(flight)_stop(trace)_stop(flight)", func(t *testing.T) {
|
||||||
|
if trace.IsEnabled() {
|
||||||
|
t.Skip("skipping because trace is already enabled")
|
||||||
|
}
|
||||||
|
frBuf := new(bytes.Buffer)
|
||||||
|
tBuf := new(bytes.Buffer)
|
||||||
|
startTrace(t, tBuf)
|
||||||
|
defer trace.Stop()
|
||||||
|
fr := startFlightRecorder(t)
|
||||||
|
defer fr.Stop()
|
||||||
|
stopTrace()
|
||||||
|
stopFlightRecorder(t, fr, frBuf)
|
||||||
|
validateTraces(t, tBuf, frBuf)
|
||||||
|
})
|
||||||
|
t.Run("start(flight)_stop(flight)_start(trace)_stop(trace)", func(t *testing.T) {
|
||||||
|
if trace.IsEnabled() {
|
||||||
|
t.Skip("skipping because trace is already enabled")
|
||||||
|
}
|
||||||
|
frBuf := new(bytes.Buffer)
|
||||||
|
tBuf := new(bytes.Buffer)
|
||||||
|
fr := startFlightRecorder(t)
|
||||||
|
defer fr.Stop()
|
||||||
|
stopFlightRecorder(t, fr, frBuf)
|
||||||
|
startTrace(t, tBuf)
|
||||||
|
defer trace.Stop()
|
||||||
|
stopTrace()
|
||||||
|
validateTraces(t, tBuf, frBuf)
|
||||||
|
})
|
||||||
|
t.Run("start(flight)_stop(flight)_start(trace)_stop(trace)", func(t *testing.T) {
|
||||||
|
if trace.IsEnabled() {
|
||||||
|
t.Skip("skipping because trace is already enabled")
|
||||||
|
}
|
||||||
|
frBuf := new(bytes.Buffer)
|
||||||
|
tBuf := new(bytes.Buffer)
|
||||||
|
fr := startFlightRecorder(t)
|
||||||
|
defer fr.Stop()
|
||||||
|
stopFlightRecorder(t, fr, frBuf)
|
||||||
|
startTrace(t, tBuf)
|
||||||
|
defer trace.Stop()
|
||||||
|
stopTrace()
|
||||||
|
validateTraces(t, tBuf, frBuf)
|
||||||
|
})
|
||||||
|
t.Run("start(flight)_start(trace)_stop(flight)_stop(trace)", func(t *testing.T) {
|
||||||
|
if trace.IsEnabled() {
|
||||||
|
t.Skip("skipping because trace is already enabled")
|
||||||
|
}
|
||||||
|
frBuf := new(bytes.Buffer)
|
||||||
|
tBuf := new(bytes.Buffer)
|
||||||
|
fr := startFlightRecorder(t)
|
||||||
|
defer fr.Stop()
|
||||||
|
startTrace(t, tBuf)
|
||||||
|
defer trace.Stop()
|
||||||
|
stopFlightRecorder(t, fr, frBuf)
|
||||||
|
stopTrace()
|
||||||
|
validateTraces(t, tBuf, frBuf)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
@ -398,22 +398,7 @@ func Open(name string, flag int, perm uint32) (fd Handle, err error) {
|
||||||
if flag&O_CLOEXEC == 0 {
|
if flag&O_CLOEXEC == 0 {
|
||||||
sa = makeInheritSa()
|
sa = makeInheritSa()
|
||||||
}
|
}
|
||||||
// We don't use CREATE_ALWAYS, because when opening a file with
|
|
||||||
// FILE_ATTRIBUTE_READONLY these will replace an existing file
|
|
||||||
// with a new, read-only one. See https://go.dev/issue/38225.
|
|
||||||
//
|
|
||||||
// Instead, we ftruncate the file after opening when O_TRUNC is set.
|
|
||||||
var createmode uint32
|
|
||||||
var attrs uint32 = FILE_ATTRIBUTE_NORMAL
|
var attrs uint32 = FILE_ATTRIBUTE_NORMAL
|
||||||
switch {
|
|
||||||
case flag&(O_CREAT|O_EXCL) == (O_CREAT | O_EXCL):
|
|
||||||
createmode = CREATE_NEW
|
|
||||||
attrs |= FILE_FLAG_OPEN_REPARSE_POINT // don't follow symlinks
|
|
||||||
case flag&O_CREAT == O_CREAT:
|
|
||||||
createmode = OPEN_ALWAYS
|
|
||||||
default:
|
|
||||||
createmode = OPEN_EXISTING
|
|
||||||
}
|
|
||||||
if perm&S_IWRITE == 0 {
|
if perm&S_IWRITE == 0 {
|
||||||
attrs = FILE_ATTRIBUTE_READONLY
|
attrs = FILE_ATTRIBUTE_READONLY
|
||||||
}
|
}
|
||||||
|
|
@ -433,6 +418,21 @@ func Open(name string, flag int, perm uint32) (fd Handle, err error) {
|
||||||
const _FILE_FLAG_WRITE_THROUGH = 0x80000000
|
const _FILE_FLAG_WRITE_THROUGH = 0x80000000
|
||||||
attrs |= _FILE_FLAG_WRITE_THROUGH
|
attrs |= _FILE_FLAG_WRITE_THROUGH
|
||||||
}
|
}
|
||||||
|
// We don't use CREATE_ALWAYS, because when opening a file with
|
||||||
|
// FILE_ATTRIBUTE_READONLY these will replace an existing file
|
||||||
|
// with a new, read-only one. See https://go.dev/issue/38225.
|
||||||
|
//
|
||||||
|
// Instead, we ftruncate the file after opening when O_TRUNC is set.
|
||||||
|
var createmode uint32
|
||||||
|
switch {
|
||||||
|
case flag&(O_CREAT|O_EXCL) == (O_CREAT | O_EXCL):
|
||||||
|
createmode = CREATE_NEW
|
||||||
|
attrs |= FILE_FLAG_OPEN_REPARSE_POINT // don't follow symlinks
|
||||||
|
case flag&O_CREAT == O_CREAT:
|
||||||
|
createmode = OPEN_ALWAYS
|
||||||
|
default:
|
||||||
|
createmode = OPEN_EXISTING
|
||||||
|
}
|
||||||
h, err := createFile(namep, access, sharemode, sa, createmode, attrs, 0)
|
h, err := createFile(namep, access, sharemode, sa, createmode, attrs, 0)
|
||||||
if h == InvalidHandle {
|
if h == InvalidHandle {
|
||||||
if err == ERROR_ACCESS_DENIED && (attrs&FILE_FLAG_BACKUP_SEMANTICS == 0) {
|
if err == ERROR_ACCESS_DENIED && (attrs&FILE_FLAG_BACKUP_SEMANTICS == 0) {
|
||||||
|
|
|
||||||
|
|
@ -83,6 +83,10 @@
|
||||||
// is associated with it. Operating on a bubbled channel, timer, or
|
// is associated with it. Operating on a bubbled channel, timer, or
|
||||||
// ticker from outside the bubble panics.
|
// ticker from outside the bubble panics.
|
||||||
//
|
//
|
||||||
|
// Cleanup functions and finalizers registered with
|
||||||
|
// [runtime.AddCleanup] and [runtime.SetFinalizer]
|
||||||
|
// run outside of any bubble.
|
||||||
|
//
|
||||||
// # Example: Context.AfterFunc
|
// # Example: Context.AfterFunc
|
||||||
//
|
//
|
||||||
// This example demonstrates testing the [context.AfterFunc] function.
|
// This example demonstrates testing the [context.AfterFunc] function.
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,34 @@
|
||||||
|
// run
|
||||||
|
|
||||||
|
// Copyright 2025 The Go Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
package main
|
||||||
|
|
||||||
|
type SourceRange struct {
|
||||||
|
x, y int
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *SourceRange) String() string {
|
||||||
|
return "hello"
|
||||||
|
}
|
||||||
|
|
||||||
|
type SourceNode interface {
|
||||||
|
SourceRange()
|
||||||
|
}
|
||||||
|
|
||||||
|
type testNode SourceRange
|
||||||
|
|
||||||
|
func (tn testNode) SourceRange() {
|
||||||
|
}
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
n := testNode(SourceRange{}) // zero value
|
||||||
|
Errorf(n)
|
||||||
|
}
|
||||||
|
|
||||||
|
//go:noinline
|
||||||
|
func Errorf(n SourceNode) {
|
||||||
|
n.SourceRange()
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,34 @@
|
||||||
|
// run
|
||||||
|
|
||||||
|
// Copyright 2025 The Go Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
package main
|
||||||
|
|
||||||
|
type SourceRange struct {
|
||||||
|
x, y int
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *SourceRange) String() string {
|
||||||
|
return "hello"
|
||||||
|
}
|
||||||
|
|
||||||
|
type SourceNode interface {
|
||||||
|
SourceRange()
|
||||||
|
}
|
||||||
|
|
||||||
|
type testNode SourceRange
|
||||||
|
|
||||||
|
func (tn testNode) SourceRange() {
|
||||||
|
}
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
n := testNode(SourceRange{1, 1}) // not zero value
|
||||||
|
Errorf(n)
|
||||||
|
}
|
||||||
|
|
||||||
|
//go:noinline
|
||||||
|
func Errorf(n SourceNode) {
|
||||||
|
n.SourceRange()
|
||||||
|
}
|
||||||
Loading…
Reference in New Issue