Compare commits
6 Commits
fret
...
develop_st
Author | SHA1 | Date | |
---|---|---|---|
f067a04e7c | |||
ae58815e83 | |||
ca33ef2823 | |||
5ef52b7a03 | |||
efef29f877 | |||
83b03ceeea |
2
fuzzers/qemu_fret/.gitignore
vendored
Normal file
2
fuzzers/qemu_fret/.gitignore
vendored
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
libpng-*
|
||||||
|
libpng_harness
|
19
fuzzers/qemu_fret/Cargo.toml
Normal file
19
fuzzers/qemu_fret/Cargo.toml
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
[package]
|
||||||
|
name = "qemu_launcher"
|
||||||
|
version = "0.8.2"
|
||||||
|
authors = ["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
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
libafl = { path = "../../libafl/" }
|
||||||
|
libafl_qemu = { path = "../../libafl_qemu/", features = ["x86_64", "usermode"] }
|
98
fuzzers/qemu_fret/Makefile.toml
Normal file
98
fuzzers/qemu_fret/Makefile.toml
Normal file
@ -0,0 +1,98 @@
|
|||||||
|
# Variables
|
||||||
|
[env]
|
||||||
|
FUZZER_NAME='libpng_harness'
|
||||||
|
PROJECT_DIR = { script = ["pwd"] }
|
||||||
|
|
||||||
|
[tasks.unsupported]
|
||||||
|
script_runner="@shell"
|
||||||
|
script='''
|
||||||
|
echo "Qemu fuzzer not supported on windows"
|
||||||
|
'''
|
||||||
|
|
||||||
|
# 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://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
|
||||||
|
'''
|
||||||
|
|
||||||
|
# fuzzer
|
||||||
|
[tasks.fuzzer]
|
||||||
|
linux_alias = "fuzzer_unix"
|
||||||
|
mac_alias = "fuzzer_unix"
|
||||||
|
windows_alias = "unsupported"
|
||||||
|
|
||||||
|
[tasks.fuzzer_unix]
|
||||||
|
command = "cargo"
|
||||||
|
args = ["build", "--release"]
|
||||||
|
|
||||||
|
# Harness
|
||||||
|
[tasks.harness]
|
||||||
|
linux_alias = "harness_unix"
|
||||||
|
mac_alias = "harness_unix"
|
||||||
|
windows_alias = "unsupported"
|
||||||
|
|
||||||
|
[tasks.harness_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
|
||||||
|
# Build the libpng harness
|
||||||
|
c++ \
|
||||||
|
./harness.cc \
|
||||||
|
./libpng-1.6.37/.libs/libpng16.a \
|
||||||
|
-I./libpng-1.6.37/ \
|
||||||
|
-o ${FUZZER_NAME} \
|
||||||
|
-lm -lz
|
||||||
|
'''
|
||||||
|
dependencies = [ "libpng" ]
|
||||||
|
|
||||||
|
# Run the fuzzer
|
||||||
|
[tasks.run]
|
||||||
|
linux_alias = "run_unix"
|
||||||
|
mac_alias = "run_unix"
|
||||||
|
windows_alias = "unsupported"
|
||||||
|
|
||||||
|
[tasks.run_unix]
|
||||||
|
command = "cargo"
|
||||||
|
args = ["run", "--release", "./${FUZZER_NAME}"]
|
||||||
|
dependencies = [ "harness", "fuzzer" ]
|
||||||
|
|
||||||
|
# Run the fuzzer
|
||||||
|
[tasks.test]
|
||||||
|
linux_alias = "test_unix"
|
||||||
|
mac_alias = "test_unix"
|
||||||
|
windows_alias = "unsupported"
|
||||||
|
|
||||||
|
# Short test
|
||||||
|
[tasks.test_unix]
|
||||||
|
script_runner = "@shell"
|
||||||
|
script='''
|
||||||
|
rm -rf libafl_unix_shmem_server || true
|
||||||
|
timeout 11s cargo run --release ./${FUZZER_NAME} 2>/dev/null &
|
||||||
|
'''
|
||||||
|
dependencies = [ "harness", "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
fuzzers/qemu_fret/README.md
Normal file
47
fuzzers/qemu_fret/README.md
Normal file
@ -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://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
|
||||||
|
```
|
||||||
|
|
||||||
|
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.
|
BIN
fuzzers/qemu_fret/corpus/not_kitty.png
Normal file
BIN
fuzzers/qemu_fret/corpus/not_kitty.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 218 B |
BIN
fuzzers/qemu_fret/corpus/not_kitty_alpha.png
Normal file
BIN
fuzzers/qemu_fret/corpus/not_kitty_alpha.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 376 B |
BIN
fuzzers/qemu_fret/corpus/not_kitty_gamma.png
Normal file
BIN
fuzzers/qemu_fret/corpus/not_kitty_gamma.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 228 B |
BIN
fuzzers/qemu_fret/corpus/not_kitty_icc.png
Normal file
BIN
fuzzers/qemu_fret/corpus/not_kitty_icc.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 427 B |
193
fuzzers/qemu_fret/harness.cc
Normal file
193
fuzzers/qemu_fret/harness.cc
Normal file
@ -0,0 +1,193 @@
|
|||||||
|
// 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
int main() {
|
||||||
|
uint8_t buf[10] = {0};
|
||||||
|
LLVMFuzzerTestOneInput(buf, 10);
|
||||||
|
}
|
236
fuzzers/qemu_fret/src/fuzzer.rs
Normal file
236
fuzzers/qemu_fret/src/fuzzer.rs
Normal file
@ -0,0 +1,236 @@
|
|||||||
|
//! A libfuzzer-like fuzzer using qemu for binary-only coverage
|
||||||
|
//!
|
||||||
|
use core::time::Duration;
|
||||||
|
use std::{env, path::PathBuf, process};
|
||||||
|
|
||||||
|
use libafl::{
|
||||||
|
bolts::{
|
||||||
|
core_affinity::Cores,
|
||||||
|
current_nanos,
|
||||||
|
launcher::Launcher,
|
||||||
|
rands::StdRand,
|
||||||
|
shmem::{ShMemProvider, StdShMemProvider},
|
||||||
|
tuples::tuple_list,
|
||||||
|
AsSlice,
|
||||||
|
},
|
||||||
|
corpus::{Corpus, InMemoryCorpus, OnDiskCorpus},
|
||||||
|
events::EventConfig,
|
||||||
|
executors::{ExitKind, TimeoutExecutor},
|
||||||
|
feedback_or, feedback_or_fast,
|
||||||
|
feedbacks::{CrashFeedback, MaxMapFeedback, TimeFeedback, TimeoutFeedback},
|
||||||
|
fuzzer::{Fuzzer, StdFuzzer},
|
||||||
|
inputs::{BytesInput, HasTargetBytes},
|
||||||
|
monitors::MultiMonitor,
|
||||||
|
mutators::scheduled::{havoc_mutations, StdScheduledMutator},
|
||||||
|
observers::{HitcountsMapObserver, TimeObserver, VariableMapObserver},
|
||||||
|
schedulers::{IndexesLenTimeMinimizerScheduler, QueueScheduler},
|
||||||
|
stages::StdMutationalStage,
|
||||||
|
state::{HasCorpus, StdState},
|
||||||
|
Error,
|
||||||
|
};
|
||||||
|
use libafl_qemu::{
|
||||||
|
//asan::QemuAsanHelper,
|
||||||
|
cmplog,
|
||||||
|
cmplog::{CmpLogObserver, QemuCmpLogHelper},
|
||||||
|
edges,
|
||||||
|
edges::QemuEdgeCoverageHelper,
|
||||||
|
elf::EasyElf,
|
||||||
|
emu::Emulator,
|
||||||
|
filter_qemu_args,
|
||||||
|
//snapshot::QemuSnapshotHelper,
|
||||||
|
MmapPerms,
|
||||||
|
QemuExecutor,
|
||||||
|
QemuHooks,
|
||||||
|
Regs,
|
||||||
|
};
|
||||||
|
|
||||||
|
// pub const MAX_INPUT_SIZE: usize = 1048576; // 1MB
|
||||||
|
|
||||||
|
pub fn fuzz() {
|
||||||
|
let MAX_INPUT_SIZE: usize = match env::var("FUZZ_SIZE") {
|
||||||
|
Ok(s) => str::parse::<usize>(&s).expect("FUZZ_SIZE was not a number"),
|
||||||
|
_ => 1048576,
|
||||||
|
}; // 1MB
|
||||||
|
// Hardcoded parameters
|
||||||
|
let timeout = Duration::from_secs(1);
|
||||||
|
let broker_port = 1337;
|
||||||
|
let cores = Cores::from_cmdline("0-11").unwrap();
|
||||||
|
let corpus_dirs = [PathBuf::from("./corpus")];
|
||||||
|
let mut objective_dir = PathBuf::from("./crashes");
|
||||||
|
|
||||||
|
// Initialize QEMU
|
||||||
|
env::remove_var("LD_LIBRARY_PATH");
|
||||||
|
let args: Vec<String> = env::args().collect();
|
||||||
|
let env: Vec<(String, String)> = env::vars().collect();
|
||||||
|
let emu = Emulator::new(&args, &env);
|
||||||
|
|
||||||
|
let mut elf_buffer = Vec::new();
|
||||||
|
let elf = EasyElf::from_file(emu.binary_path(), &mut elf_buffer).unwrap();
|
||||||
|
|
||||||
|
let test_one_input_ptr = match env::var("MAIN_FUNC") {
|
||||||
|
Ok(s) => elf
|
||||||
|
.resolve_symbol(&s, emu.load_addr())
|
||||||
|
.expect(&format!("Symbol {} not found",s)),
|
||||||
|
Err(e) => elf
|
||||||
|
.resolve_symbol("LLVMFuzzerTestOneInput", emu.load_addr())
|
||||||
|
.expect("Symbol LLVMFuzzerTestOneInput not found"),
|
||||||
|
};
|
||||||
|
println!("Main funtion @ {:#x}", test_one_input_ptr);
|
||||||
|
|
||||||
|
emu.set_breakpoint(test_one_input_ptr); // LLVMFuzzerTestOneInput
|
||||||
|
unsafe { emu.run() };
|
||||||
|
|
||||||
|
println!("Break at {:#x}", emu.read_reg::<_, u64>(Regs::Rip).unwrap());
|
||||||
|
|
||||||
|
// Get the return address
|
||||||
|
let stack_ptr: u64 = emu.read_reg(Regs::Rsp).unwrap();
|
||||||
|
let mut ret_addr = [0; 8];
|
||||||
|
unsafe { emu.read_mem(stack_ptr, &mut ret_addr) };
|
||||||
|
let ret_addr = u64::from_le_bytes(ret_addr);
|
||||||
|
|
||||||
|
println!("Stack pointer = {:#x}", stack_ptr);
|
||||||
|
println!("Return address = {:#x}", ret_addr);
|
||||||
|
|
||||||
|
emu.remove_breakpoint(test_one_input_ptr); // LLVMFuzzerTestOneInput
|
||||||
|
emu.set_breakpoint(ret_addr); // LLVMFuzzerTestOneInput ret addr
|
||||||
|
let input_addr = match env::var("DIRECT_WRITE") {
|
||||||
|
Ok(_) => elf
|
||||||
|
.resolve_symbol(&env::var("FUZZ_INPUT").expect("FUZZ_INPUT not set"), emu.load_addr())
|
||||||
|
.expect("FUZZ_INPUT symbol not found"),
|
||||||
|
_ => emu
|
||||||
|
.map_private(0, MAX_INPUT_SIZE, MmapPerms::ReadWrite)
|
||||||
|
.unwrap(),
|
||||||
|
};
|
||||||
|
|
||||||
|
println!("Placing input at {:#x}", input_addr);
|
||||||
|
|
||||||
|
// The wrapped harness function, calling out to the LLVM-style harness
|
||||||
|
let mut harness = |input: &BytesInput| {
|
||||||
|
let target = input.target_bytes();
|
||||||
|
let mut buf = target.as_slice();
|
||||||
|
let mut len = buf.len();
|
||||||
|
if len > MAX_INPUT_SIZE {
|
||||||
|
buf = &buf[0..MAX_INPUT_SIZE];
|
||||||
|
len = MAX_INPUT_SIZE;
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe {
|
||||||
|
emu.write_mem(input_addr, buf);
|
||||||
|
|
||||||
|
if env::var("DIRECT_WRITE").is_err() {
|
||||||
|
emu.write_reg(Regs::Rdi, input_addr).unwrap();
|
||||||
|
emu.write_reg(Regs::Rsi, len).unwrap();
|
||||||
|
}
|
||||||
|
emu.write_reg(Regs::Rip, test_one_input_ptr).unwrap();
|
||||||
|
emu.write_reg(Regs::Rsp, stack_ptr).unwrap();
|
||||||
|
|
||||||
|
emu.run();
|
||||||
|
}
|
||||||
|
|
||||||
|
ExitKind::Ok
|
||||||
|
};
|
||||||
|
|
||||||
|
let mut run_client = |state: Option<_>, mut mgr, _core_id| {
|
||||||
|
// Create an observation channel using the coverage map
|
||||||
|
let edges = unsafe { &mut edges::EDGES_MAP };
|
||||||
|
let edges_counter = unsafe { &mut edges::MAX_EDGES_NUM };
|
||||||
|
let edges_observer =
|
||||||
|
HitcountsMapObserver::new(VariableMapObserver::new("edges", edges, edges_counter));
|
||||||
|
|
||||||
|
// Create an observation channel to keep track of the execution time
|
||||||
|
let time_observer = TimeObserver::new("time");
|
||||||
|
|
||||||
|
// Feedback to rate the interestingness of an input
|
||||||
|
// This one is composed by two Feedbacks in OR
|
||||||
|
let mut feedback = feedback_or!(
|
||||||
|
// New maximization map feedback linked to the edges observer and the feedback state
|
||||||
|
MaxMapFeedback::new_tracking(&edges_observer, true, false),
|
||||||
|
// Time feedback, this one does not need a feedback state
|
||||||
|
TimeFeedback::new_with_observer(&time_observer)
|
||||||
|
);
|
||||||
|
|
||||||
|
// A feedback to choose if an input is a solution or not
|
||||||
|
let mut objective = feedback_or_fast!(CrashFeedback::new(), TimeoutFeedback::new());
|
||||||
|
|
||||||
|
// If not restarting, create a State from scratch
|
||||||
|
let mut state = state.unwrap_or_else(|| {
|
||||||
|
StdState::new(
|
||||||
|
// RNG
|
||||||
|
StdRand::with_seed(current_nanos()),
|
||||||
|
// Corpus that will be evolved, we keep it in memory for performance
|
||||||
|
InMemoryCorpus::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.clone()).unwrap(),
|
||||||
|
// States of the feedbacks.
|
||||||
|
// The feedbacks can report the data that should persist in the State.
|
||||||
|
&mut feedback,
|
||||||
|
// Same for objective feedbacks
|
||||||
|
&mut objective,
|
||||||
|
)
|
||||||
|
.unwrap()
|
||||||
|
});
|
||||||
|
|
||||||
|
// A minimization+queue policy to get testcasess from the corpus
|
||||||
|
let scheduler = IndexesLenTimeMinimizerScheduler::new(QueueScheduler::new());
|
||||||
|
|
||||||
|
// A fuzzer with feedbacks and a corpus scheduler
|
||||||
|
let mut fuzzer = StdFuzzer::new(scheduler, feedback, objective);
|
||||||
|
|
||||||
|
let mut hooks = QemuHooks::new(&emu, tuple_list!(QemuEdgeCoverageHelper::default()));
|
||||||
|
|
||||||
|
// Create a QEMU in-process executor
|
||||||
|
let executor = QemuExecutor::new(
|
||||||
|
&mut hooks,
|
||||||
|
&mut harness,
|
||||||
|
tuple_list!(edges_observer, time_observer),
|
||||||
|
&mut fuzzer,
|
||||||
|
&mut state,
|
||||||
|
&mut mgr,
|
||||||
|
)
|
||||||
|
.expect("Failed to create QemuExecutor");
|
||||||
|
|
||||||
|
// Wrap the executor to keep track of the timeout
|
||||||
|
let mut executor = TimeoutExecutor::new(executor, timeout);
|
||||||
|
|
||||||
|
if state.corpus().count() < 1 {
|
||||||
|
state
|
||||||
|
.load_initial_inputs(&mut fuzzer, &mut executor, &mut mgr, &corpus_dirs)
|
||||||
|
.unwrap_or_else(|_| {
|
||||||
|
println!("Failed to load initial corpus at {:?}", &corpus_dirs);
|
||||||
|
process::exit(0);
|
||||||
|
});
|
||||||
|
println!("We imported {} inputs from disk.", state.corpus().count());
|
||||||
|
}
|
||||||
|
|
||||||
|
// Setup an havoc mutator with a mutational stage
|
||||||
|
let mutator = StdScheduledMutator::new(havoc_mutations());
|
||||||
|
let mut stages = tuple_list!(StdMutationalStage::new(mutator));
|
||||||
|
|
||||||
|
fuzzer.fuzz_loop(&mut stages, &mut executor, &mut state, &mut mgr)?;
|
||||||
|
Ok(())
|
||||||
|
};
|
||||||
|
|
||||||
|
// The shared memory allocator
|
||||||
|
let shmem_provider = StdShMemProvider::new().expect("Failed to init shared memory");
|
||||||
|
|
||||||
|
// The stats reporter for the broker
|
||||||
|
let monitor = MultiMonitor::new(|s| println!("{}", s));
|
||||||
|
|
||||||
|
// Build and run a Launcher
|
||||||
|
match Launcher::builder()
|
||||||
|
.shmem_provider(shmem_provider)
|
||||||
|
.broker_port(broker_port)
|
||||||
|
.configuration(EventConfig::from_build_id())
|
||||||
|
.monitor(monitor)
|
||||||
|
.run_client(&mut run_client)
|
||||||
|
.cores(&cores)
|
||||||
|
.stdout_file(Some("/dev/null"))
|
||||||
|
.build()
|
||||||
|
.launch()
|
||||||
|
{
|
||||||
|
Ok(()) => (),
|
||||||
|
Err(Error::ShuttingDown) => println!("Fuzzing stopped by user. Good bye."),
|
||||||
|
Err(err) => panic!("Failed to run launcher: {:?}", err),
|
||||||
|
}
|
||||||
|
}
|
13
fuzzers/qemu_fret/src/main.rs
Normal file
13
fuzzers/qemu_fret/src/main.rs
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
//! A libfuzzer-like fuzzer using qemu for binary-only coverage
|
||||||
|
#[cfg(target_os = "linux")]
|
||||||
|
mod fuzzer;
|
||||||
|
|
||||||
|
#[cfg(target_os = "linux")]
|
||||||
|
pub fn main() {
|
||||||
|
fuzzer::fuzz();
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(not(target_os = "linux"))]
|
||||||
|
pub fn main() {
|
||||||
|
panic!("qemu-user and libafl_qemu is only supported on linux!");
|
||||||
|
}
|
@ -26,6 +26,8 @@ be = []
|
|||||||
usermode = []
|
usermode = []
|
||||||
systemmode = []
|
systemmode = []
|
||||||
|
|
||||||
|
slirp = [ "systemmode" ] # build qemu with host libslirp (for user networking)
|
||||||
|
|
||||||
clippy = [] # special feature for clippy, don't use in normal projects§
|
clippy = [] # special feature for clippy, don't use in normal projects§
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
|
@ -44,8 +44,6 @@ pub fn build() {
|
|||||||
println!("cargo:rustc-cfg=emulation_mode=\"{emulation_mode}\"");
|
println!("cargo:rustc-cfg=emulation_mode=\"{emulation_mode}\"");
|
||||||
|
|
||||||
println!("cargo:rerun-if-changed=build.rs");
|
println!("cargo:rerun-if-changed=build.rs");
|
||||||
println!("cargo:rerun-if-changed=src/asan-giovese.c");
|
|
||||||
println!("cargo:rerun-if-changed=src/asan-giovese.h");
|
|
||||||
println!("cargo:rerun-if-env-changed=CROSS_CC");
|
println!("cargo:rerun-if-env-changed=CROSS_CC");
|
||||||
|
|
||||||
// Make sure we have at most one architecutre feature set
|
// Make sure we have at most one architecutre feature set
|
||||||
@ -215,7 +213,7 @@ pub fn build() {
|
|||||||
//.arg("--as-static-lib")
|
//.arg("--as-static-lib")
|
||||||
.arg("--as-shared-lib")
|
.arg("--as-shared-lib")
|
||||||
.arg(&format!("--target-list={cpu_target}-{target_suffix}"))
|
.arg(&format!("--target-list={cpu_target}-{target_suffix}"))
|
||||||
.arg("--enable-slirp=internal")
|
.arg(if cfg!(feature = "slirp") {"--enable-slirp"} else {"--disable-slirp"})
|
||||||
.arg("--enable-fdt=internal")
|
.arg("--enable-fdt=internal")
|
||||||
.arg("--audio-drv-list=")
|
.arg("--audio-drv-list=")
|
||||||
.arg("--disable-alsa")
|
.arg("--disable-alsa")
|
||||||
@ -422,7 +420,6 @@ pub fn build() {
|
|||||||
build_dir.display()
|
build_dir.display()
|
||||||
))
|
))
|
||||||
.arg(format!("{}/libfdt.a", build_dir.display()))
|
.arg(format!("{}/libfdt.a", build_dir.display()))
|
||||||
.arg(format!("{}/libslirp.a", build_dir.display()))
|
|
||||||
.arg(format!("{}/libmigration.fa", build_dir.display()))
|
.arg(format!("{}/libmigration.fa", build_dir.display()))
|
||||||
.arg(format!("{}/libhwcore.fa", build_dir.display()))
|
.arg(format!("{}/libhwcore.fa", build_dir.display()))
|
||||||
.arg(format!("{}/libqom.fa", build_dir.display()))
|
.arg(format!("{}/libqom.fa", build_dir.display()))
|
||||||
@ -457,6 +454,8 @@ pub fn build() {
|
|||||||
println!("cargo:rustc-link-lib=glib-2.0");
|
println!("cargo:rustc-link-lib=glib-2.0");
|
||||||
println!("cargo:rustc-link-lib=stdc++");
|
println!("cargo:rustc-link-lib=stdc++");
|
||||||
println!("cargo:rustc-link-lib=z");
|
println!("cargo:rustc-link-lib=z");
|
||||||
|
#[cfg(all(feature = "slirp", feature = "systemmode"))]
|
||||||
|
println!("cargo:rustc-link-lib=slirp");
|
||||||
|
|
||||||
if emulation_mode == "systemmode" {
|
if emulation_mode == "systemmode" {
|
||||||
println!("cargo:rustc-link-lib=pixman-1");
|
println!("cargo:rustc-link-lib=pixman-1");
|
||||||
|
@ -7,7 +7,7 @@ use core::{
|
|||||||
};
|
};
|
||||||
#[cfg(emulation_mode = "usermode")]
|
#[cfg(emulation_mode = "usermode")]
|
||||||
use core::{mem::MaybeUninit, ptr::copy_nonoverlapping};
|
use core::{mem::MaybeUninit, ptr::copy_nonoverlapping};
|
||||||
use std::{slice::from_raw_parts, str::from_utf8_unchecked};
|
use std::{slice::from_raw_parts, str::from_utf8_unchecked,ffi::CString};
|
||||||
|
|
||||||
#[cfg(emulation_mode = "usermode")]
|
#[cfg(emulation_mode = "usermode")]
|
||||||
use libc::c_int;
|
use libc::c_int;
|
||||||
@ -226,6 +226,7 @@ extern "C" {
|
|||||||
extern "C" {
|
extern "C" {
|
||||||
fn qemu_init(argc: i32, argv: *const *const u8, envp: *const *const u8);
|
fn qemu_init(argc: i32, argv: *const *const u8, envp: *const *const u8);
|
||||||
|
|
||||||
|
fn vm_start();
|
||||||
fn qemu_main_loop();
|
fn qemu_main_loop();
|
||||||
fn qemu_cleanup();
|
fn qemu_cleanup();
|
||||||
|
|
||||||
@ -244,11 +245,9 @@ extern "C" {
|
|||||||
|
|
||||||
static mut libafl_start_vcpu: extern "C" fn(cpu: CPUStatePtr);
|
static mut libafl_start_vcpu: extern "C" fn(cpu: CPUStatePtr);
|
||||||
|
|
||||||
/*
|
fn libafl_save_qemu_snapshot(name: *const u8, sync: bool);
|
||||||
fn libafl_save_qemu_snapshot(name: *const u8);
|
|
||||||
#[allow(unused)]
|
#[allow(unused)]
|
||||||
fn libafl_load_qemu_snapshot(name: *const u8);
|
fn libafl_load_qemu_snapshot(name: *const u8, sync: bool);
|
||||||
*/
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(emulation_mode = "systemmode")]
|
#[cfg(emulation_mode = "systemmode")]
|
||||||
@ -723,7 +722,10 @@ impl Emulator {
|
|||||||
#[cfg(emulation_mode = "usermode")]
|
#[cfg(emulation_mode = "usermode")]
|
||||||
libafl_qemu_run();
|
libafl_qemu_run();
|
||||||
#[cfg(emulation_mode = "systemmode")]
|
#[cfg(emulation_mode = "systemmode")]
|
||||||
qemu_main_loop();
|
{
|
||||||
|
vm_start();
|
||||||
|
qemu_main_loop();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(emulation_mode = "usermode")]
|
#[cfg(emulation_mode = "usermode")]
|
||||||
@ -910,17 +912,17 @@ impl Emulator {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/*#[cfg(emulation_mode = "systemmode")]
|
#[cfg(emulation_mode = "systemmode")]
|
||||||
pub fn save_snapshot(&self, name: &str) {
|
pub fn save_snapshot(&self, name: &str, sync : bool) {
|
||||||
let s = CString::new(name).expect("Invalid snapshot name");
|
let s = CString::new(name).expect("Invalid snapshot name");
|
||||||
unsafe { libafl_save_qemu_snapshot(s.as_ptr() as *const _) };
|
unsafe { libafl_save_qemu_snapshot(s.as_ptr() as *const _, sync) };
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(emulation_mode = "systemmode")]
|
#[cfg(emulation_mode = "systemmode")]
|
||||||
pub fn load_snapshot(&self, name: &str) {
|
pub fn load_snapshot(&self, name: &str, sync : bool) {
|
||||||
let s = CString::new(name).expect("Invalid snapshot name");
|
let s = CString::new(name).expect("Invalid snapshot name");
|
||||||
unsafe { libafl_load_qemu_snapshot(s.as_ptr() as *const _) };
|
unsafe { libafl_load_qemu_snapshot(s.as_ptr() as *const _, sync) };
|
||||||
}*/
|
}
|
||||||
|
|
||||||
#[cfg(emulation_mode = "usermode")]
|
#[cfg(emulation_mode = "usermode")]
|
||||||
pub fn set_pre_syscall_hook(
|
pub fn set_pre_syscall_hook(
|
||||||
|
Loading…
x
Reference in New Issue
Block a user