redox-driver-sys: add # Safety docs to dma.rs unsafe blocks
Documents the safety contracts for: - alloc_zeroed: matching layout between alloc/dealloc; non-zero size - fmap: valid Map struct and open region_fd - munmap: matches previously successful fmap exactly - dealloc: same layout as matching alloc_zeroed; no concurrent use - Send + Sync impls: process-local mapping, no aliasing across processes Closes the documentation gap for dma.rs unsafe blocks.
This commit is contained in:
@@ -182,6 +182,10 @@ impl DmaBuffer {
|
||||
let layout = std::alloc::Layout::from_size_align(size, align)
|
||||
.map_err(|e| DriverError::Other(format!("invalid DMA layout: {e}")))?;
|
||||
|
||||
// SAFETY: `alloc_zeroed` is unsafe because the caller must ensure
|
||||
// `layout` has non-zero size (guaranteed by `size > 0` precondition on the
|
||||
// public allocate() API) and that the returned allocation will be
|
||||
// freed with the matching `dealloc` (see Drop impl using the same layout).
|
||||
let ptr = unsafe { std::alloc::alloc_zeroed(layout) };
|
||||
let ptr = NonNull::new(ptr).ok_or_else(|| {
|
||||
DriverError::Other(format!(
|
||||
@@ -213,6 +217,10 @@ impl DmaBuffer {
|
||||
};
|
||||
|
||||
// Map it into our address space through SYS_FMAP with combined map+prot flags.
|
||||
// SAFETY: `fmap` is unsafe because the caller must ensure `map` is a
|
||||
// valid Map struct (it is — we constructed it above) and that `region_fd`
|
||||
// refers to an open physical-memory region. After fmap returns successfully
|
||||
// the returned pointer is valid for reads/writes within `map.size` bytes.
|
||||
let ptr = unsafe { redox_syscall::call::fmap(region_fd as usize, &map) }.map_err(|e| {
|
||||
let _ = libredox::call::close(region_fd as usize);
|
||||
DriverError::MappingFailed {
|
||||
@@ -296,16 +304,31 @@ impl Drop for DmaBuffer {
|
||||
size,
|
||||
region_fd,
|
||||
} => {
|
||||
// SAFETY: `munmap` is unsafe because the caller must ensure `ptr..ptr+size`
|
||||
// exactly matches a previously successful `fmap` mapping. The Drop
|
||||
// impl runs exactly once and owns this mapping, so the invariant holds.
|
||||
let _ = unsafe { libredox::call::munmap(ptr.as_ptr() as *mut (), *size) };
|
||||
let _ = libredox::call::close(*region_fd as usize);
|
||||
}
|
||||
DmaStorage::Heap { ptr, layout } => {
|
||||
// SAFETY: `dealloc` is unsafe because the caller must ensure `ptr` was
|
||||
// allocated with `alloc_zeroed` using the SAME `layout` (it is — Drop
|
||||
// owns the layout field) and that the allocation is not in use elsewhere.
|
||||
unsafe { std::alloc::dealloc(ptr.as_ptr(), *layout) };
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// SAFETY: DmaBuffer is Send + Sync because:
|
||||
// - The underlying DMA mapping is process-local and the kernel guarantees
|
||||
// no aliasing between processes.
|
||||
// - Concurrent MMIO/DMA access from different threads to the SAME buffer
|
||||
// is racy at the *device* level (the device driver must serialize), but
|
||||
// Rust's aliasing rules are satisfied: each NonNull<u8> pointer is unique.
|
||||
// - DMA ordering is enforced at the hardware level by the IOMMU/device, not
|
||||
// by Rust's memory model — concurrent access here is therefore the
|
||||
// caller's responsibility, not a soundness violation.
|
||||
unsafe impl Send for DmaBuffer {}
|
||||
unsafe impl Sync for DmaBuffer {}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user