Skip to content

Commit 5ba287c

Browse files
committed
Merge remote-tracking branch 'go/release-branch.go1.26' into update-go1.26-rc2
2 parents 64a6cb4 + 70d5aa7 commit 5ba287c

6 files changed

Lines changed: 129 additions & 27 deletions

File tree

VERSION

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,2 @@
1-
go1.26rc2
2-
time 2026-01-08T21:54:29Z
1+
go1.26rc3
2+
time 2026-02-03T20:09:49Z

src/crypto/tls/common.go

Lines changed: 41 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ import (
2222
"internal/godebug"
2323
"io"
2424
"net"
25+
"runtime"
2526
"slices"
2627
"strings"
2728
"sync"
@@ -632,10 +633,13 @@ type Config struct {
632633
// If GetConfigForClient is nil, the Config passed to Server() will be
633634
// used for all connections.
634635
//
635-
// If SessionTicketKey was explicitly set on the returned Config, or if
636-
// SetSessionTicketKeys was called on the returned Config, those keys will
636+
// If SessionTicketKey is explicitly set on the returned Config, or if
637+
// SetSessionTicketKeys is called on the returned Config, those keys will
637638
// be used. Otherwise, the original Config keys will be used (and possibly
638-
// rotated if they are automatically managed).
639+
// rotated if they are automatically managed). WARNING: this allows session
640+
// resumtion of connections originally established with the parent (or a
641+
// sibling) Config, which may bypass the [Config.VerifyPeerCertificate]
642+
// value of the returned Config.
639643
GetConfigForClient func(*ClientHelloInfo) (*Config, error)
640644

641645
// VerifyPeerCertificate, if not nil, is called after normal
@@ -653,8 +657,10 @@ type Config struct {
653657
// rawCerts may be empty on the server if ClientAuth is RequestClientCert or
654658
// VerifyClientCertIfGiven.
655659
//
656-
// This callback is not invoked on resumed connections, as certificates are
657-
// not re-verified on resumption.
660+
// This callback is not invoked on resumed connections. WARNING: this
661+
// includes connections resumed across Configs returned by [Config.Clone] or
662+
// [Config.GetConfigForClient] and their parents. If that is not intended,
663+
// use [Config.VerifyConnection] instead, or set [Config.SessionTicketsDisabled].
658664
//
659665
// verifiedChains and its contents should not be modified.
660666
VerifyPeerCertificate func(rawCerts [][]byte, verifiedChains [][]*x509.Certificate) error
@@ -978,8 +984,15 @@ func (c *Config) ticketKeyFromBytes(b [32]byte) (key ticketKey) {
978984
// ticket, and the lifetime we set for all tickets we send.
979985
const maxSessionTicketLifetime = 7 * 24 * time.Hour
980986

981-
// Clone returns a shallow clone of c or nil if c is nil. It is safe to clone a [Config] that is
982-
// being used concurrently by a TLS client or server.
987+
// Clone returns a shallow clone of c or nil if c is nil. It is safe to clone a
988+
// [Config] that is being used concurrently by a TLS client or server.
989+
//
990+
// The returned Config can share session ticket keys with the original Config,
991+
// which means connections could be resumed across the two Configs. WARNING:
992+
// [Config.VerifyPeerCertificate] does not get called on resumed connections,
993+
// including connections that were originally established on the parent Config.
994+
// If that is not intended, use [Config.VerifyConnection] instead, or set
995+
// [Config.SessionTicketsDisabled].
983996
func (c *Config) Clone() *Config {
984997
if c == nil {
985998
return nil
@@ -1861,15 +1874,27 @@ func anyValidVerifiedChain(verifiedChains [][]*x509.Certificate, opts x509.Verif
18611874
}) {
18621875
continue
18631876
}
1864-
// Since we already validated the chain, we only care that it is
1865-
// rooted in a CA in CAs, or in the system pool. On platforms where
1866-
// we control chain validation (e.g. not Windows or macOS) this is a
1867-
// simple lookup in the CertPool internal hash map. On other
1868-
// platforms, this may be more expensive, depending on how they
1869-
// implement verification of just root certificates.
1870-
root := chain[len(chain)-1]
1871-
if _, err := root.Verify(opts); err == nil {
1872-
return true
1877+
// Since we already validated the chain, we only care that it is rooted
1878+
// in a CA in opts.Roots. On platforms where we control chain validation
1879+
// (e.g. not Windows or macOS) this is a simple lookup in the CertPool
1880+
// internal hash map, which we can simulate by running Verify on the
1881+
// root. On other platforms, we have to do full verification again,
1882+
// because EKU handling might differ. We will want to replace this with
1883+
// CertPool.Contains if/once that is available. See go.dev/issue/77376.
1884+
if runtime.GOOS == "windows" || runtime.GOOS == "darwin" || runtime.GOOS == "ios" {
1885+
opts.Intermediates = x509.NewCertPool()
1886+
for _, cert := range chain[1:max(1, len(chain)-1)] {
1887+
opts.Intermediates.AddCert(cert)
1888+
}
1889+
leaf := chain[0]
1890+
if _, err := leaf.Verify(opts); err == nil {
1891+
return true
1892+
}
1893+
} else {
1894+
root := chain[len(chain)-1]
1895+
if _, err := root.Verify(opts); err == nil {
1896+
return true
1897+
}
18731898
}
18741899
}
18751900
return false

src/crypto/tls/handshake_server_test.go

Lines changed: 20 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -2302,7 +2302,7 @@ func testHandshakeGetConfigForClientDifferentClientCAs(t *testing.T, version uin
23022302
if err != nil {
23032303
t.Fatalf("ParseCertificate: %v", err)
23042304
}
2305-
rootDER, err = x509.CreateCertificate(rand.Reader, tmpl, tmpl, &testECDSAPrivateKey.PublicKey, testECDSAPrivateKey)
2305+
rootDER, err = x509.CreateCertificate(rand.Reader, tmpl, tmpl, &testRSA2048PrivateKey.PublicKey, testRSA2048PrivateKey)
23062306
if err != nil {
23072307
t.Fatalf("CreateCertificate: %v", err)
23082308
}
@@ -2318,15 +2318,19 @@ func testHandshakeGetConfigForClientDifferentClientCAs(t *testing.T, version uin
23182318
NotAfter: now.Add(time.Hour * 24),
23192319
KeyUsage: x509.KeyUsageDigitalSignature,
23202320
}
2321-
certDER, err := x509.CreateCertificate(rand.Reader, tmpl, rootA, &testECDSAPrivateKey.PublicKey, testECDSAPrivateKey)
2321+
certA, err := x509.CreateCertificate(rand.Reader, tmpl, rootA, &testECDSAPrivateKey.PublicKey, testECDSAPrivateKey)
2322+
if err != nil {
2323+
t.Fatalf("CreateCertificate: %v", err)
2324+
}
2325+
certB, err := x509.CreateCertificate(rand.Reader, tmpl, rootB, &testECDSAPrivateKey.PublicKey, testRSA2048PrivateKey)
23222326
if err != nil {
23232327
t.Fatalf("CreateCertificate: %v", err)
23242328
}
23252329

23262330
serverConfig := testConfig.Clone()
23272331
serverConfig.MaxVersion = version
23282332
serverConfig.Certificates = []Certificate{{
2329-
Certificate: [][]byte{certDER},
2333+
Certificate: [][]byte{certA},
23302334
PrivateKey: testECDSAPrivateKey,
23312335
}}
23322336
serverConfig.Time = func() time.Time {
@@ -2351,7 +2355,7 @@ func testHandshakeGetConfigForClientDifferentClientCAs(t *testing.T, version uin
23512355
clientConfig := testConfig.Clone()
23522356
clientConfig.MaxVersion = version
23532357
clientConfig.Certificates = []Certificate{{
2354-
Certificate: [][]byte{certDER},
2358+
Certificate: [][]byte{certA},
23552359
PrivateKey: testECDSAPrivateKey,
23562360
}}
23572361
clientConfig.ClientSessionCache = NewLRUClientSessionCache(32)
@@ -2380,6 +2384,8 @@ func testHandshakeGetConfigForClientDifferentClientCAs(t *testing.T, version uin
23802384
testResume(t, serverConfig, clientConfig, false)
23812385
testResume(t, serverConfig, clientConfig, true)
23822386

2387+
clientConfig.Certificates[0].Certificate = [][]byte{certB}
2388+
23832389
// Cause GetConfigForClient to return a config cloned from the base config,
23842390
// but with a different ClientCAs pool. This should cause resumption to fail.
23852391
switchConfig = true
@@ -2414,7 +2420,7 @@ func testHandshakeChangeRootCAsResumption(t *testing.T, version uint16) {
24142420
if err != nil {
24152421
t.Fatalf("ParseCertificate: %v", err)
24162422
}
2417-
rootDER, err = x509.CreateCertificate(rand.Reader, tmpl, tmpl, &testECDSAPrivateKey.PublicKey, testECDSAPrivateKey)
2423+
rootDER, err = x509.CreateCertificate(rand.Reader, tmpl, tmpl, &testRSA2048PrivateKey.PublicKey, testRSA2048PrivateKey)
24182424
if err != nil {
24192425
t.Fatalf("CreateCertificate: %v", err)
24202426
}
@@ -2430,15 +2436,19 @@ func testHandshakeChangeRootCAsResumption(t *testing.T, version uint16) {
24302436
NotAfter: now.Add(time.Hour * 24),
24312437
KeyUsage: x509.KeyUsageDigitalSignature,
24322438
}
2433-
certDER, err := x509.CreateCertificate(rand.Reader, tmpl, rootA, &testECDSAPrivateKey.PublicKey, testECDSAPrivateKey)
2439+
certA, err := x509.CreateCertificate(rand.Reader, tmpl, rootA, &testECDSAPrivateKey.PublicKey, testECDSAPrivateKey)
2440+
if err != nil {
2441+
t.Fatalf("CreateCertificate: %v", err)
2442+
}
2443+
certB, err := x509.CreateCertificate(rand.Reader, tmpl, rootB, &testECDSAPrivateKey.PublicKey, testRSA2048PrivateKey)
24342444
if err != nil {
24352445
t.Fatalf("CreateCertificate: %v", err)
24362446
}
24372447

24382448
serverConfig := testConfig.Clone()
24392449
serverConfig.MaxVersion = version
24402450
serverConfig.Certificates = []Certificate{{
2441-
Certificate: [][]byte{certDER},
2451+
Certificate: [][]byte{certA},
24422452
PrivateKey: testECDSAPrivateKey,
24432453
}}
24442454
serverConfig.Time = func() time.Time {
@@ -2453,7 +2463,7 @@ func testHandshakeChangeRootCAsResumption(t *testing.T, version uint16) {
24532463
clientConfig := testConfig.Clone()
24542464
clientConfig.MaxVersion = version
24552465
clientConfig.Certificates = []Certificate{{
2456-
Certificate: [][]byte{certDER},
2466+
Certificate: [][]byte{certA},
24572467
PrivateKey: testECDSAPrivateKey,
24582468
}}
24592469
clientConfig.ClientSessionCache = NewLRUClientSessionCache(32)
@@ -2486,6 +2496,8 @@ func testHandshakeChangeRootCAsResumption(t *testing.T, version uint16) {
24862496
clientConfig.RootCAs = x509.NewCertPool()
24872497
clientConfig.RootCAs.AddCert(rootB)
24882498

2499+
serverConfig.Certificates[0].Certificate = [][]byte{certB}
2500+
24892501
testResume(t, serverConfig, clientConfig, false)
24902502
testResume(t, serverConfig, clientConfig, true)
24912503
}

src/crypto/tls/tls_test.go

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -572,6 +572,41 @@ func TestVerifyHostname(t *testing.T) {
572572
}
573573
}
574574

575+
func TestRealResumption(t *testing.T) {
576+
testenv.MustHaveExternalNetwork(t)
577+
578+
config := &Config{
579+
ServerName: "yahoo.com",
580+
ClientSessionCache: NewLRUClientSessionCache(0),
581+
}
582+
583+
for range 10 {
584+
conn, err := Dial("tcp", "yahoo.com:443", config)
585+
if err != nil {
586+
t.Log("Dial error:", err)
587+
continue
588+
}
589+
// Do a read to consume the NewSessionTicket messages.
590+
fmt.Fprintf(conn, "GET / HTTP/1.1\r\nHost: yahoo.com\r\nConnection: close\r\n\r\n")
591+
conn.Read(make([]byte, 4096))
592+
conn.Close()
593+
594+
conn, err = Dial("tcp", "yahoo.com:443", config)
595+
if err != nil {
596+
t.Log("second Dial error:", err)
597+
continue
598+
}
599+
state := conn.ConnectionState()
600+
conn.Close()
601+
602+
if state.DidResume {
603+
return
604+
}
605+
}
606+
607+
t.Fatal("no connection used session resumption")
608+
}
609+
575610
func TestConnCloseBreakingWrite(t *testing.T) {
576611
ln := newLocalListener(t)
577612
defer ln.Close()

src/internal/poll/fd_mutex.go

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -265,7 +265,9 @@ func (fd *FD) readWriteLock() error {
265265
// is no remaining reference.
266266
func (fd *FD) readWriteUnlock() {
267267
fd.fdmu.rwunlock(true)
268-
fd.fdmu.rwunlock(false)
268+
if fd.fdmu.rwunlock(false) {
269+
fd.destroy()
270+
}
269271
}
270272

271273
// closing returns true if fd is closing.

src/os/os_test.go

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3468,6 +3468,34 @@ func TestWriteStringAlloc(t *testing.T) {
34683468
}
34693469
}
34703470

3471+
// Test that it's OK to have parallel I/O and Close on a file.
3472+
func TestFileIOCloseRace(t *testing.T) {
3473+
t.Parallel()
3474+
file, err := Create(filepath.Join(t.TempDir(), "test.txt"))
3475+
if err != nil {
3476+
t.Fatal(err)
3477+
}
3478+
var wg sync.WaitGroup
3479+
wg.Go(func() {
3480+
var tmp [100]byte
3481+
if _, err := file.Write(tmp[:]); err != nil && !errors.Is(err, ErrClosed) {
3482+
t.Error(err)
3483+
}
3484+
})
3485+
wg.Go(func() {
3486+
var tmp [100]byte
3487+
if _, err := file.Read(tmp[:]); err != nil && err != io.EOF && !errors.Is(err, ErrClosed) {
3488+
t.Error(err)
3489+
}
3490+
})
3491+
wg.Go(func() {
3492+
if err := file.Close(); err != nil {
3493+
t.Error(err)
3494+
}
3495+
})
3496+
wg.Wait()
3497+
}
3498+
34713499
// Test that it's OK to have parallel I/O and Close on a pipe.
34723500
func TestPipeIOCloseRace(t *testing.T) {
34733501
// Skip on wasm, which doesn't have pipes.

0 commit comments

Comments
 (0)