soroban_wasmi/engine/limits/engine.rs
1use core::fmt::{self, Display};
2
3/// An error that can occur upon parsing or compiling a Wasm module when [`EnforcedLimits`] are set.
4#[derive(Debug, Copy, Clone)]
5pub enum EnforcedLimitsError {
6 /// When a Wasm module exceeds the global variable limit.
7 TooManyGlobals { limit: u32 },
8 /// When a Wasm module exceeds the table limit.
9 TooManyTables { limit: u32 },
10 /// When a Wasm module exceeds the function limit.
11 TooManyFunctions { limit: u32 },
12 /// When a Wasm module exceeds the linear memory limit.
13 TooManyMemories { limit: u32 },
14 /// When a Wasm module exceeds the element segment limit.
15 TooManyElementSegments { limit: u32 },
16 /// When a Wasm module exceeds the data segment limit.
17 TooManyDataSegments { limit: u32 },
18 /// When a Wasm module exceeds the function parameter limit.
19 TooManyParameters { limit: usize },
20 /// When a Wasm module exceeds the function results limit.
21 TooManyResults { limit: usize },
22 /// When a Wasm module exceeds the average bytes per function limit.
23 MinAvgBytesPerFunction { limit: u32, avg: u32 },
24}
25
26#[cfg(feature = "std")]
27impl std::error::Error for EnforcedLimitsError {}
28
29impl Display for EnforcedLimitsError {
30 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
31 match self {
32 Self::TooManyGlobals { limit } => write!(
33 f,
34 "the Wasm module exceeds the limit of {limit} global variables"
35 ),
36 Self::TooManyTables { limit } => {
37 write!(f, "the Wasm module exceeds the limit of {limit} tables")
38 }
39 Self::TooManyFunctions { limit } => {
40 write!(f, "the Wasm modules exceeds the limit of {limit} functions")
41 }
42 Self::TooManyMemories { limit } => {
43 write!(f, "the Wasm module exceeds the limit of {limit} memories")
44 }
45 Self::TooManyElementSegments { limit } => write!(
46 f,
47 "the Wasm module exceeds the limit of {limit} active element segments"
48 ),
49 Self::TooManyDataSegments { limit } => write!(
50 f,
51 "the Wasm module exceeds the limit of {limit} active data segments",
52 ),
53 Self::TooManyParameters { limit } => {
54 write!(f, "a function type exceeds the limit of {limit} parameters",)
55 }
56 Self::TooManyResults { limit } => {
57 write!(f, "a function type exceeds the limit of {limit} results",)
58 }
59 Self::MinAvgBytesPerFunction { limit, avg } => write!(
60 f,
61 "the Wasm module failed to meet the minumum average bytes per function of {limit}: \
62 avg={avg}"
63 ),
64 }
65 }
66}
67
68/// Stores customizable limits for the [`Engine`] when parsing or compiling Wasm modules.
69///
70/// By default no limits are enforced.
71///
72/// [`Engine`]: crate::Engine
73#[derive(Debug, Default, Copy, Clone)]
74pub struct EnforcedLimits {
75 /// Number of global variables a single Wasm module can have at most.
76 ///
77 /// # Note
78 ///
79 /// - This is checked in [`Module::new`] or [`Module::new_unchecked`].
80 /// - `None` means the limit is not enforced.
81 ///
82 /// [`Module::new`]: crate::Module::new
83 /// [`Module::new_unchecked`]: crate::Module::new_unchecked
84 pub max_globals: Option<u32>,
85 /// Number of functions a single Wasm module can have at most.
86 ///
87 /// # Note
88 ///
89 /// - This is checked in [`Module::new`] or [`Module::new_unchecked`].
90 /// - `None` means the limit is not enforced.
91 ///
92 /// [`Module::new`]: crate::Module::new
93 /// [`Module::new_unchecked`]: crate::Module::new_unchecked
94 pub max_functions: Option<u32>,
95 /// Number of tables a single Wasm module can have at most.
96 ///
97 /// # Note
98 ///
99 /// - This is checked in [`Module::new`] or [`Module::new_unchecked`].
100 /// - This is only relevant if the Wasm `reference-types` proposal is enabled.
101 /// - `None` means the limit is not enforced.
102 ///
103 /// [`Module::new`]: crate::Module::new
104 /// [`Module::new_unchecked`]: crate::Module::new_unchecked
105 pub max_tables: Option<u32>,
106 /// Number of table element segments a single Wasm module can have at most.
107 ///
108 /// # Note
109 ///
110 /// - This is checked in [`Module::new`] or [`Module::new_unchecked`].
111 /// - This is only relevant if the Wasm `reference-types` proposal is enabled.
112 /// - `None` means the limit is not enforced.
113 ///
114 /// [`Module::new`]: crate::Module::new
115 /// [`Module::new_unchecked`]: crate::Module::new_unchecked
116 pub max_element_segments: Option<u32>,
117 /// Number of linear memories a single Wasm module can have.
118 ///
119 /// # Note
120 ///
121 /// - This is checked in [`Module::new`] or [`Module::new_unchecked`].
122 /// - This is only relevant if the Wasm `multi-memories` proposal is enabled
123 /// which is not supported in Wasmi at the time of writing this comment.
124 /// - `None` means the limit is not enforced.
125 ///
126 /// [`Module::new`]: crate::Module::new
127 /// [`Module::new_unchecked`]: crate::Module::new_unchecked
128 pub max_memories: Option<u32>,
129 /// Number of linear memory data segments a single Wasm module can have at most.
130 ///
131 /// # Note
132 ///
133 /// - This is checked in [`Module::new`] or [`Module::new_unchecked`].
134 /// - This is only relevant if the Wasm `reference-types` proposal is enabled.
135 /// - `None` means the limit is not enforced.
136 ///
137 /// [`Module::new`]: crate::Module::new
138 /// [`Module::new_unchecked`]: crate::Module::new_unchecked
139 pub max_data_segments: Option<u32>,
140 /// Limits the number of parameter of all functions and control structures.
141 ///
142 /// # Note
143 ///
144 /// - This is checked in [`Module::new`] or [`Module::new_unchecked`].
145 /// - `None` means the limit is not enforced.
146 ///
147 /// [`Engine`]: crate::Engine
148 /// [`Module::new`]: crate::Module::new
149 /// [`Module::new_unchecked`]: crate::Module::new_unchecked
150 pub max_params: Option<usize>,
151 /// Limits the number of results of all functions and control structures.
152 ///
153 /// # Note
154 ///
155 /// - This is only relevant if the Wasm `multi-value` proposal is enabled.
156 /// - This is checked in [`Module::new`] or [`Module::new_unchecked`].
157 /// - `None` means the limit is not enforced.
158 ///
159 /// [`Engine`]: crate::Engine
160 /// [`Module::new`]: crate::Module::new
161 /// [`Module::new_unchecked`]: crate::Module::new_unchecked
162 pub max_results: Option<usize>,
163 /// Minimum number of bytes a function must have on average.
164 ///
165 /// # Note
166 ///
167 /// - This is checked in [`Module::new`] or [`Module::new_unchecked`].
168 /// - This limitation might seem arbitrary but is important to defend against
169 /// malicious inputs targeting lazy compilation.
170 /// - `None` means the limit is not enforced.
171 ///
172 /// [`Module::new`]: crate::Module::new
173 /// [`Module::new_unchecked`]: crate::Module::new_unchecked
174 pub min_avg_bytes_per_function: Option<AvgBytesPerFunctionLimit>,
175}
176
177/// The limit for average bytes per function limit and the threshold at which it is enforced.
178#[derive(Debug, Copy, Clone)]
179pub struct AvgBytesPerFunctionLimit {
180 /// The number of Wasm module bytes at which the limit is actually enforced.
181 ///
182 /// This represents the total number of bytes of all Wasm function bodies in the Wasm module combined.
183 ///
184 /// # Note
185 ///
186 /// - A `req_funcs_bytes` of 0 always enforces the `min_avg_bytes_per_function` limit.
187 /// - The `req_funcs_bytes` field exists to filter out small Wasm modules
188 /// that cannot seriously be used to attack the Wasmi compilation.
189 pub req_funcs_bytes: u32,
190 /// The minimum number of bytes a function must have on average.
191 pub min_avg_bytes_per_function: u32,
192}
193
194impl EnforcedLimits {
195 /// A strict set of limits that makes use of Wasmi implementation details.
196 ///
197 /// This set of strict enforced rules can be used by Wasmi users in order
198 /// to safeguard themselves against malicious actors trying to attack the Wasmi
199 /// compilation procedures.
200 pub fn strict() -> Self {
201 Self {
202 max_globals: Some(1000),
203 max_functions: Some(10_000),
204 max_tables: Some(100),
205 max_element_segments: Some(1000),
206 max_memories: Some(1),
207 max_data_segments: Some(1000),
208 max_params: Some(32),
209 max_results: Some(32),
210 min_avg_bytes_per_function: Some(AvgBytesPerFunctionLimit {
211 // If all function bodies combined use a total of at least 1000 bytes
212 // the average bytes per function body limit is enforced.
213 req_funcs_bytes: 1000,
214 // Compiled and optimized Wasm modules usually average out on 100-2500
215 // bytes per Wasm function. Thus the chosen limit is way below this threshold
216 // and should not be exceeded for non-malicous Wasm modules.
217 min_avg_bytes_per_function: 40,
218 }),
219 }
220 }
221}