Skip to content

Commit

Permalink
feat: add support for fuzzing bytecodes vs interpreter
Browse files Browse the repository at this point in the history
Co-authored-by: dergoegge <[email protected]>
  • Loading branch information
DaniPopes and dergoegge committed Jun 29, 2024
1 parent 60c9351 commit fecc46b
Show file tree
Hide file tree
Showing 12 changed files with 297 additions and 68 deletions.
36 changes: 36 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
[workspace]
members = ["crates/*", "examples/*"]
members = ["crates/*", "examples/*", "fuzz"]
default-members = ["crates/revmc-cli"]
resolver = "2"

Expand Down
2 changes: 1 addition & 1 deletion crates/revmc-cli/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ pub fn read_code_string(contents: &[u8], ext: Option<&str>) -> Result<Vec<u8>> {
hex::decode(first_line).wrap_err("given code is not valid hex")
} else if ext == Some("bin") || !contents.is_ascii() {
Ok(contents.to_vec())
} else if ext == Some("evm") {
} else if ext == Some("evm") || contents.is_ascii() {
parse_evm_dsl(utf8()?)
} else {
Err(eyre!("could not determine bytecode type"))
Expand Down
31 changes: 22 additions & 9 deletions crates/revmc-cli/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,8 @@

use clap::{Parser, ValueEnum};
use color_eyre::{eyre::eyre, Result};
use revm_primitives::{address, Bytes, Env, SpecId};
use revm_interpreter::{opcode::make_instruction_table, SharedMemory};
use revm_primitives::{address, spec_to_generic, Bytes, Env, SpecId};
use revmc::{eyre::ensure, EvmCompiler, EvmContext, EvmLlvmBackend, OptimizationLevel};
use revmc_cli::{get_benches, read_code, Bench};
use std::{
Expand Down Expand Up @@ -38,6 +39,10 @@ struct Cli {
#[arg(long)]
aot: bool,

/// Interpret the code instead of compiling.
#[arg(long, conflicts_with = "aot")]
interpret: bool,

/// Target triple.
#[arg(long, default_value = "native")]
target: String,
Expand Down Expand Up @@ -197,20 +202,28 @@ fn main() -> Result<()> {
unsafe { compiler.jit_function(f_id)? }
};

#[allow(unused_parens)]
let table = spec_to_generic!(spec_id, (const { &make_instruction_table::<_, SPEC>() }));
let mut run = |f: revmc::EvmCompilerFn| {
let mut interpreter =
revm_interpreter::Interpreter::new(contract.clone(), gas_limit, false);
host.clear();
let (mut ecx, stack, stack_len) =
EvmContext::from_interpreter_with_stack(&mut interpreter, &mut host);

for (i, input) in stack_input.iter().enumerate() {
stack.as_mut_slice()[i] = input.into();
}
*stack_len = stack_input.len();
if cli.interpret {
let action = interpreter.run(SharedMemory::new(), table, &mut host);
(interpreter.instruction_result, action)
} else {
let (mut ecx, stack, stack_len) =
EvmContext::from_interpreter_with_stack(&mut interpreter, &mut host);

let r = unsafe { f.call_noinline(Some(stack), Some(stack_len), &mut ecx) };
(r, interpreter.next_action)
for (i, input) in stack_input.iter().enumerate() {
stack.as_mut_slice()[i] = input.into();
}
*stack_len = stack_input.len();

let r = unsafe { f.call_noinline(Some(stack), Some(stack_len), &mut ecx) };
(r, interpreter.next_action)
}
};

if cli.n_iters == 0 {
Expand Down
5 changes: 5 additions & 0 deletions crates/revmc/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,8 @@ bitvec = "1"
rustc-hash.workspace = true
tracing.workspace = true

arbitrary = { version = "1.3", optional = true }

[dev-dependencies]
revmc-context = { workspace = true, features = ["host-ext-any"] }
paste.workspace = true
Expand All @@ -53,3 +55,6 @@ asm-keccak = ["alloy-primitives/asm-keccak"]
# I don't think this is supported, but it's necessary for --all-features to work in workspaces which
# also have this feature.
optimism = ["revm-primitives/optimism", "revm-interpreter/optimism"]

# Internal features.
__fuzzing = ["dep:arbitrary"]
6 changes: 2 additions & 4 deletions crates/revmc/src/bytecode/info.rs
Original file line number Diff line number Diff line change
Expand Up @@ -79,11 +79,9 @@ impl OpcodeInfo {
}

/// Returns the static info map for the given `SpecId`.
#[allow(unused_parens)]
pub const fn op_info_map(spec_id: SpecId) -> &'static [OpcodeInfo; 256] {
spec_to_generic!(spec_id, {
const MAP: &[OpcodeInfo; 256] = &make_map(<SPEC as revm_primitives::Spec>::SPEC_ID);
MAP
})
spec_to_generic!(spec_id, (const { &make_map(<SPEC as revm_primitives::Spec>::SPEC_ID) }))
}

#[allow(unused_mut)]
Expand Down
5 changes: 3 additions & 2 deletions crates/revmc/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,9 @@ pub use compiler::EvmCompiler;
mod linker;
pub use linker::Linker;

#[cfg(test)]
mod tests;
/// Internal tests and testing utilities. Not public API.
#[cfg(any(test, feature = "__fuzzing"))]
pub mod tests;

#[allow(ambiguous_glob_reexports)]
#[doc(inline)]
Expand Down
33 changes: 21 additions & 12 deletions crates/revmc/src/tests/mod.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,10 @@
#![allow(clippy::needless_update, unreachable_pub)]
#![allow(
clippy::needless_update,
unreachable_pub,
dead_code,
missing_docs,
missing_debug_implementations
)]

use crate::*;
use primitives::SpecId;
Expand All @@ -11,12 +17,15 @@ use revm_primitives::{hex, keccak256, Address, Bytes, LogData, B256, KECCAK_EMPT
#[macro_use]
mod macros;

mod fibonacci;
mod meta;

#[cfg(not(feature = "__fuzzing"))]
mod fibonacci;
#[cfg(not(feature = "__fuzzing"))]
mod resume;

mod runner;
use runner::*;
pub use runner::*;

const I256_MAX: U256 = U256::from_limbs([
0xFFFFFFFFFFFFFFFF,
Expand Down Expand Up @@ -677,7 +686,7 @@ tests! {
sstore1(@raw {
bytecode: &[op::PUSH1, 200, op::SLOAD, op::PUSH1, 100, op::PUSH1, 200, op::SSTORE, op::PUSH1, 200, op::SLOAD],
expected_stack: &[0_U256, 100_U256],
expected_gas: GAS_WHAT_THE_INTERPRETER_SAYS,
expected_gas: GAS_WHAT_INTERPRETER_SAYS,
assert_host: Some(|host| {
assert_eq!(host.storage.get(&200_U256), Some(&100_U256));
}),
Expand Down Expand Up @@ -754,7 +763,7 @@ tests! {
// NOTE: The address is pushed by the caller.
expected_stack: &[],
expected_memory: &0x69_U256.to_be_bytes::<32>(),
expected_gas: GAS_WHAT_THE_INTERPRETER_SAYS,
expected_gas: GAS_WHAT_INTERPRETER_SAYS,
expected_next_action: InterpreterAction::Create {
inputs: Box::new(CreateInputs {
caller: DEF_ADDR,
Expand All @@ -771,7 +780,7 @@ tests! {
// NOTE: The address is pushed by the caller.
expected_stack: &[],
expected_memory: &0x69_U256.to_be_bytes::<32>(),
expected_gas: GAS_WHAT_THE_INTERPRETER_SAYS,
expected_gas: GAS_WHAT_INTERPRETER_SAYS,
expected_next_action: InterpreterAction::Create {
inputs: Box::new(CreateInputs {
caller: DEF_ADDR,
Expand All @@ -797,7 +806,7 @@ tests! {
// NOTE: The return is pushed by the caller.
expected_stack: &[],
expected_memory: &[0; 32],
expected_gas: GAS_WHAT_THE_INTERPRETER_SAYS,
expected_gas: GAS_WHAT_INTERPRETER_SAYS,
expected_next_action: InterpreterAction::Call {
inputs: Box::new(CallInputs {
input: Bytes::copy_from_slice(&[0; 3]),
Expand Down Expand Up @@ -853,7 +862,7 @@ tests! {
selfdestruct(@raw {
bytecode: &[op::PUSH1, 0x69, op::SELFDESTRUCT, op::INVALID],
expected_return: InstructionResult::SelfDestruct,
expected_gas: GAS_WHAT_THE_INTERPRETER_SAYS,
expected_gas: GAS_WHAT_INTERPRETER_SAYS,
assert_host: Some(|host| {
assert_eq!(host.selfdestructs, [(DEF_ADDR, Address::with_last_byte(0x69))]);
}),
Expand All @@ -871,14 +880,14 @@ tests! {
ecx.contract.input = Bytes::from(&hex!("c0406226"));
}),
expected_return: InstructionResult::Return,
expected_stack: STACK_WHAT_THE_INTERPRETER_SAYS,
expected_gas: GAS_WHAT_THE_INTERPRETER_SAYS,
expected_memory: MEMORY_WHAT_THE_INTERPRETER_SAYS,
expected_stack: STACK_WHAT_INTERPRETER_SAYS,
expected_gas: GAS_WHAT_INTERPRETER_SAYS,
expected_memory: MEMORY_WHAT_INTERPRETER_SAYS,
expected_next_action: InterpreterAction::Return {
result: InterpreterResult {
result: InstructionResult::Return,
output: Bytes::copy_from_slice(&1_U256.to_be_bytes::<32>()),
gas: Gas::new(GAS_WHAT_THE_INTERPRETER_SAYS),
gas: Gas::new(GAS_WHAT_INTERPRETER_SAYS),
},
},
assert_host: Some(|host| {
Expand Down
Loading

0 comments on commit fecc46b

Please sign in to comment.