mirror of
https://github.com/cloud-hypervisor/cloud-hypervisor.git
synced 2024-12-22 13:45:20 +00:00
block: qcow: limit max nesting depth for backing file
Impose a limit on the maximum nesting of file formats that can open more
files. For example, a qcow2 file can have a backing file, which could be
another qcow2 file with a backing file (or even the same file as the
original), potentially causing unbounded recursion.
This commit is based on crosvm implementation:
eb1640e301
Fixes: #6472
Signed-off-by: Yu Li <liyu.yukiteru@bytedance.com>
This commit is contained in:
parent
c206c14318
commit
220455caaf
@ -31,13 +31,16 @@ use vmm_sys_util::{
|
||||
|
||||
pub use crate::qcow::raw_file::RawFile;
|
||||
|
||||
/// Nesting depth limit for disk formats that can open other disk files.
|
||||
const MAX_NESTING_DEPTH: u32 = 10;
|
||||
|
||||
#[sorted]
|
||||
#[derive(Debug, Error)]
|
||||
pub enum Error {
|
||||
#[error("Backing file io error: {0}")]
|
||||
BackingFileIo(io::Error),
|
||||
#[error("Backing file open error: {0}")]
|
||||
BackingFileOpen(Box<crate::Error>),
|
||||
BackingFileOpen(Box<Error>),
|
||||
#[error("Backing file name is too long: {0} bytes over")]
|
||||
BackingFileTooLong(usize),
|
||||
#[error("Compressed blocks not supported")]
|
||||
@ -70,6 +73,8 @@ pub enum Error {
|
||||
InvalidRefcountTableOffset,
|
||||
#[error("Invalid refcount table size: {0}")]
|
||||
InvalidRefcountTableSize(u64),
|
||||
#[error("Maximum disk nesting depth exceeded")]
|
||||
MaxNestingDepthExceeded,
|
||||
#[error("No free clusters")]
|
||||
NoFreeClusters,
|
||||
#[error("No refcount clusters")]
|
||||
@ -434,12 +439,20 @@ pub struct QcowFile {
|
||||
// List of unreferenced clusters available to be used. unref clusters become available once the
|
||||
// removal of references to them have been synced to disk.
|
||||
avail_clusters: Vec<u64>,
|
||||
backing_file: Option<Box<dyn BlockBackend>>,
|
||||
backing_file: Option<Box<Self>>,
|
||||
}
|
||||
|
||||
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.
|
||||
@ -466,14 +479,20 @@ impl QcowFile {
|
||||
let direct_io = file.is_direct();
|
||||
|
||||
let backing_file = if let Some(backing_file_path) = header.backing_file_path.as_ref() {
|
||||
if max_nesting_depth == 0 {
|
||||
return Err(Error::MaxNestingDepthExceeded);
|
||||
}
|
||||
let path = backing_file_path.clone();
|
||||
let backing_raw_file = OpenOptions::new()
|
||||
.read(true)
|
||||
.open(path)
|
||||
.map_err(Error::BackingFileIo)?;
|
||||
let backing_file = crate::create_disk_file(backing_raw_file, direct_io)
|
||||
.map_err(|e| Error::BackingFileOpen(Box::new(e)))?;
|
||||
Some(backing_file)
|
||||
let backing_file = Self::from_with_nesting_depth(
|
||||
RawFile::new(backing_raw_file, direct_io),
|
||||
max_nesting_depth - 1,
|
||||
)
|
||||
.map_err(|e| Error::BackingFileOpen(Box::new(e)))?;
|
||||
Some(Box::new(backing_file))
|
||||
} else {
|
||||
None
|
||||
};
|
||||
@ -621,20 +640,22 @@ impl QcowFile {
|
||||
file: RawFile,
|
||||
version: u32,
|
||||
backing_file_name: &str,
|
||||
backing_file_max_nesting_depth: u32,
|
||||
) -> Result<QcowFile> {
|
||||
let direct_io = file.is_direct();
|
||||
let backing_raw_file = OpenOptions::new()
|
||||
.read(true)
|
||||
.open(backing_file_name)
|
||||
.map_err(Error::BackingFileIo)?;
|
||||
let backing_file = crate::create_disk_file(backing_raw_file, direct_io)
|
||||
.map_err(|e| Error::BackingFileOpen(Box::new(e)))?;
|
||||
let size = backing_file
|
||||
.size()
|
||||
.map_err(|e| Error::BackingFileOpen(Box::new(e)))?;
|
||||
let backing_file = Self::from_with_nesting_depth(
|
||||
RawFile::new(backing_raw_file, direct_io),
|
||||
backing_file_max_nesting_depth,
|
||||
)
|
||||
.map_err(|e| Error::BackingFileOpen(Box::new(e)))?;
|
||||
let size = backing_file.virtual_size();
|
||||
let header = QcowHeader::create_for_size_and_path(version, size, Some(backing_file_name))?;
|
||||
let mut result = QcowFile::new_from_header(file, header)?;
|
||||
result.backing_file = Some(backing_file);
|
||||
result.backing_file = Some(Box::new(backing_file));
|
||||
Ok(result)
|
||||
}
|
||||
|
||||
@ -662,7 +683,7 @@ impl QcowFile {
|
||||
Ok(qcow)
|
||||
}
|
||||
|
||||
pub fn set_backing_file(&mut self, backing: Option<Box<dyn BlockBackend>>) {
|
||||
pub fn set_backing_file(&mut self, backing: Option<Box<Self>>) {
|
||||
self.backing_file = backing;
|
||||
}
|
||||
|
||||
@ -1778,11 +1799,17 @@ where
|
||||
/// Copy the contents of a disk image in `src_file` into `dst_file`.
|
||||
/// The type of `src_file` is automatically detected, and the output file type is
|
||||
/// determined by `dst_type`.
|
||||
pub fn convert(mut src_file: RawFile, dst_file: RawFile, dst_type: ImageType) -> Result<()> {
|
||||
pub fn convert(
|
||||
mut src_file: RawFile,
|
||||
dst_file: RawFile,
|
||||
dst_type: ImageType,
|
||||
src_max_nesting_depth: u32,
|
||||
) -> Result<()> {
|
||||
let src_type = detect_image_type(&mut src_file)?;
|
||||
match src_type {
|
||||
ImageType::Qcow2 => {
|
||||
let mut src_reader = QcowFile::from(src_file)?;
|
||||
let mut src_reader =
|
||||
QcowFile::from_with_nesting_depth(src_file, src_max_nesting_depth)?;
|
||||
convert_reader(&mut src_reader, dst_file, dst_type)
|
||||
}
|
||||
ImageType::Raw => {
|
||||
|
Loading…
Reference in New Issue
Block a user