fix race condition when the handshake completes

When the handshake completes, the crypto setup closes the handshakeEvent
channel. We need to make sure that the session run loop immediately
selects this case, and not any other case (especially the packet
reception case). Otherwise, the server crypto setup will deadlock,
because the sentSHLO channel won't be closed, and opening of
forward-secure packets will block indefinitely on that channel.
This commit is contained in:
Marten Seemann
2018-04-16 13:36:06 +09:00
parent ef286afa3c
commit a23c94dd87

View File

@@ -422,7 +422,6 @@ func (s *session) run() error {
}()
var closeErr closeError
handshakeEvent := s.handshakeEvent
runLoop:
for {
@@ -431,6 +430,9 @@ runLoop:
select {
case closeErr = <-s.closeChan:
break runLoop
case _, ok := <-s.handshakeEvent:
// when the handshake is completed, the channel will be closed
s.handleHandshakeEvent(!ok)
default:
}
@@ -461,20 +463,9 @@ runLoop:
putPacketBuffer(&p.header.Raw)
case p := <-s.paramsChan:
s.processTransportParameters(&p)
case _, ok := <-handshakeEvent:
if !ok { // the aeadChanged chan was closed. This means that the handshake is completed.
s.handshakeComplete = true
handshakeEvent = nil // prevent this case from ever being selected again
if !s.version.UsesTLS() && s.perspective == protocol.PerspectiveClient {
// In gQUIC, there's no equivalent to the Finished message in TLS
// The server knows that the handshake is complete when it receives the first forward-secure packet sent by the client.
// We need to make sure that the client actually sends such a packet.
s.packer.QueueControlFrame(&wire.PingFrame{})
}
close(s.handshakeChan)
} else {
s.tryDecryptingQueuedPackets()
}
case _, ok := <-s.handshakeEvent:
// when the handshake is completed, the channel will be closed
s.handleHandshakeEvent(!ok)
}
now := time.Now()
@@ -562,6 +553,23 @@ func (s *session) maybeResetTimer() {
s.timer.Reset(deadline)
}
func (s *session) handleHandshakeEvent(completed bool) {
if !completed {
s.tryDecryptingQueuedPackets()
return
}
s.handshakeComplete = true
s.handshakeEvent = nil // prevent this case from ever being selected again
if !s.version.UsesTLS() && s.perspective == protocol.PerspectiveClient {
// In gQUIC, there's no equivalent to the Finished message in TLS
// The server knows that the handshake is complete when it receives the first forward-secure packet sent by the client.
// We need to make sure that the client actually sends such a packet.
s.packer.QueueControlFrame(&wire.PingFrame{})
s.scheduleSending()
}
close(s.handshakeChan)
}
func (s *session) handlePacketImpl(p *receivedPacket) error {
if s.perspective == protocol.PerspectiveClient {
if divNonce := p.header.DiversificationNonce; len(divNonce) > 0 {