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.
Šī revīzija ir iekļauta:
vecāks
1b03258899
revīzija
faefac5f1a
9
13.go
9
13.go
@ -157,10 +157,15 @@ func (hs *serverHandshakeState) readClientFinished13() error {
|
||||
hs.finishedHash13.Write(clientFinished.marshal())
|
||||
|
||||
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.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()
|
||||
}
|
||||
|
@ -30,6 +30,6 @@ elif [ "$1" = "0-RTT" ]; then
|
||||
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
|
||||
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
|
||||
|
@ -52,20 +52,34 @@ func main() {
|
||||
http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
|
||||
tlsConn := r.Context().Value(http.TLSConnContextKey).(*tls.Conn)
|
||||
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 := ""
|
||||
if !tlsConn.ConnectionState().HandshakeConfirmed {
|
||||
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) {
|
||||
|
37
conn.go
37
conn.go
@ -30,6 +30,8 @@ type Conn struct {
|
||||
phase handshakeStatus // protected by in.Mutex
|
||||
// handshakeConfirmed is an atomic bool for phase == handshakeConfirmed
|
||||
handshakeConfirmed int32
|
||||
// confirmMutex is held by any read operation before handshakeConfirmed
|
||||
confirmMutex sync.Mutex
|
||||
|
||||
// constant after handshake; protected by handshakeMutex
|
||||
handshakeMutex sync.Mutex // handshakeMutex < in.Mutex, out.Mutex, errMutex
|
||||
@ -1254,13 +1256,27 @@ func (c *Conn) ConfirmHandshake() error {
|
||||
return err
|
||||
}
|
||||
|
||||
c.in.Lock()
|
||||
defer c.in.Unlock()
|
||||
|
||||
if c.phase == handshakeConfirmed {
|
||||
if c.vers < VersionTLS13 {
|
||||
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
|
||||
if c.phase == readingEarlyData || c.input != nil {
|
||||
buf := &bytes.Buffer{}
|
||||
@ -1341,6 +1357,19 @@ func (c *Conn) Read(b []byte) (n int, err error) {
|
||||
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()
|
||||
defer c.in.Unlock()
|
||||
|
||||
|
Notiek ielāde…
Atsaukties uz šo jaunā problēmā
Block a user