Merge pull request #1759 from AFLplusplus/dev

Dev
This commit is contained in:
van Hauser
2023-06-06 17:36:04 +03:00
committed by GitHub
122 changed files with 5472 additions and 2555 deletions

View File

@ -24,7 +24,7 @@ import importlib.metadata
# string_re = re.compile('(\\"(\\\\.|[^"\\\\])*\\")') # TODO: for future use # string_re = re.compile('(\\"(\\\\.|[^"\\\\])*\\")') # TODO: for future use
CURRENT_LLVM = os.getenv('LLVM_VERSION', 14) CURRENT_LLVM = os.getenv('LLVM_VERSION', 15)
CLANG_FORMAT_BIN = os.getenv("CLANG_FORMAT_BIN", "") CLANG_FORMAT_BIN = os.getenv("CLANG_FORMAT_BIN", "")

View File

@ -14,7 +14,7 @@ jobs:
runs-on: "${{ matrix.os }}" runs-on: "${{ matrix.os }}"
strategy: strategy:
matrix: matrix:
os: [ubuntu-22.04, ubuntu-20.04, ubuntu-18.04] os: [ubuntu-22.04, ubuntu-20.04]
env: env:
AFL_SKIP_CPUFREQ: 1 AFL_SKIP_CPUFREQ: 1
AFL_I_DONT_CARE_ABOUT_MISSING_CRASHES: 1 AFL_I_DONT_CARE_ABOUT_MISSING_CRASHES: 1
@ -36,23 +36,23 @@ jobs:
run: make distrib ASAN_BUILD=1 NO_NYX=1 run: make distrib ASAN_BUILD=1 NO_NYX=1
- name: run tests - name: run tests
run: sudo -E ./afl-system-config; make tests run: sudo -E ./afl-system-config; make tests
macos: # macos:
runs-on: macOS-latest # runs-on: macOS-latest
env: # env:
AFL_MAP_SIZE: 65536 # AFL_MAP_SIZE: 65536
AFL_SKIP_CPUFREQ: 1 # AFL_SKIP_CPUFREQ: 1
AFL_I_DONT_CARE_ABOUT_MISSING_CRASHES: 1 # AFL_I_DONT_CARE_ABOUT_MISSING_CRASHES: 1
steps: # steps:
- uses: actions/checkout@v3 # - uses: actions/checkout@v3
- name: install # - name: install
run: brew install make gcc llvm # run: brew install make gcc llvm
- name: fix install # - name: fix install
run: cd /usr/local/bin; ln -s gcc-11 gcc; ln -s g++-11 g++; which gcc; gcc -v # run: cd /usr/local/bin; ln -s gcc-11 gcc; ln -s g++-11 g++; which gcc; gcc -v
- name: build # - name: build
run: export PATH=/usr/local/Cellar/llvm/*/":$PATH"; export CC=/usr/local/Cellar/llvm/*/bin/clang; export CXX="$CC"++; export LLVM_CONFIG=/usr/local/Cellar/llvm/*/bin/llvm-config; sudo -E ./afl-system-config; gmake ASAN_BUILD=1 # run: export PATH=/usr/local/Cellar/llvm/*/":$PATH"; export CC=/usr/local/Cellar/llvm/*/bin/clang; export CXX="$CC"++; export LLVM_CONFIG=/usr/local/Cellar/llvm/*/bin/llvm-config; sudo -E ./afl-system-config; gmake ASAN_BUILD=1
- name: frida # - name: frida
run: export CC=/usr/local/Cellar/llvm/*/bin/clang; export CXX="$CC"++; cd frida_mode; gmake # run: export CC=/usr/local/Cellar/llvm/*/bin/clang; export CXX="$CC"++; cd frida_mode; gmake
- name: run tests # - name: run tests
run: sudo -E ./afl-system-config; export CC=/usr/local/Cellar/llvm/*/bin/clang; export CXX="$CC"++; export PATH=/usr/local/Cellar/llvm/*/":/usr/local/bin:$PATH"; export LLVM_CONFIG=/usr/local/Cellar/llvm/*/bin/llvm-config; gmake tests # run: sudo -E ./afl-system-config; export CC=/usr/local/Cellar/llvm/*/bin/clang; export CXX="$CC"++; export PATH=/usr/local/Cellar/llvm/*/":/usr/local/bin:$PATH"; export LLVM_CONFIG=/usr/local/Cellar/llvm/*/bin/llvm-config; gmake tests
- name: force frida test for MacOS # - name: force frida test for MacOS
run: export AFL_PATH=`pwd`; /usr/local/bin/gcc -o test-instr test-instr.c; mkdir in; echo > in/in; AFL_NO_UI=1 ./afl-fuzz -O -i in -o out -V 5 -- ./test-instr # run: export AFL_PATH=`pwd`; /usr/local/bin/gcc -o test-instr test-instr.c; mkdir in; echo > in/in; AFL_NO_UI=1 ./afl-fuzz -O -i in -o out -V 5 -- ./test-instr

View File

@ -6,7 +6,7 @@
# #
FROM ubuntu:22.04 AS aflplusplus FROM ubuntu:22.04 AS aflplusplus
LABEL "maintainer"="afl++ team <afl@aflplus.plus>" LABEL "maintainer"="AFL++ team <afl@aflplus.plus>"
LABEL "about"="AFLplusplus container image" LABEL "about"="AFLplusplus container image"
### Comment out to enable these features ### Comment out to enable these features
@ -94,4 +94,4 @@ RUN sed -i.bak 's/^ -/ /g' GNUmakefile && \
RUN echo "set encoding=utf-8" > /root/.vimrc && \ RUN echo "set encoding=utf-8" > /root/.vimrc && \
echo ". /etc/bash_completion" >> ~/.bashrc && \ echo ". /etc/bash_completion" >> ~/.bashrc && \
echo 'alias joe="joe --wordwrap --joe_state -nobackup"' >> ~/.bashrc && \ echo 'alias joe="joe --wordwrap --joe_state -nobackup"' >> ~/.bashrc && \
echo "export PS1='"'[afl++ \h] \w \$ '"'" >> ~/.bashrc echo "export PS1='"'[AFL++ \h] \w \$ '"'" >> ~/.bashrc

View File

@ -39,7 +39,7 @@ ASAN_OPTIONS=detect_leaks=0
SYS = $(shell uname -s) SYS = $(shell uname -s)
ARCH = $(shell uname -m) ARCH = $(shell uname -m)
$(info [*] Compiling afl++ for OS $(SYS) on ARCH $(ARCH)) $(info [*] Compiling AFL++ for OS $(SYS) on ARCH $(ARCH))
ifdef NO_SPLICING ifdef NO_SPLICING
override CFLAGS_OPT += -DNO_SPLICING override CFLAGS_OPT += -DNO_SPLICING
@ -100,8 +100,13 @@ else
LDFLAGS += $(SDK_LD) LDFLAGS += $(SDK_LD)
endif endif
COMPILER_TYPE=$(shell $(CC) --version|grep "Free Software Foundation")
ifneq "$(COMPILER_TYPE)" ""
#$(info gcc is being used)
CFLAGS_OPT += -Wno-error=format-truncation -Wno-format-truncation
endif
ifeq "$(SYS)" "SunOS" ifeq "$(SYS)" "SunOS"
CFLAGS_OPT += -Wno-format-truncation
LDFLAGS = -lkstat -lrt -lsocket -lnsl LDFLAGS = -lkstat -lrt -lsocket -lnsl
endif endif
@ -145,7 +150,7 @@ else
endif endif
override CFLAGS += -g -Wno-pointer-sign -Wno-variadic-macros -Wall -Wextra -Wno-pointer-arith \ override CFLAGS += -g -Wno-pointer-sign -Wno-variadic-macros -Wall -Wextra -Wno-pointer-arith \
-fPIC -I include/ -DAFL_PATH=\"$(HELPER_PATH)\" \ -fPIC -I include/ -DAFL_PATH=\"$(HELPER_PATH)\" \
-DBIN_PATH=\"$(BIN_PATH)\" -DDOC_PATH=\"$(DOC_PATH)\" -DBIN_PATH=\"$(BIN_PATH)\" -DDOC_PATH=\"$(DOC_PATH)\"
# -fstack-protector # -fstack-protector
@ -180,13 +185,13 @@ AFL_FUZZ_FILES = $(wildcard src/afl-fuzz*.c)
ifneq "$(shell command -v python3m 2>/dev/null)" "" ifneq "$(shell command -v python3m 2>/dev/null)" ""
ifneq "$(shell command -v python3m-config 2>/dev/null)" "" ifneq "$(shell command -v python3m-config 2>/dev/null)" ""
PYTHON_INCLUDE ?= $(shell python3m-config --includes) PYTHON_INCLUDE := $(shell python3m-config --includes)
PYTHON_VERSION ?= $(strip $(shell python3m --version 2>&1)) PYTHON_VERSION := $(strip $(shell python3m --version 2>&1))
# Starting with python3.8, we need to pass the `embed` flag. Earlier versions didn't know this flag. # Starting with python3.8, we need to pass the `embed` flag. Earlier versions didn't know this flag.
ifeq "$(shell python3m-config --embed --libs 2>/dev/null | grep -q lpython && echo 1 )" "1" ifeq "$(shell python3m-config --embed --libs 2>/dev/null | grep -q lpython && echo 1 )" "1"
PYTHON_LIB ?= $(shell python3m-config --libs --embed --ldflags) PYTHON_LIB := $(shell python3m-config --libs --embed --ldflags)
else else
PYTHON_LIB ?= $(shell python3m-config --ldflags) PYTHON_LIB := $(shell python3m-config --ldflags)
endif endif
endif endif
endif endif
@ -194,13 +199,13 @@ endif
ifeq "$(PYTHON_INCLUDE)" "" ifeq "$(PYTHON_INCLUDE)" ""
ifneq "$(shell command -v python3 2>/dev/null)" "" ifneq "$(shell command -v python3 2>/dev/null)" ""
ifneq "$(shell command -v python3-config 2>/dev/null)" "" ifneq "$(shell command -v python3-config 2>/dev/null)" ""
PYTHON_INCLUDE ?= $(shell python3-config --includes) PYTHON_INCLUDE := $(shell python3-config --includes)
PYTHON_VERSION ?= $(strip $(shell python3 --version 2>&1)) PYTHON_VERSION := $(strip $(shell python3 --version 2>&1))
# Starting with python3.8, we need to pass the `embed` flag. Earier versions didn't know this flag. # Starting with python3.8, we need to pass the `embed` flag. Earlier versions didn't know this flag.
ifeq "$(shell python3-config --embed --libs 2>/dev/null | grep -q lpython && echo 1 )" "1" ifeq "$(shell python3-config --embed --libs 2>/dev/null | grep -q lpython && echo 1 )" "1"
PYTHON_LIB ?= $(shell python3-config --libs --embed --ldflags) PYTHON_LIB := $(shell python3-config --libs --embed --ldflags)
else else
PYTHON_LIB ?= $(shell python3-config --ldflags) PYTHON_LIB := $(shell python3-config --ldflags)
endif endif
endif endif
endif endif
@ -209,9 +214,9 @@ endif
ifeq "$(PYTHON_INCLUDE)" "" ifeq "$(PYTHON_INCLUDE)" ""
ifneq "$(shell command -v python 2>/dev/null)" "" ifneq "$(shell command -v python 2>/dev/null)" ""
ifneq "$(shell command -v python-config 2>/dev/null)" "" ifneq "$(shell command -v python-config 2>/dev/null)" ""
PYTHON_INCLUDE ?= $(shell python-config --includes) PYTHON_INCLUDE := $(shell python-config --includes)
PYTHON_LIB ?= $(shell python-config --ldflags) PYTHON_LIB := $(shell python-config --ldflags)
PYTHON_VERSION ?= $(strip $(shell python --version 2>&1)) PYTHON_VERSION := $(strip $(shell python --version 2>&1))
endif endif
endif endif
endif endif
@ -220,9 +225,9 @@ endif
ifeq "$(PYTHON_INCLUDE)" "" ifeq "$(PYTHON_INCLUDE)" ""
ifneq "$(shell command -v python3.7 2>/dev/null)" "" ifneq "$(shell command -v python3.7 2>/dev/null)" ""
ifneq "$(shell command -v python3.7-config 2>/dev/null)" "" ifneq "$(shell command -v python3.7-config 2>/dev/null)" ""
PYTHON_INCLUDE ?= $(shell python3.7-config --includes) PYTHON_INCLUDE := $(shell python3.7-config --includes)
PYTHON_LIB ?= $(shell python3.7-config --ldflags) PYTHON_LIB := $(shell python3.7-config --ldflags)
PYTHON_VERSION ?= $(strip $(shell python3.7 --version 2>&1)) PYTHON_VERSION := $(strip $(shell python3.7 --version 2>&1))
endif endif
endif endif
endif endif
@ -231,9 +236,9 @@ endif
ifeq "$(PYTHON_INCLUDE)" "" ifeq "$(PYTHON_INCLUDE)" ""
ifneq "$(shell command -v python2.7 2>/dev/null)" "" ifneq "$(shell command -v python2.7 2>/dev/null)" ""
ifneq "$(shell command -v python2.7-config 2>/dev/null)" "" ifneq "$(shell command -v python2.7-config 2>/dev/null)" ""
PYTHON_INCLUDE ?= $(shell python2.7-config --includes) PYTHON_INCLUDE := $(shell python2.7-config --includes)
PYTHON_LIB ?= $(shell python2.7-config --ldflags) PYTHON_LIB := $(shell python2.7-config --ldflags)
PYTHON_VERSION ?= $(strip $(shell python2.7 --version 2>&1)) PYTHON_VERSION := $(strip $(shell python2.7 --version 2>&1))
endif endif
endif endif
endif endif
@ -314,7 +319,7 @@ all: test_x86 test_shm test_python ready $(PROGS) afl-as llvm gcc_plugin test_bu
@test -e afl-fuzz && echo "[+] afl-fuzz and supporting tools successfully built" || echo "[-] afl-fuzz could not be built, please set CC to a working compiler" @test -e afl-fuzz && echo "[+] afl-fuzz and supporting tools successfully built" || echo "[-] afl-fuzz could not be built, please set CC to a working compiler"
@test -e afl-llvm-pass.so && echo "[+] LLVM basic mode successfully built" || echo "[-] LLVM mode could not be built, please install at least llvm-11 and clang-11 or newer, see docs/INSTALL.md" @test -e afl-llvm-pass.so && echo "[+] LLVM basic mode successfully built" || echo "[-] LLVM mode could not be built, please install at least llvm-11 and clang-11 or newer, see docs/INSTALL.md"
@test -e SanitizerCoveragePCGUARD.so && echo "[+] LLVM mode successfully built" || echo "[-] LLVM mode could not be built, please install at least llvm-11 and clang-11 or newer, see docs/INSTALL.md" @test -e SanitizerCoveragePCGUARD.so && echo "[+] LLVM mode successfully built" || echo "[-] LLVM mode could not be built, please install at least llvm-11 and clang-11 or newer, see docs/INSTALL.md"
@test -e SanitizerCoverageLTO.so && echo "[+] LLVM LTO mode successfully built" || echo "[-] LLVM LTO mode could not be built, it is optional, if you want it, please install LLVM 11-14. More information at instrumentation/README.lto.md on how to build it" @test -e SanitizerCoverageLTO.so && echo "[+] LLVM LTO mode successfully built" || echo "[-] LLVM LTO mode could not be built, it is optional, if you want it, please install LLVM and LLD 11+. More information at instrumentation/README.lto.md on how to build it"
ifneq "$(SYS)" "Darwin" ifneq "$(SYS)" "Darwin"
@test -e afl-gcc-pass.so && echo "[+] gcc_mode successfully built" || echo "[-] gcc_mode could not be built, it is optional, install gcc-VERSION-plugin-dev to enable this" @test -e afl-gcc-pass.so && echo "[+] gcc_mode successfully built" || echo "[-] gcc_mode could not be built, it is optional, install gcc-VERSION-plugin-dev to enable this"
endif endif
@ -357,7 +362,7 @@ performance-test: source-only
help: help:
@echo "HELP --- the following make targets exist:" @echo "HELP --- the following make targets exist:"
@echo "==========================================" @echo "=========================================="
@echo "all: the main afl++ binaries and llvm/gcc instrumentation" @echo "all: the main AFL++ binaries and llvm/gcc instrumentation"
@echo "binary-only: everything for binary-only fuzzing: frida_mode, nyx_mode, qemu_mode, frida_mode, unicorn_mode, coresight_mode, libdislocator, libtokencap" @echo "binary-only: everything for binary-only fuzzing: frida_mode, nyx_mode, qemu_mode, frida_mode, unicorn_mode, coresight_mode, libdislocator, libtokencap"
@echo "source-only: everything for source code fuzzing: nyx_mode, libdislocator, libtokencap" @echo "source-only: everything for source code fuzzing: nyx_mode, libdislocator, libtokencap"
@echo "distrib: everything (for both binary-only and source code fuzzing)" @echo "distrib: everything (for both binary-only and source code fuzzing)"
@ -365,7 +370,7 @@ help:
@echo "install: installs everything you have compiled with the build option above" @echo "install: installs everything you have compiled with the build option above"
@echo "clean: cleans everything compiled (not downloads when on a checkout)" @echo "clean: cleans everything compiled (not downloads when on a checkout)"
@echo "deepclean: cleans everything including downloads" @echo "deepclean: cleans everything including downloads"
@echo "uninstall: uninstall afl++ from the system" @echo "uninstall: uninstall AFL++ from the system"
@echo "code-format: format the code, do this before you commit and send a PR please!" @echo "code-format: format the code, do this before you commit and send a PR please!"
@echo "tests: this runs the test framework. It is more catered for the developers, but if you run into problems this helps pinpointing the problem" @echo "tests: this runs the test framework. It is more catered for the developers, but if you run into problems this helps pinpointing the problem"
@echo "unit: perform unit tests (based on cmocka and GNU linker)" @echo "unit: perform unit tests (based on cmocka and GNU linker)"
@ -377,6 +382,7 @@ help:
@echo Known build environment options: @echo Known build environment options:
@echo "==========================================" @echo "=========================================="
@echo STATIC - compile AFL++ static @echo STATIC - compile AFL++ static
@echo "CODE_COVERAGE - compile the target for code coverage (see docs/instrumentation/README.llvm.md)"
@echo ASAN_BUILD - compiles AFL++ with memory sanitizer for debug purposes @echo ASAN_BUILD - compiles AFL++ with memory sanitizer for debug purposes
@echo UBSAN_BUILD - compiles AFL++ tools with undefined behaviour sanitizer for debug purposes @echo UBSAN_BUILD - compiles AFL++ tools with undefined behaviour sanitizer for debug purposes
@echo DEBUG - no optimization, -ggdb3, all warnings and -Werror @echo DEBUG - no optimization, -ggdb3, all warnings and -Werror
@ -388,10 +394,11 @@ help:
@echo NO_NYX - disable building nyx mode dependencies @echo NO_NYX - disable building nyx mode dependencies
@echo "NO_CORESIGHT - disable building coresight (arm64 only)" @echo "NO_CORESIGHT - disable building coresight (arm64 only)"
@echo NO_UNICORN_ARM64 - disable building unicorn on arm64 @echo NO_UNICORN_ARM64 - disable building unicorn on arm64
@echo "WAFL_MODE - enable for WASM fuzzing with https://github.com/fgsect/WAFL"
@echo AFL_NO_X86 - if compiling on non-intel/amd platforms @echo AFL_NO_X86 - if compiling on non-intel/amd platforms
@echo "LLVM_CONFIG - if your distro doesn't use the standard name for llvm-config (e.g., Debian)" @echo "LLVM_CONFIG - if your distro doesn't use the standard name for llvm-config (e.g., Debian)"
@echo "==========================================" @echo "=========================================="
@echo e.g.: make ASAN_BUILD=1 @echo e.g.: make LLVM_CONFIG=llvm-config-16
.PHONY: test_x86 .PHONY: test_x86
ifndef AFL_NO_X86 ifndef AFL_NO_X86
@ -430,7 +437,7 @@ endif
.PHONY: ready .PHONY: ready
ready: ready:
@echo "[+] Everything seems to be working, ready to compile." @echo "[+] Everything seems to be working, ready to compile. ($(shell $(CC) --version 2>&1|head -n 1))"
afl-as: src/afl-as.c include/afl-as.h $(COMM_HDR) | test_x86 afl-as: src/afl-as.c include/afl-as.h $(COMM_HDR) | test_x86
$(CC) $(CFLAGS) src/$@.c -o $@ $(LDFLAGS) $(CC) $(CFLAGS) src/$@.c -o $@ $(LDFLAGS)
@ -452,7 +459,7 @@ afl-fuzz: $(COMM_HDR) include/afl-fuzz.h $(AFL_FUZZ_FILES) src/afl-common.o src/
$(CC) $(CFLAGS) $(COMPILE_STATIC) $(CFLAGS_FLTO) $(AFL_FUZZ_FILES) src/afl-common.o src/afl-sharedmem.o src/afl-forkserver.o src/afl-performance.o -o $@ $(PYFLAGS) $(LDFLAGS) -lm $(CC) $(CFLAGS) $(COMPILE_STATIC) $(CFLAGS_FLTO) $(AFL_FUZZ_FILES) src/afl-common.o src/afl-sharedmem.o src/afl-forkserver.o src/afl-performance.o -o $@ $(PYFLAGS) $(LDFLAGS) -lm
afl-showmap: src/afl-showmap.c src/afl-common.o src/afl-sharedmem.o src/afl-forkserver.o src/afl-performance.o $(COMM_HDR) | test_x86 afl-showmap: src/afl-showmap.c src/afl-common.o src/afl-sharedmem.o src/afl-forkserver.o src/afl-performance.o $(COMM_HDR) | test_x86
$(CC) $(CFLAGS) $(COMPILE_STATIC) $(CFLAGS_FLTO) src/$@.c src/afl-common.o src/afl-sharedmem.o src/afl-forkserver.o src/afl-performance.o -o $@ $(LDFLAGS) $(CC) $(CFLAGS) $(COMPILE_STATIC) $(CFLAGS_FLTO) src/$@.c src/afl-fuzz-mutators.c src/afl-fuzz-python.c src/afl-common.o src/afl-sharedmem.o src/afl-forkserver.o src/afl-performance.o -o $@ $(PYFLAGS) $(LDFLAGS)
afl-tmin: src/afl-tmin.c src/afl-common.o src/afl-sharedmem.o src/afl-forkserver.o src/afl-performance.o $(COMM_HDR) | test_x86 afl-tmin: src/afl-tmin.c src/afl-common.o src/afl-sharedmem.o src/afl-forkserver.o src/afl-performance.o $(COMM_HDR) | test_x86
$(CC) $(CFLAGS) $(COMPILE_STATIC) $(CFLAGS_FLTO) src/$@.c src/afl-common.o src/afl-sharedmem.o src/afl-forkserver.o src/afl-performance.o -o $@ $(LDFLAGS) $(CC) $(CFLAGS) $(COMPILE_STATIC) $(CFLAGS_FLTO) src/$@.c src/afl-common.o src/afl-sharedmem.o src/afl-forkserver.o src/afl-performance.o -o $@ $(LDFLAGS)
@ -546,7 +553,7 @@ ifndef AFL_NO_X86
test_build: afl-cc afl-gcc afl-as afl-showmap test_build: afl-cc afl-gcc afl-as afl-showmap
@echo "[*] Testing the CC wrapper afl-cc and its instrumentation output..." @echo "[*] Testing the CC wrapper afl-cc and its instrumentation output..."
@unset AFL_MAP_SIZE AFL_USE_UBSAN AFL_USE_CFISAN AFL_USE_LSAN AFL_USE_ASAN AFL_USE_MSAN; ASAN_OPTIONS=detect_leaks=0 AFL_INST_RATIO=100 AFL_PATH=. ./afl-cc test-instr.c $(LDFLAGS) -o test-instr 2>&1 || (echo "Oops, afl-cc failed"; exit 1 ) @unset AFL_MAP_SIZE AFL_USE_UBSAN AFL_USE_CFISAN AFL_USE_LSAN AFL_USE_ASAN AFL_USE_MSAN; ASAN_OPTIONS=detect_leaks=0 AFL_INST_RATIO=100 AFL_PATH=. ./afl-cc test-instr.c $(LDFLAGS) -o test-instr 2>&1 || (echo "Oops, afl-cc failed"; exit 1 )
- ASAN_OPTIONS=detect_leaks=0 ./afl-showmap -m none -o .test-instr0 ./test-instr < /dev/null -ASAN_OPTIONS=detect_leaks=0 ./afl-showmap -q -m none -o .test-instr0 ./test-instr < /dev/null
-echo 1 | ASAN_OPTIONS=detect_leaks=0 ./afl-showmap -m none -q -o .test-instr1 ./test-instr -echo 1 | ASAN_OPTIONS=detect_leaks=0 ./afl-showmap -m none -q -o .test-instr1 ./test-instr
@rm -f test-instr @rm -f test-instr
@cmp -s .test-instr0 .test-instr1; DR="$$?"; rm -f .test-instr0 .test-instr1; if [ "$$DR" = "0" ]; then echo; echo "Oops, the instrumentation of afl-cc does not seem to be behaving correctly!"; echo; echo "Please post to https://github.com/AFLplusplus/AFLplusplus/issues to troubleshoot the issue."; echo; exit 1; fi @cmp -s .test-instr0 .test-instr1; DR="$$?"; rm -f .test-instr0 .test-instr1; if [ "$$DR" = "0" ]; then echo; echo "Oops, the instrumentation of afl-cc does not seem to be behaving correctly!"; echo; echo "Please post to https://github.com/AFLplusplus/AFLplusplus/issues to troubleshoot the issue."; echo; exit 1; fi
@ -746,7 +753,7 @@ endif
@echo @echo
%.8: % %.8: %
@echo .TH $* 8 $(BUILD_DATE) "afl++" > $@ @echo .TH $* 8 $(BUILD_DATE) "AFL++" > $@
@echo .SH NAME >> $@ @echo .SH NAME >> $@
@echo .B $* >> $@ @echo .B $* >> $@
@echo >> $@ @echo >> $@
@ -758,8 +765,8 @@ endif
@./$* -hh 2>&1 | tail -n +4 >> $@ @./$* -hh 2>&1 | tail -n +4 >> $@
@echo >> $@ @echo >> $@
@echo .SH AUTHOR >> $@ @echo .SH AUTHOR >> $@
@echo "afl++ was written by Michal \"lcamtuf\" Zalewski and is maintained by Marc \"van Hauser\" Heuse <mh@mh-sec.de>, Heiko \"hexcoder-\" Eissfeldt <heiko.eissfeldt@hexco.de>, Andrea Fioraldi <andreafioraldi@gmail.com> and Dominik Maier <domenukk@gmail.com>" >> $@ @echo "AFL++ was written by Michal \"lcamtuf\" Zalewski and is maintained by Marc \"van Hauser\" Heuse <mh@mh-sec.de>, Dominik Maier <domenukk@gmail.com>, Andrea Fioraldi <andreafioraldi@gmail.com> and Heiko \"hexcoder-\" Eissfeldt <heiko.eissfeldt@hexco.de>" >> $@
@echo The homepage of afl++ is: https://github.com/AFLplusplus/AFLplusplus >> $@ @echo The homepage of AFL++ is: https://github.com/AFLplusplus/AFLplusplus >> $@
@echo >> $@ @echo >> $@
@echo .SH LICENSE >> $@ @echo .SH LICENSE >> $@
@echo Apache License Version 2.0, January 2004 >> $@ @echo Apache License Version 2.0, January 2004 >> $@

View File

@ -175,7 +175,7 @@ all_done: test_build
.NOTPARALLEL: clean .NOTPARALLEL: clean
%.8: % %.8: %
@echo .TH $* 8 `date "+%Y-%m-%d"` "afl++" > ./$@ @echo .TH $* 8 `date "+%Y-%m-%d"` "AFL++" > ./$@
@echo .SH NAME >> ./$@ @echo .SH NAME >> ./$@
@echo .B $* >> ./$@ @echo .B $* >> ./$@
@echo >> ./$@ @echo >> ./$@
@ -187,8 +187,8 @@ all_done: test_build
@./$* -h 2>&1 | tail -n +4 >> ./$@ @./$* -h 2>&1 | tail -n +4 >> ./$@
@echo >> ./$@ @echo >> ./$@
@echo .SH AUTHOR >> ./$@ @echo .SH AUTHOR >> ./$@
@echo "afl++ was written by Michal \"lcamtuf\" Zalewski and is maintained by Marc \"van Hauser\" Heuse <mh@mh-sec.de>, Heiko \"hexcoder-\" Eissfeldt <heiko.eissfeldt@hexco.de>, Andrea Fioraldi <andreafioraldi@gmail.com> and Dominik Maier <domenukk@gmail.com>" >> ./$@ @echo "AFL++ was written by Michal \"lcamtuf\" Zalewski and is maintained by Marc \"van Hauser\" Heuse <mh@mh-sec.de>, Dominik Maier <domenukk@gmail.com>, Andrea Fioraldi <andreafioraldi@gmail.com> and Heiko \"hexcoder-\" Eissfeldt <heiko.eissfeldt@hexco.de>" >> ./$@
@echo The homepage of afl++ is: https://github.com/AFLplusplus/AFLplusplus >> ./$@ @echo The homepage of AFL++ is: https://github.com/AFLplusplus/AFLplusplus >> ./$@
@echo >> ./$@ @echo >> ./$@
@echo .SH LICENSE >> ./$@ @echo .SH LICENSE >> ./$@
@echo Apache License Version 2.0, January 2004 >> ./$@ @echo Apache License Version 2.0, January 2004 >> ./$@

View File

@ -46,10 +46,10 @@ LLVMVER = $(shell $(LLVM_CONFIG) --version 2>/dev/null | sed 's/git//' | sed 's
LLVM_MAJOR = $(shell $(LLVM_CONFIG) --version 2>/dev/null | sed 's/\..*//' ) LLVM_MAJOR = $(shell $(LLVM_CONFIG) --version 2>/dev/null | sed 's/\..*//' )
LLVM_MINOR = $(shell $(LLVM_CONFIG) --version 2>/dev/null | sed 's/.*\.//' | sed 's/git//' | sed 's/svn//' | sed 's/ .*//' ) LLVM_MINOR = $(shell $(LLVM_CONFIG) --version 2>/dev/null | sed 's/.*\.//' | sed 's/git//' | sed 's/svn//' | sed 's/ .*//' )
LLVM_UNSUPPORTED = $(shell $(LLVM_CONFIG) --version 2>/dev/null | grep -E -q '^[0-2]\.|^3.[0-7]\.' && echo 1 || echo 0 ) LLVM_UNSUPPORTED = $(shell $(LLVM_CONFIG) --version 2>/dev/null | grep -E -q '^[0-2]\.|^3.[0-7]\.' && echo 1 || echo 0 )
LLVM_TOO_NEW = $(shell $(LLVM_CONFIG) --version 2>/dev/null | grep -E -q '^1[5-9]' && echo 1 || echo 0 ) LLVM_TOO_NEW = $(shell $(LLVM_CONFIG) --version 2>/dev/null | grep -E -q '^1[7-9]' && echo 1 || echo 0 )
LLVM_NEW_API = $(shell $(LLVM_CONFIG) --version 2>/dev/null | grep -E -q '^1[0-9]' && echo 1 || echo 0 ) LLVM_NEW_API = $(shell $(LLVM_CONFIG) --version 2>/dev/null | grep -E -q '^1[0-9]' && echo 1 || echo 0 )
LLVM_NEWER_API = $(shell $(LLVM_CONFIG) --version 2>/dev/null | grep -E -q '^1[6-9]' && echo 1 || echo 0 ) LLVM_NEWER_API = $(shell $(LLVM_CONFIG) --version 2>/dev/null | grep -E -q '^1[6-9]' && echo 1 || echo 0 )
LLVM_10_OK = $(shell $(LLVM_CONFIG) --version 2>/dev/null | grep -E -q '^1[1-9]|^10\.[1-9]|^10\.0.[1-9]' && echo 1 || echo 0 ) LLVM_13_OK = $(shell $(LLVM_CONFIG) --version 2>/dev/null | grep -E -q '^1[3-9]' && echo 1 || echo 0 )
LLVM_HAVE_LTO = $(shell $(LLVM_CONFIG) --version 2>/dev/null | grep -E -q '^1[1-9]' && echo 1 || echo 0 ) LLVM_HAVE_LTO = $(shell $(LLVM_CONFIG) --version 2>/dev/null | grep -E -q '^1[1-9]' && echo 1 || echo 0 )
LLVM_BINDIR = $(shell $(LLVM_CONFIG) --bindir 2>/dev/null) LLVM_BINDIR = $(shell $(LLVM_CONFIG) --bindir 2>/dev/null)
LLVM_LIBDIR = $(shell $(LLVM_CONFIG) --libdir 2>/dev/null) LLVM_LIBDIR = $(shell $(LLVM_CONFIG) --libdir 2>/dev/null)
@ -274,6 +274,11 @@ ifndef LLVM_DEBUG
CFLAGS_SAFE += -Wno-deprecated CFLAGS_SAFE += -Wno-deprecated
endif endif
ifdef CODE_COVERAGE
override CFLAGS_SAFE += -D__AFL_CODE_COVERAGE=1
override LDFLAGS += -ldl
endif
override CFLAGS += $(CFLAGS_SAFE) override CFLAGS += $(CFLAGS_SAFE)
ifdef AFL_TRACE_PC ifdef AFL_TRACE_PC
@ -294,6 +299,11 @@ endif
CLANG_CPPFL = `$(LLVM_CONFIG) --cxxflags` -fno-rtti -fPIC $(CXXFLAGS) -Wno-deprecated-declarations CLANG_CPPFL = `$(LLVM_CONFIG) --cxxflags` -fno-rtti -fPIC $(CXXFLAGS) -Wno-deprecated-declarations
CLANG_LFL = `$(LLVM_CONFIG) --ldflags` $(LDFLAGS) CLANG_LFL = `$(LLVM_CONFIG) --ldflags` $(LDFLAGS)
# wasm fuzzing: disable thread-local storage and unset LLVM debug flag
ifdef WAFL_MODE
$(info Compiling libraries for use with WAVM)
CLANG_CPPFL += -DNDEBUG -DNO_TLS
endif
# User teor2345 reports that this is required to make things work on MacOS X. # User teor2345 reports that this is required to make things work on MacOS X.
ifeq "$(SYS)" "Darwin" ifeq "$(SYS)" "Darwin"
@ -412,7 +422,7 @@ endif
$(CXX) $(CLANG_CPPFL) -Wdeprecated -fno-rtti -fPIC -std=$(LLVM_STDCXX) -shared $< -o $@ $(CLANG_LFL) instrumentation/afl-llvm-common.o $(CXX) $(CLANG_CPPFL) -Wdeprecated -fno-rtti -fPIC -std=$(LLVM_STDCXX) -shared $< -o $@ $(CLANG_LFL) instrumentation/afl-llvm-common.o
./SanitizerCoveragePCGUARD.so: instrumentation/SanitizerCoveragePCGUARD.so.cc instrumentation/afl-llvm-common.o | test_deps ./SanitizerCoveragePCGUARD.so: instrumentation/SanitizerCoveragePCGUARD.so.cc instrumentation/afl-llvm-common.o | test_deps
ifeq "$(LLVM_10_OK)" "1" ifeq "$(LLVM_13_OK)" "1"
-$(CXX) $(CLANG_CPPFL) -fno-rtti -fPIC -std=$(LLVM_STDCXX) -shared $< -o $@ $(CLANG_LFL) -Wno-deprecated-copy-dtor -Wdeprecated instrumentation/afl-llvm-common.o -$(CXX) $(CLANG_CPPFL) -fno-rtti -fPIC -std=$(LLVM_STDCXX) -shared $< -o $@ $(CLANG_LFL) -Wno-deprecated-copy-dtor -Wdeprecated instrumentation/afl-llvm-common.o
endif endif
@ -505,7 +515,7 @@ install: all
install -m 644 instrumentation/README.*.md $${DESTDIR}$(DOC_PATH)/ install -m 644 instrumentation/README.*.md $${DESTDIR}$(DOC_PATH)/
%.8: % %.8: %
@echo .TH $* 8 $(BUILD_DATE) "afl++" > ./$@ @echo .TH $* 8 $(BUILD_DATE) "AFL++" > ./$@
@echo .SH NAME >> ./$@ @echo .SH NAME >> ./$@
@printf "%s" ".B $* \- " >> ./$@ @printf "%s" ".B $* \- " >> ./$@
@./$* -h 2>&1 | head -n 1 | sed -e "s/$$(printf '\e')[^m]*m//g" >> ./$@ @./$* -h 2>&1 | head -n 1 | sed -e "s/$$(printf '\e')[^m]*m//g" >> ./$@
@ -519,8 +529,8 @@ install: all
@./$* -h 2>&1 | tail -n +4 >> ./$@ @./$* -h 2>&1 | tail -n +4 >> ./$@
@echo >> ./$@ @echo >> ./$@
@echo .SH AUTHOR >> ./$@ @echo .SH AUTHOR >> ./$@
@echo "afl++ was written by Michal \"lcamtuf\" Zalewski and is maintained by Marc \"van Hauser\" Heuse <mh@mh-sec.de>, Heiko \"hexcoder-\" Eissfeldt <heiko.eissfeldt@hexco.de>, Andrea Fioraldi <andreafioraldi@gmail.com> and Dominik Maier <domenukk@gmail.com>" >> ./$@ @echo "AFL++ was written by Michal \"lcamtuf\" Zalewski and is maintained by Marc \"van Hauser\" Heuse <mh@mh-sec.de>, Dominik Maier <domenukk@gmail.com>, Andrea Fioraldi <andreafioraldi@gmail.com> and Heiko \"hexcoder-\" Eissfeldt <heiko.eissfeldt@hexco.de>" >> ./$@
@echo The homepage of afl++ is: https://github.com/AFLplusplus/AFLplusplus >> ./$@ @echo The homepage of AFL++ is: https://github.com/AFLplusplus/AFLplusplus >> ./$@
@echo >> ./$@ @echo >> ./$@
@echo .SH LICENSE >> ./$@ @echo .SH LICENSE >> ./$@
@echo Apache License Version 2.0, January 2004 >> ./$@ @echo Apache License Version 2.0, January 2004 >> ./$@

View File

@ -1,10 +1,10 @@
# American Fuzzy Lop plus plus (AFL++) # American Fuzzy Lop plus plus (AFL++)
<img align="right" src="https://raw.githubusercontent.com/AFLplusplus/Website/master/static/aflpp_bg.svg" alt="AFL++ logo" width="250" heigh="250"> <img align="right" src="https://raw.githubusercontent.com/AFLplusplus/Website/main/static/aflpp_bg.svg" alt="AFL++ logo" width="250" heigh="250">
Release version: [4.05c](https://github.com/AFLplusplus/AFLplusplus/releases) Release version: [4.06c](https://github.com/AFLplusplus/AFLplusplus/releases)
GitHub version: 4.06a GitHub version: 4.07a
Repository: Repository:
[https://github.com/AFLplusplus/AFLplusplus](https://github.com/AFLplusplus/AFLplusplus) [https://github.com/AFLplusplus/AFLplusplus](https://github.com/AFLplusplus/AFLplusplus)
@ -12,9 +12,9 @@ Repository:
AFL++ is maintained by: AFL++ is maintained by:
* Marc "van Hauser" Heuse <mh@mh-sec.de> * Marc "van Hauser" Heuse <mh@mh-sec.de>
* Heiko "hexcoder-" Eißfeldt <heiko.eissfeldt@hexco.de>
* Andrea Fioraldi <andreafioraldi@gmail.com> * Andrea Fioraldi <andreafioraldi@gmail.com>
* Dominik Maier <mail@dmnk.co> * Dominik Maier <mail@dmnk.co>
* Heiko "hexcoder-" Eißfeldt <heiko.eissfeldt@hexco.de>
* Documentation: Jana Aydinbas <jana.aydinbas@gmail.com> * Documentation: Jana Aydinbas <jana.aydinbas@gmail.com>
Originally developed by Michał "lcamtuf" Zalewski. Originally developed by Michał "lcamtuf" Zalewski.

View File

@ -2,9 +2,8 @@
## Should ## Should
- splicing selection weighted? - test cmplog for less than 16bit
- support afl_custom_{send,post_process}, persistent and deferred fork - support persistent and deferred fork server in afl-showmap?
server in afl-showmap
- better autodetection of shifting runtime timeout values - better autodetection of shifting runtime timeout values
- Update afl->pending_not_fuzzed for MOpt - Update afl->pending_not_fuzzed for MOpt
- afl-plot to support multiple plot_data - afl-plot to support multiple plot_data

167
afl-cmin
View File

@ -103,12 +103,14 @@ function usage() {
" -o dir - output directory for minimized files\n" \ " -o dir - output directory for minimized files\n" \
"\n" \ "\n" \
"Execution control settings:\n" \ "Execution control settings:\n" \
" -T tasks - how many parallel tasks to run (default: 1, all=nproc)\n" \
" -f file - location read by the fuzzed program (stdin)\n" \ " -f file - location read by the fuzzed program (stdin)\n" \
" -m megs - memory limit for child process ("mem_limit" MB)\n" \ " -m megs - memory limit for child process ("mem_limit" MB)\n" \
" -t msec - run time limit for child process (default: none)\n" \ " -t msec - run time limit for child process (default: 5000)\n" \
" -O - use binary-only instrumentation (FRIDA mode)\n" \ " -O - use binary-only instrumentation (FRIDA mode)\n" \
" -Q - use binary-only instrumentation (QEMU mode)\n" \ " -Q - use binary-only instrumentation (QEMU mode)\n" \
" -U - use unicorn-based instrumentation (unicorn mode)\n" \ " -U - use unicorn-based instrumentation (unicorn mode)\n" \
" -X - use Nyx mode\n" \
"\n" \ "\n" \
"Minimization settings:\n" \ "Minimization settings:\n" \
" -A - allow crashes and timeouts (not recommended)\n" \ " -A - allow crashes and timeouts (not recommended)\n" \
@ -118,20 +120,21 @@ function usage() {
"For additional tips, please consult README.md\n" \ "For additional tips, please consult README.md\n" \
"\n" \ "\n" \
"Environment variables used:\n" \ "Environment variables used:\n" \
"AFL_ALLOW_TMP: allow unsafe use of input/output directories under {/var}/tmp\n" \
"AFL_CRASH_EXITCODE: optional child exit code to be interpreted as crash\n" \ "AFL_CRASH_EXITCODE: optional child exit code to be interpreted as crash\n" \
"AFL_FORKSRV_INIT_TMOUT: time the fuzzer waits for the forkserver to come up\n" \ "AFL_FORKSRV_INIT_TMOUT: time the fuzzer waits for the forkserver to come up\n" \
"AFL_KEEP_TRACES: leave the temporary <out_dir>/.traces directory\n" \ "AFL_KEEP_TRACES: leave the temporary <out_dir>/.traces directory\n" \
"AFL_KILL_SIGNAL: Signal delivered to child processes on timeout (default: SIGKILL)\n" \ "AFL_KILL_SIGNAL: Signal delivered to child processes on timeout (default: SIGKILL)\n" \
"AFL_FORK_SERVER_KILL_SIGNAL: Signal delivered to fork server processes on termination\n" \ "AFL_FORK_SERVER_KILL_SIGNAL: Signal delivered to fork server processes on\n" \
" (default: SIGTERM). If this is not set and AFL_KILL_SIGNAL is set,\n" \ " termination (default: SIGTERM). If this is not set and AFL_KILL_SIGNAL is\n" \
" this will be set to the same value as AFL_KILL_SIGNAL.\n" \ " set, this will be set to the same value as AFL_KILL_SIGNAL.\n" \
"AFL_NO_FORKSRV: run target via execve instead of using the forkserver\n" \ "AFL_NO_FORKSRV: run target via execve instead of using the forkserver\n" \
"AFL_CMIN_ALLOW_ANY: write tuples for crashing inputs also\n" \ "AFL_CMIN_ALLOW_ANY: write tuples for crashing inputs also\n" \
"AFL_PATH: path for the afl-showmap binary if not found anywhere in PATH\n" \ "AFL_PATH: path for the afl-showmap binary if not found anywhere in PATH\n" \
"AFL_PRINT_FILENAMES: If set, the filename currently processed will be " \ "AFL_PRINT_FILENAMES: If set, the filename currently processed will be " \
"printed to stdout\n" \ "printed to stdout\n" \
"AFL_SKIP_BIN_CHECK: skip afl instrumentation checks for target binary\n" "AFL_SKIP_BIN_CHECK: skip afl instrumentation checks for target binary\n"
"AFL_CUSTOM_MUTATOR_LIBRARY: custom mutator library (post_process and send)\n"
"AFL_PYTHON_MODULE: custom mutator library (post_process and send)\n"
exit 1 exit 1
} }
@ -146,7 +149,7 @@ BEGIN {
redirected = 0 redirected = 0
} }
print "corpus minimization tool for afl++ (awk version)\n" print "corpus minimization tool for AFL++ (awk version)\n"
# defaults # defaults
extra_par = "" extra_par = ""
@ -156,13 +159,19 @@ BEGIN {
# process options # process options
Opterr = 1 # default is to diagnose Opterr = 1 # default is to diagnose
Optind = 1 # skip ARGV[0] Optind = 1 # skip ARGV[0]
while ((_go_c = getopt(ARGC, ARGV, "hi:o:f:m:t:eACOQU?")) != -1) { while ((_go_c = getopt(ARGC, ARGV, "hi:o:f:m:t:eACOQUXYT:?")) != -1) {
if (_go_c == "i") { if (_go_c == "i") {
if (!Optarg) usage() if (!Optarg) usage()
if (in_dir) { print "Option "_go_c" is only allowed once" > "/dev/stderr"} if (in_dir) { print "Option "_go_c" is only allowed once" > "/dev/stderr"}
in_dir = Optarg in_dir = Optarg
continue continue
} else } else
if (_go_c == "T") {
if (!Optarg) usage()
if (threads) { print "Option "_go_c" is only allowed once" > "/dev/stderr"}
threads = Optarg
continue
} else
if (_go_c == "o") { if (_go_c == "o") {
if (!Optarg) usage() if (!Optarg) usage()
if (out_dir) { print "Option "_go_c" is only allowed once" > "/dev/stderr"} if (out_dir) { print "Option "_go_c" is only allowed once" > "/dev/stderr"}
@ -217,6 +226,12 @@ BEGIN {
extra_par = extra_par " -U" extra_par = extra_par " -U"
unicorn_mode = 1 unicorn_mode = 1
continue continue
} else
if (_go_c == "X" || _go_c == "Y") {
if (nyx_mode) { print "Option "_go_c" is only allowed once" > "/dev/stderr"}
extra_par = extra_par " -X"
nyx_mode = 1
continue
} else } else
if (_go_c == "?") { if (_go_c == "?") {
exit 1 exit 1
@ -225,7 +240,7 @@ BEGIN {
} # while options } # while options
if (!mem_limit) mem_limit = "none" if (!mem_limit) mem_limit = "none"
if (!timeout) timeout = "none" if (!timeout) timeout = "5000"
# get program args # get program args
i = 0 i = 0
@ -244,21 +259,30 @@ BEGIN {
# Do a sanity check to discourage the use of /tmp, since we can't really # Do a sanity check to discourage the use of /tmp, since we can't really
# handle this safely from an awk script. # handle this safely from an awk script.
if (!ENVIRON["AFL_ALLOW_TMP"]) { #if (!ENVIRON["AFL_ALLOW_TMP"]) {
dirlist[0] = in_dir # dirlist[0] = in_dir
dirlist[1] = target_bin # dirlist[1] = target_bin
dirlist[2] = out_dir # dirlist[2] = out_dir
dirlist[3] = stdin_file # dirlist[3] = stdin_file
"pwd" | getline dirlist[4] # current directory # "pwd" | getline dirlist[4] # current directory
for (dirind in dirlist) { # for (dirind in dirlist) {
dir = dirlist[dirind] # dir = dirlist[dirind]
#
# if (dir ~ /^(\/var)?\/tmp/) {
# print "[-] Error: do not use this script in /tmp or /var/tmp." > "/dev/stderr"
# exit 1
# }
# }
# delete dirlist
#}
if (dir ~ /^(\/var)?\/tmp/) { if (threads && stdin_file) {
print "[-] Error: do not use this script in /tmp or /var/tmp." > "/dev/stderr" print "[-] Error: -T and -f cannot be used together." > "/dev/stderr"
exit 1 exit 1
} }
}
delete dirlist if (!threads && !stdin_file && !nyx_mode) {
print "[*] Are you aware of the '-T all' parallelize option that improves the speed for large/slow corpuses?"
} }
# If @@ is specified, but there's no -f, let's come up with a temporary input # If @@ is specified, but there's no -f, let's come up with a temporary input
@ -291,7 +315,8 @@ BEGIN {
exit 1 exit 1
} }
if (target_bin && !exists_and_is_executable(target_bin)) {
if (!nyx_mode && target_bin && !exists_and_is_executable(target_bin)) {
"command -v "target_bin" 2>/dev/null" | getline tnew "command -v "target_bin" 2>/dev/null" | getline tnew
if (!tnew || !exists_and_is_executable(tnew)) { if (!tnew || !exists_and_is_executable(tnew)) {
@ -311,7 +336,7 @@ BEGIN {
} }
} }
if (!ENVIRON["AFL_SKIP_BIN_CHECK"] && !qemu_mode && !frida_mode && !unicorn_mode) { if (!ENVIRON["AFL_SKIP_BIN_CHECK"] && !qemu_mode && !frida_mode && !unicorn_mode && !nyx_mode) {
if (0 != system( "grep -q __AFL_SHM_ID "target_bin )) { if (0 != system( "grep -q __AFL_SHM_ID "target_bin )) {
print "[-] Error: binary '"target_bin"' doesn't appear to be instrumented." > "/dev/stderr" print "[-] Error: binary '"target_bin"' doesn't appear to be instrumented." > "/dev/stderr"
exit 1 exit 1
@ -340,6 +365,18 @@ BEGIN {
exit 1 exit 1
} }
if (threads) {
"nproc" | getline nproc
if (threads == "all") {
threads = nproc
} else {
if (!(threads > 1 && threads <= nproc)) {
print "[-] Error: -T option must be between 1 and "nproc" or \"all\"." > "/dev/stderr"
exit 1
}
}
}
# Check for the more efficient way to copy files... # Check for the more efficient way to copy files...
if (0 != system("mkdir -p -m 0700 "trace_dir)) { if (0 != system("mkdir -p -m 0700 "trace_dir)) {
print "[-] Error: Cannot create directory "trace_dir > "/dev/stderr" print "[-] Error: Cannot create directory "trace_dir > "/dev/stderr"
@ -449,27 +486,79 @@ BEGIN {
# STEP 1: Collecting traces # # STEP 1: Collecting traces #
############################# #############################
if (threads) {
inputsperfile = in_count / threads
if (in_count % threads) {
inputsperfile++;
}
cnt = 0;
tmpfile=out_dir "/.filelist"
for (instance = 1; instance < threads; instance++) {
for (i = 0; i < inputsperfile; i++) {
print in_dir"/"infilesSmallToBigFull[cnt] >> tmpfile"."instance
cnt++
}
}
for (; cnt < in_count; cnt++) {
print in_dir"/"infilesSmallToBigFull[cnt] >> tmpfile"."threads
}
}
print "[*] Obtaining traces for "in_count" input files in '"in_dir"'." print "[*] Obtaining traces for "in_count" input files in '"in_dir"'."
cur = 0; cur = 0;
if (!stdin_file) {
print " Processing "in_count" files (forkserver mode)..."
# print AFL_CMIN_CRASHES_ONLY"\""showmap"\" -m "mem_limit" -t "timeout" -o \""trace_dir"\" -Z "extra_par" -i \""in_dir"\" -- \""target_bin"\" "prog_args_string
retval = system(AFL_MAP_SIZE AFL_CMIN_ALLOW_ANY AFL_CMIN_CRASHES_ONLY"\""showmap"\" -m "mem_limit" -t "timeout" -o \""trace_dir"\" -Z "extra_par" -i \""in_dir"\" -- \""target_bin"\" "prog_args_string)
} else {
print " Processing "in_count" files (forkserver mode)..."
# print AFL_CMIN_CRASHES_ONLY"\""showmap"\" -m "mem_limit" -t "timeout" -o \""trace_dir"\" -Z "extra_par" -i \""in_dir"\" -H \""stdin_file"\" -- \""target_bin"\" "prog_args_string" </dev/null"
retval = system(AFL_MAP_SIZE AFL_CMIN_ALLOW_ANY AFL_CMIN_CRASHES_ONLY"\""showmap"\" -m "mem_limit" -t "timeout" -o \""trace_dir"\" -Z "extra_par" -i \""in_dir"\" -H \""stdin_file"\" -- \""target_bin"\" "prog_args_string" </dev/null")
}
if (retval && (!AFL_CMIN_CRASHES_ONLY && !AFL_CMIN_ALLOW_ANY)) { if (threads > 1) {
print "[!] Exit code "retval" != 0 received from afl-showmap (this means a crashing or timeout input is likely present), terminating..."
if (!ENVIRON["AFL_KEEP_TRACES"]) { print "[*] Creating " threads " parallel tasks with about " inputsperfile " each."
system("rm -rf "trace_dir" 2>/dev/null") for (i = 1; i <= threads; i++) {
system("rmdir "out_dir)
if (!stdin_file) {
# print " { "AFL_MAP_SIZE AFL_CMIN_ALLOW_ANY AFL_CMIN_CRASHES_ONLY"\""showmap"\" -m "mem_limit" -t "timeout" -o \""trace_dir"\" -Z "extra_par" -I \""tmpfile"."i"\" -- \""target_bin"\" "prog_args_string"; > "tmpfile"."i".done ; } &"
retval = system(" { "AFL_MAP_SIZE AFL_CMIN_ALLOW_ANY AFL_CMIN_CRASHES_ONLY"\""showmap"\" -m "mem_limit" -t "timeout" -o \""trace_dir"\" -Z "extra_par" -I \""tmpfile"."i"\" -- \""target_bin"\" "prog_args_string"; > "tmpfile"."i".done ; } &")
} else {
stdin_file=tmpfile"."i".stdin"
# print " { "AFL_MAP_SIZE AFL_CMIN_ALLOW_ANY AFL_CMIN_CRASHES_ONLY"\""showmap"\" -m "mem_limit" -t "timeout" -o \""trace_dir"\" -Z "extra_par" -I \""tmpfile"."i"\" -H \""stdin_file"\" -- \""target_bin"\" "prog_args_string" </dev/null; > "tmpfile"."i".done ; } &"
retval = system(" { "AFL_MAP_SIZE AFL_CMIN_ALLOW_ANY AFL_CMIN_CRASHES_ONLY"\""showmap"\" -m "mem_limit" -t "timeout" -o \""trace_dir"\" -Z "extra_par" -I \""tmpfile"."i"\" -H \""stdin_file"\" -- \""target_bin"\" "prog_args_string" </dev/null; > "tmpfile"."i".done ; } &")
}
} }
exit retval print "[*] Waiting for parallel tasks to complete ..."
# wait for all processes to finish
ok=0
while (ok < threads) {
ok=0
for (i = 1; i <= threads; i++) {
if (system("test -f "tmpfile"."i".done") == 0) {
ok++
}
}
}
print "[*] Done!"
system("rm -f "tmpfile"*")
} else {
if (!stdin_file) {
print " Processing "in_count" files (forkserver mode)..."
# print AFL_CMIN_CRASHES_ONLY"\""showmap"\" -m "mem_limit" -t "timeout" -o \""trace_dir"\" -Z "extra_par" -i \""in_dir"\" -- \""target_bin"\" "prog_args_string
retval = system(AFL_MAP_SIZE AFL_CMIN_ALLOW_ANY AFL_CMIN_CRASHES_ONLY"\""showmap"\" -m "mem_limit" -t "timeout" -o \""trace_dir"\" -Z "extra_par" -i \""in_dir"\" -- \""target_bin"\" "prog_args_string)
} else {
print " Processing "in_count" files (forkserver mode)..."
# print AFL_CMIN_CRASHES_ONLY"\""showmap"\" -m "mem_limit" -t "timeout" -o \""trace_dir"\" -Z "extra_par" -i \""in_dir"\" -H \""stdin_file"\" -- \""target_bin"\" "prog_args_string" </dev/null"
retval = system(AFL_MAP_SIZE AFL_CMIN_ALLOW_ANY AFL_CMIN_CRASHES_ONLY"\""showmap"\" -m "mem_limit" -t "timeout" -o \""trace_dir"\" -Z "extra_par" -i \""in_dir"\" -H \""stdin_file"\" -- \""target_bin"\" "prog_args_string" </dev/null")
}
if (retval && (!AFL_CMIN_CRASHES_ONLY && !AFL_CMIN_ALLOW_ANY)) {
print "[!] Exit code "retval" != 0 received from afl-showmap (this means a crashing or timeout input is likely present), terminating..."
if (!ENVIRON["AFL_KEEP_TRACES"]) {
system("rm -rf "trace_dir" 2>/dev/null")
system("rmdir "out_dir)
}
exit retval
}
} }
####################################################### #######################################################

View File

@ -7,6 +7,8 @@
# #
# Copyright 2014, 2015 Google Inc. All rights reserved. # Copyright 2014, 2015 Google Inc. All rights reserved.
# #
# Copyright 2019-2023 AFLplusplus
#
# Licensed under the Apache License, Version 2.0 (the "License"); # Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License. # you may not use this file except in compliance with the License.
# You may obtain a copy of the License at: # You may obtain a copy of the License at:
@ -36,7 +38,7 @@
# array sizes. # array sizes.
# #
echo "corpus minimization tool for afl-fuzz by Michal Zalewski" echo "corpus minimization tool for afl-fuzz"
echo echo
######### #########
@ -46,14 +48,14 @@ echo
# Process command-line options... # Process command-line options...
MEM_LIMIT=none MEM_LIMIT=none
TIMEOUT=none TIMEOUT=5000
unset IN_DIR OUT_DIR STDIN_FILE EXTRA_PAR MEM_LIMIT_GIVEN \ unset IN_DIR OUT_DIR STDIN_FILE EXTRA_PAR MEM_LIMIT_GIVEN F_ARG \
AFL_CMIN_CRASHES_ONLY AFL_CMIN_ALLOW_ANY QEMU_MODE UNICORN_MODE AFL_CMIN_CRASHES_ONLY AFL_CMIN_ALLOW_ANY QEMU_MODE UNICORN_MODE T_ARG
export AFL_QUIET=1 export AFL_QUIET=1
while getopts "+i:o:f:m:t:eOQUACh" opt; do while getopts "+i:o:f:m:t:T:eOQUAChXY" opt; do
case "$opt" in case "$opt" in
@ -69,6 +71,7 @@ while getopts "+i:o:f:m:t:eOQUACh" opt; do
;; ;;
"f") "f")
STDIN_FILE="$OPTARG" STDIN_FILE="$OPTARG"
F_ARG=1
;; ;;
"m") "m")
MEM_LIMIT="$OPTARG" MEM_LIMIT="$OPTARG"
@ -94,10 +97,21 @@ while getopts "+i:o:f:m:t:eOQUACh" opt; do
EXTRA_PAR="$EXTRA_PAR -Q" EXTRA_PAR="$EXTRA_PAR -Q"
QEMU_MODE=1 QEMU_MODE=1
;; ;;
"Y")
EXTRA_PAR="$EXTRA_PAR -X"
NYX_MODE=1
;;
"X")
EXTRA_PAR="$EXTRA_PAR -X"
NYX_MODE=1
;;
"U") "U")
EXTRA_PAR="$EXTRA_PAR -U" EXTRA_PAR="$EXTRA_PAR -U"
UNICORN_MODE=1 UNICORN_MODE=1
;; ;;
"T")
T_ARG="$OPTARG"
;;
"?") "?")
exit 1 exit 1
;; ;;
@ -122,12 +136,14 @@ Required parameters:
Execution control settings: Execution control settings:
-f file - location read by the fuzzed program (stdin) -T tasks - how many parallel processes to create (default=1, "all"=nproc)
-m megs - memory limit for child process ($MEM_LIMIT MB) -f file - location read by the fuzzed program (default: stdin)
-t msec - run time limit for child process (none) -m megs - memory limit for child process (default=$MEM_LIMIT MB)
-t msec - run time limit for child process (default: 5000ms)
-O - use binary-only instrumentation (FRIDA mode) -O - use binary-only instrumentation (FRIDA mode)
-Q - use binary-only instrumentation (QEMU mode) -Q - use binary-only instrumentation (QEMU mode)
-U - use unicorn-based instrumentation (Unicorn mode) -U - use unicorn-based instrumentation (Unicorn mode)
-X - use Nyx mode
Minimization settings: Minimization settings:
@ -142,6 +158,8 @@ AFL_KEEP_TRACES: leave the temporary <out_dir>\.traces directory
AFL_NO_FORKSRV: run target via execve instead of using the forkserver AFL_NO_FORKSRV: run target via execve instead of using the forkserver
AFL_PATH: last resort location to find the afl-showmap binary AFL_PATH: last resort location to find the afl-showmap binary
AFL_SKIP_BIN_CHECK: skip check for target binary AFL_SKIP_BIN_CHECK: skip check for target binary
AFL_CUSTOM_MUTATOR_LIBRARY: custom mutator library (post_process and send)
AFL_PYTHON_MODULE: custom mutator library (post_process and send)
_EOF_ _EOF_
exit 1 exit 1
fi fi
@ -188,6 +206,11 @@ fi
# Check for obvious errors. # Check for obvious errors.
if [ ! "$T_ARG" = "" -a -n "$F_ARG" -a ! "$NYX_MODE" == 1 ]; then
echo "[-] Error: -T and -f can not be used together." 1>&2
exit 1
fi
if [ ! "$MEM_LIMIT" = "none" ]; then if [ ! "$MEM_LIMIT" = "none" ]; then
if [ "$MEM_LIMIT" -lt "5" ]; then if [ "$MEM_LIMIT" -lt "5" ]; then
@ -206,20 +229,23 @@ if [ ! "$TIMEOUT" = "none" ]; then
fi fi
if [ ! -f "$TARGET_BIN" -o ! -x "$TARGET_BIN" ]; then if [ "$NYX_MODE" = "" ]; then
if [ ! -f "$TARGET_BIN" -o ! -x "$TARGET_BIN" ]; then
TNEW="`which "$TARGET_BIN" 2>/dev/null`" TNEW="`which "$TARGET_BIN" 2>/dev/null`"
if [ ! -f "$TNEW" -o ! -x "$TNEW" ]; then
echo "[-] Error: binary '$TARGET_BIN' not found or not executable." 1>&2
exit 1
fi
TARGET_BIN="$TNEW"
if [ ! -f "$TNEW" -o ! -x "$TNEW" ]; then
echo "[-] Error: binary '$TARGET_BIN' not found or not executable." 1>&2
exit 1
fi fi
TARGET_BIN="$TNEW"
fi fi
grep -aq AFL_DUMP_MAP_SIZE "./$TARGET_BIN" && { grep -aq AFL_DUMP_MAP_SIZE "$TARGET_BIN" && {
echo "[!] Trying to obtain the map size of the target ..." echo "[!] Trying to obtain the map size of the target ..."
MAPSIZE=`AFL_DUMP_MAP_SIZE=1 "./$TARGET_BIN" 2>/dev/null` MAPSIZE=`AFL_DUMP_MAP_SIZE=1 "./$TARGET_BIN" 2>/dev/null`
test -n "$MAPSIZE" && { test -n "$MAPSIZE" && {
@ -228,7 +254,7 @@ grep -aq AFL_DUMP_MAP_SIZE "./$TARGET_BIN" && {
} }
} }
if [ "$AFL_SKIP_BIN_CHECK" = "" -a "$QEMU_MODE" = "" -a "$FRIDA_MODE" = "" -a "$UNICORN_MODE" = "" ]; then if [ "$AFL_SKIP_BIN_CHECK" = "" -a "$QEMU_MODE" = "" -a "$FRIDA_MODE" = "" -a "$UNICORN_MODE" = "" -a "$NYX_MODE" = "" ]; then
if ! grep -qF "__AFL_SHM_ID" "$TARGET_BIN"; then if ! grep -qF "__AFL_SHM_ID" "$TARGET_BIN"; then
echo "[-] Error: binary '$TARGET_BIN' doesn't appear to be instrumented." 1>&2 echo "[-] Error: binary '$TARGET_BIN' doesn't appear to be instrumented." 1>&2
@ -285,14 +311,34 @@ if [ ! -x "$SHOWMAP" ]; then
exit 1 exit 1
fi fi
THREADS=
if [ ! "$T_ARG" = "" ]; then
if [ "$T_ARG" = "all" ]; then
THREADS=$(nproc)
else
if [ "$T_ARG" -gt 1 -a "$T_ARG" -le "$(nproc)" ]; then
THREADS=$T_ARG
else
echo "[-] Error: -T parameter must between 2 and $(nproc) or \"all\"." 1>&2
fi
fi
else
if [ -z "$F_ARG" ]; then
echo "[*] Are you aware of the '-T all' parallelize option that massively improves the speed?"
fi
fi
IN_COUNT=$((`ls -- "$IN_DIR" 2>/dev/null | wc -l`)) IN_COUNT=$((`ls -- "$IN_DIR" 2>/dev/null | wc -l`))
if [ "$IN_COUNT" = "0" ]; then if [ "$IN_COUNT" = "0" ]; then
echo "[+] Hmm, no inputs in the target directory. Nothing to be done." echo "[-] Hmm, no inputs in the target directory. Nothing to be done."
rm -rf "$TRACE_DIR" rm -rf "$TRACE_DIR"
exit 1 exit 1
fi fi
echo "[*] Are you aware that afl-cmin is faster than this afl-cmin.bash script?"
echo "[+] Found $IN_COUNT files for minimizing."
FIRST_FILE=`ls "$IN_DIR" | head -1` FIRST_FILE=`ls "$IN_DIR" | head -1`
# Make sure that we're not dealing with a directory. # Make sure that we're not dealing with a directory.
@ -341,6 +387,18 @@ else
fi fi
TMPFILE=$OUT_DIR/.list.$$
if [ ! "$THREADS" = "" ]; then
ls -- "$IN_DIR" > $TMPFILE 2>/dev/null
IN_COUNT=$(cat $TMPFILE | wc -l)
SPLIT=$(($IN_COUNT / $THREADS))
if [ "$(($IN_COUNT % $THREADS))" -gt 0 ]; then
SPLIT=$(($SPLIT + 1))
fi
echo "[+] Splitting workload into $THREADS tasks with $SPLIT items on average each."
split -l $SPLIT $TMPFILE $TMPFILE.
fi
# Let's roll! # Let's roll!
############################# #############################
@ -349,6 +407,7 @@ fi
echo "[*] Obtaining traces for input files in '$IN_DIR'..." echo "[*] Obtaining traces for input files in '$IN_DIR'..."
if [ "$THREADS" = "" ]; then
( (
CUR=0 CUR=0
@ -372,17 +431,58 @@ echo "[*] Obtaining traces for input files in '$IN_DIR'..."
printf "\\r Processing file $CUR/$IN_COUNT... " printf "\\r Processing file $CUR/$IN_COUNT... "
cp "$IN_DIR/$fn" "$STDIN_FILE" cp "$IN_DIR/$fn" "$STDIN_FILE"
"$SHOWMAP" -m "$MEM_LIMIT" -t "$TIMEOUT" -o "$TRACE_DIR/$fn" -Z $EXTRA_PAR -H "$STDIN_FILE" -- "$@" </dev/null "$SHOWMAP" -m "$MEM_LIMIT" -t "$TIMEOUT" -o "$TRACE_DIR/$fn" -Z $EXTRA_PAR -H "$STDIN_FILE" -- "$@" </dev/null
done done
fi fi
echo
) )
echo else
PIDS=
CNT=0
for inputs in $(ls ${TMPFILE}.*); do
(
if [ "$STDIN_FILE" = "" ]; then
cat $inputs | while read -r fn; do
"$SHOWMAP" -m "$MEM_LIMIT" -t "$TIMEOUT" -o "$TRACE_DIR/$fn" -Z $EXTRA_PAR -- "$@" <"$IN_DIR/$fn"
done
else
STDIN_FILE="$inputs.$$"
cat $inputs | while read -r fn; do
cp "$IN_DIR/$fn" "$STDIN_FILE"
"$SHOWMAP" -m "$MEM_LIMIT" -t "$TIMEOUT" -o "$TRACE_DIR/$fn" -Z $EXTRA_PAR -H "$STDIN_FILE" -- "$@" </dev/null
done
fi
) &
PIDS="$PIDS $!"
done
echo "[+] Waiting for running tasks IDs:$PIDS"
wait
echo "[+] all $THREADS running tasks completed."
rm -f ${TMPFILE}*
echo trace dir files: $(ls $TRACE_DIR/*|wc -l)
fi
########################## ##########################
# STEP 2: SORTING TUPLES # # STEP 2: SORTING TUPLES #

View File

@ -287,9 +287,9 @@ $PLOT_EG
_EOF_ _EOF_
) | gnuplot ) | gnuplot || echo "Note: if you see errors concerning 'unknown or ambiguous terminal type' then you need to use a gnuplot that has png support compiled in."
echo "[?] You can also use -g flag to view the plots in an GUI window, and interact with the plots (if you have built afl-plot-ui). Run \"afl-plot-h\" to know more." echo "[?] You can also use -g flag to view the plots in an GUI window, and interact with the plots (if you have built afl-plot-ui). Run \"afl-plot -h\" to know more."
fi fi

View File

@ -110,7 +110,7 @@ if [ "$PLATFORM" = "Darwin" ] ; then
sysctl kern.sysv.shmall=131072000 sysctl kern.sysv.shmall=131072000
echo Settings applied. echo Settings applied.
echo echo
if [ $(launchctl list 2>/dev/null | grep -q '\.ReportCrash$') ] ; then if $(launchctl list 2>/dev/null | grep -q '\.ReportCrash$') ; then
echo echo
echo Unloading the default crash reporter echo Unloading the default crash reporter
SL=/System/Library; PL=com.apple.ReportCrash SL=/System/Library; PL=com.apple.ReportCrash
@ -119,6 +119,7 @@ if [ "$PLATFORM" = "Darwin" ] ; then
echo echo
fi fi
echo It is recommended to disable System Integrity Protection for increased performance. echo It is recommended to disable System Integrity Protection for increased performance.
echo See: https://developer.apple.com/documentation/security/disabling_and_enabling_system_integrity_protection
echo echo
DONE=1 DONE=1
fi fi

View File

@ -70,10 +70,10 @@ if [ -d queue ]; then
fi fi
RED=`tput setaf 9 1 1` RED=`tput setaf 9 1 1 2>/dev/null`
GREEN=`tput setaf 2 1 1` GREEN=`tput setaf 2 1 1 2>/dev/null`
BLUE=`tput setaf 4 1 1` BLUE=`tput setaf 4 1 1 2>/dev/null`
YELLOW=`tput setaf 11 1 1` YELLOW=`tput setaf 11 1 1 2>/dev/null`
NC=`tput sgr0` NC=`tput sgr0`
RESET="$NC" RESET="$NC"
@ -88,6 +88,7 @@ TOTAL_TIME=0
TOTAL_EXECS=0 TOTAL_EXECS=0
TOTAL_EPS=0 TOTAL_EPS=0
TOTAL_CRASHES=0 TOTAL_CRASHES=0
TOTAL_HANGS=0
TOTAL_PFAV=0 TOTAL_PFAV=0
TOTAL_PENDING=0 TOTAL_PENDING=0
@ -141,7 +142,8 @@ for i in `find . -maxdepth 2 -iname fuzzer_stats | sort`; do
sed 's/^command_line.*$/_skip:1/;s/[ ]*:[ ]*/="/;s/$/"/' "$i" >"$TMP" sed 's/^command_line.*$/_skip:1/;s/[ ]*:[ ]*/="/;s/$/"/' "$i" >"$TMP"
. "$TMP" . "$TMP"
DIR=$(dirname "$i")
DIR=${DIR##*/}
RUN_UNIX=$run_time RUN_UNIX=$run_time
RUN_DAYS=$((RUN_UNIX / 60 / 60 / 24)) RUN_DAYS=$((RUN_UNIX / 60 / 60 / 24))
RUN_HRS=$(((RUN_UNIX / 60 / 60) % 24)) RUN_HRS=$(((RUN_UNIX / 60 / 60) % 24))
@ -154,7 +156,7 @@ for i in `find . -maxdepth 2 -iname fuzzer_stats | sort`; do
if [ "$SUMMARY_ONLY" = "" ]; then if [ "$SUMMARY_ONLY" = "" ]; then
echo ">>> $afl_banner ($RUN_DAYS days, $RUN_HRS hrs) fuzzer PID: $fuzzer_pid <<<" echo ">>> $afl_banner instance: $DIR ($RUN_DAYS days, $RUN_HRS hrs) fuzzer PID: $fuzzer_pid <<<"
echo echo
fi fi
@ -189,6 +191,7 @@ for i in `find . -maxdepth 2 -iname fuzzer_stats | sort`; do
TOTAL_EPS=$((TOTAL_EPS + EXEC_SEC)) TOTAL_EPS=$((TOTAL_EPS + EXEC_SEC))
TOTAL_EXECS=$((TOTAL_EXECS + execs_done)) TOTAL_EXECS=$((TOTAL_EXECS + execs_done))
TOTAL_CRASHES=$((TOTAL_CRASHES + saved_crashes)) TOTAL_CRASHES=$((TOTAL_CRASHES + saved_crashes))
TOTAL_HANGS=$((TOTAL_HANGS + saved_hangs))
TOTAL_PENDING=$((TOTAL_PENDING + pending_total)) TOTAL_PENDING=$((TOTAL_PENDING + pending_total))
TOTAL_PFAV=$((TOTAL_PFAV + pending_favs)) TOTAL_PFAV=$((TOTAL_PFAV + pending_favs))
@ -300,6 +303,7 @@ if [ "$ALIVE_CNT" -gt "1" ]; then
fi fi
echo " Crashes saved : $TOTAL_CRASHES" echo " Crashes saved : $TOTAL_CRASHES"
echo " Hangs saved : $TOTAL_HANGS"
echo "Cycles without finds : $TOTAL_WCOP" echo "Cycles without finds : $TOTAL_WCOP"
echo " Time without finds : $TOTAL_LAST_FIND" echo " Time without finds : $TOTAL_LAST_FIND"
echo echo

View File

@ -11,29 +11,6 @@ The `./examples` folder contains examples for custom mutators in python and C.
In `./rust`, you will find rust bindings, including a simple example in `./rust/example` and an example for structured fuzzing, based on lain, in`./rust/example_lain`. In `./rust`, you will find rust bindings, including a simple example in `./rust/example` and an example for structured fuzzing, based on lain, in`./rust/example_lain`.
## The AFL++ grammar agnostic grammar mutator
In `./autotokens` you find a token-level fuzzer that does not need to know
anything about the grammar of an input as long as it is in ascii and allows
whitespace.
It is very fast and effective.
If you are looking for an example of how to effectively create a custom
mutator take a look at this one.
## The AFL++ Grammar Mutator
If you use git to clone AFL++, then the following will incorporate our
excellent grammar custom mutator:
```sh
git submodule update --init
```
Read the README in the [Grammar-Mutator] repository on how to use it.
[Grammar-Mutator]: https://github.com/AFLplusplus/Grammar-Mutator
## Production-Ready Custom Mutators ## Production-Ready Custom Mutators
This directory holds ready to use custom mutators. This directory holds ready to use custom mutators.
@ -47,6 +24,42 @@ and add `AFL_CUSTOM_MUTATOR_ONLY=1` if you only want to use the custom mutator.
Multiple custom mutators can be used by separating their paths with `:` in the environment variable. Multiple custom mutators can be used by separating their paths with `:` in the environment variable.
### The AFL++ grammar agnostic grammar mutator
In `./autotokens` you find a token-level fuzzer that does not need to know
anything about the grammar of an input as long as it is in ascii and allows
whitespace.
It is very fast and effective.
If you are looking for an example of how to effectively create a custom
mutator take a look at this one.
### The AFL++ Grammar Mutator
If you use git to clone AFL++, then the following will incorporate our
excellent grammar custom mutator:
```sh
git submodule update --init
```
Read the README in the [Grammar-Mutator] repository on how to use it.
[Grammar-Mutator]: https://github.com/AFLplusplus/Grammar-Mutator
Note that this custom mutator is not very good though!
### Other Mutators
atnwalk and gramatron are grammar custom mutators. Example grammars are
provided.
honggfuzz, libfuzzer and libafl are partial implementations based on the
mutator implementations of the respective fuzzers.
More for playing than serious usage.
radamsa is slow and not very good.
## 3rd Party Custom Mutators ## 3rd Party Custom Mutators
### Superion Mutators ### Superion Mutators

View File

@ -0,0 +1,22 @@
# An AFL++ custom mutator using TritonDSE
## Installing the requirements
`pip3 install tritondse`
## How to run with an example
```
../../afl-cc -o ../../test-instr ../../test-instr.c
mkdir -p in
echo aaaa > in/in
AFL_DISABLE_TRIM=1 AFL_CUSTOM_MUTATOR_ONLY=1 AFL_SYNC_TIME=1 AFL_PYTHON_MODULE=aflpp_tritondse PYTHONPATH=. ../../afl-fuzz -i in -o out -- ../../test-instr
```
Note that this custom mutator works differently, new finds are synced
after 10-60 seconds to the fuzzing instance. This is necessary because only
C/C++ custom mutators have access to the internal AFL++ state.
Note that you should run first with `AFL_DEBUG` for 5-10 minutes and see if
all important libraries and syscalls are hooked (look at `WARNING` and `CRITICAL`
output during the run, best use with `AFL_NO_UI=1`)

View File

@ -0,0 +1,220 @@
import sys
import os
import logging
import hashlib
from tritondse import CleLoader
from tritondse import CompositeData
from tritondse import Config
from tritondse import CoverageStrategy
from tritondse import ProcessState
from tritondse import Program
from tritondse import Seed
from tritondse import SeedFormat
from tritondse import SymbolicExecutor
from tritondse import SymbolicExplorator
is_debug = False
out_path = ""
input_file = None
prog = None
config = None
dse = None
cycle = 0
count = 0
finding = 0
hashes = set()
format = SeedFormat.RAW
def pre_exec_hook(se: SymbolicExecutor, state: ProcessState):
global count
global hashes
global finding
if se.seed.hash not in hashes:
hashes.add(se.seed.hash)
finding = 1
filename = out_path + "/id:" + f"{count:06}" + "," + se.seed.hash
if not os.path.exists(filename):
if is_debug:
print('Creating queue input ' + filename)
with open(filename, 'wb') as file:
if input_file:
file.write(se.seed.content.files[input_file])
else:
file.write(se.seed.content)
count += 1
#if input_file:
# if is_debug:
# print('Writing to ' + input_file + ' the content: ' + str(se.seed.content))
# with open(input_file, 'wb') as file:
# file.write(se.seed.content)
#def rtn_open(se: SymbolicExecutor, pstate: ProcessState, pc):
# """
# The open behavior.
# """
# logging.debug('open hooked')
#
# # Get arguments
# arg0 = pstate.get_argument_value(0) # const char *pathname
# flags = pstate.get_argument_value(1) # int flags
# mode = pstate.get_argument_value(2) # int mode
# arg0s = pstate.memory.read_string(arg0)
#
# # Concretize the whole path name
# pstate.concretize_memory_bytes(arg0, len(arg0s)+1) # Concretize the whole string + \0
#
# # We use flags as concrete value
# pstate.concretize_argument(1)
#
# # Use the flags to open the file in the write mode.
# mode = ""
# if (flags & 0xFF) == 0x00: # O_RDONLY
# mode = "r"
# elif (flags & 0xFF) == 0x01: # O_WRONLY
# mode = "w"
# elif (flags & 0xFF) == 0x02: # O_RDWR
# mode = "r+"
#
# if (flags & 0x0100): # O_CREAT
# mode += "x"
# if (flags & 0x0200): # O_APPEND
# mode = "a" # replace completely value
#
# if se.seed.is_file_defined(arg0s) and "r" in mode: # input file and opened in reading
# logging.info(f"opening an input file: {arg0s}")
# # Program is opening an input
# data = se.seed.get_file_input(arg0s)
# filedesc = pstate.create_file_descriptor(arg0s, io.BytesIO(data))
# fd = filedesc.id
# else:
# # Try to open it as a regular file
# try:
# fd = open(arg0s, mode) # use the mode here
# filedesc = pstate.create_file_descriptor(arg0s, fd)
# fd = filedesc.id
# except Exception as e:
# logging.debug(f"Failed to open {arg0s} {e}")
# fd = pstate.minus_one
#
# pstate.write_register("rax", fd) # write the return value
# pstate.cpu.program_counter = pstate.pop_stack_value() # pop the return value
# se.skip_instruction() # skip the current instruction so that the engine go straight fetching the next instruction
def init(seed):
global config
global dse
global format
global input_file
global is_debug
global out_path
global prog
# Load the program (LIEF-based program loader).
prog = CleLoader(os.environ['AFL_CUSTOM_INFO_PROGRAM'])
# Process other configuration environment variables.
argv = None
try:
foo = os.environ['AFL_DEBUG']
is_debug = True
except KeyError:
pass
if is_debug:
logging.basicConfig(level=logging.WARNING)
else:
logging.basicConfig(level=logging.CRITICAL)
try:
foo = os.environ['AFL_CUSTOM_INFO_OUT']
out_path = foo + '/../tritondse/queue'
except KeyError:
pass
try:
foo = os.environ['AFL_CUSTOM_INFO_PROGRAM_INPUT']
input_file = foo
except KeyError:
pass
try:
argv_list = os.environ['AFL_CUSTOM_INFO_PROGRAM_ARGV']
argv_tmp = [ os.environ['AFL_CUSTOM_INFO_PROGRAM'] ]
argv_tmp += argv_list.split()
argv = []
# now check for @@
for item in argv_tmp:
if "@@" in item:
input_file = out_path + '/../.input'
argv.append(input_file)
else:
argv.append(item)
except KeyError:
pass
# Create the output directory
os.makedirs(out_path, exist_ok=True)
# Debug
if is_debug:
print('DEBUG target: ' + os.environ['AFL_CUSTOM_INFO_PROGRAM'])
if argv:
print('DEBUG argv: ')
print(argv)
if input_file:
print('DEBUG input_file: ' + input_file)
print('DEBUG out_path: ' + out_path)
print('')
if input_file:
format = SeedFormat.COMPOSITE
# Now set up TritonDSE
config = Config(coverage_strategy = CoverageStrategy.PATH,
debug = is_debug,
pipe_stdout = is_debug,
pipe_stderr = is_debug,
execution_timeout = 1,
program_argv = argv,
smt_timeout= 50,
seed_format = format)
# Create an instance of the Symbolic Explorator
dse = SymbolicExplorator(config, prog)
# Add callbacks.
dse.callback_manager.register_pre_execution_callback(pre_exec_hook)
#dse.callback_manager.register_function_callback("open", rtn_open)
def fuzz(buf, add_buf, max_size):
global finding
finding = 1
while finding == 1:
finding = 0
dse.step()
return b""
def queue_new_entry(filename_new_queue, filename_orig_queue):
global cycle
global dse
# Add seed to the worklist.
with open(filename_new_queue, "rb") as file:
data = file.read()
hash = hashlib.md5(data).hexdigest()
if hash not in hashes:
hashes.add(hash)
if is_debug:
print("NEW FILE " + filename_new_queue + " hash " + hash + " count " + str(cycle))
cycle += 1
if input_file:
seed = Seed(CompositeData(files={"stdin": b"", # nothing on stdin
input_file: data}))
else:
seed = Seed(data)
dse.add_input_seed(seed)
# Start exploration!
#dse.step()
#dse.explore()
pass
# we simulate just doing one single fuzz in the custom mutator
def fuzz_count(buf):
return 1
def splice_optout():
pass

View File

@ -0,0 +1,7 @@
all: atnwalk.so
atnwalk.so: atnwalk.c
$(CC) -I ../../include/ -shared -fPIC -O3 -o atnwalk.so atnwalk.c
clean:
rm -f *.so *.o *~ core

View File

@ -0,0 +1,43 @@
# ATNwalk: Grammar-Based Fuzzing using Only Bit-Mutations
This is a custom mutator integration of ATNwalk that works by communicating via UNIX domain sockets.
Refer to [https://github.com/atnwalk/testbed](https://github.com/atnwalk/testbed) for detailed instructions on how to get ATNwalk running.
## Build
Just type `make` to build `atnwalk.so`.
## Run
**NOTE:** The commands below just demonstrate an example how running ATNwalk looks like and require a working [testbed](https://github.com/atnwalk/testbed)
```bash
# create the required a random seed first
mkdir -p ~/campaign/example/seeds
cd ~/campaign/example/seeds
head -c1 /dev/urandom | ~/atnwalk/build/javascript/bin/decode -wb > seed.decoded 2> seed.encoded
# create the required atnwalk directory and copy the seed
cd ../
mkdir -p atnwalk/in
cp ./seeds/seed.encoded atnwalk/in/seed
cd atnwalk
# assign to a single core when benchmarking it, change the CPU number as required
CPU_ID=0
# start the ATNwalk server
nohup taskset -c ${CPU_ID} ${HOME}/atnwalk/build/javascript/bin/server 100 > server.log 2>&1 &
# start AFL++ with ATNwalk
AFL_SKIP_CPUFREQ=1 \
AFL_DISABLE_TRIM=1 \
AFL_CUSTOM_MUTATOR_ONLY=1 \
AFL_CUSTOM_MUTATOR_LIBRARY=${HOME}/AFLplusplus/custom_mutators/atnwalk/atnwalk.so \
AFL_POST_PROCESS_KEEP_ORIGINAL=1 \
~/AFLplusplus/afl-fuzz -t 100 -i in/ -o out -b ${CPU_ID} -- ~/jerryscript/build/bin/jerry
# make sure to kill the ATNwalk server process after you're done
kill "$(cat atnwalk.pid)"
```

View File

@ -0,0 +1,539 @@
#include "afl-fuzz.h"
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <unistd.h>
#define BUF_SIZE_INIT 4096
#define SOCKET_NAME "./atnwalk.socket"
// how many errors (e.g. timeouts) to tolerate until moving on to the next queue
// entry
#define ATNWALK_ERRORS_MAX 1
// how many execution timeouts to tolerate until moving on to the next queue
// entry
#define EXEC_TIMEOUT_MAX 2
// handshake constants
const uint8_t SERVER_ARE_YOU_ALIVE = 213;
const uint8_t SERVER_YES_I_AM_ALIVE = 42;
// control bits
const uint8_t SERVER_CROSSOVER_BIT = 0b00000001;
const uint8_t SERVER_MUTATE_BIT = 0b00000010;
const uint8_t SERVER_DECODE_BIT = 0b00000100;
const uint8_t SERVER_ENCODE_BIT = 0b00001000;
typedef struct atnwalk_mutator {
afl_state_t *afl;
uint8_t atnwalk_error_count;
uint64_t prev_timeouts;
uint32_t prev_hits;
uint32_t stage_havoc_cur;
uint32_t stage_havoc_max;
uint32_t stage_splice_cur;
uint32_t stage_splice_max;
uint8_t *fuzz_buf;
size_t fuzz_size;
uint8_t *post_process_buf;
size_t post_process_size;
} atnwalk_mutator_t;
int read_all(int fd, uint8_t *buf, size_t buf_size) {
int n;
size_t offset = 0;
while (offset < buf_size) {
n = read(fd, buf + offset, buf_size - offset);
if (n == -1) { return 0; }
offset += n;
}
return 1;
}
int write_all(int fd, uint8_t *buf, size_t buf_size) {
int n;
size_t offset = 0;
while (offset < buf_size) {
n = write(fd, buf + offset, buf_size - offset);
if (n == -1) { return 0; }
offset += n;
}
return 1;
}
void put_uint32(uint8_t *buf, uint32_t val) {
buf[0] = (uint8_t)(val >> 24);
buf[1] = (uint8_t)((val & 0x00ff0000) >> 16);
buf[2] = (uint8_t)((val & 0x0000ff00) >> 8);
buf[3] = (uint8_t)(val & 0x000000ff);
}
uint32_t to_uint32(uint8_t *buf) {
uint32_t val = 0;
val |= (((uint32_t)buf[0]) << 24);
val |= (((uint32_t)buf[1]) << 16);
val |= (((uint32_t)buf[2]) << 8);
val |= ((uint32_t)buf[3]);
return val;
}
void put_uint64(uint8_t *buf, uint64_t val) {
buf[0] = (uint8_t)(val >> 56);
buf[1] = (uint8_t)((val & 0x00ff000000000000) >> 48);
buf[2] = (uint8_t)((val & 0x0000ff0000000000) >> 40);
buf[3] = (uint8_t)((val & 0x000000ff00000000) >> 32);
buf[4] = (uint8_t)((val & 0x00000000ff000000) >> 24);
buf[5] = (uint8_t)((val & 0x0000000000ff0000) >> 16);
buf[6] = (uint8_t)((val & 0x000000000000ff00) >> 8);
buf[7] = (uint8_t)(val & 0x00000000000000ff);
}
/**
* Initialize this custom mutator
*
* @param[in] afl a pointer to the internal state object. Can be ignored for
* now.
* @param[in] seed A seed for this mutator - the same seed should always mutate
* in the same way.
* @return Pointer to the data object this custom mutator instance should use.
* There may be multiple instances of this mutator in one afl-fuzz run!
* Return NULL on error.
*/
atnwalk_mutator_t *afl_custom_init(afl_state_t *afl, unsigned int seed) {
srand(seed);
atnwalk_mutator_t *data =
(atnwalk_mutator_t *)malloc(sizeof(atnwalk_mutator_t));
if (!data) {
perror("afl_custom_init alloc");
return NULL;
}
data->afl = afl;
data->prev_hits = 0;
data->fuzz_buf = (uint8_t *)malloc(BUF_SIZE_INIT);
data->fuzz_size = BUF_SIZE_INIT;
data->post_process_buf = (uint8_t *)malloc(BUF_SIZE_INIT);
data->post_process_size = BUF_SIZE_INIT;
return data;
}
unsigned int afl_custom_fuzz_count(atnwalk_mutator_t *data,
const unsigned char *buf, size_t buf_size) {
// afl_custom_fuzz_count is called exactly once before entering the
// 'stage-loop' for the current queue entry thus, we use it to reset the error
// count and to initialize stage variables (somewhat not intended by the API,
// but still better than rewriting the whole thing to have a custom mutator
// stage)
data->atnwalk_error_count = 0;
data->prev_timeouts = data->afl->total_tmouts;
// it might happen that on the last execution of the splice stage a new path
// is found we need to fix that here and count it
if (data->prev_hits) {
data->afl->stage_finds[STAGE_SPLICE] +=
data->afl->queued_items + data->afl->saved_crashes - data->prev_hits;
}
data->prev_hits = data->afl->queued_items + data->afl->saved_crashes;
data->stage_havoc_cur = 0;
data->stage_splice_cur = 0;
// 50% havoc, 50% splice
data->stage_havoc_max = data->afl->stage_max >> 1;
if (data->stage_havoc_max < HAVOC_MIN) { data->stage_havoc_max = HAVOC_MIN; }
data->stage_splice_max = data->stage_havoc_max;
return data->stage_havoc_max + data->stage_splice_max;
}
size_t fail_fatal(int fd_socket, uint8_t **out_buf) {
if (fd_socket != -1) { close(fd_socket); }
*out_buf = NULL;
return 0;
}
size_t fail_gracefully(int fd_socket, atnwalk_mutator_t *data, uint8_t *buf,
size_t buf_size, uint8_t **out_buf) {
if (fd_socket != -1) { close(fd_socket); }
data->atnwalk_error_count++;
if (data->atnwalk_error_count > ATNWALK_ERRORS_MAX) {
data->afl->stage_max = data->afl->stage_cur;
}
*out_buf = buf;
return buf_size;
}
/**
* Perform custom mutations on a given input
*
* (Optional for now. Required in the future)
*
* @param[in] data pointer returned in afl_custom_init for this fuzz case
* @param[in] buf Pointer to input data to be mutated
* @param[in] buf_size Size of input data
* @param[out] out_buf the buffer we will work on. we can reuse *buf. NULL on
* error.
* @param[in] add_buf Buffer containing the additional test case
* @param[in] add_buf_size Size of the additional test case
* @param[in] max_size Maximum size of the mutated output. The mutation must not
* produce data larger than max_size.
* @return Size of the mutated output.
*/
size_t afl_custom_fuzz(atnwalk_mutator_t *data, uint8_t *buf, size_t buf_size,
uint8_t **out_buf, uint8_t *add_buf, size_t add_buf_size,
size_t max_size) {
struct sockaddr_un addr;
int fd_socket;
uint8_t ctrl_buf[8];
uint8_t wanted;
// let's display what's going on in a nice way
if (data->stage_havoc_cur == 0) {
data->afl->stage_name = (uint8_t *)"atnwalk - havoc";
}
if (data->stage_havoc_cur == data->stage_havoc_max) {
data->afl->stage_name = (uint8_t *)"atnwalk - splice";
}
// increase the respective havoc or splice counters
if (data->stage_havoc_cur < data->stage_havoc_max) {
data->stage_havoc_cur++;
data->afl->stage_cycles[STAGE_HAVOC]++;
} else {
// if there is nothing to splice, continue with havoc and skip splicing this
// time
if (data->afl->ready_for_splicing_count < 1) {
data->stage_havoc_max = data->afl->stage_max;
data->stage_havoc_cur++;
data->afl->stage_cycles[STAGE_HAVOC]++;
} else {
data->stage_splice_cur++;
data->afl->stage_cycles[STAGE_SPLICE]++;
}
}
// keep track of found new corpus seeds per stage
if (data->afl->queued_items + data->afl->saved_crashes > data->prev_hits) {
if (data->stage_splice_cur <= 1) {
data->afl->stage_finds[STAGE_HAVOC] +=
data->afl->queued_items + data->afl->saved_crashes - data->prev_hits;
} else {
data->afl->stage_finds[STAGE_SPLICE] +=
data->afl->queued_items + data->afl->saved_crashes - data->prev_hits;
}
}
data->prev_hits = data->afl->queued_items + data->afl->saved_crashes;
// check whether this input produces a lot of timeouts, if it does then
// abandon this queue entry
if (data->afl->total_tmouts - data->prev_timeouts >= EXEC_TIMEOUT_MAX) {
data->afl->stage_max = data->afl->stage_cur;
return fail_gracefully(-1, data, buf, buf_size, out_buf);
}
// initialize the socket
fd_socket = socket(AF_UNIX, SOCK_STREAM, 0);
if (fd_socket == -1) { return fail_fatal(fd_socket, out_buf); }
memset(&addr, 0, sizeof(addr));
addr.sun_family = AF_UNIX;
strncpy(addr.sun_path, SOCKET_NAME, sizeof(addr.sun_path) - 1);
if (connect(fd_socket, (const struct sockaddr *)&addr, sizeof(addr)) == -1) {
return fail_fatal(fd_socket, out_buf);
}
// ask whether the server is alive
ctrl_buf[0] = SERVER_ARE_YOU_ALIVE;
if (!write_all(fd_socket, ctrl_buf, 1)) {
return fail_fatal(fd_socket, out_buf);
}
// see whether the server replies as expected
if (!read_all(fd_socket, ctrl_buf, 1) ||
ctrl_buf[0] != SERVER_YES_I_AM_ALIVE) {
return fail_fatal(fd_socket, out_buf);
}
// tell the server what we want to do
wanted = SERVER_MUTATE_BIT | SERVER_ENCODE_BIT;
// perform a crossover if we are splicing
if (data->stage_splice_cur > 0) { wanted |= SERVER_CROSSOVER_BIT; }
// tell the server what we want and how much data will be sent
ctrl_buf[0] = wanted;
put_uint32(ctrl_buf + 1, (uint32_t)buf_size);
if (!write_all(fd_socket, ctrl_buf, 5)) {
return fail_fatal(fd_socket, out_buf);
}
// send the data to mutate and encode
if (!write_all(fd_socket, buf, buf_size)) {
return fail_gracefully(fd_socket, data, buf, buf_size, out_buf);
}
if (wanted & SERVER_CROSSOVER_BIT) {
// since we requested crossover, we will first tell how much additional data
// is to be expected
put_uint32(ctrl_buf, (uint32_t)add_buf_size);
if (!write_all(fd_socket, ctrl_buf, 4)) {
return fail_gracefully(fd_socket, data, buf, buf_size, out_buf);
}
// send the additional data for crossover
if (!write_all(fd_socket, add_buf, add_buf_size)) {
return fail_gracefully(fd_socket, data, buf, buf_size, out_buf);
}
// lastly, a seed is required for crossover so send one
put_uint64(ctrl_buf, (uint64_t)rand());
if (!write_all(fd_socket, ctrl_buf, 8)) {
return fail_gracefully(fd_socket, data, buf, buf_size, out_buf);
}
}
// since we requested mutation, we need to provide a seed for that
put_uint64(ctrl_buf, (uint64_t)rand());
if (!write_all(fd_socket, ctrl_buf, 8)) {
return fail_gracefully(fd_socket, data, buf, buf_size, out_buf);
}
// obtain the required buffer size for the data that will be returned
if (!read_all(fd_socket, ctrl_buf, 4)) {
return fail_gracefully(fd_socket, data, buf, buf_size, out_buf);
}
size_t new_size = (size_t)to_uint32(ctrl_buf);
// if the data is too large then we ignore this round
if (new_size > max_size) {
return fail_gracefully(fd_socket, data, buf, buf_size, out_buf);
}
if (new_size > buf_size) {
// buf is too small, need to use data->fuzz_buf, let's see whether we need
// to reallocate
if (new_size > data->fuzz_size) {
data->fuzz_size = new_size << 1;
data->fuzz_buf = (uint8_t *)realloc(data->fuzz_buf, data->fuzz_size);
}
*out_buf = data->fuzz_buf;
} else {
// new_size fits into buf, so re-use it
*out_buf = buf;
}
// obtain the encoded data
if (!read_all(fd_socket, *out_buf, new_size)) {
return fail_gracefully(fd_socket, data, buf, buf_size, out_buf);
}
close(fd_socket);
return new_size;
}
/**
* A post-processing function to use right before AFL writes the test case to
* disk in order to execute the target.
*
* (Optional) If this functionality is not needed, simply don't define this
* function.
*
* @param[in] data pointer returned in afl_custom_init for this fuzz case
* @param[in] buf Buffer containing the test case to be executed
* @param[in] buf_size Size of the test case
* @param[out] out_buf Pointer to the buffer containing the test case after
* processing. External library should allocate memory for out_buf.
* The buf pointer may be reused (up to the given buf_size);
* @return Size of the output buffer after processing or the needed amount.
* A return of 0 indicates an error.
*/
size_t afl_custom_post_process(atnwalk_mutator_t *data, uint8_t *buf,
size_t buf_size, uint8_t **out_buf) {
struct sockaddr_un addr;
int fd_socket;
uint8_t ctrl_buf[8];
// initialize the socket
fd_socket = socket(AF_UNIX, SOCK_STREAM, 0);
if (fd_socket == -1) { return fail_fatal(fd_socket, out_buf); }
memset(&addr, 0, sizeof(addr));
addr.sun_family = AF_UNIX;
strncpy(addr.sun_path, SOCKET_NAME, sizeof(addr.sun_path) - 1);
if (connect(fd_socket, (const struct sockaddr *)&addr, sizeof(addr)) == -1) {
return fail_fatal(fd_socket, out_buf);
}
// ask whether the server is alive
ctrl_buf[0] = SERVER_ARE_YOU_ALIVE;
if (!write_all(fd_socket, ctrl_buf, 1)) {
return fail_fatal(fd_socket, out_buf);
}
// see whether the server replies as expected
if (!read_all(fd_socket, ctrl_buf, 1) ||
ctrl_buf[0] != SERVER_YES_I_AM_ALIVE) {
return fail_fatal(fd_socket, out_buf);
}
// tell the server what we want and how much data will be sent
ctrl_buf[0] = SERVER_DECODE_BIT;
put_uint32(ctrl_buf + 1, (uint32_t)buf_size);
if (!write_all(fd_socket, ctrl_buf, 5)) {
return fail_gracefully(fd_socket, data, buf, buf_size, out_buf);
}
// send the data to decode
if (!write_all(fd_socket, buf, buf_size)) {
return fail_gracefully(fd_socket, data, buf, buf_size, out_buf);
}
// obtain the required buffer size for the data that will be returned
if (!read_all(fd_socket, ctrl_buf, 4)) {
return fail_gracefully(fd_socket, data, buf, buf_size, out_buf);
}
size_t new_size = (size_t)to_uint32(ctrl_buf);
// need to use data->post_process_buf, let's see whether we need to reallocate
if (new_size > data->post_process_size) {
data->post_process_size = new_size << 1;
data->post_process_buf =
(uint8_t *)realloc(data->post_process_buf, data->post_process_size);
}
*out_buf = data->post_process_buf;
// obtain the decoded data
if (!read_all(fd_socket, *out_buf, new_size)) {
return fail_gracefully(fd_socket, data, buf, buf_size, out_buf);
}
close(fd_socket);
return new_size;
}
/**
* Deinitialize everything
*
* @param data The data ptr from afl_custom_init
*/
void afl_custom_deinit(atnwalk_mutator_t *data) {
free(data->fuzz_buf);
free(data->post_process_buf);
free(data);
}

View File

@ -1,342 +0,0 @@
#ifndef CUSTOM_MUTATOR_HELPERS
#define CUSTOM_MUTATOR_HELPERS
#include "config.h"
#include "types.h"
#include <stdlib.h>
#define INITIAL_GROWTH_SIZE (64)
#define RAND_BELOW(limit) (rand() % (limit))
/* Use in a struct: creates a name_buf and a name_size variable. */
#define BUF_VAR(type, name) \
type * name##_buf; \
size_t name##_size;
/* this fills in `&structptr->something_buf, &structptr->something_size`. */
#define BUF_PARAMS(struct, name) \
(void **)&struct->name##_buf, &struct->name##_size
typedef struct {
} afl_t;
static void surgical_havoc_mutate(u8 *out_buf, s32 begin, s32 end) {
static s8 interesting_8[] = {INTERESTING_8};
static s16 interesting_16[] = {INTERESTING_8, INTERESTING_16};
static s32 interesting_32[] = {INTERESTING_8, INTERESTING_16, INTERESTING_32};
switch (RAND_BELOW(12)) {
case 0: {
/* Flip a single bit somewhere. Spooky! */
s32 bit_idx = ((RAND_BELOW(end - begin) + begin) << 3) + RAND_BELOW(8);
out_buf[bit_idx >> 3] ^= 128 >> (bit_idx & 7);
break;
}
case 1: {
/* Set byte to interesting value. */
u8 val = interesting_8[RAND_BELOW(sizeof(interesting_8))];
out_buf[(RAND_BELOW(end - begin) + begin)] = val;
break;
}
case 2: {
/* Set word to interesting value, randomly choosing endian. */
if (end - begin < 2) break;
s32 byte_idx = (RAND_BELOW(end - begin) + begin);
if (byte_idx >= end - 1) break;
switch (RAND_BELOW(2)) {
case 0:
*(u16 *)(out_buf + byte_idx) =
interesting_16[RAND_BELOW(sizeof(interesting_16) >> 1)];
break;
case 1:
*(u16 *)(out_buf + byte_idx) =
SWAP16(interesting_16[RAND_BELOW(sizeof(interesting_16) >> 1)]);
break;
}
break;
}
case 3: {
/* Set dword to interesting value, randomly choosing endian. */
if (end - begin < 4) break;
s32 byte_idx = (RAND_BELOW(end - begin) + begin);
if (byte_idx >= end - 3) break;
switch (RAND_BELOW(2)) {
case 0:
*(u32 *)(out_buf + byte_idx) =
interesting_32[RAND_BELOW(sizeof(interesting_32) >> 2)];
break;
case 1:
*(u32 *)(out_buf + byte_idx) =
SWAP32(interesting_32[RAND_BELOW(sizeof(interesting_32) >> 2)]);
break;
}
break;
}
case 4: {
/* Set qword to interesting value, randomly choosing endian. */
if (end - begin < 8) break;
s32 byte_idx = (RAND_BELOW(end - begin) + begin);
if (byte_idx >= end - 7) break;
switch (RAND_BELOW(2)) {
case 0:
*(u64 *)(out_buf + byte_idx) =
(s64)interesting_32[RAND_BELOW(sizeof(interesting_32) >> 2)];
break;
case 1:
*(u64 *)(out_buf + byte_idx) = SWAP64(
(s64)interesting_32[RAND_BELOW(sizeof(interesting_32) >> 2)]);
break;
}
break;
}
case 5: {
/* Randomly subtract from byte. */
out_buf[(RAND_BELOW(end - begin) + begin)] -= 1 + RAND_BELOW(ARITH_MAX);
break;
}
case 6: {
/* Randomly add to byte. */
out_buf[(RAND_BELOW(end - begin) + begin)] += 1 + RAND_BELOW(ARITH_MAX);
break;
}
case 7: {
/* Randomly subtract from word, random endian. */
if (end - begin < 2) break;
s32 byte_idx = (RAND_BELOW(end - begin) + begin);
if (byte_idx >= end - 1) break;
if (RAND_BELOW(2)) {
*(u16 *)(out_buf + byte_idx) -= 1 + RAND_BELOW(ARITH_MAX);
} else {
u16 num = 1 + RAND_BELOW(ARITH_MAX);
*(u16 *)(out_buf + byte_idx) =
SWAP16(SWAP16(*(u16 *)(out_buf + byte_idx)) - num);
}
break;
}
case 8: {
/* Randomly add to word, random endian. */
if (end - begin < 2) break;
s32 byte_idx = (RAND_BELOW(end - begin) + begin);
if (byte_idx >= end - 1) break;
if (RAND_BELOW(2)) {
*(u16 *)(out_buf + byte_idx) += 1 + RAND_BELOW(ARITH_MAX);
} else {
u16 num = 1 + RAND_BELOW(ARITH_MAX);
*(u16 *)(out_buf + byte_idx) =
SWAP16(SWAP16(*(u16 *)(out_buf + byte_idx)) + num);
}
break;
}
case 9: {
/* Randomly subtract from dword, random endian. */
if (end - begin < 4) break;
s32 byte_idx = (RAND_BELOW(end - begin) + begin);
if (byte_idx >= end - 3) break;
if (RAND_BELOW(2)) {
*(u32 *)(out_buf + byte_idx) -= 1 + RAND_BELOW(ARITH_MAX);
} else {
u32 num = 1 + RAND_BELOW(ARITH_MAX);
*(u32 *)(out_buf + byte_idx) =
SWAP32(SWAP32(*(u32 *)(out_buf + byte_idx)) - num);
}
break;
}
case 10: {
/* Randomly add to dword, random endian. */
if (end - begin < 4) break;
s32 byte_idx = (RAND_BELOW(end - begin) + begin);
if (byte_idx >= end - 3) break;
if (RAND_BELOW(2)) {
*(u32 *)(out_buf + byte_idx) += 1 + RAND_BELOW(ARITH_MAX);
} else {
u32 num = 1 + RAND_BELOW(ARITH_MAX);
*(u32 *)(out_buf + byte_idx) =
SWAP32(SWAP32(*(u32 *)(out_buf + byte_idx)) + num);
}
break;
}
case 11: {
/* Just set a random byte to a random value. Because,
why not. We use XOR with 1-255 to eliminate the
possibility of a no-op. */
out_buf[(RAND_BELOW(end - begin) + begin)] ^= 1 + RAND_BELOW(255);
break;
}
}
}
/* This function calculates the next power of 2 greater or equal its argument.
@return The rounded up power of 2 (if no overflow) or 0 on overflow.
*/
static inline size_t next_pow2(size_t in) {
if (in == 0 || in > (size_t)-1)
return 0; /* avoid undefined behaviour under-/overflow */
size_t out = in - 1;
out |= out >> 1;
out |= out >> 2;
out |= out >> 4;
out |= out >> 8;
out |= out >> 16;
return out + 1;
}
/* This function makes sure *size is > size_needed after call.
It will realloc *buf otherwise.
*size will grow exponentially as per:
https://blog.mozilla.org/nnethercote/2014/11/04/please-grow-your-buffers-exponentially/
Will return NULL and free *buf if size_needed is <1 or realloc failed.
@return For convenience, this function returns *buf.
*/
static inline void *maybe_grow(void **buf, size_t *size, size_t size_needed) {
/* No need to realloc */
if (likely(size_needed && *size >= size_needed)) return *buf;
/* No initial size was set */
if (size_needed < INITIAL_GROWTH_SIZE) size_needed = INITIAL_GROWTH_SIZE;
/* grow exponentially */
size_t next_size = next_pow2(size_needed);
/* handle overflow */
if (!next_size) { next_size = size_needed; }
/* alloc */
*buf = realloc(*buf, next_size);
*size = *buf ? next_size : 0;
return *buf;
}
/* Swaps buf1 ptr and buf2 ptr, as well as their sizes */
static inline void afl_swap_bufs(void **buf1, size_t *size1, void **buf2,
size_t *size2) {
void * scratch_buf = *buf1;
size_t scratch_size = *size1;
*buf1 = *buf2;
*size1 = *size2;
*buf2 = scratch_buf;
*size2 = scratch_size;
}
#undef INITIAL_GROWTH_SIZE
#endif

View File

@ -10,21 +10,21 @@
// afl-fuzz -i in -o out -- ./test-instr -f /tmp/foo // afl-fuzz -i in -o out -- ./test-instr -f /tmp/foo
// //
#include "custom_mutator_helpers.h"
#include <stdio.h> #include <stdio.h>
#include <stdint.h> #include <stdint.h>
#include <stdlib.h> #include <stdlib.h>
#include <unistd.h> #include <unistd.h>
#include <fcntl.h> #include <fcntl.h>
#include "afl-fuzz.h"
typedef struct my_mutator { typedef struct my_mutator {
afl_t *afl; afl_state_t *afl;
} my_mutator_t; } my_mutator_t;
my_mutator_t *afl_custom_init(afl_t *afl, unsigned int seed) { my_mutator_t *afl_custom_init(afl_state_t *afl, unsigned int seed) {
my_mutator_t *data = calloc(1, sizeof(my_mutator_t)); my_mutator_t *data = calloc(1, sizeof(my_mutator_t));
if (!data) { if (!data) {

View File

@ -7,7 +7,7 @@
*/ */
// You need to use -I/path/to/AFLplusplus/include -I. // You need to use -I/path/to/AFLplusplus/include -I.
#include "custom_mutator_helpers.h" #include "afl-fuzz.h"
#include <stdint.h> #include <stdint.h>
#include <stdlib.h> #include <stdlib.h>
@ -26,19 +26,14 @@ static const char *commands[] = {
typedef struct my_mutator { typedef struct my_mutator {
afl_t *afl; afl_state_t *afl;
// any additional data here! // any additional data here!
size_t trim_size_current; size_t trim_size_current;
int trimmming_steps; int trimmming_steps;
int cur_step; int cur_step;
// Reused buffers: u8 *mutated_out, *post_process_buf, *trim_buf;
BUF_VAR(u8, fuzz);
BUF_VAR(u8, data);
BUF_VAR(u8, havoc);
BUF_VAR(u8, trim);
BUF_VAR(u8, post_process);
} my_mutator_t; } my_mutator_t;
@ -53,7 +48,7 @@ typedef struct my_mutator {
* There may be multiple instances of this mutator in one afl-fuzz run! * There may be multiple instances of this mutator in one afl-fuzz run!
* Return NULL on error. * Return NULL on error.
*/ */
my_mutator_t *afl_custom_init(afl_t *afl, unsigned int seed) { my_mutator_t *afl_custom_init(afl_state_t *afl, unsigned int seed) {
srand(seed); // needed also by surgical_havoc_mutate() srand(seed); // needed also by surgical_havoc_mutate()
@ -65,6 +60,27 @@ my_mutator_t *afl_custom_init(afl_t *afl, unsigned int seed) {
} }
if ((data->mutated_out = (u8 *)malloc(MAX_FILE)) == NULL) {
perror("afl_custom_init malloc");
return NULL;
}
if ((data->post_process_buf = (u8 *)malloc(MAX_FILE)) == NULL) {
perror("afl_custom_init malloc");
return NULL;
}
if ((data->trim_buf = (u8 *)malloc(MAX_FILE)) == NULL) {
perror("afl_custom_init malloc");
return NULL;
}
data->afl = afl; data->afl = afl;
return data; return data;
@ -96,31 +112,14 @@ size_t afl_custom_fuzz(my_mutator_t *data, uint8_t *buf, size_t buf_size,
// the fuzzer // the fuzzer
size_t mutated_size = DATA_SIZE <= max_size ? DATA_SIZE : max_size; size_t mutated_size = DATA_SIZE <= max_size ? DATA_SIZE : max_size;
// maybe_grow is optimized to be quick for reused buffers. memcpy(data->mutated_out, buf, buf_size);
u8 *mutated_out = maybe_grow(BUF_PARAMS(data, fuzz), mutated_size);
if (!mutated_out) {
*out_buf = NULL;
perror("custom mutator allocation (maybe_grow)");
return 0; /* afl-fuzz will very likely error out after this. */
}
// Randomly select a command string to add as a header to the packet // Randomly select a command string to add as a header to the packet
memcpy(mutated_out, commands[rand() % 3], 3); memcpy(data->mutated_out, commands[rand() % 3], 3);
// Mutate the payload of the packet if (mutated_size > max_size) { mutated_size = max_size; }
int i;
for (i = 0; i < 8; ++i) {
// Randomly perform one of the (no len modification) havoc mutations *out_buf = data->mutated_out;
surgical_havoc_mutate(mutated_out, 3, mutated_size);
}
if (max_size > mutated_size) { mutated_size = max_size; }
*out_buf = mutated_out;
return mutated_size; return mutated_size;
} }
@ -144,24 +143,16 @@ size_t afl_custom_fuzz(my_mutator_t *data, uint8_t *buf, size_t buf_size,
size_t afl_custom_post_process(my_mutator_t *data, uint8_t *buf, size_t afl_custom_post_process(my_mutator_t *data, uint8_t *buf,
size_t buf_size, uint8_t **out_buf) { size_t buf_size, uint8_t **out_buf) {
uint8_t *post_process_buf = if (buf_size + 5 > MAX_FILE) { buf_size = MAX_FILE - 5; }
maybe_grow(BUF_PARAMS(data, post_process), buf_size + 5);
if (!post_process_buf) {
perror("custom mutator realloc failed."); memcpy(data->post_process_buf + 5, buf, buf_size);
*out_buf = NULL; data->post_process_buf[0] = 'A';
return 0; data->post_process_buf[1] = 'F';
data->post_process_buf[2] = 'L';
data->post_process_buf[3] = '+';
data->post_process_buf[4] = '+';
} *out_buf = data->post_process_buf;
memcpy(post_process_buf + 5, buf, buf_size);
post_process_buf[0] = 'A';
post_process_buf[1] = 'F';
post_process_buf[2] = 'L';
post_process_buf[3] = '+';
post_process_buf[4] = '+';
*out_buf = post_process_buf;
return buf_size + 5; return buf_size + 5;
@ -197,13 +188,6 @@ int32_t afl_custom_init_trim(my_mutator_t *data, uint8_t *buf,
data->cur_step = 0; data->cur_step = 0;
if (!maybe_grow(BUF_PARAMS(data, trim), buf_size)) {
perror("init_trim grow");
return -1;
}
memcpy(data->trim_buf, buf, buf_size); memcpy(data->trim_buf, buf, buf_size);
data->trim_size_current = buf_size; data->trim_size_current = buf_size;
@ -284,27 +268,11 @@ int32_t afl_custom_post_trim(my_mutator_t *data, int success) {
size_t afl_custom_havoc_mutation(my_mutator_t *data, u8 *buf, size_t buf_size, size_t afl_custom_havoc_mutation(my_mutator_t *data, u8 *buf, size_t buf_size,
u8 **out_buf, size_t max_size) { u8 **out_buf, size_t max_size) {
if (buf_size == 0) { *out_buf = buf; // in-place mutation
*out_buf = maybe_grow(BUF_PARAMS(data, havoc), 1); if (buf_size <= sizeof(size_t)) { return buf_size; }
if (!*out_buf) {
perror("custom havoc: maybe_grow"); size_t victim = rand() % (buf_size - sizeof(size_t));
return 0;
}
**out_buf = rand() % 256;
buf_size = 1;
} else {
// We reuse buf here. It's legal and faster.
*out_buf = buf;
}
size_t victim = rand() % buf_size;
(*out_buf)[victim] += rand() % 10; (*out_buf)[victim] += rand() % 10;
return buf_size; return buf_size;
@ -371,9 +339,7 @@ uint8_t afl_custom_queue_new_entry(my_mutator_t *data,
void afl_custom_deinit(my_mutator_t *data) { void afl_custom_deinit(my_mutator_t *data) {
free(data->post_process_buf); free(data->post_process_buf);
free(data->havoc_buf); free(data->mutated_out);
free(data->data_buf);
free(data->fuzz_buf);
free(data->trim_buf); free(data->trim_buf);
free(data); free(data);

View File

@ -45,9 +45,8 @@
1) If you don't want to modify the test case, simply set `*out_buf = in_buf` 1) If you don't want to modify the test case, simply set `*out_buf = in_buf`
and return the original `len`. and return the original `len`.
NOTE: the following is currently NOT true, we abort in this case!
2) If you want to skip this test case altogether and have AFL generate a 2) If you want to skip this test case altogether and have AFL generate a
new one, return 0 or set `*out_buf = NULL`. new one, return 0.
Use this sparingly - it's faster than running the target program Use this sparingly - it's faster than running the target program
with patently useless inputs, but still wastes CPU time. with patently useless inputs, but still wastes CPU time.
@ -59,8 +58,6 @@
Note that the buffer will *not* be freed for you. To avoid memory leaks, Note that the buffer will *not* be freed for you. To avoid memory leaks,
you need to free it or reuse it on subsequent calls (as shown below). you need to free it or reuse it on subsequent calls (as shown below).
*** Feel free to reuse the original 'in_buf' BUFFER and return it. ***
Alright. The example below shows a simple postprocessor that tries to make Alright. The example below shows a simple postprocessor that tries to make
sure that all input files start with "GIF89a". sure that all input files start with "GIF89a".
@ -72,7 +69,7 @@
#include <stdio.h> #include <stdio.h>
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
#include "alloc-inl.h" #include "afl-fuzz.h"
/* Header that must be present at the beginning of every test case: */ /* Header that must be present at the beginning of every test case: */
@ -80,8 +77,7 @@
typedef struct post_state { typedef struct post_state {
unsigned char *buf; size_t size;
size_t size;
} post_state_t; } post_state_t;
@ -95,15 +91,6 @@ void *afl_custom_init(void *afl) {
} }
state->buf = calloc(sizeof(unsigned char), 4096);
if (!state->buf) {
free(state);
perror("calloc");
return NULL;
}
return state; return state;
} }
@ -113,6 +100,10 @@ void *afl_custom_init(void *afl) {
size_t afl_custom_post_process(post_state_t *data, unsigned char *in_buf, size_t afl_custom_post_process(post_state_t *data, unsigned char *in_buf,
unsigned int len, unsigned char **out_buf) { unsigned int len, unsigned char **out_buf) {
/* we do in-place modification as we do not increase the size */
*out_buf = in_buf;
/* Skip execution altogether for buffers shorter than 6 bytes (just to /* Skip execution altogether for buffers shorter than 6 bytes (just to
show how it's done). We can trust len to be sane. */ show how it's done). We can trust len to be sane. */
@ -120,34 +111,7 @@ size_t afl_custom_post_process(post_state_t *data, unsigned char *in_buf,
/* Do nothing for buffers that already start with the expected header. */ /* Do nothing for buffers that already start with the expected header. */
if (!memcmp(in_buf, HEADER, strlen(HEADER))) { if (!memcmp(in_buf, HEADER, strlen(HEADER))) { return len; }
*out_buf = in_buf;
return len;
}
/* Allocate memory for new buffer, reusing previous allocation if
possible. Note we have to use afl-fuzz's own realloc!
We use afl_realloc because it is effective.
You can also work within in_buf, and assign it to *out_buf. */
*out_buf = afl_realloc(out_buf, len);
/* If we're out of memory, the most graceful thing to do is to return the
original buffer and give up on modifying it. Let AFL handle OOM on its
own later on. */
if (!*out_buf) {
*out_buf = in_buf;
return len;
}
if (len > strlen(HEADER))
memcpy(*out_buf + strlen(HEADER), in_buf + strlen(HEADER),
len - strlen(HEADER));
/* Insert the new header. */ /* Insert the new header. */
@ -162,7 +126,6 @@ size_t afl_custom_post_process(post_state_t *data, unsigned char *in_buf,
/* Gets called afterwards */ /* Gets called afterwards */
void afl_custom_deinit(post_state_t *data) { void afl_custom_deinit(post_state_t *data) {
free(data->buf);
free(data); free(data);
} }

View File

@ -30,7 +30,7 @@
#include <string.h> #include <string.h>
#include <zlib.h> #include <zlib.h>
#include <arpa/inet.h> #include <arpa/inet.h>
#include "alloc-inl.h" #include "afl-fuzz.h"
/* A macro to round an integer up to 4 kB. */ /* A macro to round an integer up to 4 kB. */
@ -53,7 +53,7 @@ void *afl_custom_init(void *afl) {
} }
state->buf = calloc(sizeof(unsigned char), 4096); state->buf = calloc(sizeof(unsigned char), MAX_FILE);
if (!state->buf) { if (!state->buf) {
free(state); free(state);
@ -80,21 +80,7 @@ size_t afl_custom_post_process(post_state_t *data, const unsigned char *in_buf,
} }
/* This is not a good way to do it, if you do not need to grow the buffer unsigned int pos = 8;
then just work with in_buf instead for speed reasons.
But we want to show how to grow a buffer, so this is how it's done: */
unsigned int pos = 8;
unsigned char *new_buf = afl_realloc(out_buf, UP4K(len));
if (!new_buf) {
*out_buf = in_buf;
return len;
}
memcpy(new_buf, in_buf, len);
/* Minimum size of a zero-length PNG chunk is 12 bytes; if we /* Minimum size of a zero-length PNG chunk is 12 bytes; if we
don't have that, we can bail out. */ don't have that, we can bail out. */
@ -124,7 +110,7 @@ size_t afl_custom_post_process(post_state_t *data, const unsigned char *in_buf,
if (real_cksum != file_cksum) { if (real_cksum != file_cksum) {
*(uint32_t *)(new_buf + pos + 8 + chunk_len) = real_cksum; *(uint32_t *)(data->buf + pos + 8 + chunk_len) = real_cksum;
} }
@ -134,7 +120,7 @@ size_t afl_custom_post_process(post_state_t *data, const unsigned char *in_buf,
} }
*out_buf = new_buf; *out_buf = data->buf;
return len; return len;
} }

View File

@ -1,6 +1,6 @@
// This simple example just creates random buffer <= 100 filled with 'A' // This simple example just creates random buffer <= 100 filled with 'A'
// needs -I /path/to/AFLplusplus/include // needs -I /path/to/AFLplusplus/include
#include "custom_mutator_helpers.h" #include "afl-fuzz.h"
#include <stdint.h> #include <stdint.h>
#include <stdlib.h> #include <stdlib.h>
@ -13,14 +13,14 @@
typedef struct my_mutator { typedef struct my_mutator {
afl_t *afl; afl_state_t *afl;
// Reused buffers: // Reused buffers:
BUF_VAR(u8, fuzz); u8 *fuzz_buf;
} my_mutator_t; } my_mutator_t;
my_mutator_t *afl_custom_init(afl_t *afl, unsigned int seed) { my_mutator_t *afl_custom_init(afl_state_t *afl, unsigned int seed) {
srand(seed); srand(seed);
my_mutator_t *data = calloc(1, sizeof(my_mutator_t)); my_mutator_t *data = calloc(1, sizeof(my_mutator_t));
@ -31,6 +31,14 @@ my_mutator_t *afl_custom_init(afl_t *afl, unsigned int seed) {
} }
data->fuzz_buf = (u8 *)malloc(MAX_FILE);
if (!data->fuzz_buf) {
perror("afl_custom_init malloc");
return NULL;
}
data->afl = afl; data->afl = afl;
return data; return data;
@ -44,18 +52,10 @@ size_t afl_custom_fuzz(my_mutator_t *data, uint8_t *buf, size_t buf_size,
int size = (rand() % 100) + 1; int size = (rand() % 100) + 1;
if (size > max_size) size = max_size; if (size > max_size) size = max_size;
u8 *mutated_out = maybe_grow(BUF_PARAMS(data, fuzz), size);
if (!mutated_out) {
*out_buf = NULL; memset(data->fuzz_buf, _FIXED_CHAR, size);
perror("custom mutator allocation (maybe_grow)");
return 0; /* afl-fuzz will very likely error out after this. */
} *out_buf = data->fuzz_buf;
memset(mutated_out, _FIXED_CHAR, size);
*out_buf = mutated_out;
return size; return size;
} }

View File

@ -1,22 +0,0 @@
#ifndef CUSTOM_MUTATOR_HELPERS
#define CUSTOM_MUTATOR_HELPERS
#include "config.h"
#include "types.h"
#include "afl-fuzz.h"
#include <stdlib.h>
#define INITIAL_GROWTH_SIZE (64)
/* Use in a struct: creates a name_buf and a name_size variable. */
#define BUF_VAR(type, name) \
type * name##_buf; \
size_t name##_size;
/* this filles in `&structptr->something_buf, &structptr->something_size`. */
#define BUF_PARAMS(struct, name) \
(void **)&struct->name##_buf, &struct->name##_size
#undef INITIAL_GROWTH_SIZE
#endif

View File

@ -3,14 +3,14 @@
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
#include "custom_mutator_helpers.h" #include "afl-fuzz.h"
#include "mangle.h" #include "mangle.h"
#define NUMBER_OF_MUTATIONS 5 #define NUMBER_OF_MUTATIONS 5
uint8_t * queue_input; uint8_t *queue_input;
size_t queue_input_size; size_t queue_input_size;
afl_state_t * afl_struct; afl_state_t *afl_struct;
run_t run; run_t run;
honggfuzz_t global; honggfuzz_t global;
struct _dynfile_t dynfile; struct _dynfile_t dynfile;
@ -18,8 +18,8 @@ struct _dynfile_t dynfile;
typedef struct my_mutator { typedef struct my_mutator {
afl_state_t *afl; afl_state_t *afl;
run_t * run; run_t *run;
u8 * mutator_buf; u8 *mutator_buf;
unsigned int seed; unsigned int seed;
unsigned int extras_cnt, a_extras_cnt; unsigned int extras_cnt, a_extras_cnt;
@ -65,9 +65,9 @@ my_mutator_t *afl_custom_init(afl_state_t *afl, unsigned int seed) {
/* When a new queue entry is added we check if there are new dictionary /* When a new queue entry is added we check if there are new dictionary
entries to add to honggfuzz structure */ entries to add to honggfuzz structure */
uint8_t afl_custom_queue_new_entry(my_mutator_t * data, void afl_custom_queue_new_entry(my_mutator_t *data,
const uint8_t *filename_new_queue, const uint8_t *filename_new_queue,
const uint8_t *filename_orig_queue) { const uint8_t *filename_orig_queue) {
if (run.global->mutate.dictionaryCnt >= 1024) return; if (run.global->mutate.dictionaryCnt >= 1024) return;
@ -97,7 +97,7 @@ uint8_t afl_custom_queue_new_entry(my_mutator_t * data,
} }
return 0; return;
} }

File diff suppressed because it is too large Load Diff

View File

@ -1 +0,0 @@
../examples/custom_mutator_helpers.h

View File

@ -1,6 +1,5 @@
// This simple example just creates random buffer <= 100 filled with 'A' // This simple example just creates random buffer <= 100 filled with 'A'
// needs -I /path/to/AFLplusplus/include // needs -I /path/to/AFLplusplus/include
//#include "custom_mutator_helpers.h"
#include <stdint.h> #include <stdint.h>
#include <stdlib.h> #include <stdlib.h>
@ -8,19 +7,17 @@
#include <stdio.h> #include <stdio.h>
#include "radamsa.h" #include "radamsa.h"
#include "custom_mutator_helpers.h" #include "afl-fuzz.h"
typedef struct my_mutator { typedef struct my_mutator {
afl_t *afl; afl_state_t *afl;
u8 *mutator_buf;
u8 *mutator_buf;
unsigned int seed; unsigned int seed;
} my_mutator_t; } my_mutator_t;
my_mutator_t *afl_custom_init(afl_t *afl, unsigned int seed) { my_mutator_t *afl_custom_init(afl_state_t *afl, unsigned int seed) {
srand(seed); srand(seed);
my_mutator_t *data = calloc(1, sizeof(my_mutator_t)); my_mutator_t *data = calloc(1, sizeof(my_mutator_t));

View File

@ -5,6 +5,8 @@ This uses the symcc to find new paths into the target.
Note that this is a just a proof of concept example! It is better to use Note that this is a just a proof of concept example! It is better to use
the fuzzing helpers of symcc, symqemu, Fuzzolic, etc. rather than this. the fuzzing helpers of symcc, symqemu, Fuzzolic, etc. rather than this.
Also the symqemu custom mutator is better than this.
To use this custom mutator follow the steps in the symcc repository To use this custom mutator follow the steps in the symcc repository
[https://github.com/eurecom-s3/symcc/](https://github.com/eurecom-s3/symcc/) [https://github.com/eurecom-s3/symcc/](https://github.com/eurecom-s3/symcc/)
on how to build symcc and how to instrument a target binary (the same target on how to build symcc and how to instrument a target binary (the same target

View File

@ -0,0 +1,14 @@
ifdef DEBUG
CFLAGS += -DDEBUG
endif
all: symqemu-mutator.so
CFLAGS += -O3 -funroll-loops
symqemu-mutator.so: symqemu.c
$(CC) -g $(CFLAGS) $(CPPFLAGS) -g -I../../include -shared -fPIC -o symqemu-mutator.so symqemu.c
clean:
rm -f symqemu-mutator.so *.o *~ core

View File

@ -0,0 +1,19 @@
# custum mutator: symqemu
This uses the symcc to find new paths into the target.
## How to build and use
To use this custom mutator follow the steps in the symqemu repository
[https://github.com/eurecom-s3/symqemu/](https://github.com/eurecom-s3/symqemu/)
on how to build symqemu-x86_x64 and put it in your `PATH`.
Just type `make` to build this custom mutator.
```AFL_CUSTOM_MUTATOR_LIBRARY=custom_mutators/symqemu/symqemu-mutator.so AFL_DISABLE_TRIM=1 afl-fuzz ...```
## Options
`SYMQEMU_ALL=1` - use concolic solving on **all** queue items, not only interesting/favorite ones.
`SYMQEMU_LATE=1` - use concolic solving only after there have been no finds for 5 minutes.

View File

@ -0,0 +1,424 @@
#define _GNU_SOURCE
#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <ctype.h>
#include "config.h"
#include "debug.h"
#include "afl-fuzz.h"
#include "common.h"
afl_state_t *afl_struct;
static u32 debug = 0;
static u32 found_items = 0;
#define SYMQEMU_LOCATION "symqemu"
#define DBG(x...) \
if (debug) { fprintf(stderr, x); }
typedef struct my_mutator {
afl_state_t *afl;
u32 all;
u32 late;
u8 *mutator_buf;
u8 *out_dir;
u8 *target;
u8 *symqemu;
u8 *input_file;
u32 counter;
u32 seed;
u32 argc;
u8 **argv;
} my_mutator_t;
my_mutator_t *afl_custom_init(afl_state_t *afl, unsigned int seed) {
if (getenv("AFL_DEBUG")) debug = 1;
my_mutator_t *data = calloc(1, sizeof(my_mutator_t));
if (!data) {
perror("afl_custom_init alloc");
return NULL;
}
char *path = getenv("PATH");
char *exec_name = "symqemu-x86_64";
char *token = strtok(path, ":");
char exec_path[4096];
while (token != NULL && data->symqemu == NULL) {
snprintf(exec_path, sizeof(exec_path), "%s/%s", token, exec_name);
if (access(exec_path, X_OK) == 0) {
data->symqemu = (u8 *)strdup(exec_path);
break;
}
token = strtok(NULL, ":");
}
if (!data->symqemu) FATAL("symqemu binary %s not found", exec_name);
DBG("Found %s\n", data->symqemu);
if (getenv("AFL_CUSTOM_MUTATOR_ONLY")) {
WARNF(
"the symqemu module is not very effective with "
"AFL_CUSTOM_MUTATOR_ONLY.");
}
if ((data->mutator_buf = malloc(MAX_FILE)) == NULL) {
free(data);
perror("mutator_buf alloc");
return NULL;
}
data->target = getenv("AFL_CUSTOM_INFO_PROGRAM");
u8 *path_tmp = getenv("AFL_CUSTOM_INFO_OUT");
u32 len = strlen(path_tmp) + 32;
u8 *symqemu_path = malloc(len);
data->out_dir = malloc(len);
snprintf(symqemu_path, len, "%s/%s", path_tmp, SYMQEMU_LOCATION);
snprintf(data->out_dir, len, "%s/out", symqemu_path, path_tmp);
(void)mkdir(symqemu_path, 0755);
(void)mkdir(data->out_dir, 0755);
setenv("SYMCC_OUTPUT_DIR", data->out_dir, 1);
data->input_file = getenv("AFL_CUSTOM_INFO_PROGRAM_INPUT");
u8 *tmp = NULL;
if ((tmp = getenv("AFL_CUSTOM_INFO_PROGRAM_ARGV")) && *tmp) {
int argc = 0, index = 2;
for (u32 i = 0; i < strlen(tmp); ++i)
if (isspace(tmp[i])) ++argc;
data->argv = (u8 **)malloc((argc + 4) * sizeof(u8 **));
u8 *p = strdup(tmp);
do {
data->argv[index] = p;
while (*p && !isspace(*p))
++p;
if (*p) {
*p++ = 0;
while (isspace(*p))
++p;
}
if (strcmp(data->argv[index], "@@") == 0) {
if (!data->input_file) {
u32 ilen = strlen(symqemu_path) + 32;
data->input_file = malloc(ilen);
snprintf(data->input_file, ilen, "%s/.input", symqemu_path);
}
data->argv[index] = data->input_file;
}
DBG("%d: %s\n", index, data->argv[index]);
index++;
} while (*p);
data->argv[index] = NULL;
data->argc = index;
} else {
data->argv = (u8 **)malloc(8 * sizeof(u8 **));
data->argc = 2;
data->argv[2] = NULL;
}
data->argv[0] = data->symqemu;
data->argv[1] = data->target;
data->afl = afl;
data->seed = seed;
afl_struct = afl;
if (getenv("SYMQEMU_ALL")) { data->all = 1; }
if (getenv("SYMQEMU_LATE")) { data->late = 1; }
if (data->input_file) { setenv("SYMCC_INPUT_FILE", data->input_file, 1); }
DBG("out_dir=%s, target=%s, input_file=%s, argc=%u\n", data->out_dir,
data->target,
data->input_file ? (char *)data->input_file : (char *)"<stdin>",
data->argc);
if (debug) {
fprintf(stderr, "[");
for (u32 i = 0; i <= data->argc; ++i)
fprintf(stderr, " \"%s\"",
data->argv[i] ? (char *)data->argv[i] : "<NULL>");
fprintf(stderr, " ]\n");
}
return data;
}
/* No need to receive a splicing item */
void afl_custom_splice_optout(void *data) {
(void)(data);
}
/* Get unix time in milliseconds */
inline u64 get_cur_time(void) {
struct timeval tv;
struct timezone tz;
gettimeofday(&tv, &tz);
return (tv.tv_sec * 1000ULL) + (tv.tv_usec / 1000);
}
u32 afl_custom_fuzz_count(my_mutator_t *data, const u8 *buf, size_t buf_size) {
if (likely((!afl_struct->queue_cur->favored && !data->all) ||
afl_struct->queue_cur->was_fuzzed)) {
return 0;
}
if (likely(data->late)) {
if (unlikely(get_cur_time() - afl_struct->last_find_time <=
10 * 60 * 1000)) {
return 0;
}
}
int pipefd[2];
struct stat st;
if (afl_struct->afl_env.afl_no_ui) {
ACTF("Sending to symqemu: %s", afl_struct->queue_cur->fname);
}
if (!(stat(afl_struct->queue_cur->fname, &st) == 0 && S_ISREG(st.st_mode) &&
st.st_size)) {
PFATAL("Couldn't find enqueued file: %s", afl_struct->queue_cur->fname);
}
if (afl_struct->fsrv.use_stdin) {
if (pipe(pipefd) == -1) {
PFATAL(
"Couldn't create a pipe for interacting with symqemu child process");
}
}
if (data->input_file) {
int fd = open(data->input_file, O_WRONLY | O_CREAT | O_TRUNC, 0644);
ssize_t s = write(fd, buf, buf_size);
close(fd);
DBG("wrote %zd/%zd to %s\n", s, buf_size, data->input_file);
}
int pid = fork();
if (pid == -1) return 0;
if (likely(pid)) {
if (!data->input_file || afl_struct->fsrv.use_stdin) {
close(pipefd[0]);
if (fcntl(pipefd[1], F_GETPIPE_SZ)) {
fcntl(pipefd[1], F_SETPIPE_SZ, MAX_FILE);
}
ck_write(pipefd[1], buf, buf_size, data->input_file);
close(pipefd[1]);
}
pid = waitpid(pid, NULL, 0);
DBG("symqemu finished executing!\n");
} else /* (pid == 0) */ { // child
if (afl_struct->fsrv.use_stdin) {
close(pipefd[1]);
dup2(pipefd[0], 0);
}
DBG("exec=%s\n", data->target);
if (!debug) {
close(1);
close(2);
dup2(afl_struct->fsrv.dev_null_fd, 1);
dup2(afl_struct->fsrv.dev_null_fd, 2);
}
execvp((char *)data->argv[0], (char **)data->argv);
fprintf(stderr, "Executing: [");
for (u32 i = 0; i <= data->argc; ++i)
fprintf(stderr, " \"%s\"",
data->argv[i] ? (char *)data->argv[i] : "<NULL>");
fprintf(stderr, " ]\n");
FATAL("Failed to execute %s %s\n", data->argv[0], data->argv[1]);
exit(-1);
}
/* back in mother process */
struct dirent **nl;
s32 i, items = scandir(data->out_dir, &nl, NULL, NULL);
found_items = 0;
char source_name[4096];
if (items > 0) {
for (i = 0; i < (u32)items; ++i) {
// symqemu output files start with a digit
if (!isdigit(nl[i]->d_name[0])) continue;
struct stat st;
snprintf(source_name, sizeof(source_name), "%s/%s", data->out_dir,
nl[i]->d_name);
DBG("file=%s\n", source_name);
if (stat(source_name, &st) == 0 && S_ISREG(st.st_mode) && st.st_size) {
++found_items;
}
free(nl[i]);
}
free(nl);
}
DBG("Done, found %u items!\n", found_items);
return found_items;
}
size_t afl_custom_fuzz(my_mutator_t *data, u8 *buf, size_t buf_size,
u8 **out_buf, u8 *add_buf, size_t add_buf_size,
size_t max_size) {
struct dirent **nl;
s32 done = 0, i, items = scandir(data->out_dir, &nl, NULL, NULL);
char source_name[4096];
if (items > 0) {
for (i = 0; i < (u32)items; ++i) {
// symqemu output files start with a digit
if (!isdigit(nl[i]->d_name[0])) continue;
struct stat st;
snprintf(source_name, sizeof(source_name), "%s/%s", data->out_dir,
nl[i]->d_name);
DBG("file=%s\n", source_name);
if (stat(source_name, &st) == 0 && S_ISREG(st.st_mode) && st.st_size) {
int fd = open(source_name, O_RDONLY);
if (fd < 0) { goto got_an_issue; }
ssize_t r = read(fd, data->mutator_buf, MAX_FILE);
close(fd);
DBG("fn=%s, fd=%d, size=%ld\n", source_name, fd, r);
if (r < 1) { goto got_an_issue; }
done = 1;
--found_items;
unlink(source_name);
*out_buf = data->mutator_buf;
return (u32)r;
}
free(nl[i]);
}
free(nl);
}
got_an_issue:
*out_buf = NULL;
return 0;
}
/**
* Deinitialize everything
*
* @param data The data ptr from afl_custom_init
*/
void afl_custom_deinit(my_mutator_t *data) {
free(data->mutator_buf);
free(data);
}

View File

@ -3,24 +3,64 @@
This is the list of all noteworthy changes made in every public This is the list of all noteworthy changes made in every public
release of the tool. See README.md for the general instruction manual. release of the tool. See README.md for the general instruction manual.
### Version ++4.06a (dev) ### Version ++4.07a (dev)
- afl-fuzz:
- reverse reading the seeds only on restarts (increases performance)
- new env `AFL_POST_PROCESS_KEEP_ORIGINAL` to keep the orignal
data before post process on finds (for atnwalk custom mutator)
- new env `AFL_IGNORE_PROBLEMS_COVERAGE` to ignore coverage from
loaded libs after forkserver initialization (required by Mozilla)
- afl-cc:
- added @responsefile support
- new env `AFL_LLVM_LTO_SKIPINIT` to support the AFL++ based WASM
(https://github.com/fgsect/WAFL) project
- error and print help if afl-clan-lto is used with lto=thin
- rewrote our PCGUARD pass to be compatible with LLVM 15+ shenanigans,
requires LLVM 13+ now instead of 10.0.1+
- fallback to native LLVM PCGUARD if our PCGUARD is unavailable
- afl-showmap:
- added custom mutator post_process and send support
- add `-I filelist` option, an alternative to `-i in_dir`
- afl-cmin + afl-cmin.bash:
- `-T threads` parallel task support, can be a huge speedup!
- qemu_mode:
- Persistent mode + QASAN support for ppc32 targets by @worksbutnottested
- a new grammar custom mutator atnwalk was submitted by @voidptr127 !
- two new custom mutators are now available:
- TritonDSE in custom_mutators/aflpp_tritondse
- SymQEMU in custom_mutators/symqemu
### Version ++4.06c (release)
- afl-fuzz: - afl-fuzz:
- ensure temporary file descriptor is closed when not used - ensure temporary file descriptor is closed when not used
- added `AFL_NO_WARN_INSTABILITY` - added `AFL_NO_WARN_INSTABILITY`
- added `AFL_FRIDA_STATS_INTERVAL`
- added time_wo_finds to fuzzer_stats - added time_wo_finds to fuzzer_stats
- fixed a crash in pizza (1st april easter egg) mode. Sorry for
everyone who was affected!
- allow pizza mode to be disabled when AFL_PIZZA_MODE is set to -1
- option `-p mmopt` now also selects new queue items more often
- fix bug in post_process custom mutator implementation
- print name of custom mutator in UI
- slight changes that improve fuzzer performance
- afl-cc: - afl-cc:
- add CFI sanitizer variant to gcc targets - add CFI sanitizer variant to gcc targets
- llvm 16 support (thanks to @devnexen!) - llvm 16 + 17 support (thanks to @devnexen!)
- support llvm 15 native pcguard changes - support llvm 15 native pcguard changes
- support for LLVMFuzzerTestOneInput -1 return - support for LLVMFuzzerTestOneInput -1 return
- LTO autoken and llvm_mode: added AFL_LLVM_DICT2FILE_NO_MAIN support
- qemu_mode: - qemu_mode:
- fix _RANGES envs to allow hyphens in the filenames - fix _RANGES envs to allow hyphens in the filenames
- new custom module: autotoken, grammar free fuzzer for text inputs - basic riscv support
- LTO autoken and llvm_mode: added AFL_LLVM_DICT2FILE_NO_MAIN support - frida_mode:
- added `AFL_FRIDA_STATS_INTERVAL`
- fix issue on MacOS
- unicorn_mode:
- updated and minor issues fixed
- nyx_mode support for all tools
- better sanitizer default options support for all tools - better sanitizer default options support for all tools
- unicorn_mode: updated and minor issues fixed - new custom module: autotoken, a grammar free fuzzer for text inputs
- frida_mode: fix issue on MacOS - fixed custom mutator C examples
- more minor fixes and cross-platform support - more minor fixes and cross-platform support
### Version ++4.05c (release) ### Version ++4.05c (release)
@ -199,7 +239,7 @@
afl-showmap and other tools. afl-showmap and other tools.
- afl-cc: - afl-cc:
- detect overflow reads on initial input buffer for asan - detect overflow reads on initial input buffer for asan
- new cmplog mode (incompatible with older afl++ versions) - new cmplog mode (incompatible with older AFL++ versions)
- support llvm IR select instrumentation for default PCGUARD and LTO - support llvm IR select instrumentation for default PCGUARD and LTO
- fix for shared linking on MacOS - fix for shared linking on MacOS
- better selective instrumentation AFL_LLVM_{ALLOW|DENY}LIST - better selective instrumentation AFL_LLVM_{ALLOW|DENY}LIST

View File

@ -171,6 +171,14 @@ If you find an interesting or important question missing, submit it via
The more "unstable" edges there are, the harder it is for AFL++ to identify The more "unstable" edges there are, the harder it is for AFL++ to identify
valid new paths. valid new paths.
If you fuzz in persistent mode (`AFL_LOOP` or `LLVMFuzzerTestOneInput()`
harnesses, a large number of unstable edges can mean that the target keeps
internal state and therefore it is possible that crashes cannot be replayed.
In such a case do either **not** fuzz in persistent mode (remove `AFL_LOOP()`
from your harness or call `LLVMFuzzerTestOneInput()` harnesses with `@@`),
or set a low `AFL_LOOP` value, e.g. 100, and enable `AFL_PERSISTENT_RECORD`
in `config.h` with the same value.
A value above 90% is usually fine and a value above 80% is also still ok, and A value above 90% is usually fine and a value above 80% is also still ok, and
even a value above 20% can still result in successful finds of bugs. However, even a value above 20% can still result in successful finds of bugs. However,
it is recommended that for values below 90% or 80% you should take it is recommended that for values below 90% or 80% you should take
@ -229,7 +237,8 @@ If you find an interesting or important question missing, submit it via
If this is not a viable option, you can set `AFL_IGNORE_PROBLEMS=1` but then If this is not a viable option, you can set `AFL_IGNORE_PROBLEMS=1` but then
the existing map will be used also for the newly loaded libraries, which the existing map will be used also for the newly loaded libraries, which
allows it to work, however, the efficiency of the fuzzing will be partially allows it to work, however, the efficiency of the fuzzing will be partially
degraded. degraded. Note that there is additionally `AFL_IGNORE_PROBLEMS_COVERAGE` to
additionally tell AFL++ to ignore any coverage from the late loaded libaries.
</p></details> </p></details>
<details> <details>

View File

@ -3,9 +3,8 @@
## Linux on x86 ## Linux on x86
An easy way to install AFL++ with everything compiled is available via docker: An easy way to install AFL++ with everything compiled is available via docker:
You can use the [Dockerfile](../Dockerfile) (which has gcc-10 and clang-12 - You can use the [Dockerfile](../Dockerfile) or just pull directly from the
hence afl-clang-lto is available) or just pull directly from the Docker Hub Docker Hub (for x86_64 and arm64):
(for x86_64 and arm64):
```shell ```shell
docker pull aflplusplus/aflplusplus: docker pull aflplusplus/aflplusplus:
@ -21,14 +20,14 @@ development state of AFL++.
If you want to build AFL++ yourself, you have many options. The easiest choice If you want to build AFL++ yourself, you have many options. The easiest choice
is to build and install everything: is to build and install everything:
NOTE: depending on your Debian/Ubuntu/Kali/... release, replace `-12` with NOTE: depending on your Debian/Ubuntu/Kali/... release, replace `-14` with
whatever llvm version is available. We recommend llvm 12, 13 or 14. whatever llvm version is available. We recommend llvm 13, 14, 15 or 16.
```shell ```shell
sudo apt-get update sudo apt-get update
sudo apt-get install -y build-essential python3-dev automake cmake git flex bison libglib2.0-dev libpixman-1-dev python3-setuptools cargo libgtk-3-dev sudo apt-get install -y build-essential python3-dev automake cmake git flex bison libglib2.0-dev libpixman-1-dev python3-setuptools cargo libgtk-3-dev
# try to install llvm 12 and install the distro default if that fails # try to install llvm 14 and install the distro default if that fails
sudo apt-get install -y lld-12 llvm-12 llvm-12-dev clang-12 || sudo apt-get install -y lld llvm llvm-dev clang sudo apt-get install -y lld-14 llvm-14 llvm-14-dev clang-14 || sudo apt-get install -y lld llvm llvm-dev clang
sudo apt-get install -y gcc-$(gcc --version|head -n1|sed 's/\..*//'|sed 's/.* //')-plugin-dev libstdc++-$(gcc --version|head -n1|sed 's/\..*//'|sed 's/.* //')-dev sudo apt-get install -y gcc-$(gcc --version|head -n1|sed 's/\..*//'|sed 's/.* //')-plugin-dev libstdc++-$(gcc --version|head -n1|sed 's/\..*//'|sed 's/.* //')-dev
sudo apt-get install -y ninja-build # for QEMU mode sudo apt-get install -y ninja-build # for QEMU mode
git clone https://github.com/AFLplusplus/AFLplusplus git clone https://github.com/AFLplusplus/AFLplusplus
@ -51,7 +50,7 @@ make source-only
These build targets exist: These build targets exist:
* all: the main afl++ binaries and llvm/gcc instrumentation * all: the main AFL++ binaries and llvm/gcc instrumentation
* binary-only: everything for binary-only fuzzing: frida_mode, nyx_mode, * binary-only: everything for binary-only fuzzing: frida_mode, nyx_mode,
qemu_mode, frida_mode, unicorn_mode, coresight_mode, libdislocator, qemu_mode, frida_mode, unicorn_mode, coresight_mode, libdislocator,
libtokencap libtokencap
@ -79,22 +78,20 @@ make STATIC=1
These build options exist: These build options exist:
* STATIC - compile AFL++ static * STATIC - compile AFL++ static
* CODE_COVERAGE - compile the target for code coverage (see docs/instrumentation/README.llvm.md)
* ASAN_BUILD - compiles AFL++ with memory sanitizer for debug purposes * ASAN_BUILD - compiles AFL++ with memory sanitizer for debug purposes
* UBSAN_BUILD - compiles AFL++ tools with undefined behaviour sanitizer for * UBSAN_BUILD - compiles AFL++ tools with undefined behaviour sanitizer for debug purposes
debug purposes
* DEBUG - no optimization, -ggdb3, all warnings and -Werror * DEBUG - no optimization, -ggdb3, all warnings and -Werror
* LLVM_DEBUG - shows llvm deprecation warnings * LLVM_DEBUG - shows llvm deprecation warnings
* PROFILING - compile afl-fuzz with profiling information * PROFILING - compile afl-fuzz with profiling information
* INTROSPECTION - compile afl-fuzz with mutation introspection * INTROSPECTION - compile afl-fuzz with mutation introspection
* NO_PYTHON - disable python support * NO_PYTHON - disable python support
* NO_SPLICING - disables splicing mutation in afl-fuzz, not recommended for * NO_SPLICING - disables splicing mutation in afl-fuzz, not recommended for normal fuzzing
normal fuzzing
* NO_NYX - disable building nyx mode dependencies * NO_NYX - disable building nyx mode dependencies
* NO_CORESIGHT - disable building coresight (arm64 only) * NO_CORESIGHT - disable building coresight (arm64 only)
* NO_UNICORN_ARM64 - disable building unicorn on arm64 * NO_UNICORN_ARM64 - disable building unicorn on arm64
* AFL_NO_X86 - if compiling on non-intel/amd platforms * AFL_NO_X86 - if compiling on non-intel/amd platforms
* LLVM_CONFIG - if your distro doesn't use the standard name for llvm-config * LLVM_CONFIG - if your distro doesn't use the standard name for llvm-config (e.g., Debian)
(e.g., Debian)
e.g.: `make LLVM_CONFIG=llvm-config-14` e.g.: `make LLVM_CONFIG=llvm-config-14`

View File

@ -131,6 +131,11 @@ jitter, or is a hash map function etc., then it should not be instrumented.
To be able to exclude these functions (based on AFL++'s measured stability), the To be able to exclude these functions (based on AFL++'s measured stability), the
following process will allow to identify functions with variable edges. following process will allow to identify functions with variable edges.
Note that this is only useful for non-persistent targets!
If a persistent target is unstable whereas when run non-persistent is fine,
then this means that the target is keeping internal state, which is bad for
fuzzing. Fuzz such targets **without** persistent mode.
Four steps are required to do this and it also requires quite some knowledge of Four steps are required to do this and it also requires quite some knowledge of
coding and/or disassembly and is effectively possible only with `afl-clang-fast` coding and/or disassembly and is effectively possible only with `afl-clang-fast`
`PCGUARD` and `afl-clang-lto` `LTO` instrumentation. `PCGUARD` and `afl-clang-lto` `LTO` instrumentation.

View File

@ -118,7 +118,7 @@ def deinit(): # optional for Python
### Custom Mutation ### Custom Mutation
- `init`: - `init` (optional in Python):
This method is called when AFL++ starts up and is used to seed RNG and set This method is called when AFL++ starts up and is used to seed RNG and set
up buffers and state. up buffers and state.
@ -184,6 +184,11 @@ def deinit(): # optional for Python
to the target, e.g. if it is too short, too corrupted, etc. If so, to the target, e.g. if it is too short, too corrupted, etc. If so,
return a NULL buffer and zero length (or a 0 length string in Python). return a NULL buffer and zero length (or a 0 length string in Python).
NOTE: Do not make any random changes to the data in this function!
PERFORMANCE for C/C++: If possible make the changes in-place (so modify
the `*data` directly, and return it as `*outbuf = data`.
- `fuzz_send` (optional): - `fuzz_send` (optional):
This method can be used if you want to send data to the target yourself, This method can be used if you want to send data to the target yourself,
@ -202,7 +207,7 @@ def deinit(): # optional for Python
discovered if compiled with INTROSPECTION. The custom mutator can then discovered if compiled with INTROSPECTION. The custom mutator can then
return a string (const char *) that reports the exact mutations used. return a string (const char *) that reports the exact mutations used.
- `deinit`: - `deinit` (optional in Python):
The last method to be called, deinitializing the state. The last method to be called, deinitializing the state.
@ -299,6 +304,34 @@ Note: for some distributions, you might also need the package `python[3]-apt`.
In case your setup is different, set the necessary variables like this: In case your setup is different, set the necessary variables like this:
`PYTHON_INCLUDE=/path/to/python/include LDFLAGS=-L/path/to/python/lib make`. `PYTHON_INCLUDE=/path/to/python/include LDFLAGS=-L/path/to/python/lib make`.
### Helpers
For C/C++ custom mutators you get a pointer to `afl_state_t *afl` in the
`afl_custom_init()` which contains all information that you need.
Note that if you access it, you need to recompile your custom mutator if
you update AFL++ because the structure might have changed!
For mutators written in Python, Rust, GO, etc. there are a few environment
variables set to help you to get started:
`AFL_CUSTOM_INFO_PROGRAM` - the program name of the target that is executed.
If your custom mutator is used with modes like Qemu (`-Q`), this will still
contain the target program, not afl-qemu-trace.
`AFL_CUSTOM_INFO_PROGRAM_INPUT` - if the `-f` parameter is used with afl-fuzz
then this value is found in this environment variable.
`AFL_CUSTOM_INFO_PROGRAM_ARGV` - this contains the parameters given to the
target program and still has the `@@` identifier in there.
Note: If `AFL_CUSTOM_INFO_PROGRAM_INPUT` is empty and `AFL_CUSTOM_INFO_PROGRAM_ARGV`
is either empty or does not contain `@@` then the target gets the input via
`stdin`.
`AFL_CUSTOM_INFO_OUT` - This is the output directory for this fuzzer instance,
so if `afl-fuzz` was called with `-o out -S foobar`, then this will be set to
`out/foobar`.
### Custom Mutator Preparation ### Custom Mutator Preparation
For C/C++ mutators, the source code must be compiled as a shared object: For C/C++ mutators, the source code must be compiled as a shared object:

View File

@ -156,7 +156,7 @@ Available options:
- LTO - LTO instrumentation - LTO - LTO instrumentation
- NATIVE - clang's original pcguard based instrumentation - NATIVE - clang's original pcguard based instrumentation
- NGRAM-x - deeper previous location coverage (from NGRAM-2 up to NGRAM-16) - NGRAM-x - deeper previous location coverage (from NGRAM-2 up to NGRAM-16)
- PCGUARD - our own pcgard based instrumentation (default) - PCGUARD - our own pcguard based instrumentation (default)
#### CMPLOG #### CMPLOG
@ -240,7 +240,9 @@ combined.
the default `0x10000`. A value of 0 or empty sets the map address to be the default `0x10000`. A value of 0 or empty sets the map address to be
dynamic (the original AFL way, which is slower). dynamic (the original AFL way, which is slower).
- `AFL_LLVM_MAP_DYNAMIC` sets the shared memory address to be dynamic. - `AFL_LLVM_MAP_DYNAMIC` sets the shared memory address to be dynamic.
- `AFL_LLVM_LTO_SKIPINIT` skips adding initialization code. Some global vars
(e.g. the highest location ID) are not injected. Needed to instrument with
[WAFL](https://github.com/fgsect/WAFL.git).
For more information, see For more information, see
[instrumentation/README.lto.md](../instrumentation/README.lto.md). [instrumentation/README.lto.md](../instrumentation/README.lto.md).
@ -404,7 +406,8 @@ checks or alter some of the more exotic semantics of the tool:
- If afl-fuzz encounters an incorrect fuzzing setup during a fuzzing session - If afl-fuzz encounters an incorrect fuzzing setup during a fuzzing session
(not at startup), it will terminate. If you do not want this, then you can (not at startup), it will terminate. If you do not want this, then you can
set `AFL_IGNORE_PROBLEMS`. set `AFL_IGNORE_PROBLEMS`. If you additionally want to also ignore coverage
from late loaded libraries, you can set `AFL_IGNORE_PROBLEMS_COVERAGE`.
- When running in the `-M` or `-S` mode, setting `AFL_IMPORT_FIRST` causes the - When running in the `-M` or `-S` mode, setting `AFL_IMPORT_FIRST` causes the
fuzzer to import test cases from other instances before doing anything else. fuzzer to import test cases from other instances before doing anything else.
@ -581,7 +584,7 @@ checks or alter some of the more exotic semantics of the tool:
constructors in your target, you can set `AFL_EARLY_FORKSERVER`. constructors in your target, you can set `AFL_EARLY_FORKSERVER`.
Note that this is not a compile time option but a runtime option :-) Note that this is not a compile time option but a runtime option :-)
- Set `AFL_PIZZA_MODE` to 1 to enable the April 1st stats menu, set to 0 - Set `AFL_PIZZA_MODE` to 1 to enable the April 1st stats menu, set to -1
to disable although it is 1st of April. to disable although it is 1st of April.
- If you need a specific interval to update fuzzer_stats file, you can - If you need a specific interval to update fuzzer_stats file, you can
@ -616,6 +619,14 @@ The QEMU wrapper used to instrument binary-only code supports several settings:
- Setting `AFL_INST_LIBS` causes the translator to also instrument the code - Setting `AFL_INST_LIBS` causes the translator to also instrument the code
inside any dynamically linked libraries (notably including glibc). inside any dynamically linked libraries (notably including glibc).
- You can use `AFL_QEMU_INST_RANGES=0xaaaa-0xbbbb,0xcccc-0xdddd` to just
instrument specific memory locations, e.g. a specific library.
Excluding ranges takes priority over any included ranges or `AFL_INST_LIBS`.
- You can use `AFL_QEMU_EXCLUDE_RANGES=0xaaaa-0xbbbb,0xcccc-0xdddd` to **NOT**
instrument specific memory locations, e.g. a specific library.
Excluding ranges takes priority over any included ranges or `AFL_INST_LIBS`.
- It is possible to set `AFL_INST_RATIO` to skip the instrumentation on some - It is possible to set `AFL_INST_RATIO` to skip the instrumentation on some
of the basic blocks, which can be useful when dealing with very complex of the basic blocks, which can be useful when dealing with very complex
binaries. binaries.
@ -677,6 +688,8 @@ support.
* `AFL_FRIDA_INST_JIT` - Enable the instrumentation of Just-In-Time compiled * `AFL_FRIDA_INST_JIT` - Enable the instrumentation of Just-In-Time compiled
code. Code is considered to be JIT if the executable segment is not backed by code. Code is considered to be JIT if the executable segment is not backed by
a file. a file.
* `AFL_FRIDA_INST_NO_DYNAMIC_LOAD` - Don't instrument the code loaded late at
runtime. Strictly limits instrumentation to what has been included.
* `AFL_FRIDA_INST_NO_OPTIMIZE` - Don't use optimized inline assembly coverage * `AFL_FRIDA_INST_NO_OPTIMIZE` - Don't use optimized inline assembly coverage
instrumentation (the default where available). Required to use instrumentation (the default where available). Required to use
`AFL_FRIDA_INST_TRACE`. `AFL_FRIDA_INST_TRACE`.

View File

@ -1,5 +1,10 @@
# Tools that help fuzzing with AFL++ # Tools that help fuzzing with AFL++
## AFL++ and other development languages
* [afl-rs](https://github.com/rust-fuzz/afl.rs) - AFL++ for RUST
* [WASM](https://github.com/fgsect/WAFL) - AFL++ for WASM
## Speeding up fuzzing ## Speeding up fuzzing
* [libfiowrapper](https://github.com/marekzmyslowski/libfiowrapper) - if the * [libfiowrapper](https://github.com/marekzmyslowski/libfiowrapper) - if the

View File

@ -20,6 +20,10 @@ training, then we can highly recommend the following:
* [https://github.com/antonio-morales/Fuzzing101](https://github.com/antonio-morales/Fuzzing101) * [https://github.com/antonio-morales/Fuzzing101](https://github.com/antonio-morales/Fuzzing101)
Here is good workflow description for frida_mode:
* [https://blog.quarkslab.com/android-greybox-fuzzing-with-afl-frida-mode.html](https://blog.quarkslab.com/android-greybox-fuzzing-with-afl-frida-mode.html)
If you are interested in fuzzing structured data (where you define what the If you are interested in fuzzing structured data (where you define what the
structure is), these links have you covered (some are outdated though): structure is), these links have you covered (some are outdated though):

View File

@ -54,4 +54,5 @@
"__sanitizer_cov_trace_pc_guard"; "__sanitizer_cov_trace_pc_guard";
"__sanitizer_cov_trace_pc_guard_init"; "__sanitizer_cov_trace_pc_guard_init";
"__sanitizer_cov_trace_switch"; "__sanitizer_cov_trace_switch";
"LLVMFuzzerTestOneInput";
}; };

View File

@ -7,6 +7,8 @@ variables.
In FRIDA mode, binary programs are instrumented, similarly to QEMU mode. In FRIDA mode, binary programs are instrumented, similarly to QEMU mode.
A tutorial can be found at [https://blog.quarkslab.com/android-greybox-fuzzing-with-afl-frida-mode.html](https://blog.quarkslab.com/android-greybox-fuzzing-with-afl-frida-mode.html)
## Current progress ## Current progress
As FRIDA mode is new, it is missing a lot of features. The design is such that As FRIDA mode is new, it is missing a lot of features. The design is such that
@ -178,11 +180,13 @@ Default is 256Mb.
* `AFL_FRIDA_INST_JIT` - Enable the instrumentation of Just-In-Time compiled * `AFL_FRIDA_INST_JIT` - Enable the instrumentation of Just-In-Time compiled
code. Code is considered to be JIT if the executable segment is not backed by code. Code is considered to be JIT if the executable segment is not backed by
a file. a file.
* `AFL_FRIDA_INST_NO_DYNAMIC_LOAD` - Don't instrument the code loaded late at
runtime. Strictly limits instrumentation to what has been included.
* `AFL_FRIDA_INST_NO_OPTIMIZE` - Don't use optimized inline assembly coverage * `AFL_FRIDA_INST_NO_OPTIMIZE` - Don't use optimized inline assembly coverage
instrumentation (the default where available). Required to use instrumentation (the default where available). Required to use
`AFL_FRIDA_INST_TRACE`.
* `AFL_FRIDA_INST_REGS_FILE` - File to write raw register contents at the start * `AFL_FRIDA_INST_REGS_FILE` - File to write raw register contents at the start
of each block. of each block.
`AFL_FRIDA_INST_TRACE`.
* `AFL_FRIDA_INST_NO_CACHE` - Don't use a look-up table to cache real to * `AFL_FRIDA_INST_NO_CACHE` - Don't use a look-up table to cache real to
instrumented address block translations. instrumented address block translations.
* `AFL_FRIDA_INST_NO_PREFETCH` - Disable prefetching. By default, the child will * `AFL_FRIDA_INST_NO_PREFETCH` - Disable prefetching. By default, the child will

View File

@ -844,6 +844,12 @@ class Afl {
static setInstrumentLibraries() { static setInstrumentLibraries() {
Afl.jsApiSetInstrumentLibraries(); Afl.jsApiSetInstrumentLibraries();
} }
/**
* See `AFL_FRIDA_INST_NO_DYNAMIC_LOAD`
*/
static setInstrumentNoDynamicLoad() {
Afl.jsApiSetInstrumentNoDynamicLoad();
}
/** /**
* See `AFL_FRIDA_INST_NO_OPTIMIZE` * See `AFL_FRIDA_INST_NO_OPTIMIZE`
*/ */

View File

@ -19,6 +19,7 @@
js_api_set_instrument_jit; js_api_set_instrument_jit;
js_api_set_instrument_libraries; js_api_set_instrument_libraries;
js_api_set_instrument_instructions; js_api_set_instrument_instructions;
js_api_set_instrument_no_dynamic_load;
js_api_set_instrument_no_optimize; js_api_set_instrument_no_optimize;
js_api_set_instrument_regs_file; js_api_set_instrument_regs_file;
js_api_set_instrument_seed; js_api_set_instrument_seed;

View File

@ -14,8 +14,6 @@ void entry_init(void);
void entry_start(void); void entry_start(void);
void entry_prologue(GumStalkerIterator *iterator, GumStalkerOutput *output);
void entry_on_fork(void); void entry_on_fork(void);
#endif #endif

View File

@ -6,6 +6,7 @@
extern gboolean ranges_debug_maps; extern gboolean ranges_debug_maps;
extern gboolean ranges_inst_libs; extern gboolean ranges_inst_libs;
extern gboolean ranges_inst_jit; extern gboolean ranges_inst_jit;
extern gboolean ranges_inst_dynamic_load;
void ranges_config(void); void ranges_config(void);
void ranges_init(void); void ranges_init(void);

View File

@ -78,6 +78,7 @@ void entry_init(void) {
void entry_start(void) { void entry_start(void) {
FVERBOSE("AFL_ENTRYPOINT reached");
if (persistent_start == 0) { if (persistent_start == 0) {
ranges_exclude(); ranges_exclude();
@ -85,32 +86,7 @@ void entry_start(void) {
} }
if (entry_point == 0) { entry_launch(); }
}
static void entry_callout(GumCpuContext *cpu_context, gpointer user_data) {
UNUSED_PARAMETER(cpu_context);
UNUSED_PARAMETER(user_data);
entry_compiled = TRUE;
entry_launch(); entry_launch();
} }
void entry_prologue(GumStalkerIterator *iterator, GumStalkerOutput *output) {
UNUSED_PARAMETER(output);
FVERBOSE("AFL_ENTRYPOINT reached");
if (persistent_start == 0) {
ranges_exclude();
stalker_trust();
}
gum_stalker_iterator_put_callout(iterator, entry_callout, NULL, NULL);
}

View File

@ -169,7 +169,6 @@ static void instrument_basic_block(GumStalkerIterator *iterator,
if (unlikely(begin)) { instrument_debug_start(instr->address, output); } if (unlikely(begin)) { instrument_debug_start(instr->address, output); }
if (instr->address == entry_point) { entry_prologue(iterator, output); }
if (instr->address == persistent_start) { persistent_prologue(output); } if (instr->address == persistent_start) { persistent_prologue(output); }
if (instr->address == persistent_ret) { persistent_epilogue(output); } if (instr->address == persistent_ret) { persistent_epilogue(output); }

View File

@ -76,6 +76,45 @@ typedef struct {
} afl_log_code_asm_t; } afl_log_code_asm_t;
typedef struct {
uint32_t b_imm8; /* br #XX (end) */
uint32_t restoration_prolog; /* ldp x16, x17, [sp], #0x90 */
uint32_t stp_x0_x1; /* stp x0, x1, [sp, #-0xa0] */
uint32_t ldr_x0_p_prev_loc_1; /* ldr x0, #0xXXXX */
uint32_t ldr_x1_ptr_x0; /* ldr x1, [x0] */
uint32_t ldr_x0_p_area_offset; /* ldr x0, #0xXXXX */
uint32_t eor_x0_x1_x0; /* eor x0, x1, x0 */
uint32_t ldr_x1_p_area_ptr; /* ldr x1, #0xXXXX */
uint32_t add_x0_x1_x0; /* add x0, x1, x0 */
uint32_t ldrb_w1_x0; /* ldrb w1, [x0] */
uint32_t add_w1_w1_1; /* add w1, w1, #1 */
uint32_t add_w1_w1_w1_lsr_8; /* add x1, x1, x1, lsr #8 */
uint32_t strb_w1_ptr_x0; /* strb w1, [x0] */
uint32_t ldr_x0_p_prev_loc_2; /* ldr x0, #0xXXXX */
uint32_t ldr_x1_p_area_offset_ror; /* ldr x1, #0xXXXX */
uint32_t str_x1_ptr_x0; /* str x1, [x0] */
uint32_t ldp_x0_x1; /* ldp x0, x1, [sp, #-0xa0] */
uint32_t b_end; /* skip the data */
uint64_t area_ptr;
uint64_t prev_loc_ptr;
uint64_t area_offset;
uint64_t area_offset_ror;
uint8_t end[0];
} afl_log_code_asm_long_t;
#pragma pack(pop) #pragma pack(pop)
typedef union { typedef union {
@ -85,6 +124,13 @@ typedef union {
} afl_log_code; } afl_log_code;
typedef union {
afl_log_code_asm_long_t code;
uint8_t bytes[0];
} afl_log_code_long;
static const afl_log_code_asm_t template = static const afl_log_code_asm_t template =
{ {
@ -119,6 +165,46 @@ static const afl_log_code_asm_t template =
; ;
static const afl_log_code_asm_long_t template_long =
{.b_imm8 = 0x1400001a,
.restoration_prolog = 0xa8c947f0, /* ldp x16, x17, [sp], #0x90 */
.stp_x0_x1 = 0xa93607e0, /* stp x0, x1, [sp, #-0xa0] */
.ldr_x0_p_prev_loc_1 = 0x58000220, /* ldr x0, #0xXXXX */
.ldr_x1_ptr_x0 = 0xf9400001, /* ldr x1, [x0] */
.ldr_x0_p_area_offset = 0x58000220, /* ldr x0, #0xXXXX */
.eor_x0_x1_x0 = 0xca000020, /* eor x0, x1, x0 */
.ldr_x1_p_area_ptr = 0x58000161, /* ldr x1, #0xXXXX */
.add_x0_x1_x0 = 0x8b000020, /* add x0, x1, x0 */
.ldrb_w1_x0 = 0x39400001, /* ldrb w1, [x0] */
.add_w1_w1_1 = 0x11000421, /* add w1, w1, #1 */
.add_w1_w1_w1_lsr_8 = 0x8b412021, /* add x1, x1, x1, lsr #8 */
.strb_w1_ptr_x0 = 0x39000001, /* strb w1, [x0] */
.ldr_x0_p_prev_loc_2 = 0x580000e0, /* ldr x0, #0xXXXX */
.ldr_x1_p_area_offset_ror = 0x58000141, /* ldr x1, #0xXXXX */
.str_x1_ptr_x0 = 0xf9000001, /* str x1, [x0] */
.ldp_x0_x1 = 0xa97607e0, /* ldp x0, x1, [sp, #-0xa0] */
.b_end = 0x14000009, /* skip the data */
.area_ptr = 0x0,
.prev_loc_ptr = 0x0,
.area_offset = 0x0,
.area_offset_ror = 0x0,
.end = {}
}
;
gboolean instrument_is_coverage_optimize_supported(void) { gboolean instrument_is_coverage_optimize_supported(void) {
return true; return true;
@ -266,16 +352,22 @@ static gboolean instrument_coverage_in_range(gssize offset) {
} }
static void instrument_patch_ardp(guint32 *patch, GumAddress insn, static bool instrument_patch_ardp(guint32 *patch, GumAddress insn,
GumAddress target) { GumAddress target) {
if (!PAGE_ALIGNED(target)) { FATAL("Target not page aligned"); } if (!PAGE_ALIGNED(target)) {
FWARNF("Target not page aligned");
return false;
}
gssize distance = target - (GUM_ADDRESS(insn) & PAGE_MASK); gssize distance = target - (GUM_ADDRESS(insn) & PAGE_MASK);
if (!instrument_coverage_in_range(distance)) { if (!instrument_coverage_in_range(distance)) {
FATAL("Patch out of range 0x%016lX->0x%016lX = 0x%016lX", insn, target, FVERBOSE("Patch out of range 0x%016lX->0x%016lX = 0x%016lX", insn, target,
distance); distance);
return false;
} }
@ -283,6 +375,95 @@ static void instrument_patch_ardp(guint32 *patch, GumAddress insn,
guint32 imm_high = ((distance >> 14) & 0x7FFFF) << 5; guint32 imm_high = ((distance >> 14) & 0x7FFFF) << 5;
*patch |= imm_low; *patch |= imm_low;
*patch |= imm_high; *patch |= imm_high;
return true;
}
bool instrument_write_inline(GumArm64Writer *cw, GumAddress code_addr,
guint64 area_offset, gsize area_offset_ror) {
afl_log_code code = {0};
code.code = template;
/*
* Given our map is allocated on a 64KB boundary and our map is a multiple of
* 64KB in size, then it should also end on a 64 KB boundary. It is followed
* by our previous_pc, so this too should be 64KB aligned.
*/
g_assert(PAGE_ALIGNED(instrument_previous_pc_addr));
g_assert(PAGE_ALIGNED(__afl_area_ptr));
if (!instrument_patch_ardp(
&code.code.adrp_x0_prev_loc1,
code_addr + offsetof(afl_log_code, code.adrp_x0_prev_loc1),
GUM_ADDRESS(instrument_previous_pc_addr))) {
return false;
}
code.code.mov_x0_curr_loc |= area_offset << 5;
if (!instrument_patch_ardp(
&code.code.adrp_x1_area_ptr,
code_addr + offsetof(afl_log_code, code.adrp_x1_area_ptr),
GUM_ADDRESS(__afl_area_ptr))) {
return false;
}
if (!instrument_patch_ardp(
&code.code.adrp_x0_prev_loc2,
code_addr + offsetof(afl_log_code, code.adrp_x0_prev_loc2),
GUM_ADDRESS(instrument_previous_pc_addr))) {
return false;
}
code.code.mov_x1_curr_loc_shr_1 |= (area_offset_ror << 5);
if (instrument_suppress) {
gum_arm64_writer_put_bytes(cw, code.bytes, sizeof(afl_log_code));
} else {
size_t offset = offsetof(afl_log_code, code.stp_x0_x1);
gum_arm64_writer_put_bytes(cw, &code.bytes[offset],
sizeof(afl_log_code) - offset);
}
return true;
}
bool instrument_write_inline_long(GumArm64Writer *cw, GumAddress code_addr,
guint64 area_offset, gsize area_offset_ror) {
afl_log_code_long code = {0};
code.code = template_long;
code.code.area_ptr = GUM_ADDRESS(__afl_area_ptr);
code.code.prev_loc_ptr = GUM_ADDRESS(instrument_previous_pc_addr);
code.code.area_offset = area_offset;
code.code.area_offset_ror = GUM_ADDRESS(area_offset_ror);
if (instrument_suppress) {
gum_arm64_writer_put_bytes(cw, code.bytes, sizeof(afl_log_code_long));
} else {
size_t offset = offsetof(afl_log_code_long, code.stp_x0_x1);
gum_arm64_writer_put_bytes(cw, &code.bytes[offset],
sizeof(afl_log_code_long) - offset);
}
return true;
} }
@ -312,6 +493,8 @@ void instrument_coverage_optimize(const cs_insn *instr,
} }
// gum_arm64_writer_put_brk_imm(cw, 0x0); // gum_arm64_writer_put_brk_imm(cw, 0x0);
// uint32_t jmp_dot = 0x14000000;
// gum_arm64_writer_put_bytes(cw, (guint8 *)&jmp_dot, sizeof(jmp_dot));
if (instrument_suppress) { instrument_coverage_suppress_init(); } if (instrument_suppress) { instrument_coverage_suppress_init(); }
@ -343,47 +526,19 @@ void instrument_coverage_optimize(const cs_insn *instr,
} }
code.code = template;
/*
* Given our map is allocated on a 64KB boundary and our map is a multiple of
* 64KB in size, then it should also end on a 64 KB boundary. It is followed
* by our previous_pc, so this too should be 64KB aligned.
*/
g_assert(PAGE_ALIGNED(instrument_previous_pc_addr));
g_assert(PAGE_ALIGNED(__afl_area_ptr));
instrument_patch_ardp(
&code.code.adrp_x0_prev_loc1,
code_addr + offsetof(afl_log_code, code.adrp_x0_prev_loc1),
GUM_ADDRESS(instrument_previous_pc_addr));
code.code.mov_x0_curr_loc |= area_offset << 5;
instrument_patch_ardp(
&code.code.adrp_x1_area_ptr,
code_addr + offsetof(afl_log_code, code.adrp_x1_area_ptr),
GUM_ADDRESS(__afl_area_ptr));
map_size_pow2 = util_log2(__afl_map_size); map_size_pow2 = util_log2(__afl_map_size);
area_offset_ror = util_rotate(area_offset, 1, map_size_pow2); area_offset_ror = util_rotate(area_offset, 1, map_size_pow2);
instrument_patch_ardp( code.code = template;
&code.code.adrp_x0_prev_loc2,
code_addr + offsetof(afl_log_code, code.adrp_x0_prev_loc2),
GUM_ADDRESS(instrument_previous_pc_addr));
code.code.mov_x1_curr_loc_shr_1 |= (area_offset_ror << 5); if (!instrument_write_inline(cw, code_addr, area_offset, area_offset_ror)) {
if (instrument_suppress) { if (!instrument_write_inline_long(cw, code_addr, area_offset,
area_offset_ror)) {
gum_arm64_writer_put_bytes(cw, code.bytes, sizeof(afl_log_code)); FATAL("Failed to write inline instrumentation");
} else { }
size_t offset = offsetof(afl_log_code, code.stp_x0_x1);
gum_arm64_writer_put_bytes(cw, &code.bytes[offset],
sizeof(afl_log_code) - offset);
} }

View File

@ -150,6 +150,12 @@ class Afl {
static setInstrumentLibraries() { static setInstrumentLibraries() {
Afl.jsApiSetInstrumentLibraries(); Afl.jsApiSetInstrumentLibraries();
} }
/**
* See `AFL_FRIDA_INST_NO_DYNAMIC_LOAD`
*/
static setInstrumentNoDynamicLoad() {
Afl.jsApiSetInstrumentNoDynamicLoad();
}
/** /**
* See `AFL_FRIDA_INST_NO_OPTIMIZE` * See `AFL_FRIDA_INST_NO_OPTIMIZE`
*/ */
@ -342,6 +348,7 @@ Afl.jsApiSetInstrumentDebugFile = Afl.jsApiGetFunction("js_api_set_instrument_de
Afl.jsApiSetInstrumentInstructions = Afl.jsApiGetFunction("js_api_set_instrument_instructions", "void", []); Afl.jsApiSetInstrumentInstructions = Afl.jsApiGetFunction("js_api_set_instrument_instructions", "void", []);
Afl.jsApiSetInstrumentJit = Afl.jsApiGetFunction("js_api_set_instrument_jit", "void", []); Afl.jsApiSetInstrumentJit = Afl.jsApiGetFunction("js_api_set_instrument_jit", "void", []);
Afl.jsApiSetInstrumentLibraries = Afl.jsApiGetFunction("js_api_set_instrument_libraries", "void", []); Afl.jsApiSetInstrumentLibraries = Afl.jsApiGetFunction("js_api_set_instrument_libraries", "void", []);
Afl.jsApiSetInstrumentNoDynamicLoad = Afl.jsApiGetFunction("js_api_set_instrument_no_dynamic_load", "void", []);
Afl.jsApiSetInstrumentNoOptimize = Afl.jsApiGetFunction("js_api_set_instrument_no_optimize", "void", []); Afl.jsApiSetInstrumentNoOptimize = Afl.jsApiGetFunction("js_api_set_instrument_no_optimize", "void", []);
Afl.jsApiSetInstrumentRegsFile = Afl.jsApiGetFunction("js_api_set_instrument_regs_file", "void", ["pointer"]); Afl.jsApiSetInstrumentRegsFile = Afl.jsApiGetFunction("js_api_set_instrument_regs_file", "void", ["pointer"]);
Afl.jsApiSetInstrumentSeed = Afl.jsApiGetFunction("js_api_set_instrument_seed", "void", ["uint64"]); Afl.jsApiSetInstrumentSeed = Afl.jsApiGetFunction("js_api_set_instrument_seed", "void", ["uint64"]);

View File

@ -156,6 +156,13 @@ __attribute__((visibility("default"))) void js_api_set_instrument_instructions(
} }
__attribute__((visibility("default"))) void
js_api_set_instrument_no_dynamic_load(void) {
ranges_inst_dynamic_load = FALSE;
}
__attribute__((visibility("default"))) void js_api_set_instrument_no_optimize( __attribute__((visibility("default"))) void js_api_set_instrument_no_optimize(
void) { void) {

View File

@ -17,8 +17,8 @@ static gboolean lib_get_main_module(const GumModuleDetails *details,
GumDarwinModule **ret = (GumDarwinModule **)user_data; GumDarwinModule **ret = (GumDarwinModule **)user_data;
GumDarwinModule *module = gum_darwin_module_new_from_memory( GumDarwinModule *module = gum_darwin_module_new_from_memory(
details->path, mach_task_self(), details->range->base_address, details->path, mach_task_self(), details->range->base_address,
GUM_DARWIN_MODULE_FLAGS_NONE, NULL); GUM_DARWIN_MODULE_FLAGS_NONE, NULL);
FVERBOSE("Found main module: %s", module->name); FVERBOSE("Found main module: %s", module->name);

View File

@ -197,7 +197,7 @@ static void afl_print_env(void) {
} }
__attribute__((visibility("default"))) void afl_frida_start(void) { void afl_frida_config(void) {
FOKF(cRED "**********************"); FOKF(cRED "**********************");
FOKF(cRED "* " cYEL "******************" cRED " *"); FOKF(cRED "* " cYEL "******************" cRED " *");
@ -225,9 +225,7 @@ __attribute__((visibility("default"))) void afl_frida_start(void) {
js_start(); js_start();
/* Initialize */
output_init(); output_init();
embedded_init(); embedded_init();
entry_init(); entry_init();
instrument_init(); instrument_init();
@ -240,12 +238,35 @@ __attribute__((visibility("default"))) void afl_frida_start(void) {
ranges_init(); ranges_init();
stats_init(); stats_init();
/* Start */ }
void afl_frida_run(void) {
stalker_start(); stalker_start();
entry_start(); entry_start();
} }
__attribute__((visibility("default"))) void afl_frida_start(void) {
afl_frida_config();
afl_frida_run();
}
typedef void *(*entry_func_t)(size_t a1, size_t a2, size_t a3, size_t a4,
size_t a5, size_t a6);
static void *on_entry(size_t a1, size_t a2, size_t a3, size_t a4, size_t a5,
size_t a6) {
intercept_unhook(GSIZE_TO_POINTER(entry_point));
afl_frida_run();
entry_func_t entry = (entry_func_t)entry_point;
return entry(a1, a2, a3, a4, a5, a6);
}
static int on_main(int argc, char **argv, char **envp) { static int on_main(int argc, char **argv, char **envp) {
int ret; int ret;
@ -254,7 +275,17 @@ static int on_main(int argc, char **argv, char **envp) {
intercept_unhook_self(); intercept_unhook_self();
afl_frida_start(); afl_frida_config();
if (entry_point == 0) {
afl_frida_run();
} else {
intercept_hook(GSIZE_TO_POINTER(entry_point), on_entry, NULL);
}
if (js_main_hook != NULL) { if (js_main_hook != NULL) {

View File

@ -18,6 +18,7 @@ typedef struct {
gboolean ranges_debug_maps = FALSE; gboolean ranges_debug_maps = FALSE;
gboolean ranges_inst_libs = FALSE; gboolean ranges_inst_libs = FALSE;
gboolean ranges_inst_jit = FALSE; gboolean ranges_inst_jit = FALSE;
gboolean ranges_inst_dynamic_load = TRUE;
static GArray *module_ranges = NULL; static GArray *module_ranges = NULL;
static GArray *libs_ranges = NULL; static GArray *libs_ranges = NULL;
@ -25,6 +26,7 @@ static GArray *jit_ranges = NULL;
static GArray *include_ranges = NULL; static GArray *include_ranges = NULL;
static GArray *exclude_ranges = NULL; static GArray *exclude_ranges = NULL;
static GArray *ranges = NULL; static GArray *ranges = NULL;
static GArray *whole_memory_ranges = NULL;
static void convert_address_token(gchar *token, GumMemoryRange *range) { static void convert_address_token(gchar *token, GumMemoryRange *range) {
@ -387,6 +389,21 @@ static GArray *collect_jit_ranges(void) {
} }
static GArray *collect_whole_mem_ranges(void) {
GArray *result;
GumMemoryRange range;
result = g_array_new(false, false, sizeof(GumMemoryRange));
range.base_address = 0;
range.size = G_MAXULONG;
g_array_append_val(result, range);
return result;
}
static gboolean intersect_range(GumMemoryRange *rr, GumMemoryRange *ra, static gboolean intersect_range(GumMemoryRange *rr, GumMemoryRange *ra,
GumMemoryRange *rb) { GumMemoryRange *rb) {
@ -574,11 +591,17 @@ void ranges_config(void) {
if (getenv("AFL_FRIDA_DEBUG_MAPS") != NULL) { ranges_debug_maps = TRUE; } if (getenv("AFL_FRIDA_DEBUG_MAPS") != NULL) { ranges_debug_maps = TRUE; }
if (getenv("AFL_INST_LIBS") != NULL) { ranges_inst_libs = TRUE; } if (getenv("AFL_INST_LIBS") != NULL) { ranges_inst_libs = TRUE; }
if (getenv("AFL_FRIDA_INST_JIT") != NULL) { ranges_inst_jit = TRUE; } if (getenv("AFL_FRIDA_INST_JIT") != NULL) { ranges_inst_jit = TRUE; }
if (getenv("AFL_FRIDA_INST_NO_DYNAMIC_LOAD") != NULL) {
ranges_inst_dynamic_load = FALSE;
}
if (ranges_debug_maps) { ranges_print_debug_maps(); } if (ranges_debug_maps) { ranges_print_debug_maps(); }
include_ranges = collect_ranges("AFL_FRIDA_INST_RANGES"); include_ranges = collect_ranges("AFL_FRIDA_INST_RANGES");
exclude_ranges = collect_ranges("AFL_FRIDA_EXCLUDE_RANGES"); exclude_ranges = collect_ranges("AFL_FRIDA_EXCLUDE_RANGES");
whole_memory_ranges = collect_whole_mem_ranges();
} }
@ -628,10 +651,20 @@ void ranges_init(void) {
print_ranges("step4", step4); print_ranges("step4", step4);
/* /*
* After step4, we have the total ranges to be instrumented, we now subtract * After step 4 we have the total ranges to be instrumented, we now subtract
* that from the original ranges of the modules to configure stalker. * that either from the original ranges of the modules or from the whole
* memory if AFL_INST_NO_DYNAMIC_LOAD to configure the stalker.
*/ */
step5 = subtract_ranges(module_ranges, step4); if (ranges_inst_dynamic_load) {
step5 = subtract_ranges(module_ranges, step4);
} else {
step5 = subtract_ranges(whole_memory_ranges, step4);
}
print_ranges("step5", step5); print_ranges("step5", step5);
ranges = merge_ranges(step5); ranges = merge_ranges(step5);

View File

@ -67,3 +67,8 @@ debug:
--ex 'set environment LD_PRELOAD=$(ROOT)afl-frida-trace.so' \ --ex 'set environment LD_PRELOAD=$(ROOT)afl-frida-trace.so' \
--ex 'set disassembly-flavor intel' \ --ex 'set disassembly-flavor intel' \
--args $(TESTINSTBIN) $(TESTINSTR_DATA_FILE) --args $(TESTINSTBIN) $(TESTINSTR_DATA_FILE)
lldb:
lldb \
-O 'settings set target.env-vars DYLD_INSERT_LIBRARIES=$(ROOT)afl-frida-trace.so' \
-- $(TESTINSTBIN) $(TESTINSTR_DATA_FILE)

View File

@ -178,6 +178,13 @@ class Afl {
Afl.jsApiSetInstrumentLibraries(); Afl.jsApiSetInstrumentLibraries();
} }
/**
* See `AFL_FRIDA_INST_NO_DYNAMIC_LOAD`
*/
public static setInstrumentNoDynamicLoad(): void {
Afl.jsApiSetInstrumentNoDynamicLoad();
}
/** /**
* See `AFL_FRIDA_INST_NO_OPTIMIZE` * See `AFL_FRIDA_INST_NO_OPTIMIZE`
*/ */
@ -443,6 +450,11 @@ class Afl {
"void", "void",
[]); []);
private static readonly jsApiSetInstrumentNoDynamicLoad = Afl.jsApiGetFunction(
"js_api_set_instrument_no_dynamic_load",
"void",
[]);
private static readonly jsApiSetInstrumentNoOptimize = Afl.jsApiGetFunction( private static readonly jsApiSetInstrumentNoOptimize = Afl.jsApiGetFunction(
"js_api_set_instrument_no_optimize", "js_api_set_instrument_no_optimize",
"void", "void",

View File

@ -184,6 +184,7 @@ struct queue_entry {
handicap, /* Number of queue cycles behind */ handicap, /* Number of queue cycles behind */
depth, /* Path depth */ depth, /* Path depth */
exec_cksum, /* Checksum of the execution trace */ exec_cksum, /* Checksum of the execution trace */
custom, /* Marker for custom mutators */
stats_mutated; /* stats: # of mutations performed */ stats_mutated; /* stats: # of mutations performed */
u8 *trace_mini; /* Trace bytes, if kept */ u8 *trace_mini; /* Trace bytes, if kept */
@ -398,8 +399,9 @@ typedef struct afl_env_vars {
afl_bench_until_crash, afl_debug_child, afl_autoresume, afl_cal_fast, afl_bench_until_crash, afl_debug_child, afl_autoresume, afl_cal_fast,
afl_cycle_schedules, afl_expand_havoc, afl_statsd, afl_cmplog_only_new, afl_cycle_schedules, afl_expand_havoc, afl_statsd, afl_cmplog_only_new,
afl_exit_on_seed_issues, afl_try_affinity, afl_ignore_problems, afl_exit_on_seed_issues, afl_try_affinity, afl_ignore_problems,
afl_keep_timeouts, afl_pizza_mode, afl_no_crash_readme, afl_keep_timeouts, afl_no_crash_readme, afl_ignore_timeouts,
afl_ignore_timeouts, afl_no_startup_calibration, afl_no_warn_instability; afl_no_startup_calibration, afl_no_warn_instability,
afl_post_process_keep_original;
u8 *afl_tmpdir, *afl_custom_mutator_library, *afl_python_module, *afl_path, u8 *afl_tmpdir, *afl_custom_mutator_library, *afl_python_module, *afl_path,
*afl_hang_tmout, *afl_forksrv_init_tmout, *afl_preload, *afl_hang_tmout, *afl_forksrv_init_tmout, *afl_preload,
@ -408,6 +410,8 @@ typedef struct afl_env_vars {
*afl_testcache_entries, *afl_child_kill_signal, *afl_fsrv_kill_signal, *afl_testcache_entries, *afl_child_kill_signal, *afl_fsrv_kill_signal,
*afl_target_env, *afl_persistent_record, *afl_exit_on_time; *afl_target_env, *afl_persistent_record, *afl_exit_on_time;
s32 afl_pizza_mode;
} afl_env_vars_t; } afl_env_vars_t;
struct afl_pass_stat { struct afl_pass_stat {
@ -882,14 +886,19 @@ struct custom_mutator {
* A post-processing function to use right before AFL writes the test case to * A post-processing function to use right before AFL writes the test case to
* disk in order to execute the target. * disk in order to execute the target.
* *
* (Optional) If this functionality is not needed, simply don't define this * NOTE: Do not do any random changes to the data in this function!
*
* PERFORMANCE: If you can modify the data in-place you will have a better
* performance. Modify *data and set `*out_buf = data`.
*
* (Optional) If this functionality is not needed, simply do not define this
* function. * function.
* *
* @param[in] data pointer returned in afl_custom_init by this custom mutator * @param[in] data pointer returned in afl_custom_init by this custom mutator
* @param[in] buf Buffer containing the test case to be executed * @param[in] buf Buffer containing the test case to be executed
* @param[in] buf_size Size of the test case * @param[in] buf_size Size of the test case
* @param[out] out_buf Pointer to the buffer storing the test case after * @param[out] out_buf Pointer to the buffer storing the test case after
* processing. External library should allocate memory for out_buf. * processing. The external library should allocate memory for out_buf.
* It can chose to alter buf in-place, if the space is large enough. * It can chose to alter buf in-place, if the space is large enough.
* @return Size of the output buffer. * @return Size of the output buffer.
*/ */
@ -1095,7 +1104,6 @@ u32 count_bits(afl_state_t *, u8 *);
u32 count_bytes(afl_state_t *, u8 *); u32 count_bytes(afl_state_t *, u8 *);
u32 count_non_255_bytes(afl_state_t *, u8 *); u32 count_non_255_bytes(afl_state_t *, u8 *);
void simplify_trace(afl_state_t *, u8 *); void simplify_trace(afl_state_t *, u8 *);
void classify_counts(afl_forkserver_t *);
#ifdef WORD_SIZE_64 #ifdef WORD_SIZE_64
void discover_word(u8 *ret, u64 *current, u64 *virgin); void discover_word(u8 *ret, u64 *current, u64 *virgin);
#else #else
@ -1109,6 +1117,9 @@ u8 *describe_op(afl_state_t *, u8, size_t);
u8 save_if_interesting(afl_state_t *, void *, u32, u8); u8 save_if_interesting(afl_state_t *, void *, u32, u8);
u8 has_new_bits(afl_state_t *, u8 *); u8 has_new_bits(afl_state_t *, u8 *);
u8 has_new_bits_unclassified(afl_state_t *, u8 *); u8 has_new_bits_unclassified(afl_state_t *, u8 *);
#ifndef AFL_SHOWMAP
void classify_counts(afl_forkserver_t *);
#endif
/* Extras */ /* Extras */
@ -1184,11 +1195,13 @@ void fix_up_sync(afl_state_t *);
void check_asan_opts(afl_state_t *); void check_asan_opts(afl_state_t *);
void check_binary(afl_state_t *, u8 *); void check_binary(afl_state_t *, u8 *);
void check_if_tty(afl_state_t *); void check_if_tty(afl_state_t *);
void setup_signal_handlers(void);
void save_cmdline(afl_state_t *, u32, char **); void save_cmdline(afl_state_t *, u32, char **);
void read_foreign_testcases(afl_state_t *, int); void read_foreign_testcases(afl_state_t *, int);
void write_crash_readme(afl_state_t *afl); void write_crash_readme(afl_state_t *afl);
u8 check_if_text_buf(u8 *buf, u32 len); u8 check_if_text_buf(u8 *buf, u32 len);
#ifndef AFL_SHOWMAP
void setup_signal_handlers(void);
#endif
/* CmpLog */ /* CmpLog */
@ -1210,7 +1223,7 @@ double rand_next_percent(afl_state_t *afl);
static inline u32 rand_below(afl_state_t *afl, u32 limit) { static inline u32 rand_below(afl_state_t *afl, u32 limit) {
if (limit <= 1) return 0; if (unlikely(limit <= 1)) return 0;
/* The boundary not being necessarily a power of 2, /* The boundary not being necessarily a power of 2,
we need to ensure the result uniformity. */ we need to ensure the result uniformity. */
@ -1243,7 +1256,7 @@ static inline u32 rand_below(afl_state_t *afl, u32 limit) {
expand havoc mode */ expand havoc mode */
static inline u32 rand_below_datalen(afl_state_t *afl, u32 limit) { static inline u32 rand_below_datalen(afl_state_t *afl, u32 limit) {
if (limit <= 1) return 0; if (unlikely(limit <= 1)) return 0;
switch (rand_below(afl, 3)) { switch (rand_below(afl, 3)) {

View File

@ -42,7 +42,7 @@
// Be careful! _WANT_ORIGINAL_AFL_ALLOC is not compatible with custom mutators // Be careful! _WANT_ORIGINAL_AFL_ALLOC is not compatible with custom mutators
#ifndef _WANT_ORIGINAL_AFL_ALLOC #ifndef _WANT_ORIGINAL_AFL_ALLOC
// afl++ stuff without memory corruption checks - for speed // AFL++ stuff without memory corruption checks - for speed
/* User-facing macro to sprintf() to a dynamically allocated buffer. */ /* User-facing macro to sprintf() to a dynamically allocated buffer. */
@ -704,12 +704,11 @@ static inline void *afl_realloc(void **buf, size_t size_needed) {
*buf = NULL; *buf = NULL;
return NULL; return NULL;
} else {
new_buf = newer_buf;
} }
new_buf = newer_buf;
memset(((u8 *)new_buf) + current_size, 0, next_size - current_size);
new_buf->complete_size = next_size; new_buf->complete_size = next_size;
*buf = (void *)(new_buf->buf); *buf = (void *)(new_buf->buf);
return *buf; return *buf;

View File

@ -34,7 +34,7 @@
#define CMP_MAP_W 65536 #define CMP_MAP_W 65536
#define CMP_MAP_H 32 #define CMP_MAP_H 32
#define CMP_MAP_RTN_H (CMP_MAP_H / 4) #define CMP_MAP_RTN_H (CMP_MAP_H / 2)
#define SHAPE_BYTES(x) (x + 1) #define SHAPE_BYTES(x) (x + 1)

View File

@ -147,5 +147,11 @@ s32 create_file(u8 *fn);
void *afl_memmem(const void *haystack, size_t haystacklen, const void *needle, void *afl_memmem(const void *haystack, size_t haystacklen, const void *needle,
size_t needlelen); size_t needlelen);
#ifdef __linux__
/* Nyx helper functions to create and remove tmp workdirs */
char *create_nyx_tmp_workdir(void);
void remove_nyx_tmp_workdir(afl_forkserver_t *fsrv, char *nyx_out_dir_path);
#endif
#endif #endif

View File

@ -26,7 +26,7 @@
/* Version string: */ /* Version string: */
// c = release, a = volatile github dev, e = experimental branch // c = release, a = volatile github dev, e = experimental branch
#define VERSION "++4.06a" #define VERSION "++4.07a"
/****************************************************** /******************************************************
* * * *
@ -87,7 +87,7 @@
will be kept and written to the crash/ directory as RECORD:... files. will be kept and written to the crash/ directory as RECORD:... files.
Note that every crash will be written, not only unique ones! */ Note that every crash will be written, not only unique ones! */
//#define AFL_PERSISTENT_RECORD // #define AFL_PERSISTENT_RECORD
/* console output colors: There are three ways to configure its behavior /* console output colors: There are three ways to configure its behavior
* 1. default: colored outputs fixed on: defined USE_COLOR && defined * 1. default: colored outputs fixed on: defined USE_COLOR && defined

View File

@ -37,6 +37,10 @@ static char *afl_environment_variables[] = {
"AFL_CRASH_EXITCODE", "AFL_CRASH_EXITCODE",
"AFL_CUSTOM_MUTATOR_LIBRARY", "AFL_CUSTOM_MUTATOR_LIBRARY",
"AFL_CUSTOM_MUTATOR_ONLY", "AFL_CUSTOM_MUTATOR_ONLY",
"AFL_CUSTOM_INFO_PROGRAM",
"AFL_CUSTOM_INFO_PROGRAM_ARGV",
"AFL_CUSTOM_INFO_PROGRAM_INPUT",
"AFL_CUSTOM_INFO_OUT",
"AFL_CXX", "AFL_CXX",
"AFL_CYCLE_SCHEDULES", "AFL_CYCLE_SCHEDULES",
"AFL_DEBUG", "AFL_DEBUG",
@ -65,6 +69,7 @@ static char *afl_environment_variables[] = {
"AFL_FRIDA_INST_INSN", "AFL_FRIDA_INST_INSN",
"AFL_FRIDA_INST_JIT", "AFL_FRIDA_INST_JIT",
"AFL_FRIDA_INST_NO_CACHE", "AFL_FRIDA_INST_NO_CACHE",
"AFL_FRIDA_INST_NO_DYNAMIC_LOAD",
"AFL_FRIDA_INST_NO_OPTIMIZE", "AFL_FRIDA_INST_NO_OPTIMIZE",
"AFL_FRIDA_INST_NO_PREFETCH", "AFL_FRIDA_INST_NO_PREFETCH",
"AFL_FRIDA_INST_NO_PREFETCH_BACKPATCH", "AFL_FRIDA_INST_NO_PREFETCH_BACKPATCH",
@ -105,6 +110,7 @@ static char *afl_environment_variables[] = {
"AFL_HARDEN", "AFL_HARDEN",
"AFL_I_DONT_CARE_ABOUT_MISSING_CRASHES", "AFL_I_DONT_CARE_ABOUT_MISSING_CRASHES",
"AFL_IGNORE_PROBLEMS", "AFL_IGNORE_PROBLEMS",
"AFL_IGNORE_PROBLEMS_COVERAGE",
"AFL_IGNORE_TIMEOUTS", "AFL_IGNORE_TIMEOUTS",
"AFL_IGNORE_UNKNOWN_ENVS", "AFL_IGNORE_UNKNOWN_ENVS",
"AFL_IMPORT_FIRST", "AFL_IMPORT_FIRST",
@ -159,8 +165,9 @@ static char *afl_environment_variables[] = {
"AFL_LLVM_SKIP_NEVERZERO", "AFL_LLVM_SKIP_NEVERZERO",
"AFL_NO_AFFINITY", "AFL_NO_AFFINITY",
"AFL_TRY_AFFINITY", "AFL_TRY_AFFINITY",
"AFL_LLVM_LTO_STARTID",
"AFL_LLVM_LTO_DONTWRITEID", "AFL_LLVM_LTO_DONTWRITEID",
"AFL_LLVM_LTO_SKIPINIT"
"AFL_LLVM_LTO_STARTID",
"AFL_NO_ARITH", "AFL_NO_ARITH",
"AFL_NO_AUTODICT", "AFL_NO_AUTODICT",
"AFL_NO_BUILTIN", "AFL_NO_BUILTIN",
@ -186,6 +193,7 @@ static char *afl_environment_variables[] = {
"AFL_PATH", "AFL_PATH",
"AFL_PERFORMANCE_FILE", "AFL_PERFORMANCE_FILE",
"AFL_PERSISTENT_RECORD", "AFL_PERSISTENT_RECORD",
"AFL_POST_PROCESS_KEEP_ORIGINAL",
"AFL_PRELOAD", "AFL_PRELOAD",
"AFL_TARGET_ENV", "AFL_TARGET_ENV",
"AFL_PYTHON_MODULE", "AFL_PYTHON_MODULE",

View File

@ -51,16 +51,28 @@ typedef enum NyxReturnValue {
} NyxReturnValue; } NyxReturnValue;
typedef enum NyxProcessRole {
StandAlone,
Parent,
Child,
} NyxProcessRole;
typedef struct { typedef struct {
void *(*nyx_new)(const char *sharedir, const char *workdir, uint32_t cpu_id, void *(*nyx_config_load)(const char *sharedir);
uint32_t input_buffer_size, void (*nyx_config_set_workdir_path)(void *config, const char *workdir);
bool input_buffer_write_protection); void (*nyx_config_set_input_buffer_size)(void *config,
void *(*nyx_new_parent)(const char *sharedir, const char *workdir, uint32_t input_buffer_size);
uint32_t cpu_id, uint32_t input_buffer_size, void (*nyx_config_set_input_buffer_write_protection)(
bool input_buffer_write_protection); void *config, bool input_buffer_write_protection);
void *(*nyx_new_child)(const char *sharedir, const char *workdir, void (*nyx_config_set_hprintf_fd)(void *config, int32_t hprintf_fd);
uint32_t cpu_id, uint32_t worker_id); void (*nyx_config_set_process_role)(void *config, enum NyxProcessRole role);
void (*nyx_config_set_reuse_snapshot_path)(void *config,
const char *reuse_snapshot_path);
void *(*nyx_new)(void *config, uint32_t worker_id);
void (*nyx_shutdown)(void *qemu_process); void (*nyx_shutdown)(void *qemu_process);
void (*nyx_option_set_reload_mode)(void *qemu_process, bool enable); void (*nyx_option_set_reload_mode)(void *qemu_process, bool enable);
void (*nyx_option_set_timeout)(void *qemu_process, uint8_t timeout_sec, void (*nyx_option_set_timeout)(void *qemu_process, uint8_t timeout_sec,
@ -73,8 +85,13 @@ typedef struct {
uint32_t (*nyx_get_aux_string)(void *nyx_process, uint8_t *buffer, uint32_t (*nyx_get_aux_string)(void *nyx_process, uint8_t *buffer,
uint32_t size); uint32_t size);
bool (*nyx_remove_work_dir)(const char *workdir);
} nyx_plugin_handler_t; } nyx_plugin_handler_t;
/* Imports helper functions to enable Nyx mode (Linux only )*/
nyx_plugin_handler_t *afl_load_libnyx_plugin(u8 *libnyx_binary);
#endif #endif
typedef struct afl_forkserver { typedef struct afl_forkserver {
@ -178,6 +195,8 @@ typedef struct afl_forkserver {
u32 nyx_id; /* nyx runner id (0 -> master) */ u32 nyx_id; /* nyx runner id (0 -> master) */
u32 nyx_bind_cpu_id; /* nyx runner cpu id */ u32 nyx_bind_cpu_id; /* nyx runner cpu id */
char *nyx_aux_string; char *nyx_aux_string;
bool nyx_use_tmp_workdir;
char *nyx_tmp_workdir_path;
#endif #endif
} afl_forkserver_t; } afl_forkserver_t;

View File

@ -280,3 +280,27 @@ Please note that the default counter implementations are not thread safe!
Support for thread safe counters in mode LLVM CLASSIC can be activated with Support for thread safe counters in mode LLVM CLASSIC can be activated with
setting `AFL_LLVM_THREADSAFE_INST=1`. setting `AFL_LLVM_THREADSAFE_INST=1`.
## 8) Source code coverage through instrumentation
Measuring source code coverage is a common task in fuzzing, but it is very
difficut to do in some situations (e.g. when using snapshot fuzzing).
When using the `AFL_LLVM_INSTRUMENT=llvm-codecov` option, afl-cc will use
native trace-pc-guard instrumentation but additionally select options that
are required to utilize the instrumentation for source code coverage.
In particular, it will switch the instrumentation to be per basic block
instead of instrumenting edges, disable all guard pruning and enable the
experimental pc-table support that allows the runtime to gather 100% of
instrumented basic blocks at start, including their locations.
Note: You must compile AFL with the `CODE_COVERAGE=1` option to enable the
respective parts in the AFL compiler runtime. Support is currently only
implemented for Nyx, but can in theory also work without Nyx.
Note: You might have to adjust `MAP_SIZE_POW2` in include/config.h to ensure
that your coverage map is large enough to hold all basic blocks of your
target program without any collisions.
More documentation on how to utilize this with Nyx will follow.

View File

@ -2,36 +2,37 @@
## TL;DR: ## TL;DR:
This version requires a current llvm 11+ compiled from the GitHub master. This version requires a LLVM 11 or newer.
1. Use afl-clang-lto/afl-clang-lto++ because it is faster and gives better 1. Use afl-clang-lto/afl-clang-lto++ because the resulting binaries run
coverage than anything else that is out there in the AFL world. slightly faster and give better coverage.
2. You can use it together with llvm_mode: laf-intel and the instrument file 2. You can use it together with COMPCOV, COMPLOG and the instrument file
listing features and can be combined with cmplog/Redqueen. listing features.
3. It only works with llvm 11+. 3. It only works with LLVM 11 or newer.
4. AUTODICTIONARY feature (see below)! 4. AUTODICTIONARY feature (see below)
5. If any problems arise, be sure to set `AR=llvm-ar RANLIB=llvm-ranlib`. Some 5. If any problems arise, be sure to set `AR=llvm-ar RANLIB=llvm-ranlib AS=llvm-as`.
targets might need `LD=afl-clang-lto` and others `LD=afl-ld-lto`. Some targets might need `LD=afl-clang-lto` and others `LD=afl-ld-lto`.
## Introduction and problem description ## Introduction and problem description
A big issue with how AFL++ works is that the basic block IDs that are set during A big issue with how vanilla AFL worked was that the basic block IDs that are
compilation are random - and hence naturally the larger the number of set during compilation are random - and hence naturally the larger the number
instrumented locations, the higher the number of edge collisions are in the map. of instrumented locations, the higher the number of edge collisions are in the
This can result in not discovering new paths and therefore degrade the map. This can result in not discovering new paths and therefore degrade the
efficiency of the fuzzing process. efficiency of the fuzzing process.
*This issue is underestimated in the fuzzing community!* With a 2^16 = 64kb *This issue is underestimated in the fuzzing community* With a 2^16 = 64kb
standard map at already 256 instrumented blocks, there is on average one standard map at already 256 instrumented blocks, there is on average one
collision. On average, a target has 10.000 to 50.000 instrumented blocks, hence collision. On average, a target has 10.000 to 50.000 instrumented blocks, hence
the real collisions are between 750-18.000! the real collisions are between 750-18.000!
To reach a solution that prevents any collisions took several approaches and Note that PCGUARD (our own modified implementation and the SANCOV PCGUARD
many dead ends until we got to this: implementation from libfuzzer) also provides collision free coverage.
It is a bit slower though and can a few targets with very early constructors.
* We instrument at link time when we have all files pre-compiled. * We instrument at link time when we have all files pre-compiled.
* To instrument at link time, we compile in LTO (link time optimization) mode. * To instrument at link time, we compile in LTO (link time optimization) mode.
@ -45,9 +46,9 @@ many dead ends until we got to this:
The result: The result:
* 10-25% speed gain compared to llvm_mode * 10-25% speed gain compared to llvm_mode
* guaranteed non-colliding edge coverage :-) * guaranteed non-colliding edge coverage
* The compile time, especially for binaries to an instrumented library, can be * The compile time, especially for binaries to an instrumented library, can be
much longer. much (and sometimes much much) longer.
Example build output from a libtiff build: Example build output from a libtiff build:
@ -59,71 +60,30 @@ AUTODICTIONARY: 11 strings found
[+] Instrumented 12071 locations with no collisions (on average 1046 collisions would be in afl-gcc/afl-clang-fast) (non-hardened mode). [+] Instrumented 12071 locations with no collisions (on average 1046 collisions would be in afl-gcc/afl-clang-fast) (non-hardened mode).
``` ```
## Getting llvm 11+ ## Getting LLVM 11+
### Installing llvm version 11 or 12 ### Installing llvm
llvm 11 or even 12 should be available in all current Linux repositories. If you The best way to install LLVM is to follow [https://apt.llvm.org/](https://apt.llvm.org/)
use an outdated Linux distribution, read the next section.
### Installing llvm from the llvm repository (version 12+)
Installing the llvm snapshot builds is easy and mostly painless:
In the following line, change `NAME` for your Debian or Ubuntu release name
(e.g., buster, focal, eon, etc.):
e.g. for LLVM 15:
``` ```
echo deb http://apt.llvm.org/NAME/ llvm-toolchain-NAME NAME >> /etc/apt/sources.list wget https://apt.llvm.org/llvm.sh
chmod +x llvm.sh
sudo ./llvm.sh 15 all
``` ```
Then add the pgp key of llvm and install the packages: LLVM 11 to 16 should be available in all current Linux repositories.
## How to build afl-clang-lto
That part is easy.
Just set `LLVM_CONFIG` to the llvm-config-VERSION and build AFL++, e.g. for
LLVM 15:
``` ```
wget -O - https://apt.llvm.org/llvm-snapshot.gpg.key | apt-key add - cd ~/AFLplusplus
apt-get update && apt-get upgrade -y export LLVM_CONFIG=llvm-config-15
apt-get install -y clang-12 clang-tools-12 libc++1-12 libc++-12-dev \
libc++abi1-12 libc++abi-12-dev libclang1-12 libclang-12-dev \
libclang-common-12-dev libclang-cpp12 libclang-cpp12-dev liblld-12 \
liblld-12-dev liblldb-12 liblldb-12-dev libllvm12 libomp-12-dev \
libomp5-12 lld-12 lldb-12 llvm-12 llvm-12-dev llvm-12-runtime llvm-12-tools
```
### Building llvm yourself (version 12+)
Building llvm from GitHub takes quite some time and is not painless:
```sh
sudo apt install binutils-dev # this is *essential*!
git clone --depth=1 https://github.com/llvm/llvm-project
cd llvm-project
mkdir build
cd build
# Add -G Ninja if ninja-build installed
# "Building with ninja significantly improves your build time, especially with
# incremental builds, and improves your memory usage."
cmake \
-DCLANG_INCLUDE_DOCS="OFF" \
-DCMAKE_BUILD_TYPE=Release \
-DLLVM_BINUTILS_INCDIR=/usr/include/ \
-DLLVM_BUILD_LLVM_DYLIB="ON" \
-DLLVM_ENABLE_BINDINGS="OFF" \
-DLLVM_ENABLE_PROJECTS='clang;compiler-rt;libcxx;libcxxabi;libunwind;lld' \
-DLLVM_ENABLE_WARNINGS="OFF" \
-DLLVM_INCLUDE_BENCHMARKS="OFF" \
-DLLVM_INCLUDE_DOCS="OFF" \
-DLLVM_INCLUDE_EXAMPLES="OFF" \
-DLLVM_INCLUDE_TESTS="OFF" \
-DLLVM_LINK_LLVM_DYLIB="ON" \
-DLLVM_TARGETS_TO_BUILD="host" \
../llvm/
# NOTE: for llvm 16 this needs to be changed to:
# -DLLVM_ENABLE_PROJECTS='clang;compiler-rt;lld' \
# -DLLVM_ENABLE_RUNTIMES='libcxx;libcxxabi' \
cmake --build . -j4
export LLVM_CONFIG="$(pwd)/bin/llvm-config"
cd /path/to/AFLplusplus/
make make
sudo make install sudo make install
``` ```
@ -136,10 +96,10 @@ Also, the instrument file listing (AFL_LLVM_ALLOWLIST/AFL_LLVM_DENYLIST ->
[README.instrument_list.md](README.instrument_list.md)) and laf-intel/compcov [README.instrument_list.md](README.instrument_list.md)) and laf-intel/compcov
(AFL_LLVM_LAF_* -> [README.laf-intel.md](README.laf-intel.md)) work. (AFL_LLVM_LAF_* -> [README.laf-intel.md](README.laf-intel.md)) work.
Example: Example (note that you might need to add the version, e.g. `llvm-ar-15`:
``` ```
CC=afl-clang-lto CXX=afl-clang-lto++ RANLIB=llvm-ranlib AR=llvm-ar ./configure CC=afl-clang-lto CXX=afl-clang-lto++ RANLIB=llvm-ranlib AR=llvm-ar AS=llvm-as ./configure
make make
``` ```
@ -317,13 +277,13 @@ AS=llvm-as ...
afl-clang-lto is still work in progress. afl-clang-lto is still work in progress.
Known issues: Known issues:
* Anything that llvm 11+ cannot compile, afl-clang-lto cannot compile either - * Anything that LLVM 11+ cannot compile, afl-clang-lto cannot compile either -
obviously. obviously.
* Anything that does not compile with LTO, afl-clang-lto cannot compile either - * Anything that does not compile with LTO, afl-clang-lto cannot compile either -
obviously. obviously.
Hence, if building a target with afl-clang-lto fails, try to build it with Hence, if building a target with afl-clang-lto fails, try to build it with
llvm12 and LTO enabled (`CC=clang-12`, `CXX=clang++-12`, `CFLAGS=-flto=full`, LLVM 12 and LTO enabled (`CC=clang-12`, `CXX=clang++-12`, `CFLAGS=-flto=full`,
and `CXXFLAGS=-flto=full`). and `CXXFLAGS=-flto=full`).
If this succeeds, then there is an issue with afl-clang-lto. Please report at If this succeeds, then there is an issue with afl-clang-lto. Please report at
@ -341,7 +301,7 @@ knows what this is doing. And the developer who implemented this didn't respond
to emails.) to emails.)
In December then came the idea to implement this as a pass that is run via the In December then came the idea to implement this as a pass that is run via the
llvm "opt" program, which is performed via an own linker that afterwards calls LLVM "opt" program, which is performed via an own linker that afterwards calls
the real linker. This was first implemented in January and work ... kinda. The the real linker. This was first implemented in January and work ... kinda. The
LTO time instrumentation worked, however, "how" the basic blocks were LTO time instrumentation worked, however, "how" the basic blocks were
instrumented was a problem, as reducing duplicates turned out to be very, very instrumented was a problem, as reducing duplicates turned out to be very, very
@ -353,13 +313,13 @@ dead-end too.
The final idea to solve this came from domenukk who proposed to insert a block The final idea to solve this came from domenukk who proposed to insert a block
into an edge and then just use incremental counters ... and this worked! After into an edge and then just use incremental counters ... and this worked! After
some trials and errors to implement this vanhauser-thc found out that there is some trials and errors to implement this vanhauser-thc found out that there is
actually an llvm function for this: SplitEdge() :-) actually an LLVM function for this: SplitEdge() :-)
Still more problems came up though as this only works without bugs from llvm 9 Still more problems came up though as this only works without bugs from LLVM 9
onwards, and with high optimization the link optimization ruins the instrumented onwards, and with high optimization the link optimization ruins the instrumented
control flow graph. control flow graph.
This is all now fixed with llvm 11+. The llvm's own linker is now able to load This is all now fixed with LLVM 11+. The llvm's own linker is now able to load
passes and this bypasses all problems we had. passes and this bypasses all problems we had.
Happy end :) Happy end :)

View File

@ -1,4 +1,4 @@
/* SanitizeCoverage.cpp ported to afl++ LTO :-) */ /* SanitizeCoverage.cpp ported to AFL++ LTO :-) */
#define AFL_LLVM_PASS #define AFL_LLVM_PASS
@ -17,8 +17,12 @@
#include "llvm/Transforms/Instrumentation/SanitizerCoverage.h" #include "llvm/Transforms/Instrumentation/SanitizerCoverage.h"
#include "llvm/ADT/ArrayRef.h" #include "llvm/ADT/ArrayRef.h"
#include "llvm/ADT/SmallVector.h" #include "llvm/ADT/SmallVector.h"
#include "llvm/ADT/Triple.h" #if LLVM_VERSION_MAJOR < 17
#include "llvm/Analysis/EHPersonalities.h" #include "llvm/ADT/Triple.h"
#include "llvm/Analysis/EHPersonalities.h"
#else
#include "llvm/IR/EHPersonalities.h"
#endif
#include "llvm/Analysis/PostDominators.h" #include "llvm/Analysis/PostDominators.h"
#include "llvm/Analysis/ValueTracking.h" #include "llvm/Analysis/ValueTracking.h"
#include "llvm/IR/BasicBlock.h" #include "llvm/IR/BasicBlock.h"
@ -47,7 +51,9 @@
#include "llvm/Support/VirtualFileSystem.h" #include "llvm/Support/VirtualFileSystem.h"
#include "llvm/Support/raw_ostream.h" #include "llvm/Support/raw_ostream.h"
#include "llvm/Transforms/Instrumentation.h" #include "llvm/Transforms/Instrumentation.h"
#include "llvm/Transforms/IPO/PassManagerBuilder.h" #if LLVM_VERSION_MAJOR < 17
#include "llvm/Transforms/IPO/PassManagerBuilder.h"
#endif
#include "llvm/Transforms/Utils/BasicBlockUtils.h" #include "llvm/Transforms/Utils/BasicBlockUtils.h"
#include "llvm/Transforms/Utils/BasicBlockUtils.h" #include "llvm/Transforms/Utils/BasicBlockUtils.h"
#include "llvm/Transforms/Utils/ModuleUtils.h" #include "llvm/Transforms/Utils/ModuleUtils.h"
@ -232,7 +238,7 @@ class ModuleSanitizerCoverageLTO
SanitizerCoverageOptions Options; SanitizerCoverageOptions Options;
// afl++ START // AFL++ START
// const SpecialCaseList * Allowlist; // const SpecialCaseList * Allowlist;
// const SpecialCaseList * Blocklist; // const SpecialCaseList * Blocklist;
uint32_t autodictionary = 1; uint32_t autodictionary = 1;
@ -258,7 +264,7 @@ class ModuleSanitizerCoverageLTO
Value *MapPtrFixed = NULL; Value *MapPtrFixed = NULL;
std::ofstream dFile; std::ofstream dFile;
size_t found = 0; size_t found = 0;
// afl++ END // AFL++ END
}; };
@ -325,7 +331,7 @@ llvmGetPassPluginInfo() {
#if LLVM_VERSION_MAJOR <= 13 #if LLVM_VERSION_MAJOR <= 13
using OptimizationLevel = typename PassBuilder::OptimizationLevel; using OptimizationLevel = typename PassBuilder::OptimizationLevel;
#endif #endif
#if LLVM_VERSION_MAJOR >= 15 #if LLVM_VERSION_MAJOR >= 16
PB.registerFullLinkTimeOptimizationLastEPCallback( PB.registerFullLinkTimeOptimizationLastEPCallback(
#else #else
PB.registerOptimizerLastEPCallback( PB.registerOptimizerLastEPCallback(
@ -402,7 +408,7 @@ bool ModuleSanitizerCoverageLTO::instrumentModule(
Int8Ty = IRB.getInt8Ty(); Int8Ty = IRB.getInt8Ty();
Int1Ty = IRB.getInt1Ty(); Int1Ty = IRB.getInt1Ty();
/* afl++ START */ /* AFL++ START */
char *ptr; char *ptr;
LLVMContext &Ctx = M.getContext(); LLVMContext &Ctx = M.getContext();
Ct = &Ctx; Ct = &Ctx;
@ -431,6 +437,8 @@ bool ModuleSanitizerCoverageLTO::instrumentModule(
if ((afl_global_id = atoi(ptr)) < 0) if ((afl_global_id = atoi(ptr)) < 0)
FATAL("AFL_LLVM_LTO_STARTID value of \"%s\" is negative\n", ptr); FATAL("AFL_LLVM_LTO_STARTID value of \"%s\" is negative\n", ptr);
if (afl_global_id < 4) { afl_global_id = 4; }
if ((ptr = getenv("AFL_LLVM_DOCUMENT_IDS")) != NULL) { if ((ptr = getenv("AFL_LLVM_DOCUMENT_IDS")) != NULL) {
dFile.open(ptr, std::ofstream::out | std::ofstream::app); dFile.open(ptr, std::ofstream::out | std::ofstream::app);
@ -974,7 +982,7 @@ bool ModuleSanitizerCoverageLTO::instrumentModule(
} }
// afl++ END // AFL++ END
SanCovTracePCIndir = SanCovTracePCIndir =
M.getOrInsertFunction(SanCovTracePCIndirName, VoidTy, IntptrTy); M.getOrInsertFunction(SanCovTracePCIndirName, VoidTy, IntptrTy);
@ -998,10 +1006,11 @@ bool ModuleSanitizerCoverageLTO::instrumentModule(
for (auto &F : M) for (auto &F : M)
instrumentFunction(F, DTCallback, PDTCallback); instrumentFunction(F, DTCallback, PDTCallback);
// afl++ START // AFL++ START
if (dFile.is_open()) dFile.close(); if (dFile.is_open()) dFile.close();
if (!getenv("AFL_LLVM_LTO_DONTWRITEID") || dictionary.size() || map_addr) { if (!getenv("AFL_LLVM_LTO_SKIPINIT") &&
(!getenv("AFL_LLVM_LTO_DONTWRITEID") || dictionary.size() || map_addr)) {
// yes we could create our own function, insert it into ctors ... // yes we could create our own function, insert it into ctors ...
// but this would be a pain in the butt ... so we use afl-llvm-rt-lto.o // but this would be a pain in the butt ... so we use afl-llvm-rt-lto.o
@ -1151,7 +1160,7 @@ bool ModuleSanitizerCoverageLTO::instrumentModule(
} }
// afl++ END // AFL++ END
// We don't reference these arrays directly in any of our runtime functions, // We don't reference these arrays directly in any of our runtime functions,
// so we need to prevent them from being dead stripped. // so we need to prevent them from being dead stripped.
@ -1208,10 +1217,10 @@ static bool shouldInstrumentBlock(const Function &F, const BasicBlock *BB,
// (catchswitch blocks). // (catchswitch blocks).
if (BB->getFirstInsertionPt() == BB->end()) return false; if (BB->getFirstInsertionPt() == BB->end()) return false;
// afl++ START // AFL++ START
if (!Options.NoPrune && &F.getEntryBlock() == BB && F.size() > 1) if (!Options.NoPrune && &F.getEntryBlock() == BB && F.size() > 1)
return false; return false;
// afl++ END // AFL++ END
if (Options.NoPrune || &F.getEntryBlock() == BB) return true; if (Options.NoPrune || &F.getEntryBlock() == BB) return true;
@ -1253,10 +1262,10 @@ void ModuleSanitizerCoverageLTO::instrumentFunction(
// if (Blocklist && Blocklist->inSection("coverage", "fun", F.getName())) // if (Blocklist && Blocklist->inSection("coverage", "fun", F.getName()))
// return; // return;
// afl++ START // AFL++ START
if (!F.size()) return; if (!F.size()) return;
if (!isInInstrumentList(&F, FMNAME)) return; if (!isInInstrumentList(&F, FMNAME)) return;
// afl++ END // AFL++ END
if (Options.CoverageType >= SanitizerCoverageOptions::SCK_Edge) if (Options.CoverageType >= SanitizerCoverageOptions::SCK_Edge)
SplitAllCriticalEdges( SplitAllCriticalEdges(
@ -1469,8 +1478,8 @@ GlobalVariable *ModuleSanitizerCoverageLTO::CreateFunctionLocalArrayInSection(
ArrayType *ArrayTy = ArrayType::get(Ty, NumElements); ArrayType *ArrayTy = ArrayType::get(Ty, NumElements);
auto Array = new GlobalVariable( auto Array = new GlobalVariable(
*CurModule, ArrayTy, false, GlobalVariable::PrivateLinkage, *CurModule, ArrayTy, false, GlobalVariable::PrivateLinkage,
Constant::getNullValue(ArrayTy), "__sancov_gen_"); Constant::getNullValue(ArrayTy), "__sancov_gen_");
#if LLVM_VERSION_MAJOR >= 13 #if LLVM_VERSION_MAJOR >= 13
if (TargetTriple.supportsCOMDAT() && if (TargetTriple.supportsCOMDAT() &&
@ -1554,7 +1563,7 @@ bool ModuleSanitizerCoverageLTO::InjectCoverage(
for (size_t i = 0, N = AllBlocks.size(); i < N; i++) { for (size_t i = 0, N = AllBlocks.size(); i < N; i++) {
// afl++ START // AFL++ START
if (BlockList.size()) { if (BlockList.size()) {
int skip = 0; int skip = 0;
@ -1576,7 +1585,7 @@ bool ModuleSanitizerCoverageLTO::InjectCoverage(
} }
// afl++ END // AFL++ END
InjectCoverageAtBlock(F, *AllBlocks[i], i, IsLeafFunc); InjectCoverageAtBlock(F, *AllBlocks[i], i, IsLeafFunc);
@ -1642,7 +1651,7 @@ void ModuleSanitizerCoverageLTO::InjectCoverageAtBlock(Function &F,
if (Options.TracePCGuard) { if (Options.TracePCGuard) {
// afl++ START // AFL++ START
++afl_global_id; ++afl_global_id;
if (dFile.is_open()) { if (dFile.is_open()) {
@ -1706,7 +1715,7 @@ void ModuleSanitizerCoverageLTO::InjectCoverageAtBlock(Function &F,
// done :) // done :)
inst++; inst++;
// afl++ END // AFL++ END
/* /*
XXXXXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXXXXX
@ -1779,6 +1788,7 @@ INITIALIZE_PASS_END(ModuleSanitizerCoverageLTOLegacyPass, "sancov-lto",
"Pass for instrumenting coverage on functions", false, "Pass for instrumenting coverage on functions", false,
false) false)
#if LLVM_VERSION_MAJOR < 16
static void registerLTOPass(const PassManagerBuilder &, static void registerLTOPass(const PassManagerBuilder &,
legacy::PassManagerBase &PM) { legacy::PassManagerBase &PM) {
@ -1787,7 +1797,6 @@ static void registerLTOPass(const PassManagerBuilder &,
} }
#if LLVM_VERSION_MAJOR < 16
static RegisterStandardPasses RegisterCompTransPass( static RegisterStandardPasses RegisterCompTransPass(
PassManagerBuilder::EP_OptimizerLast, registerLTOPass); PassManagerBuilder::EP_OptimizerLast, registerLTOPass);

View File

@ -13,38 +13,63 @@
#include "llvm/Transforms/Instrumentation/SanitizerCoverage.h" #include "llvm/Transforms/Instrumentation/SanitizerCoverage.h"
#include "llvm/ADT/ArrayRef.h" #include "llvm/ADT/ArrayRef.h"
#include "llvm/ADT/SmallVector.h" #include "llvm/ADT/SmallVector.h"
#include "llvm/ADT/Triple.h" #if LLVM_VERSION_MAJOR >= 15
#include "llvm/Analysis/EHPersonalities.h" #if LLVM_VERSION_MAJOR < 17
#include "llvm/ADT/Triple.h"
#endif
#endif
#include "llvm/Analysis/PostDominators.h" #include "llvm/Analysis/PostDominators.h"
#include "llvm/IR/CFG.h" #if LLVM_VERSION_MAJOR < 15
#include "llvm/IR/CFG.h"
#endif
#include "llvm/IR/Constant.h" #include "llvm/IR/Constant.h"
#include "llvm/IR/DataLayout.h" #include "llvm/IR/DataLayout.h"
#include "llvm/IR/DebugInfo.h" #if LLVM_VERSION_MAJOR < 15
#include "llvm/IR/DebugInfo.h"
#endif
#include "llvm/IR/Dominators.h" #include "llvm/IR/Dominators.h"
#if LLVM_VERSION_MAJOR >= 17
#include "llvm/IR/EHPersonalities.h"
#else
#include "llvm/Analysis/EHPersonalities.h"
#endif
#include "llvm/IR/Function.h" #include "llvm/IR/Function.h"
#include "llvm/IR/GlobalVariable.h" #if LLVM_VERSION_MAJOR >= 16
#include "llvm/IR/GlobalVariable.h"
#endif
#include "llvm/IR/IRBuilder.h" #include "llvm/IR/IRBuilder.h"
#include "llvm/IR/InlineAsm.h" #if LLVM_VERSION_MAJOR < 15
#include "llvm/IR/InlineAsm.h"
#endif
#include "llvm/IR/IntrinsicInst.h" #include "llvm/IR/IntrinsicInst.h"
#include "llvm/IR/Intrinsics.h" #include "llvm/IR/Intrinsics.h"
#include "llvm/IR/LLVMContext.h" #include "llvm/IR/LLVMContext.h"
#include "llvm/IR/MDBuilder.h" #if LLVM_VERSION_MAJOR < 15
#include "llvm/IR/Mangler.h" #include "llvm/IR/MDBuilder.h"
#include "llvm/IR/Mangler.h"
#endif
#include "llvm/IR/Module.h" #include "llvm/IR/Module.h"
#include "llvm/IR/PassManager.h" #include "llvm/IR/PassManager.h"
#include "llvm/Passes/PassBuilder.h"
#include "llvm/Passes/PassPlugin.h"
#include "llvm/IR/Type.h" #include "llvm/IR/Type.h"
#include "llvm/InitializePasses.h" #if LLVM_VERSION_MAJOR < 17
#include "llvm/InitializePasses.h"
#endif
#include "llvm/Support/CommandLine.h" #include "llvm/Support/CommandLine.h"
#include "llvm/Support/Debug.h" #include "llvm/Support/Debug.h"
#include "llvm/Support/SpecialCaseList.h" #include "llvm/Support/SpecialCaseList.h"
#include "llvm/Support/VirtualFileSystem.h" #include "llvm/Support/VirtualFileSystem.h"
#include "llvm/Support/raw_ostream.h" #if LLVM_VERSION_MAJOR < 15
#include "llvm/Transforms/Instrumentation.h" #include "llvm/Support/raw_ostream.h"
#endif
#if LLVM_VERSION_MAJOR < 17
#include "llvm/Transforms/Instrumentation.h"
#else
#include "llvm/TargetParser/Triple.h"
#endif
#include "llvm/Transforms/Utils/BasicBlockUtils.h" #include "llvm/Transforms/Utils/BasicBlockUtils.h"
#include "llvm/Transforms/Utils/ModuleUtils.h" #include "llvm/Transforms/Utils/ModuleUtils.h"
#include "llvm/Passes/PassPlugin.h"
#include "llvm/Passes/PassBuilder.h"
#include "llvm/IR/PassManager.h"
#include "config.h" #include "config.h"
#include "debug.h" #include "debug.h"
@ -54,7 +79,8 @@ using namespace llvm;
#define DEBUG_TYPE "sancov" #define DEBUG_TYPE "sancov"
const char SanCovTracePCIndirName[] = "__sanitizer_cov_trace_pc_indir"; static const uint64_t SanCtorAndDtorPriority = 2;
const char SanCovTracePCName[] = "__sanitizer_cov_trace_pc"; const char SanCovTracePCName[] = "__sanitizer_cov_trace_pc";
const char SanCovTraceCmp1[] = "__sanitizer_cov_trace_cmp1"; const char SanCovTraceCmp1[] = "__sanitizer_cov_trace_cmp1";
const char SanCovTraceCmp2[] = "__sanitizer_cov_trace_cmp2"; const char SanCovTraceCmp2[] = "__sanitizer_cov_trace_cmp2";
@ -64,22 +90,13 @@ const char SanCovTraceConstCmp1[] = "__sanitizer_cov_trace_const_cmp1";
const char SanCovTraceConstCmp2[] = "__sanitizer_cov_trace_const_cmp2"; const char SanCovTraceConstCmp2[] = "__sanitizer_cov_trace_const_cmp2";
const char SanCovTraceConstCmp4[] = "__sanitizer_cov_trace_const_cmp4"; const char SanCovTraceConstCmp4[] = "__sanitizer_cov_trace_const_cmp4";
const char SanCovTraceConstCmp8[] = "__sanitizer_cov_trace_const_cmp8"; const char SanCovTraceConstCmp8[] = "__sanitizer_cov_trace_const_cmp8";
const char SanCovTraceDiv4[] = "__sanitizer_cov_trace_div4";
const char SanCovTraceDiv8[] = "__sanitizer_cov_trace_div8";
const char SanCovTraceGep[] = "__sanitizer_cov_trace_gep";
const char SanCovTraceSwitchName[] = "__sanitizer_cov_trace_switch"; const char SanCovTraceSwitchName[] = "__sanitizer_cov_trace_switch";
const char SanCovModuleCtorTracePcGuardName[] = const char SanCovModuleCtorTracePcGuardName[] =
"sancov.module_ctor_trace_pc_guard"; "sancov.module_ctor_trace_pc_guard";
const char SanCovModuleCtor8bitCountersName[] = const char SanCovTracePCGuardInitName[] = "__sanitizer_cov_trace_pc_guard_init";
"sancov.module_ctor_8bit_counters";
const char SanCovModuleCtorBoolFlagName[] = "sancov.module_ctor_bool_flag";
static const uint64_t SanCtorAndDtorPriority = 2;
const char SanCovTracePCGuardName[] = "__sanitizer_cov_trace_pc_guard"; const char SanCovTracePCGuardName[] = "__sanitizer_cov_trace_pc_guard";
const char SanCovTracePCGuardInitName[] = "__sanitizer_cov_trace_pc_guard_init";
const char SanCov8bitCountersInitName[] = "__sanitizer_cov_8bit_counters_init";
const char SanCovBoolFlagInitName[] = "__sanitizer_cov_bool_flag_init";
const char SanCovPCsInitName[] = "__sanitizer_cov_pcs_init";
const char SanCovGuardsSectionName[] = "sancov_guards"; const char SanCovGuardsSectionName[] = "sancov_guards";
const char SanCovCountersSectionName[] = "sancov_cntrs"; const char SanCovCountersSectionName[] = "sancov_cntrs";
@ -95,27 +112,9 @@ namespace {
SanitizerCoverageOptions OverrideFromCL(SanitizerCoverageOptions Options) { SanitizerCoverageOptions OverrideFromCL(SanitizerCoverageOptions Options) {
// Sets CoverageType and IndirectCalls. Options.CoverageType = SanitizerCoverageOptions::SCK_Edge;
// SanitizerCoverageOptions CLOpts = getOptions(ClCoverageLevel); // Options.NoPrune = true;
Options.CoverageType = Options.TracePCGuard = true; // TracePCGuard is default.
SanitizerCoverageOptions::SCK_Edge; // std::max(Options.CoverageType,
// CLOpts.CoverageType);
Options.IndirectCalls = false; // CLOpts.IndirectCalls;
Options.TraceCmp = false; //|= ClCMPTracing;
Options.TraceDiv = false; //|= ClDIVTracing;
Options.TraceGep = false; //|= ClGEPTracing;
Options.TracePC = false; //|= ClTracePC;
Options.TracePCGuard = true; // |= ClTracePCGuard;
Options.Inline8bitCounters = 0; //|= ClInline8bitCounters;
// Options.InlineBoolFlag = 0; //|= ClInlineBoolFlag;
Options.PCTable = false; //|= ClCreatePCTable;
Options.NoPrune = false; //|= !ClPruneBlocks;
Options.StackDepth = false; //|= ClStackDepth;
if (!Options.TracePCGuard && !Options.TracePC &&
!Options.Inline8bitCounters && !Options.StackDepth /*&&
!Options.InlineBoolFlag*/)
Options.TracePCGuard = true; // TracePCGuard is default.
return Options; return Options;
} }
@ -135,20 +134,13 @@ class ModuleSanitizerCoverageAFL
} }
PreservedAnalyses run(Module &M, ModuleAnalysisManager &MAM); PreservedAnalyses run(Module &M, ModuleAnalysisManager &MAM);
bool instrumentModule(Module &M, DomTreeCallback DTCallback,
bool instrumentModule(Module &M, DomTreeCallback DTCallback, PostDomTreeCallback PDTCallback);
PostDomTreeCallback PDTCallback);
private: private:
void instrumentFunction(Function &F, DomTreeCallback DTCallback, void instrumentFunction(Function &F, DomTreeCallback DTCallback,
PostDomTreeCallback PDTCallback); PostDomTreeCallback PDTCallback);
void InjectCoverageForIndirectCalls(Function &F,
ArrayRef<Instruction *> IndirCalls);
void InjectTraceForCmp(Function &F, ArrayRef<Instruction *> CmpTraceTargets); void InjectTraceForCmp(Function &F, ArrayRef<Instruction *> CmpTraceTargets);
void InjectTraceForDiv(Function &F,
ArrayRef<BinaryOperator *> DivTraceTargets);
void InjectTraceForGep(Function &F,
ArrayRef<GetElementPtrInst *> GepTraceTargets);
void InjectTraceForSwitch(Function &F, void InjectTraceForSwitch(Function &F,
ArrayRef<Instruction *> SwitchTraceTargets); ArrayRef<Instruction *> SwitchTraceTargets);
bool InjectCoverage(Function &F, ArrayRef<BasicBlock *> AllBlocks, bool InjectCoverage(Function &F, ArrayRef<BasicBlock *> AllBlocks,
@ -169,20 +161,21 @@ class ModuleSanitizerCoverageAFL
void SetNoSanitizeMetadata(Instruction *I) { void SetNoSanitizeMetadata(Instruction *I) {
#if LLVM_VERSION_MAJOR >= 16
I->setMetadata(LLVMContext::MD_nosanitize, MDNode::get(*C, std::nullopt));
#else
I->setMetadata(I->getModule()->getMDKindID("nosanitize"), I->setMetadata(I->getModule()->getMDKindID("nosanitize"),
MDNode::get(*C, None)); MDNode::get(*C, None));
#endif
} }
std::string getSectionName(const std::string &Section) const; std::string getSectionName(const std::string &Section) const;
std::string getSectionStart(const std::string &Section) const; std::string getSectionStart(const std::string &Section) const;
std::string getSectionEnd(const std::string &Section) const; std::string getSectionEnd(const std::string &Section) const;
FunctionCallee SanCovTracePCIndir;
FunctionCallee SanCovTracePC, SanCovTracePCGuard; FunctionCallee SanCovTracePC, SanCovTracePCGuard;
FunctionCallee SanCovTraceCmpFunction[4]; FunctionCallee SanCovTraceCmpFunction[4];
FunctionCallee SanCovTraceConstCmpFunction[4]; FunctionCallee SanCovTraceConstCmpFunction[4];
FunctionCallee SanCovTraceDivFunction[2];
FunctionCallee SanCovTraceGepFunction;
FunctionCallee SanCovTraceSwitchFunction; FunctionCallee SanCovTraceSwitchFunction;
GlobalVariable *SanCovLowestStack; GlobalVariable *SanCovLowestStack;
Type *IntptrTy, *IntptrPtrTy, *Int64Ty, *Int64PtrTy, *Int32Ty, *Int32PtrTy, Type *IntptrTy, *IntptrPtrTy, *Int64Ty, *Int64PtrTy, *Int32Ty, *Int32PtrTy,
@ -211,18 +204,16 @@ class ModuleSanitizerCoverageAFL
} // namespace } // namespace
#if LLVM_VERSION_MAJOR >= 11 /* use new pass manager */
extern "C" ::llvm::PassPluginLibraryInfo LLVM_ATTRIBUTE_WEAK extern "C" ::llvm::PassPluginLibraryInfo LLVM_ATTRIBUTE_WEAK
llvmGetPassPluginInfo() { llvmGetPassPluginInfo() {
return {LLVM_PLUGIN_API_VERSION, "SanitizerCoveragePCGUARD", "v0.1", return {LLVM_PLUGIN_API_VERSION, "SanitizerCoveragePCGUARD", "v0.2",
/* lambda to insert our pass into the pass pipeline. */ /* lambda to insert our pass into the pass pipeline. */
[](PassBuilder &PB) { [](PassBuilder &PB) {
#if LLVM_VERSION_MAJOR <= 13 #if LLVM_VERSION_MAJOR == 13
using OptimizationLevel = typename PassBuilder::OptimizationLevel; using OptimizationLevel = typename PassBuilder::OptimizationLevel;
#endif #endif
PB.registerOptimizerLastEPCallback( PB.registerOptimizerLastEPCallback(
[](ModulePassManager &MPM, OptimizationLevel OL) { [](ModulePassManager &MPM, OptimizationLevel OL) {
@ -234,8 +225,7 @@ llvmGetPassPluginInfo() {
} }
#endif #if LLVM_VERSION_MAJOR == 1
PreservedAnalyses ModuleSanitizerCoverageAFL::run(Module &M, PreservedAnalyses ModuleSanitizerCoverageAFL::run(Module &M,
ModuleAnalysisManager &MAM) { ModuleAnalysisManager &MAM) {
@ -253,34 +243,65 @@ PreservedAnalyses ModuleSanitizerCoverageAFL::run(Module &M,
}; };
if (!ModuleSancov.instrumentModule(M, DTCallback, PDTCallback))
return PreservedAnalyses::all();
PreservedAnalyses PA = PreservedAnalyses::none();
// GlobalsAA is considered stateless and does not get invalidated unless
// explicitly invalidated; PreservedAnalyses::none() is not enough. Sanitizers
// make changes that require GlobalsAA to be invalidated.
PA.abandon<GlobalsAA>();
return PA;
}
#else
#if LLVM_VERSION_MAJOR >= 16
PreservedAnalyses ModuleSanitizerCoverageAFL::run(Module &M,
ModuleAnalysisManager &MAM) {
#else
PreservedAnalyses ModuleSanitizerCoverageAFL::run(Module &M,
ModuleAnalysisManager &MAM) {
#endif
ModuleSanitizerCoverageAFL ModuleSancov(Options);
auto &FAM = MAM.getResult<FunctionAnalysisManagerModuleProxy>(M).getManager();
auto DTCallback = [&FAM](Function &F) -> const DominatorTree * {
return &FAM.getResult<DominatorTreeAnalysis>(F);
};
auto PDTCallback = [&FAM](Function &F) -> const PostDominatorTree * {
return &FAM.getResult<PostDominatorTreeAnalysis>(F);
};
if (ModuleSancov.instrumentModule(M, DTCallback, PDTCallback)) if (ModuleSancov.instrumentModule(M, DTCallback, PDTCallback))
return PreservedAnalyses::none(); return PreservedAnalyses::none();
return PreservedAnalyses::all(); return PreservedAnalyses::all();
} }
#endif
std::pair<Value *, Value *> ModuleSanitizerCoverageAFL::CreateSecStartEnd( std::pair<Value *, Value *> ModuleSanitizerCoverageAFL::CreateSecStartEnd(
Module &M, const char *Section, Type *Ty) { Module &M, const char *Section, Type *Ty) {
GlobalVariable *SecStart = // Use ExternalWeak so that if all sections are discarded due to section
new GlobalVariable(M, // garbage collection, the linker will not report undefined symbol errors.
#if LLVM_VERSION_MAJOR >= 15 // Windows defines the start/stop symbols in compiler-rt so no need for
Ty, // ExternalWeak.
#else GlobalValue::LinkageTypes Linkage = TargetTriple.isOSBinFormatCOFF()
Ty->getPointerElementType(), ? GlobalVariable::ExternalLinkage
#endif : GlobalVariable::ExternalWeakLinkage;
false, GlobalVariable::ExternalWeakLinkage, nullptr, GlobalVariable *SecStart = new GlobalVariable(M, Ty, false, Linkage, nullptr,
getSectionStart(Section)); getSectionStart(Section));
SecStart->setVisibility(GlobalValue::HiddenVisibility); SecStart->setVisibility(GlobalValue::HiddenVisibility);
GlobalVariable *SecEnd = GlobalVariable *SecEnd = new GlobalVariable(M, Ty, false, Linkage, nullptr,
new GlobalVariable(M, getSectionEnd(Section));
#if LLVM_VERSION_MAJOR >= 15
Ty,
#else
Ty->getPointerElementType(),
#endif
false, GlobalVariable::ExternalWeakLinkage, nullptr,
getSectionEnd(Section));
SecEnd->setVisibility(GlobalValue::HiddenVisibility); SecEnd->setVisibility(GlobalValue::HiddenVisibility);
IRBuilder<> IRB(M.getContext()); IRBuilder<> IRB(M.getContext());
if (!TargetTriple.isOSBinFormatCOFF()) if (!TargetTriple.isOSBinFormatCOFF())
@ -291,7 +312,8 @@ std::pair<Value *, Value *> ModuleSanitizerCoverageAFL::CreateSecStartEnd(
auto SecStartI8Ptr = IRB.CreatePointerCast(SecStart, Int8PtrTy); auto SecStartI8Ptr = IRB.CreatePointerCast(SecStart, Int8PtrTy);
auto GEP = IRB.CreateGEP(Int8Ty, SecStartI8Ptr, auto GEP = IRB.CreateGEP(Int8Ty, SecStartI8Ptr,
ConstantInt::get(IntptrTy, sizeof(uint64_t))); ConstantInt::get(IntptrTy, sizeof(uint64_t)));
return std::make_pair(IRB.CreatePointerCast(GEP, Ty), SecEnd); return std::make_pair(IRB.CreatePointerCast(GEP, PointerType::getUnqual(Ty)),
SecEnd);
} }
@ -303,8 +325,9 @@ Function *ModuleSanitizerCoverageAFL::CreateInitCallsForSections(
auto SecStart = SecStartEnd.first; auto SecStart = SecStartEnd.first;
auto SecEnd = SecStartEnd.second; auto SecEnd = SecStartEnd.second;
Function *CtorFunc; Function *CtorFunc;
Type *PtrTy = PointerType::getUnqual(Ty);
std::tie(CtorFunc, std::ignore) = createSanitizerCtorAndInitFunctions( std::tie(CtorFunc, std::ignore) = createSanitizerCtorAndInitFunctions(
M, CtorName, InitFunctionName, {Ty, Ty}, {SecStart, SecEnd}); M, CtorName, InitFunctionName, {PtrTy, PtrTy}, {SecStart, SecEnd});
assert(CtorFunc->getName() == CtorName); assert(CtorFunc->getName() == CtorName);
if (TargetTriple.supportsCOMDAT()) { if (TargetTriple.supportsCOMDAT()) {
@ -328,7 +351,6 @@ Function *ModuleSanitizerCoverageAFL::CreateInitCallsForSections(
// to include the sancov constructor. This way the linker can deduplicate // to include the sancov constructor. This way the linker can deduplicate
// the constructors but always leave one copy. // the constructors but always leave one copy.
CtorFunc->setLinkage(GlobalValue::WeakODRLinkage); CtorFunc->setLinkage(GlobalValue::WeakODRLinkage);
appendToUsed(M, CtorFunc);
} }
@ -340,37 +362,25 @@ bool ModuleSanitizerCoverageAFL::instrumentModule(
Module &M, DomTreeCallback DTCallback, PostDomTreeCallback PDTCallback) { Module &M, DomTreeCallback DTCallback, PostDomTreeCallback PDTCallback) {
setvbuf(stdout, NULL, _IONBF, 0); setvbuf(stdout, NULL, _IONBF, 0);
if (getenv("AFL_DEBUG")) debug = 1;
if (getenv("AFL_DEBUG")) { debug = 1; }
if ((isatty(2) && !getenv("AFL_QUIET")) || debug) { if ((isatty(2) && !getenv("AFL_QUIET")) || debug) {
SAYF(cCYA "SanitizerCoveragePCGUARD" VERSION cRST "\n"); SAYF(cCYA "SanitizerCoveragePCGUARD" VERSION cRST "\n");
} else } else {
be_quiet = 1; be_quiet = 1;
}
skip_nozero = getenv("AFL_LLVM_SKIP_NEVERZERO"); skip_nozero = getenv("AFL_LLVM_SKIP_NEVERZERO");
use_threadsafe_counters = getenv("AFL_LLVM_THREADSAFE_INST"); use_threadsafe_counters = getenv("AFL_LLVM_THREADSAFE_INST");
initInstrumentList(); initInstrumentList();
scanForDangerousFunctions(&M); scanForDangerousFunctions(&M);
if (debug) {
fprintf(stderr,
"SANCOV: covtype:%u indirect:%d stack:%d noprune:%d "
"createtable:%d tracepcguard:%d tracepc:%d\n",
Options.CoverageType, Options.IndirectCalls == true ? 1 : 0,
Options.StackDepth == true ? 1 : 0, Options.NoPrune == true ? 1 : 0,
// Options.InlineBoolFlag == true ? 1 : 0,
Options.PCTable == true ? 1 : 0,
Options.TracePCGuard == true ? 1 : 0,
Options.TracePC == true ? 1 : 0);
}
if (Options.CoverageType == SanitizerCoverageOptions::SCK_None) return false;
C = &(M.getContext()); C = &(M.getContext());
DL = &M.getDataLayout(); DL = &M.getDataLayout();
CurModule = &M; CurModule = &M;
@ -393,16 +403,14 @@ bool ModuleSanitizerCoverageAFL::instrumentModule(
Int16Ty = IRB.getInt16Ty(); Int16Ty = IRB.getInt16Ty();
Int8Ty = IRB.getInt8Ty(); Int8Ty = IRB.getInt8Ty();
Int1Ty = IRB.getInt1Ty(); Int1Ty = IRB.getInt1Ty();
LLVMContext &Ctx = M.getContext();
LLVMContext &Ctx = M.getContext();
AFLMapPtr = AFLMapPtr =
new GlobalVariable(M, PointerType::get(Int8Ty, 0), false, new GlobalVariable(M, PointerType::get(Int8Ty, 0), false,
GlobalValue::ExternalLinkage, 0, "__afl_area_ptr"); GlobalValue::ExternalLinkage, 0, "__afl_area_ptr");
One = ConstantInt::get(IntegerType::getInt8Ty(Ctx), 1); One = ConstantInt::get(IntegerType::getInt8Ty(Ctx), 1);
Zero = ConstantInt::get(IntegerType::getInt8Ty(Ctx), 0); Zero = ConstantInt::get(IntegerType::getInt8Ty(Ctx), 0);
SanCovTracePCIndir =
M.getOrInsertFunction(SanCovTracePCIndirName, VoidTy, IntptrTy);
// Make sure smaller parameters are zero-extended to i64 if required by the // Make sure smaller parameters are zero-extended to i64 if required by the
// target ABI. // target ABI.
AttributeList SanCovTraceCmpZeroExtAL; AttributeList SanCovTraceCmpZeroExtAL;
@ -432,26 +440,13 @@ bool ModuleSanitizerCoverageAFL::instrumentModule(
SanCovTraceConstCmpFunction[3] = SanCovTraceConstCmpFunction[3] =
M.getOrInsertFunction(SanCovTraceConstCmp8, VoidTy, Int64Ty, Int64Ty); M.getOrInsertFunction(SanCovTraceConstCmp8, VoidTy, Int64Ty, Int64Ty);
{
AttributeList AL;
AL = AL.addParamAttribute(*C, 0, Attribute::ZExt);
SanCovTraceDivFunction[0] =
M.getOrInsertFunction(SanCovTraceDiv4, AL, VoidTy, IRB.getInt32Ty());
}
SanCovTraceDivFunction[1] =
M.getOrInsertFunction(SanCovTraceDiv8, VoidTy, Int64Ty);
SanCovTraceGepFunction =
M.getOrInsertFunction(SanCovTraceGep, VoidTy, IntptrTy);
SanCovTraceSwitchFunction = SanCovTraceSwitchFunction =
M.getOrInsertFunction(SanCovTraceSwitchName, VoidTy, Int64Ty, Int64PtrTy); M.getOrInsertFunction(SanCovTraceSwitchName, VoidTy, Int64Ty, Int64PtrTy);
Constant *SanCovLowestStackConstant = Constant *SanCovLowestStackConstant =
M.getOrInsertGlobal(SanCovLowestStackName, IntptrTy); M.getOrInsertGlobal(SanCovLowestStackName, IntptrTy);
SanCovLowestStack = dyn_cast<GlobalVariable>(SanCovLowestStackConstant); SanCovLowestStack = dyn_cast<GlobalVariable>(SanCovLowestStackConstant);
if (!SanCovLowestStack) { if (!SanCovLowestStack || SanCovLowestStack->getValueType() != IntptrTy) {
C->emitError(StringRef("'") + SanCovLowestStackName + C->emitError(StringRef("'") + SanCovLowestStackName +
"' should not be declared by the user"); "' should not be declared by the user");
@ -461,8 +456,6 @@ bool ModuleSanitizerCoverageAFL::instrumentModule(
SanCovLowestStack->setThreadLocalMode( SanCovLowestStack->setThreadLocalMode(
GlobalValue::ThreadLocalMode::InitialExecTLSModel); GlobalValue::ThreadLocalMode::InitialExecTLSModel);
if (Options.StackDepth && !SanCovLowestStack->isDeclaration())
SanCovLowestStack->setInitializer(Constant::getAllOnesValue(IntptrTy));
SanCovTracePC = M.getOrInsertFunction(SanCovTracePCName, VoidTy); SanCovTracePC = M.getOrInsertFunction(SanCovTracePCName, VoidTy);
SanCovTracePCGuard = SanCovTracePCGuard =
@ -477,40 +470,25 @@ bool ModuleSanitizerCoverageAFL::instrumentModule(
Ctor = CreateInitCallsForSections(M, SanCovModuleCtorTracePcGuardName, Ctor = CreateInitCallsForSections(M, SanCovModuleCtorTracePcGuardName,
SanCovTracePCGuardInitName, Int32PtrTy, SanCovTracePCGuardInitName, Int32PtrTy,
SanCovGuardsSectionName); SanCovGuardsSectionName);
if (Function8bitCounterArray)
Ctor = CreateInitCallsForSections(M, SanCovModuleCtor8bitCountersName,
SanCov8bitCountersInitName, Int8PtrTy,
SanCovCountersSectionName);
if (FunctionBoolArray) {
Ctor = CreateInitCallsForSections(M, SanCovModuleCtorBoolFlagName, if (Ctor && debug) {
SanCovBoolFlagInitName, Int1PtrTy,
SanCovBoolFlagSectionName); fprintf(stderr, "SANCOV: installed pcguard_init in ctor\n");
} }
if (Ctor && Options.PCTable) { appendToUsed(M, GlobalsToAppendToUsed);
auto SecStartEnd = CreateSecStartEnd(M, SanCovPCsSectionName, IntptrPtrTy);
FunctionCallee InitFunction = declareSanitizerInitFunction(
M, SanCovPCsInitName, {IntptrPtrTy, IntptrPtrTy});
IRBuilder<> IRBCtor(Ctor->getEntryBlock().getTerminator());
IRBCtor.CreateCall(InitFunction, {SecStartEnd.first, SecStartEnd.second});
}
// We don't reference these arrays directly in any of our runtime functions,
// so we need to prevent them from being dead stripped.
if (TargetTriple.isOSBinFormatMachO()) appendToUsed(M, GlobalsToAppendToUsed);
appendToCompilerUsed(M, GlobalsToAppendToCompilerUsed); appendToCompilerUsed(M, GlobalsToAppendToCompilerUsed);
if (!be_quiet) { if (!be_quiet) {
if (!instr) if (!instr) {
WARNF("No instrumentation targets found.");
else {
char modeline[100]; WARNF("No instrumentation targets found.");
} else {
char modeline[128];
snprintf(modeline, sizeof(modeline), "%s%s%s%s%s%s", snprintf(modeline, sizeof(modeline), "%s%s%s%s%s%s",
getenv("AFL_HARDEN") ? "hardened" : "non-hardened", getenv("AFL_HARDEN") ? "hardened" : "non-hardened",
getenv("AFL_USE_ASAN") ? ", ASAN" : "", getenv("AFL_USE_ASAN") ? ", ASAN" : "",
@ -531,39 +509,36 @@ bool ModuleSanitizerCoverageAFL::instrumentModule(
} }
// True if block has successors and it dominates all of them. // True if block has successors and it dominates all of them.
bool isFullDominator(const BasicBlock *BB, const DominatorTree *DT) { static bool isFullDominator(const BasicBlock *BB, const DominatorTree *DT) {
if (succ_begin(BB) == succ_end(BB)) return false; if (succ_empty(BB)) return false;
for (const BasicBlock *SUCC : make_range(succ_begin(BB), succ_end(BB))) { return llvm::all_of(successors(BB), [&](const BasicBlock *SUCC) {
if (!DT->dominates(BB, SUCC)) return false; return DT->dominates(BB, SUCC);
} });
return true;
} }
// True if block has predecessors and it postdominates all of them. // True if block has predecessors and it postdominates all of them.
bool isFullPostDominator(const BasicBlock *BB, const PostDominatorTree *PDT) { static bool isFullPostDominator(const BasicBlock *BB,
const PostDominatorTree *PDT) {
if (pred_begin(BB) == pred_end(BB)) return false; if (pred_empty(BB)) return false;
for (const BasicBlock *PRED : make_range(pred_begin(BB), pred_end(BB))) { return llvm::all_of(predecessors(BB), [&](const BasicBlock *PRED) {
if (!PDT->dominates(BB, PRED)) return false; return PDT->dominates(BB, PRED);
} });
return true;
} }
bool shouldInstrumentBlock(const Function &F, const BasicBlock *BB, static bool shouldInstrumentBlock(const Function &F, const BasicBlock *BB,
const DominatorTree *DT, const DominatorTree *DT,
const PostDominatorTree *PDT, const PostDominatorTree *PDT,
const SanitizerCoverageOptions &Options) { const SanitizerCoverageOptions &Options) {
// Don't insert coverage for blocks containing nothing but unreachable: we // Don't insert coverage for blocks containing nothing but unreachable: we
// will never call __sanitizer_cov() for them, so counting them in // will never call __sanitizer_cov() for them, so counting them in
@ -578,10 +553,6 @@ bool shouldInstrumentBlock(const Function &F, const BasicBlock *BB,
if (Options.NoPrune || &F.getEntryBlock() == BB) return true; if (Options.NoPrune || &F.getEntryBlock() == BB) return true;
if (Options.CoverageType == SanitizerCoverageOptions::SCK_Function &&
&F.getEntryBlock() != BB)
return false;
// Do not instrument full dominators, or full post-dominators with multiple // Do not instrument full dominators, or full post-dominators with multiple
// predecessors. // predecessors.
return !isFullDominator(BB, DT) && return !isFullDominator(BB, DT) &&
@ -593,38 +564,47 @@ bool shouldInstrumentBlock(const Function &F, const BasicBlock *BB,
// A twist here is that we treat From->To as a backedge if // A twist here is that we treat From->To as a backedge if
// * To dominates From or // * To dominates From or
// * To->UniqueSuccessor dominates From // * To->UniqueSuccessor dominates From
bool IsBackEdge(BasicBlock *From, BasicBlock *To, const DominatorTree *DT) { #if 0
static bool IsBackEdge(BasicBlock *From, BasicBlock *To,
const DominatorTree *DT) {
if (DT->dominates(To, From)) return true; if (DT->dominates(To, From))
return true;
if (auto Next = To->getUniqueSuccessor()) if (auto Next = To->getUniqueSuccessor())
if (DT->dominates(Next, From)) return true; if (DT->dominates(Next, From))
return true;
return false; return false;
} }
#endif
// Prunes uninteresting Cmp instrumentation: // Prunes uninteresting Cmp instrumentation:
// * CMP instructions that feed into loop backedge branch. // * CMP instructions that feed into loop backedge branch.
// //
// Note that Cmp pruning is controlled by the same flag as the // Note that Cmp pruning is controlled by the same flag as the
// BB pruning. // BB pruning.
bool IsInterestingCmp(ICmpInst *CMP, const DominatorTree *DT, #if 0
const SanitizerCoverageOptions &Options) { static bool IsInterestingCmp(ICmpInst *CMP, const DominatorTree *DT,
const SanitizerCoverageOptions &Options) {
if (!Options.NoPrune) if (!Options.NoPrune)
if (CMP->hasOneUse()) if (CMP->hasOneUse())
if (auto BR = dyn_cast<BranchInst>(CMP->user_back())) if (auto BR = dyn_cast<BranchInst>(CMP->user_back()))
for (BasicBlock *B : BR->successors()) for (BasicBlock *B : BR->successors())
if (IsBackEdge(BR->getParent(), B, DT)) return false; if (IsBackEdge(BR->getParent(), B, DT))
return false;
return true; return true;
} }
#endif
void ModuleSanitizerCoverageAFL::instrumentFunction( void ModuleSanitizerCoverageAFL::instrumentFunction(
Function &F, DomTreeCallback DTCallback, PostDomTreeCallback PDTCallback) { Function &F, DomTreeCallback DTCallback, PostDomTreeCallback PDTCallback) {
if (F.empty()) return; if (F.empty()) return;
if (!isInInstrumentList(&F, FMNAME)) return; if (!isInInstrumentList(&F, FMNAME)) return;
if (F.getName().find(".module_ctor") != std::string::npos) if (F.getName().find(".module_ctor") != std::string::npos)
return; // Should not instrument sanitizer init functions. return; // Should not instrument sanitizer init functions.
if (F.getName().startswith("__sanitizer_")) if (F.getName().startswith("__sanitizer_"))
@ -643,15 +623,13 @@ void ModuleSanitizerCoverageAFL::instrumentFunction(
if (F.hasPersonalityFn() && if (F.hasPersonalityFn() &&
isAsynchronousEHPersonality(classifyEHPersonality(F.getPersonalityFn()))) isAsynchronousEHPersonality(classifyEHPersonality(F.getPersonalityFn())))
return; return;
if (F.hasFnAttribute(Attribute::NoSanitizeCoverage)) return;
if (Options.CoverageType >= SanitizerCoverageOptions::SCK_Edge) if (Options.CoverageType >= SanitizerCoverageOptions::SCK_Edge)
SplitAllCriticalEdges( SplitAllCriticalEdges(
F, CriticalEdgeSplittingOptions().setIgnoreUnreachableDests()); F, CriticalEdgeSplittingOptions().setIgnoreUnreachableDests());
SmallVector<Instruction *, 8> IndirCalls; SmallVector<BasicBlock *, 16> BlocksToInstrument;
SmallVector<BasicBlock *, 16> BlocksToInstrument; SmallVector<Instruction *, 8> CmpTraceTargets;
SmallVector<Instruction *, 8> CmpTraceTargets; SmallVector<Instruction *, 8> SwitchTraceTargets;
SmallVector<Instruction *, 8> SwitchTraceTargets;
SmallVector<BinaryOperator *, 8> DivTraceTargets;
SmallVector<GetElementPtrInst *, 8> GepTraceTargets;
const DominatorTree *DT = DTCallback(F); const DominatorTree *DT = DTCallback(F);
const PostDominatorTree *PDT = PDTCallback(F); const PostDominatorTree *PDT = PDTCallback(F);
@ -661,47 +639,28 @@ void ModuleSanitizerCoverageAFL::instrumentFunction(
if (shouldInstrumentBlock(F, &BB, DT, PDT, Options)) if (shouldInstrumentBlock(F, &BB, DT, PDT, Options))
BlocksToInstrument.push_back(&BB); BlocksToInstrument.push_back(&BB);
for (auto &Inst : BB) { /*
for (auto &Inst : BB) {
if (Options.IndirectCalls) { if (Options.TraceCmp) {
CallBase *CB = dyn_cast<CallBase>(&Inst); if (ICmpInst *CMP = dyn_cast<ICmpInst>(&Inst))
if (CB && !CB->getCalledFunction()) IndirCalls.push_back(&Inst); if (IsInterestingCmp(CMP, DT, Options))
CmpTraceTargets.push_back(&Inst);
if (isa<SwitchInst>(&Inst))
SwitchTraceTargets.push_back(&Inst);
} }
if (Options.TraceCmp) { }
if (ICmpInst *CMP = dyn_cast<ICmpInst>(&Inst)) */
if (IsInterestingCmp(CMP, DT, Options))
CmpTraceTargets.push_back(&Inst);
if (isa<SwitchInst>(&Inst)) SwitchTraceTargets.push_back(&Inst);
}
if (Options.TraceDiv)
if (BinaryOperator *BO = dyn_cast<BinaryOperator>(&Inst))
if (BO->getOpcode() == Instruction::SDiv ||
BO->getOpcode() == Instruction::UDiv)
DivTraceTargets.push_back(BO);
if (Options.TraceGep)
if (GetElementPtrInst *GEP = dyn_cast<GetElementPtrInst>(&Inst))
GepTraceTargets.push_back(GEP);
if (Options.StackDepth)
if (isa<InvokeInst>(Inst) ||
(isa<CallInst>(Inst) && !isa<IntrinsicInst>(Inst)))
IsLeafFunc = false;
}
} }
InjectCoverage(F, BlocksToInstrument, IsLeafFunc); InjectCoverage(F, BlocksToInstrument, IsLeafFunc);
InjectCoverageForIndirectCalls(F, IndirCalls); // InjectTraceForCmp(F, CmpTraceTargets);
InjectTraceForCmp(F, CmpTraceTargets); // InjectTraceForSwitch(F, SwitchTraceTargets);
InjectTraceForSwitch(F, SwitchTraceTargets);
InjectTraceForDiv(F, DivTraceTargets);
InjectTraceForGep(F, GepTraceTargets);
} }
@ -710,36 +669,33 @@ GlobalVariable *ModuleSanitizerCoverageAFL::CreateFunctionLocalArrayInSection(
ArrayType *ArrayTy = ArrayType::get(Ty, NumElements); ArrayType *ArrayTy = ArrayType::get(Ty, NumElements);
auto Array = new GlobalVariable( auto Array = new GlobalVariable(
*CurModule, ArrayTy, false, GlobalVariable::PrivateLinkage, *CurModule, ArrayTy, false, GlobalVariable::PrivateLinkage,
Constant::getNullValue(ArrayTy), "__sancov_gen_"); Constant::getNullValue(ArrayTy), "__sancov_gen_");
#if LLVM_VERSION_MAJOR >= 13
if (TargetTriple.supportsCOMDAT() && if (TargetTriple.supportsCOMDAT() &&
(TargetTriple.isOSBinFormatELF() || !F.isInterposable())) (TargetTriple.isOSBinFormatELF() || !F.isInterposable()))
if (auto Comdat = getOrCreateFunctionComdat(F, TargetTriple)) if (auto Comdat = getOrCreateFunctionComdat(F, TargetTriple))
Array->setComdat(Comdat); Array->setComdat(Comdat);
Array->setSection(getSectionName(Section));
#if LLVM_VERSION_MAJOR >= 16
Array->setAlignment(Align(DL->getTypeStoreSize(Ty).getFixedValue()));
#else #else
if (TargetTriple.supportsCOMDAT() && !F.isInterposable()) Array->setAlignment(Align(DL->getTypeStoreSize(Ty).getFixedSize()));
if (auto Comdat =
GetOrCreateFunctionComdat(F, TargetTriple, CurModuleUniqueId))
Array->setComdat(Comdat);
#endif #endif
Array->setSection(getSectionName(Section)); // sancov_pcs parallels the other metadata section(s). Optimizers (e.g.
#if (LLVM_VERSION_MAJOR >= 11) || \ // GlobalOpt/ConstantMerge) may not discard sancov_pcs and the other
(LLVM_VERSION_MAJOR == 10 && LLVM_VERSION_MINOR >= 1) // section(s) as a unit, so we conservatively retain all unconditionally in
#if LLVM_VERSION_MAJOR >= 16 // the compiler.
Array->setAlignment(Align(DL->getTypeStoreSize(Ty).getFixedValue())); //
#else // With comdat (COFF/ELF), the linker can guarantee the associated sections
Array->setAlignment(Align(DL->getTypeStoreSize(Ty).getFixedSize())); // will be retained or discarded as a unit, so llvm.compiler.used is
#endif // sufficient. Otherwise, conservatively make all of them retained by the
#else // linker.
Array->setAlignment(Align(4)); // cheating if (Array->hasComdat())
#endif GlobalsToAppendToCompilerUsed.push_back(Array);
GlobalsToAppendToUsed.push_back(Array); else
GlobalsToAppendToCompilerUsed.push_back(Array); GlobalsToAppendToUsed.push_back(Array);
MDNode *MD = MDNode::get(F.getContext(), ValueAsMetadata::get(&F));
Array->addMetadata(LLVMContext::MD_associated, *MD);
return Array; return Array;
@ -764,8 +720,12 @@ GlobalVariable *ModuleSanitizerCoverageAFL::CreatePCArray(
PCs.push_back((Constant *)IRB.CreatePointerCast( PCs.push_back((Constant *)IRB.CreatePointerCast(
BlockAddress::get(AllBlocks[i]), IntptrPtrTy)); BlockAddress::get(AllBlocks[i]), IntptrPtrTy));
#if LLVM_VERSION_MAJOR >= 16
PCs.push_back(Constant::getNullValue(IntptrPtrTy));
#else
PCs.push_back((Constant *)IRB.CreateIntToPtr( PCs.push_back((Constant *)IRB.CreateIntToPtr(
ConstantInt::get(IntptrTy, 0), IntptrPtrTy)); ConstantInt::get(IntptrTy, 0), IntptrPtrTy));
#endif
} }
@ -788,21 +748,13 @@ void ModuleSanitizerCoverageAFL::CreateFunctionLocalArrays(
FunctionGuardArray = CreateFunctionLocalArrayInSection( FunctionGuardArray = CreateFunctionLocalArrayInSection(
AllBlocks.size() + special, F, Int32Ty, SanCovGuardsSectionName); AllBlocks.size() + special, F, Int32Ty, SanCovGuardsSectionName);
if (Options.Inline8bitCounters)
Function8bitCounterArray = CreateFunctionLocalArrayInSection(
AllBlocks.size(), F, Int8Ty, SanCovCountersSectionName);
/*
if (Options.InlineBoolFlag)
FunctionBoolArray = CreateFunctionLocalArrayInSection(
AllBlocks.size(), F, Int1Ty, SanCovBoolFlagSectionName);
*/
if (Options.PCTable) FunctionPCsArray = CreatePCArray(F, AllBlocks);
} }
bool ModuleSanitizerCoverageAFL::InjectCoverage( bool ModuleSanitizerCoverageAFL::InjectCoverage(
Function &F, ArrayRef<BasicBlock *> AllBlocks, bool IsLeafFunc) { Function &F, ArrayRef<BasicBlock *> AllBlocks, bool IsLeafFunc) {
if (AllBlocks.empty()) return false;
uint32_t cnt_cov = 0, cnt_sel = 0, cnt_sel_inc = 0; uint32_t cnt_cov = 0, cnt_sel = 0, cnt_sel_inc = 0;
static uint32_t first = 1; static uint32_t first = 1;
@ -851,7 +803,6 @@ bool ModuleSanitizerCoverageAFL::InjectCoverage(
} }
#if (LLVM_VERSION_MAJOR >= 12)
else if (t->getTypeID() == llvm::Type::FixedVectorTyID) { else if (t->getTypeID() == llvm::Type::FixedVectorTyID) {
FixedVectorType *tt = dyn_cast<FixedVectorType>(t); FixedVectorType *tt = dyn_cast<FixedVectorType>(t);
@ -864,16 +815,14 @@ bool ModuleSanitizerCoverageAFL::InjectCoverage(
} }
#endif
} }
} }
} }
/* Create PCGUARD array */
CreateFunctionLocalArrays(F, AllBlocks, first + cnt_cov + cnt_sel_inc); CreateFunctionLocalArrays(F, AllBlocks, first + cnt_cov + cnt_sel_inc);
if (first) { first = 0; } if (first) { first = 0; }
selects += cnt_sel; selects += cnt_sel;
@ -885,12 +834,6 @@ bool ModuleSanitizerCoverageAFL::InjectCoverage(
CallInst *callInst = nullptr; CallInst *callInst = nullptr;
/*
std::string errMsg;
raw_string_ostream os(errMsg);
IN.print(os);
fprintf(stderr, "X: %s\n", os.str().c_str());
*/
if ((callInst = dyn_cast<CallInst>(&IN))) { if ((callInst = dyn_cast<CallInst>(&IN))) {
Function *Callee = callInst->getCalledFunction(); Function *Callee = callInst->getCalledFunction();
@ -1029,12 +972,6 @@ bool ModuleSanitizerCoverageAFL::InjectCoverage(
} }
/*
std::string errMsg;
raw_string_ostream os(errMsg);
x->print(os);
fprintf(stderr, "X: %s\n", os.str().c_str());
*/
result = IRB.CreateSelect(condition, x, y); result = IRB.CreateSelect(condition, x, y);
} }
@ -1059,13 +996,6 @@ bool ModuleSanitizerCoverageAFL::InjectCoverage(
IRB.CreateLoad(PointerType::get(Int8Ty, 0), AFLMapPtr); IRB.CreateLoad(PointerType::get(Int8Ty, 0), AFLMapPtr);
ModuleSanitizerCoverageAFL::SetNoSanitizeMetadata(MapPtr); ModuleSanitizerCoverageAFL::SetNoSanitizeMetadata(MapPtr);
/*
std::string errMsg;
raw_string_ostream os(errMsg);
result->print(os);
fprintf(stderr, "X: %s\n", os.str().c_str());
*/
while (1) { while (1) {
/* Get CurLoc */ /* Get CurLoc */
@ -1155,29 +1085,6 @@ bool ModuleSanitizerCoverageAFL::InjectCoverage(
} }
// On every indirect call we call a run-time function
// __sanitizer_cov_indir_call* with two parameters:
// - callee address,
// - global cache array that contains CacheSize pointers (zero-initialized).
// The cache is used to speed up recording the caller-callee pairs.
// The address of the caller is passed implicitly via caller PC.
// CacheSize is encoded in the name of the run-time function.
void ModuleSanitizerCoverageAFL::InjectCoverageForIndirectCalls(
Function &F, ArrayRef<Instruction *> IndirCalls) {
if (IndirCalls.empty()) return;
for (auto I : IndirCalls) {
IRBuilder<> IRB(I);
CallBase &CB = cast<CallBase>(*I);
Value *Callee = CB.getCalledOperand();
if (isa<InlineAsm>(Callee)) continue;
IRB.CreateCall(SanCovTracePCIndir, IRB.CreatePointerCast(Callee, IntptrTy));
}
}
// For every switch statement we insert a call: // For every switch statement we insert a call:
// __sanitizer_cov_trace_switch(CondValue, // __sanitizer_cov_trace_switch(CondValue,
// {NumCases, ValueSizeInBits, Case0Value, Case1Value, Case2Value, ... }) // {NumCases, ValueSizeInBits, Case0Value, Case1Value, Case2Value, ... })
@ -1233,41 +1140,6 @@ void ModuleSanitizerCoverageAFL::InjectTraceForSwitch(
} }
void ModuleSanitizerCoverageAFL::InjectTraceForDiv(
Function &, ArrayRef<BinaryOperator *> DivTraceTargets) {
for (auto BO : DivTraceTargets) {
IRBuilder<> IRB(BO);
Value *A1 = BO->getOperand(1);
if (isa<ConstantInt>(A1)) continue;
if (!A1->getType()->isIntegerTy()) continue;
uint64_t TypeSize = DL->getTypeStoreSizeInBits(A1->getType());
int CallbackIdx = TypeSize == 32 ? 0 : TypeSize == 64 ? 1 : -1;
if (CallbackIdx < 0) continue;
auto Ty = Type::getIntNTy(*C, TypeSize);
IRB.CreateCall(SanCovTraceDivFunction[CallbackIdx],
{IRB.CreateIntCast(A1, Ty, true)});
}
}
void ModuleSanitizerCoverageAFL::InjectTraceForGep(
Function &, ArrayRef<GetElementPtrInst *> GepTraceTargets) {
for (auto GEP : GepTraceTargets) {
IRBuilder<> IRB(GEP);
for (Use &Idx : GEP->indices())
if (!isa<ConstantInt>(Idx) && Idx->getType()->isIntegerTy())
IRB.CreateCall(SanCovTraceGepFunction,
{IRB.CreateIntCast(Idx, IntptrTy, true)});
}
}
void ModuleSanitizerCoverageAFL::InjectTraceForCmp( void ModuleSanitizerCoverageAFL::InjectTraceForCmp(
Function &, ArrayRef<Instruction *> CmpTraceTargets) { Function &, ArrayRef<Instruction *> CmpTraceTargets) {
@ -1317,27 +1189,44 @@ void ModuleSanitizerCoverageAFL::InjectCoverageAtBlock(Function &F,
BasicBlock::iterator IP = BB.getFirstInsertionPt(); BasicBlock::iterator IP = BB.getFirstInsertionPt();
bool IsEntryBB = &BB == &F.getEntryBlock(); bool IsEntryBB = &BB == &F.getEntryBlock();
DebugLoc EntryLoc;
if (IsEntryBB) { if (IsEntryBB) {
// Keep allocas and llvm.localescape calls in the entry block. Even if (auto SP = F.getSubprogram())
EntryLoc = DILocation::get(SP->getContext(), SP->getScopeLine(), 0, SP);
// Keep static allocas and llvm.localescape calls in the entry block. Even
// if we aren't splitting the block, it's nice for allocas to be before // if we aren't splitting the block, it's nice for allocas to be before
// calls. // calls.
IP = PrepareToSplitEntryBlock(BB, IP); IP = PrepareToSplitEntryBlock(BB, IP);
#if LLVM_VERSION_MAJOR < 15
} else {
EntryLoc = IP->getDebugLoc();
if (!EntryLoc)
if (auto *SP = F.getSubprogram())
EntryLoc = DILocation::get(SP->getContext(), 0, 0, SP);
#endif
} }
#if LLVM_VERSION_MAJOR >= 16
InstrumentationIRBuilder IRB(&*IP);
#else
IRBuilder<> IRB(&*IP); IRBuilder<> IRB(&*IP);
#endif
if (Options.TracePC) { if (EntryLoc) IRB.SetCurrentDebugLocation(EntryLoc);
IRB.CreateCall(SanCovTracePC);
// ->setCannotMerge(); // gets the PC using GET_CALLER_PC.
}
if (Options.TracePCGuard) { if (Options.TracePCGuard) {
/*
auto GuardPtr = IRB.CreateIntToPtr(
IRB.CreateAdd(IRB.CreatePointerCast(FunctionGuardArray, IntptrTy),
ConstantInt::get(IntptrTy, Idx * 4)),
Int32PtrTy);
IRB.CreateCall(SanCovTracePCGuard, GuardPtr)->setCannotMerge();
*/
/* Get CurLoc */ /* Get CurLoc */
Value *GuardPtr = IRB.CreateIntToPtr( Value *GuardPtr = IRB.CreateIntToPtr(
@ -1395,57 +1284,6 @@ void ModuleSanitizerCoverageAFL::InjectCoverageAtBlock(Function &F,
} }
if (Options.Inline8bitCounters) {
auto CounterPtr = IRB.CreateGEP(
Function8bitCounterArray->getValueType(), Function8bitCounterArray,
{ConstantInt::get(IntptrTy, 0), ConstantInt::get(IntptrTy, Idx)});
auto Load = IRB.CreateLoad(Int8Ty, CounterPtr);
auto Inc = IRB.CreateAdd(Load, ConstantInt::get(Int8Ty, 1));
auto Store = IRB.CreateStore(Inc, CounterPtr);
SetNoSanitizeMetadata(Load);
SetNoSanitizeMetadata(Store);
}
/*
if (Options.InlineBoolFlag) {
auto FlagPtr = IRB.CreateGEP(
FunctionBoolArray->getValueType(), FunctionBoolArray,
{ConstantInt::get(IntptrTy, 0), ConstantInt::get(IntptrTy, Idx)});
auto Load = IRB.CreateLoad(Int1Ty, FlagPtr);
auto ThenTerm =
SplitBlockAndInsertIfThen(IRB.CreateIsNull(Load), &*IP, false);
IRBuilder<> ThenIRB(ThenTerm);
auto Store = ThenIRB.CreateStore(ConstantInt::getTrue(Int1Ty), FlagPtr);
SetNoSanitizeMetadata(Load);
SetNoSanitizeMetadata(Store);
}
*/
if (Options.StackDepth && IsEntryBB && !IsLeafFunc) {
// Check stack depth. If it's the deepest so far, record it.
Module *M = F.getParent();
Function *GetFrameAddr = Intrinsic::getDeclaration(
M, Intrinsic::frameaddress,
IRB.getInt8PtrTy(M->getDataLayout().getAllocaAddrSpace()));
auto FrameAddrPtr =
IRB.CreateCall(GetFrameAddr, {Constant::getNullValue(Int32Ty)});
auto FrameAddrInt = IRB.CreatePtrToInt(FrameAddrPtr, IntptrTy);
auto LowestStack = IRB.CreateLoad(IntptrTy, SanCovLowestStack);
auto IsStackLower = IRB.CreateICmpULT(FrameAddrInt, LowestStack);
auto ThenTerm = SplitBlockAndInsertIfThen(IsStackLower, &*IP, false);
IRBuilder<> ThenIRB(ThenTerm);
auto Store = ThenIRB.CreateStore(FrameAddrInt, SanCovLowestStack);
SetNoSanitizeMetadata(LowestStack);
SetNoSanitizeMetadata(Store);
}
} }
std::string ModuleSanitizerCoverageAFL::getSectionName( std::string ModuleSanitizerCoverageAFL::getSectionName(

View File

@ -14,6 +14,16 @@
*/ */
#ifdef __AFL_CODE_COVERAGE
#ifndef _GNU_SOURCE
#define _GNU_SOURCE
#endif
#ifndef __USE_GNU
#define __USE_GNU
#endif
#include <dlfcn.h>
#endif
#ifdef __ANDROID__ #ifdef __ANDROID__
#include "android-ashmem.h" #include "android-ashmem.h"
#endif #endif
@ -105,6 +115,44 @@ u32 __afl_dictionary_len;
u64 __afl_map_addr; u64 __afl_map_addr;
u32 __afl_first_final_loc; u32 __afl_first_final_loc;
#ifdef __AFL_CODE_COVERAGE
typedef struct afl_module_info_t afl_module_info_t;
struct afl_module_info_t {
// A unique id starting with 0
u32 id;
// Name and base address of the module
char *name;
uintptr_t base_address;
// PC Guard start/stop
u32 start;
u32 stop;
// PC Table begin/end
const uintptr_t *pcs_beg;
const uintptr_t *pcs_end;
u8 mapped;
afl_module_info_t *next;
};
typedef struct {
uintptr_t PC, PCFlags;
} PCTableEntry;
afl_module_info_t *__afl_module_info = NULL;
u32 __afl_pcmap_size = 0;
uintptr_t *__afl_pcmap_ptr = NULL;
#endif // __AFL_CODE_COVERAGE
/* 1 if we are running in afl, and the forkserver was started, else 0 */ /* 1 if we are running in afl, and the forkserver was started, else 0 */
u32 __afl_connected = 0; u32 __afl_connected = 0;
@ -113,7 +161,7 @@ int __afl_selective_coverage __attribute__((weak));
int __afl_selective_coverage_start_off __attribute__((weak)); int __afl_selective_coverage_start_off __attribute__((weak));
static int __afl_selective_coverage_temp = 1; static int __afl_selective_coverage_temp = 1;
#if defined(__ANDROID__) || defined(__HAIKU__) #if defined(__ANDROID__) || defined(__HAIKU__) || defined(NO_TLS)
PREV_LOC_T __afl_prev_loc[NGRAM_SIZE_MAX]; PREV_LOC_T __afl_prev_loc[NGRAM_SIZE_MAX];
PREV_LOC_T __afl_prev_caller[CTX_MAX_K]; PREV_LOC_T __afl_prev_caller[CTX_MAX_K];
u32 __afl_prev_ctx; u32 __afl_prev_ctx;
@ -496,11 +544,12 @@ static void __afl_map_shm(void) {
if (__afl_map_size && __afl_map_size > MAP_SIZE) { if (__afl_map_size && __afl_map_size > MAP_SIZE) {
u8 *map_env = (u8 *)getenv("AFL_MAP_SIZE"); u8 *map_env = (u8 *)getenv("AFL_MAP_SIZE");
if (!map_env || atoi((char *)map_env) < MAP_SIZE) { if (!map_env || atoi((char *)map_env) < MAP_SIZE) {
send_forkserver_error(FS_ERROR_MAP_SIZE); fprintf(stderr, "FS_ERROR_MAP_SIZE\n");
_exit(1); send_forkserver_error(FS_ERROR_MAP_SIZE);
_exit(1);
} }
@ -512,13 +561,13 @@ static void __afl_map_shm(void) {
if (!__afl_area_ptr || __afl_area_ptr == (void *)-1) { if (!__afl_area_ptr || __afl_area_ptr == (void *)-1) {
if (__afl_map_addr) if (__afl_map_addr)
send_forkserver_error(FS_ERROR_MAP_ADDR); send_forkserver_error(FS_ERROR_MAP_ADDR);
else else
send_forkserver_error(FS_ERROR_SHMAT); send_forkserver_error(FS_ERROR_SHMAT);
perror("shmat for map"); perror("shmat for map");
_exit(1); _exit(1);
} }
@ -678,6 +727,27 @@ static void __afl_map_shm(void) {
} }
#ifdef __AFL_CODE_COVERAGE
char *pcmap_id_str = getenv("__AFL_PCMAP_SHM_ID");
if (pcmap_id_str) {
__afl_pcmap_size = __afl_map_size * sizeof(void *);
u32 shm_id = atoi(pcmap_id_str);
__afl_pcmap_ptr = (uintptr_t *)shmat(shm_id, NULL, 0);
if (__afl_debug) {
fprintf(stderr, "DEBUG: Received %p via shmat for pcmap\n",
__afl_pcmap_ptr);
}
}
#endif // __AFL_CODE_COVERAGE
} }
/* unmap SHM. */ /* unmap SHM. */
@ -686,6 +756,17 @@ static void __afl_unmap_shm(void) {
if (!__afl_already_initialized_shm) return; if (!__afl_already_initialized_shm) return;
#ifdef __AFL_CODE_COVERAGE
if (__afl_pcmap_size) {
shmdt((void *)__afl_pcmap_ptr);
__afl_pcmap_ptr = NULL;
__afl_pcmap_size = 0;
}
#endif // __AFL_CODE_COVERAGE
char *id_str = getenv(SHM_ENV_VAR); char *id_str = getenv(SHM_ENV_VAR);
if (id_str) { if (id_str) {
@ -1507,6 +1588,102 @@ void __sanitizer_cov_trace_pc_guard(uint32_t *guard) {
} }
#ifdef __AFL_CODE_COVERAGE
void __sanitizer_cov_pcs_init(const uintptr_t *pcs_beg,
const uintptr_t *pcs_end) {
if (__afl_debug) {
fprintf(stderr, "DEBUG: __sanitizer_cov_pcs_init called\n");
}
// If for whatever reason, we cannot get dlinfo here, then pc_guard_init also
// couldn't get it and we'd end up attributing to the wrong module.
Dl_info dlinfo;
if (!dladdr(__builtin_return_address(0), &dlinfo)) {
fprintf(stderr,
"WARNING: Ignoring __sanitizer_cov_pcs_init callback due to "
"missing module info\n");
return;
}
afl_module_info_t *last_module_info = __afl_module_info;
while (last_module_info && last_module_info->next) {
last_module_info = last_module_info->next;
}
if (!last_module_info) {
fprintf(stderr,
"ERROR: __sanitizer_cov_pcs_init called with no module info?!\n");
abort();
}
last_module_info->pcs_beg = pcs_beg;
last_module_info->pcs_end = pcs_end;
// Now update the pcmap. If this is the last module coming in, after all
// pre-loaded code, then this will also map all of our delayed previous
// modules.
if (!__afl_pcmap_ptr) { return; }
for (afl_module_info_t *mod_info = __afl_module_info; mod_info;
mod_info = mod_info->next) {
if (mod_info->mapped) { continue; }
PCTableEntry *start = (PCTableEntry *)(mod_info->pcs_beg);
PCTableEntry *end = (PCTableEntry *)(mod_info->pcs_end);
u32 in_module_index = 0;
while (start < end) {
if (mod_info->start + in_module_index >= __afl_map_size) {
fprintf(stderr, "ERROR: __sanitizer_cov_pcs_init out of bounds?!\n");
abort();
}
uintptr_t PC = start->PC;
// This is what `GetPreviousInstructionPc` in sanitizer runtime does
// for x86/x86-64. Needs more work for ARM and other archs.
PC = PC - 1;
// Calculate relative offset in module
PC = PC - mod_info->base_address;
__afl_pcmap_ptr[mod_info->start + in_module_index] = PC;
start++;
in_module_index++;
}
mod_info->mapped = 1;
if (__afl_debug) {
fprintf(stderr, "DEBUG: __sanitizer_cov_pcs_init initialized %u PCs\n",
in_module_index);
}
}
}
#endif // __AFL_CODE_COVERAGE
/* Init callback. Populates instrumentation IDs. Note that we're using /* Init callback. Populates instrumentation IDs. Note that we're using
ID of 0 as a special value to indicate non-instrumented bits. That may ID of 0 as a special value to indicate non-instrumented bits. That may
still touch the bitmap, but in a fairly harmless way. */ still touch the bitmap, but in a fairly harmless way. */
@ -1536,7 +1713,63 @@ void __sanitizer_cov_trace_pc_guard_init(uint32_t *start, uint32_t *stop) {
} }
if (start == stop || *start) return; if (start == stop || *start) { return; }
#ifdef __AFL_CODE_COVERAGE
u32 *orig_start = start;
afl_module_info_t *mod_info = NULL;
Dl_info dlinfo;
if (dladdr(__builtin_return_address(0), &dlinfo)) {
if (__afl_already_initialized_forkserver) {
fprintf(stderr, "[pcmap] Error: Module was not preloaded: %s\n",
dlinfo.dli_fname);
} else {
afl_module_info_t *last_module_info = __afl_module_info;
while (last_module_info && last_module_info->next) {
last_module_info = last_module_info->next;
}
mod_info = malloc(sizeof(afl_module_info_t));
mod_info->id = last_module_info ? last_module_info->id + 1 : 0;
mod_info->name = strdup(dlinfo.dli_fname);
mod_info->base_address = (uintptr_t)dlinfo.dli_fbase;
mod_info->start = 0;
mod_info->stop = 0;
mod_info->pcs_beg = NULL;
mod_info->pcs_end = NULL;
mod_info->mapped = 0;
mod_info->next = NULL;
if (last_module_info) {
last_module_info->next = mod_info;
} else {
__afl_module_info = mod_info;
}
fprintf(stderr, "[pcmap] Module: %s Base Address: %p\n", dlinfo.dli_fname,
dlinfo.dli_fbase);
}
} else {
fprintf(stderr, "[pcmap] dladdr call failed\n");
}
#endif // __AFL_CODE_COVERAGE
x = getenv("AFL_INST_RATIO"); x = getenv("AFL_INST_RATIO");
if (x) { if (x) {
@ -1563,16 +1796,27 @@ void __sanitizer_cov_trace_pc_guard_init(uint32_t *start, uint32_t *stop) {
"[-] FATAL: forkserver is already up, but an instrumented dlopen() " "[-] FATAL: forkserver is already up, but an instrumented dlopen() "
"library loaded afterwards. You must AFL_PRELOAD such libraries to " "library loaded afterwards. You must AFL_PRELOAD such libraries to "
"be able to fuzz them or LD_PRELOAD to run outside of afl-fuzz.\n" "be able to fuzz them or LD_PRELOAD to run outside of afl-fuzz.\n"
"To ignore this set AFL_IGNORE_PROBLEMS=1.\n"); "To ignore this set AFL_IGNORE_PROBLEMS=1 but this will lead to "
"ambiguous coverage data.\n"
"In addition, you can set AFL_IGNORE_PROBLEMS_COVERAGE=1 to "
"ignore the additional coverage instead (use with caution!).\n");
abort(); abort();
} else { } else {
static u32 offset = 4; u8 ignore_dso_after_fs = !!getenv("AFL_IGNORE_PROBLEMS_COVERAGE");
if (__afl_debug && ignore_dso_after_fs) {
fprintf(stderr, "Ignoring coverage from dynamically loaded code\n");
}
static u32 offset = 5;
while (start < stop) { while (start < stop) {
if (likely(inst_ratio == 100) || R(100) < inst_ratio) { if (!ignore_dso_after_fs &&
(likely(inst_ratio == 100) || R(100) < inst_ratio)) {
*(start++) = offset; *(start++) = offset;
@ -1582,7 +1826,7 @@ void __sanitizer_cov_trace_pc_guard_init(uint32_t *start, uint32_t *stop) {
} }
if (unlikely(++offset >= __afl_final_loc)) { offset = 4; } if (unlikely(++offset >= __afl_final_loc)) { offset = 5; }
} }
@ -1596,7 +1840,7 @@ void __sanitizer_cov_trace_pc_guard_init(uint32_t *start, uint32_t *stop) {
to avoid duplicate calls (which can happen as an artifact of the underlying to avoid duplicate calls (which can happen as an artifact of the underlying
implementation in LLVM). */ implementation in LLVM). */
if (__afl_final_loc < 3) __afl_final_loc = 3; // we skip the first 4 entries if (__afl_final_loc < 5) __afl_final_loc = 5; // we skip the first 5 entries
*(start++) = ++__afl_final_loc; *(start++) = ++__afl_final_loc;
@ -1614,6 +1858,22 @@ void __sanitizer_cov_trace_pc_guard_init(uint32_t *start, uint32_t *stop) {
} }
#ifdef __AFL_CODE_COVERAGE
if (mod_info) {
mod_info->start = *orig_start;
mod_info->stop = *(stop - 1);
if (__afl_debug) {
fprintf(stderr, "DEBUG: [pcmap] Start Index: %u Stop Index: %u\n",
mod_info->start, mod_info->stop);
}
}
#endif // __AFL_CODE_COVERAGE
if (__afl_debug) { if (__afl_debug) {
fprintf(stderr, fprintf(stderr,

View File

@ -289,6 +289,7 @@ void scanForDangerousFunctions(llvm::Module *M) {
StringRef ifunc_name = IF.getName(); StringRef ifunc_name = IF.getName();
Constant *r = IF.getResolver(); Constant *r = IF.getResolver();
if (r->getNumOperands() == 0) { continue; }
StringRef r_name = cast<Function>(r->getOperand(0))->getName(); StringRef r_name = cast<Function>(r->getOperand(0))->getName();
if (!be_quiet) if (!be_quiet)
fprintf(stderr, fprintf(stderr,
@ -583,7 +584,7 @@ bool isInInstrumentList(llvm::Function *F, std::string Filename) {
} }
// Calculate the number of average collisions that would occur if all // Calculate the number of average collisions that would occur if all
// location IDs would be assigned randomly (like normal afl/afl++). // location IDs would be assigned randomly (like normal afl/AFL++).
// This uses the "balls in bins" algorithm. // This uses the "balls in bins" algorithm.
unsigned long long int calculateCollisions(uint32_t edges) { unsigned long long int calculateCollisions(uint32_t edges) {

View File

@ -22,7 +22,9 @@ typedef long double max_align_t;
#include "llvm/IR/Module.h" #include "llvm/IR/Module.h"
#include "llvm/Support/Debug.h" #include "llvm/Support/Debug.h"
#include "llvm/Support/MathExtras.h" #include "llvm/Support/MathExtras.h"
#include "llvm/Transforms/IPO/PassManagerBuilder.h" #if LLVM_VERSION_MAJOR < 17
#include "llvm/Transforms/IPO/PassManagerBuilder.h"
#endif
#if LLVM_VERSION_MAJOR > 3 || \ #if LLVM_VERSION_MAJOR > 3 || \
(LLVM_VERSION_MAJOR == 3 && LLVM_VERSION_MINOR > 4) (LLVM_VERSION_MAJOR == 3 && LLVM_VERSION_MINOR > 4)

View File

@ -53,7 +53,9 @@
#include "llvm/IR/Verifier.h" #include "llvm/IR/Verifier.h"
#include "llvm/Support/Debug.h" #include "llvm/Support/Debug.h"
#include "llvm/Support/raw_ostream.h" #include "llvm/Support/raw_ostream.h"
#include "llvm/Transforms/IPO/PassManagerBuilder.h" #if LLVM_VERSION_MAJOR < 17
#include "llvm/Transforms/IPO/PassManagerBuilder.h"
#endif
#include "llvm/Transforms/Utils/BasicBlockUtils.h" #include "llvm/Transforms/Utils/BasicBlockUtils.h"
#include "llvm/Analysis/LoopInfo.h" #include "llvm/Analysis/LoopInfo.h"
#include "llvm/Analysis/ValueTracking.h" #include "llvm/Analysis/ValueTracking.h"
@ -744,7 +746,7 @@ static void registerAFLdict2filePass(const PassManagerBuilder &,
} }
static RegisterPass<AFLdict2filePass> X("afl-dict2file", static RegisterPass<AFLdict2filePass> X("afl-dict2file",
"afl++ dict2file instrumentation pass", "AFL++ dict2file instrumentation pass",
false, false); false, false);
static RegisterStandardPasses RegisterAFLdict2filePass( static RegisterStandardPasses RegisterAFLdict2filePass(

View File

@ -45,7 +45,7 @@
#include "llvm/IR/Module.h" #include "llvm/IR/Module.h"
#include "llvm/Pass.h" #include "llvm/Pass.h"
#include "llvm/Support/Debug.h" #include "llvm/Support/Debug.h"
//#include "llvm/Transforms/IPO/PassManagerBuilder.h" // #include "llvm/Transforms/IPO/PassManagerBuilder.h"
#include "llvm/Passes/PassPlugin.h" #include "llvm/Passes/PassPlugin.h"
#include "llvm/Passes/PassBuilder.h" #include "llvm/Passes/PassBuilder.h"
#include "llvm/IR/PassManager.h" #include "llvm/IR/PassManager.h"

View File

@ -413,7 +413,7 @@ bool AFLCoverage::runOnModule(Module &M) {
GlobalVariable *AFLContext = NULL; GlobalVariable *AFLContext = NULL;
if (ctx_str || caller_str) if (ctx_str || caller_str)
#if defined(__ANDROID__) || defined(__HAIKU__) #if defined(__ANDROID__) || defined(__HAIKU__) || defined(NO_TLS)
AFLContext = new GlobalVariable( AFLContext = new GlobalVariable(
M, Int32Ty, false, GlobalValue::ExternalLinkage, 0, "__afl_prev_ctx"); M, Int32Ty, false, GlobalValue::ExternalLinkage, 0, "__afl_prev_ctx");
#else #else
@ -424,7 +424,7 @@ bool AFLCoverage::runOnModule(Module &M) {
#ifdef AFL_HAVE_VECTOR_INTRINSICS #ifdef AFL_HAVE_VECTOR_INTRINSICS
if (ngram_size) if (ngram_size)
#if defined(__ANDROID__) || defined(__HAIKU__) #if defined(__ANDROID__) || defined(__HAIKU__) || defined(NO_TLS)
AFLPrevLoc = new GlobalVariable( AFLPrevLoc = new GlobalVariable(
M, PrevLocTy, /* isConstant */ false, GlobalValue::ExternalLinkage, M, PrevLocTy, /* isConstant */ false, GlobalValue::ExternalLinkage,
/* Initializer */ nullptr, "__afl_prev_loc"); /* Initializer */ nullptr, "__afl_prev_loc");
@ -437,7 +437,7 @@ bool AFLCoverage::runOnModule(Module &M) {
#endif #endif
else else
#endif #endif
#if defined(__ANDROID__) || defined(__HAIKU__) #if defined(__ANDROID__) || defined(__HAIKU__) || defined(NO_TLS)
AFLPrevLoc = new GlobalVariable( AFLPrevLoc = new GlobalVariable(
M, Int32Ty, false, GlobalValue::ExternalLinkage, 0, "__afl_prev_loc"); M, Int32Ty, false, GlobalValue::ExternalLinkage, 0, "__afl_prev_loc");
#else #else
@ -448,7 +448,7 @@ bool AFLCoverage::runOnModule(Module &M) {
#ifdef AFL_HAVE_VECTOR_INTRINSICS #ifdef AFL_HAVE_VECTOR_INTRINSICS
if (ctx_k) if (ctx_k)
#if defined(__ANDROID__) || defined(__HAIKU__) #if defined(__ANDROID__) || defined(__HAIKU__) || defined(NO_TLS)
AFLPrevCaller = new GlobalVariable( AFLPrevCaller = new GlobalVariable(
M, PrevCallerTy, /* isConstant */ false, GlobalValue::ExternalLinkage, M, PrevCallerTy, /* isConstant */ false, GlobalValue::ExternalLinkage,
/* Initializer */ nullptr, "__afl_prev_caller"); /* Initializer */ nullptr, "__afl_prev_caller");
@ -461,7 +461,7 @@ bool AFLCoverage::runOnModule(Module &M) {
#endif #endif
else else
#endif #endif
#if defined(__ANDROID__) || defined(__HAIKU__) #if defined(__ANDROID__) || defined(__HAIKU__) || defined(NO_TLS)
AFLPrevCaller = AFLPrevCaller =
new GlobalVariable(M, Int32Ty, false, GlobalValue::ExternalLinkage, 0, new GlobalVariable(M, Int32Ty, false, GlobalValue::ExternalLinkage, 0,
"__afl_prev_caller"); "__afl_prev_caller");

View File

@ -38,7 +38,9 @@
#include "llvm/IR/Module.h" #include "llvm/IR/Module.h"
#include "llvm/Support/Debug.h" #include "llvm/Support/Debug.h"
#include "llvm/Support/raw_ostream.h" #include "llvm/Support/raw_ostream.h"
#include "llvm/Transforms/IPO/PassManagerBuilder.h" #if LLVM_VERSION_MAJOR < 17
#include "llvm/Transforms/IPO/PassManagerBuilder.h"
#endif
#include "llvm/Transforms/Utils/BasicBlockUtils.h" #include "llvm/Transforms/Utils/BasicBlockUtils.h"
#include "llvm/Pass.h" #include "llvm/Pass.h"
#include "llvm/Analysis/ValueTracking.h" #include "llvm/Analysis/ValueTracking.h"
@ -540,7 +542,7 @@ bool CmpLogRoutines::hookRtns(Module &M) {
Value *v1Pcasted = IRB.CreatePointerCast(v1P, i8PtrTy); Value *v1Pcasted = IRB.CreatePointerCast(v1P, i8PtrTy);
Value *v2Pcasted = IRB.CreatePointerCast(v2P, i8PtrTy); Value *v2Pcasted = IRB.CreatePointerCast(v2P, i8PtrTy);
Value *v3Pbitcast = IRB.CreateBitCast( Value *v3Pbitcast = IRB.CreateBitCast(
v3P, IntegerType::get(C, v3P->getType()->getPrimitiveSizeInBits())); v3P, IntegerType::get(C, v3P->getType()->getPrimitiveSizeInBits()));
Value *v3Pcasted = Value *v3Pcasted =
IRB.CreateIntCast(v3Pbitcast, IntegerType::get(C, 64), false); IRB.CreateIntCast(v3Pbitcast, IntegerType::get(C, 64), false);
args.push_back(v1Pcasted); args.push_back(v1Pcasted);
@ -606,7 +608,7 @@ bool CmpLogRoutines::hookRtns(Module &M) {
Value *v1Pcasted = IRB.CreatePointerCast(v1P, i8PtrTy); Value *v1Pcasted = IRB.CreatePointerCast(v1P, i8PtrTy);
Value *v2Pcasted = IRB.CreatePointerCast(v2P, i8PtrTy); Value *v2Pcasted = IRB.CreatePointerCast(v2P, i8PtrTy);
Value *v3Pbitcast = IRB.CreateBitCast( Value *v3Pbitcast = IRB.CreateBitCast(
v3P, IntegerType::get(C, v3P->getType()->getPrimitiveSizeInBits())); v3P, IntegerType::get(C, v3P->getType()->getPrimitiveSizeInBits()));
Value *v3Pcasted = Value *v3Pcasted =
IRB.CreateIntCast(v3Pbitcast, IntegerType::get(C, 64), false); IRB.CreateIntCast(v3Pbitcast, IntegerType::get(C, 64), false);
args.push_back(v1Pcasted); args.push_back(v1Pcasted);

View File

@ -39,7 +39,9 @@
#include "llvm/IR/Module.h" #include "llvm/IR/Module.h"
#include "llvm/Support/Debug.h" #include "llvm/Support/Debug.h"
#include "llvm/Support/raw_ostream.h" #include "llvm/Support/raw_ostream.h"
#include "llvm/Transforms/IPO/PassManagerBuilder.h" #if LLVM_VERSION_MAJOR < 17
#include "llvm/Transforms/IPO/PassManagerBuilder.h"
#endif
#include "llvm/Transforms/Utils/BasicBlockUtils.h" #include "llvm/Transforms/Utils/BasicBlockUtils.h"
#include "llvm/Pass.h" #include "llvm/Pass.h"
#include "llvm/Analysis/ValueTracking.h" #include "llvm/Analysis/ValueTracking.h"

View File

@ -623,7 +623,7 @@ bool CompareTransform::transformCmps(Module &M, const bool processStrcmp,
IRBuilder<> cur_lenchk_IRB(&*(cur_lenchk_bb->getFirstInsertionPt())); IRBuilder<> cur_lenchk_IRB(&*(cur_lenchk_bb->getFirstInsertionPt()));
Value *icmp = cur_lenchk_IRB.CreateICmpEQ( Value *icmp = cur_lenchk_IRB.CreateICmpEQ(
sizedValue, ConstantInt::get(sizedValue->getType(), i)); sizedValue, ConstantInt::get(sizedValue->getType(), i));
cur_lenchk_IRB.CreateCondBr(icmp, end_bb, cur_cmp_bb); cur_lenchk_IRB.CreateCondBr(icmp, end_bb, cur_cmp_bb);
cur_lenchk_bb->getTerminator()->eraseFromParent(); cur_lenchk_bb->getTerminator()->eraseFromParent();

View File

@ -60,7 +60,7 @@ using namespace llvm;
// uncomment this toggle function verification at each step. horribly slow, but // uncomment this toggle function verification at each step. horribly slow, but
// helps to pinpoint a potential problem in the splitting code. // helps to pinpoint a potential problem in the splitting code.
//#define VERIFY_TOO_MUCH 1 // #define VERIFY_TOO_MUCH 1
namespace { namespace {

View File

@ -1 +1 @@
acaf7f6 c8a72dc

View File

@ -1 +1 @@
86b159b 202bace

View File

@ -1 +1 @@
5c8cf793ec 60c216bc9e

View File

@ -15,6 +15,7 @@ Underneath it is built upon KVM and QEMU and requires a modern Linux kernel
requires an Intel processor (6th generation onwards) and a special 5.10 kernel requires an Intel processor (6th generation onwards) and a special 5.10 kernel
(see [KVM-Nyx](https://github.com/nyx-fuzz/KVM-Nyx)). (see [KVM-Nyx](https://github.com/nyx-fuzz/KVM-Nyx)).
## Building Nyx mode ## Building Nyx mode
1. Install all the packages from [docs/INSTALL.md](../docs/INSTALL.md). 1. Install all the packages from [docs/INSTALL.md](../docs/INSTALL.md).
@ -41,6 +42,7 @@ requires an Intel processor (6th generation onwards) and a special 5.10 kernel
5. Optionally, for binary-only fuzzing: set up the required 5.10 kernel, see 5. Optionally, for binary-only fuzzing: set up the required 5.10 kernel, see
[KVM-Nyx](https://github.com/nyx-fuzz/KVM-Nyx). [KVM-Nyx](https://github.com/nyx-fuzz/KVM-Nyx).
## Preparing to fuzz a target with Nyx mode ## Preparing to fuzz a target with Nyx mode
For source instrumented fuzzing you can use any afl-cc mode, with LTO even For source instrumented fuzzing you can use any afl-cc mode, with LTO even
@ -68,12 +70,21 @@ This will create a directory with all necessary files and the Nyx configuration.
The name of the directory will be whatever you choose for `PACKAGE-DIRECTORY` The name of the directory will be whatever you choose for `PACKAGE-DIRECTORY`
above. above.
In the final step for the packaging we generate the Nyx configuration: Note that if the target reads from a file then use the `-file /path/to/file`
parameter to the above command.
Note that Nyx does **not** support the afl `@@` argument. Instead pass
something like `-file /foo.file -args "--file /foo.file --other-args"` to
the above command.
Then the final step: we generate the Nyx package configuration:
```shell ```shell
python3 nyx_mode/packer/packer/nyx_config_gen.py PACKAGE-DIRECTORY Kernel python3 nyx_mode/packer/packer/nyx_config_gen.py PACKAGE-DIRECTORY Kernel
``` ```
## Fuzzing with Nyx mode ## Fuzzing with Nyx mode
All the hard parts are done, fuzzing with Nyx mode is easy - just supply the All the hard parts are done, fuzzing with Nyx mode is easy - just supply the
@ -114,13 +125,39 @@ afl-fuzz -i in -o out -Y -S 1 -- ./PACKAGE-DIRECTORY
afl-fuzz -i in -o out -Y -S 2 -- ./PACKAGE-DIRECTORY afl-fuzz -i in -o out -Y -S 2 -- ./PACKAGE-DIRECTORY
``` ```
## AFL++ companion tools (afl-showmap etc.) ## AFL++ companion tools (afl-showmap etc.)
Please note that AFL++ companion tools like afl-cmin, afl-showmap, etc. are AFL++ companion tools support Nyx mode and can be used to analyze or minimize one specific input or an entire output corpus. These tools work similarly to `afl-fuzz`.
not supported with Nyx mode, only afl-fuzz.
To run a target with one of these tools, add the `-X` parameter to the command line to enable Nyx mode, and pass the path to a Nyx package directory:
```shell
afl-tmin -i in_file -o out_file -X -- ./PACKAGE-DIRECTORY
```
```shell
afl-analyze -i in_file -X -- ./PACKAGE-DIRECTORY
```
```shell
afl-showmap -i in_dir -o out_file -X -- ./PACKAGE-DIRECTORY
```
```shell
afl-cmin -i in_dir -o out_dir -X -- ./PACKAGE-DIRECTORY
```
On each program startup of one the AFL++ tools in Nyx mode, a Nyx VM is spawned, and a bootstrapping procedure is performed inside the VM to prepare the target environment. As a consequence, due to the bootstrapping procedure, the launch performance is much slower compared to other modes. However, this can be optimized by reusing an existing fuzzing snapshot to avoid the slow re-execution of the bootstrap procedure.
A fuzzing snapshot is automatically created and stored in the output directory at `out_dir/workdir/snapshot/` by the first parent process of `afl-fuzz` if parallel mode is used. To enable this feature, set the path to an existing snapshot directory in the `NYX_REUSE_SNAPSHOT` environment variable and use the tools as usual:
```shell
afl-fuzz -i ./in_dir -o ./out_dir -Y -M 0 ./PACKAGE-DIRECTORY
NYX_REUSE_SNAPSHOT=./out_dir/workdir/snapshot/ afl-analyze -i in_file -X -- ./PACKAGE-DIRECTORY
```
For source based instrumentation just use these tools normally, for
binary-only targets use with -Q for qemu_mode.
## Real-world examples ## Real-world examples

View File

@ -60,11 +60,6 @@ fi
echo "[*] Checking QEMU-Nyx ..." echo "[*] Checking QEMU-Nyx ..."
if [ ! -f "QEMU-Nyx/x86_64-softmmu/qemu-system-x86_64" ]; then if [ ! -f "QEMU-Nyx/x86_64-softmmu/qemu-system-x86_64" ]; then
if ! dpkg -s gtk3-devel > /dev/null 2>&1; then
echo "[-] Disabling GTK because gtk3-devel is not installed."
sed -i 's/--enable-gtk//g' QEMU-Nyx/compile_qemu_nyx.sh
fi
(cd QEMU-Nyx && ./compile_qemu_nyx.sh static) (cd QEMU-Nyx && ./compile_qemu_nyx.sh static)
fi fi

View File

@ -1 +1 @@
249bf0c872 a1321713c7

View File

@ -356,7 +356,7 @@ fi
if ! command -v "$CROSS" > /dev/null ; then if ! command -v "$CROSS" > /dev/null ; then
if [ "$CPU_TARGET" = "$(uname -m)" ] ; then if [ "$CPU_TARGET" = "$(uname -m)" ] ; then
echo "[+] Building afl++ qemu support libraries with CC=$CC" echo "[+] Building AFL++ qemu support libraries with CC=$CC"
echo "[+] Building libcompcov ..." echo "[+] Building libcompcov ..."
make -C libcompcov && echo "[+] libcompcov ready" make -C libcompcov && echo "[+] libcompcov ready"
echo "[+] Building unsigaction ..." echo "[+] Building unsigaction ..."
@ -371,7 +371,7 @@ if ! command -v "$CROSS" > /dev/null ; then
echo "[!] Cross compiler $CROSS could not be found, cannot compile libcompcov libqasan and unsigaction" echo "[!] Cross compiler $CROSS could not be found, cannot compile libcompcov libqasan and unsigaction"
fi fi
else else
echo "[+] Building afl++ qemu support libraries with CC=\"$CROSS $CROSS_FLAGS\"" echo "[+] Building AFL++ qemu support libraries with CC=\"$CROSS $CROSS_FLAGS\""
echo "[+] Building libcompcov ..." echo "[+] Building libcompcov ..."
make -C libcompcov CC="$CROSS $CROSS_FLAGS" && echo "[+] libcompcov ready" make -C libcompcov CC="$CROSS $CROSS_FLAGS" && echo "[+] libcompcov ready"
echo "[+] Building unsigaction ..." echo "[+] Building unsigaction ..."

View File

@ -68,7 +68,11 @@ static int debug_fd = -1;
#define MAX_MAPPINGS 1024 #define MAX_MAPPINGS 1024
static struct mapping { void *st, *en; } __compcov_ro[MAX_MAPPINGS]; static struct mapping {
void *st, *en;
} __compcov_ro[MAX_MAPPINGS];
static u32 __compcov_ro_cnt; static u32 __compcov_ro_cnt;

View File

@ -121,9 +121,9 @@ static void kill_child() {
} }
static void classify_counts(u8 *mem) { static void classify_counts(u8 *mem, u32 mem_size) {
u32 i = map_size; u32 i = mem_size;
if (edges_only) { if (edges_only) {
@ -222,7 +222,7 @@ static u64 analyze_run_target(u8 *mem, u32 len, u8 first_run) {
} }
classify_counts(fsrv.trace_bits); classify_counts(fsrv.trace_bits, fsrv.map_size);
total_execs++; total_execs++;
if (stop_soon) { if (stop_soon) {
@ -768,6 +768,7 @@ static void usage(u8 *argv0) {
" -U - use unicorn-based instrumentation (Unicorn mode)\n" " -U - use unicorn-based instrumentation (Unicorn mode)\n"
" -W - use qemu-based instrumentation with Wine (Wine " " -W - use qemu-based instrumentation with Wine (Wine "
"mode)\n" "mode)\n"
" -X - use Nyx mode\n"
#endif #endif
"\n" "\n"
@ -814,7 +815,7 @@ int main(int argc, char **argv_orig, char **envp) {
afl_fsrv_init(&fsrv); afl_fsrv_init(&fsrv);
while ((opt = getopt(argc, argv, "+i:f:m:t:eAOQUWh")) > 0) { while ((opt = getopt(argc, argv, "+i:f:m:t:eAOQUWXYh")) > 0) {
switch (opt) { switch (opt) {
@ -966,6 +967,23 @@ int main(int argc, char **argv_orig, char **envp) {
break; break;
case 'Y': // fallthough
#ifdef __linux__
case 'X': /* NYX mode */
if (fsrv.nyx_mode) { FATAL("Multiple -X options not supported"); }
fsrv.nyx_mode = 1;
fsrv.nyx_parent = true;
fsrv.nyx_standalone = true;
break;
#else
case 'X':
FATAL("Nyx mode is only availabe on linux...");
break;
#endif
case 'h': case 'h':
usage(argv[0]); usage(argv[0]);
return -1; return -1;
@ -997,7 +1015,21 @@ int main(int argc, char **argv_orig, char **envp) {
set_up_environment(argv); set_up_environment(argv);
#ifdef __linux__
if (!fsrv.nyx_mode) {
fsrv.target_path = find_binary(argv[optind]);
} else {
fsrv.target_path = ck_strdup(argv[optind]);
}
#else
fsrv.target_path = find_binary(argv[optind]); fsrv.target_path = find_binary(argv[optind]);
#endif
fsrv.trace_bits = afl_shm_init(&shm, map_size, 0); fsrv.trace_bits = afl_shm_init(&shm, map_size, 0);
detect_file_args(argv + optind, fsrv.out_file, &use_stdin); detect_file_args(argv + optind, fsrv.out_file, &use_stdin);
signal(SIGALRM, kill_child); signal(SIGALRM, kill_child);
@ -1020,6 +1052,26 @@ int main(int argc, char **argv_orig, char **envp) {
use_argv = get_cs_argv(argv[0], &target_path, argc - optind, argv + optind); use_argv = get_cs_argv(argv[0], &target_path, argc - optind, argv + optind);
#ifdef __linux__
} else if (fsrv.nyx_mode) {
fsrv.nyx_id = 0;
u8 *libnyx_binary = find_afl_binary(argv[0], "libnyx.so");
fsrv.nyx_handlers = afl_load_libnyx_plugin(libnyx_binary);
if (fsrv.nyx_handlers == NULL) {
FATAL("failed to initialize libnyx.so...");
}
fsrv.nyx_use_tmp_workdir = true;
fsrv.nyx_bind_cpu_id = 0;
use_argv = argv + optind;
#endif
} else { } else {
use_argv = argv + optind; use_argv = argv + optind;
@ -1045,7 +1097,11 @@ int main(int argc, char **argv_orig, char **envp) {
&fsrv, NULL, NULL, (fsrv.qemu_mode || unicorn_mode) ? SIGKILL : SIGTERM); &fsrv, NULL, NULL, (fsrv.qemu_mode || unicorn_mode) ? SIGKILL : SIGTERM);
read_initial_file(); read_initial_file();
#ifdef __linux__
if (!fsrv.nyx_mode) { (void)check_binary_signatures(fsrv.target_path); }
#else
(void)check_binary_signatures(fsrv.target_path); (void)check_binary_signatures(fsrv.target_path);
#endif
ACTF("Performing dry run (mem limit = %llu MB, timeout = %u ms%s)...", ACTF("Performing dry run (mem limit = %llu MB, timeout = %u ms%s)...",
mem_limit, exec_tmout, edges_only ? ", edges only" : ""); mem_limit, exec_tmout, edges_only ? ", edges only" : "");

View File

@ -31,6 +31,8 @@
#include <strings.h> #include <strings.h>
#include <limits.h> #include <limits.h>
#include <assert.h> #include <assert.h>
#include <ctype.h>
#include <sys/stat.h>
#if (LLVM_MAJOR - 0 == 0) #if (LLVM_MAJOR - 0 == 0)
#undef LLVM_MAJOR #undef LLVM_MAJOR
@ -76,6 +78,7 @@ enum {
INSTRUMENT_OPT_NGRAM = 16, INSTRUMENT_OPT_NGRAM = 16,
INSTRUMENT_OPT_CALLER = 32, INSTRUMENT_OPT_CALLER = 32,
INSTRUMENT_OPT_CTX_K = 64, INSTRUMENT_OPT_CTX_K = 64,
INSTRUMENT_OPT_CODECOV = 128,
}; };
@ -375,15 +378,308 @@ void parse_fsanitize(char *string) {
} }
static u8 fortify_set = 0, asan_set = 0, x_set = 0, bit_mode = 0,
shared_linking = 0, preprocessor_only = 0, have_unroll = 0,
have_o = 0, have_pic = 0, have_c = 0, partial_linking = 0,
non_dash = 0;
static void process_params(u32 argc, char **argv) {
if (cc_par_cnt + argc >= 1024) { FATAL("Too many command line parameters"); }
if (lto_mode && argc > 1) {
u32 idx;
for (idx = 1; idx < argc; idx++) {
if (!strncasecmp(argv[idx], "-fpic", 5)) have_pic = 1;
}
}
// for (u32 x = 0; x < argc; ++x) fprintf(stderr, "[%u] %s\n", x, argv[x]);
/* Process the argument list. */
u8 skip_next = 0;
while (--argc) {
u8 *cur = *(++argv);
if (skip_next) {
skip_next = 0;
continue;
}
if (cur[0] != '-') { non_dash = 1; }
if (!strncmp(cur, "--afl", 5)) continue;
if (lto_mode && !strncmp(cur, "-flto=thin", 10)) {
FATAL(
"afl-clang-lto cannot work with -flto=thin. Switch to -flto=full or "
"use afl-clang-fast!");
}
if (lto_mode && !strncmp(cur, "-fuse-ld=", 9)) continue;
if (lto_mode && !strncmp(cur, "--ld-path=", 10)) continue;
if (!strncmp(cur, "-fno-unroll", 11)) continue;
if (strstr(cur, "afl-compiler-rt") || strstr(cur, "afl-llvm-rt")) continue;
if (!strcmp(cur, "-Wl,-z,defs") || !strcmp(cur, "-Wl,--no-undefined") ||
!strcmp(cur, "--no-undefined")) {
continue;
}
if (compiler_mode == GCC_PLUGIN && !strcmp(cur, "-pipe")) { continue; }
if (!strcmp(cur, "-z") || !strcmp(cur, "-Wl,-z")) {
u8 *param = *(argv + 1);
if (!strcmp(param, "defs") || !strcmp(param, "-Wl,defs")) {
skip_next = 1;
continue;
}
}
if ((compiler_mode == GCC || compiler_mode == GCC_PLUGIN) &&
!strncmp(cur, "-stdlib=", 8)) {
if (!be_quiet) { WARNF("Found '%s' - stripping!", cur); }
continue;
}
if (!strncmp(cur, "-fsanitize-coverage-", 20) && strstr(cur, "list=")) {
have_instr_list = 1;
}
if (!strncmp(cur, "-fsanitize=", strlen("-fsanitize=")) &&
strchr(cur, ',')) {
parse_fsanitize(cur);
if (!cur || strlen(cur) <= strlen("-fsanitize=")) { continue; }
} else if ((!strncmp(cur, "-fsanitize=fuzzer-",
strlen("-fsanitize=fuzzer-")) ||
!strncmp(cur, "-fsanitize-coverage",
strlen("-fsanitize-coverage"))) &&
(strncmp(cur, "sanitize-coverage-allow",
strlen("sanitize-coverage-allow")) &&
strncmp(cur, "sanitize-coverage-deny",
strlen("sanitize-coverage-deny")) &&
instrument_mode != INSTRUMENT_LLVMNATIVE)) {
if (!be_quiet) { WARNF("Found '%s' - stripping!", cur); }
continue;
}
if (need_aflpplib || !strcmp(cur, "-fsanitize=fuzzer")) {
u8 *afllib = find_object("libAFLDriver.a", argv[0]);
if (!be_quiet) {
OKF("Found '-fsanitize=fuzzer', replacing with libAFLDriver.a");
}
if (!afllib) {
if (!be_quiet) {
WARNF(
"Cannot find 'libAFLDriver.a' to replace '-fsanitize=fuzzer' in "
"the flags - this will fail!");
}
} else {
cc_params[cc_par_cnt++] = afllib;
#ifdef __APPLE__
cc_params[cc_par_cnt++] = "-undefined";
cc_params[cc_par_cnt++] = "dynamic_lookup";
#endif
}
if (need_aflpplib) {
need_aflpplib = 0;
} else {
continue;
}
}
if (!strcmp(cur, "-m32")) bit_mode = 32;
if (!strcmp(cur, "armv7a-linux-androideabi")) bit_mode = 32;
if (!strcmp(cur, "-m64")) bit_mode = 64;
if (!strcmp(cur, "-fsanitize=address") || !strcmp(cur, "-fsanitize=memory"))
asan_set = 1;
if (strstr(cur, "FORTIFY_SOURCE")) fortify_set = 1;
if (!strcmp(cur, "-x")) x_set = 1;
if (!strcmp(cur, "-E")) preprocessor_only = 1;
if (!strcmp(cur, "-shared")) shared_linking = 1;
if (!strcmp(cur, "-dynamiclib")) shared_linking = 1;
if (!strcmp(cur, "--target=wasm32-wasi")) passthrough = 1;
if (!strcmp(cur, "-Wl,-r")) partial_linking = 1;
if (!strcmp(cur, "-Wl,-i")) partial_linking = 1;
if (!strcmp(cur, "-Wl,--relocatable")) partial_linking = 1;
if (!strcmp(cur, "-r")) partial_linking = 1;
if (!strcmp(cur, "--relocatable")) partial_linking = 1;
if (!strcmp(cur, "-c")) have_c = 1;
if (!strncmp(cur, "-O", 2)) have_o = 1;
if (!strncmp(cur, "-funroll-loop", 13)) have_unroll = 1;
if (*cur == '@') {
// response file support.
// we have two choices - move everything to the command line or
// rewrite the response files to temporary files and delete them
// afterwards. We choose the first for easiness.
// We do *not* support quotes in the rsp files to cope with spaces in
// filenames etc! If you need that then send a patch!
u8 *filename = cur + 1;
if (debug) { DEBUGF("response file=%s\n", filename); }
FILE *f = fopen(filename, "r");
struct stat st;
// Check not found or empty? let the compiler complain if so.
if (!f || fstat(fileno(f), &st) < 0 || st.st_size < 1) {
cc_params[cc_par_cnt++] = cur;
continue;
}
u8 *tmpbuf = malloc(st.st_size + 2), *ptr;
char **args = malloc(sizeof(char *) * (st.st_size >> 1));
int count = 1, cont = 0, cont_act = 0;
while (fgets(tmpbuf, st.st_size + 1, f)) {
ptr = tmpbuf;
// fprintf(stderr, "1: %s\n", ptr);
// no leading whitespace
while (isspace(*ptr)) {
++ptr;
cont_act = 0;
}
// no comments, no empty lines
if (*ptr == '#' || *ptr == '\n' || !*ptr) { continue; }
// remove LF
if (ptr[strlen(ptr) - 1] == '\n') { ptr[strlen(ptr) - 1] = 0; }
// remove CR
if (*ptr && ptr[strlen(ptr) - 1] == '\r') { ptr[strlen(ptr) - 1] = 0; }
// handle \ at end of line
if (*ptr && ptr[strlen(ptr) - 1] == '\\') {
cont = 1;
ptr[strlen(ptr) - 1] = 0;
}
// fprintf(stderr, "2: %s\n", ptr);
// remove whitespace at end
while (*ptr && isspace(ptr[strlen(ptr) - 1])) {
ptr[strlen(ptr) - 1] = 0;
cont = 0;
}
// fprintf(stderr, "3: %s\n", ptr);
if (*ptr) {
do {
u8 *value = ptr;
while (*ptr && !isspace(*ptr)) {
++ptr;
}
while (*ptr && isspace(*ptr)) {
*ptr++ = 0;
}
if (cont_act) {
u32 len = strlen(args[count - 1]) + strlen(value) + 1;
u8 *tmp = malloc(len);
snprintf(tmp, len, "%s%s", args[count - 1], value);
free(args[count - 1]);
args[count - 1] = tmp;
cont_act = 0;
} else {
args[count++] = strdup(value);
}
} while (*ptr);
}
if (cont) {
cont_act = 1;
cont = 0;
}
}
if (count) { process_params(count, args); }
// we cannot free args[]
free(tmpbuf);
continue;
}
cc_params[cc_par_cnt++] = cur;
}
}
/* Copy argv to cc_params, making the necessary edits. */ /* Copy argv to cc_params, making the necessary edits. */
static void edit_params(u32 argc, char **argv, char **envp) { static void edit_params(u32 argc, char **argv, char **envp) {
u8 fortify_set = 0, asan_set = 0, x_set = 0, bit_mode = 0, shared_linking = 0, cc_params = ck_alloc(1024 * sizeof(u8 *));
preprocessor_only = 0, have_unroll = 0, have_o = 0, have_pic = 0,
have_c = 0, partial_linking = 0;
cc_params = ck_alloc((argc + 128) * sizeof(u8 *));
if (lto_mode) { if (lto_mode) {
@ -641,10 +937,10 @@ static void edit_params(u32 argc, char **argv, char **envp) {
} }
//#if LLVM_MAJOR >= 13 // #if LLVM_MAJOR >= 13
// // Use the old pass manager in LLVM 14 which the afl++ passes still // // Use the old pass manager in LLVM 14 which the AFL++ passes still
// use. cc_params[cc_par_cnt++] = "-flegacy-pass-manager"; // use. cc_params[cc_par_cnt++] = "-flegacy-pass-manager";
//#endif // #endif
if (lto_mode && !have_c) { if (lto_mode && !have_c) {
@ -701,7 +997,7 @@ static void edit_params(u32 argc, char **argv, char **envp) {
if (instrument_mode == INSTRUMENT_PCGUARD) { if (instrument_mode == INSTRUMENT_PCGUARD) {
#if LLVM_MAJOR >= 11 #if LLVM_MAJOR >= 13
#if defined __ANDROID__ || ANDROID #if defined __ANDROID__ || ANDROID
cc_params[cc_par_cnt++] = "-fsanitize-coverage=trace-pc-guard"; cc_params[cc_par_cnt++] = "-fsanitize-coverage=trace-pc-guard";
instrument_mode = INSTRUMENT_LLVMNATIVE; instrument_mode = INSTRUMENT_LLVMNATIVE;
@ -718,7 +1014,7 @@ static void edit_params(u32 argc, char **argv, char **envp) {
} else { } else {
#if LLVM_MAJOR >= 11 /* use new pass manager */ #if LLVM_MAJOR >= 13 /* use new pass manager */
#if LLVM_MAJOR < 16 #if LLVM_MAJOR < 16
cc_params[cc_par_cnt++] = "-fexperimental-new-pass-manager"; cc_params[cc_par_cnt++] = "-fexperimental-new-pass-manager";
#endif #endif
@ -739,21 +1035,35 @@ static void edit_params(u32 argc, char **argv, char **envp) {
#if LLVM_MAJOR >= 4 #if LLVM_MAJOR >= 4
if (!be_quiet) if (!be_quiet)
SAYF( SAYF(
"Using unoptimized trace-pc-guard, upgrade to llvm 10.0.1+ for " "Using unoptimized trace-pc-guard, upgrade to LLVM 13+ for "
"enhanced version.\n"); "enhanced version.\n");
cc_params[cc_par_cnt++] = "-fsanitize-coverage=trace-pc-guard"; cc_params[cc_par_cnt++] = "-fsanitize-coverage=trace-pc-guard";
instrument_mode = INSTRUMENT_LLVMNATIVE; instrument_mode = INSTRUMENT_LLVMNATIVE;
#else #else
FATAL("pcguard instrumentation requires llvm 4.0.1+"); FATAL("pcguard instrumentation requires LLVM 4.0.1+");
#endif #endif
#endif #endif
} else if (instrument_mode == INSTRUMENT_LLVMNATIVE) { } else if (instrument_mode == INSTRUMENT_LLVMNATIVE) {
#if LLVM_MAJOR >= 4 #if LLVM_MAJOR >= 4
cc_params[cc_par_cnt++] = "-fsanitize-coverage=trace-pc-guard"; if (instrument_opt_mode & INSTRUMENT_OPT_CODECOV) {
#if LLVM_MAJOR >= 6
cc_params[cc_par_cnt++] =
"-fsanitize-coverage=trace-pc-guard,bb,no-prune,pc-table";
#else
FATAL("pcguard instrumentation with pc-table requires LLVM 6.0.1+");
#endif
} else {
cc_params[cc_par_cnt++] = "-fsanitize-coverage=trace-pc-guard";
}
#else #else
FATAL("pcguard instrumentation requires llvm 4.0.1+"); FATAL("pcguard instrumentation requires LLVM 4.0.1+");
#endif #endif
} else { } else {
@ -816,159 +1126,15 @@ static void edit_params(u32 argc, char **argv, char **envp) {
} }
if (!have_pic) cc_params[cc_par_cnt++] = "-fPIC";
} }
} }
/* Detect stray -v calls from ./configure scripts. */ /* Inspect the command line parameters. */
u8 skip_next = 0, non_dash = 0; process_params(argc, argv);
while (--argc) {
u8 *cur = *(++argv); if (!have_pic) { cc_params[cc_par_cnt++] = "-fPIC"; }
if (skip_next) {
skip_next = 0;
continue;
}
if (cur[0] != '-') { non_dash = 1; }
if (!strncmp(cur, "--afl", 5)) continue;
if (lto_mode && !strncmp(cur, "-fuse-ld=", 9)) continue;
if (lto_mode && !strncmp(cur, "--ld-path=", 10)) continue;
if (!strncmp(cur, "-fno-unroll", 11)) continue;
if (strstr(cur, "afl-compiler-rt") || strstr(cur, "afl-llvm-rt")) continue;
if (!strcmp(cur, "-Wl,-z,defs") || !strcmp(cur, "-Wl,--no-undefined") ||
!strcmp(cur, "--no-undefined")) {
continue;
}
if (compiler_mode == GCC_PLUGIN && !strcmp(cur, "-pipe")) { continue; }
if (!strcmp(cur, "-z") || !strcmp(cur, "-Wl,-z")) {
u8 *param = *(argv + 1);
if (!strcmp(param, "defs") || !strcmp(param, "-Wl,defs")) {
skip_next = 1;
continue;
}
}
if ((compiler_mode == GCC || compiler_mode == GCC_PLUGIN) &&
!strncmp(cur, "-stdlib=", 8)) {
if (!be_quiet) { WARNF("Found '%s' - stripping!", cur); }
continue;
}
if (!strncmp(cur, "-fsanitize-coverage-", 20) && strstr(cur, "list=")) {
have_instr_list = 1;
}
if (!strncmp(cur, "-fsanitize=", strlen("-fsanitize=")) &&
strchr(cur, ',')) {
parse_fsanitize(cur);
if (!cur || strlen(cur) <= strlen("-fsanitize=")) { continue; }
} else if ((!strncmp(cur, "-fsanitize=fuzzer-",
strlen("-fsanitize=fuzzer-")) ||
!strncmp(cur, "-fsanitize-coverage",
strlen("-fsanitize-coverage"))) &&
(strncmp(cur, "sanitize-coverage-allow",
strlen("sanitize-coverage-allow")) &&
strncmp(cur, "sanitize-coverage-deny",
strlen("sanitize-coverage-deny")) &&
instrument_mode != INSTRUMENT_LLVMNATIVE)) {
if (!be_quiet) { WARNF("Found '%s' - stripping!", cur); }
continue;
}
if (need_aflpplib || !strcmp(cur, "-fsanitize=fuzzer")) {
u8 *afllib = find_object("libAFLDriver.a", argv[0]);
if (!be_quiet) {
OKF("Found '-fsanitize=fuzzer', replacing with libAFLDriver.a");
}
if (!afllib) {
if (!be_quiet) {
WARNF(
"Cannot find 'libAFLDriver.a' to replace '-fsanitize=fuzzer' in "
"the flags - this will fail!");
}
} else {
cc_params[cc_par_cnt++] = afllib;
#ifdef __APPLE__
cc_params[cc_par_cnt++] = "-undefined";
cc_params[cc_par_cnt++] = "dynamic_lookup";
#endif
}
if (need_aflpplib) {
need_aflpplib = 0;
} else {
continue;
}
}
if (!strcmp(cur, "-m32")) bit_mode = 32;
if (!strcmp(cur, "armv7a-linux-androideabi")) bit_mode = 32;
if (!strcmp(cur, "-m64")) bit_mode = 64;
if (!strcmp(cur, "-fsanitize=address") || !strcmp(cur, "-fsanitize=memory"))
asan_set = 1;
if (strstr(cur, "FORTIFY_SOURCE")) fortify_set = 1;
if (!strcmp(cur, "-x")) x_set = 1;
if (!strcmp(cur, "-E")) preprocessor_only = 1;
if (!strcmp(cur, "-shared")) shared_linking = 1;
if (!strcmp(cur, "-dynamiclib")) shared_linking = 1;
if (!strcmp(cur, "--target=wasm32-wasi")) passthrough = 1;
if (!strcmp(cur, "-Wl,-r")) partial_linking = 1;
if (!strcmp(cur, "-Wl,-i")) partial_linking = 1;
if (!strcmp(cur, "-Wl,--relocatable")) partial_linking = 1;
if (!strcmp(cur, "-r")) partial_linking = 1;
if (!strcmp(cur, "--relocatable")) partial_linking = 1;
if (!strcmp(cur, "-c")) have_c = 1;
if (!strncmp(cur, "-O", 2)) have_o = 1;
if (!strncmp(cur, "-funroll-loop", 13)) have_unroll = 1;
cc_params[cc_par_cnt++] = cur;
}
// in case LLVM is installed not via a package manager or "make install" // in case LLVM is installed not via a package manager or "make install"
// e.g. compiled download or compiled from github then its ./lib directory // e.g. compiled download or compiled from github then its ./lib directory
@ -1101,37 +1267,45 @@ static void edit_params(u32 argc, char **argv, char **envp) {
if (!have_c) cc_params[cc_par_cnt++] = "-lrt"; if (!have_c) cc_params[cc_par_cnt++] = "-lrt";
#endif #endif
cc_params[cc_par_cnt++] = "-D__AFL_HAVE_MANUAL_CONTROL=1";
cc_params[cc_par_cnt++] = "-D__AFL_COMPILER=1"; cc_params[cc_par_cnt++] = "-D__AFL_COMPILER=1";
cc_params[cc_par_cnt++] = "-DFUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION=1"; cc_params[cc_par_cnt++] = "-DFUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION=1";
/* When the user tries to use persistent or deferred forkserver modes by /* As documented in instrumentation/README.persistent_mode.md, deferred
appending a single line to the program, we want to reliably inject a forkserver initialization and persistent mode are not available in afl-gcc
signature into the binary (to be picked up by afl-fuzz) and we want and afl-clang. */
to call a function from the runtime .o file. This is unnecessarily if (compiler_mode != GCC && compiler_mode != CLANG) {
painful for three reasons:
1) We need to convince the compiler not to optimize out the signature. cc_params[cc_par_cnt++] = "-D__AFL_HAVE_MANUAL_CONTROL=1";
This is done with __attribute__((used)).
2) We need to convince the linker, when called with -Wl,--gc-sections, /* When the user tries to use persistent or deferred forkserver modes by
not to do the same. This is done by forcing an assignment to a appending a single line to the program, we want to reliably inject a
'volatile' pointer. signature into the binary (to be picked up by afl-fuzz) and we want
to call a function from the runtime .o file. This is unnecessarily
painful for three reasons:
3) We need to declare __afl_persistent_loop() in the global namespace, 1) We need to convince the compiler not to optimize out the signature.
but doing this within a method in a class is hard - :: and extern "C" This is done with __attribute__((used)).
are forbidden and __attribute__((alias(...))) doesn't work. Hence the
__asm__ aliasing trick.
*/ 2) We need to convince the linker, when called with -Wl,--gc-sections,
not to do the same. This is done by forcing an assignment to a
'volatile' pointer.
cc_params[cc_par_cnt++] = 3) We need to declare __afl_persistent_loop() in the global namespace,
"-D__AFL_FUZZ_INIT()=" but doing this within a method in a class is hard - :: and extern "C"
"int __afl_sharedmem_fuzzing = 1;" are forbidden and __attribute__((alias(...))) doesn't work. Hence the
"extern unsigned int *__afl_fuzz_len;" __asm__ aliasing trick.
"extern unsigned char *__afl_fuzz_ptr;"
"unsigned char __afl_fuzz_alt[1048576];" */
"unsigned char *__afl_fuzz_alt_ptr = __afl_fuzz_alt;";
cc_params[cc_par_cnt++] =
"-D__AFL_FUZZ_INIT()="
"int __afl_sharedmem_fuzzing = 1;"
"extern unsigned int *__afl_fuzz_len;"
"extern unsigned char *__afl_fuzz_ptr;"
"unsigned char __afl_fuzz_alt[1048576];"
"unsigned char *__afl_fuzz_alt_ptr = __afl_fuzz_alt;";
}
if (plusplus_mode) { if (plusplus_mode) {
@ -1169,35 +1343,39 @@ static void edit_params(u32 argc, char **argv, char **envp) {
"(*__afl_fuzz_len = read(0, __afl_fuzz_alt_ptr, 1048576)) == 0xffffffff " "(*__afl_fuzz_len = read(0, __afl_fuzz_alt_ptr, 1048576)) == 0xffffffff "
"? 0 : *__afl_fuzz_len)"; "? 0 : *__afl_fuzz_len)";
cc_params[cc_par_cnt++] = if (compiler_mode != GCC && compiler_mode != CLANG) {
"-D__AFL_LOOP(_A)="
"({ static volatile char *_B __attribute__((used,unused)); "
" _B = (char*)\"" PERSIST_SIG
"\"; "
"extern int __afl_connected;"
#ifdef __APPLE__
"__attribute__((visibility(\"default\"))) "
"int _L(unsigned int) __asm__(\"___afl_persistent_loop\"); "
#else
"__attribute__((visibility(\"default\"))) "
"int _L(unsigned int) __asm__(\"__afl_persistent_loop\"); "
#endif /* ^__APPLE__ */
// if afl is connected, we run _A times, else once.
"_L(__afl_connected ? _A : 1); })";
cc_params[cc_par_cnt++] = cc_params[cc_par_cnt++] =
"-D__AFL_INIT()=" "-D__AFL_LOOP(_A)="
"do { static volatile char *_A __attribute__((used,unused)); " "({ static volatile const char *_B __attribute__((used,unused)); "
" _A = (char*)\"" DEFER_SIG " _B = (const char*)\"" PERSIST_SIG
"\"; " "\"; "
"extern int __afl_connected;"
#ifdef __APPLE__ #ifdef __APPLE__
"__attribute__((visibility(\"default\"))) " "__attribute__((visibility(\"default\"))) "
"void _I(void) __asm__(\"___afl_manual_init\"); " "int _L(unsigned int) __asm__(\"___afl_persistent_loop\"); "
#else #else
"__attribute__((visibility(\"default\"))) " "__attribute__((visibility(\"default\"))) "
"void _I(void) __asm__(\"__afl_manual_init\"); " "int _L(unsigned int) __asm__(\"__afl_persistent_loop\"); "
#endif /* ^__APPLE__ */ #endif /* ^__APPLE__ */
"_I(); } while (0)"; // if afl is connected, we run _A times, else once.
"_L(__afl_connected ? _A : 1); })";
cc_params[cc_par_cnt++] =
"-D__AFL_INIT()="
"do { static volatile const char *_A __attribute__((used,unused)); "
" _A = (const char*)\"" DEFER_SIG
"\"; "
#ifdef __APPLE__
"__attribute__((visibility(\"default\"))) "
"void _I(void) __asm__(\"___afl_manual_init\"); "
#else
"__attribute__((visibility(\"default\"))) "
"void _I(void) __asm__(\"__afl_manual_init\"); "
#endif /* ^__APPLE__ */
"_I(); } while (0)";
}
if (x_set) { if (x_set) {
@ -1639,13 +1817,17 @@ int main(int argc, char **argv, char **envp) {
instrument_mode = INSTRUMENT_CLASSIC; instrument_mode = INSTRUMENT_CLASSIC;
lto_mode = 1; lto_mode = 1;
} else if (!instrument_mode || instrument_mode == INSTRUMENT_AFL) } else if (!instrument_mode || instrument_mode == INSTRUMENT_AFL) {
instrument_mode = INSTRUMENT_AFL; instrument_mode = INSTRUMENT_AFL;
else
} else {
FATAL("main instrumentation mode already set with %s", FATAL("main instrumentation mode already set with %s",
instrument_mode_string[instrument_mode]); instrument_mode_string[instrument_mode]);
}
} }
if (strncasecmp(ptr2, "pc-guard", strlen("pc-guard")) == 0 || if (strncasecmp(ptr2, "pc-guard", strlen("pc-guard")) == 0 ||
@ -1660,7 +1842,8 @@ int main(int argc, char **argv, char **envp) {
} }
if (strncasecmp(ptr2, "llvmnative", strlen("llvmnative")) == 0 || if (strncasecmp(ptr2, "llvmnative", strlen("llvmnative")) == 0 ||
strncasecmp(ptr2, "llvm-native", strlen("llvm-native")) == 0) { strncasecmp(ptr2, "llvm-native", strlen("llvm-native")) == 0 ||
strncasecmp(ptr2, "native", strlen("native")) == 0) {
if (!instrument_mode || instrument_mode == INSTRUMENT_LLVMNATIVE) if (!instrument_mode || instrument_mode == INSTRUMENT_LLVMNATIVE)
instrument_mode = INSTRUMENT_LLVMNATIVE; instrument_mode = INSTRUMENT_LLVMNATIVE;
@ -1670,6 +1853,23 @@ int main(int argc, char **argv, char **envp) {
} }
if (strncasecmp(ptr2, "llvmcodecov", strlen("llvmcodecov")) == 0 ||
strncasecmp(ptr2, "llvm-codecov", strlen("llvm-codecov")) == 0) {
if (!instrument_mode || instrument_mode == INSTRUMENT_LLVMNATIVE) {
instrument_mode = INSTRUMENT_LLVMNATIVE;
instrument_opt_mode |= INSTRUMENT_OPT_CODECOV;
} else {
FATAL("main instrumentation mode already set with %s",
instrument_mode_string[instrument_mode]);
}
}
if (strncasecmp(ptr2, "cfg", strlen("cfg")) == 0 || if (strncasecmp(ptr2, "cfg", strlen("cfg")) == 0 ||
strncasecmp(ptr2, "instrim", strlen("instrim")) == 0) { strncasecmp(ptr2, "instrim", strlen("instrim")) == 0) {
@ -1831,7 +2031,7 @@ int main(int argc, char **argv, char **envp) {
if (!compiler_mode) { if (!compiler_mode) {
// lto is not a default because outside of afl-cc RANLIB and AR have to // lto is not a default because outside of afl-cc RANLIB and AR have to
// be set to llvm versions so this would work // be set to LLVM versions so this would work
if (have_llvm) if (have_llvm)
compiler_mode = LLVM; compiler_mode = LLVM;
else if (have_gcc_plugin) else if (have_gcc_plugin)
@ -1850,6 +2050,17 @@ int main(int argc, char **argv, char **envp) {
} }
/* if our PCGUARD implementation is not available then silently switch to
native LLVM PCGUARD */
if (compiler_mode == CLANG &&
(instrument_mode == INSTRUMENT_DEFAULT ||
instrument_mode == INSTRUMENT_PCGUARD) &&
find_object("SanitizerCoveragePCGUARD.so", argv[0]) == NULL) {
instrument_mode = INSTRUMENT_LLVMNATIVE;
}
if (compiler_mode == GCC) { if (compiler_mode == GCC) {
if (clang_mode) { if (clang_mode) {
@ -1896,12 +2107,12 @@ int main(int argc, char **argv, char **envp) {
"-------------|\n" "-------------|\n"
"MODES: NCC PERSIST DICT LAF " "MODES: NCC PERSIST DICT LAF "
"CMPLOG SELECT\n" "CMPLOG SELECT\n"
" [LTO] llvm LTO: %s%s\n" " [LTO] LLVM LTO: %s%s\n"
" PCGUARD DEFAULT yes yes yes yes yes " " PCGUARD DEFAULT yes yes yes yes yes "
" yes\n" " yes\n"
" CLASSIC yes yes yes yes yes " " CLASSIC yes yes yes yes yes "
" yes\n" " yes\n"
" [LLVM] llvm: %s%s\n" " [LLVM] LLVM: %s%s\n"
" PCGUARD %s yes yes module yes yes " " PCGUARD %s yes yes module yes yes "
"yes\n" "yes\n"
" CLASSIC %s no yes module yes yes " " CLASSIC %s no yes module yes yes "
@ -1971,7 +2182,7 @@ int main(int argc, char **argv, char **envp) {
" (instrumentation/README.lto.md)\n" " (instrumentation/README.lto.md)\n"
" PERSIST: persistent mode support [code] (huge speed increase!)\n" " PERSIST: persistent mode support [code] (huge speed increase!)\n"
" (instrumentation/README.persistent_mode.md)\n" " (instrumentation/README.persistent_mode.md)\n"
" DICT: dictionary in the target [yes=automatic or llvm module " " DICT: dictionary in the target [yes=automatic or LLVM module "
"pass]\n" "pass]\n"
" (instrumentation/README.lto.md + " " (instrumentation/README.lto.md + "
"instrumentation/README.llvm.md)\n" "instrumentation/README.llvm.md)\n"
@ -2087,6 +2298,8 @@ int main(int argc, char **argv, char **envp) {
"bb\n" "bb\n"
" AFL_REAL_LD: use this lld linker instead of the compiled in " " AFL_REAL_LD: use this lld linker instead of the compiled in "
"path\n" "path\n"
" AFL_LLVM_LTO_SKIPINIT: don't inject initialization code "
"(used in WAFL mode)\n"
"If anything fails - be sure to read README.lto.md!\n"); "If anything fails - be sure to read README.lto.md!\n");
#endif #endif
@ -2227,7 +2440,8 @@ int main(int argc, char **argv, char **envp) {
"(requires LLVM 11 or higher)"); "(requires LLVM 11 or higher)");
#endif #endif
if (instrument_opt_mode && instrument_mode != INSTRUMENT_CLASSIC) if (instrument_opt_mode && instrument_opt_mode != INSTRUMENT_OPT_CODECOV &&
instrument_mode != INSTRUMENT_CLASSIC)
FATAL( FATAL(
"CALLER, CTX and NGRAM instrumentation options can only be used with " "CALLER, CTX and NGRAM instrumentation options can only be used with "
"the LLVM CLASSIC instrumentation mode."); "the LLVM CLASSIC instrumentation mode.");

View File

@ -949,7 +949,7 @@ void read_bitmap(u8 *fname, u8 *map, size_t len) {
/* Get unix time in milliseconds */ /* Get unix time in milliseconds */
u64 get_cur_time(void) { inline u64 get_cur_time(void) {
struct timeval tv; struct timeval tv;
struct timezone tz; struct timezone tz;
@ -1359,3 +1359,52 @@ s32 create_file(u8 *fn) {
} }
#ifdef __linux__
/* Nyx requires a tmp workdir to access specific files (such as mmapped files,
* etc.). This helper function basically creates both a path to a tmp workdir
* and the workdir itself. If the environment variable TMPDIR is set, we use
* that as the base directory, otherwise we use /tmp. */
char *create_nyx_tmp_workdir(void) {
char *tmpdir = getenv("TMPDIR");
if (!tmpdir) { tmpdir = "/tmp"; }
char *nyx_out_dir_path =
alloc_printf("%s/.nyx_tmp_%d/", tmpdir, (u32)getpid());
if (mkdir(nyx_out_dir_path, 0700)) { PFATAL("Unable to create nyx workdir"); }
return nyx_out_dir_path;
}
/* Vice versa, we remove the tmp workdir for nyx with this helper function. */
void remove_nyx_tmp_workdir(afl_forkserver_t *fsrv, char *nyx_out_dir_path) {
char *workdir_path = alloc_printf("%s/workdir", nyx_out_dir_path);
if (access(workdir_path, R_OK) == 0) {
if (fsrv->nyx_handlers->nyx_remove_work_dir(workdir_path) != true) {
WARNF("Unable to remove nyx workdir (%s)", workdir_path);
}
}
if (rmdir(nyx_out_dir_path)) {
WARNF("Unable to remove nyx workdir (%s)", nyx_out_dir_path);
}
ck_free(workdir_path);
ck_free(nyx_out_dir_path);
}
#endif

View File

@ -49,6 +49,134 @@
#include <sys/select.h> #include <sys/select.h>
#include <sys/stat.h> #include <sys/stat.h>
#ifdef __linux__
#include <dlfcn.h>
/* function to load nyx_helper function from libnyx.so */
nyx_plugin_handler_t *afl_load_libnyx_plugin(u8 *libnyx_binary) {
void *handle;
nyx_plugin_handler_t *plugin = calloc(1, sizeof(nyx_plugin_handler_t));
ACTF("Trying to load libnyx.so plugin...");
handle = dlopen((char *)libnyx_binary, RTLD_NOW);
if (!handle) { goto fail; }
plugin->nyx_config_load = dlsym(handle, "nyx_config_load");
if (plugin->nyx_config_load == NULL) { goto fail; }
plugin->nyx_config_set_workdir_path =
dlsym(handle, "nyx_config_set_workdir_path");
if (plugin->nyx_config_set_workdir_path == NULL) { goto fail; }
plugin->nyx_config_set_input_buffer_size =
dlsym(handle, "nyx_config_set_input_buffer_size");
if (plugin->nyx_config_set_input_buffer_size == NULL) { goto fail; }
plugin->nyx_config_set_input_buffer_write_protection =
dlsym(handle, "nyx_config_set_input_buffer_write_protection");
if (plugin->nyx_config_set_input_buffer_write_protection == NULL) {
goto fail;
}
plugin->nyx_config_set_hprintf_fd =
dlsym(handle, "nyx_config_set_hprintf_fd");
if (plugin->nyx_config_set_hprintf_fd == NULL) { goto fail; }
plugin->nyx_config_set_process_role =
dlsym(handle, "nyx_config_set_process_role");
if (plugin->nyx_config_set_process_role == NULL) { goto fail; }
plugin->nyx_config_set_reuse_snapshot_path =
dlsym(handle, "nyx_config_set_reuse_snapshot_path");
if (plugin->nyx_config_set_reuse_snapshot_path == NULL) { goto fail; }
plugin->nyx_new = dlsym(handle, "nyx_new");
if (plugin->nyx_new == NULL) { goto fail; }
plugin->nyx_shutdown = dlsym(handle, "nyx_shutdown");
if (plugin->nyx_shutdown == NULL) { goto fail; }
plugin->nyx_option_set_reload_mode =
dlsym(handle, "nyx_option_set_reload_mode");
if (plugin->nyx_option_set_reload_mode == NULL) { goto fail; }
plugin->nyx_option_set_timeout = dlsym(handle, "nyx_option_set_timeout");
if (plugin->nyx_option_set_timeout == NULL) { goto fail; }
plugin->nyx_option_apply = dlsym(handle, "nyx_option_apply");
if (plugin->nyx_option_apply == NULL) { goto fail; }
plugin->nyx_set_afl_input = dlsym(handle, "nyx_set_afl_input");
if (plugin->nyx_set_afl_input == NULL) { goto fail; }
plugin->nyx_exec = dlsym(handle, "nyx_exec");
if (plugin->nyx_exec == NULL) { goto fail; }
plugin->nyx_get_bitmap_buffer = dlsym(handle, "nyx_get_bitmap_buffer");
if (plugin->nyx_get_bitmap_buffer == NULL) { goto fail; }
plugin->nyx_get_bitmap_buffer_size =
dlsym(handle, "nyx_get_bitmap_buffer_size");
if (plugin->nyx_get_bitmap_buffer_size == NULL) { goto fail; }
plugin->nyx_get_aux_string = dlsym(handle, "nyx_get_aux_string");
if (plugin->nyx_get_aux_string == NULL) { goto fail; }
plugin->nyx_remove_work_dir = dlsym(handle, "nyx_remove_work_dir");
if (plugin->nyx_remove_work_dir == NULL) { goto fail; }
OKF("libnyx plugin is ready!");
return plugin;
fail:
FATAL("failed to load libnyx: %s\n", dlerror());
ck_free(plugin);
return NULL;
}
void afl_nyx_runner_kill(afl_forkserver_t *fsrv) {
if (fsrv->nyx_mode) {
if (fsrv->nyx_aux_string) { ck_free(fsrv->nyx_aux_string); }
/* check if we actually got a valid nyx runner */
if (fsrv->nyx_runner) {
fsrv->nyx_handlers->nyx_shutdown(fsrv->nyx_runner);
}
/* if we have use a tmp work dir we need to remove it */
if (fsrv->nyx_use_tmp_workdir && fsrv->nyx_tmp_workdir_path) {
remove_nyx_tmp_workdir(fsrv, fsrv->nyx_tmp_workdir_path);
}
}
}
/* Wrapper for FATAL() that kills the nyx runner (and removes all created tmp
* files) before exiting. Used before "afl_fsrv_killall()" is registered as
* an atexit() handler. */
#define NYX_PRE_FATAL(fsrv, x...) \
do { \
\
afl_nyx_runner_kill(fsrv); \
FATAL(x); \
\
} while (0)
#endif
/** /**
* The correct fds for reading and writing pipes * The correct fds for reading and writing pipes
*/ */
@ -84,6 +212,8 @@ void afl_fsrv_init(afl_forkserver_t *fsrv) {
fsrv->nyx_runner = NULL; fsrv->nyx_runner = NULL;
fsrv->nyx_id = 0xFFFFFFFF; fsrv->nyx_id = 0xFFFFFFFF;
fsrv->nyx_bind_cpu_id = 0xFFFFFFFF; fsrv->nyx_bind_cpu_id = 0xFFFFFFFF;
fsrv->nyx_use_tmp_workdir = false;
fsrv->nyx_tmp_workdir_path = NULL;
#endif #endif
// this structure needs default so we initialize it if this was not done // this structure needs default so we initialize it if this was not done
@ -359,7 +489,7 @@ static void report_error_and_exit(int error) {
break; break;
case FS_ERROR_OLD_CMPLOG: case FS_ERROR_OLD_CMPLOG:
FATAL( FATAL(
"the -c cmplog target was instrumented with an too old afl++ " "the -c cmplog target was instrumented with an too old AFL++ "
"version, you need to recompile it."); "version, you need to recompile it.");
break; break;
case FS_ERROR_OLD_CMPLOG_QEMU: case FS_ERROR_OLD_CMPLOG_QEMU:
@ -397,40 +527,119 @@ void afl_fsrv_start(afl_forkserver_t *fsrv, char **argv,
if (!be_quiet) { ACTF("Spinning up the NYX backend..."); } if (!be_quiet) { ACTF("Spinning up the NYX backend..."); }
if (fsrv->out_dir_path == NULL) { FATAL("Nyx workdir path not found..."); } if (fsrv->nyx_use_tmp_workdir) {
char *x = alloc_printf("%s/workdir", fsrv->out_dir_path); fsrv->nyx_tmp_workdir_path = create_nyx_tmp_workdir();
fsrv->out_dir_path = fsrv->nyx_tmp_workdir_path;
if (fsrv->nyx_id == 0xFFFFFFFF) { FATAL("Nyx ID is not set..."); }
if (fsrv->nyx_bind_cpu_id == 0xFFFFFFFF) {
FATAL("Nyx CPU ID is not set...");
}
if (fsrv->nyx_standalone) {
fsrv->nyx_runner = fsrv->nyx_handlers->nyx_new(
fsrv->target_path, x, fsrv->nyx_bind_cpu_id, MAX_FILE, true);
} else { } else {
if (fsrv->nyx_parent) { if (fsrv->out_dir_path == NULL) {
fsrv->nyx_runner = fsrv->nyx_handlers->nyx_new_parent( NYX_PRE_FATAL(fsrv, "Nyx workdir path not found...");
fsrv->target_path, x, fsrv->nyx_bind_cpu_id, MAX_FILE, true);
} else {
fsrv->nyx_runner = fsrv->nyx_handlers->nyx_new_child(
fsrv->target_path, x, fsrv->nyx_bind_cpu_id, fsrv->nyx_id);
} }
} }
ck_free(x); /* libnyx expects an absolute path */
char *outdir_path_absolute = realpath(fsrv->out_dir_path, NULL);
if (outdir_path_absolute == NULL) {
NYX_PRE_FATAL(fsrv, "Nyx workdir path cannot be resolved ...");
}
char *workdir_path = alloc_printf("%s/workdir", outdir_path_absolute);
if (fsrv->nyx_id == 0xFFFFFFFF) {
NYX_PRE_FATAL(fsrv, "Nyx ID is not set...");
}
if (fsrv->nyx_bind_cpu_id == 0xFFFFFFFF) {
NYX_PRE_FATAL(fsrv, "Nyx CPU ID is not set...");
}
void *nyx_config = fsrv->nyx_handlers->nyx_config_load(fsrv->target_path);
fsrv->nyx_handlers->nyx_config_set_workdir_path(nyx_config, workdir_path);
fsrv->nyx_handlers->nyx_config_set_input_buffer_size(nyx_config, MAX_FILE);
fsrv->nyx_handlers->nyx_config_set_input_buffer_write_protection(nyx_config,
true);
if (fsrv->nyx_standalone) {
fsrv->nyx_handlers->nyx_config_set_process_role(nyx_config, StandAlone);
} else {
if (fsrv->nyx_parent) {
fsrv->nyx_handlers->nyx_config_set_process_role(nyx_config, Parent);
} else {
fsrv->nyx_handlers->nyx_config_set_process_role(nyx_config, Child);
}
}
if (getenv("NYX_REUSE_SNAPSHOT") != NULL) {
if (access(getenv("NYX_REUSE_SNAPSHOT"), F_OK) == -1) {
NYX_PRE_FATAL(fsrv, "NYX_REUSE_SNAPSHOT path does not exist");
}
/* stupid sanity check to avoid passing an empty or invalid snapshot
* directory */
char *snapshot_file_path =
alloc_printf("%s/global.state", getenv("NYX_REUSE_SNAPSHOT"));
if (access(snapshot_file_path, R_OK) == -1) {
NYX_PRE_FATAL(
fsrv,
"NYX_REUSE_SNAPSHOT path does not contain a valid Nyx snapshot");
}
ck_free(snapshot_file_path);
/* another sanity check to avoid passing a snapshot directory that is
* located in the current workdir (the workdir will be wiped by libnyx on
* startup) */
char *workdir_snapshot_path =
alloc_printf("%s/workdir/snapshot", outdir_path_absolute);
char *reuse_snapshot_path_real =
realpath(getenv("NYX_REUSE_SNAPSHOT"), NULL);
if (strcmp(workdir_snapshot_path, reuse_snapshot_path_real) == 0) {
NYX_PRE_FATAL(fsrv,
"NYX_REUSE_SNAPSHOT path is located in current workdir "
"(use another output directory)");
}
ck_free(reuse_snapshot_path_real);
ck_free(workdir_snapshot_path);
fsrv->nyx_handlers->nyx_config_set_reuse_snapshot_path(
nyx_config, getenv("NYX_REUSE_SNAPSHOT"));
}
fsrv->nyx_runner =
fsrv->nyx_handlers->nyx_new(nyx_config, fsrv->nyx_bind_cpu_id);
ck_free(workdir_path);
ck_free(outdir_path_absolute);
if (fsrv->nyx_runner == NULL) { FATAL("Something went wrong ..."); } if (fsrv->nyx_runner == NULL) { FATAL("Something went wrong ..."); }
@ -458,15 +667,13 @@ void afl_fsrv_start(afl_forkserver_t *fsrv, char **argv,
switch (fsrv->nyx_handlers->nyx_exec(fsrv->nyx_runner)) { switch (fsrv->nyx_handlers->nyx_exec(fsrv->nyx_runner)) {
case Abort: case Abort:
fsrv->nyx_handlers->nyx_shutdown(fsrv->nyx_runner); NYX_PRE_FATAL(fsrv, "Error: Nyx abort occured...");
FATAL("Error: Nyx abort occured...");
break; break;
case IoError: case IoError:
FATAL("Error: QEMU-Nyx has died..."); NYX_PRE_FATAL(fsrv, "Error: QEMU-Nyx has died...");
break; break;
case Error: case Error:
fsrv->nyx_handlers->nyx_shutdown(fsrv->nyx_runner); NYX_PRE_FATAL(fsrv, "Error: Nyx runtime error has occured...");
FATAL("Error: Nyx runtime error has occured...");
break; break;
default: default:
break; break;
@ -476,7 +683,8 @@ void afl_fsrv_start(afl_forkserver_t *fsrv, char **argv,
/* autodict in Nyx mode */ /* autodict in Nyx mode */
if (!ignore_autodict) { if (!ignore_autodict) {
x = alloc_printf("%s/workdir/dump/afl_autodict.txt", fsrv->out_dir_path); char *x =
alloc_printf("%s/workdir/dump/afl_autodict.txt", fsrv->out_dir_path);
int nyx_autodict_fd = open(x, O_RDONLY); int nyx_autodict_fd = open(x, O_RDONLY);
ck_free(x); ck_free(x);
@ -489,8 +697,9 @@ void afl_fsrv_start(afl_forkserver_t *fsrv, char **argv,
u8 *dict = ck_alloc(f_len); u8 *dict = ck_alloc(f_len);
if (dict == NULL) { if (dict == NULL) {
FATAL("Could not allocate %u bytes of autodictionary memory", NYX_PRE_FATAL(
f_len); fsrv, "Could not allocate %u bytes of autodictionary memory",
f_len);
} }
@ -507,7 +716,8 @@ void afl_fsrv_start(afl_forkserver_t *fsrv, char **argv,
} else { } else {
FATAL( NYX_PRE_FATAL(
fsrv,
"Reading autodictionary fail at position %u with %u bytes " "Reading autodictionary fail at position %u with %u bytes "
"left.", "left.",
offset, len); offset, len);
@ -777,7 +987,7 @@ void afl_fsrv_start(afl_forkserver_t *fsrv, char **argv,
if ((status & FS_OPT_ENABLED) == FS_OPT_ENABLED) { if ((status & FS_OPT_ENABLED) == FS_OPT_ENABLED) {
// workaround for recent afl++ versions // workaround for recent AFL++ versions
if ((status & FS_OPT_OLD_AFLPP_WORKAROUND) == FS_OPT_OLD_AFLPP_WORKAROUND) if ((status & FS_OPT_OLD_AFLPP_WORKAROUND) == FS_OPT_OLD_AFLPP_WORKAROUND)
status = (status & 0xf0ffffff); status = (status & 0xf0ffffff);
@ -849,7 +1059,7 @@ void afl_fsrv_start(afl_forkserver_t *fsrv, char **argv,
FATAL( FATAL(
"Target's coverage map size of %u is larger than the one this " "Target's coverage map size of %u is larger than the one this "
"afl++ is set with (%u). Either set AFL_MAP_SIZE=%u and restart " "AFL++ is set with (%u). Either set AFL_MAP_SIZE=%u and restart "
" afl-fuzz, or change MAP_SIZE_POW2 in config.h and recompile " " afl-fuzz, or change MAP_SIZE_POW2 in config.h and recompile "
"afl-fuzz", "afl-fuzz",
tmp_map_size, fsrv->map_size, tmp_map_size); tmp_map_size, fsrv->map_size, tmp_map_size);
@ -1016,7 +1226,7 @@ void afl_fsrv_start(afl_forkserver_t *fsrv, char **argv,
" - Less likely, there is a horrible bug in the fuzzer. If other " " - Less likely, there is a horrible bug in the fuzzer. If other "
"options\n" "options\n"
" fail, poke <afl-users@googlegroups.com> for troubleshooting " " fail, poke the Awesome Fuzzing Discord for troubleshooting "
"tips.\n"); "tips.\n");
} else { } else {
@ -1061,7 +1271,7 @@ void afl_fsrv_start(afl_forkserver_t *fsrv, char **argv,
" - Less likely, there is a horrible bug in the fuzzer. If other " " - Less likely, there is a horrible bug in the fuzzer. If other "
"options\n" "options\n"
" fail, poke <afl-users@googlegroups.com> for troubleshooting " " fail, poke the Awesome Fuzzing Discord for troubleshooting "
"tips.\n", "tips.\n",
stringify_mem_size(val_buf, sizeof(val_buf), fsrv->mem_limit << 20), stringify_mem_size(val_buf, sizeof(val_buf), fsrv->mem_limit << 20),
fsrv->mem_limit - 1); fsrv->mem_limit - 1);
@ -1111,7 +1321,7 @@ void afl_fsrv_start(afl_forkserver_t *fsrv, char **argv,
" Retry with setting AFL_MAP_SIZE=10000000.\n\n" " Retry with setting AFL_MAP_SIZE=10000000.\n\n"
"Otherwise there is a horrible bug in the fuzzer.\n" "Otherwise there is a horrible bug in the fuzzer.\n"
"Poke <afl-users@googlegroups.com> for troubleshooting tips.\n"); "Poke the Awesome Fuzzing Discord for troubleshooting tips.\n");
} else { } else {
@ -1160,7 +1370,7 @@ void afl_fsrv_start(afl_forkserver_t *fsrv, char **argv,
" - Less likely, there is a horrible bug in the fuzzer. If other " " - Less likely, there is a horrible bug in the fuzzer. If other "
"options\n" "options\n"
" fail, poke <afl-users@googlegroups.com> for troubleshooting " " fail, poke the Awesome Fuzzing Discord for troubleshooting "
"tips.\n", "tips.\n",
getenv(DEFER_ENV_VAR) getenv(DEFER_ENV_VAR)
? " - You are using deferred forkserver, but __AFL_INIT() is " ? " - You are using deferred forkserver, but __AFL_INIT() is "
@ -1194,13 +1404,7 @@ void afl_fsrv_kill(afl_forkserver_t *fsrv) {
fsrv->child_pid = -1; fsrv->child_pid = -1;
#ifdef __linux__ #ifdef __linux__
if (fsrv->nyx_mode) { afl_nyx_runner_kill(fsrv);
free(fsrv->nyx_aux_string);
fsrv->nyx_handlers->nyx_shutdown(fsrv->nyx_runner);
}
#endif #endif
} }
@ -1377,7 +1581,6 @@ afl_fsrv_run_target(afl_forkserver_t *fsrv, u32 timeout,
FATAL("FixMe: Nyx InvalidWriteToPayload handler is missing"); FATAL("FixMe: Nyx InvalidWriteToPayload handler is missing");
break; break;
case Abort: case Abort:
fsrv->nyx_handlers->nyx_shutdown(fsrv->nyx_runner);
FATAL("Error: Nyx abort occured..."); FATAL("Error: Nyx abort occured...");
case IoError: case IoError:
if (*stop_soon_p) { if (*stop_soon_p) {

View File

@ -465,7 +465,8 @@ save_if_interesting(afl_state_t *afl, void *mem, u32 len, u8 fault) {
u8 fn[PATH_MAX]; u8 fn[PATH_MAX];
u8 *queue_fn = ""; u8 *queue_fn = "";
u8 new_bits = 0, keeping = 0, res, classified = 0, is_timeout = 0; u8 new_bits = 0, keeping = 0, res, classified = 0, is_timeout = 0,
need_hash = 1;
s32 fd; s32 fd;
u64 cksum = 0; u64 cksum = 0;
@ -477,6 +478,7 @@ save_if_interesting(afl_state_t *afl, void *mem, u32 len, u8 fault) {
classify_counts(&afl->fsrv); classify_counts(&afl->fsrv);
classified = 1; classified = 1;
need_hash = 0;
cksum = hash64(afl->fsrv.trace_bits, afl->fsrv.map_size, HASH_CONST); cksum = hash64(afl->fsrv.trace_bits, afl->fsrv.map_size, HASH_CONST);
@ -499,6 +501,8 @@ save_if_interesting(afl_state_t *afl, void *mem, u32 len, u8 fault) {
new_bits = has_new_bits_unclassified(afl, afl->virgin_bits); new_bits = has_new_bits_unclassified(afl, afl->virgin_bits);
if (unlikely(new_bits)) { classified = 1; }
} }
if (likely(!new_bits)) { if (likely(!new_bits)) {
@ -577,12 +581,12 @@ save_if_interesting(afl_state_t *afl, void *mem, u32 len, u8 fault) {
} }
if (unlikely(!classified && new_bits)) { if (unlikely(need_hash && new_bits)) {
/* due to classify counts we have to recalculate the checksum */ /* due to classify counts we have to recalculate the checksum */
afl->queue_top->exec_cksum = afl->queue_top->exec_cksum =
hash64(afl->fsrv.trace_bits, afl->fsrv.map_size, HASH_CONST); hash64(afl->fsrv.trace_bits, afl->fsrv.map_size, HASH_CONST);
classified = 1; need_hash = 0;
} }

View File

@ -716,12 +716,25 @@ void read_testcases(afl_state_t *afl, u8 *directory) {
} }
// if (getenv("MYTEST")) afl->in_place_resume = 1;
if (nl_cnt) { if (nl_cnt) {
i = nl_cnt; u32 done = 0;
if (unlikely(afl->in_place_resume)) {
i = nl_cnt;
} else {
i = 0;
}
do { do {
--i; if (unlikely(afl->in_place_resume)) { --i; }
struct stat st; struct stat st;
u8 dfn[PATH_MAX]; u8 dfn[PATH_MAX];
@ -745,7 +758,7 @@ void read_testcases(afl_state_t *afl, u8 *directory) {
free(nl[i]); /* not tracked */ free(nl[i]); /* not tracked */
read_testcases(afl, fn2); read_testcases(afl, fn2);
ck_free(fn2); ck_free(fn2);
continue; goto next_entry;
} }
@ -754,7 +767,7 @@ void read_testcases(afl_state_t *afl, u8 *directory) {
if (!S_ISREG(st.st_mode) || !st.st_size || strstr(fn2, "/README.txt")) { if (!S_ISREG(st.st_mode) || !st.st_size || strstr(fn2, "/README.txt")) {
ck_free(fn2); ck_free(fn2);
continue; goto next_entry;
} }
@ -801,21 +814,23 @@ void read_testcases(afl_state_t *afl, u8 *directory) {
} }
/* next_entry:
if (unlikely(afl->schedule >= FAST && afl->schedule <= RARE)) { if (unlikely(afl->in_place_resume)) {
u64 cksum = hash64(afl->fsrv.trace_bits, afl->fsrv.map_size, if (unlikely(i == 0)) { done = 1; }
HASH_CONST); afl->queue_top->n_fuzz_entry = cksum % N_FUZZ_SIZE;
afl->n_fuzz[afl->queue_top->n_fuzz_entry] = 1;
} } else {
*/ if (unlikely(++i >= (u32)nl_cnt)) { done = 1; }
} while (i > 0); }
} while (!done);
} }
// if (getenv("MYTEST")) afl->in_place_resume = 0;
free(nl); /* not tracked */ free(nl); /* not tracked */
if (!afl->queued_items && directory == NULL) { if (!afl->queued_items && directory == NULL) {
@ -897,8 +912,10 @@ void perform_dry_run(afl_state_t *afl) {
if (res == afl->crash_mode || res == FSRV_RUN_NOBITS) { if (res == afl->crash_mode || res == FSRV_RUN_NOBITS) {
SAYF(cGRA " len = %u, map size = %u, exec speed = %llu us\n" cRST, SAYF(cGRA
q->len, q->bitmap_size, q->exec_us); " len = %u, map size = %u, exec speed = %llu us, hash = "
"%016llx\n" cRST,
q->len, q->bitmap_size, q->exec_us, q->exec_cksum);
} }
@ -995,7 +1012,7 @@ void perform_dry_run(afl_state_t *afl) {
" - Least likely, there is a horrible bug in the fuzzer. If " " - Least likely, there is a horrible bug in the fuzzer. If "
"other options\n" "other options\n"
" fail, poke <afl-users@googlegroups.com> for " " fail, poke the Awesome Fuzzing Discord for "
"troubleshooting tips.\n", "troubleshooting tips.\n",
stringify_mem_size(val_buf, sizeof(val_buf), stringify_mem_size(val_buf, sizeof(val_buf),
afl->fsrv.mem_limit << 20), afl->fsrv.mem_limit << 20),
@ -1024,7 +1041,7 @@ void perform_dry_run(afl_state_t *afl) {
" - Least likely, there is a horrible bug in the fuzzer. If " " - Least likely, there is a horrible bug in the fuzzer. If "
"other options\n" "other options\n"
" fail, poke <afl-users@googlegroups.com> for " " fail, poke the Awesome Fuzzing Discord for "
"troubleshooting tips.\n"); "troubleshooting tips.\n");
} }
@ -1153,14 +1170,14 @@ void perform_dry_run(afl_state_t *afl) {
u32 duplicates = 0, i; u32 duplicates = 0, i;
for (idx = 0; idx < afl->queued_items; idx++) { for (idx = 0; idx < afl->queued_items - 1; idx++) {
q = afl->queue_buf[idx]; q = afl->queue_buf[idx];
if (!q || q->disabled || q->cal_failed || !q->exec_cksum) { continue; } if (!q || q->disabled || q->cal_failed || !q->exec_cksum) { continue; }
u32 done = 0; u32 done = 0;
for (i = idx + 1; for (i = idx + 1;
i < afl->queued_items && !done && likely(afl->queue_buf[i]); i++) { likely(i < afl->queued_items && afl->queue_buf[i] && !done); ++i) {
struct queue_entry *p = afl->queue_buf[i]; struct queue_entry *p = afl->queue_buf[i];
if (p->disabled || p->cal_failed || !p->exec_cksum) { continue; } if (p->disabled || p->cal_failed || !p->exec_cksum) { continue; }
@ -1183,6 +1200,13 @@ void perform_dry_run(afl_state_t *afl) {
p->disabled = 1; p->disabled = 1;
p->perf_score = 0; p->perf_score = 0;
if (afl->debug) {
WARNF("Same coverage - %s is kept active, %s is disabled.",
q->fname, p->fname);
}
} else { } else {
if (!q->was_fuzzed) { if (!q->was_fuzzed) {
@ -1196,7 +1220,14 @@ void perform_dry_run(afl_state_t *afl) {
q->disabled = 1; q->disabled = 1;
q->perf_score = 0; q->perf_score = 0;
done = 1; if (afl->debug) {
WARNF("Same coverage - %s is kept active, %s is disabled.",
p->fname, q->fname);
}
done = 1; // end inner loop because outer loop entry is disabled now
} }

View File

@ -179,11 +179,19 @@ struct custom_mutator *load_custom_mutator(afl_state_t *afl, const char *fn) {
void *dh; void *dh;
struct custom_mutator *mutator = ck_alloc(sizeof(struct custom_mutator)); struct custom_mutator *mutator = ck_alloc(sizeof(struct custom_mutator));
mutator->name = fn; if (memchr(fn, '/', strlen(fn))) {
if (memchr(fn, '/', strlen(fn)))
mutator->name_short = strrchr(fn, '/') + 1; mutator->name_short = strdup(strrchr(fn, '/') + 1);
else
} else {
mutator->name_short = strdup(fn); mutator->name_short = strdup(fn);
}
if (strlen(mutator->name_short) > 22) { mutator->name_short[21] = 0; }
mutator->name = fn;
ACTF("Loading custom mutator library from '%s'...", fn); ACTF("Loading custom mutator library from '%s'...", fn);
dh = dlopen(fn, RTLD_NOW); dh = dlopen(fn, RTLD_NOW);

View File

@ -799,6 +799,7 @@ u8 fuzz_one_original(afl_state_t *afl) {
eff_map = afl_realloc(AFL_BUF_PARAM(eff), EFF_ALEN(len)); eff_map = afl_realloc(AFL_BUF_PARAM(eff), EFF_ALEN(len));
if (unlikely(!eff_map)) { PFATAL("alloc"); } if (unlikely(!eff_map)) { PFATAL("alloc"); }
memset(eff_map, 0, EFF_ALEN(len));
eff_map[0] = 1; eff_map[0] = 1;
if (EFF_APOS(len - 1) != 0) { if (EFF_APOS(len - 1) != 0) {
@ -1868,6 +1869,7 @@ custom_mutator_stage:
afl->stage_name = "custom mutator"; afl->stage_name = "custom mutator";
afl->stage_short = "custom"; afl->stage_short = "custom";
afl->stage_cur = 0;
afl->stage_val_type = STAGE_VAL_NONE; afl->stage_val_type = STAGE_VAL_NONE;
bool has_custom_fuzz = false; bool has_custom_fuzz = false;
u32 shift = unlikely(afl->custom_only) ? 7 : 8; u32 shift = unlikely(afl->custom_only) ? 7 : 8;
@ -1888,6 +1890,7 @@ custom_mutator_stage:
if (el->afl_custom_fuzz) { if (el->afl_custom_fuzz) {
afl->current_custom_fuzz = el; afl->current_custom_fuzz = el;
afl->stage_name = el->name_short;
if (el->afl_custom_fuzz_count) { if (el->afl_custom_fuzz_count) {
@ -2003,20 +2006,22 @@ custom_mutator_stage:
afl->queue_cur->stats_mutated += afl->stage_max; afl->queue_cur->stats_mutated += afl->stage_max;
#endif #endif
if (likely(afl->custom_only)) {
/* Skip other stages */
ret_val = 0;
goto abandon_entry;
}
/**************** /****************
* RANDOM HAVOC * * RANDOM HAVOC *
****************/ ****************/
havoc_stage: havoc_stage:
if (unlikely(afl->custom_only)) {
/* Force UI update */
show_stats(afl);
/* Skip other stages */
ret_val = 0;
goto abandon_entry;
}
afl->stage_cur_byte = -1; afl->stage_cur_byte = -1;
/* The havoc stage mutation code is also invoked when splicing files; if the /* The havoc stage mutation code is also invoked when splicing files; if the
@ -2028,7 +2033,7 @@ havoc_stage:
afl->stage_short = "havoc"; afl->stage_short = "havoc";
afl->stage_max = ((doing_det ? HAVOC_CYCLES_INIT : HAVOC_CYCLES) * afl->stage_max = ((doing_det ? HAVOC_CYCLES_INIT : HAVOC_CYCLES) *
perf_score / afl->havoc_div) >> perf_score / afl->havoc_div) >>
7; 8;
} else { } else {
@ -2037,7 +2042,7 @@ havoc_stage:
snprintf(afl->stage_name_buf, STAGE_BUF_SIZE, "splice %u", splice_cycle); snprintf(afl->stage_name_buf, STAGE_BUF_SIZE, "splice %u", splice_cycle);
afl->stage_name = afl->stage_name_buf; afl->stage_name = afl->stage_name_buf;
afl->stage_short = "splice"; afl->stage_short = "splice";
afl->stage_max = (SPLICE_HAVOC * perf_score / afl->havoc_div) >> 7; afl->stage_max = (SPLICE_HAVOC * perf_score / afl->havoc_div) >> 8;
} }
@ -3880,6 +3885,7 @@ static u8 mopt_common_fuzzing(afl_state_t *afl, MOpt_globals_t MOpt_globals) {
eff_map = afl_realloc(AFL_BUF_PARAM(eff), EFF_ALEN(len)); eff_map = afl_realloc(AFL_BUF_PARAM(eff), EFF_ALEN(len));
if (unlikely(!eff_map)) { PFATAL("alloc"); } if (unlikely(!eff_map)) { PFATAL("alloc"); }
memset(eff_map, 0, EFF_ALEN(len));
eff_map[0] = 1; eff_map[0] = 1;
if (EFF_APOS(len - 1) != 0) { if (EFF_APOS(len - 1) != 0) {
@ -4951,7 +4957,7 @@ pacemaker_fuzzing:
MOpt_globals.splice_stageformat, splice_cycle); MOpt_globals.splice_stageformat, splice_cycle);
afl->stage_name = afl->stage_name_buf; afl->stage_name = afl->stage_name_buf;
afl->stage_short = MOpt_globals.splice_stagenameshort; afl->stage_short = MOpt_globals.splice_stagenameshort;
afl->stage_max = (SPLICE_HAVOC * perf_score / afl->havoc_div) >> 7; afl->stage_max = (SPLICE_HAVOC * perf_score / afl->havoc_div) >> 8;
} }

View File

@ -219,11 +219,14 @@ static py_mutator_t *init_py_module(afl_state_t *afl, u8 *module_name) {
if (py_module != NULL) { if (py_module != NULL) {
u8 py_notrim = 0, py_idx; u8 py_notrim = 0;
/* init, required */
py_functions[PY_FUNC_INIT] = PyObject_GetAttrString(py_module, "init"); py_functions[PY_FUNC_INIT] = PyObject_GetAttrString(py_module, "init");
if (!py_functions[PY_FUNC_INIT]) if (!py_functions[PY_FUNC_INIT]) {
FATAL("init function not found in python module");
WARNF("init function not found in python module");
}
py_functions[PY_FUNC_FUZZ] = PyObject_GetAttrString(py_module, "fuzz"); py_functions[PY_FUNC_FUZZ] = PyObject_GetAttrString(py_module, "fuzz");
if (!py_functions[PY_FUNC_FUZZ]) if (!py_functions[PY_FUNC_FUZZ])
py_functions[PY_FUNC_FUZZ] = PyObject_GetAttrString(py_module, "mutate"); py_functions[PY_FUNC_FUZZ] = PyObject_GetAttrString(py_module, "mutate");
@ -231,12 +234,6 @@ static py_mutator_t *init_py_module(afl_state_t *afl, u8 *module_name) {
PyObject_GetAttrString(py_module, "describe"); PyObject_GetAttrString(py_module, "describe");
py_functions[PY_FUNC_FUZZ_COUNT] = py_functions[PY_FUNC_FUZZ_COUNT] =
PyObject_GetAttrString(py_module, "fuzz_count"); PyObject_GetAttrString(py_module, "fuzz_count");
if (!py_functions[PY_FUNC_FUZZ]) {
WARNF("fuzz function not found in python module");
}
py_functions[PY_FUNC_POST_PROCESS] = py_functions[PY_FUNC_POST_PROCESS] =
PyObject_GetAttrString(py_module, "post_process"); PyObject_GetAttrString(py_module, "post_process");
py_functions[PY_FUNC_INIT_TRIM] = py_functions[PY_FUNC_INIT_TRIM] =
@ -263,36 +260,6 @@ static py_mutator_t *init_py_module(afl_state_t *afl, u8 *module_name) {
if (!py_functions[PY_FUNC_DEINIT]) if (!py_functions[PY_FUNC_DEINIT])
WARNF("deinit function not found in python module"); WARNF("deinit function not found in python module");
for (py_idx = 0; py_idx < PY_FUNC_COUNT; ++py_idx) {
if (!py_functions[py_idx] || !PyCallable_Check(py_functions[py_idx])) {
if (py_idx >= PY_FUNC_INIT_TRIM && py_idx <= PY_FUNC_TRIM) {
// Implementing the trim API is optional for now
if (PyErr_Occurred()) { PyErr_Print(); }
py_notrim = 1;
} else if (py_idx >= PY_OPTIONAL) {
// Only _init and _deinit are not optional currently
if (PyErr_Occurred()) { PyErr_Print(); }
} else {
fprintf(stderr,
"Cannot find/call function with index %d in external "
"Python module.\n",
py_idx);
return NULL;
}
}
}
if (py_notrim) { if (py_notrim) {
py_functions[PY_FUNC_INIT_TRIM] = NULL; py_functions[PY_FUNC_INIT_TRIM] = NULL;
@ -345,6 +312,8 @@ static void init_py(afl_state_t *afl, py_mutator_t *py_mutator,
(void)afl; (void)afl;
if (py_mutator->py_functions[PY_FUNC_INIT] == NULL) { return; }
PyObject *py_args, *py_value; PyObject *py_args, *py_value;
/* Provide the init function a seed for the Python RNG */ /* Provide the init function a seed for the Python RNG */
@ -414,10 +383,21 @@ struct custom_mutator *load_custom_mutator_py(afl_state_t *afl,
struct custom_mutator *mutator; struct custom_mutator *mutator;
mutator = ck_alloc(sizeof(struct custom_mutator)); mutator = ck_alloc(sizeof(struct custom_mutator));
mutator->name = module_name; mutator->name = module_name;
ACTF("Loading Python mutator library from '%s'...", module_name); ACTF("Loading Python mutator library from '%s'...", module_name);
if (memchr(module_name, '/', strlen(module_name))) {
mutator->name_short = strdup(strrchr(module_name, '/') + 1);
} else {
mutator->name_short = strdup(module_name);
}
if (strlen(mutator->name_short) > 22) { mutator->name_short[21] = 0; }
py_mutator_t *py_mutator; py_mutator_t *py_mutator;
py_mutator = init_py_module(afl, module_name); py_mutator = init_py_module(afl, module_name);
mutator->data = py_mutator; mutator->data = py_mutator;

Some files were not shown because too many files have changed in this diff Show More