Skip to content

Commit

Permalink
Reduce Havoc + Fix for Olympus + Reentrancy Oracle (#251)
Browse files Browse the repository at this point in the history
* reduce havoc

* reuse corpus id

* add static call leak

* move out state mutation

* ooops

* bug fix for onchain tasks

* random poop that may fix shit

* crash fix

* trytry

* add reentrancy oracle

* revert havoc change

* some param changes

* clippy

* rewrite [sorting entry]

* clippy

---------

Co-authored-by: 0xAWM.eth <[email protected]>
  • Loading branch information
shouc and 0xAWM authored Oct 13, 2023
1 parent 45ec9d5 commit 3225ff7
Show file tree
Hide file tree
Showing 20 changed files with 547 additions and 215 deletions.
6 changes: 3 additions & 3 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -76,17 +76,17 @@ regex = "1"
typetag = "0.2.13"
lazy_static = "1.4.0"

revm = { git = "https://github.com/fuzzland/revm", rev = "f9fdf3e66f03ab864c8224c1720c7550f8488d28", features = [
revm = { git = "https://github.com/fuzzland/revm", rev = "60d409c17d9fff11d1063b985f05be3e3280fe80", features = [
"no_gas_measuring",
"serde",
"memory_limit",
] }
revm-primitives = { git = "https://github.com/fuzzland/revm", rev = "f9fdf3e66f03ab864c8224c1720c7550f8488d28", features = [
revm-primitives = { git = "https://github.com/fuzzland/revm", rev = "60d409c17d9fff11d1063b985f05be3e3280fe80", features = [
"no_gas_measuring",
"serde",
"memory_limit",
] }
revm-interpreter = { git = "https://github.com/fuzzland/revm", rev = "f9fdf3e66f03ab864c8224c1720c7550f8488d28", features = [
revm-interpreter = { git = "https://github.com/fuzzland/revm", rev = "60d409c17d9fff11d1063b985f05be3e3280fe80", features = [
"no_gas_measuring",
"serde",
"memory_limit",
Expand Down
16 changes: 7 additions & 9 deletions src/evm/bytecode_analyzer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,16 +27,14 @@ pub fn find_constants(bytecode: &Bytecode) -> HashSet<Vec<u8>> {
if next_op == JUMPI {
continue;
}
if op as usize - 0x60 + 1 >= 5 {
let mut data = vec![0u8; op as usize - 0x60 + 1];
let mut i = 0;
while i < op - 0x60 + 1 {
let offset = i as usize;
data[offset] = bytes[pc + offset + 1];
i += 1;
}
constants.insert(data);
let mut data = vec![0u8; op as usize - 0x60 + 1];
let mut i = 0;
while i < op - 0x60 + 1 {
let offset = i as usize;
data[offset] = bytes[pc + offset + 1];
i += 1;
}
constants.insert(data);
}
}
constants
Expand Down
4 changes: 0 additions & 4 deletions src/evm/concolic/concolic_host.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1410,10 +1410,6 @@ where
self.pop_ctx();
}

unsafe fn on_insert(&mut self, bytecode: &mut Bytecode, address: EVMAddress, host: &mut FuzzHost<VS, I, S, SC>, state: &mut S) {

}

fn get_type(&self) -> MiddlewareType {
Concolic
}
Expand Down
1 change: 1 addition & 0 deletions src/evm/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@ pub struct Config<VS, Addr, Code, By, Loc, SlotTy, Out, I, S, CI> {
pub replay_file: Option<String>,
pub flashloan_oracle: Rc<RefCell<IERC20OracleFlashloan>>,
pub selfdestruct_oracle: bool,
pub reentrancy_oracle: bool,
pub state_comp_oracle: Option<String>,
pub state_comp_matching: Option<String>,
pub work_dir: String,
Expand Down
120 changes: 39 additions & 81 deletions src/evm/host.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ use crate::evm::middlewares::middleware::{
add_corpus, CallMiddlewareReturn, Middleware, MiddlewareType,
};
use crate::evm::mutator::AccessPattern;
use crate::invoke_middlewares;

use crate::evm::onchain::flashloan::register_borrow_txn;
use crate::evm::onchain::flashloan::Flashloan;
Expand Down Expand Up @@ -52,7 +53,7 @@ use revm_primitives::{
PetersburgSpec, ShanghaiSpec, SpecId, SpuriousDragonSpec, TangerineSpec,
};

use super::vm::MEM_LIMIT;
use super::vm::{MEM_LIMIT, IS_FAST_CALL};

pub static mut JMP_MAP: [u8; MAP_SIZE] = [0; MAP_SIZE];

Expand Down Expand Up @@ -246,8 +247,8 @@ pub static mut ACTIVE_MATCH_EXT_CALL: bool = false;
const CONTROL_LEAK_DETECTION: bool = false;
const UNBOUND_CALL_THRESHOLD: usize = 3;

// if a PC transfers control to >2 addresses, we consider call at this PC to be unbounded
const CONTROL_LEAK_THRESHOLD: usize = 2;
// if a PC transfers control to >10 addresses, we consider call at this PC to be unbounded
const CONTROL_LEAK_THRESHOLD: usize = 10;

impl<VS, I, S, SC> FuzzHost<VS, I, S, SC>
where
Expand Down Expand Up @@ -448,20 +449,7 @@ where

pub fn set_code(&mut self, address: EVMAddress, mut code: Bytecode, state: &mut S) {
unsafe {
if self.middlewares_enabled {
if let Some(m) = self.flashloan_middleware.clone() {
let mut middleware = m.deref().borrow_mut();
middleware.on_insert(&mut code, address, self, state);
}

for middleware in &mut self.middlewares.clone().deref().borrow_mut().iter_mut() {
middleware
.deref()
.deref()
.borrow_mut()
.on_insert(&mut code, address, self, state);
}
}
invoke_middlewares!(self, None, state, on_insert, &mut code, address);
}
self.code.insert(
address,
Expand Down Expand Up @@ -588,9 +576,31 @@ where

let input_seq = input.input.to_vec();

let is_target_address_unbounded = {
assert_ne!(self._pc, 0);
self.pc_to_addresses
.entry((input.context.caller, self._pc))
.or_insert_with(HashSet::new);
let addresses_at_pc = self
.pc_to_addresses
.get_mut(&(input.context.caller, self._pc))
.unwrap();
addresses_at_pc.insert(input.contract);
addresses_at_pc.len() > CONTROL_LEAK_THRESHOLD
};

if input.context.scheme == CallScheme::StaticCall {
if state.has_caller(&input.contract) || is_target_address_unbounded {
record_func_hash!();
push_interp!();
// println!("call self {:?} -> {:?} with {:?}", input.context.caller, input.contract, hex::encode(input.input.clone()));
return (InstructionResult::AddressUnboundedStaticCall, Gas::new(0), Bytes::new());
}
}

if input.context.scheme == CallScheme::Call {
// if calling sender, then definitely control leak
if state.has_caller(&input.contract) {
if state.has_caller(&input.contract) || is_target_address_unbounded {
record_func_hash!();
push_interp!();
// println!("call self {:?} -> {:?} with {:?}", input.context.caller, input.contract, hex::encode(input.input.clone()));
Expand Down Expand Up @@ -628,26 +638,7 @@ where
Gas::new(0),
Bytes::new(),
);
}

// control leak check
assert_ne!(self._pc, 0);
self.pc_to_addresses
.entry((input.context.caller, self._pc))
.or_insert_with(HashSet::new);
let addresses_at_pc = self
.pc_to_addresses
.get_mut(&(input.context.caller, self._pc))
.unwrap();
addresses_at_pc.insert(input.contract);

// if control leak is enabled, return controlleak if it is unbounded call
if CONTROL_LEAK_DETECTION && addresses_at_pc.len() > CONTROL_LEAK_THRESHOLD {
record_func_hash!();
push_interp!();
// println!("control leak {:?} -> {:?} with {:?}", input.context.caller, input.contract, hex::encode(input.input.clone()));
return (ControlLeak, Gas::new(0), Bytes::new());
}
}
}

let input_bytes = Bytes::from(input_seq);
Expand Down Expand Up @@ -682,7 +673,7 @@ where
// if there is code, then call the code
let res = self.call_forbid_control_leak(input, state);
match res.0 {
ControlLeak | InstructionResult::ArbitraryExternalCallAddressBounded(_, _, _) => {
ControlLeak | InstructionResult::ArbitraryExternalCallAddressBounded(_, _, _) | InstructionResult::AddressUnboundedStaticCall => {
self.leak_ctx.push(SinglePostExecution::from_interp(
interp,
(out_offset, out_len),
Expand Down Expand Up @@ -768,55 +759,22 @@ macro_rules! u256_to_u8 {

#[macro_export]
macro_rules! invoke_middlewares {
($host: expr, $interp: expr, $state: expr, $invoke: ident) => {
($host:expr, $interp:expr, $state:expr, $invoke:ident $(, $arg:expr)*) => {
if $host.middlewares_enabled {
match $host.flashloan_middleware.clone() {
Some(m) => {
let mut middleware = m.deref().borrow_mut();
middleware.$invoke($interp, $host, $state);
}
_ => {}
}
if $host.setcode_data.len() > 0 {
$host.clear_codedata();
if let Some(m) = $host.flashloan_middleware.clone() {
let mut middleware = m.deref().borrow_mut();
middleware.$invoke($interp, $host, $state $(, $arg)*);
}
for middleware in &mut $host.middlewares.clone().deref().borrow_mut().iter_mut() {
middleware
.deref()
.deref()
.borrow_mut()
.$invoke($interp, $host, $state);
}

if $host.setcode_data.len() > 0 {
for (address, code) in &$host.setcode_data.clone() {
$host.set_code(address.clone(), code.clone(), $state);
}
}
}
};

($code: expr, $addr: expr, $host: expr, $state: expr, $invoke: ident) => {
if $host.middlewares_enabled {
match $host.flashloan_middleware.clone() {
Some(m) => {
let mut middleware = m.deref().borrow_mut();
middleware.$invoke($code, $addr, $host, $state);
}
_ => {}
}
if $host.setcode_data.len() > 0 {
if !$host.setcode_data.is_empty() {
$host.clear_codedata();
}

for middleware in &mut $host.middlewares.clone().deref().borrow_mut().iter_mut() {
middleware
.deref()
.deref()
.borrow_mut()
.$invoke($code, $addr, $host, $state);
middleware.deref().deref().borrow_mut().$invoke($interp, $host, $state $(, $arg)*);
}

if $host.setcode_data.len() > 0 {
if !$host.setcode_data.is_empty() {
for (address, code) in &$host.setcode_data.clone() {
$host.set_code(address.clone(), code.clone(), $state);
}
Expand Down Expand Up @@ -1312,7 +1270,7 @@ where

let res = if is_precompile(input.contract, self.precompiles.len()) {
self.call_precompile(input, state)
} else if unsafe { IS_FAST_CALL_STATIC } {
} else if unsafe { IS_FAST_CALL_STATIC || IS_FAST_CALL } {
self.call_forbid_control_leak(input, state)
} else {
self.call_allow_control_leak(input, interp, output_info, state)
Expand Down
9 changes: 0 additions & 9 deletions src/evm/middlewares/call_printer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -324,15 +324,6 @@ where
self.current_layer -= 1;
}

unsafe fn on_insert(
&mut self,
bytecode: &mut Bytecode,
address: EVMAddress,
host: &mut FuzzHost<VS, I, S, SC>,
state: &mut S,
) {
}

fn get_type(&self) -> MiddlewareType {
MiddlewareType::CallPrinter
}
Expand Down
8 changes: 7 additions & 1 deletion src/evm/middlewares/coverage.rs
Original file line number Diff line number Diff line change
Expand Up @@ -298,7 +298,13 @@ where
}
}

unsafe fn on_insert(&mut self, bytecode: &mut Bytecode, address: EVMAddress, host: &mut FuzzHost<VS, I, S, SC>, state: &mut S) {
unsafe fn on_insert(&mut self,
_: Option<&mut Interpreter>,
host: &mut FuzzHost<VS, I, S, SC>,
state: &mut S,
bytecode: &mut Bytecode,
address: EVMAddress,
) {
let (pcs, jumpis, mut skip_pcs) = instructions_pc(&bytecode.clone());

// find all skipping PCs
Expand Down
20 changes: 16 additions & 4 deletions src/evm/middlewares/middleware.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
use crate::evm::host::FuzzHost;
use crate::evm::input::{ConciseEVMInput, EVMInput, EVMInputT};
use crate::evm::vm::EVMState;
use crate::generic_vm::vm_state::VMStateT;
use crate::input::VMInputT;
use crate::state::{HasCaller, HasItyState};
Expand Down Expand Up @@ -32,6 +33,7 @@ pub enum MiddlewareType {
Sha3Bypass,
Sha3TaintAnalysis,
CallPrinter,
Reentrancy
}

#[derive(Clone, Debug, Hash, PartialEq, Eq, Serialize, Deserialize, Copy)]
Expand Down Expand Up @@ -104,14 +106,24 @@ where
host: &mut FuzzHost<VS, I, S, SC>,
state: &mut S,
ret: &Bytes,
) {
}
) { }

unsafe fn before_execute(
&mut self,
interp: Option<&mut Interpreter>,
host: &mut FuzzHost<VS, I, S, SC>,
state: &mut S,
is_step: bool,
data: &mut Bytes,
evm_state: &mut EVMState,
) { }

unsafe fn on_insert(&mut self,
interp: Option<&mut Interpreter>,
host: &mut FuzzHost<VS, I, S, SC>,
state: &mut S,
bytecode: &mut Bytecode,
address: EVMAddress,
host: &mut FuzzHost<VS, I, S, SC>,
state: &mut S);
) {}
fn get_type(&self) -> MiddlewareType;
}
1 change: 1 addition & 0 deletions src/evm/middlewares/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,4 @@ pub mod coverage;
pub mod middleware;
pub mod sha3_bypass;
pub mod call_printer;
pub mod reentrancy;
Loading

0 comments on commit 3225ff7

Please sign in to comment.