Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

More flexibility when creating fuzzers at the cost of Fuzzers requiring Clone + Send. #36

Draft
wants to merge 2 commits into
base: dev
Choose a base branch
from
Draft
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
2 changes: 1 addition & 1 deletion examples/01_getpid/src/fuzzer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ use crate::constants;

const CR3: Cr3 = Cr3(constants::CR3);

#[derive(Default)]
#[derive(Default, Clone)]
pub struct Example1Fuzzer {
// Fuzzer specific data could go in here
}
Expand Down
2 changes: 1 addition & 1 deletion examples/02_libtiff/src/fuzzer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ use crate::constants;

const CR3: Cr3 = Cr3(constants::CR3);

#[derive(Default)]
#[derive(Default, Clone)]
pub struct Example02Fuzzer {
// Fuzzer specific data could go in here
}
Expand Down
2 changes: 1 addition & 1 deletion examples/03_ffmpeg_custom_mutator/src/fuzzer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ use snapchange::linux::{read_args, ReadArgs};
use snapchange::rng::Rng;
use snapchange::Execution;

#[derive(Default)]
#[derive(Default, Clone)]
pub struct Example03Fuzzer {
file_offset: usize,
}
Expand Down
2 changes: 1 addition & 1 deletion examples/04_syscall_fuzzer/src/fuzzer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@ const SHELLCODE: u64 = constants::SHELLCODE;
const SCRATCH: u64 = constants::SCRATCH;
const CR3: Cr3 = Cr3(constants::CR3);

#[derive(Default)]
#[derive(Default, Clone)]
pub struct Example04Fuzzer {
/// Offset to the next address to write shellcode
shellcode_offset: u64,
Expand Down
2 changes: 1 addition & 1 deletion examples/05_redqueen/src/fuzzer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ const CR3: Cr3 = Cr3(constants::CR3);

// src/fuzzer.rs

#[derive(Default)]
#[derive(Default, Clone)]
pub struct Example05Fuzzer;

impl Fuzzer for Example05Fuzzer {
Expand Down
2 changes: 1 addition & 1 deletion examples/06_custom_feedback/src/fuzzer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,7 @@ impl std::default::Default for WasdArray {
}
}

#[derive(Default)]
#[derive(Default, Clone)]
pub struct MazeFuzzer {
/// this is used for input scheduling - assigning weights to each input. The latest corpus entry
/// has the biggest weight, while the first one has the smallest. Essentially this one is a
Expand Down
2 changes: 1 addition & 1 deletion examples/07_libfuzzer/src/fuzzer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ use snapchange::fuzzvm::FuzzVm;

use crate::constants;

#[derive(Default)]
#[derive(Default, Clone)]
pub struct Example7Fuzzer {}

impl Fuzzer for Example7Fuzzer {
Expand Down
9 changes: 6 additions & 3 deletions src/commands/corpus_min.rs
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,9 @@ pub(crate) fn run<FUZZER: Fuzzer>(
let chunk_size: usize = args.chunk_size;
let mut minimizer = CorpusMinimizer::default();

// create new fuzzer
let mut fuzzer = FUZZER::new(&project_state);

//
for chunk in (0..paths.len()).step_by(chunk_size) {
let ending_index = (chunk + chunk_size).min(paths.len());
Expand Down Expand Up @@ -144,11 +147,13 @@ pub(crate) fn run<FUZZER: Fuzzer>(
let timeout = args.timeout.clone();
let clean_snapshot = clean_snapshot.clone();
let project_dir = project_state.path.clone();
let fuzzer = fuzzer.clone();

// Start executing on this core
let t = std::thread::spawn(move || {
start_core::<FUZZER>(
CoreId { id: core_id },
fuzzer,
&vm,
&vbcpu,
&cpuids,
Expand Down Expand Up @@ -292,6 +297,7 @@ pub(crate) fn run<FUZZER: Fuzzer>(
/// Thread worker used to gather coverage for a specific input
pub(crate) fn start_core<FUZZER: Fuzzer>(
core_id: CoreId,
mut fuzzer: FUZZER,
vm: &VmFd,
vbcpu: &VbCpu,
cpuid: &CpuId,
Expand All @@ -317,9 +323,6 @@ pub(crate) fn start_core<FUZZER: Fuzzer>(
// Set the core affinity for this core
core_affinity::set_for_current(core_id);

// Use the current fuzzer
let mut fuzzer = FUZZER::default();

// Sanity check that the given fuzzer matches the snapshot
ensure!(
FUZZER::START_ADDRESS == vbcpu.rip,
Expand Down
2 changes: 1 addition & 1 deletion src/commands/coverage.rs
Original file line number Diff line number Diff line change
Expand Up @@ -109,7 +109,7 @@ pub(crate) fn start_core<FUZZER: Fuzzer>(
..
} = project_state;
// Use the current fuzzer
let mut fuzzer = FUZZER::default();
let mut fuzzer = FUZZER::new(project_state);

// Sanity check that the given fuzzer matches the snapshot
ensure!(
Expand Down
10 changes: 7 additions & 3 deletions src/commands/find_input.rs
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ use crate::{Cr3, Execution, ResetBreakpointType, VbCpu, VirtAddr};
#[allow(clippy::needless_pass_by_value)]
fn start_core<FUZZER: Fuzzer>(
core_id: CoreId,
mut fuzzer: FUZZER,
vm: &VmFd,
vbcpu: &VbCpu,
cpuid: &CpuId,
Expand All @@ -53,9 +54,6 @@ fn start_core<FUZZER: Fuzzer>(
// Set the core affinity for this core
core_affinity::set_for_current(core_id);

// Create a default fuzzer for single shot, tracing execution with the given input
let mut fuzzer = FUZZER::default();

// Sanity check that the given fuzzer matches the snapshot
ensure!(
FUZZER::START_ADDRESS == vbcpu.rip,
Expand Down Expand Up @@ -239,6 +237,9 @@ pub(crate) fn run<FUZZER: Fuzzer>(
let next_file_index = Arc::new(AtomicUsize::new(0));
let finished = Arc::new(AtomicBool::new(false));

// create new fuzzer
let mut fuzzer = FUZZER::new(&project_state);

for id in 1..=cores {
let files = files.clone();
let physmem_file_fd = physmem_file.as_raw_fd();
Expand All @@ -263,10 +264,13 @@ pub(crate) fn run<FUZZER: Fuzzer>(

let clean_snapshot = clean_snapshot.clone();

let fuzzer = fuzzer.clone();

// Start executing on this core
let t = std::thread::spawn(move || {
start_core::<FUZZER>(
core_id,
fuzzer,
&vm,
&vbcpu,
&cpuids,
Expand Down
94 changes: 62 additions & 32 deletions src/commands/fuzz.rs
Original file line number Diff line number Diff line change
Expand Up @@ -107,41 +107,37 @@ pub(crate) fn run<FUZZER: Fuzzer + 'static>(

log::warn!("Starting all {} worker threads", cores);

let mut fuzzer = FUZZER::new(&project_state);

// Read the input corpus from the given input directory
let mut input_corpus: Vec<Arc<InputWithMetadata<FUZZER::Input>>> = Vec::new();

// Use the user given input directory or default to <PROJECT_DIR>/input
let input_dir = if let Some(input_dir) = &args.input_dir {
input_dir.clone()
} else if project_state.path.join("current_corpus").exists() {
// If no input dir was given, and the current corpus exists, use the old corpus
project_state.path.join("current_corpus")
} else if project_state.path.join("input").exists() {
// If no given input or current corpus, use "input" directory
project_state.path.join("input")
} else {
// Default to the standard current_corpus directory
project_state.path.join("current_corpus")
project_state.path.join("input")
};

// Get the corpus directory
let mut corpus_dir = project_state.path.clone();
corpus_dir.push("current_corpus");
if !corpus_dir.exists() {
std::fs::create_dir(&corpus_dir).context("Failed to create crash dir")?;
std::fs::create_dir(&corpus_dir).context("Failed to create corpus dir")?;
}

let num_files = input_dir.read_dir()?.count();
let num_corpus = corpus_dir.read_dir()?.count();

// Give some statistics on reading the initial corpus
let mut start = std::time::Instant::now();
let mut count = 0_u32;
let mut total_loaded = 0_u32;
if input_dir.exists() {
let num_files = input_dir.read_dir()?.count();
for (i, file) in input_dir.read_dir()?.enumerate() {
if start.elapsed() >= std::time::Duration::from_millis(1000) {
let left = num_files - i;
println!(
"{i:9} / {num_files:9} | Reading corpus {:8.2} files/sec | {:6.2} seconds left",
"{i:9} / {num_files:9} | Reading seeds {:8.2} files/sec | {:6.2} seconds left",
count as f64 / start.elapsed().as_secs_f64(),
left as f64 / (count as f64 / start.elapsed().as_secs_f64()),
);
Expand All @@ -158,29 +154,58 @@ pub(crate) fn run<FUZZER: Fuzzer + 'static>(
log::debug!("Ignoring directory found in input dir: {:?}", filepath);
continue;
}

// Add the input to the input corpus
let input = fuzzer.load_seed_input(&filepath, &project_state.path)?;

let input = Arc::new(input);
input_corpus.push(input);

total_loaded += 1;
}
} else {
log::info!("no seed input directory found: {}", input_dir.display());
}

start = std::time::Instant::now();
count = 0_u32;
if num_corpus > 0 {
for (i, file) in corpus_dir.read_dir()?.enumerate() {
if start.elapsed() >= std::time::Duration::from_millis(1000) {
let left = num_corpus - i;
println!(
"{i:9} / {num_corpus:9} | Reading current corpus {:8.2} files/sec | {:6.2} seconds left",
count as f64 / start.elapsed().as_secs_f64(),
left as f64 / (count as f64 / start.elapsed().as_secs_f64()),
);
start = std::time::Instant::now();
count = 0;
}

count += 1;

let filepath = file?.path();

// Ignore directories if they exist
if filepath.is_dir() {
log::debug!("Ignoring directory found in corpus dir: {:?}", filepath);
continue;
}

// Add the input to the input corpus
let input = FUZZER::Input::from_bytes(&std::fs::read(filepath)?)?;
let metadata_path = project_state
.path
.join("metadata")
.join(crate::utils::hexdigest(&input));

let input = if let Ok(data) = std::fs::read_to_string(metadata_path) {
let metadata = serde_json::from_str(&data)?;
InputWithMetadata {
input,
metadata: RwLock::new(metadata),
}
} else {
InputWithMetadata::from_input(input)
};
let input = InputWithMetadata::<FUZZER::Input>::from_path(&filepath, &project_state.path)?;

let input = Arc::new(input);
input_corpus.push(input);

total_loaded += 1;
}
} else {
log::warn!("No input directory found: {input_dir:?}, starting with an empty corpus!");
log::info!("starting with an empty corpus");
}

if total_loaded == 0 {
log::warn!("No inputs loaded - starting with an empty corpus!");
}

// Initialize the dictionary
Expand Down Expand Up @@ -357,6 +382,9 @@ pub(crate) fn run<FUZZER: Fuzzer + 'static>(
start.elapsed()
);


let fuzzer = fuzzer;

const BETWEEN_WAIT_FOR_MILLIES: u64 = 100;

// Create a thread for each active CPU core.
Expand Down Expand Up @@ -445,10 +473,13 @@ pub(crate) fn run<FUZZER: Fuzzer + 'static>(
let stop_after_first_crash = args.stop_after_first_crash;

// Start executing on this core
let fuzzer = fuzzer.clone();
let thread = std::thread::spawn(move || -> Result<()> {
let result = std::panic::catch_unwind(|| -> Result<()> {
// let mut wrapper = (&mut fuzzer);
let result = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| -> Result<()> {
start_core::<FUZZER>(
core_id,
fuzzer,
&vm,
&project_state.vbcpu,
&cpuids,
Expand All @@ -469,7 +500,7 @@ pub(crate) fn run<FUZZER: Fuzzer + 'static>(
#[cfg(feature = "redqueen")]
redqueen_breakpoints,
)
});
}));

// Ensure this thread is signalling it is not alive
core_stats.lock().unwrap().alive = false;
Expand Down Expand Up @@ -675,6 +706,7 @@ pub(crate) fn run<FUZZER: Fuzzer + 'static>(
/// Thread worker used to fuzz the given [`VbCpu`] state with the given physical memory.
fn start_core<FUZZER: Fuzzer>(
core_id: CoreId,
mut fuzzer: FUZZER,
vm: &VmFd,
vbcpu: &VbCpu,
cpuid: &CpuId,
Expand Down Expand Up @@ -721,8 +753,6 @@ fn start_core<FUZZER: Fuzzer>(
// Unblock SIGALRM to enable this thread to handle SIGALRM
unblock_sigalrm()?;

let mut fuzzer = FUZZER::default();

// RNG for this core used for mutation of inputs
let mut rng = Rng::new();

Expand Down
7 changes: 4 additions & 3 deletions src/commands/minimize.rs
Original file line number Diff line number Diff line change
Expand Up @@ -59,10 +59,11 @@ fn start_core<FUZZER: Fuzzer>(
max_iterations: u32,
config: Config,
min_params: MinimizerConfig,
project_dir: &PathBuf,
project_state: &ProjectState,
) -> Result<()> {
let project_dir = &project_state.path;
// Use the current fuzzer
let mut fuzzer = FUZZER::default();
let mut fuzzer = FUZZER::new(project_state);

// Sanity check that the given fuzzer matches the snapshot
ensure!(
Expand Down Expand Up @@ -467,7 +468,7 @@ pub(crate) fn run<FUZZER: Fuzzer>(
args.iterations_per_stage,
project_state.config.clone(),
minparams,
&project_state.path,
&project_state,
)?;

// Success
Expand Down
2 changes: 1 addition & 1 deletion src/commands/redqueen.rs
Original file line number Diff line number Diff line change
Expand Up @@ -117,7 +117,7 @@ pub(crate) fn start_core<FUZZER: Fuzzer>(
} = project_state;

// Use the current fuzzer
let mut fuzzer = FUZZER::default();
let mut fuzzer = FUZZER::new(project_state);

// Sanity check that the given fuzzer matches the snapshot
ensure!(
Expand Down
2 changes: 1 addition & 1 deletion src/commands/trace.rs
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ fn start_core<FUZZER: Fuzzer>(
core_affinity::set_for_current(core_id);

// Create a default fuzzer for single shot, tracing execution with the given input
let mut fuzzer = FUZZER::default();
let mut fuzzer = FUZZER::new(project_state);

log::info!("Fuzzer: {:#x} RIP: {:#x}", FUZZER::START_ADDRESS, vbcpu.rip);

Expand Down
Loading