Skip to main content

storage_ptr_slice/
lib.rs

1// Copyright 2026 The Fuchsia Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5//! Safe wrappers for raw pointer byte slices.
6//!
7//! This crate provides [`PtrByteSlice`] and [`MutPtrByteSlice`], which are designed for use in
8//! scenarios involving cross-process shared memory (e.g., communication with driver processes or
9//! other untrusted components).
10//!
11//! ### Rationale
12//!
13//! In a multi-process system like Fuchsia, processes often share memory via VMOs (Virtual Memory
14//! Objects). If a process shares a memory region with another process, that other process (which
15//! may be compromised or untrusted) can modify the memory concurrently at any time.
16//!
17//! In Rust, creating a standard reference (`&[u8]` or `&mut [u8]`) over memory that can be
18//! modified concurrently by another party is **Undefined Behavior (UB)**. The Rust compiler
19//! assumes that the data behind a shared reference (`&T`) is immutable and cannot change
20//! unexpectedly, allowing it to perform optimizations that assume stability. If the memory changes
21//! concurrently, these assumptions are violated.
22//!
23//! To avoid UB, we must avoid creating standard Rust references to concurrently-modifiable shared
24//! memory. Instead, we must treat the shared memory as raw pointers.
25//!
26//! [`PtrByteSlice`] and [`MutPtrByteSlice`] wrap these raw pointers and provide a safe API to:
27//! 1.  **Copy data out** of the shared region into private, allocator-managed memory (e.g., via
28//!     `copy_to_slice` or `to_vec`). Once copied, the private data is safe from concurrent
29//!     modification and can be safely represented as standard Rust slices.
30//! 2.  **Perform structured access** (e.g., via `chunks` or `chunks_mut`) only when the underlying
31//!     types guarantee that arbitrary byte patterns are valid (via `FromBytes`) and we accept that
32//!     the values might change (though we must still be careful about Time-of-Check to Time-of-Use
33//!     (TOCTOU) vulnerabilities).
34//!
35//! By removing direct access to the underlying slice (i.e., not providing `as_slice` or
36//! `as_mut_slice` methods), this crate enforces that helper components must copy data into trusted
37//! buffers before operating on it, ensuring both memory safety (no UB) and robustness against
38//! concurrent modification.
39//!
40//! This crate does nothing to prevent data races; responsibility for handling data races lies
41//! elsewhere.
42
43use std::marker::PhantomData;
44use zerocopy::FromBytes;
45
46/// A read-only view of a raw pointer byte slice, providing a safe API.
47#[derive(Debug, Copy, Clone)]
48pub struct PtrByteSlice<'a> {
49    slice: *const [u8],
50    _marker: PhantomData<&'a [u8]>,
51}
52
53impl<'a> PtrByteSlice<'a> {
54    /// Creates a new `PtrByteSlice` from a raw pointer to a byte slice.
55    ///
56    /// # Safety
57    ///
58    /// The caller must ensure that `slice` is valid for reads for the lifetime `'a`.
59    pub unsafe fn new(slice: *const [u8]) -> Self {
60        Self { slice, _marker: PhantomData }
61    }
62
63    /// Returns the length of the slice in bytes.
64    pub fn len(&self) -> usize {
65        self.slice.len()
66    }
67
68    /// Returns `true` if the slice has a length of 0.
69    pub fn is_empty(&self) -> bool {
70        self.len() == 0
71    }
72
73    /// Copies the contents of this slice into a safe Rust mutable slice.
74    ///
75    /// # Panics
76    ///
77    /// Panics if `dest` is smaller than `self.len()`.
78    pub fn copy_to_slice(&self, dest: &mut [u8]) {
79        assert!(dest.len() >= self.len());
80        // SAFETY:
81        // - `self.slice` is valid for reads of `self.len()` bytes (guaranteed by `Self::new`
82        //   safety contract).
83        // - `dest` is valid for writes of `self.len()` bytes (ensured by the assert).
84        // - The memory regions do not overlap because `dest` is an exclusive Rust reference.
85        unsafe {
86            std::ptr::copy_nonoverlapping(self.slice as *const u8, dest.as_mut_ptr(), self.len());
87        }
88    }
89
90    /// Returns a subslice of this pointer slice.
91    ///
92    /// # Panics
93    ///
94    /// Panics if the range is out of bounds.
95    pub fn subslice(&self, range: std::ops::Range<usize>) -> Self {
96        assert!(range.start <= range.end);
97        assert!(range.end <= self.len());
98        // SAFETY:
99        // - `range` is within the bounds of `self.slice` (ensured by asserts).
100        // - The original `self.slice` is valid for reads for `'a`, so any subslice of it
101        //   is also valid for reads for `'a`.
102        unsafe {
103            let new_ptr = (self.slice as *const u8).add(range.start);
104            let new_slice = std::ptr::slice_from_raw_parts(new_ptr, range.end - range.start);
105            Self::new(new_slice)
106        }
107    }
108
109    /// Splits the slice into two at the given index.
110    ///
111    /// # Panics
112    ///
113    /// Panics if `mid` is out of bounds.
114    pub fn split_at(self, mid: usize) -> (Self, Self) {
115        assert!(mid <= self.len());
116        // SAFETY:
117        // - `mid` is within the bounds of `self.slice` (ensured by assert).
118        // - The two subslices are valid for reads for `'a` as they are parts of the original
119        //   valid slice.
120        unsafe {
121            let ptr = self.slice as *const u8;
122            (
123                Self::new(std::ptr::slice_from_raw_parts(ptr, mid)),
124                Self::new(std::ptr::slice_from_raw_parts(ptr.add(mid), self.len() - mid)),
125            )
126        }
127    }
128
129    /// Returns the raw pointer to the slice.
130    pub fn as_raw_slice_ptr(&self) -> *const [u8] {
131        self.slice
132    }
133
134    /// Returns a raw pointer to the start of the slice.
135    pub fn as_ptr(&self) -> *const u8 {
136        self.slice as *const u8
137    }
138
139    /// Allocates a new heap Vector and copies the contents into it.
140    /// Bypasses zero-initialization using raw pointer copies.
141    pub fn to_vec(&self) -> Vec<u8> {
142        let mut vec = Vec::with_capacity(self.len());
143        // SAFETY: The memory is guaranteed to be valid for reads up to `self.len()`
144        // for the lifetime of this pointer slice.
145        unsafe {
146            std::ptr::copy_nonoverlapping(self.slice as *const u8, vec.as_mut_ptr(), self.len());
147            vec.set_len(self.len());
148        }
149        vec
150    }
151
152    /// Appends the contents of this slice to the given vector, expanding its capacity if needed.
153    /// Bypasses zero-initialization using raw pointer copies.
154    pub fn append_to(&self, vec: &mut Vec<u8>) {
155        let old_len = vec.len();
156        let new_len = old_len + self.len();
157        vec.reserve(self.len());
158        // SAFETY:
159        // - We reserved enough capacity in `vec` to fit `self.len()` more bytes.
160        // - `dest_ptr` points to the unused capacity.
161        // - `self.slice` is valid for reads of `self.len()` bytes.
162        // - The source and destination do not overlap because `vec` is owned and allocated
163        //   separately.
164        unsafe {
165            let dest_ptr = vec.as_mut_ptr().add(old_len);
166            std::ptr::copy_nonoverlapping(self.slice as *const u8, dest_ptr, self.len());
167            vec.set_len(new_len);
168        }
169    }
170
171    /// Returns an iterator over read-only chunks of type `T`.
172    ///
173    /// # Panics
174    ///
175    /// Panics if the slice is not aligned to `T` or if its length in bytes is not a multiple of
176    /// `size_of::<T>()`.
177    pub fn chunks<T: Copy + FromBytes>(&self) -> Chunks<'_, T> {
178        let size = std::mem::size_of::<T>();
179        let align = std::mem::align_of::<T>();
180        assert!(size > 0, "Chunk size must be greater than 0");
181        assert_eq!(self.slice as *const u8 as usize % align, 0, "Slice is not aligned to T");
182        assert_eq!(self.len() % size, 0, "Slice length is not a multiple of T size");
183
184        // SAFETY:
185        // - `self.slice` is aligned to `T` (ensured by assert).
186        // - The end pointer is calculated within the bounds of the original slice.
187        // - Pointer arithmetic within the same allocated object is safe.
188        let end = unsafe { (self.slice as *const T).add(self.len() / size) };
189        Chunks { ptr: self.slice as *const T, end, _marker: PhantomData }
190    }
191}
192
193/// A mutable view of a raw pointer byte slice, providing a safe API.
194#[derive(Debug)]
195pub struct MutPtrByteSlice<'a> {
196    slice: *mut [u8],
197    _marker: PhantomData<&'a mut [u8]>,
198}
199
200impl<'a> MutPtrByteSlice<'a> {
201    /// Creates a new `MutPtrByteSlice` from a raw mutable pointer to a byte slice.
202    ///
203    /// # Safety
204    ///
205    /// The caller must ensure that `slice` is valid for reads and writes for the lifetime `'a`.
206    pub unsafe fn new(slice: *mut [u8]) -> Self {
207        Self { slice, _marker: PhantomData }
208    }
209
210    /// Returns the length of the slice in bytes.
211    pub fn len(&self) -> usize {
212        self.slice.len()
213    }
214
215    /// Returns `true` if the slice has a length of 0.
216    pub fn is_empty(&self) -> bool {
217        self.len() == 0
218    }
219
220    /// Copies the contents of this slice into a safe Rust mutable slice.
221    ///
222    /// # Panics
223    ///
224    /// Panics if `dest` is smaller than `self.len()`.
225    pub fn copy_to_slice(&self, dest: &mut [u8]) {
226        assert!(dest.len() >= self.len());
227        // SAFETY:
228        // - `self.slice` is valid for reads of `self.len()` bytes (guaranteed by `Self::new`
229        //   safety contract).
230        // - `dest` is valid for writes of `self.len()` bytes (ensured by the assert).
231        // - The memory regions do not overlap because `dest` is an exclusive Rust reference.
232        unsafe {
233            std::ptr::copy_nonoverlapping(self.slice as *mut u8, dest.as_mut_ptr(), self.len());
234        }
235    }
236
237    /// Copies the contents of another read-only pointer slice into this mutable slice.
238    ///
239    /// # Panics
240    ///
241    /// Panics if the lengths of the slices do not match.
242    pub fn copy_from_ptr_slice(&mut self, src: PtrByteSlice<'_>) {
243        assert_eq!(self.len(), src.len());
244        // SAFETY:
245        // - `self.slice` is valid for writes of `self.len()` bytes.
246        // - `src` is valid for reads of `src.len()` (which equals `self.len()`) bytes.
247        // - They do not overlap because `self` (mutable) and `src` (immutable) cannot alias
248        //   under Rust's borrowing rules.
249        unsafe {
250            std::ptr::copy_nonoverlapping(src.as_ptr(), self.slice as *mut u8, self.len());
251        }
252    }
253
254    /// Fills the slice with the given byte value.
255    pub fn fill(&mut self, val: u8) {
256        // SAFETY: `self.slice` is valid for writes of `self.len()` bytes.
257        unsafe {
258            std::ptr::write_bytes(self.slice as *mut u8, val, self.len());
259        }
260    }
261
262    /// Returns a read-only view of this slice.
263    pub fn as_ptr_slice(&self) -> PtrByteSlice<'_> {
264        // SAFETY: `self.slice` is valid for reads (since it is valid for writes) for `'a`.
265        unsafe { PtrByteSlice::new(self.slice as *const [u8]) }
266    }
267
268    /// Returns a mutable subslice of this pointer slice.
269    ///
270    /// # Panics
271    ///
272    /// Panics if the range is out of bounds.
273    pub fn subslice_mut(&mut self, range: std::ops::Range<usize>) -> Self {
274        assert!(range.start <= range.end);
275        assert!(range.end <= self.len());
276        // SAFETY:
277        // - `range` is within the bounds of `self.slice` (ensured by asserts).
278        // - The original `self.slice` is valid for reads and writes for `'a`, so any subslice of it
279        //   is also valid for reads and writes for `'a`.
280        unsafe {
281            let new_ptr = (self.slice as *mut u8).add(range.start);
282            let new_slice = std::ptr::slice_from_raw_parts_mut(new_ptr, range.end - range.start);
283            Self::new(new_slice)
284        }
285    }
286
287    /// Splits the slice into two at the given index.
288    ///
289    /// # Panics
290    ///
291    /// Panics if `mid` is out of bounds.
292    pub fn split_at_mut(self, mid: usize) -> (Self, Self) {
293        assert!(mid <= self.len());
294        // SAFETY:
295        // - `mid` is within the bounds of `self.slice` (ensured by assert).
296        // - The two subslices are valid for reads and writes for `'a` as they are parts of the
297        //   original valid slice.
298        // - They do not overlap.
299        unsafe {
300            let ptr = self.slice as *mut u8;
301            (
302                Self::new(std::ptr::slice_from_raw_parts_mut(ptr, mid)),
303                Self::new(std::ptr::slice_from_raw_parts_mut(ptr.add(mid), self.len() - mid)),
304            )
305        }
306    }
307
308    /// Returns a raw pointer to the start of the slice.
309    pub fn as_ptr(&self) -> *const u8 {
310        self.slice as *const u8
311    }
312
313    /// Returns a raw mutable pointer to the start of the slice.
314    pub fn as_mut_ptr(&self) -> *mut u8 {
315        self.slice as *mut u8
316    }
317
318    /// Reborrows the mutable slice with a shorter lifetime.
319    pub fn reborrow(&mut self) -> MutPtrByteSlice<'_> {
320        MutPtrByteSlice { slice: self.slice, _marker: std::marker::PhantomData }
321    }
322
323    /// Allocates a new heap Vector and copies the contents into it.
324    /// Bypasses zero-initialization using raw pointer copies.
325    pub fn to_vec(&self) -> Vec<u8> {
326        let mut vec = Vec::with_capacity(self.len());
327        // SAFETY: The memory is guaranteed to be valid for reads up to `self.len()`
328        // for the lifetime of this pointer slice.
329        unsafe {
330            std::ptr::copy_nonoverlapping(self.slice as *mut u8, vec.as_mut_ptr(), self.len());
331            vec.set_len(self.len());
332        }
333        vec
334    }
335
336    /// Appends the contents of this slice to the given vector, expanding its capacity if needed.
337    /// Bypasses zero-initialization using raw pointer copies.
338    pub fn append_to(&self, vec: &mut Vec<u8>) {
339        let old_len = vec.len();
340        let new_len = old_len + self.len();
341        vec.reserve(self.len());
342        // SAFETY:
343        // - We reserved enough capacity in `vec` to fit `self.len()` more bytes.
344        // - `dest_ptr` points to the unused capacity.
345        // - `self.slice` is valid for reads of `self.len()` bytes.
346        // - The source and destination do not overlap because `vec` is owned and allocated
347        //   separately.
348        unsafe {
349            let dest_ptr = vec.as_mut_ptr().add(old_len);
350            std::ptr::copy_nonoverlapping(self.slice as *mut u8, dest_ptr, self.len());
351            vec.set_len(new_len);
352        }
353    }
354
355    /// Returns an iterator over mutable chunks of type `T`.
356    ///
357    /// # Panics
358    ///
359    /// Panics if the slice is not aligned to `T` or if its length in bytes is not a multiple of
360    /// `size_of::<T>()`.
361    pub fn chunks_mut<T: Copy + FromBytes>(&mut self) -> ChunksMut<'_, T> {
362        let size = std::mem::size_of::<T>();
363        let align = std::mem::align_of::<T>();
364        assert!(size > 0, "Chunk size must be greater than 0");
365        assert_eq!(self.slice as *mut u8 as usize % align, 0, "Slice is not aligned to T");
366        assert_eq!(self.len() % size, 0, "Slice length is not a multiple of T size");
367
368        // SAFETY:
369        // - `self.slice` is aligned to `T` (ensured by assert).
370        // - The end pointer is calculated within the bounds of the original slice.
371        // - Pointer arithmetic within the same allocated object is safe.
372        let end = unsafe { (self.slice as *mut T).add(self.len() / size) };
373        ChunksMut { ptr: self.slice as *mut T, end, _marker: PhantomData }
374    }
375}
376
377// SAFETY: `PtrByteSlice` is conceptually a read-only view of a byte slice (`&[u8]`).
378// It does not allow mutation and does not own the underlying memory.
379// It is safe to send it to another thread (`Send`) and share it among threads (`Sync`)
380// because the underlying memory is guaranteed to be valid for the lifetime `'a`.
381unsafe impl Send for PtrByteSlice<'_> {}
382// SAFETY: See comment above.
383unsafe impl Sync for PtrByteSlice<'_> {}
384// SAFETY: `MutPtrByteSlice` is conceptually a mutable view of a byte slice (`&mut [u8]`).
385// It enforces exclusive access because it does not implement `Clone` or `Copy`,
386// and all mutating methods require `&mut self` or ownership.
387// It is safe to send it to another thread (`Send`) because only one thread can possess it
388// at a time.
389unsafe impl Send for MutPtrByteSlice<'_> {}
390// SAFETY: `MutPtrByteSlice` is safe to share among threads (`Sync`) because it does not
391// permit safe concurrent mutation through a shared reference (`&self`).
392unsafe impl Sync for MutPtrByteSlice<'_> {}
393
394impl<'a> From<&'a [u8]> for PtrByteSlice<'a> {
395    fn from(slice: &'a [u8]) -> Self {
396        // SAFETY: A standard Rust reference is guaranteed to be valid for reads.
397        unsafe { Self::new(slice as *const [u8]) }
398    }
399}
400
401impl<'a> From<MutPtrByteSlice<'a>> for PtrByteSlice<'a> {
402    fn from(slice: MutPtrByteSlice<'a>) -> Self {
403        // SAFETY: MutPtrByteSlice guarantees the memory is valid for 'a.
404        // Since we consume the MutPtrByteSlice, we can safely return a PtrByteSlice with the same
405        // lifetime.
406        unsafe { Self::new(slice.slice as *const [u8]) }
407    }
408}
409
410impl<'a> From<&'a mut [u8]> for MutPtrByteSlice<'a> {
411    fn from(slice: &'a mut [u8]) -> Self {
412        // SAFETY: A standard Rust mutable reference is guaranteed to be valid and exclusive.
413        unsafe { Self::new(slice as *mut [u8]) }
414    }
415}
416
417/// An iterator over read-only chunks of a pointer slice.
418pub struct Chunks<'a, T> {
419    ptr: *const T,
420    end: *const T,
421    _marker: PhantomData<&'a T>,
422}
423
424impl<'a, T: Copy + FromBytes> Iterator for Chunks<'a, T> {
425    type Item = Chunk<'a, T>;
426
427    fn next(&mut self) -> Option<Self::Item> {
428        if self.ptr == self.end {
429            None
430        } else {
431            let current = self.ptr;
432            // SAFETY: `self.ptr` is less than `self.end` (checked), so adding 1 is within the
433            // bounds of the allocation.
434            self.ptr = unsafe { self.ptr.add(1) };
435            Some(Chunk { ptr: current, _marker: PhantomData })
436        }
437    }
438}
439
440/// A read-only chunk of a pointer slice.
441pub struct Chunk<'a, T> {
442    ptr: *const T,
443    _marker: PhantomData<&'a T>,
444}
445
446impl<T: Copy + FromBytes> Chunk<'_, T> {
447    /// Reads the value from the chunk.
448    ///
449    /// Since alignment and validity were verified once when the iterator was created,
450    /// this access is safe and fast.
451    pub fn read(&self) -> T {
452        // SAFETY: The pointer is guaranteed to be valid and aligned.
453        unsafe { std::ptr::read(self.ptr) }
454    }
455}
456
457/// An iterator over mutable chunks of a pointer slice.
458pub struct ChunksMut<'a, T> {
459    ptr: *mut T,
460    end: *mut T,
461    _marker: PhantomData<&'a mut T>,
462}
463
464impl<'a, T: Copy + FromBytes> Iterator for ChunksMut<'a, T> {
465    type Item = ChunkMut<'a, T>;
466
467    fn next(&mut self) -> Option<Self::Item> {
468        if self.ptr == self.end {
469            None
470        } else {
471            let current = self.ptr;
472            // SAFETY: `self.ptr` is less than `self.end` (checked), so adding 1 is within the
473            // bounds of the allocation.
474            self.ptr = unsafe { self.ptr.add(1) };
475            Some(ChunkMut { ptr: current, _marker: PhantomData })
476        }
477    }
478}
479
480/// A mutable chunk of a pointer slice.
481pub struct ChunkMut<'a, T> {
482    ptr: *mut T,
483    _marker: PhantomData<&'a mut T>,
484}
485
486impl<T: Copy + FromBytes> ChunkMut<'_, T> {
487    /// Reads the value from the chunk.
488    ///
489    /// Since alignment and validity were verified once when the iterator was created,
490    /// this access is safe and fast.
491    pub fn read(&self) -> T {
492        // SAFETY: The pointer is guaranteed to be valid and aligned.
493        unsafe { std::ptr::read(self.ptr) }
494    }
495
496    /// Writes a value to the chunk.
497    ///
498    /// Since alignment and validity were verified once when the iterator was created,
499    /// this access is safe and fast.
500    pub fn write(&self, val: T) {
501        // SAFETY: The pointer is guaranteed to be valid and aligned.
502        unsafe { std::ptr::write(self.ptr, val) }
503    }
504}
505
506#[cfg(test)]
507mod tests {
508    use super::*;
509    use zerocopy::IntoBytes;
510
511    #[derive(Copy, Clone, Debug, FromBytes, IntoBytes)]
512    #[repr(C, align(4))]
513    struct Aligned4(u32);
514
515    #[test]
516    fn test_chunks_success() {
517        let bytes = [0u8; 16];
518        let slice = PtrByteSlice::from(&bytes[..]);
519        let chunks = slice.chunks::<Aligned4>();
520        assert_eq!(chunks.count(), 4);
521    }
522
523    #[test]
524    #[should_panic(expected = "Slice is not aligned to T")]
525    fn test_chunks_unaligned_panic() {
526        #[repr(C, align(4))]
527        struct AligningBuffer {
528            buffer: [u8; 17],
529        }
530        let aligned = AligningBuffer { buffer: [0u8; 17] };
531        let slice = PtrByteSlice::from(&aligned.buffer[1..17]);
532        let _ = slice.chunks::<Aligned4>();
533    }
534
535    #[test]
536    #[should_panic]
537    fn test_chunks_missized_panic() {
538        let bytes = [0u8; 15];
539        let slice = PtrByteSlice::from(&bytes[..]);
540        let _ = slice.chunks::<Aligned4>();
541    }
542
543    #[test]
544    fn test_chunks_mut_success() {
545        let mut bytes = [0u8; 16];
546        let mut slice = MutPtrByteSlice::from(&mut bytes[..]);
547        let chunks = slice.chunks_mut::<Aligned4>();
548        assert_eq!(chunks.count(), 4);
549    }
550
551    #[test]
552    #[should_panic(expected = "Slice is not aligned to T")]
553    fn test_chunks_mut_unaligned_panic() {
554        #[repr(C, align(4))]
555        struct AligningBuffer {
556            buffer: [u8; 17],
557        }
558        let mut aligned = AligningBuffer { buffer: [0u8; 17] };
559        let mut slice = MutPtrByteSlice::from(&mut aligned.buffer[1..17]);
560        let _ = slice.chunks_mut::<Aligned4>();
561    }
562
563    #[test]
564    #[should_panic]
565    fn test_chunks_mut_missized_panic() {
566        let mut bytes = [0u8; 15];
567        let mut slice = MutPtrByteSlice::from(&mut bytes[..]);
568        let _ = slice.chunks_mut::<Aligned4>();
569    }
570}