Skip to content

Commit

Permalink
canonicalize ExternRef::null() values properly
Browse files Browse the repository at this point in the history
  • Loading branch information
Robbepop committed Jan 31, 2023
1 parent 81a5277 commit 43bcba8
Showing 1 changed file with 27 additions and 11 deletions.
38 changes: 27 additions & 11 deletions crates/wasmi/src/externref.rs
Original file line number Diff line number Diff line change
Expand Up @@ -117,26 +117,18 @@ fn externref_null_to_zero() {

impl From<UntypedValue> for ExternRef {
fn from(untyped: UntypedValue) -> Self {
if untyped == UntypedValue::from(0u64) {
// Not sure if this early return and check
// is needed but better safe than sorry!
return Self::null();
}
// Safety: This operation is safe since there are no invalid
// bit patterns for [`ExternRef`] instances. Therefore
// this operation cannot produce invalid [`ExternRef`]
// instances even though the input [`UntypedValue`]
// was modified arbitrarily.
unsafe { Transposer { untyped }.externref }
unsafe { Transposer { untyped }.externref }.canonicalize()
}
}

impl From<ExternRef> for UntypedValue {
fn from(externref: ExternRef) -> Self {
if externref.is_null() {
// This early return is important to fix conversion bugs with `null`.
return UntypedValue::from(0u64);
}
let externref = externref.canonicalize();
// Safety: This operation is safe since there are no invalid
// bit patterns for [`UntypedValue`] instances. Therefore
// this operation cannot produce invalid [`UntypedValue`]
Expand All @@ -157,6 +149,30 @@ impl ExternRef {
.map(|object| ExternObject::new(ctx, object))
.map(Self::from_object)
.unwrap_or_else(Self::null)
.canonicalize()
}

/// Canonicalize `self` so that all `null` values have the same representation.
///
/// # Note
///
/// The underlying issue is that `ExternRef` has many possible values for the
/// `null` value. However, to simplify operating on encoded `ExternRef` instances
/// (encoded as `UntypedValue`) we want it to encode to exactly one `null`
/// value. The most trivial of all possible `null` values is `0_u64`, therefore
/// we canonicalize all `null` values to be represented by `0_u64`.
fn canonicalize(self) -> Self {
if self.is_null() {
// Safety: This is safe since `0u64` can be bit
// interpreted as a valid `ExternRef` value.
return unsafe {
Transposer {
untyped: UntypedValue::from(0u64),
}
.externref
};
}
self
}

/// Creates a new [`ExternRef`] to the given [`ExternObject`].
Expand All @@ -168,7 +184,7 @@ impl ExternRef {

/// Creates a new [`ExternRef`] which is `null`.
pub fn null() -> Self {
Self { inner: None }
Self { inner: None }.canonicalize()
}

/// Returns `true` if [`ExternRef`] is `null`.
Expand Down

0 comments on commit 43bcba8

Please sign in to comment.