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:
@@ -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]
|
#[test]
|
||||||
fn read_chunks_large_request_to_max_transfer() {
|
fn read_chunks_large_request_to_max_transfer() {
|
||||||
// max_transfer = 4 sectors (4 * 2048 = 8192 bytes). A read of 10
|
// max_transfer = 4 sectors (4 * 2048 = 8192 bytes). A read of 10
|
||||||
|
|||||||
Reference in New Issue
Block a user