firecracker-microvm/firecracker · error · std::io::Error
discard length overflow
Error message
discard length overflow
What it means
This std::io::Error (InvalidInput) is raised in file_discard_range when the u32/u64 discard length cannot be converted to libc::off_t for the fallocate(2) call used to punch a hole in a regular file. fallocate's length parameter is a signed off_t, so a length above i64::MAX fails TryFrom and the engine returns this error instead of passing a truncated value to the kernel. Note len itself is a u32 at the call site (u64::from(len) only for the block path), so this is effectively unreachable via normal callers and mainly guards direct/internal use and future widening.
Source
Thrown at src/vmm/src/devices/virtio/block/virtio/io/sync_io.rs:62
return Ok(0);
}
let discarded = len;
let len = u64::from(len);
if file_type.is_block_device() {
let mut range = [offset, len];
// SAFETY: file is a valid fd, BLKDISCARD expects a pointer to two u64 values
// representing byte offset and byte length.
let ret = unsafe { libc::ioctl(file.as_raw_fd(), BLKDISCARD, range.as_mut_ptr()) };
if ret < 0 {
return Err(std::io::Error::last_os_error());
}
} else {
let off = libc::off_t::try_from(offset).map_err(|_| {
std::io::Error::new(std::io::ErrorKind::InvalidInput, "discard offset overflow")
})?;
let len = libc::off_t::try_from(len).map_err(|_| {
std::io::Error::new(std::io::ErrorKind::InvalidInput, "discard length overflow")
})?;
// SAFETY: file is a valid fd and fallocate does not retain the passed values.
let ret = unsafe {
libc::fallocate(
file.as_raw_fd(),
FALLOC_FL_KEEP_SIZE | FALLOC_FL_PUNCH_HOLE,
off,
len,
)
};
if ret < 0 {
return Err(std::io::Error::last_os_error());
}
}
Ok(discarded)
}
View on GitHub (pinned to a5a45f68b8)
Solutions
- Bound the discard length to the maximum the syscall supports (min(len, disk_size - offset)) before calling file_discard_range.
- If length is derived from guest sectors, cap the per-request discard segment size in the virtio-block layer (e.g. reject len > i64::MAX as InvalidInput before the call).
- Keep len as u32 at the public boundary (as the current API does) so the conversion can never fail; only widen it with an explicit range check.
- On 32-bit platforms, ensure off_t is 64-bit (LFS) if lengths larger than 2 GiB are expected.
Example fix
// before: unbounded length handed to file_discard_range file_discard_range(&file, (offset, len_u64))?; // after: clamp so it always fits off_t and the file let len_u64 = len_u64.min(i64::MAX as u64).min(disk_size - offset); file_discard_range(&file, (offset, u32::try_from(len_u64).unwrap()))?;
Defensive patterns
Strategy: validation
Validate before calling
const OFF_T_MAX: u64 = i64::MAX as u64;
fn discard_len_ok(offset: u64, len: u64, disk_size: u64) -> bool {
len > 0 && len <= OFF_T_MAX && offset.saturating_add(len) <= disk_size
}
// call before file_discard_range:
assert!(discard_len_ok(offset, len as u64, disk_size), "discard length out of range"); Type guard
fn fits_off_t(v: u64) -> bool { v <= i64::MAX as u64 } Try / catch
match file_discard_range(&file, (offset, len)) {
Ok(n) => n,
Err(e) if e.kind() == std::io::ErrorKind::InvalidInput && e.to_string().contains("length overflow") => {
// clamp len or reject the request; do not retry
}
Err(e) => return Err(SyncIoError::Discard(e)),
} Prevention
- Keep the discard length at u32 at the API boundary so overflow is structurally impossible; widen only with an explicit check.
- Clamp len to disk_size - offset before calling, which also bounds it below off_t limits.
- Watch for u32-to-u64 widening in refactors; any widening reintroduces the overflow path.
- On 32-bit platforms verify off_t width (LFS) since even u32 lengths can overflow a 32-bit off_t.
When it happens
Trigger: Calling file_discard_range with range.1 (len) whose u64 value exceeds i64::MAX on a non-block-device file, making libc::off_t::try_from(len) fail; only reachable through internal calls or test code since SyncFileEngine::discard passes a u32 length.
Common situations: Internal refactors widening the length parameter from u32 to u64 without bounding it; test code (e.g. test_discard_regular_file variants) passing synthetic huge lengths; future support for larger discard segments on a 32-bit target where off_t is 32-bit and a u32 length near u32::MAX (2 GiB) would overflow a 32-bit off_t.
Understand the failure class
Background: "value must be between 0 and 1" / "out of range" / "must not be negative" errors: fixing range-validation failures across open-source libraries — this error's family across 42 libraries.
Related errors
AI-assisted analysis of firecracker-microvm/firecracker@a5a45f68b8 (2026-09-10).
Data as JSON: /api/errors/ceb5d909290f6e78.
Report an issue: GitHub.