Extend qemu_launcher to support multiple architectures (#1328)

* Change qemu_launcher fuzzer to support multiple architectures and remove qemu_arm_launcher

* Review changes

* Changes to milliseconds

---------

Co-authored-by: Your Name <you@example.com>
This commit is contained in:
WorksButNotTested 2023-06-30 01:18:00 +01:00 committed by GitHub
parent 871dfa0a01
commit c6062889d5
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
17 changed files with 609 additions and 799 deletions

View File

@ -1,7 +0,0 @@
libpng-*
libpng_harness
libpng_harness_crashing
zlib-*
crashes
target
drcov.log

View File

@ -1,20 +0,0 @@
[package]
name = "qemu_arm_launcher"
version = "0.10.1"
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 = ["arm", "usermode"] }
rangemap = "1.0.3"

View File

@ -1,173 +0,0 @@
# Variables
[env]
FUZZER_NAME='libpng_harness'
FUZZER_NAME_CRASHING='libpng_harness_crashing'
PROJECT_DIR = { script = ["pwd"] }
CROSS_CC = "arm-linux-gnueabi-gcc"
[tasks.unsupported]
script_runner="@shell"
script='''
echo "Qemu fuzzer not supported on windows/mac"
'''
#zlib
[tasks.zlib]
linux_alias = "zlib_unix"
mac_alias = "unsupported"
windows_alias = "unsupported"
[tasks.zlib_unix_wget]
condition = { files_not_exist = [ "./zlib-1.2.13" ] }
script_runner="@shell"
# NOTE: There's no specific reason we're using an old version of zlib,
# but newer versions get moved to fossils/ after a while.
script='''
wget https://zlib.net/fossils/zlib-1.2.13.tar.gz
tar -xvf zlib-1.2.13.tar.gz
'''
[tasks.zlib_unix]
condition = { files_not_exist = [ "./zlib-1.2.13/zlib/lib/libz.a" ] }
script_runner="@shell"
script='''
cd zlib-1.2.13 && CC=$CROSS_CC ./configure --prefix=./zlib
make install
'''
dependencies = [ "zlib_unix_wget" ]
# libpng
[tasks.libpng]
linux_alias = "libpng_unix"
mac_alias = "unsupported"
windows_alias = "unsupported"
[tasks.libpng_unix_wget]
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
'''
[tasks.libpng_unix]
condition = { files_not_exist = [ "./libpng-1.6.37/.libs/libpng16.a" ] }
script_runner="@shell"
script='''
cd libpng-1.6.37 && CC=$CROSS_CC CFLAGS=-I../zlib-1.2.13/zlib/lib LDFLAGS=-L../zlib-1.2.13/zlib/lib ./configure --enable-shared=no --with-pic=yes --enable-hardware-optimizations=yes --host=arm
make
'''
dependencies = [ "zlib", "libpng_unix_wget" ]
# 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 = "unsupported"
windows_alias = "unsupported"
[tasks.harness_unix]
script_runner="@shell"
script='''
# Build the libpng harness
arm-linux-gnueabi-g++ \
./harness.cc \
./libpng-1.6.37/.libs/libpng16.a \
./zlib-1.2.13/zlib/lib/libz.a \
-I./libpng-1.6.37/ \
-I../zlib-1.2.13/zlib/lib \
-L../zlib-1.2.13/zlib/lib \
-o ${FUZZER_NAME} \
-lm \
-static
'''
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" ]
# Harness with an artifical crash
[tasks.harness_crashing]
linux_alias = "harness_unix_crashing"
mac_alias = "unsupported"
windows_alias = "unsupported"
[tasks.harness_unix_crashing]
script_runner="@shell"
script='''
# Build the libpng harness
arm-linux-gnueabi-g++ \
./harness.cc \
./libpng-1.6.37/.libs/libpng16.a \
./zlib-1.2.13/zlib/lib/libz.a \
-I./libpng-1.6.37/ \
-I../zlib-1.2.13/zlib/lib \
-L../zlib-1.2.13/zlib/lib \
-o ${FUZZER_NAME_CRASHING} \
-lm \
-DHAS_DUMMY_CRASH \
-static
'''
dependencies = [ "libpng" ]
# Run the fuzzer with an artificial crash
[tasks.run_crashing]
linux_alias = "run_unix_crashing"
mac_alias = "unsupported"
windows_alias = "unsupported"
[tasks.run_unix_crashing]
command = "cargo"
args = ["run", "--release", "./${FUZZER_NAME_CRASHING}"]
dependencies = [ "harness_crashing", "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 ./target/release/qemu_arm_launcher ./${FUZZER_NAME} 2>/dev/null >fuzz_stdout.log || true
echo "This test is skipped"
'''
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}
rm -f ./${FUZZER_NAME_CRASHING}
rm -rf zlib-*
rm -rf libpng-*
cargo clean
'''

View File

@ -1,24 +0,0 @@
# LibAFL with launcher for libpng with qemu arm32 in usermode
This folder contains an example fuzzer for libpng using the qemu emulator in arm32 usermode.
To show off crash detection, we added an optional undefined instruction to the harness.
Everything 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.
## Prerequisites
```bash
sudo apt install gcc-arm-linux-gnueabi g++-arm-linux-gnueabi
```
## Run
```bash
cargo make run
```
## Run with artifical crash
```bash
cargo make run_crashing
```

Binary file not shown.

Before

Width:  |  Height:  |  Size: 218 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 376 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 228 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 427 B

View File

@ -1,193 +0,0 @@
// 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
#if defined(__aarch64__) || defined(__arm__)
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);
}

View File

@ -1,238 +0,0 @@
//! A libfuzzer-like fuzzer using qemu for binary-only coverage
//!
use core::{ptr::addr_of_mut, 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::{
drcov::QemuDrCovHelper,
//asan::QemuAsanHelper,
edges::{edges_map_mut_slice, QemuEdgeCoverageHelper, MAX_EDGES_NUM},
elf::EasyElf,
emu::Emulator,
MmapPerms,
QemuExecutor,
QemuHooks,
QemuInstrumentationFilter,
Regs,
};
use rangemap::RangeMap;
pub fn fuzz() {
// 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 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).unwrap();
let mut elf_buffer = Vec::new();
let elf = EasyElf::from_file(emu.binary_path(), &mut elf_buffer).unwrap();
let test_one_input_ptr = elf
.resolve_symbol("LLVMFuzzerTestOneInput", emu.load_addr())
.expect("Symbol LLVMFuzzerTestOneInput not found");
println!("LLVMFuzzerTestOneInput @ {test_one_input_ptr:#x}");
emu.set_breakpoint(test_one_input_ptr); // LLVMFuzzerTestOneInput
unsafe { emu.run() };
println!("Break at {:#x}", emu.read_reg::<_, u64>(Regs::Pc).unwrap());
// Get the return address
let stack_ptr: u64 = emu.read_reg(Regs::Sp).unwrap();
let ret_addr: u32 = emu.read_reg(Regs::Lr).unwrap();
println!("Stack pointer = {stack_ptr:#x}");
println!("Return address = {ret_addr:#x}");
emu.remove_breakpoint(test_one_input_ptr); // LLVMFuzzerTestOneInput
emu.set_breakpoint(ret_addr); // LLVMFuzzerTestOneInput ret addr
let input_addr = emu.map_private(0, 4096, MmapPerms::ReadWrite).unwrap();
println!("Placing input at {input_addr:#x}");
// 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 > 4096 {
buf = &buf[0..4096];
len = 4096;
}
unsafe {
emu.write_mem(input_addr, buf);
emu.write_reg(Regs::R0, input_addr).unwrap();
emu.write_reg(Regs::R1, len).unwrap();
emu.write_reg(Regs::Pc, test_one_input_ptr).unwrap();
emu.write_reg(Regs::Lr, ret_addr).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_observer = unsafe {
HitcountsMapObserver::new(VariableMapObserver::from_mut_slice(
"edges",
edges_map_mut_slice(),
addr_of_mut!(MAX_EDGES_NUM),
))
};
// 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::tracking(&edges_observer, true, false),
// Time feedback, this one does not need a feedback state
TimeFeedback::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 rangemap = RangeMap::<usize, (u16, String)>::new();
let mappings = emu.mappings();
let mut idx = 0;
for map in mappings {
if map.path().unwrap() != "" {
rangemap.insert(
(map.start() as usize)..(map.end() as usize),
(idx, map.path().unwrap().to_string()),
);
idx += 1;
}
}
let mut hooks = QemuHooks::new(
&emu,
tuple_list!(
QemuEdgeCoverageHelper::default(),
QemuDrCovHelper::new(
QemuInstrumentationFilter::None,
rangemap,
PathBuf::from("drcov.log"),
false,
)
),
);
// 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.must_load_initial_inputs() {
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:?}"),
}
}

View File

@ -1,13 +0,0 @@
//! 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!");
}

View File

@ -7,6 +7,14 @@ edition = "2021"
[features]
default = ["std"]
std = []
be = []
64bit = []
arm = ["libafl_qemu/arm"]
x86_64 = ["libafl_qemu/x86_64", "64bit"]
i386 = ["libafl_qemu/i386"]
aarch64 = ["libafl_qemu/aarch64", "64bit"]
mips = ["libafl_qemu/mips"]
ppc = ["libafl_qemu/ppc", "be"]
[profile.release]
lto = true
@ -14,6 +22,19 @@ codegen-units = 1
opt-level = 3
debug = true
[build-dependencies]
vergen = { version = "8.2.1", features = ["build", "cargo", "git", "gitcl", "rustc", "si"] }
[dependencies]
clap = { version = "4.3.0", features = ["derive", "string"]}
libafl = { path = "../../libafl/" }
libafl_qemu = { path = "../../libafl_qemu/", features = ["x86_64", "usermode"] }
libafl_qemu = { path = "../../libafl_qemu/", features = ["usermode"] }
rangemap = { version = "1.0.3" }

View File

@ -1,86 +1,250 @@
# Variables
[env]
FUZZER_NAME='libpng_harness'
PROJECT_DIR = { script = ["pwd"] }
CROSS_CC = "x86_64-linux-gnu-gcc"
CROSS_CXX = "x86_64-linux-gnu-g++"
CROSS_CFLAGS = ""
TARGET_DIR = "${CARGO_MAKE_CRATE_TARGET_DIRECTORY}/x86_64"
LIBPNG_ARCH = "x86_64"
LIBPNG_OPTIMIZATIONS = "yes"
FEATURE = "x86_64"
#LIBAFL_DEBUG_OUTPUT = "1"
#CUSTOM_QEMU_DIR= "~/qemu-libafl-bridge"
[env.arm]
CROSS_CC = "arm-linux-gnueabi-gcc"
CROSS_CXX = "arm-linux-gnueabi-g++"
CROSS_CFLAGS = ""
TARGET_DIR = "${CARGO_MAKE_CRATE_TARGET_DIRECTORY}/arm"
LIBPNG_ARCH = "arm"
LIBPNG_OPTIMIZATIONS = "yes"
FEATURE = "arm"
[env.aarch64]
CROSS_CC = "aarch64-linux-gnu-gcc"
CROSS_CXX = "aarch64-linux-gnu-g++"
CROSS_CFLAGS = ""
TARGET_DIR = "${CARGO_MAKE_CRATE_TARGET_DIRECTORY}/aarch64"
LIBPNG_ARCH = "aarch64"
LIBPNG_OPTIMIZATIONS = "yes"
FEATURE = "aarch64"
[env.x86_64]
CROSS_CC = "x86_64-linux-gnu-gcc"
CROSS_CXX = "x86_64-linux-gnu-g++"
CROSS_CFLAGS = ""
TARGET_DIR = "${CARGO_MAKE_CRATE_TARGET_DIRECTORY}/x86_64"
LIBPNG_ARCH = "x86_64"
LIBPNG_OPTIMIZATIONS = "yes"
FEATURE = "x86_64"
[env.i386]
CROSS_CC = "x86_64-linux-gnu-gcc"
CROSS_CXX = "x86_64-linux-gnu-g++"
CROSS_CFLAGS = "-m32"
TARGET_DIR = "${CARGO_MAKE_CRATE_TARGET_DIRECTORY}/i386"
LIBPNG_ARCH = "i386"
LIBPNG_OPTIMIZATIONS = "yes"
FEATURE = "i386"
[env.mips]
CROSS_CC = "mipsel-linux-gnu-gcc"
CROSS_CXX = "mipsel-linux-gnu-g++"
CROSS_CFLAGS = ""
TARGET_DIR = "${CARGO_MAKE_CRATE_TARGET_DIRECTORY}/mips"
LIBPNG_ARCH = "mips"
LIBPNG_OPTIMIZATIONS = "yes"
FEATURE = "mips"
[env.ppc]
CROSS_CC = "powerpc-linux-gnu-gcc"
CROSS_CXX = "powerpc-linux-gnu-g++"
CROSS_CFLAGS = ""
TARGET_DIR = "${CARGO_MAKE_CRATE_TARGET_DIRECTORY}/ppc"
LIBPNG_ARCH = "ppc"
LIBPNG_OPTIMIZATIONS = "no"
FEATURE = "ppc"
[tasks.unsupported]
script_runner="@shell"
script='''
echo "Qemu fuzzer not supported on windows"
echo "Qemu fuzzer not supported on windows/mac"
'''
# 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" ] }
[tasks.target_dir]
condition = { files_not_exist = [ "${CARGO_MAKE_CRATE_TARGET_DIRECTORY}" ] }
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
mkdir ${CARGO_MAKE_CRATE_TARGET_DIRECTORY}
'''
# fuzzer
[tasks.fuzzer]
linux_alias = "fuzzer_unix"
mac_alias = "fuzzer_unix"
[tasks.deps_dir]
dependencies = ["target_dir"]
condition = { files_not_exist = [ "${CARGO_MAKE_CRATE_TARGET_DIRECTORY}/deps/" ] }
script_runner="@shell"
script='''
mkdir ${CARGO_MAKE_CRATE_TARGET_DIRECTORY}/deps/
'''
[tasks.arch_target_dir]
dependencies = ["target_dir"]
condition = { files_not_exist = [ "${TARGET_DIR}" ] }
script_runner="@shell"
script='''
mkdir ${TARGET_DIR}
'''
[tasks.zlib]
linux_alias = "zlib_unix"
mac_alias = "unsupported"
windows_alias = "unsupported"
[tasks.fuzzer_unix]
command = "cargo"
args = ["build", "--release"]
[tasks.zlib_unix_wget]
dependencies = ["deps_dir"]
condition = { files_not_exist = [ "${CARGO_MAKE_CRATE_TARGET_DIRECTORY}/deps/zlib-1.2.13" ] }
script_runner="@shell"
# NOTE: There's no specific reason we're using an old version of zlib,
# but newer versions get moved to fossils/ after a while.
script='''
wget \
-O "${CARGO_MAKE_CRATE_TARGET_DIRECTORY}/deps/zlib-1.2.13.tar.gz" \
https://zlib.net/fossils/zlib-1.2.13.tar.gz
tar \
zxvf ${CARGO_MAKE_CRATE_TARGET_DIRECTORY}/deps/zlib-1.2.13.tar.gz \
-C ${CARGO_MAKE_CRATE_TARGET_DIRECTORY}/deps/
'''
[tasks.zlib_unix]
dependencies = ["arch_target_dir", "zlib_unix_wget" ]
condition = { files_not_exist = [ "${TARGET_DIR}/build-zlib/libz.a" ] }
script_runner="@shell"
script='''
rm -rf ${TARGET_DIR}/build-zlib/
mkdir ${TARGET_DIR}/build-zlib/
cd ${TARGET_DIR}/build-zlib/ && \
CC=$CROSS_CC \
CFLAGS=${CROSS_CFLAGS} \
${CARGO_MAKE_CRATE_TARGET_DIRECTORY}/deps/zlib-1.2.13/configure \
--prefix=./zlib
make install
'''
[tasks.libpng]
linux_alias = "libpng_unix"
mac_alias = "unsupported"
windows_alias = "unsupported"
[tasks.libpng_unix_wget]
dependencies = ["deps_dir"]
condition = { files_not_exist = [ "${CARGO_MAKE_CRATE_TARGET_DIRECTORY}/deps/libpng-1.6.37" ] }
script_runner="@shell"
script='''
wget \
-O "${CARGO_MAKE_CRATE_TARGET_DIRECTORY}/deps/libpng-1.6.37.tar.xz" \
https://deac-fra.dl.sourceforge.net/project/libpng/libpng16/1.6.37/libpng-1.6.37.tar.xz
tar \
-xvf "${CARGO_MAKE_CRATE_TARGET_DIRECTORY}/deps/libpng-1.6.37.tar.xz" \
-C ${CARGO_MAKE_CRATE_TARGET_DIRECTORY}/deps/
'''
[tasks.libpng_unix]
dependencies = [ "arch_target_dir", "zlib", "libpng_unix_wget" ]
condition = { files_not_exist = [ "${TARGET_DIR}/build-png/.libs/libpng16.a" ] }
script_runner="@shell"
script='''
rm -rf ${TARGET_DIR}/build-png/
mkdir ${TARGET_DIR}/build-png/
cd ${TARGET_DIR}/build-png/ && \
CC=$CROSS_CC \
CFLAGS="${CROSS_CFLAGS} -I"${TARGET_DIR}/build-zlib/zlib/lib"" \
LDFLAGS=-L"${TARGET_DIR}/build-zlib/zlib/lib" \
${CARGO_MAKE_CRATE_TARGET_DIRECTORY}/deps/libpng-1.6.37/configure \
--enable-shared=no \
--with-pic=yes \
--enable-hardware-optimizations=${LIBPNG_OPTIMIZATIONS} \
--host=${LIBPNG_ARCH} \
make
'''
[tasks.build]
linux_alias = "build_unix"
mac_alias = "build_unix"
windows_alias = "unsupported"
[tasks.build_unix]
command = "cargo"
args = [
"build",
"--release",
"--features", "${FEATURE}",
"--target-dir", "${TARGET_DIR}"
]
[tasks.fuzzer]
dependencies = ["build"]
script_runner="@shell"
script='''
mv ${TARGET_DIR}/release/qemu_launcher ${TARGET_DIR}/release/qemu_launcher-${CARGO_MAKE_PROFILE}
'''
# Harness
[tasks.harness]
linux_alias = "harness_unix"
mac_alias = "harness_unix"
mac_alias = "unsupported"
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++ \
${CROSS_CXX} \
./harness.cc \
./libpng-1.6.37/.libs/libpng16.a \
-I./libpng-1.6.37/ \
-o ${FUZZER_NAME} \
-lm -lz
$CROSS_CFLAGS \
"${TARGET_DIR}/build-png/.libs/libpng16.a" \
"${TARGET_DIR}/build-zlib/libz.a" \
-I"${TARGET_DIR}/build-png" \
-I"${TARGET_DIR}/build-zlib/zlib/lib" \
-L"${TARGET_DIR}/build-zlib/zlib/lib" \
-o"${TARGET_DIR}/libpng-harness-${CARGO_MAKE_PROFILE}" \
-lm \
-static
'''
dependencies = [ "libpng" ]
# Run the fuzzer
[tasks.run]
linux_alias = "run_unix"
mac_alias = "run_unix"
mac_alias = "unsupported"
windows_alias = "unsupported"
[tasks.run_unix]
command = "cargo"
args = ["run", "--release", "./${FUZZER_NAME}"]
command = "${TARGET_DIR}/release/qemu_launcher-${CARGO_MAKE_PROFILE}"
args = [
"--coverage", "${TARGET_DIR}/drcov.log",
"--input", "./corpus",
"--output", "${TARGET_DIR}/output/",
"--",
"${TARGET_DIR}/libpng-harness-${CARGO_MAKE_PROFILE}",
]
dependencies = [ "harness", "fuzzer" ]
# Run the fuzzer
[tasks.test]
linux_alias = "test_unix"
mac_alias = "test_unix"
mac_alias = "unsupported"
windows_alias = "unsupported"
# Short test
[tasks.test_unix]
# Tidy up after we've run our tests so we don't hog all the disk space
dependencies = [ "clean" ]
script_runner = "@shell"
script='''
echo "This test is skipped"
'''
dependencies = [ "harness", "fuzzer" ]
# Clean up
[tasks.clean]
linux_alias = "clean_unix"
mac_alias = "clean_unix"
@ -91,7 +255,64 @@ windows_alias = "unsupported"
clear = true
script_runner="@shell"
script='''
rm -f ./${FUZZER_NAME}
make -C libpng-1.6.37 clean
rm -rf ${CARGO_MAKE_CRATE_TARGET_DIRECTORY}
cargo clean
'''
[tasks.arm]
command = "cargo"
args = [
"make",
"-p", "arm",
"run",
]
[tasks.aarch64]
command = "cargo"
args = [
"make",
"-p", "aarch64",
"run",
]
[tasks.x86_64]
command = "cargo"
args = [
"make",
"-p", "x86_64",
"run",
]
[tasks.i386]
command = "cargo"
args = [
"make",
"-p", "i386",
"run",
]
[tasks.mips]
command = "cargo"
args = [
"make",
"-p", "mips",
"run",
]
[tasks.ppc]
command = "cargo"
args = [
"make",
"-p", "ppc",
"run",
]
[tasks.all]
dependencies = [
"arm",
"aarch64",
"x86_64",
"i386",
"mips",
"ppc"
]

View File

@ -1,47 +1,38 @@
# Libfuzzer for libpng, with launcher
# qemu_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.
This folder contains an example fuzzer for libpng, using LLMP for fast multi-process fuzzing and crash detection. It has been tested on Linux.
This automatically spawns n child processes, and binds them to a free core.
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
The following architectures are supported:
* arm
* aarch64
* i386
* x86_64
* mips
* ppc
## Prerequisites
```bash
cargo build --release
sudo apt install \
gcc-arm-linux-gnueabi \
g++-arm-linux-gnueabi \
gcc-aarch64-linux-gnu \
g++-aarch64-linux-gnu \
gcc \
g++ \
gcc-mipsel-linux-gnu \
g++-mipsel-linux-gnu \
gcc-powerpc-linux-gnu \
g++-powerpc-linux-gnu
```
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.
Defaults to `x86_64` architecture
```bash
cargo make run
```
```bash
cargo make <arch>
```

View File

@ -0,0 +1,48 @@
use vergen::EmitBuilder;
#[macro_export]
macro_rules! assert_unique_feature {
() => {};
($first:tt $(,$rest:tt)*) => {
$(
#[cfg(not(feature = "clippy"))] // ignore multiple definition for clippy
#[cfg(all(feature = $first, feature = $rest))]
compile_error!(concat!("features \"", $first, "\" and \"", $rest, "\" cannot be used together"));
)*
assert_unique_feature!($($rest),*);
}
}
fn main() {
EmitBuilder::builder()
.all_build()
.all_cargo()
.all_git()
.all_rustc()
.all_sysinfo()
.emit()
.unwrap();
assert_unique_feature!("arm", "aarch64", "i386", "x86_64", "mips", "ppc");
let cpu_target = if cfg!(feature = "x86_64") {
"x86_64".to_string()
} else if cfg!(feature = "arm") {
"arm".to_string()
} else if cfg!(feature = "aarch64") {
"aarch64".to_string()
} else if cfg!(feature = "i386") {
"i386".to_string()
} else if cfg!(feature = "mips") {
"mips".to_string()
} else if cfg!(feature = "ppc") {
"ppc".to_string()
} else {
println!("cargo:warning=No architecture specified defaulting to x86_64...");
println!("cargo:rustc-cfg=feature=\"x86_64\"");
println!("cargo:rustc-cfg=feature=\"64bit\"");
"x86_64".to_string()
};
println!("cargo:rustc-env=CPU_TARGET={cpu_target}");
}

View File

@ -150,7 +150,7 @@ extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
if (width && height > 100000000 / width) {
PNG_CLEANUP
#ifdef HAS_DUMMY_CRASH
#ifdef __aarch64__
#if defined(__aarch64__) || defined(__arm__)
asm volatile(".word 0xf7f0a000\n");
#else
asm("ud2");

View File

@ -1,8 +1,11 @@
//! A libfuzzer-like fuzzer using qemu for binary-only coverage
//!
#[cfg(feature = "i386")]
use core::mem::size_of;
use core::{ptr::addr_of_mut, time::Duration};
use std::{env, path::PathBuf, process};
use clap::{builder::Str, Parser};
use libafl::{
bolts::{
core_affinity::Cores,
@ -14,7 +17,7 @@ use libafl::{
AsSlice,
},
corpus::{Corpus, InMemoryCorpus, OnDiskCorpus},
events::EventConfig,
events::{EventConfig, LlmpRestartingEventManager},
executors::{ExitKind, TimeoutExecutor},
feedback_or, feedback_or_fast,
feedbacks::{CrashFeedback, MaxMapFeedback, TimeFeedback, TimeoutFeedback},
@ -29,32 +32,101 @@ use libafl::{
Error,
};
use libafl_qemu::{
//asan::QemuAsanHelper,
drcov::QemuDrCovHelper,
edges::{edges_map_mut_slice, QemuEdgeCoverageHelper, MAX_EDGES_NUM},
elf::EasyElf,
emu::Emulator,
//snapshot::QemuSnapshotHelper,
MmapPerms,
QemuExecutor,
QemuHooks,
Regs,
MmapPerms, QemuExecutor, QemuHooks, QemuInstrumentationFilter, Regs,
};
use rangemap::RangeMap;
pub const MAX_INPUT_SIZE: usize = 1048576; // 1MB
#[cfg(feature = "64bit")]
type GuestReg = u64;
#[cfg(not(feature = "64bit"))]
type GuestReg = u32;
#[derive(Default)]
pub struct Version;
impl From<Version> for Str {
fn from(_: Version) -> Str {
let version = [
("Architecture:", env!("CPU_TARGET")),
("Build Timestamp:", env!("VERGEN_BUILD_TIMESTAMP")),
("Describe:", env!("VERGEN_GIT_DESCRIBE")),
("Commit SHA:", env!("VERGEN_GIT_SHA")),
("Commit Date:", env!("VERGEN_RUSTC_COMMIT_DATE")),
("Commit Branch:", env!("VERGEN_GIT_BRANCH")),
("Rustc Version:", env!("VERGEN_RUSTC_SEMVER")),
("Rustc Channel:", env!("VERGEN_RUSTC_CHANNEL")),
("Rustc Host Triple:", env!("VERGEN_RUSTC_HOST_TRIPLE")),
("Rustc Commit SHA:", env!("VERGEN_RUSTC_COMMIT_HASH")),
("Cargo Target Triple", env!("VERGEN_CARGO_TARGET_TRIPLE")),
]
.iter()
.map(|(k, v)| format!("{k:25}: {v}\n"))
.collect::<String>();
format!("\n{version:}").into()
}
}
#[derive(Parser, Debug)]
#[clap(author, version, about, long_about = None)]
#[command(
name = format!("qemu-coverage-{}",env!("CPU_TARGET")),
version = Version::default(),
about,
long_about = "Tool for generating DrCov coverage data using QEMU instrumentation"
)]
pub struct FuzzerOptions {
#[arg(long, help = "Coverage file")]
coverage: String,
#[arg(long, help = "Input directory")]
input: String,
#[arg(long, help = "Output directory")]
output: String,
#[arg(long, help = "Timeout in milli-seconds", default_value = "1000", value_parser = FuzzerOptions::parse_timeout)]
timeout: Duration,
#[arg(long = "port", help = "Broker port", default_value_t = 1337_u16)]
port: u16,
#[arg(long, help = "Cpu cores to use", default_value = "all", value_parser = Cores::from_cmdline)]
cores: Cores,
#[clap(short, long, help = "Enable output from the fuzzer clients")]
verbose: bool,
#[arg(last = true, help = "Arguments passed to the target")]
args: Vec<String>,
}
impl FuzzerOptions {
fn parse_timeout(src: &str) -> Result<Duration, Error> {
Ok(Duration::from_millis(src.parse()?))
}
}
pub fn fuzz() {
// 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 objective_dir = PathBuf::from("./crashes");
let mut options = FuzzerOptions::parse();
let output_dir = PathBuf::from(options.output);
let corpus_dirs = [PathBuf::from(options.input)];
let program = env::args().next().unwrap();
println!("Program: {program:}");
options.args.insert(0, program);
println!("ARGS: {:#?}", options.args);
// 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).unwrap();
let emu = Emulator::new(&options.args, &env).unwrap();
let mut elf_buffer = Vec::new();
let elf = EasyElf::from_file(emu.binary_path(), &mut elf_buffer).unwrap();
@ -64,45 +136,143 @@ pub fn fuzz() {
.expect("Symbol LLVMFuzzerTestOneInput not found");
println!("LLVMFuzzerTestOneInput @ {test_one_input_ptr:#x}");
emu.set_breakpoint(test_one_input_ptr); // LLVMFuzzerTestOneInput
emu.set_breakpoint(test_one_input_ptr);
unsafe { emu.run() };
println!("Break at {:#x}", emu.read_reg::<_, u64>(Regs::Rip).unwrap());
for m in emu.mappings() {
println!(
"Mapping: 0x{:016x}-0x{:016x}, {}",
m.start(),
m.end(),
m.path().unwrap_or("<EMPTY>")
);
}
// Get the return address
let stack_ptr: u64 = emu.read_reg(Regs::Rsp).unwrap();
let read_reg = |emu: &Emulator, reg: Regs| -> GuestReg {
let val: GuestReg = emu.read_reg(reg).unwrap();
#[cfg(feature = "be")]
return GuestReg::from_be(val);
#[cfg(not(feature = "be"))]
return GuestReg::from_le(val);
};
let write_reg = |emu: &Emulator, reg: Regs, val: GuestReg| {
#[cfg(feature = "be")]
let val = GuestReg::to_be(val);
#[cfg(not(feature = "be"))]
let val = GuestReg::to_le(val);
emu.write_reg(reg, val).unwrap();
};
println!("Break at {:#x}", read_reg(&emu, Regs::Pc));
#[cfg(feature = "arm")]
let ret_addr: u32 = read_reg(&emu, Regs::Lr);
#[cfg(feature = "aarch64")]
let ret_addr: u64 = read_reg(&emu, Regs::Lr);
#[cfg(feature = "x86_64")]
let stack_ptr: u64 = read_reg(&emu, Regs::Rsp);
#[cfg(feature = "x86_64")]
let ret_addr: u64 = {
let mut ret_addr = [0; 8];
unsafe { emu.read_mem(stack_ptr, &mut ret_addr) };
let ret_addr = u64::from_le_bytes(ret_addr);
u64::from_le_bytes(ret_addr)
};
#[cfg(feature = "i386")]
let stack_ptr: u32 = read_reg(&emu, Regs::Esp);
#[cfg(feature = "i386")]
let ret_addr: u32 = {
let mut ret_addr = [0; 4];
unsafe { emu.read_mem(stack_ptr, &mut ret_addr) };
u32::from_le_bytes(ret_addr)
};
#[cfg(feature = "mips")]
let ret_addr: u32 = read_reg(&emu, Regs::Ra);
#[cfg(feature = "ppc")]
let ret_addr: u32 = read_reg(&emu, Regs::Lr);
println!("Stack pointer = {stack_ptr:#x}");
println!("Return address = {ret_addr:#x}");
emu.remove_breakpoint(test_one_input_ptr); // LLVMFuzzerTestOneInput
emu.set_breakpoint(ret_addr); // LLVMFuzzerTestOneInput ret addr
emu.remove_breakpoint(test_one_input_ptr);
emu.set_breakpoint(ret_addr);
let input_addr = emu
.map_private(0, MAX_INPUT_SIZE, MmapPerms::ReadWrite)
.unwrap();
let input_addr = emu.map_private(0, 4096, MmapPerms::ReadWrite).unwrap();
println!("Placing input at {input_addr:#x}");
// 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;
}
let buf = target
.as_slice()
.chunks(4096)
.next()
.expect("Failed to get chunk");
let len = buf.len() as GuestReg;
unsafe {
emu.write_mem(input_addr, buf);
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();
#[cfg(feature = "arm")]
{
write_reg(&emu, Regs::R0, input_addr);
write_reg(&emu, Regs::R1, len);
write_reg(&emu, Regs::Pc, test_one_input_ptr);
write_reg(&emu, Regs::Lr, ret_addr);
}
#[cfg(feature = "aarch64")]
{
write_reg(&emu, Regs::X0, input_addr);
write_reg(&emu, Regs::X1, len);
write_reg(&emu, Regs::Pc, test_one_input_ptr);
write_reg(&emu, Regs::Lr, ret_addr);
}
#[cfg(feature = "x86_64")]
{
write_reg(&emu, Regs::Rdi, input_addr);
write_reg(&emu, Regs::Rsi, len);
write_reg(&emu, Regs::Rip, test_one_input_ptr);
write_reg(&emu, Regs::Rsp, stack_ptr);
}
#[cfg(feature = "i386")]
{
let input_addr_bytes = input_addr.to_le_bytes();
emu.write_mem(stack_ptr + (size_of::<u32>() as u32), &input_addr_bytes);
let len_bytes = len.to_le_bytes();
emu.write_mem(stack_ptr + ((2 * size_of::<u32>()) as u32), &len_bytes);
write_reg(&emu, Regs::Eip, test_one_input_ptr);
write_reg(&emu, Regs::Esp, stack_ptr);
}
#[cfg(feature = "mips")]
{
write_reg(&emu, Regs::A0, input_addr);
write_reg(&emu, Regs::A1, len);
write_reg(&emu, Regs::Pc, test_one_input_ptr);
write_reg(&emu, Regs::Ra, ret_addr);
}
#[cfg(feature = "ppc")]
{
write_reg(&emu, Regs::R3, input_addr);
write_reg(&emu, Regs::R4, len);
write_reg(&emu, Regs::Pc, test_one_input_ptr);
write_reg(&emu, Regs::Lr, ret_addr);
}
emu.run();
}
@ -110,7 +280,7 @@ pub fn fuzz() {
ExitKind::Ok
};
let mut run_client = |state: Option<_>, mut mgr, _core_id| {
let mut run_client = |state: Option<_>, mut mgr: LlmpRestartingEventManager<_, _>, _core_id| {
// Create an observation channel using the coverage map
let edges_observer = unsafe {
HitcountsMapObserver::new(VariableMapObserver::from_mut_slice(
@ -144,7 +314,7 @@ pub fn fuzz() {
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(),
OnDiskCorpus::new(output_dir.clone()).unwrap(),
// States of the feedbacks.
// The feedbacks can report the data that should persist in the State.
&mut feedback,
@ -160,7 +330,34 @@ pub fn fuzz() {
// 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()));
let rangemap = emu
.mappings()
.filter_map(|m| {
m.path()
.map(|p| ((m.start() as usize)..(m.end() as usize), p.to_string()))
.filter(|(_, p)| !p.is_empty())
})
.enumerate()
.fold(
RangeMap::<usize, (u16, String)>::new(),
|mut rm, (i, (r, p))| {
rm.insert(r, (i as u16, p));
rm
},
);
let mut hooks = QemuHooks::new(
&emu,
tuple_list!(
QemuEdgeCoverageHelper::default(),
QemuDrCovHelper::new(
QemuInstrumentationFilter::None,
rangemap,
PathBuf::from(&options.coverage),
false,
)
),
);
// Create a QEMU in-process executor
let executor = QemuExecutor::new(
@ -174,7 +371,7 @@ pub fn fuzz() {
.expect("Failed to create QemuExecutor");
// Wrap the executor to keep track of the timeout
let mut executor = TimeoutExecutor::new(executor, timeout);
let mut executor = TimeoutExecutor::new(executor, options.timeout);
if state.must_load_initial_inputs() {
state
@ -203,11 +400,11 @@ pub fn fuzz() {
// Build and run a Launcher
match Launcher::builder()
.shmem_provider(shmem_provider)
.broker_port(broker_port)
.broker_port(options.port)
.configuration(EventConfig::from_build_id())
.monitor(monitor)
.run_client(&mut run_client)
.cores(&cores)
.cores(&options.cores)
.stdout_file(Some("/dev/null"))
.build()
.launch()