diff --git a/crates/test-programs/artifacts/Cargo.toml b/crates/test-programs/artifacts/Cargo.toml index 3228ab8119a9..614f6a7ab361 100644 --- a/crates/test-programs/artifacts/Cargo.toml +++ b/crates/test-programs/artifacts/Cargo.toml @@ -10,7 +10,7 @@ license = "Apache-2.0 WITH LLVM-exception" workspace = true [dependencies] -wasmtime = { workspace = true, features = ['incremental-cache', 'cranelift'] } +wasmtime = { workspace = true, features = ['incremental-cache', 'cranelift', 'component-model'] } [build-dependencies] heck = { workspace = true } diff --git a/crates/wasi-common/src/snapshots/preview_0.rs b/crates/wasi-common/src/snapshots/preview_0.rs index 5fbb97521b96..84c6905a753d 100644 --- a/crates/wasi-common/src/snapshots/preview_0.rs +++ b/crates/wasi-common/src/snapshots/preview_0.rs @@ -5,13 +5,10 @@ use crate::sched::{ }; use crate::snapshots::preview_1::types as snapshot1_types; use crate::snapshots::preview_1::wasi_snapshot_preview1::WasiSnapshotPreview1 as Snapshot1; -use crate::snapshots::preview_1::MAX_SHARED_BUFFER_SIZE; use crate::{ErrorExt, WasiCtx}; use cap_std::time::Duration; use std::collections::HashSet; -use std::io::{IoSlice, IoSliceMut}; -use std::ops::Deref; -use wiggle::GuestPtr; +use wiggle::{GuestMemory, GuestPtr}; wiggle::from_witx!({ witx: ["$CARGO_MANIFEST_DIR/witx/preview0/wasi_unstable.witx"], @@ -388,100 +385,128 @@ convert_flags_bidirectional!( // performing the no-op type conversions along the way. #[wiggle::async_trait] impl wasi_unstable::WasiUnstable for WasiCtx { - async fn args_get<'a>( + async fn args_get( &mut self, - argv: &GuestPtr<'a, GuestPtr<'a, u8>>, - argv_buf: &GuestPtr<'a, u8>, + memory: &mut GuestMemory<'_>, + argv: GuestPtr>, + argv_buf: GuestPtr, ) -> Result<(), Error> { - Snapshot1::args_get(self, argv, argv_buf).await?; + Snapshot1::args_get(self, memory, argv, argv_buf).await?; Ok(()) } - async fn args_sizes_get(&mut self) -> Result<(types::Size, types::Size), Error> { - let s = Snapshot1::args_sizes_get(self).await?; + async fn args_sizes_get( + &mut self, + memory: &mut GuestMemory<'_>, + ) -> Result<(types::Size, types::Size), Error> { + let s = Snapshot1::args_sizes_get(self, memory).await?; Ok(s) } - async fn environ_get<'a>( + async fn environ_get( &mut self, - environ: &GuestPtr<'a, GuestPtr<'a, u8>>, - environ_buf: &GuestPtr<'a, u8>, + memory: &mut GuestMemory<'_>, + environ: GuestPtr>, + environ_buf: GuestPtr, ) -> Result<(), Error> { - Snapshot1::environ_get(self, environ, environ_buf).await?; + Snapshot1::environ_get(self, memory, environ, environ_buf).await?; Ok(()) } - async fn environ_sizes_get(&mut self) -> Result<(types::Size, types::Size), Error> { - let s = Snapshot1::environ_sizes_get(self).await?; + async fn environ_sizes_get( + &mut self, + memory: &mut GuestMemory<'_>, + ) -> Result<(types::Size, types::Size), Error> { + let s = Snapshot1::environ_sizes_get(self, memory).await?; Ok(s) } - async fn clock_res_get(&mut self, id: types::Clockid) -> Result { - let t = Snapshot1::clock_res_get(self, id.into()).await?; + async fn clock_res_get( + &mut self, + memory: &mut GuestMemory<'_>, + id: types::Clockid, + ) -> Result { + let t = Snapshot1::clock_res_get(self, memory, id.into()).await?; Ok(t) } async fn clock_time_get( &mut self, + memory: &mut GuestMemory<'_>, id: types::Clockid, precision: types::Timestamp, ) -> Result { - let t = Snapshot1::clock_time_get(self, id.into(), precision).await?; + let t = Snapshot1::clock_time_get(self, memory, id.into(), precision).await?; Ok(t) } async fn fd_advise( &mut self, + memory: &mut GuestMemory<'_>, fd: types::Fd, offset: types::Filesize, len: types::Filesize, advice: types::Advice, ) -> Result<(), Error> { - Snapshot1::fd_advise(self, fd.into(), offset, len, advice.into()).await?; + Snapshot1::fd_advise(self, memory, fd.into(), offset, len, advice.into()).await?; Ok(()) } async fn fd_allocate( &mut self, + memory: &mut GuestMemory<'_>, fd: types::Fd, offset: types::Filesize, len: types::Filesize, ) -> Result<(), Error> { - Snapshot1::fd_allocate(self, fd.into(), offset, len).await?; + Snapshot1::fd_allocate(self, memory, fd.into(), offset, len).await?; Ok(()) } - async fn fd_close(&mut self, fd: types::Fd) -> Result<(), Error> { - Snapshot1::fd_close(self, fd.into()).await?; + async fn fd_close(&mut self, memory: &mut GuestMemory<'_>, fd: types::Fd) -> Result<(), Error> { + Snapshot1::fd_close(self, memory, fd.into()).await?; Ok(()) } - async fn fd_datasync(&mut self, fd: types::Fd) -> Result<(), Error> { - Snapshot1::fd_datasync(self, fd.into()).await?; + async fn fd_datasync( + &mut self, + memory: &mut GuestMemory<'_>, + fd: types::Fd, + ) -> Result<(), Error> { + Snapshot1::fd_datasync(self, memory, fd.into()).await?; Ok(()) } - async fn fd_fdstat_get(&mut self, fd: types::Fd) -> Result { - Ok(Snapshot1::fd_fdstat_get(self, fd.into()).await?.into()) + async fn fd_fdstat_get( + &mut self, + memory: &mut GuestMemory<'_>, + fd: types::Fd, + ) -> Result { + Ok(Snapshot1::fd_fdstat_get(self, memory, fd.into()) + .await? + .into()) } async fn fd_fdstat_set_flags( &mut self, + memory: &mut GuestMemory<'_>, fd: types::Fd, flags: types::Fdflags, ) -> Result<(), Error> { - Snapshot1::fd_fdstat_set_flags(self, fd.into(), flags.into()).await?; + Snapshot1::fd_fdstat_set_flags(self, memory, fd.into(), flags.into()).await?; Ok(()) } async fn fd_fdstat_set_rights( &mut self, + memory: &mut GuestMemory<'_>, fd: types::Fd, fs_rights_base: types::Rights, fs_rights_inheriting: types::Rights, ) -> Result<(), Error> { Snapshot1::fd_fdstat_set_rights( self, + memory, fd.into(), fs_rights_base.into(), fs_rights_inheriting.into(), @@ -490,27 +515,36 @@ impl wasi_unstable::WasiUnstable for WasiCtx { Ok(()) } - async fn fd_filestat_get(&mut self, fd: types::Fd) -> Result { - Ok(Snapshot1::fd_filestat_get(self, fd.into()).await?.into()) + async fn fd_filestat_get( + &mut self, + memory: &mut GuestMemory<'_>, + fd: types::Fd, + ) -> Result { + Ok(Snapshot1::fd_filestat_get(self, memory, fd.into()) + .await? + .into()) } async fn fd_filestat_set_size( &mut self, + memory: &mut GuestMemory<'_>, fd: types::Fd, size: types::Filesize, ) -> Result<(), Error> { - Snapshot1::fd_filestat_set_size(self, fd.into(), size).await?; + Snapshot1::fd_filestat_set_size(self, memory, fd.into(), size).await?; Ok(()) } async fn fd_filestat_set_times( &mut self, + memory: &mut GuestMemory<'_>, fd: types::Fd, atim: types::Timestamp, mtim: types::Timestamp, fst_flags: types::Fstflags, ) -> Result<(), Error> { - Snapshot1::fd_filestat_set_times(self, fd.into(), atim, mtim, fst_flags.into()).await?; + Snapshot1::fd_filestat_set_times(self, memory, fd.into(), atim, mtim, fst_flags.into()) + .await?; Ok(()) } @@ -521,283 +555,149 @@ impl wasi_unstable::WasiUnstable for WasiCtx { // The implementations are identical, but the `types::` in scope locally is different. // The bodies of these functions is mostly about converting the GuestPtr and types::-based // representation to a std::io::IoSlice(Mut) representation. + // + // TODO: update this ^ - async fn fd_read<'a>( + async fn fd_read( &mut self, + memory: &mut GuestMemory<'_>, fd: types::Fd, - iovs: &types::IovecArray<'a>, + iovs: types::IovecArray, ) -> Result { - let f = self.table().get_file(u32::from(fd))?; - - let iovs: Vec> = iovs - .iter() - .map(|iov_ptr| { - let iov_ptr = iov_ptr?; - let iov: types::Iovec = iov_ptr.read()?; - Ok(iov.buf.as_array(iov.buf_len)) - }) - .collect::>()?; - - // If the first iov structure is from shared memory we can safely assume - // all the rest will be. We then read into memory based on the memory's - // shared-ness: - // - if not shared, we copy directly into the Wasm memory - // - if shared, we use an intermediate buffer; this avoids Rust unsafety - // due to holding on to a `&mut [u8]` of Wasm memory when we cannot - // guarantee the `&mut` exclusivity--other threads could be modifying - // the data as this functions writes to it. Though likely there is no - // issue with OS writing to io structs in multi-threaded scenarios, - // since we do not know here if `&dyn WasiFile` does anything else - // (e.g., read), we cautiously incur some performance overhead by - // copying twice. - let is_shared_memory = iovs - .iter() - .next() - .and_then(|s| Some(s.is_shared_memory())) - .unwrap_or(false); - let bytes_read: u64 = if is_shared_memory { - // For shared memory, read into an intermediate buffer. Only the - // first iov will be filled and even then the read is capped by the - // `MAX_SHARED_BUFFER_SIZE`, so users are expected to re-call. - let iov = iovs.into_iter().next(); - if let Some(iov) = iov { - let mut buffer = vec![0; (iov.len() as usize).min(MAX_SHARED_BUFFER_SIZE)]; - let bytes_read = f - .file - .read_vectored(&mut [IoSliceMut::new(&mut buffer)]) - .await?; - iov.get_range(0..bytes_read.try_into()?) - .expect("it should always be possible to slice the iov smaller") - .copy_from_slice(&buffer[0..bytes_read.try_into()?])?; - bytes_read - } else { - return Ok(0); - } - } else { - // Convert all of the unsafe guest slices to safe ones--this uses - // Wiggle's internal borrow checker to ensure no overlaps. We assume - // here that, because the memory is not shared, there are no other - // threads to access it while it is written to. - let mut guest_slices: Vec> = iovs - .into_iter() - .map(|iov| Ok(iov.as_slice_mut()?.unwrap())) - .collect::>()?; - - // Read directly into the Wasm memory. - let mut ioslices: Vec = guest_slices - .iter_mut() - .map(|s| IoSliceMut::new(&mut *s)) - .collect(); - f.file.read_vectored(&mut ioslices).await? - }; - - Ok(types::Size::try_from(bytes_read)?) - } - - async fn fd_pread<'a>( + Ok(Snapshot1::fd_read(self, memory, fd.into(), iovs.cast()).await?) + } + + async fn fd_pread( &mut self, + memory: &mut GuestMemory<'_>, fd: types::Fd, - iovs: &types::IovecArray<'a>, + iovs: types::IovecArray, offset: types::Filesize, ) -> Result { - let f = self.table().get_file(u32::from(fd))?; - - let iovs: Vec> = iovs - .iter() - .map(|iov_ptr| { - let iov_ptr = iov_ptr?; - let iov: types::Iovec = iov_ptr.read()?; - Ok(iov.buf.as_array(iov.buf_len)) - }) - .collect::>()?; - - // If the first iov structure is from shared memory we can safely assume - // all the rest will be. We then read into memory based on the memory's - // shared-ness: - // - if not shared, we copy directly into the Wasm memory - // - if shared, we use an intermediate buffer; this avoids Rust unsafety - // due to holding on to a `&mut [u8]` of Wasm memory when we cannot - // guarantee the `&mut` exclusivity--other threads could be modifying - // the data as this functions writes to it. Though likely there is no - // issue with OS writing to io structs in multi-threaded scenarios, - // since we do not know here if `&dyn WasiFile` does anything else - // (e.g., read), we cautiously incur some performance overhead by - // copying twice. - let is_shared_memory = iovs - .iter() - .next() - .and_then(|s| Some(s.is_shared_memory())) - .unwrap_or(false); - let bytes_read: u64 = if is_shared_memory { - // For shared memory, read into an intermediate buffer. Only the - // first iov will be filled and even then the read is capped by the - // `MAX_SHARED_BUFFER_SIZE`, so users are expected to re-call. - let iov = iovs.into_iter().next(); - if let Some(iov) = iov { - let mut buffer = vec![0; (iov.len() as usize).min(MAX_SHARED_BUFFER_SIZE)]; - let bytes_read = f - .file - .read_vectored_at(&mut [IoSliceMut::new(&mut buffer)], offset) - .await?; - iov.get_range(0..bytes_read.try_into()?) - .expect("it should always be possible to slice the iov smaller") - .copy_from_slice(&buffer[0..bytes_read.try_into()?])?; - bytes_read - } else { - return Ok(0); - } - } else { - // Convert all of the unsafe guest slices to safe ones--this uses - // Wiggle's internal borrow checker to ensure no overlaps. We assume - // here that, because the memory is not shared, there are no other - // threads to access it while it is written to. - let mut guest_slices: Vec> = iovs - .into_iter() - .map(|iov| Ok(iov.as_slice_mut()?.unwrap())) - .collect::>()?; - - // Read directly into the Wasm memory. - let mut ioslices: Vec = guest_slices - .iter_mut() - .map(|s| IoSliceMut::new(&mut *s)) - .collect(); - f.file.read_vectored_at(&mut ioslices, offset).await? - }; - - Ok(types::Size::try_from(bytes_read)?) - } - - async fn fd_write<'a>( + Ok(Snapshot1::fd_pread(self, memory, fd.into(), iovs.cast(), offset).await?) + } + + async fn fd_write( &mut self, + memory: &mut GuestMemory<'_>, fd: types::Fd, - ciovs: &types::CiovecArray<'a>, + ciovs: types::CiovecArray, ) -> Result { - let f = self.table().get_file(u32::from(fd))?; - - let guest_slices: Vec> = ciovs - .iter() - .map(|iov_ptr| { - let iov_ptr = iov_ptr?; - let iov: types::Ciovec = iov_ptr.read()?; - Ok(iov.buf.as_array(iov.buf_len).as_cow()?) - }) - .collect::>()?; - - let ioslices: Vec = guest_slices - .iter() - .map(|s| IoSlice::new(s.deref())) - .collect(); - let bytes_written = f.file.write_vectored(&ioslices).await?; - - Ok(types::Size::try_from(bytes_written)?) + Ok(Snapshot1::fd_write(self, memory, fd.into(), ciovs.cast()).await?) } - async fn fd_pwrite<'a>( + async fn fd_pwrite( &mut self, + memory: &mut GuestMemory<'_>, fd: types::Fd, - ciovs: &types::CiovecArray<'a>, + ciovs: types::CiovecArray, offset: types::Filesize, ) -> Result { - let f = self.table().get_file(u32::from(fd))?; - - let guest_slices: Vec> = ciovs - .iter() - .map(|iov_ptr| { - let iov_ptr = iov_ptr?; - let iov: types::Ciovec = iov_ptr.read()?; - Ok(iov.buf.as_array(iov.buf_len).as_cow()?) - }) - .collect::>()?; - - let ioslices: Vec = guest_slices - .iter() - .map(|s| IoSlice::new(s.deref())) - .collect(); - let bytes_written = f.file.write_vectored_at(&ioslices, offset).await?; - - Ok(types::Size::try_from(bytes_written)?) + Ok(Snapshot1::fd_pwrite(self, memory, fd.into(), ciovs.cast(), offset).await?) } - async fn fd_prestat_get(&mut self, fd: types::Fd) -> Result { - Ok(Snapshot1::fd_prestat_get(self, fd.into()).await?.into()) + async fn fd_prestat_get( + &mut self, + memory: &mut GuestMemory<'_>, + fd: types::Fd, + ) -> Result { + Ok(Snapshot1::fd_prestat_get(self, memory, fd.into()) + .await? + .into()) } - async fn fd_prestat_dir_name<'a>( + async fn fd_prestat_dir_name( &mut self, + memory: &mut GuestMemory<'_>, fd: types::Fd, - path: &GuestPtr<'a, u8>, + path: GuestPtr, path_max_len: types::Size, ) -> Result<(), Error> { - Snapshot1::fd_prestat_dir_name(self, fd.into(), path, path_max_len).await?; + Snapshot1::fd_prestat_dir_name(self, memory, fd.into(), path, path_max_len).await?; Ok(()) } - async fn fd_renumber(&mut self, from: types::Fd, to: types::Fd) -> Result<(), Error> { - Snapshot1::fd_renumber(self, from.into(), to.into()).await?; + async fn fd_renumber( + &mut self, + memory: &mut GuestMemory<'_>, + from: types::Fd, + to: types::Fd, + ) -> Result<(), Error> { + Snapshot1::fd_renumber(self, memory, from.into(), to.into()).await?; Ok(()) } async fn fd_seek( &mut self, + memory: &mut GuestMemory<'_>, fd: types::Fd, offset: types::Filedelta, whence: types::Whence, ) -> Result { - Ok(Snapshot1::fd_seek(self, fd.into(), offset, whence.into()).await?) + Ok(Snapshot1::fd_seek(self, memory, fd.into(), offset, whence.into()).await?) } - async fn fd_sync(&mut self, fd: types::Fd) -> Result<(), Error> { - Snapshot1::fd_sync(self, fd.into()).await?; + async fn fd_sync(&mut self, memory: &mut GuestMemory<'_>, fd: types::Fd) -> Result<(), Error> { + Snapshot1::fd_sync(self, memory, fd.into()).await?; Ok(()) } - async fn fd_tell(&mut self, fd: types::Fd) -> Result { - Ok(Snapshot1::fd_tell(self, fd.into()).await?) + async fn fd_tell( + &mut self, + memory: &mut GuestMemory<'_>, + fd: types::Fd, + ) -> Result { + Ok(Snapshot1::fd_tell(self, memory, fd.into()).await?) } - async fn fd_readdir<'a>( + async fn fd_readdir( &mut self, + memory: &mut GuestMemory<'_>, fd: types::Fd, - buf: &GuestPtr<'a, u8>, + buf: GuestPtr, buf_len: types::Size, cookie: types::Dircookie, ) -> Result { - Ok(Snapshot1::fd_readdir(self, fd.into(), buf, buf_len, cookie).await?) + Ok(Snapshot1::fd_readdir(self, memory, fd.into(), buf, buf_len, cookie).await?) } - async fn path_create_directory<'a>( + async fn path_create_directory( &mut self, + memory: &mut GuestMemory<'_>, dirfd: types::Fd, - path: &GuestPtr<'a, str>, + path: GuestPtr, ) -> Result<(), Error> { - Snapshot1::path_create_directory(self, dirfd.into(), path).await?; + Snapshot1::path_create_directory(self, memory, dirfd.into(), path).await?; Ok(()) } - async fn path_filestat_get<'a>( + async fn path_filestat_get( &mut self, + memory: &mut GuestMemory<'_>, dirfd: types::Fd, flags: types::Lookupflags, - path: &GuestPtr<'a, str>, + path: GuestPtr, ) -> Result { Ok( - Snapshot1::path_filestat_get(self, dirfd.into(), flags.into(), path) + Snapshot1::path_filestat_get(self, memory, dirfd.into(), flags.into(), path) .await? .into(), ) } - async fn path_filestat_set_times<'a>( + async fn path_filestat_set_times( &mut self, + memory: &mut GuestMemory<'_>, dirfd: types::Fd, flags: types::Lookupflags, - path: &GuestPtr<'a, str>, + path: GuestPtr, atim: types::Timestamp, mtim: types::Timestamp, fst_flags: types::Fstflags, ) -> Result<(), Error> { Snapshot1::path_filestat_set_times( self, + memory, dirfd.into(), flags.into(), path, @@ -809,16 +709,18 @@ impl wasi_unstable::WasiUnstable for WasiCtx { Ok(()) } - async fn path_link<'a>( + async fn path_link( &mut self, + memory: &mut GuestMemory<'_>, src_fd: types::Fd, src_flags: types::Lookupflags, - src_path: &GuestPtr<'a, str>, + src_path: GuestPtr, target_fd: types::Fd, - target_path: &GuestPtr<'a, str>, + target_path: GuestPtr, ) -> Result<(), Error> { Snapshot1::path_link( self, + memory, src_fd.into(), src_flags.into(), src_path, @@ -829,11 +731,12 @@ impl wasi_unstable::WasiUnstable for WasiCtx { Ok(()) } - async fn path_open<'a>( + async fn path_open( &mut self, + memory: &mut GuestMemory<'_>, dirfd: types::Fd, dirflags: types::Lookupflags, - path: &GuestPtr<'a, str>, + path: GuestPtr, oflags: types::Oflags, fs_rights_base: types::Rights, fs_rights_inheriting: types::Rights, @@ -841,6 +744,7 @@ impl wasi_unstable::WasiUnstable for WasiCtx { ) -> Result { Ok(Snapshot1::path_open( self, + memory, dirfd.into(), dirflags.into(), path, @@ -853,52 +757,65 @@ impl wasi_unstable::WasiUnstable for WasiCtx { .into()) } - async fn path_readlink<'a>( + async fn path_readlink( &mut self, + memory: &mut GuestMemory<'_>, dirfd: types::Fd, - path: &GuestPtr<'a, str>, - buf: &GuestPtr<'a, u8>, + path: GuestPtr, + buf: GuestPtr, buf_len: types::Size, ) -> Result { - Ok(Snapshot1::path_readlink(self, dirfd.into(), path, buf, buf_len).await?) + Ok(Snapshot1::path_readlink(self, memory, dirfd.into(), path, buf, buf_len).await?) } - async fn path_remove_directory<'a>( + async fn path_remove_directory( &mut self, + memory: &mut GuestMemory<'_>, dirfd: types::Fd, - path: &GuestPtr<'a, str>, + path: GuestPtr, ) -> Result<(), Error> { - Snapshot1::path_remove_directory(self, dirfd.into(), path).await?; + Snapshot1::path_remove_directory(self, memory, dirfd.into(), path).await?; Ok(()) } - async fn path_rename<'a>( + async fn path_rename( &mut self, + memory: &mut GuestMemory<'_>, src_fd: types::Fd, - src_path: &GuestPtr<'a, str>, + src_path: GuestPtr, dest_fd: types::Fd, - dest_path: &GuestPtr<'a, str>, + dest_path: GuestPtr, ) -> Result<(), Error> { - Snapshot1::path_rename(self, src_fd.into(), src_path, dest_fd.into(), dest_path).await?; + Snapshot1::path_rename( + self, + memory, + src_fd.into(), + src_path, + dest_fd.into(), + dest_path, + ) + .await?; Ok(()) } - async fn path_symlink<'a>( + async fn path_symlink( &mut self, - src_path: &GuestPtr<'a, str>, + memory: &mut GuestMemory<'_>, + src_path: GuestPtr, dirfd: types::Fd, - dest_path: &GuestPtr<'a, str>, + dest_path: GuestPtr, ) -> Result<(), Error> { - Snapshot1::path_symlink(self, src_path, dirfd.into(), dest_path).await?; + Snapshot1::path_symlink(self, memory, src_path, dirfd.into(), dest_path).await?; Ok(()) } - async fn path_unlink_file<'a>( + async fn path_unlink_file( &mut self, + memory: &mut GuestMemory<'_>, dirfd: types::Fd, - path: &GuestPtr<'a, str>, + path: GuestPtr, ) -> Result<(), Error> { - Snapshot1::path_unlink_file(self, dirfd.into(), path).await?; + Snapshot1::path_unlink_file(self, memory, dirfd.into(), path).await?; Ok(()) } @@ -909,10 +826,11 @@ impl wasi_unstable::WasiUnstable for WasiCtx { // The implementations are identical, but the `types::` in scope locally is different. // The bodies of these functions is mostly about converting the GuestPtr and types::-based // representation to use the Poll abstraction. - async fn poll_oneoff<'a>( + async fn poll_oneoff( &mut self, - subs: &GuestPtr<'a, types::Subscription>, - events: &GuestPtr<'a, types::Event>, + memory: &mut GuestMemory<'_>, + subs: GuestPtr, + events: GuestPtr, nsubscriptions: types::Size, ) -> Result { if nsubscriptions == 0 { @@ -924,7 +842,7 @@ impl wasi_unstable::WasiUnstable for WasiCtx { // functions. This supports all clock IDs, because POSIX says that // `clock_settime` doesn't effect relative sleeps. if nsubscriptions == 1 { - let sub = subs.read()?; + let sub = memory.read(subs)?; if let types::SubscriptionU::Clock(clocksub) = sub.u { if !clocksub .flags @@ -933,12 +851,15 @@ impl wasi_unstable::WasiUnstable for WasiCtx { self.sched .sleep(Duration::from_nanos(clocksub.timeout)) .await?; - events.write(types::Event { - userdata: sub.userdata, - error: types::Errno::Success, - type_: types::Eventtype::Clock, - fd_readwrite: fd_readwrite_empty(), - })?; + memory.write( + events, + types::Event { + userdata: sub.userdata, + error: types::Errno::Success, + type_: types::Eventtype::Clock, + fd_readwrite: fd_readwrite_empty(), + }, + )?; return Ok(1); } } @@ -954,7 +875,7 @@ impl wasi_unstable::WasiUnstable for WasiCtx { let subs = subs.as_array(nsubscriptions); for sub_elem in subs.iter() { let sub_ptr = sub_elem?; - let sub = sub_ptr.read()?; + let sub = memory.read(sub_ptr)?; match sub.u { types::SubscriptionU::Clock(clocksub) => match clocksub.id { types::Clockid::Monotonic => { @@ -1023,106 +944,125 @@ impl wasi_unstable::WasiUnstable for WasiCtx { for ((result, userdata), event_elem) in results.into_iter().zip(events.iter()) { let event_ptr = event_elem?; let userdata: types::Userdata = userdata.into(); - event_ptr.write(match result { - SubscriptionResult::Read(r) => { - let type_ = types::Eventtype::FdRead; - match r { - Ok((nbytes, flags)) => types::Event { - userdata, - error: types::Errno::Success, - type_, - fd_readwrite: types::EventFdReadwrite { - nbytes, - flags: types::Eventrwflags::from(&flags), + memory.write( + event_ptr, + match result { + SubscriptionResult::Read(r) => { + let type_ = types::Eventtype::FdRead; + match r { + Ok((nbytes, flags)) => types::Event { + userdata, + error: types::Errno::Success, + type_, + fd_readwrite: types::EventFdReadwrite { + nbytes, + flags: types::Eventrwflags::from(&flags), + }, }, - }, - Err(e) => types::Event { - userdata, - error: types::Errno::from(e.downcast().map_err(Error::trap)?), - type_, - fd_readwrite: fd_readwrite_empty(), - }, + Err(e) => types::Event { + userdata, + error: types::Errno::from(e.downcast().map_err(Error::trap)?), + type_, + fd_readwrite: fd_readwrite_empty(), + }, + } } - } - SubscriptionResult::Write(r) => { - let type_ = types::Eventtype::FdWrite; - match r { - Ok((nbytes, flags)) => types::Event { - userdata, - error: types::Errno::Success, - type_, - fd_readwrite: types::EventFdReadwrite { - nbytes, - flags: types::Eventrwflags::from(&flags), + SubscriptionResult::Write(r) => { + let type_ = types::Eventtype::FdWrite; + match r { + Ok((nbytes, flags)) => types::Event { + userdata, + error: types::Errno::Success, + type_, + fd_readwrite: types::EventFdReadwrite { + nbytes, + flags: types::Eventrwflags::from(&flags), + }, }, - }, - Err(e) => types::Event { + Err(e) => types::Event { + userdata, + error: types::Errno::from(e.downcast().map_err(Error::trap)?), + type_, + fd_readwrite: fd_readwrite_empty(), + }, + } + } + SubscriptionResult::MonotonicClock(r) => { + let type_ = types::Eventtype::Clock; + types::Event { userdata, - error: types::Errno::from(e.downcast().map_err(Error::trap)?), + error: match r { + Ok(()) => types::Errno::Success, + Err(e) => types::Errno::from(e.downcast().map_err(Error::trap)?), + }, type_, fd_readwrite: fd_readwrite_empty(), - }, - } - } - SubscriptionResult::MonotonicClock(r) => { - let type_ = types::Eventtype::Clock; - types::Event { - userdata, - error: match r { - Ok(()) => types::Errno::Success, - Err(e) => types::Errno::from(e.downcast().map_err(Error::trap)?), - }, - type_, - fd_readwrite: fd_readwrite_empty(), + } } - } - })?; + }, + )?; } Ok(num_results.try_into().expect("results fit into memory")) } - async fn proc_exit(&mut self, status: types::Exitcode) -> anyhow::Error { - Snapshot1::proc_exit(self, status).await + async fn proc_exit( + &mut self, + memory: &mut GuestMemory<'_>, + status: types::Exitcode, + ) -> anyhow::Error { + Snapshot1::proc_exit(self, memory, status).await } - async fn proc_raise(&mut self, _sig: types::Signal) -> Result<(), Error> { + async fn proc_raise( + &mut self, + _memory: &mut GuestMemory<'_>, + _sig: types::Signal, + ) -> Result<(), Error> { Err(Error::trap(anyhow::Error::msg("proc_raise unsupported"))) } - async fn sched_yield(&mut self) -> Result<(), Error> { - Snapshot1::sched_yield(self).await?; + async fn sched_yield(&mut self, memory: &mut GuestMemory<'_>) -> Result<(), Error> { + Snapshot1::sched_yield(self, memory).await?; Ok(()) } - async fn random_get<'a>( + async fn random_get( &mut self, - buf: &GuestPtr<'a, u8>, + memory: &mut GuestMemory<'_>, + buf: GuestPtr, buf_len: types::Size, ) -> Result<(), Error> { - Snapshot1::random_get(self, buf, buf_len).await?; + Snapshot1::random_get(self, memory, buf, buf_len).await?; Ok(()) } - async fn sock_recv<'a>( + async fn sock_recv( &mut self, + _memory: &mut GuestMemory<'_>, _fd: types::Fd, - _ri_data: &types::IovecArray<'a>, + _ri_data: types::IovecArray, _ri_flags: types::Riflags, ) -> Result<(types::Size, types::Roflags), Error> { Err(Error::trap(anyhow::Error::msg("sock_recv unsupported"))) } - async fn sock_send<'a>( + async fn sock_send( &mut self, + _memory: &mut GuestMemory<'_>, _fd: types::Fd, - _si_data: &types::CiovecArray<'a>, + _si_data: types::CiovecArray, _si_flags: types::Siflags, ) -> Result { Err(Error::trap(anyhow::Error::msg("sock_send unsupported"))) } - async fn sock_shutdown(&mut self, _fd: types::Fd, _how: types::Sdflags) -> Result<(), Error> { + async fn sock_shutdown( + &mut self, + _memory: &mut GuestMemory<'_>, + _fd: types::Fd, + _how: types::Sdflags, + ) -> Result<(), Error> { Err(Error::trap(anyhow::Error::msg("sock_shutdown unsupported"))) } } diff --git a/crates/wasi-common/src/snapshots/preview_1.rs b/crates/wasi-common/src/snapshots/preview_1.rs index fb060c7a4cd3..3a022ce5b2a5 100644 --- a/crates/wasi-common/src/snapshots/preview_1.rs +++ b/crates/wasi-common/src/snapshots/preview_1.rs @@ -11,9 +11,11 @@ use crate::{ I32Exit, SystemTimeSpec, WasiCtx, }; use cap_std::time::{Duration, SystemClock}; +use std::borrow::Cow; use std::io::{IoSlice, IoSliceMut}; use std::ops::Deref; use std::sync::Arc; +use wiggle::GuestMemory; use wiggle::GuestPtr; pub mod error; @@ -41,31 +43,43 @@ impl wiggle::GuestErrorType for types::Errno { #[wiggle::async_trait] impl wasi_snapshot_preview1::WasiSnapshotPreview1 for WasiCtx { - async fn args_get<'b>( + async fn args_get( &mut self, - argv: &GuestPtr<'b, GuestPtr<'b, u8>>, - argv_buf: &GuestPtr<'b, u8>, + memory: &mut GuestMemory<'_>, + argv: GuestPtr>, + argv_buf: GuestPtr, ) -> Result<(), Error> { - self.args.write_to_guest(argv_buf, argv) + self.args.write_to_guest(memory, argv_buf, argv) } - async fn args_sizes_get(&mut self) -> Result<(types::Size, types::Size), Error> { + async fn args_sizes_get( + &mut self, + _memory: &mut GuestMemory<'_>, + ) -> Result<(types::Size, types::Size), Error> { Ok((self.args.number_elements(), self.args.cumulative_size())) } - async fn environ_get<'b>( + async fn environ_get( &mut self, - environ: &GuestPtr<'b, GuestPtr<'b, u8>>, - environ_buf: &GuestPtr<'b, u8>, + memory: &mut GuestMemory<'_>, + environ: GuestPtr>, + environ_buf: GuestPtr, ) -> Result<(), Error> { - self.env.write_to_guest(environ_buf, environ) + self.env.write_to_guest(memory, environ_buf, environ) } - async fn environ_sizes_get(&mut self) -> Result<(types::Size, types::Size), Error> { + async fn environ_sizes_get( + &mut self, + _memory: &mut GuestMemory<'_>, + ) -> Result<(types::Size, types::Size), Error> { Ok((self.env.number_elements(), self.env.cumulative_size())) } - async fn clock_res_get(&mut self, id: types::Clockid) -> Result { + async fn clock_res_get( + &mut self, + _memory: &mut GuestMemory<'_>, + id: types::Clockid, + ) -> Result { let resolution = match id { types::Clockid::Realtime => Ok(self.clocks.system()?.resolution()), types::Clockid::Monotonic => Ok(self.clocks.monotonic()?.abs_clock.resolution()), @@ -78,6 +92,7 @@ impl wasi_snapshot_preview1::WasiSnapshotPreview1 for WasiCtx { async fn clock_time_get( &mut self, + _memory: &mut GuestMemory<'_>, id: types::Clockid, precision: types::Timestamp, ) -> Result { @@ -106,6 +121,7 @@ impl wasi_snapshot_preview1::WasiSnapshotPreview1 for WasiCtx { async fn fd_advise( &mut self, + _memory: &mut GuestMemory<'_>, fd: types::Fd, offset: types::Filesize, len: types::Filesize, @@ -121,6 +137,7 @@ impl wasi_snapshot_preview1::WasiSnapshotPreview1 for WasiCtx { async fn fd_allocate( &mut self, + _memory: &mut GuestMemory<'_>, fd: types::Fd, _offset: types::Filesize, _len: types::Filesize, @@ -136,7 +153,11 @@ impl wasi_snapshot_preview1::WasiSnapshotPreview1 for WasiCtx { Err(Error::not_supported()) } - async fn fd_close(&mut self, fd: types::Fd) -> Result<(), Error> { + async fn fd_close( + &mut self, + _memory: &mut GuestMemory<'_>, + fd: types::Fd, + ) -> Result<(), Error> { let table = self.table(); let fd = u32::from(fd); @@ -156,7 +177,11 @@ impl wasi_snapshot_preview1::WasiSnapshotPreview1 for WasiCtx { Ok(()) } - async fn fd_datasync(&mut self, fd: types::Fd) -> Result<(), Error> { + async fn fd_datasync( + &mut self, + _memory: &mut GuestMemory<'_>, + fd: types::Fd, + ) -> Result<(), Error> { self.table() .get_file(u32::from(fd))? .file @@ -165,7 +190,11 @@ impl wasi_snapshot_preview1::WasiSnapshotPreview1 for WasiCtx { Ok(()) } - async fn fd_fdstat_get(&mut self, fd: types::Fd) -> Result { + async fn fd_fdstat_get( + &mut self, + _memory: &mut GuestMemory<'_>, + fd: types::Fd, + ) -> Result { let table = self.table(); let fd = u32::from(fd); if table.is::(fd) { @@ -188,6 +217,7 @@ impl wasi_snapshot_preview1::WasiSnapshotPreview1 for WasiCtx { async fn fd_fdstat_set_flags( &mut self, + _memory: &mut GuestMemory<'_>, fd: types::Fd, flags: types::Fdflags, ) -> Result<(), Error> { @@ -205,6 +235,7 @@ impl wasi_snapshot_preview1::WasiSnapshotPreview1 for WasiCtx { async fn fd_fdstat_set_rights( &mut self, + _memory: &mut GuestMemory<'_>, fd: types::Fd, _fs_rights_base: types::Rights, _fs_rights_inheriting: types::Rights, @@ -222,7 +253,11 @@ impl wasi_snapshot_preview1::WasiSnapshotPreview1 for WasiCtx { } } - async fn fd_filestat_get(&mut self, fd: types::Fd) -> Result { + async fn fd_filestat_get( + &mut self, + _memory: &mut GuestMemory<'_>, + fd: types::Fd, + ) -> Result { let table = self.table(); let fd = u32::from(fd); if table.is::(fd) { @@ -238,6 +273,7 @@ impl wasi_snapshot_preview1::WasiSnapshotPreview1 for WasiCtx { async fn fd_filestat_set_size( &mut self, + _memory: &mut GuestMemory<'_>, fd: types::Fd, size: types::Filesize, ) -> Result<(), Error> { @@ -251,6 +287,7 @@ impl wasi_snapshot_preview1::WasiSnapshotPreview1 for WasiCtx { async fn fd_filestat_set_times( &mut self, + _memory: &mut GuestMemory<'_>, fd: types::Fd, atim: types::Timestamp, mtim: types::Timestamp, @@ -286,10 +323,11 @@ impl wasi_snapshot_preview1::WasiSnapshotPreview1 for WasiCtx { } } - async fn fd_read<'a>( + async fn fd_read( &mut self, + memory: &mut GuestMemory<'_>, fd: types::Fd, - iovs: &types::IovecArray<'a>, + iovs: types::IovecArray, ) -> Result { let f = self.table().get_file(u32::from(fd))?; // Access mode check normalizes error returned (windows would prefer ACCES here) @@ -302,7 +340,7 @@ impl wasi_snapshot_preview1::WasiSnapshotPreview1 for WasiCtx { .iter() .map(|iov_ptr| { let iov_ptr = iov_ptr?; - let iov: types::Iovec = iov_ptr.read()?; + let iov: types::Iovec = memory.read(iov_ptr)?; Ok(iov.buf.as_array(iov.buf_len)) }) .collect::>()?; @@ -319,11 +357,7 @@ impl wasi_snapshot_preview1::WasiSnapshotPreview1 for WasiCtx { // since we do not know here if `&dyn WasiFile` does anything else // (e.g., read), we cautiously incur some performance overhead by // copying twice. - let is_shared_memory = iovs - .iter() - .next() - .and_then(|s| Some(s.is_shared_memory())) - .unwrap_or(false); + let is_shared_memory = memory.is_shared_memory(); let bytes_read: u64 = if is_shared_memory { // For shared memory, read into an intermediate buffer. Only the // first iov will be filled and even then the read is capped by the @@ -332,9 +366,10 @@ impl wasi_snapshot_preview1::WasiSnapshotPreview1 for WasiCtx { if let Some(iov) = iov { let mut buffer = vec![0; (iov.len() as usize).min(MAX_SHARED_BUFFER_SIZE)]; let bytes_read = f.read_vectored(&mut [IoSliceMut::new(&mut buffer)]).await?; - iov.get_range(0..bytes_read.try_into()?) - .expect("it should always be possible to slice the iov smaller") - .copy_from_slice(&buffer[0..bytes_read.try_into()?])?; + let iov = iov + .get_range(0..bytes_read.try_into()?) + .expect("it should always be possible to slice the iov smaller"); + memory.copy_from_slice(&buffer[0..bytes_read.try_into()?], iov)?; bytes_read } else { return Ok(0); @@ -346,28 +381,23 @@ impl wasi_snapshot_preview1::WasiSnapshotPreview1 for WasiCtx { // error. As read is allowed to return less than the requested amount, // it's valid (though not as efficient) for us to only perform the // read of the first buffer. - let mut guest_slices: Vec> = iovs - .into_iter() - .filter(|iov| iov.len() > 0) - .map(|iov| Ok(iov.as_slice_mut()?.unwrap())) - .take(1) - .collect::>()?; + let guest_slice: &mut [u8] = match iovs.into_iter().filter(|iov| iov.len() > 0).next() { + Some(iov) => memory.as_slice_mut(iov)?.unwrap(), + None => return Ok(0), + }; // Read directly into the Wasm memory. - let mut ioslices: Vec = guest_slices - .iter_mut() - .map(|s| IoSliceMut::new(&mut *s)) - .collect(); - f.read_vectored(&mut ioslices).await? + f.read_vectored(&mut [IoSliceMut::new(guest_slice)]).await? }; Ok(types::Size::try_from(bytes_read)?) } - async fn fd_pread<'a>( + async fn fd_pread( &mut self, + memory: &mut GuestMemory<'_>, fd: types::Fd, - iovs: &types::IovecArray<'a>, + iovs: types::IovecArray, offset: types::Filesize, ) -> Result { let f = self.table().get_file(u32::from(fd))?; @@ -381,7 +411,7 @@ impl wasi_snapshot_preview1::WasiSnapshotPreview1 for WasiCtx { .iter() .map(|iov_ptr| { let iov_ptr = iov_ptr?; - let iov: types::Iovec = iov_ptr.read()?; + let iov: types::Iovec = memory.read(iov_ptr)?; Ok(iov.buf.as_array(iov.buf_len)) }) .collect::>()?; @@ -398,11 +428,7 @@ impl wasi_snapshot_preview1::WasiSnapshotPreview1 for WasiCtx { // since we do not know here if `&dyn WasiFile` does anything else // (e.g., read), we cautiously incur some performance overhead by // copying twice. - let is_shared_memory = iovs - .iter() - .next() - .and_then(|s| Some(s.is_shared_memory())) - .unwrap_or(false); + let is_shared_memory = memory.is_shared_memory(); let bytes_read: u64 = if is_shared_memory { // For shared memory, read into an intermediate buffer. Only the // first iov will be filled and even then the read is capped by the @@ -413,40 +439,34 @@ impl wasi_snapshot_preview1::WasiSnapshotPreview1 for WasiCtx { let bytes_read = f .read_vectored_at(&mut [IoSliceMut::new(&mut buffer)], offset) .await?; - iov.get_range(0..bytes_read.try_into()?) - .expect("it should always be possible to slice the iov smaller") - .copy_from_slice(&buffer[0..bytes_read.try_into()?])?; + let iov = iov + .get_range(0..bytes_read.try_into()?) + .expect("it should always be possible to slice the iov smaller"); + memory.copy_from_slice(&buffer[0..bytes_read.try_into()?], iov)?; bytes_read } else { return Ok(0); } } else { - // Convert unsafe guest slices to safe ones -- this uses Wiggle's - // internal borrow checker to ensure no overlaps. Note that borrow - // checking is coarse at this time so at most one non-empty slice is - // chosen. - let mut guest_slices: Vec> = iovs - .into_iter() - .filter(|iov| iov.len() > 0) - .map(|iov| Ok(iov.as_slice_mut()?.unwrap())) - .take(1) - .collect::>()?; + // Convert unsafe guest slices to safe ones. + let guest_slice: &mut [u8] = match iovs.into_iter().filter(|iov| iov.len() > 0).next() { + Some(iov) => memory.as_slice_mut(iov)?.unwrap(), + None => return Ok(0), + }; // Read directly into the Wasm memory. - let mut ioslices: Vec = guest_slices - .iter_mut() - .map(|s| IoSliceMut::new(&mut *s)) - .collect(); - f.read_vectored_at(&mut ioslices, offset).await? + f.read_vectored_at(&mut [IoSliceMut::new(guest_slice)], offset) + .await? }; Ok(types::Size::try_from(bytes_read)?) } - async fn fd_write<'a>( + async fn fd_write( &mut self, + memory: &mut GuestMemory<'_>, fd: types::Fd, - ciovs: &types::CiovecArray<'a>, + ciovs: types::CiovecArray, ) -> Result { let f = self.table().get_file(u32::from(fd))?; // Access mode check normalizes error returned (windows would prefer ACCES here) @@ -455,12 +475,12 @@ impl wasi_snapshot_preview1::WasiSnapshotPreview1 for WasiCtx { } let f = &f.file; - let guest_slices: Vec> = ciovs + let guest_slices: Vec> = ciovs .iter() .map(|iov_ptr| { let iov_ptr = iov_ptr?; - let iov: types::Ciovec = iov_ptr.read()?; - Ok(iov.buf.as_array(iov.buf_len).as_cow()?) + let iov: types::Ciovec = memory.read(iov_ptr)?; + Ok(memory.as_cow(iov.buf.as_array(iov.buf_len))?) }) .collect::>()?; @@ -473,10 +493,11 @@ impl wasi_snapshot_preview1::WasiSnapshotPreview1 for WasiCtx { Ok(types::Size::try_from(bytes_written)?) } - async fn fd_pwrite<'a>( + async fn fd_pwrite( &mut self, + memory: &mut GuestMemory<'_>, fd: types::Fd, - ciovs: &types::CiovecArray<'a>, + ciovs: types::CiovecArray, offset: types::Filesize, ) -> Result { let f = self.table().get_file(u32::from(fd))?; @@ -486,12 +507,12 @@ impl wasi_snapshot_preview1::WasiSnapshotPreview1 for WasiCtx { } let f = &f.file; - let guest_slices: Vec> = ciovs + let guest_slices: Vec> = ciovs .iter() .map(|iov_ptr| { let iov_ptr = iov_ptr?; - let iov: types::Ciovec = iov_ptr.read()?; - Ok(iov.buf.as_array(iov.buf_len).as_cow()?) + let iov: types::Ciovec = memory.read(iov_ptr)?; + Ok(memory.as_cow(iov.buf.as_array(iov.buf_len))?) }) .collect::>()?; @@ -504,7 +525,11 @@ impl wasi_snapshot_preview1::WasiSnapshotPreview1 for WasiCtx { Ok(types::Size::try_from(bytes_written)?) } - async fn fd_prestat_get(&mut self, fd: types::Fd) -> Result { + async fn fd_prestat_get( + &mut self, + _memory: &mut GuestMemory<'_>, + fd: types::Fd, + ) -> Result { let table = self.table(); let dir_entry: Arc = table.get(u32::from(fd)).map_err(|_| Error::badf())?; if let Some(ref preopen) = dir_entry.preopen_path() { @@ -516,10 +541,11 @@ impl wasi_snapshot_preview1::WasiSnapshotPreview1 for WasiCtx { } } - async fn fd_prestat_dir_name<'a>( + async fn fd_prestat_dir_name( &mut self, + memory: &mut GuestMemory<'_>, fd: types::Fd, - path: &GuestPtr<'a, u8>, + path: GuestPtr, path_max_len: types::Size, ) -> Result<(), Error> { let table = self.table(); @@ -533,13 +559,19 @@ impl wasi_snapshot_preview1::WasiSnapshotPreview1 for WasiCtx { if path_len > path_max_len as usize { return Err(Error::name_too_long()); } - path.as_array(path_len as u32).copy_from_slice(path_bytes)?; + let path = path.as_array(path_len as u32); + memory.copy_from_slice(path_bytes, path)?; Ok(()) } else { Err(Error::not_supported()) } } - async fn fd_renumber(&mut self, from: types::Fd, to: types::Fd) -> Result<(), Error> { + async fn fd_renumber( + &mut self, + _memory: &mut GuestMemory<'_>, + from: types::Fd, + to: types::Fd, + ) -> Result<(), Error> { let table = self.table(); let from = u32::from(from); let to = u32::from(to); @@ -551,6 +583,7 @@ impl wasi_snapshot_preview1::WasiSnapshotPreview1 for WasiCtx { async fn fd_seek( &mut self, + _memory: &mut GuestMemory<'_>, fd: types::Fd, offset: types::Filedelta, whence: types::Whence, @@ -572,12 +605,16 @@ impl wasi_snapshot_preview1::WasiSnapshotPreview1 for WasiCtx { Ok(newoffset) } - async fn fd_sync(&mut self, fd: types::Fd) -> Result<(), Error> { + async fn fd_sync(&mut self, _memory: &mut GuestMemory<'_>, fd: types::Fd) -> Result<(), Error> { self.table().get_file(u32::from(fd))?.file.sync().await?; Ok(()) } - async fn fd_tell(&mut self, fd: types::Fd) -> Result { + async fn fd_tell( + &mut self, + _memory: &mut GuestMemory<'_>, + fd: types::Fd, + ) -> Result { let offset = self .table() .get_file(u32::from(fd))? @@ -587,10 +624,11 @@ impl wasi_snapshot_preview1::WasiSnapshotPreview1 for WasiCtx { Ok(offset) } - async fn fd_readdir<'a>( + async fn fd_readdir( &mut self, + memory: &mut GuestMemory<'_>, fd: types::Fd, - buf: &GuestPtr<'a, u8>, + buf: GuestPtr, buf_len: types::Size, cookie: types::Dircookie, ) -> Result { @@ -611,8 +649,8 @@ impl wasi_snapshot_preview1::WasiSnapshotPreview1 for WasiCtx { // Copy as many bytes of the dirent as we can, up to the end of the buffer let dirent_copy_len = std::cmp::min(dirent_len, buf_len - bufused); - buf.as_array(dirent_copy_len) - .copy_from_slice(&dirent_raw[..dirent_copy_len as usize])?; + let raw = buf.as_array(dirent_copy_len); + memory.copy_from_slice(&dirent_raw[..dirent_copy_len as usize], raw)?; // If the dirent struct wasn't compiled entirely, return that we filled the buffer, which // tells libc that we're not at EOF. @@ -625,8 +663,8 @@ impl wasi_snapshot_preview1::WasiSnapshotPreview1 for WasiCtx { // Copy as many bytes of the name as we can, up to the end of the buffer let name_copy_len = std::cmp::min(name_len, buf_len - bufused); - buf.as_array(name_copy_len) - .copy_from_slice(&name_raw[..name_copy_len as usize])?; + let raw = buf.as_array(name_copy_len); + memory.copy_from_slice(&name_raw[..name_copy_len as usize], raw)?; // If the dirent struct wasn't copied entirely, return that we filled the buffer, which // tells libc that we're not at EOF @@ -641,41 +679,44 @@ impl wasi_snapshot_preview1::WasiSnapshotPreview1 for WasiCtx { Ok(bufused) } - async fn path_create_directory<'a>( + async fn path_create_directory( &mut self, + memory: &mut GuestMemory<'_>, dirfd: types::Fd, - path: &GuestPtr<'a, str>, + path: GuestPtr, ) -> Result<(), Error> { self.table() .get_dir(u32::from(dirfd))? .dir - .create_dir(path.as_cow()?.deref()) + .create_dir(memory.as_cow_str(path)?.deref()) .await } - async fn path_filestat_get<'a>( + async fn path_filestat_get( &mut self, + memory: &mut GuestMemory<'_>, dirfd: types::Fd, flags: types::Lookupflags, - path: &GuestPtr<'a, str>, + path: GuestPtr, ) -> Result { let filestat = self .table() .get_dir(u32::from(dirfd))? .dir .get_path_filestat( - path.as_cow()?.deref(), + memory.as_cow_str(path)?.deref(), flags.contains(types::Lookupflags::SYMLINK_FOLLOW), ) .await?; Ok(types::Filestat::from(filestat)) } - async fn path_filestat_set_times<'a>( + async fn path_filestat_set_times( &mut self, + memory: &mut GuestMemory<'_>, dirfd: types::Fd, flags: types::Lookupflags, - path: &GuestPtr<'a, str>, + path: GuestPtr, atim: types::Timestamp, mtim: types::Timestamp, fst_flags: types::Fstflags, @@ -691,7 +732,7 @@ impl wasi_snapshot_preview1::WasiSnapshotPreview1 for WasiCtx { .get_dir(u32::from(dirfd))? .dir .set_times( - path.as_cow()?.deref(), + memory.as_cow_str(path)?.deref(), atim, mtim, flags.contains(types::Lookupflags::SYMLINK_FOLLOW), @@ -699,13 +740,14 @@ impl wasi_snapshot_preview1::WasiSnapshotPreview1 for WasiCtx { .await } - async fn path_link<'a>( + async fn path_link( &mut self, + memory: &mut GuestMemory<'_>, src_fd: types::Fd, src_flags: types::Lookupflags, - src_path: &GuestPtr<'a, str>, + src_path: GuestPtr, target_fd: types::Fd, - target_path: &GuestPtr<'a, str>, + target_path: GuestPtr, ) -> Result<(), Error> { let table = self.table(); let src_dir = table.get_dir(u32::from(src_fd))?; @@ -719,18 +761,19 @@ impl wasi_snapshot_preview1::WasiSnapshotPreview1 for WasiCtx { src_dir .dir .hard_link( - src_path.as_cow()?.deref(), + memory.as_cow_str(src_path)?.deref(), target_dir.dir.deref(), - target_path.as_cow()?.deref(), + memory.as_cow_str(target_path)?.deref(), ) .await } - async fn path_open<'a>( + async fn path_open( &mut self, + memory: &mut GuestMemory<'_>, dirfd: types::Fd, dirflags: types::Lookupflags, - path: &GuestPtr<'a, str>, + path: GuestPtr, oflags: types::Oflags, fs_rights_base: types::Rights, _fs_rights_inheriting: types::Rights, @@ -747,7 +790,7 @@ impl wasi_snapshot_preview1::WasiSnapshotPreview1 for WasiCtx { let oflags = OFlags::from(&oflags); let fdflags = FdFlags::from(fdflags); - let path = path.as_cow()?; + let path = memory.as_cow_str(path)?; let read = fs_rights_base.contains(types::Rights::FD_READ); let write = fs_rights_base.contains(types::Rights::FD_WRITE); @@ -774,18 +817,19 @@ impl wasi_snapshot_preview1::WasiSnapshotPreview1 for WasiCtx { Ok(types::Fd::from(fd)) } - async fn path_readlink<'a>( + async fn path_readlink( &mut self, + memory: &mut GuestMemory<'_>, dirfd: types::Fd, - path: &GuestPtr<'a, str>, - buf: &GuestPtr<'a, u8>, + path: GuestPtr, + buf: GuestPtr, buf_len: types::Size, ) -> Result { let link = self .table() .get_dir(u32::from(dirfd))? .dir - .read_link(path.as_cow()?.deref()) + .read_link(memory.as_cow_str(path)?.deref()) .await? .into_os_string() .into_string() @@ -794,29 +838,31 @@ impl wasi_snapshot_preview1::WasiSnapshotPreview1 for WasiCtx { // Like posix readlink(2), silently truncate links when they are larger than the // destination buffer: let link_len = std::cmp::min(link_bytes.len(), buf_len as usize); - buf.as_array(link_len as u32) - .copy_from_slice(&link_bytes[..link_len])?; + let buf = buf.as_array(link_len as u32); + memory.copy_from_slice(&link_bytes[..link_len], buf)?; Ok(link_len as types::Size) } - async fn path_remove_directory<'a>( + async fn path_remove_directory( &mut self, + memory: &mut GuestMemory<'_>, dirfd: types::Fd, - path: &GuestPtr<'a, str>, + path: GuestPtr, ) -> Result<(), Error> { self.table() .get_dir(u32::from(dirfd))? .dir - .remove_dir(path.as_cow()?.deref()) + .remove_dir(memory.as_cow_str(path)?.deref()) .await } - async fn path_rename<'a>( + async fn path_rename( &mut self, + memory: &mut GuestMemory<'_>, src_fd: types::Fd, - src_path: &GuestPtr<'a, str>, + src_path: GuestPtr, dest_fd: types::Fd, - dest_path: &GuestPtr<'a, str>, + dest_path: GuestPtr, ) -> Result<(), Error> { let table = self.table(); let src_dir = table.get_dir(u32::from(src_fd))?; @@ -824,42 +870,48 @@ impl wasi_snapshot_preview1::WasiSnapshotPreview1 for WasiCtx { src_dir .dir .rename( - src_path.as_cow()?.deref(), + memory.as_cow_str(src_path)?.deref(), dest_dir.dir.deref(), - dest_path.as_cow()?.deref(), + memory.as_cow_str(dest_path)?.deref(), ) .await } - async fn path_symlink<'a>( + async fn path_symlink( &mut self, - src_path: &GuestPtr<'a, str>, + memory: &mut GuestMemory<'_>, + src_path: GuestPtr, dirfd: types::Fd, - dest_path: &GuestPtr<'a, str>, + dest_path: GuestPtr, ) -> Result<(), Error> { self.table() .get_dir(u32::from(dirfd))? .dir - .symlink(src_path.as_cow()?.deref(), dest_path.as_cow()?.deref()) + .symlink( + memory.as_cow_str(src_path)?.deref(), + memory.as_cow_str(dest_path)?.deref(), + ) .await } - async fn path_unlink_file<'a>( + async fn path_unlink_file( &mut self, + memory: &mut GuestMemory<'_>, dirfd: types::Fd, - path: &GuestPtr<'a, str>, + path: GuestPtr, ) -> Result<(), Error> { self.table() .get_dir(u32::from(dirfd))? .dir - .unlink_file(path.as_cow()?.deref()) + .unlink_file(memory.as_cow_str(path)?.deref()) .await } - async fn poll_oneoff<'a>( + async fn poll_oneoff( &mut self, - subs: &GuestPtr<'a, types::Subscription>, - events: &GuestPtr<'a, types::Event>, + memory: &mut GuestMemory<'_>, + subs: GuestPtr, + events: GuestPtr, nsubscriptions: types::Size, ) -> Result { if nsubscriptions == 0 { @@ -871,7 +923,7 @@ impl wasi_snapshot_preview1::WasiSnapshotPreview1 for WasiCtx { // functions. This supports all clock IDs, because POSIX says that // `clock_settime` doesn't effect relative sleeps. if nsubscriptions == 1 { - let sub = subs.read()?; + let sub = memory.read(subs)?; if let types::SubscriptionU::Clock(clocksub) = sub.u { if !clocksub .flags @@ -880,12 +932,15 @@ impl wasi_snapshot_preview1::WasiSnapshotPreview1 for WasiCtx { self.sched .sleep(Duration::from_nanos(clocksub.timeout)) .await?; - events.write(types::Event { - userdata: sub.userdata, - error: types::Errno::Success, - type_: types::Eventtype::Clock, - fd_readwrite: fd_readwrite_empty(), - })?; + memory.write( + events, + types::Event { + userdata: sub.userdata, + error: types::Errno::Success, + type_: types::Eventtype::Clock, + fd_readwrite: fd_readwrite_empty(), + }, + )?; return Ok(1); } } @@ -901,7 +956,7 @@ impl wasi_snapshot_preview1::WasiSnapshotPreview1 for WasiCtx { let subs = subs.as_array(nsubscriptions); for sub_elem in subs.iter() { let sub_ptr = sub_elem?; - let sub = sub_ptr.read()?; + let sub = memory.read(sub_ptr)?; match sub.u { types::SubscriptionU::Clock(clocksub) => match clocksub.id { types::Clockid::Monotonic => { @@ -1004,66 +1059,73 @@ impl wasi_snapshot_preview1::WasiSnapshotPreview1 for WasiCtx { for ((result, userdata), event_elem) in results.into_iter().zip(events.iter()) { let event_ptr = event_elem?; let userdata: types::Userdata = userdata.into(); - event_ptr.write(match result { - SubscriptionResult::Read(r) => { - let type_ = types::Eventtype::FdRead; - match r { - Ok((nbytes, flags)) => types::Event { - userdata, - error: types::Errno::Success, - type_, - fd_readwrite: types::EventFdReadwrite { - nbytes, - flags: types::Eventrwflags::from(&flags), + memory.write( + event_ptr, + match result { + SubscriptionResult::Read(r) => { + let type_ = types::Eventtype::FdRead; + match r { + Ok((nbytes, flags)) => types::Event { + userdata, + error: types::Errno::Success, + type_, + fd_readwrite: types::EventFdReadwrite { + nbytes, + flags: types::Eventrwflags::from(&flags), + }, }, - }, - Err(e) => types::Event { - userdata, - error: e.downcast().map_err(Error::trap)?, - type_, - fd_readwrite: fd_readwrite_empty(), - }, + Err(e) => types::Event { + userdata, + error: e.downcast().map_err(Error::trap)?, + type_, + fd_readwrite: fd_readwrite_empty(), + }, + } } - } - SubscriptionResult::Write(r) => { - let type_ = types::Eventtype::FdWrite; - match r { - Ok((nbytes, flags)) => types::Event { - userdata, - error: types::Errno::Success, - type_, - fd_readwrite: types::EventFdReadwrite { - nbytes, - flags: types::Eventrwflags::from(&flags), + SubscriptionResult::Write(r) => { + let type_ = types::Eventtype::FdWrite; + match r { + Ok((nbytes, flags)) => types::Event { + userdata, + error: types::Errno::Success, + type_, + fd_readwrite: types::EventFdReadwrite { + nbytes, + flags: types::Eventrwflags::from(&flags), + }, }, - }, - Err(e) => types::Event { + Err(e) => types::Event { + userdata, + error: e.downcast().map_err(Error::trap)?, + type_, + fd_readwrite: fd_readwrite_empty(), + }, + } + } + SubscriptionResult::MonotonicClock(r) => { + let type_ = types::Eventtype::Clock; + types::Event { userdata, - error: e.downcast().map_err(Error::trap)?, + error: match r { + Ok(()) => types::Errno::Success, + Err(e) => e.downcast().map_err(Error::trap)?, + }, type_, fd_readwrite: fd_readwrite_empty(), - }, - } - } - SubscriptionResult::MonotonicClock(r) => { - let type_ = types::Eventtype::Clock; - types::Event { - userdata, - error: match r { - Ok(()) => types::Errno::Success, - Err(e) => e.downcast().map_err(Error::trap)?, - }, - type_, - fd_readwrite: fd_readwrite_empty(), + } } - } - })?; + }, + )?; } Ok(num_results.try_into().expect("results fit into memory")) } - async fn proc_exit(&mut self, status: types::Exitcode) -> anyhow::Error { + async fn proc_exit( + &mut self, + _memory: &mut GuestMemory<'_>, + status: types::Exitcode, + ) -> anyhow::Error { // Check that the status is within WASI's range. if status < 126 { I32Exit(status as i32).into() @@ -1072,21 +1134,26 @@ impl wasi_snapshot_preview1::WasiSnapshotPreview1 for WasiCtx { } } - async fn proc_raise(&mut self, _sig: types::Signal) -> Result<(), Error> { + async fn proc_raise( + &mut self, + _memory: &mut GuestMemory<'_>, + _sig: types::Signal, + ) -> Result<(), Error> { Err(Error::trap(anyhow::Error::msg("proc_raise unsupported"))) } - async fn sched_yield(&mut self) -> Result<(), Error> { + async fn sched_yield(&mut self, _memory: &mut GuestMemory<'_>) -> Result<(), Error> { self.sched.sched_yield().await } - async fn random_get<'a>( + async fn random_get( &mut self, - buf: &GuestPtr<'a, u8>, + memory: &mut GuestMemory<'_>, + buf: GuestPtr, buf_len: types::Size, ) -> Result<(), Error> { let buf = buf.as_array(buf_len); - if buf.is_shared_memory() { + if memory.is_shared_memory() { // If the Wasm memory is shared, copy to an intermediate buffer to // avoid Rust unsafety (i.e., the called function could rely on // `&mut [u8]`'s exclusive ownership which is not guaranteed due to @@ -1096,17 +1163,14 @@ impl wasi_snapshot_preview1::WasiSnapshotPreview1 for WasiCtx { let len = (buf.len() - copied).min(MAX_SHARED_BUFFER_SIZE as u32); let mut tmp = vec![0; len as usize]; self.random.lock().unwrap().try_fill_bytes(&mut tmp)?; - let dest = buf - .get_range(copied..copied + len) - .unwrap() - .as_unsafe_slice_mut()?; - dest.copy_from_slice(&tmp)?; + let dest = buf.get_range(copied..copied + len).unwrap(); + memory.copy_from_slice(&tmp, dest)?; copied += len; } } else { // If the Wasm memory is non-shared, copy directly into the linear // memory. - let mem = &mut buf.as_slice_mut()?.unwrap(); + let mem = &mut memory.as_slice_mut(buf)?.unwrap(); self.random.lock().unwrap().try_fill_bytes(mem)?; } Ok(()) @@ -1114,6 +1178,7 @@ impl wasi_snapshot_preview1::WasiSnapshotPreview1 for WasiCtx { async fn sock_accept( &mut self, + _memory: &mut GuestMemory<'_>, fd: types::Fd, flags: types::Fdflags, ) -> Result { @@ -1124,10 +1189,11 @@ impl wasi_snapshot_preview1::WasiSnapshotPreview1 for WasiCtx { Ok(types::Fd::from(fd)) } - async fn sock_recv<'a>( + async fn sock_recv( &mut self, + memory: &mut GuestMemory<'_>, fd: types::Fd, - ri_data: &types::IovecArray<'a>, + ri_data: types::IovecArray, ri_flags: types::Riflags, ) -> Result<(types::Size, types::Roflags), Error> { let f = self.table().get_file(u32::from(fd))?; @@ -1136,7 +1202,7 @@ impl wasi_snapshot_preview1::WasiSnapshotPreview1 for WasiCtx { .iter() .map(|iov_ptr| { let iov_ptr = iov_ptr?; - let iov: types::Iovec = iov_ptr.read()?; + let iov: types::Iovec = memory.read(iov_ptr)?; Ok(iov.buf.as_array(iov.buf_len)) }) .collect::>()?; @@ -1153,11 +1219,7 @@ impl wasi_snapshot_preview1::WasiSnapshotPreview1 for WasiCtx { // since we do not know here if `&dyn WasiFile` does anything else // (e.g., read), we cautiously incur some performance overhead by // copying twice. - let is_shared_memory = iovs - .iter() - .next() - .and_then(|s| Some(s.is_shared_memory())) - .unwrap_or(false); + let is_shared_memory = memory.is_shared_memory(); let (bytes_read, ro_flags) = if is_shared_memory { // For shared memory, read into an intermediate buffer. Only the // first iov will be filled and even then the read is capped by the @@ -1169,9 +1231,10 @@ impl wasi_snapshot_preview1::WasiSnapshotPreview1 for WasiCtx { .file .sock_recv(&mut [IoSliceMut::new(&mut buffer)], RiFlags::from(ri_flags)) .await?; - iov.get_range(0..bytes_read.try_into()?) - .expect("it should always be possible to slice the iov smaller") - .copy_from_slice(&buffer[0..bytes_read.try_into()?])?; + let iov = iov + .get_range(0..bytes_read.try_into()?) + .expect("it should always be possible to slice the iov smaller"); + memory.copy_from_slice(&buffer[0..bytes_read.try_into()?], iov)?; (bytes_read, ro_flags) } else { return Ok((0, RoFlags::empty().into())); @@ -1181,38 +1244,35 @@ impl wasi_snapshot_preview1::WasiSnapshotPreview1 for WasiCtx { // Wiggle's internal borrow checker to ensure no overlaps. We assume // here that, because the memory is not shared, there are no other // threads to access it while it is written to. - let mut guest_slices: Vec> = iovs - .into_iter() - .map(|iov| Ok(iov.as_slice_mut()?.unwrap())) - .collect::>()?; + let guest_slice: &mut [u8] = match iovs.into_iter().filter(|iov| iov.len() > 0).next() { + Some(iov) => memory.as_slice_mut(iov)?.unwrap(), + None => &mut [], + }; // Read directly into the Wasm memory. - let mut ioslices: Vec = guest_slices - .iter_mut() - .map(|s| IoSliceMut::new(&mut *s)) - .collect(); f.file - .sock_recv(&mut ioslices, RiFlags::from(ri_flags)) + .sock_recv(&mut [IoSliceMut::new(guest_slice)], RiFlags::from(ri_flags)) .await? }; Ok((types::Size::try_from(bytes_read)?, ro_flags.into())) } - async fn sock_send<'a>( + async fn sock_send( &mut self, + memory: &mut GuestMemory<'_>, fd: types::Fd, - si_data: &types::CiovecArray<'a>, + si_data: types::CiovecArray, _si_flags: types::Siflags, ) -> Result { let f = self.table().get_file(u32::from(fd))?; - let guest_slices: Vec> = si_data + let guest_slices: Vec> = si_data .iter() .map(|iov_ptr| { let iov_ptr = iov_ptr?; - let iov: types::Ciovec = iov_ptr.read()?; - Ok(iov.buf.as_array(iov.buf_len).as_cow()?) + let iov: types::Ciovec = memory.read(iov_ptr)?; + Ok(memory.as_cow(iov.buf.as_array(iov.buf_len))?) }) .collect::>()?; @@ -1225,7 +1285,12 @@ impl wasi_snapshot_preview1::WasiSnapshotPreview1 for WasiCtx { Ok(types::Size::try_from(bytes_written)?) } - async fn sock_shutdown(&mut self, fd: types::Fd, how: types::Sdflags) -> Result<(), Error> { + async fn sock_shutdown( + &mut self, + _memory: &mut GuestMemory<'_>, + fd: types::Fd, + how: types::Sdflags, + ) -> Result<(), Error> { let f = self.table().get_file(u32::from(fd))?; f.file.sock_shutdown(SdFlags::from(how)).await diff --git a/crates/wasi-common/src/string_array.rs b/crates/wasi-common/src/string_array.rs index 557784a9fff7..f35efe032c85 100644 --- a/crates/wasi-common/src/string_array.rs +++ b/crates/wasi-common/src/string_array.rs @@ -1,5 +1,5 @@ use crate::{Error, ErrorExt}; -use wiggle::GuestPtr; +use wiggle::{GuestMemory, GuestPtr}; pub struct StringArray { elems: Vec, @@ -45,10 +45,11 @@ impl StringArray { .sum::() as u32 } - pub fn write_to_guest<'a>( + pub fn write_to_guest( &self, - buffer: &GuestPtr<'a, u8>, - element_heads: &GuestPtr<'a, GuestPtr<'a, u8>>, + memory: &mut GuestMemory<'_>, + buffer: GuestPtr, + element_heads: GuestPtr>, ) -> Result<(), Error> { let element_heads = element_heads.as_array(self.number_elements()); let buffer = buffer.as_array(self.cumulative_size()); @@ -60,13 +61,13 @@ impl StringArray { let elem_buffer = buffer .get_range(cursor..(cursor + len)) .ok_or(Error::invalid_argument())?; // Elements don't fit in buffer provided - elem_buffer.copy_from_slice(bytes)?; + memory.copy_from_slice(bytes, elem_buffer)?; } - buffer - .get(cursor + len) - .ok_or(Error::invalid_argument())? - .write(0)?; // 0 terminate - head?.write(buffer.get(cursor).expect("already validated"))?; + memory.write( + buffer.get(cursor + len).ok_or(Error::invalid_argument())?, + 0, + )?; // 0 terminate + memory.write(head?, buffer.get(cursor).expect("already validated"))?; cursor += len + 1; } Ok(()) diff --git a/crates/wasi-nn/src/witx.rs b/crates/wasi-nn/src/witx.rs index ffcaccac48c3..9d78a595ec90 100644 --- a/crates/wasi-nn/src/witx.rs +++ b/crates/wasi-nn/src/witx.rs @@ -14,7 +14,7 @@ //! [`types`]: crate::wit::types use crate::ctx::{UsageError, WasiNnCtx, WasiNnError, WasiNnResult as Result}; -use wiggle::GuestPtr; +use wiggle::{GuestMemory, GuestPtr}; pub use gen::wasi_ephemeral_nn::add_to_linker; @@ -54,7 +54,8 @@ mod gen { impl gen::wasi_ephemeral_nn::WasiEphemeralNn for WasiNnCtx { fn load( &mut self, - builders: &gen::types::GraphBuilderArray<'_>, + memory: &mut GuestMemory<'_>, + builders: gen::types::GraphBuilderArray, encoding: gen::types::GraphEncoding, target: gen::types::ExecutionTarget, ) -> Result { @@ -63,10 +64,11 @@ impl gen::wasi_ephemeral_nn::WasiEphemeralNn for WasiNnCtx { // $graph_builder_array) as slices for a backend to operate on. let mut slices = vec![]; for builder in builders.iter() { - let slice = builder? - .read()? - .as_slice()? - .expect("cannot use with shared memories; see https://github.com/bytecodealliance/wasmtime/issues/5235 (TODO)"); + let builder = memory.read(builder?)?; + let slice = memory.as_slice(builder)?.expect( + "cannot use with shared memories; \ + see https://github.com/bytecodealliance/wasmtime/issues/5235 (TODO)", + ); slices.push(slice); } let slice_refs = slices.iter().map(|s| s.as_ref()).collect::>(); @@ -78,8 +80,12 @@ impl gen::wasi_ephemeral_nn::WasiEphemeralNn for WasiNnCtx { Ok(graph_id.into()) } - fn load_by_name<'b>(&mut self, name: &wiggle::GuestPtr<'b, str>) -> Result { - let name = name.as_str()?.unwrap(); + fn load_by_name( + &mut self, + memory: &mut GuestMemory<'_>, + name: wiggle::GuestPtr, + ) -> Result { + let name = memory.as_str(name)?.unwrap(); if let Some(graph) = self.registry.get_mut(&name) { let graph_id = self.graphs.insert(graph.clone().into()); Ok(graph_id.into()) @@ -90,6 +96,7 @@ impl gen::wasi_ephemeral_nn::WasiEphemeralNn for WasiNnCtx { fn init_execution_context( &mut self, + _memory: &mut GuestMemory<'_>, graph_id: gen::types::Graph, ) -> Result { let exec_context = if let Some(graph) = self.graphs.get_mut(graph_id.into()) { @@ -102,17 +109,18 @@ impl gen::wasi_ephemeral_nn::WasiEphemeralNn for WasiNnCtx { Ok(exec_context_id.into()) } - fn set_input<'b>( + fn set_input( &mut self, + memory: &mut GuestMemory<'_>, exec_context_id: gen::types::GraphExecutionContext, index: u32, - tensor: &gen::types::Tensor<'b>, + tensor: &gen::types::Tensor, ) -> Result<()> { if let Some(exec_context) = self.executions.get_mut(exec_context_id.into()) { let tensor = crate::wit::types::Tensor { - dimensions: tensor.dimensions.to_vec()?, + dimensions: memory.to_vec(tensor.dimensions)?, tensor_type: tensor.type_.into(), - data: tensor.data.to_vec()?, + data: memory.to_vec(tensor.data)?, }; Ok(exec_context.set_input(index, &tensor)?) } else { @@ -120,7 +128,11 @@ impl gen::wasi_ephemeral_nn::WasiEphemeralNn for WasiNnCtx { } } - fn compute(&mut self, exec_context_id: gen::types::GraphExecutionContext) -> Result<()> { + fn compute( + &mut self, + _memory: &mut GuestMemory<'_>, + exec_context_id: gen::types::GraphExecutionContext, + ) -> Result<()> { if let Some(exec_context) = self.executions.get_mut(exec_context_id.into()) { Ok(exec_context.compute()?) } else { @@ -130,16 +142,19 @@ impl gen::wasi_ephemeral_nn::WasiEphemeralNn for WasiNnCtx { fn get_output( &mut self, + memory: &mut GuestMemory<'_>, exec_context_id: gen::types::GraphExecutionContext, index: u32, - out_buffer: &GuestPtr<'_, u8>, + out_buffer: GuestPtr, out_buffer_max_size: u32, ) -> Result { if let Some(exec_context) = self.executions.get_mut(exec_context_id.into()) { - let mut destination = out_buffer - .as_array(out_buffer_max_size) - .as_slice_mut()? - .expect("cannot use with shared memories; see https://github.com/bytecodealliance/wasmtime/issues/5235 (TODO)"); + let mut destination = memory + .as_slice_mut(out_buffer.as_array(out_buffer_max_size))? + .expect( + "cannot use with shared memories; \ + see https://github.com/bytecodealliance/wasmtime/issues/5235 (TODO)", + ); Ok(exec_context.get_output(index, &mut destination)?) } else { Err(UsageError::InvalidGraphHandle.into()) diff --git a/crates/wasi/src/preview0.rs b/crates/wasi/src/preview0.rs index 995a88691a3c..038b98b40df2 100644 --- a/crates/wasi/src/preview0.rs +++ b/crates/wasi/src/preview0.rs @@ -7,7 +7,7 @@ use crate::preview0::types::Error; use crate::preview1::types as snapshot1_types; use crate::preview1::wasi_snapshot_preview1::WasiSnapshotPreview1 as Snapshot1; use crate::preview1::WasiP1Ctx; -use wiggle::{GuestError, GuestPtr}; +use wiggle::{GuestError, GuestMemory, GuestPtr}; pub fn add_to_linker_async( linker: &mut wasmtime::Linker, @@ -71,96 +71,128 @@ impl wiggle::GuestErrorType for types::Errno { #[wiggle::async_trait] impl wasi_unstable::WasiUnstable for T { - fn args_get<'a>( + fn args_get( &mut self, - argv: &GuestPtr<'a, GuestPtr<'a, u8>>, - argv_buf: &GuestPtr<'a, u8>, + memory: &mut GuestMemory<'_>, + argv: GuestPtr>, + argv_buf: GuestPtr, ) -> Result<(), Error> { - Snapshot1::args_get(self, argv, argv_buf)?; + Snapshot1::args_get(self, memory, argv, argv_buf)?; Ok(()) } - fn args_sizes_get(&mut self) -> Result<(types::Size, types::Size), Error> { - let s = Snapshot1::args_sizes_get(self)?; + fn args_sizes_get( + &mut self, + memory: &mut GuestMemory<'_>, + ) -> Result<(types::Size, types::Size), Error> { + let s = Snapshot1::args_sizes_get(self, memory)?; Ok(s) } - fn environ_get<'a>( + fn environ_get( &mut self, - environ: &GuestPtr<'a, GuestPtr<'a, u8>>, - environ_buf: &GuestPtr<'a, u8>, + memory: &mut GuestMemory<'_>, + environ: GuestPtr>, + environ_buf: GuestPtr, ) -> Result<(), Error> { - Snapshot1::environ_get(self, environ, environ_buf)?; + Snapshot1::environ_get(self, memory, environ, environ_buf)?; Ok(()) } - fn environ_sizes_get(&mut self) -> Result<(types::Size, types::Size), Error> { - let s = Snapshot1::environ_sizes_get(self)?; + fn environ_sizes_get( + &mut self, + memory: &mut GuestMemory<'_>, + ) -> Result<(types::Size, types::Size), Error> { + let s = Snapshot1::environ_sizes_get(self, memory)?; Ok(s) } - fn clock_res_get(&mut self, id: types::Clockid) -> Result { - let t = Snapshot1::clock_res_get(self, id.into())?; + fn clock_res_get( + &mut self, + memory: &mut GuestMemory<'_>, + id: types::Clockid, + ) -> Result { + let t = Snapshot1::clock_res_get(self, memory, id.into())?; Ok(t) } fn clock_time_get( &mut self, + memory: &mut GuestMemory<'_>, id: types::Clockid, precision: types::Timestamp, ) -> Result { - let t = Snapshot1::clock_time_get(self, id.into(), precision)?; + let t = Snapshot1::clock_time_get(self, memory, id.into(), precision)?; Ok(t) } async fn fd_advise( &mut self, + memory: &mut GuestMemory<'_>, fd: types::Fd, offset: types::Filesize, len: types::Filesize, advice: types::Advice, ) -> Result<(), Error> { - Snapshot1::fd_advise(self, fd.into(), offset, len, advice.into()).await?; + Snapshot1::fd_advise(self, memory, fd.into(), offset, len, advice.into()).await?; Ok(()) } fn fd_allocate( &mut self, + memory: &mut GuestMemory<'_>, fd: types::Fd, offset: types::Filesize, len: types::Filesize, ) -> Result<(), Error> { - Snapshot1::fd_allocate(self, fd.into(), offset, len)?; + Snapshot1::fd_allocate(self, memory, fd.into(), offset, len)?; Ok(()) } - async fn fd_close(&mut self, fd: types::Fd) -> Result<(), Error> { - Snapshot1::fd_close(self, fd.into()).await?; + async fn fd_close(&mut self, memory: &mut GuestMemory<'_>, fd: types::Fd) -> Result<(), Error> { + Snapshot1::fd_close(self, memory, fd.into()).await?; Ok(()) } - async fn fd_datasync(&mut self, fd: types::Fd) -> Result<(), Error> { - Snapshot1::fd_datasync(self, fd.into()).await?; + async fn fd_datasync( + &mut self, + memory: &mut GuestMemory<'_>, + fd: types::Fd, + ) -> Result<(), Error> { + Snapshot1::fd_datasync(self, memory, fd.into()).await?; Ok(()) } - async fn fd_fdstat_get(&mut self, fd: types::Fd) -> Result { - Ok(Snapshot1::fd_fdstat_get(self, fd.into()).await?.into()) + async fn fd_fdstat_get( + &mut self, + memory: &mut GuestMemory<'_>, + fd: types::Fd, + ) -> Result { + Ok(Snapshot1::fd_fdstat_get(self, memory, fd.into()) + .await? + .into()) } - fn fd_fdstat_set_flags(&mut self, fd: types::Fd, flags: types::Fdflags) -> Result<(), Error> { - Snapshot1::fd_fdstat_set_flags(self, fd.into(), flags.into())?; + fn fd_fdstat_set_flags( + &mut self, + memory: &mut GuestMemory<'_>, + fd: types::Fd, + flags: types::Fdflags, + ) -> Result<(), Error> { + Snapshot1::fd_fdstat_set_flags(self, memory, fd.into(), flags.into())?; Ok(()) } fn fd_fdstat_set_rights( &mut self, + memory: &mut GuestMemory<'_>, fd: types::Fd, fs_rights_base: types::Rights, fs_rights_inheriting: types::Rights, ) -> Result<(), Error> { Snapshot1::fd_fdstat_set_rights( self, + memory, fd.into(), fs_rights_base.into(), fs_rights_inheriting.into(), @@ -168,152 +200,185 @@ impl wasi_unstable::WasiUnstable for T { Ok(()) } - async fn fd_filestat_get(&mut self, fd: types::Fd) -> Result { - Ok(Snapshot1::fd_filestat_get(self, fd.into()).await?.into()) + async fn fd_filestat_get( + &mut self, + memory: &mut GuestMemory<'_>, + fd: types::Fd, + ) -> Result { + Ok(Snapshot1::fd_filestat_get(self, memory, fd.into()) + .await? + .into()) } async fn fd_filestat_set_size( &mut self, + memory: &mut GuestMemory<'_>, fd: types::Fd, size: types::Filesize, ) -> Result<(), Error> { - Snapshot1::fd_filestat_set_size(self, fd.into(), size).await?; + Snapshot1::fd_filestat_set_size(self, memory, fd.into(), size).await?; Ok(()) } async fn fd_filestat_set_times( &mut self, + memory: &mut GuestMemory<'_>, fd: types::Fd, atim: types::Timestamp, mtim: types::Timestamp, fst_flags: types::Fstflags, ) -> Result<(), Error> { - Snapshot1::fd_filestat_set_times(self, fd.into(), atim, mtim, fst_flags.into()).await?; + Snapshot1::fd_filestat_set_times(self, memory, fd.into(), atim, mtim, fst_flags.into()) + .await?; Ok(()) } - async fn fd_read<'a>( + async fn fd_read( &mut self, + memory: &mut GuestMemory<'_>, fd: types::Fd, - iovs: &types::IovecArray<'a>, + iovs: types::IovecArray, ) -> Result { assert_iovec_array_same(); - let result = Snapshot1::fd_read(self, fd.into(), &iovs.cast()).await?; + let result = Snapshot1::fd_read(self, memory, fd.into(), iovs.cast()).await?; Ok(result) } - async fn fd_pread<'a>( + async fn fd_pread( &mut self, + memory: &mut GuestMemory<'_>, fd: types::Fd, - iovs: &types::IovecArray<'a>, + iovs: types::IovecArray, offset: types::Filesize, ) -> Result { assert_iovec_array_same(); - let result = Snapshot1::fd_pread(self, fd.into(), &iovs.cast(), offset).await?; + let result = Snapshot1::fd_pread(self, memory, fd.into(), iovs.cast(), offset).await?; Ok(result) } - async fn fd_write<'a>( + async fn fd_write( &mut self, + memory: &mut GuestMemory<'_>, fd: types::Fd, - ciovs: &types::CiovecArray<'a>, + ciovs: types::CiovecArray, ) -> Result { assert_ciovec_array_same(); - let result = Snapshot1::fd_write(self, fd.into(), &ciovs.cast()).await?; + let result = Snapshot1::fd_write(self, memory, fd.into(), ciovs.cast()).await?; Ok(result) } - async fn fd_pwrite<'a>( + async fn fd_pwrite( &mut self, + memory: &mut GuestMemory<'_>, fd: types::Fd, - ciovs: &types::CiovecArray<'a>, + ciovs: types::CiovecArray, offset: types::Filesize, ) -> Result { assert_ciovec_array_same(); - let result = Snapshot1::fd_pwrite(self, fd.into(), &ciovs.cast(), offset).await?; + let result = Snapshot1::fd_pwrite(self, memory, fd.into(), ciovs.cast(), offset).await?; Ok(result) } - fn fd_prestat_get(&mut self, fd: types::Fd) -> Result { - Ok(Snapshot1::fd_prestat_get(self, fd.into())?.into()) + fn fd_prestat_get( + &mut self, + memory: &mut GuestMemory<'_>, + fd: types::Fd, + ) -> Result { + Ok(Snapshot1::fd_prestat_get(self, memory, fd.into())?.into()) } fn fd_prestat_dir_name( &mut self, + memory: &mut GuestMemory<'_>, fd: types::Fd, - path: &GuestPtr<'_, u8>, + path: GuestPtr, path_max_len: types::Size, ) -> Result<(), Error> { - Snapshot1::fd_prestat_dir_name(self, fd.into(), path, path_max_len)?; + Snapshot1::fd_prestat_dir_name(self, memory, fd.into(), path, path_max_len)?; Ok(()) } - fn fd_renumber(&mut self, from: types::Fd, to: types::Fd) -> Result<(), Error> { - Snapshot1::fd_renumber(self, from.into(), to.into())?; + fn fd_renumber( + &mut self, + memory: &mut GuestMemory<'_>, + from: types::Fd, + to: types::Fd, + ) -> Result<(), Error> { + Snapshot1::fd_renumber(self, memory, from.into(), to.into())?; Ok(()) } async fn fd_seek( &mut self, + memory: &mut GuestMemory<'_>, fd: types::Fd, offset: types::Filedelta, whence: types::Whence, ) -> Result { - Ok(Snapshot1::fd_seek(self, fd.into(), offset, whence.into()).await?) + Ok(Snapshot1::fd_seek(self, memory, fd.into(), offset, whence.into()).await?) } - async fn fd_sync(&mut self, fd: types::Fd) -> Result<(), Error> { - Snapshot1::fd_sync(self, fd.into()).await?; + async fn fd_sync(&mut self, memory: &mut GuestMemory<'_>, fd: types::Fd) -> Result<(), Error> { + Snapshot1::fd_sync(self, memory, fd.into()).await?; Ok(()) } - fn fd_tell(&mut self, fd: types::Fd) -> Result { - Ok(Snapshot1::fd_tell(self, fd.into())?) + fn fd_tell( + &mut self, + memory: &mut GuestMemory<'_>, + fd: types::Fd, + ) -> Result { + Ok(Snapshot1::fd_tell(self, memory, fd.into())?) } - async fn fd_readdir<'a>( + async fn fd_readdir( &mut self, + memory: &mut GuestMemory<'_>, fd: types::Fd, - buf: &GuestPtr<'a, u8>, + buf: GuestPtr, buf_len: types::Size, cookie: types::Dircookie, ) -> Result { - Ok(Snapshot1::fd_readdir(self, fd.into(), buf, buf_len, cookie).await?) + Ok(Snapshot1::fd_readdir(self, memory, fd.into(), buf, buf_len, cookie).await?) } - async fn path_create_directory<'a>( + async fn path_create_directory( &mut self, + memory: &mut GuestMemory<'_>, dirfd: types::Fd, - path: &GuestPtr<'a, str>, + path: GuestPtr, ) -> Result<(), Error> { - Snapshot1::path_create_directory(self, dirfd.into(), path).await?; + Snapshot1::path_create_directory(self, memory, dirfd.into(), path).await?; Ok(()) } - async fn path_filestat_get<'a>( + async fn path_filestat_get( &mut self, + memory: &mut GuestMemory<'_>, dirfd: types::Fd, flags: types::Lookupflags, - path: &GuestPtr<'a, str>, + path: GuestPtr, ) -> Result { Ok( - Snapshot1::path_filestat_get(self, dirfd.into(), flags.into(), path) + Snapshot1::path_filestat_get(self, memory, dirfd.into(), flags.into(), path) .await? .into(), ) } - async fn path_filestat_set_times<'a>( + async fn path_filestat_set_times( &mut self, + memory: &mut GuestMemory<'_>, dirfd: types::Fd, flags: types::Lookupflags, - path: &GuestPtr<'a, str>, + path: GuestPtr, atim: types::Timestamp, mtim: types::Timestamp, fst_flags: types::Fstflags, ) -> Result<(), Error> { Snapshot1::path_filestat_set_times( self, + memory, dirfd.into(), flags.into(), path, @@ -325,16 +390,18 @@ impl wasi_unstable::WasiUnstable for T { Ok(()) } - async fn path_link<'a>( + async fn path_link( &mut self, + memory: &mut GuestMemory<'_>, src_fd: types::Fd, src_flags: types::Lookupflags, - src_path: &GuestPtr<'a, str>, + src_path: GuestPtr, target_fd: types::Fd, - target_path: &GuestPtr<'a, str>, + target_path: GuestPtr, ) -> Result<(), Error> { Snapshot1::path_link( self, + memory, src_fd.into(), src_flags.into(), src_path, @@ -345,11 +412,12 @@ impl wasi_unstable::WasiUnstable for T { Ok(()) } - async fn path_open<'a>( + async fn path_open( &mut self, + memory: &mut GuestMemory<'_>, dirfd: types::Fd, dirflags: types::Lookupflags, - path: &GuestPtr<'a, str>, + path: GuestPtr, oflags: types::Oflags, fs_rights_base: types::Rights, fs_rights_inheriting: types::Rights, @@ -357,6 +425,7 @@ impl wasi_unstable::WasiUnstable for T { ) -> Result { Ok(Snapshot1::path_open( self, + memory, dirfd.into(), dirflags.into(), path, @@ -369,52 +438,65 @@ impl wasi_unstable::WasiUnstable for T { .into()) } - async fn path_readlink<'a>( + async fn path_readlink( &mut self, + memory: &mut GuestMemory<'_>, dirfd: types::Fd, - path: &GuestPtr<'a, str>, - buf: &GuestPtr<'a, u8>, + path: GuestPtr, + buf: GuestPtr, buf_len: types::Size, ) -> Result { - Ok(Snapshot1::path_readlink(self, dirfd.into(), path, buf, buf_len).await?) + Ok(Snapshot1::path_readlink(self, memory, dirfd.into(), path, buf, buf_len).await?) } - async fn path_remove_directory<'a>( + async fn path_remove_directory( &mut self, + memory: &mut GuestMemory<'_>, dirfd: types::Fd, - path: &GuestPtr<'a, str>, + path: GuestPtr, ) -> Result<(), Error> { - Snapshot1::path_remove_directory(self, dirfd.into(), path).await?; + Snapshot1::path_remove_directory(self, memory, dirfd.into(), path).await?; Ok(()) } - async fn path_rename<'a>( + async fn path_rename( &mut self, + memory: &mut GuestMemory<'_>, src_fd: types::Fd, - src_path: &GuestPtr<'a, str>, + src_path: GuestPtr, dest_fd: types::Fd, - dest_path: &GuestPtr<'a, str>, + dest_path: GuestPtr, ) -> Result<(), Error> { - Snapshot1::path_rename(self, src_fd.into(), src_path, dest_fd.into(), dest_path).await?; + Snapshot1::path_rename( + self, + memory, + src_fd.into(), + src_path, + dest_fd.into(), + dest_path, + ) + .await?; Ok(()) } - async fn path_symlink<'a>( + async fn path_symlink( &mut self, - src_path: &GuestPtr<'a, str>, + memory: &mut GuestMemory<'_>, + src_path: GuestPtr, dirfd: types::Fd, - dest_path: &GuestPtr<'a, str>, + dest_path: GuestPtr, ) -> Result<(), Error> { - Snapshot1::path_symlink(self, src_path, dirfd.into(), dest_path).await?; + Snapshot1::path_symlink(self, memory, src_path, dirfd.into(), dest_path).await?; Ok(()) } - async fn path_unlink_file<'a>( + async fn path_unlink_file( &mut self, + memory: &mut GuestMemory<'_>, dirfd: types::Fd, - path: &GuestPtr<'a, str>, + path: GuestPtr, ) -> Result<(), Error> { - Snapshot1::path_unlink_file(self, dirfd.into(), path).await?; + Snapshot1::path_unlink_file(self, memory, dirfd.into(), path).await?; Ok(()) } @@ -426,64 +508,82 @@ impl wasi_unstable::WasiUnstable for T { // the old values are restored to pretend like we didn't overwrite them. // // Surely no one would pass overlapping pointers to this API right? - async fn poll_oneoff<'a>( + async fn poll_oneoff( &mut self, - subs: &GuestPtr<'a, types::Subscription>, - events: &GuestPtr<'a, types::Event>, + memory: &mut GuestMemory<'_>, + subs: GuestPtr, + events: GuestPtr, nsubscriptions: types::Size, ) -> Result { let subs_array = subs.as_array(nsubscriptions); let mut old_subs = Vec::new(); for slot in subs_array.iter() { let slot = slot?; - let sub = slot.read()?; + let sub = memory.read(slot)?; old_subs.push(sub.clone()); - slot.cast().write(snapshot1_types::Subscription { - userdata: sub.userdata, - u: match sub.u { - types::SubscriptionU::Clock(c) => { - snapshot1_types::SubscriptionU::Clock(c.into()) - } - types::SubscriptionU::FdRead(c) => { - snapshot1_types::SubscriptionU::FdRead(c.into()) - } - types::SubscriptionU::FdWrite(c) => { - snapshot1_types::SubscriptionU::FdWrite(c.into()) - } + memory.write( + slot.cast(), + snapshot1_types::Subscription { + userdata: sub.userdata, + u: match sub.u { + types::SubscriptionU::Clock(c) => { + snapshot1_types::SubscriptionU::Clock(c.into()) + } + types::SubscriptionU::FdRead(c) => { + snapshot1_types::SubscriptionU::FdRead(c.into()) + } + types::SubscriptionU::FdWrite(c) => { + snapshot1_types::SubscriptionU::FdWrite(c.into()) + } + }, }, - })?; + )?; } - let ret = - Snapshot1::poll_oneoff(self, &subs.cast(), &events.cast(), nsubscriptions).await?; + let ret = Snapshot1::poll_oneoff(self, memory, subs.cast(), events.cast(), nsubscriptions) + .await?; for (sub, slot) in old_subs.into_iter().zip(subs_array.iter()) { - slot?.write(sub)?; + memory.write(slot?, sub)?; } Ok(ret) } - fn proc_exit(&mut self, status: types::Exitcode) -> anyhow::Error { - Snapshot1::proc_exit(self, status) + fn proc_exit( + &mut self, + memory: &mut GuestMemory<'_>, + status: types::Exitcode, + ) -> anyhow::Error { + Snapshot1::proc_exit(self, memory, status) } - fn proc_raise(&mut self, sig: types::Signal) -> Result<(), Error> { - Snapshot1::proc_raise(self, sig.into())?; + fn proc_raise( + &mut self, + memory: &mut GuestMemory<'_>, + sig: types::Signal, + ) -> Result<(), Error> { + Snapshot1::proc_raise(self, memory, sig.into())?; Ok(()) } - fn sched_yield(&mut self) -> Result<(), Error> { - Snapshot1::sched_yield(self)?; + fn sched_yield(&mut self, memory: &mut GuestMemory<'_>) -> Result<(), Error> { + Snapshot1::sched_yield(self, memory)?; Ok(()) } - fn random_get(&mut self, buf: &GuestPtr<'_, u8>, buf_len: types::Size) -> Result<(), Error> { - Snapshot1::random_get(self, buf, buf_len)?; + fn random_get( + &mut self, + memory: &mut GuestMemory<'_>, + buf: GuestPtr, + buf_len: types::Size, + ) -> Result<(), Error> { + Snapshot1::random_get(self, memory, buf, buf_len)?; Ok(()) } fn sock_recv( &mut self, + _memory: &mut GuestMemory<'_>, _fd: types::Fd, - _ri_data: &types::IovecArray<'_>, + _ri_data: types::IovecArray, _ri_flags: types::Riflags, ) -> Result<(types::Size, types::Roflags), Error> { Err(Error::trap(anyhow::Error::msg("sock_recv unsupported"))) @@ -491,14 +591,20 @@ impl wasi_unstable::WasiUnstable for T { fn sock_send( &mut self, + _memory: &mut GuestMemory<'_>, _fd: types::Fd, - _si_data: &types::CiovecArray<'_>, + _si_data: types::CiovecArray, _si_flags: types::Siflags, ) -> Result { Err(Error::trap(anyhow::Error::msg("sock_send unsupported"))) } - fn sock_shutdown(&mut self, _fd: types::Fd, _how: types::Sdflags) -> Result<(), Error> { + fn sock_shutdown( + &mut self, + _memory: &mut GuestMemory<'_>, + _fd: types::Fd, + _how: types::Sdflags, + ) -> Result<(), Error> { Err(Error::trap(anyhow::Error::msg("sock_shutdown unsupported"))) } } @@ -509,16 +615,16 @@ fn assert_iovec_array_same() { // and it's been manually verified that these two types are the same, so // it's ok to cast between them. assert_eq!( - std::mem::size_of::>(), - std::mem::size_of::>() + std::mem::size_of::(), + std::mem::size_of::() ); } fn assert_ciovec_array_same() { // NB: see above too assert_eq!( - std::mem::size_of::>(), - std::mem::size_of::>() + std::mem::size_of::(), + std::mem::size_of::() ); } diff --git a/crates/wasi/src/preview1.rs b/crates/wasi/src/preview1.rs index 56d88b32ee14..75d8baf1e57b 100644 --- a/crates/wasi/src/preview1.rs +++ b/crates/wasi/src/preview1.rs @@ -74,7 +74,6 @@ use crate::bindings::{ }; use crate::{FsError, IsATTY, ResourceTable, StreamError, StreamResult, WasiCtx, WasiView}; use anyhow::{bail, Context}; -use std::borrow::Borrow; use std::collections::{BTreeMap, HashSet}; use std::mem::{self, size_of, size_of_val}; use std::ops::{Deref, DerefMut}; @@ -84,7 +83,7 @@ use std::sync::Arc; use system_interface::fs::FileIoExt; use wasmtime::component::Resource; use wiggle::tracing::instrument; -use wiggle::{GuestError, GuestPtr, GuestStrCow, GuestType}; +use wiggle::{GuestError, GuestMemory, GuestPtr, GuestType}; // Bring all WASI traits in scope that this implementation builds on. use crate::bindings::cli::environment::Host as _; @@ -223,13 +222,16 @@ impl BlockingMode { } async fn write( &self, + memory: &mut GuestMemory<'_>, host: &mut dyn WasiView, output_stream: Resource, - bytes: GuestPtr<'_, [u8]>, + bytes: GuestPtr<[u8]>, ) -> StreamResult { use streams::HostOutputStream as Streams; - let bytes = bytes.as_cow().map_err(|e| StreamError::Trap(e.into()))?; + let bytes = memory + .as_cow(bytes) + .map_err(|e| StreamError::Trap(e.into()))?; let mut bytes = &bytes[..]; match self { @@ -1019,42 +1021,37 @@ impl From for types::Error { type Result = std::result::Result; -fn write_bytes<'a>( - ptr: impl Borrow>, +fn write_bytes( + memory: &mut GuestMemory<'_>, + ptr: GuestPtr, buf: &[u8], -) -> Result, types::Error> { +) -> Result, types::Error> { // NOTE: legacy implementation always returns Inval errno let len = u32::try_from(buf.len())?; - let ptr = ptr.borrow(); - ptr.as_array(len).copy_from_slice(buf)?; + memory.copy_from_slice(buf, ptr.as_array(len))?; let next = ptr.add(len)?; Ok(next) } -fn write_byte<'a>(ptr: impl Borrow>, byte: u8) -> Result> { - let ptr = ptr.borrow(); - ptr.write(byte)?; +fn write_byte(memory: &mut GuestMemory<'_>, ptr: GuestPtr, byte: u8) -> Result> { + memory.write(ptr, byte)?; let next = ptr.add(1)?; Ok(next) } -fn read_str<'a>(ptr: impl Borrow>) -> Result> { - let s = ptr.borrow().as_cow()?; - Ok(s) -} - -fn read_string<'a>(ptr: impl Borrow>) -> Result { - read_str(ptr).map(|s| s.to_string()) +fn read_string<'a>(memory: &'a GuestMemory<'_>, ptr: GuestPtr) -> Result { + Ok(memory.as_cow_str(ptr)?.into_owned()) } // Find first non-empty buffer. -fn first_non_empty_ciovec<'a, 'b>( - ciovs: &'a types::CiovecArray<'b>, -) -> Result>> { +fn first_non_empty_ciovec( + memory: &GuestMemory<'_>, + ciovs: types::CiovecArray, +) -> Result>> { for iov in ciovs.iter() { - let iov = iov?.read()?; + let iov = memory.read(iov?)?; if iov.buf_len == 0 { continue; } @@ -1064,9 +1061,12 @@ fn first_non_empty_ciovec<'a, 'b>( } // Find first non-empty buffer. -fn first_non_empty_iovec<'a>(iovs: &types::IovecArray<'a>) -> Result>> { +fn first_non_empty_iovec( + memory: &GuestMemory<'_>, + iovs: types::IovecArray, +) -> Result>> { for iov in iovs.iter() { - let iov = iov?.read()?; + let iov = memory.read(iov?)?; if iov.buf_len == 0 { continue; } @@ -1080,31 +1080,35 @@ fn first_non_empty_iovec<'a>(iovs: &types::IovecArray<'a>) -> Result( + #[instrument(skip(self, memory))] + fn args_get( &mut self, - argv: &GuestPtr<'b, GuestPtr<'b, u8>>, - argv_buf: &GuestPtr<'b, u8>, + memory: &mut GuestMemory<'_>, + argv: GuestPtr>, + argv_buf: GuestPtr, ) -> Result<(), types::Error> { self.as_wasi_view() .get_arguments() .context("failed to call `get-arguments`") .map_err(types::Error::trap)? .into_iter() - .try_fold((*argv, *argv_buf), |(argv, argv_buf), arg| -> Result<_> { - argv.write(argv_buf)?; + .try_fold((argv, argv_buf), |(argv, argv_buf), arg| -> Result<_> { + memory.write(argv, argv_buf)?; let argv = argv.add(1)?; - let argv_buf = write_bytes(argv_buf, arg.as_bytes())?; - let argv_buf = write_byte(argv_buf, 0)?; + let argv_buf = write_bytes(memory, argv_buf, arg.as_bytes())?; + let argv_buf = write_byte(memory, argv_buf, 0)?; Ok((argv, argv_buf)) })?; Ok(()) } - #[instrument(skip(self))] - fn args_sizes_get(&mut self) -> Result<(types::Size, types::Size), types::Error> { + #[instrument(skip(self, _memory))] + fn args_sizes_get( + &mut self, + _memory: &mut GuestMemory<'_>, + ) -> Result<(types::Size, types::Size), types::Error> { let args = self .as_wasi_view() .get_arguments() @@ -1120,11 +1124,12 @@ impl wasi_snapshot_preview1::WasiSnapshotPreview1 for WasiP1Ctx { Ok((num, len)) } - #[instrument(skip(self))] + #[instrument(skip(self, memory))] fn environ_get<'b>( &mut self, - environ: &GuestPtr<'b, GuestPtr<'b, u8>>, - environ_buf: &GuestPtr<'b, u8>, + memory: &mut GuestMemory<'_>, + environ: GuestPtr>, + environ_buf: GuestPtr, ) -> Result<(), types::Error> { self.as_wasi_view() .get_environment() @@ -1132,15 +1137,15 @@ impl wasi_snapshot_preview1::WasiSnapshotPreview1 for WasiP1Ctx { .map_err(types::Error::trap)? .into_iter() .try_fold( - (*environ, *environ_buf), + (environ, environ_buf), |(environ, environ_buf), (k, v)| -> Result<_, types::Error> { - environ.write(environ_buf)?; + memory.write(environ, environ_buf)?; let environ = environ.add(1)?; - let environ_buf = write_bytes(environ_buf, k.as_bytes())?; - let environ_buf = write_byte(environ_buf, b'=')?; - let environ_buf = write_bytes(environ_buf, v.as_bytes())?; - let environ_buf = write_byte(environ_buf, 0)?; + let environ_buf = write_bytes(memory, environ_buf, k.as_bytes())?; + let environ_buf = write_byte(memory, environ_buf, b'=')?; + let environ_buf = write_bytes(memory, environ_buf, v.as_bytes())?; + let environ_buf = write_byte(memory, environ_buf, 0)?; Ok((environ, environ_buf)) }, @@ -1148,8 +1153,11 @@ impl wasi_snapshot_preview1::WasiSnapshotPreview1 for WasiP1Ctx { Ok(()) } - #[instrument(skip(self))] - fn environ_sizes_get(&mut self) -> Result<(types::Size, types::Size), types::Error> { + #[instrument(skip(self, _memory))] + fn environ_sizes_get( + &mut self, + _memory: &mut GuestMemory<'_>, + ) -> Result<(types::Size, types::Size), types::Error> { let environ = self .as_wasi_view() .get_environment() @@ -1164,8 +1172,12 @@ impl wasi_snapshot_preview1::WasiSnapshotPreview1 for WasiP1Ctx { Ok((num, len)) } - #[instrument(skip(self))] - fn clock_res_get(&mut self, id: types::Clockid) -> Result { + #[instrument(skip(self, _memory))] + fn clock_res_get( + &mut self, + _memory: &mut GuestMemory<'_>, + id: types::Clockid, + ) -> Result { let res = match id { types::Clockid::Realtime => wall_clock::Host::resolution(self.as_wasi_view()) .context("failed to call `wall_clock::resolution`") @@ -1181,9 +1193,10 @@ impl wasi_snapshot_preview1::WasiSnapshotPreview1 for WasiP1Ctx { Ok(res) } - #[instrument(skip(self))] + #[instrument(skip(self, _memory))] fn clock_time_get( &mut self, + _memory: &mut GuestMemory<'_>, id: types::Clockid, _precision: types::Timestamp, ) -> Result { @@ -1202,9 +1215,10 @@ impl wasi_snapshot_preview1::WasiSnapshotPreview1 for WasiP1Ctx { Ok(now) } - #[instrument(skip(self))] + #[instrument(skip(self, _memory))] async fn fd_advise( &mut self, + _memory: &mut GuestMemory<'_>, fd: types::Fd, offset: types::Filesize, len: types::Filesize, @@ -1223,9 +1237,10 @@ impl wasi_snapshot_preview1::WasiSnapshotPreview1 for WasiP1Ctx { /// Force the allocation of space in a file. /// NOTE: This is similar to `posix_fallocate` in POSIX. - #[instrument(skip(self))] + #[instrument(skip(self, _memory))] fn fd_allocate( &mut self, + _memory: &mut GuestMemory<'_>, fd: types::Fd, _offset: types::Filesize, _len: types::Filesize, @@ -1236,8 +1251,12 @@ impl wasi_snapshot_preview1::WasiSnapshotPreview1 for WasiP1Ctx { /// Close a file descriptor. /// NOTE: This is similar to `close` in POSIX. - #[instrument(skip(self))] - async fn fd_close(&mut self, fd: types::Fd) -> Result<(), types::Error> { + #[instrument(skip(self, _memory))] + async fn fd_close( + &mut self, + _memory: &mut GuestMemory<'_>, + fd: types::Fd, + ) -> Result<(), types::Error> { let desc = self .transact()? .descriptors @@ -1262,8 +1281,12 @@ impl wasi_snapshot_preview1::WasiSnapshotPreview1 for WasiP1Ctx { /// Synchronize the data of a file to disk. /// NOTE: This is similar to `fdatasync` in POSIX. - #[instrument(skip(self))] - async fn fd_datasync(&mut self, fd: types::Fd) -> Result<(), types::Error> { + #[instrument(skip(self, _memory))] + async fn fd_datasync( + &mut self, + _memory: &mut GuestMemory<'_>, + fd: types::Fd, + ) -> Result<(), types::Error> { let fd = self.get_file_fd(fd)?; self.as_wasi_view().sync_data(fd).await.map_err(|e| { e.try_into() @@ -1274,8 +1297,12 @@ impl wasi_snapshot_preview1::WasiSnapshotPreview1 for WasiP1Ctx { /// Get the attributes of a file descriptor. /// NOTE: This returns similar flags to `fsync(fd, F_GETFL)` in POSIX, as well as additional fields. - #[instrument(skip(self))] - async fn fd_fdstat_get(&mut self, fd: types::Fd) -> Result { + #[instrument(skip(self, _memory))] + async fn fd_fdstat_get( + &mut self, + _memory: &mut GuestMemory<'_>, + fd: types::Fd, + ) -> Result { let (fd, blocking, append) = match self.transact()?.get_descriptor(fd)? { Descriptor::Stdin { isatty, .. } => { let fs_rights_base = types::Rights::FD_READ; @@ -1403,9 +1430,10 @@ impl wasi_snapshot_preview1::WasiSnapshotPreview1 for WasiP1Ctx { /// Adjust the flags associated with a file descriptor. /// NOTE: This is similar to `fcntl(fd, F_SETFL, flags)` in POSIX. - #[instrument(skip(self))] + #[instrument(skip(self, _memory))] fn fd_fdstat_set_flags( &mut self, + _memory: &mut GuestMemory<'_>, fd: types::Fd, flags: types::Fdflags, ) -> Result<(), types::Error> { @@ -1429,9 +1457,10 @@ impl wasi_snapshot_preview1::WasiSnapshotPreview1 for WasiP1Ctx { } /// Does not do anything if `fd` corresponds to a valid descriptor and returns `[types::Errno::Badf]` error otherwise. - #[instrument(skip(self))] + #[instrument(skip(self, _memory))] fn fd_fdstat_set_rights( &mut self, + _memory: &mut GuestMemory<'_>, fd: types::Fd, _fs_rights_base: types::Rights, _fs_rights_inheriting: types::Rights, @@ -1441,8 +1470,12 @@ impl wasi_snapshot_preview1::WasiSnapshotPreview1 for WasiP1Ctx { } /// Return the attributes of an open file. - #[instrument(skip(self))] - async fn fd_filestat_get(&mut self, fd: types::Fd) -> Result { + #[instrument(skip(self, _memory))] + async fn fd_filestat_get( + &mut self, + _memory: &mut GuestMemory<'_>, + fd: types::Fd, + ) -> Result { let t = self.transact()?; let desc = t.get_descriptor(fd)?; match desc { @@ -1502,9 +1535,10 @@ impl wasi_snapshot_preview1::WasiSnapshotPreview1 for WasiP1Ctx { /// Adjust the size of an open file. If this increases the file's size, the extra bytes are filled with zeros. /// NOTE: This is similar to `ftruncate` in POSIX. - #[instrument(skip(self))] + #[instrument(skip(self, _memory))] async fn fd_filestat_set_size( &mut self, + _memory: &mut GuestMemory<'_>, fd: types::Fd, size: types::Filesize, ) -> Result<(), types::Error> { @@ -1518,9 +1552,10 @@ impl wasi_snapshot_preview1::WasiSnapshotPreview1 for WasiP1Ctx { /// Adjust the timestamps of an open file or directory. /// NOTE: This is similar to `futimens` in POSIX. - #[instrument(skip(self))] + #[instrument(skip(self, _memory))] async fn fd_filestat_set_times( &mut self, + _memory: &mut GuestMemory<'_>, fd: types::Fd, atim: types::Timestamp, mtim: types::Timestamp, @@ -1550,11 +1585,12 @@ impl wasi_snapshot_preview1::WasiSnapshotPreview1 for WasiP1Ctx { /// Read from a file descriptor. /// NOTE: This is similar to `readv` in POSIX. - #[instrument(skip(self))] - async fn fd_read<'a>( + #[instrument(skip(self, memory))] + async fn fd_read( &mut self, + memory: &mut GuestMemory<'_>, fd: types::Fd, - iovs: &types::IovecArray<'a>, + iovs: types::IovecArray, ) -> Result { let t = self.transact()?; let desc = t.get_descriptor(fd)?; @@ -1572,10 +1608,10 @@ impl wasi_snapshot_preview1::WasiSnapshotPreview1 for WasiP1Ctx { drop(t); let pos = position.load(Ordering::Relaxed); let file = self.table().get(&fd)?.file()?; - let Some(iov) = first_non_empty_iovec(iovs)? else { + let Some(iov) = first_non_empty_iovec(memory, iovs)? else { return Ok(0); }; - let bytes_read = match (file.as_blocking_file(), iov.as_slice_mut()?) { + let bytes_read = match (file.as_blocking_file(), memory.as_slice_mut(iov)?) { // Try to read directly into wasm memory where possible // when the current thread can block and additionally wasm // memory isn't shared. @@ -1597,9 +1633,8 @@ impl wasi_snapshot_preview1::WasiSnapshotPreview1 for WasiP1Ctx { Ok(buf) }) .await?; - iov.get_range(0..u32::try_from(buf.len())?) - .unwrap() - .copy_from_slice(&buf)?; + let iov = iov.get_range(0..u32::try_from(buf.len())?).unwrap(); + memory.copy_from_slice(&buf, iov)?; buf.len() } }; @@ -1614,7 +1649,7 @@ impl wasi_snapshot_preview1::WasiSnapshotPreview1 for WasiP1Ctx { Descriptor::Stdin { stream, .. } => { let stream = stream.borrowed(); drop(t); - let Some(buf) = first_non_empty_iovec(iovs)? else { + let Some(buf) = first_non_empty_iovec(memory, iovs)? else { return Ok(0); }; let read = BlockingMode::Blocking @@ -1624,7 +1659,7 @@ impl wasi_snapshot_preview1::WasiSnapshotPreview1 for WasiP1Ctx { return Err(types::Errno::Range.into()); } let buf = buf.get_range(0..u32::try_from(read.len())?).unwrap(); - buf.copy_from_slice(&read)?; + memory.copy_from_slice(&read, buf)?; let n = read.len().try_into()?; Ok(n) } @@ -1634,11 +1669,12 @@ impl wasi_snapshot_preview1::WasiSnapshotPreview1 for WasiP1Ctx { /// Read from a file descriptor, without using and updating the file descriptor's offset. /// NOTE: This is similar to `preadv` in POSIX. - #[instrument(skip(self))] - async fn fd_pread<'a>( + #[instrument(skip(self, memory))] + async fn fd_pread( &mut self, + memory: &mut GuestMemory<'_>, fd: types::Fd, - iovs: &types::IovecArray<'a>, + iovs: types::IovecArray, offset: types::Filesize, ) -> Result { let t = self.transact()?; @@ -1650,7 +1686,7 @@ impl wasi_snapshot_preview1::WasiSnapshotPreview1 for WasiP1Ctx { let fd = fd.borrowed(); let blocking_mode = *blocking_mode; drop(t); - let Some(buf) = first_non_empty_iovec(iovs)? else { + let Some(buf) = first_non_empty_iovec(memory, iovs)? else { return Ok(0); }; @@ -1679,18 +1715,19 @@ impl wasi_snapshot_preview1::WasiSnapshotPreview1 for WasiP1Ctx { return Err(types::Errno::Range.into()); } let buf = buf.get_range(0..u32::try_from(read.len())?).unwrap(); - buf.copy_from_slice(&read)?; + memory.copy_from_slice(&read, buf)?; let n = read.len().try_into()?; Ok(n) } /// Write to a file descriptor. /// NOTE: This is similar to `writev` in POSIX. - #[instrument(skip(self))] - async fn fd_write<'a>( + #[instrument(skip(self, memory))] + async fn fd_write( &mut self, + memory: &mut GuestMemory<'_>, fd: types::Fd, - ciovs: &types::CiovecArray<'a>, + ciovs: types::CiovecArray, ) -> Result { let t = self.transact()?; let desc = t.get_descriptor(fd)?; @@ -1713,7 +1750,7 @@ impl wasi_snapshot_preview1::WasiSnapshotPreview1 for WasiP1Ctx { let append = *append; drop(t); let f = self.table().get(&fd)?.file()?; - let Some(buf) = first_non_empty_ciovec(ciovs)? else { + let Some(buf) = first_non_empty_ciovec(memory, ciovs)? else { return Ok(0); }; @@ -1728,12 +1765,12 @@ impl wasi_snapshot_preview1::WasiSnapshotPreview1 for WasiP1Ctx { let nwritten = match f.as_blocking_file() { // If we can block then skip the copy out of wasm memory and // write directly to `f`. - Some(f) => write(f, &buf.as_cow()?), + Some(f) => write(f, &memory.as_cow(buf)?), // ... otherwise copy out of wasm memory and use // `spawn_blocking` to do this write in a thread that can // block. None => { - let buf = buf.to_vec()?; + let buf = memory.to_vec(buf)?; f.spawn_blocking(move |f| write(f, &buf)).await } }; @@ -1753,11 +1790,11 @@ impl wasi_snapshot_preview1::WasiSnapshotPreview1 for WasiP1Ctx { Descriptor::Stdout { stream, .. } | Descriptor::Stderr { stream, .. } => { let stream = stream.borrowed(); drop(t); - let Some(buf) = first_non_empty_ciovec(ciovs)? else { + let Some(buf) = first_non_empty_ciovec(memory, ciovs)? else { return Ok(0); }; let n = BlockingMode::Blocking - .write(self, stream, buf) + .write(memory, self, stream, buf) .await? .try_into()?; Ok(n) @@ -1768,11 +1805,12 @@ impl wasi_snapshot_preview1::WasiSnapshotPreview1 for WasiP1Ctx { /// Write to a file descriptor, without using and updating the file descriptor's offset. /// NOTE: This is similar to `pwritev` in POSIX. - #[instrument(skip(self))] - async fn fd_pwrite<'a>( + #[instrument(skip(self, memory))] + async fn fd_pwrite( &mut self, + memory: &mut GuestMemory<'_>, fd: types::Fd, - ciovs: &types::CiovecArray<'a>, + ciovs: types::CiovecArray, offset: types::Filesize, ) -> Result { let t = self.transact()?; @@ -1784,7 +1822,7 @@ impl wasi_snapshot_preview1::WasiSnapshotPreview1 for WasiP1Ctx { let fd = fd.borrowed(); let blocking_mode = *blocking_mode; drop(t); - let Some(buf) = first_non_empty_ciovec(ciovs)? else { + let Some(buf) = first_non_empty_ciovec(memory, ciovs)? else { return Ok(0); }; let stream = self @@ -1796,7 +1834,7 @@ impl wasi_snapshot_preview1::WasiSnapshotPreview1 for WasiP1Ctx { .unwrap_or_else(types::Error::trap) })?; let result = blocking_mode - .write(self.as_wasi_view(), stream.borrowed(), buf) + .write(memory, self.as_wasi_view(), stream.borrowed(), buf) .await; streams::HostOutputStream::drop(self.as_wasi_view(), stream) .map_err(|e| types::Error::trap(e))?; @@ -1812,8 +1850,12 @@ impl wasi_snapshot_preview1::WasiSnapshotPreview1 for WasiP1Ctx { } /// Return a description of the given preopened file descriptor. - #[instrument(skip(self))] - fn fd_prestat_get(&mut self, fd: types::Fd) -> Result { + #[instrument(skip(self, _memory))] + fn fd_prestat_get( + &mut self, + _memory: &mut GuestMemory<'_>, + fd: types::Fd, + ) -> Result { if let Descriptor::Directory { preopen_path: Some(p), .. @@ -1826,11 +1868,12 @@ impl wasi_snapshot_preview1::WasiSnapshotPreview1 for WasiP1Ctx { } /// Return a description of the given preopened file descriptor. - #[instrument(skip(self))] - fn fd_prestat_dir_name<'a>( + #[instrument(skip(self, memory))] + fn fd_prestat_dir_name( &mut self, + memory: &mut GuestMemory<'_>, fd: types::Fd, - path: &GuestPtr<'a, u8>, + path: GuestPtr, path_max_len: types::Size, ) -> Result<(), types::Error> { let path_max_len = path_max_len.try_into()?; @@ -1842,15 +1885,20 @@ impl wasi_snapshot_preview1::WasiSnapshotPreview1 for WasiP1Ctx { if p.len() > path_max_len { return Err(types::Errno::Nametoolong.into()); } - write_bytes(path, p.as_bytes())?; + write_bytes(memory, path, p.as_bytes())?; return Ok(()); } Err(types::Errno::Notdir.into()) // NOTE: legacy implementation returns NOTDIR here } /// Atomically replace a file descriptor by renumbering another file descriptor. - #[instrument(skip(self))] - fn fd_renumber(&mut self, from: types::Fd, to: types::Fd) -> Result<(), types::Error> { + #[instrument(skip(self, _memory))] + fn fd_renumber( + &mut self, + _memory: &mut GuestMemory<'_>, + from: types::Fd, + to: types::Fd, + ) -> Result<(), types::Error> { let mut st = self.transact()?; let desc = st.descriptors.remove(from).ok_or(types::Errno::Badf)?; st.descriptors.insert(to.into(), desc); @@ -1859,9 +1907,10 @@ impl wasi_snapshot_preview1::WasiSnapshotPreview1 for WasiP1Ctx { /// Move the offset of a file descriptor. /// NOTE: This is similar to `lseek` in POSIX. - #[instrument(skip(self))] + #[instrument(skip(self, _memory))] async fn fd_seek( &mut self, + _memory: &mut GuestMemory<'_>, fd: types::Fd, offset: types::Filedelta, whence: types::Whence, @@ -1896,8 +1945,12 @@ impl wasi_snapshot_preview1::WasiSnapshotPreview1 for WasiP1Ctx { /// Synchronize the data and metadata of a file to disk. /// NOTE: This is similar to `fsync` in POSIX. - #[instrument(skip(self))] - async fn fd_sync(&mut self, fd: types::Fd) -> Result<(), types::Error> { + #[instrument(skip(self, _memory))] + async fn fd_sync( + &mut self, + _memory: &mut GuestMemory<'_>, + fd: types::Fd, + ) -> Result<(), types::Error> { let fd = self.get_file_fd(fd)?; self.as_wasi_view().sync(fd).await.map_err(|e| { e.try_into() @@ -1908,8 +1961,12 @@ impl wasi_snapshot_preview1::WasiSnapshotPreview1 for WasiP1Ctx { /// Return the current offset of a file descriptor. /// NOTE: This is similar to `lseek(fd, 0, SEEK_CUR)` in POSIX. - #[instrument(skip(self))] - fn fd_tell(&mut self, fd: types::Fd) -> Result { + #[instrument(skip(self, _memory))] + fn fd_tell( + &mut self, + _memory: &mut GuestMemory<'_>, + fd: types::Fd, + ) -> Result { let pos = self .transact()? .get_seekable(fd) @@ -1917,11 +1974,12 @@ impl wasi_snapshot_preview1::WasiSnapshotPreview1 for WasiP1Ctx { Ok(pos) } - #[instrument(skip(self))] - async fn fd_readdir<'a>( + #[instrument(skip(self, memory))] + async fn fd_readdir( &mut self, + memory: &mut GuestMemory<'_>, fd: types::Fd, - buf: &GuestPtr<'a, u8>, + buf: GuestPtr, buf_len: types::Size, cookie: types::Dircookie, ) -> Result { @@ -2009,7 +2067,7 @@ impl wasi_snapshot_preview1::WasiSnapshotPreview1 for WasiP1Ctx { DIRENT_SIZE, "Dirent guest repr and host repr should match" ); - let mut buf = *buf; + let mut buf = buf; let mut cap = buf_len; for (ref entry, path) in head.into_iter().chain(dir.into_iter()).skip(cookie) { let mut path = path.into_bytes(); @@ -2022,7 +2080,7 @@ impl wasi_snapshot_preview1::WasiSnapshotPreview1 for WasiP1Ctx { let entry = entry as *const _ as _; let entry = unsafe { slice::from_raw_parts(entry, entry_len as _) }; cap = cap.checked_sub(entry_len).unwrap(); - buf = write_bytes(buf, entry)?; + buf = write_bytes(memory, buf, entry)?; if cap == 0 { return Ok(buf_len); } @@ -2032,7 +2090,7 @@ impl wasi_snapshot_preview1::WasiSnapshotPreview1 for WasiP1Ctx { path.truncate(cap); } cap = cap.checked_sub(path.len() as _).unwrap(); - buf = write_bytes(buf, &path)?; + buf = write_bytes(memory, buf, &path)?; if cap == 0 { return Ok(buf_len); } @@ -2040,14 +2098,15 @@ impl wasi_snapshot_preview1::WasiSnapshotPreview1 for WasiP1Ctx { Ok(buf_len.checked_sub(cap).unwrap()) } - #[instrument(skip(self))] - async fn path_create_directory<'a>( + #[instrument(skip(self, memory))] + async fn path_create_directory( &mut self, + memory: &mut GuestMemory<'_>, dirfd: types::Fd, - path: &GuestPtr<'a, str>, + path: GuestPtr, ) -> Result<(), types::Error> { let dirfd = self.get_dir_fd(dirfd)?; - let path = read_string(path)?; + let path = read_string(memory, path)?; self.as_wasi_view() .create_directory_at(dirfd.borrowed(), path) .await @@ -2060,15 +2119,16 @@ impl wasi_snapshot_preview1::WasiSnapshotPreview1 for WasiP1Ctx { /// Return the attributes of a file or directory. /// NOTE: This is similar to `stat` in POSIX. - #[instrument(skip(self))] - async fn path_filestat_get<'a>( + #[instrument(skip(self, memory))] + async fn path_filestat_get( &mut self, + memory: &mut GuestMemory<'_>, dirfd: types::Fd, flags: types::Lookupflags, - path: &GuestPtr<'a, str>, + path: GuestPtr, ) -> Result { let dirfd = self.get_dir_fd(dirfd)?; - let path = read_string(path)?; + let path = read_string(memory, path)?; let filesystem::DescriptorStat { type_, link_count: nlink, @@ -2116,12 +2176,13 @@ impl wasi_snapshot_preview1::WasiSnapshotPreview1 for WasiP1Ctx { /// Adjust the timestamps of a file or directory. /// NOTE: This is similar to `utimensat` in POSIX. - #[instrument(skip(self))] - async fn path_filestat_set_times<'a>( + #[instrument(skip(self, memory))] + async fn path_filestat_set_times( &mut self, + memory: &mut GuestMemory<'_>, dirfd: types::Fd, flags: types::Lookupflags, - path: &GuestPtr<'a, str>, + path: GuestPtr, atim: types::Timestamp, mtim: types::Timestamp, fst_flags: types::Fstflags, @@ -2138,7 +2199,7 @@ impl wasi_snapshot_preview1::WasiSnapshotPreview1 for WasiP1Ctx { )?; let dirfd = self.get_dir_fd(dirfd)?; - let path = read_string(path)?; + let path = read_string(memory, path)?; self.as_wasi_view() .set_times_at(dirfd, flags.into(), path, atim, mtim) .await @@ -2151,19 +2212,20 @@ impl wasi_snapshot_preview1::WasiSnapshotPreview1 for WasiP1Ctx { /// Create a hard link. /// NOTE: This is similar to `linkat` in POSIX. - #[instrument(skip(self))] - async fn path_link<'a>( + #[instrument(skip(self, memory))] + async fn path_link( &mut self, + memory: &mut GuestMemory<'_>, src_fd: types::Fd, src_flags: types::Lookupflags, - src_path: &GuestPtr<'a, str>, + src_path: GuestPtr, target_fd: types::Fd, - target_path: &GuestPtr<'a, str>, + target_path: GuestPtr, ) -> Result<(), types::Error> { let src_fd = self.get_dir_fd(src_fd)?; let target_fd = self.get_dir_fd(target_fd)?; - let src_path = read_string(src_path)?; - let target_path = read_string(target_path)?; + let src_path = read_string(memory, src_path)?; + let target_path = read_string(memory, target_path)?; self.as_wasi_view() .link_at(src_fd, src_flags.into(), src_path, target_fd, target_path) .await @@ -2176,18 +2238,19 @@ impl wasi_snapshot_preview1::WasiSnapshotPreview1 for WasiP1Ctx { /// Open a file or directory. /// NOTE: This is similar to `openat` in POSIX. - #[instrument(skip(self))] - async fn path_open<'a>( + #[instrument(skip(self, memory))] + async fn path_open( &mut self, + memory: &mut GuestMemory<'_>, dirfd: types::Fd, dirflags: types::Lookupflags, - path: &GuestPtr<'a, str>, + path: GuestPtr, oflags: types::Oflags, fs_rights_base: types::Rights, _fs_rights_inheriting: types::Rights, fdflags: types::Fdflags, ) -> Result { - let path = read_string(path)?; + let path = read_string(memory, path)?; let mut flags = filesystem::DescriptorFlags::empty(); if fs_rights_base.contains(types::Rights::FD_READ) { @@ -2241,16 +2304,17 @@ impl wasi_snapshot_preview1::WasiSnapshotPreview1 for WasiP1Ctx { /// Read the contents of a symbolic link. /// NOTE: This is similar to `readlinkat` in POSIX. - #[instrument(skip(self))] - async fn path_readlink<'a>( + #[instrument(skip(self, memory))] + async fn path_readlink( &mut self, + memory: &mut GuestMemory<'_>, dirfd: types::Fd, - path: &GuestPtr<'a, str>, - buf: &GuestPtr<'a, u8>, + path: GuestPtr, + buf: GuestPtr, buf_len: types::Size, ) -> Result { let dirfd = self.get_dir_fd(dirfd)?; - let path = read_string(path)?; + let path = read_string(memory, path)?; let mut path = self .as_wasi_view() .readlink_at(dirfd, path) @@ -2266,18 +2330,19 @@ impl wasi_snapshot_preview1::WasiSnapshotPreview1 for WasiP1Ctx { path.truncate(buf_len); } let n = path.len().try_into().map_err(|_| types::Errno::Overflow)?; - write_bytes(buf, &path)?; + write_bytes(memory, buf, &path)?; Ok(n) } - #[instrument(skip(self))] - async fn path_remove_directory<'a>( + #[instrument(skip(self, memory))] + async fn path_remove_directory( &mut self, + memory: &mut GuestMemory<'_>, dirfd: types::Fd, - path: &GuestPtr<'a, str>, + path: GuestPtr, ) -> Result<(), types::Error> { let dirfd = self.get_dir_fd(dirfd)?; - let path = read_string(path)?; + let path = read_string(memory, path)?; self.as_wasi_view() .remove_directory_at(dirfd, path) .await @@ -2290,18 +2355,19 @@ impl wasi_snapshot_preview1::WasiSnapshotPreview1 for WasiP1Ctx { /// Rename a file or directory. /// NOTE: This is similar to `renameat` in POSIX. - #[instrument(skip(self))] - async fn path_rename<'a>( + #[instrument(skip(self, memory))] + async fn path_rename( &mut self, + memory: &mut GuestMemory<'_>, src_fd: types::Fd, - src_path: &GuestPtr<'a, str>, + src_path: GuestPtr, dest_fd: types::Fd, - dest_path: &GuestPtr<'a, str>, + dest_path: GuestPtr, ) -> Result<(), types::Error> { let src_fd = self.get_dir_fd(src_fd)?; let dest_fd = self.get_dir_fd(dest_fd)?; - let src_path = read_string(src_path)?; - let dest_path = read_string(dest_path)?; + let src_path = read_string(memory, src_path)?; + let dest_path = read_string(memory, dest_path)?; self.as_wasi_view() .rename_at(src_fd, src_path, dest_fd, dest_path) .await @@ -2312,16 +2378,17 @@ impl wasi_snapshot_preview1::WasiSnapshotPreview1 for WasiP1Ctx { }) } - #[instrument(skip(self))] - async fn path_symlink<'a>( + #[instrument(skip(self, memory))] + async fn path_symlink( &mut self, - src_path: &GuestPtr<'a, str>, + memory: &mut GuestMemory<'_>, + src_path: GuestPtr, dirfd: types::Fd, - dest_path: &GuestPtr<'a, str>, + dest_path: GuestPtr, ) -> Result<(), types::Error> { let dirfd = self.get_dir_fd(dirfd)?; - let src_path = read_string(src_path)?; - let dest_path = read_string(dest_path)?; + let src_path = read_string(memory, src_path)?; + let dest_path = read_string(memory, dest_path)?; self.as_wasi_view() .symlink_at(dirfd.borrowed(), src_path, dest_path) .await @@ -2332,14 +2399,15 @@ impl wasi_snapshot_preview1::WasiSnapshotPreview1 for WasiP1Ctx { }) } - #[instrument(skip(self))] - async fn path_unlink_file<'a>( + #[instrument(skip(self, memory))] + async fn path_unlink_file( &mut self, + memory: &mut GuestMemory<'_>, dirfd: types::Fd, - path: &GuestPtr<'a, str>, + path: GuestPtr, ) -> Result<(), types::Error> { let dirfd = self.get_dir_fd(dirfd)?; - let path = path.as_cow()?.to_string(); + let path = memory.as_cow_str(path)?.into_owned(); self.as_wasi_view() .unlink_file_at(dirfd.borrowed(), path) .await @@ -2350,11 +2418,12 @@ impl wasi_snapshot_preview1::WasiSnapshotPreview1 for WasiP1Ctx { }) } - #[instrument(skip(self))] - async fn poll_oneoff<'a>( + #[instrument(skip(self, memory))] + async fn poll_oneoff( &mut self, - subs: &GuestPtr<'a, types::Subscription>, - events: &GuestPtr<'a, types::Event>, + memory: &mut GuestMemory<'_>, + subs: GuestPtr, + events: GuestPtr, nsubscriptions: types::Size, ) -> Result { if nsubscriptions == 0 { @@ -2368,7 +2437,7 @@ impl wasi_snapshot_preview1::WasiSnapshotPreview1 for WasiP1Ctx { // results in more consistent sleep times. This design ensures that // wasmtime can handle real-time requirements more accurately. if nsubscriptions == 1 { - let sub = subs.read()?; + let sub = memory.read(subs)?; if let types::SubscriptionU::Clock(clocksub) = sub.u { if !clocksub .flags @@ -2376,15 +2445,18 @@ impl wasi_snapshot_preview1::WasiSnapshotPreview1 for WasiP1Ctx { && self.ctx().allow_blocking_current_thread { std::thread::sleep(std::time::Duration::from_nanos(clocksub.timeout)); - events.write(types::Event { - userdata: sub.userdata, - error: types::Errno::Success, - type_: types::Eventtype::Clock, - fd_readwrite: types::EventFdReadwrite { - flags: types::Eventrwflags::empty(), - nbytes: 1, + memory.write( + events, + types::Event { + userdata: sub.userdata, + error: types::Errno::Success, + type_: types::Eventtype::Clock, + fd_readwrite: types::EventFdReadwrite { + flags: types::Eventrwflags::empty(), + nbytes: 1, + }, }, - })?; + )?; return Ok(1); } } @@ -2396,7 +2468,7 @@ impl wasi_snapshot_preview1::WasiSnapshotPreview1 for WasiP1Ctx { let n = usize::try_from(nsubscriptions).unwrap_or(usize::MAX); let mut pollables = Vec::with_capacity(n); for sub in subs.iter() { - let sub = sub?.read()?; + let sub = memory.read(sub?)?; let p = match sub.u { types::SubscriptionU::Clock(types::SubscriptionClock { id, @@ -2527,7 +2599,7 @@ impl wasi_snapshot_preview1::WasiSnapshotPreview1 for WasiP1Ctx { .filter_map(|(idx, sub)| ready.contains(&idx).then_some(sub)) .zip(events.iter()) { - let sub = sub?.read()?; + let sub = memory.read(sub?)?; let event = event?; let e = match sub.u { types::SubscriptionU::Clock(..) => types::Event { @@ -2611,7 +2683,7 @@ impl wasi_snapshot_preview1::WasiSnapshotPreview1 for WasiP1Ctx { } } }; - event.write(e)?; + memory.write(event, e)?; count = count .checked_add(1) .ok_or_else(|| types::Error::from(types::Errno::Overflow))? @@ -2619,8 +2691,12 @@ impl wasi_snapshot_preview1::WasiSnapshotPreview1 for WasiP1Ctx { Ok(count) } - #[instrument(skip(self))] - fn proc_exit(&mut self, status: types::Exitcode) -> anyhow::Error { + #[instrument(skip(self, _memory))] + fn proc_exit( + &mut self, + _memory: &mut GuestMemory<'_>, + status: types::Exitcode, + ) -> anyhow::Error { // Check that the status is within WASI's range. if status >= 126 { return anyhow::Error::msg("exit with invalid exit status outside of [0..126)"); @@ -2628,21 +2704,26 @@ impl wasi_snapshot_preview1::WasiSnapshotPreview1 for WasiP1Ctx { crate::I32Exit(status as i32).into() } - #[instrument(skip(self))] - fn proc_raise(&mut self, _sig: types::Signal) -> Result<(), types::Error> { + #[instrument(skip(self, _memory))] + fn proc_raise( + &mut self, + _memory: &mut GuestMemory<'_>, + _sig: types::Signal, + ) -> Result<(), types::Error> { Err(types::Errno::Notsup.into()) } - #[instrument(skip(self))] - fn sched_yield(&mut self) -> Result<(), types::Error> { + #[instrument(skip(self, _memory))] + fn sched_yield(&mut self, _memory: &mut GuestMemory<'_>) -> Result<(), types::Error> { // No such thing in preview 2. Intentionally left empty. Ok(()) } - #[instrument(skip(self))] - fn random_get<'a>( + #[instrument(skip(self, memory))] + fn random_get( &mut self, - buf: &GuestPtr<'a, u8>, + memory: &mut GuestMemory<'_>, + buf: GuestPtr, buf_len: types::Size, ) -> Result<(), types::Error> { let rand = self @@ -2650,14 +2731,15 @@ impl wasi_snapshot_preview1::WasiSnapshotPreview1 for WasiP1Ctx { .get_random_bytes(buf_len.into()) .context("failed to call `get-random-bytes`") .map_err(types::Error::trap)?; - write_bytes(buf, &rand)?; + write_bytes(memory, buf, &rand)?; Ok(()) } #[allow(unused_variables)] - #[instrument(skip(self))] + #[instrument(skip(self, _memory))] fn sock_accept( &mut self, + _memory: &mut GuestMemory<'_>, fd: types::Fd, flags: types::Fdflags, ) -> Result { @@ -2665,30 +2747,37 @@ impl wasi_snapshot_preview1::WasiSnapshotPreview1 for WasiP1Ctx { } #[allow(unused_variables)] - #[instrument(skip(self))] - fn sock_recv<'a>( + #[instrument(skip(self, _memory))] + fn sock_recv( &mut self, + _memory: &mut GuestMemory<'_>, fd: types::Fd, - ri_data: &types::IovecArray<'a>, + ri_data: types::IovecArray, ri_flags: types::Riflags, ) -> Result<(types::Size, types::Roflags), types::Error> { todo!("preview1 sock_recv is not implemented") } #[allow(unused_variables)] - #[instrument(skip(self))] - fn sock_send<'a>( + #[instrument(skip(self, _memory))] + fn sock_send( &mut self, + _memory: &mut GuestMemory<'_>, fd: types::Fd, - si_data: &types::CiovecArray<'a>, + si_data: types::CiovecArray, _si_flags: types::Siflags, ) -> Result { todo!("preview1 sock_send is not implemented") } #[allow(unused_variables)] - #[instrument(skip(self))] - fn sock_shutdown(&mut self, fd: types::Fd, how: types::Sdflags) -> Result<(), types::Error> { + #[instrument(skip(self, _memory))] + fn sock_shutdown( + &mut self, + _memory: &mut GuestMemory<'_>, + fd: types::Fd, + how: types::Sdflags, + ) -> Result<(), types::Error> { todo!("preview1 sock_shutdown is not implemented") } } diff --git a/crates/wiggle/generate/src/funcs.rs b/crates/wiggle/generate/src/funcs.rs index 0bf41f7ca7ad..8498bef46cdf 100644 --- a/crates/wiggle/generate/src/funcs.rs +++ b/crates/wiggle/generate/src/funcs.rs @@ -78,9 +78,9 @@ fn _define_func( ); ); let ctx_type = if settings.mutable { - quote!(&'a mut) + quote!(&mut) } else { - quote!(&'a) + quote!(&) }; if settings.get_async(&module, &func).is_sync() { let traced_body = if settings.tracing.enabled_for(&mod_name, &func_name) { @@ -96,9 +96,9 @@ fn _define_func( ( quote!( #[allow(unreachable_code)] // deals with warnings in noreturn functions - pub fn #ident<'a>( + pub fn #ident( ctx: #ctx_type (impl #(#bounds)+*), - memory: &dyn wiggle::GuestMemory, + memory: &mut wiggle::GuestMemory<'_>, #(#abi_params),* ) -> wiggle::anyhow::Result<#abi_ret> { use std::convert::TryFrom as _; @@ -114,23 +114,21 @@ fn _define_func( #mk_span async move { #body - }.instrument(_span) + }.instrument(_span).await ) } else { quote!( - async move { - #body - } + #body ) }; ( quote!( #[allow(unreachable_code)] // deals with warnings in noreturn functions - pub fn #ident<'a>( + pub async fn #ident( ctx: #ctx_type (impl #(#bounds)+*), - memory: &'a dyn wiggle::GuestMemory, + memory: &mut wiggle::GuestMemory<'_>, #(#abi_params),* - ) -> impl std::future::Future> + 'a { + ) -> wiggle::anyhow::Result<#abi_ret> { use std::convert::TryFrom as _; #traced_body } @@ -224,7 +222,7 @@ impl witx::Bindgen for Rust<'_> { let val = operands.pop().unwrap(); let pointee_type = names::type_ref(ty, anon_lifetime()); results.push(quote! { - wiggle::GuestPtr::<#pointee_type>::new(memory, #val as u32) + wiggle::GuestPtr::<#pointee_type>::new(#val as u32) }); } @@ -239,7 +237,7 @@ impl witx::Bindgen for Rust<'_> { } }; results.push(quote! { - wiggle::GuestPtr::<#ty>::new(memory, (#ptr as u32, #len as u32)); + wiggle::GuestPtr::<#ty>::new((#ptr as u32, #len as u32)); }) } @@ -284,11 +282,11 @@ impl witx::Bindgen for Rust<'_> { let ident = names::func(&func.name); if self.settings.get_async(&self.module, &func).is_sync() { self.src.extend(quote! { - let ret = #trait_name::#ident(ctx, #(#args),*); + let ret = #trait_name::#ident(ctx, memory, #(#args),*); }) } else { self.src.extend(quote! { - let ret = #trait_name::#ident(ctx, #(#args),*).await; + let ret = #trait_name::#ident(ctx, memory, #(#args),*).await; }) }; if self @@ -368,9 +366,11 @@ impl witx::Bindgen for Rust<'_> { let wrap_err = wrap_err(&format!("write {}", ty.name.as_str())); let pointee_type = names::type_(&ty.name); self.src.extend(quote! { - wiggle::GuestPtr::<#pointee_type>::new(memory, #ptr as u32) - .write(#val) - .map_err(#wrap_err)?; + memory.write( + wiggle::GuestPtr::<#pointee_type>::new(#ptr as u32), + #val, + ) + .map_err(#wrap_err)?; }); } @@ -379,8 +379,7 @@ impl witx::Bindgen for Rust<'_> { let wrap_err = wrap_err(&format!("read {}", ty.name.as_str())); let pointee_type = names::type_(&ty.name); results.push(quote! { - wiggle::GuestPtr::<#pointee_type>::new(memory, #ptr as u32) - .read() + memory.read(wiggle::GuestPtr::<#pointee_type>::new(#ptr as u32)) .map_err(#wrap_err)? }); } diff --git a/crates/wiggle/generate/src/module_trait.rs b/crates/wiggle/generate/src/module_trait.rs index 7a7b52fb0c30..fb57919c4b4e 100644 --- a/crates/wiggle/generate/src/module_trait.rs +++ b/crates/wiggle/generate/src/module_trait.rs @@ -2,13 +2,11 @@ use proc_macro2::TokenStream; use quote::quote; use crate::codegen_settings::{CodegenSettings, ErrorType}; -use crate::lifetimes::{anon_lifetime, LifetimeExt}; use crate::names; use witx::Module; pub fn passed_by_reference(ty: &witx::Type) -> bool { match ty { - witx::Type::Pointer(_) | witx::Type::ConstPointer(_) | witx::Type::List(_) => true, witx::Type::Record(r) => r.bitflags_repr().is_none(), witx::Type::Variant(v) => !v.is_enum(), _ => false, @@ -18,23 +16,10 @@ pub fn passed_by_reference(ty: &witx::Type) -> bool { pub fn define_module_trait(m: &Module, settings: &CodegenSettings) -> TokenStream { let traitname = names::trait_name(&m.name); let traitmethods = m.funcs().map(|f| { - // Check if we're returning an entity anotated with a lifetime, - // in which case, we'll need to annotate the function itself, and - // hence will need an explicit lifetime (rather than anonymous) - let (lifetime, is_anonymous) = if f - .params - .iter() - .chain(&f.results) - .any(|ret| ret.tref.needs_lifetime()) - { - (quote!('a), false) - } else { - (anon_lifetime(), true) - }; let funcname = names::func(&f.name); let args = f.params.iter().map(|arg| { let arg_name = names::func_param(&arg.name); - let arg_typename = names::type_ref(&arg.tref, lifetime.clone()); + let arg_typename = names::type_ref(&arg.tref, quote!()); let arg_type = if passed_by_reference(&*arg.tref.type_()) { quote!(&#arg_typename) } else { @@ -56,7 +41,7 @@ pub fn define_module_trait(m: &Module, settings: &CodegenSettings) -> TokenStrea }; let ok = match ok { - Some(ty) => names::type_ref(ty, lifetime.clone()), + Some(ty) => names::type_ref(ty, quote!()), None => quote!(()), }; let err = match err { @@ -66,7 +51,7 @@ pub fn define_module_trait(m: &Module, settings: &CodegenSettings) -> TokenStrea quote!(super::#tn) } Some(ErrorType::Generated(g)) => g.typename(), - None => names::type_ref(ty, lifetime.clone()), + None => names::type_ref(ty, quote!()), }, None => quote!(()), }; @@ -86,11 +71,13 @@ pub fn define_module_trait(m: &Module, settings: &CodegenSettings) -> TokenStrea } else { quote!(&self) }; - if is_anonymous { - quote!(#asyncness fn #funcname(#self_, #(#args),*) -> #result; ) - } else { - quote!(#asyncness fn #funcname<#lifetime>(#self_, #(#args),*) -> #result;) - } + quote!( + #asyncness fn #funcname( + #self_, + mem: &mut wiggle::GuestMemory<'_>, + #(#args),* + ) -> #result; + ) }); quote! { diff --git a/crates/wiggle/generate/src/names.rs b/crates/wiggle/generate/src/names.rs index a32a35be5740..80401e44db86 100644 --- a/crates/wiggle/generate/src/names.rs +++ b/crates/wiggle/generate/src/names.rs @@ -4,7 +4,7 @@ use proc_macro2::{Ident, TokenStream}; use quote::{format_ident, quote}; use witx::{BuiltinType, Id, Type, TypeRef, WasmType}; -use crate::{lifetimes::LifetimeExt, UserErrorType}; +use crate::UserErrorType; pub fn type_(id: &Id) -> Ident { escape_id(id, NamingConvention::CamelCase) @@ -39,25 +39,21 @@ pub fn type_ref(tref: &TypeRef, lifetime: TokenStream) -> TokenStream { match tref { TypeRef::Name(nt) => { let ident = type_(&nt.name); - if nt.tref.needs_lifetime() { - quote!(#ident<#lifetime>) - } else { - quote!(#ident) - } + quote!(#ident) } TypeRef::Value(ty) => match &**ty { Type::Builtin(builtin) => builtin_type(*builtin), Type::Pointer(pointee) | Type::ConstPointer(pointee) => { let pointee_type = type_ref(&pointee, lifetime.clone()); - quote!(wiggle::GuestPtr<#lifetime, #pointee_type>) + quote!(wiggle::GuestPtr<#pointee_type>) } Type::List(pointee) => match &**pointee.type_() { Type::Builtin(BuiltinType::Char) => { - quote!(wiggle::GuestPtr<#lifetime, str>) + quote!(wiggle::GuestPtr) } _ => { let pointee_type = type_ref(&pointee, lifetime.clone()); - quote!(wiggle::GuestPtr<#lifetime, [#pointee_type]>) + quote!(wiggle::GuestPtr<[#pointee_type]>) } }, Type::Variant(v) => match v.as_expected() { diff --git a/crates/wiggle/generate/src/types/flags.rs b/crates/wiggle/generate/src/types/flags.rs index 5c117a845a65..8687d95a4a02 100644 --- a/crates/wiggle/generate/src/types/flags.rs +++ b/crates/wiggle/generate/src/types/flags.rs @@ -65,7 +65,7 @@ pub(super) fn define_flags( } } - impl<'a> wiggle::GuestType<'a> for #ident { + impl wiggle::GuestType for #ident { #[inline] fn guest_size() -> u32 { #repr::guest_size() @@ -76,16 +76,16 @@ pub(super) fn define_flags( #repr::guest_align() } - fn read(location: &wiggle::GuestPtr<#ident>) -> Result<#ident, wiggle::GuestError> { + fn read(mem: &wiggle::GuestMemory, location: wiggle::GuestPtr<#ident>) -> Result<#ident, wiggle::GuestError> { use std::convert::TryFrom; - let reprval = #repr::read(&location.cast())?; + let reprval = #repr::read(mem, location.cast())?; let value = #ident::try_from(reprval)?; Ok(value) } - fn write(location: &wiggle::GuestPtr<'_, #ident>, val: Self) -> Result<(), wiggle::GuestError> { + fn write(mem: &mut wiggle::GuestMemory, location: wiggle::GuestPtr<#ident>, val: Self) -> Result<(), wiggle::GuestError> { let val: #repr = #repr::from(val); - #repr::write(&location.cast(), val) + #repr::write(mem, location.cast(), val) } } } diff --git a/crates/wiggle/generate/src/types/handle.rs b/crates/wiggle/generate/src/types/handle.rs index 8d9b550316bc..34540f295b4c 100644 --- a/crates/wiggle/generate/src/types/handle.rs +++ b/crates/wiggle/generate/src/types/handle.rs @@ -53,7 +53,7 @@ pub(super) fn define_handle(name: &witx::Id, h: &witx::HandleDatatype) -> TokenS } } - impl<'a> wiggle::GuestType<'a> for #ident { + impl wiggle::GuestType for #ident { #[inline] fn guest_size() -> u32 { #size @@ -65,13 +65,13 @@ pub(super) fn define_handle(name: &witx::Id, h: &witx::HandleDatatype) -> TokenS } #[inline] - fn read(location: &wiggle::GuestPtr<'a, #ident>) -> Result<#ident, wiggle::GuestError> { - Ok(#ident(u32::read(&location.cast())?)) + fn read(mem: &wiggle::GuestMemory, location: wiggle::GuestPtr<#ident>) -> Result<#ident, wiggle::GuestError> { + Ok(#ident(u32::read(mem, location.cast())?)) } #[inline] - fn write(location: &wiggle::GuestPtr<'_, Self>, val: Self) -> Result<(), wiggle::GuestError> { - u32::write(&location.cast(), val.0) + fn write(mem: &mut wiggle::GuestMemory, location: wiggle::GuestPtr, val: Self) -> Result<(), wiggle::GuestError> { + u32::write(mem, location.cast(), val.0) } } } diff --git a/crates/wiggle/generate/src/types/mod.rs b/crates/wiggle/generate/src/types/mod.rs index 2820db9fbd29..556c14ca21a3 100644 --- a/crates/wiggle/generate/src/types/mod.rs +++ b/crates/wiggle/generate/src/types/mod.rs @@ -70,8 +70,8 @@ fn define_witx_pointer( fn define_witx_list(name: &witx::Id, arr_raw: &witx::TypeRef) -> TokenStream { let ident = names::type_(name); - let pointee_type = names::type_ref(arr_raw, quote!('a)); - quote!(pub type #ident<'a> = wiggle::GuestPtr<'a, [#pointee_type]>;) + let pointee_type = names::type_ref(arr_raw, quote!()); + quote!(pub type #ident = wiggle::GuestPtr<[#pointee_type]>;) } pub fn int_repr_tokens(int_repr: witx::IntRepr) -> TokenStream { diff --git a/crates/wiggle/generate/src/types/record.rs b/crates/wiggle/generate/src/types/record.rs index 0febd104026e..2b71d568fc91 100644 --- a/crates/wiggle/generate/src/types/record.rs +++ b/crates/wiggle/generate/src/types/record.rs @@ -16,17 +16,13 @@ pub(super) fn define_struct(name: &witx::Id, s: &witx::RecordDatatype) -> TokenS let type_ = match &m.tref { witx::TypeRef::Name(nt) => { let tt = names::type_(&nt.name); - if m.tref.needs_lifetime() { - quote!(#tt<'a>) - } else { - quote!(#tt) - } + quote!(#tt) } witx::TypeRef::Value(ty) => match &**ty { witx::Type::Builtin(builtin) => names::builtin_type(*builtin), witx::Type::Pointer(pointee) | witx::Type::ConstPointer(pointee) => { let pointee_type = names::type_ref(&pointee, quote!('a)); - quote!(wiggle::GuestPtr<'a, #pointee_type>) + quote!(wiggle::GuestPtr<#pointee_type>) } _ => unimplemented!("other anonymous struct members: {:?}", m.tref), }, @@ -53,20 +49,20 @@ pub(super) fn define_struct(name: &witx::Id, s: &witx::RecordDatatype) -> TokenS witx::TypeRef::Name(nt) => { let type_ = names::type_(&nt.name); quote! { - let #name = <#type_ as wiggle::GuestType>::read(&#location)?; + let #name = <#type_ as wiggle::GuestType>::read(mem, #location)?; } } witx::TypeRef::Value(ty) => match &**ty { witx::Type::Builtin(builtin) => { let type_ = names::builtin_type(*builtin); quote! { - let #name = <#type_ as wiggle::GuestType>::read(&#location)?; + let #name = <#type_ as wiggle::GuestType>::read(mem, #location)?; } } witx::Type::Pointer(pointee) | witx::Type::ConstPointer(pointee) => { let pointee_type = names::type_ref(&pointee, anon_lifetime()); quote! { - let #name = as wiggle::GuestType>::read(&#location)?; + let #name = as wiggle::GuestType>::read(mem, #location)?; } } _ => unimplemented!("other anonymous struct members: {:?}", ty), @@ -79,7 +75,8 @@ pub(super) fn define_struct(name: &witx::Id, s: &witx::RecordDatatype) -> TokenS let offset = ml.offset as u32; quote! { wiggle::GuestType::write( - &location.cast::().add(#offset)?.cast(), + mem, + location.cast::().add(#offset)?.cast(), val.#name, )?; } @@ -93,15 +90,15 @@ pub(super) fn define_struct(name: &witx::Id, s: &witx::RecordDatatype) -> TokenS quote! { #[derive(Clone, Debug #extra_derive)] - pub struct #ident #struct_lifetime { + pub struct #ident { #(#member_decls),* } - impl #struct_lifetime #ident #struct_lifetime { + impl #struct_lifetime #ident { #(#member_offsets)* } - impl<'a> wiggle::GuestType<'a> for #ident #struct_lifetime { + impl wiggle::GuestType for #ident { #[inline] fn guest_size() -> u32 { #size @@ -112,12 +109,12 @@ pub(super) fn define_struct(name: &witx::Id, s: &witx::RecordDatatype) -> TokenS #align } - fn read(location: &wiggle::GuestPtr<'a, Self>) -> Result { + fn read(mem: &wiggle::GuestMemory, location: wiggle::GuestPtr) -> Result { #(#member_reads)* Ok(#ident { #(#member_names),* }) } - fn write(location: &wiggle::GuestPtr<'_, Self>, val: Self) -> Result<(), wiggle::GuestError> { + fn write(mem: &mut wiggle::GuestMemory, location: wiggle::GuestPtr, val: Self) -> Result<(), wiggle::GuestError> { #(#member_writes)* Ok(()) } diff --git a/crates/wiggle/generate/src/types/variant.rs b/crates/wiggle/generate/src/types/variant.rs index df340edbfc25..0585660c563c 100644 --- a/crates/wiggle/generate/src/types/variant.rs +++ b/crates/wiggle/generate/src/types/variant.rs @@ -1,4 +1,3 @@ -use crate::lifetimes::LifetimeExt; use crate::names; use proc_macro2::{Literal, TokenStream}; @@ -36,7 +35,7 @@ pub(super) fn define_variant( quote! { #i => { let variant_ptr = location.cast::().add(#contents_offset)?; - let variant_val = <#varianttype as wiggle::GuestType>::read(&variant_ptr.cast())?; + let variant_val = <#varianttype as wiggle::GuestType>::read(mem, variant_ptr.cast())?; Ok(#ident::#variantname(variant_val)) } } @@ -48,7 +47,7 @@ pub(super) fn define_variant( let write_variant = v.cases.iter().enumerate().map(|(i, c)| { let variantname = names::enum_variant(&c.name); let write_tag = quote! { - location.cast().write(#i as #tag_ty)?; + mem.write(location.cast(), #i as #tag_ty)?; }; if let Some(tref) = &c.tref { let varianttype = names::type_ref(tref, lifetime.clone()); @@ -56,7 +55,7 @@ pub(super) fn define_variant( #ident::#variantname(contents) => { #write_tag let variant_ptr = location.cast::().add(#contents_offset)?; - <#varianttype as wiggle::GuestType>::write(&variant_ptr.cast(), contents)?; + <#varianttype as wiggle::GuestType>::write(mem, variant_ptr.cast(), contents)?; } } } else { @@ -121,11 +120,7 @@ pub(super) fn define_variant( quote!() }; - let (enum_lifetime, extra_derive) = if v.needs_lifetime() { - (quote!(<'a>), quote!()) - } else { - (quote!(), quote!(, PartialEq #extra_derive)) - }; + let extra_derive = quote!(, PartialEq #extra_derive); let error_impls = if derive_std_error { quote! { @@ -142,7 +137,7 @@ pub(super) fn define_variant( quote! { #[derive(Clone, Debug #extra_derive)] - pub enum #ident #enum_lifetime { + pub enum #ident { #(#variants),* } #error_impls @@ -150,7 +145,7 @@ pub(super) fn define_variant( #enum_try_from #enum_from - impl<'a> wiggle::GuestType<'a> for #ident #enum_lifetime { + impl wiggle::GuestType for #ident { #[inline] fn guest_size() -> u32 { #size @@ -161,10 +156,10 @@ pub(super) fn define_variant( #align } - fn read(location: &wiggle::GuestPtr<'a, Self>) + fn read(mem: &wiggle::GuestMemory, location: wiggle::GuestPtr) -> Result { - let tag = location.cast::<#tag_ty>().read()?; + let tag = mem.read(location.cast::<#tag_ty>())?; match tag { #(#read_variant)* _ => Err(wiggle::GuestError::InvalidEnumValue(stringify!(#ident))), @@ -172,7 +167,7 @@ pub(super) fn define_variant( } - fn write(location: &wiggle::GuestPtr<'_, Self>, val: Self) + fn write(mem: &mut wiggle::GuestMemory, location: wiggle::GuestPtr, val: Self) -> Result<(), wiggle::GuestError> { match val { diff --git a/crates/wiggle/generate/src/wasmtime.rs b/crates/wiggle/generate/src/wasmtime.rs index 0db270e27306..46158a224647 100644 --- a/crates/wiggle/generate/src/wasmtime.rs +++ b/crates/wiggle/generate/src/wasmtime.rs @@ -113,19 +113,19 @@ fn generate_func( let body = quote! { let export = caller.get_export("memory"); - let (mem, ctx) = match &export { + let (mut mem, ctx) = match &export { Some(wiggle::wasmtime_crate::Extern::Memory(m)) => { let (mem, ctx) = m.data_and_store_mut(&mut caller); let ctx = get_cx(ctx); - (wiggle::wasmtime::WasmtimeGuestMemory::new(mem), ctx) + (wiggle::GuestMemory::Unshared(mem), ctx) } Some(wiggle::wasmtime_crate::Extern::SharedMemory(m)) => { let ctx = get_cx(caller.data_mut()); - (wiggle::wasmtime::WasmtimeGuestMemory::shared(m.data()), ctx) + (wiggle::GuestMemory::Shared(m.data()), ctx) } _ => wiggle::anyhow::bail!("missing required memory export"), }; - Ok(<#ret_ty>::from(#abi_func(ctx, &mem #(, #arg_names)*) #await_ ?)) + Ok(<#ret_ty>::from(#abi_func(ctx, &mut mem #(, #arg_names)*) #await_ ?)) }; match asyncness { diff --git a/crates/wiggle/macro/build.rs b/crates/wiggle/macro/build.rs new file mode 100644 index 000000000000..be0ad5bc97cb --- /dev/null +++ b/crates/wiggle/macro/build.rs @@ -0,0 +1,5 @@ +fn main() { + println!("cargo:rerun-if-changed=build.rs"); + let out_dir = std::env::var("OUT_DIR").unwrap(); + println!("cargo:rustc-env=DEBUG_OUTPUT_DIR={out_dir}"); +} diff --git a/crates/wiggle/macro/src/lib.rs b/crates/wiggle/macro/src/lib.rs index 81304fcccf9b..474b71d6eafc 100644 --- a/crates/wiggle/macro/src/lib.rs +++ b/crates/wiggle/macro/src/lib.rs @@ -168,7 +168,30 @@ pub fn from_witx(args: TokenStream) -> TokenStream { quote!() }; - TokenStream::from(quote! { #code #metadata }) + let mut ret = quote! { #code #metadata }; + + if std::env::var("WIGGLE_DEBUG_BINDGEN").is_ok() { + use std::path::Path; + use std::sync::atomic::{AtomicUsize, Ordering::Relaxed}; + static INVOCATION: AtomicUsize = AtomicUsize::new(0); + let root = Path::new(env!("DEBUG_OUTPUT_DIR")); + let n = INVOCATION.fetch_add(1, Relaxed); + let path = root.join(format!("wiggle{n}.rs")); + + std::fs::write(&path, ret.to_string()).unwrap(); + + // optimistically format the code but don't require success + drop( + std::process::Command::new("rustfmt") + .arg(&path) + .arg("--edition=2021") + .output(), + ); + + let path = path.to_str().unwrap(); + ret = quote!(include!(#path);); + } + TokenStream::from(ret) } #[proc_macro_attribute] diff --git a/crates/wiggle/src/borrow.rs b/crates/wiggle/src/borrow.rs deleted file mode 100644 index f89c67820d47..000000000000 --- a/crates/wiggle/src/borrow.rs +++ /dev/null @@ -1,113 +0,0 @@ -use crate::{BorrowHandle, GuestError, Region}; -use std::sync::atomic::{AtomicU32, Ordering::Relaxed}; - -/// A simple borrow checker to implement the API guarantees of Wiggle. -/// -/// This is not a generalized borrow checker and is coarse-grained where it -/// doesn't actually take any regions into account. Instead it only tracks -/// whether there are temporally any shared/mutable borrows. This is -/// more-or-less a poor-man's `RefCell`. -/// -/// Note that this uses `&AtomicU32` because this is passed around as -/// `&BorrowChecker` in all `GuestPtr` structures. This needs to be mutated -/// which might look like it needs `Cell`, but `&Cell` isn't `Sync` -/// and we want futures with `&BorrowChecker` to be `Sync`, so this is an atomic -/// instead. Only one of these is created per-invocation though and it's not -/// actually shared across threads, so mutations here are not done with -/// compare-and-swap but instead just loads/stores. -pub struct BorrowChecker { - // 0 => no borrows - // >0 => shared borrows - // u32::MAX => mutable borrow - state: AtomicU32, -} - -impl BorrowChecker { - pub fn new() -> Self { - BorrowChecker { - state: AtomicU32::new(0), - } - } - pub fn shared_borrow(&self, r: Region) -> Result { - match self.state.load(Relaxed) { - n if n >= u32::MAX - 1 => Err(GuestError::PtrBorrowed(r)), - n => { - self.state.store(n + 1, Relaxed); - Ok(BorrowHandle { _priv: () }) - } - } - } - pub fn mut_borrow(&self, r: Region) -> Result { - match self.state.load(Relaxed) { - 0 => { - self.state.store(u32::MAX, Relaxed); - Ok(BorrowHandle { _priv: () }) - } - _ => Err(GuestError::PtrBorrowed(r)), - } - } - pub fn shared_unborrow(&self, _: BorrowHandle) { - let prev = self.state.load(Relaxed); - debug_assert!(prev > 0); - self.state.store(prev - 1, Relaxed); - } - pub fn mut_unborrow(&self, _: BorrowHandle) { - let prev = self.state.load(Relaxed); - debug_assert!(prev == u32::MAX); - self.state.store(0, Relaxed); - } - pub fn can_read(&self, _: Region) -> bool { - self.state.load(Relaxed) != u32::MAX - } - pub fn can_write(&self, _: Region) -> bool { - self.state.load(Relaxed) == 0 - } -} - -#[cfg(test)] -mod test { - use super::*; - - #[test] - fn smoke() { - let b = BorrowChecker::new(); - let mut next = 0; - let mut region = || { - let a = next; - next += 1; - Region::new(a, a + 1) - }; - - // can read/write initially - assert!(b.can_read(region())); - assert!(b.can_write(region())); - - // can shared borrow multiple times which will prevent mutable borrows - let h1 = b.shared_borrow(region()).unwrap(); - let h2 = b.shared_borrow(region()).unwrap(); - assert!(b.mut_borrow(region()).is_err()); - - // can read, but can't write, while there are shared borrows - assert!(b.can_read(region())); - assert!(!b.can_write(region())); - - // releasing shared borrows enables reading/writing again - b.shared_unborrow(h1); - b.shared_unborrow(h2); - assert!(b.can_read(region())); - assert!(b.can_write(region())); - - // mutable borrow disallows shared borrows - let h1 = b.mut_borrow(region()).unwrap(); - assert!(b.shared_borrow(region()).is_err()); - - // active mutable borrows disallows reads/writes - assert!(!b.can_read(region())); - assert!(!b.can_write(region())); - - // releasing the mutable borrows allows reading/writing again - b.mut_unborrow(h1); - assert!(b.can_read(region())); - assert!(b.can_write(region())); - } -} diff --git a/crates/wiggle/src/guest_type.rs b/crates/wiggle/src/guest_type.rs index b0a7e81e8c31..45bf01ec800c 100644 --- a/crates/wiggle/src/guest_type.rs +++ b/crates/wiggle/src/guest_type.rs @@ -1,4 +1,5 @@ -use crate::{GuestError, GuestPtr}; +use crate::{GuestError, GuestMemory, GuestPtr}; +use std::cell::UnsafeCell; use std::mem; use std::sync::atomic::{ AtomicI16, AtomicI32, AtomicI64, AtomicI8, AtomicU16, AtomicU32, AtomicU64, AtomicU8, Ordering, @@ -21,7 +22,7 @@ pub trait GuestErrorType { /// abstraction allows the guest representation of a type to be different from /// the host representation of a type, if necessary. It also allows for /// validation when reading/writing. -pub trait GuestType<'a>: Sized { +pub trait GuestType: Sized { /// Returns the size, in bytes, of this type in the guest memory. fn guest_size() -> u32; @@ -37,14 +38,14 @@ pub trait GuestType<'a>: Sized { /// Typically if you're implementing this by hand you'll want to delegate to /// other safe implementations of this trait (e.g. for primitive types like /// `u32`) rather than writing lots of raw code yourself. - fn read(ptr: &GuestPtr<'a, Self>) -> Result; + fn read(mem: &GuestMemory, ptr: GuestPtr) -> Result; /// Writes a value to `ptr` after verifying that `ptr` is indeed valid to /// store `val`. /// /// Similar to `read`, you'll probably want to implement this in terms of /// other primitives. - fn write(ptr: &GuestPtr<'_, Self>, val: Self) -> Result<(), GuestError>; + fn write(mem: &mut GuestMemory, ptr: GuestPtr, val: Self) -> Result<(), GuestError>; } /// A trait for `GuestType`s that have the same representation in guest memory @@ -56,43 +57,35 @@ pub trait GuestType<'a>: Sized { /// representation on the host matches the guest and all bit patterns are /// valid. This trait should only ever be implemented by /// wiggle_generate-produced code. -pub unsafe trait GuestTypeTransparent<'a>: GuestType<'a> {} +pub unsafe trait GuestTypeTransparent: GuestType {} macro_rules! integer_primitives { ($([$ty:ident, $ty_atomic:ident],)*) => ($( - impl<'a> GuestType<'a> for $ty { + impl GuestType for $ty { #[inline] fn guest_size() -> u32 { mem::size_of::() as u32 } #[inline] fn guest_align() -> usize { mem::align_of::() } #[inline] - fn read(ptr: &GuestPtr<'a, Self>) -> Result { + fn read(mem: &GuestMemory, ptr: GuestPtr) -> Result { // Use `validate_size_align` to validate offset and alignment // internally. The `host_ptr` type will be `&UnsafeCell` // indicating that the memory is valid, and next safety checks // are required to access it. let offset = ptr.offset(); - let (host_ptr, region) = super::validate_size_align::(ptr.mem(), offset, 1)?; - let host_ptr = &host_ptr[0]; - - // If this memory is mutable borrowed then it cannot be read - // here, so skip this operation. - // - // Note that shared memories don't allow borrows and other - // shared borrows are ok to overlap with this. - if !ptr.mem().can_read(region) { - return Err(GuestError::PtrBorrowed(region)); - } + let host_ptr = mem.validate_size_align::(offset, 1)?; // If the accessed memory is shared, we need to load the bytes // with the correct memory consistency. We could check if the // memory is shared each time, but we expect little performance // difference between an additional branch and a relaxed memory // access and thus always do the relaxed access here. - let atomic_value_ref: &$ty_atomic = - unsafe { &*(host_ptr.get().cast::<$ty_atomic>()) }; - let val = atomic_value_ref.load(Ordering::Relaxed); + let host_ptr: &$ty_atomic = unsafe { + let host_ptr: &UnsafeCell = &host_ptr[0]; + &*((host_ptr as *const UnsafeCell).cast::<$ty_atomic>()) + }; + let val = host_ptr.load(Ordering::Relaxed); // And as a final operation convert from the little-endian wasm // value to a native-endian value for the host. @@ -100,15 +93,12 @@ macro_rules! integer_primitives { } #[inline] - fn write(ptr: &GuestPtr<'_, Self>, val: Self) -> Result<(), GuestError> { + fn write(mem: &mut GuestMemory, ptr: GuestPtr, val: Self) -> Result<(), GuestError> { // See `read` above for various checks here. let val = val.to_le(); let offset = ptr.offset(); - let (host_ptr, region) = super::validate_size_align::(ptr.mem(), offset, 1)?; + let host_ptr = mem.validate_size_align::(offset, 1)?; let host_ptr = &host_ptr[0]; - if !ptr.mem().can_write(region) { - return Err(GuestError::PtrBorrowed(region)); - } let atomic_value_ref: &$ty_atomic = unsafe { &*(host_ptr.get().cast::<$ty_atomic>()) }; atomic_value_ref.store(val, Ordering::Relaxed); @@ -116,60 +106,31 @@ macro_rules! integer_primitives { } } - unsafe impl<'a> GuestTypeTransparent<'a> for $ty {} + unsafe impl GuestTypeTransparent for $ty {} )*) } macro_rules! float_primitives { ($([$ty:ident, $ty_unsigned:ident, $ty_atomic:ident],)*) => ($( - impl<'a> GuestType<'a> for $ty { + impl GuestType for $ty { #[inline] fn guest_size() -> u32 { mem::size_of::() as u32 } #[inline] fn guest_align() -> usize { mem::align_of::() } #[inline] - fn read(ptr: &GuestPtr<'a, Self>) -> Result { - // For more commentary see `read` for integers - let offset = ptr.offset(); - let (host_ptr, region) = super::validate_size_align::<$ty_unsigned>( - ptr.mem(), - offset, - 1, - )?; - let host_ptr = &host_ptr[0]; - if !ptr.mem().can_read(region) { - return Err(GuestError::PtrBorrowed(region)); - } - let atomic_value_ref: &$ty_atomic = - unsafe { &*(host_ptr.get().cast::<$ty_atomic>()) }; - let value = $ty_unsigned::from_le(atomic_value_ref.load(Ordering::Relaxed)); - Ok($ty::from_bits(value)) + fn read(mem: &GuestMemory, ptr: GuestPtr) -> Result { + <$ty_unsigned as GuestType>::read(mem, ptr.cast()).map($ty::from_bits) } #[inline] - fn write(ptr: &GuestPtr<'_, Self>, val: Self) -> Result<(), GuestError> { - // For more commentary see `read`/`write` for integers. - let offset = ptr.offset(); - let (host_ptr, region) = super::validate_size_align::<$ty_unsigned>( - ptr.mem(), - offset, - 1, - )?; - let host_ptr = &host_ptr[0]; - if !ptr.mem().can_write(region) { - return Err(GuestError::PtrBorrowed(region)); - } - let atomic_value_ref: &$ty_atomic = - unsafe { &*(host_ptr.get().cast::<$ty_atomic>()) }; - let le_value = $ty_unsigned::to_le(val.to_bits()); - atomic_value_ref.store(le_value, Ordering::Relaxed); - Ok(()) + fn write(mem:&mut GuestMemory, ptr: GuestPtr, val: Self) -> Result<(), GuestError> { + <$ty_unsigned as GuestType>::write(mem, ptr.cast(), val.to_bits()) } } - unsafe impl<'a> GuestTypeTransparent<'a> for $ty {} + unsafe impl GuestTypeTransparent for $ty {} )*) } @@ -186,7 +147,7 @@ float_primitives! { } // Support pointers-to-pointers where pointers are always 32-bits in wasm land -impl<'a, T> GuestType<'a> for GuestPtr<'a, T> { +impl GuestType for GuestPtr { #[inline] fn guest_size() -> u32 { u32::guest_size() @@ -197,20 +158,20 @@ impl<'a, T> GuestType<'a> for GuestPtr<'a, T> { u32::guest_align() } - fn read(ptr: &GuestPtr<'a, Self>) -> Result { - let offset = ptr.cast::().read()?; - Ok(GuestPtr::new(ptr.mem(), offset)) + fn read(mem: &GuestMemory, ptr: GuestPtr) -> Result { + let offset = u32::read(mem, ptr.cast())?; + Ok(GuestPtr::new(offset)) } - fn write(ptr: &GuestPtr<'_, Self>, val: Self) -> Result<(), GuestError> { - ptr.cast::().write(val.offset()) + fn write(mem: &mut GuestMemory, ptr: GuestPtr, val: Self) -> Result<(), GuestError> { + u32::write(mem, ptr.cast(), val.offset()) } } // Support pointers-to-arrays where pointers are always 32-bits in wasm land -impl<'a, T> GuestType<'a> for GuestPtr<'a, [T]> +impl GuestType for GuestPtr<[T]> where - T: GuestType<'a>, + T: GuestType, { #[inline] fn guest_size() -> u32 { @@ -222,16 +183,18 @@ where u32::guest_align() } - fn read(ptr: &GuestPtr<'a, Self>) -> Result { - let offset = ptr.cast::().read()?; - let len = ptr.cast::().add(1)?.read()?; - Ok(GuestPtr::new(ptr.mem(), offset).as_array(len)) + fn read(mem: &GuestMemory, ptr: GuestPtr) -> Result { + let ptr = ptr.cast::(); + let offset = u32::read(mem, ptr)?; + let len = u32::read(mem, ptr.add(1)?)?; + Ok(GuestPtr::new(offset).as_array(len)) } - fn write(ptr: &GuestPtr<'_, Self>, val: Self) -> Result<(), GuestError> { - let (offs, len) = val.offset(); - let len_ptr = ptr.cast::().add(1)?; - ptr.cast::().write(offs)?; - len_ptr.write(len) + fn write(mem: &mut GuestMemory, ptr: GuestPtr, val: Self) -> Result<(), GuestError> { + let (offset, len) = val.offset(); + let ptr = ptr.cast::(); + u32::write(mem, ptr, offset)?; + u32::write(mem, ptr.add(1)?, len)?; + Ok(()) } } diff --git a/crates/wiggle/src/lib.rs b/crates/wiggle/src/lib.rs index acdd99feb338..fc20cc272f3e 100644 --- a/crates/wiggle/src/lib.rs +++ b/crates/wiggle/src/lib.rs @@ -1,10 +1,10 @@ use anyhow::{bail, Result}; +use std::borrow::Cow; use std::cell::UnsafeCell; use std::fmt; use std::mem; -use std::slice; +use std::ops::Range; use std::str; -use std::sync::Arc; pub use wiggle_macro::{async_trait, from_witx}; @@ -16,7 +16,6 @@ pub use bitflags; #[cfg(feature = "wiggle_metadata")] pub use witx; -pub mod borrow; mod error; mod guest_type; mod region; @@ -31,271 +30,283 @@ pub mod async_trait_crate { pub use async_trait::*; } -pub mod wasmtime; #[cfg(feature = "wasmtime")] pub mod wasmtime_crate { pub use wasmtime::*; } -/// A trait which abstracts how to get at the region of host memory that -/// contains guest memory. +/// Representation of guest memory for `wiggle`-generated trait methods. /// -/// All `GuestPtr` types will contain a handle to this trait, signifying where -/// the pointer is actually pointing into. This type will need to be implemented -/// for the host's memory storage object. +/// Guest memory is represented as an array of bytes. Memories are either +/// "unshared" or "shared". Unshared means that the host has exclusive access to +/// the entire array of memory. This allows safe borrows into wasm linear +/// memory. Shared memories can be modified at any time and are represented as +/// an array of `UnsafeCell`. /// -/// # Safety -/// -/// Safety around this type is tricky, and the trait is `unsafe` since there are -/// a few contracts you need to uphold to implement this type correctly and have -/// everything else in this crate work out safely. -/// -/// The most important method of this trait is the `base` method. This returns, -/// in host memory, a pointer and a length. The pointer should point to valid -/// memory for the guest to read/write for the length contiguous bytes -/// afterwards. -/// -/// The region returned by `base` must not only be valid, however, but it must -/// be valid for "a period of time before the guest is reentered". This isn't -/// exactly well defined but the general idea is that `GuestMemory` is allowed -/// to change under our feet to accommodate instructions like `memory.grow` or -/// other guest modifications. Memory, however, cannot be changed if the guest -/// is not reentered or if no explicitly action is taken to modify the guest -/// memory. -/// -/// This provides the guarantee that host pointers based on the return value of -/// `base` have a dynamic period for which they are valid. This time duration -/// must be "somehow nonzero in length" to allow users of `GuestMemory` and -/// `GuestPtr` to safely read and write interior data. -/// -/// This type also provides methods for run-time borrow checking of references -/// into the memory. The safety of this mechanism depends on there being exactly -/// one associated tracking of borrows for a given WebAssembly memory. There -/// must be no other reads or writes of WebAssembly the memory by either Rust or -/// WebAssembly code while there are any outstanding borrows. -/// -/// # Using References -/// -/// The [`GuestPtr::as_slice`] or [`GuestPtr::as_str`] will return smart -/// pointers [`GuestSlice`] and [`GuestStr`]. These types, which implement -/// [`std::ops::Deref`] and [`std::ops::DerefMut`], provide mutable references -/// into the memory region given by a `GuestMemory`. -/// -/// These smart pointers are dynamically borrow-checked by the borrow checker -/// methods on this trait. While a `GuestSlice` or a `GuestStr` are live, -/// WebAssembly cannot be reentered because the store's borrow is connected to -/// the relevant `'a` lifetime on the guest pointer. -pub unsafe trait GuestMemory: Send + Sync { - /// Returns the base allocation of this guest memory, located in host - /// memory. +/// This is generated by the `wiggle` bindings macros. +pub enum GuestMemory<'a> { + Unshared(&'a mut [u8]), + Shared(&'a [UnsafeCell]), +} + +// manual impls are needed because of the `UnsafeCell` in the `Shared` branch +// but this otherwise upholds send/sync invariants. +unsafe impl Send for GuestMemory<'_> {} +unsafe impl Sync for GuestMemory<'_> {} + +impl<'a> GuestMemory<'a> { + /// Read a value from the provided pointer. /// - /// A pointer/length pair are returned to signify where the guest memory - /// lives in the host, and how many contiguous bytes the memory is valid for - /// after the returned pointer. + /// This method will delegate to `T`'s implementation of `read` which will + /// read a value from the `ptr` provided. /// - /// Note that there are safety guarantees about this method that - /// implementations must uphold, and for more details see the - /// [`GuestMemory`] documentation. - fn base(&self) -> &[UnsafeCell]; - - /// Convenience method for creating a `GuestPtr` at a particular offset. + /// # Errors /// - /// Note that `T` can be almost any type, and typically `offset` is a `u32`. - /// The exception is slices and strings, in which case `offset` is a `(u32, - /// u32)` of `(offset, length)`. - fn ptr<'a, T>(&'a self, offset: T::Pointer) -> GuestPtr<'a, T> + /// An error is returned if `ptr` is out of bounds, misaligned, or otherwise + /// not valid to read from. + pub fn read(&self, ptr: GuestPtr) -> Result where - Self: Sized, - T: ?Sized + Pointee, + T: GuestType, { - GuestPtr::new(self, offset) + T::read(self, ptr) } - /// Check if a region of memory can be read. + /// Writes the `val` provided to the `ptr` provided. /// - /// This will only return `true` if there are no active mutable borrows. - fn can_read(&self, r: Region) -> bool; - - /// Check if a region of memory can be written. + /// This commit will write a `val` into a guest's linear memory. This will + /// delegate to `T`'s implementation of `write`. /// - /// This will only return `true` if there are no active borrows. - fn can_write(&self, r: Region) -> bool; - - /// Acquires a mutable borrow on a region of memory. + /// # Errors /// - /// Only succeeds if there are no active shared or mutable borrows and this - /// is not a `shared` WebAssembly memory. - fn mut_borrow(&self, r: Region) -> Result; + /// An error is returned if `ptr` is out of bounds, misaligned, or otherwise + /// not valid to read from. + pub fn write(&mut self, ptr: GuestPtr, val: T) -> Result<(), GuestError> + where + T: GuestType, + { + T::write(self, ptr, val) + } - /// Acquires a shared borrow on a region of memory. + /// Acquires a slice or owned copy of the memory pointed to by `ptr`. /// - /// Only succeeds if there are no active mutable borrows and this is not a - /// `shared` WebAssembly memory. - fn shared_borrow(&self, r: Region) -> Result; + /// This method will attempt to borrow `ptr` directly from linear memory. If + /// memory is shared and cannot be borrowed directy then an owned copy is + /// returned instead. + /// + /// # Errors + /// + /// An error is returned if `ptr` is out of bounds, misaligned, or otherwise + /// not valid to read from. + pub fn as_cow(&self, ptr: GuestPtr<[u8]>) -> Result, GuestError> { + match self { + GuestMemory::Unshared(_) => match self.as_slice(ptr)? { + Some(slice) => Ok(Cow::Borrowed(slice)), + None => unreachable!(), + }, + GuestMemory::Shared(_) => Ok(Cow::Owned(self.to_vec(ptr)?)), + } + } - /// Undoes a borrow by `mut_borrow`. - fn mut_unborrow(&self, h: BorrowHandle); + /// Same as [`GuestMemory::as_cow`] but for strings. + /// + /// # Errors + /// + /// An error is returned if `ptr` is out of bounds, misaligned, or otherwise + /// not valid to read from. + pub fn as_cow_str(&self, ptr: GuestPtr) -> Result, GuestError> { + match self.as_cow(ptr.cast::<[u8]>())? { + Cow::Owned(bytes) => Ok(Cow::Owned( + String::from_utf8(bytes).map_err(|e| e.utf8_error())?, + )), + Cow::Borrowed(bytes) => Ok(Cow::Borrowed(std::str::from_utf8(bytes)?)), + } + } - /// Undoes a borrow by `shared_borrow`. - fn shared_unborrow(&self, h: BorrowHandle); + /// Attempts to borrow a raw guest slice of memory pointed to by `ptr`. + /// + /// This method will attempt to return a raw pointer into guest memory. This + /// can only be done for `Unshared` memories. A `Shared` memory will return + /// `Ok(None)` here. + /// + /// # Errors + /// + /// An error is returned if `ptr` is out of bounds, misaligned, or otherwise + /// not valid to read from. + pub fn as_slice(&self, ptr: GuestPtr<[u8]>) -> Result, GuestError> { + let range = self.validate_range::(ptr.pointer.0, ptr.pointer.1)?; + match self { + GuestMemory::Unshared(slice) => Ok(Some(&slice[range])), + GuestMemory::Shared(_) => Ok(None), + } + } - /// Check if the underlying memory is shared across multiple threads; e.g., - /// with a WebAssembly shared memory. - fn is_shared_memory(&self) -> bool { - false + /// Same as [`GuestMemory::as_slice`] but for strings. + pub fn as_str(&self, ptr: GuestPtr) -> Result, GuestError> { + match self.as_slice(ptr.cast())? { + Some(bytes) => Ok(Some(std::str::from_utf8(bytes)?)), + None => Ok(None), + } } -} -/// Validates a guest-relative pointer given various attributes, and returns -/// the corresponding host pointer. -/// -/// * `mem` - this is the guest memory being accessed. -/// * `offset` - this is the guest-relative pointer, an offset from the -/// base. -/// * `len` - this is the number of length, in units of `T`, to return -/// in the resulting slice. -/// -/// If the parameters are valid then this function will return a slice into -/// `mem` for units of `T`, assuming everything is in-bounds and properly -/// aligned. Additionally the byte-based `Region` is returned, used for borrows -/// later on. -fn validate_size_align<'a, T: GuestTypeTransparent<'a>>( - mem: &'a dyn GuestMemory, - offset: u32, - len: u32, -) -> Result<(&[UnsafeCell], Region), GuestError> { - let base = mem.base(); - let byte_len = len - .checked_mul(T::guest_size()) - .ok_or(GuestError::PtrOverflow)?; - let region = Region { - start: offset, - len: byte_len, - }; - let offset = usize::try_from(offset)?; - let byte_len = usize::try_from(byte_len)?; + /// Attempts return `ptr` as a raw slice of mutable bytes in wasm linear + /// memory. + /// + /// Like [`GuestMemory::as_slice`] this only works for `Unshared` memories + /// and will not work for `Shared` memories. + pub fn as_slice_mut(&mut self, ptr: GuestPtr<[u8]>) -> Result, GuestError> { + let range = self.validate_range::(ptr.pointer.0, ptr.pointer.1)?; + match self { + GuestMemory::Unshared(slice) => Ok(Some(&mut slice[range])), + GuestMemory::Shared(_) => Ok(None), + } + } - // Slice the input region to the byte range that we're interested in. - let bytes = base - .get(offset..) - .and_then(|s| s.get(..byte_len)) - .ok_or(GuestError::PtrOutOfBounds(region))?; + /// Copies the data in the guest region into a [`Vec`]. + /// + /// This is useful when one cannot use [`GuestPtr::as_slice`], e.g., when + /// pointing to a region of WebAssembly shared memory. + pub fn to_vec(&self, ptr: GuestPtr<[T]>) -> Result, GuestError> + where + T: GuestTypeTransparent + Copy, + { + let guest = self.validate_size_align::(ptr.pointer.0, ptr.pointer.1)?; + let mut host = Vec::with_capacity(guest.len()); - // ... and then align it to `T`, failing if either the head or tail slices - // are nonzero in length. This `unsafe` here is from the standard library - // and should be ok since the input slice is `UnsafeCell` and the output - // slice is `UnsafeCell`, meaning the only guarantee of the output is - // that it's valid addressable memory, still unsafe to actually access. - assert!(mem::align_of::() <= T::guest_align()); - let (start, mid, end) = unsafe { bytes.align_to() }; - if start.len() > 0 || end.len() > 0 { - return Err(GuestError::PtrNotAligned(region, T::guest_align() as u32)); + // SAFETY: The `guest_slice` variable is already a valid pointer into + // the guest's memory, and it may or may not be a pointer into shared + // memory. We can't naively use `.to_vec(..)` which could introduce data + // races but all that needs to happen is to copy data into our local + // `vec` as all the data is `Copy` and transparent anyway. For this + // purpose the `ptr::copy` function should be sufficient for copying + // over all the data. + // + // TODO: audit that this use of `std::ptr::copy` is safe with shared + // memory (https://github.com/bytecodealliance/wasmtime/issues/4203) + unsafe { + std::ptr::copy(guest.as_ptr().cast(), host.as_mut_ptr(), guest.len()); + host.set_len(guest.len()); + } + Ok(host) } - Ok((mid, region)) -} -/// A handle to a borrow on linear memory. It is produced by `{mut, shared}_borrow` and -/// consumed by `{mut, shared}_unborrow`. Only the `GuestMemory` impl should ever construct -/// a `BorrowHandle` or inspect its contents. -#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] -pub struct BorrowHandle { - _priv: (), -} + /// Copies the data pointed to by `slice` into this guest region. + /// + /// This method is a *safe* method to copy data from the host to the guest. + /// This requires that `self` and `slice` have the same length. The pointee + /// type `T` requires the [`GuestTypeTransparent`] trait which is an + /// assertion that the representation on the host and on the guest is the + /// same. + /// + /// # Errors + /// + /// Returns an error if this guest pointer is out of bounds or if the length + /// of this guest pointer is not equal to the length of the slice provided. + pub fn copy_from_slice(&mut self, slice: &[T], ptr: GuestPtr<[T]>) -> Result<(), GuestError> + where + T: GuestTypeTransparent + Copy, + { + if usize::try_from(ptr.len())? != slice.len() { + return Err(GuestError::SliceLengthsDiffer); + } + if slice.is_empty() { + return Ok(()); + } -// Forwarding trait implementations to the original type -unsafe impl<'a, T: ?Sized + GuestMemory> GuestMemory for &'a T { - fn base(&self) -> &[UnsafeCell] { - T::base(self) - } - fn can_read(&self, r: Region) -> bool { - T::can_read(self, r) - } - fn can_write(&self, r: Region) -> bool { - T::can_write(self, r) - } - fn mut_borrow(&self, r: Region) -> Result { - T::mut_borrow(self, r) - } - fn shared_borrow(&self, r: Region) -> Result { - T::shared_borrow(self, r) - } - fn mut_unborrow(&self, h: BorrowHandle) { - T::mut_unborrow(self, h) - } - fn shared_unborrow(&self, h: BorrowHandle) { - T::shared_unborrow(self, h) - } -} + let guest = self.validate_size_align::(ptr.pointer.0, ptr.pointer.1)?; -unsafe impl<'a, T: ?Sized + GuestMemory> GuestMemory for &'a mut T { - fn base(&self) -> &[UnsafeCell] { - T::base(self) - } - fn can_read(&self, r: Region) -> bool { - T::can_read(self, r) - } - fn can_write(&self, r: Region) -> bool { - T::can_write(self, r) - } - fn mut_borrow(&self, r: Region) -> Result { - T::mut_borrow(self, r) - } - fn shared_borrow(&self, r: Region) -> Result { - T::shared_borrow(self, r) - } - fn mut_unborrow(&self, h: BorrowHandle) { - T::mut_unborrow(self, h) - } - fn shared_unborrow(&self, h: BorrowHandle) { - T::shared_unborrow(self, h) + // SAFETY: in the shared memory case, we copy and accept that + // the guest data may be concurrently modified. TODO: audit that + // this use of `std::ptr::copy` is safe with shared memory + // (https://github.com/bytecodealliance/wasmtime/issues/4203) + // + // Also note that the validity of `guest_slice` has already been + // determined by the `as_unsafe_slice_mut` call above. + assert_eq!(guest.len(), slice.len()); + unsafe { + let guest: &[UnsafeCell] = guest; + let guest: *const UnsafeCell = guest.as_ptr(); + let guest = guest.cast_mut().cast::(); + std::ptr::copy(slice.as_ptr(), guest, slice.len()); + } + Ok(()) } -} -unsafe impl GuestMemory for Box { - fn base(&self) -> &[UnsafeCell] { - T::base(self) - } - fn can_read(&self, r: Region) -> bool { - T::can_read(self, r) - } - fn can_write(&self, r: Region) -> bool { - T::can_write(self, r) - } - fn mut_borrow(&self, r: Region) -> Result { - T::mut_borrow(self, r) - } - fn shared_borrow(&self, r: Region) -> Result { - T::shared_borrow(self, r) - } - fn mut_unborrow(&self, h: BorrowHandle) { - T::mut_unborrow(self, h) - } - fn shared_unborrow(&self, h: BorrowHandle) { - T::shared_unborrow(self, h) + /// Validates a guest-relative pointer given various attributes, and returns + /// the corresponding host pointer. + /// + /// * `mem` - this is the guest memory being accessed. + /// * `offset` - this is the guest-relative pointer, an offset from the + /// base. + /// * `len` - this is the number of length, in units of `T`, to return + /// in the resulting slice. + /// + /// If the parameters are valid then this function will return a slice into + /// `mem` for units of `T`, assuming everything is in-bounds and properly + /// aligned. Additionally the byte-based `Region` is returned, used for borrows + /// later on. + fn validate_size_align(&self, offset: u32, len: u32) -> Result<&[UnsafeCell], GuestError> + where + T: GuestTypeTransparent, + { + let range = self.validate_range::(offset, len)?; + let cells = match self { + GuestMemory::Unshared(s) => { + let s: &[u8] = s; + unsafe { &*(s as *const [u8] as *const [UnsafeCell]) } + } + GuestMemory::Shared(s) => s, + }; + let memory = &cells[range.clone()]; + + // ... and then align it to `T`, failing if either the head or tail slices + // are nonzero in length. This `unsafe` here is from the standard library + // and should be ok since the input slice is `UnsafeCell` and the output + // slice is `UnsafeCell`, meaning the only guarantee of the output is + // that it's valid addressable memory, still unsafe to actually access. + assert!(mem::align_of::() <= T::guest_align()); + let (start, mid, end) = unsafe { memory.align_to() }; + if start.len() > 0 || end.len() > 0 { + let region = Region { + start: range.start as u32, + len: range.len() as u32, + }; + return Err(GuestError::PtrNotAligned(region, T::guest_align() as u32)); + } + Ok(mid) } -} -unsafe impl GuestMemory for Arc { - fn base(&self) -> &[UnsafeCell] { - T::base(self) - } - fn can_read(&self, r: Region) -> bool { - T::can_read(self, r) - } - fn can_write(&self, r: Region) -> bool { - T::can_write(self, r) - } - fn mut_borrow(&self, r: Region) -> Result { - T::mut_borrow(self, r) - } - fn shared_borrow(&self, r: Region) -> Result { - T::shared_borrow(self, r) - } - fn mut_unborrow(&self, h: BorrowHandle) { - T::mut_unborrow(self, h) + fn validate_range(&self, offset: u32, len: u32) -> Result, GuestError> + where + T: GuestTypeTransparent, + { + let byte_len = len + .checked_mul(T::guest_size()) + .ok_or(GuestError::PtrOverflow)?; + let region = Region { + start: offset, + len: byte_len, + }; + let offset = usize::try_from(offset)?; + let byte_len = usize::try_from(byte_len)?; + + let range = offset..offset + byte_len; + let oob = match self { + GuestMemory::Unshared(b) => b.get(range.clone()).is_none(), + GuestMemory::Shared(b) => b.get(range.clone()).is_none(), + }; + if oob { + Err(GuestError::PtrOutOfBounds(region)) + } else { + Ok(range) + } } - fn shared_unborrow(&self, h: BorrowHandle) { - T::shared_unborrow(self, h) + + /// Returns whether this is a shared memory or not. + pub fn is_shared_memory(&self) -> bool { + match self { + GuestMemory::Shared(_) => true, + GuestMemory::Unshared(_) => false, + } } } @@ -349,19 +360,19 @@ unsafe impl GuestMemory for Arc { /// methods. In general though be extremely careful about writing `unsafe` code /// when working with a `GuestPtr` if you're not using one of the /// already-attached helper methods. -pub struct GuestPtr<'a, T: ?Sized + Pointee> { - mem: &'a (dyn GuestMemory + 'a), +#[repr(transparent)] +pub struct GuestPtr { pointer: T::Pointer, } -impl<'a, T: ?Sized + Pointee> GuestPtr<'a, T> { +impl GuestPtr { /// Creates a new `GuestPtr` from the given `mem` and `pointer` values. /// /// Note that for sized types like `u32`, `GuestPtr`, etc, the `pointer` /// value is a `u32` offset into guest memory. For slices and strings, /// `pointer` is a `(u32, u32)` offset/length pair. - pub fn new(mem: &'a (dyn GuestMemory + 'a), pointer: T::Pointer) -> GuestPtr<'a, T> { - GuestPtr { mem, pointer } + pub fn new(pointer: T::Pointer) -> GuestPtr { + GuestPtr { pointer } } /// Returns the offset of this pointer in guest memory. @@ -372,69 +383,17 @@ impl<'a, T: ?Sized + Pointee> GuestPtr<'a, T> { self.pointer } - /// Returns the guest memory that this pointer is coming from. - pub fn mem(&self) -> &'a (dyn GuestMemory + 'a) { - self.mem - } - /// Casts this `GuestPtr` type to a different type. /// /// This is a safe method which is useful for simply reinterpreting the type /// parameter on this `GuestPtr`. Note that this is a safe method, where /// again there's no guarantees about alignment, validity, in-bounds-ness, /// etc of the returned pointer. - pub fn cast(&self) -> GuestPtr<'a, U> + pub fn cast(&self) -> GuestPtr where U: Pointee + ?Sized, { - GuestPtr::new(self.mem, self.pointer) - } - - /// Safely read a value from this pointer. - /// - /// This is a fun method, and is one of the lynchpins of this - /// implementation. The highlight here is that this is a *safe* operation, - /// not an unsafe one like `*mut T`. This works for a few reasons: - /// - /// * The `unsafe` contract of the `GuestMemory` trait means that there's - /// always at least some backing memory for this `GuestPtr`. - /// - /// * This does not use Rust-intrinsics to read the type `T`, but rather it - /// delegates to `T`'s implementation of [`GuestType`] to actually read - /// the underlying data. This again is a safe method, so any unsafety, if - /// any, must be internally documented. - /// - /// * Eventually what typically happens it that this bottoms out in the read - /// implementations for primitives types (like `i32`) which can safely be - /// read at any time, and then it's up to the runtime to determine what to - /// do with the bytes it read in a safe manner. - /// - /// Naturally lots of things can still go wrong, such as out-of-bounds - /// checks, alignment checks, validity checks (e.g. for enums), etc. All of - /// these check failures, however, are returned as a [`GuestError`] in the - /// `Result` here, and `Ok` is only returned if all the checks passed. - pub fn read(&self) -> Result - where - T: GuestType<'a>, - { - T::read(self) - } - - /// Safely write a value to this pointer. - /// - /// This method, like [`GuestPtr::read`], is pretty crucial for the safe - /// operation of this crate. All the same reasons apply though for why this - /// method is safe, even eventually bottoming out in primitives like writing - /// an `i32` which is safe to write bit patterns into memory at any time due - /// to the guarantees of [`GuestMemory`]. - /// - /// Like `read`, `write` can fail due to any manner of pointer checks, but - /// any failure is returned as a [`GuestError`]. - pub fn write(&self, val: T) -> Result<(), GuestError> - where - T: GuestType<'a>, - { - T::write(self, val) + GuestPtr::new(self.pointer) } /// Performs pointer arithmetic on this pointer, moving the pointer forward @@ -443,9 +402,9 @@ impl<'a, T: ?Sized + Pointee> GuestPtr<'a, T> { /// This will either return the resulting pointer or `Err` if the pointer /// arithmetic calculation would overflow around the end of the address /// space. - pub fn add(&self, amt: u32) -> Result, GuestError> + pub fn add(&self, amt: u32) -> Result, GuestError> where - T: GuestType<'a> + Pointee, + T: GuestType + Pointee, { let offset = amt .checked_mul(T::guest_size()) @@ -454,25 +413,20 @@ impl<'a, T: ?Sized + Pointee> GuestPtr<'a, T> { Some(o) => o, None => return Err(GuestError::PtrOverflow), }; - Ok(GuestPtr::new(self.mem, offset)) + Ok(GuestPtr::new(offset)) } /// Returns a `GuestPtr` for an array of `T`s using this pointer as the /// base. - pub fn as_array(&self, elems: u32) -> GuestPtr<'a, [T]> + pub fn as_array(&self, elems: u32) -> GuestPtr<[T]> where - T: GuestType<'a> + Pointee, + T: GuestType + Pointee, { - GuestPtr::new(self.mem, (self.pointer, elems)) - } - - /// Check if this pointer references WebAssembly shared memory. - pub fn is_shared_memory(&self) -> bool { - self.mem.is_shared_memory() + GuestPtr::new((self.pointer, elems)) } } -impl<'a, T> GuestPtr<'a, [T]> { +impl GuestPtr<[T]> { /// For slices, specifically returns the relative pointer to the base of the /// array. /// @@ -490,160 +444,23 @@ impl<'a, T> GuestPtr<'a, [T]> { /// /// Each item is a `Result` indicating whether it overflowed past the end of /// the address space or not. - pub fn iter<'b>( - &'b self, - ) -> impl ExactSizeIterator, GuestError>> + 'b + pub fn iter(&self) -> impl ExactSizeIterator, GuestError>> + '_ where - T: GuestType<'a>, + T: GuestType, { let base = self.as_ptr(); (0..self.len()).map(move |i| base.add(i)) } - /// Attempts to create a [`GuestCow<'_, T>`] from this pointer, performing - /// bounds checks and type validation. Whereas [`GuestPtr::as_slice`] will - /// fail with `None` if attempting to access Wasm shared memory, this call - /// will succeed: if used on shared memory, this function will copy the - /// slice into [`GuestCow::Copied`]. If the memory is non-shared, this - /// returns a [`GuestCow::Borrowed`] (a thin wrapper over [`GuestSlice<'_, - /// T>]`). - pub fn as_cow(&self) -> Result, GuestError> - where - T: GuestTypeTransparent<'a> + Copy + 'a, - { - match self.as_unsafe_slice_mut()?.shared_borrow() { - UnsafeBorrowResult::Ok(slice) => Ok(GuestCow::Borrowed(slice)), - UnsafeBorrowResult::Shared(_) => Ok(GuestCow::Copied(self.to_vec()?)), - UnsafeBorrowResult::Err(e) => Err(e), - } - } - - /// Attempts to create a [`GuestSlice<'_, T>`] from this pointer, performing - /// bounds checks and type validation. The `GuestSlice` is a smart pointer - /// that can be used as a `&[T]` via the `Deref` trait. - /// - /// This method will flag the entire linear memory as marked with a shared - /// borrow. This means that any writes to memory are disallowed until - /// the returned `GuestSlice` is dropped. - /// - /// This function will return a `GuestSlice` into host memory if all checks - /// succeed (valid utf-8, valid pointers, memory is not borrowed, etc.). If - /// any checks fail then `GuestError` will be returned. - /// - /// Additionally, because it is `unsafe` to have a `GuestSlice` of shared - /// memory, this function will return `None` in this case (see - /// [`GuestPtr::as_cow`]). - pub fn as_slice(&self) -> Result>, GuestError> - where - T: GuestTypeTransparent<'a>, - { - match self.as_unsafe_slice_mut()?.shared_borrow() { - UnsafeBorrowResult::Ok(slice) => Ok(Some(slice)), - UnsafeBorrowResult::Shared(_) => Ok(None), - UnsafeBorrowResult::Err(e) => Err(e), - } - } - - /// Attempts to create a [`GuestSliceMut<'_, T>`] from this pointer, - /// performing bounds checks and type validation. The `GuestSliceMut` is a - /// smart pointer that can be used as a `&[T]` or a `&mut [T]` via the - /// `Deref` and `DerefMut` traits. - /// - /// This method will flag the entire linear memory as marked with a mutable - /// borrow. This means that all reads/writes to memory are disallowed until - /// the returned `GuestSliceMut` type is dropped. - /// - /// This function will return a `GuestSliceMut` into host memory if all - /// checks succeed (valid utf-8, valid pointers, memory is not borrowed, - /// etc). If any checks fail then `GuestError` will be returned. - /// - /// Additionally, because it is `unsafe` to have a `GuestSliceMut` of shared - /// memory, this function will return `None` in this case. - pub fn as_slice_mut(&self) -> Result>, GuestError> - where - T: GuestTypeTransparent<'a>, - { - self.as_unsafe_slice_mut()?.as_slice_mut() - } - - /// Similar to `as_slice_mut`, this function will attempt to create a smart - /// pointer to the WebAssembly linear memory. All validation and Wiggle - /// borrow checking is the same, but unlike `as_slice_mut`, the returned - /// `&mut` slice can point to WebAssembly shared memory. Though the Wiggle - /// borrow checker can guarantee no other Wiggle calls will access this - /// slice, it cannot guarantee that another thread is not modifying the - /// `&mut` slice in some other way. Thus, access to that slice is marked - /// `unsafe`. - pub fn as_unsafe_slice_mut(&self) -> Result, GuestError> - where - T: GuestTypeTransparent<'a>, - { - let (ptr, region) = validate_size_align(self.mem, self.pointer.0, self.pointer.1)?; - - Ok(UnsafeGuestSlice { - ptr, - region, - mem: self.mem, - }) - } - - /// Copies the data in the guest region into a [`Vec`]. - /// - /// This is useful when one cannot use [`GuestPtr::as_slice`], e.g., when - /// pointing to a region of WebAssembly shared memory. - pub fn to_vec(&self) -> Result, GuestError> - where - T: GuestTypeTransparent<'a> + Copy + 'a, - { - let guest_slice = self.as_unsafe_slice_mut()?; - let len = guest_slice.ptr.len(); - let mut vec = Vec::with_capacity(len); - - // SAFETY: The `guest_slice` variable is already a valid pointer into - // the guest's memory, and it may or may not be a pointer into shared - // memory. We can't naively use `.to_vec(..)` which could introduce data - // races but all that needs to happen is to copy data into our local - // `vec` as all the data is `Copy` and transparent anyway. For this - // purpose the `ptr::copy` function should be sufficient for copying - // over all the data. - // - // TODO: audit that this use of `std::ptr::copy` is safe with shared - // memory (https://github.com/bytecodealliance/wasmtime/issues/4203) - unsafe { - std::ptr::copy(guest_slice.ptr.as_ptr().cast::(), vec.as_mut_ptr(), len); - vec.set_len(len); - } - Ok(vec) - } - - /// Copies the data pointed to by `slice` into this guest region. - /// - /// This method is a *safe* method to copy data from the host to the guest. - /// This requires that `self` and `slice` have the same length. The pointee - /// type `T` requires the [`GuestTypeTransparent`] trait which is an - /// assertion that the representation on the host and on the guest is the - /// same. - /// - /// # Errors - /// - /// Returns an error if this guest pointer is out of bounds or if the length - /// of this guest pointer is not equal to the length of the slice provided. - pub fn copy_from_slice(&self, slice: &[T]) -> Result<(), GuestError> - where - T: GuestTypeTransparent<'a> + Copy + 'a, - { - self.as_unsafe_slice_mut()?.copy_from_slice(slice) - } - /// Returns a `GuestPtr` pointing to the base of the array for the interior /// type `T`. - pub fn as_ptr(&self) -> GuestPtr<'a, T> { - GuestPtr::new(self.mem, self.offset_base()) + pub fn as_ptr(&self) -> GuestPtr { + GuestPtr::new(self.offset_base()) } - pub fn get(&self, index: u32) -> Option> + pub fn get(&self, index: u32) -> Option> where - T: GuestType<'a>, + T: GuestType, { if index < self.len() { Some( @@ -656,9 +473,9 @@ impl<'a, T> GuestPtr<'a, [T]> { } } - pub fn get_range(&self, r: std::ops::Range) -> Option> + pub fn get_range(&self, r: std::ops::Range) -> Option> where - T: GuestType<'a>, + T: GuestType, { if r.end < r.start { return None; @@ -677,7 +494,7 @@ impl<'a, T> GuestPtr<'a, [T]> { } } -impl<'a> GuestPtr<'a, str> { +impl GuestPtr { /// For strings, returns the relative pointer to the base of the string /// allocation. pub fn offset_base(&self) -> u32 { @@ -691,420 +508,28 @@ impl<'a> GuestPtr<'a, str> { /// Returns a raw pointer for the underlying slice of bytes that this /// pointer points to. - pub fn as_bytes(&self) -> GuestPtr<'a, [u8]> { - GuestPtr::new(self.mem, self.pointer) - } - - /// Attempts to create a [`GuestStr<'_>`] from this pointer, performing - /// bounds checks and utf-8 checks. The resulting `GuestStr` can be used as - /// a `&str` via the `Deref` trait. The region of memory backing the `str` - /// will be marked as shareably borrowed by the [`GuestMemory`] until the - /// `GuestStr` is dropped. - /// - /// This function will return `GuestStr` into host memory if all checks - /// succeed (valid utf-8, valid pointers, etc). If any checks fail then - /// `GuestError` will be returned. - /// - /// Additionally, because it is `unsafe` to have a `GuestStr` of shared - /// memory, this function will return `None` in this case (see - /// [`GuestPtr<'_, str>::as_cow`]). - pub fn as_str(&self) -> Result>, GuestError> { - match self.as_bytes().as_unsafe_slice_mut()?.shared_borrow() { - UnsafeBorrowResult::Ok(s) => Ok(Some(s.try_into()?)), - UnsafeBorrowResult::Shared(_) => Ok(None), - UnsafeBorrowResult::Err(e) => Err(e), - } - } - - /// Attempts to create a [`GuestStrMut<'_>`] from this pointer, performing - /// bounds checks and utf-8 checks. The resulting `GuestStrMut` can be used - /// as a `&str` or `&mut str` via the `Deref` and `DerefMut` traits. The - /// region of memory backing the `str` will be marked as borrowed by the - /// [`GuestMemory`] until the `GuestStrMut` is dropped. - /// - /// This function will return `GuestStrMut` into host memory if all checks - /// succeed (valid utf-8, valid pointers, etc). If any checks fail then - /// `GuestError` will be returned. - /// - /// Additionally, because it is `unsafe` to have a `GuestStrMut` of shared - /// memory, this function will return `None` in this case. - pub fn as_str_mut(&self) -> Result>, GuestError> { - match self.as_bytes().as_unsafe_slice_mut()?.mut_borrow() { - UnsafeBorrowResult::Ok(s) => Ok(Some(s.try_into()?)), - UnsafeBorrowResult::Shared(_) => Ok(None), - UnsafeBorrowResult::Err(e) => Err(e), - } - } - - /// Attempts to create a [`GuestStrCow<'_>`] from this pointer, performing - /// bounds checks and utf-8 checks. Whereas [`GuestPtr::as_str`] will fail - /// with `None` if attempting to access Wasm shared memory, this call will - /// succeed: if used on shared memory, this function will copy the string - /// into [`GuestStrCow::Copied`]. If the memory is non-shared, this returns - /// a [`GuestStrCow::Borrowed`] (a thin wrapper over [`GuestStr<'_, T>]`). - pub fn as_cow(&self) -> Result, GuestError> { - match self.as_bytes().as_unsafe_slice_mut()?.shared_borrow() { - UnsafeBorrowResult::Ok(s) => Ok(GuestStrCow::Borrowed(s.try_into()?)), - UnsafeBorrowResult::Shared(_) => { - let copied = self.as_bytes().to_vec()?; - let utf8_string = String::from_utf8(copied).map_err(|e| e.utf8_error())?; - Ok(GuestStrCow::Copied(utf8_string)) - } - UnsafeBorrowResult::Err(e) => Err(e), - } - } -} - -impl<'a> GuestPtr<'a, [u8]> { - /// Returns a pointer to the string represented by a `[u8]` without - /// validating whether each u8 is a utf-8 codepoint. - pub fn as_str_ptr(&self) -> GuestPtr<'a, str> { - GuestPtr::new(self.mem, self.pointer) + pub fn as_bytes(&self) -> GuestPtr<[u8]> { + GuestPtr::new(self.pointer) } } -impl Clone for GuestPtr<'_, T> { +impl Clone for GuestPtr { fn clone(&self) -> Self { *self } } -impl Copy for GuestPtr<'_, T> {} +impl Copy for GuestPtr {} -impl fmt::Debug for GuestPtr<'_, T> { +impl fmt::Debug for GuestPtr { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { T::debug(self.pointer, f) } } -/// A smart pointer to an shareable slice in guest memory. -/// -/// Usable as a `&'a [T]` via [`std::ops::Deref`]. -pub struct GuestSlice<'a, T> { - ptr: &'a [UnsafeCell], - mem: &'a dyn GuestMemory, - borrow: BorrowHandle, -} - -// This is a wrapper around `&[T]` and must mirror send/sync impls due to the -// interior usage of `&[UnsafeCell]`. -unsafe impl Send for GuestSlice<'_, T> {} -unsafe impl Sync for GuestSlice<'_, T> {} - -impl<'a, T> std::ops::Deref for GuestSlice<'a, T> { - type Target = [T]; - - fn deref(&self) -> &Self::Target { - // SAFETY: The presence of `GuestSlice` indicates that this is an - // unshared memory meaning concurrent accesses will not happen. - // Furthermore the validity of the slice has already been established - // and a runtime borrow has been recorded to prevent conflicting views. - // This all adds up to the ability to return a safe slice from this - // method whose lifetime is connected to `self`. - unsafe { slice::from_raw_parts(self.ptr.as_ptr().cast(), self.ptr.len()) } - } -} - -impl<'a, T> Drop for GuestSlice<'a, T> { - fn drop(&mut self) { - self.mem.shared_unborrow(self.borrow) - } -} - -/// A smart pointer to a mutable slice in guest memory. -/// -/// Usable as a `&'a [T]` via [`std::ops::Deref`] and as a `&'a mut [T]` via -/// [`std::ops::DerefMut`]. -pub struct GuestSliceMut<'a, T> { - ptr: &'a [UnsafeCell], - mem: &'a dyn GuestMemory, - borrow: BorrowHandle, -} - -// See docs in these impls for `GuestSlice` above. -unsafe impl Send for GuestSliceMut<'_, T> {} -unsafe impl Sync for GuestSliceMut<'_, T> {} - -impl<'a, T> std::ops::Deref for GuestSliceMut<'a, T> { - type Target = [T]; - fn deref(&self) -> &Self::Target { - // SAFETY: See docs in `Deref for GuestSlice` - unsafe { slice::from_raw_parts(self.ptr.as_ptr().cast(), self.ptr.len()) } - } -} - -impl<'a, T> std::ops::DerefMut for GuestSliceMut<'a, T> { - fn deref_mut(&mut self) -> &mut Self::Target { - // SAFETY: See docs in `Deref for GuestSlice` - unsafe { slice::from_raw_parts_mut(self.ptr.as_ptr() as *mut T, self.ptr.len()) } - } -} - -impl<'a, T> Drop for GuestSliceMut<'a, T> { - fn drop(&mut self) { - self.mem.mut_unborrow(self.borrow) - } -} - -/// A smart pointer for distinguishing between different kinds of Wasm memory: -/// shared and non-shared. -/// -/// As with `GuestSlice`, this is usable as a `&'a [T]` via [`std::ops::Deref`]. -/// The major difference is that, for shared memories, the memory will be copied -/// out of Wasm linear memory to avoid the possibility of concurrent mutation by -/// another thread. This extra copy exists solely to maintain the Rust -/// guarantees regarding `&[T]`. -pub enum GuestCow<'a, T> { - Borrowed(GuestSlice<'a, T>), - Copied(Vec), -} - -impl<'a, T> std::ops::Deref for GuestCow<'a, T> { - type Target = [T]; - - fn deref(&self) -> &Self::Target { - match self { - GuestCow::Borrowed(s) => s, - GuestCow::Copied(s) => s, - } - } -} - -/// A smart pointer to an `unsafe` slice in guest memory. -/// -/// Accessing guest memory (e.g., WebAssembly linear memory) is inherently -/// `unsafe`. Even though this structure expects that we will have validated the -/// addresses, lengths, and alignment, we must be extra careful to maintain the -/// Rust borrowing guarantees if we hand out slices to the underlying memory. -/// This is done in two ways: -/// -/// - with shared memory (i.e., memory that may be accessed concurrently by -/// multiple threads), we have no guarantee that the underlying data will not -/// be changed; thus, we can only hand out slices `unsafe`-ly (TODO: -/// eventually with `UnsafeGuestSlice::as_slice`, -/// `UnsafeGuestSlice::as_slice_mut`) -/// - with non-shared memory, we _can_ maintain the Rust slice guarantees, but -/// only by manually performing borrow-checking of the underlying regions that -/// are accessed; this kind of borrowing is wrapped up in the [`GuestSlice`] -/// and [`GuestSliceMut`] smart pointers (see -/// `UnsafeGuestSlice::shared_borrow`, `UnsafeGuestSlice::mut_borrow`). -pub struct UnsafeGuestSlice<'a, T> { - /// A raw pointer to the bytes in memory. - ptr: &'a [UnsafeCell], - /// The (validated) address bounds of the slice in memory. - region: Region, - /// The original memory. - mem: &'a dyn GuestMemory, -} - -// SAFETY: `UnsafeGuestSlice` can be used across an `await` and therefore must -// be `Send` and `Sync`. As with `GuestSlice` and friends, we mirror the -// `Send`/`Sync` impls due to the interior usage of `&[UnsafeCell]`. -unsafe impl Sync for UnsafeGuestSlice<'_, T> {} -unsafe impl Send for UnsafeGuestSlice<'_, T> {} - -impl<'a, T> UnsafeGuestSlice<'a, T> { - /// See `GuestPtr::copy_from_slice`. - pub fn copy_from_slice(self, slice: &[T]) -> Result<(), GuestError> - where - T: GuestTypeTransparent<'a> + Copy + 'a, - { - // Check the length... - if self.ptr.len() != slice.len() { - return Err(GuestError::SliceLengthsDiffer); - } - if slice.len() == 0 { - return Ok(()); - } - - // ... and copy the bytes. - match self.mut_borrow() { - UnsafeBorrowResult::Ok(mut dst) => dst.copy_from_slice(slice), - UnsafeBorrowResult::Shared(guest_slice) => { - // SAFETY: in the shared memory case, we copy and accept that - // the guest data may be concurrently modified. TODO: audit that - // this use of `std::ptr::copy` is safe with shared memory - // (https://github.com/bytecodealliance/wasmtime/issues/4203) - // - // Also note that the validity of `guest_slice` has already been - // determined by the `as_unsafe_slice_mut` call above. - unsafe { - std::ptr::copy( - slice.as_ptr(), - guest_slice.ptr[0].get(), - guest_slice.ptr.len(), - ) - }; - } - UnsafeBorrowResult::Err(e) => return Err(e), - } - Ok(()) - } - - /// Return the number of items in this slice. - pub fn len(&self) -> usize { - self.ptr.len() - } - - /// Check if this slice comes from WebAssembly shared memory. - pub fn is_shared_memory(&self) -> bool { - self.mem.is_shared_memory() - } - - /// See `GuestPtr::as_slice_mut`. - pub fn as_slice_mut(self) -> Result>, GuestError> - where - T: GuestTypeTransparent<'a>, - { - match self.mut_borrow() { - UnsafeBorrowResult::Ok(slice) => Ok(Some(slice)), - UnsafeBorrowResult::Shared(_) => Ok(None), - UnsafeBorrowResult::Err(e) => Err(e), - } - } - - /// Transform an `unsafe` guest slice to a [`GuestSliceMut`]. - /// - /// # Safety - /// - /// This function is safe if and only if: - /// - the memory is not shared (it will return `None` in this case) and - /// - there are no overlapping mutable borrows for this region. - fn shared_borrow(self) -> UnsafeBorrowResult, Self> { - if self.mem.is_shared_memory() { - UnsafeBorrowResult::Shared(self) - } else { - match self.mem.shared_borrow(self.region) { - Ok(borrow) => UnsafeBorrowResult::Ok(GuestSlice { - ptr: self.ptr, - mem: self.mem, - borrow, - }), - Err(e) => UnsafeBorrowResult::Err(e), - } - } - } - - /// Transform an `unsafe` guest slice to a [`GuestSliceMut`]. - /// - /// # Safety - /// - /// This function is safe if and only if: - /// - the memory is not shared (it will return `None` in this case) and - /// - there are no overlapping borrows of any kind (shared or mutable) for - /// this region. - fn mut_borrow(self) -> UnsafeBorrowResult, Self> { - if self.mem.is_shared_memory() { - UnsafeBorrowResult::Shared(self) - } else { - match self.mem.mut_borrow(self.region) { - Ok(borrow) => UnsafeBorrowResult::Ok(GuestSliceMut { - ptr: self.ptr, - mem: self.mem, - borrow, - }), - Err(e) => UnsafeBorrowResult::Err(e), - } - } - } -} - -/// A three-way result type for expressing that borrowing from an -/// [`UnsafeGuestSlice`] could fail in multiple ways. Retaining the -/// [`UnsafeGuestSlice`] in the `Shared` case allows us to reuse it. -enum UnsafeBorrowResult { - /// The borrow succeeded. - Ok(T), - /// The borrow failed because the underlying memory was shared--we cannot - /// safely borrow in this case and return the original unsafe slice. - Shared(S), - /// The borrow failed for some other reason, e.g., the region was already - /// borrowed. - Err(GuestError), -} - -impl From for UnsafeBorrowResult { - fn from(e: GuestError) -> Self { - UnsafeBorrowResult::Err(e) - } -} - -/// A smart pointer to an shareable `str` in guest memory. -/// Usable as a `&'a str` via [`std::ops::Deref`]. -pub struct GuestStr<'a>(GuestSlice<'a, u8>); - -impl<'a> std::convert::TryFrom> for GuestStr<'a> { - type Error = GuestError; - fn try_from(slice: GuestSlice<'a, u8>) -> Result { - match str::from_utf8(&slice) { - Ok(_) => Ok(Self(slice)), - Err(e) => Err(GuestError::InvalidUtf8(e)), - } - } -} - -impl<'a> std::ops::Deref for GuestStr<'a> { - type Target = str; - fn deref(&self) -> &Self::Target { - // SAFETY: every slice in a `GuestStr` has already been checked for - // UTF-8 validity during construction (i.e., `TryFrom`). - unsafe { str::from_utf8_unchecked(&self.0) } - } -} - -/// A smart pointer to a mutable `str` in guest memory. -/// Usable as a `&'a str` via [`std::ops::Deref`] and as a `&'a mut str` via -/// [`std::ops::DerefMut`]. -pub struct GuestStrMut<'a>(GuestSliceMut<'a, u8>); - -impl<'a> std::convert::TryFrom> for GuestStrMut<'a> { - type Error = GuestError; - fn try_from(slice: GuestSliceMut<'a, u8>) -> Result { - match str::from_utf8(&slice) { - Ok(_) => Ok(Self(slice)), - Err(e) => Err(GuestError::InvalidUtf8(e)), - } - } -} - -impl<'a> std::ops::Deref for GuestStrMut<'a> { - type Target = str; - fn deref(&self) -> &Self::Target { - // SAFETY: every slice in a `GuestStrMut` has already been checked for - // UTF-8 validity during construction (i.e., `TryFrom`). - unsafe { str::from_utf8_unchecked(&self.0) } - } -} - -impl<'a> std::ops::DerefMut for GuestStrMut<'a> { - fn deref_mut(&mut self) -> &mut Self::Target { - // SAFETY: every slice in a `GuestStrMut` has already been checked for - // UTF-8 validity during construction (i.e., `TryFrom`). - unsafe { str::from_utf8_unchecked_mut(&mut self.0) } - } -} - -/// A smart pointer to a `str` for distinguishing between different kinds of -/// Wasm memory: shared and non-shared. -/// -/// As with `GuestStr`, this is usable as a `&'a str` via [`std::ops::Deref`]. -/// The major difference is that, for shared memories, the string will be copied -/// out of Wasm linear memory to avoid the possibility of concurrent mutation by -/// another thread. This extra copy exists solely to maintain the Rust -/// guarantees regarding `&str`. -pub enum GuestStrCow<'a> { - Borrowed(GuestStr<'a>), - Copied(String), -} - -impl<'a> std::ops::Deref for GuestStrCow<'a> { - type Target = str; - - fn deref(&self) -> &Self::Target { - match self { - GuestStrCow::Borrowed(s) => s, - GuestStrCow::Copied(s) => s, - } +impl PartialEq for GuestPtr { + fn eq(&self, other: &Self) -> bool { + self.pointer == other.pointer } } @@ -1121,7 +546,7 @@ mod private { /// `str` and `[T]` which have special implementations. pub trait Pointee: private::Sealed { #[doc(hidden)] - type Pointer: Copy; + type Pointer: Copy + PartialEq; #[doc(hidden)] fn debug(pointer: Self::Pointer, f: &mut fmt::Formatter) -> fmt::Result; } diff --git a/crates/wiggle/src/wasmtime.rs b/crates/wiggle/src/wasmtime.rs deleted file mode 100644 index 21933fc7aaf9..000000000000 --- a/crates/wiggle/src/wasmtime.rs +++ /dev/null @@ -1,97 +0,0 @@ -use crate::borrow::BorrowChecker; -use crate::{BorrowHandle, GuestError, GuestMemory, Region}; -use std::cell::UnsafeCell; - -/// Lightweight `wasmtime::Memory` wrapper so we can implement the -/// `wiggle::GuestMemory` trait on it. -pub struct WasmtimeGuestMemory<'a> { - mem: &'a [UnsafeCell], - bc: BorrowChecker, - shared: bool, -} - -// These need to be reapplied due to the usage of `UnsafeCell` internally. -unsafe impl Send for WasmtimeGuestMemory<'_> {} -unsafe impl Sync for WasmtimeGuestMemory<'_> {} - -impl<'a> WasmtimeGuestMemory<'a> { - pub fn new(mem: &'a mut [u8]) -> Self { - Self { - // SAFETY: here the `&mut [u8]` is casted to `&[UnsafeCell]` - // which is losing in effect the `&mut` access but retaining the - // borrow. This is done to reflect how the memory is not safe to - // access while multiple borrows are handed out internally, checked - // with `bc` below. - // - // Additionally this allows unshared memories to have the same - // internal representation as shared memories. - mem: unsafe { std::slice::from_raw_parts(mem.as_ptr().cast(), mem.len()) }, - - // Wiggle does not expose any methods for functions to re-enter - // the WebAssembly instance, or expose the memory via non-wiggle - // mechanisms. However, the user-defined code may end up - // re-entering the instance, in which case this is an incorrect - // implementation - we require exactly one BorrowChecker exist per - // instance. - // This BorrowChecker construction is a holdover until it is - // integrated fully with wasmtime: - // https://github.com/bytecodealliance/wasmtime/issues/1917 - bc: BorrowChecker::new(), - shared: false, - } - } - - pub fn shared(mem: &'a [UnsafeCell]) -> Self { - Self { - mem, - bc: BorrowChecker::new(), - shared: true, - } - } -} - -unsafe impl GuestMemory for WasmtimeGuestMemory<'_> { - #[inline] - fn base(&self) -> &[UnsafeCell] { - self.mem - } - - // Note that this implementation has special cases for shared memory - // specifically because no regions of a shared memory can ever be borrowed. - // In the shared memory cases `shared_borrow` and `mut_borrow` are never - // called so that can be used to optimize the other methods by quickly - // checking a flag before calling the more expensive borrow-checker methods. - - #[inline] - fn can_read(&self, r: Region) -> bool { - self.shared || self.bc.can_read(r) - } - #[inline] - fn can_write(&self, r: Region) -> bool { - self.shared || self.bc.can_write(r) - } - #[inline] - fn shared_borrow(&self, r: Region) -> Result { - debug_assert!(!self.shared); - self.bc.shared_borrow(r) - } - #[inline] - fn mut_borrow(&self, r: Region) -> Result { - debug_assert!(!self.shared); - self.bc.mut_borrow(r) - } - #[inline] - fn shared_unborrow(&self, h: BorrowHandle) { - debug_assert!(!self.shared); - self.bc.shared_unborrow(h) - } - #[inline] - fn mut_unborrow(&self, h: BorrowHandle) { - debug_assert!(!self.shared); - self.bc.mut_unborrow(h) - } - #[inline] - fn is_shared_memory(&self) -> bool { - self.shared - } -} diff --git a/crates/wiggle/test-helpers/examples/tracing.rs b/crates/wiggle/test-helpers/examples/tracing.rs index 8de56c8dba63..7d91b0fecf6d 100644 --- a/crates/wiggle/test-helpers/examples/tracing.rs +++ b/crates/wiggle/test-helpers/examples/tracing.rs @@ -1,4 +1,5 @@ use anyhow::Result; +use wiggle::GuestMemory; use wiggle_test::{impl_errno, HostMemory, WasiCtx}; /// The `errors` argument to the wiggle gives us a hook to map a rich error @@ -53,7 +54,12 @@ impl<'a> types::UserErrorConversion for WasiCtx<'a> { } impl<'a> one_error_conversion::OneErrorConversion for WasiCtx<'a> { - fn foo(&mut self, strike: u32, _s: &types::S) -> Result { + fn foo( + &mut self, + _: &mut GuestMemory<'_>, + strike: u32, + _s: &types::S, + ) -> Result { // We use the argument to this function to exercise all of the // possible error cases we could hit here match strike { @@ -83,11 +89,12 @@ fn main() { } let mut ctx = WasiCtx::new(); - let host_memory = HostMemory::new(); + let mut host_memory = HostMemory::new(); + let mut memory = host_memory.guest_memory(); // Exercise each of the branches in `foo`. // Start with the success case: - let r0 = one_error_conversion::foo(&mut ctx, &host_memory, 0, 0, 8).unwrap(); + let r0 = one_error_conversion::foo(&mut ctx, &mut memory, 0, 0, 8).unwrap(); assert_eq!( r0, types::Errno::Ok as i32, @@ -96,7 +103,7 @@ fn main() { assert!(ctx.log.borrow().is_empty(), "No error log for strike=0"); // First error case: - let r1 = one_error_conversion::foo(&mut ctx, &host_memory, 1, 0, 8).unwrap(); + let r1 = one_error_conversion::foo(&mut ctx, &mut memory, 1, 0, 8).unwrap(); assert_eq!( r1, types::Errno::PicketLine as i32, @@ -109,7 +116,7 @@ fn main() { ); // Second error case: - let r2 = one_error_conversion::foo(&mut ctx, &host_memory, 2, 0, 8).unwrap(); + let r2 = one_error_conversion::foo(&mut ctx, &mut memory, 2, 0, 8).unwrap(); assert_eq!( r2, types::Errno::InvalidArg as i32, diff --git a/crates/wiggle/test-helpers/src/lib.rs b/crates/wiggle/test-helpers/src/lib.rs index c6226fda14e5..4ba544c33281 100644 --- a/crates/wiggle/test-helpers/src/lib.rs +++ b/crates/wiggle/test-helpers/src/lib.rs @@ -1,7 +1,6 @@ use proptest::prelude::*; -use std::cell::UnsafeCell; use std::marker; -use wiggle::{borrow::BorrowChecker, BorrowHandle, GuestMemory, Region}; +use wiggle::GuestMemory; #[derive(Debug, Clone)] pub struct MemAreas(Vec); @@ -44,7 +43,7 @@ impl Into> for MemAreas { #[repr(align(4096))] struct HostBuffer { - cell: UnsafeCell<[u8; 4096]>, + cell: [u8; 4096], } unsafe impl Send for HostBuffer {} @@ -52,18 +51,22 @@ unsafe impl Sync for HostBuffer {} pub struct HostMemory { buffer: HostBuffer, - bc: BorrowChecker, } impl HostMemory { pub fn new() -> Self { HostMemory { - buffer: HostBuffer { - cell: UnsafeCell::new([0; 4096]), - }, - bc: BorrowChecker::new(), + buffer: HostBuffer { cell: [0; 4096] }, } } + pub fn guest_memory(&mut self) -> GuestMemory<'_> { + GuestMemory::Unshared(&mut self.buffer.cell) + } + + pub fn base(&self) -> *const u8 { + self.buffer.cell.as_ptr() + } + pub fn mem_area_strat(align: u32) -> BoxedStrategy { prop::num::u32::ANY .prop_filter_map("needs to fit in memory", move |p| { @@ -116,31 +119,6 @@ impl HostMemory { } } -unsafe impl GuestMemory for HostMemory { - fn base(&self) -> &[UnsafeCell] { - let ptr = self.buffer.cell.get(); - unsafe { std::slice::from_raw_parts(ptr.cast(), (*ptr).len()) } - } - fn can_read(&self, r: Region) -> bool { - self.bc.can_read(r) - } - fn can_write(&self, r: Region) -> bool { - self.bc.can_write(r) - } - fn mut_borrow(&self, r: Region) -> Result { - self.bc.mut_borrow(r) - } - fn shared_borrow(&self, r: Region) -> Result { - self.bc.shared_borrow(r) - } - fn shared_unborrow(&self, h: BorrowHandle) { - self.bc.shared_unborrow(h) - } - fn mut_unborrow(&self, h: BorrowHandle) { - self.bc.mut_unborrow(h) - } -} - #[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord)] pub struct MemArea { pub ptr: u32, @@ -213,9 +191,9 @@ mod test { #[test] fn hostmemory_is_aligned() { let h = HostMemory::new(); - assert_eq!(h.base().as_ptr() as usize % 4096, 0); + assert_eq!(h.base() as usize % 4096, 0); let h = Box::new(h); - assert_eq!(h.base().as_ptr() as usize % 4096, 0); + assert_eq!(h.base() as usize % 4096, 0); } #[test] diff --git a/crates/wiggle/tests/atoms.rs b/crates/wiggle/tests/atoms.rs index 3b97717b4dc7..27717efad496 100644 --- a/crates/wiggle/tests/atoms.rs +++ b/crates/wiggle/tests/atoms.rs @@ -1,5 +1,5 @@ use proptest::prelude::*; -use wiggle::GuestMemory; +use wiggle::{GuestMemory, GuestPtr}; use wiggle_test::{impl_errno, HostMemory, MemArea, WasiCtx}; wiggle::from_witx!({ @@ -9,12 +9,18 @@ wiggle::from_witx!({ impl_errno!(types::Errno); impl<'a> atoms::Atoms for WasiCtx<'a> { - fn int_float_args(&mut self, an_int: u32, an_float: f32) -> Result<(), types::Errno> { + fn int_float_args( + &mut self, + _memory: &mut GuestMemory<'_>, + an_int: u32, + an_float: f32, + ) -> Result<(), types::Errno> { println!("INT FLOAT ARGS: {} {}", an_int, an_float); Ok(()) } fn double_int_return_float( &mut self, + _memory: &mut GuestMemory<'_>, an_int: u32, ) -> Result { Ok((an_int as f32) * 2.0) @@ -32,10 +38,15 @@ struct IntFloatExercise { impl IntFloatExercise { pub fn test(&self) { let mut ctx = WasiCtx::new(); - let host_memory = HostMemory::new(); + let mut host_memory = HostMemory::new(); - let e = atoms::int_float_args(&mut ctx, &host_memory, self.an_int as i32, self.an_float) - .unwrap(); + let e = atoms::int_float_args( + &mut ctx, + &mut host_memory.guest_memory(), + self.an_int as i32, + self.an_float, + ) + .unwrap(); assert_eq!(e, types::Errno::Ok as i32, "int_float_args error"); } @@ -62,19 +73,19 @@ struct DoubleIntExercise { impl DoubleIntExercise { pub fn test(&self) { let mut ctx = WasiCtx::new(); - let host_memory = HostMemory::new(); + let mut host_memory = HostMemory::new(); + let mut memory = host_memory.guest_memory(); let e = atoms::double_int_return_float( &mut ctx, - &host_memory, + &mut memory, self.input as i32, self.return_loc.ptr as i32, ) .unwrap(); - let return_val = host_memory - .ptr::(self.return_loc.ptr) - .read() + let return_val = memory + .read(GuestPtr::::new(self.return_loc.ptr)) .expect("failed to read return"); assert_eq!(e, types::Errno::Ok as i32, "errno"); assert_eq!(return_val, (self.input as f32) * 2.0, "return val"); diff --git a/crates/wiggle/tests/atoms_async.rs b/crates/wiggle/tests/atoms_async.rs index 0fc9660b37ff..32d2877de8e2 100644 --- a/crates/wiggle/tests/atoms_async.rs +++ b/crates/wiggle/tests/atoms_async.rs @@ -2,7 +2,7 @@ use proptest::prelude::*; use std::future::Future; use std::pin::Pin; use std::task::{Context, Poll, RawWaker, RawWakerVTable, Waker}; -use wiggle::GuestMemory; +use wiggle::{GuestMemory, GuestPtr}; use wiggle_test::{impl_errno, HostMemory, MemArea, WasiCtx}; wiggle::from_witx!({ @@ -14,12 +14,18 @@ impl_errno!(types::Errno); #[wiggle::async_trait] impl<'a> atoms::Atoms for WasiCtx<'a> { - async fn int_float_args(&mut self, an_int: u32, an_float: f32) -> Result<(), types::Errno> { + async fn int_float_args( + &mut self, + _memory: &mut GuestMemory<'_>, + an_int: u32, + an_float: f32, + ) -> Result<(), types::Errno> { println!("INT FLOAT ARGS: {} {}", an_int, an_float); Ok(()) } async fn double_int_return_float( &mut self, + _memory: &mut GuestMemory<'_>, an_int: u32, ) -> Result { Ok((an_int as f32) * 2.0) @@ -37,11 +43,12 @@ struct IntFloatExercise { impl IntFloatExercise { pub fn test(&self) { let mut ctx = WasiCtx::new(); - let host_memory = HostMemory::new(); + let mut host_memory = HostMemory::new(); + let mut memory = host_memory.guest_memory(); let e = run(atoms::int_float_args( &mut ctx, - &host_memory, + &mut memory, self.an_int as i32, self.an_float, )) @@ -72,19 +79,19 @@ struct DoubleIntExercise { impl DoubleIntExercise { pub fn test(&self) { let mut ctx = WasiCtx::new(); - let host_memory = HostMemory::new(); + let mut host_memory = HostMemory::new(); + let mut memory = host_memory.guest_memory(); let e = run(atoms::double_int_return_float( &mut ctx, - &host_memory, + &mut memory, self.input as i32, self.return_loc.ptr as i32, )) .unwrap(); - let return_val = host_memory - .ptr::(self.return_loc.ptr) - .read() + let return_val = memory + .read(GuestPtr::::new(self.return_loc.ptr)) .expect("failed to read return"); assert_eq!(e, types::Errno::Ok as i32, "errno"); assert_eq!(return_val, (self.input as f32) * 2.0, "return val"); diff --git a/crates/wiggle/tests/errors.rs b/crates/wiggle/tests/errors.rs index 594937843003..c148279cf719 100644 --- a/crates/wiggle/tests/errors.rs +++ b/crates/wiggle/tests/errors.rs @@ -1,6 +1,7 @@ /// Execute the wiggle guest conversion code to exercise it mod convert_just_errno { use anyhow::Result; + use wiggle::GuestMemory; use wiggle_test::{impl_errno, HostMemory, WasiCtx}; /// The `errors` argument to the wiggle gives us a hook to map a rich error @@ -43,7 +44,7 @@ mod convert_just_errno { } impl<'a> one_error_conversion::OneErrorConversion for WasiCtx<'a> { - fn foo(&mut self, strike: u32) -> Result<(), types::ErrnoT> { + fn foo(&mut self, _memory: &mut GuestMemory<'_>, strike: u32) -> Result<(), types::ErrnoT> { // We use the argument to this function to exercise all of the // possible error cases we could hit here match strike { @@ -57,11 +58,12 @@ mod convert_just_errno { #[test] fn one_error_conversion_test() { let mut ctx = WasiCtx::new(); - let host_memory = HostMemory::new(); + let mut host_memory = HostMemory::new(); + let mut memory = host_memory.guest_memory(); // Exercise each of the branches in `foo`. // Start with the success case: - let r0 = one_error_conversion::foo(&mut ctx, &host_memory, 0).unwrap(); + let r0 = one_error_conversion::foo(&mut ctx, &mut memory, 0).unwrap(); assert_eq!( r0, types::Errno::Ok as i32, @@ -70,7 +72,7 @@ mod convert_just_errno { assert!(ctx.log.borrow().is_empty(), "No error log for strike=0"); // First error case: - let r1 = one_error_conversion::foo(&mut ctx, &host_memory, 1).unwrap(); + let r1 = one_error_conversion::foo(&mut ctx, &mut memory, 1).unwrap(); assert_eq!( r1, types::Errno::PicketLine as i32, @@ -78,7 +80,7 @@ mod convert_just_errno { ); // Second error case: - let r2 = one_error_conversion::foo(&mut ctx, &host_memory, 2).unwrap(); + let r2 = one_error_conversion::foo(&mut ctx, &mut memory, 2).unwrap(); assert_eq!( r2, types::Errno::InvalidArg as i32, @@ -92,6 +94,7 @@ mod convert_just_errno { mod convert_multiple_error_types { pub use super::convert_just_errno::RichError; use anyhow::Result; + use wiggle::GuestMemory; use wiggle_test::{impl_errno, WasiCtx}; /// Test that we can map multiple types of errors. @@ -143,13 +146,13 @@ mod convert_multiple_error_types { // And here's the witx module trait impl, bodies elided impl<'a> two_error_conversions::TwoErrorConversions for WasiCtx<'a> { - fn foo(&mut self, _: u32) -> Result<(), RichError> { + fn foo(&mut self, _: &mut GuestMemory<'_>, _: u32) -> Result<(), RichError> { unimplemented!() } - fn bar(&mut self, _: u32) -> Result<(), AnotherRichError> { + fn bar(&mut self, _: &mut GuestMemory<'_>, _: u32) -> Result<(), AnotherRichError> { unimplemented!() } - fn baz(&mut self, _: u32) -> anyhow::Error { + fn baz(&mut self, _: &mut GuestMemory<'_>, _: u32) -> anyhow::Error { unimplemented!() } } diff --git a/crates/wiggle/tests/flags.rs b/crates/wiggle/tests/flags.rs index c4089ef2d8e4..d9b04704a171 100644 --- a/crates/wiggle/tests/flags.rs +++ b/crates/wiggle/tests/flags.rs @@ -11,10 +11,11 @@ impl_errno!(types::Errno); impl<'a> flags::Flags for WasiCtx<'a> { fn configure_car( &mut self, + memory: &mut GuestMemory<'_>, old_config: types::CarConfig, - other_config_ptr: &GuestPtr, + other_config_ptr: GuestPtr, ) -> Result { - let other_config = other_config_ptr.read().map_err(|e| { + let other_config = memory.read(other_config_ptr).map_err(|e| { eprintln!("old_config_ptr error: {}", e); types::Errno::InvalidArg })?; @@ -62,17 +63,20 @@ impl ConfigureCarExercise { pub fn test(&self) { let mut ctx = WasiCtx::new(); - let host_memory = HostMemory::new(); + let mut host_memory = HostMemory::new(); + let mut memory = host_memory.guest_memory(); // Populate input ptr - host_memory - .ptr(self.other_config_by_ptr.ptr) - .write(self.other_config) + memory + .write( + GuestPtr::new(self.other_config_by_ptr.ptr), + self.other_config, + ) .expect("deref ptr mut to CarConfig"); let res = flags::configure_car( &mut ctx, - &host_memory, + &mut memory, self.old_config.bits() as i32, self.other_config_by_ptr.ptr as i32, self.return_ptr_loc.ptr as i32, @@ -80,9 +84,8 @@ impl ConfigureCarExercise { .unwrap(); assert_eq!(res, types::Errno::Ok as i32, "configure car errno"); - let res_config = host_memory - .ptr::(self.return_ptr_loc.ptr) - .read() + let res_config = memory + .read(GuestPtr::::new(self.return_ptr_loc.ptr)) .expect("deref to CarConfig value"); assert_eq!( diff --git a/crates/wiggle/tests/handles.rs b/crates/wiggle/tests/handles.rs index ea1e0abcdd22..2060afbe5882 100644 --- a/crates/wiggle/tests/handles.rs +++ b/crates/wiggle/tests/handles.rs @@ -1,5 +1,5 @@ use proptest::prelude::*; -use wiggle::{GuestMemory, GuestType}; +use wiggle::{GuestMemory, GuestPtr, GuestType}; use wiggle_test::{impl_errno, HostMemory, MemArea, WasiCtx}; const FD_VAL: u32 = 123; @@ -11,10 +11,14 @@ wiggle::from_witx!({ impl_errno!(types::Errno); impl<'a> handle_examples::HandleExamples for WasiCtx<'a> { - fn fd_create(&mut self) -> Result { + fn fd_create(&mut self, _memory: &mut GuestMemory<'_>) -> Result { Ok(types::Fd::from(FD_VAL)) } - fn fd_consume(&mut self, fd: types::Fd) -> Result<(), types::Errno> { + fn fd_consume( + &mut self, + _memory: &mut GuestMemory<'_>, + fd: types::Fd, + ) -> Result<(), types::Errno> { println!("FD_CONSUME {}", fd); if fd == types::Fd::from(FD_VAL) { Ok(()) @@ -32,25 +36,25 @@ struct HandleExercise { impl HandleExercise { pub fn test(&self) { let mut ctx = WasiCtx::new(); - let host_memory = HostMemory::new(); + let mut host_memory = HostMemory::new(); + let mut memory = host_memory.guest_memory(); let e = - handle_examples::fd_create(&mut ctx, &host_memory, self.return_loc.ptr as i32).unwrap(); + handle_examples::fd_create(&mut ctx, &mut memory, self.return_loc.ptr as i32).unwrap(); assert_eq!(e, types::Errno::Ok as i32, "fd_create error"); - let h_got: u32 = host_memory - .ptr(self.return_loc.ptr) - .read() + let h_got: u32 = memory + .read(GuestPtr::new(self.return_loc.ptr)) .expect("return ref_mut"); assert_eq!(h_got, 123, "fd_create return val"); - let e = handle_examples::fd_consume(&mut ctx, &host_memory, h_got as i32).unwrap(); + let e = handle_examples::fd_consume(&mut ctx, &mut memory, h_got as i32).unwrap(); assert_eq!(e, types::Errno::Ok as i32, "fd_consume error"); - let e = handle_examples::fd_consume(&mut ctx, &host_memory, h_got as i32 + 1).unwrap(); + let e = handle_examples::fd_consume(&mut ctx, &mut memory, h_got as i32 + 1).unwrap(); assert_eq!( e, diff --git a/crates/wiggle/tests/ints.rs b/crates/wiggle/tests/ints.rs index 551717de8b4c..21fb40db1897 100644 --- a/crates/wiggle/tests/ints.rs +++ b/crates/wiggle/tests/ints.rs @@ -1,5 +1,5 @@ use proptest::prelude::*; -use wiggle::GuestMemory; +use wiggle::{GuestMemory, GuestPtr}; use wiggle_test::{impl_errno, HostMemory, MemArea, WasiCtx}; wiggle::from_witx!({ @@ -9,7 +9,11 @@ wiggle::from_witx!({ impl_errno!(types::Errno); impl<'a> ints::Ints for WasiCtx<'a> { - fn cookie_cutter(&mut self, init_cookie: types::Cookie) -> Result { + fn cookie_cutter( + &mut self, + _memory: &mut GuestMemory<'_>, + init_cookie: types::Cookie, + ) -> Result { let res = if init_cookie == types::COOKIE_START { types::Bool::True } else { @@ -43,20 +47,20 @@ impl CookieCutterExercise { pub fn test(&self) { let mut ctx = WasiCtx::new(); - let host_memory = HostMemory::new(); + let mut host_memory = HostMemory::new(); + let mut memory = host_memory.guest_memory(); let res = ints::cookie_cutter( &mut ctx, - &host_memory, + &mut memory, self.cookie as i64, self.return_ptr_loc.ptr as i32, ) .unwrap(); assert_eq!(res, types::Errno::Ok as i32, "cookie cutter errno"); - let is_cookie_start = host_memory - .ptr::(self.return_ptr_loc.ptr) - .read() + let is_cookie_start = memory + .read(GuestPtr::::new(self.return_ptr_loc.ptr)) .expect("deref to Bool value"); assert_eq!( diff --git a/crates/wiggle/tests/keywords.rs b/crates/wiggle/tests/keywords.rs index eda062b81977..6c0f7a1df124 100644 --- a/crates/wiggle/tests/keywords.rs +++ b/crates/wiggle/tests/keywords.rs @@ -33,7 +33,7 @@ mod module_trait_fn_and_arg_test { }); impl<'a> self_::Self_ for WasiCtx<'a> { #[allow(unused_variables)] - fn fn_(&mut self, use_: u32, virtual_: u32) { + fn fn_(&mut self, _memory: &mut wiggle::GuestMemory<'_>, use_: u32, virtual_: u32) { unimplemented!(); } } diff --git a/crates/wiggle/tests/lists.rs b/crates/wiggle/tests/lists.rs index 5f94238a4565..a21c703cb5a8 100644 --- a/crates/wiggle/tests/lists.rs +++ b/crates/wiggle/tests/lists.rs @@ -11,26 +11,32 @@ impl_errno!(types::Errno); impl<'a> lists::Lists for WasiCtx<'a> { fn reduce_excuses( &mut self, - excuses: &types::ConstExcuseArray, + memory: &mut GuestMemory<'_>, + excuses: types::ConstExcuseArray, ) -> Result { - let last = &excuses - .iter() - .last() - .expect("input array is non-empty") - .expect("valid ptr to ptr") - .read() + let last = memory + .read( + excuses + .iter() + .last() + .expect("input array is non-empty") + .expect("valid ptr to ptr"), + ) .expect("valid ptr to some Excuse value"); - Ok(last.read().expect("dereferencing ptr should succeed")) + Ok(memory.read(last).expect("dereferencing ptr should succeed")) } - fn populate_excuses(&mut self, excuses: &types::ExcuseArray) -> Result<(), types::Errno> { + fn populate_excuses( + &mut self, + memory: &mut GuestMemory<'_>, + excuses: types::ExcuseArray, + ) -> Result<(), types::Errno> { for excuse in excuses.iter() { - let ptr_to_excuse = excuse - .expect("valid ptr to ptr") - .read() + let ptr_to_excuse = memory + .read(excuse.expect("valid ptr to ptr")) .expect("valid ptr to some Excuse value"); - ptr_to_excuse - .write(types::Excuse::Sleeping) + memory + .write(ptr_to_excuse, types::Excuse::Sleeping) .expect("dereferencing mut ptr should succeed"); } Ok(()) @@ -75,30 +81,31 @@ impl ReduceExcusesExcercise { pub fn test(&self) { let mut ctx = WasiCtx::new(); - let host_memory = HostMemory::new(); + let mut host_memory = HostMemory::new(); + let mut memory = host_memory.guest_memory(); // Populate memory with pointers to generated Excuse values for (&excuse, ptr) in self.excuse_values.iter().zip(self.excuse_ptr_locs.iter()) { - host_memory - .ptr(ptr.ptr) - .write(excuse) + memory + .write(GuestPtr::new(ptr.ptr), excuse) .expect("deref ptr mut to Excuse value"); } // Populate the array with pointers to generated Excuse values { - let array: GuestPtr<'_, [GuestPtr]> = - host_memory.ptr((self.array_ptr_loc.ptr, self.excuse_ptr_locs.len() as u32)); + let array: GuestPtr<[GuestPtr]> = + GuestPtr::new((self.array_ptr_loc.ptr, self.excuse_ptr_locs.len() as u32)); for (slot, ptr) in array.iter().zip(&self.excuse_ptr_locs) { let slot = slot.expect("array should be in bounds"); - slot.write(host_memory.ptr(ptr.ptr)) + memory + .write(slot, GuestPtr::new(ptr.ptr)) .expect("should succeed in writing array"); } } let res = lists::reduce_excuses( &mut ctx, - &host_memory, + &mut memory, self.array_ptr_loc.ptr as i32, self.excuse_ptr_locs.len() as i32, self.return_ptr_loc.ptr as i32, @@ -111,9 +118,8 @@ impl ReduceExcusesExcercise { .excuse_values .last() .expect("generated vec of excuses should be non-empty"); - let given: types::Excuse = host_memory - .ptr(self.return_ptr_loc.ptr) - .read() + let given: types::Excuse = memory + .read(GuestPtr::new(self.return_ptr_loc.ptr)) .expect("deref ptr to returned value"); assert_eq!(expected, given, "reduce excuses return val"); } @@ -164,38 +170,41 @@ impl PopulateExcusesExcercise { pub fn test(&self) { let mut ctx = WasiCtx::new(); - let host_memory = HostMemory::new(); + let mut host_memory = HostMemory::new(); + let mut memory = host_memory.guest_memory(); // Populate array with valid pointers to Excuse type in memory - let ptr = host_memory.ptr::<[GuestPtr<'_, types::Excuse>]>(( + let ptr = GuestPtr::<[GuestPtr]>::new(( self.array_ptr_loc.ptr, self.elements.len() as u32, )); for (ptr, val) in ptr.iter().zip(&self.elements) { - ptr.expect("should be valid pointer") - .write(host_memory.ptr(val.ptr)) + memory + .write( + ptr.expect("should be valid pointer"), + GuestPtr::new(val.ptr), + ) .expect("failed to write value"); } let res = lists::populate_excuses( &mut ctx, - &host_memory, + &mut memory, self.array_ptr_loc.ptr as i32, self.elements.len() as i32, ) .unwrap(); assert_eq!(res, types::Errno::Ok as i32, "populate excuses errno"); - let arr: GuestPtr<'_, [GuestPtr<'_, types::Excuse>]> = - host_memory.ptr((self.array_ptr_loc.ptr, self.elements.len() as u32)); + let arr: GuestPtr<[GuestPtr]> = + GuestPtr::new((self.array_ptr_loc.ptr, self.elements.len() as u32)); for el in arr.iter() { - let ptr_to_ptr = el - .expect("valid ptr to ptr") - .read() + let ptr_to_ptr = memory + .read(el.expect("valid ptr to ptr")) .expect("valid ptr to some Excuse value"); assert_eq!( - ptr_to_ptr - .read() + memory + .read(ptr_to_ptr) .expect("dereferencing ptr to some Excuse value"), types::Excuse::Sleeping, "element should equal Excuse::Sleeping" @@ -213,16 +222,20 @@ proptest! { impl<'a> array_traversal::ArrayTraversal for WasiCtx<'a> { fn sum_of_element( &mut self, - elements: &GuestPtr<[types::PairInts]>, + memory: &mut GuestMemory<'_>, + elements: GuestPtr<[types::PairInts]>, index: u32, ) -> Result { let elem_ptr = elements.get(index).ok_or(types::Errno::InvalidArg)?; - let pair = elem_ptr.read().map_err(|_| types::Errno::DontWantTo)?; + let pair = memory + .read(elem_ptr) + .map_err(|_| types::Errno::DontWantTo)?; Ok(pair.first.wrapping_add(pair.second)) } fn sum_of_elements( &mut self, - elements: &GuestPtr<[types::PairInts]>, + memory: &mut GuestMemory<'_>, + elements: GuestPtr<[types::PairInts]>, start: u32, end: u32, ) -> Result { @@ -231,9 +244,8 @@ impl<'a> array_traversal::ArrayTraversal for WasiCtx<'a> { .ok_or(types::Errno::InvalidArg)?; let mut sum: i32 = 0; for e in elem_range.iter() { - let pair = e - .map_err(|_| types::Errno::DontWantTo)? - .read() + let pair = memory + .read(e.map_err(|_| types::Errno::DontWantTo)?) .map_err(|_| types::Errno::PhysicallyUnable)?; sum = sum.wrapping_add(pair.first).wrapping_add(pair.second); } @@ -291,20 +303,21 @@ impl SumElementsExercise { } pub fn test(&self) { let mut ctx = WasiCtx::new(); - let host_memory = HostMemory::new(); + let mut host_memory = HostMemory::new(); + let mut memory = host_memory.guest_memory(); // Populate array - let ptr = host_memory - .ptr::<[types::PairInts]>((self.element_loc.ptr, self.elements.len() as u32)); + let ptr = + GuestPtr::<[types::PairInts]>::new((self.element_loc.ptr, self.elements.len() as u32)); for (ptr, val) in ptr.iter().zip(&self.elements) { - ptr.expect("should be valid pointer") - .write(val.clone()) + memory + .write(ptr.expect("should be valid pointer"), val.clone()) .expect("failed to write value"); } let res = array_traversal::sum_of_element( &mut ctx, - &host_memory, + &mut memory, self.element_loc.ptr as i32, self.elements.len() as i32, self.start_ix as i32, @@ -312,8 +325,8 @@ impl SumElementsExercise { ) .unwrap(); assert_eq!(res, types::Errno::Ok as i32, "sum_of_element errno"); - let result_ptr = host_memory.ptr::(self.return_loc.ptr); - let result = result_ptr.read().expect("read result"); + let result_ptr = GuestPtr::::new(self.return_loc.ptr); + let result = memory.read(result_ptr).expect("read result"); let e = self .elements @@ -324,7 +337,7 @@ impl SumElementsExercise { // Off the end of the array: let res = array_traversal::sum_of_element( &mut ctx, - &host_memory, + &mut memory, self.element_loc.ptr as i32, self.elements.len() as i32, self.elements.len() as i32, @@ -339,7 +352,7 @@ impl SumElementsExercise { let res = array_traversal::sum_of_elements( &mut ctx, - &host_memory, + &mut memory, self.element_loc.ptr as i32, self.elements.len() as i32, self.start_ix as i32, @@ -353,8 +366,8 @@ impl SumElementsExercise { types::Errno::Ok as i32, "expected ok sum_of_elements errno" ); - let result_ptr = host_memory.ptr::(self.return_loc.ptr); - let result = result_ptr.read().expect("read result"); + let result_ptr = GuestPtr::::new(self.return_loc.ptr); + let result = memory.read(result_ptr).expect("read result"); let mut expected_sum: i32 = 0; for elem in self @@ -379,7 +392,7 @@ impl SumElementsExercise { // Index an array off the end of the array: let res = array_traversal::sum_of_elements( &mut ctx, - &host_memory, + &mut memory, self.element_loc.ptr as i32, self.elements.len() as i32, self.start_ix as i32, diff --git a/crates/wiggle/tests/pointers.rs b/crates/wiggle/tests/pointers.rs index d3c4ac7e53ad..a5e843e05983 100644 --- a/crates/wiggle/tests/pointers.rs +++ b/crates/wiggle/tests/pointers.rs @@ -9,49 +9,50 @@ wiggle::from_witx!({ impl_errno!(types::Errno); impl<'a> pointers::Pointers for WasiCtx<'a> { - fn pointers_and_enums<'b>( + fn pointers_and_enums( &mut self, + memory: &mut GuestMemory<'_>, input1: types::Excuse, - input2_ptr: &GuestPtr<'b, types::Excuse>, - input3_ptr: &GuestPtr<'b, types::Excuse>, - input4_ptr_ptr: &GuestPtr<'b, GuestPtr<'b, types::Excuse>>, + input2_ptr: GuestPtr, + input3_ptr: GuestPtr, + input4_ptr_ptr: GuestPtr>, ) -> Result<(), types::Errno> { println!("BAZ input1 {:?}", input1); - let input2: types::Excuse = input2_ptr.read().map_err(|e| { + let input2: types::Excuse = memory.read(input2_ptr).map_err(|e| { eprintln!("input2_ptr error: {}", e); types::Errno::InvalidArg })?; println!("input2 {:?}", input2); // Read enum value from immutable ptr: - let input3 = input3_ptr.read().map_err(|e| { + let input3 = memory.read(input3_ptr).map_err(|e| { eprintln!("input3_ptr error: {}", e); types::Errno::InvalidArg })?; println!("input3 {:?}", input3); // Write enum to mutable ptr: - input2_ptr.write(input3).map_err(|e| { + memory.write(input2_ptr, input3).map_err(|e| { eprintln!("input2_ptr error: {}", e); types::Errno::InvalidArg })?; println!("wrote to input2_ref {:?}", input3); // Read ptr value from mutable ptr: - let input4_ptr: GuestPtr = input4_ptr_ptr.read().map_err(|e| { + let input4_ptr: GuestPtr = memory.read(input4_ptr_ptr).map_err(|e| { eprintln!("input4_ptr_ptr error: {}", e); types::Errno::InvalidArg })?; // Read enum value from that ptr: - let input4: types::Excuse = input4_ptr.read().map_err(|e| { + let input4: types::Excuse = memory.read(input4_ptr).map_err(|e| { eprintln!("input4_ptr error: {}", e); types::Errno::InvalidArg })?; println!("input4 {:?}", input4); // Write ptr value to mutable ptr: - input4_ptr_ptr.write(*input2_ptr).map_err(|e| { + memory.write(input4_ptr_ptr, input2_ptr).map_err(|e| { eprintln!("input4_ptr_ptr error: {}", e); types::Errno::InvalidArg })?; @@ -126,31 +127,28 @@ impl PointersAndEnumsExercise { } pub fn test(&self) { let mut ctx = WasiCtx::new(); - let host_memory = HostMemory::new(); + let mut host_memory = HostMemory::new(); + let mut memory = host_memory.guest_memory(); - host_memory - .ptr(self.input2_loc.ptr) - .write(self.input2) + memory + .write(GuestPtr::new(self.input2_loc.ptr), self.input2) .expect("input2 ref_mut"); - host_memory - .ptr(self.input3_loc.ptr) - .write(self.input3) + memory + .write(GuestPtr::new(self.input3_loc.ptr), self.input3) .expect("input3 ref_mut"); - host_memory - .ptr(self.input4_loc.ptr) - .write(self.input4) + memory + .write(GuestPtr::new(self.input4_loc.ptr), self.input4) .expect("input4 ref_mut"); - host_memory - .ptr(self.input4_ptr_loc.ptr) - .write(self.input4_loc.ptr) + memory + .write(GuestPtr::new(self.input4_ptr_loc.ptr), self.input4_loc.ptr) .expect("input4 ptr ref_mut"); let e = pointers::pointers_and_enums( &mut ctx, - &host_memory, + &mut memory, self.input1 as i32, self.input2_loc.ptr as i32, self.input3_loc.ptr as i32, @@ -160,9 +158,8 @@ impl PointersAndEnumsExercise { assert_eq!(e, types::Errno::Ok as i32, "errno"); // Implementation of pointers_and_enums writes input3 to the input2_loc: - let written_to_input2_loc: i32 = host_memory - .ptr(self.input2_loc.ptr) - .read() + let written_to_input2_loc: i32 = memory + .read(GuestPtr::new(self.input2_loc.ptr)) .expect("input2 ref"); assert_eq!( @@ -171,9 +168,8 @@ impl PointersAndEnumsExercise { ); // Implementation of pointers_and_enums writes input2_loc to input4_ptr_loc: - let written_to_input4_ptr: u32 = host_memory - .ptr(self.input4_ptr_loc.ptr) - .read() + let written_to_input4_ptr: u32 = memory + .read(GuestPtr::new(self.input4_ptr_loc.ptr)) .expect("input4_ptr_loc ref"); assert_eq!( diff --git a/crates/wiggle/tests/records.rs b/crates/wiggle/tests/records.rs index a9c7d2fa1fc5..93bc885fa4a8 100644 --- a/crates/wiggle/tests/records.rs +++ b/crates/wiggle/tests/records.rs @@ -9,63 +9,80 @@ wiggle::from_witx!({ impl_errno!(types::Errno); impl<'a> records::Records for WasiCtx<'a> { - fn sum_of_pair(&mut self, an_pair: &types::PairInts) -> Result { + fn sum_of_pair( + &mut self, + _memory: &mut GuestMemory<'_>, + an_pair: &types::PairInts, + ) -> Result { Ok(an_pair.first as i64 + an_pair.second as i64) } - fn sum_of_pair_of_ptrs(&mut self, an_pair: &types::PairIntPtrs) -> Result { - let first = an_pair - .first - .read() + fn sum_of_pair_of_ptrs( + &mut self, + memory: &mut GuestMemory<'_>, + an_pair: &types::PairIntPtrs, + ) -> Result { + let first = memory + .read(an_pair.first) .expect("dereferencing GuestPtr should succeed"); - let second = an_pair - .second - .read() + let second = memory + .read(an_pair.second) .expect("dereferencing GuestPtr should succeed"); Ok(first as i64 + second as i64) } - fn sum_of_int_and_ptr(&mut self, an_pair: &types::PairIntAndPtr) -> Result { - let first = an_pair - .first - .read() + fn sum_of_int_and_ptr( + &mut self, + memory: &mut GuestMemory<'_>, + an_pair: &types::PairIntAndPtr, + ) -> Result { + let first = memory + .read(an_pair.first) .expect("dereferencing GuestPtr should succeed"); let second = an_pair.second as i64; Ok(first as i64 + second) } - fn return_pair_ints(&mut self) -> Result { + fn return_pair_ints( + &mut self, + _memory: &mut GuestMemory<'_>, + ) -> Result { Ok(types::PairInts { first: 10, second: 20, }) } - fn return_pair_of_ptrs<'b>( + fn return_pair_of_ptrs( &mut self, - first: &GuestPtr<'b, i32>, - second: &GuestPtr<'b, i32>, - ) -> Result, types::Errno> { + _memory: &mut GuestMemory<'_>, + first: GuestPtr, + second: GuestPtr, + ) -> Result { Ok(types::PairIntPtrs { - first: *first, - second: *second, + first: first, + second: second, }) } - fn sum_array<'b>( + fn sum_array( &mut self, - record_of_list: &types::RecordOfList<'b>, + memory: &mut GuestMemory<'_>, + record_of_list: &types::RecordOfList, ) -> Result { // my kingdom for try blocks - fn aux(record_of_list: &types::RecordOfList) -> Result { + fn aux( + memory: &mut GuestMemory<'_>, + record_of_list: &types::RecordOfList, + ) -> Result { let mut s = 0; for elem in record_of_list.arr.iter() { - let v = elem?.read()?; + let v = memory.read(elem?)?; s += v as u16; } Ok(s) } - match aux(record_of_list) { + match aux(memory, record_of_list) { Ok(s) => Ok(s), Err(guest_err) => { eprintln!("guest error summing array: {:?}", guest_err); @@ -103,19 +120,18 @@ impl SumOfPairExercise { pub fn test(&self) { let mut ctx = WasiCtx::new(); - let host_memory = HostMemory::new(); + let mut host_memory = HostMemory::new(); + let mut memory = host_memory.guest_memory(); - host_memory - .ptr(self.input_loc.ptr) - .write(self.input.first) + memory + .write(GuestPtr::new(self.input_loc.ptr), self.input.first) .expect("input ref_mut"); - host_memory - .ptr(self.input_loc.ptr + 4) - .write(self.input.second) + memory + .write(GuestPtr::new(self.input_loc.ptr + 4), self.input.second) .expect("input ref_mut"); let sum_err = records::sum_of_pair( &mut ctx, - &host_memory, + &mut memory, self.input_loc.ptr as i32, self.return_loc.ptr as i32, ) @@ -123,9 +139,8 @@ impl SumOfPairExercise { assert_eq!(sum_err, types::Errno::Ok as i32, "sum errno"); - let return_val: i64 = host_memory - .ptr(self.return_loc.ptr) - .read() + let return_val: i64 = memory + .read(GuestPtr::new(self.return_loc.ptr)) .expect("return ref"); assert_eq!( @@ -192,29 +207,32 @@ impl SumPairPtrsExercise { } pub fn test(&self) { let mut ctx = WasiCtx::new(); - let host_memory = HostMemory::new(); + let mut host_memory = HostMemory::new(); + let mut memory = host_memory.guest_memory(); - host_memory - .ptr(self.input_first_loc.ptr) - .write(self.input_first) + memory + .write(GuestPtr::new(self.input_first_loc.ptr), self.input_first) .expect("input_first ref"); - host_memory - .ptr(self.input_second_loc.ptr) - .write(self.input_second) + memory + .write(GuestPtr::new(self.input_second_loc.ptr), self.input_second) .expect("input_second ref"); - host_memory - .ptr(self.input_struct_loc.ptr) - .write(self.input_first_loc.ptr) + memory + .write( + GuestPtr::new(self.input_struct_loc.ptr), + self.input_first_loc.ptr, + ) .expect("input_struct ref"); - host_memory - .ptr(self.input_struct_loc.ptr + 4) - .write(self.input_second_loc.ptr) + memory + .write( + GuestPtr::new(self.input_struct_loc.ptr + 4), + self.input_second_loc.ptr, + ) .expect("input_struct ref"); let res = records::sum_of_pair_of_ptrs( &mut ctx, - &host_memory, + &mut memory, self.input_struct_loc.ptr as i32, self.return_loc.ptr as i32, ) @@ -222,9 +240,8 @@ impl SumPairPtrsExercise { assert_eq!(res, types::Errno::Ok as i32, "sum of pair of ptrs errno"); - let doubled: i64 = host_memory - .ptr(self.return_loc.ptr) - .read() + let doubled: i64 = memory + .read(GuestPtr::new(self.return_loc.ptr)) .expect("return ref"); assert_eq!( @@ -277,24 +294,28 @@ impl SumIntAndPtrExercise { } pub fn test(&self) { let mut ctx = WasiCtx::new(); - let host_memory = HostMemory::new(); + let mut host_memory = HostMemory::new(); + let mut memory = host_memory.guest_memory(); - host_memory - .ptr(self.input_first_loc.ptr) - .write(self.input_first) + memory + .write(GuestPtr::new(self.input_first_loc.ptr), self.input_first) .expect("input_first ref"); - host_memory - .ptr(self.input_struct_loc.ptr) - .write(self.input_first_loc.ptr) + memory + .write( + GuestPtr::new(self.input_struct_loc.ptr), + self.input_first_loc.ptr, + ) .expect("input_struct ref"); - host_memory - .ptr(self.input_struct_loc.ptr + 4) - .write(self.input_second) + memory + .write( + GuestPtr::new(self.input_struct_loc.ptr + 4), + self.input_second, + ) .expect("input_struct ref"); let res = records::sum_of_int_and_ptr( &mut ctx, - &host_memory, + &mut memory, self.input_struct_loc.ptr as i32, self.return_loc.ptr as i32, ) @@ -302,9 +323,8 @@ impl SumIntAndPtrExercise { assert_eq!(res, types::Errno::Ok as i32, "sum of int and ptr errno"); - let doubled: i64 = host_memory - .ptr(self.return_loc.ptr) - .read() + let doubled: i64 = memory + .read(GuestPtr::new(self.return_loc.ptr)) .expect("return ref"); assert_eq!( @@ -335,16 +355,16 @@ impl ReturnPairInts { pub fn test(&self) { let mut ctx = WasiCtx::new(); - let host_memory = HostMemory::new(); + let mut host_memory = HostMemory::new(); + let mut memory = host_memory.guest_memory(); let err = - records::return_pair_ints(&mut ctx, &host_memory, self.return_loc.ptr as i32).unwrap(); + records::return_pair_ints(&mut ctx, &mut memory, self.return_loc.ptr as i32).unwrap(); assert_eq!(err, types::Errno::Ok as i32, "return struct errno"); - let return_struct: types::PairInts = host_memory - .ptr(self.return_loc.ptr) - .read() + let return_struct: types::PairInts = memory + .read(GuestPtr::new(self.return_loc.ptr)) .expect("return ref"); assert_eq!( @@ -401,20 +421,19 @@ impl ReturnPairPtrsExercise { } pub fn test(&self) { let mut ctx = WasiCtx::new(); - let host_memory = HostMemory::new(); + let mut host_memory = HostMemory::new(); + let mut memory = host_memory.guest_memory(); - host_memory - .ptr(self.input_first_loc.ptr) - .write(self.input_first) + memory + .write(GuestPtr::new(self.input_first_loc.ptr), self.input_first) .expect("input_first ref"); - host_memory - .ptr(self.input_second_loc.ptr) - .write(self.input_second) + memory + .write(GuestPtr::new(self.input_second_loc.ptr), self.input_second) .expect("input_second ref"); let res = records::return_pair_of_ptrs( &mut ctx, - &host_memory, + &mut memory, self.input_first_loc.ptr as i32, self.input_second_loc.ptr as i32, self.return_loc.ptr as i32, @@ -423,22 +442,21 @@ impl ReturnPairPtrsExercise { assert_eq!(res, types::Errno::Ok as i32, "return pair of ptrs errno"); - let ptr_pair_int_ptrs: types::PairIntPtrs<'_> = host_memory - .ptr(self.return_loc.ptr) - .read() + let ptr_pair_int_ptrs: types::PairIntPtrs = memory + .read(GuestPtr::new(self.return_loc.ptr)) .expect("failed to read return location"); let ret_first_ptr = ptr_pair_int_ptrs.first; let ret_second_ptr = ptr_pair_int_ptrs.second; assert_eq!( self.input_first, - ret_first_ptr - .read() + memory + .read(ret_first_ptr) .expect("deref extracted ptr to first element") ); assert_eq!( self.input_second, - ret_second_ptr - .read() + memory + .read(ret_second_ptr) .expect("deref extracted ptr to second element") ); } @@ -499,31 +517,35 @@ impl SumArrayExercise { } pub fn test(&self) { let mut ctx = WasiCtx::new(); - let host_memory = HostMemory::new(); + let mut host_memory = HostMemory::new(); + let mut memory = host_memory.guest_memory(); // Write inputs to memory as an array for (ix, val) in self.inputs.iter().enumerate() { let ix = ix as u32; - host_memory - .ptr(self.input_array_loc.ptr + ix) - .write(*val) + memory + .write(GuestPtr::new(self.input_array_loc.ptr + ix), *val) .expect("write val to array memory"); } // Write struct that contains the array - host_memory - .ptr(self.input_struct_loc.ptr) - .write(self.input_array_loc.ptr) + memory + .write( + GuestPtr::new(self.input_struct_loc.ptr), + self.input_array_loc.ptr, + ) .expect("write ptr to struct memory"); - host_memory - .ptr(self.input_struct_loc.ptr + 4) - .write(self.inputs.len() as u32) + memory + .write( + GuestPtr::new(self.input_struct_loc.ptr + 4), + self.inputs.len() as u32, + ) .expect("write len to struct memory"); // Call wiggle-generated func let res = records::sum_array( &mut ctx, - &host_memory, + &mut memory, self.input_struct_loc.ptr as i32, self.output_loc.ptr as i32, ) @@ -536,9 +558,8 @@ impl SumArrayExercise { let expected: u16 = self.inputs.iter().map(|v| *v as u16).sum(); // Wiggle stored output value in memory as u16 - let given: u16 = host_memory - .ptr(self.output_loc.ptr) - .read() + let given: u16 = memory + .read(GuestPtr::new(self.output_loc.ptr)) .expect("deref ptr to returned value"); // Assert the two calculations match diff --git a/crates/wiggle/tests/strings.rs b/crates/wiggle/tests/strings.rs index a3ccdcab945a..6e820d209e31 100644 --- a/crates/wiggle/tests/strings.rs +++ b/crates/wiggle/tests/strings.rs @@ -9,9 +9,13 @@ wiggle::from_witx!({ impl_errno!(types::Errno); impl<'a> strings::Strings for WasiCtx<'a> { - fn hello_string(&mut self, a_string: &GuestPtr) -> Result { - let s = a_string - .as_str() + fn hello_string( + &mut self, + memory: &mut GuestMemory<'_>, + a_string: GuestPtr, + ) -> Result { + let s = memory + .as_str(a_string) .expect("should be valid string") .expect("expected non-shared memory"); println!("a_string='{}'", &*s); @@ -20,20 +24,21 @@ impl<'a> strings::Strings for WasiCtx<'a> { fn multi_string( &mut self, - a: &GuestPtr, - b: &GuestPtr, - c: &GuestPtr, + memory: &mut GuestMemory<'_>, + a: GuestPtr, + b: GuestPtr, + c: GuestPtr, ) -> Result { - let sa = a - .as_str() + let sa = memory + .as_str(a) .expect("A should be valid string") .expect("expected non-shared memory"); - let sb = b - .as_str() + let sb = memory + .as_str(b) .expect("B should be valid string") .expect("expected non-shared memory"); - let sc = c - .as_str() + let sc = memory + .as_str(c) .expect("C should be valid string") .expect("expected non-shared memory"); let total_len = sa.len() + sb.len() + sc.len(); @@ -82,19 +87,20 @@ impl HelloStringExercise { pub fn test(&self) { let mut ctx = WasiCtx::new(); - let host_memory = HostMemory::new(); + let mut host_memory = HostMemory::new(); + let mut memory = host_memory.guest_memory(); // Populate string in guest's memory - let ptr = host_memory.ptr::((self.string_ptr_loc.ptr, self.test_word.len() as u32)); + let ptr = GuestPtr::::new((self.string_ptr_loc.ptr, self.test_word.len() as u32)); for (slot, byte) in ptr.as_bytes().iter().zip(self.test_word.bytes()) { - slot.expect("should be valid pointer") - .write(byte) + memory + .write(slot.expect("should be valid pointer"), byte) .expect("failed to write"); } let res = strings::hello_string( &mut ctx, - &host_memory, + &mut memory, self.string_ptr_loc.ptr as i32, self.test_word.len() as i32, self.return_ptr_loc.ptr as i32, @@ -102,9 +108,8 @@ impl HelloStringExercise { .unwrap(); assert_eq!(res, types::Errno::Ok as i32, "hello string errno"); - let given = host_memory - .ptr::(self.return_ptr_loc.ptr) - .read() + let given = memory + .read(GuestPtr::::new(self.return_ptr_loc.ptr)) .expect("deref ptr to return value"); assert_eq!(self.test_word.len() as u32, given); } @@ -195,13 +200,14 @@ impl MultiStringExercise { pub fn test(&self) { let mut ctx = WasiCtx::new(); - let host_memory = HostMemory::new(); + let mut host_memory = HostMemory::new(); + let mut memory = host_memory.guest_memory(); - let write_string = |val: &str, loc: MemArea| { - let ptr = host_memory.ptr::((loc.ptr, val.len() as u32)); + let mut write_string = |val: &str, loc: MemArea| { + let ptr = GuestPtr::::new((loc.ptr, val.len() as u32)); for (slot, byte) in ptr.as_bytes().iter().zip(val.bytes()) { - slot.expect("should be valid pointer") - .write(byte) + memory + .write(slot.expect("should be valid pointer"), byte) .expect("failed to write"); } }; @@ -212,7 +218,7 @@ impl MultiStringExercise { let res = strings::multi_string( &mut ctx, - &host_memory, + &mut memory, self.sa_ptr_loc.ptr as i32, self.a.len() as i32, self.sb_ptr_loc.ptr as i32, @@ -224,9 +230,8 @@ impl MultiStringExercise { .unwrap(); assert_eq!(res, types::Errno::Ok as i32, "multi string errno"); - let given = host_memory - .ptr::(self.return_ptr_loc.ptr) - .read() + let given = memory + .read(GuestPtr::::new(self.return_ptr_loc.ptr)) .expect("deref ptr to return value"); assert_eq!((self.a.len() + self.b.len() + self.c.len()) as u32, given); } @@ -275,13 +280,14 @@ impl OverlappingStringExercise { pub fn test(&self) { let mut ctx = WasiCtx::new(); - let host_memory = HostMemory::new(); + let mut host_memory = HostMemory::new(); + let mut memory = host_memory.guest_memory(); - let write_string = |val: &str, loc: MemArea| { - let ptr = host_memory.ptr::((loc.ptr, val.len() as u32)); + let mut write_string = |val: &str, loc: MemArea| { + let ptr = GuestPtr::::new((loc.ptr, val.len() as u32)); for (slot, byte) in ptr.as_bytes().iter().zip(val.bytes()) { - slot.expect("should be valid pointer") - .write(byte) + memory + .write(slot.expect("should be valid pointer"), byte) .expect("failed to write"); } }; @@ -291,7 +297,7 @@ impl OverlappingStringExercise { let a_len = self.a.as_bytes().len() as i32; let res = strings::multi_string( &mut ctx, - &host_memory, + &mut memory, self.sa_ptr_loc.ptr as i32, a_len, (self.sa_ptr_loc.ptr + self.offset_b) as i32, @@ -303,9 +309,8 @@ impl OverlappingStringExercise { .unwrap(); assert_eq!(res, types::Errno::Ok as i32, "multi string errno"); - let given = host_memory - .ptr::(self.return_ptr_loc.ptr) - .read() + let given = memory + .read(GuestPtr::::new(self.return_ptr_loc.ptr)) .expect("deref ptr to return value"); assert_eq!( ((3 * a_len) - (self.offset_b as i32 + self.offset_c as i32)) as u32, diff --git a/crates/wiggle/tests/variant.rs b/crates/wiggle/tests/variant.rs index c6b5ab4d58aa..1b56407e7acf 100644 --- a/crates/wiggle/tests/variant.rs +++ b/crates/wiggle/tests/variant.rs @@ -1,5 +1,5 @@ use proptest::prelude::*; -use wiggle::{GuestMemory, GuestType}; +use wiggle::{GuestMemory, GuestPtr, GuestType}; use wiggle_test::{impl_errno, HostMemory, MemArea, WasiCtx}; wiggle::from_witx!({ @@ -31,7 +31,11 @@ fn mult_zero_nan(a: f32, b: u32) -> f32 { } impl<'a> variant_example::VariantExample for WasiCtx<'a> { - fn get_tag(&mut self, u: &types::Reason) -> Result { + fn get_tag( + &mut self, + _memory: &mut GuestMemory<'_>, + u: &types::Reason, + ) -> Result { println!("GET TAG: {:?}", u); match u { types::Reason::DogAte { .. } => Ok(types::Excuse::DogAte), @@ -41,20 +45,23 @@ impl<'a> variant_example::VariantExample for WasiCtx<'a> { } fn reason_mult( &mut self, - u: &types::ReasonMut<'_>, + memory: &mut GuestMemory<'_>, + u: &types::ReasonMut, multiply_by: u32, ) -> Result<(), types::Errno> { match u { types::ReasonMut::DogAte(fptr) => { - let val = fptr.read().expect("valid pointer"); + let val = memory.read(*fptr).expect("valid pointer"); println!("REASON MULT DogAte({})", val); - fptr.write(mult_zero_nan(val, multiply_by)) + memory + .write(*fptr, mult_zero_nan(val, multiply_by)) .expect("valid pointer"); } types::ReasonMut::Traffic(iptr) => { - let val = iptr.read().expect("valid pointer"); + let val = memory.read(*iptr).expect("valid pointer"); println!("REASON MULT Traffic({})", val); - iptr.write(mult_lose_overflow(val, multiply_by)) + memory + .write(*iptr, mult_lose_overflow(val, multiply_by)) .expect("valid pointer"); } types::ReasonMut::Sleeping => { @@ -109,29 +116,27 @@ impl GetTagExercise { pub fn test(&self) { let mut ctx = WasiCtx::new(); - let host_memory = HostMemory::new(); + let mut host_memory = HostMemory::new(); + let mut memory = host_memory.guest_memory(); let discriminant = reason_tag(&self.input) as u8; - host_memory - .ptr(self.input_loc.ptr) - .write(discriminant) + memory + .write(GuestPtr::new(self.input_loc.ptr), discriminant) .expect("input discriminant ptr"); match self.input { types::Reason::DogAte(f) => { - host_memory - .ptr(self.input_loc.ptr + 4) - .write(f) + memory + .write(GuestPtr::new(self.input_loc.ptr + 4), f) .expect("input contents ref_mut"); } - types::Reason::Traffic(v) => host_memory - .ptr(self.input_loc.ptr + 4) - .write(v) + types::Reason::Traffic(v) => memory + .write(GuestPtr::new(self.input_loc.ptr + 4), v) .expect("input contents ref_mut"), types::Reason::Sleeping => {} // Do nothing } let e = variant_example::get_tag( &mut ctx, - &host_memory, + &mut memory, self.input_loc.ptr as i32, self.return_loc.ptr as i32, ) @@ -139,9 +144,8 @@ impl GetTagExercise { assert_eq!(e, types::Errno::Ok as i32, "get_tag errno"); - let return_val: types::Excuse = host_memory - .ptr(self.return_loc.ptr) - .read() + let return_val: types::Excuse = memory + .read(GuestPtr::new(self.return_loc.ptr)) .expect("return ref"); assert_eq!(return_val, reason_tag(&self.input), "get_tag return value"); @@ -187,36 +191,36 @@ impl ReasonMultExercise { pub fn test(&self) { let mut ctx = WasiCtx::new(); - let host_memory = HostMemory::new(); + let mut host_memory = HostMemory::new(); + let mut memory = host_memory.guest_memory(); let discriminant = reason_tag(&self.input) as u8; - host_memory - .ptr(self.input_loc.ptr) - .write(discriminant) + memory + .write(GuestPtr::new(self.input_loc.ptr), discriminant) .expect("input discriminant ref_mut"); - host_memory - .ptr(self.input_loc.ptr + 4) - .write(self.input_pointee_loc.ptr) + memory + .write( + GuestPtr::new(self.input_loc.ptr + 4), + self.input_pointee_loc.ptr, + ) .expect("input pointer ref_mut"); match self.input { types::Reason::DogAte(f) => { - host_memory - .ptr(self.input_pointee_loc.ptr) - .write(f) + memory + .write(GuestPtr::new(self.input_pointee_loc.ptr), f) .expect("input contents ref_mut"); } types::Reason::Traffic(v) => { - host_memory - .ptr(self.input_pointee_loc.ptr) - .write(v) + memory + .write(GuestPtr::new(self.input_pointee_loc.ptr), v) .expect("input contents ref_mut"); } types::Reason::Sleeping => {} // Do nothing } let e = variant_example::reason_mult( &mut ctx, - &host_memory, + &mut memory, self.input_loc.ptr as i32, self.multiply_by as i32, ) @@ -226,9 +230,8 @@ impl ReasonMultExercise { match self.input { types::Reason::DogAte(f) => { - let f_result: f32 = host_memory - .ptr(self.input_pointee_loc.ptr) - .read() + let f_result: f32 = memory + .read(GuestPtr::new(self.input_pointee_loc.ptr)) .expect("input contents ref_mut"); assert_eq!( mult_zero_nan(f, self.multiply_by), @@ -237,9 +240,8 @@ impl ReasonMultExercise { ) } types::Reason::Traffic(v) => { - let v_result: i32 = host_memory - .ptr(self.input_pointee_loc.ptr) - .read() + let v_result: i32 = memory + .read(GuestPtr::new(self.input_pointee_loc.ptr)) .expect("input contents ref_mut"); assert_eq!( mult_lose_overflow(v, self.multiply_by), diff --git a/crates/wiggle/tests/wasi.rs b/crates/wiggle/tests/wasi.rs index 6f0b4880ff3c..8056bda8ff0d 100644 --- a/crates/wiggle/tests/wasi.rs +++ b/crates/wiggle/tests/wasi.rs @@ -1,4 +1,4 @@ -use wiggle::{GuestErrorType, GuestPtr, GuestSlice}; +use wiggle::{GuestErrorType, GuestMemory, GuestPtr}; use wiggle_test::WasiCtx; // This test file exists to make sure that the entire `wasi.witx` file can be @@ -32,32 +32,49 @@ impl GuestErrorType for types::Errno { } impl<'a> crate::wasi_snapshot_preview1::WasiSnapshotPreview1 for WasiCtx<'a> { - fn args_get(&mut self, _argv: &GuestPtr>, _argv_buf: &GuestPtr) -> Result<()> { + fn args_get( + &mut self, + _memory: &mut GuestMemory<'_>, + _argv: GuestPtr>, + _argv_buf: GuestPtr, + ) -> Result<()> { unimplemented!("args_get") } - fn args_sizes_get(&mut self) -> Result<(types::Size, types::Size)> { + fn args_sizes_get( + &mut self, + _memory: &mut GuestMemory<'_>, + ) -> Result<(types::Size, types::Size)> { unimplemented!("args_sizes_get") } fn environ_get( &mut self, - _environ: &GuestPtr>, - _environ_buf: &GuestPtr, + _memory: &mut GuestMemory<'_>, + _environ: GuestPtr>, + _environ_buf: GuestPtr, ) -> Result<()> { unimplemented!("environ_get") } - fn environ_sizes_get(&mut self) -> Result<(types::Size, types::Size)> { + fn environ_sizes_get( + &mut self, + _memory: &mut GuestMemory<'_>, + ) -> Result<(types::Size, types::Size)> { unimplemented!("environ_sizes_get") } - fn clock_res_get(&mut self, _id: types::Clockid) -> Result { + fn clock_res_get( + &mut self, + _memory: &mut GuestMemory<'_>, + _id: types::Clockid, + ) -> Result { unimplemented!("clock_res_get") } fn clock_time_get( &mut self, + _memory: &mut GuestMemory<'_>, _id: types::Clockid, _precision: types::Timestamp, ) -> Result { @@ -66,6 +83,7 @@ impl<'a> crate::wasi_snapshot_preview1::WasiSnapshotPreview1 for WasiCtx<'a> { fn fd_advise( &mut self, + _memory: &mut GuestMemory<'_>, _fd: types::Fd, _offset: types::Filesize, _len: types::Filesize, @@ -76,6 +94,7 @@ impl<'a> crate::wasi_snapshot_preview1::WasiSnapshotPreview1 for WasiCtx<'a> { fn fd_allocate( &mut self, + _memory: &mut GuestMemory<'_>, _fd: types::Fd, _offset: types::Filesize, _len: types::Filesize, @@ -83,24 +102,34 @@ impl<'a> crate::wasi_snapshot_preview1::WasiSnapshotPreview1 for WasiCtx<'a> { unimplemented!("fd_allocate") } - fn fd_close(&mut self, _fd: types::Fd) -> Result<()> { + fn fd_close(&mut self, _memory: &mut GuestMemory<'_>, _fd: types::Fd) -> Result<()> { unimplemented!("fd_close") } - fn fd_datasync(&mut self, _fd: types::Fd) -> Result<()> { + fn fd_datasync(&mut self, _memory: &mut GuestMemory<'_>, _fd: types::Fd) -> Result<()> { unimplemented!("fd_datasync") } - fn fd_fdstat_get(&mut self, _fd: types::Fd) -> Result { + fn fd_fdstat_get( + &mut self, + _memory: &mut GuestMemory<'_>, + _fd: types::Fd, + ) -> Result { unimplemented!("fd_fdstat_get") } - fn fd_fdstat_set_flags(&mut self, _fd: types::Fd, _flags: types::Fdflags) -> Result<()> { + fn fd_fdstat_set_flags( + &mut self, + _memory: &mut GuestMemory<'_>, + _fd: types::Fd, + _flags: types::Fdflags, + ) -> Result<()> { unimplemented!("fd_fdstat_set_flags") } fn fd_fdstat_set_rights( &mut self, + _memory: &mut GuestMemory<'_>, _fd: types::Fd, _fs_rights_base: types::Rights, _fs_rights_inherting: types::Rights, @@ -108,16 +137,26 @@ impl<'a> crate::wasi_snapshot_preview1::WasiSnapshotPreview1 for WasiCtx<'a> { unimplemented!("fd_fdstat_set_rights") } - fn fd_filestat_get(&mut self, _fd: types::Fd) -> Result { + fn fd_filestat_get( + &mut self, + _memory: &mut GuestMemory<'_>, + _fd: types::Fd, + ) -> Result { unimplemented!("fd_filestat_get") } - fn fd_filestat_set_size(&mut self, _fd: types::Fd, _size: types::Filesize) -> Result<()> { + fn fd_filestat_set_size( + &mut self, + _memory: &mut GuestMemory<'_>, + _fd: types::Fd, + _size: types::Filesize, + ) -> Result<()> { unimplemented!("fd_filestat_set_size") } fn fd_filestat_set_times( &mut self, + _memory: &mut GuestMemory<'_>, _fd: types::Fd, _atim: types::Timestamp, _mtim: types::Timestamp, @@ -128,47 +167,27 @@ impl<'a> crate::wasi_snapshot_preview1::WasiSnapshotPreview1 for WasiCtx<'a> { fn fd_pread( &mut self, + _memory: &mut GuestMemory<'_>, _fd: types::Fd, - iovs: &types::IovecArray<'_>, + _iovs: types::IovecArray, _offset: types::Filesize, ) -> Result { - // This is not functional code, but the type annotations demonstrate - // that we can use the wiggle API to create the datastructures we want - // for efficient implementation of this function elsewhere. - - let mut slices: Vec> = Vec::new(); - for iov_ptr in iovs.iter() { - let iov_ptr = iov_ptr.expect("iovec element pointer is valid"); - - // Borrow checker will make sure the pointee of this read() doesn't overlap with any - // existing borrows: - let iov: types::Iovec = iov_ptr.read().expect("read iovec element"); - let base: GuestPtr = iov.buf; - let len: u32 = iov.buf_len; - let buf: GuestPtr<[u8]> = base.as_array(len); - // GuestSlice will remain borrowed until dropped: - let slice = buf - .as_slice() - .expect("borrow slice from iovec") - .expect("expected non-shared memory"); - slices.push(slice); - } - println!("iovec slices: ["); - for slice in slices { - println!(" {:?},", &*slice); - } - println!("]"); unimplemented!("fd_pread") } - fn fd_prestat_get(&mut self, _fd: types::Fd) -> Result { + fn fd_prestat_get( + &mut self, + _memory: &mut GuestMemory<'_>, + _fd: types::Fd, + ) -> Result { unimplemented!("fd_prestat_get") } fn fd_prestat_dir_name( &mut self, + _memory: &mut GuestMemory<'_>, _fd: types::Fd, - _path: &GuestPtr, + _path: GuestPtr, _path_len: types::Size, ) -> Result<()> { unimplemented!("fd_prestat_dir_name") @@ -176,33 +195,46 @@ impl<'a> crate::wasi_snapshot_preview1::WasiSnapshotPreview1 for WasiCtx<'a> { fn fd_pwrite( &mut self, + _memory: &mut GuestMemory<'_>, _fd: types::Fd, - _ciovs: &types::CiovecArray<'_>, + _ciovs: types::CiovecArray, _offset: types::Filesize, ) -> Result { unimplemented!("fd_pwrite") } - fn fd_read(&mut self, _fd: types::Fd, _iovs: &types::IovecArray<'_>) -> Result { + fn fd_read( + &mut self, + _memory: &mut GuestMemory<'_>, + _fd: types::Fd, + _iovs: types::IovecArray, + ) -> Result { unimplemented!("fd_read") } fn fd_readdir( &mut self, + _memory: &mut GuestMemory<'_>, _fd: types::Fd, - _buf: &GuestPtr, + _buf: GuestPtr, _buf_len: types::Size, _cookie: types::Dircookie, ) -> Result { unimplemented!("fd_readdir") } - fn fd_renumber(&mut self, _fd: types::Fd, _to: types::Fd) -> Result<()> { + fn fd_renumber( + &mut self, + _memory: &mut GuestMemory<'_>, + _fd: types::Fd, + _to: types::Fd, + ) -> Result<()> { unimplemented!("fd_renumber") } fn fd_seek( &mut self, + _memory: &mut GuestMemory<'_>, _fd: types::Fd, _offset: types::Filedelta, _whence: types::Whence, @@ -210,36 +242,52 @@ impl<'a> crate::wasi_snapshot_preview1::WasiSnapshotPreview1 for WasiCtx<'a> { unimplemented!("fd_seek") } - fn fd_sync(&mut self, _fd: types::Fd) -> Result<()> { + fn fd_sync(&mut self, _memory: &mut GuestMemory<'_>, _fd: types::Fd) -> Result<()> { unimplemented!("fd_sync") } - fn fd_tell(&mut self, _fd: types::Fd) -> Result { + fn fd_tell( + &mut self, + _memory: &mut GuestMemory<'_>, + _fd: types::Fd, + ) -> Result { unimplemented!("fd_tell") } - fn fd_write(&mut self, _fd: types::Fd, _ciovs: &types::CiovecArray<'_>) -> Result { + fn fd_write( + &mut self, + _memory: &mut GuestMemory<'_>, + _fd: types::Fd, + _ciovs: types::CiovecArray, + ) -> Result { unimplemented!("fd_write") } - fn path_create_directory(&mut self, _fd: types::Fd, _path: &GuestPtr<'_, str>) -> Result<()> { + fn path_create_directory( + &mut self, + _memory: &mut GuestMemory<'_>, + _fd: types::Fd, + _path: GuestPtr, + ) -> Result<()> { unimplemented!("path_create_directory") } fn path_filestat_get( &mut self, + _memory: &mut GuestMemory<'_>, _fd: types::Fd, _flags: types::Lookupflags, - _path: &GuestPtr<'_, str>, + _path: GuestPtr, ) -> Result { unimplemented!("path_filestat_get") } fn path_filestat_set_times( &mut self, + _memory: &mut GuestMemory<'_>, _fd: types::Fd, _flags: types::Lookupflags, - _path: &GuestPtr<'_, str>, + _path: GuestPtr, _atim: types::Timestamp, _mtim: types::Timestamp, _fst_flags: types::Fstflags, @@ -249,20 +297,22 @@ impl<'a> crate::wasi_snapshot_preview1::WasiSnapshotPreview1 for WasiCtx<'a> { fn path_link( &mut self, + _memory: &mut GuestMemory<'_>, _old_fd: types::Fd, _old_flags: types::Lookupflags, - _old_path: &GuestPtr<'_, str>, + _old_path: GuestPtr, _new_fd: types::Fd, - _new_path: &GuestPtr<'_, str>, + _new_path: GuestPtr, ) -> Result<()> { unimplemented!("path_link") } fn path_open( &mut self, + _memory: &mut GuestMemory<'_>, _fd: types::Fd, _dirflags: types::Lookupflags, - _path: &GuestPtr<'_, str>, + _path: GuestPtr, _oflags: types::Oflags, _fs_rights_base: types::Rights, _fs_rights_inherting: types::Rights, @@ -273,70 +323,94 @@ impl<'a> crate::wasi_snapshot_preview1::WasiSnapshotPreview1 for WasiCtx<'a> { fn path_readlink( &mut self, + _memory: &mut GuestMemory<'_>, _fd: types::Fd, - _path: &GuestPtr<'_, str>, - _buf: &GuestPtr, + _path: GuestPtr, + _buf: GuestPtr, _buf_len: types::Size, ) -> Result { unimplemented!("path_readlink") } - fn path_remove_directory(&mut self, _fd: types::Fd, _path: &GuestPtr<'_, str>) -> Result<()> { + fn path_remove_directory( + &mut self, + _memory: &mut GuestMemory<'_>, + _fd: types::Fd, + _path: GuestPtr, + ) -> Result<()> { unimplemented!("path_remove_directory") } fn path_rename( &mut self, + _memory: &mut GuestMemory<'_>, _fd: types::Fd, - _old_path: &GuestPtr<'_, str>, + _old_path: GuestPtr, _new_fd: types::Fd, - _new_path: &GuestPtr<'_, str>, + _new_path: GuestPtr, ) -> Result<()> { unimplemented!("path_rename") } fn path_symlink( &mut self, - _old_path: &GuestPtr<'_, str>, + _memory: &mut GuestMemory<'_>, + _old_path: GuestPtr, _fd: types::Fd, - _new_path: &GuestPtr<'_, str>, + _new_path: GuestPtr, ) -> Result<()> { unimplemented!("path_symlink") } - fn path_unlink_file(&mut self, _fd: types::Fd, _path: &GuestPtr<'_, str>) -> Result<()> { + fn path_unlink_file( + &mut self, + _memory: &mut GuestMemory<'_>, + _fd: types::Fd, + _path: GuestPtr, + ) -> Result<()> { unimplemented!("path_unlink_file") } fn poll_oneoff( &mut self, - _in_: &GuestPtr, - _out: &GuestPtr, + _memory: &mut GuestMemory<'_>, + _in_: GuestPtr, + _out: GuestPtr, _nsubscriptions: types::Size, ) -> Result { unimplemented!("poll_oneoff") } - fn proc_exit(&mut self, _rval: types::Exitcode) -> anyhow::Error { + fn proc_exit( + &mut self, + _memory: &mut GuestMemory<'_>, + _rval: types::Exitcode, + ) -> anyhow::Error { unimplemented!("proc_exit") } - fn proc_raise(&mut self, _sig: types::Signal) -> Result<()> { + fn proc_raise(&mut self, _memory: &mut GuestMemory<'_>, _sig: types::Signal) -> Result<()> { unimplemented!("proc_raise") } - fn sched_yield(&mut self) -> Result<()> { + fn sched_yield(&mut self, _memory: &mut GuestMemory<'_>) -> Result<()> { unimplemented!("sched_yield") } - fn random_get(&mut self, _buf: &GuestPtr, _buf_len: types::Size) -> Result<()> { + fn random_get( + &mut self, + _memory: &mut GuestMemory<'_>, + _buf: GuestPtr, + _buf_len: types::Size, + ) -> Result<()> { unimplemented!("random_get") } fn sock_recv( &mut self, + _memory: &mut GuestMemory<'_>, _fd: types::Fd, - _ri_data: &types::IovecArray<'_>, + _ri_data: types::IovecArray, _ri_flags: types::Riflags, ) -> Result<(types::Size, types::Roflags)> { unimplemented!("sock_recv") @@ -344,14 +418,20 @@ impl<'a> crate::wasi_snapshot_preview1::WasiSnapshotPreview1 for WasiCtx<'a> { fn sock_send( &mut self, + _memory: &mut GuestMemory<'_>, _fd: types::Fd, - _si_data: &types::CiovecArray<'_>, + _si_data: types::CiovecArray, _si_flags: types::Siflags, ) -> Result { unimplemented!("sock_send") } - fn sock_shutdown(&mut self, _fd: types::Fd, _how: types::Sdflags) -> Result<()> { + fn sock_shutdown( + &mut self, + _memory: &mut GuestMemory<'_>, + _fd: types::Fd, + _how: types::Sdflags, + ) -> Result<()> { unimplemented!("sock_shutdown") } } diff --git a/crates/wiggle/tests/wasmtime_async.rs b/crates/wiggle/tests/wasmtime_async.rs index 19209f9fc21e..6c5308c77aeb 100644 --- a/crates/wiggle/tests/wasmtime_async.rs +++ b/crates/wiggle/tests/wasmtime_async.rs @@ -1,4 +1,5 @@ use wasmtime::{Config, Engine, Linker, Module, Store, Val}; +use wiggle::GuestMemory; wiggle::from_witx!({ witx: ["$CARGO_MANIFEST_DIR/tests/atoms.witx"], @@ -16,12 +17,18 @@ impl wiggle::GuestErrorType for types::Errno { #[wiggle::async_trait] impl atoms::Atoms for Ctx { - fn int_float_args(&mut self, an_int: u32, an_float: f32) -> Result<(), types::Errno> { + fn int_float_args( + &mut self, + _: &mut GuestMemory<'_>, + an_int: u32, + an_float: f32, + ) -> Result<(), types::Errno> { println!("INT FLOAT ARGS: {} {}", an_int, an_float); Ok(()) } async fn double_int_return_float( &mut self, + _: &mut GuestMemory<'_>, an_int: u32, ) -> Result { // Do something inside this test that is Pending for a trivial amount of time, diff --git a/crates/wiggle/tests/wasmtime_integration.rs b/crates/wiggle/tests/wasmtime_integration.rs index 8617c6d757cc..c2c4552578b5 100644 --- a/crates/wiggle/tests/wasmtime_integration.rs +++ b/crates/wiggle/tests/wasmtime_integration.rs @@ -1,4 +1,5 @@ use wasmtime::{Engine, Linker, Module, Store, Val}; +use wiggle::GuestMemory; // from_witx invocation says the func is async. This context doesn't support async! wiggle::from_witx!({ @@ -28,12 +29,18 @@ impl wiggle::GuestErrorType for types::Errno { #[wiggle::async_trait] impl atoms::Atoms for Ctx { - fn int_float_args(&mut self, an_int: u32, an_float: f32) -> Result<(), types::Errno> { + fn int_float_args( + &mut self, + _: &mut GuestMemory<'_>, + an_int: u32, + an_float: f32, + ) -> Result<(), types::Errno> { println!("INT FLOAT ARGS: {} {}", an_int, an_float); Ok(()) } async fn double_int_return_float( &mut self, + _: &mut GuestMemory<'_>, an_int: u32, ) -> Result { Ok((an_int as f32) * 2.0) diff --git a/crates/wiggle/tests/wasmtime_sync.rs b/crates/wiggle/tests/wasmtime_sync.rs index 43c303286bc3..1b8037fa5c1f 100644 --- a/crates/wiggle/tests/wasmtime_sync.rs +++ b/crates/wiggle/tests/wasmtime_sync.rs @@ -1,4 +1,5 @@ use wasmtime::{Engine, Linker, Module, Store, Val}; +use wiggle::GuestMemory; wiggle::from_witx!({ witx: ["$CARGO_MANIFEST_DIR/tests/atoms.witx"], @@ -18,12 +19,18 @@ const TRIGGER_PENDING: u32 = 0; #[wiggle::async_trait] impl atoms::Atoms for Ctx { - fn int_float_args(&mut self, an_int: u32, an_float: f32) -> Result<(), types::Errno> { + fn int_float_args( + &mut self, + _: &mut GuestMemory<'_>, + an_int: u32, + an_float: f32, + ) -> Result<(), types::Errno> { println!("INT FLOAT ARGS: {} {}", an_int, an_float); Ok(()) } async fn double_int_return_float( &mut self, + _: &mut GuestMemory<'_>, an_int: u32, ) -> Result { if an_int == TRIGGER_PENDING {