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}