http2: respect max-table-size in headers size update

Ticket: 8755

The config parameter was only taken into account from the
settings frame HEADER_TABLE_SIZE

Emits an event http2.header_table_overflow when we have the first
header that we cannot keep in Suricata's memory due to the
configured limit.
This commit is contained in:
Philippe Antoine
2026-09-08 20:24:51 +02:00
committed by Victor Julien
parent 1197bd175b
commit 521deb07d6
3 changed files with 20 additions and 16 deletions
+1
View File
@@ -27,3 +27,4 @@ alert http2 any any -> any any (msg:"SURICATA HTTP2 data on stream zero"; flow:e
# disabled by default, as it can happen in legit cases depending on the max-frames config value
# alert http2 any any -> any any (msg:"SURICATA HTTP2 too many frames"; flow:established; app-layer-event:http2.too_many_frames; classtype:protocol-command-decode; sid:2290019; rev:1;)
alert http2 any any -> any any (msg:"SURICATA HTTP2 compression bomb"; flow:established; app-layer-event:http2.compression_bomb; classtype:protocol-command-decode; sid:2290020; rev:1;)
alert http2 any any -> any any (msg:"SURICATA HTTP2 headers table overflow"; flow:established; app-layer-event:http2.header_table_overflow; classtype:protocol-command-decode; sid:2290021; rev:1;)
+10 -14
View File
@@ -677,6 +677,7 @@ pub enum HTTP2Event {
FailedDecompression,
InvalidRange,
HeaderIntegerOverflow,
HeaderTableOverflow,
TooManyStreams,
AuthorityHostMismatch,
UserinfoInUri,
@@ -981,29 +982,24 @@ impl HTTP2State {
&mut self, blocks: &Vec<parser::HTTP2FrameHeaderBlock>, dir: Direction,
) -> Vec<HTTP2Event> {
let mut events = Vec::new();
let (mut update, mut sizeup) = (false, 0);
for block in blocks {
if block.error >= parser::HTTP2HeaderDecodeStatus::HTTP2HeaderDecodeError {
events.push(HTTP2Event::InvalidHeader);
} else if block.error == parser::HTTP2HeaderDecodeStatus::HTTP2HeaderDecodeSizeUpdate {
update = true;
if block.sizeupdate > sizeup {
sizeup = block.sizeupdate;
}
} else if block.error
== parser::HTTP2HeaderDecodeStatus::HTTP2HeaderDecodeIntegerOverflow
{
events.push(HTTP2Event::HeaderIntegerOverflow);
}
}
if update {
//borrow checker forbids to pass directly dyn_headers
let dyn_headers = if dir == Direction::ToClient {
&mut self.dynamic_headers_tc
} else {
&mut self.dynamic_headers_ts
};
dyn_headers.max_size = sizeup as usize;
let dyn_headers = if dir == Direction::ToClient {
&mut self.dynamic_headers_tc
} else {
&mut self.dynamic_headers_ts
};
if dyn_headers.overflow == 2 {
// event only once
dyn_headers.overflow = 3;
events.push(HTTP2Event::HeaderTableOverflow);
}
return events;
}
+9 -2
View File
@@ -401,8 +401,8 @@ fn http2_parse_headers_block_literal_incindex<'a>(
if dyn_headers.current_size <= (unsafe { HTTP2_MAX_TABLESIZE } as usize) {
//overflow had not yet happened
dyn_headers.table.push(headcopy);
} else if dyn_headers.current_size > dyn_headers.max_size {
//overflow happens, we cannot replace evicted headers
} else {
//overflow happens, we cannot record new headers
dyn_headers.overflow = 2;
}
}
@@ -506,7 +506,14 @@ fn http2_parse_headers_block_dynamic_size<'a>(
toremove += 1;
}
dyn_headers.table.drain(0..toremove);
if maxsize2 <= unsafe { HTTP2_MAX_TABLESIZE.into() } {
dyn_headers.overflow = 0;
}
} else if maxsize2 > unsafe { HTTP2_MAX_TABLESIZE.into() } {
//mark potential overflow
dyn_headers.overflow = 1;
}
dyn_headers.max_size = maxsize2 as usize;
return Ok((
i3,
HTTP2FrameHeaderBlock {