Skip to content

Commit 7c67378

Browse files
authored
wiggle: copy guest strings from shared memory (#5475)
* wiggle: copy guest strings from shared memory Along the same lines as #5471, this change adds a new smart pointer, `GuestStrCow`, to copy the string bytes over from Wasm memory to the host when the string is found in shared memory. This is necessary to maintain Rust guarantees: with shared memory, the bytes backing a `GuestStr` could be altered by another thread and this would invalidate the assumption that we can dereference at any point to `&str`. `GuestStrCow` is essentially a wrapper around `GuestStr` when the memory is not shared but copies the memory region into a `String` when the memory is shared. This change updates the uses of Wiggle strings in both wasi-common and wasi-crypto. * review: perform UTF-8 check on `GuestStr` construction
1 parent 52ba72f commit 7c67378

6 files changed

Lines changed: 96 additions & 54 deletions

File tree

crates/wasi-common/src/snapshots/preview_1.rs

Lines changed: 12 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -663,7 +663,7 @@ impl wasi_snapshot_preview1::WasiSnapshotPreview1 for WasiCtx {
663663
self.table()
664664
.get_dir(u32::from(dirfd))?
665665
.get_cap(DirCaps::CREATE_DIRECTORY)?
666-
.create_dir(path.as_str()?.expect("cannot use with shared memories; see https://github.com/bytecodealliance/wasmtime/issues/5235 (TODO)").deref())
666+
.create_dir(path.as_cow()?.deref())
667667
.await
668668
}
669669

@@ -678,7 +678,7 @@ impl wasi_snapshot_preview1::WasiSnapshotPreview1 for WasiCtx {
678678
.get_dir(u32::from(dirfd))?
679679
.get_cap(DirCaps::PATH_FILESTAT_GET)?
680680
.get_path_filestat(
681-
path.as_str()?.expect("cannot use with shared memories; see https://github.com/bytecodealliance/wasmtime/issues/5235 (TODO)").deref(),
681+
path.as_cow()?.deref(),
682682
flags.contains(types::Lookupflags::SYMLINK_FOLLOW),
683683
)
684684
.await?;
@@ -705,7 +705,7 @@ impl wasi_snapshot_preview1::WasiSnapshotPreview1 for WasiCtx {
705705
.get_dir(u32::from(dirfd))?
706706
.get_cap(DirCaps::PATH_FILESTAT_SET_TIMES)?
707707
.set_times(
708-
path.as_str()?.expect("cannot use with shared memories; see https://github.com/bytecodealliance/wasmtime/issues/5235 (TODO)").deref(),
708+
path.as_cow()?.deref(),
709709
atim,
710710
mtim,
711711
flags.contains(types::Lookupflags::SYMLINK_FOLLOW),
@@ -736,9 +736,9 @@ impl wasi_snapshot_preview1::WasiSnapshotPreview1 for WasiCtx {
736736

737737
src_dir
738738
.hard_link(
739-
src_path.as_str()?.expect("cannot use with shared memories; see https://github.com/bytecodealliance/wasmtime/issues/5235 (TODO)").deref(),
739+
src_path.as_cow()?.deref(),
740740
target_dir.deref(),
741-
target_path.as_str()?.expect("cannot use with shared memories; see https://github.com/bytecodealliance/wasmtime/issues/5235 (TODO)").deref(),
741+
target_path.as_cow()?.deref(),
742742
)
743743
.await
744744
}
@@ -764,7 +764,7 @@ impl wasi_snapshot_preview1::WasiSnapshotPreview1 for WasiCtx {
764764

765765
let oflags = OFlags::from(&oflags);
766766
let fdflags = FdFlags::from(fdflags);
767-
let path = path.as_str()?.expect("cannot use with shared memories; see https://github.com/bytecodealliance/wasmtime/issues/5235 (TODO)");
767+
let path = path.as_cow()?;
768768
if oflags.contains(OFlags::DIRECTORY) {
769769
if oflags.contains(OFlags::CREATE)
770770
|| oflags.contains(OFlags::EXCLUSIVE)
@@ -813,7 +813,7 @@ impl wasi_snapshot_preview1::WasiSnapshotPreview1 for WasiCtx {
813813
.table()
814814
.get_dir(u32::from(dirfd))?
815815
.get_cap(DirCaps::READLINK)?
816-
.read_link(path.as_str()?.expect("cannot use with shared memories; see https://github.com/bytecodealliance/wasmtime/issues/5235 (TODO)").deref())
816+
.read_link(path.as_cow()?.deref())
817817
.await?
818818
.into_os_string()
819819
.into_string()
@@ -835,7 +835,7 @@ impl wasi_snapshot_preview1::WasiSnapshotPreview1 for WasiCtx {
835835
self.table()
836836
.get_dir(u32::from(dirfd))?
837837
.get_cap(DirCaps::REMOVE_DIRECTORY)?
838-
.remove_dir(path.as_str()?.expect("cannot use with shared memories; see https://github.com/bytecodealliance/wasmtime/issues/5235 (TODO)").deref())
838+
.remove_dir(path.as_cow()?.deref())
839839
.await
840840
}
841841

@@ -855,9 +855,9 @@ impl wasi_snapshot_preview1::WasiSnapshotPreview1 for WasiCtx {
855855
.get_cap(DirCaps::RENAME_TARGET)?;
856856
src_dir
857857
.rename(
858-
src_path.as_str()?.expect("cannot use with shared memories; see https://github.com/bytecodealliance/wasmtime/issues/5235 (TODO)").deref(),
858+
src_path.as_cow()?.deref(),
859859
dest_dir.deref(),
860-
dest_path.as_str()?.expect("cannot use with shared memories; see https://github.com/bytecodealliance/wasmtime/issues/5235 (TODO)").deref(),
860+
dest_path.as_cow()?.deref(),
861861
)
862862
.await
863863
}
@@ -871,7 +871,7 @@ impl wasi_snapshot_preview1::WasiSnapshotPreview1 for WasiCtx {
871871
self.table()
872872
.get_dir(u32::from(dirfd))?
873873
.get_cap(DirCaps::SYMLINK)?
874-
.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())
874+
.symlink(src_path.as_cow()?.deref(), dest_path.as_cow()?.deref())
875875
.await
876876
}
877877

@@ -883,8 +883,7 @@ impl wasi_snapshot_preview1::WasiSnapshotPreview1 for WasiCtx {
883883
self.table()
884884
.get_dir(u32::from(dirfd))?
885885
.get_cap(DirCaps::UNLINK_FILE)?
886-
.unlink_file(path.as_str()?
887-
.expect("cannot use with shared memories; see https://github.com/bytecodealliance/wasmtime/issues/5235 (TODO)").deref())
886+
.unlink_file(path.as_cow()?.deref())
888887
.await
889888
}
890889

crates/wasi-crypto/src/wiggle_interfaces/asymmetric_common.rs

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ impl super::wasi_ephemeral_crypto_asymmetric_common::WasiEphemeralCryptoAsymmetr
1717
alg_str: &wiggle::GuestPtr<'_, str>,
1818
options_handle: &guest_types::OptOptions,
1919
) -> Result<guest_types::Keypair, guest_types::CryptoErrno> {
20-
let alg_str = &*alg_str.as_str()?.expect("cannot use with shared memories; see https://github.com/bytecodealliance/wasmtime/issues/5235 (TODO)");
20+
let alg_str = &*alg_str.as_cow()?;
2121
let options_handle = match *options_handle {
2222
guest_types::OptOptions::Some(options_handle) => Some(options_handle),
2323
guest_types::OptOptions::None => None,
@@ -89,7 +89,7 @@ impl super::wasi_ephemeral_crypto_asymmetric_common::WasiEphemeralCryptoAsymmetr
8989
alg_str: &wiggle::GuestPtr<'_, str>,
9090
options_handle: &guest_types::OptOptions,
9191
) -> Result<guest_types::Keypair, guest_types::CryptoErrno> {
92-
let alg_str = &*alg_str.as_str()?.expect("cannot use with shared memories; see https://github.com/bytecodealliance/wasmtime/issues/5235 (TODO)");
92+
let alg_str = &*alg_str.as_cow()?;
9393
let options_handle = match *options_handle {
9494
guest_types::OptOptions::Some(options_handle) => Some(options_handle),
9595
guest_types::OptOptions::None => None,
@@ -107,7 +107,7 @@ impl super::wasi_ephemeral_crypto_asymmetric_common::WasiEphemeralCryptoAsymmetr
107107
encoded_len: guest_types::Size,
108108
encoding: guest_types::KeypairEncoding,
109109
) -> Result<guest_types::Keypair, guest_types::CryptoErrno> {
110-
let alg_str = &*alg_str.as_str()?.expect("cannot use with shared memories; see https://github.com/bytecodealliance/wasmtime/issues/5235 (TODO)");
110+
let alg_str = &*alg_str.as_cow()?;
111111
let encoded = &*encoded_ptr
112112
.as_array(encoded_len)
113113
.as_slice()?
@@ -167,7 +167,7 @@ impl super::wasi_ephemeral_crypto_asymmetric_common::WasiEphemeralCryptoAsymmetr
167167
encoded_len: guest_types::Size,
168168
encoding: guest_types::PublickeyEncoding,
169169
) -> Result<guest_types::Publickey, guest_types::CryptoErrno> {
170-
let alg_str = &*alg_str.as_str()?.expect("cannot use with shared memories; see https://github.com/bytecodealliance/wasmtime/issues/5235 (TODO)");
170+
let alg_str = &*alg_str.as_cow()?;
171171
let encoded = &*encoded_ptr
172172
.as_array(encoded_len)
173173
.as_slice()?
@@ -218,7 +218,7 @@ impl super::wasi_ephemeral_crypto_asymmetric_common::WasiEphemeralCryptoAsymmetr
218218
encoded_len: guest_types::Size,
219219
encoding: guest_types::SecretkeyEncoding,
220220
) -> Result<guest_types::Secretkey, guest_types::CryptoErrno> {
221-
let alg_str = &*alg_str.as_str()?.expect("cannot use with shared memories; see https://github.com/bytecodealliance/wasmtime/issues/5235 (TODO)");
221+
let alg_str = &*alg_str.as_cow()?;
222222
let encoded = &*encoded_ptr
223223
.as_array(encoded_len)
224224
.as_slice()?

crates/wasi-crypto/src/wiggle_interfaces/common.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ impl super::wasi_ephemeral_crypto_common::WasiEphemeralCryptoCommon for WasiCryp
2727
value_ptr: &wiggle::GuestPtr<'_, u8>,
2828
value_len: guest_types::Size,
2929
) -> Result<(), guest_types::CryptoErrno> {
30-
let name_str: &str = &*name_str.as_str()?.expect("cannot use with shared memories; see https://github.com/bytecodealliance/wasmtime/issues/5235 (TODO)");
30+
let name_str: &str = &*name_str.as_cow()?;
3131
let value: &[u8] = {
3232
&*value_ptr
3333
.as_array(value_len)
@@ -44,7 +44,7 @@ impl super::wasi_ephemeral_crypto_common::WasiEphemeralCryptoCommon for WasiCryp
4444
buffer_ptr: &wiggle::GuestPtr<'_, u8>,
4545
buffer_len: guest_types::Size,
4646
) -> Result<(), guest_types::CryptoErrno> {
47-
let name_str: &str = &*name_str.as_str()?.expect("cannot use with shared memories; see https://github.com/bytecodealliance/wasmtime/issues/5235 (TODO)");
47+
let name_str: &str = &*name_str.as_cow()?;
4848
let buffer: &'static mut [u8] = unsafe {
4949
std::mem::transmute(
5050
&mut *buffer_ptr
@@ -62,7 +62,7 @@ impl super::wasi_ephemeral_crypto_common::WasiEphemeralCryptoCommon for WasiCryp
6262
name_str: &wiggle::GuestPtr<'_, str>,
6363
value: u64,
6464
) -> Result<(), guest_types::CryptoErrno> {
65-
let name_str: &str = &*name_str.as_str()?.expect("cannot use with shared memories; see https://github.com/bytecodealliance/wasmtime/issues/5235 (TODO)");
65+
let name_str: &str = &*name_str.as_cow()?;
6666
Ok((&*self).options_set_u64(options_handle.into(), name_str, value)?)
6767
}
6868

crates/wasi-crypto/src/wiggle_interfaces/signatures.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ impl super::wasi_ephemeral_crypto_signatures::WasiEphemeralCryptoSignatures for
2222
encoded_len: guest_types::Size,
2323
encoding: guest_types::SignatureEncoding,
2424
) -> Result<guest_types::Signature, guest_types::CryptoErrno> {
25-
let alg_str = &*alg_str.as_str()?.expect("cannot use with shared memories; see https://github.com/bytecodealliance/wasmtime/issues/5235 (TODO)");
25+
let alg_str = &*alg_str.as_cow()?;
2626
let encoded = &*encoded_ptr
2727
.as_array(encoded_len)
2828
.as_slice()?

crates/wasi-crypto/src/wiggle_interfaces/symmetric.rs

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ impl super::wasi_ephemeral_crypto_symmetric::WasiEphemeralCryptoSymmetric for Wa
1212
alg_str: &wiggle::GuestPtr<'_, str>,
1313
options_handle: &guest_types::OptOptions,
1414
) -> Result<guest_types::SymmetricKey, guest_types::CryptoErrno> {
15-
let alg_str = &*alg_str.as_str()?.expect("cannot use with shared memories; see https://github.com/bytecodealliance/wasmtime/issues/5235 (TODO)");
15+
let alg_str = &*alg_str.as_cow()?;
1616
let options_handle = match *options_handle {
1717
guest_types::OptOptions::Some(options_handle) => Some(options_handle),
1818
guest_types::OptOptions::None => None,
@@ -86,7 +86,7 @@ impl super::wasi_ephemeral_crypto_symmetric::WasiEphemeralCryptoSymmetric for Wa
8686
alg_str: &wiggle::GuestPtr<'_, str>,
8787
options_handle: &guest_types::OptOptions,
8888
) -> Result<guest_types::SymmetricKey, guest_types::CryptoErrno> {
89-
let alg_str = &*alg_str.as_str()?.expect("cannot use with shared memories; see https://github.com/bytecodealliance/wasmtime/issues/5235 (TODO)");
89+
let alg_str = &*alg_str.as_cow()?;
9090
let options_handle = match *options_handle {
9191
guest_types::OptOptions::Some(options_handle) => Some(options_handle),
9292
guest_types::OptOptions::None => None,
@@ -102,7 +102,7 @@ impl super::wasi_ephemeral_crypto_symmetric::WasiEphemeralCryptoSymmetric for Wa
102102
raw_ptr: &wiggle::GuestPtr<'_, u8>,
103103
raw_len: guest_types::Size,
104104
) -> Result<guest_types::SymmetricKey, guest_types::CryptoErrno> {
105-
let alg_str = &*alg_str.as_str()?.expect("cannot use with shared memories; see https://github.com/bytecodealliance/wasmtime/issues/5235 (TODO)");
105+
let alg_str = &*alg_str.as_cow()?;
106106
let raw = &*raw_ptr
107107
.as_array(raw_len)
108108
.as_slice()?
@@ -153,7 +153,7 @@ impl super::wasi_ephemeral_crypto_symmetric::WasiEphemeralCryptoSymmetric for Wa
153153
key_handle: &guest_types::OptSymmetricKey,
154154
options_handle: &guest_types::OptOptions,
155155
) -> Result<guest_types::SymmetricState, guest_types::CryptoErrno> {
156-
let alg_str = &*alg_str.as_str()?.expect("cannot use with shared memories; see https://github.com/bytecodealliance/wasmtime/issues/5235 (TODO)");
156+
let alg_str = &*alg_str.as_cow()?;
157157
let key_handle = match *key_handle {
158158
guest_types::OptSymmetricKey::Some(key_handle) => Some(key_handle),
159159
guest_types::OptSymmetricKey::None => None,
@@ -178,7 +178,7 @@ impl super::wasi_ephemeral_crypto_symmetric::WasiEphemeralCryptoSymmetric for Wa
178178
value_ptr: &wiggle::GuestPtr<'_, u8>,
179179
value_max_len: guest_types::Size,
180180
) -> Result<guest_types::Size, guest_types::CryptoErrno> {
181-
let name_str: &str = &*name_str.as_str()?.expect("cannot use with shared memories; see https://github.com/bytecodealliance/wasmtime/issues/5235 (TODO)");
181+
let name_str: &str = &*name_str.as_cow()?;
182182
let value = &mut *value_ptr
183183
.as_array(value_max_len)
184184
.as_slice_mut()?
@@ -193,7 +193,7 @@ impl super::wasi_ephemeral_crypto_symmetric::WasiEphemeralCryptoSymmetric for Wa
193193
symmetric_state_handle: guest_types::SymmetricState,
194194
name_str: &wiggle::GuestPtr<'_, str>,
195195
) -> Result<u64, guest_types::CryptoErrno> {
196-
let name_str: &str = &*name_str.as_str()?.expect("cannot use with shared memories; see https://github.com/bytecodealliance/wasmtime/issues/5235 (TODO)");
196+
let name_str: &str = &*name_str.as_cow()?;
197197
Ok((&*self).options_get_u64(symmetric_state_handle.into(), name_str)?)
198198
}
199199

@@ -244,7 +244,7 @@ impl super::wasi_ephemeral_crypto_symmetric::WasiEphemeralCryptoSymmetric for Wa
244244
symmetric_state_handle: guest_types::SymmetricState,
245245
alg_str: &wiggle::GuestPtr<'_, str>,
246246
) -> Result<guest_types::SymmetricKey, guest_types::CryptoErrno> {
247-
let alg_str = &*alg_str.as_str()?.expect("cannot use with shared memories; see https://github.com/bytecodealliance/wasmtime/issues/5235 (TODO)");
247+
let alg_str = &*alg_str.as_cow()?;
248248
Ok((&*self)
249249
.symmetric_state_squeeze_key(symmetric_state_handle.into(), alg_str)?
250250
.into())

crates/wiggle/src/lib.rs

Lines changed: 68 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -708,7 +708,8 @@ impl<'a> GuestPtr<'a, str> {
708708
/// `GuestError` will be returned.
709709
///
710710
/// Additionally, because it is `unsafe` to have a `GuestStr` of shared
711-
/// memory, this function will return `None` in this case.
711+
/// memory, this function will return `None` in this case (see
712+
/// [`GuestPtr<'_, str>::as_cow`]).
712713
pub fn as_str(&self) -> Result<Option<GuestStr<'a>>, GuestError> {
713714
match self.as_bytes().as_unsafe_slice_mut()?.shared_borrow() {
714715
UnsafeBorrowResult::Ok(s) => Ok(Some(s.try_into()?)),
@@ -736,6 +737,24 @@ impl<'a> GuestPtr<'a, str> {
736737
UnsafeBorrowResult::Err(e) => Err(e),
737738
}
738739
}
740+
741+
/// Attempts to create a [`GuestStrCow<'_>`] from this pointer, performing
742+
/// bounds checks and utf-8 checks. Whereas [`GuestPtr::as_str`] will fail
743+
/// with `None` if attempting to access Wasm shared memory, this call will
744+
/// succeed: if used on shared memory, this function will copy the string
745+
/// into [`GuestStrCow::Copied`]. If the memory is non-shared, this returns
746+
/// a [`GuestStrCow::Borrowed`] (a thin wrapper over [`GuestStr<'_, T>]`).
747+
pub fn as_cow(&self) -> Result<GuestStrCow<'a>, GuestError> {
748+
match self.as_bytes().as_unsafe_slice_mut()?.shared_borrow() {
749+
UnsafeBorrowResult::Ok(s) => Ok(GuestStrCow::Borrowed(s.try_into()?)),
750+
UnsafeBorrowResult::Shared(_) => {
751+
let copied = self.as_bytes().to_vec()?;
752+
let utf8_string = String::from_utf8(copied).map_err(|e| e.utf8_error())?;
753+
Ok(GuestStrCow::Copied(utf8_string))
754+
}
755+
UnsafeBorrowResult::Err(e) => Err(e),
756+
}
757+
}
739758
}
740759

741760
impl<'a> GuestPtr<'a, [u8]> {
@@ -760,30 +779,6 @@ impl<T: ?Sized + Pointee> fmt::Debug for GuestPtr<'_, T> {
760779
}
761780
}
762781

763-
/// A smart pointer for distinguishing between different kinds of Wasm memory:
764-
/// shared and non-shared.
765-
///
766-
/// As with `GuestSlice`, this is usable as a `&'a [T]` via [`std::ops::Deref`].
767-
/// The major difference is that, for shared memories, the memory will be copied
768-
/// out of Wasm linear memory to avoid the possibility of concurrent mutation by
769-
/// another thread. This extra copy exists solely to maintain the Rust
770-
/// guarantees regarding `&[T]`.
771-
pub enum GuestCow<'a, T> {
772-
Borrowed(GuestSlice<'a, T>),
773-
Copied(Vec<T>),
774-
}
775-
776-
impl<'a, T> std::ops::Deref for GuestCow<'a, T> {
777-
type Target = [T];
778-
779-
fn deref(&self) -> &Self::Target {
780-
match self {
781-
GuestCow::Borrowed(s) => s,
782-
GuestCow::Copied(s) => s,
783-
}
784-
}
785-
}
786-
787782
/// A smart pointer to an shareable slice in guest memory.
788783
///
789784
/// Usable as a `&'a [T]` via [`std::ops::Deref`].
@@ -853,6 +848,30 @@ impl<'a, T> Drop for GuestSliceMut<'a, T> {
853848
}
854849
}
855850

851+
/// A smart pointer for distinguishing between different kinds of Wasm memory:
852+
/// shared and non-shared.
853+
///
854+
/// As with `GuestSlice`, this is usable as a `&'a [T]` via [`std::ops::Deref`].
855+
/// The major difference is that, for shared memories, the memory will be copied
856+
/// out of Wasm linear memory to avoid the possibility of concurrent mutation by
857+
/// another thread. This extra copy exists solely to maintain the Rust
858+
/// guarantees regarding `&[T]`.
859+
pub enum GuestCow<'a, T> {
860+
Borrowed(GuestSlice<'a, T>),
861+
Copied(Vec<T>),
862+
}
863+
864+
impl<'a, T> std::ops::Deref for GuestCow<'a, T> {
865+
type Target = [T];
866+
867+
fn deref(&self) -> &Self::Target {
868+
match self {
869+
GuestCow::Borrowed(s) => s,
870+
GuestCow::Copied(s) => s,
871+
}
872+
}
873+
}
874+
856875
/// A smart pointer to an `unsafe` slice in guest memory.
857876
///
858877
/// Accessing guest memory (e.g., WebAssembly linear memory) is inherently
@@ -1067,6 +1086,30 @@ impl<'a> std::ops::DerefMut for GuestStrMut<'a> {
10671086
}
10681087
}
10691088

1089+
/// A smart pointer to a `str` for distinguishing between different kinds of
1090+
/// Wasm memory: shared and non-shared.
1091+
///
1092+
/// As with `GuestStr`, this is usable as a `&'a str` via [`std::ops::Deref`].
1093+
/// The major difference is that, for shared memories, the string will be copied
1094+
/// out of Wasm linear memory to avoid the possibility of concurrent mutation by
1095+
/// another thread. This extra copy exists solely to maintain the Rust
1096+
/// guarantees regarding `&str`.
1097+
pub enum GuestStrCow<'a> {
1098+
Borrowed(GuestStr<'a>),
1099+
Copied(String),
1100+
}
1101+
1102+
impl<'a> std::ops::Deref for GuestStrCow<'a> {
1103+
type Target = str;
1104+
1105+
fn deref(&self) -> &Self::Target {
1106+
match self {
1107+
GuestStrCow::Borrowed(s) => s,
1108+
GuestStrCow::Copied(s) => s,
1109+
}
1110+
}
1111+
}
1112+
10701113
mod private {
10711114
pub trait Sealed {}
10721115
impl<T> Sealed for T {}

0 commit comments

Comments
 (0)