vmm, virtio-devices: Deny zero sector writes for autodetected raw images

If the disk image was autodetected to raw (not specified with image_type
= 0) then in the virtio-block subsystem generate errors for writes to
block 0 (treat as if read-only). This gives an immediate error vs using
the image implementations in the block subsystem.

Signed-off-by: Rob Bradford <rbradford@meta.com>
This commit is contained in:
Rob Bradford
2026-02-15 22:51:56 +00:00
committed by Bo Chen
parent 6f2357c14e
commit b3e8e2abc5
3 changed files with 29 additions and 4 deletions

View File

@@ -68,6 +68,7 @@ fuzz_target!(|bytes: &[u8]| -> Corpus {
None,
queue_affinity,
true,
false,
)
.unwrap();

View File

@@ -160,6 +160,7 @@ struct BlockEpollHandler {
access_platform: Option<Arc<dyn AccessPlatform>>,
host_cpus: Option<Vec<usize>>,
acked_features: u64,
disable_sector0_writes: bool,
}
fn has_feature(features: u64, feature_flag: u64) -> bool {
@@ -167,8 +168,13 @@ fn has_feature(features: u64, feature_flag: u64) -> bool {
}
impl BlockEpollHandler {
fn check_request(features: u64, request_type: RequestType) -> result::Result<(), ExecuteError> {
if has_feature(features, VIRTIO_BLK_F_RO.into())
fn check_request(
features: u64,
request: &Request,
disable_sector0_writes: bool,
) -> result::Result<(), ExecuteError> {
let request_type = request.request_type;
if (has_feature(features, VIRTIO_BLK_F_RO.into()))
&& !(request_type == RequestType::In
|| request_type == RequestType::GetDeviceId
|| request_type == RequestType::Flush)
@@ -178,6 +184,11 @@ impl BlockEpollHandler {
// if the VIRTIO_BLK_F_RO feature if offered, and MUST NOT write any data."
return Err(ExecuteError::ReadOnly);
}
if request_type == RequestType::Out && disable_sector0_writes && request.sector == 0 {
return Err(ExecuteError::ReadOnly);
}
Ok(())
}
@@ -193,7 +204,10 @@ impl BlockEpollHandler {
// For virtio spec compliance
// "A device MUST set the status byte to VIRTIO_BLK_S_IOERR for a write request
// if the VIRTIO_BLK_F_RO feature if offered, and MUST NOT write any data."
if let Err(e) = Self::check_request(self.acked_features, request.request_type) {
// Also, if sector 0 writes are disabled, treat writes to sector 0 as read-only as well.
if let Err(e) =
Self::check_request(self.acked_features, &request, self.disable_sector0_writes)
{
warn!("Request check failed: {request:x?} {e:?}");
desc_chain
.memory()
@@ -646,6 +660,7 @@ pub struct Block {
exit_evt: EventFd,
serial: Vec<u8>,
queue_affinity: BTreeMap<u16, Vec<usize>>,
disable_sector0_writes: bool,
}
#[derive(Serialize, Deserialize)]
@@ -675,6 +690,7 @@ impl Block {
state: Option<BlockState>,
queue_affinity: BTreeMap<u16, Vec<usize>>,
sparse: bool,
disable_sector0_writes: bool,
) -> io::Result<Self> {
let (disk_nsectors, avail_features, acked_features, config, paused) =
if let Some(state) = state {
@@ -789,6 +805,7 @@ impl Block {
exit_evt,
serial,
queue_affinity,
disable_sector0_writes,
})
}
@@ -1040,6 +1057,7 @@ impl VirtioDevice for Block {
access_platform: self.common.access_platform.clone(),
host_cpus: self.queue_affinity.get(&queue_idx).cloned(),
acked_features: self.common.acked_features,
disable_sector0_writes: self.disable_sector0_writes,
};
let paused = self.common.paused.clone();

View File

@@ -2677,13 +2677,18 @@ impl DeviceManager {
let detected_image_type =
detect_image_type(&mut file).map_err(DeviceManagerError::DetectImageType)?;
let mut disable_sector0_writes = false;
if disk_cfg.image_type == ImageType::Unknown {
warn!(
"No image_type specified - detected as {detected_image_type}. \
Configuration updated to persist type across reboots and migrations."
);
if detected_image_type != ImageType::Raw {
if detected_image_type == ImageType::Raw {
warn!("Autodetected raw image type. Disabling sector 0 writes.");
disable_sector0_writes = true;
} else {
warn!(
"Non-raw image type detected. In the future it will be necessary \
to specify image_type for non-raw files."
@@ -2850,6 +2855,7 @@ impl DeviceManager {
.map_err(DeviceManagerError::RestoreGetState)?,
queue_affinity,
disk_cfg.sparse,
disable_sector0_writes,
)
.map_err(DeviceManagerError::CreateVirtioBlock)?;