diff --git a/.github/workflows/build_and_test.yml b/.github/workflows/build_and_test.yml index 62c155e265..489dd9ab5f 100644 --- a/.github/workflows/build_and_test.yml +++ b/.github/workflows/build_and_test.yml @@ -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 diff --git a/.gitignore b/.gitignore index 58fb8a1e6b..f43a82e582 100644 --- a/.gitignore +++ b/.gitignore @@ -7,6 +7,7 @@ vendor .env *.tmp +*.swp *.o *.a *.so diff --git a/fuzzers/qemu_arm_launcher/.gitignore b/fuzzers/qemu_arm_launcher/.gitignore new file mode 100644 index 0000000000..d69c2ab133 --- /dev/null +++ b/fuzzers/qemu_arm_launcher/.gitignore @@ -0,0 +1,6 @@ +libpng-* +libpng_harness +libpng_harness_crashing +zlib-* +crashes +target diff --git a/fuzzers/qemu_arm_launcher/Cargo.toml b/fuzzers/qemu_arm_launcher/Cargo.toml new file mode 100644 index 0000000000..2954c9176e --- /dev/null +++ b/fuzzers/qemu_arm_launcher/Cargo.toml @@ -0,0 +1,19 @@ +[package] +name = "qemu_arm_launcher" +version = "0.8.0" +authors = ["Andrea Fioraldi ", "Dominik Maier "] +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"] } diff --git a/fuzzers/qemu_arm_launcher/Makefile.toml b/fuzzers/qemu_arm_launcher/Makefile.toml new file mode 100644 index 0000000000..51b98678d1 --- /dev/null +++ b/fuzzers/qemu_arm_launcher/Makefile.toml @@ -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 +''' diff --git a/fuzzers/qemu_arm_launcher/README.md b/fuzzers/qemu_arm_launcher/README.md new file mode 100644 index 0000000000..8694fac552 --- /dev/null +++ b/fuzzers/qemu_arm_launcher/README.md @@ -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 +``` diff --git a/fuzzers/qemu_arm_launcher/corpus/not_kitty.png b/fuzzers/qemu_arm_launcher/corpus/not_kitty.png new file mode 100644 index 0000000000..eff7c1707b Binary files /dev/null and b/fuzzers/qemu_arm_launcher/corpus/not_kitty.png differ diff --git a/fuzzers/qemu_arm_launcher/corpus/not_kitty_alpha.png b/fuzzers/qemu_arm_launcher/corpus/not_kitty_alpha.png new file mode 100644 index 0000000000..2fb8da2c8f Binary files /dev/null and b/fuzzers/qemu_arm_launcher/corpus/not_kitty_alpha.png differ diff --git a/fuzzers/qemu_arm_launcher/corpus/not_kitty_gamma.png b/fuzzers/qemu_arm_launcher/corpus/not_kitty_gamma.png new file mode 100644 index 0000000000..939d9d29a9 Binary files /dev/null and b/fuzzers/qemu_arm_launcher/corpus/not_kitty_gamma.png differ diff --git a/fuzzers/qemu_arm_launcher/corpus/not_kitty_icc.png b/fuzzers/qemu_arm_launcher/corpus/not_kitty_icc.png new file mode 100644 index 0000000000..f0c7804d99 Binary files /dev/null and b/fuzzers/qemu_arm_launcher/corpus/not_kitty_icc.png differ diff --git a/fuzzers/qemu_arm_launcher/harness.cc b/fuzzers/qemu_arm_launcher/harness.cc new file mode 100644 index 0000000000..d9e149d5cb --- /dev/null +++ b/fuzzers/qemu_arm_launcher/harness.cc @@ -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 " 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 +#include +#include + +#include + +#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(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 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_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); +} diff --git a/fuzzers/qemu_arm_launcher/src/fuzzer.rs b/fuzzers/qemu_arm_launcher/src/fuzzer.rs new file mode 100644 index 0000000000..845a8e8be9 --- /dev/null +++ b/fuzzers/qemu_arm_launcher/src/fuzzer.rs @@ -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 = 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), + } +} diff --git a/fuzzers/qemu_arm_launcher/src/main.rs b/fuzzers/qemu_arm_launcher/src/main.rs new file mode 100644 index 0000000000..bc1e80f767 --- /dev/null +++ b/fuzzers/qemu_arm_launcher/src/main.rs @@ -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!"); +} diff --git a/libafl_qemu/build_linux.rs b/libafl_qemu/build_linux.rs index 8344da4184..de33e80a4e 100644 --- a/libafl_qemu/build_linux.rs +++ b/libafl_qemu/build_linux.rs @@ -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( diff --git a/libafl_qemu/src/asan.rs b/libafl_qemu/src/asan.rs index 7c4b2f6e40..a9ef619a55 100644 --- a/libafl_qemu/src/asan.rs +++ b/libafl_qemu/src/asan.rs @@ -446,7 +446,7 @@ where pub fn gen_readwrite_asan( hooks: &mut QemuHooks<'_, I, QT, S>, _state: Option<&mut S>, - pc: u64, + pc: GuestAddr, _size: usize, ) -> Option where @@ -454,13 +454,12 @@ where QT: QemuHelperTuple, { let h = hooks.match_helper_mut::().unwrap(); - if h.must_instrument(pc) { - Some(pc) + if h.must_instrument(pc.into()) { + Some(pc.into()) } else { None } } - pub fn trace_read1_asan( hooks: &mut QemuHooks<'_, I, QT, S>, _state: Option<&mut S>, diff --git a/libafl_qemu/src/calls.rs b/libafl_qemu/src/calls.rs index a0ec16dfa9..239e17e108 100644 --- a/libafl_qemu/src/calls.rs +++ b/libafl_qemu/src/calls.rs @@ -124,7 +124,7 @@ where { let emu = hooks.emulator(); if let Some(h) = hooks.helpers().match_first_type::() { - 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); diff --git a/libafl_qemu/src/cmplog.rs b/libafl_qemu/src/cmplog.rs index 70bd094029..84f36f3332 100644 --- a/libafl_qemu/src/cmplog.rs +++ b/libafl_qemu/src/cmplog.rs @@ -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( hooks: &mut QemuHooks<'_, I, QT, S>, state: Option<&mut S>, - pc: u64, + pc: GuestAddr, _size: usize, ) -> Option where @@ -127,7 +128,7 @@ where QT: QemuHelperTuple, { if let Some(h) = hooks.match_helper_mut::() { - 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( hooks: &mut QemuHooks<'_, I, QT, S>, _state: Option<&mut S>, - pc: u64, + pc: GuestAddr, _size: usize, ) -> Option where @@ -159,11 +160,11 @@ where QT: QemuHelperTuple, { if let Some(h) = hooks.match_helper_mut::() { - 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) { diff --git a/libafl_qemu/src/edges.rs b/libafl_qemu/src/edges.rs index 98a8832394..61c2c3bfd9 100644 --- a/libafl_qemu/src/edges.rs +++ b/libafl_qemu/src/edges.rs @@ -164,7 +164,7 @@ where QT: QemuHelperTuple, { if let Some(h) = hooks.helpers().match_first_type::() { - 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::() { - if !h.must_instrument(src) && !h.must_instrument(dest) { + if !h.must_instrument(src.into()) && !h.must_instrument(dest.into()) { return None; } }