From 326cd88074482875d69e21f4b7bcee23a3701bcc Mon Sep 17 00:00:00 2001 From: Dylan Reid Date: Fri, 24 Apr 2026 17:39:09 -0700 Subject: [PATCH] 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 --- virtio-devices/src/balloon.rs | 23 +++++++++++++++++++---- 1 file changed, 19 insertions(+), 4 deletions(-) diff --git a/virtio-devices/src/balloon.rs b/virtio-devices/src/balloon.rs index f9db09bd3..37ebb056f 100644 --- a/virtio-devices/src/balloon.rs +++ b/virtio-devices/src/balloon.rs @@ -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(