crypto/tls: stop ConfirmHandshake from locking on any Read
ConfirmHandshake should block on a Read until the handshakeConfirmed state is reached, but past that it shouldn't.
This commit is contained in:
parent
1b03258899
commit
faefac5f1a
9
13.go
9
13.go
@ -157,10 +157,15 @@ func (hs *serverHandshakeState) readClientFinished13() error {
|
|||||||
hs.finishedHash13.Write(clientFinished.marshal())
|
hs.finishedHash13.Write(clientFinished.marshal())
|
||||||
|
|
||||||
c.hs = nil // Discard the server handshake state
|
c.hs = nil // Discard the server handshake state
|
||||||
c.phase = handshakeConfirmed
|
|
||||||
atomic.StoreInt32(&c.handshakeConfirmed, 1)
|
|
||||||
c.in.setCipher(c.vers, hs.appClientCipher)
|
c.in.setCipher(c.vers, hs.appClientCipher)
|
||||||
c.in.traceErr, c.out.traceErr = nil, nil
|
c.in.traceErr, c.out.traceErr = nil, nil
|
||||||
|
c.phase = handshakeConfirmed
|
||||||
|
atomic.StoreInt32(&c.handshakeConfirmed, 1)
|
||||||
|
|
||||||
|
// Any read operation after handshakeRunning and before handshakeConfirmed
|
||||||
|
// will be holding this lock, which we release as soon as the confirmation
|
||||||
|
// happens, even if the Read call might do more work.
|
||||||
|
c.confirmMutex.Unlock()
|
||||||
|
|
||||||
return hs.sendSessionTicket13()
|
return hs.sendSessionTicket13()
|
||||||
}
|
}
|
||||||
|
@ -30,6 +30,6 @@ elif [ "$1" = "0-RTT" ]; then
|
|||||||
grep "Hello TLS 1.3" output.txt | grep "resumed" | grep "0-RTT"
|
grep "Hello TLS 1.3" output.txt | grep "resumed" | grep "0-RTT"
|
||||||
|
|
||||||
docker run --rm tls-tris:$2 $IP:5443 | tee output.txt # confirming 0-RTT
|
docker run --rm tls-tris:$2 $IP:5443 | tee output.txt # confirming 0-RTT
|
||||||
grep "Hello TLS 1.3" output.txt | grep "resumed" | grep -v "0-RTT"
|
grep "Hello TLS 1.3" output.txt | grep "resumed" | grep "0-RTT confirmed"
|
||||||
|
|
||||||
fi
|
fi
|
||||||
|
@ -52,20 +52,34 @@ func main() {
|
|||||||
http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
|
http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
|
||||||
tlsConn := r.Context().Value(http.TLSConnContextKey).(*tls.Conn)
|
tlsConn := r.Context().Value(http.TLSConnContextKey).(*tls.Conn)
|
||||||
server := r.Context().Value(http.ServerContextKey).(*http.Server)
|
server := r.Context().Value(http.ServerContextKey).(*http.Server)
|
||||||
if server.Addr == confirmingAddr {
|
|
||||||
if err := tlsConn.ConfirmHandshake(); err != nil {
|
|
||||||
log.Fatal(err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
resumed := ""
|
|
||||||
if r.TLS.DidResume {
|
|
||||||
resumed = " [resumed]"
|
|
||||||
}
|
|
||||||
with0RTT := ""
|
with0RTT := ""
|
||||||
if !tlsConn.ConnectionState().HandshakeConfirmed {
|
if !tlsConn.ConnectionState().HandshakeConfirmed {
|
||||||
with0RTT = " [0-RTT]"
|
with0RTT = " [0-RTT]"
|
||||||
}
|
}
|
||||||
fmt.Fprintf(w, "<!DOCTYPE html><p>Hello TLS %s%s%s _o/\n", tlsVersionToName[r.TLS.Version], resumed, with0RTT)
|
if server.Addr == confirmingAddr || r.URL.Path == "/confirm" {
|
||||||
|
if err := tlsConn.ConfirmHandshake(); err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
if with0RTT != "" {
|
||||||
|
with0RTT = " [0-RTT confirmed]"
|
||||||
|
}
|
||||||
|
if !tlsConn.ConnectionState().HandshakeConfirmed {
|
||||||
|
panic("HandshakeConfirmed false after ConfirmHandshake")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
resumed := ""
|
||||||
|
if r.TLS.DidResume {
|
||||||
|
resumed = " [resumed]"
|
||||||
|
}
|
||||||
|
|
||||||
|
http2 := ""
|
||||||
|
if r.ProtoMajor == 2 {
|
||||||
|
http2 = " [HTTP/2]"
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Fprintf(w, "<!DOCTYPE html><p>Hello TLS %s%s%s%s _o/\n", tlsVersionToName[r.TLS.Version], resumed, with0RTT, http2)
|
||||||
})
|
})
|
||||||
|
|
||||||
http.HandleFunc("/ch", func(w http.ResponseWriter, r *http.Request) {
|
http.HandleFunc("/ch", func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
37
conn.go
37
conn.go
@ -30,6 +30,8 @@ type Conn struct {
|
|||||||
phase handshakeStatus // protected by in.Mutex
|
phase handshakeStatus // protected by in.Mutex
|
||||||
// handshakeConfirmed is an atomic bool for phase == handshakeConfirmed
|
// handshakeConfirmed is an atomic bool for phase == handshakeConfirmed
|
||||||
handshakeConfirmed int32
|
handshakeConfirmed int32
|
||||||
|
// confirmMutex is held by any read operation before handshakeConfirmed
|
||||||
|
confirmMutex sync.Mutex
|
||||||
|
|
||||||
// constant after handshake; protected by handshakeMutex
|
// constant after handshake; protected by handshakeMutex
|
||||||
handshakeMutex sync.Mutex // handshakeMutex < in.Mutex, out.Mutex, errMutex
|
handshakeMutex sync.Mutex // handshakeMutex < in.Mutex, out.Mutex, errMutex
|
||||||
@ -1254,13 +1256,27 @@ func (c *Conn) ConfirmHandshake() error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
c.in.Lock()
|
if c.vers < VersionTLS13 {
|
||||||
defer c.in.Unlock()
|
|
||||||
|
|
||||||
if c.phase == handshakeConfirmed {
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
c.confirmMutex.Lock()
|
||||||
|
if atomic.LoadInt32(&c.handshakeConfirmed) == 1 { // c.phase == handshakeConfirmed
|
||||||
|
c.confirmMutex.Unlock()
|
||||||
|
return nil
|
||||||
|
} else {
|
||||||
|
defer func() {
|
||||||
|
// If we transitioned to handshakeConfirmed we already released the lock,
|
||||||
|
// otherwise do it here.
|
||||||
|
if c.phase != handshakeConfirmed {
|
||||||
|
c.confirmMutex.Unlock()
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
}
|
||||||
|
|
||||||
|
c.in.Lock()
|
||||||
|
defer c.in.Unlock()
|
||||||
|
|
||||||
var input *block
|
var input *block
|
||||||
if c.phase == readingEarlyData || c.input != nil {
|
if c.phase == readingEarlyData || c.input != nil {
|
||||||
buf := &bytes.Buffer{}
|
buf := &bytes.Buffer{}
|
||||||
@ -1341,6 +1357,19 @@ func (c *Conn) Read(b []byte) (n int, err error) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
c.confirmMutex.Lock()
|
||||||
|
if atomic.LoadInt32(&c.handshakeConfirmed) == 1 { // c.phase == handshakeConfirmed
|
||||||
|
c.confirmMutex.Unlock()
|
||||||
|
} else {
|
||||||
|
defer func() {
|
||||||
|
// If we transitioned to handshakeConfirmed we already released the lock,
|
||||||
|
// otherwise do it here.
|
||||||
|
if c.phase != handshakeConfirmed {
|
||||||
|
c.confirmMutex.Unlock()
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
}
|
||||||
|
|
||||||
c.in.Lock()
|
c.in.Lock()
|
||||||
defer c.in.Unlock()
|
defer c.in.Unlock()
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user