--- Begin Message ---
Package: release.debian.org
Severity: normal
Tags: trixie
X-Debbugs-Cc: [email protected], [email protected],
[email protected]
Control: affects -1 + src:suricata
User: [email protected]
Usertags: pu
Dear stable release managers,
I'd like to hand in security patches for suricata 1:7.0.10-1+deb13u3
in Debian trixie patching all the open CVEs for suricata from
March 2026. In accordance with the security team, the CVEs will
not warrant DSAs and should be included in the next point release
please.
[ Reason ]
Security fixes for:
- CVE-2026-31932
- CVE-2026-31933
- CVE-2026-31935
- CVE-2026-31937
[ Impact ]
Exploiting these CVEs can lead to reduced availability or crashes.
Here is the upstream changelog of Suricata 7.0.15 [1]:
- Security #8307: krb5: internal request/response buffering leads to
quadratic complexity (7.0.x backport)(HIGH - CVE 2026-31932)
- Security #8365: stream: quadratic complexity in stream inspection
(7.0.x backport)(HIGH - CVE 2026-31933)
- Security #8296: http2: unbounded number of http2 frames per transaction
(7.0.x backport)(CRITICAL - CVE 2026-31935)
- Security #8304: dcerpc: internal buffering logic leads to quadratic
complexity (HIGH - CVE 2026-31937)
Though the impact may seem to look rather small, the vulnerabilities
can be exploited quite easily by simply sending the 'wrong' packets
over the network. Suricata is therefore highly affected due to its
nature as an IDS by processing untrusted input from the network.
[ Tests ]
- autopkgtest runs the unit-tests and these are OK for the patched
package [2].
- Furthermore I used suricata-verify, an upstream tool for additional
testing with pcaps, which also has the same number of successful
tests between deb13u3 and deb13u4. See stable [3] vs. patched [4].
[ Risks ]
Because of successful unit tests, the risk should be OK. Not patching
the vulnerabilities can lead to crashing detection and that would
miss the point of an IDS/IPS.
[ Checklist ]
[x] *all* changes are documented in the d/changelog
[x] I reviewed all changes and I approve them
[x] attach debdiff against the package in (old)stable
[x] the issue is verified as fixed in unstable
[ Changes ]
- Applied the upstream patches for 7.0.15 fixing the CVEs, based on
the information found in the upstream redmine tickets.
- d/p/CVE-2026-31932.patch: Patch has to be refreshed to fit for
Suricata 7.0.10.
- Furthermore added Debian patch headers and changelog.
[ Other info ]
See the source-debdiff and the patches attached.
See the final branch at [5] which might be a bit easier to review,
because each change is documented in a single commit.
Thanks for your work!
[1]
https://github.com/OISF/suricata/blob/e56afc6d522fbc8e124bf91bb77748b16fa43ae1/ChangeLog
[2] https://salsa.debian.org/ecite/pkg-suricata/-/jobs/9460035
[3] https://salsa.debian.org/ecite/pkg-suricata/-/jobs/9451606
[4] https://salsa.debian.org/ecite/pkg-suricata/-/jobs/9460039
[5] https://salsa.debian.org/ecite/pkg-suricata/-/tree/debian/trixie
diff --git a/debian/changelog b/debian/changelog
index 3dabe9c6..2b77a3a7 100644
--- a/debian/changelog
+++ b/debian/changelog
@@ -1,3 +1,10 @@
+suricata (1:7.0.10-1+deb13u4) UNRELEASED; urgency=medium
+
+ * Fix CVE-2026-31932 in 7.0.10.
+ Cherry-Picked from 4c51a74e2e3a06d352dcac5a720450a392c5c5b2.
+
+ -- Andreas Dolp <[email protected]> Sun, 12 Apr 2026 20:17:32 +0200
+
suricata (1:7.0.10-1+deb13u3) trixie; urgency=medium
* Fix CVE-2026-22258 in 7.0.10.
diff --git a/debian/patches/CVE-2026-31932.patch
b/debian/patches/CVE-2026-31932.patch
new file mode 100644
index 00000000..3c34be1e
--- /dev/null
+++ b/debian/patches/CVE-2026-31932.patch
@@ -0,0 +1,142 @@
+From 4c51a74e2e3a06d352dcac5a720450a392c5c5b2 Mon Sep 17 00:00:00 2001
+From: Philippe Antoine <[email protected]>
+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<u8>,
+ pub record_tc: usize,
+- pub defrag_buf_tc: Vec<u8>,
+
+ /// List of transactions for this session
+ transactions: Vec<KRB5Transaction>,
+@@ -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<u8>;
+- 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<u8>;
+- 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 --git a/debian/patches/series b/debian/patches/series
index 54f84b03..20d59113 100644
--- a/debian/patches/series
+++ b/debian/patches/series
@@ -27,3 +27,4 @@ CVE-2026-22259_3.patch
CVE-2026-22259_4.patch
CVE-2026-22261_1.patch
CVE-2026-22261_2.patch
+CVE-2026-31932.patch
diff --git a/debian/changelog b/debian/changelog
index 2b77a3a7..71958318 100644
--- a/debian/changelog
+++ b/debian/changelog
@@ -2,6 +2,8 @@ suricata (1:7.0.10-1+deb13u4) UNRELEASED; 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.
-- Andreas Dolp <[email protected]> Sun, 12 Apr 2026 20:17:32 +0200
diff --git a/debian/patches/CVE-2026-31933.patch
b/debian/patches/CVE-2026-31933.patch
new file mode 100644
index 00000000..8c34f8f9
--- /dev/null
+++ b/debian/patches/CVE-2026-31933.patch
@@ -0,0 +1,39 @@
+From fecaa08f591c508b6486e7e9a2ee05636d1f9503 Mon Sep 17 00:00:00 2001
+From: Victor Julien <[email protected]>
+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 --git a/debian/patches/series b/debian/patches/series
index 20d59113..2052b130 100644
--- a/debian/patches/series
+++ b/debian/patches/series
@@ -28,3 +28,4 @@ CVE-2026-22259_4.patch
CVE-2026-22261_1.patch
CVE-2026-22261_2.patch
CVE-2026-31932.patch
+CVE-2026-31933.patch
diff --git a/debian/changelog b/debian/changelog
index 71958318..c4e7644a 100644
--- a/debian/changelog
+++ b/debian/changelog
@@ -4,6 +4,8 @@ suricata (1:7.0.10-1+deb13u4) UNRELEASED; urgency=medium
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.
-- Andreas Dolp <[email protected]> Sun, 12 Apr 2026 20:17:32 +0200
diff --git a/debian/patches/CVE-2026-31935.patch
b/debian/patches/CVE-2026-31935.patch
new file mode 100644
index 00000000..0d9da691
--- /dev/null
+++ b/debian/patches/CVE-2026-31935.patch
@@ -0,0 +1,111 @@
+From 82b7c9c35aaebf8a2811bdb703dd51c2fa0693c2 Mon Sep 17 00:00:00 2001
+From: Philippe Antoine <[email protected]>
+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::<usize>() {
++ 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::<u32>() {
+ 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 --git a/debian/patches/series b/debian/patches/series
index 2052b130..bd9e34fa 100644
--- a/debian/patches/series
+++ b/debian/patches/series
@@ -29,3 +29,4 @@ CVE-2026-22261_1.patch
CVE-2026-22261_2.patch
CVE-2026-31932.patch
CVE-2026-31933.patch
+CVE-2026-31935.patch
diff --git a/debian/changelog b/debian/changelog
index c4e7644a..307c028c 100644
--- a/debian/changelog
+++ b/debian/changelog
@@ -6,6 +6,8 @@ suricata (1:7.0.10-1+deb13u4) UNRELEASED; urgency=medium
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 <[email protected]> Sun, 12 Apr 2026 20:17:32 +0200
diff --git a/debian/patches/CVE-2026-31937.patch
b/debian/patches/CVE-2026-31937.patch
new file mode 100644
index 00000000..6d5e4ad9
--- /dev/null
+++ b/debian/patches/CVE-2026-31937.patch
@@ -0,0 +1,110 @@
+From 281f419c0481f7d24d8ce5482b962673a3938e9b Mon Sep 17 00:00:00 2001
+From: Philippe Antoine <[email protected]>
+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 --git a/debian/patches/series b/debian/patches/series
index bd9e34fa..0beef7b2 100644
--- a/debian/patches/series
+++ b/debian/patches/series
@@ -30,3 +30,4 @@ CVE-2026-22261_2.patch
CVE-2026-31932.patch
CVE-2026-31933.patch
CVE-2026-31935.patch
+CVE-2026-31937.patch
diff -Nru suricata-7.0.10/debian/changelog suricata-7.0.10/debian/changelog
--- suricata-7.0.10/debian/changelog 2026-02-22 13:28:52.000000000 +0100
+++ suricata-7.0.10/debian/changelog 2026-04-23 07:14:21.000000000 +0200
@@ -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 <[email protected]> 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
01:00:00.000000000 +0100
+++ suricata-7.0.10/debian/patches/CVE-2026-31932.patch 2026-04-23
07:14:21.000000000 +0200
@@ -0,0 +1,142 @@
+From 4c51a74e2e3a06d352dcac5a720450a392c5c5b2 Mon Sep 17 00:00:00 2001
+From: Philippe Antoine <[email protected]>
+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<u8>,
+ pub record_tc: usize,
+- pub defrag_buf_tc: Vec<u8>,
+
+ /// List of transactions for this session
+ transactions: Vec<KRB5Transaction>,
+@@ -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<u8>;
+- 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<u8>;
+- 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
01:00:00.000000000 +0100
+++ suricata-7.0.10/debian/patches/CVE-2026-31933.patch 2026-04-23
07:13:06.000000000 +0200
@@ -0,0 +1,39 @@
+From fecaa08f591c508b6486e7e9a2ee05636d1f9503 Mon Sep 17 00:00:00 2001
+From: Victor Julien <[email protected]>
+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
01:00:00.000000000 +0100
+++ suricata-7.0.10/debian/patches/CVE-2026-31935.patch 2026-04-23
07:13:06.000000000 +0200
@@ -0,0 +1,111 @@
+From 82b7c9c35aaebf8a2811bdb703dd51c2fa0693c2 Mon Sep 17 00:00:00 2001
+From: Philippe Antoine <[email protected]>
+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::<usize>() {
++ 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::<u32>() {
+ 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
01:00:00.000000000 +0100
+++ suricata-7.0.10/debian/patches/CVE-2026-31937.patch 2026-04-23
07:13:06.000000000 +0200
@@ -0,0 +1,110 @@
+From 281f419c0481f7d24d8ce5482b962673a3938e9b Mon Sep 17 00:00:00 2001
+From: Philippe Antoine <[email protected]>
+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 13:21:42.000000000
+0100
+++ suricata-7.0.10/debian/patches/series 2026-04-23 07:14:21.000000000
+0200
@@ -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
--- End Message ---