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/inprocess/libfuzzer_stb_image
|
||||||
# - ./fuzzers/structure_aware/libfuzzer_stb_image_concolic
|
# - ./fuzzers/structure_aware/libfuzzer_stb_image_concolic
|
||||||
# - ./fuzzers/inprocess/sqlite_centralized_multi_machine
|
# - ./fuzzers/inprocess/sqlite_centralized_multi_machine
|
||||||
|
# - ./fuzzers/inprocess/libafl_libfuzzer_windows
|
||||||
|
|
||||||
# Fuzz Anything
|
# Fuzz Anything
|
||||||
- ./fuzzers/fuzz_anything/push_harness
|
- ./fuzzers/fuzz_anything/push_harness
|
||||||
@ -569,6 +570,16 @@ jobs:
|
|||||||
- name: Build fuzzers/binary_only/frida_libpng
|
- name: Build fuzzers/binary_only/frida_libpng
|
||||||
run: cd fuzzers/binary_only/frida_libpng/ && just test
|
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:
|
windows-libfuzzer-stb-image:
|
||||||
runs-on: windows-latest
|
runs-on: windows-latest
|
||||||
needs:
|
needs:
|
||||||
|
@ -47,7 +47,7 @@ lib2: libpng
|
|||||||
|
|
||||||
[windows]
|
[windows]
|
||||||
harness: lib lib2
|
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
|
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]
|
[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:
|
[`libafl_libfuzzer_runtime`](../libafl_libfuzzer_runtime) folder and build the runtime with the following command:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
./build.sh
|
just build
|
||||||
```
|
```
|
||||||
|
|
||||||
The static library will be available at `libFuzzer.a` in the [`libafl_libfuzzer_runtime`](../libafl_libfuzzer_runtime)
|
Or you can call `build.sh` (Unix) or `build.ps1` (Windows).
|
||||||
directory.
|
|
||||||
If you encounter build failures without clear error outputs that help you resolve the issue, please [submit an issue].
|
|
||||||
|
|
||||||
This library may now be used in place of libFuzzer.
|
The static library will be available at `libFuzzer.a` (`libFuzzer.lib` for Windows) in
|
||||||
To do so, change your CFLAGS/CXXFLAGS from `-fsanitize=fuzzer` to:
|
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].
|
||||||
|
|
||||||
```
|
#### Unix
|
||||||
-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 library may now be used in place of libFuzzer. To do so, change your
|
||||||
This changes per system, but on my machine is located at `/usr/lib64/clang/16/lib/linux/libclang_rt.fuzzer-x86_64.a`.
|
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
|
#### Caveats
|
||||||
|
|
||||||
|
@ -4,16 +4,19 @@ version = "0.15.2"
|
|||||||
edition = "2024"
|
edition = "2024"
|
||||||
publish = false
|
publish = false
|
||||||
|
|
||||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
default = ["fork"]
|
default = []
|
||||||
## Enables forking mode for the LibAFL launcher (instead of starting new processes)
|
## Enables forking mode for the LibAFL launcher (instead of starting new processes)
|
||||||
fork = ["libafl/fork"]
|
fork = ["libafl/fork"]
|
||||||
track_hit_feedbacks = [
|
track_hit_feedbacks = [
|
||||||
"libafl/track_hit_feedbacks",
|
"libafl/track_hit_feedbacks",
|
||||||
"libafl_targets/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]
|
[profile.release]
|
||||||
lto = true
|
lto = true
|
||||||
@ -40,7 +43,6 @@ libafl = { path = "../libafl", default-features = false, features = [
|
|||||||
"regex",
|
"regex",
|
||||||
"errors_backtrace",
|
"errors_backtrace",
|
||||||
"serdeany_autoreg",
|
"serdeany_autoreg",
|
||||||
"tui_monitor",
|
|
||||||
"unicode",
|
"unicode",
|
||||||
] }
|
] }
|
||||||
libafl_bolts = { path = "../libafl_bolts", default-features = false, features = [
|
libafl_bolts = { path = "../libafl_bolts", default-features = false, features = [
|
||||||
|
@ -154,7 +154,7 @@ where
|
|||||||
}
|
}
|
||||||
#[inline]
|
#[inline]
|
||||||
fn count_all(&self) -> usize {
|
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)]
|
#[expect(clippy::used_underscore_items)]
|
||||||
|
@ -1,20 +1,31 @@
|
|||||||
use core::ffi::c_int;
|
use core::ffi::c_int;
|
||||||
#[cfg(unix)]
|
#[cfg(unix)]
|
||||||
use std::io::{Write, stderr, stdout};
|
use std::{
|
||||||
use std::{fmt::Debug, fs::File, net::TcpListener, os::fd::AsRawFd, str::FromStr};
|
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::{
|
use libafl::{
|
||||||
Error, Fuzzer, HasMetadata,
|
Error, Fuzzer, HasMetadata,
|
||||||
corpus::Corpus,
|
corpus::Corpus,
|
||||||
events::{
|
events::{EventReceiver, ProgressReporter, SimpleEventManager},
|
||||||
EventConfig, EventReceiver, ProgressReporter, SimpleEventManager,
|
|
||||||
SimpleRestartingEventManager, launcher::Launcher,
|
|
||||||
},
|
|
||||||
executors::ExitKind,
|
executors::ExitKind,
|
||||||
monitors::{Monitor, MultiMonitor, tui::TuiMonitor},
|
monitors::MultiMonitor,
|
||||||
stages::StagesTuple,
|
stages::StagesTuple,
|
||||||
state::{HasCurrentStageId, HasExecutions, HasLastReportTime, HasSolutions, Stoppable},
|
state::{HasCurrentStageId, HasExecutions, HasLastReportTime, HasSolutions, Stoppable},
|
||||||
};
|
};
|
||||||
|
#[cfg(unix)]
|
||||||
|
use libafl::{
|
||||||
|
events::{EventConfig, SimpleRestartingEventManager, launcher::Launcher},
|
||||||
|
monitors::Monitor,
|
||||||
|
};
|
||||||
|
#[cfg(unix)]
|
||||||
use libafl_bolts::{
|
use libafl_bolts::{
|
||||||
core_affinity::Cores,
|
core_affinity::Cores,
|
||||||
shmem::{ShMemProvider, StdShMemProvider},
|
shmem::{ShMemProvider, StdShMemProvider},
|
||||||
@ -22,25 +33,27 @@ use libafl_bolts::{
|
|||||||
|
|
||||||
use crate::{feedbacks::LibfuzzerCrashCauseMetadata, fuzz_with, options::LibfuzzerOptions};
|
use crate::{feedbacks::LibfuzzerCrashCauseMetadata, fuzz_with, options::LibfuzzerOptions};
|
||||||
|
|
||||||
|
#[cfg(unix)]
|
||||||
fn destroy_output_fds(options: &LibfuzzerOptions) {
|
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 null_fd = null_fd().unwrap();
|
||||||
let stdout_fd = stdout().as_raw_fd();
|
let stdout_fd = stdout().as_raw_fd();
|
||||||
let stderr_fd = stderr().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();
|
dup2(null_fd, stdout_fd).unwrap();
|
||||||
|
}
|
||||||
|
if options.close_fd_mask() & u8::try_from(stderr_fd).unwrap() != 0 {
|
||||||
dup2(null_fd, stderr_fd).unwrap();
|
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());
|
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(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(unix)]
|
||||||
fn fuzz_single_forking<M>(
|
fn fuzz_single_forking<M>(
|
||||||
options: &LibfuzzerOptions,
|
options: &LibfuzzerOptions,
|
||||||
harness: &extern "C" fn(*const u8, usize) -> c_int,
|
harness: &extern "C" fn(*const u8, usize) -> c_int,
|
||||||
@ -121,9 +141,7 @@ where
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Communicate the selected port to subprocesses
|
#[cfg(unix)]
|
||||||
const PORT_PROVIDER_VAR: &str = "_LIBAFL_LIBFUZZER_FORK_PORT";
|
|
||||||
|
|
||||||
fn fuzz_many_forking<M>(
|
fn fuzz_many_forking<M>(
|
||||||
options: &LibfuzzerOptions,
|
options: &LibfuzzerOptions,
|
||||||
harness: &extern "C" fn(*const u8, usize) -> c_int,
|
harness: &extern "C" fn(*const u8, usize) -> c_int,
|
||||||
@ -134,6 +152,9 @@ fn fuzz_many_forking<M>(
|
|||||||
where
|
where
|
||||||
M: Monitor + Clone + Debug + 'static,
|
M: Monitor + Clone + Debug + 'static,
|
||||||
{
|
{
|
||||||
|
// Communicate the selected port to subprocesses
|
||||||
|
const PORT_PROVIDER_VAR: &str = "_LIBAFL_LIBFUZZER_FORK_PORT";
|
||||||
|
|
||||||
destroy_output_fds(options);
|
destroy_output_fds(options);
|
||||||
let broker_port = std::env::var(PORT_PROVIDER_VAR)
|
let broker_port = std::env::var(PORT_PROVIDER_VAR)
|
||||||
.map_err(Error::from)
|
.map_err(Error::from)
|
||||||
@ -194,22 +215,31 @@ pub fn fuzz(
|
|||||||
options: &LibfuzzerOptions,
|
options: &LibfuzzerOptions,
|
||||||
harness: &extern "C" fn(*const u8, usize) -> c_int,
|
harness: &extern "C" fn(*const u8, usize) -> c_int,
|
||||||
) -> Result<(), Error> {
|
) -> Result<(), Error> {
|
||||||
|
#[cfg(unix)]
|
||||||
if let Some(forks) = options.forks() {
|
if let Some(forks) = options.forks() {
|
||||||
let shmem_provider = StdShMemProvider::new().expect("Failed to init shared memory");
|
let shmem_provider = StdShMemProvider::new().expect("Failed to init shared memory");
|
||||||
|
|
||||||
|
#[cfg(feature = "tui_monitor")]
|
||||||
if options.tui() {
|
if options.tui() {
|
||||||
let monitor = TuiMonitor::builder()
|
let monitor = TuiMonitor::builder()
|
||||||
.title(options.fuzzer_name())
|
.title(options.fuzzer_name())
|
||||||
.enhanced_graphics(true)
|
.enhanced_graphics(true)
|
||||||
.build();
|
.build();
|
||||||
fuzz_many_forking(options, harness, shmem_provider, forks, monitor)
|
return 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)
|
|
||||||
}
|
}
|
||||||
} 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
|
// if the user specifies TUI, we assume they want to fork; it would not be possible to use
|
||||||
// TUI safely otherwise
|
// TUI safely otherwise
|
||||||
let shmem_provider = StdShMemProvider::new().expect("Failed to init shared memory");
|
let shmem_provider = StdShMemProvider::new().expect("Failed to init shared memory");
|
||||||
@ -217,12 +247,15 @@ pub fn fuzz(
|
|||||||
.title(options.fuzzer_name())
|
.title(options.fuzzer_name())
|
||||||
.enhanced_graphics(true)
|
.enhanced_graphics(true)
|
||||||
.build();
|
.build();
|
||||||
fuzz_many_forking(options, harness, shmem_provider, 1, monitor)
|
return 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)
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 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!"
|
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)]
|
#![allow(clippy::borrow_deref_ref)]
|
||||||
|
|
||||||
use core::ffi::{CStr, c_char, c_int};
|
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 env_logger::Target;
|
||||||
use libafl::{
|
use libafl::{
|
||||||
Error,
|
Error,
|
||||||
@ -328,11 +333,20 @@ macro_rules! fuzz_with {
|
|||||||
|
|
||||||
// Attempt to use tokens from libfuzzer dicts
|
// Attempt to use tokens from libfuzzer dicts
|
||||||
if !state.has_metadata::<Tokens>() {
|
if !state.has_metadata::<Tokens>() {
|
||||||
|
#[cfg(any(target_os = "linux", target_vendor = "apple"))]
|
||||||
let mut toks = if let Some(tokens) = $options.dict() {
|
let mut toks = if let Some(tokens) = $options.dict() {
|
||||||
tokens.clone()
|
tokens.clone()
|
||||||
} else {
|
} else {
|
||||||
Tokens::default()
|
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"))]
|
#[cfg(any(target_os = "linux", target_vendor = "apple"))]
|
||||||
{
|
{
|
||||||
toks += libafl_targets::autotokens()?;
|
toks += libafl_targets::autotokens()?;
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
|
use std::{env::temp_dir, ffi::c_int, fs::rename};
|
||||||
|
#[cfg(unix)]
|
||||||
use std::{
|
use std::{
|
||||||
env::temp_dir,
|
fs::File,
|
||||||
ffi::c_int,
|
|
||||||
fs::{File, rename},
|
|
||||||
io::Write,
|
io::Write,
|
||||||
os::fd::{AsRawFd, FromRawFd},
|
os::fd::{AsRawFd, FromRawFd},
|
||||||
};
|
};
|
||||||
|
@ -102,6 +102,7 @@ impl Default for ArtifactPrefix {
|
|||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
#[expect(clippy::struct_excessive_bools)]
|
#[expect(clippy::struct_excessive_bools)]
|
||||||
pub struct LibfuzzerOptions {
|
pub struct LibfuzzerOptions {
|
||||||
|
#[allow(unused)]
|
||||||
fuzzer_name: String,
|
fuzzer_name: String,
|
||||||
mode: LibfuzzerMode,
|
mode: LibfuzzerMode,
|
||||||
artifact_prefix: ArtifactPrefix,
|
artifact_prefix: ArtifactPrefix,
|
||||||
@ -122,6 +123,7 @@ pub struct LibfuzzerOptions {
|
|||||||
skip_tracing: bool,
|
skip_tracing: bool,
|
||||||
tui: bool,
|
tui: bool,
|
||||||
runs: usize,
|
runs: usize,
|
||||||
|
#[allow(unused)]
|
||||||
close_fd_mask: u8,
|
close_fd_mask: u8,
|
||||||
unknown: Vec<String>,
|
unknown: Vec<String>,
|
||||||
}
|
}
|
||||||
@ -144,6 +146,7 @@ impl LibfuzzerOptions {
|
|||||||
.map(|builder| builder.build(name))
|
.map(|builder| builder.build(name))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(unix)]
|
||||||
pub fn fuzzer_name(&self) -> &str {
|
pub fn fuzzer_name(&self) -> &str {
|
||||||
&self.fuzzer_name
|
&self.fuzzer_name
|
||||||
}
|
}
|
||||||
@ -224,6 +227,7 @@ impl LibfuzzerOptions {
|
|||||||
self.runs
|
self.runs
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(unix)]
|
||||||
pub fn close_fd_mask(&self) -> u8 {
|
pub fn close_fd_mask(&self) -> u8 {
|
||||||
self.close_fd_mask
|
self.close_fd_mask
|
||||||
}
|
}
|
||||||
@ -318,6 +322,7 @@ impl<'a> LibfuzzerOptionsBuilder<'a> {
|
|||||||
})?);
|
})?);
|
||||||
}
|
}
|
||||||
"dict" => self.dict = Some(value),
|
"dict" => self.dict = Some(value),
|
||||||
|
#[cfg(not(windows))]
|
||||||
"fork" | "jobs" => {
|
"fork" | "jobs" => {
|
||||||
self.forks = Some(parse_or_bail!(name, value, usize));
|
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),
|
"runs" => self.runs = parse_or_bail!(name, value, usize),
|
||||||
"close_fd_mask" => self.close_fd_mask = parse_or_bail!(name, value, u8),
|
"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);
|
self.unknown.push(arg);
|
||||||
}
|
}
|
||||||
|
@ -3,11 +3,15 @@ use std::{
|
|||||||
fs::{read, write},
|
fs::{read, write},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
#[cfg(windows)]
|
||||||
|
use libafl::executors::inprocess::InProcessExecutor;
|
||||||
|
#[cfg(unix)]
|
||||||
|
use libafl::executors::inprocess_fork::InProcessForkExecutor;
|
||||||
use libafl::{
|
use libafl::{
|
||||||
Error, ExecutesInput, Fuzzer, StdFuzzer,
|
Error, ExecutesInput, Fuzzer, StdFuzzer,
|
||||||
corpus::{Corpus, HasTestcase, InMemoryCorpus, Testcase},
|
corpus::{Corpus, HasTestcase, InMemoryCorpus, Testcase},
|
||||||
events::SimpleEventManager,
|
events::SimpleEventManager,
|
||||||
executors::{ExitKind, inprocess_fork::InProcessForkExecutor},
|
executors::ExitKind,
|
||||||
feedbacks::{CrashFeedback, TimeoutFeedback},
|
feedbacks::{CrashFeedback, TimeoutFeedback},
|
||||||
inputs::{BytesInput, HasMutatorBytes, HasTargetBytes},
|
inputs::{BytesInput, HasMutatorBytes, HasTargetBytes},
|
||||||
mutators::{HavocScheduledMutator, Mutator, havoc_mutations_no_crossover},
|
mutators::{HavocScheduledMutator, Mutator, havoc_mutations_no_crossover},
|
||||||
@ -15,10 +19,11 @@ use libafl::{
|
|||||||
stages::StdTMinMutationalStage,
|
stages::StdTMinMutationalStage,
|
||||||
state::{HasCorpus, StdState},
|
state::{HasCorpus, StdState},
|
||||||
};
|
};
|
||||||
|
#[cfg(unix)]
|
||||||
|
use libafl_bolts::shmem::{ShMemProvider, StdShMemProvider};
|
||||||
use libafl_bolts::{
|
use libafl_bolts::{
|
||||||
AsSlice, HasLen,
|
AsSlice, HasLen,
|
||||||
rands::{RomuDuoJrRand, StdRand},
|
rands::{RomuDuoJrRand, StdRand},
|
||||||
shmem::{ShMemProvider, StdShMemProvider},
|
|
||||||
tuples::tuple_list,
|
tuples::tuple_list,
|
||||||
};
|
};
|
||||||
use libafl_targets::LLVMCustomMutator;
|
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 mut fuzzer = StdFuzzer::new(QueueScheduler::new(), (), ());
|
||||||
|
|
||||||
let shmem_provider = StdShMemProvider::new()?;
|
#[cfg(unix)]
|
||||||
let mut executor = InProcessForkExecutor::new(
|
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 harness,
|
||||||
(),
|
(),
|
||||||
&mut fuzzer,
|
&mut fuzzer,
|
||||||
&mut state,
|
&mut state,
|
||||||
&mut mgr,
|
&mut mgr,
|
||||||
options.timeout(),
|
options.timeout(),
|
||||||
shmem_provider,
|
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
let exit_kind = fuzzer.execute_input(&mut state, &mut executor, &mut mgr, &input)?;
|
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"))
|
.write_to_file(Path::new(&out_dir).join("harness_wrap.rs"))
|
||||||
.expect("Couldn't write the harness wrapper!");
|
.expect("Couldn't write the harness wrapper!");
|
||||||
|
|
||||||
cc::Build::new()
|
let mut harness_wrap = cc::Build::new();
|
||||||
.cpp(true)
|
|
||||||
.file("src/harness_wrap.cpp")
|
harness_wrap.cpp(true).file("src/harness_wrap.cpp");
|
||||||
.compile("harness_wrap");
|
|
||||||
|
harness_wrap.compile("harness_wrap");
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user