linera_wasmer/
mem_access.rs

1use crate::access::WasmRefAccess;
2use crate::externals::memory::MemoryBuffer;
3use crate::{Memory32, Memory64, MemorySize, MemoryView, WasmPtr};
4use crate::{RuntimeError, WasmSliceAccess};
5use std::convert::TryInto;
6use std::fmt;
7use std::marker::PhantomData;
8use std::mem::{self, MaybeUninit};
9use std::ops::Range;
10use std::slice;
11use std::string::FromUtf8Error;
12use thiserror::Error;
13use wasmer_types::ValueType;
14
15/// Error for invalid [`Memory`][super::Memory] access.
16#[derive(Clone, Copy, Debug, Error)]
17#[non_exhaustive]
18pub enum MemoryAccessError {
19    /// Memory access is outside heap bounds.
20    #[error("memory access out of bounds")]
21    HeapOutOfBounds,
22    /// Address calculation overflow.
23    #[error("address calculation overflow")]
24    Overflow,
25    /// String is not valid UTF-8.
26    #[error("string is not valid utf-8")]
27    NonUtf8String,
28}
29
30impl From<MemoryAccessError> for RuntimeError {
31    fn from(err: MemoryAccessError) -> Self {
32        Self::new(err.to_string())
33    }
34}
35impl From<FromUtf8Error> for MemoryAccessError {
36    fn from(_err: FromUtf8Error) -> Self {
37        Self::NonUtf8String
38    }
39}
40
41/// Reference to a value in Wasm memory.
42///
43/// The type of the value must satisfy the requirements of the `ValueType`
44/// trait which guarantees that reading and writing such a value to untrusted
45/// memory is safe.
46///
47/// The address is required to be aligned: unaligned accesses cause undefined behavior.
48///
49/// This wrapper safely handles concurrent modifications of the data by another
50/// thread.
51#[derive(Clone, Copy)]
52pub struct WasmRef<'a, T: ValueType> {
53    #[allow(unused)]
54    pub(crate) buffer: MemoryBuffer<'a>,
55    pub(crate) offset: u64,
56    marker: PhantomData<*mut T>,
57}
58
59impl<'a, T: ValueType> WasmRef<'a, T> {
60    /// Creates a new `WasmRef` at the given offset in a memory.
61    #[inline]
62    pub fn new(view: &'a MemoryView, offset: u64) -> Self {
63        Self {
64            buffer: view.buffer(),
65            offset,
66            marker: PhantomData,
67        }
68    }
69
70    /// Get the offset into Wasm linear memory for this `WasmRef`.
71    #[inline]
72    pub fn offset(self) -> u64 {
73        self.offset
74    }
75
76    /// Get a `WasmPtr` for this `WasmRef`.
77    #[inline]
78    pub fn as_ptr32(self) -> WasmPtr<T, Memory32> {
79        WasmPtr::new(self.offset as u32)
80    }
81
82    /// Get a 64-bit `WasmPtr` for this `WasmRef`.
83    #[inline]
84    pub fn as_ptr64(self) -> WasmPtr<T, Memory64> {
85        WasmPtr::new(self.offset)
86    }
87
88    /// Get a `WasmPtr` for this `WasmRef`.
89    #[inline]
90    pub fn as_ptr<M: MemorySize>(self) -> WasmPtr<T, M> {
91        let offset: M::Offset = self
92            .offset
93            .try_into()
94            .map_err(|_| "invalid offset into memory")
95            .unwrap();
96        WasmPtr::<T, M>::new(offset)
97    }
98
99    /// Reads the location pointed to by this `WasmRef`.
100    #[inline]
101    pub fn read(self) -> Result<T, MemoryAccessError> {
102        let mut out = MaybeUninit::uninit();
103        let buf =
104            unsafe { slice::from_raw_parts_mut(out.as_mut_ptr() as *mut u8, mem::size_of::<T>()) };
105        self.buffer.read(self.offset, buf)?;
106        Ok(unsafe { out.assume_init() })
107        // Ok(self.access()?.read())
108    }
109
110    /// Writes to the location pointed to by this `WasmRef`.
111    #[inline]
112    pub fn write(self, val: T) -> Result<(), MemoryAccessError> {
113        self.access()?.write(val);
114        Ok(())
115    }
116
117    /// Gains direct access to the memory of this slice
118    #[inline]
119    pub fn access(self) -> Result<WasmRefAccess<'a, T>, MemoryAccessError> {
120        WasmRefAccess::new(self)
121    }
122}
123
124impl<'a, T: ValueType> fmt::Debug for WasmRef<'a, T> {
125    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
126        write!(
127            f,
128            "WasmRef(offset: {}, pointer: {:#x})",
129            self.offset, self.offset
130        )
131    }
132}
133
134/// Reference to an array of values in Wasm memory.
135///
136/// The type of the value must satisfy the requirements of the `ValueType`
137/// trait which guarantees that reading and writing such a value to untrusted
138/// memory is safe.
139///
140/// The address is not required to be aligned: unaligned accesses are fully
141/// supported.
142///
143/// This wrapper safely handles concurrent modifications of the data by another
144/// thread.
145#[derive(Clone, Copy)]
146pub struct WasmSlice<'a, T: ValueType> {
147    pub(crate) buffer: MemoryBuffer<'a>,
148    pub(crate) offset: u64,
149    pub(crate) len: u64,
150    marker: PhantomData<*mut T>,
151}
152
153impl<'a, T: ValueType> WasmSlice<'a, T> {
154    /// Creates a new `WasmSlice` starting at the given offset in memory and
155    /// with the given number of elements.
156    ///
157    /// Returns a `MemoryAccessError` if the slice length overflows.
158    #[inline]
159    pub fn new(view: &'a MemoryView, offset: u64, len: u64) -> Result<Self, MemoryAccessError> {
160        let total_len = len
161            .checked_mul(mem::size_of::<T>() as u64)
162            .ok_or(MemoryAccessError::Overflow)?;
163        offset
164            .checked_add(total_len)
165            .ok_or(MemoryAccessError::Overflow)?;
166        Ok(Self {
167            buffer: view.buffer(),
168            offset,
169            len,
170            marker: PhantomData,
171        })
172    }
173
174    /// Get the offset into Wasm linear memory for this `WasmSlice`.
175    #[inline]
176    pub fn offset(self) -> u64 {
177        self.offset
178    }
179
180    /// Get a 32-bit `WasmPtr` for this `WasmRef`.
181    #[inline]
182    pub fn as_ptr32(self) -> WasmPtr<T, Memory32> {
183        WasmPtr::new(self.offset as u32)
184    }
185
186    /// Get a 64-bit `WasmPtr` for this `WasmRef`.
187    #[inline]
188    pub fn as_ptr64(self) -> WasmPtr<T, Memory64> {
189        WasmPtr::new(self.offset)
190    }
191
192    /// Get the number of elements in this slice.
193    #[inline]
194    pub fn len(self) -> u64 {
195        self.len
196    }
197
198    /// Returns `true` if the number of elements is 0.
199    #[inline]
200    pub fn is_empty(self) -> bool {
201        self.len == 0
202    }
203
204    /// Get a `WasmRef` to an element in the slice.
205    #[inline]
206    pub fn index(self, idx: u64) -> WasmRef<'a, T> {
207        if idx >= self.len {
208            panic!("WasmSlice out of bounds");
209        }
210        let offset = self.offset + idx * mem::size_of::<T>() as u64;
211        WasmRef {
212            buffer: self.buffer,
213            offset,
214            marker: PhantomData,
215        }
216    }
217
218    /// Get a `WasmSlice` for a subslice of this slice.
219    #[inline]
220    pub fn subslice(self, range: Range<u64>) -> WasmSlice<'a, T> {
221        if range.start > range.end || range.end > self.len {
222            panic!("WasmSlice out of bounds");
223        }
224        let offset = self.offset + range.start * mem::size_of::<T>() as u64;
225        Self {
226            buffer: self.buffer,
227            offset,
228            len: range.end - range.start,
229            marker: PhantomData,
230        }
231    }
232
233    /// Get an iterator over the elements in this slice.
234    #[inline]
235    pub fn iter(self) -> WasmSliceIter<'a, T> {
236        WasmSliceIter { slice: self }
237    }
238
239    /// Gains direct access to the memory of this slice
240    #[inline]
241    pub fn access(self) -> Result<WasmSliceAccess<'a, T>, MemoryAccessError> {
242        WasmSliceAccess::new(self)
243    }
244
245    /// Reads an element of this slice.
246    #[inline]
247    pub fn read(self, idx: u64) -> Result<T, MemoryAccessError> {
248        self.index(idx).read()
249    }
250
251    /// Writes to an element of this slice.
252    #[inline]
253    pub fn write(self, idx: u64, val: T) -> Result<(), MemoryAccessError> {
254        self.index(idx).write(val)
255    }
256
257    /// Reads the entire slice into the given buffer.
258    ///
259    /// The length of the buffer must match the length of the slice.
260    #[inline]
261    pub fn read_slice(self, buf: &mut [T]) -> Result<(), MemoryAccessError> {
262        assert_eq!(
263            buf.len() as u64,
264            self.len,
265            "slice length doesn't match WasmSlice length"
266        );
267        let size = std::mem::size_of_val(buf);
268        let bytes =
269            unsafe { slice::from_raw_parts_mut(buf.as_mut_ptr() as *mut MaybeUninit<u8>, size) };
270        self.buffer.read_uninit(self.offset, bytes)?;
271        Ok(())
272    }
273
274    /// Reads the entire slice into the given uninitialized buffer.
275    ///
276    /// The length of the buffer must match the length of the slice.
277    ///
278    /// This method returns an initialized view of the buffer.
279    #[inline]
280    pub fn read_slice_uninit(
281        self,
282        buf: &mut [MaybeUninit<T>],
283    ) -> Result<&mut [T], MemoryAccessError> {
284        assert_eq!(
285            buf.len() as u64,
286            self.len,
287            "slice length doesn't match WasmSlice length"
288        );
289        let bytes = unsafe {
290            slice::from_raw_parts_mut(
291                buf.as_mut_ptr() as *mut MaybeUninit<u8>,
292                buf.len() * mem::size_of::<T>(),
293            )
294        };
295        self.buffer.read_uninit(self.offset, bytes)?;
296        Ok(unsafe { slice::from_raw_parts_mut(buf.as_mut_ptr() as *mut T, buf.len()) })
297    }
298
299    /// Write the given slice into this `WasmSlice`.
300    ///
301    /// The length of the slice must match the length of the `WasmSlice`.
302    #[inline]
303    pub fn write_slice(self, data: &[T]) -> Result<(), MemoryAccessError> {
304        assert_eq!(
305            data.len() as u64,
306            self.len,
307            "slice length doesn't match WasmSlice length"
308        );
309        let size = std::mem::size_of_val(data);
310        let bytes = unsafe { slice::from_raw_parts(data.as_ptr() as *const u8, size) };
311        self.buffer.write(self.offset, bytes)
312    }
313
314    /// Reads this `WasmSlice` into a `slice`.
315    #[inline]
316    pub fn read_to_slice(self, buf: &mut [MaybeUninit<u8>]) -> Result<usize, MemoryAccessError> {
317        let len = self.len.try_into().expect("WasmSlice length overflow");
318        self.buffer.read_uninit(self.offset, buf)?;
319        Ok(len)
320    }
321
322    /// Reads this `WasmSlice` into a `Vec`.
323    #[inline]
324    pub fn read_to_vec(self) -> Result<Vec<T>, MemoryAccessError> {
325        let len = self.len.try_into().expect("WasmSlice length overflow");
326        let mut vec = Vec::with_capacity(len);
327        let bytes = unsafe {
328            slice::from_raw_parts_mut(
329                vec.as_mut_ptr() as *mut MaybeUninit<u8>,
330                len * mem::size_of::<T>(),
331            )
332        };
333        self.buffer.read_uninit(self.offset, bytes)?;
334        unsafe {
335            vec.set_len(len);
336        }
337        Ok(vec)
338    }
339
340    /// Reads this `WasmSlice` into a `BytesMut`
341    #[inline]
342    pub fn read_to_bytes(self) -> Result<bytes::BytesMut, MemoryAccessError> {
343        let len = self.len.try_into().expect("WasmSlice length overflow");
344        let mut ret = bytes::BytesMut::with_capacity(len);
345        let bytes = unsafe {
346            slice::from_raw_parts_mut(
347                ret.as_mut_ptr() as *mut MaybeUninit<u8>,
348                len * mem::size_of::<T>(),
349            )
350        };
351        self.buffer.read_uninit(self.offset, bytes)?;
352        unsafe {
353            ret.set_len(len);
354        }
355        Ok(ret)
356    }
357}
358
359impl<'a, T: ValueType> fmt::Debug for WasmSlice<'a, T> {
360    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
361        write!(
362            f,
363            "WasmSlice(offset: {}, len: {}, pointer: {:#x})",
364            self.offset, self.len, self.offset
365        )
366    }
367}
368
369/// Iterator over the elements of a `WasmSlice`.
370pub struct WasmSliceIter<'a, T: ValueType> {
371    slice: WasmSlice<'a, T>,
372}
373
374impl<'a, T: ValueType> Iterator for WasmSliceIter<'a, T> {
375    type Item = WasmRef<'a, T>;
376
377    fn next(&mut self) -> Option<Self::Item> {
378        if !self.slice.is_empty() {
379            let elem = self.slice.index(0);
380            self.slice = self.slice.subslice(1..self.slice.len());
381            Some(elem)
382        } else {
383            None
384        }
385    }
386
387    fn size_hint(&self) -> (usize, Option<usize>) {
388        (0..self.slice.len()).size_hint()
389    }
390}
391
392impl<'a, T: ValueType> DoubleEndedIterator for WasmSliceIter<'a, T> {
393    fn next_back(&mut self) -> Option<Self::Item> {
394        if !self.slice.is_empty() {
395            let elem = self.slice.index(self.slice.len() - 1);
396            self.slice = self.slice.subslice(0..self.slice.len() - 1);
397            Some(elem)
398        } else {
399            None
400        }
401    }
402}
403
404impl<'a, T: ValueType> ExactSizeIterator for WasmSliceIter<'a, T> {}