Merge pull request #30 from AFLplusplus/dev

Dev
This commit is contained in:
Andrea Fioraldi 2021-03-19 19:05:10 +01:00 committed by GitHub
commit 6e4c6a9b6b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
35 changed files with 1691 additions and 119 deletions

View File

@ -3,7 +3,7 @@ name: Build and Test
on:
push:
branches: [ main ]
pull_requests:
pull_request:
branches: [ main, dev ]
env:

View File

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

View File

@ -6,26 +6,43 @@ LibAFL is written and maintained by Andrea Fioraldi <andreafioraldi@gmail.com> a
It is released as Open Source Software under the [Apache v2](LICENSE-APACHE) or [MIT](LICENSE-MIT) licenses.
## Example usages
## Getting started
Clone the LibAFL repository with
```
git clone https://github.com/AFLplusplus/LibAFL
```
Build the library using
```
cargo build --release
```
Build the documentation with
```
cargo doc
```
We collect example fuzzers in `./fuzzers`. They can be build using `cargo build --example [fuzzer_name] --release`.
We collect example fuzzers in `./fuzzers`. They can be build using `cargo build --example [fuzzer_name] --release`
The best-tested fuzzer is `./fuzzers/libfuzzer_libpng`, a clone of libfuzzer using libafl for a libpng harness.
See its readme [here](./fuzzers/libfuzzer_libpng/README.md).
If you want to get a quick overview, run `cargo doc`.
Feel free to open issues or contact us directly. Thank you for your support. <3
## The Core Concepts
We're still working on the documentation. In the meantime, you can watch the Video from last year's Rc3, here:
The entire library is based on some core concepts that we think can generalize Fuzz Testing.
We're still working on extending the documentation.
In the meantime, you can watch the Video from last year's RC3, here:
[![Video explaining libAFL's core concepts](http://img.youtube.com/vi/3RWkT1Q5IV0/3.jpg)](http://www.youtube.com/watch?v=3RWkT1Q5IV0 "Fuzzers Like LEGO")
## Roadmap for release
+ ~~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 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
## Contributing
For further TODOs, see [TODO.md](./TODO.md)
Check the [TODO.md](./TODO.md) file for features that we plan to support.
For bugs, feel free to open issues or contact us directly. Thank you for your support. <3

View File

@ -2,21 +2,20 @@
- [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).
- [ ] Good documentation
- [ ] LLMP brotli compression
- [ ] Timeouts (timeout observer, objective)
- [ ] AFL-Style Forkserver Executor
- [ ] Restarting EventMgr could use forks on unix
- [x] Restarting EventMgr could use forks on unix
- [ ] Android Ashmem support
- [ ] Restart Count in Fuzzing Loop
- [ ] LAIN / structured fuzzing example
- [ ] Errors in the Fuzzer should exit the fuzz run
- [ ] More informative outpus, deeper introspection (stats, what mutation did x, etc.)
- [ ] Timeouts for executors
- [x] Timeouts for executors
- [ ] Timeout handling for llmp clients (no ping for n seconds -> treat as disconnected)
- [ ] LLMP Cross Machine Link (2 brokers connected via TCP)
- [ ] "Launcher" example that spawns broker + n clients

View File

@ -1,6 +1,7 @@
//! A libfuzzer-like fuzzer with llmp-multithreading support and restarts
//! The example harness is built for libpng.
use core::time::Duration;
use std::{env, path::PathBuf};
#[cfg(unix)]
@ -11,8 +12,8 @@ use libafl::{
QueueCorpusScheduler,
},
events::setup_restarting_mgr,
executors::{inprocess::InProcessExecutor, Executor, ExitKind},
feedbacks::{CrashFeedback, MaxMapFeedback, TimeFeedback},
executors::{inprocess::InProcessExecutor, Executor, ExitKind, TimeoutExecutor},
feedbacks::{CrashFeedback, MaxMapFeedback, TimeFeedback, TimeoutFeedback},
fuzzer::{Fuzzer, HasCorpusScheduler, StdFuzzer},
inputs::Input,
mutators::{scheduled::HavocBytesMutator, token_mutations::Tokens},
@ -117,7 +118,7 @@ fn fuzz(corpus_dirs: Vec<PathBuf>, objective_dir: PathBuf, broker_port: u16) ->
// 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()),
tuple_list!(CrashFeedback::new(), TimeoutFeedback::new()),
)
});
@ -143,13 +144,17 @@ fn fuzz(corpus_dirs: Vec<PathBuf>, objective_dir: PathBuf, broker_port: u16) ->
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)",
harness,
tuple_list!(edges_observer, TimeObserver::new("time")),
&mut state,
&mut restarting_mgr,
)?;
let mut executor = TimeoutExecutor::new(
InProcessExecutor::new(
"in-process(edges)",
harness,
tuple_list!(edges_observer, TimeObserver::new("time")),
&mut state,
&mut restarting_mgr,
)?,
// 10 seconds timeout
Duration::new(10, 0),
);
// The actual target run starts here.
// Call LLVMFUzzerInitialize() if present.

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

View File

@ -35,6 +35,7 @@ std = [] # print, sharedmap, ... support
runtime = [] # a runtime for clang inmem-executor
anymapdbg = ["serde_json"] # uses serde_json to Debug the anymap trait. Disable for smaller footprint.
derive = ["libafl_derive"] # provide derive(SerdeAny) macro.
llmp_small_maps = [] # reduces initial map size for llmp
[[example]]
name = "llmp_test"
@ -59,9 +60,11 @@ num_enum = "0.5.1"
[target.'cfg(unix)'.dependencies]
libc = "0.2" # For (*nix) libc
nix = "0.20.0"
uds = "0.2.3"
[target.'cfg(windows)'.dependencies]
windows = "0.3.1"
windows = "0.4.0"
uuid = { version = "0.8", features = ["v4"] }
[target.'cfg(windows)'.build-dependencies]
windows = "0.3.1"
windows = "0.4.0"

View File

@ -1,9 +1,10 @@
fn main() {
#[cfg(target_os = "windows")]
windows::build!(
windows::win32::system_services::HANDLE,
windows::win32::system_services::{HANDLE, BOOL, PAGE_TYPE, PSTR, ExitProcess},
windows::win32::windows_programming::CloseHandle,
// API needed for the shared memory
windows::win32::system_services::{CreateFileMappingA, OpenFileMappingA, MapViewOfFile, UnmapViewOfFile},
windows::win32::debug::{SetUnhandledExceptionFilter, EXCEPTION_POINTERS, EXCEPTION_RECORD, LPTOP_LEVEL_EXCEPTION_FILTER}
);
}

View File

@ -8,14 +8,16 @@ use core::{convert::TryInto, time::Duration};
#[cfg(all(unix, feature = "std"))]
use std::{thread, time};
use libafl::bolts::llmp::Tag;
#[cfg(all(unix, feature = "std"))]
use libafl::{
bolts::{llmp, shmem::UnixShMem},
Error,
};
const _TAG_SIMPLE_U32_V1: u32 = 0x51300321;
const _TAG_MATH_RESULT_V1: u32 = 0x77474331;
const _TAG_SIMPLE_U32_V1: Tag = 0x51300321;
const _TAG_MATH_RESULT_V1: Tag = 0x77474331;
const _TAG_1MEG_V1: Tag = 0xB1111161;
#[cfg(all(unix, feature = "std"))]
fn adder_loop(port: u16) -> ! {
@ -35,7 +37,11 @@ fn adder_loop(port: u16) -> ! {
current_result =
current_result.wrapping_add(u32::from_le_bytes(buf.try_into().unwrap()));
}
_ => println!("Adder Client ignored unknown message {}", tag),
_ => println!(
"Adder Client ignored unknown message {} with {} bytes",
tag,
buf.len()
),
};
}
@ -55,6 +61,19 @@ fn adder_loop(port: u16) -> ! {
}
}
#[cfg(all(unix, feature = "std"))]
fn large_msg_loop(port: u16) -> ! {
let mut client = llmp::LlmpClient::<UnixShMem>::create_attach_to_tcp(port).unwrap();
let meg_buf = [1u8; 1 << 20];
loop {
client.send_buf(_TAG_1MEG_V1, &meg_buf).unwrap();
println!("Sending the next megabyte");
thread::sleep(time::Duration::from_millis(100))
}
}
#[cfg(all(unix, feature = "std"))]
fn broker_message_hook(
client_id: u32,
@ -95,7 +114,7 @@ fn main() {
let mode = std::env::args()
.nth(1)
.expect("no mode specified, chose 'broker', 'ctr', or 'adder'");
.expect("no mode specified, chose 'broker', 'ctr', 'adder', or 'large'");
let port: u16 = std::env::args()
.nth(2)
.unwrap_or("1337".into())
@ -128,6 +147,9 @@ fn main() {
"adder" => {
adder_loop(port);
}
"large" => {
large_msg_loop(port);
}
_ => {
println!("No valid mode supplied");
}

View File

@ -92,6 +92,9 @@ use std::{
#[cfg(all(feature = "std", unix))]
use libc::c_char;
#[cfg(unix)]
use uds::{UnixListenerExt, UnixSocketAddr, UnixStreamExt};
#[cfg(unix)]
use crate::bolts::os::unix_signals::{c_void, setup_signal_handler, siginfo_t, Handler, Signal};
use crate::{
@ -102,20 +105,26 @@ use crate::{
use super::shmem::HasFd;
/// We'll start off with 256 megabyte maps per fuzzer client
const LLMP_PREF_INITIAL_MAP_SIZE: usize = 1 << 28;
#[cfg(not(feature = "llmp_small_maps"))]
const LLMP_CFG_INITIAL_MAP_SIZE: usize = 1 << 28;
/// If llmp_small_maps is set, we start off with 1 meg.
#[cfg(feature = "llmp_small_maps")]
const LLMP_CFG_INITIAL_MAP_SIZE: usize = 1 << 20;
/// What byte count to align messages to
/// LlmpMsg sizes (including header) will always be rounded up to be a multiple of this value
const LLMP_PREF_ALIGNNMENT: usize = 64;
const LLMP_CFG_ALIGNNMENT: usize = 64;
/// A msg fresh from the press: No tag got sent by the user yet
const LLMP_TAG_UNSET: u32 = 0xDEADAF;
const LLMP_TAG_UNSET: Tag = 0xDEADAF;
/// This message should not exist yet. Some bug in unsafe code!
const LLMP_TAG_UNINITIALIZED: u32 = 0xA143AF11;
/// The end of page mesasge
const LLMP_TAG_UNINITIALIZED: Tag = 0xA143AF11;
/// The end of page message
/// When receiving this, a new sharedmap needs to be allocated.
const LLMP_TAG_END_OF_PAGE: u32 = 0xAF1E0F1;
/// A new client for this broekr got added.
const LLMP_TAG_NEW_SHM_CLIENT: u32 = 0xC11E471;
const LLMP_TAG_END_OF_PAGE: Tag = 0xAF1E0F1;
/// A new client for this broker got added.
const LLMP_TAG_NEW_SHM_CLIENT: Tag = 0xC11E471;
/// The sender on this map is exiting (if broker exits, clients should exit gracefully);
const LLMP_TAG_EXITING: Tag = 0x13C5171;
/// An env var of this value indicates that the set value was a NULL PTR
const _NULL_ENV_STR: &str = "_NULL";
@ -197,19 +206,19 @@ unsafe fn llmp_msg_in_page(page: *const LlmpPage, msg: *const LlmpMsg) -> bool {
&& (page as *const u8).add((*page).size_total) > msg as *const u8
}
/// allign to LLMP_PREF_ALIGNNMENT=64 bytes
/// allign to LLMP_CFG_ALIGNNMENT=64 bytes
#[inline]
const fn llmp_align(to_align: usize) -> usize {
// check if we need to align first
if LLMP_PREF_ALIGNNMENT == 0 {
if LLMP_CFG_ALIGNNMENT == 0 {
return to_align;
}
// Then do the alignment
let modulo = to_align % LLMP_PREF_ALIGNNMENT;
let modulo = to_align % LLMP_CFG_ALIGNNMENT;
if modulo == 0 {
to_align
} else {
to_align + LLMP_PREF_ALIGNNMENT - modulo
to_align + LLMP_CFG_ALIGNNMENT - modulo
}
}
@ -233,7 +242,7 @@ fn msg_offset_from_env(env_name: &str) -> Result<Option<u64>, Error> {
fn new_map_size(max_alloc: usize) -> usize {
max(
max_alloc * 2 + EOP_MSG_SIZE + LLMP_PAGE_HEADER_LEN,
LLMP_PREF_INITIAL_MAP_SIZE,
LLMP_CFG_INITIAL_MAP_SIZE,
)
.next_power_of_two()
}
@ -447,7 +456,7 @@ where
{
#[cfg(all(feature = "std", unix))]
pub fn on_domain_socket(filename: &str) -> Result<Self, Error> {
match UnixListener::bind(filename) {
match UnixListener::bind_unix_addr(&UnixSocketAddr::new(filename).unwrap()) {
Ok(listener) => {
dbg!("We're the broker");
let mut broker = LlmpBroker::new()?;
@ -541,7 +550,7 @@ where
last_msg_sent: ptr::null_mut(),
out_maps: vec![LlmpSharedMap::new(
0,
SH::new_map(new_map_size(LLMP_PREF_INITIAL_MAP_SIZE))?,
SH::new_map(new_map_size(LLMP_CFG_INITIAL_MAP_SIZE))?,
)],
// drop pages to the broker if it already read them
keep_pages_forever,
@ -571,7 +580,7 @@ where
pub fn to_env(&self, env_name: &str) -> Result<(), Error> {
let current_out_map = self.out_maps.last().unwrap();
current_out_map.shmem.write_to_env(env_name)?;
current_out_map.msg_to_env(self.last_msg_sent, env_name)
unsafe { current_out_map.msg_to_env(self.last_msg_sent, env_name) }
}
/// Waits for this sender to be save to unmap.
@ -862,6 +871,7 @@ where
tag
)));
}
unsafe {
let msg = self.alloc_next(buf.len())?;
(*msg).tag = tag;
@ -927,7 +937,7 @@ where
pub fn to_env(&self, env_name: &str) -> Result<(), Error> {
let current_out_map = &self.current_recv_map;
current_out_map.shmem.write_to_env(env_name)?;
current_out_map.msg_to_env(self.last_msg_recvd, env_name)
unsafe { current_out_map.msg_to_env(self.last_msg_recvd, env_name) }
}
/// Create a Receiver, reattaching to an existing sender map.
@ -987,6 +997,11 @@ where
// Handle special, LLMP internal, messages.
match (*msg).tag {
LLMP_TAG_UNSET => panic!("BUG: Read unallocated msg"),
LLMP_TAG_EXITING => {
// The other side is done.
assert_eq!((*msg).buf_len, 0);
return Err(Error::ShuttingDown);
}
LLMP_TAG_END_OF_PAGE => {
#[cfg(feature = "std")]
dbg!("Got end of page, allocing next");
@ -1195,14 +1210,16 @@ where
/// Store this msg offset to env_name + _OFFSET env variable.
/// It can be restored using msg_from_env with the same env_name later.
/// # Safety
/// This function will dereference the msg ptr, make sure it's valid.
#[cfg(feature = "std")]
pub fn msg_to_env(&self, msg: *const LlmpMsg, map_env_name: &str) -> Result<(), Error> {
pub unsafe fn msg_to_env(&self, msg: *const LlmpMsg, map_env_name: &str) -> Result<(), Error> {
if msg.is_null() {
env::set_var(&format!("{}_OFFSET", map_env_name), _NULL_ENV_STR)
} else {
env::set_var(
&format!("{}_OFFSET", map_env_name),
format!("{}", unsafe { self.msg_to_offset(msg) }?),
format!("{}", self.msg_to_offset(msg)?),
)
};
Ok(())
@ -1319,9 +1336,8 @@ where
msg.copy_to_nonoverlapping(out, size_of::<LlmpMsg>() + (*msg).buf_len_padded as usize);
(*out).buf_len_padded = actual_size;
/* We need to replace the message ID with our own */
match self.llmp_out.send(out) {
Err(e) => panic!("Error sending msg: {:?}", e),
_ => (),
if let Err(e) = self.llmp_out.send(out) {
panic!("Error sending msg: {:?}", e)
};
self.llmp_out.last_msg_sent = out;
Ok(())
@ -1389,6 +1405,9 @@ where
None => (),
}
}
self.llmp_out
.send_buf(LLMP_TAG_EXITING, &[])
.expect("Error when shutting down broker: Could not send LLMP_TAG_EXITING msg.");
}
/// Broadcasts the given buf to all lients
@ -1422,7 +1441,7 @@ where
// Tcp out map sends messages from background thread tcp server to foreground client
let tcp_out_map = LlmpSharedMap::new(
llmp_tcp_id,
SH::new_map(new_map_size(LLMP_PREF_INITIAL_MAP_SIZE))?,
SH::new_map(new_map_size(LLMP_CFG_INITIAL_MAP_SIZE))?,
);
let tcp_out_map_str = tcp_out_map.shmem.shm_str();
let tcp_out_map_size = tcp_out_map.shmem.map().len();
@ -1465,7 +1484,7 @@ where
(*msg).tag = LLMP_TAG_NEW_SHM_CLIENT;
let pageinfo = (*msg).buf.as_mut_ptr() as *mut LlmpPayloadSharedMapInfo;
(*pageinfo).shm_str = new_client_map_str;
(*pageinfo).map_size = LLMP_PREF_INITIAL_MAP_SIZE;
(*pageinfo).map_size = LLMP_CFG_INITIAL_MAP_SIZE;
match new_client_sender.send(msg) {
Ok(()) => (),
Err(e) => println!("Error forwarding client on map: {:?}", e),
@ -1481,10 +1500,12 @@ where
.to_string_lossy()
.into_owned()
.parse()
.expect(&format!(
"ShmId is not a valid int file descriptor: {:?}",
broadcast_str_initial
));
.unwrap_or_else(|_| {
panic!(
"ShmId is not a valid int file descriptor: {:?}",
broadcast_str_initial
)
});
match sendmsg(
stream.as_raw_fd(),
@ -1528,7 +1549,7 @@ where
let pageinfo =
(*msg).buf.as_mut_ptr() as *mut LlmpPayloadSharedMapInfo;
(*pageinfo).shm_str = fdstr;
(*pageinfo).map_size = LLMP_PREF_INITIAL_MAP_SIZE;
(*pageinfo).map_size = LLMP_CFG_INITIAL_MAP_SIZE;
match new_client_sender.send(msg) {
Ok(()) => (),
Err(e) => {
@ -1739,7 +1760,7 @@ where
last_msg_sent: ptr::null_mut(),
out_maps: vec![LlmpSharedMap::new(
0,
SH::new_map(new_map_size(LLMP_PREF_INITIAL_MAP_SIZE))?,
SH::new_map(new_map_size(LLMP_CFG_INITIAL_MAP_SIZE))?,
)],
// drop pages to the broker if it already read them
keep_pages_forever: false,
@ -1840,7 +1861,7 @@ where
let ret = Self::new(LlmpSharedMap::existing(SH::existing_from_shm_slice(
&new_broker_map_str,
LLMP_PREF_INITIAL_MAP_SIZE,
LLMP_CFG_INITIAL_MAP_SIZE,
)?))?;
stream.write_all(ret.sender.out_maps.first().unwrap().shmem.shm_slice())?;
@ -1858,7 +1879,7 @@ where
#[cfg(all(unix, feature = "std"))]
/// Create a LlmpClient, getting the ID from a given filename
pub fn create_attach_to_unix(filename: &str) -> Result<Self, Error> {
let stream = UnixStream::connect(filename)?;
let stream = UnixStream::connect_to_unix_addr(&UnixSocketAddr::new(filename).unwrap())?;
println!("Connected to socket {}", filename);
let mut buf = [0u8; 5];
@ -1884,7 +1905,7 @@ where
let ret = Self::new(LlmpSharedMap::existing(SH::existing_from_shm_slice(
&fdstr,
LLMP_PREF_INITIAL_MAP_SIZE,
LLMP_CFG_INITIAL_MAP_SIZE,
)?))?;
match sendmsg(

View File

@ -1,2 +1,4 @@
#[cfg(unix)]
pub mod unix_signals;
#[cfg(windows)]
pub mod windows_exceptions;

View File

@ -22,7 +22,7 @@ use crate::Error;
pub use libc::{c_void, siginfo_t};
#[derive(IntoPrimitive, TryFromPrimitive, Hash, Clone, Copy)]
#[derive(IntoPrimitive, TryFromPrimitive, Clone, Copy)]
#[repr(i32)]
pub enum Signal {
SigAbort = SIGABRT,

View File

@ -0,0 +1,332 @@
pub use crate::bolts::bindings::windows::win32::debug::EXCEPTION_POINTERS;
use crate::{bolts::bindings::windows::win32::debug::SetUnhandledExceptionFilter, Error};
use alloc::vec::Vec;
use core::{
cell::UnsafeCell,
convert::TryFrom,
fmt::{self, Display, Formatter},
ptr::write_volatile,
sync::atomic::{compiler_fence, Ordering},
};
use std::os::raw::{c_long, c_void};
use num_enum::{IntoPrimitive, TryFromPrimitive};
const EXCEPTION_CONTINUE_EXECUTION: c_long = -1;
//const EXCEPTION_CONTINUE_SEARCH: c_long = 0;
const EXCEPTION_EXECUTE_HANDLER: c_long = 1;
// From https://github.com/wine-mirror/wine/blob/master/include/winnt.h#L611
pub const STATUS_WAIT_0: u32 = 0x00000000;
pub const STATUS_ABANDONED_WAIT_0: u32 = 0x00000080;
pub const STATUS_USER_APC: u32 = 0x000000C0;
pub const STATUS_TIMEOUT: u32 = 0x00000102;
pub const STATUS_PENDING: u32 = 0x00000103;
pub const STATUS_SEGMENT_NOTIFICATION: u32 = 0x40000005;
pub const STATUS_FATAL_APP_EXIT: u32 = 0x40000015;
pub const STATUS_GUARD_PAGE_VIOLATION: u32 = 0x80000001;
pub const STATUS_DATATYPE_MISALIGNMENT: u32 = 0x80000002;
pub const STATUS_BREAKPOINT: u32 = 0x80000003;
pub const STATUS_SINGLE_STEP: u32 = 0x80000004;
pub const STATUS_LONGJUMP: u32 = 0x80000026;
pub const STATUS_UNWIND_CONSOLIDATE: u32 = 0x80000029;
pub const STATUS_ACCESS_VIOLATION: u32 = 0xC0000005;
pub const STATUS_IN_PAGE_ERROR: u32 = 0xC0000006;
pub const STATUS_INVALID_HANDLE: u32 = 0xC0000008;
pub const STATUS_NO_MEMORY: u32 = 0xC0000017;
pub const STATUS_ILLEGAL_INSTRUCTION: u32 = 0xC000001D;
pub const STATUS_NONCONTINUABLE_EXCEPTION: u32 = 0xC0000025;
pub const STATUS_INVALID_DISPOSITION: u32 = 0xC0000026;
pub const STATUS_ARRAY_BOUNDS_EXCEEDED: u32 = 0xC000008C;
pub const STATUS_FLOAT_DENORMAL_OPERAND: u32 = 0xC000008D;
pub const STATUS_FLOAT_DIVIDE_BY_ZERO: u32 = 0xC000008E;
pub const STATUS_FLOAT_INEXACT_RESULT: u32 = 0xC000008F;
pub const STATUS_FLOAT_INVALID_OPERATION: u32 = 0xC0000090;
pub const STATUS_FLOAT_OVERFLOW: u32 = 0xC0000091;
pub const STATUS_FLOAT_STACK_CHECK: u32 = 0xC0000092;
pub const STATUS_FLOAT_UNDERFLOW: u32 = 0xC0000093;
pub const STATUS_INTEGER_DIVIDE_BY_ZERO: u32 = 0xC0000094;
pub const STATUS_INTEGER_OVERFLOW: u32 = 0xC0000095;
pub const STATUS_PRIVILEGED_INSTRUCTION: u32 = 0xC0000096;
pub const STATUS_STACK_OVERFLOW: u32 = 0xC00000FD;
pub const STATUS_DLL_NOT_FOUND: u32 = 0xC0000135;
pub const STATUS_ORDINAL_NOT_FOUND: u32 = 0xC0000138;
pub const STATUS_ENTRYPOINT_NOT_FOUND: u32 = 0xC0000139;
pub const STATUS_CONTROL_C_EXIT: u32 = 0xC000013A;
pub const STATUS_DLL_INIT_FAILED: u32 = 0xC0000142;
pub const STATUS_FLOAT_MULTIPLE_FAULTS: u32 = 0xC00002B4;
pub const STATUS_FLOAT_MULTIPLE_TRAPS: u32 = 0xC00002B5;
pub const STATUS_REG_NAT_CONSUMPTION: u32 = 0xC00002C9;
pub const STATUS_HEAP_CORRUPTION: u32 = 0xC0000374;
pub const STATUS_STACK_BUFFER_OVERRUN: u32 = 0xC0000409;
pub const STATUS_INVALID_CRUNTIME_PARAMETER: u32 = 0xC0000417;
pub const STATUS_ASSERTION_FAILURE: u32 = 0xC0000420;
pub const STATUS_SXS_EARLY_DEACTIVATION: u32 = 0xC015000F;
pub const STATUS_SXS_INVALID_DEACTIVATION: u32 = 0xC0150010;
#[derive(IntoPrimitive, TryFromPrimitive, Clone, Copy)]
#[repr(u32)]
pub enum ExceptionCode {
// From https://docs.microsoft.com/en-us/windows/win32/debug/getexceptioncode
AccessViolation = STATUS_ACCESS_VIOLATION,
ArrayBoundsExceeded = STATUS_ARRAY_BOUNDS_EXCEEDED,
Breakpoint = STATUS_BREAKPOINT,
DatatypeMisalignment = STATUS_DATATYPE_MISALIGNMENT,
FltDenormalOperand = STATUS_FLOAT_DENORMAL_OPERAND,
FltDivideByZero = STATUS_FLOAT_DIVIDE_BY_ZERO,
FltInexactResult = STATUS_FLOAT_INEXACT_RESULT,
FltInvalidOperation = STATUS_FLOAT_INVALID_OPERATION,
FltOverflow = STATUS_FLOAT_OVERFLOW,
FltStackCheck = STATUS_FLOAT_STACK_CHECK,
FltUnderflow = STATUS_FLOAT_UNDERFLOW,
GuardPageViolation = STATUS_GUARD_PAGE_VIOLATION,
IllegalInstruction = STATUS_ILLEGAL_INSTRUCTION,
InPageError = STATUS_IN_PAGE_ERROR,
IntegerDivideByZero = STATUS_INTEGER_DIVIDE_BY_ZERO,
IntegerOverflow = STATUS_INTEGER_OVERFLOW,
InvalidDisposition = STATUS_INVALID_DISPOSITION,
InvalidHandle = STATUS_INVALID_HANDLE,
NoncontinuableException = STATUS_NONCONTINUABLE_EXCEPTION,
PrivilegedInstruction = STATUS_PRIVILEGED_INSTRUCTION,
SingleStep = STATUS_SINGLE_STEP,
StackOverflow = STATUS_STACK_OVERFLOW,
UnwindConsolidate = STATUS_UNWIND_CONSOLIDATE,
// Addition exceptions
Wait0 = STATUS_WAIT_0,
AbandonedWait0 = STATUS_ABANDONED_WAIT_0,
UserAPC = STATUS_USER_APC,
Timeout = STATUS_TIMEOUT,
Pending = STATUS_PENDING,
SegmentNotification = STATUS_SEGMENT_NOTIFICATION,
FatalAppExit = STATUS_FATAL_APP_EXIT,
Longjump = STATUS_LONGJUMP,
DLLNotFound = STATUS_DLL_NOT_FOUND,
OrdinalNotFound = STATUS_ORDINAL_NOT_FOUND,
EntryPointNotFound = STATUS_ENTRYPOINT_NOT_FOUND,
ControlCExit = STATUS_CONTROL_C_EXIT,
DllInitFailed = STATUS_DLL_INIT_FAILED,
FltMultipleFaults = STATUS_FLOAT_MULTIPLE_FAULTS,
FltMultipleTraps = STATUS_FLOAT_MULTIPLE_TRAPS,
RegNatConsumption = STATUS_REG_NAT_CONSUMPTION,
HeapCorruption = STATUS_HEAP_CORRUPTION,
StackBufferOverrun = STATUS_STACK_BUFFER_OVERRUN,
InvalidCRuntimeParameter = STATUS_INVALID_CRUNTIME_PARAMETER,
AssertionFailure = STATUS_ASSERTION_FAILURE,
SXSEarlyDeactivation = STATUS_SXS_EARLY_DEACTIVATION,
SXSInvalidDeactivation = STATUS_SXS_INVALID_DEACTIVATION,
}
pub static CRASH_EXCEPTIONS: &[ExceptionCode] = &[
ExceptionCode::AccessViolation,
ExceptionCode::ArrayBoundsExceeded,
ExceptionCode::FltDivideByZero,
ExceptionCode::GuardPageViolation,
ExceptionCode::IllegalInstruction,
ExceptionCode::InPageError,
ExceptionCode::IntegerDivideByZero,
ExceptionCode::InvalidHandle,
ExceptionCode::NoncontinuableException,
ExceptionCode::PrivilegedInstruction,
ExceptionCode::StackOverflow,
ExceptionCode::HeapCorruption,
ExceptionCode::StackBufferOverrun,
ExceptionCode::AssertionFailure,
];
impl PartialEq for ExceptionCode {
fn eq(&self, other: &Self) -> bool {
*self as u32 == *other as u32
}
}
impl Eq for ExceptionCode {}
unsafe impl Sync for ExceptionCode {}
impl Display for ExceptionCode {
fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), fmt::Error> {
match self {
ExceptionCode::AccessViolation => write!(f, "STATUS_ACCESS_VIOLATION")?,
ExceptionCode::ArrayBoundsExceeded => write!(f, "STATUS_ARRAY_BOUNDS_EXCEEDED")?,
ExceptionCode::Breakpoint => write!(f, "STATUS_BREAKPOINT")?,
ExceptionCode::DatatypeMisalignment => write!(f, "STATUS_DATATYPE_MISALIGNMENT")?,
ExceptionCode::FltDenormalOperand => write!(f, "STATUS_FLOAT_DENORMAL_OPERAND")?,
ExceptionCode::FltDivideByZero => write!(f, "STATUS_FLOAT_DIVIDE_BY_ZERO")?,
ExceptionCode::FltInexactResult => write!(f, "STATUS_FLOAT_INEXACT_RESULT")?,
ExceptionCode::FltInvalidOperation => write!(f, "STATUS_FLOAT_INVALID_OPERATION")?,
ExceptionCode::FltOverflow => write!(f, "STATUS_FLOAT_OVERFLOW")?,
ExceptionCode::FltStackCheck => write!(f, "STATUS_FLOAT_STACK_CHECK")?,
ExceptionCode::FltUnderflow => write!(f, "STATUS_FLOAT_UNDERFLOW")?,
ExceptionCode::GuardPageViolation => write!(f, "STATUS_GUARD_PAGE_VIOLATION")?,
ExceptionCode::IllegalInstruction => write!(f, "STATUS_ILLEGAL_INSTRUCTION")?,
ExceptionCode::InPageError => write!(f, "STATUS_IN_PAGE_ERROR")?,
ExceptionCode::IntegerDivideByZero => write!(f, "STATUS_INTEGER_DIVIDE_BY_ZERO")?,
ExceptionCode::IntegerOverflow => write!(f, "STATUS_INTEGER_OVERFLOW")?,
ExceptionCode::InvalidDisposition => write!(f, "STATUS_INVALID_DISPOSITION")?,
ExceptionCode::InvalidHandle => write!(f, "STATUS_INVALID_HANDLE")?,
ExceptionCode::NoncontinuableException => write!(f, "STATUS_NONCONTINUABLE_EXCEPTION")?,
ExceptionCode::PrivilegedInstruction => write!(f, "STATUS_PRIVILEGED_INSTRUCTION")?,
ExceptionCode::SingleStep => write!(f, "STATUS_SINGLE_STEP")?,
ExceptionCode::StackOverflow => write!(f, "STATUS_STACK_OVERFLOW")?,
ExceptionCode::UnwindConsolidate => write!(f, "STATUS_UNWIND_CONSOLIDATE")?,
ExceptionCode::Wait0 => write!(f, "STATUS_WAIT_0")?,
ExceptionCode::AbandonedWait0 => write!(f, "STATUS_ABANDONED_WAIT_0")?,
ExceptionCode::UserAPC => write!(f, "STATUS_USER_APC")?,
ExceptionCode::Timeout => write!(f, "STATUS_TIMEOUT")?,
ExceptionCode::Pending => write!(f, "STATUS_PENDING")?,
ExceptionCode::SegmentNotification => write!(f, "STATUS_SEGMENT_NOTIFICATION")?,
ExceptionCode::FatalAppExit => write!(f, "STATUS_FATAL_APP_EXIT")?,
ExceptionCode::Longjump => write!(f, "STATUS_LONGJUMP")?,
ExceptionCode::DLLNotFound => write!(f, "STATUS_DLL_NOT_FOUND")?,
ExceptionCode::OrdinalNotFound => write!(f, "STATUS_ORDINAL_NOT_FOUND")?,
ExceptionCode::EntryPointNotFound => write!(f, "STATUS_ENTRYPOINT_NOT_FOUND")?,
ExceptionCode::ControlCExit => write!(f, "STATUS_CONTROL_C_EXIT")?,
ExceptionCode::DllInitFailed => write!(f, "STATUS_DLL_INIT_FAILED")?,
ExceptionCode::FltMultipleFaults => write!(f, "STATUS_FLOAT_MULTIPLE_FAULTS")?,
ExceptionCode::FltMultipleTraps => write!(f, "STATUS_FLOAT_MULTIPLE_TRAPS")?,
ExceptionCode::RegNatConsumption => write!(f, "STATUS_REG_NAT_CONSUMPTION")?,
ExceptionCode::HeapCorruption => write!(f, "STATUS_HEAP_CORRUPTION")?,
ExceptionCode::StackBufferOverrun => write!(f, "STATUS_STACK_BUFFER_OVERRUN")?,
ExceptionCode::InvalidCRuntimeParameter => {
write!(f, "STATUS_INVALID_CRUNTIME_PARAMETER")?
}
ExceptionCode::AssertionFailure => write!(f, "STATUS_ASSERTION_FAILURE")?,
ExceptionCode::SXSEarlyDeactivation => write!(f, "STATUS_SXS_EARLY_DEACTIVATION")?,
ExceptionCode::SXSInvalidDeactivation => write!(f, "STATUS_SXS_INVALID_DEACTIVATION")?,
};
Ok(())
}
}
pub static EXCEPTION_CODES_MAPPING: [ExceptionCode; 45] = [
ExceptionCode::AccessViolation,
ExceptionCode::ArrayBoundsExceeded,
ExceptionCode::Breakpoint,
ExceptionCode::DatatypeMisalignment,
ExceptionCode::FltDenormalOperand,
ExceptionCode::FltDivideByZero,
ExceptionCode::FltInexactResult,
ExceptionCode::FltInvalidOperation,
ExceptionCode::FltOverflow,
ExceptionCode::FltStackCheck,
ExceptionCode::FltUnderflow,
ExceptionCode::GuardPageViolation,
ExceptionCode::IllegalInstruction,
ExceptionCode::InPageError,
ExceptionCode::IntegerDivideByZero,
ExceptionCode::IntegerOverflow,
ExceptionCode::InvalidDisposition,
ExceptionCode::InvalidHandle,
ExceptionCode::NoncontinuableException,
ExceptionCode::PrivilegedInstruction,
ExceptionCode::SingleStep,
ExceptionCode::StackOverflow,
ExceptionCode::UnwindConsolidate,
ExceptionCode::Wait0,
ExceptionCode::AbandonedWait0,
ExceptionCode::UserAPC,
ExceptionCode::Timeout,
ExceptionCode::Pending,
ExceptionCode::SegmentNotification,
ExceptionCode::FatalAppExit,
ExceptionCode::Longjump,
ExceptionCode::DLLNotFound,
ExceptionCode::OrdinalNotFound,
ExceptionCode::EntryPointNotFound,
ExceptionCode::ControlCExit,
ExceptionCode::DllInitFailed,
ExceptionCode::FltMultipleFaults,
ExceptionCode::FltMultipleTraps,
ExceptionCode::RegNatConsumption,
ExceptionCode::HeapCorruption,
ExceptionCode::StackBufferOverrun,
ExceptionCode::InvalidCRuntimeParameter,
ExceptionCode::AssertionFailure,
ExceptionCode::SXSEarlyDeactivation,
ExceptionCode::SXSInvalidDeactivation,
];
pub trait Handler {
/// Handle an exception
fn handle(
&mut self,
exception_code: ExceptionCode,
exception_pointers: *mut EXCEPTION_POINTERS,
);
/// Return a list of exceptions to handle
fn exceptions(&self) -> Vec<ExceptionCode>;
}
struct HandlerHolder {
handler: UnsafeCell<*mut dyn Handler>,
}
unsafe impl Send for HandlerHolder {}
/// Keep track of which handler is registered for which exception
static mut EXCEPTION_HANDLERS: [Option<HandlerHolder>; 64] = [
None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None,
None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None,
None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None,
None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None,
];
type NativeHandlerType = extern "system" fn(*mut EXCEPTION_POINTERS) -> c_long;
static mut PREVIOUS_HANDLER: Option<NativeHandlerType> = None;
/// Internal function that is being called whenever an exception arrives (stdcall).
unsafe extern "system" fn handle_exception(exception_pointers: *mut EXCEPTION_POINTERS) -> c_long {
let code = exception_pointers
.as_mut()
.unwrap()
.exception_record
.as_mut()
.unwrap()
.exception_code;
let exception_code = ExceptionCode::try_from(code).unwrap();
let index = EXCEPTION_CODES_MAPPING
.iter()
.position(|x| *x == exception_code)
.unwrap();
let ret = match &EXCEPTION_HANDLERS[index] {
Some(handler_holder) => {
let handler = &mut **handler_holder.handler.get();
handler.handle(exception_code, exception_pointers);
EXCEPTION_EXECUTE_HANDLER
}
None => EXCEPTION_EXECUTE_HANDLER,
};
if let Some(prev_handler) = PREVIOUS_HANDLER {
prev_handler(exception_pointers)
} else {
ret
}
}
/// Setup Win32 exception handlers in a somewhat rusty way.
pub unsafe fn setup_exception_handler<T: 'static + Handler>(handler: &mut T) -> Result<(), Error> {
let exceptions = handler.exceptions();
for exception_code in exceptions {
let index = EXCEPTION_CODES_MAPPING
.iter()
.position(|x| *x == exception_code)
.unwrap();
write_volatile(
&mut EXCEPTION_HANDLERS[index],
Some(HandlerHolder {
handler: UnsafeCell::new(handler as *mut dyn Handler),
}),
);
}
compiler_fence(Ordering::SeqCst);
if let Some(prev) = SetUnhandledExceptionFilter(Some(core::mem::transmute(
handle_exception as *const c_void,
))) {
PREVIOUS_HANDLER = Some(core::mem::transmute(prev as *const c_void));
}
Ok(())
}

View File

@ -1,6 +1,11 @@
//! Wrappers that abstracts references (or pointers) and owned data accesses.
// The serialization is towards owned, allowing to serialize pointers without troubles.
use alloc::{boxed::Box, vec::Vec};
use core::{clone::Clone, fmt::Debug};
use serde::{Deserialize, Deserializer, Serialize, Serializer};
/// Wrap a reference and convert to a Box on serialize
#[derive(Clone, Debug)]
pub enum Ptr<'a, T: 'a + ?Sized> {
Ref(&'a T),
@ -40,6 +45,8 @@ impl<'a, T: Sized> AsRef<T> for Ptr<'a, T> {
}
}
/// Wrap a mutable reference and convert to a Box on serialize
#[derive(Debug)]
pub enum PtrMut<'a, T: 'a + ?Sized> {
Ref(&'a mut T),
Owned(Box<T>),
@ -87,6 +94,8 @@ impl<'a, T: Sized> AsMut<T> for PtrMut<'a, T> {
}
}
/// Wrap a slice and convert to a Vec on serialize
#[derive(Clone, Debug)]
pub enum Slice<'a, T: 'a + Sized> {
Ref(&'a [T]),
Owned(Vec<T>),
@ -125,6 +134,8 @@ impl<'a, T: Sized> Slice<'a, T> {
}
}
/// Wrap a mutable slice and convert to a Vec on serialize
#[derive(Debug)]
pub enum SliceMut<'a, T: 'a + Sized> {
Ref(&'a mut [T]),
Owned(Vec<T>),
@ -170,6 +181,7 @@ impl<'a, T: Sized> SliceMut<'a, T> {
}
}
/// Wrap a C-style pointer and convert to a Box on serialize
#[derive(Clone, Debug)]
pub enum Cptr<T: Sized> {
Cptr(*const T),
@ -206,6 +218,8 @@ impl<T: Sized> AsRef<T> for Cptr<T> {
}
}
/// Wrap a C-style mutable pointer and convert to a Box on serialize
#[derive(Clone, Debug)]
pub enum CptrMut<T: Sized> {
Cptr(*mut T),
Owned(Box<T>),
@ -250,6 +264,8 @@ impl<T: Sized> AsMut<T> for CptrMut<T> {
}
}
/// Wrap a C-style pointer to an array (with size= and convert to a Vec on serialize
#[derive(Clone, Debug)]
pub enum Array<T: Sized> {
Cptr((*const T, usize)),
Owned(Vec<T>),
@ -285,6 +301,7 @@ impl<T: Sized> Array<T> {
}
}
/// Wrap a C-style mutable pointer to an array (with size= and convert to a Vec on serialize
#[derive(Clone, Debug)]
pub enum ArrayMut<T: Sized> {
Cptr((*mut T, usize)),

View File

@ -1,4 +1,5 @@
//! Poor-rust-man's downcasts for stuff we send over the wire (or shared maps)
use serde::{Deserialize, Deserializer, Serialize, Serializer};
use alloc::boxed::Box;

View File

@ -169,7 +169,6 @@ pub mod unix_shmem {
#[cfg(target_os = "android")]
unsafe fn shmctl(__shmid: c_int, __cmd: c_int, _buf: *mut shmid_ds) -> c_int {
println!("shmctl(__shmid: {})", __shmid);
if __cmd == 0 {
let length = ioctl(__shmid, ASHMEM_GET_SIZE);
@ -188,7 +187,10 @@ pub mod unix_shmem {
#[cfg(target_os = "android")]
unsafe fn shmget(__key: c_int, __size: c_ulong, __shmflg: c_int) -> c_int {
let path = CString::new(ASHMEM_DEVICE).expect("CString::new failed!");
let boot_id = std::fs::read_to_string("/proc/sys/kernel/random/boot_id").unwrap();
let path = CString::new(format!("{}{}", ASHMEM_DEVICE, boot_id).trim())
.expect("CString::new failed!");
let fd = open(path.as_ptr(), O_RDWR);
let mut ourkey: [c_char; 20] = [0; 20];
@ -196,10 +198,9 @@ pub mod unix_shmem {
ourkey.as_mut_ptr() as *mut c_char,
size_of::<[c_char; 20]>() as c_ulong,
b"%d\x00" as *const u8 as *const c_char,
__key,
if __key == 0 { fd } else { __key },
);
println!("ourkey: {:?}", ourkey);
if ioctl(fd, ASHMEM_SET_NAME, &ourkey) != 0 {
close(fd);
return 0;
@ -210,14 +211,11 @@ pub mod unix_shmem {
return 0;
};
println!("shmget returns {}", fd);
fd
}
#[cfg(target_os = "android")]
unsafe fn shmat(__shmid: c_int, __shmaddr: *const c_void, __shmflg: c_int) -> *mut c_void {
println!("shmat(__shmid: {})", __shmid);
let size = ioctl(__shmid, ASHMEM_GET_SIZE);
if size < 0 {
return 0 as *mut c_void;
@ -235,7 +233,6 @@ pub mod unix_shmem {
return 0 as *mut c_void;
}
println!("shmat() = {:?}", ptr);
ptr
}
@ -321,14 +318,14 @@ pub mod unix_shmem {
impl Drop for UnixShMem {
fn drop(&mut self) {
unsafe {
afl_shmem_deinit(self);
unix_shmem_deinit(self);
}
}
}
/// Create an uninitialized shmap
#[cfg(unix)]
const fn afl_shmem_unitialized() -> UnixShMem {
const fn unix_shmem_unitialized() -> UnixShMem {
UnixShMem {
shm_str: [0; 20],
shm_id: -1,
@ -340,8 +337,8 @@ pub mod unix_shmem {
#[cfg(unix)]
impl UnixShMem {
pub fn from_str(shm_str: &CStr, map_size: usize) -> Result<Self, Error> {
let mut ret = afl_shmem_unitialized();
let map = unsafe { afl_shmem_by_str(&mut ret, shm_str, map_size) };
let mut ret = unix_shmem_unitialized();
let map = unsafe { unix_shmem_by_str(&mut ret, shm_str, map_size) };
if !map.is_null() {
Ok(ret)
} else {
@ -353,8 +350,8 @@ pub mod unix_shmem {
}
pub fn new(map_size: usize) -> Result<Self, Error> {
let mut ret = afl_shmem_unitialized();
let map = unsafe { afl_shmem_init(&mut ret, map_size) };
let mut ret = unix_shmem_unitialized();
let map = unsafe { unix_shmem_init(&mut ret, map_size) };
if !map.is_null() {
Ok(ret)
} else {
@ -367,7 +364,7 @@ pub mod unix_shmem {
}
/// Deinitialize this shmem instance
unsafe fn afl_shmem_deinit(shm: *mut UnixShMem) {
unsafe fn unix_shmem_deinit(shm: *mut UnixShMem) {
if shm.is_null() || (*shm).map.is_null() {
/* Serialized map id */
// Not set or not initialized;
@ -380,7 +377,7 @@ pub mod unix_shmem {
/// Functions to create Shared memory region, for observation channels and
/// opening inputs and stuff.
unsafe fn afl_shmem_init(shm: *mut UnixShMem, map_size: usize) -> *mut c_uchar {
unsafe fn unix_shmem_init(shm: *mut UnixShMem, map_size: usize) -> *mut c_uchar {
(*shm).map_size = map_size;
(*shm).map = ptr::null_mut();
(*shm).shm_id = shmget(
@ -412,7 +409,7 @@ pub mod unix_shmem {
}
/// Uses a shmap id string to open a shared map
unsafe fn afl_shmem_by_str(
unsafe fn unix_shmem_by_str(
shm: *mut UnixShMem,
shm_str: &CStr,
map_size: usize,
@ -446,18 +443,140 @@ pub mod unix_shmem {
#[cfg(all(feature = "std", windows))]
pub mod shmem {
//TODO use super::ShMem;
use super::ShMem;
use crate::{
bolts::bindings::{
windows::win32::system_services::{
CreateFileMappingA, MapViewOfFile, OpenFileMappingA, UnmapViewOfFile,
},
windows::win32::system_services::{BOOL, HANDLE, PAGE_TYPE, PSTR},
windows::win32::windows_programming::CloseHandle,
},
Error,
};
use core::{ffi::c_void, ptr, slice};
use uuid::Uuid;
const INVALID_HANDLE_VALUE: isize = -1;
const FILE_MAP_ALL_ACCESS: u32 = 0xf001f;
/// The default Sharedmap impl for windows using shmctl & shmget
#[derive(Clone, Debug)]
pub struct Win32ShMem {
pub filename: [u8; 64],
//TODO pub handle: windows::win32::system_services::HANDLE,
pub shm_str: [u8; 20],
pub handle: HANDLE,
pub map: *mut u8,
pub map_size: usize,
}
// TODO complete
impl ShMem for Win32ShMem {
fn existing_from_shm_slice(
map_str_bytes: &[u8; 20],
map_size: usize,
) -> Result<Self, Error> {
Self::from_str(map_str_bytes, map_size)
}
fn new_map(map_size: usize) -> Result<Self, Error> {
Self::new(map_size)
}
fn shm_slice(&self) -> &[u8; 20] {
&self.shm_str
}
fn map(&self) -> &[u8] {
unsafe { slice::from_raw_parts(self.map, self.map_size) }
}
fn map_mut(&mut self) -> &mut [u8] {
unsafe { slice::from_raw_parts_mut(self.map, self.map_size) }
}
}
/// Deinit sharedmaps on drop
impl Drop for Win32ShMem {
fn drop(&mut self) {
unsafe {
UnmapViewOfFile(self.map as *mut c_void);
CloseHandle(self.handle);
}
}
}
impl Win32ShMem {
pub fn from_str(map_str_bytes: &[u8; 20], map_size: usize) -> Result<Self, Error> {
unsafe {
let handle = OpenFileMappingA(
FILE_MAP_ALL_ACCESS,
BOOL(0),
PSTR(map_str_bytes as *const u8 as *mut u8),
);
if handle == HANDLE(0) {
return Err(Error::Unknown(format!(
"Cannot open shared memory {}",
String::from_utf8_lossy(map_str_bytes)
)));
}
let map =
MapViewOfFile(handle.clone(), FILE_MAP_ALL_ACCESS, 0, 0, map_size) as *mut u8;
if map == ptr::null_mut() {
return Err(Error::Unknown(format!(
"Cannot map shared memory {}",
String::from_utf8_lossy(map_str_bytes)
)));
}
let mut ret = Self {
shm_str: [0; 20],
handle: handle,
map: map,
map_size: map_size,
};
ret.shm_str.clone_from_slice(map_str_bytes);
Ok(ret)
}
}
pub fn new(map_size: usize) -> Result<Self, Error> {
unsafe {
let uuid = Uuid::new_v4();
let mut map_str = format!("libafl_{}", uuid.to_simple());
let map_str_bytes = map_str.as_mut_vec();
map_str_bytes[19] = 0; // Trucate to size 20
let handle = CreateFileMappingA(
HANDLE(INVALID_HANDLE_VALUE),
ptr::null_mut(),
PAGE_TYPE::PAGE_READWRITE,
0,
map_size as u32,
PSTR(map_str_bytes.as_mut_ptr()),
);
if handle == HANDLE(0) {
return Err(Error::Unknown(format!(
"Cannot create shared memory {}",
String::from_utf8_lossy(map_str_bytes)
)));
}
let map =
MapViewOfFile(handle.clone(), FILE_MAP_ALL_ACCESS, 0, 0, map_size) as *mut u8;
if map == ptr::null_mut() {
return Err(Error::Unknown(format!(
"Cannot map shared memory {}",
String::from_utf8_lossy(map_str_bytes)
)));
}
let mut ret = Self {
shm_str: [0; 20],
handle: handle,
map: map,
map_size: map_size,
};
ret.shm_str.clone_from_slice(&map_str_bytes[0..20]);
Ok(ret)
}
}
}
}
#[cfg(test)]

View File

@ -1,3 +1,6 @@
//! The Minimizer schedulers are a family of corpus schedulers that feed the fuzzer
// with testcases only from a subset of the total corpus.
use crate::{
bolts::serdeany::SerdeAny,
corpus::{Corpus, CorpusScheduler, Testcase},
@ -43,6 +46,7 @@ impl Default for TopRatedsMetadata {
}
}
/// Compute the favor factor of a testcase. Lower is better.
pub trait FavFactor<I>
where
I: Input,
@ -50,6 +54,8 @@ where
fn compute(testcase: &mut Testcase<I>) -> Result<u64, Error>;
}
/// Multiply the testcase size with the execution time.
/// This favors small and quick testcases.
pub struct LenTimeMulFavFactor<I>
where
I: Input + HasLen,
@ -67,6 +73,9 @@ where
}
}
/// The Minimizer scheduler employs a genetic algorithm to compute a subset of the
/// corpus that exercise all the requested features (e.g. all the coverage seen so far)
/// prioritizing testcases using FavFactor
pub struct MinimizerCorpusScheduler<C, CS, F, I, M, R, S>
where
CS: CorpusScheduler<I, S>,
@ -230,8 +239,11 @@ where
}
}
/// A MinimizerCorpusScheduler with LenTimeMulFavFactor to prioritize quick and small testcases
pub type LenTimeMinimizerCorpusScheduler<C, CS, I, M, R, S> =
MinimizerCorpusScheduler<C, CS, LenTimeMulFavFactor<I>, I, M, R, S>;
/// A MinimizerCorpusScheduler with LenTimeMulFavFactor to prioritize quick and small testcases
/// that exercise all the entries registered in the MapIndexesMetadata
pub type IndexesLenTimeMinimizerCorpusScheduler<C, CS, I, R, S> =
MinimizerCorpusScheduler<C, CS, LenTimeMulFavFactor<I>, I, MapIndexesMetadata, R, S>;

View File

@ -63,6 +63,8 @@ where
fn current_mut(&mut self) -> &mut Option<usize>;
}
/// The scheduler define how the fuzzer requests a testcase from the corpus.
/// It has hooks to corpus add/replace/remove to allow complex scheduling algorithms to collect data.
pub trait CorpusScheduler<I, S>
where
I: Input,
@ -96,6 +98,7 @@ where
fn next(&self, state: &mut S) -> Result<usize, Error>;
}
/// Feed the fuzzer simpply with a random testcase on request
pub struct RandCorpusScheduler<C, I, R, S>
where
S: HasCorpus<C, I> + HasRand<R>,

View File

@ -1,4 +1,4 @@
//! The queue corpus implements an afl-like queue mechanism
//! The queue corpus scheduler implements an AFL-like queue mechanism
use alloc::borrow::ToOwned;
use core::marker::PhantomData;
@ -10,6 +10,7 @@ use crate::{
Error,
};
/// Walk the corpus in a queue-like fashion
pub struct QueueCorpusScheduler<C, I, S>
where
S: HasCorpus<C, I>,
@ -25,7 +26,7 @@ where
C: Corpus<I>,
I: Input,
{
/// Gets the next entry at random
/// Gets the next entry in the queue
fn next(&self, state: &mut S) -> Result<usize, Error> {
if state.corpus().count() == 0 {
Err(Error::Empty("No entries in corpus".to_owned()))

View File

@ -1,8 +1,10 @@
use crate::bolts::{llmp::LlmpSender, shmem::HasFd};
use alloc::{string::ToString, vec::Vec};
use core::{marker::PhantomData, time::Duration};
use serde::{de::DeserializeOwned, Serialize};
#[cfg(feature = "std")]
use core::ptr::read_volatile;
#[cfg(feature = "std")]
use crate::bolts::llmp::LlmpReceiver;
@ -16,8 +18,8 @@ use crate::utils::{fork, ForkResult};
use crate::bolts::shmem::UnixShMem;
use crate::{
bolts::{
llmp::{self, LlmpClient, LlmpClientDescription, Tag},
shmem::ShMem,
llmp::{self, LlmpClient, LlmpClientDescription, LlmpSender, Tag},
shmem::{HasFd, ShMem},
},
corpus::CorpusScheduler,
events::{BrokerEventResult, Event, EventManager},
@ -352,7 +354,7 @@ where
llmp::LlmpConnection::IsClient { client } => {
while let Some((sender_id, tag, msg)) = client.recv_buf()? {
if tag == _LLMP_TAG_EVENT_TO_BROKER {
continue;
panic!("EVENT_TO_BROKER parcel should not have arrived in the client!");
}
let event: Event<I> = postcard::from_bytes(msg)?;
events.push((sender_id, event));
@ -519,10 +521,7 @@ where
#[cfg(target_os = "android")]
{
let path = std::env::current_dir()?;
mgr = LlmpEventManager::<I, S, SH, ST>::new_on_domain_socket(
stats,
&format!("{}/.llmp_socket", path.display()).to_string(),
)?;
mgr = LlmpEventManager::<I, S, SH, ST>::new_on_domain_socket(stats, "\x00llmp_socket")?;
};
#[cfg(not(target_os = "android"))]
{
@ -535,6 +534,7 @@ where
mgr.broker_loop()?;
return Err(Error::ShuttingDown);
} else {
// We are the fuzzer respawner in a llmp client
mgr.to_env(_ENV_FUZZER_BROKER_CLIENT_INITIAL);
// First, create a channel from the fuzzer (sender) to us (receiver) to report its state for restarts.
@ -547,7 +547,7 @@ where
sender.to_env(_ENV_FUZZER_SENDER)?;
receiver.to_env(_ENV_FUZZER_RECEIVER)?;
let mut ctr = 0;
let mut ctr: u64 = 0;
// Client->parent loop
loop {
dbg!("Spawning next client (id {})", ctr);
@ -563,7 +563,12 @@ where
#[cfg(windows)]
startable_self()?.status()?;
ctr += 1;
if unsafe { read_volatile(&(*receiver.current_recv_map.page()).size_used) } == 0 {
// Storing state in the last round did not work
panic!("Fuzzer-respawner: Storing state in crashed fuzzer instance did not work, no point to spawn the next client!");
}
ctr = ctr.wrapping_add(1);
}
}
} else {

View File

@ -1,15 +1,18 @@
//! The InProcess Executor is a libfuzzer-like executor, that will simply call a function.
//! It should usually be paired with extra error-handling, such as a restarting event manager, to be effective.
use core::marker::PhantomData;
#[cfg(unix)]
use core::{
ffi::c_void,
marker::PhantomData,
ptr::{self, write_volatile},
sync::atomic::{compiler_fence, Ordering},
};
#[cfg(unix)]
use crate::bolts::os::unix_signals::{c_void, setup_signal_handler};
use crate::bolts::os::unix_signals::setup_signal_handler;
#[cfg(windows)]
use crate::bolts::os::windows_exceptions::setup_exception_handler;
use crate::{
bolts::tuples::Named,
corpus::Corpus,
@ -59,6 +62,27 @@ where
&mut data.current_input_ptr,
_input as *const _ as *const c_void,
);
write_volatile(
&mut data.observers_ptr,
&self.observers as *const _ as *const c_void,
);
// Direct raw pointers access /aliasing is pretty undefined behavior.
// Since the state and event may have moved in memory, refresh them right before the signal may happen
write_volatile(&mut data.state_ptr, _state as *mut _ as *mut c_void);
write_volatile(&mut data.event_mgr_ptr, _event_mgr as *mut _ as *mut c_void);
compiler_fence(Ordering::SeqCst);
}
#[cfg(windows)]
unsafe {
let data = &mut windows_exception_handler::GLOBAL_STATE;
write_volatile(
&mut data.current_input_ptr,
_input as *const _ as *const c_void,
);
write_volatile(
&mut data.observers_ptr,
&self.observers as *const _ as *const c_void,
);
// Direct raw pointers access /aliasing is pretty undefined behavior.
// Since the state and event may have moved in memory, refresh them right before the signal may happen
write_volatile(&mut data.state_ptr, _state as *mut _ as *mut c_void);
@ -72,6 +96,16 @@ where
fn run_target(&mut self, input: &I) -> Result<ExitKind, Error> {
let bytes = input.target_bytes();
let ret = (self.harness_fn)(self, bytes.as_slice());
Ok(ret)
}
#[inline]
fn post_exec<EM, S>(
&mut self,
_state: &mut S,
_event_mgr: &mut EM,
_input: &I,
) -> Result<(), Error> {
#[cfg(unix)]
unsafe {
write_volatile(
@ -80,7 +114,15 @@ where
);
compiler_fence(Ordering::SeqCst);
}
Ok(ret)
#[cfg(windows)]
unsafe {
write_volatile(
&mut windows_exception_handler::GLOBAL_STATE.current_input_ptr,
ptr::null(),
);
compiler_fence(Ordering::SeqCst);
}
Ok(())
}
}
@ -138,10 +180,6 @@ where
#[cfg(unix)]
unsafe {
let data = &mut unix_signal_handler::GLOBAL_STATE;
write_volatile(
&mut data.observers_ptr,
&observers as *const _ as *const c_void,
);
write_volatile(
&mut data.crash_handler,
unix_signal_handler::inproc_crash_handler::<EM, I, OC, OFT, OT, S>,
@ -154,6 +192,21 @@ where
setup_signal_handler(data)?;
compiler_fence(Ordering::SeqCst);
}
#[cfg(windows)]
unsafe {
let data = &mut windows_exception_handler::GLOBAL_STATE;
write_volatile(
&mut data.crash_handler,
windows_exception_handler::inproc_crash_handler::<EM, I, OC, OFT, OT, S>,
);
//write_volatile(
// &mut data.timeout_handler,
// windows_exception_handler::inproc_timeout_handler::<EM, I, OC, OFT, OT, S>,
//);
setup_exception_handler(data)?;
compiler_fence(Ordering::SeqCst);
}
Ok(Self {
harness_fn,
@ -186,6 +239,8 @@ mod unix_signal_handler {
state::{HasObjectives, HasSolutions},
};
// TODO merge GLOBAL_STATE with the Windows one
/// Signal handling on unix systems needs some nasty unsafe.
pub static mut GLOBAL_STATE: InProcessExecutorHandlerData = InProcessExecutorHandlerData {
/// The state ptr for signal handling
@ -228,7 +283,9 @@ mod unix_signal_handler {
unsafe {
let data = &mut GLOBAL_STATE;
match signal {
Signal::SigUser2 => (data.timeout_handler)(signal, info, void, data),
Signal::SigUser2 | Signal::SigAlarm => {
(data.timeout_handler)(signal, info, void, data)
}
_ => (data.crash_handler)(signal, info, void, data),
}
}
@ -236,6 +293,7 @@ mod unix_signal_handler {
fn signals(&self) -> Vec<Signal> {
vec![
Signal::SigAlarm,
Signal::SigUser2,
Signal::SigAbort,
Signal::SigBus,
@ -279,7 +337,7 @@ mod unix_signal_handler {
let obj_fitness = state
.objectives_mut()
.is_interesting_all(&input, observers, ExitKind::Crash)
.is_interesting_all(&input, observers, ExitKind::Timeout)
.expect("In timeout handler objectives failure.");
if obj_fitness > 0 {
state
@ -404,6 +462,166 @@ mod unix_signal_handler {
}
}
#[cfg(windows)]
mod windows_exception_handler {
use alloc::vec::Vec;
use core::{ffi::c_void, ptr};
#[cfg(feature = "std")]
use std::io::{stdout, Write};
use crate::{
bolts::{
bindings::windows::win32::system_services::ExitProcess,
os::windows_exceptions::{
ExceptionCode, Handler, CRASH_EXCEPTIONS, EXCEPTION_POINTERS,
},
},
corpus::{Corpus, Testcase},
events::{Event, EventManager},
executors::ExitKind,
feedbacks::FeedbacksTuple,
inputs::{HasTargetBytes, Input},
observers::ObserversTuple,
state::{HasObjectives, HasSolutions},
};
/// Signal handling on unix systems needs some nasty unsafe.
pub static mut GLOBAL_STATE: InProcessExecutorHandlerData = InProcessExecutorHandlerData {
/// The state ptr for signal handling
state_ptr: ptr::null_mut(),
/// The event manager ptr for signal handling
event_mgr_ptr: ptr::null_mut(),
/// The observers ptr for signal handling
observers_ptr: ptr::null(),
/// The current input for signal handling
current_input_ptr: ptr::null(),
/// The crash handler fn
crash_handler: nop_handler,
// The timeout handler fn
//timeout_handler: nop_handler,
};
pub struct InProcessExecutorHandlerData {
pub state_ptr: *mut c_void,
pub event_mgr_ptr: *mut c_void,
pub observers_ptr: *const c_void,
pub current_input_ptr: *const c_void,
pub crash_handler: unsafe fn(ExceptionCode, *mut EXCEPTION_POINTERS, &mut Self),
//pub timeout_handler: unsafe fn(ExceptionCode, *mut EXCEPTION_POINTERS, &mut Self),
}
unsafe impl Send for InProcessExecutorHandlerData {}
unsafe impl Sync for InProcessExecutorHandlerData {}
unsafe fn nop_handler(
_code: ExceptionCode,
_exception_pointers: *mut EXCEPTION_POINTERS,
_data: &mut InProcessExecutorHandlerData,
) {
}
impl Handler for InProcessExecutorHandlerData {
fn handle(&mut self, code: ExceptionCode, exception_pointers: *mut EXCEPTION_POINTERS) {
unsafe {
let data = &mut GLOBAL_STATE;
(data.crash_handler)(code, exception_pointers, data)
}
}
fn exceptions(&self) -> Vec<ExceptionCode> {
CRASH_EXCEPTIONS.to_vec()
}
}
pub unsafe fn inproc_crash_handler<EM, I, OC, OFT, OT, S>(
code: ExceptionCode,
exception_pointers: *mut EXCEPTION_POINTERS,
data: &mut InProcessExecutorHandlerData,
) where
EM: EventManager<I, S>,
OT: ObserversTuple,
OC: Corpus<I>,
OFT: FeedbacksTuple<I>,
S: HasObjectives<OFT, I> + HasSolutions<OC, I>,
I: Input + HasTargetBytes,
{
#[cfg(feature = "std")]
println!("Crashed with {}", code);
if !data.current_input_ptr.is_null() {
let state = (data.state_ptr as *mut S).as_mut().unwrap();
let event_mgr = (data.event_mgr_ptr as *mut EM).as_mut().unwrap();
let observers = (data.observers_ptr as *const OT).as_ref().unwrap();
#[cfg(feature = "std")]
println!("Child crashed!");
#[cfg(feature = "std")]
let _ = stdout().flush();
let input = (data.current_input_ptr as *const I).as_ref().unwrap();
// Make sure we don't crash in the crash handler forever.
data.current_input_ptr = ptr::null();
let obj_fitness = state
.objectives_mut()
.is_interesting_all(&input, observers, ExitKind::Crash)
.expect("In crash handler objectives failure.");
if obj_fitness > 0 {
let new_input = input.clone();
state
.solutions_mut()
.add(Testcase::new(new_input))
.expect("In crash handler solutions failure.");
event_mgr
.fire(
state,
Event::Objective {
objective_size: state.solutions().count(),
},
)
.expect("Could not send crashing input");
}
event_mgr.on_restart(state).unwrap();
#[cfg(feature = "std")]
println!("Waiting for broker...");
event_mgr.await_restart_safe();
#[cfg(feature = "std")]
println!("Bye!");
ExitProcess(1);
} else {
#[cfg(feature = "std")]
{
println!("Double crash\n");
let crash_addr = exception_pointers
.as_mut()
.unwrap()
.exception_record
.as_mut()
.unwrap()
.exception_address as usize;
println!(
"We crashed at addr 0x{:x}, but are not in the target... Bug in the fuzzer? Exiting.",
crash_addr
);
}
#[cfg(feature = "std")]
{
println!("Type QUIT to restart the child");
let mut line = String::new();
while line.trim() != "QUIT" {
std::io::stdin().read_line(&mut line).unwrap();
}
}
// TODO tell the parent to not restart
ExitProcess(1);
}
}
}
#[cfg(test)]
mod tests {
@ -425,7 +643,6 @@ mod tests {
let mut in_process_executor = InProcessExecutor::<NopInput, ()> {
harness_fn: test_harness_fn_nop,
// TODO: on_crash_fn: Box::new(|_, _, _, _, _| ()),
observers: tuple_list!(),
name: "main",
phantom: PhantomData,

View File

@ -2,6 +2,8 @@
pub mod inprocess;
pub use inprocess::InProcessExecutor;
pub mod timeout;
pub use timeout::TimeoutExecutor;
#[cfg(feature = "runtime")]
pub mod runtime;
@ -94,7 +96,12 @@ where
/// Called right after execution finished.
#[inline]
fn post_exec<EM, S>(&mut self, _state: &S, _event_mgr: &mut EM, _input: &I) -> Result<(), Error>
fn post_exec<EM, S>(
&mut self,
_state: &mut S,
_event_mgr: &mut EM,
_input: &I,
) -> Result<(), Error>
where
EM: EventManager<I, S>,
{

View File

@ -0,0 +1,164 @@
//! A TimeoutExecutor set a timeout before each target run
use core::{marker::PhantomData, time::Duration};
use crate::{
bolts::tuples::Named,
events::EventManager,
executors::{Executor, ExitKind, HasObservers},
inputs::{HasTargetBytes, Input},
observers::ObserversTuple,
Error,
};
#[cfg(unix)]
use core::ptr::null_mut;
#[cfg(unix)]
use libc::c_int;
#[repr(C)]
#[cfg(unix)]
struct Timeval {
pub tv_sec: i64,
pub tv_usec: i64,
}
#[repr(C)]
#[cfg(unix)]
struct Itimerval {
pub it_interval: Timeval,
pub it_value: Timeval,
}
#[cfg(unix)]
extern "C" {
fn setitimer(which: c_int, new_value: *mut Itimerval, old_value: *mut Itimerval) -> c_int;
}
#[cfg(unix)]
const ITIMER_REAL: c_int = 0;
/// The timeout excutor is a wrapper that set a timeout before each run
pub struct TimeoutExecutor<E, I, OT>
where
E: Executor<I> + HasObservers<OT>,
I: Input + HasTargetBytes,
OT: ObserversTuple,
{
executor: E,
exec_tmout: Duration,
phantom: PhantomData<(I, OT)>,
}
impl<E, I, OT> Named for TimeoutExecutor<E, I, OT>
where
E: Executor<I> + HasObservers<OT>,
I: Input + HasTargetBytes,
OT: ObserversTuple,
{
fn name(&self) -> &str {
self.executor.name()
}
}
impl<E, I, OT> HasObservers<OT> for TimeoutExecutor<E, I, OT>
where
E: Executor<I> + HasObservers<OT>,
I: Input + HasTargetBytes,
OT: ObserversTuple,
{
#[inline]
fn observers(&self) -> &OT {
self.executor.observers()
}
#[inline]
fn observers_mut(&mut self) -> &mut OT {
self.executor.observers_mut()
}
}
impl<E, I, OT> TimeoutExecutor<E, I, OT>
where
E: Executor<I> + HasObservers<OT>,
I: Input + HasTargetBytes,
OT: ObserversTuple,
{
pub fn new(executor: E, exec_tmout: Duration) -> Self {
Self {
executor,
exec_tmout,
phantom: PhantomData,
}
}
}
impl<E, I, OT> Executor<I> for TimeoutExecutor<E, I, OT>
where
E: Executor<I> + HasObservers<OT>,
I: Input + HasTargetBytes,
OT: ObserversTuple,
{
#[inline]
fn pre_exec<EM: EventManager<I, S>, S>(
&mut self,
_state: &mut S,
_event_mgr: &mut EM,
_input: &I,
) -> Result<(), Error> {
#[cfg(unix)]
unsafe {
let milli_sec = self.exec_tmout.as_millis();
let it_value = Timeval {
tv_sec: (milli_sec / 1000) as i64,
tv_usec: (milli_sec % 1000) as i64,
};
let it_interval = Timeval {
tv_sec: 0,
tv_usec: 0,
};
setitimer(
ITIMER_REAL,
&mut Itimerval {
it_interval,
it_value,
},
null_mut(),
);
}
self.executor.pre_exec(_state, _event_mgr, _input)
}
#[inline]
fn post_exec<EM: EventManager<I, S>, S>(
&mut self,
_state: &mut S,
_event_mgr: &mut EM,
_input: &I,
) -> Result<(), Error> {
#[cfg(unix)]
unsafe {
let it_value = Timeval {
tv_sec: 0,
tv_usec: 0,
};
let it_interval = Timeval {
tv_sec: 0,
tv_usec: 0,
};
setitimer(
ITIMER_REAL,
&mut Itimerval {
it_interval,
it_value,
},
null_mut(),
);
}
self.executor.post_exec(_state, _event_mgr, _input)
}
fn run_target(&mut self, input: &I) -> Result<ExitKind, Error> {
self.executor.run_target(input)
}
}

View File

@ -157,6 +157,46 @@ impl Default for CrashFeedback {
}
}
#[derive(Serialize, Deserialize, Clone, Debug)]
pub struct TimeoutFeedback {}
impl<I> Feedback<I> for TimeoutFeedback
where
I: Input,
{
fn is_interesting<OT: ObserversTuple>(
&mut self,
_input: &I,
_observers: &OT,
exit_kind: ExitKind,
) -> Result<u32, Error> {
if exit_kind == ExitKind::Timeout {
Ok(1)
} else {
Ok(0)
}
}
}
impl Named for TimeoutFeedback {
#[inline]
fn name(&self) -> &str {
"TimeoutFeedback"
}
}
impl TimeoutFeedback {
pub fn new() -> Self {
Self {}
}
}
impl Default for TimeoutFeedback {
fn default() -> Self {
Self::new()
}
}
/// Nop feedback that annotates execution time in the new testcase, if any
#[derive(Serialize, Deserialize, Clone, Debug)]
pub struct TimeFeedback {