Skip to content

Commit e5b1340

Browse files
minaevmikeFiloSottile
authored andcommitted
crypto/tls: fix deadlock when Read and Close called concurrently
The existing implementation of TLS connection has a deadlock. It occurs when client connects to TLS server and doesn't send data for handshake, so server calls Close on this connection. This is because server reads data under locked mutex, while Close method tries to lock the same mutex. Fixes #23518 Change-Id: I4fb0a2a770f3d911036bfd9a7da7cc41c1b27e19 Reviewed-on: https://go-review.googlesource.com/90155 Run-TryBot: Brad Fitzpatrick <bradfitz@golang.org> TryBot-Result: Gobot Gobot <gobot@golang.org> Reviewed-by: Filippo Valsorda <filippo@golang.org>
1 parent 7b46867 commit e5b1340

File tree

5 files changed

+60
-20
lines changed

5 files changed

+60
-20
lines changed

src/crypto/tls/conn.go

+19-18
Original file line numberDiff line numberDiff line change
@@ -27,15 +27,16 @@ type Conn struct {
2727
conn net.Conn
2828
isClient bool
2929

30+
// handshakeStatus is 1 if the connection is currently transferring
31+
// application data (i.e. is not currently processing a handshake).
32+
// This field is only to be accessed with sync/atomic.
33+
handshakeStatus uint32
3034
// constant after handshake; protected by handshakeMutex
3135
handshakeMutex sync.Mutex
3236
handshakeErr error // error resulting from handshake
3337
vers uint16 // TLS version
3438
haveVers bool // version has been negotiated
3539
config *Config // configuration passed to constructor
36-
// handshakeComplete is true if the connection is currently transferring
37-
// application data (i.e. is not currently processing a handshake).
38-
handshakeComplete bool
3940
// handshakes counts the number of handshakes performed on the
4041
// connection so far. If renegotiation is disabled then this is either
4142
// zero or one.
@@ -571,12 +572,12 @@ func (c *Conn) readRecord(want recordType) error {
571572
c.sendAlert(alertInternalError)
572573
return c.in.setErrorLocked(errors.New("tls: unknown record type requested"))
573574
case recordTypeHandshake, recordTypeChangeCipherSpec:
574-
if c.handshakeComplete {
575+
if c.handshakeComplete() {
575576
c.sendAlert(alertInternalError)
576577
return c.in.setErrorLocked(errors.New("tls: handshake or ChangeCipherSpec requested while not in handshake"))
577578
}
578579
case recordTypeApplicationData:
579-
if !c.handshakeComplete {
580+
if !c.handshakeComplete() {
580581
c.sendAlert(alertInternalError)
581582
return c.in.setErrorLocked(errors.New("tls: application data record requested while in handshake"))
582583
}
@@ -1048,7 +1049,7 @@ func (c *Conn) Write(b []byte) (int, error) {
10481049
return 0, err
10491050
}
10501051

1051-
if !c.handshakeComplete {
1052+
if !c.handshakeComplete() {
10521053
return 0, alertInternalError
10531054
}
10541055

@@ -1114,7 +1115,7 @@ func (c *Conn) handleRenegotiation() error {
11141115
c.handshakeMutex.Lock()
11151116
defer c.handshakeMutex.Unlock()
11161117

1117-
c.handshakeComplete = false
1118+
atomic.StoreUint32(&c.handshakeStatus, 0)
11181119
if c.handshakeErr = c.clientHandshake(); c.handshakeErr == nil {
11191120
c.handshakes++
11201121
}
@@ -1215,11 +1216,9 @@ func (c *Conn) Close() error {
12151216

12161217
var alertErr error
12171218

1218-
c.handshakeMutex.Lock()
1219-
if c.handshakeComplete {
1219+
if c.handshakeComplete() {
12201220
alertErr = c.closeNotify()
12211221
}
1222-
c.handshakeMutex.Unlock()
12231222

12241223
if err := c.conn.Close(); err != nil {
12251224
return err
@@ -1233,9 +1232,7 @@ var errEarlyCloseWrite = errors.New("tls: CloseWrite called before handshake com
12331232
// called once the handshake has completed and does not call CloseWrite on the
12341233
// underlying connection. Most callers should just use Close.
12351234
func (c *Conn) CloseWrite() error {
1236-
c.handshakeMutex.Lock()
1237-
defer c.handshakeMutex.Unlock()
1238-
if !c.handshakeComplete {
1235+
if !c.handshakeComplete() {
12391236
return errEarlyCloseWrite
12401237
}
12411238

@@ -1264,7 +1261,7 @@ func (c *Conn) Handshake() error {
12641261
if err := c.handshakeErr; err != nil {
12651262
return err
12661263
}
1267-
if c.handshakeComplete {
1264+
if c.handshakeComplete() {
12681265
return nil
12691266
}
12701267

@@ -1284,7 +1281,7 @@ func (c *Conn) Handshake() error {
12841281
c.flush()
12851282
}
12861283

1287-
if c.handshakeErr == nil && !c.handshakeComplete {
1284+
if c.handshakeErr == nil && !c.handshakeComplete() {
12881285
panic("handshake should have had a result.")
12891286
}
12901287

@@ -1297,10 +1294,10 @@ func (c *Conn) ConnectionState() ConnectionState {
12971294
defer c.handshakeMutex.Unlock()
12981295

12991296
var state ConnectionState
1300-
state.HandshakeComplete = c.handshakeComplete
1297+
state.HandshakeComplete = c.handshakeComplete()
13011298
state.ServerName = c.serverName
13021299

1303-
if c.handshakeComplete {
1300+
if state.HandshakeComplete {
13041301
state.Version = c.vers
13051302
state.NegotiatedProtocol = c.clientProtocol
13061303
state.DidResume = c.didResume
@@ -1345,11 +1342,15 @@ func (c *Conn) VerifyHostname(host string) error {
13451342
if !c.isClient {
13461343
return errors.New("tls: VerifyHostname called on TLS server connection")
13471344
}
1348-
if !c.handshakeComplete {
1345+
if !c.handshakeComplete() {
13491346
return errors.New("tls: handshake has not yet been performed")
13501347
}
13511348
if len(c.verifiedChains) == 0 {
13521349
return errors.New("tls: handshake did not verify certificate chain")
13531350
}
13541351
return c.peerCertificates[0].VerifyHostname(host)
13551352
}
1353+
1354+
func (c *Conn) handshakeComplete() bool {
1355+
return atomic.LoadUint32(&c.handshakeStatus) == 1
1356+
}

src/crypto/tls/handshake_client.go

+2-1
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ import (
1717
"net"
1818
"strconv"
1919
"strings"
20+
"sync/atomic"
2021
)
2122

2223
type clientHandshakeState struct {
@@ -266,7 +267,7 @@ func (hs *clientHandshakeState) handshake() error {
266267

267268
c.ekm = ekmFromMasterSecret(c.vers, hs.suite, hs.masterSecret, hs.hello.random, hs.serverHello.random)
268269
c.didResume = isResume
269-
c.handshakeComplete = true
270+
atomic.StoreUint32(&c.handshakeStatus, 1)
270271

271272
return nil
272273
}

src/crypto/tls/handshake_client_test.go

+19
Original file line numberDiff line numberDiff line change
@@ -1617,3 +1617,22 @@ RwBA9Xk1KBNF
16171617
t.Error("A RSA-PSS certificate was parsed like a PKCS1 one, and it will be mistakenly used with rsa_pss_rsae_xxx signature algorithms")
16181618
}
16191619
}
1620+
1621+
func TestCloseClientConnectionOnIdleServer(t *testing.T) {
1622+
clientConn, serverConn := net.Pipe()
1623+
client := Client(clientConn, testConfig.Clone())
1624+
go func() {
1625+
var b [1]byte
1626+
serverConn.Read(b[:])
1627+
client.Close()
1628+
}()
1629+
client.SetWriteDeadline(time.Now().Add(time.Second))
1630+
err := client.Handshake()
1631+
if err != nil {
1632+
if !strings.Contains(err.Error(), "read/write on closed pipe") {
1633+
t.Errorf("Error expected containing 'read/write on closed pipe' but got '%s'", err.Error())
1634+
}
1635+
} else {
1636+
t.Errorf("Error expected, but no error returned")
1637+
}
1638+
}

src/crypto/tls/handshake_server.go

+2-1
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import (
1313
"errors"
1414
"fmt"
1515
"io"
16+
"sync/atomic"
1617
)
1718

1819
// serverHandshakeState contains details of a server handshake in progress.
@@ -103,7 +104,7 @@ func (c *Conn) serverHandshake() error {
103104
}
104105

105106
c.ekm = ekmFromMasterSecret(c.vers, hs.suite, hs.masterSecret, hs.clientHello.random, hs.hello.random)
106-
c.handshakeComplete = true
107+
atomic.StoreUint32(&c.handshakeStatus, 1)
107108

108109
return nil
109110
}

src/crypto/tls/handshake_server_test.go

+18
Original file line numberDiff line numberDiff line change
@@ -1403,3 +1403,21 @@ var testECDSAPrivateKey = &ecdsa.PrivateKey{
14031403
}
14041404

14051405
var testP256PrivateKey, _ = x509.ParseECPrivateKey(fromHex("30770201010420012f3b52bc54c36ba3577ad45034e2e8efe1e6999851284cb848725cfe029991a00a06082a8648ce3d030107a14403420004c02c61c9b16283bbcc14956d886d79b358aa614596975f78cece787146abf74c2d5dc578c0992b4f3c631373479ebf3892efe53d21c4f4f1cc9a11c3536b7f75"))
1406+
1407+
func TestCloseServerConnectionOnIdleClient(t *testing.T) {
1408+
clientConn, serverConn := net.Pipe()
1409+
server := Server(serverConn, testConfig.Clone())
1410+
go func() {
1411+
clientConn.Write([]byte{'0'})
1412+
server.Close()
1413+
}()
1414+
server.SetReadDeadline(time.Now().Add(time.Second))
1415+
err := server.Handshake()
1416+
if err != nil {
1417+
if !strings.Contains(err.Error(), "read/write on closed pipe") {
1418+
t.Errorf("Error expected containing 'read/write on closed pipe' but got '%s'", err.Error())
1419+
}
1420+
} else {
1421+
t.Errorf("Error expected, but no error returned")
1422+
}
1423+
}

0 commit comments

Comments
 (0)