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_array_new $type_index:ident) => {{
406                $self.validate_gc("array.new")?;
407                $self.validator().visit_array_new($type_index)
408            }};
409            (@visit $self:ident visit_array_new_default $type_index:ident) => {{
410                $self.validate_gc("array.new_default")?;
411                $self.validator().visit_array_new_default($type_index)
412            }};
413            (@visit $self:ident visit_array_new_fixed $type_index:ident $n:ident) => {{
414                $self.validate_gc("array.new_fixed")?;
415                $self.validator().visit_array_new_fixed($type_index, $n)
416            }};
417            (@visit $self:ident visit_ref_i31) => {{
418                $self.validate_gc("ref.i31")?;
419                $self.validator().visit_ref_i31()
420            }};
421            (@visit $self:ident visit_extern_convert_any) => {{
422                $self.validate_gc("extern.convert_any")?;
423                $self.validator().visit_extern_convert_any()
424            }};
425            (@visit $self:ident visit_any_convert_extern) => {{
426                $self.validate_gc("any.convert_extern")?;
427                $self.validator().visit_any_convert_extern()
428            }};
429            (@visit $self:ident visit_ref_i31_shared) => {{
430                $self.validate_shared_everything_threads("ref.i31_shared")?;
431                $self.validator().visit_ref_i31_shared()
432            }};
433
434            // `global.get` is a valid const expression for imported, immutable
435            // globals.
436            (@visit $self:ident visit_global_get $idx:ident) => {{
437                $self.validate_global($idx)?;
438                $self.validator().visit_global_get($idx)
439            }};
440            // `ref.func`, if it's in a `global` initializer, will insert into
441            // the set of referenced functions so it's processed here.
442            (@visit $self:ident visit_ref_func $idx:ident) => {{
443                $self.insert_ref_func($idx);
444                $self.validator().visit_ref_func($idx)
445            }};
446
447            (@visit $self:ident $op:ident $($args:tt)*) => {{
448                Err($self.not_const(stringify!($op)))
449            }}
450        }
451
452        impl<'a> VisitOperator<'a> for VisitConstOperator<'a> {
453            type Output = Result<()>;
454
455            #[cfg(feature = "simd")]
456            fn simd_visitor(
457                &mut self,
458            ) -> Option<&mut dyn crate::VisitSimdOperator<'a, Output = Self::Output>> {
459                Some(self)
460            }
461
462            crate::for_each_visit_operator!(define_visit_operator);
463        }
464
465        #[cfg(feature = "simd")]
466        impl<'a> VisitSimdOperator<'a> for VisitConstOperator<'a> {
467            crate::for_each_visit_simd_operator!(define_visit_operator);
468        }
469
470        impl<'a> FrameStack for VisitConstOperator<'a> {
471            fn current_frame(&self) -> Option<FrameKind> {
472                Some(self.ops.get_frame(0)?.kind)
473            }
474        }
475    }
476}
477
478#[derive(Debug)]
479pub(crate) struct Module {
480    // This is set once the code section starts.
481    // `WasmModuleResources` implementations use the snapshot to
482    // enable parallel validation of functions.
483    pub snapshot: Option<Arc<TypeList>>,
484    // Stores indexes into the validator's types list.
485    pub types: Vec<CoreTypeId>,
486    pub tables: Vec<TableType>,
487    pub memories: Vec<MemoryType>,
488    pub globals: Vec<GlobalType>,
489    pub element_types: Vec<RefType>,
490    pub data_count: Option<u32>,
491    // Stores indexes into `types`.
492    pub functions: Vec<u32>,
493    pub tags: Vec<CoreTypeId>,
494    pub function_references: Set<u32>,
495    pub imports: IndexMap<(String, String), Vec<EntityType>>,
496    pub exports: IndexMap<String, EntityType>,
497    pub type_size: u32,
498    num_imported_globals: u32,
499    num_imported_functions: u32,
500    features: WasmFeatures,
501}
502
503impl Module {
504    pub fn new(features: WasmFeatures) -> Self {
505        Self {
506            snapshot: Default::default(),
507            types: Default::default(),
508            tables: Default::default(),
509            memories: Default::default(),
510            globals: Default::default(),
511            element_types: Default::default(),
512            data_count: Default::default(),
513            functions: Default::default(),
514            tags: Default::default(),
515            function_references: Default::default(),
516            imports: Default::default(),
517            exports: Default::default(),
518            type_size: 1,
519            num_imported_globals: Default::default(),
520            num_imported_functions: Default::default(),
521            features,
522        }
523    }
524
525    pub(crate) fn add_types(
526        &mut self,
527        rec_group: RecGroup,
528        types: &mut TypeAlloc,
529        offset: usize,
530        check_limit: bool,
531    ) -> Result<()> {
532        if check_limit {
533            check_max(
534                self.types.len(),
535                rec_group.types().len() as u32,
536                MAX_WASM_TYPES,
537                "types",
538                offset,
539            )?;
540        }
541        self.canonicalize_and_intern_rec_group(types, rec_group, offset)
542    }
543
544    pub fn add_import(
545        &mut self,
546        mut import: crate::Import,
547        types: &TypeList,
548        offset: usize,
549    ) -> Result<()> {
550        let entity = self.check_type_ref(&mut import.ty, types, offset)?;
551
552        let (len, max, desc) = match import.ty {
553            TypeRef::Func(type_index) => {
554                self.functions.push(type_index);
555                self.num_imported_functions += 1;
556                (self.functions.len(), MAX_WASM_FUNCTIONS, "functions")
557            }
558            TypeRef::Table(ty) => {
559                self.tables.push(ty);
560                (self.tables.len(), self.max_tables(), "tables")
561            }
562            TypeRef::Memory(ty) => {
563                self.memories.push(ty);
564                (self.memories.len(), self.max_memories(), "memories")
565            }
566            TypeRef::Tag(ty) => {
567                self.tags.push(self.types[ty.func_type_idx as usize]);
568                (self.tags.len(), MAX_WASM_TAGS, "tags")
569            }
570            TypeRef::Global(ty) => {
571                if !self.features.mutable_global() && ty.mutable {
572                    return Err(BinaryReaderError::new(
573                        "mutable global support is not enabled",
574                        offset,
575                    ));
576                }
577                self.globals.push(ty);
578                self.num_imported_globals += 1;
579                (self.globals.len(), MAX_WASM_GLOBALS, "globals")
580            }
581        };
582
583        check_max(len, 0, max, desc, offset)?;
584
585        self.type_size = combine_type_sizes(self.type_size, entity.info(types).size(), offset)?;
586
587        self.imports
588            .entry((import.module.to_string(), import.name.to_string()))
589            .or_default()
590            .push(entity);
591
592        Ok(())
593    }
594
595    pub fn add_export(
596        &mut self,
597        name: &str,
598        ty: EntityType,
599        offset: usize,
600        check_limit: bool,
601        types: &TypeList,
602    ) -> Result<()> {
603        if !self.features.mutable_global() {
604            if let EntityType::Global(global_type) = ty {
605                if global_type.mutable {
606                    return Err(BinaryReaderError::new(
607                        "mutable global support is not enabled",
608                        offset,
609                    ));
610                }
611            }
612        }
613
614        if check_limit {
615            check_max(self.exports.len(), 1, MAX_WASM_EXPORTS, "exports", offset)?;
616        }
617
618        self.type_size = combine_type_sizes(self.type_size, ty.info(types).size(), offset)?;
619
620        match self.exports.insert(name.to_string(), ty) {
621            Some(_) => Err(format_err!(
622                offset,
623                "duplicate export name `{name}` already defined"
624            )),
625            None => Ok(()),
626        }
627    }
628
629    pub fn add_function(&mut self, type_index: u32, types: &TypeList, offset: usize) -> Result<()> {
630        self.func_type_at(type_index, types, offset)?;
631        self.functions.push(type_index);
632        Ok(())
633    }
634
635    pub fn add_memory(&mut self, ty: MemoryType, offset: usize) -> Result<()> {
636        self.check_memory_type(&ty, offset)?;
637        self.memories.push(ty);
638        Ok(())
639    }
640
641    pub fn add_tag(&mut self, ty: TagType, types: &TypeList, offset: usize) -> Result<()> {
642        self.check_tag_type(&ty, types, offset)?;
643        self.tags.push(self.types[ty.func_type_idx as usize]);
644        Ok(())
645    }
646
647    fn sub_type_at<'a>(&self, types: &'a TypeList, idx: u32, offset: usize) -> Result<&'a SubType> {
648        let id = self.type_id_at(idx, offset)?;
649        Ok(&types[id])
650    }
651
652    fn func_type_at<'a>(
653        &self,
654        type_index: u32,
655        types: &'a TypeList,
656        offset: usize,
657    ) -> Result<&'a FuncType> {
658        match &self
659            .sub_type_at(types, type_index, offset)?
660            .composite_type
661            .inner
662        {
663            CompositeInnerType::Func(f) => Ok(f),
664            _ => bail!(offset, "type index {type_index} is not a function type"),
665        }
666    }
667
668    pub fn check_type_ref(
669        &self,
670        type_ref: &mut TypeRef,
671        types: &TypeList,
672        offset: usize,
673    ) -> Result<EntityType> {
674        Ok(match type_ref {
675            TypeRef::Func(type_index) => {
676                self.func_type_at(*type_index, types, offset)?;
677                EntityType::Func(self.types[*type_index as usize])
678            }
679            TypeRef::Table(t) => {
680                self.check_table_type(t, types, offset)?;
681                EntityType::Table(*t)
682            }
683            TypeRef::Memory(t) => {
684                self.check_memory_type(t, offset)?;
685                EntityType::Memory(*t)
686            }
687            TypeRef::Tag(t) => {
688                self.check_tag_type(t, types, offset)?;
689                EntityType::Tag(self.types[t.func_type_idx as usize])
690            }
691            TypeRef::Global(t) => {
692                self.check_global_type(t, types, offset)?;
693                EntityType::Global(*t)
694            }
695        })
696    }
697
698    fn check_table_type(&self, ty: &mut TableType, types: &TypeList, offset: usize) -> Result<()> {
699        // The `funcref` value type is allowed all the way back to the MVP, so
700        // don't check it here.
701        if ty.element_type != RefType::FUNCREF {
702            self.check_ref_type(&mut ty.element_type, offset)?
703        }
704
705        self.check_limits(ty.initial, ty.maximum, offset)?;
706        if ty.table64 && !self.features().memory64() {
707            bail!(offset, "memory64 must be enabled for 64-bit tables");
708        }
709        if ty.shared && !self.features().shared_everything_threads() {
710            bail!(
711                offset,
712                "shared tables require the shared-everything-threads proposal"
713            );
714        }
715
716        let true_maximum = if ty.table64 {
717            u64::MAX
718        } else {
719            u32::MAX as u64
720        };
721        let err = format!("table size must be at most {true_maximum:#x} entries");
722        if ty.initial > true_maximum {
723            return Err(BinaryReaderError::new(err, offset));
724        }
725        if let Some(maximum) = ty.maximum {
726            if maximum > true_maximum {
727                return Err(BinaryReaderError::new(err, offset));
728            }
729        }
730        if ty.shared && !types.reftype_is_shared(ty.element_type) {
731            return Err(BinaryReaderError::new(
732                "shared tables must have a shared element type",
733                offset,
734            ));
735        }
736        Ok(())
737    }
738
739    fn check_memory_type(&self, ty: &MemoryType, offset: usize) -> Result<()> {
740        self.check_limits(ty.initial, ty.maximum, offset)?;
741
742        if ty.memory64 && !self.features().memory64() {
743            bail!(offset, "memory64 must be enabled for 64-bit memories");
744        }
745        if ty.shared && !self.features().threads() {
746            bail!(offset, "threads must be enabled for shared memories");
747        }
748
749        let page_size = if let Some(page_size_log2) = ty.page_size_log2 {
750            if !self.features().custom_page_sizes() {
751                return Err(BinaryReaderError::new(
752                    "the custom page sizes proposal must be enabled to customize a memory's page size",
753                    offset,
754                ));
755            }
756            // Currently 2**0 and 2**16 are the only valid page sizes, but this
757            // may be relaxed to allow any power of two in the future.
758            if page_size_log2 != 0 && page_size_log2 != 16 {
759                return Err(BinaryReaderError::new("invalid custom page size", offset));
760            }
761            let page_size = 1_u64 << page_size_log2;
762            debug_assert!(page_size.is_power_of_two());
763            debug_assert!(page_size == DEFAULT_WASM_PAGE_SIZE || page_size == 1);
764            page_size
765        } else {
766            let page_size_log2 = 16;
767            debug_assert_eq!(DEFAULT_WASM_PAGE_SIZE, 1 << page_size_log2);
768            DEFAULT_WASM_PAGE_SIZE
769        };
770        let true_maximum = if ty.memory64 {
771            max_wasm_memory64_pages(page_size)
772        } else {
773            max_wasm_memory32_pages(page_size)
774        };
775        let err = format!("memory size must be at most {true_maximum:#x} {page_size}-byte pages");
776        if ty.initial > true_maximum {
777            return Err(BinaryReaderError::new(err, offset));
778        }
779        if let Some(maximum) = ty.maximum {
780            if maximum > true_maximum {
781                return Err(BinaryReaderError::new(err, offset));
782            }
783        }
784        if ty.shared && ty.maximum.is_none() {
785            return Err(BinaryReaderError::new(
786                "shared memory must have maximum size",
787                offset,
788            ));
789        }
790        Ok(())
791    }
792
793    #[cfg(feature = "component-model")]
794    pub(crate) fn imports_for_module_type(
795        &self,
796        offset: usize,
797    ) -> Result<IndexMap<(String, String), EntityType>> {
798        // Ensure imports are unique, which is a requirement of the component model:
799        // https://github.com/WebAssembly/component-model/blob/d09f907/design/mvp/Explainer.md#import-and-export-definitions
800        self.imports
801            .iter()
802            .map(|((module, name), types)| {
803                if types.len() != 1 {
804                    bail!(
805                        offset,
806                        "module has a duplicate import name `{module}:{name}` \
807                         that is not allowed in components",
808                    );
809                }
810                Ok(((module.clone(), name.clone()), types[0]))
811            })
812            .collect::<Result<_>>()
813    }
814
815    fn check_value_type(&self, ty: &mut ValType, offset: usize) -> Result<()> {
816        // The above only checks the value type for features.
817        // We must check it if it's a reference.
818        match ty {
819            ValType::Ref(rt) => self.check_ref_type(rt, offset),
820            _ => self
821                .features
822                .check_value_type(*ty)
823                .map_err(|e| BinaryReaderError::new(e, offset)),
824        }
825    }
826
827    fn check_ref_type(&self, ty: &mut RefType, offset: usize) -> Result<()> {
828        self.features
829            .check_ref_type(*ty)
830            .map_err(|e| BinaryReaderError::new(e, offset))?;
831        let mut hty = ty.heap_type();
832        self.check_heap_type(&mut hty, offset)?;
833        *ty = RefType::new(ty.is_nullable(), hty).unwrap();
834        Ok(())
835    }
836
837    fn check_heap_type(&self, ty: &mut HeapType, offset: usize) -> Result<()> {
838        // Check that the heap type is valid.
839        let type_index = match ty {
840            HeapType::Abstract { .. } => return Ok(()),
841            HeapType::Concrete(type_index) => type_index,
842        };
843        match type_index {
844            UnpackedIndex::Module(idx) => {
845                let id = self.type_id_at(*idx, offset)?;
846                *type_index = UnpackedIndex::Id(id);
847                Ok(())
848            }
849            // Types at this stage should not be canonicalized. All
850            // canonicalized types should already be validated meaning they
851            // shouldn't be double-checked here again.
852            UnpackedIndex::RecGroup(_) | UnpackedIndex::Id(_) => unreachable!(),
853        }
854    }
855
856    fn check_tag_type(&self, ty: &TagType, types: &TypeList, offset: usize) -> Result<()> {
857        if !self.features().exceptions() {
858            bail!(offset, "exceptions proposal not enabled");
859        }
860        let ty = self.func_type_at(ty.func_type_idx, types, offset)?;
861        if !ty.results().is_empty() && !self.features.stack_switching() {
862            return Err(BinaryReaderError::new(
863                "invalid exception type: non-empty tag result type",
864                offset,
865            ));
866        }
867        Ok(())
868    }
869
870    fn check_global_type(
871        &self,
872        ty: &mut GlobalType,
873        types: &TypeList,
874        offset: usize,
875    ) -> Result<()> {
876        self.check_value_type(&mut ty.content_type, offset)?;
877        if ty.shared && !self.features.shared_everything_threads() {
878            bail!(
879                offset,
880                "shared globals require the shared-everything-threads proposal"
881            );
882        }
883        if ty.shared && !types.valtype_is_shared(ty.content_type) {
884            return Err(BinaryReaderError::new(
885                "shared globals must have a shared value type",
886                offset,
887            ));
888        }
889        Ok(())
890    }
891
892    fn check_limits<T>(&self, initial: T, maximum: Option<T>, offset: usize) -> Result<()>
893    where
894        T: Into<u64>,
895    {
896        if let Some(max) = maximum {
897            if initial.into() > max.into() {
898                return Err(BinaryReaderError::new(
899                    "size minimum must not be greater than maximum",
900                    offset,
901                ));
902            }
903        }
904        Ok(())
905    }
906
907    pub fn max_tables(&self) -> usize {
908        if self.features.reference_types() {
909            MAX_WASM_TABLES
910        } else {
911            1
912        }
913    }
914
915    pub fn max_memories(&self) -> usize {
916        if self.features.multi_memory() {
917            MAX_WASM_MEMORIES
918        } else {
919            1
920        }
921    }
922
923    pub fn export_to_entity_type(
924        &mut self,
925        export: &crate::Export,
926        offset: usize,
927    ) -> Result<EntityType> {
928        let check = |ty: &str, index: u32, total: usize| {
929            if index as usize >= total {
930                Err(format_err!(
931                    offset,
932                    "unknown {ty} {index}: exported {ty} index out of bounds",
933                ))
934            } else {
935                Ok(())
936            }
937        };
938
939        Ok(match export.kind {
940            ExternalKind::Func => {
941                check("function", export.index, self.functions.len())?;
942                self.function_references.insert(export.index);
943                EntityType::Func(self.types[self.functions[export.index as usize] as usize])
944            }
945            ExternalKind::Table => {
946                check("table", export.index, self.tables.len())?;
947                EntityType::Table(self.tables[export.index as usize])
948            }
949            ExternalKind::Memory => {
950                check("memory", export.index, self.memories.len())?;
951                EntityType::Memory(self.memories[export.index as usize])
952            }
953            ExternalKind::Global => {
954                check("global", export.index, self.globals.len())?;
955                EntityType::Global(self.globals[export.index as usize])
956            }
957            ExternalKind::Tag => {
958                check("tag", export.index, self.tags.len())?;
959                EntityType::Tag(self.tags[export.index as usize])
960            }
961        })
962    }
963
964    pub fn get_func_type<'a>(
965        &self,
966        func_idx: u32,
967        types: &'a TypeList,
968        offset: usize,
969    ) -> Result<&'a FuncType> {
970        match self.functions.get(func_idx as usize) {
971            Some(idx) => self.func_type_at(*idx, types, offset),
972            None => Err(format_err!(
973                offset,
974                "unknown function {func_idx}: func index out of bounds",
975            )),
976        }
977    }
978
979    fn global_at(&self, idx: u32, offset: usize) -> Result<&GlobalType> {
980        match self.globals.get(idx as usize) {
981            Some(t) => Ok(t),
982            None => Err(format_err!(
983                offset,
984                "unknown global {idx}: global index out of bounds"
985            )),
986        }
987    }
988
989    fn table_at(&self, idx: u32, offset: usize) -> Result<&TableType> {
990        match self.tables.get(idx as usize) {
991            Some(t) => Ok(t),
992            None => Err(format_err!(
993                offset,
994                "unknown table {idx}: table index out of bounds"
995            )),
996        }
997    }
998
999    fn memory_at(&self, idx: u32, offset: usize) -> Result<&MemoryType> {
1000        match self.memories.get(idx as usize) {
1001            Some(t) => Ok(t),
1002            None => Err(format_err!(
1003                offset,
1004                "unknown memory {idx}: memory index out of bounds"
1005            )),
1006        }
1007    }
1008}
1009
1010impl InternRecGroup for Module {
1011    fn features(&self) -> &WasmFeatures {
1012        &self.features
1013    }
1014
1015    fn add_type_id(&mut self, id: CoreTypeId) {
1016        self.types.push(id);
1017    }
1018
1019    fn type_id_at(&self, idx: u32, offset: usize) -> Result<CoreTypeId> {
1020        self.types
1021            .get(idx as usize)
1022            .copied()
1023            .ok_or_else(|| format_err!(offset, "unknown type {idx}: type index out of bounds"))
1024    }
1025
1026    fn types_len(&self) -> u32 {
1027        u32::try_from(self.types.len()).unwrap()
1028    }
1029}
1030
1031struct OperatorValidatorResources<'a> {
1032    module: &'a mut MaybeOwned<Module>,
1033    types: &'a TypeList,
1034}
1035
1036impl WasmModuleResources for OperatorValidatorResources<'_> {
1037    fn table_at(&self, at: u32) -> Option<TableType> {
1038        self.module.tables.get(at as usize).cloned()
1039    }
1040
1041    fn memory_at(&self, at: u32) -> Option<MemoryType> {
1042        self.module.memories.get(at as usize).cloned()
1043    }
1044
1045    fn tag_at(&self, at: u32) -> Option<&FuncType> {
1046        let type_id = *self.module.tags.get(at as usize)?;
1047        Some(self.types[type_id].unwrap_func())
1048    }
1049
1050    fn global_at(&self, at: u32) -> Option<GlobalType> {
1051        self.module.globals.get(at as usize).cloned()
1052    }
1053
1054    fn sub_type_at(&self, at: u32) -> Option<&SubType> {
1055        let id = *self.module.types.get(at as usize)?;
1056        Some(&self.types[id])
1057    }
1058
1059    fn sub_type_at_id(&self, at: CoreTypeId) -> &SubType {
1060        &self.types[at]
1061    }
1062
1063    fn type_id_of_function(&self, at: u32) -> Option<CoreTypeId> {
1064        let type_index = self.module.functions.get(at as usize)?;
1065        self.module.types.get(*type_index as usize).copied()
1066    }
1067
1068    fn type_index_of_function(&self, at: u32) -> Option<u32> {
1069        self.module.functions.get(at as usize).copied()
1070    }
1071
1072    fn check_heap_type(&self, t: &mut HeapType, offset: usize) -> Result<()> {
1073        self.module.check_heap_type(t, offset)
1074    }
1075
1076    fn top_type(&self, heap_type: &HeapType) -> HeapType {
1077        self.types.top_type(heap_type)
1078    }
1079
1080    fn element_type_at(&self, at: u32) -> Option<RefType> {
1081        self.module.element_types.get(at as usize).cloned()
1082    }
1083
1084    fn is_subtype(&self, a: ValType, b: ValType) -> bool {
1085        self.types.valtype_is_subtype(a, b)
1086    }
1087
1088    fn is_shared(&self, ty: RefType) -> bool {
1089        self.types.reftype_is_shared(ty)
1090    }
1091
1092    fn element_count(&self) -> u32 {
1093        self.module.element_types.len() as u32
1094    }
1095
1096    fn data_count(&self) -> Option<u32> {
1097        self.module.data_count
1098    }
1099
1100    fn is_function_referenced(&self, idx: u32) -> bool {
1101        self.module.function_references.contains(&idx)
1102    }
1103}
1104
1105/// The implementation of [`WasmModuleResources`] used by
1106/// [`Validator`](crate::Validator).
1107#[derive(Debug)]
1108pub struct ValidatorResources(pub(crate) Arc<Module>);
1109
1110impl WasmModuleResources for ValidatorResources {
1111    fn table_at(&self, at: u32) -> Option<TableType> {
1112        self.0.tables.get(at as usize).cloned()
1113    }
1114
1115    fn memory_at(&self, at: u32) -> Option<MemoryType> {
1116        self.0.memories.get(at as usize).cloned()
1117    }
1118
1119    fn tag_at(&self, at: u32) -> Option<&FuncType> {
1120        let id = *self.0.tags.get(at as usize)?;
1121        let types = self.0.snapshot.as_ref().unwrap();
1122        match &types[id].composite_type.inner {
1123            CompositeInnerType::Func(f) => Some(f),
1124            _ => None,
1125        }
1126    }
1127
1128    fn global_at(&self, at: u32) -> Option<GlobalType> {
1129        self.0.globals.get(at as usize).cloned()
1130    }
1131
1132    fn sub_type_at(&self, at: u32) -> Option<&SubType> {
1133        let id = *self.0.types.get(at as usize)?;
1134        let types = self.0.snapshot.as_ref().unwrap();
1135        Some(&types[id])
1136    }
1137
1138    fn sub_type_at_id(&self, at: CoreTypeId) -> &SubType {
1139        let types = self.0.snapshot.as_ref().unwrap();
1140        &types[at]
1141    }
1142
1143    fn type_id_of_function(&self, at: u32) -> Option<CoreTypeId> {
1144        let type_index = *self.0.functions.get(at as usize)?;
1145        self.0.types.get(type_index as usize).copied()
1146    }
1147
1148    fn type_index_of_function(&self, at: u32) -> Option<u32> {
1149        self.0.functions.get(at as usize).copied()
1150    }
1151
1152    fn check_heap_type(&self, t: &mut HeapType, offset: usize) -> Result<()> {
1153        self.0.check_heap_type(t, offset)
1154    }
1155
1156    fn top_type(&self, heap_type: &HeapType) -> HeapType {
1157        self.0.snapshot.as_ref().unwrap().top_type(heap_type)
1158    }
1159
1160    fn element_type_at(&self, at: u32) -> Option<RefType> {
1161        self.0.element_types.get(at as usize).cloned()
1162    }
1163
1164    fn is_subtype(&self, a: ValType, b: ValType) -> bool {
1165        self.0.snapshot.as_ref().unwrap().valtype_is_subtype(a, b)
1166    }
1167
1168    fn is_shared(&self, ty: RefType) -> bool {
1169        self.0.snapshot.as_ref().unwrap().reftype_is_shared(ty)
1170    }
1171
1172    fn element_count(&self) -> u32 {
1173        self.0.element_types.len() as u32
1174    }
1175
1176    fn data_count(&self) -> Option<u32> {
1177        self.0.data_count
1178    }
1179
1180    fn is_function_referenced(&self, idx: u32) -> bool {
1181        self.0.function_references.contains(&idx)
1182    }
1183}
1184
1185const _: () = {
1186    const fn assert_send<T: Send>() {}
1187
1188    // Assert that `ValidatorResources` is Send so function validation
1189    // can be parallelizable
1190    assert_send::<ValidatorResources>();
1191};
1192
1193mod arc {
1194    use alloc::sync::Arc;
1195    use core::ops::Deref;
1196
1197    enum Inner<T> {
1198        Owned(T),
1199        Shared(Arc<T>),
1200
1201        Empty, // Only used for swapping from owned to shared.
1202    }
1203
1204    pub struct MaybeOwned<T> {
1205        inner: Inner<T>,
1206    }
1207
1208    impl<T> MaybeOwned<T> {
1209        pub fn new(val: T) -> MaybeOwned<T> {
1210            MaybeOwned {
1211                inner: Inner::Owned(val),
1212            }
1213        }
1214
1215        #[inline]
1216        pub(crate) fn as_mut(&mut self) -> Option<&mut T> {
1217            match &mut self.inner {
1218                Inner::Owned(x) => Some(x),
1219                Inner::Shared(_) => None,
1220                Inner::Empty => Self::unreachable(),
1221            }
1222        }
1223
1224        #[inline]
1225        pub fn assert_mut(&mut self) -> &mut T {
1226            self.as_mut().unwrap()
1227        }
1228
1229        pub fn arc(&mut self) -> &Arc<T> {
1230            self.make_shared();
1231            match &self.inner {
1232                Inner::Shared(x) => x,
1233                _ => Self::unreachable(),
1234            }
1235        }
1236
1237        #[inline]
1238        fn make_shared(&mut self) {
1239            if let Inner::Shared(_) = self.inner {
1240                return;
1241            }
1242
1243            let inner = core::mem::replace(&mut self.inner, Inner::Empty);
1244            let x = match inner {
1245                Inner::Owned(x) => x,
1246                _ => Self::unreachable(),
1247            };
1248            let x = Arc::new(x);
1249            self.inner = Inner::Shared(x);
1250        }
1251
1252        #[cold]
1253        #[inline(never)]
1254        fn unreachable() -> ! {
1255            unreachable!()
1256        }
1257    }
1258
1259    impl<T: Default> Default for MaybeOwned<T> {
1260        fn default() -> MaybeOwned<T> {
1261            MaybeOwned::new(T::default())
1262        }
1263    }
1264
1265    impl<T> Deref for MaybeOwned<T> {
1266        type Target = T;
1267
1268        fn deref(&self) -> &T {
1269            match &self.inner {
1270                Inner::Owned(x) => x,
1271                Inner::Shared(x) => x,
1272                Inner::Empty => Self::unreachable(),
1273            }
1274        }
1275    }
1276}