k8s-operator/sessionrecording/spdy: don't pre-size header map from wire

Signed-off-by: chaosinthecrd <tom@tmlabs.co.uk>
This commit is contained in:
chaosinthecrd committed 2026-08-11 14:16:29 +01:00
1 parent 2f39c8f526
commit ccde9e701a
2 files changed
+31 -1

No files matched your search

+6 -1
View File
@@ -222,7 +222,12 @@ func parseHeaders(decompressor io.Reader, log *zap.SugaredLogger) (http.Header,
if err != nil {
return nil, fmt.Errorf("error determining num headers: %v", err)
}
h := make(http.Header, numHeaders)
// numHeaders is attacker controlled, so it must not be used to pre-size the
// map: a small frame can declare ~4 billion headers and make Go reserve
// gigabytes of bucket storage before a single header is read. The map grows
// itself fine, and the io.LimitReader above bounds how many we can actually
// read.
h := make(http.Header)
for range numHeaders {
name, err := readLenBytes()
if err != nil {
@@ -261,6 +261,31 @@ func Test_spdyFrame_parseHeaders_decompressionBomb(t *testing.T) {
if got.Get("x") != strings.Repeat("A", 16) {
t.Fatalf("unexpected header value: got %q", got.Get("x"))
}
// A block declaring a huge number of headers must be rejected without
// pre-sizing a map for that many entries. The declared count is only 4
// bytes, so it slips under the io.LimitReader cap; the fix is to not feed
// it to make(). Without that fix this does not fail, it OOMs the binary.
countBomb := bytes.NewBuffer(nil)
writeControlFramePayloadBeforeHeaders(t, countBomb, SYN_STREAM, 1)
cw, err := zlib.NewWriterLevelDict(countBomb, zlib.BestCompression, spdyTxtDictionary)
if err != nil {
t.Fatalf("error creating zlib writer: %v", err)
}
if err := binary.Write(cw, binary.BigEndian, uint32(0xFFFFFFFF)); err != nil {
t.Fatal(err)
}
if err := cw.Flush(); err != nil {
t.Fatal(err)
}
if err := cw.Close(); err != nil {
t.Fatal(err)
}
var z3 zlibReader
sf3 := &spdyFrame{Ctrl: true, Type: SYN_STREAM, Payload: countBomb.Bytes()}
if _, err := sf3.parseHeaders(&z3, zl.Sugar()); err == nil {
t.Fatal("parseHeaders accepted a block declaring 0xFFFFFFFF headers")
}
}
// Test_spdyFrame_ParseRand calls spdyFrame.Parse with randomly generated bytes