Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
bytecodealliance
GitHub Repository: bytecodealliance/wasmtime
Path: blob/main/crates/wiggle/src/lib.rs
1691 views
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::{async_trait, 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
pub mod async_trait_crate {
30
pub use async_trait::*;
31
}
32
33
#[cfg(feature = "wasmtime")]
34
pub 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.
47
pub 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.
54
unsafe impl Send for GuestMemory<'_> {}
55
unsafe impl Sync for GuestMemory<'_> {}
56
57
impl<'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)]
360
pub struct GuestPtr<T: ?Sized + Pointee> {
361
pointer: T::Pointer,
362
}
363
364
impl<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
425
impl<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
493
impl 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
512
impl<T: ?Sized + Pointee> Clone for GuestPtr<T> {
513
fn clone(&self) -> Self {
514
*self
515
}
516
}
517
518
impl<T: ?Sized + Pointee> Copy for GuestPtr<T> {}
519
520
impl<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
526
impl<T: ?Sized + Pointee> PartialEq for GuestPtr<T> {
527
fn eq(&self, other: &Self) -> bool {
528
self.pointer == other.pointer
529
}
530
}
531
532
mod 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.
543
pub 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
550
impl<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
557
impl<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
564
impl 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
571
pub 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 => bail!(
581
"Cannot wait on pending future: must enable wiggle \"async\" future and execute on an async Store"
582
),
583
}
584
585
fn dummy_waker() -> Waker {
586
return unsafe { Waker::from_raw(clone(5 as *const _)) };
587
588
unsafe fn clone(ptr: *const ()) -> RawWaker {
589
assert_eq!(ptr as usize, 5);
590
const VTABLE: RawWakerVTable = RawWakerVTable::new(clone, wake, wake_by_ref, drop);
591
RawWaker::new(ptr, &VTABLE)
592
}
593
594
unsafe fn wake(ptr: *const ()) {
595
assert_eq!(ptr as usize, 5);
596
}
597
598
unsafe fn wake_by_ref(ptr: *const ()) {
599
assert_eq!(ptr as usize, 5);
600
}
601
602
unsafe fn drop(ptr: *const ()) {
603
assert_eq!(ptr as usize, 5);
604
}
605
}
606
}
607
608