|
|
|
|
|
/*
|
|
|
american fuzzy lop++ - stats related routines
|
|
|
---------------------------------------------
|
|
|
|
|
|
Originally written by Michal Zalewski
|
|
|
|
|
|
Now maintained by Marc Heuse <mh@mh-sec.de>,
|
|
|
Dominik Meier <mail@dmnk.co>,
|
|
|
Andrea Fioraldi <andreafioraldi@gmail.com>, and
|
|
|
Heiko Eissfeldt <heiko.eissfeldt@hexco.de>
|
|
|
|
|
|
Copyright 2016, 2017 Google Inc. All rights reserved.
|
|
|
Copyright 2019-2024 AFLplusplus Project. All rights reserved.
|
|
|
|
|
|
Licensed under the Apache License, Version 2.0 (the "License");
|
|
|
you may not use this file except in compliance with the License.
|
|
|
You may obtain a copy of the License at:
|
|
|
|
|
|
https://www.apache.org/licenses/LICENSE-2.0
|
|
|
|
|
|
This is the real deal: the program takes an instrumented binary and
|
|
|
attempts a variety of basic fuzzing tricks, paying close attention to
|
|
|
how they affect the execution path.
|
|
|
|
|
|
*/
|
|
|
|
|
|
#include "afl-fuzz.h"
|
|
|
#include "envs.h"
|
|
|
#include <limits.h>
|
|
|
|
|
|
//乔成炜
|
|
|
// 定义一个二维数组,用于存储模糊测试(fuzzing)的不同状态信息。
|
|
|
static char fuzzing_state[4][12] = {"started :-)", "in progress", "final phase",
|
|
|
"finished..."};
|
|
|
|
|
|
// 函数用于获取当前的模糊测试状态。
|
|
|
char *get_fuzzing_state(afl_state_t *afl) {
|
|
|
// 获取当前时间(毫秒)。
|
|
|
u64 cur_ms = get_cur_time();
|
|
|
|
|
|
// 计算自上次发现问题以来经过的时间。
|
|
|
u64 last_find = cur_ms - afl->last_find_time;
|
|
|
|
|
|
// 计算当前运行周期的时间。
|
|
|
u64 cur_run_time = cur_ms - afl->start_time;
|
|
|
|
|
|
// 计算总的运行时间。
|
|
|
u64 cur_total_run_time = afl->prev_run_time + cur_run_time;
|
|
|
|
|
|
// 如果是非插桩模式,返回"in progress"状态。
|
|
|
if (unlikely(afl->non_instrumented_mode)) {
|
|
|
return fuzzing_state[1];
|
|
|
} else if (unlikely(cur_run_time <
|
|
|
60 * 3 * 1000 || // 如果当前运行周期小于3分钟,
|
|
|
cur_total_run_time <
|
|
|
60 * 5 * 1000)) { // 或者总运行时间小于5分钟,
|
|
|
// 返回"started :-)"状态。
|
|
|
return fuzzing_state[0];
|
|
|
} else {
|
|
|
// 计算最近一次发现问题占当前运行时间的百分比。
|
|
|
u64 last_find_100 = 100 * last_find;
|
|
|
u64 percent_cur = last_find_100 / cur_run_time;
|
|
|
// 计算最近一次发现问题占总运行时间的百分比。
|
|
|
u64 percent_total = last_find_100 / cur_total_run_time;
|
|
|
|
|
|
// 如果当前和总运行时间的百分比都大于等于80%,返回"finished..."状态。
|
|
|
if (unlikely(percent_cur >= 80 && percent_total >= 80)) {
|
|
|
return fuzzing_state[3];
|
|
|
// 如果当前和总运行时间的百分比都大于等于55%,返回"final phase"状态。
|
|
|
} else if (unlikely(percent_cur >= 55 && percent_total >= 55)) {
|
|
|
return fuzzing_state[2];
|
|
|
// 否则,返回"in progress"状态。
|
|
|
} else {
|
|
|
return fuzzing_state[1];
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
|
|
|
/* 写入模糊测试设置文件 */
|
|
|
|
|
|
// 函数用于写入模糊测试器的设置文件。
|
|
|
void write_setup_file(afl_state_t *afl, u32 argc, char **argv) {
|
|
|
// 定义两个用于存储文件名的数组。
|
|
|
u8 fn[PATH_MAX], fn2[PATH_MAX];
|
|
|
|
|
|
// 构造目标哈希文件的路径并创建该文件。
|
|
|
snprintf(fn2, PATH_MAX, "%s/target_hash", afl->out_dir);
|
|
|
FILE *f2 = create_ffile(fn2);
|
|
|
|
|
|
// 在Linux系统中,如果启用了nyx模式,则加载目标哈希。
|
|
|
#ifdef __linux__
|
|
|
if (afl->fsrv.nyx_mode) {
|
|
|
nyx_load_target_hash(&afl->fsrv);
|
|
|
// 将64位目标哈希写入文件。
|
|
|
fprintf(f2, "%llx\n", afl->fsrv.nyx_target_hash64);
|
|
|
} else {
|
|
|
// 否则,计算并写入目标二进制的哈希。
|
|
|
fprintf(f2, "%p\n", (void *)get_binary_hash(afl->fsrv.target_path));
|
|
|
}
|
|
|
#else
|
|
|
// 在非Linux系统中,计算并写入目标二进制的哈希。
|
|
|
fprintf(f2, "%p\n", (void *)get_binary_hash(afl->fsrv.target_path));
|
|
|
#endif
|
|
|
// 关闭目标哈希文件。
|
|
|
fclose(f2);
|
|
|
|
|
|
// 构造模糊测试器设置文件的路径并创建该文件。
|
|
|
snprintf(fn, PATH_MAX, "%s/fuzzer_setup", afl->out_dir);
|
|
|
FILE *f = create_ffile(fn);
|
|
|
u32 i;
|
|
|
|
|
|
// 写入环境变量部分的标题。
|
|
|
fprintf(f, "# environment variables:\n");
|
|
|
// 计算环境变量数组的长度。
|
|
|
u32 s_afl_env = (u32)sizeof(afl_environment_variables) /
|
|
|
sizeof(afl_environment_variables[0]) -
|
|
|
1U;
|
|
|
|
|
|
// 遍历环境变量数组。
|
|
|
for (i = 0; i < s_afl_env; ++i) {
|
|
|
char *val;
|
|
|
// 如果环境变量已设置,则获取其值。
|
|
|
if ((val = getenv(afl_environment_variables[i])) != NULL) {
|
|
|
// 将环境变量及其值写入设置文件。
|
|
|
fprintf(f, "%s=%s\n", afl_environment_variables[i], val);
|
|
|
}
|
|
|
}
|
|
|
// 关闭设置文件。
|
|
|
fclose(f);
|
|
|
}
|
|
|
|
|
|
// 写入命令行参数到设置文件。
|
|
|
fprintf(f, "# command line:\n");
|
|
|
|
|
|
// 定义一个 size_t 类型的变量 j,用于循环计数。
|
|
|
size_t j;
|
|
|
// 遍历命令行参数列表。
|
|
|
for (i = 0; i < argc; ++i) {
|
|
|
// 如果不是第一个参数,输出一个空格以分隔参数。
|
|
|
if (i) fprintf(f, " ");
|
|
|
|
|
|
// 在 Android 系统中,使用 memchr 函数检查参数中是否包含单引号。
|
|
|
#ifdef __ANDROID__
|
|
|
if (memchr(argv[i], '\'', strlen(argv[i]))) {
|
|
|
#else
|
|
|
// 在非 Android 系统中,使用 strchr 函数检查参数中是否包含单引号。
|
|
|
if (strchr(argv[i], '\'')) {
|
|
|
#endif
|
|
|
|
|
|
// 如果参数中包含单引号,输出一个单引号开始标记参数。
|
|
|
fprintf(f, "'");
|
|
|
// 遍历参数字符串中的每个字符。
|
|
|
for (j = 0; j < strlen(argv[i]); j++)
|
|
|
// 如果字符是单引号,输出两个单引号以转义它。
|
|
|
if (argv[i][j] == '\'') fprintf(f, "'\"'\"'");
|
|
|
// 否则,正常输出字符。
|
|
|
else
|
|
|
fprintf(f, "%c", argv[i][j]);
|
|
|
// 参数字符串结束后,输出一个单引号结束标记。
|
|
|
fprintf(f, "'");
|
|
|
|
|
|
// 如果参数中不包含单引号,直接输出参数。
|
|
|
} else {
|
|
|
fprintf(f, "'%s'", argv[i]);
|
|
|
}
|
|
|
}
|
|
|
// 输出换行符,结束命令行参数的写入。
|
|
|
fprintf(f, "\n");
|
|
|
|
|
|
// 关闭设置文件。
|
|
|
fclose(f);
|
|
|
// 这行代码的目的是避免编译器警告,表明该变量在当前上下文中未使用。
|
|
|
(void)(afl_environment_deprecated);
|
|
|
|
|
|
// 定义一个函数,用于检查字符串 line 是否以 key 开头。
|
|
|
static bool starts_with(char *key, char *line) {
|
|
|
// 使用 strncmp 函数比较 key 和 line 的前 strlen(key) 个字符。
|
|
|
return strncmp(key, line, strlen(key)) == 0;
|
|
|
}
|
|
|
|
|
|
/* 当恢复模糊测试时,加载现有的统计文件。*/
|
|
|
void load_stats_file(afl_state_t *afl) {
|
|
|
// 定义文件指针 f,用于读取统计文件。
|
|
|
FILE *f;
|
|
|
// 定义缓冲区 buf,用于存储从文件中读取的行。
|
|
|
u8 buf[MAX_LINE];
|
|
|
// 定义指针 lptr,指向 buf 中当前处理的行。
|
|
|
u8 *lptr;
|
|
|
// 定义文件名 fn,用于存储统计文件的路径。
|
|
|
u8 fn[PATH_MAX];
|
|
|
// 定义行号 lineno,用于记录读取的行数。
|
|
|
u32 lineno = 0;
|
|
|
// 构造统计文件的路径并尝试打开文件。
|
|
|
snprintf(fn, PATH_MAX, "%s/fuzzer_stats", afl->out_dir);
|
|
|
f = fopen(fn, "r");
|
|
|
if (!f) {
|
|
|
// 如果无法打开文件,输出警告信息并返回。
|
|
|
WARNF("Unable to load stats file '%s'", fn);
|
|
|
return;
|
|
|
}
|
|
|
|
|
|
// 循环读取文件中的每一行。
|
|
|
while ((lptr = fgets(buf, MAX_LINE, f))) {
|
|
|
// 每次读取新行时,行号递增。
|
|
|
lineno++;
|
|
|
// 初始化指针 lstartptr 指向当前行的开始。
|
|
|
u8 *lstartptr = lptr;
|
|
|
// 初始化指针 rptr 指向当前行的结束。
|
|
|
u8 *rptr = lptr + strlen(lptr) - 1;
|
|
|
// 定义 keystring 用于存储行中的键。
|
|
|
u8 keystring[MAX_LINE];
|
|
|
// 移动 lptr 指针,跳过行中的非键部分,直到遇到冒号或行尾。
|
|
|
while (*lptr != ':' && lptr < rptr) {
|
|
|
lptr++;
|
|
|
}
|
|
|
|
|
|
// 如果当前行是空行或者只包含换行符,输出警告信息并继续处理下一行。
|
|
|
if (*lptr == '\n' || !*lptr) {
|
|
|
WARNF("Unable to read line %d of stats file", lineno);
|
|
|
continue;
|
|
|
}
|
|
|
|
|
|
// 如果当前字符是冒号,表示这一行包含键值对信息。
|
|
|
if (*lptr == ':') {
|
|
|
// 将冒号替换为字符串结束符,分割键和值。
|
|
|
*lptr = 0;
|
|
|
strcpy(keystring, lstartptr);
|
|
|
// 移动指针到值的开始位置。
|
|
|
lptr++;
|
|
|
// 定义指针 nptr,用于strtoull函数中的转换。
|
|
|
char *nptr;
|
|
|
|
|
|
// 如果键是"run_time",将值转换为毫秒并存储到afl->prev_run_time。
|
|
|
if (starts_with("run_time", keystring)) {
|
|
|
afl->prev_run_time = 1000 * strtoull(lptr, &nptr, 10);
|
|
|
}
|
|
|
|
|
|
// 如果键是"cycles_done",将值转换为无符号长长整型并存储到afl->queue_cycle。
|
|
|
if (starts_with("cycles_done", keystring)) {
|
|
|
afl->queue_cycle =
|
|
|
strtoull(lptr, &nptr, 10) ? strtoull(lptr, &nptr, 10) + 1 : 0;
|
|
|
}
|
|
|
|
|
|
// 如果键是"calibration_time",将值转换为微秒并存储到afl->calibration_time_us。
|
|
|
if (starts_with("calibration_time", keystring)) {
|
|
|
afl->calibration_time_us = strtoull(lptr, &nptr, 10) * 1000000;
|
|
|
}
|
|
|
|
|
|
// 如果键是"sync_time",将值转换为微秒并存储到afl->sync_time_us。
|
|
|
if (starts_with("sync_time", keystring)) {
|
|
|
afl->sync_time_us = strtoull(lptr, &nptr, 10) * 1000000;
|
|
|
}
|
|
|
|
|
|
// 如果键是"cmplog_time",将值转换为微秒并存储到afl->cmplog_time_us。
|
|
|
if (starts_with("cmplog_time", keystring)) {
|
|
|
afl->cmplog_time_us = strtoull(lptr, &nptr, 10) * 1000000;
|
|
|
}
|
|
|
|
|
|
// 如果键是"trim_time",将值转换为微秒并存储到afl->trim_time_us。
|
|
|
if (starts_with("trim_time", keystring)) {
|
|
|
afl->trim_time_us = strtoull(lptr, &nptr, 10) * 1000000;
|
|
|
}
|
|
|
|
|
|
// 如果键是"execs_done",将值转换为无符号长长整型并存储到afl->fsrv.total_execs。
|
|
|
if (starts_with("execs_done", keystring)) {
|
|
|
afl->fsrv.total_execs = strtoull(lptr, &nptr, 10);
|
|
|
}
|
|
|
|
|
|
// 如果键是"corpus_count",将值转换为无符号整型并存储到afl->queued_items。
|
|
|
if (starts_with("corpus_count", keystring)) {
|
|
|
u32 corpus_count = strtoul(lptr, &nptr, 10);
|
|
|
if (corpus_count != afl->queued_items) {
|
|
|
WARNF(
|
|
|
"queue/ has been modified -- things might not work, you're "
|
|
|
"on your own!");
|
|
|
sleep(3);
|
|
|
}
|
|
|
}
|
|
|
|
|
|
// 如果键是"corpus_found",将值转换为无符号整型并存储到afl->queued_discovered。
|
|
|
if (starts_with("corpus_found", keystring)) {
|
|
|
afl->queued_discovered = strtoul(lptr, &nptr, 10);
|
|
|
}
|
|
|
|
|
|
// 如果键是"corpus_imported",将值转换为无符号整型并存储到afl->queued_imported。
|
|
|
if (starts_with("corpus_imported", keystring)) {
|
|
|
afl->queued_imported = strtoul(lptr, &nptr, 10);
|
|
|
}
|
|
|
|
|
|
// 如果键是"max_depth",将值转换为无符号整型并存储到afl->max_depth。
|
|
|
if (starts_with("max_depth", keystring)) {
|
|
|
afl->max_depth = strtoul(lptr, &nptr, 10);
|
|
|
}
|
|
|
|
|
|
// 如果键是"saved_crashes",将值转换为无符号长长整型并存储到afl->saved_crashes。
|
|
|
if (starts_with("saved_crashes", keystring)) {
|
|
|
afl->saved_crashes = strtoull(lptr, &nptr, 10);
|
|
|
}
|
|
|
|
|
|
// 如果键是"saved_hangs",将值转换为无符号长长整型并存储到afl->saved_hangs。
|
|
|
if (starts_with("saved_hangs", keystring)) {
|
|
|
afl->saved_hangs = strtoull(lptr, &nptr, 10);
|
|
|
}
|
|
|
}
|
|
|
|
|
|
// 如果有保存的崩溃信息,写入README文件。
|
|
|
if (afl->saved_crashes) { write_crash_readme(afl); }
|
|
|
|
|
|
// 结束函数。
|
|
|
return;
|
|
|
}
|
|
|
/* Update stats file for unattended monitoring. */
|
|
|
|
|
|
// 函数用于写入 AFL++ 的统计信息到文件。
|
|
|
void write_stats_file(afl_state_t * afl, u32 t_bytes, double bitmap_cvg,
|
|
|
double stability, double eps) {
|
|
|
#ifndef __HAIKU__
|
|
|
// 在非 Haiku 系统中,获取子进程的资源使用情况。
|
|
|
struct rusage rus;
|
|
|
#endif
|
|
|
|
|
|
// 获取当前时间。
|
|
|
u64 cur_time = get_cur_time();
|
|
|
// 定义临时和最终的统计文件名。
|
|
|
u8 fn_tmp[PATH_MAX], fn_final[PATH_MAX];
|
|
|
// 定义文件指针。
|
|
|
FILE *f;
|
|
|
|
|
|
// 构造临时统计文件的路径。
|
|
|
snprintf(fn_tmp, PATH_MAX, "%s/.fuzzer_stats_tmp", afl->out_dir);
|
|
|
// 构造最终统计文件的路径。
|
|
|
snprintf(fn_final, PATH_MAX, "%s/fuzzer_stats", afl->out_dir);
|
|
|
// 创建并打开临时统计文件。
|
|
|
f = create_ffile(fn_tmp);
|
|
|
|
|
|
// 如果没有提供 bitmap_cvg, stability, eps,则使用上一次的值。
|
|
|
if (!bitmap_cvg && !stability && !eps) {
|
|
|
bitmap_cvg = afl->last_bitmap_cvg;
|
|
|
stability = afl->last_stability;
|
|
|
} else {
|
|
|
// 否则更新上一次的值。
|
|
|
afl->last_bitmap_cvg = bitmap_cvg;
|
|
|
afl->last_stability = stability;
|
|
|
afl->last_eps = eps;
|
|
|
}
|
|
|
|
|
|
// 如果距离上次更新平均执行次数的时间超过60秒,或者这是第一次更新,则更新平均执行次数。
|
|
|
if (unlikely(!afl->last_avg_exec_update ||
|
|
|
cur_time - afl->last_avg_exec_update >= 60000)) {
|
|
|
afl->last_avg_execs_saved =
|
|
|
(double)(1000 * (afl->fsrv.total_execs - afl->last_avg_execs)) /
|
|
|
(double)(cur_time - afl->last_avg_exec_update);
|
|
|
afl->last_avg_execs = afl->fsrv.total_execs;
|
|
|
afl->last_avg_exec_update = cur_time;
|
|
|
}
|
|
|
|
|
|
#ifndef __HAIKU__
|
|
|
// 在非 Haiku
|
|
|
// 系统中,获取子进程的资源使用情况,如果失败则将内存使用设置为0。
|
|
|
if (getrusage(RUSAGE_CHILDREN, &rus)) { rus.ru_maxrss = 0; }
|
|
|
#endif
|
|
|
// 计算总运行时间(毫秒)。
|
|
|
u64 runtime_ms = afl->prev_run_time + cur_time - afl->start_time;
|
|
|
// 计算总开销时间(毫秒)。
|
|
|
u64 overhead_ms = (afl->calibration_time_us + afl->sync_time_us +
|
|
|
afl->trim_time_us + afl->cmplog_time_us) /
|
|
|
1000;
|
|
|
// 如果运行时间为0,则设置为1以避免除以0的错误。
|
|
|
if (!runtime_ms) { runtime_ms = 1; }
|
|
|
|
|
|
// 写入统计信息到文件。
|
|
|
fprintf(f, "start_time : %llu\n" /* ... 其他统计信息 ... */,
|
|
|
/* 参数列表 */);
|
|
|
|
|
|
// 忽略错误,继续执行。
|
|
|
|
|
|
// 如果启用了调试模式,写入额外的调试信息。
|
|
|
if (afl->debug) {
|
|
|
u32 i = 0;
|
|
|
fprintf(f, "virgin_bytes :");
|
|
|
// 遍历 virgin_bits 数组,写入非0xff的值。
|
|
|
for (i = 0; i < afl->fsrv.real_map_size; i++) {
|
|
|
if (afl->virgin_bits[i] != 0xff) {
|
|
|
fprintf(f, " %u[%02x]", i, afl->virgin_bits[i]);
|
|
|
}
|
|
|
}
|
|
|
fprintf(f, "\n");
|
|
|
fprintf(f, "var_bytes :");
|
|
|
// 遍历 var_bytes 数组,写入非0的值。
|
|
|
for (i = 0; i < afl->fsrv.real_map_size; i++) {
|
|
|
if (afl->var_bytes[i]) { fprintf(f, " %u", i); }
|
|
|
}
|
|
|
fprintf(f, "\n");
|
|
|
}
|
|
|
|
|
|
// 关闭文件。
|
|
|
fclose(f);
|
|
|
// 将临时文件重命名为最终文件。
|
|
|
rename(fn_tmp, fn_final);
|
|
|
}
|
|
|
|
|
|
#ifdef INTROSPECTION
|
|
|
// 函数用于写入队列的统计信息。
|
|
|
void write_queue_stats(afl_state_t * afl) {
|
|
|
FILE *f;
|
|
|
// 构造队列数据文件的路径。
|
|
|
u8 *fn = alloc_printf("%s/queue_data", afl->out_dir);
|
|
|
// 尝试打开文件。
|
|
|
if ((f = fopen(fn, "w")) != NULL) {
|
|
|
u32 id;
|
|
|
// 写入列标题。
|
|
|
fprintf(
|
|
|
f,
|
|
|
"# filename, length, exec_us, selected, skipped, mutations, finds, "
|
|
|
"crashes, timeouts, bitmap_size, perf_score, weight, colorized, "
|
|
|
"favored, disabled\n");
|
|
|
// 遍历队列中的每个条目。
|
|
|
for (id = 0; id < afl->queued_items; ++id) {
|
|
|
struct queue_entry *q = afl->queue_buf[id];
|
|
|
// 写入队列条目的统计信息。
|
|
|
fprintf(f, "\"%s\",%u,%llu,%u,%u,%llu,%u,%u,%u,%u,%.3f,%.3f,%u,%u,%u\n",
|
|
|
q->fname, q->len, q->exec_us, q->stats_selected,
|
|
|
q->stats_skipped, q->stats_mutated, q->stats_finds,
|
|
|
q->stats_crashes, q->stats_tmouts, q->bitmap_size,
|
|
|
q->perf_score, q->weight, q->colorized, q->favored,
|
|
|
q->disabled);
|
|
|
}
|
|
|
|
|
|
// 关闭文件。
|
|
|
fclose(f);
|
|
|
}
|
|
|
|
|
|
// 释放文件名字符串的内存。
|
|
|
ck_free(fn);
|
|
|
}
|
|
|
#endif
|
|
|
|
|
|
/* Update the plot file if there is a reason to. */
|
|
|
|
|
|
// 函数用于根据条件可能更新绘图文件。
|
|
|
void maybe_update_plot_file(afl_state_t * afl, u32 t_bytes, double bitmap_cvg,
|
|
|
double eps) {
|
|
|
// 如果不需要更新UI,或者程序即将停止,或者统计数据没有变化,或者运行时间小于60秒,则不更新绘图文件。
|
|
|
if (unlikely(!afl->force_ui_update &&
|
|
|
(afl->stop_soon ||
|
|
|
(afl->plot_prev_qp == afl->queued_items &&
|
|
|
afl->plot_prev_pf == afl->pending_favored &&
|
|
|
afl->plot_prev_pnf == afl->pending_not_fuzzed &&
|
|
|
afl->plot_prev_ce == afl->current_entry &&
|
|
|
afl->plot_prev_qc == afl->queue_cycle &&
|
|
|
afl->plot_prev_uc == afl->saved_crashes &&
|
|
|
afl->plot_prev_uh == afl->saved_hangs &&
|
|
|
afl->plot_prev_md == afl->max_depth &&
|
|
|
afl->plot_prev_ed == afl->fsrv.total_execs) ||
|
|
|
!afl->queue_cycle ||
|
|
|
get_cur_time() - afl->start_time <= 60000))) {
|
|
|
return;
|
|
|
}
|
|
|
|
|
|
// 更新绘图文件中的上一次统计数据。
|
|
|
afl->plot_prev_qp = afl->queued_items;
|
|
|
afl->plot_prev_pf = afl->pending_favored;
|
|
|
afl->plot_prev_pnf = afl->pending_not_fuzzed;
|
|
|
afl->plot_prev_ce = afl->current_entry;
|
|
|
afl->plot_prev_qc = afl->queue_cycle;
|
|
|
afl->plot_prev_uc = afl->saved_crashes;
|
|
|
afl->plot_prev_uh = afl->saved_hangs;
|
|
|
afl->plot_prev_md = afl->max_depth;
|
|
|
afl->plot_prev_ed = afl->fsrv.total_execs;
|
|
|
|
|
|
/* 在绘图文件中记录以下字段:
|
|
|
|
|
|
relative_time, cycles_done, cur_item, corpus_count, corpus_not_fuzzed,
|
|
|
favored_not_fuzzed, saved_crashes, saved_hangs, max_depth,
|
|
|
execs_per_sec, edges_found */
|
|
|
|
|
|
// 写入统计数据到绘图文件。
|
|
|
fprintf(afl->fsrv.plot_file,
|
|
|
"%llu, %llu, %u, %u, %u, %u, %0.02f%%, %llu, %llu, %u, %0.02f, "
|
|
|
"%llu, %u\n",
|
|
|
((afl->prev_run_time + get_cur_time() - afl->start_time) / 1000),
|
|
|
afl->queue_cycle - 1, afl->current_entry, afl->queued_items,
|
|
|
afl->pending_not_fuzzed, afl->pending_favored, bitmap_cvg,
|
|
|
afl->saved_crashes, afl->saved_hangs, afl->max_depth, eps,
|
|
|
afl->plot_prev_ed, t_bytes); /* ignore errors */
|
|
|
|
|
|
// 清空输出缓冲区,确保数据写入文件。
|
|
|
fflush(afl->fsrv.plot_file);
|
|
|
}
|
|
|
|
|
|
/* 记录确定性阶段的效率 */
|
|
|
|
|
|
// 函数用于记录确定性阶段的效率数据。
|
|
|
void plot_profile_data(afl_state_t * afl, struct queue_entry * q) {
|
|
|
// 获取当前时间(毫秒)。
|
|
|
u64 current_ms = get_cur_time() - afl->start_time;
|
|
|
|
|
|
// 计算当前的边数(非255字节)。
|
|
|
u32 current_edges = count_non_255_bytes(afl, afl->virgin_bits);
|
|
|
// 计算确定性发现率。
|
|
|
double det_finding_rate = (double)afl->havoc_prof->total_det_edge * 100.0 /
|
|
|
(double)current_edges,
|
|
|
det_time_rate = (double)afl->havoc_prof->total_det_time * 100.0 /
|
|
|
(double)current_ms;
|
|
|
|
|
|
// 初始化未确定的位数量。
|
|
|
u32 ndet_bits = 0;
|
|
|
// 遍历 skipdet_g->virgin_det_bits 数组,计算未确定的位数量。
|
|
|
for (u32 i = 0; i < afl->fsrv.map_size; i++) {
|
|
|
if (afl->skipdet_g->virgin_det_bits[i]) ndet_bits += 1;
|
|
|
}
|
|
|
|
|
|
// 计算已确定的模糊测试率。
|
|
|
double det_fuzzed_rate = (double)ndet_bits * 100.0 / (double)current_edges;
|
|
|
|
|
|
// 写入确定性阶段的效率数据到文件。
|
|
|
fprintf(afl->fsrv.det_plot_file,
|
|
|
"[%02lld:%02lld:%02lld] fuzz %d (%d), find %d/%d among %d(%02.2f) "
|
|
|
"and spend %lld/%lld(%02.2f), cover %02.2f yet, %d/%d undet bits, "
|
|
|
"continue %d.\n",
|
|
|
current_ms / 1000 / 3600, (current_ms / 1000 / 60) % 60,
|
|
|
(current_ms / 1000) % 60, afl->current_entry, q->fuzz_level,
|
|
|
afl->havoc_prof->edge_det_stage, afl->havoc_prof->edge_havoc_stage,
|
|
|
current_edges, det_finding_rate,
|
|
|
afl->havoc_prof->det_stage_time / 1000,
|
|
|
afl->havoc_prof->havoc_stage_time / 1000, det_time_rate,
|
|
|
det_fuzzed_rate, q->skipdet_e->undet_bits,
|
|
|
afl->skipdet_g->undet_bits_threshold, q->skipdet_e->continue_inf);
|
|
|
|
|
|
// 清空输出缓冲区,确保数据写入文件。
|
|
|
fflush(afl->fsrv.det_plot_file);
|
|
|
}
|
|
|
|
|
|
/* Check terminal dimensions after resize. */
|
|
|
|
|
|
static void check_term_size(afl_state_t *afl) {
|
|
|
|
|
|
struct winsize ws;
|
|
|
|
|
|
afl->term_too_small = 0;
|
|
|
|
|
|
if (ioctl(1, TIOCGWINSZ, &ws)) { return; }
|
|
|
|
|
|
if (ws.ws_row == 0 || ws.ws_col == 0) { return; }
|
|
|
if (ws.ws_row < 24 || ws.ws_col < 79) { afl->term_too_small = 1; }
|
|
|
|
|
|
}
|
|
|
|
|
|
/* A spiffy retro stats screen! This is called every afl->stats_update_freq
|
|
|
execve() calls, plus in several other circumstances. */
|
|
|
|
|
|
void show_stats(afl_state_t *afl) {
|
|
|
|
|
|
if (afl->pizza_is_served) {
|
|
|
|
|
|
show_stats_pizza(afl);
|
|
|
|
|
|
} else {
|
|
|
|
|
|
show_stats_normal(afl);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
void show_stats_normal(afl_state_t *afl) {
|
|
|
|
|
|
double t_byte_ratio, stab_ratio;
|
|
|
|
|
|
u64 cur_ms;
|
|
|
u32 t_bytes, t_bits;
|
|
|
|
|
|
static u8 banner[128];
|
|
|
u32 banner_len, banner_pad;
|
|
|
u8 tmp[256];
|
|
|
u8 time_tmp[64];
|
|
|
|
|
|
u8 val_buf[8][STRINGIFY_VAL_SIZE_MAX];
|
|
|
#define IB(i) (val_buf[(i)])
|
|
|
|
|
|
cur_ms = get_cur_time();
|
|
|
|
|
|
if (afl->most_time_key && afl->queue_cycle) {
|
|
|
|
|
|
if (afl->most_time * 1000 + afl->sync_time_us / 1000 <
|
|
|
cur_ms - afl->start_time) {
|
|
|
|
|
|
afl->most_time_key = 2;
|
|
|
afl->stop_soon = 2;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
if (afl->most_execs_key == 1 && afl->queue_cycle) {
|
|
|
|
|
|
if (afl->most_execs <= afl->fsrv.total_execs) {
|
|
|
|
|
|
afl->most_execs_key = 2;
|
|
|
afl->stop_soon = 2;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
/* If not enough time has passed since last UI update, bail out. */
|
|
|
|
|
|
if (cur_ms - afl->stats_last_ms < 1000 / UI_TARGET_HZ &&
|
|
|
!afl->force_ui_update) {
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
/* Check if we're past the 10 minute mark. */
|
|
|
|
|
|
if (cur_ms - afl->start_time > 10 * 60 * 1000) { afl->run_over10m = 1; }
|
|
|
|
|
|
/* Calculate smoothed exec speed stats. */
|
|
|
|
|
|
if (unlikely(!afl->stats_last_execs)) {
|
|
|
|
|
|
if (likely(cur_ms != afl->start_time)) {
|
|
|
|
|
|
afl->stats_avg_exec = ((double)afl->fsrv.total_execs) * 1000 /
|
|
|
(afl->prev_run_time + cur_ms - afl->start_time);
|
|
|
|
|
|
}
|
|
|
|
|
|
} else {
|
|
|
|
|
|
if (likely(cur_ms != afl->stats_last_ms)) {
|
|
|
|
|
|
double cur_avg =
|
|
|
((double)(afl->fsrv.total_execs - afl->stats_last_execs)) * 1000 /
|
|
|
(cur_ms - afl->stats_last_ms);
|
|
|
|
|
|
/* If there is a dramatic (5x+) jump in speed, reset the indicator
|
|
|
more quickly. */
|
|
|
|
|
|
if (cur_avg * 5 < afl->stats_avg_exec ||
|
|
|
cur_avg / 5 > afl->stats_avg_exec) {
|
|
|
|
|
|
afl->stats_avg_exec = cur_avg;
|
|
|
|
|
|
}
|
|
|
|
|
|
afl->stats_avg_exec = afl->stats_avg_exec * (1.0 - 1.0 / AVG_SMOOTHING) +
|
|
|
cur_avg * (1.0 / AVG_SMOOTHING);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
afl->stats_last_ms = cur_ms;
|
|
|
afl->stats_last_execs = afl->fsrv.total_execs;
|
|
|
|
|
|
/* Tell the callers when to contact us (as measured in execs). */
|
|
|
|
|
|
afl->stats_update_freq = afl->stats_avg_exec / (UI_TARGET_HZ * 10);
|
|
|
if (!afl->stats_update_freq) { afl->stats_update_freq = 1; }
|
|
|
|
|
|
/* Do some bitmap stats. */
|
|
|
|
|
|
t_bytes = count_non_255_bytes(afl, afl->virgin_bits);
|
|
|
t_byte_ratio = ((double)t_bytes * 100) / afl->fsrv.real_map_size;
|
|
|
|
|
|
if (unlikely(t_bytes > afl->fsrv.real_map_size)) {
|
|
|
|
|
|
if (unlikely(!afl->afl_env.afl_ignore_problems)) {
|
|
|
|
|
|
FATAL(
|
|
|
"Incorrect fuzzing setup detected. Your target seems to have loaded "
|
|
|
"incorrectly instrumented shared libraries (%u of %u/%u). If you use "
|
|
|
"LTO mode "
|
|
|
"please see instrumentation/README.lto.md. To ignore this problem "
|
|
|
"and continue fuzzing just set 'AFL_IGNORE_PROBLEMS=1'.\n",
|
|
|
t_bytes, afl->fsrv.real_map_size, afl->fsrv.map_size);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
if (likely(t_bytes) && unlikely(afl->var_byte_count)) {
|
|
|
|
|
|
stab_ratio = 100 - (((double)afl->var_byte_count * 100) / t_bytes);
|
|
|
|
|
|
} else {
|
|
|
|
|
|
stab_ratio = 100;
|
|
|
|
|
|
}
|
|
|
|
|
|
/* Roughly every minute, update fuzzer stats and save auto tokens. */
|
|
|
|
|
|
if (unlikely(
|
|
|
!afl->non_instrumented_mode &&
|
|
|
(afl->force_ui_update || cur_ms - afl->stats_last_stats_ms >
|
|
|
afl->stats_file_update_freq_msecs))) {
|
|
|
|
|
|
afl->stats_last_stats_ms = cur_ms;
|
|
|
write_stats_file(afl, t_bytes, t_byte_ratio, stab_ratio,
|
|
|
afl->stats_avg_exec);
|
|
|
save_auto(afl);
|
|
|
write_bitmap(afl);
|
|
|
|
|
|
}
|
|
|
|
|
|
if (unlikely(afl->afl_env.afl_statsd)) {
|
|
|
|
|
|
if (unlikely(afl->force_ui_update || cur_ms - afl->statsd_last_send_ms >
|
|
|
STATSD_UPDATE_SEC * 1000)) {
|
|
|
|
|
|
/* reset counter, even if send failed. */
|
|
|
afl->statsd_last_send_ms = cur_ms;
|
|
|
if (statsd_send_metric(afl)) { WARNF("could not send statsd metric."); }
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
/* Every now and then, write plot data. */
|
|
|
|
|
|
if (unlikely(afl->force_ui_update ||
|
|
|
cur_ms - afl->stats_last_plot_ms > PLOT_UPDATE_SEC * 1000)) {
|
|
|
|
|
|
afl->stats_last_plot_ms = cur_ms;
|
|
|
maybe_update_plot_file(afl, t_bytes, t_byte_ratio, afl->stats_avg_exec);
|
|
|
|
|
|
}
|
|
|
|
|
|
/* Every now and then, write queue data. */
|
|
|
|
|
|
if (unlikely(afl->force_ui_update ||
|
|
|
cur_ms - afl->stats_last_queue_ms > QUEUE_UPDATE_SEC * 1000)) {
|
|
|
|
|
|
afl->stats_last_queue_ms = cur_ms;
|
|
|
#ifdef INTROSPECTION
|
|
|
write_queue_stats(afl);
|
|
|
#endif
|
|
|
|
|
|
}
|
|
|
|
|
|
/* Honor AFL_EXIT_WHEN_DONE and AFL_BENCH_UNTIL_CRASH. */
|
|
|
|
|
|
if (unlikely(!afl->non_instrumented_mode && afl->cycles_wo_finds > 100 &&
|
|
|
!afl->pending_not_fuzzed && afl->afl_env.afl_exit_when_done)) {
|
|
|
|
|
|
afl->stop_soon = 2;
|
|
|
|
|
|
}
|
|
|
|
|
|
/* AFL_EXIT_ON_TIME. */
|
|
|
|
|
|
/* If no coverage was found yet, check whether run time is greater than
|
|
|
* exit_on_time. */
|
|
|
|
|
|
if (unlikely(!afl->non_instrumented_mode && afl->afl_env.afl_exit_on_time &&
|
|
|
((afl->last_find_time &&
|
|
|
(cur_ms - afl->last_find_time) > afl->exit_on_time) ||
|
|
|
(!afl->last_find_time &&
|
|
|
(cur_ms - afl->start_time) > afl->exit_on_time)))) {
|
|
|
|
|
|
afl->stop_soon = 2;
|
|
|
|
|
|
}
|
|
|
|
|
|
if (unlikely(afl->total_crashes && afl->afl_env.afl_bench_until_crash)) {
|
|
|
|
|
|
afl->stop_soon = 2;
|
|
|
|
|
|
}
|
|
|
|
|
|
/* If we're not on TTY, bail out. */
|
|
|
|
|
|
if (afl->not_on_tty) { return; }
|
|
|
|
|
|
/* If we haven't started doing things, bail out. */
|
|
|
|
|
|
if (unlikely(!afl->queue_cur)) { return; }
|
|
|
|
|
|
/* Compute some mildly useful bitmap stats. */
|
|
|
|
|
|
t_bits = (afl->fsrv.map_size << 3) - count_bits(afl, afl->virgin_bits);
|
|
|
|
|
|
/* Now, for the visuals... */
|
|
|
|
|
|
if (afl->clear_screen) {
|
|
|
|
|
|
SAYF(TERM_CLEAR CURSOR_HIDE);
|
|
|
afl->clear_screen = 0;
|
|
|
|
|
|
check_term_size(afl);
|
|
|
|
|
|
}
|
|
|
|
|
|
SAYF(TERM_HOME);
|
|
|
|
|
|
if (unlikely(afl->term_too_small)) {
|
|
|
|
|
|
SAYF(cBRI
|
|
|
"Your terminal is too small to display the UI.\n"
|
|
|
"Please resize terminal window to at least 79x24.\n" cRST);
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
/* Let's start by drawing a centered banner. */
|
|
|
if (unlikely(!banner[0])) {
|
|
|
|
|
|
char *si = "";
|
|
|
char *fuzzer_name;
|
|
|
|
|
|
if (afl->sync_id) { si = afl->sync_id; }
|
|
|
memset(banner, 0, sizeof(banner));
|
|
|
|
|
|
banner_len = strlen(VERSION) + strlen(si) + strlen(afl->power_name) + 4 + 6;
|
|
|
|
|
|
if (afl->crash_mode) {
|
|
|
|
|
|
fuzzer_name = "peruvian were-rabbit";
|
|
|
|
|
|
} else {
|
|
|
|
|
|
fuzzer_name = "american fuzzy lop";
|
|
|
if (banner_len + strlen(fuzzer_name) + strlen(afl->use_banner) > 75) {
|
|
|
|
|
|
fuzzer_name = "AFL";
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
banner_len += strlen(fuzzer_name);
|
|
|
|
|
|
if (strlen(afl->use_banner) + banner_len > 75) {
|
|
|
|
|
|
afl->use_banner += (strlen(afl->use_banner) + banner_len) - 76;
|
|
|
memset(afl->use_banner, '.', 3);
|
|
|
|
|
|
}
|
|
|
|
|
|
banner_len += strlen(afl->use_banner);
|
|
|
banner_pad = (79 - banner_len) / 2;
|
|
|
memset(banner, ' ', banner_pad);
|
|
|
|
|
|
#ifdef __linux__
|
|
|
if (afl->fsrv.nyx_mode) {
|
|
|
|
|
|
snprintf(banner + banner_pad, sizeof(banner) - banner_pad,
|
|
|
"%s%s " cLCY VERSION cLBL " {%s} " cLGN "(%s) " cPIN
|
|
|
"[%s] - Nyx",
|
|
|
afl->crash_mode ? cPIN : cYEL, fuzzer_name, si, afl->use_banner,
|
|
|
afl->power_name);
|
|
|
|
|
|
} else {
|
|
|
|
|
|
#endif
|
|
|
snprintf(banner + banner_pad, sizeof(banner) - banner_pad,
|
|
|
"%s%s " cLCY VERSION cLBL " {%s} " cLGN "(%s) " cPIN "[%s]",
|
|
|
afl->crash_mode ? cPIN : cYEL, fuzzer_name, si, afl->use_banner,
|
|
|
afl->power_name);
|
|
|
|
|
|
#ifdef __linux__
|
|
|
|
|
|
}
|
|
|
|
|
|
#endif
|
|
|
|
|
|
if (banner_pad)
|
|
|
for (u32 i = 0; i < banner_pad; ++i)
|
|
|
strcat(banner, " ");
|
|
|
|
|
|
}
|
|
|
|
|
|
SAYF("\n%s\n", banner);
|
|
|
|
|
|
/* "Handy" shortcuts for drawing boxes... */
|
|
|
|
|
|
#define bSTG bSTART cGRA
|
|
|
#define bH2 bH bH
|
|
|
#define bH5 bH2 bH2 bH
|
|
|
#define bH10 bH5 bH5
|
|
|
#define bH20 bH10 bH10
|
|
|
#define bH30 bH20 bH10
|
|
|
#define SP5 " "
|
|
|
#define SP10 SP5 SP5
|
|
|
#define SP20 SP10 SP10
|
|
|
|
|
|
/* Since `total_crashes` does not get reloaded from disk on restart,
|
|
|
it indicates if we found crashes this round already -> paint red.
|
|
|
If it's 0, but `saved_crashes` is set from a past run, paint in yellow. */
|
|
|
char *crash_color = afl->total_crashes ? cLRD
|
|
|
: afl->saved_crashes ? cYEL
|
|
|
: cRST;
|
|
|
|
|
|
/* Lord, forgive me this. */
|
|
|
|
|
|
SAYF(SET_G1 bSTG bLT bH bSTOP cCYA
|
|
|
" process timing " bSTG bH30 bH5 bH bHB bH bSTOP cCYA
|
|
|
" overall results " bSTG bH2 bH2 bRT "\n");
|
|
|
|
|
|
if (afl->non_instrumented_mode) {
|
|
|
|
|
|
strcpy(tmp, cRST);
|
|
|
|
|
|
} else {
|
|
|
|
|
|
u64 min_wo_finds = (cur_ms - afl->last_find_time) / 1000 / 60;
|
|
|
|
|
|
/* First queue cycle: don't stop now! */
|
|
|
if (afl->queue_cycle == 1 || min_wo_finds < 15) {
|
|
|
|
|
|
strcpy(tmp, cMGN);
|
|
|
|
|
|
} else
|
|
|
|
|
|
/* Subsequent cycles, but we're still making finds. */
|
|
|
if (afl->cycles_wo_finds < 25 || min_wo_finds < 30) {
|
|
|
|
|
|
strcpy(tmp, cYEL);
|
|
|
|
|
|
} else
|
|
|
|
|
|
/* No finds for a long time and no test cases to try. */
|
|
|
if (afl->cycles_wo_finds > 100 && !afl->pending_not_fuzzed &&
|
|
|
min_wo_finds > 120) {
|
|
|
|
|
|
strcpy(tmp, cLGN);
|
|
|
|
|
|
/* Default: cautiously OK to stop? */
|
|
|
|
|
|
} else {
|
|
|
|
|
|
strcpy(tmp, cLBL);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
u_stringify_time_diff(time_tmp, afl->prev_run_time + cur_ms, afl->start_time);
|
|
|
SAYF(bV bSTOP " run time : " cRST "%-33s " bSTG bV bSTOP
|
|
|
" cycles done : %s%-5s " bSTG bV "\n",
|
|
|
time_tmp, tmp, u_stringify_int(IB(0), afl->queue_cycle - 1));
|
|
|
|
|
|
/* We want to warn people about not seeing new paths after a full cycle,
|
|
|
except when resuming fuzzing or running in non-instrumented mode. */
|
|
|
|
|
|
if (!afl->non_instrumented_mode &&
|
|
|
(afl->last_find_time || afl->resuming_fuzz || afl->queue_cycle == 1 ||
|
|
|
afl->in_bitmap || afl->crash_mode)) {
|
|
|
|
|
|
u_stringify_time_diff(time_tmp, cur_ms, afl->last_find_time);
|
|
|
SAYF(bV bSTOP " last new find : " cRST "%-33s ", time_tmp);
|
|
|
|
|
|
} else {
|
|
|
|
|
|
if (afl->non_instrumented_mode) {
|
|
|
|
|
|
SAYF(bV bSTOP " last new find : " cPIN "n/a" cRST
|
|
|
" (non-instrumented mode) ");
|
|
|
|
|
|
} else {
|
|
|
|
|
|
SAYF(bV bSTOP " last new find : " cRST "none yet " cLRD
|
|
|
"(odd, check syntax!) ");
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
SAYF(bSTG bV bSTOP " corpus count : " cRST "%-5s " bSTG bV "\n",
|
|
|
u_stringify_int(IB(0), afl->queued_items));
|
|
|
|
|
|
/* Highlight crashes in red if found, denote going over the KEEP_UNIQUE_CRASH
|
|
|
limit with a '+' appended to the count. */
|
|
|
|
|
|
sprintf(tmp, "%s%s", u_stringify_int(IB(0), afl->saved_crashes),
|
|
|
(afl->saved_crashes >= KEEP_UNIQUE_CRASH) ? "+" : "");
|
|
|
|
|
|
u_stringify_time_diff(time_tmp, cur_ms, afl->last_crash_time);
|
|
|
SAYF(bV bSTOP "last saved crash : " cRST "%-33s " bSTG bV bSTOP
|
|
|
"saved crashes : %s%-6s" bSTG bV "\n",
|
|
|
time_tmp, crash_color, tmp);
|
|
|
|
|
|
sprintf(tmp, "%s%s", u_stringify_int(IB(0), afl->saved_hangs),
|
|
|
(afl->saved_hangs >= KEEP_UNIQUE_HANG) ? "+" : "");
|
|
|
|
|
|
u_stringify_time_diff(time_tmp, cur_ms, afl->last_hang_time);
|
|
|
SAYF(bV bSTOP " last saved hang : " cRST "%-33s " bSTG bV bSTOP
|
|
|
" saved hangs : " cRST "%-6s" bSTG bV "\n",
|
|
|
time_tmp, tmp);
|
|
|
|
|
|
SAYF(bVR bH bSTOP cCYA
|
|
|
" cycle progress " bSTG bH10 bH5 bH2 bH2 bH2 bHB bH bSTOP cCYA
|
|
|
" map coverage" bSTG bHT bH20 bH2 bVL "\n");
|
|
|
|
|
|
/* This gets funny because we want to print several variable-length variables
|
|
|
together, but then cram them into a fixed-width field - so we need to
|
|
|
put them in a temporary buffer first. */
|
|
|
|
|
|
sprintf(tmp, "%s%s%u (%0.01f%%)", u_stringify_int(IB(0), afl->current_entry),
|
|
|
afl->queue_cur->favored ? "." : "*", afl->queue_cur->fuzz_level,
|
|
|
((double)afl->current_entry * 100) / afl->queued_items);
|
|
|
|
|
|
SAYF(bV bSTOP " now processing : " cRST "%-18s " bSTG bV bSTOP, tmp);
|
|
|
|
|
|
sprintf(tmp, "%0.02f%% / %0.02f%%",
|
|
|
((double)afl->queue_cur->bitmap_size) * 100 / afl->fsrv.real_map_size,
|
|
|
t_byte_ratio);
|
|
|
|
|
|
SAYF(" map density : %s%-19s" bSTG bV "\n",
|
|
|
t_byte_ratio > 70
|
|
|
? cLRD
|
|
|
: ((t_bytes < 200 && !afl->non_instrumented_mode) ? cPIN : cRST),
|
|
|
tmp);
|
|
|
|
|
|
sprintf(tmp, "%s (%0.02f%%)", u_stringify_int(IB(0), afl->cur_skipped_items),
|
|
|
((double)afl->cur_skipped_items * 100) / afl->queued_items);
|
|
|
|
|
|
SAYF(bV bSTOP " runs timed out : " cRST "%-18s " bSTG bV, tmp);
|
|
|
|
|
|
sprintf(tmp, "%0.02f bits/tuple", t_bytes ? (((double)t_bits) / t_bytes) : 0);
|
|
|
|
|
|
SAYF(bSTOP " count coverage : " cRST "%-19s" bSTG bV "\n", tmp);
|
|
|
|
|
|
SAYF(bVR bH bSTOP cCYA
|
|
|
" stage progress " bSTG bH10 bH5 bH2 bH2 bH2 bX bH bSTOP cCYA
|
|
|
" findings in depth " bSTG bH10 bH5 bH2 bVL "\n");
|
|
|
|
|
|
sprintf(tmp, "%s (%0.02f%%)", u_stringify_int(IB(0), afl->queued_favored),
|
|
|
((double)afl->queued_favored) * 100 / afl->queued_items);
|
|
|
|
|
|
/* Yeah... it's still going on... halp? */
|
|
|
|
|
|
SAYF(bV bSTOP " now trying : " cRST "%-22s " bSTG bV bSTOP
|
|
|
" favored items : " cRST "%-20s" bSTG bV "\n",
|
|
|
afl->stage_name, tmp);
|
|
|
|
|
|
if (!afl->stage_max) {
|
|
|
|
|
|
sprintf(tmp, "%s/-", u_stringify_int(IB(0), afl->stage_cur));
|
|
|
|
|
|
} else {
|
|
|
|
|
|
sprintf(tmp, "%s/%s (%0.02f%%)", u_stringify_int(IB(0), afl->stage_cur),
|
|
|
u_stringify_int(IB(1), afl->stage_max),
|
|
|
((double)afl->stage_cur) * 100 / afl->stage_max);
|
|
|
|
|
|
}
|
|
|
|
|
|
SAYF(bV bSTOP " stage execs : " cRST "%-23s" bSTG bV bSTOP, tmp);
|
|
|
|
|
|
sprintf(tmp, "%s (%0.02f%%)", u_stringify_int(IB(0), afl->queued_with_cov),
|
|
|
((double)afl->queued_with_cov) * 100 / afl->queued_items);
|
|
|
|
|
|
SAYF(" new edges on : " cRST "%-20s" bSTG bV "\n", tmp);
|
|
|
|
|
|
sprintf(tmp, "%s (%s%s saved)", u_stringify_int(IB(0), afl->total_crashes),
|
|
|
u_stringify_int(IB(1), afl->saved_crashes),
|
|
|
(afl->saved_crashes >= KEEP_UNIQUE_CRASH) ? "+" : "");
|
|
|
|
|
|
if (afl->crash_mode) {
|
|
|
|
|
|
SAYF(bV bSTOP " total execs : " cRST "%-22s " bSTG bV bSTOP
|
|
|
" new crashes : %s%-20s" bSTG bV "\n",
|
|
|
u_stringify_int(IB(0), afl->fsrv.total_execs), crash_color, tmp);
|
|
|
|
|
|
} else {
|
|
|
|
|
|
SAYF(bV bSTOP " total execs : " cRST "%-22s " bSTG bV bSTOP
|
|
|
" total crashes : %s%-20s" bSTG bV "\n",
|
|
|
u_stringify_int(IB(0), afl->fsrv.total_execs), crash_color, tmp);
|
|
|
|
|
|
}
|
|
|
|
|
|
/* Show a warning about slow execution. */
|
|
|
|
|
|
if (afl->stats_avg_exec < 100) {
|
|
|
|
|
|
sprintf(tmp, "%s/sec (%s)", u_stringify_float(IB(0), afl->stats_avg_exec),
|
|
|
afl->stats_avg_exec < 20 ? "zzzz..." : "slow!");
|
|
|
|
|
|
SAYF(bV bSTOP " exec speed : " cLRD "%-22s ", tmp);
|
|
|
|
|
|
} else {
|
|
|
|
|
|
sprintf(tmp, "%s/sec", u_stringify_float(IB(0), afl->stats_avg_exec));
|
|
|
SAYF(bV bSTOP " exec speed : " cRST "%-22s ", tmp);
|
|
|
|
|
|
}
|
|
|
|
|
|
sprintf(tmp, "%s (%s%s saved)", u_stringify_int(IB(0), afl->total_tmouts),
|
|
|
u_stringify_int(IB(1), afl->saved_tmouts),
|
|
|
(afl->saved_tmouts >= KEEP_UNIQUE_HANG) ? "+" : "");
|
|
|
|
|
|
SAYF(bSTG bV bSTOP " total tmouts : " cRST "%-20s" bSTG bV "\n", tmp);
|
|
|
|
|
|
/* Aaaalmost there... hold on! */
|
|
|
|
|
|
SAYF(bVR bH cCYA bSTOP " fuzzing strategy yields " bSTG bH10 bH2 bHT bH10 bH2
|
|
|
bH bHB bH bSTOP cCYA " item geometry " bSTG bH5 bH2 bVL "\n");
|
|
|
|
|
|
if (unlikely(afl->custom_only)) {
|
|
|
|
|
|
strcpy(tmp, "disabled (custom-mutator-only mode)");
|
|
|
|
|
|
} else if (likely(afl->skip_deterministic)) {
|
|
|
|
|
|
strcpy(tmp, "disabled (-z switch used)");
|
|
|
|
|
|
} else {
|
|
|
|
|
|
sprintf(tmp, "%s/%s, %s/%s, %s/%s",
|
|
|
u_stringify_int(IB(0), afl->stage_finds[STAGE_FLIP1]),
|
|
|
u_stringify_int(IB(1), afl->stage_cycles[STAGE_FLIP1]),
|
|
|
u_stringify_int(IB(2), afl->stage_finds[STAGE_FLIP2]),
|
|
|
u_stringify_int(IB(3), afl->stage_cycles[STAGE_FLIP2]),
|
|
|
u_stringify_int(IB(4), afl->stage_finds[STAGE_FLIP4]),
|
|
|
u_stringify_int(IB(5), afl->stage_cycles[STAGE_FLIP4]));
|
|
|
|
|
|
}
|
|
|
|
|
|
SAYF(bV bSTOP " bit flips : " cRST "%-36s " bSTG bV bSTOP
|
|
|
" levels : " cRST "%-10s" bSTG bV "\n",
|
|
|
tmp, u_stringify_int(IB(0), afl->max_depth));
|
|
|
|
|
|
if (unlikely(!afl->skip_deterministic)) {
|
|
|
|
|
|
sprintf(tmp, "%s/%s, %s/%s, %s/%s",
|
|
|
u_stringify_int(IB(0), afl->stage_finds[STAGE_FLIP8]),
|
|
|
u_stringify_int(IB(1), afl->stage_cycles[STAGE_FLIP8]),
|
|
|
u_stringify_int(IB(2), afl->stage_finds[STAGE_FLIP16]),
|
|
|
u_stringify_int(IB(3), afl->stage_cycles[STAGE_FLIP16]),
|
|
|
u_stringify_int(IB(4), afl->stage_finds[STAGE_FLIP32]),
|
|
|
u_stringify_int(IB(5), afl->stage_cycles[STAGE_FLIP32]));
|
|
|
|
|
|
}
|
|
|
|
|
|
SAYF(bV bSTOP " byte flips : " cRST "%-36s " bSTG bV bSTOP
|
|
|
" pending : " cRST "%-10s" bSTG bV "\n",
|
|
|
tmp, u_stringify_int(IB(0), afl->pending_not_fuzzed));
|
|
|
|
|
|
if (unlikely(!afl->skip_deterministic)) {
|
|
|
|
|
|
sprintf(tmp, "%s/%s, %s/%s, %s/%s",
|
|
|
u_stringify_int(IB(0), afl->stage_finds[STAGE_ARITH8]),
|
|
|
u_stringify_int(IB(1), afl->stage_cycles[STAGE_ARITH8]),
|
|
|
u_stringify_int(IB(2), afl->stage_finds[STAGE_ARITH16]),
|
|
|
u_stringify_int(IB(3), afl->stage_cycles[STAGE_ARITH16]),
|
|
|
u_stringify_int(IB(4), afl->stage_finds[STAGE_ARITH32]),
|
|
|
u_stringify_int(IB(5), afl->stage_cycles[STAGE_ARITH32]));
|
|
|
|
|
|
}
|
|
|
|
|
|
SAYF(bV bSTOP " arithmetics : " cRST "%-36s " bSTG bV bSTOP
|
|
|
" pend fav : " cRST "%-10s" bSTG bV "\n",
|
|
|
tmp, u_stringify_int(IB(0), afl->pending_favored));
|
|
|
|
|
|
if (unlikely(!afl->skip_deterministic)) {
|
|
|
|
|
|
sprintf(tmp, "%s/%s, %s/%s, %s/%s",
|
|
|
u_stringify_int(IB(0), afl->stage_finds[STAGE_INTEREST8]),
|
|
|
u_stringify_int(IB(1), afl->stage_cycles[STAGE_INTEREST8]),
|
|
|
u_stringify_int(IB(2), afl->stage_finds[STAGE_INTEREST16]),
|
|
|
u_stringify_int(IB(3), afl->stage_cycles[STAGE_INTEREST16]),
|
|
|
u_stringify_int(IB(4), afl->stage_finds[STAGE_INTEREST32]),
|
|
|
u_stringify_int(IB(5), afl->stage_cycles[STAGE_INTEREST32]));
|
|
|
|
|
|
}
|
|
|
|
|
|
SAYF(bV bSTOP " known ints : " cRST "%-36s " bSTG bV bSTOP
|
|
|
" own finds : " cRST "%-10s" bSTG bV "\n",
|
|
|
tmp, u_stringify_int(IB(0), afl->queued_discovered));
|
|
|
|
|
|
if (unlikely(!afl->skip_deterministic)) {
|
|
|
|
|
|
sprintf(tmp, "%s/%s, %s/%s, %s/%s, %s/%s",
|
|
|
u_stringify_int(IB(0), afl->stage_finds[STAGE_EXTRAS_UO]),
|
|
|
u_stringify_int(IB(1), afl->stage_cycles[STAGE_EXTRAS_UO]),
|
|
|
u_stringify_int(IB(2), afl->stage_finds[STAGE_EXTRAS_UI]),
|
|
|
u_stringify_int(IB(3), afl->stage_cycles[STAGE_EXTRAS_UI]),
|
|
|
u_stringify_int(IB(4), afl->stage_finds[STAGE_EXTRAS_AO]),
|
|
|
u_stringify_int(IB(5), afl->stage_cycles[STAGE_EXTRAS_AO]),
|
|
|
u_stringify_int(IB(6), afl->stage_finds[STAGE_EXTRAS_AI]),
|
|
|
u_stringify_int(IB(7), afl->stage_cycles[STAGE_EXTRAS_AI]));
|
|
|
|
|
|
} else if (unlikely(!afl->extras_cnt || afl->custom_only)) {
|
|
|
|
|
|
strcpy(tmp, "n/a");
|
|
|
|
|
|
} else {
|
|
|
|
|
|
strcpy(tmp, "havoc mode");
|
|
|
|
|
|
}
|
|
|
|
|
|
SAYF(bV bSTOP " dictionary : " cRST "%-36s " bSTG bV bSTOP
|
|
|
" imported : " cRST "%-10s" bSTG bV "\n",
|
|
|
tmp,
|
|
|
afl->sync_id ? u_stringify_int(IB(0), afl->queued_imported)
|
|
|
: (u8 *)"n/a");
|
|
|
|
|
|
sprintf(tmp, "%s/%s, %s/%s",
|
|
|
u_stringify_int(IB(0), afl->stage_finds[STAGE_HAVOC]),
|
|
|
u_stringify_int(IB(2), afl->stage_cycles[STAGE_HAVOC]),
|
|
|
u_stringify_int(IB(3), afl->stage_finds[STAGE_SPLICE]),
|
|
|
u_stringify_int(IB(4), afl->stage_cycles[STAGE_SPLICE]));
|
|
|
|
|
|
SAYF(bV bSTOP "havoc/splice : " cRST "%-36s " bSTG bV bSTOP, tmp);
|
|
|
|
|
|
if (t_bytes) {
|
|
|
|
|
|
sprintf(tmp, "%0.02f%%", stab_ratio);
|
|
|
|
|
|
} else {
|
|
|
|
|
|
strcpy(tmp, "n/a");
|
|
|
|
|
|
}
|
|
|
|
|
|
SAYF(" stability : %s%-10s" bSTG bV "\n",
|
|
|
(stab_ratio < 85 && afl->var_byte_count > 40)
|
|
|
? cLRD
|
|
|
: ((afl->queued_variable &&
|
|
|
(!afl->persistent_mode || afl->var_byte_count > 20))
|
|
|
? cMGN
|
|
|
: cRST),
|
|
|
tmp);
|
|
|
|
|
|
if (unlikely(afl->afl_env.afl_python_module)) {
|
|
|
|
|
|
sprintf(tmp, "%s/%s,",
|
|
|
u_stringify_int(IB(0), afl->stage_finds[STAGE_PYTHON]),
|
|
|
u_stringify_int(IB(1), afl->stage_cycles[STAGE_PYTHON]));
|
|
|
|
|
|
} else {
|
|
|
|
|
|
strcpy(tmp, "unused,");
|
|
|
|
|
|
}
|
|
|
|
|
|
if (unlikely(afl->afl_env.afl_custom_mutator_library)) {
|
|
|
|
|
|
strcat(tmp, " ");
|
|
|
strcat(tmp, u_stringify_int(IB(2), afl->stage_finds[STAGE_CUSTOM_MUTATOR]));
|
|
|
strcat(tmp, "/");
|
|
|
strcat(tmp,
|
|
|
u_stringify_int(IB(3), afl->stage_cycles[STAGE_CUSTOM_MUTATOR]));
|
|
|
strcat(tmp, ",");
|
|
|
|
|
|
} else {
|
|
|
|
|
|
strcat(tmp, " unused,");
|
|
|
|
|
|
}
|
|
|
|
|
|
if (unlikely(afl->shm.cmplog_mode)) {
|
|
|
|
|
|
strcat(tmp, " ");
|
|
|
strcat(tmp, u_stringify_int(IB(4), afl->stage_finds[STAGE_COLORIZATION]));
|
|
|
strcat(tmp, "/");
|
|
|
strcat(tmp, u_stringify_int(IB(5), afl->stage_cycles[STAGE_COLORIZATION]));
|
|
|
strcat(tmp, ", ");
|
|
|
strcat(tmp, u_stringify_int(IB(6), afl->stage_finds[STAGE_ITS]));
|
|
|
strcat(tmp, "/");
|
|
|
strcat(tmp, u_stringify_int(IB(7), afl->stage_cycles[STAGE_ITS]));
|
|
|
|
|
|
} else {
|
|
|
|
|
|
strcat(tmp, " unused, unused");
|
|
|
|
|
|
}
|
|
|
|
|
|
SAYF(bV bSTOP "py/custom/rq : " cRST "%-36s " bSTG bVR bH20 bH2 bH bRB "\n",
|
|
|
tmp);
|
|
|
|
|
|
if (likely(afl->disable_trim)) {
|
|
|
|
|
|
sprintf(tmp, "disabled, ");
|
|
|
|
|
|
} else if (unlikely(!afl->bytes_trim_out ||
|
|
|
|
|
|
afl->bytes_trim_in <= afl->bytes_trim_out)) {
|
|
|
|
|
|
sprintf(tmp, "n/a, ");
|
|
|
|
|
|
} else {
|
|
|
|
|
|
sprintf(tmp, "%0.02f%%/%s, ",
|
|
|
((double)(afl->bytes_trim_in - afl->bytes_trim_out)) * 100 /
|
|
|
afl->bytes_trim_in,
|
|
|
u_stringify_int(IB(0), afl->trim_execs));
|
|
|
|
|
|
}
|
|
|
|
|
|
if (likely(afl->skip_deterministic)) {
|
|
|
|
|
|
strcat(tmp, "disabled");
|
|
|
|
|
|
} else if (unlikely(!afl->blocks_eff_total ||
|
|
|
|
|
|
afl->blocks_eff_select >= afl->blocks_eff_total)) {
|
|
|
|
|
|
strcat(tmp, "n/a");
|
|
|
|
|
|
} else {
|
|
|
|
|
|
u8 tmp2[128];
|
|
|
|
|
|
sprintf(tmp2, "%0.02f%%",
|
|
|
((double)(afl->blocks_eff_total - afl->blocks_eff_select)) * 100 /
|
|
|
afl->blocks_eff_total);
|
|
|
|
|
|
strcat(tmp, tmp2);
|
|
|
|
|
|
}
|
|
|
|
|
|
// if (afl->custom_mutators_count) {
|
|
|
|
|
|
//
|
|
|
// sprintf(tmp, "%s/%s",
|
|
|
// u_stringify_int(IB(0), afl->stage_finds[STAGE_CUSTOM_MUTATOR]),
|
|
|
// u_stringify_int(IB(1), afl->stage_cycles[STAGE_CUSTOM_MUTATOR]));
|
|
|
// SAYF(bV bSTOP " custom mut. : " cRST "%-36s " bSTG bV RESET_G1, tmp);
|
|
|
//
|
|
|
//} else {
|
|
|
|
|
|
SAYF(bV bSTOP " trim/eff : " cRST "%-36s " bSTG bV RESET_G1, tmp);
|
|
|
|
|
|
//}
|
|
|
|
|
|
/* Provide some CPU utilization stats. */
|
|
|
|
|
|
if (afl->cpu_core_count) {
|
|
|
|
|
|
char *spacing = SP10, snap[24] = " " cLGN "snapshot" cRST " ";
|
|
|
|
|
|
double cur_runnable = get_runnable_processes();
|
|
|
u32 cur_utilization = cur_runnable * 100 / afl->cpu_core_count;
|
|
|
|
|
|
u8 *cpu_color = cCYA;
|
|
|
|
|
|
/* If we could still run one or more processes, use green. */
|
|
|
|
|
|
if (afl->cpu_core_count > 1 && cur_runnable + 1 <= afl->cpu_core_count) {
|
|
|
|
|
|
cpu_color = cLGN;
|
|
|
|
|
|
}
|
|
|
|
|
|
/* If we're clearly oversubscribed, use red. */
|
|
|
|
|
|
if (!afl->no_cpu_meter_red && cur_utilization >= 150) { cpu_color = cLRD; }
|
|
|
|
|
|
if (afl->fsrv.snapshot) { spacing = snap; }
|
|
|
|
|
|
#ifdef HAVE_AFFINITY
|
|
|
|
|
|
if (afl->cpu_aff >= 0) {
|
|
|
|
|
|
SAYF("%s" cGRA "[cpu%03u:%s%3u%%" cGRA "]\r" cRST, spacing,
|
|
|
MIN(afl->cpu_aff, 999), cpu_color, MIN(cur_utilization, (u32)999));
|
|
|
|
|
|
} else {
|
|
|
|
|
|
SAYF("%s" cGRA " [cpu:%s%3u%%" cGRA "]\r" cRST, spacing, cpu_color,
|
|
|
MIN(cur_utilization, (u32)999));
|
|
|
|
|
|
}
|
|
|
|
|
|
#else
|
|
|
|
|
|
SAYF("%s" cGRA " [cpu:%s%3u%%" cGRA "]\r" cRST, spacing, cpu_color,
|
|
|
MIN(cur_utilization, (u32)999));
|
|
|
|
|
|
#endif /* ^HAVE_AFFINITY */
|
|
|
|
|
|
} else {
|
|
|
|
|
|
SAYF("\r");
|
|
|
|
|
|
}
|
|
|
|
|
|
/* Last line */
|
|
|
|
|
|
SAYF(SET_G1 "\n" bSTG bLB bH cCYA bSTOP " strategy:" cPIN
|
|
|
" %s " bSTG bH10 cCYA bSTOP " state:" cPIN
|
|
|
" %s " bSTG bH2 bRB bSTOP cRST RESET_G1,
|
|
|
afl->fuzz_mode == 0 ? "explore" : "exploit", get_fuzzing_state(afl));
|
|
|
|
|
|
#undef IB
|
|
|
|
|
|
/* Hallelujah! */
|
|
|
|
|
|
fflush(0);
|
|
|
|
|
|
}
|
|
|
|
|
|
void show_stats_pizza(afl_state_t *afl) {
|
|
|
|
|
|
double t_byte_ratio, stab_ratio;
|
|
|
|
|
|
u64 cur_ms;
|
|
|
u32 t_bytes, t_bits;
|
|
|
|
|
|
static u8 banner[128];
|
|
|
u32 banner_len, banner_pad;
|
|
|
u8 tmp[256];
|
|
|
u8 time_tmp[64];
|
|
|
|
|
|
u8 val_buf[8][STRINGIFY_VAL_SIZE_MAX];
|
|
|
#define IB(i) (val_buf[(i)])
|
|
|
|
|
|
cur_ms = get_cur_time();
|
|
|
|
|
|
if (afl->most_time_key && afl->queue_cycle) {
|
|
|
|
|
|
if (afl->most_time * 1000 + afl->sync_time_us / 1000 <
|
|
|
cur_ms - afl->start_time) {
|
|
|
|
|
|
afl->most_time_key = 2;
|
|
|
afl->stop_soon = 2;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
if (afl->most_execs_key == 1 && afl->queue_cycle) {
|
|
|
|
|
|
if (afl->most_execs <= afl->fsrv.total_execs) {
|
|
|
|
|
|
afl->most_execs_key = 2;
|
|
|
afl->stop_soon = 2;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
/* If not enough time has passed since last UI update, bail out. */
|
|
|
|
|
|
if (cur_ms - afl->stats_last_ms < 1000 / UI_TARGET_HZ &&
|
|
|
!afl->force_ui_update) {
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
/* Check if we're past the 10 minute mark. */
|
|
|
|
|
|
if (cur_ms - afl->start_time > 10 * 60 * 1000) { afl->run_over10m = 1; }
|
|
|
|
|
|
/* Calculate smoothed exec speed stats. */
|
|
|
|
|
|
if (unlikely(!afl->stats_last_execs)) {
|
|
|
|
|
|
if (likely(cur_ms != afl->start_time)) {
|
|
|
|
|
|
afl->stats_avg_exec = ((double)afl->fsrv.total_execs) * 1000 /
|
|
|
(afl->prev_run_time + cur_ms - afl->start_time);
|
|
|
|
|
|
}
|
|
|
|
|
|
} else {
|
|
|
|
|
|
if (likely(cur_ms != afl->stats_last_ms)) {
|
|
|
|
|
|
double cur_avg =
|
|
|
((double)(afl->fsrv.total_execs - afl->stats_last_execs)) * 1000 /
|
|
|
(cur_ms - afl->stats_last_ms);
|
|
|
|
|
|
/* If there is a dramatic (5x+) jump in speed, reset the indicator
|
|
|
more quickly. */
|
|
|
|
|
|
if (cur_avg * 5 < afl->stats_avg_exec ||
|
|
|
cur_avg / 5 > afl->stats_avg_exec) {
|
|
|
|
|
|
afl->stats_avg_exec = cur_avg;
|
|
|
|
|
|
}
|
|
|
|
|
|
afl->stats_avg_exec = afl->stats_avg_exec * (1.0 - 1.0 / AVG_SMOOTHING) +
|
|
|
cur_avg * (1.0 / AVG_SMOOTHING);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
afl->stats_last_ms = cur_ms;
|
|
|
afl->stats_last_execs = afl->fsrv.total_execs;
|
|
|
|
|
|
/* Tell the callers when to contact us (as measured in execs). */
|
|
|
|
|
|
afl->stats_update_freq = afl->stats_avg_exec / (UI_TARGET_HZ * 10);
|
|
|
if (!afl->stats_update_freq) { afl->stats_update_freq = 1; }
|
|
|
|
|
|
/* Do some bitmap stats. */
|
|
|
|
|
|
t_bytes = count_non_255_bytes(afl, afl->virgin_bits);
|
|
|
t_byte_ratio = ((double)t_bytes * 100) / afl->fsrv.real_map_size;
|
|
|
|
|
|
if (unlikely(t_bytes > afl->fsrv.real_map_size)) {
|
|
|
|
|
|
if (unlikely(!afl->afl_env.afl_ignore_problems)) {
|
|
|
|
|
|
FATAL(
|
|
|
"This is what happens when you speak italian to the rabbit "
|
|
|
"Don't speak italian to the rabbit");
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
if (likely(t_bytes) && unlikely(afl->var_byte_count)) {
|
|
|
|
|
|
stab_ratio = 100 - (((double)afl->var_byte_count * 100) / t_bytes);
|
|
|
|
|
|
} else {
|
|
|
|
|
|
stab_ratio = 100;
|
|
|
|
|
|
}
|
|
|
|
|
|
/* Roughly every minute, update fuzzer stats and save auto tokens. */
|
|
|
|
|
|
if (unlikely(!afl->non_instrumented_mode &&
|
|
|
(afl->force_ui_update ||
|
|
|
cur_ms - afl->stats_last_stats_ms > STATS_UPDATE_SEC * 1000))) {
|
|
|
|
|
|
afl->stats_last_stats_ms = cur_ms;
|
|
|
write_stats_file(afl, t_bytes, t_byte_ratio, stab_ratio,
|
|
|
afl->stats_avg_exec);
|
|
|
save_auto(afl);
|
|
|
write_bitmap(afl);
|
|
|
|
|
|
}
|
|
|
|
|
|
if (unlikely(afl->afl_env.afl_statsd)) {
|
|
|
|
|
|
if (unlikely(afl->force_ui_update || cur_ms - afl->statsd_last_send_ms >
|
|
|
STATSD_UPDATE_SEC * 1000)) {
|
|
|
|
|
|
/* reset counter, even if send failed. */
|
|
|
afl->statsd_last_send_ms = cur_ms;
|
|
|
if (statsd_send_metric(afl)) {
|
|
|
|
|
|
WARNF("Could not order tomato sauce from statsd.");
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
/* Every now and then, write plot data. */
|
|
|
|
|
|
if (unlikely(afl->force_ui_update ||
|
|
|
cur_ms - afl->stats_last_plot_ms > PLOT_UPDATE_SEC * 1000)) {
|
|
|
|
|
|
afl->stats_last_plot_ms = cur_ms;
|
|
|
maybe_update_plot_file(afl, t_bytes, t_byte_ratio, afl->stats_avg_exec);
|
|
|
|
|
|
}
|
|
|
|
|
|
/* Every now and then, write queue data. */
|
|
|
|
|
|
if (unlikely(afl->force_ui_update ||
|
|
|
cur_ms - afl->stats_last_queue_ms > QUEUE_UPDATE_SEC * 1000)) {
|
|
|
|
|
|
afl->stats_last_queue_ms = cur_ms;
|
|
|
#ifdef INTROSPECTION
|
|
|
write_queue_stats(afl);
|
|
|
#endif
|
|
|
|
|
|
}
|
|
|
|
|
|
/* Honor AFL_EXIT_WHEN_DONE and AFL_BENCH_UNTIL_CRASH. */
|
|
|
|
|
|
if (unlikely(!afl->non_instrumented_mode && afl->cycles_wo_finds > 100 &&
|
|
|
!afl->pending_not_fuzzed && afl->afl_env.afl_exit_when_done)) {
|
|
|
|
|
|
afl->stop_soon = 2;
|
|
|
|
|
|
}
|
|
|
|
|
|
/* AFL_EXIT_ON_TIME. */
|
|
|
|
|
|
/* If no coverage was found yet, check whether run time is greater than
|
|
|
* exit_on_time. */
|
|
|
|
|
|
if (unlikely(!afl->non_instrumented_mode && afl->afl_env.afl_exit_on_time &&
|
|
|
((afl->last_find_time &&
|
|
|
(cur_ms - afl->last_find_time) > afl->exit_on_time) ||
|
|
|
(!afl->last_find_time &&
|
|
|
(cur_ms - afl->start_time) > afl->exit_on_time)))) {
|
|
|
|
|
|
afl->stop_soon = 2;
|
|
|
|
|
|
}
|
|
|
|
|
|
if (unlikely(afl->total_crashes && afl->afl_env.afl_bench_until_crash)) {
|
|
|
|
|
|
afl->stop_soon = 2;
|
|
|
|
|
|
}
|
|
|
|
|
|
/* If we're not on TTY, bail out. */
|
|
|
|
|
|
if (afl->not_on_tty) { return; }
|
|
|
|
|
|
/* If we haven't started doing things, bail out. */
|
|
|
|
|
|
if (unlikely(!afl->queue_cur)) { return; }
|
|
|
|
|
|
/* Compute some mildly useful bitmap stats. */
|
|
|
|
|
|
t_bits = (afl->fsrv.map_size << 3) - count_bits(afl, afl->virgin_bits);
|
|
|
|
|
|
/* Now, for the visuals... */
|
|
|
|
|
|
if (afl->clear_screen) {
|
|
|
|
|
|
SAYF(TERM_CLEAR CURSOR_HIDE);
|
|
|
afl->clear_screen = 0;
|
|
|
|
|
|
check_term_size(afl);
|
|
|
|
|
|
}
|
|
|
|
|
|
SAYF(TERM_HOME);
|
|
|
|
|
|
if (unlikely(afl->term_too_small)) {
|
|
|
|
|
|
SAYF(cBRI
|
|
|
"Our pizzeria can't host this many guests.\n"
|
|
|
"Please call Pizzeria Caravaggio. They have tables of at least "
|
|
|
"79x24.\n" cRST);
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
/* Let's start by drawing a centered banner. */
|
|
|
if (unlikely(!banner[0])) {
|
|
|
|
|
|
char *si = "";
|
|
|
if (afl->sync_id) { si = afl->sync_id; }
|
|
|
memset(banner, 0, sizeof(banner));
|
|
|
banner_len = (afl->crash_mode ? 20 : 18) + strlen(VERSION) + strlen(si) +
|
|
|
strlen(afl->power_name) + 4 + 6;
|
|
|
|
|
|
if (strlen(afl->use_banner) + banner_len > 75) {
|
|
|
|
|
|
afl->use_banner += (strlen(afl->use_banner) + banner_len) - 76;
|
|
|
memset(afl->use_banner, '.', 3);
|
|
|
|
|
|
}
|
|
|
|
|
|
banner_len += strlen(afl->use_banner);
|
|
|
banner_pad = (79 - banner_len) / 2;
|
|
|
memset(banner, ' ', banner_pad);
|
|
|
|
|
|
#ifdef __linux__
|
|
|
if (afl->fsrv.nyx_mode) {
|
|
|
|
|
|
snprintf(banner + banner_pad, sizeof(banner) - banner_pad,
|
|
|
"%s " cLCY VERSION cLBL " {%s} " cLGN "(%s) " cPIN "[%s] - Nyx",
|
|
|
afl->crash_mode ? cPIN
|
|
|
"Mozzarbella Pizzeria table booking system"
|
|
|
: cYEL "Mozzarbella Pizzeria management system",
|
|
|
si, afl->use_banner, afl->power_name);
|
|
|
|
|
|
} else {
|
|
|
|
|
|
#endif
|
|
|
snprintf(banner + banner_pad, sizeof(banner) - banner_pad,
|
|
|
"%s " cLCY VERSION cLBL " {%s} " cLGN "(%s) " cPIN "[%s]",
|
|
|
afl->crash_mode ? cPIN
|
|
|
"Mozzarbella Pizzeria table booking system"
|
|
|
: cYEL "Mozzarbella Pizzeria management system",
|
|
|
si, afl->use_banner, afl->power_name);
|
|
|
|
|
|
#ifdef __linux__
|
|
|
|
|
|
}
|
|
|
|
|
|
#endif
|
|
|
|
|
|
}
|
|
|
|
|
|
SAYF("\n%s\n", banner);
|
|
|
|
|
|
/* "Handy" shortcuts for drawing boxes... */
|
|
|
|
|
|
#define bSTG bSTART cGRA
|
|
|
#define bH2 bH bH
|
|
|
#define bH5 bH2 bH2 bH
|
|
|
#define bH10 bH5 bH5
|
|
|
#define bH20 bH10 bH10
|
|
|
#define bH30 bH20 bH10
|
|
|
#define SP5 " "
|
|
|
#define SP10 SP5 SP5
|
|
|
#define SP20 SP10 SP10
|
|
|
|
|
|
/* Since `total_crashes` does not get reloaded from disk on restart,
|
|
|
it indicates if we found crashes this round already -> paint red.
|
|
|
If it's 0, but `saved_crashes` is set from a past run, paint in yellow. */
|
|
|
char *crash_color = afl->total_crashes ? cLRD
|
|
|
: afl->saved_crashes ? cYEL
|
|
|
: cRST;
|
|
|
|
|
|
/* Lord, forgive me this. */
|
|
|
|
|
|
SAYF(SET_G1 bSTG bLT bH bSTOP cCYA
|
|
|
" Mozzarbella has been proudly serving pizzas since " bSTG bH20 bH bH bH
|
|
|
bHB bH bSTOP cCYA " In this time, we served " bSTG bH30 bRT "\n");
|
|
|
|
|
|
if (afl->non_instrumented_mode) {
|
|
|
|
|
|
strcpy(tmp, cRST);
|
|
|
|
|
|
} else {
|
|
|
|
|
|
u64 min_wo_finds = (cur_ms - afl->last_find_time) / 1000 / 60;
|
|
|
|
|
|
/* First queue cycle: don't stop now! */
|
|
|
if (afl->queue_cycle == 1 || min_wo_finds < 15) {
|
|
|
|
|
|
strcpy(tmp, cMGN);
|
|
|
|
|
|
} else
|
|
|
|
|
|
/* Subsequent cycles, but we're still making finds. */
|
|
|
if (afl->cycles_wo_finds < 25 || min_wo_finds < 30) {
|
|
|
|
|
|
strcpy(tmp, cYEL);
|
|
|
|
|
|
} else
|
|
|
|
|
|
/* No finds for a long time and no test cases to try. */
|
|
|
if (afl->cycles_wo_finds > 100 && !afl->pending_not_fuzzed &&
|
|
|
min_wo_finds > 120) {
|
|
|
|
|
|
strcpy(tmp, cLGN);
|
|
|
|
|
|
/* Default: cautiously OK to stop? */
|
|
|
|
|
|
} else {
|
|
|
|
|
|
strcpy(tmp, cLBL);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
u_stringify_time_diff(time_tmp, afl->prev_run_time + cur_ms, afl->start_time);
|
|
|
SAYF(bV bSTOP
|
|
|
" open time : " cRST "%-37s " bSTG bV bSTOP
|
|
|
" seasons done : %s%-5s " bSTG bV "\n",
|
|
|
time_tmp, tmp, u_stringify_int(IB(0), afl->queue_cycle - 1));
|
|
|
|
|
|
/* We want to warn people about not seeing new paths after a full cycle,
|
|
|
except when resuming fuzzing or running in non-instrumented mode. */
|
|
|
|
|
|
if (!afl->non_instrumented_mode &&
|
|
|
(afl->last_find_time || afl->resuming_fuzz || afl->queue_cycle == 1 ||
|
|
|
afl->in_bitmap || afl->crash_mode)) {
|
|
|
|
|
|
u_stringify_time_diff(time_tmp, cur_ms, afl->last_find_time);
|
|
|
SAYF(bV bSTOP " last pizza baked : " cRST "%-37s ",
|
|
|
time_tmp);
|
|
|
|
|
|
} else {
|
|
|
|
|
|
if (afl->non_instrumented_mode) {
|
|
|
|
|
|
SAYF(bV bSTOP " last pizza baked : " cPIN "n/a" cRST
|
|
|
" (non-instrumented mode) ");
|
|
|
|
|
|
} else {
|
|
|
|
|
|
SAYF(bV bSTOP " last pizza baked : " cRST
|
|
|
"none yet " cLRD
|
|
|
"(odd, check Gennarino, he might be slacking!) ");
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
SAYF(bSTG bV bSTOP " pizzas on the menu : " cRST
|
|
|
"%-5s " bSTG bV "\n",
|
|
|
u_stringify_int(IB(0), afl->queued_items));
|
|
|
|
|
|
/* Highlight crashes in red if found, denote going over the KEEP_UNIQUE_CRASH
|
|
|
limit with a '+' appended to the count. */
|
|
|
|
|
|
sprintf(tmp, "%s%s", u_stringify_int(IB(0), afl->saved_crashes),
|
|
|
(afl->saved_crashes >= KEEP_UNIQUE_CRASH) ? "+" : "");
|
|
|
|
|
|
u_stringify_time_diff(time_tmp, cur_ms, afl->last_crash_time);
|
|
|
SAYF(bV bSTOP
|
|
|
" last ordered pizza : " cRST "%-33s " bSTG bV bSTOP
|
|
|
" at table : %s%-6s " bSTG bV "\n",
|
|
|
time_tmp, crash_color, tmp);
|
|
|
|
|
|
sprintf(tmp, "%s%s", u_stringify_int(IB(0), afl->saved_hangs),
|
|
|
(afl->saved_hangs >= KEEP_UNIQUE_HANG) ? "+" : "");
|
|
|
|
|
|
u_stringify_time_diff(time_tmp, cur_ms, afl->last_hang_time);
|
|
|
SAYF(bV bSTOP
|
|
|
" last conversation with customers : " cRST "%-33s " bSTG bV bSTOP
|
|
|
" number of Peroni : " cRST "%-6s " bSTG bV
|
|
|
"\n",
|
|
|
time_tmp, tmp);
|
|
|
|
|
|
SAYF(bVR bH bSTOP cCYA
|
|
|
" Baking progress " bSTG bH30 bH20 bH5 bH bX bH bSTOP cCYA
|
|
|
" Pizzeria busyness" bSTG bH30 bH5 bH bH bVL "\n");
|
|
|
|
|
|
/* This gets funny because we want to print several variable-length variables
|
|
|
together, but then cram them into a fixed-width field - so we need to
|
|
|
put them in a temporary buffer first. */
|
|
|
|
|
|
sprintf(tmp, "%s%s%u (%0.01f%%)", u_stringify_int(IB(0), afl->current_entry),
|
|
|
afl->queue_cur->favored ? "." : "*", afl->queue_cur->fuzz_level,
|
|
|
((double)afl->current_entry * 100) / afl->queued_items);
|
|
|
|
|
|
SAYF(bV bSTOP " now baking : " cRST
|
|
|
"%-18s " bSTG bV bSTOP,
|
|
|
tmp);
|
|
|
|
|
|
sprintf(tmp, "%0.02f%% / %0.02f%%",
|
|
|
((double)afl->queue_cur->bitmap_size) * 100 / afl->fsrv.real_map_size,
|
|
|
t_byte_ratio);
|
|
|
|
|
|
SAYF(" table full : %s%-19s " bSTG bV "\n",
|
|
|
t_byte_ratio > 70
|
|
|
? cLRD
|
|
|
: ((t_bytes < 200 && !afl->non_instrumented_mode) ? cPIN : cRST),
|
|
|
tmp);
|
|
|
|
|
|
sprintf(tmp, "%s (%0.02f%%)", u_stringify_int(IB(0), afl->cur_skipped_items),
|
|
|
((double)afl->cur_skipped_items * 100) / afl->queued_items);
|
|
|
|
|
|
SAYF(bV bSTOP " burned pizzas : " cRST
|
|
|
"%-18s " bSTG bV,
|
|
|
tmp);
|
|
|
|
|
|
sprintf(tmp, "%0.02f bits/tuple", t_bytes ? (((double)t_bits) / t_bytes) : 0);
|
|
|
|
|
|
SAYF(bSTOP " count coverage : " cRST "%-19s " bSTG bV "\n",
|
|
|
tmp);
|
|
|
|
|
|
SAYF(bVR bH bSTOP cCYA
|
|
|
" Pizzas almost ready " bSTG bH30 bH20 bH2 bH bX bH bSTOP cCYA
|
|
|
" Types of pizzas cooking " bSTG bH10 bH5 bH2 bH10 bH2 bH bVL "\n");
|
|
|
|
|
|
sprintf(tmp, "%s (%0.02f%%)", u_stringify_int(IB(0), afl->queued_favored),
|
|
|
((double)afl->queued_favored) * 100 / afl->queued_items);
|
|
|
|
|
|
/* Yeah... it's still going on... halp? */
|
|
|
|
|
|
SAYF(bV bSTOP " now preparing : " cRST
|
|
|
"%-22s " bSTG bV bSTOP
|
|
|
" favourite topping : " cRST "%-20s" bSTG bV
|
|
|
"\n",
|
|
|
afl->stage_name, tmp);
|
|
|
|
|
|
if (!afl->stage_max) {
|
|
|
|
|
|
sprintf(tmp, "%s/-", u_stringify_int(IB(0), afl->stage_cur));
|
|
|
|
|
|
} else {
|
|
|
|
|
|
sprintf(tmp, "%s/%s (%0.02f%%)", u_stringify_int(IB(0), afl->stage_cur),
|
|
|
u_stringify_int(IB(1), afl->stage_max),
|
|
|
((double)afl->stage_cur) * 100 / afl->stage_max);
|
|
|
|
|
|
}
|
|
|
|
|
|
SAYF(bV bSTOP " number of pizzas : " cRST
|
|
|
"%-23s " bSTG bV bSTOP,
|
|
|
tmp);
|
|
|
|
|
|
sprintf(tmp, "%s (%0.02f%%)", u_stringify_int(IB(0), afl->queued_with_cov),
|
|
|
((double)afl->queued_with_cov) * 100 / afl->queued_items);
|
|
|
|
|
|
SAYF(" new pizza type seen on Instagram : " cRST "%-20s" bSTG bV "\n", tmp);
|
|
|
|
|
|
sprintf(tmp, "%s (%s%s saved)", u_stringify_int(IB(0), afl->total_crashes),
|
|
|
u_stringify_int(IB(1), afl->saved_crashes),
|
|
|
(afl->saved_crashes >= KEEP_UNIQUE_CRASH) ? "+" : "");
|
|
|
|
|
|
if (afl->crash_mode) {
|
|
|
|
|
|
SAYF(bV bSTOP " total pizzas : " cRST
|
|
|
"%-22s " bSTG bV bSTOP
|
|
|
" pizzas with pineapple : %s%-20s" bSTG bV "\n",
|
|
|
u_stringify_int(IB(0), afl->fsrv.total_execs), crash_color, tmp);
|
|
|
|
|
|
} else {
|
|
|
|
|
|
SAYF(bV bSTOP " total pizzas : " cRST
|
|
|
"%-22s " bSTG bV bSTOP
|
|
|
" total pizzas with pineapple : %s%-20s" bSTG bV "\n",
|
|
|
u_stringify_int(IB(0), afl->fsrv.total_execs), crash_color, tmp);
|
|
|
|
|
|
}
|
|
|
|
|
|
/* Show a warning about slow execution. */
|
|
|
|
|
|
if (afl->stats_avg_exec < 20) {
|
|
|
|
|
|
sprintf(tmp, "%s/sec (%s)", u_stringify_float(IB(0), afl->stats_avg_exec),
|
|
|
"zzzz...");
|
|
|
|
|
|
SAYF(bV bSTOP " pizza making speed : " cLRD
|
|
|
"%-22s ",
|
|
|
tmp);
|
|
|
|
|
|
} else {
|
|
|
|
|
|
sprintf(tmp, "%s/sec", u_stringify_float(IB(0), afl->stats_avg_exec));
|
|
|
SAYF(bV bSTOP " pizza making speed : " cRST
|
|
|
"%-22s ",
|
|
|
tmp);
|
|
|
|
|
|
}
|
|
|
|
|
|
sprintf(tmp, "%s (%s%s saved)", u_stringify_int(IB(0), afl->total_tmouts),
|
|
|
u_stringify_int(IB(1), afl->saved_tmouts),
|
|
|
(afl->saved_tmouts >= KEEP_UNIQUE_HANG) ? "+" : "");
|
|
|
|
|
|
SAYF(bSTG bV bSTOP " burned pizzas : " cRST "%-20s" bSTG bV
|
|
|
"\n",
|
|
|
tmp);
|
|
|
|
|
|
/* Aaaalmost there... hold on! */
|
|
|
|
|
|
SAYF(bVR bH cCYA bSTOP " Promotional campaign on TikTok yields " bSTG bH30 bH2
|
|
|
bH bH2 bX bH bSTOP cCYA
|
|
|
" Customer type " bSTG bH5 bH2 bH30 bH2 bH bVL "\n");
|
|
|
|
|
|
if (unlikely(afl->custom_only)) {
|
|
|
|
|
|
strcpy(tmp, "oven off (custom-mutator-only mode)");
|
|
|
|
|
|
} else if (likely(afl->skip_deterministic)) {
|
|
|
|
|
|
strcpy(tmp, "oven off (default, enable with -D)");
|
|
|
|
|
|
} else {
|
|
|
|
|
|
sprintf(tmp, "%s/%s, %s/%s, %s/%s",
|
|
|
u_stringify_int(IB(0), afl->stage_finds[STAGE_FLIP1]),
|
|
|
u_stringify_int(IB(1), afl->stage_cycles[STAGE_FLIP1]),
|
|
|
u_stringify_int(IB(2), afl->stage_finds[STAGE_FLIP2]),
|
|
|
u_stringify_int(IB(3), afl->stage_cycles[STAGE_FLIP2]),
|
|
|
u_stringify_int(IB(4), afl->stage_finds[STAGE_FLIP4]),
|
|
|
u_stringify_int(IB(5), afl->stage_cycles[STAGE_FLIP4]));
|
|
|
|
|
|
}
|
|
|
|
|
|
SAYF(bV bSTOP
|
|
|
" pizzas for celiac : " cRST "%-36s " bSTG bV bSTOP
|
|
|
" levels : " cRST "%-10s " bSTG bV
|
|
|
"\n",
|
|
|
tmp, u_stringify_int(IB(0), afl->max_depth));
|
|
|
|
|
|
if (unlikely(!afl->skip_deterministic)) {
|
|
|
|
|
|
sprintf(tmp, "%s/%s, %s/%s, %s/%s",
|
|
|
u_stringify_int(IB(0), afl->stage_finds[STAGE_FLIP8]),
|
|
|
u_stringify_int(IB(1), afl->stage_cycles[STAGE_FLIP8]),
|
|
|
u_stringify_int(IB(2), afl->stage_finds[STAGE_FLIP16]),
|
|
|
u_stringify_int(IB(3), afl->stage_cycles[STAGE_FLIP16]),
|
|
|
u_stringify_int(IB(4), afl->stage_finds[STAGE_FLIP32]),
|
|
|
u_stringify_int(IB(5), afl->stage_cycles[STAGE_FLIP32]));
|
|
|
|
|
|
}
|
|
|
|
|
|
SAYF(bV bSTOP
|
|
|
" pizzas for kids : " cRST "%-36s " bSTG bV bSTOP
|
|
|
" pizzas to make : " cRST "%-10s " bSTG bV
|
|
|
"\n",
|
|
|
tmp, u_stringify_int(IB(0), afl->pending_not_fuzzed));
|
|
|
|
|
|
if (unlikely(!afl->skip_deterministic)) {
|
|
|
|
|
|
sprintf(tmp, "%s/%s, %s/%s, %s/%s",
|
|
|
u_stringify_int(IB(0), afl->stage_finds[STAGE_ARITH8]),
|
|
|
u_stringify_int(IB(1), afl->stage_cycles[STAGE_ARITH8]),
|
|
|
u_stringify_int(IB(2), afl->stage_finds[STAGE_ARITH16]),
|
|
|
u_stringify_int(IB(3), afl->stage_cycles[STAGE_ARITH16]),
|
|
|
u_stringify_int(IB(4), afl->stage_finds[STAGE_ARITH32]),
|
|
|
u_stringify_int(IB(5), afl->stage_cycles[STAGE_ARITH32]));
|
|
|
|
|
|
}
|
|
|
|
|
|
SAYF(bV bSTOP
|
|
|
" pizza bianca : " cRST "%-36s " bSTG bV bSTOP
|
|
|
" nice table : " cRST "%-10s " bSTG bV
|
|
|
"\n",
|
|
|
tmp, u_stringify_int(IB(0), afl->pending_favored));
|
|
|
|
|
|
if (unlikely(!afl->skip_deterministic)) {
|
|
|
|
|
|
sprintf(tmp, "%s/%s, %s/%s, %s/%s",
|
|
|
u_stringify_int(IB(0), afl->stage_finds[STAGE_INTEREST8]),
|
|
|
u_stringify_int(IB(1), afl->stage_cycles[STAGE_INTEREST8]),
|
|
|
u_stringify_int(IB(2), afl->stage_finds[STAGE_INTEREST16]),
|
|
|
u_stringify_int(IB(3), afl->stage_cycles[STAGE_INTEREST16]),
|
|
|
u_stringify_int(IB(4), afl->stage_finds[STAGE_INTEREST32]),
|
|
|
u_stringify_int(IB(5), afl->stage_cycles[STAGE_INTEREST32]));
|
|
|
|
|
|
}
|
|
|
|
|
|
SAYF(bV bSTOP
|
|
|
" recurring customers : " cRST "%-36s " bSTG bV bSTOP
|
|
|
" new customers : " cRST "%-10s " bSTG bV
|
|
|
"\n",
|
|
|
tmp, u_stringify_int(IB(0), afl->queued_discovered));
|
|
|
|
|
|
if (unlikely(!afl->skip_deterministic)) {
|
|
|
|
|
|
sprintf(tmp, "%s/%s, %s/%s, %s/%s, %s/%s",
|
|
|
u_stringify_int(IB(0), afl->stage_finds[STAGE_EXTRAS_UO]),
|
|
|
u_stringify_int(IB(1), afl->stage_cycles[STAGE_EXTRAS_UO]),
|
|
|
u_stringify_int(IB(2), afl->stage_finds[STAGE_EXTRAS_UI]),
|
|
|
u_stringify_int(IB(3), afl->stage_cycles[STAGE_EXTRAS_UI]),
|
|
|
u_stringify_int(IB(4), afl->stage_finds[STAGE_EXTRAS_AO]),
|
|
|
u_stringify_int(IB(5), afl->stage_cycles[STAGE_EXTRAS_AO]),
|
|
|
u_stringify_int(IB(6), afl->stage_finds[STAGE_EXTRAS_AI]),
|
|
|
u_stringify_int(IB(7), afl->stage_cycles[STAGE_EXTRAS_AI]));
|
|
|
|
|
|
} else if (unlikely(!afl->extras_cnt || afl->custom_only)) {
|
|
|
|
|
|
strcpy(tmp, "n/a");
|
|
|
|
|
|
} else {
|
|
|
|
|
|
strcpy(tmp, "18 year aniversary mode");
|
|
|
|
|
|
}
|
|
|
|
|
|
SAYF(bV bSTOP
|
|
|
" dictionary : " cRST "%-36s " bSTG bV bSTOP
|
|
|
" patrons from old resturant : " cRST "%-10s " bSTG bV
|
|
|
"\n",
|
|
|
tmp,
|
|
|
afl->sync_id ? u_stringify_int(IB(0), afl->queued_imported)
|
|
|
: (u8 *)"n/a");
|
|
|
|
|
|
sprintf(tmp, "%s/%s, %s/%s",
|
|
|
u_stringify_int(IB(0), afl->stage_finds[STAGE_HAVOC]),
|
|
|
u_stringify_int(IB(2), afl->stage_cycles[STAGE_HAVOC]),
|
|
|
u_stringify_int(IB(3), afl->stage_finds[STAGE_SPLICE]),
|
|
|
u_stringify_int(IB(4), afl->stage_cycles[STAGE_SPLICE]));
|
|
|
|
|
|
SAYF(bV bSTOP " 18 year anniversary mode/cleaning : " cRST
|
|
|
"%-36s " bSTG bV bSTOP,
|
|
|
tmp);
|
|
|
|
|
|
if (t_bytes) {
|
|
|
|
|
|
sprintf(tmp, "%0.02f%%", stab_ratio);
|
|
|
|
|
|
} else {
|
|
|
|
|
|
strcpy(tmp, "n/a");
|
|
|
|
|
|
}
|
|
|
|
|
|
SAYF(" oven flameout : %s%-10s " bSTG bV "\n",
|
|
|
(stab_ratio < 85 && afl->var_byte_count > 40)
|
|
|
? cLRD
|
|
|
: ((afl->queued_variable &&
|
|
|
(!afl->persistent_mode || afl->var_byte_count > 20))
|
|
|
? cMGN
|
|
|
: cRST),
|
|
|
tmp);
|
|
|
|
|
|
if (unlikely(afl->afl_env.afl_python_module)) {
|
|
|
|
|
|
sprintf(tmp, "%s/%s,",
|
|
|
u_stringify_int(IB(0), afl->stage_finds[STAGE_PYTHON]),
|
|
|
u_stringify_int(IB(1), afl->stage_cycles[STAGE_PYTHON]));
|
|
|
|
|
|
} else {
|
|
|
|
|
|
strcpy(tmp, "unused,");
|
|
|
|
|
|
}
|
|
|
|
|
|
if (unlikely(afl->afl_env.afl_custom_mutator_library)) {
|
|
|
|
|
|
strcat(tmp, " ");
|
|
|
strcat(tmp, u_stringify_int(IB(2), afl->stage_finds[STAGE_CUSTOM_MUTATOR]));
|
|
|
strcat(tmp, "/");
|
|
|
strcat(tmp,
|
|
|
u_stringify_int(IB(3), afl->stage_cycles[STAGE_CUSTOM_MUTATOR]));
|
|
|
strcat(tmp, ",");
|
|
|
|
|
|
} else {
|
|
|
|
|
|
strcat(tmp, " unused,");
|
|
|
|
|
|
}
|
|
|
|
|
|
if (unlikely(afl->shm.cmplog_mode)) {
|
|
|
|
|
|
strcat(tmp, " ");
|
|
|
strcat(tmp, u_stringify_int(IB(4), afl->stage_finds[STAGE_COLORIZATION]));
|
|
|
strcat(tmp, "/");
|
|
|
strcat(tmp, u_stringify_int(IB(5), afl->stage_cycles[STAGE_COLORIZATION]));
|
|
|
strcat(tmp, ", ");
|
|
|
strcat(tmp, u_stringify_int(IB(6), afl->stage_finds[STAGE_ITS]));
|
|
|
strcat(tmp, "/");
|
|
|
strcat(tmp, u_stringify_int(IB(7), afl->stage_cycles[STAGE_ITS]));
|
|
|
|
|
|
} else {
|
|
|
|
|
|
strcat(tmp, " unused, unused");
|
|
|
|
|
|
}
|
|
|
|
|
|
SAYF(bV bSTOP " py/custom/rq : " cRST
|
|
|
"%-36s " bSTG bVR bH20 bH2 bH30 bH2 bH bH bRB "\n",
|
|
|
tmp);
|
|
|
|
|
|
if (likely(afl->disable_trim)) {
|
|
|
|
|
|
sprintf(tmp, "disabled, ");
|
|
|
|
|
|
} else if (unlikely(!afl->bytes_trim_out)) {
|
|
|
|
|
|
sprintf(tmp, "n/a, ");
|
|
|
|
|
|
} else {
|
|
|
|
|
|
sprintf(tmp, "%0.02f%%/%s, ",
|
|
|
((double)(afl->bytes_trim_in - afl->bytes_trim_out)) * 100 /
|
|
|
afl->bytes_trim_in,
|
|
|
u_stringify_int(IB(0), afl->trim_execs));
|
|
|
|
|
|
}
|
|
|
|
|
|
if (likely(afl->skip_deterministic)) {
|
|
|
|
|
|
strcat(tmp, "disabled");
|
|
|
|
|
|
} else if (unlikely(!afl->blocks_eff_total)) {
|
|
|
|
|
|
strcat(tmp, "n/a");
|
|
|
|
|
|
} else {
|
|
|
|
|
|
u8 tmp2[128];
|
|
|
|
|
|
sprintf(tmp2, "%0.02f%%",
|
|
|
((double)(afl->blocks_eff_total - afl->blocks_eff_select)) * 100 /
|
|
|
afl->blocks_eff_total);
|
|
|
|
|
|
strcat(tmp, tmp2);
|
|
|
|
|
|
}
|
|
|
|
|
|
// if (afl->custom_mutators_count) {
|
|
|
|
|
|
//
|
|
|
// sprintf(tmp, "%s/%s",
|
|
|
// u_stringify_int(IB(0), afl->stage_finds[STAGE_CUSTOM_MUTATOR]),
|
|
|
// u_stringify_int(IB(1), afl->stage_cycles[STAGE_CUSTOM_MUTATOR]));
|
|
|
// SAYF(bV bSTOP " custom mut. : " cRST "%-36s " bSTG bV RESET_G1, tmp);
|
|
|
//
|
|
|
//} else {
|
|
|
|
|
|
SAYF(bV bSTOP " toilets clogged : " cRST
|
|
|
"%-36s " bSTG bV RESET_G1,
|
|
|
tmp);
|
|
|
|
|
|
//}
|
|
|
|
|
|
/* Provide some CPU utilization stats. */
|
|
|
|
|
|
if (afl->cpu_core_count) {
|
|
|
|
|
|
char *spacing = SP10, snap[80] = " " cLGN "Pizzaioli's busyness " cRST " ";
|
|
|
|
|
|
double cur_runnable = get_runnable_processes();
|
|
|
u32 cur_utilization = cur_runnable * 100 / afl->cpu_core_count;
|
|
|
|
|
|
u8 *cpu_color = cCYA;
|
|
|
|
|
|
/* If we could still run one or more processes, use green. */
|
|
|
|
|
|
if (afl->cpu_core_count > 1 && cur_runnable + 1 <= afl->cpu_core_count) {
|
|
|
|
|
|
cpu_color = cLGN;
|
|
|
|
|
|
}
|
|
|
|
|
|
/* If we're clearly oversubscribed, use red. */
|
|
|
|
|
|
if (!afl->no_cpu_meter_red && cur_utilization >= 150) { cpu_color = cLRD; }
|
|
|
|
|
|
if (afl->fsrv.snapshot) { spacing = snap; }
|
|
|
|
|
|
#ifdef HAVE_AFFINITY
|
|
|
|
|
|
if (afl->cpu_aff >= 0) {
|
|
|
|
|
|
SAYF("%s" cGRA "[cpu%03u:%s%3u%%" cGRA "]\r" cRST, spacing,
|
|
|
MIN(afl->cpu_aff, 999), cpu_color, MIN(cur_utilization, (u32)999));
|
|
|
|
|
|
} else {
|
|
|
|
|
|
SAYF("%s" cGRA " [cpu:%s%3u%%" cGRA "]\r" cRST, spacing, cpu_color,
|
|
|
MIN(cur_utilization, (u32)999));
|
|
|
|
|
|
}
|
|
|
|
|
|
#else
|
|
|
|
|
|
SAYF("%s" cGRA " [cpu:%s%3u%%" cGRA "]\r" cRST, spacing, cpu_color,
|
|
|
MIN(cur_utilization, (u32)999));
|
|
|
|
|
|
#endif /* ^HAVE_AFFINITY */
|
|
|
|
|
|
} else {
|
|
|
|
|
|
SAYF("\r");
|
|
|
|
|
|
}
|
|
|
|
|
|
/* Last line */
|
|
|
SAYF(SET_G1 "\n" bSTG bLB bH30 bH20 bH2 bH20 bH2 bH bRB bSTOP cRST RESET_G1);
|
|
|
|
|
|
#undef IB
|
|
|
|
|
|
/* Hallelujah! */
|
|
|
|
|
|
fflush(0);
|
|
|
|
|
|
}
|
|
|
|
|
|
/* Display quick statistics at the end of processing the input directory,
|
|
|
plus a bunch of warnings. Some calibration stuff also ended up here,
|
|
|
along with several hardcoded constants. Maybe clean up eventually. */
|
|
|
|
|
|
void show_init_stats(afl_state_t *afl) {
|
|
|
|
|
|
struct queue_entry *q;
|
|
|
u32 min_bits = 0, max_bits = 0, max_len = 0, count = 0, i;
|
|
|
u64 min_us = 0, max_us = 0;
|
|
|
u64 avg_us = 0;
|
|
|
|
|
|
u8 val_bufs[4][STRINGIFY_VAL_SIZE_MAX];
|
|
|
#define IB(i) val_bufs[(i)], sizeof(val_bufs[(i)])
|
|
|
|
|
|
if (afl->total_cal_cycles) {
|
|
|
|
|
|
avg_us = afl->total_cal_us / afl->total_cal_cycles;
|
|
|
|
|
|
}
|
|
|
|
|
|
for (i = 0; i < afl->queued_items; i++) {
|
|
|
|
|
|
q = afl->queue_buf[i];
|
|
|
if (unlikely(q->disabled)) { continue; }
|
|
|
|
|
|
if (!min_us || q->exec_us < min_us) { min_us = q->exec_us; }
|
|
|
if (q->exec_us > max_us) { max_us = q->exec_us; }
|
|
|
|
|
|
if (!min_bits || q->bitmap_size < min_bits) { min_bits = q->bitmap_size; }
|
|
|
if (q->bitmap_size > max_bits) { max_bits = q->bitmap_size; }
|
|
|
|
|
|
if (q->len > max_len) { max_len = q->len; }
|
|
|
|
|
|
++count;
|
|
|
|
|
|
}
|
|
|
|
|
|
// SAYF("\n");
|
|
|
|
|
|
if (avg_us > ((afl->fsrv.cs_mode || afl->fsrv.qemu_mode || afl->unicorn_mode)
|
|
|
? 50000
|
|
|
: 10000)) {
|
|
|
|
|
|
WARNF(cLRD
|
|
|
"The target binary is pretty slow! See "
|
|
|
"%s/fuzzing_in_depth.md#i-improve-the-speed",
|
|
|
doc_path);
|
|
|
|
|
|
}
|
|
|
|
|
|
/* Let's keep things moving with slow binaries. */
|
|
|
|
|
|
if (unlikely(afl->fixed_seed)) {
|
|
|
|
|
|
afl->havoc_div = 1;
|
|
|
|
|
|
} else if (avg_us > 50000) {
|
|
|
|
|
|
afl->havoc_div = 10; /* 0-19 execs/sec */
|
|
|
|
|
|
} else if (avg_us > 20000) {
|
|
|
|
|
|
afl->havoc_div = 5; /* 20-49 execs/sec */
|
|
|
|
|
|
} else if (avg_us > 10000) {
|
|
|
|
|
|
afl->havoc_div = 2; /* 50-100 execs/sec */
|
|
|
|
|
|
}
|
|
|
|
|
|
if (!afl->resuming_fuzz) {
|
|
|
|
|
|
if (max_len > 50 * 1024) {
|
|
|
|
|
|
WARNF(cLRD
|
|
|
"Some test cases are huge (%s) - see "
|
|
|
"%s/fuzzing_in_depth.md#i-improve-the-speed",
|
|
|
stringify_mem_size(IB(0), max_len), doc_path);
|
|
|
|
|
|
} else if (max_len > 10 * 1024) {
|
|
|
|
|
|
WARNF(
|
|
|
"Some test cases are big (%s) - see "
|
|
|
"%s/fuzzing_in_depth.md#i-improve-the-speed",
|
|
|
stringify_mem_size(IB(0), max_len), doc_path);
|
|
|
|
|
|
}
|
|
|
|
|
|
if (afl->useless_at_start && !afl->in_bitmap) {
|
|
|
|
|
|
WARNF(cLRD "Some test cases look useless. Consider using a smaller set.");
|
|
|
|
|
|
}
|
|
|
|
|
|
if (afl->queued_items > 100) {
|
|
|
|
|
|
WARNF(cLRD
|
|
|
"You probably have far too many input files! Consider trimming "
|
|
|
"down.");
|
|
|
|
|
|
} else if (afl->queued_items > 20) {
|
|
|
|
|
|
WARNF("You have lots of input files; try starting small.");
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
OKF("Here are some useful stats:\n\n"
|
|
|
|
|
|
cGRA " Test case count : " cRST
|
|
|
"%u favored, %u variable, %u ignored, %u total\n" cGRA
|
|
|
" Bitmap range : " cRST
|
|
|
"%u to %u bits (average: %0.02f bits)\n" cGRA
|
|
|
" Exec timing : " cRST "%s to %s us (average: %s us)\n",
|
|
|
afl->queued_favored, afl->queued_variable, afl->queued_items - count,
|
|
|
afl->queued_items, min_bits, max_bits,
|
|
|
((double)afl->total_bitmap_size) /
|
|
|
(afl->total_bitmap_entries ? afl->total_bitmap_entries : 1),
|
|
|
stringify_int(IB(0), min_us), stringify_int(IB(1), max_us),
|
|
|
stringify_int(IB(2), avg_us));
|
|
|
|
|
|
if (afl->timeout_given == 3) {
|
|
|
|
|
|
ACTF("Applying timeout settings from resumed session (%u ms).",
|
|
|
afl->fsrv.exec_tmout);
|
|
|
|
|
|
} else if (afl->timeout_given != 1) {
|
|
|
|
|
|
/* Figure out the appropriate timeout. The basic idea is: 5x average or
|
|
|
1x max, rounded up to EXEC_TM_ROUND ms and capped at 1 second.
|
|
|
|
|
|
If the program is slow, the multiplier is lowered to 2x or 3x, because
|
|
|
random scheduler jitter is less likely to have any impact, and because
|
|
|
our patience is wearing thin =) */
|
|
|
|
|
|
if (unlikely(afl->fixed_seed)) {
|
|
|
|
|
|
afl->fsrv.exec_tmout = avg_us * 5 / 1000;
|
|
|
|
|
|
} else if (avg_us > 50000) {
|
|
|
|
|
|
afl->fsrv.exec_tmout = avg_us * 2 / 1000;
|
|
|
|
|
|
} else if (avg_us > 10000) {
|
|
|
|
|
|
afl->fsrv.exec_tmout = avg_us * 3 / 1000;
|
|
|
|
|
|
} else {
|
|
|
|
|
|
afl->fsrv.exec_tmout = avg_us * 5 / 1000;
|
|
|
|
|
|
}
|
|
|
|
|
|
afl->fsrv.exec_tmout = MAX(afl->fsrv.exec_tmout, max_us / 1000);
|
|
|
afl->fsrv.exec_tmout =
|
|
|
(afl->fsrv.exec_tmout + EXEC_TM_ROUND) / EXEC_TM_ROUND * EXEC_TM_ROUND;
|
|
|
|
|
|
if (afl->fsrv.exec_tmout > EXEC_TIMEOUT) {
|
|
|
|
|
|
afl->fsrv.exec_tmout = EXEC_TIMEOUT;
|
|
|
|
|
|
}
|
|
|
|
|
|
ACTF("No -t option specified, so I'll use an exec timeout of %u ms.",
|
|
|
afl->fsrv.exec_tmout);
|
|
|
|
|
|
afl->timeout_given = 1;
|
|
|
|
|
|
} else {
|
|
|
|
|
|
ACTF("-t option specified. We'll use an exec timeout of %u ms.",
|
|
|
afl->fsrv.exec_tmout);
|
|
|
|
|
|
}
|
|
|
|
|
|
/* In non-instrumented 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 (afl->non_instrumented_mode && !(afl->afl_env.afl_hang_tmout)) {
|
|
|
|
|
|
afl->hang_tmout = MIN((u32)EXEC_TIMEOUT, afl->fsrv.exec_tmout * 2 + 100);
|
|
|
|
|
|
}
|
|
|
|
|
|
OKF("All set and ready to roll!");
|
|
|
#undef IB
|
|
|
|
|
|
}
|
|
|
|
|
|
inline void update_calibration_time(afl_state_t *afl, u64 *time) {
|
|
|
|
|
|
u64 cur = get_cur_time_us();
|
|
|
afl->calibration_time_us += cur - *time;
|
|
|
*time = cur;
|
|
|
|
|
|
}
|
|
|
|
|
|
inline void update_trim_time(afl_state_t *afl, u64 *time) {
|
|
|
|
|
|
u64 cur = get_cur_time_us();
|
|
|
afl->trim_time_us += cur - *time;
|
|
|
*time = cur;
|
|
|
|
|
|
}
|
|
|
|
|
|
inline void update_sync_time(afl_state_t *afl, u64 *time) {
|
|
|
|
|
|
u64 cur = get_cur_time_us();
|
|
|
afl->sync_time_us += cur - *time;
|
|
|
*time = cur;
|
|
|
|
|
|
}
|
|
|
|
|
|
inline void update_cmplog_time(afl_state_t *afl, u64 *time) {
|
|
|
|
|
|
u64 cur = get_cur_time_us();
|
|
|
afl->cmplog_time_us += cur - *time;
|
|
|
*time = cur;
|
|
|
|
|
|
}
|
|
|
|