Profile-guided System Optimizations for Accelerated Greybox Fuzzing
@inproceedings{10.1145/3576915.3616636,
author = {Zhang, Yunhang and Pang, Chengbin and Nagy, Stefan and Chen, Xun and Xu, Jun},
title = {Profile-guided System Optimizations for Accelerated Greybox Fuzzing},
year = {2023},
isbn = {9798400700507},
publisher = {Association for Computing Machinery},
address = {New York, NY, USA},
url = {https://doi.org/10.1145/3576915.3616636},
doi = {10.1145/3576915.3616636},
booktitle = {Proceedings of the 2023 ACM SIGSAC Conference on Computer and Communications Security},
pages = {1257–1271},
numpages = {15},
keywords = {greybox fuzzing, profile-driven, system optimizations},
location = {Copenhagen, Denmark},
series = {CCS '23}
}
The project consists of three main subdirectories:
- AFL : Improved version of AFL based on 2.57b gcc
AFL/llvm_mode: code for "Profile-guided State Recovery".AFL/var_mode: scripts for "Profile-guided State Recovery".AFL/gcc_mode: gcc mode for AFL.
- AFLplusplus: Improved version of AFL++ based on 4.01c
AFLplusplus/instrumentation/afl-llvm-var-rec.so.cc: code for "Profile-guided State Recovery".AFLplusplus/utils/var_mode: scripts for "Profile-guided State Recovery".
- abstractFS: code for "Profile-guided OS Abstraction"
FS: fork server, the default build method of target binarySS: snapshot mode, which was implemented in Linux Kernel in AFL, and LKM in AFL++PM: persistent modePM_REC: persistent mode with profile-guided state recoveryPM_VOS: persistent mode on top of our profile-guided OS abstraction.PM_REC_VOS: persistent mode with both profile-guided state recovery and OS abstraction.
The following macros in the code are used to enable the modes above.
VAR_RECinAFL/llvm_mode/afl-llvm-pass.so.cc: required byPM_RECandPM_REC_VOSfor AFLAFL_LLVM_VAR_RECinAFLplusplus/instrumentation/afl-llvm-var-rec.so.cc: required byPM_RECandPM_REC_VOSfor AFL++AFL_SNAPSHOT: enable snapshot kernel mode (AFL/llvm_mode/afl-llvm-rt.o.c)AFL_RT_VAR_REC: enable variable recover mode (AFL/llvm_mode/afl-llvm-rt.o.c)AFL_RT_PERSIST: enable persistent mode with wrap forexit(AFL/llvm_mode/afl-llvm-rt.o.c)
For profiling of time consumed by fuzzing (2.2 Motivating Study), you need to enable these macros : PROFILING_SYS_USR, PROFILING_FORK and PROFILING, which are located in these files:
AFL/afl-fuzz.cAFL/llvm_mode/afl-llvm-rt.o.cAFLplusplus/src/afl-fuzz.cAFLplusplus/instrumentation/afl-compiler-rt.o.c
AFL:
make && cd llvm_mode && make
AFLplusplus:
make distrib
abstractFS: Please refer to this separate README
VAR_REC: enable variable recovery mode for LLVM pass inAFL/llvm_mode/afl-llvm-pass.so.cc; and the macro wasAFL_LLVM_VAR_RECin AFL++.PROFILING_SYS_USR,PROFILING_FORKandPROFILINGwas for profiling of time.AFL_SNAPSHOT: enable snapshot kernel modeAFL_RT_VAR_REC: enable variable recover modeAFL_RT_PERSIST: enable persistent mode with wrap forexit.
- Edit
AFL/llvm_mode/afl-llvm-rt.o.cand enable macros(starts withPROFILING) from line 47 to line 49 .(Edit line 47 to line 49 inAFLplusplus/instrumentation/afl-compiler-rt.o.cfor AFLplusplus) - Buid AFL with
export CFLAGS="-DPROFILING_SYS_USR=1 -DPROFILING=1 -DPROFILING_FORK=1" - Rebuild the target program with
afl-clang-fast - Fuzz the target program with environment variable
AFL_PERFORM_DRY_RUN_ONLY=1, and the input seeds can be an existing corpus.
The following uses objdump as an example:
Option 1: Build from source code:
cd AFL
make clean
CFLAGS="-DPROFILING_SYS_USR=1 -DPROFILING=1 -DPROFILING_FORK=1" make
cd llvm_mode
# enable macros that start with PROFILING
make
sudo make install
sudo mkdir -p /out/
# Download the target src:
wget -c https://d29yy0w4awjqw3.cloudfront.net/fuzz/afl/Dec/binutils-2.38-per.tar.gz
tar -xzf binutils-2.38-per.tar.gz
# PM by default
cd binutils-2.38
CC=afl-clang-fast ./configure --disable-shared
make -j4
sudo cp binutils/objdump /out/objdumpOption 2: Build with Docker:
cd AFL/benchmarks/binutils-2.38
docker build -t benchmark-afl-objdump .
docker run --privileged --shm-size=256m --rm -it benchmark-afl-objdump bash
MODE=profile bash build.sh
There is an tarball of corpus as a sample .
AFL_NO_AFFINITY=1 AFL_PERFORM_DRY_RUN_ONLY=1 afl-fuzz -i /out/objdump_corpus -o /dev/shm/output_objdump -f /dev/shm/afl_bin_input /out/objdump -d @@
# results FYI
Profiling information:
7545 ms total work, 151133 ns/work,
33129 ms total running, 663538 ns/run,
1487 ms total write testcase, 29794 ns/write
3005 ms total forking, 60192 ns/fork,
28235 ms total purely run, 565523 ns/purely run
44263 ns/system running, 503925 ns/user running
7545 ms total pre-fuzzing, 151133 ns/pre-fuzzing,
total execution is 49928
A full example of result for profiling time distribution page fault and syscall of objdump: Link
Collectiong Global Objects:
- Rebuild target program with
afl-clang-fastvariableAFL_VAR_TRACE=1 - Run target with existing corpus to collect variable info (e.g.
for testcase in /out/output_objdump/queue/id\:0* ; do ./objdump -d $testcase ; done) - The
/tmp/.fs_globals.txtfile will be verbose information for variable. (cat /tmp/.fs_globals.txt | sort -u > /out/global_objdump.txt)
Let's take objdump as an example:
cd binutils-2.38
CC=afl-clang-fast ./configure --disable-shared
make clean
AFL_VAR_TRACE=1 make -j4
# collect the variable information
# NOTE: the corpus_objdump the AFL queue with saved test cases in it
for testcase in ./corpus_objdump/queue/id\:0* ; do ./objdump -s $testcase ; done
cat /tmp/.fs_globals.txt | sort -u > /out/global_objdump.txt`Rebuid Target Binary with state recovery(PM_REC)
Note: Setting AFL_VAR_REC=1 environment variable is required for AFL++.
- Rebuild target program with variable
AFL_VAR_SNAPSHOT=1 - Generate the assembly code from variable information:
python AFL/var_mode/gen_asm.py /path/to/target_binary /out/global_objdump.txt /tmp/var.s - Comple
.sto.o:cd AFL/var_mode && gcc -c /tmp/var.s - Rebuild target program with environment variable
AFL_VAR_SNAPSHOT=1 AFL_VAR_REC=objdump(target binary name)
Let's take objdump as an example:
# generate the PM_REC object
cd binutils-2.38
CC=afl-clang-fast ./configure --disable-shared
make clean
AFL_VAR_SNAPSHOT=1 make -j4
# /out/global_objdump.txt is the txt file with global info we collected above
python AFL/var_mode/gen_asm.py ./binutils/objdump /out/global_objdump.txt /tmp/var.s
# Just compile the var.s, and we will get var.o
cd AFL/var_mode && gcc -c /tmp/var.s
# rebuild the AFL llvm runtime object
cd AFL/llvm_mode
# enable "AFL_RT_VAR_REC" macro at AFL/llvm_mode/afl-llvm-rt.o.c to enable the state recover mode
make clean
make # It's OK to get the testing warning prompt, pls ignore it.
cd .. && sudo make install
# rebuilt the target binary
cd /Path/to/binutils-2.38
LDFLAGS="-Wl,-wrap,_exit -Wl,-wrap,exit -Wl,-wrap,_Exit" AFL_VAR_REC=nothing ./configure --disable-shared
make clean
AFL_VAR_REC=objdump AFL_VAR_SNAPSHOT=1 make -j4
# sudo cp -f binutils/objdump /out/objdumpThen, we will get a PM_REC version of objdump, and it can be verified with the 100% fuzzing stability in persistent mode.
afl-fuzz -i /out/seeds -o /dev/shm/output_objdump -f /dev/shm/afl_input -m none -- /out/objdump -d @@Run Target with "profile-guided OS abstraction(VOS)"
Follow the README for abstractFS, and set FS_AFL_SHM_ID to enable PM_VOS or PM_REC_VOS
e.g.
FS_AFL_SHM_ID=6789 afl-fuzz -i /out/seeds -o /dev/shm/output_objdump -f /dev/shm/afl_input -m none -- /out/objdump -d @@