Merge pull request #606 from mir-protocol/jumpdest_push_data

Fix interpreter JUMPDEST check + change stopping behavior
This commit is contained in:
wborgeaud 2022-07-14 13:18:15 +02:00 committed by GitHub
commit 0d62895098
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

View File

@ -1,21 +1,29 @@
use ethereum_types::{U256, U512}; use ethereum_types::{U256, U512};
/// Halt interpreter execution whenever a jump to this offset is done.
const HALT_OFFSET: usize = 0xdeadbeef;
struct Interpreter<'a> { struct Interpreter<'a> {
code: &'a [u8], code: &'a [u8],
jumpdests: Vec<usize>,
offset: usize, offset: usize,
stack: Vec<U256>, stack: Vec<U256>,
running: bool,
} }
pub fn run(code: &[u8], initial_offset: usize, initial_stack: Vec<U256>) -> Vec<U256> { pub fn run(code: &[u8], initial_offset: usize, initial_stack: Vec<U256>) -> Vec<U256> {
let mut interpreter = Interpreter { let mut interpreter = Interpreter {
code, code,
jumpdests: find_jumpdests(code),
offset: initial_offset, offset: initial_offset,
stack: initial_stack, stack: initial_stack,
running: true,
}; };
// Halt the execution if a jump to 0xdeadbeef was done.
while interpreter.offset != 0xdeadbeef { while interpreter.running {
interpreter.run_opcode(); interpreter.run_opcode();
} }
interpreter.stack interpreter.stack
} }
@ -41,10 +49,10 @@ impl<'a> Interpreter<'a> {
} }
fn run_opcode(&mut self) { fn run_opcode(&mut self) {
let opcode = self.code[self.offset]; let opcode = self.code.get(self.offset).copied().unwrap_or_default();
self.incr(1); self.incr(1);
match opcode { match opcode {
0x00 => todo!(), // "STOP", 0x00 => self.run_stop(), // "STOP",
0x01 => self.run_add(), // "ADD", 0x01 => self.run_add(), // "ADD",
0x02 => self.run_mul(), // "MUL", 0x02 => self.run_mul(), // "MUL",
0x03 => self.run_sub(), // "SUB", 0x03 => self.run_sub(), // "SUB",
@ -129,6 +137,10 @@ impl<'a> Interpreter<'a> {
}; };
} }
fn run_stop(&mut self) {
self.running = false;
}
fn run_add(&mut self) { fn run_add(&mut self) {
let x = self.pop(); let x = self.pop();
let y = self.pop(); let y = self.pop();
@ -240,8 +252,10 @@ impl<'a> Interpreter<'a> {
fn run_jump(&mut self) { fn run_jump(&mut self) {
let x = self.pop().as_usize(); let x = self.pop().as_usize();
self.offset = x; self.offset = x;
if let Some(&landing_opcode) = self.code.get(self.offset) { if self.offset == HALT_OFFSET {
assert_eq!(landing_opcode, 0x5b, "Destination is not a JUMPDEST."); self.running = false;
} else if self.jumpdests.binary_search(&self.offset).is_err() {
panic!("Destination is not a JUMPDEST.");
} }
} }
@ -250,8 +264,10 @@ impl<'a> Interpreter<'a> {
let b = self.pop(); let b = self.pop();
if !b.is_zero() { if !b.is_zero() {
self.offset = x; self.offset = x;
if let Some(&landing_opcode) = self.code.get(self.offset) { if self.offset == HALT_OFFSET {
assert_eq!(landing_opcode, 0x5b, "Destination is not a JUMPDEST."); self.running = false;
} else if self.jumpdests.binary_search(&self.offset).is_err() {
panic!("Destination is not a JUMPDEST.");
} }
} }
} }
@ -272,6 +288,22 @@ impl<'a> Interpreter<'a> {
} }
} }
/// Return the (ordered) JUMPDEST offsets in the code.
fn find_jumpdests(code: &[u8]) -> Vec<usize> {
let mut offset = 0;
let mut res = Vec::new();
while offset < code.len() {
let opcode = code[offset];
match opcode {
0x5b => res.push(offset),
x if (0x60..0x80).contains(&x) => offset += x as usize - 0x5f, // PUSH instruction, disregard data.
_ => (),
}
offset += 1;
}
res
}
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use crate::cpu::kernel::interpreter::run; use crate::cpu::kernel::interpreter::run;