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