Add AFL_FORK_SERVER_KILL_SIGNAL environment variable.

The AFL_FORK_SERVER_KILL_SIGNAL variable allows to configure the signal
used to kill the fork server on termination.
This commit is contained in:
Nils Bars
2022-10-21 12:13:43 +02:00
parent f84ea69660
commit 7512316b46
13 changed files with 58 additions and 45 deletions

View File

@ -123,6 +123,8 @@ function usage() {
"AFL_FORKSRV_INIT_TMOUT: time the fuzzer waits for the forkserver to come up\n" \
"AFL_KEEP_TRACES: leave the temporary <out_dir>/.traces directory\n" \
"AFL_KILL_SIGNAL: Signal delivered to child processes on timeout (default: SIGKILL)\n" \
"AFL_FORK_SERVER_KILL_SIGNAL: Signal delivered to fork server processes on termination\n"
" (default: SIGTERM)\n"
"AFL_NO_FORKSRV: run target via execve instead of using the forkserver\n" \
"AFL_CMIN_ALLOW_ANY: write tuples for crashing inputs also\n" \
"AFL_PATH: path for the afl-showmap binary if not found anywhere in PATH\n" \

View File

@ -409,11 +409,18 @@ checks or alter some of the more exotic semantics of the tool:
the afl-fuzz -g/-G command line option to control the minimum/maximum
of fuzzing input generated.
- `AFL_KILL_SIGNAL`: Set the signal ID to be delivered to child processes on
timeout. Unless you implement your own targets or instrumentation, you
- `AFL_KILL_SIGNAL`: Set the signal ID to be delivered to child processes
on timeout. Unless you implement your own targets or instrumentation, you
likely don't have to set it. By default, on timeout and on exit, `SIGKILL`
(`AFL_KILL_SIGNAL=9`) will be delivered to the child.
- `AFL_FORK_SERVER_KILL_SIGNAL`: Set the signal ID to be delivered to the
fork server when AFL++ is terminated. Unless you implement your
fork server, you likely do not have to set it. By default, `SIGTERM`
(`AFL_FORK_SERVER_KILL_SIGNAL=15`) will be delivered to the fork server.
NOTE: Uncatchable signals, such as `SIGKILL`, cause child processes of
the fork server to be orphaned and leaves them in a zombie state.
- `AFL_MAP_SIZE` sets the size of the shared map that afl-analyze, afl-fuzz,
afl-showmap, and afl-tmin create to gather instrumentation data from the
target. This must be equal or larger than the size the target was compiled

View File

@ -393,8 +393,8 @@ typedef struct afl_env_vars {
*afl_hang_tmout, *afl_forksrv_init_tmout, *afl_preload,
*afl_max_det_extras, *afl_statsd_host, *afl_statsd_port,
*afl_crash_exitcode, *afl_statsd_tags_flavor, *afl_testcache_size,
*afl_testcache_entries, *afl_kill_signal, *afl_target_env,
*afl_persistent_record, *afl_exit_on_time;
*afl_testcache_entries, *afl_child_kill_signal, *afl_fsrv_kill_signal,
*afl_target_env, *afl_persistent_record, *afl_exit_on_time;
} afl_env_vars_t;
@ -1268,4 +1268,3 @@ void queue_testcase_store_mem(afl_state_t *afl, struct queue_entry *q, u8 *mem);
#endif
#endif

View File

@ -67,10 +67,11 @@ u8 *find_binary(u8 *fname);
u8 *find_afl_binary(u8 *own_loc, u8 *fname);
/* Parses the kill signal environment variable, FATALs on error.
If the env is not set, sets the env to default_signal for the signal handlers
and returns the default_signal. */
int parse_afl_kill_signal_env(u8 *afl_kill_signal_env, int default_signal);
/* Parses the (numeric) kill signal environment variable passed
via `numeric_signal_as_str`.
If NULL is passed, the `default_signal` value is returned.
FATALs if `numeric_signal_as_str` is not a valid integer .*/
int parse_afl_kill_signal(u8 *numeric_signal_as_str, int default_signal);
/* Read a bitmap from file fname to memory
This is for the -B option again. */
@ -133,4 +134,3 @@ FILE *create_ffile(u8 *fn);
s32 create_file(u8 *fn);
#endif

View File

@ -110,6 +110,7 @@ static char *afl_environment_variables[] = {
"AFL_INST_RATIO",
"AFL_KEEP_TIMEOUTS",
"AFL_KILL_SIGNAL",
"AFL_FORK_SERVER_KILL_SIGNAL",
"AFL_KEEP_TRACES",
"AFL_KEEP_ASSEMBLY",
"AFL_LD_HARD_FAIL",
@ -239,4 +240,3 @@ static char *afl_environment_variables[] = {
extern char *afl_environment_variables[];
#endif

View File

@ -164,6 +164,8 @@ typedef struct afl_forkserver {
void (*add_extra_func)(void *afl_ptr, u8 *mem, u32 len);
u8 child_kill_signal;
u8 fsrv_kill_signal;
u8 persistent_mode;
#ifdef __linux__

View File

@ -1116,7 +1116,10 @@ int main(int argc, char **argv_orig, char **envp) {
}
fsrv.child_kill_signal =
parse_afl_kill_signal_env(getenv("AFL_KILL_SIGNAL"), SIGKILL);
parse_afl_kill_signal(getenv("AFL_KILL_SIGNAL"), SIGKILL);
fsrv.fsrv_kill_signal =
parse_afl_kill_signal(getenv("AFL_FORK_SERVER_KILL_SIGNAL"), SIGTERM);
read_initial_file();
(void)check_binary_signatures(fsrv.target_path);

View File

@ -456,38 +456,24 @@ u8 *find_afl_binary(u8 *own_loc, u8 *fname) {
}
/* Parses the kill signal environment variable, FATALs on error.
If the env is not set, sets the env to default_signal for the signal handlers
and returns the default_signal. */
int parse_afl_kill_signal_env(u8 *afl_kill_signal_env, int default_signal) {
if (afl_kill_signal_env && afl_kill_signal_env[0]) {
int parse_afl_kill_signal(u8 *numeric_signal_as_str, int default_signal) {
if (numeric_signal_as_str && numeric_signal_as_str[0]) {
char *endptr;
u8 signal_code;
signal_code = (u8)strtoul(afl_kill_signal_env, &endptr, 10);
signal_code = (u8)strtoul(numeric_signal_as_str, &endptr, 10);
/* Did we manage to parse the full string? */
if (*endptr != '\0' || endptr == (char *)afl_kill_signal_env) {
FATAL("Invalid AFL_KILL_SIGNAL: %s (expected unsigned int)",
afl_kill_signal_env);
if (*endptr != '\0' || endptr == (char *)numeric_signal_as_str) {
FATAL("Invalid signal name: %s", numeric_signal_as_str);
} else {
return signal_code;
}
return signal_code;
} else {
char *sigstr = alloc_printf("%d", default_signal);
if (!sigstr) { FATAL("Failed to alloc mem for signal buf"); }
/* Set the env for signal handler */
setenv("AFL_KILL_SIGNAL", sigstr, 1);
free(sigstr);
return default_signal;
}
return default_signal;
}
static inline unsigned int helper_min3(unsigned int a, unsigned int b,
@ -1253,4 +1239,3 @@ s32 create_file(u8 *fn) {
return fd;
}

View File

@ -1245,8 +1245,8 @@ void afl_fsrv_kill(afl_forkserver_t *fsrv) {
if (fsrv->child_pid > 0) { kill(fsrv->child_pid, fsrv->child_kill_signal); }
if (fsrv->fsrv_pid > 0) {
kill(fsrv->fsrv_pid, SIGTERM);
if (waitpid(fsrv->fsrv_pid, NULL, 0) <= 0) { WARNF("error waitpid\n"); }
kill(fsrv->fsrv_pid, fsrv->fsrv_kill_signal);
waitpid(fsrv->fsrv_pid, NULL, 0);
}

View File

@ -485,10 +485,15 @@ void read_afl_environment(afl_state_t *afl, char **envp) {
#endif
} else if (!strncmp(env, "AFL_KILL_SIGNAL",
afl_environment_variable_len)) {
afl->afl_env.afl_kill_signal =
afl->afl_env.afl_child_kill_signal =
(u8 *)get_afl_env(afl_environment_variables[i]);
} else if (!strncmp(env, "AFL_FORK_SERVER_KILL_SIGNAL",
afl_environment_variable_len)) {
afl->afl_env.afl_fsrv_kill_signal =
(u8 *)get_afl_env(afl_environment_variables[i]);
} else if (!strncmp(env, "AFL_TARGET_ENV",
@ -657,8 +662,7 @@ void afl_states_stop(void) {
/* NOTE: We need to make sure that the parent (the forkserver) reap the child (see below). */
if (el->fsrv.child_pid > 0) kill(el->fsrv.child_pid, el->fsrv.child_kill_signal);
if (el->fsrv.fsrv_pid > 0) {
/* This must be SIGTERM, to allow the forkserver to reap the child before exiting. */
kill(el->fsrv.fsrv_pid, SIGTERM);
kill(el->fsrv.fsrv_pid, el->fsrv.fsrv_kill_signal);
/* Make sure the forkserver does not end up as zombie. */
waitpid(el->fsrv.fsrv_pid, NULL, 0);
}

View File

@ -1359,7 +1359,9 @@ int main(int argc, char **argv_orig, char **envp) {
#endif
afl->fsrv.child_kill_signal =
parse_afl_kill_signal_env(afl->afl_env.afl_kill_signal, SIGKILL);
parse_afl_kill_signal(afl->afl_env.afl_child_kill_signal, SIGKILL);
afl->fsrv.fsrv_kill_signal =
parse_afl_kill_signal(afl->afl_env.afl_fsrv_kill_signal, SIGTERM);
setup_signal_handlers();
check_asan_opts(afl);

View File

@ -866,6 +866,8 @@ static void usage(u8 *argv0) {
"startup (in milliseconds)\n"
"AFL_KILL_SIGNAL: Signal ID delivered to child processes on timeout, "
"etc. (default: SIGKILL)\n"
"AFL_FORK_SERVER_KILL_SIGNAL: Signal delivered to fork server processes on termination"
" (default: SIGTERM)\n"
"AFL_MAP_SIZE: the shared memory size for that target. must be >= the "
"size the target was compiled for\n"
"AFL_PRELOAD: LD_PRELOAD / DYLD_INSERT_LIBRARIES settings for target\n"
@ -1259,7 +1261,9 @@ int main(int argc, char **argv_orig, char **envp) {
be_quiet = save_be_quiet;
fsrv->child_kill_signal =
parse_afl_kill_signal_env(getenv("AFL_KILL_SIGNAL"), SIGKILL);
parse_afl_kill_signal(getenv("AFL_KILL_SIGNAL"), SIGKILL);
fsrv->fsrv_kill_signal =
parse_afl_kill_signal(getenv("AFL_FORK_SERVER_KILL_SIGNAL"), SIGTERM);
if (new_map_size) {

View File

@ -881,6 +881,8 @@ static void usage(u8 *argv0) {
"AFL_CRASH_EXITCODE: optional child exit code to be interpreted as crash\n"
"AFL_FORKSRV_INIT_TMOUT: time spent waiting for forkserver during startup (in milliseconds)\n"
"AFL_KILL_SIGNAL: Signal ID delivered to child processes on timeout, etc. (default: SIGKILL)\n"
"AFL_FORK_SERVER_KILL_SIGNAL: Signal delivered to fork server processes on termination\n"
" (default: SIGTERM)\n"
"AFL_MAP_SIZE: the shared memory size for that target. must be >= the size\n"
" the target was compiled for\n"
"AFL_PRELOAD: LD_PRELOAD / DYLD_INSERT_LIBRARIES settings for target\n"
@ -1196,7 +1198,10 @@ int main(int argc, char **argv_orig, char **envp) {
}
fsrv->child_kill_signal =
parse_afl_kill_signal_env(getenv("AFL_KILL_SIGNAL"), SIGKILL);
parse_afl_kill_signal(getenv("AFL_KILL_SIGNAL"), SIGKILL);
fsrv->fsrv_kill_signal =
parse_afl_kill_signal(getenv("AFL_FORK_SERVER_KILL_SIGNAL"), SIGTERM);
if (getenv("AFL_CRASH_EXITCODE")) {