mirror of
https://github.com/cloud-hypervisor/cloud-hypervisor.git
synced 2026-08-05 02:19:16 +00:00
`as` casts can change mutability, which quickly leads to undefined behavior. Signed-off-by: Julian Schindel <mail@arctic-alpaca.de>
90 lines
2.9 KiB
Rust
90 lines
2.9 KiB
Rust
// Copyright © 2025 Demi Marie Obenour
|
|
//
|
|
// SPDX-License-Identifier: Apache-2.0 OR BSD-3-Clause
|
|
|
|
//! Helpers for `mmap()`
|
|
|
|
use core::ffi::c_int;
|
|
use core::ptr::null_mut;
|
|
use std::io::{Error, ErrorKind};
|
|
use std::os::fd::{AsRawFd as _, BorrowedFd};
|
|
|
|
use libc::size_t;
|
|
|
|
/// A region of `mmap()`-allocated memory that calls `munmap()` when dropped.
|
|
/// This guarantees that the buffer is valid and that its address space
|
|
/// will be reserved. The address space is not guaranteed to be accessible.
|
|
/// Atomic access to the data will not cause undefined behavior but might
|
|
/// cause SIGSEGV or SIGBUS. Non-atomic access will generally cause data
|
|
/// races and thus Undefined Behavior.
|
|
#[derive(Debug)]
|
|
pub struct MmapRegion {
|
|
addr: *mut u8,
|
|
len: size_t,
|
|
}
|
|
|
|
impl Drop for MmapRegion {
|
|
fn drop(&mut self) {
|
|
// SAFETY: guaranteed by type validity invariant
|
|
unsafe { assert_eq!(libc::munmap(self.addr.cast(), self.len), 0) }
|
|
}
|
|
}
|
|
// SAFETY: the caller is responsible for avoiding data races
|
|
unsafe impl Send for MmapRegion {}
|
|
// SAFETY: the caller is responsible for avoiding data races
|
|
unsafe impl Sync for MmapRegion {}
|
|
|
|
impl MmapRegion {
|
|
#[inline]
|
|
pub fn addr(&self) -> *mut u8 {
|
|
self.addr
|
|
}
|
|
|
|
/// Return the length of the region.
|
|
/// This function promises that the return value fits in [`libc::size_t`]
|
|
/// and in [`isize`] and `unsafe` code can rely on this.
|
|
#[inline]
|
|
pub fn len(&self) -> usize {
|
|
self.len
|
|
}
|
|
|
|
/// Create an [`MmapRegion`] using `mmap` of a file descriptor.
|
|
pub fn mmap(
|
|
len: u64,
|
|
prot: c_int,
|
|
fd: BorrowedFd,
|
|
offset1: u64,
|
|
offset2: u64,
|
|
) -> std::io::Result<Self> {
|
|
const BAD_LENGTH: &str = "Offsets must fit in libc::off_t";
|
|
const BAD_OFFSET: &str = "Mapping length must fit \
|
|
in both isize and libc::size_t";
|
|
let Some(offset) = offset1.checked_add(offset2) else {
|
|
return Err(Error::new(ErrorKind::InvalidInput, BAD_OFFSET));
|
|
};
|
|
let Ok(offset) = libc::off_t::try_from(offset) else {
|
|
return Err(Error::new(ErrorKind::InvalidInput, BAD_OFFSET));
|
|
};
|
|
if isize::try_from(len).is_err() {
|
|
return Err(Error::new(ErrorKind::InvalidInput, BAD_LENGTH));
|
|
}
|
|
let Ok(len) = libc::size_t::try_from(len) else {
|
|
return Err(Error::new(ErrorKind::InvalidInput, BAD_LENGTH));
|
|
};
|
|
|
|
assert!(
|
|
(prot & !(libc::PROT_READ | libc::PROT_WRITE | libc::PROT_EXEC)) == 0,
|
|
"bad protection"
|
|
);
|
|
let flags = libc::MAP_SHARED;
|
|
// SAFETY: FFI call with correct parameters.
|
|
let addr = unsafe { libc::mmap(null_mut(), len, prot, flags, fd.as_raw_fd(), offset) };
|
|
if addr == libc::MAP_FAILED {
|
|
Err(Error::last_os_error())
|
|
} else {
|
|
let addr = addr.cast();
|
|
Ok(Self { addr, len })
|
|
}
|
|
}
|
|
}
|