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