Skip to content

Commit

Permalink
merging upstream again
Browse files Browse the repository at this point in the history
  • Loading branch information
riesentoaster committed May 31, 2024
1 parent 7b90873 commit 331ff66
Show file tree
Hide file tree
Showing 16 changed files with 1,453 additions and 1 deletion.
2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ members = [
"utils/build_and_test_fuzzers",
"utils/deexit",
"utils/libafl_benches",
"utils/gramatron/construct_automata",
"utils/gramatron/construct_automata", "fuzzers/coreutils_report_unguided",
]
default-members = [
"libafl",
Expand Down
1 change: 1 addition & 0 deletions fuzzers/coreutils_differential/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
crashes*
29 changes: 29 additions & 0 deletions fuzzers/coreutils_differential/Cargo.toml
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"
160 changes: 160 additions & 0 deletions fuzzers/coreutils_differential/Makefile.toml
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 ${@}
'''
25 changes: 25 additions & 0 deletions fuzzers/coreutils_differential/README.md
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.
52 changes: 52 additions & 0 deletions fuzzers/coreutils_differential/coverage.c
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;
}
13 changes: 13 additions & 0 deletions fuzzers/coreutils_differential/get_guard_num/Cargo.toml
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"
69 changes: 69 additions & 0 deletions fuzzers/coreutils_differential/get_guard_num/src/lib.rs
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 fuzzers/coreutils_differential/setup_guard_redirection/Cargo.toml
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"
Loading

0 comments on commit 331ff66

Please sign in to comment.