Skip to content

Commit 4ef7588

Browse files
neildgopherbot
authored andcommitted
quic: handle ACK frame in packet which drops number space
Avoid incorrectly closing a connection with a protocol violation error when we receive a single packet containing a CRYPTO frame that causes us to drop a packet number space (forgetting what packet numbers we've sent in that space) followed by an ACK frame. Fixes golang/go#70703 Change-Id: I37554cb6a3086736cb9d772f8a3441b544d414dc Reviewed-on: https://go-review.googlesource.com/c/net/+/634616 LUCI-TryBot-Result: Go LUCI <[email protected]> Auto-Submit: Damien Neil <[email protected]> Reviewed-by: Jonathan Amsterdam <[email protected]>
1 parent 552d8ac commit 4ef7588

File tree

3 files changed

+72
-5
lines changed

3 files changed

+72
-5
lines changed

quic/conn_recv.go

+11
Original file line numberDiff line numberDiff line change
@@ -285,6 +285,7 @@ func (c *Conn) handleFrames(now time.Time, dgram *datagram, ptype packetType, sp
285285
__01 = packetType0RTT | packetType1RTT
286286
___1 = packetType1RTT
287287
)
288+
hasCrypto := false
288289
for len(payload) > 0 {
289290
switch payload[0] {
290291
case frameTypePadding, frameTypeAck, frameTypeAckECN,
@@ -322,6 +323,7 @@ func (c *Conn) handleFrames(now time.Time, dgram *datagram, ptype packetType, sp
322323
if !frameOK(c, ptype, IH_1) {
323324
return
324325
}
326+
hasCrypto = true
325327
n = c.handleCryptoFrame(now, space, payload)
326328
case frameTypeNewToken:
327329
if !frameOK(c, ptype, ___1) {
@@ -406,6 +408,15 @@ func (c *Conn) handleFrames(now time.Time, dgram *datagram, ptype packetType, sp
406408
}
407409
payload = payload[n:]
408410
}
411+
if hasCrypto {
412+
// Process TLS events after handling all frames in a packet.
413+
// TLS events can cause us to drop state for a number space,
414+
// so do that last, to avoid handling frames differently
415+
// depending on whether they come before or after a CRYPTO frame.
416+
if err := c.handleTLSEvents(now); err != nil {
417+
c.abort(now, err)
418+
}
419+
}
409420
return ackEliciting
410421
}
411422

quic/conn_recv_test.go

+60
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
// Copyright 2024 The Go Authors. All rights reserved.
2+
// Use of this source code is governed by a BSD-style
3+
// license that can be found in the LICENSE file.
4+
5+
//go:build go1.21
6+
7+
package quic
8+
9+
import (
10+
"crypto/tls"
11+
"testing"
12+
)
13+
14+
func TestConnReceiveAckForUnsentPacket(t *testing.T) {
15+
tc := newTestConn(t, serverSide, permissiveTransportParameters)
16+
tc.handshake()
17+
tc.writeFrames(packetType1RTT,
18+
debugFrameAck{
19+
ackDelay: 0,
20+
ranges: []i64range[packetNumber]{{0, 10}},
21+
})
22+
tc.wantFrame("ACK for unsent packet causes CONNECTION_CLOSE",
23+
packetType1RTT, debugFrameConnectionCloseTransport{
24+
code: errProtocolViolation,
25+
})
26+
}
27+
28+
// Issue #70703: If a packet contains both a CRYPTO frame which causes us to
29+
// drop state for a number space, and also contains a valid ACK frame for that space,
30+
// we shouldn't complain about the ACK.
31+
func TestConnReceiveAckForDroppedSpace(t *testing.T) {
32+
tc := newTestConn(t, serverSide, permissiveTransportParameters)
33+
tc.ignoreFrame(frameTypeAck)
34+
tc.ignoreFrame(frameTypeNewConnectionID)
35+
36+
tc.writeFrames(packetTypeInitial,
37+
debugFrameCrypto{
38+
data: tc.cryptoDataIn[tls.QUICEncryptionLevelInitial],
39+
})
40+
tc.wantFrame("send Initial crypto",
41+
packetTypeInitial, debugFrameCrypto{
42+
data: tc.cryptoDataOut[tls.QUICEncryptionLevelInitial],
43+
})
44+
tc.wantFrame("send Handshake crypto",
45+
packetTypeHandshake, debugFrameCrypto{
46+
data: tc.cryptoDataOut[tls.QUICEncryptionLevelHandshake],
47+
})
48+
49+
tc.writeFrames(packetTypeHandshake,
50+
debugFrameCrypto{
51+
data: tc.cryptoDataIn[tls.QUICEncryptionLevelHandshake],
52+
},
53+
debugFrameAck{
54+
ackDelay: 0,
55+
ranges: []i64range[packetNumber]{{0, tc.lastPacket.num + 1}},
56+
})
57+
tc.wantFrame("handshake finishes",
58+
packetType1RTT, debugFrameHandshakeDone{})
59+
tc.wantIdle("connection is idle")
60+
}

quic/tls.go

+1-5
Original file line numberDiff line numberDiff line change
@@ -119,11 +119,7 @@ func (c *Conn) handleCrypto(now time.Time, space numberSpace, off int64, data []
119119
default:
120120
return errors.New("quic: internal error: received CRYPTO frame in unexpected number space")
121121
}
122-
err := c.crypto[space].handleCrypto(off, data, func(b []byte) error {
122+
return c.crypto[space].handleCrypto(off, data, func(b []byte) error {
123123
return c.tls.HandleData(level, b)
124124
})
125-
if err != nil {
126-
return err
127-
}
128-
return c.handleTLSEvents(now)
129125
}

0 commit comments

Comments
 (0)