wiggle: adapt Wiggle guest slices for unsafe shared use (#5229)

* wiggle: adapt Wiggle guest slices for `unsafe` shared use

When multiple threads can concurrently modify a WebAssembly shared
memory, the underlying data for a Wiggle `GuestSlice` and
`GuestSliceMut` could change due to access from other threads. This
breaks Rust guarantees when `&[T]` and `&mut [T]` slices are handed out.
This change modifies `GuestPtr` to make `as_slice` and `as_slice_mut`
return an `Option` which is `None` when the underlying WebAssembly
memory is shared.

But WASI implementations still need access to the underlying WebAssembly
memory, both to read to it and write from it. This change adds new APIs:
- `GuestPtr::to_vec` copies the  bytes from WebAssembly memory (from
  which we can safely take a `&[T]`)
- `GuestPtr::as_unsafe_slice_mut` returns a wrapper `struct` from which
  we can  `unsafe`-ly return a mutable slice (users must accept the
  unsafety of concurrently modifying a `&mut [T]`)

This approach allows us to maintain Wiggle's borrow-checking
infrastructure, which enforces the guarantee that Wiggle will not modify
overlapping regions, e.g. This is important because the underlying
system calls may expect this. Though other threads may modify the same
underlying region, this is impossible to prevent; at least Wiggle will
not be able to do so.

Finally, the changes to Wiggle's API are propagated to all WASI
implementations in Wasmtime. For now, code locations that attempt to get
a guest slice will panic if the underlying memory is shared. Note that
Wiggle is not enabled for shared memory (that will come later in
something like #5054), but when it is, these panics will be clear
indicators of locations that must be re-implemented in a thread-safe
way.

* review: remove double cast

* review: refactor to include more logic in 'UnsafeGuestSlice'

* review: add reference to #4203

* review: link all thread-safe WASI fixups to #5235

* fix: consume 'UnsafeGuestSlice' during conversion to safe versions

* review: remove 'as_slice' and 'as_slice_mut'

* review: use 'as_unsafe_slice_mut' in 'to_vec'

* review: add `UnsafeBorrowResult`
This commit is contained in:
Andrew Brown
2022-11-10 13:54:52 -08:00
committed by GitHub
parent 0548952319
commit 7717d8fa55
11 changed files with 446 additions and 148 deletions

View File

@@ -468,14 +468,16 @@ impl wasi_unstable::WasiUnstable for WasiCtx {
.get_file_mut(u32::from(fd))? .get_file_mut(u32::from(fd))?
.get_cap_mut(FileCaps::READ)?; .get_cap_mut(FileCaps::READ)?;
let mut guest_slices: Vec<wiggle::GuestSliceMut<u8>> = iovs let mut guest_slices: Vec<wiggle::GuestSliceMut<u8>> =
.iter() iovs.iter()
.map(|iov_ptr| { .map(|iov_ptr| {
let iov_ptr = iov_ptr?; let iov_ptr = iov_ptr?;
let iov: types::Iovec = iov_ptr.read()?; let iov: types::Iovec = iov_ptr.read()?;
Ok(iov.buf.as_array(iov.buf_len).as_slice_mut()?) Ok(iov.buf.as_array(iov.buf_len).as_slice_mut()?.expect(
}) "cannot use with shared memories; see https://github.com/bytecodealliance/wasmtime/issues/5235 (TODO)",
.collect::<Result<_, Error>>()?; ))
})
.collect::<Result<_, Error>>()?;
let mut ioslices: Vec<IoSliceMut> = guest_slices let mut ioslices: Vec<IoSliceMut> = guest_slices
.iter_mut() .iter_mut()
@@ -497,14 +499,16 @@ impl wasi_unstable::WasiUnstable for WasiCtx {
.get_file_mut(u32::from(fd))? .get_file_mut(u32::from(fd))?
.get_cap_mut(FileCaps::READ | FileCaps::SEEK)?; .get_cap_mut(FileCaps::READ | FileCaps::SEEK)?;
let mut guest_slices: Vec<wiggle::GuestSliceMut<u8>> = iovs let mut guest_slices: Vec<wiggle::GuestSliceMut<u8>> =
.iter() iovs.iter()
.map(|iov_ptr| { .map(|iov_ptr| {
let iov_ptr = iov_ptr?; let iov_ptr = iov_ptr?;
let iov: types::Iovec = iov_ptr.read()?; let iov: types::Iovec = iov_ptr.read()?;
Ok(iov.buf.as_array(iov.buf_len).as_slice_mut()?) Ok(iov.buf.as_array(iov.buf_len).as_slice_mut()?.expect(
}) "cannot use with shared memories; see https://github.com/bytecodealliance/wasmtime/issues/5235 (TODO)",
.collect::<Result<_, Error>>()?; ))
})
.collect::<Result<_, Error>>()?;
let mut ioslices: Vec<IoSliceMut> = guest_slices let mut ioslices: Vec<IoSliceMut> = guest_slices
.iter_mut() .iter_mut()
@@ -530,7 +534,11 @@ impl wasi_unstable::WasiUnstable for WasiCtx {
.map(|iov_ptr| { .map(|iov_ptr| {
let iov_ptr = iov_ptr?; let iov_ptr = iov_ptr?;
let iov: types::Ciovec = iov_ptr.read()?; let iov: types::Ciovec = iov_ptr.read()?;
Ok(iov.buf.as_array(iov.buf_len).as_slice()?) Ok(iov
.buf
.as_array(iov.buf_len)
.as_slice()?
.expect("cannot use with shared memories; see https://github.com/bytecodealliance/wasmtime/issues/5235 (TODO)"))
}) })
.collect::<Result<_, Error>>()?; .collect::<Result<_, Error>>()?;
@@ -559,7 +567,11 @@ impl wasi_unstable::WasiUnstable for WasiCtx {
.map(|iov_ptr| { .map(|iov_ptr| {
let iov_ptr = iov_ptr?; let iov_ptr = iov_ptr?;
let iov: types::Ciovec = iov_ptr.read()?; let iov: types::Ciovec = iov_ptr.read()?;
Ok(iov.buf.as_array(iov.buf_len).as_slice()?) Ok(iov
.buf
.as_array(iov.buf_len)
.as_slice()?
.expect("cannot use with shared memories; see https://github.com/bytecodealliance/wasmtime/issues/5235 (TODO)"))
}) })
.collect::<Result<_, Error>>()?; .collect::<Result<_, Error>>()?;

View File

@@ -521,14 +521,16 @@ impl wasi_snapshot_preview1::WasiSnapshotPreview1 for WasiCtx {
.get_file_mut(u32::from(fd))? .get_file_mut(u32::from(fd))?
.get_cap_mut(FileCaps::READ)?; .get_cap_mut(FileCaps::READ)?;
let mut guest_slices: Vec<wiggle::GuestSliceMut<u8>> = iovs let mut guest_slices: Vec<wiggle::GuestSliceMut<u8>> =
.iter() iovs.iter()
.map(|iov_ptr| { .map(|iov_ptr| {
let iov_ptr = iov_ptr?; let iov_ptr = iov_ptr?;
let iov: types::Iovec = iov_ptr.read()?; let iov: types::Iovec = iov_ptr.read()?;
Ok(iov.buf.as_array(iov.buf_len).as_slice_mut()?) Ok(iov.buf.as_array(iov.buf_len).as_slice_mut()?.expect(
}) "cannot use with shared memories; see https://github.com/bytecodealliance/wasmtime/issues/5235 (TODO)",
.collect::<Result<_, Error>>()?; ))
})
.collect::<Result<_, Error>>()?;
let mut ioslices: Vec<IoSliceMut> = guest_slices let mut ioslices: Vec<IoSliceMut> = guest_slices
.iter_mut() .iter_mut()
@@ -550,14 +552,16 @@ impl wasi_snapshot_preview1::WasiSnapshotPreview1 for WasiCtx {
.get_file_mut(u32::from(fd))? .get_file_mut(u32::from(fd))?
.get_cap_mut(FileCaps::READ | FileCaps::SEEK)?; .get_cap_mut(FileCaps::READ | FileCaps::SEEK)?;
let mut guest_slices: Vec<wiggle::GuestSliceMut<u8>> = iovs let mut guest_slices: Vec<wiggle::GuestSliceMut<u8>> =
.iter() iovs.iter()
.map(|iov_ptr| { .map(|iov_ptr| {
let iov_ptr = iov_ptr?; let iov_ptr = iov_ptr?;
let iov: types::Iovec = iov_ptr.read()?; let iov: types::Iovec = iov_ptr.read()?;
Ok(iov.buf.as_array(iov.buf_len).as_slice_mut()?) Ok(iov.buf.as_array(iov.buf_len).as_slice_mut()?.expect(
}) "cannot use with shared memories; see https://github.com/bytecodealliance/wasmtime/issues/5235 (TODO)",
.collect::<Result<_, Error>>()?; ))
})
.collect::<Result<_, Error>>()?;
let mut ioslices: Vec<IoSliceMut> = guest_slices let mut ioslices: Vec<IoSliceMut> = guest_slices
.iter_mut() .iter_mut()
@@ -583,7 +587,11 @@ impl wasi_snapshot_preview1::WasiSnapshotPreview1 for WasiCtx {
.map(|iov_ptr| { .map(|iov_ptr| {
let iov_ptr = iov_ptr?; let iov_ptr = iov_ptr?;
let iov: types::Ciovec = iov_ptr.read()?; let iov: types::Ciovec = iov_ptr.read()?;
Ok(iov.buf.as_array(iov.buf_len).as_slice()?) Ok(iov
.buf
.as_array(iov.buf_len)
.as_slice()?
.expect("cannot use with shared memories; see https://github.com/bytecodealliance/wasmtime/issues/5235 (TODO)"))
}) })
.collect::<Result<_, Error>>()?; .collect::<Result<_, Error>>()?;
@@ -612,7 +620,11 @@ impl wasi_snapshot_preview1::WasiSnapshotPreview1 for WasiCtx {
.map(|iov_ptr| { .map(|iov_ptr| {
let iov_ptr = iov_ptr?; let iov_ptr = iov_ptr?;
let iov: types::Ciovec = iov_ptr.read()?; let iov: types::Ciovec = iov_ptr.read()?;
Ok(iov.buf.as_array(iov.buf_len).as_slice()?) Ok(iov
.buf
.as_array(iov.buf_len)
.as_slice()?
.expect("cannot use with shared memories; see https://github.com/bytecodealliance/wasmtime/issues/5235 (TODO)"))
}) })
.collect::<Result<_, Error>>()?; .collect::<Result<_, Error>>()?;
@@ -654,7 +666,10 @@ impl wasi_snapshot_preview1::WasiSnapshotPreview1 for WasiCtx {
if path_len < path_max_len as usize { if path_len < path_max_len as usize {
return Err(Error::name_too_long()); return Err(Error::name_too_long());
} }
let mut p_memory = path.as_array(path_len as u32).as_slice_mut()?; let mut p_memory = path
.as_array(path_len as u32)
.as_slice_mut()?
.expect("cannot use with shared memories; see https://github.com/bytecodealliance/wasmtime/issues/5235 (TODO)");
p_memory.copy_from_slice(path_bytes); p_memory.copy_from_slice(path_bytes);
Ok(()) Ok(())
} else { } else {
@@ -948,7 +963,10 @@ impl wasi_snapshot_preview1::WasiSnapshotPreview1 for WasiCtx {
if link_len > buf_len as usize { if link_len > buf_len as usize {
return Err(Error::range()); return Err(Error::range());
} }
let mut buf = buf.as_array(link_len as u32).as_slice_mut()?; let mut buf = buf
.as_array(link_len as u32)
.as_slice_mut()?
.expect("cannot use with shared memories; see https://github.com/bytecodealliance/wasmtime/issues/5235 (TODO)");
buf.copy_from_slice(link_bytes); buf.copy_from_slice(link_bytes);
Ok(link_len as types::Size) Ok(link_len as types::Size)
} }
@@ -1236,7 +1254,10 @@ impl wasi_snapshot_preview1::WasiSnapshotPreview1 for WasiCtx {
buf: &GuestPtr<'a, u8>, buf: &GuestPtr<'a, u8>,
buf_len: types::Size, buf_len: types::Size,
) -> Result<(), Error> { ) -> Result<(), Error> {
let mut buf = buf.as_array(buf_len).as_slice_mut()?; let mut buf = buf
.as_array(buf_len)
.as_slice_mut()?
.expect("cannot use with shared memories; see https://github.com/bytecodealliance/wasmtime/issues/5235 (TODO)");
self.random.try_fill_bytes(buf.deref_mut())?; self.random.try_fill_bytes(buf.deref_mut())?;
Ok(()) Ok(())
} }
@@ -1273,14 +1294,17 @@ impl wasi_snapshot_preview1::WasiSnapshotPreview1 for WasiCtx {
.get_file_mut(u32::from(fd))? .get_file_mut(u32::from(fd))?
.get_cap_mut(FileCaps::READ)?; .get_cap_mut(FileCaps::READ)?;
let mut guest_slices: Vec<wiggle::GuestSliceMut<u8>> = ri_data let mut guest_slices: Vec<wiggle::GuestSliceMut<u8>> =
.iter() ri_data
.map(|iov_ptr| { .iter()
let iov_ptr = iov_ptr?; .map(|iov_ptr| {
let iov: types::Iovec = iov_ptr.read()?; let iov_ptr = iov_ptr?;
Ok(iov.buf.as_array(iov.buf_len).as_slice_mut()?) let iov: types::Iovec = iov_ptr.read()?;
}) Ok(iov.buf.as_array(iov.buf_len).as_slice_mut()?.expect(
.collect::<Result<_, Error>>()?; "cannot use with shared memories; see https://github.com/bytecodealliance/wasmtime/issues/5235 (TODO)",
))
})
.collect::<Result<_, Error>>()?;
let mut ioslices: Vec<IoSliceMut> = guest_slices let mut ioslices: Vec<IoSliceMut> = guest_slices
.iter_mut() .iter_mut()
@@ -1307,7 +1331,11 @@ impl wasi_snapshot_preview1::WasiSnapshotPreview1 for WasiCtx {
.map(|iov_ptr| { .map(|iov_ptr| {
let iov_ptr = iov_ptr?; let iov_ptr = iov_ptr?;
let iov: types::Ciovec = iov_ptr.read()?; let iov: types::Ciovec = iov_ptr.read()?;
Ok(iov.buf.as_array(iov.buf_len).as_slice()?) Ok(iov
.buf
.as_array(iov.buf_len)
.as_slice()?
.expect("cannot use with shared memories; see https://github.com/bytecodealliance/wasmtime/issues/5235 (TODO)"))
}) })
.collect::<Result<_, Error>>()?; .collect::<Result<_, Error>>()?;

View File

@@ -39,7 +39,10 @@ impl super::wasi_ephemeral_crypto_asymmetric_common::WasiEphemeralCryptoAsymmetr
kp_id_ptr: &wiggle::GuestPtr<'_, u8>, kp_id_ptr: &wiggle::GuestPtr<'_, u8>,
kp_id_max_len: guest_types::Size, kp_id_max_len: guest_types::Size,
) -> Result<(), guest_types::CryptoErrno> { ) -> Result<(), guest_types::CryptoErrno> {
let key_id_buf = &mut *kp_id_ptr.as_array(kp_id_max_len).as_slice_mut()?; let key_id_buf = &mut *kp_id_ptr
.as_array(kp_id_max_len)
.as_slice_mut()?
.expect("cannot use with shared memories; see https://github.com/bytecodealliance/wasmtime/issues/5235 (TODO)");
Ok((&*self).keypair_store_managed( Ok((&*self).keypair_store_managed(
secrets_manager_handle.into(), secrets_manager_handle.into(),
kp_handle.into(), kp_handle.into(),
@@ -69,7 +72,10 @@ impl super::wasi_ephemeral_crypto_asymmetric_common::WasiEphemeralCryptoAsymmetr
kp_id_len: guest_types::Size, kp_id_len: guest_types::Size,
kp_version: guest_types::Version, kp_version: guest_types::Version,
) -> Result<guest_types::Keypair, guest_types::CryptoErrno> { ) -> Result<guest_types::Keypair, guest_types::CryptoErrno> {
let kp_id = &*kp_id_ptr.as_array(kp_id_len).as_slice()?; let kp_id = &*kp_id_ptr
.as_array(kp_id_len)
.as_slice()?
.expect("cannot use with shared memories; see https://github.com/bytecodealliance/wasmtime/issues/5235 (TODO)");
Ok((&*self) Ok((&*self)
.keypair_from_id(secrets_manager_handle.into(), kp_id, Version(kp_version))? .keypair_from_id(secrets_manager_handle.into(), kp_id, Version(kp_version))?
.into()) .into())
@@ -102,7 +108,10 @@ impl super::wasi_ephemeral_crypto_asymmetric_common::WasiEphemeralCryptoAsymmetr
encoding: guest_types::KeypairEncoding, encoding: guest_types::KeypairEncoding,
) -> Result<guest_types::Keypair, guest_types::CryptoErrno> { ) -> Result<guest_types::Keypair, guest_types::CryptoErrno> {
let alg_str = &*alg_str.as_str()?; let alg_str = &*alg_str.as_str()?;
let encoded = &*encoded_ptr.as_array(encoded_len).as_slice()?; let encoded = &*encoded_ptr
.as_array(encoded_len)
.as_slice()?
.expect("cannot use with shared memories; see https://github.com/bytecodealliance/wasmtime/issues/5235 (TODO)");
Ok((&*self) Ok((&*self)
.keypair_import(alg_type.into(), alg_str, encoded, encoding.into())? .keypair_import(alg_type.into(), alg_str, encoded, encoding.into())?
.into()) .into())
@@ -114,7 +123,10 @@ impl super::wasi_ephemeral_crypto_asymmetric_common::WasiEphemeralCryptoAsymmetr
kp_id_ptr: &wiggle::GuestPtr<'_, u8>, kp_id_ptr: &wiggle::GuestPtr<'_, u8>,
kp_id_max_len: guest_types::Size, kp_id_max_len: guest_types::Size,
) -> Result<(guest_types::Size, guest_types::Version), guest_types::CryptoErrno> { ) -> Result<(guest_types::Size, guest_types::Version), guest_types::CryptoErrno> {
let kp_id_buf = &mut *kp_id_ptr.as_array(kp_id_max_len as _).as_slice_mut()?; let kp_id_buf = &mut *kp_id_ptr
.as_array(kp_id_max_len as _)
.as_slice_mut()?
.expect("cannot use with shared memories; see https://github.com/bytecodealliance/wasmtime/issues/5235 (TODO)");
let (kp_id, version) = (&*self).keypair_id(kp_handle.into())?; let (kp_id, version) = (&*self).keypair_id(kp_handle.into())?;
ensure!(kp_id.len() <= kp_id_buf.len(), CryptoError::Overflow.into()); ensure!(kp_id.len() <= kp_id_buf.len(), CryptoError::Overflow.into());
kp_id_buf.copy_from_slice(&kp_id); kp_id_buf.copy_from_slice(&kp_id);
@@ -156,7 +168,10 @@ impl super::wasi_ephemeral_crypto_asymmetric_common::WasiEphemeralCryptoAsymmetr
encoding: guest_types::PublickeyEncoding, encoding: guest_types::PublickeyEncoding,
) -> Result<guest_types::Publickey, guest_types::CryptoErrno> { ) -> Result<guest_types::Publickey, guest_types::CryptoErrno> {
let alg_str = &*alg_str.as_str()?; let alg_str = &*alg_str.as_str()?;
let encoded = &*encoded_ptr.as_array(encoded_len).as_slice()?; let encoded = &*encoded_ptr
.as_array(encoded_len)
.as_slice()?
.expect("cannot use with shared memories; see https://github.com/bytecodealliance/wasmtime/issues/5235 (TODO)");
Ok((&*self) Ok((&*self)
.publickey_import(alg_type.into(), alg_str, encoded, encoding.into())? .publickey_import(alg_type.into(), alg_str, encoded, encoding.into())?
.into()) .into())
@@ -204,7 +219,10 @@ impl super::wasi_ephemeral_crypto_asymmetric_common::WasiEphemeralCryptoAsymmetr
encoding: guest_types::SecretkeyEncoding, encoding: guest_types::SecretkeyEncoding,
) -> Result<guest_types::Secretkey, guest_types::CryptoErrno> { ) -> Result<guest_types::Secretkey, guest_types::CryptoErrno> {
let alg_str = &*alg_str.as_str()?; let alg_str = &*alg_str.as_str()?;
let encoded = &*encoded_ptr.as_array(encoded_len).as_slice()?; let encoded = &*encoded_ptr
.as_array(encoded_len)
.as_slice()?
.expect("cannot use with shared memories; see https://github.com/bytecodealliance/wasmtime/issues/5235 (TODO)");
Ok((&*self) Ok((&*self)
.secretkey_import(alg_type.into(), alg_str, encoded, encoding.into())? .secretkey_import(alg_type.into(), alg_str, encoded, encoding.into())?
.into()) .into())

View File

@@ -28,7 +28,12 @@ impl super::wasi_ephemeral_crypto_common::WasiEphemeralCryptoCommon for WasiCryp
value_len: guest_types::Size, value_len: guest_types::Size,
) -> Result<(), guest_types::CryptoErrno> { ) -> Result<(), guest_types::CryptoErrno> {
let name_str: &str = &*name_str.as_str()?; let name_str: &str = &*name_str.as_str()?;
let value: &[u8] = { &*value_ptr.as_array(value_len).as_slice()? }; let value: &[u8] = {
&*value_ptr
.as_array(value_len)
.as_slice()?
.expect("cannot use with shared memories; see https://github.com/bytecodealliance/wasmtime/issues/5235 (TODO)")
};
Ok((&*self).options_set(options_handle.into(), name_str, value)?) Ok((&*self).options_set(options_handle.into(), name_str, value)?)
} }
@@ -40,8 +45,14 @@ impl super::wasi_ephemeral_crypto_common::WasiEphemeralCryptoCommon for WasiCryp
buffer_len: guest_types::Size, buffer_len: guest_types::Size,
) -> Result<(), guest_types::CryptoErrno> { ) -> Result<(), guest_types::CryptoErrno> {
let name_str: &str = &*name_str.as_str()?; let name_str: &str = &*name_str.as_str()?;
let buffer: &'static mut [u8] = let buffer: &'static mut [u8] = unsafe {
unsafe { std::mem::transmute(&mut *buffer_ptr.as_array(buffer_len).as_slice_mut()?) }; std::mem::transmute(
&mut *buffer_ptr
.as_array(buffer_len)
.as_slice_mut()?
.expect("cannot use with shared memories; see https://github.com/bytecodealliance/wasmtime/issues/5235 (TODO)"),
)
};
Ok((&*self).options_set_guest_buffer(options_handle.into(), name_str, buffer)?) Ok((&*self).options_set_guest_buffer(options_handle.into(), name_str, buffer)?)
} }
@@ -72,7 +83,12 @@ impl super::wasi_ephemeral_crypto_common::WasiEphemeralCryptoCommon for WasiCryp
buf_ptr: &wiggle::GuestPtr<'_, u8>, buf_ptr: &wiggle::GuestPtr<'_, u8>,
buf_len: guest_types::Size, buf_len: guest_types::Size,
) -> Result<guest_types::Size, guest_types::CryptoErrno> { ) -> Result<guest_types::Size, guest_types::CryptoErrno> {
let buf: &mut [u8] = { &mut *buf_ptr.as_array(buf_len).as_slice_mut()? }; let buf: &mut [u8] = {
&mut *buf_ptr
.as_array(buf_len)
.as_slice_mut()?
.expect("cannot use with shared memories; see https://github.com/bytecodealliance/wasmtime/issues/5235 (TODO)")
};
Ok((&*self) Ok((&*self)
.array_output_pull(array_output_handle.into(), buf)? .array_output_pull(array_output_handle.into(), buf)?
.try_into()?) .try_into()?)
@@ -107,7 +123,12 @@ impl super::wasi_ephemeral_crypto_common::WasiEphemeralCryptoCommon for WasiCryp
key_id_len: guest_types::Size, key_id_len: guest_types::Size,
key_version: guest_types::Version, key_version: guest_types::Version,
) -> Result<(), guest_types::CryptoErrno> { ) -> Result<(), guest_types::CryptoErrno> {
let key_id: &[u8] = { &*key_id_ptr.as_array(key_id_len).as_slice()? }; let key_id: &[u8] = {
&*key_id_ptr
.as_array(key_id_len)
.as_slice()?
.expect("cannot use with shared memories; see https://github.com/bytecodealliance/wasmtime/issues/5235 (TODO)")
};
Ok((&*self).secrets_manager_invalidate( Ok((&*self).secrets_manager_invalidate(
secrets_manager_handle.into(), secrets_manager_handle.into(),
key_id, key_id,

View File

@@ -31,7 +31,8 @@ impl super::wasi_ephemeral_crypto_kx::WasiEphemeralCryptoKx for WasiCryptoCtx {
) -> Result<guest_types::ArrayOutput, guest_types::CryptoErrno> { ) -> Result<guest_types::ArrayOutput, guest_types::CryptoErrno> {
let encapsulated_secret = &*encapsulated_secret_ptr let encapsulated_secret = &*encapsulated_secret_ptr
.as_array(encapsulated_secret_len) .as_array(encapsulated_secret_len)
.as_slice()?; .as_slice()?
.expect("cannot use with shared memories; see https://github.com/bytecodealliance/wasmtime/issues/5235 (TODO)");
Ok((&*self) Ok((&*self)
.kx_decapsulate(sk_handle.into(), encapsulated_secret)? .kx_decapsulate(sk_handle.into(), encapsulated_secret)?
.into()) .into())

View File

@@ -23,7 +23,10 @@ impl super::wasi_ephemeral_crypto_signatures::WasiEphemeralCryptoSignatures for
encoding: guest_types::SignatureEncoding, encoding: guest_types::SignatureEncoding,
) -> Result<guest_types::Signature, guest_types::CryptoErrno> { ) -> Result<guest_types::Signature, guest_types::CryptoErrno> {
let alg_str = &*alg_str.as_str()?; let alg_str = &*alg_str.as_str()?;
let encoded = &*encoded_ptr.as_array(encoded_len).as_slice()?; let encoded = &*encoded_ptr
.as_array(encoded_len)
.as_slice()?
.expect("cannot use with shared memories; see https://github.com/bytecodealliance/wasmtime/issues/5235 (TODO)");
Ok((&*self) Ok((&*self)
.signature_import(alg_str, encoded, encoding.into())? .signature_import(alg_str, encoded, encoding.into())?
.into()) .into())
@@ -42,7 +45,10 @@ impl super::wasi_ephemeral_crypto_signatures::WasiEphemeralCryptoSignatures for
input_ptr: &wiggle::GuestPtr<'_, u8>, input_ptr: &wiggle::GuestPtr<'_, u8>,
input_len: guest_types::Size, input_len: guest_types::Size,
) -> Result<(), guest_types::CryptoErrno> { ) -> Result<(), guest_types::CryptoErrno> {
let input = &*input_ptr.as_array(input_len).as_slice()?; let input = &*input_ptr
.as_array(input_len)
.as_slice()?
.expect("cannot use with shared memories; see https://github.com/bytecodealliance/wasmtime/issues/5235 (TODO)");
Ok((&*self).signature_state_update(state_handle.into(), input)?) Ok((&*self).signature_state_update(state_handle.into(), input)?)
} }
@@ -77,7 +83,10 @@ impl super::wasi_ephemeral_crypto_signatures::WasiEphemeralCryptoSignatures for
input_ptr: &wiggle::GuestPtr<'_, u8>, input_ptr: &wiggle::GuestPtr<'_, u8>,
input_len: guest_types::Size, input_len: guest_types::Size,
) -> Result<(), guest_types::CryptoErrno> { ) -> Result<(), guest_types::CryptoErrno> {
let input: &[u8] = &*input_ptr.as_array(input_len).as_slice()?; let input: &[u8] = &*input_ptr
.as_array(input_len)
.as_slice()?
.expect("cannot use with shared memories; see https://github.com/bytecodealliance/wasmtime/issues/5235 (TODO)");
Ok( Ok(
(&*self) (&*self)
.signature_verification_state_update(verification_state_handle.into(), input)?, .signature_verification_state_update(verification_state_handle.into(), input)?,

View File

@@ -35,7 +35,8 @@ impl super::wasi_ephemeral_crypto_symmetric::WasiEphemeralCryptoSymmetric for Wa
) -> Result<(), guest_types::CryptoErrno> { ) -> Result<(), guest_types::CryptoErrno> {
let key_id_buf = &mut *symmetric_key_id_ptr let key_id_buf = &mut *symmetric_key_id_ptr
.as_array(symmetric_key_id_max_len) .as_array(symmetric_key_id_max_len)
.as_slice_mut()?; .as_slice_mut()?
.expect("cannot use with shared memories; see https://github.com/bytecodealliance/wasmtime/issues/5235 (TODO)");
Ok((&*self).symmetric_key_store_managed( Ok((&*self).symmetric_key_store_managed(
secrets_manager_handle.into(), secrets_manager_handle.into(),
symmetric_key_handle.into(), symmetric_key_handle.into(),
@@ -67,7 +68,8 @@ impl super::wasi_ephemeral_crypto_symmetric::WasiEphemeralCryptoSymmetric for Wa
) -> Result<guest_types::SymmetricKey, guest_types::CryptoErrno> { ) -> Result<guest_types::SymmetricKey, guest_types::CryptoErrno> {
let symmetric_key_id = &*symmetric_key_id_ptr let symmetric_key_id = &*symmetric_key_id_ptr
.as_array(symmetric_key_id_len) .as_array(symmetric_key_id_len)
.as_slice()?; .as_slice()?
.expect("cannot use with shared memories; see https://github.com/bytecodealliance/wasmtime/issues/5235 (TODO)");
Ok((&*self) Ok((&*self)
.symmetric_key_from_id( .symmetric_key_from_id(
secrets_manager_handle.into(), secrets_manager_handle.into(),
@@ -101,7 +103,10 @@ impl super::wasi_ephemeral_crypto_symmetric::WasiEphemeralCryptoSymmetric for Wa
raw_len: guest_types::Size, raw_len: guest_types::Size,
) -> Result<guest_types::SymmetricKey, guest_types::CryptoErrno> { ) -> Result<guest_types::SymmetricKey, guest_types::CryptoErrno> {
let alg_str = &*alg_str.as_str()?; let alg_str = &*alg_str.as_str()?;
let raw = &*raw_ptr.as_array(raw_len).as_slice()?; let raw = &*raw_ptr
.as_array(raw_len)
.as_slice()?
.expect("cannot use with shared memories; see https://github.com/bytecodealliance/wasmtime/issues/5235 (TODO)");
Ok((&*self).symmetric_key_import(alg_str, raw)?.into()) Ok((&*self).symmetric_key_import(alg_str, raw)?.into())
} }
@@ -122,7 +127,8 @@ impl super::wasi_ephemeral_crypto_symmetric::WasiEphemeralCryptoSymmetric for Wa
) -> Result<(guest_types::Size, guest_types::Version), guest_types::CryptoErrno> { ) -> Result<(guest_types::Size, guest_types::Version), guest_types::CryptoErrno> {
let key_id_buf = &mut *symmetric_key_id_ptr let key_id_buf = &mut *symmetric_key_id_ptr
.as_array(symmetric_key_id_max_len) .as_array(symmetric_key_id_max_len)
.as_slice_mut()?; .as_slice_mut()?
.expect("cannot use with shared memories; see https://github.com/bytecodealliance/wasmtime/issues/5235 (TODO)");
let (key_id, version) = (&*self).symmetric_key_id(symmetric_key_handle.into())?; let (key_id, version) = (&*self).symmetric_key_id(symmetric_key_handle.into())?;
ensure!( ensure!(
key_id.len() <= key_id_buf.len(), key_id.len() <= key_id_buf.len(),
@@ -173,7 +179,10 @@ impl super::wasi_ephemeral_crypto_symmetric::WasiEphemeralCryptoSymmetric for Wa
value_max_len: guest_types::Size, value_max_len: guest_types::Size,
) -> Result<guest_types::Size, guest_types::CryptoErrno> { ) -> Result<guest_types::Size, guest_types::CryptoErrno> {
let name_str: &str = &*name_str.as_str()?; let name_str: &str = &*name_str.as_str()?;
let value = &mut *value_ptr.as_array(value_max_len).as_slice_mut()?; let value = &mut *value_ptr
.as_array(value_max_len)
.as_slice_mut()?
.expect("cannot use with shared memories; see https://github.com/bytecodealliance/wasmtime/issues/5235 (TODO)");
Ok((&*self) Ok((&*self)
.options_get(symmetric_state_handle.into(), name_str, value)? .options_get(symmetric_state_handle.into(), name_str, value)?
.try_into()?) .try_into()?)
@@ -201,7 +210,10 @@ impl super::wasi_ephemeral_crypto_symmetric::WasiEphemeralCryptoSymmetric for Wa
data_ptr: &wiggle::GuestPtr<'_, u8>, data_ptr: &wiggle::GuestPtr<'_, u8>,
data_len: guest_types::Size, data_len: guest_types::Size,
) -> Result<(), guest_types::CryptoErrno> { ) -> Result<(), guest_types::CryptoErrno> {
let data = &*data_ptr.as_array(data_len).as_slice()?; let data = &*data_ptr
.as_array(data_len)
.as_slice()?
.expect("cannot use with shared memories; see https://github.com/bytecodealliance/wasmtime/issues/5235 (TODO)");
Ok((&*self).symmetric_state_absorb(symmetric_state_handle.into(), data)?) Ok((&*self).symmetric_state_absorb(symmetric_state_handle.into(), data)?)
} }
@@ -211,7 +223,10 @@ impl super::wasi_ephemeral_crypto_symmetric::WasiEphemeralCryptoSymmetric for Wa
out_ptr: &wiggle::GuestPtr<'_, u8>, out_ptr: &wiggle::GuestPtr<'_, u8>,
out_len: guest_types::Size, out_len: guest_types::Size,
) -> Result<(), guest_types::CryptoErrno> { ) -> Result<(), guest_types::CryptoErrno> {
let out = &mut *out_ptr.as_array(out_len).as_slice_mut()?; let out = &mut *out_ptr
.as_array(out_len)
.as_slice_mut()?
.expect("cannot use with shared memories; see https://github.com/bytecodealliance/wasmtime/issues/5235 (TODO)");
Ok((&*self).symmetric_state_squeeze(symmetric_state_handle.into(), out)?) Ok((&*self).symmetric_state_squeeze(symmetric_state_handle.into(), out)?)
} }
@@ -252,8 +267,14 @@ impl super::wasi_ephemeral_crypto_symmetric::WasiEphemeralCryptoSymmetric for Wa
data_ptr: &wiggle::GuestPtr<'_, u8>, data_ptr: &wiggle::GuestPtr<'_, u8>,
data_len: guest_types::Size, data_len: guest_types::Size,
) -> Result<guest_types::Size, guest_types::CryptoErrno> { ) -> Result<guest_types::Size, guest_types::CryptoErrno> {
let out = &mut *out_ptr.as_array(out_len).as_slice_mut()?; let out = &mut *out_ptr
let data = &*data_ptr.as_array(data_len).as_slice()?; .as_array(out_len)
.as_slice_mut()?
.expect("cannot use with shared memories; see https://github.com/bytecodealliance/wasmtime/issues/5235 (TODO)");
let data = &*data_ptr
.as_array(data_len)
.as_slice()?
.expect("cannot use with shared memories; see https://github.com/bytecodealliance/wasmtime/issues/5235 (TODO)");
Ok((&*self) Ok((&*self)
.symmetric_state_encrypt(symmetric_state_handle.into(), out, data)? .symmetric_state_encrypt(symmetric_state_handle.into(), out, data)?
.try_into()?) .try_into()?)
@@ -267,8 +288,14 @@ impl super::wasi_ephemeral_crypto_symmetric::WasiEphemeralCryptoSymmetric for Wa
data_ptr: &wiggle::GuestPtr<'_, u8>, data_ptr: &wiggle::GuestPtr<'_, u8>,
data_len: guest_types::Size, data_len: guest_types::Size,
) -> Result<guest_types::SymmetricTag, guest_types::CryptoErrno> { ) -> Result<guest_types::SymmetricTag, guest_types::CryptoErrno> {
let out = &mut *out_ptr.as_array(out_len).as_slice_mut()?; let out = &mut *out_ptr
let data = &*data_ptr.as_array(data_len).as_slice()?; .as_array(out_len)
.as_slice_mut()?
.expect("cannot use with shared memories; see https://github.com/bytecodealliance/wasmtime/issues/5235 (TODO)");
let data = &*data_ptr
.as_array(data_len)
.as_slice()?
.expect("cannot use with shared memories; see https://github.com/bytecodealliance/wasmtime/issues/5235 (TODO)");
Ok((&*self) Ok((&*self)
.symmetric_state_encrypt_detached(symmetric_state_handle.into(), out, data)? .symmetric_state_encrypt_detached(symmetric_state_handle.into(), out, data)?
.into()) .into())
@@ -282,8 +309,14 @@ impl super::wasi_ephemeral_crypto_symmetric::WasiEphemeralCryptoSymmetric for Wa
data_ptr: &wiggle::GuestPtr<'_, u8>, data_ptr: &wiggle::GuestPtr<'_, u8>,
data_len: guest_types::Size, data_len: guest_types::Size,
) -> Result<guest_types::Size, guest_types::CryptoErrno> { ) -> Result<guest_types::Size, guest_types::CryptoErrno> {
let out = &mut *out_ptr.as_array(out_len).as_slice_mut()?; let out = &mut *out_ptr
let data = &*data_ptr.as_array(data_len).as_slice()?; .as_array(out_len)
.as_slice_mut()?
.expect("cannot use with shared memories; see https://github.com/bytecodealliance/wasmtime/issues/5235 (TODO)");
let data = &*data_ptr
.as_array(data_len)
.as_slice()?
.expect("cannot use with shared memories; see https://github.com/bytecodealliance/wasmtime/issues/5235 (TODO)");
Ok((&*self) Ok((&*self)
.symmetric_state_decrypt(symmetric_state_handle.into(), out, data)? .symmetric_state_decrypt(symmetric_state_handle.into(), out, data)?
.try_into()?) .try_into()?)
@@ -299,9 +332,18 @@ impl super::wasi_ephemeral_crypto_symmetric::WasiEphemeralCryptoSymmetric for Wa
raw_tag_ptr: &wiggle::GuestPtr<'_, u8>, raw_tag_ptr: &wiggle::GuestPtr<'_, u8>,
raw_tag_len: guest_types::Size, raw_tag_len: guest_types::Size,
) -> Result<guest_types::Size, guest_types::CryptoErrno> { ) -> Result<guest_types::Size, guest_types::CryptoErrno> {
let out = &mut *out_ptr.as_array(out_len).as_slice_mut()?; let out = &mut *out_ptr
let data = &*data_ptr.as_array(data_len).as_slice()?; .as_array(out_len)
let raw_tag: &[u8] = &*raw_tag_ptr.as_array(raw_tag_len).as_slice()?; .as_slice_mut()?
.expect("cannot use with shared memories; see https://github.com/bytecodealliance/wasmtime/issues/5235 (TODO)");
let data = &*data_ptr
.as_array(data_len)
.as_slice()?
.expect("cannot use with shared memories; see https://github.com/bytecodealliance/wasmtime/issues/5235 (TODO)");
let raw_tag: &[u8] = &*raw_tag_ptr
.as_array(raw_tag_len)
.as_slice()?
.expect("cannot use with shared memories; see https://github.com/bytecodealliance/wasmtime/issues/5235 (TODO)");
Ok((&*self) Ok((&*self)
.symmetric_state_decrypt_detached(symmetric_state_handle.into(), out, data, raw_tag)? .symmetric_state_decrypt_detached(symmetric_state_handle.into(), out, data, raw_tag)?
.try_into()?) .try_into()?)
@@ -331,7 +373,10 @@ impl super::wasi_ephemeral_crypto_symmetric::WasiEphemeralCryptoSymmetric for Wa
buf_ptr: &wiggle::GuestPtr<'_, u8>, buf_ptr: &wiggle::GuestPtr<'_, u8>,
buf_len: guest_types::Size, buf_len: guest_types::Size,
) -> Result<guest_types::Size, guest_types::CryptoErrno> { ) -> Result<guest_types::Size, guest_types::CryptoErrno> {
let buf = &mut *buf_ptr.as_array(buf_len).as_slice_mut()?; let buf = &mut *buf_ptr
.as_array(buf_len)
.as_slice_mut()?
.expect("cannot use with shared memories; see https://github.com/bytecodealliance/wasmtime/issues/5235 (TODO)");
Ok((&*self) Ok((&*self)
.symmetric_tag_pull(symmetric_tag_handle.into(), buf)? .symmetric_tag_pull(symmetric_tag_handle.into(), buf)?
.try_into()?) .try_into()?)
@@ -343,7 +388,10 @@ impl super::wasi_ephemeral_crypto_symmetric::WasiEphemeralCryptoSymmetric for Wa
expected_raw_ptr: &wiggle::GuestPtr<'_, u8>, expected_raw_ptr: &wiggle::GuestPtr<'_, u8>,
expected_raw_len: guest_types::Size, expected_raw_len: guest_types::Size,
) -> Result<(), guest_types::CryptoErrno> { ) -> Result<(), guest_types::CryptoErrno> {
let expected_raw = &*expected_raw_ptr.as_array(expected_raw_len).as_slice()?; let expected_raw = &*expected_raw_ptr
.as_array(expected_raw_len)
.as_slice()?
.expect("cannot use with shared memories; see https://github.com/bytecodealliance/wasmtime/issues/5235 (TODO)");
Ok((&*self).symmetric_tag_verify(symmetric_tag_handle.into(), expected_raw)?) Ok((&*self).symmetric_tag_verify(symmetric_tag_handle.into(), expected_raw)?)
} }

View File

@@ -80,8 +80,11 @@ impl<'a> WasiEphemeralNn for WasiNnCtx {
out_buffer: &GuestPtr<'_, u8>, out_buffer: &GuestPtr<'_, u8>,
out_buffer_max_size: u32, out_buffer_max_size: u32,
) -> Result<u32> { ) -> Result<u32> {
let mut destination = out_buffer.as_array(out_buffer_max_size).as_slice_mut()?;
if let Some(exec_context) = self.executions.get_mut(exec_context_id) { if let Some(exec_context) = self.executions.get_mut(exec_context_id) {
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)");
Ok(exec_context.get_output(index, &mut destination)?) Ok(exec_context.get_output(index, &mut destination)?)
} else { } else {
Err(UsageError::InvalidGraphHandle.into()) Err(UsageError::InvalidGraphHandle.into())

View File

@@ -31,8 +31,15 @@ impl Backend for OpenvinoBackend {
// Read the guest array. // Read the guest array.
let builders = builders.as_ptr(); let builders = builders.as_ptr();
let xml = builders.read()?.as_slice()?; let xml = builders
let weights = builders.add(1)?.read()?.as_slice()?; .read()?
.as_slice()?
.expect("cannot use with shared memories; see https://github.com/bytecodealliance/wasmtime/issues/5235 (TODO)");
let weights = builders
.add(1)?
.read()?
.as_slice()?
.expect("cannot use with shared memories; see https://github.com/bytecodealliance/wasmtime/issues/5235 (TODO)");
// Construct OpenVINO graph structures: `cnn_network` contains the graph // Construct OpenVINO graph structures: `cnn_network` contains the graph
// structure, `exec_network` can perform inference. // structure, `exec_network` can perform inference.
@@ -78,6 +85,7 @@ impl BackendExecutionContext for OpenvinoExecutionContext {
let dimensions = tensor let dimensions = tensor
.dimensions .dimensions
.as_slice()? .as_slice()?
.expect("cannot use with shared memories; see https://github.com/bytecodealliance/wasmtime/issues/5235 (TODO)")
.iter() .iter()
.map(|d| *d as usize) .map(|d| *d as usize)
.collect::<Vec<_>>(); .collect::<Vec<_>>();
@@ -86,7 +94,10 @@ impl BackendExecutionContext for OpenvinoExecutionContext {
// TODO There must be some good way to discover the layout here; this // TODO There must be some good way to discover the layout here; this
// should not have to default to NHWC. // should not have to default to NHWC.
let desc = TensorDesc::new(Layout::NHWC, &dimensions, precision); let desc = TensorDesc::new(Layout::NHWC, &dimensions, precision);
let data = tensor.data.as_slice()?; let data = tensor
.data
.as_slice()?
.expect("cannot use with shared memories; see https://github.com/bytecodealliance/wasmtime/issues/5235 (TODO)");
let blob = openvino::Blob::new(&desc, &data)?; let blob = openvino::Blob::new(&desc, &data)?;
// Actually assign the blob to the request. // Actually assign the blob to the request.

View File

@@ -513,88 +513,106 @@ impl<'a, T> GuestPtr<'a, [T]> {
/// Attempts to create a [`GuestSlice<'_, T>`] from this pointer, performing /// Attempts to create a [`GuestSlice<'_, T>`] from this pointer, performing
/// bounds checks and type validation. The `GuestSlice` is a smart pointer /// bounds checks and type validation. The `GuestSlice` is a smart pointer
/// that can be used as a `&[T]` via the `Deref` trait. /// that can be used as a `&[T]` via the `Deref` trait. The region of memory
/// The region of memory backing the slice will be marked as shareably /// backing the slice will be marked as shareably borrowed by the
/// borrowed by the [`GuestMemory`] until the `GuestSlice` is dropped. /// [`GuestMemory`] until the `GuestSlice` is dropped. Multiple shareable
/// Multiple shareable borrows of the same memory are permitted, but only /// borrows of the same memory are permitted, but only one mutable borrow.
/// one mutable borrow.
/// ///
/// This function will return a `GuestSlice` into host memory if all checks /// This function will return a `GuestSlice` into host memory if all checks
/// succeed (valid utf-8, valid pointers, memory is not borrowed, etc). If /// succeed (valid utf-8, valid pointers, memory is not borrowed, etc.). If
/// any checks fail then `GuestError` will be returned. /// any checks fail then `GuestError` will be returned.
pub fn as_slice(&self) -> Result<GuestSlice<'a, T>, GuestError> ///
/// Additionally, because it is `unsafe` to have a `GuestSlice` of shared
/// memory, this function will return `None` in this case.
pub fn as_slice(&self) -> Result<Option<GuestSlice<'a, T>>, GuestError>
where where
T: GuestTypeTransparent<'a>, T: GuestTypeTransparent<'a>,
{ {
let len = match self.pointer.1.checked_mul(T::guest_size()) { match self.as_unsafe_slice_mut()?.shared_borrow() {
Some(l) => l, UnsafeBorrowResult::Ok(slice) => Ok(Some(slice)),
None => return Err(GuestError::PtrOverflow), 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. The region of memory backing the slice
/// will be marked as borrowed by the [`GuestMemory`] until the `GuestSlice`
/// 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<Option<GuestSliceMut<'a, T>>, GuestError>
where
T: GuestTypeTransparent<'a>,
{
match self.as_unsafe_slice_mut()?.mut_borrow() {
UnsafeBorrowResult::Ok(slice) => Ok(Some(slice)),
UnsafeBorrowResult::Shared(_) => Ok(None),
UnsafeBorrowResult::Err(e) => Err(e),
}
}
/// 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<UnsafeGuestSlice<'a, T>, GuestError>
where
T: GuestTypeTransparent<'a>,
{
// Validate the bounds of the region in the original memory.
let len = self.checked_byte_len()?;
let ptr = let ptr =
self.mem self.mem
.validate_size_align(self.pointer.0, T::guest_align(), len)? as *mut T; .validate_size_align(self.pointer.0, T::guest_align(), len)? as *mut T;
let region = Region {
let borrow = self.mem.shared_borrow(Region {
start: self.pointer.0, start: self.pointer.0,
len, len,
})?; };
// Validate all elements in slice. // Validate all elements in slice. `T::validate` is expected to be a
// SAFETY: ptr has been validated by self.mem.validate_size_align // noop for `GuestTypeTransparent` so this check may not be entirely
// necessary (TODO).
//
// SAFETY: `ptr` has been validated by `self.mem.validate_size_align`.
for offs in 0..self.pointer.1 { for offs in 0..self.pointer.1 {
T::validate(unsafe { ptr.add(offs as usize) })?; T::validate(unsafe { ptr.add(offs as usize) })?;
} }
// SAFETY: iff there are no overlapping mut borrows it is valid to construct a &[T] Ok(UnsafeGuestSlice {
let ptr = unsafe { slice::from_raw_parts(ptr, self.pointer.1 as usize) };
Ok(GuestSlice {
ptr, ptr,
len: self.pointer.1 as usize,
region,
mem: self.mem, mem: self.mem,
borrow,
}) })
} }
/// Attempts to create a [`GuestSliceMut<'_, T>`] from this pointer, performing /// Copies the data in the guest region into a [`Vec`].
/// 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. The region of memory backing the slice will be marked as borrowed
/// by the [`GuestMemory`] until the `GuestSlice` is dropped.
/// ///
/// This function will return a `GuestSliceMut` into host memory if all checks /// This is useful when one cannot use [`GuestPtr::as_slice`], e.g., when
/// succeed (valid utf-8, valid pointers, memory is not borrowed, etc). If /// pointing to a region of WebAssembly shared memory.
/// any checks fail then `GuestError` will be returned. pub fn to_vec(&self) -> Result<Vec<T>, GuestError>
pub fn as_slice_mut(&self) -> Result<GuestSliceMut<'a, T>, GuestError>
where where
T: GuestTypeTransparent<'a>, T: GuestTypeTransparent<'a> + Copy + 'a,
{ {
let len = match self.pointer.1.checked_mul(T::guest_size()) { let guest_slice = self.as_unsafe_slice_mut()?;
Some(l) => l, let mut vec = Vec::with_capacity(guest_slice.len);
None => return Err(GuestError::PtrOverflow), for offs in 0..guest_slice.len {
}; let elem = self.get(offs as u32).expect("already validated the size");
let ptr = vec.push(elem.read()?);
self.mem
.validate_size_align(self.pointer.0, T::guest_align(), len)? as *mut T;
let borrow = self.mem.mut_borrow(Region {
start: self.pointer.0,
len,
})?;
// Validate all elements in slice.
// SAFETY: ptr has been validated by self.mem.validate_size_align
for offs in 0..self.pointer.1 {
T::validate(unsafe { ptr.add(offs as usize) })?;
} }
Ok(vec)
// SAFETY: iff there are no overlapping borrows it is valid to construct a &mut [T]
let ptr = unsafe { slice::from_raw_parts_mut(ptr, self.pointer.1 as usize) };
Ok(GuestSliceMut {
ptr,
mem: self.mem,
borrow,
})
} }
/// Copies the data pointed to by `slice` into this guest region. /// Copies the data pointed to by `slice` into this guest region.
@@ -613,14 +631,25 @@ impl<'a, T> GuestPtr<'a, [T]> {
where where
T: GuestTypeTransparent<'a> + Copy + 'a, T: GuestTypeTransparent<'a> + Copy + 'a,
{ {
// bounds check ... // Retrieve the slice of memory to copy to, performing the necessary
let mut self_slice = self.as_slice_mut()?; // bounds checks ...
let guest_slice = self.as_unsafe_slice_mut()?;
// ... length check ... // ... length check ...
if self_slice.len() != slice.len() { if guest_slice.len != slice.len() {
return Err(GuestError::SliceLengthsDiffer); return Err(GuestError::SliceLengthsDiffer);
} }
// ... and copy! // ... and copy the bytes.
self_slice.copy_from_slice(slice); match guest_slice.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)
unsafe { std::ptr::copy(slice.as_ptr(), guest_slice.ptr, guest_slice.len) };
}
UnsafeBorrowResult::Err(e) => return Err(e),
}
Ok(()) Ok(())
} }
@@ -664,6 +693,17 @@ impl<'a, T> GuestPtr<'a, [T]> {
None None
} }
} }
/// Return the number of bytes necessary to represent the pointed-to value.
fn checked_byte_len(&self) -> Result<u32, GuestError>
where
T: GuestTypeTransparent<'a>,
{
match self.pointer.1.checked_mul(T::guest_size()) {
Some(l) => Ok(l),
None => Err(GuestError::PtrOverflow),
}
}
} }
impl<'a> GuestPtr<'a, str> { impl<'a> GuestPtr<'a, str> {
@@ -780,6 +820,7 @@ impl<T: ?Sized + Pointee> fmt::Debug for GuestPtr<'_, T> {
} }
/// A smart pointer to an shareable slice in guest memory. /// A smart pointer to an shareable slice in guest memory.
///
/// Usable as a `&'a [T]` via [`std::ops::Deref`]. /// Usable as a `&'a [T]` via [`std::ops::Deref`].
pub struct GuestSlice<'a, T> { pub struct GuestSlice<'a, T> {
ptr: &'a [T], ptr: &'a [T],
@@ -801,6 +842,7 @@ impl<'a, T> Drop for GuestSlice<'a, T> {
} }
/// A smart pointer to a mutable slice in guest memory. /// 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 /// Usable as a `&'a [T]` via [`std::ops::Deref`] and as a `&'a mut [T]` via
/// [`std::ops::DerefMut`]. /// [`std::ops::DerefMut`].
pub struct GuestSliceMut<'a, T> { pub struct GuestSliceMut<'a, T> {
@@ -828,6 +870,108 @@ impl<'a, T> Drop for GuestSliceMut<'a, T> {
} }
} }
/// 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: *mut T,
/// The (validated) number of items in the slice.
len: usize,
/// The (validated) address bounds of the slice in memory.
region: Region,
/// The original memory.
mem: &'a dyn GuestMemory,
}
impl<'a, T> UnsafeGuestSlice<'a, T> {
/// 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<GuestSlice<'a, T>, Self> {
if self.mem.is_shared_memory() {
UnsafeBorrowResult::Shared(self)
} else {
match self.mem.shared_borrow(self.region) {
Ok(borrow) => {
let ptr = unsafe { slice::from_raw_parts(self.ptr, self.len) };
UnsafeBorrowResult::Ok(GuestSlice {
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<GuestSliceMut<'a, T>, Self> {
if self.mem.is_shared_memory() {
UnsafeBorrowResult::Shared(self)
} else {
match self.mem.mut_borrow(self.region) {
Ok(borrow) => {
let ptr = unsafe { slice::from_raw_parts_mut(self.ptr, self.len) };
UnsafeBorrowResult::Ok(GuestSliceMut {
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<T, S> {
/// 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<T, S> From<GuestError> for UnsafeBorrowResult<T, S> {
fn from(e: GuestError) -> Self {
UnsafeBorrowResult::Err(e)
}
}
/// A smart pointer to an shareable `str` in guest memory. /// A smart pointer to an shareable `str` in guest memory.
/// Usable as a `&'a str` via [`std::ops::Deref`]. /// Usable as a `&'a str` via [`std::ops::Deref`].
pub struct GuestStr<'a> { pub struct GuestStr<'a> {

View File

@@ -147,7 +147,10 @@ impl<'a> crate::wasi_snapshot_preview1::WasiSnapshotPreview1 for WasiCtx<'a> {
let len: u32 = iov.buf_len; let len: u32 = iov.buf_len;
let buf: GuestPtr<[u8]> = base.as_array(len); let buf: GuestPtr<[u8]> = base.as_array(len);
// GuestSlice will remain borrowed until dropped: // GuestSlice will remain borrowed until dropped:
let slice = buf.as_slice().expect("borrow slice from iovec"); let slice = buf
.as_slice()
.expect("borrow slice from iovec")
.expect("expected non-shared memory");
slices.push(slice); slices.push(slice);
} }
println!("iovec slices: ["); println!("iovec slices: [");