Module: Mesa Branch: main Commit: 471d89c4fd4151344e0a61684093d38f027b01da URL: http://cgit.freedesktop.org/mesa/mesa/commit/?id=471d89c4fd4151344e0a61684093d38f027b01da
Author: LingMan <18294-ling...@users.noreply.gitlab.freedesktop.org> Date: Mon Nov 13 04:35:19 2023 +0100 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 <kher...@redhat.com> Part-of: <https://gitlab.freedesktop.org/mesa/mesa/-/merge_requests/26157> --- src/gallium/frontends/rusticl/api/util.rs | 66 +++++++++++++++++++++++++++++++ src/gallium/frontends/rusticl/util/ptr.rs | 32 +++++++++++++++ 2 files changed, 98 insertions(+) diff --git a/src/gallium/frontends/rusticl/api/util.rs b/src/gallium/frontends/rusticl/api/util.rs index 2ec573af805..325ad12a027 100644 --- a/src/gallium/frontends/rusticl/api/util.rs +++ b/src/gallium/frontends/rusticl/api/util.rs @@ -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() + } +} diff --git a/src/gallium/frontends/rusticl/util/ptr.rs b/src/gallium/frontends/rusticl/util/ptr.rs index 8aa96373256..5fb3f0ebc9d 100644 --- a/src/gallium/frontends/rusticl/util/ptr.rs +++ b/src/gallium/frontends/rusticl/util/ptr.rs @@ -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::<()>()) + } +}