1//! State relating to validating a WebAssembly module.
2//!
3
4mod canonical;
5pub(crate) use canonical::InternRecGroup;
6
7use self::arc::MaybeOwned;
8use super::{
9 check_max, combine_type_sizes,
10 operators::{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// Section order for WebAssembly modules.
26//
27// Component sections are unordered and allow for duplicates,
28// so this isn't used for components.
29#[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 /// Internal state that is incrementally built-up for the module being
51 /// validated. This houses type information for all wasm items, like
52 /// functions. Note that this starts out as a solely owned `Arc<T>` so we can
53 /// get mutable access, but after we get to the code section this is never
54 /// mutated to we can clone it cheaply and hand it to sub-validators.
55 pub module: arc::MaybeOwned<Module>,
56
57 /// Where we are, order-wise, in the wasm binary.
58 order: Order,
59
60 /// The number of data segments in the data section (if present).
61 pub data_segment_count: u32,
62
63 /// The number of functions we expect to be defined in the code section, or
64 /// basically the length of the function section if it was found. The next
65 /// index is where we are, in the code section index space, for the next
66 /// entry in the code section (used to figure out what type is next for the
67 /// function being validated).
68 pub expected_code_bodies: Option<u32>,
69
70 const_expr_allocs: OperatorValidatorAllocations,
71
72 /// When parsing the code section, represents the current index in the section.
73 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 // Ensure that the data count section, if any, was correct.
89 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 // Ensure that the function section, if nonzero, was paired with a code
98 // section with the appropriate length.
99 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 // the `funcref` value type is allowed all the way back to the MVP, so
201 // don't check it here
202 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 // See comment in `RefFunc` below for why this is an assert.
301 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 // Functions in initialization expressions are only valid in
383 // element segment initialization expressions and globals. In
384 // these contexts we want to record all function references.
385 //
386 // Initialization expressions can also be found in the data
387 // section, however. A `RefFunc` instruction in those situations
388 // is always invalid and needs to produce a validation error. In
389 // this situation, though, we can no longer modify
390 // the state since it's been "snapshot" already for
391 // parallel validation of functions.
392 //
393 // If we cannot modify the function references then this function
394 // *should* result in a validation error, but we defer that
395 // validation error to happen later. The `uninserted_funcref`
396 // boolean here is used to track this and will cause a panic
397 // (aka a fuzz bug) if we somehow forget to emit an error somewhere
398 // else.
399 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 // These are always valid in const expressions
430 (@visit $self:ident visit_i32_const $val:ident) => {{
431 $self.validator().visit_i32_const($val)
432 }};
433 (@visit $self:ident visit_i64_const $val:ident) => {{
434 $self.validator().visit_i64_const($val)
435 }};
436 (@visit $self:ident visit_f32_const $val:ident) => {{
437 $self.validator().visit_f32_const($val)
438 }};
439 (@visit $self:ident visit_f64_const $val:ident) => {{
440 $self.validator().visit_f64_const($val)
441 }};
442 (@visit $self:ident visit_v128_const $val:ident) => {{
443 $self.validator().simd_visitor().unwrap().visit_v128_const($val)
444 }};
445 (@visit $self:ident visit_ref_null $val:ident) => {{
446 $self.validator().visit_ref_null($val)
447 }};
448 (@visit $self:ident visit_end) => {{
449 $self.validator().visit_end()
450 }};
451
452
453 // These are valid const expressions when the extended-const proposal is enabled.
454 (@visit $self:ident visit_i32_add) => {{
455 $self.validate_extended_const("i32.add")?;
456 $self.validator().visit_i32_add()
457 }};
458 (@visit $self:ident visit_i32_sub) => {{
459 $self.validate_extended_const("i32.sub")?;
460 $self.validator().visit_i32_sub()
461 }};
462 (@visit $self:ident visit_i32_mul) => {{
463 $self.validate_extended_const("i32.mul")?;
464 $self.validator().visit_i32_mul()
465 }};
466 (@visit $self:ident visit_i64_add) => {{
467 $self.validate_extended_const("i64.add")?;
468 $self.validator().visit_i64_add()
469 }};
470 (@visit $self:ident visit_i64_sub) => {{
471 $self.validate_extended_const("i64.sub")?;
472 $self.validator().visit_i64_sub()
473 }};
474 (@visit $self:ident visit_i64_mul) => {{
475 $self.validate_extended_const("i64.mul")?;
476 $self.validator().visit_i64_mul()
477 }};
478
479 // These are valid const expressions with the gc proposal is
480 // enabled.
481 (@visit $self:ident visit_struct_new $type_index:ident) => {{
482 $self.validate_gc("struct.new")?;
483 $self.validator().visit_struct_new($type_index)
484 }};
485 (@visit $self:ident visit_struct_new_default $type_index:ident) => {{
486 $self.validate_gc("struct.new_default")?;
487 $self.validator().visit_struct_new_default($type_index)
488 }};
489 (@visit $self:ident visit_array_new $type_index:ident) => {{
490 $self.validate_gc("array.new")?;
491 $self.validator().visit_array_new($type_index)
492 }};
493 (@visit $self:ident visit_array_new_default $type_index:ident) => {{
494 $self.validate_gc("array.new_default")?;
495 $self.validator().visit_array_new_default($type_index)
496 }};
497 (@visit $self:ident visit_array_new_fixed $type_index:ident $n:ident) => {{
498 $self.validate_gc("array.new_fixed")?;
499 $self.validator().visit_array_new_fixed($type_index, $n)
500 }};
501 (@visit $self:ident visit_ref_i31) => {{
502 $self.validate_gc("ref.i31")?;
503 $self.validator().visit_ref_i31()
504 }};
505 (@visit $self:ident visit_ref_i31_shared) => {{
506 $self.validate_shared_everything_threads("ref.i31_shared")?;
507 $self.validator().visit_ref_i31_shared()
508 }};
509
510 // `global.get` is a valid const expression for imported, immutable
511 // globals.
512 (@visit $self:ident visit_global_get $idx:ident) => {{
513 $self.validate_global($idx)?;
514 $self.validator().visit_global_get($idx)
515 }};
516 // `ref.func`, if it's in a `global` initializer, will insert into
517 // the set of referenced functions so it's processed here.
518 (@visit $self:ident visit_ref_func $idx:ident) => {{
519 $self.insert_ref_func($idx);
520 $self.validator().visit_ref_func($idx)
521 }};
522
523 (@visit $self:ident $op:ident $($args:tt)*) => {{
524 Err($self.not_const(stringify!($op)))
525 }}
526 }
527
528 impl<'a> VisitOperator<'a> for VisitConstOperator<'a> {
529 type Output = Result<()>;
530
531 #[cfg(feature = "simd")]
532 fn simd_visitor(
533 &mut self,
534 ) -> Option<&mut dyn crate::VisitSimdOperator<'a, Output = Self::Output>> {
535 Some(self)
536 }
537
538 crate::for_each_visit_operator!(define_visit_operator);
539 }
540
541 #[cfg(feature = "simd")]
542 impl<'a> VisitSimdOperator<'a> for VisitConstOperator<'a> {
543 crate::for_each_visit_simd_operator!(define_visit_operator);
544 }
545 }
546}
547
548#[derive(Debug)]
549pub(crate) struct Module {
550 // This is set once the code section starts.
551 // `WasmModuleResources` implementations use the snapshot to
552 // enable parallel validation of functions.
553 pub snapshot: Option<Arc<TypeList>>,
554 // Stores indexes into the validator's types list.
555 pub types: Vec<CoreTypeId>,
556 pub tables: Vec<TableType>,
557 pub memories: Vec<MemoryType>,
558 pub globals: Vec<GlobalType>,
559 pub element_types: Vec<RefType>,
560 pub data_count: Option<u32>,
561 // Stores indexes into `types`.
562 pub functions: Vec<u32>,
563 pub tags: Vec<CoreTypeId>,
564 pub function_references: Set<u32>,
565 pub imports: IndexMap<(String, String), Vec<EntityType>>,
566 pub exports: IndexMap<String, EntityType>,
567 pub type_size: u32,
568 num_imported_globals: u32,
569 num_imported_functions: u32,
570}
571
572impl Module {
573 pub(crate) fn add_types(
574 &mut self,
575 rec_group: RecGroup,
576 features: &WasmFeatures,
577 types: &mut TypeAlloc,
578 offset: usize,
579 check_limit: bool,
580 ) -> Result<()> {
581 if check_limit {
582 check_max(
583 self.types.len(),
584 rec_group.types().len() as u32,
585 MAX_WASM_TYPES,
586 "types",
587 offset,
588 )?;
589 }
590 self.canonicalize_and_intern_rec_group(features, types, rec_group, offset)
591 }
592
593 pub fn add_import(
594 &mut self,
595 mut import: crate::Import,
596 features: &WasmFeatures,
597 types: &TypeList,
598 offset: usize,
599 ) -> Result<()> {
600 let entity = self.check_type_ref(&mut import.ty, features, types, offset)?;
601
602 let (len, max, desc) = match import.ty {
603 TypeRef::Func(type_index) => {
604 self.functions.push(type_index);
605 self.num_imported_functions += 1;
606 (self.functions.len(), MAX_WASM_FUNCTIONS, "functions")
607 }
608 TypeRef::Table(ty) => {
609 self.tables.push(ty);
610 (self.tables.len(), self.max_tables(features), "tables")
611 }
612 TypeRef::Memory(ty) => {
613 self.memories.push(ty);
614 (self.memories.len(), self.max_memories(features), "memories")
615 }
616 TypeRef::Tag(ty) => {
617 self.tags.push(self.types[ty.func_type_idx as usize]);
618 (self.tags.len(), MAX_WASM_TAGS, "tags")
619 }
620 TypeRef::Global(ty) => {
621 if !features.mutable_global() && ty.mutable {
622 return Err(BinaryReaderError::new(
623 "mutable global support is not enabled",
624 offset,
625 ));
626 }
627 self.globals.push(ty);
628 self.num_imported_globals += 1;
629 (self.globals.len(), MAX_WASM_GLOBALS, "globals")
630 }
631 };
632
633 check_max(len, 0, max, desc, offset)?;
634
635 self.type_size = combine_type_sizes(self.type_size, entity.info(types).size(), offset)?;
636
637 self.imports
638 .entry((import.module.to_string(), import.name.to_string()))
639 .or_default()
640 .push(entity);
641
642 Ok(())
643 }
644
645 pub fn add_export(
646 &mut self,
647 name: &str,
648 ty: EntityType,
649 features: &WasmFeatures,
650 offset: usize,
651 check_limit: bool,
652 types: &TypeList,
653 ) -> Result<()> {
654 if !features.mutable_global() {
655 if let EntityType::Global(global_type) = ty {
656 if global_type.mutable {
657 return Err(BinaryReaderError::new(
658 "mutable global support is not enabled",
659 offset,
660 ));
661 }
662 }
663 }
664
665 if check_limit {
666 check_max(self.exports.len(), 1, MAX_WASM_EXPORTS, "exports", offset)?;
667 }
668
669 self.type_size = combine_type_sizes(self.type_size, ty.info(types).size(), offset)?;
670
671 match self.exports.insert(name.to_string(), ty) {
672 Some(_) => Err(format_err!(
673 offset,
674 "duplicate export name `{name}` already defined"
675 )),
676 None => Ok(()),
677 }
678 }
679
680 pub fn add_function(&mut self, type_index: u32, types: &TypeList, offset: usize) -> Result<()> {
681 self.func_type_at(type_index, types, offset)?;
682 self.functions.push(type_index);
683 Ok(())
684 }
685
686 pub fn add_memory(
687 &mut self,
688 ty: MemoryType,
689 features: &WasmFeatures,
690 offset: usize,
691 ) -> Result<()> {
692 self.check_memory_type(&ty, features, offset)?;
693 self.memories.push(ty);
694 Ok(())
695 }
696
697 pub fn add_tag(
698 &mut self,
699 ty: TagType,
700 features: &WasmFeatures,
701 types: &TypeList,
702 offset: usize,
703 ) -> Result<()> {
704 self.check_tag_type(&ty, features, types, offset)?;
705 self.tags.push(self.types[ty.func_type_idx as usize]);
706 Ok(())
707 }
708
709 fn sub_type_at<'a>(&self, types: &'a TypeList, idx: u32, offset: usize) -> Result<&'a SubType> {
710 let id = self.type_id_at(idx, offset)?;
711 Ok(&types[id])
712 }
713
714 fn func_type_at<'a>(
715 &self,
716 type_index: u32,
717 types: &'a TypeList,
718 offset: usize,
719 ) -> Result<&'a FuncType> {
720 match &self
721 .sub_type_at(types, type_index, offset)?
722 .composite_type
723 .inner
724 {
725 CompositeInnerType::Func(f) => Ok(f),
726 _ => bail!(offset, "type index {type_index} is not a function type"),
727 }
728 }
729
730 pub fn check_type_ref(
731 &self,
732 type_ref: &mut TypeRef,
733 features: &WasmFeatures,
734 types: &TypeList,
735 offset: usize,
736 ) -> Result<EntityType> {
737 Ok(match type_ref {
738 TypeRef::Func(type_index) => {
739 self.func_type_at(*type_index, types, offset)?;
740 EntityType::Func(self.types[*type_index as usize])
741 }
742 TypeRef::Table(t) => {
743 self.check_table_type(t, features, types, offset)?;
744 EntityType::Table(*t)
745 }
746 TypeRef::Memory(t) => {
747 self.check_memory_type(t, features, offset)?;
748 EntityType::Memory(*t)
749 }
750 TypeRef::Tag(t) => {
751 self.check_tag_type(t, features, types, offset)?;
752 EntityType::Tag(self.types[t.func_type_idx as usize])
753 }
754 TypeRef::Global(t) => {
755 self.check_global_type(t, features, types, offset)?;
756 EntityType::Global(*t)
757 }
758 })
759 }
760
761 fn check_table_type(
762 &self,
763 ty: &mut TableType,
764 features: &WasmFeatures,
765 types: &TypeList,
766 offset: usize,
767 ) -> Result<()> {
768 // The `funcref` value type is allowed all the way back to the MVP, so
769 // don't check it here.
770 if ty.element_type != RefType::FUNCREF {
771 self.check_ref_type(&mut ty.element_type, features, offset)?
772 }
773
774 if ty.table64 && !features.memory64() {
775 return Err(BinaryReaderError::new(
776 "memory64 must be enabled for 64-bit tables",
777 offset,
778 ));
779 }
780
781 self.check_limits(ty.initial, ty.maximum, offset)?;
782
783 if ty.shared {
784 if !features.shared_everything_threads() {
785 return Err(BinaryReaderError::new(
786 "shared tables require the shared-everything-threads proposal",
787 offset,
788 ));
789 }
790
791 if !types.reftype_is_shared(ty.element_type) {
792 return Err(BinaryReaderError::new(
793 "shared tables must have a shared element type",
794 offset,
795 ));
796 }
797 }
798
799 Ok(())
800 }
801
802 fn check_memory_type(
803 &self,
804 ty: &MemoryType,
805 features: &WasmFeatures,
806 offset: usize,
807 ) -> Result<()> {
808 self.check_limits(ty.initial, ty.maximum, offset)?;
809 let (page_size, page_size_log2) = if let Some(page_size_log2) = ty.page_size_log2 {
810 if !features.custom_page_sizes() {
811 return Err(BinaryReaderError::new(
812 "the custom page sizes proposal must be enabled to \
813 customize a memory's page size",
814 offset,
815 ));
816 }
817 // Currently 2**0 and 2**16 are the only valid page sizes, but this
818 // may be relaxed to allow any power of two in the future.
819 if page_size_log2 != 0 && page_size_log2 != 16 {
820 return Err(BinaryReaderError::new("invalid custom page size", offset));
821 }
822 let page_size = 1_u64 << page_size_log2;
823 debug_assert!(page_size.is_power_of_two());
824 debug_assert!(page_size == DEFAULT_WASM_PAGE_SIZE || page_size == 1);
825 (page_size, page_size_log2)
826 } else {
827 let page_size_log2 = 16;
828 debug_assert_eq!(DEFAULT_WASM_PAGE_SIZE, 1 << page_size_log2);
829 (DEFAULT_WASM_PAGE_SIZE, page_size_log2)
830 };
831 let (true_maximum, err) = if ty.memory64 {
832 if !features.memory64() {
833 return Err(BinaryReaderError::new(
834 "memory64 must be enabled for 64-bit memories",
835 offset,
836 ));
837 }
838 (
839 max_wasm_memory64_pages(page_size),
840 format!(
841 "memory size must be at most 2**{} pages",
842 64 - page_size_log2
843 ),
844 )
845 } else {
846 let max = max_wasm_memory32_pages(page_size);
847 (
848 max,
849 format!("memory size must be at most {max} pages (4GiB)"),
850 )
851 };
852 if ty.initial > true_maximum {
853 return Err(BinaryReaderError::new(err, offset));
854 }
855 if let Some(maximum) = ty.maximum {
856 if maximum > true_maximum {
857 return Err(BinaryReaderError::new(err, offset));
858 }
859 }
860 if ty.shared {
861 if !features.threads() {
862 return Err(BinaryReaderError::new(
863 "threads must be enabled for shared memories",
864 offset,
865 ));
866 }
867 if ty.maximum.is_none() {
868 return Err(BinaryReaderError::new(
869 "shared memory must have maximum size",
870 offset,
871 ));
872 }
873 }
874 Ok(())
875 }
876
877 #[cfg(feature = "component-model")]
878 pub(crate) fn imports_for_module_type(
879 &self,
880 offset: usize,
881 ) -> Result<IndexMap<(String, String), EntityType>> {
882 // Ensure imports are unique, which is a requirement of the component model
883 self.imports
884 .iter()
885 .map(|((module, name), types)| {
886 if types.len() != 1 {
887 bail!(
888 offset,
889 "module has a duplicate import name `{module}:{name}` \
890 that is not allowed in components",
891 );
892 }
893 Ok(((module.clone(), name.clone()), types[0]))
894 })
895 .collect::<Result<_>>()
896 }
897
898 fn check_value_type(
899 &self,
900 ty: &mut ValType,
901 features: &WasmFeatures,
902 offset: usize,
903 ) -> Result<()> {
904 // The above only checks the value type for features.
905 // We must check it if it's a reference.
906 match ty {
907 ValType::Ref(rt) => self.check_ref_type(rt, features, offset),
908 _ => features
909 .check_value_type(*ty)
910 .map_err(|e| BinaryReaderError::new(e, offset)),
911 }
912 }
913
914 fn check_ref_type(
915 &self,
916 ty: &mut RefType,
917 features: &WasmFeatures,
918 offset: usize,
919 ) -> Result<()> {
920 features
921 .check_ref_type(*ty)
922 .map_err(|e| BinaryReaderError::new(e, offset))?;
923 let mut hty = ty.heap_type();
924 self.check_heap_type(&mut hty, offset)?;
925 *ty = RefType::new(ty.is_nullable(), hty).unwrap();
926 Ok(())
927 }
928
929 fn check_heap_type(&self, ty: &mut HeapType, offset: usize) -> Result<()> {
930 // Check that the heap type is valid.
931 let type_index = match ty {
932 HeapType::Abstract { .. } => return Ok(()),
933 HeapType::Concrete(type_index) => type_index,
934 };
935 match type_index {
936 UnpackedIndex::Module(idx) => {
937 let id = self.type_id_at(*idx, offset)?;
938 *type_index = UnpackedIndex::Id(id);
939 Ok(())
940 }
941 // Types at this stage should not be canonicalized. All
942 // canonicalized types should already be validated meaning they
943 // shouldn't be double-checked here again.
944 UnpackedIndex::RecGroup(_) | UnpackedIndex::Id(_) => unreachable!(),
945 }
946 }
947
948 fn check_tag_type(
949 &self,
950 ty: &TagType,
951 features: &WasmFeatures,
952 types: &TypeList,
953 offset: usize,
954 ) -> Result<()> {
955 if !features.exceptions() {
956 return Err(BinaryReaderError::new(
957 "exceptions proposal not enabled",
958 offset,
959 ));
960 }
961 let ty = self.func_type_at(ty.func_type_idx, types, offset)?;
962 if !ty.results().is_empty() && !features.stack_switching() {
963 return Err(BinaryReaderError::new(
964 "invalid exception type: non-empty tag result type",
965 offset,
966 ));
967 }
968 Ok(())
969 }
970
971 fn check_global_type(
972 &self,
973 ty: &mut GlobalType,
974 features: &WasmFeatures,
975 types: &TypeList,
976 offset: usize,
977 ) -> Result<()> {
978 self.check_value_type(&mut ty.content_type, features, offset)?;
979 if ty.shared {
980 if !features.shared_everything_threads() {
981 return Err(BinaryReaderError::new(
982 "shared globals require the shared-everything-threads proposal",
983 offset,
984 ));
985 }
986 if !types.valtype_is_shared(ty.content_type) {
987 return Err(BinaryReaderError::new(
988 "shared globals must have a shared value type",
989 offset,
990 ));
991 }
992 }
993 Ok(())
994 }
995
996 fn check_limits<T>(&self, initial: T, maximum: Option<T>, offset: usize) -> Result<()>
997 where
998 T: Into<u64>,
999 {
1000 if let Some(max) = maximum {
1001 if initial.into() > max.into() {
1002 return Err(BinaryReaderError::new(
1003 "size minimum must not be greater than maximum",
1004 offset,
1005 ));
1006 }
1007 }
1008 Ok(())
1009 }
1010
1011 pub fn max_tables(&self, features: &WasmFeatures) -> usize {
1012 if features.reference_types() {
1013 MAX_WASM_TABLES
1014 } else {
1015 1
1016 }
1017 }
1018
1019 pub fn max_memories(&self, features: &WasmFeatures) -> usize {
1020 if features.multi_memory() {
1021 MAX_WASM_MEMORIES
1022 } else {
1023 1
1024 }
1025 }
1026
1027 pub fn export_to_entity_type(
1028 &mut self,
1029 export: &crate::Export,
1030 offset: usize,
1031 ) -> Result<EntityType> {
1032 let check = |ty: &str, index: u32, total: usize| {
1033 if index as usize >= total {
1034 Err(format_err!(
1035 offset,
1036 "unknown {ty} {index}: exported {ty} index out of bounds",
1037 ))
1038 } else {
1039 Ok(())
1040 }
1041 };
1042
1043 Ok(match export.kind {
1044 ExternalKind::Func => {
1045 check("function", export.index, self.functions.len())?;
1046 self.function_references.insert(export.index);
1047 EntityType::Func(self.types[self.functions[export.index as usize] as usize])
1048 }
1049 ExternalKind::Table => {
1050 check("table", export.index, self.tables.len())?;
1051 EntityType::Table(self.tables[export.index as usize])
1052 }
1053 ExternalKind::Memory => {
1054 check("memory", export.index, self.memories.len())?;
1055 EntityType::Memory(self.memories[export.index as usize])
1056 }
1057 ExternalKind::Global => {
1058 check("global", export.index, self.globals.len())?;
1059 EntityType::Global(self.globals[export.index as usize])
1060 }
1061 ExternalKind::Tag => {
1062 check("tag", export.index, self.tags.len())?;
1063 EntityType::Tag(self.tags[export.index as usize])
1064 }
1065 })
1066 }
1067
1068 pub fn get_func_type<'a>(
1069 &self,
1070 func_idx: u32,
1071 types: &'a TypeList,
1072 offset: usize,
1073 ) -> Result<&'a FuncType> {
1074 match self.functions.get(func_idx as usize) {
1075 Some(idx) => self.func_type_at(*idx, types, offset),
1076 None => Err(format_err!(
1077 offset,
1078 "unknown function {func_idx}: func index out of bounds",
1079 )),
1080 }
1081 }
1082
1083 fn global_at(&self, idx: u32, offset: usize) -> Result<&GlobalType> {
1084 match self.globals.get(idx as usize) {
1085 Some(t) => Ok(t),
1086 None => Err(format_err!(
1087 offset,
1088 "unknown global {idx}: global index out of bounds"
1089 )),
1090 }
1091 }
1092
1093 fn table_at(&self, idx: u32, offset: usize) -> Result<&TableType> {
1094 match self.tables.get(idx as usize) {
1095 Some(t) => Ok(t),
1096 None => Err(format_err!(
1097 offset,
1098 "unknown table {idx}: table index out of bounds"
1099 )),
1100 }
1101 }
1102
1103 fn memory_at(&self, idx: u32, offset: usize) -> Result<&MemoryType> {
1104 match self.memories.get(idx as usize) {
1105 Some(t) => Ok(t),
1106 None => Err(format_err!(
1107 offset,
1108 "unknown memory {idx}: memory index out of bounds"
1109 )),
1110 }
1111 }
1112}
1113
1114impl InternRecGroup for Module {
1115 fn add_type_id(&mut self, id: CoreTypeId) {
1116 self.types.push(id);
1117 }
1118
1119 fn type_id_at(&self, idx: u32, offset: usize) -> Result<CoreTypeId> {
1120 self.types
1121 .get(idx as usize)
1122 .copied()
1123 .ok_or_else(|| format_err!(offset, "unknown type {idx}: type index out of bounds"))
1124 }
1125
1126 fn types_len(&self) -> u32 {
1127 u32::try_from(self.types.len()).unwrap()
1128 }
1129}
1130
1131impl Default for Module {
1132 fn default() -> Self {
1133 Self {
1134 snapshot: Default::default(),
1135 types: Default::default(),
1136 tables: Default::default(),
1137 memories: Default::default(),
1138 globals: Default::default(),
1139 element_types: Default::default(),
1140 data_count: Default::default(),
1141 functions: Default::default(),
1142 tags: Default::default(),
1143 function_references: Default::default(),
1144 imports: Default::default(),
1145 exports: Default::default(),
1146 type_size: 1,
1147 num_imported_globals: Default::default(),
1148 num_imported_functions: Default::default(),
1149 }
1150 }
1151}
1152
1153struct OperatorValidatorResources<'a> {
1154 module: &'a mut MaybeOwned<Module>,
1155 types: &'a TypeList,
1156}
1157
1158impl WasmModuleResources for OperatorValidatorResources<'_> {
1159 fn table_at(&self, at: u32) -> Option<TableType> {
1160 self.module.tables.get(at as usize).cloned()
1161 }
1162
1163 fn memory_at(&self, at: u32) -> Option<MemoryType> {
1164 self.module.memories.get(at as usize).cloned()
1165 }
1166
1167 fn tag_at(&self, at: u32) -> Option<&FuncType> {
1168 let type_id = *self.module.tags.get(at as usize)?;
1169 Some(self.types[type_id].unwrap_func())
1170 }
1171
1172 fn global_at(&self, at: u32) -> Option<GlobalType> {
1173 self.module.globals.get(at as usize).cloned()
1174 }
1175
1176 fn sub_type_at(&self, at: u32) -> Option<&SubType> {
1177 let id = *self.module.types.get(at as usize)?;
1178 Some(&self.types[id])
1179 }
1180
1181 fn sub_type_at_id(&self, at: CoreTypeId) -> &SubType {
1182 &self.types[at]
1183 }
1184
1185 fn type_id_of_function(&self, at: u32) -> Option<CoreTypeId> {
1186 let type_index = self.module.functions.get(at as usize)?;
1187 self.module.types.get(*type_index as usize).copied()
1188 }
1189
1190 fn type_index_of_function(&self, at: u32) -> Option<u32> {
1191 self.module.functions.get(at as usize).copied()
1192 }
1193
1194 fn check_heap_type(&self, t: &mut HeapType, offset: usize) -> Result<()> {
1195 self.module.check_heap_type(t, offset)
1196 }
1197
1198 fn top_type(&self, heap_type: &HeapType) -> HeapType {
1199 self.types.top_type(heap_type)
1200 }
1201
1202 fn element_type_at(&self, at: u32) -> Option<RefType> {
1203 self.module.element_types.get(at as usize).cloned()
1204 }
1205
1206 fn is_subtype(&self, a: ValType, b: ValType) -> bool {
1207 self.types.valtype_is_subtype(a, b)
1208 }
1209
1210 fn is_shared(&self, ty: RefType) -> bool {
1211 self.types.reftype_is_shared(ty)
1212 }
1213
1214 fn element_count(&self) -> u32 {
1215 self.module.element_types.len() as u32
1216 }
1217
1218 fn data_count(&self) -> Option<u32> {
1219 self.module.data_count
1220 }
1221
1222 fn is_function_referenced(&self, idx: u32) -> bool {
1223 self.module.function_references.contains(&idx)
1224 }
1225}
1226
1227/// The implementation of [`WasmModuleResources`] used by
1228/// [`Validator`](crate::Validator).
1229#[derive(Debug)]
1230pub struct ValidatorResources(pub(crate) Arc<Module>);
1231
1232impl WasmModuleResources for ValidatorResources {
1233 fn table_at(&self, at: u32) -> Option<TableType> {
1234 self.0.tables.get(at as usize).cloned()
1235 }
1236
1237 fn memory_at(&self, at: u32) -> Option<MemoryType> {
1238 self.0.memories.get(at as usize).cloned()
1239 }
1240
1241 fn tag_at(&self, at: u32) -> Option<&FuncType> {
1242 let id = *self.0.tags.get(at as usize)?;
1243 let types = self.0.snapshot.as_ref().unwrap();
1244 match &types[id].composite_type.inner {
1245 CompositeInnerType::Func(f) => Some(f),
1246 _ => None,
1247 }
1248 }
1249
1250 fn global_at(&self, at: u32) -> Option<GlobalType> {
1251 self.0.globals.get(at as usize).cloned()
1252 }
1253
1254 fn sub_type_at(&self, at: u32) -> Option<&SubType> {
1255 let id = *self.0.types.get(at as usize)?;
1256 let types = self.0.snapshot.as_ref().unwrap();
1257 Some(&types[id])
1258 }
1259
1260 fn sub_type_at_id(&self, at: CoreTypeId) -> &SubType {
1261 let types = self.0.snapshot.as_ref().unwrap();
1262 &types[at]
1263 }
1264
1265 fn type_id_of_function(&self, at: u32) -> Option<CoreTypeId> {
1266 let type_index = *self.0.functions.get(at as usize)?;
1267 self.0.types.get(type_index as usize).copied()
1268 }
1269
1270 fn type_index_of_function(&self, at: u32) -> Option<u32> {
1271 self.0.functions.get(at as usize).copied()
1272 }
1273
1274 fn check_heap_type(&self, t: &mut HeapType, offset: usize) -> Result<()> {
1275 self.0.check_heap_type(t, offset)
1276 }
1277
1278 fn top_type(&self, heap_type: &HeapType) -> HeapType {
1279 self.0.snapshot.as_ref().unwrap().top_type(heap_type)
1280 }
1281
1282 fn element_type_at(&self, at: u32) -> Option<RefType> {
1283 self.0.element_types.get(at as usize).cloned()
1284 }
1285
1286 fn is_subtype(&self, a: ValType, b: ValType) -> bool {
1287 self.0.snapshot.as_ref().unwrap().valtype_is_subtype(a, b)
1288 }
1289
1290 fn is_shared(&self, ty: RefType) -> bool {
1291 self.0.snapshot.as_ref().unwrap().reftype_is_shared(ty)
1292 }
1293
1294 fn element_count(&self) -> u32 {
1295 self.0.element_types.len() as u32
1296 }
1297
1298 fn data_count(&self) -> Option<u32> {
1299 self.0.data_count
1300 }
1301
1302 fn is_function_referenced(&self, idx: u32) -> bool {
1303 self.0.function_references.contains(&idx)
1304 }
1305}
1306
1307const _: () = {
1308 fn assert_send<T: Send>() {}
1309
1310 // Assert that `ValidatorResources` is Send so function validation
1311 // can be parallelizable
1312 fn assert() {
1313 assert_send::<ValidatorResources>();
1314 }
1315};
1316
1317mod arc {
1318 use alloc::sync::Arc;
1319 use core::ops::Deref;
1320
1321 enum Inner<T> {
1322 Owned(T),
1323 Shared(Arc<T>),
1324
1325 Empty, // Only used for swapping from owned to shared.
1326 }
1327
1328 pub struct MaybeOwned<T> {
1329 inner: Inner<T>,
1330 }
1331
1332 impl<T> MaybeOwned<T> {
1333 #[inline]
1334 fn as_mut(&mut self) -> Option<&mut T> {
1335 match &mut self.inner {
1336 Inner::Owned(x) => Some(x),
1337 Inner::Shared(_) => None,
1338 Inner::Empty => Self::unreachable(),
1339 }
1340 }
1341
1342 #[inline]
1343 pub fn assert_mut(&mut self) -> &mut T {
1344 self.as_mut().unwrap()
1345 }
1346
1347 pub fn arc(&mut self) -> &Arc<T> {
1348 self.make_shared();
1349 match &self.inner {
1350 Inner::Shared(x) => x,
1351 _ => Self::unreachable(),
1352 }
1353 }
1354
1355 #[inline]
1356 fn make_shared(&mut self) {
1357 if let Inner::Shared(_) = self.inner {
1358 return;
1359 }
1360
1361 let inner = core::mem::replace(&mut self.inner, Inner::Empty);
1362 let x = match inner {
1363 Inner::Owned(x) => x,
1364 _ => Self::unreachable(),
1365 };
1366 let x = Arc::new(x);
1367 self.inner = Inner::Shared(x);
1368 }
1369
1370 #[cold]
1371 #[inline(never)]
1372 fn unreachable() -> ! {
1373 unreachable!()
1374 }
1375 }
1376
1377 impl<T: Default> Default for MaybeOwned<T> {
1378 fn default() -> MaybeOwned<T> {
1379 MaybeOwned {
1380 inner: Inner::Owned(T::default()),
1381 }
1382 }
1383 }
1384
1385 impl<T> Deref for MaybeOwned<T> {
1386 type Target = T;
1387
1388 fn deref(&self) -> &T {
1389 match &self.inner {
1390 Inner::Owned(x) => x,
1391 Inner::Shared(x) => x,
1392 Inner::Empty => Self::unreachable(),
1393 }
1394 }
1395 }
1396}
1397