use std::borrow::Cow;1use std::cell::UnsafeCell;2use std::fmt;3use std::mem;4use std::ops::Range;5use std::str;6use wasmtime_environ::error::{Result, bail};78pub use wiggle_macro::from_witx;910pub use wasmtime_environ::error;11pub use wiggle_macro::wasmtime_integration;1213pub use bitflags;1415#[cfg(feature = "wiggle_metadata")]16pub use witx;1718mod guest_error;19mod guest_type;20mod region;2122pub use tracing;2324pub use guest_error::GuestError;25pub use guest_type::{GuestErrorType, GuestType, GuestTypeTransparent};26pub use region::Region;2728#[cfg(feature = "wasmtime")]29pub mod wasmtime_crate {30pub use wasmtime::*;31}3233/// Representation of guest memory for `wiggle`-generated trait methods.34///35/// Guest memory is represented as an array of bytes. Memories are either36/// "unshared" or "shared". Unshared means that the host has exclusive access to37/// the entire array of memory. This allows safe borrows into wasm linear38/// memory. Shared memories can be modified at any time and are represented as39/// an array of `UnsafeCell<u8>`.40///41/// This is generated by the `wiggle` bindings macros.42pub enum GuestMemory<'a> {43Unshared(&'a mut [u8]),44Shared(&'a [UnsafeCell<u8>]),45}4647// manual impls are needed because of the `UnsafeCell` in the `Shared` branch48// but this otherwise upholds send/sync invariants.49unsafe impl Send for GuestMemory<'_> {}50unsafe impl Sync for GuestMemory<'_> {}5152impl<'a> GuestMemory<'a> {53/// Read a value from the provided pointer.54///55/// This method will delegate to `T`'s implementation of `read` which will56/// read a value from the `ptr` provided.57///58/// # Errors59///60/// An error is returned if `ptr` is out of bounds, misaligned, or otherwise61/// not valid to read from.62pub fn read<T>(&self, ptr: GuestPtr<T>) -> Result<T, GuestError>63where64T: GuestType,65{66T::read(self, ptr)67}6869/// Writes the `val` provided to the `ptr` provided.70///71/// This commit will write a `val` into a guest's linear memory. This will72/// delegate to `T`'s implementation of `write`.73///74/// # Errors75///76/// An error is returned if `ptr` is out of bounds, misaligned, or otherwise77/// not valid to read from.78pub fn write<T>(&mut self, ptr: GuestPtr<T>, val: T) -> Result<(), GuestError>79where80T: GuestType,81{82T::write(self, ptr, val)83}8485/// Acquires a slice or owned copy of the memory pointed to by `ptr`.86///87/// This method will attempt to borrow `ptr` directly from linear memory. If88/// memory is shared and cannot be borrowed directly then an owned copy is89/// returned instead.90///91/// # Errors92///93/// An error is returned if `ptr` is out of bounds, misaligned, or otherwise94/// not valid to read from.95pub fn as_cow(&self, ptr: GuestPtr<[u8]>) -> Result<Cow<'_, [u8]>, GuestError> {96match self {97GuestMemory::Unshared(_) => match self.as_slice(ptr)? {98Some(slice) => Ok(Cow::Borrowed(slice)),99None => unreachable!(),100},101GuestMemory::Shared(_) => Ok(Cow::Owned(self.to_vec(ptr)?)),102}103}104105/// Same as [`GuestMemory::as_cow`] but for strings.106///107/// # Errors108///109/// An error is returned if `ptr` is out of bounds, misaligned, or otherwise110/// not valid to read from.111pub fn as_cow_str(&self, ptr: GuestPtr<str>) -> Result<Cow<'_, str>, GuestError> {112match self.as_cow(ptr.cast::<[u8]>())? {113Cow::Owned(bytes) => Ok(Cow::Owned(114String::from_utf8(bytes).map_err(|e| e.utf8_error())?,115)),116Cow::Borrowed(bytes) => Ok(Cow::Borrowed(std::str::from_utf8(bytes)?)),117}118}119120/// Attempts to borrow a raw guest slice of memory pointed to by `ptr`.121///122/// This method will attempt to return a raw pointer into guest memory. This123/// can only be done for `Unshared` memories. A `Shared` memory will return124/// `Ok(None)` here.125///126/// # Errors127///128/// An error is returned if `ptr` is out of bounds, misaligned, or otherwise129/// not valid to read from.130pub fn as_slice(&self, ptr: GuestPtr<[u8]>) -> Result<Option<&[u8]>, GuestError> {131let range = self.validate_range::<u8>(ptr.pointer.0, ptr.pointer.1)?;132match self {133GuestMemory::Unshared(slice) => Ok(Some(&slice[range])),134GuestMemory::Shared(_) => Ok(None),135}136}137138/// Same as [`GuestMemory::as_slice`] but for strings.139pub fn as_str(&self, ptr: GuestPtr<str>) -> Result<Option<&str>, GuestError> {140match self.as_slice(ptr.cast())? {141Some(bytes) => Ok(Some(std::str::from_utf8(bytes)?)),142None => Ok(None),143}144}145146/// Attempts return `ptr` as a raw slice of mutable bytes in wasm linear147/// memory.148///149/// Like [`GuestMemory::as_slice`] this only works for `Unshared` memories150/// and will not work for `Shared` memories.151pub fn as_slice_mut(&mut self, ptr: GuestPtr<[u8]>) -> Result<Option<&mut [u8]>, GuestError> {152let range = self.validate_range::<u8>(ptr.pointer.0, ptr.pointer.1)?;153match self {154GuestMemory::Unshared(slice) => Ok(Some(&mut slice[range])),155GuestMemory::Shared(_) => Ok(None),156}157}158159/// Copies the data in the guest region into a [`Vec`].160///161/// This is useful when one cannot use [`GuestMemory::as_slice`], e.g., when162/// pointing to a region of WebAssembly shared memory.163pub fn to_vec<T>(&self, ptr: GuestPtr<[T]>) -> Result<Vec<T>, GuestError>164where165T: GuestTypeTransparent + Copy,166{167let guest = self.validate_size_align::<T>(ptr.pointer.0, ptr.pointer.1)?;168let mut host = Vec::with_capacity(guest.len());169170// SAFETY: The `guest_slice` variable is already a valid pointer into171// the guest's memory, and it may or may not be a pointer into shared172// memory. We can't naively use `.to_vec(..)` which could introduce data173// races but all that needs to happen is to copy data into our local174// `vec` as all the data is `Copy` and transparent anyway. For this175// purpose the `ptr::copy` function should be sufficient for copying176// over all the data.177//178// TODO: audit that this use of `std::ptr::copy` is safe with shared179// memory (https://github.com/bytecodealliance/wasmtime/issues/4203)180unsafe {181std::ptr::copy(guest.as_ptr().cast(), host.as_mut_ptr(), guest.len());182host.set_len(guest.len());183}184Ok(host)185}186187/// Copies the data pointed to by `slice` into this guest region.188///189/// This method is a *safe* method to copy data from the host to the guest.190/// This requires that `self` and `slice` have the same length. The pointee191/// type `T` requires the [`GuestTypeTransparent`] trait which is an192/// assertion that the representation on the host and on the guest is the193/// same.194///195/// # Errors196///197/// Returns an error if this guest pointer is out of bounds or if the length198/// of this guest pointer is not equal to the length of the slice provided.199pub fn copy_from_slice<T>(&mut self, slice: &[T], ptr: GuestPtr<[T]>) -> Result<(), GuestError>200where201T: GuestTypeTransparent + Copy,202{203if usize::try_from(ptr.len())? != slice.len() {204return Err(GuestError::SliceLengthsDiffer);205}206if slice.is_empty() {207return Ok(());208}209210let guest = self.validate_size_align::<T>(ptr.pointer.0, ptr.pointer.1)?;211212// SAFETY: in the shared memory case, we copy and accept that213// the guest data may be concurrently modified. TODO: audit that214// this use of `std::ptr::copy` is safe with shared memory215// (https://github.com/bytecodealliance/wasmtime/issues/4203)216//217// Also note that the validity of `guest_slice` has already been218// determined by the `as_unsafe_slice_mut` call above.219assert_eq!(guest.len(), slice.len());220unsafe {221let guest: &[UnsafeCell<T>] = guest;222let guest: *const UnsafeCell<T> = guest.as_ptr();223let guest = guest.cast_mut().cast::<T>();224std::ptr::copy(slice.as_ptr(), guest, slice.len());225}226Ok(())227}228229/// Validates a guest-relative pointer given various attributes, and returns230/// the corresponding host pointer.231///232/// * `mem` - this is the guest memory being accessed.233/// * `offset` - this is the guest-relative pointer, an offset from the234/// base.235/// * `len` - this is the number of length, in units of `T`, to return236/// in the resulting slice.237///238/// If the parameters are valid then this function will return a slice into239/// `mem` for units of `T`, assuming everything is in-bounds and properly240/// aligned. Additionally the byte-based `Region` is returned, used for borrows241/// later on.242fn validate_size_align<T>(&self, offset: u32, len: u32) -> Result<&[UnsafeCell<T>], GuestError>243where244T: GuestTypeTransparent,245{246let range = self.validate_range::<T>(offset, len)?;247let cells = match self {248GuestMemory::Unshared(s) => {249let s: &[u8] = s;250unsafe { &*(s as *const [u8] as *const [UnsafeCell<u8>]) }251}252GuestMemory::Shared(s) => s,253};254let memory = &cells[range.clone()];255256// ... and then align it to `T`, failing if either the head or tail slices257// are nonzero in length. This `unsafe` here is from the standard library258// and should be ok since the input slice is `UnsafeCell<u8>` and the output259// slice is `UnsafeCell<T>`, meaning the only guarantee of the output is260// that it's valid addressable memory, still unsafe to actually access.261assert!(mem::align_of::<T>() <= T::guest_align());262let (start, mid, end) = unsafe { memory.align_to() };263if start.len() > 0 || end.len() > 0 {264let region = Region {265start: range.start as u32,266len: range.len() as u32,267};268return Err(GuestError::PtrNotAligned(region, T::guest_align() as u32));269}270Ok(mid)271}272273fn validate_range<T>(&self, offset: u32, len: u32) -> Result<Range<usize>, GuestError>274where275T: GuestTypeTransparent,276{277let byte_len = len278.checked_mul(T::guest_size())279.ok_or(GuestError::PtrOverflow)?;280let region = Region {281start: offset,282len: byte_len,283};284let offset = usize::try_from(offset)?;285let byte_len = usize::try_from(byte_len)?;286287let range = offset..offset + byte_len;288let oob = match self {289GuestMemory::Unshared(b) => b.get(range.clone()).is_none(),290GuestMemory::Shared(b) => b.get(range.clone()).is_none(),291};292if oob {293Err(GuestError::PtrOutOfBounds(region))294} else {295Ok(range)296}297}298299/// Returns whether this is a shared memory or not.300pub fn is_shared_memory(&self) -> bool {301match self {302GuestMemory::Shared(_) => true,303GuestMemory::Unshared(_) => false,304}305}306}307308/// A *guest* pointer.309///310/// This type represents a pointer from the guest that points into host memory.311/// Internally a `GuestPtr` the offset into the memory that the pointer is312/// pointing at. At this time this is always a 32-bit offset so this is not313/// suitable for bindings where wasm has 64-bit addresses.314///315/// Presence of a [`GuestPtr`] does not imply any form of validity. Pointers can316/// be out-of-bounds, misaligned, etc. It is safe to construct a `GuestPtr` with317/// any offset at any time. Consider a `GuestPtr<T>` roughly equivalent to `*mut318/// T`.319///320/// ## Slices and Strings321///322/// Note that the type parameter does not need to implement the `Sized` trait,323/// so you can implement types such as this:324///325/// * `GuestPtr<str>` - a pointer to a guest string.326/// * `GuestPtr<[T]>` - a pointer to a guest array.327///328/// Note that generated bindings won't use these types so you'll have to329/// otherwise construct the types with `.cast()` or `.as_array()`. Unsized types330/// track both the pointer and length in guest memory.331///332/// ## Type parameter and pointee333///334/// The `T` type parameter is largely intended for more static safety in Rust as335/// well as having a better handle on what we're pointing to. A `GuestPtr<T>`,336/// however, does not necessarily literally imply a guest pointer pointing to337/// type `T`. Instead the [`GuestType`] trait is a layer of abstraction where338/// `GuestPtr<T>` may actually be a pointer to `U` in guest memory, but you can339/// construct a `T` from a `U`.340///341/// For example `GuestPtr<GuestPtr<T>>` is a valid type, but this is actually342/// more equivalent to `GuestPtr<u32>` because guest pointers are always343/// 32-bits. That being said you can create a `GuestPtr<T>` from a `u32`.344///345/// Additionally `GuestPtr<MyEnum>` will actually delegate, typically, to and346/// implementation which loads the underlying data as `GuestPtr<u8>` (or347/// similar) and then the bytes loaded are validated to fit within the348/// definition of `MyEnum` before `MyEnum` is returned.349///350/// For more information see the [`GuestMemory::read`] and351/// [`GuestMemory::write`] methods. In general though be extremely careful about352/// writing `unsafe` code when working with a `GuestPtr` if you're not using one353/// of the already-attached helper methods.354#[repr(transparent)]355pub struct GuestPtr<T: ?Sized + Pointee> {356pointer: T::Pointer,357}358359impl<T: ?Sized + Pointee> GuestPtr<T> {360/// Creates a new `GuestPtr` from the given `mem` and `pointer` values.361///362/// Note that for sized types like `u32`, `GuestPtr<T>`, etc, the `pointer`363/// value is a `u32` offset into guest memory. For slices and strings,364/// `pointer` is a `(u32, u32)` offset/length pair.365pub fn new(pointer: T::Pointer) -> GuestPtr<T> {366GuestPtr { pointer }367}368369/// Returns the offset of this pointer in guest memory.370///371/// Note that for sized types this returns a `u32`, but for slices and372/// strings it returns a `(u32, u32)` pointer/length pair.373pub fn offset(&self) -> T::Pointer {374self.pointer375}376377/// Casts this `GuestPtr` type to a different type.378///379/// This is a safe method which is useful for simply reinterpreting the type380/// parameter on this `GuestPtr`. Note that this is a safe method, where381/// again there's no guarantees about alignment, validity, in-bounds-ness,382/// etc of the returned pointer.383pub fn cast<U>(&self) -> GuestPtr<U>384where385U: Pointee<Pointer = T::Pointer> + ?Sized,386{387GuestPtr::new(self.pointer)388}389390/// Performs pointer arithmetic on this pointer, moving the pointer forward391/// `amt` slots.392///393/// This will either return the resulting pointer or `Err` if the pointer394/// arithmetic calculation would overflow around the end of the address395/// space.396pub fn add(&self, amt: u32) -> Result<GuestPtr<T>, GuestError>397where398T: GuestType + Pointee<Pointer = u32>,399{400let offset = amt401.checked_mul(T::guest_size())402.and_then(|o| self.pointer.checked_add(o));403let offset = match offset {404Some(o) => o,405None => return Err(GuestError::PtrOverflow),406};407Ok(GuestPtr::new(offset))408}409410/// Returns a `GuestPtr` for an array of `T`s using this pointer as the411/// base.412pub fn as_array(&self, elems: u32) -> GuestPtr<[T]>413where414T: GuestType + Pointee<Pointer = u32>,415{416GuestPtr::new((self.pointer, elems))417}418}419420impl<T> GuestPtr<[T]> {421/// For slices, specifically returns the relative pointer to the base of the422/// array.423///424/// This is similar to `<[T]>::as_ptr()`425pub fn offset_base(&self) -> u32 {426self.pointer.0427}428429/// For slices, returns the length of the slice, in elements.430pub fn len(&self) -> u32 {431self.pointer.1432}433434/// Returns an iterator over interior pointers.435///436/// Each item is a `Result` indicating whether it overflowed past the end of437/// the address space or not.438pub fn iter(&self) -> impl ExactSizeIterator<Item = Result<GuestPtr<T>, GuestError>> + '_439where440T: GuestType,441{442let base = self.as_ptr();443(0..self.len()).map(move |i| base.add(i))444}445446/// Returns a `GuestPtr` pointing to the base of the array for the interior447/// type `T`.448pub fn as_ptr(&self) -> GuestPtr<T> {449GuestPtr::new(self.offset_base())450}451452pub fn get(&self, index: u32) -> Option<GuestPtr<T>>453where454T: GuestType,455{456if index < self.len() {457Some(458self.as_ptr()459.add(index)460.expect("just performed bounds check"),461)462} else {463None464}465}466467pub fn get_range(&self, r: std::ops::Range<u32>) -> Option<GuestPtr<[T]>>468where469T: GuestType,470{471if r.end < r.start {472return None;473}474let range_length = r.end - r.start;475if r.start <= self.len() && r.end <= self.len() {476Some(477self.as_ptr()478.add(r.start)479.expect("just performed bounds check")480.as_array(range_length),481)482} else {483None484}485}486}487488impl GuestPtr<str> {489/// For strings, returns the relative pointer to the base of the string490/// allocation.491pub fn offset_base(&self) -> u32 {492self.pointer.0493}494495/// Returns the length, in bytes, of the string.496pub fn len(&self) -> u32 {497self.pointer.1498}499500/// Returns a raw pointer for the underlying slice of bytes that this501/// pointer points to.502pub fn as_bytes(&self) -> GuestPtr<[u8]> {503GuestPtr::new(self.pointer)504}505}506507impl<T: ?Sized + Pointee> Clone for GuestPtr<T> {508fn clone(&self) -> Self {509*self510}511}512513impl<T: ?Sized + Pointee> Copy for GuestPtr<T> {}514515impl<T: ?Sized + Pointee> fmt::Debug for GuestPtr<T> {516fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {517T::debug(self.pointer, f)518}519}520521impl<T: ?Sized + Pointee> PartialEq for GuestPtr<T> {522fn eq(&self, other: &Self) -> bool {523self.pointer == other.pointer524}525}526527mod private {528pub trait Sealed {}529impl<T> Sealed for T {}530impl<T> Sealed for [T] {}531impl Sealed for str {}532}533534/// Types that can be pointed to by `GuestPtr<T>`.535///536/// In essence everything can, and the only special-case is unsized types like537/// `str` and `[T]` which have special implementations.538pub trait Pointee: private::Sealed {539#[doc(hidden)]540type Pointer: Copy + PartialEq;541#[doc(hidden)]542fn debug(pointer: Self::Pointer, f: &mut fmt::Formatter) -> fmt::Result;543}544545impl<T> Pointee for T {546type Pointer = u32;547fn debug(pointer: Self::Pointer, f: &mut fmt::Formatter) -> fmt::Result {548write!(f, "*guest {pointer:#x}")549}550}551552impl<T> Pointee for [T] {553type Pointer = (u32, u32);554fn debug(pointer: Self::Pointer, f: &mut fmt::Formatter) -> fmt::Result {555write!(f, "*guest {:#x}/{}", pointer.0, pointer.1)556}557}558559impl Pointee for str {560type Pointer = (u32, u32);561fn debug(pointer: Self::Pointer, f: &mut fmt::Formatter) -> fmt::Result {562<[u8]>::debug(pointer, f)563}564}565566pub fn run_in_dummy_executor<F: std::future::Future>(future: F) -> Result<F::Output> {567use std::pin::Pin;568use std::task::{Context, Poll, RawWaker, RawWakerVTable, Waker};569570let mut f = Pin::from(Box::new(future));571let waker = dummy_waker();572let mut cx = Context::from_waker(&waker);573match f.as_mut().poll(&mut cx) {574Poll::Ready(val) => return Ok(val),575Poll::Pending => bail!(576"Cannot wait on pending future: must enable wiggle \"async\" future and execute on an async Store"577),578}579580fn dummy_waker() -> Waker {581return unsafe { Waker::from_raw(clone(5 as *const _)) };582583unsafe fn clone(ptr: *const ()) -> RawWaker {584assert_eq!(ptr as usize, 5);585const VTABLE: RawWakerVTable = RawWakerVTable::new(clone, wake, wake_by_ref, drop);586RawWaker::new(ptr, &VTABLE)587}588589unsafe fn wake(ptr: *const ()) {590assert_eq!(ptr as usize, 5);591}592593unsafe fn wake_by_ref(ptr: *const ()) {594assert_eq!(ptr as usize, 5);595}596597unsafe fn drop(ptr: *const ()) {598assert_eq!(ptr as usize, 5);599}600}601}602603604