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:
parent
871dfa0a01
commit
c6062889d5
7
fuzzers/qemu_arm_launcher/.gitignore
vendored
7
fuzzers/qemu_arm_launcher/.gitignore
vendored
@ -1,7 +0,0 @@
|
|||||||
libpng-*
|
|
||||||
libpng_harness
|
|
||||||
libpng_harness_crashing
|
|
||||||
zlib-*
|
|
||||||
crashes
|
|
||||||
target
|
|
||||||
drcov.log
|
|
@ -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"
|
|
@ -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
|
|
||||||
'''
|
|
@ -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 |
@ -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);
|
|
||||||
}
|
|
@ -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:?}"),
|
|
||||||
}
|
|
||||||
}
|
|
@ -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!");
|
|
||||||
}
|
|
@ -7,6 +7,14 @@ edition = "2021"
|
|||||||
[features]
|
[features]
|
||||||
default = ["std"]
|
default = ["std"]
|
||||||
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]
|
[profile.release]
|
||||||
lto = true
|
lto = true
|
||||||
@ -14,6 +22,19 @@ codegen-units = 1
|
|||||||
opt-level = 3
|
opt-level = 3
|
||||||
debug = true
|
debug = true
|
||||||
|
|
||||||
|
[build-dependencies]
|
||||||
|
vergen = { version = "8.2.1", features = ["build", "cargo", "git", "gitcl", "rustc", "si"] }
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
|
clap = { version = "4.3.0", features = ["derive", "string"]}
|
||||||
libafl = { path = "../../libafl/" }
|
libafl = { path = "../../libafl/" }
|
||||||
libafl_qemu = { path = "../../libafl_qemu/", features = ["x86_64", "usermode"] }
|
libafl_qemu = { path = "../../libafl_qemu/", features = ["usermode"] }
|
||||||
|
rangemap = { version = "1.0.3" }
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@ -1,86 +1,250 @@
|
|||||||
# Variables
|
|
||||||
[env]
|
[env]
|
||||||
FUZZER_NAME='libpng_harness'
|
CROSS_CC = "x86_64-linux-gnu-gcc"
|
||||||
PROJECT_DIR = { script = ["pwd"] }
|
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]
|
[tasks.unsupported]
|
||||||
script_runner="@shell"
|
script_runner="@shell"
|
||||||
script='''
|
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]
|
[tasks.target_dir]
|
||||||
condition = { files_not_exist = [ "./libpng-1.6.37" ] }
|
condition = { files_not_exist = [ "${CARGO_MAKE_CRATE_TARGET_DIRECTORY}" ] }
|
||||||
script_runner="@shell"
|
script_runner="@shell"
|
||||||
script='''
|
script='''
|
||||||
wget https://deac-fra.dl.sourceforge.net/project/libpng/libpng16/1.6.37/libpng-1.6.37.tar.xz
|
mkdir ${CARGO_MAKE_CRATE_TARGET_DIRECTORY}
|
||||||
tar -xvf libpng-1.6.37.tar.xz
|
|
||||||
'''
|
'''
|
||||||
|
|
||||||
# fuzzer
|
[tasks.deps_dir]
|
||||||
[tasks.fuzzer]
|
dependencies = ["target_dir"]
|
||||||
linux_alias = "fuzzer_unix"
|
condition = { files_not_exist = [ "${CARGO_MAKE_CRATE_TARGET_DIRECTORY}/deps/" ] }
|
||||||
mac_alias = "fuzzer_unix"
|
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"
|
windows_alias = "unsupported"
|
||||||
|
|
||||||
[tasks.fuzzer_unix]
|
[tasks.zlib_unix_wget]
|
||||||
command = "cargo"
|
dependencies = ["deps_dir"]
|
||||||
args = ["build", "--release"]
|
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]
|
[tasks.harness]
|
||||||
linux_alias = "harness_unix"
|
linux_alias = "harness_unix"
|
||||||
mac_alias = "harness_unix"
|
mac_alias = "unsupported"
|
||||||
windows_alias = "unsupported"
|
windows_alias = "unsupported"
|
||||||
|
|
||||||
[tasks.harness_unix]
|
[tasks.harness_unix]
|
||||||
script_runner="@shell"
|
script_runner="@shell"
|
||||||
script='''
|
script='''
|
||||||
cd libpng-1.6.37 && ./configure --enable-shared=no --with-pic=yes --enable-hardware-optimizations=yes
|
${CROSS_CXX} \
|
||||||
cd "${PROJECT_DIR}"
|
|
||||||
make -C libpng-1.6.37
|
|
||||||
# Build the libpng harness
|
|
||||||
c++ \
|
|
||||||
./harness.cc \
|
./harness.cc \
|
||||||
./libpng-1.6.37/.libs/libpng16.a \
|
$CROSS_CFLAGS \
|
||||||
-I./libpng-1.6.37/ \
|
"${TARGET_DIR}/build-png/.libs/libpng16.a" \
|
||||||
-o ${FUZZER_NAME} \
|
"${TARGET_DIR}/build-zlib/libz.a" \
|
||||||
-lm -lz
|
-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" ]
|
dependencies = [ "libpng" ]
|
||||||
|
|
||||||
# Run the fuzzer
|
|
||||||
[tasks.run]
|
[tasks.run]
|
||||||
linux_alias = "run_unix"
|
linux_alias = "run_unix"
|
||||||
mac_alias = "run_unix"
|
mac_alias = "unsupported"
|
||||||
windows_alias = "unsupported"
|
windows_alias = "unsupported"
|
||||||
|
|
||||||
[tasks.run_unix]
|
[tasks.run_unix]
|
||||||
command = "cargo"
|
command = "${TARGET_DIR}/release/qemu_launcher-${CARGO_MAKE_PROFILE}"
|
||||||
args = ["run", "--release", "./${FUZZER_NAME}"]
|
args = [
|
||||||
|
"--coverage", "${TARGET_DIR}/drcov.log",
|
||||||
|
"--input", "./corpus",
|
||||||
|
"--output", "${TARGET_DIR}/output/",
|
||||||
|
"--",
|
||||||
|
"${TARGET_DIR}/libpng-harness-${CARGO_MAKE_PROFILE}",
|
||||||
|
]
|
||||||
dependencies = [ "harness", "fuzzer" ]
|
dependencies = [ "harness", "fuzzer" ]
|
||||||
|
|
||||||
# Run the fuzzer
|
|
||||||
[tasks.test]
|
[tasks.test]
|
||||||
linux_alias = "test_unix"
|
linux_alias = "test_unix"
|
||||||
mac_alias = "test_unix"
|
mac_alias = "unsupported"
|
||||||
windows_alias = "unsupported"
|
windows_alias = "unsupported"
|
||||||
|
|
||||||
# Short test
|
|
||||||
[tasks.test_unix]
|
[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_runner = "@shell"
|
||||||
script='''
|
script='''
|
||||||
echo "This test is skipped"
|
echo "This test is skipped"
|
||||||
'''
|
'''
|
||||||
dependencies = [ "harness", "fuzzer" ]
|
|
||||||
|
|
||||||
# Clean up
|
|
||||||
[tasks.clean]
|
[tasks.clean]
|
||||||
linux_alias = "clean_unix"
|
linux_alias = "clean_unix"
|
||||||
mac_alias = "clean_unix"
|
mac_alias = "clean_unix"
|
||||||
@ -91,7 +255,64 @@ windows_alias = "unsupported"
|
|||||||
clear = true
|
clear = true
|
||||||
script_runner="@shell"
|
script_runner="@shell"
|
||||||
script='''
|
script='''
|
||||||
rm -f ./${FUZZER_NAME}
|
rm -rf ${CARGO_MAKE_CRATE_TARGET_DIRECTORY}
|
||||||
make -C libpng-1.6.37 clean
|
|
||||||
cargo clean
|
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"
|
||||||
|
]
|
||||||
|
@ -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.
|
This folder contains an example fuzzer for libpng, using LLMP for fast multi-process fuzzing and crash detection. It has been tested on Linux.
|
||||||
To show off crash detection, we added a `ud2` instruction to the harness, edit harness.cc if you want a non-crashing example.
|
This automatically spawns n child processes, and binds them to a free core.
|
||||||
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.
|
The following architectures are supported:
|
||||||
|
* arm
|
||||||
## Build
|
* aarch64
|
||||||
|
* i386
|
||||||
To build this example, run
|
* x86_64
|
||||||
|
* mips
|
||||||
|
* ppc
|
||||||
|
|
||||||
|
## Prerequisites
|
||||||
```bash
|
```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
|
## Run
|
||||||
|
|
||||||
Just run once, the launcher feature should do the rest.
|
Defaults to `x86_64` architecture
|
||||||
|
```bash
|
||||||
|
cargo make run
|
||||||
|
```
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cargo make <arch>
|
||||||
|
```
|
||||||
|
48
fuzzers/qemu_launcher/build.rs
Normal file
48
fuzzers/qemu_launcher/build.rs
Normal 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}");
|
||||||
|
}
|
@ -150,7 +150,7 @@ extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
|
|||||||
if (width && height > 100000000 / width) {
|
if (width && height > 100000000 / width) {
|
||||||
PNG_CLEANUP
|
PNG_CLEANUP
|
||||||
#ifdef HAS_DUMMY_CRASH
|
#ifdef HAS_DUMMY_CRASH
|
||||||
#ifdef __aarch64__
|
#if defined(__aarch64__) || defined(__arm__)
|
||||||
asm volatile(".word 0xf7f0a000\n");
|
asm volatile(".word 0xf7f0a000\n");
|
||||||
#else
|
#else
|
||||||
asm("ud2");
|
asm("ud2");
|
||||||
|
@ -1,8 +1,11 @@
|
|||||||
//! A libfuzzer-like fuzzer using qemu for binary-only coverage
|
//! 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 core::{ptr::addr_of_mut, time::Duration};
|
||||||
use std::{env, path::PathBuf, process};
|
use std::{env, path::PathBuf, process};
|
||||||
|
|
||||||
|
use clap::{builder::Str, Parser};
|
||||||
use libafl::{
|
use libafl::{
|
||||||
bolts::{
|
bolts::{
|
||||||
core_affinity::Cores,
|
core_affinity::Cores,
|
||||||
@ -14,7 +17,7 @@ use libafl::{
|
|||||||
AsSlice,
|
AsSlice,
|
||||||
},
|
},
|
||||||
corpus::{Corpus, InMemoryCorpus, OnDiskCorpus},
|
corpus::{Corpus, InMemoryCorpus, OnDiskCorpus},
|
||||||
events::EventConfig,
|
events::{EventConfig, LlmpRestartingEventManager},
|
||||||
executors::{ExitKind, TimeoutExecutor},
|
executors::{ExitKind, TimeoutExecutor},
|
||||||
feedback_or, feedback_or_fast,
|
feedback_or, feedback_or_fast,
|
||||||
feedbacks::{CrashFeedback, MaxMapFeedback, TimeFeedback, TimeoutFeedback},
|
feedbacks::{CrashFeedback, MaxMapFeedback, TimeFeedback, TimeoutFeedback},
|
||||||
@ -29,32 +32,101 @@ use libafl::{
|
|||||||
Error,
|
Error,
|
||||||
};
|
};
|
||||||
use libafl_qemu::{
|
use libafl_qemu::{
|
||||||
//asan::QemuAsanHelper,
|
drcov::QemuDrCovHelper,
|
||||||
edges::{edges_map_mut_slice, QemuEdgeCoverageHelper, MAX_EDGES_NUM},
|
edges::{edges_map_mut_slice, QemuEdgeCoverageHelper, MAX_EDGES_NUM},
|
||||||
elf::EasyElf,
|
elf::EasyElf,
|
||||||
emu::Emulator,
|
emu::Emulator,
|
||||||
//snapshot::QemuSnapshotHelper,
|
MmapPerms, QemuExecutor, QemuHooks, QemuInstrumentationFilter, Regs,
|
||||||
MmapPerms,
|
|
||||||
QemuExecutor,
|
|
||||||
QemuHooks,
|
|
||||||
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() {
|
pub fn fuzz() {
|
||||||
// Hardcoded parameters
|
let mut options = FuzzerOptions::parse();
|
||||||
let timeout = Duration::from_secs(1);
|
|
||||||
let broker_port = 1337;
|
let output_dir = PathBuf::from(options.output);
|
||||||
let cores = Cores::from_cmdline("0-11").unwrap();
|
let corpus_dirs = [PathBuf::from(options.input)];
|
||||||
let corpus_dirs = [PathBuf::from("./corpus")];
|
|
||||||
let objective_dir = PathBuf::from("./crashes");
|
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");
|
env::remove_var("LD_LIBRARY_PATH");
|
||||||
let args: Vec<String> = env::args().collect();
|
|
||||||
let env: Vec<(String, String)> = env::vars().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 mut elf_buffer = Vec::new();
|
||||||
let elf = EasyElf::from_file(emu.binary_path(), &mut elf_buffer).unwrap();
|
let elf = EasyElf::from_file(emu.binary_path(), &mut elf_buffer).unwrap();
|
||||||
@ -64,45 +136,143 @@ pub fn fuzz() {
|
|||||||
.expect("Symbol LLVMFuzzerTestOneInput not found");
|
.expect("Symbol LLVMFuzzerTestOneInput not found");
|
||||||
println!("LLVMFuzzerTestOneInput @ {test_one_input_ptr:#x}");
|
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() };
|
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 read_reg = |emu: &Emulator, reg: Regs| -> GuestReg {
|
||||||
let stack_ptr: u64 = emu.read_reg(Regs::Rsp).unwrap();
|
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];
|
let mut ret_addr = [0; 8];
|
||||||
unsafe { emu.read_mem(stack_ptr, &mut ret_addr) };
|
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}");
|
println!("Return address = {ret_addr:#x}");
|
||||||
|
|
||||||
emu.remove_breakpoint(test_one_input_ptr); // LLVMFuzzerTestOneInput
|
emu.remove_breakpoint(test_one_input_ptr);
|
||||||
emu.set_breakpoint(ret_addr); // LLVMFuzzerTestOneInput ret addr
|
emu.set_breakpoint(ret_addr);
|
||||||
|
|
||||||
let input_addr = emu
|
let input_addr = emu.map_private(0, 4096, MmapPerms::ReadWrite).unwrap();
|
||||||
.map_private(0, MAX_INPUT_SIZE, MmapPerms::ReadWrite)
|
|
||||||
.unwrap();
|
|
||||||
println!("Placing input at {input_addr:#x}");
|
println!("Placing input at {input_addr:#x}");
|
||||||
|
|
||||||
// The wrapped harness function, calling out to the LLVM-style harness
|
|
||||||
let mut harness = |input: &BytesInput| {
|
let mut harness = |input: &BytesInput| {
|
||||||
let target = input.target_bytes();
|
let target = input.target_bytes();
|
||||||
let mut buf = target.as_slice();
|
let buf = target
|
||||||
let mut len = buf.len();
|
.as_slice()
|
||||||
if len > MAX_INPUT_SIZE {
|
.chunks(4096)
|
||||||
buf = &buf[0..MAX_INPUT_SIZE];
|
.next()
|
||||||
len = MAX_INPUT_SIZE;
|
.expect("Failed to get chunk");
|
||||||
}
|
let len = buf.len() as GuestReg;
|
||||||
|
|
||||||
unsafe {
|
unsafe {
|
||||||
emu.write_mem(input_addr, buf);
|
emu.write_mem(input_addr, buf);
|
||||||
|
|
||||||
emu.write_reg(Regs::Rdi, input_addr).unwrap();
|
#[cfg(feature = "arm")]
|
||||||
emu.write_reg(Regs::Rsi, len).unwrap();
|
{
|
||||||
emu.write_reg(Regs::Rip, test_one_input_ptr).unwrap();
|
write_reg(&emu, Regs::R0, input_addr);
|
||||||
emu.write_reg(Regs::Rsp, stack_ptr).unwrap();
|
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();
|
emu.run();
|
||||||
}
|
}
|
||||||
@ -110,7 +280,7 @@ pub fn fuzz() {
|
|||||||
ExitKind::Ok
|
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
|
// Create an observation channel using the coverage map
|
||||||
let edges_observer = unsafe {
|
let edges_observer = unsafe {
|
||||||
HitcountsMapObserver::new(VariableMapObserver::from_mut_slice(
|
HitcountsMapObserver::new(VariableMapObserver::from_mut_slice(
|
||||||
@ -144,7 +314,7 @@ pub fn fuzz() {
|
|||||||
InMemoryCorpus::new(),
|
InMemoryCorpus::new(),
|
||||||
// Corpus in which we store solutions (crashes in this example),
|
// Corpus in which we store solutions (crashes in this example),
|
||||||
// on disk so the user can get them after stopping the fuzzer
|
// 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.
|
// States of the feedbacks.
|
||||||
// The feedbacks can report the data that should persist in the State.
|
// The feedbacks can report the data that should persist in the State.
|
||||||
&mut feedback,
|
&mut feedback,
|
||||||
@ -160,7 +330,34 @@ pub fn fuzz() {
|
|||||||
// A fuzzer with feedbacks and a corpus scheduler
|
// A fuzzer with feedbacks and a corpus scheduler
|
||||||
let mut fuzzer = StdFuzzer::new(scheduler, feedback, objective);
|
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
|
// Create a QEMU in-process executor
|
||||||
let executor = QemuExecutor::new(
|
let executor = QemuExecutor::new(
|
||||||
@ -174,7 +371,7 @@ pub fn fuzz() {
|
|||||||
.expect("Failed to create QemuExecutor");
|
.expect("Failed to create QemuExecutor");
|
||||||
|
|
||||||
// Wrap the executor to keep track of the timeout
|
// 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() {
|
if state.must_load_initial_inputs() {
|
||||||
state
|
state
|
||||||
@ -203,11 +400,11 @@ pub fn fuzz() {
|
|||||||
// Build and run a Launcher
|
// Build and run a Launcher
|
||||||
match Launcher::builder()
|
match Launcher::builder()
|
||||||
.shmem_provider(shmem_provider)
|
.shmem_provider(shmem_provider)
|
||||||
.broker_port(broker_port)
|
.broker_port(options.port)
|
||||||
.configuration(EventConfig::from_build_id())
|
.configuration(EventConfig::from_build_id())
|
||||||
.monitor(monitor)
|
.monitor(monitor)
|
||||||
.run_client(&mut run_client)
|
.run_client(&mut run_client)
|
||||||
.cores(&cores)
|
.cores(&options.cores)
|
||||||
.stdout_file(Some("/dev/null"))
|
.stdout_file(Some("/dev/null"))
|
||||||
.build()
|
.build()
|
||||||
.launch()
|
.launch()
|
||||||
|
Loading…
x
Reference in New Issue
Block a user