inf_wasmparser/validator/
core.rs

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