block: Avoid unsafe Vec construction in detect_image_type()

The `read_aligned_block_size()` function used `Vec::from_raw_parts()`
incorrectly, causing undefined behavior when deallocating the `Vec<u8>`.

One of the safety invariants of `Vec::from_raw_parts()` is that the
provided pointer must be allocated with the exact same alignment as `T`
(`u8` in this case), but this is clearly not true: `align_of::<u8>()` is
1, but the pointer was allocated with aligment of `blocksize` (typically
512 or greater).

Fix this by using the existing `AlignedFile` helper to read the header
block when probing the image type. This is slightly less efficient than
using `AlignedBuffer` directly, but since this is only called once per
disk image at startup, the difference is probably not worth the extra
verbosity.

Signed-off-by: Daniel Verkamp <drv@meta.com>
This commit is contained in:
Daniel Verkamp
2026-06-29 15:10:11 -07:00
committed by Bo Chen
parent ab593accb3
commit 494c30be19

View File

@@ -18,7 +18,6 @@ pub(crate) mod aligned_buffer;
pub mod aligned_file;
pub mod formats;
mod sparse;
use std::alloc::{Layout, alloc_zeroed};
use std::fmt::{self, Debug};
use std::fs::{File, OpenOptions};
use std::io::{self, Read};
@@ -528,23 +527,6 @@ impl FromStr for ImageType {
const QCOW_MAGIC: u32 = 0x5146_49fb;
const VHDX_SIGN: u64 = 0x656C_6966_7864_6876;
/// Read a block into memory aligned by the source block size (needed for O_DIRECT)
pub fn read_aligned_block_size(f: &mut File) -> io::Result<Vec<u8>> {
let blocksize = DiskTopology::probe(f)?.logical_block_size as usize;
// SAFETY: We are allocating memory that is naturally aligned (size = alignment) and we meet
// requirements for safety from Vec::from_raw_parts() as we are using the global allocator
// and transferring ownership of the memory.
let mut data = unsafe {
Vec::from_raw_parts(
alloc_zeroed(Layout::from_size_align_unchecked(blocksize, blocksize)),
blocksize,
blocksize,
)
};
f.read_exact(&mut data)?;
Ok(data)
}
/// Open a disk image file, returning a [`BlockError`] with path context
/// on failure.
pub fn open_disk_image(path: &Path, options: &OpenOptions) -> BlockResult<File> {
@@ -557,7 +539,10 @@ pub fn open_disk_image(path: &Path, options: &OpenOptions) -> BlockResult<File>
/// Determine image type through file parsing.
pub fn detect_image_type(f: &mut File) -> BlockResult<ImageType> {
let block = read_aligned_block_size(f)
let mut aligned = AlignedFile::new(f.try_clone()?, true);
let mut block = vec![0u8; aligned.alignment()];
aligned
.read_exact(&mut block)
.map_err(|e| BlockError::new(BlockErrorKind::Io, e).with_op(ErrorOp::DetectImageType))?;
// Check 4 first bytes to get the header value and determine the image type