(技术积累)AFL++ Instrumention for WAVM

(技术积累)AFL++ Instrumention for WAVM

​ 利用AFL++自带的compiler warpper对WAVM插桩,一些Bug的记录分析。

Instrumention

cmake ../ -G "Unix Makefiles" -DCMAKE_C_COMPILER=/home/wx/Shaw/discrepancy_cases_study/compdiff/compilers/diff-cc-1  -DCMAKE_CXX_COMPILER=/home/wx/Shaw/discrepancy_cases_study/compdiff/compilers/diff-cxx-1 -DLLVM_DIR=/home/wx/Shaw/discrepancy_cases_study/compdiff/llvm-project/install/lib/cmake/llvm

Bugs

1. AFL++崩溃

​ 使用afl-gcc-fast编译WAVM,然后放到修改后的AFL中运行。程序每次都在total exec=14时退出并报错:“segmentation fault”,使用GDB调试发现如下信息:

​ 问题定位到differential_compilers()函数第一次运行diff forkserver时,其afl_fsrv_run_target()函数中使用的memset()函数:

in afl_fsrv_run_target()

​ 但这里的问题就是,如果本次测试运行指定了参数-y 0,也就是不使用diff forkserver,而在differential_compilers()函数中:

错误原因:即使没有设置diff forkserver,这里还是会尝试运行afl->diff_fsrv[0],所以后续对一块没有分配过的内存进行memset操作就会出错。这里属于是对代码边界条件的判断错误。

​ 在differential_compilers()函数内加上条件判断,重新编译即可解决:

2. AFL++卡死

​ 修复完上面的Bug后,使用如下命令测试CompDiff在新版AFL上的功能:

./afl-fuzz -y 1 -i /home/wx/Shaw/idea_test/libtiff/seed -o /home/wx/Shaw/idea_test/out1 -J "out.file" -- /home/wx/Shaw/idea_test/libtiff/tools/tiffcp -M -i @@ out.file

​ 发现其每次都在total execs达到200左右时卡住,然后显示如下错误:

​ GDB调试发现程序在第一次进入differential_compilers()函数就会出错,也就是trim阶段刚结束,正式开始运行时。继续调试,发现在主函数afl-fuzz.c中,afl_fsrv_start_diff()相关代码根本就没有运行:

​ 当程序运行到afl-fuzz.c的2189行时,查看其条件判断,发现环境变量AFL_SKIP_BIN_CHECK被设置为1,导致这里直接跳过了diff forkserver的初始化过程:

​ 这是由于之前复现WAFL时使用的环境变量AFL_SKIP_BIN_CHECK还留存在了同一个bash中,导致了程序直接跳过了部分代码。

​ 并且,由于在diff-afl-forkserver.c中的read_s32_timed()函数中,从子进程读取时读出的长度是0:

​ 这是由于这里使用的是原生的afl-gcc-fast编译的程序,其插桩时还是按照198,199的文件描述符与forkserver通信,而在diff forkserver的定义中,其是从202开始的,diff 0应该从202开始与forkserver通信才对:

错误原因:

     1. 错误使用环境变量`AFL_SKIP_BIN_CHECK`导致diff forkserver根本没有初始化;
     2. 没有使用diff as对target插桩,导致fuzz target使用了错误的管道。

​ 故这里不要用原生AFL编译器编译的target,直接使用CompDiff提供的编译器编译的二进制文件即可:

./afl-fuzz -y 1 -i /home/wx/Shaw/discrepancy_cases_study/compdiff/examples/libtiff/seeds -o /home/wx/Shaw/idea_test/out -J "out.file" -- /home/wx/Shaw/idea_test/tiffcp -M -i @@ out.file

3. 按Ctrl+C后程序结束,但不返回bash

./afl-fuzz -y 1 -i /home/wx/Shaw/discrepancy_cases_study/compdiff/examples/libtiff/seeds -o /home/wx/Shaw/idea_test/out -J "out.file" -- /home/wx/Shaw/idea_test/tiffcp -M -i @@ out.file


./afl-fuzz -y 1 -i /home/wx/Shaw/idea_test/in -o /home/wx/Shaw/idea_test/out  -- /home/wx/Shaw/idea_test/wavm run /home/wx/Shaw/idea_c_test.wasm 

./afl-fuzz -y 2 -i /home/wx/Shaw/idea_test/in -o /home/wx/Shaw/idea_test/out  -- /home/wx/Shaw/idea_test/test-instr -i @@ -b

4. Without wasm:检测不到discrepancy

​ 使用如下测试程序来测试CompDiff:

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

int main(int argc, char** argv) {
  char buf[8];
  if (read(0, buf, 8) < 1) {
    printf("Hum?\n");
    exit(1);
  }

  if (buf[0] == '0')
    // printf("I'm native one!\n");
    // printf("I'm wasm one!\n");
  else
    printf("A non-zero value? How quaint!\n");
  exit(0);
}

​ 当buf[0]=’0’时,原始程序和待测试程序分别会打印不同的内容,但测试过程中diff目录没有任何记录。

错误原因1:原生的binary不参与discrenpancy的比较,只负责引导fuzzing。

​ 查看differential_compilers()代码(DiffComp比较-核心逻辑-%E4%BB%A3%E7%A0%81%E5%88%86%E6%9E%90/#stardiffcomp%E6%AF%94%E8%BE%83-%E6%A0%B8%E5%BF%83%E9%80%BB%E8%BE%91))可知,CompDiff先运行一次diff_forkserver 0,然后依次运行后续的diff forkserver,每运行一个就与最开始运行的diff_forkserver 0的结果比较,如果有不同就记录discrepancy。

​ 故这里不能指定-y为1,不然只有一个diff forkserver没有办法比较,正确做法是指定2个,然后用上述的compiler warpper包装它。

./afl-fuzz -y 2 -i /home/wx/Shaw/idea_test/in -o /home/wx/Shaw/idea_test/out  -- /home/wx/Shaw/idea_test/test-instr

​ 这样就可以成功检测到discrepancy:

5. With wasm:检测不到discrepancy

​ 在修正上述问题后,将diff forkserver 0设置为native binary,将diff forkserver 1设置为wasm binary,并修改forkserver的out_file参数和target(将target变为wavm),程序可以成功运行,但是检测不到discrepancy。

./afl-fuzz -i /home/wx/Shaw/idea_test/in -o /home/wx/Shaw/idea_test/out  -- /home/wx/Shaw/idea_test/test-instr

​ 而且注意到在测试的过程中 ,.cur_output0文件(native binary输出文件)一直有输出,但.cur_output1(wasm binary输出文件)一直是空的:

问题原因:wavm运行时间太长,导致程序运行diff forkserver 1时直接退出了。

​ 这里将diff forkserver 1的运行时间延长10倍(也就是跟forkserver初始化的时间相同),即可解决:

From differential_compilers()

6. last new find总是等于run time

​ 在测试过程中,发现输出的last new find总是等于run time,这就意味着每时每刻都在发现新cov: