pchain_runtime/wasmer/custom_tunables.rs
1/*
2 Copyright © 2023, ParallelChain Lab
3 Licensed under the Apache License, Version 2.0: http://www.apache.org/licenses/LICENSE-2.0
4*/
5
6//! Defines structs that are used as configuration of wasmer store for limiting the memory
7//! used for wasm module instantiation.
8
9use loupe::MemoryUsage;
10use std::ptr::NonNull;
11use std::sync::Arc;
12use wasmer::{
13 vm::{self, MemoryError, MemoryStyle, TableStyle, VMMemoryDefinition, VMTableDefinition},
14 MemoryType, Pages, TableType, Tunables,
15};
16
17/// CustomTunables allows the setting of an upper limit on the guest memory of the VM.
18/// CustomTunables also conists of a base Tunables attribute which all the existing logic is delegated to after
19/// guest memory adjustment
20#[derive(MemoryUsage)]
21pub struct CustomTunables<T: Tunables> {
22 /// maximum allowable guest memory (in WASM pages, each approximately 65KiB in size)
23 limit: Pages,
24
25 /// base implementation we delegate all the logic to after guest memory adjustment
26 base: T,
27}
28
29impl<T: Tunables> Tunables for CustomTunables<T> {
30 /// `memory_style` is used to construct a WebAssembly `MemoryStyle` for the provided `MemoryType` using base tunables
31 /// For more information on `memory_style`: See <https://github.com/wasmerio/wasmer/blob/master/examples/tunables_limit_memory.rs>
32 fn memory_style(&self, memory: &MemoryType) -> MemoryStyle {
33 let adjusted = self.adjust_memory(memory);
34 self.base.memory_style(&adjusted)
35 }
36
37 /// `table_style` is used to construct a WebAssembly `TableStyle` for the provided `TableType` using base tunables
38 /// For more information on `table_style`: See <https://github.com/wasmerio/wasmer/blob/master/examples/tunables_limit_memory.rs>
39 fn table_style(&self, table: &TableType) -> TableStyle {
40 self.base.table_style(table)
41 }
42
43 /// `create_host_memory` creates memory owned by the host given a WebAssembly `MemoryType` and a `MemoryStyle`
44 /// The requested memory type is validated, adjusted to the limited and then passed to base tunables
45 /// For more information on `create_host_memory`: See <https://github.com/wasmerio/wasmer/blob/master/examples/tunables_limit_memory.rs>
46 fn create_host_memory(
47 &self,
48 ty: &MemoryType,
49 style: &MemoryStyle,
50 ) -> Result<Arc<dyn vm::Memory>, MemoryError> {
51 let adjusted = self.adjust_memory(ty);
52 self.validate_memory(&adjusted)?;
53 self.base.create_host_memory(&adjusted, style)
54 }
55
56 /// `create_vm_memory` creates memory owned by the VM given a WebAssembly `MemoryType` and a `MemoryStyle`.
57 /// For more information on `create_vm_memory`: See <https://github.com/wasmerio/wasmer/blob/master/examples/tunables_limit_memory.rs>
58 unsafe fn create_vm_memory(
59 &self,
60 ty: &MemoryType,
61 style: &MemoryStyle,
62 vm_definition_location: NonNull<VMMemoryDefinition>,
63 ) -> Result<Arc<dyn vm::Memory>, MemoryError> {
64 let adjusted = self.adjust_memory(ty);
65 self.validate_memory(&adjusted)?;
66 self.base
67 .create_vm_memory(&adjusted, style, vm_definition_location)
68 }
69
70 // `create_host_table` creates a table owned by the host given a WebAssembly `TableType` and a `TableStyle`.
71 // For more information on `create_host_table`: See <https://github.com/wasmerio/wasmer/blob/master/examples/tunables_limit_memory.rs>
72 fn create_host_table(
73 &self,
74 ty: &TableType,
75 style: &TableStyle,
76 ) -> Result<Arc<dyn vm::Table>, String> {
77 self.base.create_host_table(ty, style)
78 }
79
80 // `create_vm_table` creates a table owned by the VM given a WebAssembly `TableType` and a `TableStyle`.
81 // For more information on `create_vm_table`: See <https://github.com/wasmerio/wasmer/blob/master/examples/tunables_limit_memory.rs>
82 unsafe fn create_vm_table(
83 &self,
84 ty: &TableType,
85 style: &TableStyle,
86 vm_definition_location: NonNull<VMTableDefinition>,
87 ) -> Result<Arc<dyn vm::Table>, String> {
88 self.base.create_vm_table(ty, style, vm_definition_location)
89 }
90}
91
92impl<T: Tunables> CustomTunables<T> {
93 pub fn new(base: T, limit: Pages) -> Self {
94 Self { limit, base }
95 }
96
97 // `adjust_memory` accepts an input memory descriptor requested by guest and sets
98 // a maximum limit for the descriptor, if not assigned earlier.
99 fn adjust_memory(&self, requested: &MemoryType) -> MemoryType {
100 let mut adjusted = *requested;
101 if requested.maximum.is_none() {
102 adjusted.maximum = Some(self.limit);
103 }
104 adjusted
105 }
106
107 // `validate_memory` ensures that the number of pages in the memory descriptor does not
108 // exceed the preset memory limit. It should be called in sequence after `adjust_memory`.
109 fn validate_memory(&self, ty: &MemoryType) -> Result<(), MemoryError> {
110 if ty.minimum > self.limit {
111 return Err(MemoryError::Generic(
112 "Minimum limit exceeds the allowed memory limit".to_string(),
113 ));
114 }
115
116 if let Some(max) = ty.maximum {
117 if max > self.limit {
118 return Err(MemoryError::Generic(
119 "Maximum limit exceeds the allowed memory limit".to_string(),
120 ));
121 }
122 } else {
123 return Err(MemoryError::Generic("Maximum limit unset".to_string()));
124 }
125
126 Ok(())
127 }
128}