guard_trait/
lib.rs

1//! This crate provides a guarding mechanism for memory, with an interface that is in some ways
2//! similar to `core::pin::Pin`.
3//!
4//! # Motivation
5//!
6//! What this crate attempts to solve, is the problem that data races can occur for memory that is
7//! shared with another process or the kernel (via `io_uring` for instance). If the memory is still
8//! shared when the original thread continues to execute after a system call for example, the
9//! original buffer can still be accessed while the system call allows the handler to keep using
10//! the memory. This does not happen with traditional blocking syscalls; the kernel will only
11//! access the memory during the syscall, when the process cannot temporarily do anything else.
12//!
13//! However, for more advanced asynchronous interfaces such as `io_uring` that never copy memory,
14//! the memory can still be used once the system call has started, which can lead to data races
15//! between two actors sharing memory. To prevent this data race, it is not possible to:
16//!
17//! 1) Read the memory while it is being written to by the kernel, or write to the memory while it
18//!    is being read by the kernel. This is exactly like Rust's aliasing rules: we can either allow
19//!    both the kernel and this process to read a buffer, for example in the system call
20//!    _write(2)_, we can temporarily give the kernel exclusive ownership one or more buffers when
21//!    the kernel is going to write to them, or we can avoid sharing memory at all with the kernel,
22//!    __but we cannot let either actor have mutable access while the other has any access at
23//!    all.__ (_aliasing invariant_)
24//! 2) Reclaim the memory while it is being read from or written to by the kernel. This is as
25//!    simple as it sounds: we simply do not want the buffers to be used for other purposes, either
26//!    by returning the memory to the heap, where it can be allocated simply so that the kernel can
27//!    overwrite it when it is not supposed to, or it can corrupt stack variables. (_reclamation
28//!    invariant_)
29//!
30//! The term "kernel" does not necessarily have to be the other actor that the memory is shared
31//! with; on Redox for example, the `io_uring` interface can work solely between regular userspace
32//! processes. Additionally, although being a somewhat niche case, this can also be used for safe
33//! wrappers protecting memory for DMA in device drivers, with a few additional restrictions
34//! (regarding cache coherency) to make that work.
35//!
36//! This buffer sharing logic does unfortunately not play very well with the current asynchronous
37//! ecosystem, where almost all I/O is done using regular borrowed slices, and references are
38//! merely borrows which are cancellable _at any time_, even by leaking. This functions perfectly
39//! when you use _synchronous_ (but non-blocking) system calls where either the process or the
40//! kernel can execute at a time. In contrast, `io_uring` is _asynchronous_, meaning that the
41//! kernel can read and write to buffers, _while our program is executing_. Therefore, a future
42//! that locally stores an array, aliased by the kernel in `io_uring`, cannot stop the kernel from
43//! using the memory again in any reasonable way, if the future were to be `Drop`ped, without
44//! blocking indefinitely. What is even worse, is that futures can be leaked at any time, and
45//! arrays allocated on the stack can also be dropped, when the memory is still in use by the
46//! kernel, as a buffer to write data from e.g. a socket. If a (mutable) buffer on the stack is
47//! then reused later for regular variables... arbitrary program corruption!
48//!
49//! What we need in order to solve these two complications, is some way to be able to mark a memory
50//! region as both "borrowed by the kernel" (mutably or immutably), and "undroppable". Since the
51//! Rust borrow checker is smart, any mutable reference with a lifetime that is shorter than
52//! `'static`, can trivially be leaked, and the pointer can be used again. This rules out any
53//! reference of lifetime `'a` that `'static` outlives, as those may be used again outside of the
54//! borrow, potentially mutably. Immutable static references are however completely harmless, since
55//! they cannot be dropped nor accessed mutably, and immutable aliasing is always permitted.
56//!
57//! Consequently, all buffers that are going to be used in safe code, must be owned. This either
58//! means heap-allocated objects (since we can assume that the heap as a whole has the `'static`
59//! lifetime, and allocations stay forever, until deallocated explicitly), buffer pools which
60//! themselves have a guarding mechanism, and static references (both mutable and immutable). We
61//! can however allow borrowed data as well, but because of the semantics around lifetimes, and the
62//! very fact that the compiler has no idea that the kernel is also involved, that requires unsafe
63//! code.
64//!
65//! Consider reading ["Mental experiments with
66//! `io_uring`"](https://vorner.github.io/2019/11/03/io-uring-mental-experiments.html), and ["Notes
67//! on `io-uring`"](https://without.boats/blog/io-uring/) for more information about these
68//! challenges.
69//!
70//! # Interface
71//!
72//! The way `guard_trait` solves this, is by adding two simple traits: `Guarded` and `GuardedMut`.
73//! `Guarded` is automatically implemented for every pointer type that implements `Deref`,
74//! `StableDeref` and `'static`. Similarly, `GuardedMut` is implemented under the same conditions,
75//! and provided that the pointer implements `DerefMut`. A consequence of this, is that nearly all
76//! owned container types, such as `Arc`, `Box`, `Vec`, etc., all implement the traits, and can
77//! thus be used with completion-based interfaces.
78//!
79//! For scenarios where it is impossible to ensure at the type level, that a certain pointer
80//! follows the guard invariants, `AssertSafe` also exists, but is unsafe to initialize.
81//!
82//! Buffers can also be mapped in a self-referencial way, similar to how `owning-ref` works, using
83//! `GuardedExt::map` and `GuardedMutExt::map_mut`. This is especially important when slice
84//! indexing is needed, as the only way to limit the number of bytes to do I/O with, generally is
85//! to shorten the slice.
86
87#![deny(broken_intra_doc_links, missing_docs)]
88#![cfg_attr(not(any(test, feature = "std")), no_std)]
89
90use core::marker::PhantomData;
91use core::ptr::NonNull;
92use core::{fmt, ops};
93
94// TODO: Perhaps consider basing everything on the Borrow trait, with StableBorrow.
95pub extern crate stable_deref_trait;
96pub use stable_deref_trait::StableDeref;
97
98/// A trait for pointer types that uphold the guard invariants, namely that the pointer must be
99/// owned, and that it must dereference into a stable address.
100pub unsafe trait Guarded {
101    /// The target pointee that this pointer may dereference into. There are no real restrictions
102    /// to what this type can be. However, the user must not assume that simply because a `&Target`
103    /// reference is protected, that references indirectly derived (via `Deref` and other traits)
104    /// would also be protected.
105    type Target: ?Sized;
106
107    /// Borrow the pointee, into a fixed reference that can be sent directly and safely to e.g.
108    /// memory-sharing completion-based I/O interfaces.
109    ///
110    /// Implementors of such interfaces must however take buffers by reference to maintain safety.
111    fn borrow_guarded(&self) -> &Self::Target;
112}
113/// A trait for pointer types that uphold the guard invariants, and are able to dereference
114/// mutably.
115pub unsafe trait GuardedMut: Guarded {
116    /// Borrow the pointee mutably, into a fixed reference that can be sent directly and safely to
117    /// e.g. memory-sharing completion-based I/O interfaces.
118    ///
119    /// Implementors of such interfaces must however take buffers by reference to maintain safety.
120    fn borrow_guarded_mut(&mut self) -> &mut Self::Target;
121}
122
123unsafe impl<T, U> Guarded for T
124where
125    T: ops::Deref<Target = U> + StableDeref + 'static,
126    U: ?Sized,
127{
128    type Target = U;
129
130    #[inline]
131    fn borrow_guarded(&self) -> &U {
132        &*self
133    }
134}
135unsafe impl<T, U> GuardedMut for T
136where
137    T: ops::DerefMut<Target = U> + StableDeref + 'static,
138    U: ?Sized,
139{
140    #[inline]
141    fn borrow_guarded_mut(&mut self) -> &mut U {
142        &mut *self
143    }
144}
145
146/// A type for pointers that cannot uphold the necessary guard invariants at the type level, but
147/// which can be assumed to behave properly by unsafe code.
148#[repr(transparent)]
149#[derive(Debug)]
150pub struct AssertSafe<T> {
151    inner: T,
152}
153
154impl<T> AssertSafe<T>
155where
156    T: ops::Deref,
157{
158    /// Wrap a general-purpose pointer into a wrapper that implements the `Guarded` (and
159    /// potentially `GuardedMut`) traits, provided that the pointer upholds this invariants anyway.
160    ///
161    /// # Safety
162    ///
163    /// For the guard invariants to be upheld, the pointer must:
164    ///
165    /// * dereference into a stable location. This forbids types that implement `Deref` by
166    ///   borrowing data that they own without a heap-based container in between;
167    /// * be _owned_. Any type that has a shorter lifetime than `'static`, may have its borrow
168    ///   cancelled _at any time_, with the original borrowed data accessible again.
169    #[inline]
170    pub unsafe fn new_unchecked(inner: T) -> Self {
171        Self { inner }
172    }
173}
174unsafe impl<T, U> Guarded for AssertSafe<T>
175where
176    T: ops::Deref<Target = U>,
177    U: ?Sized,
178{
179    type Target = U;
180
181    #[inline]
182    fn borrow_guarded(&self) -> &Self::Target {
183        &*self.inner
184    }
185}
186unsafe impl<T, U> GuardedMut for AssertSafe<T>
187where
188    T: ops::Deref<Target = U> + ops::DerefMut,
189    U: ?Sized,
190{
191    #[inline]
192    fn borrow_guarded_mut(&mut self) -> &mut Self::Target {
193        &mut *self.inner
194    }
195}
196
197/// A mapped guard, which contains a guarded owned pointer, and an immutable reference to that
198/// pointer. It has had a one-time closure applied to it, but only the _output_ of the closure is
199/// stored, not the closure itself. This is similar to how crates like `owning_ref` work.
200pub struct Mapped<T, U>
201where
202    T: Guarded,
203    U: ?Sized,
204{
205    // NOTE: It is important to make the distinction between the pointer and the pointee here. T is
206    // a pointer type in this context, which is guaranteed to dereference into a stable address.
207    // However, unlike with MappedMut, this can be trivially dereferenced at any time, since
208    // immutable references allow multiple aliases.
209    inner: T,
210    // NOTE: NonNull gives us a covariant non-null pointer, which is valid for immutable
211    // references. We cannot overwrite a subtype with a supertype without mutable access.
212    mapped: NonNull<U>,
213}
214impl<T, U> Mapped<T, U>
215where
216    T: Guarded,
217    U: ?Sized,
218{
219    /// Move the inner guarded pointer out from the `Mapped` wrapper, cancelling the temporary
220    /// borrow.
221    #[inline]
222    pub fn into_original(self) -> T {
223        self.inner
224    }
225    /// Get the original pointer, by immutable reference.
226    #[inline]
227    pub fn original_by_ref(&self) -> &T {
228        &self.inner
229    }
230    /// Retrieve an immutable reference to the mapped data.
231    #[inline]
232    pub fn get_ref(&self) -> &U {
233        unsafe { self.mapped.as_ref() }
234    }
235    /// Map the mapped wrapper again, converting `&U` to `&V`.
236    #[inline]
237    pub fn and_then<F, V>(self, f: F) -> Mapped<T, V>
238    where
239        F: FnOnce(&U) -> &V,
240        V: ?Sized,
241    {
242        Mapped {
243            mapped: f(self.get_ref()).into(),
244            inner: self.inner,
245        }
246    }
247    /// Attempt to map the reference again, but with the ability to short-circuit on errors.
248    #[inline]
249    pub fn try_and_then<F, V, E>(self, f: F) -> Result<Mapped<T, V>, E>
250    where
251        F: FnOnce(&U) -> Result<&V, E>,
252        V: ?Sized,
253    {
254        Ok(Mapped {
255            mapped: f(self.get_ref())?.into(),
256            inner: self.inner,
257        })
258    }
259}
260impl<T, U> fmt::Debug for Mapped<T, U>
261where
262    T: Guarded,
263    U: ?Sized + fmt::Debug,
264{
265    #[cold]
266    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
267        f.debug_tuple("Mapped").field(&self.get_ref()).finish()
268    }
269}
270
271unsafe impl<T, U> Guarded for Mapped<T, U>
272where
273    T: Guarded,
274    U: ?Sized,
275{
276    type Target = U;
277
278    #[inline]
279    fn borrow_guarded(&self) -> &Self::Target {
280        self.get_ref()
281    }
282}
283
284/// A mapped guard, which contains a guarded owned pointer, and a mutable reference to that
285/// pointer. It has had a one-time closure applied to it, but only the _output_ of the closure is
286/// stored, not the closure itself. This is similar to how crates like `owning_ref` work.
287pub struct MappedMut<T, U>
288where
289    T: GuardedMut,
290    U: ?Sized,
291{
292    // NOTE: While we are allowed to take a reference to self, which in turn contains inner, as
293    // well as inner itself, but we are not allowed to create a reference directly to the pointee
294    // inner, since that would violate the no-aliasing rule enforced by Rust's mutable references.
295    inner: T,
296    mapped: NonNull<U>,
297
298    // NOTE: We need to make this invariant, since we have a mutable reference, together with the
299    // data that it points to. If it were to be covariant, then MappedMut<Subtype, Subtype>
300    // would be trivially castable to MappedMut<Subtype, Supertype>. This is a problem since the
301    // reference is mutable, allowing Subtype to be replaced with Supertype.
302    //
303    // The regular Mapped wrapper only deals with immutable references, and is thus covariant.
304    //
305    // See [the nomicon](https://doc.rust-lang.org/nomicon/subtyping.html).
306    _invariance: PhantomData<*mut U>,
307}
308
309impl<T, U> MappedMut<T, U>
310where
311    T: GuardedMut,
312    U: ?Sized,
313{
314    /// Move out the original pointer from the mapped guard, hence cancelling the temporary borrow.
315    #[inline]
316    pub fn into_original(self) -> T {
317        self.inner
318    }
319    /// Convert `MappedMut<T, U>` to `Mapped<T, U>`.
320    #[inline]
321    pub fn into_immutable(this: MappedMut<T, U>) -> Mapped<T, U> {
322        Mapped {
323            inner: this.inner,
324            mapped: this.mapped,
325        }
326    }
327    /// Get an immutable reference to the mapped data.
328    #[inline]
329    pub fn get_ref(&self) -> &U {
330        unsafe { self.mapped.as_ref() }
331    }
332    /// Get a mutable reference to the mapped data.
333    #[inline]
334    pub fn get_mut(&mut self) -> &mut U {
335        unsafe { self.mapped.as_mut() }
336    }
337    /// Map the mapped reference again, converting `&mut U` to `&mut V`.
338    #[inline]
339    pub fn and_then<F, V>(mut self, f: F) -> MappedMut<T, V>
340    where
341        F: FnOnce(&mut U) -> &mut V,
342        V: ?Sized,
343    {
344        MappedMut {
345            mapped: f(self.get_mut()).into(),
346            inner: self.inner,
347
348            _invariance: PhantomData,
349        }
350    }
351
352    /// Attempt to map the reference again, but with the ability to short-circuit on errors.
353    #[inline]
354    pub fn try_and_then<F, V, E>(mut self, f: F) -> Result<MappedMut<T, V>, E>
355    where
356        F: FnOnce(&mut U) -> Result<&mut V, E>,
357        V: ?Sized,
358    {
359        Ok(MappedMut {
360            mapped: f(self.get_mut())?.into(),
361            inner: self.inner,
362
363            _invariance: PhantomData,
364        })
365    }
366}
367
368unsafe impl<T, U> Guarded for MappedMut<T, U>
369where
370    T: GuardedMut,
371    U: ?Sized,
372{
373    type Target = U;
374
375    #[inline]
376    fn borrow_guarded(&self) -> &Self::Target {
377        self.get_ref()
378    }
379}
380unsafe impl<T, U> GuardedMut for MappedMut<T, U>
381where
382    T: GuardedMut,
383    U: ?Sized,
384{
385    #[inline]
386    fn borrow_guarded_mut(&mut self) -> &mut Self::Target {
387        self.get_mut()
388    }
389}
390
391// TODO: Dynamically tracked "anchors" that allow referenced with lifetimes, with the cost of some
392// extra runtime tracking, to prevent dropped or leaked referenced from doing harm. This would be
393// similar to what the old API did.
394
395// TODO: Support mapped types that map into a borrowing _object_, rather than a plain reference.
396// It's not entirely clear what owning_ref does wrong there, but consider [this
397// issue](https://github.com/Kimundi/owning-ref-rs/issues/49).
398
399mod private {
400    pub trait Sealed {}
401}
402/// An extension trait for convenience methods, that is automatically implemented for all
403/// [`Guarded`] types.
404pub trait GuardedExt: private::Sealed + Guarded + Sized {
405    /// Apply a function to the pointee, creating a new guarded type that dereferences into the
406    /// result of that function.
407    ///
408    /// The closure is only evaluated once, and the resulting wrapper will only store one
409    /// null-optimizable additional word, for the reference.
410    #[inline]
411    fn map<F, T>(this: Self, f: F) -> Mapped<Self, T>
412    where
413        F: FnOnce(&<Self as Guarded>::Target) -> &T,
414        T: ?Sized,
415    {
416        Mapped {
417            mapped: f(this.borrow_guarded()).into(),
418            inner: this,
419        }
420    }
421    /// Apply a fallible function to the pointee, creating a new guarded type that dereferences
422    /// into the result of that function.
423    ///
424    /// If the function fails, the error is returned directly, and no further mapping is made.
425    #[inline]
426    fn try_map<F, T, E>(this: Self, f: F) -> Result<Mapped<Self, T>, E>
427    where
428        F: FnOnce(&<Self as Guarded>::Target) -> Result<&T, E>,
429        T: ?Sized,
430    {
431        Ok(Mapped {
432            mapped: f(this.borrow_guarded())?.into(),
433            inner: this,
434        })
435    }
436}
437/// An extension trait for convenience methods, that is automatically implemented for all
438/// [`GuardedMut`] types.
439pub trait GuardedMutExt: private::Sealed + GuardedMut + Sized {
440    /// Apply a function to the pointee, creating a new guarded type that dereferences into the
441    /// result of that function.
442    ///
443    /// This is the mutable version of [`GuardedExt::map`]. Because of this mutability, the
444    /// original pointer cannot be accessed until it is completely moved out of the wrapper.
445    #[inline]
446    fn map_mut<F, T>(mut this: Self, f: F) -> MappedMut<Self, T>
447    where
448        F: FnOnce(&mut <Self as Guarded>::Target) -> &mut T,
449        T: ?Sized,
450    {
451        MappedMut {
452            mapped: f(this.borrow_guarded_mut()).into(),
453            inner: this,
454            _invariance: PhantomData,
455        }
456    }
457    /// Apply a fallible function to the pointee, creating a new guarded type that dereferences
458    /// into the result of that function.
459    ///
460    /// If the function fails, the error is returned directly, and no further mapping is made.
461    #[inline]
462    fn try_map_mut<F, T, E>(mut this: Self, f: F) -> Result<MappedMut<Self, T>, E>
463    where
464        F: FnOnce(&mut <Self as Guarded>::Target) -> Result<&mut T, E>,
465        T: ?Sized,
466    {
467        Ok(MappedMut {
468            mapped: f(this.borrow_guarded_mut())?.into(),
469            inner: this,
470            _invariance: PhantomData,
471        })
472    }
473}
474impl<T> private::Sealed for T
475where
476    T: Guarded + Sized,
477{
478}
479impl<T> GuardedExt for T
480where
481    T: Guarded + Sized,
482{
483}
484impl<T> GuardedMutExt for T
485where
486    T: GuardedMut + Sized,
487{
488}
489
490// TODO: Perhaps an additional extension trait that allows indexing and slicing pointers to slice
491// types?
492
493#[cfg(test)]
494mod tests {
495    use super::*;
496
497    #[test]
498    fn basic_types_implement_guarded() {
499        use std::rc::Rc;
500        use std::sync::Arc;
501
502        fn does_impl_guarded<T: Guarded>() {}
503        fn does_impl_guarded_mut<T: GuardedMut>() {}
504
505        does_impl_guarded::<Arc<[u8]>>();
506        does_impl_guarded::<Rc<[u8]>>();
507        does_impl_guarded::<Box<[u8]>>();
508        does_impl_guarded::<Vec<u8>>();
509        does_impl_guarded::<&'static [u8]>();
510        does_impl_guarded::<&'static mut [u8]>();
511        does_impl_guarded::<&'static str>();
512        does_impl_guarded::<String>();
513
514        does_impl_guarded_mut::<Box<[u8]>>();
515        does_impl_guarded_mut::<Vec<u8>>();
516        does_impl_guarded::<&'static mut [u8]>();
517    }
518
519    #[test]
520    fn mapped() {
521        let mut buf = vec! [0x00_u8; 256];
522
523        let sub_buf = GuardedMutExt::map_mut(buf, |buf: &mut [u8]| -> &mut [u8] {
524            &mut buf[128..]
525        });
526        let mut subsub_buf = sub_buf.and_then(|buf| &mut buf[..64]);
527
528        for byte in subsub_buf.borrow_guarded_mut() {
529            *byte = 0xFF;
530        }
531        buf = subsub_buf.into_original();
532
533        assert!(buf[..128].iter().copied().all(|byte| byte == 0x00));
534        assert!(buf[128..192].iter().copied().all(|byte| byte == 0xFF));
535        assert!(buf[192..].iter().copied().all(|byte| byte == 0x00));
536    }
537    // TODO: try_and_then, etc
538}