remove libfuzzer_runtime and use cc wrapper for mozjpeg
@ -1,31 +1,29 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "libfuzzer_libmozjpeg"
|
name = "libfuzzer_libmozjpeg"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
authors = ["Marcin Kozlowski <marcinguy@gmail.com>"]
|
authors = ["Andrea Fioraldi <andreafioraldi@gmail.com>", "Dominik Maier <domenukk@gmail.com>"]
|
||||||
edition = "2018"
|
edition = "2018"
|
||||||
build = "build.rs"
|
|
||||||
|
|
||||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
default = ["std"]
|
default = ["std"]
|
||||||
std = []
|
std = []
|
||||||
|
|
||||||
#[profile.release]
|
[profile.release]
|
||||||
#lto = true
|
lto = true
|
||||||
#codegen-units = 1
|
codegen-units = 1
|
||||||
#opt-level = 3
|
opt-level = 3
|
||||||
#debug = true
|
debug = true
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
libafl = { path = "../../libafl/" }
|
||||||
|
libafl_targets = { path = "../../libafl_targets/", features = ["pcguard_edges", "value_profile", "libfuzzer"] }
|
||||||
|
# TODO Include it only when building cc
|
||||||
|
libafl_cc = { path = "../../libafl_cc/" }
|
||||||
|
|
||||||
[build-dependencies]
|
[build-dependencies]
|
||||||
cc = { version = "1.0", features = ["parallel"] }
|
cc = { version = "1.0", features = ["parallel"] }
|
||||||
num_cpus = "1.0"
|
num_cpus = "1.0"
|
||||||
|
|
||||||
[dependencies]
|
[lib]
|
||||||
libafl = { path = "../../libafl/" }
|
|
||||||
|
|
||||||
[[example]]
|
|
||||||
name = "libfuzzer_libmozjpeg"
|
name = "libfuzzer_libmozjpeg"
|
||||||
path = "./src/fuzzer.rs"
|
crate-type = ["staticlib"]
|
||||||
test = false
|
|
||||||
bench = false
|
|
||||||
|
@ -3,6 +3,8 @@
|
|||||||
This folder contains an example fuzzer for libmozjpeg, using LLMP for fast multi-process fuzzing and crash detection.
|
This folder contains an example fuzzer for libmozjpeg, using LLMP for fast multi-process fuzzing and crash detection.
|
||||||
It has been tested on Linux.
|
It has been tested on Linux.
|
||||||
|
|
||||||
|
https://github.com/mozilla/mozjpeg/archive/v4.0.3.tar.gz
|
||||||
|
|
||||||
## Build
|
## Build
|
||||||
|
|
||||||
To build this example, run `cargo build --example libfuzzer_libmozjpeg --release`.
|
To build this example, run `cargo build --example libfuzzer_libmozjpeg --release`.
|
||||||
|
@ -1,3 +0,0 @@
|
|||||||
#!/bin/bash
|
|
||||||
taskset -c 0 ./.libfuzzer_test.elf
|
|
||||||
|
|
@ -1,120 +1,21 @@
|
|||||||
// build.rs
|
// build.rs
|
||||||
|
|
||||||
use std::{
|
use std::env;
|
||||||
env,
|
|
||||||
path::Path,
|
|
||||||
process::{exit, Command},
|
|
||||||
};
|
|
||||||
|
|
||||||
const LIBMOZJPEG_URL: &str = "https://github.com/mozilla/mozjpeg/archive/v4.0.3.tar.gz";
|
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
if cfg!(windows) {
|
|
||||||
println!("cargo:warning=Skipping libmozjpeg example on Windows");
|
|
||||||
exit(0);
|
|
||||||
}
|
|
||||||
|
|
||||||
let out_dir = env::var_os("OUT_DIR").unwrap();
|
let out_dir = env::var_os("OUT_DIR").unwrap();
|
||||||
//let cwd = env::current_dir().unwrap().to_string_lossy().to_string();
|
|
||||||
let out_dir = out_dir.to_string_lossy().to_string();
|
let out_dir = out_dir.to_string_lossy().to_string();
|
||||||
let out_dir_path = Path::new(&out_dir);
|
|
||||||
|
|
||||||
println!("cargo:rerun-if-changed=./runtime/rt.c",);
|
println!("cargo:rerun-if-changed=harness.c");
|
||||||
println!("cargo:rerun-if-changed=harness.cc");
|
|
||||||
|
|
||||||
let libmozjpeg = format!("{}/mozjpeg-4.0.3", &out_dir);
|
|
||||||
let libmozjpeg_path = Path::new(&libmozjpeg);
|
|
||||||
let libmozjpeg_tar = format!("{}/v4.0.3.tar.gz", &out_dir);
|
|
||||||
|
|
||||||
// Enforce clang for its -fsanitize-coverage support.
|
|
||||||
std::env::set_var("CC", "clang");
|
|
||||||
std::env::set_var("CXX", "clang++");
|
|
||||||
|
|
||||||
if !libmozjpeg_path.is_dir() {
|
|
||||||
if !Path::new(&libmozjpeg_tar).is_file() {
|
|
||||||
println!("cargo:warning=Libmozjpeg not found, downloading...");
|
|
||||||
// Download libmozjpeg
|
|
||||||
Command::new("wget")
|
|
||||||
.arg("-c")
|
|
||||||
.arg(LIBMOZJPEG_URL)
|
|
||||||
.arg("-O")
|
|
||||||
.arg(&libmozjpeg_tar)
|
|
||||||
.status()
|
|
||||||
.unwrap();
|
|
||||||
}
|
|
||||||
Command::new("tar")
|
|
||||||
.current_dir(&out_dir_path)
|
|
||||||
.arg("-xvf")
|
|
||||||
.arg(&libmozjpeg_tar)
|
|
||||||
.status()
|
|
||||||
.unwrap();
|
|
||||||
//println!("cargo:warning=Running cmake on {}", &libmozjpeg);
|
|
||||||
|
|
||||||
Command::new("cmake")
|
|
||||||
.current_dir(&libmozjpeg_path)
|
|
||||||
.args(&["-G", "Unix Makefiles", "--disable-shared"])
|
|
||||||
.arg(&libmozjpeg)
|
|
||||||
.env("OPT_LEVEL", "3")
|
|
||||||
.env("CC", "clang")
|
|
||||||
.env("CXX", "clang++")
|
|
||||||
.env(
|
|
||||||
"CFLAGS",
|
|
||||||
"-O3 -g -D_DEFAULT_SOURCE -fPIE -fsanitize-coverage=trace-pc-guard",
|
|
||||||
)
|
|
||||||
.env(
|
|
||||||
"CXXFLAGS",
|
|
||||||
"-O3 -g -D_DEFAULT_SOURCE -fPIE -fsanitize-coverage=trace-pc-guard",
|
|
||||||
)
|
|
||||||
.env("LDFLAGS", "-g -fPIE -fsanitize-coverage=trace-pc-guard")
|
|
||||||
.status()
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
Command::new("make")
|
|
||||||
.current_dir(&libmozjpeg_path)
|
|
||||||
//.arg(&format!("-j{}", num_cpus::get()))
|
|
||||||
.args(&[
|
|
||||||
"CC=clang",
|
|
||||||
"CXX=clang++",
|
|
||||||
"OPT_LEVEL=3",
|
|
||||||
"CFLAGS=-O3 -g -D_DEFAULT_SOURCE -fPIE -fsanitize-coverage=trace-pc-guard",
|
|
||||||
"LDFLAGS=-g -fPIE -fsanitize-coverage=trace-pc-guard",
|
|
||||||
"CXXFLAGS=-D_DEFAULT_SOURCE -fPIE -fsanitize-coverage=trace-pc-guard",
|
|
||||||
])
|
|
||||||
.env("CC", "clang")
|
|
||||||
.env("CXX", "clang++")
|
|
||||||
.env(
|
|
||||||
"CFLAGS",
|
|
||||||
"-O3 -g -D_DEFAULT_SOURCE -fPIE -fsanitize-coverage=trace-pc-guard",
|
|
||||||
)
|
|
||||||
.env(
|
|
||||||
"CXXFLAGS",
|
|
||||||
"-O3 -g -D_DEFAULT_SOURCE -fPIE -fsanitize-coverage=trace-pc-guard",
|
|
||||||
)
|
|
||||||
.env("LDFLAGS", "-g -fPIE -fsanitize-coverage=trace-pc-guard")
|
|
||||||
.status()
|
|
||||||
.unwrap();
|
|
||||||
}
|
|
||||||
|
|
||||||
cc::Build::new()
|
cc::Build::new()
|
||||||
.file("../libfuzzer_runtime/rt.c")
|
// Use sanitizer coverage to track the edges in the PUT
|
||||||
.compile("libfuzzer-sys");
|
// Take advantage of LTO (needs lld-link set in your cargo config)
|
||||||
|
//.flag("-flto=thin")
|
||||||
cc::Build::new()
|
.file("./hook_allocs.c")
|
||||||
.include(&libmozjpeg_path)
|
.compile("hook_allocs");
|
||||||
.flag("-fsanitize-coverage=trace-pc-guard")
|
|
||||||
.file("./harness.cc")
|
|
||||||
.compile("libfuzzer-harness");
|
|
||||||
|
|
||||||
println!("cargo:rustc-link-search=native={}", &out_dir);
|
println!("cargo:rustc-link-search=native={}", &out_dir);
|
||||||
println!("cargo:rustc-link-search=native={}/", &libmozjpeg);
|
|
||||||
println!("cargo:rustc-link-lib=static=jpeg");
|
|
||||||
|
|
||||||
//Deps for libmozjpeg: -pthread -lz -lm
|
|
||||||
println!("cargo:rustc-link-lib=dylib=m");
|
|
||||||
println!("cargo:rustc-link-lib=dylib=z");
|
|
||||||
|
|
||||||
//For the C++ harness
|
|
||||||
println!("cargo:rustc-link-lib=static=stdc++");
|
|
||||||
|
|
||||||
println!("cargo:rerun-if-changed=build.rs");
|
println!("cargo:rerun-if-changed=build.rs");
|
||||||
}
|
}
|
||||||
|
52
fuzzers/libfuzzer_libmozjpeg/hook_allocs.c
Normal file
@ -0,0 +1,52 @@
|
|||||||
|
#include <stdint.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#define MAP_SIZE (16*1024)
|
||||||
|
|
||||||
|
#ifdef _WIN32
|
||||||
|
#define posix_memalign(p, a, s) (((*(p)) = _aligned_malloc((s), (a))), *(p) ?0 :errno)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#define MAX(a, b) \
|
||||||
|
({ \
|
||||||
|
\
|
||||||
|
__typeof__(a) _a = (a); \
|
||||||
|
__typeof__(b) _b = (b); \
|
||||||
|
_a > _b ? _a : _b; \
|
||||||
|
\
|
||||||
|
})
|
||||||
|
|
||||||
|
size_t libafl_alloc_map[MAP_SIZE];
|
||||||
|
|
||||||
|
void *malloc(size_t size) {
|
||||||
|
|
||||||
|
uintptr_t k = (uintptr_t)__builtin_return_address(0);
|
||||||
|
k = (k >> 4) ^ (k << 8);
|
||||||
|
k &= MAP_SIZE - 1;
|
||||||
|
libafl_alloc_map[k] = MAX(libafl_alloc_map[k], size);
|
||||||
|
|
||||||
|
// We cannot malloc in malloc.
|
||||||
|
// Hence, even realloc(NULL, size) would loop in an optimized build.
|
||||||
|
// We fall back to a stricter allocation function. Fingers crossed.
|
||||||
|
void *ret = NULL;
|
||||||
|
posix_memalign(&ret, 1<<6, size);
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void *calloc(size_t nmemb, size_t size) {
|
||||||
|
|
||||||
|
size *= nmemb;
|
||||||
|
|
||||||
|
uintptr_t k = (uintptr_t)__builtin_return_address(0);
|
||||||
|
k = (k >> 4) ^ (k << 8);
|
||||||
|
k &= MAP_SIZE - 1;
|
||||||
|
libafl_alloc_map[k] = MAX(libafl_alloc_map[k], size);
|
||||||
|
|
||||||
|
void *ret = NULL;
|
||||||
|
posix_memalign(&ret, 1<<6, size);
|
||||||
|
memset(ret, 0, size);
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
}
|
33
fuzzers/libfuzzer_libmozjpeg/src/bin/cc.rs
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
use libafl_cc::{ClangWrapper, CompilerWrapper, LIB_EXT, LIB_PREFIX};
|
||||||
|
use std::env;
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
let args: Vec<String> = env::args().collect();
|
||||||
|
if args.len() > 1 {
|
||||||
|
let mut dir = env::current_exe().unwrap();
|
||||||
|
dir.pop();
|
||||||
|
|
||||||
|
let mut cc = ClangWrapper::new("clang", "clang++");
|
||||||
|
cc.from_args(&args)
|
||||||
|
.unwrap()
|
||||||
|
.add_arg("-fsanitize-coverage=trace-pc-guard,trace-cmp".into())
|
||||||
|
.unwrap()
|
||||||
|
.add_arg("-fPIC".into())
|
||||||
|
.unwrap()
|
||||||
|
.add_link_arg(
|
||||||
|
dir.join(format!("{}libfuzzer_libmozjpeg.{}", LIB_PREFIX, LIB_EXT))
|
||||||
|
.display()
|
||||||
|
.to_string(),
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
// Libraries needed by libafl on Windows
|
||||||
|
#[cfg(windows)]
|
||||||
|
cc.add_link_arg("-lws2_32".into())
|
||||||
|
.unwrap()
|
||||||
|
.add_link_arg("-lBcrypt".into())
|
||||||
|
.unwrap()
|
||||||
|
.add_link_arg("-lAdvapi32".into())
|
||||||
|
.unwrap();
|
||||||
|
cc.run().unwrap();
|
||||||
|
}
|
||||||
|
}
|
34
fuzzers/libfuzzer_libmozjpeg/src/bin/cxx.rs
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
use libafl_cc::{ClangWrapper, CompilerWrapper, LIB_EXT, LIB_PREFIX};
|
||||||
|
use std::env;
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
let args: Vec<String> = env::args().collect();
|
||||||
|
if args.len() > 1 {
|
||||||
|
let mut dir = env::current_exe().unwrap();
|
||||||
|
dir.pop();
|
||||||
|
|
||||||
|
let mut cc = ClangWrapper::new("clang", "clang++");
|
||||||
|
cc.is_cpp()
|
||||||
|
.from_args(&args)
|
||||||
|
.unwrap()
|
||||||
|
.add_arg("-fsanitize-coverage=trace-pc-guard,trace-cmp".into())
|
||||||
|
.unwrap()
|
||||||
|
.add_arg("-fPIC".into())
|
||||||
|
.unwrap()
|
||||||
|
.add_link_arg(
|
||||||
|
dir.join(format!("{}libfuzzer_libmozjpeg.{}", LIB_PREFIX, LIB_EXT))
|
||||||
|
.display()
|
||||||
|
.to_string(),
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
// Libraries needed by libafl on Windows
|
||||||
|
#[cfg(windows)]
|
||||||
|
cc.add_link_arg("-lws2_32".into())
|
||||||
|
.unwrap()
|
||||||
|
.add_link_arg("-lBcrypt".into())
|
||||||
|
.unwrap()
|
||||||
|
.add_link_arg("-lAdvapi32".into())
|
||||||
|
.unwrap();
|
||||||
|
cc.run().unwrap();
|
||||||
|
}
|
||||||
|
}
|
@ -3,7 +3,6 @@
|
|||||||
|
|
||||||
use std::{env, path::PathBuf};
|
use std::{env, path::PathBuf};
|
||||||
|
|
||||||
#[cfg(unix)]
|
|
||||||
use libafl::{
|
use libafl::{
|
||||||
bolts::{shmem::UnixShMem, tuples::tuple_list},
|
bolts::{shmem::UnixShMem, tuples::tuple_list},
|
||||||
corpus::{Corpus, InMemoryCorpus, OnDiskCorpus, RandCorpusScheduler},
|
corpus::{Corpus, InMemoryCorpus, OnDiskCorpus, RandCorpusScheduler},
|
||||||
@ -21,21 +20,15 @@ use libafl::{
|
|||||||
Error,
|
Error,
|
||||||
};
|
};
|
||||||
|
|
||||||
/// We will interact with a C++ target, so use external c functionality
|
use libafl_targets::{libfuzzer_initialize, libfuzzer_test_one_input, EDGES_MAP, MAX_EDGES_NUM, CMP_MAP, CMP_MAP_SIZE};
|
||||||
#[cfg(unix)]
|
|
||||||
|
const ALLOC_MAP_SIZE: usize = 16*1024;
|
||||||
extern "C" {
|
extern "C" {
|
||||||
/// int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size)
|
static mut libafl_alloc_map: [usize; ALLOC_MAP_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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The main fn, usually parsing parameters, and starting the fuzzer
|
/// The main fn, usually parsing parameters, and starting the fuzzer
|
||||||
|
#[no_mangle]
|
||||||
pub fn main() {
|
pub fn main() {
|
||||||
// Registry the metadata types used in this fuzzer
|
// Registry the metadata types used in this fuzzer
|
||||||
// Needed only on no_std
|
// Needed only on no_std
|
||||||
@ -53,14 +46,7 @@ pub fn main() {
|
|||||||
.expect("An error occurred while fuzzing");
|
.expect("An error occurred while fuzzing");
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Not supported on windows right now
|
|
||||||
#[cfg(windows)]
|
|
||||||
fn fuzz(_corpus_dirs: Vec<PathBuf>, _objective_dir: PathBuf, _broker_port: u16) -> Result<(), ()> {
|
|
||||||
todo!("Example not supported on Windows");
|
|
||||||
}
|
|
||||||
|
|
||||||
/// The actual fuzzer
|
/// The actual fuzzer
|
||||||
#[cfg(unix)]
|
|
||||||
fn fuzz(corpus_dirs: Vec<PathBuf>, objective_dir: PathBuf, broker_port: u16) -> Result<(), Error> {
|
fn fuzz(corpus_dirs: Vec<PathBuf>, objective_dir: PathBuf, broker_port: u16) -> Result<(), Error> {
|
||||||
// 'While the stats are state, they are usually used in the broker - which is likely never restarted
|
// 'While the stats are state, they are usually used in the broker - which is likely never restarted
|
||||||
let stats = SimpleStats::new(|s| println!("{}", s));
|
let stats = SimpleStats::new(|s| println!("{}", s));
|
||||||
@ -71,9 +57,13 @@ fn fuzz(corpus_dirs: Vec<PathBuf>, objective_dir: PathBuf, broker_port: u16) ->
|
|||||||
.expect("Failed to setup the restarter".into());
|
.expect("Failed to setup the restarter".into());
|
||||||
|
|
||||||
// Create an observation channel using the coverage map
|
// Create an observation channel using the coverage map
|
||||||
let edges_observer = unsafe {
|
let edges_observer = StdMapObserver::new("edges", unsafe { &mut EDGES_MAP }, unsafe { MAX_EDGES_NUM });
|
||||||
StdMapObserver::new_from_ptr("edges", __lafl_edges_map, __lafl_max_edges_size as usize)
|
|
||||||
};
|
// Create an observation channel using the cmp map
|
||||||
|
let cmps_observer = StdMapObserver::new("cmps", unsafe { &mut CMP_MAP }, CMP_MAP_SIZE);
|
||||||
|
|
||||||
|
// Create an observation channel using the allocations map
|
||||||
|
let allocs_observer = StdMapObserver::new("allocs", unsafe { &mut libafl_alloc_map }, ALLOC_MAP_SIZE);
|
||||||
|
|
||||||
// If not restarting, create a State from scratch
|
// If not restarting, create a State from scratch
|
||||||
let mut state = state.unwrap_or_else(|| {
|
let mut state = state.unwrap_or_else(|| {
|
||||||
@ -96,7 +86,7 @@ fn fuzz(corpus_dirs: Vec<PathBuf>, objective_dir: PathBuf, broker_port: u16) ->
|
|||||||
|
|
||||||
// Add the JPEG tokens if not existing
|
// Add the JPEG tokens if not existing
|
||||||
if state.metadata().get::<Tokens>().is_none() {
|
if state.metadata().get::<Tokens>().is_none() {
|
||||||
state.add_metadata(Tokens::from_tokens_file("./jpeg.tkns")?);
|
state.add_metadata(Tokens::from_tokens_file("./jpeg.dict")?);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Setup a basic mutator with a mutational stage
|
// Setup a basic mutator with a mutational stage
|
||||||
@ -109,26 +99,25 @@ fn fuzz(corpus_dirs: Vec<PathBuf>, objective_dir: PathBuf, broker_port: u16) ->
|
|||||||
|
|
||||||
// The wrapped harness function, calling out to the LLVM-style harness
|
// The wrapped harness function, calling out to the LLVM-style harness
|
||||||
let mut harness = |buf: &[u8]| {
|
let mut harness = |buf: &[u8]| {
|
||||||
unsafe { LLVMFuzzerTestOneInput(buf.as_ptr(), buf.len()) };
|
libfuzzer_test_one_input(buf);
|
||||||
ExitKind::Ok
|
ExitKind::Ok
|
||||||
};
|
};
|
||||||
|
|
||||||
// Create the executor for an in-process function with just one observer for edge coverage
|
// Create the executor for an in-process function with just one observer for edge coverage
|
||||||
let mut executor = InProcessExecutor::new(
|
let mut executor = InProcessExecutor::new(
|
||||||
"in-process(edges)",
|
"in-process(edges,cmp,alloc)",
|
||||||
&mut harness,
|
&mut harness,
|
||||||
tuple_list!(edges_observer),
|
tuple_list!(edges_observer, cmps_observer, allocs_observer),
|
||||||
&mut state,
|
&mut state,
|
||||||
&mut restarting_mgr,
|
&mut restarting_mgr,
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
// The actual target run starts here.
|
// The actual target run starts here.
|
||||||
// Call LLVMFUzzerInitialize() if present.
|
// Call LLVMFUzzerInitialize() if present.
|
||||||
unsafe {
|
let args: Vec<String> = env::args().collect();
|
||||||
if afl_libfuzzer_init() == -1 {
|
if libfuzzer_initialize(&args) == -1 {
|
||||||
println!("Warning: LLVMFuzzerInitialize failed with -1")
|
println!("Warning: LLVMFuzzerInitialize failed with -1")
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
// In case the corpus is empty (on first run), reset
|
// In case the corpus is empty (on first run), reset
|
||||||
if state.corpus().count() < 1 {
|
if state.corpus().count() < 1 {
|
@ -1,9 +0,0 @@
|
|||||||
#!/bin/bash
|
|
||||||
cores=$(grep -c ^processor /proc/cpuinfo)
|
|
||||||
for (( c=1;c<$cores;c++))
|
|
||||||
do
|
|
||||||
echo $c
|
|
||||||
taskset -c $c ./.libfuzzer_test.elf 2>/dev/null &
|
|
||||||
sleep 0.1
|
|
||||||
done
|
|
||||||
|
|
@ -1,2 +0,0 @@
|
|||||||
#!/bin/bash
|
|
||||||
killall -9 .libfuzzer_test.elf
|
|
@ -1,17 +0,0 @@
|
|||||||
#!/bin/sh
|
|
||||||
|
|
||||||
mkdir -p ./crashes
|
|
||||||
|
|
||||||
cargo build --example libfuzzer_libmozjpeg --release || exit 1
|
|
||||||
cp ../../target/release/examples/libfuzzer_libmozjpeg ./.libfuzzer_test.elf
|
|
||||||
|
|
||||||
# The broker
|
|
||||||
RUST_BACKTRACE=full taskset -c 0 ./.libfuzzer_test.elf &
|
|
||||||
# Give the broker time to spawn
|
|
||||||
sleep 2
|
|
||||||
echo "Spawning client"
|
|
||||||
# The 1st fuzzer client, pin to cpu 0x1
|
|
||||||
RUST_BACKTRACE=full taskset -c 1 ./.libfuzzer_test.elf 2>/dev/null
|
|
||||||
|
|
||||||
killall .libfuzzer_test.elf
|
|
||||||
rm -rf ./.libfuzzer_test.elf
|
|
@ -4,8 +4,6 @@ version = "0.1.0"
|
|||||||
authors = ["Andrea Fioraldi <andreafioraldi@gmail.com>", "Dominik Maier <domenukk@gmail.com>"]
|
authors = ["Andrea Fioraldi <andreafioraldi@gmail.com>", "Dominik Maier <domenukk@gmail.com>"]
|
||||||
edition = "2018"
|
edition = "2018"
|
||||||
|
|
||||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
default = ["std"]
|
default = ["std"]
|
||||||
std = []
|
std = []
|
||||||
|
@ -13,7 +13,7 @@ fn main() {
|
|||||||
.add_arg("-fsanitize-coverage=trace-pc-guard".into())
|
.add_arg("-fsanitize-coverage=trace-pc-guard".into())
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.add_link_arg(
|
.add_link_arg(
|
||||||
dir.join(format!("{}libfuzzer_stb_image.{}", LIB_PREFIX, LIB_EXT))
|
dir.join(format!("{}libfuzzer_libpng.{}", LIB_PREFIX, LIB_EXT))
|
||||||
.display()
|
.display()
|
||||||
.to_string(),
|
.to_string(),
|
||||||
)
|
)
|
||||||
|
@ -14,7 +14,7 @@ fn main() {
|
|||||||
.add_arg("-fsanitize-coverage=trace-pc-guard".into())
|
.add_arg("-fsanitize-coverage=trace-pc-guard".into())
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.add_link_arg(
|
.add_link_arg(
|
||||||
dir.join(format!("{}libfuzzer_stb_image.{}", LIB_PREFIX, LIB_EXT))
|
dir.join(format!("{}libfuzzer_libpng.{}", LIB_PREFIX, LIB_EXT))
|
||||||
.display()
|
.display()
|
||||||
.to_string(),
|
.to_string(),
|
||||||
)
|
)
|
||||||
|
1
fuzzers/libfuzzer_libpng_cmpalloc/.gitignore
vendored
@ -1 +0,0 @@
|
|||||||
libpng-*
|
|
@ -1,31 +0,0 @@
|
|||||||
[package]
|
|
||||||
name = "libfuzzer_libpng_cmpalloc"
|
|
||||||
version = "0.1.0"
|
|
||||||
authors = ["Andrea Fioraldi <andreafioraldi@gmail.com>", "Dominik Maier <domenukk@gmail.com>"]
|
|
||||||
edition = "2018"
|
|
||||||
build = "build.rs"
|
|
||||||
|
|
||||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
|
||||||
|
|
||||||
[features]
|
|
||||||
default = ["std"]
|
|
||||||
std = []
|
|
||||||
|
|
||||||
#[profile.release]
|
|
||||||
#lto = true
|
|
||||||
#codegen-units = 1
|
|
||||||
#opt-level = 3
|
|
||||||
#debug = true
|
|
||||||
|
|
||||||
[build-dependencies]
|
|
||||||
cc = { version = "1.0", features = ["parallel"] }
|
|
||||||
num_cpus = "1.0"
|
|
||||||
|
|
||||||
[dependencies]
|
|
||||||
libafl = { path = "../../libafl/" }
|
|
||||||
|
|
||||||
[[example]]
|
|
||||||
name = "libfuzzer_libpng_cmpalloc"
|
|
||||||
path = "./src/fuzzer.rs"
|
|
||||||
test = false
|
|
||||||
bench = false
|
|
@ -1,28 +0,0 @@
|
|||||||
# Libfuzzer for libpng (cmp+alloc)
|
|
||||||
|
|
||||||
This folder contains an example fuzzer for libpng, using LLMP for fast multi-process fuzzing and crash detection.
|
|
||||||
To show off crash detection, we added a ud2 instruction to the harness, edit harness.cc if you want a non-crashing example.
|
|
||||||
It has been tested on Linux.
|
|
||||||
|
|
||||||
The difference between the normal Libfuzzer for libpng example here is that this fuzzer is not just using edge coverage as feedback but also comparisons values (-value-profile like) and allocations sizes.
|
|
||||||
This is an example how multiple feedbacks can be combined in a fuzzer.
|
|
||||||
|
|
||||||
## Build
|
|
||||||
|
|
||||||
To build this example, run `cargo build --example libfuzzer_libpng_cmpalloc --release`.
|
|
||||||
This will call (the build.rs)[./builld.rs], which in turn downloads a libpng archive from the web.
|
|
||||||
Then, it will link (the fuzzer)[./src/fuzzer.rs] against (the C++ harness)[./harness.cc] and the instrumented `libpng`.
|
|
||||||
Afterwards, the fuzzer will be ready to run, from `../../target/examples/libfuzzer_libpng_cmpalloc`.
|
|
||||||
|
|
||||||
## Run
|
|
||||||
|
|
||||||
The first time you run the binary, the broker will open a tcp port (currently on port `1337`), waiting for fuzzer clients to connect. This port is local and only used for the initial handshake. All further communication happens via shared map, to be independent of the kernel.
|
|
||||||
|
|
||||||
Each following execution will run a fuzzer client.
|
|
||||||
As this example uses in-process fuzzing, we added a Restarting Event Manager (`setup_restarting_mgr`).
|
|
||||||
This means each client will start itself again to listen for crashes and timeouts.
|
|
||||||
By restarting the actual fuzzer, it can recover from these exit conditions.
|
|
||||||
|
|
||||||
In any real-world scenario, you should use `taskset` to pin each client to an empty CPU core, the lib does not pick an empty core automatically (yet).
|
|
||||||
|
|
||||||
For convenience, you may just run `./test.sh` in this folder to test it.
|
|
@ -1,108 +0,0 @@
|
|||||||
// build.rs
|
|
||||||
|
|
||||||
use std::{
|
|
||||||
env,
|
|
||||||
path::Path,
|
|
||||||
process::{exit, Command},
|
|
||||||
};
|
|
||||||
|
|
||||||
const LIBPNG_URL: &str =
|
|
||||||
"https://deac-fra.dl.sourceforge.net/project/libpng/libpng16/1.6.37/libpng-1.6.37.tar.xz";
|
|
||||||
|
|
||||||
fn main() {
|
|
||||||
if cfg!(windows) {
|
|
||||||
println!("cargo:warning=Skipping libpng example on Windows");
|
|
||||||
exit(0);
|
|
||||||
}
|
|
||||||
|
|
||||||
let out_dir = env::var_os("OUT_DIR").unwrap();
|
|
||||||
let out_dir = out_dir.to_string_lossy().to_string();
|
|
||||||
let out_dir_path = Path::new(&out_dir);
|
|
||||||
|
|
||||||
println!("cargo:rerun-if-changed=../libfuzzer_runtime/rt.c",);
|
|
||||||
println!("cargo:rerun-if-changed=harness.cc");
|
|
||||||
|
|
||||||
let libpng = format!("{}/libpng-1.6.37", &out_dir);
|
|
||||||
let libpng_path = Path::new(&libpng);
|
|
||||||
let libpng_tar = format!("{}/libpng-1.6.37.tar.xz", &out_dir);
|
|
||||||
|
|
||||||
// Enforce clang for its -fsanitize-coverage support.
|
|
||||||
std::env::set_var("CC", "clang");
|
|
||||||
std::env::set_var("CXX", "clang++");
|
|
||||||
let ldflags = match env::var("LDFLAGS") {
|
|
||||||
Ok(val) => val,
|
|
||||||
Err(_) => "".to_string(),
|
|
||||||
};
|
|
||||||
|
|
||||||
if !libpng_path.is_dir() {
|
|
||||||
if !Path::new(&libpng_tar).is_file() {
|
|
||||||
println!("cargo:warning=Libpng not found, downloading...");
|
|
||||||
// Download libpng
|
|
||||||
Command::new("wget")
|
|
||||||
.arg("-c")
|
|
||||||
.arg(LIBPNG_URL)
|
|
||||||
.arg("-O")
|
|
||||||
.arg(&libpng_tar)
|
|
||||||
.status()
|
|
||||||
.unwrap();
|
|
||||||
}
|
|
||||||
Command::new("tar")
|
|
||||||
.current_dir(&out_dir_path)
|
|
||||||
.arg("-xvf")
|
|
||||||
.arg(&libpng_tar)
|
|
||||||
.status()
|
|
||||||
.unwrap();
|
|
||||||
Command::new(format!("{}/configure", &libpng))
|
|
||||||
.current_dir(&libpng_path)
|
|
||||||
.args(&[
|
|
||||||
"--disable-shared",
|
|
||||||
&format!("--host={}", env::var("TARGET").unwrap())[..],
|
|
||||||
])
|
|
||||||
.env("CC", "clang")
|
|
||||||
.env("CXX", "clang++")
|
|
||||||
.env(
|
|
||||||
"CFLAGS",
|
|
||||||
"-O3 -g -D_DEFAULT_SOURCE -fPIE -fsanitize-coverage=trace-pc-guard",
|
|
||||||
)
|
|
||||||
.env(
|
|
||||||
"CXXFLAGS",
|
|
||||||
"-O3 -g -D_DEFAULT_SOURCE -fPIE -fsanitize-coverage=trace-pc-guard",
|
|
||||||
)
|
|
||||||
.env(
|
|
||||||
"LDFLAGS",
|
|
||||||
format!("-g -fPIE -fsanitize-coverage=trace-pc-guard {}", ldflags),
|
|
||||||
)
|
|
||||||
.status()
|
|
||||||
.unwrap();
|
|
||||||
Command::new("make")
|
|
||||||
.current_dir(&libpng_path)
|
|
||||||
.status()
|
|
||||||
.unwrap();
|
|
||||||
}
|
|
||||||
|
|
||||||
cc::Build::new()
|
|
||||||
.file("../libfuzzer_runtime/rt.c")
|
|
||||||
.compile("libfuzzer-sys");
|
|
||||||
|
|
||||||
cc::Build::new()
|
|
||||||
.include(&libpng_path)
|
|
||||||
.cpp(true)
|
|
||||||
.flag("-fsanitize-coverage=trace-pc-guard")
|
|
||||||
// .define("HAS_DUMMY_CRASH", "1")
|
|
||||||
.file("./harness.cc")
|
|
||||||
.compile("libfuzzer-harness");
|
|
||||||
|
|
||||||
println!("cargo:rustc-link-search=native={}", &out_dir);
|
|
||||||
println!("cargo:rustc-link-search=native={}/.libs", &libpng);
|
|
||||||
println!("cargo:rustc-link-lib=static=png16");
|
|
||||||
|
|
||||||
//Deps for libpng: -pthread -lz -lm
|
|
||||||
println!("cargo:rustc-link-lib=dylib=m");
|
|
||||||
println!("cargo:rustc-link-lib=dylib=z");
|
|
||||||
|
|
||||||
//For the C++ harness
|
|
||||||
//must by dylib for android
|
|
||||||
println!("cargo:rustc-link-lib=dylib=stdc++");
|
|
||||||
|
|
||||||
println!("cargo:rerun-if-changed=build.rs");
|
|
||||||
}
|
|
Before Width: | Height: | Size: 218 B |
Before Width: | Height: | Size: 376 B |
Before Width: | Height: | Size: 228 B |
Before Width: | Height: | Size: 427 B |
@ -1,197 +0,0 @@
|
|||||||
// libpng_read_fuzzer.cc
|
|
||||||
// Copyright 2017-2018 Glenn Randers-Pehrson
|
|
||||||
// Copyright 2015 The Chromium Authors. All rights reserved.
|
|
||||||
// Use of this source code is governed by a BSD-style license that may
|
|
||||||
// be found in the LICENSE file https://cs.chromium.org/chromium/src/LICENSE
|
|
||||||
|
|
||||||
// Last changed in libpng 1.6.35 [July 15, 2018]
|
|
||||||
|
|
||||||
// The modifications in 2017 by Glenn Randers-Pehrson include
|
|
||||||
// 1. addition of a PNG_CLEANUP macro,
|
|
||||||
// 2. setting the option to ignore ADLER32 checksums,
|
|
||||||
// 3. adding "#include <string.h>" which is needed on some platforms
|
|
||||||
// to provide memcpy().
|
|
||||||
// 4. adding read_end_info() and creating an end_info structure.
|
|
||||||
// 5. adding calls to png_set_*() transforms commonly used by browsers.
|
|
||||||
|
|
||||||
#include <stddef.h>
|
|
||||||
#include <stdint.h>
|
|
||||||
#include <string.h>
|
|
||||||
|
|
||||||
#include <vector>
|
|
||||||
|
|
||||||
#define PNG_INTERNAL
|
|
||||||
#include "png.h"
|
|
||||||
|
|
||||||
#define PNG_CLEANUP \
|
|
||||||
if(png_handler.png_ptr) \
|
|
||||||
{ \
|
|
||||||
if (png_handler.row_ptr) \
|
|
||||||
png_free(png_handler.png_ptr, png_handler.row_ptr); \
|
|
||||||
if (png_handler.end_info_ptr) \
|
|
||||||
png_destroy_read_struct(&png_handler.png_ptr, &png_handler.info_ptr,\
|
|
||||||
&png_handler.end_info_ptr); \
|
|
||||||
else if (png_handler.info_ptr) \
|
|
||||||
png_destroy_read_struct(&png_handler.png_ptr, &png_handler.info_ptr,\
|
|
||||||
nullptr); \
|
|
||||||
else \
|
|
||||||
png_destroy_read_struct(&png_handler.png_ptr, nullptr, nullptr); \
|
|
||||||
png_handler.png_ptr = nullptr; \
|
|
||||||
png_handler.row_ptr = nullptr; \
|
|
||||||
png_handler.info_ptr = nullptr; \
|
|
||||||
png_handler.end_info_ptr = nullptr; \
|
|
||||||
}
|
|
||||||
|
|
||||||
struct BufState {
|
|
||||||
const uint8_t* data;
|
|
||||||
size_t bytes_left;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct PngObjectHandler {
|
|
||||||
png_infop info_ptr = nullptr;
|
|
||||||
png_structp png_ptr = nullptr;
|
|
||||||
png_infop end_info_ptr = nullptr;
|
|
||||||
png_voidp row_ptr = nullptr;
|
|
||||||
BufState* buf_state = nullptr;
|
|
||||||
|
|
||||||
~PngObjectHandler() {
|
|
||||||
if (row_ptr)
|
|
||||||
png_free(png_ptr, row_ptr);
|
|
||||||
if (end_info_ptr)
|
|
||||||
png_destroy_read_struct(&png_ptr, &info_ptr, &end_info_ptr);
|
|
||||||
else if (info_ptr)
|
|
||||||
png_destroy_read_struct(&png_ptr, &info_ptr, nullptr);
|
|
||||||
else
|
|
||||||
png_destroy_read_struct(&png_ptr, nullptr, nullptr);
|
|
||||||
delete buf_state;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
void user_read_data(png_structp png_ptr, png_bytep data, size_t length) {
|
|
||||||
BufState* buf_state = static_cast<BufState*>(png_get_io_ptr(png_ptr));
|
|
||||||
if (length > buf_state->bytes_left) {
|
|
||||||
png_error(png_ptr, "read error");
|
|
||||||
}
|
|
||||||
memcpy(data, buf_state->data, length);
|
|
||||||
buf_state->bytes_left -= length;
|
|
||||||
buf_state->data += length;
|
|
||||||
}
|
|
||||||
|
|
||||||
static const int kPngHeaderSize = 8;
|
|
||||||
|
|
||||||
// Entry point for LibFuzzer.
|
|
||||||
// Roughly follows the libpng book example:
|
|
||||||
// http://www.libpng.org/pub/png/book/chapter13.html
|
|
||||||
extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
|
|
||||||
if (size < kPngHeaderSize) {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::vector<unsigned char> v(data, data + size);
|
|
||||||
if (png_sig_cmp(v.data(), 0, kPngHeaderSize)) {
|
|
||||||
// not a PNG.
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
PngObjectHandler png_handler;
|
|
||||||
png_handler.png_ptr = nullptr;
|
|
||||||
png_handler.row_ptr = nullptr;
|
|
||||||
png_handler.info_ptr = nullptr;
|
|
||||||
png_handler.end_info_ptr = nullptr;
|
|
||||||
|
|
||||||
png_handler.png_ptr = png_create_read_struct
|
|
||||||
(PNG_LIBPNG_VER_STRING, nullptr, nullptr, nullptr);
|
|
||||||
if (!png_handler.png_ptr) {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
png_handler.info_ptr = png_create_info_struct(png_handler.png_ptr);
|
|
||||||
if (!png_handler.info_ptr) {
|
|
||||||
PNG_CLEANUP
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
png_handler.end_info_ptr = png_create_info_struct(png_handler.png_ptr);
|
|
||||||
if (!png_handler.end_info_ptr) {
|
|
||||||
PNG_CLEANUP
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
png_set_crc_action(png_handler.png_ptr, PNG_CRC_QUIET_USE, PNG_CRC_QUIET_USE);
|
|
||||||
#ifdef PNG_IGNORE_ADLER32
|
|
||||||
png_set_option(png_handler.png_ptr, PNG_IGNORE_ADLER32, PNG_OPTION_ON);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// Setting up reading from buffer.
|
|
||||||
png_handler.buf_state = new BufState();
|
|
||||||
png_handler.buf_state->data = data + kPngHeaderSize;
|
|
||||||
png_handler.buf_state->bytes_left = size - kPngHeaderSize;
|
|
||||||
png_set_read_fn(png_handler.png_ptr, png_handler.buf_state, user_read_data);
|
|
||||||
png_set_sig_bytes(png_handler.png_ptr, kPngHeaderSize);
|
|
||||||
|
|
||||||
if (setjmp(png_jmpbuf(png_handler.png_ptr))) {
|
|
||||||
PNG_CLEANUP
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Reading.
|
|
||||||
png_read_info(png_handler.png_ptr, png_handler.info_ptr);
|
|
||||||
|
|
||||||
// reset error handler to put png_deleter into scope.
|
|
||||||
if (setjmp(png_jmpbuf(png_handler.png_ptr))) {
|
|
||||||
PNG_CLEANUP
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
png_uint_32 width, height;
|
|
||||||
int bit_depth, color_type, interlace_type, compression_type;
|
|
||||||
int filter_type;
|
|
||||||
|
|
||||||
if (!png_get_IHDR(png_handler.png_ptr, png_handler.info_ptr, &width,
|
|
||||||
&height, &bit_depth, &color_type, &interlace_type,
|
|
||||||
&compression_type, &filter_type)) {
|
|
||||||
PNG_CLEANUP
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
// This is going to be too slow.
|
|
||||||
if (width && height > 100000000 / width) {
|
|
||||||
PNG_CLEANUP
|
|
||||||
#ifdef HAS_DUMMY_CRASH
|
|
||||||
#ifdef __aarch64__
|
|
||||||
asm volatile (".word 0xf7f0a000\n");
|
|
||||||
#else
|
|
||||||
asm("ud2");
|
|
||||||
#endif
|
|
||||||
#endif
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Set several transforms that browsers typically use:
|
|
||||||
png_set_gray_to_rgb(png_handler.png_ptr);
|
|
||||||
png_set_expand(png_handler.png_ptr);
|
|
||||||
png_set_packing(png_handler.png_ptr);
|
|
||||||
png_set_scale_16(png_handler.png_ptr);
|
|
||||||
png_set_tRNS_to_alpha(png_handler.png_ptr);
|
|
||||||
|
|
||||||
int passes = png_set_interlace_handling(png_handler.png_ptr);
|
|
||||||
|
|
||||||
png_read_update_info(png_handler.png_ptr, png_handler.info_ptr);
|
|
||||||
|
|
||||||
png_handler.row_ptr = png_malloc(
|
|
||||||
png_handler.png_ptr, png_get_rowbytes(png_handler.png_ptr,
|
|
||||||
png_handler.info_ptr));
|
|
||||||
|
|
||||||
for (int pass = 0; pass < passes; ++pass) {
|
|
||||||
for (png_uint_32 y = 0; y < height; ++y) {
|
|
||||||
png_read_row(png_handler.png_ptr,
|
|
||||||
static_cast<png_bytep>(png_handler.row_ptr), nullptr);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
png_read_end(png_handler.png_ptr, png_handler.end_info_ptr);
|
|
||||||
|
|
||||||
PNG_CLEANUP
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
@ -1,178 +0,0 @@
|
|||||||
//! A libfuzzer-like fuzzer with llmp-multithreading support and restarts
|
|
||||||
//! The example harness is built for libpng.
|
|
||||||
|
|
||||||
use std::{env, path::PathBuf};
|
|
||||||
|
|
||||||
#[cfg(unix)]
|
|
||||||
use libafl::{
|
|
||||||
bolts::{shmem::UnixShMem, tuples::tuple_list},
|
|
||||||
corpus::{Corpus, InMemoryCorpus, OnDiskCorpus, RandCorpusScheduler},
|
|
||||||
events::setup_restarting_mgr,
|
|
||||||
executors::{inprocess::InProcessExecutor, ExitKind},
|
|
||||||
feedbacks::{CrashFeedback, MaxMapFeedback},
|
|
||||||
fuzzer::{Fuzzer, StdFuzzer},
|
|
||||||
mutators::scheduled::{havoc_mutations, StdScheduledMutator},
|
|
||||||
mutators::token_mutations::Tokens,
|
|
||||||
observers::{HitcountsMapObserver, StdMapObserver},
|
|
||||||
stages::mutational::StdMutationalStage,
|
|
||||||
state::{HasCorpus, HasMetadata, State},
|
|
||||||
stats::SimpleStats,
|
|
||||||
utils::{current_nanos, StdRand},
|
|
||||||
Error,
|
|
||||||
};
|
|
||||||
|
|
||||||
#[cfg(unix)]
|
|
||||||
const MAP_SIZE: usize = 16 * 1024;
|
|
||||||
|
|
||||||
/// We will interact with a C++ target, so use external c functionality
|
|
||||||
#[cfg(unix)]
|
|
||||||
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_alloc_map: *mut usize;
|
|
||||||
static __lafl_max_edges_size: u32;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// The main fn, usually parsing parameters, and starting the fuzzer
|
|
||||||
pub fn main() {
|
|
||||||
// Registry the metadata types used in this fuzzer
|
|
||||||
// Needed only on no_std
|
|
||||||
//RegistryBuilder::register::<Tokens>();
|
|
||||||
|
|
||||||
println!(
|
|
||||||
"Workdir: {:?}",
|
|
||||||
env::current_dir().unwrap().to_string_lossy().to_string()
|
|
||||||
);
|
|
||||||
fuzz(
|
|
||||||
vec![PathBuf::from("./corpus")],
|
|
||||||
PathBuf::from("./crashes"),
|
|
||||||
1337,
|
|
||||||
)
|
|
||||||
.expect("An error occurred while fuzzing");
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Not supported on windows right now
|
|
||||||
#[cfg(windows)]
|
|
||||||
fn fuzz(_corpus_dirs: Vec<PathBuf>, _objective_dir: PathBuf, _broker_port: u16) -> Result<(), ()> {
|
|
||||||
todo!("Example not supported on Windows");
|
|
||||||
}
|
|
||||||
|
|
||||||
/// The actual fuzzer
|
|
||||||
#[cfg(unix)]
|
|
||||||
fn fuzz(corpus_dirs: Vec<PathBuf>, objective_dir: PathBuf, broker_port: u16) -> Result<(), Error> {
|
|
||||||
// 'While the stats are state, they are usually used in the broker - which is likely never restarted
|
|
||||||
let stats = SimpleStats::new(|s| println!("{}", s));
|
|
||||||
|
|
||||||
// The restarting state will spawn the same process again as child, then restarted it each time it crashes.
|
|
||||||
let (state, mut restarting_mgr) =
|
|
||||||
match setup_restarting_mgr::<_, _, UnixShMem, _>(stats, broker_port) {
|
|
||||||
Ok(res) => res,
|
|
||||||
Err(err) => match err {
|
|
||||||
Error::ShuttingDown => {
|
|
||||||
return Ok(());
|
|
||||||
}
|
|
||||||
_ => {
|
|
||||||
panic!("Failed to setup the restarter: {}", err);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
// Create an observation channel using the coverage map
|
|
||||||
let edges_observer = HitcountsMapObserver::new(unsafe {
|
|
||||||
StdMapObserver::new_from_ptr("edges", __lafl_edges_map, __lafl_max_edges_size as usize)
|
|
||||||
});
|
|
||||||
|
|
||||||
// Create an observation channel using the cmp map
|
|
||||||
let cmps_observer = unsafe { StdMapObserver::new_from_ptr("cmps", __lafl_cmp_map, MAP_SIZE) };
|
|
||||||
|
|
||||||
// Create an observation channel using the allocations map
|
|
||||||
let allocs_observer =
|
|
||||||
unsafe { StdMapObserver::new_from_ptr("allocs", __lafl_alloc_map, MAP_SIZE) };
|
|
||||||
|
|
||||||
// If not restarting, create a State from scratch
|
|
||||||
let mut state = state.unwrap_or_else(|| {
|
|
||||||
State::new(
|
|
||||||
// RNG
|
|
||||||
StdRand::with_seed(current_nanos()),
|
|
||||||
// Corpus that will be evolved, we keep it in memory for performance
|
|
||||||
InMemoryCorpus::new(),
|
|
||||||
// Feedbacks to rate the interestingness of an input
|
|
||||||
tuple_list!(
|
|
||||||
MaxMapFeedback::new_with_observer_track(&edges_observer, true, false),
|
|
||||||
MaxMapFeedback::new_with_observer(&cmps_observer),
|
|
||||||
MaxMapFeedback::new_with_observer(&allocs_observer),
|
|
||||||
),
|
|
||||||
// Corpus in which we store solutions (crashes in this example),
|
|
||||||
// on disk so the user can get them after stopping the fuzzer
|
|
||||||
OnDiskCorpus::new(objective_dir).unwrap(),
|
|
||||||
// Feedbacks to recognize an input as solution
|
|
||||||
tuple_list!(CrashFeedback::new()),
|
|
||||||
)
|
|
||||||
});
|
|
||||||
|
|
||||||
println!("We're a client, let's fuzz :)");
|
|
||||||
|
|
||||||
// Create a PNG dictionary if not existing
|
|
||||||
if state.metadata().get::<Tokens>().is_none() {
|
|
||||||
state.add_metadata(Tokens::new(vec![
|
|
||||||
vec![137, 80, 78, 71, 13, 10, 26, 10], // PNG header
|
|
||||||
"IHDR".as_bytes().to_vec(),
|
|
||||||
"IDAT".as_bytes().to_vec(),
|
|
||||||
"PLTE".as_bytes().to_vec(),
|
|
||||||
"IEND".as_bytes().to_vec(),
|
|
||||||
]));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Setup a basic mutator with a mutational stage
|
|
||||||
let mutator = StdScheduledMutator::new(havoc_mutations());
|
|
||||||
let stage = StdMutationalStage::new(mutator);
|
|
||||||
|
|
||||||
let scheduler = RandCorpusScheduler::new();
|
|
||||||
// A fuzzer with just one stage and a random policy to get testcasess from the corpus
|
|
||||||
let mut fuzzer = StdFuzzer::new(tuple_list!(stage));
|
|
||||||
|
|
||||||
// The wrapped harness function, calling out to the LLVM-style harness
|
|
||||||
let mut harness = |buf: &[u8]| {
|
|
||||||
unsafe { LLVMFuzzerTestOneInput(buf.as_ptr(), buf.len()) };
|
|
||||||
ExitKind::Ok
|
|
||||||
};
|
|
||||||
|
|
||||||
// Create the executor for an in-process function with just one observer for edge coverage
|
|
||||||
let mut executor = InProcessExecutor::new(
|
|
||||||
"in-process(edges)",
|
|
||||||
&mut harness,
|
|
||||||
tuple_list!(edges_observer, cmps_observer, allocs_observer),
|
|
||||||
&mut state,
|
|
||||||
&mut restarting_mgr,
|
|
||||||
)?;
|
|
||||||
|
|
||||||
// The actual target run starts here.
|
|
||||||
// Call LLVMFUzzerInitialize() if present.
|
|
||||||
unsafe {
|
|
||||||
if afl_libfuzzer_init() == -1 {
|
|
||||||
println!("Warning: LLVMFuzzerInitialize failed with -1")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// In case the corpus is empty (on first run), reset
|
|
||||||
if state.corpus().count() < 1 {
|
|
||||||
state
|
|
||||||
.load_initial_inputs(&mut executor, &mut restarting_mgr, &scheduler, &corpus_dirs)
|
|
||||||
.expect(&format!(
|
|
||||||
"Failed to load initial corpus at {:?}",
|
|
||||||
&corpus_dirs
|
|
||||||
));
|
|
||||||
println!("We imported {} inputs from disk.", state.corpus().count());
|
|
||||||
}
|
|
||||||
|
|
||||||
fuzzer.fuzz_loop(&mut state, &mut executor, &mut restarting_mgr, &scheduler)?;
|
|
||||||
|
|
||||||
// Never reached
|
|
||||||
Ok(())
|
|
||||||
}
|
|
@ -1,17 +0,0 @@
|
|||||||
#!/bin/sh
|
|
||||||
|
|
||||||
mkdir -p ./crashes
|
|
||||||
|
|
||||||
cargo build --example libfuzzer_libpng_cmpalloc --release || exit 1
|
|
||||||
cp ../../target/release/examples/libfuzzer_libpng ./.libfuzzer_test.elf
|
|
||||||
|
|
||||||
# The broker
|
|
||||||
RUST_BACKTRACE=full taskset -c 0 ./.libfuzzer_test.elf &
|
|
||||||
# Give the broker time to spawn
|
|
||||||
sleep 2
|
|
||||||
echo "Spawning client"
|
|
||||||
# The 1st fuzzer client, pin to cpu 0x1
|
|
||||||
RUST_BACKTRACE=full taskset -c 1 ./.libfuzzer_test.elf 2>/dev/null
|
|
||||||
|
|
||||||
killall .libfuzzer_test.elf
|
|
||||||
rm -rf ./.libfuzzer_test.elf
|
|
@ -1,189 +0,0 @@
|
|||||||
#include <stdio.h>
|
|
||||||
#include <stdint.h>
|
|
||||||
#include <stdlib.h>
|
|
||||||
#include <string.h>
|
|
||||||
|
|
||||||
#define MAP_SIZE (16*1024)
|
|
||||||
|
|
||||||
int orig_argc;
|
|
||||||
char **orig_argv;
|
|
||||||
char **orig_envp;
|
|
||||||
|
|
||||||
uint8_t __lafl_dummy_map[MAP_SIZE];
|
|
||||||
size_t __lafl_dummy_map_usize[MAP_SIZE];
|
|
||||||
|
|
||||||
uint8_t *__lafl_edges_map = __lafl_dummy_map;
|
|
||||||
uint8_t *__lafl_cmp_map = __lafl_dummy_map;
|
|
||||||
size_t *__lafl_alloc_map = __lafl_dummy_map_usize;
|
|
||||||
|
|
||||||
uint32_t __lafl_max_edges_size = 0;
|
|
||||||
|
|
||||||
void __sanitizer_cov_trace_pc_guard(uint32_t *guard) {
|
|
||||||
|
|
||||||
uint32_t pos = *guard;
|
|
||||||
uint16_t val = __lafl_edges_map[pos] + 1;
|
|
||||||
__lafl_edges_map[pos] = ((uint8_t) val) + (uint8_t) (val >> 8);
|
|
||||||
//__lafl_edges_map[pos] = 1;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
void __sanitizer_cov_trace_pc_guard_init(uint32_t *start, uint32_t *stop) {
|
|
||||||
|
|
||||||
if (start == stop || *start) { return; }
|
|
||||||
|
|
||||||
*(start++) = (++__lafl_max_edges_size) & (MAP_SIZE -1);
|
|
||||||
|
|
||||||
while (start < stop) {
|
|
||||||
|
|
||||||
*start = (++__lafl_max_edges_size) & (MAP_SIZE -1);
|
|
||||||
start++;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
#define MAX(a, b) \
|
|
||||||
({ \
|
|
||||||
\
|
|
||||||
__typeof__(a) _a = (a); \
|
|
||||||
__typeof__(b) _b = (b); \
|
|
||||||
_a > _b ? _a : _b; \
|
|
||||||
\
|
|
||||||
})
|
|
||||||
|
|
||||||
#if defined(__APPLE__)
|
|
||||||
#pragma weak __sanitizer_cov_trace_const_cmp1 = __sanitizer_cov_trace_cmp1
|
|
||||||
#pragma weak __sanitizer_cov_trace_const_cmp2 = __sanitizer_cov_trace_cmp2
|
|
||||||
#pragma weak __sanitizer_cov_trace_const_cmp4 = __sanitizer_cov_trace_cmp4
|
|
||||||
#pragma weak __sanitizer_cov_trace_const_cmp8 = __sanitizer_cov_trace_cmp8
|
|
||||||
#else
|
|
||||||
void __sanitizer_cov_trace_const_cmp1(uint8_t arg1, uint8_t arg2) __attribute__((alias("__sanitizer_cov_trace_cmp1")));
|
|
||||||
void __sanitizer_cov_trace_const_cmp2(uint16_t arg1, uint16_t arg2)
|
|
||||||
__attribute__((alias("__sanitizer_cov_trace_cmp2")));
|
|
||||||
void __sanitizer_cov_trace_const_cmp4(uint32_t arg1, uint32_t arg2)
|
|
||||||
__attribute__((alias("__sanitizer_cov_trace_cmp4")));
|
|
||||||
void __sanitizer_cov_trace_const_cmp8(uint64_t arg1, uint64_t arg2)
|
|
||||||
__attribute__((alias("__sanitizer_cov_trace_cmp8")));
|
|
||||||
#endif
|
|
||||||
|
|
||||||
void __sanitizer_cov_trace_cmp1(uint8_t arg1, uint8_t arg2) {
|
|
||||||
|
|
||||||
uintptr_t k = (uintptr_t)__builtin_return_address(0);
|
|
||||||
k = (k >> 4) ^ (k << 8);
|
|
||||||
k &= MAP_SIZE - 1;
|
|
||||||
__lafl_cmp_map[k] = MAX(__lafl_cmp_map[k], (__builtin_popcount(~(arg1 ^ arg2))));
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
void __sanitizer_cov_trace_cmp2(uint16_t arg1, uint16_t arg2) {
|
|
||||||
|
|
||||||
uintptr_t k = (uintptr_t)__builtin_return_address(0);
|
|
||||||
k = (k >> 4) ^ (k << 8);
|
|
||||||
k &= MAP_SIZE - 1;
|
|
||||||
__lafl_cmp_map[k] = MAX(__lafl_cmp_map[k], (__builtin_popcount(~(arg1 ^ arg2))));
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
void __sanitizer_cov_trace_cmp4(uint32_t arg1, uint32_t arg2) {
|
|
||||||
|
|
||||||
uintptr_t k = (uintptr_t)__builtin_return_address(0);
|
|
||||||
k = (k >> 4) ^ (k << 8);
|
|
||||||
k &= MAP_SIZE - 1;
|
|
||||||
__lafl_cmp_map[k] = MAX(__lafl_cmp_map[k], (__builtin_popcount(~(arg1 ^ arg2))));
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
void __sanitizer_cov_trace_cmp8(uint64_t arg1, uint64_t arg2) {
|
|
||||||
|
|
||||||
uintptr_t k = (uintptr_t)__builtin_return_address(0);
|
|
||||||
k = (k >> 4) ^ (k << 8);
|
|
||||||
k &= MAP_SIZE - 1;
|
|
||||||
__lafl_cmp_map[k] = MAX(__lafl_cmp_map[k], (__builtin_popcountll(~(arg1 ^ arg2))));
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
void __sanitizer_cov_trace_switch(uint64_t val, uint64_t *cases) {
|
|
||||||
|
|
||||||
uintptr_t rt = (uintptr_t)__builtin_return_address(0);
|
|
||||||
if (cases[1] == 64) {
|
|
||||||
|
|
||||||
for (uint64_t i = 0; i < cases[0]; i++) {
|
|
||||||
|
|
||||||
uintptr_t k = rt + i;
|
|
||||||
k = (k >> 4) ^ (k << 8);
|
|
||||||
k &= MAP_SIZE - 1;
|
|
||||||
__lafl_cmp_map[k] = MAX(__lafl_cmp_map[k], (__builtin_popcountll(~(val ^ cases[i + 2]))));
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
} else {
|
|
||||||
|
|
||||||
for (uint64_t i = 0; i < cases[0]; i++) {
|
|
||||||
|
|
||||||
uintptr_t k = rt + i;
|
|
||||||
k = (k >> 4) ^ (k << 8);
|
|
||||||
k &= MAP_SIZE - 1;
|
|
||||||
__lafl_cmp_map[k] = MAX(__lafl_cmp_map[k], (__builtin_popcount(~(val ^ cases[i + 2]))));
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
#ifdef _WIN32
|
|
||||||
#define posix_memalign(p, a, s) (((*(p)) = _aligned_malloc((s), (a))), *(p) ?0 :errno)
|
|
||||||
#endif
|
|
||||||
|
|
||||||
void *malloc(size_t size) {
|
|
||||||
|
|
||||||
uintptr_t k = (uintptr_t)__builtin_return_address(0);
|
|
||||||
k = (k >> 4) ^ (k << 8);
|
|
||||||
k &= MAP_SIZE - 1;
|
|
||||||
__lafl_alloc_map[k] = MAX(__lafl_alloc_map[k], size);
|
|
||||||
|
|
||||||
// We cannot malloc in malloc.
|
|
||||||
// Hence, even realloc(NULL, size) would loop in an optimized build.
|
|
||||||
// We fall back to a stricter allocation function. Fingers crossed.
|
|
||||||
void *ret = NULL;
|
|
||||||
posix_memalign(&ret, 1<<6, size);
|
|
||||||
return ret;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
void *calloc(size_t nmemb, size_t size) {
|
|
||||||
|
|
||||||
size *= nmemb;
|
|
||||||
|
|
||||||
uintptr_t k = (uintptr_t)__builtin_return_address(0);
|
|
||||||
k = (k >> 4) ^ (k << 8);
|
|
||||||
k &= MAP_SIZE - 1;
|
|
||||||
__lafl_alloc_map[k] = MAX(__lafl_alloc_map[k], size);
|
|
||||||
|
|
||||||
void *ret = NULL;
|
|
||||||
posix_memalign(&ret, 1<<6, size);
|
|
||||||
memset(ret, 0, size);
|
|
||||||
return ret;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -1,61 +0,0 @@
|
|||||||
#![allow(dead_code, mutable_transmutes, non_camel_case_types, non_snake_case,
|
|
||||||
non_upper_case_globals, unused_assignments, unused_mut)]
|
|
||||||
|
|
||||||
use std::ptr;
|
|
||||||
|
|
||||||
pub const MAP_SIZE: usize = 65536;
|
|
||||||
|
|
||||||
extern "C" {
|
|
||||||
/// __attribute__((weak)) int LLVMFuzzerInitialize(int *argc, char ***argv);
|
|
||||||
fn LLVMFuzzerInitialize(argc: *mut libc::c_int,
|
|
||||||
argv: *mut *mut *mut libc::c_char) -> libc::c_int;
|
|
||||||
|
|
||||||
/// int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size)
|
|
||||||
pub fn LLVMFuzzerTestOneInput(data: *const u8, size: usize) -> i32;
|
|
||||||
}
|
|
||||||
|
|
||||||
static mut orig_argc: libc::c_int = 0;
|
|
||||||
static mut orig_argv: *mut *mut libc::c_char = ptr::null_mut();
|
|
||||||
static mut orig_envp: *mut *mut libc::c_char = ptr::null_mut();
|
|
||||||
|
|
||||||
pub static mut edges_map: [u8; MAP_SIZE] = [0; MAP_SIZE];
|
|
||||||
pub static mut cmp_map: [u8; MAP_SIZE] = [0; MAP_SIZE];
|
|
||||||
pub static mut max_edges_size: usize = 0;
|
|
||||||
|
|
||||||
#[no_mangle]
|
|
||||||
pub unsafe extern "C" fn __sanitizer_cov_trace_pc_guard(mut guard: *mut u32) {
|
|
||||||
let mut pos: u32 = *guard;
|
|
||||||
//uint16_t val = __lafl_edges_map[pos] + 1;
|
|
||||||
//__lafl_edges_map[pos] = ((uint8_t) val) + (uint8_t) (val >> 8);
|
|
||||||
edges_map[pos as usize] = 1 as u8;
|
|
||||||
}
|
|
||||||
|
|
||||||
#[no_mangle]
|
|
||||||
pub unsafe extern "C" fn __sanitizer_cov_trace_pc_guard_init(mut start: *mut u32, mut stop: *mut u32) {
|
|
||||||
if start == stop || *start != 0 { return }
|
|
||||||
|
|
||||||
while start < stop {
|
|
||||||
max_edges_size += 1;
|
|
||||||
*start = (max_edges_size & (MAP_SIZE -1)) as u32;
|
|
||||||
start = start.offset(1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
unsafe extern "C" fn copy_args_init(mut argc: libc::c_int, mut argv: *mut *mut libc::c_char, mut envp: *mut *mut libc::c_char) {
|
|
||||||
orig_argc = argc;
|
|
||||||
orig_argv = argv;
|
|
||||||
orig_envp = envp;
|
|
||||||
}
|
|
||||||
|
|
||||||
#[no_mangle]
|
|
||||||
#[link_section = ".init_array"]
|
|
||||||
static mut p_copy_args_init: Option<unsafe extern "C" fn(_: libc::c_int, _: *mut *mut libc::c_char, _: *mut *mut libc::c_char) -> ()> = Some(copy_args_init);
|
|
||||||
|
|
||||||
#[no_mangle]
|
|
||||||
pub unsafe extern "C" fn afl_libfuzzer_init() -> libc::c_int {
|
|
||||||
if Some(LLVMFuzzerInitialize).is_some() {
|
|
||||||
LLVMFuzzerInitialize(&mut orig_argc, &mut orig_argv)
|
|
||||||
} else {
|
|
||||||
0 as libc::c_int
|
|
||||||
}
|
|
||||||
}
|
|
@ -14,7 +14,7 @@ fn main() {
|
|||||||
|
|
||||||
cc::Build::new()
|
cc::Build::new()
|
||||||
// Use sanitizer coverage to track the edges in the PUT
|
// Use sanitizer coverage to track the edges in the PUT
|
||||||
.flag("-fsanitize-coverage=trace-pc-guard,trace-cmp")
|
.flag("-fsanitize-coverage=trace-pc-guard")
|
||||||
// Take advantage of LTO (needs lld-link set in your cargo config)
|
// Take advantage of LTO (needs lld-link set in your cargo config)
|
||||||
//.flag("-flto=thin")
|
//.flag("-flto=thin")
|
||||||
.flag("-Wno-sign-compare")
|
.flag("-Wno-sign-compare")
|
||||||
|
1
fuzzers/libfuzzer_windows/.gitignore
vendored
@ -1 +0,0 @@
|
|||||||
libpng-*
|
|
@ -1,31 +0,0 @@
|
|||||||
[package]
|
|
||||||
name = "libfuzzer_windows"
|
|
||||||
version = "0.1.0"
|
|
||||||
authors = ["Andrea Fioraldi <andreafioraldi@gmail.com>", "Dominik Maier <domenukk@gmail.com>"]
|
|
||||||
edition = "2018"
|
|
||||||
build = "build.rs"
|
|
||||||
|
|
||||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
|
||||||
|
|
||||||
[features]
|
|
||||||
default = ["std"]
|
|
||||||
std = []
|
|
||||||
|
|
||||||
#[profile.release]
|
|
||||||
#lto = true
|
|
||||||
#codegen-units = 1
|
|
||||||
#opt-level = 3
|
|
||||||
#debug = true
|
|
||||||
|
|
||||||
[build-dependencies]
|
|
||||||
cc = { version = "1.0", features = ["parallel"] }
|
|
||||||
num_cpus = "1.0"
|
|
||||||
|
|
||||||
[dependencies]
|
|
||||||
libafl = { path = "../../libafl/" }
|
|
||||||
|
|
||||||
[[example]]
|
|
||||||
name = "libfuzzer_windows"
|
|
||||||
path = "./src/fuzzer.rs"
|
|
||||||
test = false
|
|
||||||
bench = false
|
|
@ -1,25 +0,0 @@
|
|||||||
# Libfuzzer for libpng
|
|
||||||
|
|
||||||
This folder contains an example fuzzer for libpng, using LLMP for fast multi-process fuzzing and crash detection.
|
|
||||||
To show off crash detection, we added a ud2 instruction to the harness, edit harness.cc if you want a non-crashing example.
|
|
||||||
It has been tested on Linux.
|
|
||||||
|
|
||||||
## Build
|
|
||||||
|
|
||||||
To build this example, run `cargo build --example libfuzzer_libpng --release`.
|
|
||||||
This will call (the build.rs)[./builld.rs], which in turn downloads a libpng archive from the web.
|
|
||||||
Then, it will link (the fuzzer)[./src/fuzzer.rs] against (the C++ harness)[./harness.cc] and the instrumented `libpng`.
|
|
||||||
Afterwards, the fuzzer will be ready to run, from `../../target/examples/libfuzzer_libpng`.
|
|
||||||
|
|
||||||
## Run
|
|
||||||
|
|
||||||
The first time you run the binary, the broker will open a tcp port (currently on port `1337`), waiting for fuzzer clients to connect. This port is local and only used for the initial handshake. All further communication happens via shared map, to be independent of the kernel.
|
|
||||||
|
|
||||||
Each following execution will run a fuzzer client.
|
|
||||||
As this example uses in-process fuzzing, we added a Restarting Event Manager (`setup_restarting_mgr`).
|
|
||||||
This means each client will start itself again to listen for crashes and timeouts.
|
|
||||||
By restarting the actual fuzzer, it can recover from these exit conditions.
|
|
||||||
|
|
||||||
In any real-world scenario, you should use `taskset` to pin each client to an empty CPU core, the lib does not pick an empty core automatically (yet).
|
|
||||||
|
|
||||||
For convenience, you may just run `./test.sh` in this folder to test it.
|
|
@ -1,58 +0,0 @@
|
|||||||
// build.rs
|
|
||||||
|
|
||||||
#[cfg(windows)]
|
|
||||||
use std::env;
|
|
||||||
|
|
||||||
#[cfg(not(windows))]
|
|
||||||
fn main() {
|
|
||||||
println!("cargo:warning=Skipping libpng windows example on non-Windows");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(windows)]
|
|
||||||
fn main() {
|
|
||||||
let out_dir = env::var_os("OUT_DIR").unwrap();
|
|
||||||
let out_dir = out_dir.to_string_lossy().to_string();
|
|
||||||
|
|
||||||
println!("cargo:rerun-if-changed=../libfuzzer_runtime/rt.c",);
|
|
||||||
println!("cargo:rerun-if-changed=harness.cc");
|
|
||||||
|
|
||||||
// Enforce clang for its -fsanitize-coverage support.
|
|
||||||
std::env::set_var("CC", "clang");
|
|
||||||
std::env::set_var("CXX", "clang++");
|
|
||||||
/*let ldflags = match env::var("LDFLAGS") {
|
|
||||||
Ok(val) => val,
|
|
||||||
Err(_) => "".to_string(),
|
|
||||||
};*/
|
|
||||||
|
|
||||||
cc::Build::new()
|
|
||||||
.file("../libfuzzer_runtime/rt.c")
|
|
||||||
.compile("libfuzzer-sys");
|
|
||||||
|
|
||||||
cc::Build::new()
|
|
||||||
.cpp(true)
|
|
||||||
.flag("-fsanitize-coverage=trace-pc-guard")
|
|
||||||
// .define("HAS_DUMMY_CRASH", "1")
|
|
||||||
.flag("-Wno-void-pointer-to-int-cast")
|
|
||||||
.flag("-Wno-pointer-to-int-cast")
|
|
||||||
.flag("-Wno-int-to-pointer-cast")
|
|
||||||
.flag("-Wno-sign-compare")
|
|
||||||
.flag("-Wno-format")
|
|
||||||
.flag("-Wno-unused-variable")
|
|
||||||
.file("./harness.cc")
|
|
||||||
.compile("windows-harness");
|
|
||||||
|
|
||||||
println!("cargo:rustc-link-search=native={}", &out_dir);
|
|
||||||
//println!("cargo:rustc-link-search=native={}/.libs", &libpng);
|
|
||||||
//println!("cargo:rustc-link-lib=static=png16");
|
|
||||||
|
|
||||||
//Deps for libpng: -pthread -lz -lm
|
|
||||||
//println!("cargo:rustc-link-lib=dylib=m");
|
|
||||||
//println!("cargo:rustc-link-lib=dylib=z");
|
|
||||||
|
|
||||||
//For the C++ harness
|
|
||||||
//must by dylib for android
|
|
||||||
//println!("cargo:rustc-link-lib=dylib=stdc++");
|
|
||||||
|
|
||||||
println!("cargo:rerun-if-changed=build.rs");
|
|
||||||
}
|
|
Before Width: | Height: | Size: 218 B |
Before Width: | Height: | Size: 376 B |
Before Width: | Height: | Size: 228 B |
Before Width: | Height: | Size: 427 B |
@ -1,231 +0,0 @@
|
|||||||
#include <stdio.h>
|
|
||||||
#include <stdint.h>
|
|
||||||
#include <windows.h>
|
|
||||||
|
|
||||||
#define STBI_ASSERT(x)
|
|
||||||
#define STBI_NO_SIMD
|
|
||||||
#define STBI_NO_LINEAR
|
|
||||||
#define STBI_NO_STDIO
|
|
||||||
#define STB_IMAGE_IMPLEMENTATION
|
|
||||||
#include "stb_image.h"
|
|
||||||
|
|
||||||
int target_func(const uint8_t *buf, size_t size) {
|
|
||||||
|
|
||||||
/*printf("BUF (%ld): ", size);
|
|
||||||
for (int i = 0; i < size; i++) {
|
|
||||||
printf("%02X", buf[i]);
|
|
||||||
}
|
|
||||||
printf("\n");*/
|
|
||||||
|
|
||||||
if (size == 0) return 0;
|
|
||||||
|
|
||||||
switch (buf[0]) {
|
|
||||||
|
|
||||||
case 1:
|
|
||||||
if (buf[1] == 0x44) {
|
|
||||||
//__builtin_trap();
|
|
||||||
return 8;
|
|
||||||
}
|
|
||||||
|
|
||||||
break;
|
|
||||||
case 0xff:
|
|
||||||
if (buf[2] == 0xff) {
|
|
||||||
if (buf[1] == 0x44) {
|
|
||||||
//*(char *)(0xdeadbeef) = 1;
|
|
||||||
return 9;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
return 1;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
int parse_pe(const uint8_t *data, int size)
|
|
||||||
{
|
|
||||||
HANDLE file = NULL;
|
|
||||||
DWORD fileSize = NULL;
|
|
||||||
DWORD bytesRead = NULL;
|
|
||||||
LPVOID fileData = NULL;
|
|
||||||
PIMAGE_DOS_HEADER dosHeader = {};
|
|
||||||
PIMAGE_NT_HEADERS imageNTHeaders = {};
|
|
||||||
PIMAGE_SECTION_HEADER sectionHeader = {};
|
|
||||||
PIMAGE_SECTION_HEADER importSection = {};
|
|
||||||
IMAGE_IMPORT_DESCRIPTOR* importDescriptor = {};
|
|
||||||
PIMAGE_THUNK_DATA thunkData = {};
|
|
||||||
DWORD thunk = NULL;
|
|
||||||
DWORD rawOffset = NULL;
|
|
||||||
|
|
||||||
// allocate heap
|
|
||||||
fileSize = size;
|
|
||||||
fileData = (void *)data;
|
|
||||||
|
|
||||||
// IMAGE_DOS_HEADER
|
|
||||||
dosHeader = (PIMAGE_DOS_HEADER)fileData;
|
|
||||||
|
|
||||||
printf("******* DOS HEADER *******\n");
|
|
||||||
printf("\t0x%x\t\tMagic number\n", dosHeader->e_magic);
|
|
||||||
/*
|
|
||||||
printf("\t0x%x\t\tBytes on last page of file\n", dosHeader->e_cblp);
|
|
||||||
printf("\t0x%x\t\tPages in file\n", dosHeader->e_cp);
|
|
||||||
printf("\t0x%x\t\tRelocations\n", dosHeader->e_crlc);
|
|
||||||
printf("\t0x%x\t\tSize of header in paragraphs\n", dosHeader->e_cparhdr);
|
|
||||||
printf("\t0x%x\t\tMinimum extra paragraphs needed\n", dosHeader->e_minalloc);
|
|
||||||
printf("\t0x%x\t\tMaximum extra paragraphs needed\n", dosHeader->e_maxalloc);
|
|
||||||
printf("\t0x%x\t\tInitial (relative) SS value\n", dosHeader->e_ss);
|
|
||||||
printf("\t0x%x\t\tInitial SP value\n", dosHeader->e_sp);
|
|
||||||
printf("\t0x%x\t\tInitial SP value\n", dosHeader->e_sp);
|
|
||||||
printf("\t0x%x\t\tChecksum\n", dosHeader->e_csum);
|
|
||||||
printf("\t0x%x\t\tInitial IP value\n", dosHeader->e_ip);
|
|
||||||
printf("\t0x%x\t\tInitial (relative) CS value\n", dosHeader->e_cs);
|
|
||||||
printf("\t0x%x\t\tFile address of relocation table\n", dosHeader->e_lfarlc);
|
|
||||||
printf("\t0x%x\t\tOverlay number\n", dosHeader->e_ovno);
|
|
||||||
printf("\t0x%x\t\tOEM identifier (for e_oeminfo)\n", dosHeader->e_oemid);
|
|
||||||
printf("\t0x%x\t\tOEM information; e_oemid specific\n", dosHeader->e_oeminfo);
|
|
||||||
printf("\t0x%x\t\tFile address of new exe header\n", dosHeader->e_lfanew);
|
|
||||||
*/
|
|
||||||
// IMAGE_NT_HEADERS
|
|
||||||
imageNTHeaders = (PIMAGE_NT_HEADERS)((DWORD)fileData + dosHeader->e_lfanew);
|
|
||||||
/*
|
|
||||||
printf("\n******* NT HEADERS *******\n");
|
|
||||||
printf("\t%x\t\tSignature\n", imageNTHeaders->Signature);
|
|
||||||
|
|
||||||
// FILE_HEADER
|
|
||||||
printf("\n******* FILE HEADER *******\n");
|
|
||||||
printf("\t0x%x\t\tMachine\n", imageNTHeaders->FileHeader.Machine);
|
|
||||||
printf("\t0x%x\t\tNumber of Sections\n", imageNTHeaders->FileHeader.NumberOfSections);
|
|
||||||
printf("\t0x%x\tTime Stamp\n", imageNTHeaders->FileHeader.TimeDateStamp);
|
|
||||||
printf("\t0x%x\t\tPointer to Symbol Table\n", imageNTHeaders->FileHeader.PointerToSymbolTable);
|
|
||||||
printf("\t0x%x\t\tNumber of Symbols\n", imageNTHeaders->FileHeader.NumberOfSymbols);
|
|
||||||
printf("\t0x%x\t\tSize of Optional Header\n", imageNTHeaders->FileHeader.SizeOfOptionalHeader);
|
|
||||||
printf("\t0x%x\t\tCharacteristics\n", imageNTHeaders->FileHeader.Characteristics);
|
|
||||||
|
|
||||||
// OPTIONAL_HEADER
|
|
||||||
printf("\n******* OPTIONAL HEADER *******\n");
|
|
||||||
printf("\t0x%x\t\tMagic\n", imageNTHeaders->OptionalHeader.Magic);
|
|
||||||
printf("\t0x%x\t\tMajor Linker Version\n", imageNTHeaders->OptionalHeader.MajorLinkerVersion);
|
|
||||||
printf("\t0x%x\t\tMinor Linker Version\n", imageNTHeaders->OptionalHeader.MinorLinkerVersion);
|
|
||||||
printf("\t0x%x\t\tSize Of Code\n", imageNTHeaders->OptionalHeader.SizeOfCode);
|
|
||||||
printf("\t0x%x\t\tSize Of Initialized Data\n", imageNTHeaders->OptionalHeader.SizeOfInitializedData);
|
|
||||||
printf("\t0x%x\t\tSize Of UnInitialized Data\n", imageNTHeaders->OptionalHeader.SizeOfUninitializedData);
|
|
||||||
printf("\t0x%x\t\tAddress Of Entry Point (.text)\n", imageNTHeaders->OptionalHeader.AddressOfEntryPoint);
|
|
||||||
printf("\t0x%x\t\tBase Of Code\n", imageNTHeaders->OptionalHeader.BaseOfCode);
|
|
||||||
//printf("\t0x%x\t\tBase Of Data\n", imageNTHeaders->OptionalHeader.BaseOfData);
|
|
||||||
printf("\t0x%x\t\tImage Base\n", imageNTHeaders->OptionalHeader.ImageBase);
|
|
||||||
printf("\t0x%x\t\tSection Alignment\n", imageNTHeaders->OptionalHeader.SectionAlignment);
|
|
||||||
printf("\t0x%x\t\tFile Alignment\n", imageNTHeaders->OptionalHeader.FileAlignment);
|
|
||||||
printf("\t0x%x\t\tMajor Operating System Version\n", imageNTHeaders->OptionalHeader.MajorOperatingSystemVersion);
|
|
||||||
printf("\t0x%x\t\tMinor Operating System Version\n", imageNTHeaders->OptionalHeader.MinorOperatingSystemVersion);
|
|
||||||
printf("\t0x%x\t\tMajor Image Version\n", imageNTHeaders->OptionalHeader.MajorImageVersion);
|
|
||||||
printf("\t0x%x\t\tMinor Image Version\n", imageNTHeaders->OptionalHeader.MinorImageVersion);
|
|
||||||
printf("\t0x%x\t\tMajor Subsystem Version\n", imageNTHeaders->OptionalHeader.MajorSubsystemVersion);
|
|
||||||
printf("\t0x%x\t\tMinor Subsystem Version\n", imageNTHeaders->OptionalHeader.MinorSubsystemVersion);
|
|
||||||
printf("\t0x%x\t\tWin32 Version Value\n", imageNTHeaders->OptionalHeader.Win32VersionValue);
|
|
||||||
printf("\t0x%x\t\tSize Of Image\n", imageNTHeaders->OptionalHeader.SizeOfImage);
|
|
||||||
printf("\t0x%x\t\tSize Of Headers\n", imageNTHeaders->OptionalHeader.SizeOfHeaders);
|
|
||||||
printf("\t0x%x\t\tCheckSum\n", imageNTHeaders->OptionalHeader.CheckSum);
|
|
||||||
printf("\t0x%x\t\tSubsystem\n", imageNTHeaders->OptionalHeader.Subsystem);
|
|
||||||
printf("\t0x%x\t\tDllCharacteristics\n", imageNTHeaders->OptionalHeader.DllCharacteristics);
|
|
||||||
printf("\t0x%x\t\tSize Of Stack Reserve\n", imageNTHeaders->OptionalHeader.SizeOfStackReserve);
|
|
||||||
printf("\t0x%x\t\tSize Of Stack Commit\n", imageNTHeaders->OptionalHeader.SizeOfStackCommit);
|
|
||||||
printf("\t0x%x\t\tSize Of Heap Reserve\n", imageNTHeaders->OptionalHeader.SizeOfHeapReserve);
|
|
||||||
printf("\t0x%x\t\tSize Of Heap Commit\n", imageNTHeaders->OptionalHeader.SizeOfHeapCommit);
|
|
||||||
printf("\t0x%x\t\tLoader Flags\n", imageNTHeaders->OptionalHeader.LoaderFlags);
|
|
||||||
printf("\t0x%x\t\tNumber Of Rva And Sizes\n", imageNTHeaders->OptionalHeader.NumberOfRvaAndSizes);
|
|
||||||
|
|
||||||
// DATA_DIRECTORIES
|
|
||||||
printf("\n******* DATA DIRECTORIES *******\n");
|
|
||||||
printf("\tExport Directory Address: 0x%x; Size: 0x%x\n", imageNTHeaders->OptionalHeader.DataDirectory[0].VirtualAddress, imageNTHeaders->OptionalHeader.DataDirectory[0].Size);
|
|
||||||
printf("\tImport Directory Address: 0x%x; Size: 0x%x\n", imageNTHeaders->OptionalHeader.DataDirectory[1].VirtualAddress, imageNTHeaders->OptionalHeader.DataDirectory[1].Size);
|
|
||||||
*/
|
|
||||||
return 0;
|
|
||||||
// SECTION_HEADERS
|
|
||||||
printf("\n******* SECTION HEADERS *******\n");
|
|
||||||
// get offset to first section headeer
|
|
||||||
DWORD sectionLocation = (DWORD)imageNTHeaders + sizeof(DWORD) + (DWORD)(sizeof(IMAGE_FILE_HEADER)) + (DWORD)imageNTHeaders->FileHeader.SizeOfOptionalHeader;
|
|
||||||
DWORD sectionSize = (DWORD)sizeof(IMAGE_SECTION_HEADER);
|
|
||||||
|
|
||||||
// get offset to the import directory RVA
|
|
||||||
DWORD importDirectoryRVA = imageNTHeaders->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress;
|
|
||||||
// print section data
|
|
||||||
for (int i = 0; i < imageNTHeaders->FileHeader.NumberOfSections; i++) {
|
|
||||||
sectionHeader = (PIMAGE_SECTION_HEADER)sectionLocation;
|
|
||||||
printf("\t%s\n", sectionHeader->Name);
|
|
||||||
printf("\t\t0x%x\t\tVirtual Size\n", sectionHeader->Misc.VirtualSize);
|
|
||||||
printf("\t\t0x%x\t\tVirtual Address\n", sectionHeader->VirtualAddress);
|
|
||||||
/*
|
|
||||||
printf("\t\t0x%x\t\tSize Of Raw Data\n", sectionHeader->SizeOfRawData);
|
|
||||||
printf("\t\t0x%x\t\tPointer To Raw Data\n", sectionHeader->PointerToRawData);
|
|
||||||
printf("\t\t0x%x\t\tPointer To Relocations\n", sectionHeader->PointerToRelocations);
|
|
||||||
printf("\t\t0x%x\t\tPointer To Line Numbers\n", sectionHeader->PointerToLinenumbers);
|
|
||||||
printf("\t\t0x%x\t\tNumber Of Relocations\n", sectionHeader->NumberOfRelocations);
|
|
||||||
printf("\t\t0x%x\t\tNumber Of Line Numbers\n", sectionHeader->NumberOfLinenumbers);
|
|
||||||
printf("\t\t0x%x\tCharacteristics\n", sectionHeader->Characteristics);
|
|
||||||
*/
|
|
||||||
// save section that contains import directory table
|
|
||||||
if (importDirectoryRVA >= sectionHeader->VirtualAddress && importDirectoryRVA < sectionHeader->VirtualAddress + sectionHeader->Misc.VirtualSize) {
|
|
||||||
importSection = sectionHeader;
|
|
||||||
}
|
|
||||||
sectionLocation += sectionSize;
|
|
||||||
}
|
|
||||||
|
|
||||||
// get file offset to import table
|
|
||||||
rawOffset = (DWORD)fileData + importSection->PointerToRawData;
|
|
||||||
|
|
||||||
// get pointer to import descriptor's file offset. Note that the formula for calculating file offset is: imageBaseAddress + pointerToRawDataOfTheSectionContainingRVAofInterest + (RVAofInterest - SectionContainingRVAofInterest.VirtualAddress)
|
|
||||||
importDescriptor = (IMAGE_IMPORT_DESCRIPTOR*)(rawOffset + (imageNTHeaders->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress - importSection->VirtualAddress));
|
|
||||||
|
|
||||||
printf("\n******* DLL IMPORTS *******\n");
|
|
||||||
for (; importDescriptor->Name != 0; importDescriptor++) {
|
|
||||||
// imported dll modules
|
|
||||||
printf("\t%s\n", rawOffset + (importDescriptor->Name - importSection->VirtualAddress));
|
|
||||||
thunk = importDescriptor->OriginalFirstThunk == 0 ? importDescriptor->FirstThunk : importDescriptor->OriginalFirstThunk;
|
|
||||||
thunkData = (PIMAGE_THUNK_DATA)(rawOffset + (thunk - importSection->VirtualAddress));
|
|
||||||
|
|
||||||
// dll exported functions
|
|
||||||
for (; thunkData->u1.AddressOfData != 0; thunkData++) {
|
|
||||||
//a cheap and probably non-reliable way of checking if the function is imported via its ordinal number ¯\_(ツ)_/¯
|
|
||||||
if (thunkData->u1.AddressOfData > 0x80000000) {
|
|
||||||
//show lower bits of the value to get the ordinal ¯\_(ツ)_/¯
|
|
||||||
printf("\t\tOrdinal: %x\n", (WORD)thunkData->u1.AddressOfData);
|
|
||||||
} else {
|
|
||||||
printf("\t\t%s\n", (rawOffset + (thunkData->u1.AddressOfData - importSection->VirtualAddress + 2)));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
int load_stbi(const uint8_t *data, int size)
|
|
||||||
{
|
|
||||||
int w;
|
|
||||||
int h;
|
|
||||||
int channels;
|
|
||||||
|
|
||||||
const unsigned char * img = stbi_load_from_memory(data, size, &w, &h, &channels, 0);
|
|
||||||
if (img) { stbi_image_free((void *)img); }
|
|
||||||
// STBI_FREE((void *)img); }
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
extern "C"
|
|
||||||
int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) {
|
|
||||||
return target_func(Data, Size);
|
|
||||||
|
|
||||||
if(Size > 0x4000) return 0;
|
|
||||||
|
|
||||||
int size = Size;
|
|
||||||
const unsigned char * data = Data;
|
|
||||||
return load_stbi(data, size);
|
|
||||||
//return parse_pe(data, size);
|
|
||||||
}
|
|
||||||
|
|
@ -1,176 +0,0 @@
|
|||||||
//! A libfuzzer-like fuzzer with llmp-multithreading support and restarts
|
|
||||||
//! The example harness is built for libpng.
|
|
||||||
|
|
||||||
#[cfg(windows)]
|
|
||||||
use std::{env, path::PathBuf};
|
|
||||||
|
|
||||||
#[cfg(windows)]
|
|
||||||
use libafl::{
|
|
||||||
bolts::{shmem::Win32ShMem, tuples::tuple_list},
|
|
||||||
corpus::{
|
|
||||||
Corpus, InMemoryCorpus, IndexesLenTimeMinimizerCorpusScheduler, OnDiskCorpus,
|
|
||||||
QueueCorpusScheduler,
|
|
||||||
},
|
|
||||||
events::setup_restarting_mgr,
|
|
||||||
executors::{inprocess::InProcessExecutor, ExitKind},
|
|
||||||
feedbacks::{CrashFeedback, MaxMapFeedback, TimeFeedback, TimeoutFeedback},
|
|
||||||
fuzzer::{Fuzzer, StdFuzzer},
|
|
||||||
mutators::scheduled::{havoc_mutations, StdScheduledMutator},
|
|
||||||
mutators::token_mutations::Tokens,
|
|
||||||
observers::{HitcountsMapObserver, StdMapObserver, TimeObserver},
|
|
||||||
stages::mutational::StdMutationalStage,
|
|
||||||
state::{HasCorpus, HasMetadata, State},
|
|
||||||
stats::SimpleStats,
|
|
||||||
utils::{current_nanos, StdRand},
|
|
||||||
Error,
|
|
||||||
};
|
|
||||||
|
|
||||||
/// We will interact with a C++ target, so use external c functionality
|
|
||||||
#[cfg(windows)]
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// The main fn, usually parsing parameters, and starting the fuzzer
|
|
||||||
pub fn main() {
|
|
||||||
// Registry the metadata types used in this fuzzer
|
|
||||||
// Needed only on no_std
|
|
||||||
//RegistryBuilder::register::<Tokens>();
|
|
||||||
|
|
||||||
#[cfg(windows)]
|
|
||||||
println!(
|
|
||||||
"Workdir: {:?}",
|
|
||||||
env::current_dir().unwrap().to_string_lossy().to_string()
|
|
||||||
);
|
|
||||||
|
|
||||||
#[cfg(not(windows))]
|
|
||||||
todo!("Example currently only supports Windows.");
|
|
||||||
|
|
||||||
#[cfg(windows)]
|
|
||||||
fuzz(
|
|
||||||
vec![PathBuf::from("./corpus")],
|
|
||||||
PathBuf::from("./crashes"),
|
|
||||||
1337,
|
|
||||||
)
|
|
||||||
.expect("An error occurred while fuzzing");
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Not supported on unix right now
|
|
||||||
//#[cfg(cfg)]
|
|
||||||
//fn fuzz(_corpus_dirs: Vec<PathBuf>, _objective_dir: PathBuf, _broker_port: u16) -> Result<(), ()> {
|
|
||||||
// todo!("Example not supported on Unix");
|
|
||||||
//}
|
|
||||||
|
|
||||||
/// The actual fuzzer
|
|
||||||
#[cfg(windows)]
|
|
||||||
fn fuzz(corpus_dirs: Vec<PathBuf>, objective_dir: PathBuf, broker_port: u16) -> Result<(), Error> {
|
|
||||||
// The wrapped harness function, calling out to the LLVM-style harness
|
|
||||||
let mut harness = |buf: &[u8]| {
|
|
||||||
unsafe { LLVMFuzzerTestOneInput(buf.as_ptr(), buf.len()) };
|
|
||||||
ExitKind::Ok
|
|
||||||
};
|
|
||||||
|
|
||||||
// 'While the stats are state, they are usually used in the broker - which is likely never restarted
|
|
||||||
let stats = SimpleStats::new(|s| println!("{}", s));
|
|
||||||
|
|
||||||
// The restarting state will spawn the same process again as child, then restarted it each time it crashes.
|
|
||||||
let (state, mut restarting_mgr) =
|
|
||||||
match setup_restarting_mgr::<_, _, Win32ShMem, _>(stats, broker_port) {
|
|
||||||
Ok(res) => res,
|
|
||||||
Err(err) => match err {
|
|
||||||
Error::ShuttingDown => {
|
|
||||||
return Ok(());
|
|
||||||
}
|
|
||||||
_ => {
|
|
||||||
panic!("Failed to setup the restarter: {}", err);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
// Create an observation channel using the coverage map
|
|
||||||
let edges_observer = HitcountsMapObserver::new(unsafe {
|
|
||||||
StdMapObserver::new_from_ptr("edges", __lafl_edges_map, __lafl_max_edges_size as usize)
|
|
||||||
});
|
|
||||||
|
|
||||||
// If not restarting, create a State from scratch
|
|
||||||
let mut state = state.unwrap_or_else(|| {
|
|
||||||
State::new(
|
|
||||||
// RNG
|
|
||||||
StdRand::with_seed(current_nanos()),
|
|
||||||
// Corpus that will be evolved, we keep it in memory for performance
|
|
||||||
InMemoryCorpus::new(),
|
|
||||||
// Feedbacks to rate the interestingness of an input
|
|
||||||
tuple_list!(
|
|
||||||
MaxMapFeedback::new_with_observer_track(&edges_observer, true, false),
|
|
||||||
TimeFeedback::new()
|
|
||||||
),
|
|
||||||
// Corpus in which we store solutions (crashes in this example),
|
|
||||||
// on disk so the user can get them after stopping the fuzzer
|
|
||||||
OnDiskCorpus::new(objective_dir).unwrap(),
|
|
||||||
// Feedbacks to recognize an input as solution
|
|
||||||
tuple_list!(CrashFeedback::new(), TimeoutFeedback::new()),
|
|
||||||
)
|
|
||||||
});
|
|
||||||
|
|
||||||
println!("We're a client, let's fuzz :)");
|
|
||||||
|
|
||||||
// Create a PNG dictionary if not existing
|
|
||||||
if state.metadata().get::<Tokens>().is_none() {
|
|
||||||
state.add_metadata(Tokens::new(vec![
|
|
||||||
vec![137, 80, 78, 71, 13, 10, 26, 10], // PNG header
|
|
||||||
"IHDR".as_bytes().to_vec(),
|
|
||||||
"IDAT".as_bytes().to_vec(),
|
|
||||||
"PLTE".as_bytes().to_vec(),
|
|
||||||
"IEND".as_bytes().to_vec(),
|
|
||||||
]));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Setup a basic mutator with a mutational stage
|
|
||||||
let mutator = StdScheduledMutator::new(havoc_mutations());
|
|
||||||
let stage = StdMutationalStage::new(mutator);
|
|
||||||
|
|
||||||
// A fuzzer with just one stage and a minimization+queue policy to get testcasess from the corpus
|
|
||||||
let scheduler = IndexesLenTimeMinimizerCorpusScheduler::new(QueueCorpusScheduler::new());
|
|
||||||
let mut fuzzer = StdFuzzer::new(tuple_list!(stage));
|
|
||||||
|
|
||||||
// Create the executor for an in-process function with just one observer for edge coverage
|
|
||||||
let mut executor = InProcessExecutor::new(
|
|
||||||
"in-process(edges)",
|
|
||||||
&mut harness,
|
|
||||||
tuple_list!(edges_observer, TimeObserver::new("time")),
|
|
||||||
&mut state,
|
|
||||||
&mut restarting_mgr,
|
|
||||||
)?;
|
|
||||||
|
|
||||||
// The actual target run starts here.
|
|
||||||
// Call LLVMFUzzerInitialize() if present.
|
|
||||||
unsafe {
|
|
||||||
if afl_libfuzzer_init() == -1 {
|
|
||||||
println!("Warning: LLVMFuzzerInitialize failed with -1")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// In case the corpus is empty (on first run), reset
|
|
||||||
if state.corpus().count() < 1 {
|
|
||||||
state
|
|
||||||
.load_initial_inputs(&mut executor, &mut restarting_mgr, &scheduler, &corpus_dirs)
|
|
||||||
.expect(&format!(
|
|
||||||
"Failed to load initial corpus at {:?}",
|
|
||||||
&corpus_dirs
|
|
||||||
));
|
|
||||||
println!("We imported {} inputs from disk.", state.corpus().count());
|
|
||||||
}
|
|
||||||
|
|
||||||
fuzzer.fuzz_loop(&mut state, &mut executor, &mut restarting_mgr, &scheduler)?;
|
|
||||||
|
|
||||||
// Never reached
|
|
||||||
Ok(())
|
|
||||||
}
|
|
@ -1,20 +0,0 @@
|
|||||||
mkdir crashes
|
|
||||||
del .\.libfuzzer_test.elf
|
|
||||||
|
|
||||||
cargo build --example libfuzzer_windows --release
|
|
||||||
timeout /T 1
|
|
||||||
cp ..\..\target\release\examples\libfuzzer_windows.exe .\.libfuzzer_test.exe
|
|
||||||
timeout /T 1
|
|
||||||
|
|
||||||
# The broker
|
|
||||||
start .\.libfuzzer_test.exe
|
|
||||||
# Give the broker time to spawn
|
|
||||||
timeout /T 1
|
|
||||||
echo "Spawning client"
|
|
||||||
start .\.libfuzzer_test.exe
|
|
||||||
# .\.libfuzzer_test.exe > nul
|
|
||||||
|
|
||||||
timeout /T 10
|
|
||||||
echo "Finished fuzzing for a bit"
|
|
||||||
TASKKILL /IM .libfuzzer_test.exe
|
|
||||||
del .libfuzzer_test.exe
|
|