Skip to content
This repository has been archived by the owner on Oct 22, 2023. It is now read-only.

Commit

Permalink
Add honggfuzz fuzzing
Browse files Browse the repository at this point in the history
  • Loading branch information
nhynes committed Oct 6, 2019
1 parent 06d5bc3 commit 01cb8ca
Show file tree
Hide file tree
Showing 12 changed files with 122 additions and 26 deletions.
4 changes: 4 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -60,3 +60,7 @@ cobertura.xml
# in case credentials are put in the repo for using
# inside the docker container
.git-credentials

fuzz/Cargo.lock
fuzz/artifacts
fuzz/hfuzz_*
2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -83,4 +83,4 @@ members = [
"gateway",
"genesis",
]
exclude = ["tests"]
exclude = ["tests", "fuzz"]
14 changes: 14 additions & 0 deletions fuzz/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
[package]
name = "fuzz"
version = "0.1.0"
authors = ["Oasis Labs <feedback@oasislabs.com>"]
edition = "2018"
publish = false

[dependencies]
# arbitrary = "0.2"
runtime-ethereum = { path = "..", features = ["test"] }
honggfuzz = "0.5"

[patch.crates-io]
ring = { git = "https://github.com/akash-fortanix/ring", branch = "sgx-target" }
18 changes: 18 additions & 0 deletions fuzz/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
# Runtime fuzzing infra

The runtime is fuzzed using [honggfuzz](https://github.com/google/honggfuzz) via [cargo hfuzz](https://crates.io/crates/honggfuzz).

Available fuzzing targets are in `src/bin/*.rs`.
Currently there are two targets:
* `create_contract`, which creates a new contract and, if that succeeds, calls it
* `tx`, which sends random transactions to random addresses

To begin fuzzing,

1. install the hfuzz [dependencies](https://github.com/rust-fuzz/honggfuzz-rs#dependencies)
2. `cargo install honggfuzz`
3. ensure that runtime is buildable (i.e. build or otherwise obtain Ekiden enclaves)
4. `./run.sh <target name>`

At the current time, both targets have been fuzzed for over 1m iterations with no crashes found.
While encouraging, don't let this stop you from trying!
8 changes: 8 additions & 0 deletions fuzz/run.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
repo_root=$(git rev-parse --show-toplevel)

export KM_ENCLAVE_PATH="$repo_root/.ekiden/target/x86_64-fortanix-unknown-sgx/debug/ekiden-keymanager-runtime.sgxs"
export RUSTFLAGS='-Ctarget-feature=+aes,+ssse3'

cd "$repo_root/fuzz"

cargo hfuzz run $1
21 changes: 21 additions & 0 deletions fuzz/src/bin/create_contract.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
fn main() {
let mut client = runtime_ethereum::test::Client::new();
loop {
honggfuzz::fuzz!(|params: (
Vec<u8>,
[u8; 32],
Option<u64>,
Option<bool>,
Vec<u8>,
[u8; 32]
)| {
let (code, balance, expiry, c10lity, call_data, call_value) = params;
let addr =
match client.create_contract_with_header(code, &balance.into(), expiry, c10lity) {
Ok((_hash, addr)) => addr,
Err(_) => return,
};
client.call(&addr, call_data, &call_value.into()).ok();
});
}
}
16 changes: 16 additions & 0 deletions fuzz/src/bin/tx.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
fn main() {
let mut client = runtime_ethereum::test::Client::new();
loop {
honggfuzz::fuzz!(|params: ([u8; 20], Vec<u8>, [u8; 32])| {
let (addr, data, value) = params;
client
.send(
Some(&addr.into()),
data,
&value.into(),
None, /* nonce */
)
.ok();
});
}
}
6 changes: 5 additions & 1 deletion src/block.rs
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,11 @@ impl EthereumBatchHandler {

impl BatchHandler for EthereumBatchHandler {
fn start_batch(&self, ctx: &mut TxnContext) {
let logger = get_logger("ethereum/block");
let logger = if cfg!(fuzzing) {
Logger::root(slog::Discard, slog::o!())
} else {
get_logger("ethereum/block")
};

info!(logger, "Computing new block"; "round" => ctx.header.round + 1);

Expand Down
16 changes: 8 additions & 8 deletions src/test/client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ use ethcore::{
};
use ethereum_types::{Address, H256, U256};
use ethkey::{KeyPair, Secret};

use failure::Fallible;
use io_context::Context as IoContext;
use keccak_hash::keccak;
use runtime_ethereum_api::ExecutionResult;
Expand Down Expand Up @@ -62,7 +62,7 @@ pub struct Client {
/// In-memory MKVS.
pub mkvs: Option<UrkelTree>,
/// Key manager client.
pub km_client: Arc<KeyManagerClient>,
pub km_client: Arc<dyn KeyManagerClient>,
/// Results.
pub results: HashMap<H256, ExecutionResult>,
}
Expand Down Expand Up @@ -210,13 +210,13 @@ impl Client {
balance: &U256,
expiry: Option<u64>,
confidentiality: Option<bool>,
) -> (H256, Address) {
) -> Fallible<(H256, Address)> {
let mut data = Self::make_header(expiry, confidentiality);
data.extend(code);
let (hash, address) = self
.send(None, data, balance, None)
.expect("deployment should succeed");
(hash, address.unwrap())
.map_err(failure::err_msg)?;
Ok((hash, address.unwrap()))
}

/// Returns the receipt for the given transaction hash.
Expand Down Expand Up @@ -252,12 +252,12 @@ impl Client {
}

/// Returns the return value of the contract's method.
pub fn call(&mut self, contract: &Address, data: Vec<u8>, value: &U256) -> Vec<u8> {
pub fn call(&mut self, contract: &Address, data: Vec<u8>, value: &U256) -> Fallible<Vec<u8>> {
let (hash, _) = self
.send(Some(contract), data, value, None)
.expect("call should succeed");
.map_err(failure::err_msg)?;
let result = self.result(hash);
result.output
Ok(result.output)
}

/// Sends a transaction onchain that updates the blockchain, analagous to the web3.js send().
Expand Down
12 changes: 9 additions & 3 deletions tests/create.rs
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,9 @@ fn create_and_set_storage() {
.encode_input(&[])
.unwrap();

let result = client.call(&deployed_addr, retrieve_a_data, &0.into());
let result = client
.call(&deployed_addr, retrieve_a_data, &0.into())
.unwrap();
U256::from(result.as_slice())
};

Expand All @@ -101,7 +103,9 @@ fn create_and_set_storage() {
.encode_input(&[])
.unwrap();

let result = client.call(&deployed_addr, retrieve_a_data, &0.into());
let result = client
.call(&deployed_addr, retrieve_a_data, &0.into())
.unwrap();
U256::from(result.as_slice())
};

Expand All @@ -119,7 +123,9 @@ fn create_and_set_storage() {
.encode_input(&[])
.unwrap();

let result = client.call(&deployed_addr, retrieve_a_data, &0.into());
let result = client
.call(&deployed_addr, retrieve_a_data, &0.into())
.unwrap();
(&ethabi::decode(
&[ethabi::ParamType::Array(Box::new(ethabi::ParamType::Uint(
256,
Expand Down
2 changes: 1 addition & 1 deletion tests/runtime_ethereum.rs
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,7 @@ fn test_solidity_x_contract_call() {
contract_b
))
.unwrap();
let output = client.call(&contract_a, data, &U256::zero());
let output = client.call(&contract_a, data, &U256::zero()).unwrap();

// expected output is 42
assert_eq!(
Expand Down
29 changes: 17 additions & 12 deletions tests/storage_expiry.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ fn get_counter<'a>(contract: &Address, client: &mut test::Client) -> U256 {
U256::from(
client
.call(contract, sighash_data, &U256::zero())
.unwrap()
.as_slice(),
)
}
Expand Down Expand Up @@ -55,12 +56,14 @@ fn test_invalid_expiry() {

// attempt to deploy counter contract with invalid expiry
let deploy_expiry = now - 1;
let (tx_hash, _) = client.create_contract_with_header(
contracts::counter::solidity_initcode(),
&U256::zero(),
Some(deploy_expiry),
None,
);
let (tx_hash, _) = client
.create_contract_with_header(
contracts::counter::solidity_initcode(),
&U256::zero(),
Some(deploy_expiry),
None,
)
.unwrap();

// check that deploy failed (0 status code)
let status = client.result(tx_hash).status_code;
Expand All @@ -77,12 +80,14 @@ fn test_expiry() {

// deploy counter contract with expiry
let duration = 31557600;
let (_, contract) = client.create_contract_with_header(
contracts::counter::solidity_initcode(),
&U256::zero(),
Some(deploy_time + duration),
None,
);
let (_, contract) = client
.create_contract_with_header(
contracts::counter::solidity_initcode(),
&U256::zero(),
Some(deploy_time + duration),
None,
)
.unwrap();

// check expiry
let expiry = client.storage_expiry(contract);
Expand Down

0 comments on commit 01cb8ca

Please sign in to comment.