wasmer/utils/mem/
access.rs

1use std::mem::{self, MaybeUninit};
2use std::slice;
3
4use crate::{
5    MemoryAccessError,
6    utils::mem::{WasmRef, WasmSlice},
7};
8
9pub(crate) enum SliceCow<'a, T> {
10    #[allow(dead_code)]
11    Borrowed(&'a mut [T]),
12    #[allow(dead_code)]
13    Owned(Vec<T>, bool),
14}
15
16impl<T> AsRef<[T]> for SliceCow<'_, T> {
17    fn as_ref(&self) -> &[T] {
18        match self {
19            Self::Borrowed(buf) => buf,
20            Self::Owned(buf, _) => buf,
21        }
22    }
23}
24
25impl<T> AsMut<[T]> for SliceCow<'_, T> {
26    fn as_mut(&mut self) -> &mut [T] {
27        // Note: Zero padding is not required here as its a typed copy which does
28        //       not leak the bytes into the memory
29        // https://stackoverflow.com/questions/61114026/does-stdptrwrite-transfer-the-uninitialized-ness-of-the-bytes-it-writes
30        match self {
31            Self::Borrowed(buf) => buf,
32            Self::Owned(buf, modified) => {
33                *modified = true;
34                buf.as_mut()
35            }
36        }
37    }
38}
39
40/// Provides direct memory access to a piece of memory that
41/// is owned by WASM
42pub struct WasmSliceAccess<'a, T>
43where
44    T: wasmer_types::ValueType,
45{
46    pub(crate) slice: WasmSlice<'a, T>,
47    pub(crate) buf: SliceCow<'a, T>,
48}
49
50impl<T> AsRef<[T]> for WasmSliceAccess<'_, T>
51where
52    T: wasmer_types::ValueType,
53{
54    fn as_ref(&self) -> &[T] {
55        self.buf.as_ref()
56    }
57}
58
59impl<T> AsMut<[T]> for WasmSliceAccess<'_, T>
60where
61    T: wasmer_types::ValueType,
62{
63    fn as_mut(&mut self) -> &mut [T] {
64        self.buf.as_mut()
65    }
66}
67
68impl<'a, T> WasmSliceAccess<'a, T>
69where
70    T: wasmer_types::ValueType,
71{
72    /// Returns an iterator of all the elements in the slice
73    pub fn iter(&'a self) -> std::slice::Iter<'a, T> {
74        self.as_ref().iter()
75    }
76
77    /// Returns an iterator of all the elements in the slice
78    pub fn iter_mut(&'a mut self) -> std::slice::IterMut<'a, T> {
79        self.buf.as_mut().iter_mut()
80    }
81
82    /// Number of elements in this slice
83    pub fn len(&self) -> usize {
84        self.buf.as_ref().len()
85    }
86
87    /// If the slice is empty
88    pub fn is_empty(&self) -> bool {
89        self.buf.as_ref().is_empty()
90    }
91}
92
93impl WasmSliceAccess<'_, u8> {
94    /// Writes to the address pointed to by this `WasmPtr` in a memory.
95    #[inline]
96    pub fn copy_from_slice(&mut self, src: &[u8]) {
97        let dst = self.buf.as_mut();
98        dst.copy_from_slice(src);
99    }
100
101    /// Writes to the address pointed to by this `WasmPtr` in a memory.
102    ///
103    /// If the source buffer is smaller than the destination buffer
104    /// only the correct amount of bytes will be copied
105    ///
106    /// Returns the number of bytes copied
107    #[inline]
108    pub fn copy_from_slice_min(&mut self, src: &[u8]) -> usize {
109        let dst = self.buf.as_mut();
110        let amt = dst.len().min(src.len());
111        dst[..amt].copy_from_slice(&src[..amt]);
112        amt
113    }
114}
115
116impl<T> Drop for WasmSliceAccess<'_, T>
117where
118    T: wasmer_types::ValueType,
119{
120    fn drop(&mut self) {
121        if let SliceCow::Owned(buf, modified) = &self.buf
122            && *modified
123        {
124            self.slice.write_slice(buf.as_ref()).ok();
125        }
126    }
127}
128
129pub(crate) enum RefCow<'a, T> {
130    #[allow(dead_code)]
131    Borrowed(&'a mut T),
132    #[allow(dead_code)]
133    Owned(T, bool),
134}
135
136impl<T> AsRef<T> for RefCow<'_, T> {
137    fn as_ref(&self) -> &T {
138        match self {
139            Self::Borrowed(val) => val,
140            Self::Owned(val, _) => val,
141        }
142    }
143}
144
145impl<T> AsMut<T> for RefCow<'_, T> {
146    fn as_mut(&mut self) -> &mut T {
147        // Note: Zero padding is not required here as its a typed copy which does
148        //       not leak the bytes into the memory
149        // https://stackoverflow.com/questions/61114026/does-stdptrwrite-transfer-the-uninitialized-ness-of-the-bytes-it-writes
150        match self {
151            Self::Borrowed(val) => val,
152            Self::Owned(val, modified) => {
153                *modified = true;
154                val
155            }
156        }
157    }
158}
159
160/// Provides direct memory access to a piece of memory that
161/// is owned by WASM
162pub struct WasmRefAccess<'a, T>
163where
164    T: wasmer_types::ValueType,
165{
166    pub(crate) is_owned: bool,
167    pub(crate) ptr: WasmRef<'a, T>,
168    pub(crate) buf: RefCow<'a, T>,
169}
170
171impl<T> AsRef<T> for WasmRefAccess<'_, T>
172where
173    T: wasmer_types::ValueType,
174{
175    fn as_ref(&self) -> &T {
176        self.buf.as_ref()
177    }
178}
179
180impl<T> AsMut<T> for WasmRefAccess<'_, T>
181where
182    T: wasmer_types::ValueType,
183{
184    fn as_mut(&mut self) -> &mut T {
185        self.buf.as_mut()
186    }
187}
188
189impl<T> Drop for WasmRefAccess<'_, T>
190where
191    T: wasmer_types::ValueType,
192{
193    fn drop(&mut self) {
194        if let RefCow::Owned(val, modified) = &self.buf
195            && *modified
196        {
197            self.ptr.write(*val).ok();
198        }
199    }
200}
201
202impl<T> WasmSliceAccess<'_, T>
203where
204    T: wasmer_types::ValueType,
205{
206    /// Returns a mutable slice that is not yet initialized
207    pub fn as_mut_uninit(&mut self) -> &mut [MaybeUninit<T>] {
208        let ret: &mut [T] = self.buf.as_mut();
209        let ret: &mut [MaybeUninit<T>] = unsafe { std::mem::transmute(ret) };
210        ret
211    }
212}
213
214impl<T> WasmRefAccess<'_, T>
215where
216    T: wasmer_types::ValueType,
217{
218    /// Returns a reference to an unitialized reference to this value
219    pub fn as_mut_uninit(&mut self) -> &mut MaybeUninit<T> {
220        let ret: &mut T = self.buf.as_mut();
221        let ret: &mut MaybeUninit<T> = unsafe { std::mem::transmute(ret) };
222        ret
223    }
224}
225
226impl<'a, T> WasmSliceAccess<'a, T>
227where
228    T: wasmer_types::ValueType,
229{
230    pub(crate) fn new(slice: WasmSlice<'a, T>, is_owned: bool) -> Result<Self, MemoryAccessError> {
231        if is_owned {
232            Self::new_owned(slice)
233        } else {
234            Self::new_borrowed(slice)
235        }
236    }
237
238    pub(crate) fn new_borrowed(slice: WasmSlice<'a, T>) -> Result<Self, MemoryAccessError> {
239        let total_len = slice
240            .len
241            .checked_mul(std::mem::size_of::<T>() as u64)
242            .ok_or(MemoryAccessError::Overflow)?;
243        let end = slice
244            .offset
245            .checked_add(total_len)
246            .ok_or(MemoryAccessError::Overflow)?;
247        if end > slice.buffer.len() as u64 {
248            tracing::warn!(
249                "attempted to read ({} bytes) beyond the bounds of the memory view ({} > {})",
250                total_len,
251                end,
252                slice.buffer.len()
253            );
254            return Err(MemoryAccessError::HeapOutOfBounds);
255        }
256        let buf = unsafe {
257            let buf_ptr: *mut u8 = slice.buffer.base().add(slice.offset as usize);
258            let buf_ptr: *mut T = std::mem::transmute(buf_ptr);
259            if !buf_ptr.is_aligned() {
260                return Err(MemoryAccessError::UnalignedPointerRead);
261            }
262            std::slice::from_raw_parts_mut(buf_ptr, slice.len as usize)
263        };
264        Ok(Self {
265            slice,
266            buf: SliceCow::Borrowed(buf),
267        })
268    }
269
270    pub(crate) fn new_owned(slice: WasmSlice<'a, T>) -> Result<Self, MemoryAccessError> {
271        let buf = slice.read_to_vec()?;
272        Ok(Self {
273            slice,
274            buf: SliceCow::Owned(buf, false),
275        })
276    }
277}
278
279impl<'a, T> WasmRefAccess<'a, T>
280where
281    T: wasmer_types::ValueType,
282{
283    pub(crate) fn new(ptr: WasmRef<'a, T>, is_owned: bool) -> Result<Self, MemoryAccessError> {
284        if is_owned {
285            Self::new_owned(ptr)
286        } else {
287            Self::new_borrowed(ptr)
288        }
289    }
290
291    pub(crate) fn new_borrowed(ptr: WasmRef<'a, T>) -> Result<Self, MemoryAccessError> {
292        let total_len = std::mem::size_of::<T>() as u64;
293        let end = ptr
294            .offset
295            .checked_add(total_len)
296            .ok_or(MemoryAccessError::Overflow)?;
297        if end > ptr.buffer.len() as u64 {
298            tracing::warn!(
299                "attempted to read ({} bytes) beyond the bounds of the memory view ({} > {})",
300                total_len,
301                end,
302                ptr.buffer.len()
303            );
304            return Err(MemoryAccessError::HeapOutOfBounds);
305        }
306        let val = unsafe {
307            let val_ptr: *mut u8 = ptr.buffer.base().add(ptr.offset as usize);
308            let val_ptr: *mut T = std::mem::transmute(val_ptr);
309            &mut *val_ptr
310        };
311        Ok(Self {
312            is_owned: false,
313            ptr,
314            buf: RefCow::Borrowed(val),
315        })
316    }
317
318    pub(crate) fn new_owned(ptr: WasmRef<'a, T>) -> Result<Self, MemoryAccessError> {
319        let mut out = MaybeUninit::uninit();
320        let buf =
321            unsafe { slice::from_raw_parts_mut(out.as_mut_ptr() as *mut u8, mem::size_of::<T>()) };
322        ptr.buffer.read(ptr.offset, buf)?;
323        let val = unsafe { out.assume_init() };
324
325        Ok(Self {
326            is_owned: true,
327            ptr,
328            buf: RefCow::Owned(val, false),
329        })
330    }
331}
332
333impl<T> WasmRefAccess<'_, T>
334where
335    T: wasmer_types::ValueType,
336{
337    /// Reads the address pointed to by this `WasmPtr` in a memory.
338    #[inline]
339    #[allow(clippy::clone_on_copy)]
340    pub fn read(&self) -> T
341    where
342        T: Clone,
343    {
344        self.as_ref().clone()
345    }
346
347    /// Writes to the address pointed to by this `WasmPtr` in a memory.
348    #[inline]
349    pub fn write(&mut self, val: T) {
350        if self.is_owned {
351            let mut data = MaybeUninit::new(val);
352            let data = unsafe {
353                slice::from_raw_parts_mut(
354                    data.as_mut_ptr() as *mut MaybeUninit<u8>,
355                    mem::size_of::<T>(),
356                )
357            };
358            val.zero_padding_bytes(data);
359            let data = unsafe { slice::from_raw_parts(data.as_ptr() as *const _, data.len()) };
360            self.ptr.buffer.write(self.ptr.offset, data).unwrap()
361        } else {
362            // Note: Zero padding is not required here as its a typed copy which does
363            //       not leak the bytes into the memory
364            // https://stackoverflow.com/questions/61114026/does-stdptrwrite-transfer-the-uninitialized-ness-of-the-bytes-it-writes
365            *(self.as_mut()) = val;
366        }
367    }
368}