default: FATAL("Unsupported suffix or bad syntax for -m");
}
if (mem_limit < 5) FATAL("Dangerously low value of -m");
if (sizeof(rlim_t) == 4 && mem_limit > 2000) FATAL("Value of -m out of range on 32-bit systems");
}
break; case'b': { /* bind CPU core */
if (cpu_to_bind_given) FATAL("Multiple -b options not supported"); cpu_to_bind_given = 1;
if (sscanf(optarg, "%u", &cpu_to_bind) < 1 || optarg[0] == '-') FATAL("Bad syntax used for -b");
break;
}
case'd': /* skip deterministic */
if (skip_deterministic) FATAL("Multiple -d options not supported"); skip_deterministic = 1; use_splicing = 1; break;
case'B': /* load bitmap */
/* This is a secret undocumented option! It is useful if you find an interesting test case during a normal fuzzing process, and want to mutate it without rediscovering any of the test cases already found during an earlier run. To use this mode, you need to point -B to the fuzz_bitmap produced by an earlier run for the exact same binary... and that's it. I only used this once or twice to get variants of a particular file, so I'm not making this an official setting. */
if (in_bitmap) FATAL("Multiple -B options not supported");
if (crash_mode) FATAL("Multiple -C options not supported"); crash_mode = FAULT_CRASH; break;
case'n': /* dumb mode */
if (dumb_mode) FATAL("Multiple -n options not supported"); if (getenv("AFL_DUMB_FORKSRV")) dumb_mode = 2; else dumb_mode = 1;
break;
case'T': /* banner */
if (use_banner) FATAL("Multiple -T options not supported"); use_banner = optarg; break;
case'Q': /* QEMU mode */
if (qemu_mode) FATAL("Multiple -Q options not supported"); qemu_mode = 1;
if (!mem_limit_given) mem_limit = MEM_LIMIT_QEMU;
break;
case'V': /* Show version number */
/* Version number has been printed already, just quit. */ exit(0);
default:
usage(argv[0]);
}
if (optind == argc || !in_dir || !out_dir) usage(argv[0]);
setup_signal_handlers(); check_asan_opts();
if (sync_id) fix_up_sync();
if (!strcmp(in_dir, out_dir)) FATAL("Input and output directories can't be the same");
if (dumb_mode) {
if (crash_mode) FATAL("-C and -n are mutually exclusive"); if (qemu_mode) FATAL("-Q and -n are mutually exclusive");
}
if (getenv("AFL_NO_FORKSRV")) no_forkserver = 1; if (getenv("AFL_NO_CPU_RED")) no_cpu_meter_red = 1; if (getenv("AFL_NO_ARITH")) no_arith = 1; if (getenv("AFL_SHUFFLE_QUEUE")) shuffle_queue = 1; if (getenv("AFL_FAST_CAL")) fast_cal = 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");
if (getenv("AFL_PRELOAD")) { setenv("LD_PRELOAD", getenv("AFL_PRELOAD"), 1); setenv("DYLD_INSERT_LIBRARIES", getenv("AFL_PRELOAD"), 1); }
if (getenv("AFL_LD_PRELOAD")) FATAL("Use AFL_PRELOAD instead of AFL_LD_PRELOAD");
while (seek_to) { current_entry++; seek_to--; queue_cur = queue_cur->next; }
show_stats();
if (not_on_tty) { ACTF("Entering queue cycle %llu.", queue_cycle); fflush(stdout); }
/* If we had a full queue cycle with no new finds, try recombination strategies next. */
if (queued_paths == prev_queued) {
if (use_splicing) cycles_wo_finds++; else use_splicing = 1;
} else cycles_wo_finds = 0;
prev_queued = queued_paths;
if (sync_id && queue_cycle == 1 && getenv("AFL_IMPORT_FIRST")) sync_fuzzers(use_argv);
}
skipped_fuzz = fuzz_one(use_argv);
if (!stop_soon && sync_id && !skipped_fuzz) { if (!(sync_interval_cnt++ % SYNC_INTERVAL)) sync_fuzzers(use_argv);
}
if (!stop_soon && exit_1) stop_soon = 2;
if (stop_soon) break;
queue_cur = queue_cur->next; current_entry++;
}
if (queue_cur) show_stats();
/* If we stopped programmatically, we kill the forkserver and the current runner. If we stopped manually, this is done by the signal handler. */ if (stop_soon == 2) { if (child_pid > 0) kill(child_pid, SIGKILL); if (forksrv_pid > 0) kill(forksrv_pid, SIGKILL); } /* Now that we've killed the forkserver, we wait for it to be able to get rusage stats. */ if (waitpid(forksrv_pid, NULL, 0) <= 0) { WARNF("error waitpid\n"); }
default: FATAL("Unsupported suffix or bad syntax for -m");
}
if (mem_limit < 5) FATAL("Dangerously low value of -m");
if (sizeof(rlim_t) == 4 && mem_limit > 2000) FATAL("Value of -m out of range on 32-bit systems");
}
break;
case'b': { /* bind CPU core */
if (cpu_to_bind_given) FATAL("Multiple -b options not supported"); cpu_to_bind_given = 1;
if (sscanf(optarg, "%u", &cpu_to_bind) < 1 || optarg[0] == '-') FATAL("Bad syntax used for -b");
break;
}
case'd': /* skip deterministic */
if (skip_deterministic) FATAL("Multiple -d options not supported"); skip_deterministic = 1; use_splicing = 1; break;
case'B': /* load bitmap */
/* This is a secret undocumented option! It is useful if you find an interesting test case during a normal fuzzing process, and want to mutate it without rediscovering any of the test cases already found during an earlier run. To use this mode, you need to point -B to the fuzz_bitmap produced by an earlier run for the exact same binary... and that's it. I only used this once or twice to get variants of a particular file, so I'm not making this an official setting. */
if (in_bitmap) FATAL("Multiple -B options not supported");
staticinline u8 has_new_bits(u8* virgin_map){ u64* current = (u64*)trace_bits; // 当前执行的覆盖率 u64* virgin = (u64*)virgin_map; // 全局virgin位图 u32 i = (MAP_SIZE >> 3); u8 ret = 0;
while (i--) { // 检查当前执行是否触及了virgin位图中的新位 if (unlikely(*current) && unlikely(*current & *virgin)) { if (likely(ret < 2)) { u8* cur = (u8*)current; u8* vir = (u8*)virgin;
// 5. 目录冲突检查 if (!strcmp(in_dir, out_dir)) FATAL("Input and output directories can't be the same");
1 2 3 4 5 6
if (dumb_mode) {
if (crash_mode) FATAL("-C and -n are mutually exclusive"); if (qemu_mode) FATAL("-Q and -n are mutually exclusive");
}
同时dubm模式与crash和qemu模式不兼容
1 2 3 4 5
if (getenv("AFL_NO_FORKSRV")) no_forkserver = 1; if (getenv("AFL_NO_CPU_RED")) no_cpu_meter_red = 1; if (getenv("AFL_NO_ARITH")) no_arith = 1; if (getenv("AFL_SHUFFLE_QUEUE")) shuffle_queue = 1; if (getenv("AFL_FAST_CAL")) fast_cal = 1;
staticvoidcheck_asan_opts(void){ u8* x = getenv("ASAN_OPTIONS");
if (x) { if (!strstr(x, "abort_on_error=1")) FATAL("Custom ASAN_OPTIONS set without abort_on_error=1 - please fix!");
if (!strstr(x, "symbolize=0")) FATAL("Custom ASAN_OPTIONS set without symbolize=0 - please fix!"); }
x = getenv("MSAN_OPTIONS");
if (x) { if (!strstr(x, "exit_code="STRINGIFY(MSAN_ERROR))) FATAL("Custom MSAN_OPTIONS set without exit_code=" STRINGIFY(MSAN_ERROR) " - please fix!");
if (!strstr(x, "symbolize=0")) FATAL("Custom MSAN_OPTIONS set without symbolize=0 - please fix!"); } }
/* We use scandir() + alphasort() rather than readdir() because otherwise, the ordering of test cases would vary somewhat randomly and would be difficult to control. */
nl_cnt = scandir(in_dir, &nl, NULL, alphasort);
if (nl_cnt < 0) {
if (errno == ENOENT || errno == ENOTDIR)
SAYF("\n" cLRD "[-] " cRST "The input directory does not seem to be valid - try again. The fuzzer needs\n" " one or more test case to start with - ideally, a small file under 1 kB\n" " or so. The cases must be stored as regular files directly in the input\n" " directory.\n");
free(nl[i]); /* not tracked */ if (lstat(fn, &st) || access(fn, R_OK)) PFATAL("Unable to access '%s'", fn);
/* This also takes care of . and .. */
if (!S_ISREG(st.st_mode) || !st.st_size || strstr(fn, "/README.testcases")) {
ck_free(fn); ck_free(dfn); continue;
}
if (st.st_size > MAX_FILE) FATAL("Test case '%s' is too big (%s, limit is %s)", fn, DMS(st.st_size), DMS(MAX_FILE));
/* Check for metadata that indicates that deterministic fuzzing is complete for this entry. We don't want to repeat deterministic fuzzing when resuming aborted scans, because it would be pointless and probably very time-consuming. */
if (!access(dfn, F_OK)) passed_det = 1; ck_free(dfn);
add_to_queue(fn, st.st_size, passed_det);
}
free(nl); /* not tracked */
if (!queued_paths) {
SAYF("\n" cLRD "[-] " cRST "Looks like there are no valid test cases in the input directory! The fuzzer\n" " needs one or more test case to start with - ideally, a small file under\n" " 1 kB or so. The cases must be stored as regular files directly in the\n" " input directory.\n");
/* We use scandir() + alphasort() rather than readdir() because otherwise, the ordering of test cases would vary somewhat randomly and would be difficult to control. */
nl_cnt = scandir(in_dir, &nl, NULL, alphasort);
if (nl_cnt < 0) {
if (errno == ENOENT || errno == ENOTDIR)
SAYF("\n" cLRD "[-] " cRST "The input directory does not seem to be valid - try again. The fuzzer needs\n" " one or more test case to start with - ideally, a small file under 1 kB\n" " or so. The cases must be stored as regular files directly in the input\n" " directory.\n");
if (lstat(fn, &st) || access(fn, R_OK)) PFATAL("Unable to access '%s'", fn);
/* This also takes care of . and .. */
if (!S_ISREG(st.st_mode) || !st.st_size || strstr(fn, "/README.testcases")) {
ck_free(fn); ck_free(dfn); continue;
}
if (st.st_size > MAX_FILE) FATAL("Test case '%s' is too big (%s, limit is %s)", fn, DMS(st.st_size), DMS(MAX_FILE));
/* Check for metadata that indicates that deterministic fuzzing is complete for this entry. We don't want to repeat deterministic fuzzing when resuming aborted scans, because it would be pointless and probably very time-consuming. */
if (!access(dfn, F_OK)) passed_det = 1; ck_free(dfn);