Merge branch 'dev' of github.com:AFLplusplus/libAFLrs into dev

This commit is contained in:
Dominik Maier 2021-03-14 18:43:20 +01:00
commit 73ceb928ae
15 changed files with 590 additions and 6 deletions

View File

@ -13,4 +13,5 @@ members = [
#example fuzzers
"fuzzers/libfuzzer_libpng",
"fuzzers/libfuzzer_libmozjpeg",
"fuzzers/libfuzzer_libpng_cmpalloc",
]

View File

@ -23,7 +23,7 @@ We're still working on the documentation. In the meantime, you can watch the Vid
+ ~~Minset corpus scheduler~~ still doc missing
+ Win32 shared mem and crash handler to have Windows in-process executor
+ Other feedbacks examples (e.g. maximize allocations to spot OOMs)
+ ~~Other feedbacks examples (e.g. maximize allocations to spot OOMs)~~
+ Other objectives examples (e.g. execution of a given program point)
+ ~~A macro crate with derive directives (e.g. for SerdeAny impl)~~ just `derive(SerdeAny)`, missing doc.
+ Good documentation

View File

@ -2,7 +2,7 @@
- [x] ~~Minset corpus scheduler~~ still doc missing
- [ ] Win32 shared mem and crash handler to have Windows in-process executor
- [ ] Other feedbacks examples (e.g. maximize allocations to spot OOMs)
- [x] Other feedbacks examples (e.g. maximize allocations to spot OOMs)
- [ ] Other objectives examples (e.g. execution of a given program point)
- [ ] Objective-Specific Corpuses (named per objective)
- [x] A macro crate with derive directives (e.g. for SerdeAny impl).

View File

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

View File

@ -0,0 +1,31 @@
[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

@ -0,0 +1,28 @@
# 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

@ -0,0 +1,109 @@
// 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 cwd = env::current_dir().unwrap().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=../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", &cwd);
// 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.

After

Width:  |  Height:  |  Size: 218 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 376 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 228 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 427 B

View File

@ -0,0 +1,197 @@
// 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

@ -0,0 +1,199 @@
//! 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, IndexesLenTimeMinimizerCorpusScheduler, OnDiskCorpus,
QueueCorpusScheduler,
},
events::setup_restarting_mgr,
executors::{inprocess::InProcessExecutor, Executor, ExitKind},
feedbacks::{CrashFeedback, MaxMapFeedback, TimeFeedback},
fuzzer::{Fuzzer, HasCorpusScheduler, StdFuzzer},
inputs::Input,
mutators::{scheduled::HavocBytesMutator, token_mutations::Tokens},
observers::{HitcountsMapObserver, StdMapObserver, TimeObserver},
stages::mutational::StdMutationalStage,
state::{HasCorpus, HasMetadata, State},
stats::SimpleStats,
utils::{current_nanos, StdRand},
Error,
};
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 wrapped harness function, calling out to the LLVM-style harness
#[cfg(unix)]
fn harness<E, I>(_executor: &E, buf: &[u8]) -> ExitKind
where
E: Executor<I>,
I: Input,
{
// println!("{:?}", buf);
unsafe {
LLVMFuzzerTestOneInput(buf.as_ptr(), buf.len());
}
ExitKind::Ok
}
/// 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),
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()),
)
});
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 = HavocBytesMutator::default();
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 fuzzer = StdFuzzer::new(scheduler, 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,cmps,allocs)",
harness,
tuple_list!(
edges_observer,
cmps_observer,
allocs_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,
fuzzer.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)?;
// Never reached
Ok(())
}

View File

@ -0,0 +1,17 @@
#!/bin/sh
mkdir -p ./crashes
cargo build --example libfuzzer_libpng --release || exit 1
cp ../../target/release/examples/libfuzzer_libpng ./.libfuzzer_test.elf
# The broker
RUST_BACKTRACE=full taskset 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 1 ./.libfuzzer_test.elf 2>/dev/null
killall .libfuzzer_test.elf
rm -rf ./.libfuzzer_test.elf

View File

@ -3,7 +3,7 @@
#include <stdlib.h>
#include <string.h>
#define MAP_SIZE 65536
#define MAP_SIZE (16*1024)
int orig_argc;
char **orig_argv;
@ -156,9 +156,10 @@ void *calloc(size_t nmemb, size_t size) {
k &= MAP_SIZE - 1;
__lafl_alloc_map[k] = MAX(__lafl_alloc_map[k], size);
void *result = realloc(NULL, size);
memset(result, 0, size);
return result;
void *ret = NULL;
posix_memalign(&ret, 1<<6, size);
memset(ret, 0, size);
return ret;
}