Skip to content

Commit cf0ec5d

Browse files
dimsthaJeztah
authored andcommitted
Guard against oversized SPDY frames
Signed-off-by: Davanum Srinivas <davanum@gmail.com> Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
1 parent f0be51f commit cf0ec5d

4 files changed

Lines changed: 72 additions & 5 deletions

File tree

connection.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -350,6 +350,9 @@ Loop:
350350
} else {
351351
debugMessage("(%p) EOF received", s)
352352
}
353+
if spdyErr, ok := err.(*spdy.Error); ok && spdyErr.Err == spdy.InvalidControlFrame {
354+
_ = s.conn.Close()
355+
}
353356
break
354357
}
355358
var priority uint8

spdy/read.go

Lines changed: 20 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,11 @@ func (frame *SettingsFrame) read(h ControlFrameHeader, f *Framer) error {
4343
if err := binary.Read(f.r, binary.BigEndian, &numSettings); err != nil {
4444
return err
4545
}
46+
// Each setting is 8 bytes (4-byte id + 4-byte value).
47+
// Payload is 4 bytes for numSettings + numSettings*8.
48+
if h.length < 4 || numSettings > (h.length-4)/8 {
49+
return &Error{InvalidControlFrame, 0}
50+
}
4651
frame.FlagIdValues = make([]SettingsFlagIdValue, numSettings)
4752
for i := uint32(0); i < numSettings; i++ {
4853
if err := binary.Read(f.r, binary.BigEndian, &frame.FlagIdValues[i].Id); err != nil {
@@ -161,8 +166,19 @@ func (f *Framer) parseControlFrame(version uint16, frameType ControlFrameType) (
161166
if err := binary.Read(f.r, binary.BigEndian, &length); err != nil {
162167
return nil, err
163168
}
169+
maxControlFramePayload := uint32(MaxDataLength)
170+
if f.maxFrameLength > 0 {
171+
maxControlFramePayload = f.maxFrameLength
172+
}
173+
164174
flags := ControlFlags((length & 0xff000000) >> 24)
165175
length &= 0xffffff
176+
if length > maxControlFramePayload {
177+
if _, err := io.CopyN(io.Discard, f.r, int64(length)); err != nil {
178+
return nil, err
179+
}
180+
return nil, &Error{InvalidControlFrame, 0}
181+
}
166182
header := ControlFrameHeader{version, frameType, flags, length}
167183
cframe, err := newControlFrame(frameType)
168184
if err != nil {
@@ -174,7 +190,7 @@ func (f *Framer) parseControlFrame(version uint16, frameType ControlFrameType) (
174190
return cframe, nil
175191
}
176192

177-
func parseHeaderValueBlock(r io.Reader, streamId StreamId) (http.Header, error) {
193+
func (f *Framer) parseHeaderValueBlock(r io.Reader, streamId StreamId) (http.Header, error) {
178194
var numHeaders uint32
179195
if err := binary.Read(r, binary.BigEndian, &numHeaders); err != nil {
180196
return nil, err
@@ -240,7 +256,7 @@ func (f *Framer) readSynStreamFrame(h ControlFrameHeader, frame *SynStreamFrame)
240256
}
241257
reader = f.headerDecompressor
242258
}
243-
frame.Headers, err = parseHeaderValueBlock(reader, frame.StreamId)
259+
frame.Headers, err = f.parseHeaderValueBlock(reader, frame.StreamId)
244260
if !f.headerCompressionDisabled && (err == io.EOF && f.headerReader.N == 0 || f.headerReader.N != 0) {
245261
err = &Error{WrongCompressedPayloadSize, 0}
246262
}
@@ -272,7 +288,7 @@ func (f *Framer) readSynReplyFrame(h ControlFrameHeader, frame *SynReplyFrame) e
272288
}
273289
reader = f.headerDecompressor
274290
}
275-
frame.Headers, err = parseHeaderValueBlock(reader, frame.StreamId)
291+
frame.Headers, err = f.parseHeaderValueBlock(reader, frame.StreamId)
276292
if !f.headerCompressionDisabled && (err == io.EOF && f.headerReader.N == 0 || f.headerReader.N != 0) {
277293
err = &Error{WrongCompressedPayloadSize, 0}
278294
}
@@ -304,7 +320,7 @@ func (f *Framer) readHeadersFrame(h ControlFrameHeader, frame *HeadersFrame) err
304320
}
305321
reader = f.headerDecompressor
306322
}
307-
frame.Headers, err = parseHeaderValueBlock(reader, frame.StreamId)
323+
frame.Headers, err = f.parseHeaderValueBlock(reader, frame.StreamId)
308324
if !f.headerCompressionDisabled && (err == io.EOF && f.headerReader.N == 0 || f.headerReader.N != 0) {
309325
err = &Error{WrongCompressedPayloadSize, 0}
310326
}

spdy/spdy_test.go

Lines changed: 47 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,8 @@ func TestHeaderParsing(t *testing.T) {
2727
var headerValueBlockBuf bytes.Buffer
2828
writeHeaderValueBlock(&headerValueBlockBuf, HeadersFixture)
2929
const bogusStreamId = 1
30-
newHeaders, err := parseHeaderValueBlock(&headerValueBlockBuf, bogusStreamId)
30+
f := &Framer{}
31+
newHeaders, err := f.parseHeaderValueBlock(&headerValueBlockBuf, bogusStreamId)
3132
if err != nil {
3233
t.Fatal("parseHeaderValueBlock:", err)
3334
}
@@ -223,6 +224,51 @@ func TestCreateParseSettings(t *testing.T) {
223224
}
224225
}
225226

227+
func TestReadFrameRejectsOversizedControlFrame(t *testing.T) {
228+
buffer := new(bytes.Buffer)
229+
framer, err := NewFramer(buffer, buffer)
230+
if err != nil {
231+
t.Fatal("Failed to create new framer:", err)
232+
}
233+
framer.maxFrameLength = 1
234+
235+
// Control frame header (version 3, SETTINGS, length = 2)
236+
_, _ = buffer.Write([]byte{
237+
0x80, 0x03, // control frame + version
238+
0x00, 0x04, // SETTINGS
239+
0x00, // flags
240+
0x00, 0x00, 0x02, // length = 2
241+
0x00, 0x01, // payload (2 bytes)
242+
})
243+
244+
_, err = framer.ReadFrame()
245+
if err == nil {
246+
t.Fatal("expected error for oversized control frame")
247+
}
248+
}
249+
250+
func TestReadFrameRejectsSettingsLengthMismatch(t *testing.T) {
251+
buffer := new(bytes.Buffer)
252+
framer, err := NewFramer(buffer, buffer)
253+
if err != nil {
254+
t.Fatal("Failed to create new framer:", err)
255+
}
256+
257+
// SETTINGS frame with declared payload length 0, but includes numSettings.
258+
_, _ = buffer.Write([]byte{
259+
0x80, 0x03, // control frame + version
260+
0x00, 0x04, // SETTINGS
261+
0x00, // flags
262+
0x00, 0x00, 0x00, // length = 0
263+
0x00, 0x00, 0x00, 0x01, // numSettings = 1
264+
})
265+
266+
_, err = framer.ReadFrame()
267+
if err == nil {
268+
t.Fatal("expected error for SETTINGS length mismatch")
269+
}
270+
}
271+
226272
func TestCreateParsePing(t *testing.T) {
227273
buffer := new(bytes.Buffer)
228274
framer, err := NewFramer(buffer, buffer)

spdy/types.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -255,6 +255,8 @@ type Framer struct {
255255
r io.Reader
256256
headerReader io.LimitedReader
257257
headerDecompressor io.ReadCloser
258+
259+
maxFrameLength uint32 // overrides the default frame payload length limit.
258260
}
259261

260262
// NewFramer allocates a new Framer for a given SPDY connection, represented by

0 commit comments

Comments
 (0)