Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

support Dump external call relations #82

Closed
wants to merge 10 commits into from
25 changes: 22 additions & 3 deletions cli/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,8 @@ use ityfuzz::evm::producers::erc20::ERC20Producer;
use std::env;
use ityfuzz::evm::host::PANIC_ON_BUG;
use ityfuzz::evm::oracles::bug::BugOracle;
use ityfuzz::evm::host::PANIC_ON_TYPEDBUG;
use ityfuzz::evm::oracles::typed_bug::TypedBugOracle;

pub fn init_sentry() {
let _guard = sentry::init(("https://96f3517bd77346ea835d28f956a84b9d@o4504503751344128.ingest.sentry.io/4504503752523776", sentry::ClientOptions {
Expand Down Expand Up @@ -123,13 +125,20 @@ struct Args {
#[arg(long, default_value = "false")]
panic_on_bug: bool,

///Enable oracle for detecting whether typed_bug() is called
#[arg(long, default_value = "true")]
typed_bug_oracle: bool,

#[arg(long, default_value = "false")]
panic_on_typedbug: bool,

/// Replay?
#[arg(long)]
replay_file: Option<String>,

// allow users to pass the path through CLI
#[arg(long, default_value = "corpus")]
corpus_path: String,
#[arg(long, default_value = "work_dir")]
work_dir: String,

// random seed
#[arg(long, default_value = "1667840158231589000")]
Expand Down Expand Up @@ -262,6 +271,16 @@ fn main() {
}
}

if args.typed_bug_oracle {
oracles.push(Rc::new(RefCell::new(TypedBugOracle::new())));

if args.panic_on_typedbug {
unsafe {
PANIC_ON_TYPEDBUG = true;
}
}
}

if args.ierc20_oracle || args.pair_oracle {
producers.push(pair_producer);
}
Expand Down Expand Up @@ -332,7 +351,7 @@ fn main() {
},
replay_file: args.replay_file,
flashloan_oracle,
corpus_path:args.corpus_path,
work_dir:args.work_dir,
};

match config.fuzzer_type {
Expand Down
10 changes: 10 additions & 0 deletions solidity_utils/lib.sol
Original file line number Diff line number Diff line change
Expand Up @@ -8,3 +8,13 @@ function bug() {
log1(p, 0x20, 0x133337)
}
}

function typed_bug(uint256 typed) {
typed = typed * 0x100 + 0x78;
bytes32 t2 = bytes32(uint256(uint160(msg.sender)));
assembly {
let p := add(msize(), 0x20)
mstore(p, t2)
log1(p, 0x20, typed)
}
}
1 change: 1 addition & 0 deletions src/evm/abi.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1258,6 +1258,7 @@ mod tests {
fn test_null() {
let mut abi = get_abi_type_boxed(&String::from("(int256,int256,int256,uint256,address)[]"));
let mut test_state = FuzzState::new(0);
test_state.addresses_pool.push(H160::zero());
let mutation_result = abi.mutate::<H160, H160, EVMState, EVMFuzzState>(&mut test_state);
println!(
"result: {:?} abi: {:?}",
Expand Down
2 changes: 1 addition & 1 deletion src/evm/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -53,5 +53,5 @@ pub struct Config<VS, Addr, Code, By, Loc, SlotTy, Out, I, S> {
pub price_oracle: Box<dyn PriceOracle>,
pub replay_file: Option<String>,
pub flashloan_oracle: Rc<RefCell<IERC20OracleFlashloan>>,
pub corpus_path: String,
pub work_dir: String,
}
75 changes: 73 additions & 2 deletions src/evm/host.rs
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ pub static mut GLOBAL_CALL_CONTEXT: Option<CallContext> = None;
pub static mut GLOBAL_CALL_DATA: Option<CallContext> = None;

pub static mut PANIC_ON_BUG: bool = false;

pub static mut PANIC_ON_TYPEDBUG: bool = false;
// for debugging purpose, return ControlLeak when the calls amount exceeds this value
pub static mut CALL_UNTIL: u32 = u32::MAX;

Expand Down Expand Up @@ -102,12 +102,20 @@ where
pub access_pattern: Rc<RefCell<AccessPattern>>,

pub bug_hit: bool,
pub typed_bug: H256,
pub call_count: u32,

#[cfg(feature = "print_logs")]
pub logs: HashSet<u64>,
// set_code data
pub setcode_data: HashMap<H160, Bytecode>,
// relations file handle
relations_file: std::fs::File,
// Filter duplicate relations
relations_hash: HashSet<u64>,
/// Filter typed_bug
typed_bug_hash: HashSet<u64>,

}

impl<VS, I, S> Debug for FuzzHost<VS, I, S>
Expand Down Expand Up @@ -169,6 +177,10 @@ where
#[cfg(feature = "print_logs")]
logs: Default::default(),
setcode_data:self.setcode_data.clone(),
relations_file: self.relations_file.try_clone().unwrap(),
relations_hash: self.relations_hash.clone(),
typed_bug: self.typed_bug.clone(),
typed_bug_hash: self.typed_bug_hash.clone(),
}
}
}
Expand All @@ -190,7 +202,7 @@ where
I: VMInputT<VS, H160, H160> + EVMInputT,
VS: VMStateT,
{
pub fn new(scheduler: Arc<dyn Scheduler<EVMInput, S>>) -> Self {
pub fn new(scheduler: Arc<dyn Scheduler<EVMInput, S>>, workdir: String) -> Self {
let ret = Self {
evmstate: EVMState::new(),
env: Env::default(),
Expand All @@ -215,6 +227,10 @@ where
#[cfg(feature = "print_logs")]
logs: Default::default(),
setcode_data:HashMap::new(),
relations_file: std::fs::File::create(format!("{}/relations.log", workdir)).unwrap(),
relations_hash: HashSet::new(),
typed_bug: Default::default(),
typed_bug_hash: HashSet::new(),
};
// ret.env.block.timestamp = U256::max_value();
ret
Expand Down Expand Up @@ -362,6 +378,29 @@ where
// vec![]
// }
}
pub fn write_relations(&mut self, caller: H160, target: H160, funtion_hash: Bytes) {
if funtion_hash.len() < 0x4 {
return;
}
let cur_write_str = format!("{{caller:0x{} --> traget:0x{} function(0x{})}}\n", hex::encode(caller), hex::encode(target), hex::encode(&funtion_hash[..4]));
let mut hasher = DefaultHasher::new();
cur_write_str.hash(&mut hasher);
let cur_wirte_hash = hasher.finish();
if self.relations_hash.contains(&cur_wirte_hash) {
return;
}
if self.relations_hash.len() == 0{
let write_head = format!("[ityfuzz relations] caller, traget, function hash\n");
self.relations_file
.write_all(write_head.as_bytes())
.unwrap();
}

self.relations_hash.insert(cur_wirte_hash);
self.relations_file
.write_all(cur_write_str.as_bytes())
.unwrap();
}
}

macro_rules! process_rw_key {
Expand Down Expand Up @@ -395,6 +434,13 @@ where
type DB = BenchmarkDB;
fn step(&mut self, interp: &mut Interpreter, _is_static: bool, state: &mut S) -> Return {
unsafe {
/*
self.write_relations(
interp.contract.caller.clone(),
interp.contract.address.clone(),
interp.contract.input.clone(),
);
*/
if self.middlewares_enabled {
match self.flashloan_middleware.clone() {
Some(m) => {
Expand Down Expand Up @@ -644,6 +690,25 @@ where
panic!("target hit");
}
self.bug_hit = true;
} else if _topics.len() == 1 && (*_topics.last().unwrap()).0[31] == 0x78{
if unsafe {PANIC_ON_TYPEDBUG} {
panic!("target typed_bug hit");
}
let mut hasher = DefaultHasher::new();
let curtyped = (*_topics.last().unwrap()).0;
let mut curtyped2:[u8;32] = [0;32];
let mut index = 1;
while index < 32 {
curtyped2[index] = curtyped[index-1];
index = index + 1;
}
let curtypedbug = H256::from(curtyped2);
curtypedbug.hash(&mut hasher);
let cur_wirte_hash = hasher.finish();
if !self.typed_bug_hash.contains(&cur_wirte_hash) {
self.typed_bug = curtypedbug;
self.typed_bug_hash.insert(cur_wirte_hash);
}
}
#[cfg(feature = "print_logs")]
{
Expand Down Expand Up @@ -718,6 +783,12 @@ where
return (ControlLeak, Gas::new(0), Bytes::new());
}

self.write_relations(
input.transfer.source.clone(),
input.contract.clone(),
input.input.clone(),
);

let mut hash = input.input.to_vec();
hash.resize(4, 0);

Expand Down
3 changes: 2 additions & 1 deletion src/evm/oracles/mod.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
pub mod erc20;
pub mod function;
pub mod v2_pair;
pub mod bug;
pub mod bug;
pub mod typed_bug;
77 changes: 77 additions & 0 deletions src/evm/oracles/typed_bug.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
use crate::evm::input::EVMInput;
use crate::evm::oracle::dummy_precondition;
use crate::evm::oracles::erc20::ORACLE_OUTPUT;
use crate::evm::producers::pair::PairProducer;
use crate::evm::types::{EVMFuzzState, EVMOracleCtx};
use crate::evm::vm::EVMState;
use crate::oracle::{Oracle, OracleCtx, Producer};
use crate::state::HasExecutionResult;
use bytes::Bytes;
use primitive_types::{H160, H256, U256};
use revm::Bytecode;
use std::borrow::Borrow;
use std::cell::RefCell;
use std::collections::HashMap;
use std::ops::Deref;
use std::rc::Rc;

pub struct TypedBugOracle;

impl TypedBugOracle {
pub fn new() -> Self {
Self {}
}

fn HToStrHex(&self, list: H256) -> String {
// get list bytes
let tmp = list.to_fixed_bytes();
let mut index = 0;
for i in tmp.iter() {
if *i != 0 {
break;
}
index += 1;
}
format!("0x{}", hex::encode(tmp[index..].to_vec()))
}

}



impl Oracle<EVMState, H160, Bytecode, Bytes, H160, U256, Vec<u8>, EVMInput, EVMFuzzState>
for TypedBugOracle
{
fn transition(&self, _ctx: &mut EVMOracleCtx<'_>, _stage: u64) -> u64 {
0
}

fn oracle(
&self,
ctx: &mut OracleCtx<
EVMState,
H160,
Bytecode,
Bytes,
H160,
U256,
Vec<u8>,
EVMInput,
EVMFuzzState,
>,
stage: u64,
) -> bool {
let is_hit = ctx.post_state.typed_bug.is_zero() == false;
if is_hit {
unsafe {
let typed_bug_hash = self.HToStrHex(ctx.post_state.typed_bug);
ORACLE_OUTPUT = format!(
"[typed_bug] typed_bug({}) hit at contract {:?}",
typed_bug_hash,
ctx.input.contract
)
}
}
return is_hit;
}
}
Loading