forked from AFLplusplus/LibAFL
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
7b90873
commit 331ff66
Showing
16 changed files
with
1,453 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
crashes* |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,29 @@ | ||
[package] | ||
name = "coreutils_differential" | ||
version = "0.1.0" | ||
authors = ["Valentin Huber <[email protected]"] | ||
edition = "2021" | ||
license = "MIT" | ||
|
||
[workspace] | ||
members = ["get_guard_num", "setup_guard_redirection"] | ||
|
||
[profile.release] | ||
# lto = true | ||
# codegen-units = 1 | ||
# opt-level = 3 | ||
debug = true | ||
|
||
[features] | ||
default = ["differential"] | ||
gnu = [] | ||
uutils = [] | ||
differential = ["gnu", "uutils"] | ||
introspection = ["libafl/scalability_introspection", "libafl/introspection"] | ||
|
||
[dependencies] | ||
libafl = { path = "../../libafl/", features = ["cli", "errors_backtrace"] } | ||
libafl_bolts = { path = "../../libafl_bolts/" } | ||
libc = "0.2" | ||
serde = { version = "1.0.199", features = ["derive"] } | ||
serde_json = "1.0.116" |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,160 @@ | ||
[config] | ||
# make sure the commands are not executed in the sub-workspaces | ||
default_to_workspace = false | ||
|
||
[env] | ||
CARGO_TARGET_DIR = "target" | ||
PROFILE = "release" | ||
PROFILE_DIR = "release" | ||
COREUTILS_VERSION = "9.5" | ||
|
||
[tasks.create_target_dir] | ||
script_runner = "@shell" | ||
condition = { files_not_exist = ["./${CARGO_TARGET_DIR}"] } | ||
script = ''' | ||
mkdir -p "${CARGO_TARGET_DIR}" | ||
''' | ||
|
||
[tasks.coverage_collector] | ||
condition = { files_not_exist = ["./${CARGO_TARGET_DIR}/coverage.o"] } | ||
dependencies = ["create_target_dir"] | ||
script_runner = "@shell" | ||
script = ''' | ||
clang -c -o "./${CARGO_TARGET_DIR}/coverage.o" coverage.c | ||
''' | ||
|
||
|
||
[tasks.gnu_coreutils_src] | ||
script_runner = "@shell" | ||
condition = { files_not_exist = ["./${CARGO_TARGET_DIR}/GNU_coreutils"] } | ||
dependencies = ["create_target_dir"] | ||
script = ''' | ||
cd "./${CARGO_TARGET_DIR}" | ||
wget "http://ftp.gnu.org/gnu/coreutils/coreutils-${COREUTILS_VERSION}.tar.gz" | ||
tar -xzf "coreutils-${COREUTILS_VERSION}.tar.gz" | ||
rm "coreutils-${COREUTILS_VERSION}.tar.gz" | ||
mv -f "coreutils-${COREUTILS_VERSION}" GNU_coreutils | ||
''' | ||
|
||
[tasks.gnu_coreutils_bin] | ||
condition = { files_not_exist = [ | ||
"./${CARGO_TARGET_DIR}/GNU_coreutils/Makefile", | ||
] } | ||
dependencies = ["gnu_coreutils_src", "coverage_collector", "create_target_dir"] | ||
script_runner = "@shell" | ||
script = ''' | ||
cd "./${CARGO_TARGET_DIR}" | ||
# absolute path because build system traverses into subdirectories | ||
COVERAGE_FILE=$(realpath "./coverage.o") | ||
cd ./GNU_coreutils | ||
export CC="clang" | ||
export CFLAGS="-g -O2 -fsanitize-coverage=trace-pc-guard" | ||
export LDFLAGS="-rdynamic ${COVERAGE_FILE}" | ||
./configure CFLAGS="$CFLAGS" LDFLAGS="$LDFLAGS" | ||
make | ||
''' | ||
|
||
[tasks.uutils_coreutils_src] | ||
script_runner = "@shell" | ||
condition = { files_not_exist = ["./${CARGO_TARGET_DIR}/uutils_coreutils"] } | ||
dependencies = ["create_target_dir"] | ||
script = ''' | ||
cd "./${CARGO_TARGET_DIR}" | ||
git clone https://github.com/uutils/coreutils | ||
mv coreutils uutils_coreutils | ||
''' | ||
|
||
[tasks.uutils_coreutils_bin] | ||
script_runner = "@shell" | ||
condition = { files_not_exist = [ | ||
"./${CARGO_TARGET_DIR}/uutils_coreutils/target/release/coreutils", | ||
] } | ||
dependencies = [ | ||
"uutils_coreutils_src", | ||
"coverage_collector", | ||
"create_target_dir", | ||
] | ||
script = ''' | ||
cd "./${CARGO_TARGET_DIR}" | ||
# absolute path because build system traverses into subdirectories | ||
COVERAGE_FILE=$(realpath "./coverage.o") | ||
cd ./uutils_coreutils | ||
export CFLAGS="-g" | ||
export RUSTFLAGS=" \ | ||
-Cpasses=sancov-module \ | ||
-Cllvm-args=-sanitizer-coverage-level=3 \ | ||
-Cllvm-args=-sanitizer-coverage-trace-pc-guard \ | ||
-Clink-arg=-rdynamic \ | ||
-Clink-arg=${COVERAGE_FILE}" | ||
export PROFILE="release" | ||
make | ||
''' | ||
|
||
[tasks.coreutils_bin] | ||
dependencies = ["gnu_coreutils_bin", "uutils_coreutils_bin"] | ||
|
||
[tasks.get_guard_num] | ||
script_runner = "@shell" | ||
script = ''' | ||
cargo build --profile ${PROFILE} --package get_guard_num | ||
''' | ||
|
||
[tasks.setup_guard_redirection] | ||
script_runner = "@shell" | ||
script = ''' | ||
cargo build --profile ${PROFILE} --package setup_guard_redirection | ||
''' | ||
|
||
[tasks.preloads] | ||
dependencies = ["get_guard_num", "setup_guard_redirection"] | ||
|
||
[tasks.fuzzer] | ||
dependencies = ["coreutils_bin", "preloads"] | ||
script_runner = "@shell" | ||
script = ''' | ||
cargo build --profile ${PROFILE} | ||
''' | ||
|
||
[tasks.fuzzer_gnu] | ||
dependencies = ["coreutils_bin", "preloads"] | ||
script_runner = "@shell" | ||
script = ''' | ||
cargo build --profile ${PROFILE} --no-default-features --features gnu | ||
''' | ||
|
||
[tasks.fuzzer_uutils] | ||
dependencies = ["coreutils_bin", "preloads"] | ||
script_runner = "@shell" | ||
script = ''' | ||
cargo build --profile ${PROFILE} --no-default-features --features uutils | ||
''' | ||
|
||
[tasks.run] | ||
dependencies = ["fuzzer"] | ||
script_runner = "@shell" | ||
script = ''' | ||
./target/release/coreutils_differential --output crashes --stdout out.log ${@} | ||
''' | ||
|
||
[tasks.run_gnu] | ||
dependencies = ["fuzzer_gnu"] | ||
script_runner = "@shell" | ||
script = ''' | ||
./target/release/coreutils_differential --output crashes --stdout out.log ${@} | ||
''' | ||
|
||
[tasks.run_uutils] | ||
dependencies = ["fuzzer_uutils"] | ||
script_runner = "@shell" | ||
script = ''' | ||
./target/release/coreutils_differential --output crashes --stdout out.log ${@} | ||
''' |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,25 @@ | ||
# Coreutils Coverage | ||
|
||
This is an example of how to perform coverage-guided fuzzing on a binary using a `CommandConfigurator`/`CommandExecutor`. Specifically, it targets coreutils, which adds the following requirements: | ||
- Coreutils use a complex build system (Autotools), and this approach does not require any changes to the build system (nor the source code for that matter) | ||
- The only changes are a different compiler, and different compilation flags, all of which can be passed as command line arguments and/or environment variables (see [Makefile.toml](./Makefile.toml)) | ||
- The binary under test calls `exit` — this is why a `CommandExecutor` is required (since this would quit the fuzzer otherwise) | ||
- Inputs are a combination of command line arguments, `stdin`, and even files | ||
- It further allows analyzing all output of the binary under test | ||
|
||
## Usage | ||
|
||
This project uses [cargo-make](https://sagiegurari.github.io/cargo-make/). Use `cargo make fuzzer` to build all necessary components or invoke the fuzzer directly with `cargo make run`. | ||
|
||
## How Coverage is transmitted | ||
1. Coreutils are compiled using clang's `-fsanitize-coverage=trace-pc-guard` (similar to LibAFL_target, and may in the future be unified) | ||
1. First, a custom coverage collection handler ([coverage.c](./coverage.c)) is compiled to an object file. | ||
2. Then, this file is passed to the compiler and linked instead of the default handler. | ||
2. Before starting the actual fuzzing, the fuzzer performs the following steps: | ||
1. First, the fuzzer loads the binary under test with an overridden main function (using the shared library created in [get_guard_num](./get_guard_num/)), which interfaces with the linked custom coverage gathering functionality to retrieve the number of guards. This number is then passed back to the fuzzer without calling the main function of the binary under test. | ||
2. Then, the fuzzer allocates a shared memory region with the retrieved size and sets it up to be accessible from the child. | ||
3. Then, the actual fuzzing starts: | ||
1. The binary is called with a reference to the shared memory region. | ||
2. The binary is once again wrapped with a shared library. This wrapper ([setup_guard_redirection](./setup_guard_redirection/)) first parses the shared memory reference. It then removes any reference from `argc`/`argv` and calls the main function of the binary under test. | ||
3. During the teardown phase, the wrapper once again intersects the usual program flow to copy the coverage data collected during execution to the shared memory region. It then continues teardown. | ||
4. This data is then accessible from the fuzzer and can be used to guide fuzzing. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,52 @@ | ||
#include <stdio.h> | ||
#include <stdint.h> | ||
|
||
// #define LOG_COV 1 | ||
|
||
static uint32_t *guard_stop = 0; | ||
static uint32_t *guard_start = 0; | ||
|
||
void __sanitizer_cov_trace_pc_guard_init(uint32_t *start, uint32_t *stop) { | ||
if (start == stop) { | ||
#ifdef LOG_COV | ||
fprintf(stderr, "Skipping initialization"); | ||
#endif | ||
return; | ||
}; | ||
|
||
#ifdef LOG_COV | ||
fprintf(stderr, "Initializing called with start %p and stop %p\n", start, | ||
stop); | ||
#endif | ||
guard_start = start; | ||
guard_stop = stop; | ||
|
||
for (uint32_t *x = start; x < stop; x++) | ||
*x = 0; | ||
|
||
#ifdef LOG_COV | ||
fprintf(stderr, "Done with initialization\n"); | ||
#endif | ||
} | ||
|
||
void __sanitizer_cov_trace_pc_guard(uint32_t *guard) { | ||
// *guard += 1; | ||
*guard = 1; | ||
#ifdef LOG_COV | ||
fprintf(stderr, "Updated guard %p to %u\n", guard, *guard); | ||
#endif | ||
} | ||
|
||
__attribute__((visibility("default"))) size_t get_guard_count() { | ||
#ifdef LOG_COV | ||
fprintf(stderr, "Returned guard count %zu\n", guard_stop - guard_start); | ||
#endif | ||
return guard_stop - guard_start; | ||
} | ||
|
||
__attribute__((visibility("default"))) uint32_t *get_guard_values() { | ||
#ifdef LOG_COV | ||
fprintf(stderr, "Returned guard values %p\n", guard_start); | ||
#endif | ||
return guard_start; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
[package] | ||
name = "get_guard_num" | ||
version = "0.1.0" | ||
authors = ["Valentin Huber <[email protected]"] | ||
edition = "2021" | ||
license = "MIT" | ||
|
||
[lib] | ||
name = "get_guard_num" | ||
crate_type = ["cdylib"] | ||
|
||
[dependencies] | ||
libc = "0.2" |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,69 @@ | ||
#![allow(clippy::missing_safety_doc)] | ||
|
||
use libc::{c_void, dlerror, dlsym, RTLD_DEFAULT, RTLD_NEXT}; | ||
|
||
use std::{ | ||
ffi::CStr, | ||
mem::{size_of, transmute_copy}, | ||
}; | ||
|
||
pub unsafe fn get_symbol<T>(name: &CStr, search_global: bool) -> T { | ||
assert_eq!( | ||
size_of::<*mut c_void>(), | ||
size_of::<T>(), | ||
"T must be the same size as a pointer." | ||
); | ||
|
||
let handle = if search_global { | ||
RTLD_DEFAULT | ||
} else { | ||
RTLD_NEXT | ||
}; | ||
|
||
let symbol_pointer: *mut c_void = dlsym(handle, name.as_ptr()); | ||
if symbol_pointer.is_null() { | ||
panic!( | ||
"Got a NULL pointer, could not load symbol {:#?}: {:#?}", | ||
name, | ||
dlerror() | ||
); | ||
} | ||
transmute_copy(&symbol_pointer) | ||
} | ||
|
||
pub type LibcStartMainFunc = fn( | ||
unsafe extern "C" fn(i32, *const *const u8, *const *const u8) -> i32, | ||
i32, | ||
*const *const char, | ||
extern "C" fn(i32, *const *const u8, *const *const u8) -> i32, | ||
extern "C" fn(), | ||
unsafe extern "C" fn(), | ||
*mut c_void, | ||
) -> i32; | ||
|
||
#[no_mangle] | ||
#[allow(clippy::similar_names)] | ||
pub unsafe extern "C" fn __libc_start_main( | ||
_main: unsafe extern "C" fn(i32, *const *const u8, *const *const u8) -> i32, | ||
argc: i32, | ||
argv: *mut *const char, | ||
init: extern "C" fn(i32, *const *const u8, *const *const u8) -> i32, | ||
fini: extern "C" fn(), | ||
rtld_fini: unsafe extern "C" fn(), | ||
stack_end: *mut c_void, | ||
) -> i32 { | ||
let orig_libc_start_main: LibcStartMainFunc = get_symbol(c"__libc_start_main", false); | ||
orig_libc_start_main(main_hook, argc, argv, init, fini, rtld_fini, stack_end) | ||
} | ||
|
||
#[no_mangle] | ||
pub unsafe extern "C" fn main_hook( | ||
_argc: i32, | ||
_argv: *const *const u8, | ||
_env: *const *const u8, | ||
) -> i32 { | ||
let get_guard_count: fn() -> usize = get_symbol(c"get_guard_count", true); | ||
let guard_count = get_guard_count(); | ||
println!("{}", guard_count); | ||
0 | ||
} |
18 changes: 18 additions & 0 deletions
18
fuzzers/coreutils_differential/setup_guard_redirection/Cargo.toml
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
[package] | ||
name = "setup_guard_redirection" | ||
version = "0.1.0" | ||
authors = ["Valentin Huber <[email protected]"] | ||
edition = "2021" | ||
license = "MIT" | ||
|
||
[lib] | ||
name = "setup_guard_redirection" | ||
crate_type = ["cdylib"] | ||
|
||
[dependencies] | ||
libafl_targets = { path = "../../../libafl_targets", features = [ | ||
"sancov_pcguard_edges", | ||
] } | ||
libafl_bolts = { path = "../../../libafl_bolts/" } | ||
libc = "0.2" | ||
serde_json = "1.0.116" |
Oops, something went wrong.