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