Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
0f9f811
feat: local call/return detection with context-sensitive resolution
DaniPopes Apr 1, 2026
3e24227
refactor: feed PCR hints as seed edges into block analysis fixpoint
DaniPopes Apr 1, 2026
d601b74
cleaner
DaniPopes Apr 1, 2026
41b7a57
merge: resolve conflicts with main
DaniPopes Apr 1, 2026
f1bf6e5
rmdead
DaniPopes Apr 2, 2026
f4d6113
Merge remote-tracking branch 'origin/main' into dani/local-jump-resol…
DaniPopes Apr 4, 2026
5ab1570
fix: doc comment typo
DaniPopes Apr 4, 2026
14f5c81
refactor: reuse interpret_block in PCR label detection
DaniPopes Apr 4, 2026
6542976
refactor: move PCR pass to dedicated module
DaniPopes Apr 4, 2026
d3efdbb
chore: clean up pcr module style
DaniPopes Apr 4, 2026
67a6d94
refactor: extract ContextWorklist struct in PCR pass
DaniPopes Apr 4, 2026
7a52e3e
clean
DaniPopes Apr 4, 2026
9e801af
fix: soundness improvements for PCR pass
DaniPopes Apr 4, 2026
14bf82f
feat: provenance-based return detection for PCR
DaniPopes Apr 4, 2026
f38420e
refactor: extract apply_stack_shuffle, replace is_return_safe_block w…
DaniPopes Apr 4, 2026
6e574aa
refactor: move apply_stack_shuffle to Bytecode, fix JUMPI provenance …
DaniPopes Apr 4, 2026
9e0c26a
fix: soundness issues in PCR, dedup, and LLVM backend
DaniPopes Apr 5, 2026
15bd25d
better
DaniPopes Apr 5, 2026
3292f14
clean
DaniPopes Apr 5, 2026
f3d3962
cclippy
DaniPopes Apr 5, 2026
7648cd3
Merge branch 'main' into dani/local-jump-resolution
DaniPopes Apr 5, 2026
5b9b6f2
tst
DaniPopes Apr 5, 2026
3465efa
Merge branch 'main' into dani/local-jump-resolution
DaniPopes Apr 5, 2026
e21a008
extra
DaniPopes Apr 6, 2026
a7b7305
skip local tops
DaniPopes Apr 6, 2026
88b07fd
stuff
DaniPopes Apr 6, 2026
3ce7e40
logs
DaniPopes Apr 6, 2026
ccf6a6c
----------- CUT HERE
DaniPopes Apr 6, 2026
91bc0d8
wip
DaniPopes Apr 7, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
79 changes: 79 additions & 0 deletions crates/revmc/src/bytecode/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -426,6 +426,85 @@ impl<'a> Bytecode<'a> {
self.code.get(start).copied().unwrap_or(0)
}

/// Applies stack-shuffling opcodes (POP, DUP, SWAP, DUPN, SWAPN, EXCHANGE) to an abstract
/// stack.
///
/// Returns `Some(true)` if the opcode was handled, `Some(false)` on invalid stack underflow
/// or decode failure, or `None` if the opcode is not a stack-shuffling instruction (caller
/// handles PUSH and fallback).
///
/// `unknown` is pushed when DUPN/SWAPN/EXCHANGE access a slot beyond the tracked stack depth.
pub(crate) fn apply_stack_shuffle<T: Copy>(
&self,
inst: &InstData,
stack: &mut Vec<T>,
unknown: T,
) -> Option<bool> {
match inst.opcode {
op::POP => {
if stack.pop().is_none() {
return Some(false);
}
}
op::DUP1..=op::DUP16 => {
let depth = (inst.opcode - op::DUP1 + 1) as usize;
if stack.len() < depth {
return Some(false);
}
stack.push(stack[stack.len() - depth]);
}
op::SWAP1..=op::SWAP16 => {
let depth = (inst.opcode - op::SWAP1 + 1) as usize;
let len = stack.len();
if len < depth + 1 {
return Some(false);
}
stack.swap(len - 1, len - 1 - depth);
}
op::DUPN => {
let Some(n) = crate::decode_single(self.get_u8_imm(inst)) else {
return Some(false);
};
let n = n as usize;
if stack.len() < n {
stack.push(unknown);
} else {
stack.push(stack[stack.len() - n]);
}
}
op::SWAPN => {
let Some(n) = crate::decode_single(self.get_u8_imm(inst)) else {
return Some(false);
};
let n = n as usize;
let len = stack.len();
if len < n + 1 {
if let Some(tos) = stack.last_mut() {
*tos = unknown;
}
} else {
stack.swap(len - 1, len - 1 - n);
}
}
op::EXCHANGE => {
let Some((n, m)) = crate::decode_pair(self.get_u8_imm(inst)) else {
return Some(false);
};
let (n, m) = (n as usize, m as usize);
let len = stack.len();
if len < m + 1 {
if len > n {
stack[len - 1 - n] = unknown;
}
} else {
stack.swap(len - 1 - n, len - 1 - m);
}
}
_ => return None,
}
Some(true)
}

/// Returns `true` if the given program counter is a valid jump destination.
fn is_valid_jump(&self, pc: usize) -> bool {
self.jumpdests.get(pc).as_deref().copied() == Some(true)
Expand Down
Loading
Loading