forked from quic-go/quic-go
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:
38
session.go
38
session.go
@@ -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 {
|
||||
|
||||
Reference in New Issue
Block a user