Skip to content

Commit e2b67d2

Browse files
committed
interpreter: Fix out-of-range execution ProgramCounter
Currently, the program counter always returns 0 when the last instruction is branch instruction and it evaluates to false. The correct behavior should be returning the faulting address. This is also weird because the program counter returns correct value if the last instruction is something else. Also, on re-compiler we do return correct faulting address. This patch fixes interpreter behavior and makes it consistent with re-compiler. Signed-off-by: Aman <[email protected]>
1 parent 4621204 commit e2b67d2

File tree

5 files changed

+81
-10
lines changed

5 files changed

+81
-10
lines changed

.github/workflows/rust.yml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,8 @@ jobs:
4646
- uses: actions/checkout@v4
4747
- name: Install clippy (base toolchain)
4848
run: rustup component add clippy
49+
- name: Install clippy (zygote toolchain)
50+
run: cd crates/polkavm-zygote && rustup component add clippy
4951
- name: Install clippy (benchtool toolchain)
5052
run: cd tools/benchtool && rustup component add clippy
5153
- name: Install clippy (guest-programs toolchain)

ci/jobs/fuzz.sh

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,3 +11,9 @@ cargo fuzz run fuzz_generic_allocator -- -runs=1000000
1111

1212
echo ">> cargo fuzz run (fuzz_shm_allocator)"
1313
cargo fuzz run fuzz_shm_allocator -- -runs=1000000
14+
15+
echo ">> cargo fuzz run (fuzz_linker)"
16+
cargo fuzz run fuzz_linker -- -runs=10000
17+
18+
echo ">> cargo fuzz run (fuzz_polkavm)"
19+
cargo fuzz run fuzz_polkavm -- -runs=10000

crates/polkavm/src/interpreter.rs

Lines changed: 48 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -411,6 +411,7 @@ pub(crate) struct InterpretedInstance {
411411
compiled_offset: u32,
412412
interrupt: InterruptKind,
413413
step_tracing: bool,
414+
unresolved_program_counter: Option<ProgramCounter>,
414415
}
415416

416417
impl InterpretedInstance {
@@ -433,6 +434,7 @@ impl InterpretedInstance {
433434
compiled_offset: 0,
434435
interrupt: InterruptKind::Finished,
435436
step_tracing,
437+
unresolved_program_counter: None,
436438
};
437439

438440
instance.initialize_module();
@@ -715,9 +717,15 @@ impl InterpretedInstance {
715717
};
716718

717719
self.program_counter = program_counter;
718-
self.compiled_offset = self.resolve_arbitrary_jump::<DEBUG>(program_counter).unwrap_or(TARGET_OUT_OF_RANGE);
719720
self.next_program_counter_changed = false;
720721

722+
if let Some(offset) = self.resolve_arbitrary_jump::<DEBUG>(program_counter) {
723+
self.compiled_offset = offset;
724+
} else {
725+
self.compiled_offset = TARGET_OUT_OF_RANGE;
726+
self.unresolved_program_counter = Some(program_counter);
727+
}
728+
721729
if DEBUG {
722730
log::debug!("Starting execution at: {} [{}]", program_counter, self.compiled_offset);
723731
}
@@ -1035,10 +1043,24 @@ impl<'a> Visitor<'a> {
10351043
) -> Option<Target> {
10361044
let s1 = self.get64(s1);
10371045
let s2 = self.get64(s2);
1038-
if callback(s1, s2) {
1039-
Some(target_true)
1046+
1047+
let (packed_target, branch_taken) = if callback(s1, s2) {
1048+
(target_true, true)
1049+
} else {
1050+
(target_false, false)
1051+
};
1052+
1053+
let (is_jump_target_valid, target) = InterpretedInstance::unpack_target(NonZeroU32::new(packed_target).unwrap());
1054+
1055+
if !is_jump_target_valid {
1056+
self.inner.unresolved_program_counter = Some(ProgramCounter(target));
1057+
if branch_taken {
1058+
Some(TARGET_INVALID_BRANCH)
1059+
} else {
1060+
Some(TARGET_OUT_OF_RANGE)
1061+
}
10401062
} else {
1041-
Some(target_false)
1063+
Some(target)
10421064
}
10431065
}
10441066

@@ -1857,6 +1879,7 @@ fn trap_impl<const DEBUG: bool>(visitor: &mut Visitor, program_counter: ProgramC
18571879
visitor.inner.program_counter_valid = true;
18581880
visitor.inner.next_program_counter = None;
18591881
visitor.inner.next_program_counter_changed = true;
1882+
visitor.inner.unresolved_program_counter = None;
18601883
visitor.inner.interrupt = InterruptKind::Trap;
18611884
None
18621885
}
@@ -1891,10 +1914,21 @@ macro_rules! handle_unresolved_branch {
18911914
}
18921915

18931916
let offset = $visitor.inner.compiled_offset;
1894-
let target_false = $visitor.inner.resolve_jump::<DEBUG>($tf).unwrap_or(TARGET_OUT_OF_RANGE);
1895-
let target_true = $visitor.inner.resolve_jump::<DEBUG>($tt).unwrap_or(TARGET_INVALID_BRANCH);
1917+
1918+
let packed_tt = if let Some(target_true) = $visitor.inner.resolve_jump::<DEBUG>($tt) {
1919+
InterpretedInstance::pack_target(cast(target_true).to_usize(), true).into()
1920+
} else {
1921+
InterpretedInstance::pack_target(cast($tt.0).to_usize(), false).into()
1922+
};
1923+
1924+
let packed_tf = if let Some(target_false) = $visitor.inner.resolve_jump::<DEBUG>($tf) {
1925+
InterpretedInstance::pack_target(cast(target_false).to_usize(), true).into()
1926+
} else {
1927+
InterpretedInstance::pack_target(cast($tf.0).to_usize(), false).into()
1928+
};
1929+
18961930
$visitor.inner.compiled_handlers[cast(offset).to_usize()] = cast_handler!(raw_handlers::$name::<DEBUG>);
1897-
$visitor.inner.compiled_args[cast(offset).to_usize()] = Args::$name($s1, $s2, target_true, target_false);
1931+
$visitor.inner.compiled_args[cast(offset).to_usize()] = Args::$name($s1, $s2, packed_tt, packed_tf);
18981932
Some(offset)
18991933
}};
19001934
}
@@ -1929,7 +1963,8 @@ define_interpreter! {
19291963
log::trace!("[{}]: trap (out of range)", visitor.inner.compiled_offset);
19301964
}
19311965

1932-
let program_counter = visitor.inner.program_counter;
1966+
let program_counter = visitor.inner.unresolved_program_counter.unwrap_or(visitor.inner.program_counter);
1967+
19331968
let new_gas = visitor.inner.gas - i64::from(gas);
19341969
if new_gas < 0 {
19351970
not_enough_gas_impl::<DEBUG>(visitor, program_counter, new_gas)
@@ -1960,8 +1995,11 @@ define_interpreter! {
19601995
log::trace!("[{}]: step (out of range)", visitor.inner.compiled_offset);
19611996
}
19621997

1998+
let program_counter = visitor.inner.unresolved_program_counter.unwrap_or(visitor.inner.program_counter);
1999+
2000+
visitor.inner.program_counter = program_counter;
19632001
visitor.inner.program_counter_valid = true;
1964-
visitor.inner.next_program_counter = Some(visitor.inner.program_counter);
2002+
visitor.inner.next_program_counter = Some(program_counter);
19652003
visitor.inner.next_program_counter_changed = false;
19662004
visitor.inner.interrupt = InterruptKind::Step;
19672005
visitor.inner.compiled_offset += 1;
@@ -3640,6 +3678,7 @@ define_interpreter! {
36403678
} else {
36413679
visitor.inner.compiled_handlers[cast(offset).to_usize()] = cast_handler!(raw_handlers::jump::<DEBUG>);
36423680
visitor.inner.compiled_args[cast(offset).to_usize()] = Args::jump(TARGET_OUT_OF_RANGE);
3681+
visitor.inner.unresolved_program_counter = Some(jump_to);
36433682
Some(TARGET_OUT_OF_RANGE)
36443683
}
36453684
}

crates/polkavm/src/tests.rs

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -692,6 +692,28 @@ fn simple_test(engine_config: Config) {
692692
assert_eq!(instance.reg(Reg::A1), 100);
693693
}
694694

695+
fn out_of_range_execution(engine_config: Config) {
696+
let _ = env_logger::try_init();
697+
let engine = Engine::new(&engine_config).unwrap();
698+
let mut builder = ProgramBlobBuilder::new();
699+
builder.add_export_by_basic_block(0, b"main");
700+
builder.set_code(&[asm::load_imm(A0, 1), asm::load_imm(A0, 2), asm::branch_eq_imm(RA, 0, 0)], &[]);
701+
702+
let blob = ProgramBlob::parse(builder.into_vec().unwrap().into()).unwrap();
703+
let module = Module::from_blob(&engine, &ModuleConfig::new(), blob).unwrap();
704+
let next_offsets: Vec<_> = module
705+
.blob()
706+
.instructions(DefaultInstructionSet::default())
707+
.map(|inst| inst.next_offset)
708+
.collect();
709+
710+
let mut instance = module.instantiate().unwrap();
711+
instance.set_reg(Reg::RA, crate::RETURN_TO_HOST);
712+
instance.set_next_program_counter(ProgramCounter(0));
713+
match_interrupt!(instance.run().unwrap(), InterruptKind::Trap);
714+
assert_eq!(instance.program_counter(), Some(next_offsets[2]));
715+
}
716+
695717
fn jump_into_middle_of_basic_block_from_outside(engine_config: Config) {
696718
let _ = env_logger::try_init();
697719
let engine = Engine::new(&engine_config).unwrap();
@@ -3861,6 +3883,8 @@ run_tests! {
38613883
trapping_preserves_all_registers_normal_trap
38623884
trapping_preserves_all_registers_segfault
38633885

3886+
out_of_range_execution
3887+
38643888
memset_basic
38653889
memset_with_dynamic_paging
38663890

fuzz/fuzz_targets/fuzz_polkavm.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -605,7 +605,7 @@ fn correctness_fuzzer_harness(data: Vec<OperationKind>) {
605605
//
606606

607607
match interrupt_interpreter.clone() {
608-
InterruptKind::NotEnoughGas | InterruptKind::Trap => {
608+
InterruptKind::Finished | InterruptKind::NotEnoughGas | InterruptKind::Trap => {
609609
break;
610610
}
611611
polkavm::InterruptKind::Ecalli(_) => {

0 commit comments

Comments
 (0)