Qemu arm launcher (#708)
* Adding qemu_arm_launcher crate * Trying to fix qemu arm usermode * Cargo fmt * Adding CROSS_CC env * Remove hardcoded arm-linux-gnueabi-gcc and replace by CROSS_CC * Adding arm-linux-gnueabi-gcc to github workflows for ubuntu * Fixing typo in apt install package * Resetting LR after each fuzzing emulation * Cargo fmt after merge conflict * Using GuestAddr * Compiling, running and running with artificial crash detection * Adding dependencies for github workflow to cross compile for arm * Fixing github workflow for ubuntu fuzzer * arm-linux-binutils for mac in github workflows * Qemu does not work for mac, no need to compile qemu_arm_launcher harness for it
This commit is contained in:
parent
376e3adfcd
commit
b2a1e03703
4
.github/workflows/build_and_test.yml
vendored
4
.github/workflows/build_and_test.yml
vendored
@ -52,7 +52,7 @@ jobs:
|
||||
- name: set mold linker as default linker
|
||||
uses: rui314/setup-mold@v1
|
||||
- name: Install deps
|
||||
run: sudo apt-get install -y llvm llvm-dev clang ninja-build clang-format-13 shellcheck
|
||||
run: sudo apt-get install -y llvm llvm-dev clang ninja-build clang-format-13 shellcheck gcc-arm-linux-gnueabi g++-arm-linux-gnueabi
|
||||
- name: get clang version
|
||||
run: command -v llvm-config && clang -v
|
||||
- name: Install cargo-hack
|
||||
@ -149,7 +149,7 @@ jobs:
|
||||
run: rustup toolchain install nightly --component rustfmt --component clippy --allow-downgrade
|
||||
- uses: lyricwulf/abc@v1
|
||||
with:
|
||||
linux: llvm llvm-dev clang nasm ninja-build
|
||||
linux: llvm llvm-dev clang nasm ninja-build gcc-arm-linux-gnueabi g++-arm-linux-gnueabi
|
||||
# update bash for macos to support `declare -A` command`
|
||||
macos: llvm libpng nasm coreutils z3 bash
|
||||
- name: install cargo-make
|
||||
|
1
.gitignore
vendored
1
.gitignore
vendored
@ -7,6 +7,7 @@ vendor
|
||||
.env
|
||||
|
||||
*.tmp
|
||||
*.swp
|
||||
*.o
|
||||
*.a
|
||||
*.so
|
||||
|
6
fuzzers/qemu_arm_launcher/.gitignore
vendored
Normal file
6
fuzzers/qemu_arm_launcher/.gitignore
vendored
Normal file
@ -0,0 +1,6 @@
|
||||
libpng-*
|
||||
libpng_harness
|
||||
libpng_harness_crashing
|
||||
zlib-*
|
||||
crashes
|
||||
target
|
19
fuzzers/qemu_arm_launcher/Cargo.toml
Normal file
19
fuzzers/qemu_arm_launcher/Cargo.toml
Normal file
@ -0,0 +1,19 @@
|
||||
[package]
|
||||
name = "qemu_arm_launcher"
|
||||
version = "0.8.0"
|
||||
authors = ["Andrea Fioraldi <andreafioraldi@gmail.com>", "Dominik Maier <domenukk@gmail.com>"]
|
||||
edition = "2018"
|
||||
|
||||
[features]
|
||||
default = ["std"]
|
||||
std = []
|
||||
|
||||
[profile.release]
|
||||
#lto = true
|
||||
#codegen-units = 1
|
||||
#opt-level = 3
|
||||
debug = true
|
||||
|
||||
[dependencies]
|
||||
libafl = { path = "../../libafl/" }
|
||||
libafl_qemu = { path = "../../libafl_qemu/", features = ["usermode", "arm"] }
|
170
fuzzers/qemu_arm_launcher/Makefile.toml
Normal file
170
fuzzers/qemu_arm_launcher/Makefile.toml
Normal file
@ -0,0 +1,170 @@
|
||||
# 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.12" ] }
|
||||
script_runner="@shell"
|
||||
script='''
|
||||
wget http://www.zlib.net/zlib-1.2.12.tar.gz
|
||||
tar -xvf zlib-1.2.12.tar.gz
|
||||
'''
|
||||
|
||||
[tasks.zlib_unix]
|
||||
condition = { files_not_exist = [ "./zlib-1.2.12/zlib/lib/libz.a" ] }
|
||||
script_runner="@shell"
|
||||
script='''
|
||||
cd zlib-1.2.12 && 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.12/zlib/lib LDFLAGS=-L../zlib-1.2.12/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.12/zlib/lib/libz.a \
|
||||
-I./libpng-1.6.37/ \
|
||||
-I../zlib-1.2.12/zlib/lib \
|
||||
-L../zlib-1.2.12/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.12/zlib/lib/libz.a \
|
||||
-I./libpng-1.6.37/ \
|
||||
-I../zlib-1.2.12/zlib/lib \
|
||||
-L../zlib-1.2.12/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 cargo run --release ./${FUZZER_NAME} 2>/dev/null &
|
||||
'''
|
||||
dependencies = [ "harness", "fuzzer" ]
|
||||
|
||||
# Clean up
|
||||
[tasks.clean]
|
||||
linux_alias = "clean_unix"
|
||||
mac_alias = "clean_unix"
|
||||
windows_alias = "unsupported"
|
||||
|
||||
[tasks.clean_unix]
|
||||
# Disable default `clean` definition
|
||||
clear = true
|
||||
script_runner="@shell"
|
||||
script='''
|
||||
rm -f ./${FUZZER_NAME}
|
||||
rm -f ./${FUZZER_NAME_CRASHING}
|
||||
rm -rf zlib-*
|
||||
rm -rf libpng-*
|
||||
cargo clean
|
||||
'''
|
24
fuzzers/qemu_arm_launcher/README.md
Normal file
24
fuzzers/qemu_arm_launcher/README.md
Normal file
@ -0,0 +1,24 @@
|
||||
# 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
|
||||
```
|
BIN
fuzzers/qemu_arm_launcher/corpus/not_kitty.png
Normal file
BIN
fuzzers/qemu_arm_launcher/corpus/not_kitty.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 218 B |
BIN
fuzzers/qemu_arm_launcher/corpus/not_kitty_alpha.png
Normal file
BIN
fuzzers/qemu_arm_launcher/corpus/not_kitty_alpha.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 376 B |
BIN
fuzzers/qemu_arm_launcher/corpus/not_kitty_gamma.png
Normal file
BIN
fuzzers/qemu_arm_launcher/corpus/not_kitty_gamma.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 228 B |
BIN
fuzzers/qemu_arm_launcher/corpus/not_kitty_icc.png
Normal file
BIN
fuzzers/qemu_arm_launcher/corpus/not_kitty_icc.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 427 B |
193
fuzzers/qemu_arm_launcher/harness.cc
Normal file
193
fuzzers/qemu_arm_launcher/harness.cc
Normal file
@ -0,0 +1,193 @@
|
||||
// libpng_read_fuzzer.cc
|
||||
// Copyright 2017-2018 Glenn Randers-Pehrson
|
||||
// Copyright 2015 The Chromium Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that may
|
||||
// be found in the LICENSE file https://cs.chromium.org/chromium/src/LICENSE
|
||||
|
||||
// Last changed in libpng 1.6.35 [July 15, 2018]
|
||||
|
||||
// The modifications in 2017 by Glenn Randers-Pehrson include
|
||||
// 1. addition of a PNG_CLEANUP macro,
|
||||
// 2. setting the option to ignore ADLER32 checksums,
|
||||
// 3. adding "#include <string.h>" which is needed on some platforms
|
||||
// to provide memcpy().
|
||||
// 4. adding read_end_info() and creating an end_info structure.
|
||||
// 5. adding calls to png_set_*() transforms commonly used by browsers.
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <vector>
|
||||
|
||||
#define PNG_INTERNAL
|
||||
#include "png.h"
|
||||
|
||||
#define PNG_CLEANUP \
|
||||
if (png_handler.png_ptr) { \
|
||||
if (png_handler.row_ptr) \
|
||||
png_free(png_handler.png_ptr, png_handler.row_ptr); \
|
||||
if (png_handler.end_info_ptr) \
|
||||
png_destroy_read_struct(&png_handler.png_ptr, &png_handler.info_ptr, \
|
||||
&png_handler.end_info_ptr); \
|
||||
else if (png_handler.info_ptr) \
|
||||
png_destroy_read_struct(&png_handler.png_ptr, &png_handler.info_ptr, \
|
||||
nullptr); \
|
||||
else \
|
||||
png_destroy_read_struct(&png_handler.png_ptr, nullptr, nullptr); \
|
||||
png_handler.png_ptr = nullptr; \
|
||||
png_handler.row_ptr = nullptr; \
|
||||
png_handler.info_ptr = nullptr; \
|
||||
png_handler.end_info_ptr = nullptr; \
|
||||
}
|
||||
|
||||
struct BufState {
|
||||
const uint8_t *data;
|
||||
size_t bytes_left;
|
||||
};
|
||||
|
||||
struct PngObjectHandler {
|
||||
png_infop info_ptr = nullptr;
|
||||
png_structp png_ptr = nullptr;
|
||||
png_infop end_info_ptr = nullptr;
|
||||
png_voidp row_ptr = nullptr;
|
||||
BufState *buf_state = nullptr;
|
||||
|
||||
~PngObjectHandler() {
|
||||
if (row_ptr) { png_free(png_ptr, row_ptr); }
|
||||
if (end_info_ptr)
|
||||
png_destroy_read_struct(&png_ptr, &info_ptr, &end_info_ptr);
|
||||
else if (info_ptr)
|
||||
png_destroy_read_struct(&png_ptr, &info_ptr, nullptr);
|
||||
else
|
||||
png_destroy_read_struct(&png_ptr, nullptr, nullptr);
|
||||
delete buf_state;
|
||||
}
|
||||
};
|
||||
|
||||
void user_read_data(png_structp png_ptr, png_bytep data, size_t length) {
|
||||
BufState *buf_state = static_cast<BufState *>(png_get_io_ptr(png_ptr));
|
||||
if (length > buf_state->bytes_left) { png_error(png_ptr, "read error"); }
|
||||
memcpy(data, buf_state->data, length);
|
||||
buf_state->bytes_left -= length;
|
||||
buf_state->data += length;
|
||||
}
|
||||
|
||||
static const int kPngHeaderSize = 8;
|
||||
|
||||
// Entry point for LibFuzzer.
|
||||
// Roughly follows the libpng book example:
|
||||
// http://www.libpng.org/pub/png/book/chapter13.html
|
||||
extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
|
||||
if (size < kPngHeaderSize) { return 0; }
|
||||
|
||||
std::vector<unsigned char> v(data, data + size);
|
||||
if (png_sig_cmp(v.data(), 0, kPngHeaderSize)) {
|
||||
// not a PNG.
|
||||
return 0;
|
||||
}
|
||||
|
||||
PngObjectHandler png_handler;
|
||||
png_handler.png_ptr = nullptr;
|
||||
png_handler.row_ptr = nullptr;
|
||||
png_handler.info_ptr = nullptr;
|
||||
png_handler.end_info_ptr = nullptr;
|
||||
|
||||
png_handler.png_ptr =
|
||||
png_create_read_struct(PNG_LIBPNG_VER_STRING, nullptr, nullptr, nullptr);
|
||||
if (!png_handler.png_ptr) { return 0; }
|
||||
|
||||
png_handler.info_ptr = png_create_info_struct(png_handler.png_ptr);
|
||||
if (!png_handler.info_ptr) {
|
||||
PNG_CLEANUP
|
||||
return 0;
|
||||
}
|
||||
|
||||
png_handler.end_info_ptr = png_create_info_struct(png_handler.png_ptr);
|
||||
if (!png_handler.end_info_ptr) {
|
||||
PNG_CLEANUP
|
||||
return 0;
|
||||
}
|
||||
|
||||
png_set_crc_action(png_handler.png_ptr, PNG_CRC_QUIET_USE, PNG_CRC_QUIET_USE);
|
||||
#ifdef PNG_IGNORE_ADLER32
|
||||
png_set_option(png_handler.png_ptr, PNG_IGNORE_ADLER32, PNG_OPTION_ON);
|
||||
#endif
|
||||
|
||||
// Setting up reading from buffer.
|
||||
png_handler.buf_state = new BufState();
|
||||
png_handler.buf_state->data = data + kPngHeaderSize;
|
||||
png_handler.buf_state->bytes_left = size - kPngHeaderSize;
|
||||
png_set_read_fn(png_handler.png_ptr, png_handler.buf_state, user_read_data);
|
||||
png_set_sig_bytes(png_handler.png_ptr, kPngHeaderSize);
|
||||
|
||||
if (setjmp(png_jmpbuf(png_handler.png_ptr))) {
|
||||
PNG_CLEANUP
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Reading.
|
||||
png_read_info(png_handler.png_ptr, png_handler.info_ptr);
|
||||
|
||||
// reset error handler to put png_deleter into scope.
|
||||
if (setjmp(png_jmpbuf(png_handler.png_ptr))) {
|
||||
PNG_CLEANUP
|
||||
return 0;
|
||||
}
|
||||
|
||||
png_uint_32 width, height;
|
||||
int bit_depth, color_type, interlace_type, compression_type;
|
||||
int filter_type;
|
||||
|
||||
if (!png_get_IHDR(png_handler.png_ptr, png_handler.info_ptr, &width, &height,
|
||||
&bit_depth, &color_type, &interlace_type, &compression_type,
|
||||
&filter_type)) {
|
||||
PNG_CLEANUP
|
||||
return 0;
|
||||
}
|
||||
|
||||
// This is going to be too slow.
|
||||
if (width && height > 100000000 / width) {
|
||||
PNG_CLEANUP
|
||||
#ifdef HAS_DUMMY_CRASH
|
||||
#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);
|
||||
}
|
210
fuzzers/qemu_arm_launcher/src/fuzzer.rs
Normal file
210
fuzzers/qemu_arm_launcher/src/fuzzer.rs
Normal file
@ -0,0 +1,210 @@
|
||||
//! A libfuzzer-like fuzzer using qemu for binary-only coverage
|
||||
//!
|
||||
use core::time::Duration;
|
||||
use std::{env, path::PathBuf, process};
|
||||
|
||||
use libafl::{
|
||||
bolts::{
|
||||
core_affinity::Cores,
|
||||
current_nanos,
|
||||
launcher::Launcher,
|
||||
rands::StdRand,
|
||||
shmem::{ShMemProvider, StdShMemProvider},
|
||||
tuples::tuple_list,
|
||||
AsSlice,
|
||||
},
|
||||
corpus::{Corpus, InMemoryCorpus, OnDiskCorpus},
|
||||
events::EventConfig,
|
||||
executors::{ExitKind, TimeoutExecutor},
|
||||
feedback_or, feedback_or_fast,
|
||||
feedbacks::{CrashFeedback, MaxMapFeedback, TimeFeedback, TimeoutFeedback},
|
||||
fuzzer::{Fuzzer, StdFuzzer},
|
||||
inputs::{BytesInput, HasTargetBytes},
|
||||
monitors::MultiMonitor,
|
||||
mutators::scheduled::{havoc_mutations, StdScheduledMutator},
|
||||
observers::{HitcountsMapObserver, TimeObserver, VariableMapObserver},
|
||||
schedulers::{IndexesLenTimeMinimizerScheduler, QueueScheduler},
|
||||
stages::StdMutationalStage,
|
||||
state::{HasCorpus, StdState},
|
||||
Error,
|
||||
};
|
||||
use libafl_qemu::{
|
||||
//asan::QemuAsanHelper,
|
||||
edges,
|
||||
edges::QemuEdgeCoverageHelper,
|
||||
elf::EasyElf,
|
||||
emu::Emulator,
|
||||
MmapPerms,
|
||||
QemuExecutor,
|
||||
QemuHooks,
|
||||
Regs,
|
||||
};
|
||||
|
||||
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);
|
||||
|
||||
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 @ {:#x}", test_one_input_ptr);
|
||||
|
||||
emu.set_breakpoint(test_one_input_ptr); // LLVMFuzzerTestOneInput
|
||||
unsafe { emu.run() };
|
||||
|
||||
println!("Break at {:#x}", emu.read_reg::<_, u64>(Regs::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 = {:#x}", stack_ptr);
|
||||
println!("Return address = {:#x}", ret_addr);
|
||||
|
||||
emu.remove_breakpoint(test_one_input_ptr); // LLVMFuzzerTestOneInput
|
||||
emu.set_breakpoint(ret_addr); // LLVMFuzzerTestOneInput ret addr
|
||||
|
||||
let input_addr = emu.map_private(0, 4096, MmapPerms::ReadWrite).unwrap();
|
||||
println!("Placing input at {:#x}", input_addr);
|
||||
|
||||
// The wrapped harness function, calling out to the LLVM-style harness
|
||||
let mut harness = |input: &BytesInput| {
|
||||
let target = input.target_bytes();
|
||||
let mut buf = target.as_slice();
|
||||
let mut len = buf.len();
|
||||
if len > 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 = unsafe { &mut edges::EDGES_MAP };
|
||||
let edges_counter = unsafe { &mut edges::MAX_EDGES_NUM };
|
||||
let edges_observer =
|
||||
HitcountsMapObserver::new(VariableMapObserver::new("edges", edges, edges_counter));
|
||||
|
||||
// Create an observation channel to keep track of the execution time
|
||||
let time_observer = TimeObserver::new("time");
|
||||
|
||||
// Feedback to rate the interestingness of an input
|
||||
// This one is composed by two Feedbacks in OR
|
||||
let mut feedback = feedback_or!(
|
||||
// New maximization map feedback linked to the edges observer and the feedback state
|
||||
MaxMapFeedback::new_tracking(&edges_observer, true, false),
|
||||
// Time feedback, this one does not need a feedback state
|
||||
TimeFeedback::new_with_observer(&time_observer)
|
||||
);
|
||||
|
||||
// A feedback to choose if an input is a solution or not
|
||||
let mut objective = feedback_or_fast!(CrashFeedback::new(), TimeoutFeedback::new());
|
||||
|
||||
// If not restarting, create a State from scratch
|
||||
let mut state = state.unwrap_or_else(|| {
|
||||
StdState::new(
|
||||
// RNG
|
||||
StdRand::with_seed(current_nanos()),
|
||||
// Corpus that will be evolved, we keep it in memory for performance
|
||||
InMemoryCorpus::new(),
|
||||
// Corpus in which we store solutions (crashes in this example),
|
||||
// on disk so the user can get them after stopping the fuzzer
|
||||
OnDiskCorpus::new(objective_dir.clone()).unwrap(),
|
||||
// States of the feedbacks.
|
||||
// The feedbacks can report the data that should persist in the State.
|
||||
&mut feedback,
|
||||
// Same for objective feedbacks
|
||||
&mut objective,
|
||||
)
|
||||
.unwrap()
|
||||
});
|
||||
|
||||
// A minimization+queue policy to get testcasess from the corpus
|
||||
let scheduler = IndexesLenTimeMinimizerScheduler::new(QueueScheduler::new());
|
||||
|
||||
// A fuzzer with feedbacks and a corpus scheduler
|
||||
let mut fuzzer = StdFuzzer::new(scheduler, feedback, objective);
|
||||
|
||||
let mut hooks = QemuHooks::new(&emu, tuple_list!(QemuEdgeCoverageHelper::default()));
|
||||
|
||||
// Create a QEMU in-process executor
|
||||
let executor = QemuExecutor::new(
|
||||
&mut hooks,
|
||||
&mut harness,
|
||||
tuple_list!(edges_observer, time_observer),
|
||||
&mut fuzzer,
|
||||
&mut state,
|
||||
&mut mgr,
|
||||
)
|
||||
.expect("Failed to create QemuExecutor");
|
||||
|
||||
// Wrap the executor to keep track of the timeout
|
||||
let mut executor = TimeoutExecutor::new(executor, timeout);
|
||||
|
||||
if state.corpus().count() < 1 {
|
||||
state
|
||||
.load_initial_inputs(&mut fuzzer, &mut executor, &mut mgr, &corpus_dirs)
|
||||
.unwrap_or_else(|_| {
|
||||
println!("Failed to load initial corpus at {:?}", &corpus_dirs);
|
||||
process::exit(0);
|
||||
});
|
||||
println!("We imported {} inputs from disk.", state.corpus().count());
|
||||
}
|
||||
|
||||
// Setup an havoc mutator with a mutational stage
|
||||
let mutator = StdScheduledMutator::new(havoc_mutations());
|
||||
let mut stages = tuple_list!(StdMutationalStage::new(mutator));
|
||||
|
||||
fuzzer.fuzz_loop(&mut stages, &mut executor, &mut state, &mut mgr)?;
|
||||
Ok(())
|
||||
};
|
||||
|
||||
// The shared memory allocator
|
||||
let shmem_provider = StdShMemProvider::new().expect("Failed to init shared memory");
|
||||
|
||||
// The stats reporter for the broker
|
||||
let monitor = MultiMonitor::new(|s| println!("{}", s));
|
||||
|
||||
// Build and run a Launcher
|
||||
match Launcher::builder()
|
||||
.shmem_provider(shmem_provider)
|
||||
.broker_port(broker_port)
|
||||
.configuration(EventConfig::from_build_id())
|
||||
.monitor(monitor)
|
||||
.run_client(&mut run_client)
|
||||
.cores(&cores)
|
||||
.stdout_file(Some("/dev/null"))
|
||||
.build()
|
||||
.launch()
|
||||
{
|
||||
Ok(()) => (),
|
||||
Err(Error::ShuttingDown) => println!("Fuzzing stopped by user. Good bye."),
|
||||
Err(err) => panic!("Failed to run launcher: {:?}", err),
|
||||
}
|
||||
}
|
13
fuzzers/qemu_arm_launcher/src/main.rs
Normal file
13
fuzzers/qemu_arm_launcher/src/main.rs
Normal file
@ -0,0 +1,13 @@
|
||||
//! A libfuzzer-like fuzzer using qemu for binary-only coverage
|
||||
#[cfg(target_os = "linux")]
|
||||
mod fuzzer;
|
||||
|
||||
#[cfg(target_os = "linux")]
|
||||
pub fn main() {
|
||||
fuzzer::fuzz();
|
||||
}
|
||||
|
||||
#[cfg(not(target_os = "linux"))]
|
||||
pub fn main() {
|
||||
panic!("qemu-user and libafl_qemu is only supported on linux!");
|
||||
}
|
@ -3,7 +3,7 @@ use which::which;
|
||||
|
||||
const QEMU_URL: &str = "https://github.com/AFLplusplus/qemu-libafl-bridge";
|
||||
const QEMU_DIRNAME: &str = "qemu-libafl-bridge";
|
||||
const QEMU_REVISION: &str = "8d8fc6b1bd69684a5ea57e2354153708cc2b3eed";
|
||||
const QEMU_REVISION: &str = "ebda58f3e94a82f769890814339295b467f16680";
|
||||
|
||||
fn build_dep_check(tools: &[&str]) {
|
||||
for tool in tools {
|
||||
@ -332,6 +332,8 @@ pub fn build() {
|
||||
println!("cargo:rustc-link-lib=glib-2.0");
|
||||
println!("cargo:rustc-link-lib=stdc++");
|
||||
|
||||
println!("cargo:rustc-link-lib=z");
|
||||
|
||||
/* #[cfg(not(feature = "python"))]
|
||||
{
|
||||
fs::copy(
|
||||
|
@ -446,7 +446,7 @@ where
|
||||
pub fn gen_readwrite_asan<I, QT, S>(
|
||||
hooks: &mut QemuHooks<'_, I, QT, S>,
|
||||
_state: Option<&mut S>,
|
||||
pc: u64,
|
||||
pc: GuestAddr,
|
||||
_size: usize,
|
||||
) -> Option<u64>
|
||||
where
|
||||
@ -454,13 +454,12 @@ where
|
||||
QT: QemuHelperTuple<I, S>,
|
||||
{
|
||||
let h = hooks.match_helper_mut::<QemuAsanHelper>().unwrap();
|
||||
if h.must_instrument(pc) {
|
||||
Some(pc)
|
||||
if h.must_instrument(pc.into()) {
|
||||
Some(pc.into())
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
pub fn trace_read1_asan<I, QT, S>(
|
||||
hooks: &mut QemuHooks<'_, I, QT, S>,
|
||||
_state: Option<&mut S>,
|
||||
|
@ -124,7 +124,7 @@ where
|
||||
{
|
||||
let emu = hooks.emulator();
|
||||
if let Some(h) = hooks.helpers().match_first_type::<QemuCallTracerHelper>() {
|
||||
if !h.must_instrument(pc) {
|
||||
if !h.must_instrument(pc.into()) {
|
||||
return None;
|
||||
}
|
||||
|
||||
@ -144,7 +144,7 @@ where
|
||||
|
||||
let mut iaddr = pc;
|
||||
|
||||
'disasm: while let Ok(insns) = h.cs.disasm_count(code, iaddr, 1) {
|
||||
'disasm: while let Ok(insns) = h.cs.disasm_count(code, iaddr.into(), 1) {
|
||||
if insns.is_empty() {
|
||||
break;
|
||||
}
|
||||
@ -186,7 +186,8 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
iaddr += insn.bytes().len() as u64;
|
||||
iaddr += insn.bytes().len() as GuestAddr;
|
||||
|
||||
#[cfg(feature = "usermode")]
|
||||
unsafe {
|
||||
code = std::slice::from_raw_parts(emu.g2h(iaddr), 512);
|
||||
|
@ -9,6 +9,7 @@ use serde::{Deserialize, Serialize};
|
||||
use crate::{
|
||||
helper::{hash_me, QemuHelper, QemuHelperTuple, QemuInstrumentationFilter},
|
||||
hooks::QemuHooks,
|
||||
GuestAddr,
|
||||
};
|
||||
|
||||
#[derive(Debug, Default, Serialize, Deserialize)]
|
||||
@ -118,7 +119,7 @@ where
|
||||
pub fn gen_unique_cmp_ids<I, QT, S>(
|
||||
hooks: &mut QemuHooks<'_, I, QT, S>,
|
||||
state: Option<&mut S>,
|
||||
pc: u64,
|
||||
pc: GuestAddr,
|
||||
_size: usize,
|
||||
) -> Option<u64>
|
||||
where
|
||||
@ -127,7 +128,7 @@ where
|
||||
QT: QemuHelperTuple<I, S>,
|
||||
{
|
||||
if let Some(h) = hooks.match_helper_mut::<QemuCmpLogHelper>() {
|
||||
if !h.must_instrument(pc) {
|
||||
if !h.must_instrument(pc.into()) {
|
||||
return None;
|
||||
}
|
||||
}
|
||||
@ -141,7 +142,7 @@ where
|
||||
.unwrap();
|
||||
let id = meta.current_id as usize;
|
||||
|
||||
Some(*meta.map.entry(pc).or_insert_with(|| {
|
||||
Some(*meta.map.entry(pc.into()).or_insert_with(|| {
|
||||
meta.current_id = ((id + 1) & (CMPLOG_MAP_W - 1)) as u64;
|
||||
id as u64
|
||||
}))
|
||||
@ -150,7 +151,7 @@ where
|
||||
pub fn gen_hashed_cmp_ids<I, QT, S>(
|
||||
hooks: &mut QemuHooks<'_, I, QT, S>,
|
||||
_state: Option<&mut S>,
|
||||
pc: u64,
|
||||
pc: GuestAddr,
|
||||
_size: usize,
|
||||
) -> Option<u64>
|
||||
where
|
||||
@ -159,11 +160,11 @@ where
|
||||
QT: QemuHelperTuple<I, S>,
|
||||
{
|
||||
if let Some(h) = hooks.match_helper_mut::<QemuCmpLogChildHelper>() {
|
||||
if !h.must_instrument(pc) {
|
||||
if !h.must_instrument(pc.into()) {
|
||||
return None;
|
||||
}
|
||||
}
|
||||
Some(hash_me(pc) & (CMPLOG_MAP_W as u64 - 1))
|
||||
Some(hash_me(pc.into()) & (CMPLOG_MAP_W as u64 - 1))
|
||||
}
|
||||
|
||||
pub extern "C" fn trace_cmp1_cmplog(id: u64, v0: u8, v1: u8, _data: u64) {
|
||||
|
@ -164,7 +164,7 @@ where
|
||||
QT: QemuHelperTuple<I, S>,
|
||||
{
|
||||
if let Some(h) = hooks.helpers().match_first_type::<QemuEdgeCoverageHelper>() {
|
||||
if !h.must_instrument(src) && !h.must_instrument(dest) {
|
||||
if !h.must_instrument(src.into()) && !h.must_instrument(dest.into()) {
|
||||
return None;
|
||||
}
|
||||
}
|
||||
@ -224,7 +224,7 @@ where
|
||||
.helpers()
|
||||
.match_first_type::<QemuEdgeCoverageChildHelper>()
|
||||
{
|
||||
if !h.must_instrument(src) && !h.must_instrument(dest) {
|
||||
if !h.must_instrument(src.into()) && !h.must_instrument(dest.into()) {
|
||||
return None;
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user