block: qcow: Reject backing file name outside first cluster

The qcow2 spec requires the backing file name string to live
entirely within the first cluster, between the end of the header
extension area and the cluster boundary. The parser previously
only validated the 1023 byte cap on the name length and accepted
any backing_file_offset, so a corrupt or malicious image could
place the name string anywhere in the file.

Add the cluster bound check in QcowHeader::new and report it via
a new BackingFileOutsideFirstCluster error.

Fixes: #8261

Signed-off-by: Anatol Belski <anbelski@linux.microsoft.com>
This commit is contained in:
Anatol Belski
2026-05-29 18:24:56 +02:00
committed by Rob Bradford
parent 3a1cf6e740
commit 4a2a9390be
2 changed files with 16 additions and 0 deletions

View File

@@ -332,6 +332,18 @@ impl QcowHeader {
return Err(Error::BackingFileTooLong(header.backing_file_size as usize));
}
if header.backing_file_offset != 0 {
let cluster_size = 1u64
.checked_shl(header.cluster_bits)
.ok_or(Error::InvalidClusterSize)?;
if header.backing_file_offset >= cluster_size
|| header.backing_file_offset + u64::from(header.backing_file_size) > cluster_size
{
return Err(Error::BackingFileOutsideFirstCluster(
header.backing_file_offset,
header.backing_file_size,
cluster_size,
));
}
f.seek(SeekFrom::Start(header.backing_file_offset))
.map_err(Error::ReadingHeader)?;
let mut backing_file_name_bytes = vec![0u8; header.backing_file_size as usize];

View File

@@ -68,6 +68,10 @@ pub enum Error {
BackingFileIo(String /* path */, #[source] io::Error),
#[error("Backing file open error: {0}")]
BackingFileOpen(String /* path */, #[source] Box<Error>),
#[error(
"Backing file name at offset {0:#x} length {1:#x} lies outside first cluster of {2:#x}"
)]
BackingFileOutsideFirstCluster(u64, u32, u64),
#[error("Backing file support is disabled")]
BackingFilesDisabled,
#[error("Backing file name is too long: {0} bytes over")]