Merge branch 'dev' of https://github.com/AFLplusplus/libAFLrs into dev
This commit is contained in:
commit
15955f0af9
21
.github/workflows/build_and_test.yml
vendored
21
.github/workflows/build_and_test.yml
vendored
@ -10,6 +10,27 @@ env:
|
||||
CARGO_TERM_COLOR: always
|
||||
|
||||
jobs:
|
||||
lint:
|
||||
strategy:
|
||||
matrix:
|
||||
os: [ubuntu-latest, windows-latest]
|
||||
runs-on: ${{ matrix.os }}
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: Cache cargo registry
|
||||
uses: actions/cache@v2
|
||||
with:
|
||||
path: |
|
||||
~/.cargo/registry
|
||||
~/.cargo/git
|
||||
key: clippy-cargo-${{ hashFiles('**/Cargo.toml') }}
|
||||
- name: Add clippy
|
||||
run: rustup component add clippy
|
||||
- name: Run clippy
|
||||
uses: actions-rs/cargo@v1
|
||||
with:
|
||||
command: clippy
|
||||
args: --all
|
||||
ubuntu:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
|
@ -1,4 +1,3 @@
|
||||
|
||||
[profile.release]
|
||||
lto = true
|
||||
codegen-units = 1
|
||||
@ -12,6 +11,7 @@ members = [
|
||||
|
||||
#example fuzzers
|
||||
"fuzzers/libfuzzer_libpng",
|
||||
"fuzzers/frida_libpng",
|
||||
"fuzzers/libfuzzer_libmozjpeg",
|
||||
"fuzzers/libfuzzer_libpng_cmpalloc",
|
||||
]
|
||||
|
1
fuzzers/frida_libpng/.gitignore
vendored
Normal file
1
fuzzers/frida_libpng/.gitignore
vendored
Normal file
@ -0,0 +1 @@
|
||||
libpng-*
|
39
fuzzers/frida_libpng/Cargo.toml
Normal file
39
fuzzers/frida_libpng/Cargo.toml
Normal file
@ -0,0 +1,39 @@
|
||||
[package]
|
||||
name = "frida_libpng"
|
||||
version = "0.1.0"
|
||||
authors = ["Andrea Fioraldi <andreafioraldi@gmail.com>", "Dominik Maier <domenukk@gmail.com>"]
|
||||
edition = "2018"
|
||||
build = "build.rs"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[features]
|
||||
default = ["std", "frida"]
|
||||
std = []
|
||||
frida = ["frida-gum", "frida-gum-sys"]
|
||||
|
||||
#[profile.release]
|
||||
#lto = true
|
||||
#codegen-units = 1
|
||||
#opt-level = 3
|
||||
#debug = true
|
||||
|
||||
[build-dependencies]
|
||||
cc = { version = "1.0", features = ["parallel"] }
|
||||
num_cpus = "1.0"
|
||||
|
||||
[dependencies]
|
||||
libafl = { path = "../../libafl/" }
|
||||
#frida-gum = { path = "../../../frida-rust/frida-gum", version = "0.2.3", optional = true, features = ["auto-download", "event-sink", "invocation-listener"] }
|
||||
#frida-gum-sys = { path = "../../../frida-rust/frida-gum-sys", version = "0.2.2", optional = true, features = ["auto-download", "event-sink", "invocation-listener"] }
|
||||
frida-gum = { version = "0.3.2", optional = true, features = ["auto-download", "event-sink", "invocation-listener"] }
|
||||
frida-gum-sys = { version = "0.2.2", optional = true, features = ["auto-download", "event-sink", "invocation-listener"] }
|
||||
lazy_static = "1.4.0"
|
||||
libc = "0.2"
|
||||
libloading = "0.7.0"
|
||||
|
||||
[[example]]
|
||||
name = "frida_libpng"
|
||||
path = "./src/fuzzer.rs"
|
||||
test = false
|
||||
bench = false
|
25
fuzzers/frida_libpng/README.md
Normal file
25
fuzzers/frida_libpng/README.md
Normal file
@ -0,0 +1,25 @@
|
||||
# Libfuzzer for libpng
|
||||
|
||||
This folder contains an example fuzzer for libpng, using LLMP for fast multi-process fuzzing and crash detection.
|
||||
To show off crash detection, we added a ud2 instruction to the harness, edit harness.cc if you want a non-crashing example.
|
||||
It has been tested on Linux.
|
||||
|
||||
## Build
|
||||
|
||||
To build this example, run `cargo build --example libfuzzer_libpng --release`.
|
||||
This will call (the build.rs)[./builld.rs], which in turn downloads a libpng archive from the web.
|
||||
Then, it will link (the fuzzer)[./src/fuzzer.rs] against (the C++ harness)[./harness.cc] and the instrumented `libpng`.
|
||||
Afterwards, the fuzzer will be ready to run, from `../../target/examples/libfuzzer_libpng`.
|
||||
|
||||
## Run
|
||||
|
||||
The first time you run the binary, the broker will open a tcp port (currently on port `1337`), waiting for fuzzer clients to connect. This port is local and only used for the initial handshake. All further communication happens via shared map, to be independent of the kernel.
|
||||
|
||||
Each following execution will run a fuzzer client.
|
||||
As this example uses in-process fuzzing, we added a Restarting Event Manager (`setup_restarting_mgr`).
|
||||
This means each client will start itself again to listen for crashes and timeouts.
|
||||
By restarting the actual fuzzer, it can recover from these exit conditions.
|
||||
|
||||
In any real-world scenario, you should use `taskset` to pin each client to an empty CPU core, the lib does not pick an empty core automatically (yet).
|
||||
|
||||
For convenience, you may just run `./test.sh` in this folder to test it.
|
114
fuzzers/frida_libpng/build.rs
Normal file
114
fuzzers/frida_libpng/build.rs
Normal file
@ -0,0 +1,114 @@
|
||||
// build.rs
|
||||
|
||||
use std::{
|
||||
env,
|
||||
path::Path,
|
||||
process::{exit, Command},
|
||||
};
|
||||
|
||||
const LIBPNG_URL: &str =
|
||||
"https://deac-fra.dl.sourceforge.net/project/libpng/libpng16/1.6.37/libpng-1.6.37.tar.xz";
|
||||
|
||||
fn main() {
|
||||
if cfg!(windows) {
|
||||
println!("cargo:warning=Skipping libpng example on Windows");
|
||||
exit(0);
|
||||
}
|
||||
|
||||
let out_dir = env::var_os("OUT_DIR").unwrap();
|
||||
let cwd = env::current_dir().unwrap().to_string_lossy().to_string();
|
||||
let out_dir = out_dir.to_string_lossy().to_string();
|
||||
let out_dir_path = Path::new(&out_dir);
|
||||
std::fs::create_dir_all(&out_dir).expect(&format!("Failed to create {}", &out_dir));
|
||||
|
||||
println!("cargo:rerun-if-changed=../libfuzzer_runtime/rt.c",);
|
||||
println!("cargo:rerun-if-changed=harness.cc");
|
||||
|
||||
let libpng = format!("{}/libpng-1.6.37", &out_dir);
|
||||
let libpng_path = Path::new(&libpng);
|
||||
let libpng_tar = format!("{}/libpng-1.6.37.tar.xz", &cwd);
|
||||
|
||||
// Enforce clang for its -fsanitize-coverage support.
|
||||
std::env::set_var("CC", "clang");
|
||||
std::env::set_var("CXX", "clang++");
|
||||
let ldflags = match env::var("LDFLAGS") {
|
||||
Ok(val) => val,
|
||||
Err(_) => "".to_string(),
|
||||
};
|
||||
|
||||
// println!("cargo:warning=output path is {}", libpng);
|
||||
if !libpng_path.is_dir() {
|
||||
if !Path::new(&libpng_tar).is_file() {
|
||||
println!("cargo:warning=Libpng not found, downloading...");
|
||||
// Download libpng
|
||||
Command::new("wget")
|
||||
.arg("-c")
|
||||
.arg(LIBPNG_URL)
|
||||
.arg("-O")
|
||||
.arg(&libpng_tar)
|
||||
.status()
|
||||
.unwrap();
|
||||
}
|
||||
Command::new("tar")
|
||||
.current_dir(&out_dir_path)
|
||||
.arg("xvf")
|
||||
.arg(&libpng_tar)
|
||||
.status()
|
||||
.unwrap();
|
||||
Command::new(format!("{}/configure", &libpng))
|
||||
.current_dir(&libpng_path)
|
||||
.args(&[
|
||||
"--disable-shared",
|
||||
&format!("--host={}", env::var("TARGET").unwrap())[..],
|
||||
])
|
||||
.env("CC", "clang")
|
||||
.env("CXX", "clang++")
|
||||
.env(
|
||||
"CFLAGS",
|
||||
"-O3 -g -D_DEFAULT_SOURCE -fPIC -fno-omit-frame-pointer",
|
||||
)
|
||||
.env(
|
||||
"CXXFLAGS",
|
||||
"-O3 -g -D_DEFAULT_SOURCE -fPIC -fno-omit-frame-pointer",
|
||||
)
|
||||
.env(
|
||||
"LDFLAGS",
|
||||
//format!("-g -fPIE -fsanitize=address {}", ldflags),
|
||||
format!("-g -fPIE {}", ldflags),
|
||||
)
|
||||
.status()
|
||||
.unwrap();
|
||||
Command::new("make")
|
||||
.current_dir(&libpng_path)
|
||||
.status()
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
let status = cc::Build::new()
|
||||
.cpp(true)
|
||||
.get_compiler()
|
||||
.to_command()
|
||||
.current_dir(&cwd)
|
||||
.arg("-I")
|
||||
.arg(format!("{}", &libpng))
|
||||
//.arg("-D")
|
||||
//.arg("HAS_DUMMY_CRASH=1")
|
||||
.arg("-fPIC")
|
||||
.arg("-shared")
|
||||
.arg(if env::var("CARGO_CFG_TARGET_OS").unwrap() == "android" {
|
||||
"-static-libstdc++"
|
||||
} else {
|
||||
""
|
||||
})
|
||||
.arg("-o")
|
||||
.arg(format!("{}/libpng-harness.so", &out_dir))
|
||||
.arg("./harness.cc")
|
||||
.arg(format!("{}/.libs/libpng16.a", &libpng))
|
||||
.arg("-l")
|
||||
.arg("z")
|
||||
.status()
|
||||
.unwrap();
|
||||
assert!(status.success());
|
||||
|
||||
println!("cargo:rerun-if-changed=build.rs");
|
||||
}
|
201
fuzzers/frida_libpng/harness.cc
Normal file
201
fuzzers/frida_libpng/harness.cc
Normal file
@ -0,0 +1,201 @@
|
||||
// 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;
|
||||
|
||||
extern "C" int afl_libfuzzer_init() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Entry point for LibFuzzer.
|
||||
// Roughly follows the libpng book example:
|
||||
// http://www.libpng.org/pub/png/book/chapter13.html
|
||||
extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
|
||||
if (size < kPngHeaderSize) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
std::vector<unsigned char> v(data, data + size);
|
||||
if (png_sig_cmp(v.data(), 0, kPngHeaderSize)) {
|
||||
// not a PNG.
|
||||
return 0;
|
||||
}
|
||||
|
||||
PngObjectHandler png_handler;
|
||||
png_handler.png_ptr = nullptr;
|
||||
png_handler.row_ptr = nullptr;
|
||||
png_handler.info_ptr = nullptr;
|
||||
png_handler.end_info_ptr = nullptr;
|
||||
|
||||
png_handler.png_ptr = png_create_read_struct
|
||||
(PNG_LIBPNG_VER_STRING, nullptr, nullptr, nullptr);
|
||||
if (!png_handler.png_ptr) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
png_handler.info_ptr = png_create_info_struct(png_handler.png_ptr);
|
||||
if (!png_handler.info_ptr) {
|
||||
PNG_CLEANUP
|
||||
return 0;
|
||||
}
|
||||
|
||||
png_handler.end_info_ptr = png_create_info_struct(png_handler.png_ptr);
|
||||
if (!png_handler.end_info_ptr) {
|
||||
PNG_CLEANUP
|
||||
return 0;
|
||||
}
|
||||
|
||||
png_set_crc_action(png_handler.png_ptr, PNG_CRC_QUIET_USE, PNG_CRC_QUIET_USE);
|
||||
#ifdef PNG_IGNORE_ADLER32
|
||||
png_set_option(png_handler.png_ptr, PNG_IGNORE_ADLER32, PNG_OPTION_ON);
|
||||
#endif
|
||||
|
||||
// Setting up reading from buffer.
|
||||
png_handler.buf_state = new BufState();
|
||||
png_handler.buf_state->data = data + kPngHeaderSize;
|
||||
png_handler.buf_state->bytes_left = size - kPngHeaderSize;
|
||||
png_set_read_fn(png_handler.png_ptr, png_handler.buf_state, user_read_data);
|
||||
png_set_sig_bytes(png_handler.png_ptr, kPngHeaderSize);
|
||||
|
||||
if (setjmp(png_jmpbuf(png_handler.png_ptr))) {
|
||||
PNG_CLEANUP
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Reading.
|
||||
png_read_info(png_handler.png_ptr, png_handler.info_ptr);
|
||||
|
||||
// reset error handler to put png_deleter into scope.
|
||||
if (setjmp(png_jmpbuf(png_handler.png_ptr))) {
|
||||
PNG_CLEANUP
|
||||
return 0;
|
||||
}
|
||||
|
||||
png_uint_32 width, height;
|
||||
int bit_depth, color_type, interlace_type, compression_type;
|
||||
int filter_type;
|
||||
|
||||
if (!png_get_IHDR(png_handler.png_ptr, png_handler.info_ptr, &width,
|
||||
&height, &bit_depth, &color_type, &interlace_type,
|
||||
&compression_type, &filter_type)) {
|
||||
PNG_CLEANUP
|
||||
return 0;
|
||||
}
|
||||
|
||||
// This is going to be too slow.
|
||||
if (width && height > 100000000 / width) {
|
||||
PNG_CLEANUP
|
||||
#ifdef HAS_DUMMY_CRASH
|
||||
#ifdef __aarch64__
|
||||
asm volatile (".word 0xf7f0a000\n");
|
||||
#else
|
||||
asm("ud2");
|
||||
#endif
|
||||
#endif
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Set several transforms that browsers typically use:
|
||||
png_set_gray_to_rgb(png_handler.png_ptr);
|
||||
png_set_expand(png_handler.png_ptr);
|
||||
png_set_packing(png_handler.png_ptr);
|
||||
png_set_scale_16(png_handler.png_ptr);
|
||||
png_set_tRNS_to_alpha(png_handler.png_ptr);
|
||||
|
||||
int passes = png_set_interlace_handling(png_handler.png_ptr);
|
||||
|
||||
png_read_update_info(png_handler.png_ptr, png_handler.info_ptr);
|
||||
|
||||
png_handler.row_ptr = png_malloc(
|
||||
png_handler.png_ptr, png_get_rowbytes(png_handler.png_ptr,
|
||||
png_handler.info_ptr));
|
||||
|
||||
for (int pass = 0; pass < passes; ++pass) {
|
||||
for (png_uint_32 y = 0; y < height; ++y) {
|
||||
png_read_row(png_handler.png_ptr,
|
||||
static_cast<png_bytep>(png_handler.row_ptr), nullptr);
|
||||
}
|
||||
}
|
||||
|
||||
png_read_end(png_handler.png_ptr, png_handler.end_info_ptr);
|
||||
|
||||
PNG_CLEANUP
|
||||
return 0;
|
||||
}
|
||||
|
524
fuzzers/frida_libpng/src/fuzzer.rs
Normal file
524
fuzzers/frida_libpng/src/fuzzer.rs
Normal file
@ -0,0 +1,524 @@
|
||||
//! A libfuzzer-like fuzzer with llmp-multithreading support and restarts
|
||||
//! The example harness is built for libpng.
|
||||
|
||||
use libafl::{
|
||||
bolts::{
|
||||
shmem::UnixShMem,
|
||||
tuples::{tuple_list, Named},
|
||||
},
|
||||
corpus::{
|
||||
Corpus, InMemoryCorpus, IndexesLenTimeMinimizerCorpusScheduler, OnDiskCorpus,
|
||||
QueueCorpusScheduler,
|
||||
},
|
||||
events::{setup_restarting_mgr, EventManager},
|
||||
executors::{inprocess::InProcessExecutor, Executor, ExitKind, HasObservers},
|
||||
feedbacks::{CrashFeedback, MaxMapFeedback},
|
||||
fuzzer::{Fuzzer, HasCorpusScheduler, StdFuzzer},
|
||||
inputs::{HasTargetBytes, Input},
|
||||
mutators::{scheduled::HavocBytesMutator, token_mutations::Tokens},
|
||||
observers::{HitcountsMapObserver, ObserversTuple, StdMapObserver},
|
||||
stages::mutational::StdMutationalStage,
|
||||
state::{HasCorpus, HasMetadata, State},
|
||||
stats::SimpleStats,
|
||||
utils::{current_nanos, StdRand},
|
||||
Error,
|
||||
};
|
||||
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
use frida_gum::instruction_writer::X86Register;
|
||||
#[cfg(target_arch = "aarch64")]
|
||||
use frida_gum::instruction_writer::{Aarch64Register, IndexMode};
|
||||
use frida_gum::{
|
||||
instruction_writer::InstructionWriter,
|
||||
stalker::{NoneEventSink, Stalker, Transformer},
|
||||
};
|
||||
use frida_gum::{Gum, MemoryRange, Module, NativePointer, PageProtection};
|
||||
|
||||
use libloading;
|
||||
|
||||
use std::{cell::RefCell, env, ffi::c_void, path::PathBuf};
|
||||
|
||||
/// An helper that feeds FridaInProcessExecutor with user-supplied instrumentation
|
||||
pub trait FridaHelper<'a> {
|
||||
fn transformer(&self) -> &Transformer<'a>;
|
||||
}
|
||||
|
||||
const MAP_SIZE: usize = 64 * 1024;
|
||||
|
||||
/// An helper that feeds FridaInProcessExecutor with edge-coverage instrumentation
|
||||
struct FridaEdgeCoverageHelper<'a> {
|
||||
map: [u8; MAP_SIZE],
|
||||
previous_pc: RefCell<u64>,
|
||||
base_address: u64,
|
||||
size: usize,
|
||||
current_log_impl: u64,
|
||||
/// Transformer that has to be passed to FridaInProcessExecutor
|
||||
transformer: Option<Transformer<'a>>,
|
||||
}
|
||||
|
||||
impl<'a> FridaHelper<'a> for FridaEdgeCoverageHelper<'a> {
|
||||
fn transformer(&self) -> &Transformer<'a> {
|
||||
self.transformer.as_ref().unwrap()
|
||||
}
|
||||
}
|
||||
|
||||
/// Helper function to get the size of a module's CODE section from frida
|
||||
pub fn get_module_size(module_name: &str) -> usize {
|
||||
let mut code_size = 0;
|
||||
let code_size_ref = &mut code_size;
|
||||
Module::enumerate_ranges(module_name, PageProtection::ReadExecute, move |details| {
|
||||
*code_size_ref = details.memory_range().size() as usize;
|
||||
true
|
||||
});
|
||||
|
||||
code_size
|
||||
}
|
||||
|
||||
/// A minimal maybe_log implementation. We insert this into the transformed instruction stream
|
||||
/// every time we need a copy that is within a direct branch of the start of the transformed basic
|
||||
/// block.
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
const MAYBE_LOG_CODE: [u8; 69] = [
|
||||
0x9c, // pushfq
|
||||
0x50, // push rax
|
||||
0x51, // push rcx
|
||||
0x52, // push rdx
|
||||
0x56, // push rsi
|
||||
0x89, 0xf8, // mov eax, edi
|
||||
0xc1, 0xe0, 0x08, // shl eax, 8
|
||||
0xc1, 0xef, 0x04, // shr edi, 4
|
||||
0x31, 0xc7, // xor edi, eax
|
||||
0x0f, 0xb7, 0xc7, // movzx eax, di
|
||||
0x48, 0x8d, 0x0d, 0x34, 0x00, 0x00, 0x00, // lea rcx, sym._afl_area_ptr_ptr
|
||||
0x48, 0x8b, 0x09, // mov rcx, qword [rcx]
|
||||
0x48, 0x8d, 0x15, 0x22, 0x00, 0x00, 0x00, // lea rdx, sym._afl_prev_loc_ptr
|
||||
0x48, 0x8b, 0x32, // mov rsi, qword [rdx]
|
||||
0x48, 0x8b, 0x36, // mov rsi, qword [rsi]
|
||||
0x48, 0x31, 0xc6, // xor rsi, rax
|
||||
0x48, 0x81, 0xe6, 0xff, 0x1f, 0x00,
|
||||
0x00, // and rsi, 0x1fff (8 * 1024 - 1) TODO: make this variable
|
||||
0xfe, 0x04, 0x31, // inc byte [rcx + rsi]
|
||||
0x48, 0xd1, 0xe8, // shr rax, 1
|
||||
0x48, 0x8b, 0x0a, // mov rcx, qword [rdx]
|
||||
0x48, 0x89, 0x01, // mov qword [rcx], rax
|
||||
0x5e, // pop rsi
|
||||
0x5a, // pop rdx
|
||||
0x59, // pop rcx
|
||||
0x58, // pop rax
|
||||
0x9d, // popfq
|
||||
0xc3, // ret
|
||||
// Read-only data goes here:
|
||||
// uint64_t* afl_prev_loc_ptr
|
||||
// uint8_t** afl_area_ptr_ptr
|
||||
// unsigned int afl_instr_rms
|
||||
];
|
||||
|
||||
#[cfg(target_arch = "aarch64")]
|
||||
const MAYBE_LOG_CODE: [u8; 104] = [
|
||||
0xE1, 0x0B, 0xBF, 0xA9, // stp x1, x2, [sp, -0x10]!
|
||||
0xE3, 0x13, 0xBF, 0xA9, // stp x3, x4, [sp, -0x10]!
|
||||
0xE1, 0x03, 0x00, 0xAA, // mov x1, x0
|
||||
0x00, 0xDC, 0x78, 0xD3, // lsl x0, x0, #8
|
||||
0x21, 0xFC, 0x44, 0xD3, // lsr x1, x1, #4
|
||||
0x00, 0x00, 0x01, 0xCA, // eor x0, x0, x1
|
||||
0x00, 0x3C, 0x00, 0x53, // uxth w0, w0
|
||||
0xa1, 0x02, 0x00, 0x58, // ldr x1, =area_ptr
|
||||
0x42, 0x02, 0x00, 0x58, // ldr x2, =pc_ptr
|
||||
0x43, 0x00, 0x40, 0xF9, // ldr x3, [x2]
|
||||
0x63, 0x00, 0x00, 0xCA, // eor x3, x3, x0
|
||||
0x63, 0x40, 0x40, 0x92, // and x3, x3, #0x1ffff
|
||||
0x21, 0x00, 0x03, 0x8B, // add x1, x1, x3
|
||||
0x24, 0x00, 0x40, 0x39, // ldrb w4, [x1, #0
|
||||
0x84, 0x04, 0x00, 0x91, // add x4, x4, #1
|
||||
0x24, 0x00, 0x00, 0x39, // strb w4, [x1, #0]
|
||||
0x00, 0xFC, 0x41, 0xD3, // lsr x0, x0, #1
|
||||
0x40, 0x00, 0x00, 0xF9, // str x0, [x2]
|
||||
0xE3, 0x13, 0xc1, 0xA8, // ldp x3, x4, [sp], #0x10
|
||||
0xE1, 0x0B, 0xc1, 0xA8, // ldp x1, x2, [sp], #0x10
|
||||
0xC0, 0x03, 0x5F, 0xD6, // ret
|
||||
0x1f, 0x20, 0x03, 0xD5, // nop
|
||||
0x1f, 0x20, 0x03, 0xD5, // nop
|
||||
0x1f, 0x20, 0x03, 0xD5, // nop
|
||||
0x1f, 0x20, 0x03, 0xD5, // nop
|
||||
0x1f, 0x20, 0x03, 0xD5, // nop
|
||||
];
|
||||
|
||||
/// The implementation of the FridaEdgeCoverageHelper
|
||||
impl<'a> FridaEdgeCoverageHelper<'a> {
|
||||
/// Constructor function to create a new FridaEdgeCoverageHelper, given a module_name.
|
||||
pub fn new(gum: &'a Gum, module_name: &str) -> Self {
|
||||
let mut helper = Self {
|
||||
map: [0u8; MAP_SIZE],
|
||||
previous_pc: RefCell::new(0x0),
|
||||
base_address: Module::find_base_address(module_name).0 as u64,
|
||||
size: get_module_size(module_name),
|
||||
current_log_impl: 0,
|
||||
transformer: None,
|
||||
};
|
||||
|
||||
let transformer = Transformer::from_callback(gum, |basic_block, _output| {
|
||||
let mut first = true;
|
||||
for instruction in basic_block {
|
||||
if first {
|
||||
first = false;
|
||||
let address = unsafe { (*instruction.instr()).address };
|
||||
if address >= helper.base_address
|
||||
&& address <= helper.base_address + helper.size as u64
|
||||
{
|
||||
let writer = _output.writer();
|
||||
if helper.current_log_impl == 0
|
||||
|| !writer.can_branch_directly_to(helper.current_log_impl)
|
||||
|| !writer.can_branch_directly_between(
|
||||
writer.pc() + 128,
|
||||
helper.current_log_impl,
|
||||
)
|
||||
{
|
||||
let after_log_impl = writer.code_offset() + 1;
|
||||
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
writer.put_jmp_near_label(after_log_impl);
|
||||
#[cfg(target_arch = "aarch64")]
|
||||
writer.put_b_label(after_log_impl);
|
||||
|
||||
helper.current_log_impl = writer.pc();
|
||||
writer.put_bytes(&MAYBE_LOG_CODE);
|
||||
let prev_loc_pointer = helper.previous_pc.as_ptr() as *mut _ as usize;
|
||||
let map_pointer = helper.map.as_ptr() as usize;
|
||||
|
||||
writer.put_bytes(&prev_loc_pointer.to_ne_bytes());
|
||||
writer.put_bytes(&map_pointer.to_ne_bytes());
|
||||
|
||||
writer.put_label(after_log_impl);
|
||||
}
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
{
|
||||
println!("here");
|
||||
writer.put_lea_reg_reg_offset(
|
||||
X86Register::Rsp,
|
||||
X86Register::Rsp,
|
||||
-(frida_gum_sys::GUM_RED_ZONE_SIZE as i32),
|
||||
);
|
||||
writer.put_push_reg(X86Register::Rdi);
|
||||
writer.put_mov_reg_address(X86Register::Rdi, address);
|
||||
writer.put_call_address(helper.current_log_impl);
|
||||
writer.put_pop_reg(X86Register::Rdi);
|
||||
writer.put_lea_reg_reg_offset(
|
||||
X86Register::Rsp,
|
||||
X86Register::Rsp,
|
||||
frida_gum_sys::GUM_RED_ZONE_SIZE as i32,
|
||||
);
|
||||
}
|
||||
#[cfg(target_arch = "aarch64")]
|
||||
{
|
||||
writer.put_stp_reg_reg_reg_offset(
|
||||
Aarch64Register::Lr,
|
||||
Aarch64Register::X0,
|
||||
Aarch64Register::Sp,
|
||||
-(16 + frida_gum_sys::GUM_RED_ZONE_SIZE as i32) as i64,
|
||||
IndexMode::PreAdjust,
|
||||
);
|
||||
writer.put_ldr_reg_u64(Aarch64Register::X0, address);
|
||||
writer.put_bl_imm(helper.current_log_impl);
|
||||
writer.put_ldp_reg_reg_reg_offset(
|
||||
Aarch64Register::Lr,
|
||||
Aarch64Register::X0,
|
||||
Aarch64Register::Sp,
|
||||
16 + frida_gum_sys::GUM_RED_ZONE_SIZE as i64,
|
||||
IndexMode::PostAdjust,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
instruction.keep()
|
||||
}
|
||||
});
|
||||
|
||||
helper.transformer = Some(transformer);
|
||||
helper
|
||||
}
|
||||
}
|
||||
|
||||
struct FridaInProcessExecutor<'a, FH, H, I, OT>
|
||||
where
|
||||
FH: FridaHelper<'a>,
|
||||
H: FnMut(&[u8]) -> ExitKind,
|
||||
I: Input + HasTargetBytes,
|
||||
OT: ObserversTuple,
|
||||
{
|
||||
base: InProcessExecutor<'a, H, I, OT>,
|
||||
/// Frida's dynamic rewriting engine
|
||||
stalker: Stalker<'a>,
|
||||
/// User provided callback for instrumentation
|
||||
helper: &'a FH,
|
||||
followed: bool,
|
||||
}
|
||||
|
||||
impl<'a, FH, H, I, OT> Executor<I> for FridaInProcessExecutor<'a, FH, H, I, OT>
|
||||
where
|
||||
FH: FridaHelper<'a>,
|
||||
H: FnMut(&[u8]) -> ExitKind,
|
||||
I: Input + HasTargetBytes,
|
||||
OT: ObserversTuple,
|
||||
{
|
||||
/// Called right before exexution starts
|
||||
#[inline]
|
||||
fn pre_exec<EM, S>(&mut self, state: &mut S, event_mgr: &mut EM, input: &I) -> Result<(), Error>
|
||||
where
|
||||
EM: EventManager<I, S>,
|
||||
{
|
||||
if !self.followed {
|
||||
self.followed = true;
|
||||
self.stalker
|
||||
.follow_me::<NoneEventSink>(self.helper.transformer(), None);
|
||||
} else {
|
||||
self.stalker.activate(NativePointer(
|
||||
self.base.harness_mut() as *mut _ as *mut c_void
|
||||
))
|
||||
}
|
||||
self.base.pre_exec(state, event_mgr, input)
|
||||
}
|
||||
|
||||
/// Instruct the target about the input and run
|
||||
#[inline]
|
||||
fn run_target(&mut self, input: &I) -> Result<ExitKind, Error> {
|
||||
self.base.run_target(input)
|
||||
}
|
||||
|
||||
/// Called right after execution finished.
|
||||
#[inline]
|
||||
fn post_exec<EM, S>(
|
||||
&mut self,
|
||||
state: &mut S,
|
||||
event_mgr: &mut EM,
|
||||
input: &I,
|
||||
) -> Result<(), Error>
|
||||
where
|
||||
EM: EventManager<I, S>,
|
||||
{
|
||||
self.stalker.deactivate();
|
||||
self.base.post_exec(state, event_mgr, input)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, FH, H, I, OT> HasObservers<OT> for FridaInProcessExecutor<'a, FH, H, I, OT>
|
||||
where
|
||||
FH: FridaHelper<'a>,
|
||||
H: FnMut(&[u8]) -> ExitKind,
|
||||
I: Input + HasTargetBytes,
|
||||
OT: ObserversTuple,
|
||||
{
|
||||
#[inline]
|
||||
fn observers(&self) -> &OT {
|
||||
self.base.observers()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn observers_mut(&mut self) -> &mut OT {
|
||||
self.base.observers_mut()
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, FH, H, I, OT> Named for FridaInProcessExecutor<'a, FH, H, I, OT>
|
||||
where
|
||||
FH: FridaHelper<'a>,
|
||||
H: FnMut(&[u8]) -> ExitKind,
|
||||
I: Input + HasTargetBytes,
|
||||
OT: ObserversTuple,
|
||||
{
|
||||
fn name(&self) -> &str {
|
||||
self.base.name()
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, FH, H, I, OT> FridaInProcessExecutor<'a, FH, H, I, OT>
|
||||
where
|
||||
FH: FridaHelper<'a>,
|
||||
H: FnMut(&[u8]) -> ExitKind,
|
||||
I: Input + HasTargetBytes,
|
||||
OT: ObserversTuple,
|
||||
{
|
||||
pub fn new(gum: &'a Gum, base: InProcessExecutor<'a, H, I, OT>, helper: &'a FH) -> Self {
|
||||
let mut stalker = Stalker::new(gum);
|
||||
|
||||
// Let's exclude the main module and libc.so at least:
|
||||
stalker.exclude(&MemoryRange::new(
|
||||
Module::find_base_address(&env::args().next().unwrap()),
|
||||
get_module_size(&env::args().next().unwrap()),
|
||||
));
|
||||
stalker.exclude(&MemoryRange::new(
|
||||
Module::find_base_address("libc.so"),
|
||||
get_module_size("libc.so"),
|
||||
));
|
||||
|
||||
Self {
|
||||
base: base,
|
||||
stalker: stalker,
|
||||
helper: helper,
|
||||
followed: false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// The main fn, usually parsing parameters, and starting the fuzzer
|
||||
pub fn main() {
|
||||
// Registry the metadata types used in this fuzzer
|
||||
// Needed only on no_std
|
||||
//RegistryBuilder::register::<Tokens>();
|
||||
|
||||
println!(
|
||||
"Workdir: {:?}",
|
||||
env::current_dir().unwrap().to_string_lossy().to_string()
|
||||
);
|
||||
unsafe {
|
||||
fuzz(
|
||||
&env::args().nth(1).expect("no module specified"),
|
||||
&env::args().nth(2).expect("no symbol specified"),
|
||||
vec![PathBuf::from("./corpus")],
|
||||
PathBuf::from("./crashes"),
|
||||
1337,
|
||||
)
|
||||
.expect("An error occurred while fuzzing");
|
||||
}
|
||||
}
|
||||
|
||||
/// Not supported on windows right now
|
||||
#[cfg(windows)]
|
||||
fn fuzz(
|
||||
_module_name: &str,
|
||||
_symbol_name: &str,
|
||||
_corpus_dirs: Vec<PathBuf>,
|
||||
_objective_dir: PathBuf,
|
||||
_broker_port: u16,
|
||||
) -> Result<(), ()> {
|
||||
todo!("Example not supported on Windows");
|
||||
}
|
||||
|
||||
/// The actual fuzzer
|
||||
#[cfg(unix)]
|
||||
unsafe fn fuzz(
|
||||
module_name: &str,
|
||||
symbol_name: &str,
|
||||
corpus_dirs: Vec<PathBuf>,
|
||||
objective_dir: PathBuf,
|
||||
broker_port: u16,
|
||||
) -> Result<(), Error> {
|
||||
// 'While the stats are state, they are usually used in the broker - which is likely never restarted
|
||||
let stats = SimpleStats::new(|s| println!("{}", s));
|
||||
|
||||
// The restarting state will spawn the same process again as child, then restarted it each time it crashes.
|
||||
let (state, mut restarting_mgr) =
|
||||
match setup_restarting_mgr::<_, _, UnixShMem, _>(stats, broker_port) {
|
||||
Ok(res) => res,
|
||||
Err(err) => match err {
|
||||
Error::ShuttingDown => {
|
||||
return Ok(());
|
||||
}
|
||||
_ => {
|
||||
panic!("Failed to setup the restarter: {}", err);
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
let gum = Gum::obtain();
|
||||
let lib = libloading::Library::new(module_name).unwrap();
|
||||
let target_func: libloading::Symbol<unsafe extern "C" fn(data: *const u8, size: usize) -> i32> =
|
||||
lib.get(symbol_name.as_bytes()).unwrap();
|
||||
let mut frida_helper = FridaEdgeCoverageHelper::new(&gum, module_name);
|
||||
|
||||
// Create an observation channel using the coverage map
|
||||
let edges_observer = HitcountsMapObserver::new(StdMapObserver::new_from_ptr(
|
||||
"edges",
|
||||
frida_helper.map.as_mut_ptr(),
|
||||
MAP_SIZE,
|
||||
));
|
||||
|
||||
let mut frida_harness = move |buf: &[u8]| {
|
||||
(target_func)(buf.as_ptr(), buf.len());
|
||||
ExitKind::Ok
|
||||
};
|
||||
|
||||
// If not restarting, create a State from scratch
|
||||
let mut state = state.unwrap_or_else(|| {
|
||||
State::new(
|
||||
// RNG
|
||||
StdRand::with_seed(current_nanos()),
|
||||
// Corpus that will be evolved, we keep it in memory for performance
|
||||
InMemoryCorpus::new(),
|
||||
// Feedbacks to rate the interestingness of an input
|
||||
tuple_list!(MaxMapFeedback::new_with_observer_track(
|
||||
&edges_observer,
|
||||
true,
|
||||
false
|
||||
)),
|
||||
// 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).unwrap(),
|
||||
// Feedbacks to recognize an input as solution
|
||||
tuple_list!(CrashFeedback::new()),
|
||||
)
|
||||
});
|
||||
|
||||
println!("We're a client, let's fuzz :)");
|
||||
|
||||
// Create a PNG dictionary if not existing
|
||||
if state.metadata().get::<Tokens>().is_none() {
|
||||
state.add_metadata(Tokens::new(vec![
|
||||
vec![137, 80, 78, 71, 13, 10, 26, 10], // PNG header
|
||||
"IHDR".as_bytes().to_vec(),
|
||||
"IDAT".as_bytes().to_vec(),
|
||||
"PLTE".as_bytes().to_vec(),
|
||||
"IEND".as_bytes().to_vec(),
|
||||
]));
|
||||
}
|
||||
|
||||
// Setup a basic mutator with a mutational stage
|
||||
let mutator = HavocBytesMutator::default();
|
||||
let stage = StdMutationalStage::new(mutator);
|
||||
|
||||
// A fuzzer with just one stage and a minimization+queue policy to get testcasess from the corpus
|
||||
let scheduler = IndexesLenTimeMinimizerCorpusScheduler::new(QueueCorpusScheduler::new());
|
||||
let fuzzer = StdFuzzer::new(scheduler, tuple_list!(stage));
|
||||
|
||||
// Create the executor for an in-process function with just one observer for edge coverage
|
||||
let mut executor = FridaInProcessExecutor::new(
|
||||
&gum,
|
||||
InProcessExecutor::new(
|
||||
"in-process(edges)",
|
||||
&mut frida_harness,
|
||||
tuple_list!(edges_observer),
|
||||
&mut state,
|
||||
&mut restarting_mgr,
|
||||
)?,
|
||||
&frida_helper,
|
||||
);
|
||||
// Let's exclude the main module and libc.so at least:
|
||||
executor.stalker.exclude(&MemoryRange::new(
|
||||
Module::find_base_address(&env::args().next().unwrap()),
|
||||
get_module_size(&env::args().next().unwrap()),
|
||||
));
|
||||
executor.stalker.exclude(&MemoryRange::new(
|
||||
Module::find_base_address("libc.so"),
|
||||
get_module_size("libc.so"),
|
||||
));
|
||||
|
||||
// In case the corpus is empty (on first run), reset
|
||||
if state.corpus().count() < 1 {
|
||||
state
|
||||
.load_initial_inputs(
|
||||
&mut executor,
|
||||
&mut restarting_mgr,
|
||||
fuzzer.scheduler(),
|
||||
&corpus_dirs,
|
||||
)
|
||||
.expect(&format!(
|
||||
"Failed to load initial corpus at {:?}",
|
||||
&corpus_dirs
|
||||
));
|
||||
println!("We imported {} inputs from disk.", state.corpus().count());
|
||||
}
|
||||
|
||||
fuzzer.fuzz_loop(&mut state, &mut executor, &mut restarting_mgr)?;
|
||||
|
||||
// Never reached
|
||||
Ok(())
|
||||
}
|
@ -15,7 +15,7 @@ fn main() {
|
||||
}
|
||||
|
||||
let out_dir = env::var_os("OUT_DIR").unwrap();
|
||||
let cwd = env::current_dir().unwrap().to_string_lossy().to_string();
|
||||
//let cwd = env::current_dir().unwrap().to_string_lossy().to_string();
|
||||
let out_dir = out_dir.to_string_lossy().to_string();
|
||||
let out_dir_path = Path::new(&out_dir);
|
||||
|
||||
@ -24,7 +24,7 @@ fn main() {
|
||||
|
||||
let libmozjpeg = format!("{}/mozjpeg-4.0.3", &out_dir);
|
||||
let libmozjpeg_path = Path::new(&libmozjpeg);
|
||||
let libmozjpeg_tar = format!("{}/v4.0.3.tar.gz", &cwd);
|
||||
let libmozjpeg_tar = format!("{}/v4.0.3.tar.gz", &out_dir);
|
||||
|
||||
// Enforce clang for its -fsanitize-coverage support.
|
||||
std::env::set_var("CC", "clang");
|
||||
@ -48,16 +48,13 @@ fn main() {
|
||||
.arg(&libmozjpeg_tar)
|
||||
.status()
|
||||
.unwrap();
|
||||
Command::new(format!("{}/cmake", &libmozjpeg))
|
||||
.current_dir(&out_dir_path)
|
||||
.args(&[
|
||||
"-G\"Unix Makefiles\"",
|
||||
"--disable-shared",
|
||||
&libmozjpeg,
|
||||
"CC=clang",
|
||||
"CFLAGS=-O3 -g -D_DEFAULT_SOURCE -fPIE -fsanitize-coverage=trace-pc-guard",
|
||||
"LDFLAGS=-g -fPIE -fsanitize-coverage=trace-pc-guard",
|
||||
])
|
||||
//println!("cargo:warning=Running cmake on {}", &libmozjpeg);
|
||||
|
||||
Command::new("cmake")
|
||||
.current_dir(&libmozjpeg_path)
|
||||
.args(&["-G", "Unix Makefiles", "--disable-shared"])
|
||||
.arg(&libmozjpeg)
|
||||
.env("OPT_LEVEL", "3")
|
||||
.env("CC", "clang")
|
||||
.env("CXX", "clang++")
|
||||
.env(
|
||||
@ -68,13 +65,17 @@ fn main() {
|
||||
"CXXFLAGS",
|
||||
"-O3 -g -D_DEFAULT_SOURCE -fPIE -fsanitize-coverage=trace-pc-guard",
|
||||
)
|
||||
.env("LDFLAGS", "-g -fPIE -fsanitize-coverage=trace-pc-guard");
|
||||
.env("LDFLAGS", "-g -fPIE -fsanitize-coverage=trace-pc-guard")
|
||||
.status()
|
||||
.unwrap();
|
||||
|
||||
Command::new("make")
|
||||
.current_dir(&libmozjpeg_path)
|
||||
//.arg(&format!("-j{}", num_cpus::get()))
|
||||
.args(&[
|
||||
"CC=clang",
|
||||
"CXX=clang++",
|
||||
"OPT_LEVEL=3",
|
||||
"CFLAGS=-O3 -g -D_DEFAULT_SOURCE -fPIE -fsanitize-coverage=trace-pc-guard",
|
||||
"LDFLAGS=-g -fPIE -fsanitize-coverage=trace-pc-guard",
|
||||
"CXXFLAGS=-D_DEFAULT_SOURCE -fPIE -fsanitize-coverage=trace-pc-guard",
|
||||
|
@ -8,10 +8,9 @@ use libafl::{
|
||||
bolts::{shmem::UnixShMem, tuples::tuple_list},
|
||||
corpus::{Corpus, InMemoryCorpus, OnDiskCorpus, RandCorpusScheduler},
|
||||
events::setup_restarting_mgr,
|
||||
executors::{inprocess::InProcessExecutor, Executor, ExitKind},
|
||||
executors::{inprocess::InProcessExecutor, ExitKind},
|
||||
feedbacks::{CrashFeedback, MaxMapFeedback},
|
||||
fuzzer::{Fuzzer, HasCorpusScheduler, StdFuzzer},
|
||||
inputs::Input,
|
||||
mutators::scheduled::HavocBytesMutator,
|
||||
mutators::token_mutations::Tokens,
|
||||
observers::StdMapObserver,
|
||||
@ -36,20 +35,6 @@ extern "C" {
|
||||
static __lafl_max_edges_size: u32;
|
||||
}
|
||||
|
||||
/// The wrapped harness function, calling out to the LLVM-style harness
|
||||
#[cfg(unix)]
|
||||
fn harness<E, I>(_executor: &E, buf: &[u8]) -> ExitKind
|
||||
where
|
||||
E: Executor<I>,
|
||||
I: Input,
|
||||
{
|
||||
// println!("{:?}", buf);
|
||||
unsafe {
|
||||
LLVMFuzzerTestOneInput(buf.as_ptr(), buf.len());
|
||||
}
|
||||
ExitKind::Ok
|
||||
}
|
||||
|
||||
/// The main fn, usually parsing parameters, and starting the fuzzer
|
||||
pub fn main() {
|
||||
// Registry the metadata types used in this fuzzer
|
||||
@ -121,10 +106,16 @@ fn fuzz(corpus_dirs: Vec<PathBuf>, objective_dir: PathBuf, broker_port: u16) ->
|
||||
// A fuzzer with just one stage and a random policy to get testcasess from the corpus
|
||||
let fuzzer = StdFuzzer::new(RandCorpusScheduler::new(), tuple_list!(stage));
|
||||
|
||||
// The wrapped harness function, calling out to the LLVM-style harness
|
||||
let mut harness = |buf: &[u8]| {
|
||||
unsafe { LLVMFuzzerTestOneInput(buf.as_ptr(), buf.len()) };
|
||||
ExitKind::Ok
|
||||
};
|
||||
|
||||
// Create the executor for an in-process function with just one observer for edge coverage
|
||||
let mut executor = InProcessExecutor::new(
|
||||
"in-process(edges)",
|
||||
harness,
|
||||
&mut harness,
|
||||
tuple_list!(edges_observer),
|
||||
&mut state,
|
||||
&mut restarting_mgr,
|
||||
|
@ -12,10 +12,9 @@ use libafl::{
|
||||
QueueCorpusScheduler,
|
||||
},
|
||||
events::setup_restarting_mgr,
|
||||
executors::{inprocess::InProcessExecutor, Executor, ExitKind, TimeoutExecutor},
|
||||
executors::{inprocess::InProcessExecutor, ExitKind, TimeoutExecutor},
|
||||
feedbacks::{CrashFeedback, MaxMapFeedback, TimeFeedback, TimeoutFeedback},
|
||||
fuzzer::{Fuzzer, HasCorpusScheduler, StdFuzzer},
|
||||
inputs::Input,
|
||||
mutators::{scheduled::HavocBytesMutator, token_mutations::Tokens},
|
||||
observers::{HitcountsMapObserver, StdMapObserver, TimeObserver},
|
||||
stages::mutational::StdMutationalStage,
|
||||
@ -39,20 +38,6 @@ extern "C" {
|
||||
static __lafl_max_edges_size: u32;
|
||||
}
|
||||
|
||||
/// The wrapped harness function, calling out to the LLVM-style harness
|
||||
#[cfg(unix)]
|
||||
fn harness<E, I>(_executor: &E, buf: &[u8]) -> ExitKind
|
||||
where
|
||||
E: Executor<I>,
|
||||
I: Input,
|
||||
{
|
||||
// println!("{:?}", buf);
|
||||
unsafe {
|
||||
LLVMFuzzerTestOneInput(buf.as_ptr(), buf.len());
|
||||
}
|
||||
ExitKind::Ok
|
||||
}
|
||||
|
||||
/// The main fn, usually parsing parameters, and starting the fuzzer
|
||||
pub fn main() {
|
||||
// Registry the metadata types used in this fuzzer
|
||||
@ -143,11 +128,17 @@ fn fuzz(corpus_dirs: Vec<PathBuf>, objective_dir: PathBuf, broker_port: u16) ->
|
||||
let scheduler = IndexesLenTimeMinimizerCorpusScheduler::new(QueueCorpusScheduler::new());
|
||||
let fuzzer = StdFuzzer::new(scheduler, tuple_list!(stage));
|
||||
|
||||
// The wrapped harness function, calling out to the LLVM-style harness
|
||||
let mut harness = |buf: &[u8]| {
|
||||
unsafe { LLVMFuzzerTestOneInput(buf.as_ptr(), buf.len()) };
|
||||
ExitKind::Ok
|
||||
};
|
||||
|
||||
// Create the executor for an in-process function with just one observer for edge coverage
|
||||
let mut executor = TimeoutExecutor::new(
|
||||
InProcessExecutor::new(
|
||||
"in-process(edges)",
|
||||
harness,
|
||||
&mut harness,
|
||||
tuple_list!(edges_observer, TimeObserver::new("time")),
|
||||
&mut state,
|
||||
&mut restarting_mgr,
|
||||
|
@ -16,7 +16,6 @@ fn main() {
|
||||
}
|
||||
|
||||
let out_dir = env::var_os("OUT_DIR").unwrap();
|
||||
let cwd = env::current_dir().unwrap().to_string_lossy().to_string();
|
||||
let out_dir = out_dir.to_string_lossy().to_string();
|
||||
let out_dir_path = Path::new(&out_dir);
|
||||
|
||||
@ -25,7 +24,7 @@ fn main() {
|
||||
|
||||
let libpng = format!("{}/libpng-1.6.37", &out_dir);
|
||||
let libpng_path = Path::new(&libpng);
|
||||
let libpng_tar = format!("{}/libpng-1.6.37.tar.xz", &cwd);
|
||||
let libpng_tar = format!("{}/libpng-1.6.37.tar.xz", &out_dir);
|
||||
|
||||
// Enforce clang for its -fsanitize-coverage support.
|
||||
std::env::set_var("CC", "clang");
|
||||
|
@ -11,10 +11,9 @@ use libafl::{
|
||||
QueueCorpusScheduler,
|
||||
},
|
||||
events::setup_restarting_mgr,
|
||||
executors::{inprocess::InProcessExecutor, Executor, ExitKind},
|
||||
executors::{inprocess::InProcessExecutor, ExitKind},
|
||||
feedbacks::{CrashFeedback, MaxMapFeedback, TimeFeedback},
|
||||
fuzzer::{Fuzzer, HasCorpusScheduler, StdFuzzer},
|
||||
inputs::Input,
|
||||
mutators::{scheduled::HavocBytesMutator, token_mutations::Tokens},
|
||||
observers::{HitcountsMapObserver, StdMapObserver, TimeObserver},
|
||||
stages::mutational::StdMutationalStage,
|
||||
@ -41,20 +40,6 @@ extern "C" {
|
||||
static __lafl_max_edges_size: u32;
|
||||
}
|
||||
|
||||
/// The wrapped harness function, calling out to the LLVM-style harness
|
||||
#[cfg(unix)]
|
||||
fn harness<E, I>(_executor: &E, buf: &[u8]) -> ExitKind
|
||||
where
|
||||
E: Executor<I>,
|
||||
I: Input,
|
||||
{
|
||||
// println!("{:?}", buf);
|
||||
unsafe {
|
||||
LLVMFuzzerTestOneInput(buf.as_ptr(), buf.len());
|
||||
}
|
||||
ExitKind::Ok
|
||||
}
|
||||
|
||||
/// The main fn, usually parsing parameters, and starting the fuzzer
|
||||
pub fn main() {
|
||||
// Registry the metadata types used in this fuzzer
|
||||
@ -154,10 +139,16 @@ fn fuzz(corpus_dirs: Vec<PathBuf>, objective_dir: PathBuf, broker_port: u16) ->
|
||||
let scheduler = IndexesLenTimeMinimizerCorpusScheduler::new(QueueCorpusScheduler::new());
|
||||
let fuzzer = StdFuzzer::new(scheduler, tuple_list!(stage));
|
||||
|
||||
// The wrapped harness function, calling out to the LLVM-style harness
|
||||
let mut harness = |buf: &[u8]| {
|
||||
unsafe { LLVMFuzzerTestOneInput(buf.as_ptr(), buf.len()) };
|
||||
ExitKind::Ok
|
||||
};
|
||||
|
||||
// Create the executor for an in-process function with just one observer for edge coverage
|
||||
let mut executor = InProcessExecutor::new(
|
||||
"in-process(edges,cmps,allocs)",
|
||||
harness,
|
||||
&mut harness,
|
||||
tuple_list!(
|
||||
edges_observer,
|
||||
cmps_observer,
|
||||
|
@ -89,10 +89,10 @@ use std::{
|
||||
},
|
||||
};
|
||||
|
||||
#[cfg(all(feature = "std", unix))]
|
||||
#[cfg(all(unix, feature = "std"))]
|
||||
use libc::c_char;
|
||||
|
||||
#[cfg(unix)]
|
||||
#[cfg(all(unix, feature = "std"))]
|
||||
use uds::{UnixListenerExt, UnixSocketAddr, UnixStreamExt};
|
||||
|
||||
#[cfg(unix)]
|
||||
@ -966,7 +966,7 @@ where
|
||||
unsafe fn recv(&mut self) -> Result<Option<*mut LlmpMsg>, Error> {
|
||||
/* DBG("recv %p %p\n", page, last_msg); */
|
||||
compiler_fence(Ordering::SeqCst);
|
||||
let page = self.current_recv_map.page_mut();
|
||||
let mut page = self.current_recv_map.page_mut();
|
||||
let last_msg = self.last_msg_recvd;
|
||||
let current_msg_id = ptr::read_volatile(&(*page).current_msg_id);
|
||||
|
||||
@ -1004,7 +1004,7 @@ where
|
||||
}
|
||||
LLMP_TAG_END_OF_PAGE => {
|
||||
#[cfg(feature = "std")]
|
||||
dbg!("Got end of page, allocing next");
|
||||
println!("Received end of page, allocating next");
|
||||
// Handle end of page
|
||||
if (*msg).buf_len < size_of::<LlmpPayloadSharedMapInfo>() as u64 {
|
||||
panic!(
|
||||
@ -1015,18 +1015,19 @@ where
|
||||
}
|
||||
let pageinfo = (*msg).buf.as_mut_ptr() as *mut LlmpPayloadSharedMapInfo;
|
||||
|
||||
/* We can reuse the map mem space, no need to free and calloc.
|
||||
However, the pageinfo points to the map we're about to unmap.
|
||||
Clone the contents first to be safe (probably fine in rust eitner way). */
|
||||
let pageinfo_cpy = (*pageinfo).clone();
|
||||
/* The pageinfo points to the map we're about to unmap.
|
||||
Copy the contents first to be safe (probably fine in rust either way). */
|
||||
let pageinfo_cpy = *pageinfo;
|
||||
|
||||
// Mark the old page save to unmap, in case we didn't so earlier.
|
||||
ptr::write_volatile(&mut (*page).save_to_unmap, 1);
|
||||
|
||||
// Map the new page. The old one should be unmapped by Drop
|
||||
self.current_recv_map = LlmpSharedMap::existing(SH::existing_from_shm_slice(
|
||||
&pageinfo_cpy.shm_str,
|
||||
pageinfo_cpy.map_size,
|
||||
)?);
|
||||
page = self.current_recv_map.page_mut();
|
||||
// Mark the new page save to unmap also (it's mapped by us, the broker now)
|
||||
ptr::write_volatile(&mut (*page).save_to_unmap, 1);
|
||||
|
||||
@ -1434,7 +1435,9 @@ where
|
||||
// to read from the initial map id.
|
||||
|
||||
let client_out_map_mem = &self.llmp_out.out_maps.first().unwrap().shmem;
|
||||
let broadcast_str_initial = client_out_map_mem.shm_slice().clone();
|
||||
let broadcast_map_description = postcard::to_allocvec(&client_out_map_mem.description())?;
|
||||
|
||||
let mut incoming_map_description_serialized = vec![0u8; broadcast_map_description.len()];
|
||||
|
||||
let llmp_tcp_id = self.llmp_clients.len() as u32;
|
||||
|
||||
@ -1462,33 +1465,37 @@ where
|
||||
match listener.accept() {
|
||||
ListenerStream::Tcp(mut stream, addr) => {
|
||||
dbg!("New connection", addr, stream.peer_addr().unwrap());
|
||||
match stream.write(&broadcast_str_initial) {
|
||||
match stream.write(&broadcast_map_description) {
|
||||
Ok(_) => {} // fire & forget
|
||||
Err(e) => {
|
||||
dbg!("Could not send to shmap to client", e);
|
||||
continue;
|
||||
}
|
||||
};
|
||||
let mut new_client_map_str: [u8; 20] = Default::default();
|
||||
match stream.read_exact(&mut new_client_map_str) {
|
||||
match stream.read_exact(&mut incoming_map_description_serialized) {
|
||||
Ok(()) => (),
|
||||
Err(e) => {
|
||||
dbg!("Ignoring failed read from client", e);
|
||||
continue;
|
||||
}
|
||||
};
|
||||
unsafe {
|
||||
let msg = new_client_sender
|
||||
.alloc_next(size_of::<LlmpPayloadSharedMapInfo>())
|
||||
.expect("Could not allocate a new message in shared map.");
|
||||
(*msg).tag = LLMP_TAG_NEW_SHM_CLIENT;
|
||||
let pageinfo = (*msg).buf.as_mut_ptr() as *mut LlmpPayloadSharedMapInfo;
|
||||
(*pageinfo).shm_str = new_client_map_str;
|
||||
(*pageinfo).map_size = LLMP_CFG_INITIAL_MAP_SIZE;
|
||||
match new_client_sender.send(msg) {
|
||||
Ok(()) => (),
|
||||
Err(e) => println!("Error forwarding client on map: {:?}", e),
|
||||
};
|
||||
if let Ok(incoming_map_description) = postcard::from_bytes::<ShMemDescription>(
|
||||
&incoming_map_description_serialized,
|
||||
) {
|
||||
unsafe {
|
||||
let msg = new_client_sender
|
||||
.alloc_next(size_of::<LlmpPayloadSharedMapInfo>())
|
||||
.expect("Could not allocate a new message in shared map.");
|
||||
(*msg).tag = LLMP_TAG_NEW_SHM_CLIENT;
|
||||
let pageinfo =
|
||||
(*msg).buf.as_mut_ptr() as *mut LlmpPayloadSharedMapInfo;
|
||||
(*pageinfo).shm_str = incoming_map_description.str_bytes;
|
||||
(*pageinfo).map_size = incoming_map_description.size;
|
||||
match new_client_sender.send(msg) {
|
||||
Ok(()) => (),
|
||||
Err(e) => println!("Error forwarding client on map: {:?}", e),
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
#[cfg(unix)]
|
||||
@ -1496,14 +1503,14 @@ where
|
||||
dbg!("New connection", addr);
|
||||
|
||||
let broadcast_fd_initial: i32 =
|
||||
CStr::from_ptr(broadcast_str_initial.as_ptr() as *const c_char)
|
||||
CStr::from_ptr(broadcast_map_description.as_ptr() as *const c_char)
|
||||
.to_string_lossy()
|
||||
.into_owned()
|
||||
.parse()
|
||||
.unwrap_or_else(|_| {
|
||||
panic!(
|
||||
"ShmId is not a valid int file descriptor: {:?}",
|
||||
broadcast_str_initial
|
||||
broadcast_map_description
|
||||
)
|
||||
});
|
||||
|
||||
@ -1856,15 +1863,24 @@ where
|
||||
let mut stream = TcpStream::connect(format!("127.0.0.1:{}", port))?;
|
||||
println!("Connected to port {}", port);
|
||||
|
||||
let mut new_broker_map_str: [u8; 20] = Default::default();
|
||||
// First, get the serialized description size by serializing a dummy.
|
||||
let dummy_description = ShMemDescription {
|
||||
size: 0,
|
||||
str_bytes: Default::default(),
|
||||
};
|
||||
let mut new_broker_map_str = postcard::to_allocvec(&dummy_description)?;
|
||||
|
||||
stream.read_exact(&mut new_broker_map_str)?;
|
||||
|
||||
let ret = Self::new(LlmpSharedMap::existing(SH::existing_from_shm_slice(
|
||||
&new_broker_map_str,
|
||||
LLMP_CFG_INITIAL_MAP_SIZE,
|
||||
let broker_map_description: ShMemDescription = postcard::from_bytes(&new_broker_map_str)?;
|
||||
|
||||
let ret = Self::new(LlmpSharedMap::existing(SH::existing_from_description(
|
||||
&broker_map_description,
|
||||
)?))?;
|
||||
|
||||
stream.write_all(ret.sender.out_maps.first().unwrap().shmem.shm_slice())?;
|
||||
let own_map_description_bytes =
|
||||
postcard::to_allocvec(&ret.sender.out_maps.first().unwrap().shmem.description())?;
|
||||
stream.write_all(&own_map_description_bytes)?;
|
||||
Ok(ret)
|
||||
}
|
||||
}
|
||||
|
@ -20,9 +20,9 @@ use crate::Error;
|
||||
#[derive(Copy, Clone, Debug, Serialize, Deserialize)]
|
||||
pub struct ShMemDescription {
|
||||
/// Size of this map
|
||||
size: usize,
|
||||
pub size: usize,
|
||||
/// of name of this map, as fixed 20 bytes c-string
|
||||
str_bytes: [u8; 20],
|
||||
pub str_bytes: [u8; 20],
|
||||
}
|
||||
|
||||
/// A Shared map
|
||||
@ -370,7 +370,7 @@ pub mod unix_shmem {
|
||||
// Not set or not initialized;
|
||||
return;
|
||||
}
|
||||
(*shm).shm_str[0 as usize] = 0u8;
|
||||
(*shm).shm_str[0_usize] = 0u8;
|
||||
shmctl((*shm).shm_id, 0 as c_int, ptr::null_mut());
|
||||
(*shm).map = ptr::null_mut();
|
||||
}
|
||||
|
@ -25,26 +25,25 @@ use crate::{
|
||||
Error,
|
||||
};
|
||||
|
||||
/// The inmem executor harness
|
||||
type HarnessFunction<E> = fn(&E, &[u8]) -> ExitKind;
|
||||
|
||||
/// The inmem executor simply calls a target function, then returns afterwards.
|
||||
pub struct InProcessExecutor<I, OT>
|
||||
pub struct InProcessExecutor<'a, H, I, OT>
|
||||
where
|
||||
H: FnMut(&[u8]) -> ExitKind,
|
||||
I: Input + HasTargetBytes,
|
||||
OT: ObserversTuple,
|
||||
{
|
||||
/// The name of this executor instance, to address it from other components
|
||||
name: &'static str,
|
||||
/// The harness function, being executed for each fuzzing loop execution
|
||||
harness_fn: HarnessFunction<Self>,
|
||||
harness_fn: &'a mut H,
|
||||
/// The observers, observing each run
|
||||
observers: OT,
|
||||
phantom: PhantomData<I>,
|
||||
}
|
||||
|
||||
impl<I, OT> Executor<I> for InProcessExecutor<I, OT>
|
||||
impl<'a, H, I, OT> Executor<I> for InProcessExecutor<'a, H, I, OT>
|
||||
where
|
||||
H: FnMut(&[u8]) -> ExitKind,
|
||||
I: Input + HasTargetBytes,
|
||||
OT: ObserversTuple,
|
||||
{
|
||||
@ -95,7 +94,7 @@ where
|
||||
#[inline]
|
||||
fn run_target(&mut self, input: &I) -> Result<ExitKind, Error> {
|
||||
let bytes = input.target_bytes();
|
||||
let ret = (self.harness_fn)(self, bytes.as_slice());
|
||||
let ret = (self.harness_fn)(bytes.as_slice());
|
||||
Ok(ret)
|
||||
}
|
||||
|
||||
@ -126,8 +125,9 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
impl<I, OT> Named for InProcessExecutor<I, OT>
|
||||
impl<'a, H, I, OT> Named for InProcessExecutor<'a, H, I, OT>
|
||||
where
|
||||
H: FnMut(&[u8]) -> ExitKind,
|
||||
I: Input + HasTargetBytes,
|
||||
OT: ObserversTuple,
|
||||
{
|
||||
@ -136,8 +136,9 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
impl<I, OT> HasObservers<OT> for InProcessExecutor<I, OT>
|
||||
impl<'a, H, I, OT> HasObservers<OT> for InProcessExecutor<'a, H, I, OT>
|
||||
where
|
||||
H: FnMut(&[u8]) -> ExitKind,
|
||||
I: Input + HasTargetBytes,
|
||||
OT: ObserversTuple,
|
||||
{
|
||||
@ -152,8 +153,9 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
impl<I, OT> InProcessExecutor<I, OT>
|
||||
impl<'a, H, I, OT> InProcessExecutor<'a, H, I, OT>
|
||||
where
|
||||
H: FnMut(&[u8]) -> ExitKind,
|
||||
I: Input + HasTargetBytes,
|
||||
OT: ObserversTuple,
|
||||
{
|
||||
@ -166,7 +168,7 @@ where
|
||||
/// This may return an error on unix, if signal handler setup fails
|
||||
pub fn new<EM, OC, OFT, S>(
|
||||
name: &'static str,
|
||||
harness_fn: HarnessFunction<Self>,
|
||||
harness_fn: &'a mut H,
|
||||
observers: OT,
|
||||
_state: &mut S,
|
||||
_event_mgr: &mut EM,
|
||||
@ -215,6 +217,18 @@ where
|
||||
phantom: PhantomData,
|
||||
})
|
||||
}
|
||||
|
||||
/// Retrieve the harness function.
|
||||
#[inline]
|
||||
pub fn harness(&self) -> &H {
|
||||
self.harness_fn
|
||||
}
|
||||
|
||||
/// Retrieve the harness function for a mutable reference.
|
||||
#[inline]
|
||||
pub fn harness_mut(&mut self) -> &mut H {
|
||||
self.harness_fn
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(unix)]
|
||||
@ -223,10 +237,7 @@ mod unix_signal_handler {
|
||||
use core::ptr;
|
||||
use libc::{c_void, siginfo_t};
|
||||
#[cfg(feature = "std")]
|
||||
use std::{
|
||||
fs,
|
||||
io::{stdout, Write},
|
||||
};
|
||||
use std::io::{stdout, Write};
|
||||
|
||||
use crate::{
|
||||
bolts::os::unix_signals::{Handler, Signal},
|
||||
@ -443,7 +454,7 @@ mod unix_signal_handler {
|
||||
}
|
||||
// let's yolo-cat the maps for debugging, if possible.
|
||||
#[cfg(all(target_os = "linux", feature = "std"))]
|
||||
match fs::read_to_string("/proc/self/maps") {
|
||||
match std::fs::read_to_string("/proc/self/maps") {
|
||||
Ok(maps) => println!("maps:\n{}", maps),
|
||||
Err(e) => println!("Couldn't load mappings: {:?}", e),
|
||||
};
|
||||
@ -630,19 +641,15 @@ mod tests {
|
||||
use crate::{
|
||||
bolts::tuples::tuple_list,
|
||||
executors::{Executor, ExitKind, InProcessExecutor},
|
||||
inputs::Input,
|
||||
inputs::NopInput,
|
||||
};
|
||||
|
||||
fn test_harness_fn_nop<E: Executor<I>, I: Input>(_executor: &E, _buf: &[u8]) -> ExitKind {
|
||||
ExitKind::Ok
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_inmem_exec() {
|
||||
use crate::inputs::NopInput;
|
||||
let mut harness = |_buf: &[u8]| ExitKind::Ok;
|
||||
|
||||
let mut in_process_executor = InProcessExecutor::<NopInput, ()> {
|
||||
harness_fn: test_harness_fn_nop,
|
||||
let mut in_process_executor = InProcessExecutor::<_, NopInput, ()> {
|
||||
harness_fn: &mut harness,
|
||||
observers: tuple_list!(),
|
||||
name: "main",
|
||||
phantom: PhantomData,
|
||||
|
@ -6,10 +6,15 @@ use crate::{
|
||||
observers::ObserversTuple,
|
||||
stages::StagesTuple,
|
||||
state::HasExecutions,
|
||||
utils::{current_milliseconds, current_time},
|
||||
utils::current_time,
|
||||
Error,
|
||||
};
|
||||
use core::marker::PhantomData;
|
||||
|
||||
use alloc::string::ToString;
|
||||
use core::{marker::PhantomData, time::Duration};
|
||||
|
||||
/// Send a stats update all 6 (or more) seconds
|
||||
const STATS_TIMEOUT_DEFAULT: Duration = Duration::from_millis(6 * 1000);
|
||||
|
||||
/// Holds a set of stages
|
||||
pub trait HasStages<CS, E, EM, I, S, ST>
|
||||
@ -38,9 +43,55 @@ where
|
||||
|
||||
/// The main fuzzer trait.
|
||||
pub trait Fuzzer<E, EM, S> {
|
||||
/// Fuzz for a single iteration
|
||||
/// Returns the index of the last fuzzed corpus item
|
||||
fn fuzz_one(&self, state: &mut S, executor: &mut E, manager: &mut EM) -> Result<usize, Error>;
|
||||
|
||||
fn fuzz_loop(&self, state: &mut S, executor: &mut E, manager: &mut EM) -> Result<usize, Error>;
|
||||
/// Fuzz forever (or until stopped)
|
||||
fn fuzz_loop(&self, state: &mut S, executor: &mut E, manager: &mut EM) -> Result<(), Error> {
|
||||
let mut last = current_time();
|
||||
let stats_timeout = STATS_TIMEOUT_DEFAULT;
|
||||
loop {
|
||||
self.fuzz_one(state, executor, manager)?;
|
||||
last = Self::maybe_report_stats(state, manager, last, stats_timeout)?;
|
||||
}
|
||||
}
|
||||
|
||||
/// Fuzz for n iterations
|
||||
/// Returns the index of the last fuzzed corpus item
|
||||
fn fuzz_loop_for(
|
||||
&self,
|
||||
state: &mut S,
|
||||
executor: &mut E,
|
||||
manager: &mut EM,
|
||||
iters: u64,
|
||||
) -> Result<usize, Error> {
|
||||
if iters == 0 {
|
||||
return Err(Error::IllegalArgument(
|
||||
"Cannot fuzz for 0 iterations!".to_string(),
|
||||
));
|
||||
}
|
||||
|
||||
let mut ret = 0;
|
||||
let mut last = current_time();
|
||||
let stats_timeout = STATS_TIMEOUT_DEFAULT;
|
||||
|
||||
for _ in 0..iters {
|
||||
ret = self.fuzz_one(state, executor, manager)?;
|
||||
last = Self::maybe_report_stats(state, manager, last, stats_timeout)?;
|
||||
}
|
||||
Ok(ret)
|
||||
}
|
||||
|
||||
/// Given the last time, if stats_timeout seconds passed, send off an info/stats/heartbeat message to the broker.
|
||||
/// Returns the new `last` time (so the old one, unless `stats_timeout` time has passed and stats have been sent)
|
||||
/// Will return an Error, if the stats could not be sent.
|
||||
fn maybe_report_stats(
|
||||
state: &mut S,
|
||||
manager: &mut EM,
|
||||
last: Duration,
|
||||
stats_timeout: Duration,
|
||||
) -> Result<Duration, Error>;
|
||||
}
|
||||
|
||||
/// Your default fuzzer instance, for everyday use.
|
||||
@ -102,6 +153,31 @@ where
|
||||
OT: ObserversTuple,
|
||||
I: Input,
|
||||
{
|
||||
#[inline]
|
||||
fn maybe_report_stats(
|
||||
state: &mut S,
|
||||
manager: &mut EM,
|
||||
last: Duration,
|
||||
stats_timeout: Duration,
|
||||
) -> Result<Duration, Error> {
|
||||
let cur = current_time();
|
||||
if cur - last > stats_timeout {
|
||||
//println!("Fire {:?} {:?} {:?}", cur, last, stats_timeout);
|
||||
manager.fire(
|
||||
state,
|
||||
Event::UpdateStats {
|
||||
executions: *state.executions(),
|
||||
time: cur,
|
||||
phantom: PhantomData,
|
||||
},
|
||||
)?;
|
||||
Ok(cur)
|
||||
} else {
|
||||
if cur.as_millis() % 1000 == 0 {}
|
||||
Ok(last)
|
||||
}
|
||||
}
|
||||
|
||||
fn fuzz_one(&self, state: &mut S, executor: &mut E, manager: &mut EM) -> Result<usize, Error> {
|
||||
let idx = self.scheduler().next(state)?;
|
||||
|
||||
@ -111,25 +187,6 @@ where
|
||||
manager.process(state, executor, self.scheduler())?;
|
||||
Ok(idx)
|
||||
}
|
||||
|
||||
fn fuzz_loop(&self, state: &mut S, executor: &mut E, manager: &mut EM) -> Result<usize, Error> {
|
||||
let mut last = current_milliseconds();
|
||||
loop {
|
||||
self.fuzz_one(state, executor, manager)?;
|
||||
let cur = current_milliseconds();
|
||||
if cur - last > 60 * 100 {
|
||||
last = cur;
|
||||
manager.fire(
|
||||
state,
|
||||
Event::UpdateStats {
|
||||
executions: *state.executions(),
|
||||
time: current_time(),
|
||||
phantom: PhantomData,
|
||||
},
|
||||
)?
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<CS, ST, E, EM, I, OT, S> StdFuzzer<CS, ST, E, EM, I, OT, S>
|
||||
|
@ -137,8 +137,8 @@ mod tests {
|
||||
use crate::{
|
||||
bolts::tuples::tuple_list,
|
||||
corpus::{Corpus, InMemoryCorpus, RandCorpusScheduler, Testcase},
|
||||
executors::{Executor, ExitKind, InProcessExecutor},
|
||||
inputs::{BytesInput, Input},
|
||||
executors::{ExitKind, InProcessExecutor},
|
||||
inputs::BytesInput,
|
||||
mutators::{mutation_bitflip, ComposedByMutations, StdScheduledMutator},
|
||||
stages::StdMutationalStage,
|
||||
state::{HasCorpus, State},
|
||||
@ -150,10 +150,6 @@ mod tests {
|
||||
#[cfg(feature = "std")]
|
||||
use crate::events::SimpleEventManager;
|
||||
|
||||
fn harness<E: Executor<I>, I: Input>(_executor: &E, _buf: &[u8]) -> ExitKind {
|
||||
ExitKind::Ok
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_fuzzer() {
|
||||
let rand = StdRand::with_seed(0);
|
||||
@ -175,9 +171,10 @@ mod tests {
|
||||
});
|
||||
let mut event_manager = SimpleEventManager::new(stats);
|
||||
|
||||
let mut harness = |_buf: &[u8]| ExitKind::Ok;
|
||||
let mut executor = InProcessExecutor::new(
|
||||
"main",
|
||||
harness,
|
||||
&mut harness,
|
||||
tuple_list!(),
|
||||
//Box::new(|_, _, _, _, _| ()),
|
||||
&mut state,
|
||||
|
@ -752,13 +752,13 @@ where
|
||||
|
||||
// Converts a hex u8 to its u8 value: 'A' -> 10 etc.
|
||||
fn from_hex(hex: u8) -> Result<u8, Error> {
|
||||
if hex >= 48 && hex <= 57 {
|
||||
if (48..=57).contains(&hex) {
|
||||
return Ok(hex - 48);
|
||||
}
|
||||
if hex >= 65 && hex <= 70 {
|
||||
if (65..=70).contains(&hex) {
|
||||
return Ok(hex - 55);
|
||||
}
|
||||
if hex >= 97 && hex <= 102 {
|
||||
if (97..=102).contains(&hex) {
|
||||
return Ok(hex - 87);
|
||||
}
|
||||
Err(Error::IllegalArgument("".to_owned()))
|
||||
@ -781,17 +781,15 @@ pub fn str_decode(item: &str) -> Result<Vec<u8>, Error> {
|
||||
decoded += from_hex(c)?;
|
||||
token.push(decoded);
|
||||
take_next_two = 0;
|
||||
} else {
|
||||
if c != backslash || take_next {
|
||||
if take_next && (c == 120 || c == 88) {
|
||||
take_next_two = 1;
|
||||
} else {
|
||||
token.push(c);
|
||||
}
|
||||
take_next = false;
|
||||
} else if c != backslash || take_next {
|
||||
if take_next && (c == 120 || c == 88) {
|
||||
take_next_two = 1;
|
||||
} else {
|
||||
take_next = true;
|
||||
token.push(c);
|
||||
}
|
||||
take_next = false;
|
||||
} else {
|
||||
take_next = true;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -206,6 +206,7 @@ mod tests {
|
||||
#[cfg(feature = "std")]
|
||||
use std::fs;
|
||||
|
||||
#[cfg(feature = "std")]
|
||||
use super::Tokens;
|
||||
|
||||
#[cfg(feature = "std")]
|
||||
|
@ -1,4 +1,7 @@
|
||||
use alloc::string::{String, ToString};
|
||||
use alloc::{
|
||||
string::{String, ToString},
|
||||
vec::Vec,
|
||||
};
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use crate::{
|
||||
@ -126,6 +129,16 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
/// Creates a new MapObserver with an owned map
|
||||
pub fn new_owned(name: &'static str, map: Vec<T>) -> Self {
|
||||
let initial = if map.is_empty() { T::default() } else { map[0] };
|
||||
Self {
|
||||
map: ArrayMut::Owned(map),
|
||||
name: name.to_string(),
|
||||
initial,
|
||||
}
|
||||
}
|
||||
|
||||
/// Creates a new MapObserver from a raw pointer
|
||||
/// # Safety
|
||||
/// Will dereference the map_ptr with up to len elements.
|
||||
|
@ -160,28 +160,16 @@ pub fn current_time() -> time::Duration {
|
||||
time::Duration::from_millis(1)
|
||||
}
|
||||
|
||||
#[cfg(feature = "std")]
|
||||
#[inline]
|
||||
/// Gets current nanoseconds since UNIX_EPOCH
|
||||
#[inline]
|
||||
pub fn current_nanos() -> u64 {
|
||||
SystemTime::now()
|
||||
.duration_since(UNIX_EPOCH)
|
||||
.unwrap()
|
||||
.as_nanos() as u64
|
||||
current_time().as_nanos() as u64
|
||||
}
|
||||
|
||||
#[cfg(feature = "std")]
|
||||
/// Gets current milliseconds since UNIX_EPOCH
|
||||
#[inline]
|
||||
pub fn current_milliseconds() -> u64 {
|
||||
SystemTime::now()
|
||||
.duration_since(UNIX_EPOCH)
|
||||
.unwrap()
|
||||
.as_millis() as u64
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "std"))]
|
||||
pub fn current_milliseconds() -> u64 {
|
||||
1000
|
||||
current_time().as_millis() as u64
|
||||
}
|
||||
|
||||
/// XXH3 Based, hopefully speedy, rnd implementation
|
||||
|
Loading…
x
Reference in New Issue
Block a user