wasmer_types/
value.rs

1use core::{fmt, marker::PhantomData, mem::MaybeUninit};
2
3/// Raw representation of a WebAssembly value.
4///
5/// In most cases you will want to use the type-safe `Value` wrapper instead.
6#[allow(missing_docs)]
7#[repr(C)]
8#[derive(Copy, Clone)]
9pub union RawValue {
10    pub i32: i32,
11    pub i64: i64,
12    pub u32: u32,
13    pub u64: u64,
14    pub f32: f32,
15    pub f64: f64,
16    pub i128: i128,
17    pub u128: u128,
18    pub funcref: usize,
19    pub externref: usize,
20    pub exnref: u32,
21    pub bytes: [u8; 16],
22}
23
24impl RawValue {
25    #[inline]
26    fn with_zeroed_bytes(f: impl FnOnce(&mut Self)) -> Self {
27        let mut raw = Self::default();
28        f(&mut raw);
29        raw
30    }
31}
32
33impl From<i32> for RawValue {
34    fn from(value: i32) -> Self {
35        Self::with_zeroed_bytes(|raw| raw.i32 = value)
36    }
37}
38
39impl From<i64> for RawValue {
40    fn from(value: i64) -> Self {
41        Self::with_zeroed_bytes(|raw| raw.i64 = value)
42    }
43}
44
45impl From<f32> for RawValue {
46    fn from(value: f32) -> Self {
47        Self::with_zeroed_bytes(|raw| raw.f32 = value)
48    }
49}
50
51impl From<f64> for RawValue {
52    fn from(value: f64) -> Self {
53        Self::with_zeroed_bytes(|raw| raw.f64 = value)
54    }
55}
56
57impl Default for RawValue {
58    fn default() -> Self {
59        Self { bytes: [0; 16] }
60    }
61}
62
63impl fmt::Debug for RawValue {
64    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
65        f.debug_struct("RawValue")
66            .field("bytes", unsafe { &self.bytes })
67            .finish()
68    }
69}
70
71macro_rules! partial_eq {
72    ($($t:ty => $f:tt),*) => ($(
73        impl PartialEq<$t> for RawValue {
74            fn eq(&self, o: &$t) -> bool {
75                unsafe { self.$f == *o }
76            }
77        }
78    )*)
79}
80
81partial_eq! {
82    i32 => i32,
83    u32 => u32,
84    i64 => i64,
85    u64 => u64,
86    f32 => f32,
87    f64 => f64,
88    i128 => i128,
89    u128 => u128
90}
91
92impl PartialEq for RawValue {
93    fn eq(&self, o: &Self) -> bool {
94        unsafe { self.u128 == o.u128 }
95    }
96}
97
98/// Trait for a Value type. A Value type is a type that is always valid and may
99/// be safely copied.
100///
101/// # Safety
102///
103/// To maintain safety, types which implement this trait must be valid for all
104/// bit patterns. This means that it cannot contain enums, `bool`, references,
105/// etc.
106///
107/// Concretely a `u32` is a Value type because every combination of 32 bits is
108/// a valid `u32`. However a `bool` is _not_ a Value type because any bit patterns
109/// other than `0` and `1` are invalid in Rust and may cause undefined behavior if
110/// a `bool` is constructed from those bytes.
111///
112/// Additionally this trait has a method which zeros out any uninitializes bytes
113/// prior to writing them to Wasm memory, which prevents information leaks into
114/// the sandbox.
115pub unsafe trait ValueType: Copy {
116    /// This method is passed a byte slice which contains the byte
117    /// representation of `self`. It must zero out any bytes which are
118    /// uninitialized (e.g. padding bytes).
119    fn zero_padding_bytes(&self, bytes: &mut [MaybeUninit<u8>]);
120}
121
122// Trivial implementations for primitive types and arrays of them.
123macro_rules! primitives {
124    ($($t:ident)*) => ($(
125        unsafe impl ValueType for $t {
126            #[inline]
127            fn zero_padding_bytes(&self, _bytes: &mut [MaybeUninit<u8>]) {}
128        }
129        unsafe impl<const N: usize> ValueType for [$t; N] {
130            #[inline]
131            fn zero_padding_bytes(&self, _bytes: &mut [MaybeUninit<u8>]) {}
132        }
133    )*)
134}
135primitives! {
136    bool
137    i8 u8
138    i16 u16
139    i32 u32
140    i64 u64
141    i128 u128
142    isize usize
143    f32 f64
144}
145
146// This impl for PhantomData allows #[derive(ValueType)] to work with types
147// that contain a PhantomData.
148unsafe impl<T: ?Sized> ValueType for PhantomData<T> {
149    #[inline]
150    fn zero_padding_bytes(&self, _bytes: &mut [MaybeUninit<u8>]) {}
151}
152
153#[cfg(test)]
154mod tests {
155    use super::RawValue;
156
157    #[test]
158    fn raw_value_zero_initialized() {
159        #[cfg(target_endian = "little")]
160        {
161            assert_eq!(RawValue::from(123i32), 123i64);
162            assert_eq!(RawValue::from(0.0f32), 0i64);
163            assert_eq!(RawValue::from(0.0f32), 0u128);
164
165            const VAL: f64 = 123.456;
166            assert_eq!(
167                RawValue::from(VAL),
168                u128::from(u64::from_ne_bytes(VAL.to_ne_bytes()))
169            );
170        }
171    }
172}