wiggle/
lib.rs

1use anyhow::{Result, bail};
2use std::borrow::Cow;
3use std::cell::UnsafeCell;
4use std::fmt;
5use std::mem;
6use std::ops::Range;
7use std::str;
8
9pub use wiggle_macro::from_witx;
10
11pub use anyhow;
12pub use wiggle_macro::wasmtime_integration;
13
14pub use bitflags;
15
16#[cfg(feature = "wiggle_metadata")]
17pub use witx;
18
19mod error;
20mod guest_type;
21mod region;
22
23pub use tracing;
24
25pub use error::GuestError;
26pub use guest_type::{GuestErrorType, GuestType, GuestTypeTransparent};
27pub use region::Region;
28
29#[cfg(feature = "wasmtime")]
30pub mod wasmtime_crate {
31    pub use wasmtime::*;
32}
33
34/// Representation of guest memory for `wiggle`-generated trait methods.
35///
36/// Guest memory is represented as an array of bytes. Memories are either
37/// "unshared" or "shared". Unshared means that the host has exclusive access to
38/// the entire array of memory. This allows safe borrows into wasm linear
39/// memory. Shared memories can be modified at any time and are represented as
40/// an array of `UnsafeCell<u8>`.
41///
42/// This is generated by the `wiggle` bindings macros.
43pub enum GuestMemory<'a> {
44    Unshared(&'a mut [u8]),
45    Shared(&'a [UnsafeCell<u8>]),
46}
47
48// manual impls are needed because of the `UnsafeCell` in the `Shared` branch
49// but this otherwise upholds send/sync invariants.
50unsafe impl Send for GuestMemory<'_> {}
51unsafe impl Sync for GuestMemory<'_> {}
52
53impl<'a> GuestMemory<'a> {
54    /// Read a value from the provided pointer.
55    ///
56    /// This method will delegate to `T`'s implementation of `read` which will
57    /// read a value from the `ptr` provided.
58    ///
59    /// # Errors
60    ///
61    /// An error is returned if `ptr` is out of bounds, misaligned, or otherwise
62    /// not valid to read from.
63    pub fn read<T>(&self, ptr: GuestPtr<T>) -> Result<T, GuestError>
64    where
65        T: GuestType,
66    {
67        T::read(self, ptr)
68    }
69
70    /// Writes the `val` provided to the `ptr` provided.
71    ///
72    /// This commit will write a `val` into a guest's linear memory. This will
73    /// delegate to `T`'s implementation of `write`.
74    ///
75    /// # Errors
76    ///
77    /// An error is returned if `ptr` is out of bounds, misaligned, or otherwise
78    /// not valid to read from.
79    pub fn write<T>(&mut self, ptr: GuestPtr<T>, val: T) -> Result<(), GuestError>
80    where
81        T: GuestType,
82    {
83        T::write(self, ptr, val)
84    }
85
86    /// Acquires a slice or owned copy of the memory pointed to by `ptr`.
87    ///
88    /// This method will attempt to borrow `ptr` directly from linear memory. If
89    /// memory is shared and cannot be borrowed directly then an owned copy is
90    /// returned instead.
91    ///
92    /// # Errors
93    ///
94    /// An error is returned if `ptr` is out of bounds, misaligned, or otherwise
95    /// not valid to read from.
96    pub fn as_cow(&self, ptr: GuestPtr<[u8]>) -> Result<Cow<'_, [u8]>, GuestError> {
97        match self {
98            GuestMemory::Unshared(_) => match self.as_slice(ptr)? {
99                Some(slice) => Ok(Cow::Borrowed(slice)),
100                None => unreachable!(),
101            },
102            GuestMemory::Shared(_) => Ok(Cow::Owned(self.to_vec(ptr)?)),
103        }
104    }
105
106    /// Same as [`GuestMemory::as_cow`] but for strings.
107    ///
108    /// # Errors
109    ///
110    /// An error is returned if `ptr` is out of bounds, misaligned, or otherwise
111    /// not valid to read from.
112    pub fn as_cow_str(&self, ptr: GuestPtr<str>) -> Result<Cow<'_, str>, GuestError> {
113        match self.as_cow(ptr.cast::<[u8]>())? {
114            Cow::Owned(bytes) => Ok(Cow::Owned(
115                String::from_utf8(bytes).map_err(|e| e.utf8_error())?,
116            )),
117            Cow::Borrowed(bytes) => Ok(Cow::Borrowed(std::str::from_utf8(bytes)?)),
118        }
119    }
120
121    /// Attempts to borrow a raw guest slice of memory pointed to by `ptr`.
122    ///
123    /// This method will attempt to return a raw pointer into guest memory. This
124    /// can only be done for `Unshared` memories. A `Shared` memory will return
125    /// `Ok(None)` here.
126    ///
127    /// # Errors
128    ///
129    /// An error is returned if `ptr` is out of bounds, misaligned, or otherwise
130    /// not valid to read from.
131    pub fn as_slice(&self, ptr: GuestPtr<[u8]>) -> Result<Option<&[u8]>, GuestError> {
132        let range = self.validate_range::<u8>(ptr.pointer.0, ptr.pointer.1)?;
133        match self {
134            GuestMemory::Unshared(slice) => Ok(Some(&slice[range])),
135            GuestMemory::Shared(_) => Ok(None),
136        }
137    }
138
139    /// Same as [`GuestMemory::as_slice`] but for strings.
140    pub fn as_str(&self, ptr: GuestPtr<str>) -> Result<Option<&str>, GuestError> {
141        match self.as_slice(ptr.cast())? {
142            Some(bytes) => Ok(Some(std::str::from_utf8(bytes)?)),
143            None => Ok(None),
144        }
145    }
146
147    /// Attempts return `ptr` as a raw slice of mutable bytes in wasm linear
148    /// memory.
149    ///
150    /// Like [`GuestMemory::as_slice`] this only works for `Unshared` memories
151    /// and will not work for `Shared` memories.
152    pub fn as_slice_mut(&mut self, ptr: GuestPtr<[u8]>) -> Result<Option<&mut [u8]>, GuestError> {
153        let range = self.validate_range::<u8>(ptr.pointer.0, ptr.pointer.1)?;
154        match self {
155            GuestMemory::Unshared(slice) => Ok(Some(&mut slice[range])),
156            GuestMemory::Shared(_) => Ok(None),
157        }
158    }
159
160    /// Copies the data in the guest region into a [`Vec`].
161    ///
162    /// This is useful when one cannot use [`GuestMemory::as_slice`], e.g., when
163    /// pointing to a region of WebAssembly shared memory.
164    pub fn to_vec<T>(&self, ptr: GuestPtr<[T]>) -> Result<Vec<T>, GuestError>
165    where
166        T: GuestTypeTransparent + Copy,
167    {
168        let guest = self.validate_size_align::<T>(ptr.pointer.0, ptr.pointer.1)?;
169        let mut host = Vec::with_capacity(guest.len());
170
171        // SAFETY: The `guest_slice` variable is already a valid pointer into
172        // the guest's memory, and it may or may not be a pointer into shared
173        // memory. We can't naively use `.to_vec(..)` which could introduce data
174        // races but all that needs to happen is to copy data into our local
175        // `vec` as all the data is `Copy` and transparent anyway. For this
176        // purpose the `ptr::copy` function should be sufficient for copying
177        // over all the data.
178        //
179        // TODO: audit that this use of `std::ptr::copy` is safe with shared
180        // memory (https://github.com/bytecodealliance/wasmtime/issues/4203)
181        unsafe {
182            std::ptr::copy(guest.as_ptr().cast(), host.as_mut_ptr(), guest.len());
183            host.set_len(guest.len());
184        }
185        Ok(host)
186    }
187
188    /// Copies the data pointed to by `slice` into this guest region.
189    ///
190    /// This method is a *safe* method to copy data from the host to the guest.
191    /// This requires that `self` and `slice` have the same length. The pointee
192    /// type `T` requires the [`GuestTypeTransparent`] trait which is an
193    /// assertion that the representation on the host and on the guest is the
194    /// same.
195    ///
196    /// # Errors
197    ///
198    /// Returns an error if this guest pointer is out of bounds or if the length
199    /// of this guest pointer is not equal to the length of the slice provided.
200    pub fn copy_from_slice<T>(&mut self, slice: &[T], ptr: GuestPtr<[T]>) -> Result<(), GuestError>
201    where
202        T: GuestTypeTransparent + Copy,
203    {
204        if usize::try_from(ptr.len())? != slice.len() {
205            return Err(GuestError::SliceLengthsDiffer);
206        }
207        if slice.is_empty() {
208            return Ok(());
209        }
210
211        let guest = self.validate_size_align::<T>(ptr.pointer.0, ptr.pointer.1)?;
212
213        // SAFETY: in the shared memory case, we copy and accept that
214        // the guest data may be concurrently modified. TODO: audit that
215        // this use of `std::ptr::copy` is safe with shared memory
216        // (https://github.com/bytecodealliance/wasmtime/issues/4203)
217        //
218        // Also note that the validity of `guest_slice` has already been
219        // determined by the `as_unsafe_slice_mut` call above.
220        assert_eq!(guest.len(), slice.len());
221        unsafe {
222            let guest: &[UnsafeCell<T>] = guest;
223            let guest: *const UnsafeCell<T> = guest.as_ptr();
224            let guest = guest.cast_mut().cast::<T>();
225            std::ptr::copy(slice.as_ptr(), guest, slice.len());
226        }
227        Ok(())
228    }
229
230    /// Validates a guest-relative pointer given various attributes, and returns
231    /// the corresponding host pointer.
232    ///
233    /// * `mem` - this is the guest memory being accessed.
234    /// * `offset` - this is the guest-relative pointer, an offset from the
235    ///   base.
236    /// * `len` - this is the number of length, in units of `T`, to return
237    ///   in the resulting slice.
238    ///
239    /// If the parameters are valid then this function will return a slice into
240    /// `mem` for units of `T`, assuming everything is in-bounds and properly
241    /// aligned. Additionally the byte-based `Region` is returned, used for borrows
242    /// later on.
243    fn validate_size_align<T>(&self, offset: u32, len: u32) -> Result<&[UnsafeCell<T>], GuestError>
244    where
245        T: GuestTypeTransparent,
246    {
247        let range = self.validate_range::<T>(offset, len)?;
248        let cells = match self {
249            GuestMemory::Unshared(s) => {
250                let s: &[u8] = s;
251                unsafe { &*(s as *const [u8] as *const [UnsafeCell<u8>]) }
252            }
253            GuestMemory::Shared(s) => s,
254        };
255        let memory = &cells[range.clone()];
256
257        // ... and then align it to `T`, failing if either the head or tail slices
258        // are nonzero in length. This `unsafe` here is from the standard library
259        // and should be ok since the input slice is `UnsafeCell<u8>` and the output
260        // slice is `UnsafeCell<T>`, meaning the only guarantee of the output is
261        // that it's valid addressable memory, still unsafe to actually access.
262        assert!(mem::align_of::<T>() <= T::guest_align());
263        let (start, mid, end) = unsafe { memory.align_to() };
264        if start.len() > 0 || end.len() > 0 {
265            let region = Region {
266                start: range.start as u32,
267                len: range.len() as u32,
268            };
269            return Err(GuestError::PtrNotAligned(region, T::guest_align() as u32));
270        }
271        Ok(mid)
272    }
273
274    fn validate_range<T>(&self, offset: u32, len: u32) -> Result<Range<usize>, GuestError>
275    where
276        T: GuestTypeTransparent,
277    {
278        let byte_len = len
279            .checked_mul(T::guest_size())
280            .ok_or(GuestError::PtrOverflow)?;
281        let region = Region {
282            start: offset,
283            len: byte_len,
284        };
285        let offset = usize::try_from(offset)?;
286        let byte_len = usize::try_from(byte_len)?;
287
288        let range = offset..offset + byte_len;
289        let oob = match self {
290            GuestMemory::Unshared(b) => b.get(range.clone()).is_none(),
291            GuestMemory::Shared(b) => b.get(range.clone()).is_none(),
292        };
293        if oob {
294            Err(GuestError::PtrOutOfBounds(region))
295        } else {
296            Ok(range)
297        }
298    }
299
300    /// Returns whether this is a shared memory or not.
301    pub fn is_shared_memory(&self) -> bool {
302        match self {
303            GuestMemory::Shared(_) => true,
304            GuestMemory::Unshared(_) => false,
305        }
306    }
307}
308
309/// A *guest* pointer.
310///
311/// This type represents a pointer from the guest that points into host memory.
312/// Internally a `GuestPtr` the offset into the memory that the pointer is
313/// pointing at. At this time this is always a 32-bit offset so this is not
314/// suitable for bindings where wasm has 64-bit addresses.
315///
316/// Presence of a [`GuestPtr`] does not imply any form of validity. Pointers can
317/// be out-of-bounds, misaligned, etc. It is safe to construct a `GuestPtr` with
318/// any offset at any time. Consider a `GuestPtr<T>` roughly equivalent to `*mut
319/// T`.
320///
321/// ## Slices and Strings
322///
323/// Note that the type parameter does not need to implement the `Sized` trait,
324/// so you can implement types such as this:
325///
326/// * `GuestPtr<str>` - a pointer to a guest string.
327/// * `GuestPtr<[T]>` - a pointer to a guest array.
328///
329/// Note that generated bindings won't use these types so you'll have to
330/// otherwise construct the types with `.cast()` or `.as_array()`. Unsized types
331/// track both the pointer and length in guest memory.
332///
333/// ## Type parameter and pointee
334///
335/// The `T` type parameter is largely intended for more static safety in Rust as
336/// well as having a better handle on what we're pointing to. A `GuestPtr<T>`,
337/// however, does not necessarily literally imply a guest pointer pointing to
338/// type `T`. Instead the [`GuestType`] trait is a layer of abstraction where
339/// `GuestPtr<T>` may actually be a pointer to `U` in guest memory, but you can
340/// construct a `T` from a `U`.
341///
342/// For example `GuestPtr<GuestPtr<T>>` is a valid type, but this is actually
343/// more equivalent to `GuestPtr<u32>` because guest pointers are always
344/// 32-bits. That being said you can create a `GuestPtr<T>` from a `u32`.
345///
346/// Additionally `GuestPtr<MyEnum>` will actually delegate, typically, to and
347/// implementation which loads the underlying data as `GuestPtr<u8>` (or
348/// similar) and then the bytes loaded are validated to fit within the
349/// definition of `MyEnum` before `MyEnum` is returned.
350///
351/// For more information see the [`GuestMemory::read`] and
352/// [`GuestMemory::write`] methods. In general though be extremely careful about
353/// writing `unsafe` code when working with a `GuestPtr` if you're not using one
354/// of the already-attached helper methods.
355#[repr(transparent)]
356pub struct GuestPtr<T: ?Sized + Pointee> {
357    pointer: T::Pointer,
358}
359
360impl<T: ?Sized + Pointee> GuestPtr<T> {
361    /// Creates a new `GuestPtr` from the given `mem` and `pointer` values.
362    ///
363    /// Note that for sized types like `u32`, `GuestPtr<T>`, etc, the `pointer`
364    /// value is a `u32` offset into guest memory. For slices and strings,
365    /// `pointer` is a `(u32, u32)` offset/length pair.
366    pub fn new(pointer: T::Pointer) -> GuestPtr<T> {
367        GuestPtr { pointer }
368    }
369
370    /// Returns the offset of this pointer in guest memory.
371    ///
372    /// Note that for sized types this returns a `u32`, but for slices and
373    /// strings it returns a `(u32, u32)` pointer/length pair.
374    pub fn offset(&self) -> T::Pointer {
375        self.pointer
376    }
377
378    /// Casts this `GuestPtr` type to a different type.
379    ///
380    /// This is a safe method which is useful for simply reinterpreting the type
381    /// parameter on this `GuestPtr`. Note that this is a safe method, where
382    /// again there's no guarantees about alignment, validity, in-bounds-ness,
383    /// etc of the returned pointer.
384    pub fn cast<U>(&self) -> GuestPtr<U>
385    where
386        U: Pointee<Pointer = T::Pointer> + ?Sized,
387    {
388        GuestPtr::new(self.pointer)
389    }
390
391    /// Performs pointer arithmetic on this pointer, moving the pointer forward
392    /// `amt` slots.
393    ///
394    /// This will either return the resulting pointer or `Err` if the pointer
395    /// arithmetic calculation would overflow around the end of the address
396    /// space.
397    pub fn add(&self, amt: u32) -> Result<GuestPtr<T>, GuestError>
398    where
399        T: GuestType + Pointee<Pointer = u32>,
400    {
401        let offset = amt
402            .checked_mul(T::guest_size())
403            .and_then(|o| self.pointer.checked_add(o));
404        let offset = match offset {
405            Some(o) => o,
406            None => return Err(GuestError::PtrOverflow),
407        };
408        Ok(GuestPtr::new(offset))
409    }
410
411    /// Returns a `GuestPtr` for an array of `T`s using this pointer as the
412    /// base.
413    pub fn as_array(&self, elems: u32) -> GuestPtr<[T]>
414    where
415        T: GuestType + Pointee<Pointer = u32>,
416    {
417        GuestPtr::new((self.pointer, elems))
418    }
419}
420
421impl<T> GuestPtr<[T]> {
422    /// For slices, specifically returns the relative pointer to the base of the
423    /// array.
424    ///
425    /// This is similar to `<[T]>::as_ptr()`
426    pub fn offset_base(&self) -> u32 {
427        self.pointer.0
428    }
429
430    /// For slices, returns the length of the slice, in elements.
431    pub fn len(&self) -> u32 {
432        self.pointer.1
433    }
434
435    /// Returns an iterator over interior pointers.
436    ///
437    /// Each item is a `Result` indicating whether it overflowed past the end of
438    /// the address space or not.
439    pub fn iter(&self) -> impl ExactSizeIterator<Item = Result<GuestPtr<T>, GuestError>> + '_
440    where
441        T: GuestType,
442    {
443        let base = self.as_ptr();
444        (0..self.len()).map(move |i| base.add(i))
445    }
446
447    /// Returns a `GuestPtr` pointing to the base of the array for the interior
448    /// type `T`.
449    pub fn as_ptr(&self) -> GuestPtr<T> {
450        GuestPtr::new(self.offset_base())
451    }
452
453    pub fn get(&self, index: u32) -> Option<GuestPtr<T>>
454    where
455        T: GuestType,
456    {
457        if index < self.len() {
458            Some(
459                self.as_ptr()
460                    .add(index)
461                    .expect("just performed bounds check"),
462            )
463        } else {
464            None
465        }
466    }
467
468    pub fn get_range(&self, r: std::ops::Range<u32>) -> Option<GuestPtr<[T]>>
469    where
470        T: GuestType,
471    {
472        if r.end < r.start {
473            return None;
474        }
475        let range_length = r.end - r.start;
476        if r.start <= self.len() && r.end <= self.len() {
477            Some(
478                self.as_ptr()
479                    .add(r.start)
480                    .expect("just performed bounds check")
481                    .as_array(range_length),
482            )
483        } else {
484            None
485        }
486    }
487}
488
489impl GuestPtr<str> {
490    /// For strings, returns the relative pointer to the base of the string
491    /// allocation.
492    pub fn offset_base(&self) -> u32 {
493        self.pointer.0
494    }
495
496    /// Returns the length, in bytes, of the string.
497    pub fn len(&self) -> u32 {
498        self.pointer.1
499    }
500
501    /// Returns a raw pointer for the underlying slice of bytes that this
502    /// pointer points to.
503    pub fn as_bytes(&self) -> GuestPtr<[u8]> {
504        GuestPtr::new(self.pointer)
505    }
506}
507
508impl<T: ?Sized + Pointee> Clone for GuestPtr<T> {
509    fn clone(&self) -> Self {
510        *self
511    }
512}
513
514impl<T: ?Sized + Pointee> Copy for GuestPtr<T> {}
515
516impl<T: ?Sized + Pointee> fmt::Debug for GuestPtr<T> {
517    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
518        T::debug(self.pointer, f)
519    }
520}
521
522impl<T: ?Sized + Pointee> PartialEq for GuestPtr<T> {
523    fn eq(&self, other: &Self) -> bool {
524        self.pointer == other.pointer
525    }
526}
527
528mod private {
529    pub trait Sealed {}
530    impl<T> Sealed for T {}
531    impl<T> Sealed for [T] {}
532    impl Sealed for str {}
533}
534
535/// Types that can be pointed to by `GuestPtr<T>`.
536///
537/// In essence everything can, and the only special-case is unsized types like
538/// `str` and `[T]` which have special implementations.
539pub trait Pointee: private::Sealed {
540    #[doc(hidden)]
541    type Pointer: Copy + PartialEq;
542    #[doc(hidden)]
543    fn debug(pointer: Self::Pointer, f: &mut fmt::Formatter) -> fmt::Result;
544}
545
546impl<T> Pointee for T {
547    type Pointer = u32;
548    fn debug(pointer: Self::Pointer, f: &mut fmt::Formatter) -> fmt::Result {
549        write!(f, "*guest {pointer:#x}")
550    }
551}
552
553impl<T> Pointee for [T] {
554    type Pointer = (u32, u32);
555    fn debug(pointer: Self::Pointer, f: &mut fmt::Formatter) -> fmt::Result {
556        write!(f, "*guest {:#x}/{}", pointer.0, pointer.1)
557    }
558}
559
560impl Pointee for str {
561    type Pointer = (u32, u32);
562    fn debug(pointer: Self::Pointer, f: &mut fmt::Formatter) -> fmt::Result {
563        <[u8]>::debug(pointer, f)
564    }
565}
566
567pub fn run_in_dummy_executor<F: std::future::Future>(future: F) -> Result<F::Output> {
568    use std::pin::Pin;
569    use std::task::{Context, Poll, RawWaker, RawWakerVTable, Waker};
570
571    let mut f = Pin::from(Box::new(future));
572    let waker = dummy_waker();
573    let mut cx = Context::from_waker(&waker);
574    match f.as_mut().poll(&mut cx) {
575        Poll::Ready(val) => return Ok(val),
576        Poll::Pending => bail!(
577            "Cannot wait on pending future: must enable wiggle \"async\" future and execute on an async Store"
578        ),
579    }
580
581    fn dummy_waker() -> Waker {
582        return unsafe { Waker::from_raw(clone(5 as *const _)) };
583
584        unsafe fn clone(ptr: *const ()) -> RawWaker {
585            assert_eq!(ptr as usize, 5);
586            const VTABLE: RawWakerVTable = RawWakerVTable::new(clone, wake, wake_by_ref, drop);
587            RawWaker::new(ptr, &VTABLE)
588        }
589
590        unsafe fn wake(ptr: *const ()) {
591            assert_eq!(ptr as usize, 5);
592        }
593
594        unsafe fn wake_by_ref(ptr: *const ()) {
595            assert_eq!(ptr as usize, 5);
596        }
597
598        unsafe fn drop(ptr: *const ()) {
599            assert_eq!(ptr as usize, 5);
600        }
601    }
602}