Version in base suite: 7.0.10-1+deb13u3 Base version: suricata_7.0.10-1+deb13u3 Target version: suricata_7.0.10-1+deb13u4 Base file: /srv/ftp-master.debian.org/ftp/pool/main/s/suricata/suricata_7.0.10-1+deb13u3.dsc Target file: /srv/ftp-master.debian.org/policy/pool/main/s/suricata/suricata_7.0.10-1+deb13u4.dsc changelog | 13 +++ patches/CVE-2026-31932.patch | 142 +++++++++++++++++++++++++++++++++++++++++++ patches/CVE-2026-31933.patch | 39 +++++++++++ patches/CVE-2026-31935.patch | 111 +++++++++++++++++++++++++++++++++ patches/CVE-2026-31937.patch | 110 +++++++++++++++++++++++++++++++++ patches/series | 4 + 6 files changed, 419 insertions(+) dpkg-source: warning: cannot verify inline signature for /srv/release.debian.org/tmp/tmp7avalw3r/suricata_7.0.10-1+deb13u3.dsc: no acceptable signature found dpkg-source: warning: cannot verify inline signature for /srv/release.debian.org/tmp/tmp7avalw3r/suricata_7.0.10-1+deb13u4.dsc: no acceptable signature found diff -Nru suricata-7.0.10/debian/changelog suricata-7.0.10/debian/changelog --- suricata-7.0.10/debian/changelog 2026-02-22 12:28:52.000000000 +0000 +++ suricata-7.0.10/debian/changelog 2026-04-23 05:14:21.000000000 +0000 @@ -1,3 +1,16 @@ +suricata (1:7.0.10-1+deb13u4) trixie; urgency=medium + + * Fix CVE-2026-31932 in 7.0.10. + Cherry-Picked from 4c51a74e2e3a06d352dcac5a720450a392c5c5b2. + * Fix CVE-2026-31933 in 7.0.10. + Cherry-Picked from fecaa08f591c508b6486e7e9a2ee05636d1f9503. + * Fix CVE-2026-31935 in 7.0.10. + Cherry-Picked from 82b7c9c35aaebf8a2811bdb703dd51c2fa0693c2. + * Fix CVE-2026-31937 in 7.0.10. + Cherry-Picked from 281f419c0481f7d24d8ce5482b962673a3938e9b. + + -- Andreas Dolp Thu, 23 Apr 2026 07:14:21 +0200 + suricata (1:7.0.10-1+deb13u3) trixie; urgency=medium * Fix CVE-2026-22258 in 7.0.10. diff -Nru suricata-7.0.10/debian/patches/CVE-2026-31932.patch suricata-7.0.10/debian/patches/CVE-2026-31932.patch --- suricata-7.0.10/debian/patches/CVE-2026-31932.patch 1970-01-01 00:00:00.000000000 +0000 +++ suricata-7.0.10/debian/patches/CVE-2026-31932.patch 2026-04-23 05:14:21.000000000 +0000 @@ -0,0 +1,142 @@ +From 4c51a74e2e3a06d352dcac5a720450a392c5c5b2 Mon Sep 17 00:00:00 2001 +From: Philippe Antoine +Date: Wed, 18 Feb 2026 17:19:07 +0100 +Subject: [PATCH] krb5: use app-layer incomplete support + +Ticket: 3540 +Ticket: 8305 +(cherry picked from commit 8e886a8ecdb5d0b21568be489686a7e0f8136e58) + +Origin: upstream, https://github.com/OISF/suricata/commit/4c51a74e2e3a06d352dcac5a720450a392c5c5b2.patch +Bug: https://redmine.openinfosecfoundation.org/issues/8307 +Subject: Upstream fix for CVE-2026-31932 +--- + rust/src/krb/krb5.rs | 66 +++++++++++--------------------------------- + 1 file changed, 16 insertions(+), 50 deletions(-) + +--- a/rust/src/krb/krb5.rs ++++ b/rust/src/krb/krb5.rs +@@ -41,9 +41,7 @@ + pub req_id: u8, + + pub record_ts: usize, +- pub defrag_buf_ts: Vec, + pub record_tc: usize, +- pub defrag_buf_tc: Vec, + + /// List of transactions for this session + transactions: Vec, +@@ -109,9 +107,7 @@ + state_data: AppLayerStateData::new(), + req_id: 0, + record_ts: 0, +- defrag_buf_ts: Vec::new(), + record_tc: 0, +- defrag_buf_tc: Vec::new(), + transactions: Vec::new(), + tx_id: 0, + } +@@ -463,25 +459,9 @@ + stream_slice: StreamSlice, + _data: *const std::os::raw::c_void, + ) -> AppLayerResult { +- let state = cast_pointer!(state,KRB5State); +- let buf = stream_slice.as_slice(); +- +- let mut v : Vec; +- let tcp_buffer = match state.record_ts { +- 0 => buf, +- _ => { +- // sanity check to avoid memory exhaustion +- if state.defrag_buf_ts.len() + buf.len() > 100000 { +- SCLogDebug!("rs_krb5_parse_request_tcp: TCP buffer exploded {} {}", +- state.defrag_buf_ts.len(), buf.len()); +- return AppLayerResult::err(); +- } +- v = state.defrag_buf_ts.split_off(0); +- v.extend_from_slice(buf); +- v.as_slice() +- } +- }; +- let mut cur_i = tcp_buffer; ++ let state = cast_pointer!(state, KRB5State); ++ let mut cur_i = stream_slice.as_slice(); ++ let start_len = cur_i.len(); + while !cur_i.is_empty() { + if state.record_ts == 0 { + match be_u32(cur_i) as IResult<&[u8],u32> { +@@ -490,8 +470,7 @@ + cur_i = rem; + }, + Err(Err::Incomplete(_)) => { +- state.defrag_buf_ts.extend_from_slice(cur_i); +- return AppLayerResult::ok(); ++ return AppLayerResult::incomplete((start_len - cur_i.len()) as u32, 4u32); + } + _ => { + SCLogDebug!("rs_krb5_parse_request_tcp: reading record mark failed!"); +@@ -507,8 +486,10 @@ + cur_i = &cur_i[state.record_ts..]; + } else { + // more fragments required +- state.defrag_buf_ts.extend_from_slice(cur_i); +- return AppLayerResult::ok(); ++ return AppLayerResult::incomplete( ++ (start_len - cur_i.len()) as u32, ++ state.record_ts as u32, ++ ); + } + } + AppLayerResult::ok() +@@ -521,25 +502,9 @@ + stream_slice: StreamSlice, + _data: *const std::os::raw::c_void, + ) -> AppLayerResult { +- let state = cast_pointer!(state,KRB5State); +- let buf = stream_slice.as_slice(); +- +- let mut v : Vec; +- let tcp_buffer = match state.record_tc { +- 0 => buf, +- _ => { +- // sanity check to avoid memory exhaustion +- if state.defrag_buf_tc.len() + buf.len() > 100000 { +- SCLogDebug!("rs_krb5_parse_response_tcp: TCP buffer exploded {} {}", +- state.defrag_buf_tc.len(), buf.len()); +- return AppLayerResult::err(); +- } +- v = state.defrag_buf_tc.split_off(0); +- v.extend_from_slice(buf); +- v.as_slice() +- } +- }; +- let mut cur_i = tcp_buffer; ++ let state = cast_pointer!(state, KRB5State); ++ let mut cur_i = stream_slice.as_slice(); ++ let start_len = cur_i.len(); + while !cur_i.is_empty() { + if state.record_tc == 0 { + match be_u32(cur_i) as IResult<&[u8],_> { +@@ -548,8 +513,7 @@ + cur_i = rem; + }, + Err(Err::Incomplete(_)) => { +- state.defrag_buf_tc.extend_from_slice(cur_i); +- return AppLayerResult::ok(); ++ return AppLayerResult::incomplete((start_len - cur_i.len()) as u32, 4u32); + } + _ => { + SCLogDebug!("reading record mark failed!"); +@@ -565,8 +529,10 @@ + cur_i = &cur_i[state.record_tc..]; + } else { + // more fragments required +- state.defrag_buf_tc.extend_from_slice(cur_i); +- return AppLayerResult::ok(); ++ return AppLayerResult::incomplete( ++ (start_len - cur_i.len()) as u32, ++ state.record_tc as u32, ++ ); + } + } + AppLayerResult::ok() diff -Nru suricata-7.0.10/debian/patches/CVE-2026-31933.patch suricata-7.0.10/debian/patches/CVE-2026-31933.patch --- suricata-7.0.10/debian/patches/CVE-2026-31933.patch 1970-01-01 00:00:00.000000000 +0000 +++ suricata-7.0.10/debian/patches/CVE-2026-31933.patch 2026-04-23 05:13:06.000000000 +0000 @@ -0,0 +1,39 @@ +From fecaa08f591c508b6486e7e9a2ee05636d1f9503 Mon Sep 17 00:00:00 2001 +From: Victor Julien +Date: Mon, 9 Mar 2026 11:56:55 +0100 +Subject: [PATCH] stream/reassembly: improve progress tracking for GAP cases + +When during raw reassembly it is detected that last ack is moved beyond +the progress and also beyond the data retrieved, update progress to the +last ack value. + +Bug: #8272. +(cherry picked from commit ac1a514c7b57ea24f603020ef790c59b84143244) + +Origin: upstream, https://github.com/OISF/suricata/commit/fecaa08f591c508b6486e7e9a2ee05636d1f9503.patch +Bug: https://redmine.openinfosecfoundation.org/issues/8365 +Subject: Upstream fix for CVE-2026-31933 +--- + src/stream-tcp-reassemble.c | 6 ++++++ + 1 file changed, 6 insertions(+) + +diff --git a/src/stream-tcp-reassemble.c b/src/stream-tcp-reassemble.c +index 25f0ce4fe..f6a646261 100644 +--- a/src/stream-tcp-reassemble.c ++++ b/src/stream-tcp-reassemble.c +@@ -1872,6 +1872,12 @@ static int StreamReassembleRawDo(const TcpSession *ssn, const TcpStream *stream, + progress = mydata_offset; + SCLogDebug("raw progress now %"PRIu64, progress); + ++ /* data is beyond the progress we'd like, and also beyond the last ack: ++ * there is a gap and we can't expect it to get filled anymore. */ ++ } else if (mydata_offset > progress && mydata_offset == re) { ++ SCLogDebug("mydata_offset %" PRIu64 ", progress %" PRIu64 ", re %" PRIu64, ++ mydata_offset, progress, re); ++ progress = re; + } else { + SCLogDebug("not increasing progress, data gap => mydata_offset " + "%"PRIu64" != progress %"PRIu64, mydata_offset, progress); +-- +2.47.3 + diff -Nru suricata-7.0.10/debian/patches/CVE-2026-31935.patch suricata-7.0.10/debian/patches/CVE-2026-31935.patch --- suricata-7.0.10/debian/patches/CVE-2026-31935.patch 1970-01-01 00:00:00.000000000 +0000 +++ suricata-7.0.10/debian/patches/CVE-2026-31935.patch 2026-04-23 05:13:06.000000000 +0000 @@ -0,0 +1,111 @@ +From 82b7c9c35aaebf8a2811bdb703dd51c2fa0693c2 Mon Sep 17 00:00:00 2001 +From: Philippe Antoine +Date: Wed, 18 Feb 2026 16:40:23 +0100 +Subject: [PATCH] http2: bound number of http2 frames per tx + +Ticket: 8289 + +If stream.reassembly.depth is unlimited, +an attacker controlling the 2 sides of a communication going through Suricata +can send a transition with an infinite number of headers, until suricata OOMs + +Solution is to offer a configuration option to bound the number +of HTTP2 frames we store in a HTTP2 transaction, and produce an +anomaly if this bound is crossed + +(cherry picked from commit 784e173278944c3596ea9cb219afcfafece6d156) + +Origin: upstream, https://github.com/OISF/suricata/commit/82b7c9c35aaebf8a2811bdb703dd51c2fa0693c2.patch +Bug: https://redmine.openinfosecfoundation.org/issues/8296 +Subject: Upstream fix for CVE-2026-31935 +--- + rules/http2-events.rules | 2 ++ + rust/src/http2/http2.rs | 23 +++++++++++++++++------ + suricata.yaml.in | 2 ++ + 3 files changed, 21 insertions(+), 6 deletions(-) + +diff --git a/rules/http2-events.rules b/rules/http2-events.rules +index 8242e2f79..ff170e8ed 100644 +--- a/rules/http2-events.rules ++++ b/rules/http2-events.rules +@@ -22,3 +22,5 @@ alert http2 any any -> any any (msg:"SURICATA HTTP2 authority host mismatch"; fl + alert http2 any any -> any any (msg:"SURICATA HTTP2 user info in uri"; flow:established,to_server; app-layer-event:http2.userinfo_in_uri; classtype:protocol-command-decode; sid:2290014; rev:1;) + alert http2 any any -> any any (msg:"SURICATA HTTP2 reassembly limit reached"; flow:established; app-layer-event:http2.reassembly_limit_reached; classtype:protocol-command-decode; sid:2290015; rev:1;) + alert http2 any any -> any any (msg:"SURICATA HTTP2 data on stream zero"; flow:established; app-layer-event:http2.data_stream_zero; classtype:protocol-command-decode; sid:2290018; rev:1;) ++# 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;) +diff --git a/rust/src/http2/http2.rs b/rust/src/http2/http2.rs +index 008bfeb6d..49647bc0b 100644 +--- a/rust/src/http2/http2.rs ++++ b/rust/src/http2/http2.rs +@@ -64,6 +64,7 @@ pub static mut HTTP2_MAX_TABLESIZE: u32 = 65536; // 0x10000 + // maximum size of reassembly for header + continuation + static mut HTTP2_MAX_REASS: usize = 102400; + static mut HTTP2_MAX_STREAMS: usize = 4096; // 0x1000 ++static mut HTTP2_MAX_FRAMES: usize = 65536; + + #[repr(u8)] + #[derive(Copy, Clone, PartialOrd, PartialEq, Eq, Debug)] +@@ -410,6 +411,7 @@ pub enum HTTP2Event { + UserinfoInUri, + ReassemblyLimitReached, + DataStreamZero, ++ TooManyFrames, + } + + pub struct HTTP2DynTable { +@@ -1067,16 +1069,18 @@ impl HTTP2State { + let ftype = head.ftype; + let sid = head.stream_id; + let padded = head.flags & parser::HTTP2_FLAG_HEADER_PADDED != 0; +- if dir == Direction::ToServer { +- tx.frames_ts.push(HTTP2Frame { +- header: head, +- data: txdata, +- }); ++ let h2frames = if dir == Direction::ToServer { ++ &mut tx.frames_ts + } else { +- tx.frames_tc.push(HTTP2Frame { ++ &mut tx.frames_tc ++ }; ++ if h2frames.len() < unsafe { HTTP2_MAX_FRAMES } { ++ h2frames.push(HTTP2Frame { + header: head, + data: txdata, + }); ++ } else { ++ tx.tx_data.set_event(HTTP2Event::TooManyFrames as u8); + } + if ftype == parser::HTTP2FrameType::Data as u8 && sid == 0 { + tx.tx_data.set_event(HTTP2Event::DataStreamZero as u8); +@@ -1393,6 +1397,13 @@ pub unsafe extern "C" fn rs_http2_register_parser() { + SCLogError!("Invalid value for http2.max-streams"); + } + } ++ if let Some(val) = conf_get("app-layer.protocols.http2.max-frames") { ++ if let Ok(v) = val.parse::() { ++ HTTP2_MAX_FRAMES = v; ++ } else { ++ SCLogError!("Invalid value for http2.max-frames"); ++ } ++ } + if let Some(val) = conf_get("app-layer.protocols.http2.max-table-size") { + if let Ok(v) = val.parse::() { + HTTP2_MAX_TABLESIZE = v; +diff --git a/suricata.yaml.in b/suricata.yaml.in +index 3cc45a461..b891e7646 100644 +--- a/suricata.yaml.in ++++ b/suricata.yaml.in +@@ -969,6 +969,8 @@ app-layer: + #max-table-size: 65536 + # Maximum reassembly size for header + continuation frames + #max-reassembly-size: 102400 ++ # Maximum number of frames per tx ++ #max-frames: 65536 + smtp: + enabled: yes + raw-extraction: no +-- +2.47.3 + diff -Nru suricata-7.0.10/debian/patches/CVE-2026-31937.patch suricata-7.0.10/debian/patches/CVE-2026-31937.patch --- suricata-7.0.10/debian/patches/CVE-2026-31937.patch 1970-01-01 00:00:00.000000000 +0000 +++ suricata-7.0.10/debian/patches/CVE-2026-31937.patch 2026-04-23 05:13:06.000000000 +0000 @@ -0,0 +1,110 @@ +From 281f419c0481f7d24d8ce5482b962673a3938e9b Mon Sep 17 00:00:00 2001 +From: Philippe Antoine +Date: Wed, 18 Feb 2026 15:00:08 +0100 +Subject: [PATCH] dcerpc: use take instead of split_off(0) + +Ticket: 8304 + +Avoids quadratic complexity. +With split_off(0), the whole vec is copied, so if wa have other +calls supplying one byte at a time, we keep on copying increasing +data + +Origin: upstream, https://github.com/OISF/suricata/commit/281f419c0481f7d24d8ce5482b962673a3938e9b.patch +Bug: https://redmine.openinfosecfoundation.org/issues/8304 +Subject: Upstream fix for CVE-2026-31937 +--- + rust/src/dcerpc/dcerpc.rs | 42 ++++++++++++++++++++------------------- + 1 file changed, 22 insertions(+), 20 deletions(-) + +diff --git a/rust/src/dcerpc/dcerpc.rs b/rust/src/dcerpc/dcerpc.rs +index 7297ff82e..daa2db836 100644 +--- a/rust/src/dcerpc/dcerpc.rs ++++ b/rust/src/dcerpc/dcerpc.rs +@@ -467,18 +467,6 @@ impl DCERPCState { + self.bytes_consumed = 0; + } + +- pub fn extend_buffer(&mut self, buffer: &[u8], direction: Direction) { +- match direction { +- Direction::ToServer => { +- self.buffer_ts.extend_from_slice(buffer); +- } +- Direction::ToClient => { +- self.buffer_tc.extend_from_slice(buffer); +- } +- } +- self.data_needed_for_dir = direction; +- } +- + pub fn reset_direction(&mut self, direction: Direction) { + if direction == Direction::ToServer { + self.data_needed_for_dir = Direction::ToClient; +@@ -952,24 +940,24 @@ impl DCERPCState { + self.data_needed_for_dir = direction; + } + +- let buffer = match direction { ++ let mut buffer = match direction { + Direction::ToServer => { + if self.buffer_ts.len() + input_len > 1024 * 1024 { + SCLogDebug!("DCERPC TOSERVER stream: Buffer Overflow"); + return AppLayerResult::err(); + } +- v = self.buffer_ts.split_off(0); ++ v = std::mem::take(&mut self.buffer_ts); + v.extend_from_slice(cur_i); +- v.as_slice() ++ v + } + Direction::ToClient => { + if self.buffer_tc.len() + input_len > 1024 * 1024 { + SCLogDebug!("DCERPC TOCLIENT stream: Buffer Overflow"); + return AppLayerResult::err(); + } +- v = self.buffer_tc.split_off(0); ++ v = std::mem::take(&mut self.buffer_tc); + v.extend_from_slice(cur_i); +- v.as_slice() ++ v + } + }; + +@@ -983,9 +971,16 @@ impl DCERPCState { + // Check if header data was complete. In case of EoF or incomplete data, wait for more + // data else return error + if self.bytes_consumed < DCERPC_HDR_LEN.into() && input_len > 0 { +- parsed = self.process_header(buffer); ++ parsed = self.process_header(buffer.as_slice()); + if parsed == -1 { +- self.extend_buffer(buffer, direction); ++ match direction { ++ Direction::ToServer => { ++ self.buffer_ts = std::mem::take(&mut buffer); ++ } ++ Direction::ToClient => { ++ self.buffer_tc = std::mem::take(&mut buffer); ++ } ++ } + return AppLayerResult::ok(); + } + if parsed == -2 { +@@ -998,7 +993,14 @@ impl DCERPCState { + + if (buffer.len()) < fraglen as usize { + SCLogDebug!("Possibly fragmented data, waiting for more.."); +- self.extend_buffer(buffer, direction); ++ match direction { ++ Direction::ToServer => { ++ self.buffer_ts = std::mem::take(&mut buffer); ++ } ++ Direction::ToClient => { ++ self.buffer_tc = std::mem::take(&mut buffer); ++ } ++ } + return AppLayerResult::ok(); + } else { + self.query_completed = true; +-- +2.47.3 + diff -Nru suricata-7.0.10/debian/patches/series suricata-7.0.10/debian/patches/series --- suricata-7.0.10/debian/patches/series 2026-02-22 12:21:42.000000000 +0000 +++ suricata-7.0.10/debian/patches/series 2026-04-23 05:14:21.000000000 +0000 @@ -27,3 +27,7 @@ CVE-2026-22259_4.patch CVE-2026-22261_1.patch CVE-2026-22261_2.patch +CVE-2026-31932.patch +CVE-2026-31933.patch +CVE-2026-31935.patch +CVE-2026-31937.patch