summaryrefslogtreecommitdiff
path: root/meta-security/recipes-ids/suricata/files/CVE-2024-38535_pre.patch
diff options
context:
space:
mode:
Diffstat (limited to 'meta-security/recipes-ids/suricata/files/CVE-2024-38535_pre.patch')
-rw-r--r--meta-security/recipes-ids/suricata/files/CVE-2024-38535_pre.patch292
1 files changed, 292 insertions, 0 deletions
diff --git a/meta-security/recipes-ids/suricata/files/CVE-2024-38535_pre.patch b/meta-security/recipes-ids/suricata/files/CVE-2024-38535_pre.patch
new file mode 100644
index 0000000000..2aa42c465a
--- /dev/null
+++ b/meta-security/recipes-ids/suricata/files/CVE-2024-38535_pre.patch
@@ -0,0 +1,292 @@
+From 390f09692eb99809c679d3f350c7cc185d163e1a Mon Sep 17 00:00:00 2001
+From: Philippe Antoine <pantoine@oisf.net>
+Date: Wed, 27 Mar 2024 14:33:54 +0100
+Subject: [PATCH] http2: use a reference counter for headers
+
+Ticket: 6892
+
+As HTTP hpack header compression allows one single byte to
+express a previously seen arbitrary-size header block (name+value)
+we should avoid to copy the vectors data, but just point
+to the same data, while reamining memory safe, even in the case
+of later headers eviction from the dybnamic table.
+
+Rust std solution is Rc, and the use of clone, so long as the
+data is accessed by only one thread.
+
+Note: This patch is needed to patch CVE-2024-38535 as it defines Rc.
+Upstream-Status: Backport from [https://github.com/OISF/suricata/commit/390f09692eb99809c679d3f350c7cc185d163e1a]
+Signed-off-by: Siddharth Doshi <sdoshi@mvista.com>
+---
+ rust/src/http2/detect.rs | 19 +++++++------
+ rust/src/http2/http2.rs | 2 +-
+ rust/src/http2/parser.rs | 61 +++++++++++++++++++++-------------------
+ 3 files changed, 43 insertions(+), 39 deletions(-)
+
+diff --git a/rust/src/http2/detect.rs b/rust/src/http2/detect.rs
+index 9c2f8ab..e068a17 100644
+--- a/rust/src/http2/detect.rs
++++ b/rust/src/http2/detect.rs
+@@ -23,6 +23,7 @@ use crate::core::Direction;
+ use crate::detect::uint::{detect_match_uint, DetectUintData};
+ use std::ffi::CStr;
+ use std::str::FromStr;
++use std::rc::Rc;
+
+ fn http2_tx_has_frametype(
+ tx: &mut HTTP2Transaction, direction: Direction, value: u8,
+@@ -404,7 +405,7 @@ fn http2_frames_get_header_firstvalue<'a>(
+ for frame in frames {
+ if let Some(blocks) = http2_header_blocks(frame) {
+ for block in blocks.iter() {
+- if block.name == name.as_bytes() {
++ if block.name.as_ref() == name.as_bytes() {
+ return Ok(&block.value);
+ }
+ }
+@@ -428,7 +429,7 @@ pub fn http2_frames_get_header_value_vec(
+ for frame in frames {
+ if let Some(blocks) = http2_header_blocks(frame) {
+ for block in blocks.iter() {
+- if block.name == name.as_bytes() {
++ if block.name.as_ref() == name.as_bytes() {
+ if found == 0 {
+ vec.extend_from_slice(&block.value);
+ found = 1;
+@@ -465,7 +466,7 @@ fn http2_frames_get_header_value<'a>(
+ for frame in frames {
+ if let Some(blocks) = http2_header_blocks(frame) {
+ for block in blocks.iter() {
+- if block.name == name.as_bytes() {
++ if block.name.as_ref() == name.as_bytes() {
+ if found == 0 {
+ single = Ok(&block.value);
+ found = 1;
+@@ -905,8 +906,8 @@ fn http2_tx_set_header(state: &mut HTTP2State, name: &[u8], input: &[u8]) {
+ };
+ let mut blocks = Vec::new();
+ let b = parser::HTTP2FrameHeaderBlock {
+- name: name.to_vec(),
+- value: input.to_vec(),
++ name: Rc::new(name.to_vec()),
++ value: Rc::new(input.to_vec()),
+ error: parser::HTTP2HeaderDecodeStatus::HTTP2HeaderDecodeSuccess,
+ sizeupdate: 0,
+ };
+@@ -1061,15 +1062,15 @@ mod tests {
+ };
+ let mut blocks = Vec::new();
+ let b = parser::HTTP2FrameHeaderBlock {
+- name: "Host".as_bytes().to_vec(),
+- value: "abc.com".as_bytes().to_vec(),
++ name: "Host".as_bytes().to_vec().into(),
++ value: "abc.com".as_bytes().to_vec().into(),
+ error: parser::HTTP2HeaderDecodeStatus::HTTP2HeaderDecodeSuccess,
+ sizeupdate: 0,
+ };
+ blocks.push(b);
+ let b2 = parser::HTTP2FrameHeaderBlock {
+- name: "Host".as_bytes().to_vec(),
+- value: "efg.net".as_bytes().to_vec(),
++ name: "Host".as_bytes().to_vec().into(),
++ value: "efg.net".as_bytes().to_vec().into(),
+ error: parser::HTTP2HeaderDecodeStatus::HTTP2HeaderDecodeSuccess,
+ sizeupdate: 0,
+ };
+diff --git a/rust/src/http2/http2.rs b/rust/src/http2/http2.rs
+index 326030f..d14ca06 100644
+--- a/rust/src/http2/http2.rs
++++ b/rust/src/http2/http2.rs
+@@ -204,7 +204,7 @@ impl HTTP2Transaction {
+
+ fn handle_headers(&mut self, blocks: &[parser::HTTP2FrameHeaderBlock], dir: Direction) {
+ for block in blocks {
+- if block.name == b"content-encoding" {
++ if block.name.as_ref() == b"content-encoding" {
+ self.decoder.http2_encoding_fromvec(&block.value, dir);
+ }
+ }
+diff --git a/rust/src/http2/parser.rs b/rust/src/http2/parser.rs
+index adabeb2..1a46437 100644
+--- a/rust/src/http2/parser.rs
++++ b/rust/src/http2/parser.rs
+@@ -30,6 +30,7 @@ use nom7::sequence::tuple;
+ use nom7::{Err, IResult};
+ use std::fmt;
+ use std::str::FromStr;
++use std::rc::Rc;
+
+ #[repr(u8)]
+ #[derive(Clone, Copy, PartialEq, Eq, FromPrimitive, Debug)]
+@@ -295,8 +296,8 @@ fn http2_frame_header_static(n: u64, dyn_headers: &HTTP2DynTable) -> Option<HTTP
+ };
+ if !name.is_empty() {
+ return Some(HTTP2FrameHeaderBlock {
+- name: name.as_bytes().to_vec(),
+- value: value.as_bytes().to_vec(),
++ name: Rc::new(name.as_bytes().to_vec()),
++ value: Rc::new(value.as_bytes().to_vec()),
+ error: HTTP2HeaderDecodeStatus::HTTP2HeaderDecodeSuccess,
+ sizeupdate: 0,
+ });
+@@ -304,23 +305,23 @@ fn http2_frame_header_static(n: u64, dyn_headers: &HTTP2DynTable) -> Option<HTTP
+ //use dynamic table
+ if n == 0 {
+ return Some(HTTP2FrameHeaderBlock {
+- name: Vec::new(),
+- value: Vec::new(),
++ name: Rc::new(Vec::new()),
++ value: Rc::new(Vec::new()),
+ error: HTTP2HeaderDecodeStatus::HTTP2HeaderDecodeIndex0,
+ sizeupdate: 0,
+ });
+ } else if dyn_headers.table.len() + HTTP2_STATIC_HEADERS_NUMBER < n as usize {
+ return Some(HTTP2FrameHeaderBlock {
+- name: Vec::new(),
+- value: Vec::new(),
++ name: Rc::new(Vec::new()),
++ value: Rc::new(Vec::new()),
+ error: HTTP2HeaderDecodeStatus::HTTP2HeaderDecodeNotIndexed,
+ sizeupdate: 0,
+ });
+ } else {
+ let indyn = dyn_headers.table.len() - (n as usize - HTTP2_STATIC_HEADERS_NUMBER);
+ let headcopy = HTTP2FrameHeaderBlock {
+- name: dyn_headers.table[indyn].name.to_vec(),
+- value: dyn_headers.table[indyn].value.to_vec(),
++ name: dyn_headers.table[indyn].name.clone(),
++ value: dyn_headers.table[indyn].value.clone(),
+ error: HTTP2HeaderDecodeStatus::HTTP2HeaderDecodeSuccess,
+ sizeupdate: 0,
+ };
+@@ -348,8 +349,10 @@ impl fmt::Display for HTTP2HeaderDecodeStatus {
+
+ #[derive(Clone, Debug)]
+ pub struct HTTP2FrameHeaderBlock {
+- pub name: Vec<u8>,
+- pub value: Vec<u8>,
++ // Use Rc reference counted so that indexed headers do not get copied.
++ // Otherwise, this leads to quadratic complexity in memory occupation.
++ pub name: Rc<Vec<u8>>,
++ pub value: Rc<Vec<u8>>,
+ pub error: HTTP2HeaderDecodeStatus,
+ pub sizeupdate: u64,
+ }
+@@ -391,7 +394,7 @@ fn http2_parse_headers_block_literal_common<'a>(
+ ) -> IResult<&'a [u8], HTTP2FrameHeaderBlock> {
+ let (i3, name, error) = if index == 0 {
+ match http2_parse_headers_block_string(input) {
+- Ok((r, n)) => Ok((r, n, HTTP2HeaderDecodeStatus::HTTP2HeaderDecodeSuccess)),
++ Ok((r, n)) => Ok((r, Rc::new(n), HTTP2HeaderDecodeStatus::HTTP2HeaderDecodeSuccess)),
+ Err(e) => Err(e),
+ }
+ } else {
+@@ -403,7 +406,7 @@ fn http2_parse_headers_block_literal_common<'a>(
+ )),
+ None => Ok((
+ input,
+- Vec::new(),
++ Rc::new(Vec::new()),
+ HTTP2HeaderDecodeStatus::HTTP2HeaderDecodeNotIndexed,
+ )),
+ }
+@@ -413,7 +416,7 @@ fn http2_parse_headers_block_literal_common<'a>(
+ i4,
+ HTTP2FrameHeaderBlock {
+ name,
+- value,
++ value: Rc::new(value),
+ error,
+ sizeupdate: 0,
+ },
+@@ -435,8 +438,8 @@ fn http2_parse_headers_block_literal_incindex<'a>(
+ match r {
+ Ok((r, head)) => {
+ let headcopy = HTTP2FrameHeaderBlock {
+- name: head.name.to_vec(),
+- value: head.value.to_vec(),
++ name: head.name.clone(),
++ value: head.value.clone(),
+ error: head.error,
+ sizeupdate: 0,
+ };
+@@ -556,8 +559,8 @@ fn http2_parse_headers_block_dynamic_size<'a>(
+ return Ok((
+ i3,
+ HTTP2FrameHeaderBlock {
+- name: Vec::new(),
+- value: Vec::new(),
++ name: Rc::new(Vec::new()),
++ value: Rc::new(Vec::new()),
+ error: HTTP2HeaderDecodeStatus::HTTP2HeaderDecodeSizeUpdate,
+ sizeupdate: maxsize2,
+ },
+@@ -614,8 +617,8 @@ fn http2_parse_headers_blocks<'a>(
+ // if we error from http2_parse_var_uint, we keep the first parsed headers
+ if err.code == ErrorKind::LengthValue {
+ blocks.push(HTTP2FrameHeaderBlock {
+- name: Vec::new(),
+- value: Vec::new(),
++ name: Rc::new(Vec::new()),
++ value: Rc::new(Vec::new()),
+ error: HTTP2HeaderDecodeStatus::HTTP2HeaderDecodeIntegerOverflow,
+ sizeupdate: 0,
+ });
+@@ -765,8 +768,8 @@ mod tests {
+ match r0 {
+ Ok((remainder, hd)) => {
+ // Check the first message.
+- assert_eq!(hd.name, ":method".as_bytes().to_vec());
+- assert_eq!(hd.value, "GET".as_bytes().to_vec());
++ assert_eq!(hd.name, ":method".as_bytes().to_vec().into());
++ assert_eq!(hd.value, "GET".as_bytes().to_vec().into());
+ // And we should have no bytes left.
+ assert_eq!(remainder.len(), 0);
+ }
+@@ -782,8 +785,8 @@ mod tests {
+ match r1 {
+ Ok((remainder, hd)) => {
+ // Check the first message.
+- assert_eq!(hd.name, "accept".as_bytes().to_vec());
+- assert_eq!(hd.value, "*/*".as_bytes().to_vec());
++ assert_eq!(hd.name, "accept".as_bytes().to_vec().into());
++ assert_eq!(hd.value, "*/*".as_bytes().to_vec().into());
+ // And we should have no bytes left.
+ assert_eq!(remainder.len(), 0);
+ assert_eq!(dynh.table.len(), 1);
+@@ -802,8 +805,8 @@ mod tests {
+ match result {
+ Ok((remainder, hd)) => {
+ // Check the first message.
+- assert_eq!(hd.name, ":authority".as_bytes().to_vec());
+- assert_eq!(hd.value, "localhost:3000".as_bytes().to_vec());
++ assert_eq!(hd.name, ":authority".as_bytes().to_vec().into());
++ assert_eq!(hd.value, "localhost:3000".as_bytes().to_vec().into());
+ // And we should have no bytes left.
+ assert_eq!(remainder.len(), 0);
+ assert_eq!(dynh.table.len(), 2);
+@@ -820,8 +823,8 @@ mod tests {
+ match r3 {
+ Ok((remainder, hd)) => {
+ // same as before
+- assert_eq!(hd.name, ":authority".as_bytes().to_vec());
+- assert_eq!(hd.value, "localhost:3000".as_bytes().to_vec());
++ assert_eq!(hd.name, ":authority".as_bytes().to_vec().into());
++ assert_eq!(hd.value, "localhost:3000".as_bytes().to_vec().into());
+ // And we should have no bytes left.
+ assert_eq!(remainder.len(), 0);
+ assert_eq!(dynh.table.len(), 2);
+@@ -856,8 +859,8 @@ mod tests {
+ match r2 {
+ Ok((remainder, hd)) => {
+ // Check the first message.
+- assert_eq!(hd.name, ":path".as_bytes().to_vec());
+- assert_eq!(hd.value, "/doc/manual/html/index.html".as_bytes().to_vec());
++ assert_eq!(hd.name, ":path".as_bytes().to_vec().into());
++ assert_eq!(hd.value, "/doc/manual/html/index.html".as_bytes().to_vec().into());
+ // And we should have no bytes left.
+ assert_eq!(remainder.len(), 0);
+ assert_eq!(dynh.table.len(), 2);
+--
+2.44.0
+