mirror of
https://github.com/AFLplusplus/AFLplusplus.git
synced 2025-06-14 02:58:08 +00:00
@ -24,7 +24,7 @@ import importlib.metadata
|
||||
|
||||
# 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", "")
|
||||
|
||||
|
||||
|
42
.github/workflows/ci.yml
vendored
42
.github/workflows/ci.yml
vendored
@ -14,7 +14,7 @@ jobs:
|
||||
runs-on: "${{ matrix.os }}"
|
||||
strategy:
|
||||
matrix:
|
||||
os: [ubuntu-22.04, ubuntu-20.04, ubuntu-18.04]
|
||||
os: [ubuntu-22.04, ubuntu-20.04]
|
||||
env:
|
||||
AFL_SKIP_CPUFREQ: 1
|
||||
AFL_I_DONT_CARE_ABOUT_MISSING_CRASHES: 1
|
||||
@ -36,23 +36,23 @@ jobs:
|
||||
run: make distrib ASAN_BUILD=1 NO_NYX=1
|
||||
- name: run tests
|
||||
run: sudo -E ./afl-system-config; make tests
|
||||
macos:
|
||||
runs-on: macOS-latest
|
||||
env:
|
||||
AFL_MAP_SIZE: 65536
|
||||
AFL_SKIP_CPUFREQ: 1
|
||||
AFL_I_DONT_CARE_ABOUT_MISSING_CRASHES: 1
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- name: install
|
||||
run: brew install make gcc llvm
|
||||
- name: fix install
|
||||
run: cd /usr/local/bin; ln -s gcc-11 gcc; ln -s g++-11 g++; which gcc; gcc -v
|
||||
- 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
|
||||
- name: frida
|
||||
run: export CC=/usr/local/Cellar/llvm/*/bin/clang; export CXX="$CC"++; cd frida_mode; gmake
|
||||
- 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
|
||||
- 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
|
||||
# macos:
|
||||
# runs-on: macOS-latest
|
||||
# env:
|
||||
# AFL_MAP_SIZE: 65536
|
||||
# AFL_SKIP_CPUFREQ: 1
|
||||
# AFL_I_DONT_CARE_ABOUT_MISSING_CRASHES: 1
|
||||
# steps:
|
||||
# - uses: actions/checkout@v3
|
||||
# - name: install
|
||||
# run: brew install make gcc llvm
|
||||
# - name: fix install
|
||||
# run: cd /usr/local/bin; ln -s gcc-11 gcc; ln -s g++-11 g++; which gcc; gcc -v
|
||||
# - 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
|
||||
# - name: frida
|
||||
# run: export CC=/usr/local/Cellar/llvm/*/bin/clang; export CXX="$CC"++; cd frida_mode; gmake
|
||||
# - 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
|
||||
# - 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
|
||||
|
@ -6,7 +6,7 @@
|
||||
#
|
||||
|
||||
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"
|
||||
|
||||
### 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 && \
|
||||
echo ". /etc/bash_completion" >> ~/.bashrc && \
|
||||
echo 'alias joe="joe --wordwrap --joe_state -nobackup"' >> ~/.bashrc && \
|
||||
echo "export PS1='"'[afl++ \h] \w \$ '"'" >> ~/.bashrc
|
||||
echo "export PS1='"'[AFL++ \h] \w \$ '"'" >> ~/.bashrc
|
||||
|
67
GNUmakefile
67
GNUmakefile
@ -39,7 +39,7 @@ ASAN_OPTIONS=detect_leaks=0
|
||||
SYS = $(shell uname -s)
|
||||
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
|
||||
override CFLAGS_OPT += -DNO_SPLICING
|
||||
@ -100,8 +100,13 @@ else
|
||||
LDFLAGS += $(SDK_LD)
|
||||
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"
|
||||
CFLAGS_OPT += -Wno-format-truncation
|
||||
LDFLAGS = -lkstat -lrt -lsocket -lnsl
|
||||
endif
|
||||
|
||||
@ -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-config 2>/dev/null)" ""
|
||||
PYTHON_INCLUDE ?= $(shell python3m-config --includes)
|
||||
PYTHON_VERSION ?= $(strip $(shell python3m --version 2>&1))
|
||||
PYTHON_INCLUDE := $(shell python3m-config --includes)
|
||||
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.
|
||||
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
|
||||
PYTHON_LIB ?= $(shell python3m-config --ldflags)
|
||||
PYTHON_LIB := $(shell python3m-config --ldflags)
|
||||
endif
|
||||
endif
|
||||
endif
|
||||
@ -194,13 +199,13 @@ endif
|
||||
ifeq "$(PYTHON_INCLUDE)" ""
|
||||
ifneq "$(shell command -v python3 2>/dev/null)" ""
|
||||
ifneq "$(shell command -v python3-config 2>/dev/null)" ""
|
||||
PYTHON_INCLUDE ?= $(shell python3-config --includes)
|
||||
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.
|
||||
PYTHON_INCLUDE := $(shell python3-config --includes)
|
||||
PYTHON_VERSION := $(strip $(shell python3 --version 2>&1))
|
||||
# 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"
|
||||
PYTHON_LIB ?= $(shell python3-config --libs --embed --ldflags)
|
||||
PYTHON_LIB := $(shell python3-config --libs --embed --ldflags)
|
||||
else
|
||||
PYTHON_LIB ?= $(shell python3-config --ldflags)
|
||||
PYTHON_LIB := $(shell python3-config --ldflags)
|
||||
endif
|
||||
endif
|
||||
endif
|
||||
@ -209,9 +214,9 @@ endif
|
||||
ifeq "$(PYTHON_INCLUDE)" ""
|
||||
ifneq "$(shell command -v python 2>/dev/null)" ""
|
||||
ifneq "$(shell command -v python-config 2>/dev/null)" ""
|
||||
PYTHON_INCLUDE ?= $(shell python-config --includes)
|
||||
PYTHON_LIB ?= $(shell python-config --ldflags)
|
||||
PYTHON_VERSION ?= $(strip $(shell python --version 2>&1))
|
||||
PYTHON_INCLUDE := $(shell python-config --includes)
|
||||
PYTHON_LIB := $(shell python-config --ldflags)
|
||||
PYTHON_VERSION := $(strip $(shell python --version 2>&1))
|
||||
endif
|
||||
endif
|
||||
endif
|
||||
@ -220,9 +225,9 @@ endif
|
||||
ifeq "$(PYTHON_INCLUDE)" ""
|
||||
ifneq "$(shell command -v python3.7 2>/dev/null)" ""
|
||||
ifneq "$(shell command -v python3.7-config 2>/dev/null)" ""
|
||||
PYTHON_INCLUDE ?= $(shell python3.7-config --includes)
|
||||
PYTHON_LIB ?= $(shell python3.7-config --ldflags)
|
||||
PYTHON_VERSION ?= $(strip $(shell python3.7 --version 2>&1))
|
||||
PYTHON_INCLUDE := $(shell python3.7-config --includes)
|
||||
PYTHON_LIB := $(shell python3.7-config --ldflags)
|
||||
PYTHON_VERSION := $(strip $(shell python3.7 --version 2>&1))
|
||||
endif
|
||||
endif
|
||||
endif
|
||||
@ -231,9 +236,9 @@ endif
|
||||
ifeq "$(PYTHON_INCLUDE)" ""
|
||||
ifneq "$(shell command -v python2.7 2>/dev/null)" ""
|
||||
ifneq "$(shell command -v python2.7-config 2>/dev/null)" ""
|
||||
PYTHON_INCLUDE ?= $(shell python2.7-config --includes)
|
||||
PYTHON_LIB ?= $(shell python2.7-config --ldflags)
|
||||
PYTHON_VERSION ?= $(strip $(shell python2.7 --version 2>&1))
|
||||
PYTHON_INCLUDE := $(shell python2.7-config --includes)
|
||||
PYTHON_LIB := $(shell python2.7-config --ldflags)
|
||||
PYTHON_VERSION := $(strip $(shell python2.7 --version 2>&1))
|
||||
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-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 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"
|
||||
@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
|
||||
@ -357,7 +362,7 @@ performance-test: source-only
|
||||
help:
|
||||
@echo "HELP --- the following make targets exist:"
|
||||
@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 "source-only: everything for source code fuzzing: nyx_mode, libdislocator, libtokencap"
|
||||
@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 "clean: cleans everything compiled (not downloads when on a checkout)"
|
||||
@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 "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)"
|
||||
@ -377,6 +382,7 @@ help:
|
||||
@echo Known build environment options:
|
||||
@echo "=========================================="
|
||||
@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 UBSAN_BUILD - compiles AFL++ tools with undefined behaviour sanitizer for debug purposes
|
||||
@echo DEBUG - no optimization, -ggdb3, all warnings and -Werror
|
||||
@ -388,10 +394,11 @@ help:
|
||||
@echo NO_NYX - disable building nyx mode dependencies
|
||||
@echo "NO_CORESIGHT - disable building coresight (arm64 only)"
|
||||
@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 "LLVM_CONFIG - if your distro doesn't use the standard name for llvm-config (e.g., Debian)"
|
||||
@echo "=========================================="
|
||||
@echo e.g.: make ASAN_BUILD=1
|
||||
@echo e.g.: make LLVM_CONFIG=llvm-config-16
|
||||
|
||||
.PHONY: test_x86
|
||||
ifndef AFL_NO_X86
|
||||
@ -430,7 +437,7 @@ endif
|
||||
|
||||
.PHONY: 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
|
||||
$(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
|
||||
|
||||
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
|
||||
$(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
|
||||
@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 )
|
||||
- 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
|
||||
@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
|
||||
@ -746,7 +753,7 @@ endif
|
||||
@echo
|
||||
|
||||
%.8: %
|
||||
@echo .TH $* 8 $(BUILD_DATE) "afl++" > $@
|
||||
@echo .TH $* 8 $(BUILD_DATE) "AFL++" > $@
|
||||
@echo .SH NAME >> $@
|
||||
@echo .B $* >> $@
|
||||
@echo >> $@
|
||||
@ -758,8 +765,8 @@ endif
|
||||
@./$* -hh 2>&1 | tail -n +4 >> $@
|
||||
@echo >> $@
|
||||
@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 The homepage of afl++ is: https://github.com/AFLplusplus/AFLplusplus >> $@
|
||||
@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 >> $@
|
||||
@echo .SH LICENSE >> $@
|
||||
@echo Apache License Version 2.0, January 2004 >> $@
|
||||
|
@ -175,7 +175,7 @@ all_done: test_build
|
||||
.NOTPARALLEL: clean
|
||||
|
||||
%.8: %
|
||||
@echo .TH $* 8 `date "+%Y-%m-%d"` "afl++" > ./$@
|
||||
@echo .TH $* 8 `date "+%Y-%m-%d"` "AFL++" > ./$@
|
||||
@echo .SH NAME >> ./$@
|
||||
@echo .B $* >> ./$@
|
||||
@echo >> ./$@
|
||||
@ -187,8 +187,8 @@ all_done: test_build
|
||||
@./$* -h 2>&1 | tail -n +4 >> ./$@
|
||||
@echo >> ./$@
|
||||
@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 The homepage of afl++ is: https://github.com/AFLplusplus/AFLplusplus >> ./$@
|
||||
@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 >> ./$@
|
||||
@echo .SH LICENSE >> ./$@
|
||||
@echo Apache License Version 2.0, January 2004 >> ./$@
|
||||
|
@ -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_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_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_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_BINDIR = $(shell $(LLVM_CONFIG) --bindir 2>/dev/null)
|
||||
LLVM_LIBDIR = $(shell $(LLVM_CONFIG) --libdir 2>/dev/null)
|
||||
@ -274,6 +274,11 @@ ifndef LLVM_DEBUG
|
||||
CFLAGS_SAFE += -Wno-deprecated
|
||||
endif
|
||||
|
||||
ifdef CODE_COVERAGE
|
||||
override CFLAGS_SAFE += -D__AFL_CODE_COVERAGE=1
|
||||
override LDFLAGS += -ldl
|
||||
endif
|
||||
|
||||
override CFLAGS += $(CFLAGS_SAFE)
|
||||
|
||||
ifdef AFL_TRACE_PC
|
||||
@ -294,6 +299,11 @@ endif
|
||||
CLANG_CPPFL = `$(LLVM_CONFIG) --cxxflags` -fno-rtti -fPIC $(CXXFLAGS) -Wno-deprecated-declarations
|
||||
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.
|
||||
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
|
||||
|
||||
./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
|
||||
endif
|
||||
|
||||
@ -505,7 +515,7 @@ install: all
|
||||
install -m 644 instrumentation/README.*.md $${DESTDIR}$(DOC_PATH)/
|
||||
|
||||
%.8: %
|
||||
@echo .TH $* 8 $(BUILD_DATE) "afl++" > ./$@
|
||||
@echo .TH $* 8 $(BUILD_DATE) "AFL++" > ./$@
|
||||
@echo .SH NAME >> ./$@
|
||||
@printf "%s" ".B $* \- " >> ./$@
|
||||
@./$* -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 >> ./$@
|
||||
@echo >> ./$@
|
||||
@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 The homepage of afl++ is: https://github.com/AFLplusplus/AFLplusplus >> ./$@
|
||||
@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 >> ./$@
|
||||
@echo .SH LICENSE >> ./$@
|
||||
@echo Apache License Version 2.0, January 2004 >> ./$@
|
||||
|
@ -1,10 +1,10 @@
|
||||
# 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:
|
||||
[https://github.com/AFLplusplus/AFLplusplus](https://github.com/AFLplusplus/AFLplusplus)
|
||||
@ -12,9 +12,9 @@ Repository:
|
||||
AFL++ is maintained by:
|
||||
|
||||
* Marc "van Hauser" Heuse <mh@mh-sec.de>
|
||||
* Heiko "hexcoder-" Eißfeldt <heiko.eissfeldt@hexco.de>
|
||||
* Andrea Fioraldi <andreafioraldi@gmail.com>
|
||||
* Dominik Maier <mail@dmnk.co>
|
||||
* Heiko "hexcoder-" Eißfeldt <heiko.eissfeldt@hexco.de>
|
||||
* Documentation: Jana Aydinbas <jana.aydinbas@gmail.com>
|
||||
|
||||
Originally developed by Michał "lcamtuf" Zalewski.
|
||||
|
5
TODO.md
5
TODO.md
@ -2,9 +2,8 @@
|
||||
|
||||
## Should
|
||||
|
||||
- splicing selection weighted?
|
||||
- support afl_custom_{send,post_process}, persistent and deferred fork
|
||||
server in afl-showmap
|
||||
- test cmplog for less than 16bit
|
||||
- support persistent and deferred fork server in afl-showmap?
|
||||
- better autodetection of shifting runtime timeout values
|
||||
- Update afl->pending_not_fuzzed for MOpt
|
||||
- afl-plot to support multiple plot_data
|
||||
|
133
afl-cmin
133
afl-cmin
@ -103,12 +103,14 @@ function usage() {
|
||||
" -o dir - output directory for minimized files\n" \
|
||||
"\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" \
|
||||
" -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" \
|
||||
" -Q - use binary-only instrumentation (QEMU mode)\n" \
|
||||
" -U - use unicorn-based instrumentation (unicorn mode)\n" \
|
||||
" -X - use Nyx mode\n" \
|
||||
"\n" \
|
||||
"Minimization settings:\n" \
|
||||
" -A - allow crashes and timeouts (not recommended)\n" \
|
||||
@ -118,20 +120,21 @@ function usage() {
|
||||
"For additional tips, please consult README.md\n" \
|
||||
"\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_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_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" \
|
||||
" (default: SIGTERM). If this is not set and AFL_KILL_SIGNAL is set,\n" \
|
||||
" this will be set to the same value as AFL_KILL_SIGNAL.\n" \
|
||||
"AFL_FORK_SERVER_KILL_SIGNAL: Signal delivered to fork server processes on\n" \
|
||||
" termination (default: SIGTERM). If this is not set and AFL_KILL_SIGNAL is\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_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_PRINT_FILENAMES: If set, the filename currently processed will be " \
|
||||
"printed to stdout\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
|
||||
}
|
||||
|
||||
@ -146,7 +149,7 @@ BEGIN {
|
||||
redirected = 0
|
||||
}
|
||||
|
||||
print "corpus minimization tool for afl++ (awk version)\n"
|
||||
print "corpus minimization tool for AFL++ (awk version)\n"
|
||||
|
||||
# defaults
|
||||
extra_par = ""
|
||||
@ -156,13 +159,19 @@ BEGIN {
|
||||
# process options
|
||||
Opterr = 1 # default is to diagnose
|
||||
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 (!Optarg) usage()
|
||||
if (in_dir) { print "Option "_go_c" is only allowed once" > "/dev/stderr"}
|
||||
in_dir = Optarg
|
||||
continue
|
||||
} 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 (!Optarg) usage()
|
||||
if (out_dir) { print "Option "_go_c" is only allowed once" > "/dev/stderr"}
|
||||
@ -218,6 +227,12 @@ BEGIN {
|
||||
unicorn_mode = 1
|
||||
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
|
||||
if (_go_c == "?") {
|
||||
exit 1
|
||||
} else
|
||||
@ -225,7 +240,7 @@ BEGIN {
|
||||
} # while options
|
||||
|
||||
if (!mem_limit) mem_limit = "none"
|
||||
if (!timeout) timeout = "none"
|
||||
if (!timeout) timeout = "5000"
|
||||
|
||||
# get program args
|
||||
i = 0
|
||||
@ -244,21 +259,30 @@ BEGIN {
|
||||
# Do a sanity check to discourage the use of /tmp, since we can't really
|
||||
# handle this safely from an awk script.
|
||||
|
||||
if (!ENVIRON["AFL_ALLOW_TMP"]) {
|
||||
dirlist[0] = in_dir
|
||||
dirlist[1] = target_bin
|
||||
dirlist[2] = out_dir
|
||||
dirlist[3] = stdin_file
|
||||
"pwd" | getline dirlist[4] # current directory
|
||||
for (dirind in dirlist) {
|
||||
dir = dirlist[dirind]
|
||||
#if (!ENVIRON["AFL_ALLOW_TMP"]) {
|
||||
# dirlist[0] = in_dir
|
||||
# dirlist[1] = target_bin
|
||||
# dirlist[2] = out_dir
|
||||
# dirlist[3] = stdin_file
|
||||
# "pwd" | getline dirlist[4] # current directory
|
||||
# for (dirind in dirlist) {
|
||||
# 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/) {
|
||||
print "[-] Error: do not use this script in /tmp or /var/tmp." > "/dev/stderr"
|
||||
if (threads && stdin_file) {
|
||||
print "[-] Error: -T and -f cannot be used together." > "/dev/stderr"
|
||||
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
|
||||
@ -291,7 +315,8 @@ BEGIN {
|
||||
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
|
||||
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 )) {
|
||||
print "[-] Error: binary '"target_bin"' doesn't appear to be instrumented." > "/dev/stderr"
|
||||
exit 1
|
||||
@ -340,6 +365,18 @@ BEGIN {
|
||||
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...
|
||||
if (0 != system("mkdir -p -m 0700 "trace_dir)) {
|
||||
print "[-] Error: Cannot create directory "trace_dir > "/dev/stderr"
|
||||
@ -449,9 +486,59 @@ BEGIN {
|
||||
# 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"'."
|
||||
|
||||
cur = 0;
|
||||
|
||||
if (threads > 1) {
|
||||
|
||||
print "[*] Creating " threads " parallel tasks with about " inputsperfile " each."
|
||||
for (i = 1; i <= threads; i++) {
|
||||
|
||||
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 ; } &")
|
||||
}
|
||||
}
|
||||
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
|
||||
@ -472,6 +559,8 @@ BEGIN {
|
||||
exit retval
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#######################################################
|
||||
# STEP 2: register smallest input file for each tuple #
|
||||
# STEP 3: copy that file (at most once) #
|
||||
|
130
afl-cmin.bash
130
afl-cmin.bash
@ -7,6 +7,8 @@
|
||||
#
|
||||
# Copyright 2014, 2015 Google Inc. All rights reserved.
|
||||
#
|
||||
# Copyright 2019-2023 AFLplusplus
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at:
|
||||
@ -36,7 +38,7 @@
|
||||
# array sizes.
|
||||
#
|
||||
|
||||
echo "corpus minimization tool for afl-fuzz by Michal Zalewski"
|
||||
echo "corpus minimization tool for afl-fuzz"
|
||||
echo
|
||||
|
||||
#########
|
||||
@ -46,14 +48,14 @@ echo
|
||||
# Process command-line options...
|
||||
|
||||
MEM_LIMIT=none
|
||||
TIMEOUT=none
|
||||
TIMEOUT=5000
|
||||
|
||||
unset IN_DIR OUT_DIR STDIN_FILE EXTRA_PAR MEM_LIMIT_GIVEN \
|
||||
AFL_CMIN_CRASHES_ONLY AFL_CMIN_ALLOW_ANY QEMU_MODE UNICORN_MODE
|
||||
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 T_ARG
|
||||
|
||||
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
|
||||
|
||||
@ -69,6 +71,7 @@ while getopts "+i:o:f:m:t:eOQUACh" opt; do
|
||||
;;
|
||||
"f")
|
||||
STDIN_FILE="$OPTARG"
|
||||
F_ARG=1
|
||||
;;
|
||||
"m")
|
||||
MEM_LIMIT="$OPTARG"
|
||||
@ -94,10 +97,21 @@ while getopts "+i:o:f:m:t:eOQUACh" opt; do
|
||||
EXTRA_PAR="$EXTRA_PAR -Q"
|
||||
QEMU_MODE=1
|
||||
;;
|
||||
"Y")
|
||||
EXTRA_PAR="$EXTRA_PAR -X"
|
||||
NYX_MODE=1
|
||||
;;
|
||||
"X")
|
||||
EXTRA_PAR="$EXTRA_PAR -X"
|
||||
NYX_MODE=1
|
||||
;;
|
||||
"U")
|
||||
EXTRA_PAR="$EXTRA_PAR -U"
|
||||
UNICORN_MODE=1
|
||||
;;
|
||||
"T")
|
||||
T_ARG="$OPTARG"
|
||||
;;
|
||||
"?")
|
||||
exit 1
|
||||
;;
|
||||
@ -122,12 +136,14 @@ Required parameters:
|
||||
|
||||
Execution control settings:
|
||||
|
||||
-f file - location read by the fuzzed program (stdin)
|
||||
-m megs - memory limit for child process ($MEM_LIMIT MB)
|
||||
-t msec - run time limit for child process (none)
|
||||
-T tasks - how many parallel processes to create (default=1, "all"=nproc)
|
||||
-f file - location read by the fuzzed program (default: stdin)
|
||||
-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)
|
||||
-Q - use binary-only instrumentation (QEMU mode)
|
||||
-U - use unicorn-based instrumentation (Unicorn mode)
|
||||
-X - use Nyx mode
|
||||
|
||||
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_PATH: last resort location to find the afl-showmap 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_
|
||||
exit 1
|
||||
fi
|
||||
@ -188,6 +206,11 @@ fi
|
||||
|
||||
# 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" -lt "5" ]; then
|
||||
@ -206,7 +229,8 @@ if [ ! "$TIMEOUT" = "none" ]; then
|
||||
|
||||
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`"
|
||||
|
||||
@ -217,9 +241,11 @@ if [ ! -f "$TARGET_BIN" -o ! -x "$TARGET_BIN" ]; then
|
||||
|
||||
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 ..."
|
||||
MAPSIZE=`AFL_DUMP_MAP_SIZE=1 "./$TARGET_BIN" 2>/dev/null`
|
||||
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
|
||||
echo "[-] Error: binary '$TARGET_BIN' doesn't appear to be instrumented." 1>&2
|
||||
@ -285,14 +311,34 @@ if [ ! -x "$SHOWMAP" ]; then
|
||||
exit 1
|
||||
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`))
|
||||
|
||||
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"
|
||||
exit 1
|
||||
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`
|
||||
|
||||
# Make sure that we're not dealing with a directory.
|
||||
@ -341,6 +387,18 @@ else
|
||||
|
||||
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!
|
||||
|
||||
#############################
|
||||
@ -349,6 +407,7 @@ fi
|
||||
|
||||
echo "[*] Obtaining traces for input files in '$IN_DIR'..."
|
||||
|
||||
if [ "$THREADS" = "" ]; then
|
||||
(
|
||||
|
||||
CUR=0
|
||||
@ -372,17 +431,58 @@ echo "[*] Obtaining traces for input files in '$IN_DIR'..."
|
||||
printf "\\r Processing file $CUR/$IN_COUNT... "
|
||||
|
||||
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
|
||||
|
||||
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 #
|
||||
|
4
afl-plot
4
afl-plot
@ -287,9 +287,9 @@ $PLOT_EG
|
||||
|
||||
_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
|
||||
|
||||
|
@ -110,7 +110,7 @@ if [ "$PLATFORM" = "Darwin" ] ; then
|
||||
sysctl kern.sysv.shmall=131072000
|
||||
echo Settings applied.
|
||||
echo
|
||||
if [ $(launchctl list 2>/dev/null | grep -q '\.ReportCrash$') ] ; then
|
||||
if $(launchctl list 2>/dev/null | grep -q '\.ReportCrash$') ; then
|
||||
echo
|
||||
echo Unloading the default crash reporter
|
||||
SL=/System/Library; PL=com.apple.ReportCrash
|
||||
@ -119,6 +119,7 @@ if [ "$PLATFORM" = "Darwin" ] ; then
|
||||
echo
|
||||
fi
|
||||
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
|
||||
DONE=1
|
||||
fi
|
||||
|
16
afl-whatsup
16
afl-whatsup
@ -70,10 +70,10 @@ if [ -d queue ]; then
|
||||
|
||||
fi
|
||||
|
||||
RED=`tput setaf 9 1 1`
|
||||
GREEN=`tput setaf 2 1 1`
|
||||
BLUE=`tput setaf 4 1 1`
|
||||
YELLOW=`tput setaf 11 1 1`
|
||||
RED=`tput setaf 9 1 1 2>/dev/null`
|
||||
GREEN=`tput setaf 2 1 1 2>/dev/null`
|
||||
BLUE=`tput setaf 4 1 1 2>/dev/null`
|
||||
YELLOW=`tput setaf 11 1 1 2>/dev/null`
|
||||
NC=`tput sgr0`
|
||||
RESET="$NC"
|
||||
|
||||
@ -88,6 +88,7 @@ TOTAL_TIME=0
|
||||
TOTAL_EXECS=0
|
||||
TOTAL_EPS=0
|
||||
TOTAL_CRASHES=0
|
||||
TOTAL_HANGS=0
|
||||
TOTAL_PFAV=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"
|
||||
. "$TMP"
|
||||
|
||||
DIR=$(dirname "$i")
|
||||
DIR=${DIR##*/}
|
||||
RUN_UNIX=$run_time
|
||||
RUN_DAYS=$((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
|
||||
|
||||
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
|
||||
|
||||
fi
|
||||
@ -189,6 +191,7 @@ for i in `find . -maxdepth 2 -iname fuzzer_stats | sort`; do
|
||||
TOTAL_EPS=$((TOTAL_EPS + EXEC_SEC))
|
||||
TOTAL_EXECS=$((TOTAL_EXECS + execs_done))
|
||||
TOTAL_CRASHES=$((TOTAL_CRASHES + saved_crashes))
|
||||
TOTAL_HANGS=$((TOTAL_HANGS + saved_hangs))
|
||||
TOTAL_PENDING=$((TOTAL_PENDING + pending_total))
|
||||
TOTAL_PFAV=$((TOTAL_PFAV + pending_favs))
|
||||
|
||||
@ -300,6 +303,7 @@ if [ "$ALIVE_CNT" -gt "1" ]; then
|
||||
fi
|
||||
|
||||
echo " Crashes saved : $TOTAL_CRASHES"
|
||||
echo " Hangs saved : $TOTAL_HANGS"
|
||||
echo "Cycles without finds : $TOTAL_WCOP"
|
||||
echo " Time without finds : $TOTAL_LAST_FIND"
|
||||
echo
|
||||
|
@ -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`.
|
||||
|
||||
## 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
|
||||
|
||||
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.
|
||||
|
||||
### 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
|
||||
|
||||
### Superion Mutators
|
||||
|
22
custom_mutators/aflpp_tritondse/README.md
Normal file
22
custom_mutators/aflpp_tritondse/README.md
Normal 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`)
|
220
custom_mutators/aflpp_tritondse/aflpp_tritondse.py
Normal file
220
custom_mutators/aflpp_tritondse/aflpp_tritondse.py
Normal 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
|
7
custom_mutators/atnwalk/Makefile
Normal file
7
custom_mutators/atnwalk/Makefile
Normal 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
|
43
custom_mutators/atnwalk/README.md
Normal file
43
custom_mutators/atnwalk/README.md
Normal 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)"
|
||||
```
|
539
custom_mutators/atnwalk/atnwalk.c
Normal file
539
custom_mutators/atnwalk/atnwalk.c
Normal 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);
|
||||
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -10,21 +10,21 @@
|
||||
// afl-fuzz -i in -o out -- ./test-instr -f /tmp/foo
|
||||
//
|
||||
|
||||
#include "custom_mutator_helpers.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <fcntl.h>
|
||||
|
||||
#include "afl-fuzz.h"
|
||||
|
||||
typedef struct my_mutator {
|
||||
|
||||
afl_t *afl;
|
||||
afl_state_t *afl;
|
||||
|
||||
} 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));
|
||||
if (!data) {
|
||||
|
@ -7,7 +7,7 @@
|
||||
*/
|
||||
|
||||
// You need to use -I/path/to/AFLplusplus/include -I.
|
||||
#include "custom_mutator_helpers.h"
|
||||
#include "afl-fuzz.h"
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
@ -26,19 +26,14 @@ static const char *commands[] = {
|
||||
|
||||
typedef struct my_mutator {
|
||||
|
||||
afl_t *afl;
|
||||
afl_state_t *afl;
|
||||
|
||||
// any additional data here!
|
||||
size_t trim_size_current;
|
||||
int trimmming_steps;
|
||||
int cur_step;
|
||||
|
||||
// Reused buffers:
|
||||
BUF_VAR(u8, fuzz);
|
||||
BUF_VAR(u8, data);
|
||||
BUF_VAR(u8, havoc);
|
||||
BUF_VAR(u8, trim);
|
||||
BUF_VAR(u8, post_process);
|
||||
u8 *mutated_out, *post_process_buf, *trim_buf;
|
||||
|
||||
} my_mutator_t;
|
||||
|
||||
@ -53,7 +48,7 @@ typedef struct my_mutator {
|
||||
* There may be multiple instances of this mutator in one afl-fuzz run!
|
||||
* 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()
|
||||
|
||||
@ -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;
|
||||
|
||||
return data;
|
||||
@ -96,31 +112,14 @@ size_t afl_custom_fuzz(my_mutator_t *data, uint8_t *buf, size_t buf_size,
|
||||
// the fuzzer
|
||||
size_t mutated_size = DATA_SIZE <= max_size ? DATA_SIZE : max_size;
|
||||
|
||||
// maybe_grow is optimized to be quick for reused buffers.
|
||||
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. */
|
||||
|
||||
}
|
||||
memcpy(data->mutated_out, buf, buf_size);
|
||||
|
||||
// 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
|
||||
int i;
|
||||
for (i = 0; i < 8; ++i) {
|
||||
if (mutated_size > max_size) { mutated_size = max_size; }
|
||||
|
||||
// Randomly perform one of the (no len modification) havoc mutations
|
||||
surgical_havoc_mutate(mutated_out, 3, mutated_size);
|
||||
|
||||
}
|
||||
|
||||
if (max_size > mutated_size) { mutated_size = max_size; }
|
||||
|
||||
*out_buf = mutated_out;
|
||||
*out_buf = data->mutated_out;
|
||||
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 buf_size, uint8_t **out_buf) {
|
||||
|
||||
uint8_t *post_process_buf =
|
||||
maybe_grow(BUF_PARAMS(data, post_process), buf_size + 5);
|
||||
if (!post_process_buf) {
|
||||
if (buf_size + 5 > MAX_FILE) { buf_size = MAX_FILE - 5; }
|
||||
|
||||
perror("custom mutator realloc failed.");
|
||||
*out_buf = NULL;
|
||||
return 0;
|
||||
memcpy(data->post_process_buf + 5, buf, buf_size);
|
||||
data->post_process_buf[0] = 'A';
|
||||
data->post_process_buf[1] = 'F';
|
||||
data->post_process_buf[2] = 'L';
|
||||
data->post_process_buf[3] = '+';
|
||||
data->post_process_buf[4] = '+';
|
||||
|
||||
}
|
||||
|
||||
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;
|
||||
*out_buf = data->post_process_buf;
|
||||
|
||||
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;
|
||||
|
||||
if (!maybe_grow(BUF_PARAMS(data, trim), buf_size)) {
|
||||
|
||||
perror("init_trim grow");
|
||||
return -1;
|
||||
|
||||
}
|
||||
|
||||
memcpy(data->trim_buf, buf, 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,
|
||||
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 (!*out_buf) {
|
||||
if (buf_size <= sizeof(size_t)) { return buf_size; }
|
||||
|
||||
perror("custom havoc: maybe_grow");
|
||||
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;
|
||||
size_t victim = rand() % (buf_size - sizeof(size_t));
|
||||
(*out_buf)[victim] += rand() % 10;
|
||||
|
||||
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) {
|
||||
|
||||
free(data->post_process_buf);
|
||||
free(data->havoc_buf);
|
||||
free(data->data_buf);
|
||||
free(data->fuzz_buf);
|
||||
free(data->mutated_out);
|
||||
free(data->trim_buf);
|
||||
free(data);
|
||||
|
||||
|
@ -45,9 +45,8 @@
|
||||
1) If you don't want to modify the test case, simply set `*out_buf = in_buf`
|
||||
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
|
||||
new one, return 0 or set `*out_buf = NULL`.
|
||||
new one, return 0.
|
||||
Use this sparingly - it's faster than running the target program
|
||||
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,
|
||||
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
|
||||
sure that all input files start with "GIF89a".
|
||||
|
||||
@ -72,7 +69,7 @@
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include "alloc-inl.h"
|
||||
#include "afl-fuzz.h"
|
||||
|
||||
/* Header that must be present at the beginning of every test case: */
|
||||
|
||||
@ -80,7 +77,6 @@
|
||||
|
||||
typedef struct post_state {
|
||||
|
||||
unsigned char *buf;
|
||||
size_t size;
|
||||
|
||||
} 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;
|
||||
|
||||
}
|
||||
@ -113,6 +100,10 @@ void *afl_custom_init(void *afl) {
|
||||
size_t afl_custom_post_process(post_state_t *data, unsigned char *in_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
|
||||
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. */
|
||||
|
||||
if (!memcmp(in_buf, HEADER, strlen(HEADER))) {
|
||||
|
||||
*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));
|
||||
if (!memcmp(in_buf, HEADER, strlen(HEADER))) { return len; }
|
||||
|
||||
/* 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 */
|
||||
void afl_custom_deinit(post_state_t *data) {
|
||||
|
||||
free(data->buf);
|
||||
free(data);
|
||||
|
||||
}
|
||||
|
@ -30,7 +30,7 @@
|
||||
#include <string.h>
|
||||
#include <zlib.h>
|
||||
#include <arpa/inet.h>
|
||||
#include "alloc-inl.h"
|
||||
#include "afl-fuzz.h"
|
||||
|
||||
/* 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) {
|
||||
|
||||
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
|
||||
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
|
||||
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) {
|
||||
|
||||
*(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;
|
||||
|
||||
}
|
||||
|
@ -1,6 +1,6 @@
|
||||
// This simple example just creates random buffer <= 100 filled with 'A'
|
||||
// needs -I /path/to/AFLplusplus/include
|
||||
#include "custom_mutator_helpers.h"
|
||||
#include "afl-fuzz.h"
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
@ -13,14 +13,14 @@
|
||||
|
||||
typedef struct my_mutator {
|
||||
|
||||
afl_t *afl;
|
||||
afl_state_t *afl;
|
||||
|
||||
// Reused buffers:
|
||||
BUF_VAR(u8, fuzz);
|
||||
u8 *fuzz_buf;
|
||||
|
||||
} 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);
|
||||
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;
|
||||
|
||||
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;
|
||||
if (size > max_size) size = max_size;
|
||||
u8 *mutated_out = maybe_grow(BUF_PARAMS(data, fuzz), size);
|
||||
if (!mutated_out) {
|
||||
|
||||
*out_buf = NULL;
|
||||
perror("custom mutator allocation (maybe_grow)");
|
||||
return 0; /* afl-fuzz will very likely error out after this. */
|
||||
memset(data->fuzz_buf, _FIXED_CHAR, size);
|
||||
|
||||
}
|
||||
|
||||
memset(mutated_out, _FIXED_CHAR, size);
|
||||
|
||||
*out_buf = mutated_out;
|
||||
*out_buf = data->fuzz_buf;
|
||||
return size;
|
||||
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -3,14 +3,14 @@
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "custom_mutator_helpers.h"
|
||||
#include "afl-fuzz.h"
|
||||
#include "mangle.h"
|
||||
|
||||
#define NUMBER_OF_MUTATIONS 5
|
||||
|
||||
uint8_t * queue_input;
|
||||
uint8_t *queue_input;
|
||||
size_t queue_input_size;
|
||||
afl_state_t * afl_struct;
|
||||
afl_state_t *afl_struct;
|
||||
run_t run;
|
||||
honggfuzz_t global;
|
||||
struct _dynfile_t dynfile;
|
||||
@ -18,8 +18,8 @@ struct _dynfile_t dynfile;
|
||||
typedef struct my_mutator {
|
||||
|
||||
afl_state_t *afl;
|
||||
run_t * run;
|
||||
u8 * mutator_buf;
|
||||
run_t *run;
|
||||
u8 *mutator_buf;
|
||||
unsigned int seed;
|
||||
unsigned int extras_cnt, a_extras_cnt;
|
||||
|
||||
@ -65,7 +65,7 @@ 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
|
||||
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_orig_queue) {
|
||||
|
||||
@ -97,7 +97,7 @@ uint8_t afl_custom_queue_new_entry(my_mutator_t * data,
|
||||
|
||||
}
|
||||
|
||||
return 0;
|
||||
return;
|
||||
|
||||
}
|
||||
|
||||
|
@ -39,11 +39,16 @@
|
||||
#include "libhfcommon/log.h"
|
||||
#include "libhfcommon/util.h"
|
||||
|
||||
static inline size_t mangle_LenLeft(run_t* run, size_t off) {
|
||||
static inline size_t mangle_LenLeft(run_t *run, size_t off) {
|
||||
|
||||
if (off >= run->dynfile->size) {
|
||||
|
||||
LOG_F("Offset is too large: off:%zu >= len:%zu", off, run->dynfile->size);
|
||||
|
||||
}
|
||||
|
||||
return (run->dynfile->size - off - 1);
|
||||
|
||||
}
|
||||
|
||||
/*
|
||||
@ -51,46 +56,46 @@ static inline size_t mangle_LenLeft(run_t* run, size_t off) {
|
||||
* Based on an idea by https://twitter.com/gamozolabs
|
||||
*/
|
||||
static inline size_t mangle_getLen(size_t max) {
|
||||
|
||||
if (max > _HF_INPUT_MAX_SIZE) {
|
||||
LOG_F("max (%zu) > _HF_INPUT_MAX_SIZE (%zu)", max, (size_t)_HF_INPUT_MAX_SIZE);
|
||||
}
|
||||
if (max == 0) {
|
||||
LOG_F("max == 0");
|
||||
}
|
||||
if (max == 1) {
|
||||
return 1;
|
||||
|
||||
LOG_F("max (%zu) > _HF_INPUT_MAX_SIZE (%zu)", max,
|
||||
(size_t)_HF_INPUT_MAX_SIZE);
|
||||
|
||||
}
|
||||
|
||||
if (max == 0) { LOG_F("max == 0"); }
|
||||
if (max == 1) { return 1; }
|
||||
|
||||
/* Give 50% chance the the uniform distribution */
|
||||
if (util_rnd64() & 1) {
|
||||
return (size_t)util_rndGet(1, max);
|
||||
}
|
||||
if (util_rnd64() & 1) { return (size_t)util_rndGet(1, max); }
|
||||
|
||||
/* effectively exprand() */
|
||||
return (size_t)util_rndGet(1, util_rndGet(1, max));
|
||||
|
||||
}
|
||||
|
||||
/* Prefer smaller values here, so use mangle_getLen() */
|
||||
static inline size_t mangle_getOffSet(run_t* run) {
|
||||
static inline size_t mangle_getOffSet(run_t *run) {
|
||||
|
||||
return mangle_getLen(run->dynfile->size) - 1;
|
||||
|
||||
}
|
||||
|
||||
/* Offset which can be equal to the file size */
|
||||
static inline size_t mangle_getOffSetPlus1(run_t* run) {
|
||||
static inline size_t mangle_getOffSetPlus1(run_t *run) {
|
||||
|
||||
size_t reqlen = HF_MIN(run->dynfile->size + 1, _HF_INPUT_MAX_SIZE);
|
||||
return mangle_getLen(reqlen) - 1;
|
||||
|
||||
}
|
||||
|
||||
static inline void mangle_Move(run_t* run, size_t off_from, size_t off_to, size_t len) {
|
||||
if (off_from >= run->dynfile->size) {
|
||||
return;
|
||||
}
|
||||
if (off_to >= run->dynfile->size) {
|
||||
return;
|
||||
}
|
||||
if (off_from == off_to) {
|
||||
return;
|
||||
}
|
||||
static inline void mangle_Move(run_t *run, size_t off_from, size_t off_to,
|
||||
size_t len) {
|
||||
|
||||
if (off_from >= run->dynfile->size) { return; }
|
||||
if (off_to >= run->dynfile->size) { return; }
|
||||
if (off_from == off_to) { return; }
|
||||
|
||||
size_t len_from = run->dynfile->size - off_from;
|
||||
len = HF_MIN(len, len_from);
|
||||
@ -99,65 +104,79 @@ static inline void mangle_Move(run_t* run, size_t off_from, size_t off_to, size_
|
||||
len = HF_MIN(len, len_to);
|
||||
|
||||
memmove(&run->dynfile->data[off_to], &run->dynfile->data[off_from], len);
|
||||
|
||||
}
|
||||
|
||||
static inline void mangle_Overwrite(
|
||||
run_t* run, size_t off, const uint8_t* src, size_t len, bool printable) {
|
||||
if (len == 0) {
|
||||
return;
|
||||
}
|
||||
static inline void mangle_Overwrite(run_t *run, size_t off, const uint8_t *src,
|
||||
size_t len, bool printable) {
|
||||
|
||||
if (len == 0) { return; }
|
||||
size_t maxToCopy = run->dynfile->size - off;
|
||||
if (len > maxToCopy) {
|
||||
len = maxToCopy;
|
||||
}
|
||||
if (len > maxToCopy) { len = maxToCopy; }
|
||||
|
||||
memmove(&run->dynfile->data[off], src, len);
|
||||
if (printable) {
|
||||
util_turnToPrintable(&run->dynfile->data[off], len);
|
||||
}
|
||||
if (printable) { util_turnToPrintable(&run->dynfile->data[off], len); }
|
||||
|
||||
}
|
||||
|
||||
static inline size_t mangle_Inflate(run_t* run, size_t off, size_t len, bool printable) {
|
||||
if (run->dynfile->size >= run->global->mutate.maxInputSz) {
|
||||
return 0;
|
||||
}
|
||||
static inline size_t mangle_Inflate(run_t *run, size_t off, size_t len,
|
||||
bool printable) {
|
||||
|
||||
if (run->dynfile->size >= run->global->mutate.maxInputSz) { return 0; }
|
||||
if (len > (run->global->mutate.maxInputSz - run->dynfile->size)) {
|
||||
|
||||
len = run->global->mutate.maxInputSz - run->dynfile->size;
|
||||
|
||||
}
|
||||
|
||||
input_setSize(run, run->dynfile->size + len);
|
||||
mangle_Move(run, off, off + len, run->dynfile->size);
|
||||
if (printable) {
|
||||
memset(&run->dynfile->data[off], ' ', len);
|
||||
}
|
||||
if (printable) { memset(&run->dynfile->data[off], ' ', len); }
|
||||
|
||||
return len;
|
||||
|
||||
}
|
||||
|
||||
static inline void mangle_Insert(
|
||||
run_t* run, size_t off, const uint8_t* val, size_t len, bool printable) {
|
||||
static inline void mangle_Insert(run_t *run, size_t off, const uint8_t *val,
|
||||
size_t len, bool printable) {
|
||||
|
||||
len = mangle_Inflate(run, off, len, printable);
|
||||
mangle_Overwrite(run, off, val, len, printable);
|
||||
|
||||
}
|
||||
|
||||
static inline void mangle_UseValue(run_t* run, const uint8_t* val, size_t len, bool printable) {
|
||||
static inline void mangle_UseValue(run_t *run, const uint8_t *val, size_t len,
|
||||
bool printable) {
|
||||
|
||||
if (util_rnd64() & 1) {
|
||||
|
||||
mangle_Overwrite(run, mangle_getOffSet(run), val, len, printable);
|
||||
|
||||
} else {
|
||||
|
||||
mangle_Insert(run, mangle_getOffSetPlus1(run), val, len, printable);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
static inline void mangle_UseValueAt(
|
||||
run_t* run, size_t off, const uint8_t* val, size_t len, bool printable) {
|
||||
static inline void mangle_UseValueAt(run_t *run, size_t off, const uint8_t *val,
|
||||
size_t len, bool printable) {
|
||||
|
||||
if (util_rnd64() & 1) {
|
||||
|
||||
mangle_Overwrite(run, off, val, len, printable);
|
||||
|
||||
} else {
|
||||
|
||||
mangle_Insert(run, off, val, len, printable);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
static void mangle_MemSwap(run_t* run, bool printable HF_ATTR_UNUSED) {
|
||||
static void mangle_MemSwap(run_t *run, bool printable HF_ATTR_UNUSED) {
|
||||
|
||||
/* No big deal if those two are overlapping */
|
||||
size_t off1 = mangle_getOffSet(run);
|
||||
size_t maxlen1 = run->dynfile->size - off1;
|
||||
@ -165,82 +184,105 @@ static void mangle_MemSwap(run_t* run, bool printable HF_ATTR_UNUSED) {
|
||||
size_t maxlen2 = run->dynfile->size - off2;
|
||||
size_t len = mangle_getLen(HF_MIN(maxlen1, maxlen2));
|
||||
|
||||
if (off1 == off2) {
|
||||
return;
|
||||
}
|
||||
if (off1 == off2) { return; }
|
||||
|
||||
for (size_t i = 0; i < (len / 2); i++) {
|
||||
|
||||
/*
|
||||
* First - from the head, next from the tail. Don't worry about layout of the overlapping
|
||||
* part - there's no good solution to that, and it can be left somewhat scrambled,
|
||||
* while still preserving the entropy
|
||||
* First - from the head, next from the tail. Don't worry about layout of
|
||||
* the overlapping part - there's no good solution to that, and it can be
|
||||
* left somewhat scrambled, while still preserving the entropy
|
||||
*/
|
||||
const uint8_t tmp1 = run->dynfile->data[off2 + i];
|
||||
run->dynfile->data[off2 + i] = run->dynfile->data[off1 + i];
|
||||
run->dynfile->data[off1 + i] = tmp1;
|
||||
const uint8_t tmp2 = run->dynfile->data[off2 + (len - 1) - i];
|
||||
run->dynfile->data[off2 + (len - 1) - i] = run->dynfile->data[off1 + (len - 1) - i];
|
||||
run->dynfile->data[off2 + (len - 1) - i] =
|
||||
run->dynfile->data[off1 + (len - 1) - i];
|
||||
run->dynfile->data[off1 + (len - 1) - i] = tmp2;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
static void mangle_MemCopy(run_t* run, bool printable HF_ATTR_UNUSED) {
|
||||
static void mangle_MemCopy(run_t *run, bool printable HF_ATTR_UNUSED) {
|
||||
|
||||
size_t off = mangle_getOffSet(run);
|
||||
size_t len = mangle_getLen(run->dynfile->size - off);
|
||||
|
||||
/* Use a temp buf, as Insert/Inflate can change source bytes */
|
||||
uint8_t* tmpbuf = (uint8_t*)util_Malloc(len);
|
||||
uint8_t *tmpbuf = (uint8_t *)util_Malloc(len);
|
||||
defer {
|
||||
|
||||
free(tmpbuf);
|
||||
|
||||
};
|
||||
|
||||
memmove(tmpbuf, &run->dynfile->data[off], len);
|
||||
|
||||
mangle_UseValue(run, tmpbuf, len, printable);
|
||||
|
||||
}
|
||||
|
||||
static void mangle_Bytes(run_t* run, bool printable) {
|
||||
static void mangle_Bytes(run_t *run, bool printable) {
|
||||
|
||||
uint16_t buf;
|
||||
if (printable) {
|
||||
util_rndBufPrintable((uint8_t*)&buf, sizeof(buf));
|
||||
|
||||
util_rndBufPrintable((uint8_t *)&buf, sizeof(buf));
|
||||
|
||||
} else {
|
||||
|
||||
buf = util_rnd64();
|
||||
|
||||
}
|
||||
|
||||
/* Overwrite with random 1-2-byte values */
|
||||
size_t toCopy = util_rndGet(1, 2);
|
||||
mangle_UseValue(run, (const uint8_t*)&buf, toCopy, printable);
|
||||
mangle_UseValue(run, (const uint8_t *)&buf, toCopy, printable);
|
||||
|
||||
}
|
||||
|
||||
static void mangle_ByteRepeat(run_t* run, bool printable) {
|
||||
static void mangle_ByteRepeat(run_t *run, bool printable) {
|
||||
|
||||
size_t off = mangle_getOffSet(run);
|
||||
size_t destOff = off + 1;
|
||||
size_t maxSz = run->dynfile->size - destOff;
|
||||
|
||||
/* No space to repeat */
|
||||
if (!maxSz) {
|
||||
|
||||
mangle_Bytes(run, printable);
|
||||
return;
|
||||
|
||||
}
|
||||
|
||||
size_t len = mangle_getLen(maxSz);
|
||||
if (util_rnd64() & 0x1) {
|
||||
|
||||
len = mangle_Inflate(run, destOff, len, printable);
|
||||
|
||||
}
|
||||
|
||||
memset(&run->dynfile->data[destOff], run->dynfile->data[off], len);
|
||||
|
||||
}
|
||||
|
||||
static void mangle_Bit(run_t* run, bool printable) {
|
||||
static void mangle_Bit(run_t *run, bool printable) {
|
||||
|
||||
size_t off = mangle_getOffSet(run);
|
||||
run->dynfile->data[off] ^= (uint8_t)(1U << util_rndGet(0, 7));
|
||||
if (printable) {
|
||||
util_turnToPrintable(&(run->dynfile->data[off]), 1);
|
||||
}
|
||||
if (printable) { util_turnToPrintable(&(run->dynfile->data[off]), 1); }
|
||||
|
||||
}
|
||||
|
||||
static const struct {
|
||||
|
||||
const uint8_t val[8];
|
||||
const size_t size;
|
||||
|
||||
} mangleMagicVals[] = {
|
||||
|
||||
/* 1B - No endianness */
|
||||
{"\x00\x00\x00\x00\x00\x00\x00\x00", 1},
|
||||
{"\x01\x00\x00\x00\x00\x00\x00\x00", 1},
|
||||
@ -472,160 +514,206 @@ static const struct {
|
||||
{"\x00\x00\x00\x00\x00\x00\x00\x80", 8},
|
||||
{"\x01\x00\x00\x00\x00\x00\x00\x80", 8},
|
||||
{"\xFE\xFF\xFF\xFF\xFF\xFF\xFF\xFF", 8},
|
||||
|
||||
};
|
||||
|
||||
static void mangle_Magic(run_t* run, bool printable) {
|
||||
static void mangle_Magic(run_t *run, bool printable) {
|
||||
|
||||
uint64_t choice = util_rndGet(0, ARRAYSIZE(mangleMagicVals) - 1);
|
||||
mangle_UseValue(run, mangleMagicVals[choice].val, mangleMagicVals[choice].size, printable);
|
||||
mangle_UseValue(run, mangleMagicVals[choice].val,
|
||||
mangleMagicVals[choice].size, printable);
|
||||
|
||||
}
|
||||
|
||||
static void mangle_StaticDict(run_t* run, bool printable) {
|
||||
static void mangle_StaticDict(run_t *run, bool printable) {
|
||||
|
||||
if (run->global->mutate.dictionaryCnt == 0) {
|
||||
|
||||
mangle_Bytes(run, printable);
|
||||
return;
|
||||
|
||||
}
|
||||
|
||||
uint64_t choice = util_rndGet(0, run->global->mutate.dictionaryCnt - 1);
|
||||
mangle_UseValue(run, run->global->mutate.dictionary[choice].val,
|
||||
run->global->mutate.dictionary[choice].len, printable);
|
||||
|
||||
}
|
||||
|
||||
static inline const uint8_t* mangle_FeedbackDict(run_t* run, size_t* len) {
|
||||
if (!run->global->feedback.cmpFeedback) {
|
||||
return NULL;
|
||||
}
|
||||
cmpfeedback_t* cmpf = run->global->feedback.cmpFeedbackMap;
|
||||
static inline const uint8_t *mangle_FeedbackDict(run_t *run, size_t *len) {
|
||||
|
||||
if (!run->global->feedback.cmpFeedback) { return NULL; }
|
||||
cmpfeedback_t *cmpf = run->global->feedback.cmpFeedbackMap;
|
||||
uint32_t cnt = ATOMIC_GET(cmpf->cnt);
|
||||
if (cnt == 0) {
|
||||
return NULL;
|
||||
}
|
||||
if (cnt > ARRAYSIZE(cmpf->valArr)) {
|
||||
cnt = ARRAYSIZE(cmpf->valArr);
|
||||
}
|
||||
if (cnt == 0) { return NULL; }
|
||||
if (cnt > ARRAYSIZE(cmpf->valArr)) { cnt = ARRAYSIZE(cmpf->valArr); }
|
||||
uint32_t choice = util_rndGet(0, cnt - 1);
|
||||
*len = (size_t)ATOMIC_GET(cmpf->valArr[choice].len);
|
||||
if (*len == 0) {
|
||||
return NULL;
|
||||
}
|
||||
if (*len == 0) { return NULL; }
|
||||
return cmpf->valArr[choice].val;
|
||||
|
||||
}
|
||||
|
||||
static void mangle_ConstFeedbackDict(run_t* run, bool printable) {
|
||||
static void mangle_ConstFeedbackDict(run_t *run, bool printable) {
|
||||
|
||||
size_t len;
|
||||
const uint8_t* val = mangle_FeedbackDict(run, &len);
|
||||
const uint8_t *val = mangle_FeedbackDict(run, &len);
|
||||
if (val == NULL) {
|
||||
|
||||
mangle_Bytes(run, printable);
|
||||
return;
|
||||
|
||||
}
|
||||
|
||||
mangle_UseValue(run, val, len, printable);
|
||||
|
||||
}
|
||||
|
||||
static void mangle_MemSet(run_t* run, bool printable) {
|
||||
static void mangle_MemSet(run_t *run, bool printable) {
|
||||
|
||||
size_t off = mangle_getOffSet(run);
|
||||
size_t len = mangle_getLen(run->dynfile->size - off);
|
||||
int val = printable ? (int)util_rndPrintable() : (int)util_rndGet(0, UINT8_MAX);
|
||||
int val =
|
||||
printable ? (int)util_rndPrintable() : (int)util_rndGet(0, UINT8_MAX);
|
||||
|
||||
if (util_rnd64() & 1) {
|
||||
len = mangle_Inflate(run, off, len, printable);
|
||||
}
|
||||
if (util_rnd64() & 1) { len = mangle_Inflate(run, off, len, printable); }
|
||||
|
||||
memset(&run->dynfile->data[off], val, len);
|
||||
|
||||
}
|
||||
|
||||
static void mangle_MemClr(run_t* run, bool printable) {
|
||||
static void mangle_MemClr(run_t *run, bool printable) {
|
||||
|
||||
size_t off = mangle_getOffSet(run);
|
||||
size_t len = mangle_getLen(run->dynfile->size - off);
|
||||
int val = printable ? ' ' : 0;
|
||||
|
||||
if (util_rnd64() & 1) {
|
||||
len = mangle_Inflate(run, off, len, printable);
|
||||
}
|
||||
if (util_rnd64() & 1) { len = mangle_Inflate(run, off, len, printable); }
|
||||
|
||||
memset(&run->dynfile->data[off], val, len);
|
||||
|
||||
}
|
||||
|
||||
static void mangle_RandomBuf(run_t* run, bool printable) {
|
||||
static void mangle_RandomBuf(run_t *run, bool printable) {
|
||||
|
||||
size_t off = mangle_getOffSet(run);
|
||||
size_t len = mangle_getLen(run->dynfile->size - off);
|
||||
|
||||
if (util_rnd64() & 1) {
|
||||
len = mangle_Inflate(run, off, len, printable);
|
||||
}
|
||||
if (util_rnd64() & 1) { len = mangle_Inflate(run, off, len, printable); }
|
||||
|
||||
if (printable) {
|
||||
|
||||
util_rndBufPrintable(&run->dynfile->data[off], len);
|
||||
|
||||
} else {
|
||||
|
||||
util_rndBuf(&run->dynfile->data[off], len);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
static inline void mangle_AddSubWithRange(
|
||||
run_t* run, size_t off, size_t varLen, uint64_t range, bool printable) {
|
||||
static inline void mangle_AddSubWithRange(run_t *run, size_t off, size_t varLen,
|
||||
uint64_t range, bool printable) {
|
||||
|
||||
int64_t delta = (int64_t)util_rndGet(0, range * 2) - (int64_t)range;
|
||||
|
||||
switch (varLen) {
|
||||
|
||||
case 1: {
|
||||
|
||||
run->dynfile->data[off] += delta;
|
||||
break;
|
||||
|
||||
}
|
||||
|
||||
case 2: {
|
||||
|
||||
int16_t val;
|
||||
memcpy(&val, &run->dynfile->data[off], sizeof(val));
|
||||
if (util_rnd64() & 0x1) {
|
||||
|
||||
val += delta;
|
||||
|
||||
} else {
|
||||
|
||||
/* Foreign endianess */
|
||||
val = __builtin_bswap16(val);
|
||||
val += delta;
|
||||
val = __builtin_bswap16(val);
|
||||
|
||||
}
|
||||
mangle_Overwrite(run, off, (uint8_t*)&val, varLen, printable);
|
||||
|
||||
mangle_Overwrite(run, off, (uint8_t *)&val, varLen, printable);
|
||||
break;
|
||||
|
||||
}
|
||||
|
||||
case 4: {
|
||||
|
||||
int32_t val;
|
||||
memcpy(&val, &run->dynfile->data[off], sizeof(val));
|
||||
if (util_rnd64() & 0x1) {
|
||||
|
||||
val += delta;
|
||||
|
||||
} else {
|
||||
|
||||
/* Foreign endianess */
|
||||
val = __builtin_bswap32(val);
|
||||
val += delta;
|
||||
val = __builtin_bswap32(val);
|
||||
|
||||
}
|
||||
mangle_Overwrite(run, off, (uint8_t*)&val, varLen, printable);
|
||||
|
||||
mangle_Overwrite(run, off, (uint8_t *)&val, varLen, printable);
|
||||
break;
|
||||
|
||||
}
|
||||
|
||||
case 8: {
|
||||
|
||||
int64_t val;
|
||||
memcpy(&val, &run->dynfile->data[off], sizeof(val));
|
||||
if (util_rnd64() & 0x1) {
|
||||
|
||||
val += delta;
|
||||
|
||||
} else {
|
||||
|
||||
/* Foreign endianess */
|
||||
val = __builtin_bswap64(val);
|
||||
val += delta;
|
||||
val = __builtin_bswap64(val);
|
||||
|
||||
}
|
||||
mangle_Overwrite(run, off, (uint8_t*)&val, varLen, printable);
|
||||
|
||||
mangle_Overwrite(run, off, (uint8_t *)&val, varLen, printable);
|
||||
break;
|
||||
|
||||
}
|
||||
|
||||
default: {
|
||||
|
||||
LOG_F("Unknown variable length size: %zu", varLen);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
static void mangle_AddSub(run_t* run, bool printable) {
|
||||
static void mangle_AddSub(run_t *run, bool printable) {
|
||||
|
||||
size_t off = mangle_getOffSet(run);
|
||||
|
||||
/* 1,2,4,8 */
|
||||
size_t varLen = 1U << util_rndGet(0, 3);
|
||||
if ((run->dynfile->size - off) < varLen) {
|
||||
varLen = 1;
|
||||
}
|
||||
if ((run->dynfile->size - off) < varLen) { varLen = 1; }
|
||||
|
||||
uint64_t range;
|
||||
switch (varLen) {
|
||||
|
||||
case 1:
|
||||
range = 16;
|
||||
break;
|
||||
@ -640,107 +728,140 @@ static void mangle_AddSub(run_t* run, bool printable) {
|
||||
break;
|
||||
default:
|
||||
LOG_F("Invalid operand size: %zu", varLen);
|
||||
|
||||
}
|
||||
|
||||
mangle_AddSubWithRange(run, off, varLen, range, printable);
|
||||
|
||||
}
|
||||
|
||||
static void mangle_IncByte(run_t* run, bool printable) {
|
||||
static void mangle_IncByte(run_t *run, bool printable) {
|
||||
|
||||
size_t off = mangle_getOffSet(run);
|
||||
if (printable) {
|
||||
|
||||
run->dynfile->data[off] = (run->dynfile->data[off] - 32 + 1) % 95 + 32;
|
||||
|
||||
} else {
|
||||
|
||||
run->dynfile->data[off] += (uint8_t)1UL;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
static void mangle_DecByte(run_t* run, bool printable) {
|
||||
static void mangle_DecByte(run_t *run, bool printable) {
|
||||
|
||||
size_t off = mangle_getOffSet(run);
|
||||
if (printable) {
|
||||
|
||||
run->dynfile->data[off] = (run->dynfile->data[off] - 32 + 94) % 95 + 32;
|
||||
|
||||
} else {
|
||||
|
||||
run->dynfile->data[off] -= (uint8_t)1UL;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
static void mangle_NegByte(run_t* run, bool printable) {
|
||||
static void mangle_NegByte(run_t *run, bool printable) {
|
||||
|
||||
size_t off = mangle_getOffSet(run);
|
||||
if (printable) {
|
||||
|
||||
run->dynfile->data[off] = 94 - (run->dynfile->data[off] - 32) + 32;
|
||||
|
||||
} else {
|
||||
|
||||
run->dynfile->data[off] = ~(run->dynfile->data[off]);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
static void mangle_Expand(run_t* run, bool printable) {
|
||||
static void mangle_Expand(run_t *run, bool printable) {
|
||||
|
||||
size_t off = mangle_getOffSet(run);
|
||||
size_t len;
|
||||
if (util_rnd64() % 16) {
|
||||
|
||||
len = mangle_getLen(HF_MIN(16, run->global->mutate.maxInputSz - off));
|
||||
|
||||
} else {
|
||||
|
||||
len = mangle_getLen(run->global->mutate.maxInputSz - off);
|
||||
|
||||
}
|
||||
|
||||
mangle_Inflate(run, off, len, printable);
|
||||
|
||||
}
|
||||
|
||||
static void mangle_Shrink(run_t* run, bool printable HF_ATTR_UNUSED) {
|
||||
if (run->dynfile->size <= 2U) {
|
||||
return;
|
||||
}
|
||||
static void mangle_Shrink(run_t *run, bool printable HF_ATTR_UNUSED) {
|
||||
|
||||
if (run->dynfile->size <= 2U) { return; }
|
||||
|
||||
size_t off_start = mangle_getOffSet(run);
|
||||
size_t len = mangle_LenLeft(run, off_start);
|
||||
if (len == 0) {
|
||||
return;
|
||||
}
|
||||
if (len == 0) { return; }
|
||||
if (util_rnd64() % 16) {
|
||||
|
||||
len = mangle_getLen(HF_MIN(16, len));
|
||||
|
||||
} else {
|
||||
|
||||
len = mangle_getLen(len);
|
||||
|
||||
}
|
||||
|
||||
size_t off_end = off_start + len;
|
||||
size_t len_to_move = run->dynfile->size - off_end;
|
||||
|
||||
mangle_Move(run, off_end, off_start, len_to_move);
|
||||
input_setSize(run, run->dynfile->size - len);
|
||||
|
||||
}
|
||||
static void mangle_ASCIINum(run_t* run, bool printable) {
|
||||
|
||||
static void mangle_ASCIINum(run_t *run, bool printable) {
|
||||
|
||||
size_t len = util_rndGet(2, 8);
|
||||
|
||||
char buf[20];
|
||||
snprintf(buf, sizeof(buf), "%-19" PRId64, (int64_t)util_rnd64());
|
||||
|
||||
mangle_UseValue(run, (const uint8_t*)buf, len, printable);
|
||||
mangle_UseValue(run, (const uint8_t *)buf, len, printable);
|
||||
|
||||
}
|
||||
|
||||
static void mangle_ASCIINumChange(run_t* run, bool printable) {
|
||||
static void mangle_ASCIINumChange(run_t *run, bool printable) {
|
||||
|
||||
size_t off = mangle_getOffSet(run);
|
||||
|
||||
/* Find a digit */
|
||||
for (; off < run->dynfile->size; off++) {
|
||||
if (isdigit(run->dynfile->data[off])) {
|
||||
break;
|
||||
}
|
||||
|
||||
if (isdigit(run->dynfile->data[off])) { break; }
|
||||
|
||||
}
|
||||
|
||||
size_t left = run->dynfile->size - off;
|
||||
if (left == 0) {
|
||||
return;
|
||||
}
|
||||
if (left == 0) { return; }
|
||||
|
||||
size_t len = 0;
|
||||
uint64_t val = 0;
|
||||
/* 20 is maximum lenght of a string representing a 64-bit unsigned value */
|
||||
for (len = 0; (len < 20) && (len < left); len++) {
|
||||
|
||||
char c = run->dynfile->data[off + len];
|
||||
if (!isdigit(c)) {
|
||||
break;
|
||||
}
|
||||
if (!isdigit(c)) { break; }
|
||||
val *= 10;
|
||||
val += (c - '0');
|
||||
|
||||
}
|
||||
|
||||
switch (util_rndGet(0, 7)) {
|
||||
|
||||
case 0:
|
||||
val++;
|
||||
break;
|
||||
@ -767,43 +888,56 @@ static void mangle_ASCIINumChange(run_t* run, bool printable) {
|
||||
break;
|
||||
default:
|
||||
LOG_F("Invalid choice");
|
||||
|
||||
};
|
||||
|
||||
char buf[20];
|
||||
snprintf(buf, sizeof(buf), "%-19" PRIu64, val);
|
||||
|
||||
mangle_UseValueAt(run, off, (const uint8_t*)buf, len, printable);
|
||||
mangle_UseValueAt(run, off, (const uint8_t *)buf, len, printable);
|
||||
|
||||
}
|
||||
|
||||
static void mangle_Splice(run_t* run, bool printable) {
|
||||
static void mangle_Splice(run_t *run, bool printable) {
|
||||
|
||||
if (run->global->feedback.dynFileMethod == _HF_DYNFILE_NONE) {
|
||||
|
||||
mangle_Bytes(run, printable);
|
||||
return;
|
||||
|
||||
}
|
||||
|
||||
size_t sz = 0;
|
||||
const uint8_t* buf = input_getRandomInputAsBuf(run, &sz);
|
||||
const uint8_t *buf = input_getRandomInputAsBuf(run, &sz);
|
||||
if (!buf) {
|
||||
|
||||
LOG_E("input_getRandomInputAsBuf() returned no input");
|
||||
mangle_Bytes(run, printable);
|
||||
return;
|
||||
|
||||
}
|
||||
|
||||
if (!sz) {
|
||||
|
||||
mangle_Bytes(run, printable);
|
||||
return;
|
||||
|
||||
}
|
||||
|
||||
size_t remoteOff = mangle_getLen(sz) - 1;
|
||||
size_t len = mangle_getLen(sz - remoteOff);
|
||||
mangle_UseValue(run, &buf[remoteOff], len, printable);
|
||||
|
||||
}
|
||||
|
||||
static void mangle_Resize(run_t* run, bool printable) {
|
||||
static void mangle_Resize(run_t *run, bool printable) {
|
||||
|
||||
ssize_t oldsz = run->dynfile->size;
|
||||
ssize_t newsz = 0;
|
||||
|
||||
uint64_t choice = util_rndGet(0, 32);
|
||||
switch (choice) {
|
||||
|
||||
case 0: /* Set new size arbitrarily */
|
||||
newsz = (ssize_t)util_rndGet(1, run->global->mutate.maxInputSz);
|
||||
break;
|
||||
@ -825,83 +959,98 @@ static void mangle_Resize(run_t* run, bool printable) {
|
||||
default:
|
||||
LOG_F("Illegal value from util_rndGet: %" PRIu64, choice);
|
||||
break;
|
||||
|
||||
}
|
||||
if (newsz < 1) {
|
||||
newsz = 1;
|
||||
}
|
||||
|
||||
if (newsz < 1) { newsz = 1; }
|
||||
if (newsz > (ssize_t)run->global->mutate.maxInputSz) {
|
||||
|
||||
newsz = run->global->mutate.maxInputSz;
|
||||
|
||||
}
|
||||
|
||||
input_setSize(run, (size_t)newsz);
|
||||
if (newsz > oldsz) {
|
||||
if (printable) {
|
||||
memset(&run->dynfile->data[oldsz], ' ', newsz - oldsz);
|
||||
}
|
||||
|
||||
if (printable) { memset(&run->dynfile->data[oldsz], ' ', newsz - oldsz); }
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void mangle_mangleContent(run_t* run, int speed_factor) {
|
||||
void mangle_mangleContent(run_t *run, int speed_factor) {
|
||||
|
||||
static void (*const mangleFuncs[])(run_t * run, bool printable) = {
|
||||
mangle_Shrink,
|
||||
mangle_Expand,
|
||||
mangle_Bit,
|
||||
mangle_IncByte,
|
||||
mangle_DecByte,
|
||||
mangle_NegByte,
|
||||
mangle_AddSub,
|
||||
mangle_MemSet,
|
||||
mangle_MemClr,
|
||||
mangle_MemSwap,
|
||||
mangle_MemCopy,
|
||||
mangle_Bytes,
|
||||
mangle_ASCIINum,
|
||||
mangle_ASCIINumChange,
|
||||
mangle_ByteRepeat,
|
||||
mangle_Magic,
|
||||
mangle_StaticDict,
|
||||
mangle_ConstFeedbackDict,
|
||||
mangle_RandomBuf,
|
||||
mangle_Splice,
|
||||
|
||||
mangle_Shrink, mangle_Expand, mangle_Bit,
|
||||
mangle_IncByte, mangle_DecByte, mangle_NegByte,
|
||||
mangle_AddSub, mangle_MemSet, mangle_MemClr,
|
||||
mangle_MemSwap, mangle_MemCopy, mangle_Bytes,
|
||||
mangle_ASCIINum, mangle_ASCIINumChange, mangle_ByteRepeat,
|
||||
mangle_Magic, mangle_StaticDict, mangle_ConstFeedbackDict,
|
||||
mangle_RandomBuf, mangle_Splice,
|
||||
|
||||
};
|
||||
|
||||
if (run->mutationsPerRun == 0U) {
|
||||
return;
|
||||
}
|
||||
if (run->mutationsPerRun == 0U) { return; }
|
||||
if (run->dynfile->size == 0U) {
|
||||
|
||||
mangle_Resize(run, /* printable= */ run->global->cfg.only_printable);
|
||||
|
||||
}
|
||||
|
||||
uint64_t changesCnt = run->global->mutate.mutationsPerRun;
|
||||
|
||||
if (speed_factor < 5) {
|
||||
|
||||
changesCnt = util_rndGet(1, run->global->mutate.mutationsPerRun);
|
||||
|
||||
} else if (speed_factor < 10) {
|
||||
|
||||
changesCnt = run->global->mutate.mutationsPerRun;
|
||||
|
||||
} else {
|
||||
|
||||
changesCnt = HF_MIN(speed_factor, 10);
|
||||
changesCnt = HF_MAX(changesCnt, (run->global->mutate.mutationsPerRun * 5));
|
||||
|
||||
}
|
||||
|
||||
/* If last coverage acquisition was more than 5 secs ago, use splicing more frequently */
|
||||
/* If last coverage acquisition was more than 5 secs ago, use splicing more
|
||||
* frequently */
|
||||
if ((time(NULL) - ATOMIC_GET(run->global->timing.lastCovUpdate)) > 5) {
|
||||
|
||||
if (util_rnd64() & 0x1) {
|
||||
|
||||
mangle_Splice(run, run->global->cfg.only_printable);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
for (uint64_t x = 0; x < changesCnt; x++) {
|
||||
|
||||
if (run->global->feedback.cmpFeedback && (util_rnd64() & 0x1)) {
|
||||
|
||||
/*
|
||||
* mangle_ConstFeedbackDict() is quite powerful if the dynamic feedback dictionary
|
||||
* exists. If so, give it 50% chance of being used among all mangling functions.
|
||||
* mangle_ConstFeedbackDict() is quite powerful if the dynamic feedback
|
||||
* dictionary exists. If so, give it 50% chance of being used among all
|
||||
* mangling functions.
|
||||
*/
|
||||
mangle_ConstFeedbackDict(run, /* printable= */ run->global->cfg.only_printable);
|
||||
mangle_ConstFeedbackDict(
|
||||
run, /* printable= */ run->global->cfg.only_printable);
|
||||
|
||||
} else {
|
||||
|
||||
uint64_t choice = util_rndGet(0, ARRAYSIZE(mangleFuncs) - 1);
|
||||
mangleFuncs[choice](run, /* printable= */ run->global->cfg.only_printable);
|
||||
mangleFuncs[choice](run,
|
||||
/* printable= */ run->global->cfg.only_printable);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
wmb();
|
||||
|
||||
}
|
||||
|
||||
|
@ -1 +0,0 @@
|
||||
../examples/custom_mutator_helpers.h
|
@ -1,6 +1,5 @@
|
||||
// This simple example just creates random buffer <= 100 filled with 'A'
|
||||
// needs -I /path/to/AFLplusplus/include
|
||||
//#include "custom_mutator_helpers.h"
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
@ -8,19 +7,17 @@
|
||||
#include <stdio.h>
|
||||
|
||||
#include "radamsa.h"
|
||||
#include "custom_mutator_helpers.h"
|
||||
#include "afl-fuzz.h"
|
||||
|
||||
typedef struct my_mutator {
|
||||
|
||||
afl_t *afl;
|
||||
|
||||
afl_state_t *afl;
|
||||
u8 *mutator_buf;
|
||||
|
||||
unsigned int seed;
|
||||
|
||||
} 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);
|
||||
my_mutator_t *data = calloc(1, sizeof(my_mutator_t));
|
||||
|
@ -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
|
||||
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
|
||||
[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
|
||||
|
14
custom_mutators/symqemu/Makefile
Normal file
14
custom_mutators/symqemu/Makefile
Normal 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
|
19
custom_mutators/symqemu/README.md
Normal file
19
custom_mutators/symqemu/README.md
Normal 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.
|
424
custom_mutators/symqemu/symqemu.c
Normal file
424
custom_mutators/symqemu/symqemu.c
Normal 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);
|
||||
|
||||
}
|
||||
|
@ -3,24 +3,64 @@
|
||||
This is the list of all noteworthy changes made in every public
|
||||
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:
|
||||
- ensure temporary file descriptor is closed when not used
|
||||
- added `AFL_NO_WARN_INSTABILITY`
|
||||
- added `AFL_FRIDA_STATS_INTERVAL`
|
||||
- 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:
|
||||
- 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 for LLVMFuzzerTestOneInput -1 return
|
||||
- LTO autoken and llvm_mode: added AFL_LLVM_DICT2FILE_NO_MAIN support
|
||||
- qemu_mode:
|
||||
- fix _RANGES envs to allow hyphens in the filenames
|
||||
- new custom module: autotoken, grammar free fuzzer for text inputs
|
||||
- LTO autoken and llvm_mode: added AFL_LLVM_DICT2FILE_NO_MAIN support
|
||||
- basic riscv 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
|
||||
- unicorn_mode: updated and minor issues fixed
|
||||
- frida_mode: fix issue on MacOS
|
||||
- new custom module: autotoken, a grammar free fuzzer for text inputs
|
||||
- fixed custom mutator C examples
|
||||
- more minor fixes and cross-platform support
|
||||
|
||||
### Version ++4.05c (release)
|
||||
@ -199,7 +239,7 @@
|
||||
afl-showmap and other tools.
|
||||
- afl-cc:
|
||||
- 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
|
||||
- fix for shared linking on MacOS
|
||||
- better selective instrumentation AFL_LLVM_{ALLOW|DENY}LIST
|
||||
|
11
docs/FAQ.md
11
docs/FAQ.md
@ -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
|
||||
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
|
||||
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
|
||||
@ -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
|
||||
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
|
||||
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>
|
||||
|
||||
<details>
|
||||
|
@ -3,9 +3,8 @@
|
||||
## Linux on x86
|
||||
|
||||
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 -
|
||||
hence afl-clang-lto is available) or just pull directly from the Docker Hub
|
||||
(for x86_64 and arm64):
|
||||
You can use the [Dockerfile](../Dockerfile) or just pull directly from the
|
||||
Docker Hub (for x86_64 and arm64):
|
||||
|
||||
```shell
|
||||
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
|
||||
is to build and install everything:
|
||||
|
||||
NOTE: depending on your Debian/Ubuntu/Kali/... release, replace `-12` with
|
||||
whatever llvm version is available. We recommend llvm 12, 13 or 14.
|
||||
NOTE: depending on your Debian/Ubuntu/Kali/... release, replace `-14` with
|
||||
whatever llvm version is available. We recommend llvm 13, 14, 15 or 16.
|
||||
|
||||
```shell
|
||||
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
|
||||
# try to install llvm 12 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
|
||||
# try to install llvm 14 and install the distro default if that fails
|
||||
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 ninja-build # for QEMU mode
|
||||
git clone https://github.com/AFLplusplus/AFLplusplus
|
||||
@ -51,7 +50,7 @@ make source-only
|
||||
|
||||
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,
|
||||
qemu_mode, frida_mode, unicorn_mode, coresight_mode, libdislocator,
|
||||
libtokencap
|
||||
@ -79,22 +78,20 @@ make STATIC=1
|
||||
These build options exist:
|
||||
|
||||
* 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
|
||||
* UBSAN_BUILD - compiles AFL++ tools with undefined behaviour sanitizer for
|
||||
debug purposes
|
||||
* UBSAN_BUILD - compiles AFL++ tools with undefined behaviour sanitizer for debug purposes
|
||||
* DEBUG - no optimization, -ggdb3, all warnings and -Werror
|
||||
* LLVM_DEBUG - shows llvm deprecation warnings
|
||||
* PROFILING - compile afl-fuzz with profiling information
|
||||
* INTROSPECTION - compile afl-fuzz with mutation introspection
|
||||
* NO_PYTHON - disable python support
|
||||
* NO_SPLICING - disables splicing mutation in afl-fuzz, not recommended for
|
||||
normal fuzzing
|
||||
* NO_SPLICING - disables splicing mutation in afl-fuzz, not recommended for normal fuzzing
|
||||
* NO_NYX - disable building nyx mode dependencies
|
||||
* NO_CORESIGHT - disable building coresight (arm64 only)
|
||||
* NO_UNICORN_ARM64 - disable building unicorn on arm64
|
||||
* AFL_NO_X86 - if compiling on non-intel/amd platforms
|
||||
* LLVM_CONFIG - if your distro doesn't use the standard name for llvm-config
|
||||
(e.g., Debian)
|
||||
* LLVM_CONFIG - if your distro doesn't use the standard name for llvm-config (e.g., Debian)
|
||||
|
||||
e.g.: `make LLVM_CONFIG=llvm-config-14`
|
||||
|
||||
|
@ -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
|
||||
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
|
||||
coding and/or disassembly and is effectively possible only with `afl-clang-fast`
|
||||
`PCGUARD` and `afl-clang-lto` `LTO` instrumentation.
|
||||
|
@ -118,7 +118,7 @@ def deinit(): # optional for Python
|
||||
|
||||
### Custom Mutation
|
||||
|
||||
- `init`:
|
||||
- `init` (optional in Python):
|
||||
|
||||
This method is called when AFL++ starts up and is used to seed RNG and set
|
||||
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,
|
||||
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):
|
||||
|
||||
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
|
||||
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.
|
||||
|
||||
@ -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:
|
||||
`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
|
||||
|
||||
For C/C++ mutators, the source code must be compiled as a shared object:
|
||||
|
@ -156,7 +156,7 @@ Available options:
|
||||
- LTO - LTO instrumentation
|
||||
- NATIVE - clang's original pcguard based instrumentation
|
||||
- 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
|
||||
|
||||
@ -240,7 +240,9 @@ combined.
|
||||
the default `0x10000`. A value of 0 or empty sets the map address to be
|
||||
dynamic (the original AFL way, which is slower).
|
||||
- `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
|
||||
[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
|
||||
(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
|
||||
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`.
|
||||
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.
|
||||
|
||||
- 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
|
||||
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
|
||||
of the basic blocks, which can be useful when dealing with very complex
|
||||
binaries.
|
||||
@ -677,6 +688,8 @@ support.
|
||||
* `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
|
||||
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
|
||||
instrumentation (the default where available). Required to use
|
||||
`AFL_FRIDA_INST_TRACE`.
|
||||
|
@ -1,5 +1,10 @@
|
||||
# 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
|
||||
|
||||
* [libfiowrapper](https://github.com/marekzmyslowski/libfiowrapper) - if the
|
||||
|
@ -20,6 +20,10 @@ training, then we can highly recommend the following:
|
||||
|
||||
* [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
|
||||
structure is), these links have you covered (some are outdated though):
|
||||
|
||||
|
@ -54,4 +54,5 @@
|
||||
"__sanitizer_cov_trace_pc_guard";
|
||||
"__sanitizer_cov_trace_pc_guard_init";
|
||||
"__sanitizer_cov_trace_switch";
|
||||
"LLVMFuzzerTestOneInput";
|
||||
};
|
||||
|
@ -7,6 +7,8 @@ variables.
|
||||
|
||||
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
|
||||
|
||||
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
|
||||
code. Code is considered to be JIT if the executable segment is not backed by
|
||||
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
|
||||
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
|
||||
of each block.
|
||||
`AFL_FRIDA_INST_TRACE`.
|
||||
* `AFL_FRIDA_INST_NO_CACHE` - Don't use a look-up table to cache real to
|
||||
instrumented address block translations.
|
||||
* `AFL_FRIDA_INST_NO_PREFETCH` - Disable prefetching. By default, the child will
|
||||
|
@ -844,6 +844,12 @@ class Afl {
|
||||
static setInstrumentLibraries() {
|
||||
Afl.jsApiSetInstrumentLibraries();
|
||||
}
|
||||
/**
|
||||
* See `AFL_FRIDA_INST_NO_DYNAMIC_LOAD`
|
||||
*/
|
||||
static setInstrumentNoDynamicLoad() {
|
||||
Afl.jsApiSetInstrumentNoDynamicLoad();
|
||||
}
|
||||
/**
|
||||
* See `AFL_FRIDA_INST_NO_OPTIMIZE`
|
||||
*/
|
||||
|
@ -19,6 +19,7 @@
|
||||
js_api_set_instrument_jit;
|
||||
js_api_set_instrument_libraries;
|
||||
js_api_set_instrument_instructions;
|
||||
js_api_set_instrument_no_dynamic_load;
|
||||
js_api_set_instrument_no_optimize;
|
||||
js_api_set_instrument_regs_file;
|
||||
js_api_set_instrument_seed;
|
||||
|
@ -14,8 +14,6 @@ void entry_init(void);
|
||||
|
||||
void entry_start(void);
|
||||
|
||||
void entry_prologue(GumStalkerIterator *iterator, GumStalkerOutput *output);
|
||||
|
||||
void entry_on_fork(void);
|
||||
|
||||
#endif
|
||||
|
@ -6,6 +6,7 @@
|
||||
extern gboolean ranges_debug_maps;
|
||||
extern gboolean ranges_inst_libs;
|
||||
extern gboolean ranges_inst_jit;
|
||||
extern gboolean ranges_inst_dynamic_load;
|
||||
|
||||
void ranges_config(void);
|
||||
void ranges_init(void);
|
||||
|
@ -78,6 +78,7 @@ void entry_init(void) {
|
||||
|
||||
void entry_start(void) {
|
||||
|
||||
FVERBOSE("AFL_ENTRYPOINT reached");
|
||||
if (persistent_start == 0) {
|
||||
|
||||
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();
|
||||
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
}
|
||||
|
||||
|
@ -169,7 +169,6 @@ static void instrument_basic_block(GumStalkerIterator *iterator,
|
||||
|
||||
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_ret) { persistent_epilogue(output); }
|
||||
|
||||
|
@ -76,6 +76,45 @@ typedef struct {
|
||||
|
||||
} 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)
|
||||
|
||||
typedef union {
|
||||
@ -85,6 +124,13 @@ typedef union {
|
||||
|
||||
} 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 =
|
||||
{
|
||||
|
||||
@ -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) {
|
||||
|
||||
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) {
|
||||
|
||||
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);
|
||||
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);
|
||||
return false;
|
||||
|
||||
}
|
||||
|
||||
@ -283,6 +375,95 @@ static void instrument_patch_ardp(guint32 *patch, GumAddress insn,
|
||||
guint32 imm_high = ((distance >> 14) & 0x7FFFF) << 5;
|
||||
*patch |= imm_low;
|
||||
*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);
|
||||
// uint32_t jmp_dot = 0x14000000;
|
||||
// gum_arm64_writer_put_bytes(cw, (guint8 *)&jmp_dot, sizeof(jmp_dot));
|
||||
|
||||
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);
|
||||
area_offset_ror = util_rotate(area_offset, 1, map_size_pow2);
|
||||
|
||||
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));
|
||||
code.code = template;
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
@ -150,6 +150,12 @@ class Afl {
|
||||
static setInstrumentLibraries() {
|
||||
Afl.jsApiSetInstrumentLibraries();
|
||||
}
|
||||
/**
|
||||
* See `AFL_FRIDA_INST_NO_DYNAMIC_LOAD`
|
||||
*/
|
||||
static setInstrumentNoDynamicLoad() {
|
||||
Afl.jsApiSetInstrumentNoDynamicLoad();
|
||||
}
|
||||
/**
|
||||
* 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.jsApiSetInstrumentJit = Afl.jsApiGetFunction("js_api_set_instrument_jit", "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.jsApiSetInstrumentRegsFile = Afl.jsApiGetFunction("js_api_set_instrument_regs_file", "void", ["pointer"]);
|
||||
Afl.jsApiSetInstrumentSeed = Afl.jsApiGetFunction("js_api_set_instrument_seed", "void", ["uint64"]);
|
||||
|
@ -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(
|
||||
void) {
|
||||
|
||||
|
@ -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 "* " cYEL "******************" cRED " *");
|
||||
@ -225,9 +225,7 @@ __attribute__((visibility("default"))) void afl_frida_start(void) {
|
||||
|
||||
js_start();
|
||||
|
||||
/* Initialize */
|
||||
output_init();
|
||||
|
||||
embedded_init();
|
||||
entry_init();
|
||||
instrument_init();
|
||||
@ -240,12 +238,35 @@ __attribute__((visibility("default"))) void afl_frida_start(void) {
|
||||
ranges_init();
|
||||
stats_init();
|
||||
|
||||
/* Start */
|
||||
}
|
||||
|
||||
void afl_frida_run(void) {
|
||||
|
||||
stalker_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) {
|
||||
|
||||
int ret;
|
||||
@ -254,7 +275,17 @@ static int on_main(int argc, char **argv, char **envp) {
|
||||
|
||||
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) {
|
||||
|
||||
|
@ -18,6 +18,7 @@ typedef struct {
|
||||
gboolean ranges_debug_maps = FALSE;
|
||||
gboolean ranges_inst_libs = FALSE;
|
||||
gboolean ranges_inst_jit = FALSE;
|
||||
gboolean ranges_inst_dynamic_load = TRUE;
|
||||
|
||||
static GArray *module_ranges = NULL;
|
||||
static GArray *libs_ranges = NULL;
|
||||
@ -25,6 +26,7 @@ static GArray *jit_ranges = NULL;
|
||||
static GArray *include_ranges = NULL;
|
||||
static GArray *exclude_ranges = NULL;
|
||||
static GArray *ranges = NULL;
|
||||
static GArray *whole_memory_ranges = NULL;
|
||||
|
||||
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,
|
||||
GumMemoryRange *rb) {
|
||||
|
||||
@ -574,11 +591,17 @@ void ranges_config(void) {
|
||||
if (getenv("AFL_FRIDA_DEBUG_MAPS") != NULL) { ranges_debug_maps = 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_NO_DYNAMIC_LOAD") != NULL) {
|
||||
|
||||
ranges_inst_dynamic_load = FALSE;
|
||||
|
||||
}
|
||||
|
||||
if (ranges_debug_maps) { ranges_print_debug_maps(); }
|
||||
|
||||
include_ranges = collect_ranges("AFL_FRIDA_INST_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);
|
||||
|
||||
/*
|
||||
* After step4, we have the total ranges to be instrumented, we now subtract
|
||||
* that from the original ranges of the modules to configure stalker.
|
||||
* After step 4 we have the total ranges to be instrumented, we now subtract
|
||||
* that either from the original ranges of the modules or from the whole
|
||||
* memory if AFL_INST_NO_DYNAMIC_LOAD to configure the stalker.
|
||||
*/
|
||||
if (ranges_inst_dynamic_load) {
|
||||
|
||||
step5 = subtract_ranges(module_ranges, step4);
|
||||
|
||||
} else {
|
||||
|
||||
step5 = subtract_ranges(whole_memory_ranges, step4);
|
||||
|
||||
}
|
||||
|
||||
print_ranges("step5", step5);
|
||||
|
||||
ranges = merge_ranges(step5);
|
||||
|
@ -67,3 +67,8 @@ debug:
|
||||
--ex 'set environment LD_PRELOAD=$(ROOT)afl-frida-trace.so' \
|
||||
--ex 'set disassembly-flavor intel' \
|
||||
--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)
|
||||
|
@ -178,6 +178,13 @@ class Afl {
|
||||
Afl.jsApiSetInstrumentLibraries();
|
||||
}
|
||||
|
||||
/**
|
||||
* See `AFL_FRIDA_INST_NO_DYNAMIC_LOAD`
|
||||
*/
|
||||
public static setInstrumentNoDynamicLoad(): void {
|
||||
Afl.jsApiSetInstrumentNoDynamicLoad();
|
||||
}
|
||||
|
||||
/**
|
||||
* See `AFL_FRIDA_INST_NO_OPTIMIZE`
|
||||
*/
|
||||
@ -443,6 +450,11 @@ class Afl {
|
||||
"void",
|
||||
[]);
|
||||
|
||||
private static readonly jsApiSetInstrumentNoDynamicLoad = Afl.jsApiGetFunction(
|
||||
"js_api_set_instrument_no_dynamic_load",
|
||||
"void",
|
||||
[]);
|
||||
|
||||
private static readonly jsApiSetInstrumentNoOptimize = Afl.jsApiGetFunction(
|
||||
"js_api_set_instrument_no_optimize",
|
||||
"void",
|
||||
|
@ -184,6 +184,7 @@ struct queue_entry {
|
||||
handicap, /* Number of queue cycles behind */
|
||||
depth, /* Path depth */
|
||||
exec_cksum, /* Checksum of the execution trace */
|
||||
custom, /* Marker for custom mutators */
|
||||
stats_mutated; /* stats: # of mutations performed */
|
||||
|
||||
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_cycle_schedules, afl_expand_havoc, afl_statsd, afl_cmplog_only_new,
|
||||
afl_exit_on_seed_issues, afl_try_affinity, afl_ignore_problems,
|
||||
afl_keep_timeouts, afl_pizza_mode, afl_no_crash_readme,
|
||||
afl_ignore_timeouts, afl_no_startup_calibration, afl_no_warn_instability;
|
||||
afl_keep_timeouts, afl_no_crash_readme, afl_ignore_timeouts,
|
||||
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,
|
||||
*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_target_env, *afl_persistent_record, *afl_exit_on_time;
|
||||
|
||||
s32 afl_pizza_mode;
|
||||
|
||||
} afl_env_vars_t;
|
||||
|
||||
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
|
||||
* 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.
|
||||
*
|
||||
* @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_size Size of the test case
|
||||
* @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.
|
||||
* @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_non_255_bytes(afl_state_t *, u8 *);
|
||||
void simplify_trace(afl_state_t *, u8 *);
|
||||
void classify_counts(afl_forkserver_t *);
|
||||
#ifdef WORD_SIZE_64
|
||||
void discover_word(u8 *ret, u64 *current, u64 *virgin);
|
||||
#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 has_new_bits(afl_state_t *, u8 *);
|
||||
u8 has_new_bits_unclassified(afl_state_t *, u8 *);
|
||||
#ifndef AFL_SHOWMAP
|
||||
void classify_counts(afl_forkserver_t *);
|
||||
#endif
|
||||
|
||||
/* Extras */
|
||||
|
||||
@ -1184,11 +1195,13 @@ void fix_up_sync(afl_state_t *);
|
||||
void check_asan_opts(afl_state_t *);
|
||||
void check_binary(afl_state_t *, u8 *);
|
||||
void check_if_tty(afl_state_t *);
|
||||
void setup_signal_handlers(void);
|
||||
void save_cmdline(afl_state_t *, u32, char **);
|
||||
void read_foreign_testcases(afl_state_t *, int);
|
||||
void write_crash_readme(afl_state_t *afl);
|
||||
u8 check_if_text_buf(u8 *buf, u32 len);
|
||||
#ifndef AFL_SHOWMAP
|
||||
void setup_signal_handlers(void);
|
||||
#endif
|
||||
|
||||
/* CmpLog */
|
||||
|
||||
@ -1210,7 +1223,7 @@ double rand_next_percent(afl_state_t *afl);
|
||||
|
||||
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,
|
||||
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 */
|
||||
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)) {
|
||||
|
||||
|
@ -42,7 +42,7 @@
|
||||
// Be careful! _WANT_ORIGINAL_AFL_ALLOC is not compatible with custom mutators
|
||||
|
||||
#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. */
|
||||
|
||||
@ -704,11 +704,10 @@ static inline void *afl_realloc(void **buf, size_t size_needed) {
|
||||
*buf = NULL;
|
||||
return NULL;
|
||||
|
||||
} else {
|
||||
}
|
||||
|
||||
new_buf = newer_buf;
|
||||
|
||||
}
|
||||
memset(((u8 *)new_buf) + current_size, 0, next_size - current_size);
|
||||
|
||||
new_buf->complete_size = next_size;
|
||||
*buf = (void *)(new_buf->buf);
|
||||
|
@ -34,7 +34,7 @@
|
||||
|
||||
#define CMP_MAP_W 65536
|
||||
#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)
|
||||
|
||||
|
@ -147,5 +147,11 @@ s32 create_file(u8 *fn);
|
||||
void *afl_memmem(const void *haystack, size_t haystacklen, const void *needle,
|
||||
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
|
||||
|
||||
|
@ -26,7 +26,7 @@
|
||||
/* Version string: */
|
||||
|
||||
// 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.
|
||||
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
|
||||
* 1. default: colored outputs fixed on: defined USE_COLOR && defined
|
||||
|
@ -37,6 +37,10 @@ static char *afl_environment_variables[] = {
|
||||
"AFL_CRASH_EXITCODE",
|
||||
"AFL_CUSTOM_MUTATOR_LIBRARY",
|
||||
"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_CYCLE_SCHEDULES",
|
||||
"AFL_DEBUG",
|
||||
@ -65,6 +69,7 @@ static char *afl_environment_variables[] = {
|
||||
"AFL_FRIDA_INST_INSN",
|
||||
"AFL_FRIDA_INST_JIT",
|
||||
"AFL_FRIDA_INST_NO_CACHE",
|
||||
"AFL_FRIDA_INST_NO_DYNAMIC_LOAD",
|
||||
"AFL_FRIDA_INST_NO_OPTIMIZE",
|
||||
"AFL_FRIDA_INST_NO_PREFETCH",
|
||||
"AFL_FRIDA_INST_NO_PREFETCH_BACKPATCH",
|
||||
@ -105,6 +110,7 @@ static char *afl_environment_variables[] = {
|
||||
"AFL_HARDEN",
|
||||
"AFL_I_DONT_CARE_ABOUT_MISSING_CRASHES",
|
||||
"AFL_IGNORE_PROBLEMS",
|
||||
"AFL_IGNORE_PROBLEMS_COVERAGE",
|
||||
"AFL_IGNORE_TIMEOUTS",
|
||||
"AFL_IGNORE_UNKNOWN_ENVS",
|
||||
"AFL_IMPORT_FIRST",
|
||||
@ -159,8 +165,9 @@ static char *afl_environment_variables[] = {
|
||||
"AFL_LLVM_SKIP_NEVERZERO",
|
||||
"AFL_NO_AFFINITY",
|
||||
"AFL_TRY_AFFINITY",
|
||||
"AFL_LLVM_LTO_STARTID",
|
||||
"AFL_LLVM_LTO_DONTWRITEID",
|
||||
"AFL_LLVM_LTO_SKIPINIT"
|
||||
"AFL_LLVM_LTO_STARTID",
|
||||
"AFL_NO_ARITH",
|
||||
"AFL_NO_AUTODICT",
|
||||
"AFL_NO_BUILTIN",
|
||||
@ -186,6 +193,7 @@ static char *afl_environment_variables[] = {
|
||||
"AFL_PATH",
|
||||
"AFL_PERFORMANCE_FILE",
|
||||
"AFL_PERSISTENT_RECORD",
|
||||
"AFL_POST_PROCESS_KEEP_ORIGINAL",
|
||||
"AFL_PRELOAD",
|
||||
"AFL_TARGET_ENV",
|
||||
"AFL_PYTHON_MODULE",
|
||||
|
@ -51,16 +51,28 @@ typedef enum NyxReturnValue {
|
||||
|
||||
} NyxReturnValue;
|
||||
|
||||
typedef enum NyxProcessRole {
|
||||
|
||||
StandAlone,
|
||||
Parent,
|
||||
Child,
|
||||
|
||||
} NyxProcessRole;
|
||||
|
||||
typedef struct {
|
||||
|
||||
void *(*nyx_new)(const char *sharedir, const char *workdir, uint32_t cpu_id,
|
||||
uint32_t input_buffer_size,
|
||||
bool input_buffer_write_protection);
|
||||
void *(*nyx_new_parent)(const char *sharedir, const char *workdir,
|
||||
uint32_t cpu_id, uint32_t input_buffer_size,
|
||||
bool input_buffer_write_protection);
|
||||
void *(*nyx_new_child)(const char *sharedir, const char *workdir,
|
||||
uint32_t cpu_id, uint32_t worker_id);
|
||||
void *(*nyx_config_load)(const char *sharedir);
|
||||
void (*nyx_config_set_workdir_path)(void *config, const char *workdir);
|
||||
void (*nyx_config_set_input_buffer_size)(void *config,
|
||||
uint32_t input_buffer_size);
|
||||
void (*nyx_config_set_input_buffer_write_protection)(
|
||||
void *config, bool input_buffer_write_protection);
|
||||
void (*nyx_config_set_hprintf_fd)(void *config, int32_t hprintf_fd);
|
||||
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_option_set_reload_mode)(void *qemu_process, bool enable);
|
||||
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 size);
|
||||
|
||||
bool (*nyx_remove_work_dir)(const char *workdir);
|
||||
|
||||
} 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
|
||||
|
||||
typedef struct afl_forkserver {
|
||||
@ -178,6 +195,8 @@ typedef struct afl_forkserver {
|
||||
u32 nyx_id; /* nyx runner id (0 -> master) */
|
||||
u32 nyx_bind_cpu_id; /* nyx runner cpu id */
|
||||
char *nyx_aux_string;
|
||||
bool nyx_use_tmp_workdir;
|
||||
char *nyx_tmp_workdir_path;
|
||||
#endif
|
||||
|
||||
} afl_forkserver_t;
|
||||
|
@ -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
|
||||
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.
|
||||
|
@ -2,36 +2,37 @@
|
||||
|
||||
## 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
|
||||
coverage than anything else that is out there in the AFL world.
|
||||
1. Use afl-clang-lto/afl-clang-lto++ because the resulting binaries run
|
||||
slightly faster and give better coverage.
|
||||
|
||||
2. You can use it together with llvm_mode: laf-intel and the instrument file
|
||||
listing features and can be combined with cmplog/Redqueen.
|
||||
2. You can use it together with COMPCOV, COMPLOG and the instrument file
|
||||
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
|
||||
targets might need `LD=afl-clang-lto` and others `LD=afl-ld-lto`.
|
||||
5. If any problems arise, be sure to set `AR=llvm-ar RANLIB=llvm-ranlib AS=llvm-as`.
|
||||
Some targets might need `LD=afl-clang-lto` and others `LD=afl-ld-lto`.
|
||||
|
||||
## Introduction and problem description
|
||||
|
||||
A big issue with how AFL++ works is that the basic block IDs that are set during
|
||||
compilation are random - and hence naturally the larger the number of
|
||||
instrumented locations, the higher the number of edge collisions are in the map.
|
||||
This can result in not discovering new paths and therefore degrade the
|
||||
A big issue with how vanilla AFL worked was that the basic block IDs that are
|
||||
set during compilation are random - and hence naturally the larger the number
|
||||
of instrumented locations, the higher the number of edge collisions are in the
|
||||
map. This can result in not discovering new paths and therefore degrade the
|
||||
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
|
||||
collision. On average, a target has 10.000 to 50.000 instrumented blocks, hence
|
||||
the real collisions are between 750-18.000!
|
||||
|
||||
To reach a solution that prevents any collisions took several approaches and
|
||||
many dead ends until we got to this:
|
||||
Note that PCGUARD (our own modified implementation and the SANCOV PCGUARD
|
||||
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.
|
||||
* 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:
|
||||
|
||||
* 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
|
||||
much longer.
|
||||
much (and sometimes much much) longer.
|
||||
|
||||
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).
|
||||
```
|
||||
|
||||
## 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
|
||||
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.):
|
||||
The best way to install LLVM is to follow [https://apt.llvm.org/](https://apt.llvm.org/)
|
||||
|
||||
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 -
|
||||
apt-get update && apt-get upgrade -y
|
||||
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/
|
||||
cd ~/AFLplusplus
|
||||
export LLVM_CONFIG=llvm-config-15
|
||||
make
|
||||
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
|
||||
(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
|
||||
```
|
||||
|
||||
@ -317,13 +277,13 @@ AS=llvm-as ...
|
||||
afl-clang-lto is still work in progress.
|
||||
|
||||
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.
|
||||
* Anything that does not compile with LTO, afl-clang-lto cannot compile either -
|
||||
obviously.
|
||||
|
||||
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`).
|
||||
|
||||
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.)
|
||||
|
||||
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
|
||||
LTO time instrumentation worked, however, "how" the basic blocks were
|
||||
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
|
||||
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
|
||||
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
|
||||
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.
|
||||
|
||||
Happy end :)
|
||||
|
@ -1,4 +1,4 @@
|
||||
/* SanitizeCoverage.cpp ported to afl++ LTO :-) */
|
||||
/* SanitizeCoverage.cpp ported to AFL++ LTO :-) */
|
||||
|
||||
#define AFL_LLVM_PASS
|
||||
|
||||
@ -17,8 +17,12 @@
|
||||
#include "llvm/Transforms/Instrumentation/SanitizerCoverage.h"
|
||||
#include "llvm/ADT/ArrayRef.h"
|
||||
#include "llvm/ADT/SmallVector.h"
|
||||
#include "llvm/ADT/Triple.h"
|
||||
#include "llvm/Analysis/EHPersonalities.h"
|
||||
#if LLVM_VERSION_MAJOR < 17
|
||||
#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/ValueTracking.h"
|
||||
#include "llvm/IR/BasicBlock.h"
|
||||
@ -47,7 +51,9 @@
|
||||
#include "llvm/Support/VirtualFileSystem.h"
|
||||
#include "llvm/Support/raw_ostream.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/ModuleUtils.h"
|
||||
@ -232,7 +238,7 @@ class ModuleSanitizerCoverageLTO
|
||||
|
||||
SanitizerCoverageOptions Options;
|
||||
|
||||
// afl++ START
|
||||
// AFL++ START
|
||||
// const SpecialCaseList * Allowlist;
|
||||
// const SpecialCaseList * Blocklist;
|
||||
uint32_t autodictionary = 1;
|
||||
@ -258,7 +264,7 @@ class ModuleSanitizerCoverageLTO
|
||||
Value *MapPtrFixed = NULL;
|
||||
std::ofstream dFile;
|
||||
size_t found = 0;
|
||||
// afl++ END
|
||||
// AFL++ END
|
||||
|
||||
};
|
||||
|
||||
@ -325,7 +331,7 @@ llvmGetPassPluginInfo() {
|
||||
#if LLVM_VERSION_MAJOR <= 13
|
||||
using OptimizationLevel = typename PassBuilder::OptimizationLevel;
|
||||
#endif
|
||||
#if LLVM_VERSION_MAJOR >= 15
|
||||
#if LLVM_VERSION_MAJOR >= 16
|
||||
PB.registerFullLinkTimeOptimizationLastEPCallback(
|
||||
#else
|
||||
PB.registerOptimizerLastEPCallback(
|
||||
@ -402,7 +408,7 @@ bool ModuleSanitizerCoverageLTO::instrumentModule(
|
||||
Int8Ty = IRB.getInt8Ty();
|
||||
Int1Ty = IRB.getInt1Ty();
|
||||
|
||||
/* afl++ START */
|
||||
/* AFL++ START */
|
||||
char *ptr;
|
||||
LLVMContext &Ctx = M.getContext();
|
||||
Ct = &Ctx;
|
||||
@ -431,6 +437,8 @@ bool ModuleSanitizerCoverageLTO::instrumentModule(
|
||||
if ((afl_global_id = atoi(ptr)) < 0)
|
||||
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) {
|
||||
|
||||
dFile.open(ptr, std::ofstream::out | std::ofstream::app);
|
||||
@ -974,7 +982,7 @@ bool ModuleSanitizerCoverageLTO::instrumentModule(
|
||||
|
||||
}
|
||||
|
||||
// afl++ END
|
||||
// AFL++ END
|
||||
|
||||
SanCovTracePCIndir =
|
||||
M.getOrInsertFunction(SanCovTracePCIndirName, VoidTy, IntptrTy);
|
||||
@ -998,10 +1006,11 @@ bool ModuleSanitizerCoverageLTO::instrumentModule(
|
||||
for (auto &F : M)
|
||||
instrumentFunction(F, DTCallback, PDTCallback);
|
||||
|
||||
// afl++ START
|
||||
// AFL++ START
|
||||
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 ...
|
||||
// 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,
|
||||
// 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).
|
||||
if (BB->getFirstInsertionPt() == BB->end()) return false;
|
||||
|
||||
// afl++ START
|
||||
// AFL++ START
|
||||
if (!Options.NoPrune && &F.getEntryBlock() == BB && F.size() > 1)
|
||||
return false;
|
||||
// afl++ END
|
||||
// AFL++ END
|
||||
|
||||
if (Options.NoPrune || &F.getEntryBlock() == BB) return true;
|
||||
|
||||
@ -1253,10 +1262,10 @@ void ModuleSanitizerCoverageLTO::instrumentFunction(
|
||||
// if (Blocklist && Blocklist->inSection("coverage", "fun", F.getName()))
|
||||
// return;
|
||||
|
||||
// afl++ START
|
||||
// AFL++ START
|
||||
if (!F.size()) return;
|
||||
if (!isInInstrumentList(&F, FMNAME)) return;
|
||||
// afl++ END
|
||||
// AFL++ END
|
||||
|
||||
if (Options.CoverageType >= SanitizerCoverageOptions::SCK_Edge)
|
||||
SplitAllCriticalEdges(
|
||||
@ -1554,7 +1563,7 @@ bool ModuleSanitizerCoverageLTO::InjectCoverage(
|
||||
|
||||
for (size_t i = 0, N = AllBlocks.size(); i < N; i++) {
|
||||
|
||||
// afl++ START
|
||||
// AFL++ START
|
||||
if (BlockList.size()) {
|
||||
|
||||
int skip = 0;
|
||||
@ -1576,7 +1585,7 @@ bool ModuleSanitizerCoverageLTO::InjectCoverage(
|
||||
|
||||
}
|
||||
|
||||
// afl++ END
|
||||
// AFL++ END
|
||||
|
||||
InjectCoverageAtBlock(F, *AllBlocks[i], i, IsLeafFunc);
|
||||
|
||||
@ -1642,7 +1651,7 @@ void ModuleSanitizerCoverageLTO::InjectCoverageAtBlock(Function &F,
|
||||
|
||||
if (Options.TracePCGuard) {
|
||||
|
||||
// afl++ START
|
||||
// AFL++ START
|
||||
++afl_global_id;
|
||||
|
||||
if (dFile.is_open()) {
|
||||
@ -1706,7 +1715,7 @@ void ModuleSanitizerCoverageLTO::InjectCoverageAtBlock(Function &F,
|
||||
// done :)
|
||||
|
||||
inst++;
|
||||
// afl++ END
|
||||
// AFL++ END
|
||||
|
||||
/*
|
||||
XXXXXXXXXXXXXXXXXXX
|
||||
@ -1779,6 +1788,7 @@ INITIALIZE_PASS_END(ModuleSanitizerCoverageLTOLegacyPass, "sancov-lto",
|
||||
"Pass for instrumenting coverage on functions", false,
|
||||
false)
|
||||
|
||||
#if LLVM_VERSION_MAJOR < 16
|
||||
static void registerLTOPass(const PassManagerBuilder &,
|
||||
legacy::PassManagerBase &PM) {
|
||||
|
||||
@ -1787,7 +1797,6 @@ static void registerLTOPass(const PassManagerBuilder &,
|
||||
|
||||
}
|
||||
|
||||
#if LLVM_VERSION_MAJOR < 16
|
||||
static RegisterStandardPasses RegisterCompTransPass(
|
||||
PassManagerBuilder::EP_OptimizerLast, registerLTOPass);
|
||||
|
||||
|
@ -13,38 +13,63 @@
|
||||
#include "llvm/Transforms/Instrumentation/SanitizerCoverage.h"
|
||||
#include "llvm/ADT/ArrayRef.h"
|
||||
#include "llvm/ADT/SmallVector.h"
|
||||
#include "llvm/ADT/Triple.h"
|
||||
#include "llvm/Analysis/EHPersonalities.h"
|
||||
#if LLVM_VERSION_MAJOR >= 15
|
||||
#if LLVM_VERSION_MAJOR < 17
|
||||
#include "llvm/ADT/Triple.h"
|
||||
#endif
|
||||
#endif
|
||||
#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/DataLayout.h"
|
||||
#include "llvm/IR/DebugInfo.h"
|
||||
#if LLVM_VERSION_MAJOR < 15
|
||||
#include "llvm/IR/DebugInfo.h"
|
||||
#endif
|
||||
#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/GlobalVariable.h"
|
||||
#if LLVM_VERSION_MAJOR >= 16
|
||||
#include "llvm/IR/GlobalVariable.h"
|
||||
#endif
|
||||
#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/Intrinsics.h"
|
||||
#include "llvm/IR/LLVMContext.h"
|
||||
#include "llvm/IR/MDBuilder.h"
|
||||
#include "llvm/IR/Mangler.h"
|
||||
#if LLVM_VERSION_MAJOR < 15
|
||||
#include "llvm/IR/MDBuilder.h"
|
||||
#include "llvm/IR/Mangler.h"
|
||||
#endif
|
||||
#include "llvm/IR/Module.h"
|
||||
#include "llvm/IR/PassManager.h"
|
||||
#include "llvm/Passes/PassBuilder.h"
|
||||
#include "llvm/Passes/PassPlugin.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/Debug.h"
|
||||
#include "llvm/Support/SpecialCaseList.h"
|
||||
#include "llvm/Support/VirtualFileSystem.h"
|
||||
#include "llvm/Support/raw_ostream.h"
|
||||
#include "llvm/Transforms/Instrumentation.h"
|
||||
#if LLVM_VERSION_MAJOR < 15
|
||||
#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/ModuleUtils.h"
|
||||
#include "llvm/Passes/PassPlugin.h"
|
||||
#include "llvm/Passes/PassBuilder.h"
|
||||
#include "llvm/IR/PassManager.h"
|
||||
|
||||
#include "config.h"
|
||||
#include "debug.h"
|
||||
@ -54,7 +79,8 @@ using namespace llvm;
|
||||
|
||||
#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 SanCovTraceCmp1[] = "__sanitizer_cov_trace_cmp1";
|
||||
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 SanCovTraceConstCmp4[] = "__sanitizer_cov_trace_const_cmp4";
|
||||
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 SanCovModuleCtorTracePcGuardName[] =
|
||||
"sancov.module_ctor_trace_pc_guard";
|
||||
const char SanCovModuleCtor8bitCountersName[] =
|
||||
"sancov.module_ctor_8bit_counters";
|
||||
const char SanCovModuleCtorBoolFlagName[] = "sancov.module_ctor_bool_flag";
|
||||
static const uint64_t SanCtorAndDtorPriority = 2;
|
||||
const char SanCovTracePCGuardInitName[] = "__sanitizer_cov_trace_pc_guard_init";
|
||||
|
||||
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 SanCovCountersSectionName[] = "sancov_cntrs";
|
||||
@ -95,27 +112,9 @@ namespace {
|
||||
|
||||
SanitizerCoverageOptions OverrideFromCL(SanitizerCoverageOptions Options) {
|
||||
|
||||
// Sets CoverageType and IndirectCalls.
|
||||
// SanitizerCoverageOptions CLOpts = getOptions(ClCoverageLevel);
|
||||
Options.CoverageType =
|
||||
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.CoverageType = SanitizerCoverageOptions::SCK_Edge;
|
||||
// Options.NoPrune = true;
|
||||
Options.TracePCGuard = true; // TracePCGuard is default.
|
||||
|
||||
return Options;
|
||||
|
||||
}
|
||||
@ -135,20 +134,13 @@ class ModuleSanitizerCoverageAFL
|
||||
}
|
||||
|
||||
PreservedAnalyses run(Module &M, ModuleAnalysisManager &MAM);
|
||||
|
||||
bool instrumentModule(Module &M, DomTreeCallback DTCallback,
|
||||
PostDomTreeCallback PDTCallback);
|
||||
|
||||
private:
|
||||
void instrumentFunction(Function &F, DomTreeCallback DTCallback,
|
||||
PostDomTreeCallback PDTCallback);
|
||||
void InjectCoverageForIndirectCalls(Function &F,
|
||||
ArrayRef<Instruction *> IndirCalls);
|
||||
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,
|
||||
ArrayRef<Instruction *> SwitchTraceTargets);
|
||||
bool InjectCoverage(Function &F, ArrayRef<BasicBlock *> AllBlocks,
|
||||
@ -169,20 +161,21 @@ class ModuleSanitizerCoverageAFL
|
||||
|
||||
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"),
|
||||
MDNode::get(*C, None));
|
||||
#endif
|
||||
|
||||
}
|
||||
|
||||
std::string getSectionName(const std::string &Section) const;
|
||||
std::string getSectionStart(const std::string &Section) const;
|
||||
std::string getSectionEnd(const std::string &Section) const;
|
||||
FunctionCallee SanCovTracePCIndir;
|
||||
FunctionCallee SanCovTracePC, SanCovTracePCGuard;
|
||||
FunctionCallee SanCovTraceCmpFunction[4];
|
||||
FunctionCallee SanCovTraceConstCmpFunction[4];
|
||||
FunctionCallee SanCovTraceDivFunction[2];
|
||||
FunctionCallee SanCovTraceGepFunction;
|
||||
FunctionCallee SanCovTraceSwitchFunction;
|
||||
GlobalVariable *SanCovLowestStack;
|
||||
Type *IntptrTy, *IntptrPtrTy, *Int64Ty, *Int64PtrTy, *Int32Ty, *Int32PtrTy,
|
||||
@ -211,18 +204,16 @@ class ModuleSanitizerCoverageAFL
|
||||
|
||||
} // namespace
|
||||
|
||||
#if LLVM_VERSION_MAJOR >= 11 /* use new pass manager */
|
||||
|
||||
extern "C" ::llvm::PassPluginLibraryInfo LLVM_ATTRIBUTE_WEAK
|
||||
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. */
|
||||
[](PassBuilder &PB) {
|
||||
|
||||
#if LLVM_VERSION_MAJOR <= 13
|
||||
#if LLVM_VERSION_MAJOR == 13
|
||||
using OptimizationLevel = typename PassBuilder::OptimizationLevel;
|
||||
#endif
|
||||
#endif
|
||||
PB.registerOptimizerLastEPCallback(
|
||||
[](ModulePassManager &MPM, OptimizationLevel OL) {
|
||||
|
||||
@ -234,8 +225,7 @@ llvmGetPassPluginInfo() {
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
#if LLVM_VERSION_MAJOR == 1
|
||||
PreservedAnalyses ModuleSanitizerCoverageAFL::run(Module &M,
|
||||
ModuleAnalysisManager &MAM) {
|
||||
|
||||
@ -253,33 +243,64 @@ 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))
|
||||
return PreservedAnalyses::none();
|
||||
return PreservedAnalyses::all();
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
std::pair<Value *, Value *> ModuleSanitizerCoverageAFL::CreateSecStartEnd(
|
||||
Module &M, const char *Section, Type *Ty) {
|
||||
|
||||
GlobalVariable *SecStart =
|
||||
new GlobalVariable(M,
|
||||
#if LLVM_VERSION_MAJOR >= 15
|
||||
Ty,
|
||||
#else
|
||||
Ty->getPointerElementType(),
|
||||
#endif
|
||||
false, GlobalVariable::ExternalWeakLinkage, nullptr,
|
||||
// Use ExternalWeak so that if all sections are discarded due to section
|
||||
// garbage collection, the linker will not report undefined symbol errors.
|
||||
// Windows defines the start/stop symbols in compiler-rt so no need for
|
||||
// ExternalWeak.
|
||||
GlobalValue::LinkageTypes Linkage = TargetTriple.isOSBinFormatCOFF()
|
||||
? GlobalVariable::ExternalLinkage
|
||||
: GlobalVariable::ExternalWeakLinkage;
|
||||
GlobalVariable *SecStart = new GlobalVariable(M, Ty, false, Linkage, nullptr,
|
||||
getSectionStart(Section));
|
||||
SecStart->setVisibility(GlobalValue::HiddenVisibility);
|
||||
GlobalVariable *SecEnd =
|
||||
new GlobalVariable(M,
|
||||
#if LLVM_VERSION_MAJOR >= 15
|
||||
Ty,
|
||||
#else
|
||||
Ty->getPointerElementType(),
|
||||
#endif
|
||||
false, GlobalVariable::ExternalWeakLinkage, nullptr,
|
||||
GlobalVariable *SecEnd = new GlobalVariable(M, Ty, false, Linkage, nullptr,
|
||||
getSectionEnd(Section));
|
||||
SecEnd->setVisibility(GlobalValue::HiddenVisibility);
|
||||
IRBuilder<> IRB(M.getContext());
|
||||
@ -291,7 +312,8 @@ std::pair<Value *, Value *> ModuleSanitizerCoverageAFL::CreateSecStartEnd(
|
||||
auto SecStartI8Ptr = IRB.CreatePointerCast(SecStart, Int8PtrTy);
|
||||
auto GEP = IRB.CreateGEP(Int8Ty, SecStartI8Ptr,
|
||||
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 SecEnd = SecStartEnd.second;
|
||||
Function *CtorFunc;
|
||||
Type *PtrTy = PointerType::getUnqual(Ty);
|
||||
std::tie(CtorFunc, std::ignore) = createSanitizerCtorAndInitFunctions(
|
||||
M, CtorName, InitFunctionName, {Ty, Ty}, {SecStart, SecEnd});
|
||||
M, CtorName, InitFunctionName, {PtrTy, PtrTy}, {SecStart, SecEnd});
|
||||
assert(CtorFunc->getName() == CtorName);
|
||||
|
||||
if (TargetTriple.supportsCOMDAT()) {
|
||||
@ -328,7 +351,6 @@ Function *ModuleSanitizerCoverageAFL::CreateInitCallsForSections(
|
||||
// to include the sancov constructor. This way the linker can deduplicate
|
||||
// the constructors but always leave one copy.
|
||||
CtorFunc->setLinkage(GlobalValue::WeakODRLinkage);
|
||||
appendToUsed(M, CtorFunc);
|
||||
|
||||
}
|
||||
|
||||
@ -340,37 +362,25 @@ bool ModuleSanitizerCoverageAFL::instrumentModule(
|
||||
Module &M, DomTreeCallback DTCallback, PostDomTreeCallback PDTCallback) {
|
||||
|
||||
setvbuf(stdout, NULL, _IONBF, 0);
|
||||
if (getenv("AFL_DEBUG")) debug = 1;
|
||||
|
||||
if (getenv("AFL_DEBUG")) { debug = 1; }
|
||||
|
||||
if ((isatty(2) && !getenv("AFL_QUIET")) || debug) {
|
||||
|
||||
SAYF(cCYA "SanitizerCoveragePCGUARD" VERSION cRST "\n");
|
||||
|
||||
} else
|
||||
} else {
|
||||
|
||||
be_quiet = 1;
|
||||
|
||||
}
|
||||
|
||||
skip_nozero = getenv("AFL_LLVM_SKIP_NEVERZERO");
|
||||
use_threadsafe_counters = getenv("AFL_LLVM_THREADSAFE_INST");
|
||||
|
||||
initInstrumentList();
|
||||
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());
|
||||
DL = &M.getDataLayout();
|
||||
CurModule = &M;
|
||||
@ -393,16 +403,14 @@ bool ModuleSanitizerCoverageAFL::instrumentModule(
|
||||
Int16Ty = IRB.getInt16Ty();
|
||||
Int8Ty = IRB.getInt8Ty();
|
||||
Int1Ty = IRB.getInt1Ty();
|
||||
LLVMContext &Ctx = M.getContext();
|
||||
|
||||
LLVMContext &Ctx = M.getContext();
|
||||
AFLMapPtr =
|
||||
new GlobalVariable(M, PointerType::get(Int8Ty, 0), false,
|
||||
GlobalValue::ExternalLinkage, 0, "__afl_area_ptr");
|
||||
One = ConstantInt::get(IntegerType::getInt8Ty(Ctx), 1);
|
||||
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
|
||||
// target ABI.
|
||||
AttributeList SanCovTraceCmpZeroExtAL;
|
||||
@ -432,26 +440,13 @@ bool ModuleSanitizerCoverageAFL::instrumentModule(
|
||||
SanCovTraceConstCmpFunction[3] =
|
||||
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 =
|
||||
M.getOrInsertFunction(SanCovTraceSwitchName, VoidTy, Int64Ty, Int64PtrTy);
|
||||
|
||||
Constant *SanCovLowestStackConstant =
|
||||
M.getOrInsertGlobal(SanCovLowestStackName, IntptrTy);
|
||||
SanCovLowestStack = dyn_cast<GlobalVariable>(SanCovLowestStackConstant);
|
||||
if (!SanCovLowestStack) {
|
||||
if (!SanCovLowestStack || SanCovLowestStack->getValueType() != IntptrTy) {
|
||||
|
||||
C->emitError(StringRef("'") + SanCovLowestStackName +
|
||||
"' should not be declared by the user");
|
||||
@ -461,8 +456,6 @@ bool ModuleSanitizerCoverageAFL::instrumentModule(
|
||||
|
||||
SanCovLowestStack->setThreadLocalMode(
|
||||
GlobalValue::ThreadLocalMode::InitialExecTLSModel);
|
||||
if (Options.StackDepth && !SanCovLowestStack->isDeclaration())
|
||||
SanCovLowestStack->setInitializer(Constant::getAllOnesValue(IntptrTy));
|
||||
|
||||
SanCovTracePC = M.getOrInsertFunction(SanCovTracePCName, VoidTy);
|
||||
SanCovTracePCGuard =
|
||||
@ -477,40 +470,25 @@ bool ModuleSanitizerCoverageAFL::instrumentModule(
|
||||
Ctor = CreateInitCallsForSections(M, SanCovModuleCtorTracePcGuardName,
|
||||
SanCovTracePCGuardInitName, Int32PtrTy,
|
||||
SanCovGuardsSectionName);
|
||||
if (Function8bitCounterArray)
|
||||
Ctor = CreateInitCallsForSections(M, SanCovModuleCtor8bitCountersName,
|
||||
SanCov8bitCountersInitName, Int8PtrTy,
|
||||
SanCovCountersSectionName);
|
||||
if (FunctionBoolArray) {
|
||||
|
||||
Ctor = CreateInitCallsForSections(M, SanCovModuleCtorBoolFlagName,
|
||||
SanCovBoolFlagInitName, Int1PtrTy,
|
||||
SanCovBoolFlagSectionName);
|
||||
if (Ctor && debug) {
|
||||
|
||||
fprintf(stderr, "SANCOV: installed pcguard_init in ctor\n");
|
||||
|
||||
}
|
||||
|
||||
if (Ctor && Options.PCTable) {
|
||||
|
||||
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);
|
||||
appendToUsed(M, GlobalsToAppendToUsed);
|
||||
appendToCompilerUsed(M, GlobalsToAppendToCompilerUsed);
|
||||
|
||||
if (!be_quiet) {
|
||||
|
||||
if (!instr)
|
||||
WARNF("No instrumentation targets found.");
|
||||
else {
|
||||
if (!instr) {
|
||||
|
||||
char modeline[100];
|
||||
WARNF("No instrumentation targets found.");
|
||||
|
||||
} else {
|
||||
|
||||
char modeline[128];
|
||||
snprintf(modeline, sizeof(modeline), "%s%s%s%s%s%s",
|
||||
getenv("AFL_HARDEN") ? "hardened" : "non-hardened",
|
||||
getenv("AFL_USE_ASAN") ? ", ASAN" : "",
|
||||
@ -531,36 +509,33 @@ bool ModuleSanitizerCoverageAFL::instrumentModule(
|
||||
}
|
||||
|
||||
// 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.
|
||||
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 PostDominatorTree *PDT,
|
||||
const SanitizerCoverageOptions &Options) {
|
||||
@ -578,10 +553,6 @@ bool shouldInstrumentBlock(const Function &F, const BasicBlock *BB,
|
||||
|
||||
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
|
||||
// predecessors.
|
||||
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
|
||||
// * To dominates From or
|
||||
// * 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 (DT->dominates(Next, From)) return true;
|
||||
if (DT->dominates(Next, From))
|
||||
return true;
|
||||
return false;
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
// Prunes uninteresting Cmp instrumentation:
|
||||
// * CMP instructions that feed into loop backedge branch.
|
||||
//
|
||||
// Note that Cmp pruning is controlled by the same flag as the
|
||||
// BB pruning.
|
||||
bool IsInterestingCmp(ICmpInst *CMP, const DominatorTree *DT,
|
||||
#if 0
|
||||
static bool IsInterestingCmp(ICmpInst *CMP, const DominatorTree *DT,
|
||||
const SanitizerCoverageOptions &Options) {
|
||||
|
||||
if (!Options.NoPrune)
|
||||
if (CMP->hasOneUse())
|
||||
if (auto BR = dyn_cast<BranchInst>(CMP->user_back()))
|
||||
for (BasicBlock *B : BR->successors())
|
||||
if (IsBackEdge(BR->getParent(), B, DT)) return false;
|
||||
if (IsBackEdge(BR->getParent(), B, DT))
|
||||
return false;
|
||||
return true;
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
void ModuleSanitizerCoverageAFL::instrumentFunction(
|
||||
Function &F, DomTreeCallback DTCallback, PostDomTreeCallback PDTCallback) {
|
||||
|
||||
if (F.empty()) return;
|
||||
if (!isInInstrumentList(&F, FMNAME)) return;
|
||||
|
||||
if (F.getName().find(".module_ctor") != std::string::npos)
|
||||
return; // Should not instrument sanitizer init functions.
|
||||
if (F.getName().startswith("__sanitizer_"))
|
||||
@ -643,15 +623,13 @@ void ModuleSanitizerCoverageAFL::instrumentFunction(
|
||||
if (F.hasPersonalityFn() &&
|
||||
isAsynchronousEHPersonality(classifyEHPersonality(F.getPersonalityFn())))
|
||||
return;
|
||||
if (F.hasFnAttribute(Attribute::NoSanitizeCoverage)) return;
|
||||
if (Options.CoverageType >= SanitizerCoverageOptions::SCK_Edge)
|
||||
SplitAllCriticalEdges(
|
||||
F, CriticalEdgeSplittingOptions().setIgnoreUnreachableDests());
|
||||
SmallVector<Instruction *, 8> IndirCalls;
|
||||
SmallVector<BasicBlock *, 16> BlocksToInstrument;
|
||||
SmallVector<Instruction *, 8> CmpTraceTargets;
|
||||
SmallVector<Instruction *, 8> SwitchTraceTargets;
|
||||
SmallVector<BinaryOperator *, 8> DivTraceTargets;
|
||||
SmallVector<GetElementPtrInst *, 8> GepTraceTargets;
|
||||
|
||||
const DominatorTree *DT = DTCallback(F);
|
||||
const PostDominatorTree *PDT = PDTCallback(F);
|
||||
@ -661,47 +639,28 @@ void ModuleSanitizerCoverageAFL::instrumentFunction(
|
||||
|
||||
if (shouldInstrumentBlock(F, &BB, DT, PDT, Options))
|
||||
BlocksToInstrument.push_back(&BB);
|
||||
/*
|
||||
for (auto &Inst : BB) {
|
||||
|
||||
if (Options.IndirectCalls) {
|
||||
|
||||
CallBase *CB = dyn_cast<CallBase>(&Inst);
|
||||
if (CB && !CB->getCalledFunction()) IndirCalls.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 (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);
|
||||
InjectCoverageForIndirectCalls(F, IndirCalls);
|
||||
InjectTraceForCmp(F, CmpTraceTargets);
|
||||
InjectTraceForSwitch(F, SwitchTraceTargets);
|
||||
InjectTraceForDiv(F, DivTraceTargets);
|
||||
InjectTraceForGep(F, GepTraceTargets);
|
||||
// InjectTraceForCmp(F, CmpTraceTargets);
|
||||
// InjectTraceForSwitch(F, SwitchTraceTargets);
|
||||
|
||||
}
|
||||
|
||||
@ -713,33 +672,30 @@ GlobalVariable *ModuleSanitizerCoverageAFL::CreateFunctionLocalArrayInSection(
|
||||
*CurModule, ArrayTy, false, GlobalVariable::PrivateLinkage,
|
||||
Constant::getNullValue(ArrayTy), "__sancov_gen_");
|
||||
|
||||
#if LLVM_VERSION_MAJOR >= 13
|
||||
if (TargetTriple.supportsCOMDAT() &&
|
||||
(TargetTriple.isOSBinFormatELF() || !F.isInterposable()))
|
||||
if (auto Comdat = getOrCreateFunctionComdat(F, TargetTriple))
|
||||
Array->setComdat(Comdat);
|
||||
Array->setSection(getSectionName(Section));
|
||||
#if LLVM_VERSION_MAJOR >= 16
|
||||
Array->setAlignment(Align(DL->getTypeStoreSize(Ty).getFixedValue()));
|
||||
#else
|
||||
if (TargetTriple.supportsCOMDAT() && !F.isInterposable())
|
||||
if (auto Comdat =
|
||||
GetOrCreateFunctionComdat(F, TargetTriple, CurModuleUniqueId))
|
||||
Array->setComdat(Comdat);
|
||||
Array->setAlignment(Align(DL->getTypeStoreSize(Ty).getFixedSize()));
|
||||
#endif
|
||||
|
||||
Array->setSection(getSectionName(Section));
|
||||
#if (LLVM_VERSION_MAJOR >= 11) || \
|
||||
(LLVM_VERSION_MAJOR == 10 && LLVM_VERSION_MINOR >= 1)
|
||||
#if LLVM_VERSION_MAJOR >= 16
|
||||
Array->setAlignment(Align(DL->getTypeStoreSize(Ty).getFixedValue()));
|
||||
#else
|
||||
Array->setAlignment(Align(DL->getTypeStoreSize(Ty).getFixedSize()));
|
||||
#endif
|
||||
#else
|
||||
Array->setAlignment(Align(4)); // cheating
|
||||
#endif
|
||||
GlobalsToAppendToUsed.push_back(Array);
|
||||
// sancov_pcs parallels the other metadata section(s). Optimizers (e.g.
|
||||
// GlobalOpt/ConstantMerge) may not discard sancov_pcs and the other
|
||||
// section(s) as a unit, so we conservatively retain all unconditionally in
|
||||
// the compiler.
|
||||
//
|
||||
// With comdat (COFF/ELF), the linker can guarantee the associated sections
|
||||
// will be retained or discarded as a unit, so llvm.compiler.used is
|
||||
// sufficient. Otherwise, conservatively make all of them retained by the
|
||||
// linker.
|
||||
if (Array->hasComdat())
|
||||
GlobalsToAppendToCompilerUsed.push_back(Array);
|
||||
MDNode *MD = MDNode::get(F.getContext(), ValueAsMetadata::get(&F));
|
||||
Array->addMetadata(LLVMContext::MD_associated, *MD);
|
||||
else
|
||||
GlobalsToAppendToUsed.push_back(Array);
|
||||
|
||||
return Array;
|
||||
|
||||
@ -764,8 +720,12 @@ GlobalVariable *ModuleSanitizerCoverageAFL::CreatePCArray(
|
||||
|
||||
PCs.push_back((Constant *)IRB.CreatePointerCast(
|
||||
BlockAddress::get(AllBlocks[i]), IntptrPtrTy));
|
||||
#if LLVM_VERSION_MAJOR >= 16
|
||||
PCs.push_back(Constant::getNullValue(IntptrPtrTy));
|
||||
#else
|
||||
PCs.push_back((Constant *)IRB.CreateIntToPtr(
|
||||
ConstantInt::get(IntptrTy, 0), IntptrPtrTy));
|
||||
#endif
|
||||
|
||||
}
|
||||
|
||||
@ -788,21 +748,13 @@ void ModuleSanitizerCoverageAFL::CreateFunctionLocalArrays(
|
||||
FunctionGuardArray = CreateFunctionLocalArrayInSection(
|
||||
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(
|
||||
Function &F, ArrayRef<BasicBlock *> AllBlocks, bool IsLeafFunc) {
|
||||
|
||||
if (AllBlocks.empty()) return false;
|
||||
|
||||
uint32_t cnt_cov = 0, cnt_sel = 0, cnt_sel_inc = 0;
|
||||
static uint32_t first = 1;
|
||||
|
||||
@ -851,7 +803,6 @@ bool ModuleSanitizerCoverageAFL::InjectCoverage(
|
||||
|
||||
}
|
||||
|
||||
#if (LLVM_VERSION_MAJOR >= 12)
|
||||
else if (t->getTypeID() == llvm::Type::FixedVectorTyID) {
|
||||
|
||||
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);
|
||||
|
||||
if (first) { first = 0; }
|
||||
selects += cnt_sel;
|
||||
|
||||
@ -885,12 +834,6 @@ bool ModuleSanitizerCoverageAFL::InjectCoverage(
|
||||
|
||||
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))) {
|
||||
|
||||
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);
|
||||
|
||||
}
|
||||
@ -1059,13 +996,6 @@ bool ModuleSanitizerCoverageAFL::InjectCoverage(
|
||||
IRB.CreateLoad(PointerType::get(Int8Ty, 0), AFLMapPtr);
|
||||
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) {
|
||||
|
||||
/* 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:
|
||||
// __sanitizer_cov_trace_switch(CondValue,
|
||||
// {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(
|
||||
Function &, ArrayRef<Instruction *> CmpTraceTargets) {
|
||||
|
||||
@ -1317,27 +1189,44 @@ void ModuleSanitizerCoverageAFL::InjectCoverageAtBlock(Function &F,
|
||||
|
||||
BasicBlock::iterator IP = BB.getFirstInsertionPt();
|
||||
bool IsEntryBB = &BB == &F.getEntryBlock();
|
||||
DebugLoc EntryLoc;
|
||||
|
||||
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
|
||||
// calls.
|
||||
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);
|
||||
|
||||
if (Options.TracePC) {
|
||||
|
||||
IRB.CreateCall(SanCovTracePC);
|
||||
// ->setCannotMerge(); // gets the PC using GET_CALLER_PC.
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
if (EntryLoc) IRB.SetCurrentDebugLocation(EntryLoc);
|
||||
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 */
|
||||
|
||||
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(
|
||||
|
@ -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__
|
||||
#include "android-ashmem.h"
|
||||
#endif
|
||||
@ -105,6 +115,44 @@ u32 __afl_dictionary_len;
|
||||
u64 __afl_map_addr;
|
||||
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 */
|
||||
u32 __afl_connected = 0;
|
||||
|
||||
@ -113,7 +161,7 @@ int __afl_selective_coverage __attribute__((weak));
|
||||
int __afl_selective_coverage_start_off __attribute__((weak));
|
||||
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_caller[CTX_MAX_K];
|
||||
u32 __afl_prev_ctx;
|
||||
@ -499,6 +547,7 @@ static void __afl_map_shm(void) {
|
||||
u8 *map_env = (u8 *)getenv("AFL_MAP_SIZE");
|
||||
if (!map_env || atoi((char *)map_env) < MAP_SIZE) {
|
||||
|
||||
fprintf(stderr, "FS_ERROR_MAP_SIZE\n");
|
||||
send_forkserver_error(FS_ERROR_MAP_SIZE);
|
||||
_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. */
|
||||
@ -686,6 +756,17 @@ static void __afl_unmap_shm(void) {
|
||||
|
||||
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);
|
||||
|
||||
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
|
||||
ID of 0 as a special value to indicate non-instrumented bits. That may
|
||||
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");
|
||||
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() "
|
||||
"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"
|
||||
"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();
|
||||
|
||||
} 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) {
|
||||
|
||||
if (likely(inst_ratio == 100) || R(100) < inst_ratio) {
|
||||
if (!ignore_dso_after_fs &&
|
||||
(likely(inst_ratio == 100) || R(100) < inst_ratio)) {
|
||||
|
||||
*(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
|
||||
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;
|
||||
|
||||
@ -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) {
|
||||
|
||||
fprintf(stderr,
|
||||
|
@ -289,6 +289,7 @@ void scanForDangerousFunctions(llvm::Module *M) {
|
||||
|
||||
StringRef ifunc_name = IF.getName();
|
||||
Constant *r = IF.getResolver();
|
||||
if (r->getNumOperands() == 0) { continue; }
|
||||
StringRef r_name = cast<Function>(r->getOperand(0))->getName();
|
||||
if (!be_quiet)
|
||||
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
|
||||
// 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.
|
||||
unsigned long long int calculateCollisions(uint32_t edges) {
|
||||
|
||||
|
@ -22,7 +22,9 @@ typedef long double max_align_t;
|
||||
#include "llvm/IR/Module.h"
|
||||
#include "llvm/Support/Debug.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 || \
|
||||
(LLVM_VERSION_MAJOR == 3 && LLVM_VERSION_MINOR > 4)
|
||||
|
@ -53,7 +53,9 @@
|
||||
#include "llvm/IR/Verifier.h"
|
||||
#include "llvm/Support/Debug.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/Analysis/LoopInfo.h"
|
||||
#include "llvm/Analysis/ValueTracking.h"
|
||||
@ -744,7 +746,7 @@ static void registerAFLdict2filePass(const PassManagerBuilder &,
|
||||
}
|
||||
|
||||
static RegisterPass<AFLdict2filePass> X("afl-dict2file",
|
||||
"afl++ dict2file instrumentation pass",
|
||||
"AFL++ dict2file instrumentation pass",
|
||||
false, false);
|
||||
|
||||
static RegisterStandardPasses RegisterAFLdict2filePass(
|
||||
|
@ -45,7 +45,7 @@
|
||||
#include "llvm/IR/Module.h"
|
||||
#include "llvm/Pass.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/PassBuilder.h"
|
||||
#include "llvm/IR/PassManager.h"
|
||||
|
@ -413,7 +413,7 @@ bool AFLCoverage::runOnModule(Module &M) {
|
||||
GlobalVariable *AFLContext = NULL;
|
||||
|
||||
if (ctx_str || caller_str)
|
||||
#if defined(__ANDROID__) || defined(__HAIKU__)
|
||||
#if defined(__ANDROID__) || defined(__HAIKU__) || defined(NO_TLS)
|
||||
AFLContext = new GlobalVariable(
|
||||
M, Int32Ty, false, GlobalValue::ExternalLinkage, 0, "__afl_prev_ctx");
|
||||
#else
|
||||
@ -424,7 +424,7 @@ bool AFLCoverage::runOnModule(Module &M) {
|
||||
|
||||
#ifdef AFL_HAVE_VECTOR_INTRINSICS
|
||||
if (ngram_size)
|
||||
#if defined(__ANDROID__) || defined(__HAIKU__)
|
||||
#if defined(__ANDROID__) || defined(__HAIKU__) || defined(NO_TLS)
|
||||
AFLPrevLoc = new GlobalVariable(
|
||||
M, PrevLocTy, /* isConstant */ false, GlobalValue::ExternalLinkage,
|
||||
/* Initializer */ nullptr, "__afl_prev_loc");
|
||||
@ -437,7 +437,7 @@ bool AFLCoverage::runOnModule(Module &M) {
|
||||
#endif
|
||||
else
|
||||
#endif
|
||||
#if defined(__ANDROID__) || defined(__HAIKU__)
|
||||
#if defined(__ANDROID__) || defined(__HAIKU__) || defined(NO_TLS)
|
||||
AFLPrevLoc = new GlobalVariable(
|
||||
M, Int32Ty, false, GlobalValue::ExternalLinkage, 0, "__afl_prev_loc");
|
||||
#else
|
||||
@ -448,7 +448,7 @@ bool AFLCoverage::runOnModule(Module &M) {
|
||||
|
||||
#ifdef AFL_HAVE_VECTOR_INTRINSICS
|
||||
if (ctx_k)
|
||||
#if defined(__ANDROID__) || defined(__HAIKU__)
|
||||
#if defined(__ANDROID__) || defined(__HAIKU__) || defined(NO_TLS)
|
||||
AFLPrevCaller = new GlobalVariable(
|
||||
M, PrevCallerTy, /* isConstant */ false, GlobalValue::ExternalLinkage,
|
||||
/* Initializer */ nullptr, "__afl_prev_caller");
|
||||
@ -461,7 +461,7 @@ bool AFLCoverage::runOnModule(Module &M) {
|
||||
#endif
|
||||
else
|
||||
#endif
|
||||
#if defined(__ANDROID__) || defined(__HAIKU__)
|
||||
#if defined(__ANDROID__) || defined(__HAIKU__) || defined(NO_TLS)
|
||||
AFLPrevCaller =
|
||||
new GlobalVariable(M, Int32Ty, false, GlobalValue::ExternalLinkage, 0,
|
||||
"__afl_prev_caller");
|
||||
|
@ -38,7 +38,9 @@
|
||||
#include "llvm/IR/Module.h"
|
||||
#include "llvm/Support/Debug.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/Pass.h"
|
||||
#include "llvm/Analysis/ValueTracking.h"
|
||||
|
@ -39,7 +39,9 @@
|
||||
#include "llvm/IR/Module.h"
|
||||
#include "llvm/Support/Debug.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/Pass.h"
|
||||
#include "llvm/Analysis/ValueTracking.h"
|
||||
|
@ -60,7 +60,7 @@ using namespace llvm;
|
||||
|
||||
// uncomment this toggle function verification at each step. horribly slow, but
|
||||
// helps to pinpoint a potential problem in the splitting code.
|
||||
//#define VERIFY_TOO_MUCH 1
|
||||
// #define VERIFY_TOO_MUCH 1
|
||||
|
||||
namespace {
|
||||
|
||||
|
@ -1 +1 @@
|
||||
acaf7f6
|
||||
c8a72dc
|
||||
|
@ -1 +1 @@
|
||||
86b159b
|
||||
202bace
|
||||
|
Submodule nyx_mode/QEMU-Nyx updated: 5c8cf793ec...60c216bc9e
@ -1 +1 @@
|
||||
5c8cf793ec
|
||||
60c216bc9e
|
||||
|
@ -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
|
||||
(see [KVM-Nyx](https://github.com/nyx-fuzz/KVM-Nyx)).
|
||||
|
||||
|
||||
## Building Nyx mode
|
||||
|
||||
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
|
||||
[KVM-Nyx](https://github.com/nyx-fuzz/KVM-Nyx).
|
||||
|
||||
|
||||
## Preparing to fuzz a target with Nyx mode
|
||||
|
||||
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`
|
||||
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
|
||||
python3 nyx_mode/packer/packer/nyx_config_gen.py PACKAGE-DIRECTORY Kernel
|
||||
```
|
||||
|
||||
|
||||
## Fuzzing with Nyx mode
|
||||
|
||||
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++ companion tools (afl-showmap etc.)
|
||||
|
||||
Please note that AFL++ companion tools like afl-cmin, afl-showmap, etc. are
|
||||
not supported with Nyx mode, only afl-fuzz.
|
||||
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`.
|
||||
|
||||
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
|
||||
|
||||
|
@ -60,11 +60,6 @@ fi
|
||||
|
||||
echo "[*] Checking QEMU-Nyx ..."
|
||||
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)
|
||||
fi
|
||||
|
||||
|
Submodule nyx_mode/libnyx updated: acaf7f6346...2da7f08b6e
Submodule nyx_mode/packer updated: 86b159bafc...202bace888
@ -1 +1 @@
|
||||
249bf0c872
|
||||
a1321713c7
|
||||
|
@ -356,7 +356,7 @@ fi
|
||||
|
||||
if ! command -v "$CROSS" > /dev/null ; 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 ..."
|
||||
make -C libcompcov && echo "[+] libcompcov ready"
|
||||
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"
|
||||
fi
|
||||
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 ..."
|
||||
make -C libcompcov CC="$CROSS $CROSS_FLAGS" && echo "[+] libcompcov ready"
|
||||
echo "[+] Building unsigaction ..."
|
||||
|
@ -68,7 +68,11 @@ static int debug_fd = -1;
|
||||
|
||||
#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;
|
||||
|
||||
|
Submodule qemu_mode/qemuafl updated: 249bf0c872...a1321713c7
@ -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) {
|
||||
|
||||
@ -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++;
|
||||
|
||||
if (stop_soon) {
|
||||
@ -768,6 +768,7 @@ static void usage(u8 *argv0) {
|
||||
" -U - use unicorn-based instrumentation (Unicorn mode)\n"
|
||||
" -W - use qemu-based instrumentation with Wine (Wine "
|
||||
"mode)\n"
|
||||
" -X - use Nyx mode\n"
|
||||
#endif
|
||||
"\n"
|
||||
|
||||
@ -814,7 +815,7 @@ int main(int argc, char **argv_orig, char **envp) {
|
||||
|
||||
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) {
|
||||
|
||||
@ -966,6 +967,23 @@ int main(int argc, char **argv_orig, char **envp) {
|
||||
|
||||
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':
|
||||
usage(argv[0]);
|
||||
return -1;
|
||||
@ -997,7 +1015,21 @@ int main(int argc, char **argv_orig, char **envp) {
|
||||
|
||||
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]);
|
||||
#endif
|
||||
|
||||
fsrv.trace_bits = afl_shm_init(&shm, map_size, 0);
|
||||
detect_file_args(argv + optind, fsrv.out_file, &use_stdin);
|
||||
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);
|
||||
|
||||
#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 {
|
||||
|
||||
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);
|
||||
|
||||
read_initial_file();
|
||||
#ifdef __linux__
|
||||
if (!fsrv.nyx_mode) { (void)check_binary_signatures(fsrv.target_path); }
|
||||
#else
|
||||
(void)check_binary_signatures(fsrv.target_path);
|
||||
#endif
|
||||
|
||||
ACTF("Performing dry run (mem limit = %llu MB, timeout = %u ms%s)...",
|
||||
mem_limit, exec_tmout, edges_only ? ", edges only" : "");
|
||||
|
560
src/afl-cc.c
560
src/afl-cc.c
@ -31,6 +31,8 @@
|
||||
#include <strings.h>
|
||||
#include <limits.h>
|
||||
#include <assert.h>
|
||||
#include <ctype.h>
|
||||
#include <sys/stat.h>
|
||||
|
||||
#if (LLVM_MAJOR - 0 == 0)
|
||||
#undef LLVM_MAJOR
|
||||
@ -76,6 +78,7 @@ enum {
|
||||
INSTRUMENT_OPT_NGRAM = 16,
|
||||
INSTRUMENT_OPT_CALLER = 32,
|
||||
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. */
|
||||
|
||||
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,
|
||||
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 *));
|
||||
cc_params = ck_alloc(1024 * sizeof(u8 *));
|
||||
|
||||
if (lto_mode) {
|
||||
|
||||
@ -641,10 +937,10 @@ static void edit_params(u32 argc, char **argv, char **envp) {
|
||||
|
||||
}
|
||||
|
||||
//#if LLVM_MAJOR >= 13
|
||||
// // Use the old pass manager in LLVM 14 which the afl++ passes still
|
||||
// #if LLVM_MAJOR >= 13
|
||||
// // Use the old pass manager in LLVM 14 which the AFL++ passes still
|
||||
// use. cc_params[cc_par_cnt++] = "-flegacy-pass-manager";
|
||||
//#endif
|
||||
// #endif
|
||||
|
||||
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 LLVM_MAJOR >= 11
|
||||
#if LLVM_MAJOR >= 13
|
||||
#if defined __ANDROID__ || ANDROID
|
||||
cc_params[cc_par_cnt++] = "-fsanitize-coverage=trace-pc-guard";
|
||||
instrument_mode = INSTRUMENT_LLVMNATIVE;
|
||||
@ -718,7 +1014,7 @@ static void edit_params(u32 argc, char **argv, char **envp) {
|
||||
|
||||
} else {
|
||||
|
||||
#if LLVM_MAJOR >= 11 /* use new pass manager */
|
||||
#if LLVM_MAJOR >= 13 /* use new pass manager */
|
||||
#if LLVM_MAJOR < 16
|
||||
cc_params[cc_par_cnt++] = "-fexperimental-new-pass-manager";
|
||||
#endif
|
||||
@ -739,21 +1035,35 @@ static void edit_params(u32 argc, char **argv, char **envp) {
|
||||
#if LLVM_MAJOR >= 4
|
||||
if (!be_quiet)
|
||||
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");
|
||||
cc_params[cc_par_cnt++] = "-fsanitize-coverage=trace-pc-guard";
|
||||
instrument_mode = INSTRUMENT_LLVMNATIVE;
|
||||
#else
|
||||
FATAL("pcguard instrumentation requires llvm 4.0.1+");
|
||||
FATAL("pcguard instrumentation requires LLVM 4.0.1+");
|
||||
#endif
|
||||
#endif
|
||||
|
||||
} else if (instrument_mode == INSTRUMENT_LLVMNATIVE) {
|
||||
|
||||
#if LLVM_MAJOR >= 4
|
||||
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
|
||||
FATAL("pcguard instrumentation requires llvm 4.0.1+");
|
||||
FATAL("pcguard instrumentation requires LLVM 4.0.1+");
|
||||
#endif
|
||||
|
||||
} 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;
|
||||
while (--argc) {
|
||||
process_params(argc, argv);
|
||||
|
||||
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, "-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;
|
||||
|
||||
}
|
||||
if (!have_pic) { cc_params[cc_par_cnt++] = "-fPIC"; }
|
||||
|
||||
// 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
|
||||
@ -1101,10 +1267,16 @@ static void edit_params(u32 argc, char **argv, char **envp) {
|
||||
if (!have_c) cc_params[cc_par_cnt++] = "-lrt";
|
||||
#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++] = "-DFUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION=1";
|
||||
|
||||
/* As documented in instrumentation/README.persistent_mode.md, deferred
|
||||
forkserver initialization and persistent mode are not available in afl-gcc
|
||||
and afl-clang. */
|
||||
if (compiler_mode != GCC && compiler_mode != CLANG) {
|
||||
|
||||
cc_params[cc_par_cnt++] = "-D__AFL_HAVE_MANUAL_CONTROL=1";
|
||||
|
||||
/* When the user tries to use persistent or deferred forkserver modes by
|
||||
appending a single line to the program, we want to reliably inject a
|
||||
signature into the binary (to be picked up by afl-fuzz) and we want
|
||||
@ -1133,6 +1305,8 @@ static void edit_params(u32 argc, char **argv, char **envp) {
|
||||
"unsigned char __afl_fuzz_alt[1048576];"
|
||||
"unsigned char *__afl_fuzz_alt_ptr = __afl_fuzz_alt;";
|
||||
|
||||
}
|
||||
|
||||
if (plusplus_mode) {
|
||||
|
||||
cc_params[cc_par_cnt++] =
|
||||
@ -1169,10 +1343,12 @@ static void edit_params(u32 argc, char **argv, char **envp) {
|
||||
"(*__afl_fuzz_len = read(0, __afl_fuzz_alt_ptr, 1048576)) == 0xffffffff "
|
||||
"? 0 : *__afl_fuzz_len)";
|
||||
|
||||
if (compiler_mode != GCC && compiler_mode != CLANG) {
|
||||
|
||||
cc_params[cc_par_cnt++] =
|
||||
"-D__AFL_LOOP(_A)="
|
||||
"({ static volatile char *_B __attribute__((used,unused)); "
|
||||
" _B = (char*)\"" PERSIST_SIG
|
||||
"({ static volatile const char *_B __attribute__((used,unused)); "
|
||||
" _B = (const char*)\"" PERSIST_SIG
|
||||
"\"; "
|
||||
"extern int __afl_connected;"
|
||||
#ifdef __APPLE__
|
||||
@ -1187,8 +1363,8 @@ static void edit_params(u32 argc, char **argv, char **envp) {
|
||||
|
||||
cc_params[cc_par_cnt++] =
|
||||
"-D__AFL_INIT()="
|
||||
"do { static volatile char *_A __attribute__((used,unused)); "
|
||||
" _A = (char*)\"" DEFER_SIG
|
||||
"do { static volatile const char *_A __attribute__((used,unused)); "
|
||||
" _A = (const char*)\"" DEFER_SIG
|
||||
"\"; "
|
||||
#ifdef __APPLE__
|
||||
"__attribute__((visibility(\"default\"))) "
|
||||
@ -1199,6 +1375,8 @@ static void edit_params(u32 argc, char **argv, char **envp) {
|
||||
#endif /* ^__APPLE__ */
|
||||
"_I(); } while (0)";
|
||||
|
||||
}
|
||||
|
||||
if (x_set) {
|
||||
|
||||
cc_params[cc_par_cnt++] = "-x";
|
||||
@ -1639,15 +1817,19 @@ int main(int argc, char **argv, char **envp) {
|
||||
instrument_mode = INSTRUMENT_CLASSIC;
|
||||
lto_mode = 1;
|
||||
|
||||
} else if (!instrument_mode || instrument_mode == INSTRUMENT_AFL)
|
||||
} else if (!instrument_mode || instrument_mode == INSTRUMENT_AFL) {
|
||||
|
||||
instrument_mode = INSTRUMENT_AFL;
|
||||
else
|
||||
|
||||
} else {
|
||||
|
||||
FATAL("main instrumentation mode already set with %s",
|
||||
instrument_mode_string[instrument_mode]);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if (strncasecmp(ptr2, "pc-guard", strlen("pc-guard")) == 0 ||
|
||||
strncasecmp(ptr2, "pcguard", strlen("pcguard")) == 0) {
|
||||
|
||||
@ -1660,7 +1842,8 @@ int main(int argc, char **argv, char **envp) {
|
||||
}
|
||||
|
||||
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)
|
||||
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 ||
|
||||
strncasecmp(ptr2, "instrim", strlen("instrim")) == 0) {
|
||||
|
||||
@ -1831,7 +2031,7 @@ int main(int argc, char **argv, char **envp) {
|
||||
if (!compiler_mode) {
|
||||
|
||||
// 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)
|
||||
compiler_mode = LLVM;
|
||||
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 (clang_mode) {
|
||||
@ -1896,12 +2107,12 @@ int main(int argc, char **argv, char **envp) {
|
||||
"-------------|\n"
|
||||
"MODES: NCC PERSIST DICT LAF "
|
||||
"CMPLOG SELECT\n"
|
||||
" [LTO] llvm LTO: %s%s\n"
|
||||
" [LTO] LLVM LTO: %s%s\n"
|
||||
" PCGUARD DEFAULT yes yes yes yes yes "
|
||||
" yes\n"
|
||||
" CLASSIC yes yes yes yes yes "
|
||||
" yes\n"
|
||||
" [LLVM] llvm: %s%s\n"
|
||||
" [LLVM] LLVM: %s%s\n"
|
||||
" PCGUARD %s yes yes module yes yes "
|
||||
"yes\n"
|
||||
" CLASSIC %s no yes module yes yes "
|
||||
@ -1971,7 +2182,7 @@ int main(int argc, char **argv, char **envp) {
|
||||
" (instrumentation/README.lto.md)\n"
|
||||
" PERSIST: persistent mode support [code] (huge speed increase!)\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"
|
||||
" (instrumentation/README.lto.md + "
|
||||
"instrumentation/README.llvm.md)\n"
|
||||
@ -2087,6 +2298,8 @@ int main(int argc, char **argv, char **envp) {
|
||||
"bb\n"
|
||||
" AFL_REAL_LD: use this lld linker instead of the compiled in "
|
||||
"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");
|
||||
#endif
|
||||
|
||||
@ -2227,7 +2440,8 @@ int main(int argc, char **argv, char **envp) {
|
||||
"(requires LLVM 11 or higher)");
|
||||
#endif
|
||||
|
||||
if (instrument_opt_mode && instrument_mode != INSTRUMENT_CLASSIC)
|
||||
if (instrument_opt_mode && instrument_opt_mode != INSTRUMENT_OPT_CODECOV &&
|
||||
instrument_mode != INSTRUMENT_CLASSIC)
|
||||
FATAL(
|
||||
"CALLER, CTX and NGRAM instrumentation options can only be used with "
|
||||
"the LLVM CLASSIC instrumentation mode.");
|
||||
|
@ -949,7 +949,7 @@ void read_bitmap(u8 *fname, u8 *map, size_t len) {
|
||||
|
||||
/* Get unix time in milliseconds */
|
||||
|
||||
u64 get_cur_time(void) {
|
||||
inline u64 get_cur_time(void) {
|
||||
|
||||
struct timeval tv;
|
||||
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
|
||||
|
||||
|
@ -49,6 +49,134 @@
|
||||
#include <sys/select.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
|
||||
*/
|
||||
@ -84,6 +212,8 @@ void afl_fsrv_init(afl_forkserver_t *fsrv) {
|
||||
fsrv->nyx_runner = NULL;
|
||||
fsrv->nyx_id = 0xFFFFFFFF;
|
||||
fsrv->nyx_bind_cpu_id = 0xFFFFFFFF;
|
||||
fsrv->nyx_use_tmp_workdir = false;
|
||||
fsrv->nyx_tmp_workdir_path = NULL;
|
||||
#endif
|
||||
|
||||
// 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;
|
||||
case FS_ERROR_OLD_CMPLOG:
|
||||
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.");
|
||||
break;
|
||||
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 (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..."); }
|
||||
} else {
|
||||
|
||||
if (fsrv->nyx_bind_cpu_id == 0xFFFFFFFF) {
|
||||
if (fsrv->out_dir_path == NULL) {
|
||||
|
||||
FATAL("Nyx CPU ID is not set...");
|
||||
NYX_PRE_FATAL(fsrv, "Nyx workdir path not found...");
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/* 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_runner = fsrv->nyx_handlers->nyx_new(
|
||||
fsrv->target_path, x, fsrv->nyx_bind_cpu_id, MAX_FILE, true);
|
||||
fsrv->nyx_handlers->nyx_config_set_process_role(nyx_config, StandAlone);
|
||||
|
||||
} else {
|
||||
|
||||
if (fsrv->nyx_parent) {
|
||||
|
||||
fsrv->nyx_runner = fsrv->nyx_handlers->nyx_new_parent(
|
||||
fsrv->target_path, x, fsrv->nyx_bind_cpu_id, MAX_FILE, true);
|
||||
fsrv->nyx_handlers->nyx_config_set_process_role(nyx_config, Parent);
|
||||
|
||||
} else {
|
||||
|
||||
fsrv->nyx_runner = fsrv->nyx_handlers->nyx_new_child(
|
||||
fsrv->target_path, x, fsrv->nyx_bind_cpu_id, fsrv->nyx_id);
|
||||
fsrv->nyx_handlers->nyx_config_set_process_role(nyx_config, Child);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
ck_free(x);
|
||||
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 ..."); }
|
||||
|
||||
@ -458,15 +667,13 @@ void afl_fsrv_start(afl_forkserver_t *fsrv, char **argv,
|
||||
switch (fsrv->nyx_handlers->nyx_exec(fsrv->nyx_runner)) {
|
||||
|
||||
case Abort:
|
||||
fsrv->nyx_handlers->nyx_shutdown(fsrv->nyx_runner);
|
||||
FATAL("Error: Nyx abort occured...");
|
||||
NYX_PRE_FATAL(fsrv, "Error: Nyx abort occured...");
|
||||
break;
|
||||
case IoError:
|
||||
FATAL("Error: QEMU-Nyx has died...");
|
||||
NYX_PRE_FATAL(fsrv, "Error: QEMU-Nyx has died...");
|
||||
break;
|
||||
case Error:
|
||||
fsrv->nyx_handlers->nyx_shutdown(fsrv->nyx_runner);
|
||||
FATAL("Error: Nyx runtime error has occured...");
|
||||
NYX_PRE_FATAL(fsrv, "Error: Nyx runtime error has occured...");
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
@ -476,7 +683,8 @@ void afl_fsrv_start(afl_forkserver_t *fsrv, char **argv,
|
||||
/* autodict in Nyx mode */
|
||||
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);
|
||||
ck_free(x);
|
||||
|
||||
@ -489,7 +697,8 @@ void afl_fsrv_start(afl_forkserver_t *fsrv, char **argv,
|
||||
u8 *dict = ck_alloc(f_len);
|
||||
if (dict == NULL) {
|
||||
|
||||
FATAL("Could not allocate %u bytes of autodictionary memory",
|
||||
NYX_PRE_FATAL(
|
||||
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 {
|
||||
|
||||
FATAL(
|
||||
NYX_PRE_FATAL(
|
||||
fsrv,
|
||||
"Reading autodictionary fail at position %u with %u bytes "
|
||||
"left.",
|
||||
offset, len);
|
||||
@ -777,7 +987,7 @@ void afl_fsrv_start(afl_forkserver_t *fsrv, char **argv,
|
||||
|
||||
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)
|
||||
status = (status & 0xf0ffffff);
|
||||
|
||||
@ -849,7 +1059,7 @@ void afl_fsrv_start(afl_forkserver_t *fsrv, char **argv,
|
||||
|
||||
FATAL(
|
||||
"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",
|
||||
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 "
|
||||
"options\n"
|
||||
" fail, poke <afl-users@googlegroups.com> for troubleshooting "
|
||||
" fail, poke the Awesome Fuzzing Discord for troubleshooting "
|
||||
"tips.\n");
|
||||
|
||||
} 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 "
|
||||
"options\n"
|
||||
" fail, poke <afl-users@googlegroups.com> for troubleshooting "
|
||||
" fail, poke the Awesome Fuzzing Discord for troubleshooting "
|
||||
"tips.\n",
|
||||
stringify_mem_size(val_buf, sizeof(val_buf), fsrv->mem_limit << 20),
|
||||
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"
|
||||
|
||||
"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 {
|
||||
|
||||
@ -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 "
|
||||
"options\n"
|
||||
" fail, poke <afl-users@googlegroups.com> for troubleshooting "
|
||||
" fail, poke the Awesome Fuzzing Discord for troubleshooting "
|
||||
"tips.\n",
|
||||
getenv(DEFER_ENV_VAR)
|
||||
? " - 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;
|
||||
|
||||
#ifdef __linux__
|
||||
if (fsrv->nyx_mode) {
|
||||
|
||||
free(fsrv->nyx_aux_string);
|
||||
fsrv->nyx_handlers->nyx_shutdown(fsrv->nyx_runner);
|
||||
|
||||
}
|
||||
|
||||
afl_nyx_runner_kill(fsrv);
|
||||
#endif
|
||||
|
||||
}
|
||||
@ -1377,7 +1581,6 @@ afl_fsrv_run_target(afl_forkserver_t *fsrv, u32 timeout,
|
||||
FATAL("FixMe: Nyx InvalidWriteToPayload handler is missing");
|
||||
break;
|
||||
case Abort:
|
||||
fsrv->nyx_handlers->nyx_shutdown(fsrv->nyx_runner);
|
||||
FATAL("Error: Nyx abort occured...");
|
||||
case IoError:
|
||||
if (*stop_soon_p) {
|
||||
|
@ -465,7 +465,8 @@ save_if_interesting(afl_state_t *afl, void *mem, u32 len, u8 fault) {
|
||||
|
||||
u8 fn[PATH_MAX];
|
||||
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;
|
||||
u64 cksum = 0;
|
||||
|
||||
@ -477,6 +478,7 @@ save_if_interesting(afl_state_t *afl, void *mem, u32 len, u8 fault) {
|
||||
|
||||
classify_counts(&afl->fsrv);
|
||||
classified = 1;
|
||||
need_hash = 0;
|
||||
|
||||
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);
|
||||
|
||||
if (unlikely(new_bits)) { classified = 1; }
|
||||
|
||||
}
|
||||
|
||||
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 */
|
||||
afl->queue_top->exec_cksum =
|
||||
hash64(afl->fsrv.trace_bits, afl->fsrv.map_size, HASH_CONST);
|
||||
classified = 1;
|
||||
need_hash = 0;
|
||||
|
||||
}
|
||||
|
||||
|
@ -716,12 +716,25 @@ void read_testcases(afl_state_t *afl, u8 *directory) {
|
||||
|
||||
}
|
||||
|
||||
// if (getenv("MYTEST")) afl->in_place_resume = 1;
|
||||
|
||||
if (nl_cnt) {
|
||||
|
||||
u32 done = 0;
|
||||
|
||||
if (unlikely(afl->in_place_resume)) {
|
||||
|
||||
i = nl_cnt;
|
||||
|
||||
} else {
|
||||
|
||||
i = 0;
|
||||
|
||||
}
|
||||
|
||||
do {
|
||||
|
||||
--i;
|
||||
if (unlikely(afl->in_place_resume)) { --i; }
|
||||
|
||||
struct stat st;
|
||||
u8 dfn[PATH_MAX];
|
||||
@ -745,7 +758,7 @@ void read_testcases(afl_state_t *afl, u8 *directory) {
|
||||
free(nl[i]); /* not tracked */
|
||||
read_testcases(afl, 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")) {
|
||||
|
||||
ck_free(fn2);
|
||||
continue;
|
||||
goto next_entry;
|
||||
|
||||
}
|
||||
|
||||
@ -801,21 +814,23 @@ void read_testcases(afl_state_t *afl, u8 *directory) {
|
||||
|
||||
}
|
||||
|
||||
/*
|
||||
if (unlikely(afl->schedule >= FAST && afl->schedule <= RARE)) {
|
||||
next_entry:
|
||||
if (unlikely(afl->in_place_resume)) {
|
||||
|
||||
u64 cksum = hash64(afl->fsrv.trace_bits, afl->fsrv.map_size,
|
||||
HASH_CONST); afl->queue_top->n_fuzz_entry = cksum % N_FUZZ_SIZE;
|
||||
afl->n_fuzz[afl->queue_top->n_fuzz_entry] = 1;
|
||||
if (unlikely(i == 0)) { done = 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 */
|
||||
|
||||
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) {
|
||||
|
||||
SAYF(cGRA " len = %u, map size = %u, exec speed = %llu us\n" cRST,
|
||||
q->len, q->bitmap_size, q->exec_us);
|
||||
SAYF(cGRA
|
||||
" 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 "
|
||||
"other options\n"
|
||||
" fail, poke <afl-users@googlegroups.com> for "
|
||||
" fail, poke the Awesome Fuzzing Discord for "
|
||||
"troubleshooting tips.\n",
|
||||
stringify_mem_size(val_buf, sizeof(val_buf),
|
||||
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 "
|
||||
"other options\n"
|
||||
" fail, poke <afl-users@googlegroups.com> for "
|
||||
" fail, poke the Awesome Fuzzing Discord for "
|
||||
"troubleshooting tips.\n");
|
||||
|
||||
}
|
||||
@ -1153,14 +1170,14 @@ void perform_dry_run(afl_state_t *afl) {
|
||||
|
||||
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];
|
||||
if (!q || q->disabled || q->cal_failed || !q->exec_cksum) { continue; }
|
||||
|
||||
u32 done = 0;
|
||||
|
||||
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];
|
||||
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->perf_score = 0;
|
||||
|
||||
if (afl->debug) {
|
||||
|
||||
WARNF("Same coverage - %s is kept active, %s is disabled.",
|
||||
q->fname, p->fname);
|
||||
|
||||
}
|
||||
|
||||
} else {
|
||||
|
||||
if (!q->was_fuzzed) {
|
||||
@ -1196,7 +1220,14 @@ void perform_dry_run(afl_state_t *afl) {
|
||||
q->disabled = 1;
|
||||
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
|
||||
|
||||
}
|
||||
|
||||
|
@ -179,11 +179,19 @@ struct custom_mutator *load_custom_mutator(afl_state_t *afl, const char *fn) {
|
||||
void *dh;
|
||||
struct custom_mutator *mutator = ck_alloc(sizeof(struct custom_mutator));
|
||||
|
||||
mutator->name = fn;
|
||||
if (memchr(fn, '/', strlen(fn)))
|
||||
mutator->name_short = strrchr(fn, '/') + 1;
|
||||
else
|
||||
if (memchr(fn, '/', strlen(fn))) {
|
||||
|
||||
mutator->name_short = strdup(strrchr(fn, '/') + 1);
|
||||
|
||||
} else {
|
||||
|
||||
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);
|
||||
|
||||
dh = dlopen(fn, RTLD_NOW);
|
||||
|
@ -799,6 +799,7 @@ u8 fuzz_one_original(afl_state_t *afl) {
|
||||
|
||||
eff_map = afl_realloc(AFL_BUF_PARAM(eff), EFF_ALEN(len));
|
||||
if (unlikely(!eff_map)) { PFATAL("alloc"); }
|
||||
memset(eff_map, 0, EFF_ALEN(len));
|
||||
eff_map[0] = 1;
|
||||
|
||||
if (EFF_APOS(len - 1) != 0) {
|
||||
@ -1868,6 +1869,7 @@ custom_mutator_stage:
|
||||
|
||||
afl->stage_name = "custom mutator";
|
||||
afl->stage_short = "custom";
|
||||
afl->stage_cur = 0;
|
||||
afl->stage_val_type = STAGE_VAL_NONE;
|
||||
bool has_custom_fuzz = false;
|
||||
u32 shift = unlikely(afl->custom_only) ? 7 : 8;
|
||||
@ -1888,6 +1890,7 @@ custom_mutator_stage:
|
||||
if (el->afl_custom_fuzz) {
|
||||
|
||||
afl->current_custom_fuzz = el;
|
||||
afl->stage_name = el->name_short;
|
||||
|
||||
if (el->afl_custom_fuzz_count) {
|
||||
|
||||
@ -2003,20 +2006,22 @@ custom_mutator_stage:
|
||||
afl->queue_cur->stats_mutated += afl->stage_max;
|
||||
#endif
|
||||
|
||||
if (likely(afl->custom_only)) {
|
||||
|
||||
/* Skip other stages */
|
||||
ret_val = 0;
|
||||
goto abandon_entry;
|
||||
|
||||
}
|
||||
|
||||
/****************
|
||||
* RANDOM HAVOC *
|
||||
****************/
|
||||
|
||||
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;
|
||||
|
||||
/* 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_max = ((doing_det ? HAVOC_CYCLES_INIT : HAVOC_CYCLES) *
|
||||
perf_score / afl->havoc_div) >>
|
||||
7;
|
||||
8;
|
||||
|
||||
} else {
|
||||
|
||||
@ -2037,7 +2042,7 @@ havoc_stage:
|
||||
snprintf(afl->stage_name_buf, STAGE_BUF_SIZE, "splice %u", splice_cycle);
|
||||
afl->stage_name = afl->stage_name_buf;
|
||||
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));
|
||||
if (unlikely(!eff_map)) { PFATAL("alloc"); }
|
||||
memset(eff_map, 0, EFF_ALEN(len));
|
||||
eff_map[0] = 1;
|
||||
|
||||
if (EFF_APOS(len - 1) != 0) {
|
||||
@ -4951,7 +4957,7 @@ pacemaker_fuzzing:
|
||||
MOpt_globals.splice_stageformat, splice_cycle);
|
||||
afl->stage_name = afl->stage_name_buf;
|
||||
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;
|
||||
|
||||
}
|
||||
|
||||
|
@ -219,11 +219,14 @@ static py_mutator_t *init_py_module(afl_state_t *afl, u8 *module_name) {
|
||||
|
||||
if (py_module != NULL) {
|
||||
|
||||
u8 py_notrim = 0, py_idx;
|
||||
/* init, required */
|
||||
u8 py_notrim = 0;
|
||||
py_functions[PY_FUNC_INIT] = PyObject_GetAttrString(py_module, "init");
|
||||
if (!py_functions[PY_FUNC_INIT])
|
||||
FATAL("init function not found in python module");
|
||||
if (!py_functions[PY_FUNC_INIT]) {
|
||||
|
||||
WARNF("init function not found in python module");
|
||||
|
||||
}
|
||||
|
||||
py_functions[PY_FUNC_FUZZ] = PyObject_GetAttrString(py_module, "fuzz");
|
||||
if (!py_functions[PY_FUNC_FUZZ])
|
||||
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");
|
||||
py_functions[PY_FUNC_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] =
|
||||
PyObject_GetAttrString(py_module, "post_process");
|
||||
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])
|
||||
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) {
|
||||
|
||||
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;
|
||||
|
||||
if (py_mutator->py_functions[PY_FUNC_INIT] == NULL) { return; }
|
||||
|
||||
PyObject *py_args, *py_value;
|
||||
|
||||
/* 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;
|
||||
|
||||
mutator = ck_alloc(sizeof(struct custom_mutator));
|
||||
|
||||
mutator->name = 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 = init_py_module(afl, module_name);
|
||||
mutator->data = py_mutator;
|
||||
|
@ -49,11 +49,13 @@ inline u32 select_next_queue_entry(afl_state_t *afl) {
|
||||
|
||||
u32 s = rand_below(afl, afl->queued_items);
|
||||
double p = rand_next_percent(afl);
|
||||
|
||||
/*
|
||||
fprintf(stderr, "select: p=%f s=%u ... p < prob[s]=%f ? s=%u : alias[%u]=%u"
|
||||
" ==> %u\n", p, s, afl->alias_probability[s], s, s, afl->alias_table[s], p <
|
||||
afl->alias_probability[s] ? s : afl->alias_table[s]);
|
||||
*/
|
||||
|
||||
return (p < afl->alias_probability[s] ? s : afl->alias_table[s]);
|
||||
|
||||
}
|
||||
@ -74,7 +76,8 @@ double compute_weight(afl_state_t *afl, struct queue_entry *q,
|
||||
if (likely(afl->schedule < RARE)) { weight *= (avg_exec_us / q->exec_us); }
|
||||
weight *= (log(q->bitmap_size) / avg_bitmap_size);
|
||||
weight *= (1 + (q->tc_ref / avg_top_size));
|
||||
if (unlikely(weight < 1.0)) { weight = 1.0; }
|
||||
|
||||
if (unlikely(weight < 0.1)) { weight = 0.1; }
|
||||
if (unlikely(q->favored)) { weight *= 5; }
|
||||
if (unlikely(!q->was_fuzzed)) { weight *= 2; }
|
||||
|
||||
@ -86,25 +89,28 @@ double compute_weight(afl_state_t *afl, struct queue_entry *q,
|
||||
|
||||
void create_alias_table(afl_state_t *afl) {
|
||||
|
||||
u32 n = afl->queued_items, i = 0, a, g;
|
||||
u32 n = afl->queued_items, i = 0, nSmall = 0, nLarge = n - 1;
|
||||
double sum = 0;
|
||||
|
||||
double *P = (double *)afl_realloc(AFL_BUF_PARAM(out), n * sizeof(double));
|
||||
u32 *Small = (int *)afl_realloc(AFL_BUF_PARAM(out_scratch), n * sizeof(u32));
|
||||
u32 *Large = (int *)afl_realloc(AFL_BUF_PARAM(in_scratch), n * sizeof(u32));
|
||||
|
||||
afl->alias_table =
|
||||
(u32 *)afl_realloc((void **)&afl->alias_table, n * sizeof(u32));
|
||||
afl->alias_probability = (double *)afl_realloc(
|
||||
(void **)&afl->alias_probability, n * sizeof(double));
|
||||
double *P = (double *)afl_realloc(AFL_BUF_PARAM(out), n * sizeof(double));
|
||||
int *S = (int *)afl_realloc(AFL_BUF_PARAM(out_scratch), n * sizeof(u32));
|
||||
int *L = (int *)afl_realloc(AFL_BUF_PARAM(in_scratch), n * sizeof(u32));
|
||||
|
||||
if (!P || !S || !L || !afl->alias_table || !afl->alias_probability) {
|
||||
if (!P || !Small || !Large || !afl->alias_table || !afl->alias_probability) {
|
||||
|
||||
FATAL("could not acquire memory for alias table");
|
||||
|
||||
}
|
||||
|
||||
memset((void *)afl->alias_table, 0, n * sizeof(u32));
|
||||
memset((void *)afl->alias_probability, 0, n * sizeof(double));
|
||||
memset((void *)afl->alias_table, 0, n * sizeof(u32));
|
||||
memset((void *)Small, 0, n * sizeof(u32));
|
||||
memset((void *)Large, 0, n * sizeof(u32));
|
||||
|
||||
if (likely(afl->schedule < RARE)) {
|
||||
|
||||
@ -148,73 +154,116 @@ void create_alias_table(afl_state_t *afl) {
|
||||
|
||||
}
|
||||
|
||||
if (unlikely(afl->schedule == MMOPT) && afl->queued_discovered) {
|
||||
|
||||
u32 cnt = afl->queued_discovered >= 5 ? 5 : afl->queued_discovered;
|
||||
|
||||
for (i = n - cnt; i < n; i++) {
|
||||
|
||||
struct queue_entry *q = afl->queue_buf[i];
|
||||
|
||||
if (likely(!q->disabled)) { q->weight *= 2.0; }
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
for (i = 0; i < n; i++) {
|
||||
|
||||
// weight is always 0 for disabled entries
|
||||
if (unlikely(afl->queue_buf[i]->disabled)) {
|
||||
|
||||
P[i] = 0;
|
||||
|
||||
} else {
|
||||
|
||||
P[i] = (afl->queue_buf[i]->weight * n) / sum;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
} else {
|
||||
|
||||
for (i = 0; i < n; i++) {
|
||||
|
||||
struct queue_entry *q = afl->queue_buf[i];
|
||||
|
||||
if (likely(!q->disabled)) { q->perf_score = calculate_score(afl, q); }
|
||||
if (likely(!q->disabled)) {
|
||||
|
||||
q->perf_score = calculate_score(afl, q);
|
||||
sum += q->perf_score;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
for (i = 0; i < n; i++) {
|
||||
|
||||
// perf_score is always 0 for disabled entries
|
||||
if (unlikely(afl->queue_buf[i]->disabled)) {
|
||||
|
||||
P[i] = 0;
|
||||
|
||||
} else {
|
||||
|
||||
P[i] = (afl->queue_buf[i]->perf_score * n) / sum;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
int nS = 0, nL = 0, s;
|
||||
for (s = (s32)n - 1; s >= 0; --s) {
|
||||
}
|
||||
|
||||
if (P[s] < 1) {
|
||||
// Done collecting weightings in P, now create the arrays.
|
||||
|
||||
S[nS++] = s;
|
||||
for (s32 j = (s32)(n - 1); j >= 0; j--) {
|
||||
|
||||
if (P[j] < 1) {
|
||||
|
||||
Small[nSmall++] = (u32)j;
|
||||
|
||||
} else {
|
||||
|
||||
L[nL++] = s;
|
||||
Large[nLarge--] = (u32)j;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
while (nS && nL) {
|
||||
while (nSmall && nLarge != n - 1) {
|
||||
|
||||
a = S[--nS];
|
||||
g = L[--nL];
|
||||
afl->alias_probability[a] = P[a];
|
||||
afl->alias_table[a] = g;
|
||||
P[g] = P[g] + P[a] - 1;
|
||||
if (P[g] < 1) {
|
||||
u32 small = Small[--nSmall];
|
||||
u32 large = Large[++nLarge];
|
||||
|
||||
S[nS++] = g;
|
||||
afl->alias_probability[small] = P[small];
|
||||
afl->alias_table[small] = large;
|
||||
|
||||
P[large] = P[large] - (1 - P[small]);
|
||||
|
||||
if (P[large] < 1) {
|
||||
|
||||
Small[nSmall++] = large;
|
||||
|
||||
} else {
|
||||
|
||||
L[nL++] = g;
|
||||
Large[nLarge--] = large;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
while (nL)
|
||||
afl->alias_probability[L[--nL]] = 1;
|
||||
while (nSmall) {
|
||||
|
||||
while (nS)
|
||||
afl->alias_probability[S[--nS]] = 1;
|
||||
afl->alias_probability[Small[--nSmall]] = 1;
|
||||
|
||||
}
|
||||
|
||||
while (nLarge != n - 1) {
|
||||
|
||||
afl->alias_probability[Large[++nLarge]] = 1;
|
||||
|
||||
}
|
||||
|
||||
afl->reinit_table = 0;
|
||||
|
||||
@ -249,7 +298,7 @@ void create_alias_table(afl_state_t *afl) {
|
||||
*/
|
||||
/*
|
||||
fprintf(stderr, " entry alias probability perf_score weight
|
||||
filename\n"); for (u32 i = 0; i < n; ++i) fprintf(stderr, " %5u %5u %11u
|
||||
filename\n"); for (i = 0; i < n; ++i) fprintf(stderr, " %5u %5u %11u
|
||||
%0.9f %0.9f %s\n", i, afl->alias_table[i], afl->alias_probability[i],
|
||||
afl->queue_buf[i]->perf_score, afl->queue_buf[i]->weight,
|
||||
afl->queue_buf[i]->fname);
|
||||
|
@ -28,8 +28,8 @@
|
||||
#include "afl-fuzz.h"
|
||||
#include "cmplog.h"
|
||||
|
||||
//#define _DEBUG
|
||||
//#define CMPLOG_INTROSPECTION
|
||||
// #define _DEBUG
|
||||
// #define CMPLOG_INTROSPECTION
|
||||
|
||||
// CMP attribute enum
|
||||
enum {
|
||||
@ -379,7 +379,7 @@ static u8 colorization(afl_state_t *afl, u8 *buf, u32 len,
|
||||
|
||||
}
|
||||
|
||||
if (++afl->stage_cur % screen_update == 0) { show_stats(afl); };
|
||||
if (unlikely(++afl->stage_cur % screen_update == 0)) { show_stats(afl); };
|
||||
|
||||
}
|
||||
|
||||
@ -571,7 +571,7 @@ static u8 its_fuzz(afl_state_t *afl, u8 *buf, u32 len, u8 *status) {
|
||||
|
||||
}
|
||||
|
||||
//#ifdef CMPLOG_SOLVE_TRANSFORM
|
||||
// #ifdef CMPLOG_SOLVE_TRANSFORM
|
||||
static int strntoll(const char *str, size_t sz, char **end, int base,
|
||||
long long *out) {
|
||||
|
||||
@ -771,7 +771,7 @@ static void to_base64(u8 *src, u8 *dst, u32 dst_len) {
|
||||
|
||||
#endif
|
||||
|
||||
//#endif
|
||||
// #endif
|
||||
|
||||
static u8 cmp_extend_encoding(afl_state_t *afl, struct cmp_header *h,
|
||||
u64 pattern, u64 repl, u64 o_pattern,
|
||||
@ -790,7 +790,7 @@ static u8 cmp_extend_encoding(afl_state_t *afl, struct cmp_header *h,
|
||||
|
||||
u32 its_len = MIN(len - idx, taint_len);
|
||||
|
||||
if (afl->fsrv.total_execs - last_update > screen_update) {
|
||||
if (unlikely(afl->fsrv.total_execs - last_update > screen_update)) {
|
||||
|
||||
show_stats(afl);
|
||||
last_update = afl->fsrv.total_execs;
|
||||
@ -803,7 +803,7 @@ static u8 cmp_extend_encoding(afl_state_t *afl, struct cmp_header *h,
|
||||
// o_pattern, pattern, repl, changed_val, idx, taint_len,
|
||||
// hshape, attr);
|
||||
|
||||
//#ifdef CMPLOG_SOLVE_TRANSFORM
|
||||
// #ifdef CMPLOG_SOLVE_TRANSFORM
|
||||
// reverse atoi()/strnu?toll() is expensive, so we only to it in lvl 3
|
||||
if (afl->cmplog_enable_transform && (lvl & LVL3)) {
|
||||
|
||||
@ -1120,7 +1120,7 @@ static u8 cmp_extend_encoding(afl_state_t *afl, struct cmp_header *h,
|
||||
|
||||
}
|
||||
|
||||
//#endif
|
||||
// #endif
|
||||
|
||||
// we only allow this for ascii2integer (above) so leave if this is the case
|
||||
if (unlikely(pattern == o_pattern)) { return 0; }
|
||||
@ -1275,7 +1275,7 @@ static u8 cmp_extend_encoding(afl_state_t *afl, struct cmp_header *h,
|
||||
// 16 = modified float, 32 = modified integer (modified = wont match
|
||||
// in original buffer)
|
||||
|
||||
//#ifdef CMPLOG_SOLVE_ARITHMETIC
|
||||
// #ifdef CMPLOG_SOLVE_ARITHMETIC
|
||||
if (!afl->cmplog_enable_arith || lvl < LVL3 || attr == IS_TRANSFORM) {
|
||||
|
||||
return 0;
|
||||
@ -1440,7 +1440,7 @@ static u8 cmp_extend_encoding(afl_state_t *afl, struct cmp_header *h,
|
||||
|
||||
}
|
||||
|
||||
//#endif /*
|
||||
// #endif /*
|
||||
// CMPLOG_SOLVE_ARITHMETIC
|
||||
|
||||
return 0;
|
||||
@ -1455,7 +1455,7 @@ static u8 cmp_extend_encodingN(afl_state_t *afl, struct cmp_header *h,
|
||||
u32 taint_len, u8 *orig_buf, u8 *buf, u8 *cbuf,
|
||||
u32 len, u8 do_reverse, u8 lvl, u8 *status) {
|
||||
|
||||
if (afl->fsrv.total_execs - last_update > screen_update) {
|
||||
if (unlikely(afl->fsrv.total_execs - last_update > screen_update)) {
|
||||
|
||||
show_stats(afl);
|
||||
last_update = afl->fsrv.total_execs;
|
||||
@ -1948,11 +1948,11 @@ static u8 rtn_extend_encoding(afl_state_t *afl, u8 entry,
|
||||
#ifndef CMPLOG_COMBINE
|
||||
(void)(cbuf);
|
||||
#endif
|
||||
//#ifndef CMPLOG_SOLVE_TRANSFORM
|
||||
// #ifndef CMPLOG_SOLVE_TRANSFORM
|
||||
// (void)(changed_val);
|
||||
//#endif
|
||||
// #endif
|
||||
|
||||
if (afl->fsrv.total_execs - last_update > screen_update) {
|
||||
if (unlikely(afl->fsrv.total_execs - last_update > screen_update)) {
|
||||
|
||||
show_stats(afl);
|
||||
last_update = afl->fsrv.total_execs;
|
||||
@ -2418,7 +2418,7 @@ static u8 rtn_extend_encoding(afl_state_t *afl, u8 entry,
|
||||
|
||||
}
|
||||
|
||||
//#endif
|
||||
// #endif
|
||||
|
||||
return 0;
|
||||
|
||||
@ -2818,9 +2818,9 @@ u8 input_to_state_stage(afl_state_t *afl, u8 *orig_buf, u8 *buf, u32 len) {
|
||||
|
||||
} else if ((lvl & LVL1)
|
||||
|
||||
//#ifdef CMPLOG_SOLVE_TRANSFORM
|
||||
// #ifdef CMPLOG_SOLVE_TRANSFORM
|
||||
|| ((lvl & LVL3) && afl->cmplog_enable_transform)
|
||||
//#endif
|
||||
// #endif
|
||||
) {
|
||||
|
||||
if (unlikely(rtn_fuzz(afl, k, orig_buf, buf, cbuf, len, lvl, taint))) {
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user