Coverage Report

Created: 2024-10-16 07:58

/src/wasmer/lib/api/src/ptr.rs
Line
Count
Source (jump to first uncovered line)
1
use crate::access::WasmRefAccess;
2
use crate::mem_access::{MemoryAccessError, WasmRef, WasmSlice};
3
use crate::{AsStoreRef, FromToNativeWasmType, MemoryView, NativeWasmTypeInto};
4
use std::convert::TryFrom;
5
use std::{fmt, marker::PhantomData, mem};
6
pub use wasmer_types::Memory32;
7
pub use wasmer_types::Memory64;
8
pub use wasmer_types::MemorySize;
9
use wasmer_types::ValueType;
10
11
/// Alias for `WasmPtr<T, Memory64>.
12
pub type WasmPtr64<T> = WasmPtr<T, Memory64>;
13
14
/// A zero-cost type that represents a pointer to something in Wasm linear
15
/// memory.
16
///
17
/// This type can be used directly in the host function arguments:
18
/// ```
19
/// # use wasmer::Memory;
20
/// # use wasmer::WasmPtr;
21
/// # use wasmer::FunctionEnvMut;
22
/// pub fn host_import(mut env: FunctionEnvMut<()>, memory: Memory, ptr: WasmPtr<u32>) {
23
///     let memory = memory.view(&env);
24
///     let derefed_ptr = ptr.deref(&memory);
25
///     let inner_val: u32 = derefed_ptr.read().expect("pointer in bounds");
26
///     println!("Got {} from Wasm memory address 0x{:X}", inner_val, ptr.offset());
27
///     // update the value being pointed to
28
///     derefed_ptr.write(inner_val + 1).expect("pointer in bounds");
29
/// }
30
/// ```
31
///
32
/// This type can also be used with primitive-filled structs, but be careful of
33
/// guarantees required by `ValueType`.
34
/// ```
35
/// # use wasmer::Memory;
36
/// # use wasmer::WasmPtr;
37
/// # use wasmer::ValueType;
38
/// # use wasmer::FunctionEnvMut;
39
///
40
/// // This is safe as the 12 bytes represented by this struct
41
/// // are valid for all bit combinations.
42
/// #[derive(Copy, Clone, Debug, ValueType)]
43
/// #[repr(C)]
44
/// struct V3 {
45
///     x: f32,
46
///     y: f32,
47
///     z: f32
48
/// }
49
///
50
/// fn update_vector_3(mut env: FunctionEnvMut<()>, memory: Memory, ptr: WasmPtr<V3>) {
51
///     let memory = memory.view(&env);
52
///     let derefed_ptr = ptr.deref(&memory);
53
///     let mut inner_val: V3 = derefed_ptr.read().expect("pointer in bounds");
54
///     println!("Got {:?} from Wasm memory address 0x{:X}", inner_val, ptr.offset());
55
///     // update the value being pointed to
56
///     inner_val.x = 10.4;
57
///     derefed_ptr.write(inner_val).expect("pointer in bounds");
58
/// }
59
/// ```
60
#[repr(transparent)]
61
pub struct WasmPtr<T, M: MemorySize = Memory32> {
62
    offset: M::Offset,
63
    _phantom: PhantomData<T>,
64
}
65
66
impl<T, M: MemorySize> WasmPtr<T, M> {
67
    /// Create a new `WasmPtr` at the given offset.
68
    #[inline]
69
0
    pub fn new(offset: M::Offset) -> Self {
70
0
        Self {
71
0
            offset,
72
0
            _phantom: PhantomData,
73
0
        }
74
0
    }
Unexecuted instantiation: <wasmer::ptr::WasmPtr<_, _>>::new
Unexecuted instantiation: <wasmer::ptr::WasmPtr<_, _>>::new
75
76
    /// Get the offset into Wasm linear memory for this `WasmPtr`.
77
    #[inline]
78
0
    pub fn offset(&self) -> M::Offset {
79
0
        self.offset
80
0
    }
Unexecuted instantiation: <wasmer::ptr::WasmPtr<_, _>>::offset
Unexecuted instantiation: <wasmer::ptr::WasmPtr<_, _>>::offset
81
82
    /// Casts this `WasmPtr` to a `WasmPtr` of a different type.
83
    #[inline]
84
0
    pub fn cast<U>(self) -> WasmPtr<U, M> {
85
0
        WasmPtr {
86
0
            offset: self.offset,
87
0
            _phantom: PhantomData,
88
0
        }
89
0
    }
Unexecuted instantiation: <wasmer::ptr::WasmPtr<_, _>>::cast::<_>
Unexecuted instantiation: <wasmer::ptr::WasmPtr<_, _>>::cast::<_>
90
91
    /// Returns a null `UserPtr`.
92
    #[inline]
93
0
    pub fn null() -> Self {
94
0
        Self::new(M::ZERO)
95
0
    }
Unexecuted instantiation: <wasmer::ptr::WasmPtr<_, _>>::null
Unexecuted instantiation: <wasmer::ptr::WasmPtr<_, _>>::null
96
97
    /// Checks whether the `WasmPtr` is null.
98
    #[inline]
99
0
    pub fn is_null(&self) -> bool {
100
0
        self.offset.into() == 0
101
0
    }
Unexecuted instantiation: <wasmer::ptr::WasmPtr<_, _>>::is_null
Unexecuted instantiation: <wasmer::ptr::WasmPtr<_, _>>::is_null
102
103
    /// Calculates an offset from the current pointer address. The argument is
104
    /// in units of `T`.
105
    ///
106
    /// This method returns an error if an address overflow occurs.
107
    #[inline]
108
0
    pub fn add_offset(self, offset: M::Offset) -> Result<Self, MemoryAccessError> {
109
0
        let base = self.offset.into();
110
0
        let index = offset.into();
111
0
        let offset = index
112
0
            .checked_mul(mem::size_of::<T>() as u64)
113
0
            .ok_or(MemoryAccessError::Overflow)?;
114
0
        let address = base
115
0
            .checked_add(offset)
116
0
            .ok_or(MemoryAccessError::Overflow)?;
117
0
        let address = M::Offset::try_from(address).map_err(|_| MemoryAccessError::Overflow)?;
Unexecuted instantiation: <wasmer::ptr::WasmPtr<_, _>>::add_offset::{closure#0}
Unexecuted instantiation: <wasmer::ptr::WasmPtr<_, _>>::add_offset::{closure#0}
118
0
        Ok(Self::new(address))
119
0
    }
Unexecuted instantiation: <wasmer::ptr::WasmPtr<_, _>>::add_offset
Unexecuted instantiation: <wasmer::ptr::WasmPtr<_, _>>::add_offset
120
121
    /// Calculates an offset from the current pointer address. The argument is
122
    /// in units of `T`.
123
    ///
124
    /// This method returns an error if an address underflow occurs.
125
    #[inline]
126
0
    pub fn sub_offset(self, offset: M::Offset) -> Result<Self, MemoryAccessError> {
127
0
        let base = self.offset.into();
128
0
        let index = offset.into();
129
0
        let offset = index
130
0
            .checked_mul(mem::size_of::<T>() as u64)
131
0
            .ok_or(MemoryAccessError::Overflow)?;
132
0
        let address = base
133
0
            .checked_sub(offset)
134
0
            .ok_or(MemoryAccessError::Overflow)?;
135
0
        let address = M::Offset::try_from(address).map_err(|_| MemoryAccessError::Overflow)?;
Unexecuted instantiation: <wasmer::ptr::WasmPtr<_, _>>::sub_offset::{closure#0}
Unexecuted instantiation: <wasmer::ptr::WasmPtr<_, _>>::sub_offset::{closure#0}
136
0
        Ok(Self::new(address))
137
0
    }
Unexecuted instantiation: <wasmer::ptr::WasmPtr<_, _>>::sub_offset
Unexecuted instantiation: <wasmer::ptr::WasmPtr<_, _>>::sub_offset
138
}
139
140
impl<T: ValueType, M: MemorySize> WasmPtr<T, M> {
141
    /// Creates a `WasmRef` from this `WasmPtr` which allows reading and
142
    /// mutating of the value being pointed to.
143
    #[inline]
144
0
    pub fn deref<'a>(&self, view: &'a MemoryView) -> WasmRef<'a, T> {
145
0
        WasmRef::new(view, self.offset.into())
146
0
    }
Unexecuted instantiation: <wasmer::ptr::WasmPtr<_, _>>::deref
Unexecuted instantiation: <wasmer::ptr::WasmPtr<_, _>>::deref
147
148
    /// Reads the address pointed to by this `WasmPtr` in a memory.
149
    #[inline]
150
0
    pub fn read(&self, view: &MemoryView) -> Result<T, MemoryAccessError> {
151
0
        self.deref(view).read()
152
0
    }
Unexecuted instantiation: <wasmer::ptr::WasmPtr<_, _>>::read
Unexecuted instantiation: <wasmer::ptr::WasmPtr<_, _>>::read
153
154
    /// Writes to the address pointed to by this `WasmPtr` in a memory.
155
    #[inline]
156
0
    pub fn write(&self, view: &MemoryView, val: T) -> Result<(), MemoryAccessError> {
157
0
        self.deref(view).write(val)
158
0
    }
Unexecuted instantiation: <wasmer::ptr::WasmPtr<_, _>>::write
Unexecuted instantiation: <wasmer::ptr::WasmPtr<_, _>>::write
159
160
    /// Creates a `WasmSlice` starting at this `WasmPtr` which allows reading
161
    /// and mutating of an array of value being pointed to.
162
    ///
163
    /// Returns a `MemoryAccessError` if the slice length overflows a 64-bit
164
    /// address.
165
    #[inline]
166
0
    pub fn slice<'a>(
167
0
        &self,
168
0
        view: &'a MemoryView,
169
0
        len: M::Offset,
170
0
    ) -> Result<WasmSlice<'a, T>, MemoryAccessError> {
171
0
        WasmSlice::new(view, self.offset.into(), len.into())
172
0
    }
Unexecuted instantiation: <wasmer::ptr::WasmPtr<_, _>>::slice
Unexecuted instantiation: <wasmer::ptr::WasmPtr<_, _>>::slice
173
174
    /// Reads a sequence of values from this `WasmPtr` until a value that
175
    /// matches the given condition is found.
176
    ///
177
    /// This last value is not included in the returned vector.
178
    #[inline]
179
0
    pub fn read_until(
180
0
        &self,
181
0
        view: &MemoryView,
182
0
        mut end: impl FnMut(&T) -> bool,
183
0
    ) -> Result<Vec<T>, MemoryAccessError> {
184
0
        let mut vec = Vec::new();
185
0
        for i in 0u64.. {
186
0
            let i = M::Offset::try_from(i).map_err(|_| MemoryAccessError::Overflow)?;
Unexecuted instantiation: <wasmer::ptr::WasmPtr<_, _>>::read_until::<_>::{closure#0}
Unexecuted instantiation: <wasmer::ptr::WasmPtr<_, _>>::read_until::<_>::{closure#0}
187
0
            let val = self.add_offset(i)?.deref(view).read()?;
188
0
            if end(&val) {
189
0
                break;
190
0
            }
191
0
            vec.push(val);
192
        }
193
0
        Ok(vec)
194
0
    }
Unexecuted instantiation: <wasmer::ptr::WasmPtr<_, _>>::read_until::<_>
Unexecuted instantiation: <wasmer::ptr::WasmPtr<_, _>>::read_until::<_>
195
196
    /// Creates a `WasmAccess`
197
    #[inline]
198
0
    pub fn access<'a>(
199
0
        &self,
200
0
        view: &'a MemoryView,
201
0
    ) -> Result<WasmRefAccess<'a, T>, MemoryAccessError> {
202
0
        self.deref(view).access()
203
0
    }
Unexecuted instantiation: <wasmer::ptr::WasmPtr<_, _>>::access
Unexecuted instantiation: <wasmer::ptr::WasmPtr<_, _>>::access
204
}
205
206
impl<M: MemorySize> WasmPtr<u8, M> {
207
    /// Reads a UTF-8 string from the `WasmPtr` with the given length.
208
    ///
209
    /// This method is safe to call even if the memory is being concurrently
210
    /// modified.
211
    #[inline]
212
0
    pub fn read_utf8_string(
213
0
        &self,
214
0
        view: &MemoryView,
215
0
        len: M::Offset,
216
0
    ) -> Result<String, MemoryAccessError> {
217
0
        let vec = self.slice(view, len)?.read_to_vec()?;
218
0
        Ok(String::from_utf8(vec)?)
219
0
    }
Unexecuted instantiation: <wasmer::ptr::WasmPtr<u8, _>>::read_utf8_string
Unexecuted instantiation: <wasmer::ptr::WasmPtr<u8, _>>::read_utf8_string
220
221
    /// Reads a null-terminated UTF-8 string from the `WasmPtr`.
222
    ///
223
    /// This method is safe to call even if the memory is being concurrently
224
    /// modified.
225
    #[inline]
226
0
    pub fn read_utf8_string_with_nul(
227
0
        &self,
228
0
        view: &MemoryView,
229
0
    ) -> Result<String, MemoryAccessError> {
230
0
        let vec = self.read_until(view, |&byte| byte == 0)?;
Unexecuted instantiation: <wasmer::ptr::WasmPtr<u8, _>>::read_utf8_string_with_nul::{closure#0}
Unexecuted instantiation: <wasmer::ptr::WasmPtr<u8, _>>::read_utf8_string_with_nul::{closure#0}
231
0
        Ok(String::from_utf8(vec)?)
232
0
    }
Unexecuted instantiation: <wasmer::ptr::WasmPtr<u8, _>>::read_utf8_string_with_nul
Unexecuted instantiation: <wasmer::ptr::WasmPtr<u8, _>>::read_utf8_string_with_nul
233
}
234
235
unsafe impl<T: ValueType, M: MemorySize> FromToNativeWasmType for WasmPtr<T, M>
236
where
237
    <M as wasmer_types::MemorySize>::Native: NativeWasmTypeInto,
238
{
239
    type Native = M::Native;
240
241
0
    fn to_native(self) -> Self::Native {
242
0
        M::offset_to_native(self.offset)
243
0
    }
Unexecuted instantiation: <wasmer::ptr::WasmPtr<_, _> as wasmer::native_type::FromToNativeWasmType>::to_native
Unexecuted instantiation: <wasmer::ptr::WasmPtr<_, _> as wasmer::native_type::FromToNativeWasmType>::to_native
244
0
    fn from_native(n: Self::Native) -> Self {
245
0
        Self {
246
0
            offset: M::native_to_offset(n),
247
0
            _phantom: PhantomData,
248
0
        }
249
0
    }
Unexecuted instantiation: <wasmer::ptr::WasmPtr<_, _> as wasmer::native_type::FromToNativeWasmType>::from_native
Unexecuted instantiation: <wasmer::ptr::WasmPtr<_, _> as wasmer::native_type::FromToNativeWasmType>::from_native
250
    #[inline]
251
0
    fn is_from_store(&self, _store: &impl AsStoreRef) -> bool {
252
0
        true // in Javascript there are no different stores
253
0
    }
Unexecuted instantiation: <wasmer::ptr::WasmPtr<_, _> as wasmer::native_type::FromToNativeWasmType>::is_from_store::<_>
Unexecuted instantiation: <wasmer::ptr::WasmPtr<_, _> as wasmer::native_type::FromToNativeWasmType>::is_from_store::<_>
254
}
255
256
unsafe impl<T: ValueType, M: MemorySize> ValueType for WasmPtr<T, M> {
257
0
    fn zero_padding_bytes(&self, _bytes: &mut [mem::MaybeUninit<u8>]) {}
Unexecuted instantiation: <wasmer::ptr::WasmPtr<_, _> as wasmer_types::value::ValueType>::zero_padding_bytes
Unexecuted instantiation: <wasmer::ptr::WasmPtr<_, _> as wasmer_types::value::ValueType>::zero_padding_bytes
258
}
259
260
impl<T: ValueType, M: MemorySize> Clone for WasmPtr<T, M> {
261
0
    fn clone(&self) -> Self {
262
0
        *self
263
0
    }
Unexecuted instantiation: <wasmer::ptr::WasmPtr<_, _> as core::clone::Clone>::clone
Unexecuted instantiation: <wasmer::ptr::WasmPtr<_, _> as core::clone::Clone>::clone
264
}
265
266
impl<T: ValueType, M: MemorySize> Copy for WasmPtr<T, M> {}
267
268
impl<T: ValueType, M: MemorySize> PartialEq for WasmPtr<T, M> {
269
0
    fn eq(&self, other: &Self) -> bool {
270
0
        self.offset.into() == other.offset.into()
271
0
    }
Unexecuted instantiation: <wasmer::ptr::WasmPtr<_, _> as core::cmp::PartialEq>::eq
Unexecuted instantiation: <wasmer::ptr::WasmPtr<_, _> as core::cmp::PartialEq>::eq
272
}
273
274
impl<T: ValueType, M: MemorySize> Eq for WasmPtr<T, M> {}
275
276
impl<T: ValueType, M: MemorySize> fmt::Debug for WasmPtr<T, M> {
277
0
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
278
0
        write!(f, "{}(@{})", std::any::type_name::<T>(), self.offset.into())
279
0
    }
Unexecuted instantiation: <wasmer::ptr::WasmPtr<_, _> as core::fmt::Debug>::fmt
Unexecuted instantiation: <wasmer::ptr::WasmPtr<_, _> as core::fmt::Debug>::fmt
280
}