From 2d8811ad820f2c20243272c6376e2f95f52a77e9 Mon Sep 17 00:00:00 2001 From: Anatol Belski Date: Fri, 29 May 2026 19:30:40 +0200 Subject: [PATCH] block: qcow: Test rejection when backing file offset equals cluster size A backing file string placed exactly at cluster_size starts past the first cluster boundary, so QcowHeader::new must reject it. Introduce a read_header_with_patched_backing helper that builds a valid header, patches backing_file_offset and backing_file_size, writes it out and re-parses it. Use it to cover this case. Assisted-by: Claude:Opus-4.7 Signed-off-by: Anatol Belski --- block/src/formats/qcow/internal/mod.rs | 32 ++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/block/src/formats/qcow/internal/mod.rs b/block/src/formats/qcow/internal/mod.rs index b3203d2f8..56f7660e4 100644 --- a/block/src/formats/qcow/internal/mod.rs +++ b/block/src/formats/qcow/internal/mod.rs @@ -2434,6 +2434,7 @@ mod unit_tests { use vmm_sys_util::tempfile::TempFile; use vmm_sys_util::write_zeroes::WriteZeroes; + use super::header::DEFAULT_CLUSTER_BITS; use super::util::{COMPRESSED_FLAG, ZERO_FLAG}; use super::*; use crate::formats::qcow::common::unit_tests::compress_allocated_clusters; @@ -2695,6 +2696,37 @@ mod unit_tests { ); } + /// Write a header to a fresh file with backing_file_offset and + /// backing_file_size patched. Panics on setup failures, returns + /// the parse result of the patched header. + fn read_header_with_patched_backing(offset: u64, size: u32) -> Result { + let mut header = QcowHeader::create_for_size_and_path(3, 0x10_0000, None) + .expect("Failed to create header."); + header.backing_file_offset = offset; + header.backing_file_size = size; + let mut disk_file: RawFile = RawFile::new( + TempFile::new() + .expect("Failed to create temp file.") + .into_file(), + false, + ); + header + .write_to(&mut disk_file) + .expect("Failed to write header."); + disk_file.rewind().expect("Failed to rewind disk file."); + QcowHeader::new(&mut disk_file) + } + + #[test] + fn backing_file_offset_at_cluster_boundary() { + let cluster_size = 1u64 << DEFAULT_CLUSTER_BITS; + let err = read_header_with_patched_backing(cluster_size, 1).unwrap_err(); + assert!(matches!( + err, + Error::BackingFileOutsideFirstCluster(_, _, _) + )); + } + /// Helper to create a test file with header extensions fn create_header_with_extension(ext_type: u32, ext_data: &[u8]) -> (RawFile, QcowHeader) { let header = QcowHeader::create_for_size_and_path(3, 0x10_0000, None)