wasmtime/runtime/vm/
provenance.rs

1//! Helpers related to pointer provenance for Wasmtime and its runtime.
2//!
3//! This module encapsulates the efforts and lengths that Wasmtime goes to in
4//! order to properly respect pointer provenance in Rust with respect to unsafe
5//! code. Wasmtime has a nontrivial amount of `unsafe` code and when/where
6//! pointers are valid is something we need to be particularly careful about.
7//! All safe Rust does not need to worry about this module and only the unsafe
8//! runtime bits need to worry about it.
9//!
10//! In general Wasmtime does not work with Rust's strict pointer provenance
11//! rules. The primary reason for this is that Cranelift does not have the
12//! concept of a pointer type meaning that backends cannot know what values are
13//! pointers and what aren't. This isn't a huge issue for ISAs like x64 but for
14//! an ISA like Pulley Bytecode it means that the Pulley interpreter cannot
15//! respect strict provenance.
16//!
17//! > **Aside**: an example of how Pulley can't respect pointer provenance is
18//! > consider a wasm load. The wasm load will add a wasm address to the base
19//! > address of the host. In this situation what actually needs to happen is
20//! > that the base address of the host is a pointer which is byte-offset'd by
21//! > the wasm address. Cranelift IR has no knowledge of which value is
22//! > the wasm address and which is the host address. This means that Cranelift
23//! > can freely commute the operands of the addition. This means that when
24//! > executing Pulley doesn't know which values are addresses and which aren't.
25//!
26//! This isn't the end of the world for Wasmtime, however, it just means that
27//! when we run in MIRI we are restricted to "permissive provenance" or "exposed
28//! provenance". The tl;dr; of exposed provenance is that at certain points we
29//! declare a pointer as "this is now exposed". That converts a pointer to the
30//! `usize` address and then semantically (just for rustc/llvm mostly) indicates
31//! that the provenance of the pointer is added to a global list of provenances.
32//! Later on Wasmtime will execute an operation to convert a `usize` back into a
33//! pointer which will pick "the most appropriate provenance" from said global
34//! list of provenances.
35//!
36//! In practice we expect that at runtime all of these provenance-related ops
37//! are noops and compile away to nothing. The only practical effect that's
38//! expected is that some optimizations may be hindered in LLVM occasionally or
39//! something like that which is by-and-large what we want to happen. Note that
40//! another practical consequence of not working with "strict provenance" means
41//! that Wasmtime is incompatible with platforms such as CHERI where exposed
42//! provenance is not available.
43
44use crate::vm::SendSyncPtr;
45use core::fmt;
46use core::marker;
47use core::num::NonZeroUsize;
48use core::ptr::NonNull;
49use core::sync::atomic::AtomicUsize;
50use wasmtime_environ::VMSharedTypeIndex;
51
52/// A pointer that is used by compiled code, or in other words is accessed
53/// outside of Rust.
54///
55/// This is intended to be the fundamental data type used to share
56/// pointers-to-things with compiled wasm compiled code for example. An example
57/// of this is that the `VMMemoryDefinition` type, which compiled code reads to
58/// learn about linear memory, uses a `VmPtr<u8>` to represent the base pointer
59/// of linear memory.
60///
61/// This type is pointer-sized and typed-like-a-pointer. This is additionally
62/// like a `NonNull<T>` in that it's never a null pointer (and
63/// `Option<VmPtr<T>>` is pointer-sized). This pointer auto-infers
64/// `Send` and `Sync` based on `T`. Note the lack of `T: ?Sized` bounds in this
65/// type additionally, meaning that it only works with sized types. That's
66/// intentional as compiled code should not be interacting with dynamically
67/// sized types in Rust.
68///
69/// This type serves two major purposes with respect to provenance and safety:
70///
71/// * Primarily this type is the only pointer type that implements `VmSafe`, the
72///   marker trait below. That forces all pointers shared with compiled code to
73///   use this type.
74///
75/// * This type represents a pointer with "exposed provenance". Once a value of
76///   this type is created the original pointer's provenance will be marked as
77///   exposed. This operation may hinder optimizations around the use of said
78///   pointer in that case.
79///
80/// This type is expected to be used not only when sending pointers to compiled
81/// code (e.g. `VMContext`) but additionally for any data at rest which shares
82/// pointers with compiled code (for example the base of linear memory or
83/// pointers stored within `VMContext` itself).
84///
85/// In general usage of this type should be minimized to only where absolutely
86/// necessary when sharing data structures with compiled code. Prefer to use
87/// `NonNull` or `SendSyncPtr` where possible.
88#[repr(transparent)]
89pub struct VmPtr<T> {
90    ptr: NonZeroUsize,
91    _marker: marker::PhantomData<SendSyncPtr<T>>,
92}
93
94impl<T> VmPtr<T> {
95    /// View this pointer as a [`SendSyncPtr<T>`].
96    ///
97    /// This operation will convert the storage at-rest to a native pointer on
98    /// the host. This is effectively an integer-to-pointer operation which will
99    /// assume that the original pointer's provenance was previously exposed.
100    /// In typical operation this means that Wasmtime will initialize data
101    /// structures by creating an instance of `VmPtr`, exposing provenance.
102    /// Later on this type will be handed back to Wasmtime or read from its
103    /// location at-rest in which case provenance will be "re-acquired".
104    pub fn as_send_sync(&self) -> SendSyncPtr<T> {
105        SendSyncPtr::from(self.as_non_null())
106    }
107
108    /// Similar to `as_send_sync`, but returns a `NonNull<T>`.
109    pub fn as_non_null(&self) -> NonNull<T> {
110        #[cfg(has_provenance_apis)]
111        let ptr = core::ptr::with_exposed_provenance_mut(self.ptr.get());
112        #[cfg(not(has_provenance_apis))]
113        let ptr = self.ptr.get() as *mut T;
114
115        unsafe { NonNull::new_unchecked(ptr) }
116    }
117
118    /// Similar to `as_send_sync`, but returns a `*mut T`.
119    pub fn as_ptr(&self) -> *mut T {
120        self.as_non_null().as_ptr()
121    }
122}
123
124// `VmPtr<T>`, like raw pointers, is trivially `Clone`/`Copy`.
125impl<T> Clone for VmPtr<T> {
126    fn clone(&self) -> VmPtr<T> {
127        *self
128    }
129}
130
131impl<T> Copy for VmPtr<T> {}
132
133// Forward debugging to `SendSyncPtr<T>` which renders the address.
134impl<T> fmt::Debug for VmPtr<T> {
135    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
136        self.as_send_sync().fmt(f)
137    }
138}
139
140// Constructor from `NonNull<T>`
141impl<T> From<NonNull<T>> for VmPtr<T> {
142    fn from(ptr: NonNull<T>) -> VmPtr<T> {
143        VmPtr {
144            #[cfg(has_provenance_apis)]
145            ptr: unsafe { NonZeroUsize::new_unchecked(ptr.as_ptr().expose_provenance()) },
146            #[cfg(not(has_provenance_apis))]
147            ptr: unsafe { NonZeroUsize::new_unchecked(ptr.as_ptr() as usize) },
148            _marker: marker::PhantomData,
149        }
150    }
151}
152
153// Constructor from `SendSyncPtr<T>`
154impl<T> From<SendSyncPtr<T>> for VmPtr<T> {
155    fn from(ptr: SendSyncPtr<T>) -> VmPtr<T> {
156        ptr.as_non_null().into()
157    }
158}
159
160/// A custom "marker trait" used to tag types that are safe to share with
161/// compiled wasm code.
162///
163/// The intention of this trait is to be used as a bound in a few core locations
164/// in Wasmtime, such as `Instance::vmctx_plus_offset_mut`, and otherwise not
165/// present very often. The purpose of this trait is to ensure that all types
166/// stored to be shared with compiled code have a known layout and are
167/// guaranteed to be "safe" to share with compiled wasm code.
168///
169/// This is an `unsafe` trait as it's generally not safe to share anything with
170/// compiled code and it is used to invite extra scrutiny to manual `impl`s of
171/// this trait. Types which implement this marker trait must satisfy at least
172/// the following requirements.
173///
174/// * The ABI of `Self` must be well-known and defined. This means that the type
175///   can interoperate with compiled code. For example `u8` is well defined as
176///   is a `#[repr(C)]` structure. Types lacking `#[repr(C)]` or other types
177///   like Rust tuples do not satisfy this requirement.
178///
179/// * For types which contain pointers the pointer's provenance is guaranteed to
180///   have been exposed when the type is constructed. This is satisfied where
181///   the only pointer that implements this trait is `VmPtr<T>` above which is
182///   explicitly used to indicate exposed provenance. Notably `*mut T` and
183///   `NonNull<T>` do not implement this trait, and intentionally so.
184///
185/// * For composite structures (e.g. `struct`s in Rust) all member fields must
186///   satisfy the above criteria. All fields must have defined layouts and
187///   pointers must be `VmPtr<T>`.
188///
189/// * Newtype or wrapper types around primitives that are used by value must be
190///   `#[repr(transparent)]` to ensure they aren't considered aggregates by the
191///   compile to match the ABI of the primitive type.
192///
193/// In this module a number of impls are provided for the primitives of Rust,
194/// for example integers. Additionally some basic pointer-related impls are
195/// provided for `VmPtr<T>` above. More impls can be found in `vmcontext.rs`
196/// where there are manual impls for all `VM*` data structures which are shared
197/// with compiled code.
198pub unsafe trait VmSafe {}
199
200// Implementations for primitive types. Note that atomics are included here as
201// some atomic values are shared with compiled code. Rust's atomics are
202// guaranteed to have the same memory representation as their primitive.
203unsafe impl VmSafe for u8 {}
204unsafe impl VmSafe for u16 {}
205unsafe impl VmSafe for u32 {}
206unsafe impl VmSafe for u64 {}
207unsafe impl VmSafe for u128 {}
208unsafe impl VmSafe for usize {}
209unsafe impl VmSafe for i8 {}
210unsafe impl VmSafe for i16 {}
211unsafe impl VmSafe for i32 {}
212unsafe impl VmSafe for i64 {}
213unsafe impl VmSafe for i128 {}
214unsafe impl VmSafe for isize {}
215unsafe impl VmSafe for AtomicUsize {}
216#[cfg(target_has_atomic = "64")]
217unsafe impl VmSafe for core::sync::atomic::AtomicU64 {}
218
219// This is a small `u32` wrapper defined in `wasmtime-environ`, so impl the
220// vm-safe-ness here.
221unsafe impl VmSafe for VMSharedTypeIndex {}
222
223// Core implementations for `VmPtr`. Notably `VMPtr<T>` requires that `T` also
224// implements `VmSafe`. Additionally an `Option` wrapper is allowed as that's
225// just a nullable pointer.
226unsafe impl<T: VmSafe> VmSafe for VmPtr<T> {}
227unsafe impl<T: VmSafe> VmSafe for Option<VmPtr<T>> {}