Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
bytecodealliance
GitHub Repository: bytecodealliance/wasmtime
Path: blob/main/crates/wiggle/src/lib.rs
3069 views
1
use std::borrow::Cow;
2
use std::cell::UnsafeCell;
3
use std::fmt;
4
use std::mem;
5
use std::ops::Range;
6
use std::str;
7
use wasmtime_environ::error::{Result, bail};
8
9
pub use wiggle_macro::from_witx;
10
11
pub use wasmtime_environ::error;
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 guest_error;
20
mod guest_type;
21
mod region;
22
23
pub use tracing;
24
25
pub use guest_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
604