Skip to content

Commit

Permalink
improved crash logging and status messages, code cleanup
Browse files Browse the repository at this point in the history
  • Loading branch information
matt24smith committed Jan 20, 2023
1 parent 9ab82f0 commit 808f3f3
Showing 1 changed file with 89 additions and 82 deletions.
171 changes: 89 additions & 82 deletions src/main.rs
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
use std::fs::remove_file;
use std::time::{Duration, Instant};

use ecfuzz::corpus::{Corpus, CorpusInput};
use ecfuzz::execute::{
check_report_coverage, count_branch_total, exec_target, index_target_report, Config, Exec,
};
use std::collections::HashSet;
use std::fs::{create_dir, remove_file};
use std::path::PathBuf;
use std::process::id;
//use std::time::{Duration, Instant};
use std::time::Instant;

use ecfuzz::corpus::Corpus;
use ecfuzz::execute::{count_branch_total, Config, Exec, ExecResult};
use ecfuzz::mutator::Mutation;

/// main loop:
Expand All @@ -18,63 +20,79 @@ pub fn _main_loop(
) -> Result<(), Box<dyn std::error::Error>> {
// crashlog
let mut crash_corpus = Corpus::new();
let branch_count = count_branch_total(cfg, profdata)?;
let outdir = PathBuf::from("output/mutations");
let crashdir = PathBuf::from("output/crashes");
let profraw = format!("output/{}.profraw", id());
let profdata = format!("output/{}.profdata", id());
create_dir(&outdir).unwrap_or_default();

// start some timers
let mut cmd_timer: Instant;
let mut exec_time = Duration::new(0, 0);
let mut profile_time = Duration::new(0, 0);
let mut cov_time = Duration::new(0, 0);
let mut timer_start = Instant::now();

let branch_count = count_branch_total(cfg, profdata)?;

for i in 0..cfg.iterations + 1 {
// create a string name for this iteration
let i_padded = format!("{:0>8}", i);
let profraw = format!("output/{}_id{:?}.profraw", i_padded, std::process::id(),);
let profdata = format!("output/{}_id{:?}.profdata", i_padded, std::process::id(),);

// mutate the input
let idx = mutation.hashfunc() % cov_corpus.inputs.len();
mutation.data = cov_corpus.inputs[idx].data.clone();
mutation.mutate();

cmd_timer = Instant::now();
let caused_crash = exec_target(cfg, &profraw, &mutation.data)?;
exec_time += cmd_timer.elapsed();

if caused_crash {
crash_corpus.add(CorpusInput {
data: mutation.data.clone(),
coverage: cov_corpus.inputs[i % cov_corpus.inputs.len()]
.coverage
.clone(),
});
//remove_file(&profraw)?;
//continue;
} else {
// index the raw profile data
cmd_timer = Instant::now();
index_target_report(cfg, &profraw, &profdata).unwrap();
profile_time += cmd_timer.elapsed();

// generate JSON report from profile data
cmd_timer = Instant::now();
let coverage = check_report_coverage(cfg, &profdata).expect("getting report coverage");
cov_time += cmd_timer.elapsed();

// file cleanup
remove_file(&profraw).expect("removing raw profile data");
remove_file(&profdata).expect("removing coverage profile data");

// if the report contains new coverate,
// add both to the corpus as CorpusInput
if !cov_corpus.total_coverage.is_superset(&coverage) {
let corpus_entry = CorpusInput {
data: mutation.data.clone(),
coverage,
};
cov_corpus.add_and_distill_corpus(corpus_entry);
let (corpus_entry, result) = Exec::trial(
cfg,
&profraw,
&profdata,
&mutation.data,
cov_corpus.inputs[idx].file_stem.clone(),
cov_corpus.inputs[idx].file_ext.clone(),
cov_corpus.inputs[idx].lifetime,
);

// if the report contains new coverage, add to corpus as CorpusInput
match result {
ExecResult::Ok(_output) => {
if !cov_corpus
.total_coverage
.is_superset(&corpus_entry.coverage)
{
corpus_entry.serialize(&outdir).unwrap();
cov_corpus.add_and_distill_corpus(corpus_entry.clone());
println!(
"\n\x1b[32mNew coverage hit!\x1b[0m updating inputs... {}",
cov_corpus
);
} else {
// file cleanup
remove_file(&profraw).expect("removing raw profile data");
remove_file(&profdata).expect("removing coverage profile data");
}
}
ExecResult::Err(output) => {
if !crash_corpus.total_coverage.is_superset(
&corpus_entry
.coverage
.union(&cov_corpus.inputs[idx].coverage)
.map(|i| i.to_owned())
.collect::<HashSet<u64>>(),
) {
crash_corpus.add_and_distill_corpus(corpus_entry);
eprintln!(
"\n{}\x1b[31mNew crash!\x1b[0m crash log:{}",
String::from_utf8_lossy(&output.stderr),
&crash_corpus
);
} else {
// file cleanup
remove_file(&profraw).expect("removing raw profile data");
remove_file(&profdata).expect("removing coverage profile data");
eprintln!(
"\n{}\x1b[91mKnown crash!\x1b[0m {}",
String::from_utf8_lossy(&output.stderr),
crash_corpus
);
if corpus_entry.coverage.is_empty() {
eprintln!(
"Error: could not read coverage from crash! See output from sanitizer"
);
}
}
}
}

Expand All @@ -85,27 +103,20 @@ pub fn _main_loop(
max = mutation.data.len();
}
println!(
//"branch hits: {:>2}/{} exec/s: {:.2} ratio: {:.2}/{:.2}/{:.2} inputs: {} i: {:<4} {}",
"hits: {:>2}/{} exec/s: {:.2} inputs: {} i: {:<4} {}",
"coverage: {:>2}/{} exec/s: {:.2} inputs: {} new crashes: {} i: {:<4} {}",
cov_corpus.total_coverage.len(),
branch_count,
cfg.iter_check as f32 / (timer_start.elapsed().as_millis() as f32 / 1000.0),
//exec_time.as_millis() as f32 / 1000.0,
//profile_time.as_millis() as f32 / 1000.0,
//cov_time.as_millis() as f32 / 1000.0,
cov_corpus.inputs.len(),
crash_corpus.inputs.len(),
i,
String::from_utf8_lossy(&mutation.data[0..max]),
);
exec_time = Duration::new(0, 0);
profile_time = Duration::new(0, 0);
cov_time = Duration::new(0, 0);
timer_start = Instant::now();
}
}

cov_corpus.save(&cfg.corpus_dir, "mutations", false)?;
crash_corpus.save(&cfg.corpus_dir, "crashes", false)?;
cov_corpus.save(outdir).unwrap();
crash_corpus.save(crashdir).unwrap();

Ok(())
}
Expand All @@ -114,29 +125,25 @@ pub fn _main_loop(
pub fn main() -> Result<(), Box<dyn std::error::Error>> {
// configure paths and initial state
let cfg = Config::parse_args()?;
let mut engine = Mutation::with_seed(cfg.dict_path.clone(), cfg.seed.clone());
let mut engine = Mutation::with_seed(cfg.dict_path.clone(), cfg.seed.clone(), cfg.multiplier);
let mut cov_corpus = Corpus::new();

// compile target
Exec::initialize(&cfg)?;

// initial profile paths
let rawprof = "init.profraw";
let profdata = "init.profdata";

// check code coverage for seeded inputs
// coverage profile paths
println!("seeding...");
for input in &cfg.seed_corpus {
assert!(!input.is_empty());
let _caused_crash = exec_target(&cfg, rawprof, input)?;
index_target_report(&cfg, rawprof, profdata)?;

let corpus_entry = CorpusInput {
data: input.to_vec(),
coverage: check_report_coverage(&cfg, profdata)?,
};
cov_corpus.add(corpus_entry);
//let profraw = &format!("output/{}.profraw", id());
let profdata = &format!("output/{}.profdata", id());

// check code coverage for initial corpus inputs
for filepath in &cfg.corpus_files {
cov_corpus.load(&cfg, filepath, false);
}
for filepath in &cfg.corpus_dirs {
cov_corpus.load(&cfg, filepath, true);
}

let branch_count = count_branch_total(&cfg, profdata)?;

println!(
Expand Down

0 comments on commit 808f3f3

Please sign in to comment.