Skip to content

Commit

Permalink
Tree-shaped multi-machine fuzzing (#2302)
Browse files Browse the repository at this point in the history
* tree-shaped multi-machine fuzzing

* forgot main file

* aaa

* moving things around

* fix

* working?

* remove debug panic

* aaa

* aaa

* fmt

* normal centralized adapted

* removed old useless code

* cleanup

* llmp hooks

* working multi machine apparently?

* aaa

* cleanup (#2305)

* added old message dispatch.
thread safety stuff

* testing things around

* opti opti opti

* :)

* fuzz

* limit the amound received at once to avoid congestion

* remove useless corpus
mv to sqlite
less warnings

* aaa

* ;

* big opti

* adding cfgs

* fix

* fixer

* fix

* s

* clippy and reduce generics

* debugging

* fix

* more robust disconnection

* aaa

* aaa

* aaa

* nostd

* more nostd

* clippy

* not in ci

* unused

* aaa

* doc

* clippy

* clippy

* clippy

* no crash in libpng

* aaa

* aaa

* aaa

* aaa

* graph generator

* fix

* fix

* windows fix all

---------

Co-authored-by: Dongjia "toka" Zhang <tokazerkje@outlook.com>
  • Loading branch information
rmalmain and tokatoka committed Jun 17, 2024
1 parent a4070de commit fa17f47
Show file tree
Hide file tree
Showing 44 changed files with 2,404 additions and 1,753 deletions.
3 changes: 2 additions & 1 deletion .github/workflows/build_and_test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -294,7 +294,8 @@ jobs:
- ./fuzzers/libfuzzer_libpng_launcher
- ./fuzzers/libfuzzer_libpng_accounting
- ./fuzzers/forkserver_libafl_cc
- ./fuzzers/libfuzzer_libpng_tcp_manager
# - ./fuzzers/libfuzzer_libpng_tcp_manager
# - ./fuzzers/sqlite_centralized_multi_machine
- ./fuzzers/backtrace_baby_fuzzers
- ./fuzzers/fuzzbench_qemu
- ./fuzzers/nyx_libxml2_parallel
Expand Down
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ vendor
.DS_Store
.env

*.test
*.tmp
*.swp
*.o
Expand Down
2 changes: 2 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,8 @@ exclude = [
"utils/noaslr",
"utils/gdb_qemu",
"utils/libafl_fmt",
"utils/desyscall",
"utils/multi_machine_generator",
"scripts",
]

Expand Down
2 changes: 1 addition & 1 deletion fuzzers/libfuzzer_libpng_centralized/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -136,7 +136,7 @@ pub extern "C" fn libafl_main() {
let monitor = MultiMonitor::new(|s| println!("{s}"));

let mut secondary_run_client = |state: Option<_>,
mut mgr: CentralizedEventManager<_, _>,
mut mgr: CentralizedEventManager<_, _, _, _>,
_core_id: CoreId| {
// Create an observation channel using the coverage map
let edges_observer =
Expand Down
2 changes: 1 addition & 1 deletion fuzzers/libfuzzer_libpng_tcp_manager/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ debug = true

[build-dependencies]
cc = { version = "1.0", features = ["parallel"] }
which = "4.4"
which = "6.0"

[dependencies]
libafl = { path = "../../libafl/", features = ["default", "tcp_manager"] }
Expand Down
34 changes: 34 additions & 0 deletions fuzzers/sqlite_centralized_multi_machine/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
[package]
name = "libfuzzer_libpng_launcher_centralized_multi_machine"
version = "0.12.0"
authors = ["Romain Malmain <romain.malmain@pm.me>", "Andrea Fioraldi <andreafioraldi@gmail.com>", "Dominik Maier <domenukk@gmail.com>"]
edition = "2021"

[features]
default = ["std"]
std = []

[profile.release]
lto = true
codegen-units = 1
opt-level = 3
debug = true

[build-dependencies]
cc = { version = "1.0", features = ["parallel"] }
which = "6.0"

[dependencies]
# no llmp compression for now, better perfs.
libafl = { path = "../../libafl", default-features = false, features = ["std", "derive", "llmp_small_maps", "llmp_broker_timeouts", "rand_trait", "fork", "prelude", "gzip", "regex", "serdeany_autoreg", "tui_monitor", "std", "derive", "rand_trait", "fork", "prelude", "gzip", "regex", "scalability_introspection", "multi_machine", "errors_backtrace"] }
libafl_bolts = { path = "../../libafl_bolts", features = ["xxh3"] }
libafl_targets = { path = "../../libafl_targets", features = ["sancov_pcguard_hitcounts", "libfuzzer"] }
# TODO Include it only when building cc
libafl_cc = { path = "../../libafl_cc" }
clap = { version = "4.0", features = ["derive"] }
mimalloc = { version = "*", default-features = false }
env_logger = "0.11"

[lib]
name = "libfuzzer_libpng"
crate-type = ["staticlib"]
133 changes: 133 additions & 0 deletions fuzzers/sqlite_centralized_multi_machine/Makefile.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,133 @@
# Variables
[env]
FUZZER_NAME='fuzzer_libpng_launcher'
CARGO_TARGET_DIR = { value = "${PROJECT_DIR}/target", condition = { env_not_set = ["CARGO_TARGET_DIR"] } }
PROFILE = { value = "release", condition = {env_not_set = ["PROFILE"]} }
PROFILE_DIR = {value = "release", condition = {env_not_set = ["PROFILE_DIR"] }}
LIBAFL_CC = '${CARGO_TARGET_DIR}/${PROFILE_DIR}/libafl_cc'
LIBAFL_CXX = '${CARGO_TARGET_DIR}/${PROFILE}/libafl_cxx'
FUZZER = '${CARGO_TARGET_DIR}/${PROFILE_DIR}/${FUZZER_NAME}'
PROJECT_DIR = { script = ["pwd"] }

[tasks.unsupported]
script_runner="@shell"
script='''
echo "Cargo-make not integrated yet on this platform"
'''

# libpng
[tasks.libpng]
linux_alias = "libpng_unix"
mac_alias = "libpng_unix"
windows_alias = "unsupported"

[tasks.libpng_unix]
condition = { files_not_exist = ["./libpng-1.6.37"]}
script_runner="@shell"
script='''
wget https://github.com/glennrp/libpng/archive/refs/tags/v1.6.37.tar.gz
tar -xvf v1.6.37.tar.gz
'''

# Compilers
[tasks.cxx]
linux_alias = "cxx_unix"
mac_alias = "cxx_unix"
windows_alias = "unsupported"

[tasks.cxx_unix]
command = "cargo"
args = ["build" , "--profile", "${PROFILE}"]

[tasks.cc]
linux_alias = "cc_unix"
mac_alias = "cc_unix"
windows_alias = "unsupported"

[tasks.cc_unix]
command = "cargo"
args = ["build" , "--profile", "${PROFILE}"]

# Library
[tasks.lib]
linux_alias = "lib_unix"
mac_alias = "lib_unix"
windows_alias = "unsupported"

[tasks.lib_unix]
script_runner="@shell"
script='''
cd libpng-1.6.37 && ./configure --enable-shared=no --with-pic=yes --enable-hardware-optimizations=yes
cd "${PROJECT_DIR}"
make -C libpng-1.6.37 CC="${CARGO_TARGET_DIR}/${PROFILE_DIR}/libafl_cc" CXX="${CARGO_TARGET_DIR}/${PROFILE_DIR}/libafl_cxx"
'''
dependencies = [ "libpng", "cxx", "cc" ]


# Harness
[tasks.fuzzer]
linux_alias = "fuzzer_unix"
mac_alias = "fuzzer_unix"
windows_alias = "unsupported"

[tasks.fuzzer_unix]
command = "${CARGO_TARGET_DIR}/${PROFILE_DIR}/libafl_cxx"
args = ["${PROJECT_DIR}/harness.cc", "${PROJECT_DIR}/libpng-1.6.37/.libs/libpng16.a", "-I", "${PROJECT_DIR}/libpng-1.6.37/", "-o", "${FUZZER_NAME}", "-lm", "-lz"]
dependencies = [ "lib", "cxx", "cc" ]

# Run the fuzzer
[tasks.run]
linux_alias = "run_unix"
mac_alias = "run_unix"
windows_alias = "unsupported"

[tasks.run_unix]
script_runner = "@shell"
script='''
./${FUZZER_NAME} --cores 0-1 --input ./corpus --parent-addr 0.0.0.0:12345
'''
dependencies = [ "fuzzer" ]

# Test
[tasks.test]
linux_alias = "test_unix"
mac_alias = "test_mac"
windows_alias = "unsupported"

[tasks.test_unix]
script_runner = "@shell"
script='''
rm -rf libafl_unix_shmem_server || true
timeout 31s ./${FUZZER_NAME} --cores 0-1 --input ./corpus 2>/dev/null | tee fuzz_stdout.log || true
if grep -qa "corpus: 30" fuzz_stdout.log; then
echo "Fuzzer is working"
else
echo "Fuzzer does not generate any testcases or any crashes"
exit 1
fi
'''
dependencies = [ "fuzzer" ]

[tasks.test_mac]
script_runner = "@shell"
script='''
rm -rf libafl_unix_shmem_server || true
timeout 31s ./${FUZZER_NAME} --cores 0 --input ./corpus 2>/dev/null | tee fuzz_stdout.log || true
'''
dependencies = [ "fuzzer" ]

# Clean up
[tasks.clean]
linux_alias = "clean_unix"
mac_alias = "clean_unix"
windows_alias = "unsupported"

[tasks.clean_unix]
# Disable default `clean` definition
clear = true
script_runner="@shell"
script='''
rm -f ./${FUZZER_NAME}
make -C libpng-1.6.37 clean
cargo clean
'''
47 changes: 47 additions & 0 deletions fuzzers/sqlite_centralized_multi_machine/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
# Libfuzzer for libpng, with launcher

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.

In contrast to the normal libfuzzer libpng example, this uses the `launcher` feature, that automatically spawns `n` child processes, and binds them to a free core.

## Build

To build this example, run

```bash
cargo build --release
```

This will build the library with the fuzzer (src/lib.rs) with the libfuzzer compatibility layer and the SanitizerCoverage runtime functions for coverage feedback.
In addition, it will also build two C and C++ compiler wrappers (bin/libafl_c(libafl_c/xx).rs) that you must use to compile the target.

Then download libpng, and unpack the archive:
```bash
wget https://github.com/glennrp/libpng/archive/refs/tags/v1.6.37.tar.gz
tar -xvf v1.6.37.tar.gz
```

Now compile libpng, using the libafl_cc compiler wrapper:

```bash
cd libpng-1.6.37
./configure
make CC=../target/release/libafl_cc CXX=../target/release/libafl_cxx -j `nproc`
```

You can find the static lib at `libpng-1.6.37/.libs/libpng16.a`.

Now, we have to build the libfuzzer harness and link all together to create our fuzzer binary.

```
cd ..
./target/release/libafl_cxx ./harness.cc libpng-1.6.37/.libs/libpng16.a -I libpng-1.6.37/ -o fuzzer_libpng -lz -lm
```

Afterwards, the fuzzer will be ready to run.

## Run

Just run once, the launcher feature should do the rest.
42 changes: 42 additions & 0 deletions fuzzers/sqlite_centralized_multi_machine/build.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
#!/bin/bash

if [ ! -d "sqlite3" ]; then
curl 'https://sqlite.org/src/tarball/sqlite.tar.gz?r=c78cbf2e86850cc6' -o sqlite3.tar.gz && mkdir sqlite3 && pushd sqlite3 && tar xzf ../sqlite3.tar.gz --strip-components 1 && popd
mkdir corpus
find ./sqlite3 -name "*.test" -exec cp {} corpus/ \;
fi

if [ "$1" = "release" ]; then
cargo build --release
else
cargo build
fi

export CC=`pwd`/target/debug/libafl_cc
export CXX=`pwd`/target/debug/libafl_cxx
export CFLAGS='--libafl'
export CXXFLAGS='--libafl'
export CFLAGS="$CFLAGS -DSQLITE_MAX_LENGTH=128000000 \
-DSQLITE_MAX_SQL_LENGTH=128000000 \
-DSQLITE_MAX_MEMORY=25000000 \
-DSQLITE_PRINTF_PRECISION_LIMIT=1048576 \
-DSQLITE_DEBUG=1 \
-DSQLITE_MAX_PAGE_COUNT=16384"
pushd sqlite3

if [ ! -f "Makefile" ]; then
echo "Run configure..."
./configure
fi
make -j$(nproc)
make sqlite3.c
popd

if [ "$1" = "release" ]; then
./target/release/libafl_cc --libafl -I ./sqlite3 -c ./sqlite3/test/ossfuzz.c -o ./sqlite3/test/ossfuzz.o
./target/release/libafl_cxx --libafl -o ossfuzz ./sqlite3/test/ossfuzz.o ./sqlite3/sqlite3.o -pthread -ldl -lz
else
./target/debug/libafl_cc --libafl -I ./sqlite3 -c ./sqlite3/test/ossfuzz.c -o ./sqlite3/test/ossfuzz.o
./target/debug/libafl_cxx --libafl -o ossfuzz ./sqlite3/test/ossfuzz.o ./sqlite3/sqlite3.o -pthread -ldl -lz
fi

3 changes: 3 additions & 0 deletions fuzzers/sqlite_centralized_multi_machine/run_child.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
#!/bin/bash

./ossfuzz --cores 0-3 --input ./corpus --parent-addr 0.0.0.0:50000 --broker-port 3000
3 changes: 3 additions & 0 deletions fuzzers/sqlite_centralized_multi_machine/run_parent.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
#!/bin/bash

./ossfuzz --cores 4-7 --input ./corpus
36 changes: 36 additions & 0 deletions fuzzers/sqlite_centralized_multi_machine/src/bin/libafl_cc.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
use std::env;

use libafl_cc::{ClangWrapper, CompilerWrapper, ToolWrapper};

pub fn main() {
let args: Vec<String> = env::args().collect();
if args.len() > 1 {
let mut dir = env::current_exe().unwrap();
let wrapper_name = dir.file_name().unwrap().to_str().unwrap();

let is_cpp = match wrapper_name[wrapper_name.len()-2..].to_lowercase().as_str() {
"cc" => false,
"++" | "pp" | "xx" => true,
_ => panic!("Could not figure out if c or c++ wrapper was called. Expected {dir:?} to end with c or cxx"),
};

dir.pop();

let mut cc = ClangWrapper::new();
if let Some(code) = cc
.cpp(is_cpp)
// silence the compiler wrapper output, needed for some configure scripts.
.silence(true)
.parse_args(&args)
.expect("Failed to parse the command line")
.link_staticlib(&dir, "libfuzzer_libpng")
.add_arg("-fsanitize-coverage=trace-pc-guard")
.run()
.expect("Failed to run the wrapped compiler")
{
std::process::exit(code);
}
} else {
panic!("LibAFL CC: No Arguments given");
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
pub mod libafl_cc;

fn main() {
libafl_cc::main();
}
Loading

0 comments on commit fa17f47

Please sign in to comment.