Better handling of exit codes used by sanitzers

This commit is contained in:
Andy Knowles
2025-03-24 16:30:05 +01:00
parent 42465480ef
commit 4cabb81996
4 changed files with 84 additions and 13 deletions

View File

@ -247,6 +247,12 @@ CFISAN. You might need to experiment which sanitizers you can combine in a
target (which means more instances can be run without a sanitized target, which target (which means more instances can be run without a sanitized target, which
is more effective). is more effective).
Note that some sanitizers (MSAN and LSAN) exit with a particular exit code
instead of aborting. afl-fuzz treats these exit codes as a crash when these
sanitizers are enabled. If the target uses these exit codes there could be false
positives among the saved crashes. LSAN uses exit code 23 and MSAN uses exit
code 86.
### d) Modifying the target ### d) Modifying the target
If the target has features that make fuzzing more difficult, e.g., checksums, If the target has features that make fuzzing more difficult, e.g., checksums,

View File

@ -137,6 +137,8 @@ typedef struct afl_forkserver {
u8 last_kill_signal; /* Signal that killed the child */ u8 last_kill_signal; /* Signal that killed the child */
u8 last_exit_code; /* Child exit code if counted as a crash */
bool use_shmem_fuzz; /* use shared mem for test cases */ bool use_shmem_fuzz; /* use shared mem for test cases */
bool support_shmem_fuzz; /* set by afl-fuzz */ bool support_shmem_fuzz; /* set by afl-fuzz */
@ -155,7 +157,7 @@ typedef struct afl_forkserver {
bool no_unlink; /* do not unlink cur_input */ bool no_unlink; /* do not unlink cur_input */
bool uses_asan; /* Target uses ASAN? */ u8 uses_asan; /* Target uses ASAN/LSAN/MSAN? (bit 0/1/2 respectively) */
bool debug; /* debug mode? */ bool debug; /* debug mode? */

View File

@ -258,7 +258,7 @@ void afl_fsrv_init(afl_forkserver_t *fsrv) {
fsrv->last_run_timed_out = false; fsrv->last_run_timed_out = false;
fsrv->debug = false; fsrv->debug = false;
fsrv->uses_crash_exitcode = false; fsrv->uses_crash_exitcode = false;
fsrv->uses_asan = false; fsrv->uses_asan = 0;
#ifdef __AFL_CODE_COVERAGE #ifdef __AFL_CODE_COVERAGE
fsrv->persistent_trace_bits = NULL; fsrv->persistent_trace_bits = NULL;
@ -2087,17 +2087,19 @@ fsrv_run_result_t __attribute__((hot)) afl_fsrv_run_target(
/* Did we crash? /* Did we crash?
In a normal case, (abort) WIFSIGNALED(child_status) will be set. In a normal case, (abort) WIFSIGNALED(child_status) will be set.
MSAN in uses_asan mode uses a special exit code as it doesn't support MSAN & LSAN in uses_asan mode use special exit codes as they doesn't support
abort_on_error. On top, a user may specify a custom AFL_CRASH_EXITCODE. abort_on_error. On top, a user may specify a custom AFL_CRASH_EXITCODE.
Handle all three cases here. */ Handle all four cases here. */
if (unlikely( if (unlikely(
/* A normal crash/abort */ /* A normal crash/abort */
(WIFSIGNALED(fsrv->child_status)) || (WIFSIGNALED(fsrv->child_status)) ||
/* special handling for msan and lsan */ /* special handling for msan */
(fsrv->uses_asan && ((fsrv->uses_asan & 4) &&
(WEXITSTATUS(fsrv->child_status) == MSAN_ERROR || WEXITSTATUS(fsrv->child_status) == MSAN_ERROR) ||
WEXITSTATUS(fsrv->child_status) == LSAN_ERROR)) || /* special handling for lsan */
((fsrv->uses_asan & 2) &&
WEXITSTATUS(fsrv->child_status) == LSAN_ERROR) ||
/* the custom crash_exitcode was returned by the target */ /* the custom crash_exitcode was returned by the target */
(fsrv->uses_crash_exitcode && (fsrv->uses_crash_exitcode &&
WEXITSTATUS(fsrv->child_status) == fsrv->crash_exitcode))) { WEXITSTATUS(fsrv->child_status) == fsrv->crash_exitcode))) {
@ -2106,6 +2108,10 @@ fsrv_run_result_t __attribute__((hot)) afl_fsrv_run_target(
fsrv->last_kill_signal = fsrv->last_kill_signal =
WIFSIGNALED(fsrv->child_status) ? WTERMSIG(fsrv->child_status) : 0; WIFSIGNALED(fsrv->child_status) ? WTERMSIG(fsrv->child_status) : 0;
/* For a special exit code, set last_exit_code to non-zero */
fsrv->last_exit_code =
WIFSIGNALED(fsrv->child_status) ? 0 : WEXITSTATUS(fsrv->child_status);
#ifdef AFL_PERSISTENT_RECORD #ifdef AFL_PERSISTENT_RECORD
if (unlikely(fsrv->persistent_record)) { if (unlikely(fsrv->persistent_record)) {

View File

@ -1042,6 +1042,47 @@ void perform_dry_run(afl_state_t *afl) {
if (afl->crash_mode) { break; } if (afl->crash_mode) { break; }
const u8 *msg_exit_code = "";
if (afl->fsrv.uses_asan && !afl->fsrv.last_kill_signal) {
if ((afl->fsrv.uses_asan & 4) &&
afl->fsrv.last_exit_code == MSAN_ERROR) {
msg_exit_code =
" - The test case terminated with the exit code that is "
"used by MSAN to\n"
" indicate an error. This is counted as a crash by "
"afl-fuzz because you\n"
" have compiled the target with MSAN enabled. This could "
"be a false\n"
" positive if the program returns this exit code under "
"normal operation.\n"
" In that case, either disable MSAN or change the test "
"case or program\n"
" to avoid generating this exit code.\n\n";
} else if ((afl->fsrv.uses_asan & 2) &&
afl->fsrv.last_exit_code == LSAN_ERROR) {
msg_exit_code =
" - The test case terminated with the exit code that is "
"used by LSAN to\n"
" indicate an error. This is counted as a crash by "
"afl-fuzz because you\n"
" have compiled the target with LSAN enabled. This could "
"be a false\n"
" positive if the program returns this exit code under "
"normal operation.\n"
" In that case, either disable LSAN or change the test "
"case or program\n"
" to avoid generating this exit code.\n\n";
}
}
if (afl->fsrv.mem_limit) { if (afl->fsrv.mem_limit) {
u8 val_buf[STRINGIFY_VAL_SIZE_MAX]; u8 val_buf[STRINGIFY_VAL_SIZE_MAX];
@ -1056,6 +1097,7 @@ void perform_dry_run(afl_state_t *afl) {
" so, please remove it. The fuzzer should be seeded with " " so, please remove it. The fuzzer should be seeded with "
"interesting\n" "interesting\n"
" inputs - but not ones that cause an outright crash.\n\n" " inputs - but not ones that cause an outright crash.\n\n"
"%s"
" - The current memory limit (%s) is too low for this " " - The current memory limit (%s) is too low for this "
"program, causing\n" "program, causing\n"
@ -1085,6 +1127,7 @@ void perform_dry_run(afl_state_t *afl) {
"other options\n" "other options\n"
" fail, poke the Awesome Fuzzing Discord for " " fail, poke the Awesome Fuzzing Discord for "
"troubleshooting tips.\n", "troubleshooting tips.\n",
msg_exit_code,
stringify_mem_size(val_buf, sizeof(val_buf), stringify_mem_size(val_buf, sizeof(val_buf),
afl->fsrv.mem_limit << 20), afl->fsrv.mem_limit << 20),
afl->fsrv.mem_limit - 1); afl->fsrv.mem_limit - 1);
@ -1101,6 +1144,7 @@ void perform_dry_run(afl_state_t *afl) {
" so, please remove it. The fuzzer should be seeded with " " so, please remove it. The fuzzer should be seeded with "
"interesting\n" "interesting\n"
" inputs - but not ones that cause an outright crash.\n\n" " inputs - but not ones that cause an outright crash.\n\n"
"%s"
" - In QEMU persistent mode the selected address(es) for the " " - In QEMU persistent mode the selected address(es) for the "
"loop are not\n" "loop are not\n"
@ -1113,7 +1157,8 @@ void perform_dry_run(afl_state_t *afl) {
" - Least likely, there is a horrible bug in the fuzzer. If " " - Least likely, there is a horrible bug in the fuzzer. If "
"other options\n" "other options\n"
" fail, poke the Awesome Fuzzing Discord for " " fail, poke the Awesome Fuzzing Discord for "
"troubleshooting tips.\n"); "troubleshooting tips.\n",
msg_exit_code);
} }
@ -3118,11 +3163,23 @@ void check_binary(afl_state_t *afl, u8 *fname) {
} }
if (afl_memmem(f_data, f_len, "__asan_init", 11) || afl->fsrv.uses_asan = 0;
afl_memmem(f_data, f_len, "__msan_init", 11) ||
afl_memmem(f_data, f_len, "__lsan_init", 11)) {
afl->fsrv.uses_asan = 1; if (afl_memmem(f_data, f_len, "__asan_init", 11)) {
afl->fsrv.uses_asan |= 1;
}
if (afl_memmem(f_data, f_len, "__lsan_init", 11)) {
afl->fsrv.uses_asan |= 2;
}
if (afl_memmem(f_data, f_len, "__msan_init", 11)) {
afl->fsrv.uses_asan |= 4;
} }