wasmi/memory/
mod.rs

1mod buffer;
2mod data;
3mod error;
4
5#[cfg(test)]
6mod tests;
7
8use self::buffer::ByteBuffer;
9pub use self::{
10    data::{DataSegment, DataSegmentEntity, DataSegmentIdx},
11    error::MemoryError,
12};
13use super::{AsContext, AsContextMut, StoreContext, StoreContextMut, Stored};
14use crate::{
15    collections::arena::ArenaIndex,
16    core::{Pages, TrapCode},
17    error::EntityGrowError,
18    store::{Fuel, ResourceLimiterRef},
19};
20
21/// A raw index to a linear memory entity.
22#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord)]
23pub struct MemoryIdx(u32);
24
25impl ArenaIndex for MemoryIdx {
26    fn into_usize(self) -> usize {
27        self.0 as usize
28    }
29
30    fn from_usize(value: usize) -> Self {
31        let value = value.try_into().unwrap_or_else(|error| {
32            panic!("index {value} is out of bounds as memory index: {error}")
33        });
34        Self(value)
35    }
36}
37
38/// The memory type of a linear memory.
39#[derive(Debug, Copy, Clone, PartialEq, Eq)]
40pub struct MemoryType {
41    initial_pages: Pages,
42    maximum_pages: Option<Pages>,
43}
44
45impl MemoryType {
46    /// Creates a new memory type with initial and optional maximum pages.
47    ///
48    /// # Errors
49    ///
50    /// If the linear memory type initial or maximum size exceeds the
51    /// maximum limits of 2^16 pages.
52    pub fn new(initial: u32, maximum: Option<u32>) -> Result<Self, MemoryError> {
53        let initial_pages = Pages::new(initial).ok_or(MemoryError::InvalidMemoryType)?;
54        let maximum_pages = match maximum {
55            Some(maximum) => Pages::new(maximum)
56                .ok_or(MemoryError::InvalidMemoryType)?
57                .into(),
58            None => None,
59        };
60        Ok(Self {
61            initial_pages,
62            maximum_pages,
63        })
64    }
65
66    /// Returns the initial pages of the memory type.
67    pub fn initial_pages(self) -> Pages {
68        self.initial_pages
69    }
70
71    /// Returns the maximum pages of the memory type.
72    ///
73    /// # Note
74    ///
75    /// - Returns `None` if there is no limit set.
76    /// - Maximum memory size cannot exceed `65536` pages or 4GiB.
77    pub fn maximum_pages(self) -> Option<Pages> {
78        self.maximum_pages
79    }
80
81    /// Checks if `self` is a subtype of `other`.
82    ///
83    /// # Note
84    ///
85    /// This implements the [subtyping rules] according to the WebAssembly spec.
86    ///
87    /// [import subtyping]:
88    /// https://webassembly.github.io/spec/core/valid/types.html#import-subtyping
89    ///
90    /// # Errors
91    ///
92    /// - If the `minimum` size of `self` is less than or equal to the `minimum` size of `other`.
93    /// - If the `maximum` size of `self` is greater than the `maximum` size of `other`.
94    pub(crate) fn is_subtype_or_err(&self, other: &MemoryType) -> Result<(), MemoryError> {
95        match self.is_subtype_of(other) {
96            true => Ok(()),
97            false => Err(MemoryError::InvalidSubtype {
98                ty: *self,
99                other: *other,
100            }),
101        }
102    }
103
104    /// Returns `true` if the [`MemoryType`] is a subtype of the `other` [`MemoryType`].
105    ///
106    /// # Note
107    ///
108    /// This implements the [subtyping rules] according to the WebAssembly spec.
109    ///
110    /// [import subtyping]:
111    /// https://webassembly.github.io/spec/core/valid/types.html#import-subtyping
112    pub(crate) fn is_subtype_of(&self, other: &MemoryType) -> bool {
113        if self.initial_pages() < other.initial_pages() {
114            return false;
115        }
116        match (self.maximum_pages(), other.maximum_pages()) {
117            (_, None) => true,
118            (Some(max), Some(other_max)) => max <= other_max,
119            _ => false,
120        }
121    }
122}
123
124/// A linear memory entity.
125#[derive(Debug)]
126pub struct MemoryEntity {
127    bytes: ByteBuffer,
128    memory_type: MemoryType,
129    current_pages: Pages,
130}
131
132impl MemoryEntity {
133    /// Creates a new memory entity with the given memory type.
134    pub fn new(
135        memory_type: MemoryType,
136        limiter: &mut ResourceLimiterRef<'_>,
137    ) -> Result<Self, MemoryError> {
138        let initial_pages = memory_type.initial_pages();
139        let initial_len = initial_pages.to_bytes();
140        let maximum_pages = memory_type.maximum_pages().unwrap_or_else(Pages::max);
141        let maximum_len = maximum_pages.to_bytes();
142
143        if let Some(limiter) = limiter.as_resource_limiter() {
144            if !limiter.memory_growing(0, initial_len.unwrap_or(usize::MAX), maximum_len)? {
145                // Here there's no meaningful way to map Ok(false) to
146                // INVALID_GROWTH_ERRCODE, so we just translate it to an
147                // appropriate Err(...)
148                return Err(MemoryError::OutOfBoundsAllocation);
149            }
150        }
151
152        if let Some(initial_len) = initial_len {
153            let memory = Self {
154                bytes: ByteBuffer::new(initial_len),
155                memory_type,
156                current_pages: initial_pages,
157            };
158            Ok(memory)
159        } else {
160            let err = MemoryError::OutOfBoundsAllocation;
161            if let Some(limiter) = limiter.as_resource_limiter() {
162                limiter.memory_grow_failed(&err)
163            }
164            Err(err)
165        }
166    }
167
168    /// Creates a new memory entity with the given memory type.
169    pub fn new_static(
170        memory_type: MemoryType,
171        limiter: &mut ResourceLimiterRef<'_>,
172        buf: &'static mut [u8],
173    ) -> Result<Self, MemoryError> {
174        let initial_pages = memory_type.initial_pages();
175        let initial_len = initial_pages.to_bytes();
176        let maximum_pages = memory_type.maximum_pages().unwrap_or_else(Pages::max);
177        let maximum_len = maximum_pages.to_bytes();
178
179        if let Some(limiter) = limiter.as_resource_limiter() {
180            if !limiter.memory_growing(0, initial_len.unwrap_or(usize::MAX), maximum_len)? {
181                // Here there's no meaningful way to map Ok(false) to
182                // INVALID_GROWTH_ERRCODE, so we just translate it to an
183                // appropriate Err(...)
184                return Err(MemoryError::OutOfBoundsAllocation);
185            }
186        }
187
188        if let Some(initial_len) = initial_len {
189            if buf.len() < initial_len {
190                return Err(MemoryError::InvalidStaticBufferSize);
191            }
192            let memory = Self {
193                bytes: ByteBuffer::new_static(buf, initial_len),
194                memory_type,
195                current_pages: initial_pages,
196            };
197            Ok(memory)
198        } else {
199            let err = MemoryError::OutOfBoundsAllocation;
200            if let Some(limiter) = limiter.as_resource_limiter() {
201                limiter.memory_grow_failed(&err)
202            }
203            Err(err)
204        }
205    }
206
207    /// Returns the memory type of the linear memory.
208    pub fn ty(&self) -> MemoryType {
209        self.memory_type
210    }
211
212    /// Returns the dynamic [`MemoryType`] of the [`MemoryEntity`].
213    ///
214    /// # Note
215    ///
216    /// This respects the current size of the [`MemoryEntity`] as
217    /// its minimum size and is useful for import subtyping checks.
218    pub fn dynamic_ty(&self) -> MemoryType {
219        let current_pages = self.current_pages().into();
220        let maximum_pages = self.ty().maximum_pages().map(Into::into);
221        MemoryType::new(current_pages, maximum_pages)
222            .unwrap_or_else(|_| panic!("must result in valid memory type due to invariants"))
223    }
224
225    /// Returns the amount of pages in use by the linear memory.
226    pub fn current_pages(&self) -> Pages {
227        self.current_pages
228    }
229
230    /// Grows the linear memory by the given amount of new pages.
231    ///
232    /// Returns the amount of pages before the operation upon success.
233    ///
234    /// # Errors
235    ///
236    /// If the linear memory would grow beyond its maximum limit after
237    /// the grow operation.
238    pub fn grow(
239        &mut self,
240        additional: Pages,
241        fuel: Option<&mut Fuel>,
242        limiter: &mut ResourceLimiterRef<'_>,
243    ) -> Result<Pages, EntityGrowError> {
244        fn notify_limiter(
245            limiter: &mut ResourceLimiterRef<'_>,
246            err: EntityGrowError,
247        ) -> Result<Pages, EntityGrowError> {
248            if let Some(limiter) = limiter.as_resource_limiter() {
249                limiter.memory_grow_failed(&MemoryError::OutOfBoundsGrowth)
250            }
251            Err(err)
252        }
253
254        let current_pages = self.current_pages();
255        if additional == Pages::from(0) {
256            // Nothing to do in this case. Bail out early.
257            return Ok(current_pages);
258        }
259
260        let maximum_pages = self.ty().maximum_pages().unwrap_or_else(Pages::max);
261        let desired_pages = current_pages.checked_add(additional);
262
263        // ResourceLimiter gets first look at the request.
264        if let Some(limiter) = limiter.as_resource_limiter() {
265            let current_size = current_pages.to_bytes().unwrap_or(usize::MAX);
266            let desired_size = desired_pages
267                .unwrap_or_else(Pages::max)
268                .to_bytes()
269                .unwrap_or(usize::MAX);
270            let maximum_size = maximum_pages.to_bytes();
271            match limiter.memory_growing(current_size, desired_size, maximum_size) {
272                Ok(true) => (),
273                Ok(false) => return Err(EntityGrowError::InvalidGrow),
274                Err(_) => return Err(EntityGrowError::TrapCode(TrapCode::GrowthOperationLimited)),
275            }
276        }
277
278        let Some(new_pages) = desired_pages else {
279            return notify_limiter(limiter, EntityGrowError::InvalidGrow);
280        };
281        if new_pages > maximum_pages {
282            return notify_limiter(limiter, EntityGrowError::InvalidGrow);
283        }
284        let Some(new_size) = new_pages.to_bytes() else {
285            return notify_limiter(limiter, EntityGrowError::InvalidGrow);
286        };
287        if let Some(fuel) = fuel {
288            let additional_bytes = additional.to_bytes().unwrap_or(usize::MAX) as u64;
289            if fuel
290                .consume_fuel_if(|costs| costs.fuel_for_bytes(additional_bytes))
291                .is_err()
292            {
293                return notify_limiter(limiter, EntityGrowError::TrapCode(TrapCode::OutOfFuel));
294            }
295        }
296        // At this point all checks passed to grow the linear memory:
297        //
298        // 1. The resource limiter validated the memory consumption.
299        // 2. The growth is within bounds.
300        // 3. There is enough fuel for the operation.
301        self.bytes.grow(new_size);
302        self.current_pages = new_pages;
303        Ok(current_pages)
304    }
305
306    /// Returns a shared slice to the bytes underlying to the byte buffer.
307    pub fn data(&self) -> &[u8] {
308        self.bytes.data()
309    }
310
311    /// Returns an exclusive slice to the bytes underlying to the byte buffer.
312    pub fn data_mut(&mut self) -> &mut [u8] {
313        self.bytes.data_mut()
314    }
315
316    /// Reads `n` bytes from `memory[offset..offset+n]` into `buffer`
317    /// where `n` is the length of `buffer`.
318    ///
319    /// # Errors
320    ///
321    /// If this operation accesses out of bounds linear memory.
322    pub fn read(&self, offset: usize, buffer: &mut [u8]) -> Result<(), MemoryError> {
323        let len_buffer = buffer.len();
324        let slice = self
325            .data()
326            .get(offset..(offset + len_buffer))
327            .ok_or(MemoryError::OutOfBoundsAccess)?;
328        buffer.copy_from_slice(slice);
329        Ok(())
330    }
331
332    /// Writes `n` bytes to `memory[offset..offset+n]` from `buffer`
333    /// where `n` if the length of `buffer`.
334    ///
335    /// # Errors
336    ///
337    /// If this operation accesses out of bounds linear memory.
338    pub fn write(&mut self, offset: usize, buffer: &[u8]) -> Result<(), MemoryError> {
339        let len_buffer = buffer.len();
340        let slice = self
341            .data_mut()
342            .get_mut(offset..(offset + len_buffer))
343            .ok_or(MemoryError::OutOfBoundsAccess)?;
344        slice.copy_from_slice(buffer);
345        Ok(())
346    }
347}
348
349/// A Wasm linear memory reference.
350#[derive(Debug, Copy, Clone)]
351#[repr(transparent)]
352pub struct Memory(Stored<MemoryIdx>);
353
354impl Memory {
355    /// Creates a new linear memory reference.
356    pub(super) fn from_inner(stored: Stored<MemoryIdx>) -> Self {
357        Self(stored)
358    }
359
360    /// Returns the underlying stored representation.
361    pub(super) fn as_inner(&self) -> &Stored<MemoryIdx> {
362        &self.0
363    }
364
365    /// Creates a new linear memory to the store.
366    ///
367    /// # Errors
368    ///
369    /// If more than [`u32::MAX`] much linear memory is allocated.
370    pub fn new(mut ctx: impl AsContextMut, ty: MemoryType) -> Result<Self, MemoryError> {
371        let (inner, mut resource_limiter) = ctx
372            .as_context_mut()
373            .store
374            .store_inner_and_resource_limiter_ref();
375
376        let entity = MemoryEntity::new(ty, &mut resource_limiter)?;
377        let memory = inner.alloc_memory(entity);
378        Ok(memory)
379    }
380
381    /// Creates a new linear memory to the store.
382    ///
383    /// # Errors
384    ///
385    /// If more than [`u32::MAX`] much linear memory is allocated.
386    /// - If static buffer is invalid
387    pub fn new_static(
388        mut ctx: impl AsContextMut,
389        ty: MemoryType,
390        buf: &'static mut [u8],
391    ) -> Result<Self, MemoryError> {
392        let (inner, mut resource_limiter) = ctx
393            .as_context_mut()
394            .store
395            .store_inner_and_resource_limiter_ref();
396
397        let entity = MemoryEntity::new_static(ty, &mut resource_limiter, buf)?;
398        let memory = inner.alloc_memory(entity);
399        Ok(memory)
400    }
401
402    /// Returns the memory type of the linear memory.
403    ///
404    /// # Panics
405    ///
406    /// Panics if `ctx` does not own this [`Memory`].
407    pub fn ty(&self, ctx: impl AsContext) -> MemoryType {
408        ctx.as_context().store.inner.resolve_memory(self).ty()
409    }
410
411    /// Returns the dynamic [`MemoryType`] of the [`Memory`].
412    ///
413    /// # Note
414    ///
415    /// This respects the current size of the [`Memory`] as
416    /// its minimum size and is useful for import subtyping checks.
417    ///
418    /// # Panics
419    ///
420    /// Panics if `ctx` does not own this [`Memory`].
421    pub(crate) fn dynamic_ty(&self, ctx: impl AsContext) -> MemoryType {
422        ctx.as_context()
423            .store
424            .inner
425            .resolve_memory(self)
426            .dynamic_ty()
427    }
428
429    /// Returns the amount of pages in use by the linear memory.
430    ///
431    /// # Panics
432    ///
433    /// Panics if `ctx` does not own this [`Memory`].
434    pub fn current_pages(&self, ctx: impl AsContext) -> Pages {
435        ctx.as_context()
436            .store
437            .inner
438            .resolve_memory(self)
439            .current_pages()
440    }
441
442    /// Grows the linear memory by the given amount of new pages.
443    ///
444    /// Returns the amount of pages before the operation upon success.
445    ///
446    /// # Errors
447    ///
448    /// If the linear memory would grow beyond its maximum limit after
449    /// the grow operation.
450    ///
451    /// # Panics
452    ///
453    /// Panics if `ctx` does not own this [`Memory`].
454    pub fn grow(
455        &self,
456        mut ctx: impl AsContextMut,
457        additional: Pages,
458    ) -> Result<Pages, MemoryError> {
459        let (inner, mut limiter) = ctx
460            .as_context_mut()
461            .store
462            .store_inner_and_resource_limiter_ref();
463        inner
464            .resolve_memory_mut(self)
465            .grow(additional, None, &mut limiter)
466            .map_err(|_| MemoryError::OutOfBoundsGrowth)
467    }
468
469    /// Returns a shared slice to the bytes underlying the [`Memory`].
470    ///
471    /// # Panics
472    ///
473    /// Panics if `ctx` does not own this [`Memory`].
474    pub fn data<'a, T: 'a>(&self, ctx: impl Into<StoreContext<'a, T>>) -> &'a [u8] {
475        ctx.into().store.inner.resolve_memory(self).data()
476    }
477
478    /// Returns an exclusive slice to the bytes underlying the [`Memory`].
479    ///
480    /// # Panics
481    ///
482    /// Panics if `ctx` does not own this [`Memory`].
483    pub fn data_mut<'a, T: 'a>(&self, ctx: impl Into<StoreContextMut<'a, T>>) -> &'a mut [u8] {
484        ctx.into().store.inner.resolve_memory_mut(self).data_mut()
485    }
486
487    /// Returns an exclusive slice to the bytes underlying the [`Memory`], and an exclusive
488    /// reference to the user provided state.
489    ///
490    /// # Panics
491    ///
492    /// Panics if `ctx` does not own this [`Memory`].
493    pub fn data_and_store_mut<'a, T: 'a>(
494        &self,
495        ctx: impl Into<StoreContextMut<'a, T>>,
496    ) -> (&'a mut [u8], &'a mut T) {
497        let (memory, store) = ctx.into().store.resolve_memory_and_state_mut(self);
498        (memory.data_mut(), store)
499    }
500
501    /// Reads `n` bytes from `memory[offset..offset+n]` into `buffer`
502    /// where `n` is the length of `buffer`.
503    ///
504    /// # Errors
505    ///
506    /// If this operation accesses out of bounds linear memory.
507    ///
508    /// # Panics
509    ///
510    /// Panics if `ctx` does not own this [`Memory`].
511    pub fn read(
512        &self,
513        ctx: impl AsContext,
514        offset: usize,
515        buffer: &mut [u8],
516    ) -> Result<(), MemoryError> {
517        ctx.as_context()
518            .store
519            .inner
520            .resolve_memory(self)
521            .read(offset, buffer)
522    }
523
524    /// Writes `n` bytes to `memory[offset..offset+n]` from `buffer`
525    /// where `n` if the length of `buffer`.
526    ///
527    /// # Errors
528    ///
529    /// If this operation accesses out of bounds linear memory.
530    ///
531    /// # Panics
532    ///
533    /// Panics if `ctx` does not own this [`Memory`].
534    pub fn write(
535        &self,
536        mut ctx: impl AsContextMut,
537        offset: usize,
538        buffer: &[u8],
539    ) -> Result<(), MemoryError> {
540        ctx.as_context_mut()
541            .store
542            .inner
543            .resolve_memory_mut(self)
544            .write(offset, buffer)
545    }
546}