Assert the undersized-buffer guard on the chunked read path

Drive::read and read_fua split any request larger than the transport's
transfer limit into chunks and slice the caller's buffer by count * 2048.
The up-front length check is the only thing between an undersized buffer and
a "range end index out of range" panic out of a public API, and the comment
above it records that this was once a live panic.

Every existing read test stays on the single-chunk path, where an undersized
buffer is already tolerated and returns Err(DiscRead), so the guard itself
had no coverage at all and a mutation run flipped its arithmetic freely.

The test drives a mock transport with a small transfer limit and asserts the
two paths agree: an undersized buffer is an error either way, and behaviour
on a caller mistake does not depend on the drive's transfer limit. Confirmed
by hand that both the reported * -> + mutation and a < -> > flip now fail.
This commit is contained in:
Matthew Jackson
2026-08-01 13:56:58 -07:00
parent 1b95d346bb
commit 4e70d9a5c5
+37
View File
@@ -2054,6 +2054,43 @@ mod command_tests {
}
}
/// An undersized caller buffer must be rejected identically whether the
/// request fits in one transfer or has to be chunked.
///
/// The chunk loop slices `buf` by `count * 2048`, so without the up-front
/// length check this PANICKED with "range end index out of range" out of
/// the public `read`/`read_fua` — while the single-chunk path tolerated the
/// same buffer and returned `Err(DiscRead)`. Behaviour on a caller error
/// must not depend on the drive's transfer limit, and a library inside a
/// long-running service must not panic on it at all.
///
/// Every other read test stays on the single-chunk path, so this guard was
/// entirely unasserted and a mutation run flipped its arithmetic freely.
#[test]
fn an_undersized_buffer_errors_on_the_chunked_path_just_like_the_single_one() {
// max_transfer = 4 sectors, so a 10-sector read must chunk.
let mut h = chunking(4 * 2048, None);
let mut small = vec![0u8; 4096]; // 2 sectors' worth for a 10-sector read
let chunked = h.drive.read(0, 10, &mut small, false);
assert!(
matches!(chunked, Err(Error::DiscRead { .. })),
"an undersized buffer on the chunked path must be an error, not a panic"
);
// The single-chunk path, same undersized buffer, same verdict.
let single = h.drive.read(0, 3, &mut small, false);
assert!(
matches!(single, Err(Error::DiscRead { .. })),
"the single-chunk path must agree"
);
// Exactly-sized still works, so the guard is not simply rejecting
// everything on the chunked path.
let mut exact = vec![0u8; 10 * 2048];
assert!(h.drive.read(0, 10, &mut exact, false).is_ok());
}
#[test]
fn read_chunks_large_request_to_max_transfer() {
// max_transfer = 4 sectors (4 * 2048 = 8192 bytes). A read of 10