Version in base suite: 7.0.10-1+deb13u2 Base version: suricata_7.0.10-1+deb13u2 Target version: suricata_7.0.10-1+deb13u3 Base file: /srv/ftp-master.debian.org/ftp/pool/main/s/suricata/suricata_7.0.10-1+deb13u2.dsc Target file: /srv/ftp-master.debian.org/policy/pool/main/s/suricata/suricata_7.0.10-1+deb13u3.dsc changelog | 29 ++++ patches/CVE-2026-22258_1.patch | 286 +++++++++++++++++++++++++++++++++++++++++ patches/CVE-2026-22258_2.patch | 54 +++++++ patches/CVE-2026-22258_3.patch | 73 ++++++++++ patches/CVE-2026-22259_1.patch | 38 +++++ patches/CVE-2026-22259_2.patch | 135 +++++++++++++++++++ patches/CVE-2026-22259_3.patch | 138 +++++++++++++++++++ patches/CVE-2026-22259_4.patch | 125 +++++++++++++++++ patches/CVE-2026-22261_1.patch | 68 +++++++++ patches/CVE-2026-22261_2.patch | 128 ++++++++++++++++++ patches/CVE-2026-22262_1.patch | 42 ++++++ patches/CVE-2026-22262_2.patch | 66 +++++++++ patches/CVE-2026-22264.patch | 84 ++++++++++++ patches/series | 12 + 14 files changed, 1278 insertions(+) dpkg-source: warning: cannot verify inline signature for /srv/release.debian.org/tmp/tmpu0boro7_/suricata_7.0.10-1+deb13u2.dsc: no acceptable signature found dpkg-source: warning: cannot verify inline signature for /srv/release.debian.org/tmp/tmpu0boro7_/suricata_7.0.10-1+deb13u3.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 2025-12-10 19:12:20.000000000 +0000 +++ suricata-7.0.10/debian/changelog 2026-02-22 12:28:52.000000000 +0000 @@ -1,3 +1,32 @@ +suricata (1:7.0.10-1+deb13u3) trixie; urgency=medium + + * Fix CVE-2026-22258 in 7.0.10. + Cherry-Picked from: + * f82a388d0283725cb76782cf64e8341cab370830 + * df389f8a43a06c718bb336ea082d6c80d6fefda0 + * c9b80e5affe073ce9d95d0c935a8d67647c83bf7 + * Fix CVE-2026-22262 in 7.0.10. + Cherry-Picked from: + * 32609e6896f9079c175665a94005417cec7637eb + * 27a2180bceaa3477419c78c54fce364398d011f1 + * Fix CVE-2026-22264 in 7.0.10. + Cherry-Picked from 5789a3d3760dbf33d93fc56c27bd9529e5bdc8f2. + * Fix CVE-2026-22259 in 7.0.10. + Cherry-Picked from: + * 63225d5f8ef64cc65164c0bb1800730842d54942 + * 635af8dc8be09667689be71d781912718ca1aa49 + * fdd79bdb14488244604729f1d68ca4bc60000dbd + * a6d950315d9b6c1e35c10c24d9bb7128d422c21f + With this fix, DNP3 has reduced the default maximum number of + outstanding transactions from 500 down to 32. + Read the update instructions for Suricata 7.0.14 for more details. + * Fix CVE-2026-22261 in 7.0.10. + Cherry-Picked from: + * 44d0c81f537f230e9215c769453fb4d7214217a1 + * 7e704a3f50690b5f5d5cc573147ef41449fe37ac + + -- Andreas Dolp Sun, 22 Feb 2026 13:28:52 +0100 + suricata (1:7.0.10-1+deb13u2) trixie; urgency=medium * Fix CVE-2025-64344 in 7.0.10. diff -Nru suricata-7.0.10/debian/patches/CVE-2026-22258_1.patch suricata-7.0.10/debian/patches/CVE-2026-22258_1.patch --- suricata-7.0.10/debian/patches/CVE-2026-22258_1.patch 1970-01-01 00:00:00.000000000 +0000 +++ suricata-7.0.10/debian/patches/CVE-2026-22258_1.patch 2026-02-22 12:21:42.000000000 +0000 @@ -0,0 +1,286 @@ +From f82a388d0283725cb76782cf64e8341cab370830 Mon Sep 17 00:00:00 2001 +From: Shivani Bhardwaj +Date: Tue, 6 Jan 2026 16:44:52 +0530 +Subject: [PATCH 1/3] dcerpc: add upper limit on stub data + +DCERPC parsers had no upper bounds when it came to extending the stub +data buffer. Traffic can be crafted to bypass some internal parser +conditions to create an indefinite buffering in the stub_data array that +can make Suricata crash. + +Add a default limit of 1MiB and make it configurable for the user. + +Security 8182 + +Co-authored-by: Philippe Antoine +(cherry picked from commit e412215af990feeffbb66c7dd9f392813a20ae50) + +Origin: upstream, https://github.com/OISF/suricata/commit/f82a388d0283725cb76782cf64e8341cab370830.patch +Bug: https://redmine.openinfosecfoundation.org/issues/8182 +Subject: Upstream fix for CVE-2026-22258 part 1 +--- + rust/src/dcerpc/dcerpc.rs | 31 +++++++++++++++++++++++++++++-- + rust/src/dcerpc/dcerpc_udp.rs | 18 +++++++++++++----- + rust/src/smb/dcerpc.rs | 31 +++++++++++++++++++++++-------- + rust/src/smb/smb.rs | 23 +++++++++++++++++++++++ + suricata.yaml.in | 4 ++++ + 5 files changed, 92 insertions(+), 15 deletions(-) + +diff --git a/rust/src/dcerpc/dcerpc.rs b/rust/src/dcerpc/dcerpc.rs +index 5469b736b..7297ff82e 100644 +--- a/rust/src/dcerpc/dcerpc.rs ++++ b/rust/src/dcerpc/dcerpc.rs +@@ -25,7 +25,9 @@ use std; + use std::cmp; + use std::ffi::CString; + use std::collections::VecDeque; +-use crate::conf::conf_get; ++use crate::conf::{conf_get, get_memval}; ++ ++pub static mut DCERPC_MAX_STUB_SIZE: u32 = 1048576; + + // Constant DCERPC UDP Header length + pub const DCERPC_HDR_LEN: u16 = 16; +@@ -163,6 +165,11 @@ pub fn get_req_type_for_resp(t: u8) -> u8 { + _ => DCERPC_TYPE_UNKNOWN, + } + } ++#[inline(always)] ++pub fn cfg_max_stub_size() -> u32 { ++ unsafe { DCERPC_MAX_STUB_SIZE } ++} ++ + + #[derive(Default, Debug)] + pub struct DCERPCTransaction { +@@ -1096,7 +1103,12 @@ fn evaluate_stub_params( + } + + let input_slice = &input[..stub_len as usize]; +- stub_data_buffer.extend_from_slice(input_slice); ++ let max_size = cfg_max_stub_size() as usize; ++ if (stub_data_buffer.len() + input_slice.len()) < max_size { ++ stub_data_buffer.extend_from_slice(input_slice); ++ } else if stub_data_buffer.len() < max_size { ++ stub_data_buffer.extend_from_slice(&input_slice[..max_size - stub_data_buffer.len()]); ++ } + + stub_len + } +@@ -1396,6 +1408,21 @@ pub unsafe extern "C" fn rs_dcerpc_register_parser() { + } + } + SCLogDebug!("Rust DCERPC parser registered."); ++ let retval = conf_get("app-layer.protocols.dcerpc.max-stub-size"); ++ if let Some(val) = retval { ++ match get_memval(val) { ++ Ok(retval) => { ++ if retval > 0 { ++ DCERPC_MAX_STUB_SIZE = retval as u32; ++ } else { ++ SCLogError!("Invalid max-stub-size value"); ++ } ++ } ++ Err(_) => { ++ SCLogError!("Invalid max-stub-size value"); ++ } ++ } ++ } + } else { + SCLogDebug!("Protocol detector and parser disabled for DCERPC."); + } +diff --git a/rust/src/dcerpc/dcerpc_udp.rs b/rust/src/dcerpc/dcerpc_udp.rs +index d70ca1b53..0a6213a87 100644 +--- a/rust/src/dcerpc/dcerpc_udp.rs ++++ b/rust/src/dcerpc/dcerpc_udp.rs +@@ -1,4 +1,4 @@ +-/* Copyright (C) 2020 Open Information Security Foundation ++/* Copyright (C) 2020-2026 Open Information Security Foundation + * + * You can copy, redistribute or modify this Program under the terms of + * the GNU General Public License version 2 as published by the Free +@@ -19,7 +19,7 @@ use crate::applayer::{self, *}; + use crate::core::{self, Direction, DIR_BOTH}; + use crate::dcerpc::dcerpc::{ + DCERPCTransaction, DCERPC_MAX_TX, DCERPC_TYPE_REQUEST, DCERPC_TYPE_RESPONSE, PFCL1_FRAG, PFCL1_LASTFRAG, +- rs_dcerpc_get_alstate_progress, ALPROTO_DCERPC, PARSER_NAME, ++ rs_dcerpc_get_alstate_progress, ALPROTO_DCERPC, PARSER_NAME, cfg_max_stub_size, + }; + use nom7::Err; + use std; +@@ -169,18 +169,27 @@ impl DCERPCUDPState { + tx.tx_data.updated_ts = true; + let done = (hdr.flags1 & PFCL1_FRAG) == 0 || (hdr.flags1 & PFCL1_LASTFRAG) != 0; + ++ let max_size = cfg_max_stub_size() as usize; + match hdr.pkt_type { + DCERPC_TYPE_REQUEST => { +- tx.stub_data_buffer_ts.extend_from_slice(input); + tx.frag_cnt_ts += 1; ++ if input.len() + tx.stub_data_buffer_ts.len() < max_size { ++ tx.stub_data_buffer_ts.extend_from_slice(input); ++ } else if tx.stub_data_buffer_ts.len() < max_size { ++ tx.stub_data_buffer_ts.extend_from_slice(&input[..max_size - tx.stub_data_buffer_ts.len()]); ++ } + if done { + tx.req_done = true; + } + return true; + } + DCERPC_TYPE_RESPONSE => { +- tx.stub_data_buffer_tc.extend_from_slice(input); + tx.frag_cnt_tc += 1; ++ if input.len() + tx.stub_data_buffer_tc.len() < max_size { ++ tx.stub_data_buffer_tc.extend_from_slice(input); ++ } else if tx.stub_data_buffer_tc.len() < max_size { ++ tx.stub_data_buffer_tc.extend_from_slice(&input[..max_size - tx.stub_data_buffer_tc.len()]); ++ } + if done { + tx.resp_done = true; + } +@@ -397,7 +406,6 @@ pub unsafe extern "C" fn rs_dcerpc_udp_register_parser() { + } + } + +- + #[cfg(test)] + mod tests { + use crate::applayer::AppLayerResult; +diff --git a/rust/src/smb/dcerpc.rs b/rust/src/smb/dcerpc.rs +index 6c2a2f934..1e62241bb 100644 +--- a/rust/src/smb/dcerpc.rs ++++ b/rust/src/smb/dcerpc.rs +@@ -18,7 +18,7 @@ + // written by Victor Julien + + use uuid; +-use crate::smb::smb::*; ++use crate::smb::smb::{cfg_max_stub_size, *}; + use crate::smb::smb2::*; + use crate::smb::dcerpc_records::*; + use crate::smb::events::*; +@@ -205,10 +205,15 @@ pub fn smb_write_dcerpc_record(state: &mut SMBState, + SCLogDebug!("previous CMD {} found at tx {} => {:?}", + dcer.packet_type, tx.id, tx); + if let Some(SMBTransactionTypeData::DCERPC(ref mut tdn)) = tx.type_data { +- SCLogDebug!("additional frag of size {}", recr.data.len()); +- tdn.stub_data_ts.extend_from_slice(recr.data); + tdn.frag_cnt_ts += 1; +- SCLogDebug!("stub_data now {}", tdn.stub_data_ts.len()); ++ let max_size = cfg_max_stub_size() as usize; ++ if recr.data.len() + tdn.stub_data_ts.len() < max_size { ++ SCLogDebug!("additional frag of size {}", recr.data.len()); ++ tdn.stub_data_ts.extend_from_slice(recr.data); ++ SCLogDebug!("stub_data now {}", tdn.stub_data_ts.len()); ++ } else if tdn.stub_data_ts.len() < max_size { ++ tdn.stub_data_ts.extend_from_slice(&recr.data[..max_size - tdn.stub_data_ts.len()]); ++ } + } + if dcer.last_frag { + SCLogDebug!("last frag set, so request side of DCERPC closed"); +@@ -240,12 +245,17 @@ pub fn smb_write_dcerpc_record(state: &mut SMBState, + SCLogDebug!("DCERPC: REQUEST {:?}", recr); + if let Some(SMBTransactionTypeData::DCERPC(ref mut tdn)) = tx.type_data { + SCLogDebug!("first frag size {}", recr.data.len()); +- tdn.stub_data_ts.extend_from_slice(recr.data); + tdn.opnum = recr.opnum; + tdn.context_id = recr.context_id; + tdn.frag_cnt_ts += 1; +- SCLogDebug!("DCERPC: REQUEST opnum {} stub data len {}", +- tdn.opnum, tdn.stub_data_ts.len()); ++ let max_size = cfg_max_stub_size() as usize; ++ if tdn.stub_data_ts.len() + recr.data.len() < max_size { ++ tdn.stub_data_ts.extend_from_slice(recr.data); ++ SCLogDebug!("DCERPC: REQUEST opnum {} stub data len {}", ++ tdn.opnum, tdn.stub_data_ts.len()); ++ } else if tdn.stub_data_ts.len() < max_size { ++ tdn.stub_data_ts.extend_from_slice(&recr.data[..max_size - tdn.stub_data_ts.len()]); ++ } + } + if dcer.last_frag { + tx.request_done = true; +@@ -407,8 +417,13 @@ fn dcerpc_response_handle(tx: &mut SMBTransaction, + if let Some(SMBTransactionTypeData::DCERPC(ref mut tdn)) = tx.type_data { + SCLogDebug!("CMD 11 found at tx {}", tx.id); + tdn.set_result(DCERPC_TYPE_RESPONSE); +- tdn.stub_data_tc.extend_from_slice(respr.data); ++ let max_size = cfg_max_stub_size() as usize; + tdn.frag_cnt_tc += 1; ++ if tdn.stub_data_tc.len() + respr.data.len() < max_size { ++ tdn.stub_data_tc.extend_from_slice(respr.data); ++ } else if tdn.stub_data_tc.len() < max_size { ++ tdn.stub_data_tc.extend_from_slice(&respr.data[..max_size - tdn.stub_data_tc.len()]); ++ } + } + tx.vercmd.set_ntstatus(ntstatus); + tx.response_done = dcer.last_frag; +diff --git a/rust/src/smb/smb.rs b/rust/src/smb/smb.rs +index c22bc9fc9..0a0fc1082 100644 +--- a/rust/src/smb/smb.rs ++++ b/rust/src/smb/smb.rs +@@ -81,6 +81,8 @@ pub static mut SMB_CFG_MAX_WRITE_SIZE: u32 = 16777216; + pub static mut SMB_CFG_MAX_WRITE_QUEUE_SIZE: u32 = 67108864; + pub static mut SMB_CFG_MAX_WRITE_QUEUE_CNT: u32 = 64; + ++pub static mut SMB_DCERPC_MAX_STUB_SIZE: u32 = 1048576; ++ + static mut ALPROTO_SMB: AppProto = ALPROTO_UNKNOWN; + + static mut SMB_MAX_TX: usize = 1024; +@@ -2438,6 +2440,21 @@ pub unsafe extern "C" fn rs_smb_register_parser() { + SCLogError!("Invalid value for smb.max-tx"); + } + } ++ let retval = conf_get("app-layer.protocols.smb.dcerpc.max-stub-size"); ++ if let Some(val) = retval { ++ match get_memval(val) { ++ Ok(retval) => { ++ if retval > 0 { ++ SMB_DCERPC_MAX_STUB_SIZE = retval as u32; ++ } else { ++ SCLogError!("Invalid max-stub-size value"); ++ } ++ } ++ Err(_) => { ++ SCLogError!("Invalid max-stub-size value"); ++ } ++ } ++ } + SCLogConfig!("read: max record size: {}, max queued chunks {}, max queued size {}", + SMB_CFG_MAX_READ_SIZE, SMB_CFG_MAX_READ_QUEUE_CNT, SMB_CFG_MAX_READ_QUEUE_SIZE); + SCLogConfig!("write: max record size: {}, max queued chunks {}, max queued size {}", +@@ -2446,3 +2463,9 @@ pub unsafe extern "C" fn rs_smb_register_parser() { + SCLogDebug!("Protocol detector and parser disabled for SMB."); + } + } ++ ++#[inline(always)] ++pub fn cfg_max_stub_size() -> u32 { ++ unsafe { SMB_DCERPC_MAX_STUB_SIZE } ++} ++ +diff --git a/suricata.yaml.in b/suricata.yaml.in +index 7640f2b62..eab6ca500 100644 +--- a/suricata.yaml.in ++++ b/suricata.yaml.in +@@ -951,6 +951,8 @@ app-layer: + enabled: yes + # Maximum number of live DCERPC transactions per flow + # max-tx: 1024 ++ #max-stub-size: 1MiB ++ + ftp: + enabled: yes + # memcap: 64mb +@@ -1015,6 +1017,8 @@ app-layer: + + # Stream reassembly size for SMB streams. By default track it completely. + #stream-depth: 0 ++ #dcerpc: ++ # max-stub-size: 1MiB + + nfs: + enabled: yes +-- +2.47.3 + diff -Nru suricata-7.0.10/debian/patches/CVE-2026-22258_2.patch suricata-7.0.10/debian/patches/CVE-2026-22258_2.patch --- suricata-7.0.10/debian/patches/CVE-2026-22258_2.patch 1970-01-01 00:00:00.000000000 +0000 +++ suricata-7.0.10/debian/patches/CVE-2026-22258_2.patch 2026-02-22 12:21:42.000000000 +0000 @@ -0,0 +1,54 @@ +From df389f8a43a06c718bb336ea082d6c80d6fefda0 Mon Sep 17 00:00:00 2001 +From: Shivani Bhardwaj +Date: Wed, 7 Jan 2026 10:33:57 +0530 +Subject: [PATCH 2/3] doc: add dcerpc.max-stub-size config param + +(cherry picked from commit 6702791a9c4463858c8b54ee8678fd4f5fbe831a) + +Origin: upstream, https://github.com/OISF/suricata/commit/df389f8a43a06c718bb336ea082d6c80d6fefda0.patch +Bug: https://redmine.openinfosecfoundation.org/issues/8182 +Subject: Upstream fix for CVE-2026-22258 part 2 +--- + doc/userguide/configuration/suricata-yaml.rst | 12 ++++++++++++ + 1 file changed, 12 insertions(+) + +diff --git a/doc/userguide/configuration/suricata-yaml.rst b/doc/userguide/configuration/suricata-yaml.rst +index bd414fbc6..8ea093b51 100644 +--- a/doc/userguide/configuration/suricata-yaml.rst ++++ b/doc/userguide/configuration/suricata-yaml.rst +@@ -1674,6 +1674,9 @@ Several options are available for limiting record sizes and data chunk tracking. + max-write-queue-size: 16mb + max-write-queue-cnt: 16 + ++ dcerpc: ++ max-stub-size: 1MiB ++ + The `max-read-size` option can be set to control the max size of accepted + READ records. Events will be raised if a READ request asks for too much data + and/or if READ responses are too big. A value of 0 disables the checks. +@@ -1685,6 +1688,8 @@ data. A value of 0 disables the checks. + Additionally if the `max-read-size` or `max-write-size` values in the + "negotiate protocol response" exceeds this limit an event will also be raised. + ++To control the size of the DCERPC stub data, `dcerpc.max-stub-size` should be ++used. It is by default set to 1MiB. + + For file tracking, extraction and file data inspection the parser queues up + out of order data chunks for both READs and WRITEs. To avoid using too much +@@ -1710,6 +1715,13 @@ the limits are exceeded, and an event will be raised. + `max-write-queue-size` and `max-write-queue-cnt` are as the READ variants, + but then for WRITEs. + ++Configure DCERPC ++~~~~~~~~~~~~~~~~ ++ ++DCERPC has one parameter that can be customized. ++`max-stub-size` is used to control the stub data size of a DCERPC request/response. By ++default, it is set to 1MiB. ++ + Configure HTTP2 + ~~~~~~~~~~~~~~~ + +-- +2.47.3 + diff -Nru suricata-7.0.10/debian/patches/CVE-2026-22258_3.patch suricata-7.0.10/debian/patches/CVE-2026-22258_3.patch --- suricata-7.0.10/debian/patches/CVE-2026-22258_3.patch 1970-01-01 00:00:00.000000000 +0000 +++ suricata-7.0.10/debian/patches/CVE-2026-22258_3.patch 2026-02-22 12:21:42.000000000 +0000 @@ -0,0 +1,73 @@ +From c9b80e5affe073ce9d95d0c935a8d67647c83bf7 Mon Sep 17 00:00:00 2001 +From: Philippe Antoine +Date: Thu, 8 Jan 2026 14:48:40 +0100 +Subject: [PATCH 3/3] dcerpc: use saturating_add to count fragments + +And do not overflow if we have traffic with more than 65K fragments + +(cherry picked from commit a48200b9e5befb1f0aa45ad5b33e2664e6a9fa41) + +Origin: upstream, https://github.com/OISF/suricata/commit/c9b80e5affe073ce9d95d0c935a8d67647c83bf7.patch +Bug: https://redmine.openinfosecfoundation.org/issues/8182 +Subject: Upstream fix for CVE-2026-22258 part 3 +--- + rust/src/dcerpc/dcerpc_udp.rs | 4 ++-- + rust/src/smb/dcerpc.rs | 6 +++--- + 2 files changed, 5 insertions(+), 5 deletions(-) + +diff --git a/rust/src/dcerpc/dcerpc_udp.rs b/rust/src/dcerpc/dcerpc_udp.rs +index 0a6213a87..87fa9764b 100644 +--- a/rust/src/dcerpc/dcerpc_udp.rs ++++ b/rust/src/dcerpc/dcerpc_udp.rs +@@ -172,7 +172,7 @@ impl DCERPCUDPState { + let max_size = cfg_max_stub_size() as usize; + match hdr.pkt_type { + DCERPC_TYPE_REQUEST => { +- tx.frag_cnt_ts += 1; ++ tx.frag_cnt_ts = tx.frag_cnt_ts.saturating_add(1); + if input.len() + tx.stub_data_buffer_ts.len() < max_size { + tx.stub_data_buffer_ts.extend_from_slice(input); + } else if tx.stub_data_buffer_ts.len() < max_size { +@@ -184,7 +184,7 @@ impl DCERPCUDPState { + return true; + } + DCERPC_TYPE_RESPONSE => { +- tx.frag_cnt_tc += 1; ++ tx.frag_cnt_tc = tx.frag_cnt_tc.saturating_add(1); + if input.len() + tx.stub_data_buffer_tc.len() < max_size { + tx.stub_data_buffer_tc.extend_from_slice(input); + } else if tx.stub_data_buffer_tc.len() < max_size { +diff --git a/rust/src/smb/dcerpc.rs b/rust/src/smb/dcerpc.rs +index 1e62241bb..5cb1adeba 100644 +--- a/rust/src/smb/dcerpc.rs ++++ b/rust/src/smb/dcerpc.rs +@@ -205,7 +205,7 @@ pub fn smb_write_dcerpc_record(state: &mut SMBState, + SCLogDebug!("previous CMD {} found at tx {} => {:?}", + dcer.packet_type, tx.id, tx); + if let Some(SMBTransactionTypeData::DCERPC(ref mut tdn)) = tx.type_data { +- tdn.frag_cnt_ts += 1; ++ tdn.frag_cnt_ts = tdn.frag_cnt_ts.saturating_add(1); + let max_size = cfg_max_stub_size() as usize; + if recr.data.len() + tdn.stub_data_ts.len() < max_size { + SCLogDebug!("additional frag of size {}", recr.data.len()); +@@ -247,7 +247,7 @@ pub fn smb_write_dcerpc_record(state: &mut SMBState, + SCLogDebug!("first frag size {}", recr.data.len()); + tdn.opnum = recr.opnum; + tdn.context_id = recr.context_id; +- tdn.frag_cnt_ts += 1; ++ tdn.frag_cnt_ts = tdn.frag_cnt_ts.saturating_add(1); + let max_size = cfg_max_stub_size() as usize; + if tdn.stub_data_ts.len() + recr.data.len() < max_size { + tdn.stub_data_ts.extend_from_slice(recr.data); +@@ -418,7 +418,7 @@ fn dcerpc_response_handle(tx: &mut SMBTransaction, + SCLogDebug!("CMD 11 found at tx {}", tx.id); + tdn.set_result(DCERPC_TYPE_RESPONSE); + let max_size = cfg_max_stub_size() as usize; +- tdn.frag_cnt_tc += 1; ++ tdn.frag_cnt_tc = tdn.frag_cnt_tc.saturating_add(1); + if tdn.stub_data_tc.len() + respr.data.len() < max_size { + tdn.stub_data_tc.extend_from_slice(respr.data); + } else if tdn.stub_data_tc.len() < max_size { +-- +2.47.3 + diff -Nru suricata-7.0.10/debian/patches/CVE-2026-22259_1.patch suricata-7.0.10/debian/patches/CVE-2026-22259_1.patch --- suricata-7.0.10/debian/patches/CVE-2026-22259_1.patch 1970-01-01 00:00:00.000000000 +0000 +++ suricata-7.0.10/debian/patches/CVE-2026-22259_1.patch 2026-02-22 12:21:42.000000000 +0000 @@ -0,0 +1,38 @@ +From 63225d5f8ef64cc65164c0bb1800730842d54942 Mon Sep 17 00:00:00 2001 +From: Jason Ish +Date: Tue, 6 Jan 2026 16:15:09 -0600 +Subject: [PATCH 1/4] dnp3: check done state, not complete state for progress + +Complete is a flag used to tell if the message was completely parsed, +as not all messages may be completely parsed if we don't know all +their objects. However, they are still "done". + +In the alstate-progress callback, check the done flag, not the +complete flag. + +Ticket: #8181 +(cherry picked from commit d61eef9a8a0d92921989479de15e5cbfec3251a9) + +Origin: upstream, https://github.com/OISF/suricata/commit/63225d5f8ef64cc65164c0bb1800730842d54942.patch +Bug: https://redmine.openinfosecfoundation.org/issues/8181 +Subject: Upstream fix for CVE-2026-22259 part 1 +--- + src/app-layer-dnp3.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/src/app-layer-dnp3.c b/src/app-layer-dnp3.c +index ecae4ae63..1a8b3cb63 100644 +--- a/src/app-layer-dnp3.c ++++ b/src/app-layer-dnp3.c +@@ -1438,7 +1438,7 @@ static int DNP3GetAlstateProgress(void *tx, uint8_t direction) + SCReturnInt(1); + } + +- if (dnp3tx->complete) ++ if (dnp3tx->done) + retval = 1; + + SCReturnInt(retval); +-- +2.47.3 + diff -Nru suricata-7.0.10/debian/patches/CVE-2026-22259_2.patch suricata-7.0.10/debian/patches/CVE-2026-22259_2.patch --- suricata-7.0.10/debian/patches/CVE-2026-22259_2.patch 1970-01-01 00:00:00.000000000 +0000 +++ suricata-7.0.10/debian/patches/CVE-2026-22259_2.patch 2026-02-22 12:21:42.000000000 +0000 @@ -0,0 +1,135 @@ +From 635af8dc8be09667689be71d781912718ca1aa49 Mon Sep 17 00:00:00 2001 +From: Jason Ish +Date: Tue, 6 Jan 2026 11:06:40 -0600 +Subject: [PATCH 2/4] dnp3: reduce flood threshold to 32 and make configurable + +Lower the number of unreplied requests from 500 to 32 to consider a +flood. At the very least this is an anomaly given the DNP3 spec mentions +that DNP3 should only have one outstanding request at a time, with an +exception for unsolicited responses, so in practice no more than 2 +should be seen. + +Additionally make this value configurable by introducing the max-tx +parameter. + +Ticket: #8181 +(cherry picked from commit a16f087b93be1ff2f2edf47371866ad9b28593c1) + +Origin: upstream, https://github.com/OISF/suricata/commit/635af8dc8be09667689be71d781912718ca1aa49.patch +Bug: https://redmine.openinfosecfoundation.org/issues/8181 +Subject: Upstream fix for CVE-2026-22259 part 2 +--- + doc/userguide/upgrade.rst | 10 ++++++++++ + src/app-layer-dnp3.c | 29 +++++++++++++++++++---------- + suricata.yaml.in | 1 + + 3 files changed, 30 insertions(+), 10 deletions(-) + +--- a/doc/userguide/upgrade.rst ++++ b/doc/userguide/upgrade.rst +@@ -34,6 +34,16 @@ + this guide. Those features are either not enabled by default or require + dedicated new configuration. + ++Upgrading to 7.0.14 (trixie-security 1:7.0.10-1~bpo13u3) ++------------------- ++ ++Other Changes ++~~~~~~~~~~~~~ ++- ``dnp3`` has reduced the default maximum number of outstanding ++ transactions from 500 down to 32. A ``max-tx`` parameter has been ++ added to the ``dnp3`` parser for users that need a larger number of ++ in-flight transactions. ++ + Upgrading to 7.0.9 + ------------------ + - The AF_PACKET default block size for both TPACKET_V2 and TPACKET_V3 +--- a/src/app-layer-dnp3.c ++++ b/src/app-layer-dnp3.c +@@ -40,9 +40,6 @@ + #include "app-layer-dnp3.h" + #include "app-layer-dnp3-objects.h" + +-/* Default number of unreplied requests to be considered a flood. */ +-#define DNP3_DEFAULT_REQ_FLOOD_COUNT 500 +- + #define DNP3_DEFAULT_PORT "20000" + + /* Expected values for the start bytes. */ +@@ -93,6 +90,14 @@ + /* Extract the range code from the object qualifier. */ + #define DNP3_OBJ_RANGE(x) (x & 0xf) + ++/* Default number of unreplied requests to be considered a flood. ++ * ++ * DNP3 is a request/response SCADA protocol with typically only 1-2 ++ * transactions in flight. But set a limit high enough to allow for ++ * some pipelining but reduce the chance of memory exhaustion ++ * attacks. */ ++static uint64_t dnp3_max_tx = 32; ++ + /* Decoder event map. */ + SCEnumCharMap dnp3_decoder_event_table[] = { + {"FLOODED", DNP3_DECODER_EVENT_FLOODED}, +@@ -514,7 +519,7 @@ + TAILQ_INSERT_TAIL(&dnp3->tx_list, tx, next); + + /* Check for flood state. */ +- if (dnp3->unreplied > DNP3_DEFAULT_REQ_FLOOD_COUNT) { ++ if (dnp3->unreplied > dnp3_max_tx && !dnp3->flooded) { + DNP3SetEvent(dnp3, DNP3_DECODER_EVENT_FLOODED); + dnp3->flooded = 1; + } +@@ -1384,7 +1389,7 @@ + dnp3->unreplied--; + + /* Check flood state. */ +- if (dnp3->flooded && dnp3->unreplied < DNP3_DEFAULT_REQ_FLOOD_COUNT) { ++ if (dnp3->flooded && dnp3->unreplied < dnp3_max_tx) { + dnp3->flooded = 0; + } + +@@ -1430,8 +1435,7 @@ + int retval = 0; + + /* If flooded, "ack" old transactions. */ +- if (dnp3->flooded && (dnp3->transaction_max - +- dnp3tx->tx_num >= DNP3_DEFAULT_REQ_FLOOD_COUNT)) { ++ if (dnp3->flooded && (dnp3->transaction_max - dnp3tx->tx_num >= dnp3_max_tx)) { + SCLogDebug("flooded: returning tx as done."); + SCReturnInt(1); + } +@@ -1604,8 +1608,13 @@ + AppLayerParserRegisterTxDataFunc(IPPROTO_TCP, ALPROTO_DNP3, + DNP3GetTxData); + AppLayerParserRegisterStateDataFunc(IPPROTO_TCP, ALPROTO_DNP3, DNP3GetStateData); +- } +- else { ++ ++ /* Parse max-tx configuration. */ ++ intmax_t value = 0; ++ if (ConfGetInt("app-layer.protocols.dnp3.max-tx", &value)) { ++ dnp3_max_tx = (uint64_t)value; ++ } ++ } else { + SCLogConfig("Parser disabled for protocol %s. " + "Protocol detection still on.", proto_name); + } +@@ -2252,7 +2261,7 @@ + FAIL_IF_NOT(tx->done); + FAIL_IF_NOT(DNP3GetAlstateProgress(tx, STREAM_TOSERVER)); + +- for (int i = 0; i < DNP3_DEFAULT_REQ_FLOOD_COUNT - 1; i++) { ++ for (uint64_t i = 0; i < dnp3_max_tx - 1; i++) { + SCMutexLock(&flow.m); + FAIL_IF(AppLayerParserParse(NULL, alp_tctx, &flow, ALPROTO_DNP3, + STREAM_TOSERVER, request, sizeof(request))); +--- a/suricata.yaml.in ++++ b/suricata.yaml.in +@@ -1161,6 +1161,7 @@ + enabled: no + detection-ports: + dp: 20000 ++ #max-tx: 32 + + # SCADA EtherNet/IP and CIP protocol support + enip: diff -Nru suricata-7.0.10/debian/patches/CVE-2026-22259_3.patch suricata-7.0.10/debian/patches/CVE-2026-22259_3.patch --- suricata-7.0.10/debian/patches/CVE-2026-22259_3.patch 1970-01-01 00:00:00.000000000 +0000 +++ suricata-7.0.10/debian/patches/CVE-2026-22259_3.patch 2026-02-22 12:21:42.000000000 +0000 @@ -0,0 +1,138 @@ +From fdd79bdb14488244604729f1d68ca4bc60000dbd Mon Sep 17 00:00:00 2001 +From: Jason Ish +Date: Wed, 7 Jan 2026 09:23:09 -0600 +Subject: [PATCH 3/4] dnp3: set a bound on the number of points per message + +16384 is used as the max, but a configuration parameter has been +provided. The reason for setting an upper bound is that bit flags can +create a memory amplification as we parse them into individual data +structures. + +Ticket: #8181 +(cherry picked from commit 3a32bb5743c35afb3278a6448f7e9669512dbe92) + +Origin: upstream, https://github.com/OISF/suricata/commit/fdd79bdb14488244604729f1d68ca4bc60000dbd.patch +Bug: https://redmine.openinfosecfoundation.org/issues/8181 +Subject: Upstream fix for CVE-2026-22259 part 3 +--- + doc/userguide/upgrade.rst | 9 +++++---- + rules/dnp3-events.rules | 5 +++++ + src/app-layer-dnp3.c | 33 ++++++++++++++++++++++++++------- + src/app-layer-dnp3.h | 1 + + suricata.yaml.in | 1 + + 5 files changed, 38 insertions(+), 11 deletions(-) + +--- a/doc/userguide/upgrade.rst ++++ b/doc/userguide/upgrade.rst +@@ -39,10 +39,11 @@ + + Other Changes + ~~~~~~~~~~~~~ +-- ``dnp3`` has reduced the default maximum number of outstanding +- transactions from 500 down to 32. A ``max-tx`` parameter has been +- added to the ``dnp3`` parser for users that need a larger number of +- in-flight transactions. ++- ``dnp3`` has reduced the maximum number of open transactions from ++ 500 down to 32, and the maximum number of points per message from ++ unbounded to 16384. Configuration options, ``max-tx`` and ++ ``max-points`` have been added for users who may need to change ++ these defaults. + + Upgrading to 7.0.9 + ------------------ +--- a/rules/dnp3-events.rules ++++ b/rules/dnp3-events.rules +@@ -24,3 +24,8 @@ + # Unknown object. + alert dnp3 any any -> any any (msg:"SURICATA DNP3 Unknown object"; \ + app-layer-event:dnp3.unknown_object; classtype:protocol-command-decode; sid:2270004; rev:2;) ++ ++# Too many points in a message. ++alert dnp3 any any -> any any (msg:"SURICATA DNP3 Too many points in message"; \ ++ app-layer-event:dnp3.too_many_points; \ ++ classtype:protocol-command-decode; sid:2270005; rev:1;) +--- a/src/app-layer-dnp3.c ++++ b/src/app-layer-dnp3.c +@@ -98,15 +98,19 @@ + * attacks. */ + static uint64_t dnp3_max_tx = 32; + ++/* The maximum number of points allowed per message (configurable). */ ++static uint64_t max_points = 16384; ++ + /* Decoder event map. */ + SCEnumCharMap dnp3_decoder_event_table[] = { +- {"FLOODED", DNP3_DECODER_EVENT_FLOODED}, +- {"LEN_TOO_SMALL", DNP3_DECODER_EVENT_LEN_TOO_SMALL}, +- {"BAD_LINK_CRC", DNP3_DECODER_EVENT_BAD_LINK_CRC}, +- {"BAD_TRANSPORT_CRC", DNP3_DECODER_EVENT_BAD_TRANSPORT_CRC}, +- {"MALFORMED", DNP3_DECODER_EVENT_MALFORMED}, +- {"UNKNOWN_OBJECT", DNP3_DECODER_EVENT_UNKNOWN_OBJECT}, +- {NULL, -1}, ++ { "FLOODED", DNP3_DECODER_EVENT_FLOODED }, ++ { "LEN_TOO_SMALL", DNP3_DECODER_EVENT_LEN_TOO_SMALL }, ++ { "BAD_LINK_CRC", DNP3_DECODER_EVENT_BAD_LINK_CRC }, ++ { "BAD_TRANSPORT_CRC", DNP3_DECODER_EVENT_BAD_TRANSPORT_CRC }, ++ { "MALFORMED", DNP3_DECODER_EVENT_MALFORMED }, ++ { "UNKNOWN_OBJECT", DNP3_DECODER_EVENT_UNKNOWN_OBJECT }, ++ { "TOO_MANY_POINTS", DNP3_DECODER_EVENT_TOO_MANY_POINTS }, ++ { NULL, -1 }, + }; + + /* Calculate the next transport sequence number. */ +@@ -709,6 +713,7 @@ + uint32_t len, DNP3ObjectList *objects) + { + int retval = 0; ++ uint64_t point_count = 0; + + if (buf == NULL || len == 0) { + return 1; +@@ -839,6 +844,13 @@ + goto next; + } + ++ /* Check if we've exceeded the maximum number of points per message. */ ++ point_count += object->count; ++ if (point_count > max_points) { ++ DNP3SetEventTx(tx, DNP3_DECODER_EVENT_TOO_MANY_POINTS); ++ goto done; ++ } ++ + int event = DNP3DecodeObject(header->group, header->variation, &buf, + &len, object->prefix_code, object->start, object->count, + object->points); +@@ -1614,6 +1626,13 @@ + if (ConfGetInt("app-layer.protocols.dnp3.max-tx", &value)) { + dnp3_max_tx = (uint64_t)value; + } ++ ++ /* Parse max-points configuration. */ ++ if (ConfGetInt("app-layer.protocols.dnp3.max-points", &value)) { ++ if (value > 0) { ++ max_points = (uint64_t)value; ++ } ++ } + } else { + SCLogConfig("Parser disabled for protocol %s. " + "Protocol detection still on.", proto_name); +--- a/src/app-layer-dnp3.h ++++ b/src/app-layer-dnp3.h +@@ -109,6 +109,7 @@ + DNP3_DECODER_EVENT_BAD_TRANSPORT_CRC, + DNP3_DECODER_EVENT_MALFORMED, + DNP3_DECODER_EVENT_UNKNOWN_OBJECT, ++ DNP3_DECODER_EVENT_TOO_MANY_POINTS, + }; + + /** +--- a/suricata.yaml.in ++++ b/suricata.yaml.in +@@ -1162,6 +1162,7 @@ + detection-ports: + dp: 20000 + #max-tx: 32 ++ #max-points: 16384 + + # SCADA EtherNet/IP and CIP protocol support + enip: diff -Nru suricata-7.0.10/debian/patches/CVE-2026-22259_4.patch suricata-7.0.10/debian/patches/CVE-2026-22259_4.patch --- suricata-7.0.10/debian/patches/CVE-2026-22259_4.patch 1970-01-01 00:00:00.000000000 +0000 +++ suricata-7.0.10/debian/patches/CVE-2026-22259_4.patch 2026-02-22 12:21:42.000000000 +0000 @@ -0,0 +1,125 @@ +From a6d950315d9b6c1e35c10c24d9bb7128d422c21f Mon Sep 17 00:00:00 2001 +From: Jason Ish +Date: Tue, 6 Jan 2026 17:14:21 -0600 +Subject: [PATCH 4/4] dnp3: bound the maximum number of objects per tx + +Default to 2048, but provide a user configuration value. + +Ticket: #8181 +(cherry picked from commit 2c95f1ff44e17c3bc8693d5e23e175f2bc90ea10) + +Origin: upstream, https://github.com/OISF/suricata/commit/a6d950315d9b6c1e35c10c24d9bb7128d422c21f.patch +Bug: https://redmine.openinfosecfoundation.org/issues/8181 +Subject: Upstream fix for CVE-2026-22259 part 4 +--- + doc/userguide/upgrade.rst | 9 +++++---- + rules/dnp3-events.rules | 5 +++++ + src/app-layer-dnp3.c | 18 ++++++++++++++++++ + src/app-layer-dnp3.h | 1 + + suricata.yaml.in | 1 + + 5 files changed, 30 insertions(+), 4 deletions(-) + +--- a/doc/userguide/upgrade.rst ++++ b/doc/userguide/upgrade.rst +@@ -40,10 +40,11 @@ + Other Changes + ~~~~~~~~~~~~~ + - ``dnp3`` has reduced the maximum number of open transactions from +- 500 down to 32, and the maximum number of points per message from +- unbounded to 16384. Configuration options, ``max-tx`` and +- ``max-points`` have been added for users who may need to change +- these defaults. ++ 500 down to 32, the maximum number of points per message from ++ unbounded to 16384, and the maximum number of objects per message ++ from unbounded to 2048. Configuration options, ``max-tx``, ++ ``max-points``, and ``max-objects`` have been added for users who ++ may need to change these defaults. + + Upgrading to 7.0.9 + ------------------ +--- a/rules/dnp3-events.rules ++++ b/rules/dnp3-events.rules +@@ -29,3 +29,8 @@ + alert dnp3 any any -> any any (msg:"SURICATA DNP3 Too many points in message"; \ + app-layer-event:dnp3.too_many_points; \ + classtype:protocol-command-decode; sid:2270005; rev:1;) ++ ++# Too many objects. ++alert dnp3 any any -> any any (msg:"SURICATA DNP3 Too many objects"; \ ++ app-layer-event:dnp3.too_many_objects; \ ++ classtype:protocol-command-decode; sid:2270006; rev:1;) +--- a/src/app-layer-dnp3.c ++++ b/src/app-layer-dnp3.c +@@ -101,6 +101,9 @@ + /* The maximum number of points allowed per message (configurable). */ + static uint64_t max_points = 16384; + ++/* The maximum number of objects allowed per message (configurable). */ ++static uint64_t dnp3_max_objects = 2048; ++ + /* Decoder event map. */ + SCEnumCharMap dnp3_decoder_event_table[] = { + { "FLOODED", DNP3_DECODER_EVENT_FLOODED }, +@@ -110,6 +113,7 @@ + { "MALFORMED", DNP3_DECODER_EVENT_MALFORMED }, + { "UNKNOWN_OBJECT", DNP3_DECODER_EVENT_UNKNOWN_OBJECT }, + { "TOO_MANY_POINTS", DNP3_DECODER_EVENT_TOO_MANY_POINTS }, ++ { "TOO_MANY_OBJECTS", DNP3_DECODER_EVENT_TOO_MANY_OBJECTS }, + { NULL, -1 }, + }; + +@@ -714,6 +718,7 @@ + { + int retval = 0; + uint64_t point_count = 0; ++ uint64_t object_count = 0; + + if (buf == NULL || len == 0) { + return 1; +@@ -728,6 +733,12 @@ + DNP3ObjHeader *header = (DNP3ObjHeader *)buf; + offset += sizeof(DNP3ObjHeader); + ++ /* Check if we've exceeded the maximum number of objects. */ ++ if (++object_count > dnp3_max_objects) { ++ DNP3SetEventTx(tx, DNP3_DECODER_EVENT_TOO_MANY_OBJECTS); ++ goto done; ++ } ++ + DNP3Object *object = DNP3ObjectAlloc(); + if (unlikely(object == NULL)) { + goto done; +@@ -1633,6 +1644,13 @@ + max_points = (uint64_t)value; + } + } ++ ++ /* Parse max-objects configuration. */ ++ if (ConfGetInt("app-layer.protocols.dnp3.max-objects", &value)) { ++ if (value > 0) { ++ dnp3_max_objects = (uint64_t)value; ++ } ++ } + } else { + SCLogConfig("Parser disabled for protocol %s. " + "Protocol detection still on.", proto_name); +--- a/src/app-layer-dnp3.h ++++ b/src/app-layer-dnp3.h +@@ -110,6 +110,7 @@ + DNP3_DECODER_EVENT_MALFORMED, + DNP3_DECODER_EVENT_UNKNOWN_OBJECT, + DNP3_DECODER_EVENT_TOO_MANY_POINTS, ++ DNP3_DECODER_EVENT_TOO_MANY_OBJECTS, + }; + + /** +--- a/suricata.yaml.in ++++ b/suricata.yaml.in +@@ -1163,6 +1163,7 @@ + dp: 20000 + #max-tx: 32 + #max-points: 16384 ++ #max-objects: 2048 + + # SCADA EtherNet/IP and CIP protocol support + enip: diff -Nru suricata-7.0.10/debian/patches/CVE-2026-22261_1.patch suricata-7.0.10/debian/patches/CVE-2026-22261_1.patch --- suricata-7.0.10/debian/patches/CVE-2026-22261_1.patch 1970-01-01 00:00:00.000000000 +0000 +++ suricata-7.0.10/debian/patches/CVE-2026-22261_1.patch 2026-02-22 12:21:42.000000000 +0000 @@ -0,0 +1,68 @@ +From 44d0c81f537f230e9215c769453fb4d7214217a1 Mon Sep 17 00:00:00 2001 +From: Philippe Antoine +Date: Tue, 9 Dec 2025 09:21:58 +0100 +Subject: [PATCH 1/2] output: optimize loop for finding alert http xff + +Ticket: 8156 + +In case of non-tx alerts, we try to loop over all the txs to find +the xff header. Do not start from tx_id 0, but from min_id +as AppLayerParserTransactionsCleanup to skip txs that were freed + +(cherry picked from commit 3b1a6c1711b8f7d0bde4cb05f15cf50c751eda60) + +Origin: upstream, https://github.com/OISF/suricata/commit/44d0c81f537f230e9215c769453fb4d7214217a1.patch +Bug: https://redmine.openinfosecfoundation.org/issues/8156 +Subject: Upstream fix for CVE-2026-22261 part 1 +--- + src/app-layer-htp-xff.c | 2 +- + src/app-layer-parser.c | 7 +++++++ + src/app-layer-parser.h | 1 + + 3 files changed, 9 insertions(+), 1 deletion(-) + +diff --git a/src/app-layer-htp-xff.c b/src/app-layer-htp-xff.c +index c145e5818..6eae5b1b3 100644 +--- a/src/app-layer-htp-xff.c ++++ b/src/app-layer-htp-xff.c +@@ -180,7 +180,7 @@ int HttpXFFGetIPFromTx(const Flow *f, uint64_t tx_id, HttpXFFCfg *xff_cfg, + int HttpXFFGetIP(const Flow *f, HttpXFFCfg *xff_cfg, char *dstbuf, int dstbuflen) + { + HtpState *htp_state = NULL; +- uint64_t tx_id = 0; ++ uint64_t tx_id = AppLayerParserGetMinId(f->alparser); + uint64_t total_txs = 0; + + htp_state = (HtpState *)FlowGetAppState(f); +diff --git a/src/app-layer-parser.c b/src/app-layer-parser.c +index 627bf929a..2b8a59a14 100644 +--- a/src/app-layer-parser.c ++++ b/src/app-layer-parser.c +@@ -721,6 +721,13 @@ uint64_t AppLayerParserGetTransactionLogId(AppLayerParserState *pstate) + SCReturnCT((pstate == NULL) ? 0 : pstate->log_id, "uint64_t"); + } + ++uint64_t AppLayerParserGetMinId(AppLayerParserState *pstate) ++{ ++ SCEnter(); ++ ++ SCReturnCT((pstate == NULL) ? 0 : pstate->min_id, "uint64_t"); ++} ++ + void AppLayerParserSetTransactionLogId(AppLayerParserState *pstate, uint64_t tx_id) + { + SCEnter(); +diff --git a/src/app-layer-parser.h b/src/app-layer-parser.h +index 77e8c813f..dad0b613c 100644 +--- a/src/app-layer-parser.h ++++ b/src/app-layer-parser.h +@@ -230,6 +230,7 @@ void AppLayerParserDestroyProtocolParserLocalStorage(uint8_t ipproto, AppProto a + + + uint64_t AppLayerParserGetTransactionLogId(AppLayerParserState *pstate); ++uint64_t AppLayerParserGetMinId(AppLayerParserState *pstate); + void AppLayerParserSetTransactionLogId(AppLayerParserState *pstate, uint64_t tx_id); + + uint64_t AppLayerParserGetTransactionInspectId(AppLayerParserState *pstate, uint8_t direction); +-- +2.47.3 + diff -Nru suricata-7.0.10/debian/patches/CVE-2026-22261_2.patch suricata-7.0.10/debian/patches/CVE-2026-22261_2.patch --- suricata-7.0.10/debian/patches/CVE-2026-22261_2.patch 1970-01-01 00:00:00.000000000 +0000 +++ suricata-7.0.10/debian/patches/CVE-2026-22261_2.patch 2026-02-22 12:21:42.000000000 +0000 @@ -0,0 +1,128 @@ +From 7e704a3f50690b5f5d5cc573147ef41449fe37ac Mon Sep 17 00:00:00 2001 +From: Philippe Antoine +Date: Tue, 9 Dec 2025 09:38:31 +0100 +Subject: [PATCH 2/2] output: use tx iterator for finding alert http xff + +Ticket: 8156 + +Allows better performance. + +(cherry picked from commit ab2e128176744ead5130707bb53fa59038e19634) + +Origin: upstream, https://github.com/OISF/suricata/commit/7e704a3f50690b5f5d5cc573147ef41449fe37ac.patch +Bug: https://redmine.openinfosecfoundation.org/issues/8156 +Subject: Upstream fix for CVE-2026-22261 part 2 +--- + src/app-layer-htp-xff.c | 77 +++++++++++++++++++++++++---------------- + 1 file changed, 47 insertions(+), 30 deletions(-) + +diff --git a/src/app-layer-htp-xff.c b/src/app-layer-htp-xff.c +index 6eae5b1b3..2e5c25cdb 100644 +--- a/src/app-layer-htp-xff.c ++++ b/src/app-layer-htp-xff.c +@@ -107,38 +107,12 @@ static int ParseXFFString(char *input, char *output, int output_size) + return 0; + } + +-/** +- * \brief Function to return XFF IP if any in the selected transaction. The +- * caller needs to lock the flow. +- * \retval 1 if the IP has been found and returned in dstbuf +- * \retval 0 if the IP has not being found or error +- */ +-int HttpXFFGetIPFromTx(const Flow *f, uint64_t tx_id, HttpXFFCfg *xff_cfg, +- char *dstbuf, int dstbuflen) ++static int HttpXFFGetIPFromTxAux( ++ const Flow *f, htp_tx_t *tx, HttpXFFCfg *xff_cfg, char *dstbuf, int dstbuflen) + { + uint8_t xff_chain[XFF_CHAIN_MAXLEN]; +- HtpState *htp_state = NULL; +- htp_tx_t *tx = NULL; +- uint64_t total_txs = 0; + uint8_t *p_xff = NULL; + +- htp_state = (HtpState *)FlowGetAppState(f); +- +- if (htp_state == NULL) { +- SCLogDebug("no http state, XFF IP cannot be retrieved"); +- return 0; +- } +- +- total_txs = AppLayerParserGetTxCnt(f, htp_state); +- if (tx_id >= total_txs) +- return 0; +- +- tx = AppLayerParserGetTx(f->proto, ALPROTO_HTTP1, htp_state, tx_id); +- if (tx == NULL) { +- SCLogDebug("tx is NULL, XFF cannot be retrieved"); +- return 0; +- } +- + htp_header_t *h_xff = NULL; + if (tx->request_headers != NULL) { + h_xff = htp_table_get_c(tx->request_headers, xff_cfg->header); +@@ -172,6 +146,38 @@ int HttpXFFGetIPFromTx(const Flow *f, uint64_t tx_id, HttpXFFCfg *xff_cfg, + return 0; + } + ++/** ++ * \brief Function to return XFF IP if any in the selected transaction. The ++ * caller needs to lock the flow. ++ * \retval 1 if the IP has been found and returned in dstbuf ++ * \retval 0 if the IP has not being found or error ++ */ ++int HttpXFFGetIPFromTx( ++ const Flow *f, uint64_t tx_id, HttpXFFCfg *xff_cfg, char *dstbuf, int dstbuflen) ++{ ++ HtpState *htp_state = NULL; ++ uint64_t total_txs = 0; ++ htp_tx_t *tx = NULL; ++ ++ htp_state = (HtpState *)FlowGetAppState(f); ++ ++ if (htp_state == NULL) { ++ SCLogDebug("no http state, XFF IP cannot be retrieved"); ++ return 0; ++ } ++ ++ total_txs = AppLayerParserGetTxCnt(f, htp_state); ++ if (tx_id >= total_txs) ++ return 0; ++ ++ tx = AppLayerParserGetTx(f->proto, ALPROTO_HTTP1, htp_state, tx_id); ++ if (tx == NULL) { ++ SCLogDebug("tx is NULL, XFF cannot be retrieved"); ++ return 0; ++ } ++ return HttpXFFGetIPFromTxAux(f, tx, xff_cfg, dstbuf, dstbuflen); ++} ++ + /** + * \brief Function to return XFF IP if any. The caller needs to lock the flow. + * \retval 1 if the IP has been found and returned in dstbuf +@@ -190,9 +196,20 @@ int HttpXFFGetIP(const Flow *f, HttpXFFCfg *xff_cfg, char *dstbuf, int dstbuflen + } + + total_txs = AppLayerParserGetTxCnt(f, htp_state); +- for (; tx_id < total_txs; tx_id++) { +- if (HttpXFFGetIPFromTx(f, tx_id, xff_cfg, dstbuf, dstbuflen) == 1) ++ AppLayerGetTxIteratorFunc IterFunc = AppLayerGetTxIterator(f->proto, f->alproto); ++ AppLayerGetTxIterState state; ++ memset(&state, 0, sizeof(state)); ++ ++ while (1) { ++ AppLayerGetTxIterTuple ires = ++ IterFunc(f->proto, f->alproto, f->alstate, tx_id, total_txs, &state); ++ if (ires.tx_ptr == NULL) ++ break; ++ ++ if (HttpXFFGetIPFromTxAux(f, ires.tx_ptr, xff_cfg, dstbuf, dstbuflen) == 1) + return 1; ++ ++ tx_id = ires.tx_id + 1; + } + + end: +-- +2.47.3 + diff -Nru suricata-7.0.10/debian/patches/CVE-2026-22262_1.patch suricata-7.0.10/debian/patches/CVE-2026-22262_1.patch --- suricata-7.0.10/debian/patches/CVE-2026-22262_1.patch 1970-01-01 00:00:00.000000000 +0000 +++ suricata-7.0.10/debian/patches/CVE-2026-22262_1.patch 2026-02-22 12:21:42.000000000 +0000 @@ -0,0 +1,42 @@ +From 32609e6896f9079c175665a94005417cec7637eb Mon Sep 17 00:00:00 2001 +From: Philippe Antoine +Date: Mon, 17 Nov 2025 13:27:54 +0100 +Subject: [PATCH 1/2] datasets: explicitly errors on too long string + +Also avoids stack allocation + +Ticket: 8110 +(cherry picked from commit 0eff24213763c2aa2bb0957901d5dc1e18414dbf) + +Origin: upstream, https://github.com/OISF/suricata/commit/32609e6896f9079c175665a94005417cec7637eb.patch +Bug: https://redmine.openinfosecfoundation.org/issues/8110 +Subject: Upstream fix for CVE-2026-22262 part 1 +--- + src/datasets-string.c | 9 +++++---- + 1 file changed, 5 insertions(+), 4 deletions(-) + +diff --git a/src/datasets-string.c b/src/datasets-string.c +index 0a8f499ae..524a60ad9 100644 +--- a/src/datasets-string.c ++++ b/src/datasets-string.c +@@ -49,12 +49,13 @@ int StringAsBase64(const void *s, char *out, size_t out_size) + const StringType *str = s; + + unsigned long len = Base64EncodeBufferSize(str->len); +- uint8_t encoded_data[len]; +- if (Base64Encode((unsigned char *)str->ptr, str->len, +- encoded_data, &len) != SC_BASE64_OK) ++ if (len + 2 > out_size) { ++ // linefeed and final zero ++ return 0; ++ } ++ if (Base64Encode((unsigned char *)str->ptr, str->len, (uint8_t *)out, &len) != SC_BASE64_OK) + return 0; + +- strlcpy(out, (const char *)encoded_data, out_size); + strlcat(out, "\n", out_size); + return strlen(out); + } +-- +2.47.3 + diff -Nru suricata-7.0.10/debian/patches/CVE-2026-22262_2.patch suricata-7.0.10/debian/patches/CVE-2026-22262_2.patch --- suricata-7.0.10/debian/patches/CVE-2026-22262_2.patch 1970-01-01 00:00:00.000000000 +0000 +++ suricata-7.0.10/debian/patches/CVE-2026-22262_2.patch 2026-02-22 12:21:42.000000000 +0000 @@ -0,0 +1,66 @@ +From 27a2180bceaa3477419c78c54fce364398d011f1 Mon Sep 17 00:00:00 2001 +From: Philippe Antoine +Date: Tue, 25 Nov 2025 14:43:18 +0100 +Subject: [PATCH 2/2] datasets: allocates on the heap if string base64 is long + +Ticket: 8110 +(cherry picked from commit d6bc718e303ecbec5999066b8bc88eeeca743658) + +Origin: upstream, https://github.com/OISF/suricata/commit/27a2180bceaa3477419c78c54fce364398d011f1.patch +Bug: https://redmine.openinfosecfoundation.org/issues/8110 +Subject: Upstream fix for CVE-2026-22262 part 2 +--- + src/datasets-string.c | 4 ++-- + src/util-thash.c | 21 ++++++++++++++++++++- + 2 files changed, 22 insertions(+), 3 deletions(-) + +diff --git a/src/datasets-string.c b/src/datasets-string.c +index 524a60ad9..53a179a10 100644 +--- a/src/datasets-string.c ++++ b/src/datasets-string.c +@@ -50,8 +50,8 @@ int StringAsBase64(const void *s, char *out, size_t out_size) + + unsigned long len = Base64EncodeBufferSize(str->len); + if (len + 2 > out_size) { +- // linefeed and final zero +- return 0; ++ // linefeed and final zero : signal we need more space ++ return len + 2; + } + if (Base64Encode((unsigned char *)str->ptr, str->len, (uint8_t *)out, &len) != SC_BASE64_OK) + return 0; +diff --git a/src/util-thash.c b/src/util-thash.c +index 548637916..c6df02cf3 100644 +--- a/src/util-thash.c ++++ b/src/util-thash.c +@@ -390,7 +390,26 @@ int THashWalk(THashTableContext *ctx, THashFormatFunc FormatterFunc, THashOutput + char output_string[1024] = ""; + int size = FormatterFunc(h->data, output_string, sizeof(output_string)); + if (size > 0) { +- if (OutputterFunc(output_ctx, (const uint8_t *)output_string, size) < 0) { ++ if (size > 1024) { ++ // we did not provide enough space on the stack, let's allocate on the heap ++ char *out_alloc = SCCalloc(1, size); ++ if (out_alloc == NULL) { ++ err = true; ++ break; ++ } ++ size = FormatterFunc(h->data, out_alloc, size); ++ if (size == 0) { ++ err = true; ++ SCFree(out_alloc); ++ break; ++ } ++ if (OutputterFunc(output_ctx, (const uint8_t *)out_alloc, size) < 0) { ++ err = true; ++ SCFree(out_alloc); ++ break; ++ } ++ SCFree(out_alloc); ++ } else if (OutputterFunc(output_ctx, (const uint8_t *)output_string, size) < 0) { + err = true; + break; + } +-- +2.47.3 + diff -Nru suricata-7.0.10/debian/patches/CVE-2026-22264.patch suricata-7.0.10/debian/patches/CVE-2026-22264.patch --- suricata-7.0.10/debian/patches/CVE-2026-22264.patch 1970-01-01 00:00:00.000000000 +0000 +++ suricata-7.0.10/debian/patches/CVE-2026-22264.patch 2026-02-22 12:21:42.000000000 +0000 @@ -0,0 +1,84 @@ +From 5789a3d3760dbf33d93fc56c27bd9529e5bdc8f2 Mon Sep 17 00:00:00 2001 +From: Shivani Bhardwaj +Date: Mon, 5 Jan 2026 19:27:11 +0530 +Subject: [PATCH] detect/alert: check alert queue capacity before expanding + +So far, the alert queue was expanded by doubling in size w/o any +boundary checks in place. This led to situations where doubling +the alert_queue_capacity meant overflow of the very same value +stored in det_ctx. +This led to heap-use-after-free in some conditions where +det_ctx->alert_queue_capacity overflowed. + +Fix this by capping the max of alert_queue_capacity by checking if its +expansion could result in an overflow. + +Security 8190 + +(cherry picked from commit ac1eb394181530430fb7262969f423a1bf8f209b) + +Origin: upstream, https://github.com/OISF/suricata/commit/5789a3d3760dbf33d93fc56c27bd9529e5bdc8f2.patch +Bug: https://redmine.openinfosecfoundation.org/issues/8190 +Subject: Upstream fix for CVE-2026-22264 +--- + src/detect-engine-alert.c | 35 +++++++++++++++++++++++++---------- + 1 file changed, 25 insertions(+), 10 deletions(-) + +diff --git a/src/detect-engine-alert.c b/src/detect-engine-alert.c +index f3e49586e..0cce98196 100644 +--- a/src/detect-engine-alert.c ++++ b/src/detect-engine-alert.c +@@ -238,6 +238,22 @@ void AlertQueueFree(DetectEngineThreadCtx *det_ctx) + det_ctx->alert_queue_capacity = 0; + } + ++static inline uint16_t AlertQueueExpandDo(DetectEngineThreadCtx *det_ctx, uint16_t new_cap) ++{ ++ DEBUG_VALIDATE_BUG_ON(det_ctx->alert_queue_capacity >= new_cap); ++ ++ void *tmp_queue = SCRealloc(det_ctx->alert_queue, new_cap * sizeof(PacketAlert)); ++ if (unlikely(tmp_queue == NULL)) { ++ /* queue capacity didn't change */ ++ return det_ctx->alert_queue_capacity; ++ } ++ det_ctx->alert_queue = tmp_queue; ++ det_ctx->alert_queue_capacity = new_cap; ++ SCLogDebug("Alert queue size expanded: %u elements, bytes: %" PRIuMAX "", ++ det_ctx->alert_queue_capacity, (uintmax_t)(new_cap * sizeof(PacketAlert))); ++ return new_cap; ++} ++ + /** \internal + * \retval the new capacity + */ +@@ -247,18 +263,17 @@ static uint16_t AlertQueueExpand(DetectEngineThreadCtx *det_ctx) + if (unlikely(g_eps_is_alert_queue_fail_mode)) + return det_ctx->alert_queue_capacity; + #endif +- uint16_t new_cap = det_ctx->alert_queue_capacity * 2; +- void *tmp_queue = SCRealloc(det_ctx->alert_queue, (size_t)(sizeof(PacketAlert) * new_cap)); +- if (unlikely(tmp_queue == NULL)) { +- /* queue capacity didn't change */ ++ if (det_ctx->alert_queue_capacity == UINT16_MAX) { + return det_ctx->alert_queue_capacity; + } +- det_ctx->alert_queue = tmp_queue; +- det_ctx->alert_queue_capacity = new_cap; +- SCLogDebug("Alert queue size doubled: %u elements, bytes: %" PRIuMAX "", +- det_ctx->alert_queue_capacity, +- (uintmax_t)(sizeof(PacketAlert) * det_ctx->alert_queue_capacity)); +- return new_cap; ++ ++ uint16_t new_cap; ++ if (det_ctx->alert_queue_capacity > (UINT16_MAX / 2)) { ++ new_cap = UINT16_MAX; ++ } else { ++ new_cap = det_ctx->alert_queue_capacity * 2; ++ } ++ return AlertQueueExpandDo(det_ctx, new_cap); + } + + /** \internal +-- +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 2025-12-10 19:12:20.000000000 +0000 +++ suricata-7.0.10/debian/patches/series 2026-02-22 12:21:42.000000000 +0000 @@ -15,3 +15,15 @@ CVE-2025-64332.patch CVE-2025-64331.patch CVE-2025-64330.patch +CVE-2026-22258_1.patch +CVE-2026-22258_2.patch +CVE-2026-22258_3.patch +CVE-2026-22262_1.patch +CVE-2026-22262_2.patch +CVE-2026-22264.patch +CVE-2026-22259_1.patch +CVE-2026-22259_2.patch +CVE-2026-22259_3.patch +CVE-2026-22259_4.patch +CVE-2026-22261_1.patch +CVE-2026-22261_2.patch