Windows Support for LibAFL-LibFuzzer (#3130)
* Add windows build script and additional changes to support windows for libafl-libfuzzer * Update build scripts and harness wrapping directives * Resolve issue with corpus edge count calculation * Add help message and make fork do nothing on Windows * Format harness_wrap.cpp * Clippy happiness pass * Clippy happiness pass * Clippy happiness pass * Correct logic * Correct logic * Update help output and make runs argument work * Add test for libafl_libfuzzer on windows * Add workflow for libafl_libfuzzer test * Fix copy without dependent task * Add libafl_libfuzzer_windows to preflight list * Format harness * Explicitly ignore windows fuzzer * Remove windows-specific copy from unix instructions * Ensure using nightly * Fix job name * Update build to use libFuzzer.lib on Windows to keep consistent with Linux * Remove nightly requirement --------- Co-authored-by: Rowan Hart <rowanhart@microsoft.com>
This commit is contained in:
parent
db1d38eeb6
commit
0b25d723c0
11
.github/workflows/build_and_test.yml
vendored
11
.github/workflows/build_and_test.yml
vendored
@ -329,6 +329,7 @@ jobs:
|
||||
- ./fuzzers/inprocess/libfuzzer_stb_image
|
||||
# - ./fuzzers/structure_aware/libfuzzer_stb_image_concolic
|
||||
# - ./fuzzers/inprocess/sqlite_centralized_multi_machine
|
||||
# - ./fuzzers/inprocess/libafl_libfuzzer_windows
|
||||
|
||||
# Fuzz Anything
|
||||
- ./fuzzers/fuzz_anything/push_harness
|
||||
@ -569,6 +570,16 @@ jobs:
|
||||
- name: Build fuzzers/binary_only/frida_libpng
|
||||
run: cd fuzzers/binary_only/frida_libpng/ && just test
|
||||
|
||||
windows-libafl-libfuzzer:
|
||||
runs-on: windows-latest
|
||||
needs:
|
||||
- common
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: ./.github/workflows/windows-tester-prepare
|
||||
- name: Build fuzzers/inprocess/libafl_libfuzzer_windows
|
||||
run: cd fuzzers/inprocess/libafl_libfuzzer_windows && just test
|
||||
|
||||
windows-libfuzzer-stb-image:
|
||||
runs-on: windows-latest
|
||||
needs:
|
||||
|
@ -47,7 +47,7 @@ lib2: libpng
|
||||
|
||||
[windows]
|
||||
harness: lib lib2
|
||||
copy libpng-1.6.37\Release\libpng16.lib . && copy libpng-1.6.37\Release\libpng16.dll . && copy zlib\Release\zlib.lib . && copy zlib\Release\zlib.dll . && copy target\release\frida_fuzzer.exe .
|
||||
copy libpng-1.6.37\Release\libpng16.lib . && copy libpng-1.6.37\Release\libpng16.dll . && copy zlib\Release\zlib.lib . && copy zlib\Release\zlib.dll .
|
||||
cl /O2 /c /I .\libpng-1.6.37 harness.cc /Fo:harness.obj && link /DLL /OUT:libpng-harness.dll harness.obj libpng16.lib zlib.lib
|
||||
|
||||
[unix]
|
||||
|
30
fuzzers/inprocess/libafl_libfuzzer_windows/Justfile
Normal file
30
fuzzers/inprocess/libafl_libfuzzer_windows/Justfile
Normal file
@ -0,0 +1,30 @@
|
||||
import "../../../just/libafl.just"
|
||||
|
||||
FUZZER_NAME := "libafl_libfuzzer_windows"
|
||||
FUZZER_NAME_WIN := "libafl_libfuzzer_windows.exe"
|
||||
|
||||
set windows-shell := ['cmd.exe', '/c']
|
||||
set unstable
|
||||
|
||||
[windows]
|
||||
libafl_libfuzzer:
|
||||
powershell -File ..\..\..\libafl_libfuzzer_runtime\build.ps1
|
||||
|
||||
[windows]
|
||||
harness: libafl_libfuzzer
|
||||
copy ..\..\..\libafl_libfuzzer_runtime\libFuzzer.lib .
|
||||
cl /c /O2 /EHsc /std:c++17 /MDd /fsanitize-coverage=inline-8bit-counters /fsanitize-coverage=edge /fsanitize-coverage=trace-cmp /fsanitize-coverage=trace-div /Fo:harness.obj harness.cc
|
||||
link harness.obj libFuzzer.lib sancov.lib /OUT:libafl_libfuzzer_windows.exe
|
||||
|
||||
[windows]
|
||||
run: harness
|
||||
if not exist corpus mkdir corpus
|
||||
{{FUZZER_NAME_WIN}} -use_value_profile=1 corpus
|
||||
|
||||
[windows]
|
||||
[script("cmd.exe", "/c")]
|
||||
test: harness
|
||||
if exist corpus rd /s /q corpus
|
||||
mkdir corpus
|
||||
{{FUZZER_NAME_WIN}} -use_value_profile=1 -runs=30000 corpus
|
||||
dir /a-d corpus && (echo Files exist) || (exit /b 1337)
|
4
fuzzers/inprocess/libafl_libfuzzer_windows/README.md
Normal file
4
fuzzers/inprocess/libafl_libfuzzer_windows/README.md
Normal file
@ -0,0 +1,4 @@
|
||||
# LibAFL-LibFuzzer Windows
|
||||
|
||||
A simple example demonstrating how to build LibFuzzer harnesses with LibAFL-LibFuzzer
|
||||
as an alternative runtime on Windows.
|
33
fuzzers/inprocess/libafl_libfuzzer_windows/harness.cc
Normal file
33
fuzzers/inprocess/libafl_libfuzzer_windows/harness.cc
Normal file
@ -0,0 +1,33 @@
|
||||
// Simple decoder function with an off by one error that is triggered under
|
||||
// certain conditions.
|
||||
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
|
||||
int DecodeInput(const uint8_t *data, size_t size) {
|
||||
if (size < 5) {
|
||||
return -1; // Error: not enough data
|
||||
}
|
||||
|
||||
if (data[0] != 'F' || data[1] != 'U' || data[2] != 'Z' || data[3] == 'Z') {
|
||||
return -1; // Error: invalid header
|
||||
}
|
||||
|
||||
if (data[4] <= 0) {
|
||||
return -1; // Error: invalid size
|
||||
}
|
||||
|
||||
int csum = 0;
|
||||
|
||||
for (size_t i = 5; i < size; ++i) {
|
||||
csum += data[i];
|
||||
}
|
||||
|
||||
return csum; // Error: checksum mismatch
|
||||
}
|
||||
|
||||
extern "C" __declspec(dllexport) int LLVMFuzzerTestOneInput(const uint8_t *data,
|
||||
size_t size) {
|
||||
DecodeInput(data, size);
|
||||
return 0;
|
||||
}
|
@ -86,22 +86,35 @@ To do so, [ensure a recent nightly version of Rust is installed](https://rustup.
|
||||
[`libafl_libfuzzer_runtime`](../libafl_libfuzzer_runtime) folder and build the runtime with the following command:
|
||||
|
||||
```bash
|
||||
./build.sh
|
||||
just build
|
||||
```
|
||||
|
||||
The static library will be available at `libFuzzer.a` in the [`libafl_libfuzzer_runtime`](../libafl_libfuzzer_runtime)
|
||||
directory.
|
||||
If you encounter build failures without clear error outputs that help you resolve the issue, please [submit an issue].
|
||||
Or you can call `build.sh` (Unix) or `build.ps1` (Windows).
|
||||
|
||||
This library may now be used in place of libFuzzer.
|
||||
To do so, change your CFLAGS/CXXFLAGS from `-fsanitize=fuzzer` to:
|
||||
The static library will be available at `libFuzzer.a` (`libFuzzer.lib` for Windows) in
|
||||
the [`libafl_libfuzzer_runtime`](../libafl_libfuzzer_runtime) directory. If you
|
||||
encounter build failures without clear error outputs that help you resolve the issue,
|
||||
please [submit an issue].
|
||||
|
||||
```
|
||||
-fsanitize=fuzzer-no-link -L/path/to/libafl_libfuzzer_runtime -lFuzzer
|
||||
```
|
||||
#### Unix
|
||||
|
||||
Alternatively, you may directly overwrite the system libFuzzer library and use `-fsanitize=fuzzer` as normal.
|
||||
This changes per system, but on my machine is located at `/usr/lib64/clang/16/lib/linux/libclang_rt.fuzzer-x86_64.a`.
|
||||
This library may now be used in place of libFuzzer. To do so, change your
|
||||
CFLAGS/CXXFLAGS from `-fsanitize=fuzzer` to:
|
||||
|
||||
``` -fsanitize=fuzzer-no-link -L/path/to/libafl_libfuzzer_runtime -lFuzzer ```
|
||||
|
||||
Alternatively, you may directly overwrite the system libFuzzer library and use
|
||||
`-fsanitize=fuzzer` as normal. This changes per system, but on my machine is located at
|
||||
`/usr/lib64/clang/16/lib/linux/libclang_rt.fuzzer-x86_64.a`.
|
||||
|
||||
#### Windows
|
||||
|
||||
For Windows, change your CFLAGS/CXXFLAGS from `-fsanitize=fuzzer` to:
|
||||
|
||||
```/fsanitize-coverage=inline-8bit-counters /fsanitize-coverage=edge /fsanitize-coverage=trace-cmp /fsanitize-coverage=trace-div```
|
||||
|
||||
And then ensure you link with `sancov.lib` when producing your final executable. See
|
||||
`fuzzers\inprocess\libafl_libfuzzer_windows` for an example.
|
||||
|
||||
#### Caveats
|
||||
|
||||
|
@ -4,16 +4,19 @@ version = "0.15.2"
|
||||
edition = "2024"
|
||||
publish = false
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[features]
|
||||
default = ["fork"]
|
||||
default = []
|
||||
## Enables forking mode for the LibAFL launcher (instead of starting new processes)
|
||||
fork = ["libafl/fork"]
|
||||
track_hit_feedbacks = [
|
||||
"libafl/track_hit_feedbacks",
|
||||
"libafl_targets/track_hit_feedbacks",
|
||||
]
|
||||
tui_monitor = ["libafl/tui_monitor"]
|
||||
|
||||
[target.'cfg(not(windows))'.features]
|
||||
## Enable the `fork` feature on non-windows platforms
|
||||
default = ["fork", "tui_monitor"]
|
||||
|
||||
[profile.release]
|
||||
lto = true
|
||||
@ -40,7 +43,6 @@ libafl = { path = "../libafl", default-features = false, features = [
|
||||
"regex",
|
||||
"errors_backtrace",
|
||||
"serdeany_autoreg",
|
||||
"tui_monitor",
|
||||
"unicode",
|
||||
] }
|
||||
libafl_bolts = { path = "../libafl_bolts", default-features = false, features = [
|
||||
|
@ -154,7 +154,7 @@ where
|
||||
}
|
||||
#[inline]
|
||||
fn count_all(&self) -> usize {
|
||||
self.count_disabled().saturating_add(self.count_disabled())
|
||||
self.count().saturating_add(self.count_disabled())
|
||||
}
|
||||
|
||||
#[expect(clippy::used_underscore_items)]
|
||||
|
@ -1,20 +1,31 @@
|
||||
use core::ffi::c_int;
|
||||
#[cfg(unix)]
|
||||
use std::io::{Write, stderr, stdout};
|
||||
use std::{fmt::Debug, fs::File, net::TcpListener, os::fd::AsRawFd, str::FromStr};
|
||||
use std::{
|
||||
fmt::Debug,
|
||||
fs::File,
|
||||
io::{Write, stderr, stdout},
|
||||
net::TcpListener,
|
||||
os::fd::AsRawFd,
|
||||
str::FromStr,
|
||||
};
|
||||
|
||||
#[cfg(feature = "tui_monitor")]
|
||||
use libafl::monitors::tui::TuiMonitor;
|
||||
use libafl::{
|
||||
Error, Fuzzer, HasMetadata,
|
||||
corpus::Corpus,
|
||||
events::{
|
||||
EventConfig, EventReceiver, ProgressReporter, SimpleEventManager,
|
||||
SimpleRestartingEventManager, launcher::Launcher,
|
||||
},
|
||||
events::{EventReceiver, ProgressReporter, SimpleEventManager},
|
||||
executors::ExitKind,
|
||||
monitors::{Monitor, MultiMonitor, tui::TuiMonitor},
|
||||
monitors::MultiMonitor,
|
||||
stages::StagesTuple,
|
||||
state::{HasCurrentStageId, HasExecutions, HasLastReportTime, HasSolutions, Stoppable},
|
||||
};
|
||||
#[cfg(unix)]
|
||||
use libafl::{
|
||||
events::{EventConfig, SimpleRestartingEventManager, launcher::Launcher},
|
||||
monitors::Monitor,
|
||||
};
|
||||
#[cfg(unix)]
|
||||
use libafl_bolts::{
|
||||
core_affinity::Cores,
|
||||
shmem::{ShMemProvider, StdShMemProvider},
|
||||
@ -22,25 +33,27 @@ use libafl_bolts::{
|
||||
|
||||
use crate::{feedbacks::LibfuzzerCrashCauseMetadata, fuzz_with, options::LibfuzzerOptions};
|
||||
|
||||
#[cfg(unix)]
|
||||
fn destroy_output_fds(options: &LibfuzzerOptions) {
|
||||
#[cfg(unix)]
|
||||
{
|
||||
use libafl_bolts::os::{dup2, null_fd};
|
||||
use libafl_bolts::os::{dup2, null_fd};
|
||||
|
||||
let null_fd = null_fd().unwrap();
|
||||
let stdout_fd = stdout().as_raw_fd();
|
||||
let stderr_fd = stderr().as_raw_fd();
|
||||
let null_fd = null_fd().unwrap();
|
||||
let stdout_fd = stdout().as_raw_fd();
|
||||
let stderr_fd = stderr().as_raw_fd();
|
||||
|
||||
if options.tui() {
|
||||
#[cfg(feature = "tui_monitor")]
|
||||
if options.tui() {
|
||||
dup2(null_fd, stdout_fd).unwrap();
|
||||
dup2(null_fd, stderr_fd).unwrap();
|
||||
return;
|
||||
}
|
||||
|
||||
if options.close_fd_mask() != 0 {
|
||||
if options.close_fd_mask() & u8::try_from(stderr_fd).unwrap() != 0 {
|
||||
dup2(null_fd, stdout_fd).unwrap();
|
||||
}
|
||||
if options.close_fd_mask() & u8::try_from(stderr_fd).unwrap() != 0 {
|
||||
dup2(null_fd, stderr_fd).unwrap();
|
||||
} else if options.close_fd_mask() != 0 {
|
||||
if options.close_fd_mask() & u8::try_from(stderr_fd).unwrap() != 0 {
|
||||
dup2(null_fd, stdout_fd).unwrap();
|
||||
}
|
||||
if options.close_fd_mask() & u8::try_from(stderr_fd).unwrap() != 0 {
|
||||
dup2(null_fd, stderr_fd).unwrap();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -87,10 +100,17 @@ where
|
||||
return Err(Error::shutting_down());
|
||||
}
|
||||
}
|
||||
fuzzer.fuzz_loop(stages, executor, state, mgr)?;
|
||||
if options.runs() == 0 {
|
||||
fuzzer.fuzz_loop(stages, executor, state, mgr)?;
|
||||
} else {
|
||||
for _ in 0..options.runs() {
|
||||
fuzzer.fuzz_one(stages, executor, state, mgr)?;
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[cfg(unix)]
|
||||
fn fuzz_single_forking<M>(
|
||||
options: &LibfuzzerOptions,
|
||||
harness: &extern "C" fn(*const u8, usize) -> c_int,
|
||||
@ -121,9 +141,7 @@ where
|
||||
})
|
||||
}
|
||||
|
||||
/// Communicate the selected port to subprocesses
|
||||
const PORT_PROVIDER_VAR: &str = "_LIBAFL_LIBFUZZER_FORK_PORT";
|
||||
|
||||
#[cfg(unix)]
|
||||
fn fuzz_many_forking<M>(
|
||||
options: &LibfuzzerOptions,
|
||||
harness: &extern "C" fn(*const u8, usize) -> c_int,
|
||||
@ -134,6 +152,9 @@ fn fuzz_many_forking<M>(
|
||||
where
|
||||
M: Monitor + Clone + Debug + 'static,
|
||||
{
|
||||
// Communicate the selected port to subprocesses
|
||||
const PORT_PROVIDER_VAR: &str = "_LIBAFL_LIBFUZZER_FORK_PORT";
|
||||
|
||||
destroy_output_fds(options);
|
||||
let broker_port = std::env::var(PORT_PROVIDER_VAR)
|
||||
.map_err(Error::from)
|
||||
@ -194,22 +215,31 @@ pub fn fuzz(
|
||||
options: &LibfuzzerOptions,
|
||||
harness: &extern "C" fn(*const u8, usize) -> c_int,
|
||||
) -> Result<(), Error> {
|
||||
#[cfg(unix)]
|
||||
if let Some(forks) = options.forks() {
|
||||
let shmem_provider = StdShMemProvider::new().expect("Failed to init shared memory");
|
||||
|
||||
#[cfg(feature = "tui_monitor")]
|
||||
if options.tui() {
|
||||
let monitor = TuiMonitor::builder()
|
||||
.title(options.fuzzer_name())
|
||||
.enhanced_graphics(true)
|
||||
.build();
|
||||
fuzz_many_forking(options, harness, shmem_provider, forks, monitor)
|
||||
} else if forks == 1 {
|
||||
let monitor = MultiMonitor::new(create_monitor_closure());
|
||||
fuzz_single_forking(options, harness, shmem_provider, monitor)
|
||||
} else {
|
||||
let monitor = MultiMonitor::new(create_monitor_closure());
|
||||
fuzz_many_forking(options, harness, shmem_provider, forks, monitor)
|
||||
return fuzz_many_forking(options, harness, shmem_provider, forks, monitor);
|
||||
}
|
||||
} else if options.tui() {
|
||||
|
||||
// Non-TUI path or when tui_monitor feature is disabled
|
||||
let monitor = MultiMonitor::new(create_monitor_closure());
|
||||
|
||||
if forks == 1 {
|
||||
return fuzz_single_forking(options, harness, shmem_provider, monitor);
|
||||
}
|
||||
|
||||
return fuzz_many_forking(options, harness, shmem_provider, forks, monitor);
|
||||
}
|
||||
|
||||
#[cfg(feature = "tui_monitor")]
|
||||
if options.tui() {
|
||||
// if the user specifies TUI, we assume they want to fork; it would not be possible to use
|
||||
// TUI safely otherwise
|
||||
let shmem_provider = StdShMemProvider::new().expect("Failed to init shared memory");
|
||||
@ -217,12 +247,15 @@ pub fn fuzz(
|
||||
.title(options.fuzzer_name())
|
||||
.enhanced_graphics(true)
|
||||
.build();
|
||||
fuzz_many_forking(options, harness, shmem_provider, 1, monitor)
|
||||
} else {
|
||||
destroy_output_fds(options);
|
||||
fuzz_with!(options, harness, do_fuzz, |fuzz_single| {
|
||||
let mgr = SimpleEventManager::new(MultiMonitor::new(create_monitor_closure()));
|
||||
crate::start_fuzzing_single(fuzz_single, None, mgr)
|
||||
})
|
||||
return fuzz_many_forking(options, harness, shmem_provider, 1, monitor);
|
||||
}
|
||||
|
||||
// Default path when no forks or TUI are specified, or when tui_monitor feature is disabled
|
||||
#[cfg(unix)]
|
||||
destroy_output_fds(options);
|
||||
|
||||
fuzz_with!(options, harness, do_fuzz, |fuzz_single| {
|
||||
let mgr = SimpleEventManager::new(MultiMonitor::new(create_monitor_closure()));
|
||||
crate::start_fuzzing_single(fuzz_single, None, mgr)
|
||||
})
|
||||
}
|
||||
|
@ -8,3 +8,23 @@ extern "C" int libafl_libfuzzer_test_one_input(
|
||||
return -2; // custom code for "we died!"
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef _WIN32
|
||||
// For Windows API functions used by MiMalloc
|
||||
#pragma comment(lib, "advapi32.lib")
|
||||
// For Windows networking functionality used by LibAFL
|
||||
#pragma comment(lib, "ws2_32.lib")
|
||||
// For Windows API functions to retrieve user home directory used by Rust STD
|
||||
#pragma comment(lib, "userenv.lib")
|
||||
// For base Windows API functions like file reads and writes
|
||||
#pragma comment(lib, "ntdll.lib")
|
||||
// For crypto functions called by LibAFL's random utilities
|
||||
#pragma comment(lib, "bcrypt.lib")
|
||||
// Required by windows_core
|
||||
#pragma comment(lib, "ole32.lib")
|
||||
// For debug facilities used in debug builds
|
||||
#pragma comment(lib, "dbghelp.lib")
|
||||
|
||||
#pragma comment(linker, "/export:LLVMFuzzerRunDriver")
|
||||
#pragma comment(linker, "/export:__sanitizer_cov_8bit_counters_init")
|
||||
#endif // _WIN32
|
||||
|
@ -67,8 +67,13 @@
|
||||
#![allow(clippy::borrow_deref_ref)]
|
||||
|
||||
use core::ffi::{CStr, c_char, c_int};
|
||||
use std::{fs::File, io::stderr, os::fd::RawFd};
|
||||
#[cfg(unix)]
|
||||
use std::{
|
||||
os::fd::RawFd,
|
||||
{fs::File, io::stderr},
|
||||
};
|
||||
|
||||
#[cfg(unix)]
|
||||
use env_logger::Target;
|
||||
use libafl::{
|
||||
Error,
|
||||
@ -328,11 +333,20 @@ macro_rules! fuzz_with {
|
||||
|
||||
// Attempt to use tokens from libfuzzer dicts
|
||||
if !state.has_metadata::<Tokens>() {
|
||||
#[cfg(any(target_os = "linux", target_vendor = "apple"))]
|
||||
let mut toks = if let Some(tokens) = $options.dict() {
|
||||
tokens.clone()
|
||||
} else {
|
||||
Tokens::default()
|
||||
};
|
||||
|
||||
#[cfg(not(any(target_os = "linux", target_vendor = "apple")))]
|
||||
let toks = if let Some(tokens) = $options.dict() {
|
||||
tokens.clone()
|
||||
} else {
|
||||
Tokens::default()
|
||||
};
|
||||
|
||||
#[cfg(any(target_os = "linux", target_vendor = "apple"))]
|
||||
{
|
||||
toks += libafl_targets::autotokens()?;
|
||||
|
@ -1,7 +1,7 @@
|
||||
use std::{env::temp_dir, ffi::c_int, fs::rename};
|
||||
#[cfg(unix)]
|
||||
use std::{
|
||||
env::temp_dir,
|
||||
ffi::c_int,
|
||||
fs::{File, rename},
|
||||
fs::File,
|
||||
io::Write,
|
||||
os::fd::{AsRawFd, FromRawFd},
|
||||
};
|
||||
|
@ -102,6 +102,7 @@ impl Default for ArtifactPrefix {
|
||||
#[derive(Debug, Clone)]
|
||||
#[expect(clippy::struct_excessive_bools)]
|
||||
pub struct LibfuzzerOptions {
|
||||
#[allow(unused)]
|
||||
fuzzer_name: String,
|
||||
mode: LibfuzzerMode,
|
||||
artifact_prefix: ArtifactPrefix,
|
||||
@ -122,6 +123,7 @@ pub struct LibfuzzerOptions {
|
||||
skip_tracing: bool,
|
||||
tui: bool,
|
||||
runs: usize,
|
||||
#[allow(unused)]
|
||||
close_fd_mask: u8,
|
||||
unknown: Vec<String>,
|
||||
}
|
||||
@ -144,6 +146,7 @@ impl LibfuzzerOptions {
|
||||
.map(|builder| builder.build(name))
|
||||
}
|
||||
|
||||
#[cfg(unix)]
|
||||
pub fn fuzzer_name(&self) -> &str {
|
||||
&self.fuzzer_name
|
||||
}
|
||||
@ -224,6 +227,7 @@ impl LibfuzzerOptions {
|
||||
self.runs
|
||||
}
|
||||
|
||||
#[cfg(unix)]
|
||||
pub fn close_fd_mask(&self) -> u8 {
|
||||
self.close_fd_mask
|
||||
}
|
||||
@ -318,6 +322,7 @@ impl<'a> LibfuzzerOptionsBuilder<'a> {
|
||||
})?);
|
||||
}
|
||||
"dict" => self.dict = Some(value),
|
||||
#[cfg(not(windows))]
|
||||
"fork" | "jobs" => {
|
||||
self.forks = Some(parse_or_bail!(name, value, usize));
|
||||
}
|
||||
@ -358,6 +363,47 @@ impl<'a> LibfuzzerOptionsBuilder<'a> {
|
||||
}
|
||||
"runs" => self.runs = parse_or_bail!(name, value, usize),
|
||||
"close_fd_mask" => self.close_fd_mask = parse_or_bail!(name, value, u8),
|
||||
"help" => {
|
||||
println!(
|
||||
"Usage:\n\
|
||||
\n\
|
||||
To run fuzzing pass 0 or more directories.\n\
|
||||
{name} [-flag1=val1 [-flag2=val2 ...] ] [dir1 [dir2 ...] ]\n\
|
||||
\n\
|
||||
To run individual tests without fuzzing pass 1 or more files:\n\
|
||||
{name} [-flag1=val1 [-flag2=val2 ...] ] file1 [file2 ...]\n\
|
||||
\n\
|
||||
Flags: (strictly in form -flag=value)\n\
|
||||
artifact_prefix 0 Write fuzzing artifacts (crash, timeout, or slow inputs) as $(artifact_prefix)file\n\
|
||||
timeout 1200 Timeout in seconds. If one unit runs more than this number of seconds the process will abort.\n\
|
||||
grimoire 0 If 1, enable the Grimoire mutator that is structure-aware.\n\
|
||||
use_value_profile 0 Use value profile to guide fuzzing.\n\
|
||||
unicode 1 If 1, generate Unicode inputs.\n\
|
||||
dict 0 Use the dictionary file.\n\
|
||||
fork 0 Number of forks to use (>1 requires Unix-like OS).\n\
|
||||
jobs 0 Same as fork. Number of jobs to run with stdout/stderr redirected.\n\
|
||||
ignore_crashes 0 If 1, ignore crashes in fork mode.\n\
|
||||
ignore_timeouts 0 If 1, ignore timeouts in fork mode.\n\
|
||||
ignore_ooms 0 If 1, ignore out-of-memory errors in fork mode.\n\
|
||||
rss_limit_mb 2048 If non-zero, the fuzzer will exit upon reaching this limit of RSS memory usage (in Mb).\n\
|
||||
malloc_limit_mb 2048 If non-zero, the fuzzer will exit if the target tries to allocate this number of Mb with one malloc call.\n\
|
||||
ignore_remaining_args 0 If 1, ignore all arguments passed after this one.\n\
|
||||
dedup 0 If 1, deduplicate corpus elements.\n\
|
||||
shrink 0 If 1, try to shrink corpus elements.\n\
|
||||
skip_tracing 0 If 1, skip coverage tracing for faster execution.\n\
|
||||
tui 0 If 1, use the terminal UI interface.\n\
|
||||
runs 0 Number of individual test runs (0 for infinite runs).\n\
|
||||
close_fd_mask 0 If 1, close stdout; if 2, close stderr; if 3, close both.\n\
|
||||
merge 0 If 1, merge multiple corpora into a single one.\n\
|
||||
minimize_crash 0 If 1, minimize crashes to their smallest reproducing input.\n\
|
||||
report 0 If 1, report statistics without actually fuzzing.\n\
|
||||
help 0 Print this help message.\n\
|
||||
\n\
|
||||
Flags starting with '--' will be ignored and will be passed verbatim to subprocesses.\n\
|
||||
"
|
||||
);
|
||||
std::process::exit(0);
|
||||
}
|
||||
_ => {
|
||||
self.unknown.push(arg);
|
||||
}
|
||||
|
@ -3,11 +3,15 @@ use std::{
|
||||
fs::{read, write},
|
||||
};
|
||||
|
||||
#[cfg(windows)]
|
||||
use libafl::executors::inprocess::InProcessExecutor;
|
||||
#[cfg(unix)]
|
||||
use libafl::executors::inprocess_fork::InProcessForkExecutor;
|
||||
use libafl::{
|
||||
Error, ExecutesInput, Fuzzer, StdFuzzer,
|
||||
corpus::{Corpus, HasTestcase, InMemoryCorpus, Testcase},
|
||||
events::SimpleEventManager,
|
||||
executors::{ExitKind, inprocess_fork::InProcessForkExecutor},
|
||||
executors::ExitKind,
|
||||
feedbacks::{CrashFeedback, TimeoutFeedback},
|
||||
inputs::{BytesInput, HasMutatorBytes, HasTargetBytes},
|
||||
mutators::{HavocScheduledMutator, Mutator, havoc_mutations_no_crossover},
|
||||
@ -15,10 +19,11 @@ use libafl::{
|
||||
stages::StdTMinMutationalStage,
|
||||
state::{HasCorpus, StdState},
|
||||
};
|
||||
#[cfg(unix)]
|
||||
use libafl_bolts::shmem::{ShMemProvider, StdShMemProvider};
|
||||
use libafl_bolts::{
|
||||
AsSlice, HasLen,
|
||||
rands::{RomuDuoJrRand, StdRand},
|
||||
shmem::{ShMemProvider, StdShMemProvider},
|
||||
tuples::tuple_list,
|
||||
};
|
||||
use libafl_targets::LLVMCustomMutator;
|
||||
@ -60,15 +65,28 @@ fn minimize_crash_with_mutator<M: Mutator<BytesInput, TMinState>>(
|
||||
|
||||
let mut fuzzer = StdFuzzer::new(QueueScheduler::new(), (), ());
|
||||
|
||||
let shmem_provider = StdShMemProvider::new()?;
|
||||
let mut executor = InProcessForkExecutor::new(
|
||||
#[cfg(unix)]
|
||||
let mut executor = {
|
||||
let shmem_provider = StdShMemProvider::new()?;
|
||||
InProcessForkExecutor::new(
|
||||
&mut harness,
|
||||
(),
|
||||
&mut fuzzer,
|
||||
&mut state,
|
||||
&mut mgr,
|
||||
options.timeout(),
|
||||
shmem_provider,
|
||||
)?
|
||||
};
|
||||
|
||||
#[cfg(windows)]
|
||||
let mut executor = InProcessExecutor::with_timeout(
|
||||
&mut harness,
|
||||
(),
|
||||
&mut fuzzer,
|
||||
&mut state,
|
||||
&mut mgr,
|
||||
options.timeout(),
|
||||
shmem_provider,
|
||||
)?;
|
||||
|
||||
let exit_kind = fuzzer.execute_input(&mut state, &mut executor, &mut mgr, &input)?;
|
||||
|
10
libafl_libfuzzer_runtime/Justfile
Normal file
10
libafl_libfuzzer_runtime/Justfile
Normal file
@ -0,0 +1,10 @@
|
||||
set windows-shell := ['cmd.exe', '/c']
|
||||
set unstable
|
||||
|
||||
[unix]
|
||||
build:
|
||||
./build.sh
|
||||
|
||||
[windows]
|
||||
build:
|
||||
powershell -File build.ps1
|
46
libafl_libfuzzer_runtime/build.ps1
Normal file
46
libafl_libfuzzer_runtime/build.ps1
Normal file
@ -0,0 +1,46 @@
|
||||
#!/usr/bin/env pwsh
|
||||
|
||||
$ErrorActionPreference = "Stop"
|
||||
|
||||
$SCRIPT_DIR = Split-Path -Parent $MyInvocation.MyCommand.Path
|
||||
|
||||
Set-Location $SCRIPT_DIR
|
||||
|
||||
if ($args.Count -eq 0) {
|
||||
$profile = "release"
|
||||
} else {
|
||||
$profile = $args[0]
|
||||
}
|
||||
|
||||
Write-Host "Building libafl_libfuzzer runtime with profile '$profile'" -ForegroundColor Green
|
||||
Invoke-Expression "cargo build --profile $profile"
|
||||
|
||||
$tmpdir = Join-Path $env:TEMP ([System.IO.Path]::GetRandomFileName())
|
||||
New-Item -ItemType Directory -Path $tmpdir | Out-Null
|
||||
|
||||
function Cleanup {
|
||||
if (Test-Path $tmpdir) {
|
||||
Remove-Item -Recurse -Force $tmpdir
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
if ($profile -eq "dev") {
|
||||
# Set the profile to debug for dev builds, because the path isn't the same
|
||||
# as the profile name
|
||||
$profile = "debug"
|
||||
}
|
||||
|
||||
$targetPath = Join-Path $SCRIPT_DIR "target\$profile\afl_libfuzzer_runtime.lib"
|
||||
$outputPath = Join-Path $SCRIPT_DIR "libFuzzer.lib"
|
||||
|
||||
Copy-Item -Path $targetPath -Destination $outputPath -Force | Out-Null
|
||||
|
||||
if ($LASTEXITCODE -ne 0) {
|
||||
throw "Failed to copy final library"
|
||||
}
|
||||
|
||||
Write-Host "Done! Wrote the runtime to '$outputPath'" -ForegroundColor Green
|
||||
} finally {
|
||||
Cleanup
|
||||
}
|
@ -17,8 +17,9 @@ fn main() {
|
||||
.write_to_file(Path::new(&out_dir).join("harness_wrap.rs"))
|
||||
.expect("Couldn't write the harness wrapper!");
|
||||
|
||||
cc::Build::new()
|
||||
.cpp(true)
|
||||
.file("src/harness_wrap.cpp")
|
||||
.compile("harness_wrap");
|
||||
let mut harness_wrap = cc::Build::new();
|
||||
|
||||
harness_wrap.cpp(true).file("src/harness_wrap.cpp");
|
||||
|
||||
harness_wrap.compile("harness_wrap");
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user