tinywasm_wasmparser/validator/
core.rs

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