1use crate::{invariant::RandomCallGenerator, strategies::EvmFuzzState};
2use revm::{
3 interpreter::{CallInputs, CallOutcome, CallScheme, Interpreter},
4 Database, EvmContext, Inspector,
5};
67/// An inspector that can fuzz and collect data for that effect.
8#[derive(Clone, Debug)]
9pub struct Fuzzer {
10/// Given a strategy, it generates a random call.
11pub call_generator: Option<RandomCallGenerator>,
12/// If set, it collects `stack` and `memory` values for fuzzing purposes.
13pub collect: bool,
14/// If `collect` is set, we store the collected values in this fuzz dictionary.
15pub fuzz_state: EvmFuzzState,
16}
1718impl<DB: Database> Inspector<DB> for Fuzzer {
19#[inline]
20fn step(&mut self, interp: &mut Interpreter, _context: &mut EvmContext<DB>) {
21// We only collect `stack` and `memory` data before and after calls.
22if self.collect {
23self.collect_data(interp);
24self.collect = false;
25 }
26 }
2728#[inline]
29fn call(&mut self, ecx: &mut EvmContext<DB>, inputs: &mut CallInputs) -> Option<CallOutcome> {
30// We don't want to override the very first call made to the test contract.
31if self.call_generator.is_some() && ecx.env.tx.caller != inputs.caller {
32self.override_call(inputs);
33 }
3435// We only collect `stack` and `memory` data before and after calls.
36 // this will be turned off on the next `step`
37self.collect = true;
3839None40 }
4142#[inline]
43fn call_end(
44&mut self,
45 _context: &mut EvmContext<DB>,
46 _inputs: &CallInputs,
47 outcome: CallOutcome,
48 ) -> CallOutcome {
49if let Some(ref mut call_generator) = self.call_generator {
50call_generator.used = false;
51 }
5253// We only collect `stack` and `memory` data before and after calls.
54 // this will be turned off on the next `step`
55self.collect = true;
5657outcome58 }
59}
6061impl Fuzzer {
62/// Collects `stack` and `memory` values into the fuzz dictionary.
63fn collect_data(&mut self, interpreter: &Interpreter) {
64self.fuzz_state.collect_values(interpreter.stack().data().iter().copied().map(Into::into));
6566// TODO: disabled for now since it's flooding the dictionary
67 // for index in 0..interpreter.shared_memory.len() / 32 {
68 // let mut slot = [0u8; 32];
69 // slot.clone_from_slice(interpreter.shared_memory.get_slice(index * 32, 32));
7071 // state.insert(slot);
72 // }
73}
7475/// Overrides an external call and tries to call any method of msg.sender.
76fn override_call(&mut self, call: &mut CallInputs) {
77if let Some(ref mut call_generator) = self.call_generator {
78// We only override external calls which are not coming from the test contract.
79if call.caller != call_generator.test_address &&
80 call.scheme == CallScheme::Call &&
81 !call_generator.used
82 {
83// There's only a 30% chance that an override happens.
84if let Some(tx) = call_generator.next(call.caller, call.target_address) {
85*call.input = tx.call_details.calldata.0;
86call.caller = tx.sender;
87call.target_address = tx.call_details.target;
8889// TODO: in what scenarios can the following be problematic
90call.bytecode_address = tx.call_details.target;
91call_generator.used = true;
92 }
93 }
94 }
95 }
96}