wasmparser_nostd/validator/func.rs
1use super::operators::{Frame, OperatorValidator, OperatorValidatorAllocations};
2use crate::{BinaryReader, Result, ValType, VisitOperator};
3use crate::{FunctionBody, Operator, WasmFeatures, WasmModuleResources};
4
5/// Resources necessary to perform validation of a function.
6///
7/// This structure is created by
8/// [`Validator::code_section_entry`](crate::Validator::code_section_entry) and
9/// is created per-function in a WebAssembly module. This structure is suitable
10/// for sending to other threads while the original
11/// [`Validator`](crate::Validator) continues processing other functions.
12pub struct FuncToValidate<T> {
13 /// The reusable heap allocated resources required for Wasm validation.
14 pub resources: T,
15 /// The function index within the Wasm module.
16 pub index: u32,
17 /// The type index of the function.
18 pub ty: u32,
19 /// The Wasm features used for validating the function.
20 pub features: WasmFeatures,
21}
22
23impl<T: WasmModuleResources> FuncToValidate<T> {
24 /// Creates a new function to validate which will have the specified
25 /// configuration parameters:
26 ///
27 /// * `index` - the core wasm function index being validated
28 /// * `ty` - the core wasm type index of the function being validated,
29 /// defining the results and parameters to the function.
30 /// * `resources` - metadata and type information about the module that
31 /// this function is validated within.
32 /// * `features` - enabled WebAssembly features.
33 pub fn new(index: u32, ty: u32, resources: T, features: &WasmFeatures) -> FuncToValidate<T> {
34 FuncToValidate {
35 resources,
36 index,
37 ty,
38 features: *features,
39 }
40 }
41
42 /// Converts this [`FuncToValidate`] into a [`FuncValidator`] using the
43 /// `allocs` provided.
44 ///
45 /// This method, in conjunction with [`FuncValidator::into_allocations`],
46 /// provides a means to reuse allocations across validation of each
47 /// individual function. Note that it is also sufficient to call this
48 /// method with `Default::default()` if no prior allocations are
49 /// available.
50 ///
51 /// # Panics
52 ///
53 /// If a `FuncToValidate` was created with an invalid `ty` index then this
54 /// function will panic.
55 pub fn into_validator(self, allocs: FuncValidatorAllocations) -> FuncValidator<T> {
56 let FuncToValidate {
57 resources,
58 index,
59 ty,
60 features,
61 } = self;
62 let validator =
63 OperatorValidator::new_func(ty, 0, &features, &resources, allocs.0).unwrap();
64 FuncValidator {
65 validator,
66 resources,
67 index,
68 }
69 }
70}
71
72/// Validation context for a WebAssembly function.
73///
74/// This is a finalized validator which is ready to process a [`FunctionBody`].
75/// This is created from the [`FuncToValidate::into_validator`] method.
76pub struct FuncValidator<T> {
77 validator: OperatorValidator,
78 resources: T,
79 index: u32,
80}
81
82/// External handle to the internal allocations used during function validation.
83///
84/// This is created with either the `Default` implementation or with
85/// [`FuncValidator::into_allocations`]. It is then passed as an argument to
86/// [`FuncToValidate::into_validator`] to provide a means of reusing allocations
87/// between each function.
88#[derive(Default)]
89pub struct FuncValidatorAllocations(OperatorValidatorAllocations);
90
91impl<T: WasmModuleResources> FuncValidator<T> {
92 /// Convenience function to validate an entire function's body.
93 ///
94 /// You may not end up using this in final implementations because you'll
95 /// often want to interleave validation with parsing.
96 pub fn validate(&mut self, body: &FunctionBody<'_>) -> Result<()> {
97 let mut reader = body.get_binary_reader();
98 self.read_locals(&mut reader)?;
99 reader.allow_memarg64(self.validator.features.memory64);
100 while !reader.eof() {
101 reader.visit_operator(&mut self.visitor(reader.original_position()))??;
102 }
103 self.finish(reader.original_position())
104 }
105
106 /// Reads the local definitions from the given `BinaryReader`, often sourced
107 /// from a `FunctionBody`.
108 ///
109 /// This function will automatically advance the `BinaryReader` forward,
110 /// leaving reading operators up to the caller afterwards.
111 pub fn read_locals(&mut self, reader: &mut BinaryReader<'_>) -> Result<()> {
112 for _ in 0..reader.read_var_u32()? {
113 let offset = reader.original_position();
114 let cnt = reader.read()?;
115 let ty = reader.read()?;
116 self.define_locals(offset, cnt, ty)?;
117 }
118 Ok(())
119 }
120
121 /// Defines locals into this validator.
122 ///
123 /// This should be used if the application is already reading local
124 /// definitions and there's no need to re-parse the function again.
125 pub fn define_locals(&mut self, offset: usize, count: u32, ty: ValType) -> Result<()> {
126 self.validator.define_locals(offset, count, ty)
127 }
128
129 /// Validates the next operator in a function.
130 ///
131 /// This functions is expected to be called once-per-operator in a
132 /// WebAssembly function. Each operator's offset in the original binary and
133 /// the operator itself are passed to this function to provide more useful
134 /// error messages.
135 pub fn op(&mut self, offset: usize, operator: &Operator<'_>) -> Result<()> {
136 self.visitor(offset).visit_operator(operator)
137 }
138
139 /// Get the operator visitor for the next operator in the function.
140 ///
141 /// The returned visitor is intended to visit just one instruction at the `offset`.
142 ///
143 /// # Example
144 ///
145 /// ```
146 /// # use wasmparser_nostd::{WasmModuleResources, FuncValidator, FunctionBody, Result};
147 /// pub fn validate<R>(validator: &mut FuncValidator<R>, body: &FunctionBody<'_>) -> Result<()>
148 /// where R: WasmModuleResources
149 /// {
150 /// let mut operator_reader = body.get_binary_reader();
151 /// while !operator_reader.eof() {
152 /// let mut visitor = validator.visitor(operator_reader.original_position());
153 /// operator_reader.visit_operator(&mut visitor)??;
154 /// }
155 /// validator.finish(operator_reader.original_position())
156 /// }
157 /// ```
158 pub fn visitor<'this, 'a: 'this>(
159 &'this mut self,
160 offset: usize,
161 ) -> impl VisitOperator<'a, Output = Result<()>> + 'this {
162 self.validator.with_resources(&self.resources, offset)
163 }
164
165 /// Function that must be called after the last opcode has been processed.
166 ///
167 /// This will validate that the function was properly terminated with the
168 /// `end` opcode. If this function is not called then the function will not
169 /// be properly validated.
170 ///
171 /// The `offset` provided to this function will be used as a position for an
172 /// error if validation fails.
173 pub fn finish(&mut self, offset: usize) -> Result<()> {
174 self.validator.finish(offset)
175 }
176
177 /// Returns the underlying module resources that this validator is using.
178 pub fn resources(&self) -> &T {
179 &self.resources
180 }
181
182 /// The index of the function within the module's function index space that
183 /// is being validated.
184 pub fn index(&self) -> u32 {
185 self.index
186 }
187
188 /// Returns the number of defined local variables in the function.
189 pub fn len_locals(&self) -> u32 {
190 self.validator.locals.len_locals()
191 }
192
193 /// Returns the type of the local variable at the given `index` if any.
194 pub fn get_local_type(&self, index: u32) -> Option<ValType> {
195 self.validator.locals.get(index)
196 }
197
198 /// Get the current height of the operand stack.
199 ///
200 /// This returns the height of the whole operand stack for this function,
201 /// not just for the current control frame.
202 pub fn operand_stack_height(&self) -> u32 {
203 self.validator.operand_stack_height() as u32
204 }
205
206 /// Returns the optional value type of the value operand at the given
207 /// `depth` from the top of the operand stack.
208 ///
209 /// - Returns `None` if the `depth` is out of bounds.
210 /// - Returns `Some(None)` if there is a value with unknown type
211 /// at the given `depth`.
212 ///
213 /// # Note
214 ///
215 /// A `depth` of 0 will refer to the last operand on the stack.
216 pub fn get_operand_type(&self, depth: usize) -> Option<Option<ValType>> {
217 self.validator.peek_operand_at(depth)
218 }
219
220 /// Returns the number of frames on the control flow stack.
221 ///
222 /// This returns the height of the whole control stack for this function,
223 /// not just for the current control frame.
224 pub fn control_stack_height(&self) -> u32 {
225 self.validator.control_stack_height() as u32
226 }
227
228 /// Returns a shared reference to the control flow [`Frame`] of the
229 /// control flow stack at the given `depth` if any.
230 ///
231 /// Returns `None` if the `depth` is out of bounds.
232 ///
233 /// # Note
234 ///
235 /// A `depth` of 0 will refer to the last frame on the stack.
236 pub fn get_control_frame(&self, depth: usize) -> Option<&Frame> {
237 self.validator.get_frame(depth)
238 }
239
240 /// Consumes this validator and returns the underlying allocations that
241 /// were used during the validation process.
242 ///
243 /// The returned value here can be paired with
244 /// [`FuncToValidate::into_validator`] to reuse the allocations already
245 /// created by this validator.
246 pub fn into_allocations(self) -> FuncValidatorAllocations {
247 FuncValidatorAllocations(self.validator.into_allocations())
248 }
249}
250
251#[cfg(test)]
252mod tests {
253 use super::*;
254 use crate::WasmFuncType;
255
256 struct EmptyResources;
257
258 impl WasmModuleResources for EmptyResources {
259 type FuncType = EmptyFuncType;
260
261 fn table_at(&self, _at: u32) -> Option<crate::TableType> {
262 todo!()
263 }
264 fn memory_at(&self, _at: u32) -> Option<crate::MemoryType> {
265 todo!()
266 }
267 fn tag_at(&self, _at: u32) -> Option<&Self::FuncType> {
268 todo!()
269 }
270 fn global_at(&self, _at: u32) -> Option<crate::GlobalType> {
271 todo!()
272 }
273 fn func_type_at(&self, _type_idx: u32) -> Option<&Self::FuncType> {
274 Some(&EmptyFuncType)
275 }
276 fn type_of_function(&self, _func_idx: u32) -> Option<&Self::FuncType> {
277 todo!()
278 }
279 fn element_type_at(&self, _at: u32) -> Option<ValType> {
280 todo!()
281 }
282 fn element_count(&self) -> u32 {
283 todo!()
284 }
285 fn data_count(&self) -> Option<u32> {
286 todo!()
287 }
288 fn is_function_referenced(&self, _idx: u32) -> bool {
289 todo!()
290 }
291 }
292
293 struct EmptyFuncType;
294
295 impl WasmFuncType for EmptyFuncType {
296 fn len_inputs(&self) -> usize {
297 0
298 }
299 fn len_outputs(&self) -> usize {
300 0
301 }
302 fn input_at(&self, _at: u32) -> Option<ValType> {
303 todo!()
304 }
305 fn output_at(&self, _at: u32) -> Option<ValType> {
306 todo!()
307 }
308 }
309
310 #[test]
311 fn operand_stack_height() {
312 let mut v = FuncToValidate::new(0, 0, EmptyResources, &Default::default())
313 .into_validator(Default::default());
314
315 // Initially zero values on the stack.
316 assert_eq!(v.operand_stack_height(), 0);
317
318 // Pushing a constant value makes use have one value on the stack.
319 assert!(v.op(0, &Operator::I32Const { value: 0 }).is_ok());
320 assert_eq!(v.operand_stack_height(), 1);
321
322 // Entering a new control block does not affect the stack height.
323 assert!(v
324 .op(
325 1,
326 &Operator::Block {
327 blockty: crate::BlockType::Empty
328 }
329 )
330 .is_ok());
331 assert_eq!(v.operand_stack_height(), 1);
332
333 // Pushing another constant value makes use have two values on the stack.
334 assert!(v.op(2, &Operator::I32Const { value: 99 }).is_ok());
335 assert_eq!(v.operand_stack_height(), 2);
336 }
337}