fix dequeuing logic for tiny CRYPTO frames (#5104)

For very small sizes, cryptoStream.PopCryptoStream could
have returned CRYPTO frames larger than the requested size.

Instead, it should return a nil frame.
This commit is contained in:
Marten Seemann
2025-05-02 00:35:11 +08:00
committed by GitHub
parent 9d06679273
commit d7b8447e0e
4 changed files with 20 additions and 8 deletions

View File

@@ -826,7 +826,9 @@ func (s *connection) handleHandshakeComplete(now time.Time) error {
if ticket != nil { // may be nil if session tickets are disabled via tls.Config.SessionTicketsDisabled
s.oneRTTStream.Write(ticket)
for s.oneRTTStream.HasData() {
s.queueControlFrame(s.oneRTTStream.PopCryptoFrame(protocol.MaxPostHandshakeCryptoFrameSize))
if cf := s.oneRTTStream.PopCryptoFrame(protocol.MaxPostHandshakeCryptoFrameSize); cf != nil {
s.queueControlFrame(cf)
}
}
}
token, err := s.tokenGenerator.NewToken(s.conn.RemoteAddr())

View File

@@ -76,6 +76,9 @@ func (s *cryptoStream) HasData() bool {
func (s *cryptoStream) PopCryptoFrame(maxLen protocol.ByteCount) *wire.CryptoFrame {
f := &wire.CryptoFrame{Offset: s.writeOffset}
n := min(f.MaxDataLen(maxLen), protocol.ByteCount(len(s.writeBuf)))
if n == 0 {
return nil
}
f.Data = s.writeBuf[:n]
s.writeBuf = s.writeBuf[n:]
s.writeOffset += n

View File

@@ -91,11 +91,17 @@ func TestCryptoStreamWrite(t *testing.T) {
require.NoError(t, err)
require.True(t, str.HasData())
f := str.PopCryptoFrame(expectedCryptoFrameLen(0) + 3)
require.Equal(t, &wire.CryptoFrame{Data: []byte("foo")}, f)
for i := range expectedCryptoFrameLen(0) {
require.Nil(t, str.PopCryptoFrame(i))
}
f := str.PopCryptoFrame(expectedCryptoFrameLen(0) + 1)
require.Equal(t, &wire.CryptoFrame{Data: []byte("f")}, f)
require.True(t, str.HasData())
f = str.PopCryptoFrame(expectedCryptoFrameLen(1) + 3)
// the three write calls were coalesced into a single frame
require.Equal(t, &wire.CryptoFrame{Offset: 1, Data: []byte("oob")}, f)
f = str.PopCryptoFrame(protocol.MaxByteCount)
// the two write calls were coalesced into a single frame
require.Equal(t, &wire.CryptoFrame{Offset: 3, Data: []byte("barbaz")}, f)
require.Equal(t, &wire.CryptoFrame{Offset: 4, Data: []byte("arbaz")}, f)
require.False(t, str.HasData())
}

View File

@@ -561,9 +561,10 @@ func (p *packetPacker) maybeGetCryptoPacket(
maxPacketSize -= frameLen
}
} else if s.HasData() {
cf := s.PopCryptoFrame(maxPacketSize)
pl.frames = append(pl.frames, ackhandler.Frame{Frame: cf, Handler: handler})
pl.length += cf.Length(v)
if cf := s.PopCryptoFrame(maxPacketSize); cf != nil {
pl.frames = append(pl.frames, ackhandler.Frame{Frame: cf, Handler: handler})
pl.length += cf.Length(v)
}
}
return hdr, pl
}