mirror of
https://gitlab.freedesktop.org/mesa/mesa.git
synced 2026-05-30 07:28:20 +02:00
rusticl/api: Add checking wrappers around slice::from_raw_parts{_mut}
They check for null, alignment, excessive size, and address space wrapping. If any of the checks fails, `Err(CL_INVALID_VALUE)` is returned. The caller still has to uphold the other requirements of the `from_raw_parts` fns. Reviewed-by: Karol Herbst <kherbst@redhat.com> Part-of: <https://gitlab.freedesktop.org/mesa/mesa/-/merge_requests/26157>
This commit is contained in:
parent
572a96aa59
commit
471d89c4fd
2 changed files with 98 additions and 0 deletions
|
|
@ -365,3 +365,69 @@ pub fn check_copy_overlap(
|
|||
/* Otherwise src and dst overlap. */
|
||||
true
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub mod cl_slice {
|
||||
use crate::api::util::CLResult;
|
||||
use mesa_rust_util::ptr::addr;
|
||||
use rusticl_opencl_gen::CL_INVALID_VALUE;
|
||||
use std::mem;
|
||||
use std::slice;
|
||||
|
||||
/// Wrapper around [`std::slice::from_raw_parts`] that returns `Err(CL_INVALID_VALUE)` if any of these conditions is met:
|
||||
/// - `data` is null
|
||||
/// - `data` is not correctly aligned for `T`
|
||||
/// - `len * std::mem::size_of::<T>()` is larger than `isize::MAX`
|
||||
/// - `data` + `len * std::mem::size_of::<T>()` wraps around the address space
|
||||
///
|
||||
/// # Safety
|
||||
/// The behavior is undefined if any of the other requirements imposed by
|
||||
/// [`std::slice::from_raw_parts`] is violated.
|
||||
#[inline]
|
||||
pub unsafe fn from_raw_parts<'a, T>(data: *const T, len: usize) -> CLResult<&'a [T]> {
|
||||
if allocation_obviously_invalid(data, len) {
|
||||
return Err(CL_INVALID_VALUE);
|
||||
}
|
||||
|
||||
// SAFETY: We've checked that `data` is not null and properly aligned. We've also checked
|
||||
// that the total size in bytes does not exceed `isize::MAX` and that adding that size to
|
||||
// `data` does not wrap around the address space.
|
||||
//
|
||||
// The caller has to uphold the other safety requirements imposed by [`std::slice::from_raw_parts`].
|
||||
unsafe { Ok(slice::from_raw_parts(data, len)) }
|
||||
}
|
||||
|
||||
/// Wrapper around [`std::slice::from_raw_parts_mut`] that returns `Err(CL_INVALID_VALUE)` if any of these conditions is met:
|
||||
/// - `data` is null
|
||||
/// - `data` is not correctly aligned for `T`
|
||||
/// - `len * std::mem::size_of::<T>()` is larger than `isize::MAX`
|
||||
/// - `data` + `len * std::mem::size_of::<T>()` wraps around the address space
|
||||
///
|
||||
/// # Safety
|
||||
/// The behavior is undefined if any of the other requirements imposed by
|
||||
/// [`std::slice::from_raw_parts_mut`] is violated.
|
||||
#[inline]
|
||||
pub unsafe fn from_raw_parts_mut<'a, T>(data: *mut T, len: usize) -> CLResult<&'a mut [T]> {
|
||||
if allocation_obviously_invalid(data, len) {
|
||||
return Err(CL_INVALID_VALUE);
|
||||
}
|
||||
|
||||
// SAFETY: We've checked that `data` is not null and properly aligned. We've also checked
|
||||
// that the total size in bytes does not exceed `isize::MAX` and that adding that size to
|
||||
// `data` does not wrap around the address space.
|
||||
//
|
||||
// The caller has to uphold the other safety requirements imposed by [`std::slice::from_raw_parts_mut`].
|
||||
unsafe { Ok(slice::from_raw_parts_mut(data, len)) }
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
fn allocation_obviously_invalid<T>(data: *const T, len: usize) -> bool {
|
||||
let Some(total_size) = mem::size_of::<T>().checked_mul(len) else {
|
||||
return true;
|
||||
};
|
||||
data.is_null()
|
||||
|| !mesa_rust_util::ptr::is_aligned(data)
|
||||
|| total_size > isize::MAX as usize
|
||||
|| addr(data).checked_add(total_size).is_none()
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
use std::mem;
|
||||
use std::ptr;
|
||||
|
||||
pub trait CheckedPtr<T> {
|
||||
|
|
@ -52,3 +53,34 @@ macro_rules! offset_of {
|
|||
offset()
|
||||
}};
|
||||
}
|
||||
|
||||
// Adapted from libstd since std::ptr::is_aligned is still unstable
|
||||
// See https://github.com/rust-lang/rust/issues/96284
|
||||
#[must_use]
|
||||
#[inline]
|
||||
pub const fn is_aligned<T>(ptr: *const T) -> bool
|
||||
where
|
||||
T: Sized,
|
||||
{
|
||||
let align = mem::align_of::<T>();
|
||||
addr(ptr) & (align - 1) == 0
|
||||
}
|
||||
|
||||
// Adapted from libstd since std::ptr::addr is still unstable
|
||||
// See https://github.com/rust-lang/rust/issues/95228
|
||||
#[must_use]
|
||||
#[inline(always)]
|
||||
pub const fn addr<T>(ptr: *const T) -> usize {
|
||||
// The libcore implementations of `addr` and `expose_addr` suggest that, while both transmuting
|
||||
// and casting to usize will give you the address of a ptr in the end, they are not identical
|
||||
// in their side-effects.
|
||||
// A cast "exposes" a ptr, which can potentially cause the compiler to optimize less
|
||||
// aggressively around it.
|
||||
// Let's trust the libcore devs over clippy on whether a transmute also exposes a ptr.
|
||||
#[allow(clippy::transmutes_expressible_as_ptr_casts)]
|
||||
// SAFETY: Pointer-to-integer transmutes are valid (if you are okay with losing the
|
||||
// provenance).
|
||||
unsafe {
|
||||
mem::transmute(ptr.cast::<()>())
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue