Skip to content

Commit 1a02350

Browse files
Danielius1922Sean-Der
authored andcommitted
Fix race between Conn.Close and Conn.Handshake
1 parent 032d60c commit 1a02350

2 files changed

Lines changed: 81 additions & 10 deletions

File tree

conn.go

Lines changed: 30 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -74,13 +74,13 @@ type Conn struct {
7474

7575
handshakeCompletedSuccessfully atomic.Value
7676
handshakeMutex sync.Mutex
77+
handshakeDone chan struct{}
7778

7879
encryptedPackets []addrPkt
7980

8081
connectionClosedByUser bool
8182
closeLock sync.Mutex
8283
closed *closer.Closer
83-
handshakeLoopsFinished sync.WaitGroup
8484

8585
readDeadline *deadline.Deadline
8686
writeDeadline *deadline.Deadline
@@ -256,6 +256,12 @@ func (c *Conn) HandshakeContext(ctx context.Context) error {
256256
return nil
257257
}
258258

259+
handshakeDone := make(chan struct{})
260+
defer close(handshakeDone)
261+
c.closeLock.Lock()
262+
c.handshakeDone = handshakeDone
263+
c.closeLock.Unlock()
264+
259265
// rfc5246#section-7.4.3
260266
// In addition, the hash and signature algorithms MUST be compatible
261267
// with the key in the server's end-entity certificate.
@@ -405,7 +411,12 @@ func (c *Conn) Write(p []byte) (int, error) {
405411
// Close closes the connection.
406412
func (c *Conn) Close() error {
407413
err := c.close(true) //nolint:contextcheck
408-
c.handshakeLoopsFinished.Wait()
414+
c.closeLock.Lock()
415+
handshakeDone := c.handshakeDone
416+
c.closeLock.Unlock()
417+
if handshakeDone != nil {
418+
<-handshakeDone
419+
}
409420
return err
410421
}
411422

@@ -1026,7 +1037,6 @@ func (c *Conn) handshake(ctx context.Context, cfg *handshakeConfig, initialFligh
10261037

10271038
done := make(chan struct{})
10281039
ctxRead, cancelRead := context.WithCancel(context.Background())
1029-
c.cancelHandshakeReader = cancelRead
10301040
cfg.onFlightState = func(_ flightVal, s handshakeState) {
10311041
if s == handshakeFinished && !c.isHandshakeCompletedSuccessfully() {
10321042
c.setHandshakeCompletedSuccessfully()
@@ -1035,16 +1045,21 @@ func (c *Conn) handshake(ctx context.Context, cfg *handshakeConfig, initialFligh
10351045
}
10361046

10371047
ctxHs, cancel := context.WithCancel(context.Background())
1048+
1049+
c.closeLock.Lock()
10381050
c.cancelHandshaker = cancel
1051+
c.cancelHandshakeReader = cancelRead
1052+
c.closeLock.Unlock()
10391053

10401054
firstErr := make(chan error, 1)
10411055

1042-
c.handshakeLoopsFinished.Add(2)
1056+
var handshakeLoopsFinished sync.WaitGroup
1057+
handshakeLoopsFinished.Add(2)
10431058

10441059
// Handshake routine should be live until close.
10451060
// The other party may request retransmission of the last flight to cope with packet drop.
10461061
go func() {
1047-
defer c.handshakeLoopsFinished.Done()
1062+
defer handshakeLoopsFinished.Done()
10481063
err := c.fsm.Run(ctxHs, c, initialState)
10491064
if !errors.Is(err, context.Canceled) {
10501065
select {
@@ -1064,7 +1079,7 @@ func (c *Conn) handshake(ctx context.Context, cfg *handshakeConfig, initialFligh
10641079
// Force stop handshaker when the underlying connection is closed.
10651080
cancel()
10661081
}()
1067-
defer c.handshakeLoopsFinished.Done()
1082+
defer handshakeLoopsFinished.Done()
10681083
for {
10691084
if err := c.readAndBuffer(ctxRead); err != nil {
10701085
var e *alertError
@@ -1123,12 +1138,12 @@ func (c *Conn) handshake(ctx context.Context, cfg *handshakeConfig, initialFligh
11231138
case err := <-firstErr:
11241139
cancelRead()
11251140
cancel()
1126-
c.handshakeLoopsFinished.Wait()
1141+
handshakeLoopsFinished.Wait()
11271142
return c.translateHandshakeCtxError(err)
11281143
case <-ctx.Done():
11291144
cancelRead()
11301145
cancel()
1131-
c.handshakeLoopsFinished.Wait()
1146+
handshakeLoopsFinished.Wait()
11321147
return c.translateHandshakeCtxError(ctx.Err())
11331148
case <-done:
11341149
return nil
@@ -1146,8 +1161,13 @@ func (c *Conn) translateHandshakeCtxError(err error) error {
11461161
}
11471162

11481163
func (c *Conn) close(byUser bool) error {
1149-
c.cancelHandshaker()
1150-
c.cancelHandshakeReader()
1164+
c.closeLock.Lock()
1165+
cancelHandshaker := c.cancelHandshaker
1166+
cancelHandshakeReader := c.cancelHandshakeReader
1167+
c.closeLock.Unlock()
1168+
1169+
cancelHandshaker()
1170+
cancelHandshakeReader()
11511171

11521172
if c.isHandshakeCompletedSuccessfully() && byUser {
11531173
// Discard error from notify() to return non-error on the first user call of Close()

conn_test.go

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3662,3 +3662,54 @@ func TestMultiHandshake(t *testing.T) {
36623662
t.Fatal(err)
36633663
}
36643664
}
3665+
3666+
func TestCloseDuringHandshake(t *testing.T) {
3667+
defer test.CheckRoutines(t)()
3668+
defer test.TimeOut(time.Second * 10).Stop()
3669+
3670+
serverCert, err := selfsign.GenerateSelfSigned()
3671+
if err != nil {
3672+
t.Fatal(err)
3673+
}
3674+
3675+
for i := 0; i < 100; i++ {
3676+
_, cb := dpipe.Pipe()
3677+
server, err := Server(dtlsnet.PacketConnFromConn(cb), cb.RemoteAddr(), &Config{
3678+
Certificates: []tls.Certificate{serverCert},
3679+
})
3680+
if err != nil {
3681+
t.Fatal(err)
3682+
}
3683+
3684+
waitChan := make(chan struct{})
3685+
go func() {
3686+
close(waitChan)
3687+
_ = server.Handshake()
3688+
}()
3689+
3690+
<-waitChan
3691+
if err = server.Close(); err != nil {
3692+
t.Fatal(err)
3693+
}
3694+
}
3695+
}
3696+
3697+
func TestCloseWithoutHandshake(t *testing.T) {
3698+
defer test.CheckRoutines(t)()
3699+
defer test.TimeOut(time.Second * 10).Stop()
3700+
3701+
serverCert, err := selfsign.GenerateSelfSigned()
3702+
if err != nil {
3703+
t.Fatal(err)
3704+
}
3705+
_, cb := dpipe.Pipe()
3706+
server, err := Server(dtlsnet.PacketConnFromConn(cb), cb.RemoteAddr(), &Config{
3707+
Certificates: []tls.Certificate{serverCert},
3708+
})
3709+
if err != nil {
3710+
t.Fatal(err)
3711+
}
3712+
if err = server.Close(); err != nil {
3713+
t.Fatal(err)
3714+
}
3715+
}

0 commit comments

Comments
 (0)