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}