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