From 5475212f21e13a93ae0b954fbaed906778c3dcda Mon Sep 17 00:00:00 2001 From: Philippe Antoine Date: Thu, 2 Dec 2021 09:54:05 +0100 Subject: [PATCH] http2: limits the number of active transactions per flow Ticket: 4530 So, that we do not get DOS by quadratic complexity, while looking for a new stream id over the ever growing list of active streams --- rules/http2-events.rules | 1 + rust/src/http2/http2.rs | 18 +++++++++++++++++- 2 files changed, 18 insertions(+), 1 deletion(-) diff --git a/rules/http2-events.rules b/rules/http2-events.rules index 15b7eed51c..c7a88b0c2b 100644 --- a/rules/http2-events.rules +++ b/rules/http2-events.rules @@ -17,3 +17,4 @@ alert http2 any any -> any any (msg:"SURICATA HTTP2 invalid HTTP1 settings durin alert http2 any any -> any any (msg:"SURICATA HTTP2 failed decompression"; flow:established; app-layer-event:http2.failed_decompression; classtype:protocol-command-decode; sid:2290009; rev:1;) alert http2 any any -> any any (msg:"SURICATA HTTP2 invalid range header"; flow:established; app-layer-event:http2.invalid_range; classtype:protocol-command-decode; sid:2290010; rev:1;) alert http2 any any -> any any (msg:"SURICATA HTTP2 variable-length integer overflow"; flow:established; app-layer-event:http2.header_integer_overflow; classtype:protocol-command-decode; sid:2290011; rev:1;) +alert http2 any any -> any any (msg:"SURICATA HTTP2 too many streams"; flow:established; app-layer-event:http2.too_many_streams; classtype:protocol-command-decode; sid:2290012; rev:1;) diff --git a/rust/src/http2/http2.rs b/rust/src/http2/http2.rs index def71d70e2..7d9488519d 100644 --- a/rust/src/http2/http2.rs +++ b/rust/src/http2/http2.rs @@ -58,8 +58,9 @@ const HTTP2_FRAME_GOAWAY_LEN: usize = 4; const HTTP2_FRAME_RSTSTREAM_LEN: usize = 4; const HTTP2_FRAME_PRIORITY_LEN: usize = 5; const HTTP2_FRAME_WINDOWUPDATE_LEN: usize = 4; -//TODO make this configurable +//TODO make these configurable pub const HTTP2_MAX_TABLESIZE: u32 = 0x10000; // 65536 +pub const HTTP2_MAX_STREAMS: usize = 0x1000; // 4096 #[repr(u8)] #[derive(Copy, Clone, PartialOrd, PartialEq, Debug)] @@ -110,6 +111,8 @@ pub enum HTTP2TransactionState { HTTP2StateClosed = 7, //not a RFC-defined state, used for stream 0 frames appyling to the global connection HTTP2StateGlobal = 8, + //not a RFC-defined state, dropping this old tx because we have too many + HTTP2StateTodrop = 9, } #[derive(Debug)] @@ -367,6 +370,7 @@ pub enum HTTP2Event { FailedDecompression, InvalidRange, HeaderIntegerOverflow, + TooManyStreams, } pub struct HTTP2DynTable { @@ -571,6 +575,18 @@ impl HTTP2State { tx.tx_id = self.tx_id; tx.stream_id = sid; tx.state = HTTP2TransactionState::HTTP2StateOpen; + // do not use SETTINGS_MAX_CONCURRENT_STREAMS as it can grow too much + if self.transactions.len() > HTTP2_MAX_STREAMS { + // set at least one another transaction to the drop state + for tx_old in &mut self.transactions { + if tx_old.state != HTTP2TransactionState::HTTP2StateTodrop { + // use a distinct state, even if we do not log it + tx_old.set_event(HTTP2Event::TooManyStreams); + tx_old.state = HTTP2TransactionState::HTTP2StateTodrop; + break; + } + } + } self.transactions.push(tx); return self.transactions.last_mut().unwrap(); }