kernel/processbuffer.rs
1// Licensed under the Apache License, Version 2.0 or the MIT License.
2// SPDX-License-Identifier: Apache-2.0 OR MIT
3// Copyright Tock Contributors 2022.
4
5//! Data structures for passing application memory to the kernel.
6//!
7//! A Tock process can pass read-write or read-only buffers into the
8//! kernel for it to use. The kernel checks that read-write buffers
9//! exist within a process's RAM address space, and that read-only
10//! buffers exist either within its RAM or flash address space. These
11//! buffers are shared with the allow_read_write() and
12//! allow_read_only() system calls.
13//!
14//! A read-write and read-only call is mapped to the high-level Rust
15//! types [`ReadWriteProcessBuffer`] and [`ReadOnlyProcessBuffer`]
16//! respectively. The memory regions can be accessed through the
17//! [`ReadableProcessBuffer`] and [`WriteableProcessBuffer`] traits,
18//! implemented on the process buffer structs.
19//!
20//! Each access to the buffer structs requires a liveness check to ensure that
21//! the process memory is still valid. For a more traditional interface, users
22//! can convert buffers into [`ReadableProcessSlice`] or
23//! [`WriteableProcessSlice`] and use these for the lifetime of their
24//! operations. Users cannot hold live-lived references to these slices,
25//! however.
26
27use core::cell::Cell;
28use core::marker::PhantomData;
29use core::ops::{Deref, Index, Range, RangeFrom, RangeTo};
30
31use crate::ErrorCode;
32use crate::capabilities;
33use crate::process::{self, ProcessId};
34
35/// Convert a process buffer's internal pointer+length representation to a
36/// [`ReadableProcessSlice`].
37///
38/// This function will automatically convert zero-length process buffers into
39/// valid zero-sized Rust slices, regardless of the value of `ptr` (i.e., `ptr`
40/// is allowed to be null for these slices).
41///
42/// # Safety
43///
44/// In the case of `len != 0`, the memory `[ptr; ptr + len)` must be assigned to
45/// one or more processes, and `ptr` must be nonzero. This memory region must be
46/// mapped as _readable_, and optionally _writable_. It must remain a valid,
47/// readable allocation assigned to one or more processes for the entire
48/// lifetime `'a`, and must not be used as backing memory for any Rust
49/// allocations (apart from other process slices).
50///
51/// Callers must ensure that, for its lifetime `'a`, no other programs (other
52/// than this Tock kernel instance) modify the memory behind a
53/// [`ReadableProcessSlice`]. This includes userspace programs, which must not
54/// run in parallel to the Tock kernel holding a process slice reference, or
55/// other Tock kernel instances executing in parallel.
56///
57/// It is sound for multiple (partially) aliased [`ReadableProcessSlice`]s or
58/// [`WriteableProcessSlice`]s to be in scope at the same time, as they use
59/// interior mutability, and their memory is not accessed in parallel by
60/// userspace or other programs running concurrently.
61unsafe fn raw_processbuf_to_roprocessslice<'a>(
62 ptr: *const u8,
63 len: usize,
64) -> &'a ReadableProcessSlice {
65 let ptr: *const ReadableProcessByte = ptr.cast();
66
67 // Transmute a slice reference over readable (read-only or read-write, and
68 // potentially aliased) bytes into a `ReadableProcessSlice` reference.
69 //
70 // SAFETY: This is sound, as `ReadableProcessSlice` is merely a
71 // `#[repr(transparent)]` wrapper around `[ReadableProcessByte]`. However,
72 // we cannot build this struct safely from an intermediate
73 // `[ReadableProcessByte]` slice reference, as we cannot dereference this
74 // unsized type.
75 unsafe {
76 core::mem::transmute::<&[ReadableProcessByte], &ReadableProcessSlice>(
77 // Create a slice of `ReadableProcessByte`s from the supplied
78 // pointer. `ReadableProcessByte` itself permits interior mutability,
79 // and hence this intermediate reference is safe to construct given the
80 // safety contract of this function.
81 //
82 // Rust has very strict requirements on pointer validity[1] which also
83 // in part apply to accesses of length 0. We allow an application to
84 // supply arbitrary pointers if the buffer length is 0, but this is not
85 // allowed for Rust slices. For instance, a null pointer is _never_
86 // valid, not even for accesses of size zero.
87 //
88 // To get a pointer which does not point to valid (allocated) memory,
89 // but is safe to construct for accesses of size zero, we must call
90 // NonNull::dangling(). The resulting pointer is guaranteed to be
91 // well-aligned and uphold the guarantees required for accesses of size
92 // zero.
93 //
94 // [1]: https://doc.rust-lang.org/core/ptr/index.html#safety
95 match len {
96 0 => core::slice::from_raw_parts(
97 core::ptr::NonNull::<ReadableProcessByte>::dangling().as_ptr(),
98 0,
99 ),
100 _ => core::slice::from_raw_parts(ptr, len),
101 },
102 )
103 }
104}
105
106/// Convert a process buffer's internal pointer+length representation to a
107/// [`WriteableProcessSlice`].
108///
109/// This function will automatically convert zero-length process buffers into
110/// valid zero-sized Rust slices, regardless of the value of `ptr` (i.e., `ptr`
111/// is allowed to be null for these slices).
112///
113/// # Safety
114///
115/// In the case of `len != 0`, the memory `[ptr; ptr + len)` must be assigned to
116/// one or more processes, and `ptr` must be nonzero. This memory region must be
117/// mapped as _readable_ and _writable_. It must remain a valid, readable and
118/// writeable allocation assigned to one or more processes for the entire
119/// lifetime `'a`, and must not be used as backing memory for any Rust
120/// allocations (apart from other process slices).
121///
122/// Callers must ensure that, for its lifetime `'a`, no other programs (other
123/// than this Tock kernel instance) modify the memory behind a
124/// [`ReadableProcessSlice`]. This includes userspace programs, which must not
125/// run in parallel to the Tock kernel holding a process slice reference, or
126/// other Tock kernel instances executing in parallel.
127///
128/// It is sound for multiple (partially) aliased [`ReadableProcessSlice`]s or
129/// [`WriteableProcessSlice`]s to be in scope at the same time, as they use
130/// interior mutability, and their memory is not accessed in parallel by
131/// userspace or other programs running concurrently.
132unsafe fn raw_processbuf_to_rwprocessslice<'a>(
133 ptr: *mut u8,
134 len: usize,
135) -> &'a WriteableProcessSlice {
136 // Transmute a slice reference over writeable and potentially aliased bytes
137 // into a `WriteableProcessSlice` reference.
138 //
139 // SAFETY: This is sound, as `WriteableProcessSlice` is merely a
140 // `#[repr(transparent)]` wrapper around `[Cell<u8>]`. However, we cannot
141 // build this struct safely from an intermediate `[WriteableProcessByte]`
142 // slice reference, as we cannot dereference this unsized type.
143 unsafe {
144 core::mem::transmute::<&[Cell<u8>], &WriteableProcessSlice>(
145 // Create a slice of `Cell<u8>`s from the supplied pointer. `Cell<u8>`
146 // itself permits interior mutability, and hence this intermediate
147 // reference is safe to construct given the safety contract of this
148 // function.
149 //
150 // Rust has very strict requirements on pointer validity[1] which also
151 // in part apply to accesses of length 0. We allow an application to
152 // supply arbitrary pointers if the buffer length is 0, but this is not
153 // allowed for Rust slices. For instance, a null pointer is _never_
154 // valid, not even for accesses of size zero.
155 //
156 // To get a pointer which does not point to valid (allocated) memory,
157 // but is safe to construct for accesses of size zero, we must call
158 // NonNull::dangling(). The resulting pointer is guaranteed to be
159 // well-aligned and uphold the guarantees required for accesses of size
160 // zero.
161 //
162 // [1]: https://doc.rust-lang.org/core/ptr/index.html#safety
163 match len {
164 0 => core::slice::from_raw_parts(
165 core::ptr::NonNull::<Cell<u8>>::dangling().as_ptr(),
166 0,
167 ),
168 _ => core::slice::from_raw_parts(ptr as *const Cell<u8>, len),
169 },
170 )
171 }
172}
173
174/// A readable region of userspace process memory.
175///
176/// This trait can be used to gain read-only access to memory regions
177/// wrapped in either a [`ReadOnlyProcessBuffer`] or a
178/// [`ReadWriteProcessBuffer`] type.
179///
180/// # Safety
181///
182/// This is an `unsafe trait` as users of this trait need to trust that the
183/// implementation of [`ReadableProcessBuffer::ptr`] is correct. Implementors of
184/// this trait must ensure that the [`ReadableProcessBuffer::ptr`] method
185/// follows the semantics and invariants described in its documentation.
186pub unsafe trait ReadableProcessBuffer {
187 /// Length of the memory region.
188 ///
189 /// If the process is no longer alive and the memory has been
190 /// reclaimed, this method must return 0.
191 ///
192 /// # Default Process Buffer
193 ///
194 /// A default instance of a process buffer must return 0.
195 fn len(&self) -> usize;
196
197 /// Pointer to the first byte of the userspace-allowed memory region.
198 ///
199 /// If [`ReadableProcessBuffer::len`] returns a non-zero value,
200 /// then this method is guaranteed to return a pointer to the
201 /// start address of a memory region (of length returned by
202 /// `len`), allowable by a userspace process, and allowed to the
203 /// kernel for read operations. The memory region must not be
204 /// written to through this pointer.
205 ///
206 /// If the length of the initially shared memory region
207 /// (irrespective of the return value of
208 /// [`len`](ReadableProcessBuffer::len)) is 0, this function
209 /// returns a pointer to address `0x0`. This is because processes
210 /// may allow zero-length buffer to share no memory with the
211 /// kernel. Because these buffers have zero length, they may have
212 /// any arbitrary pointer value. However, these "dummy addresses"
213 /// should not be leaked, so this method returns 0 for zero-length
214 /// slices. Care must be taken to not create a Rust (slice)
215 /// reference over a null-pointer, as that is undefined behavior.
216 ///
217 /// Users of this pointer must not produce any mutable aliasing, such as by
218 /// creating a reference from this pointer concurrently with calling
219 /// [`WriteableProcessBuffer::mut_enter`].
220 ///
221 /// # Default Process Buffer
222 ///
223 /// A default instance of a process buffer must return a pointer
224 /// to address `0x0`.
225 fn ptr(&self) -> *const u8;
226
227 /// Applies a function to the (read only) process slice reference
228 /// pointed to by the process buffer.
229 ///
230 /// If the process is no longer alive and the memory has been
231 /// reclaimed, this method must return
232 /// `Err(process::Error::NoSuchApp)`.
233 ///
234 /// # Default Process Buffer
235 ///
236 /// A default instance of a process buffer must return
237 /// `Err(process::Error::NoSuchApp)` without executing the passed
238 /// closure.
239 fn enter<F, R>(&self, fun: F) -> Result<R, process::Error>
240 where
241 F: FnOnce(&ReadableProcessSlice) -> R;
242}
243
244/// A readable and writeable region of userspace process memory.
245///
246/// This trait can be used to gain read-write access to memory regions
247/// wrapped in a [`ReadWriteProcessBuffer`].
248///
249/// This is a supertrait of [`ReadableProcessBuffer`], which features
250/// methods allowing mutable access.
251///
252/// # Safety
253///
254/// This is an `unsafe trait` as users of this trait need to trust that the
255/// implementation of [`WriteableProcessBuffer::mut_ptr`] is
256/// correct.
257///
258/// Implementors of this trait must ensure that the
259/// [`WriteableProcessBuffer::mut_ptr`] method follows the semantics and
260/// invariants described in its documentation, and that the length of the
261/// [`WriteableProcessBuffer`] is identical to the value returned by the
262/// [`ReadableProcessBuffer::len`] supertrait method.
263///
264/// Additionally, when using the default implementation of `mut_ptr` provided by
265/// this trait, implementors guarantee that the readable pointer returned by
266/// [`ReadableProcessBuffer::ptr`] points to the same read-write allowed shared
267/// memory region as described by the [`WriteableProcessBuffer`], and that
268/// writes through the pointer returned by [`ReadableProcessBuffer::ptr`] are
269/// sound for [`ReadableProcessBuffer::len`] bytes, notwithstanding any aliasing
270/// requirements.
271pub unsafe trait WriteableProcessBuffer: ReadableProcessBuffer {
272 /// Pointer to the first byte of the userspace-allowed memory region.
273 ///
274 /// If [`ReadableProcessBuffer::len`] returns a non-zero value,
275 /// then this method is guaranteed to return a pointer to the
276 /// start address of a memory region (of length returned by
277 /// `len`), allowable by a userspace process, and allowed to the
278 /// kernel for read or write operations.
279 ///
280 /// If the length of the initially shared memory region
281 /// (irrespective of the return value of
282 /// [`len`](ReadableProcessBuffer::len)) is 0, this function
283 /// returns a pointer to address `0x0`. This is because processes
284 /// may allow zero-length buffer to share no memory with the
285 /// kernel. Because these buffers have zero length, they may have
286 /// any arbitrary pointer value. However, these "dummy addresses"
287 /// should not be leaked, so this method returns 0 for zero-length
288 /// slices. Care must be taken to not create a Rust (slice)
289 /// reference over a null-pointer, as that is undefined behavior.
290 ///
291 /// Users of this pointer must not produce any mutable aliasing, such as by
292 /// creating a reference from this pointer concurrently with calling
293 /// [`WriteableProcessBuffer::mut_enter`].
294 ///
295 /// # Default Process Buffer
296 ///
297 /// A default instance of a process buffer must return a pointer
298 /// to address `0x0`.
299 fn mut_ptr(&self) -> *mut u8 {
300 ReadableProcessBuffer::ptr(self).cast_mut()
301 }
302
303 /// Applies a function to the mutable process slice reference
304 /// pointed to by the [`ReadWriteProcessBuffer`].
305 ///
306 /// If the process is no longer alive and the memory has been
307 /// reclaimed, this method must return
308 /// `Err(process::Error::NoSuchApp)`.
309 ///
310 /// # Default Process Buffer
311 ///
312 /// A default instance of a process buffer must return
313 /// `Err(process::Error::NoSuchApp)` without executing the passed
314 /// closure.
315 fn mut_enter<F, R>(&self, fun: F) -> Result<R, process::Error>
316 where
317 F: FnOnce(&WriteableProcessSlice) -> R;
318}
319
320/// Read-only buffer shared by a userspace process.
321///
322/// This struct is provided to capsules when a process `allow`s a
323/// particular section of its memory to the kernel and gives the
324/// kernel read access to this memory.
325///
326/// It can be used to obtain a [`ReadableProcessSlice`], which is
327/// based around a slice of [`Cell`]s. This is because a userspace can
328/// `allow` overlapping sections of memory into different
329/// [`ReadableProcessSlice`]. Having at least one mutable Rust slice
330/// along with read-only slices to overlapping memory in Rust violates
331/// Rust's aliasing rules. A slice of [`Cell`]s avoids this issue by
332/// explicitly supporting interior mutability. Still, a memory barrier
333/// prior to switching to userspace is required, as the compiler is
334/// free to reorder reads and writes, even through [`Cell`]s.
335pub struct ReadOnlyProcessBuffer {
336 ptr: *const u8,
337 len: usize,
338 process_id: Option<ProcessId>,
339}
340
341impl ReadOnlyProcessBuffer {
342 /// Construct a new [`ReadOnlyProcessBuffer`] over a given pointer and
343 /// length.
344 ///
345 /// # Safety
346 ///
347 /// Refer to the safety requirements of
348 /// [`ReadOnlyProcessBuffer::new_external`].
349 pub(crate) unsafe fn new(ptr: *const u8, len: usize, process_id: ProcessId) -> Self {
350 ReadOnlyProcessBuffer {
351 ptr,
352 len,
353 process_id: Some(process_id),
354 }
355 }
356
357 /// Construct a new [`ReadOnlyProcessBuffer`] over a given pointer
358 /// and length.
359 ///
360 /// Publicly accessible constructor, which requires the
361 /// [`capabilities::ExternalProcessCapability`] capability. This
362 /// is provided to allow implementations of the
363 /// [`Process`](crate::process::Process) trait outside of the
364 /// `kernel` crate.
365 ///
366 /// # Safety
367 ///
368 /// If the length is `0`, an arbitrary pointer may be passed into
369 /// `ptr`. It does not necessarily have to point to allocated
370 /// memory, nor does it have to meet [Rust's pointer validity
371 /// requirements](https://doc.rust-lang.org/core/ptr/index.html#safety).
372 /// [`ReadOnlyProcessBuffer`] must ensure that all Rust slices
373 /// with a length of `0` must be constructed over a valid (but not
374 /// necessarily allocated) base pointer.
375 ///
376 /// If the length is not `0`, the memory region of `[ptr; ptr +
377 /// len)` must be valid memory of the process of the given
378 /// [`ProcessId`]. It must be allocated and and accessible over
379 /// the entire lifetime of the [`ReadOnlyProcessBuffer`]. It must
380 /// not point to memory outside of the process' accessible memory
381 /// range, or point (in part) to other processes or kernel
382 /// memory. The `ptr` must meet [Rust's requirements for pointer
383 /// validity](https://doc.rust-lang.org/core/ptr/index.html#safety),
384 /// in particular it must have a minimum alignment of
385 /// `core::mem::align_of::<u8>()` on the respective platform. It
386 /// must point to memory mapped as _readable_ and optionally
387 /// _writable_ and _executable_.
388 pub unsafe fn new_external(
389 ptr: *const u8,
390 len: usize,
391 process_id: ProcessId,
392 _cap: &dyn capabilities::ExternalProcessCapability,
393 ) -> Self {
394 // SAFETY: See function description.
395 unsafe { Self::new(ptr, len, process_id) }
396 }
397
398 /// Consumes the ReadOnlyProcessBuffer, returning its constituent
399 /// pointer and size. This ensures that there cannot
400 /// simultaneously be both a `ReadOnlyProcessBuffer` and a pointer
401 /// to its internal data.
402 ///
403 /// `consume` can be used when the kernel needs to pass the
404 /// underlying values across the kernel-to-user boundary (e.g., in
405 /// return values to system calls).
406 pub(crate) fn consume(self) -> (*const u8, usize) {
407 (self.ptr, self.len)
408 }
409}
410
411unsafe impl ReadableProcessBuffer for ReadOnlyProcessBuffer {
412 /// Return the length of the buffer in bytes.
413 fn len(&self) -> usize {
414 self.process_id
415 .map_or(0, |pid| pid.kernel.process_map_or(0, pid, |_| self.len))
416 }
417
418 /// Return the pointer to the start of the buffer.
419 fn ptr(&self) -> *const u8 {
420 if self.len == 0 {
421 core::ptr::null::<u8>()
422 } else {
423 self.ptr
424 }
425 }
426
427 /// Access the contents of the buffer in a closure.
428 ///
429 /// This verifies the process is still valid before accessing the underlying
430 /// memory.
431 fn enter<F, R>(&self, fun: F) -> Result<R, process::Error>
432 where
433 F: FnOnce(&ReadableProcessSlice) -> R,
434 {
435 match self.process_id {
436 None => Err(process::Error::NoSuchApp),
437 Some(pid) => pid
438 .kernel
439 .process_map_or(Err(process::Error::NoSuchApp), pid, |_| {
440 // SAFETY: `kernel.process_map_or()` validates that
441 // the process still exists and its memory is still
442 // valid. In particular, `Process` tracks the "high water
443 // mark" of memory that the process has `allow`ed to the
444 // kernel. Because `Process` does not feature an API to
445 // move the "high water mark" down again, which would be
446 // called once a `ProcessBuffer` has been passed back into
447 // the kernel, a given `Process` implementation must assume
448 // that the memory described by a once-allowed
449 // `ProcessBuffer` is still in use, and thus will not
450 // permit the process to free any memory after it has
451 // been `allow`ed to the kernel once. This guarantees
452 // that the buffer is safe to convert into a slice
453 // here. For more information, refer to the
454 // comment and subsequent discussion on tock/tock#2632:
455 // https://github.com/tock/tock/pull/2632#issuecomment-869974365
456 Ok(fun(unsafe {
457 raw_processbuf_to_roprocessslice(self.ptr, self.len)
458 }))
459 }),
460 }
461 }
462}
463
464impl Default for ReadOnlyProcessBuffer {
465 fn default() -> Self {
466 ReadOnlyProcessBuffer {
467 ptr: core::ptr::null_mut::<u8>(),
468 len: 0,
469 process_id: None,
470 }
471 }
472}
473
474/// Provides access to a [`ReadOnlyProcessBuffer`] with a restricted lifetime.
475/// This automatically dereferences into a ReadOnlyProcessBuffer
476pub struct ReadOnlyProcessBufferRef<'a> {
477 buf: ReadOnlyProcessBuffer,
478 _phantom: PhantomData<&'a ()>,
479}
480
481impl ReadOnlyProcessBufferRef<'_> {
482 /// Construct a new [`ReadOnlyProcessBufferRef`] over a given pointer and
483 /// length with a lifetime derived from the caller.
484 ///
485 /// # Safety
486 ///
487 /// Refer to the safety requirements of
488 /// [`ReadOnlyProcessBuffer::new_external`]. The derived lifetime can
489 /// help enforce the invariant that this incoming pointer may only
490 /// be access for a certain duration.
491 pub(crate) unsafe fn new(ptr: *const u8, len: usize, process_id: ProcessId) -> Self {
492 // SAFETY: See function description.
493 unsafe {
494 Self {
495 buf: ReadOnlyProcessBuffer::new(ptr, len, process_id),
496 _phantom: PhantomData,
497 }
498 }
499 }
500}
501
502impl Deref for ReadOnlyProcessBufferRef<'_> {
503 type Target = ReadOnlyProcessBuffer;
504 fn deref(&self) -> &Self::Target {
505 &self.buf
506 }
507}
508
509/// Read-writable buffer shared by a userspace process.
510///
511/// This struct is provided to capsules when a process `allows` a
512/// particular section of its memory to the kernel and gives the
513/// kernel read and write access to this memory.
514///
515/// It can be used to obtain a [`WriteableProcessSlice`], which is
516/// based around a slice of [`Cell`]s. This is because a userspace can
517/// `allow` overlapping sections of memory into different
518/// [`WriteableProcessSlice`]. Having at least one mutable Rust slice
519/// along with read-only or other mutable slices to overlapping memory
520/// in Rust violates Rust's aliasing rules. A slice of [`Cell`]s
521/// avoids this issue by explicitly supporting interior
522/// mutability. Still, a memory barrier prior to switching to
523/// userspace is required, as the compiler is free to reorder reads
524/// and writes, even through [`Cell`]s.
525pub struct ReadWriteProcessBuffer {
526 ptr: *mut u8,
527 len: usize,
528 process_id: Option<ProcessId>,
529}
530
531impl ReadWriteProcessBuffer {
532 /// Construct a new [`ReadWriteProcessBuffer`] over a given
533 /// pointer and length.
534 ///
535 /// # Safety
536 ///
537 /// Refer to the safety requirements of
538 /// [`ReadWriteProcessBuffer::new_external`].
539 pub(crate) unsafe fn new(ptr: *mut u8, len: usize, process_id: ProcessId) -> Self {
540 ReadWriteProcessBuffer {
541 ptr,
542 len,
543 process_id: Some(process_id),
544 }
545 }
546
547 /// Construct a new [`ReadWriteProcessBuffer`] over a given
548 /// pointer and length.
549 ///
550 /// Publicly accessible constructor, which requires the
551 /// [`capabilities::ExternalProcessCapability`] capability. This
552 /// is provided to allow implementations of the
553 /// [`Process`](crate::process::Process) trait outside of the
554 /// `kernel` crate.
555 ///
556 /// # Safety
557 ///
558 /// If the length is `0`, an arbitrary pointer may be passed into
559 /// `ptr`. It does not necessarily have to point to allocated
560 /// memory, nor does it have to meet [Rust's pointer validity
561 /// requirements](https://doc.rust-lang.org/core/ptr/index.html#safety).
562 /// [`ReadWriteProcessBuffer`] must ensure that all Rust slices
563 /// with a length of `0` must be constructed over a valid (but not
564 /// necessarily allocated) base pointer.
565 ///
566 /// If the length is not `0`, the memory region of `[ptr; ptr +
567 /// len)` must be valid memory of the process of the given
568 /// [`ProcessId`]. It must be allocated and and accessible over
569 /// the entire lifetime of the [`ReadWriteProcessBuffer`]. It must
570 /// not point to memory outside of the process' accessible memory
571 /// range, or point (in part) to other processes or kernel
572 /// memory. The `ptr` must meet [Rust's requirements for pointer
573 /// validity](https://doc.rust-lang.org/core/ptr/index.html#safety),
574 /// in particular it must have a minimum alignment of
575 /// `core::mem::align_of::<u8>()` on the respective platform. It
576 /// must point to memory mapped as _readable_ and optionally
577 /// _writable_ and _executable_.
578 pub unsafe fn new_external(
579 ptr: *mut u8,
580 len: usize,
581 process_id: ProcessId,
582 _cap: &dyn capabilities::ExternalProcessCapability,
583 ) -> Self {
584 // SAFETY: See function description.
585 unsafe { Self::new(ptr, len, process_id) }
586 }
587
588 /// Consumes the ReadWriteProcessBuffer, returning its constituent
589 /// pointer and size. This ensures that there cannot
590 /// simultaneously be both a `ReadWriteProcessBuffer` and a pointer to
591 /// its internal data.
592 ///
593 /// `consume` can be used when the kernel needs to pass the
594 /// underlying values across the kernel-to-user boundary (e.g., in
595 /// return values to system calls).
596 pub(crate) fn consume(self) -> (*mut u8, usize) {
597 (self.ptr, self.len)
598 }
599
600 /// This is a `const` version of `Default::default` with the same
601 /// semantics.
602 ///
603 /// Having a const initializer allows initializing a fixed-size
604 /// array with default values without the struct being marked
605 /// `Copy` as such:
606 ///
607 /// ```
608 /// use kernel::processbuffer::ReadWriteProcessBuffer;
609 /// const DEFAULT_RWPROCBUF_VAL: ReadWriteProcessBuffer
610 /// = ReadWriteProcessBuffer::const_default();
611 /// let my_array = [DEFAULT_RWPROCBUF_VAL; 12];
612 /// ```
613 pub const fn const_default() -> Self {
614 Self {
615 ptr: core::ptr::null_mut::<u8>(),
616 len: 0,
617 process_id: None,
618 }
619 }
620}
621
622unsafe impl ReadableProcessBuffer for ReadWriteProcessBuffer {
623 /// Return the length of the buffer in bytes.
624 fn len(&self) -> usize {
625 self.process_id
626 .map_or(0, |pid| pid.kernel.process_map_or(0, pid, |_| self.len))
627 }
628
629 /// Return the pointer to the start of the buffer.
630 fn ptr(&self) -> *const u8 {
631 if self.len == 0 {
632 core::ptr::null::<u8>()
633 } else {
634 self.ptr
635 }
636 }
637
638 /// Access the contents of the buffer in a closure.
639 ///
640 /// This verifies the process is still valid before accessing the underlying
641 /// memory.
642 fn enter<F, R>(&self, fun: F) -> Result<R, process::Error>
643 where
644 F: FnOnce(&ReadableProcessSlice) -> R,
645 {
646 match self.process_id {
647 None => Err(process::Error::NoSuchApp),
648 Some(pid) => pid
649 .kernel
650 .process_map_or(Err(process::Error::NoSuchApp), pid, |_| {
651 // SAFETY: `kernel.process_map_or()` validates that
652 // the process still exists and its memory is still
653 // valid. In particular, `Process` tracks the "high water
654 // mark" of memory that the process has `allow`ed to the
655 // kernel. Because `Process` does not feature an API to
656 // move the "high water mark" down again, which would be
657 // called once a `ProcessBuffer` has been passed back into
658 // the kernel, a given `Process` implementation must assume
659 // that the memory described by a once-allowed
660 // `ProcessBuffer` is still in use, and thus will not
661 // permit the process to free any memory after it has
662 // been `allow`ed to the kernel once. This guarantees
663 // that the buffer is safe to convert into a slice
664 // here. For more information, refer to the
665 // comment and subsequent discussion on tock/tock#2632:
666 // https://github.com/tock/tock/pull/2632#issuecomment-869974365
667 Ok(fun(unsafe {
668 raw_processbuf_to_roprocessslice(self.ptr, self.len)
669 }))
670 }),
671 }
672 }
673}
674
675unsafe impl WriteableProcessBuffer for ReadWriteProcessBuffer {
676 fn mut_enter<F, R>(&self, fun: F) -> Result<R, process::Error>
677 where
678 F: FnOnce(&WriteableProcessSlice) -> R,
679 {
680 match self.process_id {
681 None => Err(process::Error::NoSuchApp),
682 Some(pid) => pid
683 .kernel
684 .process_map_or(Err(process::Error::NoSuchApp), pid, |_| {
685 // SAFETY: `kernel.process_map_or()` validates that
686 // the process still exists and its memory is still
687 // valid. In particular, `Process` tracks the "high water
688 // mark" of memory that the process has `allow`ed to the
689 // kernel. Because `Process` does not feature an API to
690 // move the "high water mark" down again, which would be
691 // called once a `ProcessBuffer` has been passed back into
692 // the kernel, a given `Process` implementation must assume
693 // that the memory described by a once-allowed
694 // `ProcessBuffer` is still in use, and thus will not
695 // permit the process to free any memory after it has
696 // been `allow`ed to the kernel once. This guarantees
697 // that the buffer is safe to convert into a slice
698 // here. For more information, refer to the
699 // comment and subsequent discussion on tock/tock#2632:
700 // https://github.com/tock/tock/pull/2632#issuecomment-869974365
701 Ok(fun(unsafe {
702 raw_processbuf_to_rwprocessslice(self.ptr, self.len)
703 }))
704 }),
705 }
706 }
707}
708
709impl Default for ReadWriteProcessBuffer {
710 fn default() -> Self {
711 Self::const_default()
712 }
713}
714
715/// Provides access to a [`ReadWriteProcessBuffer`] with a restricted lifetime.
716/// This automatically dereferences into a ReadWriteProcessBuffer
717pub struct ReadWriteProcessBufferRef<'a> {
718 buf: ReadWriteProcessBuffer,
719 _phantom: PhantomData<&'a ()>,
720}
721
722impl ReadWriteProcessBufferRef<'_> {
723 /// Construct a new [`ReadWriteProcessBufferRef`] over a given pointer and
724 /// length with a lifetime derived from the caller.
725 ///
726 /// # Safety
727 ///
728 /// Refer to the safety requirements of
729 /// [`ReadWriteProcessBuffer::new_external`]. The derived lifetime can
730 /// help enforce the invariant that this incoming pointer may only
731 /// be access for a certain duration.
732 pub(crate) unsafe fn new(ptr: *mut u8, len: usize, process_id: ProcessId) -> Self {
733 // SAFETY: See function description.
734 unsafe {
735 Self {
736 buf: ReadWriteProcessBuffer::new(ptr, len, process_id),
737 _phantom: PhantomData,
738 }
739 }
740 }
741}
742
743impl Deref for ReadWriteProcessBufferRef<'_> {
744 type Target = ReadWriteProcessBuffer;
745 fn deref(&self) -> &Self::Target {
746 &self.buf
747 }
748}
749
750/// A shareable region of userspace memory.
751///
752/// This trait can be used to gain read-write access to memory regions
753/// wrapped in a ProcessBuffer type.
754// We currently don't need any special functionality in the kernel for this
755// type so we alias it as `ReadWriteProcessBuffer`.
756pub type UserspaceReadableProcessBuffer = ReadWriteProcessBuffer;
757
758/// Equivalent of the Rust core library's
759/// [`SliceIndex`](core::slice::SliceIndex) type for process slices.
760///
761/// This helper trait is used to abstract over indexing operators into
762/// process slices, and is used to "overload" the `.get()` methods
763/// such that it can be called with multiple different indexing
764/// operators.
765///
766/// While we can use the core library's `SliceIndex` trait, parameterized over
767/// our own `ProcessSlice` types, this trait includes mandatory methods that are
768/// undesirable for the process buffer infrastructure, such as unchecked or
769/// mutable index operations. Furthermore, implementing it requires the
770/// `slice_index_methods` nightly feature. Thus we vendor our own, small variant
771/// of this trait.
772pub trait ProcessSliceIndex<PB: ?Sized>: private_process_slice_index::Sealed {
773 type Output: ?Sized;
774 fn get(self, slice: &PB) -> Option<&Self::Output>;
775 fn index(self, slice: &PB) -> &Self::Output;
776}
777
778// Analog to `private_slice_index` from
779// https://github.com/rust-lang/rust/blob/a1eceec00b2684f947481696ae2322e20d59db60/library/core/src/slice/index.rs#L149
780mod private_process_slice_index {
781 use core::ops::{Range, RangeFrom, RangeTo};
782
783 pub trait Sealed {}
784
785 impl Sealed for usize {}
786 impl Sealed for Range<usize> {}
787 impl Sealed for RangeFrom<usize> {}
788 impl Sealed for RangeTo<usize> {}
789}
790
791/// Read-only wrapper around a [`Cell`]
792///
793/// This type is used in providing the [`ReadableProcessSlice`]. The
794/// memory over which a [`ReadableProcessSlice`] exists must never be
795/// written to by the kernel. However, it may either exist in flash
796/// (read-only memory) or RAM (read-writeable memory). Consequently, a
797/// process may `allow` memory overlapping with a
798/// [`ReadOnlyProcessBuffer`] also simultaneously through a
799/// [`ReadWriteProcessBuffer`]. Hence, the kernel can have two
800/// references to the same memory, where one can lead to mutation of
801/// the memory contents. Therefore, the kernel must use [`Cell`]s
802/// around the bytes shared with userspace, to avoid violating Rust's
803/// aliasing rules.
804///
805/// This read-only wrapper around a [`Cell`] only exposes methods
806/// which are safe to call on a process-shared read-only `allow`
807/// memory.
808#[repr(transparent)]
809pub struct ReadableProcessByte {
810 cell: Cell<u8>,
811}
812
813impl ReadableProcessByte {
814 #[inline]
815 pub fn get(&self) -> u8 {
816 self.cell.get()
817 }
818}
819
820/// Readable and accessible slice of memory of a process buffer.
821///
822///
823/// The only way to obtain this struct is through a
824/// [`ReadWriteProcessBuffer`] or [`ReadOnlyProcessBuffer`].
825///
826/// Slices provide a more convenient, traditional interface to process
827/// memory. These slices are transient, as the underlying buffer must
828/// be checked each time a slice is created. This is usually enforced
829/// by the anonymous lifetime defined by the creation of the slice.
830#[repr(transparent)]
831pub struct ReadableProcessSlice {
832 slice: [ReadableProcessByte],
833}
834
835fn cast_byte_slice_to_process_slice(byte_slice: &[ReadableProcessByte]) -> &ReadableProcessSlice {
836 // As ReadableProcessSlice is a transparent wrapper around its inner type,
837 // [ReadableProcessByte], we can safely transmute a reference to the inner
838 // type as a reference to the outer type with the same lifetime.
839 unsafe { core::mem::transmute::<&[ReadableProcessByte], &ReadableProcessSlice>(byte_slice) }
840}
841
842// Allow a u8 slice to be viewed as a ReadableProcessSlice to allow client code
843// to be authored once and accept either [u8] or ReadableProcessSlice.
844impl<'a> From<&'a [u8]> for &'a ReadableProcessSlice {
845 fn from(val: &'a [u8]) -> Self {
846 // SAFETY: The layout of a [u8] and ReadableProcessSlice are guaranteed to be
847 // the same. This also extends the lifetime of the buffer, so aliasing
848 // rules are thus maintained properly.
849 unsafe { core::mem::transmute(val) }
850 }
851}
852
853// Allow a mutable u8 slice to be viewed as a ReadableProcessSlice to allow
854// client code to be authored once and accept either [u8] or
855// ReadableProcessSlice.
856impl<'a> From<&'a mut [u8]> for &'a ReadableProcessSlice {
857 fn from(val: &'a mut [u8]) -> Self {
858 // SAFETY: The layout of a [u8] and ReadableProcessSlice are guaranteed to be
859 // the same. This also extends the mutable lifetime of the buffer, so
860 // aliasing rules are thus maintained properly.
861 unsafe { core::mem::transmute(val) }
862 }
863}
864
865impl ReadableProcessSlice {
866 /// Copy the contents of a [`ReadableProcessSlice`] into a mutable
867 /// slice reference.
868 ///
869 /// The length of `self` must be the same as `dest`. Subslicing
870 /// can be used to obtain a slice of matching length.
871 ///
872 /// # Panics
873 ///
874 /// This function will panic if `self.len() != dest.len()`.
875 pub fn copy_to_slice(&self, dest: &mut [u8]) {
876 // The panic code path was put into a cold function to not
877 // bloat the call site.
878 #[inline(never)]
879 #[cold]
880 #[track_caller]
881 fn len_mismatch_fail(dst_len: usize, src_len: usize) -> ! {
882 panic!(
883 "source slice length ({}) does not match destination slice length ({})",
884 src_len, dst_len,
885 );
886 }
887
888 if self.copy_to_slice_or_err(dest).is_err() {
889 len_mismatch_fail(dest.len(), self.len());
890 }
891 }
892
893 /// Copy the contents of a [`ReadableProcessSlice`] into a mutable
894 /// slice reference.
895 ///
896 /// The length of `self` must be the same as `dest`. Subslicing
897 /// can be used to obtain a slice of matching length.
898 pub fn copy_to_slice_or_err(&self, dest: &mut [u8]) -> Result<(), ErrorCode> {
899 // Method implemetation adopted from the
900 // core::slice::copy_from_slice method implementation:
901 // https://doc.rust-lang.org/src/core/slice/mod.rs.html#3034-3036
902
903 if self.len() != dest.len() {
904 Err(ErrorCode::SIZE)
905 } else {
906 // _If_ this turns out to not be efficiently optimized, it
907 // should be possible to use a ptr::copy_nonoverlapping here
908 // given we have exclusive mutable access to the destination
909 // slice which will never be in process memory, and the layout
910 // of &[ReadableProcessByte] is guaranteed to be compatible to
911 // &[u8].
912 for (i, b) in self.slice.iter().enumerate() {
913 dest[i] = b.get();
914 }
915 Ok(())
916 }
917 }
918
919 /// Return the length of the slice in bytes.
920 pub fn len(&self) -> usize {
921 self.slice.len()
922 }
923
924 /// Return an iterator over the bytes of the slice.
925 pub fn iter(&self) -> core::slice::Iter<'_, ReadableProcessByte> {
926 self.slice.iter()
927 }
928
929 /// Iterate the slice in chunks.
930 pub fn chunks(
931 &self,
932 chunk_size: usize,
933 ) -> impl core::iter::Iterator<Item = &ReadableProcessSlice> {
934 self.slice
935 .chunks(chunk_size)
936 .map(cast_byte_slice_to_process_slice)
937 }
938
939 /// Access a portion of the slice with bounds checking. If the access is not
940 /// within the slice then `None` is returned.
941 pub fn get<I: ProcessSliceIndex<Self>>(
942 &self,
943 index: I,
944 ) -> Option<&<I as ProcessSliceIndex<Self>>::Output> {
945 index.get(self)
946 }
947
948 /// Access a portion of the slice with bounds checking. If the access is not
949 /// within the slice then `None` is returned.
950 #[deprecated = "Use ReadableProcessSlice::get instead"]
951 pub fn get_from(&self, range: RangeFrom<usize>) -> Option<&ReadableProcessSlice> {
952 range.get(self)
953 }
954
955 /// Access a portion of the slice with bounds checking. If the access is not
956 /// within the slice then `None` is returned.
957 #[deprecated = "Use ReadableProcessSlice::get instead"]
958 pub fn get_to(&self, range: RangeTo<usize>) -> Option<&ReadableProcessSlice> {
959 range.get(self)
960 }
961}
962
963impl ProcessSliceIndex<ReadableProcessSlice> for usize {
964 type Output = ReadableProcessByte;
965
966 fn get(self, slice: &ReadableProcessSlice) -> Option<&Self::Output> {
967 slice.slice.get(self)
968 }
969
970 fn index(self, slice: &ReadableProcessSlice) -> &Self::Output {
971 &slice.slice[self]
972 }
973}
974
975impl ProcessSliceIndex<ReadableProcessSlice> for Range<usize> {
976 type Output = ReadableProcessSlice;
977
978 fn get(self, slice: &ReadableProcessSlice) -> Option<&Self::Output> {
979 slice.slice.get(self).map(cast_byte_slice_to_process_slice)
980 }
981
982 fn index(self, slice: &ReadableProcessSlice) -> &Self::Output {
983 cast_byte_slice_to_process_slice(&slice.slice[self])
984 }
985}
986
987impl ProcessSliceIndex<ReadableProcessSlice> for RangeFrom<usize> {
988 type Output = ReadableProcessSlice;
989
990 fn get(self, slice: &ReadableProcessSlice) -> Option<&Self::Output> {
991 slice.slice.get(self).map(cast_byte_slice_to_process_slice)
992 }
993
994 fn index(self, slice: &ReadableProcessSlice) -> &Self::Output {
995 cast_byte_slice_to_process_slice(&slice.slice[self])
996 }
997}
998
999impl ProcessSliceIndex<ReadableProcessSlice> for RangeTo<usize> {
1000 type Output = ReadableProcessSlice;
1001
1002 fn get(self, slice: &ReadableProcessSlice) -> Option<&Self::Output> {
1003 slice.slice.get(self).map(cast_byte_slice_to_process_slice)
1004 }
1005
1006 fn index(self, slice: &ReadableProcessSlice) -> &Self::Output {
1007 cast_byte_slice_to_process_slice(&slice.slice[self])
1008 }
1009}
1010
1011impl<I: ProcessSliceIndex<Self>> Index<I> for ReadableProcessSlice {
1012 type Output = I::Output;
1013
1014 fn index(&self, index: I) -> &Self::Output {
1015 index.index(self)
1016 }
1017}
1018
1019/// Read-writeable and accessible slice of memory of a process buffer
1020///
1021/// The only way to obtain this struct is through a
1022/// [`ReadWriteProcessBuffer`].
1023///
1024/// Slices provide a more convenient, traditional interface to process
1025/// memory. These slices are transient, as the underlying buffer must
1026/// be checked each time a slice is created. This is usually enforced
1027/// by the anonymous lifetime defined by the creation of the slice.
1028#[repr(transparent)]
1029pub struct WriteableProcessSlice {
1030 slice: [Cell<u8>],
1031}
1032
1033fn cast_cell_slice_to_process_slice(cell_slice: &[Cell<u8>]) -> &WriteableProcessSlice {
1034 // SAFETY: As WriteableProcessSlice is a transparent wrapper around its inner type,
1035 // [Cell<u8>], we can safely transmute a reference to the inner type as the
1036 // outer type with the same lifetime.
1037 unsafe { core::mem::transmute(cell_slice) }
1038}
1039
1040// Allow a mutable u8 slice to be viewed as a WritableProcessSlice to allow
1041// client code to be authored once and accept either [u8] or
1042// WriteableProcessSlice.
1043impl<'a> From<&'a mut [u8]> for &'a WriteableProcessSlice {
1044 fn from(val: &'a mut [u8]) -> Self {
1045 // SAFETY: The layout of a [u8] and WriteableProcessSlice are guaranteed to be
1046 // the same. This also extends the mutable lifetime of the buffer, so
1047 // aliasing rules are thus maintained properly.
1048 unsafe { core::mem::transmute(val) }
1049 }
1050}
1051
1052impl WriteableProcessSlice {
1053 /// Copy the contents of a [`WriteableProcessSlice`] into a mutable
1054 /// slice reference.
1055 ///
1056 /// The length of `self` must be the same as `dest`. Subslicing
1057 /// can be used to obtain a slice of matching length.
1058 ///
1059 /// # Panics
1060 ///
1061 /// This function will panic if `self.len() != dest.len()`.
1062 pub fn copy_to_slice(&self, dest: &mut [u8]) {
1063 // The panic code path was put into a cold function to not
1064 // bloat the call site.
1065 #[inline(never)]
1066 #[cold]
1067 #[track_caller]
1068 fn len_mismatch_fail(dst_len: usize, src_len: usize) -> ! {
1069 panic!(
1070 "source slice length ({}) does not match destination slice length ({})",
1071 src_len, dst_len,
1072 );
1073 }
1074
1075 if self.copy_to_slice_or_err(dest).is_err() {
1076 len_mismatch_fail(dest.len(), self.len());
1077 }
1078 }
1079
1080 /// Copy the contents of a [`WriteableProcessSlice`] into a mutable
1081 /// slice reference.
1082 ///
1083 /// The length of `self` must be the same as `dest`. Subslicing
1084 /// can be used to obtain a slice of matching length.
1085 pub fn copy_to_slice_or_err(&self, dest: &mut [u8]) -> Result<(), ErrorCode> {
1086 // Method implemetation adopted from the
1087 // core::slice::copy_from_slice method implementation:
1088 // https://doc.rust-lang.org/src/core/slice/mod.rs.html#3034-3036
1089
1090 if self.len() != dest.len() {
1091 Err(ErrorCode::SIZE)
1092 } else {
1093 // _If_ this turns out to not be efficiently optimized, it
1094 // should be possible to use a ptr::copy_nonoverlapping here
1095 // given we have exclusive mutable access to the destination
1096 // slice which will never be in process memory, and the layout
1097 // of &[Cell<u8>] is guaranteed to be compatible to &[u8].
1098 self.slice
1099 .iter()
1100 .zip(dest.iter_mut())
1101 .for_each(|(src, dst)| *dst = src.get());
1102 Ok(())
1103 }
1104 }
1105
1106 /// Copy the contents of a slice of bytes into a [`WriteableProcessSlice`].
1107 ///
1108 /// The length of `src` must be the same as `self`. Subslicing can
1109 /// be used to obtain a slice of matching length.
1110 ///
1111 /// # Panics
1112 ///
1113 /// This function will panic if `src.len() != self.len()`.
1114 pub fn copy_from_slice(&self, src: &[u8]) {
1115 // Method implemetation adopted from the
1116 // core::slice::copy_from_slice method implementation:
1117 // https://doc.rust-lang.org/src/core/slice/mod.rs.html#3034-3036
1118
1119 // The panic code path was put into a cold function to not
1120 // bloat the call site.
1121 #[inline(never)]
1122 #[cold]
1123 #[track_caller]
1124 fn len_mismatch_fail(dst_len: usize, src_len: usize) -> ! {
1125 panic!(
1126 "src slice len ({}) != dest slice len ({})",
1127 src_len, dst_len,
1128 );
1129 }
1130
1131 if self.copy_from_slice_or_err(src).is_err() {
1132 len_mismatch_fail(self.len(), src.len());
1133 }
1134 }
1135
1136 /// Copy the contents of a slice of bytes into a [`WriteableProcessSlice`].
1137 ///
1138 /// The length of `src` must be the same as `self`. Subslicing can
1139 /// be used to obtain a slice of matching length.
1140 pub fn copy_from_slice_or_err(&self, src: &[u8]) -> Result<(), ErrorCode> {
1141 // Method implemetation adopted from the
1142 // core::slice::copy_from_slice method implementation:
1143 // https://doc.rust-lang.org/src/core/slice/mod.rs.html#3034-3036
1144
1145 if self.len() != src.len() {
1146 Err(ErrorCode::SIZE)
1147 } else {
1148 // _If_ this turns out to not be efficiently optimized, it
1149 // should be possible to use a ptr::copy_nonoverlapping here
1150 // given we have exclusive mutable access to the destination
1151 // slice which will never be in process memory, and the layout
1152 // of &[Cell<u8>] is guaranteed to be compatible to &[u8].
1153 src.iter()
1154 .zip(self.slice.iter())
1155 .for_each(|(src, dst)| dst.set(*src));
1156 Ok(())
1157 }
1158 }
1159
1160 /// Return the length of the slice in bytes.
1161 pub fn len(&self) -> usize {
1162 self.slice.len()
1163 }
1164
1165 /// Return an iterator over the slice.
1166 pub fn iter(&self) -> core::slice::Iter<'_, Cell<u8>> {
1167 self.slice.iter()
1168 }
1169
1170 /// Iterate over the slice in chunks.
1171 pub fn chunks(
1172 &self,
1173 chunk_size: usize,
1174 ) -> impl core::iter::Iterator<Item = &WriteableProcessSlice> {
1175 self.slice
1176 .chunks(chunk_size)
1177 .map(cast_cell_slice_to_process_slice)
1178 }
1179
1180 /// Access a portion of the slice with bounds checking. If the access is not
1181 /// within the slice then `None` is returned.
1182 pub fn get<I: ProcessSliceIndex<Self>>(
1183 &self,
1184 index: I,
1185 ) -> Option<&<I as ProcessSliceIndex<Self>>::Output> {
1186 index.get(self)
1187 }
1188
1189 /// Access a portion of the slice with bounds checking. If the access is not
1190 /// within the slice then `None` is returned.
1191 #[deprecated = "Use WriteableProcessSlice::get instead"]
1192 pub fn get_from(&self, range: RangeFrom<usize>) -> Option<&WriteableProcessSlice> {
1193 range.get(self)
1194 }
1195
1196 /// Access a portion of the slice with bounds checking. If the access is not
1197 /// within the slice then `None` is returned.
1198 #[deprecated = "Use WriteableProcessSlice::get instead"]
1199 pub fn get_to(&self, range: RangeTo<usize>) -> Option<&WriteableProcessSlice> {
1200 range.get(self)
1201 }
1202}
1203
1204impl ProcessSliceIndex<WriteableProcessSlice> for usize {
1205 type Output = Cell<u8>;
1206
1207 fn get(self, slice: &WriteableProcessSlice) -> Option<&Self::Output> {
1208 slice.slice.get(self)
1209 }
1210
1211 fn index(self, slice: &WriteableProcessSlice) -> &Self::Output {
1212 &slice.slice[self]
1213 }
1214}
1215
1216impl ProcessSliceIndex<WriteableProcessSlice> for Range<usize> {
1217 type Output = WriteableProcessSlice;
1218
1219 fn get(self, slice: &WriteableProcessSlice) -> Option<&Self::Output> {
1220 slice.slice.get(self).map(cast_cell_slice_to_process_slice)
1221 }
1222
1223 fn index(self, slice: &WriteableProcessSlice) -> &Self::Output {
1224 cast_cell_slice_to_process_slice(&slice.slice[self])
1225 }
1226}
1227
1228impl ProcessSliceIndex<WriteableProcessSlice> for RangeFrom<usize> {
1229 type Output = WriteableProcessSlice;
1230
1231 fn get(self, slice: &WriteableProcessSlice) -> Option<&Self::Output> {
1232 slice.slice.get(self).map(cast_cell_slice_to_process_slice)
1233 }
1234
1235 fn index(self, slice: &WriteableProcessSlice) -> &Self::Output {
1236 cast_cell_slice_to_process_slice(&slice.slice[self])
1237 }
1238}
1239
1240impl ProcessSliceIndex<WriteableProcessSlice> for RangeTo<usize> {
1241 type Output = WriteableProcessSlice;
1242
1243 fn get(self, slice: &WriteableProcessSlice) -> Option<&Self::Output> {
1244 slice.slice.get(self).map(cast_cell_slice_to_process_slice)
1245 }
1246
1247 fn index(self, slice: &WriteableProcessSlice) -> &Self::Output {
1248 cast_cell_slice_to_process_slice(&slice.slice[self])
1249 }
1250}
1251
1252impl<I: ProcessSliceIndex<Self>> Index<I> for WriteableProcessSlice {
1253 type Output = I::Output;
1254
1255 fn index(&self, index: I) -> &Self::Output {
1256 index.index(self)
1257 }
1258}
1259
1260#[cfg(test)]
1261mod miri_tests {
1262 use super::*;
1263 use core::cell::UnsafeCell;
1264
1265 // Helper to get a raw mutable pointer to the backing memory we use to
1266 // create process slices over. This backing memory, though allocated by
1267 // Rust, contains only `UnsafeCell`s and thus is suitable for creating
1268 // process slice references over.
1269 fn get_backing_memory_ptr<const N: usize>(mem: &[UnsafeCell<u8>; N]) -> *mut u8 {
1270 mem as *const _ as *mut u8
1271 }
1272
1273 #[test]
1274 fn test_basic_read_write() {
1275 let memory = [const { UnsafeCell::new(0u8) }; 16];
1276 let ptr = get_backing_memory_ptr(&memory);
1277 let slice = unsafe { raw_processbuf_to_rwprocessslice(ptr, memory.len()) };
1278
1279 // Test writing via the slice
1280 slice[0].set(42);
1281 slice[5].set(100);
1282
1283 // Test reading back
1284 assert_eq!(slice[0].get(), 42);
1285 assert_eq!(slice[5].get(), 100);
1286
1287 // Verify backing memory was actually updated
1288 assert_eq!(unsafe { *memory[0].get() }, 42);
1289 }
1290
1291 #[test]
1292 fn test_concurrent_rw_rw_aliasing() {
1293 // Ensure multiple mutable slices to the same memory do not violate tree
1294 // borrows. This works because WriteableProcessSlice uses Cell
1295 // internally.
1296 let memory = [const { UnsafeCell::new(0u8) }; 16];
1297 let ptr = get_backing_memory_ptr(&memory);
1298
1299 // Create two overlapping slices
1300 let slice1 = unsafe { raw_processbuf_to_rwprocessslice(ptr, memory.len()) };
1301 let slice2 = unsafe { raw_processbuf_to_rwprocessslice(ptr, memory.len()) };
1302
1303 slice1[0].set(10);
1304 assert_eq!(slice2[0].get(), 10);
1305
1306 slice2[0].set(20);
1307 assert_eq!(slice1[0].get(), 20);
1308
1309 // Test interleaved access
1310 let sub1 = slice1.get(0..4).unwrap();
1311 let sub2 = slice2.get(2..6).unwrap();
1312
1313 // sub1: [0, 1, 2, 3]
1314 // sub2: [2, 3, 4, 5]
1315 // Intersection at indices 2 and 3 of the original buffer
1316
1317 sub1[2].set(55); // Index 2 of backing
1318 assert_eq!(sub2[0].get(), 55); // Idx 0 of sub2 is idx 2 of backing
1319 }
1320
1321 #[test]
1322 fn test_concurrent_ro_rw_aliasing() {
1323 // Ensure multiple mutable slices to the same memory do not violate tree
1324 // borrows. This works because ReadnableProcessSlice and
1325 // WriteableProcessSlice both use Cell internally.
1326 let memory = [const { UnsafeCell::new(0u8) }; 16];
1327 let ptr = get_backing_memory_ptr(&memory);
1328
1329 // Create two overlapping slices
1330 let slice1 = unsafe { raw_processbuf_to_roprocessslice(ptr, memory.len()) };
1331 let slice2 = unsafe { raw_processbuf_to_rwprocessslice(ptr, memory.len()) };
1332
1333 slice2[0].set(20);
1334 assert_eq!(slice1[0].get(), 20);
1335
1336 // Test interleaved access
1337 let sub1 = slice1.get(0..4).unwrap();
1338 let sub2 = slice2.get(2..6).unwrap();
1339
1340 // sub1: [0, 1, 2, 3]
1341 // sub2: [2, 3, 4, 5]
1342 // Intersection at indices 2 and 3 of the original buffer
1343
1344 sub2[0].set(55); // Index 0 of sub2 is index 2 of backing
1345 assert_eq!(sub1[2].get(), 55); // Index 2 of backing
1346 }
1347
1348 #[test]
1349 fn test_zero_length_null_ptr_ro() {
1350 // Should be safe to create a 0-len slice from a null pointer
1351 let slice = unsafe { raw_processbuf_to_roprocessslice(core::ptr::null_mut(), 0) };
1352 assert_eq!(slice.len(), 0);
1353 assert!(slice.get(0).is_none());
1354
1355 // Iteration should simply yield nothing
1356 let mut count = 0;
1357 for _ in slice.iter() {
1358 count += 1;
1359 }
1360 assert_eq!(count, 0);
1361
1362 // Slice should be created over a non-null pointer
1363 // (NonNull::dangling()):
1364 assert_eq!(
1365 slice as *const ReadableProcessSlice as *const u8,
1366 core::ptr::NonNull::<u8>::dangling().as_ptr(),
1367 );
1368 }
1369
1370 #[test]
1371 fn test_zero_length_non_null_ptr_ro() {
1372 // Should be safe to create a 0-len slice from any arbitrary
1373 // non-null pointer:
1374 let slice = unsafe {
1375 raw_processbuf_to_roprocessslice(
1376 // Under strict provenance, we cannot simply cast an arbitrary
1377 // integer into a pointer. However, with a zero-length process
1378 // slice, the pointer passed to this function must never be
1379 // dereferencable anyways. Thus we simply start from a
1380 // null-pointer, and derive another pointer from it (and its
1381 // provenance) at an offset.
1382 core::ptr::null_mut::<u8>().wrapping_byte_add(42),
1383 0,
1384 )
1385 };
1386 assert_eq!(slice.len(), 0);
1387 assert!(slice.get(0).is_none());
1388
1389 // Iteration should simply yield nothing
1390 let mut count = 0;
1391 for _ in slice.iter() {
1392 count += 1;
1393 }
1394 assert_eq!(count, 0);
1395
1396 // Slice should not retain its pointer, and return a non-null
1397 // (dangling) pointer instead:
1398 assert_eq!(
1399 slice as *const ReadableProcessSlice as *const u8,
1400 core::ptr::NonNull::<u8>::dangling().as_ptr()
1401 );
1402 }
1403
1404 #[test]
1405 fn test_zero_length_null_ptr_rw() {
1406 // Should be safe to create a 0-len slice from a null pointer
1407 let slice = unsafe { raw_processbuf_to_rwprocessslice(core::ptr::null_mut(), 0) };
1408 assert_eq!(slice.len(), 0);
1409 assert!(slice.get(0).is_none());
1410
1411 // Iteration should simply yield nothing
1412 let mut count = 0;
1413 for _ in slice.iter() {
1414 count += 1;
1415 }
1416 assert_eq!(count, 0);
1417
1418 // Slice should be created over a non-null pointer
1419 // (NonNull::dangling()):
1420 assert_eq!(
1421 slice as *const WriteableProcessSlice as *const u8,
1422 core::ptr::NonNull::<u8>::dangling().as_ptr(),
1423 );
1424 }
1425
1426 #[test]
1427 fn test_zero_length_non_null_ptr_rw() {
1428 // Should be safe to create a 0-len slice from any arbitrary
1429 // non-null pointer:
1430 let slice = unsafe {
1431 raw_processbuf_to_rwprocessslice(
1432 // Under strict provenance, we cannot simply cast an arbitrary
1433 // integer into a pointer. However, with a zero-length process
1434 // slice, the pointer passed to this function must never be
1435 // dereferencable anyways. Thus we simply start from a
1436 // null-pointer, and derive another pointer from it (and its
1437 // provenance) at an offset.
1438 core::ptr::null_mut::<u8>().wrapping_byte_add(42),
1439 0,
1440 )
1441 };
1442 assert_eq!(slice.len(), 0);
1443 assert!(slice.get(0).is_none());
1444
1445 // Iteration should simply yield nothing
1446 let mut count = 0;
1447 for _ in slice.iter() {
1448 count += 1;
1449 }
1450 assert_eq!(count, 0);
1451
1452 // Slice should not retain its pointer, and return a non-null
1453 // (dangling) pointer instead:
1454 assert_eq!(
1455 slice as *const WriteableProcessSlice as *const u8,
1456 core::ptr::NonNull::<u8>::dangling().as_ptr()
1457 );
1458 }
1459
1460 #[test]
1461 fn test_out_of_bounds_ro() {
1462 let memory = [const { UnsafeCell::new(0u8) }; 4];
1463 let ptr = get_backing_memory_ptr(&memory);
1464 let slice = unsafe { raw_processbuf_to_roprocessslice(ptr, 4) };
1465
1466 assert!(slice.get(3).is_some());
1467 assert!(slice.get(4).is_none());
1468 assert!(slice.get(100).is_none());
1469
1470 // Range OOB
1471 assert!(slice.get(2..5).is_none());
1472 }
1473
1474 #[test]
1475 #[should_panic(expected = "index out of bounds: the len is 4 but the index is 4")]
1476 fn test_out_of_bounds_panic_ro() {
1477 let memory = [const { UnsafeCell::new(0u8) }; 4];
1478 let ptr = get_backing_memory_ptr(&memory);
1479 let slice = unsafe { raw_processbuf_to_roprocessslice(ptr, 4) };
1480
1481 assert_eq!(slice[3].get(), 0);
1482
1483 // This is out of bounds and will panic:
1484 assert_eq!(slice[4].get(), 0);
1485 }
1486
1487 #[test]
1488 fn test_out_of_bounds_rw() {
1489 let memory = [const { UnsafeCell::new(0u8) }; 4];
1490 let ptr = get_backing_memory_ptr(&memory);
1491 let slice = unsafe { raw_processbuf_to_rwprocessslice(ptr, 4) };
1492
1493 assert!(slice.get(3).is_some());
1494 assert!(slice.get(4).is_none());
1495 assert!(slice.get(100).is_none());
1496
1497 // Range OOB
1498 assert!(slice.get(2..5).is_none());
1499 }
1500
1501 #[test]
1502 #[should_panic(expected = "index out of bounds: the len is 4 but the index is 4")]
1503 fn test_out_of_bounds_panic_rw() {
1504 let memory = [const { UnsafeCell::new(0u8) }; 4];
1505 let ptr = get_backing_memory_ptr(&memory);
1506 let slice = unsafe { raw_processbuf_to_rwprocessslice(ptr, 4) };
1507
1508 assert_eq!(slice[3].get(), 0);
1509
1510 // This is out of bounds and will panic:
1511 assert_eq!(slice[4].get(), 0);
1512 }
1513
1514 #[test]
1515 fn test_copy_logic() {
1516 let memory = [const { UnsafeCell::new(0u8) }; 4];
1517 let ptr = get_backing_memory_ptr(&memory);
1518 let src_data = [10, 20, 30, 40];
1519 let mut dst_data = [0u8; 4];
1520
1521 let slice = unsafe { raw_processbuf_to_rwprocessslice(ptr, 4) };
1522
1523 // Copy into slice
1524 slice.copy_from_slice(&src_data);
1525 assert_eq!(slice[0].get(), 10);
1526 assert_eq!(slice[3].get(), 40);
1527
1528 // Copy out of slice
1529 slice.copy_to_slice(&mut dst_data);
1530 assert_eq!(dst_data, src_data);
1531 }
1532
1533 #[test]
1534 #[should_panic(
1535 expected = "source slice length (4) does not match destination slice length (2)"
1536 )]
1537 fn test_copy_panic_len_mismatch() {
1538 let memory = [const { UnsafeCell::new(0u8) }; 4];
1539 let ptr = get_backing_memory_ptr(&memory);
1540 let mut small_dst = [0u8; 2];
1541
1542 let slice = unsafe { raw_processbuf_to_rwprocessslice(ptr, 4) };
1543 slice.copy_to_slice(&mut small_dst);
1544 }
1545
1546 #[test]
1547 fn test_transmute_from_immutable_slice() {
1548 // This test exercises the `From<&[u8]>` implementation for
1549 // ReadableProcessSlice.
1550 //
1551 // We take a standard, immutable Rust slice (`&[u8]`). This creates a
1552 // shared, read-only borrow of the stack memory. We then convert it
1553 // into a `&ReadableProcessSlice`. This struct wraps
1554 // `ReadableProcessByte`, which wraps `Cell<u8>`.
1555 //
1556 // This is problematic under stacked-borrows, as we are transmuting
1557 // `&[u8]` (immutable, noalias) to `&[Cell<u8>]` (shared, interior
1558 // mutability).
1559 //
1560 // Therefore, we expect the following results:
1561 //
1562 // - Stacked Borrows (Default Miri as of Jan 2026): FAIL.
1563 //
1564 // Stacked Borrows forbids "upgrading" a SharedReadOnly reference to
1565 // one that claims it can mutate (SharedReadWrite), even if we don't
1566 // actually write.
1567 //
1568 // - Tree Borrows (`-Zmiri-tree-borrows`): PASS.
1569 //
1570 // Tree Borrows is experimental and handles "retagging" differently.
1571 // It tolerates this transmute as long as we do not actually perform a
1572 // write operation through the Cell while the original data is frozen.
1573 //
1574 let data = [10u8, 20, 30, 40];
1575 let slice: &[u8] = &data;
1576
1577 // 1. Convert &u8 to &ReadableProcessSlice (which wraps Cell<u8>)
1578 let proc_slice: &ReadableProcessSlice = slice.into();
1579
1580 // 2. Read from it.
1581 //
1582 // Even though we only read, the type of `proc_slice` implies the
1583 // *capability* to mutate, which contradicts the provenance of `slice`.
1584 assert_eq!(proc_slice[0].get(), 10);
1585 assert_eq!(proc_slice[3].get(), 40);
1586 }
1587}