Skip to content

Commit

Permalink
2.41b
Browse files Browse the repository at this point in the history
  • Loading branch information
thomas-huet committed Jun 18, 2017
1 parent 0e4298b commit a8aa47c
Show file tree
Hide file tree
Showing 6 changed files with 100 additions and 48 deletions.
107 changes: 69 additions & 38 deletions afl-fuzz.c
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,9 @@ EXP_ST u8 *in_dir, /* Input directory with test cases */
*orig_cmdline; /* Original command line */

EXP_ST u32 exec_tmout = EXEC_TIMEOUT; /* Configurable exec timeout (ms) */
EXP_ST u64 mem_limit = MEM_LIMIT; /* Memory cap for child (MB) */
static u32 hang_tmout = EXEC_TIMEOUT; /* Timeout used for hang det (ms) */

EXP_ST u64 mem_limit = MEM_LIMIT; /* Memory cap for child (MB) */

static u32 stats_update_freq = 1; /* Stats update frequency (execs) */

Expand Down Expand Up @@ -132,7 +134,7 @@ static s32 forksrv_pid, /* PID of the fork server */
EXP_ST u8* trace_bits; /* SHM with instrumentation bitmap */

EXP_ST u8 virgin_bits[MAP_SIZE], /* Regions yet untouched by fuzzing */
virgin_hang[MAP_SIZE], /* Bits we haven't seen in hangs */
virgin_tmout[MAP_SIZE], /* Bits we haven't seen in tmouts */
virgin_crash[MAP_SIZE]; /* Bits we haven't seen in crashes */

static u8 var_bytes[MAP_SIZE]; /* Bytes that appear to be variable */
Expand Down Expand Up @@ -162,7 +164,8 @@ EXP_ST u32 queued_paths, /* Total number of queued testcases */

EXP_ST u64 total_crashes, /* Total number of crashes */
unique_crashes, /* Crashes with unique signatures */
total_hangs, /* Total number of hangs */
total_tmouts, /* Total number of timeouts */
unique_tmouts, /* Timeouts with unique signatures */
unique_hangs, /* Hangs with unique signatures */
total_execs, /* Total execve() calls */
start_time, /* Unix start time (ms) */
Expand All @@ -178,7 +181,7 @@ EXP_ST u64 total_crashes, /* Total number of crashes */
blocks_eff_total, /* Blocks subject to effector maps */
blocks_eff_select; /* Blocks selected as fuzzable */

static u32 subseq_hangs; /* Number of hangs in a row */
static u32 subseq_tmouts; /* Number of timeouts in a row */

static u8 *stage_name = "init", /* Name of the current fuzz stage */
*stage_short, /* Short stage name */
Expand Down Expand Up @@ -308,7 +311,7 @@ enum {

enum {
/* 00 */ FAULT_NONE,
/* 01 */ FAULT_HANG,
/* 01 */ FAULT_TMOUT,
/* 02 */ FAULT_CRASH,
/* 03 */ FAULT_ERROR,
/* 04 */ FAULT_NOINST,
Expand Down Expand Up @@ -1031,7 +1034,7 @@ static u32 count_non_255_bytes(u8* mem) {

/* Destructively simplify trace by eliminating hit count information
and replacing it with 0x80 or 0x01 depending on whether the tuple
is hit or not. Called on every new crash or hang, should be
is hit or not. Called on every new crash or timeout, should be
reasonably fast. */

static const u8 simplify_lookup[256] = {
Expand Down Expand Up @@ -1339,7 +1342,7 @@ EXP_ST void setup_shm(void) {

if (!in_bitmap) memset(virgin_bits, 255, MAP_SIZE);

memset(virgin_hang, 255, MAP_SIZE);
memset(virgin_tmout, 255, MAP_SIZE);
memset(virgin_crash, 255, MAP_SIZE);

shm_id = shmget(IPC_PRIVATE, MAP_SIZE, IPC_CREAT | IPC_EXCL | 0600);
Expand Down Expand Up @@ -2252,7 +2255,7 @@ EXP_ST void init_forkserver(char** argv) {
/* Execute target application, monitoring for timeouts. Return status
information. The called program will update trace_bits[]. */

static u8 run_target(char** argv) {
static u8 run_target(char** argv, u32 timeout) {

static struct itimerval it;
static u32 prev_timed_out = 0;
Expand Down Expand Up @@ -2378,8 +2381,8 @@ static u8 run_target(char** argv) {

/* Configure timeout, as requested by user, then wait for child to terminate. */

it.it_value.tv_sec = (exec_tmout / 1000);
it.it_value.tv_usec = (exec_tmout % 1000) * 1000;
it.it_value.tv_sec = (timeout / 1000);
it.it_value.tv_usec = (timeout % 1000) * 1000;

setitimer(ITIMER_REAL, &it, NULL);

Expand Down Expand Up @@ -2429,7 +2432,7 @@ static u8 run_target(char** argv) {

/* Report outcome to caller. */

if (child_timed_out) return FAULT_HANG;
if (child_timed_out) return FAULT_TMOUT;

if (WIFSIGNALED(status) && !stop_soon) {
kill_signal = WTERMSIG(status);
Expand Down Expand Up @@ -2529,16 +2532,17 @@ static u8 calibrate_case(char** argv, struct queue_entry* q, u8* use_mem,

u64 start_us, stop_us;

s32 old_sc = stage_cur, old_sm = stage_max, old_tmout = exec_tmout;
s32 old_sc = stage_cur, old_sm = stage_max;
u32 use_tmout = exec_tmout;
u8* old_sn = stage_name;

/* Be a bit more generous about timeouts when resuming sessions, or when
trying to calibrate already-added finds. This helps avoid trouble due
to intermittent latency. */

if (!from_queue || resuming_fuzz)
exec_tmout = MAX(exec_tmout + CAL_TMOUT_ADD,
exec_tmout * CAL_TMOUT_PERC / 100);
use_tmout = MAX(exec_tmout + CAL_TMOUT_ADD,
exec_tmout * CAL_TMOUT_PERC / 100);

q->cal_failed++;

Expand All @@ -2563,7 +2567,7 @@ static u8 calibrate_case(char** argv, struct queue_entry* q, u8* use_mem,

write_to_testcase(use_mem, q->len);

fault = run_target(argv);
fault = run_target(argv, use_tmout);

/* stop_soon is set by the handler for Ctrl+C. When it's pressed,
we want to bail out quickly. */
Expand Down Expand Up @@ -2657,7 +2661,6 @@ static u8 calibrate_case(char** argv, struct queue_entry* q, u8* use_mem,
stage_name = old_sn;
stage_cur = old_sc;
stage_max = old_sm;
exec_tmout = old_tmout;

if (!first_run) show_stats();

Expand Down Expand Up @@ -2730,7 +2733,7 @@ static void perform_dry_run(char** argv) {

break;

case FAULT_HANG:
case FAULT_TMOUT:

if (timeout_given) {

Expand All @@ -2739,7 +2742,7 @@ static void perform_dry_run(char** argv) {
out. */

if (timeout_given > 1) {
WARNF("Test case results in a hang (skipping)");
WARNF("Test case results in a timeout (skipping)");
q->cal_failed = CAL_CHANCES;
cal_failures++;
break;
Expand All @@ -2753,7 +2756,7 @@ static void perform_dry_run(char** argv) {
" '+' at the end of the value passed to -t ('-t %u+').\n", exec_tmout,
exec_tmout);

FATAL("Test case '%s' results in a hang", fn);
FATAL("Test case '%s' results in a timeout", fn);

} else {

Expand All @@ -2765,7 +2768,7 @@ static void perform_dry_run(char** argv) {
" If this test case is just a fluke, the other option is to just avoid it\n"
" altogether, and find one that is less of a CPU hog.\n", exec_tmout);

FATAL("Test case '%s' results in a hang", fn);
FATAL("Test case '%s' results in a timeout", fn);

}

Expand Down Expand Up @@ -3161,14 +3164,14 @@ static u8 save_if_interesting(char** argv, void* mem, u32 len, u8 fault) {

switch (fault) {

case FAULT_HANG:
case FAULT_TMOUT:

/* Hangs are not very interesting, but we're still obliged to keep
/* Timeouts are not very interesting, but we're still obliged to keep
a handful of samples. We use the presence of new bits in the
hang-specific bitmap as a signal of uniqueness. In "dumb" mode, we
just keep everything. */

total_hangs++;
total_tmouts++;

if (unique_hangs >= KEEP_UNIQUE_HANG) return keeping;

Expand All @@ -3180,7 +3183,23 @@ static u8 save_if_interesting(char** argv, void* mem, u32 len, u8 fault) {
simplify_trace((u32*)trace_bits);
#endif /* ^__x86_64__ */

if (!has_new_bits(virgin_hang)) return keeping;
if (!has_new_bits(virgin_tmout)) return keeping;

}

unique_tmouts++;

/* Before saving, we make sure that it's a genuine hang by re-running
the target with a more generous timeout (unless the default timeout
is already generous). */

if (exec_tmout < hang_tmout) {

u8 new_fault;
write_to_testcase(mem, len);
new_fault = run_target(argv, hang_tmout);

if (stop_soon || new_fault != FAULT_TMOUT) return keeping;

}

Expand All @@ -3204,8 +3223,9 @@ static u8 save_if_interesting(char** argv, void* mem, u32 len, u8 fault) {

case FAULT_CRASH:

/* This is handled in a manner roughly similar to hangs,
except for slightly different limits. */
/* This is handled in a manner roughly similar to timeouts,
except for slightly different limits and no need to re-run test
cases. */

total_crashes++;

Expand Down Expand Up @@ -4132,10 +4152,10 @@ static void show_stats(void) {

}

sprintf(tmp, "%s (%s%s unique)", DI(total_hangs), DI(unique_hangs),
sprintf(tmp, "%s (%s%s unique)", DI(total_tmouts), DI(unique_tmouts),
(unique_hangs >= KEEP_UNIQUE_HANG) ? "+" : "");

SAYF (bSTG bV bSTOP " total hangs : " cRST "%-22s " bSTG bV "\n", tmp);
SAYF (bSTG bV bSTOP " total tmouts : " cRST "%-22s " bSTG bV "\n", tmp);

/* Aaaalmost there... hold on! */

Expand Down Expand Up @@ -4388,6 +4408,12 @@ static void show_init_stats(void) {

}

/* In dumb mode, re-running every timing out test case with a generous time
limit is very expensive, so let's select a more conservative default. */

if (dumb_mode && !getenv("AFL_HANG_TMOUT"))
hang_tmout = exec_tmout * 4;

OKF("All set and ready to roll!");

}
Expand Down Expand Up @@ -4452,7 +4478,7 @@ static u8 trim_case(char** argv, struct queue_entry* q, u8* in_buf) {

write_with_gap(in_buf, q->len, remove_pos, trim_avail);

fault = run_target(argv);
fault = run_target(argv, exec_tmout);
trim_execs++;

if (stop_soon || fault == FAULT_ERROR) goto abort_trimming;
Expand Down Expand Up @@ -4547,18 +4573,18 @@ EXP_ST u8 common_fuzz_stuff(char** argv, u8* out_buf, u32 len) {

write_to_testcase(out_buf, len);

fault = run_target(argv);
fault = run_target(argv, exec_tmout);

if (stop_soon) return 1;

if (fault == FAULT_HANG) {
if (fault == FAULT_TMOUT) {

if (subseq_hangs++ > HANG_LIMIT) {
if (subseq_tmouts++ > TMOUT_LIMIT) {
cur_skipped_paths++;
return 1;
}

} else subseq_hangs = 0;
} else subseq_tmouts = 0;

/* Users can hit us with SIGUSR1 to request the current input
to be abandoned. */
Expand Down Expand Up @@ -4963,7 +4989,7 @@ static u8 fuzz_one(char** argv) {

out_buf = ck_alloc_nozero(len);

subseq_hangs = 0;
subseq_tmouts = 0;

cur_depth = queue_cur->depth;

Expand All @@ -4973,7 +4999,7 @@ static u8 fuzz_one(char** argv) {

if (queue_cur->cal_failed) {

u8 res = FAULT_HANG;
u8 res = FAULT_TMOUT;

if (queue_cur->cal_failed < CAL_CHANCES) {

Expand Down Expand Up @@ -6674,7 +6700,7 @@ static void sync_fuzzers(char** argv) {

write_to_testcase(mem, st.st_size);

fault = run_target(argv);
fault = run_target(argv, exec_tmout);

if (stop_soon) return;

Expand Down Expand Up @@ -7180,7 +7206,7 @@ static void check_crash_handling(void) {
" external crash reporting utility. This will cause issues due to the\n"
" extended delay between the fuzzed binary malfunctioning and this fact\n"
" being relayed to the fuzzer via the standard waitpid() API.\n\n"
" To avoid having crashes misinterpreted as hangs, please run the\n"
" To avoid having crashes misinterpreted as timeouts, please run the\n"
" following commands:\n\n"

" SL=/System/Library; PL=com.apple.ReportCrash\n"
Expand Down Expand Up @@ -7210,7 +7236,7 @@ static void check_crash_handling(void) {
" between stumbling upon a crash and having this information relayed to the\n"
" fuzzer via the standard waitpid() API.\n\n"

" To avoid having crashes misinterpreted as hangs, please log in as root\n"
" To avoid having crashes misinterpreted as timeouts, please log in as root\n"
" and temporarily modify /proc/sys/kernel/core_pattern, like so:\n\n"

" echo core >/proc/sys/kernel/core_pattern\n");
Expand Down Expand Up @@ -7858,6 +7884,11 @@ int main(int argc, char** argv) {
if (getenv("AFL_NO_CPU_RED")) no_cpu_meter_red = 1;
if (getenv("AFL_SHUFFLE_QUEUE")) shuffle_queue = 1;

if (getenv("AFL_HANG_TMOUT")) {
hang_tmout = atoi(getenv("AFL_HANG_TMOUT"));
if (!hang_tmout) FATAL("Invalid value of AFL_HANG_TMOUT");
}

if (dumb_mode == 2 && no_forkserver)
FATAL("AFL_DUMB_FORKSRV and AFL_NO_FORKSRV are mutually exclusive");

Expand Down
9 changes: 5 additions & 4 deletions config.h
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@

/* Version string: */

#define VERSION "2.40b"
#define VERSION "2.41b"

/******************************************************
* *
Expand All @@ -38,7 +38,8 @@

#define FANCY_BOXES

/* Default timeout for fuzzed code (milliseconds): */
/* Default timeout for fuzzed code (milliseconds). This is the upper bound,
also used for detecting hangs; the actual value is auto-scaled: */

#define EXEC_TIMEOUT 1000

Expand All @@ -64,9 +65,9 @@
#define CAL_CYCLES 8
#define CAL_CYCLES_LONG 40

/* Number of subsequent hangs before abandoning an input file: */
/* Number of subsequent timeouts before abandoning an input file: */

#define HANG_LIMIT 250
#define TMOUT_LIMIT 250

/* Maximum number of unique hangs or crashes to record: */

Expand Down
8 changes: 8 additions & 0 deletions docs/ChangeLog
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,14 @@ Not sure if you should upgrade? The lowest currently recommended version
is 2.31b. If you're stuck on an earlier release, it's strongly advisable
to get on with the times.

--------------
Version 2.41b:
--------------

- Addressed a major user complaint related to timeout detection. Timing out
inputs are now binned as "hangs" only if they exceed a far more generous
time limit than the one used to reject slow paths.

--------------
Version 2.40b:
--------------
Expand Down
Loading

0 comments on commit a8aa47c

Please sign in to comment.