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