Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
287 changes: 287 additions & 0 deletions SPECS/cloud-hypervisor-cvm/CVE-2026-27211.patch
Original file line number Diff line number Diff line change
@@ -0,0 +1,287 @@
From 69e16ca82cdcd7ad3c4361223a4754cc8ce7f672 Mon Sep 17 00:00:00 2001
From: Rob Bradford <rbradford@meta.com>
Date: Sun, 8 Feb 2026 21:14:28 +0000
Subject: [PATCH] vmm: Add option to control backing files

Backing files (e.g. for QCOW2) interact badly with landlock since they
are not obvious from the initial VM configuration. Only enable their use
with an explicit option.

Signed-off-by: Rob Bradford <rbradford@meta.com>

Upstream Patch reference: https://github.com/microsoft/cloud-hypervisor/commit/69e16ca82cdcd7ad3c4361223a4754cc8ce7f672.patch
---
block/src/lib.rs | 1 +
block/src/qcow/mod.rs | 20 +++++++++++++++++++-
block/src/qcow_sync.rs | 14 ++++++++++----
tests/integration.rs | 21 +++++++++++----------
vmm/src/config.rs | 14 ++++++++++++--
vmm/src/device_manager.rs | 6 +++++-
vmm/src/vm_config.rs | 2 ++
7 files changed, 60 insertions(+), 18 deletions(-)

diff --git a/block/src/lib.rs b/block/src/lib.rs
index 45db0e4..a22bda3 100644
--- a/block/src/lib.rs
+++ b/block/src/lib.rs
@@ -764,6 +764,7 @@ where
fn file(&mut self) -> MutexGuard<F>;
}

+#[derive(PartialEq, Eq, Debug)]
pub enum ImageType {
FixedVhd,
Qcow2,
diff --git a/block/src/qcow/mod.rs b/block/src/qcow/mod.rs
index 264244c..f99fcfd 100644
--- a/block/src/qcow/mod.rs
+++ b/block/src/qcow/mod.rs
@@ -50,6 +50,7 @@ pub enum Error {
InvalidOffset(u64),
InvalidRefcountTableOffset,
InvalidRefcountTableSize(u64),
+ MaxNestingDepthExceeded,
NoFreeClusters,
NoRefcountClusters,
NotEnoughSpaceForRefcounts,
@@ -103,6 +104,7 @@ impl Display for Error {
InvalidOffset(_) => write!(f, "invalid offset"),
InvalidRefcountTableOffset => write!(f, "invalid refcount table offset"),
InvalidRefcountTableSize(size) => write!(f, "invalid refcount table size: {size}"),
+ MaxNestingDepthExceeded => write!(f, "Maximum disk nesting depth exceeded"),
NoFreeClusters => write!(f, "no free clusters"),
NoRefcountClusters => write!(f, "no refcount clusters"),
NotEnoughSpaceForRefcounts => write!(f, "not enough space for refcounts"),
@@ -134,6 +136,9 @@ pub enum ImageType {
Qcow2,
}

+/// Nesting depth limit for disk formats that can open other disk files.
+const MAX_NESTING_DEPTH: u32 = 10;
+
// Maximum data size supported.
const MAX_QCOW_FILE_SIZE: u64 = 0x01 << 44; // 16 TB.

@@ -450,7 +455,15 @@ pub struct QcowFile {

impl QcowFile {
/// Creates a QcowFile from `file`. File must be a valid qcow2 image.
- pub fn from(mut file: RawFile) -> Result<QcowFile> {
+ ///
+ /// Additionally, max nesting depth of this qcow2 image will be set to default value 10
+ pub fn from(file: RawFile) -> Result<QcowFile> {
+ Self::from_with_nesting_depth(file, MAX_NESTING_DEPTH)
+ }
+
+ /// Creates a QcowFile from `file` and with a max nesting depth. File must be a valid qcow2
+ /// image.
+ pub fn from_with_nesting_depth(mut file: RawFile, max_nesting_depth: u32) -> Result<QcowFile> {
let header = QcowHeader::new(&mut file)?;

// Only v2 and v3 files are supported.
@@ -477,6 +490,11 @@ impl QcowFile {
let direct_io = file.is_direct();

let backing_file = if let Some(backing_file_path) = header.backing_file_path.as_ref() {
+ // Check nesting depth - applies to any backing file
+ if max_nesting_depth == 0 {
+ return Err(Error::MaxNestingDepthExceeded);
+ }
+
let path = backing_file_path.clone();
let backing_raw_file = OpenOptions::new()
.read(true)
diff --git a/block/src/qcow_sync.rs b/block/src/qcow_sync.rs
index 6455be6..5253adb 100644
--- a/block/src/qcow_sync.rs
+++ b/block/src/qcow_sync.rs
@@ -16,10 +16,16 @@ pub struct QcowDiskSync {
}

impl QcowDiskSync {
- pub fn new(file: File, direct_io: bool) -> QcowResult<Self> {
- Ok(QcowDiskSync {
- qcow_file: Arc::new(Mutex::new(QcowFile::from(RawFile::new(file, direct_io))?)),
- })
+ pub fn new(file: File, direct_io: bool, backing_files: bool) -> QcowResult<Self> {
+ if backing_files {
+ Ok(QcowDiskSync {
+ qcow_file: Arc::new(Mutex::new(QcowFile::from(RawFile::new(file, direct_io))?)),
+ })
+ } else {
+ Ok(QcowDiskSync {
+ qcow_file: Arc::new(Mutex::new(QcowFile::from_with_nesting_depth(RawFile::new(file, direct_io), 0)?)),
+ })
+ }
}
}

diff --git a/tests/integration.rs b/tests/integration.rs
index ae98f36..da6c858 100644
--- a/tests/integration.rs
+++ b/tests/integration.rs
@@ -3350,7 +3350,7 @@ mod common_parallel {
handle_child_output(r, &output);
}

- fn _test_virtio_block(image_name: &str, disable_io_uring: bool, disable_aio: bool) {
+ fn _test_virtio_block(image_name: &str, disable_io_uring: bool, disable_aio: bool, backing_files: bool) {
let focal = UbuntuDiskConfig::new(image_name.to_string());
let guest = Guest::new(Box::new(focal));

@@ -3366,8 +3366,9 @@ mod common_parallel {
.args([
"--disk",
format!(
- "path={}",
- guest.disk_config.disk(DiskType::OperatingSystem).unwrap()
+ "path={},backing_files={}",
+ guest.disk_config.disk(DiskType::OperatingSystem).unwrap(),
+ if backing_files { "on"} else {"off"}
)
.as_str(),
format!(
@@ -3443,27 +3444,27 @@ mod common_parallel {

#[test]
fn test_virtio_block_io_uring() {
- _test_virtio_block(FOCAL_IMAGE_NAME, false, true)
+ _test_virtio_block(FOCAL_IMAGE_NAME, false, true, false)
}

#[test]
fn test_virtio_block_aio() {
- _test_virtio_block(FOCAL_IMAGE_NAME, true, false)
+ _test_virtio_block(FOCAL_IMAGE_NAME, true, false, false)
}

#[test]
fn test_virtio_block_sync() {
- _test_virtio_block(FOCAL_IMAGE_NAME, true, true)
+ _test_virtio_block(FOCAL_IMAGE_NAME, true, true, false)
}

#[test]
fn test_virtio_block_qcow2() {
- _test_virtio_block(FOCAL_IMAGE_NAME_QCOW2, false, false)
+ _test_virtio_block(FOCAL_IMAGE_NAME_QCOW2, false, false, false)
}

#[test]
fn test_virtio_block_qcow2_backing_file() {
- _test_virtio_block(FOCAL_IMAGE_NAME_QCOW2_BACKING_FILE, false, false)
+ _test_virtio_block(FOCAL_IMAGE_NAME_QCOW2_BACKING_FILE, false, false, true)
}

#[test]
@@ -3488,7 +3489,7 @@ mod common_parallel {
.output()
.expect("Expect generating VHD image from RAW image");

- _test_virtio_block(FOCAL_IMAGE_NAME_VHD, false, false)
+ _test_virtio_block(FOCAL_IMAGE_NAME_VHD, false, false, false)
}

#[test]
@@ -3512,7 +3513,7 @@ mod common_parallel {
.output()
.expect("Expect generating dynamic VHDx image from RAW image");

- _test_virtio_block(FOCAL_IMAGE_NAME_VHDX, false, false)
+ _test_virtio_block(FOCAL_IMAGE_NAME_VHDX, false, false, false)
}

#[test]
diff --git a/vmm/src/config.rs b/vmm/src/config.rs
index 1c70a26..cceac98 100644
--- a/vmm/src/config.rs
+++ b/vmm/src/config.rs
@@ -1014,7 +1014,8 @@ impl DiskConfig {
bw_size=<bytes>,bw_one_time_burst=<bytes>,bw_refill_time=<ms>,\
ops_size=<io_ops>,ops_one_time_burst=<io_ops>,ops_refill_time=<ms>,\
id=<device_id>,pci_segment=<segment_id>,rate_limit_group=<group_id>,\
- queue_affinity=<list_of_queue_indices_with_their_associated_cpuset>";
+ queue_affinity=<list_of_queue_indices_with_their_associated_cpuset>,\
+ backing_files=on|off";

pub fn parse(disk: &str) -> Result<Self> {
let mut parser = OptionParser::new();
@@ -1039,7 +1040,8 @@ impl DiskConfig {
.add("pci_segment")
.add("serial")
.add("rate_limit_group")
- .add("queue_affinity");
+ .add("queue_affinity")
+ .add("backing_files");
parser.parse(disk).map_err(Error::ParseDisk)?;

let path = parser.get("path").map(PathBuf::from);
@@ -1124,6 +1126,12 @@ impl DiskConfig {
})
.collect()
});
+ let backing_files = parser
+ .convert::<Toggle>("backing_files")
+ .map_err(Error::ParseDisk)?
+ .unwrap_or(Toggle(false))
+ .0;
+
let bw_tb_config = if bw_size != 0 && bw_refill_time != 0 {
Some(TokenBucketConfig {
size: bw_size,
@@ -1168,6 +1176,7 @@ impl DiskConfig {
pci_segment,
serial,
queue_affinity,
+ backing_files,
})
}

@@ -2961,6 +2970,7 @@ mod tests {
pci_segment: 0,
serial: None,
queue_affinity: None,
+ backing_files: false,
}
}

diff --git a/vmm/src/device_manager.rs b/vmm/src/device_manager.rs
index 7330fa3..2bb95f4 100644
--- a/vmm/src/device_manager.rs
+++ b/vmm/src/device_manager.rs
@@ -2442,6 +2442,10 @@ impl DeviceManager {
let image_type =
detect_image_type(&mut file).map_err(DeviceManagerError::DetectImageType)?;

+ if image_type != ImageType::Qcow2 && disk_cfg.backing_files {
+ warn!("Enabling backing_files option only applies for QCOW2 files");
+ }
+
let image = match image_type {
ImageType::FixedVhd => {
// Use asynchronous backend relying on io_uring if the
@@ -2495,7 +2499,7 @@ impl DeviceManager {
ImageType::Qcow2 => {
info!("Using synchronous QCOW disk file");
Box::new(
- QcowDiskSync::new(file, disk_cfg.direct)
+ QcowDiskSync::new(file, disk_cfg.direct, disk_cfg.backing_files)
.map_err(DeviceManagerError::CreateQcowDiskSync)?,
) as Box<dyn DiskFile>
}
diff --git a/vmm/src/vm_config.rs b/vmm/src/vm_config.rs
index 5c11b3a..bba40f1 100644
--- a/vmm/src/vm_config.rs
+++ b/vmm/src/vm_config.rs
@@ -227,6 +227,8 @@ pub struct DiskConfig {
pub serial: Option<String>,
#[serde(default)]
pub queue_affinity: Option<Vec<VirtQueueAffinity>>,
+ #[serde(default)]
+ pub backing_files: bool,
}

pub const DEFAULT_DISK_NUM_QUEUES: usize = 1;
--
2.43.0

8 changes: 6 additions & 2 deletions SPECS/cloud-hypervisor-cvm/cloud-hypervisor-cvm.spec
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
Name: cloud-hypervisor-cvm
Summary: Cloud Hypervisor CVM is an open source Virtual Machine Monitor (VMM) that enables running SEV SNP enabled VMs on top of MSHV using the IGVM file format as payload.
Version: 38.0.72.2
Release: 5%{?dist}
Release: 6%{?dist}
License: ASL 2.0 OR BSD-3-clause
Vendor: Microsoft Corporation
Distribution: Mariner
Expand Down Expand Up @@ -34,6 +34,7 @@ Patch0: upgrade-openssl-to-3.3.2-to-address-CVE-2024-6119.patch
Patch1: 0001-hypervisor-mshv-Fix-panic-when-rejecting-extended-gu.patch
Patch2: microsoft-cloud-hypervisor-6695.patch
Patch3: CVE-2024-12797.patch
Patch4: CVE-2026-27211.patch

Conflicts: cloud-hypervisor

Expand Down Expand Up @@ -152,7 +153,10 @@ cargo build --release --target=%{rust_musl_target} %{cargo_pkg_feature_opts} %{c
%license LICENSE-BSD-3-Clause

%changelog
* Sun Feb 16 2024 Kanishk Bansal <kanbansal@microsoft.com> - 38.0.72.2-5
* Thu Feb 27 2026 Akhila Guruju <v-guakhila@microsoft.com> - 30.0.72.2-6
- Patch CVE-2026-27211

* Fri Feb 16 2024 Kanishk Bansal <kanbansal@microsoft.com> - 38.0.72.2-5
- Address CVE-2024-12797

* Tue Oct 01 2024 Aurelien Bombo <abombo@microsoft.com> - 38.0.72.2-4
Expand Down
Loading