Skip to content

Commit 686ea89

Browse files
authored
Make Val::to_raw a safe function (#11319)
This commit updates Wasmtime's core `Val::to_raw` function a safe function. This was previously marked as `unsafe` with documentation that the raw pointer could be invalid, but that's not a reason for the function itself to be `unsafe`. Usage of the returned value is still `unsafe`, but simply acquiring the value is not itself an unsafe operation. This additionally marks a number of GC-related `from_raw` functions as safe. Wasmtime's GC is safe in the face of heap corruption, so it's memory safe to pass in any 32-bit value. Documentation still indicates that panics are possible, however.
1 parent 2278bd2 commit 686ea89

File tree

11 files changed

+90
-85
lines changed

11 files changed

+90
-85
lines changed

crates/wasmtime/src/runtime/func.rs

Lines changed: 9 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1015,7 +1015,8 @@ impl Func {
10151015
/// Converts the raw representation of a `funcref` into an `Option<Func>`
10161016
///
10171017
/// This is intended to be used in conjunction with [`Func::new_unchecked`],
1018-
/// [`Func::call_unchecked`], and [`ValRaw`] with its `funcref` field.
1018+
/// [`Func::call_unchecked`], and [`ValRaw`] with its `funcref` field. This
1019+
/// is the dual of [`Func::to_raw`].
10191020
///
10201021
/// # Unsafety
10211022
///
@@ -1038,12 +1039,12 @@ impl Func {
10381039
/// This function returns a value that's suitable for writing into the
10391040
/// `funcref` field of the [`ValRaw`] structure.
10401041
///
1041-
/// # Unsafety
1042+
/// # Safety
10421043
///
1043-
/// The returned value is only valid for as long as the store is alive and
1044-
/// this function is properly rooted within it. Additionally this function
1045-
/// should not be liberally used since it's a very low-level knob.
1046-
pub unsafe fn to_raw(&self, mut store: impl AsContextMut) -> *mut c_void {
1044+
/// The returned value is only valid for as long as the store is alive.
1045+
/// This value is safe to pass to [`Func::from_raw`] so long as the same
1046+
/// `store` is provided.
1047+
pub fn to_raw(&self, mut store: impl AsContextMut) -> *mut c_void {
10471048
self.vm_func_ref(store.as_context_mut().0).as_ptr().cast()
10481049
}
10491050

@@ -1155,9 +1156,7 @@ impl Func {
11551156
debug_assert!(values_vec.is_empty());
11561157
values_vec.resize_with(values_vec_size, || ValRaw::v128(0));
11571158
for (arg, slot) in params.iter().cloned().zip(&mut values_vec) {
1158-
unsafe {
1159-
*slot = arg.to_raw(&mut *store)?;
1160-
}
1159+
*slot = arg.to_raw(&mut *store)?;
11611160
}
11621161

11631162
unsafe {
@@ -1255,9 +1254,7 @@ impl Func {
12551254
for (i, (ret, ty)) in results.iter().zip(ty.results()).enumerate() {
12561255
ret.ensure_matches_ty(caller.store.0, &ty)
12571256
.context("function attempted to return an incompatible value")?;
1258-
unsafe {
1259-
values_vec[i] = ret.to_raw(&mut caller.store)?;
1260-
}
1257+
values_vec[i] = ret.to_raw(&mut caller.store)?;
12611258
}
12621259

12631260
// Restore our `val_vec` back into the store so it's usable for the next

crates/wasmtime/src/runtime/gc/disabled/anyref.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -61,17 +61,17 @@ impl AnyRef {
6161
unreachable!()
6262
}
6363

64-
pub unsafe fn from_raw(_store: impl AsContextMut, raw: u32) -> Option<Rooted<Self>> {
64+
pub fn from_raw(_store: impl AsContextMut, raw: u32) -> Option<Rooted<Self>> {
6565
assert_eq!(raw, 0);
6666
None
6767
}
6868

69-
pub unsafe fn _from_raw(_store: &mut AutoAssertNoGc<'_>, raw: u32) -> Option<Rooted<Self>> {
69+
pub fn _from_raw(_store: &mut AutoAssertNoGc<'_>, raw: u32) -> Option<Rooted<Self>> {
7070
assert_eq!(raw, 0);
7171
None
7272
}
7373

74-
pub unsafe fn to_raw(&self, _store: impl AsContextMut) -> Result<u32> {
74+
pub fn to_raw(&self, _store: impl AsContextMut) -> Result<u32> {
7575
match *self {}
7676
}
7777

crates/wasmtime/src/runtime/gc/disabled/exnref.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,19 +16,19 @@ pub enum ExnRef {}
1616
impl GcRefImpl for ExnRef {}
1717

1818
impl ExnRef {
19-
pub unsafe fn from_raw(_store: impl AsContextMut, _raw: u32) -> Option<Rooted<Self>> {
19+
pub fn from_raw(_store: impl AsContextMut, _raw: u32) -> Option<Rooted<Self>> {
2020
None
2121
}
2222

2323
pub(crate) fn _from_raw(_store: &mut AutoAssertNoGc, _raw: u32) -> Option<Rooted<Self>> {
2424
None
2525
}
2626

27-
pub unsafe fn to_raw(&self, _store: impl AsContextMut) -> Result<u32> {
27+
pub fn to_raw(&self, _store: impl AsContextMut) -> Result<u32> {
2828
Ok(0)
2929
}
3030

31-
pub(crate) unsafe fn _to_raw(&self, _store: &mut AutoAssertNoGc<'_>) -> Result<u32> {
31+
pub(crate) fn _to_raw(&self, _store: &mut AutoAssertNoGc<'_>) -> Result<u32> {
3232
Ok(0)
3333
}
3434

crates/wasmtime/src/runtime/gc/disabled/externref.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -38,17 +38,17 @@ impl ExternRef {
3838
match *self {}
3939
}
4040

41-
pub unsafe fn from_raw(_store: impl AsContextMut, raw: u32) -> Option<Rooted<Self>> {
41+
pub fn from_raw(_store: impl AsContextMut, raw: u32) -> Option<Rooted<Self>> {
4242
assert_eq!(raw, 0);
4343
None
4444
}
4545

46-
pub unsafe fn _from_raw(_store: &mut AutoAssertNoGc<'_>, raw: u32) -> Option<Rooted<Self>> {
46+
pub fn _from_raw(_store: &mut AutoAssertNoGc<'_>, raw: u32) -> Option<Rooted<Self>> {
4747
assert_eq!(raw, 0);
4848
None
4949
}
5050

51-
pub unsafe fn to_raw(&self, _store: impl AsContextMut) -> Result<u32> {
51+
pub fn to_raw(&self, _store: impl AsContextMut) -> Result<u32> {
5252
match *self {}
5353
}
5454
}

crates/wasmtime/src/runtime/gc/enabled/anyref.rs

Lines changed: 20 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -249,27 +249,30 @@ impl AnyRef {
249249
/// This function assumes that `raw` is an `anyref` value which is currently
250250
/// rooted within the [`Store`].
251251
///
252-
/// # Unsafety
252+
/// # Correctness
253253
///
254-
/// This function is particularly `unsafe` because `raw` not only must be a
255-
/// valid `anyref` value produced prior by [`AnyRef::to_raw`] but it must
256-
/// also be correctly rooted within the store. When arguments are provided
257-
/// to a callback with [`Func::new_unchecked`], for example, or returned via
258-
/// [`Func::call_unchecked`], if a GC is performed within the store then
259-
/// floating `anyref` values are not rooted and will be GC'd, meaning that
260-
/// this function will no longer be safe to call with the values cleaned up.
261-
/// This function must be invoked *before* possible GC operations can happen
262-
/// (such as calling Wasm).
254+
/// This function is tricky to get right because `raw` not only must be a
255+
/// valid `anyref` value produced prior by [`AnyRef::to_raw`] but it
256+
/// must also be correctly rooted within the store. When arguments are
257+
/// provided to a callback with [`Func::new_unchecked`], for example, or
258+
/// returned via [`Func::call_unchecked`], if a GC is performed within the
259+
/// store then floating `anyref` values are not rooted and will be GC'd,
260+
/// meaning that this function will no longer be correct to call with the
261+
/// values cleaned up. This function must be invoked *before* possible GC
262+
/// operations can happen (such as calling Wasm).
263263
///
264-
/// When in doubt try to not use this. Instead use the safe Rust APIs of
265-
/// [`TypedFunc`] and friends.
264+
///
265+
/// When in doubt try to not use this. Instead use the Rust APIs of
266+
/// [`TypedFunc`] and friends. Note though that this function is not
267+
/// `unsafe` as any value can be passed in. Incorrect values can result in
268+
/// runtime panics, however, so care must still be taken with this method.
266269
///
267270
/// [`Func::call_unchecked`]: crate::Func::call_unchecked
268271
/// [`Func::new_unchecked`]: crate::Func::new_unchecked
269272
/// [`Store`]: crate::Store
270273
/// [`TypedFunc`]: crate::TypedFunc
271274
/// [`ValRaw`]: crate::ValRaw
272-
pub unsafe fn from_raw(mut store: impl AsContextMut, raw: u32) -> Option<Rooted<Self>> {
275+
pub fn from_raw(mut store: impl AsContextMut, raw: u32) -> Option<Rooted<Self>> {
273276
let mut store = AutoAssertNoGc::new(store.as_context_mut().0);
274277
Self::_from_raw(&mut store, raw)
275278
}
@@ -320,19 +323,19 @@ impl AnyRef {
320323
///
321324
/// Returns an error if this `anyref` has been unrooted.
322325
///
323-
/// # Unsafety
326+
/// # Correctness
324327
///
325-
/// Produces a raw value which is only safe to pass into a store if a GC
328+
/// Produces a raw value which is only valid to pass into a store if a GC
326329
/// doesn't happen between when the value is produce and when it's passed
327330
/// into the store.
328331
///
329332
/// [`ValRaw`]: crate::ValRaw
330-
pub unsafe fn to_raw(&self, mut store: impl AsContextMut) -> Result<u32> {
333+
pub fn to_raw(&self, mut store: impl AsContextMut) -> Result<u32> {
331334
let mut store = AutoAssertNoGc::new(store.as_context_mut().0);
332335
self._to_raw(&mut store)
333336
}
334337

335-
pub(crate) unsafe fn _to_raw(&self, store: &mut AutoAssertNoGc<'_>) -> Result<u32> {
338+
pub(crate) fn _to_raw(&self, store: &mut AutoAssertNoGc<'_>) -> Result<u32> {
336339
let gc_ref = self.inner.try_clone_gc_ref(store)?;
337340
let raw = if gc_ref.is_i31() {
338341
gc_ref.as_raw_non_zero_u32()

crates/wasmtime/src/runtime/gc/enabled/exnref.rs

Lines changed: 14 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -136,27 +136,29 @@ impl ExnRef {
136136
/// This function assumes that `raw` is an `exnref` value which is currently
137137
/// rooted within the [`Store`].
138138
///
139-
/// # Unsafety
139+
/// # Correctness
140140
///
141-
/// This function is particularly `unsafe` because `raw` not only must be a
141+
/// This function is tricky to get right because `raw` not only must be a
142142
/// valid `exnref` value produced prior by [`ExnRef::to_raw`] but it must
143143
/// also be correctly rooted within the store. When arguments are provided
144144
/// to a callback with [`Func::new_unchecked`], for example, or returned via
145145
/// [`Func::call_unchecked`], if a GC is performed within the store then
146146
/// floating `exnref` values are not rooted and will be GC'd, meaning that
147-
/// this function will no longer be safe to call with the values cleaned up.
148-
/// This function must be invoked *before* possible GC operations can happen
149-
/// (such as calling Wasm).
147+
/// this function will no longer be correct to call with the values cleaned
148+
/// up. This function must be invoked *before* possible GC operations can
149+
/// happen (such as calling Wasm).
150150
///
151-
/// When in doubt try to not use this. Instead use the safe Rust APIs of
152-
/// [`TypedFunc`] and friends.
151+
/// When in doubt try to not use this. Instead use the Rust APIs of
152+
/// [`TypedFunc`] and friends. Note though that this function is not
153+
/// `unsafe` as any value can be passed in. Incorrect values can result in
154+
/// runtime panics, however, so care must still be taken with this method.
153155
///
154156
/// [`Func::call_unchecked`]: crate::Func::call_unchecked
155157
/// [`Func::new_unchecked`]: crate::Func::new_unchecked
156158
/// [`Store`]: crate::Store
157159
/// [`TypedFunc`]: crate::TypedFunc
158160
/// [`ValRaw`]: crate::ValRaw
159-
pub unsafe fn from_raw(mut store: impl AsContextMut, raw: u32) -> Option<Rooted<Self>> {
161+
pub fn from_raw(mut store: impl AsContextMut, raw: u32) -> Option<Rooted<Self>> {
160162
let mut store = AutoAssertNoGc::new(store.as_context_mut().0);
161163
Self::_from_raw(&mut store, raw)
162164
}
@@ -402,19 +404,19 @@ impl ExnRef {
402404
///
403405
/// Returns an error if this `exnref` has been unrooted.
404406
///
405-
/// # Unsafety
407+
/// # Correctness
406408
///
407-
/// Produces a raw value which is only safe to pass into a store if a GC
409+
/// Produces a raw value which is only valid to pass into a store if a GC
408410
/// doesn't happen between when the value is produce and when it's passed
409411
/// into the store.
410412
///
411413
/// [`ValRaw`]: crate::ValRaw
412-
pub unsafe fn to_raw(&self, mut store: impl AsContextMut) -> Result<u32> {
414+
pub fn to_raw(&self, mut store: impl AsContextMut) -> Result<u32> {
413415
let mut store = AutoAssertNoGc::new(store.as_context_mut().0);
414416
self._to_raw(&mut store)
415417
}
416418

417-
pub(crate) unsafe fn _to_raw(&self, store: &mut AutoAssertNoGc<'_>) -> Result<u32> {
419+
pub(crate) fn _to_raw(&self, store: &mut AutoAssertNoGc<'_>) -> Result<u32> {
418420
let gc_ref = self.inner.try_clone_gc_ref(store)?;
419421
let raw = if gc_ref.is_i31() {
420422
gc_ref.as_raw_non_zero_u32()

crates/wasmtime/src/runtime/gc/enabled/externref.rs

Lines changed: 19 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -548,27 +548,30 @@ impl ExternRef {
548548
/// This function assumes that `raw` is an externref value which is
549549
/// currently rooted within the [`Store`].
550550
///
551-
/// # Unsafety
551+
/// # Correctness
552552
///
553-
/// This function is particularly `unsafe` because `raw` not only must be a
554-
/// valid externref value produced prior by `to_raw` but it must also be
555-
/// correctly rooted within the store. When arguments are provided to a
556-
/// callback with [`Func::new_unchecked`], for example, or returned via
557-
/// [`Func::call_unchecked`], if a GC is performed within the store then
558-
/// floating externref values are not rooted and will be GC'd, meaning that
559-
/// this function will no longer be safe to call with the values cleaned up.
560-
/// This function must be invoked *before* possible GC operations can happen
561-
/// (such as calling wasm).
553+
/// This function is tricky to get right because `raw` not only must be a
554+
/// valid `externref` value produced prior by [`ExternRef::to_raw`] but it
555+
/// must also be correctly rooted within the store. When arguments are
556+
/// provided to a callback with [`Func::new_unchecked`], for example, or
557+
/// returned via [`Func::call_unchecked`], if a GC is performed within the
558+
/// store then floating `externref` values are not rooted and will be GC'd,
559+
/// meaning that this function will no longer be correct to call with the
560+
/// values cleaned up. This function must be invoked *before* possible GC
561+
/// operations can happen (such as calling Wasm).
562562
///
563-
/// When in doubt try to not use this. Instead use the safe Rust APIs of
564-
/// [`TypedFunc`] and friends.
563+
///
564+
/// When in doubt try to not use this. Instead use the Rust APIs of
565+
/// [`TypedFunc`] and friends. Note though that this function is not
566+
/// `unsafe` as any value can be passed in. Incorrect values can result in
567+
/// runtime panics, however, so care must still be taken with this method.
565568
///
566569
/// [`Func::call_unchecked`]: crate::Func::call_unchecked
567570
/// [`Func::new_unchecked`]: crate::Func::new_unchecked
568571
/// [`Store`]: crate::Store
569572
/// [`TypedFunc`]: crate::TypedFunc
570573
/// [`ValRaw`]: crate::ValRaw
571-
pub unsafe fn from_raw(mut store: impl AsContextMut, raw: u32) -> Option<Rooted<ExternRef>> {
574+
pub fn from_raw(mut store: impl AsContextMut, raw: u32) -> Option<Rooted<ExternRef>> {
572575
let mut store = AutoAssertNoGc::new(store.as_context_mut().0);
573576
Self::_from_raw(&mut store, raw)
574577
}
@@ -585,14 +588,14 @@ impl ExternRef {
585588
///
586589
/// Returns an error if this `externref` has been unrooted.
587590
///
588-
/// # Unsafety
591+
/// # Correctness
589592
///
590-
/// Produces a raw value which is only safe to pass into a store if a GC
593+
/// Produces a raw value which is only valid to pass into a store if a GC
591594
/// doesn't happen between when the value is produce and when it's passed
592595
/// into the store.
593596
///
594597
/// [`ValRaw`]: crate::ValRaw
595-
pub unsafe fn to_raw(&self, mut store: impl AsContextMut) -> Result<u32> {
598+
pub fn to_raw(&self, mut store: impl AsContextMut) -> Result<u32> {
596599
let mut store = AutoAssertNoGc::new(store.as_context_mut().0);
597600
self._to_raw(&mut store)
598601
}

crates/wasmtime/src/runtime/values.rs

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -230,11 +230,12 @@ impl Val {
230230
/// Returns an error if this value is a GC reference and the GC reference
231231
/// has been unrooted.
232232
///
233-
/// # Unsafety
233+
/// # Safety
234234
///
235-
/// This method is unsafe for the reasons that [`ExternRef::to_raw`] and
236-
/// [`Func::to_raw`] are unsafe.
237-
pub unsafe fn to_raw(&self, store: impl AsContextMut) -> Result<ValRaw> {
235+
/// The returned [`ValRaw`] does not carry type information and is only safe
236+
/// to use within the context of this store itself. For more information see
237+
/// [`ExternRef::to_raw`] and [`Func::to_raw`].
238+
pub fn to_raw(&self, store: impl AsContextMut) -> Result<ValRaw> {
238239
match self {
239240
Val::I32(i) => Ok(ValRaw::i32(*i)),
240241
Val::I64(i) => Ok(ValRaw::i64(*i)),

crates/wasmtime/src/runtime/vm/const_expr.rs

Lines changed: 10 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -88,7 +88,7 @@ impl ConstEvalContext {
8888

8989
let allocator = StructRefPre::_new(store, struct_ty);
9090
let struct_ref = unsafe { StructRef::new_maybe_async(store, &allocator, &fields)? };
91-
let raw = unsafe { struct_ref.to_anyref()._to_raw(store)? };
91+
let raw = struct_ref.to_anyref()._to_raw(store)?;
9292
Ok(ValRaw::anyref(raw))
9393
}
9494

@@ -293,7 +293,7 @@ impl ConstExprEvaluator {
293293
let array = unsafe { ArrayRef::new_maybe_async(&mut store, &pre, &elem, len)? };
294294

295295
self.stack
296-
.push(unsafe { ValRaw::anyref(array.to_anyref()._to_raw(&mut store)?) });
296+
.push(ValRaw::anyref(array.to_anyref()._to_raw(&mut store)?));
297297
}
298298

299299
#[cfg(feature = "gc")]
@@ -311,7 +311,7 @@ impl ConstExprEvaluator {
311311
let array = unsafe { ArrayRef::new_maybe_async(&mut store, &pre, &elem, len)? };
312312

313313
self.stack
314-
.push(unsafe { ValRaw::anyref(array.to_anyref()._to_raw(&mut store)?) });
314+
.push(ValRaw::anyref(array.to_anyref()._to_raw(&mut store)?));
315315
}
316316

317317
#[cfg(feature = "gc")]
@@ -347,7 +347,7 @@ impl ConstExprEvaluator {
347347
unsafe { ArrayRef::new_fixed_maybe_async(&mut store, &pre, &elems)? };
348348

349349
self.stack
350-
.push(unsafe { ValRaw::anyref(array.to_anyref()._to_raw(&mut store)?) });
350+
.push(ValRaw::anyref(array.to_anyref()._to_raw(&mut store)?));
351351
}
352352

353353
#[cfg(feature = "gc")]
@@ -363,13 +363,12 @@ impl ConstExprEvaluator {
363363

364364
#[cfg(feature = "gc")]
365365
ConstOp::AnyConvertExtern => {
366-
let result = match ExternRef::_from_raw(&mut store, self.pop()?.get_externref())
367-
{
368-
Some(externref) => unsafe {
369-
AnyRef::_convert_extern(&mut store, externref)?._to_raw(&mut store)?
370-
},
371-
None => 0,
372-
};
366+
let result =
367+
match ExternRef::_from_raw(&mut store, self.pop()?.get_externref()) {
368+
Some(externref) => AnyRef::_convert_extern(&mut store, externref)?
369+
._to_raw(&mut store)?,
370+
None => 0,
371+
};
373372
self.stack.push(ValRaw::anyref(result));
374373
}
375374
}

tests/all/gc.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -818,8 +818,8 @@ fn to_raw_from_raw_doesnt_leak() -> Result<()> {
818818
{
819819
let mut scope = RootScope::new(&mut store);
820820
let x = ExternRef::new(&mut scope, SetFlagOnDrop(flag.clone()))?;
821-
let raw = unsafe { x.to_raw(&mut scope)? };
822-
let _x = unsafe { ExternRef::from_raw(&mut scope, raw) };
821+
let raw = x.to_raw(&mut scope)?;
822+
let _x = ExternRef::from_raw(&mut scope, raw);
823823
}
824824

825825
store.gc(None);

0 commit comments

Comments
 (0)