From 47e25c32ca4befeb11fcbb78c34c26950282d916 Mon Sep 17 00:00:00 2001 From: Jacob Chia <61052369+jacob-chia@users.noreply.github.com> Date: Fri, 29 Sep 2023 09:38:22 +0800 Subject: [PATCH] refactor: upgrade rustc, libafl, and clap to the latest version (#201) * refactor: upgrade rustc, libafl, and clap to the latest version * Implement for Sha3WrappedFeedback and ConcolicFeedbackWrapper * Re-implement Stage for CoverageStage * resolve conflicts --------- Co-authored-by: 0xAWM <29773064+0xAWM@users.noreply.github.com> --- Cargo.toml | 3 +- cli/Cargo.toml | 4 +- rust-toolchain.toml | 2 +- src/evm/abi.rs | 7 +- src/evm/blaz/builder.rs | 8 +- src/evm/bytecode_analyzer.rs | 4 +- src/evm/concolic/concolic_host.rs | 14 ++- src/evm/concolic/concolic_stage.rs | 80 ++++++++----- src/evm/corpus_initializer.rs | 57 +++++---- src/evm/cov_stage.rs | 57 +++++---- src/evm/feedbacks.rs | 119 +++++++++++------- src/evm/host.rs | 83 +++++++------ src/evm/input.rs | 4 +- src/evm/middlewares/call_printer.rs | 37 +++--- src/evm/middlewares/coverage.rs | 32 ++--- src/evm/middlewares/middleware.rs | 30 +++-- src/evm/middlewares/sha3_bypass.rs | 25 ++-- src/evm/mutator.rs | 56 +++++---- src/evm/onchain/flashloan.rs | 49 ++++++-- src/evm/onchain/onchain.rs | 33 ++--- src/evm/oracle.rs | 3 +- src/evm/oracles/arb_call.rs | 8 +- src/evm/oracles/arb_transfer.rs | 4 +- src/evm/oracles/selfdestruct.rs | 2 +- src/evm/oracles/typed_bug.rs | 2 +- src/evm/presets/pair.rs | 10 +- src/evm/presets/presets.rs | 9 +- src/evm/types.rs | 15 ++- src/evm/vm.rs | 36 +++--- src/executor.rs | 58 +++++++-- src/feedback.rs | 92 +++++--------- src/fuzzer.rs | 179 +++++++++++++++------------- src/fuzzers/evm_fuzzer.rs | 40 ++++--- src/fuzzers/move_fuzzer.rs | 22 ++-- src/generic_vm/vm_state.rs | 1 + src/indexed_corpus.rs | 109 ++++++++++++++--- src/move/corpus_initializer.rs | 33 +++-- src/move/input.rs | 12 +- src/move/movevm.rs | 12 +- src/move/mutator.rs | 46 +++---- src/move/oracles/typed_bug.rs | 11 +- src/move/scheduler.rs | 101 +++++++++------- src/move/vm_state.rs | 17 +-- src/mutation_utils.rs | 15 +-- src/oracle.rs | 8 +- src/scheduler.rs | 122 ++++++++++--------- src/state.rs | 142 ++++++++++++++++++---- src/tracer.rs | 4 +- 48 files changed, 1095 insertions(+), 722 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index cc13a9e4b..923c82f2c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -45,7 +45,8 @@ revm-primitives = { path = "./externals/revm/crates/primitives", features = ["no revm-interpreter = { path = "./externals/revm/crates/interpreter", features = ["no_gas_measuring", "serde"] } hex = "0.4" primitive-types = { version = "0.12.1", features = ["rlp", "serde"] } -libafl = "0.8.2" +libafl = "0.11.1" +libafl_bolts = "0.11.1" rand = "0.8.5" nix = "0.24" serde = "1.0.147" diff --git a/cli/Cargo.toml b/cli/Cargo.toml index ed115e368..e51c0e46c 100644 --- a/cli/Cargo.toml +++ b/cli/Cargo.toml @@ -7,7 +7,7 @@ edition = "2021" [dependencies] ityfuzz = {path = "../", version = "0.1.0"} -clap = {version = "=4.0.18", features = ["derive"]} +clap = {version = "4.4.4", features = ["derive"]} primitive-types = { version = "0.12.1", features = ["rlp", "serde"] } sentry = "0.29.1" reqwest = "0.11.6" @@ -15,4 +15,4 @@ serde_json = "1.0.73" serde = "1.0.147" rlp = "0.5.2" hex = "0.4" -ethers = "2.0.7" \ No newline at end of file +ethers = "2.0.7" diff --git a/rust-toolchain.toml b/rust-toolchain.toml index c1a53e726..ebc0b6c28 100644 --- a/rust-toolchain.toml +++ b/rust-toolchain.toml @@ -1,2 +1,2 @@ [toolchain] -channel = "nightly-2023-04-09" +channel = "nightly-2023-09-09" diff --git a/src/evm/abi.rs b/src/evm/abi.rs index f1168456a..d0f0e2ba6 100644 --- a/src/evm/abi.rs +++ b/src/evm/abi.rs @@ -7,10 +7,11 @@ use crate::input::ConciseSerde; use crate::mutation_utils::{byte_mutator, byte_mutator_with_expansion}; use crate::state::{HasCaller, HasItyState}; use itertools::Itertools; -use libafl::impl_serdeany; +use libafl_bolts::impl_serdeany; use libafl::inputs::{HasBytesVec, Input}; use libafl::mutators::MutationResult; -use libafl::prelude::{HasMetadata, Rand}; +use libafl::prelude::HasMetadata; +use libafl_bolts::bolts_prelude::Rand; use libafl::state::{HasMaxSize, HasRand, State}; use once_cell::sync::Lazy; use serde::de::DeserializeOwned; @@ -87,7 +88,7 @@ impl ABIAddressToInstanceMap { pub fn register_abi_instance(address: EVMAddress, abi: BoxedABI, state: &mut S) { let abi_map = state - .metadata_mut() + .metadata_map_mut() .get_mut::() .expect("ABIAddressToInstanceMap not found"); abi_map.add(address, abi); diff --git a/src/evm/blaz/builder.rs b/src/evm/blaz/builder.rs index afe89767e..ffa92e9b7 100644 --- a/src/evm/blaz/builder.rs +++ b/src/evm/blaz/builder.rs @@ -10,7 +10,7 @@ use std::time::Duration; use std::collections::hash_map::DefaultHasher; use bytes::Bytes; use itertools::Itertools; -use libafl::impl_serdeany; +use libafl_bolts::impl_serdeany; use serde::{Deserialize, Serialize}; use serde_json::Value; use crate::cache::{Cache, FileSystemCache}; @@ -19,7 +19,7 @@ use crate::evm::host::FuzzHost; use crate::evm::input::{ConciseEVMInput, EVMInput}; use crate::evm::onchain::endpoints::Chain; use crate::evm::srcmap::parser::{decode_instructions, decode_instructions_with_replacement, SourceMapLocation}; -use crate::evm::types::{EVMAddress, EVMFuzzState, ProjectSourceMapTy}; +use crate::evm::types::{EVMAddress, EVMFuzzState, ProjectSourceMapTy, EVMQueueExecutor}; use crate::evm::vm::{EVMExecutor, EVMState}; use crate::generic_vm::vm_executor::GenericVM; @@ -257,7 +257,7 @@ impl BuildJobResult { let bytecode = Vec::from((**executor) .borrow_mut() .as_any() - .downcast_ref::>() + .downcast_ref::() .unwrap() .host .code @@ -330,4 +330,4 @@ mod tests { // let result = job.wait_build_job().expect("wait build job failed"); // println!("{:?}", result.abi); // } -} \ No newline at end of file +} diff --git a/src/evm/bytecode_analyzer.rs b/src/evm/bytecode_analyzer.rs index 193d96bff..e43727928 100644 --- a/src/evm/bytecode_analyzer.rs +++ b/src/evm/bytecode_analyzer.rs @@ -49,7 +49,7 @@ where S: HasMetadata + State, { let constants = find_constants(bytecode); - match state.metadata_mut().get_mut::() { + match state.metadata_map_mut().get_mut::() { Some(meta) => { for constant in constants { if !meta.constants.contains(&constant) { @@ -58,7 +58,7 @@ where } } None => { - state.metadata_mut().insert(ConstantPoolMetadata { + state.metadata_map_mut().insert(ConstantPoolMetadata { constants: constants.into_iter().collect(), }); } diff --git a/src/evm/concolic/concolic_host.rs b/src/evm/concolic/concolic_host.rs index 7ab9b066e..f96d9ff79 100644 --- a/src/evm/concolic/concolic_host.rs +++ b/src/evm/concolic/concolic_host.rs @@ -1,4 +1,5 @@ use bytes::Bytes; +use libafl::schedulers::Scheduler; use crate::evm::abi::BoxedABI; use crate::evm::input::{ConciseEVMInput, EVMInput, EVMInputT, EVMInputTy}; @@ -682,23 +683,24 @@ fn str_to_bytes(s: &str) -> Vec { bytes } -impl Middleware for ConcolicHost +impl Middleware for ConcolicHost where I: Input + VMInputT + EVMInputT + 'static, VS: VMStateT, S: State + HasCaller - + HasCorpus + + HasCorpus + HasItyState + HasMetadata + HasCurrentInputIdx + Debug + Clone, + SC: Scheduler + Clone, { unsafe fn on_step( &mut self, interp: &mut Interpreter, - host: &mut FuzzHost, + host: &mut FuzzHost, state: &mut S, ) { macro_rules! fast_peek { @@ -1313,7 +1315,7 @@ where // .clone(); if solutions.len() > 0 { - let meta = state.metadata_mut().get_mut::().expect("Failed to get metadata"); + let meta = state.metadata_map_mut().get_mut::().expect("Failed to get metadata"); for solution in solutions { meta.solutions.push((solution, self.testcase_ref.clone())); } @@ -1324,14 +1326,14 @@ where unsafe fn on_return( &mut self, interp: &mut Interpreter, - host: &mut FuzzHost, + host: &mut FuzzHost, state: &mut S, by: &Bytes ) { self.pop_ctx(); } - unsafe fn on_insert(&mut self, bytecode: &mut Bytecode, address: EVMAddress, host: &mut FuzzHost, state: &mut S) { + unsafe fn on_insert(&mut self, bytecode: &mut Bytecode, address: EVMAddress, host: &mut FuzzHost, state: &mut S) { } diff --git a/src/evm/concolic/concolic_stage.rs b/src/evm/concolic/concolic_stage.rs index 4e4f65f00..353b30989 100644 --- a/src/evm/concolic/concolic_stage.rs +++ b/src/evm/concolic/concolic_stage.rs @@ -3,20 +3,21 @@ use std::fmt::Debug; use std::ops::Deref; use std::rc::Rc; use std::sync::Arc; -use libafl::{Error, Evaluator, Fuzzer, impl_serdeany}; +use libafl::{Error, Evaluator, Fuzzer}; +use libafl_bolts::{impl_serdeany, Named}; use libafl::corpus::{Corpus, Testcase}; use libafl::events::{EventFirer, ProgressReporter}; use libafl::executors::ExitKind; use libafl::feedbacks::Feedback; use libafl::inputs::Input; -use libafl::prelude::{HasClientPerfMonitor, HasMetadata, Named, ObserversTuple, Stage}; -use libafl::state::HasCorpus; +use libafl::prelude::{HasClientPerfMonitor, HasMetadata, ObserversTuple, Stage, CorpusId, UsesInput}; +use libafl::state::{HasCorpus, UsesState}; use revm_primitives::HashSet; use serde::{Deserialize, Serialize}; use crate::evm::concolic::concolic_host::{ConcolicHost, Field, Solution}; use crate::evm::input::{ConciseEVMInput, EVMInput, EVMInputT}; use crate::evm::middlewares::middleware::MiddlewareType; -use crate::evm::types::{EVMFuzzExecutor, EVMFuzzState}; +use crate::evm::types::{EVMFuzzExecutor, EVMFuzzState, EVMQueueExecutor}; use crate::evm::vm::{EVMExecutor, EVMState}; use crate::executor::FuzzExecutor; use crate::generic_vm::vm_executor::GenericVM; @@ -27,14 +28,18 @@ pub struct ConcolicStage { pub enabled: bool, pub allow_symbolic_addresses: bool, pub known_state_input: HashSet<(usize, usize)>, - pub vm_executor: Rc>>, - pub phantom: std::marker::PhantomData<(OT)>, + pub vm_executor: Rc>, + pub phantom: std::marker::PhantomData, +} + +impl UsesState for ConcolicStage { + type State = EVMFuzzState; } impl ConcolicStage { pub fn new(enabled: bool, allow_symbolic_addresses: bool, - vm_executor: Rc>>) -> Self { + vm_executor: Rc>) -> Self { Self { enabled, allow_symbolic_addresses, @@ -54,31 +59,32 @@ pub struct ConcolicPrioritizationMetadata { impl_serdeany!(ConcolicPrioritizationMetadata); -impl Stage, EM, EVMFuzzState, Z> for ConcolicStage -where Z: Evaluator, EM, EVMInput, EVMFuzzState>, - EM: ProgressReporter, - OT: ObserversTuple +impl Stage, EM, Z> for ConcolicStage +where + Z: Evaluator, EM, State = Self::State>, + EM: ProgressReporter + UsesState, + OT: ObserversTuple, { fn perform(&mut self, fuzzer: &mut Z, executor: &mut EVMFuzzExecutor, - state: &mut EVMFuzzState, + state: &mut Self::State, manager: &mut EM, - corpus_idx: usize + corpus_idx: CorpusId, ) -> Result<(), Error> { if !self.enabled { return Ok(()); } - if !state.metadata().contains::() { - state.metadata_mut().insert(ConcolicPrioritizationMetadata { + if !state.metadata_map().contains::() { + state.metadata_map_mut().insert(ConcolicPrioritizationMetadata { interesting_idx: Default::default(), solutions: vec![], }); } let meta = state - .metadata() + .metadata_map() .get::() .unwrap() .clone(); @@ -87,7 +93,7 @@ where Z: Evaluator, EM, EVMInput, EVMFuzzState>, println!("Running concolic execution on testcase #{}", idx); let testcase = state.corpus() - .get(*idx).unwrap() + .get((*idx).into()).unwrap() .borrow() .input() .clone() @@ -117,7 +123,7 @@ where Z: Evaluator, EM, EVMInput, EVMFuzzState>, } { - let mut metadata = state.metadata_mut().get_mut::().unwrap(); + let mut metadata = state.metadata_map_mut().get_mut::().unwrap(); metadata.interesting_idx.clear(); let mut testcases = vec![]; @@ -177,17 +183,33 @@ impl Named for ConcolicFeedbackWrapper { } } -impl Feedback for ConcolicFeedbackWrapper -where F: Feedback + Named + Debug, - I: Input, - S: HasClientPerfMonitor + HasMetadata + HasCorpus,{ - fn is_interesting(&mut self, state: &mut S, manager: &mut EM, input: &I, observers: &OT, exit_kind: &ExitKind) -> Result where EM: EventFirer, OT: ObserversTuple { +impl Feedback for ConcolicFeedbackWrapper +where + I: Input, + F: Feedback + Named + Debug, + S: HasClientPerfMonitor + HasMetadata + HasCorpus + UsesInput, +{ + fn is_interesting(&mut self, state: &mut S, manager: &mut EM, input: &S::Input, observers: &OT, exit_kind: &ExitKind) -> Result + where + EM: EventFirer, + OT: ObserversTuple, + { self.inner.is_interesting(state, manager, input, observers, exit_kind) } - fn append_metadata(&mut self, state: &mut S, _testcase: &mut Testcase) -> Result<(), Error> { - if !state.metadata().contains::() { - state.metadata_mut().insert(ConcolicPrioritizationMetadata { + #[inline] + #[allow(unused_variables)] + fn append_metadata( + &mut self, + state: &mut S, + observers: &OT, + testcase: &mut Testcase, + ) -> Result<(), Error> + where + OT: ObserversTuple, + { + if !state.metadata_map().contains::() { + state.metadata_map_mut().insert(ConcolicPrioritizationMetadata { interesting_idx: Default::default(), solutions: vec![], }); @@ -195,12 +217,12 @@ where F: Feedback + Named + Debug, let idx = state.corpus().count(); let mut meta = state - .metadata_mut() + .metadata_map_mut() .get_mut::() .unwrap(); meta.interesting_idx.push(idx); - Ok(()) + + self.inner.append_metadata(state, observers, testcase) } } - diff --git a/src/evm/corpus_initializer.rs b/src/evm/corpus_initializer.rs index 61686a20b..f721e473a 100644 --- a/src/evm/corpus_initializer.rs +++ b/src/evm/corpus_initializer.rs @@ -31,10 +31,12 @@ use crate::fuzzer::REPLAY; use crate::input::ConciseSerde; use hex; use itertools::Itertools; -use libafl::impl_serdeany; + +use crypto::sha3::Sha3Mode::Keccak256; use libafl::prelude::HasMetadata; use libafl::schedulers::Scheduler; use libafl::state::HasCorpus; +use libafl_bolts::impl_serdeany; use revm_primitives::Bytecode; use serde::{Deserialize, Serialize}; use std::cell::RefCell; @@ -48,13 +50,17 @@ use std::time::Duration; pub const INITIAL_BALANCE: u128 = 100_000_000_000_000_000_000; // 100 ether -pub struct EVMCorpusInitializer<'a> { - executor: &'a mut EVMExecutor, - scheduler: &'a dyn Scheduler, - infant_scheduler: &'a dyn Scheduler, +pub struct EVMCorpusInitializer<'a, SC, ISC> +where + SC: Scheduler + Clone, + ISC: Scheduler, +{ + executor: &'a mut EVMExecutor, + scheduler: SC, + infant_scheduler: ISC, state: &'a mut EVMFuzzState, #[cfg(feature = "use_presets")] - presets: Vec<&'a dyn Preset>, + presets: Vec<&'a dyn Preset>, work_dir: String, } @@ -100,7 +106,9 @@ macro_rules! handle_contract_insertion { None => (false, false), }; if is_erc20 { - register_borrow_txn(&$host, $state, $deployed_address); + // scheduler should be mutable but host cannot be borrowed as mutable + let scheduler = $host.scheduler.clone(); + register_borrow_txn(scheduler, $state, $deployed_address); } if is_pair { let mut mid = $host @@ -133,11 +141,15 @@ macro_rules! add_input_to_corpus { }; } -impl<'a> EVMCorpusInitializer<'a> { +impl<'a, SC, ISC> EVMCorpusInitializer<'a, SC, ISC> +where + SC: Scheduler + Clone + 'static, + ISC: Scheduler, +{ pub fn new( - executor: &'a mut EVMExecutor, - scheduler: &'a dyn Scheduler, - infant_scheduler: &'a dyn Scheduler, + executor: &'a mut EVMExecutor, + scheduler: SC, + infant_scheduler: ISC, state: &'a mut EVMFuzzState, work_dir: String, ) -> Self { @@ -153,12 +165,15 @@ impl<'a> EVMCorpusInitializer<'a> { } #[cfg(feature = "use_presets")] - pub fn register_preset(&mut self, preset: &'a dyn Preset) { + pub fn register_preset( + &mut self, + preset: &'a dyn Preset, + ) { self.presets.push(preset); } pub fn initialize(&mut self, loader: &mut ContractLoader) -> EVMInitializationArtifacts { - self.state.metadata_mut().insert(ABIMap::new()); + self.state.metadata_map_mut().insert(ABIMap::new()); self.setup_default_callers(); self.setup_contract_callers(); self.initialize_contract(loader); @@ -228,7 +243,7 @@ impl<'a> EVMCorpusInitializer<'a> { let sigs = extract_sig_from_contract(&contract_code); let mut unknown_sigs: usize = 0; for sig in &sigs { - if let Some(abi) = self.state.metadata().get::().unwrap().get(sig) { + if let Some(abi) = self.state.metadata_map().get::().unwrap().get(sig) { contract.abi.push(abi.clone()); } else { unknown_sigs += 1; @@ -242,7 +257,7 @@ impl<'a> EVMCorpusInitializer<'a> { .map(|abi| { if let Some(known_abi) = self .state - .metadata() + .metadata_map() .get::() .unwrap() .get(&abi.function) @@ -314,12 +329,7 @@ impl<'a> EVMCorpusInitializer<'a> { } for abi in contract.abi.clone() { - self.add_abi( - &abi, - self.scheduler, - contract.deployed_address, - &mut artifacts, - ); + self.add_abi(&abi, contract.deployed_address, &mut artifacts); } // add transfer txn { @@ -400,7 +410,6 @@ impl<'a> EVMCorpusInitializer<'a> { fn add_abi( &mut self, abi: &ABIConfig, - scheduler: &dyn Scheduler, deployed_address: EVMAddress, artifacts: &mut EVMInitializationArtifacts, ) { @@ -456,7 +465,7 @@ impl<'a> EVMCorpusInitializer<'a> { randomness: vec![0], repeat: 1, }; - add_input_to_corpus!(self.state, scheduler, input.clone()); + add_input_to_corpus!(self.state, &mut self.scheduler, input.clone()); #[cfg(feature = "print_txn_corpus")] { let corpus_dir = format!("{}/corpus", self.work_dir.as_str()); @@ -468,7 +477,7 @@ impl<'a> EVMCorpusInitializer<'a> { for p in presets { let mut presets = p.presets(abi.function, &input, self.executor); presets.iter().for_each(|preset| { - add_input_to_corpus!(self.state, scheduler, preset.clone()); + add_input_to_corpus!(self.state, &mut self.scheduler, preset.clone()); }); } } diff --git a/src/evm/cov_stage.rs b/src/evm/cov_stage.rs index 5d3989a03..412fa7438 100644 --- a/src/evm/cov_stage.rs +++ b/src/evm/cov_stage.rs @@ -6,14 +6,15 @@ use std::ops::Deref; use std::rc::Rc; use std::sync::Arc; use itertools::Itertools; -use libafl::{Error, Evaluator, Fuzzer, impl_serdeany}; +use libafl::{Error, Evaluator, Fuzzer}; +use libafl_bolts::{impl_serdeany, Named}; use libafl::corpus::{Corpus, Testcase}; use libafl::events::{EventFirer, ProgressReporter}; use libafl::executors::ExitKind; use libafl::feedbacks::Feedback; use libafl::inputs::Input; -use libafl::prelude::{HasClientPerfMonitor, HasMetadata, Named, ObserversTuple, Stage}; -use libafl::state::HasCorpus; +use libafl::prelude::{HasClientPerfMonitor, HasMetadata, ObserversTuple, Stage, CorpusId}; +use libafl::state::{HasCorpus, UsesState}; use revm_primitives::HashSet; use serde::{Deserialize, Serialize}; use crate::evm::concolic::concolic_host::{ConcolicHost, Field, Solution}; @@ -22,7 +23,7 @@ use crate::evm::input::{ConciseEVMInput, EVMInput, EVMInputT}; use crate::evm::middlewares::call_printer::CallPrinter; use crate::evm::middlewares::coverage::{Coverage, EVAL_COVERAGE}; use crate::evm::middlewares::middleware::MiddlewareType; -use crate::evm::types::{EVMFuzzExecutor, EVMFuzzState, EVMStagedVMState}; +use crate::evm::types::{EVMFuzzExecutor, EVMFuzzState, EVMStagedVMState, EVMQueueExecutor}; use crate::evm::vm::{EVMExecutor, EVMState}; use crate::executor::FuzzExecutor; use crate::generic_vm::vm_executor::GenericVM; @@ -33,16 +34,20 @@ use crate::state::HasInfantStateState; pub struct CoverageStage { pub last_corpus_idx: usize, - executor: Rc>>, + executor: Rc>, coverage: Rc>, call_printer: Rc>, trace_dir: String, - pub phantom: std::marker::PhantomData<(OT)>, + pub phantom: std::marker::PhantomData, +} + +impl UsesState for CoverageStage { + type State = EVMFuzzState; } impl CoverageStage { pub fn new( - executor: Rc>>, + executor: Rc>, coverage: Rc>, call_printer: Rc>, work_dir: String, @@ -63,7 +68,7 @@ impl CoverageStage { fn get_call_seq(vm_state: &EVMStagedVMState, state: &mut EVMFuzzState) -> Vec<(EVMInput, u32)> { if let Some(from_idx) = vm_state.trace.from_idx { - let corpus_item = state.get_infant_state_state().corpus().get(from_idx); + let corpus_item = state.get_infant_state_state().corpus().get(from_idx.into()); // This happens when full_trace feature is not enabled, the corpus item may be discarded if corpus_item.is_err() { return vec![]; @@ -93,28 +98,34 @@ impl CoverageStage { } } -impl Stage, EM, EVMFuzzState, Z> for CoverageStage - where Z: Evaluator, EM, EVMInput, EVMFuzzState>, - EM: ProgressReporter, - OT: ObserversTuple +impl Stage, EM, Z> for CoverageStage +where + Z: Evaluator, EM, State = Self::State>, + EM: ProgressReporter + UsesState, + OT: ObserversTuple, { fn perform(&mut self, fuzzer: &mut Z, executor: &mut EVMFuzzExecutor, - state: &mut EVMFuzzState, + state: &mut Self::State, manager: &mut EM, - corpus_idx: usize + corpus_idx: CorpusId, ) -> Result<(), Error> { - let total = state.corpus().count(); - if self.last_corpus_idx == total { + let last_idx = state.corpus().last(); + if last_idx.is_none() { + return Ok(()); + } + let last_idx = last_idx.unwrap().into(); + if self.last_corpus_idx == last_idx { return Ok(()); } let mut exec = self.executor.deref().borrow_mut(); exec.host.add_middlewares(self.call_printer.clone()); - let meta = state.metadata().get::().unwrap().clone(); - for i in self.last_corpus_idx..total { + let meta = state.metadata_map().get::().unwrap().clone(); + let mut current_idx = CorpusId::from(self.last_corpus_idx); + while let Some(i) = state.corpus().next(current_idx) { self.call_printer.deref().borrow_mut().cleanup(); let testcase = state.corpus().get(i).unwrap().borrow().clone(); let last_input = testcase.input().as_ref().expect("Input should be present"); @@ -145,22 +156,24 @@ impl Stage, EM, EVMFuzzState, Z> for CoverageStag } self.call_printer.deref().borrow_mut().save_trace(format!("{}/{}", self.trace_dir, i).as_str()); - if let Some(bug_idx) = meta.corpus_idx_to_bug.get(&i) { + if let Some(bug_idx) = meta.corpus_idx_to_bug.get(&i.into()) { for id in bug_idx { fs::copy(format!("{}/{}.json", self.trace_dir, i), format!("{}/bug_{}.json", self.trace_dir, id)).unwrap(); } } unsafe { EVAL_COVERAGE = false; } + + current_idx = i; } + exec.host.remove_middlewares_by_ty(&MiddlewareType::CallPrinter); - if self.last_corpus_idx == total { + if self.last_corpus_idx == last_idx { return Ok(()); } self.coverage.deref().borrow_mut().record_instruction_coverage(); - self.last_corpus_idx = total; + self.last_corpus_idx = last_idx; Ok(()) } } - diff --git a/src/evm/feedbacks.rs b/src/evm/feedbacks.rs index a900d6c17..a0628a6c5 100644 --- a/src/evm/feedbacks.rs +++ b/src/evm/feedbacks.rs @@ -1,17 +1,18 @@ use std::borrow::BorrowMut; use std::cell::RefCell; use std::fmt::{Debug, Formatter}; -use std::ops::{Deref, DerefMut}; +use std::ops::Deref; use std::rc::Rc; use libafl::Error; use libafl::events::EventFirer; use libafl::executors::ExitKind; use libafl::feedbacks::Feedback; -use libafl::inputs::Input; use libafl::observers::ObserversTuple; -use libafl::prelude::{HasCorpus, HasMetadata, HasRand, Named, State}; +use libafl::prelude::{HasCorpus, HasMetadata, HasRand, State, UsesInput, Input, Testcase}; +use libafl::schedulers::Scheduler; +use libafl_bolts::Named; use libafl::state::HasClientPerfMonitor; -use crate::evm::input::{ConciseEVMInput, EVMInput, EVMInputT}; +use crate::evm::input::{ConciseEVMInput, EVMInputT}; use crate::evm::middlewares::sha3_bypass::Sha3TaintAnalysis; use crate::evm::types::EVMAddress; use crate::evm::vm::EVMExecutor; @@ -21,43 +22,46 @@ use crate::state::{HasCaller, HasCurrentInputIdx, HasItyState}; /// A wrapper around a feedback that also performs sha3 taint analysis /// when the feedback is interesting. -pub struct Sha3WrappedFeedback - where S: State + HasCaller + Debug + Clone + HasClientPerfMonitor + 'static, - I: VMInputT + EVMInputT, - VS: VMStateT, - F: Feedback +pub struct Sha3WrappedFeedback +where + S: State + HasCorpus + HasCaller + Debug + Clone + HasClientPerfMonitor + 'static, + I: VMInputT + EVMInputT, + VS: VMStateT, + F: Feedback, + SC: Scheduler + Clone, { pub inner_feedback: Box, pub sha3_taints: Rc>, - pub evm_executor: Rc>>, + pub evm_executor: Rc>>, pub enabled: bool, } - - -impl Feedback for Sha3WrappedFeedback -where S: State + HasRand - + HasCorpus - + HasItyState - + HasMetadata - + HasCaller - + HasCurrentInputIdx - + HasClientPerfMonitor - + Default - + Clone - + Debug - + 'static, - I: VMInputT + EVMInputT + 'static, - VS: VMStateT + 'static, - F: Feedback +impl Feedback for Sha3WrappedFeedback +where + S: State + HasRand + + HasCorpus + + HasItyState + + HasMetadata + + HasCaller + + HasCurrentInputIdx + + HasClientPerfMonitor + + Default + + Clone + + Debug + + UsesInput + + 'static, + I: VMInputT + EVMInputT + 'static, + VS: VMStateT + 'static, + F: Feedback, + SC: Scheduler + Clone + 'static, { fn is_interesting(&mut self, state: &mut S, manager: &mut EM, - input: &I, + input: &S::Input, observers: &OT, exit_kind: &ExitKind) - -> Result where EM: EventFirer, OT: ObserversTuple { + -> Result where EM: EventFirer, OT: ObserversTuple { // checks if the inner feedback is interesting if self.enabled { match self.inner_feedback.is_interesting(state, manager, input, observers, exit_kind) { @@ -81,17 +85,34 @@ where S: State + HasRand self.inner_feedback.is_interesting(state, manager, input, observers, exit_kind) } } + + #[inline] + #[allow(unused_variables)] + fn append_metadata( + &mut self, + state: &mut S, + observers: &OT, + testcase: &mut Testcase, + ) -> Result<(), Error> + where + OT: ObserversTuple, + { + self.inner_feedback.as_mut().append_metadata(state, observers, testcase) + } } -impl Sha3WrappedFeedback - where S: State + HasCaller + Debug + Clone + HasClientPerfMonitor + 'static, - I: VMInputT + EVMInputT, - VS: VMStateT, - F: Feedback{ +impl Sha3WrappedFeedback +where + S: State + HasCorpus + HasCaller + Debug + Clone + HasClientPerfMonitor + 'static, + I: VMInputT + EVMInputT, + VS: VMStateT, + F: Feedback, + SC: Scheduler + Clone, +{ pub(crate) fn new(inner_feedback: F, sha3_taints: Rc>, - evm_executor: Rc>>, + evm_executor: Rc>>, enabled: bool ) -> Self { Self { @@ -103,22 +124,28 @@ impl Sha3WrappedFeedback } } -impl Named for Sha3WrappedFeedback - where S: State + HasCaller + Debug + Clone + HasClientPerfMonitor + 'static, - I: VMInputT + EVMInputT, - VS: VMStateT, - F: Feedback{ +impl Named for Sha3WrappedFeedback +where + S: State + HasCorpus + HasCaller + Debug + Clone + HasClientPerfMonitor + 'static, + I: VMInputT + EVMInputT, + VS: VMStateT, + F: Feedback, + SC: Scheduler + Clone, +{ fn name(&self) -> &str { todo!() } } -impl Debug for Sha3WrappedFeedback - where S: State + HasCaller + Debug + Clone + HasClientPerfMonitor + 'static, - I: VMInputT + EVMInputT, - VS: VMStateT, - F: Feedback { +impl Debug for Sha3WrappedFeedback +where + S: State + HasCorpus + HasCaller + Debug + Clone + HasClientPerfMonitor + 'static, + I: VMInputT + EVMInputT, + VS: VMStateT, + F: Feedback, + SC: Scheduler + Clone, +{ fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), std::fmt::Error> { todo!() } -} \ No newline at end of file +} diff --git a/src/evm/host.rs b/src/evm/host.rs index e7ee61e68..74ee802fb 100644 --- a/src/evm/host.rs +++ b/src/evm/host.rs @@ -8,7 +8,7 @@ use crate::evm::onchain::flashloan::register_borrow_txn; use crate::evm::onchain::flashloan::Flashloan; use bytes::Bytes; use itertools::Itertools; -use libafl::prelude::{HasCorpus, HasMetadata, HasRand, Scheduler}; +use libafl::prelude::{HasCorpus, HasMetadata, HasRand, Scheduler, UsesInput}; use libafl::state::State; use revm_interpreter::InstructionResult::{Continue, ControlLeak, Revert}; @@ -97,11 +97,12 @@ pub fn is_precompile(address: EVMAddress, num_of_precompiles: usize) -> bool { num.wrapping_sub(1) < num_of_precompiles as u16 } -pub struct FuzzHost +pub struct FuzzHost where - S: State + HasCaller + Debug + Clone + 'static, + S: State + HasCorpus + HasCaller + Debug + Clone + 'static, I: VMInputT + EVMInputT, VS: VMStateT, + SC: Scheduler + Clone, { pub evmstate: EVMState, // these are internal to the host @@ -114,7 +115,7 @@ where pub pc_to_create: HashMap<(EVMAddress, usize), usize>, pub pc_to_call_hash: HashMap<(EVMAddress, usize, usize), HashSet>>, pub middlewares_enabled: bool, - pub middlewares: Rc>>>>>, + pub middlewares: Rc>>>>>, pub coverage_changed: bool, @@ -122,7 +123,7 @@ where pub middlewares_latent_call_actions: Vec, - pub scheduler: Arc>, + pub scheduler: SC, // controlled by onchain module, if sload cant find the slot, use this value pub next_slot: EVMU256, @@ -164,11 +165,12 @@ where pub jumpi_trace: usize, } -impl Debug for FuzzHost +impl Debug for FuzzHost where - S: State + HasCaller + Debug + Clone + 'static, + S: State + HasCorpus + HasCaller + Debug + Clone + 'static, I: VMInputT + EVMInputT, VS: VMStateT, + SC: Scheduler + Clone, { fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { f.debug_struct("FuzzHost") @@ -190,11 +192,12 @@ where } // all clones would not include middlewares and states -impl Clone for FuzzHost +impl Clone for FuzzHost where - S: State + HasCaller + Debug + Clone + 'static, + S: State + HasCorpus + HasCaller + Debug + Clone + 'static, I: VMInputT + EVMInputT, VS: VMStateT, + SC: Scheduler + Clone, { fn clone(&self) -> Self { Self { @@ -246,22 +249,23 @@ const UNBOUND_CALL_THRESHOLD: usize = 3; // if a PC transfers control to >2 addresses, we consider call at this PC to be unbounded const CONTROL_LEAK_THRESHOLD: usize = 2; -impl FuzzHost +impl FuzzHost where S: State + HasRand + HasCaller + Debug + Clone - + HasCorpus + + HasCorpus + HasMetadata + HasItyState + + UsesInput + 'static, I: VMInputT + EVMInputT + 'static, VS: VMStateT, + SC: Scheduler + Clone, { - pub fn new(scheduler: Arc>, workdir: String) -> Self { - // ret.env.block.timestamp = EVMU256::max_value(); + pub fn new(scheduler: SC, workdir: String) -> Self { Self { evmstate: EVMState::new(), env: Env::default(), @@ -308,37 +312,43 @@ where /// custom spec id run_inspect pub fn run_inspect(&mut self, interp: &mut Interpreter, state: &mut S) -> InstructionResult { match self.spec_id { - SpecId::LATEST => interp.run_inspect::, LatestSpec>(self, state), + SpecId::LATEST => { + interp.run_inspect::, LatestSpec>(self, state) + } SpecId::FRONTIER => { - interp.run_inspect::, FrontierSpec>(self, state) + interp.run_inspect::, FrontierSpec>(self, state) } SpecId::HOMESTEAD => { - interp.run_inspect::, HomesteadSpec>(self, state) + interp.run_inspect::, HomesteadSpec>(self, state) } SpecId::TANGERINE => { - interp.run_inspect::, TangerineSpec>(self, state) + interp.run_inspect::, TangerineSpec>(self, state) } SpecId::SPURIOUS_DRAGON => { - interp.run_inspect::, SpuriousDragonSpec>(self, state) + interp.run_inspect::, SpuriousDragonSpec>(self, state) } SpecId::BYZANTIUM => { - interp.run_inspect::, ByzantiumSpec>(self, state) + interp.run_inspect::, ByzantiumSpec>(self, state) } SpecId::CONSTANTINOPLE | SpecId::PETERSBURG => { - interp.run_inspect::, PetersburgSpec>(self, state) + interp.run_inspect::, PetersburgSpec>(self, state) } SpecId::ISTANBUL => { - interp.run_inspect::, IstanbulSpec>(self, state) + interp.run_inspect::, IstanbulSpec>(self, state) } SpecId::MUIR_GLACIER | SpecId::BERLIN => { - interp.run_inspect::, BerlinSpec>(self, state) + interp.run_inspect::, BerlinSpec>(self, state) + } + SpecId::LONDON => { + interp.run_inspect::, LondonSpec>(self, state) + } + SpecId::MERGE => { + interp.run_inspect::, MergeSpec>(self, state) } - SpecId::LONDON => interp.run_inspect::, LondonSpec>(self, state), - SpecId::MERGE => interp.run_inspect::, MergeSpec>(self, state), SpecId::SHANGHAI => { - interp.run_inspect::, ShanghaiSpec>(self, state) + interp.run_inspect::, ShanghaiSpec>(self, state) } - _ => interp.run_inspect::, LatestSpec>(self, state), + _ => interp.run_inspect::, LatestSpec>(self, state), } } @@ -347,13 +357,13 @@ where self.middlewares.deref().borrow_mut().clear(); } - pub fn add_middlewares(&mut self, middlewares: Rc>>) { + pub fn add_middlewares(&mut self, middlewares: Rc>>) { self.middlewares_enabled = true; // let ty = middlewares.deref().borrow().get_type(); self.middlewares.deref().borrow_mut().push(middlewares); } - pub fn remove_middlewares(&mut self, middlewares: Rc>>) { + pub fn remove_middlewares(&mut self, middlewares: Rc>>) { let ty = middlewares.deref().borrow().get_type(); self.middlewares .deref() @@ -815,19 +825,21 @@ macro_rules! invoke_middlewares { }; } -impl Host for FuzzHost +impl Host for FuzzHost where S: State + HasRand + HasCaller + Debug + Clone - + HasCorpus + + HasCorpus + HasMetadata + HasItyState + + UsesInput + 'static, I: VMInputT + EVMInputT + 'static, VS: VMStateT, + SC: Scheduler + Clone, { fn step(&mut self, interp: &mut Interpreter, state: &mut S) -> InstructionResult { unsafe { @@ -899,7 +911,7 @@ where let compressed_value = u256_to_u8!(value) + 1; WRITE_MAP[process_rw_key!(key)] = compressed_value; - let res = as Host>::sload( + let res = as Host>::sload( self, interp.contract.address, fast_peek!(0), @@ -1178,7 +1190,7 @@ where let mut unknown_sigs: usize = 0; let mut parsed_abi = vec![]; for sig in &sigs { - if let Some(abi) = state.metadata().get::().unwrap().get(sig) { + if let Some(abi) = state.metadata_map().get::().unwrap().get(sig) { parsed_abi.push(abi.clone()); } else { unknown_sigs += 1; @@ -1190,8 +1202,11 @@ where let abis = fetch_abi_heimdall(contract_code_str) .iter() .map(|abi| { - if let Some(known_abi) = - state.metadata().get::().unwrap().get(&abi.function) + if let Some(known_abi) = state + .metadata_map() + .get::() + .unwrap() + .get(&abi.function) { known_abi } else { diff --git a/src/evm/input.rs b/src/evm/input.rs index fb626d69c..1b1d00dc8 100644 --- a/src/evm/input.rs +++ b/src/evm/input.rs @@ -7,10 +7,10 @@ use crate::input::{ConciseSerde, VMInputT}; use crate::state::{HasCaller, HasItyState}; use crate::state_input::StagedVMState; -use libafl::bolts::HasLen; +use libafl_bolts::{HasLen, prelude::Rand}; use libafl::inputs::Input; use libafl::mutators::MutationResult; -use libafl::prelude::{HasBytesVec, HasMaxSize, HasMetadata, HasRand, Rand, State}; +use libafl::prelude::{HasBytesVec, HasMaxSize, HasMetadata, HasRand, State}; use primitive_types::U512; use revm_primitives::Env; use serde::{Deserialize, Deserializer, Serialize}; diff --git a/src/evm/middlewares/call_printer.rs b/src/evm/middlewares/call_printer.rs index 64991442f..873ee1c5c 100644 --- a/src/evm/middlewares/call_printer.rs +++ b/src/evm/middlewares/call_printer.rs @@ -10,6 +10,7 @@ use bytes::Bytes; use itertools::Itertools; use libafl::inputs::Input; use libafl::prelude::{HasCorpus, HasMetadata, State}; +use libafl::schedulers::Scheduler; use revm_interpreter::Interpreter; use revm_interpreter::opcode::{INVALID, JUMPDEST, JUMPI, REVERT, STOP}; use revm_primitives::Bytecode; @@ -132,23 +133,24 @@ impl CallPrinter { } -impl Middleware for CallPrinter - where - I: Input + VMInputT + EVMInputT + 'static, - VS: VMStateT, - S: State - + HasCaller - + HasCorpus - + HasItyState - + HasMetadata - + HasCurrentInputIdx - + Debug - + Clone, +impl Middleware for CallPrinter +where + I: Input + VMInputT + EVMInputT + 'static, + VS: VMStateT, + S: State + + HasCaller + + HasCorpus + + HasItyState + + HasMetadata + + HasCurrentInputIdx + + Debug + + Clone, + SC: Scheduler + Clone, { unsafe fn on_step( &mut self, interp: &mut Interpreter, - host: &mut FuzzHost, + host: &mut FuzzHost, state: &mut S, ) { if self.entry { @@ -168,7 +170,7 @@ impl Middleware for CallPrinter source: if let Some(Some(source)) = self.sourcemaps.get(&code_address) && let Some(source) = source.get(&interp.program_counter()) { Some(source.clone()) - } else if let Some(artifact) = state.metadata_mut().get_mut::() && let Some(build_result) = artifact.get_mut(&code_address) { + } else if let Some(artifact) = state.metadata_map_mut().get_mut::() && let Some(build_result) = artifact.get_mut(&code_address) { if let Some(srcmap) = build_result.get_sourcemap(caller_code).get(&interp.program_counter()) { Some(srcmap.clone()) } else { @@ -256,7 +258,7 @@ impl Middleware for CallPrinter source: if let Some(Some(source)) = self.sourcemaps.get(&caller_code_address) && let Some(source) = source.get(&interp.program_counter()) { Some(source.clone()) - } else if let Some(artifact) = state.metadata_mut().get_mut::() && let Some(build_result) = artifact.get_mut(&caller_code_address) { + } else if let Some(artifact) = state.metadata_map_mut().get_mut::() && let Some(build_result) = artifact.get_mut(&caller_code_address) { if let Some(srcmap) = build_result.get_sourcemap(caller_code).get(&interp.program_counter()) { Some(srcmap.clone()) } else { @@ -272,7 +274,7 @@ impl Middleware for CallPrinter unsafe fn on_return( &mut self, interp: &mut Interpreter, - host: &mut FuzzHost, + host: &mut FuzzHost, state: &mut S, by: &Bytes ) { @@ -284,7 +286,7 @@ impl Middleware for CallPrinter self.current_layer -= 1; } - unsafe fn on_insert(&mut self, bytecode: &mut Bytecode, address: EVMAddress, host: &mut FuzzHost, state: &mut S) { + unsafe fn on_insert(&mut self, bytecode: &mut Bytecode, address: EVMAddress, host: &mut FuzzHost, state: &mut S) { } @@ -292,4 +294,3 @@ impl Middleware for CallPrinter MiddlewareType::CallPrinter } } - diff --git a/src/evm/middlewares/coverage.rs b/src/evm/middlewares/coverage.rs index a509a202f..b5586fa3e 100644 --- a/src/evm/middlewares/coverage.rs +++ b/src/evm/middlewares/coverage.rs @@ -9,6 +9,7 @@ use std::time::{SystemTime, UNIX_EPOCH}; use itertools::Itertools; use libafl::inputs::Input; use libafl::prelude::{HasCorpus, HasMetadata, State}; +use libafl::schedulers::Scheduler; use revm_interpreter::Interpreter; use revm_interpreter::opcode::{INVALID, JUMPDEST, JUMPI, REVERT, STOP}; use revm_primitives::Bytecode; @@ -264,23 +265,24 @@ impl Coverage { } -impl Middleware for Coverage - where - I: Input + VMInputT + EVMInputT + 'static, - VS: VMStateT, - S: State - + HasCaller - + HasCorpus - + HasItyState - + HasMetadata - + HasCurrentInputIdx - + Debug - + Clone, +impl Middleware for Coverage +where + I: Input + VMInputT + EVMInputT + 'static, + VS: VMStateT, + S: State + + HasCaller + + HasCorpus + + HasItyState + + HasMetadata + + HasCurrentInputIdx + + Debug + + Clone, + SC: Scheduler + Clone, { unsafe fn on_step( &mut self, interp: &mut Interpreter, - host: &mut FuzzHost, + host: &mut FuzzHost, state: &mut S, ) { if IN_DEPLOY || !EVAL_COVERAGE { @@ -296,11 +298,11 @@ impl Middleware for Coverage } } - unsafe fn on_insert(&mut self, bytecode: &mut Bytecode, address: EVMAddress, host: &mut FuzzHost, state: &mut S) { + unsafe fn on_insert(&mut self, bytecode: &mut Bytecode, address: EVMAddress, host: &mut FuzzHost, state: &mut S) { let (pcs, jumpis, mut skip_pcs) = instructions_pc(&bytecode.clone()); // find all skipping PCs - let meta = state.metadata_mut().get_mut::().expect("ArtifactInfoMetadata not found"); + let meta = state.metadata_map_mut().get_mut::().expect("ArtifactInfoMetadata not found"); if let Some(build_artifact) = meta.get_mut(&address) { self.sources.insert(address, build_artifact.sources.clone()); diff --git a/src/evm/middlewares/middleware.rs b/src/evm/middlewares/middleware.rs index 3d43d5116..f086f9fd3 100644 --- a/src/evm/middlewares/middleware.rs +++ b/src/evm/middlewares/middleware.rs @@ -7,6 +7,8 @@ use crate::state::{HasCaller, HasItyState}; use bytes::Bytes; use libafl::corpus::{Corpus, Testcase}; use libafl::inputs::Input; +use libafl::prelude::UsesInput; +use libafl::schedulers::Scheduler; use libafl::state::{HasCorpus, HasMetadata, State}; use primitive_types::U512; use serde::{Deserialize, Serialize}; @@ -59,18 +61,20 @@ pub enum MiddlewareOp { MakeSubsequentCallSuccess(Bytes), } -pub fn add_corpus(host: &FuzzHost, state: &mut S, input: &EVMInput) +pub fn add_corpus(host: &mut FuzzHost, state: &mut S, input: &EVMInput) where I: Input + VMInputT + EVMInputT + 'static, S: State - + HasCorpus + + HasCorpus + HasItyState + HasMetadata + HasCaller + Clone + Debug + + UsesInput + 'static, VS: VMStateT + Default, + SC: Scheduler + Clone, { let mut tc = Testcase::new(input.as_any().downcast_ref::().unwrap().clone()) as Testcase; tc.set_exec_time(Duration::from_secs(0)); @@ -80,34 +84,34 @@ where .expect("failed to call scheduler on_add"); } -pub trait Middleware: Debug +pub trait Middleware: Debug where - S: State + HasCaller + Clone + Debug, + S: State + HasCorpus + HasCaller + Clone + Debug, I: VMInputT + EVMInputT, VS: VMStateT, + SC: Scheduler + Clone, { unsafe fn on_step( &mut self, interp: &mut Interpreter, - host: &mut FuzzHost, + host: &mut FuzzHost, state: &mut S, ); unsafe fn on_return( &mut self, interp: &mut Interpreter, - host: &mut FuzzHost, + host: &mut FuzzHost, state: &mut S, ret: &Bytes, ) { } - unsafe fn on_insert( - &mut self, - bytecode: &mut Bytecode, - address: EVMAddress, - host: &mut FuzzHost, - state: &mut S, - ); + + unsafe fn on_insert(&mut self, + bytecode: &mut Bytecode, + address: EVMAddress, + host: &mut FuzzHost, + state: &mut S); fn get_type(&self) -> MiddlewareType; } diff --git a/src/evm/middlewares/sha3_bypass.rs b/src/evm/middlewares/sha3_bypass.rs index b7db2d35f..a8b41de03 100644 --- a/src/evm/middlewares/sha3_bypass.rs +++ b/src/evm/middlewares/sha3_bypass.rs @@ -9,6 +9,7 @@ use bytes::Bytes; use itertools::Itertools; use libafl::inputs::Input; use libafl::prelude::{HasCorpus, HasMetadata, State}; +use libafl::schedulers::Scheduler; use revm_interpreter::opcode::JUMPI; use revm_interpreter::Interpreter; use revm_primitives::Bytecode; @@ -101,23 +102,24 @@ impl Sha3TaintAnalysis { } } -impl Middleware for Sha3TaintAnalysis +impl Middleware for Sha3TaintAnalysis where I: Input + VMInputT + EVMInputT + 'static, VS: VMStateT, S: State + HasCaller - + HasCorpus + + HasCorpus + HasItyState + HasMetadata + HasCurrentInputIdx + Debug + Clone, + SC: Scheduler + Clone, { unsafe fn on_step( &mut self, interp: &mut Interpreter, - host: &mut FuzzHost, + host: &mut FuzzHost, state: &mut S, ) { // @@ -386,7 +388,7 @@ where } unsafe fn on_return( - &mut self, interp: &mut Interpreter, host: &mut FuzzHost, state: &mut S, + &mut self, interp: &mut Interpreter, host: &mut FuzzHost, state: &mut S, by: &Bytes ) { self.pop_ctx(); @@ -396,7 +398,7 @@ where &mut self, bytecode: &mut Bytecode, address: EVMAddress, - host: &mut FuzzHost, + host: &mut FuzzHost, state: &mut S, ) { } @@ -417,23 +419,24 @@ impl Sha3Bypass { } } -impl Middleware for Sha3Bypass +impl Middleware for Sha3Bypass where I: Input + VMInputT + EVMInputT + 'static, VS: VMStateT, S: State + HasCaller - + HasCorpus + + HasCorpus + HasItyState + HasMetadata + HasCurrentInputIdx + Debug + Clone, + SC: Scheduler + Clone, { unsafe fn on_step( &mut self, interp: &mut Interpreter, - host: &mut FuzzHost, + host: &mut FuzzHost, state: &mut S, ) { if *interp.instruction_pointer == JUMPI { @@ -455,7 +458,7 @@ where &mut self, bytecode: &mut Bytecode, address: EVMAddress, - host: &mut FuzzHost, + host: &mut FuzzHost, state: &mut S, ) { } @@ -491,9 +494,9 @@ mod tests { if !path.exists() { std::fs::create_dir(path); } - let mut evm_executor: EVMExecutor = + let mut evm_executor: EVMExecutor> = EVMExecutor::new( - FuzzHost::new(Arc::new(StdScheduler::new()), "work_dir".to_string()), + FuzzHost::new(StdScheduler::new(), "work_dir".to_string()), generate_random_address(&mut state), ); diff --git a/src/evm/mutator.rs b/src/evm/mutator.rs index 85438e2ff..b7d585ea4 100644 --- a/src/evm/mutator.rs +++ b/src/evm/mutator.rs @@ -6,7 +6,9 @@ use crate::input::{ConciseSerde, VMInputT}; use crate::state::{HasCaller, InfantStateState}; use libafl::inputs::Input; use libafl::mutators::MutationResult; -use libafl::prelude::{HasMaxSize, HasRand, Mutator, Rand, State}; +use libafl::prelude::{HasMaxSize, HasRand, Mutator, State}; +use libafl_bolts::Named; +use libafl_bolts::prelude::Rand; use libafl::schedulers::Scheduler; use libafl::state::HasMetadata; use libafl::Error; @@ -18,10 +20,9 @@ use std::fmt::Debug; use revm_interpreter::Interpreter; use crate::evm::abi::ABIAddressToInstanceMap; use crate::evm::types::{convert_u256_to_h160, EVMAddress, EVMU256}; -use crate::evm::vm::{Constraint, EVMState, EVMStateT}; +use crate::evm::vm::{Constraint, EVMStateT}; use crate::state::HasItyState; -use crate::state_input::StagedVMState; /// [`AccessPattern`] records the access pattern of the input during execution. This helps /// to determine what is needed to be fuzzed. For instance, we don't need to mutate caller @@ -89,30 +90,30 @@ impl AccessPattern { } /// [`FuzzMutator`] is a mutator that mutates the input based on the ABI and access pattern -pub struct FuzzMutator<'a, VS, Loc, Addr, SC, CI> +pub struct FuzzMutator where VS: Default + VMStateT, - SC: Scheduler, InfantStateState>, + SC: Scheduler>, Addr: Serialize + DeserializeOwned + Debug + Clone, Loc: Serialize + DeserializeOwned + Debug + Clone, CI: Serialize + DeserializeOwned + Debug + Clone + ConciseSerde { /// Scheduler for selecting the next VM state to use if we decide to mutate the VM state of /// the input - pub infant_scheduler: &'a SC, + pub infant_scheduler: SC, pub phantom: std::marker::PhantomData<(VS, Loc, Addr, CI)>, } -impl<'a, VS, Loc, Addr, SC, CI> FuzzMutator<'a, VS, Loc, Addr, SC, CI> +impl FuzzMutator where VS: Default + VMStateT, - SC: Scheduler, InfantStateState>, + SC: Scheduler>, Addr: Serialize + DeserializeOwned + Debug + Clone, Loc: Serialize + DeserializeOwned + Debug + Clone, CI: Serialize + DeserializeOwned + Debug + Clone + ConciseSerde { /// Create a new [`FuzzMutator`] with the given scheduler - pub fn new(infant_scheduler: &'a SC) -> Self { + pub fn new(infant_scheduler: SC) -> Self { Self { infant_scheduler, phantom: Default::default(), @@ -135,7 +136,7 @@ impl<'a, VS, Loc, Addr, SC, CI> FuzzMutator<'a, VS, Loc, Addr, SC, CI> let rand_int = state.rand_mut().next(); let always_none = state.rand_mut().next() % 30 == 0; let abis = state - .metadata() + .metadata_map() .get::() .expect("ABIAddressToInstanceMap not found"); let abi = match abis.map.get(&target) { @@ -164,11 +165,24 @@ impl<'a, VS, Loc, Addr, SC, CI> FuzzMutator<'a, VS, Loc, Addr, SC, CI> } } -impl<'a, VS, Loc, Addr, I, S, SC, CI> Mutator for FuzzMutator<'a, VS, Loc, Addr, SC, CI> +impl Named for FuzzMutator +where + VS: Default + VMStateT, + SC: Scheduler>, + Addr: Serialize + DeserializeOwned + Debug + Clone, + Loc: Serialize + DeserializeOwned + Debug + Clone, + CI: Serialize + DeserializeOwned + Debug + Clone + ConciseSerde +{ + fn name(&self) -> &str { + "FuzzMutator" + } +} + +impl Mutator for FuzzMutator where I: VMInputT + Input + EVMInputT, S: State + HasRand + HasMaxSize + HasItyState + HasCaller + HasMetadata, - SC: Scheduler, InfantStateState>, + SC: Scheduler>, VS: Default + VMStateT + EVMStateT, Addr: PartialEq + Debug + Serialize + DeserializeOwned + Clone, Loc: Serialize + DeserializeOwned + Debug + Clone, @@ -183,7 +197,7 @@ impl<'a, VS, Loc, Addr, I, S, SC, CI> Mutator for FuzzMutator<'a, VS, Loc, ) -> Result { // if the VM state of the input is not initialized, swap it with a state initialized if !input.get_staged_state().initialized { - let concrete = state.get_infant_state(self.infant_scheduler).unwrap(); + let concrete = state.get_infant_state(&mut self.infant_scheduler).unwrap(); input.set_staged_state(concrete.1, concrete.0); } @@ -242,7 +256,7 @@ impl<'a, VS, Loc, Addr, I, S, SC, CI> Mutator for FuzzMutator<'a, VS, Loc, // mutate the VM state let old_idx = input.get_state_idx(); let (idx, new_state) = - state.get_infant_state(self.infant_scheduler).unwrap(); + state.get_infant_state(&mut self.infant_scheduler).unwrap(); if idx == old_idx { return MutationResult::Skipped; } @@ -279,7 +293,7 @@ impl<'a, VS, Loc, Addr, I, S, SC, CI> Mutator for FuzzMutator<'a, VS, Loc, already_crossed = true; // cross over infant state let old_idx = input.get_state_idx(); - let (idx, new_state) = state.get_infant_state(self.infant_scheduler).unwrap(); + let (idx, new_state) = state.get_infant_state(&mut self.infant_scheduler).unwrap(); if idx == old_idx { return MutationResult::Skipped; } @@ -329,14 +343,4 @@ impl<'a, VS, Loc, Addr, I, S, SC, CI> Mutator for FuzzMutator<'a, VS, Loc, } Ok(res) } - - fn post_exec( - &mut self, - _state: &mut S, - _stage_idx: i32, - _corpus_idx: Option, - ) -> Result<(), Error> { - // todo!() - Ok(()) - } -} \ No newline at end of file +} diff --git a/src/evm/onchain/flashloan.rs b/src/evm/onchain/flashloan.rs index 2da038187..f3a3d0bb5 100644 --- a/src/evm/onchain/flashloan.rs +++ b/src/evm/onchain/flashloan.rs @@ -9,6 +9,7 @@ use crate::evm::middlewares::middleware::{Middleware, MiddlewareOp, MiddlewareTy use crate::evm::mutator::AccessPattern; use crate::evm::onchain::endpoints::{OnChainConfig, PriceOracle}; use std::borrow::BorrowMut; +use libafl::schedulers::Scheduler; use revm_interpreter::Interpreter; use crate::evm::host::FuzzHost; use crate::generic_vm::vm_state::VMStateT; @@ -17,9 +18,9 @@ use crate::oracle::Oracle; use crate::state::{HasCaller, HasItyState}; use bytes::Bytes; use libafl::corpus::{Corpus, Testcase}; -use libafl::impl_serdeany; +use libafl_bolts::impl_serdeany; use libafl::inputs::Input; -use libafl::prelude::{HasCorpus, State}; +use libafl::prelude::{HasCorpus, State, UsesInput}; use libafl::state::{HasMetadata, HasRand}; use serde::{Deserialize, Serialize}; @@ -96,18 +97,20 @@ impl PriceOracle for DummyPriceOracle { } } -pub fn register_borrow_txn(host: &FuzzHost, state: &mut S, token: EVMAddress) +pub fn register_borrow_txn(mut scheduler: SC, state: &mut S, token: EVMAddress) where I: Input + VMInputT + EVMInputT + 'static, S: State - + HasCorpus + + HasCorpus + HasItyState + HasMetadata + HasCaller + Clone + Debug + + UsesInput + 'static, VS: VMStateT + Default, + SC: Scheduler + Clone, { let mut tc = Testcase::new( { @@ -137,14 +140,23 @@ where ) as Testcase; tc.set_exec_time(Duration::from_secs(0)); let idx = state.corpus_mut().add(tc).expect("failed to add"); - host.scheduler + scheduler .on_add(state, idx) .expect("failed to call scheduler on_add"); } impl Flashloan where - S: State + HasRand + HasCaller + HasCorpus + Debug + Clone + HasMetadata + HasItyState + 'static, + S: State + + HasRand + + HasCaller + + HasCorpus + + Debug + + Clone + + HasMetadata + + HasItyState + + UsesInput + + 'static, I: VMInputT + EVMInputT + 'static, VS: VMStateT, { @@ -256,7 +268,10 @@ where } #[cfg(feature = "flashloan_v2")] - pub fn on_pair_insertion(&mut self, host: &FuzzHost, state: &mut S, pair: EVMAddress) { + pub fn on_pair_insertion(&mut self, host: &FuzzHost, state: &mut S, pair: EVMAddress) + where + SC: Scheduler + Clone, + { let slots = host.find_static_call_read_slot( pair, Bytes::from(vec![0x09, 0x02, 0xf1, 0xac]), // getReserves @@ -299,17 +314,27 @@ where } } -impl Middleware for Flashloan +impl Middleware for Flashloan where - S: State +HasRand+ HasCaller+ HasMetadata + HasCorpus + Debug + Clone + HasItyState + 'static, + S: State + + HasRand + + HasCaller + + HasMetadata + + HasCorpus + + Debug + + Clone + + HasItyState + + UsesInput + + 'static, I: VMInputT + EVMInputT + 'static, VS: VMStateT, + SC: Scheduler + Clone, { #[cfg(not(feature = "flashloan_v2"))] unsafe fn on_step( &mut self, interp: &mut Interpreter, - host: &mut FuzzHost, + host: &mut FuzzHost, _state: &mut S, ) { macro_rules! earned { @@ -447,7 +472,7 @@ where } #[cfg(feature = "flashloan_v2")] - unsafe fn on_step(&mut self, interp: &mut Interpreter, host: &mut FuzzHost, s: &mut S) + unsafe fn on_step(&mut self, interp: &mut Interpreter, host: &mut FuzzHost, s: &mut S) where S: HasCaller, { @@ -498,7 +523,7 @@ where } } - unsafe fn on_insert(&mut self, bytecode: &mut Bytecode, address: EVMAddress, host: &mut FuzzHost, state: &mut S) { + unsafe fn on_insert(&mut self, bytecode: &mut Bytecode, address: EVMAddress, host: &mut FuzzHost, state: &mut S) { } diff --git a/src/evm/onchain/onchain.rs b/src/evm/onchain/onchain.rs index a71241e05..4399173ed 100644 --- a/src/evm/onchain/onchain.rs +++ b/src/evm/onchain/onchain.rs @@ -18,8 +18,9 @@ use crate::state_input::StagedVMState; use crypto::digest::Digest; use crypto::sha3::Sha3; use libafl::corpus::Corpus; -use libafl::prelude::{HasCorpus, HasMetadata, Input}; +use libafl::prelude::{HasCorpus, HasMetadata, Input, UsesInput}; +use libafl::schedulers::Scheduler; use libafl::state::{HasRand, State}; use std::cell::RefCell; @@ -149,24 +150,26 @@ pub fn keccak_hex(data: EVMU256) -> String { hex::encode(output) } -impl Middleware for OnChain +impl Middleware for OnChain where I: Input + VMInputT + EVMInputT + 'static, S: State + HasRand + Debug + HasCaller - + HasCorpus + + HasCorpus + HasItyState + HasMetadata + Clone + + UsesInput + 'static, VS: VMStateT + Default + 'static, + SC: Scheduler + Clone, { unsafe fn on_step( &mut self, interp: &mut Interpreter, - host: &mut FuzzHost, + host: &mut FuzzHost, state: &mut S, ) { #[cfg(feature = "force_cache")] @@ -314,11 +317,8 @@ where // replace the code with the one from builder // println!("replace code for {:?} with builder's", address_h160); // host.set_codedata(address_h160, contract_code.clone()); - state - .metadata_mut() - .get_mut::() - .expect("artifact info metadata") - .add(address_h160, job); + state.metadata_map_mut().get_mut::() + .expect("artifact info metadata").add(address_h160, job); } } @@ -339,7 +339,7 @@ where let sigs = extract_sig_from_contract(&contract_code_str); let mut unknown_sigs: usize = 0; for sig in &sigs { - if let Some(abi) = state.metadata().get::().unwrap().get(sig) { + if let Some(abi) = state.metadata_map().get::().unwrap().get(sig) { parsed_abi.push(abi.clone()); } else { unknown_sigs += 1; @@ -351,9 +351,7 @@ where let abis = fetch_abi_heimdall(contract_code_str) .iter() .map(|abi| { - if let Some(known_abi) = - state.metadata().get::().unwrap().get(&abi.function) - { + if let Some(known_abi) = state.metadata_map().get::().unwrap().get(&abi.function) { known_abi } else { abi @@ -471,13 +469,8 @@ where } } - unsafe fn on_insert( - &mut self, - bytecode: &mut Bytecode, - address: EVMAddress, - host: &mut FuzzHost, - state: &mut S, - ) { + unsafe fn on_insert(&mut self, bytecode: &mut Bytecode, address: EVMAddress, host: &mut FuzzHost, state: &mut S) { + } fn get_type(&self) -> MiddlewareType { diff --git a/src/evm/oracle.rs b/src/evm/oracle.rs index ef13fcd29..58be1f8df 100644 --- a/src/evm/oracle.rs +++ b/src/evm/oracle.rs @@ -10,7 +10,7 @@ use crate::oracle::{Oracle, OracleCtx}; use crate::state::HasExecutionResult; use bytes::Bytes; -use libafl::impl_serdeany; +use libafl_bolts::impl_serdeany; use crate::evm::uniswap::{liquidate_all_token, TokenContext}; use revm_primitives::Bytecode; @@ -88,4 +88,3 @@ impl EVMBugResult { } } } - diff --git a/src/evm/oracles/arb_call.rs b/src/evm/oracles/arb_call.rs index 46c53d401..081573b36 100644 --- a/src/evm/oracles/arb_call.rs +++ b/src/evm/oracles/arb_call.rs @@ -8,7 +8,7 @@ use crate::evm::vm::EVMState; use crate::oracle::{Oracle, OracleCtx}; use bytes::Bytes; use itertools::Itertools; -use libafl::impl_serdeany; +use libafl_bolts::impl_serdeany; use libafl::prelude::HasMetadata; use revm_primitives::{Bytecode, HashSet}; use serde::{Deserialize, Serialize}; @@ -79,12 +79,12 @@ Oracle< let mut res = vec![]; for (caller, target, pc) in ctx.post_state.arbitrary_calls.iter() { if !ctx.fuzz_state.has_metadata::() { - ctx.fuzz_state.metadata_mut().insert(ArbitraryCallMetadata { + ctx.fuzz_state.metadata_map_mut().insert(ArbitraryCallMetadata { known_calls: HashMap::new(), }); } - let mut metadata = ctx.fuzz_state.metadata_mut().get_mut::().unwrap(); + let mut metadata = ctx.fuzz_state.metadata_map_mut().get_mut::().unwrap(); let entry = metadata.known_calls.entry((*caller, *pc)).or_insert(HashSet::new()); if entry.len() > 3 { continue; @@ -102,7 +102,7 @@ Oracle< .clone(); let srcmap = BuildJobResult::get_sourcemap_executor( - ctx.fuzz_state.metadata_mut().get_mut::().expect("get metadata failed") + ctx.fuzz_state.metadata_map_mut().get_mut::().expect("get metadata failed") .get_mut(caller), ctx.executor, caller, diff --git a/src/evm/oracles/arb_transfer.rs b/src/evm/oracles/arb_transfer.rs index f42307d00..2b2ae0a56 100644 --- a/src/evm/oracles/arb_transfer.rs +++ b/src/evm/oracles/arb_transfer.rs @@ -8,7 +8,7 @@ use crate::evm::vm::EVMState; use crate::oracle::{Oracle, OracleCtx}; use bytes::Bytes; use itertools::Itertools; -use libafl::impl_serdeany; +use libafl_bolts::impl_serdeany; use libafl::prelude::HasMetadata; use revm_primitives::{Bytecode, HashSet}; use serde::{Deserialize, Serialize}; @@ -82,7 +82,7 @@ Oracle< .clone(); let srcmap = BuildJobResult::get_sourcemap_executor( - ctx.fuzz_state.metadata_mut().get_mut::().expect("get metadata failed") + ctx.fuzz_state.metadata_map_mut().get_mut::().expect("get metadata failed") .get_mut(caller), ctx.executor, caller, diff --git a/src/evm/oracles/selfdestruct.rs b/src/evm/oracles/selfdestruct.rs index 4c54fb8bf..9b20af126 100644 --- a/src/evm/oracles/selfdestruct.rs +++ b/src/evm/oracles/selfdestruct.rs @@ -71,7 +71,7 @@ for SelfdestructOracle .clone(); let srcmap = BuildJobResult::get_sourcemap_executor( - ctx.fuzz_state.metadata_mut().get_mut::().expect("get metadata failed") + ctx.fuzz_state.metadata_map_mut().get_mut::().expect("get metadata failed") .get_mut(addr), ctx.executor, addr, diff --git a/src/evm/oracles/typed_bug.rs b/src/evm/oracles/typed_bug.rs index 713de92ba..d0c0d062f 100644 --- a/src/evm/oracles/typed_bug.rs +++ b/src/evm/oracles/typed_bug.rs @@ -72,7 +72,7 @@ impl Oracle, let real_bug_idx = (hasher.finish() as u64) << 8 + TYPED_BUG_BUG_IDX; let srcmap = BuildJobResult::get_sourcemap_executor( - ctx.fuzz_state.metadata_mut().get_mut::().expect("get metadata failed") + ctx.fuzz_state.metadata_map_mut().get_mut::().expect("get metadata failed") .get_mut(addr), ctx.executor, addr, diff --git a/src/evm/presets/pair.rs b/src/evm/presets/pair.rs index e16b1d495..acbaa3eb2 100644 --- a/src/evm/presets/pair.rs +++ b/src/evm/presets/pair.rs @@ -5,24 +5,26 @@ use crate::evm::vm::EVMExecutor; use crate::generic_vm::vm_state::VMStateT; use crate::input::VMInputT; use crate::state::HasCaller; -use libafl::state::State; +use libafl::schedulers::Scheduler; +use libafl::state::{State, HasCorpus}; use std::fmt::Debug; use std::ops::Deref; use crate::evm::types::EVMAddress; pub struct PairPreset; -impl Preset for PairPreset +impl Preset for PairPreset where - S: State + HasCaller + Debug + Clone + 'static, + S: State + HasCorpus + HasCaller + Debug + Clone + 'static, I: VMInputT + EVMInputT, VS: VMStateT, + SC: Scheduler + Clone, { fn presets( &self, function_sig: [u8; 4], input: &EVMInput, - evm_executor: &EVMExecutor, + evm_executor: &EVMExecutor, ) -> Vec { let mut res = vec![]; match function_sig { diff --git a/src/evm/presets/presets.rs b/src/evm/presets/presets.rs index 4015d6c07..8f1d14e03 100644 --- a/src/evm/presets/presets.rs +++ b/src/evm/presets/presets.rs @@ -4,19 +4,22 @@ use crate::generic_vm::vm_state::VMStateT; use crate::input::VMInputT; use crate::state::HasCaller; use libafl::prelude::State; +use libafl::schedulers::Scheduler; +use libafl::state::HasCorpus; use std::fmt::Debug; use crate::evm::types::EVMAddress; -pub trait Preset +pub trait Preset where - S: State + HasCaller + Debug + Clone + 'static, + S: State + HasCorpus + HasCaller + Debug + Clone + 'static, I: VMInputT + EVMInputT, VS: VMStateT, + SC: Scheduler + Clone, { fn presets( &self, function_sig: [u8; 4], input: &EVMInput, - evm_executor: &EVMExecutor, + evm_executor: &EVMExecutor, ) -> Vec; } diff --git a/src/evm/types.rs b/src/evm/types.rs index 6b56ad652..6b70b2ebe 100644 --- a/src/evm/types.rs +++ b/src/evm/types.rs @@ -2,17 +2,18 @@ use std::collections::HashMap; /// Common generic types for EVM fuzzing use crate::evm::input::{ConciseEVMInput, EVMInput}; use crate::evm::mutator::FuzzMutator; -use crate::evm::vm::EVMState; +use crate::evm::vm::{EVMState, EVMExecutor}; use crate::oracle::OracleCtx; use crate::scheduler::SortedDroppingScheduler; use crate::state::{FuzzState, InfantStateState}; use crate::state_input::StagedVMState; use bytes::Bytes; -use libafl::prelude::{HasRand, RomuDuoJrRand}; +use libafl::prelude::HasRand; +use libafl::schedulers::QueueScheduler; use primitive_types::{H160, H256}; use revm_primitives::{B160, Bytecode, U256}; -use libafl::prelude::Rand; +use libafl_bolts::bolts_prelude::{Rand, RomuDuoJrRand}; use revm_primitives::ruint::aliases::U512; use crate::evm::srcmap::parser::SourceMapLocation; use crate::executor::FuzzExecutor; @@ -25,14 +26,10 @@ pub type EVMFuzzState = FuzzState = OracleCtx<'a, EVMState, EVMAddress, Bytecode, Bytes, EVMAddress, EVMU256, Vec, EVMInput, EVMFuzzState, ConciseEVMInput>; pub type EVMFuzzMutator<'a> = FuzzMutator< - 'a, EVMState, EVMAddress, EVMAddress, - SortedDroppingScheduler< - StagedVMState, - InfantStateState, - >, + SortedDroppingScheduler>, ConciseEVMInput >; @@ -46,6 +43,8 @@ pub type ProjectSourceMapTy = HashMap = FuzzExecutor, EVMInput, EVMFuzzState, OT, ConciseEVMInput>; +pub type EVMQueueExecutor = EVMExecutor>; + /// convert array of 20x u8 to H160 pub fn convert_H160(v: [u8; 20]) -> H160 { return v.into(); diff --git a/src/evm/vm.rs b/src/evm/vm.rs index f64fbcdc8..543421eb1 100644 --- a/src/evm/vm.rs +++ b/src/evm/vm.rs @@ -24,7 +24,7 @@ use crate::input::{ConciseSerde, VMInputT}; use crate::state_input::StagedVMState; use bytes::Bytes; -use libafl::prelude::{HasMetadata, HasRand}; +use libafl::prelude::{HasMetadata, HasRand, UsesInput}; use libafl::schedulers::Scheduler; use libafl::state::{HasCorpus, State}; @@ -390,14 +390,15 @@ pub static mut IS_FAST_CALL_STATIC: bool = false; /// EVM executor, wrapper of revm #[derive(Debug, Clone)] -pub struct EVMExecutor +pub struct EVMExecutor where - S: State + HasCaller + Debug + Clone + 'static, + S: State + HasCorpus + HasCaller + Debug + Clone + 'static, I: VMInputT + EVMInputT, VS: VMStateT, + SC: Scheduler + Clone, { /// Host providing the blockchain environment (e.g., writing/reading storage), needed by revm - pub host: FuzzHost, + pub host: FuzzHost, /// [Depreciated] Deployer address pub deployer: EVMAddress, /// Known arbitrary (caller,pc) @@ -432,12 +433,12 @@ pub struct IntermediateExecutionResult { pub memory: Vec, } -impl EVMExecutor +impl EVMExecutor where I: VMInputT + EVMInputT + 'static, S: State + HasRand - + HasCorpus + + HasCorpus + HasItyState + HasMetadata + HasCaller @@ -445,12 +446,14 @@ where + Default + Clone + Debug + + UsesInput + 'static, VS: Default + VMStateT + 'static, CI: Serialize + DeserializeOwned + Debug + Clone + ConciseSerde + 'static, + SC: Scheduler + Clone + 'static, { /// Create a new EVM executor given a host and deployer address - pub fn new(fuzz_host: FuzzHost, deployer: EVMAddress) -> Self { + pub fn new(fuzz_host: FuzzHost, deployer: EVMAddress) -> Self { Self { host: fuzz_host, deployer, @@ -478,7 +481,7 @@ where data: Bytes, input: &I, post_exec: Option, - mut state: &mut S, + state: &mut S, cleanup: bool, ) -> IntermediateExecutionResult { // Initial setups @@ -830,7 +833,7 @@ where &mut self, input: &I, state: &mut S, - middleware: Rc>>, + middleware: Rc>>, ) { self.host.add_middlewares(middleware.clone()); self.execute(input, state); @@ -840,14 +843,14 @@ where pub static mut IN_DEPLOY: bool = false; -impl +impl GenericVM, I, S, CI> - for EVMExecutor + for EVMExecutor where I: VMInputT + EVMInputT + 'static, S: State + HasRand - + HasCorpus + + HasCorpus + HasItyState + HasMetadata + HasCaller @@ -855,9 +858,11 @@ where + Default + Clone + Debug + + UsesInput + 'static, VS: VMStateT + Default + 'static, CI: Serialize + DeserializeOwned + Debug + Clone + ConciseSerde + 'static, + SC: Scheduler + Clone + 'static, { /// Deploy a contract fn deploy( @@ -1084,7 +1089,8 @@ mod tests { use crate::state::FuzzState; use crate::state_input::StagedVMState; use bytes::Bytes; - use libafl::prelude::{tuple_list, StdScheduler}; + use libafl::prelude::StdScheduler; + use libafl_bolts::tuples::tuple_list; use revm_primitives::Bytecode; use std::cell::RefCell; use std::path::Path; @@ -1098,9 +1104,9 @@ mod tests { if !path.exists() { std::fs::create_dir(path).unwrap(); } - let mut evm_executor: EVMExecutor = + let mut evm_executor: EVMExecutor> = EVMExecutor::new( - FuzzHost::new(Arc::new(StdScheduler::new()), "work_dir".to_string()), + FuzzHost::new(StdScheduler::new(), "work_dir".to_string()), generate_random_address(&mut state), ); let mut observers = tuple_list!(); diff --git a/src/executor.rs b/src/executor.rs index 184a0f784..c7904de4b 100644 --- a/src/executor.rs +++ b/src/executor.rs @@ -6,8 +6,8 @@ use std::marker::PhantomData; use crate::evm::input::EVMInput; use libafl::executors::{Executor, ExitKind}; use libafl::inputs::Input; -use libafl::prelude::{HasCorpus, HasMetadata, HasObservers, ObserversTuple}; -use libafl::state::State; +use libafl::prelude::{HasCorpus, HasMetadata, HasObservers, ObserversTuple, UsesInput, UsesObservers}; +use libafl::state::{State, UsesState}; use libafl::Error; use serde::de::DeserializeOwned; use serde::Serialize; @@ -26,7 +26,8 @@ use crate::state::HasExecutionResult; pub struct FuzzExecutor where I: VMInputT, - OT: ObserversTuple, + S: UsesInput, + OT: ObserversTuple, VS: Default + VMStateT, Addr: Serialize + DeserializeOwned + Debug + Clone, Loc: Serialize + DeserializeOwned + Debug + Clone, @@ -39,11 +40,40 @@ where phantom: PhantomData<(I, S, Addr, Out)>, } +impl UsesState + for FuzzExecutor +where + I: VMInputT, + S: UsesInput, + OT: ObserversTuple, + VS: Default + VMStateT, + Addr: Serialize + DeserializeOwned + Debug + Clone, + Loc: Serialize + DeserializeOwned + Debug + Clone, + CI: Serialize + DeserializeOwned + Debug + Clone + ConciseSerde +{ + type State = S; +} + +impl UsesObservers + for FuzzExecutor +where + I: VMInputT, + S: UsesInput, + OT: ObserversTuple, + VS: Default + VMStateT, + Addr: Serialize + DeserializeOwned + Debug + Clone, + Loc: Serialize + DeserializeOwned + Debug + Clone, + CI: Serialize + DeserializeOwned + Debug + Clone + ConciseSerde +{ + type Observers = OT; +} + impl Debug for FuzzExecutor where I: VMInputT, - OT: ObserversTuple, + S: UsesInput, + OT: ObserversTuple, VS: Default + VMStateT, Addr: Serialize + DeserializeOwned + Debug + Clone, Loc: Serialize + DeserializeOwned + Debug + Clone, @@ -61,7 +91,8 @@ impl FuzzExecutor where I: VMInputT, - OT: ObserversTuple, + S: UsesInput, + OT: ObserversTuple, VS: Default + VMStateT, Addr: Serialize + DeserializeOwned + Debug + Clone, Loc: Serialize + DeserializeOwned + Debug + Clone, @@ -80,25 +111,27 @@ where } } -impl Executor +impl Executor for FuzzExecutor where I: VMInputT + Input + 'static, - OT: ObserversTuple, - S: State + HasExecutionResult + HasCorpus + HasMetadata + 'static, + OT: ObserversTuple, + S: State + HasExecutionResult + HasCorpus + HasMetadata + UsesInput + 'static, VS: Default + VMStateT, Addr: Serialize + DeserializeOwned + Debug + Clone, Loc: Serialize + DeserializeOwned + Debug + Clone, Out: Default, CI: Serialize + DeserializeOwned + Debug + Clone + ConciseSerde + 'static, + EM: UsesState, + Z: UsesState, { /// Run the VM to execute the input fn run_target( &mut self, _fuzzer: &mut Z, - state: &mut S, + state: &mut Self::State, _mgr: &mut EM, - input: &I, + input: &Self::Input, ) -> Result { let res = self.vm.deref().borrow_mut().execute(input, state); // the execution result is added to the fuzzer state @@ -109,11 +142,12 @@ where } // implement HasObservers trait for ItyFuzzer -impl HasObservers +impl HasObservers for FuzzExecutor where I: VMInputT, - OT: ObserversTuple, + S: UsesInput, + OT: ObserversTuple, VS: Default + VMStateT, Addr: Serialize + DeserializeOwned + Debug + Clone, Loc: Serialize + DeserializeOwned + Debug + Clone, diff --git a/src/feedback.rs b/src/feedback.rs index 452023639..3df4dee9c 100644 --- a/src/feedback.rs +++ b/src/feedback.rs @@ -13,7 +13,9 @@ use libafl::events::EventFirer; use libafl::executors::ExitKind; use libafl::inputs::Input; use libafl::observers::ObserversTuple; -use libafl::prelude::{Feedback, HasMetadata, Named}; +use libafl::prelude::{Feedback, HasMetadata, UsesInput}; +use libafl_bolts::Named; + use libafl::schedulers::Scheduler; use libafl::state::{HasClientPerfMonitor, HasCorpus, State}; use libafl::Error; @@ -103,14 +105,15 @@ where } } -impl<'a, VS, Addr, Code, By, Loc, SlotTy, Out, I, S, CI> Feedback +impl<'a, VS, Addr, Code, By, Loc, SlotTy, Out, I, S, CI> Feedback for OracleFeedback<'a, VS, Addr, Code, By, Loc, SlotTy, Out, I, S, CI> where S: State + HasClientPerfMonitor + HasExecutionResult - + HasCorpus + + HasCorpus + HasMetadata + + UsesInput + 'static, I: VMInputT + 'static, VS: Default + VMStateT, @@ -132,23 +135,23 @@ where &mut self, state: &mut S, _manager: &mut EMI, - input: &I, + input: &S::Input, _observers: &OT, _exit_kind: &ExitKind, ) -> Result where - EMI: EventFirer, - OT: ObserversTuple, + EMI: EventFirer, + OT: ObserversTuple, { if state.get_execution_result().reverted { return Ok(false); } { if !state.has_metadata::() { - state.metadata_mut().insert(BugMetadata::default()); + state.metadata_map_mut().insert(BugMetadata::default()); } - state.metadata_mut().get_mut::().unwrap().current_bugs.clear(); + state.metadata_map_mut().get_mut::().unwrap().current_bugs.clear(); } @@ -191,7 +194,7 @@ where .deref() .borrow() .oracle(&mut oracle_ctx, original_stage) { - let metadata = oracle_ctx.fuzz_state.metadata_mut().get_mut::().unwrap(); + let metadata = oracle_ctx.fuzz_state.metadata_map_mut().get_mut::().unwrap(); if metadata.known_bugs.contains(&bug_idx) || has_post_exec { continue; } @@ -211,20 +214,6 @@ where before_exit!(); Ok(is_any_bug_hit) } - - // dummy method - fn append_metadata( - &mut self, - _state: &mut S, - _testcase: &mut Testcase, - ) -> Result<(), Error> { - Ok(()) - } - - // dummy method - fn discard_metadata(&mut self, _state: &mut S, _input: &I) -> Result<(), Error> { - Ok(()) - } } /// DataflowFeedback is a feedback that uses dataflow analysis to determine @@ -274,10 +263,10 @@ impl<'a, VS, Loc, Addr, Out, CI> DataflowFeedback<'a, VS, Loc, Addr, Out, CI> { } #[cfg(feature = "dataflow")] -impl<'a, VS, Loc, Addr, I, S, Out, CI> Feedback for DataflowFeedback<'a, VS, Loc, Addr, Out, CI> +impl<'a, VS, Loc, Addr, S, Out, CI, I> Feedback for DataflowFeedback<'a, VS, Loc, Addr, Out, CI> where - S: State + HasClientPerfMonitor + HasExecutionResult, I: VMInputT, + S: State + HasClientPerfMonitor + HasExecutionResult + UsesInput, VS: Default + VMStateT, Addr: Serialize + DeserializeOwned + Debug + Clone, Loc: Serialize + DeserializeOwned + Debug + Clone, @@ -293,13 +282,13 @@ where &mut self, _state: &mut S, _manager: &mut EMI, - _input: &I, + _input: &S::Input, _observers: &OT, _exit_kind: &ExitKind, ) -> Result where - EMI: EventFirer, - OT: ObserversTuple, + EMI: EventFirer, + OT: ObserversTuple, { let mut interesting = false; for i in 0..MAP_SIZE { @@ -331,18 +320,6 @@ where } return Ok(interesting); } - - fn append_metadata( - &mut self, - _state: &mut S, - _testcase: &mut Testcase, - ) -> Result<(), Error> { - Ok(()) - } - - fn discard_metadata(&mut self, _state: &mut S, _input: &I) -> Result<(), Error> { - Ok(()) - } } @@ -374,7 +351,7 @@ pub struct CmpFeedback<'a, VS, Addr, Code, By, Loc, SlotTy, Out, I, S, SC, CI> { /// a set of hashes of already encountered VMStates so that we don't re-analyze them known_states: HashSet, /// votable scheduler that can vote on whether a VMState is interesting or not - scheduler: &'a SC, + scheduler: SC, /// the VM providing information about the current execution vm: Rc>>, phantom: PhantomData<(Addr, Out)>, @@ -384,8 +361,7 @@ pub struct CmpFeedback<'a, VS, Addr, Code, By, Loc, SlotTy, Out, I, S, SC, CI> { impl<'a, VS, Addr, Code, By, Loc, SlotTy, Out, I, S, SC, CI> CmpFeedback<'a, VS, Addr, Code, By, Loc, SlotTy, Out, I, S, SC, CI> where - SC: Scheduler, InfantStateState> - + HasVote, InfantStateState>, + SC: Scheduler> + HasVote>, VS: Default + VMStateT, SlotTy: PartialOrd + Copy + TryFrom, Addr: Serialize + DeserializeOwned + Debug + Clone, @@ -396,7 +372,7 @@ where /// Create a new CmpFeedback. pub(crate) fn new( current_map: &'a mut [SlotTy], - scheduler: &'a SC, + scheduler: SC, vm: Rc>>, ) -> Self { Self { @@ -429,16 +405,16 @@ impl<'a, VS, Addr, Code, By, Loc, SlotTy, Out, I, S, SC, CI> Debug } #[cfg(feature = "cmp")] -impl<'a, VS, Addr, Code, By, Loc, SlotTy, Out, I, S, I0, S0, SC, CI> Feedback +impl<'a, VS, Addr, Code, By, Loc, SlotTy, Out, I, S, I0, S0, SC, CI> Feedback for CmpFeedback<'a, VS, Addr, Code, By, Loc, SlotTy, Out, I, S, SC, CI> where + I0: Input + VMInputT, S0: State + HasClientPerfMonitor + HasInfantStateState - + HasExecutionResult, - I0: Input + VMInputT, - SC: Scheduler, InfantStateState> - + HasVote, InfantStateState>, + + HasExecutionResult + + UsesInput, + SC: Scheduler> + HasVote>, VS: Default + VMStateT + 'static, SlotTy: PartialOrd + Copy, Addr: Serialize + DeserializeOwned + Debug + Clone, @@ -456,13 +432,13 @@ where &mut self, state: &mut S0, _manager: &mut EMI, - input: &I0, + input: &S0::Input, _observers: &OT, _exit_kind: &ExitKind, ) -> Result where - EMI: EventFirer, - OT: ObserversTuple, + EMI: EventFirer, + OT: ObserversTuple, { let mut cmp_interesting = false; let mut cov_interesting = false; @@ -503,16 +479,4 @@ where } Ok(false) } - - fn append_metadata( - &mut self, - _state: &mut S0, - _testcase: &mut Testcase, - ) -> Result<(), Error> { - Ok(()) - } - - fn discard_metadata(&mut self, _state: &mut S0, _input: &I0) -> Result<(), Error> { - Ok(()) - } } diff --git a/src/fuzzer.rs b/src/fuzzer.rs index 8f087ad3a..4bcf814c0 100644 --- a/src/fuzzer.rs +++ b/src/fuzzer.rs @@ -21,15 +21,16 @@ use libafl::{ fuzzer::Fuzzer, mark_feature_time, prelude::{ - current_time, Corpus, Event, EventConfig, EventManager, Executor, Feedback, HasObservers, - ObserversTuple, Testcase, + Corpus, Event, EventConfig, EventManager, Executor, Feedback, HasObservers, + ObserversTuple, Testcase, CorpusId, UsesInput, }, schedulers::Scheduler, stages::StagesTuple, start_timer, - state::{HasClientPerfMonitor, HasCorpus, HasExecutions, HasMetadata, HasSolutions}, + state::{HasClientPerfMonitor, HasCorpus, HasExecutions, HasMetadata, HasSolutions, UsesState, HasLastReportTime}, Error, Evaluator, ExecuteInputResult, }; +use libafl_bolts::current_time; use crate::evm::host::JMP_MAP; use crate::evm::input::ConciseEVMInput; @@ -62,17 +63,16 @@ pub static mut ORACLE_OUTPUT: Vec = vec![]; /// Addr: The address type (e.g., H160) /// Loc: The call target location type (e.g., H160) #[derive(Debug)] -pub struct ItyFuzzer<'a, VS, Loc, Addr, Out, CS, IS, F, IF, IFR, I, OF, S, OT, CI> +pub struct ItyFuzzer where - CS: Scheduler, - IS: Scheduler, InfantStateState> - + HasReportCorpus>, - F: Feedback, - IF: Feedback, - IFR: Feedback, + CS: Scheduler, + IS: Scheduler> + HasReportCorpus>, + F: Feedback, + IF: Feedback, + IFR: Feedback, I: VMInputT, - OF: Feedback, - S: HasClientPerfMonitor + HasCorpus + HasRand + HasMetadata, + OF: Feedback, + S: HasClientPerfMonitor + HasCorpus + HasRand + HasMetadata + UsesInput, VS: Default + VMStateT, Addr: Serialize + DeserializeOwned + Debug + Clone, Loc: Serialize + DeserializeOwned + Debug + Clone, @@ -87,7 +87,7 @@ where /// The feedback for the resultant state to be inserted into infant state corpus (e.g., dataflow, etc.) infant_result_feedback: IFR, /// The scheduler for the infant state corpus - infant_scheduler: &'a IS, + infant_scheduler: IS, /// The objective for the input corpus (e.g., oracles) objective: OF, /// Map from hash of a testcase can do (e.g., coverage map) to the (testcase idx, fav factor) @@ -98,18 +98,17 @@ where work_dir: String, } -impl<'a, VS, Loc, Addr, Out, CS, IS, F, IF, IFR, I, OF, S, OT, CI> - ItyFuzzer<'a, VS, Loc, Addr, Out, CS, IS, F, IF, IFR, I, OF, S, OT, CI> +impl + ItyFuzzer where - CS: Scheduler, - IS: Scheduler, InfantStateState> - + HasReportCorpus>, - F: Feedback, - IF: Feedback, - IFR: Feedback, + CS: Scheduler, + IS: Scheduler> + HasReportCorpus>, + F: Feedback, + IF: Feedback, + IFR: Feedback, I: VMInputT, - OF: Feedback, - S: HasClientPerfMonitor + HasCorpus + HasRand + HasMetadata, + OF: Feedback, + S: HasClientPerfMonitor + HasCorpus + HasRand + HasMetadata + UsesInput, VS: Default + VMStateT, Addr: Serialize + DeserializeOwned + Debug + Clone, Loc: Serialize + DeserializeOwned + Debug + Clone, @@ -118,7 +117,7 @@ where /// Creates a new ItyFuzzer pub fn new( scheduler: CS, - infant_scheduler: &'a IS, + infant_scheduler: IS, feedback: F, infant_feedback: IF, infant_result_feedback: IFR, @@ -189,27 +188,48 @@ where } } + +impl UsesState + for ItyFuzzer +where + CS: Scheduler, + IS: Scheduler> + HasReportCorpus>, + F: Feedback, + IF: Feedback, + IFR: Feedback, + I: VMInputT, + OF: Feedback, + S: HasClientPerfMonitor + HasCorpus + HasRand + HasMetadata + UsesInput, + VS: Default + VMStateT, + Addr: Serialize + DeserializeOwned + Debug + Clone, + Loc: Serialize + DeserializeOwned + Debug + Clone, + CI: Serialize + DeserializeOwned + Debug + Clone + ConciseSerde, +{ + type State = S; +} + /// Implement fuzzer trait for ItyFuzzer -impl<'a, VS, Loc, Addr, Out, CS, IS, E, EM, F, IF, IFR, I, OF, S, ST, OT, CI> - Fuzzer - for ItyFuzzer<'a, VS, Loc, Addr, Out, CS, IS, F, IF, IFR, I, OF, S, OT, CI> +impl Fuzzer + for ItyFuzzer where - CS: Scheduler, - IS: Scheduler, InfantStateState> - + HasReportCorpus>, - EM: EventManager, - F: Feedback, - IF: Feedback, - IFR: Feedback, + CS: Scheduler, + IS: Scheduler> + HasReportCorpus>, + E: Executor, + EM: EventManager, + F: Feedback, + IF: Feedback, + IFR: Feedback, I: VMInputT, - OF: Feedback, + OF: Feedback, S: HasClientPerfMonitor + HasExecutions + HasMetadata + HasCurrentInputIdx + HasRand - + HasCorpus, - ST: StagesTuple + ?Sized, + + HasCorpus + + HasLastReportTime + + UsesInput, + ST: StagesTuple, VS: Default + VMStateT, Addr: Serialize + DeserializeOwned + Debug + Clone, Loc: Serialize + DeserializeOwned + Debug + Clone, @@ -220,11 +240,11 @@ where &mut self, stages: &mut ST, executor: &mut E, - state: &mut S, + state: &mut EM::State, manager: &mut EM, - ) -> Result { + ) -> Result { let idx = self.scheduler.next(state)?; - state.set_current_input_idx(idx); + state.set_current_input_idx(idx.into()); // TODO: if the idx input is a concolic input returned by the solver // we should not perform all stages. @@ -241,15 +261,14 @@ where &mut self, stages: &mut ST, executor: &mut E, - state: &mut S, + state: &mut EM::State, manager: &mut EM, - ) -> Result { - let mut last = current_time(); + ) -> Result { // now report stats to manager every 0.1 sec let monitor_timeout = STATS_TIMEOUT_DEFAULT; loop { self.fuzz_one(stages, executor, state, manager)?; - last = manager.maybe_report_progress(state, last, monitor_timeout)?; + manager.maybe_report_progress(state, monitor_timeout)?; } } } @@ -341,29 +360,30 @@ macro_rules! dump_txn { } // implement evaluator trait for ItyFuzzer -impl<'a, VS, Loc, Addr, Out, E, EM, I, S, CS, IS, F, IF, IFR, OF, OT, CI> Evaluator - for ItyFuzzer<'a, VS, Loc, Addr, Out, CS, IS, F, IF, IFR, I, OF, S, OT, CI> +impl Evaluator + for ItyFuzzer where - CS: Scheduler, - IS: Scheduler, InfantStateState> - + HasReportCorpus>, - F: Feedback, - IF: Feedback, - IFR: Feedback, - E: Executor + HasObservers, - OT: ObserversTuple + serde::Serialize + serde::de::DeserializeOwned, - EM: EventManager, + CS: Scheduler, + IS: Scheduler> + HasReportCorpus>, + F: Feedback, + IF: Feedback, + IFR: Feedback, + E: Executor + HasObservers, + OT: ObserversTuple + serde::Serialize + serde::de::DeserializeOwned, + EM: EventManager, I: VMInputT, - OF: Feedback, + OF: Feedback, S: HasClientPerfMonitor - + HasCorpus - + HasSolutions + + HasCorpus + + HasSolutions + HasInfantStateState + HasItyState + HasExecutionResult + HasExecutions + HasMetadata - + HasRand, + + HasRand + + HasLastReportTime + + UsesInput, VS: Default + VMStateT, Addr: Serialize + DeserializeOwned + Debug + Clone, Loc: Serialize + DeserializeOwned + Debug + Clone, @@ -373,12 +393,12 @@ where /// Evaluate input (execution + feedback + objectives) fn evaluate_input_events( &mut self, - state: &mut S, + state: &mut Self::State, executor: &mut E, manager: &mut EM, - input: I, - send_events: bool, - ) -> Result<(ExecuteInputResult, Option), Error> { + input: ::Input, + send_events: bool + ) -> Result<(ExecuteInputResult, Option), Error> { start_timer!(state); executor.observers_mut().pre_exec_all(state, &input)?; mark_feature_time!(state, PerfFeature::PreExecObservers); @@ -426,8 +446,8 @@ where if is_infant_interesting && !reverted { state_idx = state.add_infant_state( &state.get_execution_result().new_state.clone(), - self.infant_scheduler, - input.get_state_idx(), + &mut self.infant_scheduler, + input.get_state_idx() ); if self @@ -459,16 +479,16 @@ where } } - let mut corpus_idx = 0; + let mut corpus_idx = CorpusId::from(0usize); if res == ExecuteInputResult::Corpus || res == ExecuteInputResult::Solution { // Add the input to the main corpus let mut testcase = Testcase::new(input.clone()); - self.feedback.append_metadata(state, &mut testcase)?; + self.feedback.append_metadata(state, observers, &mut testcase)?; corpus_idx = state.corpus_mut().add(testcase)?; self.infant_scheduler .report_corpus(state.get_infant_state_state(), state_idx); self.scheduler.on_add(state, corpus_idx)?; - self.on_add_corpus(&input, unsafe { &JMP_MAP }, corpus_idx); + self.on_add_corpus(&input, unsafe { &JMP_MAP }, corpus_idx.into()); } let final_res = match res { @@ -477,7 +497,7 @@ where self.objective.discard_metadata(state, &input)?; match self.should_replace(&input, unsafe { &JMP_MAP }) { Some((hash, new_fav_factor, old_testcase_idx)) => { - state.corpus_mut().remove(old_testcase_idx)?; + state.corpus_mut().remove(old_testcase_idx.into())?; let mut testcase = Testcase::new(input.clone()); let new_testcase_idx = state.corpus_mut().add(testcase)?; @@ -486,7 +506,7 @@ where self.scheduler.on_add(state, new_testcase_idx)?; self.on_replace_corpus( (hash, new_fav_factor, old_testcase_idx), - new_testcase_idx, + new_testcase_idx.into(), ); Ok((res, Some(new_testcase_idx))) @@ -508,7 +528,7 @@ where let observers_buf = if manager.configuration() == EventConfig::AlwaysUnique { None } else { - Some(manager.serialize_observers(observers)?) + manager.serialize_observers(observers)? }; manager.fire( state, @@ -520,6 +540,7 @@ where client_config: manager.configuration(), time: current_time(), executions: *state.executions(), + forward_id: None, }, )?; } @@ -556,11 +577,9 @@ where .expect("Unable to write data"); f.write_all(b"\n").expect("Unable to write data"); - state - .metadata_mut() - .get_mut::() - .unwrap() - .register_corpus_idx(corpus_idx); + state.metadata_map_mut().get_mut::().unwrap().register_corpus_idx( + corpus_idx.into() + ); #[cfg(feature = "print_txn_corpus")] { @@ -578,7 +597,7 @@ where // The input is a solution, add it to the respective corpus let mut testcase = Testcase::new(input.clone()); - self.objective.append_metadata(state, &mut testcase)?; + self.objective.append_metadata(state, observers, &mut testcase)?; state.solutions_mut().add(testcase)?; if send_events { @@ -602,11 +621,11 @@ where /// never called! fn add_input( &mut self, - _state: &mut S, + _state: &mut Self::State, _executor: &mut E, _manager: &mut EM, - _input: I, - ) -> Result { + _input: ::Input + ) -> Result { todo!() } } diff --git a/src/fuzzers/evm_fuzzer.rs b/src/fuzzers/evm_fuzzer.rs index c11da2897..42c9c32d7 100644 --- a/src/fuzzers/evm_fuzzer.rs +++ b/src/fuzzers/evm_fuzzer.rs @@ -13,16 +13,19 @@ use crate::{ evm::contract_utils::FIX_DEPLOYER, evm::host::FuzzHost, evm::vm::EVMExecutor, executor::FuzzExecutor, fuzzer::ItyFuzzer, }; -use glob::glob; use itertools::Itertools; use libafl::feedbacks::Feedback; -use libafl::prelude::{HasMetadata, ShMemProvider}; +use libafl::prelude::HasMetadata; use libafl::prelude::{QueueScheduler, SimpleEventManager}; use libafl::stages::{CalibrationStage, StdMutationalStage}; use libafl::{ - prelude::{tuple_list, MaxMapFeedback, SimpleMonitor, StdMapObserver}, + prelude::{MaxMapFeedback, SimpleMonitor, StdMapObserver}, Evaluator, Fuzzer, }; +use libafl_bolts::bolts_prelude::ShMemProvider; + +use glob::glob; +use libafl_bolts::tuples::tuple_list; use crate::evm::host::CALL_UNTIL; use crate::evm::host::{ @@ -59,7 +62,9 @@ use crate::evm::oracles::state_comp::StateCompOracle; use crate::evm::oracles::typed_bug::TypedBugOracle; use crate::evm::presets::pair::PairPreset; use crate::evm::srcmap::parser::BASE_PATH; -use crate::evm::types::{fixed_address, EVMAddress, EVMFuzzMutator, EVMFuzzState, EVMU256}; +use crate::evm::types::{ + fixed_address, EVMAddress, EVMFuzzMutator, EVMFuzzState, EVMQueueExecutor, EVMU256, +}; use crate::fuzzer::{REPLAY, RUN_FOREVER}; use crate::input::{ConciseSerde, VMInputT}; use crate::oracle::BugMetadata; @@ -102,17 +107,17 @@ pub fn evm_fuzzer( let monitor = SimpleMonitor::new(|s| println!("{}", s)); let mut mgr = SimpleEventManager::new(monitor); - let infant_scheduler = SortedDroppingScheduler::new(); + let mut infant_scheduler = SortedDroppingScheduler::new(); let mut scheduler = QueueScheduler::new(); let jmps = unsafe { &mut JMP_MAP }; let cmps = unsafe { &mut CMP_MAP }; let reads = unsafe { &mut READ_MAP }; let writes = unsafe { &mut WRITE_MAP }; - let jmp_observer = StdMapObserver::new("jmp", jmps); + let jmp_observer = unsafe { StdMapObserver::new("jmp", jmps) }; let deployer = fixed_address(FIX_DEPLOYER); - let mut fuzz_host = FuzzHost::new(Arc::new(scheduler.clone()), config.work_dir.clone()); + let mut fuzz_host = FuzzHost::new(scheduler.clone(), config.work_dir.clone()); fuzz_host.set_spec_id(config.spec_id); let onchain_middleware = match config.onchain.clone() { @@ -202,8 +207,7 @@ pub fn evm_fuzzer( fuzz_host.add_middlewares(Rc::new(RefCell::new(Sha3Bypass::new(sha3_taint.clone())))); } - let mut evm_executor: EVMExecutor = - EVMExecutor::new(fuzz_host, deployer); + let mut evm_executor: EVMQueueExecutor = EVMExecutor::new(fuzz_host, deployer); if config.replay_file.is_some() { // add coverage middleware for replay @@ -214,8 +218,8 @@ pub fn evm_fuzzer( let mut corpus_initializer = EVMCorpusInitializer::new( &mut evm_executor, - &mut scheduler, - &infant_scheduler, + scheduler.clone(), + infant_scheduler.clone(), state, config.work_dir.clone(), ); @@ -248,11 +252,12 @@ pub fn evm_fuzzer( // now evm executor is ready, we can clone it let evm_executor_ref = Rc::new(RefCell::new(evm_executor)); - if !state.metadata().contains::() { - state.metadata_mut().insert(ArtifactInfoMetadata::new()); + if !state.metadata_map().contains::() { + state.metadata_map_mut().insert(ArtifactInfoMetadata::new()); } + let meta = state - .metadata_mut() + .metadata_map_mut() .get_mut::() .unwrap(); for (addr, build_artifact) in &artifacts.build_artifacts { @@ -278,7 +283,7 @@ pub fn evm_fuzzer( config.concolic_caller, evm_executor_ref.clone(), ); - let mutator: EVMFuzzMutator<'_> = FuzzMutator::new(&infant_scheduler); + let mutator: EVMFuzzMutator = FuzzMutator::new(infant_scheduler.clone()); let std_stage = StdMutationalStage::new(mutator); @@ -300,7 +305,8 @@ pub fn evm_fuzzer( #[cfg(feature = "deployer_is_attacker")] state.add_caller(&deployer); - let infant_feedback = CmpFeedback::new(cmps, &infant_scheduler, evm_executor_ref.clone()); + let infant_feedback = + CmpFeedback::new(cmps, infant_scheduler.clone(), evm_executor_ref.clone()); let infant_result_feedback = DataflowFeedback::new(reads, writes); let mut oracles = config.oracle; @@ -384,7 +390,7 @@ pub fn evm_fuzzer( let mut fuzzer = ItyFuzzer::new( scheduler, - &infant_scheduler, + infant_scheduler, wrapped_feedback, infant_feedback, infant_result_feedback, diff --git a/src/fuzzers/move_fuzzer.rs b/src/fuzzers/move_fuzzer.rs index 9cd123df6..8e804a9a1 100644 --- a/src/fuzzers/move_fuzzer.rs +++ b/src/fuzzers/move_fuzzer.rs @@ -13,13 +13,15 @@ use crate::{ executor::FuzzExecutor, fuzzer::ItyFuzzer, }; use libafl::feedbacks::Feedback; -use libafl::prelude::{HasMetadata, MapFeedback, ShMemProvider}; +use libafl::prelude::{HasMetadata, MapFeedback}; +use libafl_bolts::bolts_prelude::ShMemProvider; use libafl::prelude::{QueueScheduler, SimpleEventManager}; use libafl::stages::{CalibrationStage, StdMutationalStage}; use libafl::{ - prelude::{tuple_list, MaxMapFeedback, SimpleMonitor, StdMapObserver}, + prelude::{MaxMapFeedback, SimpleMonitor, StdMapObserver}, Evaluator, Fuzzer, }; +use libafl_bolts::tuples::tuple_list; use glob::glob; use itertools::Itertools; use crate::feedback::{CmpFeedback, DataflowFeedback, OracleFeedback}; @@ -72,29 +74,27 @@ pub fn move_fuzzer( MoveCorpusInitializer::new( &mut state, &mut vm, - &scheduler, - &infant_scheduler, + scheduler.clone(), + infant_scheduler.clone(), ).setup(vec![config.target.clone()]); } let vm_ref = Rc::new(RefCell::new(vm)); - let jmp_observer = StdMapObserver::new("jmp", vm_ref.borrow().get_jmp()); - let mut feedback: - MapFeedback - = MaxMapFeedback::new(&jmp_observer); + let jmp_observer = unsafe { StdMapObserver::new("jmp", vm_ref.borrow().get_jmp()) }; + let mut feedback: MapFeedback<_, _, _, MoveFuzzState, _> = MaxMapFeedback::new(&jmp_observer); feedback .init_state(&mut state) .expect("Failed to init state"); - let mutator = MoveFuzzMutator::new(&infant_scheduler); + let mutator = MoveFuzzMutator::new(infant_scheduler.clone()); let std_stage = StdMutationalStage::new(mutator); let mut stages = tuple_list!(std_stage); let mut executor = FuzzExecutor::new(vm_ref.clone(), tuple_list!(jmp_observer)); - let infant_feedback = CmpFeedback::new(vm_ref.borrow().get_cmp(), &infant_scheduler, vm_ref.clone()); + let infant_feedback = CmpFeedback::new(vm_ref.borrow().get_cmp(), infant_scheduler.clone(), vm_ref.clone()); let infant_result_feedback = DataflowFeedback::new(vm_ref.borrow().get_read(), vm_ref.borrow().get_write()); let mut oracles: Vec>>> = vec![ @@ -108,7 +108,7 @@ pub fn move_fuzzer( // let mut fuzzer = ItyFuzzer::new( scheduler, - &infant_scheduler, + infant_scheduler, feedback, infant_feedback, infant_result_feedback, diff --git a/src/generic_vm/vm_state.rs b/src/generic_vm/vm_state.rs index 1b276ff66..d01a61abe 100644 --- a/src/generic_vm/vm_state.rs +++ b/src/generic_vm/vm_state.rs @@ -1,3 +1,4 @@ +use libafl::prelude::UsesInput; use serde::{de::DeserializeOwned, Serialize}; use std::fmt::Debug; use primitive_types::H256; diff --git a/src/indexed_corpus.rs b/src/indexed_corpus.rs index fe9c5d23a..2f5dc517d 100644 --- a/src/indexed_corpus.rs +++ b/src/indexed_corpus.rs @@ -1,21 +1,21 @@ /// A corpus in memory with self-incementing indexes for items. use core::cell::RefCell; -use std::collections::HashMap; +use std::cell::{Ref, RefMut}; use serde::{Deserialize, Serialize}; use libafl::{ corpus::{Corpus, Testcase}, - inputs::Input, - Error, + inputs::{Input, UsesInput}, + Error, prelude::{HasTestcase, CorpusId}, }; pub trait HasIndexed {} impl HasIndexed for IndexedInMemoryCorpus where I: Input {} /// A corpus in memory with self-incementing indexes for items. -#[derive(Clone, Debug, Default, Deserialize, Serialize)] +#[derive(Clone, Debug, Deserialize, Serialize)] #[serde(bound = "I: Input")] pub struct IndexedInMemoryCorpus where @@ -28,11 +28,17 @@ where /// Current index current_idx: usize, /// Current testcase scheduled - current: Option, + current: Option, } +impl UsesInput for IndexedInMemoryCorpus +where + I: Input, +{ + type Input = I; +} -impl Corpus for IndexedInMemoryCorpus +impl Corpus for IndexedInMemoryCorpus where I: Input, { @@ -44,14 +50,15 @@ where /// Add an entry to the corpus and return its index #[inline] - fn add(&mut self, testcase: Testcase) -> Result { + fn add(&mut self, testcase: Testcase) -> Result { self.entries.push(RefCell::new(testcase)); - Ok(self.entries.len() - 1) + Ok(CorpusId::from(self.entries.len() - 1)) } /// Replaces the testcase at the given idx #[inline] - fn replace(&mut self, idx: usize, testcase: Testcase) -> Result, Error> { + fn replace(&mut self, idx: CorpusId, testcase: Testcase) -> Result, Error> { + let idx = usize::from(idx); if idx >= self.entries.len() { return Err(Error::key_not_found(format!("Index {idx} out of bounds"))); } @@ -61,15 +68,15 @@ where /// Removes an entry from the corpus, returning it if it was present. #[inline] - fn remove(&mut self, idx: usize) -> Result>, Error> { - self.entries[idx] = self.dummy.clone(); - Ok(None) + fn remove(&mut self, idx: CorpusId) -> Result, Error> { + let dummy = self.dummy.borrow().clone(); + self.replace(idx, dummy) } /// Get by id #[inline] - fn get(&self, idx: usize) -> Result<&RefCell>, Error> { - match self.entries.get(idx) { + fn get(&self, idx: CorpusId) -> Result<&RefCell>, Error> { + match self.entries.get(usize::from(idx)) { Some(entry) => Ok(entry), _ => Err(Error::key_not_found(format!("Index {idx} out of bounds"))), } @@ -77,15 +84,85 @@ where /// Current testcase scheduled #[inline] - fn current(&self) -> &Option { + fn current(&self) -> &Option { &self.current } /// Current testcase scheduled (mutable) #[inline] - fn current_mut(&mut self) -> &mut Option { + fn current_mut(&mut self) -> &mut Option { &mut self.current } + + #[inline] + fn next(&self, idx: CorpusId) -> Option { + let idx = usize::from(idx); + if idx < self.count() - 1 { + Some(CorpusId::from(idx + 1)) + } else { + None + } + } + + #[inline] + fn prev(&self, idx: CorpusId) -> Option { + let idx = usize::from(idx); + if idx > 0 && idx < self.count(){ + Some(CorpusId::from(idx - 1)) + } else { + None + } + } + + #[inline] + fn first(&self) -> Option { + if self.count() > 0 { + Some(CorpusId::from(0usize)) + } else { + None + } + } + + #[inline] + fn last(&self) -> Option { + if self.count() > 0 { + Some(CorpusId::from(self.count() - 1)) + } else { + None + } + } + + #[inline] + fn nth(&self, nth: usize) -> CorpusId { + CorpusId::from(nth) + } + + #[inline] + fn load_input_into(&self, _: &mut Testcase) -> Result<(), Error> { + Ok(()) + } + + #[inline] + fn store_input_from(&self, _: &Testcase) -> Result<(), Error> { + Ok(()) + } +} + +impl HasTestcase for IndexedInMemoryCorpus +where + I: Input, +{ + /// Shorthand to receive a [`Ref`] to a stored [`Testcase`], by [`CorpusId`]. + /// For a normal state, this should return a [`Testcase`] in the corpus, not the objectives. + fn testcase(&self, id: CorpusId) -> Result::Input>>, Error> { + Ok(self.get(id)?.borrow()) + } + + /// Shorthand to receive a [`RefMut`] to a stored [`Testcase`], by [`CorpusId`]. + /// For a normal state, this should return a [`Testcase`] in the corpus, not the objectives. + fn testcase_mut(&self, id: CorpusId) -> Result::Input>>, Error> { + Ok(self.get(id)?.borrow_mut()) + } } impl IndexedInMemoryCorpus diff --git a/src/move/corpus_initializer.rs b/src/move/corpus_initializer.rs index bb2305e80..837e6a86a 100644 --- a/src/move/corpus_initializer.rs +++ b/src/move/corpus_initializer.rs @@ -6,7 +6,7 @@ use std::time::Duration; use glob::glob; use itertools::Itertools; use libafl::corpus::{Corpus, Testcase}; -use libafl::prelude::Rand; +use libafl_bolts::prelude::Rand; use libafl::schedulers::Scheduler; use libafl::state::{HasCorpus, HasMetadata, HasRand, State}; use move_binary_format::access::ModuleAccess; @@ -40,11 +40,15 @@ pub enum MoveInputStatus { DependentOnStructs(Value, Vec), } -pub struct MoveCorpusInitializer<'a> { +pub struct MoveCorpusInitializer<'a, SC, ISC> +where + SC: Scheduler, + ISC: Scheduler, +{ pub state: &'a mut MoveFuzzState, pub executor: &'a mut movevm::MoveVM, - pub scheduler: &'a dyn Scheduler, - pub infant_scheduler: &'a dyn Scheduler, + pub scheduler: SC, + pub infant_scheduler: ISC, pub default_state: MoveStagedVMState, } @@ -55,14 +59,17 @@ pub fn is_tx_context(struct_tag: &StructTag) -> bool { ) && struct_tag.module == TX_CONTEXT_MODULE_NAME.into() && struct_tag.name == TX_CONTEXT_STRUCT_NAME.into() } -impl<'a> MoveCorpusInitializer<'a> +impl<'a, SC, ISC> MoveCorpusInitializer<'a, SC, ISC> +where + SC: Scheduler, + ISC: Scheduler, { pub fn new( state: &'a mut MoveFuzzState, executor: &'a mut movevm::MoveVM, - scheduler: &'a dyn Scheduler, - infant_scheduler: &'a dyn Scheduler, + scheduler: SC, + infant_scheduler: ISC, ) -> Self { Self { state, @@ -87,9 +94,9 @@ impl<'a> MoveCorpusInitializer<'a> self.state.add_caller(&AccountAddress::random()); // add metadata - self.state.metadata_mut().insert(StructAbilities::new()); - self.state.metadata_mut().insert(ConstantPoolMetadata::new()); - self.state.infant_states_state.metadata_mut().insert(MoveSchedulerMeta::new()); + self.state.metadata_map_mut().insert(StructAbilities::new()); + self.state.metadata_map_mut().insert(ConstantPoolMetadata::new()); + self.state.infant_states_state.metadata_map_mut().insert(MoveSchedulerMeta::new()); // setup infant scheduler & corpus self.default_state = StagedVMState::new_with_state( @@ -137,7 +144,7 @@ impl<'a> MoveCorpusInitializer<'a> } fn extract_constants(&mut self, module: &CompiledModule) { - let constant_pool = self.state.metadata_mut() + let constant_pool = self.state.metadata_map_mut() .get_mut::() .expect("failed to get constant pool metadata"); @@ -440,7 +447,7 @@ impl<'a> MoveCorpusInitializer<'a> let mut resolved = true; let mut deps = HashMap::new(); let type_tag_info = self.state - .metadata() + .metadata_map() .get::() .expect("type tag info not found") .clone(); @@ -483,4 +490,4 @@ impl<'a> MoveCorpusInitializer<'a> // println!("input: {:?}", input); return Some(input); } -} \ No newline at end of file +} diff --git a/src/move/input.rs b/src/move/input.rs index ebea354b4..5bcbfef0f 100644 --- a/src/move/input.rs +++ b/src/move/input.rs @@ -5,7 +5,7 @@ use crate::r#move::vm_state::{Gate, MoveVMState, MoveVMStateT}; use crate::state::{HasCaller, HasItyState}; use libafl::inputs::Input; -use libafl::prelude::{HasBytesVec, HasMaxSize, HasMetadata, MutationResult, Rand, State}; +use libafl::prelude::{HasBytesVec, HasMaxSize, HasMetadata, MutationResult, State}; use libafl::state::HasRand; use std::rc::Rc; use move_core_types::account_address::AccountAddress; @@ -23,7 +23,7 @@ use std::fmt::Debug; use std::ops::{Deref, DerefMut}; use std::sync::Arc; use itertools::Itertools; -use libafl::impl_serdeany; +use libafl_bolts::{impl_serdeany, prelude::Rand}; use move_binary_format::file_format::AbilitySet; use move_vm_runtime::loader::Function; use move_vm_types::loaded_data::runtime_types::Type; @@ -286,7 +286,7 @@ impl MoveFunctionInputT for MoveFunctionInput { for (arg, ty) in self.args.iter_mut() .zip(self.function_info.get_function().parameter_types.iter()) { - if state.metadata().get::().expect("type tag info").is_tx_context(&ty) { + if state.metadata_map().get::().expect("type tag info").is_tx_context(&ty) { continue; } @@ -731,7 +731,7 @@ impl VMInputT for MoveF } let nth = _state.rand_mut().below(self.args.len() as u64) as usize; let ty = self.function_info.get_function().parameter_types[nth].clone(); - if _state.metadata().get::().expect("type tag info").is_tx_context(&ty) { + if _state.metadata_map().get::().expect("type tag info").is_tx_context(&ty) { return MutationResult::Skipped; } @@ -857,6 +857,7 @@ mod tests { use move_binary_format::file_format::{Ability, AbilitySet}; use crate::r#move::types::MoveStagedVMState; + macro_rules! get_dummy_func { ($tys: expr) => { { @@ -992,7 +993,7 @@ mod tests { ($init_v: expr, $tys: expr, $sstate: expr, $struct_abilities: expr) => { { let mut state: MoveFuzzState = Default::default(); - state.metadata_mut().insert($struct_abilities); + state.metadata_map_mut().insert($struct_abilities); let (v, res) = { let mut v = dummy_input!($init_v, $sstate, $tys); @@ -1199,4 +1200,3 @@ mod tests { } - diff --git a/src/move/movevm.rs b/src/move/movevm.rs index 5915eb17f..64e1ef9c0 100644 --- a/src/move/movevm.rs +++ b/src/move/movevm.rs @@ -28,7 +28,7 @@ use serde::{Deserialize, Serialize}; use std::collections::{BTreeMap, HashMap, VecDeque}; use std::fmt::Debug; use std::sync::Arc; -use libafl::impl_serdeany; +use libafl_bolts::impl_serdeany; use libafl::state::HasMetadata; use move_binary_format::errors::{PartialVMResult, VMResult}; use move_binary_format::file_format::Bytecode; @@ -416,8 +416,8 @@ impl ConciseMoveInput > for MoveVM where - I: VMInputT + MoveFunctionInputT, - S: HasMetadata + HasCaller, + I: VMInputT + MoveFunctionInputT + 'static, + S: HasMetadata + HasCaller + 'static, { fn deploy( &mut self, @@ -428,11 +428,11 @@ where ) -> Option { // println!("deploying module dep: {:?}", module.self_id()); - if !state.metadata_mut().contains::() { - state.metadata_mut().insert(TypeTagInfoMeta::new()); + if !state.metadata_map_mut().contains::() { + state.metadata_map_mut().insert(TypeTagInfoMeta::new()); } - let meta = state.metadata_mut().get_mut::().unwrap(); + let meta = state.metadata_map_mut().get_mut::().unwrap(); let func_off = self.loader.module_cache.read().functions.len(); let module_name = module.name().to_owned(); diff --git a/src/move/mutator.rs b/src/move/mutator.rs index e5184cd7c..5c5edeea0 100644 --- a/src/move/mutator.rs +++ b/src/move/mutator.rs @@ -2,7 +2,8 @@ use std::fmt::Debug; use libafl::Error; use libafl::inputs::Input; use libafl::mutators::{MutationResult, Mutator}; -use libafl::prelude::{HasMaxSize, HasMetadata, HasRand, Rand, Scheduler, State}; +use libafl::prelude::{HasMaxSize, HasMetadata, HasRand, Scheduler, State}; +use libafl_bolts::{Named, prelude::Rand}; use serde::de::DeserializeOwned; use serde::Serialize; use crate::generic_vm::vm_state::VMStateT; @@ -12,27 +13,27 @@ use crate::r#move::vm_state::MoveVMStateT; use crate::state::{HasCaller, HasItyState, InfantStateState}; use crate::state_input::StagedVMState; -pub struct MoveFuzzMutator<'a, VS, Loc, Addr, SC, CI> +pub struct MoveFuzzMutator where VS: Default + VMStateT, - SC: Scheduler, InfantStateState>, + SC: Scheduler>, Addr: Serialize + DeserializeOwned + Debug + Clone, Loc: Serialize + DeserializeOwned + Debug + Clone, CI: Serialize + DeserializeOwned + Debug + Clone + ConciseSerde, { - pub infant_scheduler: &'a SC, + pub infant_scheduler: SC, pub phantom: std::marker::PhantomData<(VS, Loc, Addr, CI)>, } -impl<'a, VS, Loc, Addr, SC, CI> MoveFuzzMutator<'a, VS, Loc, Addr, SC, CI> +impl MoveFuzzMutator where VS: Default + VMStateT, - SC: Scheduler, InfantStateState>, + SC: Scheduler>, Addr: Serialize + DeserializeOwned + Debug + Clone, Loc: Serialize + DeserializeOwned + Debug + Clone, CI: Serialize + DeserializeOwned + Debug + Clone + ConciseSerde, { - pub fn new(infant_scheduler: &'a SC) -> Self { + pub fn new(infant_scheduler: SC) -> Self { Self { infant_scheduler, phantom: Default::default(), @@ -40,11 +41,24 @@ impl<'a, VS, Loc, Addr, SC, CI> MoveFuzzMutator<'a, VS, Loc, Addr, SC, CI> } } -impl<'a, VS, Loc, Addr, I, S, SC, CI> Mutator for MoveFuzzMutator<'a, VS, Loc, Addr, SC, CI> +impl Named for MoveFuzzMutator +where + VS: Default + VMStateT, + SC: Scheduler>, + Addr: Serialize + DeserializeOwned + Debug + Clone, + Loc: Serialize + DeserializeOwned + Debug + Clone, + CI: Serialize + DeserializeOwned + Debug + Clone + ConciseSerde, +{ + fn name(&self) -> &str { + "MoveFuzzMutator" + } +} + +impl Mutator for MoveFuzzMutator where I: VMInputT + Input + MoveFunctionInputT, S: State + HasRand + HasMaxSize + HasItyState + HasCaller + HasMetadata, - SC: Scheduler, InfantStateState>, + SC: Scheduler>, VS: Default + VMStateT + MoveVMStateT, Addr: PartialEq + Debug + Serialize + DeserializeOwned + Clone, Loc: Serialize + DeserializeOwned + Debug + Clone, @@ -58,7 +72,7 @@ impl<'a, VS, Loc, Addr, I, S, SC, CI> Mutator for MoveFuzzMutator<'a, VS, ) -> Result { // If the state is not initialized, initialize it if !input.get_staged_state().initialized { - let concrete = state.get_infant_state(self.infant_scheduler).unwrap(); + let concrete = state.get_infant_state(&mut self.infant_scheduler).unwrap(); input.set_staged_state(concrete.1, concrete.0); } @@ -75,7 +89,7 @@ impl<'a, VS, Loc, Addr, I, S, SC, CI> Mutator for MoveFuzzMutator<'a, VS, // cross over infant state // we need power schedule here for infant states let old_idx = input.get_state_idx(); - let (idx, new_state) = state.get_infant_state(self.infant_scheduler).unwrap(); + let (idx, new_state) = state.get_infant_state(&mut self.infant_scheduler).unwrap(); if idx == old_idx { return MutationResult::Skipped; } @@ -111,14 +125,4 @@ impl<'a, VS, Loc, Addr, I, S, SC, CI> Mutator for MoveFuzzMutator<'a, VS, } Ok(res) } - - fn post_exec( - &mut self, - _state: &mut S, - _stage_idx: i32, - _corpus_idx: Option, - ) -> Result<(), Error> { - // todo!() - Ok(()) - } } diff --git a/src/move/oracles/typed_bug.rs b/src/move/oracles/typed_bug.rs index d78c6acbb..7763dcdcc 100644 --- a/src/move/oracles/typed_bug.rs +++ b/src/move/oracles/typed_bug.rs @@ -2,6 +2,7 @@ use crate::oracle::{Oracle, OracleCtx, Producer}; use crate::state::HasExecutionResult; use bytes::Bytes; use primitive_types::{H160, H256, U256}; +use serde_json::json; use std::borrow::Borrow; use std::cell::RefCell; use std::collections::hash_map::DefaultHasher; @@ -42,11 +43,11 @@ for TypedBugOracle { ) -> Vec { if ctx.post_state.typed_bug.len() > 0 { unsafe { - ORACLE_OUTPUT += format!( - "[typed_bug] {:?} hit at module {:?}\n", - ctx.post_state.typed_bug, - ctx.input.module - ).as_str(); + let msg = json!({ + "typed_bug": ctx.post_state.typed_bug, + "module": ctx.input.module, + }); + ORACLE_OUTPUT.push(msg); } ctx.post_state.typed_bug.iter().map(|bug_id| { let mut hasher = DefaultHasher::new(); diff --git a/src/move/scheduler.rs b/src/move/scheduler.rs index 377f67ecb..027371cff 100644 --- a/src/move/scheduler.rs +++ b/src/move/scheduler.rs @@ -1,9 +1,12 @@ use std::collections::HashSet; use itertools::Itertools; use libafl::corpus::{Corpus, Testcase}; -use libafl::{Error, impl_serdeany}; +use libafl::Error; use libafl::inputs::Input; -use libafl::prelude::{HasCorpus, HasMetadata, HasRand, Rand, Scheduler}; +use libafl::state::UsesState; +use libafl::prelude::{HasCorpus, HasMetadata, HasRand, CorpusId, UsesInput}; +use libafl_bolts::{impl_serdeany, prelude::Rand}; +use libafl::schedulers::{Scheduler, RemovableScheduler}; use move_vm_types::loaded_data::runtime_types::Type; use revm_primitives::HashMap; use serde::{Deserialize, Serialize}; @@ -116,7 +119,7 @@ impl MoveSchedulerMeta { } } - +#[derive(Debug, Clone)] pub struct MoveTestcaseScheduler { pub inner: SC, } @@ -125,17 +128,24 @@ pub struct MoveTestcaseScheduler { impl MoveTestcaseScheduler { } +impl UsesState for MoveTestcaseScheduler +where + SC: UsesState, +{ + type State = SC::State; +} -impl Scheduler for MoveTestcaseScheduler - where SC: Scheduler +impl Scheduler for MoveTestcaseScheduler +where + SC: Scheduler, { - fn on_add(&self, _state: &mut MoveFuzzState, _idx: usize) -> Result<(), Error> { + fn on_add(&mut self, _state: &mut Self::State, _idx: CorpusId) -> Result<(), Error> { let tc = _state.corpus().get(_idx).expect("Missing testcase"); let input = tc.borrow().input().clone().expect("Missing input"); - let meta = _state.infant_states_state.metadata_mut().get_mut::().expect("Missing metadata"); + let meta = _state.infant_states_state.metadata_map_mut().get_mut::().expect("Missing metadata"); meta.testcase_to_deps .insert( - _idx, + _idx.into(), input._deps .iter() .map(|(ty, amount)| TypeWithAmount::new(ty.clone(), *amount)) @@ -144,12 +154,12 @@ impl Scheduler for MoveTestcaseScheduler Result { + fn next(&mut self, state: &mut Self::State) -> Result { let mut next_idx = self.inner.next(state)?; loop { let tc = state.corpus().get(next_idx).expect("Missing testcase"); let input = tc.borrow().input().clone().expect("Missing input"); - let mut meta = state.infant_states_state.metadata_mut().get_mut::().expect("Missing metadata"); + let mut meta = state.infant_states_state.metadata_map_mut().get_mut::().expect("Missing metadata"); if input._deps.len() == 0 { meta.current_working_states = meta.all_states.clone(); break; @@ -169,20 +179,20 @@ impl Scheduler for MoveTestcaseScheduler() .expect("Missing metadata"); - meta.current_idx = next_idx; - Ok(next_idx) + meta.current_idx = next_idx.into(); + Ok(next_idx.into()) } } - +#[derive(Debug, Clone)] pub struct MoveVMStateScheduler { - pub inner: SortedDroppingScheduler + pub inner: SortedDroppingScheduler } -impl HasVote for MoveVMStateScheduler { +impl HasVote for MoveVMStateScheduler { fn vote(&self, state: &mut MoveInfantStateState, idx: usize, amount: usize) { self.inner.vote(state, idx, amount) } @@ -198,9 +208,12 @@ impl HasReportCorpus for MoveVMStateScheduler { } } +impl UsesState for MoveVMStateScheduler { + type State = MoveInfantStateState; +} -impl Scheduler for MoveVMStateScheduler { - fn on_add(&self, state: &mut MoveInfantStateState, idx: usize) -> Result<(), Error> { +impl Scheduler for MoveVMStateScheduler { + fn on_add(&mut self, state: &mut Self::State, idx: CorpusId) -> Result<(), Error> { let interesting_types = state.corpus().get(idx).expect("Missing infant state") .borrow() .input() @@ -211,9 +224,9 @@ impl Scheduler for MoveVMStateScheduler .iter() .map(|(ty, amount)| TypeWithAmount::new(ty.clone(), amount.iter().map(|(_, amt)| amt).sum::())) .collect_vec(); - let mut meta = state.metadata_mut().get_mut::().expect("Missing metadata"); - meta.all_states.insert(idx); - let entry = meta.state_to_deps.entry(idx).or_insert(Default::default()); + let mut meta = state.metadata_map_mut().get_mut::().expect("Missing metadata"); + meta.all_states.insert(idx.into()); + let entry = meta.state_to_deps.entry(idx.into()).or_insert(Default::default()); interesting_types.iter().for_each( |v| { entry.insert(v.clone()); @@ -221,45 +234,34 @@ impl Scheduler for MoveVMStateScheduler ); for ta in interesting_types{ - meta.add_type(ta, idx); + meta.add_type(ta, idx.into()); } - let res = self.inner.on_add(state, idx); { - let votes_meta = state.metadata_mut().get_mut::().expect("Missing metadata"); + let votes_meta = state.metadata_map_mut().get_mut::().expect("Missing metadata"); if votes_meta.to_remove.len() > 0 { let to_remove = votes_meta.to_remove.clone(); votes_meta.to_remove.clear(); for idx in to_remove { - self.on_remove(state, idx, &None).expect("Failed to remove"); + self.on_remove(state, idx.into(), &None).expect("Failed to remove"); } } } res } - fn on_remove(&self, state: &mut MoveInfantStateState, idx: usize, _testcase: &Option>) -> Result<(), Error> { - let mut meta = state.metadata_mut().get_mut::().expect("Missing metadata"); - meta.remove_type(idx); - meta.state_to_deps.remove(&idx); - meta.all_states.remove(&idx); - meta.current_working_states.remove(&idx); - - Ok(()) - } - - fn next(&self, state: &mut MoveInfantStateState) -> Result { + fn next(&mut self, state: &mut Self::State) -> Result { let mut sample_idx = HashSet::new(); { - let mut meta = state.metadata_mut().get_mut::().expect("Missing metadata"); + let mut meta = state.metadata_map_mut().get_mut::().expect("Missing metadata"); sample_idx = meta.current_working_states.clone(); } let mut total_votes = 0; let mut sample_list = vec![]; { - let mut sampling_meta = state.metadata().get::().unwrap(); + let mut sampling_meta = state.metadata_map().get::().unwrap(); for idx in sample_idx { let (votes, visits) = sampling_meta.votes_and_visits.get(&idx).unwrap(); sample_list.push((idx, (*votes, *visits))); @@ -285,13 +287,32 @@ impl Scheduler for MoveVMStateScheduler } { - let sampling_meta = state.metadata_mut().get_mut::().unwrap(); + let sampling_meta = state.metadata_map_mut().get_mut::().unwrap(); sampling_meta.votes_and_visits.get_mut(&idx).unwrap().1 += 1; sampling_meta.visits_total += 1; } - Ok(idx) + Ok(idx.into()) } } +impl RemovableScheduler for MoveVMStateScheduler +where + Self::State: UsesInput, +{ + fn on_remove( + &mut self, + state: &mut Self::State, + idx: CorpusId, + _testcase: &Option::Input>> + ) -> Result<(), Error> { + let idx = usize::from(idx); + let mut meta = state.metadata_map_mut().get_mut::().expect("Missing metadata"); + meta.remove_type(idx); + meta.state_to_deps.remove(&idx); + meta.all_states.remove(&idx); + meta.current_working_states.remove(&idx); + Ok(()) + } +} diff --git a/src/move/vm_state.rs b/src/move/vm_state.rs index a20da654c..c02a39c9a 100644 --- a/src/move/vm_state.rs +++ b/src/move/vm_state.rs @@ -18,7 +18,8 @@ use std::collections::hash_map::DefaultHasher; use std::collections::HashMap; use std::hash::{Hash, Hasher}; use std::ops::Index; -use libafl::prelude::{HasMetadata, Rand}; +use libafl::prelude::HasMetadata; +use libafl_bolts::prelude::Rand; use libafl::state::HasRand; use move_binary_format::file_format::AbilitySet; use move_core_types::identifier::IdentStr; @@ -190,10 +191,10 @@ impl MoveVMState { } if !state.has_metadata::() { - state.metadata_mut().insert(StructAbilities::new()); + state.metadata_map_mut().insert(StructAbilities::new()); } - state.metadata_mut().get_mut::().unwrap().set_ability( + state.metadata_map_mut().get_mut::().unwrap().set_ability( ty.clone(), abilities, ); @@ -243,7 +244,7 @@ impl MoveVMState { self.ref_in_use.push((ty.clone(), val.clone())); } else { let struct_abilities = state - .metadata() + .metadata_map() .get::() .expect("StructAbilities not found") .get_ability(ty) @@ -264,7 +265,7 @@ impl MoveVMState { /// Used by mutator when trying to mutate a struct. pub fn restock_args(&mut self, ty: &Type, value: GatedValue, is_ref: bool, state: &mut S) where S: HasMetadata { - if state.metadata().get::().unwrap().is_tx_context(ty) { + if state.metadata_map().get::().unwrap().is_tx_context(ty) { return; } @@ -276,7 +277,7 @@ impl MoveVMState { self.ref_in_use.remove(offset); } let struct_abilities = state - .metadata() + .metadata_map() .get::() .expect("StructAbilities not found") .get_ability(ty) @@ -299,7 +300,7 @@ impl MoveVMState { pub fn restock_struct(&mut self, ty: &Type, value: Value, ret_ty: &Gate, state: &mut S) where S: HasMetadata { - if state.metadata().get::().unwrap().is_tx_context(ty) { + if state.metadata_map().get::().unwrap().is_tx_context(ty) { return; } @@ -320,7 +321,7 @@ impl MoveVMState { if !ret_ty.is_ref() { println!("looking for struct abilities for {:?} {:?}", value, ty); let struct_abilities = state - .metadata() + .metadata_map() .get::() .expect("StructAbilities not found") .get_ability(ty) diff --git a/src/mutation_utils.rs b/src/mutation_utils.rs index 4d5ca423d..8cb3e3e2a 100644 --- a/src/mutation_utils.rs +++ b/src/mutation_utils.rs @@ -3,14 +3,15 @@ use crate::input::VMInputT; use libafl::inputs::{HasBytesVec, Input}; use libafl::mutators::MutationResult; use libafl::prelude::{ - tuple_list, BitFlipMutator, ByteAddMutator, ByteDecMutator, ByteFlipMutator, ByteIncMutator, + BitFlipMutator, ByteAddMutator, ByteDecMutator, ByteFlipMutator, ByteIncMutator, ByteInterestingMutator, ByteNegMutator, ByteRandMutator, BytesCopyMutator, BytesExpandMutator, BytesInsertMutator, BytesRandInsertMutator, BytesRandSetMutator, BytesSetMutator, - BytesSwapMutator, DwordAddMutator, DwordInterestingMutator, HasMetadata, Mutator, Named, - QwordAddMutator, Rand, StdScheduledMutator, WordAddMutator, WordInterestingMutator, + BytesSwapMutator, DwordAddMutator, DwordInterestingMutator, HasMetadata, Mutator, + QwordAddMutator, StdScheduledMutator, WordAddMutator, WordInterestingMutator, }; +use libafl_bolts::{impl_serdeany, Named, prelude::Rand, tuples::tuple_list}; use libafl::state::{HasMaxSize, HasRand, State}; -use libafl::{impl_serdeany, Error}; +use libafl::Error; use serde::{Deserialize, Serialize}; use std::collections::HashMap; @@ -73,9 +74,9 @@ where ) -> Result { let idx = state.rand_mut().next() as usize; - let constant = match state.metadata().get::() { - Some(meta) if !meta.constants.is_empty() => unsafe { - meta.constants.get_unchecked(idx % meta.constants.len()) + let constant = match state.metadata_map().get::() { + Some(meta) if !meta.constants.is_empty() => unsafe { + meta.constants.get_unchecked(idx % meta.constants.len()) }, _ => return Ok(MutationResult::Skipped), }; diff --git a/src/oracle.rs b/src/oracle.rs index 0f140047b..63fd9a0a6 100644 --- a/src/oracle.rs +++ b/src/oracle.rs @@ -5,7 +5,8 @@ use crate::generic_vm::vm_state::VMStateT; use crate::input::{ConciseSerde, VMInputT}; use crate::state::HasExecutionResult; -use libafl::prelude::{HasCorpus, HasMetadata, SerdeAnyMap}; +use libafl::prelude::{HasCorpus, HasMetadata}; +use libafl_bolts::bolts_prelude::SerdeAnyMap; use libafl::state::State; use serde::de::DeserializeOwned; use serde::{Serialize, Deserialize}; @@ -15,7 +16,7 @@ use std::fmt::Debug; use std::marker::PhantomData; use std::ops::Deref; use std::rc::Rc; -use libafl::impl_serdeany; +use libafl_bolts::impl_serdeany; /// The context passed to the oracle pub struct OracleCtx<'a, VS, Addr, Code, By, Loc, SlotTy, Out, I, S: 'static, CI> @@ -46,7 +47,7 @@ impl<'a, VS, Addr, Code, By, Loc, SlotTy, Out, I, S, CI> OracleCtx<'a, VS, Addr, Code, By, Loc, SlotTy, Out, I, S, CI> where I: VMInputT + 'static, - S: State + HasCorpus + HasMetadata + HasExecutionResult, + S: State + HasCorpus + HasMetadata + HasExecutionResult, VS: Default + VMStateT, Addr: Serialize + DeserializeOwned + Debug + Clone, Loc: Serialize + DeserializeOwned + Debug + Clone, @@ -153,4 +154,3 @@ impl BugMetadata { } impl_serdeany!(BugMetadata); - diff --git a/src/scheduler.rs b/src/scheduler.rs index 0b77df4b7..8227e72dd 100644 --- a/src/scheduler.rs +++ b/src/scheduler.rs @@ -3,10 +3,12 @@ use libafl::corpus::Corpus; use libafl::corpus::Testcase; -use libafl::prelude::{HasMetadata, HasRand, Input, Rand}; -use libafl::schedulers::Scheduler; +use libafl::prelude::{HasMetadata, HasRand, Input, UsesInput, HasTestcase, CorpusId}; +use libafl::schedulers::{Scheduler, RemovableScheduler}; use libafl::state::HasCorpus; -use libafl::{impl_serdeany, Error}; +use libafl::Error; +use libafl::state::UsesState; +use libafl_bolts::{impl_serdeany, prelude::Rand}; use serde::{Deserialize, Serialize}; @@ -20,10 +22,9 @@ use crate::input::ConciseSerde; use crate::state::HasParent; /// A trait providing functions necessary for voting mechanisms -pub trait HasVote +pub trait HasVote where - S: HasCorpus + HasRand + HasMetadata, - I: Input, + S: HasCorpus + HasRand + HasMetadata, { fn vote(&self, state: &mut S, idx: usize, amount: usize); } @@ -37,11 +38,11 @@ pub const VISIT_IGNORE_THRESHOLD: usize = 2; /// A scheduler that drops inputs (or VMState) based on a voting mechanism #[derive(Debug, Clone)] -pub struct SortedDroppingScheduler { - phantom: std::marker::PhantomData<(I, S)>, +pub struct SortedDroppingScheduler { + phantom: std::marker::PhantomData, } -impl SortedDroppingScheduler { +impl SortedDroppingScheduler { /// Create a new SortedDroppingScheduler pub fn new() -> Self { Self { @@ -49,6 +50,14 @@ impl SortedDroppingScheduler { } } } + +impl UsesState for SortedDroppingScheduler +where + S: UsesInput, +{ + type State = S; +} + #[derive(Serialize, Deserialize, Clone, Debug)] pub struct Node { parent: usize, // 0 for root node @@ -145,14 +154,13 @@ pub trait HasReportCorpus fn sponsor_state(&self, state: &mut S, state_idx: usize, amt: usize); } -impl HasReportCorpus for SortedDroppingScheduler - where - S: HasCorpus + HasRand + HasMetadata + HasParent, - I: Input + Debug, +impl HasReportCorpus for SortedDroppingScheduler +where + S: HasCorpus + HasRand + HasMetadata + HasParent, { fn report_corpus(&self, state: &mut S, state_idx: usize) { self.vote(state, state_idx, 3); - let mut data = state.metadata_mut().get_mut::().unwrap(); + let mut data = state.metadata_map_mut().get_mut::().unwrap(); #[cfg(feature = "full_trace")] data.deps.mark_never_delete(state_idx); @@ -171,17 +179,18 @@ impl_serdeany!(VoteData); #[cfg(feature = "full_trace")] pub static mut REMOVED_CORPUS: usize = 0; -impl Scheduler for SortedDroppingScheduler +impl Scheduler for SortedDroppingScheduler where - S: HasCorpus + HasRand + HasMetadata + HasParent, - I: Input + Debug, + S: HasCorpus + HasTestcase + HasRand + HasMetadata + HasParent, { /// Hooks called every time an input (or VMState) is added to the corpus /// Set up the metadata for the input (or VMState) - fn on_add(&self, state: &mut S, idx: usize) -> Result<(), Error> { + fn on_add(&mut self, state: &mut Self::State, idx: CorpusId) -> Result<(), Error> { + let idx = usize::from(idx); + // Initialize metadata if it doesn't exist if !state.has_metadata::() { - state.metadata_mut().insert(VoteData { + state.metadata_map_mut().insert(VoteData { votes_and_visits: HashMap::new(), sorted_votes: vec![], visits_total: 1, @@ -194,7 +203,7 @@ where // Setup metadata for the input (or VMState) { let parent_idx = state.get_parent_idx(); - let mut data = state.metadata_mut().get_mut::().unwrap(); + let mut data = state.metadata_map_mut().get_mut::().unwrap(); data.votes_and_visits.insert(idx, (3, 1)); data.visits_total += 1; data.votes_total += 3; @@ -211,7 +220,7 @@ where { let mut corpus_size = state.corpus().count(); let _corpus_mut = state.corpus_mut(); - let data = state.metadata().get::().unwrap(); + let data = state.metadata_map().get::().unwrap(); #[cfg(feature = "full_trace")] { corpus_size -= unsafe { REMOVED_CORPUS }; @@ -235,24 +244,24 @@ where // Remove inputs (or VMState) from metadata and corpus to_remove.iter().for_each(|x| { - self.on_remove(state, *x, &None); + self.on_remove(state, (*x).into(), &None); #[cfg(feature = "full_trace")] { - state.metadata_mut().get_mut::().unwrap().deps.remove_node(*x); + state.metadata_map_mut().get_mut::().unwrap().deps.remove_node(*x); unsafe { REMOVED_CORPUS += 1; } } #[cfg(not(feature = "full_trace"))] { - state.corpus_mut().remove(*x).expect("failed to remove"); + state.corpus_mut().remove((*x).into()).expect("failed to remove"); } }); - state.metadata_mut().get_mut::().unwrap().to_remove = to_remove; + state.metadata_map_mut().get_mut::().unwrap().to_remove = to_remove; #[cfg(feature = "full_trace")] { - for idx in state.metadata_mut().get_mut::().unwrap().deps.garbage_collection() { - state.corpus_mut().remove(idx).expect("failed to remove"); + for idx in state.metadata_map_mut().get_mut::().unwrap().deps.garbage_collection() { + state.corpus_mut().remove(idx.into()).expect("failed to remove"); } } } @@ -260,29 +269,13 @@ where Ok(()) } - /// Hooks called every time an input (or VMState) is removed from the corpus - /// Update the metadata caches for the input (or VMState) - fn on_remove( - &self, - state: &mut S, - idx: usize, - _testcase: &Option>, - ) -> Result<(), Error> { - let mut data = state.metadata_mut().get_mut::().unwrap(); - data.votes_total -= data.votes_and_visits.get(&idx).unwrap().0; - data.visits_total -= data.votes_and_visits.get(&idx).unwrap().1; - data.votes_and_visits.remove(&idx); - data.sorted_votes.retain(|x| *x != idx); - Ok(()) - } - /// Selects next input (or VMState) to run - fn next(&self, state: &mut S) -> Result { + fn next(&mut self, state: &mut Self::State) -> Result { // Debugging prints #[cfg(feature = "print_infant_corpus")] { let corpus_size = state.corpus().count(); - let data = state.metadata().get::().unwrap(); + let data = state.metadata_map().get::().unwrap(); use crate::r#const::DEBUG_PRINT_PERCENT; if random::() % DEBUG_PRINT_PERCENT == 0 { println!( @@ -291,7 +284,7 @@ where ); for idx in &data.sorted_votes { let (votes, visits) = data.votes_and_visits.get(&idx).unwrap(); - let inp = state.corpus().get(*idx).unwrap().clone(); + let inp = state.corpus().get((*idx).into()).unwrap().clone(); match inp.into_inner().input() { Some(x) => { println!( @@ -308,8 +301,8 @@ where // Conduct a probabilistic sampling from votes and visits (weighted by votes) let threshold = (state.rand_mut().below(1000) as f64 / 1000.0) - * state.metadata().get::().unwrap().votes_total as f64; - let mut data = state.metadata_mut().get_mut::().unwrap(); + * state.metadata_map().get::().unwrap().votes_total as f64; + let mut data = state.metadata_map_mut().get_mut::().unwrap(); let mut idx = usize::MAX; let mut s: f64 = 0.0; // sum of votes so far @@ -332,18 +325,39 @@ where data.visits_total += 1; } - Ok(idx) + Ok(idx.into()) + } +} + +impl RemovableScheduler for SortedDroppingScheduler +where + S: HasCorpus + HasTestcase + HasRand + HasMetadata + HasParent, +{ + /// Hooks called every time an input (or VMState) is removed from the corpus + /// Update the metadata caches for the input (or VMState) + fn on_remove( + &mut self, + state: &mut Self::State, + idx: CorpusId, + _testcase: &Option::Input>>, + ) -> Result<(), Error> { + let idx = usize::from(idx); + let data = state.metadata_map_mut().get_mut::().unwrap(); + data.votes_total -= data.votes_and_visits.get(&idx).unwrap().0; + data.visits_total -= data.votes_and_visits.get(&idx).unwrap().1; + data.votes_and_visits.remove(&idx); + data.sorted_votes.retain(|x| *x != idx); + Ok(()) } } -impl HasVote for SortedDroppingScheduler +impl HasVote for SortedDroppingScheduler where - S: HasCorpus + HasRand + HasMetadata, - I: Input, + S: HasCorpus + HasRand + HasMetadata, { /// Vote for an input (or VMState) fn vote(&self, state: &mut S, idx: usize, increment: usize) { - let data = state.metadata_mut().get_mut::().unwrap(); + let data = state.metadata_map_mut().get_mut::().unwrap(); // increment votes for the input (or VMState) data.votes_total += increment; @@ -391,4 +405,4 @@ mod tests { assert!(!tree.nodes.contains_key(&2)); assert!(!tree.nodes.contains_key(&3)); } -} \ No newline at end of file +} diff --git a/src/state.rs b/src/state.rs index 531d22a9d..fbe7360ae 100644 --- a/src/state.rs +++ b/src/state.rs @@ -6,16 +6,17 @@ use crate::state_input::StagedVMState; use libafl::corpus::{Corpus, InMemoryCorpus, OnDiskCorpus, Testcase}; use libafl::inputs::Input; use libafl::monitors::ClientPerfMonitor; -use libafl::prelude::{ - current_nanos, HasMetadata, NamedSerdeAnyMap, Rand, RomuDuoJrRand, Scheduler, SerdeAnyMap, - StdRand, -}; +use libafl::prelude::{HasMetadata, Scheduler, UsesInput, HasTestcase, CorpusId}; +use libafl_bolts::{current_nanos, bolts_prelude::{NamedSerdeAnyMap, Rand, RomuDuoJrRand, SerdeAnyMap, StdRand}}; +use std::borrow::BorrowMut; +use std::cell::{Ref, RefMut}; use std::collections::HashSet; use std::fmt::Debug; +use std::time::Duration; use libafl::state::{ HasClientPerfMonitor, HasCorpus, HasExecutions, HasMaxSize, HasNamedMetadata, HasRand, - HasSolutions, State, + HasSolutions, State, HasLastReportTime, }; use primitive_types::H160; @@ -46,15 +47,15 @@ where /// If the corpus is not empty, return Some((index of VMState in corpus, VMState)) fn get_infant_state( &mut self, - scheduler: &SC, + scheduler: &mut SC, ) -> Option<(usize, StagedVMState)> where - SC: Scheduler, InfantStateState>; + SC: Scheduler>; /// Add a VMState to the infant state corpus - fn add_infant_state(&mut self, state: &StagedVMState, scheduler: &SC, parent_idx: usize) -> usize + fn add_infant_state(&mut self, state: &StagedVMState, scheduler: &mut SC, parent_idx: usize) -> usize where - SC: Scheduler, InfantStateState>; + SC: Scheduler>; } /// Trait providing caller/address functions @@ -181,9 +182,47 @@ where /// Mapping between function hash with the contract addresses that have the function, required for implementing [`HasHashToAddress`] trait pub hash_to_address: std::collections::HashMap<[u8; 4], HashSet>, + /// The last time we reported progress (if available/used). + /// This information is used by fuzzer `maybe_report_progress` and updated by event_manager. + last_report_time: Option, + pub phantom: std::marker::PhantomData<(VI, Addr)>, } +impl UsesInput for FuzzState +where + VS: Default + VMStateT, + VI: VMInputT + Input, + Addr: Serialize + DeserializeOwned + Debug + Clone, + Loc: Serialize + DeserializeOwned + Debug + Clone, + Out: Default, + CI: Serialize + DeserializeOwned + Debug + Clone + ConciseSerde, +{ + type Input = VI; +} + +impl HasTestcase for FuzzState +where + VS: Default + VMStateT, + VI: VMInputT + Input, + Addr: Serialize + DeserializeOwned + Debug + Clone, + Loc: Serialize + DeserializeOwned + Debug + Clone, + Out: Default, + CI: Serialize + DeserializeOwned + Debug + Clone + ConciseSerde, +{ + /// Shorthand to receive a [`Ref`] to a stored [`Testcase`], by [`CorpusId`]. + /// For a normal state, this should return a [`Testcase`] in the corpus, not the objectives. + fn testcase(&self, id: CorpusId) -> Result::Input>>, Error> { + Ok(self.corpus().get(id)?.borrow()) + } + + /// Shorthand to receive a [`RefMut`] to a stored [`Testcase`], by [`CorpusId`]. + /// For a normal state, this should return a [`Testcase`] in the corpus, not the objectives. + fn testcase_mut(&self, id: CorpusId) -> Result::Input>>, Error> { + Ok(self.corpus().get(id)?.borrow_mut()) + } +} + impl FuzzState where VS: Default + VMStateT + 'static, @@ -217,12 +256,13 @@ where rand_generator: RomuDuoJrRand::with_seed(seed), max_size: 20, hash_to_address: Default::default(), + last_report_time: None, phantom: Default::default(), } } /// Add an input testcase to the input corpus - pub fn add_tx_to_corpus(&mut self, input: Testcase) -> Result { + pub fn add_tx_to_corpus(&mut self, input: Testcase) -> Result { self.txn_corpus.add(input) } } @@ -301,6 +341,36 @@ where pub rand_generator: StdRand, } +impl UsesInput for InfantStateState +where + VS: Default + VMStateT, + Addr: Serialize + DeserializeOwned + Debug + Clone, + Loc: Serialize + DeserializeOwned + Debug + Clone, + CI: Serialize + DeserializeOwned + Debug + Clone + ConciseSerde, +{ + type Input = StagedVMState; +} + +impl HasTestcase for InfantStateState +where + VS: Default + VMStateT, + Addr: Serialize + DeserializeOwned + Debug + Clone, + Loc: Serialize + DeserializeOwned + Debug + Clone, + CI: Serialize + DeserializeOwned + Debug + Clone + ConciseSerde, +{ + /// Shorthand to receive a [`Ref`] to a stored [`Testcase`], by [`CorpusId`]. + /// For a normal state, this should return a [`Testcase`] in the corpus, not the objectives. + fn testcase(&self, id: CorpusId) -> Result::Input>>, Error> { + Ok(self.corpus().get(id)?.borrow()) + } + + /// Shorthand to receive a [`RefMut`] to a stored [`Testcase`], by [`CorpusId`]. + /// For a normal state, this should return a [`Testcase`] in the corpus, not the objectives. + fn testcase_mut(&self, id: CorpusId) -> Result::Input>>, Error> { + Ok(self.corpus().get(id)?.borrow_mut()) + } +} + pub trait HasParent { fn get_parent_idx(&self) -> usize; fn set_parent_idx(&mut self, idx: usize); @@ -364,7 +434,7 @@ where { } -impl HasCorpus> for InfantStateState +impl HasCorpus for InfantStateState where VS: Default + VMStateT + DeserializeOwned, Addr: Serialize + DeserializeOwned + Debug + Clone, @@ -389,11 +459,11 @@ where Loc: Serialize + DeserializeOwned + Debug + Clone, CI: Serialize + DeserializeOwned + Debug + Clone + ConciseSerde, { - fn metadata(&self) -> &SerdeAnyMap { + fn metadata_map(&self) -> &SerdeAnyMap { &self.metadata } - fn metadata_mut(&mut self) -> &mut SerdeAnyMap { + fn metadata_map_mut(&mut self) -> &mut SerdeAnyMap { &mut self.metadata } } @@ -428,10 +498,10 @@ where /// Get a infant state from the infant state corpus using the scheduler fn get_infant_state( &mut self, - scheduler: &SC, + scheduler: &mut SC, ) -> Option<(usize, StagedVMState)> where - SC: Scheduler, InfantStateState>, + SC: Scheduler>, { let idx = scheduler .next(&mut self.infant_states_state) @@ -443,14 +513,14 @@ where .unwrap() .borrow_mut(); - Some((idx, state.input().clone().unwrap())) + Some((idx.into(), state.input().clone().unwrap())) } /// Add a new infant state to the infant state corpus /// and setup the scheduler - fn add_infant_state(&mut self, state: &StagedVMState, scheduler: &SC, parent_idx: usize) -> usize + fn add_infant_state(&mut self, state: &StagedVMState, scheduler: &mut SC, parent_idx: usize) -> usize where - SC: Scheduler, InfantStateState>, + SC: Scheduler>, { let idx = self .infant_states_state @@ -461,7 +531,7 @@ where scheduler .on_add(&mut self.infant_states_state, idx) .expect("Failed to setup scheduler"); - idx + idx.into() } } @@ -573,17 +643,17 @@ where CI: Serialize + DeserializeOwned + Debug + Clone + ConciseSerde, { /// Get the metadata - fn metadata(&self) -> &SerdeAnyMap { + fn metadata_map(&self) -> &SerdeAnyMap { &self.metadata } /// Get the mutable metadata - fn metadata_mut(&mut self) -> &mut SerdeAnyMap { + fn metadata_map_mut(&mut self) -> &mut SerdeAnyMap { &mut self.metadata } } -impl HasCorpus for FuzzState +impl HasCorpus for FuzzState where VS: Default + VMStateT, VI: VMInputT + Input, @@ -608,7 +678,7 @@ where } } -impl HasSolutions for FuzzState +impl HasSolutions for FuzzState where VS: Default + VMStateT, VI: VMInputT + Input, @@ -690,12 +760,12 @@ where CI: Serialize + DeserializeOwned + Debug + Clone + ConciseSerde, { /// Get the named metadata - fn named_metadata(&self) -> &NamedSerdeAnyMap { + fn named_metadata_map(&self) -> &NamedSerdeAnyMap { &self.named_metadata } /// Get the mutable named metadata - fn named_metadata_mut(&mut self) -> &mut NamedSerdeAnyMap { + fn named_metadata_map_mut(&mut self) -> &mut NamedSerdeAnyMap { &mut self.named_metadata } } @@ -710,3 +780,25 @@ where CI: Serialize + DeserializeOwned + Debug + Clone + ConciseSerde, { } + +impl HasLastReportTime for FuzzState +where + VS: Default + VMStateT, + VI: VMInputT + Input, + Addr: Serialize + DeserializeOwned + Debug + Clone, + Loc: Serialize + DeserializeOwned + Debug + Clone, + Out: Default, + CI: Serialize + DeserializeOwned + Debug + Clone + ConciseSerde, +{ + /// The last time we reported progress,if available/used. + /// This information is used by fuzzer `maybe_report_progress`. + fn last_report_time(&self) -> &Option { + &self.last_report_time + } + + /// The last time we reported progress,if available/used (mutable). + /// This information is used by fuzzer `maybe_report_progress`. + fn last_report_time_mut(&mut self) -> &mut Option { + &mut self.last_report_time + } +} diff --git a/src/tracer.rs b/src/tracer.rs index 245058376..82eb40b6b 100644 --- a/src/tracer.rs +++ b/src/tracer.rs @@ -61,7 +61,7 @@ where CI: Serialize + DeserializeOwned + Debug + Clone + ConciseSerde { return String::from("Begin\n"); } let current_idx = self.from_idx.unwrap(); - let corpus_item = state.get_infant_state_state().corpus().get(current_idx); + let corpus_item = state.get_infant_state_state().corpus().get(current_idx.into()); // This happens when full_trace feature is not enabled, the corpus item may be discarded if corpus_item.is_err() { return String::from("Corpus returning error\n"); @@ -95,7 +95,7 @@ where CI: Serialize + DeserializeOwned + Debug + Clone + ConciseSerde { return String::from(""); } let current_idx = self.from_idx.unwrap(); - let corpus_item = state.get_infant_state_state().corpus().get(current_idx); + let corpus_item = state.get_infant_state_state().corpus().get(current_idx.into()); // This happens when full_trace feature is not enabled, the corpus item may be discarded if corpus_item.is_err() { return String::from("Corpus returning error\n");