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::<()>())
+    }
+}

Reply via email to