1 use crate::{GuestError, GuestMemory, GuestPtr}; 2 use std::cell::UnsafeCell; 3 use std::mem; 4 use std::sync::atomic::{ 5 AtomicI8, AtomicI16, AtomicI32, AtomicI64, AtomicU8, AtomicU16, AtomicU32, AtomicU64, Ordering, 6 }; 7 8 /// A trait for types which are used to report errors. Each type used in the 9 /// first result position of an interface function is used, by convention, to 10 /// indicate whether the function was successful and subsequent results are valid, 11 /// or whether an error occurred. This trait allows wiggle to return the correct 12 /// value when the interface function's idiomatic Rust method returns 13 /// `Ok(<rest of return values>)`. 14 pub trait GuestErrorType { success() -> Self15 fn success() -> Self; 16 } 17 18 /// A trait for types that are intended to be pointees in `GuestPtr<T>`. 19 /// 20 /// This trait abstracts how to read/write information from the guest memory, as 21 /// well as how to offset elements in an array of guest memory. This layer of 22 /// abstraction allows the guest representation of a type to be different from 23 /// the host representation of a type, if necessary. It also allows for 24 /// validation when reading/writing. 25 pub trait GuestType: Sized { 26 /// Returns the size, in bytes, of this type in the guest memory. guest_size() -> u3227 fn guest_size() -> u32; 28 29 /// Returns the required alignment of this type, in bytes, for both guest 30 /// and host memory. guest_align() -> usize31 fn guest_align() -> usize; 32 33 /// Reads this value from the provided `ptr`. 34 /// 35 /// Must internally perform any safety checks necessary and is allowed to 36 /// fail if the bytes pointed to are also invalid. 37 /// 38 /// Typically if you're implementing this by hand you'll want to delegate to 39 /// other safe implementations of this trait (e.g. for primitive types like 40 /// `u32`) rather than writing lots of raw code yourself. read(mem: &GuestMemory, ptr: GuestPtr<Self>) -> Result<Self, GuestError>41 fn read(mem: &GuestMemory, ptr: GuestPtr<Self>) -> Result<Self, GuestError>; 42 43 /// Writes a value to `ptr` after verifying that `ptr` is indeed valid to 44 /// store `val`. 45 /// 46 /// Similar to `read`, you'll probably want to implement this in terms of 47 /// other primitives. write(mem: &mut GuestMemory, ptr: GuestPtr<Self>, val: Self) -> Result<(), GuestError>48 fn write(mem: &mut GuestMemory, ptr: GuestPtr<Self>, val: Self) -> Result<(), GuestError>; 49 } 50 51 /// A trait for `GuestType`s that have the same representation in guest memory 52 /// as in Rust. These types can be used with the `GuestPtr::as_slice` method to 53 /// view as a slice. 54 /// 55 /// Unsafe trait because a correct `GuestTypeTransparent` implementation ensures 56 /// that the `GuestPtr::as_slice` methods are safe, notably that the 57 /// representation on the host matches the guest and all bit patterns are 58 /// valid. This trait should only ever be implemented by 59 /// wiggle_generate-produced code. 60 pub unsafe trait GuestTypeTransparent: GuestType {} 61 62 macro_rules! integer_primitives { 63 ($([$ty:ident, $ty_atomic:ident],)*) => ($( 64 impl GuestType for $ty { 65 #[inline] 66 fn guest_size() -> u32 { mem::size_of::<Self>() as u32 } 67 #[inline] 68 fn guest_align() -> usize { mem::align_of::<Self>() } 69 70 #[inline] 71 fn read(mem: &GuestMemory, ptr: GuestPtr<Self>) -> Result<Self, GuestError> { 72 // Use `validate_size_align` to validate offset and alignment 73 // internally. The `host_ptr` type will be `&UnsafeCell<Self>` 74 // indicating that the memory is valid, and next safety checks 75 // are required to access it. 76 let offset = ptr.offset(); 77 let host_ptr = mem.validate_size_align::<Self>(offset, 1)?; 78 79 // If the accessed memory is shared, we need to load the bytes 80 // with the correct memory consistency. We could check if the 81 // memory is shared each time, but we expect little performance 82 // difference between an additional branch and a relaxed memory 83 // access and thus always do the relaxed access here. 84 let host_ptr: &$ty_atomic = unsafe { 85 let host_ptr: &UnsafeCell<Self> = &host_ptr[0]; 86 &*((host_ptr as *const UnsafeCell<Self>).cast::<$ty_atomic>()) 87 }; 88 let val = host_ptr.load(Ordering::Relaxed); 89 90 // And as a final operation convert from the little-endian wasm 91 // value to a native-endian value for the host. 92 Ok($ty::from_le(val)) 93 } 94 95 #[inline] 96 fn write(mem: &mut GuestMemory, ptr: GuestPtr<Self>, val: Self) -> Result<(), GuestError> { 97 // See `read` above for various checks here. 98 let val = val.to_le(); 99 let offset = ptr.offset(); 100 let host_ptr = mem.validate_size_align::<Self>(offset, 1)?; 101 let host_ptr = &host_ptr[0]; 102 let atomic_value_ref: &$ty_atomic = 103 unsafe { &*(host_ptr.get().cast::<$ty_atomic>()) }; 104 atomic_value_ref.store(val, Ordering::Relaxed); 105 Ok(()) 106 } 107 } 108 109 unsafe impl GuestTypeTransparent for $ty {} 110 111 )*) 112 } 113 114 macro_rules! float_primitives { 115 ($([$ty:ident, $ty_unsigned:ident, $ty_atomic:ident],)*) => ($( 116 impl GuestType for $ty { 117 #[inline] 118 fn guest_size() -> u32 { mem::size_of::<Self>() as u32 } 119 #[inline] 120 fn guest_align() -> usize { mem::align_of::<Self>() } 121 122 #[inline] 123 fn read(mem: &GuestMemory, ptr: GuestPtr<Self>) -> Result<Self, GuestError> { 124 <$ty_unsigned as GuestType>::read(mem, ptr.cast()).map($ty::from_bits) 125 } 126 127 #[inline] 128 fn write(mem:&mut GuestMemory, ptr: GuestPtr<Self>, val: Self) -> Result<(), GuestError> { 129 <$ty_unsigned as GuestType>::write(mem, ptr.cast(), val.to_bits()) 130 } 131 } 132 133 unsafe impl GuestTypeTransparent for $ty {} 134 135 )*) 136 } 137 138 integer_primitives! { 139 // signed 140 [i8, AtomicI8], [i16, AtomicI16], [i32, AtomicI32], [i64, AtomicI64], 141 // unsigned 142 [u8, AtomicU8], [u16, AtomicU16], [u32, AtomicU32], [u64, AtomicU64], 143 } 144 145 float_primitives! { 146 [f32, u32, AtomicU32], [f64, u64, AtomicU64], 147 } 148 149 // Support pointers-to-pointers where pointers are always 32-bits in wasm land 150 impl<T> GuestType for GuestPtr<T> { 151 #[inline] guest_size() -> u32152 fn guest_size() -> u32 { 153 u32::guest_size() 154 } 155 156 #[inline] guest_align() -> usize157 fn guest_align() -> usize { 158 u32::guest_align() 159 } 160 read(mem: &GuestMemory, ptr: GuestPtr<Self>) -> Result<Self, GuestError>161 fn read(mem: &GuestMemory, ptr: GuestPtr<Self>) -> Result<Self, GuestError> { 162 let offset = u32::read(mem, ptr.cast())?; 163 Ok(GuestPtr::new(offset)) 164 } 165 write(mem: &mut GuestMemory, ptr: GuestPtr<Self>, val: Self) -> Result<(), GuestError>166 fn write(mem: &mut GuestMemory, ptr: GuestPtr<Self>, val: Self) -> Result<(), GuestError> { 167 u32::write(mem, ptr.cast(), val.offset()) 168 } 169 } 170 171 // Support pointers-to-arrays where pointers are always 32-bits in wasm land 172 impl<T> GuestType for GuestPtr<[T]> 173 where 174 T: GuestType, 175 { 176 #[inline] guest_size() -> u32177 fn guest_size() -> u32 { 178 u32::guest_size() * 2 179 } 180 181 #[inline] guest_align() -> usize182 fn guest_align() -> usize { 183 u32::guest_align() 184 } 185 read(mem: &GuestMemory, ptr: GuestPtr<Self>) -> Result<Self, GuestError>186 fn read(mem: &GuestMemory, ptr: GuestPtr<Self>) -> Result<Self, GuestError> { 187 let ptr = ptr.cast::<u32>(); 188 let offset = u32::read(mem, ptr)?; 189 let len = u32::read(mem, ptr.add(1)?)?; 190 Ok(GuestPtr::new(offset).as_array(len)) 191 } 192 write(mem: &mut GuestMemory, ptr: GuestPtr<Self>, val: Self) -> Result<(), GuestError>193 fn write(mem: &mut GuestMemory, ptr: GuestPtr<Self>, val: Self) -> Result<(), GuestError> { 194 let (offset, len) = val.offset(); 195 let ptr = ptr.cast::<u32>(); 196 u32::write(mem, ptr, offset)?; 197 u32::write(mem, ptr.add(1)?, len)?; 198 Ok(()) 199 } 200 } 201