diff --git a/Cargo.toml b/Cargo.toml index 2f8442c..0a440ba 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -36,7 +36,7 @@ rayon = "1.5.3" dashmap = "5.3.4" #crossbeam = "0.8.1" psutil = "3.2.2" -bitflags = "2.2.1" +bitflags = {version ="2.2.1", features = ["serde"] } #[lib] #proc-macro = true diff --git a/README.md b/README.md index fa03107..f3a28c5 100644 --- a/README.md +++ b/README.md @@ -15,7 +15,7 @@ wget https://github.com/protocolbuffers/protobuf/releases/download/v21.5/protoc- ## Run Basic run (for example, run on 20 cores): ``` -taskset -c 0-19 cargo run --release -- -c "your/command @@" -i input_corpus --core 20 +taskset -c 0-19 cargo run --release -- -c "your/command @@" -i input_corpus -o output --core 20 ``` Check the usage: diff --git a/src/datatype.rs b/src/datatype.rs index d2584d2..7a0156d 100644 --- a/src/datatype.rs +++ b/src/datatype.rs @@ -1,9 +1,10 @@ use crate::mutator::afl_mutator::DeterministicMutationPass; +use serde::{Deserialize, Serialize}; use std::sync::atomic::{AtomicU32, Ordering}; pub type TestCaseID = u32; -#[derive(Default, Clone, Debug)] +#[derive(Default, Clone, Debug, Serialize, Deserialize)] pub struct TestCaseMetaInfo { #[allow(dead_code)] done_pass: DeterministicMutationPass, // All the finished determinsitic passes. @@ -28,7 +29,7 @@ impl TestCaseMetaInfo { } } -#[derive(Default, Clone, Debug)] +#[derive(Default, Clone, Debug, Serialize, Deserialize)] pub struct TestCase { id: TestCaseID, // The id of the test_case itself or the id of parent. Since we won't use both of them at the same time, we reuse the same slot. mutator_id: u32, diff --git a/src/feedback/bitmap_collector.rs b/src/feedback/bitmap_collector.rs index 5abe1e2..d12f8c5 100644 --- a/src/feedback/bitmap_collector.rs +++ b/src/feedback/bitmap_collector.rs @@ -74,6 +74,7 @@ impl FeedbackCollector for BitmapCollector { let mut crash_counter: HashMap = HashMap::new(); let mut interesting_test_cases = Vec::default(); let mut crash_test_cases = Vec::default(); + let mut crashes_vec = Vec::new(); let mut total_timeout: u64 = 0; let mut total_crash: u64 = 0; @@ -100,6 +101,9 @@ impl FeedbackCollector for BitmapCollector { } ExecutionStatus::Crash => { assert!(feedback.contain_test_case()); + let test_case_str = + serde_json::to_string(feedback.borrow_test_case().unwrap()).unwrap(); + crashes_vec.push(test_case_str); crash_test_cases.push(feedback); *crash_counter.entry(mutation_info).or_insert(1) += 1; total_crash += 1; @@ -113,7 +117,8 @@ impl FeedbackCollector for BitmapCollector { // generate monitor data if any if total_crash != 0 { - self.monitor_data.push(json!({ "crash": total_crash })); + self.monitor_data + .push(json!({ "crash": total_crash, "testcases": crashes_vec })); } if total_timeout != 0 { self.monitor_data.push(json!({ "timeout": total_timeout })); diff --git a/src/lib.rs b/src/lib.rs index 7b82dab..70f84e4 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -965,10 +965,7 @@ pub async fn run_fuzzer_local_async_lock_free_mode(fuzzer: FuzzerConfig) { let closure = move || { // TODO: Fix the testcase minimizer - new_queue_frontend::QueueManagerWorker::new_with_seeds( - Some(init_seeds.clone()), - None, - ) + new_queue_frontend::QueueManagerWorker::new_with_seeds(Some(init_seeds.clone()), None) }; let mut queue_manager_frontend = new_queue_frontend::QueueManagerFrontend::new(Box::new(closure)); diff --git a/src/monitor.rs b/src/monitor.rs index cb1b62f..556a21e 100644 --- a/src/monitor.rs +++ b/src/monitor.rs @@ -5,6 +5,11 @@ use std::net::SocketAddr; use std::net::{IpAddr, Ipv4Addr}; use std::sync::{Arc, Mutex, RwLock}; +use crate::datatype::TestCase; +use crate::executor::ExecutionStatus; + +use self::output_writer::OutputWriter; + pub mod output_writer; pub mod rpc; pub mod stats; @@ -48,6 +53,35 @@ pub fn get_worker_num() -> u32 { WORKER_NUM } +fn dump_json_testcases( + output_writer: &OutputWriter, + stats: &serde_json::Value, + testcase_type: ExecutionStatus, +) { + let test_case_vec = stats.get("testcases").unwrap().as_array().unwrap(); + for test_case_val in test_case_vec { + let test_case_str = test_case_val.as_str().unwrap(); + let mut test_case: TestCase = serde_json::from_str(test_case_str).unwrap(); + match testcase_type { + ExecutionStatus::Crash => { + test_case.gen_id(); + match output_writer.save_crash(&test_case) { + Ok(_v) => {} + Err(_e) => println!("Cannot write crash file"), + } + } + ExecutionStatus::Interesting => { + output_writer.save_queue(&test_case).unwrap(); + } + ExecutionStatus::Timeout => { + test_case.gen_id(); + output_writer.save_hang(&test_case).unwrap(); + } + _ => {} + } + } +} + impl SimpleMonitor { fn default() -> Self { SimpleMonitor { @@ -112,10 +146,12 @@ impl Monitor for SimpleMonitor { // TODO: Receive crash/hang/interesting test cases for saving in disk. fn receive_statistics(&self, stats: serde_json::Value) { + let mut testcase_type = ExecutionStatus::Ok; if let Some(v) = stats.get("exec") { self.fuzzer_info.add_exec(v.as_u64().unwrap()); } else if let Some(v) = stats.get("crash") { self.fuzzer_info.add_crash(v.as_u64().unwrap()); + testcase_type = ExecutionStatus::Crash; } else if let Some(v) = stats.get("timeout") { self.fuzzer_info.add_timeout_exec(v.as_u64().unwrap()); } else if let Some(v) = stats.get("interesting_test_case") { @@ -123,6 +159,13 @@ impl Monitor for SimpleMonitor { } else { unreachable!(); } + if self.fuzzer_info.get_output_writer().is_some() && stats.get("testcases").is_some() { + dump_json_testcases( + self.fuzzer_info.get_output_writer().unwrap(), + &stats, + testcase_type, + ) + } } fn show_statistics(&self) { diff --git a/src/monitor/stats.rs b/src/monitor/stats.rs index b567271..fd36a09 100644 --- a/src/monitor/stats.rs +++ b/src/monitor/stats.rs @@ -90,6 +90,14 @@ impl FuzzerInfo { self.start_time = SystemTime::now(); } + pub fn get_output_writer(&self) -> Option<&OutputWriter> { + if self.output_writer.is_some() { + Some(self.output_writer.as_ref().unwrap()) + } else { + None + } + } + fn simple_calculate(&self) { let elapsed_time = (self.start_time.elapsed().unwrap().as_millis() as u64).max(1); let last_print_time = self.last_print_time.swap(elapsed_time, Ordering::Relaxed); diff --git a/src/mutator/afl_mutator.rs b/src/mutator/afl_mutator.rs index caa1ec4..36cc603 100644 --- a/src/mutator/afl_mutator.rs +++ b/src/mutator/afl_mutator.rs @@ -8,6 +8,7 @@ use rand::seq::SliceRandom; use rand::Rng; use rand::SeedableRng; use rand_distr::WeightedAliasIndex; +use serde::{Deserialize, Serialize}; //use super::MutatorFunc; use core::mem::size_of; @@ -85,7 +86,7 @@ pub struct BitFlipMutator { bitflags! { //TODO: Each determinitic pass should have a flag. - #[derive(Default, Clone, Debug, Copy)] + #[derive(Default, Clone, Debug, Copy, Serialize, Deserialize)] pub struct DeterministicMutationPass: u32 { const BITFLIP1 = 0b1; const BITFLIP2 = 0b10;