remove libfuzzer_runtime and use cc wrapper for mozjpeg

This commit is contained in:
Andrea Fioraldi 2021-03-29 15:56:54 +02:00
parent cbfe17b95c
commit 0c2a267075
41 changed files with 164 additions and 9302 deletions

View File

@ -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

View File

@ -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`.

View File

@ -1,3 +0,0 @@
#!/bin/bash
taskset -c 0 ./.libfuzzer_test.elf

View File

@ -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");
} }

View 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;
}

View 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();
}
}

View 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();
}
}

View File

@ -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 {

View File

@ -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

View File

@ -1,2 +0,0 @@
#!/bin/bash
killall -9 .libfuzzer_test.elf

View File

@ -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

View File

@ -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 = []

View File

@ -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(),
) )

View File

@ -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(),
) )

View File

@ -1 +0,0 @@
libpng-*

View File

@ -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

View File

@ -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.

View File

@ -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");
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 218 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 376 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 228 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 427 B

View File

@ -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;
}

View File

@ -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(())
}

View File

@ -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

View File

@ -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;
}
}

View File

@ -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
}
}

View File

@ -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")

View File

@ -1 +0,0 @@
libpng-*

View File

@ -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

View File

@ -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.

View File

@ -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");
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 218 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 376 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 228 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 427 B

View File

@ -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);
}

View File

@ -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(())
}

File diff suppressed because it is too large Load Diff

View File

@ -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