wasmparser_nostd/validator/
core.rs

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