wiggle/
lib.rs

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