wasmparser/validator/
core.rs

1//! State relating to validating a WebAssembly module.
2//!
3use super::{
4    check_max, combine_type_sizes,
5    operators::{ty_to_str, OperatorValidator, OperatorValidatorAllocations},
6    types::{EntityType, Type, TypeAlloc, TypeId, TypeList},
7};
8use crate::limits::*;
9use crate::validator::core::arc::MaybeOwned;
10use crate::{
11    BinaryReaderError, ConstExpr, Data, DataKind, Element, ElementKind, ExternalKind, FuncType,
12    Global, GlobalType, HeapType, MemoryType, RefType, Result, Table, TableInit, TableType,
13    TagType, TypeRef, ValType, VisitOperator, WasmFeatures, WasmFuncType, WasmModuleResources,
14};
15use indexmap::IndexMap;
16use std::mem;
17use std::{collections::HashSet, sync::Arc};
18
19// Section order for WebAssembly modules.
20//
21// Component sections are unordered and allow for duplicates,
22// so this isn't used for components.
23#[derive(Copy, Clone, PartialOrd, Ord, PartialEq, Eq, Debug)]
24pub enum Order {
25    Initial,
26    Type,
27    Import,
28    Function,
29    Table,
30    Memory,
31    Tag,
32    Global,
33    Export,
34    Start,
35    Element,
36    DataCount,
37    Code,
38    Data,
39}
40
41impl Default for Order {
42    fn default() -> Order {
43        Order::Initial
44    }
45}
46
47#[derive(Default)]
48pub(crate) struct ModuleState {
49    /// Internal state that is incrementally built-up for the module being
50    /// validated. This houses type information for all wasm items, like
51    /// functions. Note that this starts out as a solely owned `Arc<T>` so we can
52    /// get mutable access, but after we get to the code section this is never
53    /// mutated to we can clone it cheaply and hand it to sub-validators.
54    pub module: arc::MaybeOwned<Module>,
55
56    /// Where we are, order-wise, in the wasm binary.
57    order: Order,
58
59    /// The number of data segments in the data section (if present).
60    pub data_segment_count: u32,
61
62    /// The number of functions we expect to be defined in the code section, or
63    /// basically the length of the function section if it was found. The next
64    /// index is where we are, in the code section index space, for the next
65    /// entry in the code section (used to figure out what type is next for the
66    /// function being validated).
67    pub expected_code_bodies: Option<u32>,
68
69    const_expr_allocs: OperatorValidatorAllocations,
70
71    /// When parsing the code section, represents the current index in the section.
72    code_section_index: Option<usize>,
73}
74
75impl ModuleState {
76    pub fn update_order(&mut self, order: Order, offset: usize) -> Result<()> {
77        if self.order >= order {
78            return Err(BinaryReaderError::new("section out of order", offset));
79        }
80
81        self.order = order;
82
83        Ok(())
84    }
85
86    pub fn validate_end(&self, offset: usize) -> Result<()> {
87        // Ensure that the data count section, if any, was correct.
88        if let Some(data_count) = self.module.data_count {
89            if data_count != self.data_segment_count {
90                return Err(BinaryReaderError::new(
91                    "data count and data section have inconsistent lengths",
92                    offset,
93                ));
94            }
95        }
96        // Ensure that the function section, if nonzero, was paired with a code
97        // section with the appropriate length.
98        if let Some(n) = self.expected_code_bodies {
99            if n > 0 {
100                return Err(BinaryReaderError::new(
101                    "function and code section have inconsistent lengths",
102                    offset,
103                ));
104            }
105        }
106
107        Ok(())
108    }
109
110    pub fn next_code_index_and_type(&mut self, offset: usize) -> Result<(u32, u32)> {
111        let index = self
112            .code_section_index
113            .get_or_insert(self.module.num_imported_functions as usize);
114
115        if *index >= self.module.functions.len() {
116            return Err(BinaryReaderError::new(
117                "code section entry exceeds number of functions",
118                offset,
119            ));
120        }
121
122        let ty = self.module.functions[*index];
123        *index += 1;
124
125        Ok(((*index - 1) as u32, ty))
126    }
127
128    pub fn add_global(
129        &mut self,
130        global: Global,
131        features: &WasmFeatures,
132        types: &TypeList,
133        offset: usize,
134    ) -> Result<()> {
135        self.module
136            .check_global_type(&global.ty, features, types, offset)?;
137        self.check_const_expr(&global.init_expr, global.ty.content_type, features, types)?;
138        self.module.assert_mut().globals.push(global.ty);
139        Ok(())
140    }
141
142    pub fn add_table(
143        &mut self,
144        table: Table<'_>,
145        features: &WasmFeatures,
146        types: &TypeList,
147        offset: usize,
148    ) -> Result<()> {
149        self.module
150            .check_table_type(&table.ty, features, types, offset)?;
151
152        match &table.init {
153            TableInit::RefNull => {
154                if !table.ty.element_type.nullable {
155                    bail!(offset, "type mismatch: non-defaultable element type");
156                }
157            }
158            TableInit::Expr(expr) => {
159                if !features.function_references {
160                    bail!(
161                        offset,
162                        "tables with expression initializers require \
163                         the function-references proposal"
164                    );
165                }
166                self.check_const_expr(expr, table.ty.element_type.into(), features, types)?;
167            }
168        }
169        self.module.assert_mut().tables.push(table.ty);
170        Ok(())
171    }
172
173    pub fn add_data_segment(
174        &mut self,
175        data: Data,
176        features: &WasmFeatures,
177        types: &TypeList,
178        offset: usize,
179    ) -> Result<()> {
180        match data.kind {
181            DataKind::Passive => Ok(()),
182            DataKind::Active {
183                memory_index,
184                offset_expr,
185            } => {
186                let ty = self.module.memory_at(memory_index, offset)?.index_type();
187                self.check_const_expr(&offset_expr, ty, features, types)
188            }
189        }
190    }
191
192    pub fn add_element_segment(
193        &mut self,
194        e: Element,
195        features: &WasmFeatures,
196        types: &TypeList,
197        offset: usize,
198    ) -> Result<()> {
199        // the `funcref` value type is allowed all the way back to the MVP, so
200        // don't check it here
201        if e.ty != RefType::FUNCREF {
202            self.module
203                .check_value_type(ValType::Ref(e.ty), features, types, offset)?;
204        }
205        match e.kind {
206            ElementKind::Active {
207                table_index,
208                offset_expr,
209            } => {
210                let table = self.module.table_at(table_index, offset)?;
211                if !self
212                    .module
213                    .matches(ValType::Ref(e.ty), ValType::Ref(table.element_type), types)
214                {
215                    return Err(BinaryReaderError::new(
216                        format!(
217                            "type mismatch: invalid element type `{}` for table type `{}`",
218                            ty_to_str(e.ty.into()),
219                            ty_to_str(table.element_type.into()),
220                        ),
221                        offset,
222                    ));
223                }
224
225                self.check_const_expr(&offset_expr, ValType::I32, features, types)?;
226            }
227            ElementKind::Passive | ElementKind::Declared => {
228                if !features.bulk_memory {
229                    return Err(BinaryReaderError::new(
230                        "bulk memory must be enabled",
231                        offset,
232                    ));
233                }
234            }
235        }
236
237        let validate_count = |count: u32| -> Result<(), BinaryReaderError> {
238            if count > MAX_WASM_TABLE_ENTRIES as u32 {
239                Err(BinaryReaderError::new(
240                    "number of elements is out of bounds",
241                    offset,
242                ))
243            } else {
244                Ok(())
245            }
246        };
247        match e.items {
248            crate::ElementItems::Functions(reader) => {
249                let count = reader.count();
250                if !e.ty.nullable && count <= 0 {
251                    return Err(BinaryReaderError::new(
252                        "a non-nullable element must come with an initialization expression",
253                        offset,
254                    ));
255                }
256                validate_count(count)?;
257                for f in reader.into_iter_with_offsets() {
258                    let (offset, f) = f?;
259                    self.module.get_func_type(f, types, offset)?;
260                    self.module.assert_mut().function_references.insert(f);
261                }
262            }
263            crate::ElementItems::Expressions(reader) => {
264                validate_count(reader.count())?;
265                for expr in reader {
266                    self.check_const_expr(&expr?, ValType::Ref(e.ty), features, types)?;
267                }
268            }
269        }
270        self.module.assert_mut().element_types.push(e.ty);
271        Ok(())
272    }
273
274    fn check_const_expr(
275        &mut self,
276        expr: &ConstExpr<'_>,
277        expected_ty: ValType,
278        features: &WasmFeatures,
279        types: &TypeList,
280    ) -> Result<()> {
281        let mut validator = VisitConstOperator {
282            offset: 0,
283            order: self.order,
284            uninserted_funcref: false,
285            ops: OperatorValidator::new_const_expr(
286                features,
287                expected_ty,
288                mem::take(&mut self.const_expr_allocs),
289            ),
290            resources: OperatorValidatorResources {
291                types,
292                module: &mut self.module,
293            },
294        };
295
296        let mut ops = expr.get_operators_reader();
297        while !ops.eof() {
298            validator.offset = ops.original_position();
299            ops.visit_operator(&mut validator)??;
300        }
301        validator.ops.finish(ops.original_position())?;
302
303        // See comment in `RefFunc` below for why this is an assert.
304        assert!(!validator.uninserted_funcref);
305
306        self.const_expr_allocs = validator.ops.into_allocations();
307
308        return Ok(());
309
310        struct VisitConstOperator<'a> {
311            offset: usize,
312            uninserted_funcref: bool,
313            ops: OperatorValidator,
314            resources: OperatorValidatorResources<'a>,
315            order: Order,
316        }
317
318        impl VisitConstOperator<'_> {
319            fn validator(&mut self) -> impl VisitOperator<'_, Output = Result<()>> {
320                self.ops.with_resources(&self.resources, self.offset)
321            }
322
323            fn validate_extended_const(&mut self) -> Result<()> {
324                if self.ops.features.extended_const {
325                    Ok(())
326                } else {
327                    Err(BinaryReaderError::new(
328                        "constant expression required: non-constant operator",
329                        self.offset,
330                    ))
331                }
332            }
333
334            fn validate_global(&mut self, index: u32) -> Result<()> {
335                let module = &self.resources.module;
336                let global = module.global_at(index, self.offset)?;
337                if index >= module.num_imported_globals {
338                    return Err(BinaryReaderError::new(
339                        "constant expression required: global.get of locally defined global",
340                        self.offset,
341                    ));
342                }
343                if global.mutable {
344                    return Err(BinaryReaderError::new(
345                        "constant expression required: global.get of mutable global",
346                        self.offset,
347                    ));
348                }
349                Ok(())
350            }
351
352            // Functions in initialization expressions are only valid in
353            // element segment initialization expressions and globals. In
354            // these contexts we want to record all function references.
355            //
356            // Initialization expressions can also be found in the data
357            // section, however. A `RefFunc` instruction in those situations
358            // is always invalid and needs to produce a validation error. In
359            // this situation, though, we can no longer modify
360            // the state since it's been "snapshot" already for
361            // parallel validation of functions.
362            //
363            // If we cannot modify the function references then this function
364            // *should* result in a validation error, but we defer that
365            // validation error to happen later. The `uninserted_funcref`
366            // boolean here is used to track this and will cause a panic
367            // (aka a fuzz bug) if we somehow forget to emit an error somewhere
368            // else.
369            fn insert_ref_func(&mut self, index: u32) {
370                if self.order == Order::Data {
371                    self.uninserted_funcref = true;
372                } else {
373                    self.resources
374                        .module
375                        .assert_mut()
376                        .function_references
377                        .insert(index);
378                }
379            }
380        }
381
382        macro_rules! define_visit_operator {
383            ($(@$proposal:ident $op:ident $({ $($arg:ident: $argty:ty),* })? => $visit:ident)*) => {
384                $(
385                    #[allow(unused_variables)]
386                    fn $visit(&mut self $($(,$arg: $argty)*)?) -> Self::Output {
387                        define_visit_operator!(@visit self $visit $($($arg)*)?)
388                    }
389                )*
390            };
391
392            // These are always valid in const expressions
393            (@visit $self:ident visit_i32_const $val:ident) => {{
394                $self.validator().visit_i32_const($val)
395            }};
396            (@visit $self:ident visit_i64_const $val:ident) => {{
397                $self.validator().visit_i64_const($val)
398            }};
399            (@visit $self:ident visit_f32_const $val:ident) => {{
400                $self.validator().visit_f32_const($val)
401            }};
402            (@visit $self:ident visit_f64_const $val:ident) => {{
403                $self.validator().visit_f64_const($val)
404            }};
405            (@visit $self:ident visit_v128_const $val:ident) => {{
406                $self.validator().visit_v128_const($val)
407            }};
408            (@visit $self:ident visit_ref_null $val:ident) => {{
409                $self.validator().visit_ref_null($val)
410            }};
411            (@visit $self:ident visit_end) => {{
412                $self.validator().visit_end()
413            }};
414
415
416            // These are valid const expressions when the extended-const proposal is enabled.
417            (@visit $self:ident visit_i32_add) => {{
418                $self.validate_extended_const()?;
419                $self.validator().visit_i32_add()
420            }};
421            (@visit $self:ident visit_i32_sub) => {{
422                $self.validate_extended_const()?;
423                $self.validator().visit_i32_sub()
424            }};
425            (@visit $self:ident visit_i32_mul) => {{
426                $self.validate_extended_const()?;
427                $self.validator().visit_i32_mul()
428            }};
429            (@visit $self:ident visit_i64_add) => {{
430                $self.validate_extended_const()?;
431                $self.validator().visit_i64_add()
432            }};
433            (@visit $self:ident visit_i64_sub) => {{
434                $self.validate_extended_const()?;
435                $self.validator().visit_i64_sub()
436            }};
437            (@visit $self:ident visit_i64_mul) => {{
438                $self.validate_extended_const()?;
439                $self.validator().visit_i64_mul()
440            }};
441
442            // `global.get` is a valid const expression for imported, immutable
443            // globals.
444            (@visit $self:ident visit_global_get $idx:ident) => {{
445                $self.validate_global($idx)?;
446                $self.validator().visit_global_get($idx)
447            }};
448            // `ref.func`, if it's in a `global` initializer, will insert into
449            // the set of referenced functions so it's processed here.
450            (@visit $self:ident visit_ref_func $idx:ident) => {{
451                $self.insert_ref_func($idx);
452                $self.validator().visit_ref_func($idx)
453            }};
454
455            (@visit $self:ident $op:ident $($args:tt)*) => {{
456                Err(BinaryReaderError::new(
457                    "constant expression required: non-constant operator",
458                    $self.offset,
459                ))
460            }}
461        }
462
463        impl<'a> VisitOperator<'a> for VisitConstOperator<'a> {
464            type Output = Result<()>;
465
466            for_each_operator!(define_visit_operator);
467        }
468    }
469}
470
471pub(crate) struct Module {
472    // This is set once the code section starts.
473    // `WasmModuleResources` implementations use the snapshot to
474    // enable parallel validation of functions.
475    pub snapshot: Option<Arc<TypeList>>,
476    // Stores indexes into the validator's types list.
477    pub types: Vec<TypeId>,
478    pub tables: Vec<TableType>,
479    pub memories: Vec<MemoryType>,
480    pub globals: Vec<GlobalType>,
481    pub element_types: Vec<RefType>,
482    pub data_count: Option<u32>,
483    // Stores indexes into `types`.
484    pub functions: Vec<u32>,
485    pub tags: Vec<TypeId>,
486    pub function_references: HashSet<u32>,
487    pub imports: IndexMap<(String, String), Vec<EntityType>>,
488    pub exports: IndexMap<String, EntityType>,
489    pub type_size: u32,
490    num_imported_globals: u32,
491    num_imported_functions: u32,
492}
493
494impl Module {
495    pub fn add_type(
496        &mut self,
497        ty: crate::Type,
498        features: &WasmFeatures,
499        types: &mut TypeAlloc,
500        offset: usize,
501        check_limit: bool,
502    ) -> Result<()> {
503        let ty = match ty {
504            crate::Type::Func(t) => {
505                for ty in t.params().iter().chain(t.results()) {
506                    self.check_value_type(*ty, features, types, offset)?;
507                }
508                if t.results().len() > 1 && !features.multi_value {
509                    return Err(BinaryReaderError::new(
510                        "func type returns multiple values but the multi-value feature is not enabled",
511                        offset,
512                    ));
513                }
514                Type::Func(t)
515            }
516        };
517
518        if check_limit {
519            check_max(self.types.len(), 1, MAX_WASM_TYPES, "types", offset)?;
520        }
521
522        let id = types.push_defined(ty);
523        self.types.push(id);
524        Ok(())
525    }
526
527    pub fn add_import(
528        &mut self,
529        import: crate::Import,
530        features: &WasmFeatures,
531        types: &TypeList,
532        offset: usize,
533    ) -> Result<()> {
534        let entity = self.check_type_ref(&import.ty, features, types, offset)?;
535
536        let (len, max, desc) = match import.ty {
537            TypeRef::Func(type_index) => {
538                self.functions.push(type_index);
539                self.num_imported_functions += 1;
540                (self.functions.len(), MAX_WASM_FUNCTIONS, "functions")
541            }
542            TypeRef::Table(ty) => {
543                self.tables.push(ty);
544                (self.tables.len(), self.max_tables(features), "tables")
545            }
546            TypeRef::Memory(ty) => {
547                self.memories.push(ty);
548                (self.memories.len(), self.max_memories(features), "memories")
549            }
550            TypeRef::Tag(ty) => {
551                self.tags.push(self.types[ty.func_type_idx as usize]);
552                (self.tags.len(), MAX_WASM_TAGS, "tags")
553            }
554            TypeRef::Global(ty) => {
555                if !features.mutable_global && ty.mutable {
556                    return Err(BinaryReaderError::new(
557                        "mutable global support is not enabled",
558                        offset,
559                    ));
560                }
561                self.globals.push(ty);
562                self.num_imported_globals += 1;
563                (self.globals.len(), MAX_WASM_GLOBALS, "globals")
564            }
565        };
566
567        check_max(len, 0, max, desc, offset)?;
568
569        self.type_size = combine_type_sizes(self.type_size, entity.type_size(), offset)?;
570
571        self.imports
572            .entry((import.module.to_string(), import.name.to_string()))
573            .or_default()
574            .push(entity);
575
576        Ok(())
577    }
578
579    pub fn add_export(
580        &mut self,
581        name: &str,
582        ty: EntityType,
583        features: &WasmFeatures,
584        offset: usize,
585        check_limit: bool,
586    ) -> Result<()> {
587        if !features.mutable_global {
588            if let EntityType::Global(global_type) = ty {
589                if global_type.mutable {
590                    return Err(BinaryReaderError::new(
591                        "mutable global support is not enabled",
592                        offset,
593                    ));
594                }
595            }
596        }
597
598        if check_limit {
599            check_max(self.exports.len(), 1, MAX_WASM_EXPORTS, "exports", offset)?;
600        }
601
602        self.type_size = combine_type_sizes(self.type_size, ty.type_size(), offset)?;
603
604        match self.exports.insert(name.to_string(), ty) {
605            Some(_) => Err(format_err!(
606                offset,
607                "duplicate export name `{name}` already defined"
608            )),
609            None => Ok(()),
610        }
611    }
612
613    pub fn add_function(&mut self, type_index: u32, types: &TypeList, offset: usize) -> Result<()> {
614        self.func_type_at(type_index, types, offset)?;
615        self.functions.push(type_index);
616        Ok(())
617    }
618
619    pub fn add_memory(
620        &mut self,
621        ty: MemoryType,
622        features: &WasmFeatures,
623        offset: usize,
624    ) -> Result<()> {
625        self.check_memory_type(&ty, features, offset)?;
626        self.memories.push(ty);
627        Ok(())
628    }
629
630    pub fn add_tag(
631        &mut self,
632        ty: TagType,
633        features: &WasmFeatures,
634        types: &TypeList,
635        offset: usize,
636    ) -> Result<()> {
637        self.check_tag_type(&ty, features, types, offset)?;
638        self.tags.push(self.types[ty.func_type_idx as usize]);
639        Ok(())
640    }
641
642    pub fn type_at(&self, idx: u32, offset: usize) -> Result<TypeId> {
643        self.types
644            .get(idx as usize)
645            .copied()
646            .ok_or_else(|| format_err!(offset, "unknown type {idx}: type index out of bounds"))
647    }
648
649    fn func_type_at<'a>(
650        &self,
651        type_index: u32,
652        types: &'a TypeList,
653        offset: usize,
654    ) -> Result<&'a FuncType> {
655        types[self.type_at(type_index, offset)?]
656            .as_func_type()
657            .ok_or_else(|| format_err!(offset, "type index {type_index} is not a function type"))
658    }
659
660    pub fn check_type_ref(
661        &self,
662        type_ref: &TypeRef,
663        features: &WasmFeatures,
664        types: &TypeList,
665        offset: usize,
666    ) -> Result<EntityType> {
667        Ok(match type_ref {
668            TypeRef::Func(type_index) => {
669                self.func_type_at(*type_index, types, offset)?;
670                EntityType::Func(self.types[*type_index as usize])
671            }
672            TypeRef::Table(t) => {
673                self.check_table_type(t, features, types, offset)?;
674                EntityType::Table(*t)
675            }
676            TypeRef::Memory(t) => {
677                self.check_memory_type(t, features, offset)?;
678                EntityType::Memory(*t)
679            }
680            TypeRef::Tag(t) => {
681                self.check_tag_type(t, features, types, offset)?;
682                EntityType::Tag(self.types[t.func_type_idx as usize])
683            }
684            TypeRef::Global(t) => {
685                self.check_global_type(t, features, types, offset)?;
686                EntityType::Global(*t)
687            }
688        })
689    }
690
691    fn check_table_type(
692        &self,
693        ty: &TableType,
694        features: &WasmFeatures,
695        types: &TypeList,
696        offset: usize,
697    ) -> Result<()> {
698        // the `funcref` value type is allowed all the way back to the MVP, so
699        // don't check it here
700        if ty.element_type != RefType::FUNCREF {
701            self.check_value_type(ValType::Ref(ty.element_type), features, types, offset)?
702        }
703
704        self.check_limits(ty.initial, ty.maximum, offset)?;
705        if ty.initial > MAX_WASM_TABLE_ENTRIES as u32 {
706            return Err(BinaryReaderError::new(
707                "minimum table size is out of bounds",
708                offset,
709            ));
710        }
711        Ok(())
712    }
713
714    fn check_memory_type(
715        &self,
716        ty: &MemoryType,
717        features: &WasmFeatures,
718        offset: usize,
719    ) -> Result<()> {
720        self.check_limits(ty.initial, ty.maximum, offset)?;
721        let (true_maximum, err) = if ty.memory64 {
722            if !features.memory64 {
723                return Err(BinaryReaderError::new(
724                    "memory64 must be enabled for 64-bit memories",
725                    offset,
726                ));
727            }
728            (
729                MAX_WASM_MEMORY64_PAGES,
730                "memory size must be at most 2**48 pages",
731            )
732        } else {
733            (
734                MAX_WASM_MEMORY32_PAGES,
735                "memory size must be at most 65536 pages (4GiB)",
736            )
737        };
738        if ty.initial > true_maximum {
739            return Err(BinaryReaderError::new(err, offset));
740        }
741        if let Some(maximum) = ty.maximum {
742            if maximum > true_maximum {
743                return Err(BinaryReaderError::new(err, offset));
744            }
745        }
746        if ty.shared {
747            if !features.threads {
748                return Err(BinaryReaderError::new(
749                    "threads must be enabled for shared memories",
750                    offset,
751                ));
752            }
753            if ty.maximum.is_none() {
754                return Err(BinaryReaderError::new(
755                    "shared memory must have maximum size",
756                    offset,
757                ));
758            }
759        }
760        Ok(())
761    }
762
763    pub(crate) fn imports_for_module_type(
764        &self,
765        offset: usize,
766    ) -> Result<IndexMap<(String, String), EntityType>> {
767        // Ensure imports are unique, which is a requirement of the component model
768        self.imports
769            .iter()
770            .map(|((module, name), types)| {
771                if types.len() != 1 {
772                    bail!(
773                        offset,
774                        "module has a duplicate import name `{module}:{name}` \
775                         that is not allowed in components",
776                    );
777                }
778                Ok(((module.clone(), name.clone()), types[0]))
779            })
780            .collect::<Result<_>>()
781    }
782
783    fn check_value_type(
784        &self,
785        ty: ValType,
786        features: &WasmFeatures,
787        types: &TypeList,
788        offset: usize,
789    ) -> Result<()> {
790        match features.check_value_type(ty) {
791            Ok(()) => Ok(()),
792            Err(e) => Err(BinaryReaderError::new(e, offset)),
793        }?;
794        // The above only checks the value type for features.
795        // We must check it if it's a reference.
796        match ty {
797            ValType::Ref(rt) => {
798                self.check_ref_type(rt, types, offset)?;
799            }
800            _ => (),
801        }
802        Ok(())
803    }
804
805    fn check_ref_type(&self, ty: RefType, types: &TypeList, offset: usize) -> Result<()> {
806        // Check that the heap type is valid
807        match ty.heap_type {
808            HeapType::Func | HeapType::Extern => (),
809            HeapType::TypedFunc(type_index) => {
810                // Just check that the index is valid
811                self.func_type_at(type_index.into(), types, offset)?;
812            }
813        }
814        Ok(())
815    }
816
817    fn eq_valtypes(&self, ty1: ValType, ty2: ValType, types: &TypeList) -> bool {
818        match (ty1, ty2) {
819            (ValType::Ref(rt1), ValType::Ref(rt2)) => {
820                rt1.nullable == rt2.nullable
821                    && match (rt1.heap_type, rt2.heap_type) {
822                        (HeapType::Func, HeapType::Func) => true,
823                        (HeapType::Extern, HeapType::Extern) => true,
824                        (HeapType::TypedFunc(n1), HeapType::TypedFunc(n2)) => {
825                            let n1 = self.func_type_at(n1.into(), types, 0).unwrap();
826                            let n2 = self.func_type_at(n2.into(), types, 0).unwrap();
827                            self.eq_fns(n1, n2, types)
828                        }
829                        (_, _) => false,
830                    }
831            }
832            _ => ty1 == ty2,
833        }
834    }
835    fn eq_fns(&self, f1: &impl WasmFuncType, f2: &impl WasmFuncType, types: &TypeList) -> bool {
836        f1.len_inputs() == f2.len_inputs()
837            && f2.len_outputs() == f2.len_outputs()
838            && f1
839                .inputs()
840                .zip(f2.inputs())
841                .all(|(t1, t2)| self.eq_valtypes(t1, t2, types))
842            && f1
843                .outputs()
844                .zip(f2.outputs())
845                .all(|(t1, t2)| self.eq_valtypes(t1, t2, types))
846    }
847
848    pub(crate) fn matches(&self, ty1: ValType, ty2: ValType, types: &TypeList) -> bool {
849        fn matches_null(null1: bool, null2: bool) -> bool {
850            (null1 == null2) || null2
851        }
852
853        let matches_heap = |ty1: HeapType, ty2: HeapType, types: &TypeList| -> bool {
854            match (ty1, ty2) {
855                (HeapType::TypedFunc(n1), HeapType::TypedFunc(n2)) => {
856                    // Check whether the defined types are (structurally) equivalent.
857                    let n1 = self.func_type_at(n1.into(), types, 0).unwrap();
858                    let n2 = self.func_type_at(n2.into(), types, 0).unwrap();
859                    self.eq_fns(n1, n2, types)
860                }
861                (HeapType::TypedFunc(_), HeapType::Func) => true,
862                (_, _) => ty1 == ty2,
863            }
864        };
865
866        let matches_ref = |ty1: RefType, ty2: RefType, types: &TypeList| -> bool {
867            matches_heap(ty1.heap_type, ty2.heap_type, types)
868                && matches_null(ty1.nullable, ty2.nullable)
869        };
870
871        match (ty1, ty2) {
872            (ValType::Ref(rt1), ValType::Ref(rt2)) => matches_ref(rt1, rt2, types),
873            (_, _) => ty1 == ty2,
874        }
875    }
876
877    fn check_tag_type(
878        &self,
879        ty: &TagType,
880        features: &WasmFeatures,
881        types: &TypeList,
882        offset: usize,
883    ) -> Result<()> {
884        if !features.exceptions {
885            return Err(BinaryReaderError::new(
886                "exceptions proposal not enabled",
887                offset,
888            ));
889        }
890        let ty = self.func_type_at(ty.func_type_idx, types, offset)?;
891        if !ty.results().is_empty() {
892            return Err(BinaryReaderError::new(
893                "invalid exception type: non-empty tag result type",
894                offset,
895            ));
896        }
897        Ok(())
898    }
899
900    fn check_global_type(
901        &self,
902        ty: &GlobalType,
903        features: &WasmFeatures,
904        types: &TypeList,
905        offset: usize,
906    ) -> Result<()> {
907        self.check_value_type(ty.content_type, features, types, offset)
908    }
909
910    fn check_limits<T>(&self, initial: T, maximum: Option<T>, offset: usize) -> Result<()>
911    where
912        T: Into<u64>,
913    {
914        if let Some(max) = maximum {
915            if initial.into() > max.into() {
916                return Err(BinaryReaderError::new(
917                    "size minimum must not be greater than maximum",
918                    offset,
919                ));
920            }
921        }
922        Ok(())
923    }
924
925    pub fn max_tables(&self, features: &WasmFeatures) -> usize {
926        if features.reference_types {
927            MAX_WASM_TABLES
928        } else {
929            1
930        }
931    }
932
933    pub fn max_memories(&self, features: &WasmFeatures) -> usize {
934        if features.multi_memory {
935            MAX_WASM_MEMORIES
936        } else {
937            1
938        }
939    }
940
941    pub fn export_to_entity_type(
942        &mut self,
943        export: &crate::Export,
944        offset: usize,
945    ) -> Result<EntityType> {
946        let check = |ty: &str, index: u32, total: usize| {
947            if index as usize >= total {
948                Err(format_err!(
949                    offset,
950                    "unknown {ty} {index}: exported {ty} index out of bounds",
951                ))
952            } else {
953                Ok(())
954            }
955        };
956
957        Ok(match export.kind {
958            ExternalKind::Func => {
959                check("function", export.index, self.functions.len())?;
960                self.function_references.insert(export.index);
961                EntityType::Func(self.types[self.functions[export.index as usize] as usize])
962            }
963            ExternalKind::Table => {
964                check("table", export.index, self.tables.len())?;
965                EntityType::Table(self.tables[export.index as usize])
966            }
967            ExternalKind::Memory => {
968                check("memory", export.index, self.memories.len())?;
969                EntityType::Memory(self.memories[export.index as usize])
970            }
971            ExternalKind::Global => {
972                check("global", export.index, self.globals.len())?;
973                EntityType::Global(self.globals[export.index as usize])
974            }
975            ExternalKind::Tag => {
976                check("tag", export.index, self.tags.len())?;
977                EntityType::Tag(self.tags[export.index as usize])
978            }
979        })
980    }
981
982    pub fn get_func_type<'a>(
983        &self,
984        func_idx: u32,
985        types: &'a TypeList,
986        offset: usize,
987    ) -> Result<&'a FuncType> {
988        match self.functions.get(func_idx as usize) {
989            Some(idx) => self.func_type_at(*idx, types, offset),
990            None => Err(format_err!(
991                offset,
992                "unknown function {func_idx}: func index out of bounds",
993            )),
994        }
995    }
996
997    fn global_at(&self, idx: u32, offset: usize) -> Result<&GlobalType> {
998        match self.globals.get(idx as usize) {
999            Some(t) => Ok(t),
1000            None => Err(format_err!(
1001                offset,
1002                "unknown global {idx}: global index out of bounds"
1003            )),
1004        }
1005    }
1006
1007    fn table_at(&self, idx: u32, offset: usize) -> Result<&TableType> {
1008        match self.tables.get(idx as usize) {
1009            Some(t) => Ok(t),
1010            None => Err(format_err!(
1011                offset,
1012                "unknown table {idx}: table index out of bounds"
1013            )),
1014        }
1015    }
1016
1017    fn memory_at(&self, idx: u32, offset: usize) -> Result<&MemoryType> {
1018        match self.memories.get(idx as usize) {
1019            Some(t) => Ok(t),
1020            None => Err(format_err!(
1021                offset,
1022                "unknown memory {idx}: memory index out of bounds"
1023            )),
1024        }
1025    }
1026}
1027
1028impl Default for Module {
1029    fn default() -> Self {
1030        Self {
1031            snapshot: Default::default(),
1032            types: Default::default(),
1033            tables: Default::default(),
1034            memories: Default::default(),
1035            globals: Default::default(),
1036            element_types: Default::default(),
1037            data_count: Default::default(),
1038            functions: Default::default(),
1039            tags: Default::default(),
1040            function_references: Default::default(),
1041            imports: Default::default(),
1042            exports: Default::default(),
1043            type_size: 1,
1044            num_imported_globals: Default::default(),
1045            num_imported_functions: Default::default(),
1046        }
1047    }
1048}
1049
1050struct OperatorValidatorResources<'a> {
1051    module: &'a mut MaybeOwned<Module>,
1052    types: &'a TypeList,
1053}
1054
1055impl WasmModuleResources for OperatorValidatorResources<'_> {
1056    type FuncType = crate::FuncType;
1057
1058    fn table_at(&self, at: u32) -> Option<TableType> {
1059        self.module.tables.get(at as usize).cloned()
1060    }
1061
1062    fn memory_at(&self, at: u32) -> Option<MemoryType> {
1063        self.module.memories.get(at as usize).cloned()
1064    }
1065
1066    fn tag_at(&self, at: u32) -> Option<&Self::FuncType> {
1067        Some(
1068            self.types[*self.module.tags.get(at as usize)?]
1069                .as_func_type()
1070                .unwrap(),
1071        )
1072    }
1073
1074    fn global_at(&self, at: u32) -> Option<GlobalType> {
1075        self.module.globals.get(at as usize).cloned()
1076    }
1077
1078    fn func_type_at(&self, at: u32) -> Option<&Self::FuncType> {
1079        Some(
1080            self.types[*self.module.types.get(at as usize)?]
1081                .as_func_type()
1082                .unwrap(),
1083        )
1084    }
1085
1086    fn type_index_of_function(&self, at: u32) -> Option<u32> {
1087        self.module.functions.get(at as usize).cloned()
1088    }
1089
1090    fn type_of_function(&self, at: u32) -> Option<&Self::FuncType> {
1091        self.func_type_at(self.type_index_of_function(at)?)
1092    }
1093
1094    fn check_value_type(&self, t: ValType, features: &WasmFeatures, offset: usize) -> Result<()> {
1095        self.module
1096            .check_value_type(t, features, self.types, offset)
1097    }
1098
1099    fn element_type_at(&self, at: u32) -> Option<RefType> {
1100        self.module.element_types.get(at as usize).cloned()
1101    }
1102
1103    fn matches(&self, t1: ValType, t2: ValType) -> bool {
1104        self.module.matches(t1, t2, self.types)
1105    }
1106
1107    fn element_count(&self) -> u32 {
1108        self.module.element_types.len() as u32
1109    }
1110
1111    fn data_count(&self) -> Option<u32> {
1112        self.module.data_count
1113    }
1114
1115    fn is_function_referenced(&self, idx: u32) -> bool {
1116        self.module.function_references.contains(&idx)
1117    }
1118}
1119
1120/// The implementation of [`WasmModuleResources`] used by
1121/// [`Validator`](crate::Validator).
1122pub struct ValidatorResources(pub(crate) Arc<Module>);
1123
1124impl WasmModuleResources for ValidatorResources {
1125    type FuncType = crate::FuncType;
1126
1127    fn table_at(&self, at: u32) -> Option<TableType> {
1128        self.0.tables.get(at as usize).cloned()
1129    }
1130
1131    fn memory_at(&self, at: u32) -> Option<MemoryType> {
1132        self.0.memories.get(at as usize).cloned()
1133    }
1134
1135    fn tag_at(&self, at: u32) -> Option<&Self::FuncType> {
1136        Some(
1137            self.0.snapshot.as_ref().unwrap()[*self.0.tags.get(at as usize)?]
1138                .as_func_type()
1139                .unwrap(),
1140        )
1141    }
1142
1143    fn global_at(&self, at: u32) -> Option<GlobalType> {
1144        self.0.globals.get(at as usize).cloned()
1145    }
1146
1147    fn func_type_at(&self, at: u32) -> Option<&Self::FuncType> {
1148        Some(
1149            self.0.snapshot.as_ref().unwrap()[*self.0.types.get(at as usize)?]
1150                .as_func_type()
1151                .unwrap(),
1152        )
1153    }
1154
1155    fn type_index_of_function(&self, at: u32) -> Option<u32> {
1156        self.0.functions.get(at as usize).cloned()
1157    }
1158
1159    fn type_of_function(&self, at: u32) -> Option<&Self::FuncType> {
1160        self.func_type_at(self.type_index_of_function(at)?)
1161    }
1162
1163    fn check_value_type(&self, t: ValType, features: &WasmFeatures, offset: usize) -> Result<()> {
1164        self.0
1165            .check_value_type(t, features, self.0.snapshot.as_ref().unwrap(), offset)
1166    }
1167
1168    fn element_type_at(&self, at: u32) -> Option<RefType> {
1169        self.0.element_types.get(at as usize).cloned()
1170    }
1171
1172    fn matches(&self, t1: ValType, t2: ValType) -> bool {
1173        self.0.matches(t1, t2, self.0.snapshot.as_ref().unwrap())
1174    }
1175
1176    fn element_count(&self) -> u32 {
1177        self.0.element_types.len() as u32
1178    }
1179
1180    fn data_count(&self) -> Option<u32> {
1181        self.0.data_count
1182    }
1183
1184    fn is_function_referenced(&self, idx: u32) -> bool {
1185        self.0.function_references.contains(&idx)
1186    }
1187}
1188
1189const _: () = {
1190    fn assert_send<T: Send>() {}
1191
1192    // Assert that `ValidatorResources` is Send so function validation
1193    // can be parallelizable
1194    fn assert() {
1195        assert_send::<ValidatorResources>();
1196    }
1197};
1198
1199mod arc {
1200    use std::ops::Deref;
1201    use std::sync::Arc;
1202
1203    enum Inner<T> {
1204        Owned(T),
1205        Shared(Arc<T>),
1206
1207        Empty, // Only used for swapping from owned to shared.
1208    }
1209
1210    pub struct MaybeOwned<T> {
1211        inner: Inner<T>,
1212    }
1213
1214    impl<T> MaybeOwned<T> {
1215        #[inline]
1216        fn as_mut(&mut self) -> Option<&mut T> {
1217            match &mut self.inner {
1218                Inner::Owned(x) => Some(x),
1219                Inner::Shared(_) => None,
1220                Inner::Empty => Self::unreachable(),
1221            }
1222        }
1223
1224        #[inline]
1225        pub fn assert_mut(&mut self) -> &mut T {
1226            self.as_mut().unwrap()
1227        }
1228
1229        pub fn arc(&mut self) -> &Arc<T> {
1230            self.make_shared();
1231            match &self.inner {
1232                Inner::Shared(x) => x,
1233                _ => Self::unreachable(),
1234            }
1235        }
1236
1237        #[inline]
1238        fn make_shared(&mut self) {
1239            if let Inner::Shared(_) = self.inner {
1240                return;
1241            }
1242
1243            let inner = std::mem::replace(&mut self.inner, Inner::Empty);
1244            let x = match inner {
1245                Inner::Owned(x) => x,
1246                _ => Self::unreachable(),
1247            };
1248            let x = Arc::new(x);
1249            self.inner = Inner::Shared(x);
1250        }
1251
1252        #[cold]
1253        #[inline(never)]
1254        fn unreachable() -> ! {
1255            unreachable!()
1256        }
1257    }
1258
1259    impl<T: Default> Default for MaybeOwned<T> {
1260        fn default() -> MaybeOwned<T> {
1261            MaybeOwned {
1262                inner: Inner::Owned(T::default()),
1263            }
1264        }
1265    }
1266
1267    impl<T> Deref for MaybeOwned<T> {
1268        type Target = T;
1269
1270        fn deref(&self) -> &T {
1271            match &self.inner {
1272                Inner::Owned(x) => x,
1273                Inner::Shared(x) => x,
1274                Inner::Empty => Self::unreachable(),
1275            }
1276        }
1277    }
1278}