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