block: Validate segment count for DISCARD and WRITE_ZEROES requests

Split the data length check into two conditions:

- reject descriptors shorter than one
virtio_blk_discard_write_zeroes segment, and
- reject payloads exceeding MAX_DISCARD_WRITE_ZEROES_SEG segments

Previously only the minimum length was checked and extra segments
were silently dropped.

Signed-off-by: Anatol Belski <anbelski@linux.microsoft.com>
This commit is contained in:
Anatol Belski
2026-03-21 09:37:35 +01:00
committed by Rob Bradford
parent fc79d08d7d
commit d3cad420a5
2 changed files with 19 additions and 6 deletions

View File

@@ -70,7 +70,11 @@ use crate::vhdx::VhdxError;
const SECTOR_SHIFT: u8 = 9;
pub const SECTOR_SIZE: u64 = 0x01 << SECTOR_SHIFT;
/// Field offsets within `struct virtio_blk_discard_write_zeroes`.
/// Maximum number of segments per DISCARD or WRITE_ZEROES request.
pub const MAX_DISCARD_WRITE_ZEROES_SEG: u32 = 1;
/// Size and field offsets within `struct virtio_blk_discard_write_zeroes`.
const DISCARD_WZ_SEG_SIZE: u32 = mem::size_of::<virtio_blk_discard_write_zeroes>() as u32;
const DISCARD_WZ_SECTOR_OFFSET: u64 =
mem::offset_of!(virtio_blk_discard_write_zeroes, sector) as u64;
const DISCARD_WZ_NUM_SECTORS_OFFSET: u64 =
@@ -105,6 +109,8 @@ pub enum Error {
RawFileError(#[source] std::io::Error),
#[error("The requested operation does not support multiple descriptors")]
TooManyDescriptors,
#[error("Request contains too many segments")]
TooManySegments,
#[error("Failure in vhdx")]
VhdxError(#[source] VhdxError),
}
@@ -591,9 +597,12 @@ impl Request {
return Err(ExecuteError::BadRequest(Error::TooManyDescriptors));
};
if data_len < 16 {
if data_len < DISCARD_WZ_SEG_SIZE {
return Err(ExecuteError::BadRequest(Error::DescriptorLengthTooSmall));
}
if data_len > DISCARD_WZ_SEG_SIZE * MAX_DISCARD_WRITE_ZEROES_SEG {
return Err(ExecuteError::BadRequest(Error::TooManySegments));
}
let mut discard_sector = [0u8; 8];
let mut discard_num_sectors = [0u8; 4];
@@ -630,9 +639,12 @@ impl Request {
return Err(ExecuteError::BadRequest(Error::TooManyDescriptors));
};
if data_len < 16 {
if data_len < DISCARD_WZ_SEG_SIZE {
return Err(ExecuteError::BadRequest(Error::DescriptorLengthTooSmall));
}
if data_len > DISCARD_WZ_SEG_SIZE * MAX_DISCARD_WRITE_ZEROES_SEG {
return Err(ExecuteError::BadRequest(Error::TooManySegments));
}
let mut wz_sector = [0u8; 8];
let mut wz_num_sectors = [0u8; 4];

View File

@@ -23,7 +23,8 @@ use block::disk_file::DiskBackend;
use block::error::BlockError;
use block::fcntl::{LockError, LockGranularity, LockGranularityChoice, LockType, get_lock_state};
use block::{
ExecuteAsync, ExecuteError, Request, RequestType, VirtioBlockConfig, build_serial, fcntl,
ExecuteAsync, ExecuteError, MAX_DISCARD_WRITE_ZEROES_SEG, Request, RequestType,
VirtioBlockConfig, build_serial, fcntl,
};
use event_monitor::event;
use log::{debug, error, info, warn};
@@ -829,12 +830,12 @@ impl Block {
if avail_features & (1u64 << VIRTIO_BLK_F_WRITE_ZEROES) != 0 {
config.max_write_zeroes_sectors = u32::MAX;
config.max_write_zeroes_seg = 1;
config.max_write_zeroes_seg = MAX_DISCARD_WRITE_ZEROES_SEG;
config.write_zeroes_may_unmap = if discard_supported { 1 } else { 0 };
}
if avail_features & (1u64 << VIRTIO_BLK_F_DISCARD) != 0 {
config.max_discard_sectors = u32::MAX;
config.max_discard_seg = 1;
config.max_discard_seg = MAX_DISCARD_WRITE_ZEROES_SEG;
config.discard_sector_alignment = (logical_block_size / SECTOR_SIZE) as u32;
}