1 use anyhow::{Result, bail}; 2 use std::borrow::Cow; 3 use std::cell::UnsafeCell; 4 use std::fmt; 5 use std::mem; 6 use std::ops::Range; 7 use std::str; 8 9 pub use wiggle_macro::from_witx; 10 11 pub use anyhow; 12 pub use wiggle_macro::wasmtime_integration; 13 14 pub use bitflags; 15 16 #[cfg(feature = "wiggle_metadata")] 17 pub use witx; 18 19 mod error; 20 mod guest_type; 21 mod region; 22 23 pub use tracing; 24 25 pub use error::GuestError; 26 pub use guest_type::{GuestErrorType, GuestType, GuestTypeTransparent}; 27 pub use region::Region; 28 29 #[cfg(feature = "wasmtime")] 30 pub 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. 43 pub 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. 50 unsafe impl Send for GuestMemory<'_> {} 51 unsafe impl Sync for GuestMemory<'_> {} 52 53 impl<'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)] 356 pub struct GuestPtr<T: ?Sized + Pointee> { 357 pointer: T::Pointer, 358 } 359 360 impl<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 421 impl<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 489 impl 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 508 impl<T: ?Sized + Pointee> Clone for GuestPtr<T> { 509 fn clone(&self) -> Self { 510 *self 511 } 512 } 513 514 impl<T: ?Sized + Pointee> Copy for GuestPtr<T> {} 515 516 impl<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 522 impl<T: ?Sized + Pointee> PartialEq for GuestPtr<T> { 523 fn eq(&self, other: &Self) -> bool { 524 self.pointer == other.pointer 525 } 526 } 527 528 mod 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. 539 pub 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 546 impl<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 553 impl<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 560 impl 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 567 pub 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 } 603