aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/crypto/tls/conn.go2
-rw-r--r--src/crypto/tls/quic.go21
-rw-r--r--src/crypto/tls/quic_test.go19
3 files changed, 29 insertions, 13 deletions
diff --git a/src/crypto/tls/conn.go b/src/crypto/tls/conn.go
index a840125a45..9c662ef8f6 100644
--- a/src/crypto/tls/conn.go
+++ b/src/crypto/tls/conn.go
@@ -1531,7 +1531,7 @@ func (c *Conn) handshakeContext(ctx context.Context) (ret error) {
defer cancel()
if c.quic != nil {
- c.quic.cancelc = handshakeCtx.Done()
+ c.quic.ctx = handshakeCtx
c.quic.cancel = cancel
} else if ctx.Done() != nil {
// Close the connection if ctx is canceled before the function returns.
diff --git a/src/crypto/tls/quic.go b/src/crypto/tls/quic.go
index 76b7eb2cbd..c7d8508acb 100644
--- a/src/crypto/tls/quic.go
+++ b/src/crypto/tls/quic.go
@@ -162,7 +162,7 @@ type quicState struct {
started bool
signalc chan struct{} // handshake data is available to be read
blockedc chan struct{} // handshake is waiting for data, closed when done
- cancelc <-chan struct{} // handshake has been canceled
+ ctx context.Context // handshake context
cancel context.CancelFunc
waitingForDrain bool
@@ -261,10 +261,11 @@ func (q *QUICConn) NextEvent() QUICEvent {
// Close closes the connection and stops any in-progress handshake.
func (q *QUICConn) Close() error {
- if q.conn.quic.cancel == nil {
+ if q.conn.quic.ctx == nil {
return nil // never started
}
q.conn.quic.cancel()
+ <-q.conn.quic.signalc
for range q.conn.quic.blockedc {
// Wait for the handshake goroutine to return.
}
@@ -511,20 +512,16 @@ func (c *Conn) quicWaitForSignal() error {
// Send on blockedc to notify the QUICConn that the handshake is blocked.
// Exported methods of QUICConn wait for the handshake to become blocked
// before returning to the user.
- select {
- case c.quic.blockedc <- struct{}{}:
- case <-c.quic.cancelc:
- return c.sendAlertLocked(alertCloseNotify)
- }
+ c.quic.blockedc <- struct{}{}
// The QUICConn reads from signalc to notify us that the handshake may
// be able to proceed. (The QUICConn reads, because we close signalc to
// indicate that the handshake has completed.)
- select {
- case c.quic.signalc <- struct{}{}:
- c.hand.Write(c.quic.readbuf)
- c.quic.readbuf = nil
- case <-c.quic.cancelc:
+ c.quic.signalc <- struct{}{}
+ if c.quic.ctx.Err() != nil {
+ // The connection has been canceled.
return c.sendAlertLocked(alertCloseNotify)
}
+ c.hand.Write(c.quic.readbuf)
+ c.quic.readbuf = nil
return nil
}
diff --git a/src/crypto/tls/quic_test.go b/src/crypto/tls/quic_test.go
index bd0eaa4d47..0cf8b337e1 100644
--- a/src/crypto/tls/quic_test.go
+++ b/src/crypto/tls/quic_test.go
@@ -11,6 +11,7 @@ import (
"fmt"
"reflect"
"strings"
+ "sync"
"testing"
)
@@ -480,6 +481,24 @@ func TestQUICStartContextPropagation(t *testing.T) {
}
}
+func TestQUICContextCancelation(t *testing.T) {
+ ctx, cancel := context.WithCancel(context.Background())
+ config := &QUICConfig{TLSConfig: testConfig.Clone()}
+ config.TLSConfig.MinVersion = VersionTLS13
+ cli := newTestQUICClient(t, config)
+ cli.conn.SetTransportParameters(nil)
+ srv := newTestQUICServer(t, config)
+ srv.conn.SetTransportParameters(nil)
+ // Verify that canceling the connection context concurrently does not cause any races.
+ // See https://go.dev/issue/77274.
+ var wg sync.WaitGroup
+ wg.Go(func() {
+ _ = runTestQUICConnection(ctx, cli, srv, nil)
+ })
+ wg.Go(cancel)
+ wg.Wait()
+}
+
func TestQUICDelayedTransportParameters(t *testing.T) {
clientConfig := &QUICConfig{TLSConfig: testConfig.Clone()}
clientConfig.TLSConfig.MinVersion = VersionTLS13