foundry_evm_fuzz/
inspector.rs

1use crate::{invariant::RandomCallGenerator, strategies::EvmFuzzState};
2use revm::{
3    interpreter::{CallInputs, CallOutcome, CallScheme, Interpreter},
4    Database, EvmContext, Inspector,
5};
6
7/// 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.
11    pub call_generator: Option<RandomCallGenerator>,
12    /// If set, it collects `stack` and `memory` values for fuzzing purposes.
13    pub collect: bool,
14    /// If `collect` is set, we store the collected values in this fuzz dictionary.
15    pub fuzz_state: EvmFuzzState,
16}
17
18impl<DB: Database> Inspector<DB> for Fuzzer {
19    #[inline]
20    fn step(&mut self, interp: &mut Interpreter, _context: &mut EvmContext<DB>) {
21        // We only collect `stack` and `memory` data before and after calls.
22        if self.collect {
23            self.collect_data(interp);
24            self.collect = false;
25        }
26    }
27
28    #[inline]
29    fn 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.
31        if self.call_generator.is_some() && ecx.env.tx.caller != inputs.caller {
32            self.override_call(inputs);
33        }
34
35        // We only collect `stack` and `memory` data before and after calls.
36        // this will be turned off on the next `step`
37        self.collect = true;
38
39        None
40    }
41
42    #[inline]
43    fn call_end(
44        &mut self,
45        _context: &mut EvmContext<DB>,
46        _inputs: &CallInputs,
47        outcome: CallOutcome,
48    ) -> CallOutcome {
49        if let Some(ref mut call_generator) = self.call_generator {
50            call_generator.used = false;
51        }
52
53        // We only collect `stack` and `memory` data before and after calls.
54        // this will be turned off on the next `step`
55        self.collect = true;
56
57        outcome
58    }
59}
60
61impl Fuzzer {
62    /// Collects `stack` and `memory` values into the fuzz dictionary.
63    fn collect_data(&mut self, interpreter: &Interpreter) {
64        self.fuzz_state.collect_values(interpreter.stack().data().iter().copied().map(Into::into));
65
66        // 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));
70
71        //     state.insert(slot);
72        // }
73    }
74
75    /// Overrides an external call and tries to call any method of msg.sender.
76    fn override_call(&mut self, call: &mut CallInputs) {
77        if let Some(ref mut call_generator) = self.call_generator {
78            // We only override external calls which are not coming from the test contract.
79            if 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.
84                if let Some(tx) = call_generator.next(call.caller, call.target_address) {
85                    *call.input = tx.call_details.calldata.0;
86                    call.caller = tx.sender;
87                    call.target_address = tx.call_details.target;
88
89                    // TODO: in what scenarios can the following be problematic
90                    call.bytecode_address = tx.call_details.target;
91                    call_generator.used = true;
92                }
93            }
94        }
95    }
96}