virtio-devices: balloon: Clamp range to region before PUNCH_HOLE

release_memory_range took (range_base, range_len) verbatim from a
guest-controlled descriptor (free-page-reporting and inflate paths) and
called fallocate(PUNCH_HOLE | KEEP_SIZE) on the backing file before any
length check ran. find_region only validates that range_base lands
inside *some* region; range_len can extend past the region's end. When
the operator uses --memory-zone file=PATH against a host file larger
than the zone, the punch zeroes host file content past the guest's
memory extent.

Free-page reporting is advisory, so the VMM is allowed to act on a
subset of a free range. Clamp range_len to the bytes that actually fit
within the region returned by find_region. Going past the end of a
region is most likely a guest bug so log it.

Signed-off-by: Dylan Reid <dgreid@fb.com>
This commit is contained in:
Dylan Reid
2026-04-24 17:39:09 -07:00
committed by Rob Bradford
parent 6d01695e5c
commit 326cd88074

View File

@@ -23,7 +23,7 @@ use std::sync::{Arc, Barrier};
use anyhow::anyhow;
use event_monitor::event;
use log::{error, info};
use log::{error, info, warn};
use seccompiler::SeccompAction;
use serde::{Deserialize, Serialize};
use thiserror::Error;
@@ -201,15 +201,30 @@ impl BalloonEpollHandler {
let region = memory.find_region(range_base).ok_or(Error::GuestMemory(
GuestMemoryError::InvalidGuestAddress(range_base),
))?;
// No underflow possible because range_base was found in the region by `find_region`.
let offset = range_base.0 - region.start_addr().0;
let region_limit = region.len() - offset;
let len = std::cmp::min(range_len as u64, region_limit);
if len < range_len as u64 {
warn!(
"Clamping reported range at GPA 0x{:x} from {} to {} bytes \
to fit inside its memory region",
range_base.0, range_len, len
);
}
if len == 0 {
return Ok(());
}
if let Some(f_off) = region.file_offset() {
let offset = range_base.0 - region.start_addr().0;
// SAFETY: FFI call with valid arguments
let res = unsafe {
libc::fallocate64(
f_off.file().as_raw_fd(),
libc::FALLOC_FL_PUNCH_HOLE | libc::FALLOC_FL_KEEP_SIZE,
(offset + f_off.start()) as libc::off64_t,
range_len as libc::off64_t,
len as libc::off64_t,
)
};
@@ -218,7 +233,7 @@ impl BalloonEpollHandler {
}
}
Self::advise_memory_range(memory, range_base, range_len, libc::MADV_DONTNEED)
Self::advise_memory_range(memory, range_base, len as usize, libc::MADV_DONTNEED)
}
fn release_memory_range_4k(