phala_wasmer_tunables/
lib.rs

1use std::ptr::NonNull;
2
3use wasmer::{
4    vm::{self, MemoryError, MemoryStyle, TableStyle, VMMemoryDefinition, VMTableDefinition},
5    MemoryType, Pages, TableType, Tunables,
6};
7
8/// A custom tunables that allows you to set a memory limit.
9///
10/// After adjusting the memory limits, it delegates all other logic
11/// to the base tunables.
12pub struct LimitingTunables<T: Tunables> {
13    /// The maximum a linear memory is allowed to be (in Wasm pages, 64 KiB each).
14    /// Since Wasmer ensures there is only none or one memory, this is practically
15    /// an upper limit for the guest memory.
16    limit: Pages,
17    /// The base implementation we delegate all the logic to
18    base: T,
19}
20
21impl<T: Tunables> LimitingTunables<T> {
22    pub fn new(base: T, limit: Pages) -> Self {
23        Self { limit, base }
24    }
25
26    /// Takes an input memory type as requested by the guest and sets
27    /// a maximum if missing. The resulting memory type is final if
28    /// valid. However, this can produce invalid types, such that
29    /// validate_memory must be called before creating the memory.
30    fn adjust_memory(&self, requested: &MemoryType) -> MemoryType {
31        let mut adjusted = *requested;
32        if requested.maximum.is_none() {
33            adjusted.maximum = Some(self.limit);
34        }
35        adjusted
36    }
37
38    /// Ensures the a given memory type does not exceed the memory limit.
39    /// Call this after adjusting the memory.
40    fn validate_memory(&self, ty: &MemoryType) -> Result<(), MemoryError> {
41        if ty.minimum > self.limit {
42            return Err(MemoryError::Generic(
43                "Minimum exceeds the allowed memory limit".to_string(),
44            ));
45        }
46
47        if let Some(max) = ty.maximum {
48            if max > self.limit {
49                return Err(MemoryError::Generic(
50                    "Maximum exceeds the allowed memory limit".to_string(),
51                ));
52            }
53        } else {
54            return Err(MemoryError::Generic("Maximum unset".to_string()));
55        }
56
57        Ok(())
58    }
59}
60
61impl<T: Tunables> Tunables for LimitingTunables<T> {
62    /// Construct a `MemoryStyle` for the provided `MemoryType`
63    ///
64    /// Delegated to base.
65    fn memory_style(&self, memory: &MemoryType) -> MemoryStyle {
66        let adjusted = self.adjust_memory(memory);
67        self.base.memory_style(&adjusted)
68    }
69
70    /// Construct a `TableStyle` for the provided `TableType`
71    ///
72    /// Delegated to base.
73    fn table_style(&self, table: &TableType) -> TableStyle {
74        self.base.table_style(table)
75    }
76
77    /// Create a memory owned by the host given a [`MemoryType`] and a [`MemoryStyle`].
78    ///
79    /// The requested memory type is validated, adjusted to the limited and then passed to base.
80    fn create_host_memory(
81        &self,
82        ty: &MemoryType,
83        style: &MemoryStyle,
84    ) -> Result<vm::VMMemory, MemoryError> {
85        let adjusted = self.adjust_memory(ty);
86        self.validate_memory(&adjusted)?;
87        self.base.create_host_memory(&adjusted, style)
88    }
89
90    /// Create a memory owned by the VM given a [`MemoryType`] and a [`MemoryStyle`].
91    ///
92    /// Delegated to base.
93    unsafe fn create_vm_memory(
94        &self,
95        ty: &MemoryType,
96        style: &MemoryStyle,
97        vm_definition_location: NonNull<VMMemoryDefinition>,
98    ) -> Result<vm::VMMemory, MemoryError> {
99        let adjusted = self.adjust_memory(ty);
100        self.validate_memory(&adjusted)?;
101        self.base
102            .create_vm_memory(&adjusted, style, vm_definition_location)
103    }
104
105    /// Create a table owned by the host given a [`TableType`] and a [`TableStyle`].
106    ///
107    /// Delegated to base.
108    fn create_host_table(&self, ty: &TableType, style: &TableStyle) -> Result<vm::VMTable, String> {
109        self.base.create_host_table(ty, style)
110    }
111
112    /// Create a table owned by the VM given a [`TableType`] and a [`TableStyle`].
113    ///
114    /// Delegated to base.
115    unsafe fn create_vm_table(
116        &self,
117        ty: &TableType,
118        style: &TableStyle,
119        vm_definition_location: NonNull<VMTableDefinition>,
120    ) -> Result<vm::VMTable, String> {
121        self.base.create_vm_table(ty, style, vm_definition_location)
122    }
123}