首页
社区
课程
招聘
[原创] AFL 源代码速通笔记
发表于: 2023-4-6 19:27 21066

[原创] AFL 源代码速通笔记

2023-4-6 19:27
21066

因为认识的师傅们都开始卷 fuzz 了,迫于生活压力,于是也开始看这方面的内容了。由于 AFL 作为一个现在仍然适用且比较经典的 fuzzer,因此笔者也打算从它开始。

本来,本篇博文叫做 《AFL 源代码阅读笔记》,结果跟着大佬们的笔记去读(sakura师傅的笔记确实是神中神,本文也有很多地方照搬了师傅的原文,因为说实话我觉得自己也写不到那么详细),囫囵吞枣般速通了,前前后后三天时间这样,但感觉自己尚且没有自己实现的能力,还是比较令人失望的(我怎么这么菜)

首先,一般我们用 afl 去 fuzz 一些项目的时候都需要用 afl-gcc 去代替 gcc 进行编译。先说结论,这一步的目的其实是为了向代码中插桩,完成插桩后其实还是调用原生的 gcc 进行编译

首先需要说明的是,gcc 对代码的编译流程的分层次的:

源代码-->预编译后的源代码-->汇编代码-->机器码-->链接后的二进制文件

其中,从源代码到汇编代码的步骤由 gcc 完成;而汇编代码到机器码的部分由 as 完成。

而 afl-gcc 的源代码如下:

挺长的,不过逻辑基本上都是重复的,主要做两件事:

在完成了汇编以后,接下来会使用 afl-as 对生成的汇编代码进行插桩:

afl-as 中,仍然使用 edit_params 编辑和修改参数,并使用 add_instrumentation 来对生成的汇编代码进行插桩。完成插桩后,用 fork 生成子进程,并调用原生的 as 进行编译。

插桩逻辑也很朴素:

简单来说就是一个循环读取每行汇编代码,并对特定的汇编代码进行插桩:

插桩的具体代码保存在 afl-as.h 中,在最后一节中笔者会另外介绍,这里我们可以暂时忽略它的实现细节继续往下。

按照顺序,现在程序是编译好了,接下来就要用 afl-fuzz 对它进行模糊测试了。

一般来说,我们会用 afl-fuzz -i input -o output -- programe 启动 fuzzer,对应的,afl-fuzz.c 中的前半部分都在做参数解析的工作:

这部分我们大致看一下就行了,主要的关注点自然不在参数解析部分。

设置一些信号处理函数,比如说退出信号时要主动释放子进程、窗口大小调整时要跟踪变化等。

读取环境变量ASAN_OPTIONS和MSAN_OPTIONS,做一些检查

保存当前的命令

创建一个 banner

检查是否在tty终端上面运行。

计数logical CPU cores。

检查崩溃处理函数,确保崩溃后不会进入程序。

笔者在 Ubuntu20 上跑 AFL 就会遇到这个问题,因为在默认情况下,系统会将崩溃信息通过管道发送给外部程序,由于这会影响到效率,因此通过 echo core >/proc/sys/kernel/core_pattern 修改保存崩溃信息的方式,将它保存为本地文件。

检查cpu的调节器,来使得cpu可以处于高效的运行状态。

如果用户指定了环境变量 AFL_POST_LIBRARY ,那么就会从对应的路径下加载动态库并加载 afl_postprocess 函数并保存在 post_handler 中。

初始化 virgin_bits 数组用于保存后续模糊测试中覆盖的路径,virgin_tmout 保存超时的路径,virgin_crash 保存崩溃的路径。

同时建立共享内存 trace_bits,该变量用于储存样例运行时的路径。

同时将共享内存的唯一标识符 shm_id 转为字符串后保存在环境变量 SHM_ENV_VAR 中。

初始化count_class_lookup16数组,帮助快速归类统计路径覆盖的数量。

创建输出目录。

读取测试样例。

afl-fuzz 维护一个 queue_entry 的链表,该链表用来保存测试样例,每次调用 add_to_queue 都会将新样例储存到链表头部。

另外还有一个 q_prev100 也是 queue_entry 的链表,但它每 100 个测试样例保存一次。

尝试在输入目录下寻找自动生成的字典文件,调用 maybe_add_auto 将相应的字典加入到全局变量 a_extras 中,用于后续字典模式的变异当中。

在输出文件夹中创建与输入样例间的硬链接,称之为 orignal

如果指定了 -x 参数(字典模式),加载对应的字典到全局变量extras当中,用于后续字典模式的变异当中。

如果指定了 resuming_fuzz ,即从输出目录当中恢复模糊测试状态,会从之前的模糊测试状态 fuzzer_stats 文件中计算中 timeout 值,保存在 exec_tmout 中。

检测输入的命令行中是否包含@@参数,如果包含的话需要将 @@ 替换成目录文件 "%s/.cur_input", out_dir ,使得模糊测试目标程序的命令完整;同时将目录文件 "%s/.cur_input" 路径保存在 out_file 当中,后续变异的内容保存在该文件路径中,用于运行测试目标文件。

如果目标程序的输入不是来源于文件而是来源于标准输入的话,则将目录文件 "%s/.cur_input" 文件打开保存在 out_fd 文件句柄中,后续将标准输入重定向到该文件中;结合 detect_file_args 函数实现了将变异的内容保存在 "%s/.cur_input" 文件中,运行目标测试文件并进行模糊测试。

检查二进制文件是否合法。

将每个测试样例作为输入去运行目标程序,检查程序是否能够正常工作:

对每个测试样例使用 calibrate_case 进行测试,并返回运行结果,然后处理其中异常的情况,比如说程序崩溃或运行超时等。

该函数用以对样例进行测试,在后续的测试过程中也会反复调用。此处,其主要的工作是:

fork server 是 AFL 中一个重要的机制。

afl-fuzz 主动建立一个子进程为 fork server,而模糊测试则是通过 fork server 调用 fork 建立子进程来进行测试。

参考在源代码注释中的这篇文章可以有更加深入的理解:
https://lcamtuf.blogspot.com/2014/10/fuzzing-binaries-without-execve.html

之所以需要设计它,笔者在这里给出一个比较概括的理由:

一般来说,如果我们想要测试输入样例,就需要用 fork+execve 去执行相关的二进制程序,但是执行程序是需要加载代码、动态库、符号解析等各种耗时的行为,这会让 AFL 不够效率。

但是这个过程其实是存在浪费的,可以注意到,如果我们要对相同的二进制程序进行多次不同的输入样本进行测试,那按照原本的操作,我们应该多次执行 fork+execve ,而浪费就出现在这,因为我们明明已经加载好了一切,却又要因此重复加载释放。

因此 fork server 的设计主要就是为了解决这个浪费。它通过向代码中进行插桩的方式,使得在二进制程序中去建立一个 fork server(对,它实际上是由目标程序去建立的),然后这个 fork server 会在完成一切初始化后,停止在某一个地方(往往设定在 main 函数)等待 fuzzer 去喊开始执行。

一旦 fuzzer 喊了开始,就会由这个 fork server 去调用 fork 然后往下执行。而我们知道,fork 由于写时复制的机制存在,它其实并没有过多的开销,可以完全继承原有的所有上下文信息,从而避开了多次 execve 的加载开销。

摘抄一段这部分插桩的内容:

fork server 主要是通过管道和 afl-fuzz 中的 fork server 进行通信的,但他们其实不做过多的事情,往往只是通知一下程序运行的状态。因为真正的反馈信息,包括路径的发现等这部分功能是通过共享内存去实现的,它们不需要用 fork server 这种效率较低的方案去记录数据。

剩下的就是关闭一些不需要的文件或管道了,代码姑且贴在这里,以备未来有需要时可以现查:

将运行过的种子根据运行的效果进行排序,后续模糊测试根据排序的结果来挑选样例进行模糊测试。

初始化 UI

如果是恢复运行,则调用该函数来寻找到对应的样例的位置。

更新统计信息文件以进行无人值守的监视。

保存自动提取的 token ,用于后续字典模式的 fuzz

从测试样例的队列中取出 current_entry 进行测试,成功则返回 0 ,否则返回 1。这里主要是对该函数主要内容进行记录,不做细节的代码分析。

缩减的思路是这样的:如果对一个样本进行缩减后,它所覆盖的路径并未发生变化,那么就说明缩减的这部分内容是可有可无的,因此可以删除。

具体策略如下:

这个阶段读起来感觉比较抽象。首先定义了这么一个宏:

这个宏的操作是对一个 bit 进行反转。

而接下来首先有一个循环:

stage_max 是输入的总 bit 数,然后分别对每个 bit 进行翻转后用 common_fuzz_stuff 进行测试,然后再将其翻转回来。

而如果对某个字节的最后一个 bit 翻转后测试,发现路径并未增加,就能够将其认为是一个 token

然后在后面的一个循环中又做类似的事,但每次会翻转两个 bit:

然后进入 bitflip 16/8 部分,按对每两个字节进行一次翻转然后测试:

最后是 bitflip 32/8 阶段,每 4 个字节进行翻转然后测试:

此处展示 arith 8/8 部分代码:

此处给出 interest 8/8 部分代码:

此处给出 auto extras (over) 部分的源代码:

该部分使用一个巨大的 switch ,通过随机数进行跳转,并在每个分支中使用随机数来完成随机性的行为:

最后一个阶段,它会随机选择出另外一个输入样例,然后对当前的输入样例和另外一个样例都选择出合适的偏移量,然后从该处将他们拼接起来,然后重新进入到 RANDOM HAVOC 阶段。

读取其他 sync 文件夹下的 queue 文件,然后保存到自己的 queue 里。

总结来说,这个函数就是先读取有哪些 fuzzer 文件夹,然后读取其他 fuzzer 文件夹下的 queue 文件夹里的 case,并依次执行,如果发现了新 path,就保存到自己的 queue 文件夹里,而且将最后一个 sync 的 case id 写入到 .synced/其他fuzzer文件夹名 文件里,以避免重复运行。

因为 fuzz_one 部分过于庞大,而这个函数又不是那么特殊,因此把它拉出来做一个简短的说明。

执行结果是否发现了新路径,决定是否保存或跳过。如果保存了这个 case,则返回 1,否则返回 0。

其实插桩已经叙述过一部分了,在上文中的 fork server 部分,笔者就介绍过该机制就是通过插桩实现的。

但还有一部分内容没有涉及,新路径是如何在发现的同时被通知给 fuzzer 的?

在插桩阶段,我们为每个分支跳转都添加了一小段代码,这里笔者以 64 位的情况进行说明:

它首先保存了一部分将要被破坏的寄存器,然后调用了 __afl_maybe_log 来记录路径的发现。该函数同样是由汇编编写的,但我们可以用一些其他工具来反编译它:

前面的一大段代码其实都是为了去建立我们在上文所说的“共享内存”,在完成初始化后调用最后这么一小段代码进行记录:

此处 v6 即为共享内存的地址,而 a4cur_location ,因此 v7=cur_location ^ prev_location ,它将作为索引,使得共享内存中的对应偏移处的值增加。而在 fuzzer 部分就可以通过检查这块内存来发现是否有新路径被得到了。

另外,_afl_prev_loc = _afl_prev_loc >> 1; 的目的是为了避开 A->AB->B 以及 A->BB->A 被识别为相同路径的情况。

sakuraのAFL源码全注释
https://eternalsakura13.com/2020/08/23/afl/

fuzzer AFL 源码分析
https://tttang.com/user/f1tao

AFL二三事——源码分析
https://paper.seebug.org/1732/#afl-afl-asc

AFL漏洞挖掘技术漫谈(一):用AFL开始你的第一次Fuzzing
https://paper.seebug.org/841/

其实这个描述有些偏颇,插桩其实是 afl-as 负责的,不过在这里,笔者将 afl-gcc 和 afl-as 放到同一节,因此用了这样的表述,下文会具体分析 afl-as 的原理。
其实这个描述有些偏颇,插桩其实是 afl-as 负责的,不过在这里,笔者将 afl-gcc 和 afl-as 放到同一节,因此用了这样的表述,下文会具体分析 afl-as 的原理。
 
 
int main(int argc, char** argv) {
  .......
 
  find_as(argv[0]);
  edit_params(argc, argv);
  execvp(cc_params[0], (char**)cc_params);
  FATAL("Oops, failed to execute '%s' - check your PATH", cc_params[0]);
  return 0;
}
int main(int argc, char** argv) {
  .......
 
  find_as(argv[0]);
  edit_params(argc, argv);
  execvp(cc_params[0], (char**)cc_params);
  FATAL("Oops, failed to execute '%s' - check your PATH", cc_params[0]);
  return 0;
}
static void edit_params(u32 argc, char** argv) {
    ......
#else
 
    if (!strcmp(name, "afl-g++")) {
      u8* alt_cxx = getenv("AFL_CXX");
      cc_params[0] = alt_cxx ? alt_cxx : (u8*)"g++";
    } else if (!strcmp(name, "afl-gcj")) {
      u8* alt_cc = getenv("AFL_GCJ");
      cc_params[0] = alt_cc ? alt_cc : (u8*)"gcj";
    } else {
      u8* alt_cc = getenv("AFL_CC");
      cc_params[0] = alt_cc ? alt_cc : (u8*)"gcc";
    }
 
#endif /* __APPLE__ */
 
  }
 
  while (--argc) {
    u8* cur = *(++argv);
 
    if (!strncmp(cur, "-B", 2)) {
 
      if (!be_quiet) WARNF("-B is already set, overriding");
 
      if (!cur[2] && argc > 1) { argc--; argv++; }
      continue;
 
    }
 
    if (!strcmp(cur, "-integrated-as")) continue;
 
    if (!strcmp(cur, "-pipe")) continue;
 
#if defined(__FreeBSD__) && defined(__x86_64__)
    if (!strcmp(cur, "-m32")) m32_set = 1;
#endif
 
    if (!strcmp(cur, "-fsanitize=address") ||
        !strcmp(cur, "-fsanitize=memory")) asan_set = 1;
 
    if (strstr(cur, "FORTIFY_SOURCE")) fortify_set = 1;
 
    cc_params[cc_par_cnt++] = cur;
 
  }
 
  cc_params[cc_par_cnt++] = "-B";
  cc_params[cc_par_cnt++] = as_path;
 
  if (clang_mode)
    cc_params[cc_par_cnt++] = "-no-integrated-as";
 
  if (getenv("AFL_HARDEN")) {
 
    cc_params[cc_par_cnt++] = "-fstack-protector-all";
 
    if (!fortify_set)
      cc_params[cc_par_cnt++] = "-D_FORTIFY_SOURCE=2";
 
  }
 
  if (asan_set) {
 
    /* Pass this on to afl-as to adjust map density. */
 
    setenv("AFL_USE_ASAN", "1", 1);
 
  } else if (getenv("AFL_USE_ASAN")) {
 
    if (getenv("AFL_USE_MSAN"))
      FATAL("ASAN and MSAN are mutually exclusive");
 
    if (getenv("AFL_HARDEN"))
      FATAL("ASAN and AFL_HARDEN are mutually exclusive");
 
    cc_params[cc_par_cnt++] = "-U_FORTIFY_SOURCE";
    cc_params[cc_par_cnt++] = "-fsanitize=address";
 
  } else if (getenv("AFL_USE_MSAN")) {
 
    if (getenv("AFL_USE_ASAN"))
      FATAL("ASAN and MSAN are mutually exclusive");
 
    if (getenv("AFL_HARDEN"))
      FATAL("MSAN and AFL_HARDEN are mutually exclusive");
 
    cc_params[cc_par_cnt++] = "-U_FORTIFY_SOURCE";
    cc_params[cc_par_cnt++] = "-fsanitize=memory";
  }
 
  if (!getenv("AFL_DONT_OPTIMIZE")) {
 
#if defined(__FreeBSD__) && defined(__x86_64__)
 
    /* On 64-bit FreeBSD systems, clang -g -m32 is broken, but -m32 itself
       works OK. This has nothing to do with us, but let's avoid triggering
       that bug. */
 
    if (!clang_mode || !m32_set)
      cc_params[cc_par_cnt++] = "-g";
#else
      cc_params[cc_par_cnt++] = "-g";
#endif
 
    cc_params[cc_par_cnt++] = "-O3";
    cc_params[cc_par_cnt++] = "-funroll-loops";
 
    /* Two indicators that you're building for fuzzing; one of them is
       AFL-specific, the other is shared with libfuzzer. */
 
    cc_params[cc_par_cnt++] = "-D__AFL_COMPILER=1";
    cc_params[cc_par_cnt++] = "-DFUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION=1";
 
  }
 
  if (getenv("AFL_NO_BUILTIN")) {
 
    cc_params[cc_par_cnt++] = "-fno-builtin-strcmp";
    cc_params[cc_par_cnt++] = "-fno-builtin-strncmp";
    cc_params[cc_par_cnt++] = "-fno-builtin-strcasecmp";
    cc_params[cc_par_cnt++] = "-fno-builtin-strncasecmp";
    cc_params[cc_par_cnt++] = "-fno-builtin-memcmp";
    cc_params[cc_par_cnt++] = "-fno-builtin-strstr";
    cc_params[cc_par_cnt++] = "-fno-builtin-strcasestr";
 
  }
  cc_params[cc_par_cnt] = NULL;
}
static void edit_params(u32 argc, char** argv) {
    ......
#else
 
    if (!strcmp(name, "afl-g++")) {
      u8* alt_cxx = getenv("AFL_CXX");
      cc_params[0] = alt_cxx ? alt_cxx : (u8*)"g++";
    } else if (!strcmp(name, "afl-gcj")) {
      u8* alt_cc = getenv("AFL_GCJ");
      cc_params[0] = alt_cc ? alt_cc : (u8*)"gcj";
    } else {
      u8* alt_cc = getenv("AFL_CC");
      cc_params[0] = alt_cc ? alt_cc : (u8*)"gcc";
    }
 
#endif /* __APPLE__ */
 
  }
 
  while (--argc) {
    u8* cur = *(++argv);
 
    if (!strncmp(cur, "-B", 2)) {
 
      if (!be_quiet) WARNF("-B is already set, overriding");
 
      if (!cur[2] && argc > 1) { argc--; argv++; }
      continue;
 
    }
 
    if (!strcmp(cur, "-integrated-as")) continue;
 
    if (!strcmp(cur, "-pipe")) continue;
 
#if defined(__FreeBSD__) && defined(__x86_64__)
    if (!strcmp(cur, "-m32")) m32_set = 1;
#endif
 
    if (!strcmp(cur, "-fsanitize=address") ||
        !strcmp(cur, "-fsanitize=memory")) asan_set = 1;
 
    if (strstr(cur, "FORTIFY_SOURCE")) fortify_set = 1;
 
    cc_params[cc_par_cnt++] = cur;
 
  }
 
  cc_params[cc_par_cnt++] = "-B";
  cc_params[cc_par_cnt++] = as_path;
 
  if (clang_mode)
    cc_params[cc_par_cnt++] = "-no-integrated-as";
 
  if (getenv("AFL_HARDEN")) {
 
    cc_params[cc_par_cnt++] = "-fstack-protector-all";
 
    if (!fortify_set)
      cc_params[cc_par_cnt++] = "-D_FORTIFY_SOURCE=2";
 
  }
 
  if (asan_set) {
 
    /* Pass this on to afl-as to adjust map density. */
 
    setenv("AFL_USE_ASAN", "1", 1);
 
  } else if (getenv("AFL_USE_ASAN")) {
 
    if (getenv("AFL_USE_MSAN"))
      FATAL("ASAN and MSAN are mutually exclusive");
 
    if (getenv("AFL_HARDEN"))
      FATAL("ASAN and AFL_HARDEN are mutually exclusive");
 
    cc_params[cc_par_cnt++] = "-U_FORTIFY_SOURCE";
    cc_params[cc_par_cnt++] = "-fsanitize=address";
 
  } else if (getenv("AFL_USE_MSAN")) {
 
    if (getenv("AFL_USE_ASAN"))
      FATAL("ASAN and MSAN are mutually exclusive");
 
    if (getenv("AFL_HARDEN"))
      FATAL("MSAN and AFL_HARDEN are mutually exclusive");
 
    cc_params[cc_par_cnt++] = "-U_FORTIFY_SOURCE";
    cc_params[cc_par_cnt++] = "-fsanitize=memory";
  }
 
  if (!getenv("AFL_DONT_OPTIMIZE")) {
 
#if defined(__FreeBSD__) && defined(__x86_64__)
 
    /* On 64-bit FreeBSD systems, clang -g -m32 is broken, but -m32 itself
       works OK. This has nothing to do with us, but let's avoid triggering
       that bug. */
 
    if (!clang_mode || !m32_set)
      cc_params[cc_par_cnt++] = "-g";
#else
      cc_params[cc_par_cnt++] = "-g";
#endif
 
    cc_params[cc_par_cnt++] = "-O3";
    cc_params[cc_par_cnt++] = "-funroll-loops";
 
    /* Two indicators that you're building for fuzzing; one of them is
       AFL-specific, the other is shared with libfuzzer. */
 
    cc_params[cc_par_cnt++] = "-D__AFL_COMPILER=1";
    cc_params[cc_par_cnt++] = "-DFUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION=1";
 
  }
 
  if (getenv("AFL_NO_BUILTIN")) {
 
    cc_params[cc_par_cnt++] = "-fno-builtin-strcmp";
    cc_params[cc_par_cnt++] = "-fno-builtin-strncmp";
    cc_params[cc_par_cnt++] = "-fno-builtin-strcasecmp";
    cc_params[cc_par_cnt++] = "-fno-builtin-strncasecmp";
    cc_params[cc_par_cnt++] = "-fno-builtin-memcmp";
    cc_params[cc_par_cnt++] = "-fno-builtin-strstr";
    cc_params[cc_par_cnt++] = "-fno-builtin-strcasestr";
 
  }
  cc_params[cc_par_cnt] = NULL;
}
int main(int argc, char** argv) {
  ......
 
  gettimeofday(&tv, &tz);
  rand_seed = tv.tv_sec ^ tv.tv_usec ^ getpid();
  srandom(rand_seed);
 
  edit_params(argc, argv);
 
  ......
 
  if (!just_version) add_instrumentation();
 
  if (!(pid = fork())) {
 
    execvp(as_params[0], (char**)as_params);
    FATAL("Oops, failed to execute '%s' - check your PATH", as_params[0]);
 
  }
 
  if (pid < 0) PFATAL("fork() failed");
 
  if (waitpid(pid, &status, 0) <= 0) PFATAL("waitpid() failed");
 
  if (!getenv("AFL_KEEP_ASSEMBLY")) unlink(modified_file);
 
  exit(WEXITSTATUS(status));
}
int main(int argc, char** argv) {
  ......
 
  gettimeofday(&tv, &tz);
  rand_seed = tv.tv_sec ^ tv.tv_usec ^ getpid();
  srandom(rand_seed);
 
  edit_params(argc, argv);
 
  ......
 
  if (!just_version) add_instrumentation();
 
  if (!(pid = fork())) {
 
    execvp(as_params[0], (char**)as_params);
    FATAL("Oops, failed to execute '%s' - check your PATH", as_params[0]);
 
  }
 
  if (pid < 0) PFATAL("fork() failed");
 
  if (waitpid(pid, &status, 0) <= 0) PFATAL("waitpid() failed");
 
  if (!getenv("AFL_KEEP_ASSEMBLY")) unlink(modified_file);
 
  exit(WEXITSTATUS(status));
}
 
static void add_instrumentation(void) {
    ......
 
    /* If we're in the right mood for instrumenting, check for function
       names or conditional labels. This is a bit messy, but in essence,
       we want to catch:
 
         ^main:      - function entry point (always instrumented)
         ^.L0:       - GCC branch label
         ^.LBB0_0:   - clang branch label (but only in clang mode)
         ^\tjnz foo  - conditional branches
 
       ...but not:
 
         ^# BB#0:    - clang comments
         ^ # BB#0:   - ditto
         ^.Ltmp0:    - clang non-branch labels
         ^.LC0       - GCC non-branch labels
         ^.LBB0_0:   - ditto (when in GCC mode)
         ^\tjmp foo  - non-conditional jumps
 
       Additionally, clang and GCC on MacOS X follow a different convention
       with no leading dots on labels, hence the weird maze of #ifdefs
       later on.
 
     */
 
    if (skip_intel || skip_app || skip_csect || !instr_ok ||
        line[0] == '#' || line[0] == ' ') continue;
 
    /* Conditional branch instruction (jnz, etc). We append the instrumentation
       right after the branch (to instrument the not-taken path) and at the
       branch destination label (handled later on). */
 
    if (line[0] == '\t') {
 
      if (line[1] == 'j' && line[2] != 'm' && R(100) < inst_ratio) {
 
        fprintf(outf, use_64bit ? trampoline_fmt_64 : trampoline_fmt_32,
                R(MAP_SIZE));
 
        ins_lines++;
 
      }
 
      continue;
 
    }
 
    /* Label of some sort. This may be a branch destination, but we need to
       tread carefully and account for several different formatting
       conventions. */
 
#ifdef __APPLE__
 
    /* Apple: L<whatever><digit>: */
 
    if ((colon_pos = strstr(line, ":"))) {
 
      if (line[0] == 'L' && isdigit(*(colon_pos - 1))) {
 
#else
 
    /* Everybody else: .L<whatever>: */
 
    if (strstr(line, ":")) {
 
      if (line[0] == '.') {
 
#endif /* __APPLE__ */
 
        /* .L0: or LBB0_0: style jump destination */
 
#ifdef __APPLE__
 
        /* Apple: L<num> / LBB<num> */
 
        if ((isdigit(line[1]) || (clang_mode && !strncmp(line, "LBB", 3)))
            && R(100) < inst_ratio) {
 
#else
        /* Apple: .L<num> / .LBB<num> */
        if ((isdigit(line[2]) || (clang_mode && !strncmp(line + 1, "LBB", 3)))
            && R(100) < inst_ratio) {
#endif /* __APPLE__ */
 
          /* An optimization is possible here by adding the code only if the
             label is mentioned in the code in contexts other than call / jmp.
             That said, this complicates the code by requiring two-pass
             processing (messy with stdin), and results in a speed gain
             typically under 10%, because compilers are generally pretty good
             about not generating spurious intra-function jumps.
 
             We use deferred output chiefly to avoid disrupting
             .Lfunc_begin0-style exception handling calculations (a problem on
             MacOS X). */
 
          if (!skip_next_label) instrument_next = 1; else skip_next_label = 0;
        }
      } else {
        /* Function label (always instrumented, deferred mode). */
        instrument_next = 1;
      }
    }
  }
 
  if (ins_lines)
    fputs(use_64bit ? main_payload_64 : main_payload_32, outf);
 
  if (input_file) fclose(inf);
  fclose(outf);
 
  if (!be_quiet) {
 
    if (!ins_lines) WARNF("No instrumentation targets found%s.",
                          pass_thru ? " (pass-thru mode)" : "");
    else OKF("Instrumented %u locations (%s-bit, %s mode, ratio %u%%).",
             ins_lines, use_64bit ? "64" : "32",
             getenv("AFL_HARDEN") ? "hardened" :
             (sanitizer ? "ASAN/MSAN" : "non-hardened"),
             inst_ratio);
 
  }
}
static void add_instrumentation(void) {
    ......
 
    /* If we're in the right mood for instrumenting, check for function
       names or conditional labels. This is a bit messy, but in essence,
       we want to catch:
 
         ^main:      - function entry point (always instrumented)
         ^.L0:       - GCC branch label
         ^.LBB0_0:   - clang branch label (but only in clang mode)
         ^\tjnz foo  - conditional branches
 
       ...but not:
 
         ^# BB#0:    - clang comments
         ^ # BB#0:   - ditto
         ^.Ltmp0:    - clang non-branch labels
         ^.LC0       - GCC non-branch labels
         ^.LBB0_0:   - ditto (when in GCC mode)
         ^\tjmp foo  - non-conditional jumps
 
       Additionally, clang and GCC on MacOS X follow a different convention
       with no leading dots on labels, hence the weird maze of #ifdefs
       later on.
 
     */
 
    if (skip_intel || skip_app || skip_csect || !instr_ok ||
        line[0] == '#' || line[0] == ' ') continue;
 
    /* Conditional branch instruction (jnz, etc). We append the instrumentation
       right after the branch (to instrument the not-taken path) and at the
       branch destination label (handled later on). */
 
    if (line[0] == '\t') {
 
      if (line[1] == 'j' && line[2] != 'm' && R(100) < inst_ratio) {
 
        fprintf(outf, use_64bit ? trampoline_fmt_64 : trampoline_fmt_32,
                R(MAP_SIZE));
 
        ins_lines++;
 
      }
 
      continue;
 
    }
 
    /* Label of some sort. This may be a branch destination, but we need to
       tread carefully and account for several different formatting
       conventions. */
 
#ifdef __APPLE__
 
    /* Apple: L<whatever><digit>: */
 
    if ((colon_pos = strstr(line, ":"))) {
 
      if (line[0] == 'L' && isdigit(*(colon_pos - 1))) {
 
#else
 
    /* Everybody else: .L<whatever>: */
 
    if (strstr(line, ":")) {
 
      if (line[0] == '.') {
 
#endif /* __APPLE__ */
 
        /* .L0: or LBB0_0: style jump destination */
 
#ifdef __APPLE__
 
        /* Apple: L<num> / LBB<num> */
 
        if ((isdigit(line[1]) || (clang_mode && !strncmp(line, "LBB", 3)))
            && R(100) < inst_ratio) {
 
#else
        /* Apple: .L<num> / .LBB<num> */
        if ((isdigit(line[2]) || (clang_mode && !strncmp(line + 1, "LBB", 3)))
            && R(100) < inst_ratio) {
#endif /* __APPLE__ */
 
          /* An optimization is possible here by adding the code only if the
             label is mentioned in the code in contexts other than call / jmp.
             That said, this complicates the code by requiring two-pass
             processing (messy with stdin), and results in a speed gain
             typically under 10%, because compilers are generally pretty good
             about not generating spurious intra-function jumps.
 
             We use deferred output chiefly to avoid disrupting
             .Lfunc_begin0-style exception handling calculations (a problem on
             MacOS X). */
 
          if (!skip_next_label) instrument_next = 1; else skip_next_label = 0;
        }
      } else {
        /* Function label (always instrumented, deferred mode). */
        instrument_next = 1;
      }
    }
  }
 
  if (ins_lines)
    fputs(use_64bit ? main_payload_64 : main_payload_32, outf);
 
  if (input_file) fclose(inf);
  fclose(outf);
 
  if (!be_quiet) {
 
    if (!ins_lines) WARNF("No instrumentation targets found%s.",
                          pass_thru ? " (pass-thru mode)" : "");
    else OKF("Instrumented %u locations (%s-bit, %s mode, ratio %u%%).",
             ins_lines, use_64bit ? "64" : "32",
             getenv("AFL_HARDEN") ? "hardened" :
             (sanitizer ? "ASAN/MSAN" : "non-hardened"),
             inst_ratio);
 
  }
}
 
int main(int argc, char** argv) {
  ......
 
  gettimeofday(&tv, &tz);
  srandom(tv.tv_sec ^ tv.tv_usec ^ getpid());
 
  while ((opt = getopt(argc, argv, "+i:o:f:m:b:t:T:dnCB:S:M:x:QV")) > 0)
 
    switch (opt) {
 
      case 'i': /* input dir */
 
        if (in_dir) FATAL("Multiple -i options not supported");
        in_dir = optarg;
 
        if (!strcmp(in_dir, "-")) in_place_resume = 1;
 
        break;
 
      case 'o': /* output dir */
 
        if (out_dir) FATAL("Multiple -o options not supported");
        out_dir = optarg;
        break;
 
    ......
 
      case 'V': /* Show version number */
 
        /* Version number has been printed already, just quit. */
        exit(0);
 
      default:
        usage(argv[0]);
    }
int main(int argc, char** argv) {
  ......
 
  gettimeofday(&tv, &tz);
  srandom(tv.tv_sec ^ tv.tv_usec ^ getpid());
 
  while ((opt = getopt(argc, argv, "+i:o:f:m:b:t:T:dnCB:S:M:x:QV")) > 0)
 
    switch (opt) {
 
      case 'i': /* input dir */
 
        if (in_dir) FATAL("Multiple -i options not supported");
        in_dir = optarg;
 
        if (!strcmp(in_dir, "-")) in_place_resume = 1;
 
        break;
 
      case 'o': /* output dir */
 
        if (out_dir) FATAL("Multiple -o options not supported");
        out_dir = optarg;
        break;
 
    ......
 
      case 'V': /* Show version number */
 
        /* Version number has been printed already, just quit. */
        exit(0);
 
      default:
        usage(argv[0]);
    }
int main(int argc, char** argv) {
  ......
 
  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");
 
  save_cmdline(argc, argv);
 
  fix_up_banner(argv[optind]);
 
  check_if_tty();
 
  get_core_count();
 
#ifdef HAVE_AFFINITY
  bind_to_free_cpu();
#endif /* HAVE_AFFINITY */
 
  check_crash_handling();
  check_cpu_governor();
 
  setup_post();
  setup_shm();
  init_count_class16();
 
  setup_dirs_fds();
  read_testcases();
  load_auto();
 
  pivot_inputs();
 
  if (extras_dir) load_extras(extras_dir);
 
  if (!timeout_given) find_timeout();
 
  detect_file_args(argv + optind + 1);
 
  if (!out_file) setup_stdio_file();
 
  check_binary(argv[optind]);
 
  start_time = get_cur_time();
 
  if (qemu_mode)
    use_argv = get_qemu_argv(argv[0], argv + optind, argc - optind);
  else
    use_argv = argv + optind;
 
  perform_dry_run(use_argv);
 
  cull_queue();
 
  show_init_stats();
 
  seek_to = find_start_position();
 
  write_stats_file(0, 0, 0);
  save_auto();
 
  if (stop_soon) goto stop_fuzzing;
 
  /* Woop woop woop */
 
  if (!not_on_tty) {
    sleep(4);
    start_time += 4000;
    if (stop_soon) goto stop_fuzzing;
  }
 
  while (1) {
 
    u8 skipped_fuzz;
 
    cull_queue();
 
    if (!queue_cur) {
 
      queue_cycle++;
      current_entry     = 0;
      cur_skipped_paths = 0;
      queue_cur         = queue;
 
      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");
  }
 
  write_bitmap();
  write_stats_file(0, 0, 0);
  save_auto();
 
stop_fuzzing:
 
  SAYF(CURSOR_SHOW cLRD "\n\n+++ Testing aborted %s +++\n" cRST,
       stop_soon == 2 ? "programmatically" : "by user");
 
  /* Running for more than 30 minutes but still doing first cycle? */
 
  if (queue_cycle == 1 && get_cur_time() - start_time > 30 * 60 * 1000) {
 
    SAYF("\n" cYEL "[!] " cRST
           "Stopped during the first cycle, results may be incomplete.\n"
           "    (For info on resuming, see %s/README.)\n", doc_path);
 
  }
 
  fclose(plot_file);
  destroy_queue();
  destroy_extras();
  ck_free(target_path);
  ck_free(sync_id);
 
  alloc_report();
 
  OKF("We're done here. Have a nice day!\n");
 
  exit(0);
}
int main(int argc, char** argv) {
  ......
 
  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");
 
  save_cmdline(argc, argv);
 
  fix_up_banner(argv[optind]);
 
  check_if_tty();
 
  get_core_count();
 
#ifdef HAVE_AFFINITY
  bind_to_free_cpu();
#endif /* HAVE_AFFINITY */
 
  check_crash_handling();
  check_cpu_governor();
 
  setup_post();
  setup_shm();
  init_count_class16();
 
  setup_dirs_fds();
  read_testcases();
  load_auto();
 
  pivot_inputs();
 
  if (extras_dir) load_extras(extras_dir);
 
  if (!timeout_given) find_timeout();
 
  detect_file_args(argv + optind + 1);
 
  if (!out_file) setup_stdio_file();
 
  check_binary(argv[optind]);
 
  start_time = get_cur_time();
 
  if (qemu_mode)
    use_argv = get_qemu_argv(argv[0], argv + optind, argc - optind);
  else
    use_argv = argv + optind;
 
  perform_dry_run(use_argv);
 
  cull_queue();
 
  show_init_stats();
 
  seek_to = find_start_position();
 
  write_stats_file(0, 0, 0);
  save_auto();
 
  if (stop_soon) goto stop_fuzzing;
 
  /* Woop woop woop */
 
  if (!not_on_tty) {
    sleep(4);
    start_time += 4000;
    if (stop_soon) goto stop_fuzzing;
  }
 
  while (1) {
 
    u8 skipped_fuzz;
 
    cull_queue();
 
    if (!queue_cur) {
 
      queue_cycle++;
      current_entry     = 0;
      cur_skipped_paths = 0;
      queue_cur         = queue;
 
      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");
  }
 
  write_bitmap();
  write_stats_file(0, 0, 0);
  save_auto();
 
stop_fuzzing:
 
  SAYF(CURSOR_SHOW cLRD "\n\n+++ Testing aborted %s +++\n" cRST,
       stop_soon == 2 ? "programmatically" : "by user");
 
  /* Running for more than 30 minutes but still doing first cycle? */
 
  if (queue_cycle == 1 && get_cur_time() - start_time > 30 * 60 * 1000) {
 
    SAYF("\n" cYEL "[!] " cRST
           "Stopped during the first cycle, results may be incomplete.\n"
           "    (For info on resuming, see %s/README.)\n", doc_path);
 
  }
 
  fclose(plot_file);
  destroy_queue();
  destroy_extras();
  ck_free(target_path);
  ck_free(sync_id);
 
  alloc_report();
 
  OKF("We're done here. Have a nice day!\n");
 
  exit(0);
}
s32 fd = open("/proc/sys/kernel/core_pattern", O_RDONLY);
u8  fchar;
if (fd < 0) return;
ACTF("Checking core_pattern...");
if (read(fd, &fchar, 1) == 1 && fchar == '|') {
  SAYF("\n" cLRD "[-] " cRST
       "Hmm, your system is configured to send core dump notifications to an\n"
       "    external utility. This will cause issues: there will be an extended delay\n"
       "    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 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");
 
  if (!getenv("AFL_I_DONT_CARE_ABOUT_MISSING_CRASHES"))
    FATAL("Pipe at the beginning of 'core_pattern'");
}
close(fd);
s32 fd = open("/proc/sys/kernel/core_pattern", O_RDONLY);
u8  fchar;
if (fd < 0) return;
ACTF("Checking core_pattern...");
if (read(fd, &fchar, 1) == 1 && fchar == '|') {
  SAYF("\n" cLRD "[-] " cRST
       "Hmm, your system is configured to send core dump notifications to an\n"
       "    external utility. This will cause issues: there will be an extended delay\n"
       "    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 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");
 
  if (!getenv("AFL_I_DONT_CARE_ABOUT_MISSING_CRASHES"))
    FATAL("Pipe at the beginning of 'core_pattern'");
}
close(fd);
EXP_ST void setup_shm(void) {
  u8* shm_str;
  if (!in_bitmap) memset(virgin_bits, 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);
  if (shm_id < 0) PFATAL("shmget() failed");
  atexit(remove_shm);
  shm_str = alloc_printf("%d", shm_id);
  /* If somebody is asking us to fuzz instrumented binaries in dumb mode,
     we don't want them to detect instrumentation, since we won't be sending
     fork server commands. This should be replaced with better auto-detection
     later on, perhaps? */
  if (!dumb_mode) setenv(SHM_ENV_VAR, shm_str, 1);
  ck_free(shm_str);
  trace_bits = shmat(shm_id, NULL, 0);
  if (trace_bits == (void *)-1) PFATAL("shmat() failed");
}
EXP_ST void setup_shm(void) {
  u8* shm_str;
  if (!in_bitmap) memset(virgin_bits, 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);
  if (shm_id < 0) PFATAL("shmget() failed");
  atexit(remove_shm);
  shm_str = alloc_printf("%d", shm_id);
  /* If somebody is asking us to fuzz instrumented binaries in dumb mode,
     we don't want them to detect instrumentation, since we won't be sending
     fork server commands. This should be replaced with better auto-detection
     later on, perhaps? */
  if (!dumb_mode) setenv(SHM_ENV_VAR, shm_str, 1);
  ck_free(shm_str);
  trace_bits = shmat(shm_id, NULL, 0);
  if (trace_bits == (void *)-1) PFATAL("shmat() failed");
}
 
 
static void read_testcases(void) {
 
  struct dirent **nl;
  s32 nl_cnt;
  u32 i;
  u8* fn;
 
  /* Auto-detect non-in-place resumption attempts. */
 
  fn = alloc_printf("%s/queue", in_dir);
  if (!access(fn, F_OK)) in_dir = fn; else ck_free(fn);
 
  ACTF("Scanning '%s'...", in_dir);
 
  /* 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");
 
    PFATAL("Unable to open '%s'", in_dir);
 
  }
 
  if (shuffle_queue && nl_cnt > 1) {
 
    ACTF("Shuffling queue...");
    shuffle_ptrs((void**)nl, nl_cnt);
 
  }
 
  for (i = 0; i < nl_cnt; i++) {
 
    struct stat st;
 
    u8* fn = alloc_printf("%s/%s", in_dir, nl[i]->d_name);
    u8* dfn = alloc_printf("%s/.state/deterministic_done/%s", in_dir, nl[i]->d_name);
 
    u8  passed_det = 0;
 
    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);
 
  }
static void read_testcases(void) {
 
  struct dirent **nl;
  s32 nl_cnt;
  u32 i;
  u8* fn;
 
  /* Auto-detect non-in-place resumption attempts. */
 
  fn = alloc_printf("%s/queue", in_dir);
  if (!access(fn, F_OK)) in_dir = fn; else ck_free(fn);
 
  ACTF("Scanning '%s'...", in_dir);
 
  /* 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");
 
    PFATAL("Unable to open '%s'", in_dir);
 
  }
 
  if (shuffle_queue && nl_cnt > 1) {
 
    ACTF("Shuffling queue...");
    shuffle_ptrs((void**)nl, nl_cnt);
 
  }
 
  for (i = 0; i < nl_cnt; i++) {
 
    struct stat st;
 
    u8* fn = alloc_printf("%s/%s", in_dir, nl[i]->d_name);
    u8* dfn = alloc_printf("%s/.state/deterministic_done/%s", in_dir, nl[i]->d_name);
 
    u8  passed_det = 0;
 
    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);
 
  }
static void add_to_queue(u8* fname, u32 len, u8 passed_det) {
 
  struct queue_entry* q = ck_alloc(sizeof(struct queue_entry));
  q->fname        = fname;
  q->len          = len;
  q->depth        = cur_depth + 1;
  q->passed_det   = passed_det;
 
  if (q->depth > max_depth) max_depth = q->depth;
 
  if (queue_top) {
 
    queue_top->next = q;
    queue_top = q;
 
  } else q_prev100 = queue = queue_top = q;
 
  queued_paths++;
  pending_not_fuzzed++;
 
  cycles_wo_finds = 0;
 
  /* Set next_100 pointer for every 100th element (index 0, 100, etc) to allow faster iteration. */
  if ((queued_paths - 1) % 100 == 0 && queued_paths > 1) {
 
    q_prev100->next_100 = q;
    q_prev100 = q;
 
  }
  last_path_time = get_cur_time();
}
static void add_to_queue(u8* fname, u32 len, u8 passed_det) {
 
  struct queue_entry* q = ck_alloc(sizeof(struct queue_entry));
  q->fname        = fname;
  q->len          = len;
  q->depth        = cur_depth + 1;
  q->passed_det   = passed_det;
 
  if (q->depth > max_depth) max_depth = q->depth;
 
  if (queue_top) {
 
    queue_top->next = q;
    queue_top = q;
 
  } else q_prev100 = queue = queue_top = q;
 
  queued_paths++;
  pending_not_fuzzed++;
 
  cycles_wo_finds = 0;
 
  /* Set next_100 pointer for every 100th element (index 0, 100, etc) to allow faster iteration. */
  if ((queued_paths - 1) % 100 == 0 && queued_paths > 1) {
 
    q_prev100->next_100 = q;
    q_prev100 = q;
 
  }
  last_path_time = get_cur_time();
}
 
static void perform_dry_run(char** argv) {
 
  struct queue_entry* q = queue;
  u32 cal_failures = 0;
  u8* skip_crashes = getenv("AFL_SKIP_CRASHES");
 
  while (q) {
 
    u8* use_mem;
    u8  res;
    s32 fd;
 
    u8* fn = strrchr(q->fname, '/') + 1;
 
    ACTF("Attempting dry run with '%s'...", fn);
 
    fd = open(q->fname, O_RDONLY);
    if (fd < 0) PFATAL("Unable to open '%s'", q->fname);
 
    use_mem = ck_alloc_nozero(q->len);
 
    if (read(fd, use_mem, q->len) != q->len)
      FATAL("Short read from '%s'", q->fname);
 
    close(fd);
 
    res = calibrate_case(argv, q, use_mem, 0, 1);
    ck_free(use_mem);
 
    if (stop_soon) return;
 
    if (res == crash_mode || res == FAULT_NOBITS)
      SAYF(cGRA "    len = %u, map size = %u, exec speed = %llu us\n" cRST,
           q->len, q->bitmap_size, q->exec_us);
 
    ......
 
    if (q->var_behavior) WARNF("Instrumentation output varies across runs.");
 
    q = q->next;
 
  }
 
  if (cal_failures) {
 
    if (cal_failures == queued_paths)
      FATAL("All test cases time out%s, giving up!",
            skip_crashes ? " or crash" : "");
 
    WARNF("Skipped %u test cases (%0.02f%%) due to timeouts%s.", cal_failures,
          ((double)cal_failures) * 100 / queued_paths,
          skip_crashes ? " or crashes" : "");
 
    if (cal_failures * 5 > queued_paths)
      WARNF(cLRD "High percentage of rejected test cases, check settings!");
 
  }
  OKF("All test cases processed.");
}
static void perform_dry_run(char** argv) {
 
  struct queue_entry* q = queue;
  u32 cal_failures = 0;
  u8* skip_crashes = getenv("AFL_SKIP_CRASHES");
 
  while (q) {
 
    u8* use_mem;
    u8  res;
    s32 fd;
 
    u8* fn = strrchr(q->fname, '/') + 1;
 
    ACTF("Attempting dry run with '%s'...", fn);
 
    fd = open(q->fname, O_RDONLY);
    if (fd < 0) PFATAL("Unable to open '%s'", q->fname);
 
    use_mem = ck_alloc_nozero(q->len);
 
    if (read(fd, use_mem, q->len) != q->len)
      FATAL("Short read from '%s'", q->fname);
 
    close(fd);
 
    res = calibrate_case(argv, q, use_mem, 0, 1);
    ck_free(use_mem);
 
    if (stop_soon) return;
 
    if (res == crash_mode || res == FAULT_NOBITS)
      SAYF(cGRA "    len = %u, map size = %u, exec speed = %llu us\n" cRST,
           q->len, q->bitmap_size, q->exec_us);
 
    ......
 
    if (q->var_behavior) WARNF("Instrumentation output varies across runs.");
 
    q = q->next;
 
  }
 
  if (cal_failures) {
 
    if (cal_failures == queued_paths)
      FATAL("All test cases time out%s, giving up!",
            skip_crashes ? " or crash" : "");
 
    WARNF("Skipped %u test cases (%0.02f%%) due to timeouts%s.", cal_failures,
          ((double)cal_failures) * 100 / queued_paths,
          skip_crashes ? " or crashes" : "");
 
    if (cal_failures * 5 > queued_paths)
      WARNF(cLRD "High percentage of rejected test cases, check settings!");
 
  }
  OKF("All test cases processed.");
}
static u8 calibrate_case(char** argv, struct queue_entry* q, u8* use_mem,
                         u32 handicap, u8 from_queue) {
 
  static u8 first_trace[MAP_SIZE];
 
  u8  fault = 0, new_bits = 0, var_detected = 0, hnb = 0,
      first_run = (q->exec_cksum == 0);
 
  u64 start_us, stop_us;
 
  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)
    use_tmout = MAX(exec_tmout + CAL_TMOUT_ADD,
                    exec_tmout * CAL_TMOUT_PERC / 100);
 
  q->cal_failed++;
 
  stage_name = "calibration";
  stage_max  = fast_cal ? 3 : CAL_CYCLES;
 
  /* Make sure the forkserver is up before we do anything, and let's not
     count its spin-up time toward binary calibration. */
 
  if (dumb_mode != 1 && !no_forkserver && !forksrv_pid)
    init_forkserver(argv);
 
  if (q->exec_cksum) {
 
    memcpy(first_trace, trace_bits, MAP_SIZE);
    hnb = has_new_bits(virgin_bits);
    if (hnb > new_bits) new_bits = hnb;
 
  }
 
  start_us = get_cur_time_us();
 
  for (stage_cur = 0; stage_cur < stage_max; stage_cur++) {
 
    u32 cksum;
 
    if (!first_run && !(stage_cur % stats_update_freq)) show_stats();
 
    write_to_testcase(use_mem, q->len);
 
    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. */
 
    if (stop_soon || fault != crash_mode) goto abort_calibration;
 
    if (!dumb_mode && !stage_cur && !count_bytes(trace_bits)) {
      fault = FAULT_NOINST;
      goto abort_calibration;
    }
 
    cksum = hash32(trace_bits, MAP_SIZE, HASH_CONST);
 
    if (q->exec_cksum != cksum) {
 
      hnb = has_new_bits(virgin_bits);
      if (hnb > new_bits) new_bits = hnb;
 
      if (q->exec_cksum) {
 
        u32 i;
 
        for (i = 0; i < MAP_SIZE; i++) {
 
          if (!var_bytes[i] && first_trace[i] != trace_bits[i]) {
 
            var_bytes[i] = 1;
            stage_max    = CAL_CYCLES_LONG;
 
          }
 
        }
 
        var_detected = 1;
 
      } else {
 
        q->exec_cksum = cksum;
        memcpy(first_trace, trace_bits, MAP_SIZE);
 
      }
 
    }
 
  }
 
  stop_us = get_cur_time_us();
 
  total_cal_us     += stop_us - start_us;
  total_cal_cycles += stage_max;
 
  /* OK, let's collect some stats about the performance of this test case.
     This is used for fuzzing air time calculations in calculate_score(). */
 
  q->exec_us     = (stop_us - start_us) / stage_max;
  q->bitmap_size = count_bytes(trace_bits);
  q->handicap    = handicap;
  q->cal_failed  = 0;
 
  total_bitmap_size += q->bitmap_size;
  total_bitmap_entries++;
 
  update_bitmap_score(q);
 
  /* If this case didn't result in new output from the instrumentation, tell
     parent. This is a non-critical problem, but something to warn the user
     about. */
 
  if (!dumb_mode && first_run && !fault && !new_bits) fault = FAULT_NOBITS;
 
abort_calibration:
 
  if (new_bits == 2 && !q->has_new_cov) {
    q->has_new_cov = 1;
    queued_with_cov++;
  }
 
  /* Mark variable paths. */
 
  if (var_detected) {
 
    var_byte_count = count_bytes(var_bytes);
 
    if (!q->var_behavior) {
      mark_as_variable(q);
      queued_variable++;
    }
 
  }
 
  stage_name = old_sn;
  stage_cur  = old_sc;
  stage_max  = old_sm;
 
  if (!first_run) show_stats();
 
  return fault;
 
}
static u8 calibrate_case(char** argv, struct queue_entry* q, u8* use_mem,
                         u32 handicap, u8 from_queue) {
 
  static u8 first_trace[MAP_SIZE];
 
  u8  fault = 0, new_bits = 0, var_detected = 0, hnb = 0,
      first_run = (q->exec_cksum == 0);
 
  u64 start_us, stop_us;
 
  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)
    use_tmout = MAX(exec_tmout + CAL_TMOUT_ADD,
                    exec_tmout * CAL_TMOUT_PERC / 100);
 
  q->cal_failed++;
 
  stage_name = "calibration";
  stage_max  = fast_cal ? 3 : CAL_CYCLES;
 
  /* Make sure the forkserver is up before we do anything, and let's not
     count its spin-up time toward binary calibration. */
 
  if (dumb_mode != 1 && !no_forkserver && !forksrv_pid)
    init_forkserver(argv);
 
  if (q->exec_cksum) {
 
    memcpy(first_trace, trace_bits, MAP_SIZE);
    hnb = has_new_bits(virgin_bits);
    if (hnb > new_bits) new_bits = hnb;
 
  }
 
  start_us = get_cur_time_us();
 
  for (stage_cur = 0; stage_cur < stage_max; stage_cur++) {
 
    u32 cksum;
 
    if (!first_run && !(stage_cur % stats_update_freq)) show_stats();
 
    write_to_testcase(use_mem, q->len);
 
    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. */
 
    if (stop_soon || fault != crash_mode) goto abort_calibration;
 
    if (!dumb_mode && !stage_cur && !count_bytes(trace_bits)) {
      fault = FAULT_NOINST;
      goto abort_calibration;
    }
 
    cksum = hash32(trace_bits, MAP_SIZE, HASH_CONST);
 
    if (q->exec_cksum != cksum) {
 
      hnb = has_new_bits(virgin_bits);
      if (hnb > new_bits) new_bits = hnb;
 
      if (q->exec_cksum) {
 
        u32 i;
 
        for (i = 0; i < MAP_SIZE; i++) {
 
          if (!var_bytes[i] && first_trace[i] != trace_bits[i]) {
 
            var_bytes[i] = 1;
            stage_max    = CAL_CYCLES_LONG;
 
          }
 
        }
 
        var_detected = 1;
 
      } else {
 
        q->exec_cksum = cksum;
        memcpy(first_trace, trace_bits, MAP_SIZE);
 
      }
 
    }
 
  }
 
  stop_us = get_cur_time_us();
 
  total_cal_us     += stop_us - start_us;
  total_cal_cycles += stage_max;
 
  /* OK, let's collect some stats about the performance of this test case.
     This is used for fuzzing air time calculations in calculate_score(). */
 
  q->exec_us     = (stop_us - start_us) / stage_max;
  q->bitmap_size = count_bytes(trace_bits);
  q->handicap    = handicap;
  q->cal_failed  = 0;
 
  total_bitmap_size += q->bitmap_size;
  total_bitmap_entries++;
 
  update_bitmap_score(q);
 
  /* If this case didn't result in new output from the instrumentation, tell
     parent. This is a non-critical problem, but something to warn the user
     about. */
 
  if (!dumb_mode && first_run && !fault && !new_bits) fault = FAULT_NOBITS;
 
abort_calibration:
 
  if (new_bits == 2 && !q->has_new_cov) {
    q->has_new_cov = 1;
    queued_with_cov++;
  }
 
  /* Mark variable paths. */
 
  if (var_detected) {
 
    var_byte_count = count_bytes(var_bytes);
 
    if (!q->var_behavior) {
      mark_as_variable(q);
      queued_variable++;
    }
 
  }
 
  stage_name = old_sn;
  stage_cur  = old_sc;
  stage_max  = old_sm;
 
  if (!first_run) show_stats();
 
  return fault;
 
}
 
 
 
 
 
 
 
__afl_forkserver:
 
  /* Phone home and tell the parent that we're OK. */
 
  pushl $4          /* length    */
  pushl $__afl_temp /* data      */
  pushl $199        /* file desc */
  call  write
  addl  $12, %esp
 
__afl_fork_wait_loop:
 
  /* Wait for parent by reading from the pipe. This will block until
     the parent sends us something. Abort if read fails. */
 
  pushl $4          /* length    */
  pushl $__afl_temp /* data      */
  pushl $198        /* file desc */
  call  read
  addl  $12, %esp
 
  cmpl  $4, %eax
  jne   __afl_die
 
  /* Once woken up, create a clone of our process. */
 
  call fork
 
  cmpl $0, %eax
  jl   __afl_die
  je   __afl_fork_resume
 
  /* In parent process: write PID to pipe, then wait for child.
     Parent will handle timeouts and SIGKILL the child as needed. */
 
  movl  %eax, __afl_fork_pid
 
  pushl $4              /* length    */
  pushl $__afl_fork_pid /* data      */
  pushl $199            /* file desc */
  call  write
  addl  $12, %esp
 
  pushl $2             /* WUNTRACED */
  pushl $__afl_temp    /* status    */
  pushl __afl_fork_pid /* PID       */
  call  waitpid
  addl  $12, %esp
 
  cmpl  $0, %eax
  jle   __afl_die
 
  /* Relay wait status to pipe, then loop back. */
 
  pushl $4          /* length    */
  pushl $__afl_temp /* data      */
  pushl $199        /* file desc */
  call  write
  addl  $12, %esp
 
  jmp __afl_fork_wait_loop
 
__afl_fork_resume:
 
  /* In child process: close fds, resume execution. */
 
  pushl $198
  call  close
 
  pushl $199
  call  close
 
  addl  $8, %esp
  ret
__afl_forkserver:
 
  /* Phone home and tell the parent that we're OK. */
 
  pushl $4          /* length    */
  pushl $__afl_temp /* data      */
  pushl $199        /* file desc */
  call  write
  addl  $12, %esp
 
__afl_fork_wait_loop:
 
  /* Wait for parent by reading from the pipe. This will block until
     the parent sends us something. Abort if read fails. */
 
  pushl $4          /* length    */
  pushl $__afl_temp /* data      */
  pushl $198        /* file desc */
  call  read
  addl  $12, %esp
 
  cmpl  $4, %eax
  jne   __afl_die
 
  /* Once woken up, create a clone of our process. */
 
  call fork
 
  cmpl $0, %eax
  jl   __afl_die
  je   __afl_fork_resume
 
  /* In parent process: write PID to pipe, then wait for child.
     Parent will handle timeouts and SIGKILL the child as needed. */
 
  movl  %eax, __afl_fork_pid
 
  pushl $4              /* length    */
  pushl $__afl_fork_pid /* data      */
  pushl $199            /* file desc */
  call  write
  addl  $12, %esp
 
  pushl $2             /* WUNTRACED */
  pushl $__afl_temp    /* status    */
  pushl __afl_fork_pid /* PID       */
  call  waitpid
  addl  $12, %esp
 
  cmpl  $0, %eax
  jle   __afl_die
 
  /* Relay wait status to pipe, then loop back. */
 
  pushl $4          /* length    */
  pushl $__afl_temp /* data      */
  pushl $199        /* file desc */
  call  write
  addl  $12, %esp
 
  jmp __afl_fork_wait_loop
 
__afl_fork_resume:
 
  /* In child process: close fds, resume execution. */
 
  pushl $198
  call  close
 
  pushl $199
  call  close
 
  addl  $8, %esp
  ret
 
EXP_ST void init_forkserver(char** argv) {
 
  static struct itimerval it;
  int st_pipe[2], ctl_pipe[2];
  int status;
  s32 rlen;
 
  ACTF("Spinning up the fork server...");
 
  if (pipe(st_pipe) || pipe(ctl_pipe)) PFATAL("pipe() failed");
 
  forksrv_pid = fork();
 
  if (forksrv_pid < 0) PFATAL("fork() failed");
 
  if (!forksrv_pid) {
 
    struct rlimit r;
 
    /* Umpf. On OpenBSD, the default fd limit for root users is set to
       soft 128. Let's try to fix that... */
 
    if (!getrlimit(RLIMIT_NOFILE, &r) && r.rlim_cur < FORKSRV_FD + 2) {
 
      r.rlim_cur = FORKSRV_FD + 2;
      setrlimit(RLIMIT_NOFILE, &r); /* Ignore errors */
 
    }
 
    if (mem_limit) {
 
      r.rlim_max = r.rlim_cur = ((rlim_t)mem_limit) << 20;
 
#ifdef RLIMIT_AS
 
      setrlimit(RLIMIT_AS, &r); /* Ignore errors */
 
#else
 
      /* This takes care of OpenBSD, which doesn't have RLIMIT_AS, but
         according to reliable sources, RLIMIT_DATA covers anonymous
         maps - so we should be getting good protection against OOM bugs. */
 
      setrlimit(RLIMIT_DATA, &r); /* Ignore errors */
 
#endif /* ^RLIMIT_AS */
 
 
    }
 
    /* Dumping cores is slow and can lead to anomalies if SIGKILL is delivered
       before the dump is complete. */
 
    r.rlim_max = r.rlim_cur = 0;
 
    setrlimit(RLIMIT_CORE, &r); /* Ignore errors */
 
    /* Isolate the process and configure standard descriptors. If out_file is
       specified, stdin is /dev/null; otherwise, out_fd is cloned instead. */
 
    setsid();
 
    dup2(dev_null_fd, 1);
    dup2(dev_null_fd, 2);
 
    if (out_file) {
 
      dup2(dev_null_fd, 0);
 
    } else {
 
      dup2(out_fd, 0);
      close(out_fd);
 
    }
 
    /* Set up control and status pipes, close the unneeded original fds. */
 
    if (dup2(ctl_pipe[0], FORKSRV_FD) < 0) PFATAL("dup2() failed");
    if (dup2(st_pipe[1], FORKSRV_FD + 1) < 0) PFATAL("dup2() failed");
 
    close(ctl_pipe[0]);
    close(ctl_pipe[1]);
    close(st_pipe[0]);
    close(st_pipe[1]);
 
    close(out_dir_fd);
    close(dev_null_fd);
    close(dev_urandom_fd);
    close(fileno(plot_file));
 
    /* This should improve performance a bit, since it stops the linker from
       doing extra work post-fork(). */
 
    if (!getenv("LD_BIND_LAZY")) setenv("LD_BIND_NOW", "1", 0);
 
    /* Set sane defaults for ASAN if nothing else specified. */
 
    setenv("ASAN_OPTIONS", "abort_on_error=1:"
                           "detect_leaks=0:"
                           "symbolize=0:"
                           "allocator_may_return_null=1", 0);
 
    /* MSAN is tricky, because it doesn't support abort_on_error=1 at this
       point. So, we do this in a very hacky way. */
 
    setenv("MSAN_OPTIONS", "exit_code=" STRINGIFY(MSAN_ERROR) ":"
                           "symbolize=0:"
                           "abort_on_error=1:"
                           "allocator_may_return_null=1:"
                           "msan_track_origins=0", 0);
 
    execv(target_path, argv);
 
    /* Use a distinctive bitmap signature to tell the parent about execv()
       falling through. */
 
    *(u32*)trace_bits = EXEC_FAIL_SIG;
    exit(0);
 
  }
 
  /* Close the unneeded endpoints. */
 
  close(ctl_pipe[0]);
  close(st_pipe[1]);
 
  fsrv_ctl_fd = ctl_pipe[1];
  fsrv_st_fd  = st_pipe[0];
 
  /* Wait for the fork server to come up, but don't wait too long. */
 
  it.it_value.tv_sec = ((exec_tmout * FORK_WAIT_MULT) / 1000);
  it.it_value.tv_usec = ((exec_tmout * FORK_WAIT_MULT) % 1000) * 1000;
 
  setitimer(ITIMER_REAL, &it, NULL);
 
  rlen = read(fsrv_st_fd, &status, 4);
 
  it.it_value.tv_sec = 0;
  it.it_value.tv_usec = 0;
 
  setitimer(ITIMER_REAL, &it, NULL);
 
  /* If we have a four-byte "hello" message from the server, we're all set.
     Otherwise, try to figure out what went wrong. */
 
  if (rlen == 4) {
    OKF("All right - fork server is up.");
    return;
  }
 
  if (child_timed_out)
    FATAL("Timeout while initializing fork server (adjusting -t may help)");
 
  if (waitpid(forksrv_pid, &status, 0) <= 0)
    PFATAL("waitpid() failed");
 
  if (WIFSIGNALED(status)) {
 
    if (mem_limit && mem_limit < 500 && uses_asan) {
 
      SAYF("\n" cLRD "[-] " cRST
           "Whoops, the target binary crashed suddenly, before receiving any input\n"
           "    from the fuzzer! Since it seems to be built with ASAN and you have a\n"
           "    restrictive memory limit configured, this is expected; please read\n"
           "    %s/notes_for_asan.txt for help.\n", doc_path);
 
    } else if (!mem_limit) {
 
      SAYF("\n" cLRD "[-] " cRST
           "Whoops, the target binary crashed suddenly, before receiving any input\n"
           "    from the fuzzer! There are several probable explanations:\n\n"
 
           "    - The binary is just buggy and explodes entirely on its own. If so, you\n"
           "      need to fix the underlying problem or find a better replacement.\n\n"
 
#ifdef __APPLE__
 
           "    - On MacOS X, the semantics of fork() syscalls are non-standard and may\n"
           "      break afl-fuzz performance optimizations when running platform-specific\n"
           "      targets. To fix this, set AFL_NO_FORKSRV=1 in the environment.\n\n"
 
#endif /* __APPLE__ */
 
           "    - Less likely, there is a horrible bug in the fuzzer. If other options\n"
           "      fail, poke <lcamtuf@coredump.cx> for troubleshooting tips.\n");
 
    } else {
 
      SAYF("\n" cLRD "[-] " cRST
           "Whoops, the target binary crashed suddenly, before receiving any input\n"
           "    from the fuzzer! There are several probable explanations:\n\n"
 
           "    - The current memory limit (%s) is too restrictive, causing the\n"
           "      target to hit an OOM condition in the dynamic linker. Try bumping up\n"
           "      the limit with the -m setting in the command line. A simple way confirm\n"
           "      this diagnosis would be:\n\n"
 
#ifdef RLIMIT_AS
           "      ( ulimit -Sv $[%llu << 10]; /path/to/fuzzed_app )\n\n"
#else
           "      ( ulimit -Sd $[%llu << 10]; /path/to/fuzzed_app )\n\n"
#endif /* ^RLIMIT_AS */
 
           "      Tip: you can use http://jwilk.net/software/recidivm to quickly\n"
           "      estimate the required amount of virtual memory for the binary.\n\n"
 
           "    - The binary is just buggy and explodes entirely on its own. If so, you\n"
           "      need to fix the underlying problem or find a better replacement.\n\n"
 
#ifdef __APPLE__
 
           "    - On MacOS X, the semantics of fork() syscalls are non-standard and may\n"
           "      break afl-fuzz performance optimizations when running platform-specific\n"
           "      targets. To fix this, set AFL_NO_FORKSRV=1 in the environment.\n\n"
 
#endif /* __APPLE__ */
 
           "    - Less likely, there is a horrible bug in the fuzzer. If other options\n"
           "      fail, poke <lcamtuf@coredump.cx> for troubleshooting tips.\n",
           DMS(mem_limit << 20), mem_limit - 1);
 
    }
 
    FATAL("Fork server crashed with signal %d", WTERMSIG(status));
 
  }
 
  if (*(u32*)trace_bits == EXEC_FAIL_SIG)
    FATAL("Unable to execute target application ('%s')", argv[0]);
 
  if (mem_limit && mem_limit < 500 && uses_asan) {
 
    SAYF("\n" cLRD "[-] " cRST
           "Hmm, looks like the target binary terminated before we could complete a\n"
           "    handshake with the injected code. Since it seems to be built with ASAN and\n"
           "    you have a restrictive memory limit configured, this is expected; please\n"
           "    read %s/notes_for_asan.txt for help.\n", doc_path);
 
  } else if (!mem_limit) {
 
    SAYF("\n" cLRD "[-] " cRST
         "Hmm, looks like the target binary terminated before we could complete a\n"
         "    handshake with the injected code. Perhaps there is a horrible bug in the\n"
         "    fuzzer. Poke <lcamtuf@coredump.cx> for troubleshooting tips.\n");
 
  } else {
 
    SAYF("\n" cLRD "[-] " cRST
         "Hmm, looks like the target binary terminated before we could complete a\n"
         "    handshake with the injected code. There are %s probable explanations:\n\n"
 
         "%s"
         "    - The current memory limit (%s) is too restrictive, causing an OOM\n"
         "      fault in the dynamic linker. This can be fixed with the -m option. A\n"
         "      simple way to confirm the diagnosis may be:\n\n"
 
#ifdef RLIMIT_AS
         "      ( ulimit -Sv $[%llu << 10]; /path/to/fuzzed_app )\n\n"
#else
         "      ( ulimit -Sd $[%llu << 10]; /path/to/fuzzed_app )\n\n"
#endif /* ^RLIMIT_AS */
 
         "      Tip: you can use http://jwilk.net/software/recidivm to quickly\n"
         "      estimate the required amount of virtual memory for the binary.\n\n"
 
         "    - Less likely, there is a horrible bug in the fuzzer. If other options\n"
         "      fail, poke <lcamtuf@coredump.cx> for troubleshooting tips.\n",
         getenv(DEFER_ENV_VAR) ? "three" : "two",
         getenv(DEFER_ENV_VAR) ?
         "    - You are using deferred forkserver, but __AFL_INIT() is never\n"
         "      reached before the program terminates.\n\n" : "",
         DMS(mem_limit << 20), mem_limit - 1);
 
  }
 
  FATAL("Fork server handshake failed");
 
}
EXP_ST void init_forkserver(char** argv) {
 
  static struct itimerval it;
  int st_pipe[2], ctl_pipe[2];
  int status;
  s32 rlen;
 
  ACTF("Spinning up the fork server...");
 
  if (pipe(st_pipe) || pipe(ctl_pipe)) PFATAL("pipe() failed");
 
  forksrv_pid = fork();
 
  if (forksrv_pid < 0) PFATAL("fork() failed");
 
  if (!forksrv_pid) {
 
    struct rlimit r;
 
    /* Umpf. On OpenBSD, the default fd limit for root users is set to
       soft 128. Let's try to fix that... */
 
    if (!getrlimit(RLIMIT_NOFILE, &r) && r.rlim_cur < FORKSRV_FD + 2) {
 
      r.rlim_cur = FORKSRV_FD + 2;
      setrlimit(RLIMIT_NOFILE, &r); /* Ignore errors */
 
    }
 
    if (mem_limit) {
 
      r.rlim_max = r.rlim_cur = ((rlim_t)mem_limit) << 20;
 
#ifdef RLIMIT_AS
 
      setrlimit(RLIMIT_AS, &r); /* Ignore errors */
 
#else
 
      /* This takes care of OpenBSD, which doesn't have RLIMIT_AS, but
         according to reliable sources, RLIMIT_DATA covers anonymous
         maps - so we should be getting good protection against OOM bugs. */
 
      setrlimit(RLIMIT_DATA, &r); /* Ignore errors */
 
#endif /* ^RLIMIT_AS */
 
 
    }
 
    /* Dumping cores is slow and can lead to anomalies if SIGKILL is delivered
       before the dump is complete. */
 
    r.rlim_max = r.rlim_cur = 0;
 
    setrlimit(RLIMIT_CORE, &r); /* Ignore errors */
 
    /* Isolate the process and configure standard descriptors. If out_file is
       specified, stdin is /dev/null; otherwise, out_fd is cloned instead. */
 
    setsid();
 
    dup2(dev_null_fd, 1);
    dup2(dev_null_fd, 2);
 
    if (out_file) {
 
      dup2(dev_null_fd, 0);
 
    } else {
 
      dup2(out_fd, 0);
      close(out_fd);
 
    }
 
    /* Set up control and status pipes, close the unneeded original fds. */
 
    if (dup2(ctl_pipe[0], FORKSRV_FD) < 0) PFATAL("dup2() failed");
    if (dup2(st_pipe[1], FORKSRV_FD + 1) < 0) PFATAL("dup2() failed");
 
    close(ctl_pipe[0]);
    close(ctl_pipe[1]);
    close(st_pipe[0]);
    close(st_pipe[1]);
 
    close(out_dir_fd);
    close(dev_null_fd);
    close(dev_urandom_fd);
    close(fileno(plot_file));
 
    /* This should improve performance a bit, since it stops the linker from
       doing extra work post-fork(). */
 
    if (!getenv("LD_BIND_LAZY")) setenv("LD_BIND_NOW", "1", 0);
 
    /* Set sane defaults for ASAN if nothing else specified. */
 
    setenv("ASAN_OPTIONS", "abort_on_error=1:"
                           "detect_leaks=0:"
                           "symbolize=0:"
                           "allocator_may_return_null=1", 0);
 
    /* MSAN is tricky, because it doesn't support abort_on_error=1 at this
       point. So, we do this in a very hacky way. */
 
    setenv("MSAN_OPTIONS", "exit_code=" STRINGIFY(MSAN_ERROR) ":"
                           "symbolize=0:"
                           "abort_on_error=1:"
                           "allocator_may_return_null=1:"
                           "msan_track_origins=0", 0);
 
    execv(target_path, argv);
 
    /* Use a distinctive bitmap signature to tell the parent about execv()
       falling through. */
 
    *(u32*)trace_bits = EXEC_FAIL_SIG;
    exit(0);
 
  }
 
  /* Close the unneeded endpoints. */
 
  close(ctl_pipe[0]);
  close(st_pipe[1]);
 
  fsrv_ctl_fd = ctl_pipe[1];
  fsrv_st_fd  = st_pipe[0];
 
  /* Wait for the fork server to come up, but don't wait too long. */
 
  it.it_value.tv_sec = ((exec_tmout * FORK_WAIT_MULT) / 1000);
  it.it_value.tv_usec = ((exec_tmout * FORK_WAIT_MULT) % 1000) * 1000;
 
  setitimer(ITIMER_REAL, &it, NULL);
 
  rlen = read(fsrv_st_fd, &status, 4);
 
  it.it_value.tv_sec = 0;
  it.it_value.tv_usec = 0;
 
  setitimer(ITIMER_REAL, &it, NULL);
 
  /* If we have a four-byte "hello" message from the server, we're all set.
     Otherwise, try to figure out what went wrong. */
 
  if (rlen == 4) {
    OKF("All right - fork server is up.");
    return;
  }
 
  if (child_timed_out)
    FATAL("Timeout while initializing fork server (adjusting -t may help)");
 
  if (waitpid(forksrv_pid, &status, 0) <= 0)
    PFATAL("waitpid() failed");
 
  if (WIFSIGNALED(status)) {
 
    if (mem_limit && mem_limit < 500 && uses_asan) {
 
      SAYF("\n" cLRD "[-] " cRST
           "Whoops, the target binary crashed suddenly, before receiving any input\n"
           "    from the fuzzer! Since it seems to be built with ASAN and you have a\n"
           "    restrictive memory limit configured, this is expected; please read\n"
           "    %s/notes_for_asan.txt for help.\n", doc_path);
 
    } else if (!mem_limit) {
 
      SAYF("\n" cLRD "[-] " cRST
           "Whoops, the target binary crashed suddenly, before receiving any input\n"
           "    from the fuzzer! There are several probable explanations:\n\n"
 
           "    - The binary is just buggy and explodes entirely on its own. If so, you\n"
           "      need to fix the underlying problem or find a better replacement.\n\n"
 
#ifdef __APPLE__
 
           "    - On MacOS X, the semantics of fork() syscalls are non-standard and may\n"
           "      break afl-fuzz performance optimizations when running platform-specific\n"
           "      targets. To fix this, set AFL_NO_FORKSRV=1 in the environment.\n\n"
 
#endif /* __APPLE__ */
 
           "    - Less likely, there is a horrible bug in the fuzzer. If other options\n"
           "      fail, poke <lcamtuf@coredump.cx> for troubleshooting tips.\n");
 
    } else {
 
      SAYF("\n" cLRD "[-] " cRST
           "Whoops, the target binary crashed suddenly, before receiving any input\n"
           "    from the fuzzer! There are several probable explanations:\n\n"
 
           "    - The current memory limit (%s) is too restrictive, causing the\n"
           "      target to hit an OOM condition in the dynamic linker. Try bumping up\n"
           "      the limit with the -m setting in the command line. A simple way confirm\n"
           "      this diagnosis would be:\n\n"
 
#ifdef RLIMIT_AS
           "      ( ulimit -Sv $[%llu << 10]; /path/to/fuzzed_app )\n\n"
#else
           "      ( ulimit -Sd $[%llu << 10]; /path/to/fuzzed_app )\n\n"
#endif /* ^RLIMIT_AS */
 
           "      Tip: you can use http://jwilk.net/software/recidivm to quickly\n"
           "      estimate the required amount of virtual memory for the binary.\n\n"
 
           "    - The binary is just buggy and explodes entirely on its own. If so, you\n"
           "      need to fix the underlying problem or find a better replacement.\n\n"
 
#ifdef __APPLE__
 
           "    - On MacOS X, the semantics of fork() syscalls are non-standard and may\n"
           "      break afl-fuzz performance optimizations when running platform-specific\n"
           "      targets. To fix this, set AFL_NO_FORKSRV=1 in the environment.\n\n"
 
#endif /* __APPLE__ */
 
           "    - Less likely, there is a horrible bug in the fuzzer. If other options\n"
           "      fail, poke <lcamtuf@coredump.cx> for troubleshooting tips.\n",
           DMS(mem_limit << 20), mem_limit - 1);
 
    }
 
    FATAL("Fork server crashed with signal %d", WTERMSIG(status));
 
  }
 
  if (*(u32*)trace_bits == EXEC_FAIL_SIG)
    FATAL("Unable to execute target application ('%s')", argv[0]);
 
  if (mem_limit && mem_limit < 500 && uses_asan) {
 
    SAYF("\n" cLRD "[-] " cRST
           "Hmm, looks like the target binary terminated before we could complete a\n"
           "    handshake with the injected code. Since it seems to be built with ASAN and\n"
           "    you have a restrictive memory limit configured, this is expected; please\n"
           "    read %s/notes_for_asan.txt for help.\n", doc_path);
 
  } else if (!mem_limit) {
 
    SAYF("\n" cLRD "[-] " cRST
         "Hmm, looks like the target binary terminated before we could complete a\n"
         "    handshake with the injected code. Perhaps there is a horrible bug in the\n"
         "    fuzzer. Poke <lcamtuf@coredump.cx> for troubleshooting tips.\n");
 
  } else {
 
    SAYF("\n" cLRD "[-] " cRST
         "Hmm, looks like the target binary terminated before we could complete a\n"
         "    handshake with the injected code. There are %s probable explanations:\n\n"
 
         "%s"
         "    - The current memory limit (%s) is too restrictive, causing an OOM\n"
         "      fault in the dynamic linker. This can be fixed with the -m option. A\n"
         "      simple way to confirm the diagnosis may be:\n\n"
 
#ifdef RLIMIT_AS
         "      ( ulimit -Sv $[%llu << 10]; /path/to/fuzzed_app )\n\n"
#else
         "      ( ulimit -Sd $[%llu << 10]; /path/to/fuzzed_app )\n\n"

[招生]科锐逆向工程师培训(2024年11月15日实地,远程教学同时开班, 第51期)

收藏
免费 8
支持
分享
最新回复 (3)
雪    币: 14517
活跃值: (17538)
能力值: ( LV12,RANK:290 )
在线值:
发帖
回帖
粉丝
2
感谢分享
2023-4-7 09:57
0
雪    币: 3059
活跃值: (30876)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
3
感谢分享
2023-4-13 09:09
1
雪    币: 1804
活跃值: (1225)
能力值: ( LV5,RANK:70 )
在线值:
发帖
回帖
粉丝
4
迫于生活压力太真实了,hh。
2023-4-24 11:20
0
游客
登录 | 注册 方可回帖
返回
//