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