diff --git a/afl/src/corpus/mod.rs b/afl/src/corpus/mod.rs index 92d047adf4..c2688e00fc 100644 --- a/afl/src/corpus/mod.rs +++ b/afl/src/corpus/mod.rs @@ -149,6 +149,12 @@ where I: Input, R: Rand, { + /// Returns the number of elements + #[inline] + fn count(&self) -> usize { + self.entries().len() + } + /// Gets the next entry #[inline] fn next(&mut self, rand: &mut R) -> Result<(&RefCell>, usize), AflError> { diff --git a/afl/src/engines/mod.rs b/afl/src/engines/mod.rs index 791e2c7156..b17c9e05a9 100644 --- a/afl/src/engines/mod.rs +++ b/afl/src/engines/mod.rs @@ -3,18 +3,24 @@ use core::fmt::Debug; use core::marker::PhantomData; use serde::{Deserialize, Serialize}; +use std::{ + fs, + path::{Path, PathBuf}, +}; use crate::corpus::{Corpus, Testcase}; use crate::events::EventManager; use crate::executors::{Executor, ExecutorsTuple, HasObservers}; use crate::feedbacks::FeedbacksTuple; use crate::generators::Generator; +use crate::inputs::bytes::BytesInput; use crate::inputs::Input; use crate::observers::ObserversTuple; use crate::serde_anymap::{SerdeAny, SerdeAnyMap}; use crate::stages::StagesTuple; use crate::tuples::{tuple_list, tuple_list_type}; use crate::utils::{current_milliseconds, Rand}; + use crate::AflError; pub trait StateMetadata: Debug { @@ -44,6 +50,81 @@ where phantom: PhantomData<(I, R, OT)>, } +impl State +where + R: Rand, + FT: FeedbacksTuple, + OT: ObserversTuple, +{ + pub fn load_from_directory( + &mut self, + corpus: &mut C, + generator: &mut G, + engine: &mut Engine, + manager: &mut EM, + in_dir: &Path, + ) -> Result<(), AflError> + where + G: Generator, + C: Corpus, + E: Executor + HasObservers, + ET: ExecutorsTuple, + EM: EventManager, + { + for entry in fs::read_dir(in_dir)? { + let entry = entry?; + let path = entry.path(); + let attributes = fs::metadata(&path); + + if !attributes.is_ok() { + continue; + } + + let attr = attributes?; + + if attr.is_file() && attr.len() > 0 { + println!("Loading file {:?} ...", &path); + let bytes = std::fs::read(&path)?; + let input = BytesInput::new(bytes); + let fitness = self.evaluate_input(&input, engine.executor_mut())?; + if self.add_if_interesting(corpus, input, fitness)?.is_none() { + println!("File {:?} was not interesting, skipped.", &path); + } + } else if attr.is_dir() { + self.load_from_directory(corpus, generator, engine, manager, &path)?; + } + } + + Ok(()) + } + + pub fn load_initial_inputs( + &mut self, + corpus: &mut C, + generator: &mut G, + engine: &mut Engine, + manager: &mut EM, + in_dirs: &[PathBuf], + ) -> Result<(), AflError> + where + G: Generator, + C: Corpus, + E: Executor + HasObservers, + ET: ExecutorsTuple, + EM: EventManager, + { + for in_dir in in_dirs { + self.load_from_directory(corpus, generator, engine, manager, in_dir)?; + } + manager.log( + 0, + format!("Loaded {} initial testcases.", corpus.count()), // get corpus count + )?; + manager.process(self, corpus)?; + Ok(()) + } +} + impl State where I: Input, diff --git a/afl/src/events/mod.rs b/afl/src/events/mod.rs index 86bd20f63d..163b6d9670 100644 --- a/afl/src/events/mod.rs +++ b/afl/src/events/mod.rs @@ -742,7 +742,7 @@ where /// Else, it will act as client. pub fn new_on_port_std(stats: ST) -> Result { Ok(Self { - llmp: llmp::LlmpConnection::on_port(port)?, + llmp: llmp::LlmpConnection::on_port(1337)?, stats: stats, phantom: PhantomData, }) diff --git a/fuzzers/libfuzzer/Cargo.toml b/fuzzers/libfuzzer/Cargo.toml index f31f776145..f8af052585 100644 --- a/fuzzers/libfuzzer/Cargo.toml +++ b/fuzzers/libfuzzer/Cargo.toml @@ -17,6 +17,7 @@ opt-level = 3 debug = true [dependencies] +clap = "2.32.0" afl = { path = "../../afl/" } [lib] diff --git a/fuzzers/libfuzzer/runtime/rt.c b/fuzzers/libfuzzer/runtime/rt.c index 1fa6ef9755..43ccccc2f7 100644 --- a/fuzzers/libfuzzer/runtime/rt.c +++ b/fuzzers/libfuzzer/runtime/rt.c @@ -1,7 +1,12 @@ +#include #include #define MAP_SIZE 65536 +int orig_argc; +char **orig_argv; +char **orig_envp; + uint8_t __lafl_dummy_map[MAP_SIZE]; uint8_t *__lafl_edges_map = __lafl_dummy_map; @@ -119,15 +124,29 @@ void __sanitizer_cov_trace_switch(uint64_t val, uint64_t *cases) { } -__attribute__((weak)) int LLVMFuzzerInitialize(int *argc, char ***argv); + static void afl_libfuzzer_copy_args(int argc, char** argv, char** envp) { + orig_argc = argc; + orig_argv = argv; + orig_envp = envp; +} + +__attribute__((section(".init_array"))) void (* p_afl_libfuzzer_copy_args)(int,char*[],char*[]) = &afl_libfuzzer_copy_args; + +__attribute__((weak)) int LLVMFuzzerInitialize(int *argc, char ***argv); void afl_libfuzzer_main(); +int afl_libfuzzer_init() { + + if (LLVMFuzzerInitialize) + return LLVMFuzzerInitialize(&orig_argc, &orig_argv); + else + return 0; + +} + int main(int argc, char** argv) { - if (LLVMFuzzerInitialize) - LLVMFuzzerInitialize(&argc, &argv); - afl_libfuzzer_main(); return 0; diff --git a/fuzzers/libfuzzer/src/lib.rs b/fuzzers/libfuzzer/src/lib.rs index ff80a8f9e6..56cd01cd32 100644 --- a/fuzzers/libfuzzer/src/lib.rs +++ b/fuzzers/libfuzzer/src/lib.rs @@ -1,7 +1,14 @@ #![cfg_attr(not(feature = "std"), no_std)] +#[macro_use] +extern crate clap; extern crate alloc; +use clap::{App, Arg}; +use std::env; +use std::path::PathBuf; + +use afl::corpus::Corpus; use afl::corpus::InMemoryCorpus; use afl::engines::Engine; use afl::engines::Fuzzer; @@ -24,6 +31,9 @@ extern "C" { /// int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) fn LLVMFuzzerTestOneInput(data: *const u8, size: usize) -> i32; + // afl_libfuzzer_init calls LLVMFUzzerInitialize() + fn afl_libfuzzer_init() -> i32; + static __lafl_edges_map: *mut u8; static __lafl_cmp_map: *mut u8; static __lafl_max_edges_size: u32; @@ -40,14 +50,69 @@ const NAME_COV_MAP: &str = "cov_map"; #[no_mangle] pub extern "C" fn afl_libfuzzer_main() { - let mut rand = StdRand::new(0); + let matches = App::new("libAFLrs fuzzer harness") + .about("libAFLrs fuzzer harness help options.") + .arg( + Arg::with_name("port") + .short("p") + .value_name("PORT") + .takes_value(true) + .help("Broker TCP port to use."), + ) + .arg( + Arg::with_name("dictionary") + .short("x") + .value_name("DICTIONARY") + .takes_value(true) + .multiple(true) + .help("Dictionary file to use, can be specified multiple times."), + ) + .arg( + Arg::with_name("statstime") + .short("T") + .value_name("STATSTIME") + .takes_value(true) + .help("How often to print statistics in seconds [default: 5, disable: 0]"), + ) + .arg(Arg::with_name("workdir") + .help("Where to write the corpus, also reads the data on start. If more than one is supplied the first will be the work directory, all others will just be initially read from.") + .multiple(true) + .value_name("WORKDIR") + ) + .get_matches(); + let statstime = value_t!(matches, "statstime", u32).unwrap_or(5); + let broker_port = value_t!(matches, "port", u16).unwrap_or(1337); + + let workdir = if matches.is_present("workdir") { + matches.value_of("workdir").unwrap().to_string() + } else { + env::current_dir().unwrap().to_string_lossy().to_string() + }; + + let mut dictionary: Option> = None; + + if matches.is_present("dictionary") { + dictionary = Some(values_t!(matches, "dictionary", PathBuf).unwrap_or_else(|e| e.exit())); + } + + let mut input: Option> = None; + if matches.is_present("workdir") { + input = Some(values_t!(matches, "workdir", PathBuf).unwrap_or_else(|e| e.exit())); + } + + if dictionary != None || input != None { + println!("Information: the first process started is the broker and only processes the \'-p PORT\' option if present."); + } + + println!("Workdir: {:?}", workdir); + + let mut rand = StdRand::new(0); let mut corpus = InMemoryCorpus::new(); let mut generator = RandPrintablesGenerator::new(32); - let stats = SimpleStats::new(|s| println!("{}", s)); + let mut mgr = LlmpEventManager::new_on_port_std(broker_port, stats).unwrap(); - let mut mgr = LlmpEventManager::new_on_port_std(1337, stats).unwrap(); if mgr.is_broker() { println!("Doing broker things. Run this tool again to start fuzzing in a client."); mgr.broker_loop().unwrap(); @@ -66,16 +131,35 @@ pub extern "C" fn afl_libfuzzer_main() { let mut engine = Engine::new(executor); - state - .generate_initial_inputs( - &mut rand, - &mut corpus, - &mut generator, - &mut engine, - &mut mgr, - 4, - ) - .expect("Failed to load initial inputs"); + // Call LLVMFUzzerInitialize() if present. + unsafe { + if afl_libfuzzer_init() == -1 { + println!("Warning: LLVMFuzzerInitialize failed with -1") + } + } + + match input { + Some(x) => state + .load_initial_inputs(&mut corpus, &mut generator, &mut engine, &mut mgr, &x) + .expect("Failed to load initial corpus"), + None => (), + } + + if corpus.count() < 1 { + println!("Generating random inputs"); + state + .generate_initial_inputs( + &mut rand, + &mut corpus, + &mut generator, + &mut engine, + &mut mgr, + 4, + ) + .expect("Failed to generate initial inputs"); + } + + println!("We have {} inputs.", corpus.count()); let mut mutator = HavocBytesMutator::new_default(); mutator.set_max_size(4096); diff --git a/fuzzers/libfuzzer/test.sh b/fuzzers/libfuzzer/test.sh index f88f80aefa..335985452d 100755 --- a/fuzzers/libfuzzer/test.sh +++ b/fuzzers/libfuzzer/test.sh @@ -1,11 +1,21 @@ #!/bin/sh -cargo build --release -make -C runtime +cargo build --release || exit 1 +make -C runtime || exit 1 -./compiler -flto=thin -c test/test.c -o test_fuzz.o -./compiler -flto=thin -fuse-ld=lld test_fuzz.o -o test_fuzz.elf +rm -f test_fuzz.elf test_fuzz.o +./compiler -flto=thin -c test/test.c -o test_fuzz.o || exit 1 +./compiler -flto=thin test_fuzz.o -o test_fuzz.elf || exit 1 -RUST_BACKTRACE=1 ./test_fuzz.elf +RUST_BACKTRACE=1 ./test_fuzz.elf & + +test "$!" -gt 0 && { + + usleep 250 + RUST_BACKTRACE=1 ./test_fuzz.elf -x a -x b -T5 in1 in2 & + +} + +sleep 10 +killall test_fuzz.elf -#rm ./test_fuzz.elf