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        let ptr = core::ptr::with_exposed_provenance_mut(self.ptr.get());
111        unsafe { NonNull::new_unchecked(ptr) }
112    }
113
114    /// Similar to `as_send_sync`, but returns a `*mut T`.
115    pub fn as_ptr(&self) -> *mut T {
116        self.as_non_null().as_ptr()
117    }
118}
119
120// `VmPtr<T>`, like raw pointers, is trivially `Clone`/`Copy`.
121impl<T> Clone for VmPtr<T> {
122    fn clone(&self) -> VmPtr<T> {
123        *self
124    }
125}
126
127impl<T> Copy for VmPtr<T> {}
128
129// Forward debugging to `SendSyncPtr<T>` which renders the address.
130impl<T> fmt::Debug for VmPtr<T> {
131    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
132        self.as_send_sync().fmt(f)
133    }
134}
135
136// Constructor from `NonNull<T>`
137impl<T> From<NonNull<T>> for VmPtr<T> {
138    fn from(ptr: NonNull<T>) -> VmPtr<T> {
139        VmPtr {
140            ptr: unsafe { NonZeroUsize::new_unchecked(ptr.as_ptr().expose_provenance()) },
141            _marker: marker::PhantomData,
142        }
143    }
144}
145
146// Constructor from `SendSyncPtr<T>`
147impl<T> From<SendSyncPtr<T>> for VmPtr<T> {
148    fn from(ptr: SendSyncPtr<T>) -> VmPtr<T> {
149        ptr.as_non_null().into()
150    }
151}
152
153/// A custom "marker trait" used to tag types that are safe to share with
154/// compiled wasm code.
155///
156/// The intention of this trait is to be used as a bound in a few core locations
157/// in Wasmtime, such as `Instance::vmctx_plus_offset_mut`, and otherwise not
158/// present very often. The purpose of this trait is to ensure that all types
159/// stored to be shared with compiled code have a known layout and are
160/// guaranteed to be "safe" to share with compiled wasm code.
161///
162/// This is an `unsafe` trait as it's generally not safe to share anything with
163/// compiled code and it is used to invite extra scrutiny to manual `impl`s of
164/// this trait. Types which implement this marker trait must satisfy at least
165/// the following requirements.
166///
167/// * The ABI of `Self` must be well-known and defined. This means that the type
168///   can interoperate with compiled code. For example `u8` is well defined as
169///   is a `#[repr(C)]` structure. Types lacking `#[repr(C)]` or other types
170///   like Rust tuples do not satisfy this requirement.
171///
172/// * For types which contain pointers the pointer's provenance is guaranteed to
173///   have been exposed when the type is constructed. This is satisfied where
174///   the only pointer that implements this trait is `VmPtr<T>` above which is
175///   explicitly used to indicate exposed provenance. Notably `*mut T` and
176///   `NonNull<T>` do not implement this trait, and intentionally so.
177///
178/// * For composite structures (e.g. `struct`s in Rust) all member fields must
179///   satisfy the above criteria. All fields must have defined layouts and
180///   pointers must be `VmPtr<T>`.
181///
182/// * Newtype or wrapper types around primitives that are used by value must be
183///   `#[repr(transparent)]` to ensure they aren't considered aggregates by the
184///   compile to match the ABI of the primitive type.
185///
186/// In this module a number of impls are provided for the primitives of Rust,
187/// for example integers. Additionally some basic pointer-related impls are
188/// provided for `VmPtr<T>` above. More impls can be found in `vmcontext.rs`
189/// where there are manual impls for all `VM*` data structures which are shared
190/// with compiled code.
191pub unsafe trait VmSafe {}
192
193// Implementations for primitive types. Note that atomics are included here as
194// some atomic values are shared with compiled code. Rust's atomics are
195// guaranteed to have the same memory representation as their primitive.
196unsafe impl VmSafe for u8 {}
197unsafe impl VmSafe for u16 {}
198unsafe impl VmSafe for u32 {}
199unsafe impl VmSafe for u64 {}
200unsafe impl VmSafe for u128 {}
201unsafe impl VmSafe for usize {}
202unsafe impl VmSafe for i8 {}
203unsafe impl VmSafe for i16 {}
204unsafe impl VmSafe for i32 {}
205unsafe impl VmSafe for i64 {}
206unsafe impl VmSafe for i128 {}
207unsafe impl VmSafe for isize {}
208unsafe impl VmSafe for AtomicUsize {}
209#[cfg(target_has_atomic = "64")]
210unsafe impl VmSafe for core::sync::atomic::AtomicU64 {}
211
212// This is a small `u32` wrapper defined in `wasmtime-environ`, so impl the
213// vm-safe-ness here.
214unsafe impl VmSafe for VMSharedTypeIndex {}
215
216// Core implementations for `VmPtr`. Notably `VMPtr<T>` requires that `T` also
217// implements `VmSafe`. Additionally an `Option` wrapper is allowed as that's
218// just a nullable pointer.
219unsafe impl<T: VmSafe> VmSafe for VmPtr<T> {}
220unsafe impl<T: VmSafe> VmSafe for Option<VmPtr<T>> {}