wasmparser/validator/
func.rs

1use super::operators::{Frame, OperatorValidator, OperatorValidatorAllocations};
2use crate::{BinaryReader, Result, ValType, VisitOperator};
3use crate::{FrameStack, FunctionBody, ModuleArity, 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
66impl<T: WasmModuleResources> ModuleArity for FuncValidator<T> {
67    fn sub_type_at(&self, type_idx: u32) -> Option<&crate::SubType> {
68        self.resources.sub_type_at(type_idx)
69    }
70
71    fn tag_type_arity(&self, at: u32) -> Option<(u32, u32)> {
72        let ty = self.resources.tag_at(at)?;
73        Some((
74            u32::try_from(ty.params().len()).unwrap(),
75            u32::try_from(ty.results().len()).unwrap(),
76        ))
77    }
78
79    fn type_index_of_function(&self, func_idx: u32) -> Option<u32> {
80        self.resources.type_index_of_function(func_idx)
81    }
82
83    fn func_type_of_cont_type(&self, cont_ty: &crate::ContType) -> Option<&crate::FuncType> {
84        let id = cont_ty.0.as_core_type_id()?;
85        Some(self.resources.sub_type_at_id(id).unwrap_func())
86    }
87
88    fn sub_type_of_ref_type(&self, rt: &crate::RefType) -> Option<&crate::SubType> {
89        let id = rt.type_index()?.as_core_type_id()?;
90        Some(self.resources.sub_type_at_id(id))
91    }
92
93    fn control_stack_height(&self) -> u32 {
94        u32::try_from(self.validator.control_stack_height()).unwrap()
95    }
96
97    fn label_block(&self, depth: u32) -> Option<(crate::BlockType, crate::FrameKind)> {
98        self.validator.jump(depth)
99    }
100}
101
102/// External handle to the internal allocations used during function validation.
103///
104/// This is created with either the `Default` implementation or with
105/// [`FuncValidator::into_allocations`]. It is then passed as an argument to
106/// [`FuncToValidate::into_validator`] to provide a means of reusing allocations
107/// between each function.
108#[derive(Default)]
109pub struct FuncValidatorAllocations(OperatorValidatorAllocations);
110
111impl<T: WasmModuleResources> FuncValidator<T> {
112    /// Convenience function to validate an entire function's body.
113    ///
114    /// You may not end up using this in final implementations because you'll
115    /// often want to interleave validation with parsing.
116    pub fn validate(&mut self, body: &FunctionBody<'_>) -> Result<()> {
117        let mut reader = body.get_binary_reader();
118        self.read_locals(&mut reader)?;
119        #[cfg(feature = "features")]
120        {
121            reader.set_features(self.validator.features);
122        }
123        while !reader.eof() {
124            // In a debug build, verify that the validator's pops and pushes to and from
125            // the operand stack match the operator's arity.
126            #[cfg(debug_assertions)]
127            let (ops_before, arity) = {
128                let op = reader.peek_operator(&self.visitor(reader.original_position()))?;
129                let arity = op.operator_arity(&self.visitor(reader.original_position()));
130                (reader.clone(), arity)
131            };
132
133            reader.visit_operator(&mut self.visitor(reader.original_position()))??;
134
135            #[cfg(debug_assertions)]
136            {
137                let (params, results) = arity.ok_or(format_err!(
138                    reader.original_position(),
139                    "could not calculate operator arity"
140                ))?;
141
142                // Analyze the log to determine the actual, externally visible
143                // pop/push count. This allows us to hide the fact that we might
144                // push and then pop a temporary while validating an
145                // instruction, which shouldn't be visible from the outside.
146                let mut pop_count = 0;
147                let mut push_count = 0;
148                for op in self.validator.pop_push_log.drain(..) {
149                    match op {
150                        true => push_count += 1,
151                        false if push_count > 0 => push_count -= 1,
152                        false => pop_count += 1,
153                    }
154                }
155
156                if pop_count != params || push_count != results {
157                    panic!(
158                        "\
159arity mismatch in validation
160    operator: {:?}
161    expected: {params} -> {results}
162    got       {pop_count} -> {push_count}",
163                        ops_before.peek_operator(&self.visitor(ops_before.original_position()))?,
164                    );
165                }
166            }
167        }
168        reader.finish_expression(&self.visitor(reader.original_position()))
169    }
170
171    /// Reads the local definitions from the given `BinaryReader`, often sourced
172    /// from a `FunctionBody`.
173    ///
174    /// This function will automatically advance the `BinaryReader` forward,
175    /// leaving reading operators up to the caller afterwards.
176    pub fn read_locals(&mut self, reader: &mut BinaryReader<'_>) -> Result<()> {
177        for _ in 0..reader.read_var_u32()? {
178            let offset = reader.original_position();
179            let cnt = reader.read()?;
180            let ty = reader.read()?;
181            self.define_locals(offset, cnt, ty)?;
182        }
183        Ok(())
184    }
185
186    /// Defines locals into this validator.
187    ///
188    /// This should be used if the application is already reading local
189    /// definitions and there's no need to re-parse the function again.
190    pub fn define_locals(&mut self, offset: usize, count: u32, ty: ValType) -> Result<()> {
191        self.validator
192            .define_locals(offset, count, ty, &self.resources)
193    }
194
195    /// Validates the next operator in a function.
196    ///
197    /// This functions is expected to be called once-per-operator in a
198    /// WebAssembly function. Each operator's offset in the original binary and
199    /// the operator itself are passed to this function to provide more useful
200    /// error messages.
201    pub fn op(&mut self, offset: usize, operator: &Operator<'_>) -> Result<()> {
202        self.visitor(offset).visit_operator(operator)
203    }
204
205    /// Get the operator visitor for the next operator in the function.
206    ///
207    /// The returned visitor is intended to visit just one instruction at the `offset`.
208    ///
209    /// # Example
210    ///
211    /// ```
212    /// # use wasmparser::{WasmModuleResources, FuncValidator, FunctionBody, Result};
213    /// pub fn validate<R>(validator: &mut FuncValidator<R>, body: &FunctionBody<'_>) -> Result<()>
214    /// where R: WasmModuleResources
215    /// {
216    ///     let mut operator_reader = body.get_binary_reader_for_operators()?;
217    ///     while !operator_reader.eof() {
218    ///         let mut visitor = validator.visitor(operator_reader.original_position());
219    ///         operator_reader.visit_operator(&mut visitor)??;
220    ///     }
221    ///     operator_reader.finish_expression(&validator.visitor(operator_reader.original_position()))
222    /// }
223    /// ```
224    pub fn visitor<'this, 'a: 'this>(
225        &'this mut self,
226        offset: usize,
227    ) -> impl VisitOperator<'a, Output = Result<()>> + ModuleArity + FrameStack + 'this {
228        self.validator.with_resources(&self.resources, offset)
229    }
230
231    /// Same as [`FuncValidator::visitor`] except that the returned type
232    /// implements the [`VisitSimdOperator`](crate::VisitSimdOperator) trait as
233    /// well.
234    #[cfg(feature = "simd")]
235    pub fn simd_visitor<'this, 'a: 'this>(
236        &'this mut self,
237        offset: usize,
238    ) -> impl crate::VisitSimdOperator<'a, Output = Result<()>> + ModuleArity + 'this {
239        self.validator.with_resources_simd(&self.resources, offset)
240    }
241
242    /// Returns the Wasm features enabled for this validator.
243    pub fn features(&self) -> &WasmFeatures {
244        &self.validator.features
245    }
246
247    /// Returns the underlying module resources that this validator is using.
248    pub fn resources(&self) -> &T {
249        &self.resources
250    }
251
252    /// The index of the function within the module's function index space that
253    /// is being validated.
254    pub fn index(&self) -> u32 {
255        self.index
256    }
257
258    /// Returns the number of defined local variables in the function.
259    pub fn len_locals(&self) -> u32 {
260        self.validator.locals.len_locals()
261    }
262
263    /// Returns the type of the local variable at the given `index` if any.
264    pub fn get_local_type(&self, index: u32) -> Option<ValType> {
265        self.validator.locals.get(index)
266    }
267
268    /// Get the current height of the operand stack.
269    ///
270    /// This returns the height of the whole operand stack for this function,
271    /// not just for the current control frame.
272    pub fn operand_stack_height(&self) -> u32 {
273        self.validator.operand_stack_height() as u32
274    }
275
276    /// Returns the optional value type of the value operand at the given
277    /// `depth` from the top of the operand stack.
278    ///
279    /// - Returns `None` if the `depth` is out of bounds.
280    /// - Returns `Some(None)` if there is a value with unknown type
281    /// at the given `depth`.
282    ///
283    /// # Note
284    ///
285    /// A `depth` of 0 will refer to the last operand on the stack.
286    pub fn get_operand_type(&self, depth: usize) -> Option<Option<ValType>> {
287        self.validator.peek_operand_at(depth)
288    }
289
290    /// Returns the number of frames on the control flow stack.
291    ///
292    /// This returns the height of the whole control stack for this function,
293    /// not just for the current control frame.
294    pub fn control_stack_height(&self) -> u32 {
295        self.validator.control_stack_height() as u32
296    }
297
298    /// Returns a shared reference to the control flow [`Frame`] of the
299    /// control flow stack at the given `depth` if any.
300    ///
301    /// Returns `None` if the `depth` is out of bounds.
302    ///
303    /// # Note
304    ///
305    /// A `depth` of 0 will refer to the last frame on the stack.
306    pub fn get_control_frame(&self, depth: usize) -> Option<&Frame> {
307        self.validator.get_frame(depth)
308    }
309
310    /// Consumes this validator and returns the underlying allocations that
311    /// were used during the validation process.
312    ///
313    /// The returned value here can be paired with
314    /// [`FuncToValidate::into_validator`] to reuse the allocations already
315    /// created by this validator.
316    pub fn into_allocations(self) -> FuncValidatorAllocations {
317        FuncValidatorAllocations(self.validator.into_allocations())
318    }
319}
320
321#[cfg(test)]
322mod tests {
323    use super::*;
324    use crate::types::CoreTypeId;
325    use crate::{HeapType, Parser, RefType, Validator};
326    use alloc::vec::Vec;
327
328    struct EmptyResources(crate::SubType);
329
330    impl Default for EmptyResources {
331        fn default() -> Self {
332            EmptyResources(crate::SubType {
333                supertype_idx: None,
334                is_final: true,
335                composite_type: crate::CompositeType {
336                    inner: crate::CompositeInnerType::Func(crate::FuncType::new([], [])),
337                    shared: false,
338                },
339            })
340        }
341    }
342
343    impl WasmModuleResources for EmptyResources {
344        fn table_at(&self, _at: u32) -> Option<crate::TableType> {
345            todo!()
346        }
347        fn memory_at(&self, _at: u32) -> Option<crate::MemoryType> {
348            todo!()
349        }
350        fn tag_at(&self, _at: u32) -> Option<&crate::FuncType> {
351            todo!()
352        }
353        fn global_at(&self, _at: u32) -> Option<crate::GlobalType> {
354            todo!()
355        }
356        fn sub_type_at(&self, _type_idx: u32) -> Option<&crate::SubType> {
357            Some(&self.0)
358        }
359        fn sub_type_at_id(&self, _id: CoreTypeId) -> &crate::SubType {
360            todo!()
361        }
362        fn type_id_of_function(&self, _at: u32) -> Option<CoreTypeId> {
363            todo!()
364        }
365        fn type_index_of_function(&self, _at: u32) -> Option<u32> {
366            todo!()
367        }
368        fn check_heap_type(&self, _t: &mut HeapType, _offset: usize) -> Result<()> {
369            Ok(())
370        }
371        fn top_type(&self, _heap_type: &HeapType) -> HeapType {
372            todo!()
373        }
374        fn element_type_at(&self, _at: u32) -> Option<crate::RefType> {
375            todo!()
376        }
377        fn is_subtype(&self, _t1: ValType, _t2: ValType) -> bool {
378            todo!()
379        }
380        fn is_shared(&self, _ty: RefType) -> bool {
381            todo!()
382        }
383        fn element_count(&self) -> u32 {
384            todo!()
385        }
386        fn data_count(&self) -> Option<u32> {
387            todo!()
388        }
389        fn is_function_referenced(&self, _idx: u32) -> bool {
390            todo!()
391        }
392    }
393
394    #[test]
395    fn operand_stack_height() {
396        let mut v = FuncToValidate {
397            index: 0,
398            ty: 0,
399            resources: EmptyResources::default(),
400            features: Default::default(),
401        }
402        .into_validator(Default::default());
403
404        // Initially zero values on the stack.
405        assert_eq!(v.operand_stack_height(), 0);
406
407        // Pushing a constant value makes use have one value on the stack.
408        assert!(v.op(0, &Operator::I32Const { value: 0 }).is_ok());
409        assert_eq!(v.operand_stack_height(), 1);
410
411        // Entering a new control block does not affect the stack height.
412        assert!(
413            v.op(
414                1,
415                &Operator::Block {
416                    blockty: crate::BlockType::Empty
417                }
418            )
419            .is_ok()
420        );
421        assert_eq!(v.operand_stack_height(), 1);
422
423        // Pushing another constant value makes use have two values on the stack.
424        assert!(v.op(2, &Operator::I32Const { value: 99 }).is_ok());
425        assert_eq!(v.operand_stack_height(), 2);
426    }
427
428    fn assert_arity(wat: &str, expected: Vec<Vec<(u32, u32)>>) {
429        let wasm = wat::parse_str(wat).unwrap();
430        assert!(Validator::new().validate_all(&wasm).is_ok());
431
432        let parser = Parser::new(0);
433        let mut validator = Validator::new();
434
435        let mut actual = vec![];
436
437        for payload in parser.parse_all(&wasm) {
438            let payload = payload.unwrap();
439            match payload {
440                crate::Payload::CodeSectionEntry(body) => {
441                    let mut arity = vec![];
442                    let mut func_validator = validator
443                        .code_section_entry(&body)
444                        .unwrap()
445                        .into_validator(FuncValidatorAllocations::default());
446                    let ops = body.get_operators_reader().unwrap();
447                    for op in ops.into_iter() {
448                        let op = op.unwrap();
449                        arity.push(
450                            op.operator_arity(&func_validator)
451                                .expect("valid operators should have arity"),
452                        );
453                        func_validator.op(usize::MAX, &op).expect("should be valid");
454                    }
455                    actual.push(arity);
456                }
457                p => {
458                    validator.payload(&p).unwrap();
459                }
460            }
461        }
462
463        assert_eq!(actual, expected);
464    }
465
466    #[test]
467    fn arity_smoke_test() {
468        let wasm = r#"
469            (module
470                (type $pair (struct (field i32) (field i32)))
471
472                (func $add (param i32 i32) (result i32)
473                    local.get 0
474                    local.get 1
475                    i32.add
476                )
477
478                (func $f (param i32 i32) (result (ref null $pair))
479                    local.get 0
480                    local.get 1
481                    call $add
482                    if (result (ref null $pair))
483                    local.get 0
484                    local.get 1
485                      struct.new $pair
486                    else
487                      unreachable
488                      i32.add
489                      unreachable
490                    end
491                )
492            )
493        "#;
494
495        assert_arity(
496            wasm,
497            vec![
498                // $add
499                vec![
500                    // local.get 0
501                    (0, 1),
502                    // local.get 1
503                    (0, 1),
504                    // i32.add
505                    (2, 1),
506                    // end
507                    (1, 1),
508                ],
509                // $f
510                vec![
511                    // local.get 0
512                    (0, 1),
513                    // local.get 1
514                    (0, 1),
515                    // call $add
516                    (2, 1),
517                    // if
518                    (1, 0),
519                    // local.get 0
520                    (0, 1),
521                    // local.get 1
522                    (0, 1),
523                    // struct.new $pair
524                    (2, 1),
525                    // else
526                    (1, 0),
527                    // unreachable,
528                    (0, 0),
529                    // i32.add
530                    (2, 1),
531                    // unreachable
532                    (0, 0),
533                    // end
534                    (1, 1),
535                    // implicit end
536                    (1, 1),
537                ],
538            ],
539        );
540    }
541
542    #[test]
543    fn arity_if_no_else_same_params_and_results() {
544        let wasm = r#"
545            (module
546                (func (export "f") (param i64 i32) (result i64)
547                    (local.get 0)
548                    (local.get 1)
549                    ;; If with no else. Same number of params and results.
550                    if (param i64) (result i64)
551                        drop
552                        i64.const -1
553                    end
554                )
555            )
556        "#;
557
558        assert_arity(
559            wasm,
560            vec![vec![
561                // local.get 0
562                (0, 1),
563                // local.get 1
564                (0, 1),
565                // if
566                (2, 1),
567                // drop
568                (1, 0),
569                // i64.const -1
570                (0, 1),
571                // end
572                (1, 1),
573                // implicit end
574                (1, 1),
575            ]],
576        );
577    }
578
579    #[test]
580    fn arity_br_table() {
581        let wasm = r#"
582            (module
583                (func (export "f") (result i32 i32)
584                    i32.const 0
585                    i32.const 1
586                    i32.const 2
587                    br_table 0 0
588                )
589            )
590        "#;
591
592        assert_arity(
593            wasm,
594            vec![vec![
595                // i32.const 0
596                (0, 1),
597                // i32.const 1
598                (0, 1),
599                // i32.const 2
600                (0, 1),
601                // br_table
602                (3, 0),
603                // implicit end
604                (2, 2),
605            ]],
606        );
607    }
608}