use anyhow::{Result, bail};1use std::borrow::Cow;2use std::cell::UnsafeCell;3use std::fmt;4use std::mem;5use std::ops::Range;6use std::str;78pub use wiggle_macro::{async_trait, from_witx};910pub use anyhow;11pub use wiggle_macro::wasmtime_integration;1213pub use bitflags;1415#[cfg(feature = "wiggle_metadata")]16pub use witx;1718mod error;19mod guest_type;20mod region;2122pub use tracing;2324pub use error::GuestError;25pub use guest_type::{GuestErrorType, GuestType, GuestTypeTransparent};26pub use region::Region;2728pub mod async_trait_crate {29pub use async_trait::*;30}3132#[cfg(feature = "wasmtime")]33pub mod wasmtime_crate {34pub use wasmtime::*;35}3637/// Representation of guest memory for `wiggle`-generated trait methods.38///39/// Guest memory is represented as an array of bytes. Memories are either40/// "unshared" or "shared". Unshared means that the host has exclusive access to41/// the entire array of memory. This allows safe borrows into wasm linear42/// memory. Shared memories can be modified at any time and are represented as43/// an array of `UnsafeCell<u8>`.44///45/// This is generated by the `wiggle` bindings macros.46pub enum GuestMemory<'a> {47Unshared(&'a mut [u8]),48Shared(&'a [UnsafeCell<u8>]),49}5051// manual impls are needed because of the `UnsafeCell` in the `Shared` branch52// but this otherwise upholds send/sync invariants.53unsafe impl Send for GuestMemory<'_> {}54unsafe impl Sync for GuestMemory<'_> {}5556impl<'a> GuestMemory<'a> {57/// Read a value from the provided pointer.58///59/// This method will delegate to `T`'s implementation of `read` which will60/// read a value from the `ptr` provided.61///62/// # Errors63///64/// An error is returned if `ptr` is out of bounds, misaligned, or otherwise65/// not valid to read from.66pub fn read<T>(&self, ptr: GuestPtr<T>) -> Result<T, GuestError>67where68T: GuestType,69{70T::read(self, ptr)71}7273/// Writes the `val` provided to the `ptr` provided.74///75/// This commit will write a `val` into a guest's linear memory. This will76/// delegate to `T`'s implementation of `write`.77///78/// # Errors79///80/// An error is returned if `ptr` is out of bounds, misaligned, or otherwise81/// not valid to read from.82pub fn write<T>(&mut self, ptr: GuestPtr<T>, val: T) -> Result<(), GuestError>83where84T: GuestType,85{86T::write(self, ptr, val)87}8889/// Acquires a slice or owned copy of the memory pointed to by `ptr`.90///91/// This method will attempt to borrow `ptr` directly from linear memory. If92/// memory is shared and cannot be borrowed directly then an owned copy is93/// returned instead.94///95/// # Errors96///97/// An error is returned if `ptr` is out of bounds, misaligned, or otherwise98/// not valid to read from.99pub fn as_cow(&self, ptr: GuestPtr<[u8]>) -> Result<Cow<'_, [u8]>, GuestError> {100match self {101GuestMemory::Unshared(_) => match self.as_slice(ptr)? {102Some(slice) => Ok(Cow::Borrowed(slice)),103None => unreachable!(),104},105GuestMemory::Shared(_) => Ok(Cow::Owned(self.to_vec(ptr)?)),106}107}108109/// Same as [`GuestMemory::as_cow`] but for strings.110///111/// # Errors112///113/// An error is returned if `ptr` is out of bounds, misaligned, or otherwise114/// not valid to read from.115pub fn as_cow_str(&self, ptr: GuestPtr<str>) -> Result<Cow<'_, str>, GuestError> {116match self.as_cow(ptr.cast::<[u8]>())? {117Cow::Owned(bytes) => Ok(Cow::Owned(118String::from_utf8(bytes).map_err(|e| e.utf8_error())?,119)),120Cow::Borrowed(bytes) => Ok(Cow::Borrowed(std::str::from_utf8(bytes)?)),121}122}123124/// Attempts to borrow a raw guest slice of memory pointed to by `ptr`.125///126/// This method will attempt to return a raw pointer into guest memory. This127/// can only be done for `Unshared` memories. A `Shared` memory will return128/// `Ok(None)` here.129///130/// # Errors131///132/// An error is returned if `ptr` is out of bounds, misaligned, or otherwise133/// not valid to read from.134pub fn as_slice(&self, ptr: GuestPtr<[u8]>) -> Result<Option<&[u8]>, GuestError> {135let range = self.validate_range::<u8>(ptr.pointer.0, ptr.pointer.1)?;136match self {137GuestMemory::Unshared(slice) => Ok(Some(&slice[range])),138GuestMemory::Shared(_) => Ok(None),139}140}141142/// Same as [`GuestMemory::as_slice`] but for strings.143pub fn as_str(&self, ptr: GuestPtr<str>) -> Result<Option<&str>, GuestError> {144match self.as_slice(ptr.cast())? {145Some(bytes) => Ok(Some(std::str::from_utf8(bytes)?)),146None => Ok(None),147}148}149150/// Attempts return `ptr` as a raw slice of mutable bytes in wasm linear151/// memory.152///153/// Like [`GuestMemory::as_slice`] this only works for `Unshared` memories154/// and will not work for `Shared` memories.155pub fn as_slice_mut(&mut self, ptr: GuestPtr<[u8]>) -> Result<Option<&mut [u8]>, GuestError> {156let range = self.validate_range::<u8>(ptr.pointer.0, ptr.pointer.1)?;157match self {158GuestMemory::Unshared(slice) => Ok(Some(&mut slice[range])),159GuestMemory::Shared(_) => Ok(None),160}161}162163/// Copies the data in the guest region into a [`Vec`].164///165/// This is useful when one cannot use [`GuestMemory::as_slice`], e.g., when166/// pointing to a region of WebAssembly shared memory.167pub fn to_vec<T>(&self, ptr: GuestPtr<[T]>) -> Result<Vec<T>, GuestError>168where169T: GuestTypeTransparent + Copy,170{171let guest = self.validate_size_align::<T>(ptr.pointer.0, ptr.pointer.1)?;172let mut host = Vec::with_capacity(guest.len());173174// SAFETY: The `guest_slice` variable is already a valid pointer into175// the guest's memory, and it may or may not be a pointer into shared176// memory. We can't naively use `.to_vec(..)` which could introduce data177// races but all that needs to happen is to copy data into our local178// `vec` as all the data is `Copy` and transparent anyway. For this179// purpose the `ptr::copy` function should be sufficient for copying180// over all the data.181//182// TODO: audit that this use of `std::ptr::copy` is safe with shared183// memory (https://github.com/bytecodealliance/wasmtime/issues/4203)184unsafe {185std::ptr::copy(guest.as_ptr().cast(), host.as_mut_ptr(), guest.len());186host.set_len(guest.len());187}188Ok(host)189}190191/// Copies the data pointed to by `slice` into this guest region.192///193/// This method is a *safe* method to copy data from the host to the guest.194/// This requires that `self` and `slice` have the same length. The pointee195/// type `T` requires the [`GuestTypeTransparent`] trait which is an196/// assertion that the representation on the host and on the guest is the197/// same.198///199/// # Errors200///201/// Returns an error if this guest pointer is out of bounds or if the length202/// of this guest pointer is not equal to the length of the slice provided.203pub fn copy_from_slice<T>(&mut self, slice: &[T], ptr: GuestPtr<[T]>) -> Result<(), GuestError>204where205T: GuestTypeTransparent + Copy,206{207if usize::try_from(ptr.len())? != slice.len() {208return Err(GuestError::SliceLengthsDiffer);209}210if slice.is_empty() {211return Ok(());212}213214let guest = self.validate_size_align::<T>(ptr.pointer.0, ptr.pointer.1)?;215216// SAFETY: in the shared memory case, we copy and accept that217// the guest data may be concurrently modified. TODO: audit that218// this use of `std::ptr::copy` is safe with shared memory219// (https://github.com/bytecodealliance/wasmtime/issues/4203)220//221// Also note that the validity of `guest_slice` has already been222// determined by the `as_unsafe_slice_mut` call above.223assert_eq!(guest.len(), slice.len());224unsafe {225let guest: &[UnsafeCell<T>] = guest;226let guest: *const UnsafeCell<T> = guest.as_ptr();227let guest = guest.cast_mut().cast::<T>();228std::ptr::copy(slice.as_ptr(), guest, slice.len());229}230Ok(())231}232233/// Validates a guest-relative pointer given various attributes, and returns234/// the corresponding host pointer.235///236/// * `mem` - this is the guest memory being accessed.237/// * `offset` - this is the guest-relative pointer, an offset from the238/// base.239/// * `len` - this is the number of length, in units of `T`, to return240/// in the resulting slice.241///242/// If the parameters are valid then this function will return a slice into243/// `mem` for units of `T`, assuming everything is in-bounds and properly244/// aligned. Additionally the byte-based `Region` is returned, used for borrows245/// later on.246fn validate_size_align<T>(&self, offset: u32, len: u32) -> Result<&[UnsafeCell<T>], GuestError>247where248T: GuestTypeTransparent,249{250let range = self.validate_range::<T>(offset, len)?;251let cells = match self {252GuestMemory::Unshared(s) => {253let s: &[u8] = s;254unsafe { &*(s as *const [u8] as *const [UnsafeCell<u8>]) }255}256GuestMemory::Shared(s) => s,257};258let memory = &cells[range.clone()];259260// ... and then align it to `T`, failing if either the head or tail slices261// are nonzero in length. This `unsafe` here is from the standard library262// and should be ok since the input slice is `UnsafeCell<u8>` and the output263// slice is `UnsafeCell<T>`, meaning the only guarantee of the output is264// that it's valid addressable memory, still unsafe to actually access.265assert!(mem::align_of::<T>() <= T::guest_align());266let (start, mid, end) = unsafe { memory.align_to() };267if start.len() > 0 || end.len() > 0 {268let region = Region {269start: range.start as u32,270len: range.len() as u32,271};272return Err(GuestError::PtrNotAligned(region, T::guest_align() as u32));273}274Ok(mid)275}276277fn validate_range<T>(&self, offset: u32, len: u32) -> Result<Range<usize>, GuestError>278where279T: GuestTypeTransparent,280{281let byte_len = len282.checked_mul(T::guest_size())283.ok_or(GuestError::PtrOverflow)?;284let region = Region {285start: offset,286len: byte_len,287};288let offset = usize::try_from(offset)?;289let byte_len = usize::try_from(byte_len)?;290291let range = offset..offset + byte_len;292let oob = match self {293GuestMemory::Unshared(b) => b.get(range.clone()).is_none(),294GuestMemory::Shared(b) => b.get(range.clone()).is_none(),295};296if oob {297Err(GuestError::PtrOutOfBounds(region))298} else {299Ok(range)300}301}302303/// Returns whether this is a shared memory or not.304pub fn is_shared_memory(&self) -> bool {305match self {306GuestMemory::Shared(_) => true,307GuestMemory::Unshared(_) => false,308}309}310}311312/// A *guest* pointer.313///314/// This type represents a pointer from the guest that points into host memory.315/// Internally a `GuestPtr` the offset into the memory that the pointer is316/// pointing at. At this time this is always a 32-bit offset so this is not317/// suitable for bindings where wasm has 64-bit addresses.318///319/// Presence of a [`GuestPtr`] does not imply any form of validity. Pointers can320/// be out-of-bounds, misaligned, etc. It is safe to construct a `GuestPtr` with321/// any offset at any time. Consider a `GuestPtr<T>` roughly equivalent to `*mut322/// T`.323///324/// ## Slices and Strings325///326/// Note that the type parameter does not need to implement the `Sized` trait,327/// so you can implement types such as this:328///329/// * `GuestPtr<str>` - a pointer to a guest string.330/// * `GuestPtr<[T]>` - a pointer to a guest array.331///332/// Note that generated bindings won't use these types so you'll have to333/// otherwise construct the types with `.cast()` or `.as_array()`. Unsized types334/// track both the pointer and length in guest memory.335///336/// ## Type parameter and pointee337///338/// The `T` type parameter is largely intended for more static safety in Rust as339/// well as having a better handle on what we're pointing to. A `GuestPtr<T>`,340/// however, does not necessarily literally imply a guest pointer pointing to341/// type `T`. Instead the [`GuestType`] trait is a layer of abstraction where342/// `GuestPtr<T>` may actually be a pointer to `U` in guest memory, but you can343/// construct a `T` from a `U`.344///345/// For example `GuestPtr<GuestPtr<T>>` is a valid type, but this is actually346/// more equivalent to `GuestPtr<u32>` because guest pointers are always347/// 32-bits. That being said you can create a `GuestPtr<T>` from a `u32`.348///349/// Additionally `GuestPtr<MyEnum>` will actually delegate, typically, to and350/// implementation which loads the underlying data as `GuestPtr<u8>` (or351/// similar) and then the bytes loaded are validated to fit within the352/// definition of `MyEnum` before `MyEnum` is returned.353///354/// For more information see the [`GuestMemory::read`] and355/// [`GuestMemory::write`] methods. In general though be extremely careful about356/// writing `unsafe` code when working with a `GuestPtr` if you're not using one357/// of the already-attached helper methods.358#[repr(transparent)]359pub struct GuestPtr<T: ?Sized + Pointee> {360pointer: T::Pointer,361}362363impl<T: ?Sized + Pointee> GuestPtr<T> {364/// Creates a new `GuestPtr` from the given `mem` and `pointer` values.365///366/// Note that for sized types like `u32`, `GuestPtr<T>`, etc, the `pointer`367/// value is a `u32` offset into guest memory. For slices and strings,368/// `pointer` is a `(u32, u32)` offset/length pair.369pub fn new(pointer: T::Pointer) -> GuestPtr<T> {370GuestPtr { pointer }371}372373/// Returns the offset of this pointer in guest memory.374///375/// Note that for sized types this returns a `u32`, but for slices and376/// strings it returns a `(u32, u32)` pointer/length pair.377pub fn offset(&self) -> T::Pointer {378self.pointer379}380381/// Casts this `GuestPtr` type to a different type.382///383/// This is a safe method which is useful for simply reinterpreting the type384/// parameter on this `GuestPtr`. Note that this is a safe method, where385/// again there's no guarantees about alignment, validity, in-bounds-ness,386/// etc of the returned pointer.387pub fn cast<U>(&self) -> GuestPtr<U>388where389U: Pointee<Pointer = T::Pointer> + ?Sized,390{391GuestPtr::new(self.pointer)392}393394/// Performs pointer arithmetic on this pointer, moving the pointer forward395/// `amt` slots.396///397/// This will either return the resulting pointer or `Err` if the pointer398/// arithmetic calculation would overflow around the end of the address399/// space.400pub fn add(&self, amt: u32) -> Result<GuestPtr<T>, GuestError>401where402T: GuestType + Pointee<Pointer = u32>,403{404let offset = amt405.checked_mul(T::guest_size())406.and_then(|o| self.pointer.checked_add(o));407let offset = match offset {408Some(o) => o,409None => return Err(GuestError::PtrOverflow),410};411Ok(GuestPtr::new(offset))412}413414/// Returns a `GuestPtr` for an array of `T`s using this pointer as the415/// base.416pub fn as_array(&self, elems: u32) -> GuestPtr<[T]>417where418T: GuestType + Pointee<Pointer = u32>,419{420GuestPtr::new((self.pointer, elems))421}422}423424impl<T> GuestPtr<[T]> {425/// For slices, specifically returns the relative pointer to the base of the426/// array.427///428/// This is similar to `<[T]>::as_ptr()`429pub fn offset_base(&self) -> u32 {430self.pointer.0431}432433/// For slices, returns the length of the slice, in elements.434pub fn len(&self) -> u32 {435self.pointer.1436}437438/// Returns an iterator over interior pointers.439///440/// Each item is a `Result` indicating whether it overflowed past the end of441/// the address space or not.442pub fn iter(&self) -> impl ExactSizeIterator<Item = Result<GuestPtr<T>, GuestError>> + '_443where444T: GuestType,445{446let base = self.as_ptr();447(0..self.len()).map(move |i| base.add(i))448}449450/// Returns a `GuestPtr` pointing to the base of the array for the interior451/// type `T`.452pub fn as_ptr(&self) -> GuestPtr<T> {453GuestPtr::new(self.offset_base())454}455456pub fn get(&self, index: u32) -> Option<GuestPtr<T>>457where458T: GuestType,459{460if index < self.len() {461Some(462self.as_ptr()463.add(index)464.expect("just performed bounds check"),465)466} else {467None468}469}470471pub fn get_range(&self, r: std::ops::Range<u32>) -> Option<GuestPtr<[T]>>472where473T: GuestType,474{475if r.end < r.start {476return None;477}478let range_length = r.end - r.start;479if r.start <= self.len() && r.end <= self.len() {480Some(481self.as_ptr()482.add(r.start)483.expect("just performed bounds check")484.as_array(range_length),485)486} else {487None488}489}490}491492impl GuestPtr<str> {493/// For strings, returns the relative pointer to the base of the string494/// allocation.495pub fn offset_base(&self) -> u32 {496self.pointer.0497}498499/// Returns the length, in bytes, of the string.500pub fn len(&self) -> u32 {501self.pointer.1502}503504/// Returns a raw pointer for the underlying slice of bytes that this505/// pointer points to.506pub fn as_bytes(&self) -> GuestPtr<[u8]> {507GuestPtr::new(self.pointer)508}509}510511impl<T: ?Sized + Pointee> Clone for GuestPtr<T> {512fn clone(&self) -> Self {513*self514}515}516517impl<T: ?Sized + Pointee> Copy for GuestPtr<T> {}518519impl<T: ?Sized + Pointee> fmt::Debug for GuestPtr<T> {520fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {521T::debug(self.pointer, f)522}523}524525impl<T: ?Sized + Pointee> PartialEq for GuestPtr<T> {526fn eq(&self, other: &Self) -> bool {527self.pointer == other.pointer528}529}530531mod private {532pub trait Sealed {}533impl<T> Sealed for T {}534impl<T> Sealed for [T] {}535impl Sealed for str {}536}537538/// Types that can be pointed to by `GuestPtr<T>`.539///540/// In essence everything can, and the only special-case is unsized types like541/// `str` and `[T]` which have special implementations.542pub trait Pointee: private::Sealed {543#[doc(hidden)]544type Pointer: Copy + PartialEq;545#[doc(hidden)]546fn debug(pointer: Self::Pointer, f: &mut fmt::Formatter) -> fmt::Result;547}548549impl<T> Pointee for T {550type Pointer = u32;551fn debug(pointer: Self::Pointer, f: &mut fmt::Formatter) -> fmt::Result {552write!(f, "*guest {pointer:#x}")553}554}555556impl<T> Pointee for [T] {557type Pointer = (u32, u32);558fn debug(pointer: Self::Pointer, f: &mut fmt::Formatter) -> fmt::Result {559write!(f, "*guest {:#x}/{}", pointer.0, pointer.1)560}561}562563impl Pointee for str {564type Pointer = (u32, u32);565fn debug(pointer: Self::Pointer, f: &mut fmt::Formatter) -> fmt::Result {566<[u8]>::debug(pointer, f)567}568}569570pub fn run_in_dummy_executor<F: std::future::Future>(future: F) -> Result<F::Output> {571use std::pin::Pin;572use std::task::{Context, Poll, RawWaker, RawWakerVTable, Waker};573574let mut f = Pin::from(Box::new(future));575let waker = dummy_waker();576let mut cx = Context::from_waker(&waker);577match f.as_mut().poll(&mut cx) {578Poll::Ready(val) => return Ok(val),579Poll::Pending => bail!(580"Cannot wait on pending future: must enable wiggle \"async\" future and execute on an async Store"581),582}583584fn dummy_waker() -> Waker {585return unsafe { Waker::from_raw(clone(5 as *const _)) };586587unsafe fn clone(ptr: *const ()) -> RawWaker {588assert_eq!(ptr as usize, 5);589const VTABLE: RawWakerVTable = RawWakerVTable::new(clone, wake, wake_by_ref, drop);590RawWaker::new(ptr, &VTABLE)591}592593unsafe fn wake(ptr: *const ()) {594assert_eq!(ptr as usize, 5);595}596597unsafe fn wake_by_ref(ptr: *const ()) {598assert_eq!(ptr as usize, 5);599}600601unsafe fn drop(ptr: *const ()) {602assert_eq!(ptr as usize, 5);603}604}605}606607608