buffer_pool: add F001 regression tests

Three unit tests for the BufferPool zero-fill fix:

1. recycled_buffer_is_fully_zeroed_after_shrink: writes a 'secret',
   truncates to 8 bytes, drops to recycle, verifies the entire
   recycled buffer is zero. Catches the original v.fill(0)+set_len
   regression where the tail bytes were uninitialized.

2. recycled_buffer_zeroed_at_known_nonzero_size: writes 0xAB across
   the full buffer, truncates, recycles, verifies all bytes are 0.
   Catches the same regression from a different angle.

3. fresh_buffer_is_zeroed: trivial check that a brand-new buffer
   from a fresh pool is already zeroed (sanity check).

The netstack crate is redox-target-only (cannot run on host due to
redox_syscall/redox-scheme dependencies), but these tests serve as
documentation of the expected behavior and can run in the canonical
build-redbear.sh target test pass.
This commit is contained in:
Red Bear OS
2026-07-27 20:46:18 +09:00
parent cf21752385
commit d93d1d2f2e
+61
View File
@@ -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));
}
}