Skip to content

Commit eb63605

Browse files
dimsthaJeztah
authored andcommitted
spdy: limit header-size and header-count
Signed-off-by: Davanum Srinivas <davanum@gmail.com> Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
1 parent 2f21da4 commit eb63605

3 files changed

Lines changed: 61 additions & 1 deletion

File tree

spdy/read.go

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -195,13 +195,27 @@ func (f *Framer) parseHeaderValueBlock(r io.Reader, streamId StreamId) (http.Hea
195195
if err := binary.Read(r, binary.BigEndian, &numHeaders); err != nil {
196196
return nil, err
197197
}
198+
maxHeaders := defaultMaxHeaderCount
199+
if f.maxHeaderCount > 0 {
200+
maxHeaders = f.maxHeaderCount
201+
}
202+
if numHeaders > maxHeaders {
203+
return nil, &Error{InvalidControlFrame, streamId}
204+
}
205+
maxFieldSize := defaultMaxHeaderFieldSize
206+
if f.maxHeaderFieldSize > 0 {
207+
maxFieldSize = f.maxHeaderFieldSize
208+
}
198209
var e error
199210
h := make(http.Header, int(numHeaders))
200211
for i := 0; i < int(numHeaders); i++ {
201212
var length uint32
202213
if err := binary.Read(r, binary.BigEndian, &length); err != nil {
203214
return nil, err
204215
}
216+
if length > maxFieldSize {
217+
return nil, &Error{InvalidControlFrame, streamId}
218+
}
205219
nameBytes := make([]byte, length)
206220
if _, err := io.ReadFull(r, nameBytes); err != nil {
207221
return nil, err
@@ -217,6 +231,9 @@ func (f *Framer) parseHeaderValueBlock(r io.Reader, streamId StreamId) (http.Hea
217231
if err := binary.Read(r, binary.BigEndian, &length); err != nil {
218232
return nil, err
219233
}
234+
if length > maxFieldSize {
235+
return nil, &Error{InvalidControlFrame, streamId}
236+
}
220237
value := make([]byte, length)
221238
if _, err := io.ReadFull(r, value); err != nil {
222239
return nil, err

spdy/spdy_test.go

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -269,6 +269,42 @@ func TestReadFrameRejectsSettingsLengthMismatch(t *testing.T) {
269269
}
270270
}
271271

272+
func TestParseHeaderValueBlockRejectsHeaderCountOverLimit(t *testing.T) {
273+
buffer := bytes.NewBuffer([]byte{
274+
0x00, 0x00, 0x00, 0x02, // numHeaders = 2
275+
})
276+
277+
f := &Framer{maxHeaderCount: 1}
278+
_, err := f.parseHeaderValueBlock(buffer, 1)
279+
if err == nil {
280+
t.Fatal("expected error for excessive header count")
281+
}
282+
spdyErr, ok := err.(*Error)
283+
if !ok || spdyErr.Err != InvalidControlFrame {
284+
t.Fatalf("unexpected error: %v", err)
285+
}
286+
}
287+
288+
func TestParseHeaderValueBlockRejectsHeaderFieldSizeOverLimit(t *testing.T) {
289+
buffer := bytes.NewBuffer([]byte{
290+
0x00, 0x00, 0x00, 0x01, // numHeaders = 1
291+
0x00, 0x00, 0x00, 0x02, // name length = 2
292+
0x61, 0x62, // name = "ab"
293+
0x00, 0x00, 0x00, 0x01, // value length = 1
294+
0x63, // value = "c"
295+
})
296+
297+
f := &Framer{maxHeaderFieldSize: 1}
298+
_, err := f.parseHeaderValueBlock(buffer, 1)
299+
if err == nil {
300+
t.Fatal("expected error for excessive header field size")
301+
}
302+
spdyErr, ok := err.(*Error)
303+
if !ok || spdyErr.Err != InvalidControlFrame {
304+
t.Fatalf("unexpected error: %v", err)
305+
}
306+
}
307+
272308
func TestCreateParsePing(t *testing.T) {
273309
buffer := new(bytes.Buffer)
274310
framer, err := NewFramer(buffer, buffer)

spdy/types.go

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,11 @@ const (
5151
// MaxDataLength is the maximum number of bytes that can be stored in one frame.
5252
const MaxDataLength = 1<<24 - 1
5353

54+
const (
55+
defaultMaxHeaderFieldSize uint32 = 1 << 20
56+
defaultMaxHeaderCount uint32 = 1000
57+
)
58+
5459
// headerValueSepator separates multiple header values.
5560
const headerValueSeparator = "\x00"
5661

@@ -256,7 +261,9 @@ type Framer struct {
256261
headerReader io.LimitedReader
257262
headerDecompressor io.ReadCloser
258263

259-
maxFrameLength uint32 // overrides the default frame payload length limit.
264+
maxFrameLength uint32 // overrides the default frame payload length limit.
265+
maxHeaderFieldSize uint32 // overrides the default per-header name/value length limit.
266+
maxHeaderCount uint32 // overrides the default header count limit.
260267
}
261268

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

0 commit comments

Comments
 (0)