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