Reachability example (#65)
* add reachability observer/feedback * add fuzzer exmaple * fmt * remove reachabilityobserver, use stdmapobserver instead * update diff.patch * update README * fix the clippy warning * Squashed commit of the following: commit f20524ebd77011481e86b420c925e8504bd11308 Author: Andrea Fioraldi <andreafioraldi@gmail.com> Date: Tue May 4 16:00:39 2021 +0200 Composing feedback (#85) * composing feedbacks as logic operations and bump to 0.2 * adapt fuzzers and libafl_frida * fix windows build commit e06efaa03bc96ef71740d7376c7381572bf11c6c Author: Andrea Fioraldi <andreafioraldi@gmail.com> Date: Tue May 4 13:54:46 2021 +0200 Observers refactor (#84) * new observer structure with HasExecHooks * adapt libafl_frida to new observers * docstrings commit 17c6fcd31cb746c099654be2b7a168bd04d46381 Merge: 08a2d43 a78a4b7 Author: Andrea Fioraldi <andreafioraldi@gmail.com> Date: Mon May 3 11:16:49 2021 +0200 Merge branch 'main' into dev commit 08a2d43790797d8864565fec99e7043289a46283 Author: David CARLIER <devnexen@gmail.com> Date: Mon May 3 10:15:28 2021 +0100 Build warning fix proposal, mostly about reference to packed fields. (#79) commit 88fe8fa532ac34cbc10782f5f71264f620385dda Merge: d5d46ad d2e7719 Author: Andrea Fioraldi <andreafioraldi@gmail.com> Date: Mon May 3 11:05:42 2021 +0200 Merge pull request #80 from marcograss/book-typos fixed some minor typos in the book commit a78a4b73fa798c1ed7a3d053369cca435e57aa07 Author: s1341 <s1341@users.noreply.github.com> Date: Mon May 3 10:34:15 2021 +0300 frida-asan: Un-inline report funclet to reduce code bloat (#81) * frida-asan: Outline report funclet to reduce code bloat * fmt commit d2e7719a8bea3a993394c187e2183d3e91f02c75 Author: Marco Grassi <marco.gra@gmail.com> Date: Sun May 2 21:58:33 2021 +0800 fixed some minor typos in the book commit d5d46ad7e440fd4a2925352ed1ccb9ced5d9463d Author: Dominik Maier <domenukk@gmail.com> Date: Sat May 1 23:09:10 2021 +0200 make clippy less pedantic commit 52d25e979e23589587c885803641058dc36aa998 Author: Dominik Maier <domenukk@gmail.com> Date: Sat May 1 22:23:59 2021 +0200 fixing clippy::match-same-arms commit cd66f880dea830d1e38e89fd1bf3c20fd89c9d70 Author: Dominik Maier <domenukk@gmail.com> Date: Sat May 1 14:02:07 2021 +0200 fixed clippy run in workflow commit ddcf086acde2b703c36e4ec3976588313fc3d591 Author: Dominik Maier <domenukk@gmail.com> Date: Sat May 1 13:53:29 2021 +0200 Update README.md commit c715f1fe6e42942e53bd13ea6a23214620f6c829 Author: Dominik Maier <domenukk@gmail.com> Date: Sat May 1 13:48:38 2021 +0200 using clippy.sh commit 9374b26b1d2d44c6042fdd653a8d960ce698592c Author: Dominik Maier <domenukk@gmail.com> Date: Sat May 1 13:47:44 2021 +0200 some clippy warning ignored commit b9e75c0c98fdfb1e70778e6f3612a94b71dcd21a Author: Dominik Maier <domenukk@gmail.com> Date: Sat May 1 13:24:02 2021 +0200 Tcp Broker to Broker Communication (#66) * initial b2b implementation * no_std and clippy fixes * b2b testcase added * more correct testcases * fixed b2b * typo * fixed unused warning * feedbacks now return a boolean value * use feedback_or, and modify Cargo.toml * fix diff between dev and this branch * fmt Co-authored-by: Dominik Maier <domenukk@gmail.com>
This commit is contained in:
parent
2e192fd6cc
commit
46e010ba0b
1
fuzzers/libfuzzer_reachability/.gitignore
vendored
Normal file
1
fuzzers/libfuzzer_reachability/.gitignore
vendored
Normal file
@ -0,0 +1 @@
|
||||
libpng-*
|
30
fuzzers/libfuzzer_reachability/Cargo.toml
Normal file
30
fuzzers/libfuzzer_reachability/Cargo.toml
Normal file
@ -0,0 +1,30 @@
|
||||
[package]
|
||||
name = "libfuzzer_reachability"
|
||||
version = "0.2.0"
|
||||
authors = ["Andrea Fioraldi <andreafioraldi@gmail.com>", "Dominik Maier <domenukk@gmail.com>"]
|
||||
edition = "2018"
|
||||
|
||||
[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 = { version = "4.0.2" }
|
||||
num_cpus = "1.0"
|
||||
|
||||
[dependencies]
|
||||
libafl = { path = "../../libafl/" }
|
||||
libafl_targets = { path = "../../libafl_targets/", features = ["pcguard_hitcounts", "libfuzzer"] }
|
||||
# TODO Include it only when building cc
|
||||
libafl_cc = { path = "../../libafl_cc/" }
|
||||
|
||||
[lib]
|
||||
name = "libfuzzer_libpng"
|
||||
crate-type = ["staticlib"]
|
79
fuzzers/libfuzzer_reachability/README.md
Normal file
79
fuzzers/libfuzzer_reachability/README.md
Normal file
@ -0,0 +1,79 @@
|
||||
# Libfuzzer for libpng
|
||||
|
||||
This folder contains an example fuzzer for libpng, using LLMP for fast multi-process fuzzing and crash detection.
|
||||
|
||||
In contrast to other fuzzer examples, this setup uses `fuzz_loop_for`, to occasionally respawn the fuzzer executor.
|
||||
While this costs performance, it can be useful for targets with memory leaks or other instabilities.
|
||||
If your target is really instable, however, consider exchanging the `InProcessExecutor` for a `ForkserverExecutor` instead.
|
||||
|
||||
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.
|
||||
|
||||
## 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.
|
||||
|
||||
The compiler wrappers, `libafl_cc` and libafl_cxx`, will end up in `./target/release/` (or `./target/debug`, in case you did not build with the `--release` flag).
|
||||
|
||||
Then download libpng, and unpack the archive:
|
||||
```bash
|
||||
wget https://deac-fra.dl.sourceforge.net/project/libpng/libpng16/1.6.37/libpng-1.6.37.tar.xz
|
||||
tar -xvf libpng-1.6.37.tar.xz
|
||||
```
|
||||
Run `patch libpng-1.6.37/png.c diff.patch` before compiling the libpng
|
||||
Now compile libpng, using the libafl_cc compiler wrapper:
|
||||
|
||||
```bash
|
||||
cd libpng-1.6.37
|
||||
./configure
|
||||
make CC=$(realpath ../target/release/libafl_cc) CXX=$(realpath ../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
|
||||
```
|
||||
|
||||
Afterward, the fuzzer will be ready to run.
|
||||
Note that, unless you use the `launcher`, you will have to run the binary multiple times to actually start the fuzz process, see `Run` in the following.
|
||||
This allows you to run multiple different builds of the same fuzzer alongside, for example, with and without ASAN (`-fsanitize=address`) or with different mutators.
|
||||
|
||||
## 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. Currently you must run the clients from the libfuzzer_libpng directory for them to be able to access the PNG corpus.
|
||||
|
||||
```
|
||||
./fuzzer_libpng
|
||||
|
||||
[libafl/src/bolts/llmp.rs:407] "We're the broker" = "We\'re the broker"
|
||||
Doing broker things. Run this tool again to start fuzzing in a client.
|
||||
```
|
||||
|
||||
And after running the above again in a separate terminal:
|
||||
|
||||
```
|
||||
[libafl/src/bolts/llmp.rs:1464] "New connection" = "New connection"
|
||||
[libafl/src/bolts/llmp.rs:1464] addr = 127.0.0.1:33500
|
||||
[libafl/src/bolts/llmp.rs:1464] stream.peer_addr().unwrap() = 127.0.0.1:33500
|
||||
[LOG Debug]: Loaded 4 initial testcases.
|
||||
[New Testcase #2] clients: 3, corpus: 6, objectives: 0, executions: 5, exec/sec: 0
|
||||
< fuzzing stats >
|
||||
```
|
||||
|
||||
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).
|
||||
|
BIN
fuzzers/libfuzzer_reachability/corpus/not_kitty.png
Normal file
BIN
fuzzers/libfuzzer_reachability/corpus/not_kitty.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 218 B |
BIN
fuzzers/libfuzzer_reachability/corpus/not_kitty_alpha.png
Normal file
BIN
fuzzers/libfuzzer_reachability/corpus/not_kitty_alpha.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 376 B |
BIN
fuzzers/libfuzzer_reachability/corpus/not_kitty_gamma.png
Normal file
BIN
fuzzers/libfuzzer_reachability/corpus/not_kitty_gamma.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 228 B |
BIN
fuzzers/libfuzzer_reachability/corpus/not_kitty_icc.png
Normal file
BIN
fuzzers/libfuzzer_reachability/corpus/not_kitty_icc.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 427 B |
9
fuzzers/libfuzzer_reachability/diff.patch
Normal file
9
fuzzers/libfuzzer_reachability/diff.patch
Normal file
@ -0,0 +1,9 @@
|
||||
15a16,19
|
||||
> #define TARGET_SIZE 4
|
||||
>
|
||||
> size_t __lafl_dummy_list[TARGET_SIZE] = {0};
|
||||
> size_t *__libafl_target_list = __lafl_dummy_list;
|
||||
2562a2567
|
||||
> __lafl_dummy_list[0] = 1;
|
||||
2584a2590
|
||||
> __lafl_dummy_list[1] = 1;
|
197
fuzzers/libfuzzer_reachability/harness.cc
Normal file
197
fuzzers/libfuzzer_reachability/harness.cc
Normal 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;
|
||||
}
|
||||
|
33
fuzzers/libfuzzer_reachability/src/bin/libafl_cc.rs
Normal file
33
fuzzers/libfuzzer_reachability/src/bin/libafl_cc.rs
Normal file
@ -0,0 +1,33 @@
|
||||
use libafl_cc::{ClangWrapper, CompilerWrapper, LIB_EXT, LIB_PREFIX};
|
||||
use std::env;
|
||||
|
||||
fn main() {
|
||||
let args: Vec<String> = env::args().collect();
|
||||
if args.len() > 1 {
|
||||
let mut dir = env::current_exe().unwrap();
|
||||
dir.pop();
|
||||
|
||||
let mut cc = ClangWrapper::new("clang", "clang++");
|
||||
cc.from_args(&args)
|
||||
.unwrap()
|
||||
.add_arg("-fsanitize-coverage=trace-pc-guard".into())
|
||||
.unwrap()
|
||||
.add_link_arg(
|
||||
dir.join(format!("{}libfuzzer_libpng.{}", LIB_PREFIX, LIB_EXT))
|
||||
.display()
|
||||
.to_string(),
|
||||
)
|
||||
.unwrap();
|
||||
// Libraries needed by libafl on Windows
|
||||
#[cfg(windows)]
|
||||
cc.add_link_arg("-lws2_32".into())
|
||||
.unwrap()
|
||||
.add_link_arg("-lBcrypt".into())
|
||||
.unwrap()
|
||||
.add_link_arg("-lAdvapi32".into())
|
||||
.unwrap();
|
||||
cc.run().unwrap();
|
||||
} else {
|
||||
panic!("LibAFL CC: No Arguments given");
|
||||
}
|
||||
}
|
34
fuzzers/libfuzzer_reachability/src/bin/libafl_cxx.rs
Normal file
34
fuzzers/libfuzzer_reachability/src/bin/libafl_cxx.rs
Normal file
@ -0,0 +1,34 @@
|
||||
use libafl_cc::{ClangWrapper, CompilerWrapper, LIB_EXT, LIB_PREFIX};
|
||||
use std::env;
|
||||
|
||||
fn main() {
|
||||
let args: Vec<String> = env::args().collect();
|
||||
if args.len() > 1 {
|
||||
let mut dir = env::current_exe().unwrap();
|
||||
dir.pop();
|
||||
|
||||
let mut cc = ClangWrapper::new("clang", "clang++");
|
||||
cc.is_cpp()
|
||||
.from_args(&args)
|
||||
.unwrap()
|
||||
.add_arg("-fsanitize-coverage=trace-pc-guard".into())
|
||||
.unwrap()
|
||||
.add_link_arg(
|
||||
dir.join(format!("{}libfuzzer_libpng.{}", LIB_PREFIX, LIB_EXT))
|
||||
.display()
|
||||
.to_string(),
|
||||
)
|
||||
.unwrap();
|
||||
// Libraries needed by libafl on Windows
|
||||
#[cfg(windows)]
|
||||
cc.add_link_arg("-lws2_32".into())
|
||||
.unwrap()
|
||||
.add_link_arg("-lBcrypt".into())
|
||||
.unwrap()
|
||||
.add_link_arg("-lAdvapi32".into())
|
||||
.unwrap();
|
||||
cc.run().unwrap();
|
||||
} else {
|
||||
panic!("LibAFL CC: No Arguments given");
|
||||
}
|
||||
}
|
189
fuzzers/libfuzzer_reachability/src/lib.rs
Normal file
189
fuzzers/libfuzzer_reachability/src/lib.rs
Normal file
@ -0,0 +1,189 @@
|
||||
//! 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};
|
||||
|
||||
use libafl::{
|
||||
bolts::tuples::tuple_list,
|
||||
corpus::{
|
||||
Corpus, InMemoryCorpus, IndexesLenTimeMinimizerCorpusScheduler, OnDiskCorpus,
|
||||
QueueCorpusScheduler,
|
||||
},
|
||||
events::{setup_restarting_mgr_std, EventManager},
|
||||
executors::{inprocess::InProcessExecutor, ExitKind, TimeoutExecutor},
|
||||
feedback_or,
|
||||
feedbacks::{
|
||||
CrashFeedback, MaxMapFeedback, ReachabilityFeedback, TimeFeedback, TimeoutFeedback,
|
||||
},
|
||||
fuzzer::{Fuzzer, StdFuzzer},
|
||||
mutators::scheduled::{havoc_mutations, StdScheduledMutator},
|
||||
mutators::token_mutations::Tokens,
|
||||
observers::{HitcountsMapObserver, StdMapObserver, TimeObserver},
|
||||
stages::mutational::StdMutationalStage,
|
||||
state::{HasCorpus, HasMetadata, State},
|
||||
stats::SimpleStats,
|
||||
utils::{current_nanos, StdRand},
|
||||
Error,
|
||||
};
|
||||
|
||||
use libafl_targets::{libfuzzer_initialize, libfuzzer_test_one_input, EDGES_MAP, MAX_EDGES_NUM};
|
||||
|
||||
#[cfg(unix)]
|
||||
const TARGET_SIZE: usize = 4;
|
||||
|
||||
#[cfg(unix)]
|
||||
extern "C" {
|
||||
static __libafl_target_list: *mut usize;
|
||||
}
|
||||
|
||||
/// The main fn, no_mangle as it is a C main
|
||||
#[no_mangle]
|
||||
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");
|
||||
}
|
||||
|
||||
/// The actual fuzzer
|
||||
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_std(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("edges", &mut EDGES_MAP, MAX_EDGES_NUM)
|
||||
});
|
||||
|
||||
let reachability_observer =
|
||||
unsafe { StdMapObserver::new_from_ptr("png.c", __libafl_target_list, TARGET_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
|
||||
feedback_or!(
|
||||
MaxMapFeedback::new_with_observer_track(&edges_observer, true, false),
|
||||
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
|
||||
feedback_or!(
|
||||
CrashFeedback::new(),
|
||||
TimeoutFeedback::new(),
|
||||
ReachabilityFeedback::new_with_observer(&reachability_observer)
|
||||
),
|
||||
)
|
||||
});
|
||||
|
||||
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 = StdScheduledMutator::new(havoc_mutations());
|
||||
let stage = StdMutationalStage::new(mutator);
|
||||
|
||||
// A fuzzer with just one stage
|
||||
let mut fuzzer = StdFuzzer::new(tuple_list!(stage));
|
||||
|
||||
// A minimization+queue policy to get testcasess from the corpus
|
||||
let scheduler = IndexesLenTimeMinimizerCorpusScheduler::new(QueueCorpusScheduler::new());
|
||||
|
||||
// The wrapped harness function, calling out to the LLVM-style harness
|
||||
let mut harness = |buf: &[u8]| {
|
||||
libfuzzer_test_one_input(buf);
|
||||
ExitKind::Ok
|
||||
};
|
||||
|
||||
// Create the executor for an in-process function with one observer for edge coverage and one for the execution time
|
||||
let mut executor = TimeoutExecutor::new(
|
||||
InProcessExecutor::new(
|
||||
&mut harness,
|
||||
tuple_list!(
|
||||
edges_observer,
|
||||
reachability_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.
|
||||
let args: Vec<String> = env::args().collect();
|
||||
if libfuzzer_initialize(&args) == -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, &scheduler, &corpus_dirs)
|
||||
.expect(&format!(
|
||||
"Failed to load initial corpus at {:?}",
|
||||
&corpus_dirs
|
||||
));
|
||||
println!("We imported {} inputs from disk.", state.corpus().count());
|
||||
}
|
||||
|
||||
// This fuzzer restarts after 1 mio `fuzz_one` executions.
|
||||
// Each fuzz_one will internally do many executions of the target.
|
||||
// If your target is very instable, setting a low count here may help.
|
||||
// However, you will lose a lot of performance that way.
|
||||
let iters = 1_000_000;
|
||||
fuzzer.fuzz_loop_for(
|
||||
&mut state,
|
||||
&mut executor,
|
||||
&mut restarting_mgr,
|
||||
&scheduler,
|
||||
iters,
|
||||
)?;
|
||||
|
||||
// It's important, that we store the state before restarting!
|
||||
// Else, the parent will not respawn a new child and quit.
|
||||
restarting_mgr.on_restart(&mut state)?;
|
||||
|
||||
Ok(())
|
||||
}
|
10
fuzzers/libfuzzer_reachability/test.sh
Executable file
10
fuzzers/libfuzzer_reachability/test.sh
Executable file
@ -0,0 +1,10 @@
|
||||
cargo build --release
|
||||
./target/release/libafl_cxx ./harness.cc libpng-1.6.37/.libs/libpng16.a -I libpng-1.6.37/ -o fuzzer_libpng -lz -lm
|
||||
|
||||
taskset -c 0 ./fuzzer_libpng &
|
||||
sleep 1
|
||||
taskset -c 1 ./fuzzer_libpng 2>/dev/null
|
||||
|
||||
|
||||
killall ./fuzzer_libpng
|
||||
rm -rf ./fuzzer_libpng
|
@ -320,3 +320,82 @@ where
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Clone, Debug)]
|
||||
pub struct ReachabilityFeedback<O> {
|
||||
name: String,
|
||||
target_idx: Vec<usize>,
|
||||
phantom: PhantomData<O>,
|
||||
}
|
||||
|
||||
impl<O> ReachabilityFeedback<O>
|
||||
where
|
||||
O: MapObserver<usize>,
|
||||
{
|
||||
pub fn new_with_observer(map_observer: &O) -> Self {
|
||||
Self {
|
||||
name: map_observer.name().to_string(),
|
||||
target_idx: vec![],
|
||||
phantom: PhantomData,
|
||||
}
|
||||
}
|
||||
pub fn new(name: &'static str) -> Self {
|
||||
Self {
|
||||
name: name.to_string(),
|
||||
target_idx: vec![],
|
||||
phantom: PhantomData,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<I, O> Feedback<I> for ReachabilityFeedback<O>
|
||||
where
|
||||
I: Input,
|
||||
O: MapObserver<usize>,
|
||||
{
|
||||
fn is_interesting<OT: ObserversTuple>(
|
||||
&mut self,
|
||||
_input: &I,
|
||||
observers: &OT,
|
||||
_exit_kind: &ExitKind,
|
||||
) -> Result<bool, Error> {
|
||||
let observer = observers.match_name_type::<O>(&self.name).unwrap();
|
||||
let size = observer.usable_count();
|
||||
let mut hit_target: bool = false;
|
||||
//check if we've hit any targets.
|
||||
for i in 0..size {
|
||||
if observer.map()[i] > 0 {
|
||||
self.target_idx.push(i);
|
||||
hit_target = true;
|
||||
}
|
||||
}
|
||||
if hit_target {
|
||||
Ok(true)
|
||||
} else {
|
||||
Ok(false)
|
||||
}
|
||||
}
|
||||
|
||||
fn append_metadata(&mut self, testcase: &mut Testcase<I>) -> Result<(), Error> {
|
||||
if !self.target_idx.is_empty() {
|
||||
let meta = MapIndexesMetadata::new(core::mem::take(self.target_idx.as_mut()));
|
||||
testcase.add_metadata(meta);
|
||||
};
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn discard_metadata(&mut self, _input: &I) -> Result<(), Error> {
|
||||
self.target_idx.clear();
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl<O> Named for ReachabilityFeedback<O>
|
||||
where
|
||||
O: MapObserver<usize>,
|
||||
{
|
||||
#[inline]
|
||||
fn name(&self) -> &str {
|
||||
self.name.as_str()
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user