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}