1use crate::{
13 constants::*,
14 parse::{
15 parse_custom, parse_sec_with_default, CodeSkeletonSection, InstructionValidationContext,
16 OpCodeIterator, ParseResult, Skeleton, EMPTY_CTX,
17 },
18 types::*,
19};
20use anyhow::{anyhow, bail, ensure};
21use std::{borrow::Borrow, collections::BTreeSet, convert::TryInto, rc::Rc};
22
23#[derive(Debug)]
24pub enum ValidationError {
25 TooManyLocals {
26 actual: u32,
27 max: u32,
28 },
29}
30
31impl std::fmt::Display for ValidationError {
32 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
33 match self {
34 ValidationError::TooManyLocals {
35 actual,
36 max,
37 } => write!(f, "The number of locals ({}) is more than allowed ({}).", actual, max),
38 }
39 }
40}
41
42pub type ValidateResult<A> = anyhow::Result<A>;
44
45#[derive(Debug, Default)]
46pub(crate) struct OperandStack {
52 pub(crate) stack: Vec<MaybeKnown>,
53}
54
55#[derive(Debug, Default)]
56pub(crate) struct ControlStack {
61 pub(crate) stack: Vec<ControlFrame>,
62}
63
64impl ControlStack {
65 pub fn get(&self, n: u32) -> Option<&ControlFrame> {
67 let n = n as usize;
68 if n >= self.stack.len() {
69 None
70 } else {
71 self.stack.get(self.stack.len() - n - 1)
72 }
73 }
74
75 pub fn get_label(&self, n: u32) -> Option<BlockType> {
78 self.get(n).map(|frame| frame.label_type)
79 }
80
81 pub fn outermost(&self) -> Option<&ControlFrame> { self.stack.first() }
83}
84
85#[derive(Debug)]
86pub(crate) struct ControlFrame {
90 pub(crate) is_if: bool,
92 pub(crate) label_type: BlockType,
95 pub(crate) end_type: BlockType,
98 pub(crate) height: usize,
100 pub(crate) unreachable: bool,
104}
105
106#[derive(Debug)]
107pub struct ValidationState {
111 pub(crate) opds: OperandStack,
112 pub(crate) ctrls: ControlStack,
113 pub(crate) max_reachable_height: usize,
115 pub(crate) unreachable_section: Option<usize>,
118}
119
120impl ValidationState {
121 pub fn reachability(&self) -> Reachability {
125 let Some(idx) = self.unreachable_section else {
126 return Reachability::Reachable
127 };
128 if idx + 1 < self.ctrls.stack.len() {
129 Reachability::UnreachableFrame
130 } else {
131 Reachability::UnreachableInstruction
132 }
133 }
134
135 pub fn done(&self) -> bool { self.ctrls.stack.is_empty() }
138}
139
140#[derive(Eq, PartialEq, Debug, Clone, Copy)]
141pub(crate) enum MaybeKnown {
145 Unknown,
146 Known(ValueType),
147}
148
149use MaybeKnown::*;
150
151impl MaybeKnown {
152 pub(crate) fn is_unknown(self) -> bool { self == MaybeKnown::Unknown }
153}
154
155impl ValidationState {
156 fn push_opd(&mut self, m_type: MaybeKnown) {
158 self.opds.stack.push(m_type);
159 if matches!(
160 self.ctrls.stack.last(),
161 Some(ControlFrame {
162 unreachable: false,
163 ..
164 })
165 ) {
166 self.max_reachable_height =
167 std::cmp::max(self.max_reachable_height, self.opds.stack.len());
168 }
169 }
170
171 fn pop_opd(&mut self) -> ValidateResult<MaybeKnown> {
173 match self.ctrls.stack.last() {
174 None => bail!("Control frame exhausted."),
175 Some(frame) => {
176 if self.opds.stack.len() == frame.height {
177 if frame.unreachable {
178 Ok(Unknown)
179 } else {
180 bail!("Operand stack exhausted for the current block.")
181 }
182 } else {
183 self.opds
184 .stack
185 .pop()
186 .ok_or_else(|| anyhow!("Stack exhausted, should not happen."))
187 }
188 }
189 }
190 }
191
192 fn pop_expect_opd(&mut self, expect: MaybeKnown) -> ValidateResult<MaybeKnown> {
198 let actual = self.pop_opd()?;
199 if actual.is_unknown() {
200 return Ok(expect);
201 }
202 if expect.is_unknown() {
203 return Ok(actual);
204 }
205 ensure!(
206 actual == expect,
207 "Actual type different from expected {:#?} /= {:#?}.",
208 actual,
209 expect
210 );
211 Ok(actual)
212 }
213
214 fn push_opds(&mut self, tys: BlockType) {
216 if let BlockType::ValueType(ty) = tys {
217 self.push_opd(Known(ty))
218 }
219 }
220
221 fn pop_opds(&mut self, expected: BlockType) -> ValidateResult<()> {
224 if let BlockType::ValueType(ty) = expected {
225 self.pop_expect_opd(Known(ty))?;
226 }
227 Ok(())
228 }
229
230 fn push_ctrl(&mut self, is_if: bool, label_type: BlockType, end_type: BlockType) {
240 let frame = ControlFrame {
241 is_if,
242 label_type,
243 end_type,
244 height: self.opds.stack.len(),
245 unreachable: false,
246 };
247 self.ctrls.stack.push(frame)
248 }
249
250 fn pop_ctrl(&mut self) -> ValidateResult<(BlockType, bool)> {
253 match self.ctrls.stack.last().map(|frame| (frame.end_type, frame.height, frame.is_if)) {
257 None => bail!("Control stack exhausted."),
258 Some((end_type, height, opcode)) => {
259 if let BlockType::ValueType(ty) = end_type {
260 self.pop_expect_opd(Known(ty))?;
261 }
262 ensure!(self.opds.stack.len() == height, "Operand stack not exhausted.");
263 self.ctrls.stack.pop();
265 if let Some(idx) = self.unreachable_section {
268 if idx == self.ctrls.stack.len() {
269 self.unreachable_section = None;
270 } }
272 Ok((end_type, opcode))
273 }
274 }
275 }
276
277 fn mark_unreachable(&mut self) -> ValidateResult<()> {
278 match self.ctrls.stack.last_mut() {
279 None => bail!("Control stack exhausted."),
280 Some(frame) => {
281 self.opds.stack.truncate(frame.height);
282 frame.unreachable = true;
283 let last_idx = self.ctrls.stack.len() - 1;
284 if let Some(idx) = self.unreachable_section {
285 self.unreachable_section = Some(std::cmp::min(idx, last_idx));
286 } else {
288 self.unreachable_section = Some(last_idx);
289 }
290 Ok(())
291 }
292 }
293 }
294}
295
296pub(crate) struct LocalsRange {
298 pub(crate) start: LocalIndex,
299 pub(crate) end: LocalIndex,
300 pub(crate) ty: ValueType,
301}
302
303pub(crate) struct FunctionContext<'a> {
305 pub(crate) return_type: BlockType,
306 pub(crate) globals: &'a [Global],
307 pub(crate) funcs: &'a [TypeIndex],
308 pub(crate) types: &'a [Rc<FunctionType>],
309 pub(crate) locals: Vec<LocalsRange>,
310 pub(crate) memory: bool,
312 pub(crate) table: bool,
314}
315
316fn make_locals(ty: &FunctionType, locals: &[Local]) -> ValidateResult<(Vec<LocalsRange>, u32)> {
321 let mut out = Vec::with_capacity(ty.parameters.len() + locals.len());
322 let mut start = 0;
323 for &ty in ty.parameters.iter() {
324 let end = start + 1;
325 out.push(LocalsRange {
326 start,
327 end,
328 ty,
329 });
330 start = end;
331 }
332 for local in locals.iter() {
333 let end =
334 start.checked_add(local.multiplicity).ok_or_else(|| anyhow!("Too many locals"))?;
335 out.push(LocalsRange {
336 start,
337 end,
338 ty: local.ty,
339 });
340 start = end;
341 }
342 let num_locals = start;
343 ensure!(num_locals <= ALLOWED_LOCALS, ValidationError::TooManyLocals {
344 actual: num_locals,
345 max: ALLOWED_LOCALS,
346 });
347 Ok((out, num_locals))
348}
349
350pub trait HasValidationContext {
355 fn get_local(&self, idx: LocalIndex) -> ValidateResult<ValueType>;
358
359 fn get_global(&self, idx: GlobalIndex) -> ValidateResult<(ValueType, bool)>;
362
363 fn memory_exists(&self) -> bool;
365
366 fn table_exists(&self) -> bool;
368
369 fn get_func(&self, idx: FuncIndex) -> ValidateResult<&Rc<FunctionType>>;
371
372 fn get_type(&self, idx: TypeIndex) -> ValidateResult<&Rc<FunctionType>>;
374
375 fn return_type(&self) -> BlockType;
377}
378
379impl<'a> HasValidationContext for FunctionContext<'a> {
380 fn get_local(&self, idx: LocalIndex) -> ValidateResult<ValueType> {
381 let res = self.locals.binary_search_by(|locals| {
382 if locals.end <= idx {
383 std::cmp::Ordering::Less
384 } else if idx < locals.start {
385 std::cmp::Ordering::Greater
386 } else {
387 std::cmp::Ordering::Equal
388 }
389 });
390 match res {
391 Ok(idx) => Ok(self.locals[idx].ty),
392 Err(_) => bail!("Local index out of range."),
393 }
394 }
395
396 fn get_global(&self, idx: GlobalIndex) -> ValidateResult<(ValueType, bool)> {
398 if let Some(global) = self.globals.get(idx as usize) {
399 Ok((global.init.ty(), global.mutable))
400 } else {
401 bail!("Global index out of range.")
402 }
403 }
404
405 fn memory_exists(&self) -> bool { self.memory }
406
407 fn table_exists(&self) -> bool { self.table }
408
409 fn get_func(&self, idx: FuncIndex) -> ValidateResult<&Rc<FunctionType>> {
410 if let Some(&type_idx) = self.funcs.get(idx as usize) {
411 self.get_type(type_idx)
412 } else {
413 bail!("Function index out of range.")
414 }
415 }
416
417 fn get_type(&self, idx: TypeIndex) -> ValidateResult<&Rc<FunctionType>> {
418 self.types.get(idx as usize).ok_or_else(|| anyhow!("Type index out of range."))
419 }
420
421 fn return_type(&self) -> BlockType { self.return_type }
422}
423
424enum Type {
426 I8,
427 I16,
428 I32,
429 I64,
430}
431
432fn ensure_alignment(num: u32, align: Type) -> ValidateResult<()> {
434 match align {
435 Type::I8 => {
436 ensure!(num == 0, "Type I8 alignment must be less than 0, but is {}.", num);
437 }
438 Type::I16 => {
439 ensure!(num <= 1, "Type I16 alignment must be less than 1, but is {}.", num);
440 }
441 Type::I32 => {
442 ensure!(num <= 2, "Type I32 alignment must be less than 2, but is {}", num);
443 }
444 Type::I64 => {
445 ensure!(num <= 3, "Type I64 alignment must be less than 3, but is {}.", num);
446 }
447 }
448 Ok(())
449}
450
451#[derive(Debug, Clone, Copy)]
454pub enum Reachability {
455 Reachable,
457 UnreachableInstruction,
459 UnreachableFrame,
461}
462
463pub trait Handler<Ctx: HasValidationContext, O> {
468 type Outcome: Sized;
469
470 fn handle_opcode(
474 &mut self,
475 ctx: &Ctx,
476 state: &ValidationState,
477 reachability: Reachability,
478 opcode: O,
479 ) -> anyhow::Result<()>;
480
481 fn finish(self, state: &ValidationState) -> anyhow::Result<Self::Outcome>;
484}
485
486#[derive(Default)]
489pub struct PureWasmModuleHandler {
490 pub(crate) instr: Vec<OpCode>,
491}
492
493impl<Ctx: HasValidationContext> Handler<Ctx, OpCode> for PureWasmModuleHandler {
494 type Outcome = (Self, usize);
495
496 #[cfg_attr(not(feature = "fuzz-coverage"), inline(always))]
497 fn handle_opcode(
498 &mut self,
499 _ctx: &Ctx,
500 _state: &ValidationState,
501 _reachability: Reachability,
502 opcode: OpCode,
503 ) -> anyhow::Result<()> {
504 anyhow::ensure!(!matches!(opcode, OpCode::TickEnergy(_)));
505 self.instr.push(opcode);
506 Ok(())
507 }
508
509 #[cfg_attr(not(feature = "fuzz-coverage"), inline(always))]
510 fn finish(self, state: &ValidationState) -> anyhow::Result<Self::Outcome> {
511 Ok((self, state.max_reachable_height))
512 }
513}
514
515pub fn validate<O: Borrow<OpCode>, Ctx: HasValidationContext, H: Handler<Ctx, O>>(
522 context: &Ctx,
523 opcodes: impl Iterator<Item = ParseResult<O>>,
524 mut handler: H,
525) -> ValidateResult<H::Outcome> {
526 let mut state = ValidationState {
527 opds: OperandStack::default(),
528 ctrls: ControlStack::default(),
529 max_reachable_height: 0,
530 unreachable_section: None,
531 };
532 state.push_ctrl(false, context.return_type(), context.return_type());
533 for opcode in opcodes {
534 let next_opcode = opcode?;
535 let unreachable_before = state.reachability();
536 match next_opcode.borrow() {
537 OpCode::TickEnergy(_) => {
538 }
541 OpCode::End => {
542 let (res, is_if) = state.pop_ctrl()?;
543 if is_if {
544 ensure!(
545 res == BlockType::EmptyType,
546 "If without an else must have empty return type"
547 )
548 }
549 state.push_opds(res);
550 }
551 OpCode::Nop => {
552 }
554 OpCode::Unreachable => {
555 state.mark_unreachable()?;
556 }
557 OpCode::Block(ty) => {
558 state.push_ctrl(false, *ty, *ty);
559 }
560 OpCode::Loop(ty) => {
561 state.push_ctrl(false, BlockType::EmptyType, *ty);
562 }
563 OpCode::If {
564 ty,
565 } => {
566 state.pop_expect_opd(Known(ValueType::I32))?;
567 state.push_ctrl(true, *ty, *ty);
568 }
569 OpCode::Else => {
570 let (res, is_if) = state.pop_ctrl()?;
571 ensure!(is_if, "Else can only come after an if");
572 state.push_ctrl(false, res, res);
573 }
574 OpCode::Br(label) => {
575 if let Some(label_type) = state.ctrls.get_label(*label) {
576 state.pop_opds(label_type)?;
577 state.mark_unreachable()?;
578 } else {
579 bail!("Jump to a non-existent label.")
580 }
581 }
582 OpCode::BrIf(label) => {
583 if let Some(label_type) = state.ctrls.get_label(*label) {
584 state.pop_expect_opd(Known(ValueType::I32))?;
585 state.pop_opds(label_type)?;
586 state.push_opds(label_type);
587 } else {
588 bail!("Conditional jump to non-existent label.")
589 }
590 }
591 OpCode::BrTable {
592 labels,
593 default,
594 } => {
595 ensure!(
596 labels.len() <= MAX_SWITCH_SIZE,
597 "Size of switch statement exceeds maximum."
598 );
599 if let Some(default_label_type) = state.ctrls.get_label(*default) {
600 for &label in labels.iter() {
601 if let Some(target_frame) = state.ctrls.get(label) {
602 ensure!(
603 default_label_type == target_frame.label_type,
604 "Different targets have different label types."
605 );
606 } else {
607 bail!("Table jump to non-existent label.")
608 }
609 }
610 state.pop_expect_opd(Known(ValueType::I32))?;
611 state.pop_opds(default_label_type)?;
612 state.mark_unreachable()?;
613 } else {
614 bail!("Table jump to non-existent label.")
615 }
616 }
617 OpCode::Return => {
618 if let Some(label_type) = state.ctrls.outermost().map(|frame| frame.label_type) {
619 state.pop_opds(label_type)?;
620 state.mark_unreachable()?;
621 }
622 }
623 OpCode::Call(idx) => {
624 let func = context.get_func(*idx)?;
625 for &ty in func.parameters.iter().rev() {
626 state.pop_expect_opd(Known(ty))?;
627 }
628 for &ty in func.result.iter() {
629 state.push_opd(Known(ty))
630 }
631 }
632 OpCode::CallIndirect(idx) => {
633 ensure!(context.table_exists(), "Table with index 0 must exist.");
634 let func = context.get_type(*idx)?;
636 state.pop_expect_opd(Known(ValueType::I32))?;
637 for &ty in func.parameters.iter().rev() {
638 state.pop_expect_opd(Known(ty))?;
639 }
640 for &ty in func.result.iter() {
641 state.push_opd(Known(ty))
642 }
643 }
644 OpCode::Drop => {
645 state.pop_opd()?;
646 }
647 OpCode::Select => {
648 state.pop_expect_opd(Known(ValueType::I32))?;
649 let t1 = state.pop_opd()?;
650 let t2 = state.pop_expect_opd(t1)?;
651 state.push_opd(t2);
652 }
653 OpCode::LocalGet(idx) => {
654 let ty = context.get_local(*idx)?;
655 state.push_opd(Known(ty));
656 }
657 OpCode::LocalSet(idx) => {
658 let ty = context.get_local(*idx)?;
659 state.pop_expect_opd(Known(ty))?;
660 }
661 OpCode::LocalTee(idx) => {
662 let ty = context.get_local(*idx)?;
663 let stack_ty = state.pop_expect_opd(Known(ty))?;
664 state.push_opd(stack_ty);
665 }
666 OpCode::GlobalGet(idx) => {
667 let ty = context.get_global(*idx)?.0;
668 state.push_opd(Known(ty));
669 }
670 OpCode::GlobalSet(idx) => {
671 let (ty, mutable) = context.get_global(*idx)?;
672 ensure!(mutable, "Trying to set a const global.");
673 state.pop_expect_opd(Known(ty))?;
674 }
675 OpCode::I32Load(memarg) => {
676 ensure!(context.memory_exists(), "Memory should exist.");
677 ensure_alignment(memarg.align, Type::I32)?;
678 state.pop_expect_opd(Known(ValueType::I32))?;
679 state.push_opd(Known(ValueType::I32));
680 }
681 OpCode::I64Load(memarg) => {
682 ensure!(context.memory_exists(), "Memory should exist.");
683 ensure_alignment(memarg.align, Type::I64)?;
684 state.pop_expect_opd(Known(ValueType::I32))?;
685 state.push_opd(Known(ValueType::I64));
686 }
687 OpCode::I32Load8S(memarg) => {
688 ensure!(context.memory_exists(), "Memory should exist.");
689 ensure_alignment(memarg.align, Type::I8)?;
690 state.pop_expect_opd(Known(ValueType::I32))?;
691 state.push_opd(Known(ValueType::I32));
692 }
693 OpCode::I32Load8U(memarg) => {
694 ensure!(context.memory_exists(), "Memory should exist.");
695 ensure_alignment(memarg.align, Type::I8)?;
696 state.pop_expect_opd(Known(ValueType::I32))?;
697 state.push_opd(Known(ValueType::I32));
698 }
699 OpCode::I32Load16S(memarg) => {
700 ensure!(context.memory_exists(), "Memory should exist.");
701 ensure_alignment(memarg.align, Type::I16)?;
702 state.pop_expect_opd(Known(ValueType::I32))?;
703 state.push_opd(Known(ValueType::I32));
704 }
705 OpCode::I32Load16U(memarg) => {
706 ensure!(context.memory_exists(), "Memory should exist.");
707 ensure_alignment(memarg.align, Type::I16)?;
708 state.pop_expect_opd(Known(ValueType::I32))?;
709 state.push_opd(Known(ValueType::I32));
710 }
711 OpCode::I64Load8S(memarg) => {
712 ensure!(context.memory_exists(), "Memory should exist.");
713 ensure_alignment(memarg.align, Type::I8)?;
714 state.pop_expect_opd(Known(ValueType::I32))?;
715 state.push_opd(Known(ValueType::I64));
716 }
717 OpCode::I64Load8U(memarg) => {
718 ensure!(context.memory_exists(), "Memory should exist.");
719 ensure_alignment(memarg.align, Type::I8)?;
720 ensure!(memarg.align == 0, "Alignment out of range");
721 state.pop_expect_opd(Known(ValueType::I32))?;
722 state.push_opd(Known(ValueType::I64));
723 }
724 OpCode::I64Load16S(memarg) => {
725 ensure!(context.memory_exists(), "Memory should exist.");
726 ensure_alignment(memarg.align, Type::I16)?;
727 state.pop_expect_opd(Known(ValueType::I32))?;
728 state.push_opd(Known(ValueType::I64));
729 }
730 OpCode::I64Load16U(memarg) => {
731 ensure!(context.memory_exists(), "Memory should exist.");
732 ensure_alignment(memarg.align, Type::I16)?;
733 state.pop_expect_opd(Known(ValueType::I32))?;
734 state.push_opd(Known(ValueType::I64));
735 }
736 OpCode::I64Load32S(memarg) => {
737 ensure!(context.memory_exists(), "Memory should exist.");
738 ensure_alignment(memarg.align, Type::I32)?;
739 state.pop_expect_opd(Known(ValueType::I32))?;
740 state.push_opd(Known(ValueType::I64));
741 }
742 OpCode::I64Load32U(memarg) => {
743 ensure!(context.memory_exists(), "Memory should exist.");
744 ensure_alignment(memarg.align, Type::I32)?;
745 state.pop_expect_opd(Known(ValueType::I32))?;
746 state.push_opd(Known(ValueType::I64));
747 }
748 OpCode::I32Store(memarg) => {
749 ensure!(context.memory_exists(), "Memory should exist.");
750 ensure_alignment(memarg.align, Type::I32)?;
751 state.pop_expect_opd(Known(ValueType::I32))?;
752 state.pop_expect_opd(Known(ValueType::I32))?;
753 }
754 OpCode::I64Store(memarg) => {
755 ensure!(context.memory_exists(), "Memory should exist.");
756 ensure_alignment(memarg.align, Type::I64)?;
757 state.pop_expect_opd(Known(ValueType::I64))?;
758 state.pop_expect_opd(Known(ValueType::I32))?;
759 }
760 OpCode::I32Store8(memarg) => {
761 ensure!(context.memory_exists(), "Memory should exist.");
762 ensure_alignment(memarg.align, Type::I8)?;
763 state.pop_expect_opd(Known(ValueType::I32))?;
764 state.pop_expect_opd(Known(ValueType::I32))?;
765 }
766 OpCode::I32Store16(memarg) => {
767 ensure!(context.memory_exists(), "Memory should exist.");
768 ensure_alignment(memarg.align, Type::I16)?;
769 state.pop_expect_opd(Known(ValueType::I32))?;
770 state.pop_expect_opd(Known(ValueType::I32))?;
771 }
772 OpCode::I64Store8(memarg) => {
773 ensure!(context.memory_exists(), "Memory should exist.");
774 ensure_alignment(memarg.align, Type::I8)?;
775 state.pop_expect_opd(Known(ValueType::I64))?;
776 state.pop_expect_opd(Known(ValueType::I32))?;
777 }
778 OpCode::I64Store16(memarg) => {
779 ensure!(context.memory_exists(), "Memory should exist.");
780 ensure_alignment(memarg.align, Type::I16)?;
781 state.pop_expect_opd(Known(ValueType::I64))?;
782 state.pop_expect_opd(Known(ValueType::I32))?;
783 }
784 OpCode::I64Store32(memarg) => {
785 ensure!(context.memory_exists(), "Memory should exist.");
786 ensure_alignment(memarg.align, Type::I32)?;
787 state.pop_expect_opd(Known(ValueType::I64))?;
788 state.pop_expect_opd(Known(ValueType::I32))?;
789 }
790 OpCode::MemorySize => {
791 ensure!(context.memory_exists(), "Memory should exist.");
792 state.push_opd(Known(ValueType::I32))
793 }
794 OpCode::MemoryGrow => {
795 ensure!(context.memory_exists(), "Memory should exist.");
796 state.pop_expect_opd(Known(ValueType::I32))?;
797 state.push_opd(Known(ValueType::I32))
798 }
799 OpCode::I32Const(_) => {
800 state.push_opd(Known(ValueType::I32));
801 }
802 OpCode::I64Const(_) => {
803 state.push_opd(Known(ValueType::I64));
804 }
805 OpCode::I32Eqz => {
806 state.pop_expect_opd(Known(ValueType::I32))?;
807 state.push_opd(Known(ValueType::I32));
808 }
809 OpCode::I32Eq
810 | OpCode::I32Ne
811 | OpCode::I32LtS
812 | OpCode::I32LtU
813 | OpCode::I32GtS
814 | OpCode::I32GtU
815 | OpCode::I32LeS
816 | OpCode::I32LeU
817 | OpCode::I32GeS
818 | OpCode::I32GeU => {
819 state.pop_expect_opd(Known(ValueType::I32))?;
820 state.pop_expect_opd(Known(ValueType::I32))?;
821 state.push_opd(Known(ValueType::I32));
822 }
823 OpCode::I64Eqz => {
824 state.pop_expect_opd(Known(ValueType::I64))?;
825 state.push_opd(Known(ValueType::I32));
826 }
827 OpCode::I64Eq
828 | OpCode::I64Ne
829 | OpCode::I64LtS
830 | OpCode::I64LtU
831 | OpCode::I64GtS
832 | OpCode::I64GtU
833 | OpCode::I64LeS
834 | OpCode::I64LeU
835 | OpCode::I64GeS
836 | OpCode::I64GeU => {
837 state.pop_expect_opd(Known(ValueType::I64))?;
838 state.pop_expect_opd(Known(ValueType::I64))?;
839 state.push_opd(Known(ValueType::I32));
840 }
841 OpCode::I32Clz | OpCode::I32Ctz | OpCode::I32Popcnt => {
842 state.pop_expect_opd(Known(ValueType::I32))?;
843 state.push_opd(Known(ValueType::I32));
844 }
845 OpCode::I32Add
846 | OpCode::I32Sub
847 | OpCode::I32Mul
848 | OpCode::I32DivS
849 | OpCode::I32DivU
850 | OpCode::I32RemS
851 | OpCode::I32RemU
852 | OpCode::I32And
853 | OpCode::I32Or
854 | OpCode::I32Xor
855 | OpCode::I32Shl
856 | OpCode::I32ShrS
857 | OpCode::I32ShrU
858 | OpCode::I32Rotl
859 | OpCode::I32Rotr => {
860 state.pop_expect_opd(Known(ValueType::I32))?;
861 state.pop_expect_opd(Known(ValueType::I32))?;
862 state.push_opd(Known(ValueType::I32));
863 }
864 OpCode::I64Clz | OpCode::I64Ctz | OpCode::I64Popcnt => {
865 state.pop_expect_opd(Known(ValueType::I64))?;
866 state.push_opd(Known(ValueType::I64));
867 }
868 OpCode::I64Add
869 | OpCode::I64Sub
870 | OpCode::I64Mul
871 | OpCode::I64DivS
872 | OpCode::I64DivU
873 | OpCode::I64RemS
874 | OpCode::I64RemU
875 | OpCode::I64And
876 | OpCode::I64Or
877 | OpCode::I64Xor
878 | OpCode::I64Shl
879 | OpCode::I64ShrS
880 | OpCode::I64ShrU
881 | OpCode::I64Rotl
882 | OpCode::I64Rotr => {
883 state.pop_expect_opd(Known(ValueType::I64))?;
884 state.pop_expect_opd(Known(ValueType::I64))?;
885 state.push_opd(Known(ValueType::I64));
886 }
887 OpCode::I32WrapI64 => {
888 state.pop_expect_opd(Known(ValueType::I64))?;
889 state.push_opd(Known(ValueType::I32));
890 }
891 OpCode::I64ExtendI32S | OpCode::I64ExtendI32U => {
892 state.pop_expect_opd(Known(ValueType::I32))?;
893 state.push_opd(Known(ValueType::I64));
894 }
895 OpCode::I32Extend8S | OpCode::I32Extend16S => {
896 state.pop_expect_opd(Known(ValueType::I32))?;
897 state.push_opd(Known(ValueType::I32));
898 }
899 OpCode::I64Extend8S | OpCode::I64Extend16S | OpCode::I64Extend32S => {
900 state.pop_expect_opd(Known(ValueType::I64))?;
901 state.push_opd(Known(ValueType::I64));
902 }
903 }
904 handler.handle_opcode(context, &state, unreachable_before, next_opcode)?;
905 }
906 if state.done() {
907 handler.finish(&state)
908 } else {
909 bail!("Improperly terminated instruction sequence.")
910 }
911}
912
913pub trait ValidateImportExport {
915 fn validate_import_function(
918 &self,
919 duplicate: bool,
920 mod_name: &Name,
921 item_name: &Name,
922 ty: &FunctionType,
923 ) -> bool;
924
925 fn validate_export_function(&self, item_name: &Name, ty: &FunctionType) -> bool;
928}
929
930#[derive(Copy, Clone, Debug)]
938pub struct ValidationConfig {
939 pub allow_globals_in_init: bool,
944 pub allow_sign_extension_instr: bool,
946}
947
948impl ValidationConfig {
949 pub const V0: Self = Self {
951 allow_globals_in_init: true,
952 allow_sign_extension_instr: false,
953 };
954 pub const V1: Self = Self {
956 allow_globals_in_init: false,
957 allow_sign_extension_instr: true,
958 };
959}
960
961pub fn validate_module(
964 config: ValidationConfig,
965 imp: &impl ValidateImportExport,
966 skeleton: &Skeleton<'_>,
967) -> ValidateResult<Module> {
968 for cs in skeleton.custom.iter() {
972 parse_custom(cs)?;
973 }
974
975 let ty: TypeSection = parse_sec_with_default(EMPTY_CTX, &skeleton.ty)?;
977 let import: ImportSection = parse_sec_with_default(EMPTY_CTX, &skeleton.import)?;
979 {
980 let mut seen_imports = BTreeSet::new();
981 for i in import.imports.iter() {
982 match i.description {
983 ImportDescription::Func {
984 type_idx,
985 } => {
986 if let Some(ty) = ty.get(type_idx) {
987 let is_new = seen_imports.insert((&i.mod_name, &i.item_name));
988 ensure!(
989 imp.validate_import_function(!is_new, &i.mod_name, &i.item_name, ty),
990 "Disallowed import."
991 );
992 } else {
993 bail!("Import refers to a non-existent type.");
994 }
995 }
996 }
997 }
998 }
999 let table: TableSection = parse_sec_with_default(EMPTY_CTX, &skeleton.table)?;
1002
1003 let memory: MemorySection = parse_sec_with_default(EMPTY_CTX, &skeleton.memory)?;
1006
1007 let global: GlobalSection = parse_sec_with_default(config, &skeleton.global)?;
1011 ensure!(
1012 global.globals.len() <= MAX_NUM_GLOBALS,
1013 "The number of globals must not exceed {}.",
1014 MAX_NUM_GLOBALS
1015 );
1016
1017 let start = parse_sec_with_default(EMPTY_CTX, &skeleton.start)?;
1020
1021 let func: FunctionSection = parse_sec_with_default(EMPTY_CTX, &skeleton.func)?;
1025 for &type_idx in func.types.iter() {
1026 ensure!(ty.get(type_idx).is_some(), "Function refers to a type that does not exist.")
1027 }
1028
1029 let total_funcs =
1034 import.imports.iter().filter(|&x| Import::is_func(x)).count() + func.types.len();
1035
1036 let code: CodeSkeletonSection = parse_sec_with_default(EMPTY_CTX, &skeleton.code)?;
1037 ensure!(
1038 func.types.len() == code.impls.len(),
1039 "The number of functions in the function and code sections must match."
1040 );
1041 let funcs = import
1043 .imports
1044 .iter()
1045 .map(|i| match i.description {
1046 ImportDescription::Func {
1047 type_idx,
1048 } => type_idx,
1049 })
1050 .chain(func.types.iter().copied())
1051 .collect::<Vec<TypeIndex>>();
1052
1053 let mut parsed_code = Vec::with_capacity(code.impls.len());
1054 for (&f, c) in func.types.iter().zip(code.impls) {
1055 match ty.get(f) {
1056 Some(func_ty) => {
1057 let (locals, num_locals) = make_locals(func_ty, &c.locals)?;
1058 let ctx = FunctionContext {
1059 return_type: BlockType::from(func_ty.result),
1060 globals: &global.globals,
1061 funcs: &funcs,
1062 types: &ty.types,
1063 locals,
1064 memory: memory.memory_type.is_some(),
1065 table: table.table_type.is_some(),
1066 };
1067 let (opcodes, max_height) = validate(
1068 &ctx,
1069 &mut OpCodeIterator::new(config.allow_sign_extension_instr, c.expr_bytes),
1070 PureWasmModuleHandler::default(),
1071 )?;
1072 ensure!(
1073 num_locals as usize + max_height <= MAX_ALLOWED_STACK_HEIGHT,
1074 "Stack height would exceed allowed limits."
1075 );
1076
1077 let code = Code {
1078 ty: func_ty.clone(),
1079 ty_idx: f,
1080 num_locals,
1081 locals: c.locals,
1082 expr: Expression {
1083 instrs: opcodes.instr,
1084 },
1085 };
1086 parsed_code.push(code)
1087 }
1088 None => bail!("Function has a type that does not exist."),
1089 }
1090 }
1091 let export: ExportSection = parse_sec_with_default(EMPTY_CTX, &skeleton.export)?;
1094 let mut export_names = BTreeSet::new();
1095 ensure!(export.exports.len() <= MAX_NUM_EXPORTS, "Module exceeds maximum number of exports.");
1096 for e in export.exports.iter() {
1097 ensure!(export_names.insert(&e.name), "Duplicate exports {}.", e.name);
1099
1100 match e.description {
1101 ExportDescription::Func {
1102 index,
1103 } => {
1104 if let Some(ty) = funcs.get(index as usize).and_then(|ty_idx| ty.get(*ty_idx)) {
1105 ensure!(imp.validate_export_function(&e.name, ty), "Export function not valid.")
1106 } else {
1107 bail!("Trying to export a function that does not exist.")
1108 }
1109 }
1110 ExportDescription::Table => {
1111 ensure!(
1112 table.table_type.is_some(),
1113 "Trying to export a table, but no table is declared."
1114 );
1115 }
1116 ExportDescription::Memory => {
1117 ensure!(
1118 memory.memory_type.is_some(),
1119 "Trying to export a memory, but no memory is declared."
1120 );
1121 }
1122 ExportDescription::Global {
1123 index,
1124 } => {
1125 ensure!(
1126 global.get(index).is_some(),
1127 "Trying to export a global that does not exist."
1128 );
1129 }
1130 }
1131 }
1132
1133 let instr_validation_ctx = InstructionValidationContext {
1139 globals_allowed: if config.allow_globals_in_init {
1140 Some(&global)
1141 } else {
1142 None
1143 },
1144 allow_sign_extension_instr: config.allow_sign_extension_instr,
1145 };
1146 let element: ElementSection = parse_sec_with_default(instr_validation_ctx, &skeleton.element)?;
1147 ensure!(
1148 element.elements.is_empty() || table.table_type.is_some(),
1149 "There is an elements section, but no table."
1150 );
1151 for elem in element.elements.iter() {
1152 let inits_len: u32 = elem.inits.len().try_into()?;
1153 ensure!(
1154 inits_len <= MAX_INIT_TABLE_SIZE,
1155 "Number of initial elements is more than the table size."
1156 );
1157 if let Some(table_type) = table.table_type.as_ref() {
1158 let offset = elem.offset as u32;
1159 let end = offset
1164 .checked_add(inits_len)
1165 .ok_or_else(|| anyhow!("The end of the table exceeds u32 max bound."))?;
1166 ensure!(
1167 end <= table_type.limits.min,
1168 "Initialization expression for the table exceeds table size {} > {}.",
1169 end,
1170 table_type.limits.min
1171 );
1172 }
1173 for &init in elem.inits.iter() {
1174 ensure!(
1175 (init as usize) < total_funcs,
1176 "Index in the element segment refers to a non-existent function."
1177 );
1178 }
1179 }
1180
1181 let data: DataSection = parse_sec_with_default(instr_validation_ctx, &skeleton.data)?;
1187 if let Some(memory_type) = memory.memory_type.as_ref() {
1191 for data in data.sections.iter() {
1192 let inits_len: u32 = data.init.len().try_into()?;
1193 ensure!(
1194 inits_len <= memory_type.limits.min * PAGE_SIZE,
1197 "Number of initial elements is more than the initial memory size."
1198 );
1199 let offset: u32 = data.offset.try_into()?;
1200 let end = offset
1201 .checked_add(inits_len)
1202 .ok_or_else(|| anyhow!("The end of the memory exceeds u32 max bound."))?;
1203 ensure!(
1204 memory_type.limits.min.checked_mul(PAGE_SIZE).map_or(false, |l| end <= l),
1207 "Initialization expression for the data segment exceeds initial memory size {} > \
1208 {}.",
1209 end,
1210 memory_type.limits.min * PAGE_SIZE
1211 );
1212 }
1213 } else {
1214 ensure!(data.sections.is_empty(), "There are data sections, but no declared memory.");
1216 }
1217 Ok(Module {
1218 ty,
1219 import,
1220 func,
1221 table,
1222 memory,
1223 global,
1224 export,
1225 start,
1226 element,
1227 code: CodeSection {
1228 impls: parsed_code,
1229 },
1230 data,
1231 })
1232}