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