block: replace as <pointer> casts with safer alternatives

`as` casts can change mutability, which quickly leads to undefined
behavior.

Signed-off-by: Julian Schindel <mail@arctic-alpaca.de>
This commit is contained in:
Julian Schindel
2026-04-29 21:55:06 +02:00
committed by Rob Bradford
parent 7455ff1ea4
commit 2b6e9df4e3
8 changed files with 37 additions and 47 deletions

View File

@@ -931,8 +931,7 @@ mod unit_tests {
unsafe { ptr::write_bytes(buf, 0xAB, alignment) }; unsafe { ptr::write_bytes(buf, 0xAB, alignment) };
// SAFETY: buf is aligned and sized for O_DIRECT; fd is valid. // SAFETY: buf is aligned and sized for O_DIRECT; fd is valid.
let written = let written = unsafe { libc::pwrite(f.as_raw_fd(), buf.cast(), alignment, 0) };
unsafe { libc::pwrite(f.as_raw_fd(), buf as *const libc::c_void, alignment, 0) };
assert_eq!( assert_eq!(
written as usize, written as usize,
alignment, alignment,
@@ -943,7 +942,7 @@ mod unit_tests {
// SAFETY: buf is valid for `alignment` bytes. // SAFETY: buf is valid for `alignment` bytes.
unsafe { ptr::write_bytes(buf, 0x00, alignment) }; unsafe { ptr::write_bytes(buf, 0x00, alignment) };
// SAFETY: buf is aligned and sized for O_DIRECT; fd is valid. // SAFETY: buf is aligned and sized for O_DIRECT; fd is valid.
let read = unsafe { libc::pread(f.as_raw_fd(), buf as *mut libc::c_void, alignment, 0) }; let read = unsafe { libc::pread(f.as_raw_fd(), buf.cast(), alignment, 0) };
assert_eq!( assert_eq!(
read as usize, read as usize,
alignment, alignment,

View File

@@ -15,7 +15,6 @@ use std::os::fd::{AsFd, BorrowedFd};
use std::os::unix::io::{AsRawFd, RawFd}; use std::os::unix::io::{AsRawFd, RawFd};
use std::slice; use std::slice;
use libc::c_void;
use vmm_sys_util::file_traits::FileSync; use vmm_sys_util::file_traits::FileSync;
use vmm_sys_util::seek_hole::SeekHole; use vmm_sys_util::seek_hole::SeekHole;
use vmm_sys_util::write_zeroes::{PunchHole, WriteZeroesAt}; use vmm_sys_util::write_zeroes::{PunchHole, WriteZeroesAt};
@@ -39,14 +38,7 @@ fn is_valid_alignment(fd: RawFd, alignment: usize) -> bool {
assert!(!ptr.is_null()); assert!(!ptr.is_null());
// SAFETY: FFI call // SAFETY: FFI call
let ret = unsafe { let ret = unsafe { ::libc::pread(fd, ptr.cast(), alignment, alignment.try_into().unwrap()) };
::libc::pread(
fd,
ptr as *mut c_void,
alignment,
alignment.try_into().unwrap(),
)
};
// SAFETY: ptr was allocated by alloc_zeroed with layout // SAFETY: ptr was allocated by alloc_zeroed with layout
unsafe { dealloc(ptr, layout) }; unsafe { dealloc(ptr, layout) };
@@ -187,7 +179,7 @@ impl Read for RawFile {
let ret = unsafe { let ret = unsafe {
::libc::pread64( ::libc::pread64(
self.file.as_raw_fd(), self.file.as_raw_fd(),
tmp_buf.as_mut_ptr() as *mut c_void, tmp_buf.as_mut_ptr().cast(),
tmp_buf.len(), tmp_buf.len(),
rounded_pos.try_into().unwrap(), rounded_pos.try_into().unwrap(),
) )
@@ -267,7 +259,7 @@ impl Write for RawFile {
let ret = unsafe { let ret = unsafe {
::libc::pread64( ::libc::pread64(
self.file.as_raw_fd(), self.file.as_raw_fd(),
tmp_buf.as_mut_ptr() as *mut c_void, tmp_buf.as_mut_ptr().cast(),
tmp_buf.len(), tmp_buf.len(),
rounded_pos.try_into().unwrap(), rounded_pos.try_into().unwrap(),
) )
@@ -286,7 +278,7 @@ impl Write for RawFile {
let ret = unsafe { let ret = unsafe {
::libc::pwrite64( ::libc::pwrite64(
self.file.as_raw_fd(), self.file.as_raw_fd(),
tmp_buf.as_ptr() as *const c_void, tmp_buf.as_ptr().cast(),
tmp_buf.len(), tmp_buf.len(),
rounded_pos.try_into().unwrap(), rounded_pos.try_into().unwrap(),
) )

View File

@@ -597,7 +597,7 @@ mod unit_tests {
let mut val = 0u64; let mut val = 0u64;
// SAFETY: reading 8 bytes from a valid eventfd. // SAFETY: reading 8 bytes from a valid eventfd.
unsafe { unsafe {
libc::read(fd, &mut val as *mut u64 as *mut libc::c_void, 8); libc::read(fd, (&raw mut val).cast(), 8);
} }
} }
} }
@@ -605,7 +605,7 @@ mod unit_tests {
fn async_write(disk: &QcowDisk, offset: u64, data: &[u8]) { fn async_write(disk: &QcowDisk, offset: u64, data: &[u8]) {
let mut async_io = disk.create_async_io(1).unwrap(); let mut async_io = disk.create_async_io(1).unwrap();
let iovec = libc::iovec { let iovec = libc::iovec {
iov_base: data.as_ptr() as *mut libc::c_void, iov_base: data.as_ptr().cast::<libc::c_void>().cast_mut(),
iov_len: data.len(), iov_len: data.len(),
}; };
async_io async_io
@@ -624,7 +624,7 @@ mod unit_tests {
let mut async_io = disk.create_async_io(1).unwrap(); let mut async_io = disk.create_async_io(1).unwrap();
let mut buf = vec![0xFFu8; len]; let mut buf = vec![0xFFu8; len];
let iovec = libc::iovec { let iovec = libc::iovec {
iov_base: buf.as_mut_ptr() as *mut libc::c_void, iov_base: buf.as_mut_ptr().cast(),
iov_len: buf.len(), iov_len: buf.len(),
}; };
async_io async_io
@@ -755,11 +755,11 @@ mod unit_tests {
let offset_b: u64 = 65536; let offset_b: u64 = 65536;
let iov_a = libc::iovec { let iov_a = libc::iovec {
iov_base: write_a.as_ptr() as *mut libc::c_void, iov_base: write_a.as_ptr().cast::<libc::c_void>().cast_mut(),
iov_len: write_a.len(), iov_len: write_a.len(),
}; };
let iov_b = libc::iovec { let iov_b = libc::iovec {
iov_base: write_b.as_ptr() as *mut libc::c_void, iov_base: write_b.as_ptr().cast::<libc::c_void>().cast_mut(),
iov_len: write_b.len(), iov_len: write_b.len(),
}; };
@@ -793,11 +793,11 @@ mod unit_tests {
let mut read_a = vec![0u8; 4096]; let mut read_a = vec![0u8; 4096];
let mut read_b = vec![0u8; 4096]; let mut read_b = vec![0u8; 4096];
let riov_a = libc::iovec { let riov_a = libc::iovec {
iov_base: read_a.as_mut_ptr() as *mut libc::c_void, iov_base: read_a.as_mut_ptr().cast(),
iov_len: read_a.len(), iov_len: read_a.len(),
}; };
let riov_b = libc::iovec { let riov_b = libc::iovec {
iov_base: read_b.as_mut_ptr() as *mut libc::c_void, iov_base: read_b.as_mut_ptr().cast(),
iov_len: read_b.len(), iov_len: read_b.len(),
}; };
@@ -1068,7 +1068,7 @@ mod unit_tests {
let mut async_io = disk.create_async_io(1).unwrap(); let mut async_io = disk.create_async_io(1).unwrap();
let mut buf = vec![0xFFu8; cluster_size]; let mut buf = vec![0xFFu8; cluster_size];
let iovec = libc::iovec { let iovec = libc::iovec {
iov_base: buf.as_mut_ptr() as *mut libc::c_void, iov_base: buf.as_mut_ptr().cast(),
iov_len: buf.len(), iov_len: buf.len(),
}; };
async_io.read_vectored(0, &[iovec], 1).unwrap(); async_io.read_vectored(0, &[iovec], 1).unwrap();

View File

@@ -30,7 +30,7 @@ pub fn pread_exact(fd: RawFd, buf: &mut [u8], offset: u64) -> io::Result<()> {
let ret = unsafe { let ret = unsafe {
libc::pread64( libc::pread64(
fd, fd,
buf[total..].as_mut_ptr() as *mut libc::c_void, buf[total..].as_mut_ptr().cast(),
buf.len() - total, buf.len() - total,
(offset + total as u64) as libc::off_t, (offset + total as u64) as libc::off_t,
) )
@@ -81,7 +81,7 @@ pub fn pwrite_all(fd: RawFd, buf: &[u8], offset: u64) -> io::Result<()> {
let ret = unsafe { let ret = unsafe {
libc::pwrite64( libc::pwrite64(
fd, fd,
buf[total..].as_ptr() as *const libc::c_void, buf[total..].as_ptr().cast(),
buf.len() - total, buf.len() - total,
(offset + total as u64) as libc::off_t, (offset + total as u64) as libc::off_t,
) )
@@ -211,7 +211,7 @@ pub unsafe fn scatter_to_iovecs(iovecs: &[libc::iovec], start: usize, data: &[u8
let count = min(available, remaining.len()); let count = min(available, remaining.len());
// SAFETY: iov_base is valid for iov_len bytes per caller contract. // SAFETY: iov_base is valid for iov_len bytes per caller contract.
unsafe { unsafe {
let dst = (iov.iov_base as *mut u8).add(iov_start); let dst = iov.iov_base.cast::<u8>().add(iov_start);
ptr::copy_nonoverlapping(remaining.as_ptr(), dst, count); ptr::copy_nonoverlapping(remaining.as_ptr(), dst, count);
} }
remaining = &remaining[count..]; remaining = &remaining[count..];
@@ -240,7 +240,7 @@ pub unsafe fn zero_fill_iovecs(iovecs: &[libc::iovec], start: usize, len: usize)
let count = min(available, remaining); let count = min(available, remaining);
// SAFETY: iov_base is valid for iov_len bytes per caller contract. // SAFETY: iov_base is valid for iov_len bytes per caller contract.
unsafe { unsafe {
let dst = (iov.iov_base as *mut u8).add(iov_start); let dst = iov.iov_base.cast::<u8>().add(iov_start);
ptr::write_bytes(dst, 0, count); ptr::write_bytes(dst, 0, count);
} }
remaining -= count; remaining -= count;
@@ -270,7 +270,7 @@ pub unsafe fn gather_from_iovecs_into(iovecs: &[libc::iovec], start: usize, dst:
let count = min(available, len - written); let count = min(available, len - written);
// SAFETY: iov_base is valid for iov_len bytes per caller contract. // SAFETY: iov_base is valid for iov_len bytes per caller contract.
unsafe { unsafe {
let src = (iov.iov_base as *const u8).add(iov_start); let src = iov.iov_base.cast::<u8>().add(iov_start);
ptr::copy_nonoverlapping(src, dst.as_mut_ptr().add(written), count); ptr::copy_nonoverlapping(src, dst.as_mut_ptr().add(written), count);
} }
written += count; written += count;

View File

@@ -353,7 +353,7 @@ mod unit_tests {
let mut async_io = disk.create_async_io(1).unwrap(); let mut async_io = disk.create_async_io(1).unwrap();
let mut buf = vec![0xFFu8; len]; let mut buf = vec![0xFFu8; len];
let iovec = libc::iovec { let iovec = libc::iovec {
iov_base: buf.as_mut_ptr() as *mut libc::c_void, iov_base: buf.as_mut_ptr().cast(),
iov_len: buf.len(), iov_len: buf.len(),
}; };
async_io async_io
@@ -368,7 +368,7 @@ mod unit_tests {
fn async_write(disk: &QcowDisk, offset: u64, data: &[u8]) { fn async_write(disk: &QcowDisk, offset: u64, data: &[u8]) {
let mut async_io = disk.create_async_io(1).unwrap(); let mut async_io = disk.create_async_io(1).unwrap();
let iovec = libc::iovec { let iovec = libc::iovec {
iov_base: data.as_ptr() as *mut libc::c_void, iov_base: data.as_ptr().cast::<libc::c_void>().cast_mut(),
iov_len: data.len(), iov_len: data.len(),
}; };
async_io async_io
@@ -1550,15 +1550,15 @@ mod unit_tests {
let c = vec![0xCCu8; 16 * 1024]; let c = vec![0xCCu8; 16 * 1024];
let iovecs_w = [ let iovecs_w = [
libc::iovec { libc::iovec {
iov_base: a.as_ptr() as *mut libc::c_void, iov_base: a.as_ptr().cast::<libc::c_void>().cast_mut(),
iov_len: a.len(), iov_len: a.len(),
}, },
libc::iovec { libc::iovec {
iov_base: b.as_ptr() as *mut libc::c_void, iov_base: b.as_ptr().cast::<libc::c_void>().cast_mut(),
iov_len: b.len(), iov_len: b.len(),
}, },
libc::iovec { libc::iovec {
iov_base: c.as_ptr() as *mut libc::c_void, iov_base: c.as_ptr().cast::<libc::c_void>().cast_mut(),
iov_len: c.len(), iov_len: c.len(),
}, },
]; ];
@@ -1578,15 +1578,15 @@ mod unit_tests {
let mut r3 = vec![0u8; 8 * 1024]; let mut r3 = vec![0u8; 8 * 1024];
let iovecs_r = [ let iovecs_r = [
libc::iovec { libc::iovec {
iov_base: r1.as_mut_ptr() as *mut libc::c_void, iov_base: r1.as_mut_ptr().cast(),
iov_len: r1.len(), iov_len: r1.len(),
}, },
libc::iovec { libc::iovec {
iov_base: r2.as_mut_ptr() as *mut libc::c_void, iov_base: r2.as_mut_ptr().cast(),
iov_len: r2.len(), iov_len: r2.len(),
}, },
libc::iovec { libc::iovec {
iov_base: r3.as_mut_ptr() as *mut libc::c_void, iov_base: r3.as_mut_ptr().cast(),
iov_len: r3.len(), iov_len: r3.len(),
}, },
]; ];

View File

@@ -291,7 +291,7 @@ impl Request {
.get_slice(data_addr, data_len) .get_slice(data_addr, data_len)
.map_err(ExecuteError::GetHostAddress)?; .map_err(ExecuteError::GetHostAddress)?;
assert!(origin_ptr.len() >= data_len); assert!(origin_ptr.len() >= data_len);
let origin_ptr = origin_ptr.ptr_guard(); let origin_ptr = origin_ptr.ptr_guard_mut();
// O_DIRECT requires buffer addresses to be aligned to the // O_DIRECT requires buffer addresses to be aligned to the
// backend device's logical block size. In case it's not properly // backend device's logical block size. In case it's not properly
@@ -299,7 +299,7 @@ impl Request {
// alignment, and a copy from/to the origin buffer is performed, // alignment, and a copy from/to the origin buffer is performed,
// depending on the type of operation. // depending on the type of operation.
let iov_base = if (origin_ptr.as_ptr() as u64).is_multiple_of(alignment) { let iov_base = if (origin_ptr.as_ptr() as u64).is_multiple_of(alignment) {
origin_ptr.as_ptr() as *mut libc::c_void origin_ptr.as_ptr().cast()
} else { } else {
let layout = Layout::from_size_align(data_len, alignment as usize).unwrap(); let layout = Layout::from_size_align(data_len, alignment as usize).unwrap();
// SAFETY: layout has non-zero size // SAFETY: layout has non-zero size
@@ -327,7 +327,7 @@ impl Request {
layout, layout,
}); });
aligned_ptr as *mut libc::c_void aligned_ptr.cast()
}; };
let iovec = libc::iovec { let iovec = libc::iovec {

View File

@@ -135,7 +135,7 @@ impl Header {
.map_err(VhdxHeaderError::ReadHeader)?; .map_err(VhdxHeaderError::ReadHeader)?;
// SAFETY: buffer is of correct size and has been successfully filled. // SAFETY: buffer is of correct size and has been successfully filled.
let header = unsafe { *(buffer.as_ptr() as *mut Header) }; let header: Header = unsafe { *(buffer.as_ptr().cast()) };
if header.signature != HEADER_SIGN { if header.signature != HEADER_SIGN {
return Err(VhdxHeaderError::InvalidHeaderSign); return Err(VhdxHeaderError::InvalidHeaderSign);
} }
@@ -151,9 +151,8 @@ impl Header {
/// Converts the header structure into a buffer /// Converts the header structure into a buffer
fn write_to_buffer(&self, buffer: &mut [u8; HEADER_SIZE as usize]) { fn write_to_buffer(&self, buffer: &mut [u8; HEADER_SIZE as usize]) {
// SAFETY: self is a valid header. // SAFETY: self is a valid header.
let reference = unsafe { let reference =
std::slice::from_raw_parts(self as *const Header as *const u8, HEADER_SIZE as usize) unsafe { std::slice::from_raw_parts((&raw const *self).cast(), HEADER_SIZE as usize) };
};
*buffer = reference.try_into().unwrap(); *buffer = reference.try_into().unwrap();
} }
@@ -222,7 +221,7 @@ impl RegionTableHeader {
.map_err(VhdxHeaderError::ReadRegionTableHeader)?; .map_err(VhdxHeaderError::ReadRegionTableHeader)?;
// SAFETY: buffer is of correct size and has been successfully filled. // SAFETY: buffer is of correct size and has been successfully filled.
let region_table_header = unsafe { *(buffer.as_ptr() as *mut RegionTableHeader) }; let region_table_header: RegionTableHeader = unsafe { *(buffer.as_ptr().cast()) };
if region_table_header.signature != REGION_SIGN { if region_table_header.signature != REGION_SIGN {
return Err(VhdxHeaderError::InvalidRegionSign); return Err(VhdxHeaderError::InvalidRegionSign);
} }
@@ -340,7 +339,7 @@ impl RegionTableEntry {
pub fn new(buffer: &[u8]) -> Result<RegionTableEntry> { pub fn new(buffer: &[u8]) -> Result<RegionTableEntry> {
assert!(buffer.len() == std::mem::size_of::<RegionTableEntry>()); assert!(buffer.len() == std::mem::size_of::<RegionTableEntry>());
// SAFETY: the assertion above makes sure the buffer size is correct. // SAFETY: the assertion above makes sure the buffer size is correct.
let mut region_table_entry = unsafe { *(buffer.as_ptr() as *mut RegionTableEntry) }; let mut region_table_entry: RegionTableEntry = unsafe { *(buffer.as_ptr().cast()) };
let uuid = crate::vhdx::uuid_from_guid(buffer); let uuid = crate::vhdx::uuid_from_guid(buffer);
region_table_entry.guid = uuid; region_table_entry.guid = uuid;

View File

@@ -280,7 +280,7 @@ impl MetadataTableHeader {
pub fn new(buffer: &[u8]) -> Result<MetadataTableHeader> { pub fn new(buffer: &[u8]) -> Result<MetadataTableHeader> {
assert!(buffer.len() == std::mem::size_of::<MetadataTableHeader>()); assert!(buffer.len() == std::mem::size_of::<MetadataTableHeader>());
// SAFETY: the assertion above makes sure the buffer size is correct. // SAFETY: the assertion above makes sure the buffer size is correct.
let metadata_table_header = unsafe { *(buffer.as_ptr() as *mut MetadataTableHeader) }; let metadata_table_header: MetadataTableHeader = unsafe { *(buffer.as_ptr().cast()) };
if metadata_table_header.signature != METADATA_SIGN { if metadata_table_header.signature != METADATA_SIGN {
return Err(VhdxMetadataError::InvalidMetadataSign); return Err(VhdxMetadataError::InvalidMetadataSign);
@@ -313,7 +313,7 @@ impl MetadataTableEntry {
fn new(buffer: &[u8]) -> Result<MetadataTableEntry> { fn new(buffer: &[u8]) -> Result<MetadataTableEntry> {
assert!(buffer.len() == std::mem::size_of::<MetadataTableEntry>()); assert!(buffer.len() == std::mem::size_of::<MetadataTableEntry>());
// SAFETY: the assertion above makes sure the buffer size is correct. // SAFETY: the assertion above makes sure the buffer size is correct.
let mut metadata_table_entry = unsafe { *(buffer.as_ptr() as *mut MetadataTableEntry) }; let mut metadata_table_entry: MetadataTableEntry = unsafe { *(buffer.as_ptr().cast()) };
let uuid = crate::vhdx::uuid_from_guid(buffer); let uuid = crate::vhdx::uuid_from_guid(buffer);
metadata_table_entry.item_id = uuid; metadata_table_entry.item_id = uuid;