diff --git a/SPECS/kata-containers/CVE-2026-25800.patch b/SPECS/kata-containers/CVE-2026-25800.patch new file mode 100644 index 00000000000..f6d7415fd7f --- /dev/null +++ b/SPECS/kata-containers/CVE-2026-25800.patch @@ -0,0 +1,443 @@ +From c1869e4ca921ddf5f8e6c11cd3aa56a7788b2af8 Mon Sep 17 00:00:00 2001 +From: Dirkjan Ochtman +Date: Thu, 18 Jun 2026 15:08:11 +0200 +Subject: [PATCH] proto: yield error on too many gaps in assembler + +Signed-off-by: Azure Linux Security Servicing Account +Upstream-reference: https://github.com/quinn-rs/quinn/commit/fed0321a9a672819662caab37f5662f1ad91308e.patch +--- + .../quinn-proto/src/connection/assembler.rs | 153 ++++++++++-------- + vendor/quinn-proto/src/connection/mod.rs | 4 +- + .../src/connection/streams/recv.rs | 4 +- + 3 files changed, 90 insertions(+), 71 deletions(-) + +diff --git a/vendor/quinn-proto/src/connection/assembler.rs b/vendor/quinn-proto/src/connection/assembler.rs +index 735e6598..98eed6d3 100644 +--- a/vendor/quinn-proto/src/connection/assembler.rs ++++ b/vendor/quinn-proto/src/connection/assembler.rs +@@ -147,7 +147,12 @@ impl Assembler { + + // Note: If a packet contains many frames from the same stream, the estimated over-allocation + // will be much higher because we are counting the same allocation multiple times. +- pub(super) fn insert(&mut self, mut offset: u64, mut bytes: Bytes, allocation_size: usize) { ++ pub(super) fn insert( ++ &mut self, ++ mut offset: u64, ++ mut bytes: Bytes, ++ allocation_size: usize, ++ ) -> Result<(), TooManyChunks> { + debug_assert!( + bytes.len() <= allocation_size, + "allocation_size less than bytes.len(): {:?} < {:?}", +@@ -174,7 +179,7 @@ impl Assembler { + } + } else if offset < self.bytes_read { + if (offset + bytes.len() as u64) <= self.bytes_read { +- return; ++ return Ok(()); + } else { + let diff = self.bytes_read - offset; + offset += diff; +@@ -183,7 +188,7 @@ impl Assembler { + } + + if bytes.is_empty() { +- return; ++ return Ok(()); + } + let buffer = Buffer::new(offset, bytes, allocation_size); + self.buffered += buffer.bytes.len(); +@@ -204,8 +209,14 @@ impl Assembler { + // balance between defragmentation overhead and over-allocation. + let threshold = 32768.max(buffered * 3 / 2); + if over_allocation > threshold { +- self.defragment() ++ self.defragment(); ++ // ngtcp2 uses a threshold of 4000 -- try to be a little more conservative? ++ if self.data.len() > 1024 { ++ return Err(TooManyChunks); ++ } + } ++ ++ Ok(()) + } + + /// Number of bytes consumed by the application +@@ -335,6 +346,10 @@ impl State { + #[derive(Debug)] + pub struct IllegalOrderedRead; + ++/// Error indicating that too many chunks are buffered due to maliciously small/gapped frames ++#[derive(Debug)] ++pub(crate) struct TooManyChunks; ++ + #[cfg(test)] + mod test { + use super::*; +@@ -344,13 +359,13 @@ mod test { + fn assemble_ordered() { + let mut x = Assembler::new(); + assert_matches!(next(&mut x, 32), None); +- x.insert(0, Bytes::from_static(b"123"), 3); ++ x.insert(0, Bytes::from_static(b"123"), 3).unwrap(); + assert_matches!(next(&mut x, 1), Some(ref y) if &y[..] == b"1"); + assert_matches!(next(&mut x, 3), Some(ref y) if &y[..] == b"23"); +- x.insert(3, Bytes::from_static(b"456"), 3); ++ x.insert(3, Bytes::from_static(b"456"), 3).unwrap(); + assert_matches!(next(&mut x, 32), Some(ref y) if &y[..] == b"456"); +- x.insert(6, Bytes::from_static(b"789"), 3); +- x.insert(9, Bytes::from_static(b"10"), 2); ++ x.insert(6, Bytes::from_static(b"789"), 3).unwrap(); ++ x.insert(9, Bytes::from_static(b"10"), 2).unwrap(); + assert_matches!(next(&mut x, 32), Some(ref y) if &y[..] == b"789"); + assert_matches!(next(&mut x, 32), Some(ref y) if &y[..] == b"10"); + assert_matches!(next(&mut x, 32), None); +@@ -360,9 +375,9 @@ mod test { + fn assemble_unordered() { + let mut x = Assembler::new(); + x.ensure_ordering(false).unwrap(); +- x.insert(3, Bytes::from_static(b"456"), 3); ++ x.insert(3, Bytes::from_static(b"456"), 3).unwrap(); + assert_matches!(next(&mut x, 32), None); +- x.insert(0, Bytes::from_static(b"123"), 3); ++ x.insert(0, Bytes::from_static(b"123"), 3).unwrap(); + assert_matches!(next(&mut x, 32), Some(ref y) if &y[..] == b"123"); + assert_matches!(next(&mut x, 32), Some(ref y) if &y[..] == b"456"); + assert_matches!(next(&mut x, 32), None); +@@ -371,8 +386,8 @@ mod test { + #[test] + fn assemble_duplicate() { + let mut x = Assembler::new(); +- x.insert(0, Bytes::from_static(b"123"), 3); +- x.insert(0, Bytes::from_static(b"123"), 3); ++ x.insert(0, Bytes::from_static(b"123"), 3).unwrap(); ++ x.insert(0, Bytes::from_static(b"123"), 3).unwrap(); + assert_matches!(next(&mut x, 32), Some(ref y) if &y[..] == b"123"); + assert_matches!(next(&mut x, 32), None); + } +@@ -380,8 +395,8 @@ mod test { + #[test] + fn assemble_duplicate_compact() { + let mut x = Assembler::new(); +- x.insert(0, Bytes::from_static(b"123"), 3); +- x.insert(0, Bytes::from_static(b"123"), 3); ++ x.insert(0, Bytes::from_static(b"123"), 3).unwrap(); ++ x.insert(0, Bytes::from_static(b"123"), 3).unwrap(); + x.defragment(); + assert_matches!(next(&mut x, 32), Some(ref y) if &y[..] == b"123"); + assert_matches!(next(&mut x, 32), None); +@@ -390,8 +405,8 @@ mod test { + #[test] + fn assemble_contained() { + let mut x = Assembler::new(); +- x.insert(0, Bytes::from_static(b"12345"), 5); +- x.insert(1, Bytes::from_static(b"234"), 3); ++ x.insert(0, Bytes::from_static(b"12345"), 5).unwrap(); ++ x.insert(1, Bytes::from_static(b"234"), 3).unwrap(); + assert_matches!(next(&mut x, 32), Some(ref y) if &y[..] == b"12345"); + assert_matches!(next(&mut x, 32), None); + } +@@ -399,8 +414,8 @@ mod test { + #[test] + fn assemble_contained_compact() { + let mut x = Assembler::new(); +- x.insert(0, Bytes::from_static(b"12345"), 5); +- x.insert(1, Bytes::from_static(b"234"), 3); ++ x.insert(0, Bytes::from_static(b"12345"), 5).unwrap(); ++ x.insert(1, Bytes::from_static(b"234"), 3).unwrap(); + x.defragment(); + assert_matches!(next(&mut x, 32), Some(ref y) if &y[..] == b"12345"); + assert_matches!(next(&mut x, 32), None); +@@ -409,8 +424,8 @@ mod test { + #[test] + fn assemble_contains() { + let mut x = Assembler::new(); +- x.insert(1, Bytes::from_static(b"234"), 3); +- x.insert(0, Bytes::from_static(b"12345"), 5); ++ x.insert(1, Bytes::from_static(b"234"), 3).unwrap(); ++ x.insert(0, Bytes::from_static(b"12345"), 5).unwrap(); + assert_matches!(next(&mut x, 32), Some(ref y) if &y[..] == b"12345"); + assert_matches!(next(&mut x, 32), None); + } +@@ -418,8 +433,8 @@ mod test { + #[test] + fn assemble_contains_compact() { + let mut x = Assembler::new(); +- x.insert(1, Bytes::from_static(b"234"), 3); +- x.insert(0, Bytes::from_static(b"12345"), 5); ++ x.insert(1, Bytes::from_static(b"234"), 3).unwrap(); ++ x.insert(0, Bytes::from_static(b"12345"), 5).unwrap(); + x.defragment(); + assert_matches!(next(&mut x, 32), Some(ref y) if &y[..] == b"12345"); + assert_matches!(next(&mut x, 32), None); +@@ -428,8 +443,8 @@ mod test { + #[test] + fn assemble_overlapping() { + let mut x = Assembler::new(); +- x.insert(0, Bytes::from_static(b"123"), 3); +- x.insert(1, Bytes::from_static(b"234"), 3); ++ x.insert(0, Bytes::from_static(b"123"), 3).unwrap(); ++ x.insert(1, Bytes::from_static(b"234"), 3).unwrap(); + assert_matches!(next(&mut x, 32), Some(ref y) if &y[..] == b"123"); + assert_matches!(next(&mut x, 32), Some(ref y) if &y[..] == b"4"); + assert_matches!(next(&mut x, 32), None); +@@ -438,8 +453,8 @@ mod test { + #[test] + fn assemble_overlapping_compact() { + let mut x = Assembler::new(); +- x.insert(0, Bytes::from_static(b"123"), 4); +- x.insert(1, Bytes::from_static(b"234"), 4); ++ x.insert(0, Bytes::from_static(b"123"), 4).unwrap(); ++ x.insert(1, Bytes::from_static(b"234"), 4).unwrap(); + x.defragment(); + assert_matches!(next(&mut x, 32), Some(ref y) if &y[..] == b"1234"); + assert_matches!(next(&mut x, 32), None); +@@ -448,10 +463,10 @@ mod test { + #[test] + fn assemble_complex() { + let mut x = Assembler::new(); +- x.insert(0, Bytes::from_static(b"1"), 1); +- x.insert(2, Bytes::from_static(b"3"), 1); +- x.insert(4, Bytes::from_static(b"5"), 1); +- x.insert(0, Bytes::from_static(b"123456"), 6); ++ x.insert(0, Bytes::from_static(b"1"), 1).unwrap(); ++ x.insert(2, Bytes::from_static(b"3"), 1).unwrap(); ++ x.insert(4, Bytes::from_static(b"5"), 1).unwrap(); ++ x.insert(0, Bytes::from_static(b"123456"), 6).unwrap(); + assert_matches!(next(&mut x, 32), Some(ref y) if &y[..] == b"123456"); + assert_matches!(next(&mut x, 32), None); + } +@@ -459,10 +474,10 @@ mod test { + #[test] + fn assemble_complex_compact() { + let mut x = Assembler::new(); +- x.insert(0, Bytes::from_static(b"1"), 1); +- x.insert(2, Bytes::from_static(b"3"), 1); +- x.insert(4, Bytes::from_static(b"5"), 1); +- x.insert(0, Bytes::from_static(b"123456"), 6); ++ x.insert(0, Bytes::from_static(b"1"), 1).unwrap(); ++ x.insert(2, Bytes::from_static(b"3"), 1).unwrap(); ++ x.insert(4, Bytes::from_static(b"5"), 1).unwrap(); ++ x.insert(0, Bytes::from_static(b"123456"), 6).unwrap(); + x.defragment(); + assert_matches!(next(&mut x, 32), Some(ref y) if &y[..] == b"123456"); + assert_matches!(next(&mut x, 32), None); +@@ -471,19 +486,19 @@ mod test { + #[test] + fn assemble_old() { + let mut x = Assembler::new(); +- x.insert(0, Bytes::from_static(b"1234"), 4); ++ x.insert(0, Bytes::from_static(b"1234"), 4).unwrap(); + assert_matches!(next(&mut x, 32), Some(ref y) if &y[..] == b"1234"); +- x.insert(0, Bytes::from_static(b"1234"), 4); ++ x.insert(0, Bytes::from_static(b"1234"), 4).unwrap(); + assert_matches!(next(&mut x, 32), None); + } + + #[test] + fn compact() { + let mut x = Assembler::new(); +- x.insert(0, Bytes::from_static(b"abc"), 4); +- x.insert(3, Bytes::from_static(b"def"), 4); +- x.insert(9, Bytes::from_static(b"jkl"), 4); +- x.insert(12, Bytes::from_static(b"mno"), 4); ++ x.insert(0, Bytes::from_static(b"abc"), 4).unwrap(); ++ x.insert(3, Bytes::from_static(b"def"), 4).unwrap(); ++ x.insert(9, Bytes::from_static(b"jkl"), 4).unwrap(); ++ x.insert(12, Bytes::from_static(b"mno"), 4).unwrap(); + x.defragment(); + assert_eq!( + next_unordered(&mut x), +@@ -498,7 +513,7 @@ mod test { + #[test] + fn defrag_with_missing_prefix() { + let mut x = Assembler::new(); +- x.insert(3, Bytes::from_static(b"def"), 3); ++ x.insert(3, Bytes::from_static(b"def"), 3).unwrap(); + x.defragment(); + assert_eq!( + next_unordered(&mut x), +@@ -509,17 +524,17 @@ mod test { + #[test] + fn defrag_read_chunk() { + let mut x = Assembler::new(); +- x.insert(3, Bytes::from_static(b"def"), 4); +- x.insert(0, Bytes::from_static(b"abc"), 4); +- x.insert(7, Bytes::from_static(b"hij"), 4); +- x.insert(11, Bytes::from_static(b"lmn"), 4); ++ x.insert(3, Bytes::from_static(b"def"), 4).unwrap(); ++ x.insert(0, Bytes::from_static(b"abc"), 4).unwrap(); ++ x.insert(7, Bytes::from_static(b"hij"), 4).unwrap(); ++ x.insert(11, Bytes::from_static(b"lmn"), 4).unwrap(); + x.defragment(); + assert_matches!(x.read(usize::MAX, true), Some(ref y) if &y.bytes[..] == b"abcdef"); +- x.insert(5, Bytes::from_static(b"fghijklmn"), 9); ++ x.insert(5, Bytes::from_static(b"fghijklmn"), 9).unwrap(); + assert_matches!(x.read(usize::MAX, true), Some(ref y) if &y.bytes[..] == b"ghijklmn"); +- x.insert(13, Bytes::from_static(b"nopq"), 4); ++ x.insert(13, Bytes::from_static(b"nopq"), 4).unwrap(); + assert_matches!(x.read(usize::MAX, true), Some(ref y) if &y.bytes[..] == b"opq"); +- x.insert(15, Bytes::from_static(b"pqrs"), 4); ++ x.insert(15, Bytes::from_static(b"pqrs"), 4).unwrap(); + assert_matches!(x.read(usize::MAX, true), Some(ref y) if &y.bytes[..] == b"rs"); + assert_matches!(x.read(usize::MAX, true), None); + } +@@ -528,13 +543,13 @@ mod test { + fn unordered_happy_path() { + let mut x = Assembler::new(); + x.ensure_ordering(false).unwrap(); +- x.insert(0, Bytes::from_static(b"abc"), 3); ++ x.insert(0, Bytes::from_static(b"abc"), 3).unwrap(); + assert_eq!( + next_unordered(&mut x), + Chunk::new(0, Bytes::from_static(b"abc")) + ); + assert_eq!(x.read(usize::MAX, false), None); +- x.insert(3, Bytes::from_static(b"def"), 3); ++ x.insert(3, Bytes::from_static(b"def"), 3).unwrap(); + assert_eq!( + next_unordered(&mut x), + Chunk::new(3, Bytes::from_static(b"def")) +@@ -546,15 +561,15 @@ mod test { + fn unordered_dedup() { + let mut x = Assembler::new(); + x.ensure_ordering(false).unwrap(); +- x.insert(3, Bytes::from_static(b"def"), 3); ++ x.insert(3, Bytes::from_static(b"def"), 3).unwrap(); + assert_eq!( + next_unordered(&mut x), + Chunk::new(3, Bytes::from_static(b"def")) + ); + assert_eq!(x.read(usize::MAX, false), None); +- x.insert(0, Bytes::from_static(b"a"), 1); +- x.insert(0, Bytes::from_static(b"abcdefghi"), 9); +- x.insert(0, Bytes::from_static(b"abcd"), 4); ++ x.insert(0, Bytes::from_static(b"a"), 1).unwrap(); ++ x.insert(0, Bytes::from_static(b"abcdefghi"), 9).unwrap(); ++ x.insert(0, Bytes::from_static(b"abcd"), 4).unwrap(); + assert_eq!( + next_unordered(&mut x), + Chunk::new(0, Bytes::from_static(b"a")) +@@ -568,30 +583,30 @@ mod test { + Chunk::new(6, Bytes::from_static(b"ghi")) + ); + assert_eq!(x.read(usize::MAX, false), None); +- x.insert(8, Bytes::from_static(b"ijkl"), 4); ++ x.insert(8, Bytes::from_static(b"ijkl"), 4).unwrap(); + assert_eq!( + next_unordered(&mut x), + Chunk::new(9, Bytes::from_static(b"jkl")) + ); + assert_eq!(x.read(usize::MAX, false), None); +- x.insert(12, Bytes::from_static(b"mno"), 3); ++ x.insert(12, Bytes::from_static(b"mno"), 3).unwrap(); + assert_eq!( + next_unordered(&mut x), + Chunk::new(12, Bytes::from_static(b"mno")) + ); + assert_eq!(x.read(usize::MAX, false), None); +- x.insert(2, Bytes::from_static(b"cde"), 3); ++ x.insert(2, Bytes::from_static(b"cde"), 3).unwrap(); + assert_eq!(x.read(usize::MAX, false), None); + } + + #[test] + fn chunks_dedup() { + let mut x = Assembler::new(); +- x.insert(3, Bytes::from_static(b"def"), 3); ++ x.insert(3, Bytes::from_static(b"def"), 3).unwrap(); + assert_eq!(x.read(usize::MAX, true), None); +- x.insert(0, Bytes::from_static(b"a"), 1); +- x.insert(1, Bytes::from_static(b"bcdefghi"), 9); +- x.insert(0, Bytes::from_static(b"abcd"), 4); ++ x.insert(0, Bytes::from_static(b"a"), 1).unwrap(); ++ x.insert(1, Bytes::from_static(b"bcdefghi"), 9).unwrap(); ++ x.insert(0, Bytes::from_static(b"abcd"), 4).unwrap(); + assert_eq!( + x.read(usize::MAX, true), + Some(Chunk::new(0, Bytes::from_static(b"abcd"))) +@@ -601,34 +616,34 @@ mod test { + Some(Chunk::new(4, Bytes::from_static(b"efghi"))) + ); + assert_eq!(x.read(usize::MAX, true), None); +- x.insert(8, Bytes::from_static(b"ijkl"), 4); ++ x.insert(8, Bytes::from_static(b"ijkl"), 4).unwrap(); + assert_eq!( + x.read(usize::MAX, true), + Some(Chunk::new(9, Bytes::from_static(b"jkl"))) + ); + assert_eq!(x.read(usize::MAX, true), None); +- x.insert(12, Bytes::from_static(b"mno"), 3); ++ x.insert(12, Bytes::from_static(b"mno"), 3).unwrap(); + assert_eq!( + x.read(usize::MAX, true), + Some(Chunk::new(12, Bytes::from_static(b"mno"))) + ); + assert_eq!(x.read(usize::MAX, true), None); +- x.insert(2, Bytes::from_static(b"cde"), 3); ++ x.insert(2, Bytes::from_static(b"cde"), 3).unwrap(); + assert_eq!(x.read(usize::MAX, true), None); + } + + #[test] + fn ordered_eager_discard() { + let mut x = Assembler::new(); +- x.insert(0, Bytes::from_static(b"abc"), 3); ++ x.insert(0, Bytes::from_static(b"abc"), 3).unwrap(); + assert_eq!(x.data.len(), 1); + assert_eq!( + x.read(usize::MAX, true), + Some(Chunk::new(0, Bytes::from_static(b"abc"))) + ); +- x.insert(0, Bytes::from_static(b"ab"), 2); ++ x.insert(0, Bytes::from_static(b"ab"), 2).unwrap(); + assert_eq!(x.data.len(), 0); +- x.insert(2, Bytes::from_static(b"cd"), 2); ++ x.insert(2, Bytes::from_static(b"cd"), 2).unwrap(); + assert_eq!( + x.data.peek(), + Some(&Buffer::new(3, Bytes::from_static(b"d"), 2)) +@@ -638,8 +653,8 @@ mod test { + #[test] + fn ordered_insert_unordered_read() { + let mut x = Assembler::new(); +- x.insert(0, Bytes::from_static(b"abc"), 3); +- x.insert(0, Bytes::from_static(b"abc"), 3); ++ x.insert(0, Bytes::from_static(b"abc"), 3).unwrap(); ++ x.insert(0, Bytes::from_static(b"abc"), 3).unwrap(); + x.ensure_ordering(false).unwrap(); + assert_eq!( + x.read(3, false), +diff --git a/vendor/quinn-proto/src/connection/mod.rs b/vendor/quinn-proto/src/connection/mod.rs +index 38ea8fbb..5b1d7c76 100644 +--- a/vendor/quinn-proto/src/connection/mod.rs ++++ b/vendor/quinn-proto/src/connection/mod.rs +@@ -2105,7 +2105,9 @@ impl Connection { + + space + .crypto_stream +- .insert(crypto.offset, crypto.data.clone(), payload_len); ++ .insert(crypto.offset, crypto.data.clone(), payload_len) ++ .map_err(|_| TransportError::INTERNAL_ERROR("too many gaps in crypto stream buffer"))?; ++ + while let Some(chunk) = space.crypto_stream.read(usize::MAX, true) { + trace!("consumed {} CRYPTO bytes", chunk.bytes.len()); + if self.crypto.read_handshake(&chunk.bytes)? { +diff --git a/vendor/quinn-proto/src/connection/streams/recv.rs b/vendor/quinn-proto/src/connection/streams/recv.rs +index 1aee5354..5361a22e 100644 +--- a/vendor/quinn-proto/src/connection/streams/recv.rs ++++ b/vendor/quinn-proto/src/connection/streams/recv.rs +@@ -78,7 +78,9 @@ impl Recv { + // Don't bother storing data or releasing stream-level flow control credit if the stream's + // already stopped + if !self.stopped { +- self.assembler.insert(frame.offset, frame.data, payload_len); ++ self.assembler ++ .insert(frame.offset, frame.data, payload_len) ++ .map_err(|_| TransportError::INTERNAL_ERROR("too many gaps in stream buffer"))?; + } + + Ok((new_bytes, frame.fin && self.stopped)) +-- +2.45.4 + diff --git a/SPECS/kata-containers/kata-containers.spec b/SPECS/kata-containers/kata-containers.spec index ae78c4c422b..0a0325b5909 100644 --- a/SPECS/kata-containers/kata-containers.spec +++ b/SPECS/kata-containers/kata-containers.spec @@ -2,7 +2,7 @@ Name: kata-containers Version: 3.32.0.kata0 -Release: 2%{?dist} +Release: 3%{?dist} Summary: Kata Containers package developed for Pod Sandboxing on AKS License: ASL 2.0 URL: https://github.com/microsoft/kata-containers @@ -17,6 +17,7 @@ Patch0: dbs-arch-cpuid-unsafe.patch Patch1: CVE-2025-11065.patch Patch2: CVE-2026-41602.patch Patch3: CVE-2026-56852.patch +Patch4: CVE-2026-25800.patch BuildRequires: azurelinux-release BuildRequires: golang BuildRequires: protobuf-compiler @@ -142,6 +143,9 @@ install -m 0644 \ %{tools_pkg}/tools/osbuilder/node-builder/azure-linux/agent-install/usr/lib/systemd/system/kata-agent.service %changelog +* Thu Aug 06 2026 Azure Linux Security Servicing Account - 3.32.0.kata0-3 +- Patch for CVE-2026-25800 + * Tue Aug 04 2026 Saul Paredes - 3.32.0.kata0-2 - Use smaller vendored sources