diff options
| -rw-r--r-- | src/crypto/tls/conn.go | 2 | ||||
| -rw-r--r-- | src/crypto/tls/quic.go | 21 | ||||
| -rw-r--r-- | src/crypto/tls/quic_test.go | 19 |
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 |
