Forkserver example with forkserver.c (#726) (#973)

* forkserver: Add an API to setup the shared memory region for edge coverage

This is inspired from and meant to be similar to afl-cc's instrumentation.
Remove ! return type from __afl_start_forkserver as it returns in several cases.

* Add example fuzzer using LibAFL's forkserver

The fuzzer is instrumented with libafl_cc as well.

Co-authored-by: ergrelet <ergrelet@users.noreply.github.com>
This commit is contained in:
Erwan Grelet 2022-12-28 22:16:27 +01:00 committed by GitHub
parent 676a149497
commit 3e38862837
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 574 additions and 83 deletions

View File

@ -0,0 +1,32 @@
[package]
name = "forkserver_libafl_cc"
version = "0.8.2"
authors = ["ergrelet <ergrelet@users.noreply.github.com>"]
edition = "2021"
[features]
default = ["std"]
std = []
# Forces a crash
crash = []
[profile.release]
lto = true
codegen-units = 1
opt-level = 3
debug = true
[build-dependencies]
cc = { version = "1.0", features = ["parallel"] }
which = { version = "4.0.2" }
[dependencies]
libafl = { path = "../../libafl/", features = ["default"] }
clap = { version = "4.0", features = ["derive"] }
nix = "0.25"
libafl_targets = { path = "../../libafl_targets/" }
libafl_cc = { path = "../../libafl_cc/" }
[lib]
name = "libforkserver_libafl_cc"
crate-type = ["staticlib"]

View File

@ -0,0 +1,115 @@
# Variables
[env]
FUZZER_NAME='fuzzer_libafl_cc'
CARGO_TARGET_DIR = { value = "${PROJECT_DIR}/target", condition = { env_not_set = ["CARGO_TARGET_DIR"] } }
LIBAFL_CC = '${CARGO_TARGET_DIR}/release/libafl_cc'
LIBAFL_CXX = '${CARGO_TARGET_DIR}/release/libafl_cxx'
FUZZER = '${CARGO_TARGET_DIR}/release/${FUZZER_NAME}'
PROJECT_DIR = { script = ["pwd"] }
[tasks.unsupported]
script_runner="@shell"
script='''
echo "Cargo-make not integrated yet on this"
'''
# Compilers
[tasks.cxx]
linux_alias = "cxx_unix"
mac_alias = "cxx_unix"
windows_alias = "unsupported"
[tasks.cxx_unix]
command = "cargo"
args = ["build" , "--release"]
[tasks.cc]
linux_alias = "cc_unix"
mac_alias = "cc_unix"
windows_alias = "unsupported"
[tasks.cc_unix]
command = "cargo"
args = ["build" , "--release"]
[tasks.crash_cxx]
linux_alias = "crash_cxx_unix"
mac_alias = "crash_cxx_unix"
windows_alias = "unsupported"
[tasks.crash_cxx_unix]
command = "cargo"
args = ["build" , "--release", "--features=crash"]
[tasks.crash_cc]
linux_alias = "crash_cc_unix"
mac_alias = "crash_cc_unix"
windows_alias = "unsupported"
[tasks.crash_cc_unix]
command = "cargo"
args = ["build" , "--release", "--features=crash"]
# Harness
[tasks.fuzzer]
linux_alias = "fuzzer_unix"
mac_alias = "fuzzer_unix"
windows_alias = "unsupported"
[tasks.fuzzer_unix]
command = "${CARGO_TARGET_DIR}/release/libafl_cc"
args = ["${PROJECT_DIR}/src/program.c", "-o", "${FUZZER_NAME}", "-lm"]
dependencies = [ "cxx", "cc" ]
# Crashing Harness
[tasks.fuzzer_crash]
linux_alias = "fuzzer_crash_unix"
mac_alias = "fuzzer_crash_unix"
windows_alias = "unsupported"
[tasks.fuzzer_crash_unix]
command = "${CARGO_TARGET_DIR}/release/libafl_cc"
args = ["${PROJECT_DIR}/src/program.c", "-o", "${FUZZER_NAME}_crash", "-lm"]
dependencies = [ "crash_cxx", "crash_cc" ]
# Run the fuzzer
[tasks.run]
linux_alias = "run_unix"
mac_alias = "run_unix"
windows_alias = "unsupported"
[tasks.run_unix]
script_runner = "@shell"
script='''
taskset -c 1 ${CARGO_TARGET_DIR}/release/${CARGO_MAKE_PROJECT_NAME} ./${FUZZER_NAME} ./corpus/ -t 1000
'''
dependencies = [ "fuzzer" ]
# Run the fuzzer with a crash
[tasks.crash]
linux_alias = "crash_unix"
mac_alias = "crash_unix"
windows_alias = "unsupported"
[tasks.crash_unix]
script_runner = "@shell"
script='''
taskset -c 1 ${CARGO_TARGET_DIR}/release/${CARGO_MAKE_PROJECT_NAME} ./${FUZZER_NAME}_crash ./corpus/ -t 1000
'''
dependencies = [ "fuzzer_crash" ]
# Clean up
[tasks.clean]
linux_alias = "clean_unix"
mac_alias = "clean_unix"
windows_alias = "unsupported"
[tasks.clean_unix]
# Disable default `clean` definition
clear = true
script_runner="@shell"
script='''
rm -f ./${FUZZER_NAME}
cargo clean
'''

View File

@ -0,0 +1,13 @@
# Simple Forkserver Fuzzer
This is a simple example fuzzer to fuzz an executable instrumented by libafl_cc.
## Usage
You can build this example by running `cargo make fuzzer`.
This compiles, libafl_cc, the fuzzer and the example harness program in
`src/program.c` with libafl_cc.
## Run
You can run this example by running `cargo make run`.

View File

@ -0,0 +1 @@
aaa

View File

@ -0,0 +1,45 @@
use std::env;
use libafl_cc::{ClangWrapper, CompilerWrapper, LLVMPasses};
pub fn main() {
let args: Vec<String> = env::args().collect();
if args.len() > 1 {
let mut dir = env::current_exe().unwrap();
let wrapper_name = dir.file_name().unwrap().to_str().unwrap();
let is_cpp = match wrapper_name[wrapper_name.len()-2..].to_lowercase().as_str() {
"cc" => false,
"++" | "pp" | "xx" => true,
_ => panic!("Could not figure out if c or c++ wrapper was called. Expected {dir:?} to end with c or cxx"),
};
dir.pop();
let mut cc = ClangWrapper::new();
if let Some(code) = cc
.cpp(is_cpp)
// silence the compiler wrapper output, needed for some configure scripts.
.silence(true)
.parse_args(&args)
.expect("Failed to parse the command line")
// Enable libafl's coverage instrumentation
.add_pass(LLVMPasses::AFLCoverage)
.add_arg("-mllvm")
.add_arg("-ctx") // Context sensitive coverage
// Imitate afl-cc's compile definitions
.add_arg("-D__AFL_FUZZ_INIT()=int __afl_sharedmem_fuzzing = 1;extern unsigned int *__afl_fuzz_len;extern unsigned char *__afl_fuzz_ptr;unsigned char __afl_fuzz_alt[1048576];unsigned char *__afl_fuzz_alt_ptr = __afl_fuzz_alt;void libafl_start_forkserver(void)")
.add_arg("-D__AFL_FUZZ_TESTCASE_BUF=(__afl_fuzz_ptr ? __afl_fuzz_ptr : __afl_fuzz_alt_ptr)")
.add_arg("-D__AFL_FUZZ_TESTCASE_LEN=(__afl_fuzz_ptr ? *__afl_fuzz_len : (*__afl_fuzz_len = read(0, __afl_fuzz_alt_ptr, 1048576)) == 0xffffffff ? 0 : *__afl_fuzz_len)")
.add_arg("-D__AFL_INIT()=libafl_start_forkserver()")
// Link with libafl's forkserver implementation
.link_staticlib(&dir, "libforkserver_libafl_cc")
.run()
.expect("Failed to run the wrapped compiler")
{
std::process::exit(code);
}
} else {
panic!("LibAFL CC: No Arguments given");
}
}

View File

@ -0,0 +1,5 @@
pub mod libafl_cc;
fn main() {
libafl_cc::main();
}

View File

@ -0,0 +1,9 @@
use libafl_targets::{map_shared_memory, start_forkserver};
#[no_mangle]
pub fn libafl_start_forkserver() {
// Map shared memory region for the edge coverage map
map_shared_memory();
// Start the forkserver
start_forkserver();
}

View File

@ -0,0 +1,215 @@
use core::time::Duration;
use std::path::PathBuf;
use clap::{self, Parser};
use libafl::{
bolts::{
current_nanos,
rands::StdRand,
shmem::{ShMem, ShMemProvider, UnixShMemProvider},
tuples::{tuple_list, MatchName, Merge},
AsMutSlice,
},
corpus::{Corpus, InMemoryCorpus, OnDiskCorpus},
events::SimpleEventManager,
executors::{
forkserver::{ForkserverExecutor, TimeoutForkserverExecutor},
HasObservers,
},
feedback_and_fast, feedback_or,
feedbacks::{CrashFeedback, MaxMapFeedback, TimeFeedback},
fuzzer::{Fuzzer, StdFuzzer},
inputs::BytesInput,
monitors::SimpleMonitor,
mutators::{scheduled::havoc_mutations, tokens_mutations, StdScheduledMutator, Tokens},
observers::{HitcountsMapObserver, MapObserver, StdMapObserver, TimeObserver},
schedulers::{IndexesLenTimeMinimizerScheduler, QueueScheduler},
stages::mutational::StdMutationalStage,
state::{HasCorpus, HasMetadata, StdState},
};
use nix::sys::signal::Signal;
/// The commandline args this fuzzer accepts
#[derive(Debug, Parser)]
#[command(
name = "forkserver_libafl_cc",
about = "This is a simple example fuzzer to fuzz a executable instrumented by libafl_cc.",
author = "ergrelet <ergrelet@users.noreply.github.com>"
)]
struct Opt {
#[arg(
help = "The instrumented binary we want to fuzz",
name = "EXEC",
required = true
)]
executable: String,
#[arg(
help = "The directory to read initial inputs from ('seeds')",
name = "INPUT_DIR",
required = true
)]
in_dir: PathBuf,
#[arg(
help = "Timeout for each individual execution, in milliseconds",
short = 't',
long = "timeout",
default_value = "1200"
)]
timeout: u64,
#[arg(
help = "If not set, the child's stdout and stderror will be redirected to /dev/null",
short = 'd',
long = "debug-child",
default_value = "false"
)]
debug_child: bool,
#[arg(
help = "Arguments passed to the target",
name = "arguments",
num_args(1..),
allow_hyphen_values = true,
)]
arguments: Vec<String>,
#[arg(
help = "Signal used to stop child",
short = 's',
long = "signal",
value_parser = str::parse::<Signal>,
default_value = "SIGKILL"
)]
signal: Signal,
}
#[allow(clippy::similar_names)]
pub fn main() {
const MAP_SIZE: usize = 65536;
let opt = Opt::parse();
let corpus_dirs: Vec<PathBuf> = [opt.in_dir].to_vec();
// The unix shmem provider supported by LibAFL for shared memory
let mut shmem_provider = UnixShMemProvider::new().unwrap();
// The coverage map shared between observer and executor
let mut shmem = shmem_provider.new_shmem(MAP_SIZE).unwrap();
// let the forkserver know the shmid
shmem.write_to_env("__AFL_SHM_ID").unwrap();
let shmem_buf = shmem.as_mut_slice();
// Create an observation channel using the signals map
let edges_observer =
unsafe { HitcountsMapObserver::new(StdMapObserver::new("shared_mem", shmem_buf)) };
// Create an observation channel to keep track of the execution time
let time_observer = TimeObserver::new("time");
// Feedback to rate the interestingness of an input
// This one is composed by two Feedbacks in OR
let mut feedback = feedback_or!(
// New maximization map feedback linked to the edges observer and the feedback state
MaxMapFeedback::new_tracking(&edges_observer, true, false),
// Time feedback, this one does not need a feedback state
TimeFeedback::new_with_observer(&time_observer)
);
// A feedback to choose if an input is a solution or not
// We want to do the same crash deduplication that AFL does
let mut objective = feedback_and_fast!(
// Must be a crash
CrashFeedback::new(),
// Take it onlt if trigger new coverage over crashes
MaxMapFeedback::new(&edges_observer)
);
// create a State from scratch
let mut state = StdState::new(
// RNG
StdRand::with_seed(current_nanos()),
// Corpus that will be evolved, we keep it in memory for performance
InMemoryCorpus::<BytesInput>::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(PathBuf::from("./crashes")).unwrap(),
// States of the feedbacks.
// The feedbacks can report the data that should persist in the State.
&mut feedback,
// Same for objective feedbacks
&mut objective,
)
.unwrap();
// The Monitor trait define how the fuzzer stats are reported to the user
let monitor = SimpleMonitor::new(|s| println!("{s}"));
// The event manager handle the various events generated during the fuzzing loop
// such as the notification of the addition of a new item to the corpus
let mut mgr = SimpleEventManager::new(monitor);
// A minimization+queue policy to get testcasess from the corpus
let scheduler = IndexesLenTimeMinimizerScheduler::new(QueueScheduler::new());
// A fuzzer with feedbacks and a corpus scheduler
let mut fuzzer = StdFuzzer::new(scheduler, feedback, objective);
// If we should debug the child
let debug_child = opt.debug_child;
// Create the executor for the forkserver
let args = opt.arguments;
let mut tokens = Tokens::new();
let mut forkserver = ForkserverExecutor::builder()
.program(opt.executable)
.debug_child(debug_child)
.shmem_provider(&mut shmem_provider)
.autotokens(&mut tokens)
.parse_afl_cmdline(args)
.coverage_map_size(MAP_SIZE)
.build(tuple_list!(time_observer, edges_observer))
.unwrap();
if let Some(dynamic_map_size) = forkserver.coverage_map_size() {
forkserver
.observers_mut()
.match_name_mut::<HitcountsMapObserver<StdMapObserver<'_, u8, false>>>("shared_mem")
.unwrap()
.downsize_map(dynamic_map_size);
}
let mut executor = TimeoutForkserverExecutor::with_signal(
forkserver,
Duration::from_millis(opt.timeout),
opt.signal,
)
.expect("Failed to create the executor.");
// In case the corpus is empty (on first run), reset
if state.corpus().count() < 1 {
state
.load_initial_inputs(&mut fuzzer, &mut executor, &mut mgr, &corpus_dirs)
.unwrap_or_else(|err| {
panic!(
"Failed to load initial corpus at {:?}: {:?}",
&corpus_dirs, err
)
});
println!("We imported {} inputs from disk.", state.corpus().count());
}
state.add_metadata(tokens);
// Setup a mutational stage with a basic bytes mutator
let mutator =
StdScheduledMutator::with_max_stack_pow(havoc_mutations().merge(tokens_mutations()), 6);
let mut stages = tuple_list!(StdMutationalStage::new(mutator));
fuzzer
.fuzz_loop(&mut stages, &mut executor, &mut state, &mut mgr)
.expect("Error in the fuzzing loop");
}

View File

@ -0,0 +1,37 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
// The following line is needed for shared memory testcase fuzzing
__AFL_FUZZ_INIT();
void vuln(char *buf) {
if (strcmp(buf, "vuln") == 0) { abort(); }
}
int main(int argc, char **argv) {
// Start the forkserver at this point (i.e., forks will happen here)
__AFL_INIT();
// The following five lines are for normal fuzzing.
/*
FILE *file = stdin;
if (argc > 1) { file = fopen(argv[1], "rb"); }
char buf[16];
char *p = fgets(buf, 16, file);
buf[15] = 0;
*/
// The following line is also needed for shared memory testcase fuzzing
unsigned char *buf = __AFL_FUZZ_TESTCASE_BUF; // must be after __AFL_INIT
// printf("input: %s\n", buf);
if (buf[0] == 'b') {
if (buf[1] == 'a') {
if (buf[2] == 'd') { abort(); }
}
}
vuln((char *)buf);
return 0;
}

View File

@ -7,17 +7,24 @@
#include <unistd.h>
#ifndef USEMMAP
#include <sys/shm.h>
#else
#include <sys/mman.h>
#include <sys/stat.h>
#include <fcntl.h>
#endif
#include <sys/wait.h>
#include <sys/types.h>
#define write_error(s) fprintf(stderr, "Error at %s:%d: %s\n", __FILE__, __LINE__, s)
#define write_error(s) \
fprintf(stderr, "Error at %s:%d: %s\n", __FILE__, __LINE__, s)
// AFL++ constants
#define FORKSRV_FD 198
#define MAX_FILE (1024 * 1024)
#define SHMEM_FUZZ_HDR_SIZE 4
#define SHM_ENV_VAR "__AFL_SHM_ID"
#define SHM_FUZZ_ENV_VAR "__AFL_SHM_FUZZ_ID"
#define DEFAULT_PERMISSION 0600
/* Reporting errors */
#define FS_OPT_ERROR 0xf800008f
@ -45,10 +52,12 @@
#define FS_OPT_SET_MAPSIZE(x) \
(x <= 1 || x > FS_OPT_MAX_MAPSIZE ? 0 : ((x - 1) << 1))
// Set by this macro https://github.com/AFLplusplus/AFLplusplus/blob/stable/src/afl-cc.c#L993
// Set by this macro
// https://github.com/AFLplusplus/AFLplusplus/blob/stable/src/afl-cc.c#L993
int __afl_sharedmem_fuzzing __attribute__((weak));
extern uint8_t *__afl_area_ptr;
extern size_t __afl_map_size;
extern uint8_t *__token_start;
extern uint8_t *__token_stop;
@ -57,6 +66,7 @@ uint8_t* __afl_fuzz_ptr;
static uint32_t __afl_fuzz_len_local;
uint32_t *__afl_fuzz_len = &__afl_fuzz_len_local;
int already_initialized_shm;
int already_initialized_forkserver;
static int child_pid;
@ -65,47 +75,97 @@ static void (*old_sigterm_handler)(int) = 0;
static uint8_t is_persistent;
void __afl_set_persistent_mode(uint8_t mode) {
is_persistent = mode;
}
/* Error reporting to forkserver controller */
static void send_forkserver_error(int error) {
uint32_t status;
if (!error || error > 0xffff) return;
status = (FS_OPT_ERROR | FS_OPT_SET_ERROR(error));
if (write(FORKSRV_FD + 1, (char *)&status, 4) != 4) { return; }
}
/* Ensure we kill the child on termination */
static void at_exit(int signal) {
(void)signal;
if (child_pid > 0) {
kill(child_pid, SIGKILL);
child_pid = -1;
}
_exit(0);
}
/* SHM fuzzing setup. */
static void map_input_shared_memory() {
void __afl_map_shm(void) {
if (already_initialized_shm) return;
already_initialized_shm = 1;
char *id_str = getenv(SHM_ENV_VAR);
if (id_str) {
#ifdef USEMMAP
const char *shm_file_path = id_str;
int shm_fd = -1;
unsigned char *shm_base = NULL;
/* create the shared memory segment as if it was a file */
shm_fd = shm_open(shm_file_path, O_RDWR, DEFAULT_PERMISSION);
if (shm_fd == -1) {
fprintf(stderr, "shm_open() failed\n");
send_forkserver_error(FS_ERROR_SHM_OPEN);
exit(1);
}
shm_base =
mmap(0, __afl_map_size, PROT_READ | PROT_WRITE, MAP_SHARED, shm_fd, 0);
close(shm_fd);
shm_fd = -1;
if (shm_base == MAP_FAILED) {
fprintf(stderr, "mmap() failed\n");
perror("mmap for map");
send_forkserver_error(FS_ERROR_MMAP);
exit(2);
}
__afl_area_ptr = shm_base;
#else
uint32_t shm_id = atoi(id_str);
__afl_area_ptr = (uint8_t *)shmat(shm_id, NULL, 0);
/* Whooooops. */
if (!__afl_area_ptr || __afl_area_ptr == (void *)-1) {
send_forkserver_error(FS_ERROR_SHMAT);
perror("shmat for map");
exit(1);
}
#endif
/* Write something into the bitmap so that even with low AFL_INST_RATIO,
our parent doesn't give up on us. */
__afl_area_ptr[0] = 1;
} else {
fprintf(stderr,
"Error: variable for edge coverage shared memory is not set\n");
send_forkserver_error(FS_ERROR_SHM_OPEN);
exit(1);
}
}
static void map_input_shared_memory() {
char *id_str = getenv(SHM_FUZZ_ENV_VAR);
if (id_str) {
uint8_t *map = NULL;
#ifdef USEMMAP
@ -115,14 +175,13 @@ static void map_input_shared_memory() {
/* create the shared memory segment as if it was a file */
shm_fd = shm_open(shm_file_path, O_RDWR, DEFAULT_PERMISSION);
if (shm_fd == -1) {
fprintf(stderr, "shm_open() failed for fuzz\n");
send_forkserver_error(FS_ERROR_SHM_OPEN);
exit(1);
}
map = (uint8_t* )mmap(0, MAX_FILE + sizeof(uint32_t), PROT_READ, MAP_SHARED, shm_fd, 0);
map = (uint8_t *)mmap(0, MAX_FILE + sizeof(uint32_t), PROT_READ, MAP_SHARED,
shm_fd, 0);
#else
uint32_t shm_id = atoi(id_str);
@ -133,30 +192,24 @@ static void map_input_shared_memory() {
/* Whooooops. */
if (!map || map == (void *)-1) {
perror("Could not access fuzzing shared memory");
send_forkserver_error(FS_ERROR_SHM_OPEN);
exit(1);
}
__afl_fuzz_len = (uint32_t *)map;
__afl_fuzz_ptr = map + sizeof(uint32_t);
} else {
fprintf(stderr, "Error: variable for fuzzing shared memory is not set\n");
send_forkserver_error(FS_ERROR_SHM_OPEN);
exit(1);
}
}
/* Fork server logic. */
void __afl_start_forkserver(void) {
if (already_initialized_forkserver) return;
already_initialized_forkserver = 1;
@ -175,24 +228,14 @@ void __afl_start_forkserver(void) {
void (*old_sigchld_handler)(int) = signal(SIGCHLD, SIG_DFL);
if (__afl_map_size <= FS_OPT_MAX_MAPSIZE) {
status_for_fsrv |= (FS_OPT_SET_MAPSIZE(__afl_map_size) | FS_OPT_MAPSIZE);
}
int autodict_on = __token_start != NULL && __token_stop != NULL;
if (autodict_on) {
status_for_fsrv |= FS_OPT_AUTODICT;
}
if (autodict_on) { status_for_fsrv |= FS_OPT_AUTODICT; }
if (__afl_sharedmem_fuzzing != 0) { status_for_fsrv |= FS_OPT_SHDMEM_FUZZ; }
if (status_for_fsrv) {
status_for_fsrv |= FS_OPT_ENABLED;
}
if (status_for_fsrv) { status_for_fsrv |= FS_OPT_ENABLED; }
memcpy(tmp, &status_for_fsrv, 4);
@ -202,73 +245,57 @@ void __afl_start_forkserver(void) {
if (write(FORKSRV_FD + 1, tmp, 4) != 4) { return; }
if (__afl_sharedmem_fuzzing || autodict_on) {
if (read(FORKSRV_FD, &was_killed, 4) != 4) _exit(1);
if ((was_killed & (FS_OPT_ENABLED | FS_OPT_SHDMEM_FUZZ)) == (FS_OPT_ENABLED | FS_OPT_SHDMEM_FUZZ)) {
if ((was_killed & (FS_OPT_ENABLED | FS_OPT_SHDMEM_FUZZ)) ==
(FS_OPT_ENABLED | FS_OPT_SHDMEM_FUZZ)) {
map_input_shared_memory();
}
if ((was_killed & (FS_OPT_ENABLED | FS_OPT_AUTODICT)) == (FS_OPT_ENABLED | FS_OPT_AUTODICT) && autodict_on) {
if ((was_killed & (FS_OPT_ENABLED | FS_OPT_AUTODICT)) ==
(FS_OPT_ENABLED | FS_OPT_AUTODICT) &&
autodict_on) {
// great lets pass the dictionary through the forkserver FD
uint32_t len = (__token_stop - __token_start), offset = 0;
if (write(FORKSRV_FD + 1, &len, 4) != 4) {
write_error("could not send dictionary len");
_exit(1);
}
while (len != 0) {
int32_t ret;
ret = write(FORKSRV_FD + 1, __token_start + offset, len);
if (ret < 1) {
write_error("could not send dictionary");
_exit(1);
}
len -= ret;
offset += ret;
}
} else {
// uh this forkserver does not understand extended option passing
// or does not want the dictionary
if (!__afl_fuzz_ptr) already_read_first = 1;
}
}
while (1) {
int status;
/* Wait for parent by reading from the pipe. Abort if read fails. */
if (already_read_first) {
already_read_first = 0;
} else {
if (read(FORKSRV_FD, &was_killed, 4) != 4) {
// write_error("read from afl-fuzz");
_exit(1);
}
}
/* If we stopped the child in persistent mode, but there was a race
@ -276,33 +303,25 @@ void __afl_start_forkserver(void) {
process. */
if (child_stopped && was_killed) {
child_stopped = 0;
if (waitpid(child_pid, &status, 0) < 0) {
write_error("child_stopped && was_killed");
_exit(1);
}
}
if (!child_stopped) {
/* Once woken up, create a clone of our process. */
child_pid = fork();
if (child_pid < 0) {
write_error("fork");
_exit(1);
}
/* In child process: close fds, resume execution. */
if (!child_pid) {
//(void)nice(-20);
signal(SIGCHLD, old_sigchld_handler);
@ -311,33 +330,26 @@ void __afl_start_forkserver(void) {
close(FORKSRV_FD);
close(FORKSRV_FD + 1);
return;
}
} else {
/* Special handling for persistent mode: if the child is alive but
currently stopped, simply restart it with SIGCONT. */
kill(child_pid, SIGCONT);
child_stopped = 0;
}
/* In parent process: write PID to pipe, then wait for child. */
if (write(FORKSRV_FD + 1, &child_pid, 4) != 4) {
write_error("write to afl-fuzz");
_exit(1);
}
if (waitpid(child_pid, &status, is_persistent ? WUNTRACED : 0) < 0) {
write_error("waitpid");
_exit(1);
}
/* In persistent mode, the child stops itself with SIGSTOP to indicate
@ -349,12 +361,8 @@ void __afl_start_forkserver(void) {
/* Relay wait status to pipe, then loop back. */
if (write(FORKSRV_FD + 1, &status, 4) != 4) {
write_error("writing to afl-fuzz");
_exit(1);
}
}
}

View File

@ -1,8 +1,19 @@
//! Forkserver logic into targets
extern "C" {
/// Map a shared memory region for the edge coverage map.
fn __afl_map_shm();
/// Start the forkserver.
fn __afl_start_forkserver() -> !;
fn __afl_start_forkserver();
}
/// Map a shared memory region for the edge coverage map.
///
/// # Note
///
/// The function's logic is written in C and this code is a wrapper.
pub fn map_shared_memory() {
unsafe { __afl_map_shm() }
}
/// Start the forkserver from this point. Any shared memory must be created before.
@ -10,6 +21,6 @@ extern "C" {
/// # Note
///
/// The forkserver logic is written in C and this code is a wrapper.
pub fn start_forkserver() -> ! {
pub fn start_forkserver() {
unsafe { __afl_start_forkserver() }
}