Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

8 changes: 4 additions & 4 deletions tooling/ssa_fuzzer/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,23 +31,23 @@

2. Run fuzzer:
```
cargo +nightly fuzz run base_target --fuzz-dir ./fuzzer
cargo +nightly fuzz run acir_vs_brillig --fuzz-dir ./fuzzer
```

or in 5 threads
```
cargo +nightly fuzz run base_target --fuzz-dir ./fuzzer -- -jobs=5 -workers=5
cargo +nightly fuzz run acir_vs_brillig --fuzz-dir ./fuzzer -- -jobs=5 -workers=5
```

3. Triage crashes:
```
TRIAGE=FULL/FINAL cargo +nightly fuzz run base_target --fuzz-dir ./fuzzer PATH_TO_CRASH
TRIAGE=FULL/FINAL cargo +nightly fuzz run acir_vs_brillig --fuzz-dir ./fuzzer PATH_TO_CRASH
```
FULL mode will show all SSA passes, FINAL mode will show only the final SSA pass (After Dead Instruction Elimination (3)).

4. Minimize crashes:
```
cargo +nightly fuzz tmin base_target --fuzz-dir ./fuzzer PATH_TO_CRASH -runs=1000
cargo +nightly fuzz tmin acir_vs_brillig --fuzz-dir ./fuzzer PATH_TO_CRASH -runs=1000

Check warning on line 50 in tooling/ssa_fuzzer/README.md

View workflow job for this annotation

GitHub Actions / Code

Unknown word (tmin)
```


Expand Down Expand Up @@ -98,7 +98,7 @@
b1 b2
```
Inserts chosen instruction blocks into b1 and b2.
Then terminates b0 with jmp_if_else and switch context to b1. Condtion for jmp_if_else is the last defined boolean variable in the block.

Check warning on line 101 in tooling/ssa_fuzzer/README.md

View workflow job for this annotation

GitHub Actions / Code

Misspelled word (Condtion) Suggestions: (condition*)
```
b0
then ↙ ↘ else
Expand Down
15 changes: 11 additions & 4 deletions tooling/ssa_fuzzer/fuzzer/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ workspace = true
libfuzzer-sys = { version = "0.4.0", features = ["arbitrary-derive"] }
noirc_evaluator.workspace = true
noirc_driver.workspace = true
noirc_frontend.workspace = true
acvm.workspace = true
noir_ssa_executor.workspace = true
log.workspace = true
Expand All @@ -25,8 +26,8 @@ serde.workspace = true
serde_json.workspace = true
base64.workspace = true
sha1 = "0.10.6"
redis = "0.26"
lazy_static = "1.4"
redis = { version = "0.26"}
lazy_static = { version = "1.4" }
strum_macros = "0.24"
strum = "0.24"
sha2.workspace = true
Expand All @@ -37,12 +38,18 @@ k256 = "0.13.4"
path = "../"

[[bin]]
name = "fuzz_target"
path = "src/fuzz_target.rs"
name = "acir_vs_brillig"
path = "src/acir_vs_brillig.rs"
test = false
doc = false
bench = false

[[bin]]
name = "brillig"
path = "src/brillig.rs"
test = false
doc = false
bench = false

[[test]]
name = "test_fuzz_target"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,11 +13,17 @@ use fuzz_lib::{
use libfuzzer_sys::Corpus;
use mutations::mutate;
use noirc_driver::CompileOptions;
use noirc_evaluator::ssa::ir::function::RuntimeType;
use noirc_frontend::monomorphization::ast::InlineType as FrontendInlineType;
use rand::{SeedableRng, rngs::StdRng};
use sha1::{Digest, Sha1};
use utils::{push_fuzzer_output_to_redis_queue, redis};

const MAX_EXECUTION_TIME_TO_KEEP_IN_CORPUS: u64 = 3;
const INLINE_TYPE: FrontendInlineType = FrontendInlineType::Inline;
const ACIR_RUNTIME: RuntimeType = RuntimeType::Acir(INLINE_TYPE);
const BRILLIG_RUNTIME: RuntimeType = RuntimeType::Brillig(INLINE_TYPE);
const TARGET_RUNTIMES: [RuntimeType; 2] = [ACIR_RUNTIME, BRILLIG_RUNTIME];

libfuzzer_sys::fuzz_target!(|data: &[u8]| -> Corpus {
let _ = env_logger::try_init();
Expand Down Expand Up @@ -70,12 +76,11 @@ libfuzzer_sys::fuzz_target!(|data: &[u8]| -> Corpus {
.unwrap_or((FuzzerData::default(), 1337))
.0;
let start = std::time::Instant::now();
let fuzzer_output = fuzz_target(fuzzer_data, options);
let fuzzer_output = fuzz_target(fuzzer_data, TARGET_RUNTIMES.to_vec(), options);

// If REDIS_URL is set and generated program is executed
if redis::ensure_redis_connection() && fuzzer_output.is_some() {
if redis::ensure_redis_connection() {
// cargo-fuzz saves tests with name equal to sha1 of content
let fuzzer_output = fuzzer_output.unwrap();
let mut hasher = Sha1::new();
hasher.update(data);
let sha1_hash = hasher.finalize();
Expand Down
103 changes: 103 additions & 0 deletions tooling/ssa_fuzzer/fuzzer/src/brillig.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
#![no_main]

pub(crate) mod fuzz_lib;
mod mutations;
mod utils;

use bincode::serde::{borrow_decode_from_slice, encode_to_vec};
use fuzz_lib::{
fuzz_target_lib::fuzz_target,
fuzzer::FuzzerData,
options::{FuzzerCommandOptions, FuzzerMode, FuzzerOptions, InstructionOptions},
};
use libfuzzer_sys::Corpus;
use mutations::mutate;
use noirc_driver::CompileOptions;
use noirc_evaluator::ssa::ir::function::RuntimeType;
use noirc_frontend::monomorphization::ast::InlineType as FrontendInlineType;
use rand::{SeedableRng, rngs::StdRng};
use sha1::{Digest, Sha1};
use utils::{push_fuzzer_output_to_redis_queue, redis};

const MAX_EXECUTION_TIME_TO_KEEP_IN_CORPUS: u64 = 3;
const INLINE_TYPE: FrontendInlineType = FrontendInlineType::Inline;
const BRILLIG_RUNTIME: RuntimeType = RuntimeType::Brillig(INLINE_TYPE);
const TARGET_RUNTIMES: [RuntimeType; 1] = [BRILLIG_RUNTIME];

libfuzzer_sys::fuzz_target!(|data: &[u8]| -> Corpus {
let _ = env_logger::try_init();

let mut compile_options = CompileOptions::default();
if let Ok(triage_value) = std::env::var("TRIAGE") {
match triage_value.as_str() {
"FULL" => compile_options.show_ssa = true,
"FINAL" => {
compile_options.show_ssa_pass =
vec!["After Dead Instruction Elimination - ACIR".to_string()];
}
"FIRST_AND_FINAL" => {
compile_options.show_ssa_pass = vec![
"After Removing Unreachable Functions (1)".to_string(),
"After Dead Instruction Elimination - ACIR".to_string(),
];
}
_ => (),
}
}

// You can disable some instructions with bugs that are not fixed yet
let modes = vec![FuzzerMode::NonConstant];
let instruction_options = InstructionOptions {
array_get_enabled: false,
array_set_enabled: false,
ecdsa_secp256k1_enabled: false,
ecdsa_secp256r1_enabled: false,
..InstructionOptions::default()
};
let fuzzer_command_options =
FuzzerCommandOptions { loops_enabled: false, ..FuzzerCommandOptions::default() };
let options = FuzzerOptions {
compile_options,
instruction_options,
modes,
fuzzer_command_options,
..FuzzerOptions::default()
};
let fuzzer_data = borrow_decode_from_slice(data, bincode::config::legacy())
.unwrap_or((FuzzerData::default(), 1337))
.0;
let start = std::time::Instant::now();
let fuzzer_output = fuzz_target(fuzzer_data, TARGET_RUNTIMES.to_vec(), options);

// If REDIS_URL is set and generated program is executed
if redis::ensure_redis_connection() {
// cargo-fuzz saves tests with name equal to sha1 of content
let mut hasher = Sha1::new();
hasher.update(data);
let sha1_hash = hasher.finalize();
let test_id = format!("{sha1_hash:x}");
match push_fuzzer_output_to_redis_queue("fuzzer_output", test_id, fuzzer_output) {
Ok(json_str) => log::debug!("{json_str}"),
Err(e) => log::error!("Failed to push to Redis queue: {e}"),
}
}

if start.elapsed().as_secs() > MAX_EXECUTION_TIME_TO_KEEP_IN_CORPUS {
return Corpus::Reject;
}
Corpus::Keep
});

libfuzzer_sys::fuzz_mutator!(|data: &mut [u8], _size: usize, max_size: usize, seed: u32| {
let mut rng = StdRng::seed_from_u64(seed as u64);
let mut new_fuzzer_data: FuzzerData = borrow_decode_from_slice(data, bincode::config::legacy())
.unwrap_or((FuzzerData::default(), 1337))
.0;
mutate(&mut new_fuzzer_data, &mut rng);
let new_bytes = encode_to_vec(&new_fuzzer_data, bincode::config::legacy()).unwrap();
if new_bytes.len() > max_size {
return 0;
}
data[..new_bytes.len()].copy_from_slice(&new_bytes);
new_bytes.len()
});
Loading
Loading