commit
6e4c6a9b6b
2
.github/workflows/build_and_test.yml
vendored
2
.github/workflows/build_and_test.yml
vendored
@ -3,7 +3,7 @@ name: Build and Test
|
||||
on:
|
||||
push:
|
||||
branches: [ main ]
|
||||
pull_requests:
|
||||
pull_request:
|
||||
branches: [ main, dev ]
|
||||
|
||||
env:
|
||||
|
@ -13,4 +13,5 @@ members = [
|
||||
#example fuzzers
|
||||
"fuzzers/libfuzzer_libpng",
|
||||
"fuzzers/libfuzzer_libmozjpeg",
|
||||
"fuzzers/libfuzzer_libpng_cmpalloc",
|
||||
]
|
||||
|
45
README.md
45
README.md
@ -6,26 +6,43 @@ LibAFL is written and maintained by Andrea Fioraldi <andreafioraldi@gmail.com> a
|
||||
|
||||
It is released as Open Source Software under the [Apache v2](LICENSE-APACHE) or [MIT](LICENSE-MIT) licenses.
|
||||
|
||||
## Example usages
|
||||
## Getting started
|
||||
|
||||
Clone the LibAFL repository with
|
||||
|
||||
```
|
||||
git clone https://github.com/AFLplusplus/LibAFL
|
||||
```
|
||||
|
||||
Build the library using
|
||||
|
||||
```
|
||||
cargo build --release
|
||||
```
|
||||
|
||||
Build the documentation with
|
||||
|
||||
```
|
||||
cargo doc
|
||||
```
|
||||
|
||||
We collect example fuzzers in `./fuzzers`. They can be build using `cargo build --example [fuzzer_name] --release`.
|
||||
|
||||
We collect example fuzzers in `./fuzzers`. They can be build using `cargo build --example [fuzzer_name] --release`
|
||||
The best-tested fuzzer is `./fuzzers/libfuzzer_libpng`, a clone of libfuzzer using libafl for a libpng harness.
|
||||
See its readme [here](./fuzzers/libfuzzer_libpng/README.md).
|
||||
|
||||
If you want to get a quick overview, run `cargo doc`.
|
||||
Feel free to open issues or contact us directly. Thank you for your support. <3
|
||||
|
||||
## The Core Concepts
|
||||
|
||||
We're still working on the documentation. In the meantime, you can watch the Video from last year's Rc3, here:
|
||||
The entire library is based on some core concepts that we think can generalize Fuzz Testing.
|
||||
|
||||
We're still working on extending the documentation.
|
||||
|
||||
In the meantime, you can watch the Video from last year's RC3, here:
|
||||
|
||||
[](http://www.youtube.com/watch?v=3RWkT1Q5IV0 "Fuzzers Like LEGO")
|
||||
## Roadmap for release
|
||||
|
||||
+ ~~Minset corpus scheduler~~ still doc missing
|
||||
+ Win32 shared mem and crash handler to have Windows in-process executor
|
||||
+ Other feedbacks examples (e.g. maximize allocations to spot OOMs)
|
||||
+ Other objectives examples (e.g. execution of a given program point)
|
||||
+ ~~A macro crate with derive directives (e.g. for SerdeAny impl)~~ just `derive(SerdeAny)`, missing doc.
|
||||
+ Good documentation
|
||||
## Contributing
|
||||
|
||||
For further TODOs, see [TODO.md](./TODO.md)
|
||||
Check the [TODO.md](./TODO.md) file for features that we plan to support.
|
||||
|
||||
For bugs, feel free to open issues or contact us directly. Thank you for your support. <3
|
||||
|
7
TODO.md
7
TODO.md
@ -2,21 +2,20 @@
|
||||
|
||||
- [x] ~~Minset corpus scheduler~~ still doc missing
|
||||
- [ ] Win32 shared mem and crash handler to have Windows in-process executor
|
||||
- [ ] Other feedbacks examples (e.g. maximize allocations to spot OOMs)
|
||||
- [x] Other feedbacks examples (e.g. maximize allocations to spot OOMs)
|
||||
- [ ] Other objectives examples (e.g. execution of a given program point)
|
||||
- [ ] Objective-Specific Corpuses (named per objective)
|
||||
- [x] A macro crate with derive directives (e.g. for SerdeAny impl).
|
||||
- [ ] Good documentation
|
||||
- [ ] LLMP brotli compression
|
||||
- [ ] Timeouts (timeout observer, objective)
|
||||
- [ ] AFL-Style Forkserver Executor
|
||||
- [ ] Restarting EventMgr could use forks on unix
|
||||
- [x] Restarting EventMgr could use forks on unix
|
||||
- [ ] Android Ashmem support
|
||||
- [ ] Restart Count in Fuzzing Loop
|
||||
- [ ] LAIN / structured fuzzing example
|
||||
- [ ] Errors in the Fuzzer should exit the fuzz run
|
||||
- [ ] More informative outpus, deeper introspection (stats, what mutation did x, etc.)
|
||||
- [ ] Timeouts for executors
|
||||
- [x] Timeouts for executors
|
||||
- [ ] Timeout handling for llmp clients (no ping for n seconds -> treat as disconnected)
|
||||
- [ ] LLMP Cross Machine Link (2 brokers connected via TCP)
|
||||
- [ ] "Launcher" example that spawns broker + n clients
|
||||
|
@ -1,6 +1,7 @@
|
||||
//! A libfuzzer-like fuzzer with llmp-multithreading support and restarts
|
||||
//! The example harness is built for libpng.
|
||||
|
||||
use core::time::Duration;
|
||||
use std::{env, path::PathBuf};
|
||||
|
||||
#[cfg(unix)]
|
||||
@ -11,8 +12,8 @@ use libafl::{
|
||||
QueueCorpusScheduler,
|
||||
},
|
||||
events::setup_restarting_mgr,
|
||||
executors::{inprocess::InProcessExecutor, Executor, ExitKind},
|
||||
feedbacks::{CrashFeedback, MaxMapFeedback, TimeFeedback},
|
||||
executors::{inprocess::InProcessExecutor, Executor, ExitKind, TimeoutExecutor},
|
||||
feedbacks::{CrashFeedback, MaxMapFeedback, TimeFeedback, TimeoutFeedback},
|
||||
fuzzer::{Fuzzer, HasCorpusScheduler, StdFuzzer},
|
||||
inputs::Input,
|
||||
mutators::{scheduled::HavocBytesMutator, token_mutations::Tokens},
|
||||
@ -117,7 +118,7 @@ fn fuzz(corpus_dirs: Vec<PathBuf>, objective_dir: PathBuf, broker_port: u16) ->
|
||||
// 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()),
|
||||
tuple_list!(CrashFeedback::new(), TimeoutFeedback::new()),
|
||||
)
|
||||
});
|
||||
|
||||
@ -143,13 +144,17 @@ fn fuzz(corpus_dirs: Vec<PathBuf>, objective_dir: PathBuf, broker_port: u16) ->
|
||||
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 = InProcessExecutor::new(
|
||||
"in-process(edges)",
|
||||
harness,
|
||||
tuple_list!(edges_observer, TimeObserver::new("time")),
|
||||
&mut state,
|
||||
&mut restarting_mgr,
|
||||
)?;
|
||||
let mut executor = TimeoutExecutor::new(
|
||||
InProcessExecutor::new(
|
||||
"in-process(edges)",
|
||||
harness,
|
||||
tuple_list!(edges_observer, TimeObserver::new("time")),
|
||||
&mut state,
|
||||
&mut restarting_mgr,
|
||||
)?,
|
||||
// 10 seconds timeout
|
||||
Duration::new(10, 0),
|
||||
);
|
||||
|
||||
// The actual target run starts here.
|
||||
// Call LLVMFUzzerInitialize() if present.
|
||||
|
1
fuzzers/libfuzzer_libpng_cmpalloc/.gitignore
vendored
Normal file
1
fuzzers/libfuzzer_libpng_cmpalloc/.gitignore
vendored
Normal file
@ -0,0 +1 @@
|
||||
libpng-*
|
31
fuzzers/libfuzzer_libpng_cmpalloc/Cargo.toml
Normal file
31
fuzzers/libfuzzer_libpng_cmpalloc/Cargo.toml
Normal file
@ -0,0 +1,31 @@
|
||||
[package]
|
||||
name = "libfuzzer_libpng_cmpalloc"
|
||||
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"]
|
||||
std = []
|
||||
|
||||
#[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/" }
|
||||
|
||||
[[example]]
|
||||
name = "libfuzzer_libpng_cmpalloc"
|
||||
path = "./src/fuzzer.rs"
|
||||
test = false
|
||||
bench = false
|
28
fuzzers/libfuzzer_libpng_cmpalloc/README.md
Normal file
28
fuzzers/libfuzzer_libpng_cmpalloc/README.md
Normal file
@ -0,0 +1,28 @@
|
||||
# Libfuzzer for libpng (cmp+alloc)
|
||||
|
||||
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.
|
||||
|
||||
The difference between the normal Libfuzzer for libpng example here is that this fuzzer is not just using edge coverage as feedback but also comparisons values (-value-profile like) and allocations sizes.
|
||||
This is an example how multiple feedbacks can be combined in a fuzzer.
|
||||
|
||||
## Build
|
||||
|
||||
To build this example, run `cargo build --example libfuzzer_libpng_cmpalloc --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_cmpalloc`.
|
||||
|
||||
## 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.
|
109
fuzzers/libfuzzer_libpng_cmpalloc/build.rs
Normal file
109
fuzzers/libfuzzer_libpng_cmpalloc/build.rs
Normal file
@ -0,0 +1,109 @@
|
||||
// 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);
|
||||
|
||||
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(),
|
||||
};
|
||||
|
||||
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 -fPIE -fsanitize-coverage=trace-pc-guard",
|
||||
)
|
||||
.env(
|
||||
"CXXFLAGS",
|
||||
"-O3 -g -D_DEFAULT_SOURCE -fPIE -fsanitize-coverage=trace-pc-guard",
|
||||
)
|
||||
.env(
|
||||
"LDFLAGS",
|
||||
format!("-g -fPIE -fsanitize-coverage=trace-pc-guard {}", ldflags),
|
||||
)
|
||||
.status()
|
||||
.unwrap();
|
||||
Command::new("make")
|
||||
.current_dir(&libpng_path)
|
||||
.status()
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
cc::Build::new()
|
||||
.file("../libfuzzer_runtime/rt.c")
|
||||
.compile("libfuzzer-sys");
|
||||
|
||||
cc::Build::new()
|
||||
.include(&libpng_path)
|
||||
.cpp(true)
|
||||
.flag("-fsanitize-coverage=trace-pc-guard")
|
||||
// .define("HAS_DUMMY_CRASH", "1")
|
||||
.file("./harness.cc")
|
||||
.compile("libfuzzer-harness");
|
||||
|
||||
println!("cargo:rustc-link-search=native={}", &out_dir);
|
||||
println!("cargo:rustc-link-search=native={}/.libs", &libpng);
|
||||
println!("cargo:rustc-link-lib=static=png16");
|
||||
|
||||
//Deps for libpng: -pthread -lz -lm
|
||||
println!("cargo:rustc-link-lib=dylib=m");
|
||||
println!("cargo:rustc-link-lib=dylib=z");
|
||||
|
||||
//For the C++ harness
|
||||
//must by dylib for android
|
||||
println!("cargo:rustc-link-lib=dylib=stdc++");
|
||||
|
||||
println!("cargo:rerun-if-changed=build.rs");
|
||||
}
|
BIN
fuzzers/libfuzzer_libpng_cmpalloc/corpus/not_kitty.png
Normal file
BIN
fuzzers/libfuzzer_libpng_cmpalloc/corpus/not_kitty.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 218 B |
BIN
fuzzers/libfuzzer_libpng_cmpalloc/corpus/not_kitty_alpha.png
Normal file
BIN
fuzzers/libfuzzer_libpng_cmpalloc/corpus/not_kitty_alpha.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 376 B |
BIN
fuzzers/libfuzzer_libpng_cmpalloc/corpus/not_kitty_gamma.png
Normal file
BIN
fuzzers/libfuzzer_libpng_cmpalloc/corpus/not_kitty_gamma.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 228 B |
BIN
fuzzers/libfuzzer_libpng_cmpalloc/corpus/not_kitty_icc.png
Normal file
BIN
fuzzers/libfuzzer_libpng_cmpalloc/corpus/not_kitty_icc.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 427 B |
197
fuzzers/libfuzzer_libpng_cmpalloc/harness.cc
Normal file
197
fuzzers/libfuzzer_libpng_cmpalloc/harness.cc
Normal file
@ -0,0 +1,197 @@
|
||||
// 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
|
||||
#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;
|
||||
}
|
||||
|
199
fuzzers/libfuzzer_libpng_cmpalloc/src/fuzzer.rs
Normal file
199
fuzzers/libfuzzer_libpng_cmpalloc/src/fuzzer.rs
Normal file
@ -0,0 +1,199 @@
|
||||
//! A libfuzzer-like fuzzer with llmp-multithreading support and restarts
|
||||
//! The example harness is built for libpng.
|
||||
|
||||
use std::{env, path::PathBuf};
|
||||
|
||||
#[cfg(unix)]
|
||||
use libafl::{
|
||||
bolts::{shmem::UnixShMem, tuples::tuple_list},
|
||||
corpus::{
|
||||
Corpus, InMemoryCorpus, IndexesLenTimeMinimizerCorpusScheduler, OnDiskCorpus,
|
||||
QueueCorpusScheduler,
|
||||
},
|
||||
events::setup_restarting_mgr,
|
||||
executors::{inprocess::InProcessExecutor, Executor, ExitKind},
|
||||
feedbacks::{CrashFeedback, MaxMapFeedback, TimeFeedback},
|
||||
fuzzer::{Fuzzer, HasCorpusScheduler, StdFuzzer},
|
||||
inputs::Input,
|
||||
mutators::{scheduled::HavocBytesMutator, token_mutations::Tokens},
|
||||
observers::{HitcountsMapObserver, StdMapObserver, TimeObserver},
|
||||
stages::mutational::StdMutationalStage,
|
||||
state::{HasCorpus, HasMetadata, State},
|
||||
stats::SimpleStats,
|
||||
utils::{current_nanos, StdRand},
|
||||
Error,
|
||||
};
|
||||
|
||||
const MAP_SIZE: usize = 16 * 1024;
|
||||
|
||||
/// We will interact with a C++ target, so use external c functionality
|
||||
#[cfg(unix)]
|
||||
extern "C" {
|
||||
/// int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size)
|
||||
fn LLVMFuzzerTestOneInput(data: *const u8, size: usize) -> i32;
|
||||
|
||||
// afl_libfuzzer_init calls LLVMFUzzerInitialize()
|
||||
fn afl_libfuzzer_init() -> i32;
|
||||
|
||||
static __lafl_edges_map: *mut u8;
|
||||
static __lafl_cmp_map: *mut u8;
|
||||
static __lafl_alloc_map: *mut usize;
|
||||
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
|
||||
// Needed only on no_std
|
||||
//RegistryBuilder::register::<Tokens>();
|
||||
|
||||
println!(
|
||||
"Workdir: {:?}",
|
||||
env::current_dir().unwrap().to_string_lossy().to_string()
|
||||
);
|
||||
fuzz(
|
||||
vec![PathBuf::from("./corpus")],
|
||||
PathBuf::from("./crashes"),
|
||||
1337,
|
||||
)
|
||||
.expect("An error occurred while fuzzing");
|
||||
}
|
||||
|
||||
/// Not supported on windows right now
|
||||
#[cfg(windows)]
|
||||
fn fuzz(_corpus_dirs: Vec<PathBuf>, _objective_dir: PathBuf, _broker_port: u16) -> Result<(), ()> {
|
||||
todo!("Example not supported on Windows");
|
||||
}
|
||||
|
||||
/// The actual fuzzer
|
||||
#[cfg(unix)]
|
||||
fn fuzz(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);
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
// Create an observation channel using the coverage map
|
||||
let edges_observer = HitcountsMapObserver::new(unsafe {
|
||||
StdMapObserver::new_from_ptr("edges", __lafl_edges_map, __lafl_max_edges_size as usize)
|
||||
});
|
||||
|
||||
// Create an observation channel using the cmp map
|
||||
let cmps_observer = unsafe { StdMapObserver::new_from_ptr("cmps", __lafl_cmp_map, MAP_SIZE) };
|
||||
|
||||
// Create an observation channel using the allocations map
|
||||
let allocs_observer =
|
||||
unsafe { StdMapObserver::new_from_ptr("allocs", __lafl_alloc_map, MAP_SIZE) };
|
||||
|
||||
// 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),
|
||||
MaxMapFeedback::new_with_observer(&cmps_observer),
|
||||
MaxMapFeedback::new_with_observer(&allocs_observer),
|
||||
TimeFeedback::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).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 = InProcessExecutor::new(
|
||||
"in-process(edges,cmps,allocs)",
|
||||
harness,
|
||||
tuple_list!(
|
||||
edges_observer,
|
||||
cmps_observer,
|
||||
allocs_observer,
|
||||
TimeObserver::new("time")
|
||||
),
|
||||
&mut state,
|
||||
&mut restarting_mgr,
|
||||
)?;
|
||||
|
||||
// The actual target run starts here.
|
||||
// Call LLVMFUzzerInitialize() if present.
|
||||
unsafe {
|
||||
if afl_libfuzzer_init() == -1 {
|
||||
println!("Warning: LLVMFuzzerInitialize failed with -1")
|
||||
}
|
||||
}
|
||||
|
||||
// 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(())
|
||||
}
|
17
fuzzers/libfuzzer_libpng_cmpalloc/test.sh
Executable file
17
fuzzers/libfuzzer_libpng_cmpalloc/test.sh
Executable file
@ -0,0 +1,17 @@
|
||||
#!/bin/sh
|
||||
|
||||
mkdir -p ./crashes
|
||||
|
||||
cargo build --example libfuzzer_libpng --release || exit 1
|
||||
cp ../../target/release/examples/libfuzzer_libpng ./.libfuzzer_test.elf
|
||||
|
||||
# The broker
|
||||
RUST_BACKTRACE=full taskset 0 ./.libfuzzer_test.elf &
|
||||
# Give the broker time to spawn
|
||||
sleep 2
|
||||
echo "Spawning client"
|
||||
# The 1st fuzzer client, pin to cpu 0x1
|
||||
RUST_BACKTRACE=full taskset 1 ./.libfuzzer_test.elf 2>/dev/null
|
||||
|
||||
killall .libfuzzer_test.elf
|
||||
rm -rf ./.libfuzzer_test.elf
|
@ -3,7 +3,7 @@
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#define MAP_SIZE 65536
|
||||
#define MAP_SIZE (16*1024)
|
||||
|
||||
int orig_argc;
|
||||
char **orig_argv;
|
||||
@ -156,9 +156,10 @@ void *calloc(size_t nmemb, size_t size) {
|
||||
k &= MAP_SIZE - 1;
|
||||
__lafl_alloc_map[k] = MAX(__lafl_alloc_map[k], size);
|
||||
|
||||
void *result = realloc(NULL, size);
|
||||
memset(result, 0, size);
|
||||
return result;
|
||||
void *ret = NULL;
|
||||
posix_memalign(&ret, 1<<6, size);
|
||||
memset(ret, 0, size);
|
||||
return ret;
|
||||
|
||||
}
|
||||
|
||||
|
@ -35,6 +35,7 @@ std = [] # print, sharedmap, ... support
|
||||
runtime = [] # a runtime for clang inmem-executor
|
||||
anymapdbg = ["serde_json"] # uses serde_json to Debug the anymap trait. Disable for smaller footprint.
|
||||
derive = ["libafl_derive"] # provide derive(SerdeAny) macro.
|
||||
llmp_small_maps = [] # reduces initial map size for llmp
|
||||
|
||||
[[example]]
|
||||
name = "llmp_test"
|
||||
@ -59,9 +60,11 @@ num_enum = "0.5.1"
|
||||
[target.'cfg(unix)'.dependencies]
|
||||
libc = "0.2" # For (*nix) libc
|
||||
nix = "0.20.0"
|
||||
uds = "0.2.3"
|
||||
|
||||
[target.'cfg(windows)'.dependencies]
|
||||
windows = "0.3.1"
|
||||
windows = "0.4.0"
|
||||
uuid = { version = "0.8", features = ["v4"] }
|
||||
|
||||
[target.'cfg(windows)'.build-dependencies]
|
||||
windows = "0.3.1"
|
||||
windows = "0.4.0"
|
||||
|
@ -1,9 +1,10 @@
|
||||
fn main() {
|
||||
#[cfg(target_os = "windows")]
|
||||
windows::build!(
|
||||
windows::win32::system_services::HANDLE,
|
||||
windows::win32::system_services::{HANDLE, BOOL, PAGE_TYPE, PSTR, ExitProcess},
|
||||
windows::win32::windows_programming::CloseHandle,
|
||||
// API needed for the shared memory
|
||||
windows::win32::system_services::{CreateFileMappingA, OpenFileMappingA, MapViewOfFile, UnmapViewOfFile},
|
||||
windows::win32::debug::{SetUnhandledExceptionFilter, EXCEPTION_POINTERS, EXCEPTION_RECORD, LPTOP_LEVEL_EXCEPTION_FILTER}
|
||||
);
|
||||
}
|
||||
|
@ -8,14 +8,16 @@ use core::{convert::TryInto, time::Duration};
|
||||
#[cfg(all(unix, feature = "std"))]
|
||||
use std::{thread, time};
|
||||
|
||||
use libafl::bolts::llmp::Tag;
|
||||
#[cfg(all(unix, feature = "std"))]
|
||||
use libafl::{
|
||||
bolts::{llmp, shmem::UnixShMem},
|
||||
Error,
|
||||
};
|
||||
|
||||
const _TAG_SIMPLE_U32_V1: u32 = 0x51300321;
|
||||
const _TAG_MATH_RESULT_V1: u32 = 0x77474331;
|
||||
const _TAG_SIMPLE_U32_V1: Tag = 0x51300321;
|
||||
const _TAG_MATH_RESULT_V1: Tag = 0x77474331;
|
||||
const _TAG_1MEG_V1: Tag = 0xB1111161;
|
||||
|
||||
#[cfg(all(unix, feature = "std"))]
|
||||
fn adder_loop(port: u16) -> ! {
|
||||
@ -35,7 +37,11 @@ fn adder_loop(port: u16) -> ! {
|
||||
current_result =
|
||||
current_result.wrapping_add(u32::from_le_bytes(buf.try_into().unwrap()));
|
||||
}
|
||||
_ => println!("Adder Client ignored unknown message {}", tag),
|
||||
_ => println!(
|
||||
"Adder Client ignored unknown message {} with {} bytes",
|
||||
tag,
|
||||
buf.len()
|
||||
),
|
||||
};
|
||||
}
|
||||
|
||||
@ -55,6 +61,19 @@ fn adder_loop(port: u16) -> ! {
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(all(unix, feature = "std"))]
|
||||
fn large_msg_loop(port: u16) -> ! {
|
||||
let mut client = llmp::LlmpClient::<UnixShMem>::create_attach_to_tcp(port).unwrap();
|
||||
|
||||
let meg_buf = [1u8; 1 << 20];
|
||||
|
||||
loop {
|
||||
client.send_buf(_TAG_1MEG_V1, &meg_buf).unwrap();
|
||||
println!("Sending the next megabyte");
|
||||
thread::sleep(time::Duration::from_millis(100))
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(all(unix, feature = "std"))]
|
||||
fn broker_message_hook(
|
||||
client_id: u32,
|
||||
@ -95,7 +114,7 @@ fn main() {
|
||||
|
||||
let mode = std::env::args()
|
||||
.nth(1)
|
||||
.expect("no mode specified, chose 'broker', 'ctr', or 'adder'");
|
||||
.expect("no mode specified, chose 'broker', 'ctr', 'adder', or 'large'");
|
||||
let port: u16 = std::env::args()
|
||||
.nth(2)
|
||||
.unwrap_or("1337".into())
|
||||
@ -128,6 +147,9 @@ fn main() {
|
||||
"adder" => {
|
||||
adder_loop(port);
|
||||
}
|
||||
"large" => {
|
||||
large_msg_loop(port);
|
||||
}
|
||||
_ => {
|
||||
println!("No valid mode supplied");
|
||||
}
|
||||
|
@ -92,6 +92,9 @@ use std::{
|
||||
#[cfg(all(feature = "std", unix))]
|
||||
use libc::c_char;
|
||||
|
||||
#[cfg(unix)]
|
||||
use uds::{UnixListenerExt, UnixSocketAddr, UnixStreamExt};
|
||||
|
||||
#[cfg(unix)]
|
||||
use crate::bolts::os::unix_signals::{c_void, setup_signal_handler, siginfo_t, Handler, Signal};
|
||||
use crate::{
|
||||
@ -102,20 +105,26 @@ use crate::{
|
||||
use super::shmem::HasFd;
|
||||
|
||||
/// We'll start off with 256 megabyte maps per fuzzer client
|
||||
const LLMP_PREF_INITIAL_MAP_SIZE: usize = 1 << 28;
|
||||
#[cfg(not(feature = "llmp_small_maps"))]
|
||||
const LLMP_CFG_INITIAL_MAP_SIZE: usize = 1 << 28;
|
||||
/// If llmp_small_maps is set, we start off with 1 meg.
|
||||
#[cfg(feature = "llmp_small_maps")]
|
||||
const LLMP_CFG_INITIAL_MAP_SIZE: usize = 1 << 20;
|
||||
/// What byte count to align messages to
|
||||
/// LlmpMsg sizes (including header) will always be rounded up to be a multiple of this value
|
||||
const LLMP_PREF_ALIGNNMENT: usize = 64;
|
||||
const LLMP_CFG_ALIGNNMENT: usize = 64;
|
||||
|
||||
/// A msg fresh from the press: No tag got sent by the user yet
|
||||
const LLMP_TAG_UNSET: u32 = 0xDEADAF;
|
||||
const LLMP_TAG_UNSET: Tag = 0xDEADAF;
|
||||
/// This message should not exist yet. Some bug in unsafe code!
|
||||
const LLMP_TAG_UNINITIALIZED: u32 = 0xA143AF11;
|
||||
/// The end of page mesasge
|
||||
const LLMP_TAG_UNINITIALIZED: Tag = 0xA143AF11;
|
||||
/// The end of page message
|
||||
/// When receiving this, a new sharedmap needs to be allocated.
|
||||
const LLMP_TAG_END_OF_PAGE: u32 = 0xAF1E0F1;
|
||||
/// A new client for this broekr got added.
|
||||
const LLMP_TAG_NEW_SHM_CLIENT: u32 = 0xC11E471;
|
||||
const LLMP_TAG_END_OF_PAGE: Tag = 0xAF1E0F1;
|
||||
/// A new client for this broker got added.
|
||||
const LLMP_TAG_NEW_SHM_CLIENT: Tag = 0xC11E471;
|
||||
/// The sender on this map is exiting (if broker exits, clients should exit gracefully);
|
||||
const LLMP_TAG_EXITING: Tag = 0x13C5171;
|
||||
|
||||
/// An env var of this value indicates that the set value was a NULL PTR
|
||||
const _NULL_ENV_STR: &str = "_NULL";
|
||||
@ -197,19 +206,19 @@ unsafe fn llmp_msg_in_page(page: *const LlmpPage, msg: *const LlmpMsg) -> bool {
|
||||
&& (page as *const u8).add((*page).size_total) > msg as *const u8
|
||||
}
|
||||
|
||||
/// allign to LLMP_PREF_ALIGNNMENT=64 bytes
|
||||
/// allign to LLMP_CFG_ALIGNNMENT=64 bytes
|
||||
#[inline]
|
||||
const fn llmp_align(to_align: usize) -> usize {
|
||||
// check if we need to align first
|
||||
if LLMP_PREF_ALIGNNMENT == 0 {
|
||||
if LLMP_CFG_ALIGNNMENT == 0 {
|
||||
return to_align;
|
||||
}
|
||||
// Then do the alignment
|
||||
let modulo = to_align % LLMP_PREF_ALIGNNMENT;
|
||||
let modulo = to_align % LLMP_CFG_ALIGNNMENT;
|
||||
if modulo == 0 {
|
||||
to_align
|
||||
} else {
|
||||
to_align + LLMP_PREF_ALIGNNMENT - modulo
|
||||
to_align + LLMP_CFG_ALIGNNMENT - modulo
|
||||
}
|
||||
}
|
||||
|
||||
@ -233,7 +242,7 @@ fn msg_offset_from_env(env_name: &str) -> Result<Option<u64>, Error> {
|
||||
fn new_map_size(max_alloc: usize) -> usize {
|
||||
max(
|
||||
max_alloc * 2 + EOP_MSG_SIZE + LLMP_PAGE_HEADER_LEN,
|
||||
LLMP_PREF_INITIAL_MAP_SIZE,
|
||||
LLMP_CFG_INITIAL_MAP_SIZE,
|
||||
)
|
||||
.next_power_of_two()
|
||||
}
|
||||
@ -447,7 +456,7 @@ where
|
||||
{
|
||||
#[cfg(all(feature = "std", unix))]
|
||||
pub fn on_domain_socket(filename: &str) -> Result<Self, Error> {
|
||||
match UnixListener::bind(filename) {
|
||||
match UnixListener::bind_unix_addr(&UnixSocketAddr::new(filename).unwrap()) {
|
||||
Ok(listener) => {
|
||||
dbg!("We're the broker");
|
||||
let mut broker = LlmpBroker::new()?;
|
||||
@ -541,7 +550,7 @@ where
|
||||
last_msg_sent: ptr::null_mut(),
|
||||
out_maps: vec![LlmpSharedMap::new(
|
||||
0,
|
||||
SH::new_map(new_map_size(LLMP_PREF_INITIAL_MAP_SIZE))?,
|
||||
SH::new_map(new_map_size(LLMP_CFG_INITIAL_MAP_SIZE))?,
|
||||
)],
|
||||
// drop pages to the broker if it already read them
|
||||
keep_pages_forever,
|
||||
@ -571,7 +580,7 @@ where
|
||||
pub fn to_env(&self, env_name: &str) -> Result<(), Error> {
|
||||
let current_out_map = self.out_maps.last().unwrap();
|
||||
current_out_map.shmem.write_to_env(env_name)?;
|
||||
current_out_map.msg_to_env(self.last_msg_sent, env_name)
|
||||
unsafe { current_out_map.msg_to_env(self.last_msg_sent, env_name) }
|
||||
}
|
||||
|
||||
/// Waits for this sender to be save to unmap.
|
||||
@ -862,6 +871,7 @@ where
|
||||
tag
|
||||
)));
|
||||
}
|
||||
|
||||
unsafe {
|
||||
let msg = self.alloc_next(buf.len())?;
|
||||
(*msg).tag = tag;
|
||||
@ -927,7 +937,7 @@ where
|
||||
pub fn to_env(&self, env_name: &str) -> Result<(), Error> {
|
||||
let current_out_map = &self.current_recv_map;
|
||||
current_out_map.shmem.write_to_env(env_name)?;
|
||||
current_out_map.msg_to_env(self.last_msg_recvd, env_name)
|
||||
unsafe { current_out_map.msg_to_env(self.last_msg_recvd, env_name) }
|
||||
}
|
||||
|
||||
/// Create a Receiver, reattaching to an existing sender map.
|
||||
@ -987,6 +997,11 @@ where
|
||||
// Handle special, LLMP internal, messages.
|
||||
match (*msg).tag {
|
||||
LLMP_TAG_UNSET => panic!("BUG: Read unallocated msg"),
|
||||
LLMP_TAG_EXITING => {
|
||||
// The other side is done.
|
||||
assert_eq!((*msg).buf_len, 0);
|
||||
return Err(Error::ShuttingDown);
|
||||
}
|
||||
LLMP_TAG_END_OF_PAGE => {
|
||||
#[cfg(feature = "std")]
|
||||
dbg!("Got end of page, allocing next");
|
||||
@ -1195,14 +1210,16 @@ where
|
||||
|
||||
/// Store this msg offset to env_name + _OFFSET env variable.
|
||||
/// It can be restored using msg_from_env with the same env_name later.
|
||||
/// # Safety
|
||||
/// This function will dereference the msg ptr, make sure it's valid.
|
||||
#[cfg(feature = "std")]
|
||||
pub fn msg_to_env(&self, msg: *const LlmpMsg, map_env_name: &str) -> Result<(), Error> {
|
||||
pub unsafe fn msg_to_env(&self, msg: *const LlmpMsg, map_env_name: &str) -> Result<(), Error> {
|
||||
if msg.is_null() {
|
||||
env::set_var(&format!("{}_OFFSET", map_env_name), _NULL_ENV_STR)
|
||||
} else {
|
||||
env::set_var(
|
||||
&format!("{}_OFFSET", map_env_name),
|
||||
format!("{}", unsafe { self.msg_to_offset(msg) }?),
|
||||
format!("{}", self.msg_to_offset(msg)?),
|
||||
)
|
||||
};
|
||||
Ok(())
|
||||
@ -1319,9 +1336,8 @@ where
|
||||
msg.copy_to_nonoverlapping(out, size_of::<LlmpMsg>() + (*msg).buf_len_padded as usize);
|
||||
(*out).buf_len_padded = actual_size;
|
||||
/* We need to replace the message ID with our own */
|
||||
match self.llmp_out.send(out) {
|
||||
Err(e) => panic!("Error sending msg: {:?}", e),
|
||||
_ => (),
|
||||
if let Err(e) = self.llmp_out.send(out) {
|
||||
panic!("Error sending msg: {:?}", e)
|
||||
};
|
||||
self.llmp_out.last_msg_sent = out;
|
||||
Ok(())
|
||||
@ -1389,6 +1405,9 @@ where
|
||||
None => (),
|
||||
}
|
||||
}
|
||||
self.llmp_out
|
||||
.send_buf(LLMP_TAG_EXITING, &[])
|
||||
.expect("Error when shutting down broker: Could not send LLMP_TAG_EXITING msg.");
|
||||
}
|
||||
|
||||
/// Broadcasts the given buf to all lients
|
||||
@ -1422,7 +1441,7 @@ where
|
||||
// Tcp out map sends messages from background thread tcp server to foreground client
|
||||
let tcp_out_map = LlmpSharedMap::new(
|
||||
llmp_tcp_id,
|
||||
SH::new_map(new_map_size(LLMP_PREF_INITIAL_MAP_SIZE))?,
|
||||
SH::new_map(new_map_size(LLMP_CFG_INITIAL_MAP_SIZE))?,
|
||||
);
|
||||
let tcp_out_map_str = tcp_out_map.shmem.shm_str();
|
||||
let tcp_out_map_size = tcp_out_map.shmem.map().len();
|
||||
@ -1465,7 +1484,7 @@ where
|
||||
(*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_PREF_INITIAL_MAP_SIZE;
|
||||
(*pageinfo).map_size = LLMP_CFG_INITIAL_MAP_SIZE;
|
||||
match new_client_sender.send(msg) {
|
||||
Ok(()) => (),
|
||||
Err(e) => println!("Error forwarding client on map: {:?}", e),
|
||||
@ -1481,10 +1500,12 @@ where
|
||||
.to_string_lossy()
|
||||
.into_owned()
|
||||
.parse()
|
||||
.expect(&format!(
|
||||
"ShmId is not a valid int file descriptor: {:?}",
|
||||
broadcast_str_initial
|
||||
));
|
||||
.unwrap_or_else(|_| {
|
||||
panic!(
|
||||
"ShmId is not a valid int file descriptor: {:?}",
|
||||
broadcast_str_initial
|
||||
)
|
||||
});
|
||||
|
||||
match sendmsg(
|
||||
stream.as_raw_fd(),
|
||||
@ -1528,7 +1549,7 @@ where
|
||||
let pageinfo =
|
||||
(*msg).buf.as_mut_ptr() as *mut LlmpPayloadSharedMapInfo;
|
||||
(*pageinfo).shm_str = fdstr;
|
||||
(*pageinfo).map_size = LLMP_PREF_INITIAL_MAP_SIZE;
|
||||
(*pageinfo).map_size = LLMP_CFG_INITIAL_MAP_SIZE;
|
||||
match new_client_sender.send(msg) {
|
||||
Ok(()) => (),
|
||||
Err(e) => {
|
||||
@ -1739,7 +1760,7 @@ where
|
||||
last_msg_sent: ptr::null_mut(),
|
||||
out_maps: vec![LlmpSharedMap::new(
|
||||
0,
|
||||
SH::new_map(new_map_size(LLMP_PREF_INITIAL_MAP_SIZE))?,
|
||||
SH::new_map(new_map_size(LLMP_CFG_INITIAL_MAP_SIZE))?,
|
||||
)],
|
||||
// drop pages to the broker if it already read them
|
||||
keep_pages_forever: false,
|
||||
@ -1840,7 +1861,7 @@ where
|
||||
|
||||
let ret = Self::new(LlmpSharedMap::existing(SH::existing_from_shm_slice(
|
||||
&new_broker_map_str,
|
||||
LLMP_PREF_INITIAL_MAP_SIZE,
|
||||
LLMP_CFG_INITIAL_MAP_SIZE,
|
||||
)?))?;
|
||||
|
||||
stream.write_all(ret.sender.out_maps.first().unwrap().shmem.shm_slice())?;
|
||||
@ -1858,7 +1879,7 @@ where
|
||||
#[cfg(all(unix, feature = "std"))]
|
||||
/// Create a LlmpClient, getting the ID from a given filename
|
||||
pub fn create_attach_to_unix(filename: &str) -> Result<Self, Error> {
|
||||
let stream = UnixStream::connect(filename)?;
|
||||
let stream = UnixStream::connect_to_unix_addr(&UnixSocketAddr::new(filename).unwrap())?;
|
||||
println!("Connected to socket {}", filename);
|
||||
|
||||
let mut buf = [0u8; 5];
|
||||
@ -1884,7 +1905,7 @@ where
|
||||
|
||||
let ret = Self::new(LlmpSharedMap::existing(SH::existing_from_shm_slice(
|
||||
&fdstr,
|
||||
LLMP_PREF_INITIAL_MAP_SIZE,
|
||||
LLMP_CFG_INITIAL_MAP_SIZE,
|
||||
)?))?;
|
||||
|
||||
match sendmsg(
|
||||
|
@ -1,2 +1,4 @@
|
||||
#[cfg(unix)]
|
||||
pub mod unix_signals;
|
||||
#[cfg(windows)]
|
||||
pub mod windows_exceptions;
|
||||
|
@ -22,7 +22,7 @@ use crate::Error;
|
||||
|
||||
pub use libc::{c_void, siginfo_t};
|
||||
|
||||
#[derive(IntoPrimitive, TryFromPrimitive, Hash, Clone, Copy)]
|
||||
#[derive(IntoPrimitive, TryFromPrimitive, Clone, Copy)]
|
||||
#[repr(i32)]
|
||||
pub enum Signal {
|
||||
SigAbort = SIGABRT,
|
||||
|
332
libafl/src/bolts/os/windows_exceptions.rs
Normal file
332
libafl/src/bolts/os/windows_exceptions.rs
Normal file
@ -0,0 +1,332 @@
|
||||
pub use crate::bolts::bindings::windows::win32::debug::EXCEPTION_POINTERS;
|
||||
|
||||
use crate::{bolts::bindings::windows::win32::debug::SetUnhandledExceptionFilter, Error};
|
||||
|
||||
use alloc::vec::Vec;
|
||||
use core::{
|
||||
cell::UnsafeCell,
|
||||
convert::TryFrom,
|
||||
fmt::{self, Display, Formatter},
|
||||
ptr::write_volatile,
|
||||
sync::atomic::{compiler_fence, Ordering},
|
||||
};
|
||||
use std::os::raw::{c_long, c_void};
|
||||
|
||||
use num_enum::{IntoPrimitive, TryFromPrimitive};
|
||||
|
||||
const EXCEPTION_CONTINUE_EXECUTION: c_long = -1;
|
||||
//const EXCEPTION_CONTINUE_SEARCH: c_long = 0;
|
||||
const EXCEPTION_EXECUTE_HANDLER: c_long = 1;
|
||||
|
||||
// From https://github.com/wine-mirror/wine/blob/master/include/winnt.h#L611
|
||||
pub const STATUS_WAIT_0: u32 = 0x00000000;
|
||||
pub const STATUS_ABANDONED_WAIT_0: u32 = 0x00000080;
|
||||
pub const STATUS_USER_APC: u32 = 0x000000C0;
|
||||
pub const STATUS_TIMEOUT: u32 = 0x00000102;
|
||||
pub const STATUS_PENDING: u32 = 0x00000103;
|
||||
pub const STATUS_SEGMENT_NOTIFICATION: u32 = 0x40000005;
|
||||
pub const STATUS_FATAL_APP_EXIT: u32 = 0x40000015;
|
||||
pub const STATUS_GUARD_PAGE_VIOLATION: u32 = 0x80000001;
|
||||
pub const STATUS_DATATYPE_MISALIGNMENT: u32 = 0x80000002;
|
||||
pub const STATUS_BREAKPOINT: u32 = 0x80000003;
|
||||
pub const STATUS_SINGLE_STEP: u32 = 0x80000004;
|
||||
pub const STATUS_LONGJUMP: u32 = 0x80000026;
|
||||
pub const STATUS_UNWIND_CONSOLIDATE: u32 = 0x80000029;
|
||||
pub const STATUS_ACCESS_VIOLATION: u32 = 0xC0000005;
|
||||
pub const STATUS_IN_PAGE_ERROR: u32 = 0xC0000006;
|
||||
pub const STATUS_INVALID_HANDLE: u32 = 0xC0000008;
|
||||
pub const STATUS_NO_MEMORY: u32 = 0xC0000017;
|
||||
pub const STATUS_ILLEGAL_INSTRUCTION: u32 = 0xC000001D;
|
||||
pub const STATUS_NONCONTINUABLE_EXCEPTION: u32 = 0xC0000025;
|
||||
pub const STATUS_INVALID_DISPOSITION: u32 = 0xC0000026;
|
||||
pub const STATUS_ARRAY_BOUNDS_EXCEEDED: u32 = 0xC000008C;
|
||||
pub const STATUS_FLOAT_DENORMAL_OPERAND: u32 = 0xC000008D;
|
||||
pub const STATUS_FLOAT_DIVIDE_BY_ZERO: u32 = 0xC000008E;
|
||||
pub const STATUS_FLOAT_INEXACT_RESULT: u32 = 0xC000008F;
|
||||
pub const STATUS_FLOAT_INVALID_OPERATION: u32 = 0xC0000090;
|
||||
pub const STATUS_FLOAT_OVERFLOW: u32 = 0xC0000091;
|
||||
pub const STATUS_FLOAT_STACK_CHECK: u32 = 0xC0000092;
|
||||
pub const STATUS_FLOAT_UNDERFLOW: u32 = 0xC0000093;
|
||||
pub const STATUS_INTEGER_DIVIDE_BY_ZERO: u32 = 0xC0000094;
|
||||
pub const STATUS_INTEGER_OVERFLOW: u32 = 0xC0000095;
|
||||
pub const STATUS_PRIVILEGED_INSTRUCTION: u32 = 0xC0000096;
|
||||
pub const STATUS_STACK_OVERFLOW: u32 = 0xC00000FD;
|
||||
pub const STATUS_DLL_NOT_FOUND: u32 = 0xC0000135;
|
||||
pub const STATUS_ORDINAL_NOT_FOUND: u32 = 0xC0000138;
|
||||
pub const STATUS_ENTRYPOINT_NOT_FOUND: u32 = 0xC0000139;
|
||||
pub const STATUS_CONTROL_C_EXIT: u32 = 0xC000013A;
|
||||
pub const STATUS_DLL_INIT_FAILED: u32 = 0xC0000142;
|
||||
pub const STATUS_FLOAT_MULTIPLE_FAULTS: u32 = 0xC00002B4;
|
||||
pub const STATUS_FLOAT_MULTIPLE_TRAPS: u32 = 0xC00002B5;
|
||||
pub const STATUS_REG_NAT_CONSUMPTION: u32 = 0xC00002C9;
|
||||
pub const STATUS_HEAP_CORRUPTION: u32 = 0xC0000374;
|
||||
pub const STATUS_STACK_BUFFER_OVERRUN: u32 = 0xC0000409;
|
||||
pub const STATUS_INVALID_CRUNTIME_PARAMETER: u32 = 0xC0000417;
|
||||
pub const STATUS_ASSERTION_FAILURE: u32 = 0xC0000420;
|
||||
pub const STATUS_SXS_EARLY_DEACTIVATION: u32 = 0xC015000F;
|
||||
pub const STATUS_SXS_INVALID_DEACTIVATION: u32 = 0xC0150010;
|
||||
|
||||
#[derive(IntoPrimitive, TryFromPrimitive, Clone, Copy)]
|
||||
#[repr(u32)]
|
||||
pub enum ExceptionCode {
|
||||
// From https://docs.microsoft.com/en-us/windows/win32/debug/getexceptioncode
|
||||
AccessViolation = STATUS_ACCESS_VIOLATION,
|
||||
ArrayBoundsExceeded = STATUS_ARRAY_BOUNDS_EXCEEDED,
|
||||
Breakpoint = STATUS_BREAKPOINT,
|
||||
DatatypeMisalignment = STATUS_DATATYPE_MISALIGNMENT,
|
||||
FltDenormalOperand = STATUS_FLOAT_DENORMAL_OPERAND,
|
||||
FltDivideByZero = STATUS_FLOAT_DIVIDE_BY_ZERO,
|
||||
FltInexactResult = STATUS_FLOAT_INEXACT_RESULT,
|
||||
FltInvalidOperation = STATUS_FLOAT_INVALID_OPERATION,
|
||||
FltOverflow = STATUS_FLOAT_OVERFLOW,
|
||||
FltStackCheck = STATUS_FLOAT_STACK_CHECK,
|
||||
FltUnderflow = STATUS_FLOAT_UNDERFLOW,
|
||||
GuardPageViolation = STATUS_GUARD_PAGE_VIOLATION,
|
||||
IllegalInstruction = STATUS_ILLEGAL_INSTRUCTION,
|
||||
InPageError = STATUS_IN_PAGE_ERROR,
|
||||
IntegerDivideByZero = STATUS_INTEGER_DIVIDE_BY_ZERO,
|
||||
IntegerOverflow = STATUS_INTEGER_OVERFLOW,
|
||||
InvalidDisposition = STATUS_INVALID_DISPOSITION,
|
||||
InvalidHandle = STATUS_INVALID_HANDLE,
|
||||
NoncontinuableException = STATUS_NONCONTINUABLE_EXCEPTION,
|
||||
PrivilegedInstruction = STATUS_PRIVILEGED_INSTRUCTION,
|
||||
SingleStep = STATUS_SINGLE_STEP,
|
||||
StackOverflow = STATUS_STACK_OVERFLOW,
|
||||
UnwindConsolidate = STATUS_UNWIND_CONSOLIDATE,
|
||||
// Addition exceptions
|
||||
Wait0 = STATUS_WAIT_0,
|
||||
AbandonedWait0 = STATUS_ABANDONED_WAIT_0,
|
||||
UserAPC = STATUS_USER_APC,
|
||||
Timeout = STATUS_TIMEOUT,
|
||||
Pending = STATUS_PENDING,
|
||||
SegmentNotification = STATUS_SEGMENT_NOTIFICATION,
|
||||
FatalAppExit = STATUS_FATAL_APP_EXIT,
|
||||
Longjump = STATUS_LONGJUMP,
|
||||
DLLNotFound = STATUS_DLL_NOT_FOUND,
|
||||
OrdinalNotFound = STATUS_ORDINAL_NOT_FOUND,
|
||||
EntryPointNotFound = STATUS_ENTRYPOINT_NOT_FOUND,
|
||||
ControlCExit = STATUS_CONTROL_C_EXIT,
|
||||
DllInitFailed = STATUS_DLL_INIT_FAILED,
|
||||
FltMultipleFaults = STATUS_FLOAT_MULTIPLE_FAULTS,
|
||||
FltMultipleTraps = STATUS_FLOAT_MULTIPLE_TRAPS,
|
||||
RegNatConsumption = STATUS_REG_NAT_CONSUMPTION,
|
||||
HeapCorruption = STATUS_HEAP_CORRUPTION,
|
||||
StackBufferOverrun = STATUS_STACK_BUFFER_OVERRUN,
|
||||
InvalidCRuntimeParameter = STATUS_INVALID_CRUNTIME_PARAMETER,
|
||||
AssertionFailure = STATUS_ASSERTION_FAILURE,
|
||||
SXSEarlyDeactivation = STATUS_SXS_EARLY_DEACTIVATION,
|
||||
SXSInvalidDeactivation = STATUS_SXS_INVALID_DEACTIVATION,
|
||||
}
|
||||
|
||||
pub static CRASH_EXCEPTIONS: &[ExceptionCode] = &[
|
||||
ExceptionCode::AccessViolation,
|
||||
ExceptionCode::ArrayBoundsExceeded,
|
||||
ExceptionCode::FltDivideByZero,
|
||||
ExceptionCode::GuardPageViolation,
|
||||
ExceptionCode::IllegalInstruction,
|
||||
ExceptionCode::InPageError,
|
||||
ExceptionCode::IntegerDivideByZero,
|
||||
ExceptionCode::InvalidHandle,
|
||||
ExceptionCode::NoncontinuableException,
|
||||
ExceptionCode::PrivilegedInstruction,
|
||||
ExceptionCode::StackOverflow,
|
||||
ExceptionCode::HeapCorruption,
|
||||
ExceptionCode::StackBufferOverrun,
|
||||
ExceptionCode::AssertionFailure,
|
||||
];
|
||||
|
||||
impl PartialEq for ExceptionCode {
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
*self as u32 == *other as u32
|
||||
}
|
||||
}
|
||||
|
||||
impl Eq for ExceptionCode {}
|
||||
|
||||
unsafe impl Sync for ExceptionCode {}
|
||||
|
||||
impl Display for ExceptionCode {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), fmt::Error> {
|
||||
match self {
|
||||
ExceptionCode::AccessViolation => write!(f, "STATUS_ACCESS_VIOLATION")?,
|
||||
ExceptionCode::ArrayBoundsExceeded => write!(f, "STATUS_ARRAY_BOUNDS_EXCEEDED")?,
|
||||
ExceptionCode::Breakpoint => write!(f, "STATUS_BREAKPOINT")?,
|
||||
ExceptionCode::DatatypeMisalignment => write!(f, "STATUS_DATATYPE_MISALIGNMENT")?,
|
||||
ExceptionCode::FltDenormalOperand => write!(f, "STATUS_FLOAT_DENORMAL_OPERAND")?,
|
||||
ExceptionCode::FltDivideByZero => write!(f, "STATUS_FLOAT_DIVIDE_BY_ZERO")?,
|
||||
ExceptionCode::FltInexactResult => write!(f, "STATUS_FLOAT_INEXACT_RESULT")?,
|
||||
ExceptionCode::FltInvalidOperation => write!(f, "STATUS_FLOAT_INVALID_OPERATION")?,
|
||||
ExceptionCode::FltOverflow => write!(f, "STATUS_FLOAT_OVERFLOW")?,
|
||||
ExceptionCode::FltStackCheck => write!(f, "STATUS_FLOAT_STACK_CHECK")?,
|
||||
ExceptionCode::FltUnderflow => write!(f, "STATUS_FLOAT_UNDERFLOW")?,
|
||||
ExceptionCode::GuardPageViolation => write!(f, "STATUS_GUARD_PAGE_VIOLATION")?,
|
||||
ExceptionCode::IllegalInstruction => write!(f, "STATUS_ILLEGAL_INSTRUCTION")?,
|
||||
ExceptionCode::InPageError => write!(f, "STATUS_IN_PAGE_ERROR")?,
|
||||
ExceptionCode::IntegerDivideByZero => write!(f, "STATUS_INTEGER_DIVIDE_BY_ZERO")?,
|
||||
ExceptionCode::IntegerOverflow => write!(f, "STATUS_INTEGER_OVERFLOW")?,
|
||||
ExceptionCode::InvalidDisposition => write!(f, "STATUS_INVALID_DISPOSITION")?,
|
||||
ExceptionCode::InvalidHandle => write!(f, "STATUS_INVALID_HANDLE")?,
|
||||
ExceptionCode::NoncontinuableException => write!(f, "STATUS_NONCONTINUABLE_EXCEPTION")?,
|
||||
ExceptionCode::PrivilegedInstruction => write!(f, "STATUS_PRIVILEGED_INSTRUCTION")?,
|
||||
ExceptionCode::SingleStep => write!(f, "STATUS_SINGLE_STEP")?,
|
||||
ExceptionCode::StackOverflow => write!(f, "STATUS_STACK_OVERFLOW")?,
|
||||
ExceptionCode::UnwindConsolidate => write!(f, "STATUS_UNWIND_CONSOLIDATE")?,
|
||||
ExceptionCode::Wait0 => write!(f, "STATUS_WAIT_0")?,
|
||||
ExceptionCode::AbandonedWait0 => write!(f, "STATUS_ABANDONED_WAIT_0")?,
|
||||
ExceptionCode::UserAPC => write!(f, "STATUS_USER_APC")?,
|
||||
ExceptionCode::Timeout => write!(f, "STATUS_TIMEOUT")?,
|
||||
ExceptionCode::Pending => write!(f, "STATUS_PENDING")?,
|
||||
ExceptionCode::SegmentNotification => write!(f, "STATUS_SEGMENT_NOTIFICATION")?,
|
||||
ExceptionCode::FatalAppExit => write!(f, "STATUS_FATAL_APP_EXIT")?,
|
||||
ExceptionCode::Longjump => write!(f, "STATUS_LONGJUMP")?,
|
||||
ExceptionCode::DLLNotFound => write!(f, "STATUS_DLL_NOT_FOUND")?,
|
||||
ExceptionCode::OrdinalNotFound => write!(f, "STATUS_ORDINAL_NOT_FOUND")?,
|
||||
ExceptionCode::EntryPointNotFound => write!(f, "STATUS_ENTRYPOINT_NOT_FOUND")?,
|
||||
ExceptionCode::ControlCExit => write!(f, "STATUS_CONTROL_C_EXIT")?,
|
||||
ExceptionCode::DllInitFailed => write!(f, "STATUS_DLL_INIT_FAILED")?,
|
||||
ExceptionCode::FltMultipleFaults => write!(f, "STATUS_FLOAT_MULTIPLE_FAULTS")?,
|
||||
ExceptionCode::FltMultipleTraps => write!(f, "STATUS_FLOAT_MULTIPLE_TRAPS")?,
|
||||
ExceptionCode::RegNatConsumption => write!(f, "STATUS_REG_NAT_CONSUMPTION")?,
|
||||
ExceptionCode::HeapCorruption => write!(f, "STATUS_HEAP_CORRUPTION")?,
|
||||
ExceptionCode::StackBufferOverrun => write!(f, "STATUS_STACK_BUFFER_OVERRUN")?,
|
||||
ExceptionCode::InvalidCRuntimeParameter => {
|
||||
write!(f, "STATUS_INVALID_CRUNTIME_PARAMETER")?
|
||||
}
|
||||
ExceptionCode::AssertionFailure => write!(f, "STATUS_ASSERTION_FAILURE")?,
|
||||
ExceptionCode::SXSEarlyDeactivation => write!(f, "STATUS_SXS_EARLY_DEACTIVATION")?,
|
||||
ExceptionCode::SXSInvalidDeactivation => write!(f, "STATUS_SXS_INVALID_DEACTIVATION")?,
|
||||
};
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
pub static EXCEPTION_CODES_MAPPING: [ExceptionCode; 45] = [
|
||||
ExceptionCode::AccessViolation,
|
||||
ExceptionCode::ArrayBoundsExceeded,
|
||||
ExceptionCode::Breakpoint,
|
||||
ExceptionCode::DatatypeMisalignment,
|
||||
ExceptionCode::FltDenormalOperand,
|
||||
ExceptionCode::FltDivideByZero,
|
||||
ExceptionCode::FltInexactResult,
|
||||
ExceptionCode::FltInvalidOperation,
|
||||
ExceptionCode::FltOverflow,
|
||||
ExceptionCode::FltStackCheck,
|
||||
ExceptionCode::FltUnderflow,
|
||||
ExceptionCode::GuardPageViolation,
|
||||
ExceptionCode::IllegalInstruction,
|
||||
ExceptionCode::InPageError,
|
||||
ExceptionCode::IntegerDivideByZero,
|
||||
ExceptionCode::IntegerOverflow,
|
||||
ExceptionCode::InvalidDisposition,
|
||||
ExceptionCode::InvalidHandle,
|
||||
ExceptionCode::NoncontinuableException,
|
||||
ExceptionCode::PrivilegedInstruction,
|
||||
ExceptionCode::SingleStep,
|
||||
ExceptionCode::StackOverflow,
|
||||
ExceptionCode::UnwindConsolidate,
|
||||
ExceptionCode::Wait0,
|
||||
ExceptionCode::AbandonedWait0,
|
||||
ExceptionCode::UserAPC,
|
||||
ExceptionCode::Timeout,
|
||||
ExceptionCode::Pending,
|
||||
ExceptionCode::SegmentNotification,
|
||||
ExceptionCode::FatalAppExit,
|
||||
ExceptionCode::Longjump,
|
||||
ExceptionCode::DLLNotFound,
|
||||
ExceptionCode::OrdinalNotFound,
|
||||
ExceptionCode::EntryPointNotFound,
|
||||
ExceptionCode::ControlCExit,
|
||||
ExceptionCode::DllInitFailed,
|
||||
ExceptionCode::FltMultipleFaults,
|
||||
ExceptionCode::FltMultipleTraps,
|
||||
ExceptionCode::RegNatConsumption,
|
||||
ExceptionCode::HeapCorruption,
|
||||
ExceptionCode::StackBufferOverrun,
|
||||
ExceptionCode::InvalidCRuntimeParameter,
|
||||
ExceptionCode::AssertionFailure,
|
||||
ExceptionCode::SXSEarlyDeactivation,
|
||||
ExceptionCode::SXSInvalidDeactivation,
|
||||
];
|
||||
|
||||
pub trait Handler {
|
||||
/// Handle an exception
|
||||
fn handle(
|
||||
&mut self,
|
||||
exception_code: ExceptionCode,
|
||||
exception_pointers: *mut EXCEPTION_POINTERS,
|
||||
);
|
||||
/// Return a list of exceptions to handle
|
||||
fn exceptions(&self) -> Vec<ExceptionCode>;
|
||||
}
|
||||
|
||||
struct HandlerHolder {
|
||||
handler: UnsafeCell<*mut dyn Handler>,
|
||||
}
|
||||
|
||||
unsafe impl Send for HandlerHolder {}
|
||||
|
||||
/// Keep track of which handler is registered for which exception
|
||||
static mut EXCEPTION_HANDLERS: [Option<HandlerHolder>; 64] = [
|
||||
None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None,
|
||||
None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None,
|
||||
None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None,
|
||||
None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None,
|
||||
];
|
||||
|
||||
type NativeHandlerType = extern "system" fn(*mut EXCEPTION_POINTERS) -> c_long;
|
||||
static mut PREVIOUS_HANDLER: Option<NativeHandlerType> = None;
|
||||
|
||||
/// Internal function that is being called whenever an exception arrives (stdcall).
|
||||
unsafe extern "system" fn handle_exception(exception_pointers: *mut EXCEPTION_POINTERS) -> c_long {
|
||||
let code = exception_pointers
|
||||
.as_mut()
|
||||
.unwrap()
|
||||
.exception_record
|
||||
.as_mut()
|
||||
.unwrap()
|
||||
.exception_code;
|
||||
let exception_code = ExceptionCode::try_from(code).unwrap();
|
||||
let index = EXCEPTION_CODES_MAPPING
|
||||
.iter()
|
||||
.position(|x| *x == exception_code)
|
||||
.unwrap();
|
||||
let ret = match &EXCEPTION_HANDLERS[index] {
|
||||
Some(handler_holder) => {
|
||||
let handler = &mut **handler_holder.handler.get();
|
||||
handler.handle(exception_code, exception_pointers);
|
||||
EXCEPTION_EXECUTE_HANDLER
|
||||
}
|
||||
None => EXCEPTION_EXECUTE_HANDLER,
|
||||
};
|
||||
if let Some(prev_handler) = PREVIOUS_HANDLER {
|
||||
prev_handler(exception_pointers)
|
||||
} else {
|
||||
ret
|
||||
}
|
||||
}
|
||||
|
||||
/// Setup Win32 exception handlers in a somewhat rusty way.
|
||||
pub unsafe fn setup_exception_handler<T: 'static + Handler>(handler: &mut T) -> Result<(), Error> {
|
||||
let exceptions = handler.exceptions();
|
||||
for exception_code in exceptions {
|
||||
let index = EXCEPTION_CODES_MAPPING
|
||||
.iter()
|
||||
.position(|x| *x == exception_code)
|
||||
.unwrap();
|
||||
write_volatile(
|
||||
&mut EXCEPTION_HANDLERS[index],
|
||||
Some(HandlerHolder {
|
||||
handler: UnsafeCell::new(handler as *mut dyn Handler),
|
||||
}),
|
||||
);
|
||||
}
|
||||
compiler_fence(Ordering::SeqCst);
|
||||
|
||||
if let Some(prev) = SetUnhandledExceptionFilter(Some(core::mem::transmute(
|
||||
handle_exception as *const c_void,
|
||||
))) {
|
||||
PREVIOUS_HANDLER = Some(core::mem::transmute(prev as *const c_void));
|
||||
}
|
||||
Ok(())
|
||||
}
|
@ -1,6 +1,11 @@
|
||||
//! Wrappers that abstracts references (or pointers) and owned data accesses.
|
||||
// The serialization is towards owned, allowing to serialize pointers without troubles.
|
||||
|
||||
use alloc::{boxed::Box, vec::Vec};
|
||||
use core::{clone::Clone, fmt::Debug};
|
||||
use serde::{Deserialize, Deserializer, Serialize, Serializer};
|
||||
|
||||
/// Wrap a reference and convert to a Box on serialize
|
||||
#[derive(Clone, Debug)]
|
||||
pub enum Ptr<'a, T: 'a + ?Sized> {
|
||||
Ref(&'a T),
|
||||
@ -40,6 +45,8 @@ impl<'a, T: Sized> AsRef<T> for Ptr<'a, T> {
|
||||
}
|
||||
}
|
||||
|
||||
/// Wrap a mutable reference and convert to a Box on serialize
|
||||
#[derive(Debug)]
|
||||
pub enum PtrMut<'a, T: 'a + ?Sized> {
|
||||
Ref(&'a mut T),
|
||||
Owned(Box<T>),
|
||||
@ -87,6 +94,8 @@ impl<'a, T: Sized> AsMut<T> for PtrMut<'a, T> {
|
||||
}
|
||||
}
|
||||
|
||||
/// Wrap a slice and convert to a Vec on serialize
|
||||
#[derive(Clone, Debug)]
|
||||
pub enum Slice<'a, T: 'a + Sized> {
|
||||
Ref(&'a [T]),
|
||||
Owned(Vec<T>),
|
||||
@ -125,6 +134,8 @@ impl<'a, T: Sized> Slice<'a, T> {
|
||||
}
|
||||
}
|
||||
|
||||
/// Wrap a mutable slice and convert to a Vec on serialize
|
||||
#[derive(Debug)]
|
||||
pub enum SliceMut<'a, T: 'a + Sized> {
|
||||
Ref(&'a mut [T]),
|
||||
Owned(Vec<T>),
|
||||
@ -170,6 +181,7 @@ impl<'a, T: Sized> SliceMut<'a, T> {
|
||||
}
|
||||
}
|
||||
|
||||
/// Wrap a C-style pointer and convert to a Box on serialize
|
||||
#[derive(Clone, Debug)]
|
||||
pub enum Cptr<T: Sized> {
|
||||
Cptr(*const T),
|
||||
@ -206,6 +218,8 @@ impl<T: Sized> AsRef<T> for Cptr<T> {
|
||||
}
|
||||
}
|
||||
|
||||
/// Wrap a C-style mutable pointer and convert to a Box on serialize
|
||||
#[derive(Clone, Debug)]
|
||||
pub enum CptrMut<T: Sized> {
|
||||
Cptr(*mut T),
|
||||
Owned(Box<T>),
|
||||
@ -250,6 +264,8 @@ impl<T: Sized> AsMut<T> for CptrMut<T> {
|
||||
}
|
||||
}
|
||||
|
||||
/// Wrap a C-style pointer to an array (with size= and convert to a Vec on serialize
|
||||
#[derive(Clone, Debug)]
|
||||
pub enum Array<T: Sized> {
|
||||
Cptr((*const T, usize)),
|
||||
Owned(Vec<T>),
|
||||
@ -285,6 +301,7 @@ impl<T: Sized> Array<T> {
|
||||
}
|
||||
}
|
||||
|
||||
/// Wrap a C-style mutable pointer to an array (with size= and convert to a Vec on serialize
|
||||
#[derive(Clone, Debug)]
|
||||
pub enum ArrayMut<T: Sized> {
|
||||
Cptr((*mut T, usize)),
|
||||
|
@ -1,4 +1,5 @@
|
||||
//! Poor-rust-man's downcasts for stuff we send over the wire (or shared maps)
|
||||
|
||||
use serde::{Deserialize, Deserializer, Serialize, Serializer};
|
||||
|
||||
use alloc::boxed::Box;
|
||||
|
@ -169,7 +169,6 @@ pub mod unix_shmem {
|
||||
|
||||
#[cfg(target_os = "android")]
|
||||
unsafe fn shmctl(__shmid: c_int, __cmd: c_int, _buf: *mut shmid_ds) -> c_int {
|
||||
println!("shmctl(__shmid: {})", __shmid);
|
||||
if __cmd == 0 {
|
||||
let length = ioctl(__shmid, ASHMEM_GET_SIZE);
|
||||
|
||||
@ -188,7 +187,10 @@ pub mod unix_shmem {
|
||||
|
||||
#[cfg(target_os = "android")]
|
||||
unsafe fn shmget(__key: c_int, __size: c_ulong, __shmflg: c_int) -> c_int {
|
||||
let path = CString::new(ASHMEM_DEVICE).expect("CString::new failed!");
|
||||
let boot_id = std::fs::read_to_string("/proc/sys/kernel/random/boot_id").unwrap();
|
||||
|
||||
let path = CString::new(format!("{}{}", ASHMEM_DEVICE, boot_id).trim())
|
||||
.expect("CString::new failed!");
|
||||
let fd = open(path.as_ptr(), O_RDWR);
|
||||
|
||||
let mut ourkey: [c_char; 20] = [0; 20];
|
||||
@ -196,10 +198,9 @@ pub mod unix_shmem {
|
||||
ourkey.as_mut_ptr() as *mut c_char,
|
||||
size_of::<[c_char; 20]>() as c_ulong,
|
||||
b"%d\x00" as *const u8 as *const c_char,
|
||||
__key,
|
||||
if __key == 0 { fd } else { __key },
|
||||
);
|
||||
|
||||
println!("ourkey: {:?}", ourkey);
|
||||
if ioctl(fd, ASHMEM_SET_NAME, &ourkey) != 0 {
|
||||
close(fd);
|
||||
return 0;
|
||||
@ -210,14 +211,11 @@ pub mod unix_shmem {
|
||||
return 0;
|
||||
};
|
||||
|
||||
println!("shmget returns {}", fd);
|
||||
fd
|
||||
}
|
||||
|
||||
#[cfg(target_os = "android")]
|
||||
unsafe fn shmat(__shmid: c_int, __shmaddr: *const c_void, __shmflg: c_int) -> *mut c_void {
|
||||
println!("shmat(__shmid: {})", __shmid);
|
||||
|
||||
let size = ioctl(__shmid, ASHMEM_GET_SIZE);
|
||||
if size < 0 {
|
||||
return 0 as *mut c_void;
|
||||
@ -235,7 +233,6 @@ pub mod unix_shmem {
|
||||
return 0 as *mut c_void;
|
||||
}
|
||||
|
||||
println!("shmat() = {:?}", ptr);
|
||||
ptr
|
||||
}
|
||||
|
||||
@ -321,14 +318,14 @@ pub mod unix_shmem {
|
||||
impl Drop for UnixShMem {
|
||||
fn drop(&mut self) {
|
||||
unsafe {
|
||||
afl_shmem_deinit(self);
|
||||
unix_shmem_deinit(self);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Create an uninitialized shmap
|
||||
#[cfg(unix)]
|
||||
const fn afl_shmem_unitialized() -> UnixShMem {
|
||||
const fn unix_shmem_unitialized() -> UnixShMem {
|
||||
UnixShMem {
|
||||
shm_str: [0; 20],
|
||||
shm_id: -1,
|
||||
@ -340,8 +337,8 @@ pub mod unix_shmem {
|
||||
#[cfg(unix)]
|
||||
impl UnixShMem {
|
||||
pub fn from_str(shm_str: &CStr, map_size: usize) -> Result<Self, Error> {
|
||||
let mut ret = afl_shmem_unitialized();
|
||||
let map = unsafe { afl_shmem_by_str(&mut ret, shm_str, map_size) };
|
||||
let mut ret = unix_shmem_unitialized();
|
||||
let map = unsafe { unix_shmem_by_str(&mut ret, shm_str, map_size) };
|
||||
if !map.is_null() {
|
||||
Ok(ret)
|
||||
} else {
|
||||
@ -353,8 +350,8 @@ pub mod unix_shmem {
|
||||
}
|
||||
|
||||
pub fn new(map_size: usize) -> Result<Self, Error> {
|
||||
let mut ret = afl_shmem_unitialized();
|
||||
let map = unsafe { afl_shmem_init(&mut ret, map_size) };
|
||||
let mut ret = unix_shmem_unitialized();
|
||||
let map = unsafe { unix_shmem_init(&mut ret, map_size) };
|
||||
if !map.is_null() {
|
||||
Ok(ret)
|
||||
} else {
|
||||
@ -367,7 +364,7 @@ pub mod unix_shmem {
|
||||
}
|
||||
|
||||
/// Deinitialize this shmem instance
|
||||
unsafe fn afl_shmem_deinit(shm: *mut UnixShMem) {
|
||||
unsafe fn unix_shmem_deinit(shm: *mut UnixShMem) {
|
||||
if shm.is_null() || (*shm).map.is_null() {
|
||||
/* Serialized map id */
|
||||
// Not set or not initialized;
|
||||
@ -380,7 +377,7 @@ pub mod unix_shmem {
|
||||
|
||||
/// Functions to create Shared memory region, for observation channels and
|
||||
/// opening inputs and stuff.
|
||||
unsafe fn afl_shmem_init(shm: *mut UnixShMem, map_size: usize) -> *mut c_uchar {
|
||||
unsafe fn unix_shmem_init(shm: *mut UnixShMem, map_size: usize) -> *mut c_uchar {
|
||||
(*shm).map_size = map_size;
|
||||
(*shm).map = ptr::null_mut();
|
||||
(*shm).shm_id = shmget(
|
||||
@ -412,7 +409,7 @@ pub mod unix_shmem {
|
||||
}
|
||||
|
||||
/// Uses a shmap id string to open a shared map
|
||||
unsafe fn afl_shmem_by_str(
|
||||
unsafe fn unix_shmem_by_str(
|
||||
shm: *mut UnixShMem,
|
||||
shm_str: &CStr,
|
||||
map_size: usize,
|
||||
@ -446,18 +443,140 @@ pub mod unix_shmem {
|
||||
#[cfg(all(feature = "std", windows))]
|
||||
pub mod shmem {
|
||||
|
||||
//TODO use super::ShMem;
|
||||
use super::ShMem;
|
||||
use crate::{
|
||||
bolts::bindings::{
|
||||
windows::win32::system_services::{
|
||||
CreateFileMappingA, MapViewOfFile, OpenFileMappingA, UnmapViewOfFile,
|
||||
},
|
||||
windows::win32::system_services::{BOOL, HANDLE, PAGE_TYPE, PSTR},
|
||||
windows::win32::windows_programming::CloseHandle,
|
||||
},
|
||||
Error,
|
||||
};
|
||||
|
||||
use core::{ffi::c_void, ptr, slice};
|
||||
use uuid::Uuid;
|
||||
|
||||
const INVALID_HANDLE_VALUE: isize = -1;
|
||||
const FILE_MAP_ALL_ACCESS: u32 = 0xf001f;
|
||||
|
||||
/// The default Sharedmap impl for windows using shmctl & shmget
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct Win32ShMem {
|
||||
pub filename: [u8; 64],
|
||||
//TODO pub handle: windows::win32::system_services::HANDLE,
|
||||
pub shm_str: [u8; 20],
|
||||
pub handle: HANDLE,
|
||||
pub map: *mut u8,
|
||||
pub map_size: usize,
|
||||
}
|
||||
|
||||
// TODO complete
|
||||
impl ShMem for Win32ShMem {
|
||||
fn existing_from_shm_slice(
|
||||
map_str_bytes: &[u8; 20],
|
||||
map_size: usize,
|
||||
) -> Result<Self, Error> {
|
||||
Self::from_str(map_str_bytes, map_size)
|
||||
}
|
||||
|
||||
fn new_map(map_size: usize) -> Result<Self, Error> {
|
||||
Self::new(map_size)
|
||||
}
|
||||
|
||||
fn shm_slice(&self) -> &[u8; 20] {
|
||||
&self.shm_str
|
||||
}
|
||||
|
||||
fn map(&self) -> &[u8] {
|
||||
unsafe { slice::from_raw_parts(self.map, self.map_size) }
|
||||
}
|
||||
|
||||
fn map_mut(&mut self) -> &mut [u8] {
|
||||
unsafe { slice::from_raw_parts_mut(self.map, self.map_size) }
|
||||
}
|
||||
}
|
||||
|
||||
/// Deinit sharedmaps on drop
|
||||
impl Drop for Win32ShMem {
|
||||
fn drop(&mut self) {
|
||||
unsafe {
|
||||
UnmapViewOfFile(self.map as *mut c_void);
|
||||
CloseHandle(self.handle);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Win32ShMem {
|
||||
pub fn from_str(map_str_bytes: &[u8; 20], map_size: usize) -> Result<Self, Error> {
|
||||
unsafe {
|
||||
let handle = OpenFileMappingA(
|
||||
FILE_MAP_ALL_ACCESS,
|
||||
BOOL(0),
|
||||
PSTR(map_str_bytes as *const u8 as *mut u8),
|
||||
);
|
||||
if handle == HANDLE(0) {
|
||||
return Err(Error::Unknown(format!(
|
||||
"Cannot open shared memory {}",
|
||||
String::from_utf8_lossy(map_str_bytes)
|
||||
)));
|
||||
}
|
||||
let map =
|
||||
MapViewOfFile(handle.clone(), FILE_MAP_ALL_ACCESS, 0, 0, map_size) as *mut u8;
|
||||
if map == ptr::null_mut() {
|
||||
return Err(Error::Unknown(format!(
|
||||
"Cannot map shared memory {}",
|
||||
String::from_utf8_lossy(map_str_bytes)
|
||||
)));
|
||||
}
|
||||
let mut ret = Self {
|
||||
shm_str: [0; 20],
|
||||
handle: handle,
|
||||
map: map,
|
||||
map_size: map_size,
|
||||
};
|
||||
ret.shm_str.clone_from_slice(map_str_bytes);
|
||||
Ok(ret)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn new(map_size: usize) -> Result<Self, Error> {
|
||||
unsafe {
|
||||
let uuid = Uuid::new_v4();
|
||||
let mut map_str = format!("libafl_{}", uuid.to_simple());
|
||||
let map_str_bytes = map_str.as_mut_vec();
|
||||
map_str_bytes[19] = 0; // Trucate to size 20
|
||||
let handle = CreateFileMappingA(
|
||||
HANDLE(INVALID_HANDLE_VALUE),
|
||||
ptr::null_mut(),
|
||||
PAGE_TYPE::PAGE_READWRITE,
|
||||
0,
|
||||
map_size as u32,
|
||||
PSTR(map_str_bytes.as_mut_ptr()),
|
||||
);
|
||||
if handle == HANDLE(0) {
|
||||
return Err(Error::Unknown(format!(
|
||||
"Cannot create shared memory {}",
|
||||
String::from_utf8_lossy(map_str_bytes)
|
||||
)));
|
||||
}
|
||||
let map =
|
||||
MapViewOfFile(handle.clone(), FILE_MAP_ALL_ACCESS, 0, 0, map_size) as *mut u8;
|
||||
if map == ptr::null_mut() {
|
||||
return Err(Error::Unknown(format!(
|
||||
"Cannot map shared memory {}",
|
||||
String::from_utf8_lossy(map_str_bytes)
|
||||
)));
|
||||
}
|
||||
let mut ret = Self {
|
||||
shm_str: [0; 20],
|
||||
handle: handle,
|
||||
map: map,
|
||||
map_size: map_size,
|
||||
};
|
||||
ret.shm_str.clone_from_slice(&map_str_bytes[0..20]);
|
||||
Ok(ret)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
|
@ -1,3 +1,6 @@
|
||||
//! The Minimizer schedulers are a family of corpus schedulers that feed the fuzzer
|
||||
// with testcases only from a subset of the total corpus.
|
||||
|
||||
use crate::{
|
||||
bolts::serdeany::SerdeAny,
|
||||
corpus::{Corpus, CorpusScheduler, Testcase},
|
||||
@ -43,6 +46,7 @@ impl Default for TopRatedsMetadata {
|
||||
}
|
||||
}
|
||||
|
||||
/// Compute the favor factor of a testcase. Lower is better.
|
||||
pub trait FavFactor<I>
|
||||
where
|
||||
I: Input,
|
||||
@ -50,6 +54,8 @@ where
|
||||
fn compute(testcase: &mut Testcase<I>) -> Result<u64, Error>;
|
||||
}
|
||||
|
||||
/// Multiply the testcase size with the execution time.
|
||||
/// This favors small and quick testcases.
|
||||
pub struct LenTimeMulFavFactor<I>
|
||||
where
|
||||
I: Input + HasLen,
|
||||
@ -67,6 +73,9 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
/// The Minimizer scheduler employs a genetic algorithm to compute a subset of the
|
||||
/// corpus that exercise all the requested features (e.g. all the coverage seen so far)
|
||||
/// prioritizing testcases using FavFactor
|
||||
pub struct MinimizerCorpusScheduler<C, CS, F, I, M, R, S>
|
||||
where
|
||||
CS: CorpusScheduler<I, S>,
|
||||
@ -230,8 +239,11 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
/// A MinimizerCorpusScheduler with LenTimeMulFavFactor to prioritize quick and small testcases
|
||||
pub type LenTimeMinimizerCorpusScheduler<C, CS, I, M, R, S> =
|
||||
MinimizerCorpusScheduler<C, CS, LenTimeMulFavFactor<I>, I, M, R, S>;
|
||||
|
||||
/// A MinimizerCorpusScheduler with LenTimeMulFavFactor to prioritize quick and small testcases
|
||||
/// that exercise all the entries registered in the MapIndexesMetadata
|
||||
pub type IndexesLenTimeMinimizerCorpusScheduler<C, CS, I, R, S> =
|
||||
MinimizerCorpusScheduler<C, CS, LenTimeMulFavFactor<I>, I, MapIndexesMetadata, R, S>;
|
||||
|
@ -63,6 +63,8 @@ where
|
||||
fn current_mut(&mut self) -> &mut Option<usize>;
|
||||
}
|
||||
|
||||
/// The scheduler define how the fuzzer requests a testcase from the corpus.
|
||||
/// It has hooks to corpus add/replace/remove to allow complex scheduling algorithms to collect data.
|
||||
pub trait CorpusScheduler<I, S>
|
||||
where
|
||||
I: Input,
|
||||
@ -96,6 +98,7 @@ where
|
||||
fn next(&self, state: &mut S) -> Result<usize, Error>;
|
||||
}
|
||||
|
||||
/// Feed the fuzzer simpply with a random testcase on request
|
||||
pub struct RandCorpusScheduler<C, I, R, S>
|
||||
where
|
||||
S: HasCorpus<C, I> + HasRand<R>,
|
||||
|
@ -1,4 +1,4 @@
|
||||
//! The queue corpus implements an afl-like queue mechanism
|
||||
//! The queue corpus scheduler implements an AFL-like queue mechanism
|
||||
|
||||
use alloc::borrow::ToOwned;
|
||||
use core::marker::PhantomData;
|
||||
@ -10,6 +10,7 @@ use crate::{
|
||||
Error,
|
||||
};
|
||||
|
||||
/// Walk the corpus in a queue-like fashion
|
||||
pub struct QueueCorpusScheduler<C, I, S>
|
||||
where
|
||||
S: HasCorpus<C, I>,
|
||||
@ -25,7 +26,7 @@ where
|
||||
C: Corpus<I>,
|
||||
I: Input,
|
||||
{
|
||||
/// Gets the next entry at random
|
||||
/// Gets the next entry in the queue
|
||||
fn next(&self, state: &mut S) -> Result<usize, Error> {
|
||||
if state.corpus().count() == 0 {
|
||||
Err(Error::Empty("No entries in corpus".to_owned()))
|
||||
|
@ -1,8 +1,10 @@
|
||||
use crate::bolts::{llmp::LlmpSender, shmem::HasFd};
|
||||
use alloc::{string::ToString, vec::Vec};
|
||||
use core::{marker::PhantomData, time::Duration};
|
||||
use serde::{de::DeserializeOwned, Serialize};
|
||||
|
||||
#[cfg(feature = "std")]
|
||||
use core::ptr::read_volatile;
|
||||
|
||||
#[cfg(feature = "std")]
|
||||
use crate::bolts::llmp::LlmpReceiver;
|
||||
|
||||
@ -16,8 +18,8 @@ use crate::utils::{fork, ForkResult};
|
||||
use crate::bolts::shmem::UnixShMem;
|
||||
use crate::{
|
||||
bolts::{
|
||||
llmp::{self, LlmpClient, LlmpClientDescription, Tag},
|
||||
shmem::ShMem,
|
||||
llmp::{self, LlmpClient, LlmpClientDescription, LlmpSender, Tag},
|
||||
shmem::{HasFd, ShMem},
|
||||
},
|
||||
corpus::CorpusScheduler,
|
||||
events::{BrokerEventResult, Event, EventManager},
|
||||
@ -352,7 +354,7 @@ where
|
||||
llmp::LlmpConnection::IsClient { client } => {
|
||||
while let Some((sender_id, tag, msg)) = client.recv_buf()? {
|
||||
if tag == _LLMP_TAG_EVENT_TO_BROKER {
|
||||
continue;
|
||||
panic!("EVENT_TO_BROKER parcel should not have arrived in the client!");
|
||||
}
|
||||
let event: Event<I> = postcard::from_bytes(msg)?;
|
||||
events.push((sender_id, event));
|
||||
@ -519,10 +521,7 @@ where
|
||||
#[cfg(target_os = "android")]
|
||||
{
|
||||
let path = std::env::current_dir()?;
|
||||
mgr = LlmpEventManager::<I, S, SH, ST>::new_on_domain_socket(
|
||||
stats,
|
||||
&format!("{}/.llmp_socket", path.display()).to_string(),
|
||||
)?;
|
||||
mgr = LlmpEventManager::<I, S, SH, ST>::new_on_domain_socket(stats, "\x00llmp_socket")?;
|
||||
};
|
||||
#[cfg(not(target_os = "android"))]
|
||||
{
|
||||
@ -535,6 +534,7 @@ where
|
||||
mgr.broker_loop()?;
|
||||
return Err(Error::ShuttingDown);
|
||||
} else {
|
||||
// We are the fuzzer respawner in a llmp client
|
||||
mgr.to_env(_ENV_FUZZER_BROKER_CLIENT_INITIAL);
|
||||
|
||||
// First, create a channel from the fuzzer (sender) to us (receiver) to report its state for restarts.
|
||||
@ -547,7 +547,7 @@ where
|
||||
sender.to_env(_ENV_FUZZER_SENDER)?;
|
||||
receiver.to_env(_ENV_FUZZER_RECEIVER)?;
|
||||
|
||||
let mut ctr = 0;
|
||||
let mut ctr: u64 = 0;
|
||||
// Client->parent loop
|
||||
loop {
|
||||
dbg!("Spawning next client (id {})", ctr);
|
||||
@ -563,7 +563,12 @@ where
|
||||
#[cfg(windows)]
|
||||
startable_self()?.status()?;
|
||||
|
||||
ctr += 1;
|
||||
if unsafe { read_volatile(&(*receiver.current_recv_map.page()).size_used) } == 0 {
|
||||
// Storing state in the last round did not work
|
||||
panic!("Fuzzer-respawner: Storing state in crashed fuzzer instance did not work, no point to spawn the next client!");
|
||||
}
|
||||
|
||||
ctr = ctr.wrapping_add(1);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
|
@ -1,15 +1,18 @@
|
||||
//! The InProcess Executor is a libfuzzer-like executor, that will simply call a function.
|
||||
//! It should usually be paired with extra error-handling, such as a restarting event manager, to be effective.
|
||||
|
||||
use core::marker::PhantomData;
|
||||
#[cfg(unix)]
|
||||
use core::{
|
||||
ffi::c_void,
|
||||
marker::PhantomData,
|
||||
ptr::{self, write_volatile},
|
||||
sync::atomic::{compiler_fence, Ordering},
|
||||
};
|
||||
|
||||
#[cfg(unix)]
|
||||
use crate::bolts::os::unix_signals::{c_void, setup_signal_handler};
|
||||
use crate::bolts::os::unix_signals::setup_signal_handler;
|
||||
#[cfg(windows)]
|
||||
use crate::bolts::os::windows_exceptions::setup_exception_handler;
|
||||
|
||||
use crate::{
|
||||
bolts::tuples::Named,
|
||||
corpus::Corpus,
|
||||
@ -59,6 +62,27 @@ where
|
||||
&mut data.current_input_ptr,
|
||||
_input as *const _ as *const c_void,
|
||||
);
|
||||
write_volatile(
|
||||
&mut data.observers_ptr,
|
||||
&self.observers as *const _ as *const c_void,
|
||||
);
|
||||
// Direct raw pointers access /aliasing is pretty undefined behavior.
|
||||
// Since the state and event may have moved in memory, refresh them right before the signal may happen
|
||||
write_volatile(&mut data.state_ptr, _state as *mut _ as *mut c_void);
|
||||
write_volatile(&mut data.event_mgr_ptr, _event_mgr as *mut _ as *mut c_void);
|
||||
compiler_fence(Ordering::SeqCst);
|
||||
}
|
||||
#[cfg(windows)]
|
||||
unsafe {
|
||||
let data = &mut windows_exception_handler::GLOBAL_STATE;
|
||||
write_volatile(
|
||||
&mut data.current_input_ptr,
|
||||
_input as *const _ as *const c_void,
|
||||
);
|
||||
write_volatile(
|
||||
&mut data.observers_ptr,
|
||||
&self.observers as *const _ as *const c_void,
|
||||
);
|
||||
// Direct raw pointers access /aliasing is pretty undefined behavior.
|
||||
// Since the state and event may have moved in memory, refresh them right before the signal may happen
|
||||
write_volatile(&mut data.state_ptr, _state as *mut _ as *mut c_void);
|
||||
@ -72,6 +96,16 @@ where
|
||||
fn run_target(&mut self, input: &I) -> Result<ExitKind, Error> {
|
||||
let bytes = input.target_bytes();
|
||||
let ret = (self.harness_fn)(self, bytes.as_slice());
|
||||
Ok(ret)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn post_exec<EM, S>(
|
||||
&mut self,
|
||||
_state: &mut S,
|
||||
_event_mgr: &mut EM,
|
||||
_input: &I,
|
||||
) -> Result<(), Error> {
|
||||
#[cfg(unix)]
|
||||
unsafe {
|
||||
write_volatile(
|
||||
@ -80,7 +114,15 @@ where
|
||||
);
|
||||
compiler_fence(Ordering::SeqCst);
|
||||
}
|
||||
Ok(ret)
|
||||
#[cfg(windows)]
|
||||
unsafe {
|
||||
write_volatile(
|
||||
&mut windows_exception_handler::GLOBAL_STATE.current_input_ptr,
|
||||
ptr::null(),
|
||||
);
|
||||
compiler_fence(Ordering::SeqCst);
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
@ -138,10 +180,6 @@ where
|
||||
#[cfg(unix)]
|
||||
unsafe {
|
||||
let data = &mut unix_signal_handler::GLOBAL_STATE;
|
||||
write_volatile(
|
||||
&mut data.observers_ptr,
|
||||
&observers as *const _ as *const c_void,
|
||||
);
|
||||
write_volatile(
|
||||
&mut data.crash_handler,
|
||||
unix_signal_handler::inproc_crash_handler::<EM, I, OC, OFT, OT, S>,
|
||||
@ -154,6 +192,21 @@ where
|
||||
setup_signal_handler(data)?;
|
||||
compiler_fence(Ordering::SeqCst);
|
||||
}
|
||||
#[cfg(windows)]
|
||||
unsafe {
|
||||
let data = &mut windows_exception_handler::GLOBAL_STATE;
|
||||
write_volatile(
|
||||
&mut data.crash_handler,
|
||||
windows_exception_handler::inproc_crash_handler::<EM, I, OC, OFT, OT, S>,
|
||||
);
|
||||
//write_volatile(
|
||||
// &mut data.timeout_handler,
|
||||
// windows_exception_handler::inproc_timeout_handler::<EM, I, OC, OFT, OT, S>,
|
||||
//);
|
||||
|
||||
setup_exception_handler(data)?;
|
||||
compiler_fence(Ordering::SeqCst);
|
||||
}
|
||||
|
||||
Ok(Self {
|
||||
harness_fn,
|
||||
@ -186,6 +239,8 @@ mod unix_signal_handler {
|
||||
state::{HasObjectives, HasSolutions},
|
||||
};
|
||||
|
||||
// TODO merge GLOBAL_STATE with the Windows one
|
||||
|
||||
/// Signal handling on unix systems needs some nasty unsafe.
|
||||
pub static mut GLOBAL_STATE: InProcessExecutorHandlerData = InProcessExecutorHandlerData {
|
||||
/// The state ptr for signal handling
|
||||
@ -228,7 +283,9 @@ mod unix_signal_handler {
|
||||
unsafe {
|
||||
let data = &mut GLOBAL_STATE;
|
||||
match signal {
|
||||
Signal::SigUser2 => (data.timeout_handler)(signal, info, void, data),
|
||||
Signal::SigUser2 | Signal::SigAlarm => {
|
||||
(data.timeout_handler)(signal, info, void, data)
|
||||
}
|
||||
_ => (data.crash_handler)(signal, info, void, data),
|
||||
}
|
||||
}
|
||||
@ -236,6 +293,7 @@ mod unix_signal_handler {
|
||||
|
||||
fn signals(&self) -> Vec<Signal> {
|
||||
vec![
|
||||
Signal::SigAlarm,
|
||||
Signal::SigUser2,
|
||||
Signal::SigAbort,
|
||||
Signal::SigBus,
|
||||
@ -279,7 +337,7 @@ mod unix_signal_handler {
|
||||
|
||||
let obj_fitness = state
|
||||
.objectives_mut()
|
||||
.is_interesting_all(&input, observers, ExitKind::Crash)
|
||||
.is_interesting_all(&input, observers, ExitKind::Timeout)
|
||||
.expect("In timeout handler objectives failure.");
|
||||
if obj_fitness > 0 {
|
||||
state
|
||||
@ -404,6 +462,166 @@ mod unix_signal_handler {
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(windows)]
|
||||
mod windows_exception_handler {
|
||||
use alloc::vec::Vec;
|
||||
use core::{ffi::c_void, ptr};
|
||||
#[cfg(feature = "std")]
|
||||
use std::io::{stdout, Write};
|
||||
|
||||
use crate::{
|
||||
bolts::{
|
||||
bindings::windows::win32::system_services::ExitProcess,
|
||||
os::windows_exceptions::{
|
||||
ExceptionCode, Handler, CRASH_EXCEPTIONS, EXCEPTION_POINTERS,
|
||||
},
|
||||
},
|
||||
corpus::{Corpus, Testcase},
|
||||
events::{Event, EventManager},
|
||||
executors::ExitKind,
|
||||
feedbacks::FeedbacksTuple,
|
||||
inputs::{HasTargetBytes, Input},
|
||||
observers::ObserversTuple,
|
||||
state::{HasObjectives, HasSolutions},
|
||||
};
|
||||
|
||||
/// Signal handling on unix systems needs some nasty unsafe.
|
||||
pub static mut GLOBAL_STATE: InProcessExecutorHandlerData = InProcessExecutorHandlerData {
|
||||
/// The state ptr for signal handling
|
||||
state_ptr: ptr::null_mut(),
|
||||
/// The event manager ptr for signal handling
|
||||
event_mgr_ptr: ptr::null_mut(),
|
||||
/// The observers ptr for signal handling
|
||||
observers_ptr: ptr::null(),
|
||||
/// The current input for signal handling
|
||||
current_input_ptr: ptr::null(),
|
||||
/// The crash handler fn
|
||||
crash_handler: nop_handler,
|
||||
// The timeout handler fn
|
||||
//timeout_handler: nop_handler,
|
||||
};
|
||||
|
||||
pub struct InProcessExecutorHandlerData {
|
||||
pub state_ptr: *mut c_void,
|
||||
pub event_mgr_ptr: *mut c_void,
|
||||
pub observers_ptr: *const c_void,
|
||||
pub current_input_ptr: *const c_void,
|
||||
pub crash_handler: unsafe fn(ExceptionCode, *mut EXCEPTION_POINTERS, &mut Self),
|
||||
//pub timeout_handler: unsafe fn(ExceptionCode, *mut EXCEPTION_POINTERS, &mut Self),
|
||||
}
|
||||
|
||||
unsafe impl Send for InProcessExecutorHandlerData {}
|
||||
unsafe impl Sync for InProcessExecutorHandlerData {}
|
||||
|
||||
unsafe fn nop_handler(
|
||||
_code: ExceptionCode,
|
||||
_exception_pointers: *mut EXCEPTION_POINTERS,
|
||||
_data: &mut InProcessExecutorHandlerData,
|
||||
) {
|
||||
}
|
||||
|
||||
impl Handler for InProcessExecutorHandlerData {
|
||||
fn handle(&mut self, code: ExceptionCode, exception_pointers: *mut EXCEPTION_POINTERS) {
|
||||
unsafe {
|
||||
let data = &mut GLOBAL_STATE;
|
||||
(data.crash_handler)(code, exception_pointers, data)
|
||||
}
|
||||
}
|
||||
|
||||
fn exceptions(&self) -> Vec<ExceptionCode> {
|
||||
CRASH_EXCEPTIONS.to_vec()
|
||||
}
|
||||
}
|
||||
|
||||
pub unsafe fn inproc_crash_handler<EM, I, OC, OFT, OT, S>(
|
||||
code: ExceptionCode,
|
||||
exception_pointers: *mut EXCEPTION_POINTERS,
|
||||
data: &mut InProcessExecutorHandlerData,
|
||||
) where
|
||||
EM: EventManager<I, S>,
|
||||
OT: ObserversTuple,
|
||||
OC: Corpus<I>,
|
||||
OFT: FeedbacksTuple<I>,
|
||||
S: HasObjectives<OFT, I> + HasSolutions<OC, I>,
|
||||
I: Input + HasTargetBytes,
|
||||
{
|
||||
#[cfg(feature = "std")]
|
||||
println!("Crashed with {}", code);
|
||||
if !data.current_input_ptr.is_null() {
|
||||
let state = (data.state_ptr as *mut S).as_mut().unwrap();
|
||||
let event_mgr = (data.event_mgr_ptr as *mut EM).as_mut().unwrap();
|
||||
let observers = (data.observers_ptr as *const OT).as_ref().unwrap();
|
||||
|
||||
#[cfg(feature = "std")]
|
||||
println!("Child crashed!");
|
||||
#[cfg(feature = "std")]
|
||||
let _ = stdout().flush();
|
||||
|
||||
let input = (data.current_input_ptr as *const I).as_ref().unwrap();
|
||||
// Make sure we don't crash in the crash handler forever.
|
||||
data.current_input_ptr = ptr::null();
|
||||
|
||||
let obj_fitness = state
|
||||
.objectives_mut()
|
||||
.is_interesting_all(&input, observers, ExitKind::Crash)
|
||||
.expect("In crash handler objectives failure.");
|
||||
if obj_fitness > 0 {
|
||||
let new_input = input.clone();
|
||||
state
|
||||
.solutions_mut()
|
||||
.add(Testcase::new(new_input))
|
||||
.expect("In crash handler solutions failure.");
|
||||
event_mgr
|
||||
.fire(
|
||||
state,
|
||||
Event::Objective {
|
||||
objective_size: state.solutions().count(),
|
||||
},
|
||||
)
|
||||
.expect("Could not send crashing input");
|
||||
}
|
||||
|
||||
event_mgr.on_restart(state).unwrap();
|
||||
|
||||
#[cfg(feature = "std")]
|
||||
println!("Waiting for broker...");
|
||||
event_mgr.await_restart_safe();
|
||||
#[cfg(feature = "std")]
|
||||
println!("Bye!");
|
||||
|
||||
ExitProcess(1);
|
||||
} else {
|
||||
#[cfg(feature = "std")]
|
||||
{
|
||||
println!("Double crash\n");
|
||||
let crash_addr = exception_pointers
|
||||
.as_mut()
|
||||
.unwrap()
|
||||
.exception_record
|
||||
.as_mut()
|
||||
.unwrap()
|
||||
.exception_address as usize;
|
||||
|
||||
println!(
|
||||
"We crashed at addr 0x{:x}, but are not in the target... Bug in the fuzzer? Exiting.",
|
||||
crash_addr
|
||||
);
|
||||
}
|
||||
#[cfg(feature = "std")]
|
||||
{
|
||||
println!("Type QUIT to restart the child");
|
||||
let mut line = String::new();
|
||||
while line.trim() != "QUIT" {
|
||||
std::io::stdin().read_line(&mut line).unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
// TODO tell the parent to not restart
|
||||
ExitProcess(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
|
||||
@ -425,7 +643,6 @@ mod tests {
|
||||
|
||||
let mut in_process_executor = InProcessExecutor::<NopInput, ()> {
|
||||
harness_fn: test_harness_fn_nop,
|
||||
// TODO: on_crash_fn: Box::new(|_, _, _, _, _| ()),
|
||||
observers: tuple_list!(),
|
||||
name: "main",
|
||||
phantom: PhantomData,
|
||||
|
@ -2,6 +2,8 @@
|
||||
|
||||
pub mod inprocess;
|
||||
pub use inprocess::InProcessExecutor;
|
||||
pub mod timeout;
|
||||
pub use timeout::TimeoutExecutor;
|
||||
#[cfg(feature = "runtime")]
|
||||
pub mod runtime;
|
||||
|
||||
@ -94,7 +96,12 @@ where
|
||||
|
||||
/// Called right after execution finished.
|
||||
#[inline]
|
||||
fn post_exec<EM, S>(&mut self, _state: &S, _event_mgr: &mut EM, _input: &I) -> Result<(), Error>
|
||||
fn post_exec<EM, S>(
|
||||
&mut self,
|
||||
_state: &mut S,
|
||||
_event_mgr: &mut EM,
|
||||
_input: &I,
|
||||
) -> Result<(), Error>
|
||||
where
|
||||
EM: EventManager<I, S>,
|
||||
{
|
||||
|
164
libafl/src/executors/timeout.rs
Normal file
164
libafl/src/executors/timeout.rs
Normal file
@ -0,0 +1,164 @@
|
||||
//! A TimeoutExecutor set a timeout before each target run
|
||||
|
||||
use core::{marker::PhantomData, time::Duration};
|
||||
|
||||
use crate::{
|
||||
bolts::tuples::Named,
|
||||
events::EventManager,
|
||||
executors::{Executor, ExitKind, HasObservers},
|
||||
inputs::{HasTargetBytes, Input},
|
||||
observers::ObserversTuple,
|
||||
Error,
|
||||
};
|
||||
|
||||
#[cfg(unix)]
|
||||
use core::ptr::null_mut;
|
||||
#[cfg(unix)]
|
||||
use libc::c_int;
|
||||
|
||||
#[repr(C)]
|
||||
#[cfg(unix)]
|
||||
struct Timeval {
|
||||
pub tv_sec: i64,
|
||||
pub tv_usec: i64,
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
#[cfg(unix)]
|
||||
struct Itimerval {
|
||||
pub it_interval: Timeval,
|
||||
pub it_value: Timeval,
|
||||
}
|
||||
|
||||
#[cfg(unix)]
|
||||
extern "C" {
|
||||
fn setitimer(which: c_int, new_value: *mut Itimerval, old_value: *mut Itimerval) -> c_int;
|
||||
}
|
||||
|
||||
#[cfg(unix)]
|
||||
const ITIMER_REAL: c_int = 0;
|
||||
|
||||
/// The timeout excutor is a wrapper that set a timeout before each run
|
||||
pub struct TimeoutExecutor<E, I, OT>
|
||||
where
|
||||
E: Executor<I> + HasObservers<OT>,
|
||||
I: Input + HasTargetBytes,
|
||||
OT: ObserversTuple,
|
||||
{
|
||||
executor: E,
|
||||
exec_tmout: Duration,
|
||||
phantom: PhantomData<(I, OT)>,
|
||||
}
|
||||
|
||||
impl<E, I, OT> Named for TimeoutExecutor<E, I, OT>
|
||||
where
|
||||
E: Executor<I> + HasObservers<OT>,
|
||||
I: Input + HasTargetBytes,
|
||||
OT: ObserversTuple,
|
||||
{
|
||||
fn name(&self) -> &str {
|
||||
self.executor.name()
|
||||
}
|
||||
}
|
||||
|
||||
impl<E, I, OT> HasObservers<OT> for TimeoutExecutor<E, I, OT>
|
||||
where
|
||||
E: Executor<I> + HasObservers<OT>,
|
||||
I: Input + HasTargetBytes,
|
||||
OT: ObserversTuple,
|
||||
{
|
||||
#[inline]
|
||||
fn observers(&self) -> &OT {
|
||||
self.executor.observers()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn observers_mut(&mut self) -> &mut OT {
|
||||
self.executor.observers_mut()
|
||||
}
|
||||
}
|
||||
|
||||
impl<E, I, OT> TimeoutExecutor<E, I, OT>
|
||||
where
|
||||
E: Executor<I> + HasObservers<OT>,
|
||||
I: Input + HasTargetBytes,
|
||||
OT: ObserversTuple,
|
||||
{
|
||||
pub fn new(executor: E, exec_tmout: Duration) -> Self {
|
||||
Self {
|
||||
executor,
|
||||
exec_tmout,
|
||||
phantom: PhantomData,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<E, I, OT> Executor<I> for TimeoutExecutor<E, I, OT>
|
||||
where
|
||||
E: Executor<I> + HasObservers<OT>,
|
||||
I: Input + HasTargetBytes,
|
||||
OT: ObserversTuple,
|
||||
{
|
||||
#[inline]
|
||||
fn pre_exec<EM: EventManager<I, S>, S>(
|
||||
&mut self,
|
||||
_state: &mut S,
|
||||
_event_mgr: &mut EM,
|
||||
_input: &I,
|
||||
) -> Result<(), Error> {
|
||||
#[cfg(unix)]
|
||||
unsafe {
|
||||
let milli_sec = self.exec_tmout.as_millis();
|
||||
let it_value = Timeval {
|
||||
tv_sec: (milli_sec / 1000) as i64,
|
||||
tv_usec: (milli_sec % 1000) as i64,
|
||||
};
|
||||
let it_interval = Timeval {
|
||||
tv_sec: 0,
|
||||
tv_usec: 0,
|
||||
};
|
||||
setitimer(
|
||||
ITIMER_REAL,
|
||||
&mut Itimerval {
|
||||
it_interval,
|
||||
it_value,
|
||||
},
|
||||
null_mut(),
|
||||
);
|
||||
}
|
||||
self.executor.pre_exec(_state, _event_mgr, _input)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn post_exec<EM: EventManager<I, S>, S>(
|
||||
&mut self,
|
||||
_state: &mut S,
|
||||
_event_mgr: &mut EM,
|
||||
_input: &I,
|
||||
) -> Result<(), Error> {
|
||||
#[cfg(unix)]
|
||||
unsafe {
|
||||
let it_value = Timeval {
|
||||
tv_sec: 0,
|
||||
tv_usec: 0,
|
||||
};
|
||||
let it_interval = Timeval {
|
||||
tv_sec: 0,
|
||||
tv_usec: 0,
|
||||
};
|
||||
setitimer(
|
||||
ITIMER_REAL,
|
||||
&mut Itimerval {
|
||||
it_interval,
|
||||
it_value,
|
||||
},
|
||||
null_mut(),
|
||||
);
|
||||
}
|
||||
self.executor.post_exec(_state, _event_mgr, _input)
|
||||
}
|
||||
|
||||
fn run_target(&mut self, input: &I) -> Result<ExitKind, Error> {
|
||||
self.executor.run_target(input)
|
||||
}
|
||||
}
|
@ -157,6 +157,46 @@ impl Default for CrashFeedback {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Clone, Debug)]
|
||||
pub struct TimeoutFeedback {}
|
||||
|
||||
impl<I> Feedback<I> for TimeoutFeedback
|
||||
where
|
||||
I: Input,
|
||||
{
|
||||
fn is_interesting<OT: ObserversTuple>(
|
||||
&mut self,
|
||||
_input: &I,
|
||||
_observers: &OT,
|
||||
exit_kind: ExitKind,
|
||||
) -> Result<u32, Error> {
|
||||
if exit_kind == ExitKind::Timeout {
|
||||
Ok(1)
|
||||
} else {
|
||||
Ok(0)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Named for TimeoutFeedback {
|
||||
#[inline]
|
||||
fn name(&self) -> &str {
|
||||
"TimeoutFeedback"
|
||||
}
|
||||
}
|
||||
|
||||
impl TimeoutFeedback {
|
||||
pub fn new() -> Self {
|
||||
Self {}
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for TimeoutFeedback {
|
||||
fn default() -> Self {
|
||||
Self::new()
|
||||
}
|
||||
}
|
||||
|
||||
/// Nop feedback that annotates execution time in the new testcase, if any
|
||||
#[derive(Serialize, Deserialize, Clone, Debug)]
|
||||
pub struct TimeFeedback {
|
||||
|
Loading…
x
Reference in New Issue
Block a user