Skip to content
This repository was archived by the owner on Oct 3, 2025. It is now read-only.

Commit d59d31f

Browse files
committed
fix bug with br_if instruction corrupting stack
1 parent 3aa69da commit d59d31f

File tree

2 files changed

+29
-19
lines changed

2 files changed

+29
-19
lines changed

crates/tinywasm/src/interpreter/executor.rs

Lines changed: 23 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -104,6 +104,7 @@ impl<'store, 'stack> Executor<'store, 'stack> {
104104
/// can mutliply time spent in execution
105105
/// execution may not be suspended in the middle of execution the funcion:
106106
/// so only do it as the last thing or first thing in the intsruction execution
107+
#[must_use = "If this returns ControlFlow::Break, the caller should propagate it"]
107108
fn check_should_suspend(&mut self) -> ControlFlow<ReasonToBreak> {
108109
if let Some(flag) = &self.store.suspend_cond.suspend_flag {
109110
if flag.load(core::sync::atomic::Ordering::Acquire) {
@@ -522,28 +523,34 @@ impl<'store, 'stack> Executor<'store, 'stack> {
522523
});
523524
}
524525
fn exec_br(&mut self, to: u32) -> ControlFlow<ReasonToBreak> {
525-
let break_type = if let Some(bl_ty) = self.cf.break_to(to, &mut self.stack.values, &mut self.stack.blocks) {
526-
bl_ty
527-
} else {
526+
let block_ty = self.cf.break_to(to, &mut self.stack.values, &mut self.stack.blocks);
527+
if block_ty.is_none() {
528528
return self.exec_return();
529-
};
530-
529+
}
530+
531531
self.cf.incr_instr_ptr();
532532

533-
if matches!(break_type, BlockType::Loop) {
533+
if matches!(block_ty, Some(BlockType::Loop)){
534534
self.check_should_suspend()?;
535535
}
536-
537536
ControlFlow::Continue(())
538537
}
539538
fn exec_br_if(&mut self, to: u32) -> ControlFlow<ReasonToBreak> {
540-
let break_type = self.cf.break_to(to, &mut self.stack.values, &mut self.stack.blocks);
541-
if self.stack.values.pop::<i32>() != 0 && break_type.is_none() {
542-
return self.exec_return();
543-
}
539+
let should_check_suspend = if self.stack.values.pop::<i32>() != 0 {
540+
// condition says we should break
541+
let block_ty = self.cf.break_to(to, &mut self.stack.values, &mut self.stack.blocks);
542+
if block_ty.is_none() {
543+
return self.exec_return();
544+
}
545+
matches!(block_ty, Some(BlockType::Loop))
546+
} else {
547+
// condition says we shouldn't break
548+
false
549+
};
550+
544551
self.cf.incr_instr_ptr();
545552

546-
if matches!(break_type, Some(BlockType::Loop)) {
553+
if should_check_suspend {
547554
self.check_should_suspend()?;
548555
}
549556
ControlFlow::Continue(())
@@ -567,19 +574,16 @@ impl<'store, 'stack> Executor<'store, 'stack> {
567574
_ => return ReasonToBreak::Errored(Error::Other("br_table out of bounds".to_string())).into(),
568575
};
569576

570-
let break_type = self.cf.break_to(to, &mut self.stack.values, &mut self.stack.blocks);
571-
let break_type = if let Some(br_ty) = break_type {
572-
br_ty
573-
} else {
577+
let block_ty = self.cf.break_to(to, &mut self.stack.values, &mut self.stack.blocks);
578+
if block_ty.is_none() {
574579
return self.exec_return();
575-
};
580+
}
576581

577582
self.cf.incr_instr_ptr();
578583

579-
if matches!(break_type, BlockType::Loop) {
584+
if matches!(block_ty, Some(BlockType::Loop)){
580585
self.check_should_suspend()?;
581586
}
582-
583587
ControlFlow::Continue(())
584588
}
585589
fn exec_return(&mut self) -> ControlFlow<ReasonToBreak> {

crates/tinywasm/src/interpreter/stack/call_stack.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -101,6 +101,12 @@ impl CallFrame {
101101

102102
/// Break to a block at the given index (relative to the current frame)
103103
/// Returns `None` if there is no block at the given index (e.g. if we need to return, this is handled by the caller)
104+
/// otherwise returns type if block it broke to
105+
/// <div class="warning">
106+
/// if it returned Some (broke to block),
107+
/// it expects caller to increment instruction pointer after calling it:
108+
/// otherwise caller might exit block that's already exited or inter block caller's already in
109+
/// </div>
104110
#[inline]
105111
pub(crate) fn break_to(
106112
&mut self,

0 commit comments

Comments
 (0)