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