diff --git a/.gitignore b/.gitignore index 82bd44bbb..0dd28911e 100644 --- a/.gitignore +++ b/.gitignore @@ -5,6 +5,13 @@ /*bench bin corpus +.idea .vscode node_modules -cache \ No newline at end of file +cache +*.bin +*.abi +output +work_dir +.z3-trace +*.code-workspace \ No newline at end of file diff --git a/Cargo.toml b/Cargo.toml index 0d13fa6fb..6625b5fb4 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -12,7 +12,7 @@ name = "ityfuzz" harness = false [features] -default = ["cmp", "dataflow", "evm", "print_txn_corpus", "full_trace", ] +default = ["cmp", "dataflow", "evm", "print_txn_corpus", "full_trace"] evm = [] cmp = [] dataflow = [] @@ -29,14 +29,32 @@ force_cache = [] use_presets = [] print_logs = [] z3_debug = [] -sui_support = ["dep:move-binary-format", "dep:move-core-types", "dep:move-stdlib", "dep:move-vm-runtime", "dep:move-vm-types", "dep:sui-move-natives-latest", "dep:sui-protocol-config", "dep:sui-types"] +sui_support = [ + "dep:move-binary-format", + "dep:move-core-types", + "dep:move-stdlib", + "dep:move-vm-runtime", + "dep:move-vm-types", + "dep:sui-move-natives-latest", + "dep:sui-protocol-config", + "dep:sui-types", +] debug = [] [dependencies] bytes = { version = "1.2.1", features = ["serde"] } -revm = { path = "./externals/revm/crates/revm", features = ["no_gas_measuring", "serde"] } -revm-primitives = { path = "./externals/revm/crates/primitives", features = ["no_gas_measuring", "serde"] } -revm-interpreter = { path = "./externals/revm/crates/interpreter", features = ["no_gas_measuring", "serde"] } +revm = { path = "./externals/revm/crates/revm", features = [ + "no_gas_measuring", + "serde", +] } +revm-primitives = { path = "./externals/revm/crates/primitives", features = [ + "no_gas_measuring", + "serde", +] } +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" @@ -45,7 +63,7 @@ nix = "0.24" serde = "1.0.147" serde_traitobject = "0.2.7" serde_json = "1.0.73" -z3 = {version="0.11.2", features = ["static-link-z3"]} +z3 = { version = "0.11.2", features = ["static-link-z3"] } z3-sys = "0.7.1" glob = "0.3.0" rust-crypto = "0.2" @@ -61,14 +79,17 @@ heimdall = { path = "./externals/heimdall-rs/heimdall" } # from https://github.com/aptos-labs/aptos-core/blob/main/Cargo.toml#L452 move-binary-format = { path = "./externals/sui/external-crates/move/move-binary-format", optional = true } -move-core-types = { path = "./externals/sui/external-crates/move/move-core/types", features = ["address32"], optional = true } +move-core-types = { path = "./externals/sui/external-crates/move/move-core/types", features = [ + "address32", +], optional = true } move-stdlib = { path = "./externals/sui/external-crates/move/move-stdlib", optional = true } -move-vm-runtime = { path = "./externals/sui/external-crates/move/move-vm/runtime", features = ["lazy_natives"], optional = true } +move-vm-runtime = { path = "./externals/sui/external-crates/move/move-vm/runtime", features = [ + "lazy_natives", +], optional = true } move-vm-types = { path = "./externals/sui/external-crates/move/move-vm/types", optional = true } sui-move-natives-latest = { path = "./externals/sui/sui-execution/latest/sui-move-natives", optional = true } -sui-protocol-config = { path = "./externals/sui/crates/sui-protocol-config", optional = true } -sui-types = { path = "./externals/sui/crates/sui-types", optional = true } - +sui-protocol-config = { path = "./externals/sui/crates/sui-protocol-config", optional = true } +sui-types = { path = "./externals/sui/crates/sui-types", optional = true } retry = "2.0.0" diff --git a/cli/src/evm.rs b/cli/src/evm.rs index cc0a0a67f..38f9db95e 100644 --- a/cli/src/evm.rs +++ b/cli/src/evm.rs @@ -1,16 +1,11 @@ use clap::Parser; use ethers::types::Transaction; -use hex::{decode, encode}; use ityfuzz::evm::config::{Config, FuzzerTypes, StorageFetchingMode}; -use ityfuzz::evm::contract_utils::{set_hash, ContractLoader}; -use ityfuzz::evm::host::PANIC_ON_BUG; +use ityfuzz::evm::contract_utils::ContractLoader; use ityfuzz::evm::input::{ConciseEVMInput, EVMInput}; -use ityfuzz::evm::middlewares::middleware::Middleware; use ityfuzz::evm::onchain::endpoints::{Chain, OnChainConfig}; -use ityfuzz::evm::onchain::flashloan::{DummyPriceOracle, Flashloan}; -use ityfuzz::evm::oracles::echidna::EchidnaOracle; +use ityfuzz::evm::onchain::flashloan::DummyPriceOracle; use ityfuzz::evm::oracles::erc20::IERC20OracleFlashloan; -use ityfuzz::evm::oracles::function::FunctionHarnessOracle; use ityfuzz::evm::oracles::selfdestruct::SelfdestructOracle; use ityfuzz::evm::oracles::typed_bug::TypedBugOracle; use ityfuzz::evm::oracles::v2_pair::PairBalanceOracle; @@ -20,21 +15,18 @@ use ityfuzz::evm::types::{EVMAddress, EVMFuzzState, EVMU256}; use ityfuzz::evm::vm::EVMState; use ityfuzz::fuzzers::evm_fuzzer::evm_fuzzer; use ityfuzz::oracle::{Oracle, Producer}; -use ityfuzz::r#const; use ityfuzz::state::FuzzState; use serde::Deserialize; use std::cell::RefCell; use std::collections::HashMap; use std::collections::HashSet; -use std::env; use std::rc::Rc; use std::str::FromStr; - pub fn parse_constructor_args_string(input: String) -> HashMap> { let mut map = HashMap::new(); - if input.len() == 0 { + if input.is_empty() { return map; } @@ -50,6 +42,7 @@ pub fn parse_constructor_args_string(input: String) -> HashMap, } +#[allow(dead_code)] #[derive(Deserialize)] struct Response { data: ResponseData, } +#[allow(dead_code)] #[derive(Deserialize)] struct ResponseData { id: u16, result: TXResult, } +#[allow(dead_code)] #[derive(Deserialize)] struct TXResult { input: String, } /// CLI for ItyFuzz for EVM smart contracts -#[derive(Parser, Debug)] +#[derive(Parser, Debug, Default)] #[command(author, version, about, long_about = None)] pub struct EvmArgs { /// Glob pattern / address to find contracts #[arg(short, long)] - target: String, + pub(crate) target: String, #[arg(long, default_value = "false")] fetch_tx_data: bool, @@ -226,7 +222,6 @@ pub struct EvmArgs { ///spec id #[arg(long, default_value = "Latest")] spec_id: String, - } enum EVMTargetType { @@ -288,7 +283,7 @@ pub fn evm_main(args: EvmArgs) { let pair_producer = Rc::new(RefCell::new(PairProducer::new())); let erc20_producer = Rc::new(RefCell::new(ERC20Producer::new())); - let mut flashloan_oracle = Rc::new(RefCell::new({ + let flashloan_oracle = Rc::new(RefCell::new({ IERC20OracleFlashloan::new(pair_producer.clone(), erc20_producer.clone()) })); @@ -297,7 +292,7 @@ pub fn evm_main(args: EvmArgs) { // set_hash(harness_code, &mut harness_hash); // let mut function_oracle = // FunctionHarnessOracle::new_no_condition(EVMAddress::zero(), Vec::from(harness_hash)); - + #[allow(clippy::type_complexity)] let mut oracles: Vec< Rc< RefCell< @@ -311,12 +306,13 @@ pub fn evm_main(args: EvmArgs) { Vec, EVMInput, EVMFuzzState, - ConciseEVMInput + ConciseEVMInput, >, >, >, > = vec![]; + #[allow(clippy::type_complexity)] let mut producers: Vec< Rc< RefCell< @@ -330,7 +326,7 @@ pub fn evm_main(args: EvmArgs) { Vec, EVMInput, EVMFuzzState, - ConciseEVMInput + ConciseEVMInput, >, >, >, @@ -352,7 +348,6 @@ pub fn evm_main(args: EvmArgs) { if args.typed_bug_oracle { oracles.push(Rc::new(RefCell::new(TypedBugOracle::new()))); - } if args.ierc20_oracle || args.pair_oracle { @@ -386,8 +381,8 @@ pub fn evm_main(args: EvmArgs) { let data = params[0].clone(); - let data = if data.starts_with("0x") { - &data[2..] + let data = if let Some(stripped) = data.strip_prefix("0x") { + stripped } else { &data }; @@ -407,14 +402,12 @@ pub fn evm_main(args: EvmArgs) { let config = Config { fuzzer_type: FuzzerTypes::from_str(args.fuzzer_type.as_str()).expect("unknown fuzzer"), contract_loader: match target_type { - EVMTargetType::Glob => { - ContractLoader::from_glob( - args.target.as_str(), - &mut state, - &proxy_deploy_codes, - &constructor_args_map, - ) - } + EVMTargetType::Glob => ContractLoader::from_glob( + args.target.as_str(), + &mut state, + &proxy_deploy_codes, + &constructor_args_map, + ), EVMTargetType::Address => { if onchain.is_none() { panic!("Onchain is required for address target type"); @@ -425,29 +418,32 @@ pub fn evm_main(args: EvmArgs) { const ETH_ADDRESS: &str = "0x7a250d5630b4cf539739df2c5dacb4c659f2488d"; const BSC_ADDRESS: &str = "0x10ed43c718714eb63d5aa57b78b54704e256024e"; if "bsc" == onchain.as_ref().unwrap().chain_name { - if args_target.find(BSC_ADDRESS) == None { - args_target.push_str(","); + if !args_target.contains(BSC_ADDRESS) { + args_target.push(','); args_target.push_str(BSC_ADDRESS); } - } else if "eth" == onchain.as_ref().unwrap().chain_name { - if args_target.find(ETH_ADDRESS) == None { - args_target.push_str(","); - args_target.push_str(ETH_ADDRESS); - } + } else if "eth" == onchain.as_ref().unwrap().chain_name + && !args_target.contains(ETH_ADDRESS) + { + args_target.push(','); + args_target.push_str(ETH_ADDRESS); } } let addresses: Vec = args_target - .split(",") + .split(',') .map(|s| EVMAddress::from_str(s).unwrap()) .collect(); ContractLoader::from_address( - &mut onchain.as_mut().unwrap(), + onchain.as_mut().unwrap(), HashSet::from_iter(addresses), ) } }, - only_fuzz: if args.only_fuzz.len() > 0 { - args.only_fuzz.split(",").map(|s| EVMAddress::from_str(s).expect("failed to parse only fuzz")).collect() + only_fuzz: if !args.only_fuzz.is_empty() { + args.only_fuzz + .split(',') + .map(|s| EVMAddress::from_str(s).expect("failed to parse only fuzz")) + .collect() } else { HashSet::new() }, @@ -474,12 +470,12 @@ pub fn evm_main(args: EvmArgs) { replay_file: args.replay_file, flashloan_oracle, selfdestruct_oracle: args.selfdestruct_oracle, - state_comp_matching: if args.state_comp_oracle.len() > 0 { + state_comp_matching: if !args.state_comp_oracle.is_empty() { Some(args.state_comp_matching) } else { None }, - state_comp_oracle: if args.state_comp_oracle.len() > 0 { + state_comp_oracle: if !args.state_comp_oracle.is_empty() { Some(args.state_comp_oracle) } else { None @@ -494,10 +490,8 @@ pub fn evm_main(args: EvmArgs) { spec_id: args.spec_id, }; - match config.fuzzer_type { - FuzzerTypes::CMP => evm_fuzzer(config, &mut state), - // FuzzerTypes::BASIC => basic_fuzzer(config) - _ => {} + if let FuzzerTypes::CMP = config.fuzzer_type { + evm_fuzzer(config, &mut state) } // // Some(v) => { diff --git a/cli/src/main.rs b/cli/src/main.rs index a026ead55..9939abae1 100644 --- a/cli/src/main.rs +++ b/cli/src/main.rs @@ -1,40 +1,11 @@ mod evm; mod r#move; -use clap::Parser; -use ethers::types::Transaction; -use hex::{decode, encode}; -use ityfuzz::evm::config::{Config, FuzzerTypes, StorageFetchingMode}; -use ityfuzz::evm::contract_utils::{set_hash, ContractLoader}; -use ityfuzz::evm::host::PANIC_ON_BUG; -use ityfuzz::evm::input::{ConciseEVMInput, EVMInput}; -use ityfuzz::evm::middlewares::middleware::Middleware; -use ityfuzz::evm::onchain::endpoints::{Chain, OnChainConfig}; -use ityfuzz::evm::onchain::flashloan::{DummyPriceOracle, Flashloan}; -use ityfuzz::evm::oracles::echidna::EchidnaOracle; -use ityfuzz::evm::oracles::erc20::IERC20OracleFlashloan; -use ityfuzz::evm::oracles::function::FunctionHarnessOracle; -use ityfuzz::evm::oracles::selfdestruct::SelfdestructOracle; -use ityfuzz::evm::oracles::typed_bug::TypedBugOracle; -use ityfuzz::evm::oracles::v2_pair::PairBalanceOracle; -use ityfuzz::evm::producers::erc20::ERC20Producer; -use ityfuzz::evm::producers::pair::PairProducer; -use ityfuzz::evm::types::{EVMAddress, EVMFuzzState, EVMU256}; -use ityfuzz::evm::vm::EVMState; -use ityfuzz::fuzzers::evm_fuzzer::evm_fuzzer; -use ityfuzz::oracle::{Oracle, Producer}; -use ityfuzz::r#const; -use ityfuzz::state::FuzzState; -use serde::Deserialize; -use std::cell::RefCell; -use std::collections::HashMap; -use std::collections::HashSet; -use std::env; -use std::rc::Rc; -use std::str::FromStr; use crate::evm::{evm_main, EvmArgs}; use crate::r#move::{move_main, MoveArgs}; +use clap::Parser; use clap::Subcommand; +use std::env; pub fn init_sentry() { let _guard = sentry::init(("https://96f3517bd77346ea835d28f956a84b9d@o4504503751344128.ingest.sentry.io/4504503752523776", sentry::ClientOptions { @@ -61,7 +32,7 @@ struct Cli { #[derive(Subcommand, Debug)] enum Commands { EVM(EvmArgs), - MOVE(MoveArgs) + MOVE(MoveArgs), } fn main() { @@ -75,5 +46,4 @@ fn main() { move_main(args); } } - } diff --git a/integration_test.py b/integration_test.py index d5ef165a5..9636c0c91 100644 --- a/integration_test.py +++ b/integration_test.py @@ -1,9 +1,8 @@ import glob -import subprocess import os +import subprocess import time - TIMEOUT_BIN = "timeout" if os.name == "posix" else "gtimeout" @@ -13,9 +12,23 @@ def test_one(path): # compile with solc p = subprocess.run( - " ".join(["solc", f"{path}/*.sol", "-o", f"{path}/", - "--bin", "--abi", "--overwrite", "--base-path", "."]), - shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE) + " ".join( + [ + "solc", + f"{path}/*.sol", + "-o", + f"{path}/", + "--bin", + "--abi", + "--overwrite", + "--base-path", + ".", + ] + ), + shell=True, + stdout=subprocess.PIPE, + stderr=subprocess.PIPE, + ) if b"Error" in p.stderr or b"Error" in p.stdout: print(f"Error compiling {path}") @@ -23,23 +36,32 @@ def test_one(path): # run fuzzer and check whether the stdout has string success start_time = time.time() - cmd = [TIMEOUT_BIN, "3m", "./cli/target/release/cli", "evm", "-t", f"'{path}/*'", "-f", "--panic-on-bug"] + cmd = [ + TIMEOUT_BIN, + "3m", + "./cli/target/release/cli", + "evm", + "-t", + f"'{path}/*'", + "-f", + "--panic-on-bug", + ] if "concolic" in path: cmd.append("--concolic --concolic-caller") - p = subprocess.run(" ".join(cmd), - stdout=subprocess.PIPE, - stderr=subprocess.PIPE, - shell=True + p = subprocess.run( + " ".join(cmd), stdout=subprocess.PIPE, stderr=subprocess.PIPE, shell=True ) - if b"target bug found" not in p.stderr \ - and b"bug() hit" not in p.stdout \ - and b"[typed_bug]" not in p.stdout \ - and b"[selfdestruct]" not in p.stdout \ - and b"[echidna_bug]" not in p.stdout\ - and b"Found violations!" not in p.stdout: + if ( + b"target bug found" not in p.stderr + and b"bug() hit" not in p.stdout + and b"[typed_bug]" not in p.stdout + and b"[selfdestruct]" not in p.stdout + and b"[echidna_bug]" not in p.stdout + and b"Found violations!" not in p.stdout + ): print("================ STDERR =================") print(p.stderr.decode("utf-8")) print("================ STDOUT =================") diff --git a/src/evm/contract_utils.rs b/src/evm/contract_utils.rs index e1daa1a7f..351b6fbbf 100644 --- a/src/evm/contract_utils.rs +++ b/src/evm/contract_utils.rs @@ -1,13 +1,10 @@ -use crate::evm::types::{ - fixed_address, generate_random_address, EVMAddress, EVMFuzzMutator, EVMFuzzState, -}; +use crate::evm::types::{fixed_address, generate_random_address, EVMAddress, EVMFuzzState}; /// Load contract from file system or remote use glob::glob; use serde_json::Value; use std::collections::{HashMap, HashSet}; use std::fs::File; -use crate::state::FuzzState; use itertools::Itertools; use std::io::Read; use std::path::Path; @@ -19,9 +16,6 @@ use crate::evm::srcmap::parser::{decode_instructions, SourceMapLocation}; use self::crypto::digest::Digest; use self::crypto::sha3::Sha3; -use crate::evm::onchain::abi_decompiler::fetch_abi_heimdall; -use hex::encode; -use regex::Regex; use serde::{Deserialize, Serialize}; // to use this address, call rand_utils::fixed_address(FIX_DEPLOYER) @@ -72,7 +66,7 @@ impl ContractLoader { let mut data = String::new(); file.read_to_string(&mut data) .expect("failed to read abis file"); - return Self::parse_abi_str(&data); + Self::parse_abi_str(&data) } fn process_input(ty: String, input: &Value) -> String { @@ -98,8 +92,8 @@ impl ContractLoader { } } - pub fn parse_abi_str(data: &String) -> Vec { - let json: Vec = serde_json::from_str(&data).expect("failed to parse abis file"); + pub fn parse_abi_str(data: &str) -> Vec { + let json: Vec = serde_json::from_str(data).expect("failed to parse abis file"); json.iter() .flat_map(|abi| { if abi["type"] == "function" || abi["type"] == "constructor" { @@ -148,12 +142,12 @@ impl ContractLoader { hex::decode(data).expect("Failed to parse hex file") } - fn constructor_args_encode(constructor_args: &Vec) -> Vec { + fn constructor_args_encode(constructor_args: &[String]) -> Vec { constructor_args .iter() .flat_map(|arg| { - let arg = if arg.starts_with("0x") { - &arg[2..] + let arg = if let Some(stripped) = arg.strip_prefix("0x") { + stripped } else { arg }; @@ -180,9 +174,9 @@ impl ContractLoader { state: &mut EVMFuzzState, source_map_info: Option, proxy_deploy_codes: &Vec, - constructor_args: &Vec, + constructor_args: &[String], ) -> Self { - let contract_name = prefix.split("/").last().unwrap().replace("*", ""); + let contract_name = prefix.split('/').last().unwrap().replace('*', ""); // get constructor args let constructor_args_in_bytes: Vec = Self::constructor_args_encode(constructor_args); @@ -197,13 +191,12 @@ impl ContractLoader { deployed_address: generate_random_address(state), source_map: source_map_info.map(|info| { info.get(contract_name.as_str()) - .expect( - format!( + .unwrap_or_else(|| { + panic!( "combined.json provided but contract ({:?}) not found", contract_name ) - .as_str(), - ) + }) .clone() }), }; @@ -244,7 +237,7 @@ impl ContractLoader { let mut abi_instance = get_abi_type_boxed_with_address(&abi.abi, fixed_address(FIX_DEPLOYER).0.to_vec()); abi_instance.set_func_with_name(abi.function, abi.function_name.clone()); - if contract_result.constructor_args.len() == 0 { + if contract_result.constructor_args.is_empty() { println!("No constructor args found, using default constructor args"); contract_result.constructor_args = abi_instance.get().get_bytes(); } @@ -262,8 +255,8 @@ impl ContractLoader { let current_code = hex::encode(&contract_result.code); for deployed_code in proxy_deploy_codes { // if deploy_code startwiths '0x' then remove it - let deployed_code_cleaned = if deployed_code.starts_with("0x") { - &deployed_code[2..] + let deployed_code_cleaned = if let Some(stripped) = deployed_code.strip_prefix("0x") { + stripped } else { deployed_code }; @@ -287,22 +280,22 @@ impl ContractLoader { } } } - return Self { - contracts: if contract_result.code.len() > 0 { + Self { + contracts: if !contract_result.code.is_empty() { vec![contract_result] } else { vec![] }, abis: vec![abi_result], - }; + } } // This function loads constructs Contract infos from path p // The organization of directory p should be // p - // |- contract1.abis + // |- contract1.abi // |- contract1.bin - // |- contract2.abis + // |- contract2.abi // |- contract2.bin pub fn from_glob( p: &str, @@ -316,9 +309,9 @@ impl ContractLoader { match i { Ok(path) => { let path_str = path.to_str().unwrap(); - if path_str.ends_with(".abis") { + if path_str.ends_with(".abi") { *prefix_file_count - .entry(path_str.replace(".abis", "").clone()) + .entry(path_str.replace(".abi", "").clone()) .or_insert(0) += 1; } else if path_str.ends_with(".bin") { *prefix_file_count @@ -410,7 +403,6 @@ impl ContractLoader { } } -type ContractSourceMap = HashMap; type ContractsSourceMapInfo = HashMap>; pub fn parse_combined_json(json: String) -> ContractsSourceMapInfo { @@ -430,7 +422,6 @@ pub fn parse_combined_json(json: String) -> ContractsSourceMapInfo { for (contract_name, contract_info) in contracts { let splitter = contract_name.split(':').collect::>(); - let file_name = splitter.iter().take(splitter.len() - 1).join(":"); let contract_name = splitter.last().unwrap().to_string(); let bin_runtime = contract_info["bin-runtime"] @@ -478,8 +469,9 @@ pub fn extract_sig_from_contract(code: &str) -> Vec<[u8; 4]> { code_sig.push(sig_bytes.try_into().unwrap()); } } - /// skip off the PUSH XXXxxxxxxXXX instruction - if op >= 0x60 && op <= 0x7f { + + // skip off the PUSH XXXxxxxxxXXX instruction + if (0x60..=0x7f).contains(&op) { i += op as usize - 0x5f; continue; } @@ -488,9 +480,6 @@ pub fn extract_sig_from_contract(code: &str) -> Vec<[u8; 4]> { } mod tests { - use super::*; - use std::str::FromStr; - #[test] fn test_load() { let codes: Vec = vec![]; diff --git a/src/evm/corpus_initializer.rs b/src/evm/corpus_initializer.rs index 87d0eaf4a..0f3f3b272 100644 --- a/src/evm/corpus_initializer.rs +++ b/src/evm/corpus_initializer.rs @@ -1,13 +1,16 @@ /// Utilities to initialize the corpus /// Add all potential calls with default args to the corpus -use crate::evm::abi::{BoxedABI, get_abi_type_boxed}; +use crate::evm::abi::{get_abi_type_boxed, BoxedABI}; use crate::evm::bytecode_analyzer; -use crate::evm::contract_utils::{ABIConfig, ABIInfo, ContractInfo, ContractLoader, extract_sig_from_contract}; -use crate::evm::input::{ConciseEVMInput, EVMInput, EVMInputTy}; +use crate::evm::contract_utils::{extract_sig_from_contract, ABIConfig, ContractLoader}; +use crate::evm::input::{ConciseEVMInput, EVMInput}; use crate::evm::mutator::AccessPattern; use crate::evm::onchain::onchain::BLACKLIST_ADDR; -use crate::evm::types::{fixed_address, EVMAddress, EVMFuzzState, EVMInfantStateState, EVMStagedVMState, EVMU256, ProjectSourceMapTy}; +use crate::evm::types::{ + fixed_address, EVMAddress, EVMFuzzState, EVMInfantStateState, EVMStagedVMState, + ProjectSourceMapTy, EVMU256, +}; use crate::evm::vm::{EVMExecutor, EVMState}; use crate::generic_vm::vm_executor::GenericVM; @@ -16,35 +19,29 @@ use crate::state_input::StagedVMState; use bytes::Bytes; use libafl::corpus::{Corpus, Testcase}; +#[cfg(feature = "print_txn_corpus")] +use crate::fuzzer::DUMP_FILE_COUNT; +use crate::fuzzer::REPLAY; use libafl::schedulers::Scheduler; use libafl::state::HasCorpus; use revm_primitives::Bytecode; -use crate::fuzzer::REPLAY; -#[cfg(feature = "print_txn_corpus")] -use crate::fuzzer::DUMP_FILE_COUNT; use std::cell::RefCell; use std::collections::{HashMap, HashSet}; -use std::ops::Deref; -use crate::evm::onchain::flashloan::register_borrow_txn; -use crate::evm::presets::presets::Preset; -use crate::evm::srcmap::parser::{decode_instructions, SourceMapLocation}; +use crate::dump_txn; +use crate::evm::onchain::abi_decompiler::fetch_abi_heimdall; +use crate::evm::types::EVMExecutionResult; +use crate::input::ConciseSerde; use hex; use itertools::Itertools; -use std::rc::Rc; -use std::time::Duration; -use crypto::sha3::Sha3Mode::Keccak256; use libafl::impl_serdeany; use libafl::prelude::HasMetadata; use serde::{Deserialize, Serialize}; -use crate::{dump_file, dump_txn}; use std::fs::File; -use std::path::Path; -use crate::input::ConciseSerde; use std::io::Write; -use crate::generic_vm::vm_executor::ExecutionResult; -use crate::evm::types::EVMExecutionResult; -use crate::evm::onchain::abi_decompiler::fetch_abi_heimdall; +use std::path::Path; +use std::rc::Rc; +use std::time::Duration; pub struct EVMCorpusInitializer<'a> { executor: &'a mut EVMExecutor, @@ -65,7 +62,7 @@ pub struct EVMInitializationArtifacts { pub initial_state: EVMStagedVMState, } -#[derive(Clone, Debug, Serialize, Deserialize)] +#[derive(Clone, Debug, Serialize, Deserialize, Default)] pub struct ABIMap { pub signature_to_abi: HashMap<[u8; 4], ABIConfig>, } @@ -74,13 +71,11 @@ impl_serdeany!(ABIMap); impl ABIMap { pub fn new() -> Self { - Self { - signature_to_abi: HashMap::new(), - } + Self::default() } pub fn insert(&mut self, abi: ABIConfig) { - self.signature_to_abi.insert(abi.function.clone(), abi); + self.signature_to_abi.insert(abi.function, abi); } pub fn get(&self, signature: &[u8; 4]) -> Option<&ABIConfig> { @@ -156,7 +151,7 @@ impl<'a> EVMCorpusInitializer<'a> { self.presets.push(preset); } - pub fn initialize(&mut self, loader: &mut ContractLoader) -> EVMInitializationArtifacts{ + pub fn initialize(&mut self, loader: &mut ContractLoader) -> EVMInitializationArtifacts { self.state.metadata_mut().insert(ABIMap::new()); self.setup_default_callers(); self.setup_contract_callers(); @@ -196,7 +191,6 @@ impl<'a> EVMCorpusInitializer<'a> { } } - pub fn initialize_corpus(&mut self, loader: &mut ContractLoader) -> EVMInitializationArtifacts { let mut artifacts = EVMInitializationArtifacts { address_to_bytecode: HashMap::new(), @@ -204,10 +198,10 @@ impl<'a> EVMCorpusInitializer<'a> { address_to_abi: HashMap::new(), address_to_abi_object: Default::default(), address_to_name: Default::default(), - initial_state: StagedVMState::new_uninitialized() + initial_state: StagedVMState::new_uninitialized(), }; for contract in &mut loader.contracts { - if contract.abi.len() == 0 { + if contract.abi.is_empty() { // this contract's abi is not available, we will use 3 layers to handle this // 1. Extract abi from bytecode, and see do we have any function sig available in state // 2. Use Heimdall to extract abi @@ -229,7 +223,13 @@ impl<'a> EVMCorpusInitializer<'a> { let abis = fetch_abi_heimdall(contract_code) .iter() .map(|abi| { - if let Some(known_abi) = self.state.metadata().get::().unwrap().get(&abi.function) { + if let Some(known_abi) = self + .state + .metadata() + .get::() + .unwrap() + .get(&abi.function) + { known_abi } else { abi @@ -241,18 +241,30 @@ impl<'a> EVMCorpusInitializer<'a> { } } - artifacts.address_to_sourcemap.insert(contract.deployed_address, contract.source_map.clone()); - artifacts.address_to_abi.insert(contract.deployed_address, contract.abi.clone()); + artifacts + .address_to_sourcemap + .insert(contract.deployed_address, contract.source_map.clone()); + artifacts + .address_to_abi + .insert(contract.deployed_address, contract.abi.clone()); let mut code = vec![]; - self.executor.host.code.clone().get(&contract.deployed_address).map(|c| { + if let Some(c) = self + .executor + .host + .code + .clone() + .get(&contract.deployed_address) + { code.extend_from_slice(c.bytecode()); - }); + } artifacts.address_to_bytecode.insert( contract.deployed_address, - Bytecode::new_raw(Bytes::from(code)) + Bytecode::new_raw(Bytes::from(code)), + ); + artifacts.address_to_name.insert( + contract.deployed_address, + contract.name.clone().trim_end_matches('*').to_string(), ); - artifacts.address_to_name.insert(contract.deployed_address, - contract.name.clone().trim_end_matches('*').to_string()); #[cfg(feature = "flashloan_v2")] { @@ -264,16 +276,23 @@ impl<'a> EVMCorpusInitializer<'a> { ); } - if unsafe { BLACKLIST_ADDR.is_some() - && BLACKLIST_ADDR.as_ref().unwrap().contains(&contract.deployed_address) + && BLACKLIST_ADDR + .as_ref() + .unwrap() + .contains(&contract.deployed_address) } { continue; } for abi in contract.abi.clone() { - self.add_abi(&abi, self.scheduler, contract.deployed_address, &mut artifacts); + self.add_abi( + &abi, + self.scheduler, + contract.deployed_address, + &mut artifacts, + ); } // add transfer txn { @@ -298,9 +317,8 @@ impl<'a> EVMCorpusInitializer<'a> { add_input_to_corpus!(self.state, self.scheduler, input); } } - artifacts.initial_state = StagedVMState::new_with_state( - self.executor.host.evmstate.clone(), - ); + artifacts.initial_state = + StagedVMState::new_with_state(self.executor.host.evmstate.clone()); let mut tc = Testcase::new(artifacts.initial_state.clone()); tc.set_exec_time(Duration::from_secs(0)); @@ -366,7 +384,7 @@ impl<'a> EVMCorpusInitializer<'a> { None => { self.state .hash_to_address - .insert(abi.function.clone(), HashSet::from([deployed_address])); + .insert(abi.function, HashSet::from([deployed_address])); } } #[cfg(not(feature = "fuzz_static"))] @@ -376,7 +394,8 @@ impl<'a> EVMCorpusInitializer<'a> { let mut abi_instance = get_abi_type_boxed(&abi.abi); abi_instance.set_func_with_name(abi.function, abi.function_name.clone()); - artifacts.address_to_abi_object + artifacts + .address_to_abi_object .entry(deployed_address) .or_insert(vec![]) .push(abi_instance.clone()); @@ -405,7 +424,7 @@ impl<'a> EVMCorpusInitializer<'a> { add_input_to_corpus!(self.state, scheduler, input.clone()); #[cfg(feature = "print_txn_corpus")] { - let corpus_dir = format!("{}/corpus", self.work_dir.as_str()).to_string(); + let corpus_dir = format!("{}/corpus", self.work_dir.as_str()); dump_txn!(corpus_dir, &input) } #[cfg(feature = "use_presets")] diff --git a/src/evm/input.rs b/src/evm/input.rs index fb626d69c..6cfe85549 100644 --- a/src/evm/input.rs +++ b/src/evm/input.rs @@ -1,9 +1,9 @@ use crate::evm::abi::{AEmpty, AUnknown, BoxedABI}; -use crate::mutation_utils::byte_mutator; use crate::evm::mutator::AccessPattern; -use crate::evm::types::{EVMAddress, EVMExecutionResult, EVMStagedVMState, EVMU256, EVMU512}; +use crate::evm::types::{EVMAddress, EVMStagedVMState, EVMU256, EVMU512}; use crate::evm::vm::EVMState; use crate::input::{ConciseSerde, VMInputT}; +use crate::mutation_utils::byte_mutator; use crate::state::{HasCaller, HasItyState}; use crate::state_input::StagedVMState; @@ -11,18 +11,16 @@ use libafl::bolts::HasLen; use libafl::inputs::Input; use libafl::mutators::MutationResult; use libafl::prelude::{HasBytesVec, HasMaxSize, HasMetadata, HasRand, Rand, State}; -use primitive_types::U512; use revm_primitives::Env; use serde::{Deserialize, Deserializer, Serialize}; +use crate::generic_vm::vm_executor::ExecutionResult; +use crate::generic_vm::vm_state::VMStateT; use bytes::Bytes; use std::cell::RefCell; use std::fmt::Debug; use std::ops::Deref; use std::rc::Rc; -use crate::generic_vm::vm_executor::ExecutionResult; -use crate::generic_vm::vm_state::VMStateT; - /// EVM Input Types #[derive(Serialize, Deserialize, Clone, PartialEq, Eq, Debug)] @@ -37,12 +35,6 @@ pub enum EVMInputTy { Liquidate, } -impl Default for EVMInputTy { - fn default() -> Self { - EVMInputTy::ABI - } -} - /// EVM Input Trait pub trait EVMInputT { /// Set the contract and ABI @@ -90,7 +82,6 @@ pub trait EVMInputT { fn get_repeat(&self) -> usize; } - /// EVM Input #[derive(Serialize, Deserialize, Clone)] pub struct EVMInput { @@ -187,11 +178,14 @@ pub struct ConciseEVMInput { pub call_leak: u32, } - impl ConciseEVMInput { - pub fn from_input(input: &I, execution_result: &ExecutionResult) -> Self - where I: VMInputT + EVMInputT, - Out: Default + pub fn from_input( + input: &I, + execution_result: &ExecutionResult, + ) -> Self + where + I: VMInputT + EVMInputT, + Out: Default, { Self { #[cfg(feature = "flashloan_v2")] @@ -203,7 +197,7 @@ impl ConciseEVMInput { #[cfg(feature = "debug")] direct_data: match &input.get_data_abi() { Some(v) => hex::encode(v.get_bytes()), - None => "".to_string() + None => "".to_string(), }, txn_value: input.get_txn_value(), step: input.is_step(), @@ -215,8 +209,8 @@ impl ConciseEVMInput { layer: input.get_state().get_post_execution_len(), call_leak: match execution_result.additional_info { Some(ref info) => info[0] as u32, - None => u32::MAX - } + None => u32::MAX, + }, } } @@ -242,12 +236,11 @@ impl ConciseEVMInput { #[cfg(not(feature = "debug"))] direct_data: Bytes::new(), #[cfg(feature = "debug")] - direct_data: Bytes::from( - hex::decode(&self.direct_data).unwrap_or_default() - ), + direct_data: Bytes::from(hex::decode(&self.direct_data).unwrap_or_default()), randomness: self.randomness.clone(), repeat: self.repeat, - }, self.call_leak + }, + self.call_leak, ) } @@ -259,7 +252,8 @@ impl ConciseEVMInput { match self.data { Some(ref d) => Some(format!( "{:?} => {:?} {} with {} ETH ({}), liq percent: {}", - self.caller, self.contract, + self.caller, + self.contract, d.to_string(), self.txn_value.unwrap_or(EVMU256::ZERO), hex::encode(d.get_bytes()), @@ -268,20 +262,24 @@ impl ConciseEVMInput { None => match self.input_type { EVMInputTy::ABI | EVMInputTy::ArbitraryCallBoundedAddr => Some(format!( "{:?} => {:?} with {:?} ETH, liq percent: {}", - self.caller, self.contract, - self.txn_value, liq + self.caller, self.contract, self.txn_value, liq )), EVMInputTy::Borrow => Some(format!( "{:?} borrow token {:?} with {:?} ETH, liq percent: {}", - self.caller, self.contract, - self.txn_value, liq + self.caller, self.contract, self.txn_value, liq )), EVMInputTy::Liquidate => None, }, } #[cfg(feature = "debug")] - Some(format!("{:?} => {:?} with {:?} ETH, {}", self.caller, self.contract, self.txn_value, hex::encode(self.direct_data.clone()))) + Some(format!( + "{:?} => {:?} with {:?} ETH, {}", + self.caller, + self.contract, + self.txn_value, + hex::encode(self.direct_data.clone()) + )) } #[cfg(not(feature = "flashloan_v2"))] @@ -289,20 +287,22 @@ impl ConciseEVMInput { match self.data { Some(ref d) => Some(format!( "{:?} => {:?} {} with {} ETH ({})", - self.caller, self.contract, - d.to_string(), + self.caller, + self.contract, + d, self.txn_value.unwrap_or(EVMU256::ZERO), hex::encode(d.get_bytes()) )), - None => Some(format!("{:?} => {:?} transfer {} ETH", - self.caller, self.contract, - self.txn_value.unwrap_or(EVMU256::ZERO), + None => Some(format!( + "{:?} => {:?} transfer {} ETH", + self.caller, + self.contract, + self.txn_value.unwrap_or(EVMU256::ZERO), )), } } } - impl HasLen for EVMInput { /// Get the length of the ABI encoded input fn len(&self) -> usize { @@ -393,7 +393,6 @@ impl EVMInputT for EVMInput { } } - /// macro_rules! impl_env_mutator_u256 { ($item: ident, $loc: ident) => { @@ -406,14 +405,15 @@ macro_rules! impl_env_mutator_u256 { } else { None }; - let mut input_by: [u8; 32] = input.get_vm_env().$loc.$item.to_be_bytes(); + let input_by: [u8; 32] = input.get_vm_env().$loc.$item.to_be_bytes(); let mut input_vec = input_by.to_vec(); let mut wrapper = MutatorInput::new(&mut input_vec); let res = byte_mutator(state_, &mut wrapper, vm_slots); if res == MutationResult::Skipped { return res; } - input.get_vm_env_mut().$loc.$item = EVMU256::try_from_be_slice(&input_vec.as_slice()).unwrap(); + input.get_vm_env_mut().$loc.$item = + EVMU256::try_from_be_slice(&input_vec.as_slice()).unwrap(); res } }; @@ -502,7 +502,7 @@ impl EVMInput { { // not supported yet // unreachable!(); - return MutationResult::Skipped; + MutationResult::Skipped } pub fn gas_price(_input: &mut EVMInput, _state_: &mut S) -> MutationResult @@ -511,7 +511,7 @@ impl EVMInput { { // not supported yet // unreachable!(); - return MutationResult::Skipped; + MutationResult::Skipped } pub fn balance(_input: &mut EVMInput, _state_: &mut S) -> MutationResult @@ -520,7 +520,7 @@ impl EVMInput { { // not supported yet // unreachable!(); - return MutationResult::Skipped; + MutationResult::Skipped } pub fn caller(input: &mut EVMInput, state_: &mut S) -> MutationResult @@ -529,7 +529,7 @@ impl EVMInput { { let caller = state_.get_rand_caller(); if caller == input.get_caller() { - return MutationResult::Skipped; + MutationResult::Skipped } else { input.set_caller(caller); MutationResult::Mutated @@ -540,15 +540,8 @@ impl EVMInput { where S: State + HasCaller + HasRand + HasMetadata, { - let vm_slots = if let Some(s) = input.get_state().get(&input.get_contract()) { - Some(s.clone()) - } else { - None - }; - let mut input_by: [u8; 32] = input - .get_txn_value() - .unwrap_or(EVMU256::ZERO) - .to_be_bytes(); + let vm_slots = input.get_state().get(&input.get_contract()).cloned(); + let input_by: [u8; 32] = input.get_txn_value().unwrap_or(EVMU256::ZERO).to_be_bytes(); let mut input_vec = input_by.to_vec(); let mut wrapper = MutatorInput::new(&mut input_vec); let res = byte_mutator(state_, &mut wrapper, vm_slots); @@ -556,9 +549,9 @@ impl EVMInput { return res; } // make set first 16 bytes to 0 - for i in 0..16 { + (0..16).for_each(|i| { input_vec[i] = 0; - } + }); input.set_txn_value(EVMU256::try_from_be_slice(input_vec.as_slice()).unwrap()); res } @@ -585,7 +578,7 @@ impl EVMInput { }; } add_mutator!(caller); - add_mutator!(balance, ap.balance.len() > 0); + add_mutator!(balance, !ap.balance.is_empty()); if ap.call_value || self.get_txn_value().is_some() { mutators .push(&EVMInput::call_value as &dyn Fn(&mut EVMInput, &mut S) -> MutationResult); @@ -599,7 +592,7 @@ impl EVMInput { add_mutator!(chain_id); add_mutator!(prevrandao); - if mutators.len() == 0 { + if mutators.is_empty() { return MutationResult::Skipped; } @@ -614,8 +607,7 @@ impl ConciseSerde for ConciseEVMInput { } fn deserialize_concise(data: &[u8]) -> Self { - serde_json::from_slice(data) - .expect("Failed to deserialize concise input") + serde_json::from_slice(data).expect("Failed to deserialize concise input") } fn serialize_string(&self) -> String { @@ -624,10 +616,14 @@ impl ConciseSerde for ConciseEVMInput { s.push_str("=="); } if self.layer > 0 { - s.push_str(" "); + s.push(' '); } - s.push_str(self.pretty_txn().expect("Failed to pretty print txn").as_str()); + s.push_str( + self.pretty_txn() + .expect("Failed to pretty print txn") + .as_str(), + ); s } } @@ -645,11 +641,7 @@ impl VMInputT for EVMInput { if state.rand_mut().next() % 100 > 87 || self.data.is_none() { return self.mutate_env_with_access_pattern(state); } - let vm_slots = if let Some(s) = self.get_state().get(&self.get_contract()) { - Some(s.clone()) - } else { - None - }; + let vm_slots = self.get_state().get(&self.get_contract()).cloned(); match self.data { Some(ref mut data) => data.mutate_with_vm_slots(state, vm_slots), None => MutationResult::Skipped, @@ -661,7 +653,7 @@ impl VMInputT for EVMInput { } fn get_caller(&self) -> EVMAddress { - self.caller.clone() + self.caller } fn set_caller(&mut self, caller: EVMAddress) { @@ -669,7 +661,7 @@ impl VMInputT for EVMInput { } fn get_contract(&self) -> EVMAddress { - self.contract.clone() + self.contract } fn get_state(&self) -> &EVMState { @@ -751,7 +743,10 @@ impl VMInputT for EVMInput { self.txn_value } - fn get_concise(&self, exec_res: &ExecutionResult) -> ConciseEVMInput { + fn get_concise( + &self, + exec_res: &ExecutionResult, + ) -> ConciseEVMInput { ConciseEVMInput::from_input(self, exec_res) } } diff --git a/src/evm/middlewares/call_printer.rs b/src/evm/middlewares/call_printer.rs index b96e54deb..6e421a146 100644 --- a/src/evm/middlewares/call_printer.rs +++ b/src/evm/middlewares/call_printer.rs @@ -1,48 +1,57 @@ -use std::collections::{HashMap, HashSet}; -use std::fmt::{Debug}; -use std::fs; -use std::fs::OpenOptions; -use std::io::Write; -use std::ops::AddAssign; -use std::path::Path; -use std::time::{SystemTime, UNIX_EPOCH}; -use itertools::Itertools; -use libafl::inputs::Input; -use libafl::prelude::{HasCorpus, HasMetadata, State}; -use revm_interpreter::Interpreter; -use revm_interpreter::opcode::{INVALID, JUMPDEST, JUMPI, REVERT, STOP}; -use revm_primitives::Bytecode; -use serde::{Deserialize, Serialize}; use crate::evm::host::FuzzHost; use crate::evm::input::{ConciseEVMInput, EVMInput, EVMInputT}; use crate::evm::middlewares::middleware::{Middleware, MiddlewareType}; -use crate::evm::srcmap::parser::{pretty_print_source_map, SourceMapAvailability, SourceMapLocation, SourceMapWithCode}; use crate::evm::srcmap::parser::SourceMapAvailability::Available; +use crate::evm::srcmap::parser::{ + pretty_print_source_map, SourceMapAvailability, SourceMapLocation, SourceMapWithCode, +}; +use crate::evm::types::{as_u64, convert_u256_to_h160, is_zero, EVMAddress, ProjectSourceMapTy}; +use crate::evm::vm::IN_DEPLOY; use crate::generic_vm::vm_state::VMStateT; use crate::input::VMInputT; use crate::state::{HasCaller, HasCurrentInputIdx, HasItyState}; -use crate::evm::types::{as_u64, convert_u256_to_h160, EVMAddress, is_zero, ProjectSourceMapTy}; -use crate::evm::vm::IN_DEPLOY; +use itertools::Itertools; +use libafl::inputs::Input; +use libafl::prelude::{HasCorpus, HasMetadata, State}; +use revm_interpreter::opcode::{INVALID, JUMPDEST, JUMPI, REVERT, STOP}; +use revm_interpreter::Interpreter; +use revm_primitives::Bytecode; +use serde::{Deserialize, Serialize}; use serde_json; +use std::collections::{HashMap, HashSet}; +use std::fmt::Debug; +use std::fs; +use std::fs::OpenOptions; +use std::io::Write; +use std::ops::AddAssign; +use std::path::Path; +use std::time::{SystemTime, UNIX_EPOCH}; #[derive(Clone, Debug, Serialize, Default, Deserialize)] pub struct CallPrinter { pub layer: usize, - pub data: String + pub data: String, } - impl CallPrinter { pub fn new() -> Self { - Self { layer: 0, data: "".to_string() } + Self { + layer: 0, + data: "".to_string(), + } } pub fn register_input(&mut self, input: &EVMInput) { self.layer = 0; self.data = "".to_string(); self.data.push_str( - format!("[{:?}]=>{:?} {}", input.get_caller(), input.get_contract(), hex::encode(input.to_bytes())) - .as_str() + format!( + "[{:?}]=>{:?} {}", + input.get_caller(), + input.get_contract(), + hex::encode(input.to_bytes()) + ) + .as_str(), ) } @@ -51,12 +60,11 @@ impl CallPrinter { } } - impl Middleware for CallPrinter - where - I: Input + VMInputT + EVMInputT + 'static, - VS: VMStateT, - S: State +where + I: Input + VMInputT + EVMInputT + 'static, + VS: VMStateT, + S: State + HasCaller + HasCorpus + HasItyState @@ -72,18 +80,8 @@ impl Middleware for CallPrinter state: &mut S, ) { let (arg_offset, arg_len) = match unsafe { *interp.instruction_pointer } { - 0xf1 | 0xf2 => { - ( - interp.stack.peek(3).unwrap(), - interp.stack.peek(4).unwrap(), - ) - } - 0xf4 | 0xfa => { - ( - interp.stack.peek(2).unwrap(), - interp.stack.peek(3).unwrap(), - ) - } + 0xf1 | 0xf2 => (interp.stack.peek(3).unwrap(), interp.stack.peek(4).unwrap()), + 0xf4 | 0xfa => (interp.stack.peek(2).unwrap(), interp.stack.peek(3).unwrap()), _ => { return; } @@ -94,7 +92,11 @@ impl Middleware for CallPrinter let arg_offset = as_u64(arg_offset) as usize; let arg_len = as_u64(arg_len) as usize; - let arg = interp.memory.get_slice(arg_offset, arg_len); + let arg = if arg_len == 0 { + vec![] + } else { + interp.memory.get_slice(arg_offset, arg_len).to_vec() + }; let caller = interp.contract.address; let address = match *interp.instruction_pointer { @@ -114,9 +116,8 @@ impl Middleware for CallPrinter address_h160, hex::encode(arg) ) - .as_str(), + .as_str(), ); - } unsafe fn on_return( @@ -128,12 +129,16 @@ impl Middleware for CallPrinter self.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, + ) { } fn get_type(&self) -> MiddlewareType { MiddlewareType::CallPrinter } } - diff --git a/src/fuzzer.rs b/src/fuzzer.rs index e0b7aaebe..af43c0778 100644 --- a/src/fuzzer.rs +++ b/src/fuzzer.rs @@ -1,14 +1,13 @@ /// Implements fuzzing logic for ItyFuzz - use crate::{ input::VMInputT, state::{HasCurrentInputIdx, HasInfantStateState, HasItyState, InfantStateState}, state_input::StagedVMState, }; use std::collections::hash_map::DefaultHasher; -use std::collections::{HashMap, HashSet}; +use std::collections::HashMap; use std::fmt::Debug; -use std::fs::{File, OpenOptions}; +use std::fs::File; use std::io::Write; use std::path::Path; @@ -33,22 +32,18 @@ use libafl::{ }; use crate::evm::host::JMP_MAP; +use crate::input::ConciseSerde; +use crate::scheduler::HasReportCorpus; +use crate::telemetry::report_vulnerability; +use libafl::prelude::HasRand; use serde::de::DeserializeOwned; use serde::Serialize; use std::hash::{Hash, Hasher}; -use libafl::prelude::HasRand; -use primitive_types::H256; -use crate::evm::input::ConciseEVMInput; -use crate::evm::vm::EVMState; -use crate::input::ConciseSerde; -use crate::scheduler::{HasReportCorpus, HasVote}; -use crate::telemetry::report_vulnerability; const STATS_TIMEOUT_DEFAULT: Duration = Duration::from_millis(100); pub static mut RUN_FOREVER: bool = false; pub static mut ORACLE_OUTPUT: String = String::new(); - /// A fuzzer that implements ItyFuzz logic using LibAFL's [`Fuzzer`] trait /// /// CS: The scheduler for the input corpus @@ -65,7 +60,8 @@ pub static mut ORACLE_OUTPUT: String = String::new(); pub struct ItyFuzzer<'a, VS, Loc, Addr, Out, CS, IS, F, IF, IFR, I, OF, S, OT, CI> where CS: Scheduler, - IS: Scheduler, InfantStateState> + HasReportCorpus>, + IS: Scheduler, InfantStateState> + + HasReportCorpus>, F: Feedback, IF: Feedback, IFR: Feedback, @@ -101,7 +97,8 @@ 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> where CS: Scheduler, - IS: Scheduler, InfantStateState> + HasReportCorpus>, + IS: Scheduler, InfantStateState> + + HasReportCorpus>, F: Feedback, IF: Feedback, IFR: Feedback, @@ -138,12 +135,7 @@ where /// Called every time a new testcase is added to the corpus /// Setup the minimizer map - pub fn on_add_corpus( - &mut self, - input: &I, - coverage: &[u8; MAP_SIZE], - testcase_idx: usize, - ) -> () { + pub fn on_add_corpus(&mut self, input: &I, coverage: &[u8; MAP_SIZE], testcase_idx: usize) { let mut hasher = DefaultHasher::new(); coverage.hash(&mut hasher); let hash = hasher.finish(); @@ -157,7 +149,7 @@ where &mut self, (hash, new_fav_factor, _): (u64, f64, usize), new_testcase_idx: usize, - ) -> () { + ) { let res = self.minimizer_map.get_mut(&hash).unwrap(); res.0 = new_testcase_idx; res.1 = new_fav_factor; @@ -180,7 +172,7 @@ where let new_fav_factor = input.fav_factor(); // if the new testcase has a higher fav factor, replace the old one if new_fav_factor > *fav_factor { - return Some((hash, new_fav_factor, testcase_idx.clone())); + return Some((hash, new_fav_factor, *testcase_idx)); } } None @@ -188,23 +180,30 @@ where } /// 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 +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> where CS: Scheduler, - IS: Scheduler, InfantStateState> + HasReportCorpus>, + IS: Scheduler, InfantStateState> + + HasReportCorpus>, EM: EventManager, F: Feedback, IF: Feedback, IFR: Feedback, I: VMInputT, OF: Feedback, - S: HasClientPerfMonitor + HasExecutions + HasMetadata + HasCurrentInputIdx + HasRand + HasCorpus, + S: HasClientPerfMonitor + + HasExecutions + + HasMetadata + + HasCurrentInputIdx + + HasRand + + HasCorpus, ST: StagesTuple + ?Sized, VS: Default + VMStateT, Addr: Serialize + DeserializeOwned + Debug + Clone, Loc: Serialize + DeserializeOwned + Debug + Clone, - CI: Serialize + DeserializeOwned + Debug + Clone + ConciseSerde + CI: Serialize + DeserializeOwned + Debug + Clone + ConciseSerde, { /// Fuzz one input fn fuzz_one( @@ -250,77 +249,85 @@ pub static mut DUMP_FILE_COUNT: usize = 0; pub static mut REPLAY: bool = false; - #[macro_export] macro_rules! dump_file { - ($state: expr, $corpus_path: expr, $print: expr) => { - { - if !unsafe {REPLAY} { - unsafe { - DUMP_FILE_COUNT += 1; - } - - let tx_trace = $state.get_execution_result().new_state.trace.clone(); - let txn_text = tx_trace.to_string($state); - let txn_text_replayable = tx_trace.to_file_str($state); + ($state: expr, $corpus_path: expr, $print: expr) => {{ + if !unsafe { REPLAY } { + unsafe { + DUMP_FILE_COUNT += 1; + } - let data = format!( - "Reverted? {} \n Txn: {}", - $state.get_execution_result().reverted, - txn_text - ); - if $print { - println!("============= New Corpus Item ============="); - println!("{}", data); - println!("=========================================="); - } + let tx_trace = $state.get_execution_result().new_state.trace.clone(); + let txn_text = tx_trace.to_string($state); + let txn_text_replayable = tx_trace.to_file_str($state); - // write to file - let path = Path::new($corpus_path.as_str()); - if !path.exists() { - std::fs::create_dir_all(path).unwrap(); - } - let mut file = - File::create(format!("{}/{}", $corpus_path, unsafe { DUMP_FILE_COUNT })).unwrap(); - file.write_all(data.as_bytes()).unwrap(); + let data = format!( + "Reverted? {} \n Txn: {}", + $state.get_execution_result().reverted, + txn_text + ); + if $print { + println!("============= New Corpus Item ============="); + println!("{}", data); + println!("=========================================="); + } - let mut replayable_file = - File::create(format!("{}/{}_replayable", $corpus_path, unsafe { DUMP_FILE_COUNT })).unwrap(); - replayable_file.write_all(txn_text_replayable.as_bytes()).unwrap(); + // write to file + let path = Path::new($corpus_path.as_str()); + if !path.exists() { + std::fs::create_dir_all(path).unwrap(); } + let mut file = + File::create(format!("{}/{}", $corpus_path, unsafe { DUMP_FILE_COUNT })).unwrap(); + file.write_all(data.as_bytes()).unwrap(); + + let mut replayable_file = + File::create(format!("{}/{}_replayable", $corpus_path, unsafe { + DUMP_FILE_COUNT + })) + .unwrap(); + replayable_file + .write_all(txn_text_replayable.as_bytes()) + .unwrap(); } - }; + }}; } #[macro_export] macro_rules! dump_txn { - ($corpus_path: expr, $input: expr) => { - { - if !unsafe {REPLAY} { - unsafe { - DUMP_FILE_COUNT += 1; - } - // write to file - let path = Path::new($corpus_path.as_str()); - if !path.exists() { - std::fs::create_dir_all(path).unwrap(); - } - - let concise_input = ConciseEVMInput::from_input($input, &EVMExecutionResult::empty_result()); - - let txn_text = concise_input.serialize_string(); - let txn_text_replayable = String::from_utf8(concise_input.serialize_concise()).unwrap(); - - let mut file = - File::create(format!("{}/{}_seed", $corpus_path, unsafe { DUMP_FILE_COUNT })).unwrap(); - file.write_all(txn_text.as_bytes()).unwrap(); - - let mut replayable_file = - File::create(format!("{}/{}_seed_replayable", $corpus_path, unsafe { DUMP_FILE_COUNT })).unwrap(); - replayable_file.write_all(txn_text_replayable.as_bytes()).unwrap(); + ($corpus_path: expr, $input: expr) => {{ + if !unsafe { REPLAY } { + unsafe { + DUMP_FILE_COUNT += 1; } + // write to file + let path = Path::new($corpus_path.as_str()); + if !path.exists() { + std::fs::create_dir_all(path).unwrap(); + } + + let concise_input = + ConciseEVMInput::from_input($input, &EVMExecutionResult::empty_result()); + + let txn_text = concise_input.serialize_string(); + let txn_text_replayable = String::from_utf8(concise_input.serialize_concise()).unwrap(); + + let mut file = File::create(format!("{}/{}_seed", $corpus_path, unsafe { + DUMP_FILE_COUNT + })) + .unwrap(); + file.write_all(txn_text.as_bytes()).unwrap(); + + let mut replayable_file = + File::create(format!("{}/{}_seed_replayable", $corpus_path, unsafe { + DUMP_FILE_COUNT + })) + .unwrap(); + replayable_file + .write_all(txn_text_replayable.as_bytes()) + .unwrap(); } - }; + }}; } // implement evaluator trait for ItyFuzzer @@ -328,7 +335,8 @@ impl<'a, VS, Loc, Addr, Out, E, EM, I, S, CS, IS, F, IF, IFR, OF, OT, CI> Evalua for ItyFuzzer<'a, VS, Loc, Addr, Out, CS, IS, F, IF, IFR, I, OF, S, OT, CI> where CS: Scheduler, - IS: Scheduler, InfantStateState> + HasReportCorpus>, + IS: Scheduler, InfantStateState> + + HasReportCorpus>, F: Feedback, IF: Feedback, IFR: Feedback, @@ -350,7 +358,7 @@ where Addr: Serialize + DeserializeOwned + Debug + Clone, Loc: Serialize + DeserializeOwned + Debug + Clone, Out: Default, - CI: Serialize + DeserializeOwned + Debug + Clone + ConciseSerde + CI: Serialize + DeserializeOwned + Debug + Clone + ConciseSerde, { /// Evaluate input (execution + feedback + objectives) fn evaluate_input_events( @@ -409,15 +417,15 @@ where state_idx = state.add_infant_state( &state.get_execution_result().new_state.clone(), self.infant_scheduler, - input.get_state_idx() + input.get_state_idx(), ); if self .infant_result_feedback - .is_interesting(state, manager, &input, observers, &exitkind)? { - self.infant_scheduler.sponsor_state( - state.get_infant_state_state(), state_idx, 3 - ) + .is_interesting(state, manager, &input, observers, &exitkind)? + { + self.infant_scheduler + .sponsor_state(state.get_infant_state_state(), state_idx, 3) } } @@ -435,7 +443,7 @@ where // Debugging prints #[cfg(feature = "print_txn_corpus")] { - let corpus_dir = format!("{}/corpus", self.work_dir.as_str()).to_string(); + let corpus_dir = format!("{}/corpus", self.work_dir.as_str()); dump_file!(state, corpus_dir, true); } } @@ -449,12 +457,10 @@ where Some((hash, new_fav_factor, old_testcase_idx)) => { state.corpus_mut().remove(old_testcase_idx)?; - let mut testcase = Testcase::new(input.clone()); + let testcase = Testcase::new(input.clone()); let new_testcase_idx = state.corpus_mut().add(testcase)?; - self.infant_scheduler.report_corpus( - state.get_infant_state_state(), - state_idx - ); + self.infant_scheduler + .report_corpus(state.get_infant_state_state(), state_idx); self.scheduler.on_add(state, new_testcase_idx)?; self.on_replace_corpus( (hash, new_fav_factor, old_testcase_idx), @@ -478,10 +484,8 @@ where let mut testcase = Testcase::new(input.clone()); self.feedback.append_metadata(state, &mut testcase)?; let idx = state.corpus_mut().add(testcase)?; - self.infant_scheduler.report_corpus( - state.get_infant_state_state(), - state_idx - ); + self.infant_scheduler + .report_corpus(state.get_infant_state_state(), state_idx); self.scheduler.on_add(state, idx)?; self.on_add_corpus(&input, unsafe { &JMP_MAP }, idx); @@ -510,9 +514,7 @@ where } // find the solution ExecuteInputResult::Solution => { - report_vulnerability( - unsafe {ORACLE_OUTPUT.clone()}, - ); + report_vulnerability(unsafe { ORACLE_OUTPUT.clone() }); println!("\n\n\nšŸ˜ŠšŸ˜Š Found violations! \n\n"); let cur_report = format!( diff --git a/src/fuzzers/evm_fuzzer.rs b/src/fuzzers/evm_fuzzer.rs index 821734a15..c5ed0165d 100644 --- a/src/fuzzers/evm_fuzzer.rs +++ b/src/fuzzers/evm_fuzzer.rs @@ -6,48 +6,38 @@ use std::io::Read; use std::ops::Deref; use std::path::Path; use std::rc::Rc; -use std::str::FromStr; use std::sync::Arc; 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::stages::StdMutationalStage; use libafl::{ prelude::{tuple_list, MaxMapFeedback, SimpleMonitor, StdMapObserver}, Evaluator, Fuzzer, }; -use glob::glob; -use itertools::Itertools; -use crate::evm::host::{ACTIVE_MATCH_EXT_CALL, CMP_MAP, JMP_MAP, PANIC_ON_BUG, READ_MAP, WRITE_MAP, WRITE_RELATIONSHIPS}; -use crate::evm::host::{CALL_UNTIL}; +use crate::evm::host::CALL_UNTIL; +use crate::evm::host::{ + ACTIVE_MATCH_EXT_CALL, CMP_MAP, JMP_MAP, PANIC_ON_BUG, READ_MAP, WRITE_MAP, WRITE_RELATIONSHIPS, +}; use crate::evm::vm::EVMState; use crate::feedback::{CmpFeedback, DataflowFeedback, OracleFeedback}; use crate::scheduler::SortedDroppingScheduler; -use crate::state::{FuzzState, HasCaller, HasExecutionResult}; -use crate::state_input::StagedVMState; +use crate::state::HasExecutionResult; use crate::evm::config::Config; use crate::evm::corpus_initializer::EVMCorpusInitializer; -use crate::evm::input::{ConciseEVMInput, EVMInput, EVMInputT, EVMInputTy}; +use crate::evm::input::{ConciseEVMInput, EVMInput}; -use crate::evm::mutator::{AccessPattern, FuzzMutator}; -use crate::evm::onchain::flashloan::Flashloan; -use crate::evm::onchain::onchain::{OnChain, WHITELIST_ADDR}; -use crate::evm::onchain::selfdestruct::{Selfdestruct}; -use crate::evm::presets::pair::PairPreset; -use crate::evm::types::{EVMAddress, EVMFuzzMutator, EVMFuzzState, EVMU256, fixed_address}; -use primitive_types::{H160, U256}; -use revm_primitives::{BlockEnv, Bytecode, Env}; -use revm_primitives::bitvec::view::BitViewSized; use crate::evm::abi::ABIAddressToInstanceMap; -use crate::evm::concolic::concolic_host::ConcolicHost; use crate::evm::concolic::concolic_stage::{ConcolicFeedbackWrapper, ConcolicStage}; use crate::evm::cov_stage::CoverageStage; use crate::evm::feedbacks::Sha3WrappedFeedback; @@ -55,24 +45,32 @@ use crate::evm::middlewares::call_printer::CallPrinter; use crate::evm::middlewares::coverage::{Coverage, EVAL_COVERAGE}; use crate::evm::middlewares::middleware::Middleware; use crate::evm::middlewares::sha3_bypass::{Sha3Bypass, Sha3TaintAnalysis}; +use crate::evm::mutator::FuzzMutator; +use crate::evm::onchain::flashloan::Flashloan; +use crate::evm::onchain::onchain::{OnChain, WHITELIST_ADDR}; +use crate::evm::onchain::selfdestruct::Selfdestruct; use crate::evm::oracles::echidna::EchidnaOracle; use crate::evm::oracles::state_comp::StateCompOracle; use crate::evm::srcmap::parser::BASE_PATH; +use crate::evm::types::{fixed_address, EVMAddress, EVMFuzzMutator, EVMFuzzState, EVMU256}; use crate::fuzzer::{REPLAY, RUN_FOREVER}; -use crate::input::{ConciseSerde, VMInputT}; - -struct ABIConfig { - abi: String, - function: [u8; 4], -} - -struct ContractInfo { - name: String, - abi: Vec, -} +use crate::input::ConciseSerde; +use revm_primitives::Bytecode; pub fn evm_fuzzer( - config: Config, EVMInput, EVMFuzzState, ConciseEVMInput>, state: &mut EVMFuzzState + config: Config< + EVMState, + EVMAddress, + Bytecode, + Bytes, + EVMAddress, + EVMU256, + Vec, + EVMInput, + EVMFuzzState, + ConciseEVMInput, + >, + state: &mut EVMFuzzState, ) { // create work dir if not exists let path = Path::new(config.work_dir.as_str()); @@ -83,7 +81,7 @@ pub fn evm_fuzzer( let monitor = SimpleMonitor::new(|s| println!("{}", s)); let mut mgr = SimpleEventManager::new(monitor); let infant_scheduler = SortedDroppingScheduler::new(); - let mut scheduler = QueueScheduler::new(); + let scheduler = QueueScheduler::new(); let jmps = unsafe { &mut JMP_MAP }; let cmps = unsafe { &mut CMP_MAP }; @@ -100,11 +98,11 @@ pub fn evm_fuzzer( let mid = Rc::new(RefCell::new( Selfdestruct::::new(), )); - fuzz_host.add_middlewares(mid.clone()); + fuzz_host.add_middlewares(mid); // Selfdestruct end } - let onchain_middleware = match config.onchain.clone() { + let _onchain_middleware = match config.onchain.clone() { Some(onchain) => { Some({ let mid = Rc::new(RefCell::new( @@ -147,7 +145,7 @@ pub fn evm_fuzzer( PANIC_ON_BUG = config.panic_on_bug; } - if config.only_fuzz.len() > 0 { + if !config.only_fuzz.is_empty() { unsafe { WHITELIST_ADDR = Some(config.only_fuzz); } @@ -198,10 +196,10 @@ pub fn evm_fuzzer( let mut corpus_initializer = EVMCorpusInitializer::new( &mut evm_executor, - &mut scheduler, + &scheduler, &infant_scheduler, state, - config.work_dir.clone() + config.work_dir.clone(), ); #[cfg(feature = "use_presets")] @@ -210,11 +208,12 @@ pub fn evm_fuzzer( let mut artifacts = corpus_initializer.initialize(&mut config.contract_loader.clone()); let mut instance_map = ABIAddressToInstanceMap::new(); - artifacts.address_to_abi_object.iter().for_each( - |(addr, abi)| { - instance_map.map.insert(addr.clone(), abi.clone()); - } - ); + artifacts + .address_to_abi_object + .iter() + .for_each(|(addr, abi)| { + instance_map.map.insert(*addr, abi.clone()); + }); let cov_middleware = Rc::new(RefCell::new(Coverage::new( artifacts.address_to_sourcemap.clone(), @@ -224,9 +223,7 @@ pub fn evm_fuzzer( evm_executor.host.add_middlewares(cov_middleware.clone()); - state.add_metadata( - instance_map - ); + state.add_metadata(instance_map); evm_executor.host.initialize(state); @@ -234,42 +231,33 @@ pub fn evm_fuzzer( let evm_executor_ref = Rc::new(RefCell::new(evm_executor)); - for (addr, bytecode) in &mut artifacts.address_to_bytecode { unsafe { cov_middleware.deref().borrow_mut().on_insert( bytecode, *addr, &mut evm_executor_ref.deref().borrow_mut().host, - state + state, ); } } - let mut feedback = MaxMapFeedback::new(&jmp_observer); - feedback - .init_state(state) - .expect("Failed to init state"); + feedback.init_state(state).expect("Failed to init state"); // let calibration = CalibrationStage::new(&feedback); let concolic_stage = ConcolicStage::new( config.concolic, config.concolic_caller, - evm_executor_ref.clone() + evm_executor_ref.clone(), ); let mutator: EVMFuzzMutator<'_> = FuzzMutator::new(&infant_scheduler); let std_stage = StdMutationalStage::new(mutator); - let coverage_obs_stage = CoverageStage::new( - evm_executor_ref.clone(), - cov_middleware.clone(), - ); + let coverage_obs_stage = CoverageStage::new(evm_executor_ref.clone(), cov_middleware.clone()); let mut stages = tuple_list!(std_stage, concolic_stage, coverage_obs_stage); - - let mut executor = FuzzExecutor::new(evm_executor_ref.clone(), tuple_list!(jmp_observer)); #[cfg(feature = "deployer_is_attacker")] @@ -281,54 +269,46 @@ pub fn evm_fuzzer( if config.echidna_oracle { let echidna_oracle = EchidnaOracle::new( - artifacts.address_to_abi.iter() - .map( - |(address, abis)| { - abis.iter().filter( - |abi| { - abi.function_name.starts_with("echidna_") - && abi.abi == "()" - } - ).map( - |abi| (address.clone(), abi.function.to_vec()) - ).collect_vec() - } - ).flatten().collect_vec(), - - artifacts.address_to_abi.iter() - .map( - |(address, abis)| { - abis.iter().filter( - |abi| { - abi.function_name.starts_with("echidna_") - && abi.abi == "()" - } - ).map( - |abi| (abi.function.to_vec(), abi.function_name.clone()) - ).collect_vec() - } - ).flatten().collect::, String>>(), + artifacts + .address_to_abi + .iter() + .flat_map(|(address, abis)| { + abis.iter() + .filter(|abi| abi.function_name.starts_with("echidna_") && abi.abi == "()") + .map(|abi| (*address, abi.function.to_vec())) + .collect_vec() + }) + .collect_vec(), + artifacts + .address_to_abi + .iter() + .flat_map(|(_address, abis)| { + abis.iter() + .filter(|abi| abi.function_name.starts_with("echidna_") && abi.abi == "()") + .map(|abi| (abi.function.to_vec(), abi.function_name.clone())) + .collect_vec() + }) + .collect::, String>>(), ); oracles.push(Rc::new(RefCell::new(echidna_oracle))); } if let Some(path) = config.state_comp_oracle { - let mut file = File::open(path.clone()).expect("Failed to open state comp oracle file"); + let mut file = File::open(path).expect("Failed to open state comp oracle file"); let mut buf = String::new(); - file.read_to_string(&mut buf).expect("Failed to read state comp oracle file"); + file.read_to_string(&mut buf) + .expect("Failed to read state comp oracle file"); - let evm_state = serde_json::from_str::(buf.as_str()).expect("Failed to parse state comp oracle file"); + let evm_state = serde_json::from_str::(buf.as_str()) + .expect("Failed to parse state comp oracle file"); - let oracle = Rc::new(RefCell::new( - StateCompOracle::new( - evm_state, - config.state_comp_matching.unwrap(), - ) - )); + let oracle = Rc::new(RefCell::new(StateCompOracle::new( + evm_state, + config.state_comp_matching.unwrap(), + ))); oracles.push(oracle); } - let mut producers = config.producers; let objective = OracleFeedback::new(&mut oracles, &mut producers, evm_executor_ref.clone()); @@ -336,7 +316,7 @@ pub fn evm_fuzzer( feedback, sha3_taint, evm_executor_ref.clone(), - config.sha3_bypass + config.sha3_bypass, )); let mut fuzzer = ItyFuzzer::new( @@ -360,7 +340,10 @@ pub fn evm_fuzzer( } let printer = Rc::new(RefCell::new(CallPrinter::new())); - evm_executor_ref.borrow_mut().host.add_middlewares(printer.clone()); + evm_executor_ref + .borrow_mut() + .host + .add_middlewares(printer.clone()); let initial_vm_state = artifacts.initial_state.clone(); for file in glob(files.as_str()).expect("Failed to read glob pattern") { @@ -373,19 +356,22 @@ pub fn evm_fuzzer( let mut idx = 0; - for txn in transactions.split("\n") { + for txn in transactions.split('\n') { idx += 1; // let splitter = txn.split(" ").collect::>(); if txn.len() < 4 { continue; } + print!(" "); // [is_step] [caller] [target] [input] [value] let (inp, call_until) = ConciseEVMInput::deserialize_concise(txn.as_bytes()) .to_input(vm_state.clone()); printer.borrow_mut().register_input(&inp); - unsafe {CALL_UNTIL = call_until;} + unsafe { + CALL_UNTIL = call_until; + } fuzzer .evaluate_input_events(state, &mut executor, &mut mgr, inp, false) @@ -396,10 +382,7 @@ pub fn evm_fuzzer( "reverted: {:?}", state.get_execution_result().clone().reverted ); - println!( - "call trace:\n{}", - printer.deref().borrow().get_trace() - ); + println!("call trace:\n{}", printer.deref().borrow().get_trace()); println!( "output: {:?}", hex::encode(state.get_execution_result().clone().output) diff --git a/src/telemetry.rs b/src/telemetry.rs index ed66a28f6..4db87e24d 100644 --- a/src/telemetry.rs +++ b/src/telemetry.rs @@ -1,33 +1,33 @@ //! Providing telemetry for the fuzzing campaign pub use reqwest; -use std::env; -use std::fmt::format; use serde_json::json; pub static mut TELEMETRY_ENABLED: bool = true; - const TELEMETRY_HOST: &str = "https://telemetry.fuzz.land/api/v1/"; /// sends a ping to the telemetry server when campaign starts /// only send (is_onchain, first 10 bytes of contract_address / name) pub fn report_campaign(is_onchain: bool, contracts: String) { - - if unsafe {TELEMETRY_ENABLED} { + if unsafe { TELEMETRY_ENABLED } { let json = json!({ "is_onchain": is_onchain, "contracts": contracts }); let client = reqwest::blocking::Client::new(); - client.post(TELEMETRY_HOST.to_owned() + "telemetry").json(&json).send().unwrap(); + client + .post(TELEMETRY_HOST.to_owned() + "telemetry") + .json(&json) + .send() + .unwrap(); } } /// sends a ping to the telemetry server when vulnerability is found pub fn report_vulnerability(vuln: String) { - if unsafe {TELEMETRY_ENABLED} { - reqwest::blocking::get(TELEMETRY_HOST.to_owned() + format!("vuln/{}",vuln.len()).as_str()) + if unsafe { TELEMETRY_ENABLED } { + reqwest::blocking::get(TELEMETRY_HOST.to_owned() + format!("vuln/{}", vuln.len()).as_str()) .unwrap(); } }