|
| 1 | +From 9049e95ce8e99bea5d863f029bfa0ddf356f39f8 Mon Sep 17 00:00:00 2001 |
| 2 | +From: Rob Bradford <rbradford@meta.com> |
| 3 | +Date: Sun, 8 Feb 2026 21:14:28 +0000 |
| 4 | +Subject: [PATCH] vmm: Add option to control backing files |
| 5 | + |
| 6 | +Backing files (e.g. for QCOW2) interact badly with landlock since they |
| 7 | +are not obvious from the initial VM configuration. Only enable their use |
| 8 | +with an explicit option. |
| 9 | + |
| 10 | +Signed-off-by: Rob Bradford <rbradford@meta.com> |
| 11 | +Signed-off-by: Azure Linux Security Servicing Account <azurelinux-security@microsoft.com> |
| 12 | +Upstream-reference: https://github.com/microsoft/cloud-hypervisor/commit/69e16ca82cdcd7ad3c4361223a4754cc8ce7f672.patch |
| 13 | +--- |
| 14 | + block/src/lib.rs | 1 + |
| 15 | + block/src/qcow_sync.rs | 14 ++++++++++---- |
| 16 | + tests/integration.rs | 39 +++++++++++++++++++++++++++------------ |
| 17 | + vmm/src/config.rs | 13 +++++++++++-- |
| 18 | + vmm/src/device_manager.rs | 6 +++++- |
| 19 | + vmm/src/vm_config.rs | 2 ++ |
| 20 | + 6 files changed, 56 insertions(+), 19 deletions(-) |
| 21 | + |
| 22 | +diff --git a/block/src/lib.rs b/block/src/lib.rs |
| 23 | +index 5599258..ef8c05b 100644 |
| 24 | +--- a/block/src/lib.rs |
| 25 | ++++ b/block/src/lib.rs |
| 26 | +@@ -790,6 +790,7 @@ pub trait AsyncAdaptor { |
| 27 | + } |
| 28 | + } |
| 29 | + |
| 30 | ++#[derive(PartialEq, Eq, Debug)] |
| 31 | + pub enum ImageType { |
| 32 | + FixedVhd, |
| 33 | + Qcow2, |
| 34 | +diff --git a/block/src/qcow_sync.rs b/block/src/qcow_sync.rs |
| 35 | +index cd6a1fb..a05022f 100644 |
| 36 | +--- a/block/src/qcow_sync.rs |
| 37 | ++++ b/block/src/qcow_sync.rs |
| 38 | +@@ -20,10 +20,16 @@ pub struct QcowDiskSync { |
| 39 | + } |
| 40 | + |
| 41 | + impl QcowDiskSync { |
| 42 | +- pub fn new(file: File, direct_io: bool) -> QcowResult<Self> { |
| 43 | +- Ok(QcowDiskSync { |
| 44 | +- qcow_file: QcowFile::from(RawFile::new(file, direct_io))?, |
| 45 | +- }) |
| 46 | ++ pub fn new(file: File, direct_io: bool, backing_files: bool) -> QcowResult<Self> { |
| 47 | ++ if backing_files { |
| 48 | ++ Ok(QcowDiskSync { |
| 49 | ++ qcow_file: QcowFile::from(RawFile::new(file, direct_io))?, |
| 50 | ++ }) |
| 51 | ++ } else { |
| 52 | ++ Ok(QcowDiskSync { |
| 53 | ++ qcow_file: QcowFile::from_with_nesting_depth(RawFile::new(file, direct_io), 0)?, |
| 54 | ++ }) |
| 55 | ++ } |
| 56 | + } |
| 57 | + } |
| 58 | + |
| 59 | +diff --git a/tests/integration.rs b/tests/integration.rs |
| 60 | +index cf20554..f6c8755 100644 |
| 61 | +--- a/tests/integration.rs |
| 62 | ++++ b/tests/integration.rs |
| 63 | +@@ -3631,6 +3631,7 @@ mod common_parallel { |
| 64 | + disable_io_uring: bool, |
| 65 | + disable_aio: bool, |
| 66 | + verify_os_disk: bool, |
| 67 | ++ backing_files: bool, |
| 68 | + ) { |
| 69 | + let disk_config = UbuntuDiskConfig::new(image_name.to_string()); |
| 70 | + let guest = Guest::new(Box::new(disk_config)); |
| 71 | +@@ -3653,8 +3654,9 @@ mod common_parallel { |
| 72 | + .args([ |
| 73 | + "--disk", |
| 74 | + format!( |
| 75 | +- "path={}", |
| 76 | +- guest.disk_config.disk(DiskType::OperatingSystem).unwrap() |
| 77 | ++ "path={},backing_files={}", |
| 78 | ++ guest.disk_config.disk(DiskType::OperatingSystem).unwrap(), |
| 79 | ++ if backing_files { "on"} else {"off"} |
| 80 | + ) |
| 81 | + .as_str(), |
| 82 | + format!( |
| 83 | +@@ -3741,17 +3743,17 @@ mod common_parallel { |
| 84 | + |
| 85 | + #[test] |
| 86 | + fn test_virtio_block_io_uring() { |
| 87 | +- _test_virtio_block(FOCAL_IMAGE_NAME, false, true, false); |
| 88 | ++ _test_virtio_block(FOCAL_IMAGE_NAME, false, true, false, false); |
| 89 | + } |
| 90 | + |
| 91 | + #[test] |
| 92 | + fn test_virtio_block_aio() { |
| 93 | +- _test_virtio_block(FOCAL_IMAGE_NAME, true, false, false); |
| 94 | ++ _test_virtio_block(FOCAL_IMAGE_NAME, true, false, false, false); |
| 95 | + } |
| 96 | + |
| 97 | + #[test] |
| 98 | + fn test_virtio_block_sync() { |
| 99 | +- _test_virtio_block(FOCAL_IMAGE_NAME, true, true, false); |
| 100 | ++ _test_virtio_block(FOCAL_IMAGE_NAME, true, true, false, false); |
| 101 | + } |
| 102 | + |
| 103 | + fn run_qemu_img(path: &std::path::Path, args: &[&str]) -> std::process::Output { |
| 104 | +@@ -3927,22 +3929,28 @@ mod common_parallel { |
| 105 | + |
| 106 | + #[test] |
| 107 | + fn test_virtio_block_qcow2() { |
| 108 | +- _test_virtio_block(JAMMY_IMAGE_NAME_QCOW2, false, false, true); |
| 109 | ++ _test_virtio_block(JAMMY_IMAGE_NAME_QCOW2, false, false, true, false); |
| 110 | + } |
| 111 | + |
| 112 | + #[test] |
| 113 | + fn test_virtio_block_qcow2_zlib() { |
| 114 | +- _test_virtio_block(JAMMY_IMAGE_NAME_QCOW2_ZLIB, false, false, true); |
| 115 | ++ _test_virtio_block(JAMMY_IMAGE_NAME_QCOW2_ZLIB, false, false, true, false); |
| 116 | + } |
| 117 | + |
| 118 | + #[test] |
| 119 | + fn test_virtio_block_qcow2_zstd() { |
| 120 | +- _test_virtio_block(JAMMY_IMAGE_NAME_QCOW2_ZSTD, false, false, true); |
| 121 | ++ _test_virtio_block(JAMMY_IMAGE_NAME_QCOW2_ZSTD, false, false, true, false); |
| 122 | + } |
| 123 | + |
| 124 | + #[test] |
| 125 | + fn test_virtio_block_qcow2_backing_zstd_file() { |
| 126 | +- _test_virtio_block(JAMMY_IMAGE_NAME_QCOW2_BACKING_ZSTD_FILE, false, false, true); |
| 127 | ++ _test_virtio_block( |
| 128 | ++ JAMMY_IMAGE_NAME_QCOW2_BACKING_ZSTD_FILE, |
| 129 | ++ false, |
| 130 | ++ false, |
| 131 | ++ true, |
| 132 | ++ true, |
| 133 | ++ ); |
| 134 | + } |
| 135 | + |
| 136 | + #[test] |
| 137 | +@@ -3952,12 +3960,19 @@ mod common_parallel { |
| 138 | + false, |
| 139 | + false, |
| 140 | + true, |
| 141 | ++ true, |
| 142 | + ); |
| 143 | + } |
| 144 | + |
| 145 | + #[test] |
| 146 | + fn test_virtio_block_qcow2_backing_raw_file() { |
| 147 | +- _test_virtio_block(JAMMY_IMAGE_NAME_QCOW2_BACKING_RAW_FILE, false, false, true); |
| 148 | ++ _test_virtio_block( |
| 149 | ++ JAMMY_IMAGE_NAME_QCOW2_BACKING_RAW_FILE, |
| 150 | ++ false, |
| 151 | ++ false, |
| 152 | ++ true, |
| 153 | ++ true, |
| 154 | ++ ); |
| 155 | + } |
| 156 | + |
| 157 | + #[test] |
| 158 | +@@ -3982,7 +3997,7 @@ mod common_parallel { |
| 159 | + .output() |
| 160 | + .expect("Expect generating VHD image from RAW image"); |
| 161 | + |
| 162 | +- _test_virtio_block(FOCAL_IMAGE_NAME_VHD, false, false, false); |
| 163 | ++ _test_virtio_block(FOCAL_IMAGE_NAME_VHD, false, false, false, false); |
| 164 | + } |
| 165 | + |
| 166 | + #[test] |
| 167 | +@@ -4006,7 +4021,7 @@ mod common_parallel { |
| 168 | + .output() |
| 169 | + .expect("Expect generating dynamic VHDx image from RAW image"); |
| 170 | + |
| 171 | +- _test_virtio_block(FOCAL_IMAGE_NAME_VHDX, false, false, true); |
| 172 | ++ _test_virtio_block(FOCAL_IMAGE_NAME_VHDX, false, false, true, false); |
| 173 | + } |
| 174 | + |
| 175 | + #[test] |
| 176 | +diff --git a/vmm/src/config.rs b/vmm/src/config.rs |
| 177 | +index 196956a..f4d201f 100644 |
| 178 | +--- a/vmm/src/config.rs |
| 179 | ++++ b/vmm/src/config.rs |
| 180 | +@@ -1091,7 +1091,7 @@ impl DiskConfig { |
| 181 | + ops_size=<io_ops>,ops_one_time_burst=<io_ops>,ops_refill_time=<ms>,\ |
| 182 | + id=<device_id>,pci_segment=<segment_id>,rate_limit_group=<group_id>,\ |
| 183 | + queue_affinity=<list_of_queue_indices_with_their_associated_cpuset>,\ |
| 184 | +- serial=<serial_number>"; |
| 185 | ++ serial=<serial_number>,backing_files=on|off"; |
| 186 | + |
| 187 | + pub fn parse(disk: &str) -> Result<Self> { |
| 188 | + let mut parser = OptionParser::new(); |
| 189 | +@@ -1116,7 +1116,8 @@ impl DiskConfig { |
| 190 | + .add("pci_segment") |
| 191 | + .add("serial") |
| 192 | + .add("rate_limit_group") |
| 193 | +- .add("queue_affinity"); |
| 194 | ++ .add("queue_affinity") |
| 195 | ++ .add("backing_files"); |
| 196 | + parser.parse(disk).map_err(Error::ParseDisk)?; |
| 197 | + |
| 198 | + let path = parser.get("path").map(PathBuf::from); |
| 199 | +@@ -1201,6 +1202,12 @@ impl DiskConfig { |
| 200 | + }) |
| 201 | + .collect() |
| 202 | + }); |
| 203 | ++ let backing_files = parser |
| 204 | ++ .convert::<Toggle>("backing_files") |
| 205 | ++ .map_err(Error::ParseDisk)? |
| 206 | ++ .unwrap_or(Toggle(false)) |
| 207 | ++ .0; |
| 208 | ++ |
| 209 | + let bw_tb_config = if bw_size != 0 && bw_refill_time != 0 { |
| 210 | + Some(TokenBucketConfig { |
| 211 | + size: bw_size, |
| 212 | +@@ -1245,6 +1252,7 @@ impl DiskConfig { |
| 213 | + pci_segment, |
| 214 | + serial, |
| 215 | + queue_affinity, |
| 216 | ++ backing_files, |
| 217 | + }) |
| 218 | + } |
| 219 | + |
| 220 | +@@ -3425,6 +3433,7 @@ mod tests { |
| 221 | + pci_segment: 0, |
| 222 | + serial: None, |
| 223 | + queue_affinity: None, |
| 224 | ++ backing_files: false, |
| 225 | + } |
| 226 | + } |
| 227 | + |
| 228 | +diff --git a/vmm/src/device_manager.rs b/vmm/src/device_manager.rs |
| 229 | +index d47c461..b26dfea 100644 |
| 230 | +--- a/vmm/src/device_manager.rs |
| 231 | ++++ b/vmm/src/device_manager.rs |
| 232 | +@@ -2706,6 +2706,10 @@ impl DeviceManager { |
| 233 | + let image_type = |
| 234 | + detect_image_type(&mut file).map_err(DeviceManagerError::DetectImageType)?; |
| 235 | + |
| 236 | ++ if image_type != ImageType::Qcow2 && disk_cfg.backing_files { |
| 237 | ++ warn!("Enabling backing_files option only applies for QCOW2 files"); |
| 238 | ++ } |
| 239 | ++ |
| 240 | + let image = match image_type { |
| 241 | + ImageType::FixedVhd => { |
| 242 | + // Use asynchronous backend relying on io_uring if the |
| 243 | +@@ -2759,7 +2763,7 @@ impl DeviceManager { |
| 244 | + ImageType::Qcow2 => { |
| 245 | + info!("Using synchronous QCOW2 disk file"); |
| 246 | + Box::new( |
| 247 | +- QcowDiskSync::new(file, disk_cfg.direct) |
| 248 | ++ QcowDiskSync::new(file, disk_cfg.direct, disk_cfg.backing_files) |
| 249 | + .map_err(DeviceManagerError::CreateQcowDiskSync)?, |
| 250 | + ) as Box<dyn DiskFile> |
| 251 | + } |
| 252 | +diff --git a/vmm/src/vm_config.rs b/vmm/src/vm_config.rs |
| 253 | +index 61f194e..9722e4e 100644 |
| 254 | +--- a/vmm/src/vm_config.rs |
| 255 | ++++ b/vmm/src/vm_config.rs |
| 256 | +@@ -278,6 +278,8 @@ pub struct DiskConfig { |
| 257 | + pub serial: Option<String>, |
| 258 | + #[serde(default)] |
| 259 | + pub queue_affinity: Option<Vec<VirtQueueAffinity>>, |
| 260 | ++ #[serde(default)] |
| 261 | ++ pub backing_files: bool, |
| 262 | + } |
| 263 | + |
| 264 | + impl ApplyLandlock for DiskConfig { |
| 265 | +-- |
| 266 | +2.45.4 |
| 267 | + |
0 commit comments