From 5e1143c7561479d01db1e5f5e45d6eb8d0961a85 Mon Sep 17 00:00:00 2001 From: "0xAWM.eth" <29773064+0xAWM@users.noreply.github.com> Date: Fri, 24 Nov 2023 15:44:36 +0800 Subject: [PATCH 01/23] fix srcmap not match solc generated code --- src/evm/bytecode_iterator.rs | 9 +++++++++ src/evm/srcmap/mod.rs | 6 ++---- 2 files changed, 11 insertions(+), 4 deletions(-) diff --git a/src/evm/bytecode_iterator.rs b/src/evm/bytecode_iterator.rs index 1fc030c17..e9acf18bf 100644 --- a/src/evm/bytecode_iterator.rs +++ b/src/evm/bytecode_iterator.rs @@ -39,6 +39,13 @@ pub fn all_bytecode(bytes: &Vec) -> Vec<(usize, u8)> { if bytes.is_empty() { return vec![]; } + + // remove ending 00 + let mut bytes = bytes.clone(); + while *bytes.last().unwrap() == 0 { + bytes.pop(); + } + let mut i = 0; let last_op = *bytes.last().unwrap(); let has_cbor = last_op != JUMP && @@ -57,6 +64,7 @@ pub fn all_bytecode(bytes: &Vec) -> Vec<(usize, u8)> { } else { 0 }; + // println!("cbor_len: {} lastop: {}", cbor_len, last_op); let mut res = Vec::new(); @@ -68,6 +76,7 @@ pub fn all_bytecode(bytes: &Vec) -> Vec<(usize, u8)> { i += op as usize - 0x5f; } } + // println!("res: {:?}", res.len()); res } diff --git a/src/evm/srcmap/mod.rs b/src/evm/srcmap/mod.rs index 8a1ff44de..295cd61ee 100644 --- a/src/evm/srcmap/mod.rs +++ b/src/evm/srcmap/mod.rs @@ -124,10 +124,8 @@ impl SourceMapProvider { } } } - None => unreachable!("{}", format!("pc {} is invalid @ {}", pc, address)), /* No source code for pc. - * Should not happen if pc - * is - * valid */ + None => SourceCodeResult::NoSourceCode, /* No source code for pc. + * usually occurs in the unavailable opcode before metadata */ } } else { SourceCodeResult::NoSourceMap // No source map From d2ed838a2eaccaf7798af409870f3b55568662be Mon Sep 17 00:00:00 2001 From: 0xAWM <29773064+0xAWM@users.noreply.github.com> Date: Fri, 24 Nov 2023 17:43:47 +0800 Subject: [PATCH 02/23] Update bytecode_iterator.rs --- src/evm/bytecode_iterator.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/evm/bytecode_iterator.rs b/src/evm/bytecode_iterator.rs index e9acf18bf..ae894848b 100644 --- a/src/evm/bytecode_iterator.rs +++ b/src/evm/bytecode_iterator.rs @@ -42,12 +42,12 @@ pub fn all_bytecode(bytes: &Vec) -> Vec<(usize, u8)> { // remove ending 00 let mut bytes = bytes.clone(); - while *bytes.last().unwrap() == 0 { + while *bytes.last().unwrap_or(1) == 0 { bytes.pop(); } let mut i = 0; - let last_op = *bytes.last().unwrap(); + let last_op = *bytes.last().unwrap_or_default(); let has_cbor = last_op != JUMP && last_op != JUMPI && last_op != STOP && From ef181dd1a8a47fe5df41fcd49786bcef959e10df Mon Sep 17 00:00:00 2001 From: 0xAWM <29773064+0xAWM@users.noreply.github.com> Date: Fri, 24 Nov 2023 17:45:27 +0800 Subject: [PATCH 03/23] Update bytecode_iterator.rs --- src/evm/bytecode_iterator.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/evm/bytecode_iterator.rs b/src/evm/bytecode_iterator.rs index ae894848b..0d289a6c5 100644 --- a/src/evm/bytecode_iterator.rs +++ b/src/evm/bytecode_iterator.rs @@ -47,7 +47,7 @@ pub fn all_bytecode(bytes: &Vec) -> Vec<(usize, u8)> { } let mut i = 0; - let last_op = *bytes.last().unwrap_or_default(); + let last_op = *bytes.last().unwrap_or(RETURN); let has_cbor = last_op != JUMP && last_op != JUMPI && last_op != STOP && From 0f8b2651aca258427bb8ca9390197d4a2aad2b9b Mon Sep 17 00:00:00 2001 From: "0xAWM.eth" <29773064+0xAWM@users.noreply.github.com> Date: Fri, 24 Nov 2023 18:05:09 +0800 Subject: [PATCH 04/23] Fix unwrap bug --- src/evm/bytecode_iterator.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/evm/bytecode_iterator.rs b/src/evm/bytecode_iterator.rs index 0d289a6c5..209422e8e 100644 --- a/src/evm/bytecode_iterator.rs +++ b/src/evm/bytecode_iterator.rs @@ -42,12 +42,12 @@ pub fn all_bytecode(bytes: &Vec) -> Vec<(usize, u8)> { // remove ending 00 let mut bytes = bytes.clone(); - while *bytes.last().unwrap_or(1) == 0 { + while bytes.last().unwrap_or(&1) == &0 { bytes.pop(); } let mut i = 0; - let last_op = *bytes.last().unwrap_or(RETURN); + let last_op = *bytes.last().unwrap_or(&RETURN); let has_cbor = last_op != JUMP && last_op != JUMPI && last_op != STOP && From b58e80cc488536ea081e195071803a6790a1d831 Mon Sep 17 00:00:00 2001 From: "0xAWM.eth" <29773064+0xAWM@users.noreply.github.com> Date: Sun, 26 Nov 2023 09:14:55 +0800 Subject: [PATCH 05/23] merge --- Cargo.toml | 8 +------- integration_test.py | 7 +++++-- onchain_tests.txt | 4 +--- src/evm/blaz/builder.rs | 1 + src/evm/host.rs | 8 ++++---- src/evm/middlewares/coverage.rs | 7 ++++--- src/evm/middlewares/mod.rs | 1 + src/evm/mod.rs | 34 +++++++++++++++++++++++++++++++++ src/evm/onchain/mod.rs | 11 +++++++++++ src/evm/oracles/mod.rs | 3 ++- src/evm/vm.rs | 8 ++++---- src/fuzzers/evm_fuzzer.rs | 14 ++++++++++++++ 12 files changed, 82 insertions(+), 24 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 90489e427..9eedcd2b1 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -8,13 +8,7 @@ edition = "2021" criterion = "0.4.0" [features] -default = [ - "cmp", - "dataflow", - "evm", - "print_txn_corpus", - "full_trace", -] +default = ["cmp", "dataflow", "evm", "print_txn_corpus", "full_trace"] evm = [] cmp = [] dataflow = [] diff --git a/integration_test.py b/integration_test.py index 9e2bb512c..4a7bed699 100644 --- a/integration_test.py +++ b/integration_test.py @@ -72,7 +72,6 @@ def test_one(path): if "taint" in path: cmd.append("--sha3-bypass") - print(" ".join(cmd)) p = subprocess.run( @@ -145,6 +144,8 @@ def test_onchain(test): etherscan_key, "--work-dir", f"w_{name}", + "--onchain-builder", + "https://solc-builder.dev.infra.fuzz.land/" # "--run-forever" ] @@ -152,6 +153,8 @@ def test_onchain(test): # try 3 times in case of rpc failure for i in range(3): + print(f"=== Testing onchain for contracts: {name}, try {i}") + print(" ".join(cmd)) p = subprocess.run( " ".join(cmd), stdout=subprocess.PIPE, @@ -235,7 +238,7 @@ def build_flash_loan_v2_fuzzer(): if "onchain" in actions: build_flash_loan_v2_fuzzer() tests = read_onchain_tests() - with multiprocessing.Pool(10) as p: + with multiprocessing.Pool(1) as p: p.map(test_onchain, tests) if crashed_any: diff --git a/onchain_tests.txt b/onchain_tests.txt index 130c269bc..3dc1effe6 100644 --- a/onchain_tests.txt +++ b/onchain_tests.txt @@ -1,4 +1,3 @@ -VerilogCTF polygon 35718198 0xbcf6e9d27bf95f3f5eddb93c38656d684317d5b4,0x5d6c48f05ad0fde3f64bab50628637d73b1eb0bb EAC_exp bsc 31273018 0x55d398326f99059fF775485246999027B3197955,0x64f291DE10eCd36D5f7b64aaEbC70943CFACE28E,0x20dcf125f0563417d257b98a116c3fea4f0b2db2,0xa08a40e0F11090Dcb09967973DF82040bFf63561 ApeDAO_exp bsc 30072293 0x81917eb96b397dFb1C6000d28A5bc08c0f05fC1d,0x55d398326f99059fF775485246999027B3197955,0x45aa258ad08eeeb841c1c02eca7658f9dd4779c0,0xb47955b5b7eaf49c815ebc389850eb576c460092,0xee2a9D05B943C1F33f3920C750Ac88F74D0220c3,0xB47955B5B7EAF49C815EBc389850eb576C460092 ROI_exp bsc 21143795 0xe9e7CEA3DedcA5984780Bafc599bD69ADd087D56,0xe48b75dc1b131fd3a8364b0580f76efd04cf6e9c,0xbb4CdB9CBd36B01bD1cBaEBF2De08d9173bc095c,0x745D6Dd206906dd32b3f35E00533AD0963805124,0x216FC1D66677c9A778C60E6825189508b9619908,0xE48b75dc1b131fd3A8364b0580f76eFD04cF6e9c,0x158af3d23d96e3104bcc65b76d1a6f53d0f74ed0 @@ -19,10 +18,9 @@ THB_exp bsc 21785004 0x72e901F1bb2BfA2339326DfB90c5cEc911e2ba3C,0xae191Ca19F0f8E Novo_exp bsc 18225002 0xEeBc161437FA948AAb99383142564160c92D2974,0xa0787daad6062349f63b7c228cbfd5d8a3db08f1,0x3463a663de4ccc59c8b21190f81027096f18cf2a,0x6Fb2020C236BBD5a7DDEb07E14c9298642253333,0xbb4CdB9CBd36B01bD1cBaEBF2De08d9173bc095c,0x128cd0Ae1a0aE7e67419111714155E1B1c6B2D8D PLTD_exp bsc 22252045 0x55d398326f99059fF775485246999027B3197955,0xD7B7218D778338Ea05f5Ecce82f86D365E25dBCE,0x4397C76088db8f16C15455eB943Dd11F2DF56545,0x29b2525e11BC0B0E9E59f705F318601eA6756645 DYNA_exp bsc 25879486 0xa7B5eabC3Ee82c585f5F4ccC26b81c3Bd62Ff3a9,0xbb4CdB9CBd36B01bD1cBaEBF2De08d9173bc095c,0x5c0d0111ffc638802c9EfCcF55934D5C63aB3f79,0xb6148c6fA6Ebdd6e22eF5150c5C3ceE78b24a3a0 -OLIFE_exp bsc 27470678 0x915C2DFc34e773DC3415Fe7045bB1540F8BDAE84,0xbb4CdB9CBd36B01bD1cBaEBF2De08d9173bc095c,0xb5a0Ce3Acd6eC557d39aFDcbC93B07a1e1a9e3fa AUR_exp bsc 23282134 0x73A1163EA930A0a67dFEFB9C3713Ef0923755B78,0xbb4CdB9CBd36B01bD1cBaEBF2De08d9173bc095c,0x70678291bDDfd95498d1214BE368e19e882f7614 SellToken_exp bsc 28168034 0xbb4CdB9CBd36B01bD1cBaEBF2De08d9173bc095c,0xa645995e9801F2ca6e2361eDF4c2A138362BADe4,0x57Db19127617B77c8abd9420b5a35502b59870D6 Shadowfi_exp bsc 20969095 0xbb4CdB9CBd36B01bD1cBaEBF2De08d9173bc095c,0x10bc28d2810dD462E16facfF18f78783e859351b,0xF9e3151e813cd6729D52d9A0C3ee69F22CcE650A RFB_exp bsc 23649423 0x26f1457f067bF26881F311833391b52cA871a4b5,0x03184AAA6Ad4F7BE876423D9967d1467220a544e,0xbb4CdB9CBd36B01bD1cBaEBF2De08d9173bc095c,0x0fe261aeE0d1C4DFdDee4102E82Dd425999065F4 BIGFI_exp bsc 26685503 0x55d398326f99059fF775485246999027B3197955,0x28ec0B36F0819ecB5005cAB836F4ED5a2eCa4D13,0xd3d4B46Db01C006Fb165879f343fc13174a1cEeB,0xA269556EdC45581F355742e46D2d722c5F3f551a -Axioma_exp bsc 27620320 0x2C25aEe99ED08A61e7407A5674BC2d1A72B5D8E3,0xB6CF5b77B92a722bF34f6f5D6B1Fe4700908935E,0x6a3Fa7D2C71fd7D44BF3a2890aA257F34083c90f +Axioma_exp bsc 27620320 0x2C25aEe99ED08A61e7407A5674BC2d1A72B5D8E3,0xB6CF5b77B92a722bF34f6f5D6B1Fe4700908935E,0x6a3Fa7D2C71fd7D44BF3a2890aA257F34083c90f \ No newline at end of file diff --git a/src/evm/blaz/builder.rs b/src/evm/blaz/builder.rs index 8046d9155..dfc85715b 100644 --- a/src/evm/blaz/builder.rs +++ b/src/evm/blaz/builder.rs @@ -80,6 +80,7 @@ impl BuildJob { } pub fn onchain_job(&self, chain: String, addr: EVMAddress) -> Option { + debug!("will get onchain_job: {:?}", addr); if let Some(replacement) = self.replacements.get(&addr) { return replacement.clone(); } diff --git a/src/evm/host.rs b/src/evm/host.rs index 90aba6585..6ccf3c1cf 100644 --- a/src/evm/host.rs +++ b/src/evm/host.rs @@ -201,8 +201,8 @@ where pub current_self_destructs: Vec<(EVMAddress, usize)>, // arbitrary calls pub current_arbitrary_calls: Vec<(EVMAddress, EVMAddress, usize)>, - // integer_overflow - pub current_integer_overflow: HashSet<(EVMAddress, usize, &'static str)>, + // integer_overflow / precision_loss + pub current_math_error: HashSet<(EVMAddress, usize, &'static str)>, // relations file handle relations_file: std::fs::File, // Filter duplicate relations @@ -289,7 +289,7 @@ where setcode_data: self.setcode_data.clone(), current_self_destructs: self.current_self_destructs.clone(), current_arbitrary_calls: self.current_arbitrary_calls.clone(), - current_integer_overflow: self.current_integer_overflow.clone(), + current_math_error: self.current_math_error.clone(), relations_file: self.relations_file.try_clone().unwrap(), relations_hash: self.relations_hash.clone(), current_typed_bug: self.current_typed_bug.clone(), @@ -349,7 +349,7 @@ where setcode_data: HashMap::new(), current_self_destructs: Default::default(), current_arbitrary_calls: Default::default(), - current_integer_overflow: Default::default(), + current_math_error: Default::default(), relations_file: std::fs::File::create(format!("{}/relations.log", workdir)).unwrap(), relations_hash: HashSet::new(), current_typed_bug: Default::default(), diff --git a/src/evm/middlewares/coverage.rs b/src/evm/middlewares/coverage.rs index 83e2a702f..32489550d 100644 --- a/src/evm/middlewares/coverage.rs +++ b/src/evm/middlewares/coverage.rs @@ -8,6 +8,7 @@ use std::{ time::{SystemTime, UNIX_EPOCH}, }; +use bytes::Bytes; use itertools::Itertools; use libafl::{schedulers::Scheduler, state::HasMetadata}; use revm_interpreter::{ @@ -33,11 +34,11 @@ pub static mut EVAL_COVERAGE: bool = false; /// Finds all PCs (offsets of bytecode) that are instructions / JUMPDEST /// Returns a tuple of (instruction PCs, JUMPI PCs, Skip PCs) -pub fn instructions_pc(bytecode: &Bytecode) -> (HashSet, HashSet, HashSet) { +pub fn instructions_pc(bytecode: &Bytes) -> (HashSet, HashSet, HashSet) { let mut complete_bytes = vec![]; let mut skip_instructions = HashSet::new(); let mut total_jumpi_set = HashSet::new(); - all_bytecode(&bytecode.bytes().to_vec()).iter().for_each(|(pc, op)| { + all_bytecode(&bytecode.to_vec()).iter().for_each(|(pc, op)| { if *op == JUMPDEST || *op == STOP || *op == INVALID { skip_instructions.insert(*pc); } @@ -346,7 +347,7 @@ where self.sources.insert(address, build_artifact.sources.clone()); } - let (pcs, jumpis, mut skip_pcs) = instructions_pc(&bytecode.clone()); + let (pcs, jumpis, mut skip_pcs) = instructions_pc(&bytecode.bytecode); // find all skipping PCs pcs.iter().for_each( diff --git a/src/evm/middlewares/mod.rs b/src/evm/middlewares/mod.rs index 49b1f16c9..ab3f80793 100644 --- a/src/evm/middlewares/mod.rs +++ b/src/evm/middlewares/mod.rs @@ -1,6 +1,7 @@ pub mod call_printer; pub mod cheatcode; pub mod coverage; +pub mod math_calculate; pub mod middleware; pub mod reentrancy; pub mod sha3_bypass; diff --git a/src/evm/mod.rs b/src/evm/mod.rs index fd7201f32..ce2e739e4 100644 --- a/src/evm/mod.rs +++ b/src/evm/mod.rs @@ -177,6 +177,40 @@ pub struct EvmArgs { /// (Default: high_confidence) #[arg(long, short, default_value = "high_confidence")] detectors: String, // <- internally this is known as oracles + /// Detect selfdestruct (Default: true) + #[arg(long, default_value = "true")] + selfdestruct_oracle: bool, + + /// Detect pontential reentrancy vulnerability (Default: false) + #[arg(long, default_value = "false")] + reentrancy_oracle: bool, + + #[arg(long, default_value = "true")] + arbitrary_external_call_oracle: bool, + + #[arg(long, default_value = "true")] + math_calculate_oracle: bool, + + #[arg(long, default_value = "true")] + echidna_oracle: bool, + + #[arg(long, default_value = "true")] + invariant_oracle: bool, + + ///Enable oracle for detecting whether bug() / typed_bug() is called + #[arg(long, default_value = "true")] + typed_bug_oracle: bool, + + /// Setting any string here will enable state comparison oracle. + /// This arg holds file path pointing to state comparison oracle's desired + /// state + #[arg(long, default_value = "")] + state_comp_oracle: String, + + /// Matching style for state comparison oracle (Select from "Exact", + /// "DesiredContain", "StateContain") + #[arg(long, default_value = "Exact")] + state_comp_matching: String, // /// Matching style for state comparison oracle (Select from "Exact", // /// "DesiredContain", "StateContain") diff --git a/src/evm/onchain/mod.rs b/src/evm/onchain/mod.rs index c756de996..0b3c05afd 100644 --- a/src/evm/onchain/mod.rs +++ b/src/evm/onchain/mod.rs @@ -19,6 +19,7 @@ use libafl::{prelude::HasMetadata, schedulers::Scheduler}; use revm_interpreter::{analysis::to_analysed, Interpreter}; use revm_primitives::Bytecode; use tracing::debug; +use tracing_subscriber::field::debug; use super::{corpus_initializer::EnvMetadata, types::EVMFuzzState}; use crate::{ @@ -45,6 +46,7 @@ use crate::{ pub static mut BLACKLIST_ADDR: Option> = None; pub static mut WHITELIST_ADDR: Option> = None; +#[cfg(feature = "force_cache")] const UNBOUND_THRESHOLD: usize = 30; pub struct OnChain { @@ -316,11 +318,19 @@ impl OnChain { bytecode_analyzer::add_analysis_result_to_state(&contract_code, state); host.set_codedata(address_h160, contract_code.clone()); } + debug!("load code for {:?}", address_h160); if unsafe { IS_FAST_CALL } || self.blacklist.contains(&address_h160) || !should_setup_abi { + debug!( + "return due to {} or {} or {}", + unsafe { IS_FAST_CALL }, + self.blacklist.contains(&address_h160), + !should_setup_abi + ); return; } // setup abi + debug!("setup abi for {:?}", address_h160); self.loaded_abi.insert(address_h160); let mut parsed_abi = vec![]; @@ -328,6 +338,7 @@ impl OnChain { parsed_abi = abis.clone(); } else { let mut abi = None; + debug!("try use abi from builder"); if let Some(builder) = &self.builder { debug!("onchain job {:?}", address_h160); let build_job = builder.onchain_job(self.endpoint.chain_name.clone(), address_h160); diff --git a/src/evm/oracles/mod.rs b/src/evm/oracles/mod.rs index 036f5c522..674da0257 100644 --- a/src/evm/oracles/mod.rs +++ b/src/evm/oracles/mod.rs @@ -5,6 +5,7 @@ pub mod echidna; pub mod erc20; pub mod function; pub mod invariant; +pub mod math_calculate; pub mod reentrancy; pub mod selfdestruct; pub mod state_comp; @@ -21,7 +22,7 @@ pub static STATE_COMP_BUG_IDX: u64 = 7; pub static ARB_CALL_BUG_IDX: u64 = 8; pub static REENTRANCY_BUG_IDX: u64 = 9; pub static INVARIANT_BUG_IDX: u64 = 10; -pub static INTEGER_OVERFLOW_BUG_IDX: u64 = 11; +pub static MATH_CALCULATE_BUG_IDX: u64 = 11; /// Divide a U512 by another U512 and return a string with the decimal point at /// the correct position For example, 1000 / 3 = 333.333, then a = 1000e6, b = diff --git a/src/evm/vm.rs b/src/evm/vm.rs index 19656dd0b..91a13734a 100644 --- a/src/evm/vm.rs +++ b/src/evm/vm.rs @@ -261,7 +261,7 @@ pub struct EVMState { pub arbitrary_calls: HashSet<(EVMAddress, EVMAddress, usize)>, // integer overflow in sol #[serde(skip)] - pub integer_overflow: HashSet<(EVMAddress, usize, &'static str)>, + pub math_error: HashSet<(EVMAddress, usize, &'static str)>, #[serde(skip)] pub reentrancy_metadata: ReentrancyData, } @@ -821,12 +821,12 @@ where .chain(self.host.current_arbitrary_calls.iter().cloned()), ); - r.new_state.integer_overflow = HashSet::from_iter( + r.new_state.math_error = HashSet::from_iter( vm_state - .integer_overflow + .math_error .iter() .cloned() - .chain(self.host.current_integer_overflow.iter().cloned()), + .chain(self.host.current_math_error.iter().cloned()), ); unsafe { diff --git a/src/fuzzers/evm_fuzzer.rs b/src/fuzzers/evm_fuzzer.rs index c34e49b92..7fd4037b9 100644 --- a/src/fuzzers/evm_fuzzer.rs +++ b/src/fuzzers/evm_fuzzer.rs @@ -42,6 +42,7 @@ use crate::{ call_printer::CallPrinter, cheatcode::Cheatcode, coverage::{Coverage, EVAL_COVERAGE}, + math_calculate::MathCalculateMiddleware, middleware::Middleware, reentrancy::ReentrancyTracer, sha3_bypass::{Sha3Bypass, Sha3TaintAnalysis}, @@ -53,6 +54,7 @@ use crate::{ arb_call::ArbitraryCallOracle, echidna::EchidnaOracle, invariant::InvariantOracle, + math_calculate::MathCalculateOracle, reentrancy::ReentrancyOracle, selfdestruct::SelfdestructOracle, typed_bug::TypedBugOracle, @@ -179,6 +181,12 @@ pub fn evm_fuzzer( fuzz_host.add_middlewares(Rc::new(RefCell::new(ReentrancyTracer::new()))); } + if config.math_calculate_oracle { + debug!("math_calculate oracle enabled"); + let integer_overflow_middleware = Rc::new(RefCell::new(MathCalculateMiddleware::new(config.onchain, true))); + fuzz_host.add_middlewares(integer_overflow_middleware); + } + let mut evm_executor: EVMQueueExecutor = EVMExecutor::new(fuzz_host, deployer); if config.replay_file.is_some() { @@ -417,6 +425,12 @@ pub fn evm_fuzzer( )))); } + if config.math_calculate_oracle { + oracles.push(Rc::new(RefCell::new(MathCalculateOracle::new( + artifacts.address_to_name.clone(), + )))); + } + if let Some(m) = onchain_middleware.clone() { m.borrow_mut().add_abi(artifacts.address_to_abi.clone()); } From e28982adf0d98fe846625aaee876516201a34538 Mon Sep 17 00:00:00 2001 From: "0xAWM.eth" <29773064+0xAWM@users.noreply.github.com> Date: Sun, 26 Nov 2023 09:15:37 +0800 Subject: [PATCH 06/23] Feat: math oracle --- .gitignore | 1 + src/evm/middlewares/math_calculate.rs | 137 ++++++++++++++++++++++++++ src/evm/oracles/math_calculate.rs | 94 ++++++++++++++++++ 3 files changed, 232 insertions(+) create mode 100644 src/evm/middlewares/math_calculate.rs create mode 100644 src/evm/oracles/math_calculate.rs diff --git a/.gitignore b/.gitignore index 71ec3d4cd..92fd3dae1 100644 --- a/.gitignore +++ b/.gitignore @@ -6,6 +6,7 @@ bin corpus .idea .vscode +.DS_Store node_modules cache *.bin diff --git a/src/evm/middlewares/math_calculate.rs b/src/evm/middlewares/math_calculate.rs new file mode 100644 index 000000000..ebd8d8346 --- /dev/null +++ b/src/evm/middlewares/math_calculate.rs @@ -0,0 +1,137 @@ +use std::{collections::HashSet, fmt::Debug, str::FromStr}; + +use libafl::schedulers::Scheduler; +use revm_interpreter::Interpreter; +use revm_primitives::{keccak256, B256}; +use serde::Serialize; +use tracing::{debug, info}; + +use crate::evm::{ + host::FuzzHost, + middlewares::middleware::{Middleware, MiddlewareType}, + onchain::endpoints::{Chain, OnChainConfig}, + srcmap::{SourceCodeResult, SOURCE_MAP_PROVIDER}, + types::{EVMAddress, EVMFuzzState}, + uniswap::{get_uniswap_info, UniswapProvider}, +}; + +#[derive(Serialize, Debug, Clone, Default)] +pub struct MathCalculateMiddleware { + report_when_no_srcmap: bool, + whitelist: HashSet, + pub fp: HashSet<(EVMAddress, usize)>, + pair_hash: B256, +} + +impl MathCalculateMiddleware { + pub fn new(onchain: Option, report_when_no_srcmap: bool) -> Self { + if let Some(OnChainConfig { chain_name, .. }) = onchain { + let chain = &Chain::from_str(&chain_name).unwrap(); + let info = get_uniswap_info(&UniswapProvider::UniswapV2, chain); + let whitelist = HashSet::from([info.router]); + let pair_hash = keccak256(&info.pair_bytecode); + println!("pair_hash: {:?}", pair_hash); + return Self { + report_when_no_srcmap, + whitelist, + fp: HashSet::new(), + pair_hash, + }; + } + Self::default() + } +} + +impl Middleware for MathCalculateMiddleware +where + SC: Scheduler + Clone, +{ + unsafe fn on_step(&mut self, interp: &mut Interpreter, host: &mut FuzzHost, _state: &mut EVMFuzzState) { + let addr = interp.contract.code_address; + let pc = interp.program_counter(); + macro_rules! check { + ($overflow_fn: ident, $op: expr) => { + if self.whitelist.contains(&interp.contract.code_address) { + return; + } + let (l, r) = (interp.stack.peek(0).unwrap(), interp.stack.peek(1).unwrap()); + let overflow = if $op == "/" { l < r } else { l.$overflow_fn(r).1 }; + if !overflow || + // already in fp + self.fp.contains(&(addr, pc)) || + // already reported + host.current_math_error.contains(&(addr, pc, $op)) + { + return; + } + let bytecode = host.code.get(&addr).unwrap(); + if bytecode.hash() == self.pair_hash { + // add whitelist for uniswap pair + debug!("add overflow whitelist for uniswap pair: {:?}", addr); + self.whitelist.insert(addr); + return; + } + match SOURCE_MAP_PROVIDER.lock().unwrap().get_source_code(&addr, pc) { + SourceCodeResult::NoSourceMap => { + // no srcmap + if self.report_when_no_srcmap { + info!("contract {:?} maybe math error on pc[{pc:x}]: {} {} {}", addr, l, $op, r); + host.current_math_error.insert((addr, pc, $op)); + } else { + self.fp.insert((addr, pc)); + } + } + SourceCodeResult::NoSourceCode | SourceCodeResult::SourceCodeNoPcMatch(_) => { + // fp because of solc generated code + self.fp.insert((addr, pc)); + } + SourceCodeResult::SourceCode(source_code) => { + if source_code.contains($op) { + // real bug + info!("contract {:?} math error on pc[{pc:x}]: {} {} {} {source_code:?}", addr, l, $op, r); + host.current_math_error.insert((addr, pc, $op)); + } else { + // fp + self.fp.insert((addr, pc)); + } + } + } + }; + } + match *interp.instruction_pointer { + 0x01 => { + // +ADD + check!(overflowing_add, "+"); + } + 0x02 => { + // *MUL + check!(overflowing_mul, "*"); + } + 0x03 => { + // -SUB + check!(overflowing_sub, "-"); + } + 0x04 | 0x05 => { + // DIV/ SDIV + // overflowing_add for placeholder, not used + check!(overflowing_add, "/"); + } + 0x0a => { + // ** EXP + check!(overflowing_pow, "**"); + } + _ => {} + } + } + + fn get_type(&self) -> MiddlewareType { + MiddlewareType::IntegerOverflow + } +} + +#[cfg(test)] +mod test { + + #[test] + fn test_merge() {} +} diff --git a/src/evm/oracles/math_calculate.rs b/src/evm/oracles/math_calculate.rs new file mode 100644 index 000000000..ee0e6960f --- /dev/null +++ b/src/evm/oracles/math_calculate.rs @@ -0,0 +1,94 @@ +use std::{ + collections::{hash_map::DefaultHasher, HashMap}, + hash::{Hash, Hasher}, +}; + +use bytes::Bytes; +use itertools::Itertools; +use revm_primitives::Bytecode; + +use crate::{ + evm::{ + input::{ConciseEVMInput, EVMInput}, + oracle::EVMBugResult, + oracles::MATH_CALCULATE_BUG_IDX, + types::{EVMAddress, EVMFuzzState, EVMOracleCtx, EVMU256}, + vm::EVMState, + }, + oracle::{Oracle, OracleCtx}, + state::HasExecutionResult, +}; + +pub struct MathCalculateOracle { + pub address_to_name: HashMap, +} + +impl MathCalculateOracle { + pub fn new(address_to_name: HashMap) -> Self { + Self { address_to_name } + } +} + +impl + Oracle, EVMInput, EVMFuzzState, ConciseEVMInput> + for MathCalculateOracle +{ + fn transition(&self, _ctx: &mut EVMOracleCtx<'_>, _stage: u64) -> u64 { + 0 + } + + fn oracle( + &self, + ctx: &mut OracleCtx< + EVMState, + EVMAddress, + Bytecode, + Bytes, + EVMAddress, + EVMU256, + Vec, + EVMInput, + EVMFuzzState, + ConciseEVMInput, + >, + _stage: u64, + ) -> Vec { + let mut bug_indexes = Vec::new(); + for (addr, pc, op) in ctx.post_state.math_error.clone().into_iter() { + let mut hasher = DefaultHasher::new(); + addr.hash(&mut hasher); + pc.hash(&mut hasher); + let real_bug_idx = hasher.finish() << (8 + MATH_CALCULATE_BUG_IDX); + let name = self + .address_to_name + .get(&addr) + .unwrap_or(&format!("{:?}", addr)) + .clone(); + if op == "/" { + EVMBugResult::new( + "Loss of Accuracy".to_string(), + real_bug_idx, + format!("PrecisionLoss: {addr:?} , PC: {pc:x}, OP: {op:?}"), + ConciseEVMInput::from_input(ctx.input, ctx.fuzz_state.get_execution_result()), + None, + Some(name.clone()), + ) + .push_to_output(); + bug_indexes.push(real_bug_idx); + continue; + } + + EVMBugResult::new( + "IntegerOverflow".to_string(), + real_bug_idx, + format!("IntegerOverflow: {addr:?} , PC: {pc:x}, OP: {op:?} "), + ConciseEVMInput::from_input(ctx.input, ctx.fuzz_state.get_execution_result()), + None, + Some(name.clone()), + ) + .push_to_output(); + bug_indexes.push(real_bug_idx); + } + bug_indexes.into_iter().unique().filter(|x| *x != 0).collect_vec() + } +} From 1306a2f319bd61c4355ed137e0c53cbcbf504652 Mon Sep 17 00:00:00 2001 From: "0xAWM.eth" <29773064+0xAWM@users.noreply.github.com> Date: Sun, 26 Nov 2023 09:22:41 +0800 Subject: [PATCH 07/23] config --- integration_test.py | 2 +- onchain_tests.txt | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/integration_test.py b/integration_test.py index 4a7bed699..596045810 100644 --- a/integration_test.py +++ b/integration_test.py @@ -238,7 +238,7 @@ def build_flash_loan_v2_fuzzer(): if "onchain" in actions: build_flash_loan_v2_fuzzer() tests = read_onchain_tests() - with multiprocessing.Pool(1) as p: + with multiprocessing.Pool(10) as p: p.map(test_onchain, tests) if crashed_any: diff --git a/onchain_tests.txt b/onchain_tests.txt index 3dc1effe6..014882bcd 100644 --- a/onchain_tests.txt +++ b/onchain_tests.txt @@ -1,3 +1,4 @@ +VerilogCTF polygon 35718198 0xbcf6e9d27bf95f3f5eddb93c38656d684317d5b4,0x5d6c48f05ad0fde3f64bab50628637d73b1eb0bb EAC_exp bsc 31273018 0x55d398326f99059fF775485246999027B3197955,0x64f291DE10eCd36D5f7b64aaEbC70943CFACE28E,0x20dcf125f0563417d257b98a116c3fea4f0b2db2,0xa08a40e0F11090Dcb09967973DF82040bFf63561 ApeDAO_exp bsc 30072293 0x81917eb96b397dFb1C6000d28A5bc08c0f05fC1d,0x55d398326f99059fF775485246999027B3197955,0x45aa258ad08eeeb841c1c02eca7658f9dd4779c0,0xb47955b5b7eaf49c815ebc389850eb576c460092,0xee2a9D05B943C1F33f3920C750Ac88F74D0220c3,0xB47955B5B7EAF49C815EBc389850eb576C460092 ROI_exp bsc 21143795 0xe9e7CEA3DedcA5984780Bafc599bD69ADd087D56,0xe48b75dc1b131fd3a8364b0580f76efd04cf6e9c,0xbb4CdB9CBd36B01bD1cBaEBF2De08d9173bc095c,0x745D6Dd206906dd32b3f35E00533AD0963805124,0x216FC1D66677c9A778C60E6825189508b9619908,0xE48b75dc1b131fd3A8364b0580f76eFD04cF6e9c,0x158af3d23d96e3104bcc65b76d1a6f53d0f74ed0 From 28ba2d19144c6a8ab0d95226dd3f5be74ae3a0d8 Mon Sep 17 00:00:00 2001 From: "0xAWM.eth" <29773064+0xAWM@users.noreply.github.com> Date: Sun, 26 Nov 2023 10:07:53 +0800 Subject: [PATCH 08/23] fix test --- src/evm/middlewares/coverage.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/evm/middlewares/coverage.rs b/src/evm/middlewares/coverage.rs index 32489550d..bbcbb3c16 100644 --- a/src/evm/middlewares/coverage.rs +++ b/src/evm/middlewares/coverage.rs @@ -389,7 +389,7 @@ mod tests { Bytes::from( hex::decode("60806040526004361061004e5760003560e01c80632d2c55651461008d578063819d4cc6146100de5780638980f11f146101005780638b21f170146101205780639342c8f41461015457600080fd5b36610088576040513481527f27f12abfe35860a9a927b465bb3d4a9c23c8428174b83f278fe45ed7b4da26629060200160405180910390a1005b600080fd5b34801561009957600080fd5b506100c17f0000000000000000000000003e40d73eb977dc6a537af587d48316fee66e9c8c81565b6040516001600160a01b0390911681526020015b60405180910390f35b3480156100ea57600080fd5b506100fe6100f93660046106bb565b610182565b005b34801561010c57600080fd5b506100fe61011b3660046106bb565b61024e565b34801561012c57600080fd5b506100c17f000000000000000000000000ae7ab96520de3a18e5e111b5eaab095312d7fe8481565b34801561016057600080fd5b5061017461016f3660046106f3565b610312565b6040519081526020016100d5565b6040518181526001600160a01b0383169033907f6a30e6784464f0d1f4158aa4cb65ae9239b0fa87c7f2c083ee6dde44ba97b5e69060200160405180910390a36040516323b872dd60e01b81523060048201526001600160a01b037f0000000000000000000000003e40d73eb977dc6a537af587d48316fee66e9c8c81166024830152604482018390528316906323b872dd90606401600060405180830381600087803b15801561023257600080fd5b505af1158015610246573d6000803e3d6000fd5b505050505050565b6000811161029a5760405162461bcd60e51b815260206004820152601460248201527316915493d7d49150d3d591549657d05353d5539560621b60448201526064015b60405180910390fd5b6040518181526001600160a01b0383169033907faca8fb252cde442184e5f10e0f2e6e4029e8cd7717cae63559079610702436aa9060200160405180910390a361030e6001600160a01b0383167f0000000000000000000000003e40d73eb977dc6a537af587d48316fee66e9c8c83610418565b5050565b6000336001600160a01b037f000000000000000000000000ae7ab96520de3a18e5e111b5eaab095312d7fe8416146103855760405162461bcd60e51b81526020600482015260166024820152754f4e4c595f4c49444f5f43414e5f574954484452415760501b6044820152606401610291565b478281116103935780610395565b825b91508115610412577f000000000000000000000000ae7ab96520de3a18e5e111b5eaab095312d7fe846001600160a01b0316634ad509b2836040518263ffffffff1660e01b81526004016000604051808303818588803b1580156103f857600080fd5b505af115801561040c573d6000803e3d6000fd5b50505050505b50919050565b604080516001600160a01b038416602482015260448082018490528251808303909101815260649091019091526020810180516001600160e01b031663a9059cbb60e01b17905261046a90849061046f565b505050565b60006104c4826040518060400160405280602081526020017f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c6564815250856001600160a01b03166105419092919063ffffffff16565b80519091501561046a57808060200190518101906104e2919061070c565b61046a5760405162461bcd60e51b815260206004820152602a60248201527f5361666545524332303a204552433230206f7065726174696f6e20646964206e6044820152691bdd081cdd58d8d9595960b21b6064820152608401610291565b6060610550848460008561055a565b90505b9392505050565b6060824710156105bb5760405162461bcd60e51b815260206004820152602660248201527f416464726573733a20696e73756666696369656e742062616c616e636520666f6044820152651c8818d85b1b60d21b6064820152608401610291565b843b6106095760405162461bcd60e51b815260206004820152601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e74726163740000006044820152606401610291565b600080866001600160a01b03168587604051610625919061075e565b60006040518083038185875af1925050503d8060008114610662576040519150601f19603f3d011682016040523d82523d6000602084013e610667565b606091505b5091509150610677828286610682565b979650505050505050565b60608315610691575081610553565b8251156106a15782518084602001fd5b8160405162461bcd60e51b8152600401610291919061077a565b600080604083850312156106ce57600080fd5b82356001600160a01b03811681146106e557600080fd5b946020939093013593505050565b60006020828403121561070557600080fd5b5035919050565b60006020828403121561071e57600080fd5b8151801515811461055357600080fd5b60005b83811015610749578181015183820152602001610731565b83811115610758576000848401525b50505050565b6000825161077081846020870161072e565b9190910192915050565b602081526000825180602084015261079981604085016020870161072e565b601f01601f1916919091016040019291505056fea2646970667358221220c0f03149dd58fa21e9bfb72a010b74b1e518d704a2d63d8cc44c0ad3a2f573da64736f6c63430008090033").unwrap() ) - )); + ).bytecode); assert_eq!(pcs.len(), 1107); } From 1a1bca0784480d99a30de0cba8a500327208c467 Mon Sep 17 00:00:00 2001 From: "0xAWM.eth" <29773064+0xAWM@users.noreply.github.com> Date: Mon, 27 Nov 2023 09:48:06 +0800 Subject: [PATCH 09/23] restore onchain test --- onchain_tests.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/onchain_tests.txt b/onchain_tests.txt index 014882bcd..610d95a36 100644 --- a/onchain_tests.txt +++ b/onchain_tests.txt @@ -19,6 +19,7 @@ THB_exp bsc 21785004 0x72e901F1bb2BfA2339326DfB90c5cEc911e2ba3C,0xae191Ca19F0f8E Novo_exp bsc 18225002 0xEeBc161437FA948AAb99383142564160c92D2974,0xa0787daad6062349f63b7c228cbfd5d8a3db08f1,0x3463a663de4ccc59c8b21190f81027096f18cf2a,0x6Fb2020C236BBD5a7DDEb07E14c9298642253333,0xbb4CdB9CBd36B01bD1cBaEBF2De08d9173bc095c,0x128cd0Ae1a0aE7e67419111714155E1B1c6B2D8D PLTD_exp bsc 22252045 0x55d398326f99059fF775485246999027B3197955,0xD7B7218D778338Ea05f5Ecce82f86D365E25dBCE,0x4397C76088db8f16C15455eB943Dd11F2DF56545,0x29b2525e11BC0B0E9E59f705F318601eA6756645 DYNA_exp bsc 25879486 0xa7B5eabC3Ee82c585f5F4ccC26b81c3Bd62Ff3a9,0xbb4CdB9CBd36B01bD1cBaEBF2De08d9173bc095c,0x5c0d0111ffc638802c9EfCcF55934D5C63aB3f79,0xb6148c6fA6Ebdd6e22eF5150c5C3ceE78b24a3a0 +OLIFE_exp bsc 27470678 0x915C2DFc34e773DC3415Fe7045bB1540F8BDAE84,0xbb4CdB9CBd36B01bD1cBaEBF2De08d9173bc095c,0xb5a0Ce3Acd6eC557d39aFDcbC93B07a1e1a9e3fa AUR_exp bsc 23282134 0x73A1163EA930A0a67dFEFB9C3713Ef0923755B78,0xbb4CdB9CBd36B01bD1cBaEBF2De08d9173bc095c,0x70678291bDDfd95498d1214BE368e19e882f7614 SellToken_exp bsc 28168034 0xbb4CdB9CBd36B01bD1cBaEBF2De08d9173bc095c,0xa645995e9801F2ca6e2361eDF4c2A138362BADe4,0x57Db19127617B77c8abd9420b5a35502b59870D6 Shadowfi_exp bsc 20969095 0xbb4CdB9CBd36B01bD1cBaEBF2De08d9173bc095c,0x10bc28d2810dD462E16facfF18f78783e859351b,0xF9e3151e813cd6729D52d9A0C3ee69F22CcE650A From e3267159a26429bc275ba68c228d536f0cdb39aa Mon Sep 17 00:00:00 2001 From: 0xAWM <29773064+0xAWM@users.noreply.github.com> Date: Mon, 27 Nov 2023 13:57:55 +0800 Subject: [PATCH 10/23] Update integration_test.py --- integration_test.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/integration_test.py b/integration_test.py index 810b421fd..cbdb77711 100644 --- a/integration_test.py +++ b/integration_test.py @@ -144,8 +144,8 @@ def test_onchain(test): etherscan_key, "--work-dir", f"w_{name}", - "--onchain-builder", - "https://solc-builder.dev.infra.fuzz.land/" + # "--onchain-builder", + # "https://solc-builder.dev.infra.fuzz.land/" # "--run-forever" ] From 97b73f5a68498f0092438881280d0ebb6ae3fcee Mon Sep 17 00:00:00 2001 From: "0xAWM.eth" <29773064+0xAWM@users.noreply.github.com> Date: Tue, 28 Nov 2023 10:54:43 +0800 Subject: [PATCH 11/23] clippy --- src/evm/mod.rs | 48 +++++++-------------------------------- src/evm/onchain/mod.rs | 1 - src/fuzzers/evm_fuzzer.rs | 5 +--- 3 files changed, 9 insertions(+), 45 deletions(-) diff --git a/src/evm/mod.rs b/src/evm/mod.rs index 38c56689c..252ff6979 100644 --- a/src/evm/mod.rs +++ b/src/evm/mod.rs @@ -177,40 +177,6 @@ pub struct EvmArgs { /// (Default: high_confidence) #[arg(long, short, default_value = "high_confidence")] detectors: String, // <- internally this is known as oracles - /// Detect selfdestruct (Default: true) - #[arg(long, default_value = "true")] - selfdestruct_oracle: bool, - - /// Detect pontential reentrancy vulnerability (Default: false) - #[arg(long, default_value = "false")] - reentrancy_oracle: bool, - - #[arg(long, default_value = "true")] - arbitrary_external_call_oracle: bool, - - #[arg(long, default_value = "true")] - math_calculate_oracle: bool, - - #[arg(long, default_value = "true")] - echidna_oracle: bool, - - #[arg(long, default_value = "true")] - invariant_oracle: bool, - - ///Enable oracle for detecting whether bug() / typed_bug() is called - #[arg(long, default_value = "true")] - typed_bug_oracle: bool, - - /// Setting any string here will enable state comparison oracle. - /// This arg holds file path pointing to state comparison oracle's desired - /// state - #[arg(long, default_value = "")] - state_comp_oracle: String, - - /// Matching style for state comparison oracle (Select from "Exact", - /// "DesiredContain", "StateContain") - #[arg(long, default_value = "Exact")] - state_comp_matching: String, // /// Matching style for state comparison oracle (Select from "Exact", // /// "DesiredContain", "StateContain") @@ -229,7 +195,7 @@ pub struct EvmArgs { write_relationship: bool, /// Do not quit when a bug is found, continue find new bugs - #[arg(long, default_value = "false")] + #[arg(long, default_value = "false", short = 'f')] run_forever: bool, /// random seed @@ -315,6 +281,7 @@ enum EVMTargetType { } impl EVMTargetType { + #[allow(dead_code)] fn as_str(&self) -> &'static str { match self { EVMTargetType::Glob => "glob", @@ -352,6 +319,7 @@ enum OracleType { } impl OracleType { + #[allow(dead_code)] fn as_str(&self) -> &'static str { match self { OracleType::ERC20 => "erc20", @@ -427,10 +395,6 @@ impl OracleType { pub fn evm_main(args: EvmArgs) { let target = args.target.clone(); let work_dir = args.work_dir.clone(); - let work_path = Path::new(work_dir.as_str()); - if !work_path.exists() { - std::fs::create_dir(work_path).unwrap(); - } let mut target_type: EVMTargetType = match args.target_type { Some(v) => EVMTargetType::from_str(v.as_str()), @@ -783,7 +747,11 @@ pub fn evm_main(args: EvmArgs) { let work_dir = args.work_dir.clone(); - let abis_json = format!("{}/abis.json", args.work_dir.clone().as_str()); + let path = Path::new(work_dir.as_str()); + if !path.exists() { + std::fs::create_dir_all(path).unwrap(); + } + let abis_json = format!("{}/abis.json", work_dir.as_str()); let mut file = OpenOptions::new() .create(true) diff --git a/src/evm/onchain/mod.rs b/src/evm/onchain/mod.rs index 0b3c05afd..8f30f6e0c 100644 --- a/src/evm/onchain/mod.rs +++ b/src/evm/onchain/mod.rs @@ -19,7 +19,6 @@ use libafl::{prelude::HasMetadata, schedulers::Scheduler}; use revm_interpreter::{analysis::to_analysed, Interpreter}; use revm_primitives::Bytecode; use tracing::debug; -use tracing_subscriber::field::debug; use super::{corpus_initializer::EnvMetadata, types::EVMFuzzState}; use crate::{ diff --git a/src/fuzzers/evm_fuzzer.rs b/src/fuzzers/evm_fuzzer.rs index 7fd4037b9..aeb1f6520 100644 --- a/src/fuzzers/evm_fuzzer.rs +++ b/src/fuzzers/evm_fuzzer.rs @@ -1,4 +1,4 @@ -use std::{cell::RefCell, collections::HashMap, fs::File, io::Read, ops::Deref, path::Path, process::exit, rc::Rc}; +use std::{cell::RefCell, collections::HashMap, fs::File, io::Read, ops::Deref, process::exit, rc::Rc}; use bytes::Bytes; use glob::glob; @@ -90,9 +90,6 @@ pub fn evm_fuzzer( ) { info!("\n\n ================ EVM Fuzzer Start ===================\n\n"); - // create work dir if not exists - let path = Path::new(config.work_dir.as_str()); - let monitor = SimpleMonitor::new(|s| info!("{}", s)); let mut mgr = SimpleEventManager::new(monitor); let infant_scheduler = SortedDroppingScheduler::new(); From 503af431fb11ef58519935ddc79dfea80555834a Mon Sep 17 00:00:00 2001 From: "0xAWM.eth" <29773064+0xAWM@users.noreply.github.com> Date: Wed, 29 Nov 2023 09:39:59 +0800 Subject: [PATCH 12/23] enable math_calculate detector --- integration_test.py | 23 +++++------------------ 1 file changed, 5 insertions(+), 18 deletions(-) diff --git a/integration_test.py b/integration_test.py index cbdb77711..ad234e7db 100644 --- a/integration_test.py +++ b/integration_test.py @@ -144,8 +144,10 @@ def test_onchain(test): etherscan_key, "--work-dir", f"w_{name}", - # "--onchain-builder", - # "https://solc-builder.dev.infra.fuzz.land/" + "-d", + "math_calculate", + "--onchain-builder", + "https://solc-builder.dev.infra.fuzz.land/" # "--run-forever" ] @@ -189,20 +191,6 @@ def test_onchain(test): def build_fuzzer(): - # build fuzzer - subprocess.run( - [ - "cargo", - "build", - "--release", - "--features", - "cmp dataflow evm print_txn_corpus full_trace", - "--no-default-features", - ] - ) - - -def build_flash_loan_v2_fuzzer(): # build fuzzer subprocess.run( [ @@ -230,13 +218,12 @@ def build_flash_loan_v2_fuzzer(): else: actions = ["onchain", "offchain"] + build_fuzzer() if "offchain" in actions: - build_fuzzer() with multiprocessing.Pool(3) as p: p.map(test_one, glob.glob("./tests/evm/*", recursive=True)) if "onchain" in actions: - build_flash_loan_v2_fuzzer() tests = read_onchain_tests() with multiprocessing.Pool(10) as p: p.map(test_onchain, tests) From 938113e67df5eabd67b082e22f95597f156a5336 Mon Sep 17 00:00:00 2001 From: "0xAWM.eth" <29773064+0xAWM@users.noreply.github.com> Date: Wed, 29 Nov 2023 12:49:56 +0800 Subject: [PATCH 13/23] disable precision loss --- src/evm/middlewares/math_calculate.rs | 2 +- src/evm/uniswap/mod.rs | 6 +++++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/src/evm/middlewares/math_calculate.rs b/src/evm/middlewares/math_calculate.rs index ebd8d8346..cb87df92c 100644 --- a/src/evm/middlewares/math_calculate.rs +++ b/src/evm/middlewares/math_calculate.rs @@ -114,7 +114,7 @@ where 0x04 | 0x05 => { // DIV/ SDIV // overflowing_add for placeholder, not used - check!(overflowing_add, "/"); + // check!(overflowing_add, "/"); } 0x0a => { // ** EXP diff --git a/src/evm/uniswap/mod.rs b/src/evm/uniswap/mod.rs index ede40ada4..cad448839 100644 --- a/src/evm/uniswap/mod.rs +++ b/src/evm/uniswap/mod.rs @@ -1,6 +1,7 @@ use std::{cell::RefCell, fmt::Debug, ops::Deref, rc::Rc, str::FromStr, sync::Arc}; use alloy_primitives::hex; +use tracing::warn; use crate::evm::{ abi::{A256InnerType, AArray, AEmpty, BoxedABI, A256}, @@ -310,7 +311,10 @@ pub fn get_uniswap_info(provider: &UniswapProvider, chain: &Chain) -> UniswapInf init_code_hash: hex::decode("96e8ac4277198ff8b6f785478aa9a39f403cb768dd02cbee326c3e7da348845f").unwrap(), pair_bytecode: hex::decode(ETH_UNISWAPV2_PAIR_BYTECODE).unwrap(), }, - _ => panic!("Uniswap provider {:?} @ chain {:?} not supported", provider, chain), + _ => { + warn!("Uniswap provider {:?} @ chain {:?} not supported", provider, chain); + UniswapInfo::default() + } } } pub const BSC_PANCAKEV2_PAIR_BYTECODE: &str = include_str!("bsc_pancakeV2_pair.bin"); From 09e602efadac5c9d5865c38f32ebe042b261e4ce Mon Sep 17 00:00:00 2001 From: "0xAWM.eth" <29773064+0xAWM@users.noreply.github.com> Date: Fri, 1 Dec 2023 19:11:10 +0800 Subject: [PATCH 14/23] fix --- src/evm/middlewares/math_calculate.rs | 6 +++--- src/evm/vm.rs | 8 +------- 2 files changed, 4 insertions(+), 10 deletions(-) diff --git a/src/evm/middlewares/math_calculate.rs b/src/evm/middlewares/math_calculate.rs index cb87df92c..7252f5515 100644 --- a/src/evm/middlewares/math_calculate.rs +++ b/src/evm/middlewares/math_calculate.rs @@ -55,7 +55,7 @@ where return; } let (l, r) = (interp.stack.peek(0).unwrap(), interp.stack.peek(1).unwrap()); - let overflow = if $op == "/" { l < r } else { l.$overflow_fn(r).1 }; + let overflow = if $op == "/" {l < r && alloy_primitives::Uint::ZERO == l } else { l.$overflow_fn(r).1 }; if !overflow || // already in fp self.fp.contains(&(addr, pc)) || @@ -86,7 +86,7 @@ where self.fp.insert((addr, pc)); } SourceCodeResult::SourceCode(source_code) => { - if source_code.contains($op) { + if let Some(pos) = source_code.find($op) && pos != 0 { // real bug info!("contract {:?} math error on pc[{pc:x}]: {} {} {} {source_code:?}", addr, l, $op, r); host.current_math_error.insert((addr, pc, $op)); @@ -114,7 +114,7 @@ where 0x04 | 0x05 => { // DIV/ SDIV // overflowing_add for placeholder, not used - // check!(overflowing_add, "/"); + check!(overflowing_add, "/"); } 0x0a => { // ** EXP diff --git a/src/evm/vm.rs b/src/evm/vm.rs index 5f4b1b99e..00e867033 100644 --- a/src/evm/vm.rs +++ b/src/evm/vm.rs @@ -820,13 +820,7 @@ where .chain(self.host.current_arbitrary_calls.iter().cloned()), ); - r.new_state.math_error = HashSet::from_iter( - vm_state - .math_error - .iter() - .cloned() - .chain(self.host.current_math_error.iter().cloned()), - ); + r.new_state.math_error = self.host.current_math_error.clone(); unsafe { ExecutionResult { From c3ef9486933614c4205d6e0e3966f44b0fc79762 Mon Sep 17 00:00:00 2001 From: Chaofan Shou Date: Sun, 24 Dec 2023 08:17:36 -0800 Subject: [PATCH 15/23] fix swap reverted but committed --- src/evm/input.rs | 2 ++ src/evm/oracles/erc20.rs | 13 ++++++++----- 2 files changed, 10 insertions(+), 5 deletions(-) diff --git a/src/evm/input.rs b/src/evm/input.rs index 3ac1e7527..128381b3b 100644 --- a/src/evm/input.rs +++ b/src/evm/input.rs @@ -144,6 +144,7 @@ pub struct EVMInput { pub repeat: usize, /// Swap data + #[serde(skip_deserializing)] pub swap_data: HashMap, } @@ -193,6 +194,7 @@ pub struct ConciseEVMInput { pub return_data: Option>, /// Swap data + #[serde(skip_deserializing)] pub swap_data: HashMap, } diff --git a/src/evm/oracles/erc20.rs b/src/evm/oracles/erc20.rs index 4703bf19d..f53285447 100644 --- a/src/evm/oracles/erc20.rs +++ b/src/evm/oracles/erc20.rs @@ -108,13 +108,16 @@ impl let (_out, mut state) = ctx.call_post_batch_dyn(&liquidation_txs); + let is_reverted = _out.iter().any(|(_, is_success)| *is_success == false); + // Record the swap info for generating foundry in the future. - state.swap_data = ctx.fuzz_state.get_execution_result().new_state.state.swap_data.clone(); - for (target, mut abi, _) in swap_infos { - state.swap_data.push(&target, &mut abi); + if !is_reverted { + state.swap_data = ctx.fuzz_state.get_execution_result().new_state.state.swap_data.clone(); + for (target, mut abi, _) in swap_infos { + state.swap_data.push(&target, &mut abi); + } + ctx.fuzz_state.get_execution_result_mut().new_state.state = state; } - - ctx.fuzz_state.get_execution_result_mut().new_state.state = state; } let exec_res = ctx.fuzz_state.get_execution_result_mut(); From c008251947a67d2b9e0f96945dede10a9e8d20ca Mon Sep 17 00:00:00 2001 From: Chaofan Shou Date: Sun, 24 Dec 2023 08:58:40 -0800 Subject: [PATCH 16/23] fix libafl --- Cargo.toml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 4a2f98a76..c17db5963 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -52,16 +52,16 @@ bytes = { version = "1.2.1", features = ["serde"] } retry = "2.0.0" serde_cbor = "0.11.2" clap = { version = "4.4.4", features = ["derive"] } -sentry = "0.29.1" +sentry = "0.32.1" rlp = "0.5.2" ethers = "2.0.7" hex = "0.4" primitive-types = { version = "0.12.1", features = ["rlp", "serde"] } -libafl = "0.11.1" -libafl_bolts = "0.11.1" +libafl = "=0.11.1" +libafl_bolts = "=0.11.1" rand = "0.8.5" -nix = "0.24" +nix = "0.27.1" serde = "1.0.147" serde_traitobject = "0.2.8" serde_json = "1.0.73" From 005e52f22d030888fde07cbf7258368cc093f3d3 Mon Sep 17 00:00:00 2001 From: "0xAWM.eth" <29773064+0xAWM@users.noreply.github.com> Date: Mon, 25 Dec 2023 11:13:44 +0800 Subject: [PATCH 17/23] fix --- offchain_config_1434_0_1701112172878_63428.json | 1 + src/evm/middlewares/coverage.rs | 9 --------- 2 files changed, 1 insertion(+), 9 deletions(-) create mode 100644 offchain_config_1434_0_1701112172878_63428.json diff --git a/offchain_config_1434_0_1701112172878_63428.json b/offchain_config_1434_0_1701112172878_63428.json new file mode 100644 index 000000000..81b8bc5f4 --- /dev/null +++ b/offchain_config_1434_0_1701112172878_63428.json @@ -0,0 +1 @@ +{"main.sol":{"HelloWorld":{"address":"0xe2f299db16d6263a0547edcda60a36abd4ab53ce","constructor_args":"0x"}}} \ No newline at end of file diff --git a/src/evm/middlewares/coverage.rs b/src/evm/middlewares/coverage.rs index 2b37c3c11..3d113f84d 100644 --- a/src/evm/middlewares/coverage.rs +++ b/src/evm/middlewares/coverage.rs @@ -319,15 +319,6 @@ where bytecode: &mut Bytecode, address: EVMAddress, ) { - 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()); - } - let (pcs, jumpis, mut skip_pcs) = instructions_pc(&bytecode.bytecode); // find all skipping PCs From 676a4b26e6e9783960b17db43c7a888105001cd1 Mon Sep 17 00:00:00 2001 From: "0xAWM.eth" <29773064+0xAWM@users.noreply.github.com> Date: Mon, 25 Dec 2023 11:15:39 +0800 Subject: [PATCH 18/23] fix div overflow --- src/evm/middlewares/math_calculate.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/evm/middlewares/math_calculate.rs b/src/evm/middlewares/math_calculate.rs index 7252f5515..5f31c73d3 100644 --- a/src/evm/middlewares/math_calculate.rs +++ b/src/evm/middlewares/math_calculate.rs @@ -55,7 +55,7 @@ where return; } let (l, r) = (interp.stack.peek(0).unwrap(), interp.stack.peek(1).unwrap()); - let overflow = if $op == "/" {l < r && alloy_primitives::Uint::ZERO == l } else { l.$overflow_fn(r).1 }; + let overflow = if $op == "/" {l < r || r == alloy_primitives::Uint::ZERO } else { l.$overflow_fn(r).1 }; if !overflow || // already in fp self.fp.contains(&(addr, pc)) || From b029191f9db3246e061d8b2b7bb3edbc99b4f6c9 Mon Sep 17 00:00:00 2001 From: Chaofan Shou Date: Mon, 25 Dec 2023 14:05:15 -0800 Subject: [PATCH 19/23] fix uncleared oracle data --- src/evm/vm.rs | 11 ++++++++++- src/fuzzer.rs | 14 +++++++++----- 2 files changed, 19 insertions(+), 6 deletions(-) diff --git a/src/evm/vm.rs b/src/evm/vm.rs index 8e9565c7b..9991ce441 100644 --- a/src/evm/vm.rs +++ b/src/evm/vm.rs @@ -451,6 +451,7 @@ macro_rules! init_host { ($host:expr) => { $host.current_self_destructs = vec![]; $host.current_arbitrary_calls = vec![]; + $host.current_math_error = HashSet::new(); $host.call_count = 0; $host.jumpi_trace = 37; $host.current_typed_bug = vec![]; @@ -563,6 +564,7 @@ where self.host.jumpi_trace = 37; self.host.current_self_destructs = vec![]; self.host.current_arbitrary_calls = vec![]; + self.host.current_math_error = HashSet::new(); // Initially, there is no state change unsafe { STATE_CHANGE = false; @@ -831,7 +833,13 @@ where .chain(self.host.current_arbitrary_calls.iter().cloned()), ); - r.new_state.math_error = self.host.current_math_error.clone(); + r.new_state.math_error = HashSet::from_iter( + vm_state + .math_error + .iter() + .cloned() + .chain(self.host.current_math_error.iter().cloned()), + ); unsafe { ExecutionResult { @@ -1074,6 +1082,7 @@ where self.host.evmstate = vm_state.as_any().downcast_ref_unchecked::().clone(); self.host.current_self_destructs = vec![]; self.host.current_arbitrary_calls = vec![]; + self.host.current_math_error = HashSet::new(); self.host.call_count = 0; self.host.jumpi_trace = 37; self.host.current_typed_bug = vec![]; diff --git a/src/fuzzer.rs b/src/fuzzer.rs index cd7d68adf..81b90ba16 100644 --- a/src/fuzzer.rs +++ b/src/fuzzer.rs @@ -598,11 +598,15 @@ where if !path.exists() { std::fs::create_dir_all(path).unwrap(); } - let mut file = File::create(format!("{}/{}", vulns_dir, bug_idxs.clone())).unwrap(); - file.write_all(data.as_bytes()).unwrap(); - let mut replayable_file = - File::create(format!("{}/{}_replayable", vulns_dir, bug_idxs)).unwrap(); - replayable_file.write_all(txn_json.as_bytes()).unwrap(); + + // println!("bug_idxs: {}", bug_idxs); + for bug_idx in bug_idxs.split(",") { + let mut file = File::create(format!("{}/{}", vulns_dir, bug_idx.clone())).unwrap(); + file.write_all(data.as_bytes()).unwrap(); + let mut replayable_file = + File::create(format!("{}/{}_replayable", vulns_dir, bug_idx)).unwrap(); + replayable_file.write_all(txn_json.as_bytes()).unwrap(); + } } // dump_file!(state, vulns_dir, false); } From fc3e78a9ff48c464a455e645fb6c34aab9be9b99 Mon Sep 17 00:00:00 2001 From: "0xAWM.eth" <29773064+0xAWM@users.noreply.github.com> Date: Tue, 26 Dec 2023 09:54:14 +0800 Subject: [PATCH 20/23] 10m timeout --- integration_test.py | 2 +- src/evm/middlewares/math_calculate.rs | 4 ++++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/integration_test.py b/integration_test.py index ad234e7db..c706d1b42 100644 --- a/integration_test.py +++ b/integration_test.py @@ -56,7 +56,7 @@ def test_one(path): start_time = time.time() cmd = [ TIMEOUT_BIN, - "5s", + "10m", "./target/release/ityfuzz", "evm", "-t", diff --git a/src/evm/middlewares/math_calculate.rs b/src/evm/middlewares/math_calculate.rs index 5f31c73d3..2f71bf039 100644 --- a/src/evm/middlewares/math_calculate.rs +++ b/src/evm/middlewares/math_calculate.rs @@ -88,6 +88,10 @@ where SourceCodeResult::SourceCode(source_code) => { if let Some(pos) = source_code.find($op) && pos != 0 { // real bug + if host.current_math_error.contains(&(addr, pc, $op)) { + // already reported + return; + } info!("contract {:?} math error on pc[{pc:x}]: {} {} {} {source_code:?}", addr, l, $op, r); host.current_math_error.insert((addr, pc, $op)); } else { From 218b8f64fb08393515c46ab03b3648c245761e13 Mon Sep 17 00:00:00 2001 From: "0xAWM.eth" <29773064+0xAWM@users.noreply.github.com> Date: Tue, 26 Dec 2023 09:56:29 +0800 Subject: [PATCH 21/23] 15m timeout --- integration_test.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/integration_test.py b/integration_test.py index c706d1b42..67cdf0481 100644 --- a/integration_test.py +++ b/integration_test.py @@ -56,7 +56,7 @@ def test_one(path): start_time = time.time() cmd = [ TIMEOUT_BIN, - "10m", + "5s", "./target/release/ityfuzz", "evm", "-t", @@ -130,7 +130,7 @@ def test_onchain(test): cmd = [ TIMEOUT_BIN, # set timeout to 5m because it takes longer time to sync the chain - "5m", + "15m", "./target/release/ityfuzz", "evm", "-t", From 228d5775e91fa054fbb65161f1619ed91450010f Mon Sep 17 00:00:00 2001 From: "0xAWM.eth" <29773064+0xAWM@users.noreply.github.com> Date: Tue, 26 Dec 2023 16:21:35 +0800 Subject: [PATCH 22/23] clean --- integration_test.py | 2 +- src/evm/config.rs | 1 + src/evm/middlewares/math_calculate.rs | 22 +++++++--------------- src/evm/mod.rs | 4 ++++ src/fuzzers/evm_fuzzer.rs | 5 ++++- 5 files changed, 17 insertions(+), 17 deletions(-) diff --git a/integration_test.py b/integration_test.py index 67cdf0481..ad234e7db 100644 --- a/integration_test.py +++ b/integration_test.py @@ -130,7 +130,7 @@ def test_onchain(test): cmd = [ TIMEOUT_BIN, # set timeout to 5m because it takes longer time to sync the chain - "15m", + "5m", "./target/release/ityfuzz", "evm", "-t", diff --git a/src/evm/config.rs b/src/evm/config.rs index 5dd77fdf7..8d38d6a35 100644 --- a/src/evm/config.rs +++ b/src/evm/config.rs @@ -86,6 +86,7 @@ pub struct Config { pub typed_bug: bool, pub arbitrary_external_call: bool, pub math_calculate_oracle: bool, + pub math_calculate_report_when_no_srcmap: bool, pub builder: Option, pub local_files_basedir_pattern: Option, pub load_corpus: String, diff --git a/src/evm/middlewares/math_calculate.rs b/src/evm/middlewares/math_calculate.rs index 2f71bf039..abd19a36f 100644 --- a/src/evm/middlewares/math_calculate.rs +++ b/src/evm/middlewares/math_calculate.rs @@ -4,7 +4,7 @@ use libafl::schedulers::Scheduler; use revm_interpreter::Interpreter; use revm_primitives::{keccak256, B256}; use serde::Serialize; -use tracing::{debug, info}; +use tracing::debug; use crate::evm::{ host::FuzzHost, @@ -30,7 +30,6 @@ impl MathCalculateMiddleware { let info = get_uniswap_info(&UniswapProvider::UniswapV2, chain); let whitelist = HashSet::from([info.router]); let pair_hash = keccak256(&info.pair_bytecode); - println!("pair_hash: {:?}", pair_hash); return Self { report_when_no_srcmap, whitelist, @@ -71,11 +70,15 @@ where self.whitelist.insert(addr); return; } + if host.current_math_error.contains(&(addr, pc, $op)) { + // already reported + return; + } match SOURCE_MAP_PROVIDER.lock().unwrap().get_source_code(&addr, pc) { SourceCodeResult::NoSourceMap => { // no srcmap if self.report_when_no_srcmap { - info!("contract {:?} maybe math error on pc[{pc:x}]: {} {} {}", addr, l, $op, r); + debug!("contract {:?} maybe math error on pc[{pc:x}]: {} {} {}", addr, l, $op, r); host.current_math_error.insert((addr, pc, $op)); } else { self.fp.insert((addr, pc)); @@ -88,11 +91,7 @@ where SourceCodeResult::SourceCode(source_code) => { if let Some(pos) = source_code.find($op) && pos != 0 { // real bug - if host.current_math_error.contains(&(addr, pc, $op)) { - // already reported - return; - } - info!("contract {:?} math error on pc[{pc:x}]: {} {} {} {source_code:?}", addr, l, $op, r); + debug!("contract {:?} math error on pc[{pc:x}]: {} {} {} {source_code:?}", addr, l, $op, r); host.current_math_error.insert((addr, pc, $op)); } else { // fp @@ -132,10 +131,3 @@ where MiddlewareType::IntegerOverflow } } - -#[cfg(test)] -mod test { - - #[test] - fn test_merge() {} -} diff --git a/src/evm/mod.rs b/src/evm/mod.rs index cb4907c9b..e55c3a132 100644 --- a/src/evm/mod.rs +++ b/src/evm/mod.rs @@ -178,6 +178,9 @@ pub struct EvmArgs { #[arg(long, short, default_value = "high_confidence")] detectors: String, // <- internally this is known as oracles + #[arg(long, short, default_value = "false")] + math_calculate_report_when_no_srcmap: bool, + // /// Matching style for state comparison oracle (Select from "Exact", // /// "DesiredContain", "StateContain") // #[arg(long, default_value = "Exact")] @@ -715,6 +718,7 @@ pub fn evm_main(args: EvmArgs) { typed_bug: oracle_types.contains(&OracleType::TypedBug), arbitrary_external_call: oracle_types.contains(&OracleType::ArbitraryCall), math_calculate_oracle: oracle_types.contains(&OracleType::MathCalculate), + math_calculate_report_when_no_srcmap: args.math_calculate_report_when_no_srcmap, builder, local_files_basedir_pattern: match target_type { EVMTargetType::Glob => Some(args.target), diff --git a/src/fuzzers/evm_fuzzer.rs b/src/fuzzers/evm_fuzzer.rs index aeb1f6520..039cbfcff 100644 --- a/src/fuzzers/evm_fuzzer.rs +++ b/src/fuzzers/evm_fuzzer.rs @@ -180,7 +180,10 @@ pub fn evm_fuzzer( if config.math_calculate_oracle { debug!("math_calculate oracle enabled"); - let integer_overflow_middleware = Rc::new(RefCell::new(MathCalculateMiddleware::new(config.onchain, true))); + let integer_overflow_middleware = Rc::new(RefCell::new(MathCalculateMiddleware::new( + config.onchain, + config.math_calculate_report_when_no_srcmap, + ))); fuzz_host.add_middlewares(integer_overflow_middleware); } From 4e449c2a96bf770f5a237d5636be41195d1e8ca5 Mon Sep 17 00:00:00 2001 From: Chaofan Shou Date: Tue, 26 Dec 2023 10:52:22 -0800 Subject: [PATCH 23/23] rerun ci --- integration_test.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/integration_test.py b/integration_test.py index ad234e7db..9189fcc60 100644 --- a/integration_test.py +++ b/integration_test.py @@ -144,8 +144,6 @@ def test_onchain(test): etherscan_key, "--work-dir", f"w_{name}", - "-d", - "math_calculate", "--onchain-builder", "https://solc-builder.dev.infra.fuzz.land/" # "--run-forever"