-
-
Notifications
You must be signed in to change notification settings - Fork 370
Update forkserver.rs #3138
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
20urc3
wants to merge
3
commits into
AFLplusplus:main
Choose a base branch
from
20urc3:patch-1
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Update forkserver.rs #3138
Changes from all commits
Commits
Show all changes
3 commits
Select commit
Hold shift + click to select a range
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,4 +1,22 @@ | ||
//! Expose an `Executor` based on a `Forkserver` in order to execute AFL/AFL++ binaries | ||
//! # LibAFL Forkserver Executor | ||
//! | ||
//! This module implements an executor that communicates with LibAFL instrumented binaries | ||
//! through a forkserver mechanism. The forkserver allows for efficient process creation by | ||
//! forking from a pre-initialized parent process, avoiding the overhead of repeatedly executing | ||
//! the program from scratch. | ||
//! | ||
//! Key features: | ||
//! - Support for LibAFL (libafl_cc/libafl_cxx) instrumented binaries | ||
//! - Compatible with AFL/AFL++ instrumentation | ||
//! - Shared memory testcase passing for improved performance | ||
//! - Support for persistent mode execution | ||
//! - Handling of deferred forkserver initialization | ||
//! - Automatic dictionary token extraction | ||
//! - Timeout and signal handling for target programs | ||
//! - Compatible with various observer types for feedback collection | ||
//! | ||
//! This implementation follows the forkserver protocol and provides | ||
//! a flexible builder pattern for configuration. | ||
|
||
use alloc::{borrow::ToOwned, string::ToString, vec::Vec}; | ||
use core::{ | ||
|
@@ -94,28 +112,49 @@ const FS_ERROR_OLD_CMPLOG_QEMU: i32 = 64_u32 as i32; | |
/// Forkserver message. We'll reuse it in a testcase. | ||
const FAILED_TO_START_FORKSERVER_MSG: &str = "Failed to start forkserver"; | ||
|
||
/// Translates forkserver error codes into human-readable error messages | ||
/// | ||
/// This function interprets error statuses received from the forkserver and returns | ||
/// appropriate error messages with troubleshooting advice. It handles various failure modes | ||
/// such as map size issues, shared memory problems, and compatibility errors. | ||
/// | ||
/// # Arguments | ||
/// * `status` - The error status code received from the forkserver | ||
/// | ||
/// # Returns | ||
/// * `Result<(), Error>` - Always returns `Err` with a specific error type that includes | ||
/// a descriptive message explaining the failure and suggesting possible solutions | ||
/// | ||
/// # Error Codes and Corresponding Error Types | ||
/// * `FS_ERROR_MAP_SIZE` - Returns `Error::Configuration` for coverage map size issues | ||
/// * `FS_ERROR_MAP_ADDR` - Returns `Error::Compilation` for hardcoded map address conflicts | ||
/// * `FS_ERROR_SHM_OPEN` - Returns `Error::System` for shared memory opening failures | ||
/// * `FS_ERROR_SHMAT` - Returns `Error::System` for shared memory attachment failures | ||
/// * `FS_ERROR_MMAP` - Returns `Error::System` for memory mapping failures | ||
/// * `FS_ERROR_OLD_CMPLOG` - Returns `Error::Version` for outdated instrumentation | ||
/// * `FS_ERROR_OLD_CMPLOG_QEMU` - Returns `Error::Version` for outdated loader versions | ||
fn report_error_and_exit(status: i32) -> Result<(), Error> { | ||
/* Report on the error received via the forkserver controller and exit */ | ||
match status { | ||
FS_ERROR_MAP_SIZE => | ||
Err(Error::unknown( | ||
"AFL_MAP_SIZE is not set and fuzzing target reports that the required size is very large. Solution: Run the fuzzing target stand-alone with the environment variable AFL_DEBUG=1 set and set the value for __afl_final_loc in the AFL_MAP_SIZE environment variable for afl-fuzz.".to_string())), | ||
Err(Error::configuration( | ||
"AFL_MAP_SIZE is not set and fuzzing target reports that the required size is very large. Solution: Run the fuzzing target stand-alone with the environment variable AFL_DEBUG=1 set and set the value for __afl_final_loc in the AFL_MAP_SIZE environment variable for afl-fuzz.")), | ||
FS_ERROR_MAP_ADDR => | ||
Err(Error::unknown( | ||
"the fuzzing target reports that hardcoded map address might be the reason the mmap of the shared memory failed. Solution: recompile the target with either afl-clang-lto and do not set AFL_LLVM_MAP_ADDR or recompile with afl-clang-fast.".to_string())), | ||
Err(Error::compilation( | ||
"The fuzzing target reports that hardcoded map address might be the reason the mmap of the shared memory failed. Solution: recompile the target with either afl-clang-lto and do not set AFL_LLVM_MAP_ADDR or recompile with afl-clang-fast.")), | ||
FS_ERROR_SHM_OPEN => | ||
Err(Error::unknown("the fuzzing target reports that the shm_open() call failed.".to_string())), | ||
Err(Error::system("The fuzzing target reports that the shm_open() call failed.")), | ||
FS_ERROR_SHMAT => | ||
Err(Error::unknown("the fuzzing target reports that the shmat() call failed.".to_string())), | ||
Err(Error::system("The fuzzing target reports that the shmat() call failed.")), | ||
FS_ERROR_MMAP => | ||
Err(Error::unknown("the fuzzing target reports that the mmap() call to the shared memory failed.".to_string())), | ||
Err(Error::system("The fuzzing target reports that the mmap() call to the shared memory failed.")), | ||
FS_ERROR_OLD_CMPLOG => | ||
Err(Error::unknown( | ||
"the -c cmplog target was instrumented with an too old AFL++ version, you need to recompile it.".to_string())), | ||
Err(Error::version( | ||
"The -c cmplog target was instrumented with an too old AFL++ version, you need to recompile it.")), | ||
FS_ERROR_OLD_CMPLOG_QEMU => | ||
Err(Error::unknown("The AFL++ QEMU/FRIDA loaders are from an older version, for -c you need to recompile it.".to_string())), | ||
Err(Error::version("The AFL++ QEMU/FRIDA loaders are from an older version, for -c you need to recompile it.")), | ||
_ => | ||
Err(Error::unknown(format!("unknown error code {status} from fuzzing target!"))), | ||
Err(Error::unknown(format!("Unknown error code {status} from fuzzing target!"))), | ||
} | ||
} | ||
|
||
|
@@ -253,8 +292,12 @@ impl ConfigTarget for Command { | |
} | ||
} | ||
|
||
/// The [`Forkserver`] is communication channel with a child process that forks on request of the fuzzer. | ||
/// The communication happens via pipe. | ||
/// A communication channel with an instrumented target process that handles efficient forking | ||
/// | ||
/// The Forkserver implements the LibAFL/AFL++ forkserver protocol, allowing the fuzzer to | ||
/// request new process instances without the overhead of loading the program from scratch. | ||
/// It communicates with the target via pipes for control messages and status information, | ||
/// and manages child process creation, monitoring, and termination. | ||
#[derive(Debug)] | ||
pub struct Forkserver { | ||
/// The "actual" forkserver we spawned in the target | ||
|
@@ -500,7 +543,16 @@ impl Forkserver { | |
} | ||
} | ||
|
||
/// Read bytes of any length from the st pipe | ||
/// Reads exactly `size` bytes from the status pipe | ||
/// | ||
/// Efficiently allocates and fills a buffer with the exact number of bytes | ||
/// requested from the forkserver's status pipe. | ||
/// | ||
/// # Arguments | ||
/// * `size` - Number of bytes to read | ||
/// | ||
/// # Returns | ||
/// The read bytes or an error if the read fails | ||
pub fn read_st_of_len(&mut self, size: usize) -> Result<Vec<u8>, Error> { | ||
let mut buf = Vec::with_capacity(size); | ||
// SAFETY: `buf` will not be returned with `Ok` unless it is filled with `size` bytes. | ||
|
@@ -550,6 +602,13 @@ impl Forkserver { | |
let mut readfds = FdSet::new(); | ||
readfds.insert(st_read); | ||
// We'll pass a copied timeout to keep the original timeout intact, because select updates timeout to indicate how much time was left. See select(2) | ||
|
||
// Use pselect to wait for data with timeout protection | ||
// If data is available (sret > 0), read a 4-byte integer from the pipe | ||
// Returns: | ||
// - Ok(Some(val)) if we successfully read a value | ||
// - Err if communication fails | ||
// - Ok(None) if timeout occurs with no data | ||
let sret = pselect( | ||
Some(readfds.highest().unwrap().as_raw_fd() + 1), | ||
&mut readfds, | ||
|
@@ -573,10 +632,15 @@ impl Forkserver { | |
} | ||
} | ||
|
||
/// This [`Executor`] can run binaries compiled for AFL/AFL++ that make use of a forkserver. | ||
/// An executor that runs LibAFL-instrumented binaries via a forkserver protocol | ||
/// | ||
/// This executor communicates with instrumented targets using the forkserver mechanism | ||
/// for efficient process creation. It supports shared memory-based test case passing, | ||
/// customizable timeouts, and can handle persistent mode execution. | ||
/// | ||
/// Shared memory feature is also available, but you have to set things up in your code. | ||
/// Please refer to AFL++'s docs. <https://github.com/AFLplusplus/AFLplusplus/blob/stable/instrumentation/README.persistent_mode.md> | ||
/// For persistent mode details, see: | ||
/// <https://github.com/AFLplusplus/AFLplusplus/blob/stable/instrumentation/README.persistent_mode.md> | ||
|
||
pub struct ForkserverExecutor<I, OT, S, SHM, TC> { | ||
target: OsString, | ||
args: Vec<OsString>, | ||
|
@@ -769,7 +833,17 @@ where | |
} | ||
} | ||
|
||
/// The builder for `ForkserverExecutor` | ||
/// Builder for [`ForkserverExecutor`] with a fluent interface for configuration | ||
/// | ||
/// Provides methods to customize all aspects of forkserver execution: | ||
/// - Target program path and arguments | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. forkserver instantiation |
||
/// - Input handling (file, stdin, shared memory) | ||
/// - Execution parameters (timeouts, signals) | ||
/// - Performance options (persistent mode, map size) | ||
/// - Instrumentation features (deferred mode, ASan support) | ||
/// | ||
/// Use methods like `program()`, `arg()`, and `timeout()` to configure | ||
/// the executor before calling `build()`. | ||
#[derive(Debug)] | ||
#[expect(clippy::struct_excessive_bools)] | ||
pub struct ForkserverExecutorBuilder<'a, TC, SP> { | ||
|
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
... if they use the [whatever its name was] feature
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
wdym exactly? :)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In general it doesn't support libafl_cc instrumented binaries, only with this random forkserver feature