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