wiggle: adapt Wiggle strings for shared use (#5264)

* wiggle: adapt Wiggle strings for shared use

This is an extension of #5229 for the `&str` and `&mut str` types. As
documented there, we are attempting to maintain Rust guarantees for
slices that Wiggle hands out in the presence of WebAssembly shared
memory, in which case multiple threads could be modifying the underlying
data of the slice.

This change changes the API of `GuestPtr` to return an `Option` which is
`None` when attempting to view the WebAssembly data as a string and the
underlying WebAssembly memory is shared. This reuses the
`UnsafeGuestSlice` structure from #5229 to do so and appropriately marks
the region as borrowed in Wiggle's manual borrow checker. Each original
call site in this project's WASI implementations is fixed up to `expect`
that a non-shared memory is used.  (Note that I can find no uses of
`GuestStrMut` in the WASI implementations).

* wiggle: make `GuestStr*` containers wrappers of `GuestSlice*`

This change makes it possible to reuse the underlying logic in
`UnsafeGuestSlice` and the `GuestSlice*` implementations to continue to
expose the `GuestStr` and `GuestStrMut` types. These types now are
simple wrappers of their `GuestSlice*` variant. The UTF-8 validation
that distinguished `GuestStr*` now lives in the `TryFrom`
implementations for each type.
This commit is contained in:
Andrew Brown
2022-11-14 14:33:24 -08:00
committed by GitHub
parent 7a6fbe0898
commit 060f12571d
7 changed files with 94 additions and 107 deletions

View File

@@ -803,7 +803,7 @@ impl wasi_snapshot_preview1::WasiSnapshotPreview1 for WasiCtx {
self.table() self.table()
.get_dir(u32::from(dirfd))? .get_dir(u32::from(dirfd))?
.get_cap(DirCaps::CREATE_DIRECTORY)? .get_cap(DirCaps::CREATE_DIRECTORY)?
.create_dir(path.as_str()?.deref()) .create_dir(path.as_str()?.expect("cannot use with shared memories; see https://github.com/bytecodealliance/wasmtime/issues/5235 (TODO)").deref())
.await .await
} }
@@ -818,7 +818,7 @@ impl wasi_snapshot_preview1::WasiSnapshotPreview1 for WasiCtx {
.get_dir(u32::from(dirfd))? .get_dir(u32::from(dirfd))?
.get_cap(DirCaps::PATH_FILESTAT_GET)? .get_cap(DirCaps::PATH_FILESTAT_GET)?
.get_path_filestat( .get_path_filestat(
path.as_str()?.deref(), path.as_str()?.expect("cannot use with shared memories; see https://github.com/bytecodealliance/wasmtime/issues/5235 (TODO)").deref(),
flags.contains(types::Lookupflags::SYMLINK_FOLLOW), flags.contains(types::Lookupflags::SYMLINK_FOLLOW),
) )
.await?; .await?;
@@ -845,7 +845,7 @@ impl wasi_snapshot_preview1::WasiSnapshotPreview1 for WasiCtx {
.get_dir(u32::from(dirfd))? .get_dir(u32::from(dirfd))?
.get_cap(DirCaps::PATH_FILESTAT_SET_TIMES)? .get_cap(DirCaps::PATH_FILESTAT_SET_TIMES)?
.set_times( .set_times(
path.as_str()?.deref(), path.as_str()?.expect("cannot use with shared memories; see https://github.com/bytecodealliance/wasmtime/issues/5235 (TODO)").deref(),
atim, atim,
mtim, mtim,
flags.contains(types::Lookupflags::SYMLINK_FOLLOW), flags.contains(types::Lookupflags::SYMLINK_FOLLOW),
@@ -876,9 +876,9 @@ impl wasi_snapshot_preview1::WasiSnapshotPreview1 for WasiCtx {
src_dir src_dir
.hard_link( .hard_link(
src_path.as_str()?.deref(), src_path.as_str()?.expect("cannot use with shared memories; see https://github.com/bytecodealliance/wasmtime/issues/5235 (TODO)").deref(),
target_dir.deref(), target_dir.deref(),
target_path.as_str()?.deref(), target_path.as_str()?.expect("cannot use with shared memories; see https://github.com/bytecodealliance/wasmtime/issues/5235 (TODO)").deref(),
) )
.await .await
} }
@@ -904,7 +904,7 @@ impl wasi_snapshot_preview1::WasiSnapshotPreview1 for WasiCtx {
let oflags = OFlags::from(&oflags); let oflags = OFlags::from(&oflags);
let fdflags = FdFlags::from(fdflags); let fdflags = FdFlags::from(fdflags);
let path = path.as_str()?; let path = path.as_str()?.expect("cannot use with shared memories; see https://github.com/bytecodealliance/wasmtime/issues/5235 (TODO)");
if oflags.contains(OFlags::DIRECTORY) { if oflags.contains(OFlags::DIRECTORY) {
if oflags.contains(OFlags::CREATE) if oflags.contains(OFlags::CREATE)
|| oflags.contains(OFlags::EXCLUSIVE) || oflags.contains(OFlags::EXCLUSIVE)
@@ -953,7 +953,7 @@ impl wasi_snapshot_preview1::WasiSnapshotPreview1 for WasiCtx {
.table() .table()
.get_dir(u32::from(dirfd))? .get_dir(u32::from(dirfd))?
.get_cap(DirCaps::READLINK)? .get_cap(DirCaps::READLINK)?
.read_link(path.as_str()?.deref()) .read_link(path.as_str()?.expect("cannot use with shared memories; see https://github.com/bytecodealliance/wasmtime/issues/5235 (TODO)").deref())
.await? .await?
.into_os_string() .into_os_string()
.into_string() .into_string()
@@ -979,7 +979,7 @@ impl wasi_snapshot_preview1::WasiSnapshotPreview1 for WasiCtx {
self.table() self.table()
.get_dir(u32::from(dirfd))? .get_dir(u32::from(dirfd))?
.get_cap(DirCaps::REMOVE_DIRECTORY)? .get_cap(DirCaps::REMOVE_DIRECTORY)?
.remove_dir(path.as_str()?.deref()) .remove_dir(path.as_str()?.expect("cannot use with shared memories; see https://github.com/bytecodealliance/wasmtime/issues/5235 (TODO)").deref())
.await .await
} }
@@ -999,9 +999,9 @@ impl wasi_snapshot_preview1::WasiSnapshotPreview1 for WasiCtx {
.get_cap(DirCaps::RENAME_TARGET)?; .get_cap(DirCaps::RENAME_TARGET)?;
src_dir src_dir
.rename( .rename(
src_path.as_str()?.deref(), src_path.as_str()?.expect("cannot use with shared memories; see https://github.com/bytecodealliance/wasmtime/issues/5235 (TODO)").deref(),
dest_dir.deref(), dest_dir.deref(),
dest_path.as_str()?.deref(), dest_path.as_str()?.expect("cannot use with shared memories; see https://github.com/bytecodealliance/wasmtime/issues/5235 (TODO)").deref(),
) )
.await .await
} }
@@ -1015,7 +1015,7 @@ impl wasi_snapshot_preview1::WasiSnapshotPreview1 for WasiCtx {
self.table() self.table()
.get_dir(u32::from(dirfd))? .get_dir(u32::from(dirfd))?
.get_cap(DirCaps::SYMLINK)? .get_cap(DirCaps::SYMLINK)?
.symlink(src_path.as_str()?.deref(), dest_path.as_str()?.deref()) .symlink(src_path.as_str()?.expect("cannot use with shared memories; see https://github.com/bytecodealliance/wasmtime/issues/5235 (TODO)").deref(), dest_path.as_str()?.expect("cannot use with shared memories; see https://github.com/bytecodealliance/wasmtime/issues/5235 (TODO)").deref())
.await .await
} }
@@ -1027,7 +1027,8 @@ impl wasi_snapshot_preview1::WasiSnapshotPreview1 for WasiCtx {
self.table() self.table()
.get_dir(u32::from(dirfd))? .get_dir(u32::from(dirfd))?
.get_cap(DirCaps::UNLINK_FILE)? .get_cap(DirCaps::UNLINK_FILE)?
.unlink_file(path.as_str()?.deref()) .unlink_file(path.as_str()?
.expect("cannot use with shared memories; see https://github.com/bytecodealliance/wasmtime/issues/5235 (TODO)").deref())
.await .await
} }

View File

@@ -17,7 +17,7 @@ impl super::wasi_ephemeral_crypto_asymmetric_common::WasiEphemeralCryptoAsymmetr
alg_str: &wiggle::GuestPtr<'_, str>, alg_str: &wiggle::GuestPtr<'_, str>,
options_handle: &guest_types::OptOptions, options_handle: &guest_types::OptOptions,
) -> 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()?.expect("cannot use with shared memories; see https://github.com/bytecodealliance/wasmtime/issues/5235 (TODO)");
let options_handle = match *options_handle { let options_handle = match *options_handle {
guest_types::OptOptions::Some(options_handle) => Some(options_handle), guest_types::OptOptions::Some(options_handle) => Some(options_handle),
guest_types::OptOptions::None => None, guest_types::OptOptions::None => None,
@@ -89,7 +89,7 @@ impl super::wasi_ephemeral_crypto_asymmetric_common::WasiEphemeralCryptoAsymmetr
alg_str: &wiggle::GuestPtr<'_, str>, alg_str: &wiggle::GuestPtr<'_, str>,
options_handle: &guest_types::OptOptions, options_handle: &guest_types::OptOptions,
) -> 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()?.expect("cannot use with shared memories; see https://github.com/bytecodealliance/wasmtime/issues/5235 (TODO)");
let options_handle = match *options_handle { let options_handle = match *options_handle {
guest_types::OptOptions::Some(options_handle) => Some(options_handle), guest_types::OptOptions::Some(options_handle) => Some(options_handle),
guest_types::OptOptions::None => None, guest_types::OptOptions::None => None,
@@ -107,7 +107,7 @@ impl super::wasi_ephemeral_crypto_asymmetric_common::WasiEphemeralCryptoAsymmetr
encoded_len: guest_types::Size, encoded_len: guest_types::Size,
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()?.expect("cannot use with shared memories; see https://github.com/bytecodealliance/wasmtime/issues/5235 (TODO)");
let encoded = &*encoded_ptr let encoded = &*encoded_ptr
.as_array(encoded_len) .as_array(encoded_len)
.as_slice()? .as_slice()?
@@ -167,7 +167,7 @@ impl super::wasi_ephemeral_crypto_asymmetric_common::WasiEphemeralCryptoAsymmetr
encoded_len: guest_types::Size, encoded_len: guest_types::Size,
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()?.expect("cannot use with shared memories; see https://github.com/bytecodealliance/wasmtime/issues/5235 (TODO)");
let encoded = &*encoded_ptr let encoded = &*encoded_ptr
.as_array(encoded_len) .as_array(encoded_len)
.as_slice()? .as_slice()?
@@ -218,7 +218,7 @@ impl super::wasi_ephemeral_crypto_asymmetric_common::WasiEphemeralCryptoAsymmetr
encoded_len: guest_types::Size, encoded_len: guest_types::Size,
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()?.expect("cannot use with shared memories; see https://github.com/bytecodealliance/wasmtime/issues/5235 (TODO)");
let encoded = &*encoded_ptr let encoded = &*encoded_ptr
.as_array(encoded_len) .as_array(encoded_len)
.as_slice()? .as_slice()?

View File

@@ -27,7 +27,7 @@ impl super::wasi_ephemeral_crypto_common::WasiEphemeralCryptoCommon for WasiCryp
value_ptr: &wiggle::GuestPtr<'_, u8>, value_ptr: &wiggle::GuestPtr<'_, u8>,
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()?.expect("cannot use with shared memories; see https://github.com/bytecodealliance/wasmtime/issues/5235 (TODO)");
let value: &[u8] = { let value: &[u8] = {
&*value_ptr &*value_ptr
.as_array(value_len) .as_array(value_len)
@@ -44,7 +44,7 @@ impl super::wasi_ephemeral_crypto_common::WasiEphemeralCryptoCommon for WasiCryp
buffer_ptr: &wiggle::GuestPtr<'_, u8>, buffer_ptr: &wiggle::GuestPtr<'_, u8>,
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()?.expect("cannot use with shared memories; see https://github.com/bytecodealliance/wasmtime/issues/5235 (TODO)");
let buffer: &'static mut [u8] = unsafe { let buffer: &'static mut [u8] = unsafe {
std::mem::transmute( std::mem::transmute(
&mut *buffer_ptr &mut *buffer_ptr
@@ -62,7 +62,7 @@ impl super::wasi_ephemeral_crypto_common::WasiEphemeralCryptoCommon for WasiCryp
name_str: &wiggle::GuestPtr<'_, str>, name_str: &wiggle::GuestPtr<'_, str>,
value: u64, value: u64,
) -> Result<(), guest_types::CryptoErrno> { ) -> Result<(), guest_types::CryptoErrno> {
let name_str: &str = &*name_str.as_str()?; let name_str: &str = &*name_str.as_str()?.expect("cannot use with shared memories; see https://github.com/bytecodealliance/wasmtime/issues/5235 (TODO)");
Ok((&*self).options_set_u64(options_handle.into(), name_str, value)?) Ok((&*self).options_set_u64(options_handle.into(), name_str, value)?)
} }

View File

@@ -22,7 +22,7 @@ impl super::wasi_ephemeral_crypto_signatures::WasiEphemeralCryptoSignatures for
encoded_len: guest_types::Size, encoded_len: guest_types::Size,
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()?.expect("cannot use with shared memories; see https://github.com/bytecodealliance/wasmtime/issues/5235 (TODO)");
let encoded = &*encoded_ptr let encoded = &*encoded_ptr
.as_array(encoded_len) .as_array(encoded_len)
.as_slice()? .as_slice()?

View File

@@ -12,7 +12,7 @@ impl super::wasi_ephemeral_crypto_symmetric::WasiEphemeralCryptoSymmetric for Wa
alg_str: &wiggle::GuestPtr<'_, str>, alg_str: &wiggle::GuestPtr<'_, str>,
options_handle: &guest_types::OptOptions, options_handle: &guest_types::OptOptions,
) -> 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()?.expect("cannot use with shared memories; see https://github.com/bytecodealliance/wasmtime/issues/5235 (TODO)");
let options_handle = match *options_handle { let options_handle = match *options_handle {
guest_types::OptOptions::Some(options_handle) => Some(options_handle), guest_types::OptOptions::Some(options_handle) => Some(options_handle),
guest_types::OptOptions::None => None, guest_types::OptOptions::None => None,
@@ -86,7 +86,7 @@ impl super::wasi_ephemeral_crypto_symmetric::WasiEphemeralCryptoSymmetric for Wa
alg_str: &wiggle::GuestPtr<'_, str>, alg_str: &wiggle::GuestPtr<'_, str>,
options_handle: &guest_types::OptOptions, options_handle: &guest_types::OptOptions,
) -> 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()?.expect("cannot use with shared memories; see https://github.com/bytecodealliance/wasmtime/issues/5235 (TODO)");
let options_handle = match *options_handle { let options_handle = match *options_handle {
guest_types::OptOptions::Some(options_handle) => Some(options_handle), guest_types::OptOptions::Some(options_handle) => Some(options_handle),
guest_types::OptOptions::None => None, guest_types::OptOptions::None => None,
@@ -102,7 +102,7 @@ impl super::wasi_ephemeral_crypto_symmetric::WasiEphemeralCryptoSymmetric for Wa
raw_ptr: &wiggle::GuestPtr<'_, u8>, raw_ptr: &wiggle::GuestPtr<'_, u8>,
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()?.expect("cannot use with shared memories; see https://github.com/bytecodealliance/wasmtime/issues/5235 (TODO)");
let raw = &*raw_ptr let raw = &*raw_ptr
.as_array(raw_len) .as_array(raw_len)
.as_slice()? .as_slice()?
@@ -153,7 +153,7 @@ impl super::wasi_ephemeral_crypto_symmetric::WasiEphemeralCryptoSymmetric for Wa
key_handle: &guest_types::OptSymmetricKey, key_handle: &guest_types::OptSymmetricKey,
options_handle: &guest_types::OptOptions, options_handle: &guest_types::OptOptions,
) -> Result<guest_types::SymmetricState, guest_types::CryptoErrno> { ) -> Result<guest_types::SymmetricState, guest_types::CryptoErrno> {
let alg_str = &*alg_str.as_str()?; let alg_str = &*alg_str.as_str()?.expect("cannot use with shared memories; see https://github.com/bytecodealliance/wasmtime/issues/5235 (TODO)");
let key_handle = match *key_handle { let key_handle = match *key_handle {
guest_types::OptSymmetricKey::Some(key_handle) => Some(key_handle), guest_types::OptSymmetricKey::Some(key_handle) => Some(key_handle),
guest_types::OptSymmetricKey::None => None, guest_types::OptSymmetricKey::None => None,
@@ -178,7 +178,7 @@ impl super::wasi_ephemeral_crypto_symmetric::WasiEphemeralCryptoSymmetric for Wa
value_ptr: &wiggle::GuestPtr<'_, u8>, value_ptr: &wiggle::GuestPtr<'_, u8>,
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()?.expect("cannot use with shared memories; see https://github.com/bytecodealliance/wasmtime/issues/5235 (TODO)");
let value = &mut *value_ptr let value = &mut *value_ptr
.as_array(value_max_len) .as_array(value_max_len)
.as_slice_mut()? .as_slice_mut()?
@@ -193,7 +193,7 @@ impl super::wasi_ephemeral_crypto_symmetric::WasiEphemeralCryptoSymmetric for Wa
symmetric_state_handle: guest_types::SymmetricState, symmetric_state_handle: guest_types::SymmetricState,
name_str: &wiggle::GuestPtr<'_, str>, name_str: &wiggle::GuestPtr<'_, str>,
) -> Result<u64, guest_types::CryptoErrno> { ) -> Result<u64, guest_types::CryptoErrno> {
let name_str: &str = &*name_str.as_str()?; let name_str: &str = &*name_str.as_str()?.expect("cannot use with shared memories; see https://github.com/bytecodealliance/wasmtime/issues/5235 (TODO)");
Ok((&*self).options_get_u64(symmetric_state_handle.into(), name_str)?) Ok((&*self).options_get_u64(symmetric_state_handle.into(), name_str)?)
} }
@@ -244,7 +244,7 @@ impl super::wasi_ephemeral_crypto_symmetric::WasiEphemeralCryptoSymmetric for Wa
symmetric_state_handle: guest_types::SymmetricState, symmetric_state_handle: guest_types::SymmetricState,
alg_str: &wiggle::GuestPtr<'_, str>, alg_str: &wiggle::GuestPtr<'_, str>,
) -> 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()?.expect("cannot use with shared memories; see https://github.com/bytecodealliance/wasmtime/issues/5235 (TODO)");
Ok((&*self) Ok((&*self)
.symmetric_state_squeeze_key(symmetric_state_handle.into(), alg_str)? .symmetric_state_squeeze_key(symmetric_state_handle.into(), alg_str)?
.into()) .into())

View File

@@ -724,42 +724,23 @@ impl<'a> GuestPtr<'a, str> {
GuestPtr::new(self.mem, self.pointer) GuestPtr::new(self.mem, self.pointer)
} }
/// Returns a pointer for the underlying slice of bytes that this
/// pointer points to.
pub fn as_byte_ptr(&self) -> GuestPtr<'a, [u8]> {
GuestPtr::new(self.mem, self.pointer)
}
/// Attempts to create a [`GuestStr<'_>`] from this pointer, performing /// Attempts to create a [`GuestStr<'_>`] from this pointer, performing
/// bounds checks and utf-8 checks. The resulting `GuestStr` can be used /// bounds checks and utf-8 checks. The resulting `GuestStr` can be used as
/// as a `&str` via the `Deref` trait. The region of memory backing the /// a `&str` via the `Deref` trait. The region of memory backing the `str`
/// `str` will be marked as shareably borrowed by the [`GuestMemory`] /// will be marked as shareably borrowed by the [`GuestMemory`] until the
/// until the `GuestStr` is dropped. /// `GuestStr` is dropped.
/// ///
/// This function will return `GuestStr` into host memory if all checks /// This function will return `GuestStr` into host memory if all checks
/// succeed (valid utf-8, valid pointers, etc). If any checks fail then /// succeed (valid utf-8, valid pointers, etc). If any checks fail then
/// `GuestError` will be returned. /// `GuestError` will be returned.
pub fn as_str(&self) -> Result<GuestStr<'a>, GuestError> { ///
let ptr = self /// Additionally, because it is `unsafe` to have a `GuestStr` of shared
.mem /// memory, this function will return `None` in this case.
.validate_size_align(self.pointer.0, 1, self.pointer.1)?; pub fn as_str(&self) -> Result<Option<GuestStr<'a>>, GuestError> {
match self.as_bytes().as_unsafe_slice_mut()?.shared_borrow() {
let borrow = self.mem.shared_borrow(Region { UnsafeBorrowResult::Ok(s) => Ok(Some(s.try_into()?)),
start: self.pointer.0, UnsafeBorrowResult::Shared(_) => Ok(None),
len: self.pointer.1, UnsafeBorrowResult::Err(e) => Err(e),
})?;
// SAFETY: iff there are no overlapping borrows it is ok to construct
// a &mut str.
let ptr = unsafe { slice::from_raw_parts(ptr, self.pointer.1 as usize) };
// Validate that contents are utf-8:
match str::from_utf8(ptr) {
Ok(ptr) => Ok(GuestStr {
ptr,
mem: self.mem,
borrow,
}),
Err(e) => Err(GuestError::InvalidUtf8(e)),
} }
} }
@@ -772,27 +753,14 @@ impl<'a> GuestPtr<'a, str> {
/// This function will return `GuestStrMut` into host memory if all checks /// This function will return `GuestStrMut` into host memory if all checks
/// succeed (valid utf-8, valid pointers, etc). If any checks fail then /// succeed (valid utf-8, valid pointers, etc). If any checks fail then
/// `GuestError` will be returned. /// `GuestError` will be returned.
pub fn as_str_mut(&self) -> Result<GuestStrMut<'a>, GuestError> { ///
let ptr = self /// Additionally, because it is `unsafe` to have a `GuestStrMut` of shared
.mem /// memory, this function will return `None` in this case.
.validate_size_align(self.pointer.0, 1, self.pointer.1)?; pub fn as_str_mut(&self) -> Result<Option<GuestStrMut<'a>>, GuestError> {
match self.as_bytes().as_unsafe_slice_mut()?.mut_borrow() {
let borrow = self.mem.mut_borrow(Region { UnsafeBorrowResult::Ok(s) => Ok(Some(s.try_into()?)),
start: self.pointer.0, UnsafeBorrowResult::Shared(_) => Ok(None),
len: self.pointer.1, UnsafeBorrowResult::Err(e) => Err(e),
})?;
// SAFETY: iff there are no overlapping borrows it is ok to construct
// a &mut str.
let ptr = unsafe { slice::from_raw_parts_mut(ptr, self.pointer.1 as usize) };
// Validate that contents are utf-8:
match str::from_utf8_mut(ptr) {
Ok(ptr) => Ok(GuestStrMut {
ptr,
mem: self.mem,
borrow,
}),
Err(e) => Err(GuestError::InvalidUtf8(e)),
} }
} }
} }
@@ -974,50 +942,56 @@ impl<T, S> From<GuestError> for UnsafeBorrowResult<T, S> {
/// 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>(GuestSlice<'a, u8>);
ptr: &'a str,
mem: &'a dyn GuestMemory, impl<'a> std::convert::TryFrom<GuestSlice<'a, u8>> for GuestStr<'a> {
borrow: BorrowHandle, type Error = GuestError;
fn try_from(slice: GuestSlice<'a, u8>) -> Result<Self, Self::Error> {
match str::from_utf8(&slice) {
Ok(_) => Ok(Self(slice)),
Err(e) => Err(GuestError::InvalidUtf8(e)),
}
}
} }
impl<'a> std::ops::Deref for GuestStr<'a> { impl<'a> std::ops::Deref for GuestStr<'a> {
type Target = str; type Target = str;
fn deref(&self) -> &Self::Target { fn deref(&self) -> &Self::Target {
self.ptr // 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) }
impl<'a> Drop for GuestStr<'a> {
fn drop(&mut self) {
self.mem.shared_unborrow(self.borrow)
} }
} }
/// A smart pointer to a mutable `str` in guest memory. /// 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 /// Usable as a `&'a str` via [`std::ops::Deref`] and as a `&'a mut str` via
/// [`std::ops::DerefMut`]. /// [`std::ops::DerefMut`].
pub struct GuestStrMut<'a> { pub struct GuestStrMut<'a>(GuestSliceMut<'a, u8>);
ptr: &'a mut str,
mem: &'a dyn GuestMemory, impl<'a> std::convert::TryFrom<GuestSliceMut<'a, u8>> for GuestStrMut<'a> {
borrow: BorrowHandle, type Error = GuestError;
fn try_from(slice: GuestSliceMut<'a, u8>) -> Result<Self, Self::Error> {
match str::from_utf8(&slice) {
Ok(_) => Ok(Self(slice)),
Err(e) => Err(GuestError::InvalidUtf8(e)),
}
}
} }
impl<'a> std::ops::Deref for GuestStrMut<'a> { impl<'a> std::ops::Deref for GuestStrMut<'a> {
type Target = str; type Target = str;
fn deref(&self) -> &Self::Target { fn deref(&self) -> &Self::Target {
self.ptr // 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> { impl<'a> std::ops::DerefMut for GuestStrMut<'a> {
fn deref_mut(&mut self) -> &mut Self::Target { fn deref_mut(&mut self) -> &mut Self::Target {
self.ptr // 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) }
impl<'a> Drop for GuestStrMut<'a> {
fn drop(&mut self) {
self.mem.mut_unborrow(self.borrow)
} }
} }

View File

@@ -10,7 +10,10 @@ impl_errno!(types::Errno);
impl<'a> strings::Strings for WasiCtx<'a> { impl<'a> strings::Strings for WasiCtx<'a> {
fn hello_string(&mut self, a_string: &GuestPtr<str>) -> Result<u32, types::Errno> { fn hello_string(&mut self, a_string: &GuestPtr<str>) -> Result<u32, types::Errno> {
let s = a_string.as_str().expect("should be valid string"); let s = a_string
.as_str()
.expect("should be valid string")
.expect("expected non-shared memory");
println!("a_string='{}'", &*s); println!("a_string='{}'", &*s);
Ok(s.len() as u32) Ok(s.len() as u32)
} }
@@ -21,9 +24,18 @@ impl<'a> strings::Strings for WasiCtx<'a> {
b: &GuestPtr<str>, b: &GuestPtr<str>,
c: &GuestPtr<str>, c: &GuestPtr<str>,
) -> Result<u32, types::Errno> { ) -> Result<u32, types::Errno> {
let sa = a.as_str().expect("A should be valid string"); let sa = a
let sb = b.as_str().expect("B should be valid string"); .as_str()
let sc = c.as_str().expect("C should be valid string"); .expect("A should be valid string")
.expect("expected non-shared memory");
let sb = b
.as_str()
.expect("B should be valid string")
.expect("expected non-shared memory");
let sc = c
.as_str()
.expect("C should be valid string")
.expect("expected non-shared memory");
let total_len = sa.len() + sb.len() + sc.len(); let total_len = sa.len() + sb.len() + sc.len();
println!( println!(
"len={}, a='{}', b='{}', c='{}'", "len={}, a='{}', b='{}', c='{}'",