diff --git a/netstack/src/buffer_pool.rs b/netstack/src/buffer_pool.rs index 85de46e41d..a17f4fd272 100644 --- a/netstack/src/buffer_pool.rs +++ b/netstack/src/buffer_pool.rs @@ -104,3 +104,64 @@ impl BufferPool { } } } + +#[cfg(test)] +mod tests { + use super::*; + use std::io::Write; + + #[test] + fn recycled_buffer_is_fully_zeroed_after_shrink() { + // Regression test for the F001 information-disclosure fix: + // a recycled buffer must be fully zeroed across its entire + // capacity, including the part that was previously a + // shorter logical length (the shrink-then-recycle path that + // originally leaked stale packet data into the new flow). + let mut pool = BufferPool::new(256); + + // Round 1: write a "secret" then shrink. + let mut buf = pool.get_buffer(); + buf.as_mut_slice() + .write_all(b"top secret password: hunter2") + .unwrap(); + buf.as_mut_slice().truncate(8); + + // Recycle. + drop(buf); + let buf = pool.get_buffer(); + + // Round 2: every byte must be zero. + let zeros = buf.as_ref().iter().all(|&b| b == 0); + assert!(zeros, "recycled buffer is not fully zeroed"); + } + + #[test] + fn recycled_buffer_zeroed_at_known_nonzero_size() { + // Make sure a recycled buffer that was previously written + // with nonzero data and resized to 8 bytes still shows all + // zeros across its new capacity. This catches the previous + // v.fill(0) + set_len(capacity) implementation that only zeroed + // the old logical length. + let mut pool = BufferPool::new(64); + + let mut buf = pool.get_buffer(); + for i in 0..buf.as_ref().len() { + buf.as_mut_slice()[i] = 0xAB; + } + buf.as_mut_slice().truncate(32); + + drop(buf); + let buf = pool.get_buffer(); + for i in 0..buf.as_ref().len() { + assert_eq!(buf.as_ref()[i], 0, "byte {i} not zeroed"); + } + } + + #[test] + fn fresh_buffer_is_zeroed() { + // First use of a buffer from a fresh pool must be all-zero. + let mut pool = BufferPool::new(128); + let buf = pool.get_buffer(); + assert!(buf.as_ref().iter().all(|&b| b == 0)); + } +}