wiggle/
guest_type.rs

1use crate::{GuestError, GuestMemory, GuestPtr};
2use std::cell::UnsafeCell;
3use std::mem;
4use std::sync::atomic::{
5    AtomicI16, AtomicI32, AtomicI64, AtomicI8, AtomicU16, AtomicU32, AtomicU64, AtomicU8, 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>)`.
14pub trait GuestErrorType {
15    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.
25pub trait GuestType: Sized {
26    /// Returns the size, in bytes, of this type in the guest memory.
27    fn guest_size() -> u32;
28
29    /// Returns the required alignment of this type, in bytes, for both guest
30    /// and host memory.
31    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.
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.
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.
60pub unsafe trait GuestTypeTransparent: GuestType {}
61
62macro_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
114macro_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
138integer_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
145float_primitives! {
146    [f32, u32, AtomicU32], [f64, u64, AtomicU64],
147}
148
149// Support pointers-to-pointers where pointers are always 32-bits in wasm land
150impl<T> GuestType for GuestPtr<T> {
151    #[inline]
152    fn guest_size() -> u32 {
153        u32::guest_size()
154    }
155
156    #[inline]
157    fn guest_align() -> usize {
158        u32::guest_align()
159    }
160
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
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
172impl<T> GuestType for GuestPtr<[T]>
173where
174    T: GuestType,
175{
176    #[inline]
177    fn guest_size() -> u32 {
178        u32::guest_size() * 2
179    }
180
181    #[inline]
182    fn guest_align() -> usize {
183        u32::guest_align()
184    }
185
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
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}