diff --git a/fuzzing/frames/corpus/multiple-frames-0 b/fuzzing/frames/corpus/multiple-frames-0 index 1371b452..d707df21 100644 Binary files a/fuzzing/frames/corpus/multiple-frames-0 and b/fuzzing/frames/corpus/multiple-frames-0 differ diff --git a/fuzzing/frames/corpus/multiple-frames-1 b/fuzzing/frames/corpus/multiple-frames-1 index 9cb23807..83615ab1 100644 Binary files a/fuzzing/frames/corpus/multiple-frames-1 and b/fuzzing/frames/corpus/multiple-frames-1 differ diff --git a/fuzzing/frames/corpus/multiple-frames-10 b/fuzzing/frames/corpus/multiple-frames-10 index a264d1b8..455886f4 100644 Binary files a/fuzzing/frames/corpus/multiple-frames-10 and b/fuzzing/frames/corpus/multiple-frames-10 differ diff --git a/fuzzing/frames/corpus/multiple-frames-11 b/fuzzing/frames/corpus/multiple-frames-11 index 583723ee..92f19930 100644 Binary files a/fuzzing/frames/corpus/multiple-frames-11 and b/fuzzing/frames/corpus/multiple-frames-11 differ diff --git a/fuzzing/frames/corpus/multiple-frames-12 b/fuzzing/frames/corpus/multiple-frames-12 index f4aa432f..9d659107 100644 Binary files a/fuzzing/frames/corpus/multiple-frames-12 and b/fuzzing/frames/corpus/multiple-frames-12 differ diff --git a/fuzzing/frames/corpus/multiple-frames-13 b/fuzzing/frames/corpus/multiple-frames-13 index d2e06b87..126a23c4 100644 Binary files a/fuzzing/frames/corpus/multiple-frames-13 and b/fuzzing/frames/corpus/multiple-frames-13 differ diff --git a/fuzzing/frames/corpus/multiple-frames-14 b/fuzzing/frames/corpus/multiple-frames-14 index e87b05ae..fd35b2c7 100644 Binary files a/fuzzing/frames/corpus/multiple-frames-14 and b/fuzzing/frames/corpus/multiple-frames-14 differ diff --git a/fuzzing/frames/corpus/multiple-frames-15 b/fuzzing/frames/corpus/multiple-frames-15 index 882e74d5..393a018d 100644 Binary files a/fuzzing/frames/corpus/multiple-frames-15 and b/fuzzing/frames/corpus/multiple-frames-15 differ diff --git a/fuzzing/frames/corpus/multiple-frames-16 b/fuzzing/frames/corpus/multiple-frames-16 index c9dc68b6..882f073c 100644 Binary files a/fuzzing/frames/corpus/multiple-frames-16 and b/fuzzing/frames/corpus/multiple-frames-16 differ diff --git a/fuzzing/frames/corpus/multiple-frames-17 b/fuzzing/frames/corpus/multiple-frames-17 index 05840c0c..39d2b642 100644 Binary files a/fuzzing/frames/corpus/multiple-frames-17 and b/fuzzing/frames/corpus/multiple-frames-17 differ diff --git a/fuzzing/frames/corpus/multiple-frames-18 b/fuzzing/frames/corpus/multiple-frames-18 index 6b0411cb..e9e4006a 100644 Binary files a/fuzzing/frames/corpus/multiple-frames-18 and b/fuzzing/frames/corpus/multiple-frames-18 differ diff --git a/fuzzing/frames/corpus/multiple-frames-19 b/fuzzing/frames/corpus/multiple-frames-19 index d26e7929..5bcf4fec 100644 Binary files a/fuzzing/frames/corpus/multiple-frames-19 and b/fuzzing/frames/corpus/multiple-frames-19 differ diff --git a/fuzzing/frames/corpus/multiple-frames-2 b/fuzzing/frames/corpus/multiple-frames-2 index 1e46b2ec..ffcf8bfd 100644 Binary files a/fuzzing/frames/corpus/multiple-frames-2 and b/fuzzing/frames/corpus/multiple-frames-2 differ diff --git a/fuzzing/frames/corpus/multiple-frames-20 b/fuzzing/frames/corpus/multiple-frames-20 index 2a0ab65d..062c3a54 100644 Binary files a/fuzzing/frames/corpus/multiple-frames-20 and b/fuzzing/frames/corpus/multiple-frames-20 differ diff --git a/fuzzing/frames/corpus/multiple-frames-21 b/fuzzing/frames/corpus/multiple-frames-21 index b5853681..acc6f8dd 100644 Binary files a/fuzzing/frames/corpus/multiple-frames-21 and b/fuzzing/frames/corpus/multiple-frames-21 differ diff --git a/fuzzing/frames/corpus/multiple-frames-22 b/fuzzing/frames/corpus/multiple-frames-22 index 0931891f..80fe82c7 100644 Binary files a/fuzzing/frames/corpus/multiple-frames-22 and b/fuzzing/frames/corpus/multiple-frames-22 differ diff --git a/fuzzing/frames/corpus/multiple-frames-23 b/fuzzing/frames/corpus/multiple-frames-23 index ba8e0692..ffec7f02 100644 Binary files a/fuzzing/frames/corpus/multiple-frames-23 and b/fuzzing/frames/corpus/multiple-frames-23 differ diff --git a/fuzzing/frames/corpus/multiple-frames-24 b/fuzzing/frames/corpus/multiple-frames-24 index 57e534b7..177bf663 100644 Binary files a/fuzzing/frames/corpus/multiple-frames-24 and b/fuzzing/frames/corpus/multiple-frames-24 differ diff --git a/fuzzing/frames/corpus/multiple-frames-3 b/fuzzing/frames/corpus/multiple-frames-3 index e425cdae..a1ea7e51 100644 Binary files a/fuzzing/frames/corpus/multiple-frames-3 and b/fuzzing/frames/corpus/multiple-frames-3 differ diff --git a/fuzzing/frames/corpus/multiple-frames-4 b/fuzzing/frames/corpus/multiple-frames-4 index 1e413072..020a2bc0 100644 Binary files a/fuzzing/frames/corpus/multiple-frames-4 and b/fuzzing/frames/corpus/multiple-frames-4 differ diff --git a/fuzzing/frames/corpus/multiple-frames-5 b/fuzzing/frames/corpus/multiple-frames-5 index c2253045..0d50411a 100644 Binary files a/fuzzing/frames/corpus/multiple-frames-5 and b/fuzzing/frames/corpus/multiple-frames-5 differ diff --git a/fuzzing/frames/corpus/multiple-frames-6 b/fuzzing/frames/corpus/multiple-frames-6 index 8d62efe3..6debd4cc 100644 Binary files a/fuzzing/frames/corpus/multiple-frames-6 and b/fuzzing/frames/corpus/multiple-frames-6 differ diff --git a/fuzzing/frames/corpus/multiple-frames-7 b/fuzzing/frames/corpus/multiple-frames-7 index 66e20ecb..898b19f1 100644 Binary files a/fuzzing/frames/corpus/multiple-frames-7 and b/fuzzing/frames/corpus/multiple-frames-7 differ diff --git a/fuzzing/frames/corpus/multiple-frames-8 b/fuzzing/frames/corpus/multiple-frames-8 index c7621143..b62d3ea8 100644 Binary files a/fuzzing/frames/corpus/multiple-frames-8 and b/fuzzing/frames/corpus/multiple-frames-8 differ diff --git a/fuzzing/frames/corpus/multiple-frames-9 b/fuzzing/frames/corpus/multiple-frames-9 index a49da679..e3dea7d2 100644 Binary files a/fuzzing/frames/corpus/multiple-frames-9 and b/fuzzing/frames/corpus/multiple-frames-9 differ diff --git a/fuzzing/frames/corpus/single-frame-0 b/fuzzing/frames/corpus/single-frame-0 index bd55da9b..a40ba96c 100644 --- a/fuzzing/frames/corpus/single-frame-0 +++ b/fuzzing/frames/corpus/single-frame-0 @@ -1 +1 @@ -s  \ No newline at end of file +  \ No newline at end of file diff --git a/fuzzing/frames/corpus/single-frame-1 b/fuzzing/frames/corpus/single-frame-1 index 5b360e5a..9fc0410e 100644 --- a/fuzzing/frames/corpus/single-frame-1 +++ b/fuzzing/frames/corpus/single-frame-1 @@ -1 +1 @@ - ݝRכMvBaz ;[ L5pX?au)ڞoNtKoLܺ>>OTm.|UyϢǎ \ No newline at end of file +d ݝRכMvBaz ;[ L5pX?au)ڞoNtKoLܺ>>OTm.|UyϢǎ \ No newline at end of file diff --git a/fuzzing/frames/corpus/single-frame-10 b/fuzzing/frames/corpus/single-frame-10 index 777d7232..6e7e4a7b 100644 --- a/fuzzing/frames/corpus/single-frame-10 +++ b/fuzzing/frames/corpus/single-frame-10 @@ -1 +1 @@ -74 \ No newline at end of file +W \ No newline at end of file diff --git a/fuzzing/frames/corpus/single-frame-11 b/fuzzing/frames/corpus/single-frame-11 index 8acb1d44..54aa55a3 100644 --- a/fuzzing/frames/corpus/single-frame-11 +++ b/fuzzing/frames/corpus/single-frame-11 @@ -1 +1 @@ -z \ No newline at end of file +fLlQ5 \ No newline at end of file diff --git a/fuzzing/frames/corpus/single-frame-12 b/fuzzing/frames/corpus/single-frame-12 index a2efe6f2..9ca1eec6 100644 Binary files a/fuzzing/frames/corpus/single-frame-12 and b/fuzzing/frames/corpus/single-frame-12 differ diff --git a/fuzzing/frames/corpus/single-frame-13 b/fuzzing/frames/corpus/single-frame-13 index 8c8103d2..d432b3ee 100644 Binary files a/fuzzing/frames/corpus/single-frame-13 and b/fuzzing/frames/corpus/single-frame-13 differ diff --git a/fuzzing/frames/corpus/single-frame-14 b/fuzzing/frames/corpus/single-frame-14 index ac664285..0729a1b1 100644 Binary files a/fuzzing/frames/corpus/single-frame-14 and b/fuzzing/frames/corpus/single-frame-14 differ diff --git a/fuzzing/frames/corpus/single-frame-15 b/fuzzing/frames/corpus/single-frame-15 index 20357ea4..33279a57 100644 Binary files a/fuzzing/frames/corpus/single-frame-15 and b/fuzzing/frames/corpus/single-frame-15 differ diff --git a/fuzzing/frames/corpus/single-frame-16 b/fuzzing/frames/corpus/single-frame-16 index 1aaa6aeb..06e4595b 100644 --- a/fuzzing/frames/corpus/single-frame-16 +++ b/fuzzing/frames/corpus/single-frame-16 @@ -1 +1,2 @@ -?lQ \ No newline at end of file +6 +u*.dh \ No newline at end of file diff --git a/fuzzing/frames/corpus/single-frame-17 b/fuzzing/frames/corpus/single-frame-17 index 11307256..7f4a14df 100644 --- a/fuzzing/frames/corpus/single-frame-17 +++ b/fuzzing/frames/corpus/single-frame-17 @@ -1 +1 @@ - \ No newline at end of file +wK \ No newline at end of file diff --git a/fuzzing/frames/corpus/single-frame-18 b/fuzzing/frames/corpus/single-frame-18 index ae2947e7..44966aba 100644 --- a/fuzzing/frames/corpus/single-frame-18 +++ b/fuzzing/frames/corpus/single-frame-18 @@ -1 +1 @@ -5M \ No newline at end of file + \ No newline at end of file diff --git a/fuzzing/frames/corpus/single-frame-19 b/fuzzing/frames/corpus/single-frame-19 index a25478c9..b5b22d77 100644 --- a/fuzzing/frames/corpus/single-frame-19 +++ b/fuzzing/frames/corpus/single-frame-19 @@ -1 +1 @@ - p \ No newline at end of file +H \ No newline at end of file diff --git a/fuzzing/frames/corpus/single-frame-2 b/fuzzing/frames/corpus/single-frame-2 index 8491c1ca..b8663b66 100644 Binary files a/fuzzing/frames/corpus/single-frame-2 and b/fuzzing/frames/corpus/single-frame-2 differ diff --git a/fuzzing/frames/corpus/single-frame-20 b/fuzzing/frames/corpus/single-frame-20 index 305b8638..6e239ffa 100644 --- a/fuzzing/frames/corpus/single-frame-20 +++ b/fuzzing/frames/corpus/single-frame-20 @@ -1 +1 @@ -M \ No newline at end of file +u \ No newline at end of file diff --git a/fuzzing/frames/corpus/single-frame-21 b/fuzzing/frames/corpus/single-frame-21 index c0c75efd..0fd29056 100644 --- a/fuzzing/frames/corpus/single-frame-21 +++ b/fuzzing/frames/corpus/single-frame-21 @@ -1 +1 @@ -/' \ No newline at end of file +ZG& \ No newline at end of file diff --git a/fuzzing/frames/corpus/single-frame-22 b/fuzzing/frames/corpus/single-frame-22 index 9d97cd76..3ce1160d 100644 --- a/fuzzing/frames/corpus/single-frame-22 +++ b/fuzzing/frames/corpus/single-frame-22 @@ -1 +1 @@ -R \ No newline at end of file +` \ No newline at end of file diff --git a/fuzzing/frames/corpus/single-frame-23 b/fuzzing/frames/corpus/single-frame-23 index 283d7a8d..cd9e99e6 100644 --- a/fuzzing/frames/corpus/single-frame-23 +++ b/fuzzing/frames/corpus/single-frame-23 @@ -1 +1 @@ -P \ No newline at end of file + \ No newline at end of file diff --git a/fuzzing/frames/corpus/single-frame-24 b/fuzzing/frames/corpus/single-frame-24 index fde3f489..5fc3630f 100644 --- a/fuzzing/frames/corpus/single-frame-24 +++ b/fuzzing/frames/corpus/single-frame-24 @@ -1 +1 @@ -o/ \ No newline at end of file + \ No newline at end of file diff --git a/fuzzing/frames/corpus/single-frame-25 b/fuzzing/frames/corpus/single-frame-25 index e812b364..a2f47892 100644 --- a/fuzzing/frames/corpus/single-frame-25 +++ b/fuzzing/frames/corpus/single-frame-25 @@ -1 +1 @@ -g \ No newline at end of file +5z \ No newline at end of file diff --git a/fuzzing/frames/corpus/single-frame-26 b/fuzzing/frames/corpus/single-frame-26 index 390c6473..5e55b934 100644 --- a/fuzzing/frames/corpus/single-frame-26 +++ b/fuzzing/frames/corpus/single-frame-26 @@ -1 +1 @@ -E \ No newline at end of file +k \ No newline at end of file diff --git a/fuzzing/frames/corpus/single-frame-27 b/fuzzing/frames/corpus/single-frame-27 index aae59dd1..b2ee6aac 100644 --- a/fuzzing/frames/corpus/single-frame-27 +++ b/fuzzing/frames/corpus/single-frame-27 @@ -1 +1 @@ -L \ No newline at end of file +. \ No newline at end of file diff --git a/fuzzing/frames/corpus/single-frame-28 b/fuzzing/frames/corpus/single-frame-28 index 60e870cb..baf3cd6a 100644 --- a/fuzzing/frames/corpus/single-frame-28 +++ b/fuzzing/frames/corpus/single-frame-28 @@ -1 +1 @@ - \ No newline at end of file +M \ No newline at end of file diff --git a/fuzzing/frames/corpus/single-frame-29 b/fuzzing/frames/corpus/single-frame-29 index f5120b16..aaa181cd 100644 Binary files a/fuzzing/frames/corpus/single-frame-29 and b/fuzzing/frames/corpus/single-frame-29 differ diff --git a/fuzzing/frames/corpus/single-frame-3 b/fuzzing/frames/corpus/single-frame-3 index 73b51835..62df93f3 100644 --- a/fuzzing/frames/corpus/single-frame-3 +++ b/fuzzing/frames/corpus/single-frame-3 @@ -1 +1 @@ -C .T^'?Wko'tV68MǞSCsffOI#n \ No newline at end of file +w .T^'?Wko'tV68MǞSCsffOI#n \ No newline at end of file diff --git a/fuzzing/frames/corpus/single-frame-30 b/fuzzing/frames/corpus/single-frame-30 index 445b0e6a..7abbec41 100644 Binary files a/fuzzing/frames/corpus/single-frame-30 and b/fuzzing/frames/corpus/single-frame-30 differ diff --git a/fuzzing/frames/corpus/single-frame-31 b/fuzzing/frames/corpus/single-frame-31 index b46f5cbc..3603ff73 100644 Binary files a/fuzzing/frames/corpus/single-frame-31 and b/fuzzing/frames/corpus/single-frame-31 differ diff --git a/fuzzing/frames/corpus/single-frame-32 b/fuzzing/frames/corpus/single-frame-32 index 0f72c79a..44ef1481 100644 Binary files a/fuzzing/frames/corpus/single-frame-32 and b/fuzzing/frames/corpus/single-frame-32 differ diff --git a/fuzzing/frames/corpus/single-frame-33 b/fuzzing/frames/corpus/single-frame-33 index c4fadce7..a8d80454 100644 --- a/fuzzing/frames/corpus/single-frame-33 +++ b/fuzzing/frames/corpus/single-frame-33 @@ -1 +1 @@ - ҕHG(+^ \ No newline at end of file + @d=˿)y[{sACN6O}y8\!cu&93i\*QHf=s᫟}&;'I{TGMˆ*hM h֥t \ No newline at end of file diff --git a/fuzzing/frames/corpus/single-frame-34 b/fuzzing/frames/corpus/single-frame-34 index b1961f02..fd81113a 100644 --- a/fuzzing/frames/corpus/single-frame-34 +++ b/fuzzing/frames/corpus/single-frame-34 @@ -1 +1 @@ -FFՙVQ__SfJ| \ No newline at end of file + w[m!v)nvDݸ#/ \ No newline at end of file diff --git a/fuzzing/frames/corpus/single-frame-35 b/fuzzing/frames/corpus/single-frame-35 index e032e44c..35d3ea3c 100644 Binary files a/fuzzing/frames/corpus/single-frame-35 and b/fuzzing/frames/corpus/single-frame-35 differ diff --git a/fuzzing/frames/corpus/single-frame-36 b/fuzzing/frames/corpus/single-frame-36 index 0398d0cb..e3920df3 100644 Binary files a/fuzzing/frames/corpus/single-frame-36 and b/fuzzing/frames/corpus/single-frame-36 differ diff --git a/fuzzing/frames/corpus/single-frame-4 b/fuzzing/frames/corpus/single-frame-4 index bb58ccb2..e22faccf 100644 --- a/fuzzing/frames/corpus/single-frame-4 +++ b/fuzzing/frames/corpus/single-frame-4 @@ -1 +1,2 @@ - gKu!g \ No newline at end of file +} gʆ+Ku!_]2`ޗԠ6l FMFO: u2zÚҳyS:D,G2`u3Y𝣞Ji9{4 +nM_$!^)*|aͳ3s,'!2QfN,`H0?#HSh_v򧘼d]K*l8#BvZ֖-`F^)ܤjàp;qJd1Q?BJẟ*rf,*MF \ No newline at end of file diff --git a/fuzzing/frames/corpus/single-frame-5 b/fuzzing/frames/corpus/single-frame-5 index 57f5900e..34b19b2a 100644 --- a/fuzzing/frames/corpus/single-frame-5 +++ b/fuzzing/frames/corpus/single-frame-5 @@ -1 +1 @@ - _ΦF`ޗ \ No newline at end of file + ^$0S \ No newline at end of file diff --git a/fuzzing/frames/corpus/single-frame-6 b/fuzzing/frames/corpus/single-frame-6 index 85d5167c..05e6d784 100644 Binary files a/fuzzing/frames/corpus/single-frame-6 and b/fuzzing/frames/corpus/single-frame-6 differ diff --git a/fuzzing/frames/corpus/single-frame-7 b/fuzzing/frames/corpus/single-frame-7 index e02737e4..b643cce5 100644 Binary files a/fuzzing/frames/corpus/single-frame-7 and b/fuzzing/frames/corpus/single-frame-7 differ diff --git a/fuzzing/frames/corpus/single-frame-8 b/fuzzing/frames/corpus/single-frame-8 index 591017f4..8ee681c5 100644 Binary files a/fuzzing/frames/corpus/single-frame-8 and b/fuzzing/frames/corpus/single-frame-8 differ diff --git a/fuzzing/frames/corpus/single-frame-9 b/fuzzing/frames/corpus/single-frame-9 index a221134e..6adeaa3e 100644 Binary files a/fuzzing/frames/corpus/single-frame-9 and b/fuzzing/frames/corpus/single-frame-9 differ diff --git a/fuzzing/frames/fuzz.go b/fuzzing/frames/fuzz.go index a5b8fbd5..45f7d8df 100644 --- a/fuzzing/frames/fuzz.go +++ b/fuzzing/frames/fuzz.go @@ -57,6 +57,7 @@ func Fuzz(data []byte) int { // We accept empty STREAM frames, but we don't write them. if sf, ok := f.(*wire.StreamFrame); ok { if sf.DataLen() == 0 { + sf.PutBack() continue } } @@ -68,6 +69,9 @@ func Fuzz(data []byte) int { if f.Length(version) != protocol.ByteCount(frameLen) { panic(fmt.Sprintf("Inconsistent frame length for %#v: expected %d, got %d", f, frameLen, f.Length(version))) } + if sf, ok := f.(*wire.StreamFrame); ok { + sf.PutBack() + } } if b.Len() > parsedLen { panic(fmt.Sprintf("Serialized length (%d) is longer than parsed length (%d)", b.Len(), parsedLen)) diff --git a/fuzzing/frames/main.go b/fuzzing/frames/main.go index f397be6f..4ba96c5d 100644 --- a/fuzzing/frames/main.go +++ b/fuzzing/frames/main.go @@ -88,6 +88,12 @@ func getFrames() []wire.Frame { Data: getRandomData(50), FinBit: true, }, + &wire.StreamFrame{ // STREAM frame at non-zero offset, with data and FIN bit. Long enough to use the buffer. + StreamID: protocol.StreamID(getRandomNumber()), + Offset: protocol.ByteCount(getRandomNumber()), + Data: getRandomData(2 * protocol.MinStreamFrameBufferSize), + FinBit: true, + }, &wire.StreamFrame{ // STREAM frame at maximum offset, with FIN bit StreamID: protocol.StreamID(getRandomNumber()), Offset: protocol.MaxByteCount - 5, diff --git a/internal/protocol/params.go b/internal/protocol/params.go index dd191de2..daa4688e 100644 --- a/internal/protocol/params.go +++ b/internal/protocol/params.go @@ -81,6 +81,11 @@ const MaxNonAckElicitingAcks = 19 // prevents DoS attacks against the streamFrameSorter const MaxStreamFrameSorterGaps = 1000 +// MinStreamFrameBufferSize is the minimum data length of a received STREAM frame +// that we use the buffer for. This protects against a DoS where an attacker would send us +// very small STREAM frames to consume a lot of memory. +const MinStreamFrameBufferSize = 128 + // MaxCryptoStreamOffset is the maximum offset allowed on any of the crypto streams. // This limits the size of the ClientHello and Certificates that can be received. const MaxCryptoStreamOffset = 16 * (1 << 10) diff --git a/internal/wire/pool.go b/internal/wire/pool.go new file mode 100644 index 00000000..966a4758 --- /dev/null +++ b/internal/wire/pool.go @@ -0,0 +1,33 @@ +package wire + +import ( + "sync" + + "github.com/lucas-clemente/quic-go/internal/protocol" +) + +var pool sync.Pool + +func init() { + pool.New = func() interface{} { + return &StreamFrame{ + Data: make([]byte, 0, protocol.MaxReceivePacketSize), + fromPool: true, + } + } +} + +func getStreamFrame() *StreamFrame { + f := pool.Get().(*StreamFrame) + return f +} + +func putStreamFrame(f *StreamFrame) { + if !f.fromPool { + return + } + if protocol.ByteCount(cap(f.Data)) != protocol.MaxReceivePacketSize { + panic("wire.PutStreamFrame called with packet of wrong size!") + } + pool.Put(f) +} diff --git a/internal/wire/pool_test.go b/internal/wire/pool_test.go new file mode 100644 index 00000000..ed9593c9 --- /dev/null +++ b/internal/wire/pool_test.go @@ -0,0 +1,24 @@ +package wire + +import ( + . "github.com/onsi/ginkgo" + . "github.com/onsi/gomega" +) + +var _ = Describe("Pool", func() { + It("gets and puts STREAM frames", func() { + f := getStreamFrame() + putStreamFrame(f) + }) + + It("panics when putting a STREAM frame with a wrong capacity", func() { + f := getStreamFrame() + f.Data = []byte("foobar") + Expect(func() { putStreamFrame(f) }).To(Panic()) + }) + + It("accepts STREAM frames not from the buffer, but ignores them", func() { + f := &StreamFrame{Data: []byte("foobar")} + putStreamFrame(f) + }) +}) diff --git a/internal/wire/stream_frame.go b/internal/wire/stream_frame.go index 65269cf0..41254166 100644 --- a/internal/wire/stream_frame.go +++ b/internal/wire/stream_frame.go @@ -17,6 +17,8 @@ type StreamFrame struct { DataLenPresent bool Offset protocol.ByteCount Data []byte + + fromPool bool } func parseStreamFrame(r *bytes.Reader, version protocol.VersionNumber) (*StreamFrame, error) { @@ -26,45 +28,53 @@ func parseStreamFrame(r *bytes.Reader, version protocol.VersionNumber) (*StreamF } hasOffset := typeByte&0x4 > 0 - frame := &StreamFrame{ - FinBit: typeByte&0x1 > 0, - DataLenPresent: typeByte&0x2 > 0, - } + fin := typeByte&0x1 > 0 + hasDataLen := typeByte&0x2 > 0 streamID, err := utils.ReadVarInt(r) if err != nil { return nil, err } - frame.StreamID = protocol.StreamID(streamID) + var offset uint64 if hasOffset { - offset, err := utils.ReadVarInt(r) + offset, err = utils.ReadVarInt(r) if err != nil { return nil, err } - frame.Offset = protocol.ByteCount(offset) } var dataLen uint64 - if frame.DataLenPresent { + if hasDataLen { var err error dataLen, err = utils.ReadVarInt(r) if err != nil { return nil, err } - // shortcut to prevent the unnecessary allocation of dataLen bytes - // if the dataLen is larger than the remaining length of the packet - // reading the packet contents would result in EOF when attempting to READ - if dataLen > uint64(r.Len()) { - return nil, io.EOF - } } else { // The rest of the packet is data dataLen = uint64(r.Len()) } + + var frame *StreamFrame + if dataLen < protocol.MinStreamFrameBufferSize { + frame = &StreamFrame{Data: make([]byte, dataLen)} + } else { + frame = getStreamFrame() + // The STREAM frame can't be larger than the StreamFrame we obtained from the buffer, + // since those StreamFrames have a buffer length of the maximum packet size. + if dataLen > uint64(cap(frame.Data)) { + return nil, io.EOF + } + frame.Data = frame.Data[:dataLen] + } + + frame.StreamID = protocol.StreamID(streamID) + frame.Offset = protocol.ByteCount(offset) + frame.FinBit = fin + frame.DataLenPresent = hasDataLen + if dataLen != 0 { - frame.Data = make([]byte, dataLen) if _, err := io.ReadFull(r, frame.Data); err != nil { - // this should never happen, since we already checked the dataLen earlier return nil, err } } @@ -156,16 +166,25 @@ func (f *StreamFrame) MaybeSplitOffFrame(maxSize protocol.ByteCount, version pro if n == 0 { return nil, true } - newFrame := &StreamFrame{ - FinBit: false, - StreamID: f.StreamID, - Offset: f.Offset, - Data: f.Data[:n], - DataLenPresent: f.DataLenPresent, - } - f.Data = f.Data[n:] + new := getStreamFrame() + new.StreamID = f.StreamID + new.Offset = f.Offset + new.FinBit = false + new.DataLenPresent = f.DataLenPresent + + // swap the data slices + new.Data, f.Data = f.Data, new.Data + new.fromPool, f.fromPool = f.fromPool, new.fromPool + + f.Data = f.Data[:protocol.ByteCount(len(new.Data))-n] + copy(f.Data, new.Data[n:]) + new.Data = new.Data[:n] f.Offset += n - return newFrame, true + return new, true +} + +func (f *StreamFrame) PutBack() { + putStreamFrame(f) } diff --git a/internal/wire/stream_frame_test.go b/internal/wire/stream_frame_test.go index 3b115203..7b67f603 100644 --- a/internal/wire/stream_frame_test.go +++ b/internal/wire/stream_frame_test.go @@ -2,6 +2,7 @@ package wire import ( "bytes" + "io" "github.com/lucas-clemente/quic-go/internal/protocol" "github.com/lucas-clemente/quic-go/internal/utils" @@ -78,6 +79,16 @@ var _ = Describe("STREAM frame", func() { Expect(err).To(MatchError("FRAME_ENCODING_ERROR: stream data overflows maximum offset")) }) + It("rejects frames that claim to be longer than the packet size", func() { + data := []byte{0x8 ^ 0x2} + data = append(data, encodeVarInt(0x12345)...) // stream ID + data = append(data, encodeVarInt(uint64(protocol.MaxReceivePacketSize)+1)...) // data length + data = append(data, make([]byte, protocol.MaxReceivePacketSize+1)...) + r := bytes.NewReader(data) + _, err := parseStreamFrame(r, versionIETFFrames) + Expect(err).To(Equal(io.EOF)) + }) + It("errors on EOFs", func() { data := []byte{0x8 ^ 0x4 ^ 0x2} data = append(data, encodeVarInt(0x12345)...) // stream ID @@ -93,6 +104,40 @@ var _ = Describe("STREAM frame", func() { }) }) + Context("using the buffer", func() { + It("uses the buffer for long STREAM frames", func() { + data := []byte{0x8} + data = append(data, encodeVarInt(0x12345)...) // stream ID + data = append(data, bytes.Repeat([]byte{'f'}, protocol.MinStreamFrameBufferSize)...) + r := bytes.NewReader(data) + frame, err := parseStreamFrame(r, versionIETFFrames) + Expect(err).ToNot(HaveOccurred()) + Expect(frame.StreamID).To(Equal(protocol.StreamID(0x12345))) + Expect(frame.Data).To(Equal(bytes.Repeat([]byte{'f'}, protocol.MinStreamFrameBufferSize))) + Expect(frame.DataLen()).To(BeEquivalentTo(protocol.MinStreamFrameBufferSize)) + Expect(frame.FinBit).To(BeFalse()) + Expect(frame.fromPool).To(BeTrue()) + Expect(r.Len()).To(BeZero()) + Expect(frame.PutBack).ToNot(Panic()) + }) + + It("doesn't use the buffer for short STREAM frames", func() { + data := []byte{0x8} + data = append(data, encodeVarInt(0x12345)...) // stream ID + data = append(data, bytes.Repeat([]byte{'f'}, protocol.MinStreamFrameBufferSize-1)...) + r := bytes.NewReader(data) + frame, err := parseStreamFrame(r, versionIETFFrames) + Expect(err).ToNot(HaveOccurred()) + Expect(frame.StreamID).To(Equal(protocol.StreamID(0x12345))) + Expect(frame.Data).To(Equal(bytes.Repeat([]byte{'f'}, protocol.MinStreamFrameBufferSize-1))) + Expect(frame.DataLen()).To(BeEquivalentTo(protocol.MinStreamFrameBufferSize - 1)) + Expect(frame.FinBit).To(BeFalse()) + Expect(frame.fromPool).To(BeFalse()) + Expect(r.Len()).To(BeZero()) + Expect(frame.PutBack).ToNot(Panic()) + }) + }) + Context("when writing", func() { It("writes a frame without offset", func() { f := &StreamFrame{ @@ -294,6 +339,7 @@ var _ = Describe("STREAM frame", func() { frame, needsSplit = f.MaybeSplitOffFrame(f.Length(versionIETFFrames)-1, versionIETFFrames) Expect(needsSplit).To(BeTrue()) Expect(frame.DataLen()).To(BeEquivalentTo(99)) + f.PutBack() }) It("keeps the data len", func() { @@ -353,6 +399,7 @@ var _ = Describe("STREAM frame", func() { Expect(f).To(BeNil()) } for i := minFrameSize; i < size; i++ { + f.fromPool = false f.Data = make([]byte, size) f, needsSplit := f.MaybeSplitOffFrame(i, versionIETFFrames) Expect(needsSplit).To(BeTrue()) @@ -376,6 +423,7 @@ var _ = Describe("STREAM frame", func() { } var frameOneByteTooSmallCounter int for i := minFrameSize; i < size; i++ { + f.fromPool = false f.Data = make([]byte, size) newFrame, needsSplit := f.MaybeSplitOffFrame(i, versionIETFFrames) Expect(needsSplit).To(BeTrue()) diff --git a/packet_packer_test.go b/packet_packer_test.go index 669ba1b9..c1b9a58c 100644 --- a/packet_packer_test.go +++ b/packet_packer_test.go @@ -412,7 +412,11 @@ var _ = Describe("Packet packer", func() { frameParser := wire.NewFrameParser(packer.version) frame, err := frameParser.ParseNext(r, protocol.Encryption1RTT) Expect(err).ToNot(HaveOccurred()) - Expect(frame).To(Equal(f)) + Expect(frame).To(BeAssignableToTypeOf(&wire.StreamFrame{})) + sf := frame.(*wire.StreamFrame) + Expect(sf.StreamID).To(Equal(f.StreamID)) + Expect(sf.FinBit).To(Equal(f.FinBit)) + Expect(sf.Data).To(BeEmpty()) Expect(r.Len()).To(BeZero()) })