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.
Tento commit je obsažen v:
Filippo Valsorda 2016-12-05 17:11:46 -05:00 odevzdal Peter Wu
rodič 1b03258899
revize faefac5f1a
4 změnil soubory, kde provedl 65 přidání a 17 odebrání

9
13.go
Zobrazit soubor

@ -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()
}

Zobrazit soubor

@ -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

Zobrazit soubor

@ -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
Zobrazit soubor

@ -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()